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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion mods/src/patches/parts/hotkeys.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "config.h"
#include "patches/ship_shortcut_hint.h"

#include <spud/detour.h>
#include <il2cpp/method_contract.h>
Expand Down Expand Up @@ -266,7 +267,18 @@ void ShortcutKeybindHint_UpdateText_Hook(auto original, void* _this)
return;
}

auto shortcut = MapKey::GetShortcutHint(mapping->primary);
auto hint_action = mapping->primary;
if (hint_action >= GameFunction::SelectShip1 && hint_action <= GameFunction::SelectShip8) {
// Use the same context array as FleetBarViewController::RequestSelect(int, bool), not the native dock index.
auto* bar = ObjectFinder<FleetBarViewController>::Get();
auto* context = bar ? bar->CanvasContext() : nullptr;
auto* fleets = context ? context->FleetData() : nullptr;
hint_action = ShipShortcutHintAction(hint_action, fleets ? fleets->max_length : 0, [fleets](std::size_t position) {
auto* fleet = reinterpret_cast<FleetPlayerData**>(fleets->vector)[position];
return fleet ? fleet->Index : -1;
});
}
auto shortcut = hint_action == GameFunction::Max ? std::string{} : MapKey::GetShortcutHint(hint_action);
if (shortcut.empty() && mapping->fallback != GameFunction::Max) {
shortcut = MapKey::GetShortcutHint(mapping->fallback);
}
Expand Down
22 changes: 22 additions & 0 deletions mods/src/patches/ship_shortcut_hint.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "gamefunctions.h"

#include <cstddef>

// Native ship actions identify FleetPlayerData::Index. Mod selection passes a position in FleetBarContext::FleetData
// to RequestSelect instead; locked docks can leave gaps in the native indices.
template <typename FleetIndexAt>
GameFunction ShipShortcutHintAction(GameFunction native_action, std::size_t fleet_count, FleetIndexAt fleet_index_at)
{
if (native_action < GameFunction::SelectShip1 || native_action > GameFunction::SelectShip8)
return native_action;

const auto fleet_index = native_action - GameFunction::SelectShip1;
constexpr auto shortcut_count = GameFunction::SelectShip8 - GameFunction::SelectShip1 + 1;
for (std::size_t position = 0; position < fleet_count && position < shortcut_count; ++position) {
if (fleet_index_at(position) == fleet_index)
return static_cast<GameFunction>(GameFunction::SelectShip1 + position);
}
return GameFunction::Max;
}
6 changes: 6 additions & 0 deletions mods/src/prime/FleetBarViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ struct FleetBarContext {
}

public:
Il2CppArraySize* FleetData()
{
static auto property = get_class_helper().GetProperty("FleetData");
return property.GetRaw<Il2CppArraySize>(this);
}

void* __get_CurrentFleet()
{
static auto field = get_class_helper().GetProperty("CurrentFleet");
Expand Down
34 changes: 34 additions & 0 deletions tests/shortcut_hint_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// MapKey parsing, ModifierKey parsing, binding detection, and hint caching are production code.
#include "patches/mapkey.h"
#include "defaultconfig.h"
#include "patches/ship_shortcut_hint.h"
#include <array>

#include <cstdlib>
Expand Down Expand Up @@ -41,6 +42,39 @@ void Check(bool condition, const char* message)

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>(GameFunction::SelectShip1 + index);
Check(ShipShortcutHintAction(action, 8, [](std::size_t i) { return static_cast<int>(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");
Expand Down
Loading