From f74b27e02e6484faac9e78e99fffbcf4a70b47a6 Mon Sep 17 00:00:00 2001 From: mojsior Date: Thu, 29 Jan 2026 18:49:15 +0100 Subject: [PATCH] access: town item tracking, item cues in town, dead bodies tracker --- README.md | 4 +- Source/diablo.cpp | 474 +++++++++++++++++++++---------- Source/utils/proximity_audio.cpp | 139 ++++----- Translations/pl.po | 28 ++ 4 files changed, 417 insertions(+), 228 deletions(-) diff --git a/README.md b/README.md index 5024c0481..09f2189e4 100644 --- a/README.md +++ b/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. diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 5e8760384..842ed6bf0 100644 --- a/Source/diablo.cpp +++ b/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 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 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 FindNearestGroundItemId(Point playerPosition) -{ - std::optional bestId; - int bestDistance = 0; +std::optional FindNearestGroundItemId(Point playerPosition) +{ + std::optional bestId; + int bestDistance = 0; for (int y = 0; y < MAXDUNY; ++y) { for (int x = 0; x < MAXDUNX; ++x) { @@ -2699,15 +2715,47 @@ std::optional 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 FindNearestCorpseId(Point playerPosition) +{ + std::optional 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 CollectNearbyCorpseTrackerCandidates(Point playerPosition, int maxDistance) +{ + std::vector 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 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 FindNearestUnopenedChestObjectId(Point playerPosition) +{ + return FindNearestObjectId(playerPosition, IsTrackedChestObject); +} std::optional 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 nearbyCandidates = CollectNearbyMonsterTrackerCandidates(playerPosition, TrackerCycleDistanceTiles); - if (cycleTarget) { - targetId = FindNextTrackerCandidateId(nearbyCandidates, lockedTargetId); + case TrackerTargetCategory::Monsters: { + const std::vector 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 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(MaxMonsters)) { - AutoWalkTrackerTargetId = -1; + case TrackerTargetCategory::Monsters: { + const int monsterId = AutoWalkTrackerTargetId; + if (monsterId < 0 || monsterId >= static_cast(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(MaxMonsters)) { - targetId = lockedTargetId; - } else { + case TrackerTargetCategory::Monsters: { + if (lockedTargetId >= 0 && lockedTargetId < static_cast(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"), diff --git a/Source/utils/proximity_audio.cpp b/Source/utils/proximity_audio.cpp index 4170b115e..e97c1b046 100644 --- a/Source/utils/proximity_audio.cpp +++ b/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(objectId)), - .sound = soundId, - .position = object.position, - .distance = distance, - .intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinIntervalMs, MaxIntervalMs), - }); - } + ConsiderCandidate(best, CandidateEmitter { + .emitterId = MakeEmitterId(EmitterType::Object, static_cast(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(i)), - .sound = SoundPool::SoundId::Stairs, - .position = triggerPosition, - .distance = distance, - .intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinIntervalMs, MaxIntervalMs), - }); - } + ConsiderCandidate(best, CandidateEmitter { + .emitterId = MakeEmitterId(EmitterType::Trigger, static_cast(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(ActiveMonsters[i]); - const Monster &monster = Monsters[monsterId]; + for (size_t i = 0; i < ActiveMonsterCount; i++) { + const int monsterId = static_cast(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(monsterId)), - .sound = SoundPool::SoundId::Monster, - .position = monsterSoundPosition, - .distance = distance, - .intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinMonsterIntervalMs, MaxMonsterIntervalMs), - }); + ConsiderCandidate(best, CandidateEmitter { + .emitterId = MakeEmitterId(EmitterType::Monster, static_cast(monsterId)), + .sound = SoundPool::SoundId::Monster, + .position = monsterSoundPosition, + .distance = distance, + .intervalMs = IntervalMsForDistance(distance, MaxCueDistanceTiles, MinMonsterIntervalMs, MaxMonsterIntervalMs), + }); + } } std::array requests; diff --git a/Translations/pl.po b/Translations/pl.po index 03affa9bd..0ff7be6ff 100644 --- a/Translations/pl.po +++ b/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ść."