Browse Source

Add Ctrl+E town dungeon selection

pull/8474/head
mojsior 2 months ago
parent
commit
91caa77e48
  1. 2
      README.md
  2. 113
      Source/diablo.cpp

2
README.md

@ -25,7 +25,7 @@ Keybinds are configurable, but these are the defaults most players will use:
- `Shift`+`N` - cycle to the next target in the current tracker category (speaks name only; duplicates get ordinal numbers).
- `Ctrl`+`N` - clear the tracker target.
- `H` - speak nearest unexplored space.
- `E` - speak nearest exit (hold `Shift` for quest entrances).
- `E` - speak nearest exit (hold `Shift` for quest entrances; in town: `Ctrl`+`E` cycles dungeon entrances).
- `,` - speak nearest stairs up.
- `.` - speak nearest stairs down.
- `L` - speak current dungeon + floor.

113
Source/diablo.cpp

@ -4383,6 +4383,69 @@ std::string TriggerLabelForSpeech(const TriggerStruct &trigger)
}
}
std::optional<int> LockedTownDungeonTriggerIndex;
std::vector<int> CollectTownDungeonTriggerIndices()
{
std::vector<int> result;
result.reserve(static_cast<size_t>(std::max(0, numtrigs)));
for (int i = 0; i < numtrigs; ++i) {
if (IsAnyOf(trigs[i]._tmsg, WM_DIABNEXTLVL, WM_DIABTOWNWARP))
result.push_back(i);
}
std::sort(result.begin(), result.end(), [](int a, int b) {
const TriggerStruct &ta = trigs[a];
const TriggerStruct &tb = trigs[b];
const int kindA = ta._tmsg == WM_DIABNEXTLVL ? 0 : (ta._tmsg == WM_DIABTOWNWARP ? 1 : 2);
const int kindB = tb._tmsg == WM_DIABNEXTLVL ? 0 : (tb._tmsg == WM_DIABTOWNWARP ? 1 : 2);
if (kindA != kindB)
return kindA < kindB;
if (ta._tmsg == WM_DIABTOWNWARP && tb._tmsg == WM_DIABTOWNWARP && ta._tlvl != tb._tlvl)
return ta._tlvl < tb._tlvl;
return a < b;
});
return result;
}
std::optional<int> FindDefaultTownDungeonTriggerIndex(const std::vector<int> &candidates)
{
for (const int index : candidates) {
if (trigs[index]._tmsg == WM_DIABNEXTLVL)
return index;
}
if (!candidates.empty())
return candidates.front();
return std::nullopt;
}
std::optional<int> FindLockedTownDungeonTriggerIndex(const std::vector<int> &candidates)
{
if (!LockedTownDungeonTriggerIndex)
return std::nullopt;
if (std::find(candidates.begin(), candidates.end(), *LockedTownDungeonTriggerIndex) != candidates.end())
return *LockedTownDungeonTriggerIndex;
return std::nullopt;
}
std::optional<int> FindNextTownDungeonTriggerIndex(const std::vector<int> &candidates, int current)
{
if (candidates.empty())
return std::nullopt;
const auto it = std::find(candidates.begin(), candidates.end(), current);
if (it == candidates.end())
return candidates.front();
if (std::next(it) == candidates.end())
return candidates.front();
return *std::next(it);
}
std::optional<int> FindPreferredExitTriggerIndex()
{
if (numtrigs <= 0)
@ -4533,6 +4596,7 @@ void SpeakNearestExitKeyPressed()
const SDL_Keymod modState = SDL_GetModState();
const bool seekQuestEntrance = (modState & SDL_KMOD_SHIFT) != 0;
const bool cycleTownDungeon = (modState & SDL_KMOD_CTRL) != 0;
if (seekQuestEntrance) {
if (const std::optional<QuestSetLevelEntrance> entrance = FindNearestQuestSetLevelEntranceOnCurrentLevel(); entrance) {
@ -4553,6 +4617,53 @@ void SpeakNearestExitKeyPressed()
return;
}
if (leveltype == DTYPE_TOWN) {
const std::vector<int> dungeonCandidates = CollectTownDungeonTriggerIndices();
if (dungeonCandidates.empty()) {
SpeakText(_("No exits found."), true);
return;
}
if (cycleTownDungeon) {
if (dungeonCandidates.size() <= 1) {
SpeakText(_("No other dungeon entrances found."), true);
return;
}
const int current = LockedTownDungeonTriggerIndex.value_or(-1);
const std::optional<int> next = FindNextTownDungeonTriggerIndex(dungeonCandidates, current);
if (!next) {
SpeakText(_("No other dungeon entrances found."), true);
return;
}
LockedTownDungeonTriggerIndex = *next;
const std::string label = TriggerLabelForSpeech(trigs[*next]);
if (!label.empty())
SpeakText(label, true);
return;
}
const int triggerIndex = FindLockedTownDungeonTriggerIndex(dungeonCandidates)
.value_or(FindDefaultTownDungeonTriggerIndex(dungeonCandidates).value_or(dungeonCandidates.front()));
LockedTownDungeonTriggerIndex = triggerIndex;
const TriggerStruct &trigger = trigs[triggerIndex];
const Point targetPosition { trigger.position.x, trigger.position.y };
const std::optional<std::vector<int8_t>> path = FindKeyboardWalkPathForSpeech(*MyPlayer, startPosition, targetPosition);
std::string message = TriggerLabelForSpeech(trigger);
if (!message.empty())
message.append(": ");
if (!path)
AppendDirectionalFallback(message, targetPosition - startPosition);
else
AppendKeyboardWalkPathForSpeech(message, *path);
SpeakText(message, true);
return;
}
if (leveltype != DTYPE_TOWN) {
if (const std::optional<Point> portalPosition = FindNearestTownPortalOnCurrentLevel(); portalPosition) {
const std::optional<std::vector<int8_t>> path = FindKeyboardWalkPathForSpeech(*MyPlayer, startPosition, *portalPosition);
@ -5222,7 +5333,7 @@ void InitKeymapActions()
options.Keymapper.AddAction(
"SpeakNearestExit",
N_("Nearest exit"),
N_("Speaks the nearest exit. Hold Shift for quest entrances."),
N_("Speaks the nearest exit. Hold Shift for quest entrances. In town, press Ctrl+E to cycle dungeon entrances."),
'E',
SpeakNearestExitKeyPressed,
nullptr,

Loading…
Cancel
Save