diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml new file mode 100644 index 000000000..8c26d470e --- /dev/null +++ b/.github/workflows/sync-upstream.yml @@ -0,0 +1,24 @@ +name: Sync Upstream + +on: + schedule: + - cron: '0 0 * * *' # Viene eseguito ogni giorno a mezzanotte + workflow_dispatch: # Ti permette di lanciarlo anche a mano dai tasti di GitHub + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout target repo + uses: actions/checkout@v3 + with: + ref: main # O il nome del tuo branch principale (es. master) + + - name: Sync upstream changes + id: sync + uses: a2vu/sync-upstream-repo@v1.1.0 + with: + upstream_repo: https://github.com/lanedirt/OGameX.git + upstream_branch: main + target_branch: main # Il tuo branch + force: false # Metti true solo se vuoi sovrascrivere tutto (rischioso!) \ No newline at end of file diff --git a/app/Enums/DarkMatterTransactionType.php b/app/Enums/DarkMatterTransactionType.php index 7e72ebf69..f0329741f 100644 --- a/app/Enums/DarkMatterTransactionType.php +++ b/app/Enums/DarkMatterTransactionType.php @@ -14,4 +14,5 @@ enum DarkMatterTransactionType: string case SPEEDUP = 'speedup'; case ADMIN_ADJUSTMENT = 'admin_adjustment'; case HALVING = 'halving'; + case OFFICER_PURCHASE = 'officer_purchase'; } diff --git a/app/GameMissions/EspionageMission.php b/app/GameMissions/EspionageMission.php index 3a8e57600..f0f4937f3 100644 --- a/app/GameMissions/EspionageMission.php +++ b/app/GameMissions/EspionageMission.php @@ -22,6 +22,7 @@ use OGame\Models\Resources; use OGame\Services\CounterEspionageService; use OGame\Services\DebrisFieldService; +use OGame\Services\OfficerService; use OGame\Services\PlanetService; use OGame\Services\PlayerService; use RuntimeException; @@ -107,8 +108,13 @@ protected function processArrival(FleetMission $mission): void // Calculate counter-espionage chance $counterEspionageService = resolve(CounterEspionageService::class); $attackerProbeCount = $mission->espionage_probe; - $attackerEspionageLevel = $originPlayer->getResearchLevel('espionage_technology'); - $defenderEspionageLevel = $targetPlayer->getResearchLevel('espionage_technology'); + $officerService = app(OfficerService::class); + $attackerEspionageLevel = $originPlayer->getResearchLevel('espionage_technology') + + $officerService->getAdditionalEspionageLevels($originPlayer->getUser()) + + $officerService->getCommandingStaffEspionageLevels($originPlayer->getUser()); + $defenderEspionageLevel = $targetPlayer->getResearchLevel('espionage_technology') + + $officerService->getAdditionalEspionageLevels($targetPlayer->getUser()) + + $officerService->getCommandingStaffEspionageLevels($targetPlayer->getUser()); // TODO: Include ACS Defend fleets in counter-espionage chance calculation // Currently only counts planet owner's ships via getDefenderShipCount() @@ -487,8 +493,13 @@ private function createEspionageReport(FleetMission $mission, PlanetService $ori } // TODO: Validate this does not cause issues when probing slot 16 - $attackerEspionageLevel = $originPlayer->getResearchLevel('espionage_technology'); - $defenderEspionageLevel = $targetPlayer->getResearchLevel('espionage_technology'); + $officerServiceReport = app(OfficerService::class); + $attackerEspionageLevel = $originPlayer->getResearchLevel('espionage_technology') + + $officerServiceReport->getAdditionalEspionageLevels($originPlayer->getUser()) + + $officerServiceReport->getCommandingStaffEspionageLevels($originPlayer->getUser()); + $defenderEspionageLevel = $targetPlayer->getResearchLevel('espionage_technology') + + $officerServiceReport->getAdditionalEspionageLevels($targetPlayer->getUser()) + + $officerServiceReport->getCommandingStaffEspionageLevels($targetPlayer->getUser()); $techDifference = $defenderEspionageLevel - $attackerEspionageLevel; $levelDifference = max(0, $techDifference); $extraProbesRequired = pow($levelDifference, 2); diff --git a/app/Http/Controllers/Abstracts/AbstractBuildingsController.php b/app/Http/Controllers/Abstracts/AbstractBuildingsController.php index a69d1e54b..b0dfa2909 100644 --- a/app/Http/Controllers/Abstracts/AbstractBuildingsController.php +++ b/app/Http/Controllers/Abstracts/AbstractBuildingsController.php @@ -273,7 +273,7 @@ public function addBuildRequest(Request $request, PlayerService $player): JsonRe return response()->json([ 'status' => 'success', - 'message' => 'Building construction started.', + 'message' => __('t_ingame.buildings.building_started'), ]); } diff --git a/app/Http/Controllers/Admin/DeveloperShortcutsController.php b/app/Http/Controllers/Admin/DeveloperShortcutsController.php index 2dbda4047..b6cbf1834 100644 --- a/app/Http/Controllers/Admin/DeveloperShortcutsController.php +++ b/app/Http/Controllers/Admin/DeveloperShortcutsController.php @@ -17,6 +17,7 @@ use OGame\Services\DebrisFieldService; use OGame\Services\ObjectService; use OGame\Services\PlayerService; +use OGame\Services\OfficerService; use OGame\Services\SettingsService; class DeveloperShortcutsController extends OGameController @@ -454,4 +455,39 @@ public function impersonate(Request $request): RedirectResponse return redirect()->route('overview.index') ->with('success', __('Now impersonating :username', ['username' => $targetUser->username])); } + + /** + * Activate an officer for a player from the developer shortcuts page. + */ + public function activateOfficer(Request $request, OfficerService $officerService): RedirectResponse + { + $validOfficerKeys = array_values(OfficerService::TYPE_MAP); + $validOfficerKeys[] = 'all_officers'; + + $validated = $request->validate([ + 'username' => ['required', 'string'], + 'officer_key' => ['required', 'string', 'in:' . implode(',', $validOfficerKeys)], + 'days' => ['required', 'integer', 'in:' . implode(',', OfficerService::DURATIONS)], + ]); + + $user = User::where('username', $validated['username'])->first(); + if (!$user) { + return redirect()->back()->with('error', 'Player "' . $validated['username'] . '" not found.'); + } + + $officer = $officerService->getOfficer($user); + + if ($validated['officer_key'] === 'all_officers') { + foreach (array_values(OfficerService::TYPE_MAP) as $key) { + $officer->activate($key, (int)$validated['days']); + } + } else { + $officer->activate($validated['officer_key'], (int)$validated['days']); + } + + $officer->save(); + $officerService->clearCache($user); + + return redirect()->back()->with('success', 'Officer "' . $validated['officer_key'] . '" activated for ' . $validated['days'] . ' days for player "' . $validated['username'] . '".'); + } } diff --git a/app/Http/Controllers/FleetEventsController.php b/app/Http/Controllers/FleetEventsController.php index 9d3a09928..ee662386c 100644 --- a/app/Http/Controllers/FleetEventsController.php +++ b/app/Http/Controllers/FleetEventsController.php @@ -499,7 +499,7 @@ public function fetchEventList(PlayerService $player, FleetMissionService $fleet $summaryRow->time_departure = $initiator->time_departure; $summaryRow->is_return_trip = false; $summaryRow->is_recallable = false; - $summaryRow->friendly_status = $initiator->friendly_status ?? 'friendly'; + $summaryRow->friendly_status = $initiator->friendly_status; $summaryRow->destination_planet_name = $initiator->destination_planet_name; $summaryRow->destination_planet_coords = $initiator->destination_planet_coords; diff --git a/app/Http/Controllers/GalaxyController.php b/app/Http/Controllers/GalaxyController.php index 8b4f5c888..dac9fdd9c 100644 --- a/app/Http/Controllers/GalaxyController.php +++ b/app/Http/Controllers/GalaxyController.php @@ -218,7 +218,7 @@ private function createDebrisFieldArray(DebrisFieldService $debrisField): array 'availableMissions' => [ [ 'missionType' => 8, - 'name' => 'Harvest', + 'name' => __('t_ingame.fleet.mission_recycle'), ], ], 'requiredShips' => $debrisField->calculateRequiredRecyclers(), @@ -293,7 +293,7 @@ private function getAvailableMissions(int $galaxy, int $system, int $position, P $availableMissions[] = [ 'missionType' => 3, 'link' => route('fleet.index', ['galaxy' => $galaxy, 'system' => $system, 'position' => $position, 'type' => $planet->getPlanetType()->value, 'mission' => 3]), - 'name' => __('Transport'), + 'name' => __('t_ingame.fleet.mission_transport'), ]; $targetPlayer = $planet->getPlayer(); @@ -313,14 +313,14 @@ private function getAvailableMissions(int $galaxy, int $system, int $position, P 'reportId' => '', 'reportLink' => '', 'link' => route('fleet.dispatch.sendfleet', ['galaxy' => $galaxy, 'system' => $system, 'position' => $position, 'type' => $planet->getPlanetType()->value, 'mission' => 6, 'am210' => 1]), - 'name' => __('Espionage'), + 'name' => __('t_ingame.fleet.mission_espionage'), ]; // Attack (only if foreign planet and not Legor). $availableMissions[] = [ 'missionType' => 1, 'link' => route('fleet.index', ['galaxy' => $galaxy, 'system' => $system, 'position' => $position, 'type' => $planet->getPlanetType()->value, 'mission' => 1]), - 'name' => __('Attack'), + 'name' => __('t_ingame.fleet.mission_attack'), ]; } @@ -335,7 +335,7 @@ private function getAvailableMissions(int $galaxy, int $system, int $position, P $availableMissions[] = [ 'missionType' => 5, 'link' => route('fleet.index', ['galaxy' => $galaxy, 'system' => $system, 'position' => $position, 'type' => $planet->getPlanetType()->value, 'mission' => 5]), - 'name' => __('ACS Defend'), + 'name' => __('t_ingame.fleet.mission_acs_defend'), ]; } @@ -344,7 +344,7 @@ private function getAvailableMissions(int $galaxy, int $system, int $position, P $availableMissions[] = [ 'missionType' => 10, 'link' => route('fleet.index', ['galaxy' => $galaxy, 'system' => $system, 'position' => $position, 'type' => $planet->getPlanetType()->value, 'mission' => 10]), - 'name' => __('Moon destruction'), + 'name' => __('t_ingame.fleet.mission_destroy_moon'), ]; } } else { @@ -352,7 +352,7 @@ private function getAvailableMissions(int $galaxy, int $system, int $position, P $availableMissions[] = [ 'missionType' => 4, 'link' => route('fleet.index', ['galaxy' => $galaxy, 'system' => $system, 'position' => $position, 'type' => $planet->getPlanetType()->value, 'mission' => 4]), - 'name' => __('Deployment'), + 'name' => __('t_ingame.fleet.mission_deploy'), ]; } @@ -406,7 +406,7 @@ private function getPlanetActions(PlanetService $planet, int $galaxy, int $syste if ($has_deuterium) { $can_phalanx = true; } else { - $phalanx_inactive_reason = 'Not enough deuterium to use phalanx'; + $phalanx_inactive_reason = __('t_ingame.galaxy.phalanx_no_deut'); } } } @@ -452,7 +452,7 @@ private function getPlanetActions(PlanetService $planet, int $galaxy, int $syste 'phalanxInactiveReason' => $phalanx_inactive_reason, 'canSendProbes' => $canEspionage, 'canWrite' => false, - 'discoveryUnlocked' => 'You haven\'t unlocked the research to discover new lifeforms yet.\n', + 'discoveryUnlocked' => __('t_galaxy.discovery.locked'), 'missileAttackLink' => $missileAttackLink, ]; } @@ -512,7 +512,7 @@ private function getPlayerInfo(PlayerService $player): array 'highscoreLink' => route('highscore.index', ['category' => 2, 'page' => $highscorePage]), 'highscoreTitle' => (string)$highscoreRank, 'infoPageLink' => route('alliance.index'), - 'infoPageTitle' => __('Alliance Page'), + 'infoPageTitle' => __('t_ingame.galaxy.alliance_page'), // Alliance class not implemented yet 'allianceClassName' => null, 'allianceClassCss' => null, @@ -521,7 +521,7 @@ private function getPlayerInfo(PlayerService $player): array ? route('alliance.index', ['alliance_id' => $alliance->id]) : null, 'applicationTitle' => (!$this->playerService->getUser()->alliance_id && $alliance->is_open) - ? __('Apply') + ? __('t_ingame.galaxy.apply') : null, ]; } @@ -550,19 +550,19 @@ private function getPlayerInfo(PlayerService $player): array 'available' => $isTargetAdmin, 'playerId' => $player->getId(), 'link' => 'javascript:void(0);', // TODO: Implement proper support contact link when messaging system is ready - 'title' => 'Contact support', + 'title' => __('t_ingame.galaxy.contact_support'), 'playerName' => $player->getUsername(), ], 'highscore' => [ 'available' => $playerRank !== null, 'rank' => $playerRank, - 'title' => 'Ranking', + 'title' => __('t_ingame.galaxy.ranking'), 'link' => route('highscore.index', ['category' => 1, 'page' => $highscorePage]), ], 'message' => [ 'available' => $isForeignPlayer && !$isTargetAdmin, 'disabledChatBar' => false, - 'title' => __('Write message'), + 'title' => __('t_ingame.highscore.write_message'), 'link' => 'javascript:void(0);', 'playerId' => $player->getId(), ], @@ -613,7 +613,7 @@ private function createEmptySpaceRow(int $galaxy, int $system, int $position): a 'moveAction' => 'prepareMove', 'moveLink' => route('planetMove.move'), 'galaxyLink' => route('galaxy.index', ['galaxy' => $galaxy, 'system' => $system]), - 'title' => 'Relocate' + 'title' => __('t_ingame.galaxy.relocate_title') ], [ 'missionType' => 7, @@ -629,10 +629,10 @@ private function createEmptySpaceRow(int $galaxy, int $system, int $position): a 'planets' => [], 'player' => [ 'playerId' => 99999, - 'playerName' => 'Deep space' + 'playerName' => __('t_ingame.fleet.deep_space') ], 'playerId' => 99999, - 'playerName' => 'Deep space', + 'playerName' => __('t_ingame.fleet.deep_space'), 'position' => $position, 'positionFilters' => 'empty_filter', 'system' => $system @@ -668,10 +668,10 @@ private function createExpeditionDebrisRow(int $galaxy, int $system, int $positi 'planets' => $debrisFieldObject, 'player' => [ 'playerId' => 99999, - 'playerName' => 'Deep space' + 'playerName' => __('t_ingame.fleet.deep_space') ], 'playerId' => 99999, - 'playerName' => 'Deep space', + 'playerName' => __('t_ingame.fleet.deep_space'), 'position' => $position, 'positionFilters' => 'expedition_debris', 'system' => $system @@ -695,7 +695,7 @@ public function ajax(Request $request, PlayerService $player, PlanetServiceFacto if ($player->isInVacationMode()) { return response()->json([ 'success' => false, - 'error' => __('You cannot use the galaxy view whilst in vacation mode!'), + 'error' => __('t_ingame.galaxy.vacation_error'), ], 403); } diff --git a/app/Http/Controllers/PremiumController.php b/app/Http/Controllers/PremiumController.php index 6b492a313..9ff50fe64 100644 --- a/app/Http/Controllers/PremiumController.php +++ b/app/Http/Controllers/PremiumController.php @@ -2,25 +2,112 @@ namespace OGame\Http\Controllers; -use Illuminate\Support\Facades\Auth; +use Exception; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; use Illuminate\View\View; +use OGame\Services\DarkMatterService; +use OGame\Services\OfficerService; +use OGame\Services\PlayerService; class PremiumController extends OGameController { + public function __construct( + private OfficerService $officerService, + private DarkMatterService $darkMatterService + ) { + parent::__construct(); + } + /** - * Shows the premium/officers index page - * - * @return View + * Shows the premium/officers index page. */ - public function index(): View + public function index(PlayerService $player): View { $this->setBodyId('premium'); - // Get current user's dark matter balance - $darkMatter = Auth::user()->dark_matter ?? 0; + $user = $player->getUser(); + $officer = $this->officerService->getOfficer($user); return view('ingame.premium.index', [ - 'darkMatter' => $darkMatter, + 'darkMatter' => $user->dark_matter, + 'officer' => $officer, ]); } + + /** + * AJAX: restituisce l'HTML del pannello dettagli per un singolo ufficiale. + * Chiamato da loadDetails() nel main layout tramite GET /ajax/premium?type=X + */ + public function ajax(Request $request, PlayerService $player): string + { + $typeId = (int) $request->input('type', 0); + $user = $player->getUser(); + $officer = $this->officerService->getOfficer($user); + + // Type 1 = Dark Matter (solo info saldo, nessun acquisto ufficiale) + if ($typeId === 1) { + return view('ingame.premium.detail-darkmatter', [ + 'darkMatter' => $user->dark_matter, + ])->render(); + } + + $officerKey = $this->officerService->getKeyFromTypeId($typeId); + if ($officerKey === null) { + return ''; + } + + $column = $officerKey . '_until'; + $isActive = $officer->isOfficerActive($officerKey); + $expiresAt = $officer->$column; + $costs = OfficerService::COSTS[$officerKey] ?? []; + + return view('ingame.premium.detail-officer', [ + 'officerKey' => $officerKey, + 'typeId' => $typeId, + 'isActive' => $isActive, + 'expiresAt' => $expiresAt, + 'costs' => $costs, + 'darkMatter' => $user->dark_matter, + 'benefitKeys' => OfficerService::BENEFIT_KEYS[$officerKey] ?? [], + ])->render(); + } + + /** + * GET: acquista/attiva un ufficiale e reindirizza alla pagina premium. + * Segue il pattern OGame originale: link diretto → acquisto → redirect. + */ + public function purchase(Request $request, PlayerService $player): RedirectResponse + { + $typeId = (int) $request->input('type'); + $days = (int) $request->input('days'); + $user = $player->getUser(); + + $officerKey = $this->officerService->getKeyFromTypeId($typeId); + if ($officerKey === null) { + return redirect()->route('premium.index') + ->with('error', 'Tipo ufficiale non valido.'); + } + + if (!in_array($days, OfficerService::DURATIONS, true)) { + return redirect()->route('premium.index') + ->with('error', 'Durata non valida.'); + } + + $cost = $this->officerService->getCost($officerKey, $days); + if (!$this->darkMatterService->canAfford($user, $cost)) { + return redirect()->route('premium.index') + ->with('error', __('t_ingame.premium.insufficient_dark_matter')); + } + + try { + $this->officerService->purchase($user, $officerKey, $days); + } catch (Exception $e) { + return redirect()->route('premium.index') + ->with('error', $e->getMessage()); + } + + return redirect()->route('premium.index') + ->with('status', __('t_ingame.premium.purchase_success')); + } } diff --git a/app/Http/Controllers/ResearchController.php b/app/Http/Controllers/ResearchController.php index 3cd504600..be46ad5d0 100644 --- a/app/Http/Controllers/ResearchController.php +++ b/app/Http/Controllers/ResearchController.php @@ -188,7 +188,7 @@ public function addBuildRequest(Request $request, PlayerService $player): JsonRe return response()->json([ 'status' => 'success', - 'message' => 'Building construction started.', + 'message' => __('t_ingame.buildings.building_started'), ]); } diff --git a/app/Http/ViewComposers/IngameMainComposer.php b/app/Http/ViewComposers/IngameMainComposer.php index 6744cb8b2..7dec41eda 100644 --- a/app/Http/ViewComposers/IngameMainComposer.php +++ b/app/Http/ViewComposers/IngameMainComposer.php @@ -16,6 +16,7 @@ use OGame\Services\FleetMissionService; use OGame\Services\HighscoreService; use OGame\Services\MessageService; +use OGame\Services\OfficerService; use OGame\Services\PlayerService; use OGame\Services\SettingsService; @@ -42,7 +43,7 @@ class IngameMainComposer * @param HighscoreService $highscoreService * @param BuddyService $buddyService */ - public function __construct(private Request $request, private PlayerService $player, private MessageService $messageService, private SettingsService $settingsService, private FleetMissionService $fleetMissionService, private HighscoreService $highscoreService, private BuddyService $buddyService, private ChatService $chatService) + public function __construct(private Request $request, private PlayerService $player, private MessageService $messageService, private SettingsService $settingsService, private FleetMissionService $fleetMissionService, private HighscoreService $highscoreService, private BuddyService $buddyService, private ChatService $chatService, private OfficerService $officerService) { } @@ -138,6 +139,7 @@ public function compose(View $view): void 'locale' => $locale, 'isImpersonating' => $isImpersonating, 'impersonateLeaveUrl' => $isImpersonating ? route('impersonate.leave') : null, + 'currentOfficer' => $this->officerService->getOfficer($this->player->getUser()), 'attackBlockActive' => $attackBlockActive, 'attackBlockUntil' => $attackBlockUntil, 'attackBlockTooltip' => $attackBlockActive diff --git a/app/Models/BuildingQueue.php b/app/Models/BuildingQueue.php index fec45b424..f4ee978ba 100644 --- a/app/Models/BuildingQueue.php +++ b/app/Models/BuildingQueue.php @@ -23,6 +23,7 @@ * @property int $building * @property int $processed * @property int $canceled + * @property bool $dm_halved * @property Carbon|null $created_at * @property Carbon|null $updated_at * @method static Builder|BuildingQueue newModelQuery() diff --git a/app/Models/Officer.php b/app/Models/Officer.php new file mode 100644 index 000000000..5fc08d6d7 --- /dev/null +++ b/app/Models/Officer.php @@ -0,0 +1,124 @@ + 'datetime', + 'admiral_until' => 'datetime', + 'engineer_until' => 'datetime', + 'geologist_until' => 'datetime', + 'technocrat_until' => 'datetime', + 'all_officers_until' => 'datetime', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function isCommanderActive(): bool + { + return $this->isOfficerActive('commander'); + } + + public function isAdmiralActive(): bool + { + return $this->isOfficerActive('admiral'); + } + + public function isEngineerActive(): bool + { + return $this->isOfficerActive('engineer'); + } + + public function isGeologistActive(): bool + { + return $this->isOfficerActive('geologist'); + } + + public function isTechnocratActive(): bool + { + return $this->isOfficerActive('technocrat'); + } + + public function isAllOfficersActive(): bool + { + return $this->isOfficerActive('all_officers'); + } + + /** + * Check if a specific officer type is active (either directly or via all_officers). + */ + public function isOfficerActive(string $type): bool + { + $column = $type . '_until'; + $directActive = $this->$column !== null && $this->$column->isFuture(); + + if ($type === 'all_officers') { + return $directActive; + } + + // Un ufficiale è attivo se attivato singolarmente O se all_officers è attivo + return $directActive || $this->isAllOfficersActive(); + } + + /** + * Activate or extend an officer for a given number of days. + */ + public function activate(string $type, int $days): void + { + $column = $type . '_until'; + $current = $this->$column; + + if ($current !== null && $current->isFuture()) { + // Estende dalla scadenza attuale (copy() evita mutazione in-place) + $this->$column = $current->copy()->addDays($days); + } else { + // Nuova attivazione da adesso + $this->$column = now()->addDays($days); + } + } + + /** + * Get the number of individually active officers (excluding all_officers slot). + */ + public function getActiveOfficerCount(): int + { + $count = 0; + foreach (['commander', 'admiral', 'engineer', 'geologist', 'technocrat'] as $type) { + if ($this->isOfficerActive($type)) { + $count++; + } + } + return $count; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 7c5030252..7672b908e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -152,6 +152,11 @@ public function tech(): HasOne return $this->hasOne(UserTech::class); } + public function officer(): HasOne + { + return $this->hasOne(Officer::class); + } + /** * Get the dark matter transactions for the user. * diff --git a/app/Services/OfficerService.php b/app/Services/OfficerService.php new file mode 100644 index 000000000..9b8ec3a94 --- /dev/null +++ b/app/Services/OfficerService.php @@ -0,0 +1,225 @@ + 'commander', + 3 => 'admiral', + 4 => 'engineer', + 5 => 'geologist', + 6 => 'technocrat', + 12 => 'all_officers', + ]; + + /** + * Costs in Dark Matter per officer per duration (days). + */ + public const COSTS = [ + 'commander' => [7 => 10000, 91 => 100000], + 'admiral' => [7 => 5000, 91 => 50000], + 'engineer' => [7 => 5000, 91 => 50000], + 'geologist' => [7 => 12500, 91 => 125000], + 'technocrat' => [7 => 10000, 91 => 100000], + 'all_officers' => [7 => 42500, 91 => 425000], + ]; + + /** + * Valid durations in days. + */ + public const DURATIONS = [7, 91]; + + /** + * Benefit translation key lists per officer (each entry = one with checkmark). + */ + public const BENEFIT_KEYS = [ + 'commander' => [ + 'officer_commander_benefit_favourites', + 'officer_commander_benefit_queue', + 'officer_commander_benefit_scanner', + 'officer_commander_benefit_ads', + ], + 'admiral' => [ + 'officer_admiral_benefit_fleet_slots', + 'officer_admiral_benefit_expeditions', + 'officer_admiral_benefit_escape', + 'officer_admiral_benefit_save_slots', + ], + 'engineer' => [ + 'officer_engineer_benefit_defence', + 'officer_engineer_benefit_energy', + ], + 'geologist' => [ + 'officer_geologist_benefit_mines', + ], + 'technocrat' => [ + 'officer_technocrat_benefit_espionage', + 'officer_technocrat_benefit_research', + ], + 'all_officers' => [ + 'officer_all_officers_benefit_fleet_slots', + 'officer_all_officers_benefit_energy', + 'officer_all_officers_benefit_mines', + 'officer_all_officers_benefit_espionage', + ], + ]; + + /** + * In-memory cache of Officer records to avoid repeated DB queries per request. + * + * @var array + */ + private array $cache = []; + + public function __construct( + private DarkMatterService $darkMatterService + ) { + } + + /** + * Get or create the Officer record for a user (cached per request). + */ + public function getOfficer(User $user): Officer + { + if (!isset($this->cache[$user->id])) { + if (empty($user->id)) { + return new Officer(); + } + $this->cache[$user->id] = Officer::firstOrCreate(['user_id' => $user->id]); + } + return $this->cache[$user->id]; + } + + /** + * Clear the in-memory cache for a user (call after purchase to refresh). + */ + public function clearCache(User $user): void + { + unset($this->cache[$user->id]); + } + + /** + * Get officer key from type ID. + */ + public function getKeyFromTypeId(int $typeId): string|null + { + return self::TYPE_MAP[$typeId] ?? null; + } + + /** + * Get the cost for an officer + duration combination. + */ + public function getCost(string $officerKey, int $days): int + { + return self::COSTS[$officerKey][$days] ?? 0; + } + + /** + * Purchase/activate an officer for a user. + * + * @throws Exception + */ + public function purchase(User $user, string $officerKey, int $days): void + { + if (!isset(self::COSTS[$officerKey])) { + throw new Exception("Invalid officer type: {$officerKey}"); + } + + if (!in_array($days, self::DURATIONS, true)) { + throw new Exception("Invalid duration: {$days}"); + } + + $cost = $this->getCost($officerKey, $days); + + // Debit dark matter (throws if insufficient) + $this->darkMatterService->debit( + $user, + $cost, + DarkMatterTransactionType::OFFICER_PURCHASE->value, + "Officer activation: {$officerKey} for {$days} days" + ); + + // Activate/extend the officer + $officer = $this->getOfficer($user); + $officer->activate($officerKey, $days); + $officer->save(); + + // Clear cache so subsequent reads reflect the update + $this->clearCache($user); + } + + /** + * Check if a specific officer is active for a user (including all_officers effect). + */ + public function isActive(User $user, string $officerKey): bool + { + $officer = $this->getOfficer($user); + return $officer->isOfficerActive($officerKey); + } + + // ── Bonus helpers ───────────────────────────────────────────────────────── + + /** Geologo: +10% produzione miniere. */ + public function getMineProductionBonus(User $user): float + { + return $this->isActive($user, 'geologist') ? 1.10 : 1.0; + } + + /** Ingegnere: +10% produzione energia. */ + public function getEnergyProductionBonus(User $user): float + { + return $this->isActive($user, 'engineer') ? 1.10 : 1.0; + } + + /** Ammiraglio: +2 slot flotta. */ + public function getAdmiralFleetSlots(User $user): int + { + return $this->isActive($user, 'admiral') ? 2 : 0; + } + + /** Ammiraglio: +1 slot spedizione. */ + public function getAdditionalExpeditionSlots(User $user): int + { + return $this->isActive($user, 'admiral') ? 1 : 0; + } + + /** Tecnico: moltiplicatore tempo ricerca (0.75 = -25%). */ + public function getResearchTimeMultiplier(User $user): float + { + return $this->isActive($user, 'technocrat') ? 0.75 : 1.0; + } + + /** Tecnico: +2 livelli spia aggiuntivi. */ + public function getAdditionalEspionageLevels(User $user): int + { + return $this->isActive($user, 'technocrat') ? 2 : 0; + } + + /** Staff di Comando: +1 slot flotta extra se tutti e 5 gli ufficiali attivi. */ + public function getCommandingStaffFleetSlots(User $user): int + { + return ($this->getOfficer($user)->getActiveOfficerCount() >= 5) ? 1 : 0; + } + + /** Staff di Comando: +1 livello spia extra se tutti e 5 gli ufficiali attivi. */ + public function getCommandingStaffEspionageLevels(User $user): int + { + return ($this->getOfficer($user)->getActiveOfficerCount() >= 5) ? 1 : 0; + } +} diff --git a/app/Services/PlanetService.php b/app/Services/PlanetService.php index 815de45cb..31fdbf2aa 100644 --- a/app/Services/PlanetService.php +++ b/app/Services/PlanetService.php @@ -24,6 +24,7 @@ use OGame\Models\Resource; use OGame\Models\Resources; use OGame\Models\UnitQueue; +use OGame\Services\OfficerService; use RuntimeException; use Throwable; @@ -1005,6 +1006,13 @@ public function getTechnologyResearchTime(string $machine_name): float $time_seconds = (int)($time_seconds * $timeMultiplier); } + // Apply Technocrat officer research time multiplier (-25%) + $officerService = app(OfficerService::class); + $technocratMultiplier = $officerService->getResearchTimeMultiplier($this->player->getUser()); + if ($technocratMultiplier != 1.0) { + $time_seconds = (int)($time_seconds * $technocratMultiplier); + } + // Minimum time is always 1 second for all objects/units. if ($time_seconds < 1) { $time_seconds = 1; diff --git a/app/Services/PlayerService.php b/app/Services/PlayerService.php index 3bc287681..64f86785c 100644 --- a/app/Services/PlayerService.php +++ b/app/Services/PlayerService.php @@ -559,7 +559,12 @@ public function getFleetSlotsMax(): int $user = $this->getUser(); $fleet_slots_bonus = $characterClassService->getAdditionalFleetSlots($user); - return $fleet_slots_from_research + $fleet_slots_bonus; + // Add officer bonuses (Admiral: +2, CommandingStaff: +1) + $officerService = app(OfficerService::class); + $officer_fleet_bonus = $officerService->getAdmiralFleetSlots($user) + + $officerService->getCommandingStaffFleetSlots($user); + + return $fleet_slots_from_research + $fleet_slots_bonus + $officer_fleet_bonus; } /** @@ -604,7 +609,11 @@ public function getExpeditionSlotsMax(): int $user = $this->getUser(); $expedition_slots_bonus = $characterClassService->getExpeditionSlotsBonus($user); - return $expedition_slots_from_research + $bonus_slots + $expedition_slots_bonus; + // Add Admiral officer bonus (+1 expedition slot) + $officerService = app(OfficerService::class); + $officer_expedition_bonus = $officerService->getAdditionalExpeditionSlots($user); + + return $expedition_slots_from_research + $bonus_slots + $expedition_slots_bonus + $officer_expedition_bonus; } /** @@ -943,32 +952,27 @@ public function isBuildingObject(string $machine_name): bool public function hasCommander(): bool { - // TODO: add logic - return false; + return app(OfficerService::class)->isActive($this->getUser(), 'commander'); } public function hasAdmiral(): bool { - // TODO: add logic - return false; + return app(OfficerService::class)->isActive($this->getUser(), 'admiral'); } public function hasEngineer(): bool { - // TODO: add logic - return false; + return app(OfficerService::class)->isActive($this->getUser(), 'engineer'); } public function hasGeologist(): bool { - // TODO: add logic - return false; + return app(OfficerService::class)->isActive($this->getUser(), 'geologist'); } public function hasTechnocrat(): bool { - // TODO: add logic - return false; + return app(OfficerService::class)->isActive($this->getUser(), 'technocrat'); } public function hasCommandingStaff(): bool diff --git a/app/ViewModels/FleetEventRowViewModel.php b/app/ViewModels/FleetEventRowViewModel.php index d8a160a10..dff3e890d 100644 --- a/app/ViewModels/FleetEventRowViewModel.php +++ b/app/ViewModels/FleetEventRowViewModel.php @@ -23,7 +23,7 @@ class FleetEventRowViewModel */ public bool $is_recallable; - public string $friendly_status; + public string $friendly_status = 'own'; public bool $has_return_trip = false; diff --git a/database/migrations/2026_04_01_000001_create_officers_table.php b/database/migrations/2026_04_01_000001_create_officers_table.php new file mode 100644 index 000000000..0f8165c47 --- /dev/null +++ b/database/migrations/2026_04_01_000001_create_officers_table.php @@ -0,0 +1,35 @@ +increments('id'); + // user_id must be unsigned int to match users.id (which uses increments/int) + $table->integer('user_id', false, true); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->dateTime('commander_until')->nullable(); + $table->dateTime('admiral_until')->nullable(); + $table->dateTime('engineer_until')->nullable(); + $table->dateTime('geologist_until')->nullable(); + $table->dateTime('technocrat_until')->nullable(); + $table->dateTime('all_officers_until')->nullable(); + $table->timestamps(); + + $table->unique('user_id'); + }); + } + + public function down(): void + { + Schema::dropIfExists('officers'); + } +}; diff --git a/database/migrations/2026_04_06_000001_add_dm_halved_to_building_queues_table.php b/database/migrations/2026_04_06_000001_add_dm_halved_to_building_queues_table.php new file mode 100644 index 000000000..1f21defcd --- /dev/null +++ b/database/migrations/2026_04_06_000001_add_dm_halved_to_building_queues_table.php @@ -0,0 +1,28 @@ +boolean('dm_halved')->default(false)->after('canceled'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('building_queues', function (Blueprint $table) { + $table->dropColumn('dm_halved'); + }); + } +}; diff --git a/resources/lang/en/t_ingame.php b/resources/lang/en/t_ingame.php index 474ca5023..aeea4d7c4 100644 --- a/resources/lang/en/t_ingame.php +++ b/resources/lang/en/t_ingame.php @@ -1336,6 +1336,43 @@ 'benefit_mines' => '+2% mine production', 'benefit_espionage_title' => '1 level will be added to your espionage research.', 'benefit_espionage' => '+1 espionage levels', + + // ── Detail panel / officer purchase ─────────────────────────────── + 'dark_matter_title' => 'Dark Matter', + 'dark_matter_label' => 'Dark Matter', + 'dark_matter_description' => 'Dark Matter is a rare substance that can only be stored with great effort. It allows you to generate large amounts of energy. The process of obtaining Dark Matter is complex and risky, making it extremely valuable.
Only purchased Dark Matter that is still available can protect against account deletion!', + 'dark_matter_benefits' => 'Dark Matter allows you to hire Officers and Commanders, pay merchant offers, move planets, and purchase items.', + 'active_for_days' => 'Active for :days more days', + 'not_active' => 'Not active', + 'days' => 'days', + 'advantages' => 'Advantages:', + 'buy_dark_matter' => 'Purchase Dark Matter', + 'insufficient_dark_matter' => 'You do not have enough Dark Matter.', + + // ── Officer titles, descriptions and tooltips ────────────────────── + 'officer_commander_title' => 'Commander', + 'officer_commander_description' => 'The Commander manages and optimizes the building queues of all your planets. He also ensures that your messages are better organized and that your empire is advertisement-free.', + 'officer_commander_tooltip' => '+40 favorites

With more favorites you can save more messages, which can then also be shared.


Building queue

Place up to 4 additional building contracts at the same time in the building queue.


Transport scanner

The number of resources that the transporter is bringing to your planet will be shown.


Advertisement free

You no longer see advertising for other games, instead only ads about OGame-specific events and offers will be shown.

', + + 'officer_admiral_title' => 'Admiral', + 'officer_admiral_description' => 'The Admiral leads your fleets into battle. Under his command, your fleets are better organized and more effective.', + 'officer_admiral_tooltip' => 'Max. fleet slots +2

You can dispatch more fleets at the same time.


Max. expeditions +1

You can dispatch one additional expedition at the same time.


Improved fleet escape rate

Until you reach 500,000 points, your fleet is able to retreat when forces are three times bigger than your own.


Max. save slots +20

You can save more combat simulations at once.

', + + 'officer_engineer_title' => 'Engineer', + 'officer_engineer_description' => 'The Engineer oversees the defense systems of your planets and ensures that they work as efficiently as possible.', + 'officer_engineer_tooltip' => 'Halves losses to defense systems

After a battle, half of all lost defense systems will be rebuilt.


+10% energy production

Your power stations and solar satellites produce 10% more energy.

', + + 'officer_geologist_title' => 'Geologist', + 'officer_geologist_description' => 'The Geologist analyzes the ground of your planets and finds ways to extract more resources.', + 'officer_geologist_tooltip' => '+10% mine production

Your mines produce 10% more.

', + + 'officer_technocrat_title' => 'Technocrat', + 'officer_technocrat_description' => 'The Technocrat researches new technologies and ensures that research projects are completed faster.', + 'officer_technocrat_tooltip' => '+2 espionage levels

2 levels will be added to your espionage research.


25% less research time

Your research requires 25% less time till completion.

', + + 'officer_all_officers_title' => 'Commanding Staff', + 'officer_all_officers_description' => 'If you hire all five officers, you will receive additional bonuses for your entire empire.', + 'officer_all_officers_tooltip' => 'Max. fleet slots +1

You can dispatch more fleets at the same time.


+2% energy production

Your power stations and solar satellites produce 2% more energy.


+2% mine production

Your mines produce 2% more.


+1 espionage levels

1 levels will be added to your espionage research.

', ], // ── Shop ──────────────────────────────────────────────────────────────── diff --git a/resources/views/ingame/admin/developershortcuts.blade.php b/resources/views/ingame/admin/developershortcuts.blade.php index 31998c9bf..336cfcf34 100644 --- a/resources/views/ingame/admin/developershortcuts.blade.php +++ b/resources/views/ingame/admin/developershortcuts.blade.php @@ -12,32 +12,53 @@
-

@lang('Developer shortcuts')

+

{{ __('t_ingame.admin.dev_title') }}

-

@lang('Developer shortcuts')

+

{{ __('t_ingame.admin.dev_title') }}

+ {{-- Masquerade as user (admin-only helper) --}} +
+ {{ csrf_field() }} +

{{ __('t_ingame.admin.dev_masquerade') }}

+
+
+ +
+ +
+
+
+ +
+
+
+
{{ csrf_field() }} -

@lang('Update current planet:')

+

{{ __('t_ingame.admin.dev_update_planet') }}

- - - - + + + +
-

@lang('Add X of unit to current planet:')

+

{{ __('t_ingame.admin.dev_add_units') }}

- +
@@ -47,14 +68,14 @@ @foreach ($units as $unit) @endforeach - +
-

@lang('Set building level on current planet:')

+

{{ __('t_ingame.admin.dev_set_building') }}

- +
@@ -66,10 +87,10 @@
-

@lang('Set research level for current player:')

+

{{ __('t_ingame.admin.dev_set_research_level') }}

- +
@@ -81,59 +102,59 @@
-

@lang('Character Class Settings')

+

{{ __('t_ingame.admin.dev_class_settings') }}

@php $freeClassChanges = app(\OGame\Services\SettingsService::class)->get('dev_free_class_changes', false); @endphp @if($freeClassChanges) - + @else - + @endif - - @lang('Go to Class Selection') + + {{ __('t_ingame.admin.dev_goto_class') }}
-

@lang('Reset planet')

+

{{ __('t_ingame.admin.dev_reset_planet') }}

- - - - + + + +
{{ csrf_field() }} -

@lang('Add / subtract resources at coordinates:')

+

{{ __('t_ingame.admin.dev_add_resources') }}

-
@lang('You can enter positive or negative values to add or subtract to the selected resource. Supports k/m/b suffixes (e.g., 1k, 2m, 3b)')
- +
{{ __('t_ingame.admin.dev_resources_desc') }}
+
- +
- +
- +
-
+
@foreach (\OGame\Models\Enums\ResourceType::cases() as $resource)
@@ -146,47 +167,47 @@ class="textInput w100 textCenter textBeefy"
- - + +
{{ csrf_field() }} -

@lang('Create planet/moon at coordinates:')

+

{{ __('t_ingame.admin.dev_create_planet_moon') }}

- +
- +
- +
- +
- +
- + Examples: 100k, 500k, 1M, 2M
- + Leave blank = random @@ -195,40 +216,40 @@ class="textInput w100 textCenter textBeefy" Formula: diameter = floor((x + 3*debris/100000)^0.5 * 1000)
- - - - + + + +
{{ csrf_field() }} -

@lang('Create/delete debris field at coordinates:')

+

{{ __('t_ingame.admin.dev_create_debris') }}

- +
- +
- +
- +
- +
@foreach (\OGame\Models\Enums\ResourceType::cases() as $resource)
@@ -241,13 +262,13 @@ class="textInput w100 textCenter textBeefy"
- - + +
-

Quick shortcut for testing Discoverer class:

+

{{ __('t_ingame.admin.dev_quick_shortcut_desc') }}

@@ -255,31 +276,31 @@ class="textInput w100 textCenter textBeefy" {{ csrf_field() }} -

@lang('Add / subtract dark matter for player at coordinates:')

+

{{ __('t_ingame.admin.dev_add_dm') }}

-
@lang('Enter positive value to add or negative value to subtract dark matter. Supports k/m/b suffixes.')
- +
{{ __('t_ingame.admin.dev_dm_desc') }}
+
- +
- +
- +
- +
- +
diff --git a/resources/views/ingame/admin/serversettings.blade.php b/resources/views/ingame/admin/serversettings.blade.php index ffa9a809d..5b25954af 100644 --- a/resources/views/ingame/admin/serversettings.blade.php +++ b/resources/views/ingame/admin/serversettings.blade.php @@ -10,63 +10,63 @@
-

@lang('Server settings')

+

{{ __('t_ingame.admin.title') }}

-

@lang('Server settings')

+

{{ __('t_ingame.admin.title') }}

{{ csrf_field() }}
-

@lang('Basic settings.')

+

{{ __('t_ingame.admin.section_basic') }}

- +
-

@lang('You can change the server settings below. Changes will be applied immediately.')

+

{{ __('t_ingame.admin.section_changes_note') }}

- +
- +
- +
- +
- +
- +
(= {{ \OGame\Facades\AppUtil::formatNumber($basic_income_metal * $economy_speed) }})
- +
(= {{ \OGame\Facades\AppUtil::formatNumber($basic_income_crystal * $economy_speed) }})
- +
(= {{ \OGame\Facades\AppUtil::formatNumber($basic_income_deuterium * $economy_speed) }})
- +
(= {{ \OGame\Facades\AppUtil::formatNumber($basic_income_energy * $economy_speed) }})
-

@lang('New player settings.')

+

{{ __('t_ingame.admin.section_new_player') }}

- +
- +
-

@lang('Dark Matter regeneration settings.')

+

{{ __('t_ingame.admin.section_dm_regen') }}

- @lang('Enable periodic Dark Matter regeneration for all players. This is disabled by default to match the official game behavior. When enabled, players will receive Dark Matter automatically at the configured interval.') + {{ __('t_ingame.admin.dm_regen_description') }}
- +
@@ -143,63 +143,63 @@
- +
- +
-

@lang('Planet relocation settings.')

+

{{ __('t_ingame.admin.section_relocation') }}

- +
- +
-

@lang('Alliance settings.')

+

{{ __('t_ingame.admin.section_alliance') }}

- +
-
@lang('Days a player must wait after leaving an alliance before joining/creating another')
+
{{ __('t_ingame.admin.alliance_cooldown_desc') }}
-

@lang('Battle settings.')

+

{{ __('t_ingame.admin.section_battle') }}

- +
-
@lang('The Rust battle engine is up to 200x more performant than PHP. Only switch to PHP if Rust cannot be run on your server.')
+
{{ __('t_ingame.admin.battle_engine_desc') }}
- +
@@ -208,7 +208,7 @@
- +
@@ -236,7 +236,7 @@
- +
@@ -245,42 +245,42 @@
- +
-
@lang('Minimum resource value that must be lost for wreck field formation.')
+
{{ __('t_ingame.admin.wreck_min_resources_desc') }}
- +
-
@lang('Minimum percentage of defender fleet that must be destroyed for wreck field formation.')
+
{{ __('t_ingame.admin.wreck_min_fleet_pct_desc') }}
- +
-
@lang('Hours before a wreck field expires if not repaired.')
+
{{ __('t_ingame.admin.wreck_lifetime_desc') }}
- +
-
@lang('Maximum time for ship repairs in the Space Dock.')
+
{{ __('t_ingame.admin.wreck_repair_max_desc') }}
- +
-
@lang('Minimum time before any ships can be repaired in the Space Dock.')
+
{{ __('t_ingame.admin.wreck_repair_min_desc') }}
- +
@@ -308,150 +308,150 @@
-

@lang('Expedition settings.')

+

{{ __('t_ingame.admin.section_expedition') }}

-

@lang('Expedition slots and reward multipliers.')

+

{{ __('t_ingame.admin.section_expedition_slots') }}

- @lang('Bonus expedition slots are added to the base slots from Astrophysics research. Reward multipliers are multiplicative with economy speed and apply to the final reward amounts (1.0 = default, 2.0 = double rewards).') + {{ __('t_ingame.admin.expedition_slots_desc') }}
- +
- +
- +
- +
- +
-

@lang('Expedition outcome weights.')

+

{{ __('t_ingame.admin.section_expedition_weights') }}

- @lang('Outcome weights determine the relative probability of each expedition result. Higher values mean more frequent occurrence. Weights are relative to each other (e.g., weight 20 is twice as likely as weight 10). Set to 0 to disable an outcome.') + {{ __('t_ingame.admin.expedition_weights_desc') }}
- @lang('Default Percentages:')
- @lang('Nothing: 25% | Resources: 35% | Ships: 17% | Delay: 7.5% | Speedup: 2.75% | Dark Matter: 7.5% | Pirates: 3% | Aliens: 1.5% | Items: 0.5% | Merchant: 0.4% | Black Hole: 0.2%') + {{ __('t_ingame.admin.expedition_weights_defaults') }}
+ {{ __('t_ingame.admin.expedition_weights_values') }}
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
-

@lang('Highscore settings.')

+

{{ __('t_ingame.admin.section_highscore') }}

- +
-
@lang('When enabled, admin users appear in highscores with orange-highlighted names. When disabled (default), admins are excluded from rankings entirely.')
+
{{ __('t_ingame.admin.highscore_admin_visible_desc') }}
-

@lang('Galaxy settings.')

+

{{ __('t_ingame.admin.section_galaxy') }}

- +
@@ -460,7 +460,7 @@
- +
@@ -469,7 +469,7 @@
- +
+
diff --git a/resources/views/ingame/ajax/object.blade.php b/resources/views/ingame/ajax/object.blade.php index 43e637d6f..67a269ee8 100644 --- a/resources/views/ingame/ajax/object.blade.php +++ b/resources/views/ingame/ajax/object.blade.php @@ -4,23 +4,23 @@
@if ($has_requirements) @else @endif @if ($object_type == \OGame\GameObjects\Models\Enums\GameObjectType::Building || $object_type == \OGame\GameObjects\Models\Enums\GameObjectType::Station || $object_type == \OGame\GameObjects\Models\Enums\GameObjectType::Research) @if (!empty($build_active_current) && $build_active_current->object->id == $object->id) - + @endif @endif
@@ -32,16 +32,16 @@
@if ($object_type === \OGame\GameObjects\Models\Enums\GameObjectType::Ship || $object_type === \OGame\GameObjects\Models\Enums\GameObjectType::Defense) - @lang('Number'): {!! $current_level !!} + {{ __('t_ingame.ajax_object.number') }}: {!! $current_level !!} @else - @lang('Level') {!! $current_level !!} + {{ __('t_ingame.ajax_object.level') }} {!! $current_level !!} @endif
    -
  • @lang('Production duration:') +
  • {{ __('t_ingame.ajax_object.production_duration') }}
-

Fleet Dispatch I - {{ $planet->getPlanetName() }}

+

{{ __('t_ingame.fleet.dispatch_1_title') }} - {{ $planet->getPlanetName() }}

@@ -802,24 +802,24 @@ class="tooltip js_hideTipOnMobile standardFleetSubmit float_right icon_link"
- Fleets: {{ $fleetSlotsInUse }}/{{ $fleetSlotsMax }} -
+ {{ __('t_ingame.fleet.fleets') }}: {{ $fleetSlotsInUse }}/{{ $fleetSlotsMax }} +
- - Expeditions: + + {{ __('t_ingame.fleet.expeditions') }}: {{ $expeditionSlotsInUse }}/{{ $expeditionSlotsMax }}

@@ -827,48 +827,28 @@ class="tooltip js_hideTipOnMobile standardFleetSubmit float_right icon_link"
- - - Tactical retreat: + + {{ __('t_ingame.fleet.tactical_retreat_label') }} - Never + {{ __('t_ingame.fleet.never') }} 5:1 + title="{{ __('t_ingame.fleet.tactical_retreat_admiral_tooltip') }}"> 3:1
-
+
- Deuterium consumption: + {{ __('t_ingame.fleet.deuterium_consumption') }}: 5
@@ -893,7 +873,7 @@ class="disabled tooltipHTML" class="missionName">{{ __('t_ingame.fleet.no_selection') }}
  • {{ __('t_ingame.fleet.target_label') }}: [{{ $planet->getPlanetCoordinates()->asString() }}]
    {{ $planet->getPlanetName() }}
  • + title="{{ $planet->isPlanet() ? __('t_ingame.fleet.planet') : __('t_ingame.fleet.moon') }}">{{ $planet->getPlanetName() }}
  • {{ __('t_ingame.fleet.player_name_label') }}: {{ $player->getUsername() }}
  • @@ -961,12 +941,12 @@ class="bonus">
    - + - + @@ -974,7 +954,7 @@ class="bonus">
    + data-overlay-inline="#zeuch666" data-overlay-title="{{ __('t_ingame.fleet.edit_standard_fleets') }}"> {{ __('t_ingame.fleet.standard_fleets') }} @@ -1017,7 +997,7 @@ class="bonus">
  • {{ __('t_ingame.fleet.mission_label') }}: {{ __('t_ingame.fleet.no_selection') }}
  • {{ __('t_ingame.fleet.target_label') }}: [{{ $planet->getPlanetCoordinates()->asString() }}]
    {{ $planet->getPlanetName() }}
    + class="planetIcon {{ $planet->isPlanet() ? 'planet' : 'moon' }} tooltip js_hideTipOnMobile" title="{{ $planet->isPlanet() ? __('t_ingame.fleet.planet') : __('t_ingame.fleet.moon') }}">{{ $planet->getPlanetName() }}
  • {{ __('t_ingame.fleet.player_name_label') }}: {{ $player->getUsername() }}
  • @@ -1118,7 +1098,7 @@ class="planet hideNumberSpin" size="2" value="10" @if ($planet_record->getPlanetId() !== $planet->getPlanetId()) @@ -1278,7 +1258,7 @@ class="icon icon_warning"> {{ __('t_ingame.fleet.bashing_disabled') }}
    -
    -
    + {{-- detailWrapper è necessario per GFSlider: currHeight = detailWrapper.offsetHeight --}} +
    +
    +
    +
    @@ -36,7 +39,7 @@
  • -
  • + @php + $officerList = [ + ['ref' => 2, 'key' => 'commander', 'css' => 'commander', 'active' => $officer->isCommanderActive(), 'title' => __('t_ingame.premium.info_commander')], + ['ref' => 3, 'key' => 'admiral', 'css' => 'admiral', 'active' => $officer->isAdmiralActive(), 'title' => __('t_ingame.premium.info_admiral')], + ['ref' => 4, 'key' => 'engineer', 'css' => 'engineer', 'active' => $officer->isEngineerActive(), 'title' => __('t_ingame.premium.info_engineer')], + ['ref' => 5, 'key' => 'geologist', 'css' => 'geologist', 'active' => $officer->isGeologistActive(), 'title' => __('t_ingame.premium.info_geologist')], + ['ref' => 6, 'key' => 'technocrat', 'css' => 'technocrat', 'active' => $officer->isTechnocratActive(), 'title' => __('t_ingame.premium.info_technocrat')], + ['ref' => 12, 'key' => 'all_officers','css' => 'allOfficers', 'active' => $officer->isAllOfficersActive(), 'title' => __('t_ingame.premium.info_commanding_staff')], + ]; + @endphp + @foreach($officerList as $off) +
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • -
    - + @if($off['ref'] === 12)
    - {{ __('t_ingame.premium.remaining_officers', ['current' => 0, 'max' => 5]) }} + {{ __('t_ingame.premium.remaining_officers', ['current' => $officer->getActiveOfficerCount(), 'max' => 5]) }}
    + @endif
  • + @endforeach -
  • +
  • {{ __('t_ingame.premium.benefit_fleet_slots') }}{{ __('t_ingame.premium.benefit_energy') }}{{ __('t_ingame.premium.benefit_mines') }}{{ __('t_ingame.premium.benefit_espionage') }}

  • @@ -136,4 +92,28 @@
    + + @endsection diff --git a/resources/views/ingame/serversettings/overlay.blade.php b/resources/views/ingame/serversettings/overlay.blade.php index ef82fa088..210c87989 100644 --- a/resources/views/ingame/serversettings/overlay.blade.php +++ b/resources/views/ingame/serversettings/overlay.blade.php @@ -6,80 +6,80 @@
    - @lang('Alliance Combat System enabled') + {{ __('t_ingame.serversettings_overlay.acs_enabled') }}
    - @lang('Dark Matter bonus:') {{ $dark_matter_bonus }} + {{ __('t_ingame.serversettings_overlay.dm_bonus') }} {{ $dark_matter_bonus }}
    - @lang('Defensive structures in debris fields:') +{{ $debris_field_from_defense }}% + {{ __('t_ingame.serversettings_overlay.debris_defense') }} +{{ $debris_field_from_defense }}%
    - @lang('Destroyed ships in debris fields:') +{{ $debris_field_from_ships }}% + {{ __('t_ingame.serversettings_overlay.debris_ships') }} +{{ $debris_field_from_ships }}%
    - @lang('Deuterium in debris fields') + {{ __('t_ingame.serversettings_overlay.debris_deuterium') }}
    - @lang('Fleet Deuterium consumption reduction:') 30% + {{ __('t_ingame.serversettings_overlay.fleet_deut_reduction') }} 30%
    - @lang('War fleet speed:') x{{ $fleet_speed_war }} + {{ __('t_ingame.serversettings_overlay.fleet_speed_war') }} x{{ $fleet_speed_war }}
    - @lang('Holding fleet speed:') x{{ $fleet_speed_holding }} + {{ __('t_ingame.serversettings_overlay.fleet_speed_holding') }} x{{ $fleet_speed_holding }}
    - @lang('Peaceful fleet speed:') x{{ $fleet_speed_peaceful }} + {{ __('t_ingame.serversettings_overlay.fleet_speed_peace') }} x{{ $fleet_speed_peaceful }}
    - @lang('Empty systems are ignored') + {{ __('t_ingame.serversettings_overlay.ignore_empty') }}
    - @lang('Inactive systems are ignored') + {{ __('t_ingame.serversettings_overlay.ignore_inactive') }}
    - @lang('Number of galaxies:') {{ $number_of_galaxies }} + {{ __('t_ingame.serversettings_overlay.num_galaxies') }} {{ $number_of_galaxies }}
    - @lang('Planet field bonus:') {{ $planet_fields_bonus }} + {{ __('t_ingame.serversettings_overlay.planet_field_bonus') }} {{ $planet_fields_bonus }}
    - @lang('Development speed:') x{{ $economy_speed }} + {{ __('t_ingame.serversettings_overlay.dev_speed') }} x{{ $economy_speed }}
    - @lang('Research speed:') x{{ $research_speed }} + {{ __('t_ingame.serversettings_overlay.research_speed') }} x{{ $research_speed }}
    - @lang('Dark Matter regeneration enabled') + {{ __('t_ingame.serversettings_overlay.dm_regen_enabled') }}
    @if($dark_matter_regen_enabled)
    - @lang('Dark Matter regeneration amount:') {{ number_format($dark_matter_regen_amount) }} + {{ __('t_ingame.serversettings_overlay.dm_regen_amount') }} {{ number_format($dark_matter_regen_amount) }}
    - @lang('Dark Matter regeneration period:') {{ number_format($dark_matter_regen_period / 86400) }} @lang('days') + {{ __('t_ingame.serversettings_overlay.dm_regen_period') }} {{ number_format($dark_matter_regen_period / 86400) }} {{ __('t_ingame.serversettings_overlay.days') }}
    @endif \ No newline at end of file diff --git a/resources/views/ingame/shared/buildqueue/unit-active.blade.php b/resources/views/ingame/shared/buildqueue/unit-active.blade.php index 19402f41d..4a12f5910 100644 --- a/resources/views/ingame/shared/buildqueue/unit-active.blade.php +++ b/resources/views/ingame/shared/buildqueue/unit-active.blade.php @@ -18,7 +18,7 @@ - @lang('Building duration') + {{ __('t_ingame.buildqueue.building_duration') }} @@ -27,7 +27,7 @@ - @lang('Total time'): + {{ __('t_ingame.buildqueue.total_time') }}: @@ -44,21 +44,21 @@ @endphp @if ($wouldComplete) -
    - @lang('Complete') - @lang('Costs: :amount DM', ['amount' => number_format($halvingCost)]) +
    + {{ __('t_ingame.buildqueue.complete') }} + {{ __('t_ingame.buildqueue.halve_cost', ['amount' => number_format($halvingCost)]) }}
    @else -
    - @lang('Halve time') - @lang('Costs: :amount DM', ['amount' => number_format($halvingCost)]) +
    + {{ __('t_ingame.buildqueue.halve_time') }} + {{ __('t_ingame.buildqueue.halve_cost', ['amount' => number_format($halvingCost)]) }}
    @endif @@ -68,9 +68,9 @@