Browse Source

access: remove old town NPC tracker code (fixes #7)

Remove the legacy town_npc_nav tracker system which was superseded by
the unified tracker. The old tracker was still firing on PgUp/PgDown
due to hardcoded fallback logic in PressKey().

Changes:
- Remove PgUp/PgDown fallbacks in PressKey() (keep store/chat handling)
- Remove 5 keymapper registrations (ListTownNpcs, PreviousTownNpc,
  NextTownNpc, SpeakSelectedTownNpc, GoToSelectedTownNpc)
- Remove UpdateAutoWalkTownNpc() and ResetAutoWalkTownNpc() calls
- Remove migration code references in options.cpp
- Delete town_npc_nav.cpp and town_npc_nav.hpp
- Update CMakeLists.txt

Town NPC tracking is now handled by the unified tracker's NPCs category.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
pull/8474/head
hidwood 1 month ago
parent
commit
9b695033c0
  1. 1
      Source/CMakeLists.txt
  2. 348
      Source/controls/town_npc_nav.cpp
  3. 19
      Source/controls/town_npc_nav.hpp
  4. 51
      Source/diablo.cpp
  5. 190
      Source/options.cpp

1
Source/CMakeLists.txt

@ -52,7 +52,6 @@ set(libdevilutionx_SRCS
controls/menu_controls.cpp
controls/modifier_hints.cpp
controls/plrctrls.cpp
controls/town_npc_nav.cpp
controls/tracker.cpp
DiabloUI/button.cpp

348
Source/controls/town_npc_nav.cpp

@ -1,348 +0,0 @@
/**
* @file controls/town_npc_nav.cpp
*
* Town NPC navigation for accessibility.
*/
#include "controls/town_npc_nav.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <string>
#include <vector>
#include <fmt/format.h>
#include "controls/accessibility_keys.hpp"
#include "controls/plrctrls.h"
#include "diablo.h"
#include "engine/path.h"
#include "help.h"
#include "levels/gendung.h"
#include "levels/tile_properties.hpp"
#include "multi.h"
#include "options.h"
#include "player.h"
#include "qol/chatlog.h"
#include "stores.h"
#include "towners.h"
#include "utils/language.h"
#include "utils/screen_reader.hpp"
#include "utils/str_cat.hpp"
#include "utils/walk_path_speech.hpp"
namespace devilution {
namespace {
std::vector<int> TownNpcOrder;
int SelectedTownNpc = -1;
int AutoWalkTownNpcTarget = -1;
void ResetTownNpcSelection()
{
TownNpcOrder.clear();
SelectedTownNpc = -1;
}
void RefreshTownNpcOrder(bool selectFirst = false)
{
TownNpcOrder.clear();
if (leveltype != DTYPE_TOWN)
return;
if (MyPlayer == nullptr) {
SelectedTownNpc = -1;
return;
}
const Point playerPosition = MyPlayer->position.future;
for (size_t i = 0; i < GetNumTowners(); ++i) {
const Towner &towner = Towners[i];
if (!IsTownerPresent(towner._ttype))
continue;
if (towner._ttype == TOWN_COW)
continue;
TownNpcOrder.push_back(static_cast<int>(i));
}
if (TownNpcOrder.empty()) {
SelectedTownNpc = -1;
return;
}
std::sort(TownNpcOrder.begin(), TownNpcOrder.end(), [&playerPosition](int a, int b) {
const Towner &townerA = Towners[a];
const Towner &townerB = Towners[b];
const int distanceA = playerPosition.WalkingDistance(townerA.position);
const int distanceB = playerPosition.WalkingDistance(townerB.position);
if (distanceA != distanceB)
return distanceA < distanceB;
return townerA.name < townerB.name;
});
if (selectFirst) {
SelectedTownNpc = TownNpcOrder.front();
return;
}
const auto it = std::find(TownNpcOrder.begin(), TownNpcOrder.end(), SelectedTownNpc);
if (it == TownNpcOrder.end())
SelectedTownNpc = TownNpcOrder.front();
}
void EnsureTownNpcOrder()
{
if (leveltype != DTYPE_TOWN) {
ResetTownNpcSelection();
return;
}
if (TownNpcOrder.empty()) {
RefreshTownNpcOrder(true);
return;
}
if (SelectedTownNpc < 0 || SelectedTownNpc >= static_cast<int>(GetNumTowners())) {
RefreshTownNpcOrder(true);
return;
}
const auto it = std::find(TownNpcOrder.begin(), TownNpcOrder.end(), SelectedTownNpc);
if (it == TownNpcOrder.end())
SelectedTownNpc = TownNpcOrder.front();
}
void SelectTownNpcRelative(int delta)
{
if (!IsTownNpcActionAllowed())
return;
EnsureTownNpcOrder();
if (TownNpcOrder.empty()) {
SpeakText(_("No town NPCs found."), true);
return;
}
auto it = std::find(TownNpcOrder.begin(), TownNpcOrder.end(), SelectedTownNpc);
int currentIndex = (it != TownNpcOrder.end()) ? static_cast<int>(it - TownNpcOrder.begin()) : 0;
const int size = static_cast<int>(TownNpcOrder.size());
int newIndex = (currentIndex + delta) % size;
if (newIndex < 0)
newIndex += size;
SelectedTownNpc = TownNpcOrder[static_cast<size_t>(newIndex)];
SpeakSelectedTownNpc();
}
} // namespace
bool IsTownNpcActionAllowed()
{
return CanPlayerTakeAction()
&& leveltype == DTYPE_TOWN
&& !IsPlayerInStore()
&& !ChatLogFlag
&& !HelpFlag;
}
void SpeakSelectedTownNpc()
{
EnsureTownNpcOrder();
if (SelectedTownNpc < 0 || SelectedTownNpc >= static_cast<int>(GetNumTowners())) {
SpeakText(_("No NPC selected."), true);
return;
}
if (MyPlayer == nullptr) {
SpeakText(_("No NPC selected."), true);
return;
}
const Towner &towner = Towners[SelectedTownNpc];
const Point playerPosition = MyPlayer->position.future;
const int distance = playerPosition.WalkingDistance(towner.position);
std::string msg;
StrAppend(msg, towner.name);
StrAppend(msg, "\n", _("Distance: "), distance);
StrAppend(msg, "\n", _("Position: "), towner.position.x, ", ", towner.position.y);
SpeakText(msg, true);
}
void SelectNextTownNpcKeyPressed()
{
SelectTownNpcRelative(+1);
}
void SelectPreviousTownNpcKeyPressed()
{
SelectTownNpcRelative(-1);
}
void GoToSelectedTownNpcKeyPressed()
{
if (!IsTownNpcActionAllowed())
return;
EnsureTownNpcOrder();
if (SelectedTownNpc < 0 || SelectedTownNpc >= static_cast<int>(GetNumTowners())) {
SpeakText(_("No NPC selected."), true);
return;
}
const Towner &towner = Towners[SelectedTownNpc];
std::string msg;
StrAppend(msg, _("Going to: "), towner.name);
SpeakText(msg, true);
AutoWalkTownNpcTarget = SelectedTownNpc;
UpdateAutoWalkTownNpc();
}
void UpdateAutoWalkTownNpc()
{
if (AutoWalkTownNpcTarget < 0)
return;
if (leveltype != DTYPE_TOWN || IsPlayerInStore() || ChatLogFlag || HelpFlag) {
AutoWalkTownNpcTarget = -1;
return;
}
if (!CanPlayerTakeAction())
return;
if (MyPlayer == nullptr) {
AutoWalkTownNpcTarget = -1;
return;
}
if (MyPlayer->_pmode != PM_STAND)
return;
if (MyPlayer->walkpath[0] != WALK_NONE)
return;
if (MyPlayer->destAction != ACTION_NONE)
return;
if (AutoWalkTownNpcTarget >= static_cast<int>(GetNumTowners())) {
AutoWalkTownNpcTarget = -1;
SpeakText(_("No NPC selected."), true);
return;
}
const Towner &towner = Towners[AutoWalkTownNpcTarget];
if (!IsTownerPresent(towner._ttype) || towner._ttype == TOWN_COW) {
AutoWalkTownNpcTarget = -1;
SpeakText(_("No NPC selected."), true);
return;
}
Player &myPlayer = *MyPlayer;
const Point playerPosition = myPlayer.position.future;
if (playerPosition.WalkingDistance(towner.position) < 2) {
const int townerIdx = AutoWalkTownNpcTarget;
AutoWalkTownNpcTarget = -1;
NetSendCmdLocParam1(true, CMD_TALKXY, towner.position, static_cast<uint16_t>(townerIdx));
return;
}
constexpr size_t MaxAutoWalkPathLength = 512;
std::array<int8_t, MaxAutoWalkPathLength> path;
path.fill(WALK_NONE);
const int steps = FindPath(CanStep, [&myPlayer](Point position) { return PosOkPlayer(myPlayer, position); }, playerPosition, towner.position, path.data(), path.size());
if (steps == 0) {
AutoWalkTownNpcTarget = -1;
std::string error;
StrAppend(error, _("Can't find a path to: "), towner.name);
SpeakText(error, true);
return;
}
// FindPath returns 0 when it fails to find a usable path.
// The player walkpath buffer is MaxPathLengthPlayer, so keep segments strictly shorter.
if (steps < static_cast<int>(MaxPathLengthPlayer)) {
const int townerIdx = AutoWalkTownNpcTarget;
AutoWalkTownNpcTarget = -1;
NetSendCmdLocParam1(true, CMD_TALKXY, towner.position, static_cast<uint16_t>(townerIdx));
return;
}
const int segmentSteps = std::min(steps - 1, static_cast<int>(MaxPathLengthPlayer - 1));
const Point waypoint = PositionAfterWalkPathSteps(playerPosition, path.data(), segmentSteps);
NetSendCmdLoc(MyPlayerId, true, CMD_WALKXY, waypoint);
}
void ResetAutoWalkTownNpc()
{
AutoWalkTownNpcTarget = -1;
}
void ListTownNpcsKeyPressed()
{
if (leveltype != DTYPE_TOWN) {
ResetTownNpcSelection();
SpeakText(_("Not in town."), true);
return;
}
if (IsPlayerInStore())
return;
std::vector<const Towner *> townNpcs;
std::vector<const Towner *> cows;
townNpcs.reserve(Towners.size());
cows.reserve(Towners.size());
if (MyPlayer == nullptr) {
SpeakText(_("No town NPCs found."), true);
return;
}
const Point playerPosition = MyPlayer->position.future;
for (const Towner &towner : Towners) {
if (!IsTownerPresent(towner._ttype))
continue;
if (towner._ttype == TOWN_COW) {
cows.push_back(&towner);
continue;
}
townNpcs.push_back(&towner);
}
if (townNpcs.empty() && cows.empty()) {
ResetTownNpcSelection();
SpeakText(_("No town NPCs found."), true);
return;
}
std::sort(townNpcs.begin(), townNpcs.end(), [&playerPosition](const Towner *a, const Towner *b) {
const int distanceA = playerPosition.WalkingDistance(a->position);
const int distanceB = playerPosition.WalkingDistance(b->position);
if (distanceA != distanceB)
return distanceA < distanceB;
return a->name < b->name;
});
std::string output;
StrAppend(output, _("Town NPCs:"));
for (size_t i = 0; i < townNpcs.size(); ++i) {
StrAppend(output, "\n", i + 1, ". ", townNpcs[i]->name);
}
if (!cows.empty()) {
StrAppend(output, "\n", _("Cows: "), static_cast<int>(cows.size()));
}
RefreshTownNpcOrder(true);
if (SelectedTownNpc >= 0 && SelectedTownNpc < static_cast<int>(GetNumTowners())) {
const Towner &towner = Towners[SelectedTownNpc];
StrAppend(output, "\n", _("Selected: "), towner.name);
StrAppend(output, "\n", _("PageUp/PageDown: select. Home: go. End: repeat."));
}
const std::string_view exitKey = GetOptions().Keymapper.KeyNameForAction("SpeakNearestExit");
if (!exitKey.empty()) {
StrAppend(output, "\n", fmt::format(fmt::runtime(_("Cathedral entrance: press {:s}.")), exitKey));
}
SpeakText(output, true);
}
} // namespace devilution

19
Source/controls/town_npc_nav.hpp

@ -1,19 +0,0 @@
/**
* @file controls/town_npc_nav.hpp
*
* Town NPC navigation for accessibility.
*/
#pragma once
namespace devilution {
void SelectNextTownNpcKeyPressed();
void SelectPreviousTownNpcKeyPressed();
void GoToSelectedTownNpcKeyPressed();
void SpeakSelectedTownNpc();
void ListTownNpcsKeyPressed();
void UpdateAutoWalkTownNpc();
bool IsTownNpcActionAllowed();
void ResetAutoWalkTownNpc();
} // namespace devilution

51
Source/diablo.cpp

@ -43,7 +43,6 @@
#include "controls/keymapper.hpp"
#include "controls/plrctrls.h"
#include "controls/remap_keyboard.h"
#include "controls/town_npc_nav.hpp"
#include "controls/tracker.hpp"
#include "diablo.h"
#include "diablo_msg.hpp"
@ -704,10 +703,6 @@ void PressKey(SDL_Keycode vkey, uint16_t modState)
StorePrior();
} else if (ChatLogFlag) {
ChatLogScrollTop();
} else {
const KeymapperOptions::Action *action = GetOptions().Keymapper.findAction(static_cast<uint32_t>(vkey));
if (action == nullptr || !action->isEnabled())
SelectPreviousTownNpcKeyPressed();
}
return;
case SDLK_PAGEDOWN:
@ -715,10 +710,6 @@ void PressKey(SDL_Keycode vkey, uint16_t modState)
StoreNext();
} else if (ChatLogFlag) {
ChatLogScrollBottom();
} else {
const KeymapperOptions::Action *action = GetOptions().Keymapper.findAction(static_cast<uint32_t>(vkey));
if (action == nullptr || !action->isEnabled())
SelectNextTownNpcKeyPressed();
}
return;
case SDLK_LEFT:
@ -1632,7 +1623,6 @@ void GameLogic()
if (gbProcessPlayers) {
gGameLogicStep = GameLogicStep::ProcessPlayers;
ProcessPlayers();
UpdateAutoWalkTownNpc();
UpdateAutoWalkTracker();
UpdateLowDurabilityWarnings();
}
@ -1773,7 +1763,6 @@ const auto OptionChangeHandlerLanguage = (GetOptions().Language.code.SetValueCha
void CancelAutoWalkInternal()
{
ResetAutoWalkTracker();
ResetAutoWalkTownNpc();
}
} // namespace
@ -1927,46 +1916,6 @@ void InitKeymapActions()
nullptr,
IsGameRunning);
options.Keymapper.AddAction(
"ListTownNpcs",
N_("List town NPCs"),
N_("Speaks a list of town NPCs."),
SDLK_UNKNOWN,
ListTownNpcsKeyPressed,
nullptr,
CanPlayerTakeAction);
options.Keymapper.AddAction(
"PreviousTownNpc",
N_("Previous town NPC"),
N_("Select previous town NPC (speaks)."),
SDLK_UNKNOWN,
SelectPreviousTownNpcKeyPressed,
nullptr,
IsTownNpcActionAllowed);
options.Keymapper.AddAction(
"NextTownNpc",
N_("Next town NPC"),
N_("Select next town NPC (speaks)."),
SDLK_UNKNOWN,
SelectNextTownNpcKeyPressed,
nullptr,
IsTownNpcActionAllowed);
options.Keymapper.AddAction(
"SpeakSelectedTownNpc",
N_("Speak selected town NPC"),
N_("Speaks the currently selected town NPC."),
SDLK_UNKNOWN,
SpeakSelectedTownNpc,
nullptr,
IsTownNpcActionAllowed);
options.Keymapper.AddAction(
"GoToSelectedTownNpc",
N_("Go to selected town NPC"),
N_("Walks to the selected town NPC."),
SDLK_UNKNOWN,
GoToSelectedTownNpcKeyPressed,
nullptr,
IsTownNpcActionAllowed);
options.Keymapper.AddAction(
"SpeakNearestUnexploredSpace",
N_("Nearest unexplored space"),

190
Source/options.cpp

@ -508,30 +508,30 @@ std::vector<OptionEntryBase *> HellfireOptions::GetEntries()
};
}
AudioOptions::AudioOptions()
: OptionCategoryBase("Audio", N_("Audio"), N_("Audio Settings"))
, soundVolume("Sound Volume", OptionEntryFlags::Invisible, "Sound Volume", "Movie and SFX volume.", VOLUME_MAX)
, audioCuesVolume("Audio Cues Volume", OptionEntryFlags::Invisible, "Audio Cues Volume", "Navigation audio cues volume.", VOLUME_MAX)
, musicVolume("Music Volume", OptionEntryFlags::Invisible, "Music Volume", "Music Volume.", VOLUME_MAX)
, walkingSound("Walking Sound", OptionEntryFlags::None, N_("Walking Sound"), N_("Player emits sound when walking."), true)
, autoEquipSound("Auto Equip Sound", OptionEntryFlags::None, N_("Auto Equip Sound"), N_("Automatically equipping items on pickup emits the equipment sound."), false)
, itemPickupSound("Item Pickup Sound", OptionEntryFlags::None, N_("Item Pickup Sound"), N_("Picking up items emits the items pickup sound."), false)
, sampleRate("Sample Rate", OptionEntryFlags::CantChangeInGame, N_("Sample Rate"), N_("Output sample rate (Hz)."), DEFAULT_AUDIO_SAMPLE_RATE, { 22050, 44100, 48000 })
AudioOptions::AudioOptions()
: OptionCategoryBase("Audio", N_("Audio"), N_("Audio Settings"))
, soundVolume("Sound Volume", OptionEntryFlags::Invisible, "Sound Volume", "Movie and SFX volume.", VOLUME_MAX)
, audioCuesVolume("Audio Cues Volume", OptionEntryFlags::Invisible, "Audio Cues Volume", "Navigation audio cues volume.", VOLUME_MAX)
, musicVolume("Music Volume", OptionEntryFlags::Invisible, "Music Volume", "Music Volume.", VOLUME_MAX)
, walkingSound("Walking Sound", OptionEntryFlags::None, N_("Walking Sound"), N_("Player emits sound when walking."), true)
, autoEquipSound("Auto Equip Sound", OptionEntryFlags::None, N_("Auto Equip Sound"), N_("Automatically equipping items on pickup emits the equipment sound."), false)
, itemPickupSound("Item Pickup Sound", OptionEntryFlags::None, N_("Item Pickup Sound"), N_("Picking up items emits the items pickup sound."), false)
, sampleRate("Sample Rate", OptionEntryFlags::CantChangeInGame, N_("Sample Rate"), N_("Output sample rate (Hz)."), DEFAULT_AUDIO_SAMPLE_RATE, { 22050, 44100, 48000 })
, channels("Channels", OptionEntryFlags::CantChangeInGame, N_("Channels"), N_("Number of output channels."), DEFAULT_AUDIO_CHANNELS, { 1, 2 })
, bufferSize("Buffer Size", OptionEntryFlags::CantChangeInGame, N_("Buffer Size"), N_("Buffer size (number of frames per channel)."), DEFAULT_AUDIO_BUFFER_SIZE, { 1024, 2048, 5120 })
, resamplingQuality("Resampling Quality", OptionEntryFlags::CantChangeInGame, N_("Resampling Quality"), N_("Quality of the resampler, from 0 (lowest) to 5 (highest)."), DEFAULT_AUDIO_RESAMPLING_QUALITY, { 0, 1, 2, 3, 4, 5 })
{
}
std::vector<OptionEntryBase *> AudioOptions::GetEntries()
{
// clang-format off
return {
&soundVolume,
&audioCuesVolume,
&musicVolume,
&walkingSound,
&autoEquipSound,
&itemPickupSound,
std::vector<OptionEntryBase *> AudioOptions::GetEntries()
{
// clang-format off
return {
&soundVolume,
&audioCuesVolume,
&musicVolume,
&walkingSound,
&autoEquipSound,
&itemPickupSound,
&sampleRate,
&channels,
&bufferSize,
@ -1130,19 +1130,19 @@ KeymapperOptions::KeymapperOptions()
keyIDToKeyName.emplace(SDLK_PERIOD, ".");
keyIDToKeyName.emplace(SDLK_SLASH, "/");
keyIDToKeyName.emplace(SDLK_BACKSPACE, "BACKSPACE");
keyIDToKeyName.emplace(SDLK_CAPSLOCK, "CAPSLOCK");
keyIDToKeyName.emplace(SDLK_SCROLLLOCK, "SCROLLLOCK");
keyIDToKeyName.emplace(SDLK_INSERT, "INSERT");
keyIDToKeyName.emplace(SDLK_DELETE, "DELETE");
keyIDToKeyName.emplace(SDLK_HOME, "HOME");
keyIDToKeyName.emplace(SDLK_END, "END");
keyIDToKeyName.emplace(SDLK_PAGEUP, "PAGEUP");
keyIDToKeyName.emplace(SDLK_PAGEDOWN, "PAGEDOWN");
keyIDToKeyName.emplace(SDLK_UP, "UP");
keyIDToKeyName.emplace(SDLK_DOWN, "DOWN");
keyIDToKeyName.emplace(SDLK_LEFT, "LEFT");
keyIDToKeyName.emplace(SDLK_RIGHT, "RIGHT");
keyIDToKeyName.emplace(SDLK_BACKSPACE, "BACKSPACE");
keyIDToKeyName.emplace(SDLK_CAPSLOCK, "CAPSLOCK");
keyIDToKeyName.emplace(SDLK_SCROLLLOCK, "SCROLLLOCK");
keyIDToKeyName.emplace(SDLK_INSERT, "INSERT");
keyIDToKeyName.emplace(SDLK_DELETE, "DELETE");
keyIDToKeyName.emplace(SDLK_HOME, "HOME");
keyIDToKeyName.emplace(SDLK_END, "END");
keyIDToKeyName.emplace(SDLK_PAGEUP, "PAGEUP");
keyIDToKeyName.emplace(SDLK_PAGEDOWN, "PAGEDOWN");
keyIDToKeyName.emplace(SDLK_UP, "UP");
keyIDToKeyName.emplace(SDLK_DOWN, "DOWN");
keyIDToKeyName.emplace(SDLK_LEFT, "LEFT");
keyIDToKeyName.emplace(SDLK_RIGHT, "RIGHT");
keyIDToKeyName.emplace(SDLK_KP_DIVIDE, "KEYPAD /");
keyIDToKeyName.emplace(SDLK_KP_MULTIPLY, "KEYPAD *");
@ -1186,61 +1186,61 @@ std::string_view KeymapperOptions::Action::GetName() const
return dynamicName;
}
void KeymapperOptions::Action::LoadFromIni(std::string_view category)
{
const std::span<const Ini::Value> iniValues = ini->get(category, key);
if (iniValues.empty()) {
SetValue(defaultKey);
return; // Use the default key if no key has been set.
}
const std::string_view iniValue = iniValues.back().value;
if (iniValue.empty()) {
if (key == "SpeakCurrentLocation") {
const std::span<const Ini::Value> chatLogValues = ini->get(category, "ChatLog");
if (!chatLogValues.empty() && chatLogValues.back().value == "L") {
SetValue(defaultKey);
return;
}
}
// Migration: `SpeakNearestTownPortal` originally defaulted to `P` but could be
// saved as unbound due to key conflicts in earlier versions. Treat an explicit
// empty mapping as "use default" so existing configs start working again.
if (key == "SpeakNearestTownPortal") {
SetValue(defaultKey);
return;
}
// Migration: some actions were previously saved as unbound because their default
// keys were not supported by the keymapper. If we see an explicit empty mapping
// for these actions, treat it as "use default".
if (IsAnyOf(key, "PreviousTownNpc", "NextTownNpc", "KeyboardWalkNorth", "KeyboardWalkSouth", "KeyboardWalkEast", "KeyboardWalkWest")) {
SetValue(defaultKey);
return;
}
SetValue(SDLK_UNKNOWN);
return;
}
void KeymapperOptions::Action::LoadFromIni(std::string_view category)
{
const std::span<const Ini::Value> iniValues = ini->get(category, key);
if (iniValues.empty()) {
SetValue(defaultKey);
return; // Use the default key if no key has been set.
}
const std::string_view iniValue = iniValues.back().value;
if (iniValue.empty()) {
if (key == "SpeakCurrentLocation") {
const std::span<const Ini::Value> chatLogValues = ini->get(category, "ChatLog");
if (!chatLogValues.empty() && chatLogValues.back().value == "L") {
SetValue(defaultKey);
return;
}
}
// Migration: `SpeakNearestTownPortal` originally defaulted to `P` but could be
// saved as unbound due to key conflicts in earlier versions. Treat an explicit
// empty mapping as "use default" so existing configs start working again.
if (key == "SpeakNearestTownPortal") {
SetValue(defaultKey);
return;
}
// Migration: some actions were previously saved as unbound because their default
// keys were not supported by the keymapper. If we see an explicit empty mapping
// for these actions, treat it as "use default".
if (IsAnyOf(key, "KeyboardWalkNorth", "KeyboardWalkSouth", "KeyboardWalkEast", "KeyboardWalkWest")) {
SetValue(defaultKey);
return;
}
SetValue(SDLK_UNKNOWN);
return;
}
auto keyIt = GetOptions().Keymapper.keyNameToKeyID.find(iniValue);
if (keyIt == GetOptions().Keymapper.keyNameToKeyID.end()) {
// Use the default key if the key is unknown.
Log("Keymapper: unknown key '{}'", iniValue);
SetValue(defaultKey);
return;
}
if (key == "ChatLog" && iniValue == "L") {
SetValue(defaultKey);
return;
}
// Store the key in action.key and in the map so we can save() the
// actions while keeping the same order as they have been added.
SetValue(keyIt->second);
}
if (keyIt == GetOptions().Keymapper.keyNameToKeyID.end()) {
// Use the default key if the key is unknown.
Log("Keymapper: unknown key '{}'", iniValue);
SetValue(defaultKey);
return;
}
if (key == "ChatLog" && iniValue == "L") {
SetValue(defaultKey);
return;
}
// Store the key in action.key and in the map so we can save() the
// actions while keeping the same order as they have been added.
SetValue(keyIt->second);
}
void KeymapperOptions::Action::SaveToIni(std::string_view category) const
{
if (boundKey == SDLK_UNKNOWN) {
@ -1256,16 +1256,16 @@ void KeymapperOptions::Action::SaveToIni(std::string_view category) const
ini->set(category, key, keyNameIt->second);
}
std::string_view KeymapperOptions::Action::GetValueDescription() const
{
if (boundKey == SDLK_UNKNOWN)
return _("Unbound");
auto keyNameIt = GetOptions().Keymapper.keyIDToKeyName.find(boundKey);
if (keyNameIt == GetOptions().Keymapper.keyIDToKeyName.end()) {
return "";
}
return keyNameIt->second;
}
std::string_view KeymapperOptions::Action::GetValueDescription() const
{
if (boundKey == SDLK_UNKNOWN)
return _("Unbound");
auto keyNameIt = GetOptions().Keymapper.keyIDToKeyName.find(boundKey);
if (keyNameIt == GetOptions().Keymapper.keyIDToKeyName.end()) {
return "";
}
return keyNameIt->second;
}
bool KeymapperOptions::Action::SetValue(int value)
{

Loading…
Cancel
Save