Browse Source

Add town portal navigation hotkey (P)

pull/8474/head
mojsior 2 months ago
parent
commit
61908a6d06
  1. 1
      README.md
  2. 147
      Source/diablo.cpp

1
README.md

@ -26,6 +26,7 @@ Keybinds are configurable, but these are the defaults most players will use:
- `Ctrl`+`N` - clear the tracker target.
- `H` - speak nearest unexplored space.
- `E` - speak nearest exit (hold `Shift` for quest entrances; in town: `Ctrl`+`E` cycles dungeon entrances).
- `P` - speak nearest open town portal (town only).
- `,` - speak nearest stairs up.
- `.` - speak nearest stairs down.
- `L` - speak current dungeon + floor.

147
Source/diablo.cpp

@ -4542,6 +4542,93 @@ std::optional<Point> FindNearestTownPortalOnCurrentLevel()
return bestPosition;
}
struct TownPortalInTown {
int portalIndex;
Point position;
int distance;
};
std::optional<TownPortalInTown> FindNearestTownPortalInTown()
{
if (MyPlayer == nullptr || leveltype != DTYPE_TOWN)
return std::nullopt;
const Point playerPosition = MyPlayer->position.future;
std::optional<TownPortalInTown> best;
int bestDistance = 0;
for (const Missile &missile : Missiles) {
if (missile._mitype != MissileID::TownPortal)
continue;
if (missile._misource < 0 || missile._misource >= MAXPORTAL)
continue;
if (!Portals[missile._misource].open)
continue;
const Point portalPosition = missile.position.tile;
const int distance = playerPosition.WalkingDistance(portalPosition);
if (!best || distance < bestDistance) {
best = TownPortalInTown {
.portalIndex = missile._misource,
.position = portalPosition,
.distance = distance,
};
bestDistance = distance;
}
}
return best;
}
[[nodiscard]] std::string TownPortalLabelForSpeech(const Portal &portal)
{
if (portal.level <= 0)
return std::string { _("Town portal") };
if (portal.setlvl) {
const auto questLevel = static_cast<_setlevels>(portal.level);
const char *questLevelName = QuestLevelNames[questLevel];
if (questLevelName == nullptr || questLevelName[0] == '\0')
return std::string { _("Town portal to set level") };
return fmt::format(fmt::runtime(_(/* TRANSLATORS: {:s} is a set/quest level name. */ "Town portal to {:s}")), _(questLevelName));
}
constexpr std::array<const char *, DTYPE_LAST + 1> DungeonStrs = {
N_("Town"),
N_("Cathedral"),
N_("Catacombs"),
N_("Caves"),
N_("Hell"),
N_("Nest"),
N_("Crypt"),
};
std::string dungeonStr;
if (portal.ltype >= DTYPE_TOWN && portal.ltype <= DTYPE_LAST) {
dungeonStr = _(DungeonStrs[static_cast<size_t>(portal.ltype)]);
} else {
dungeonStr = _(/* TRANSLATORS: type of dungeon (i.e. Cathedral, Caves)*/ "None");
}
int floor = portal.level;
if (portal.ltype == DTYPE_CATACOMBS)
floor -= 4;
else if (portal.ltype == DTYPE_CAVES)
floor -= 8;
else if (portal.ltype == DTYPE_HELL)
floor -= 12;
else if (portal.ltype == DTYPE_NEST)
floor -= 16;
else if (portal.ltype == DTYPE_CRYPT)
floor -= 20;
if (floor > 0)
return fmt::format(fmt::runtime(_(/* TRANSLATORS: {:s} is a dungeon name and {:d} is a floor number. */ "Town portal to {:s} {:d}")), dungeonStr, floor);
return fmt::format(fmt::runtime(_(/* TRANSLATORS: {:s} is a dungeon name. */ "Town portal to {:s}")), dungeonStr);
}
struct QuestSetLevelEntrance {
_setlevels questLevel;
Point entrancePosition;
@ -4718,6 +4805,42 @@ void SpeakNearestExitKeyPressed()
SpeakText(message, true);
}
void SpeakNearestTownPortalInTownKeyPressed()
{
if (!CanPlayerTakeAction())
return;
if (AutomapActive) {
SpeakText(_("Close the map first."), true);
return;
}
if (leveltype != DTYPE_TOWN) {
SpeakText(_("Not in town."), true);
return;
}
if (MyPlayer == nullptr)
return;
const std::optional<TownPortalInTown> portal = FindNearestTownPortalInTown();
if (!portal) {
SpeakText(_("No town portals found."), true);
return;
}
const Point startPosition = MyPlayer->position.future;
const Point targetPosition = portal->position;
const std::optional<std::vector<int8_t>> path = FindKeyboardWalkPathForSpeech(*MyPlayer, startPosition, targetPosition);
std::string message = TownPortalLabelForSpeech(Portals[portal->portalIndex]);
message.append(": ");
if (!path)
AppendDirectionalFallback(message, targetPosition - startPosition);
else
AppendKeyboardWalkPathForSpeech(message, *path);
SpeakText(message, true);
}
void SpeakNearestStairsKeyPressed(int triggerMessage)
{
if (!CanPlayerTakeAction())
@ -5525,8 +5648,8 @@ void InitKeymapActions()
"PauseGame",
N_("Pause Game"),
N_("Pauses the game."),
'P',
diablo_pause_game);
SDLK_UNKNOWN,
diablo_pause_game);
options.Keymapper.AddAction(
"PauseGameAlternate",
N_("Pause Game (Alternate)"),
@ -5598,7 +5721,7 @@ void InitKeymapActions()
"SortInv",
N_("Sort Inventory"),
N_("Sorts the inventory."),
'R',
'R',
[] {
ReorganizeInventory(*MyPlayer);
});
@ -5615,11 +5738,19 @@ void InitKeymapActions()
"Programming is like magic.",
'X',
[] {
DebugToggle = !DebugToggle;
});
#endif
options.Keymapper.CommitActions();
}
DebugToggle = !DebugToggle;
});
#endif
options.Keymapper.AddAction(
"SpeakNearestTownPortal",
N_("Nearest town portal"),
N_("Speaks directions to the nearest open town portal in town."),
'P',
SpeakNearestTownPortalInTownKeyPressed,
nullptr,
[]() { return CanPlayerTakeAction() && leveltype == DTYPE_TOWN; });
options.Keymapper.CommitActions();
}
void InitPadmapActions()
{

Loading…
Cancel
Save