Browse Source

access: town item tracking, item cues in town, dead bodies tracker

pull/8474/head
mojsior 2 months ago
parent
commit
f74b27e02e
  1. 4
      README.md
  2. 474
      Source/diablo.cpp
  3. 139
      Source/utils/proximity_audio.cpp
  4. 28
      Translations/pl.po

4
README.md

@ -20,9 +20,9 @@ Keybinds are configurable, but these are the defaults most players will use:
## Navigation / tracking
- `T` - cycle tracker target (items / chests / doors / shrines / objects / breakables / monsters).
- `T` - cycle tracker target (items / chests / doors / shrines / objects / breakables / monsters / dead bodies).
- `Shift`+`T` - cycle tracker target backwards.
- `N` - tracker directions to the nearest target (speaks target name + directions).
- `N` - tracker directions to the nearest target (speaks target name + directions; in town: items only).
- `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.

474
Source/diablo.cpp

@ -2101,10 +2101,10 @@ void GameLogic()
UpdateAutoWalkTracker();
UpdateLowDurabilityWarnings();
}
if (leveltype != DTYPE_TOWN) {
gGameLogicStep = GameLogicStep::ProcessMonsters;
#ifdef _DEBUG
if (!DebugInvisible)
if (leveltype != DTYPE_TOWN) {
gGameLogicStep = GameLogicStep::ProcessMonsters;
#ifdef _DEBUG
if (!DebugInvisible)
#endif
ProcessMonsters();
gGameLogicStep = GameLogicStep::ProcessObjects;
@ -2115,18 +2115,19 @@ void GameLogic()
ProcessItems();
ProcessLightList();
ProcessVisionList();
UpdateBossHealthAnnouncements();
UpdateProximityAudioCues();
UpdateAttackableMonsterAnnouncements();
UpdateInteractableDoorAnnouncements();
} else {
gGameLogicStep = GameLogicStep::ProcessTowners;
ProcessTowners();
gGameLogicStep = GameLogicStep::ProcessItemsTown;
ProcessItems();
gGameLogicStep = GameLogicStep::ProcessMissilesTown;
ProcessMissiles();
}
UpdateBossHealthAnnouncements();
UpdateProximityAudioCues();
UpdateAttackableMonsterAnnouncements();
UpdateInteractableDoorAnnouncements();
} else {
gGameLogicStep = GameLogicStep::ProcessTowners;
ProcessTowners();
gGameLogicStep = GameLogicStep::ProcessItemsTown;
ProcessItems();
gGameLogicStep = GameLogicStep::ProcessMissilesTown;
ProcessMissiles();
UpdateProximityAudioCues();
}
UpdatePlayerLowHpWarningSound();
@ -2226,15 +2227,16 @@ std::vector<int> TownNpcOrder;
int SelectedTownNpc = -1;
int AutoWalkTownNpcTarget = -1;
enum class TrackerTargetCategory : uint8_t {
Items,
Chests,
Doors,
Shrines,
Objects,
Breakables,
Monsters,
};
enum class TrackerTargetCategory : uint8_t {
Items,
Chests,
Doors,
Shrines,
Objects,
Breakables,
Monsters,
DeadBodies,
};
TrackerTargetCategory SelectedTrackerTargetCategory = TrackerTargetCategory::Items;
TrackerTargetCategory AutoWalkTrackerTargetCategory = TrackerTargetCategory::Items; ///< Category of the active auto-walk target.
@ -2520,13 +2522,14 @@ namespace {
constexpr int TrackerInteractDistanceTiles = 1;
constexpr int TrackerCycleDistanceTiles = 12;
int LockedTrackerItemId = -1;
int LockedTrackerChestId = -1;
int LockedTrackerDoorId = -1;
int LockedTrackerShrineId = -1;
int LockedTrackerObjectId = -1;
int LockedTrackerBreakableId = -1;
int LockedTrackerMonsterId = -1;
int LockedTrackerItemId = -1;
int LockedTrackerChestId = -1;
int LockedTrackerDoorId = -1;
int LockedTrackerShrineId = -1;
int LockedTrackerObjectId = -1;
int LockedTrackerBreakableId = -1;
int LockedTrackerMonsterId = -1;
int LockedTrackerDeadBodyId = -1;
struct TrackerLevelKey {
dungeon_type levelType;
@ -2537,16 +2540,17 @@ struct TrackerLevelKey {
std::optional<TrackerLevelKey> LockedTrackerLevelKey;
void ClearTrackerLocks()
{
LockedTrackerItemId = -1;
LockedTrackerChestId = -1;
LockedTrackerDoorId = -1;
LockedTrackerShrineId = -1;
LockedTrackerObjectId = -1;
LockedTrackerBreakableId = -1;
LockedTrackerMonsterId = -1;
}
void ClearTrackerLocks()
{
LockedTrackerItemId = -1;
LockedTrackerChestId = -1;
LockedTrackerDoorId = -1;
LockedTrackerShrineId = -1;
LockedTrackerObjectId = -1;
LockedTrackerBreakableId = -1;
LockedTrackerMonsterId = -1;
LockedTrackerDeadBodyId = -1;
}
void EnsureTrackerLocksMatchCurrentLevel()
{
@ -2579,11 +2583,13 @@ int &LockedTrackerTargetId(TrackerTargetCategory category)
return LockedTrackerObjectId;
case TrackerTargetCategory::Breakables:
return LockedTrackerBreakableId;
case TrackerTargetCategory::Monsters:
return LockedTrackerMonsterId;
}
app_fatal("Invalid TrackerTargetCategory");
}
case TrackerTargetCategory::Monsters:
return LockedTrackerMonsterId;
case TrackerTargetCategory::DeadBodies:
return LockedTrackerDeadBodyId;
}
app_fatal("Invalid TrackerTargetCategory");
}
std::string_view TrackerTargetCategoryLabel(TrackerTargetCategory category)
{
@ -2600,12 +2606,14 @@ std::string_view TrackerTargetCategoryLabel(TrackerTargetCategory category)
return _("objects");
case TrackerTargetCategory::Breakables:
return _("breakables");
case TrackerTargetCategory::Monsters:
return _("monsters");
default:
return _("items");
}
}
case TrackerTargetCategory::Monsters:
return _("monsters");
case TrackerTargetCategory::DeadBodies:
return _("dead bodies");
default:
return _("items");
}
}
void SpeakTrackerTargetCategory()
{
@ -2624,14 +2632,14 @@ void CycleTrackerTargetKeyPressed()
const SDL_Keymod modState = SDL_GetModState();
const bool cyclePrevious = (modState & SDL_KMOD_SHIFT) != 0;
if (cyclePrevious) {
switch (SelectedTrackerTargetCategory) {
case TrackerTargetCategory::Items:
SelectedTrackerTargetCategory = TrackerTargetCategory::Monsters;
break;
case TrackerTargetCategory::Chests:
SelectedTrackerTargetCategory = TrackerTargetCategory::Items;
break;
if (cyclePrevious) {
switch (SelectedTrackerTargetCategory) {
case TrackerTargetCategory::Items:
SelectedTrackerTargetCategory = TrackerTargetCategory::DeadBodies;
break;
case TrackerTargetCategory::Chests:
SelectedTrackerTargetCategory = TrackerTargetCategory::Items;
break;
case TrackerTargetCategory::Doors:
SelectedTrackerTargetCategory = TrackerTargetCategory::Chests;
break;
@ -2644,13 +2652,17 @@ void CycleTrackerTargetKeyPressed()
case TrackerTargetCategory::Breakables:
SelectedTrackerTargetCategory = TrackerTargetCategory::Objects;
break;
case TrackerTargetCategory::Monsters:
SelectedTrackerTargetCategory = TrackerTargetCategory::Breakables;
break;
}
} else {
switch (SelectedTrackerTargetCategory) {
case TrackerTargetCategory::Items:
case TrackerTargetCategory::Monsters:
SelectedTrackerTargetCategory = TrackerTargetCategory::Breakables;
break;
case TrackerTargetCategory::DeadBodies:
default:
SelectedTrackerTargetCategory = TrackerTargetCategory::Monsters;
break;
}
} else {
switch (SelectedTrackerTargetCategory) {
case TrackerTargetCategory::Items:
SelectedTrackerTargetCategory = TrackerTargetCategory::Chests;
break;
case TrackerTargetCategory::Chests:
@ -2665,22 +2677,26 @@ void CycleTrackerTargetKeyPressed()
case TrackerTargetCategory::Objects:
SelectedTrackerTargetCategory = TrackerTargetCategory::Breakables;
break;
case TrackerTargetCategory::Breakables:
SelectedTrackerTargetCategory = TrackerTargetCategory::Monsters;
break;
case TrackerTargetCategory::Monsters:
SelectedTrackerTargetCategory = TrackerTargetCategory::Items;
break;
}
}
case TrackerTargetCategory::Breakables:
SelectedTrackerTargetCategory = TrackerTargetCategory::Monsters;
break;
case TrackerTargetCategory::Monsters:
SelectedTrackerTargetCategory = TrackerTargetCategory::DeadBodies;
break;
case TrackerTargetCategory::DeadBodies:
default:
SelectedTrackerTargetCategory = TrackerTargetCategory::Items;
break;
}
}
SpeakTrackerTargetCategory();
}
std::optional<int> FindNearestGroundItemId(Point playerPosition)
{
std::optional<int> bestId;
int bestDistance = 0;
std::optional<int> FindNearestGroundItemId(Point playerPosition)
{
std::optional<int> bestId;
int bestDistance = 0;
for (int y = 0; y < MAXDUNY; ++y) {
for (int x = 0; x < MAXDUNX; ++x) {
@ -2699,15 +2715,47 @@ std::optional<int> FindNearestGroundItemId(Point playerPosition)
}
}
}
return bestId;
}
struct TrackerCandidate {
int id;
int distance;
StringOrView name;
};
return bestId;
}
[[nodiscard]] constexpr int CorpseTrackerIdForPosition(Point position)
{
return position.x + position.y * MAXDUNX;
}
[[nodiscard]] constexpr Point CorpsePositionForTrackerId(int corpseId)
{
return { corpseId % MAXDUNX, corpseId / MAXDUNX };
}
std::optional<int> FindNearestCorpseId(Point playerPosition)
{
std::optional<int> bestId;
int bestDistance = 0;
for (int y = 0; y < MAXDUNY; ++y) {
for (int x = 0; x < MAXDUNX; ++x) {
if (dCorpse[x][y] == 0)
continue;
const Point position { x, y };
const int distance = playerPosition.WalkingDistance(position);
if (!bestId || distance < bestDistance) {
bestId = CorpseTrackerIdForPosition(position);
bestDistance = distance;
}
}
}
return bestId;
}
struct TrackerCandidate {
int id;
int distance;
StringOrView name;
};
[[nodiscard]] bool IsBetterTrackerCandidate(const TrackerCandidate &a, const TrackerCandidate &b)
{
@ -2753,14 +2801,45 @@ struct TrackerCandidate {
}
}
std::sort(result.begin(), result.end(), [](const TrackerCandidate &a, const TrackerCandidate &b) { return IsBetterTrackerCandidate(a, b); });
return result;
}
[[nodiscard]] constexpr bool IsTrackedChestObject(const Object &object)
{
return object.canInteractWith() && (object.IsChest() || object._otype == _object_id::OBJ_SIGNCHEST);
}
std::sort(result.begin(), result.end(), [](const TrackerCandidate &a, const TrackerCandidate &b) { return IsBetterTrackerCandidate(a, b); });
return result;
}
[[nodiscard]] std::vector<TrackerCandidate> CollectNearbyCorpseTrackerCandidates(Point playerPosition, int maxDistance)
{
std::vector<TrackerCandidate> result;
const int minX = std::max(0, playerPosition.x - maxDistance);
const int minY = std::max(0, playerPosition.y - maxDistance);
const int maxX = std::min(MAXDUNX - 1, playerPosition.x + maxDistance);
const int maxY = std::min(MAXDUNY - 1, playerPosition.y + maxDistance);
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) {
if (dCorpse[x][y] == 0)
continue;
const Point position { x, y };
const int distance = playerPosition.WalkingDistance(position);
if (distance > maxDistance)
continue;
result.push_back(TrackerCandidate {
.id = CorpseTrackerIdForPosition(position),
.distance = distance,
.name = _("Dead body"),
});
}
}
std::sort(result.begin(), result.end(), [](const TrackerCandidate &a, const TrackerCandidate &b) { return IsBetterTrackerCandidate(a, b); });
return result;
}
[[nodiscard]] constexpr bool IsTrackedChestObject(const Object &object)
{
return object.canInteractWith() && (object.IsChest() || object._otype == _object_id::OBJ_SIGNCHEST);
}
[[nodiscard]] constexpr bool IsTrackedDoorObject(const Object &object)
{
@ -3006,23 +3085,32 @@ void DecorateTrackerTargetNameWithOrdinalIfNeeded(int targetId, StringOrView &ta
targetName = std::move(decorated);
}
[[nodiscard]] bool IsGroundItemPresent(int itemId)
{
if (itemId < 0 || itemId > MAXITEMS)
return false;
[[nodiscard]] bool IsGroundItemPresent(int itemId)
{
if (itemId < 0 || itemId > MAXITEMS)
return false;
for (uint8_t i = 0; i < ActiveItemCount; ++i) {
if (ActiveItems[i] == itemId)
return true;
}
return false;
}
std::optional<int> FindNearestUnopenedChestObjectId(Point playerPosition)
{
return FindNearestObjectId(playerPosition, IsTrackedChestObject);
}
return false;
}
[[nodiscard]] bool IsCorpsePresent(int corpseId)
{
if (corpseId < 0 || corpseId >= MAXDUNX * MAXDUNY)
return false;
const Point position = CorpsePositionForTrackerId(corpseId);
return InDungeonBounds(position) && dCorpse[position.x][position.y] != 0;
}
std::optional<int> FindNearestUnopenedChestObjectId(Point playerPosition)
{
return FindNearestObjectId(playerPosition, IsTrackedChestObject);
}
std::optional<int> FindNearestDoorObjectId(Point playerPosition)
{
@ -3334,14 +3422,14 @@ struct TrackerPathBlockInfo {
return std::nullopt;
}
void NavigateToTrackerTargetKeyPressed()
{
if (!CanPlayerTakeAction() || InGameMenu())
return;
if (leveltype == DTYPE_TOWN) {
SpeakText(_("Not in a dungeon."), true);
return;
}
void NavigateToTrackerTargetKeyPressed()
{
if (!CanPlayerTakeAction() || InGameMenu())
return;
if (leveltype == DTYPE_TOWN && IsNoneOf(SelectedTrackerTargetCategory, TrackerTargetCategory::Items, TrackerTargetCategory::DeadBodies)) {
SpeakText(_("Not in a dungeon."), true);
return;
}
if (AutomapActive) {
SpeakText(_("Close the map first."), true);
return;
@ -3626,10 +3714,10 @@ void NavigateToTrackerTargetKeyPressed()
}
break;
}
case TrackerTargetCategory::Monsters: {
const std::vector<TrackerCandidate> nearbyCandidates = CollectNearbyMonsterTrackerCandidates(playerPosition, TrackerCycleDistanceTiles);
if (cycleTarget) {
targetId = FindNextTrackerCandidateId(nearbyCandidates, lockedTargetId);
case TrackerTargetCategory::Monsters: {
const std::vector<TrackerCandidate> nearbyCandidates = CollectNearbyMonsterTrackerCandidates(playerPosition, TrackerCycleDistanceTiles);
if (cycleTarget) {
targetId = FindNextTrackerCandidateId(nearbyCandidates, lockedTargetId);
if (!targetId) {
if (nearbyCandidates.empty())
SpeakText(_("No monsters found."), true);
@ -3662,16 +3750,51 @@ void NavigateToTrackerTargetKeyPressed()
targetName = tracked.name();
DecorateTrackerTargetNameWithOrdinalIfNeeded(*targetId, targetName, nearbyCandidates);
if (!cycleTarget) {
targetPosition = tracked.position.tile;
}
break;
}
}
if (cycleTarget) {
SpeakText(targetName.str(), /*force=*/true);
return;
if (!cycleTarget) {
targetPosition = tracked.position.tile;
}
break;
}
case TrackerTargetCategory::DeadBodies: {
const std::vector<TrackerCandidate> nearbyCandidates = CollectNearbyCorpseTrackerCandidates(playerPosition, TrackerCycleDistanceTiles);
if (cycleTarget) {
targetId = FindNextTrackerCandidateId(nearbyCandidates, lockedTargetId);
if (!targetId) {
if (nearbyCandidates.empty())
SpeakText(_("No dead bodies found."), true);
else
SpeakText(_("No next dead body."), true);
return;
}
} else if (IsCorpsePresent(lockedTargetId)) {
targetId = lockedTargetId;
} else {
targetId = FindNearestCorpseId(playerPosition);
}
if (!targetId) {
SpeakText(_("No dead bodies found."), true);
return;
}
if (!IsCorpsePresent(*targetId)) {
lockedTargetId = -1;
SpeakText(_("No dead bodies found."), true);
return;
}
lockedTargetId = *targetId;
targetName = _("Dead body");
DecorateTrackerTargetNameWithOrdinalIfNeeded(*targetId, targetName, nearbyCandidates);
if (!cycleTarget) {
targetPosition = CorpsePositionForTrackerId(*targetId);
}
break;
}
}
if (cycleTarget) {
SpeakText(targetName.str(), /*force=*/true);
return;
}
if (!targetPosition) {
@ -3964,10 +4087,10 @@ void UpdateAutoWalkTracker()
if (!ValidateAutoWalkObjectTarget(myPlayer, playerPosition, IsTrackedBreakableObject, N_("Target breakable is gone."), N_("Breakable in range."), destination))
return;
break;
case TrackerTargetCategory::Monsters: {
const int monsterId = AutoWalkTrackerTargetId;
if (monsterId < 0 || monsterId >= static_cast<int>(MaxMonsters)) {
AutoWalkTrackerTargetId = -1;
case TrackerTargetCategory::Monsters: {
const int monsterId = AutoWalkTrackerTargetId;
if (monsterId < 0 || monsterId >= static_cast<int>(MaxMonsters)) {
AutoWalkTrackerTargetId = -1;
SpeakText(_("Target monster is gone."), true);
return;
}
@ -3982,11 +4105,29 @@ void UpdateAutoWalkTracker()
AutoWalkTrackerTargetId = -1;
SpeakText(_("Monster in range."), true);
return;
}
destination = FindBestAdjacentApproachTile(myPlayer, playerPosition, monsterPosition);
break;
}
}
}
destination = FindBestAdjacentApproachTile(myPlayer, playerPosition, monsterPosition);
break;
}
case TrackerTargetCategory::DeadBodies: {
const int corpseId = AutoWalkTrackerTargetId;
if (!IsCorpsePresent(corpseId)) {
AutoWalkTrackerTargetId = -1;
SpeakText(_("Target dead body is gone."), true);
return;
}
const Point corpsePosition = CorpsePositionForTrackerId(corpseId);
if (playerPosition.WalkingDistance(corpsePosition) <= TrackerInteractDistanceTiles) {
AutoWalkTrackerTargetId = -1;
SpeakText(_("Dead body in range."), true);
return;
}
destination = corpsePosition;
break;
}
}
if (!destination) {
AutoWalkTrackerTargetId = -1;
@ -4144,10 +4285,10 @@ void AutoWalkToTrackerTargetKeyPressed()
if (!targetId)
return;
break;
case TrackerTargetCategory::Monsters: {
if (lockedTargetId >= 0 && lockedTargetId < static_cast<int>(MaxMonsters)) {
targetId = lockedTargetId;
} else {
case TrackerTargetCategory::Monsters: {
if (lockedTargetId >= 0 && lockedTargetId < static_cast<int>(MaxMonsters)) {
targetId = lockedTargetId;
} else {
targetId = FindNearestMonsterId(playerPosition);
}
if (!targetId) {
@ -4163,11 +4304,30 @@ void AutoWalkToTrackerTargetKeyPressed()
return;
}
}
lockedTargetId = *targetId;
targetName = Monsters[*targetId].name();
break;
}
}
lockedTargetId = *targetId;
targetName = Monsters[*targetId].name();
break;
}
case TrackerTargetCategory::DeadBodies: {
if (IsCorpsePresent(lockedTargetId)) {
targetId = lockedTargetId;
} else {
targetId = FindNearestCorpseId(playerPosition);
}
if (!targetId) {
SpeakText(_("No dead bodies found."), true);
return;
}
if (!IsCorpsePresent(*targetId)) {
lockedTargetId = -1;
SpeakText(_("No dead bodies found."), true);
return;
}
lockedTargetId = *targetId;
targetName = _("Dead body");
break;
}
}
std::string msg;
StrAppend(msg, _("Going to: "), targetName);
@ -5902,14 +6062,14 @@ void InitKeymapActions()
SpeakNearestStairsUpKeyPressed,
nullptr,
[]() { return CanPlayerTakeAction() && leveltype != DTYPE_TOWN; });
options.Keymapper.AddAction(
"CycleTrackerTarget",
N_("Cycle tracker target"),
N_("Cycles what the tracker looks for (items, chests, doors, shrines, objects, breakables, monsters). Hold Shift to cycle backwards."),
'T',
CycleTrackerTargetKeyPressed,
nullptr,
[]() { return CanPlayerTakeAction() && !InGameMenu(); });
options.Keymapper.AddAction(
"CycleTrackerTarget",
N_("Cycle tracker target"),
N_("Cycles what the tracker looks for (items, chests, doors, shrines, objects, breakables, monsters, dead bodies). Hold Shift to cycle backwards."),
'T',
CycleTrackerTargetKeyPressed,
nullptr,
[]() { return CanPlayerTakeAction() && !InGameMenu(); });
options.Keymapper.AddAction(
"NavigateToTrackerTarget",
N_("Tracker directions"),

139
Source/utils/proximity_audio.cpp

@ -316,8 +316,6 @@ void UpdateProximityAudioCues()
{
if (!gbSndInited || !gbSoundOn)
return;
if (leveltype == DTYPE_TOWN)
return;
if (MyPlayer == nullptr || MyPlayerIsDead || MyPlayer->_pmode == PM_DEATH)
return;
if (InGameMenu())
@ -384,86 +382,89 @@ void UpdateProximityAudioCues()
});
}
for (int i = 0; i < ActiveObjectCount; i++) {
const int objectId = ActiveObjects[i];
const Object &object = Objects[objectId];
if (!object.canInteractWith())
continue;
if (!object.isDoor() && !object.IsChest())
continue;
// Only enable non-item emitters inside dungeons for now.
if (leveltype != DTYPE_TOWN) {
for (int i = 0; i < ActiveObjectCount; i++) {
const int objectId = ActiveObjects[i];
const Object &object = Objects[objectId];
if (!object.canInteractWith())
continue;
if (!object.isDoor() && !object.IsChest())
continue;
SoundPool::SoundId soundId;
if (object.IsChest()) {
soundId = SoundPool::SoundId::Chest;
} else {
soundId = SoundPool::SoundId::Door;
}
SoundPool::SoundId soundId;
if (object.IsChest()) {
soundId = SoundPool::SoundId::Chest;
} else {
soundId = SoundPool::SoundId::Door;
}
if (!pool.IsLoaded(soundId))
continue;
if (!pool.IsLoaded(soundId))
continue;
const int distance = playerPosition.ApproxDistance(object.position);
if (distance > MaxCueDistanceTiles)
continue;
const int distance = playerPosition.ApproxDistance(object.position);
if (distance > MaxCueDistanceTiles)
continue;
ConsiderCandidate(best, CandidateEmitter {
.emitterId = MakeEmitterId(EmitterType::Object, static_cast<uint32_t>(objectId)),
.sound = soundId,
.position = object.position,
.distance = distance,
.intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinIntervalMs, MaxIntervalMs),
});
}
ConsiderCandidate(best, CandidateEmitter {
.emitterId = MakeEmitterId(EmitterType::Object, static_cast<uint32_t>(objectId)),
.sound = soundId,
.position = object.position,
.distance = distance,
.intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinIntervalMs, MaxIntervalMs),
});
}
for (int i = 0; i < numtrigs; ++i) {
if (!IsAnyOf(trigs[i]._tmsg, WM_DIABNEXTLVL, WM_DIABPREVLVL))
continue;
for (int i = 0; i < numtrigs; ++i) {
if (!IsAnyOf(trigs[i]._tmsg, WM_DIABNEXTLVL, WM_DIABPREVLVL))
continue;
if (!pool.IsLoaded(SoundPool::SoundId::Stairs))
continue;
if (!pool.IsLoaded(SoundPool::SoundId::Stairs))
continue;
const Point triggerPosition { trigs[i].position.x, trigs[i].position.y };
const int distance = playerPosition.ApproxDistance(triggerPosition);
if (distance > MaxCueDistanceTiles)
continue;
const Point triggerPosition { trigs[i].position.x, trigs[i].position.y };
const int distance = playerPosition.ApproxDistance(triggerPosition);
if (distance > MaxCueDistanceTiles)
continue;
ConsiderCandidate(best, CandidateEmitter {
.emitterId = MakeEmitterId(EmitterType::Trigger, static_cast<uint32_t>(i)),
.sound = SoundPool::SoundId::Stairs,
.position = triggerPosition,
.distance = distance,
.intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinIntervalMs, MaxIntervalMs),
});
}
ConsiderCandidate(best, CandidateEmitter {
.emitterId = MakeEmitterId(EmitterType::Trigger, static_cast<uint32_t>(i)),
.sound = SoundPool::SoundId::Stairs,
.position = triggerPosition,
.distance = distance,
.intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinIntervalMs, MaxIntervalMs),
});
}
for (size_t i = 0; i < ActiveMonsterCount; i++) {
const int monsterId = static_cast<int>(ActiveMonsters[i]);
const Monster &monster = Monsters[monsterId];
for (size_t i = 0; i < ActiveMonsterCount; i++) {
const int monsterId = static_cast<int>(ActiveMonsters[i]);
const Monster &monster = Monsters[monsterId];
if (monster.isInvalid)
continue;
if ((monster.flags & MFLAG_HIDDEN) != 0)
continue;
if (monster.hitPoints <= 0)
continue;
if (monster.isInvalid)
continue;
if ((monster.flags & MFLAG_HIDDEN) != 0)
continue;
if (monster.hitPoints <= 0)
continue;
if (!pool.IsLoaded(SoundPool::SoundId::Monster))
continue;
if (!pool.IsLoaded(SoundPool::SoundId::Monster))
continue;
// Use the future position for distance/tempo so cues react immediately when a monster starts moving.
const Point monsterSoundPosition { monster.position.tile };
const Point monsterDistancePosition { monster.position.future };
const int distance = playerPosition.ApproxDistance(monsterDistancePosition);
if (distance > MaxCueDistanceTiles)
continue;
// Use the future position for distance/tempo so cues react immediately when a monster starts moving.
const Point monsterSoundPosition { monster.position.tile };
const Point monsterDistancePosition { monster.position.future };
const int distance = playerPosition.ApproxDistance(monsterDistancePosition);
if (distance > MaxCueDistanceTiles)
continue;
ConsiderCandidate(best, CandidateEmitter {
.emitterId = MakeEmitterId(EmitterType::Monster, static_cast<uint32_t>(monsterId)),
.sound = SoundPool::SoundId::Monster,
.position = monsterSoundPosition,
.distance = distance,
.intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinMonsterIntervalMs, MaxMonsterIntervalMs),
});
ConsiderCandidate(best, CandidateEmitter {
.emitterId = MakeEmitterId(EmitterType::Monster, static_cast<uint32_t>(monsterId)),
.sound = SoundPool::SoundId::Monster,
.position = monsterSoundPosition,
.distance = distance,
.intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinMonsterIntervalMs, MaxMonsterIntervalMs),
});
}
}
std::array<SoundPool::EmitterRequest, MaxEmitters> requests;

28
Translations/pl.po

@ -6560,6 +6560,10 @@ msgstr "Nie ma następnego niszczalnego obiektu."
msgid "No next monster."
msgstr "Nie ma następnego potwora."
#: Source/diablo.cpp
msgid "No next dead body."
msgstr "Nie ma następnego martwego ciała."
#: Source/diablo.cpp
msgid "Tracker directions"
msgstr "Kierunki do celu trackera"
@ -12226,6 +12230,10 @@ msgstr "Zmienia, czego szuka tracker (przedmioty, skrzynie, drzwi, kapliczki, ob
msgid "Cycles what the tracker looks for (items, chests, doors, shrines, objects, breakables, monsters). Hold Shift to cycle backwards."
msgstr "Zmienia, czego szuka tracker (przedmioty, skrzynie, drzwi, kapliczki, obiekty, niszczalne, potwory). Przytrzymaj Shift, aby przełączać wstecz."
#: Source/diablo.cpp
msgid "Cycles what the tracker looks for (items, chests, doors, shrines, objects, breakables, monsters, dead bodies). Hold Shift to cycle backwards."
msgstr "Zmienia, czego szuka tracker (przedmioty, skrzynie, drzwi, kapliczki, obiekty, niszczalne, potwory, martwe ciała). Przytrzymaj Shift, aby przełączać wstecz."
#: Source/diablo.cpp
msgid "Navigate to tracker target"
msgstr "Nawiguj do celu trackera"
@ -12266,6 +12274,10 @@ msgstr "niszczalne"
msgid "monsters"
msgstr "potwory"
#: Source/diablo.cpp
msgid "dead bodies"
msgstr "martwe ciała"
#: Source/diablo.cpp
msgid "No items found."
msgstr "Nie znaleziono żadnych przedmiotów."
@ -12294,6 +12306,10 @@ msgstr "Nie znaleziono żadnych niszczalnych obiektów."
msgid "No monsters found."
msgstr "Nie znaleziono żadnych potworów."
#: Source/diablo.cpp
msgid "No dead bodies found."
msgstr "Nie znaleziono żadnych martwych ciał."
#: Source/diablo.cpp
msgid "Navigating to nearest item."
msgstr "Nawiguję do najbliższego przedmiotu."
@ -12330,6 +12346,18 @@ msgstr "Docelowy potwór zniknął."
msgid "Monster in range."
msgstr "Potwór jest w zasięgu."
#: Source/diablo.cpp
msgid "Dead body"
msgstr "Martwe ciało"
#: Source/diablo.cpp
msgid "Target dead body is gone."
msgstr "Docelowe martwe ciało zniknęło."
#: Source/diablo.cpp
msgid "Dead body in range."
msgstr "Martwe ciało jest w zasięgu."
#: Source/diablo.cpp
msgid "Can't find a nearby tile to walk to."
msgstr "Nie mogę znaleźć pobliskiego pola, na które da się podejść."

Loading…
Cancel
Save