From 0c81ba2a61df3a6902fa063c68894e1ec4db0daf Mon Sep 17 00:00:00 2001 From: tashcan Date: Thu, 15 Oct 2026 16:40:41 -0700 Subject: [PATCH 01/24] Collapse game bindings into domain headers About 120 single-class headers under prime become ten headers split by what they model: chat, fleet, inventory, navigation, server, UI, Unity, viewers and a few more. Binding bodies are unchanged; only includes move, and bindings nobody used are gone. String-view trimming now respects explicit lengths, so a trimmed ship name no longer keeps trailing text. --- mods/src/config.cc | 2 +- mods/src/errormsg.h | 5 - mods/src/file.h | 2 +- mods/src/patches/battle_notify_parser.cc | 9 +- mods/src/patches/fleet_watch.cc | 2 +- mods/src/patches/fleet_watch.h | 2 +- mods/src/patches/key.cc | 4 +- mods/src/patches/notification_service.cc | 4 +- mods/src/patches/parts/alliance_store.cc | 9 +- mods/src/patches/parts/artifact_exchange.cc | 2 +- mods/src/patches/parts/buff_fixes.cc | 4 +- mods/src/patches/parts/cargo_format.cc | 2 +- mods/src/patches/parts/chat.cc | 6 +- mods/src/patches/parts/chat_titles.cc | 6 +- .../patches/parts/daily_faction_bulk_claim.cc | 5 +- mods/src/patches/parts/disable_banners.cc | 2 +- .../patches/parts/double_click_assign_ship.cc | 4 +- .../patches/parts/double_click_assign_ship.h | 2 +- mods/src/patches/parts/fix_pan.cc | 8 +- mods/src/patches/parts/focus_search.cc | 9 +- .../patches/parts/forbidden_tech_ship_name.cc | 11 +- mods/src/patches/parts/free_resize.cc | 10 +- .../patches/parts/galactic_anomaly_timer.cc | 29 +- mods/src/patches/parts/haven_zoom.cc | 3 +- mods/src/patches/parts/hotkeys.cc | 45 +- mods/src/patches/parts/instant_cargo.cc | 2 +- .../parts/instant_warp_confirmation.cc | 4 +- .../src/patches/parts/loading_screen_common.h | 9 +- mods/src/patches/parts/misc.cc | 13 +- mods/src/patches/parts/mission_hud_tweaks.cc | 2 +- mods/src/patches/parts/object_tracker.cc | 27 +- .../patches/parts/open_bulk_claim_gifts.cc | 2 +- mods/src/patches/parts/pinned_ship_click.cc | 8 +- mods/src/patches/parts/pinned_ship_click.h | 2 +- mods/src/patches/parts/pinned_ship_sort.cc | 3 +- mods/src/patches/parts/reveal_confirmation.cc | 2 +- mods/src/patches/parts/ship_tile_click.cc | 2 +- mods/src/patches/parts/sync.cc | 50 +- mods/src/patches/parts/testing.cc | 24 +- mods/src/patches/parts/ui_scale.cc | 10 +- mods/src/patches/parts/warp_action_label.cc | 7 +- mods/src/patches/parts/zoom.cc | 8 +- mods/src/prime/ActionData.h | 69 -- mods/src/prime/ActionQueueManager.h | 25 - mods/src/prime/ActionRequirement.h | 12 - mods/src/prime/ActivatedAbilityManager.h | 12 - .../AllianceStarbaseObjectViewerWidget.h | 7 - .../AnimatedRewardsScreenViewController.h | 14 - mods/src/prime/ArmadaObjectViewerWidget.h | 21 - .../prime/ArtifactHallDetailsViewController.h | 53 -- mods/src/prime/AspectRatioConstraintHandler.h | 14 - mods/src/prime/AssignShipsWidget.h | 20 - mods/src/prime/BattleResultHeader.h | 55 -- mods/src/prime/BattleTargetData.h | 12 - mods/src/prime/BlurController.h | 19 - mods/src/prime/BookmarksManager.h | 41 -- mods/src/prime/Bundle.h | 7 - mods/src/prime/BundleDataWidget.h | 38 -- mods/src/prime/Button.h | 10 - mods/src/prime/ButtonAndContextWrapper.h | 12 - mods/src/prime/Camera.h | 27 - mods/src/prime/CanRepairRequirement.h | 8 - mods/src/prime/Canvas.h | 12 - mods/src/prime/CanvasController.h | 37 - mods/src/prime/CanvasScaler.h | 23 - mods/src/prime/CelestialObjectViewerWidget.h | 7 - mods/src/prime/ChatManager.h | 50 -- .../ChatMessageListLocalViewController.h | 23 - mods/src/prime/ChatMessageWidget.h | 8 - mods/src/prime/ChatPreviewController.h | 31 - mods/src/prime/ClientModifierType.h | 150 ---- mods/src/prime/Color.h | 7 - mods/src/prime/Component.h | 16 - mods/src/prime/CourseData.h | 12 - mods/src/prime/CoursePromptPopupWidget.h | 37 - mods/src/prime/DeploymentManager.h | 50 -- .../src/prime/ElementSelectorViewController.h | 44 -- mods/src/prime/EmbassyObjectViewer.h | 7 - mods/src/prime/EventSystem.h | 17 - mods/src/prime/FleetBarViewController.h | 32 - mods/src/prime/FleetDeployedData.h | 29 - mods/src/prime/FleetLiveData.h | 73 -- mods/src/prime/FleetLocalViewController.h | 29 - mods/src/prime/FleetMeshSelector.h | 12 - mods/src/prime/FleetPlayerData.h | 147 ---- mods/src/prime/FleetsManager.h | 51 -- mods/src/prime/FullScreenChatViewController.h | 15 - mods/src/prime/GameObject.h | 34 - mods/src/prime/GenericButtonContext.h | 12 - mods/src/prime/GenericButtonWidget.h | 17 - .../GenericRewardsScreenViewController.h | 12 - mods/src/prime/HousingObjectViewerWidget.h | 7 - mods/src/prime/HttpJob.h | 33 - mods/src/prime/HttpRequest.h | 13 - mods/src/prime/HttpResponse.h | 14 - mods/src/prime/HullSpec.h | 39 -- mods/src/prime/IBuffComparer.h | 15 - mods/src/prime/IBuffData.h | 12 - mods/src/prime/IEnumerator.h | 6 - mods/src/prime/Image.h | 48 -- mods/src/prime/InputFieldWidget.h | 17 - mods/src/prime/InterstitialViewController.h | 10 - mods/src/prime/InventoryForPopup.h | 20 - mods/src/prime/InventoryListViewController.h | 20 - mods/src/prime/InventoryUseRowWidget.h | 8 - mods/src/prime/LanguageManager.h | 25 - mods/src/prime/LocaleTextContext.h | 31 - mods/src/prime/MiningObjectViewerWidget.h | 27 - mods/src/prime/MissionsObjectViewerWidget.h | 7 - mods/src/prime/MonoSingleton.h | 12 - mods/src/prime/NavigationFleetWidget.h | 12 - .../prime/NavigationInteractionUIContext.h | 7 - .../NavigationInteractionUIViewController.h | 46 -- mods/src/prime/NavigationLOD.h | 16 - mods/src/prime/NavigationManager.h | 10 - mods/src/prime/NavigationPan.h | 48 -- mods/src/prime/NavigationSectionManager.h | 22 - mods/src/prime/NavigationZoom.h | 95 --- mods/src/prime/ObjectViewerBaseWidget.h | 22 - .../prime/OfficerAssignmentViewController.h | 20 - mods/src/prime/OrbitFrameProvider.h | 13 - .../prime/ParentObjectViewerViewController.h | 21 - mods/src/prime/PlanetViewUtils.h | 19 - mods/src/prime/PlanetaryBaseManager.h | 26 - mods/src/prime/PopulatedSystemData.h | 14 - mods/src/prime/PreScanStationTargetWidget.h | 7 - mods/src/prime/PreScanTargetWidget.h | 30 - mods/src/prime/RealtimeDataPayload.h | 32 - mods/src/prime/RecallRequirement.h | 8 - mods/src/prime/RectTransform.h | 33 - mods/src/prime/RewardsButtonWidget.h | 15 - mods/src/prime/ScanEngageButtonsWidget.h | 19 - mods/src/prime/ScanTargetViewController.h | 12 - mods/src/prime/SceneArgs.h | 5 - mods/src/prime/SceneManager.h | 25 - mods/src/prime/ScreenManager.h | 32 - mods/src/prime/SelectableList.h | 33 - mods/src/prime/SemaphoreButtonListener.h | 17 - mods/src/prime/SemaphoreListenerBase.h | 7 - mods/src/prime/ServiceResponse.h | 63 -- .../ShipActionPanelLocalViewController.h | 20 - .../prime/ShipBarItemLocalViewController.h | 7 - .../ShipManagementScreenStateViewController.h | 16 - mods/src/prime/ShipManagementViewContext.h | 26 - mods/src/prime/ShipManagementViewController.h | 14 - mods/src/prime/ShipTileWidget.h | 8 - mods/src/prime/ShopSummaryDirector.h | 13 - mods/src/prime/ShortcutsManager.h | 15 - mods/src/prime/SpecService.h | 14 - mods/src/prime/StarNodeObjectViewerWidget.h | 15 - mods/src/prime/StateContainer.h | 14 - mods/src/prime/SystemViewScaler.h | 12 - mods/src/prime/TKTouch.h | 22 - mods/src/prime/TMP_InputField.h | 19 - mods/src/prime/TabBarViewController.h | 20 - mods/src/prime/TextLocalizer.h | 35 - mods/src/prime/Toast.h | 79 --- mods/src/prime/Transform.h | 32 - mods/src/prime/TransitionManager.h | 19 - mods/src/prime/TransitionViewController.h | 17 - mods/src/prime/UIBehaviour.h | 7 - mods/src/prime/UnityWebRequest.h | 12 - mods/src/prime/UserProfileManager.h | 2 +- mods/src/prime/UserProfileWidget.h | 10 - mods/src/prime/Vector2.h | 17 - mods/src/prime/Vector3.h | 14 - mods/src/prime/ViewController.h | 9 - mods/src/prime/VisibilityController.h | 29 - .../src/prime/WaveDefenseObjectViewerWidget.h | 35 - mods/src/prime/Widget.h | 15 - mods/src/prime/ZoomLevels.h | 8 - mods/src/prime/chat.h | 118 ++++ mods/src/prime/fleet.h | 639 ++++++++++++++++++ mods/src/prime/inventory.h | 97 +++ mods/src/prime/navigation.h | 327 +++++++++ mods/src/prime/{EntityGroup.h => server.h} | 376 +++++++++++ mods/src/prime/ui.h | 471 +++++++++++++ mods/src/prime/unity.h | 267 ++++++++ mods/src/prime/viewers.h | 187 +++++ mods/src/prime_types.h | 111 --- mods/src/ship_name_match.h | 5 +- mods/src/str_utils.h | 171 ++--- 182 files changed, 2654 insertions(+), 3615 deletions(-) delete mode 100644 mods/src/prime/ActionData.h delete mode 100644 mods/src/prime/ActionQueueManager.h delete mode 100644 mods/src/prime/ActionRequirement.h delete mode 100644 mods/src/prime/ActivatedAbilityManager.h delete mode 100644 mods/src/prime/AllianceStarbaseObjectViewerWidget.h delete mode 100644 mods/src/prime/AnimatedRewardsScreenViewController.h delete mode 100644 mods/src/prime/ArmadaObjectViewerWidget.h delete mode 100644 mods/src/prime/ArtifactHallDetailsViewController.h delete mode 100644 mods/src/prime/AspectRatioConstraintHandler.h delete mode 100644 mods/src/prime/AssignShipsWidget.h delete mode 100644 mods/src/prime/BattleResultHeader.h delete mode 100644 mods/src/prime/BattleTargetData.h delete mode 100644 mods/src/prime/BlurController.h delete mode 100644 mods/src/prime/BookmarksManager.h delete mode 100644 mods/src/prime/Bundle.h delete mode 100644 mods/src/prime/BundleDataWidget.h delete mode 100644 mods/src/prime/Button.h delete mode 100644 mods/src/prime/ButtonAndContextWrapper.h delete mode 100644 mods/src/prime/Camera.h delete mode 100644 mods/src/prime/CanRepairRequirement.h delete mode 100644 mods/src/prime/Canvas.h delete mode 100644 mods/src/prime/CanvasController.h delete mode 100644 mods/src/prime/CanvasScaler.h delete mode 100644 mods/src/prime/CelestialObjectViewerWidget.h delete mode 100644 mods/src/prime/ChatManager.h delete mode 100644 mods/src/prime/ChatMessageListLocalViewController.h delete mode 100644 mods/src/prime/ChatMessageWidget.h delete mode 100644 mods/src/prime/ChatPreviewController.h delete mode 100644 mods/src/prime/ClientModifierType.h delete mode 100644 mods/src/prime/Color.h delete mode 100644 mods/src/prime/Component.h delete mode 100644 mods/src/prime/CourseData.h delete mode 100644 mods/src/prime/CoursePromptPopupWidget.h delete mode 100644 mods/src/prime/DeploymentManager.h delete mode 100644 mods/src/prime/ElementSelectorViewController.h delete mode 100644 mods/src/prime/EmbassyObjectViewer.h delete mode 100644 mods/src/prime/EventSystem.h delete mode 100644 mods/src/prime/FleetBarViewController.h delete mode 100644 mods/src/prime/FleetDeployedData.h delete mode 100644 mods/src/prime/FleetLiveData.h delete mode 100644 mods/src/prime/FleetLocalViewController.h delete mode 100644 mods/src/prime/FleetMeshSelector.h delete mode 100644 mods/src/prime/FleetPlayerData.h delete mode 100644 mods/src/prime/FleetsManager.h delete mode 100644 mods/src/prime/FullScreenChatViewController.h delete mode 100644 mods/src/prime/GameObject.h delete mode 100644 mods/src/prime/GenericButtonContext.h delete mode 100644 mods/src/prime/GenericButtonWidget.h delete mode 100644 mods/src/prime/GenericRewardsScreenViewController.h delete mode 100644 mods/src/prime/HousingObjectViewerWidget.h delete mode 100644 mods/src/prime/HttpJob.h delete mode 100644 mods/src/prime/HttpRequest.h delete mode 100644 mods/src/prime/HttpResponse.h delete mode 100644 mods/src/prime/HullSpec.h delete mode 100644 mods/src/prime/IBuffComparer.h delete mode 100644 mods/src/prime/IBuffData.h delete mode 100644 mods/src/prime/IEnumerator.h delete mode 100644 mods/src/prime/Image.h delete mode 100644 mods/src/prime/InputFieldWidget.h delete mode 100644 mods/src/prime/InterstitialViewController.h delete mode 100644 mods/src/prime/InventoryForPopup.h delete mode 100644 mods/src/prime/InventoryListViewController.h delete mode 100644 mods/src/prime/InventoryUseRowWidget.h delete mode 100644 mods/src/prime/LanguageManager.h delete mode 100644 mods/src/prime/LocaleTextContext.h delete mode 100644 mods/src/prime/MiningObjectViewerWidget.h delete mode 100644 mods/src/prime/MissionsObjectViewerWidget.h delete mode 100644 mods/src/prime/MonoSingleton.h delete mode 100644 mods/src/prime/NavigationFleetWidget.h delete mode 100644 mods/src/prime/NavigationInteractionUIContext.h delete mode 100644 mods/src/prime/NavigationInteractionUIViewController.h delete mode 100644 mods/src/prime/NavigationLOD.h delete mode 100644 mods/src/prime/NavigationManager.h delete mode 100644 mods/src/prime/NavigationPan.h delete mode 100644 mods/src/prime/NavigationSectionManager.h delete mode 100644 mods/src/prime/NavigationZoom.h delete mode 100644 mods/src/prime/ObjectViewerBaseWidget.h delete mode 100644 mods/src/prime/OfficerAssignmentViewController.h delete mode 100644 mods/src/prime/OrbitFrameProvider.h delete mode 100644 mods/src/prime/ParentObjectViewerViewController.h delete mode 100644 mods/src/prime/PlanetViewUtils.h delete mode 100644 mods/src/prime/PlanetaryBaseManager.h delete mode 100644 mods/src/prime/PopulatedSystemData.h delete mode 100644 mods/src/prime/PreScanStationTargetWidget.h delete mode 100644 mods/src/prime/PreScanTargetWidget.h delete mode 100644 mods/src/prime/RealtimeDataPayload.h delete mode 100644 mods/src/prime/RecallRequirement.h delete mode 100644 mods/src/prime/RectTransform.h delete mode 100644 mods/src/prime/RewardsButtonWidget.h delete mode 100644 mods/src/prime/ScanEngageButtonsWidget.h delete mode 100644 mods/src/prime/ScanTargetViewController.h delete mode 100644 mods/src/prime/SceneArgs.h delete mode 100644 mods/src/prime/SceneManager.h delete mode 100644 mods/src/prime/ScreenManager.h delete mode 100644 mods/src/prime/SelectableList.h delete mode 100644 mods/src/prime/SemaphoreButtonListener.h delete mode 100644 mods/src/prime/SemaphoreListenerBase.h delete mode 100644 mods/src/prime/ServiceResponse.h delete mode 100644 mods/src/prime/ShipActionPanelLocalViewController.h delete mode 100644 mods/src/prime/ShipBarItemLocalViewController.h delete mode 100644 mods/src/prime/ShipManagementScreenStateViewController.h delete mode 100644 mods/src/prime/ShipManagementViewContext.h delete mode 100644 mods/src/prime/ShipManagementViewController.h delete mode 100644 mods/src/prime/ShipTileWidget.h delete mode 100644 mods/src/prime/ShopSummaryDirector.h delete mode 100644 mods/src/prime/ShortcutsManager.h delete mode 100644 mods/src/prime/SpecService.h delete mode 100644 mods/src/prime/StarNodeObjectViewerWidget.h delete mode 100644 mods/src/prime/StateContainer.h delete mode 100644 mods/src/prime/SystemViewScaler.h delete mode 100644 mods/src/prime/TKTouch.h delete mode 100644 mods/src/prime/TMP_InputField.h delete mode 100644 mods/src/prime/TabBarViewController.h delete mode 100644 mods/src/prime/TextLocalizer.h delete mode 100644 mods/src/prime/Toast.h delete mode 100644 mods/src/prime/Transform.h delete mode 100644 mods/src/prime/TransitionManager.h delete mode 100644 mods/src/prime/TransitionViewController.h delete mode 100644 mods/src/prime/UIBehaviour.h delete mode 100644 mods/src/prime/UnityWebRequest.h delete mode 100644 mods/src/prime/UserProfileWidget.h delete mode 100644 mods/src/prime/Vector2.h delete mode 100644 mods/src/prime/Vector3.h delete mode 100644 mods/src/prime/ViewController.h delete mode 100644 mods/src/prime/VisibilityController.h delete mode 100644 mods/src/prime/WaveDefenseObjectViewerWidget.h delete mode 100644 mods/src/prime/Widget.h delete mode 100644 mods/src/prime/ZoomLevels.h create mode 100644 mods/src/prime/chat.h create mode 100644 mods/src/prime/fleet.h create mode 100644 mods/src/prime/inventory.h create mode 100644 mods/src/prime/navigation.h rename mods/src/prime/{EntityGroup.h => server.h} (52%) create mode 100644 mods/src/prime/ui.h create mode 100644 mods/src/prime/unity.h create mode 100644 mods/src/prime/viewers.h delete mode 100644 mods/src/prime_types.h diff --git a/mods/src/config.cc b/mods/src/config.cc index 367441c34..2928b0076 100644 --- a/mods/src/config.cc +++ b/mods/src/config.cc @@ -5,7 +5,7 @@ #include "ship_name_match.h" #include "str_utils.h" #include "version.h" -#include +#include #include #include diff --git a/mods/src/errormsg.h b/mods/src/errormsg.h index acaa9d7f0..a32af946d 100644 --- a/mods/src/errormsg.h +++ b/mods/src/errormsg.h @@ -15,11 +15,6 @@ static void SyncMsg(const char* section, const std::string& msg) spdlog::error("Failed to send {} sync data: {}", section, msg); } -static void SyncMsg(const char* section, const std::wstring& msg) -{ - spdlog::error("Failed to send {} sync data: {}", section, to_string(msg)); -} - static void SyncRuntime(const char* section, const std::runtime_error& e) { spdlog::error("Runtime error sending {} sync data: {}", section, e.what()); diff --git a/mods/src/file.h b/mods/src/file.h index ce07efbfc..8adcf99e0 100644 --- a/mods/src/file.h +++ b/mods/src/file.h @@ -3,7 +3,7 @@ #include "prime/KeyCode.h" #include "str_utils.h" #include "version.h" -#include +#include #include #include diff --git a/mods/src/patches/battle_notify_parser.cc b/mods/src/patches/battle_notify_parser.cc index 903824a67..92003e5d9 100644 --- a/mods/src/patches/battle_notify_parser.cc +++ b/mods/src/patches/battle_notify_parser.cc @@ -3,12 +3,9 @@ #include "str_utils.h" #include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include diff --git a/mods/src/patches/fleet_watch.cc b/mods/src/patches/fleet_watch.cc index e4189c28d..472466711 100644 --- a/mods/src/patches/fleet_watch.cc +++ b/mods/src/patches/fleet_watch.cc @@ -3,7 +3,7 @@ #include "errormsg.h" #include "patches/screen_update_hook.h" -#include +#include #include diff --git a/mods/src/patches/fleet_watch.h b/mods/src/patches/fleet_watch.h index 4e590f7c1..071ac1ef1 100644 --- a/mods/src/patches/fleet_watch.h +++ b/mods/src/patches/fleet_watch.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace fleet_watch { diff --git a/mods/src/patches/key.cc b/mods/src/patches/key.cc index 71c3b95d5..d12bc16af 100644 --- a/mods/src/patches/key.cc +++ b/mods/src/patches/key.cc @@ -1,7 +1,7 @@ #include "key.h" -#include "prime/EventSystem.h" +#include "prime/unity.h" #include "str_utils.h" -#include +#include #include #include diff --git a/mods/src/patches/notification_service.cc b/mods/src/patches/notification_service.cc index e4f796d18..7b827423b 100644 --- a/mods/src/patches/notification_service.cc +++ b/mods/src/patches/notification_service.cc @@ -5,9 +5,7 @@ #include "str_utils.h" #include -#include -#include -#include +#include #include diff --git a/mods/src/patches/parts/alliance_store.cc b/mods/src/patches/parts/alliance_store.cc index cdcec5867..2de77a391 100644 --- a/mods/src/patches/parts/alliance_store.cc +++ b/mods/src/patches/parts/alliance_store.cc @@ -5,13 +5,10 @@ #include #include -#include -#include -#include #include -#include -#include -#include +#include +#include +#include #include #if _WIN32 diff --git a/mods/src/patches/parts/artifact_exchange.cc b/mods/src/patches/parts/artifact_exchange.cc index 7335a728e..b1dcb7a1d 100644 --- a/mods/src/patches/parts/artifact_exchange.cc +++ b/mods/src/patches/parts/artifact_exchange.cc @@ -2,7 +2,7 @@ #include "config.h" -#include "prime/Component.h" +#include "prime/unity.h" #include #include diff --git a/mods/src/patches/parts/buff_fixes.cc b/mods/src/patches/parts/buff_fixes.cc index 89e1bedbf..997479525 100644 --- a/mods/src/patches/parts/buff_fixes.cc +++ b/mods/src/patches/parts/buff_fixes.cc @@ -1,9 +1,7 @@ #include "config.h" -#include "prime_types.h" #include -#include -#include +#include #include diff --git a/mods/src/patches/parts/cargo_format.cc b/mods/src/patches/parts/cargo_format.cc index ab5092f1e..ed399e5b5 100644 --- a/mods/src/patches/parts/cargo_format.cc +++ b/mods/src/patches/parts/cargo_format.cc @@ -1,5 +1,5 @@ #include "config.h" -#include "prime/TextLocalizer.h" +#include "prime/ui.h" #include #include diff --git a/mods/src/patches/parts/chat.cc b/mods/src/patches/parts/chat.cc index de2ea8d18..9f38abd8d 100644 --- a/mods/src/patches/parts/chat.cc +++ b/mods/src/patches/parts/chat.cc @@ -1,6 +1,5 @@ -#include "prime/ChatPreviewController.h" -#include "prime/FullScreenChatViewController.h" -#include "prime/GenericButtonContext.h" +#include "prime/chat.h" +#include "prime/ui.h" #include "config.h" @@ -13,7 +12,6 @@ #include "patches/chat_startup.h" #include "patches/screen_update_hook.h" #include "prime/Hub.h" -#include "prime/ScreenManager.h" namespace { diff --git a/mods/src/patches/parts/chat_titles.cc b/mods/src/patches/parts/chat_titles.cc index 2e97e6cba..7e336888b 100644 --- a/mods/src/patches/parts/chat_titles.cc +++ b/mods/src/patches/parts/chat_titles.cc @@ -1,6 +1,6 @@ -#include "prime/ChatMessageWidget.h" -#include "prime/GameObject.h" -#include "prime/UserProfileWidget.h" +#include "prime/chat.h" +#include "prime/unity.h" +#include "prime/chat.h" #include "config.h" #include "errormsg.h" diff --git a/mods/src/patches/parts/daily_faction_bulk_claim.cc b/mods/src/patches/parts/daily_faction_bulk_claim.cc index 426c9cba4..623335309 100644 --- a/mods/src/patches/parts/daily_faction_bulk_claim.cc +++ b/mods/src/patches/parts/daily_faction_bulk_claim.cc @@ -1,10 +1,7 @@ #include "config.h" #include "str_utils.h" -#include -#include -#include -#include +#include #include #include diff --git a/mods/src/patches/parts/disable_banners.cc b/mods/src/patches/parts/disable_banners.cc index e7be0daf1..b9d45c556 100644 --- a/mods/src/patches/parts/disable_banners.cc +++ b/mods/src/patches/parts/disable_banners.cc @@ -2,7 +2,7 @@ #include "patches/notification_service.h" #include -#include +#include #include diff --git a/mods/src/patches/parts/double_click_assign_ship.cc b/mods/src/patches/parts/double_click_assign_ship.cc index 9a2058d8a..0ddaac71d 100644 --- a/mods/src/patches/parts/double_click_assign_ship.cc +++ b/mods/src/patches/parts/double_click_assign_ship.cc @@ -3,8 +3,8 @@ #include "patches/object_tracker.h" #include "double_click_assign_ship.h" -#include "prime/AssignShipsWidget.h" -#include "prime/CanvasController.h" +#include "prime/fleet.h" +#include "prime/ui.h" #include "prime/KeyCode.h" #include "ship_tile_click.h" diff --git a/mods/src/patches/parts/double_click_assign_ship.h b/mods/src/patches/parts/double_click_assign_ship.h index 1ee8bff98..78eb81f51 100644 --- a/mods/src/patches/parts/double_click_assign_ship.h +++ b/mods/src/patches/parts/double_click_assign_ship.h @@ -1,6 +1,6 @@ #pragma once -#include "prime/FleetPlayerData.h" +#include "prime/fleet.h" void AssignShipEnterKeyUpdate(); void DoubleClickOnShipTileClicked(FleetPlayerData* ship); diff --git a/mods/src/patches/parts/fix_pan.cc b/mods/src/patches/parts/fix_pan.cc index 2956718f1..54b98ab7d 100644 --- a/mods/src/patches/parts/fix_pan.cc +++ b/mods/src/patches/parts/fix_pan.cc @@ -2,12 +2,10 @@ #include -#include +#include #include -#include -#include -#include -#include +#include +#include #include #include diff --git a/mods/src/patches/parts/focus_search.cc b/mods/src/patches/parts/focus_search.cc index 076ca5a4d..e69428f35 100644 --- a/mods/src/patches/parts/focus_search.cc +++ b/mods/src/patches/parts/focus_search.cc @@ -3,11 +3,10 @@ #include "config.h" -#include "prime/AssignShipsWidget.h" -#include "prime/CanvasController.h" -#include "prime/ChatMessageListLocalViewController.h" -#include "prime/InventoryListViewController.h" -#include "prime/OfficerAssignmentViewController.h" +#include "prime/fleet.h" +#include "prime/ui.h" +#include "prime/chat.h" +#include "prime/inventory.h" #include diff --git a/mods/src/patches/parts/forbidden_tech_ship_name.cc b/mods/src/patches/parts/forbidden_tech_ship_name.cc index eff78d869..18e2ff2f8 100644 --- a/mods/src/patches/parts/forbidden_tech_ship_name.cc +++ b/mods/src/patches/parts/forbidden_tech_ship_name.cc @@ -1,14 +1,9 @@ #include "config.h" #include "ship_name_match.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include diff --git a/mods/src/patches/parts/free_resize.cc b/mods/src/patches/parts/free_resize.cc index 81706ab0b..fbb7a4fb0 100644 --- a/mods/src/patches/parts/free_resize.cc +++ b/mods/src/patches/parts/free_resize.cc @@ -6,9 +6,17 @@ #include -#include "prime/AspectRatioConstraintHandler.h" #include "windowtitle.h" +#include + +struct AspectRatioConstraintHandler { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Utils", "AspectRatioConstraintHandler">; + + static WNDPROC _unityWndProc() + { return reinterpret_cast(Class::Field<"_unityWndProc", intptr_t>()); } +}; + static bool WndProcInstalled = false; static LONG_PTR oWndProc = NULL; static WINDOWPLACEMENT g_wpPrev = {sizeof(g_wpPrev)}; diff --git a/mods/src/patches/parts/galactic_anomaly_timer.cc b/mods/src/patches/parts/galactic_anomaly_timer.cc index b1727ff9b..d759f7fd2 100644 --- a/mods/src/patches/parts/galactic_anomaly_timer.cc +++ b/mods/src/patches/parts/galactic_anomaly_timer.cc @@ -1,15 +1,8 @@ #include "config.h" #include "patches/screen_update_hook.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include @@ -90,7 +83,7 @@ struct HudAllianceAndNewsViewControllerContext : il2cpp::Object { using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "HudAllianceAndNewsViewControllerContext">; }; -struct GalacticAnomaliesManager : MonoSingleton { +struct GalacticAnomaliesManager : il2cpp::Object { using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.GalacticAnomalies", "GalacticAnomaliesManager">; // Digit.PrimeServer.Models.GalacticAnomaly, or null. @@ -104,6 +97,12 @@ bool Alive(Il2CppObject* object) void Destroy(Il2CppObject* object) { UnityObject::Call<"Destroy">(object); } +template T* Find() +{ + auto* type = T::Class::TypeObject(); + return type ? static_cast(UnityObject::Call<"FindObjectOfType", Il2CppObject*>(type)) : nullptr; +} + // The first active T on `root`'s GameObject or below it. Scoped to one hierarchy, unlike FindObjectOfType, which // scans every loaded object. template T* FindInChildren(il2cpp::Object* root) @@ -303,9 +302,9 @@ int64_t RemainingTicks(HudNavigationViewController* nav) // Cache the Unity object, not its timer data. Unity's liveness check detects // destruction even while the managed wrapper is retained by our GC handle. if (!Alive(anomalyManager.Get())) - anomalyManager = il2cpp::GcHandle::Strong(GalacticAnomaliesManager::Existing()); + anomalyManager = il2cpp::GcHandle::Strong(Find()); auto* manager = anomalyManager.Get(); - auto* anomaly = Alive(manager) ? manager->GetSystemGalacticAnomalies(system) : nullptr; + auto* anomaly = manager ? manager->GetSystemGalacticAnomalies(system) : nullptr; if (!anomaly || !anomaly->Get<"IsActive", bool>()) return 0; auto* timer = anomaly->Get<"EndTimeTimerDataContext", il2cpp::Object*>(); @@ -324,12 +323,6 @@ void Update() Clear(); return; } - // The anomaly button only exists in system view. - auto* sections = Hub::get_SectionManager(); - if (!sections || sections->CurrentSection() != SectionID::Navigation_System) { - Clear(); - return; - } // Rediscovery walks every registered canvas, so retry at most once a second. A hidden HUD is kept until a // different live one turns up. static auto nextDiscovery = std::chrono::steady_clock::time_point{}; diff --git a/mods/src/patches/parts/haven_zoom.cc b/mods/src/patches/parts/haven_zoom.cc index f267e4847..fa14a0175 100644 --- a/mods/src/patches/parts/haven_zoom.cc +++ b/mods/src/patches/parts/haven_zoom.cc @@ -2,8 +2,7 @@ #include #include -#include -#include +#include #include #include diff --git a/mods/src/patches/parts/hotkeys.cc b/mods/src/patches/parts/hotkeys.cc index 208490b02..ad1edf91d 100644 --- a/mods/src/patches/parts/hotkeys.cc +++ b/mods/src/patches/parts/hotkeys.cc @@ -1,51 +1,22 @@ #include "config.h" #include "patches/object_tracker.h" #include "patches/ship_shortcut_hint.h" -#include "prime/ActivatedAbilityManager.h" +#include "prime/fleet.h" #include #include // Object Viewers -#include "prime/AllianceStarbaseObjectViewerWidget.h" -#include "prime/ArmadaObjectViewerWidget.h" -#include "prime/WaveDefenseObjectViewerWidget.h" -#include "prime/CelestialObjectViewerWidget.h" -#include "prime/EmbassyObjectViewer.h" -#include "prime/HousingObjectViewerWidget.h" -#include "prime/MiningObjectViewerWidget.h" -#include "prime/MissionsObjectViewerWidget.h" -#include "prime/StarNodeObjectViewerWidget.h" - -#include "prime/ActionQueueManager.h" -#include "prime/AnimatedRewardsScreenViewController.h" -#include "prime/ArtifactHallDetailsViewController.h" -#include "prime/AssignShipsWidget.h" -#include "prime/BookmarksManager.h" -#include "prime/CanvasController.h" -#include "prime/ChatManager.h" -#include "prime/DeploymentManager.h" -#include "prime/ElementSelectorViewController.h" -#include "prime/FleetBarViewController.h" -#include "prime/FleetLocalViewController.h" -#include "prime/FleetsManager.h" -#include "prime/FullScreenChatViewController.h" +#include "prime/viewers.h" + +#include "prime/fleet.h" +#include "prime/ui.h" +#include "prime/navigation.h" +#include "prime/chat.h" +#include "prime/inventory.h" #include "prime/Hub.h" #include "prime/KeyCode.h" -#include "prime/LanguageManager.h" -#include "prime/NavigationInteractionUIViewController.h" -#include "prime/NavigationSectionManager.h" -#include "prime/OfficerAssignmentViewController.h" -#include "prime/PlanetaryBaseManager.h" -#include "prime/PreScanTargetWidget.h" -#include "prime/ScanEngageButtonsWidget.h" -#include "prime/ScreenManager.h" -#include "prime/SelectableList.h" -#include "prime/ShipManagementViewController.h" -#include "prime/ShipManagementScreenStateViewController.h" -#include "prime/ShortcutsManager.h" -#include "prime/TextLocalizer.h" #include "patches/parts/action_queue.h" #include "patches/key.h" diff --git a/mods/src/patches/parts/instant_cargo.cc b/mods/src/patches/parts/instant_cargo.cc index 491a591c7..5a0d2a4b4 100644 --- a/mods/src/patches/parts/instant_cargo.cc +++ b/mods/src/patches/parts/instant_cargo.cc @@ -1,6 +1,6 @@ // Show actual cargo totals through the native text renderer, without its count-up animation. #include "config.h" -#include "prime/TextLocalizer.h" +#include "prime/ui.h" #include #include diff --git a/mods/src/patches/parts/instant_warp_confirmation.cc b/mods/src/patches/parts/instant_warp_confirmation.cc index 6a3429f2c..fd0ee9bf1 100644 --- a/mods/src/patches/parts/instant_warp_confirmation.cc +++ b/mods/src/patches/parts/instant_warp_confirmation.cc @@ -2,8 +2,8 @@ #include "patches/instant_warp_policy.h" #include "ship_name_match.h" -#include -#include +#include +#include #include diff --git a/mods/src/patches/parts/loading_screen_common.h b/mods/src/patches/parts/loading_screen_common.h index 348d867de..8513e50f6 100644 --- a/mods/src/patches/parts/loading_screen_common.h +++ b/mods/src/patches/parts/loading_screen_common.h @@ -2,14 +2,7 @@ #include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include diff --git a/mods/src/patches/parts/misc.cc b/mods/src/patches/parts/misc.cc index 05ecf0646..b9e6e4c5b 100644 --- a/mods/src/patches/parts/misc.cc +++ b/mods/src/patches/parts/misc.cc @@ -1,12 +1,9 @@ #include "config.h" -#include "prime/InterstitialViewController.h" +#include "prime/ui.h" -#include -#include +#include +#include #include -#include -#include -#include #include @@ -19,8 +16,8 @@ #include #include -#include -#include +#include +#include namespace { diff --git a/mods/src/patches/parts/mission_hud_tweaks.cc b/mods/src/patches/parts/mission_hud_tweaks.cc index 589352a2f..05c70cd7a 100644 --- a/mods/src/patches/parts/mission_hud_tweaks.cc +++ b/mods/src/patches/parts/mission_hud_tweaks.cc @@ -1,7 +1,7 @@ #include "config.h" #include -#include +#include #include #include diff --git a/mods/src/patches/parts/object_tracker.cc b/mods/src/patches/parts/object_tracker.cc index 964f1fd59..c6244339e 100644 --- a/mods/src/patches/parts/object_tracker.cc +++ b/mods/src/patches/parts/object_tracker.cc @@ -2,27 +2,12 @@ #include -#include "prime/AllianceStarbaseObjectViewerWidget.h" -#include "prime/AnimatedRewardsScreenViewController.h" -#include "prime/ArmadaObjectViewerWidget.h" -#include "prime/ArtifactHallDetailsViewController.h" -#include "prime/AssignShipsWidget.h" -#include "prime/CelestialObjectViewerWidget.h" -#include "prime/EmbassyObjectViewer.h" -#include "prime/FleetBarViewController.h" -#include "prime/FleetMeshSelector.h" -#include "prime/FullScreenChatViewController.h" -#include "prime/HousingObjectViewerWidget.h" -#include "prime/InventoryListViewController.h" -#include "prime/MiningObjectViewerWidget.h" -#include "prime/OfficerAssignmentViewController.h" -#include "prime/MissionsObjectViewerWidget.h" -#include "prime/NavigationInteractionUIViewController.h" -#include "prime/PreScanTargetWidget.h" -#include "prime/ElementSelectorViewController.h" -#include "prime/ShipManagementViewController.h" -#include "prime/ShipManagementScreenStateViewController.h" -#include "prime/StarNodeObjectViewerWidget.h" +#include "prime/viewers.h" +#include "prime/ui.h" +#include "prime/fleet.h" +#include "prime/chat.h" +#include "prime/inventory.h" +#include "prime/navigation.h" #include #include diff --git a/mods/src/patches/parts/open_bulk_claim_gifts.cc b/mods/src/patches/parts/open_bulk_claim_gifts.cc index f4f90bbf3..07507578f 100644 --- a/mods/src/patches/parts/open_bulk_claim_gifts.cc +++ b/mods/src/patches/parts/open_bulk_claim_gifts.cc @@ -1,6 +1,6 @@ #include "config.h" -#include +#include #include #include diff --git a/mods/src/patches/parts/pinned_ship_click.cc b/mods/src/patches/parts/pinned_ship_click.cc index f2d95cd0a..1930032d4 100644 --- a/mods/src/patches/parts/pinned_ship_click.cc +++ b/mods/src/patches/parts/pinned_ship_click.cc @@ -5,10 +5,10 @@ #include "ship_name_match.h" #include "pinned_ship_click.h" -#include "prime/AssignShipsWidget.h" -#include "prime/CanvasController.h" -#include "prime/ShipManagementViewContext.h" -#include "prime/ShipManagementViewController.h" +#include "prime/fleet.h" +#include "prime/ui.h" +#include "prime/fleet.h" +#include "prime/fleet.h" #include "ship_tile_click.h" #include diff --git a/mods/src/patches/parts/pinned_ship_click.h b/mods/src/patches/parts/pinned_ship_click.h index 923e42a0d..4977e7e2a 100644 --- a/mods/src/patches/parts/pinned_ship_click.h +++ b/mods/src/patches/parts/pinned_ship_click.h @@ -1,6 +1,6 @@ #pragma once -#include "prime/FleetPlayerData.h" +#include "prime/fleet.h" void PinShipClickOnTileClicked(FleetPlayerData* ship); void InstallPinnedShipClickHooks(); diff --git a/mods/src/patches/parts/pinned_ship_sort.cc b/mods/src/patches/parts/pinned_ship_sort.cc index 0a930f53c..c5909835f 100644 --- a/mods/src/patches/parts/pinned_ship_sort.cc +++ b/mods/src/patches/parts/pinned_ship_sort.cc @@ -1,8 +1,7 @@ #include "config.h" #include "ship_name_match.h" -#include -#include +#include #include diff --git a/mods/src/patches/parts/reveal_confirmation.cc b/mods/src/patches/parts/reveal_confirmation.cc index 75a8dfdbd..f40d6b9c6 100644 --- a/mods/src/patches/parts/reveal_confirmation.cc +++ b/mods/src/patches/parts/reveal_confirmation.cc @@ -4,7 +4,7 @@ #include #include -#include +#include "prime/fleet.h" #include #include diff --git a/mods/src/patches/parts/ship_tile_click.cc b/mods/src/patches/parts/ship_tile_click.cc index 21904d555..df285c297 100644 --- a/mods/src/patches/parts/ship_tile_click.cc +++ b/mods/src/patches/parts/ship_tile_click.cc @@ -5,7 +5,7 @@ #include "double_click_assign_ship.h" #include "pinned_ship_click.h" -#include "prime/ShipTileWidget.h" +#include "prime/fleet.h" #include diff --git a/mods/src/patches/parts/sync.cc b/mods/src/patches/parts/sync.cc index 562036e7b..6a889364e 100644 --- a/mods/src/patches/parts/sync.cc +++ b/mods/src/patches/parts/sync.cc @@ -9,11 +9,9 @@ #include #include #include -#include -#include +#include +#include #include -#include -#include #include #include @@ -829,10 +827,9 @@ static std::unordered_map away_assignment_states; static std::mutex away_assignment_states_mtx; static std::unordered_map structure_states; -static std::mutex structure_states_mtx; - static std::unordered_map pieces_states; -static std::mutex pieces_states_mtx; +static std::mutex pieces_states_mtx; +static std::mutex structure_states_mtx; static eastl::ring_buffer previously_sent_battlelogs; static std::mutex previously_sent_battlelogs_mtx; @@ -974,8 +971,6 @@ static void ship_sync_data() ErrorMsg::SyncRuntime("ship", e); } catch (const std::exception& e) { ErrorMsg::SyncMsg("ship", e.what()); - } catch (const std::wstring& sz) { - ErrorMsg::SyncMsg("ship", sz); #if _WIN32 } catch (winrt::hresult_error const& ex) { ErrorMsg::SyncWinRT("ship", ex); @@ -1126,8 +1121,6 @@ static void ship_combat_log_data() ErrorMsg::SyncRuntime("combat", e); } catch (const std::exception& e) { ErrorMsg::SyncException("combat", e); - } catch (const std::wstring& sz) { - ErrorMsg::SyncMsg("combat", sz); #if _WIN32 } catch (winrt::hresult_error const& ex) { ErrorMsg::SyncWinRT("combat", ex); @@ -1607,7 +1600,6 @@ static void planetary_map(std::unique_ptr&& bytes) { if (auto response = Digit::PrimeServer::Models::PlanetaryMapResponse(); response.ParseFromString(*bytes)) { http::logging::trace("PROCESS", "planetary map", STR_FORMAT("Processing {} haven structures", response.mapdata().buildings_size())); - const auto& owner = response.owneruserid(); if (!owner.empty()) { auto* localProfile = local_user_profile(); @@ -1748,10 +1740,9 @@ static void player_inventories(std::unique_ptr&& bytes) static void jobs(std::unique_ptr&& bytes) { using json = nlohmann::json; - using job_state = std::tuple; - static std::unordered_map jobs_active; - static std::mutex jobs_active_mtx; - static std::atomic_bool is_first_sync{true}; + static std::unordered_set jobs_active; + static std::mutex jobs_active_mtx; + static std::atomic_bool is_first_sync{true}; if (auto response = Digit::PrimeServer::Models::JobResponse(); response.ParseFromString(*bytes)) { @@ -1765,14 +1756,10 @@ static void jobs(std::unique_ptr&& bytes) const std::string& uuid = job.uuid(); uuids_in_response.insert(uuid); - const job_state state{job.starttime().seconds(), job.duration(), job.reductioninseconds()}; - bool emit = false; { std::scoped_lock lk(jobs_active_mtx); - const auto [it, inserted] = jobs_active.try_emplace(uuid, state); - emit = inserted || it->second != state; - it->second = state; + emit = jobs_active.insert(uuid).second; } if (!emit) { @@ -1829,8 +1816,8 @@ static void jobs(std::unique_ptr&& bytes) { std::scoped_lock lk(jobs_active_mtx); for (auto it = jobs_active.begin(); it != jobs_active.end();) { - if (!uuids_in_response.contains(it->first)) { - job_array.push_back({{"type", "completed_" + SyncConfig::Type::Jobs}, {"uuid", it->first}}); + if (!uuids_in_response.contains(*it)) { + job_array.push_back({{"type", "completed_" + SyncConfig::Type::Jobs}, {"uuid", *it}}); it = jobs_active.erase(it); } else { ++it; @@ -2772,13 +2759,8 @@ namespace rtc if (const auto& expiry_time = data["consumable_slot_params"]["expiry_time"]; expiry_time.is_null()) { slot_params["expiry_time"] = nullptr; } else { - const auto timestamp = - parse_timestamp(data["consumable_slot_params"]["expiry_time"].get_ref()); - if (timestamp.has_value()) { - slot_params["expiry_time"] = timestamp.value().time_since_epoch().count(); - } else { - slot_params["expiry_time"] = nullptr; - } + const auto timestamp = parse_timestamp(expiry_time.get_ref()); + slot_params["expiry_time"] = timestamp ? json(timestamp->time_since_epoch().count()) : json(nullptr); } break; case Digit::PrimeServer::Models::SLOTTYPE_OFFICERPRESET: @@ -2797,12 +2779,8 @@ namespace rtc if (const auto& expiry_time = params["cooldown_expiration"]; expiry_time.is_null()) { slot_params["cooldown_expiration"] = nullptr; } else { - const auto timestamp = parse_timestamp(params["cooldown_expiration"].get_ref()); - if (timestamp.has_value()) { - slot_params["cooldown_expiration"] = timestamp.value().time_since_epoch().count(); - } else { - slot_params["cooldown_expiration"] = nullptr; - } + const auto timestamp = parse_timestamp(expiry_time.get_ref()); + slot_params["cooldown_expiration"] = timestamp ? json(timestamp->time_since_epoch().count()) : json(nullptr); } } break; diff --git a/mods/src/patches/parts/testing.cc b/mods/src/patches/parts/testing.cc index eb1e78092..a9501a945 100644 --- a/mods/src/patches/parts/testing.cc +++ b/mods/src/patches/parts/testing.cc @@ -1,24 +1,14 @@ #include "config.h" -#include "prime/ActionQueueManager.h" -#include "prime/ActionRequirement.h" -#include "prime/BlurController.h" -#include "prime/BookmarksManager.h" -#include "prime/ChatManager.h" -#include "prime/ChatMessageListLocalViewController.h" -#include "prime/ClientModifierType.h" -#include "prime/DeploymentManager.h" -#include "prime/FleetLocalViewController.h" -#include "prime/FleetsManager.h" -#include "prime/FullScreenChatViewController.h" +#include "prime/fleet.h" +#include "prime/ui.h" +#include "prime/navigation.h" +#include "prime/chat.h" +#include "prime/server.h" #include "prime/Hub.h" -#include "prime/InventoryForPopup.h" +#include "prime/inventory.h" #include "prime/KeyCode.h" -#include "prime/NavigationSectionManager.h" -#include "prime/ScanTargetViewController.h" -#include "prime/SceneManager.h" -#include "prime/ScreenManager.h" -#include +#include #include #include diff --git a/mods/src/patches/parts/ui_scale.cc b/mods/src/patches/parts/ui_scale.cc index 78736ef74..84ccafab0 100644 --- a/mods/src/patches/parts/ui_scale.cc +++ b/mods/src/patches/parts/ui_scale.cc @@ -3,15 +3,13 @@ #include -#include -#include -#include -#include +#include +#include +#include #include #include -#include #include namespace @@ -105,7 +103,7 @@ void ScreenManager_UpdateCanvasRootScaleFactor_Hook(auto original, ScreenManager void CanvasController_Show(auto original, CanvasController* _this, int desiredEntryPoint, bool instant) { const auto ui_scale_viewer = Config::Get().ui_scale_viewer; - if (_this && ui_scale_viewer != 0.0f && to_wstring(_this->name()) == L"ObjectViewerTemplate_Canvas") { + if (_this && ui_scale_viewer != 0.0f && to_string(_this->name()) == "ObjectViewerTemplate_Canvas") { if (auto* transform = _this->transform()) transform->SetLocalScale(Vector3{ui_scale_viewer, ui_scale_viewer, ui_scale_viewer}); } diff --git a/mods/src/patches/parts/warp_action_label.cc b/mods/src/patches/parts/warp_action_label.cc index 3a01dd54b..8202aeeca 100644 --- a/mods/src/patches/parts/warp_action_label.cc +++ b/mods/src/patches/parts/warp_action_label.cc @@ -3,10 +3,9 @@ #include "patches/instant_warp_policy.h" #include "patches/screen_update_hook.h" -#include "prime/DeploymentManager.h" -#include "prime/FleetsManager.h" -#include "prime/StarNodeObjectViewerWidget.h" -#include "prime/TextLocalizer.h" +#include "prime/fleet.h" +#include "prime/viewers.h" +#include "prime/ui.h" #include "str_utils.h" #include #include diff --git a/mods/src/patches/parts/zoom.cc b/mods/src/patches/parts/zoom.cc index 49bc8fb6f..36ee939c1 100644 --- a/mods/src/patches/parts/zoom.cc +++ b/mods/src/patches/parts/zoom.cc @@ -4,12 +4,8 @@ #include -#include -#include -#include -#include -#include -#include +#include +#include #include #include diff --git a/mods/src/prime/ActionData.h b/mods/src/prime/ActionData.h deleted file mode 100644 index e7f7505a9..000000000 --- a/mods/src/prime/ActionData.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include - -using IActionData = void*; - -// Digit.PrimeServer.Models.ActionType is a struct around a flag value, not an enum, so it crosses the -// managed boundary as a 4-byte struct. -struct ActionType { - int32_t flagValue; - - friend bool operator==(ActionType, ActionType) = default; - - static const ActionType Invalid; - static const ActionType Upgrade; - static const ActionType Purchase; - static const ActionType Manage; - static const ActionType Harvest; - static const ActionType Repair; - static const ActionType View; - static const ActionType Recall; - static const ActionType Promote; - static const ActionType Demote; - static const ActionType Move; - static const ActionType Attack; - static const ActionType Assign; - static const ActionType Add; - static const ActionType Remove; - static const ActionType Claim; - static const ActionType Chat; - static const ActionType Help; - static const ActionType Invite; - static const ActionType Report; - static const ActionType Copy; - static const ActionType Share; -}; - -inline constexpr ActionType ActionType::Invalid {-1}; -inline constexpr ActionType ActionType::Upgrade {1433481724}; -inline constexpr ActionType ActionType::Purchase{1807968545}; -inline constexpr ActionType ActionType::Manage {-1997567611}; -inline constexpr ActionType ActionType::Harvest {-1933286583}; -inline constexpr ActionType ActionType::Repair {-1850668115}; -inline constexpr ActionType ActionType::View {2666181}; -inline constexpr ActionType ActionType::Recall {-1851055311}; -inline constexpr ActionType ActionType::Promote {1355442080}; -inline constexpr ActionType ActionType::Demote {2043415476}; -inline constexpr ActionType ActionType::Move {2404337}; -inline constexpr ActionType ActionType::Attack {1971575400}; -inline constexpr ActionType ActionType::Assign {1970629903}; -inline constexpr ActionType ActionType::Add {65665}; -inline constexpr ActionType ActionType::Remove {-1850743644}; -inline constexpr ActionType ActionType::Claim {65189916}; -inline constexpr ActionType ActionType::Chat {2099064}; -inline constexpr ActionType ActionType::Help {2245473}; -inline constexpr ActionType ActionType::Invite {-2099832023}; -inline constexpr ActionType ActionType::Report {-1850654380}; -inline constexpr ActionType ActionType::Copy {2106261}; -inline constexpr ActionType ActionType::Share {79847359}; - - -enum ActionBehaviour { - Default = 1, - Instant = 2, - Speedup = 4, - Collect = 8, - AskHelp = 16, - RequestConfirmation = 32, -}; \ No newline at end of file diff --git a/mods/src/prime/ActionQueueManager.h b/mods/src/prime/ActionQueueManager.h deleted file mode 100644 index 1e9dae326..000000000 --- a/mods/src/prime/ActionQueueManager.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "FleetPlayerData.h" -#include "MonoSingleton.h" - -#include - -struct ActionQueueManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Prime.ActionQueue", "ActionQueueManager">; - - void ClearQueue(FleetPlayerData* playerData) - { Call<"ClearQueue">(playerData); } - bool IsQueueFull(FleetPlayerData* playerData) - { return Call<"IsQueueFull", bool>(playerData); } - bool IsFleetInQueue(FleetPlayerData* playerData) - { return Call<"IsFleetInQueue", bool>(playerData); } - bool IsQueueUnlocked() - { return Call<"IsQueueUnlocked", bool>(); } - - bool CanAddToQueue(FleetPlayerData* playerData) - { return IsQueueUnlocked() && !IsQueueFull(playerData); } - - void AddToQueue(int64_t targetId) - { Call<"AddActionToQueue">(targetId); } -}; diff --git a/mods/src/prime/ActionRequirement.h b/mods/src/prime/ActionRequirement.h deleted file mode 100644 index 7f6aa85af..000000000 --- a/mods/src/prime/ActionRequirement.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -struct ActionRequirement : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "ActionRequirement">; - - bool IsMet() - { return Get<"IsMet", bool>(); } - bool CheckIsMet() - { return Call<"CheckIsMet", bool>(); } -}; diff --git a/mods/src/prime/ActivatedAbilityManager.h b/mods/src/prime/ActivatedAbilityManager.h deleted file mode 100644 index 630755604..000000000 --- a/mods/src/prime/ActivatedAbilityManager.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "MonoSingleton.h" -#include "FleetPlayerData.h" - -struct ActivatedAbilityManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ActivatedAbilities", "ActivatedAbilityManager">; - static constexpr int32_t RevealHostileVgerAbilityType = 86002; - int32_t GetActivatedAbilityType(int64_t abilityId) - { return Call<"GetActivatedAbilityType", int32_t>(abilityId); } - bool CanUseAbility(FleetPlayerData* fleet, int64_t abilityId) - { return Call<"CanUseAbility", bool>(fleet, abilityId); } -}; diff --git a/mods/src/prime/AllianceStarbaseObjectViewerWidget.h b/mods/src/prime/AllianceStarbaseObjectViewerWidget.h deleted file mode 100644 index fa6504a64..000000000 --- a/mods/src/prime/AllianceStarbaseObjectViewerWidget.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "ObjectViewerBaseWidget.h" - -struct AllianceStarbaseObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "AllianceStarbaseObjectViewerWidget">; -}; diff --git a/mods/src/prime/AnimatedRewardsScreenViewController.h b/mods/src/prime/AnimatedRewardsScreenViewController.h deleted file mode 100644 index 156f3155d..000000000 --- a/mods/src/prime/AnimatedRewardsScreenViewController.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -struct AnimatedRewardsScreenViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Missions.UI", "AnimatedRewardsScreenViewController">; - - void GoBackToLastSection() - { Call<"GoBackToLastSection">(); } - void OnCollectClicked() - { Call<"OnCollectClicked">(); } - bool IsActive() - { return Call<"IsActive", bool>(); } -}; diff --git a/mods/src/prime/ArmadaObjectViewerWidget.h b/mods/src/prime/ArmadaObjectViewerWidget.h deleted file mode 100644 index b86ff3a2f..000000000 --- a/mods/src/prime/ArmadaObjectViewerWidget.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "FleetPlayerData.h" -#include "GenericButtonContext.h" -#include "ObjectViewerBaseWidget.h" - -struct ArmadaObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "ArmadaObjectViewerWidget">; - - void SetCourseToArmada() - { Call<"SetCourseToArmada">(); } - void ValidateThenJoinArmada() - { Call<"ValidateThenJoinArmada">(); } - void JoinArmada() - { Call<"JoinArmada">(); } - bool IsAllowedToJoinArmada(FleetPlayerData* fleet) - { return Call<"IsAllowedToJoinArmada", bool>(fleet); } - - GenericButtonContext* joinContext() - { return Field<"_joinContext", GenericButtonContext*>(); } -}; diff --git a/mods/src/prime/ArtifactHallDetailsViewController.h b/mods/src/prime/ArtifactHallDetailsViewController.h deleted file mode 100644 index 0d676c219..000000000 --- a/mods/src/prime/ArtifactHallDetailsViewController.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "SemaphoreButtonListener.h" - -#include -#include - -struct ArtifactHallDetailsViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ArtifactHall", "ArtifactHallDetailsViewController">; - - // Treat the controller as active if the icall is unavailable, so the arrow hotkeys still try. - bool isActiveAndEnabled() - { - static auto get_isActiveAndEnabled = il2cpp::ResolveICall( - "UnityEngine.Behaviour::get_isActiveAndEnabled()"); - return get_isActiveAndEnabled ? get_isActiveAndEnabled(this) : true; - } - - void PressLeftArrow() - { - auto* listener = leftArrowButton(); - if (!listener) { - spdlog::trace("No left button listener"); - return; - } - auto* button = listener->TheButton(); - if (!button) { - spdlog::trace("No left button"); - return; - } - button->Press(); - } - - void PressRightArrow() - { - auto* listener = rightArrowButton(); - if (!listener) { - spdlog::info("No right button listener"); - return; - } - auto* button = listener->TheButton(); - if (!button) { - spdlog::info("No right button"); - return; - } - button->Press(); - } - - SemaphoreButtonListener* leftArrowButton() - { return Field<"_leftArrowButton", SemaphoreButtonListener*>(); } - SemaphoreButtonListener* rightArrowButton() - { return Field<"_rightArrowButton", SemaphoreButtonListener*>(); } -}; diff --git a/mods/src/prime/AspectRatioConstraintHandler.h b/mods/src/prime/AspectRatioConstraintHandler.h deleted file mode 100644 index d66498f66..000000000 --- a/mods/src/prime/AspectRatioConstraintHandler.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#if _WIN32 - -#include - -struct AspectRatioConstraintHandler { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Utils", "AspectRatioConstraintHandler">; - - static WNDPROC _unityWndProc() - { return reinterpret_cast(Class::Field<"_unityWndProc", intptr_t>()); } -}; - -#endif diff --git a/mods/src/prime/AssignShipsWidget.h b/mods/src/prime/AssignShipsWidget.h deleted file mode 100644 index e72e35d1c..000000000 --- a/mods/src/prime/AssignShipsWidget.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "ButtonAndContextWrapper.h" -#include "InputFieldWidget.h" -#include "SelectableList.h" - -#include - -struct AssignShipsWidget : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "AssignShipsWidget">; - - InputFieldWidget* inputField() - { return Field<"_inputField", InputFieldWidget*>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - ButtonAndContextWrapper* assignButton() - { return Field<"_assignButton", ButtonAndContextWrapper*>(); } - SelectableList* selectableList() - { return Field<"_selectableList", SelectableList*>(); } -}; diff --git a/mods/src/prime/BattleResultHeader.h b/mods/src/prime/BattleResultHeader.h deleted file mode 100644 index 40f5ec1e5..000000000 --- a/mods/src/prime/BattleResultHeader.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - -#include - -enum class BattleType : int32_t { - Fleet = 0, - Base = 1, - PassiveMarauder = 2, - NpcInstantiated = 3, - DockingPoint = 4, - ActiveMarauder_MarauderInit = 5, - ActiveMarauder_PlayerInit = 6, - ArmadaBase = 7, - ArmadaMarauder = 8, - PveDockingPoint = 9, - ArmadaAsb = 10, - ArmadaMta = 11, - Hazard = 12, - PveCuttingBeam = 13, - PvpCuttingBeam = 14, - PveChainShot = 15, - PvpChainShot = 16 -}; - -enum class BattleResultType : int32_t { - Defeat = 0, - Victory = 1, - PartialVictory = 2 -}; - -enum class FleetDataType : int32_t { - DeployedFleet = 0, - Starbase = 1, - Armada = 2 -}; - -struct BattleResultHeader : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "BattleResultHeader">; - - Il2CppObject* get_PlayerUserProfile() - { return Get<"PlayerUserProfile", Il2CppObject*>(); } - Il2CppObject* get_EnemyUserProfile() - { return Get<"EnemyUserProfile", Il2CppObject*>(); } - Il2CppObject* get_SpecService() - { return Field<"_specService", Il2CppObject*>(); } - int32_t get_BattleType() - { return Get<"BattleType", int32_t>(); } - - int64_t PlayerShipHullId() - { return Get<"PlayerShipHullId", int64_t>(); } - int64_t EnemyShipHullId() - { return Get<"EnemyShipHullId", int64_t>(); } -}; diff --git a/mods/src/prime/BattleTargetData.h b/mods/src/prime/BattleTargetData.h deleted file mode 100644 index 6361b81e7..000000000 --- a/mods/src/prime/BattleTargetData.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "FleetDeployedData.h" - -#include - -struct BattleTargetData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "BattleTargetData">; - - FleetDeployedData* TargetFleetDeployedData() - { return Field<"TargetFleetDeployedData", FleetDeployedData*>(); } -}; diff --git a/mods/src/prime/BlurController.h b/mods/src/prime/BlurController.h deleted file mode 100644 index 866d65b00..000000000 --- a/mods/src/prime/BlurController.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -struct BlurController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.LoadingScreen", "BlurController">; - - float blurTime() - { return Field<"_blurTime", float>(); } - void SetBlurTime(float value) - { SetField<"_blurTime">(value); } - int32_t waitFrames() - { return Field<"_waitFrames", int32_t>(); } - void SetWaitFrames(int32_t value) - { SetField<"_waitFrames">(value); } - - void ForceCompletion() - { Call<"ForceCompletion">(); } -}; diff --git a/mods/src/prime/BookmarksManager.h b/mods/src/prime/BookmarksManager.h deleted file mode 100644 index a491a5ba8..000000000 --- a/mods/src/prime/BookmarksManager.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "Hub.h" -#include "MonoSingleton.h" - -#include - -struct CoordinateSearchContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Bookmarks", "CoordinateSearchContext">; - - void SetDefaultXCoordinate(Il2CppString* value) - { Set<"DefaultXCoordinate">(value); } - void SetDefaultYCoordinate(Il2CppString* value) - { Set<"DefaultYCoordinate">(value); } - void SetDefaultSystemInput(Il2CppString* value) - { Set<"DefaultSystemInput">(value); } -}; - -struct BookmarksManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Bookmarks", "BookmarksManager">; - - void ViewBookmarks() - { Call<"ViewBookmarks">(); } - - void ViewCoordinateSearch() - { - auto* context = il2cpp::New(); - // The binding already reported the missing class or constructor; plain bookmarks are the next best view. - if (!context) { - ViewBookmarks(); - return; - } - - auto* zero = il2cpp_string_new("0"); - context->SetDefaultXCoordinate(zero); - context->SetDefaultYCoordinate(zero); - context->SetDefaultSystemInput(il2cpp_string_new("")); - Hub::get_SectionManager()->TriggerSectionChange(SectionID::Bookmarks_Search_Coordinates, context, false, false, - true); - } -}; diff --git a/mods/src/prime/Bundle.h b/mods/src/prime/Bundle.h deleted file mode 100644 index 30fe1e564..000000000 --- a/mods/src/prime/Bundle.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -struct Bundle : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimePlatform.Content", "Bundle">; -}; diff --git a/mods/src/prime/BundleDataWidget.h b/mods/src/prime/BundleDataWidget.h deleted file mode 100644 index 0c34585e8..000000000 --- a/mods/src/prime/BundleDataWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "Bundle.h" -#include "Widget.h" - -#include - -#include - -struct BundleDataWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Shop", "BundleDataWidget">; - - enum ItemState : int32_t { - None = 0, - Package = 1, - Highlighted = 2, - LegendOn = 4, - ExpiryTimerOn = 8, - DiscountOn = 16, // 0x00000010 - RemoteImage = 32, // 0x00000020 - Showcase = 256, // 0x00000100 - HasBanner = 512, // 0x00000200 - ShowViewButton = 1024, // 0x00000400 - ShowMaxPurchases = 2048, // 0x00000800 - ShowItemRarity = 4096, // 0x00001000 - ShowItemType = 8192, // 0x00002000 - CooldownTimerOn = 16384, // 0x00004000 - ShowSynthetic = 32768, // 0x00008000 - ShowPurchaseLimits = 65536, // 0x00010000 - ShowFreeTag = 131072, // 0x00020000 - }; - - ItemState CurrentState() - { return Field<"_currentState", ItemState>(); } - - void AuxViewButtonPressedHandler() - { Call<"AuxViewButtonPressedHandler">(); } -}; diff --git a/mods/src/prime/Button.h b/mods/src/prime/Button.h deleted file mode 100644 index 2a25bdfdb..000000000 --- a/mods/src/prime/Button.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -struct Button : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.UI", "Button">; - - void Press() - { Call<"Press">(); } -}; diff --git a/mods/src/prime/ButtonAndContextWrapper.h b/mods/src/prime/ButtonAndContextWrapper.h deleted file mode 100644 index 4e5cc5f44..000000000 --- a/mods/src/prime/ButtonAndContextWrapper.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "GenericButtonWidget.h" - -#include - -struct ButtonAndContextWrapper : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "ButtonAndContextWrapper">; - - GenericButtonWidget* Widget() - { return Field<"Widget", GenericButtonWidget*>(); } -}; diff --git a/mods/src/prime/Camera.h b/mods/src/prime/Camera.h deleted file mode 100644 index d260fc441..000000000 --- a/mods/src/prime/Camera.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "Color.h" - -#include - -struct Camera : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Camera">; - - float farClipPlane() - { return Get<"farClipPlane", float>(); } - void SetFarClipPlane(float value) - { Set<"farClipPlane">(value); } - float nearClipPlane() - { return Get<"nearClipPlane", float>(); } - void SetNearClipPlane(float value) - { Set<"nearClipPlane">(value); } - // UnityEngine.CameraClearFlags - int32_t clearFlags() - { return Get<"clearFlags", int32_t>(); } - void SetClearFlags(int32_t value) - { Set<"clearFlags">(value); } - color backgroundColor() - { return Get<"backgroundColor", color>(); } - void SetBackgroundColor(color value) - { Set<"backgroundColor">(value); } -}; diff --git a/mods/src/prime/CanRepairRequirement.h b/mods/src/prime/CanRepairRequirement.h deleted file mode 100644 index 7b599c7e3..000000000 --- a/mods/src/prime/CanRepairRequirement.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "ActionRequirement.h" - -struct CanRepairRequirement : ActionRequirement { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", - "FleetPlayerData/CanRepairRequirement">; -}; diff --git a/mods/src/prime/Canvas.h b/mods/src/prime/Canvas.h deleted file mode 100644 index d2e7ebb8f..000000000 --- a/mods/src/prime/Canvas.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -struct Canvas : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.UIModule", "UnityEngine", "Canvas">; - - float scaleFactor() - { return Get<"scaleFactor", float>(); } - void SetScaleFactor(float value) - { Set<"scaleFactor">(value); } -}; diff --git a/mods/src/prime/CanvasController.h b/mods/src/prime/CanvasController.h deleted file mode 100644 index 51052a545..000000000 --- a/mods/src/prime/CanvasController.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "Canvas.h" -#include "Transform.h" - -#include - -struct CanvasController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "CanvasController">; - - bool ResetFocus() - { return Call<"ResetFocus", bool>(); } - bool Visible() - { return Get<"Visible", bool>(); } - // The bound data context, of the class the canvas's view expects. - il2cpp::Object* Context() - { return Get<"Context", il2cpp::Object*>(); } - bool get_enabled() - { return Get<"enabled", bool>(); } - bool m_Visible() - { return Field<"m_visible", bool>(); } - - Il2CppString* name() - { return Get<"name", Il2CppString*>(); } - Transform* transform() - { return Get<"transform", Transform*>(); } - Canvas* canvas() - { return Field<"m_canvas", Canvas*>(); } -}; - -inline CanvasController* GetCanvasControllerFromComponent(il2cpp::Object* component) -{ - if (!component) - return nullptr; - // Passing System.Type selects GetComponentInParent(Type) over the generic overload. - return component->Call<"GetComponentInParent", CanvasController*>(CanvasController::Class::TypeObject()); -} diff --git a/mods/src/prime/CanvasScaler.h b/mods/src/prime/CanvasScaler.h deleted file mode 100644 index ae839dfef..000000000 --- a/mods/src/prime/CanvasScaler.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Vector2.h" - -#include - -struct CanvasScaler : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.UI", "CanvasScaler">; - - // Writes Canvas.scaleFactor directly. The scaleFactor property is only applied on the scaler's next Update. - void SetScaleFactor(float value) - { Call<"SetScaleFactor">(value); } - - float scaleFactor() - { return Get<"scaleFactor", float>(); } - // Named after the managed setter because SetScaleFactor is the managed method above. - void set_scaleFactor(float value) - { Set<"scaleFactor">(value); } - vec2 referenceResolution() - { return Get<"referenceResolution", vec2>(); } - void SetReferenceResolution(vec2 value) - { Set<"referenceResolution">(value); } -}; diff --git a/mods/src/prime/CelestialObjectViewerWidget.h b/mods/src/prime/CelestialObjectViewerWidget.h deleted file mode 100644 index 9b41638b4..000000000 --- a/mods/src/prime/CelestialObjectViewerWidget.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "ObjectViewerBaseWidget.h" - -struct CelestialObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "CelestialObjectViewerWidget">; -}; diff --git a/mods/src/prime/ChatManager.h b/mods/src/prime/ChatManager.h deleted file mode 100644 index df7006ded..000000000 --- a/mods/src/prime/ChatManager.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "MonoSingleton.h" - -#include - -enum class ChatChannelCategory : int32_t { - None = -1, - Newbie = 0, - Global = 1, - Alliance = 2, - Private = 3, - Private_Message = 4, - Block = 5, - Regional = 6, -}; - -enum class ChatViewMode : int32_t { - Fullscreen = 0, - Side = 1, -}; - -struct ChatManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatManager">; - - void OpenChannel(ChatChannelCategory category = ChatChannelCategory::Alliance) - { Call<"OpenChannel">(category, il2cpp_string_new("")); } - - void OpenChannel(ChatChannelCategory category, ChatViewMode viewMode) - { - SetViewMode(viewMode); - OpenChannel(category); - } - - bool CanSeeNewbieChat() - { return Call<"CanSeeNewbieChat", bool>(); } - bool CanSeeRegionalChat() - { return Call<"CanSeeRegionalChat", bool>(); } - - bool IsSideChatAllowed() - { return Get<"IsSideChatAllowed", bool>(); } - bool IsSideChatOpen() - { return Get<"IsSideChatOpen", bool>(); } - bool isContextInitialised() - { return Field<"_isContextInitialised", bool>(); } - ChatViewMode ViewMode() - { return Get<"ViewMode", ChatViewMode>(); } - void SetViewMode(ChatViewMode value) - { Set<"ViewMode">(value); } -}; diff --git a/mods/src/prime/ChatMessageListLocalViewController.h b/mods/src/prime/ChatMessageListLocalViewController.h deleted file mode 100644 index ee2fb7d16..000000000 --- a/mods/src/prime/ChatMessageListLocalViewController.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "ChatManager.h" -#include "TMP_InputField.h" -#include "ViewController.h" - -#include - -struct ChatSectionContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatSectionContext">; - - ChatChannelCategory category() - { return Field<"_category", ChatChannelCategory>(); } -}; - -struct ChatMessageListLocalViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatMessageListLocalViewController">; - - TMP_InputField* inputField() - { return Field<"_inputField", TMP_InputField*>(); } - bool inputFieldSelected() - { return Field<"_inputFieldSelected", bool>(); } -}; diff --git a/mods/src/prime/ChatMessageWidget.h b/mods/src/prime/ChatMessageWidget.h deleted file mode 100644 index 1f61620e4..000000000 --- a/mods/src/prime/ChatMessageWidget.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include -#include "UserProfileWidget.h" -struct ChatMessageWidget : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatMessageWidget">; - UserProfileWidget* userProfileWidget() - { return Field<"_userProfileWidget", UserProfileWidget*>(); } -}; diff --git a/mods/src/prime/ChatPreviewController.h b/mods/src/prime/ChatPreviewController.h deleted file mode 100644 index 9d4938224..000000000 --- a/mods/src/prime/ChatPreviewController.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "ChatManager.h" - -#include - -struct SwipeScroller : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SwipeScroller">; - - int32_t currentContentIndex() - { return Field<"_currentContentIndex", int32_t>(); } - void FocusOnInstantly(int32_t index) - { Call<"FocusOnInstantly">(index); } -}; - -struct ChatPreviewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatPreviewController">; - - // ImageSelector[] - il2cpp::Array* panelIndicators() - { return Field<"_panelIndicators", il2cpp::Array*>(); } - SwipeScroller* swipeScroller() - { return Field<"_swipeScroller", SwipeScroller*>(); } - ChatChannelCategory focusedPanel() - { return Field<"_focusedPanel", ChatChannelCategory>(); } - void SetFocusedPanel(ChatChannelCategory value) - { SetField<"_focusedPanel">(value); } - // Set by the game after its visibility refresh for a section change. - bool isSectionChangedCompleted() - { return Field<"_isSectionChangedCompleted", bool>(); } -}; diff --git a/mods/src/prime/ClientModifierType.h b/mods/src/prime/ClientModifierType.h deleted file mode 100644 index 9b10a38ca..000000000 --- a/mods/src/prime/ClientModifierType.h +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once - -#include - -enum class ClientModifierType : int32_t { - Invalid = -0x7FFF, - FleetOfficerBonusHealth = -17, - FleetOfficerBonusDefense = -16, - FleetOfficerBonusAttack = -15, - FleetStrength = -14, - FleetOfficerRating = -13, - FleetSensorRating = -12, - FleetDeflectorRating = -11, - FleetHealthRating = -10, - FleetDefenseRating = -9, - FleetOffenseRating = -8, - FleetGrade = -7, - ShipShieldDamage = -6, - ShipHullDamage = -5, - FleetMiningBonus = -4, - ShipPlating = -3, - ShipAbsorption = -2, - ShipDamagePerRound = -1, - ModEnergyDamage = 0, - ModKineticDamage = 1, - ModAllDamage = 2, - ModShotsPerAttack = 3, - ModAllLoadSpeed = 4, - ModAllReloadSpeed = 5, - ModAccuracy = 6, - ModArmorPiercing = 7, - ModShieldPiercing = 8, - ModCritChance = 9, - ModCritDamage = 10, - ModShipDodge = 11, - ModShipArmor = 12, - ModShields = 13, - ModCptManeuverEffect = 14, - ModOffAbilityEffect = 15, - ModShieldHpRepair = 16, - ModHullHpRepair = 17, - ModAddState = 18, - ModRemoveState = 19, - ModParsteelProtection = 20, - ModTritaniumProtection = 21, - ModDilithiumProtection = 22, - ModCombatParsteelReward = 23, - ModCombatTritaniumReward = 24, - ModCombatDilithiumReward = 25, - ModCombatIntelReward = 26, - ModCombatLootDropChance = 27, - ModCombatLootDropQuantity = 28, - ModCombatXpReward = 29, - ModMissionsParsteelReward = 30, - ModMissionsTritaniumReward = 31, - ModMissionsDilithiumReward = 32, - ModMissionsIntelReward = 33, - ModMissionsLootDropChance = 34, - ModMissionsLootDropQuantity = 35, - ModMissionsXpReward = 36, - ModFactionPointsFederationReward = 37, - ModFactionPointsKlingonReward = 38, - ModFactionPointsRomulanReward = 39, - ModFactionPointsAllReward = 40, - ModMiningRateParsteel = 41, - ModMiningRateTritanium = 42, - ModMiningRateDilithium = 43, - ModMiningRateOre = 44, - ModMiningRateHydrocarbon = 45, - ModMiningRewardParsteel = 46, - ModMiningRewardTritanium = 47, - ModMiningRewardDilithium = 48, - ModMiningRewardOre = 49, - ModMiningRewardHydrocarbon = 50, - ModRepairCostsParsteel = 51, - ModRepairCostsTritanium = 52, - ModRepairCostsDilithium = 53, - ModRepairCostsAll = 54, - ModRepairTime = 55, - ModOfficerStatAttack = 56, - ModOfficerStatDefense = 57, - ModOfficerStatHealth = 58, - ModOfficerStatAll = 59, - ModShieldHpMax = 60, - ModHullHpMax = 61, - ModImpulseSpeed = 62, - ModWarpSpeed = 63, - ModWarpDistance = 64, - ModEnterWarpDelay = 65, - ModCargoCapacity = 66, - ModCargoProtection = 67, - ModAutoRecall = 68, - ModEnergyLoadSpeed = 69, - ModKineticLoadSpeed = 70, - ModEnergyReloadSpeed = 71, - ModKineticReloadSpeed = 72, - ModAllDefenses = 73, - ModAllPiercing = 74, - ModShieldRegenTime = 75, - ModShieldMitigation = 76, - ModResourceProtection = 100, - ModCombatResourceReward = 101, - ModMissionResourceReward = 102, - ModFactionPointsRewards = 103, - ModFactionPointsLosses = 104, - ModMiningRate = 105, - ModMiningReward = 106, - ModRepairCosts = 107, - ModStarbaseModuleConstructionSpeed = 108, - ModStarbaseModuleConstructionCost = 109, - ModResearchSpeed = 110, - ModResearchCost = 111, - ModResourceGeneration = 112, - ModResourceStorage = 113, - ModForcefieldRechargeSpeed = 114, - ModForcefieldHp = 115, - ModComponentCost = 116, - ModShipConstructionSpeed = 117, - ModShipConstructionCost = 118, - ModTierUpSpeed = 119, - ModFactionPointsPenaltyRecution = 120, - ModFactionStoreLootBonus = 121, - ModOfficerLevelUpCost = 122, - ModOfficerPromoteCost = 123, - ModScanShip = 124, - ModScanStation = 125, - ModScanSystem = 126, - ModIncomingStationAttackInformation = 127, - ModIncomingStationAttackAdvancedWarning = 128, - ModShipScrapResourceBonus = 129, - ModShipScrapLootBonus = 130, - ModShipScrapSpeed = 131, - ModAwayMissionSpeed = 132, - ModAwayMissionResourceRewards = 133, - ModAwayMissionLootBonus = 134, - ModResourceProducerResourceStorage = 135, - ModWarehouseResourceStorage = 136, - ModVaultResourceStorage = 137, - ModHullId = 138, - ModShipInventorySize = 139, - ModScanCost = 140, - ModTradeTax = 141, - ModRepairStation = 142, - ModArmadaSize = 143, - ModJumpAndTowCostEff = 147, - ModSkillCloakingCooldown = 150, - ModSkillCloakingDuration = 151, - ModSkillCloakingCost = 152, - ModSkillCloakingSneakChance = 153 -}; \ No newline at end of file diff --git a/mods/src/prime/Color.h b/mods/src/prime/Color.h deleted file mode 100644 index faf533982..000000000 --- a/mods/src/prime/Color.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#pragma pack(push, 1) -struct color { - float r, g, b, a; -}; -#pragma pack(pop) diff --git a/mods/src/prime/Component.h b/mods/src/prime/Component.h deleted file mode 100644 index fc0f55df3..000000000 --- a/mods/src/prime/Component.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "GameObject.h" -#include "Transform.h" - -#include - -// UnityEngine.Component, the base of every MonoBehaviour. Use As() on other bindings. -struct Component : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Component">; - - Transform* transform() - { return Get<"transform", Transform*>(); } - GameObject* gameObject() - { return Get<"gameObject", GameObject*>(); } -}; diff --git a/mods/src/prime/CourseData.h b/mods/src/prime/CourseData.h deleted file mode 100644 index 65fa5cfde..000000000 --- a/mods/src/prime/CourseData.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "FleetPlayerData.h" - -#include - -struct CourseData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "CourseData">; - - FleetPlayerData* PlayerFleet() - { return Get<"PlayerFleet", FleetPlayerData*>(); } -}; diff --git a/mods/src/prime/CoursePromptPopupWidget.h b/mods/src/prime/CoursePromptPopupWidget.h deleted file mode 100644 index 8f1935004..000000000 --- a/mods/src/prime/CoursePromptPopupWidget.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "CourseData.h" -#include "GenericButtonWidget.h" -#include "ViewController.h" -#include "Widget.h" - -#include - -struct CoursePromptPopupDataContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "CoursePromptPopupDataContext">; - - bool HasInstantWarp() - { return Field<"HasInstantWarp", bool>(); } - CourseData* GetCourseData() - { return Field<"CourseData", CourseData*>(); } -}; - -struct CoursePromptPopupWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "CoursePromptPopupWidget">; - - GenericButtonWidget* LeftButton() - { return Field<"_leftButton", GenericButtonWidget*>(); } - GenericButtonWidget* RightButton() - { return Field<"_rightButton", GenericButtonWidget*>(); } - void InitiateRegularWarp() - { Call<"InitiateRegularWarp">(); } - void OnInstantWarpButtonClick() - { Call<"OnInstantWarpButtonClick">(); } -}; - -struct CoursePromptPopupViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "CoursePromptPopupViewController">; - - CoursePromptPopupWidget* PopupWidget() - { return Field<"_coursePromptPopupWidget", CoursePromptPopupWidget*>(); } -}; diff --git a/mods/src/prime/DeploymentManager.h b/mods/src/prime/DeploymentManager.h deleted file mode 100644 index 188acb2f7..000000000 --- a/mods/src/prime/DeploymentManager.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "FleetDeployedData.h" -#include "FleetPlayerData.h" -#include "MonoSingleton.h" -#include "Vector3.h" - -#include - -struct DeploymentService : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Services", "DeploymentService">; - - // The compiler-generated d__N iterator. Unbound so MoveNext resolves on whatever class the game - // returns; the suffix changes between client builds. - struct IEnumerator_PlanCourse : il2cpp::Object { - bool MoveNext() - { return Call<"MoveNext", bool>(); } - }; -}; - -// The managed class is DeploymentManager; the misspelled C++ name is kept for existing callers. -struct DeploymentManger : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "", "DeploymentManager">; - - void SetTowRequest(uint64_t towedFleetId, uint64_t towingFleetId) - { - // The game expects a CallbackContainer; an empty one without handlers is enough. Take the concrete - // generic instance from the method signature instead of spelling the inflated class out here. - static Il2CppClass* const callback_class = [] { - auto method = Class::Method("SetTowRequest", 3); - return method ? il2cpp_class_from_type(method.Info()->parameters[2]) : nullptr; - }(); - if (!callback_class) - return; - Call<"SetTowRequest">(towedFleetId, towingFleetId, il2cpp_object_new(callback_class)); - } - - bool HasInstantWarpAbility(FleetPlayerData* selectedFleet) - { return Call<"HasInstantWarpAbility", bool>(selectedFleet); } - - // targetAddress is a NodeAddress; the trailing data arguments are optional references. - DeploymentService::IEnumerator_PlanCourse* PlanCourse(FleetPlayerData* selectedFleet, void* targetAddress, - Vector3 targetPosition, FleetDeployedData* targetDeployedFleet, - void* starbaseData, void* allianceStarbaseData, - void* outpost = nullptr) - { - return Call<"PlanCourse", DeploymentService::IEnumerator_PlanCourse*>( - selectedFleet, targetAddress, targetPosition, targetDeployedFleet, starbaseData, allianceStarbaseData, outpost); - } -}; diff --git a/mods/src/prime/ElementSelectorViewController.h b/mods/src/prime/ElementSelectorViewController.h deleted file mode 100644 index 0daefee5a..000000000 --- a/mods/src/prime/ElementSelectorViewController.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "Button.h" - -#include - -struct ElementSelectorViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.UI", "ElementSelectorViewController">; - - Button* decrementButton() - { return Field<"_decrementButton", Button*>(); } - Button* incrementButton() - { return Field<"_incrementButton", Button*>(); } - - // Unity's internal call, so a missing binding never hides the selector hotkeys. - bool isActiveAndEnabled() - { - static auto get_isActiveAndEnabled = - il2cpp::ResolveICall("UnityEngine.Behaviour::get_isActiveAndEnabled()"); - if (get_isActiveAndEnabled) { - return get_isActiveAndEnabled(this); - } - return true; - } - - void Decrement() - { Call<"Decrement">(); } - void Increment() - { Call<"Increment">(); } - - void PressDecrement() - { - if (auto btn = decrementButton()) { - btn->Press(); - } - } - - void PressIncrement() - { - if (auto btn = incrementButton()) { - btn->Press(); - } - } -}; diff --git a/mods/src/prime/EmbassyObjectViewer.h b/mods/src/prime/EmbassyObjectViewer.h deleted file mode 100644 index e96af1e77..000000000 --- a/mods/src/prime/EmbassyObjectViewer.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "ObjectViewerBaseWidget.h" - -struct EmbassyObjectViewer : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "EmbassyObjectViewer">; -}; diff --git a/mods/src/prime/EventSystem.h b/mods/src/prime/EventSystem.h deleted file mode 100644 index bfa0d43cb..000000000 --- a/mods/src/prime/EventSystem.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "GameObject.h" - -#include - -struct EventSystem : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.EventSystems", "EventSystem">; - - static EventSystem* current() - { return Class::Get<"current", EventSystem*>(); } - - GameObject* currentSelectedGameObject() - { return Get<"currentSelectedGameObject", GameObject*>(); } - void SetSelectedGameObject(GameObject* object) - { Call<"SetSelectedGameObject">(object); } -}; diff --git a/mods/src/prime/FleetBarViewController.h b/mods/src/prime/FleetBarViewController.h deleted file mode 100644 index f43a17004..000000000 --- a/mods/src/prime/FleetBarViewController.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "FleetLocalViewController.h" -#include "FleetPlayerData.h" -#include "ViewController.h" - -#include - -#include - -struct FleetBarContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "FleetBarContext">; - - il2cpp::Array* FleetData() - { return Get<"FleetData", il2cpp::Array*>(); } - - FleetPlayerData* CurrentFleet() - { return Get<"CurrentFleet", FleetPlayerData*>(); } -}; - -struct FleetBarViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "FleetBarViewController">; - - // The int overload; the other takes the list element Component. - void RequestSelect(int32_t index, bool simulated = false) - { Call<"RequestSelect">(index, simulated); } - bool IsIndexSelected(int32_t index) - { return Call<"IsIndexSelected", bool>(index); } - - FleetLocalViewController* fleetPanelController() - { return Field<"_fleetPanelController", FleetLocalViewController*>(); } -}; diff --git a/mods/src/prime/FleetDeployedData.h b/mods/src/prime/FleetDeployedData.h deleted file mode 100644 index 7197c1143..000000000 --- a/mods/src/prime/FleetDeployedData.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "HullSpec.h" - -#include - -#include - -enum class DeployedFleetType : int32_t { - Nonexistent, - Player, - Marauder, - NpcInstantiated, - Sentinel, - Alliance, - Challenge, -}; - -struct FleetDeployedData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "FleetDeployedData">; - - int64_t ID() - { return Get<"ID", int64_t>(); } - HullSpec* Hull() - { return Get<"Hull", HullSpec*>(); } - // Nonexistent when the property is missing. - DeployedFleetType FleetType() - { return Get<"FleetType", DeployedFleetType>(); } -}; diff --git a/mods/src/prime/FleetLiveData.h b/mods/src/prime/FleetLiveData.h deleted file mode 100644 index 0fb21159b..000000000 --- a/mods/src/prime/FleetLiveData.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include - -#include -#include - -// Cargo, mining slot and timers off FleetPlayerData. The interfaces are bound as such: their getters dispatch to the -// runtime class, which also covers FleetMiningSlot's explicit IMiningSlot implementation. - -// System.DateTime: ticks in the low 62 bits, the Kind in the top two. -struct DateTime { - uint64_t dateData; - - // Unix seconds, or empty for DateTime.MinValue (unset). - std::optional UnixSeconds() const - { - const auto ticks = static_cast(dateData & 0x3FFFFFFFFFFFFFFFull); - if (ticks <= 0) - return std::nullopt; - return (ticks - 621355968000000000LL) / 10000000LL; - } -}; - -// Concrete: ProgressData, CargoProgressData. -struct IProgressData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IProgressData">; - - double CurrentValue() - { return Get<"CurrentValue", double>(); } - double MaxValue() - { return Get<"MaxValue", double>(); } -}; - -struct CargoHoldData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "CargoHoldData">; - - IProgressData* CurrentCargo() - { return Get<"CurrentCargo", IProgressData*>(); } - IProgressData* ProtectedCargoProgress() - { return Get<"ProtectedCargoProgress", IProgressData*>(); } -}; - -struct IMiningNodeParams : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IMiningNodeParams">; - - int64_t Amount() - { return Get<"Amount", int64_t>(); } -}; - -// Concrete: FleetMiningSlot, MiningSlot. -struct IMiningSlot : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IMiningSlot">; - - int64_t ParentId() - { return Get<"ParentId", int64_t>(); } - int64_t ResourceId() - { return Get<"ResourceId", int64_t>(); } - double AmountMined() - { return Get<"AmountMined", double>(); } - DateTime OccupyTimeUtc() - { return Get<"OccupyTimeUtc", DateTime>(); } - IMiningNodeParams* PointData() - { return Get<"PointData", IMiningNodeParams*>(); } -}; - -// Concrete: MiningSlotTimer and others. -struct ITimerData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "ITimerData">; - - DateTime DueTime() - { return Get<"DueTime", DateTime>(); } -}; diff --git a/mods/src/prime/FleetLocalViewController.h b/mods/src/prime/FleetLocalViewController.h deleted file mode 100644 index fed1e224b..000000000 --- a/mods/src/prime/FleetLocalViewController.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "ActionData.h" -#include "ShipActionPanelLocalViewController.h" -#include "FleetPlayerData.h" -#include "ShipBarItemLocalViewController.h" - -#include - -#include - -struct FleetLocalViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "FleetLocalViewController">; - - ShipActionPanelLocalViewController* shipActionPanelLocalViewController() - { return Field<"_shipActionPanelLocalViewController", ShipActionPanelLocalViewController*>(); } - FleetPlayerData* fleet() - { return Get<"fleet", FleetPlayerData*>(); } - ShipBarItemLocalViewController* shipBarItemLocalViewController() - { return Field<"_shipBarItemLocalViewController", ShipBarItemLocalViewController*>(); } - - void CancelButtonClicked() - { Call<"CancelButtonClicked">(); } - - // callback is an Action; null is accepted. - bool RequestAction(IActionData target, ActionType type, int64_t index, ActionBehaviour behaviourMask, - void* callback = nullptr) - { return Call<"RequestAction", bool>(target, type, index, behaviourMask, callback); } -}; diff --git a/mods/src/prime/FleetMeshSelector.h b/mods/src/prime/FleetMeshSelector.h deleted file mode 100644 index cd77a5afb..000000000 --- a/mods/src/prime/FleetMeshSelector.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "GameObject.h" - -#include - -struct FleetMeshSelector : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "FleetMeshSelector">; - - GameObject* LoadedObject() - { return Get<"LoadedObject", GameObject*>(); } -}; diff --git a/mods/src/prime/FleetPlayerData.h b/mods/src/prime/FleetPlayerData.h deleted file mode 100644 index 66340354b..000000000 --- a/mods/src/prime/FleetPlayerData.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -#include "BattleTargetData.h" -#include "HullSpec.h" -#include "RecallRequirement.h" -#include "CanRepairRequirement.h" -#include "FleetLiveData.h" - -#include - -#include -#include - -enum class FleetState : int32_t { - Unknown = 0, - IdleInSpace = 1, - Docked = 2, - Mining = 4, - Destroyed = 8, - TieringUp = 16, - CanReplaceOfficers = 18, - Repairing = 32, - CannotLaunch = 56, - Battling = 64, - WarpCharging = 128, - Warping = 256, - CanRemove = 384, - Impulsing = 512, - CanActivateAbility = 513, - CanDisco = 515, - Capturing = 1024, - AutoHunting = 2048, - CannotMove = 2552, - CanManage = 2947, - CanBeTargetedByAbility = 3589, - CanEngage = 3591, - Outposting = 4096, - CanRecall = 5637, - Deployed = 8133, - CanLocate = 8135 -}; - -struct FleetPlayerData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "FleetPlayerData">; - - int32_t Index() - { return Get<"Index", int32_t>(); } - HullSpec* Hull() - { return Get<"Hull", HullSpec*>(); } - void* Address() - { return Get<"Address", void*>(); } - FleetState CurrentState() - { return Get<"CurrentState", FleetState>(); } - FleetState PreviousState() - { return Get<"PreviousState", FleetState>(); } - uint64_t Id() - { return Get<"Id", uint64_t>(); } - int64_t Level() - { return Get<"Level", int64_t>(); } - bool HasShip() - { return Get<"HasShip", bool>(); } - int64_t HullId() - { return Get<"HullId", int64_t>(); } - bool HasAppliedTech() - { - struct BaseShip : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "BaseShip">; - int64_t Id() - { return Get<"Id", int64_t>(); } - }; - struct Slot : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "EntitySlot">; - int64_t SpecId() - { return Get<"SlotSpecId", int64_t>(); } - bool HasItem() - { - static const auto* getter = Class::Method("get_SlotItemId", 0).Info(); - if (!getter || (getter->flags & METHOD_ATTRIBUTE_STATIC) - || getter->return_type->type != IL2CPP_TYPE_GENERICINST) - return false; - // Runtime invocation boxes Nullable as null or its value, without assuming its native layout. - return il2cpp::detail::InvokeBoxed(getter, this, nullptr) != nullptr; - } - }; - struct Manager : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ForbiddenTechs", "ForbiddenTechManager">; - bool ActiveSlots(int64_t ship_id, il2cpp::List*& slots) - { return Call<"TryGetActiveForbiddenTechSlot", bool>(ship_id, std::ref(slots)); } - }; - auto* ship = Get<"Ship", BaseShip*>(); - auto* manager = Manager::Class::Get<"Instance", Manager*>(); - il2cpp::List* slots = nullptr; - if (!ship || !manager || !manager->ActiveSlots(ship->Id(), slots) || !slots) - return false; - for (auto* slot : *slots) { - if (!slot) - continue; - const auto spec = slot->SpecId(); - if ((spec == 3502081615 || spec == 953301906) && slot->HasItem()) - return true; - } - return false; - } - - // Live state, read for fleet sync. - bool IsHome() - { return Get<"IsHome", bool>(); } - bool IsDeployed() - { return Get<"IsDeployed", bool>(); } - bool IsMining() - { return Get<"IsMining", bool>(); } - bool IsRepairing() - { return Get<"IsRepairing", bool>(); } - bool IsDamaged() - { return Get<"IsDamaged", bool>(); } - bool IsDestroyed() - { return Get<"IsDestroyed", bool>(); } - int32_t DrydockID() - { return Get<"DrydockID", int32_t>(); } - float CargoResourceFillLevel() - { return Get<"CargoResourceFillLevel", float>(); } - CargoHoldData* CargoHold() - { return Get<"CargoHoldData", CargoHoldData*>(); } - IMiningSlot* MiningData() - { return Get<"MiningData", IMiningSlot*>(); } - ITimerData* Timer() - { return Get<"Timer", ITimerData*>(); } - // No property for the mining timer, so read the private field. - ITimerData* MiningSlotTimer() - { return Field<"_miningSlotTimer", ITimerData*>(); } - float GetMiningRate() - { return Call<"GetMiningRate", float>(); } - // Damage on the fleet's ship, so a docked ship's hull is known without the server. Empty without a ship. - std::optional HullDamage() - { - // IShipModel, read on the ship's runtime class. - struct Ship : il2cpp::Object { - float HullDamage() - { return Get<"HullDamage", float>(); } - }; - auto* ship = Get<"Ship", Ship*>(); - return ship ? std::optional(ship->HullDamage()) : std::nullopt; - } - - int64_t GetLocaId() - { return Call<"GetLocaId", int64_t>(); } -}; diff --git a/mods/src/prime/FleetsManager.h b/mods/src/prime/FleetsManager.h deleted file mode 100644 index 8a00a1c90..000000000 --- a/mods/src/prime/FleetsManager.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "FleetDeployedData.h" -#include "FleetPlayerData.h" -#include "MonoSingleton.h" - -#include - -#include - -struct FleetsManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.FleetManagement", "FleetsManager">; - - void RequestViewFleet(FleetPlayerData* fleetData, bool showSystemInfo = false) - { Call<"RequestViewFleet">(fleetData, showSystemInfo); } - - // The int overload takes a fleet slot; the long overload takes a fleet id. - FleetPlayerData* GetFleetPlayerData(int32_t idx) - { return Call<"GetFleetPlayerData", FleetPlayerData*>(idx); } - - FleetPlayerData* GetSelectedFleetData() - { return Call<"GetSelectedFleetData", FleetPlayerData*>(); } - - FleetDeployedData* targetFleetData() - { return Field<"_targetFleetData", FleetDeployedData*>(); } - - // _fleetServiceCache is a CachedService struct whose HasService getter resolves and - // stores the service lazily. It has to run on the field in place, not on a boxed copy, so this - // stays on the raw runtime API. - bool HasFleetService() - { - static FieldInfo* const field = Class::Raw() ? il2cpp_class_get_field_from_name(Class::Raw(), "_fleetServiceCache") - : nullptr; - static const MethodInfo* const has_service = - field ? il2cpp_class_get_method_from_name(il2cpp_class_from_type(il2cpp_field_get_type(field)), - "get_HasService", 0) - : nullptr; - if (!has_service) { - return false; - } - - auto* cache = reinterpret_cast(this) + il2cpp_field_get_offset(field); - if (*reinterpret_cast(cache)) { - return true; - } - - Il2CppException* exception = nullptr; - auto* result = il2cpp_runtime_invoke(has_service, cache, nullptr, &exception); - return !exception && result && *static_cast(il2cpp_object_unbox(result)); - } -}; diff --git a/mods/src/prime/FullScreenChatViewController.h b/mods/src/prime/FullScreenChatViewController.h deleted file mode 100644 index 9a1687400..000000000 --- a/mods/src/prime/FullScreenChatViewController.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "ChatMessageListLocalViewController.h" -#include "TabBarViewController.h" - -#include - -struct FullScreenChatViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "FullScreenChatViewController">; - - ChatMessageListLocalViewController* messageList() - { return Field<"_messageList", ChatMessageListLocalViewController*>(); } - TabBarViewController* categoriesTabBarViewController() - { return Field<"_categoriesTabBarViewController", TabBarViewController*>(); } -}; diff --git a/mods/src/prime/GameObject.h b/mods/src/prime/GameObject.h deleted file mode 100644 index 0d6febde7..000000000 --- a/mods/src/prime/GameObject.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "str_utils.h" - -#include - -#include - -struct GameObject : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "GameObject">; - - std::string Name() - { - auto* name = Get<"name", Il2CppString*>(); - return name ? to_string(name) : std::string{}; - } - bool activeInHierarchy() - { return Get<"activeInHierarchy", bool>(); } - void SetActive(bool active) - { Call<"SetActive">(active); } - - // Passing System.Type selects the (Type) overloads over the string and generic ones. A null - // Type would throw in managed code, so a missing class yields null instead. - template T* GetComponent() - { - auto* type = T::Class::TypeObject(); - return type ? Call<"GetComponent", T*>(type) : nullptr; - } - template T* AddComponent() - { - auto* type = T::Class::TypeObject(); - return type ? Call<"AddComponent", T*>(type) : nullptr; - } -}; diff --git a/mods/src/prime/GenericButtonContext.h b/mods/src/prime/GenericButtonContext.h deleted file mode 100644 index b003b9b46..000000000 --- a/mods/src/prime/GenericButtonContext.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -struct GenericButtonContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "GenericButtonContext">; - - bool Interactable() - { return Get<"Interactable", bool>(); } - void SetInteractable(bool value) - { Set<"Interactable">(value); } -}; diff --git a/mods/src/prime/GenericButtonWidget.h b/mods/src/prime/GenericButtonWidget.h deleted file mode 100644 index 87ade7771..000000000 --- a/mods/src/prime/GenericButtonWidget.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "GenericButtonContext.h" -#include "SemaphoreButtonListener.h" -#include "TextLocalizer.h" -#include "Widget.h" - -struct GenericButtonWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "GenericButtonWidget">; - - SemaphoreButtonListener* SemaphoreListener() - { return Field<"_semaphoreButtonListener", SemaphoreButtonListener*>(); } - TextLocalizer* mainText() - { return Field<"_mainText", TextLocalizer*>(); } - bool Interactable() - { return Get<"Interactable", bool>(); } -}; diff --git a/mods/src/prime/GenericRewardsScreenViewController.h b/mods/src/prime/GenericRewardsScreenViewController.h deleted file mode 100644 index b3a33ed0a..000000000 --- a/mods/src/prime/GenericRewardsScreenViewController.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -struct GenericRewardsScreenViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.SharedFeatures", "GenericRewardsScreenViewController">; - - void OnCollectClicked() - { Call<"OnCollectClicked">(); } - bool IsActive() - { return Call<"IsActive", bool>(); } -}; diff --git a/mods/src/prime/HousingObjectViewerWidget.h b/mods/src/prime/HousingObjectViewerWidget.h deleted file mode 100644 index 1e21655f0..000000000 --- a/mods/src/prime/HousingObjectViewerWidget.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "ObjectViewerBaseWidget.h" - -struct HousingObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "HousingObjectViewerWidget">; -}; diff --git a/mods/src/prime/HttpJob.h b/mods/src/prime/HttpJob.h deleted file mode 100644 index efe7e2e27..000000000 --- a/mods/src/prime/HttpJob.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "HttpRequest.h" -#include "UnityWebRequest.h" - -#include - -#include - -enum class HttpJobState : int32_t { - None = 0, - Queued = 1, - RequestSending = 2, - ResponseReceiving = 3, - ResponseReceived = 4, - ResponseBuilt = 5, - WaitForAsyncCallback = 6, - Complete = 7 -}; - -struct HttpJob : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Engine.HttpClient.Runtime", "Digit.Engine.HttpClient", "HttpJob">; - - HttpRequest* Request() - { return Field<"Request", HttpRequest*>(); } - UnityWebRequest* UnityRequest() - { return Field<"UnityRequest", UnityWebRequest*>(); } - HttpJobState State() - { return Field<"State", HttpJobState>(); } - // Writes the field directly and bypasses the managed SetState(HttpJobState). - void SetState(HttpJobState state) - { SetField<"State">(state); } -}; diff --git a/mods/src/prime/HttpRequest.h b/mods/src/prime/HttpRequest.h deleted file mode 100644 index 207afa2c5..000000000 --- a/mods/src/prime/HttpRequest.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -struct HttpRequest : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Engine.HttpClient.Runtime", "Digit.Engine.HttpClient", "HttpRequest">; - - const Il2CppChar* get_URL() - { - auto* url = Get<"URL", Il2CppString*>(); - return url ? il2cpp_string_chars(url) : nullptr; - } -}; diff --git a/mods/src/prime/HttpResponse.h b/mods/src/prime/HttpResponse.h deleted file mode 100644 index c0515c04f..000000000 --- a/mods/src/prime/HttpResponse.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "HttpRequest.h" - -#include - -struct HttpResponse : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Engine.HttpClient.Runtime", "Digit.Engine.HttpClient", "HttpResponse">; - - HttpRequest* get_Request() - { return Get<"Request", HttpRequest*>(); } - Il2CppString* Body() - { return Get<"Body", Il2CppString*>(); } -}; diff --git a/mods/src/prime/HullSpec.h b/mods/src/prime/HullSpec.h deleted file mode 100644 index dae14687c..000000000 --- a/mods/src/prime/HullSpec.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include - -enum class HullType : int32_t { - Any = -1, - Destroyer = 0, - Survey = 1, - Explorer = 2, - Battleship = 3, - Defense = 4, - ArmadaTarget = 5 -}; - -struct IdRefs : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IdRefs">; - - Il2CppString* locaStringId() - { return Field<"_locaStringId", Il2CppString*>(); } - int64_t locaId() - { return Field<"locaId_", int64_t>(); } -}; - -struct HullSpec : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "HullSpec">; - - int64_t Id() - { return Field<"id_", int64_t>(); } - Il2CppString* Name() - { return Field<"name_", Il2CppString*>(); } - Il2CppString* IdStr() - { return Field<"idStr_", Il2CppString*>(); } - HullType Type() - { return Get<"Type", HullType>(); } - IdRefs* idRefs() - { return Field<"idRefs_", IdRefs*>(); } -}; diff --git a/mods/src/prime/IBuffComparer.h b/mods/src/prime/IBuffComparer.h deleted file mode 100644 index 46a81981a..000000000 --- a/mods/src/prime/IBuffComparer.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#include - -// Interface binding; the getters dispatch to the receiver's implementation. -struct IBuffComparer : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Services", "IBuffComparer">; - - int64_t get_HullId() - { return Get<"HullID", int64_t>(); } - int64_t get_FactionId() - { return Get<"FactionID", int64_t>(); } -}; diff --git a/mods/src/prime/IBuffData.h b/mods/src/prime/IBuffData.h deleted file mode 100644 index 3c1c64212..000000000 --- a/mods/src/prime/IBuffData.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "ClientModifierType.h" - -#include - -struct IBuffData : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit", "IBuffData">; - - ClientModifierType ModifierType() - { return Get<"ModifierType", ClientModifierType>(); } -}; diff --git a/mods/src/prime/IEnumerator.h b/mods/src/prime/IEnumerator.h deleted file mode 100644 index fc2c54dda..000000000 --- a/mods/src/prime/IEnumerator.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -struct IEnumerator : il2cpp::Object { -}; diff --git a/mods/src/prime/Image.h b/mods/src/prime/Image.h deleted file mode 100644 index fba0a1c9a..000000000 --- a/mods/src/prime/Image.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "Color.h" -#include "Component.h" - -#include - -#include - -struct Sprite : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Sprite">; - - float pixelsPerUnit() - { return Get<"pixelsPerUnit", float>(); } -}; - -// UnityEngine.UI.Image; color and raycastTarget come from Graphic. -struct Image : Component { - using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.UI", "Image">; - - enum class Type : int32_t { - Simple = 0, - Sliced = 1, - Tiled = 2, - Filled = 3, - }; - - Sprite* sprite() - { return Get<"sprite", Sprite*>(); } - void SetSprite(Sprite* value) - { Set<"sprite">(value); } - Sprite* overrideSprite() - { return Get<"overrideSprite", Sprite*>(); } - void SetOverrideSprite(Sprite* value) - { Set<"overrideSprite">(value); } - void SetType(Type value) - { Set<"type">(value); } - void SetPreserveAspect(bool value) - { Set<"preserveAspect">(value); } - void SetPixelsPerUnitMultiplier(float value) - { Set<"pixelsPerUnitMultiplier">(value); } - void SetColor(color value) - { Set<"color">(value); } - void SetRaycastTarget(bool value) - { Set<"raycastTarget">(value); } - void SetVerticesDirty() - { Call<"SetVerticesDirty">(); } -}; diff --git a/mods/src/prime/InputFieldWidget.h b/mods/src/prime/InputFieldWidget.h deleted file mode 100644 index d8d16e554..000000000 --- a/mods/src/prime/InputFieldWidget.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "TMP_InputField.h" - -#include - -struct InputFieldWidget : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "InputFieldWidget">; - - void Focus() - { Call<"Focus">(); } - - TMP_InputField* input() - { return Field<"_input", TMP_InputField*>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } -}; diff --git a/mods/src/prime/InterstitialViewController.h b/mods/src/prime/InterstitialViewController.h deleted file mode 100644 index b6f17e474..000000000 --- a/mods/src/prime/InterstitialViewController.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -struct InterstitialViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Interstitial", "InterstitialViewController">; - - void CloseWhenReady() - { Call<"CloseWhenReady">(); } -}; diff --git a/mods/src/prime/InventoryForPopup.h b/mods/src/prime/InventoryForPopup.h deleted file mode 100644 index e1258d63e..000000000 --- a/mods/src/prime/InventoryForPopup.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#include - -struct InventoryForPopup : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Inventories", "InventoryForPopup">; - - int64_t MaxItemsToUse() - { return Get<"MaxItemsToUse", int64_t>(); } - void SetMaxItemsToUse(int64_t value) - { Set<"MaxItemsToUse">(value); } - bool IsDonationUse() - { return Get<"IsDonationUse", bool>(); } - void SetIsDonationUse(bool value) - { Set<"IsDonationUse">(value); } - bool IsChestPurchase() - { return Get<"IsChestPurchase", bool>(); } -}; diff --git a/mods/src/prime/InventoryListViewController.h b/mods/src/prime/InventoryListViewController.h deleted file mode 100644 index fdd353cd2..000000000 --- a/mods/src/prime/InventoryListViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "CanvasController.h" -#include "Hub.h" -#include "InputFieldWidget.h" - -#include - -struct InventoryListViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Inventories", "InventoryListViewController">; - - InputFieldWidget* inputField() - { return Field<"_inputField", InputFieldWidget*>(); } - SectionID targetSection() - { return Field<"_targetSection", SectionID>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - CanvasController* canvasController() - { return GetCanvasControllerFromComponent(this); } -}; diff --git a/mods/src/prime/InventoryUseRowWidget.h b/mods/src/prime/InventoryUseRowWidget.h deleted file mode 100644 index 5e9db5d2e..000000000 --- a/mods/src/prime/InventoryUseRowWidget.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "InventoryForPopup.h" -#include "Widget.h" - -struct InventoryUseRowWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Inventories", "InventoryUseRowWidget">; -}; diff --git a/mods/src/prime/LanguageManager.h b/mods/src/prime/LanguageManager.h deleted file mode 100644 index 339c2df18..000000000 --- a/mods/src/prime/LanguageManager.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "LocaleTextContext.h" -#include "MonoSingleton.h" - -#include - -#include - -struct LanguageManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Localization", "LanguageManager">; - - bool TryGetTranslation(Il2CppString* category, int32_t key, Il2CppString** translatedText) - { - if (!translatedText) - return false; - return Call<"TryGetTranslation", bool(Il2CppString*, int32_t, Il2CppString*&)>(category, key, *translatedText); - } - - bool Localize(Il2CppString*& translation, LocaleTextContext* context) - { return Call<"Localize", bool(Il2CppString*&, LocaleTextContext*)>(translation, context); } - - static void ClearCache() - { Class::Call<"ClearCache">(); } -}; diff --git a/mods/src/prime/LocaleTextContext.h b/mods/src/prime/LocaleTextContext.h deleted file mode 100644 index 7d7fef4cc..000000000 --- a/mods/src/prime/LocaleTextContext.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -#include - -// Identifier + category pair that the localizer turns into display text. -// Construct with il2cpp::New(identifier, category). -struct LocaleTextContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "LocaleTextContext">; - - // Fills the {N} slots of the identifier itself, e.g. "ship_name_{0}". - void ApplyIdentifierParameters(il2cpp::Array* parameters) - { Call<"ApplyIdentifierParameters">(parameters); } -}; - -struct LocaleUtilities { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Localization", "LocaleUtilities">; - - static Il2CppString* Localize(LocaleTextContext* context, bool localizeTextParams = false, - bool invariantCulture = false) - { return Class::Call<"Localize", Il2CppString*>(context, localizeTextParams, invariantCulture); } -}; - -struct Localizer { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Localization", "Localizer">; - - // textCase is TextLocalizer.TextCase; 0 leaves the text as translated. - static Il2CppString* Localise(LocaleTextContext* context, bool forceCommas = false, int32_t textCase = 0) - { return Class::Call<"Localise", Il2CppString*>(context, forceCommas, textCase); } -}; diff --git a/mods/src/prime/MiningObjectViewerWidget.h b/mods/src/prime/MiningObjectViewerWidget.h deleted file mode 100644 index c9a9cd89f..000000000 --- a/mods/src/prime/MiningObjectViewerWidget.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "ObjectViewerBaseWidget.h" -#include "ScanEngageButtonsWidget.h" - -#include - -#include - -// MiningObjectViewerWidget.OccupiedState, a private nested enum. -enum class OccupiedState : int32_t { - NotOccupied = 0, - LocalPlayerOccupied = 1, - OtherPlayerOccupied = 2 -}; - -struct MiningObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "MiningObjectViewerWidget">; - - void MineClicked() - { Call<"MineClicked">(); } - - OccupiedState occupiedState() - { return Field<"_occupiedState", OccupiedState>(); } - ScanEngageButtonsWidget* scanEngageButtonsWidget() - { return Field<"_scanEngageButtonsWidget", ScanEngageButtonsWidget*>(); } -}; diff --git a/mods/src/prime/MissionsObjectViewerWidget.h b/mods/src/prime/MissionsObjectViewerWidget.h deleted file mode 100644 index f1c0b34e5..000000000 --- a/mods/src/prime/MissionsObjectViewerWidget.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "ObjectViewerBaseWidget.h" - -struct MissionsObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "MissionsObjectViewerWidget">; -}; diff --git a/mods/src/prime/MonoSingleton.h b/mods/src/prime/MonoSingleton.h deleted file mode 100644 index 0a592e4c1..000000000 --- a/mods/src/prime/MonoSingleton.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -// Unity singletons expose a static Instance on their MonoSingleton`1 base. -template struct MonoSingleton : il2cpp::Object { - static T* Instance() - { return T::Class::template Get<"Instance", T*>(); } - // The singleton s_instance already holds, or null. Instance can search the scene or create a replacement. - static T* Existing() - { return T::Class::template Field<"s_instance", T*>(); } -}; diff --git a/mods/src/prime/NavigationFleetWidget.h b/mods/src/prime/NavigationFleetWidget.h deleted file mode 100644 index b1c73cc16..000000000 --- a/mods/src/prime/NavigationFleetWidget.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "FleetDeployedData.h" -#include "NavigationLOD.h" -#include "Widget.h" - -struct NavigationFleetWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationFleetWidget">; - - NavigationLOD* lod() - { return Field<"_lod", NavigationLOD*>(); } -}; diff --git a/mods/src/prime/NavigationInteractionUIContext.h b/mods/src/prime/NavigationInteractionUIContext.h deleted file mode 100644 index 3a1b7c309..000000000 --- a/mods/src/prime/NavigationInteractionUIContext.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -struct NavigationInteractionUIContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationInteractionUIContext">; -}; diff --git a/mods/src/prime/NavigationInteractionUIViewController.h b/mods/src/prime/NavigationInteractionUIViewController.h deleted file mode 100644 index 7ea98bc27..000000000 --- a/mods/src/prime/NavigationInteractionUIViewController.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include "VisibilityController.h" -#include "SemaphoreButtonListener.h" - -struct SetCourseWidget : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "SetCourseWidget">; - VisibilityController* visibilityController() - { return Field<"_visibilityController", VisibilityController*>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - SemaphoreButtonListener* joinWaveDefenseButton() - { return Field<"_joinWaveDefenseButton", SemaphoreButtonListener*>(); } -}; - -struct NavigationInteractionUIViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationInteractionUIViewController">; - - bool TryPressJoinWaveDefense() - { - if (!IsSetCourseVisible()) { - return false; - } - auto* widget = Field<"_setCourseWidget", SetCourseWidget*>(); - auto* listener = widget ? widget->joinWaveDefenseButton() : nullptr; - if (!listener || !listener->isActiveAndEnabled()) { - return false; - } - // Button.Press preserves Unity's active/interactable checks and the native join callback. - if (auto* button = listener->TheButton()) { - button->Press(); - } - return true; - } - - bool IsSetCourseVisible() - { - auto* widget = Field<"_setCourseWidget", SetCourseWidget*>(); - auto* visibility = widget ? widget->visibilityController() : nullptr; - return visibility && (visibility->state() == VisibilityState::Visible || visibility->state() == VisibilityState::Show) - && widget->isActiveAndEnabled(); - } - void OnSetCourseButtonClick() - { Call<"OnSetCourseButtonClick">(); } -}; diff --git a/mods/src/prime/NavigationLOD.h b/mods/src/prime/NavigationLOD.h deleted file mode 100644 index 5ce0f5a73..000000000 --- a/mods/src/prime/NavigationLOD.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "ZoomLevels.h" - -#include - -struct NavigationLOD : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationLOD">; - - void UpdateLOD(ZoomLevels level) - { Call<"UpdateLOD">(level); } - - // Writes the backing field directly; the managed setter is private. - void SetTargetLevel(ZoomLevels level) - { SetField<"k__BackingField">(level); } -}; diff --git a/mods/src/prime/NavigationManager.h b/mods/src/prime/NavigationManager.h deleted file mode 100644 index 96ac794f5..000000000 --- a/mods/src/prime/NavigationManager.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -struct NavigationManager : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationManager">; - - void HideInteraction() - { Call<"HideInteraction">(); } -}; diff --git a/mods/src/prime/NavigationPan.h b/mods/src/prime/NavigationPan.h deleted file mode 100644 index de5a1f119..000000000 --- a/mods/src/prime/NavigationPan.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "Vector2.h" - -#include - -#pragma pack(push, 1) -struct vec3 { - float x, y, z; -}; -#pragma pack(pop) - -struct NavigationPan : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationPan">; - - void MoveCamera(vec2 delta, bool isMomentum = false) - { Call<"MoveCamera">(delta, isMomentum); } - - static bool BlockPan() - { return Class::Field<"BlockPan", bool>(); } - - Vector2 lastDelta() - { return Field<"_lastDelta", Vector2>(); } - void SetLastDelta(Vector2 value) - { SetField<"_lastDelta">(value); } - bool panningStarted() - { return Field<"_panningStarted", bool>(); } - float deltaScaler() - { return Field<"_deltaScaler", float>(); } - float keyboardDeltaScaler() - { return Field<"_keyboardDeltaScaler", float>(); } - // Managed POI; only checked for null or cleared. - void* trackingPOI() - { return Field<"_trackingPOI", void*>(); } - void SetTrackingPOI(void* value) - { SetField<"_trackingPOI">(value); } - - float farMagRadiusRatioSystemNormal() - { return Field<"_farMagRadiusRatioSystemNormal", float>(); } - void SetFarMagRadiusRatioSystemNormal(float value) - { SetField<"_farMagRadiusRatioSystemNormal">(value); } - float farMagRadiusRatioSystemExtended() - { return Field<"_farMagRadiusRatioSystemExtended", float>(); } - void SetFarMagRadiusRatioSystemExtended(float value) - { SetField<"_farMagRadiusRatioSystemExtended">(value); } - float nearMagRadiusRatioSystem() - { return Field<"_nearMagRadiusRatioSystemNormal", float>(); } -}; diff --git a/mods/src/prime/NavigationSectionManager.h b/mods/src/prime/NavigationSectionManager.h deleted file mode 100644 index d2b7dd4ba..000000000 --- a/mods/src/prime/NavigationSectionManager.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "Hub.h" -#include "NavigationManager.h" - -#include - -#include - -struct NavigationSectionManager : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationSectionManager">; - - static void ChangeNavigationSection(SectionID sectionId, int64_t systemId = -1) - { Class::Call<"ChangeNavigationSection">(sectionId, systemId); } - - static NavigationSectionManager* Instance() - { return Class::Get<"Instance", NavigationSectionManager*>(); } - - // Not NavigationManager(): that would hide the type inside this struct. - NavigationManager* SNavigationManager() - { return Get<"NavigationManager", NavigationManager*>(); } -}; diff --git a/mods/src/prime/NavigationZoom.h b/mods/src/prime/NavigationZoom.h deleted file mode 100644 index ed03a6d6f..000000000 --- a/mods/src/prime/NavigationZoom.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "Camera.h" -#include "NavigationPan.h" -#include "Vector2.h" -#include "ZoomLevels.h" - -#include - -#include - -enum class NodeDepth : int32_t { - Galaxy = 1, - SolarSystem = 2, - PlanetSystem = 4, - Starbase = 8, -}; - -struct NavigationZoom : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationZoom">; - - void SetViewParameters(float radius, NodeDepth depth) - { Call<"SetViewParameters">(radius, depth); } - void ZoomCameraAtWorldPoint() - { Call<"ZoomCameraAtWorldPoint">(); } - - float Distance() - { return Get<"Distance", float>(); } - void SetDistance(float value) - { Set<"Distance">(value); } - float NormalizedZoom() - { return Get<"NormalizedZoom", float>(); } - - NodeDepth depth() - { return Field<"_depth", NodeDepth>(); } - void SetDepth(NodeDepth value) - { SetField<"_depth">(value); } - ZoomLevels zoomLevel() - { return Field<"_zoomLevel", ZoomLevels>(); } - Camera* sceneCamera() - { return Field<"_sceneCamera", Camera*>(); } - - float storedZoomDistanceSystem() - { return Field<"_storedZoomDistanceSystem", float>(); } - void SetStoredZoomDistanceSystem(float value) - { SetField<"_storedZoomDistanceSystem">(value); } - float systemDefaultZoomRatio() - { return Field<"_systemDefaultZoomRatio", float>(); } - void SetSystemDefaultZoomRatio(float value) - { SetField<"_systemDefaultZoomRatio">(value); } - float actualDistance() - { return Field<"_actualDistance", float>(); } - void SetActualDistance(float value) - { SetField<"_actualDistance">(value); } - float zoomtotal() - { return Field<"_zoomtotal", float>(); } - void SetZoomtotal(float value) - { SetField<"_zoomtotal">(value); } - float minimum() - { return Field<"_minimum", float>(); } - void SetMinimum(float value) - { SetField<"_minimum">(value); } - float maximum() - { return Field<"_maximum", float>(); } - void SetMaximum(float value) - { SetField<"_maximum">(value); } - float zoomDelta() - { return Field<"_zoomDelta", float>(); } - void SetZoomDelta(float value) - { SetField<"_zoomDelta">(value); } - float viewRadius() - { return Field<"_viewRadius", float>(); } - void SetViewRadius(float value) - { SetField<"_viewRadius">(value); } - float lastZoomDelta() - { return Field<"_lastZoomDelta", float>(); } - void SetLastZoomDelta(float value) - { SetField<"_lastZoomDelta">(value); } - vec2 zoomLocation() - { return Field<"_zoomLocation", vec2>(); } - void SetZoomLocation(vec2 value) - { SetField<"_zoomLocation">(value); } - vec3 worldPoint() - { return Field<"_worldPoint", vec3>(); } - void SetWorldPoint(vec3 value) - { SetField<"_worldPoint">(value); } - float farRatioSystemNormal() - { return Field<"_farRatioSystemNormal", float>(); } - void SetFarRatioSystemNormal(float value) - { SetField<"_farRatioSystemNormal">(value); } - float farRatioSystemExtended() - { return Field<"_farRatioSystemExtended", float>(); } - void SetFarRatioSystemExtended(float value) - { SetField<"_farRatioSystemExtended">(value); } -}; diff --git a/mods/src/prime/ObjectViewerBaseWidget.h b/mods/src/prime/ObjectViewerBaseWidget.h deleted file mode 100644 index 35be10c32..000000000 --- a/mods/src/prime/ObjectViewerBaseWidget.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "NavigationInteractionUIContext.h" -#include "ParentObjectViewerViewController.h" -#include "VisibilityController.h" -#include "Widget.h" - -struct ObjectViewerBaseWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "ObjectViewerBaseWidget">; - - void HideAllViewers() - { Call<"HideAllViewers">(); } - - VisibilityController* visibilityController() - { return Field<"_visibilityController", VisibilityController*>(); } - ParentObjectViewerViewController* Parent() - { return Get<"Parent", ParentObjectViewerViewController*>(); } - bool IsInfoShown() - { return Get<"IsInfoShown", bool>(); } - void SetIsInfoShown(bool value) - { Set<"IsInfoShown">(value); } -}; diff --git a/mods/src/prime/OfficerAssignmentViewController.h b/mods/src/prime/OfficerAssignmentViewController.h deleted file mode 100644 index af5d3f875..000000000 --- a/mods/src/prime/OfficerAssignmentViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "CanvasController.h" -#include "GenericButtonWidget.h" -#include "InputFieldWidget.h" - -#include - -struct OfficerAssignmentViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.OfficerAssignment", "OfficerAssignmentViewController">; - - InputFieldWidget* inputField() - { return Field<"_inputField", InputFieldWidget*>(); } - GenericButtonWidget* officerPresetsButton() - { return Field<"_officerPresetsButton", GenericButtonWidget*>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - CanvasController* canvasController() - { return GetCanvasControllerFromComponent(this); } -}; diff --git a/mods/src/prime/OrbitFrameProvider.h b/mods/src/prime/OrbitFrameProvider.h deleted file mode 100644 index d12d4975f..000000000 --- a/mods/src/prime/OrbitFrameProvider.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -struct OrbitFrameProvider : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.CameraController", "OrbitFrameProvider">; - - // Declared on AbstractOrbitFrameProvider. - float RotationAngleDelta() - { return Field<"_rotationAngleDelta", float>(); } - void SetRotationAngleDelta(float value) - { SetField<"_rotationAngleDelta">(value); } -}; diff --git a/mods/src/prime/ParentObjectViewerViewController.h b/mods/src/prime/ParentObjectViewerViewController.h deleted file mode 100644 index 867303b67..000000000 --- a/mods/src/prime/ParentObjectViewerViewController.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "NavigationInteractionUIContext.h" -#include "ViewController.h" -#include "VisibilityController.h" - -struct ParentObjectViewerViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "ParentObjectViewerViewController">; - - void ActivateViewerHUDState(bool isHUDOpen = false) - { Call<"ActivateViewerHUDState">(isHUDOpen); } - void TriggerInstantHide() - { Call<"TriggerInstantHide">(); } - void TriggerShow() - { Call<"TriggerShow">(); } - - VisibilityController* visibilityController() - { return Field<"_visibilityController", VisibilityController*>(); } - bool IsShowing() - { return Get<"IsShowing", bool>(); } -}; diff --git a/mods/src/prime/PlanetViewUtils.h b/mods/src/prime/PlanetViewUtils.h deleted file mode 100644 index bccde2892..000000000 --- a/mods/src/prime/PlanetViewUtils.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "PopulatedSystemData.h" -#include "Transform.h" - -#include - -struct PlanetViewUtils : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "PlanetViewUtils">; - - // Managed FlatRenderable. Calling this also runs any detour on get_FlatRenderable. - void* GetFlatRenderable() - { return Get<"FlatRenderable", void*>(); } - - PopulatedSystemData* popData() - { return Field<"_popData", PopulatedSystemData*>(); } - Transform* activeSystemParent() - { return Field<"_activeSystemParent", Transform*>(); } -}; diff --git a/mods/src/prime/PlanetaryBaseManager.h b/mods/src/prime/PlanetaryBaseManager.h deleted file mode 100644 index 464c2fdd3..000000000 --- a/mods/src/prime/PlanetaryBaseManager.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "Hub.h" -#include "MonoSingleton.h" - -#include - -// Older clients lack this class; there the binding logs it once and ViewOwnHaven does nothing. -struct PlanetaryBaseManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.PlanetaryBase", "PlanetaryBaseManager">; - - static void ViewOwnHaven() - { - auto* manager = Instance(); - auto* sections = Hub::get_SectionManager(); - if (!manager || !sections) - return; - - // Preserve the game's Iconian Gateway prerequisite and its locked-building message. - if (!manager->Call<"IsPlanetaryBaseCenterReady", bool>(true)) - return; - // PlanetarySectionContext is reserved for visiting another player's Haven. Passing no context makes the - // PlanetarySectionDirector initialize the complete local Haven state from the service cache. - sections->TriggerSectionChange(SectionID::Starbase_Planetary, nullptr, false, false, true); - } -}; diff --git a/mods/src/prime/PopulatedSystemData.h b/mods/src/prime/PopulatedSystemData.h deleted file mode 100644 index 01a1bcedb..000000000 --- a/mods/src/prime/PopulatedSystemData.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#include - -struct PopulatedSystemData : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "PopulatedSystemData">; - - void* backdrop() - { return Field<"_backdrop", void*>(); } - int64_t systemId() - { return Field<"_systemId", int64_t>(); } -}; diff --git a/mods/src/prime/PreScanStationTargetWidget.h b/mods/src/prime/PreScanStationTargetWidget.h deleted file mode 100644 index 25991e3e9..000000000 --- a/mods/src/prime/PreScanStationTargetWidget.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "PreScanTargetWidget.h" - -struct PreScanStationTargetWidget : PreScanTargetWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "PreScanStationTargetWidget">; -}; diff --git a/mods/src/prime/PreScanTargetWidget.h b/mods/src/prime/PreScanTargetWidget.h deleted file mode 100644 index fbd30c253..000000000 --- a/mods/src/prime/PreScanTargetWidget.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "BattleTargetData.h" -#include "GenericButtonWidget.h" -#include "ObjectViewerBaseWidget.h" -#include "RewardsButtonWidget.h" -#include "ScanEngageButtonsWidget.h" - -struct PreScanTargetWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "PreScanTargetWidget">; - - void ValidateThenCreateArmada() - { Call<"ValidateThenCreateArmada">(); } - void OnAddToQueueClickedEventHandler() - { Call<"OnAddToQueueClickedEventHandler">(); } - // Declared on ObjectViewerBaseWidget. - void OnInfoClick() - { Call<"OnInfoClick">(); } - - BattleTargetData* battleTargetData() - { return Field<"_battleTargetData", BattleTargetData*>(); } - ScanEngageButtonsWidget* scanEngageButtonsWidget() - { return Field<"_scanEngageButtonsWidget", ScanEngageButtonsWidget*>(); } - RewardsButtonWidget* rewardsButtonWidget() - { return Field<"_rewardsButtonWidget", RewardsButtonWidget*>(); } - GenericButtonWidget* addToQueueButtonWidget() - { return Field<"_addToQueueButtonWidget", GenericButtonWidget*>(); } - GenericButtonWidget* armadaAttackButton() - { return Field<"_armadaAttackButton", GenericButtonWidget*>(); } -}; diff --git a/mods/src/prime/RealtimeDataPayload.h b/mods/src/prime/RealtimeDataPayload.h deleted file mode 100644 index e28e6ed13..000000000 --- a/mods/src/prime/RealtimeDataPayload.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#include - -enum DataType : int32_t -{ - Unspecified = -1, - JSON = 0, - MsgPack = 1, - ProtoBuff = 2, - UrlEncoded = 3, -}; - -struct RealtimeDataPayload : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.Networking.RTC", "RealtimeDataPayload">; - - // Managed DataType is a string, not the DataType enum above. - Il2CppString* DataType() - { return Get<"DataType", Il2CppString*>(); } - Il2CppString* ChannelId() - { return Get<"ChannelId", Il2CppString*>(); } - int32_t InstanceIdJson() - { return Get<"InstanceIdJson", int32_t>(); } - Il2CppString* Target() - { return Get<"Target", Il2CppString*>(); } - Il2CppString* Source() - { return Get<"Source", Il2CppString*>(); } - Il2CppString* Data() - { return Get<"Data", Il2CppString*>(); } -}; diff --git a/mods/src/prime/RecallRequirement.h b/mods/src/prime/RecallRequirement.h deleted file mode 100644 index f3afff55d..000000000 --- a/mods/src/prime/RecallRequirement.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "ActionRequirement.h" - -struct RecallRequirement : ActionRequirement { - using Class = - il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "FleetPlayerData/RecallRequirement">; -}; diff --git a/mods/src/prime/RectTransform.h b/mods/src/prime/RectTransform.h deleted file mode 100644 index 36a6f72c4..000000000 --- a/mods/src/prime/RectTransform.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Transform.h" -#include "Vector2.h" - -#include - -struct RectTransform : Transform { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "RectTransform">; - - Vector2 anchorMin() - { return Get<"anchorMin", Vector2>(); } - Vector2 anchorMax() - { return Get<"anchorMax", Vector2>(); } - Vector2 offsetMin() - { return Get<"offsetMin", Vector2>(); } - Vector2 offsetMax() - { return Get<"offsetMax", Vector2>(); } - void SetOffsetMin(Vector2 value) - { Set<"offsetMin">(value); } - void SetOffsetMax(Vector2 value) - { Set<"offsetMax">(value); } - void SetAnchorMin(Vector2 value) - { Set<"anchorMin">(value); } - void SetAnchorMax(Vector2 value) - { Set<"anchorMax">(value); } - void SetPivot(Vector2 value) - { Set<"pivot">(value); } - void SetSizeDelta(Vector2 value) - { Set<"sizeDelta">(value); } - void SetAnchoredPosition(Vector2 value) - { Set<"anchoredPosition">(value); } -}; diff --git a/mods/src/prime/RewardsButtonWidget.h b/mods/src/prime/RewardsButtonWidget.h deleted file mode 100644 index 982ef3d82..000000000 --- a/mods/src/prime/RewardsButtonWidget.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "BattleTargetData.h" -#include "VisibilityController.h" -#include "Widget.h" - -struct RewardsButtonWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "RewardsButtonWidget">; - - void RewardsClicked() - { Call<"RewardsClicked">(); } - - VisibilityController* rewardsController() - { return Field<"_rewardsController", VisibilityController*>(); } -}; diff --git a/mods/src/prime/ScanEngageButtonsWidget.h b/mods/src/prime/ScanEngageButtonsWidget.h deleted file mode 100644 index 7efd829c5..000000000 --- a/mods/src/prime/ScanEngageButtonsWidget.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "BattleTargetData.h" -#include "GenericButtonWidget.h" -#include "Widget.h" - -struct ScanEngageButtonsWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "ScanEngageButtonsWidget">; - - void OnArmadaButtonClicked() - { Call<"OnArmadaButtonClicked">(); } - void OnEngageButtonClicked() - { Call<"OnEngageButtonClicked">(); } - void OnScanButtonClicked() - { Call<"OnScanButtonClicked">(); } - - GenericButtonWidget* armadaButton() - { return Field<"_armadaButton", GenericButtonWidget*>(); } -}; diff --git a/mods/src/prime/ScanTargetViewController.h b/mods/src/prime/ScanTargetViewController.h deleted file mode 100644 index e635482e6..000000000 --- a/mods/src/prime/ScanTargetViewController.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "BattleTargetData.h" -#include "ScanEngageButtonsWidget.h" -#include "ViewController.h" - -struct ScanTargetViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "ScanTargetViewController">; - - ScanEngageButtonsWidget* scanEngageButtonsWidget() - { return Field<"_scanEngageButtonsWidget", ScanEngageButtonsWidget*>(); } -}; diff --git a/mods/src/prime/SceneArgs.h b/mods/src/prime/SceneArgs.h deleted file mode 100644 index 366c4d21d..000000000 --- a/mods/src/prime/SceneArgs.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -class SceneArgs -{ -}; \ No newline at end of file diff --git a/mods/src/prime/SceneManager.h b/mods/src/prime/SceneManager.h deleted file mode 100644 index 7c8dafaca..000000000 --- a/mods/src/prime/SceneManager.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -// Scene is a struct wrapping an int handle; the internal calls below take and fill it directly. -struct SceneManager { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine.SceneManagement", "SceneManager">; - - static const Il2CppChar* GetSceneName(void* scene) - { - static auto GetSceneName = - il2cpp::ResolveICall("UnityEngine.SceneManagement.Scene::GetNameInternal(System.Int32)"); - auto name = GetSceneName(scene); - return il2cpp_string_chars(name); - } - - static void* GetActiveScene() - { - static auto GetActiveScene_Injected = il2cpp::ResolveICall( - "UnityEngine.SceneManagement.SceneManager::GetActiveScene_Injected(UnityEngine.SceneManagement.Scene&)"); - void* scene = nullptr; - GetActiveScene_Injected(&scene); - return scene; - } -}; diff --git a/mods/src/prime/ScreenManager.h b/mods/src/prime/ScreenManager.h deleted file mode 100644 index 6a5132fc9..000000000 --- a/mods/src/prime/ScreenManager.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "CanvasController.h" -#include "CanvasScaler.h" -#include "MonoSingleton.h" - -struct UIFrameManager : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "UIFrameManager">; - - // The game clamps the width to the available space and the side frame's minimum. False when the call failed. - bool ResizeSideFrame(float width) - { return TryCall<"ResizeSideFrame", void(float)>(width); } -}; - -struct ScreenManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "ScreenManager">; - - static CanvasController* GetTopCanvas(bool visibleOnly = false) - { return Class::Call<"GetTopCanvas", CanvasController*>(visibleOnly); } - - // Every registered canvas, visible or not. - il2cpp::List* screens() - { return Field<"_screens", il2cpp::List*>(); } - CanvasScaler* canvasRootScaler() - { return Field<"m_canvasRootScaler", CanvasScaler*>(); } - float minimumCanvasScaleFactor() - { return Field<"_minimumCanvasScaleFactor", float>(); } - float maximumCanvasScaleFactor() - { return Field<"_maximumCanvasScaleFactor", float>(); } - UIFrameManager* frameManager() - { return Field<"_frameManager", UIFrameManager*>(); } -}; diff --git a/mods/src/prime/SelectableList.h b/mods/src/prime/SelectableList.h deleted file mode 100644 index 7de77238f..000000000 --- a/mods/src/prime/SelectableList.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include - -// Digit.Client.UI list hierarchy: BaseListContainer <- SelectableListBase <- SelectableList <- TabBar. -// Data is a System.Collections.IList; elements are whatever the list was bound to. -struct BaseListContainer : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "BaseListContainer">; - - il2cpp::List* data() - { return Field<"_data", il2cpp::List*>(); } - int32_t Count() - { return Get<"Count", int32_t>(); } -}; - -struct SelectableList : BaseListContainer { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SelectableList">; - - int32_t SelectedIndex() - { return Get<"SelectedIndex", int32_t>(); } - - // Declared on SelectableListBase. The int overload is picked over RequestSelect(Component, bool). - void RequestSelect(int32_t index, bool simulated = false) - { Call<"RequestSelect">(index, simulated); } - - il2cpp::Object* DataItem(int32_t index) - { - auto* list = Get<"Data", il2cpp::List*>(); - return list ? (*list)[index] : nullptr; - } -}; diff --git a/mods/src/prime/SemaphoreButtonListener.h b/mods/src/prime/SemaphoreButtonListener.h deleted file mode 100644 index 8b29eb344..000000000 --- a/mods/src/prime/SemaphoreButtonListener.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "Button.h" -#include "SemaphoreListenerBase.h" - -struct SemaphoreButtonListener : SemaphoreListenerBase { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SemaphoreButtonListener">; - - Button* TheButton() - { return Field<"_button", Button*>(); } - bool Interactable() - { return Get<"Interactable", bool>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - void SetInteractable(bool value) - { Set<"Interactable">(value); } -}; diff --git a/mods/src/prime/SemaphoreListenerBase.h b/mods/src/prime/SemaphoreListenerBase.h deleted file mode 100644 index 13fc27052..000000000 --- a/mods/src/prime/SemaphoreListenerBase.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -struct SemaphoreListenerBase : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SemaphoreListenerBase">; -}; diff --git a/mods/src/prime/ServiceResponse.h b/mods/src/prime/ServiceResponse.h deleted file mode 100644 index 8a9c289cd..000000000 --- a/mods/src/prime/ServiceResponse.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "EntityGroup.h" - -#include - -struct ServiceResponse : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "ServiceResponse">; - - // Google.Protobuf.Collections.RepeatedField; List resolves Count/get_Item on it. - il2cpp::List* EntityGroups() - { return Get<"EntityGroups", il2cpp::List*>(); } -}; - -// The platform service's responses, Digit.Platform.Models. They pass through the same model registry code as game -// server responses, but their entity groups have their own type enum. -struct PlatformEntityGroup : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.Platform.Models", "EntityGroup">; - - enum Type : int32_t { - StaticTranslations = 0, - DynamicTranslations = 1, - StoreOffers = 2, - OrderVirtualGood = 3, - Json = 4, - TournamentBundles = 5, - EventRanking = 6, - EventsModel = 7, - LeaderboardRanking = 8, - LeaderboardsModel = 9, - EventNotification = 10, - PlayerStats = 11, - GameNews = 12, - FileSignedUrls = 13, - GameInfo = 14, - PlayerProfiles = 15, - PlayerlifecycleNotification = 16, - MinigamesList = 17, - GameNewsVideo = 18, - PromoCodeRedeem = 19, - StoreGroups = 20, - AllianceGameState = 21, - AllianceGameChangeNotification = 22, - ApmFiltersStatus = 23, - ApmFiltersUpdated = 24, - ListReferralConnections = 25, - RewardRedeem = 26, - LoginStreakRecalculate = 27, - OrdersVirtualGood = 28, - }; - - Type Type_() - { return Get<"Type", Type>(); } - ByteString* Group() - { return Get<"Group", ByteString*>(); } -}; - -struct PlatformServiceResponse : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.Platform.Models", "ServiceResponse">; - - il2cpp::List* EntityGroups() - { return Get<"EntityGroups", il2cpp::List*>(); } -}; diff --git a/mods/src/prime/ShipActionPanelLocalViewController.h b/mods/src/prime/ShipActionPanelLocalViewController.h deleted file mode 100644 index df6edc12a..000000000 --- a/mods/src/prime/ShipActionPanelLocalViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "GenericButtonWidget.h" - -struct AbilityTimeSpan { int64_t ticks; }; -struct AbilityTimerDataContext : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "TimerDataContext">; - AbilityTimeSpan RemainingTime() - { return Get<"RemainingTime", AbilityTimeSpan>(); } -}; -struct ShipActionPanelLocalViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipActionPanelLocalViewController">; - il2cpp::Array* abilityIDs() - { return Field<"_abilityIDs", il2cpp::Array*>(); } - il2cpp::Array* buttons() - { return Field<"_buttons", il2cpp::Array*>(); } - AbilityTimerDataContext* GetRemainingCooldown(int64_t abilityId) - { return Call<"GetRemainingCooldown", AbilityTimerDataContext*>(abilityId); } - bool HasShipAnActiveAbilityWithActiveEffect() - { return Call<"HasShipAnActiveAbilityWithActiveEffect", bool>(); } -}; diff --git a/mods/src/prime/ShipBarItemLocalViewController.h b/mods/src/prime/ShipBarItemLocalViewController.h deleted file mode 100644 index 5862bec1d..000000000 --- a/mods/src/prime/ShipBarItemLocalViewController.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -struct ShipBarItemLocalViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipBarItemLocalViewController">; -}; diff --git a/mods/src/prime/ShipManagementScreenStateViewController.h b/mods/src/prime/ShipManagementScreenStateViewController.h deleted file mode 100644 index 2719deb61..000000000 --- a/mods/src/prime/ShipManagementScreenStateViewController.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Button.h" -#include "ShipManagementViewContext.h" -#include "ViewController.h" - -struct ShipManagementScreenStateViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipManagementScreenStateViewController">; - - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - Button* officerIconButton() - { return Field<"_officerIconButton", Button*>(); } - Button* swapShipButton() - { return Field<"_swapShipButton", Button*>(); } -}; diff --git a/mods/src/prime/ShipManagementViewContext.h b/mods/src/prime/ShipManagementViewContext.h deleted file mode 100644 index 3a5e3c180..000000000 --- a/mods/src/prime/ShipManagementViewContext.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "FleetPlayerData.h" - -#include - -#include - -struct ShipManagementViewContext : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipManagementViewContext">; - - FleetPlayerData* CurrentFleet() - { return Get<"CurrentFleet", FleetPlayerData*>(); } - void Sort() - { Call<"Sort">(); } - int32_t CurrentIndex() - { return Get<"CurrentIndex", int32_t>(); } - void SetCurrentIndex(int32_t index) - { Set<"CurrentIndex">(index); } - void SetLastMovementWasToIncrement(bool increment) - { Set<"LastMovementWasToIncrement">(increment); } - int32_t ListCount() - { return Get<"ListCount", int32_t>(); } - il2cpp::List* SortedIdleShips() - { return Field<"SortedIdleShips", il2cpp::List*>(); } -}; diff --git a/mods/src/prime/ShipManagementViewController.h b/mods/src/prime/ShipManagementViewController.h deleted file mode 100644 index 148f99dc1..000000000 --- a/mods/src/prime/ShipManagementViewController.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "ShipManagementViewContext.h" -#include "ViewController.h" - -struct ShipManagementViewController : ViewController { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipManagementViewController">; - - // m_context lives on the generic ViewController base; field lookup walks the parents. - ShipManagementViewContext* Context() - { return Field<"m_context", ShipManagementViewContext*>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } -}; diff --git a/mods/src/prime/ShipTileWidget.h b/mods/src/prime/ShipTileWidget.h deleted file mode 100644 index a9cd16e57..000000000 --- a/mods/src/prime/ShipTileWidget.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "FleetPlayerData.h" -#include "Widget.h" - -struct ShipTileWidget : Widget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipTileWidget">; -}; diff --git a/mods/src/prime/ShopSummaryDirector.h b/mods/src/prime/ShopSummaryDirector.h deleted file mode 100644 index 2765c0388..000000000 --- a/mods/src/prime/ShopSummaryDirector.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#include - -struct ShopSummaryDirector : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Shop", "ShopSummaryDirector">; - - // SectionID[] declared on SectionDirectorBase; elements are the SectionID enum (Hub.h). - il2cpp::Array* backLogicSkipSectionIds() - { return Field<"_backLogicSkipSectionIds", il2cpp::Array*>(); } -}; diff --git a/mods/src/prime/ShortcutsManager.h b/mods/src/prime/ShortcutsManager.h deleted file mode 100644 index e1f814a2c..000000000 --- a/mods/src/prime/ShortcutsManager.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "MonoSingleton.h" - -struct ShortcutsManager : MonoSingleton { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.GameInput", "ShortcutsManager">; - - bool ShowKeybindings() - { return Get<"ShowKeybindings", bool>(); } - void SetShowKeybindings(bool value) - { Set<"ShowKeybindings">(value); } - // Private static gate the native shortcut actions check (tutorial, modals, text input). - static bool CanUseShortcuts() - { return Class::Get<"CanUseShortcuts", bool>(); } -}; diff --git a/mods/src/prime/SpecService.h b/mods/src/prime/SpecService.h deleted file mode 100644 index 16f75dde7..000000000 --- a/mods/src/prime/SpecService.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "HullSpec.h" - -#include - -#include - -struct SpecService : il2cpp::Object { - using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Services", "SpecService">; - - HullSpec* GetHull(int64_t hullId) - { return Call<"GetHull", HullSpec*>(hullId); } -}; diff --git a/mods/src/prime/StarNodeObjectViewerWidget.h b/mods/src/prime/StarNodeObjectViewerWidget.h deleted file mode 100644 index 7eaf95ca1..000000000 --- a/mods/src/prime/StarNodeObjectViewerWidget.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "GenericButtonWidget.h" -#include "ObjectViewerBaseWidget.h" - -struct StarNodeObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "StarNodeObjectViewerWidget">; - - void OnViewButtonActivation() - { Call<"OnViewButtonActivation">(); } - void InitiateWarp() - { Call<"InitiateWarp">(); } - GenericButtonWidget* visitButton() - { return Field<"_visitButton", GenericButtonWidget*>(); } -}; diff --git a/mods/src/prime/StateContainer.h b/mods/src/prime/StateContainer.h deleted file mode 100644 index a6b3bbac4..000000000 --- a/mods/src/prime/StateContainer.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -// Digit.PrimeServer.Models.StateContainer, a managed struct. Layout matches T values of at most -// pointer alignment (the game uses it with int enums). -template struct StateContainer { - T currentState; - T previousState; - void* equalityComparer; - - T CurrentState() const - { return currentState; } - T PreviousState() const - { return previousState; } -}; diff --git a/mods/src/prime/SystemViewScaler.h b/mods/src/prime/SystemViewScaler.h deleted file mode 100644 index 2a82b2b74..000000000 --- a/mods/src/prime/SystemViewScaler.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -struct SystemViewScaler : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "SystemViewScaler">; - - float baseSystemRadius() - { return Field<"_baseSystemRadius", float>(); } - void SetBaseSystemRadius(float value) - { SetField<"_baseSystemRadius">(value); } -}; diff --git a/mods/src/prime/TKTouch.h b/mods/src/prime/TKTouch.h deleted file mode 100644 index 739087db6..000000000 --- a/mods/src/prime/TKTouch.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -#include - -enum class TouchPhase : int32_t { - Began = 0, - Moved = 1, - Stationary = 2, - Ended = 3, - Canceled = 4, -}; - -struct TKTouch : il2cpp::Object { - using Class = il2cpp::Class<"TouchKit", "", "TKTouch">; - - TouchPhase phase() - { return Field<"phase", TouchPhase>(); } - void SetPhase(TouchPhase phase) - { SetField<"phase">(phase); } -}; diff --git a/mods/src/prime/TMP_InputField.h b/mods/src/prime/TMP_InputField.h deleted file mode 100644 index b92afd234..000000000 --- a/mods/src/prime/TMP_InputField.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -struct TMP_InputField : il2cpp::Object { - using Class = il2cpp::Class<"Unity.TextMeshPro", "TMPro", "TMP_InputField">; - - bool isFocused() - { return Get<"isFocused", bool>(); } - - void ActivateInputField() - { Call<"ActivateInputField">(); } - void DeactivateInputField(bool clearSelection = false) - { Call<"DeactivateInputField">(clearSelection); } - void SendOnFocusLost() - { Call<"SendOnFocusLost">(); } - void SendOnFocus() - { Call<"SendOnFocus">(); } -}; diff --git a/mods/src/prime/TabBarViewController.h b/mods/src/prime/TabBarViewController.h deleted file mode 100644 index b0259c980..000000000 --- a/mods/src/prime/TabBarViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "SelectableList.h" - -#include - -struct TabBar : SelectableList { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "TabBar">; - - // GenericButtonContext[] - il2cpp::Array* defaultOptions() - { return Field<"_defaultOptions", il2cpp::Array*>(); } -}; - -struct TabBarViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "TabBarViewController">; - - TabBar* tabBar() - { return Field<"_tabBar", TabBar*>(); } -}; diff --git a/mods/src/prime/TextLocalizer.h b/mods/src/prime/TextLocalizer.h deleted file mode 100644 index ee225d23b..000000000 --- a/mods/src/prime/TextLocalizer.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include - -// Digit.Client.UI.TextLocalizer: the label component that renders localized, parameterized text. -struct TextLocalizer : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "TextLocalizer">; - - // Declared on GraphicLocalizer<,>. - Il2CppString* identifier() - { return Field<"m_identifier", Il2CppString*>(); } - // The backing field, not the SignificantDecimals property. - int32_t significantDecimals() - { return Field<"m_significantDecimals", int32_t>(); } - void SetSignificantDecimals(int32_t value) - { SetField<"m_significantDecimals">(value); } - bool textOverride() - { return Field<"_textOverride", bool>(); } - // Declared on GraphicLocalizer<,>: the rendered graphic, resolved lazily even on inactive labels. - il2cpp::Object* Target() - { return Get<"Target", il2cpp::Object*>(); } - - Il2CppString* CurrentText() - { return Call<"CurrentText", Il2CppString*>(); } - void ClearTextOverride() - { Call<"ClearTextOverride">(); } - void OverrideLocalizedText(Il2CppString* text) - { Call<"OverrideLocalizedText">(text); } -}; - -struct ColourTextLocalizer : TextLocalizer { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "ColourTextLocalizer">; -}; diff --git a/mods/src/prime/Toast.h b/mods/src/prime/Toast.h deleted file mode 100644 index 0db5a0bc9..000000000 --- a/mods/src/prime/Toast.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include - -enum ToastState { - All = -1, - Standard = 0, - FactionWarning = 1, - FactionLevelUp = 2, - FactionLevelDown = 3, - FactionDiscovered = 4, - IncomingAttack = 5, - IncomingAttackFaction = 6, - FleetBattle = 7, - StationBattle = 8, - StationVictory = 9, - Victory = 10, - Defeat = 11, - StationDefeat = 12, - Tournament = 14, - ArmadaCreated = 15, - ArmadaCanceled = 16, - ArmadaIncomingAttack = 17, - ArmadaBattleWon = 18, - ArmadaBattleLost = 19, - DiplomacyUpdated = 20, - JoinedTakeover = 21, - CompetitorJoinedTakeover = 22, - AbandonedTerritory = 23, - TakeoverVictory = 24, - TakeoverDefeat = 25, - TreasuryProgress = 26, - TreasuryFull = 27, - Achievement = 28, - AssaultVictory = 29, - AssaultDefeat = 30, - ChallengeComplete = 31, - ChallengeFailed = 32, - StrikeHit = 33, - StrikeDefeat = 34, - WarchestProgress = 35, - WarchestFull = 36, - PartialVictory = 37, - ArenaTimeLeft = 38, - ChainedEventScored = 39, - FleetPresetApplied = 40, - SurgeWarmUpEnded = 41, - SurgeHostileGroupDefeated = 42, - SurgeTimeLeft = 43, - QueueForLeaseActivated = 44, - QueueForLeaseExpired = 45, - PermanentQueuePurchased = 46, - OutpostStartedOrEnded = 47, - CrossAllianceArmadaVictory = 48, - CrossAllianceArmadaDefeat = 49, - CrossAllianceArmadaPartialVictory = 50, - FactionWeeklyEventsProgress = 51, - FactionWeeklyEventsComplete = 52, - ArmadaPlayerBlocked = 53, - ArmadaPlayerUnblocked = 54, - DynamicCrisisUpdate = 55, - DynamicCrisisFailed = 56, - DynamicCrisisCompleted = 57, - GalacticAnomalySystemEntered = 58, -}; - -struct Toast : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "Toast">; - - il2cpp::Array* get_TextParameters() - { return Get<"TextParameters", il2cpp::Array*>(); } - // LocaleTextContext - void* get_TextLocaleTextContext() - { return Get<"TextLocaleTextContext", void*>(); } - Il2CppObject* get_Data() - { return Get<"Data", Il2CppObject*>(); } - int get_State() - { return Get<"State", ToastState>(); } -}; diff --git a/mods/src/prime/Transform.h b/mods/src/prime/Transform.h deleted file mode 100644 index 496db99a7..000000000 --- a/mods/src/prime/Transform.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "GameObject.h" -#include "Vector3.h" - -#include - -struct Transform : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Transform">; - - int32_t childCount() - { return Get<"childCount", int32_t>(); } - Transform* GetChild(int32_t index) - { return Call<"GetChild", Transform*>(index); } - Transform* parent() - { return Get<"parent", Transform*>(); } - GameObject* gameObject() - { return Get<"gameObject", GameObject*>(); } - Vector3 localScale() - { return Get<"localScale", Vector3>(); } - void SetLocalScale(Vector3 value) - { Set<"localScale">(value); } - void SetLocalEulerAngles(Vector3 value) - { Set<"localEulerAngles">(value); } - void SetParent(Transform* parent, bool worldPositionStays) - { Call<"SetParent">(parent, worldPositionStays); } - void SetAsFirstSibling() - { Call<"SetAsFirstSibling">(); } - // Finds a descendant by slash-separated path, e.g. "Hint/Background". - Transform* Find(Il2CppString* path) - { return Call<"Find", Transform*>(path); } -}; diff --git a/mods/src/prime/TransitionManager.h b/mods/src/prime/TransitionManager.h deleted file mode 100644 index 73210d33a..000000000 --- a/mods/src/prime/TransitionManager.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "BlurController.h" - -#include - -#include - -struct TransitionManager : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.LoadingScreen", "TransitionManager">; - - // Public field named BlurController; the S prefix keeps the type name visible in this scope. - BlurController* SBlurController() - { return Field<"BlurController", BlurController*>(); } - int32_t waitFrames() - { return Field<"_waitFrames", int32_t>(); } - void SetWaitFrames(int32_t value) - { SetField<"_waitFrames">(value); } -}; diff --git a/mods/src/prime/TransitionViewController.h b/mods/src/prime/TransitionViewController.h deleted file mode 100644 index c23b3a838..000000000 --- a/mods/src/prime/TransitionViewController.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -struct TransitionViewController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.LoadingScreen", "TransitionViewController">; - - // Image - void* staticOverride() - { return Field<"_staticOverride", void*>(); } - // Animator - void* animator() - { return Field<"_animator", void*>(); } - // SmartScroller - void* scroller() - { return Field<"_scroller", void*>(); } -}; diff --git a/mods/src/prime/UIBehaviour.h b/mods/src/prime/UIBehaviour.h deleted file mode 100644 index 07edd67a3..000000000 --- a/mods/src/prime/UIBehaviour.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -struct UIBehaviour : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.EventSystems", "UIBehaviour">; -}; diff --git a/mods/src/prime/UnityWebRequest.h b/mods/src/prime/UnityWebRequest.h deleted file mode 100644 index 5f636a94f..000000000 --- a/mods/src/prime/UnityWebRequest.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#include - -struct UnityWebRequest : il2cpp::Object { - using Class = il2cpp::Class<"UnityEngine.UnityWebRequestModule", "UnityEngine.Networking", "UnityWebRequest">; - - uintptr_t get_uploadHandler() - { return reinterpret_cast(Get<"uploadHandler", Il2CppObject*>()); } -}; diff --git a/mods/src/prime/UserProfileManager.h b/mods/src/prime/UserProfileManager.h index e032c778d..e030db6d7 100644 --- a/mods/src/prime/UserProfileManager.h +++ b/mods/src/prime/UserProfileManager.h @@ -1,6 +1,6 @@ #pragma once -#include "MonoSingleton.h" +#include "ui.h" #include "UserProfile.h" struct UserProfileManager : MonoSingleton { diff --git a/mods/src/prime/UserProfileWidget.h b/mods/src/prime/UserProfileWidget.h deleted file mode 100644 index 205f08845..000000000 --- a/mods/src/prime/UserProfileWidget.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include -struct PlayerTitleWidget : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.PlayerAvatars", "PlayerTitleWidget">; -}; -struct UserProfileWidget : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.PlayerProfile", "UserProfileWidget">; - PlayerTitleWidget* playerTitleWidget() - { return Field<"_playerTitleWidget", PlayerTitleWidget*>(); } -}; diff --git a/mods/src/prime/Vector2.h b/mods/src/prime/Vector2.h deleted file mode 100644 index 329435ede..000000000 --- a/mods/src/prime/Vector2.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#include - -struct Vector2 { - float x, y; -}; - -#pragma pack(push, 1) -struct vec2 { - float x, y; - - friend auto operator<=>(const vec2&, const vec2&) = default; -}; -#pragma pack(pop) \ No newline at end of file diff --git a/mods/src/prime/Vector3.h b/mods/src/prime/Vector3.h deleted file mode 100644 index 2f1b0cf32..000000000 --- a/mods/src/prime/Vector3.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -struct Vector3 { - using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Vector3">; - - float x; - float y; - float z; - - static Vector3 zero() - { return Class::Field<"zeroVector", Vector3>(); } -}; diff --git a/mods/src/prime/ViewController.h b/mods/src/prime/ViewController.h deleted file mode 100644 index 5ec1584ab..000000000 --- a/mods/src/prime/ViewController.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -// Digit.Client.UI.ViewController`1. Members resolve on each controller's runtime class. -template struct ViewController : il2cpp::Object { - T* CanvasContext() - { return Get<"CanvasContext", T*>(); } -}; diff --git a/mods/src/prime/VisibilityController.h b/mods/src/prime/VisibilityController.h deleted file mode 100644 index 87713329c..000000000 --- a/mods/src/prime/VisibilityController.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include - -enum class VisibilityState : int32_t // TypeDefIndex: 13743 -{ - Unknown = 0, - Show = 1, - Hide = 2, - Hidden = 3, - Visible = 4 -}; - -struct VisibilityController : il2cpp::Object { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "VisibilityController">; - - // bool selects Show(bool)/Hide(bool) over the int entry-point overloads. - void Show(bool instant = false) - { Call<"Show">(instant); } - void Hide(bool instant = false) - { Call<"Hide">(instant); } - - VisibilityState state() - { return Field<"_state", VisibilityState>(); } - VisibilityState State() - { return Get<"VisibilityState", VisibilityState>(); } -}; diff --git a/mods/src/prime/WaveDefenseObjectViewerWidget.h b/mods/src/prime/WaveDefenseObjectViewerWidget.h deleted file mode 100644 index ac8ada8bd..000000000 --- a/mods/src/prime/WaveDefenseObjectViewerWidget.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "GenericButtonWidget.h" -#include "ObjectViewerBaseWidget.h" - -struct WaveDefenseObjectViewerWidget : ObjectViewerBaseWidget { - using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "WaveDefenseObjectViewerWidget">; - - // Every active viewer of this class in the loaded scenes. - static il2cpp::Array* FindActiveViewers() - { - using UnityObject = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Object">; - auto* type = Class::TypeObject(); - return type ? UnityObject::Call<"FindObjectsOfType", il2cpp::Array*>(type) - : nullptr; - } - - GenericButtonWidget* primaryButton() - { return Field<"_primaryButton", GenericButtonWidget*>(); } - - void PressPrimaryButton() - { - auto* widget = primaryButton(); - if (!widget || !widget->isActiveAndEnabled()) { - return; - } - auto* context = widget->Context(); - auto* listener = widget->SemaphoreListener(); - if (context && context->Interactable() && listener) { - if (auto* button = listener->TheButton()) { - button->Press(); - } - } - } -}; diff --git a/mods/src/prime/Widget.h b/mods/src/prime/Widget.h deleted file mode 100644 index 24768ea59..000000000 --- a/mods/src/prime/Widget.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -// Digit.Client.UI.Widget`1. Members resolve on each widget's runtime class. -template struct Widget : il2cpp::Object { - T* Context() - { return Get<"Context", T*>(); } - bool enabled() - { return Get<"enabled", bool>(); } - bool isActiveAndEnabled() - { return Get<"isActiveAndEnabled", bool>(); } - bool IsActive() - { return Call<"IsActive", bool>(); } -}; diff --git a/mods/src/prime/ZoomLevels.h b/mods/src/prime/ZoomLevels.h deleted file mode 100644 index d5b0f1295..000000000 --- a/mods/src/prime/ZoomLevels.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -enum class ZoomLevels { - None = 0, - Near = 1, - Middle = 2, - Far = 3, -}; diff --git a/mods/src/prime/chat.h b/mods/src/prime/chat.h new file mode 100644 index 000000000..ba444959c --- /dev/null +++ b/mods/src/prime/chat.h @@ -0,0 +1,118 @@ +#pragma once + +// Chat manager and chat view controllers. + +#include "ui.h" +#include "unity.h" + +#include + +enum class ChatChannelCategory : int32_t { + None = -1, + Newbie = 0, + Global = 1, + Alliance = 2, + Private = 3, + Private_Message = 4, + Block = 5, + Regional = 6, +}; + +enum class ChatViewMode : int32_t { + Fullscreen = 0, + Side = 1, +}; + +struct ChatManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatManager">; + + void OpenChannel(ChatChannelCategory category = ChatChannelCategory::Alliance) + { Call<"OpenChannel">(category, il2cpp_string_new("")); } + + void OpenChannel(ChatChannelCategory category, ChatViewMode viewMode) + { + SetViewMode(viewMode); + OpenChannel(category); + } + + bool CanSeeNewbieChat() + { return Call<"CanSeeNewbieChat", bool>(); } + bool CanSeeRegionalChat() + { return Call<"CanSeeRegionalChat", bool>(); } + + bool IsSideChatAllowed() + { return Get<"IsSideChatAllowed", bool>(); } + bool IsSideChatOpen() + { return Get<"IsSideChatOpen", bool>(); } + bool isContextInitialised() + { return Field<"_isContextInitialised", bool>(); } + ChatViewMode ViewMode() + { return Get<"ViewMode", ChatViewMode>(); } + void SetViewMode(ChatViewMode value) + { Set<"ViewMode">(value); } +}; + +struct SwipeScroller : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SwipeScroller">; + + int32_t currentContentIndex() + { return Field<"_currentContentIndex", int32_t>(); } + void FocusOnInstantly(int32_t index) + { Call<"FocusOnInstantly">(index); } +}; + +struct ChatPreviewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatPreviewController">; + + // ImageSelector[] + il2cpp::Array* panelIndicators() + { return Field<"_panelIndicators", il2cpp::Array*>(); } + SwipeScroller* swipeScroller() + { return Field<"_swipeScroller", SwipeScroller*>(); } + ChatChannelCategory focusedPanel() + { return Field<"_focusedPanel", ChatChannelCategory>(); } + void SetFocusedPanel(ChatChannelCategory value) + { SetField<"_focusedPanel">(value); } + // Set by the game after its visibility refresh for a section change. + bool isSectionChangedCompleted() + { return Field<"_isSectionChangedCompleted", bool>(); } +}; + +struct ChatSectionContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatSectionContext">; + + ChatChannelCategory category() + { return Field<"_category", ChatChannelCategory>(); } +}; + +struct ChatMessageListLocalViewController : ViewController { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatMessageListLocalViewController">; + + TMP_InputField* inputField() + { return Field<"_inputField", TMP_InputField*>(); } + bool inputFieldSelected() + { return Field<"_inputFieldSelected", bool>(); } +}; + +struct FullScreenChatViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "FullScreenChatViewController">; + + ChatMessageListLocalViewController* messageList() + { return Field<"_messageList", ChatMessageListLocalViewController*>(); } + TabBarViewController* categoriesTabBarViewController() + { return Field<"_categoriesTabBarViewController", TabBarViewController*>(); } +}; + +struct PlayerTitleWidget : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.PlayerAvatars", "PlayerTitleWidget">; +}; +struct UserProfileWidget : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.PlayerProfile", "UserProfileWidget">; + PlayerTitleWidget* playerTitleWidget() + { return Field<"_playerTitleWidget", PlayerTitleWidget*>(); } +}; +struct ChatMessageWidget : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Chat", "ChatMessageWidget">; + UserProfileWidget* userProfileWidget() + { return Field<"_userProfileWidget", UserProfileWidget*>(); } +}; diff --git a/mods/src/prime/fleet.h b/mods/src/prime/fleet.h new file mode 100644 index 000000000..26804522a --- /dev/null +++ b/mods/src/prime/fleet.h @@ -0,0 +1,639 @@ +#pragma once + +// Player fleets and ships: fleet data, managers, the fleet bar and ship management screens. + +#include "Hub.h" +#include "UserProfile.h" +#include "ui.h" +#include "unity.h" + +#include + +#include +#include + +struct ActionRequirement : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "ActionRequirement">; + + bool IsMet() + { return Get<"IsMet", bool>(); } + bool CheckIsMet() + { return Call<"CheckIsMet", bool>(); } +}; + +struct RecallRequirement : ActionRequirement { + using Class = + il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "FleetPlayerData/RecallRequirement">; +}; + +struct CanRepairRequirement : ActionRequirement { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", + "FleetPlayerData/CanRepairRequirement">; +}; + +enum class HullType : int32_t { + Any = -1, + Destroyer = 0, + Survey = 1, + Explorer = 2, + Battleship = 3, + Defense = 4, + ArmadaTarget = 5 +}; + +struct IdRefs : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IdRefs">; + + Il2CppString* locaStringId() + { return Field<"_locaStringId", Il2CppString*>(); } + int64_t locaId() + { return Field<"locaId_", int64_t>(); } +}; + +struct HullSpec : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "HullSpec">; + + int64_t Id() + { return Field<"id_", int64_t>(); } + Il2CppString* Name() + { return Field<"name_", Il2CppString*>(); } + Il2CppString* IdStr() + { return Field<"idStr_", Il2CppString*>(); } + HullType Type() + { return Get<"Type", HullType>(); } + IdRefs* idRefs() + { return Field<"idRefs_", IdRefs*>(); } +}; + +enum class DeployedFleetType : int32_t { + Nonexistent, + Player, + Marauder, + NpcInstantiated, + Sentinel, + Alliance, + Challenge, +}; + +struct FleetDeployedData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "FleetDeployedData">; + + int64_t ID() + { return Get<"ID", int64_t>(); } + HullSpec* Hull() + { return Get<"Hull", HullSpec*>(); } + // Nonexistent when the property is missing. + DeployedFleetType FleetType() + { return Get<"FleetType", DeployedFleetType>(); } +}; + +struct BattleTargetData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "BattleTargetData">; + + FleetDeployedData* TargetFleetDeployedData() + { return Field<"TargetFleetDeployedData", FleetDeployedData*>(); } +}; + +enum class FleetState : int32_t { + Unknown = 0, + IdleInSpace = 1, + Docked = 2, + Mining = 4, + Destroyed = 8, + TieringUp = 16, + CanReplaceOfficers = 18, + Repairing = 32, + CannotLaunch = 56, + Battling = 64, + WarpCharging = 128, + Warping = 256, + CanRemove = 384, + Impulsing = 512, + CanActivateAbility = 513, + CanDisco = 515, + Capturing = 1024, + AutoHunting = 2048, + CannotMove = 2552, + CanManage = 2947, + CanBeTargetedByAbility = 3589, + CanEngage = 3591, + Outposting = 4096, + CanRecall = 5637, + Deployed = 8133, + CanLocate = 8135 +}; + +// Cargo, mining slot and timers off FleetPlayerData, read for fleet sync. The interfaces are bound as such: their +// getters dispatch to the runtime class, which also covers FleetMiningSlot's explicit IMiningSlot implementation. + +// System.DateTime: ticks in the low 62 bits, the Kind in the top two. +struct DateTime { + uint64_t dateData; + + // Unix seconds, or empty for DateTime.MinValue (unset). + std::optional UnixSeconds() const + { + const auto ticks = static_cast(dateData & 0x3FFFFFFFFFFFFFFFull); + if (ticks <= 0) + return std::nullopt; + return (ticks - 621355968000000000LL) / 10000000LL; + } +}; + +// Concrete: ProgressData, CargoProgressData. +struct IProgressData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IProgressData">; + + double CurrentValue() + { return Get<"CurrentValue", double>(); } + double MaxValue() + { return Get<"MaxValue", double>(); } +}; + +struct CargoHoldData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "CargoHoldData">; + + IProgressData* CurrentCargo() + { return Get<"CurrentCargo", IProgressData*>(); } + IProgressData* ProtectedCargoProgress() + { return Get<"ProtectedCargoProgress", IProgressData*>(); } +}; + +struct IMiningNodeParams : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IMiningNodeParams">; + + int64_t Amount() + { return Get<"Amount", int64_t>(); } +}; + +// Concrete: FleetMiningSlot, MiningSlot. +struct IMiningSlot : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "IMiningSlot">; + + int64_t ParentId() + { return Get<"ParentId", int64_t>(); } + int64_t ResourceId() + { return Get<"ResourceId", int64_t>(); } + double AmountMined() + { return Get<"AmountMined", double>(); } + DateTime OccupyTimeUtc() + { return Get<"OccupyTimeUtc", DateTime>(); } + IMiningNodeParams* PointData() + { return Get<"PointData", IMiningNodeParams*>(); } +}; + +// Concrete: MiningSlotTimer and others. +struct ITimerData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "ITimerData">; + + DateTime DueTime() + { return Get<"DueTime", DateTime>(); } +}; + +struct FleetPlayerData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "FleetPlayerData">; + + int32_t Index() + { return Get<"Index", int32_t>(); } + HullSpec* Hull() + { return Get<"Hull", HullSpec*>(); } + void* Address() + { return Get<"Address", void*>(); } + FleetState CurrentState() + { return Get<"CurrentState", FleetState>(); } + FleetState PreviousState() + { return Get<"PreviousState", FleetState>(); } + uint64_t Id() + { return Get<"Id", uint64_t>(); } + int64_t Level() + { return Get<"Level", int64_t>(); } + bool HasShip() + { return Get<"HasShip", bool>(); } + int64_t HullId() + { return Get<"HullId", int64_t>(); } + bool HasAppliedTech() + { + struct BaseShip : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "BaseShip">; + int64_t Id() + { return Get<"Id", int64_t>(); } + }; + struct Slot : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "EntitySlot">; + int64_t SpecId() + { return Get<"SlotSpecId", int64_t>(); } + bool HasItem() + { + static const auto* getter = Class::Method("get_SlotItemId", 0).Info(); + if (!getter || (getter->flags & METHOD_ATTRIBUTE_STATIC) + || getter->return_type->type != IL2CPP_TYPE_GENERICINST) + return false; + // Runtime invocation boxes Nullable as null or its value, without assuming its native layout. + return il2cpp::detail::InvokeBoxed(getter, this, nullptr) != nullptr; + } + }; + struct Manager : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ForbiddenTechs", "ForbiddenTechManager">; + bool ActiveSlots(int64_t ship_id, il2cpp::List*& slots) + { return Call<"TryGetActiveForbiddenTechSlot", bool>(ship_id, std::ref(slots)); } + }; + auto* ship = Get<"Ship", BaseShip*>(); + auto* manager = Manager::Class::Get<"Instance", Manager*>(); + il2cpp::List* slots = nullptr; + if (!ship || !manager || !manager->ActiveSlots(ship->Id(), slots) || !slots) + return false; + for (auto* slot : *slots) { + if (!slot) + continue; + const auto spec = slot->SpecId(); + if ((spec == 3502081615 || spec == 953301906) && slot->HasItem()) + return true; + } + return false; + } + + // Live state, read for fleet sync. + bool IsHome() + { return Get<"IsHome", bool>(); } + bool IsDeployed() + { return Get<"IsDeployed", bool>(); } + bool IsMining() + { return Get<"IsMining", bool>(); } + bool IsRepairing() + { return Get<"IsRepairing", bool>(); } + bool IsDamaged() + { return Get<"IsDamaged", bool>(); } + bool IsDestroyed() + { return Get<"IsDestroyed", bool>(); } + int32_t DrydockID() + { return Get<"DrydockID", int32_t>(); } + float CargoResourceFillLevel() + { return Get<"CargoResourceFillLevel", float>(); } + CargoHoldData* CargoHold() + { return Get<"CargoHoldData", CargoHoldData*>(); } + IMiningSlot* MiningData() + { return Get<"MiningData", IMiningSlot*>(); } + ITimerData* Timer() + { return Get<"Timer", ITimerData*>(); } + // No property for the mining timer, so read the private field. + ITimerData* MiningSlotTimer() + { return Field<"_miningSlotTimer", ITimerData*>(); } + float GetMiningRate() + { return Call<"GetMiningRate", float>(); } + // Damage on the fleet's ship, so a docked ship's hull is known without the server. Empty without a ship. + std::optional HullDamage() + { + // IShipModel, read on the ship's runtime class. + struct Ship : il2cpp::Object { + float HullDamage() + { return Get<"HullDamage", float>(); } + }; + auto* ship = Get<"Ship", Ship*>(); + return ship ? std::optional(ship->HullDamage()) : std::nullopt; + } + + int64_t GetLocaId() + { return Call<"GetLocaId", int64_t>(); } +}; + +using IActionData = void*; + +// Digit.PrimeServer.Models.ActionType is a struct around a flag value, not an enum, so it crosses the +// managed boundary as a 4-byte struct. +struct ActionType { + int32_t flagValue; + + friend bool operator==(ActionType, ActionType) = default; + + static const ActionType Invalid; + static const ActionType Upgrade; + static const ActionType Purchase; + static const ActionType Manage; + static const ActionType Harvest; + static const ActionType Repair; + static const ActionType View; + static const ActionType Recall; + static const ActionType Promote; + static const ActionType Demote; + static const ActionType Move; + static const ActionType Attack; + static const ActionType Assign; + static const ActionType Add; + static const ActionType Remove; + static const ActionType Claim; + static const ActionType Chat; + static const ActionType Help; + static const ActionType Invite; + static const ActionType Report; + static const ActionType Copy; + static const ActionType Share; +}; + +inline constexpr ActionType ActionType::Invalid {-1}; +inline constexpr ActionType ActionType::Upgrade {1433481724}; +inline constexpr ActionType ActionType::Purchase{1807968545}; +inline constexpr ActionType ActionType::Manage {-1997567611}; +inline constexpr ActionType ActionType::Harvest {-1933286583}; +inline constexpr ActionType ActionType::Repair {-1850668115}; +inline constexpr ActionType ActionType::View {2666181}; +inline constexpr ActionType ActionType::Recall {-1851055311}; +inline constexpr ActionType ActionType::Promote {1355442080}; +inline constexpr ActionType ActionType::Demote {2043415476}; +inline constexpr ActionType ActionType::Move {2404337}; +inline constexpr ActionType ActionType::Attack {1971575400}; +inline constexpr ActionType ActionType::Assign {1970629903}; +inline constexpr ActionType ActionType::Add {65665}; +inline constexpr ActionType ActionType::Remove {-1850743644}; +inline constexpr ActionType ActionType::Claim {65189916}; +inline constexpr ActionType ActionType::Chat {2099064}; +inline constexpr ActionType ActionType::Help {2245473}; +inline constexpr ActionType ActionType::Invite {-2099832023}; +inline constexpr ActionType ActionType::Report {-1850654380}; +inline constexpr ActionType ActionType::Copy {2106261}; +inline constexpr ActionType ActionType::Share {79847359}; + + +enum ActionBehaviour { + Default = 1, + Instant = 2, + Speedup = 4, + Collect = 8, + AskHelp = 16, + RequestConfirmation = 32, +}; + +struct ShipBarItemLocalViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipBarItemLocalViewController">; +}; + +struct AbilityTimeSpan { int64_t ticks; }; + +struct AbilityTimerDataContext : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "TimerDataContext">; + AbilityTimeSpan RemainingTime() + { return Get<"RemainingTime", AbilityTimeSpan>(); } +}; + +struct ShipActionPanelLocalViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipActionPanelLocalViewController">; + il2cpp::Array* abilityIDs() + { return Field<"_abilityIDs", il2cpp::Array*>(); } + il2cpp::Array* buttons() + { return Field<"_buttons", il2cpp::Array*>(); } + AbilityTimerDataContext* GetRemainingCooldown(int64_t abilityId) + { return Call<"GetRemainingCooldown", AbilityTimerDataContext*>(abilityId); } + bool HasShipAnActiveAbilityWithActiveEffect() + { return Call<"HasShipAnActiveAbilityWithActiveEffect", bool>(); } +}; + +struct FleetLocalViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "FleetLocalViewController">; + + ShipActionPanelLocalViewController* shipActionPanelLocalViewController() + { return Field<"_shipActionPanelLocalViewController", ShipActionPanelLocalViewController*>(); } + FleetPlayerData* fleet() + { return Get<"fleet", FleetPlayerData*>(); } + ShipBarItemLocalViewController* shipBarItemLocalViewController() + { return Field<"_shipBarItemLocalViewController", ShipBarItemLocalViewController*>(); } + + void CancelButtonClicked() + { Call<"CancelButtonClicked">(); } + + // callback is an Action; null is accepted. + bool RequestAction(IActionData target, ActionType type, int64_t index, ActionBehaviour behaviourMask, + void* callback = nullptr) + { return Call<"RequestAction", bool>(target, type, index, behaviourMask, callback); } +}; + +struct FleetBarContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "FleetBarContext">; + + il2cpp::Array* FleetData() + { return Get<"FleetData", il2cpp::Array*>(); } + + FleetPlayerData* CurrentFleet() + { return Get<"CurrentFleet", FleetPlayerData*>(); } +}; + +struct FleetBarViewController : ViewController { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "FleetBarViewController">; + + // The int overload; the other takes the list element Component. + void RequestSelect(int32_t index, bool simulated = false) + { Call<"RequestSelect">(index, simulated); } + bool IsIndexSelected(int32_t index) + { return Call<"IsIndexSelected", bool>(index); } + + FleetLocalViewController* fleetPanelController() + { return Field<"_fleetPanelController", FleetLocalViewController*>(); } +}; + +struct FleetsManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.FleetManagement", "FleetsManager">; + + void RequestViewFleet(FleetPlayerData* fleetData, bool showSystemInfo = false) + { Call<"RequestViewFleet">(fleetData, showSystemInfo); } + + // The int overload takes a fleet slot; the long overload takes a fleet id. + FleetPlayerData* GetFleetPlayerData(int32_t idx) + { return Call<"GetFleetPlayerData", FleetPlayerData*>(idx); } + + FleetPlayerData* GetSelectedFleetData() + { return Call<"GetSelectedFleetData", FleetPlayerData*>(); } + + FleetDeployedData* targetFleetData() + { return Field<"_targetFleetData", FleetDeployedData*>(); } + + // _fleetServiceCache is a CachedService struct whose HasService getter resolves and + // stores the service lazily. It has to run on the field in place, not on a boxed copy, so this + // stays on the raw runtime API. + bool HasFleetService() + { + static FieldInfo* const field = Class::Raw() ? il2cpp_class_get_field_from_name(Class::Raw(), "_fleetServiceCache") + : nullptr; + static const MethodInfo* const has_service = + field ? il2cpp_class_get_method_from_name(il2cpp_class_from_type(il2cpp_field_get_type(field)), + "get_HasService", 0) + : nullptr; + if (!has_service) { + return false; + } + + auto* cache = reinterpret_cast(this) + il2cpp_field_get_offset(field); + if (*reinterpret_cast(cache)) { + return true; + } + + Il2CppException* exception = nullptr; + auto* result = il2cpp_runtime_invoke(has_service, cache, nullptr, &exception); + return !exception && result && *static_cast(il2cpp_object_unbox(result)); + } +}; + +struct DeploymentService : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Services", "DeploymentService">; + + // The compiler-generated d__N iterator. Unbound so MoveNext resolves on whatever class the game + // returns; the suffix changes between client builds. + struct IEnumerator_PlanCourse : il2cpp::Object { + bool MoveNext() + { return Call<"MoveNext", bool>(); } + }; +}; + +// The managed class is DeploymentManager; the misspelled C++ name is kept for existing callers. +struct DeploymentManger : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "", "DeploymentManager">; + + void SetTowRequest(uint64_t towedFleetId, uint64_t towingFleetId) + { + // The game expects a CallbackContainer; an empty one without handlers is enough. Take the concrete + // generic instance from the method signature instead of spelling the inflated class out here. + static Il2CppClass* const callback_class = [] { + auto method = Class::Method("SetTowRequest", 3); + return method ? il2cpp_class_from_type(method.Info()->parameters[2]) : nullptr; + }(); + if (!callback_class) + return; + Call<"SetTowRequest">(towedFleetId, towingFleetId, il2cpp_object_new(callback_class)); + } + + bool HasInstantWarpAbility(FleetPlayerData* selectedFleet) + { return Call<"HasInstantWarpAbility", bool>(selectedFleet); } + + // targetAddress is a NodeAddress; the trailing data arguments are optional references. + DeploymentService::IEnumerator_PlanCourse* PlanCourse(FleetPlayerData* selectedFleet, void* targetAddress, + Vector3 targetPosition, FleetDeployedData* targetDeployedFleet, + void* starbaseData, void* allianceStarbaseData, + void* outpost = nullptr) + { + return Call<"PlanCourse", DeploymentService::IEnumerator_PlanCourse*>( + selectedFleet, targetAddress, targetPosition, targetDeployedFleet, starbaseData, allianceStarbaseData, outpost); + } +}; + +struct ActionQueueManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Prime.ActionQueue", "ActionQueueManager">; + + void ClearQueue(FleetPlayerData* playerData) + { Call<"ClearQueue">(playerData); } + bool IsQueueFull(FleetPlayerData* playerData) + { return Call<"IsQueueFull", bool>(playerData); } + bool IsFleetInQueue(FleetPlayerData* playerData) + { return Call<"IsFleetInQueue", bool>(playerData); } + bool IsQueueUnlocked() + { return Call<"IsQueueUnlocked", bool>(); } + + bool CanAddToQueue(FleetPlayerData* playerData) + { return IsQueueUnlocked() && !IsQueueFull(playerData); } + + void AddToQueue(int64_t targetId) + { Call<"AddActionToQueue">(targetId); } +}; + +struct FleetMeshSelector : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "FleetMeshSelector">; + + GameObject* LoadedObject() + { return Get<"LoadedObject", GameObject*>(); } +}; + +struct CourseData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "CourseData">; + + FleetPlayerData* PlayerFleet() + { return Get<"PlayerFleet", FleetPlayerData*>(); } +}; + +struct SpecService : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Services", "SpecService">; + + HullSpec* GetHull(int64_t hullId) + { return Call<"GetHull", HullSpec*>(hullId); } +}; + +struct ShipManagementViewContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipManagementViewContext">; + + FleetPlayerData* CurrentFleet() + { return Get<"CurrentFleet", FleetPlayerData*>(); } + void Sort() + { Call<"Sort">(); } + int32_t CurrentIndex() + { return Get<"CurrentIndex", int32_t>(); } + void SetCurrentIndex(int32_t index) + { Set<"CurrentIndex">(index); } + void SetLastMovementWasToIncrement(bool increment) + { Set<"LastMovementWasToIncrement">(increment); } + int32_t ListCount() + { return Get<"ListCount", int32_t>(); } + il2cpp::List* SortedIdleShips() + { return Field<"SortedIdleShips", il2cpp::List*>(); } +}; + +struct ShipManagementViewController : ViewController { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipManagementViewController">; + + // m_context lives on the generic ViewController base; field lookup walks the parents. + ShipManagementViewContext* Context() + { return Field<"m_context", ShipManagementViewContext*>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } +}; + +struct ShipManagementScreenStateViewController : ViewController { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipManagementScreenStateViewController">; + + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } + Button* officerIconButton() + { return Field<"_officerIconButton", Button*>(); } + Button* swapShipButton() + { return Field<"_swapShipButton", Button*>(); } +}; + +struct AssignShipsWidget : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "AssignShipsWidget">; + + InputFieldWidget* inputField() + { return Field<"_inputField", InputFieldWidget*>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } + ButtonAndContextWrapper* assignButton() + { return Field<"_assignButton", ButtonAndContextWrapper*>(); } + SelectableList* selectableList() + { return Field<"_selectableList", SelectableList*>(); } +}; + +struct ShipTileWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Ships", "ShipTileWidget">; +}; + +// Older clients lack this class; there the binding logs it once and ViewOwnHaven does nothing. +struct PlanetaryBaseManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.PlanetaryBase", "PlanetaryBaseManager">; + + static void ViewOwnHaven() + { + auto* manager = Instance(); + auto* sections = Hub::get_SectionManager(); + if (!manager || !sections) + return; + + // Preserve the game's Iconian Gateway prerequisite and its locked-building message. + if (!manager->Call<"IsPlanetaryBaseCenterReady", bool>(true)) + return; + // PlanetarySectionContext is reserved for visiting another player's Haven. Passing no context makes the + // PlanetarySectionDirector initialize the complete local Haven state from the service cache. + sections->TriggerSectionChange(SectionID::Starbase_Planetary, nullptr, false, false, true); + } +}; + +struct ActivatedAbilityManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ActivatedAbilities", "ActivatedAbilityManager">; + static constexpr int32_t RevealHostileVgerAbilityType = 86002; + int32_t GetActivatedAbilityType(int64_t abilityId) + { return Call<"GetActivatedAbilityType", int32_t>(abilityId); } + bool CanUseAbility(FleetPlayerData* fleet, int64_t abilityId) + { return Call<"CanUseAbility", bool>(fleet, abilityId); } +}; diff --git a/mods/src/prime/inventory.h b/mods/src/prime/inventory.h new file mode 100644 index 000000000..efb6011c3 --- /dev/null +++ b/mods/src/prime/inventory.h @@ -0,0 +1,97 @@ +#pragma once + +// Inventory, officer roster, and shop widgets. + +#include "Hub.h" +#include "ui.h" + +#include + +#include + +struct InventoryForPopup : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Inventories", "InventoryForPopup">; + + int64_t MaxItemsToUse() + { return Get<"MaxItemsToUse", int64_t>(); } + void SetMaxItemsToUse(int64_t value) + { Set<"MaxItemsToUse">(value); } + bool IsDonationUse() + { return Get<"IsDonationUse", bool>(); } + void SetIsDonationUse(bool value) + { Set<"IsDonationUse">(value); } + bool IsChestPurchase() + { return Get<"IsChestPurchase", bool>(); } +}; + +struct InventoryUseRowWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Inventories", "InventoryUseRowWidget">; +}; + +struct InventoryListViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Inventories", "InventoryListViewController">; + + InputFieldWidget* inputField() + { return Field<"_inputField", InputFieldWidget*>(); } + SectionID targetSection() + { return Field<"_targetSection", SectionID>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } + CanvasController* canvasController() + { return GetCanvasControllerFromComponent(this); } +}; + +struct OfficerAssignmentViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.OfficerAssignment", "OfficerAssignmentViewController">; + + InputFieldWidget* inputField() + { return Field<"_inputField", InputFieldWidget*>(); } + GenericButtonWidget* officerPresetsButton() + { return Field<"_officerPresetsButton", GenericButtonWidget*>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } + CanvasController* canvasController() + { return GetCanvasControllerFromComponent(this); } +}; + +struct Bundle : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimePlatform.Content", "Bundle">; +}; + +struct BundleDataWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Shop", "BundleDataWidget">; + + enum ItemState : int32_t { + None = 0, + Package = 1, + Highlighted = 2, + LegendOn = 4, + ExpiryTimerOn = 8, + DiscountOn = 16, // 0x00000010 + RemoteImage = 32, // 0x00000020 + Showcase = 256, // 0x00000100 + HasBanner = 512, // 0x00000200 + ShowViewButton = 1024, // 0x00000400 + ShowMaxPurchases = 2048, // 0x00000800 + ShowItemRarity = 4096, // 0x00001000 + ShowItemType = 8192, // 0x00002000 + CooldownTimerOn = 16384, // 0x00004000 + ShowSynthetic = 32768, // 0x00008000 + ShowPurchaseLimits = 65536, // 0x00010000 + ShowFreeTag = 131072, // 0x00020000 + }; + + ItemState CurrentState() + { return Field<"_currentState", ItemState>(); } + + void AuxViewButtonPressedHandler() + { Call<"AuxViewButtonPressedHandler">(); } +}; + +struct ShopSummaryDirector : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Shop", "ShopSummaryDirector">; + + // SectionID[] declared on SectionDirectorBase; elements are the SectionID enum (Hub.h). + il2cpp::Array* backLogicSkipSectionIds() + { return Field<"_backLogicSkipSectionIds", il2cpp::Array*>(); } +}; diff --git a/mods/src/prime/navigation.h b/mods/src/prime/navigation.h new file mode 100644 index 000000000..749e2a9c0 --- /dev/null +++ b/mods/src/prime/navigation.h @@ -0,0 +1,327 @@ +#pragma once + +// System and galaxy navigation: camera zoom and pan, LOD, sections, bookmarks, course prompts. + +#include "Hub.h" +#include "fleet.h" +#include "ui.h" +#include "unity.h" + +#include + +#include + +enum class ZoomLevels { + None = 0, + Near = 1, + Middle = 2, + Far = 3, +}; + +struct NavigationLOD : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationLOD">; + + void UpdateLOD(ZoomLevels level) + { Call<"UpdateLOD">(level); } + + // Writes the backing field directly; the managed setter is private. + void SetTargetLevel(ZoomLevels level) + { SetField<"k__BackingField">(level); } +}; + +#pragma pack(push, 1) +struct vec3 { + float x, y, z; +}; +#pragma pack(pop) + +struct NavigationPan : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationPan">; + + void MoveCamera(vec2 delta, bool isMomentum = false) + { Call<"MoveCamera">(delta, isMomentum); } + + static bool BlockPan() + { return Class::Field<"BlockPan", bool>(); } + + Vector2 lastDelta() + { return Field<"_lastDelta", Vector2>(); } + void SetLastDelta(Vector2 value) + { SetField<"_lastDelta">(value); } + bool panningStarted() + { return Field<"_panningStarted", bool>(); } + float deltaScaler() + { return Field<"_deltaScaler", float>(); } + float keyboardDeltaScaler() + { return Field<"_keyboardDeltaScaler", float>(); } + // Managed POI; only checked for null or cleared. + void* trackingPOI() + { return Field<"_trackingPOI", void*>(); } + void SetTrackingPOI(void* value) + { SetField<"_trackingPOI">(value); } + + float farMagRadiusRatioSystemNormal() + { return Field<"_farMagRadiusRatioSystemNormal", float>(); } + void SetFarMagRadiusRatioSystemNormal(float value) + { SetField<"_farMagRadiusRatioSystemNormal">(value); } + float farMagRadiusRatioSystemExtended() + { return Field<"_farMagRadiusRatioSystemExtended", float>(); } + void SetFarMagRadiusRatioSystemExtended(float value) + { SetField<"_farMagRadiusRatioSystemExtended">(value); } + float nearMagRadiusRatioSystem() + { return Field<"_nearMagRadiusRatioSystemNormal", float>(); } +}; + +enum class NodeDepth : int32_t { + Galaxy = 1, + SolarSystem = 2, + PlanetSystem = 4, + Starbase = 8, +}; + +struct NavigationZoom : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationZoom">; + + void SetViewParameters(float radius, NodeDepth depth) + { Call<"SetViewParameters">(radius, depth); } + void ZoomCameraAtWorldPoint() + { Call<"ZoomCameraAtWorldPoint">(); } + + float Distance() + { return Get<"Distance", float>(); } + void SetDistance(float value) + { Set<"Distance">(value); } + float NormalizedZoom() + { return Get<"NormalizedZoom", float>(); } + + NodeDepth depth() + { return Field<"_depth", NodeDepth>(); } + void SetDepth(NodeDepth value) + { SetField<"_depth">(value); } + ZoomLevels zoomLevel() + { return Field<"_zoomLevel", ZoomLevels>(); } + Camera* sceneCamera() + { return Field<"_sceneCamera", Camera*>(); } + + float storedZoomDistanceSystem() + { return Field<"_storedZoomDistanceSystem", float>(); } + void SetStoredZoomDistanceSystem(float value) + { SetField<"_storedZoomDistanceSystem">(value); } + float systemDefaultZoomRatio() + { return Field<"_systemDefaultZoomRatio", float>(); } + void SetSystemDefaultZoomRatio(float value) + { SetField<"_systemDefaultZoomRatio">(value); } + float actualDistance() + { return Field<"_actualDistance", float>(); } + void SetActualDistance(float value) + { SetField<"_actualDistance">(value); } + float zoomtotal() + { return Field<"_zoomtotal", float>(); } + void SetZoomtotal(float value) + { SetField<"_zoomtotal">(value); } + float minimum() + { return Field<"_minimum", float>(); } + void SetMinimum(float value) + { SetField<"_minimum">(value); } + float maximum() + { return Field<"_maximum", float>(); } + void SetMaximum(float value) + { SetField<"_maximum">(value); } + float zoomDelta() + { return Field<"_zoomDelta", float>(); } + void SetZoomDelta(float value) + { SetField<"_zoomDelta">(value); } + float viewRadius() + { return Field<"_viewRadius", float>(); } + void SetViewRadius(float value) + { SetField<"_viewRadius">(value); } + float lastZoomDelta() + { return Field<"_lastZoomDelta", float>(); } + void SetLastZoomDelta(float value) + { SetField<"_lastZoomDelta">(value); } + vec2 zoomLocation() + { return Field<"_zoomLocation", vec2>(); } + void SetZoomLocation(vec2 value) + { SetField<"_zoomLocation">(value); } + vec3 worldPoint() + { return Field<"_worldPoint", vec3>(); } + void SetWorldPoint(vec3 value) + { SetField<"_worldPoint">(value); } + float farRatioSystemNormal() + { return Field<"_farRatioSystemNormal", float>(); } + void SetFarRatioSystemNormal(float value) + { SetField<"_farRatioSystemNormal">(value); } + float farRatioSystemExtended() + { return Field<"_farRatioSystemExtended", float>(); } + void SetFarRatioSystemExtended(float value) + { SetField<"_farRatioSystemExtended">(value); } +}; + +struct NavigationFleetWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationFleetWidget">; + + NavigationLOD* lod() + { return Field<"_lod", NavigationLOD*>(); } +}; + +struct NavigationManager : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationManager">; + + void HideInteraction() + { Call<"HideInteraction">(); } +}; + +struct NavigationSectionManager : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationSectionManager">; + + static void ChangeNavigationSection(SectionID sectionId, int64_t systemId = -1) + { Class::Call<"ChangeNavigationSection">(sectionId, systemId); } + + static NavigationSectionManager* Instance() + { return Class::Get<"Instance", NavigationSectionManager*>(); } + + // Not NavigationManager(): that would hide the type inside this struct. + NavigationManager* SNavigationManager() + { return Get<"NavigationManager", NavigationManager*>(); } +}; + +struct NavigationInteractionUIContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationInteractionUIContext">; +}; + +struct SetCourseWidget : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "SetCourseWidget">; + VisibilityController* visibilityController() + { return Field<"_visibilityController", VisibilityController*>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } + SemaphoreButtonListener* joinWaveDefenseButton() + { return Field<"_joinWaveDefenseButton", SemaphoreButtonListener*>(); } +}; + +struct NavigationInteractionUIViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "NavigationInteractionUIViewController">; + + bool TryPressJoinWaveDefense() + { + if (!IsSetCourseVisible()) { + return false; + } + auto* widget = Field<"_setCourseWidget", SetCourseWidget*>(); + auto* listener = widget ? widget->joinWaveDefenseButton() : nullptr; + if (!listener || !listener->isActiveAndEnabled()) { + return false; + } + // Button.Press preserves Unity's active/interactable checks and the native join callback. + if (auto* button = listener->TheButton()) { + button->Press(); + } + return true; + } + + bool IsSetCourseVisible() + { + auto* widget = Field<"_setCourseWidget", SetCourseWidget*>(); + auto* visibility = widget ? widget->visibilityController() : nullptr; + return visibility && (visibility->state() == VisibilityState::Visible || visibility->state() == VisibilityState::Show) + && widget->isActiveAndEnabled(); + } + void OnSetCourseButtonClick() + { Call<"OnSetCourseButtonClick">(); } +}; + +struct OrbitFrameProvider : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.CameraController", "OrbitFrameProvider">; + + // Declared on AbstractOrbitFrameProvider. + float RotationAngleDelta() + { return Field<"_rotationAngleDelta", float>(); } + void SetRotationAngleDelta(float value) + { SetField<"_rotationAngleDelta">(value); } +}; + +struct PopulatedSystemData : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "PopulatedSystemData">; + + void* backdrop() + { return Field<"_backdrop", void*>(); } + int64_t systemId() + { return Field<"_systemId", int64_t>(); } +}; + +struct PlanetViewUtils : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "PlanetViewUtils">; + + // Managed FlatRenderable. Calling this also runs any detour on get_FlatRenderable. + void* GetFlatRenderable() + { return Get<"FlatRenderable", void*>(); } + + PopulatedSystemData* popData() + { return Field<"_popData", PopulatedSystemData*>(); } + Transform* activeSystemParent() + { return Field<"_activeSystemParent", Transform*>(); } +}; + +struct CoordinateSearchContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Bookmarks", "CoordinateSearchContext">; + + void SetDefaultXCoordinate(Il2CppString* value) + { Set<"DefaultXCoordinate">(value); } + void SetDefaultYCoordinate(Il2CppString* value) + { Set<"DefaultYCoordinate">(value); } + void SetDefaultSystemInput(Il2CppString* value) + { Set<"DefaultSystemInput">(value); } +}; + +struct BookmarksManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Bookmarks", "BookmarksManager">; + + void ViewBookmarks() + { Call<"ViewBookmarks">(); } + + void ViewCoordinateSearch() + { + auto* context = il2cpp::New(); + // The binding already reported the missing class or constructor; plain bookmarks are the next best view. + if (!context) { + ViewBookmarks(); + return; + } + + auto* zero = il2cpp_string_new("0"); + context->SetDefaultXCoordinate(zero); + context->SetDefaultYCoordinate(zero); + context->SetDefaultSystemInput(il2cpp_string_new("")); + Hub::get_SectionManager()->TriggerSectionChange(SectionID::Bookmarks_Search_Coordinates, context, false, false, + true); + } +}; + +struct CoursePromptPopupDataContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "CoursePromptPopupDataContext">; + + bool HasInstantWarp() + { return Field<"HasInstantWarp", bool>(); } + CourseData* GetCourseData() + { return Field<"CourseData", CourseData*>(); } +}; + +struct CoursePromptPopupWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Navigation", "CoursePromptPopupWidget">; + + GenericButtonWidget* LeftButton() + { return Field<"_leftButton", GenericButtonWidget*>(); } + GenericButtonWidget* RightButton() + { return Field<"_rightButton", GenericButtonWidget*>(); } + void InitiateRegularWarp() + { Call<"InitiateRegularWarp">(); } + void OnInstantWarpButtonClick() + { Call<"OnInstantWarpButtonClick">(); } +}; + +struct CoursePromptPopupViewController : ViewController { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "CoursePromptPopupViewController">; + + CoursePromptPopupWidget* PopupWidget() + { return Field<"_coursePromptPopupWidget", CoursePromptPopupWidget*>(); } +}; diff --git a/mods/src/prime/EntityGroup.h b/mods/src/prime/server.h similarity index 52% rename from mods/src/prime/EntityGroup.h rename to mods/src/prime/server.h index 8f89f437d..f2f2d7281 100644 --- a/mods/src/prime/EntityGroup.h +++ b/mods/src/prime/server.h @@ -1,5 +1,7 @@ #pragma once +// Server payloads: entity groups, realtime data, battle results, and buff data. + #include #include @@ -272,3 +274,377 @@ struct EntityGroup : il2cpp::Object { ByteString* Group() { return Get<"Group", ByteString*>(); } }; + +struct ServiceResponse : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "ServiceResponse">; + + // Google.Protobuf.Collections.RepeatedField; List resolves Count/get_Item on it. + il2cpp::List* EntityGroups() + { return Get<"EntityGroups", il2cpp::List*>(); } +}; + +// The platform service's responses, Digit.Platform.Models. They pass through the same model registry code as game +// server responses, but their entity groups have their own type enum. +struct PlatformEntityGroup : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.Platform.Models", "EntityGroup">; + + enum Type : int32_t { + StaticTranslations = 0, + DynamicTranslations = 1, + StoreOffers = 2, + OrderVirtualGood = 3, + Json = 4, + TournamentBundles = 5, + EventRanking = 6, + EventsModel = 7, + LeaderboardRanking = 8, + LeaderboardsModel = 9, + EventNotification = 10, + PlayerStats = 11, + GameNews = 12, + FileSignedUrls = 13, + GameInfo = 14, + PlayerProfiles = 15, + PlayerlifecycleNotification = 16, + MinigamesList = 17, + GameNewsVideo = 18, + PromoCodeRedeem = 19, + StoreGroups = 20, + AllianceGameState = 21, + AllianceGameChangeNotification = 22, + ApmFiltersStatus = 23, + ApmFiltersUpdated = 24, + ListReferralConnections = 25, + RewardRedeem = 26, + LoginStreakRecalculate = 27, + OrdersVirtualGood = 28, + }; + + Type Type_() + { return Get<"Type", Type>(); } + ByteString* Group() + { return Get<"Group", ByteString*>(); } +}; + +struct PlatformServiceResponse : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.Platform.Models", "ServiceResponse">; + + il2cpp::List* EntityGroups() + { return Get<"EntityGroups", il2cpp::List*>(); } +}; + +enum DataType : int32_t +{ + Unspecified = -1, + JSON = 0, + MsgPack = 1, + ProtoBuff = 2, + UrlEncoded = 3, +}; + +struct RealtimeDataPayload : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.Networking.RTC", "RealtimeDataPayload">; + + // Managed DataType is a string, not the DataType enum above. + Il2CppString* DataType() + { return Get<"DataType", Il2CppString*>(); } + Il2CppString* ChannelId() + { return Get<"ChannelId", Il2CppString*>(); } + int32_t InstanceIdJson() + { return Get<"InstanceIdJson", int32_t>(); } + Il2CppString* Target() + { return Get<"Target", Il2CppString*>(); } + Il2CppString* Source() + { return Get<"Source", Il2CppString*>(); } + Il2CppString* Data() + { return Get<"Data", Il2CppString*>(); } +}; + +enum class BattleType : int32_t { + Fleet = 0, + Base = 1, + PassiveMarauder = 2, + NpcInstantiated = 3, + DockingPoint = 4, + ActiveMarauder_MarauderInit = 5, + ActiveMarauder_PlayerInit = 6, + ArmadaBase = 7, + ArmadaMarauder = 8, + PveDockingPoint = 9, + ArmadaAsb = 10, + ArmadaMta = 11, + Hazard = 12, + PveCuttingBeam = 13, + PvpCuttingBeam = 14, + PveChainShot = 15, + PvpChainShot = 16 +}; + +enum class BattleResultType : int32_t { + Defeat = 0, + Victory = 1, + PartialVictory = 2 +}; + +enum class FleetDataType : int32_t { + DeployedFleet = 0, + Starbase = 1, + Armada = 2 +}; + +struct BattleResultHeader : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Models", "BattleResultHeader">; + + Il2CppObject* get_PlayerUserProfile() + { return Get<"PlayerUserProfile", Il2CppObject*>(); } + Il2CppObject* get_EnemyUserProfile() + { return Get<"EnemyUserProfile", Il2CppObject*>(); } + Il2CppObject* get_SpecService() + { return Field<"_specService", Il2CppObject*>(); } + int32_t get_BattleType() + { return Get<"BattleType", int32_t>(); } + + int64_t PlayerShipHullId() + { return Get<"PlayerShipHullId", int64_t>(); } + int64_t EnemyShipHullId() + { return Get<"EnemyShipHullId", int64_t>(); } +}; + +enum class ClientModifierType : int32_t { + Invalid = -0x7FFF, + FleetOfficerBonusHealth = -17, + FleetOfficerBonusDefense = -16, + FleetOfficerBonusAttack = -15, + FleetStrength = -14, + FleetOfficerRating = -13, + FleetSensorRating = -12, + FleetDeflectorRating = -11, + FleetHealthRating = -10, + FleetDefenseRating = -9, + FleetOffenseRating = -8, + FleetGrade = -7, + ShipShieldDamage = -6, + ShipHullDamage = -5, + FleetMiningBonus = -4, + ShipPlating = -3, + ShipAbsorption = -2, + ShipDamagePerRound = -1, + ModEnergyDamage = 0, + ModKineticDamage = 1, + ModAllDamage = 2, + ModShotsPerAttack = 3, + ModAllLoadSpeed = 4, + ModAllReloadSpeed = 5, + ModAccuracy = 6, + ModArmorPiercing = 7, + ModShieldPiercing = 8, + ModCritChance = 9, + ModCritDamage = 10, + ModShipDodge = 11, + ModShipArmor = 12, + ModShields = 13, + ModCptManeuverEffect = 14, + ModOffAbilityEffect = 15, + ModShieldHpRepair = 16, + ModHullHpRepair = 17, + ModAddState = 18, + ModRemoveState = 19, + ModParsteelProtection = 20, + ModTritaniumProtection = 21, + ModDilithiumProtection = 22, + ModCombatParsteelReward = 23, + ModCombatTritaniumReward = 24, + ModCombatDilithiumReward = 25, + ModCombatIntelReward = 26, + ModCombatLootDropChance = 27, + ModCombatLootDropQuantity = 28, + ModCombatXpReward = 29, + ModMissionsParsteelReward = 30, + ModMissionsTritaniumReward = 31, + ModMissionsDilithiumReward = 32, + ModMissionsIntelReward = 33, + ModMissionsLootDropChance = 34, + ModMissionsLootDropQuantity = 35, + ModMissionsXpReward = 36, + ModFactionPointsFederationReward = 37, + ModFactionPointsKlingonReward = 38, + ModFactionPointsRomulanReward = 39, + ModFactionPointsAllReward = 40, + ModMiningRateParsteel = 41, + ModMiningRateTritanium = 42, + ModMiningRateDilithium = 43, + ModMiningRateOre = 44, + ModMiningRateHydrocarbon = 45, + ModMiningRewardParsteel = 46, + ModMiningRewardTritanium = 47, + ModMiningRewardDilithium = 48, + ModMiningRewardOre = 49, + ModMiningRewardHydrocarbon = 50, + ModRepairCostsParsteel = 51, + ModRepairCostsTritanium = 52, + ModRepairCostsDilithium = 53, + ModRepairCostsAll = 54, + ModRepairTime = 55, + ModOfficerStatAttack = 56, + ModOfficerStatDefense = 57, + ModOfficerStatHealth = 58, + ModOfficerStatAll = 59, + ModShieldHpMax = 60, + ModHullHpMax = 61, + ModImpulseSpeed = 62, + ModWarpSpeed = 63, + ModWarpDistance = 64, + ModEnterWarpDelay = 65, + ModCargoCapacity = 66, + ModCargoProtection = 67, + ModAutoRecall = 68, + ModEnergyLoadSpeed = 69, + ModKineticLoadSpeed = 70, + ModEnergyReloadSpeed = 71, + ModKineticReloadSpeed = 72, + ModAllDefenses = 73, + ModAllPiercing = 74, + ModShieldRegenTime = 75, + ModShieldMitigation = 76, + ModResourceProtection = 100, + ModCombatResourceReward = 101, + ModMissionResourceReward = 102, + ModFactionPointsRewards = 103, + ModFactionPointsLosses = 104, + ModMiningRate = 105, + ModMiningReward = 106, + ModRepairCosts = 107, + ModStarbaseModuleConstructionSpeed = 108, + ModStarbaseModuleConstructionCost = 109, + ModResearchSpeed = 110, + ModResearchCost = 111, + ModResourceGeneration = 112, + ModResourceStorage = 113, + ModForcefieldRechargeSpeed = 114, + ModForcefieldHp = 115, + ModComponentCost = 116, + ModShipConstructionSpeed = 117, + ModShipConstructionCost = 118, + ModTierUpSpeed = 119, + ModFactionPointsPenaltyRecution = 120, + ModFactionStoreLootBonus = 121, + ModOfficerLevelUpCost = 122, + ModOfficerPromoteCost = 123, + ModScanShip = 124, + ModScanStation = 125, + ModScanSystem = 126, + ModIncomingStationAttackInformation = 127, + ModIncomingStationAttackAdvancedWarning = 128, + ModShipScrapResourceBonus = 129, + ModShipScrapLootBonus = 130, + ModShipScrapSpeed = 131, + ModAwayMissionSpeed = 132, + ModAwayMissionResourceRewards = 133, + ModAwayMissionLootBonus = 134, + ModResourceProducerResourceStorage = 135, + ModWarehouseResourceStorage = 136, + ModVaultResourceStorage = 137, + ModHullId = 138, + ModShipInventorySize = 139, + ModScanCost = 140, + ModTradeTax = 141, + ModRepairStation = 142, + ModArmadaSize = 143, + ModJumpAndTowCostEff = 147, + ModSkillCloakingCooldown = 150, + ModSkillCloakingDuration = 151, + ModSkillCloakingCost = 152, + ModSkillCloakingSneakChance = 153 +}; + +struct IBuffData : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit", "IBuffData">; + + ClientModifierType ModifierType() + { return Get<"ModifierType", ClientModifierType>(); } +}; + +// Interface binding; the getters dispatch to the receiver's implementation. +struct IBuffComparer : il2cpp::Object { + using Class = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Services", "IBuffComparer">; + + int64_t get_HullId() + { return Get<"HullID", int64_t>(); } + int64_t get_FactionId() + { return Get<"FactionID", int64_t>(); } +}; + +// Digit.PrimeServer.Models.BuffCondition; only the values the mod or curious readers care about are named. +enum class BuffCondition : int32_t { + Invalid = -1, + SelfExplorer = 0, + SelfInterceptor = 1, + SelfBattleship = 2, + SelfSurveyor = 3, + EnemyExplorer = 4, + EnemyInterceptor = 5, + EnemyBattleship = 6, + EnemySentinel = 8, + CondCargoFull = 9, + CondCargoEmpty = 10, + CondSelfMining = 12, + SelfOrSentinelAttacked = 14, + // TODO(alexander): Find a better name + EnemyStronger = 16, + CondEnemyHullFaction = 17, + CondEnemyHostile = 27, + CondEnemyPlayer = 28, + CondSelfHasMorale = 33, + CondSelfHasHullBreach = 34, + CondSelfIsBurning = 35, + CondEnemyHullBreach = 37, + CondEnemyBurning = 38, + CondModuleEnergy = 39, + CondModuleKinetic = 40, + CondHitEnemyWithEnergy = 41, + CondHitEnemyWithKinetic = 42, + CondHullHealthBelow = 43, + CondHullHealthBelowStartOfCombat = 44, + // I wonder what could be here...maybe some server only things? Who knows + CondSelfShipGrade = 102, + CondSelfHullFaction = 113, + CondSelfAtStation = 114, + SelfSentinel = 116, + CondEnemySentinel = 117, // TODO(alexander): What wait???? We have the before aswell, why is this a thing again??? see condition 8 + CondResourceId = 118, + CondSelfModuleId = 119, + CondSelfShipgrade1 = 120, + CondSelfShipgrade2 = 121, + CondSelfShipgrade3 = 122, + CondSelfShipgrade4 = 123, + CondSelfIsFederation = 124, // Not sure why they haven't used CondSelfHullFaction for this...but hey whatever + CondSelfIsRomulan = 125, + CondSelfIsKlingon = 126, + CondSelfFranklin = 127, + CondSelfIsArmada = 129, + CondSelfIsNotArmada = 131, + CondTargetShipGrade1 = 132, + CondTargetShipGrade2 = 133, + CondTargetShipGrade3 = 134, + CondTargetShipGrade4 = 135, + CondSelfHullMudd = 150, + CondSelfHullBotanyBay = 151, + CondSelfHullDiscovery = 152, + CondSelfHullSarcophagus = 153, + CondSelfHullVidar = 154, + CondSelfHullDvor = 155, + CondSelfHullFranklin = 156, + CondSelfHullBlackJellyfish = 157, + CondSelfHullFranklin2 = 158, + CondSelfHullEnterprise = 159, + CondSelfHullEnterpriseA = 160, + CondSelfHullD4 = 161, + CondSelfHullHeghta = 162, + CondSelfHullAugur = 163, + CondSelfHullTribune = 164, + CondSelfHullSaladin = 165, + CondSelfHullCenturion = 166, + CondSelfHullBortas = 167, + CondSelfIsSynciate30 = 203, + CondSelfIsMantis = 207, +}; diff --git a/mods/src/prime/ui.h b/mods/src/prime/ui.h new file mode 100644 index 000000000..e0ae795ac --- /dev/null +++ b/mods/src/prime/ui.h @@ -0,0 +1,471 @@ +#pragma once + +// Game UI framework: widgets, view controllers, canvases, buttons, localization, toasts. + +#include "unity.h" + +#include + +#include + +// Unity singletons expose a static Instance on their MonoSingleton`1 base. +template struct MonoSingleton : il2cpp::Object { + static T* Instance() + { return T::Class::template Get<"Instance", T*>(); } + // The singleton s_instance already holds, or null. Instance can search the scene or create a replacement. + static T* Existing() + { return T::Class::template Field<"s_instance", T*>(); } +}; + +// Digit.Client.UI.Widget`1. Members resolve on each widget's runtime class. +template struct Widget : il2cpp::Object { + T* Context() + { return Get<"Context", T*>(); } + bool enabled() + { return Get<"enabled", bool>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } + bool IsActive() + { return Call<"IsActive", bool>(); } +}; + +// Digit.Client.UI.ViewController`1. Members resolve on each controller's runtime class. +template struct ViewController : il2cpp::Object { + T* CanvasContext() + { return Get<"CanvasContext", T*>(); } +}; + +enum class VisibilityState : int32_t // TypeDefIndex: 13743 +{ + Unknown = 0, + Show = 1, + Hide = 2, + Hidden = 3, + Visible = 4 +}; + +struct VisibilityController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "VisibilityController">; + + // bool selects Show(bool)/Hide(bool) over the int entry-point overloads. + void Show(bool instant = false) + { Call<"Show">(instant); } + void Hide(bool instant = false) + { Call<"Hide">(instant); } + + VisibilityState state() + { return Field<"_state", VisibilityState>(); } + VisibilityState State() + { return Get<"VisibilityState", VisibilityState>(); } +}; + +struct CanvasController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "CanvasController">; + + bool ResetFocus() + { return Call<"ResetFocus", bool>(); } + bool Visible() + { return Get<"Visible", bool>(); } + // The bound data context, of the class the canvas's view expects. + il2cpp::Object* Context() + { return Get<"Context", il2cpp::Object*>(); } + bool get_enabled() + { return Get<"enabled", bool>(); } + bool m_Visible() + { return Field<"m_visible", bool>(); } + + Il2CppString* name() + { return Get<"name", Il2CppString*>(); } + Transform* transform() + { return Get<"transform", Transform*>(); } + Canvas* canvas() + { return Field<"m_canvas", Canvas*>(); } +}; + +inline CanvasController* GetCanvasControllerFromComponent(il2cpp::Object* component) +{ + if (!component) + return nullptr; + // Passing System.Type selects GetComponentInParent(Type) over the generic overload. + return component->Call<"GetComponentInParent", CanvasController*>(CanvasController::Class::TypeObject()); +} + +// Digit.Client.UI.TextLocalizer: the label component that renders localized, parameterized text. +struct TextLocalizer : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "TextLocalizer">; + + // Declared on GraphicLocalizer<,>. + Il2CppString* identifier() + { return Field<"m_identifier", Il2CppString*>(); } + // The backing field, not the SignificantDecimals property. + int32_t significantDecimals() + { return Field<"m_significantDecimals", int32_t>(); } + void SetSignificantDecimals(int32_t value) + { SetField<"m_significantDecimals">(value); } + bool textOverride() + { return Field<"_textOverride", bool>(); } + // Declared on GraphicLocalizer<,>: the rendered graphic, resolved lazily even on inactive labels. + il2cpp::Object* Target() + { return Get<"Target", il2cpp::Object*>(); } + + Il2CppString* CurrentText() + { return Call<"CurrentText", Il2CppString*>(); } + void ClearTextOverride() + { Call<"ClearTextOverride">(); } + void OverrideLocalizedText(Il2CppString* text) + { Call<"OverrideLocalizedText">(text); } +}; + +struct ColourTextLocalizer : TextLocalizer { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "ColourTextLocalizer">; +}; + +// Identifier + category pair that the localizer turns into display text. +// Construct with il2cpp::New(identifier, category). +struct LocaleTextContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "LocaleTextContext">; + + // Fills the {N} slots of the identifier itself, e.g. "ship_name_{0}". + void ApplyIdentifierParameters(il2cpp::Array* parameters) + { Call<"ApplyIdentifierParameters">(parameters); } +}; + +struct LocaleUtilities { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Localization", "LocaleUtilities">; + + static Il2CppString* Localize(LocaleTextContext* context, bool localizeTextParams = false, + bool invariantCulture = false) + { return Class::Call<"Localize", Il2CppString*>(context, localizeTextParams, invariantCulture); } +}; + +struct Localizer { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Localization", "Localizer">; + + // textCase is TextLocalizer.TextCase; 0 leaves the text as translated. + static Il2CppString* Localise(LocaleTextContext* context, bool forceCommas = false, int32_t textCase = 0) + { return Class::Call<"Localise", Il2CppString*>(context, forceCommas, textCase); } +}; + +struct LanguageManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.Localization", "LanguageManager">; + + bool TryGetTranslation(Il2CppString* category, int32_t key, Il2CppString** translatedText) + { + if (!translatedText) + return false; + return Call<"TryGetTranslation", bool(Il2CppString*, int32_t, Il2CppString*&)>(category, key, *translatedText); + } + + bool Localize(Il2CppString*& translation, LocaleTextContext* context) + { return Call<"Localize", bool(Il2CppString*&, LocaleTextContext*)>(translation, context); } + + static void ClearCache() + { Class::Call<"ClearCache">(); } +}; + +struct Button : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.UI", "Button">; + + void Press() + { Call<"Press">(); } +}; + +struct SemaphoreListenerBase : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SemaphoreListenerBase">; +}; + +struct SemaphoreButtonListener : SemaphoreListenerBase { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SemaphoreButtonListener">; + + Button* TheButton() + { return Field<"_button", Button*>(); } + bool Interactable() + { return Get<"Interactable", bool>(); } + void SetInteractable(bool value) + { Set<"Interactable">(value); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } +}; + +struct GenericButtonContext : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "GenericButtonContext">; + + bool Interactable() + { return Get<"Interactable", bool>(); } + void SetInteractable(bool value) + { Set<"Interactable">(value); } +}; + +struct GenericButtonWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "GenericButtonWidget">; + + SemaphoreButtonListener* SemaphoreListener() + { return Field<"_semaphoreButtonListener", SemaphoreButtonListener*>(); } + TextLocalizer* mainText() + { return Field<"_mainText", TextLocalizer*>(); } + bool Interactable() + { return Get<"Interactable", bool>(); } +}; + +struct ButtonAndContextWrapper : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "ButtonAndContextWrapper">; + + GenericButtonWidget* Widget() + { return Field<"Widget", GenericButtonWidget*>(); } +}; + +struct InputFieldWidget : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "InputFieldWidget">; + + void Focus() + { Call<"Focus">(); } + + TMP_InputField* input() + { return Field<"_input", TMP_InputField*>(); } + bool isActiveAndEnabled() + { return Get<"isActiveAndEnabled", bool>(); } +}; + +// Digit.Client.UI list hierarchy: BaseListContainer <- SelectableListBase <- SelectableList <- TabBar. +// Data is a System.Collections.IList; elements are whatever the list was bound to. +struct BaseListContainer : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "BaseListContainer">; + + il2cpp::List* data() + { return Field<"_data", il2cpp::List*>(); } + int32_t Count() + { return Get<"Count", int32_t>(); } +}; + +struct SelectableList : BaseListContainer { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "SelectableList">; + + int32_t SelectedIndex() + { return Get<"SelectedIndex", int32_t>(); } + + // Declared on SelectableListBase. The int overload is picked over RequestSelect(Component, bool). + void RequestSelect(int32_t index, bool simulated = false) + { Call<"RequestSelect">(index, simulated); } + + il2cpp::Object* DataItem(int32_t index) + { + auto* list = Get<"Data", il2cpp::List*>(); + return list ? (*list)[index] : nullptr; + } +}; + +struct TabBar : SelectableList { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "TabBar">; + + // GenericButtonContext[] + il2cpp::Array* defaultOptions() + { return Field<"_defaultOptions", il2cpp::Array*>(); } +}; + +struct TabBarViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "TabBarViewController">; + + TabBar* tabBar() + { return Field<"_tabBar", TabBar*>(); } +}; + +struct ShortcutsManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.GameInput", "ShortcutsManager">; + + bool ShowKeybindings() + { return Get<"ShowKeybindings", bool>(); } + void SetShowKeybindings(bool value) + { Set<"ShowKeybindings">(value); } + // Private static gate the native shortcut actions check (tutorial, modals, text input). + static bool CanUseShortcuts() + { return Class::Get<"CanUseShortcuts", bool>(); } +}; + +struct UIFrameManager : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "UIFrameManager">; + + // The game clamps the width to the available space and the side frame's minimum. False when the call failed. + bool ResizeSideFrame(float width) + { return TryCall<"ResizeSideFrame", void(float)>(width); } +}; + +struct ScreenManager : MonoSingleton { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Client.UI", "ScreenManager">; + + static CanvasController* GetTopCanvas(bool visibleOnly = false) + { return Class::Call<"GetTopCanvas", CanvasController*>(visibleOnly); } + + // Every registered canvas, visible or not. + il2cpp::List* screens() + { return Field<"_screens", il2cpp::List*>(); } + CanvasScaler* canvasRootScaler() + { return Field<"m_canvasRootScaler", CanvasScaler*>(); } + float minimumCanvasScaleFactor() + { return Field<"_minimumCanvasScaleFactor", float>(); } + float maximumCanvasScaleFactor() + { return Field<"_maximumCanvasScaleFactor", float>(); } + UIFrameManager* frameManager() + { return Field<"_frameManager", UIFrameManager*>(); } +}; + +enum ToastState { + All = -1, + Standard = 0, + FactionWarning = 1, + FactionLevelUp = 2, + FactionLevelDown = 3, + FactionDiscovered = 4, + IncomingAttack = 5, + IncomingAttackFaction = 6, + FleetBattle = 7, + StationBattle = 8, + StationVictory = 9, + Victory = 10, + Defeat = 11, + StationDefeat = 12, + Tournament = 14, + ArmadaCreated = 15, + ArmadaCanceled = 16, + ArmadaIncomingAttack = 17, + ArmadaBattleWon = 18, + ArmadaBattleLost = 19, + DiplomacyUpdated = 20, + JoinedTakeover = 21, + CompetitorJoinedTakeover = 22, + AbandonedTerritory = 23, + TakeoverVictory = 24, + TakeoverDefeat = 25, + TreasuryProgress = 26, + TreasuryFull = 27, + Achievement = 28, + AssaultVictory = 29, + AssaultDefeat = 30, + ChallengeComplete = 31, + ChallengeFailed = 32, + StrikeHit = 33, + StrikeDefeat = 34, + WarchestProgress = 35, + WarchestFull = 36, + PartialVictory = 37, + ArenaTimeLeft = 38, + ChainedEventScored = 39, + FleetPresetApplied = 40, + SurgeWarmUpEnded = 41, + SurgeHostileGroupDefeated = 42, + SurgeTimeLeft = 43, + QueueForLeaseActivated = 44, + QueueForLeaseExpired = 45, + PermanentQueuePurchased = 46, + OutpostStartedOrEnded = 47, + CrossAllianceArmadaVictory = 48, + CrossAllianceArmadaDefeat = 49, + CrossAllianceArmadaPartialVictory = 50, + FactionWeeklyEventsProgress = 51, + FactionWeeklyEventsComplete = 52, + ArmadaPlayerBlocked = 53, + ArmadaPlayerUnblocked = 54, + DynamicCrisisUpdate = 55, + DynamicCrisisFailed = 56, + DynamicCrisisCompleted = 57, + GalacticAnomalySystemEntered = 58, +}; + +struct Toast : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.HUD", "Toast">; + + il2cpp::Array* get_TextParameters() + { return Get<"TextParameters", il2cpp::Array*>(); } + // LocaleTextContext + void* get_TextLocaleTextContext() + { return Get<"TextLocaleTextContext", void*>(); } + Il2CppObject* get_Data() + { return Get<"Data", Il2CppObject*>(); } + int get_State() + { return Get<"State", ToastState>(); } +}; + +struct InterstitialViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Interstitial", "InterstitialViewController">; + + void CloseWhenReady() + { Call<"CloseWhenReady">(); } +}; + +struct AnimatedRewardsScreenViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Missions.UI", "AnimatedRewardsScreenViewController">; + + void GoBackToLastSection() + { Call<"GoBackToLastSection">(); } + void OnCollectClicked() + { Call<"OnCollectClicked">(); } + bool IsActive() + { return Call<"IsActive", bool>(); } +}; + +struct ElementSelectorViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.UI", "ElementSelectorViewController">; + + Button* decrementButton() + { return Field<"_decrementButton", Button*>(); } + Button* incrementButton() + { return Field<"_incrementButton", Button*>(); } + + // Unity's internal call, so a missing binding never hides the selector hotkeys. + bool isActiveAndEnabled() + { + static auto get_isActiveAndEnabled = + il2cpp::ResolveICall("UnityEngine.Behaviour::get_isActiveAndEnabled()"); + if (get_isActiveAndEnabled) { + return get_isActiveAndEnabled(this); + } + return true; + } + + void Decrement() + { Call<"Decrement">(); } + void Increment() + { Call<"Increment">(); } + + void PressDecrement() + { + if (auto btn = decrementButton()) { + btn->Press(); + } + } + + void PressIncrement() + { + if (auto btn = incrementButton()) { + btn->Press(); + } + } +}; + +struct ArtifactHallDetailsViewController : il2cpp::Object { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ArtifactHall", "ArtifactHallDetailsViewController">; + + // Treat the controller as active if the icall is unavailable, so the arrow hotkeys still try. + bool isActiveAndEnabled() + { + static auto get_isActiveAndEnabled = il2cpp::ResolveICall( + "UnityEngine.Behaviour::get_isActiveAndEnabled()"); + return get_isActiveAndEnabled ? get_isActiveAndEnabled(this) : true; + } + + void PressLeftArrow() + { Press(leftArrowButton()); } + void PressRightArrow() + { Press(rightArrowButton()); } + + SemaphoreButtonListener* leftArrowButton() + { return Field<"_leftArrowButton", SemaphoreButtonListener*>(); } + SemaphoreButtonListener* rightArrowButton() + { return Field<"_rightArrowButton", SemaphoreButtonListener*>(); } + +private: + static void Press(SemaphoreButtonListener* listener) + { + if (auto* button = listener ? listener->TheButton() : nullptr) + button->Press(); + } +}; diff --git a/mods/src/prime/unity.h b/mods/src/prime/unity.h new file mode 100644 index 000000000..bb0e1583a --- /dev/null +++ b/mods/src/prime/unity.h @@ -0,0 +1,267 @@ +#pragma once + +// Unity engine types the bindings share: math structs, scene objects, input. + +#include "str_utils.h" +#include + +#include +#include +#include + +struct Vector2 { + float x, y; +}; + +#pragma pack(push, 1) +struct vec2 { + float x, y; + + friend auto operator<=>(const vec2&, const vec2&) = default; +}; +#pragma pack(pop) + +struct Vector3 { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Vector3">; + + float x; + float y; + float z; + + static Vector3 zero() + { return Class::Field<"zeroVector", Vector3>(); } +}; + +#pragma pack(push, 1) +struct color { + float r, g, b, a; +}; +#pragma pack(pop) + +struct GameObject : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "GameObject">; + + std::string Name() + { + auto* name = Get<"name", Il2CppString*>(); + return name ? to_string(name) : std::string{}; + } + bool activeInHierarchy() + { return Get<"activeInHierarchy", bool>(); } + void SetActive(bool active) + { Call<"SetActive">(active); } + + // Passing System.Type selects the (Type) overloads over the string and generic ones. A null + // Type would throw in managed code, so a missing class yields null instead. + template T* GetComponent() + { + auto* type = T::Class::TypeObject(); + return type ? Call<"GetComponent", T*>(type) : nullptr; + } + template T* AddComponent() + { + auto* type = T::Class::TypeObject(); + return type ? Call<"AddComponent", T*>(type) : nullptr; + } +}; + +struct Transform : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Transform">; + + int32_t childCount() + { return Get<"childCount", int32_t>(); } + Transform* GetChild(int32_t index) + { return Call<"GetChild", Transform*>(index); } + Transform* parent() + { return Get<"parent", Transform*>(); } + GameObject* gameObject() + { return Get<"gameObject", GameObject*>(); } + Vector3 localScale() + { return Get<"localScale", Vector3>(); } + void SetLocalScale(Vector3 value) + { Set<"localScale">(value); } + void SetLocalEulerAngles(Vector3 value) + { Set<"localEulerAngles">(value); } + void SetParent(Transform* parent, bool worldPositionStays) + { Call<"SetParent">(parent, worldPositionStays); } + void SetAsFirstSibling() + { Call<"SetAsFirstSibling">(); } + // Finds a descendant by slash-separated path, e.g. "Hint/Background". + Transform* Find(Il2CppString* path) + { return Call<"Find", Transform*>(path); } +}; + +struct RectTransform : Transform { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "RectTransform">; + + Vector2 anchorMin() + { return Get<"anchorMin", Vector2>(); } + Vector2 anchorMax() + { return Get<"anchorMax", Vector2>(); } + Vector2 offsetMin() + { return Get<"offsetMin", Vector2>(); } + Vector2 offsetMax() + { return Get<"offsetMax", Vector2>(); } + void SetOffsetMin(Vector2 value) + { Set<"offsetMin">(value); } + void SetOffsetMax(Vector2 value) + { Set<"offsetMax">(value); } + void SetAnchorMin(Vector2 value) + { Set<"anchorMin">(value); } + void SetAnchorMax(Vector2 value) + { Set<"anchorMax">(value); } + void SetPivot(Vector2 value) + { Set<"pivot">(value); } + void SetSizeDelta(Vector2 value) + { Set<"sizeDelta">(value); } + void SetAnchoredPosition(Vector2 value) + { Set<"anchoredPosition">(value); } +}; + +// UnityEngine.Component, the base of every MonoBehaviour. Use As() on other bindings. +struct Component : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Component">; + + Transform* transform() + { return Get<"transform", Transform*>(); } + GameObject* gameObject() + { return Get<"gameObject", GameObject*>(); } +}; + +struct Camera : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Camera">; + + float farClipPlane() + { return Get<"farClipPlane", float>(); } + void SetFarClipPlane(float value) + { Set<"farClipPlane">(value); } + float nearClipPlane() + { return Get<"nearClipPlane", float>(); } + void SetNearClipPlane(float value) + { Set<"nearClipPlane">(value); } + // UnityEngine.CameraClearFlags + int32_t clearFlags() + { return Get<"clearFlags", int32_t>(); } + void SetClearFlags(int32_t value) + { Set<"clearFlags">(value); } + color backgroundColor() + { return Get<"backgroundColor", color>(); } + void SetBackgroundColor(color value) + { Set<"backgroundColor">(value); } +}; + +struct Canvas : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.UIModule", "UnityEngine", "Canvas">; + + float scaleFactor() + { return Get<"scaleFactor", float>(); } + void SetScaleFactor(float value) + { Set<"scaleFactor">(value); } +}; + +struct CanvasScaler : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.UI", "CanvasScaler">; + + // Writes Canvas.scaleFactor directly. The scaleFactor property is only applied on the scaler's next Update. + void SetScaleFactor(float value) + { Call<"SetScaleFactor">(value); } + + float scaleFactor() + { return Get<"scaleFactor", float>(); } + // Named after the managed setter because SetScaleFactor is the managed method above. + void set_scaleFactor(float value) + { Set<"scaleFactor">(value); } + vec2 referenceResolution() + { return Get<"referenceResolution", vec2>(); } + void SetReferenceResolution(vec2 value) + { Set<"referenceResolution">(value); } +}; + +struct Sprite : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Sprite">; + + float pixelsPerUnit() + { return Get<"pixelsPerUnit", float>(); } +}; + +// UnityEngine.UI.Image; color and raycastTarget come from Graphic. +struct Image : Component { + using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.UI", "Image">; + + enum class Type : int32_t { + Simple = 0, + Sliced = 1, + Tiled = 2, + Filled = 3, + }; + + Sprite* sprite() + { return Get<"sprite", Sprite*>(); } + void SetSprite(Sprite* value) + { Set<"sprite">(value); } + Sprite* overrideSprite() + { return Get<"overrideSprite", Sprite*>(); } + void SetOverrideSprite(Sprite* value) + { Set<"overrideSprite">(value); } + void SetType(Type value) + { Set<"type">(value); } + void SetPreserveAspect(bool value) + { Set<"preserveAspect">(value); } + void SetPixelsPerUnitMultiplier(float value) + { Set<"pixelsPerUnitMultiplier">(value); } + void SetColor(color value) + { Set<"color">(value); } + void SetRaycastTarget(bool value) + { Set<"raycastTarget">(value); } + void SetVerticesDirty() + { Call<"SetVerticesDirty">(); } +}; + +struct EventSystem : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.EventSystems", "EventSystem">; + + static EventSystem* current() + { return Class::Get<"current", EventSystem*>(); } + + GameObject* currentSelectedGameObject() + { return Get<"currentSelectedGameObject", GameObject*>(); } + void SetSelectedGameObject(GameObject* object) + { Call<"SetSelectedGameObject">(object); } +}; + +struct TMP_InputField : il2cpp::Object { + using Class = il2cpp::Class<"Unity.TextMeshPro", "TMPro", "TMP_InputField">; + + bool isFocused() + { return Get<"isFocused", bool>(); } + + void ActivateInputField() + { Call<"ActivateInputField">(); } + void DeactivateInputField(bool clearSelection = false) + { Call<"DeactivateInputField">(clearSelection); } + void SendOnFocusLost() + { Call<"SendOnFocusLost">(); } + void SendOnFocus() + { Call<"SendOnFocus">(); } +}; + +enum class TouchPhase : int32_t { + Began = 0, + Moved = 1, + Stationary = 2, + Ended = 3, + Canceled = 4, +}; + +struct TKTouch : il2cpp::Object { + using Class = il2cpp::Class<"TouchKit", "", "TKTouch">; + + TouchPhase phase() + { return Field<"phase", TouchPhase>(); } + void SetPhase(TouchPhase phase) + { SetField<"phase">(phase); } +}; + +struct UIBehaviour : il2cpp::Object { + using Class = il2cpp::Class<"UnityEngine.UI", "UnityEngine.EventSystems", "UIBehaviour">; +}; diff --git a/mods/src/prime/viewers.h b/mods/src/prime/viewers.h new file mode 100644 index 000000000..91e56008f --- /dev/null +++ b/mods/src/prime/viewers.h @@ -0,0 +1,187 @@ +#pragma once + +// Object viewer cards shown for targets in space, and their scan/engage buttons. + +#include "fleet.h" +#include "navigation.h" +#include "ui.h" + +#include + +#include + +struct ParentObjectViewerViewController : ViewController { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "ParentObjectViewerViewController">; + + void ActivateViewerHUDState(bool isHUDOpen = false) + { Call<"ActivateViewerHUDState">(isHUDOpen); } + void TriggerInstantHide() + { Call<"TriggerInstantHide">(); } + void TriggerShow() + { Call<"TriggerShow">(); } + + VisibilityController* visibilityController() + { return Field<"_visibilityController", VisibilityController*>(); } + bool IsShowing() + { return Get<"IsShowing", bool>(); } +}; + +struct ObjectViewerBaseWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "ObjectViewerBaseWidget">; + + void HideAllViewers() + { Call<"HideAllViewers">(); } + + VisibilityController* visibilityController() + { return Field<"_visibilityController", VisibilityController*>(); } + ParentObjectViewerViewController* Parent() + { return Get<"Parent", ParentObjectViewerViewController*>(); } + bool IsInfoShown() + { return Get<"IsInfoShown", bool>(); } + void SetIsInfoShown(bool value) + { Set<"IsInfoShown">(value); } +}; + +struct RewardsButtonWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "RewardsButtonWidget">; + + void RewardsClicked() + { Call<"RewardsClicked">(); } + + VisibilityController* rewardsController() + { return Field<"_rewardsController", VisibilityController*>(); } +}; + +struct ScanEngageButtonsWidget : Widget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "ScanEngageButtonsWidget">; + + void OnArmadaButtonClicked() + { Call<"OnArmadaButtonClicked">(); } + void OnEngageButtonClicked() + { Call<"OnEngageButtonClicked">(); } + void OnScanButtonClicked() + { Call<"OnScanButtonClicked">(); } + + GenericButtonWidget* armadaButton() + { return Field<"_armadaButton", GenericButtonWidget*>(); } +}; + +struct PreScanTargetWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.Combat", "PreScanTargetWidget">; + + void ValidateThenCreateArmada() + { Call<"ValidateThenCreateArmada">(); } + void OnAddToQueueClickedEventHandler() + { Call<"OnAddToQueueClickedEventHandler">(); } + // Declared on ObjectViewerBaseWidget. + void OnInfoClick() + { Call<"OnInfoClick">(); } + + BattleTargetData* battleTargetData() + { return Field<"_battleTargetData", BattleTargetData*>(); } + ScanEngageButtonsWidget* scanEngageButtonsWidget() + { return Field<"_scanEngageButtonsWidget", ScanEngageButtonsWidget*>(); } + RewardsButtonWidget* rewardsButtonWidget() + { return Field<"_rewardsButtonWidget", RewardsButtonWidget*>(); } + GenericButtonWidget* addToQueueButtonWidget() + { return Field<"_addToQueueButtonWidget", GenericButtonWidget*>(); } + GenericButtonWidget* armadaAttackButton() + { return Field<"_armadaAttackButton", GenericButtonWidget*>(); } +}; + +struct AllianceStarbaseObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "AllianceStarbaseObjectViewerWidget">; +}; + +struct ArmadaObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "ArmadaObjectViewerWidget">; + + void SetCourseToArmada() + { Call<"SetCourseToArmada">(); } + void ValidateThenJoinArmada() + { Call<"ValidateThenJoinArmada">(); } + void JoinArmada() + { Call<"JoinArmada">(); } + bool IsAllowedToJoinArmada(FleetPlayerData* fleet) + { return Call<"IsAllowedToJoinArmada", bool>(fleet); } + + GenericButtonContext* joinContext() + { return Field<"_joinContext", GenericButtonContext*>(); } +}; + +struct CelestialObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "CelestialObjectViewerWidget">; +}; + +struct EmbassyObjectViewer : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "EmbassyObjectViewer">; +}; + +struct HousingObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "HousingObjectViewerWidget">; +}; + +// MiningObjectViewerWidget.OccupiedState, a private nested enum. +enum class OccupiedState : int32_t { + NotOccupied = 0, + LocalPlayerOccupied = 1, + OtherPlayerOccupied = 2 +}; + +struct MiningObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "MiningObjectViewerWidget">; + + void MineClicked() + { Call<"MineClicked">(); } + + OccupiedState occupiedState() + { return Field<"_occupiedState", OccupiedState>(); } + ScanEngageButtonsWidget* scanEngageButtonsWidget() + { return Field<"_scanEngageButtonsWidget", ScanEngageButtonsWidget*>(); } +}; + +struct MissionsObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "MissionsObjectViewerWidget">; +}; + +struct WaveDefenseObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "WaveDefenseObjectViewerWidget">; + + // Every active viewer of this class in the loaded scenes. + static il2cpp::Array* FindActiveViewers() + { + using UnityObject = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Object">; + auto* type = Class::TypeObject(); + return type ? UnityObject::Call<"FindObjectsOfType", il2cpp::Array*>(type) + : nullptr; + } + + GenericButtonWidget* primaryButton() + { return Field<"_primaryButton", GenericButtonWidget*>(); } + + void PressPrimaryButton() + { + auto* widget = primaryButton(); + if (!widget || !widget->isActiveAndEnabled()) { + return; + } + auto* context = widget->Context(); + auto* listener = widget->SemaphoreListener(); + if (context && context->Interactable() && listener) { + if (auto* button = listener->TheButton()) { + button->Press(); + } + } + } +}; + +struct StarNodeObjectViewerWidget : ObjectViewerBaseWidget { + using Class = il2cpp::Class<"Assembly-CSharp", "Digit.Prime.ObjectViewer", "StarNodeObjectViewerWidget">; + + void OnViewButtonActivation() + { Call<"OnViewButtonActivation">(); } + void InitiateWarp() + { Call<"InitiateWarp">(); } + GenericButtonWidget* visitButton() + { return Field<"_visitButton", GenericButtonWidget*>(); } +}; diff --git a/mods/src/prime_types.h b/mods/src/prime_types.h deleted file mode 100644 index b2c790be1..000000000 --- a/mods/src/prime_types.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include - -enum Faction -{ - None = -1, - Undefined = 0, - Federation = 1, - Klingon = 2, - Romulan = 3, -}; - -struct HullSpec; - -class SpecManager; -using GetHull_t = HullSpec *(*)(SpecManager *_this, uint64_t, void *); - -#pragma pack(push, 1) -struct SpecManagerVtbl -{ - char pad0[0x1C8]; - GetHull_t GetHull; - void *TypeDefinition; -}; - -// static_assert(offsetof(SpecManagerVtbl, GetHull) == 0x1C8); - -class SpecManager -{ -public: - SpecManagerVtbl vtbl; -}; -// static_assert(offsetof(SpecManager, vtbl) == 0x0); -#pragma pack(pop) - - -enum BuffCondition -{ - Invalid = -1, - SelfExplorer = 0, - SelfInterceptor = 1, - SelfBattleship = 2, - SelfSurveyor = 3, - EnemyExplorer = 4, - EnemyInterceptor = 5, - EnemyBattleship = 6, - EnemySentinel = 8, - CondCargoFull = 9, - CondCargoEmpty = 10, - CondSelfMining = 12, - SelfOrSentinelAttacked = 14, - // TODO(alexander): Find a better name - EnemyStronger = 16, - CondEnemyHullFaction = 17, - CondEnemyHostile = 27, - CondEnemyPlayer = 28, - CondSelfHasMorale = 33, - CondSelfHasHullBreach = 34, - CondSelfIsBurning = 35, - CondEnemyHullBreach = 37, - CondEnemyBurning = 38, - CondModuleEnergy = 39, - CondModuleKinetic = 40, - CondHitEnemyWithEnergy = 41, - CondHitEnemyWithKinetic = 42, - CondHullHealthBelow = 43, - CondHullHealthBelowStartOfCombat = 44, - // I wonder what could be here...maybe some server only things? Who knows - CondSelfShipGrade = 102, - CondSelfHullFaction = 113, - CondSelfAtStation = 114, - SelfSentinel = 116, - CondEnemySentinel = 117, // TODO(alexander): What wait???? We have the before aswell, why is this a thing again??? see condition 8 - CondResourceId = 118, - CondSelfModuleId = 119, - CondSelfShipgrade1 = 120, - CondSelfShipgrade2 = 121, - CondSelfShipgrade3 = 122, - CondSelfShipgrade4 = 123, - CondSelfIsFederation = 124, // Not sure why they haven't used CondSelfHullFaction for this...but hey whatever - CondSelfIsRomulan = 125, - CondSelfIsKlingon = 126, - CondSelfFranklin = 127, - CondSelfIsArmada = 129, - CondSelfIsNotArmada = 131, - CondTargetShipGrade1 = 132, - CondTargetShipGrade2 = 133, - CondTargetShipGrade3 = 134, - CondTargetShipGrade4 = 135, - CondSelfHullMudd = 150, - CondSelfHullBotanyBay = 151, - CondSelfHullDiscovery = 152, - CondSelfHullSarcophagus = 153, - CondSelfHullVidar = 154, - CondSelfHullDvor = 155, - CondSelfHullFranklin = 156, - CondSelfHullBlackJellyfish = 157, - CondSelfHullFranklin2 = 158, - CondSelfHullEnterprise = 159, - CondSelfHullEnterpriseA = 160, - CondSelfHullD4 = 161, - CondSelfHullHeghta = 162, - CondSelfHullAugur = 163, - CondSelfHullTribune = 164, - CondSelfHullSaladin = 165, - CondSelfHullCenturion = 166, - CondSelfHullBortas = 167, - CondSelfIsSynciate30 = 203, - CondSelfIsMantis = 207, -}; \ No newline at end of file diff --git a/mods/src/ship_name_match.h b/mods/src/ship_name_match.h index c02ca65a9..a0dc6312c 100644 --- a/mods/src/ship_name_match.h +++ b/mods/src/ship_name_match.h @@ -4,9 +4,8 @@ #include -#include -#include -#include +#include +#include #include #include diff --git a/mods/src/str_utils.h b/mods/src/str_utils.h index 774e29d86..44d6fccbc 100644 --- a/mods/src/str_utils.h +++ b/mods/src/str_utils.h @@ -1,160 +1,87 @@ #pragma once -#include -#include +// ASCII string helpers for config parsing and UTF-16 conversion of managed strings. + +#include +#include + +#include +#include #include +#include #include +#include +#include #include #include #include -#include -#include +constexpr bool IsAsciiSpace(char c) +{ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; } -#if _WIN32 -#include -#else -#include -#endif - -inline bool ascii_isspace(unsigned char c) -{ - return std::isspace(c); -} - -constexpr std::string_view StripTrailingAsciiWhitespace(const std::string_view str) +constexpr std::string_view StripTrailingAsciiWhitespace(std::string_view str) { - const auto it = std::find_if_not(str.rbegin(), str.rend(), ascii_isspace); - return str.substr(0, static_cast(str.rend() - it)); -} - -constexpr std::string_view StripLeadingAsciiWhitespace(const std::string_view str) -{ - const auto it = std::ranges::find_if_not(str, ascii_isspace); - return str.substr(static_cast(it - str.begin())); -} - -constexpr std::string_view StripAsciiWhitespace(const std::string_view str) -{ - return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str)); -} - -constexpr std::string_view StripSuffix(std::string_view str, const std::string_view suffix) -{ - if (str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix) { - return str.substr(0, str.size() - suffix.size()); - } + while (!str.empty() && IsAsciiSpace(str.back())) + str.remove_suffix(1); return str; } -constexpr std::string_view StripPrefix(std::string_view str, const std::string_view prefix) +constexpr std::string_view StripLeadingAsciiWhitespace(std::string_view str) { - if (str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix) { - return str.substr(prefix.size()); - } + while (!str.empty() && IsAsciiSpace(str.front())) + str.remove_prefix(1); return str; } -constexpr std::string AsciiStrToUpper(const std::string_view s) +constexpr std::string_view StripAsciiWhitespace(std::string_view str) +{ return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str)); } + +constexpr std::string_view StripPrefix(std::string_view str, std::string_view prefix) +{ return str.starts_with(prefix) ? str.substr(prefix.size()) : str; } + +constexpr std::string AsciiStrToUpper(std::string_view s) { - std::string str = s.data(); - std::ranges::transform(str, str.begin(), ::toupper); + std::string str(s); + for (auto& c : str) + if (c >= 'a' && c <= 'z') + c = static_cast(c - 'a' + 'A'); return str; } -constexpr std::string AsciiStrToLower(const std::string_view s) +constexpr std::string AsciiStrToLower(std::string_view s) { - std::string str = s.data(); - std::ranges::transform(str, str.begin(), ::tolower); + std::string str(s); + for (auto& c : str) + if (c >= 'A' && c <= 'Z') + c = static_cast(c - 'A' + 'a'); return str; } -constexpr std::vector StrSplit(const std::string& input, const char delimiter) +// Splits on `delimiter`, trims each piece, and drops the empty ones: "a, ,b" -> {"a", "b"}. +constexpr std::vector StrSplit(std::string_view input, char delimiter) { std::vector result; - int last_pos = 0; - for (int i = 0; i < input.length(); i++) { - if (input[i] != delimiter) { - continue; - } - - if (i - last_pos > 0) { - result.emplace_back(input.substr(last_pos, i - last_pos)); - } - last_pos = i + 1; + while (true) { + const auto end = input.find(delimiter); + const auto token = StripAsciiWhitespace(input.substr(0, end)); + if (!token.empty()) + result.emplace_back(token); + if (end == std::string_view::npos) + return result; + input.remove_prefix(end + 1); } - - if (last_pos != input.length()) { - auto sp = input.substr(last_pos, input.length() - last_pos); - sp = StripAsciiWhitespace(sp); - if (!sp.empty()) { - result.emplace_back(sp); - } - } - - return result; -} - -inline std::wstring to_wstring(const std::string& str) -{ -#if _WIN32 - return winrt::to_hstring(str).c_str(); -#else - size_t expected_utf32words = simdutf::utf32_length_from_utf8(str.data(), str.length()); - std::unique_ptr utf32_output{new char32_t[expected_utf32words]}; - size_t utf16words = simdutf::convert_utf8_to_utf32(str.data(), str.length(), utf32_output.get()); - return std::wstring(utf32_output.get(), utf32_output.get() + utf16words); -#endif -} - -inline std::wstring to_wstring(Il2CppString* str) -{ -#if _WIN32 - return str->chars; -#else - size_t expected_utf32words = simdutf::utf32_length_from_utf16(str->chars, str->length); - std::unique_ptr utf32_output{new char32_t[expected_utf32words]}; - size_t utf16words = simdutf::convert_utf16_to_utf32(str->chars, str->length, utf32_output.get()); - return std::wstring(utf32_output.get(), utf32_output.get() + utf16words); -#endif -} - -inline std::string to_string(const std::wstring& str) -{ -#if _WIN32 - size_t expected_utf16words = simdutf::utf8_length_from_utf16((char16_t*)str.data(), str.length()); - std::unique_ptr utf8_output{new char8_t[expected_utf16words]}; - size_t utf16words = simdutf::convert_utf16_to_utf8((char16_t*)str.data(), str.length(), (char*)utf8_output.get()); - return {utf8_output.get(), utf8_output.get() + utf16words}; -#else - size_t expected_utf32words = simdutf::utf8_length_from_utf32((char32_t*)str.data(), str.length()); - std::unique_ptr utf32_output{new char8_t[expected_utf32words]}; - size_t utf16words = simdutf::convert_utf32_to_utf8((char32_t*)str.data(), str.length(), (char*)utf32_output.get()); - return std::string(utf32_output.get(), utf32_output.get() + utf16words); -#endif } inline std::string to_string(const Il2CppString* str) { - size_t expected_utf8_bytes = simdutf::utf8_length_from_utf16( - reinterpret_cast(str->chars), str->length); - std::string result(expected_utf8_bytes, '\0'); - size_t actual_utf8_bytes = simdutf::convert_utf16_to_utf8( - reinterpret_cast(str->chars), str->length, result.data()); - result.resize(actual_utf8_bytes); + if (!str) + return {}; + const auto* chars = reinterpret_cast(str->chars); + std::string result(simdutf::utf8_length_from_utf16(chars, str->length), '\0'); + result.resize(simdutf::convert_utf16_to_utf8(chars, str->length, result.data())); return result; } -inline bool operator==(const Il2CppString* lhs, const std::string& rhs) -{ - return to_string(lhs) == rhs; -} - -inline bool operator==(const std::string& lhs, const Il2CppString* rhs) -{ - return lhs == to_string(rhs); -} - inline std::optional> parse_timestamp(const std::string& timestamp) { #ifdef _WIN32 From b35d024ffb50f8c9d4ae3e0d6bd6fe542d6d8982 Mon Sep 17 00:00:00 2001 From: tashcan Date: Thu, 15 Oct 2026 16:40:40 -0700 Subject: [PATCH 02/24] Extract input handling from hotkey code Keyboard and mouse state move into an input module cached per frame, keyed on the frame count instead of an explicit reset from the hotkey hook. Shortcuts gain one settings table with defaults, tiers and deprecated aliases. The shortcut tests link the production code against a fake input and run on any host. Per-frame feature updates now run whatever the hotkey mode, instead of only when the hotkey patch happened to be installed. --- mods/src/config.cc | 315 +--------- mods/src/file.h | 2 - mods/src/patches/fleet_watch.cc | 190 +----- mods/src/patches/fleet_watch.h | 15 +- mods/src/patches/gamefunctions.h | 113 ---- mods/src/patches/input.cc | 140 +++++ mods/src/patches/input.h | 29 + mods/src/patches/key.cc | 367 ----------- mods/src/patches/key.h | 42 -- mods/src/patches/mapkey.cc | 291 --------- mods/src/patches/mapkey.h | 41 -- mods/src/patches/modifierkey.cc | 115 ---- mods/src/patches/modifierkey.h | 31 - mods/src/patches/parts/alliance_store.cc | 52 +- mods/src/patches/parts/chat.cc | 12 +- .../patches/parts/daily_faction_bulk_claim.cc | 3 + .../patches/parts/daily_faction_bulk_claim.h | 5 - .../patches/parts/double_click_assign_ship.cc | 3 +- mods/src/patches/parts/fix_pan.cc | 12 +- mods/src/patches/parts/focus_search.cc | 96 --- mods/src/patches/parts/focus_search.h | 3 - .../patches/parts/galactic_anomaly_timer.cc | 16 +- mods/src/patches/parts/hotkeys.cc | 332 ++++++---- mods/src/patches/parts/pinned_ship_click.cc | 2 +- mods/src/patches/parts/sync.cc | 13 +- mods/src/patches/parts/warp_action_label.cc | 15 +- mods/src/patches/parts/zoom.cc | 35 +- mods/src/patches/patches.h | 56 ++ mods/src/patches/screen_update_hook.cc | 48 -- mods/src/patches/screen_update_hook.h | 12 - mods/src/patches/ship_shortcut_hint.h | 2 +- mods/src/patches/shortcuts.cc | 590 ++++++++++++++++++ mods/src/patches/shortcuts.h | 166 +++++ tests/chat_startup.cc | 27 - tests/mod_logic.cc | 222 +++++++ tests/run-shortcut-hint-cache.ps1 | 22 - tests/shortcut_hint_cache.cc | 147 ----- xmake.lua | 9 +- 38 files changed, 1534 insertions(+), 2057 deletions(-) delete mode 100644 mods/src/patches/gamefunctions.h create mode 100644 mods/src/patches/input.cc create mode 100644 mods/src/patches/input.h delete mode 100644 mods/src/patches/key.cc delete mode 100644 mods/src/patches/key.h delete mode 100644 mods/src/patches/mapkey.cc delete mode 100644 mods/src/patches/mapkey.h delete mode 100644 mods/src/patches/modifierkey.cc delete mode 100644 mods/src/patches/modifierkey.h delete mode 100644 mods/src/patches/parts/daily_faction_bulk_claim.h delete mode 100644 mods/src/patches/parts/focus_search.cc delete mode 100644 mods/src/patches/parts/focus_search.h delete mode 100644 mods/src/patches/screen_update_hook.cc delete mode 100644 mods/src/patches/screen_update_hook.h create mode 100644 mods/src/patches/shortcuts.cc create mode 100644 mods/src/patches/shortcuts.h delete mode 100644 tests/chat_startup.cc create mode 100644 tests/mod_logic.cc delete mode 100644 tests/run-shortcut-hint-cache.ps1 delete mode 100644 tests/shortcut_hint_cache.cc diff --git a/mods/src/config.cc b/mods/src/config.cc index 2928b0076..410556fd3 100644 --- a/mods/src/config.cc +++ b/mods/src/config.cc @@ -1,7 +1,6 @@ #include "config.h" #include "file.h" -#include "patches/mapkey.h" -#include "prime/KeyCode.h" +#include "patches/shortcuts.h" #include "ship_name_match.h" #include "str_utils.h" #include "version.h" @@ -711,172 +710,6 @@ void read_sync_targets(toml::table& config, toml::table& new_config, } } -struct ShortcutConfigValue { - std::string value; - std::string source_item; - bool from_config = false; - bool valid_type = true; -}; - -std::string shortcut_source_label(const ShortcutConfigValue& shortcut_value, std::string_view item) -{ - if (!shortcut_value.valid_type) { - return "invalid"; - } - if (!shortcut_value.from_config) { - return "default"; - } - if (shortcut_value.source_item == item) { - return "config"; - } - - std::string label = "alias:"; - label.append(shortcut_value.source_item); - return label; -} - -void set_shortcut_source(toml::node_view sourceTable, std::string_view item, std::string_view source) -{ - sourceTable.as_table()->insert_or_assign(item, std::string(source)); -} - -void set_shortcut_noop(toml::node_view sectionTable, toml::node_view sourceTable, - std::string_view item, std::string_view source) -{ - sectionTable.as_table()->insert_or_assign(item, "NONE"); - set_shortcut_source(sourceTable, item, source); - spdlog::debug("shortcut value shortcuts.{} value: NONE", item); -} - -void parse_config_shortcut_value(toml::table& new_config, std::string_view item, GameFunction gameFunction, - std::string_view default_value, const ShortcutConfigValue& shortcut_value) -{ - auto section = "shortcuts"; - auto source = "shortcuts_source"; - - new_config.emplace(section, toml::table()); - new_config.emplace(source, toml::table()); - - auto sectionTable = new_config[section]; - auto sourceTable = new_config[source]; - auto sourceLabel = shortcut_source_label(shortcut_value, item); - - if (!shortcut_value.valid_type) { - spdlog::error("Invalid shortcut value [shortcuts].{} must be a string; using default for [shortcuts].{}.", - shortcut_value.source_item, item); - return parse_config_shortcut_value(new_config, item, gameFunction, default_value, - {std::string(default_value), std::string(item), false, true}); - } - - auto config_value = shortcut_value.value; - auto valueTrimmed = StripTrailingAsciiWhitespace(config_value); - auto valueLowered = AsciiStrToUpper(valueTrimmed); - - // An explicit empty string disables the binding, just like NONE. Missing or - // invalid settings still fall back to their defaults. - if (valueTrimmed.empty() || valueLowered == "NONE") { - set_shortcut_noop(sectionTable, sourceTable, item, sourceLabel); - return; - } - - auto wantedKeys = StrSplit(valueLowered, '|'); - - bool keyAdded = false; - for (std::string_view wantedKey : wantedKeys) { - MapKey mapKey = MapKey::Parse(wantedKey); - - if (mapKey.Key != KeyCode::None) { - keyAdded = true; - MapKey::AddMappedKey(gameFunction, std::move(mapKey)); - } else if (!wantedKey.empty()) { - spdlog::warn("Invalid shortcut token [shortcuts].{} token='{}' value='{}'; ignoring token.", - shortcut_value.source_item, wantedKey, config_value); - } - } - - if (!keyAdded) { - if (shortcut_value.from_config) { - spdlog::error("No valid shortcut tokens for [shortcuts].{} value='{}'; using default for [shortcuts].{}.", - shortcut_value.source_item, config_value, item); - return parse_config_shortcut_value(new_config, item, gameFunction, default_value, - {std::string(default_value), std::string(item), false, true}); - } else { - spdlog::error("Default shortcut [shortcuts].{} value='{}' has no valid tokens; action disabled.", item, - config_value); - } - set_shortcut_noop(sectionTable, sourceTable, item, "invalid"); - return; - } - - auto shortcut = MapKey::GetShortcuts(gameFunction); - sectionTable.as_table()->insert_or_assign(item, shortcut); - set_shortcut_source(sourceTable, item, sourceLabel); - - spdlog::debug("shortcut value {}.{} value: {}", section, item, shortcut); -} - -bool shortcut_key_exists(toml::table& config, std::string_view item) -{ - auto shortcuts = config["shortcuts"].as_table(); - return shortcuts && shortcuts->contains(item); -} - -ShortcutConfigValue get_shortcut_value_or_default(toml::table& config, std::string_view item, - std::string_view default_value) -{ - if (!shortcut_key_exists(config, item)) { - return {std::string(default_value), std::string(item), false, true}; - } - - auto value = config["shortcuts"][item].value(); - if (!value) { - return {"", std::string(item), true, false}; - } - - return {*value, std::string(item), true, true}; -} - -void parse_config_shortcut(toml::table& config, toml::table& new_config, std::string_view item, - GameFunction gameFunction, std::string_view default_value) -{ - auto section = "shortcuts"; - - config.emplace(section, toml::table()); - - parse_config_shortcut_value(new_config, item, gameFunction, default_value, - get_shortcut_value_or_default(config, item, default_value)); -} - -void parse_config_shortcut_aliases(toml::table& config, toml::table& new_config, std::string_view item, - GameFunction gameFunction, std::string_view default_value, - std::initializer_list aliases) -{ - auto section = "shortcuts"; - - config.emplace(section, toml::table()); - - auto config_value = get_shortcut_value_or_default(config, item, default_value); - const auto has_item = shortcut_key_exists(config, item); - - for (const auto alias : aliases) { - if (!shortcut_key_exists(config, alias)) { - continue; - } - - if (has_item) { - spdlog::warn("Ignoring deprecated shortcut alias [shortcuts].{} because [shortcuts].{} is set.", alias, item); - continue; - } - - config_value = get_shortcut_value_or_default(config, alias, default_value); - spdlog::warn("Deprecated shortcut alias [shortcuts].{} is supported for compatibility; use [shortcuts].{}.", - alias, item); - break; - } - - parse_config_shortcut_value(new_config, item, gameFunction, default_value, config_value); -} - void migrate_mac_config_if_needed(const char* filename) { #if !_WIN32 @@ -1419,151 +1252,7 @@ void Config::Load() spdlog::debug(""); - parse_config_shortcut(config, parsed, "move_up", GameFunction::MoveUp, DCSH::move_up); - parse_config_shortcut(config, parsed, "move_down", GameFunction::MoveDown, DCSH::move_down); - parse_config_shortcut(config, parsed, "move_left", GameFunction::MoveLeft, DCSH::move_left); - parse_config_shortcut(config, parsed, "move_right", GameFunction::MoveRight, DCSH::move_right); - - parse_config_shortcut_aliases(config, parsed, "set_hotkeys_disabled", GameFunction::DisableHotKeys, - DCSH::set_hotkeys_disabled, {"set_hotkeys_disble", "set_hotkeys_disable"}); - parse_config_shortcut_aliases(config, parsed, "set_hotkeys_enabled", GameFunction::EnableHotKeys, - DCSH::set_hotkeys_enabled, {"set_hotkeys_enable"}); - - parse_config_shortcut(config, parsed, "select_chatalliance", GameFunction::SelectChatAlliance, - DCSH::select_chatalliance); - parse_config_shortcut(config, parsed, "select_chatglobal", GameFunction::SelectChatGlobal, DCSH::select_chatglobal); - parse_config_shortcut(config, parsed, "select_chatprivate", GameFunction::SelectChatPrivate, - DCSH::select_chatprivate); - parse_config_shortcut(config, parsed, "quit", GameFunction::Quit, DCSH::quit); - - parse_config_shortcut(config, parsed, "select_ship1", GameFunction::SelectShip1, DCSH::select_ship1); - parse_config_shortcut(config, parsed, "select_ship2", GameFunction::SelectShip2, DCSH::select_ship2); - parse_config_shortcut(config, parsed, "select_ship3", GameFunction::SelectShip3, DCSH::select_ship3); - parse_config_shortcut(config, parsed, "select_ship4", GameFunction::SelectShip4, DCSH::select_ship4); - parse_config_shortcut(config, parsed, "select_ship5", GameFunction::SelectShip5, DCSH::select_ship5); - parse_config_shortcut(config, parsed, "select_ship6", GameFunction::SelectShip6, DCSH::select_ship6); - parse_config_shortcut(config, parsed, "select_ship7", GameFunction::SelectShip7, DCSH::select_ship7); - parse_config_shortcut(config, parsed, "select_ship8", GameFunction::SelectShip8, DCSH::select_ship8); - parse_config_shortcut(config, parsed, "select_current", GameFunction::SelectCurrent, DCSH::select_current); - parse_config_shortcut(config, parsed, "focus_search", GameFunction::FocusSearch, DCSH::focus_search); - - parse_config_shortcut(config, parsed, "action_primary", GameFunction::ActionPrimary, DCSH::action_primary); - parse_config_shortcut(config, parsed, "action_secondary", GameFunction::ActionSecondary, DCSH::action_secondary); - parse_config_shortcut(config, parsed, "action_queue", GameFunction::ActionQueue, DCSH::action_queue); - parse_config_shortcut(config, parsed, "action_queue_clear", GameFunction::ActionQueueClear, DCSH::action_queue_clear); - parse_config_shortcut(config, parsed, "action_view", GameFunction::ActionView, DCSH::action_view); - parse_config_shortcut(config, parsed, "action_recall", GameFunction::ActionRecall, DCSH::action_recall); - parse_config_shortcut(config, parsed, "action_recall_cancel", GameFunction::ActionRecallCancel, - DCSH::action_recall_cancel); - parse_config_shortcut(config, parsed, "action_repair", GameFunction::ActionRepair, DCSH::action_repair); - parse_config_shortcut(config, parsed, "show_chat", GameFunction::ShowChat, DCSH::show_chat); - parse_config_shortcut(config, parsed, "show_chatside1", GameFunction::ShowChatSide1, DCSH::show_chatside1); - parse_config_shortcut(config, parsed, "show_chatside2", GameFunction::ShowChatSide2, DCSH::show_chatside2); - parse_config_shortcut(config, parsed, "show_galaxy", GameFunction::ShowGalaxy, DCSH::show_galaxy); - parse_config_shortcut_aliases(config, parsed, "show_galaxy_native", GameFunction::NativeShortcutGalaxy, - DCSH::show_galaxy_native, {"native_shortcut_galaxy"}); - parse_config_shortcut(config, parsed, "show_system", GameFunction::ShowSystem, DCSH::show_system); - parse_config_shortcut(config, parsed, "zoom_preset1", GameFunction::ZoomPreset1, DCSH::zoom_preset1); - parse_config_shortcut(config, parsed, "zoom_preset2", GameFunction::ZoomPreset2, DCSH::zoom_preset2); - parse_config_shortcut(config, parsed, "zoom_preset3", GameFunction::ZoomPreset3, DCSH::zoom_preset3); - parse_config_shortcut(config, parsed, "zoom_preset4", GameFunction::ZoomPreset4, DCSH::zoom_preset4); - parse_config_shortcut(config, parsed, "zoom_preset5", GameFunction::ZoomPreset5, DCSH::zoom_preset5); - parse_config_shortcut(config, parsed, "zoom_in", GameFunction::ZoomIn, DCSH::zoom_in); - parse_config_shortcut(config, parsed, "zoom_out", GameFunction::ZoomOut, DCSH::zoom_out); - parse_config_shortcut(config, parsed, "zoom_max", GameFunction::ZoomMax, DCSH::zoom_max); - parse_config_shortcut(config, parsed, "zoom_min", GameFunction::ZoomMin, DCSH::zoom_min); - parse_config_shortcut(config, parsed, "zoom_reset", GameFunction::ZoomReset, DCSH::zoom_reset); - parse_config_shortcut(config, parsed, "ui_scaleup", GameFunction::UiScaleUp, DCSH::ui_scaleup); - parse_config_shortcut(config, parsed, "ui_scaledown", GameFunction::UiScaleDown, DCSH::ui_scaledown); - parse_config_shortcut(config, parsed, "ui_scaleshipup", GameFunction::UiShipScaleUp, DCSH::ui_scaleshipup); - parse_config_shortcut(config, parsed, "ui_scaleshipdown", GameFunction::UiShipScaleDown, DCSH::ui_scaleshipdown); - parse_config_shortcut(config, parsed, "ui_scaleviewerup", GameFunction::UiViewerScaleUp, DCSH::ui_scaleviewerup); - parse_config_shortcut(config, parsed, "ui_scaleviewerdown", GameFunction::UiViewerScaleDown, - DCSH::ui_scaleviewerdown); - - parse_config_shortcut(config, parsed, "log_debug", GameFunction::LogLevelDebug, DCSH::log_debug); - parse_config_shortcut(config, parsed, "log_trace", GameFunction::LogLevelTrace, DCSH::log_trace); - parse_config_shortcut(config, parsed, "log_info", GameFunction::LogLevelInfo, DCSH::log_info); - parse_config_shortcut(config, parsed, "log_warn", GameFunction::LogLevelWarn, DCSH::log_warn); - parse_config_shortcut(config, parsed, "log_error", GameFunction::LogLevelError, DCSH::log_error); - parse_config_shortcut(config, parsed, "log_off", GameFunction::LogLevelOff, DCSH::log_off); - parse_config_shortcut(config, parsed, "restart", GameFunction::Restart, DCSH::restart); - - parse_config_shortcut(config, parsed, "show_awayteam", GameFunction::ShowAwayTeam, DCSH::show_awayteam); - parse_config_shortcut(config, parsed, "show_gifts", GameFunction::ShowGifts, DCSH::show_gifts); - parse_config_shortcut(config, parsed, "show_artifacts", GameFunction::ShowArtifacts, DCSH::show_artifacts); - parse_config_shortcut(config, parsed, "show_commander", GameFunction::ShowCommander, DCSH::show_commander); - parse_config_shortcut(config, parsed, "show_daily", GameFunction::ShowDaily, DCSH::show_daily); - parse_config_shortcut(config, parsed, "show_events", GameFunction::ShowEvents, DCSH::show_events); - parse_config_shortcut_aliases(config, parsed, "show_events_native", GameFunction::NativeShortcutEvents, - DCSH::show_events_native, {"native_shortcut_events"}); - parse_config_shortcut(config, parsed, "show_exocomp", GameFunction::ShowExoComp, DCSH::show_exocomp); - parse_config_shortcut(config, parsed, "show_factions", GameFunction::ShowFactions, DCSH::show_factions); - parse_config_shortcut(config, parsed, "show_inventory", GameFunction::ShowInventory, DCSH::show_inventory); - parse_config_shortcut(config, parsed, "show_missions", GameFunction::ShowMissions, DCSH::show_missions); - parse_config_shortcut(config, parsed, "show_research", GameFunction::ShowResearch, DCSH::show_research); - parse_config_shortcut(config, parsed, "show_scrapyard", GameFunction::ShowScrapYard, DCSH::show_scrapyard); - parse_config_shortcut(config, parsed, "show_settings", GameFunction::ShowSettings, DCSH::show_settings); - parse_config_shortcut(config, parsed, "toggle_shortcut_hints", GameFunction::ToggleShortcutHints, - DCSH::toggle_shortcut_hints); - parse_config_shortcut(config, parsed, "show_officers", GameFunction::ShowOfficers, DCSH::show_officers); - parse_config_shortcut(config, parsed, "show_officerpresets", GameFunction::ShowOfficerPresets, - DCSH::show_officerpresets); - parse_config_shortcut(config, parsed, "show_officerswap", GameFunction::ShowOfficerSwap, DCSH::show_officerswap); - parse_config_shortcut(config, parsed, "show_qtrials", GameFunction::ShowQTrials, DCSH::show_qtrials); - parse_config_shortcut(config, parsed, "show_refinery", GameFunction::ShowRefinery, DCSH::show_refinery); - parse_config_shortcut(config, parsed, "show_ships", GameFunction::ShowShips, DCSH::show_ships); - parse_config_shortcut(config, parsed, "show_shipconstruction", GameFunction::ShowShipConstruction, - DCSH::show_shipconstruction); - parse_config_shortcut(config, parsed, "show_shipswap", GameFunction::ShowShipSwap, DCSH::show_shipswap); - parse_config_shortcut(config, parsed, "show_shields", GameFunction::ShowShields, DCSH::show_shields); - parse_config_shortcut(config, parsed, "show_battlelogs", GameFunction::ShowBattlelogs, DCSH::show_battlelogs); - parse_config_shortcut(config, parsed, "show_stationexterior", GameFunction::ShoWStationExterior, - DCSH::show_stationexterior); - parse_config_shortcut(config, parsed, "show_stationinterior", GameFunction::ShowStationInterior, - DCSH::show_stationinterior); - parse_config_shortcut(config, parsed, "show_haven", GameFunction::ShowHaven, DCSH::show_haven); - parse_config_shortcut(config, parsed, "toggle_queue", GameFunction::ToggleQueue, DCSH::toggle_queue); - parse_config_shortcut(config, parsed, "toggle_instant_warp", GameFunction::ToggleAutoConfirmInstantWarp, - DCSH::toggle_instant_warp); - - if (this->hotkeys_extended) { - parse_config_shortcut(config, parsed, "show_alliance", GameFunction::ShowAlliance, DCSH::show_alliance); - - if (this->enable_experimental) { - parse_config_shortcut(config, parsed, "show_alliance_help", GameFunction::ShowAllianceHelp, - DCSH::show_alliance_help); - parse_config_shortcut(config, parsed, "show_alliance_armada", GameFunction::ShowAllianceArmada, - DCSH::show_alliance_armada); - } - - parse_config_shortcut(config, parsed, "show_bookmarks", GameFunction::ShowBookmarks, DCSH::show_bookmarks); - - if (this->enable_experimental) { - parse_config_shortcut(config, parsed, "show_lookup", GameFunction::ShowLookup, DCSH::show_lookup); - } - - parse_config_shortcut(config, parsed, "set_zoom_preset1", GameFunction::SetZoomPreset1, DCSH::set_zoom_preset1); - parse_config_shortcut(config, parsed, "set_zoom_preset2", GameFunction::SetZoomPreset2, DCSH::set_zoom_preset2); - parse_config_shortcut(config, parsed, "set_zoom_preset3", GameFunction::SetZoomPreset3, DCSH::set_zoom_preset3); - parse_config_shortcut(config, parsed, "set_zoom_preset4", GameFunction::SetZoomPreset4, DCSH::set_zoom_preset4); - parse_config_shortcut(config, parsed, "set_zoom_preset5", GameFunction::SetZoomPreset5, DCSH::set_zoom_preset5); - parse_config_shortcut(config, parsed, "set_zoom_default", GameFunction::SetZoomDefault, DCSH::set_zoom_default); - parse_config_shortcut(config, parsed, "toggle_preview_locate", GameFunction::TogglePreviewLocate, - DCSH::toggle_preview_locate); - parse_config_shortcut(config, parsed, "toggle_preview_recall", GameFunction::TogglePreviewRecall, - DCSH::toggle_preview_recall); - parse_config_shortcut(config, parsed, "toggle_cargo_default", GameFunction::ToggleCargoDefault, - DCSH::toggle_cargo_default); - parse_config_shortcut(config, parsed, "toggle_cargo_player", GameFunction::ToggleCargoPlayer, - DCSH::toggle_cargo_player); - parse_config_shortcut(config, parsed, "toggle_cargo_station", GameFunction::ToggleCargoStation, - DCSH::toggle_cargo_station); - parse_config_shortcut(config, parsed, "toggle_cargo_hostile", GameFunction::ToggleCargoHostile, - DCSH::toggle_cargo_hostile); - parse_config_shortcut(config, parsed, "toggle_cargo_armada", GameFunction::ToggleCargoArmada, - DCSH::toggle_cargo_armada); - } + Shortcut::Load(config, parsed, this->hotkeys_extended, this->enable_experimental); spdlog::debug(""); diff --git a/mods/src/file.h b/mods/src/file.h index 8adcf99e0..74a504ba6 100644 --- a/mods/src/file.h +++ b/mods/src/file.h @@ -1,6 +1,4 @@ #include "config.h" -#include "patches/mapkey.h" -#include "prime/KeyCode.h" #include "str_utils.h" #include "version.h" #include diff --git a/mods/src/patches/fleet_watch.cc b/mods/src/patches/fleet_watch.cc index 472466711..d26fe83d2 100644 --- a/mods/src/patches/fleet_watch.cc +++ b/mods/src/patches/fleet_watch.cc @@ -1,7 +1,7 @@ #include "patches/fleet_watch.h" #include "errormsg.h" -#include "patches/screen_update_hook.h" +#include "patches/patches.h" #include @@ -11,9 +11,7 @@ #include #include #include -#include #include -#include #include namespace @@ -21,10 +19,6 @@ namespace constexpr int kFleetSlotCount = 10; constexpr int64_t kManagerProbeIntervalMs = 250; constexpr int64_t kManagerDiscoveryIntervalMs = 5'000; -constexpr int64_t kFastPollIntervalMs = 250; -constexpr int64_t kFastPollBackoffMs = 5'000; -constexpr int64_t kFastPollPeriodMs = 30'000; -constexpr int64_t kFastPollLifetimeMs = 24LL * 60 * 60 * 1'000; constexpr int64_t kSeedIntervalMs = 1'000; constexpr int64_t kSeedBackoffIntervalMs = 5'000; constexpr int64_t kSeedFastPeriodMs = 30'000; @@ -33,11 +27,9 @@ constexpr int64_t kSeedStabilizationMs = 5'000; constexpr int64_t kSeedMaxStabilizationMs = 30'000; struct ObservedFleet { - uint64_t fleet_id = 0; - FleetState state = FleetState::Unknown; - bool occupied = false; - int64_t fast_poll_started_ms = 0; - bool fast_poll_exhausted = false; + uint64_t fleet_id = 0; + FleetState state = FleetState::Unknown; + bool occupied = false; }; std::array s_slots{}; @@ -53,14 +45,8 @@ int64_t s_seed_last_change_ms = 0; int64_t s_last_seed_attempt_ms = 0; int64_t s_last_manager_probe_ms = 0; int64_t s_last_manager_discovery_ms = 0; -int64_t s_last_fast_poll_ms = 0; int s_manager_probe_slot = 0; -int s_fast_poll_count = 0; int s_dispatch_depth = 0; -bool s_evaluating_fast_poll = false; -#ifdef _MODDBG -bool s_runtime_probe_enabled = false; -#endif class CallbackScope { @@ -78,36 +64,6 @@ int64_t now_milliseconds() .count(); } -bool needs_fast_poll(FleetState state) -{ - if (s_evaluating_fast_poll) { - spdlog::warn("[FleetWatch] suppressed recursive fast-poll predicate evaluation"); - return false; - } - s_evaluating_fast_poll = true; - struct PredicateScope { - ~PredicateScope() - { s_evaluating_fast_poll = false; } - } predicate_scope; - CallbackScope callback_scope; - - for (const auto& subscription : s_subscriptions) { - if (!subscription.needs_fast_poll) { - continue; - } - try { - if (subscription.needs_fast_poll(state)) { - return true; - } - } catch (const std::exception& error) { - spdlog::warn("[FleetWatch] fast-poll predicate failed: {}", error.what()); - } catch (...) { - spdlog::warn("[FleetWatch] fast-poll predicate failed with an unknown exception"); - } - } - return false; -} - void reset_observation() { s_slots = {}; @@ -121,9 +77,7 @@ void reset_observation() s_last_seed_attempt_ms = 0; s_last_manager_probe_ms = 0; s_last_manager_discovery_ms = 0; - s_last_fast_poll_ms = 0; s_manager_probe_slot = 0; - s_fast_poll_count = 0; } void restart_seed(int slot, uint64_t fleet_id) @@ -132,14 +86,6 @@ void restart_seed(int slot, uint64_t fleet_id) reset_observation(); } -void clear_slot(int slot) -{ - if (s_slots[slot].occupied && s_slots[slot].fast_poll_started_ms != 0) { - --s_fast_poll_count; - } - s_slots[slot] = {}; -} - int occupied_slot_count() { return static_cast( @@ -198,24 +144,10 @@ void observe_fleet(FleetPlayerData* fleet, int requested_slot, bool publish) restart_seed(slot, fleet_id); } - const bool changed = !same_state; - - const bool was_fast_polling = previous.occupied && previous.fast_poll_started_ms != 0; - int64_t fast_started_ms = 0; - const bool fast_exhausted = same_state && previous.fast_poll_exhausted; - if (!fast_exhausted && needs_fast_poll(state)) { - fast_started_ms = same_fleet && was_fast_polling ? previous.fast_poll_started_ms : now_milliseconds(); - } + const bool changed = !same_state; const auto previous_state = previous.state; - previous = ObservedFleet{fleet_id, state, true, fast_started_ms, fast_exhausted}; + previous = ObservedFleet{fleet_id, state, true}; - const bool is_fast_polling = fast_started_ms != 0; - if (was_fast_polling != is_fast_polling) { - s_fast_poll_count += is_fast_polling ? 1 : -1; - if (is_fast_polling && s_fast_poll_count == 1) { - s_last_fast_poll_ms = 0; - } - } if (s_seed_pending && changed) { const auto now_ms = now_milliseconds(); if (!s_seed_has_observation) { @@ -231,14 +163,12 @@ void observe_fleet(FleetPlayerData* fleet, int requested_slot, bool publish) } struct ScanResult { - int observed_count = 0; - bool manager_available = false; - bool topology_changed = false; - int changed_slot = -1; - uint64_t changed_fleet = 0; + int observed_count = 0; + bool manager_available = false; }; -ScanResult scan_manager(bool publish, bool only_fast_polling) +// Reads every slot while a baseline is being established. Transitions are not published from here. +ScanResult scan_manager() { ScanResult result; auto* manager = FleetsManager::Instance(); @@ -248,57 +178,34 @@ ScanResult scan_manager(bool publish, bool only_fast_polling) result.manager_available = true; std::array fleets{}; + bool topology_changed = false; for (int slot = 0; slot < kFleetSlotCount; ++slot) { - if (only_fast_polling && (!s_slots[slot].occupied || s_slots[slot].fast_poll_started_ms == 0)) { - continue; - } fleets[slot] = manager->GetFleetPlayerData(slot); if (fleets[slot]) { ++result.observed_count; } - if (only_fast_polling) { - continue; - } const bool occupied = fleets[slot] != nullptr; const auto fleet_id = occupied ? fleets[slot]->Id() : 0; if (s_slots[slot].occupied != occupied || (occupied && s_slots[slot].fleet_id != fleet_id)) { - result.topology_changed = true; - if (result.changed_slot < 0) { - result.changed_slot = slot; - result.changed_fleet = fleet_id; - } + topology_changed = true; } } - if (result.topology_changed && publish && !s_seed_pending) { - restart_seed(result.changed_slot, result.changed_fleet); - publish = false; - } else if (result.topology_changed && s_seed_pending && s_seed_has_observation) { + if (topology_changed && s_seed_pending && s_seed_has_observation) { s_seed_last_change_ms = now_milliseconds(); s_seed_finalize_candidate = false; } for (int slot = 0; slot < kFleetSlotCount; ++slot) { - if (only_fast_polling && (!s_slots[slot].occupied || s_slots[slot].fast_poll_started_ms == 0)) { - continue; - } if (fleets[slot]) { - observe_fleet(fleets[slot], slot, publish); - } else if (!only_fast_polling) { - clear_slot(slot); + observe_fleet(fleets[slot], slot, false); + } else { + s_slots[slot] = {}; } } s_seed_manager_backed = true; return result; } - -bool install_observer() -{ - if (!install_screen_manager_update_hook()) { - return false; - } - return register_screen_manager_update_callback(fleet_watch::Tick); -} } // namespace namespace fleet_watch @@ -316,7 +223,7 @@ bool Subscribe(Subscription subscription) } if (!s_observer_installed) { - if (!install_observer()) { + if (!InstallScreenManagerUpdateHook()) { return false; } s_observer_installed = true; @@ -341,7 +248,7 @@ void Tick() const auto interval = lifetime < kSeedFastPeriodMs ? kSeedIntervalMs : kSeedBackoffIntervalMs; if (s_last_seed_attempt_ms == 0 || now_ms - s_last_seed_attempt_ms >= interval) { s_last_seed_attempt_ms = now_ms; - const auto scan = scan_manager(false, false); + const auto scan = scan_manager(); if (s_seed_manager_backed && !scan.manager_available) { reset_observation(); return; @@ -361,7 +268,7 @@ void Tick() s_seed_finalize_candidate = true; return; } - const auto final_scan = scan_manager(false, false); + const auto final_scan = scan_manager(); if (!forced && now_ms - s_seed_last_change_ms < kSeedStabilizationMs) { s_seed_finalize_candidate = false; return; @@ -370,13 +277,7 @@ void Tick() const auto baseline_count = final_scan.observed_count > 0 ? final_scan.observed_count : occupied_slot_count(); s_seed_pending = false; s_last_manager_probe_ms = now_ms; - s_last_fast_poll_ms = s_fast_poll_count > 0 ? now_ms : 0; spdlog::debug("[FleetWatch] established stable baseline for {} fleet slots", baseline_count); -#ifdef _MODDBG - if (s_runtime_probe_enabled) { - spdlog::info("[FleetWatchProbe] established stable baseline for {} fleet slots", baseline_count); - } -#endif if (final_scan.manager_available) { dispatch_baseline(); } @@ -421,58 +322,5 @@ void Tick() observe_fleet(fleet, slot, true); } } - - if (s_fast_poll_count <= 0) { - return; - } - bool fast_period = false; - for (auto& slot : s_slots) { - if (!slot.occupied || slot.fast_poll_started_ms == 0) { - continue; - } - const auto lifetime = now_ms - slot.fast_poll_started_ms; - if (lifetime >= kFastPollLifetimeMs) { - slot.fast_poll_started_ms = 0; - slot.fast_poll_exhausted = true; - --s_fast_poll_count; - } else if (lifetime < kFastPollPeriodMs) { - fast_period = true; - } - } - const auto interval = fast_period ? kFastPollIntervalMs : kFastPollBackoffMs; - if (s_fast_poll_count > 0 && (s_last_fast_poll_ms == 0 || now_ms - s_last_fast_poll_ms >= interval)) { - s_last_fast_poll_ms = now_ms; - scan_manager(true, true); - } -} - -#ifdef _MODDBG -namespace -{ - void runtime_probe_transition(const Transition& transition) - { - spdlog::info("[FleetWatchProbe] slot={} fleet={} oldState={} newState={}", transition.after.slot, - transition.after.fleet_id, static_cast(transition.before.state), - static_cast(transition.after.state)); - } - - bool runtime_probe_fast_poll(FleetState state) - { return state != FleetState::Unknown; } -} // namespace - -void InstallRuntimeProbe() -{ - const auto* enabled = std::getenv("STFC_MOD_FLEET_WATCH_PROBE"); - if (!enabled || std::string_view{enabled} != "1") { - return; - } - s_runtime_probe_enabled = true; - if (Subscribe({runtime_probe_transition, runtime_probe_fast_poll})) { - spdlog::info("[FleetWatchProbe] runtime subscriber installed"); - } else { - s_runtime_probe_enabled = false; - spdlog::warn("[FleetWatchProbe] runtime subscriber installation failed"); - } } -#endif } // namespace fleet_watch diff --git a/mods/src/patches/fleet_watch.h b/mods/src/patches/fleet_watch.h index 071ac1ef1..89810fa80 100644 --- a/mods/src/patches/fleet_watch.h +++ b/mods/src/patches/fleet_watch.h @@ -17,29 +17,22 @@ struct Transition { }; using TransitionCallback = void (*)(const Transition& transition); -using FastPollPredicate = bool (*)(FleetState state); using BaselineCallback = void (*)(); struct Subscription { - TransitionCallback on_transition = nullptr; - FastPollPredicate needs_fast_poll = nullptr; + TransitionCallback on_transition = nullptr; // Optional. Runs on the game thread each time a stable baseline is established while the fleet service is available // (after login, and again after any fleet-slot topology change). Transitions are only dispatched after a baseline, so // a consumer that needs the complete fleet list reads FleetsManager here. - BaselineCallback on_baseline = nullptr; + BaselineCallback on_baseline = nullptr; }; -// Registers a process-lifetime fleet-state observer. The first subscription joins the shared screen-update dispatcher; +// Registers a process-lifetime fleet-state observer. The first subscription asks for the ScreenManager.Update tick; // with no subscriptions, Fleet Watch performs no scans or IL2CPP work. // Register during patch installation. Callbacks run synchronously on the game thread, and Transition::fleet is valid // only for the duration of the callback. bool Subscribe(Subscription subscription); -// Called by the shared ScreenManager.Update detour. Consumers should use Subscribe rather than calling this directly. +// Called from the ScreenManager.Update tick. Consumers should use Subscribe rather than calling this directly. void Tick(); - -#ifdef _MODDBG -// Enables a raw transition logger when STFC_MOD_FLEET_WATCH_PROBE=1 for exact-artifact runtime validation. -void InstallRuntimeProbe(); -#endif } // namespace fleet_watch diff --git a/mods/src/patches/gamefunctions.h b/mods/src/patches/gamefunctions.h deleted file mode 100644 index 7c2c8ba4d..000000000 --- a/mods/src/patches/gamefunctions.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -enum GameFunction { - MoveLeft, - MoveRight, - MoveUp, - MoveDown, - SelectChatAlliance, - SelectChatGlobal, - SelectChatPrivate, - SelectShip1, - SelectShip2, - SelectShip3, - SelectShip4, - SelectShip5, - SelectShip6, - SelectShip7, - SelectShip8, - SelectCurrent, - ShowAlliance, - ShowAllianceArmada, - ShowAllianceHelp, - ShowArtifacts, - ShowOfficers, - ShowOfficerPresets, - ShowCommander, - ShowRefinery, - ShowQTrials, - ShowBookmarks, - ShowLookup, - ShowExoComp, - ShowFactions, - ShowGifts, - ShowDaily, - ShowAwayTeam, - ShowMissions, - ShowResearch, - ShowScrapYard, - ShowShips, - ShowInventory, - ShowStationInterior, - ShowHaven, - ShoWStationExterior, - ShowGalaxy, - NativeShortcutGalaxy, - ShowSystem, - ShowChat, - ShowChatSide1, - ShowChatSide2, - ShowEvents, - NativeShortcutEvents, - ShowSettings, - ToggleShortcutHints, - ZoomPreset1, - ZoomPreset2, - ZoomPreset3, - ZoomPreset4, - ZoomPreset5, - ZoomIn, - ZoomOut, - ZoomMin, - ZoomMax, - ZoomReset, - UiScaleUp, - UiScaleDown, - UiShipScaleUp, - UiShipScaleDown, - UiViewerScaleUp, - UiViewerScaleDown, - ActionPrimary, - ActionSecondary, - ActionQueue, - ActionQueueClear, - ActionView, - ActionRecall, - ActionRecallCancel, - ActionRepair, - SetZoomPreset1, - SetZoomPreset2, - SetZoomPreset3, - SetZoomPreset4, - SetZoomPreset5, - SetZoomDefault, - DisableHotKeys, - EnableHotKeys, - ToggleQueue, - ToggleAutoConfirmInstantWarp, - TogglePreviewLocate, - TogglePreviewRecall, - ToggleCargoDefault, - ToggleCargoPlayer, - ToggleCargoStation, - ToggleCargoHostile, - ToggleCargoArmada, - LogLevelDebug, - LogLevelInfo, - LogLevelTrace, - LogLevelError, - LogLevelWarn, - LogLevelOff, - Restart, - Quit, - FocusSearch, - - ShowShipConstruction, - ShowOfficerSwap, - ShowShipSwap, - ShowShields, - ShowBattlelogs, - - // Automatic max value - Max -}; diff --git a/mods/src/patches/input.cc b/mods/src/patches/input.cc new file mode 100644 index 000000000..4cc4a6e7b --- /dev/null +++ b/mods/src/patches/input.cc @@ -0,0 +1,140 @@ +#include "patches/input.h" + +#include + +#include + +#include +#include + +namespace +{ +constexpr auto kKeyCount = static_cast(KeyCode::Max); + +constexpr std::array kDirectionalKeys{KeyCode::LeftArrow, KeyCode::RightArrow, KeyCode::UpArrow, KeyCode::DownArrow}; + +// 0 = not read yet this frame, 1 = true, -1 = false. +int cache_frame = -1; +int cached_input_focused = 0; +int cached_modified = 0; +std::array cached_pressed{}; +std::array cached_down{}; +std::array claimed_directions{}; + +int CurrentFrame() +{ + static auto get_frameCount = il2cpp::ResolveICall("UnityEngine.Time::get_frameCount()"); + return get_frameCount(); +} + +TMP_InputField* FocusedInputField() +{ + auto* event_system = EventSystem::current(); + auto* selected = event_system ? event_system->currentSelectedGameObject() : nullptr; + auto* field = selected ? selected->GetComponent() : nullptr; + return field && field->isFocused() ? field : nullptr; +} + +void EnsureCurrentFrame() +{ + const auto frame = CurrentFrame(); + if (frame == cache_frame) + return; + + cache_frame = frame; + cached_input_focused = 0; + cached_modified = 0; + cached_pressed.fill(0); + cached_down.fill(0); + + // Prune released claims here as well as in IsDirectionalInputClaimed, so a claim cannot outlive a scene + // without NavigationPan. + for (const auto key : kDirectionalKeys) { + auto& claimed = claimed_directions[static_cast(key)]; + if (claimed && !Key::Pressed(key)) + claimed = false; + } +} +} // namespace + +namespace Key +{ +bool Pressed(KeyCode key) +{ + EnsureCurrentFrame(); + static auto GetKeyInt = il2cpp::ResolveICall("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)"); + + auto& cached = cached_pressed[static_cast(key)]; + if (cached == 0) + cached = GetKeyInt(key) ? 1 : -1; + return cached == 1; +} + +bool Down(KeyCode key) +{ + EnsureCurrentFrame(); + static auto GetKeyDownInt = + il2cpp::ResolveICall("UnityEngine.Input::GetKeyDownInt(UnityEngine.KeyCode)"); + + auto& cached = cached_down[static_cast(key)]; + if (cached == 0) + cached = GetKeyDownInt(key) ? 1 : -1; + return cached == 1; +} + +bool IsModified() +{ + EnsureCurrentFrame(); + if (cached_modified == 0) { + constexpr std::array kModifiers{KeyCode::LeftAlt, KeyCode::LeftControl, KeyCode::LeftShift, + KeyCode::RightAlt, KeyCode::RightControl, KeyCode::RightShift, + KeyCode::AltGr, KeyCode::LeftCommand, KeyCode::LeftWindows, + KeyCode::RightCommand, KeyCode::RightWindows}; + cached_modified = std::ranges::any_of(kModifiers, Pressed) ? 1 : -1; + } + return cached_modified == 1; +} + +bool HasShift() +{ return Pressed(KeyCode::LeftShift) || Pressed(KeyCode::RightShift); } + +bool HasAlt() +{ return Pressed(KeyCode::LeftAlt) || Pressed(KeyCode::RightAlt); } + +bool HasCtrl() +{ return Pressed(KeyCode::LeftControl) || Pressed(KeyCode::RightControl); } + +bool IsInputFocused() +{ + EnsureCurrentFrame(); + if (cached_input_focused == 0) + cached_input_focused = FocusedInputField() ? 1 : -1; + return cached_input_focused == 1; +} + +void ClearInputFocus() +{ + if (FocusedInputField()) + EventSystem::current()->SetSelectedGameObject(nullptr); +} + +void ClaimDirectionalInput(KeyCode key) +{ + if (std::ranges::find(kDirectionalKeys, key) != kDirectionalKeys.end()) + claimed_directions[static_cast(key)] = true; +} + +bool IsDirectionalInputClaimed() +{ + EnsureCurrentFrame(); + bool claimed = false; + for (const auto key : kDirectionalKeys) { + auto& direction = claimed_directions[static_cast(key)]; + if (direction && Pressed(key)) + claimed = true; + else + direction = false; + } + return claimed; +} +} // namespace Key diff --git a/mods/src/patches/input.h b/mods/src/patches/input.h new file mode 100644 index 000000000..077c578f2 --- /dev/null +++ b/mods/src/patches/input.h @@ -0,0 +1,29 @@ +#pragma once + +// Keyboard and mouse state from Unity's legacy Input API, read once per frame and cached. + +#include + +namespace Key +{ +// Held during this frame. +bool Pressed(KeyCode key); +// Went down this frame. +bool Down(KeyCode key); + +// Any of Ctrl, Alt, AltGr, Shift, Cmd, or Win is held. +bool IsModified(); +bool HasShift(); +bool HasAlt(); +bool HasCtrl(); + +// A TextMeshPro input field has keyboard focus, so typing must not trigger shortcuts. +bool IsInputFocused(); +// The game only clears a focused field when its back button is clicked; Escape needs this. +void ClearInputFocus(); + +// A modified shortcut on an arrow key owns that arrow until it is released, so CTRL-DOWN does not +// also pan the camera on the following frames. +void ClaimDirectionalInput(KeyCode key); +bool IsDirectionalInputClaimed(); +} // namespace Key diff --git a/mods/src/patches/key.cc b/mods/src/patches/key.cc deleted file mode 100644 index d12bc16af..000000000 --- a/mods/src/patches/key.cc +++ /dev/null @@ -1,367 +0,0 @@ -#include "key.h" -#include "prime/unity.h" -#include "str_utils.h" -#include - -#include -#include -#include - -int Key::cacheInputFocused = 0; -int Key::cacheInputModified = 0; -int Key::cacheFrame = -1; - -std::array Key::cacheKeyPressed = {}; -std::array Key::cacheKeyDown = {}; -std::array Key::claimedDirectionalInput = {}; - -namespace -{ -constexpr std::array directionalKeys = { - KeyCode::LeftArrow, - KeyCode::RightArrow, - KeyCode::UpArrow, - KeyCode::DownArrow, -}; - -int CurrentInputFrame() -{ - static auto GetFrameCount = il2cpp::ResolveICall("UnityEngine.Time::get_frameCount()"); - return GetFrameCount(); -} -} - -const std::unordered_map Key::mappedKeys = { - {"LALT", KeyCode::LeftAlt}, - {"LAPPLE", KeyCode::LeftApple}, - {"LCOM", KeyCode::LeftCommand}, - {"LCTRL", KeyCode::LeftControl}, - {"ALTGR", KeyCode::AltGr}, - {"END", KeyCode::End}, - {"HOME", KeyCode::Home}, - {"PGDOWN", KeyCode::PageDown}, - {"PGUP", KeyCode::PageUp}, - {"DOWN", KeyCode::DownArrow}, - {"LEFT", KeyCode::LeftArrow}, - {"RIGHT", KeyCode::RightArrow}, - {"UP", KeyCode::UpArrow}, - {"BACKSPACE", KeyCode::Backspace}, - {"BREAK", KeyCode::Break}, - {"CAPS", KeyCode::CapsLock}, - {"CLEAR", KeyCode::Clear}, - {"DELETE", KeyCode::Delete}, - {"ESCAPE", KeyCode::Escape}, - {"HELP", KeyCode::Help}, - {"INSERT", KeyCode::Insert}, - {"LSHIFT", KeyCode::LeftShift}, - {"LWIN", KeyCode::LeftWindows}, - {"MENU", KeyCode::Menu}, - {"PAUSE", KeyCode::Pause}, - {"PRINT", KeyCode::Print}, - {"RALT", KeyCode::RightAlt}, - {"RAPPLE", KeyCode::RightApple}, - {"RCOM", KeyCode::RightCommand}, - {"RCTRL", KeyCode::RightControl}, - {"RETURN", KeyCode::Return}, - {"RSHIFT", KeyCode::RightShift}, - {"RWIN", KeyCode::RightWindows}, - {"SCROLL", KeyCode::ScrollLock}, - {"SYSREQ", KeyCode::SysReq}, - {"TAB", KeyCode::Tab}, - {"MOUSE0", KeyCode::Mouse0}, - {"MOUSE1", KeyCode::Mouse1}, - {"MOUSE2", KeyCode::Mouse2}, - {"MOUSE3", KeyCode::Mouse3}, - {"MOUSE4", KeyCode::Mouse4}, - {"MOUSE5", KeyCode::Mouse5}, - {"MOUSE6", KeyCode::Mouse6}, - {"SPACE", KeyCode::Space}, - {"MINUS", KeyCode::Minus}, - {"_", KeyCode::Underscore}, - {",", KeyCode::Comma}, - {";", KeyCode::Semicolon}, - {":", KeyCode::Colon}, - {"!", KeyCode::Exclaim}, - {"?", KeyCode::Question}, - {".", KeyCode::Period}, - {"'", KeyCode::Quote}, - {"(", KeyCode::LeftParen}, - {")", KeyCode::RightParen}, - {"[", KeyCode::LeftBracket}, - {"]", KeyCode::RightBracket}, - {"{", KeyCode::LeftCurlyBracket}, - {"}", KeyCode::RightCurlyBracket}, - {"@", KeyCode::At}, - {"*", KeyCode::Asterisk}, - {"/", KeyCode::Slash}, - {"\"", KeyCode::DoubleQuote}, - {"\\", KeyCode::Backslash}, - {"&", KeyCode::Ampersand}, - {"#", KeyCode::Hash}, - {"%", KeyCode::Percent}, - {"`", KeyCode::BackQuote}, - {"^", KeyCode::Caret}, - {"+", KeyCode::Plus}, - {"<", KeyCode::Less}, - {"=", KeyCode::Equals}, - {">", KeyCode::Greater}, - {"|", KeyCode::Pipe}, - {"~", KeyCode::Tilde}, - {"$", KeyCode::Dollar}, - {"0", KeyCode::Alpha0}, - {"1", KeyCode::Alpha1}, - {"2", KeyCode::Alpha2}, - {"3", KeyCode::Alpha3}, - {"4", KeyCode::Alpha4}, - {"5", KeyCode::Alpha5}, - {"6", KeyCode::Alpha6}, - {"7", KeyCode::Alpha7}, - {"8", KeyCode::Alpha8}, - {"9", KeyCode::Alpha9}, - {"A", KeyCode::A}, - {"B", KeyCode::B}, - {"C", KeyCode::C}, - {"D", KeyCode::D}, - {"E", KeyCode::E}, - {"F", KeyCode::F}, - {"F1", KeyCode::F1}, - {"F10", KeyCode::F10}, - {"F11", KeyCode::F11}, - {"F12", KeyCode::F12}, - {"F13", KeyCode::F13}, - {"F14", KeyCode::F14}, - {"F15", KeyCode::F15}, - {"F2", KeyCode::F2}, - {"F3", KeyCode::F3}, - {"F4", KeyCode::F4}, - {"F5", KeyCode::F5}, - {"F6", KeyCode::F6}, - {"F7", KeyCode::F7}, - {"F8", KeyCode::F8}, - {"F9", KeyCode::F9}, - {"G", KeyCode::G}, - {"H", KeyCode::H}, - {"I", KeyCode::I}, - {"J", KeyCode::J}, - {"K", KeyCode::K}, - {"L", KeyCode::L}, - {"M", KeyCode::M}, - {"N", KeyCode::N}, - {"O", KeyCode::O}, - {"P", KeyCode::P}, - {"Q", KeyCode::Q}, - {"R", KeyCode::R}, - {"S", KeyCode::S}, - {"T", KeyCode::T}, - {"U", KeyCode::U}, - {"V", KeyCode::V}, - {"W", KeyCode::W}, - {"X", KeyCode::X}, - {"Y", KeyCode::Y}, - {"Z", KeyCode::Z}, - {"NUMLOCK", KeyCode::Numlock}, - {"KEY0", KeyCode::Keypad0}, - {"KEY1", KeyCode::Keypad1}, - {"KEY2", KeyCode::Keypad2}, - {"KEY3", KeyCode::Keypad3}, - {"KEY4", KeyCode::Keypad4}, - {"KEY5", KeyCode::Keypad5}, - {"KEY6", KeyCode::Keypad6}, - {"KEY7", KeyCode::Keypad7}, - {"KEY8", KeyCode::Keypad8}, - {"KEY9", KeyCode::Keypad9}, - {"KEYDIVIDE", KeyCode::KeypadDivide}, - {"KEYENTER", KeyCode::KeypadEnter}, - {"KEYEQUAL", KeyCode::KeypadEquals}, - {"KEYMINUS", KeyCode::KeypadMinus}, - {"KEYMULTI", KeyCode::KeypadMultiply}, - {"KEYPERIOD", KeyCode::KeypadPeriod}, - {"KEYPLUS", KeyCode::KeypadPlus}, -}; - -KeyCode Key::Parse(std::string_view key) -{ - auto wantedKey = AsciiStrToUpper(key); - for (const auto& [value, keycode] : mappedKeys) { - if (wantedKey == value) { - return (KeyCode)keycode; - } - } - - return KeyCode::None; -} - -bool Key::IsModifier(KeyCode key) -{ - switch (key) { - case KeyCode::LeftAlt: - case KeyCode::LeftCommand: - case KeyCode::LeftControl: - case KeyCode::AltGr: - case KeyCode::LeftShift: - case KeyCode::LeftWindows: - case KeyCode::RightAlt: - case KeyCode::RightCommand: - case KeyCode::RightControl: - case KeyCode::RightShift: - case KeyCode::RightWindows: - return true; - default: - return false; - } -} - -void Key::ClaimDirectionalInput(KeyCode key) -{ - switch (key) { - case KeyCode::LeftArrow: - case KeyCode::RightArrow: - case KeyCode::UpArrow: - case KeyCode::DownArrow: - claimedDirectionalInput[(int)key] = true; - break; - default: - break; - } -} - -bool Key::IsDirectionalInputClaimed() -{ - EnsureCurrentFrame(); - - auto claimed = false; - for (const auto key : directionalKeys) { - auto& directionClaimed = claimedDirectionalInput[(int)key]; - if (!directionClaimed) { - continue; - } - - if (Pressed(key)) { - claimed = true; - } else { - directionClaimed = false; - } - } - - return claimed; -} - -bool Key::IsModified() -{ - EnsureCurrentFrame(); - - if (cacheInputModified == 0) { - cacheInputModified = -1; - if (Key::Pressed(KeyCode::LeftAlt) || Key::Pressed(KeyCode::LeftControl) || Key::Pressed(KeyCode::LeftShift) - || Key::Pressed(KeyCode::RightAlt) || Key::Pressed(KeyCode::RightControl) || Key::Pressed(KeyCode::RightShift) - || Key::Pressed(KeyCode::AltGr) || Key::Pressed(KeyCode::LeftCommand) || Key::Pressed(KeyCode::LeftWindows) - || Key::Pressed(KeyCode::RightCommand) || Key::Pressed(KeyCode::RightWindows)) { - cacheInputModified = 1; - } - } - - return cacheInputModified == 1; -} - -void Key::ResetCache() -{ - Key::cacheFrame = CurrentInputFrame(); - Key::cacheInputFocused = 0; - Key::cacheInputModified = 0; - for (int i = 0; i < (int)KeyCode::Max; i++) { - Key::cacheKeyDown[i] = 0; - Key::cacheKeyPressed[i] = 0; - } - - // Direction claims span frames while their key is held so one-shot chords cannot leak into native movement. - // Prune them here as well as at the consumer so claims cannot survive a scene without NavigationPan. - for (const auto key : directionalKeys) { - auto& directionClaimed = claimedDirectionalInput[(int)key]; - if (directionClaimed && !Pressed(key)) { - directionClaimed = false; - } - } -} - -void Key::EnsureCurrentFrame() -{ - if (cacheFrame != CurrentInputFrame()) { - ResetCache(); - } -} - -bool Key::Down(KeyCode key) -{ - EnsureCurrentFrame(); - - static auto GetKeyDownInt = - il2cpp::ResolveICall("UnityEngine.Input::GetKeyDownInt(UnityEngine.KeyCode)"); - - if (cacheKeyDown[(int)key] == 0) { - cacheKeyDown[(int)key] = GetKeyDownInt(key) ? 1 : -1; - } - - return cacheKeyDown[(int)key] == 1; -} - -bool Key::Pressed(KeyCode key) -{ - EnsureCurrentFrame(); - - static auto GetKeyInt = il2cpp::ResolveICall("UnityEngine.Input::GetKeyInt(UnityEngine.KeyCode)"); - - if (cacheKeyPressed[(int)key] == 0) { - cacheKeyPressed[(int)key] = GetKeyInt(key) ? 1 : -1; - } - - return cacheKeyPressed[(int)key] == 1; -} - -bool Key::IsInputFocused() -{ - EnsureCurrentFrame(); - - if (cacheInputFocused == 0) { - cacheInputFocused = -1; - - auto eventSystem = EventSystem::current(); - if (eventSystem) { - auto n = eventSystem->currentSelectedGameObject(); - if (n) { - auto n2 = n->GetComponent(); - if (n2 && n2->isFocused()) { - cacheInputFocused = 1; - } - } - } - } - - return cacheInputFocused == 1; -} - -bool Key::HasShift() -{ - return Key::Pressed(KeyCode::LeftShift) || Key::Pressed(KeyCode::RightShift); -} - -bool Key::HasAlt() -{ - return Key::Pressed(KeyCode::LeftAlt) || Key::Pressed(KeyCode::RightAlt); -} - -bool Key::HasCtrl() -{ - return Key::Pressed(KeyCode::LeftControl) || Key::Pressed(KeyCode::RightControl); -} - -void Key::ClearInputFocus() -{ - if (auto eventSystem = EventSystem::current(); eventSystem) { - if (auto n = eventSystem->currentSelectedGameObject(); n) { - auto n2 = n->GetComponent(); - if (n2 && n2->isFocused()) { - eventSystem->SetSelectedGameObject(nullptr); - } - } - } -} diff --git a/mods/src/patches/key.h b/mods/src/patches/key.h deleted file mode 100644 index 81211c2e0..000000000 --- a/mods/src/patches/key.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -class Key -{ -private: - static const std::unordered_map mappedKeys; - - static int cacheInputFocused; - static int cacheInputModified; - static int cacheFrame; - - static std::array cacheKeyPressed; - static std::array cacheKeyDown; - static std::array claimedDirectionalInput; - - static void EnsureCurrentFrame(); - -public: - static void ClearInputFocus(); - static void ResetCache(); - static KeyCode Parse(std::string_view key); - - static bool Pressed(KeyCode key); - static bool Down(KeyCode key); - - static void ClaimDirectionalInput(KeyCode key); - static bool IsDirectionalInputClaimed(); - - static bool IsModifier(KeyCode key); - - static bool IsModified(); - static bool IsInputFocused(); - static bool HasShift(); - static bool HasAlt(); - static bool HasCtrl(); -}; diff --git a/mods/src/patches/mapkey.cc b/mods/src/patches/mapkey.cc deleted file mode 100644 index a3234eeba..000000000 --- a/mods/src/patches/mapkey.cc +++ /dev/null @@ -1,291 +0,0 @@ -#include "mapkey.h" -#include "gamefunctions.h" -#include "key.h" -#include "modifierkey.h" -#include "str_utils.h" -#include - -#include -#include -#include -#include -#include -#include - -namespace -{ -struct CompactShortcutTokenMapping { - std::string_view token; - std::string_view compact; - bool primary_key_only = false; -}; - -// Use AutoHotkey's established ASCII modifier notation so the native game's limited font can render every badge. -constexpr auto kCompactShortcutTokenMappings = std::to_array({ - {"SHIFT", "+"}, - {"CTRL", "^"}, - {"ALT", "!"}, - {"ALTGR", "!"}, - {"APPLE", "#"}, - {"CMD", "#"}, - {"WIN", "#"}, - {"LSHIFT", "<+"}, - {"RSHIFT", ">+"}, - {"LCTRL", "<^"}, - {"RCTRL", ">^"}, - {"LALT", "!"}, - {"LAPPLE", "<#"}, - {"LCOM", "<#"}, - {"LWIN", "<#"}, - {"RAPPLE", ">#"}, - {"RCOM", ">#"}, - {"RWIN", ">#"}, - {"+", "PLS", true}, - {"^", "CAR", true}, - {"!", "EXC", true}, - {"#", "HSH", true}, - {"SPACE", "SPC"}, - {"MOUSE0", "M0"}, - {"MOUSE1", "M1"}, - {"MOUSE2", "M2"}, - {"MOUSE3", "M3"}, - {"MOUSE4", "M4"}, - {"MOUSE5", "M5"}, - {"MOUSE6", "M6"}, - {"ENTER", "ENT"}, - {"RETURN", "ENT"}, - {"ESCAPE", "ESC"}, - {"TAB", "TAB"}, - {"BACKSPACE", "BS"}, - {"DELETE", "DEL"}, - {"MINUS", "-"}, - {"EQUAL", "="}, - {"LEFT", "LT"}, - {"RIGHT", "RT"}, - {"UP", "UP"}, - {"DOWN", "DN"}, - {"PGUP", "PU"}, - {"PGDOWN", "PD"}, - {"HOME", "HM"}, - {"END", "END"}, -}); - -consteval bool CompactShortcutTokensAreUnique() -{ - for (size_t index = 0; index < kCompactShortcutTokenMappings.size(); ++index) { - for (size_t other = index + 1; other < kCompactShortcutTokenMappings.size(); ++other) { - if (kCompactShortcutTokenMappings[index].token == kCompactShortcutTokenMappings[other].token) { - return false; - } - } - } - return true; -} - -static_assert(CompactShortcutTokensAreUnique()); - -constexpr std::string_view CompactShortcutToken(std::string_view token, bool is_primary_key) -{ - for (const auto& mapping : kCompactShortcutTokenMappings) { - if (mapping.token == token && (!mapping.primary_key_only || is_primary_key)) { - return mapping.compact; - } - } - return token; -} - -static_assert(CompactShortcutToken("CTRL", false) == "^"); -static_assert(CompactShortcutToken("ALTGR", false) == "!"); -static_assert(CompactShortcutToken("+", true) == "PLS"); -static_assert(CompactShortcutToken("+", false) == "+"); -static_assert(CompactShortcutToken("F7", true) == "F7"); - -std::string CompactShortcutForHint(const std::vector& shortcuts) -{ - std::string compact; - for (size_t index = 0; index < shortcuts.size(); ++index) { - compact.append(CompactShortcutToken(shortcuts[index], index + 1 == shortcuts.size())); - } - return compact; -} -} // namespace - -MapKey::MapKey() -{ - this->Key = KeyCode::None; - this->hasModifiers = false; -} - -MapKey MapKey::Parse(std::string_view key) -{ - auto strippedKey = StripTrailingAsciiWhitespace(key); - auto lowerKey = AsciiStrToUpper(strippedKey); - auto wantedKeys = StrSplit(lowerKey, '-'); - - MapKey mapKey; - for (std::string_view wantedKey : wantedKeys) { - auto modifier = ModifierKey::Parse(wantedKey); - if (modifier.HasModifiers()) { - mapKey.hasModifiers = true; - mapKey.Modifiers.emplace_back(std::move(modifier)); - mapKey.Shortcuts.emplace_back(wantedKey); - } else { - auto parsedKey = Key::Parse(wantedKey); - - if (Key::IsModifier(parsedKey)) { - continue; - } - - if (parsedKey != KeyCode::None) { - mapKey.Key = parsedKey; - mapKey.Shortcuts.emplace_back(wantedKey); - } - } - -#ifndef NDEBUG - if (mapKey.Key == KeyCode::X) { - std::cout << "\n\n----------\nX key:\n" << mapKey.GetParsedValues() << "\n----------\n\n"; - } -#endif - } - - return mapKey; -} - -std::string MapKey::GetShortcuts(GameFunction gameFunction) -{ - const auto &mapKeys = MapKey::mappedKeys[gameFunction]; - - bool appendPipe = false; - - std::string shortcuts = ""; - for (const MapKey &mapKey : mapKeys) { - if (appendPipe) { - shortcuts.append(" | "); - } - shortcuts.append(mapKey.GetParsedValues()); - appendPipe = true; - } - - return shortcuts; -} - -std::string MapKey::GetShortcutHint(GameFunction gameFunction) -{ - const auto& mapKeys = MapKey::mappedKeys[gameFunction]; - return mapKeys.empty() ? "" : mapKeys.front().shortcutHint; -} - -void MapKey::CacheShortcutHints() -{ - // Native badges display only the first binding for each action. - for (auto& mapKeys : mappedKeys) { - if (!mapKeys.empty()) { - mapKeys.front().shortcutHint = CompactShortcutForHint(mapKeys.front().Shortcuts); - } - } -} - -bool MapKey::HasBinding(GameFunction gameFunction) -{ - for (const auto& mapKey : mappedKeys[gameFunction]) { - if (mapKey.Key != KeyCode::None) { - return true; - } - } - return false; -} - -void MapKey::AddMappedKey(GameFunction gameFunction, MapKey mappedKey) -{ - MapKey::mappedKeys[gameFunction].emplace_back(std::move(mappedKey)); -} - -// Movement reads the direction itself; claiming it would suppress its own pan -// on the next frame. Other modified actions retain ownership until key release. -static bool IsMovementAction(GameFunction action) -{ - return action == GameFunction::MoveLeft || action == GameFunction::MoveRight - || action == GameFunction::MoveUp || action == GameFunction::MoveDown; -} - -bool MapKey::IsPressed(GameFunction gameFunction) -{ - const auto &mapKeys = MapKey::mappedKeys[(int)gameFunction]; - for (const MapKey &mapKey : mapKeys) { - if (mapKey.Key != KeyCode::None) { - if (Key::Pressed(mapKey.Key)) { - if (MapKey::HasCorrectModifiers(mapKey)) { - if (mapKey.hasModifiers && !IsMovementAction(gameFunction)) { - Key::ClaimDirectionalInput(mapKey.Key); - } - return true; - } - } - } - } - - return false; -} - -bool MapKey::IsDown(GameFunction gameFunction) -{ - const auto &mapKeys = MapKey::mappedKeys[(int)gameFunction]; - for (const MapKey &mapKey : mapKeys) { - if (mapKey.Key != KeyCode::None) { - if (Key::Down(mapKey.Key)) { - if (MapKey::HasCorrectModifiers(mapKey)) { - if (mapKey.hasModifiers && !IsMovementAction(gameFunction)) { - Key::ClaimDirectionalInput(mapKey.Key); - } - return true; - } - } - } - } - - return false; -} - -bool MapKey::HasCorrectModifiers(const MapKey& mapKey) -{ - auto result = false; - std::string section = "non set"; - if (!mapKey.hasModifiers) { - section = "no modifiers"; - result = !Key::IsModified(); - } else { - result = true; - for (const ModifierKey& modifier : mapKey.Modifiers) { - if (!modifier.IsPressed()) { - section = modifier.GetParsedValues(); - result = false; - break; - } - } - } - -#ifndef NDEBUG - if (mapKey.Key == KeyCode::Backslash && Key::Pressed(KeyCode::Backslash)) { - std::cout << "HasCorrectModifiers(" << mapKey.GetParsedValues() << "): [" << section << "] " << result << "\n"; - } -#endif - - return result; -} - -std::string MapKey::GetParsedValues() const -{ - std::string output = ""; - for (const std::string_view key : this->Shortcuts) { - if (output.length()) { - output.append("-"); - } - output.append(key); - } - - return output; -} - -std::array, (int)GameFunction::Max> MapKey::mappedKeys = {}; diff --git a/mods/src/patches/mapkey.h b/mods/src/patches/mapkey.h deleted file mode 100644 index b5d73bedf..000000000 --- a/mods/src/patches/mapkey.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "modifierkey.h" -#include -#include - -#include -#include -#include -#include - -class MapKey -{ -public: - MapKey(); - - static MapKey Parse(std::string_view key); - static void AddMappedKey(GameFunction gameFunction, MapKey mappedKey); - static bool HasBinding(GameFunction gameFunction); - static bool IsPressed(GameFunction gameFunction); - static bool IsDown(GameFunction gameFunction); - static bool HasCorrectModifiers(const MapKey& mapKey); - - static std::string GetShortcuts(GameFunction gameFunction); - static std::string GetShortcutHint(GameFunction gameFunction); - // Call once after config parsing, only when shortcut hints are enabled. - static void CacheShortcutHints(); - - std::string GetParsedValues() const; - - std::vector Modifiers; - std::vector Shortcuts; - - KeyCode Key; - -private: - static std::array, (int)GameFunction::Max> mappedKeys; - - bool hasModifiers; - std::string shortcutHint; -}; diff --git a/mods/src/patches/modifierkey.cc b/mods/src/patches/modifierkey.cc deleted file mode 100644 index d9205193e..000000000 --- a/mods/src/patches/modifierkey.cc +++ /dev/null @@ -1,115 +0,0 @@ -#include "config.h" -#include "str_utils.h" -#include "key.h" -#include "modifierkey.h" - -#include -#include -#include - -bool ModifierKey::HasModifiers() const -{ - return this->hasModifier; -} - -ModifierKey::ModifierKey() -{ - this->hasModifier = false; -} - -ModifierKey ModifierKey::Parse(std::string_view key) -{ - auto strippedKey = StripTrailingAsciiWhitespace(key); - auto upperKey = AsciiStrToUpper(strippedKey); - auto wantedKeys = StrSplit(upperKey, '-'); - - ModifierKey modifierKey; - for (std::string_view wantedKey : wantedKeys) { - if (wantedKey == "SHIFT") { - modifierKey.AddModifier(wantedKey, KeyCode::LeftShift, KeyCode::RightShift); - } else if (wantedKey == "ALT") { - modifierKey.AddModifier(wantedKey, KeyCode::LeftAlt, KeyCode::RightAlt); - } else if (wantedKey == "CTRL") { - modifierKey.AddModifier(wantedKey, KeyCode::LeftControl, KeyCode::RightControl); - } else if (wantedKey == "APPLE") { - modifierKey.AddModifier(wantedKey, KeyCode::LeftApple, KeyCode::RightApple); - } else if (wantedKey == "CMD") { - modifierKey.AddModifier(wantedKey, KeyCode::LeftCommand, KeyCode::RightCommand); - } else if (wantedKey == "WIN") { - modifierKey.AddModifier(wantedKey, KeyCode::LeftWindows, KeyCode::RightWindows); - } else { - auto parsedKey = Key::Parse(wantedKey); - - if (Key::IsModifier(parsedKey)) { - modifierKey.AddModifier(wantedKey, parsedKey, KeyCode::None); - } - } - } - - return modifierKey; -} - -bool ModifierKey::Contains(KeyCode modifier) const -{ - if (this->hasModifier) { - return (std::find(this->Modifiers.begin(), this->Modifiers.end(), modifier) != this->Modifiers.end()); - } - - return false; -} - -void ModifierKey::AddModifier(std::string_view shortcut, KeyCode modifier1, KeyCode modifier2) -{ - if (!this->Contains(modifier1)) { - this->hasModifier = true; - this->Modifiers.emplace_back(modifier1); - this->Shortcuts.emplace_back(shortcut); - } - - if (!this->Contains(modifier2)) { - this->hasModifier = true; - this->Modifiers.emplace_back(modifier2); - this->Shortcuts.emplace_back(shortcut); - } -} - -bool ModifierKey::IsPressed() const -{ - if (this->hasModifier) { - for (auto modifier : this->Modifiers) { - if (Key::Pressed(modifier)) { - return true; - } - } - } - - return false; -} - -bool ModifierKey::IsDown() const -{ - if (this->hasModifier) { - for (auto modifier : this->Modifiers) { - if (Key::Down(modifier)) { - return true; - } - } - } - - return false; -} - -std::string ModifierKey::GetParsedValues() const -{ - std::string output = ""; - if (this->hasModifier) { - for (const std::string_view key : this->Shortcuts) { - if (output.length()) { - output.append("-"); - } - output.append(key); - } - } - - return output; -} diff --git a/mods/src/patches/modifierkey.h b/mods/src/patches/modifierkey.h deleted file mode 100644 index a74587bca..000000000 --- a/mods/src/patches/modifierkey.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - - -#include -#include - -#include "key.h" - -#include -#include - -struct ModifierKey -{ -public: - ModifierKey(); - - static ModifierKey Parse(std::string_view key); - - void AddModifier(std::string_view shortcut, KeyCode modifier1, KeyCode modifier2); - bool Contains(KeyCode modifier) const; - bool IsPressed() const; - bool IsDown() const; - bool HasModifiers() const; - - std::string GetParsedValues() const; - -private: - std::vector Modifiers; - std::vector Shortcuts; - bool hasModifier; -}; diff --git a/mods/src/patches/parts/alliance_store.cc b/mods/src/patches/parts/alliance_store.cc index 2de77a391..b85baad3f 100644 --- a/mods/src/patches/parts/alliance_store.cc +++ b/mods/src/patches/parts/alliance_store.cc @@ -1,7 +1,7 @@ // Alliance Games store: native material research tooltips and currency refresh // after bundle readiness. One feature owner, no claim-trace or file probe. #include "patches/native_hook_extent.h" -#include "patches/screen_update_hook.h" +#include "patches/patches.h" #include #include @@ -618,7 +618,30 @@ void BundleDataWidget_OnDidBindContext_Hook(auto original, BundleDataWidget* sel std::string StoreCount(const auto& value) { return value ? std::to_string(*value) : "unavailable"; } -void AllianceStoreFrame() +bool StoreHookFits(const MethodInfo* method) +{ + if (!method || !method->methodPointer) + return false; + auto* entry = reinterpret_cast(method->methodPointer); +#if _WIN32 && defined(_M_X64) + DWORD64 base = 0; + auto* function = RtlLookupFunctionEntry(reinterpret_cast(entry), &base, nullptr); + if (!function || base + function->BeginAddress != reinterpret_cast(entry) + || function->EndAddress <= function->BeginAddress) + return false; + const size_t extent = function->EndAddress - function->BeginAddress; + const auto plan = spud::inspect_detour(entry, spud::Arch::kHost, extent); + return plan.ok() && plan.resolved_target == reinterpret_cast(entry) && plan.far_jump_size != 0 + && plan.far_required_patch_bytes >= plan.far_jump_size && plan.far_required_patch_bytes <= extent; +#elif __APPLE__ + return native_hooks::MacHookFits(entry); +#else + return false; +#endif +} +} // namespace + +void AllianceGamesStoreUpdate() { if (!StoreThread()) return; @@ -721,29 +744,6 @@ void AllianceStoreFrame() StoreCount(currency_after), StoreCount(visible), StoreCount(position)); } -bool StoreHookFits(const MethodInfo* method) -{ - if (!method || !method->methodPointer) - return false; - auto* entry = reinterpret_cast(method->methodPointer); -#if _WIN32 && defined(_M_X64) - DWORD64 base = 0; - auto* function = RtlLookupFunctionEntry(reinterpret_cast(entry), &base, nullptr); - if (!function || base + function->BeginAddress != reinterpret_cast(entry) - || function->EndAddress <= function->BeginAddress) - return false; - const size_t extent = function->EndAddress - function->BeginAddress; - const auto plan = spud::inspect_detour(entry, spud::Arch::kHost, extent); - return plan.ok() && plan.resolved_target == reinterpret_cast(entry) && plan.far_jump_size != 0 - && plan.far_required_patch_bytes >= plan.far_jump_size && plan.far_required_patch_bytes <= extent; -#elif __APPLE__ - return native_hooks::MacHookFits(entry); -#else - return false; -#endif -} -} // namespace - void InstallAllianceGamesStoreHooks() { static bool attempted = false; @@ -774,7 +774,7 @@ void InstallAllianceGamesStoreHooks() return; } } - if (!install_screen_manager_update_hook() || !register_screen_manager_update_callback(AllianceStoreFrame)) { + if (!InstallScreenManagerUpdateHook()) { spdlog::warn("[AllianceGamesStore] unavailable: shared frame delivery"); return; } diff --git a/mods/src/patches/parts/chat.cc b/mods/src/patches/parts/chat.cc index 9f38abd8d..5d527f590 100644 --- a/mods/src/patches/parts/chat.cc +++ b/mods/src/patches/parts/chat.cc @@ -10,7 +10,7 @@ #include #include #include "patches/chat_startup.h" -#include "patches/screen_update_hook.h" +#include "patches/patches.h" #include "prime/Hub.h" namespace @@ -19,6 +19,7 @@ il2cpp::GcHandle preview_handle; ChatStartup::Gate startup; bool width_applied = false; bool startup_failed = false; +bool options_installed = false; void ObserveChatPreview(ChatPreviewController* preview) { @@ -101,6 +102,12 @@ void UpdateChatOptions() } } // namespace +void ChatOptionsUpdate() +{ + if (options_installed) + UpdateChatOptions(); +} + auto GetChatTabIndices() { if (const auto chat_manager = ChatManager::Instance(); chat_manager) { @@ -232,7 +239,8 @@ void ChatPreviewController_OnRegionalMessageReceived(auto original, ChatPreviewC void InstallChatPatches() { - if (install_screen_manager_update_hook() && register_screen_manager_update_callback(UpdateChatOptions)) + options_installed = InstallScreenManagerUpdateHook(); + if (options_installed) spdlog::info("[ChatOptions] lifecycle v2 registered: auto_open={} width={}", Config::Get().auto_open_chat, Config::Get().default_chat_width); if (auto m = FullScreenChatViewController::Class::Method("AboutToShow")) diff --git a/mods/src/patches/parts/daily_faction_bulk_claim.cc b/mods/src/patches/parts/daily_faction_bulk_claim.cc index 623335309..5cc831a86 100644 --- a/mods/src/patches/parts/daily_faction_bulk_claim.cc +++ b/mods/src/patches/parts/daily_faction_bulk_claim.cc @@ -1,4 +1,5 @@ #include "config.h" +#include "patches/patches.h" #include "str_utils.h" #include @@ -284,4 +285,6 @@ void InstallDailyFactionBulkClaimHooks() SPUD_STATIC_DETOUR(m.Pointer(), MissionViewController_AboutToShow); if (auto m = Controller::Method("AboutToHide", 0)) SPUD_STATIC_DETOUR(m.Pointer(), MissionViewController_AboutToHide); + if (Config::Get().daily_bulk_claim_toggle_default_on) + InstallScreenManagerUpdateHook(); } diff --git a/mods/src/patches/parts/daily_faction_bulk_claim.h b/mods/src/patches/parts/daily_faction_bulk_claim.h deleted file mode 100644 index 46f646b60..000000000 --- a/mods/src/patches/parts/daily_faction_bulk_claim.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -void DailyFactionBulkClaimUpdate(); - -void InstallDailyFactionBulkClaimHooks(); diff --git a/mods/src/patches/parts/double_click_assign_ship.cc b/mods/src/patches/parts/double_click_assign_ship.cc index 0ddaac71d..3f7096a07 100644 --- a/mods/src/patches/parts/double_click_assign_ship.cc +++ b/mods/src/patches/parts/double_click_assign_ship.cc @@ -1,5 +1,6 @@ #include "config.h" -#include "patches/key.h" +#include "patches/patches.h" +#include "patches/input.h" #include "patches/object_tracker.h" #include "double_click_assign_ship.h" diff --git a/mods/src/patches/parts/fix_pan.cc b/mods/src/patches/parts/fix_pan.cc index 54b98ab7d..c715af0d1 100644 --- a/mods/src/patches/parts/fix_pan.cc +++ b/mods/src/patches/parts/fix_pan.cc @@ -7,8 +7,8 @@ #include #include -#include -#include +#include "patches/input.h" +#include "patches/shortcuts.h" #include #include @@ -64,8 +64,8 @@ bool NavigationPan_LateUpdate_Hook(auto original, NavigationPan* _this) native_handled = true; } else if (!_this->BlockPan() && !_this->trackingPOI() && !dragging && KeyboardPanAllowed()) { // Opposite bindings cancel; normalize diagonals like the native 2D composite. - float x = float(MapKey::IsPressed(GameFunction::MoveRight)) - float(MapKey::IsPressed(GameFunction::MoveLeft)); - float y = float(MapKey::IsPressed(GameFunction::MoveUp)) - float(MapKey::IsPressed(GameFunction::MoveDown)); + float x = float(Shortcut::IsPressed(GameFunction::MoveRight)) - float(Shortcut::IsPressed(GameFunction::MoveLeft)); + float y = float(Shortcut::IsPressed(GameFunction::MoveUp)) - float(Shortcut::IsPressed(GameFunction::MoveDown)); const float length = std::sqrt(x * x + y * y); if (length > 0.0f) { d.x = -x / length * _this->keyboardDeltaScaler(); @@ -107,10 +107,10 @@ void OrbitFrameProvider_UpdateInputData_Hook(auto original, OrbitFrameProvider* constexpr float keyboard_rotation_speed = 45.0f; const auto frame_delta = keyboard_rotation_speed * GetDeltaTime(); - if (MapKey::IsPressed(GameFunction::MoveLeft)) { + if (Shortcut::IsPressed(GameFunction::MoveLeft)) { _this->SetRotationAngleDelta(_this->RotationAngleDelta() - frame_delta); } - if (MapKey::IsPressed(GameFunction::MoveRight)) { + if (Shortcut::IsPressed(GameFunction::MoveRight)) { _this->SetRotationAngleDelta(_this->RotationAngleDelta() + frame_delta); } } diff --git a/mods/src/patches/parts/focus_search.cc b/mods/src/patches/parts/focus_search.cc deleted file mode 100644 index e69428f35..000000000 --- a/mods/src/patches/parts/focus_search.cc +++ /dev/null @@ -1,96 +0,0 @@ -#include "focus_search.h" -#include "patches/object_tracker.h" - -#include "config.h" - -#include "prime/fleet.h" -#include "prime/ui.h" -#include "prime/chat.h" -#include "prime/inventory.h" - -#include - -#include - -bool FocusSearchBox() -{ - if (!Config::Get().focus_search) { - return false; - } - - // Guard chat input so we don't steal focus from the chat text box - if (auto chat = ObjectFinder::Get(); chat && chat->inputFieldSelected()) { -#ifdef _MODDBG - spdlog::info("[FocusSearch] blocked: chat input is selected"); -#endif - return false; - } - - // InventoryListViewController covers OfficerRosterViewController and other inventory subclasses - auto inventoryControllers = ObjectFinder::GetAll(); - for (auto controller : inventoryControllers) { - if (!controller || !controller->inputField()) { - continue; - } - - auto canvas = controller->canvasController(); - bool visible = canvas && canvas->Visible(); - bool active = controller->isActiveAndEnabled() && controller->inputField()->isActiveAndEnabled(); - spdlog::debug("[FocusSearch] inventory controller={} section={} canvas={} visible={} active={}", (void*)controller, - (int32_t)controller->targetSection(), (void*)canvas, visible, active); - - if (visible && active) { -#ifdef _MODDBG - spdlog::info("[FocusSearch] focusing visible inventory search"); -#endif - controller->inputField()->Focus(); - return true; - } - } - - // OfficerAssignmentViewController (separate from inventory list) - auto officerControllers = ObjectFinder::GetAll(); - for (auto controller : officerControllers) { - if (!controller || !controller->inputField()) { - continue; - } - - auto canvas = controller->canvasController(); - bool visible = canvas && canvas->Visible(); - bool active = controller->isActiveAndEnabled() && controller->inputField()->isActiveAndEnabled(); - spdlog::debug("[FocusSearch] officer assignment controller={} canvas={} visible={} active={}", (void*)controller, - (void*)canvas, visible, active); - - if (visible && active) { -#ifdef _MODDBG - spdlog::info("[FocusSearch] focusing visible officer assignment search"); -#endif - controller->inputField()->Focus(); - return true; - } - } - - // AssignShipsWidget (ship selection / assignment) - auto assignShipWidgets = ObjectFinder::GetAll(); - for (auto widget : assignShipWidgets) { - if (!widget || !widget->inputField()) { - continue; - } - - auto canvas = GetCanvasControllerFromComponent(widget); - bool visible = canvas && canvas->Visible(); - bool active = widget->isActiveAndEnabled() && widget->inputField()->isActiveAndEnabled(); - spdlog::debug("[FocusSearch] assign ships widget={} canvas={} visible={} active={}", (void*)widget, (void*)canvas, - visible, active); - - if (visible && active) { -#ifdef _MODDBG - spdlog::info("[FocusSearch] focusing visible assign ships search"); -#endif - widget->inputField()->Focus(); - return true; - } - } - - return false; -} diff --git a/mods/src/patches/parts/focus_search.h b/mods/src/patches/parts/focus_search.h deleted file mode 100644 index a1744e32f..000000000 --- a/mods/src/patches/parts/focus_search.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -bool FocusSearchBox(); diff --git a/mods/src/patches/parts/galactic_anomaly_timer.cc b/mods/src/patches/parts/galactic_anomaly_timer.cc index d759f7fd2..b06af437a 100644 --- a/mods/src/patches/parts/galactic_anomaly_timer.cc +++ b/mods/src/patches/parts/galactic_anomaly_timer.cc @@ -1,5 +1,5 @@ #include "config.h" -#include "patches/screen_update_hook.h" +#include "patches/patches.h" #include #include @@ -13,7 +13,8 @@ namespace { -// All Unity work runs on ScreenManager's main-thread dispatcher. No additional detours. +// All Unity work runs on the ScreenManager.Update tick. No detours of its own. +bool installed = false; using UnityObject = il2cpp::Class<"UnityEngine.CoreModule", "UnityEngine", "Object">; @@ -373,10 +374,15 @@ void Update() } } // namespace +void GalacticAnomalyTimerUpdate() +{ + if (installed) + Update(); +} + void InstallGalacticAnomalyTimer() { - if (install_screen_manager_update_hook()) { - register_screen_manager_update_callback(Update); + installed = InstallScreenManagerUpdateHook(); + if (installed) spdlog::info("Galactic anomaly countdown enabled (below Help All / Engage / Claim)"); - } } diff --git a/mods/src/patches/parts/hotkeys.cc b/mods/src/patches/parts/hotkeys.cc index ad1edf91d..2ba3cee95 100644 --- a/mods/src/patches/parts/hotkeys.cc +++ b/mods/src/patches/parts/hotkeys.cc @@ -19,20 +19,17 @@ #include "prime/KeyCode.h" #include "patches/parts/action_queue.h" -#include "patches/key.h" -#include "patches/mapkey.h" -#include "patches/parts/daily_faction_bulk_claim.h" +#include "patches/fleet_watch.h" +#include "patches/input.h" +#include "patches/shortcuts.h" +#include "patches/patches.h" + #include "patches/parts/double_click_assign_ship.h" -#include "patches/parts/focus_search.h" + #include "str_utils.h" #include -#include "patches/screen_update_hook.h" - -#ifdef _MODDBG -#include "patches/fleet_watch.h" -#endif #include @@ -73,7 +70,7 @@ struct ShortcutKeybindHint : il2cpp::Object { }; } // namespace -static bool reset_focus_next_frame = false; +static bool hotkeys_installed = false; static int show_info_pending = 0; static int show_officer_presets_pending = 0; static int show_officer_swap_pending = 0; @@ -125,7 +122,7 @@ struct NativeShortcutMapping { constexpr auto kNativeShortcutMappings = std::to_array({ {"interior_view", GameFunction::ShowStationInterior}, - {"exterior_view", GameFunction::ShoWStationExterior}, + {"exterior_view", GameFunction::ShowStationExterior}, {"system_view", GameFunction::ShowSystem}, {"galaxy_view", GameFunction::ShowGalaxy}, {"events", GameFunction::ShowEvents}, @@ -237,9 +234,10 @@ void ShortcutKeybindHint_UpdateText_Hook(auto original, ShortcutKeybindHint* _th return fleet ? fleet->Index() : -1; }); } - auto shortcut = hint_action == GameFunction::Max ? std::string{} : MapKey::GetShortcutHint(hint_action); + auto shortcut = hint_action == GameFunction::Max ? std::string{} : Shortcut::Hint(hint_action); + if (shortcut.empty() && mapping->fallback != GameFunction::Max) { - shortcut = MapKey::GetShortcutHint(mapping->fallback); + shortcut = Shortcut::Hint(mapping->fallback); } if (shortcut.empty()) { shortcut = "-"; @@ -624,10 +622,95 @@ bool PressOfficerPresetsButton() return false; } +// Focuses the search box of whichever inventory, officer, or ship list is on screen. +bool FocusSearchBox() +{ + if (!Config::Get().focus_search) { + return false; + } + + // Guard chat input so we don't steal focus from the chat text box + if (auto chat = ObjectFinder::Get(); chat && chat->inputFieldSelected()) { + return false; + } + + // InventoryListViewController covers OfficerRosterViewController and other inventory subclasses + auto inventoryControllers = ObjectFinder::GetAll(); + for (auto controller : inventoryControllers) { + if (!controller || !controller->inputField()) { + continue; + } + + auto canvas = controller->canvasController(); + bool visible = canvas && canvas->Visible(); + bool active = controller->isActiveAndEnabled() && controller->inputField()->isActiveAndEnabled(); + spdlog::debug("[FocusSearch] inventory controller={} section={} canvas={} visible={} active={}", (void*)controller, + (int32_t)controller->targetSection(), (void*)canvas, visible, active); + + if (visible && active) { + controller->inputField()->Focus(); + return true; + } + } + + // OfficerAssignmentViewController (separate from inventory list) + auto officerControllers = ObjectFinder::GetAll(); + for (auto controller : officerControllers) { + if (!controller || !controller->inputField()) { + continue; + } + + auto canvas = controller->canvasController(); + bool visible = canvas && canvas->Visible(); + bool active = controller->isActiveAndEnabled() && controller->inputField()->isActiveAndEnabled(); + spdlog::debug("[FocusSearch] officer assignment controller={} canvas={} visible={} active={}", (void*)controller, + (void*)canvas, visible, active); + + if (visible && active) { + controller->inputField()->Focus(); + return true; + } + } + + // AssignShipsWidget (ship selection / assignment) + auto assignShipWidgets = ObjectFinder::GetAll(); + for (auto widget : assignShipWidgets) { + if (!widget || !widget->inputField()) { + continue; + } + + auto canvas = GetCanvasControllerFromComponent(widget); + bool visible = canvas && canvas->Visible(); + bool active = widget->isActiveAndEnabled() && widget->inputField()->isActiveAndEnabled(); + spdlog::debug("[FocusSearch] assign ships widget={} canvas={} visible={} active={}", (void*)widget, (void*)canvas, + visible, active); + + if (visible && active) { + widget->inputField()->Focus(); + return true; + } + } + + return false; +} + void ScreenManager_Update_Hook(auto original, ScreenManager* _this) { - dispatch_screen_manager_update_callbacks(); - if (!Config::Get().installHotkeyHooks) { + // Each returns at once when its feature is off. They run in every hotkey mode, including Scopely's and while + // hotkeys are toggled off. An exception must not unwind into the game's native frame. + for (auto update : {AllianceGamesStoreUpdate, AssignShipEnterKeyUpdate, ChatOptionsUpdate, + DailyFactionBulkClaimUpdate, fleet_watch::Tick, FleetSyncUpdate, GalacticAnomalyTimerUpdate, + WarpActionLabelUpdate}) { + try { + update(); + } catch (const std::exception& error) { + spdlog::warn("[FrameUpdate] update failed: {}", error.what()); + } catch (...) { + spdlog::warn("[FrameUpdate] update failed with an unknown exception"); + } + } + + if (!hotkeys_installed) { return original(_this); } @@ -639,26 +722,19 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) static std::chrono::time_point select_clock = std::chrono::steady_clock::now(); static int32_t last_ship_select_request = -1; - Key::ResetCache(); - - if (MapKey::IsDown(GameFunction::DisableHotKeys)) { + if (Shortcut::IsDown(GameFunction::DisableHotKeys)) { if (shortcut_hints_ready) { SetNativeShortcutHintsVisible(false); } Config::Get().hotkeys_enabled = false; spdlog::warn("Setting hotkeys to DISABLED"); return; - } else if (MapKey::IsDown(GameFunction::EnableHotKeys)) { + } else if (Shortcut::IsDown(GameFunction::EnableHotKeys)) { Config::Get().hotkeys_enabled = true; spdlog::warn("Setting hotkeys to ENABLED"); return; } - // Per-frame driver - must run every frame, before the hotkey-mode - // early-returns below (it must also run in Scopely-hotkey mode and - // while hotkeys are toggled off). - AssignShipEnterKeyUpdate(); - if (show_officer_presets_pending > 0) { if (PressOfficerPresetsButton()) { show_officer_presets_pending = 0; @@ -706,7 +782,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) return; } - if (shortcut_hints_ready && MapKey::IsDown(GameFunction::ToggleShortcutHints)) { + if (shortcut_hints_ready && Shortcut::IsDown(GameFunction::ToggleShortcutHints)) { ToggleNativeShortcutHints(); return; } @@ -714,7 +790,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) const auto is_in_chat = Hub::IsInChat(); const auto config = &Config::Get(); - if (MapKey::IsDown(GameFunction::Restart)) { + if (Shortcut::IsDown(GameFunction::Restart)) { spdlog::info("Clearing localisation cache and restarting"); LanguageManager::ClearCache(); Hub::get_App()->Reload(); @@ -722,32 +798,32 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } #ifdef _WIN32 - if (MapKey::IsDown(GameFunction::Quit)) { + if (Shortcut::IsDown(GameFunction::Quit)) { TerminateProcess(GetCurrentProcess(), 1); } #elif defined(__APPLE__) - if (MapKey::IsDown(GameFunction::Quit)) { + if (Shortcut::IsDown(GameFunction::Quit)) { Hub::get_App()->Quit(); return; } #endif int32_t ship_select_request = -1; - if (MapKey::IsDown(GameFunction::SelectShip1)) { + if (Shortcut::IsDown(GameFunction::SelectShip1)) { ship_select_request = 0; - } else if (MapKey::IsDown(GameFunction::SelectShip2)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip2)) { ship_select_request = 1; - } else if (MapKey::IsDown(GameFunction::SelectShip3)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip3)) { ship_select_request = 2; - } else if (MapKey::IsDown(GameFunction::SelectShip4)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip4)) { ship_select_request = 3; - } else if (MapKey::IsDown(GameFunction::SelectShip5)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip5)) { ship_select_request = 4; - } else if (MapKey::IsDown(GameFunction::SelectShip6)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip6)) { ship_select_request = 5; - } else if (MapKey::IsDown(GameFunction::SelectShip7)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip7)) { ship_select_request = 6; - } else if (MapKey::IsDown(GameFunction::SelectShip8)) { + } else if (Shortcut::IsDown(GameFunction::SelectShip8)) { ship_select_request = 7; } @@ -824,7 +900,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) if (!is_in_chat) { if (!Key::IsInputFocused()) { - if (MapKey::IsDown(GameFunction::SelectCurrent)) { + if (Shortcut::IsDown(GameFunction::SelectCurrent)) { auto fleet_bar = ObjectFinder::Get(); if (fleet_bar) { auto fleet_controller = fleet_bar->fleetPanelController(); @@ -839,15 +915,15 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } - if ((MapKey::IsDown(GameFunction::ToggleQueue))) { + if ((Shortcut::IsDown(GameFunction::ToggleQueue))) { config->queue_enabled = !config->queue_enabled; // Forget stale recovery authorizations; existing native target queues keep processing. ResetQueueRecoveryTracking(); return; } - if ((MapKey::IsDown(GameFunction::ShowChat) || MapKey::IsDown(GameFunction::ShowChatSide1) - || MapKey::IsDown(GameFunction::ShowChatSide2))) { + if ((Shortcut::IsDown(GameFunction::ShowChat) || Shortcut::IsDown(GameFunction::ShowChatSide1) + || Shortcut::IsDown(GameFunction::ShowChatSide2))) { if (auto chat_manager = ChatManager::Instance(); chat_manager) { if (chat_manager->IsSideChatOpen()) { if (auto view_controller = ObjectFinder::Get(); view_controller) { @@ -857,7 +933,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } } - } else if (MapKey::IsDown(GameFunction::ShowChatSide1) || MapKey::IsDown(GameFunction::ShowChatSide2)) { + } else if (Shortcut::IsDown(GameFunction::ShowChatSide1) || Shortcut::IsDown(GameFunction::ShowChatSide2)) { chat_manager->OpenChannel(ChatChannelCategory::Alliance, ChatViewMode::Side); } else { chat_manager->OpenChannel(ChatChannelCategory::Alliance, ChatViewMode::Fullscreen); @@ -865,7 +941,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } - if (MapKey::IsDown(GameFunction::MoveLeft)) { + if (Shortcut::IsDown(GameFunction::MoveLeft)) { auto const result = MoveDockInManagementView(true) || MoveShipSelectionInDock(true) || MoveArtifactCanvas(true) || MoveOfficerCanvas(true); if (result) { @@ -873,7 +949,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } - if (MapKey::IsDown(GameFunction::MoveRight)) { + if (Shortcut::IsDown(GameFunction::MoveRight)) { auto const result = MoveDockInManagementView(false) || MoveShipSelectionInDock(false) || MoveArtifactCanvas(false) || MoveOfficerCanvas(false); if (result) { @@ -881,21 +957,21 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } - if (Config::Get().focus_search && MapKey::IsDown(GameFunction::FocusSearch)) { + if (Config::Get().focus_search && Shortcut::IsDown(GameFunction::FocusSearch)) { if (FocusSearchBox()) { return; } } - if (MapKey::IsDown(GameFunction::ShowQTrials)) { + if (Shortcut::IsDown(GameFunction::ShowQTrials)) { return GotoSection(SectionID::ChallengeSelection); - } else if (MapKey::IsDown(GameFunction::ShowBookmarks)) { + } else if (Shortcut::IsDown(GameFunction::ShowBookmarks)) { auto bookmark_manager = BookmarksManager::Instance(); if (bookmark_manager) { return bookmark_manager->ViewBookmarks(); } return GotoSection(SectionID::Bookmarks_Main); - } else if (MapKey::IsDown(GameFunction::ShowLookup)) { + } else if (Shortcut::IsDown(GameFunction::ShowLookup)) { auto bookmark_manager = BookmarksManager::Instance(); if (bookmark_manager) { bookmark_manager->ViewCoordinateSearch(); @@ -904,29 +980,29 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) spdlog::warn("[ShowLookup] BookmarksManager instance not available, falling back to main bookmarks"); GotoSection(SectionID::Bookmarks_Main); return; - } else if (MapKey::IsDown(GameFunction::ShowRefinery)) { + } else if (Shortcut::IsDown(GameFunction::ShowRefinery)) { return GotoSection(SectionID::Shop_Refining_List); - } else if (MapKey::IsDown(GameFunction::ShowFactions)) { + } else if (Shortcut::IsDown(GameFunction::ShowFactions)) { return GotoSection(SectionID::Shop_MainFactions); - } else if (MapKey::IsDown(GameFunction::ShoWStationExterior)) { + } else if (Shortcut::IsDown(GameFunction::ShowStationExterior)) { return GotoSection(SectionID::Starbase_Exterior); - } else if (MapKey::IsDown(GameFunction::NativeShortcutGalaxy)) { + } else if (Shortcut::IsDown(GameFunction::NativeShortcutGalaxy)) { InvokeNativeShortcut<"OnGalaxyAction">("Galaxy"); return; - } else if (MapKey::IsDown(GameFunction::ShowGalaxy)) { + } else if (Shortcut::IsDown(GameFunction::ShowGalaxy)) { return ChangeNavigationSection(SectionID::Navigation_Galaxy); - } else if (MapKey::IsDown(GameFunction::ShowStationInterior)) { + } else if (Shortcut::IsDown(GameFunction::ShowStationInterior)) { return GotoSection(SectionID::Starbase_Interior); - } else if (MapKey::IsDown(GameFunction::ShowHaven)) { + } else if (Shortcut::IsDown(GameFunction::ShowHaven)) { return PlanetaryBaseManager::ViewOwnHaven(); - } else if (MapKey::IsDown(GameFunction::ShowSystem)) { + } else if (Shortcut::IsDown(GameFunction::ShowSystem)) { return ChangeNavigationSection(SectionID::Navigation_System); - } else if (MapKey::IsDown(GameFunction::ShowArtifacts)) { + } else if (Shortcut::IsDown(GameFunction::ShowArtifacts)) { return GotoSection(SectionID::ArtifactHall_Inventory); - } else if (MapKey::IsDown(GameFunction::ShowShipConstruction)) { + } else if (Shortcut::IsDown(GameFunction::ShowShipConstruction)) { InvokeNativeShortcut<"OnShipsAction">("Ship Construction"); return; - } else if (MapKey::IsDown(GameFunction::ShowOfficerSwap)) { + } else if (Shortcut::IsDown(GameFunction::ShowOfficerSwap)) { auto* section_manager = Hub::get_SectionManager(); spdlog::info("[OfficerSwap] shortcut pressed current_section={}", section_manager ? static_cast(section_manager->CurrentSection()) : 0); @@ -946,7 +1022,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) spdlog::warn("[Hotkeys] unable to open Manage Ships for Officer Swap"); } return; - } else if (MapKey::IsDown(GameFunction::ShowShipSwap)) { + } else if (Shortcut::IsDown(GameFunction::ShowShipSwap)) { auto* section_manager = Hub::get_SectionManager(); spdlog::info("[ShipSwap] shortcut pressed current_section={}", section_manager ? static_cast(section_manager->CurrentSection()) : 0); @@ -965,23 +1041,23 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) spdlog::warn("[Hotkeys] unable to open Manage Ships for Ship Swap"); } return; - } else if (MapKey::IsDown(GameFunction::ShowShields)) { + } else if (Shortcut::IsDown(GameFunction::ShowShields)) { InvokeNativeShortcut<"OnPeaceShieldAction">("Peace Shields"); return; - } else if (MapKey::IsDown(GameFunction::ShowBattlelogs)) { + } else if (Shortcut::IsDown(GameFunction::ShowBattlelogs)) { InvokeNativeShortcut<"OnInboxAction">("Battle Reports"); return; - } else if (MapKey::IsDown(GameFunction::ShowInventory)) { + } else if (Shortcut::IsDown(GameFunction::ShowInventory)) { return GotoSection(SectionID::InventoryList); - } else if (MapKey::IsDown(GameFunction::ShowMissions)) { + } else if (Shortcut::IsDown(GameFunction::ShowMissions)) { return GotoSection(SectionID::Missions_AcceptedList); - } else if (MapKey::IsDown(GameFunction::ShowResearch)) { + } else if (Shortcut::IsDown(GameFunction::ShowResearch)) { return GotoSection(SectionID::Research_LandingPage); - } else if (MapKey::IsDown(GameFunction::ShowScrapYard)) { + } else if (Shortcut::IsDown(GameFunction::ShowScrapYard)) { return GotoSection(SectionID::ShipScrapping_List); - } else if (MapKey::IsDown(GameFunction::ShowOfficers)) { + } else if (Shortcut::IsDown(GameFunction::ShowOfficers)) { return GotoSection(SectionID::OfficerInventory); - } else if (MapKey::IsDown(GameFunction::ShowOfficerPresets)) { + } else if (Shortcut::IsDown(GameFunction::ShowOfficerPresets)) { if (PressOfficerPresetsButton()) { return; } @@ -1002,84 +1078,84 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } show_officer_presets_pending = 300; return; - } else if (MapKey::IsDown(GameFunction::ShowCommander)) { + } else if (Shortcut::IsDown(GameFunction::ShowCommander)) { // TODO: Does not work properly, defaults to first FleetCommander (spock, rather than selected fleet // commander) return GotoSection(SectionID::FleetCommander_Management); - } else if (MapKey::IsDown(GameFunction::ShowAwayTeam)) { + } else if (Shortcut::IsDown(GameFunction::ShowAwayTeam)) { return GotoSection(SectionID::Missions_AwayTeamsList); - } else if (MapKey::IsDown(GameFunction::NativeShortcutEvents)) { + } else if (Shortcut::IsDown(GameFunction::NativeShortcutEvents)) { InvokeNativeShortcut<"OnEventsAction">("Events"); return; - } else if (MapKey::IsDown(GameFunction::ShowEvents)) { + } else if (Shortcut::IsDown(GameFunction::ShowEvents)) { return GotoSection(SectionID::Tournament_Group_Selection); - } else if (MapKey::IsDown(GameFunction::ShowExoComp)) { + } else if (Shortcut::IsDown(GameFunction::ShowExoComp)) { return GotoSection(SectionID::Consumables); - } else if (MapKey::IsDown(GameFunction::ShowDaily)) { + } else if (Shortcut::IsDown(GameFunction::ShowDaily)) { return GotoSection(SectionID::Missions_DailyGoals); - } else if (MapKey::IsDown(GameFunction::ShowGifts)) { + } else if (Shortcut::IsDown(GameFunction::ShowGifts)) { return GotoSection(SectionID::Shop_List); - } else if (MapKey::IsDown(GameFunction::ShowAlliance)) { + } else if (Shortcut::IsDown(GameFunction::ShowAlliance)) { return GotoSection(SectionID::Alliance_Main); - } else if (MapKey::IsDown(GameFunction::ShowAllianceHelp)) { + } else if (Shortcut::IsDown(GameFunction::ShowAllianceHelp)) { return GotoSection(SectionID::Alliance_Help); - } else if (MapKey::IsDown(GameFunction::ShowAllianceArmada)) { + } else if (Shortcut::IsDown(GameFunction::ShowAllianceArmada)) { return GotoSection(SectionID::Alliance_Armadas); - } else if (MapKey::IsDown(GameFunction::ShowSettings)) { + } else if (Shortcut::IsDown(GameFunction::ShowSettings)) { return GotoSection(SectionID::GameSettings); - } else if (MapKey::IsPressed(GameFunction::UiScaleUp)) { + } else if (Shortcut::IsPressed(GameFunction::UiScaleUp)) { config->AdjustUiScale(true); - } else if (MapKey::IsPressed(GameFunction::UiScaleDown)) { + } else if (Shortcut::IsPressed(GameFunction::UiScaleDown)) { config->AdjustUiScale(false); - } else if (MapKey::IsPressed(GameFunction::UiShipScaleUp)) { + } else if (Shortcut::IsPressed(GameFunction::UiShipScaleUp)) { config->AdjustUiShipScale(true); - } else if (MapKey::IsPressed(GameFunction::UiShipScaleDown)) { + } else if (Shortcut::IsPressed(GameFunction::UiShipScaleDown)) { config->AdjustUiShipScale(false); - } else if (MapKey::IsPressed(GameFunction::UiViewerScaleUp)) { + } else if (Shortcut::IsPressed(GameFunction::UiViewerScaleUp)) { config->AdjustUiViewerScale(true); - } else if (MapKey::IsPressed(GameFunction::UiViewerScaleDown)) { + } else if (Shortcut::IsPressed(GameFunction::UiViewerScaleDown)) { config->AdjustUiViewerScale(false); - } else if (MapKey::IsDown(GameFunction::ToggleAutoConfirmInstantWarp)) { + } else if (Shortcut::IsDown(GameFunction::ToggleAutoConfirmInstantWarp)) { CycleAutoConfirmInstantWarp(*config); - } else if (MapKey::IsDown(GameFunction::TogglePreviewLocate)) { + } else if (Shortcut::IsDown(GameFunction::TogglePreviewLocate)) { config->disable_preview_locate = !config->disable_preview_locate; - } else if (MapKey::IsDown(GameFunction::TogglePreviewRecall)) { + } else if (Shortcut::IsDown(GameFunction::TogglePreviewRecall)) { config->disable_preview_recall = !config->disable_preview_recall; - } else if (MapKey::IsDown(GameFunction::ToggleCargoDefault)) { + } else if (Shortcut::IsDown(GameFunction::ToggleCargoDefault)) { config->show_cargo_default = !config->show_cargo_default; - } else if (MapKey::IsDown(GameFunction::ToggleCargoPlayer)) { + } else if (Shortcut::IsDown(GameFunction::ToggleCargoPlayer)) { config->show_player_cargo = !config->show_player_cargo; - } else if (MapKey::IsDown(GameFunction::ToggleCargoStation)) { + } else if (Shortcut::IsDown(GameFunction::ToggleCargoStation)) { config->show_station_cargo = !config->show_station_cargo; - } else if (MapKey::IsDown(GameFunction::ToggleCargoHostile)) { + } else if (Shortcut::IsDown(GameFunction::ToggleCargoHostile)) { config->show_hostile_cargo = !config->show_hostile_cargo; - } else if (MapKey::IsDown(GameFunction::ToggleCargoArmada)) { + } else if (Shortcut::IsDown(GameFunction::ToggleCargoArmada)) { config->show_armada_cargo = !config->show_armada_cargo; - } else if (MapKey::IsDown(GameFunction::LogLevelOff)) { + } else if (Shortcut::IsDown(GameFunction::LogLevelOff)) { // spdlog::log("Setting log level to OFF"); spdlog::set_level(spdlog::level::off); spdlog::flush_on(spdlog::level::off); - } else if (MapKey::IsDown(GameFunction::LogLevelError)) { + } else if (Shortcut::IsDown(GameFunction::LogLevelError)) { spdlog::set_level(spdlog::level::err); spdlog::flush_on(spdlog::level::err); // spdlog::log("Setting log level to ERROR"); - } else if (MapKey::IsDown(GameFunction::LogLevelWarn)) { + } else if (Shortcut::IsDown(GameFunction::LogLevelWarn)) { spdlog::set_level(spdlog::level::warn); spdlog::flush_on(spdlog::level::warn); // spdlog::log("Setting log level to WARN"); - } else if (MapKey::IsDown(GameFunction::LogLevelInfo)) { + } else if (Shortcut::IsDown(GameFunction::LogLevelInfo)) { spdlog::set_level(spdlog::level::info); spdlog::flush_on(spdlog::level::info); // spdlog::log("Setting log level to INFO"); - } else if (MapKey::IsDown(GameFunction::LogLevelDebug)) { + } else if (Shortcut::IsDown(GameFunction::LogLevelDebug)) { spdlog::set_level(spdlog::level::debug); spdlog::flush_on(spdlog::level::debug); // spdlog::log("Setting log level to DEBUG"); - } else if (MapKey::IsDown(GameFunction::LogLevelTrace)) { + } else if (Shortcut::IsDown(GameFunction::LogLevelTrace)) { spdlog::set_level(spdlog::level::trace); spdlog::flush_on(spdlog::level::trace); // spdlog::log("Setting log level to TRACE"); - } else if (MapKey::IsDown(GameFunction::ShowShips)) { + } else if (Shortcut::IsDown(GameFunction::ShowShips)) { auto fleet_bar = ObjectFinder::Get(); auto fleet_controller = fleet_bar ? fleet_bar->fleetPanelController() : nullptr; auto fleet = fleet_controller ? fleet_controller->fleet() : nullptr; @@ -1090,11 +1166,11 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } else { if (auto chat_manager = ChatManager::Instance(); chat_manager) { - if (MapKey::IsDown(GameFunction::SelectChatGlobal)) { + if (Shortcut::IsDown(GameFunction::SelectChatGlobal)) { return chat_manager->OpenChannel(ChatChannelCategory::Global); - } else if (MapKey::IsDown(GameFunction::SelectChatAlliance)) { + } else if (Shortcut::IsDown(GameFunction::SelectChatAlliance)) { return chat_manager->OpenChannel(ChatChannelCategory::Alliance); - } else if (MapKey::IsDown(GameFunction::SelectChatPrivate)) { + } else if (Shortcut::IsDown(GameFunction::SelectChatPrivate)) { return chat_manager->OpenChannel(ChatChannelCategory::Private); } } @@ -1112,7 +1188,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } // Dismiss the golden rewards screen when escape or space is pressed. - if (MapKey::IsDown(GameFunction::ActionPrimary) || Key::Pressed(KeyCode::Escape)) { + if (Shortcut::IsDown(GameFunction::ActionPrimary) || Key::Pressed(KeyCode::Escape)) { if (auto reward_controller = ObjectFinder::Get(); reward_controller) { if (reward_controller->IsActive()) { return reward_controller->GoBackToLastSection(); @@ -1120,9 +1196,9 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } - if (MapKey::IsDown(GameFunction::ActionPrimary) || MapKey::IsDown(GameFunction::ActionSecondary) - || MapKey::IsDown(GameFunction::ActionRecall) || MapKey::IsDown(GameFunction::ActionRepair) - || MapKey::IsDown(GameFunction::ActionQueue) || MapKey::IsDown(GameFunction::ActionQueueClear) + if (Shortcut::IsDown(GameFunction::ActionPrimary) || Shortcut::IsDown(GameFunction::ActionSecondary) + || Shortcut::IsDown(GameFunction::ActionRecall) || Shortcut::IsDown(GameFunction::ActionRepair) + || Shortcut::IsDown(GameFunction::ActionQueue) || Shortcut::IsDown(GameFunction::ActionQueueClear) || force_space_action_next_frame) { if (Hub::IsInSystemOrGalaxyOrStarbase() && !Hub::IsInChat() && !Key::IsInputFocused()) { auto fleet_bar = ObjectFinder::Get(); @@ -1134,9 +1210,7 @@ void ScreenManager_Update_Hook(auto original, ScreenManager* _this) } } - DailyFactionBulkClaimUpdate(); - - if (MapKey::IsDown(GameFunction::ActionView)) { + if (Shortcut::IsDown(GameFunction::ActionView)) { auto all_pre_scan_widgets = ObjectFinder::GetAll(); for (auto& pre_scan_widget : all_pre_scan_widgets) { @@ -1348,7 +1422,7 @@ bool DidExecuteRepair(FleetBarViewController* fleet_bar) void ExecuteSpaceAction(FleetBarViewController* fleet_bar) { - if (MapKey::IsDown(GameFunction::ActionPrimary)) { + if (Shortcut::IsDown(GameFunction::ActionPrimary)) { if (auto* navigation = ObjectFinder::Get(); navigation && navigation->TryPressJoinWaveDefense()) { return; @@ -1380,14 +1454,14 @@ void ExecuteSpaceAction(FleetBarViewController* fleet_bar) return; } - auto has_primary = MapKey::IsDown(GameFunction::ActionPrimary) || force_space_action_next_frame; - auto has_repair = MapKey::IsDown(GameFunction::ActionRepair); - auto has_recall_cancel = MapKey::IsDown(GameFunction::ActionRecallCancel); - auto has_secondary = MapKey::IsDown(GameFunction::ActionSecondary); - auto has_queue = MapKey::IsDown(GameFunction::ActionQueue); - auto has_queue_clear = MapKey::IsDown(GameFunction::ActionQueueClear); + auto has_primary = Shortcut::IsDown(GameFunction::ActionPrimary) || force_space_action_next_frame; + auto has_repair = Shortcut::IsDown(GameFunction::ActionRepair); + auto has_recall_cancel = Shortcut::IsDown(GameFunction::ActionRecallCancel); + auto has_secondary = Shortcut::IsDown(GameFunction::ActionSecondary); + auto has_queue = Shortcut::IsDown(GameFunction::ActionQueue); + auto has_queue_clear = Shortcut::IsDown(GameFunction::ActionQueueClear); auto has_recall = - MapKey::IsDown(GameFunction::ActionRecall) && (!Config::Get().disable_preview_recall || !CanHideViewers()); + Shortcut::IsDown(GameFunction::ActionRecall) && (!Config::Get().disable_preview_recall || !CanHideViewers()); const bool require_visible_target = has_primary && Config::Get().primary_requires_visible_target; if (has_queue_clear) { @@ -1651,17 +1725,15 @@ void ShowWithFleet_Hook(auto original, PreScanTargetWidget* _this, void* a1) } } -bool install_screen_manager_update_hook() +bool InstallScreenManagerUpdateHook() { static bool installed = false; - if (installed) { - return true; - } - - if (auto m = ScreenManager::Class::Method("Update")) { - if (!SPUD_STATIC_DETOUR(m.Pointer(), ScreenManager_Update_Hook)) - return false; - installed = true; + if (!installed) { + if (auto m = ScreenManager::Class::Method("Update")) { + if (!SPUD_STATIC_DETOUR(m.Pointer(), ScreenManager_Update_Hook)) + return false; + installed = true; + } } return installed; } @@ -1669,7 +1741,7 @@ bool install_screen_manager_update_hook() void InstallShortcutHintHooks() { // Configuration is fully parsed before hook installation. NONE, empty, and invalid bindings opt out. - if (!MapKey::HasBinding(GameFunction::ToggleShortcutHints)) { + if (!Shortcut::HasBinding(GameFunction::ToggleShortcutHints)) { return; } @@ -1681,7 +1753,6 @@ void InstallShortcutHintHooks() return; } - MapKey::CacheShortcutHints(); original_shortcut_hint_update_text = SPUD_STATIC_DETOUR(update_text.Pointer(), ShortcutKeybindHint_UpdateText_Hook); SPUD_STATIC_DETOUR(update_visibility.Pointer(), ShortcutKeybindHint_UpdateVisibility_Hook); shortcut_hints_ready = true; @@ -1696,10 +1767,7 @@ void InstallHotkeyHooks() InstallShortcutHintHooks(); - install_screen_manager_update_hook(); -#ifdef _MODDBG - fleet_watch::InstallRuntimeProbe(); -#endif + hotkeys_installed = InstallScreenManagerUpdateHook(); if (auto m = RewardsButtonWidget::Class::Method("OnDidBindContext")) SPUD_STATIC_DETOUR(m.Pointer(), OnDidBindContext_Hook); diff --git a/mods/src/patches/parts/pinned_ship_click.cc b/mods/src/patches/parts/pinned_ship_click.cc index 1930032d4..3fd3909a5 100644 --- a/mods/src/patches/parts/pinned_ship_click.cc +++ b/mods/src/patches/parts/pinned_ship_click.cc @@ -1,7 +1,7 @@ #include "patches/object_tracker.h" #include "config.h" #include "file.h" -#include "patches/key.h" +#include "patches/input.h" #include "ship_name_match.h" #include "pinned_ship_click.h" diff --git a/mods/src/patches/parts/sync.cc b/mods/src/patches/parts/sync.cc index 6a889364e..e4dfe0383 100644 --- a/mods/src/patches/parts/sync.cc +++ b/mods/src/patches/parts/sync.cc @@ -2,7 +2,7 @@ #include "errormsg.h" #include "file.h" #include "patches/fleet_watch.h" -#include "patches/screen_update_hook.h" +#include "patches/patches.h" #include "str_utils.h" #include @@ -3360,6 +3360,9 @@ static void reread_requested() } } +// Set once fleet_watch accepted the subscription, which also installs the frame tick. +static bool installed = false; + static void tick() { reread_requested(); @@ -3382,11 +3385,17 @@ static void install() return; } - register_screen_manager_update_callback(tick); + installed = true; spdlog::info("Sync: fleet state sync enabled"); } } // namespace fleets +void FleetSyncUpdate() +{ + if (fleets::installed) + fleets::tick(); +} + using GameServerModelRegistry = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Core", "GameServerModelRegistry">; using GameServer = il2cpp::Class<"Digit.Client.PrimeLib.Runtime", "Digit.PrimeServer.Core", "GameServer">; diff --git a/mods/src/patches/parts/warp_action_label.cc b/mods/src/patches/parts/warp_action_label.cc index 8202aeeca..bdcd359ff 100644 --- a/mods/src/patches/parts/warp_action_label.cc +++ b/mods/src/patches/parts/warp_action_label.cc @@ -2,7 +2,7 @@ #include "patches/object_tracker.h" #include "patches/instant_warp_policy.h" -#include "patches/screen_update_hook.h" +#include "patches/patches.h" #include "prime/fleet.h" #include "prime/viewers.h" #include "prime/ui.h" @@ -15,7 +15,9 @@ namespace { -// This is a client of the existing frame dispatcher, not another UI detour. +bool installed = false; + +// Runs on the ScreenManager.Update tick, not another UI detour. // Never mutate the button context, timer, resource cost, or click handler. // Unity's `bool(UnityEngine.Object)`: false for null and for destroyed native objects. bool Alive(il2cpp::Object* object) @@ -127,8 +129,15 @@ void Update() } } // namespace +void WarpActionLabelUpdate() +{ + if (installed) + Update(); +} + void InstallWarpActionLabel() { - if (install_screen_manager_update_hook() && register_screen_manager_update_callback(Update)) + installed = InstallScreenManagerUpdateHook(); + if (installed) spdlog::info("[WarpActionLabel] system-card action labels enabled"); } diff --git a/mods/src/patches/parts/zoom.cc b/mods/src/patches/parts/zoom.cc index 36ee939c1..cf8ab4d6d 100644 --- a/mods/src/patches/parts/zoom.cc +++ b/mods/src/patches/parts/zoom.cc @@ -1,6 +1,7 @@ #include "config.h" -#include +#include "patches/input.h" +#include "patches/shortcuts.h" #include @@ -314,45 +315,45 @@ void NavigationZoom_Update_Hook(auto original, NavigationZoom *_this) if (!Key::IsInputFocused()) { if (camera_shortcuts_enabled) { - if (MapKey::IsDown(GameFunction::SetZoomPreset1)) { + if (Shortcut::IsDown(GameFunction::SetZoomPreset1)) { return StoreZoom("System Preset 1", config->system_zoom_preset_1, _this); - } else if (MapKey::IsDown(GameFunction::SetZoomPreset2)) { + } else if (Shortcut::IsDown(GameFunction::SetZoomPreset2)) { return StoreZoom("System Preset 2", config->system_zoom_preset_2, _this); - } else if (MapKey::IsDown(GameFunction::SetZoomPreset3)) { + } else if (Shortcut::IsDown(GameFunction::SetZoomPreset3)) { return StoreZoom("System Preset 3", config->system_zoom_preset_3, _this); - } else if (MapKey::IsDown(GameFunction::SetZoomPreset4)) { + } else if (Shortcut::IsDown(GameFunction::SetZoomPreset4)) { return StoreZoom("System Preset 4", config->system_zoom_preset_4, _this); - } else if (MapKey::IsDown(GameFunction::SetZoomPreset5)) { + } else if (Shortcut::IsDown(GameFunction::SetZoomPreset5)) { return StoreZoom("System Preset 5", config->system_zoom_preset_5, _this); - } else if (MapKey::IsDown(GameFunction::SetZoomDefault)) { + } else if (Shortcut::IsDown(GameFunction::SetZoomDefault)) { return StoreZoom("System Default", config->default_system_zoom, _this); } do_absolute_zoom = true; - if (MapKey::IsDown(GameFunction::ZoomPreset1)) { + if (Shortcut::IsDown(GameFunction::ZoomPreset1)) { zoomDelta = config->system_zoom_preset_1; do_store_zoom = true; - } else if (MapKey::IsDown(GameFunction::ZoomPreset2)) { + } else if (Shortcut::IsDown(GameFunction::ZoomPreset2)) { zoomDelta = config->system_zoom_preset_2; do_store_zoom = true; - } else if (MapKey::IsDown(GameFunction::ZoomPreset3)) { + } else if (Shortcut::IsDown(GameFunction::ZoomPreset3)) { zoomDelta = config->system_zoom_preset_3; do_store_zoom = true; - } else if (MapKey::IsDown(GameFunction::ZoomPreset4)) { + } else if (Shortcut::IsDown(GameFunction::ZoomPreset4)) { zoomDelta = config->system_zoom_preset_4; do_store_zoom = true; - } else if (MapKey::IsDown(GameFunction::ZoomPreset5)) { + } else if (Shortcut::IsDown(GameFunction::ZoomPreset5)) { zoomDelta = config->system_zoom_preset_5; do_store_zoom = true; } if (config->hotkeys_extended) { - if (MapKey::IsDown(GameFunction::ZoomReset)) { + if (Shortcut::IsDown(GameFunction::ZoomReset)) { do_absolute_zoom = false; do_default_zoom = true; - } else if (MapKey::IsDown(GameFunction::ZoomMin)) { + } else if (Shortcut::IsDown(GameFunction::ZoomMin)) { zoomDelta = config->zoom; - } else if (MapKey::IsDown(GameFunction::ZoomMax)) { + } else if (Shortcut::IsDown(GameFunction::ZoomMax)) { zoomDelta = 100; } } @@ -368,7 +369,7 @@ void NavigationZoom_Update_Hook(auto original, NavigationZoom *_this) zoomDelta = config->keyboard_zoom_speed * dt; } - if ((camera_shortcuts_enabled && MapKey::IsPressed(GameFunction::ZoomIn)) || do_absolute_zoom) { + if ((camera_shortcuts_enabled && Shortcut::IsPressed(GameFunction::ZoomIn)) || do_absolute_zoom) { vec3 mousePos; GetMousePosition(&mousePos); _this->SetZoomLocation(vec2{.x = mousePos.x, .y = mousePos.y}); @@ -381,7 +382,7 @@ void NavigationZoom_Update_Hook(auto original, NavigationZoom *_this) } _this->SetWorldPoint(GetMouseWorldPos(_this->sceneCamera(), &mousePos)); _this->ZoomCameraAtWorldPoint(); - } else if (camera_shortcuts_enabled && MapKey::IsPressed(GameFunction::ZoomOut)) { + } else if (camera_shortcuts_enabled && Shortcut::IsPressed(GameFunction::ZoomOut)) { vec3 mousePos; GetMousePosition(&mousePos); _this->SetZoomLocation(vec2{.x = mousePos.x, .y = mousePos.y}); diff --git a/mods/src/patches/patches.h b/mods/src/patches/patches.h index 8a3f2c1c5..cdaaf491b 100644 --- a/mods/src/patches/patches.h +++ b/mods/src/patches/patches.h @@ -1,3 +1,59 @@ #pragma once +// The mod's features. ApplyPatches is the entry point the platform loaders call; the rest is the table of +// features patches.cc installs, plus the few calls features make into each other. + +// Loads GameAssembly and hooks il2cpp_init, which installs everything else once the runtime is up. void ApplyPatches(); + +void InstallAudioEventHooks(); +void InstallBuffFixHooks(); +void InstallCargoFormatHooks(); +void InstallChatPatches(); +void InstallDailyFactionBulkClaimHooks(); +void InstallDoubleClickAssignShipHooks(); +void InstallForbiddenTechConfirmationHooks(); +void InstallFreeResizeHooks(); +void InstallGalacticAnomalyTimer(); +void InstallGiftsBulkClaimHooks(); +void InstallHotkeyHooks(); +void InstallInstantCargoCounterHooks(); +void InstallInstantWarpConfirmationHooks(); +void InstallLoadingScreenHooks(); +void InstallLoadingTipHooks(); +void InstallMiscPatches(); +void InstallMissionHudTweaksHooks(); +void InstallArtifactExchangeHooks(); +void InstallObjectTrackers(); +void InstallOfficerSortHooks(); +void InstallPanHooks(); +void InstallPinnedShipSortHooks(); +void InstallSyncPatches(); +void InstallTempCrashFixes(); +void InstallTestPatches(); +void InstallToastBannerHooks(); +void InstallTransitionScreenHooks(); +void InstallUiScaleHooks(); +void InstallZoomHooks(); + +// ScreenManager.Update is the mod's per-frame tick. Hotkeys own the detour; features that poll every frame ask +// for it with this, and hotkeys.cc calls their Update functions in order. Safe to call more than once. +bool InstallScreenManagerUpdateHook(); +void AllianceGamesStoreUpdate(); +void AssignShipEnterKeyUpdate(); +void ChatOptionsUpdate(); +void DailyFactionBulkClaimUpdate(); +void FleetSyncUpdate(); +void GalacticAnomalyTimerUpdate(); +void WarpActionLabelUpdate(); + +// UI scale hotkeys, implemented in ui_scale.cc. +void AdjustUiScale(bool up); +void AdjustUiShipScale(bool up); +void AdjustUiViewerScale(bool up); + +// The monitor's physical-to-logical pixel ratio on Windows; 1 elsewhere. Cached until `refresh`. +float WindowDpiScale(bool refresh = false); + +void InstallHavenZoomHooks(); +void InstallHavenHistoryHooks(); diff --git a/mods/src/patches/screen_update_hook.cc b/mods/src/patches/screen_update_hook.cc deleted file mode 100644 index 494fe77b0..000000000 --- a/mods/src/patches/screen_update_hook.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include "patches/screen_update_hook.h" - -#include - -#include -#include -#include - -namespace -{ -std::vector s_callbacks; -bool s_dispatching = false; -} // namespace - -bool register_screen_manager_update_callback(ScreenManagerUpdateCallback callback) -{ - if (!callback) { - return false; - } - if (s_dispatching) { - spdlog::warn("[ScreenUpdate] rejected callback registration during dispatch"); - return false; - } - if (std::find(s_callbacks.begin(), s_callbacks.end(), callback) != s_callbacks.end()) { - return true; - } - s_callbacks.push_back(callback); - return true; -} - -void dispatch_screen_manager_update_callbacks() -{ - if (s_dispatching) { - spdlog::warn("[ScreenUpdate] suppressed recursive callback dispatch"); - return; - } - s_dispatching = true; - for (const auto callback : s_callbacks) { - try { - callback(); - } catch (const std::exception& error) { - spdlog::warn("[ScreenUpdate] callback failed: {}", error.what()); - } catch (...) { - spdlog::warn("[ScreenUpdate] callback failed with an unknown exception"); - } - } - s_dispatching = false; -} diff --git a/mods/src/patches/screen_update_hook.h b/mods/src/patches/screen_update_hook.h deleted file mode 100644 index 613d5edee..000000000 --- a/mods/src/patches/screen_update_hook.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// ScreenManager.Update has one detour owner shared by HotkeyHooks and opt-in frame services. -// Installation is idempotent so either owner can request the dispatcher. -bool install_screen_manager_update_hook(); - -using ScreenManagerUpdateCallback = void (*)(); - -// Registers a process-lifetime callback that runs before the game's ScreenManager.Update handling. -// Register during patch installation. Re-registering the same callback is harmless. -bool register_screen_manager_update_callback(ScreenManagerUpdateCallback callback); -void dispatch_screen_manager_update_callbacks(); diff --git a/mods/src/patches/ship_shortcut_hint.h b/mods/src/patches/ship_shortcut_hint.h index 671e74d54..43ba1396c 100644 --- a/mods/src/patches/ship_shortcut_hint.h +++ b/mods/src/patches/ship_shortcut_hint.h @@ -1,6 +1,6 @@ #pragma once -#include "gamefunctions.h" +#include "shortcuts.h" #include diff --git a/mods/src/patches/shortcuts.cc b/mods/src/patches/shortcuts.cc new file mode 100644 index 000000000..e9b68d5ec --- /dev/null +++ b/mods/src/patches/shortcuts.cc @@ -0,0 +1,590 @@ +#include "patches/shortcuts.h" + +#include "defaultconfig.h" +#include "patches/input.h" +#include "str_utils.h" + +#include + +#include +#include +#include + +namespace +{ +namespace DCSH = DefaultConfig::Shortcuts; + +const std::unordered_map kKeyNames = { + {"LALT", KeyCode::LeftAlt}, + {"LAPPLE", KeyCode::LeftApple}, + {"LCOM", KeyCode::LeftCommand}, + {"LCTRL", KeyCode::LeftControl}, + {"ALTGR", KeyCode::AltGr}, + {"END", KeyCode::End}, + {"HOME", KeyCode::Home}, + {"PGDOWN", KeyCode::PageDown}, + {"PGUP", KeyCode::PageUp}, + {"DOWN", KeyCode::DownArrow}, + {"LEFT", KeyCode::LeftArrow}, + {"RIGHT", KeyCode::RightArrow}, + {"UP", KeyCode::UpArrow}, + {"BACKSPACE", KeyCode::Backspace}, + {"BREAK", KeyCode::Break}, + {"CAPS", KeyCode::CapsLock}, + {"CLEAR", KeyCode::Clear}, + {"DELETE", KeyCode::Delete}, + {"ESCAPE", KeyCode::Escape}, + {"HELP", KeyCode::Help}, + {"INSERT", KeyCode::Insert}, + {"LSHIFT", KeyCode::LeftShift}, + {"LWIN", KeyCode::LeftWindows}, + {"MENU", KeyCode::Menu}, + {"PAUSE", KeyCode::Pause}, + {"PRINT", KeyCode::Print}, + {"RALT", KeyCode::RightAlt}, + {"RAPPLE", KeyCode::RightApple}, + {"RCOM", KeyCode::RightCommand}, + {"RCTRL", KeyCode::RightControl}, + {"RETURN", KeyCode::Return}, + {"RSHIFT", KeyCode::RightShift}, + {"RWIN", KeyCode::RightWindows}, + {"SCROLL", KeyCode::ScrollLock}, + {"SYSREQ", KeyCode::SysReq}, + {"TAB", KeyCode::Tab}, + {"MOUSE0", KeyCode::Mouse0}, + {"MOUSE1", KeyCode::Mouse1}, + {"MOUSE2", KeyCode::Mouse2}, + {"MOUSE3", KeyCode::Mouse3}, + {"MOUSE4", KeyCode::Mouse4}, + {"MOUSE5", KeyCode::Mouse5}, + {"MOUSE6", KeyCode::Mouse6}, + {"SPACE", KeyCode::Space}, + // Chords are split on '-', so the minus key needs a name. + {"MINUS", KeyCode::Minus}, + {"_", KeyCode::Underscore}, + {",", KeyCode::Comma}, + {";", KeyCode::Semicolon}, + {":", KeyCode::Colon}, + {"!", KeyCode::Exclaim}, + {"?", KeyCode::Question}, + {".", KeyCode::Period}, + {"'", KeyCode::Quote}, + {"(", KeyCode::LeftParen}, + {")", KeyCode::RightParen}, + {"[", KeyCode::LeftBracket}, + {"]", KeyCode::RightBracket}, + {"{", KeyCode::LeftCurlyBracket}, + {"}", KeyCode::RightCurlyBracket}, + {"@", KeyCode::At}, + {"*", KeyCode::Asterisk}, + {"/", KeyCode::Slash}, + {"\"", KeyCode::DoubleQuote}, + {"\\", KeyCode::Backslash}, + {"&", KeyCode::Ampersand}, + {"#", KeyCode::Hash}, + {"%", KeyCode::Percent}, + {"`", KeyCode::BackQuote}, + {"^", KeyCode::Caret}, + {"+", KeyCode::Plus}, + {"<", KeyCode::Less}, + {"=", KeyCode::Equals}, + {">", KeyCode::Greater}, + {"~", KeyCode::Tilde}, + {"$", KeyCode::Dollar}, + {"0", KeyCode::Alpha0}, + {"1", KeyCode::Alpha1}, + {"2", KeyCode::Alpha2}, + {"3", KeyCode::Alpha3}, + {"4", KeyCode::Alpha4}, + {"5", KeyCode::Alpha5}, + {"6", KeyCode::Alpha6}, + {"7", KeyCode::Alpha7}, + {"8", KeyCode::Alpha8}, + {"9", KeyCode::Alpha9}, + {"A", KeyCode::A}, + {"B", KeyCode::B}, + {"C", KeyCode::C}, + {"D", KeyCode::D}, + {"E", KeyCode::E}, + {"F", KeyCode::F}, + {"G", KeyCode::G}, + {"H", KeyCode::H}, + {"I", KeyCode::I}, + {"J", KeyCode::J}, + {"K", KeyCode::K}, + {"L", KeyCode::L}, + {"M", KeyCode::M}, + {"N", KeyCode::N}, + {"O", KeyCode::O}, + {"P", KeyCode::P}, + {"Q", KeyCode::Q}, + {"R", KeyCode::R}, + {"S", KeyCode::S}, + {"T", KeyCode::T}, + {"U", KeyCode::U}, + {"V", KeyCode::V}, + {"W", KeyCode::W}, + {"X", KeyCode::X}, + {"Y", KeyCode::Y}, + {"Z", KeyCode::Z}, + {"F1", KeyCode::F1}, + {"F2", KeyCode::F2}, + {"F3", KeyCode::F3}, + {"F4", KeyCode::F4}, + {"F5", KeyCode::F5}, + {"F6", KeyCode::F6}, + {"F7", KeyCode::F7}, + {"F8", KeyCode::F8}, + {"F9", KeyCode::F9}, + {"F10", KeyCode::F10}, + {"F11", KeyCode::F11}, + {"F12", KeyCode::F12}, + {"F13", KeyCode::F13}, + {"F14", KeyCode::F14}, + {"F15", KeyCode::F15}, + {"NUMLOCK", KeyCode::Numlock}, + {"KEY0", KeyCode::Keypad0}, + {"KEY1", KeyCode::Keypad1}, + {"KEY2", KeyCode::Keypad2}, + {"KEY3", KeyCode::Keypad3}, + {"KEY4", KeyCode::Keypad4}, + {"KEY5", KeyCode::Keypad5}, + {"KEY6", KeyCode::Keypad6}, + {"KEY7", KeyCode::Keypad7}, + {"KEY8", KeyCode::Keypad8}, + {"KEY9", KeyCode::Keypad9}, + {"KEYDIVIDE", KeyCode::KeypadDivide}, + {"KEYENTER", KeyCode::KeypadEnter}, + {"KEYEQUAL", KeyCode::KeypadEquals}, + {"KEYMINUS", KeyCode::KeypadMinus}, + {"KEYMULTI", KeyCode::KeypadMultiply}, + {"KEYPERIOD", KeyCode::KeypadPeriod}, + {"KEYPLUS", KeyCode::KeypadPlus}, +}; + +// Modifier names that match either side of the keyboard. +std::optional> ModifierGroup(std::string_view token) +{ + if (token == "SHIFT") + return {{KeyCode::LeftShift, KeyCode::RightShift}}; + if (token == "ALT") + return {{KeyCode::LeftAlt, KeyCode::RightAlt}}; + if (token == "CTRL") + return {{KeyCode::LeftControl, KeyCode::RightControl}}; + if (token == "APPLE") + return {{KeyCode::LeftApple, KeyCode::RightApple}}; + if (token == "CMD") + return {{KeyCode::LeftCommand, KeyCode::RightCommand}}; + if (token == "WIN") + return {{KeyCode::LeftWindows, KeyCode::RightWindows}}; + if (const auto key = ParseKeyName(token); IsModifierKey(key)) + return {{key}}; + return std::nullopt; +} + +struct HintToken { + std::string_view token; + std::string_view compact; + bool primary_key_only = false; +}; + +// AutoHotkey's ASCII modifier notation, so the game's limited badge font can render every hint. +constexpr auto kHintTokens = std::to_array({ + {"SHIFT", "+"}, {"CTRL", "^"}, {"ALT", "!"}, {"ALTGR", "!"}, {"APPLE", "#"}, + {"CMD", "#"}, {"WIN", "#"}, {"LSHIFT", "<+"}, {"RSHIFT", ">+"}, {"LCTRL", "<^"}, + {"RCTRL", ">^"}, {"LALT", "!"}, {"LAPPLE", "<#"}, {"LCOM", "<#"}, + {"LWIN", "<#"}, {"RAPPLE", ">#"}, {"RCOM", ">#"}, {"RWIN", ">#"}, {"+", "PLS", true}, + {"^", "CAR", true}, {"!", "EXC", true}, {"#", "HSH", true}, {"SPACE", "SPC"}, {"MOUSE0", "M0"}, + {"MOUSE1", "M1"}, {"MOUSE2", "M2"}, {"MOUSE3", "M3"}, {"MOUSE4", "M4"}, {"MOUSE5", "M5"}, + {"MOUSE6", "M6"}, {"ENTER", "ENT"}, {"RETURN", "ENT"}, {"ESCAPE", "ESC"}, {"TAB", "TAB"}, + {"BACKSPACE", "BS"}, {"DELETE", "DEL"}, {"MINUS", "-"}, {"EQUAL", "="}, {"LEFT", "LT"}, + {"RIGHT", "RT"}, {"UP", "UP"}, {"DOWN", "DN"}, {"PGUP", "PU"}, {"PGDOWN", "PD"}, + {"HOME", "HM"}, {"END", "END"}, +}); + +constexpr std::string_view CompactToken(std::string_view token, bool is_primary_key) +{ + for (const auto& mapping : kHintTokens) + if (mapping.token == token && (!mapping.primary_key_only || is_primary_key)) + return mapping.compact; + return token; +} + +static_assert(CompactToken("CTRL", false) == "^"); +static_assert(CompactToken("+", true) == "PLS"); +static_assert(CompactToken("+", false) == "+"); +static_assert(CompactToken("F7", true) == "F7"); + +std::array, GameFunction::Max> bindings; + +// Movement reads its own direction, so claiming it would suppress its own pan on the next frame. Other modified +// actions keep ownership of an arrow until it is released. +bool IsMovement(GameFunction action) +{ + return action == GameFunction::MoveLeft || action == GameFunction::MoveRight || action == GameFunction::MoveUp + || action == GameFunction::MoveDown; +} + +bool ModifiersMatch(const KeyChord& chord) +{ + if (chord.modifiers.empty()) + return !Key::IsModified(); + return std::ranges::all_of(chord.modifiers, + [](const auto& group) { return std::ranges::any_of(group, Key::Pressed); }); +} + +template bool Matches(GameFunction action) +{ + for (const auto& chord : bindings[action]) { + if (chord.key == KeyCode::None || !KeyState(chord.key) || !ModifiersMatch(chord)) + continue; + if (!chord.modifiers.empty() && !IsMovement(action)) + Key::ClaimDirectionalInput(chord.key); + return true; + } + return false; +} + +enum class Tier { kAlways, kExtended, kExperimental }; + +struct Setting { + std::string_view key; + GameFunction action; + std::string_view fallback; + Tier tier = Tier::kAlways; + std::array aliases = {}; +}; + +// Order is the order of the debug log; the TOML output is sorted by key anyway. +constexpr Setting kSettings[] = { + {"move_up", GameFunction::MoveUp, DCSH::move_up}, + {"move_down", GameFunction::MoveDown, DCSH::move_down}, + {"move_left", GameFunction::MoveLeft, DCSH::move_left}, + {"move_right", GameFunction::MoveRight, DCSH::move_right}, + {"set_hotkeys_disabled", + GameFunction::DisableHotKeys, + DCSH::set_hotkeys_disabled, + Tier::kAlways, + {"set_hotkeys_disble", "set_hotkeys_disable"}}, + {"set_hotkeys_enabled", + GameFunction::EnableHotKeys, + DCSH::set_hotkeys_enabled, + Tier::kAlways, + {"set_hotkeys_enable"}}, + {"select_chatalliance", GameFunction::SelectChatAlliance, DCSH::select_chatalliance}, + {"select_chatglobal", GameFunction::SelectChatGlobal, DCSH::select_chatglobal}, + {"select_chatprivate", GameFunction::SelectChatPrivate, DCSH::select_chatprivate}, + {"quit", GameFunction::Quit, DCSH::quit}, + {"select_ship1", GameFunction::SelectShip1, DCSH::select_ship1}, + {"select_ship2", GameFunction::SelectShip2, DCSH::select_ship2}, + {"select_ship3", GameFunction::SelectShip3, DCSH::select_ship3}, + {"select_ship4", GameFunction::SelectShip4, DCSH::select_ship4}, + {"select_ship5", GameFunction::SelectShip5, DCSH::select_ship5}, + {"select_ship6", GameFunction::SelectShip6, DCSH::select_ship6}, + {"select_ship7", GameFunction::SelectShip7, DCSH::select_ship7}, + {"select_ship8", GameFunction::SelectShip8, DCSH::select_ship8}, + {"select_current", GameFunction::SelectCurrent, DCSH::select_current}, + {"focus_search", GameFunction::FocusSearch, DCSH::focus_search}, + {"action_primary", GameFunction::ActionPrimary, DCSH::action_primary}, + {"action_secondary", GameFunction::ActionSecondary, DCSH::action_secondary}, + {"action_queue", GameFunction::ActionQueue, DCSH::action_queue}, + {"action_queue_clear", GameFunction::ActionQueueClear, DCSH::action_queue_clear}, + {"action_view", GameFunction::ActionView, DCSH::action_view}, + {"action_recall", GameFunction::ActionRecall, DCSH::action_recall}, + {"action_recall_cancel", GameFunction::ActionRecallCancel, DCSH::action_recall_cancel}, + {"action_repair", GameFunction::ActionRepair, DCSH::action_repair}, + {"show_chat", GameFunction::ShowChat, DCSH::show_chat}, + {"show_chatside1", GameFunction::ShowChatSide1, DCSH::show_chatside1}, + {"show_chatside2", GameFunction::ShowChatSide2, DCSH::show_chatside2}, + {"show_galaxy", GameFunction::ShowGalaxy, DCSH::show_galaxy}, + {"show_galaxy_native", + GameFunction::NativeShortcutGalaxy, + DCSH::show_galaxy_native, + Tier::kAlways, + {"native_shortcut_galaxy"}}, + {"show_system", GameFunction::ShowSystem, DCSH::show_system}, + {"zoom_preset1", GameFunction::ZoomPreset1, DCSH::zoom_preset1}, + {"zoom_preset2", GameFunction::ZoomPreset2, DCSH::zoom_preset2}, + {"zoom_preset3", GameFunction::ZoomPreset3, DCSH::zoom_preset3}, + {"zoom_preset4", GameFunction::ZoomPreset4, DCSH::zoom_preset4}, + {"zoom_preset5", GameFunction::ZoomPreset5, DCSH::zoom_preset5}, + {"zoom_in", GameFunction::ZoomIn, DCSH::zoom_in}, + {"zoom_out", GameFunction::ZoomOut, DCSH::zoom_out}, + {"zoom_max", GameFunction::ZoomMax, DCSH::zoom_max}, + {"zoom_min", GameFunction::ZoomMin, DCSH::zoom_min}, + {"zoom_reset", GameFunction::ZoomReset, DCSH::zoom_reset}, + {"ui_scaleup", GameFunction::UiScaleUp, DCSH::ui_scaleup}, + {"ui_scaledown", GameFunction::UiScaleDown, DCSH::ui_scaledown}, + {"ui_scaleshipup", GameFunction::UiShipScaleUp, DCSH::ui_scaleshipup}, + {"ui_scaleshipdown", GameFunction::UiShipScaleDown, DCSH::ui_scaleshipdown}, + {"ui_scaleviewerup", GameFunction::UiViewerScaleUp, DCSH::ui_scaleviewerup}, + {"ui_scaleviewerdown", GameFunction::UiViewerScaleDown, DCSH::ui_scaleviewerdown}, + {"log_debug", GameFunction::LogLevelDebug, DCSH::log_debug}, + {"log_trace", GameFunction::LogLevelTrace, DCSH::log_trace}, + {"log_info", GameFunction::LogLevelInfo, DCSH::log_info}, + {"log_warn", GameFunction::LogLevelWarn, DCSH::log_warn}, + {"log_error", GameFunction::LogLevelError, DCSH::log_error}, + {"log_off", GameFunction::LogLevelOff, DCSH::log_off}, + {"restart", GameFunction::Restart, DCSH::restart}, + {"show_awayteam", GameFunction::ShowAwayTeam, DCSH::show_awayteam}, + {"show_gifts", GameFunction::ShowGifts, DCSH::show_gifts}, + {"show_artifacts", GameFunction::ShowArtifacts, DCSH::show_artifacts}, + {"show_commander", GameFunction::ShowCommander, DCSH::show_commander}, + {"show_daily", GameFunction::ShowDaily, DCSH::show_daily}, + {"show_events", GameFunction::ShowEvents, DCSH::show_events}, + {"show_events_native", + GameFunction::NativeShortcutEvents, + DCSH::show_events_native, + Tier::kAlways, + {"native_shortcut_events"}}, + {"show_exocomp", GameFunction::ShowExoComp, DCSH::show_exocomp}, + {"show_factions", GameFunction::ShowFactions, DCSH::show_factions}, + {"show_inventory", GameFunction::ShowInventory, DCSH::show_inventory}, + {"show_missions", GameFunction::ShowMissions, DCSH::show_missions}, + {"show_research", GameFunction::ShowResearch, DCSH::show_research}, + {"show_scrapyard", GameFunction::ShowScrapYard, DCSH::show_scrapyard}, + {"show_settings", GameFunction::ShowSettings, DCSH::show_settings}, + {"toggle_shortcut_hints", GameFunction::ToggleShortcutHints, DCSH::toggle_shortcut_hints}, + {"show_officers", GameFunction::ShowOfficers, DCSH::show_officers}, + {"show_officerpresets", GameFunction::ShowOfficerPresets, DCSH::show_officerpresets}, + {"show_officerswap", GameFunction::ShowOfficerSwap, DCSH::show_officerswap}, + {"show_qtrials", GameFunction::ShowQTrials, DCSH::show_qtrials}, + {"show_refinery", GameFunction::ShowRefinery, DCSH::show_refinery}, + {"show_ships", GameFunction::ShowShips, DCSH::show_ships}, + {"show_shipconstruction", GameFunction::ShowShipConstruction, DCSH::show_shipconstruction}, + {"show_shipswap", GameFunction::ShowShipSwap, DCSH::show_shipswap}, + {"show_shields", GameFunction::ShowShields, DCSH::show_shields}, + {"show_battlelogs", GameFunction::ShowBattlelogs, DCSH::show_battlelogs}, + {"show_stationexterior", GameFunction::ShowStationExterior, DCSH::show_stationexterior}, + {"show_stationinterior", GameFunction::ShowStationInterior, DCSH::show_stationinterior}, + {"show_haven", GameFunction::ShowHaven, DCSH::show_haven}, + {"toggle_queue", GameFunction::ToggleQueue, DCSH::toggle_queue}, + {"toggle_instant_warp", GameFunction::ToggleAutoConfirmInstantWarp, DCSH::toggle_instant_warp}, + {"show_alliance", GameFunction::ShowAlliance, DCSH::show_alliance, Tier::kExtended}, + {"show_alliance_help", GameFunction::ShowAllianceHelp, DCSH::show_alliance_help, Tier::kExperimental}, + {"show_alliance_armada", GameFunction::ShowAllianceArmada, DCSH::show_alliance_armada, Tier::kExperimental}, + {"show_bookmarks", GameFunction::ShowBookmarks, DCSH::show_bookmarks, Tier::kExtended}, + {"show_lookup", GameFunction::ShowLookup, DCSH::show_lookup, Tier::kExperimental}, + {"set_zoom_preset1", GameFunction::SetZoomPreset1, DCSH::set_zoom_preset1, Tier::kExtended}, + {"set_zoom_preset2", GameFunction::SetZoomPreset2, DCSH::set_zoom_preset2, Tier::kExtended}, + {"set_zoom_preset3", GameFunction::SetZoomPreset3, DCSH::set_zoom_preset3, Tier::kExtended}, + {"set_zoom_preset4", GameFunction::SetZoomPreset4, DCSH::set_zoom_preset4, Tier::kExtended}, + {"set_zoom_preset5", GameFunction::SetZoomPreset5, DCSH::set_zoom_preset5, Tier::kExtended}, + {"set_zoom_default", GameFunction::SetZoomDefault, DCSH::set_zoom_default, Tier::kExtended}, + {"toggle_preview_locate", GameFunction::TogglePreviewLocate, DCSH::toggle_preview_locate, Tier::kExtended}, + {"toggle_preview_recall", GameFunction::TogglePreviewRecall, DCSH::toggle_preview_recall, Tier::kExtended}, + {"toggle_cargo_default", GameFunction::ToggleCargoDefault, DCSH::toggle_cargo_default, Tier::kExtended}, + {"toggle_cargo_player", GameFunction::ToggleCargoPlayer, DCSH::toggle_cargo_player, Tier::kExtended}, + {"toggle_cargo_station", GameFunction::ToggleCargoStation, DCSH::toggle_cargo_station, Tier::kExtended}, + {"toggle_cargo_hostile", GameFunction::ToggleCargoHostile, DCSH::toggle_cargo_hostile, Tier::kExtended}, + {"toggle_cargo_armada", GameFunction::ToggleCargoArmada, DCSH::toggle_cargo_armada, Tier::kExtended}, +}; + +// The value a setting resolves to before parsing: the configured string, an alias, or the default. +struct Source { + std::string value; + std::string key; // where the value came from + bool from_config = false; + bool valid_type = true; + + std::string Label(std::string_view setting) const + { + if (!valid_type) + return "invalid"; + if (!from_config) + return "default"; + return key == setting ? "config" : "alias:" + key; + } +}; + +Source Lookup(const toml::table* shortcuts, std::string_view key, std::string_view fallback) +{ + const auto* node = shortcuts ? shortcuts->get(key) : nullptr; + if (!node) + return {std::string(fallback), std::string(key), false, true}; + if (auto value = node->value()) + return {*value, std::string(key), true, true}; + return {"", std::string(key), true, false}; +} + +Source Resolve(const toml::table* shortcuts, const Setting& setting) +{ + auto source = Lookup(shortcuts, setting.key, setting.fallback); + for (const auto alias : setting.aliases) { + if (alias.empty() || !shortcuts || !shortcuts->contains(alias)) + continue; + if (source.from_config) { + spdlog::warn("Ignoring deprecated shortcut alias [shortcuts].{} because [shortcuts].{} is set.", alias, + setting.key); + continue; + } + spdlog::warn("Deprecated shortcut alias [shortcuts].{} is supported for compatibility; use [shortcuts].{}.", alias, + setting.key); + return Lookup(shortcuts, alias, setting.fallback); + } + return source; +} + +void LoadSetting(const toml::table* shortcuts, toml::table& out, toml::table& out_source, const Setting& setting) +{ + auto source = Resolve(shortcuts, setting); + if (!source.valid_type) { + spdlog::error("Invalid shortcut value [shortcuts].{} must be a string; using default for [shortcuts].{}.", + source.key, setting.key); + source = {std::string(setting.fallback), std::string(setting.key), false, true}; + } + + const auto disable = [&](std::string label) { + out.insert_or_assign(setting.key, "NONE"); + out_source.insert_or_assign(setting.key, std::move(label)); + spdlog::debug("shortcut value shortcuts.{} value: NONE", setting.key); + }; + + // An explicit empty string disables the binding, just like NONE. Missing or invalid settings fall back to + // their defaults. + const auto value = AsciiStrToUpper(StripAsciiWhitespace(source.value)); + if (value.empty() || value == "NONE") + return disable(source.Label(setting.key)); + + bool bound = false; + for (const auto& text : StrSplit(value, '|')) { + if (auto chord = KeyChord::Parse(text); chord.key != KeyCode::None) { + Shortcut::Bind(setting.action, std::move(chord)); + bound = true; + } else { + spdlog::warn("Invalid shortcut token [shortcuts].{} token='{}' value='{}'; ignoring token.", source.key, text, + source.value); + } + } + + if (!bound) { + if (source.from_config) { + spdlog::error("No valid shortcut tokens for [shortcuts].{} value='{}'; using default for [shortcuts].{}.", + source.key, source.value, setting.key); + return LoadSetting(nullptr, out, out_source, setting); + } + spdlog::error("Default shortcut [shortcuts].{} value='{}' has no valid tokens; action disabled.", setting.key, + source.value); + return disable("invalid"); + } + + const auto described = Shortcut::Describe(setting.action); + out.insert_or_assign(setting.key, described); + out_source.insert_or_assign(setting.key, source.Label(setting.key)); + spdlog::debug("shortcut value shortcuts.{} value: {}", setting.key, described); +} +} // namespace + +KeyCode ParseKeyName(std::string_view name) +{ + const auto it = kKeyNames.find(AsciiStrToUpper(name)); + return it == kKeyNames.end() ? KeyCode::None : it->second; +} + +bool IsModifierKey(KeyCode key) +{ + switch (key) { + case KeyCode::LeftAlt: + case KeyCode::LeftCommand: + case KeyCode::LeftControl: + case KeyCode::AltGr: + case KeyCode::LeftShift: + case KeyCode::LeftWindows: + case KeyCode::RightAlt: + case KeyCode::RightCommand: + case KeyCode::RightControl: + case KeyCode::RightShift: + case KeyCode::RightWindows: + return true; + default: + return false; + } +} + +KeyChord KeyChord::Parse(std::string_view text) +{ + KeyChord chord; + for (auto& token : StrSplit(AsciiStrToUpper(text), '-')) { + if (auto group = ModifierGroup(token)) { + chord.modifiers.push_back(std::move(*group)); + chord.tokens.push_back(std::move(token)); + } else if (const auto key = ParseKeyName(token); key != KeyCode::None) { + chord.key = key; + chord.tokens.push_back(std::move(token)); + } + } + return chord; +} + +std::string KeyChord::Text() const +{ + std::string text; + for (const auto& token : tokens) { + if (!text.empty()) + text += '-'; + text += token; + } + return text; +} + +std::string KeyChord::Hint() const +{ + std::string hint; + for (size_t i = 0; i < tokens.size(); ++i) + hint += CompactToken(tokens[i], i + 1 == tokens.size()); + return hint; +} + +namespace Shortcut +{ +void Load(const toml::table& config, toml::table& parsed, bool extended, bool experimental) +{ + const auto* shortcuts = config["shortcuts"].as_table(); + auto& out = *parsed.emplace("shortcuts").first->second.as_table(); + auto& source = *parsed.emplace("shortcuts_source").first->second.as_table(); + + for (const auto& setting : kSettings) { + if ((setting.tier == Tier::kExtended && !extended) + || (setting.tier == Tier::kExperimental && !(extended && experimental))) + continue; + LoadSetting(shortcuts, out, source, setting); + } +} + +void Bind(GameFunction action, KeyChord chord) +{ bindings[action].push_back(std::move(chord)); } + +void Clear() +{ + for (auto& chords : bindings) + chords.clear(); +} + +bool HasBinding(GameFunction action) +{ + return std::ranges::any_of(bindings[action], [](const auto& chord) { return chord.key != KeyCode::None; }); +} + +bool IsPressed(GameFunction action) +{ return Matches(action); } + +bool IsDown(GameFunction action) +{ return Matches(action); } + +std::string Describe(GameFunction action) +{ + std::string text; + for (const auto& chord : bindings[action]) { + if (!text.empty()) + text += " | "; + text += chord.Text(); + } + return text; +} + +std::string Hint(GameFunction action) +{ + // The game's badges have room for one binding. + return bindings[action].empty() ? std::string{} : bindings[action].front().Hint(); +} +} // namespace Shortcut diff --git a/mods/src/patches/shortcuts.h b/mods/src/patches/shortcuts.h new file mode 100644 index 000000000..6cebfaf93 --- /dev/null +++ b/mods/src/patches/shortcuts.h @@ -0,0 +1,166 @@ +#pragma once + +// Configurable shortcuts: the actions they trigger, parsing [shortcuts] from the settings file, and +// matching the parsed key chords against this frame's input. + +#include + +#include + +#include +#include +#include + +enum GameFunction { + MoveLeft, + MoveRight, + MoveUp, + MoveDown, + SelectChatAlliance, + SelectChatGlobal, + SelectChatPrivate, + SelectShip1, + SelectShip2, + SelectShip3, + SelectShip4, + SelectShip5, + SelectShip6, + SelectShip7, + SelectShip8, + SelectCurrent, + ShowAlliance, + ShowAllianceArmada, + ShowAllianceHelp, + ShowArtifacts, + ShowOfficers, + ShowOfficerPresets, + ShowCommander, + ShowRefinery, + ShowQTrials, + ShowBookmarks, + ShowLookup, + ShowExoComp, + ShowFactions, + ShowGifts, + ShowDaily, + ShowAwayTeam, + ShowMissions, + ShowResearch, + ShowScrapYard, + ShowShips, + ShowInventory, + ShowStationInterior, + ShowHaven, + ShowStationExterior, + ShowGalaxy, + NativeShortcutGalaxy, + ShowSystem, + ShowChat, + ShowChatSide1, + ShowChatSide2, + ShowEvents, + NativeShortcutEvents, + ShowSettings, + ToggleShortcutHints, + ZoomPreset1, + ZoomPreset2, + ZoomPreset3, + ZoomPreset4, + ZoomPreset5, + ZoomIn, + ZoomOut, + ZoomMin, + ZoomMax, + ZoomReset, + UiScaleUp, + UiScaleDown, + UiShipScaleUp, + UiShipScaleDown, + UiViewerScaleUp, + UiViewerScaleDown, + ActionPrimary, + ActionSecondary, + ActionQueue, + ActionQueueClear, + ActionView, + ActionRecall, + ActionRecallCancel, + ActionRepair, + SetZoomPreset1, + SetZoomPreset2, + SetZoomPreset3, + SetZoomPreset4, + SetZoomPreset5, + SetZoomDefault, + DisableHotKeys, + EnableHotKeys, + ToggleQueue, + ToggleAutoConfirmInstantWarp, + TogglePreviewLocate, + TogglePreviewRecall, + ToggleCargoDefault, + ToggleCargoPlayer, + ToggleCargoStation, + ToggleCargoHostile, + ToggleCargoArmada, + LogLevelDebug, + LogLevelInfo, + LogLevelTrace, + LogLevelError, + LogLevelWarn, + LogLevelOff, + Restart, + Quit, + FocusSearch, + ShowShipConstruction, + ShowOfficerSwap, + ShowShipSwap, + ShowShields, + ShowBattlelogs, + + Max +}; + +// One key with its required modifiers, such as CTRL-SHIFT-F1. +struct KeyChord { + KeyCode key = KeyCode::None; + // Each group is satisfied by any one of its keys: SHIFT is {LeftShift, RightShift}, LSHIFT is {LeftShift}. + std::vector> modifiers; + // The upper-cased tokens as written, for display: {"CTRL", "SHIFT", "F1"}. + std::vector tokens; + + // Parses one chord. Unknown tokens are ignored; a chord without a non-modifier key has key == None. + static KeyChord Parse(std::string_view text); + + // "CTRL-SHIFT-F1" + std::string Text() const; + // Compact form for the game's keybind badges: "^+F1". + std::string Hint() const; +}; + +// Maps a key name from the settings file ("PGUP", "F1", "MOUSE1", "=") to a Unity key code. +KeyCode ParseKeyName(std::string_view name); +bool IsModifierKey(KeyCode key); + +namespace Shortcut +{ +// Reads every [shortcuts] entry from `config`, binds it, and records the effective bindings and where each came +// from in `parsed` ([shortcuts] and [shortcuts_source]). `extended` and `experimental` gate the optional actions. +void Load(const toml::table& config, toml::table& parsed, bool extended, bool experimental); + +// Adds a binding directly. Load uses this; tests use it to set up bindings. +void Bind(GameFunction action, KeyChord chord); +void Clear(); + +bool HasBinding(GameFunction action); +// Any binding of `action` is held with its modifiers. A binding without modifiers needs none held; one with +// modifiers still fires when extra modifiers are held (CTRL-F1 fires under CTRL+SHIFT). +bool IsPressed(GameFunction action); +// Any binding of `action` went down this frame with its modifiers held. +bool IsDown(GameFunction action); + +// "CTRL-G | F8" +std::string Describe(GameFunction action); +// The badge text of the first binding, or empty when unbound. +std::string Hint(GameFunction action); +} // namespace Shortcut diff --git a/tests/chat_startup.cc b/tests/chat_startup.cc deleted file mode 100644 index 32daffcb6..000000000 --- a/tests/chat_startup.cc +++ /dev/null @@ -1,27 +0,0 @@ -#include "patches/chat_startup.h" -#include -#include - -int main() -{ - ChatStartup::Gate gate; - // Slow initial load must never open, regardless of elapsed wall time. - assert(!gate.Tick(false, false, 0)); - assert(!gate.Tick(false, false, 100)); - assert(!gate.Tick(true, false, 101)); - assert(!gate.Tick(true, false, 103.99)); - // A transition or lost readiness restarts the full settling period. - assert(!gate.Tick(false, false, 104)); - assert(!gate.Tick(true, false, 110)); - assert(!gate.Tick(true, false, 112.99)); - assert(gate.Tick(true, false, 113)); - assert(!gate.Tick(true, false, 200)); // User closes chat: do not reopen. - gate = {}; // Login/reload permits one new open. - assert(!gate.Tick(true, false, 201)); - assert(gate.Tick(true, false, 204)); - gate = {}; - assert(!gate.Tick(false, true, 205)); // Manual chat wins, even before readiness. - assert(!gate.Tick(true, false, 300)); - assert(!gate.Tick(true, false, 303)); - std::cout << "Chat startup readiness regression tests passed\n"; -} diff --git a/tests/mod_logic.cc b/tests/mod_logic.cc new file mode 100644 index 000000000..15d7805e0 --- /dev/null +++ b/tests/mod_logic.cc @@ -0,0 +1,222 @@ +// Settings parsing and shortcut matching, run against production code with Unity input replaced by a fake. +#include "defaultconfig.h" +#include "patches/chat_startup.h" +#include "patches/input.h" +#include "patches/ship_shortcut_hint.h" +#include "patches/shortcuts.h" +#include "str_utils.h" + +#include + +#include +#include +#include +#include + +namespace +{ +std::array(KeyCode::Max)> held{}; +KeyCode claimed = KeyCode::None; +int failures = 0; + +void Check(bool condition, const char* what, std::source_location where = std::source_location::current()) +{ + if (!condition) { + std::cerr << where.file_name() << ":" << where.line() << ": " << what << '\n'; + ++failures; + } +} + +void Hold(std::initializer_list keys) +{ + held.fill(false); + for (auto key : keys) + held[static_cast(key)] = true; +} + +toml::table Load(std::string_view settings, bool extended = true, bool experimental = false) +{ + Shortcut::Clear(); + toml::table parsed; + Shortcut::Load(toml::parse(settings), parsed, extended, experimental); + return parsed; +} + +std::string Value(const toml::table& parsed, std::string_view section, std::string_view key) +{ + return parsed[section][key].value_or(std::string("")); +} + +void ShipHints() +{ + Shortcut::Clear(); + // A locked dock F leaves native G at the sixth selectable position (the reported 1,2,3,4,5,7 layout). + const std::array gapped_fleets{0, 1, 2, 3, 4, 6}; + auto ship_hint = [&](GameFunction action) { + return ShipShortcutHintAction(action, gapped_fleets.size(), [&](std::size_t i) { return gapped_fleets[i]; }); + }; + Check(ship_hint(GameFunction::SelectShip7) == GameFunction::SelectShip6, + "Native ship G must advertise the sixth mod shortcut when dock F is locked"); + Check(ship_hint(GameFunction::SelectShip5) == GameFunction::SelectShip5, "Earlier ship hints must stay unchanged"); + Check(ship_hint(GameFunction::SelectShip6) == GameFunction::Max, "Missing dock must not advertise a shortcut"); + Check(ship_hint(GameFunction::ShowGalaxy) == GameFunction::ShowGalaxy, "Non-ship hint mapping changed"); + Check(ShipShortcutHintAction(GameFunction::SelectShip1, 0, [](std::size_t) { return 0; }) == GameFunction::Max, + "Unavailable fleet context must not advertise a guessed ship shortcut"); + for (int index = 0; index < 8; ++index) { + auto action = static_cast(GameFunction::SelectShip1 + index); + Check(ShipShortcutHintAction(action, 8, [](std::size_t i) { return static_cast(i); }) == action, + "Contiguous eight-dock hints must keep their original shortcuts"); + } + const std::array reordered_fleets{7, 0, 2, -1, 6, 4, 1, 3, 5}; + auto reordered_hint = [&](GameFunction action) { + return ShipShortcutHintAction(action, reordered_fleets.size(), [&](std::size_t i) { return reordered_fleets[i]; }); + }; + Check(reordered_hint(GameFunction::SelectShip8) == GameFunction::SelectShip1, "Ship hints must follow bar order"); + Check(reordered_hint(GameFunction::SelectShip7) == GameFunction::SelectShip5, + "Null fleet entries must preserve the positions passed to RequestSelect"); + Check(reordered_hint(GameFunction::SelectShip6) == GameFunction::Max, + "Fleet beyond the eight supported mod shortcuts must not advertise a shortcut"); + Check(Shortcut::Hint(ship_hint(GameFunction::SelectShip7)).empty(), + "Unbound ship selection must not advertise a working shortcut"); + Shortcut::Bind(GameFunction::SelectShip6, KeyChord::Parse("CTRL-F8")); + Check(Shortcut::Hint(ship_hint(GameFunction::SelectShip7)) == "^F8", + "Gapped ship hint must use the actual custom binding, not a hardcoded number"); +} + +void StringHelpers() +{ + Check(StrSplit(" a, ,b ,", ',') == std::vector{"a", "b"}, "StrSplit trims and drops empty tokens"); + Check(StrSplit("", ',').empty(), "StrSplit of nothing"); + // A view into a longer string must not pick up the text after it. + constexpr std::string_view padded = " warp "; + Check(AsciiStrToUpper(StripAsciiWhitespace(padded)) == "WARP", "upper-casing a trimmed view"); + Check(AsciiStrToLower("FeDeRaTiOn") == "federation", "lower-casing"); +} + +void Chords() +{ + const auto chord = KeyChord::Parse("ctrl-shift-f1"); + Check(chord.key == KeyCode::F1, "chord key"); + Check(chord.modifiers.size() == 2, "chord modifiers"); + Check(chord.Text() == "CTRL-SHIFT-F1", "chord text"); + Check(chord.Hint() == "^+F1", "chord badge"); + Check(KeyChord::Parse("SHIFT-+").Hint() == "+PLS", "plus key as the primary key"); + Check(KeyChord::Parse("CTRL").key == KeyCode::None, "modifier alone has no key"); + Check(KeyChord::Parse("NONSENSE").key == KeyCode::None, "unknown key"); +} + +void Loading() +{ + auto parsed = Load(R"( + [shortcuts] + show_galaxy = "CTRL-G | F8" + show_system = "" + show_events = "BOGUS" + quit = 42 + set_hotkeys_disble = "F3" + )"); + Check(Shortcut::Describe(GameFunction::ShowGalaxy) == "CTRL-G | F8", "both alternatives bound"); + Check(Shortcut::Hint(GameFunction::ShowGalaxy) == "^G", "badge shows the first alternative"); + Check(Value(parsed, "shortcuts", "show_galaxy") == "CTRL-G | F8", "vars records the bindings"); + Check(Value(parsed, "shortcuts_source", "show_galaxy") == "config", "source is the settings file"); + + Check(!Shortcut::HasBinding(GameFunction::ShowSystem), "an empty string disables the binding"); + Check(Value(parsed, "shortcuts", "show_system") == "NONE", "disabled binding recorded as NONE"); + + Check(Shortcut::Describe(GameFunction::ShowEvents) == DefaultConfig::Shortcuts::show_events, + "a binding with no valid key falls back to the default"); + Check(Value(parsed, "shortcuts_source", "show_events") == "default", "fallback recorded as default"); + Check(Shortcut::Describe(GameFunction::Quit) == DefaultConfig::Shortcuts::quit, "a non-string falls back too"); + + Check(Shortcut::Describe(GameFunction::DisableHotKeys) == "F3", "deprecated alias still binds"); + Check(Value(parsed, "shortcuts_source", "set_hotkeys_disabled") == "alias:set_hotkeys_disble", "alias source"); + + Check(Shortcut::HasBinding(GameFunction::ShowBookmarks), "extended shortcuts load when extended"); + Check(!Shortcut::HasBinding(GameFunction::ShowLookup), "experimental shortcuts need experimental"); + parsed = Load("", false); + Check(!Shortcut::HasBinding(GameFunction::ShowBookmarks), "extended shortcuts are skipped otherwise"); + Check(Value(parsed, "shortcuts", "show_bookmarks") == "", "skipped shortcuts are not recorded"); +} + +void Matching() +{ + Load(""); // defaults: move_down = "S|DOWN", toggle_shortcut_hints = "HOME" + + Hold({KeyCode::DownArrow}); + Check(Shortcut::IsPressed(GameFunction::MoveDown), "default movement key"); + for (auto modifier : {KeyCode::LeftControl, KeyCode::RightShift, KeyCode::LeftAlt, KeyCode::LeftCommand}) { + Hold({KeyCode::DownArrow, modifier}); + Check(!Shortcut::IsPressed(GameFunction::MoveDown), "a modified chord must not trigger the plain binding"); + } + Hold({}); + Check(!Shortcut::IsPressed(GameFunction::MoveDown), "released key"); + + // A modified movement binding must not claim its own direction; other actions keep the arrow until released. + Shortcut::Bind(GameFunction::MoveDown, KeyChord::Parse("CTRL-DOWN")); + Shortcut::Bind(GameFunction::ToggleShortcutHints, KeyChord::Parse("CTRL-DOWN")); + Hold({KeyCode::LeftControl, KeyCode::DownArrow}); + claimed = KeyCode::None; + Check(Shortcut::IsDown(GameFunction::MoveDown), "explicit movement chord"); + Check(claimed == KeyCode::None, "movement claimed its own direction"); + Check(Shortcut::IsDown(GameFunction::ToggleShortcutHints) && claimed == KeyCode::DownArrow, + "other chords claim the arrow"); + held.fill(false); +} +} // namespace + +// The fake Unity input that the shortcut code reads. +namespace Key +{ +bool Pressed(KeyCode key) +{ + return held[static_cast(key)]; +} +bool Down(KeyCode key) +{ + return Pressed(key); +} +bool IsModified() +{ + for (auto key : {KeyCode::LeftControl, KeyCode::RightControl, KeyCode::LeftShift, KeyCode::RightShift, + KeyCode::LeftAlt, KeyCode::RightAlt, KeyCode::LeftCommand, KeyCode::RightCommand}) + if (Pressed(key)) + return true; + return false; +} +void ClaimDirectionalInput(KeyCode key) +{ + claimed = key; +} +} // namespace Key + +void ChatStartupGate() +{ + ChatStartup::Gate gate; + // A slow initial load never opens, whatever the elapsed wall time. + Check(!gate.Tick(false, false, 0) && !gate.Tick(false, false, 100), "chat gate waits while not ready"); + Check(!gate.Tick(true, false, 101) && !gate.Tick(true, false, 103.99), "chat gate waits three seconds"); + // A transition or lost readiness restarts the full settling period. + Check(!gate.Tick(false, false, 104) && !gate.Tick(true, false, 110) && !gate.Tick(true, false, 112.99), + "lost readiness restarts the chat gate"); + Check(gate.Tick(true, false, 113), "chat gate opens after three ready seconds"); + Check(!gate.Tick(true, false, 200), "chat gate opens once; a closed chat is not reopened"); + gate = {}; // Login or reload permits one new open. + Check(!gate.Tick(true, false, 201) && gate.Tick(true, false, 204), "a reset chat gate opens again"); + gate = {}; + Check(!gate.Tick(false, true, 205) && !gate.Tick(true, false, 300) && !gate.Tick(true, false, 303), + "chat already open consumes the gate, even before readiness"); +} + +int main() +{ + ShipHints(); + StringHelpers(); + Chords(); + Loading(); + Matching(); + ChatStartupGate(); + if (failures) + return EXIT_FAILURE; + std::cout << "mod logic tests passed\n"; + return EXIT_SUCCESS; +} diff --git a/tests/run-shortcut-hint-cache.ps1 b/tests/run-shortcut-hint-cache.ps1 deleted file mode 100644 index 8e9c40bb0..000000000 --- a/tests/run-shortcut-hint-cache.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -# Run after zig build mods il2cpp -Dtarget=x86_64-windows-msvc -Dmoddbg=true -Dlto=false. -# Uses production MapKey/ModifierKey objects from mods.lib; does not launch or modify the game. -[CmdletBinding()] -param() - -$ErrorActionPreference = 'Stop' -$repoRoot = Split-Path -Parent $PSScriptRoot -Push-Location $repoRoot -try { - if (-not (Test-Path -LiteralPath 'zig-out/lib/mods.lib') -or -not (Test-Path -LiteralPath 'zig-out/lib/il2cpp.lib')) { - throw 'Build the mods and il2cpp targets with -Dlto=false first.' - } - # Use Microsoft link.exe for the Windows static CRT and import libraries. - & clang++ --driver-mode=cl -fuse-ld=link /std:c++latest /EHsc /MT /Imods/src /Ilibs/il2cpp/include ` - tests/shortcut_hint_cache.cc /Fozig-out/bin/shortcut_hint_cache.obj /Fezig-out/bin/shortcut_hint_cache.exe ` - /link /LTCG /LIBPATH:libs/il2cpp zig-out/lib/mods.lib zig-out/lib/il2cpp.lib - if ($LASTEXITCODE -ne 0) { throw 'Shortcut hint test compilation failed.' } - & ./zig-out/bin/shortcut_hint_cache.exe - if ($LASTEXITCODE -ne 0) { throw 'Shortcut hint cache regression failed.' } -} finally { - Pop-Location -} diff --git a/tests/shortcut_hint_cache.cc b/tests/shortcut_hint_cache.cc deleted file mode 100644 index affb21e10..000000000 --- a/tests/shortcut_hint_cache.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Link against the built mods library. Only Unity-dependent Key operations are stubbed; -// MapKey parsing, ModifierKey parsing, binding detection, and hint caching are production code. -#include "defaultconfig.h" -#include "patches/mapkey.h" -#include "patches/ship_shortcut_hint.h" -#include - -#include -#include - -static std::array(KeyCode::Max)> held{}; -static KeyCode claimed = KeyCode::None; - -KeyCode Key::Parse(std::string_view key) -{ - if (key == "F7") return KeyCode::F7; - if (key == "F8") return KeyCode::F8; - if (key == "G") return KeyCode::G; - for (const auto [token, code] : {std::pair{"W", KeyCode::W}, {"A", KeyCode::A}, {"S", KeyCode::S}, - {"D", KeyCode::D}, {"UP", KeyCode::UpArrow}, {"DOWN", KeyCode::DownArrow}, - {"LEFT", KeyCode::LeftArrow}, {"RIGHT", KeyCode::RightArrow}}) - if (key == token) return code; - return KeyCode::None; -} -bool Key::IsModifier(KeyCode) { return false; } -bool Key::Pressed(KeyCode key) { return held[static_cast(key)]; } -bool Key::Down(KeyCode key) { return Key::Pressed(key); } -bool Key::IsModified() { - for (auto key : {KeyCode::LeftControl, KeyCode::RightControl, KeyCode::LeftShift, KeyCode::LeftAlt, KeyCode::LeftCommand}) - if (Key::Pressed(key)) return true; - return false; -} -void Key::ClaimDirectionalInput(KeyCode key) { claimed = key; } - -void Check(bool condition, const char* message) -{ - if (!condition) { - std::cerr << message << '\n'; - std::exit(1); - } -} - -int main() -{ - // A locked dock F leaves native G at the sixth selectable position (the reported 1,2,3,4,5,7 layout). - const std::array gapped_fleets{0, 1, 2, 3, 4, 6}; - auto ship_hint = [&](GameFunction action) { - return ShipShortcutHintAction(action, gapped_fleets.size(), [&](std::size_t i) { return gapped_fleets[i]; }); - }; - Check(ship_hint(GameFunction::SelectShip7) == GameFunction::SelectShip6, - "Native ship G must advertise the sixth mod shortcut when dock F is locked"); - Check(ship_hint(GameFunction::SelectShip5) == GameFunction::SelectShip5, "Earlier ship hints must stay unchanged"); - Check(ship_hint(GameFunction::SelectShip6) == GameFunction::Max, "Missing dock must not advertise a shortcut"); - Check(ship_hint(GameFunction::ShowGalaxy) == GameFunction::ShowGalaxy, "Non-ship hint mapping changed"); - Check(ShipShortcutHintAction(GameFunction::SelectShip1, 0, [](std::size_t) { return 0; }) == GameFunction::Max, - "Unavailable fleet context must not advertise a guessed ship shortcut"); - for (int index = 0; index < 8; ++index) { - auto action = static_cast(GameFunction::SelectShip1 + index); - Check(ShipShortcutHintAction(action, 8, [](std::size_t i) { return static_cast(i); }) == action, - "Contiguous eight-dock hints must keep their original shortcuts"); - } - const std::array reordered_fleets{7, 0, 2, -1, 6, 4, 1, 3, 5}; - auto reordered_hint = [&](GameFunction action) { - return ShipShortcutHintAction(action, reordered_fleets.size(), [&](std::size_t i) { return reordered_fleets[i]; }); - }; - Check(reordered_hint(GameFunction::SelectShip8) == GameFunction::SelectShip1, "Ship hints must follow bar order"); - Check(reordered_hint(GameFunction::SelectShip7) == GameFunction::SelectShip5, - "Null fleet entries must preserve the positions passed to RequestSelect"); - Check(reordered_hint(GameFunction::SelectShip6) == GameFunction::Max, - "Fleet beyond the eight supported mod shortcuts must not advertise a shortcut"); - Check(MapKey::GetShortcutHint(ship_hint(GameFunction::SelectShip7)).empty(), - "Unbound ship selection must not advertise a working shortcut"); - MapKey::AddMappedKey(GameFunction::SelectShip6, MapKey::Parse("CTRL-F8")); - MapKey::CacheShortcutHints(); - Check(MapKey::GetShortcutHint(ship_hint(GameFunction::SelectShip7)) == "^F8", - "Gapped ship hint must use the actual custom binding, not a hardcoded number"); - - constexpr auto toggle = GameFunction::ToggleShortcutHints; - constexpr auto galaxy = GameFunction::ShowGalaxy; - Check(!MapKey::HasBinding(toggle), "Absent binding must not enable hints"); - - // Match config registration: only keys with a primary key are accepted. - for (const auto* text : {"NONE", "", "INVALID", "CTRL"}) { - auto parsed = MapKey::Parse(text); - Check(parsed.Key == KeyCode::None, "Disabled/invalid binding acquired a key"); - if (parsed.Key != KeyCode::None) MapKey::AddMappedKey(toggle, std::move(parsed)); - Check(!MapKey::HasBinding(toggle), "Disabled/invalid binding enabled hints"); - } - - MapKey::AddMappedKey(galaxy, MapKey::Parse("CTRL-G")); - MapKey::AddMappedKey(galaxy, MapKey::Parse("F8")); - Check(MapKey::GetShortcutHint(galaxy).empty(), "Parsing must not populate hint cache"); - Check(MapKey::GetShortcuts(galaxy) == "CTRL-G | F8", "Normal shortcut labels changed"); - - // The toggle may be parsed after bindings whose badges it enables. - MapKey::AddMappedKey(toggle, MapKey::Parse("SHIFT-F7")); - Check(MapKey::HasBinding(toggle), "Valid chord must enable hints"); - Check(MapKey::GetShortcutHint(toggle).empty(), "Binding registration must not populate hint cache"); - Check(MapKey::GetShortcutHint(galaxy).empty(), "Registering toggle must not format earlier bindings"); - MapKey::CacheShortcutHints(); - Check(MapKey::GetShortcutHint(galaxy) == "^G", "Cache must include earlier binding, first alternative only"); - Check(MapKey::GetShortcutHint(toggle) == "+F7", "Toggle badge missing"); - Check(MapKey::GetShortcutHint(GameFunction::ShowResearch).empty(), "Unbound action acquired a badge"); - MapKey::CacheShortcutHints(); - Check(MapKey::GetShortcutHint(galaxy) == "^G", "Repeated cache preparation changed label"); - // Pan uses the production shortcut matcher: both default alternatives work, - // but adding a modifier must not also match the unmodified movement action. - for (const auto [action, defaults] : { - std::pair{GameFunction::MoveUp, DefaultConfig::Shortcuts::move_up}, - std::pair{GameFunction::MoveDown, DefaultConfig::Shortcuts::move_down}, - std::pair{GameFunction::MoveLeft, DefaultConfig::Shortcuts::move_left}, - std::pair{GameFunction::MoveRight, DefaultConfig::Shortcuts::move_right}}) { - std::string_view remaining = defaults; - while (!remaining.empty()) { - auto end = remaining.find('|'); - auto binding = MapKey::Parse(std::string(remaining.substr(0, end))); - Check(binding.Key != KeyCode::None, "Invalid movement default"); - MapKey::AddMappedKey(action, binding); - held[static_cast(binding.Key)] = true; - Check(MapKey::IsPressed(action), "Default movement key did not match"); - for (auto modifier : {KeyCode::LeftControl, KeyCode::RightControl, KeyCode::LeftShift, - KeyCode::LeftAlt, KeyCode::LeftCommand}) { - held[static_cast(modifier)] = true; - Check(!MapKey::IsPressed(action), "Modified chord leaked into plain movement"); - held[static_cast(modifier)] = false; - } - held[static_cast(binding.Key)] = false; - Check(!MapKey::IsPressed(action), "Released movement key remained active"); - if (end == std::string_view::npos) break; - remaining.remove_prefix(end + 1); - } - } - // A modified movement binding must not claim its own direction. Non-movement - // actions still claim it, preserving the existing hold-until-release guard. - MapKey::AddMappedKey(GameFunction::MoveDown, MapKey::Parse("CTRL-DOWN")); - held[static_cast(KeyCode::LeftControl)] = true; - held[static_cast(KeyCode::DownArrow)] = true; - claimed = KeyCode::None; - Check(MapKey::IsDown(GameFunction::MoveDown) && MapKey::IsPressed(GameFunction::MoveDown), - "Explicit movement chord did not match"); - Check(claimed == KeyCode::None, "Movement claimed and blocked itself"); - MapKey::AddMappedKey(toggle, MapKey::Parse("CTRL-DOWN")); - Check(MapKey::IsDown(toggle) && claimed == KeyCode::DownArrow, "Other chord lost directional ownership"); - held.fill(false); - std::cout << "Shortcut hint and movement binding tests passed\n"; - -} diff --git a/xmake.lua b/xmake.lua index dc452fa2e..2c94b1ad4 100644 --- a/xmake.lua +++ b/xmake.lua @@ -51,9 +51,10 @@ if is_arch("x64", "x86_64") then add_packages("spud") end --- Pure startup gate regression tests; no game process required. -target("chat-startup-tests") +target("mod-logic-tests") set_kind("binary") set_default(false) - add_files("tests/chat_startup.cc") - add_includedirs("mods/src") + set_exceptions("cxx") + add_files("tests/mod_logic.cc", "mods/src/patches/shortcuts.cc") + add_includedirs("mods/src", "libs/il2cpp/include", "third_party/libil2cpp") + add_packages("spdlog", "toml++", "simdutf") From 739cc171f314009498799e112d45a934066db2c9 Mon Sep 17 00:00:00 2001 From: tashcan Date: Thu, 15 Oct 2026 16:40:40 -0700 Subject: [PATCH 03/24] Separate file paths from the settings reader File becomes a namespace that parses the command line once instead of a class re-checking an init flag in every accessor. Path joining and legacy-file migration move with it, and the settings reader shrinks to reading values through one small reader type. Parsing the command line once fixes -debug and -trace being ignored as last arguments and a doubled directory for -ccm paths on macOS. The macOS Library path is copied before its autorelease pool can free it. Window state and UI scale handlers move to the files that use them. --- mods/src/config.cc | 1619 ++++++------------- mods/src/config.h | 112 +- mods/src/defaultconfig.h | 100 +- mods/src/file.cc | 387 ++--- mods/src/file.h | 93 +- mods/src/file_macos.mm | 15 + mods/src/folder_manager.h | 55 - mods/src/folder_manager.mm | 31 - mods/src/patches/parts/free_resize.cc | 170 -- mods/src/patches/parts/hotkeys.cc | 12 +- mods/src/patches/parts/pinned_ship_click.cc | 11 +- mods/src/patches/parts/sync.cc | 5 +- mods/src/patches/parts/testing.cc | 4 + mods/src/patches/parts/ui_scale.cc | 138 +- mods/src/patches/parts/window.cc | 120 ++ mods/src/patches/patches.cc | 7 +- mods/src/titlelinux.h | 54 - mods/src/titlemac.mm | 36 - mods/src/titlewindows.h | 38 - mods/src/windowtitle.h | 13 - 20 files changed, 992 insertions(+), 2028 deletions(-) create mode 100644 mods/src/file_macos.mm delete mode 100644 mods/src/folder_manager.h delete mode 100644 mods/src/folder_manager.mm delete mode 100644 mods/src/patches/parts/free_resize.cc create mode 100644 mods/src/patches/parts/window.cc delete mode 100644 mods/src/titlelinux.h delete mode 100644 mods/src/titlemac.mm delete mode 100644 mods/src/titlewindows.h delete mode 100644 mods/src/windowtitle.h diff --git a/mods/src/config.cc b/mods/src/config.cc index 410556fd3..83b10721e 100644 --- a/mods/src/config.cc +++ b/mods/src/config.cc @@ -1,26 +1,26 @@ #include "config.h" + +#include "defaultconfig.h" #include "file.h" #include "patches/shortcuts.h" #include "ship_name_match.h" #include "str_utils.h" #include "version.h" + #include -#include #include +#include -#include "defaultconfig.h" #include -#include #include -#include -#include +#include #include #include -#include -#include -#include +#include +namespace +{ namespace DCP = DefaultConfig::Patches; namespace DCA = DefaultConfig::Audio; namespace DCG = DefaultConfig::Graphics; @@ -29,40 +29,9 @@ namespace DCU = DefaultConfig::UI; namespace DCBS = DefaultConfig::Buffs; namespace DCS = DefaultConfig::Sync; namespace DCSC = DefaultConfig::SystemConfig; -namespace DCSH = DefaultConfig::Shortcuts; -namespace -{ -constexpr bool is_all_audio_wildcard(std::string_view value) -{ - return value.size() == 3 && (value[0] == 'a' || value[0] == 'A') && (value[1] == 'l' || value[1] == 'L') - && (value[2] == 'l' || value[2] == 'L'); -} - -static_assert(is_all_audio_wildcard("aLl")); -static_assert(!is_all_audio_wildcard(std::string_view{"All\0suffix", 10})); -static_assert(!is_all_audio_wildcard(std::string_view{"\xffll", 3})); - -struct ToastAudioAlertConfig { - int toast_state; - std::string_view config_name; - std::string_view default_sound; - NotificationSound Config::* config_member; -}; - -constexpr auto kToastAudioAlerts = std::to_array({ - {ToastState::Victory, "alert_victory", DCA::alert_victory, &Config::alert_victory}, - {ToastState::Defeat, "alert_defeat", DCA::alert_defeat, &Config::alert_defeat}, - {ToastState::ArmadaCreated, "alert_armada_created", DCA::alert_armada_created, &Config::alert_armada_created}, - {ToastState::ArmadaBattleWon, "alert_armada_battle_won", DCA::alert_armada_battle_won, - &Config::alert_armada_battle_won}, - {ToastState::ArmadaBattleLost, "alert_armada_battle_lost", DCA::alert_armada_battle_lost, - &Config::alert_armada_battle_lost}, -}); -} // namespace - -static const eastl::tuple bannerTypes[] = { - {"All", ToastState::All}, +// Names of ToastState values as written in disabled_banner_types and notify_banner_types. +constexpr std::pair kBannerTypes[] = { {"Standard", ToastState::Standard}, {"FactionWarning", ToastState::FactionWarning}, {"FactionLevelUp", ToastState::FactionLevelUp}, @@ -104,517 +73,174 @@ static const eastl::tuple bannerTypes[] = { {"SurgeTimeLeft", ToastState::SurgeTimeLeft}, }; -bool SyncConfig::enabled(SyncConfig::Type type) const -{ - for (const auto& opt : SyncOptions) { - if (opt.type == type) { - return this->*opt.option; - } - } - - return false; -} - -Config::Config() -{ - Load(); -} - -void Config::Save(const toml::table& config, const std::string_view filename, bool apply_warning) -{ - std::ofstream config_file; - - auto config_path = File::MakePath(filename, true); - config_file.open(config_path); - - if (apply_warning) { - char defaultFile[255], configFile[255]; - snprintf(defaultFile, 255, "%s", File::Default()); - snprintf(configFile, 255, "%s", File::Config()); - - config_file << "#######################################################################\n"; - config_file << "#######################################################################\n"; - config_file << "#### ####\n"; - config_file << "#### NOTE: This file is not the configuration file that is used ####\n"; - config_file << "#### by the STFC Community Mod. It is provided to help ####\n"; - config_file << "#### see what configuration is being used by the runtime ####\n"; - config_file << "#### and any desired settings should be copied to the same ####\n"; - config_file << "#### section in: " << defaultFile << "\n"; - config_file << "#### ####\n"; - config_file << "#### Config in: " << configFile << "\n"; - config_file << "#### ####\n"; - config_file << "#######################################################################\n"; - config_file << "#######################################################################\n\n"; - } - - config_file << config; - config_file.close(); -} - -Config& Config::Get() -{ - static Config config; - return config; -} - -NotificationSound Config::NotificationSoundForToast(int toast_state) const -{ - const auto alert = std::ranges::find(kToastAudioAlerts, toast_state, &ToastAudioAlertConfig::toast_state); - return alert == kToastAudioAlerts.end() ? NotificationSound::None : this->*(alert->config_member); -} - -MissionHudVisibility Config::MissionHudButtonVisibility(std::string_view button_name) const -{ - const auto it = this->mission_hud_buttons.find(std::string(button_name)); - return it == this->mission_hud_buttons.end() ? MissionHudVisibility::Auto : it->second; -} - -#if _WIN32 -static HMONITOR lastMonitor = (HMONITOR)-1; -static float dpi = 1.0f; - -HWND Config::WindowHandle() -{ - static HWND hwnd = nullptr; - - if (hwnd == nullptr) { - DWORD processId = GetCurrentProcessId(); - hwnd = GetTopWindow(nullptr); // Start with the first top-level window - - while (hwnd != nullptr) { - DWORD windowProcessId; - GetWindowThreadProcessId(hwnd, &windowProcessId); - - // Check if the window belongs to the current process and is the main window - if (windowProcessId == processId && GetWindow(hwnd, GW_OWNER) == nullptr && IsWindowVisible(hwnd)) { - break; - } - - hwnd = GetNextWindow(hwnd, GW_HWNDNEXT); // Move to the next top-level window - } - } - - return hwnd; -} - -float Config::RefreshDPI() -{ - lastMonitor = (HMONITOR)-1; +template struct Choice { + std::string_view name; + E value; +}; - return Config::GetDPI(); -} +// The first name listed for a value is the one written back; later ones are accepted synonyms. +constexpr Choice kHudVisibility[] = { + {"auto", MissionHudVisibility::Auto}, + {"always", MissionHudVisibility::Always}, + {"never", MissionHudVisibility::Never}, + {"", MissionHudVisibility::Auto}, +}; -float Config::GetDPI() -{ - auto activeWindow = GetActiveWindow(); - HMONITOR monitor = MonitorFromWindow(activeWindow, MONITOR_DEFAULTTONEAREST); - - if (monitor != lastMonitor) { - // Get the logical width and height of the monitor - MONITORINFOEX monitorInfoEx; - monitorInfoEx.cbSize = sizeof(monitorInfoEx); - GetMonitorInfo(monitor, &monitorInfoEx); - auto cxLogical = monitorInfoEx.rcMonitor.right - monitorInfoEx.rcMonitor.left; - auto cyLogical = monitorInfoEx.rcMonitor.bottom - monitorInfoEx.rcMonitor.top; - - // Get the physical width and height of the monitor - DEVMODE devMode; - devMode.dmSize = sizeof(devMode); - devMode.dmDriverExtra = 0; - EnumDisplaySettings(monitorInfoEx.szDevice, ENUM_CURRENT_SETTINGS, &devMode); - auto cxPhysical = devMode.dmPelsWidth; - auto cyPhysical = devMode.dmPelsHeight; - - // Calculate the scaling factor - auto horizontalScale = ((double)cxPhysical / (double)cxLogical); - auto verticalScale = ((double)cyPhysical / (double)cyLogical); - - spdlog::trace("Horizontal scaling: {}", horizontalScale); - spdlog::trace("Vertical scaling: {}", verticalScale); - - dpi = horizontalScale; - lastMonitor = monitor; - } +constexpr Choice kInstantWarp[] = { + {"none", InstantWarpConfirmation::None}, {"warp", InstantWarpConfirmation::Warp}, + {"jump", InstantWarpConfirmation::Jump}, {"left", InstantWarpConfirmation::Warp}, + {"right", InstantWarpConfirmation::Jump}, {"", InstantWarpConfirmation::None}, +}; - return dpi; -} -#else -float Config::RefreshDPI() -{ - return Config::GetDPI(); -} +constexpr Choice kFleetLabelDetail[] = { + {"native", FleetLabelDetail::Native}, {"expanded", FleetLabelDetail::Expanded}, + {"compact", FleetLabelDetail::Compact}, {"threshold", FleetLabelDetail::Threshold}, + {"always", FleetLabelDetail::Expanded}, {"never", FleetLabelDetail::Compact}, + {"custom", FleetLabelDetail::Threshold}, {"auto", FleetLabelDetail::Native}, + {"none", FleetLabelDetail::Native}, {"off", FleetLabelDetail::Native}, + {"", FleetLabelDetail::Native}, +}; -float Config::GetDPI() +std::string MaskToken(const std::string& token) { - return 1.0f; + if (token.size() <= 21) + return token; + auto masked = token; + for (size_t i = 9; i < token.size() - 12; ++i) + if (masked[i] != '-') + masked[i] = '*'; + return masked; } -#endif -void Config::AdjustUiScale(bool scaleUp) +// Reads settings from the user's file and records every effective value in `parsed`, which is written out as the +// runtime vars file so users can see what the mod actually used. +class Reader { - if (this->ui_scale != 0.0f) { - auto old_scale = this->ui_scale; - auto scale_factor = (scaleUp ? 1.0f : -1.0f) * this->ui_scale_step; - auto new_scale = this->ui_scale + scale_factor; - this->ui_scale = std::clamp(new_scale, 0.1f, 2.0f); - - auto dpi = Config::RefreshDPI(); - spdlog::info("UI has been scaled {}, was {}, now {} (unclamped {}) @ {} DPI Scaling", (scaleUp ? "UP" : "DOWN"), - old_scale, this->ui_scale, new_scale, dpi); +public: + Reader(const toml::table& config, toml::table& parsed, bool log) + : config_(config) + , parsed_(parsed) + , log_(log) + { } -} -void Config::AdjustUiViewerScale(bool scaleUp) -{ - if (this->ui_scale_viewer != 0.0f) { - auto old_scale = this->ui_scale_viewer; - auto scale_factor = (scaleUp ? 1.0f : -1.0f) * this->ui_scale_step; - auto new_scale = this->ui_scale_viewer + (scale_factor * 0.25f); - this->ui_scale_viewer = std::clamp(new_scale, 0.1f, 2.0f); - - spdlog::info("UI Viewer has been scaled {}, was {}, now {} (unclamped {})", (scaleUp ? "UP" : "DOWN"), old_scale, - this->ui_scale_viewer, new_scale); - } -} + const toml::table& Config() const + { return config_; } -void Config::AdjustUiShipScale(bool scaleUp) -{ - const auto old_scale = this->ui_scale_ship; - const auto scale_factor = (scaleUp ? 1.0f : -1.0f) * this->ui_scale_step; - const auto new_scale = this->ui_scale_ship + scale_factor; - this->ui_scale_ship = std::clamp(new_scale, 0.1f, 20.0f); - - spdlog::info("System ship models have been scaled {}, was {}, now {} (unclamped {})", (scaleUp ? "UP" : "DOWN"), - old_scale, this->ui_scale_ship, new_scale); - ApplyUiShipScaleToLoadedShips(old_scale, this->ui_scale_ship); -} + toml::table& Section(std::string_view name) + { return *parsed_.emplace(name).first->second.as_table(); } -inline std::string mask_token(const std::string& token) -{ - if (token.size() > 21) { - std::string masked = token; - for (size_t i = 9; i < token.size() - 12; ++i) { - if (masked[i] != '-') { - masked[i] = '*'; - } + // The configured value converted to T, or `fallback` when missing or of the wrong type. TOML integers read fine + // into float settings; fractions into int settings do not. + template T Get(std::string_view section, std::string_view key, T fallback) + { + T value = fallback; + auto node = config_[section][key]; + if (auto read = node.template value()) + value = *read; + else if (node) + spdlog::warn("invalid config value {}.{}; using {}", section, key, fallback); + Record(section, key, value); + return value; + } + + std::string Get(std::string_view section, std::string_view key, const char* fallback) + { return Get(section, key, fallback); } + + // A setting that was renamed: `key`, or `old_key` when only the old name is set. The vars file records `key`. + template + T GetRenamed(std::string_view section, std::string_view key, std::string_view old_key, T fallback) + { + if (!config_[section][key] && config_[section][old_key]) { + T value = fallback; + if (auto read = config_[section][old_key].template value()) + value = *read; + else + spdlog::warn("invalid config value {}.{}; using {}", section, old_key, fallback); + Record(section, key, value); + return value; } - return masked; + return Get(section, key, fallback); } - return token; -} - -std::string get_config_type_as_string(const toml::node_type type) -{ - switch (type) { - case toml::node_type::none: - return "Not-a-node."; - case toml::node_type::table: - return "toml::table."; - case toml::node_type::array: - return "toml::array."; - case toml::node_type::string: - return "toml::value."; - case toml::node_type::integer: - return "toml::value."; - case toml::node_type::floating_point: - return "toml::value."; - case toml::node_type::boolean: - return "toml::value."; - case toml::node_type::date: - return "toml::value."; - case toml::node_type::time: - return "toml::value