Fix #2452: Per-object cooldown for under-attack radar alarm#2845
Fix #2452: Per-object cooldown for under-attack radar alarm#2845secprentice wants to merge 1 commit into
Conversation
…alarm Each object now tracks the last logic frame its under-attack alarm fired. A new alarm from the same object is suppressed until 10 seconds have elapsed, regardless of what other objects are doing. This replaces the map-wide PRESERVE_RADAR_WARNING_SUPPRESSION workaround (which could suppress a legitimate base-under-attack warning because a cargo plane's alarm had fired moments earlier). With per-object cooldown each unit is silenced independently, so simultaneous attacks at different locations each report correctly while a single continuously-hit unit (e.g. supply plane) cannot spam the alarm every 10 seconds. PRESERVE_RADAR_WARNING_SUPPRESSION is set to 0; the area-based 250-unit radius suppression in tryEvent handles the case of multiple units in the same locale. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/Common/GameDefines.h | Sets PRESERVE_RADAR_WARNING_SUPPRESSION to 0, disabling the old map-wide suppression hack in favour of the new per-object cooldown; accompanied by an explanatory comment. |
| Core/GameEngine/Include/Common/Radar.h | Adds m_lastUnderAttackAlarmFrame (UnsignedInt) to RadarObject; field placement and comment are consistent with surrounding members. |
| Core/GameEngine/Source/Common/System/Radar.cpp | Initializes m_lastUnderAttackAlarmFrame to 0xFFFFFFFF in the constructor and gates tryUnderAttackEvent on a per-object cooldown; the new field is not serialized in xfer, so the cooldown silently resets on save/load. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Caller
participant tryUnderAttackEvent
participant RadarObject
participant tryEvent
Caller->>tryUnderAttackEvent: obj attacked
tryUnderAttackEvent->>RadarObject: friend_getRadarData()
RadarObject-->>tryUnderAttackEvent: radarData (or null)
alt radarData exists AND cooldown not expired
tryUnderAttackEvent-->>Caller: return (silenced)
else cooldown expired or no radarData
tryUnderAttackEvent->>tryEvent: RADAR_EVENT_UNDER_ATTACK, pos
alt area suppression (within 250 units, within 10s)
tryEvent-->>tryUnderAttackEvent: false
else no recent nearby event
tryEvent->>tryEvent: createEvent()
tryEvent-->>tryUnderAttackEvent: true
tryUnderAttackEvent->>RadarObject: "m_lastUnderAttackAlarmFrame = currentFrame"
tryUnderAttackEvent->>Caller: UI feedback (glow, audio, message)
end
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Caller
participant tryUnderAttackEvent
participant RadarObject
participant tryEvent
Caller->>tryUnderAttackEvent: obj attacked
tryUnderAttackEvent->>RadarObject: friend_getRadarData()
RadarObject-->>tryUnderAttackEvent: radarData (or null)
alt radarData exists AND cooldown not expired
tryUnderAttackEvent-->>Caller: return (silenced)
else cooldown expired or no radarData
tryUnderAttackEvent->>tryEvent: RADAR_EVENT_UNDER_ATTACK, pos
alt area suppression (within 250 units, within 10s)
tryEvent-->>tryUnderAttackEvent: false
else no recent nearby event
tryEvent->>tryEvent: createEvent()
tryEvent-->>tryUnderAttackEvent: true
tryUnderAttackEvent->>RadarObject: "m_lastUnderAttackAlarmFrame = currentFrame"
tryUnderAttackEvent->>Caller: UI feedback (glow, audio, message)
end
end
Comments Outside Diff (1)
-
Core/GameEngine/Source/Common/System/Radar.cpp, line 139-171 (link)Per-object cooldown not persisted across save/load
m_lastUnderAttackAlarmFrameis initialized to0xFFFFFFFFin the constructor but is never written or read inRadarObject::xfer—m_colorand the object ID are xferred but this new field is omitted. On every save/load the cooldown silently resets, so an object that fired an alarm immediately before a save will be able to fire it again immediately after load. For a purely transient UI notification this may be intentional, but if so thexferversion comment is worth updating to document the deliberate omission, otherwise a future maintainer may assume it was overlooked.Prompt To Fix With AI
This is a comment left during a code review. Path: Core/GameEngine/Source/Common/System/Radar.cpp Line: 139-171 Comment: **Per-object cooldown not persisted across save/load** `m_lastUnderAttackAlarmFrame` is initialized to `0xFFFFFFFF` in the constructor but is never written or read in `RadarObject::xfer` — `m_color` and the object ID are xferred but this new field is omitted. On every save/load the cooldown silently resets, so an object that fired an alarm immediately before a save will be able to fire it again immediately after load. For a purely transient UI notification this may be intentional, but if so the `xfer` version comment is worth updating to document the deliberate omission, otherwise a future maintainer may assume it was overlooked. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
Core/GameEngine/Source/Common/System/Radar.cpp:139-171
**Per-object cooldown not persisted across save/load**
`m_lastUnderAttackAlarmFrame` is initialized to `0xFFFFFFFF` in the constructor but is never written or read in `RadarObject::xfer` — `m_color` and the object ID are xferred but this new field is omitted. On every save/load the cooldown silently resets, so an object that fired an alarm immediately before a save will be able to fire it again immediately after load. For a purely transient UI notification this may be intentional, but if so the `xfer` version comment is worth updating to document the deliberate omission, otherwise a future maintainer may assume it was overlooked.
Reviews (1): Last reviewed commit: "Fix #2452: Per-object cooldown for under..." | Re-trigger Greptile
|
See guidelines for AI contributions https://github.com/TheSuperHackers/GeneralsGameCode/blob/main/CONTRIBUTING.md |
Skyaero42
left a comment
There was a problem hiding this comment.
It does not look like this implementation has been tested.
|
|
||
| #ifndef PRESERVE_RADAR_WARNING_SUPPRESSION | ||
| #define PRESERVE_RADAR_WARNING_SUPPRESSION (1) | ||
| #define PRESERVE_RADAR_WARNING_SUPPRESSION (0) // Per-object cooldown in tryUnderAttackEvent supersedes this map-wide suppression. |
There was a problem hiding this comment.
Disabling another fix is not a proper solution. If this fix superseeds the previous fix and not backwards compatibility is needed, than the old fix needs to be removed. If it needs to be backwards compatible, than this macro should not be changed and your fix should be in the same macro tags (#if !PRESERVE_RADAR_WARNING_SUPPRESSION)
| const UnsignedInt framesBetweenAlarms = LOGICFRAMES_PER_SECOND * 10; | ||
| UnsignedInt currentFrame = TheGameLogic->getFrame(); | ||
| RadarObject *radarData = obj->friend_getRadarData(); | ||
| if( radarData && currentFrame - radarData->m_lastUnderAttackAlarmFrame < framesBetweenAlarms ) |
There was a problem hiding this comment.
m_lastUnderAttackAlarmFrame has the maximum value assigned (line 103). This if statement is therefore always true. Therefore line 1063 will never fire and m_lastUnderAttackAlarmFrame will never be reset.
Summary
Fixes #2452.
PR #2368 correctly fixed the broken 2-D squared-distance formula in
Radar::tryEvent. A side effect was that the 10-second suppression window became area-local (250 world units), so simultaneous attacks on cargo planes at different map locations each triggered their own alarm — causing the reported spam.PR #2540 worked around this by adding
PRESERVE_RADAR_WARNING_SUPPRESSION, which forces map-wide 10-second suppression for allRADAR_EVENT_UNDER_ATTACKevents. This restores retail noise levels but introduces a new problem: a cargo plane alarm suppresses a legitimate base under attack warning for the same 10-second window, even though the two events are in completely different locations.This fix
Adds a
m_lastUnderAttackAlarmFramefield toRadarObject(initialized to0xFFFFFFFFso unsigned subtraction safely yields "never fired"). InRadar::tryUnderAttackEvent, the per-object frame is checked before callingtryEvent: if this specific object has already alarmed within the last 10 seconds, the call is skipped entirely.PRESERVE_RADAR_WARNING_SUPPRESSIONis set to0. The original area-based (250-unit radius) suppression intryEventhandles the case of multiple nearby units being attacked simultaneously.Result:
RadarObjectand its own cooldown.Files changed
Core/GameEngine/Include/Common/GameDefines.h—PRESERVE_RADAR_WARNING_SUPPRESSION→ 0Core/GameEngine/Include/Common/Radar.h— addm_lastUnderAttackAlarmFrametoRadarObjectCore/GameEngine/Source/Common/System/Radar.cpp— initialize field; add per-object cooldown check intryUnderAttackEventTest plan