Prerequisites
Game Version
Bug Description
In AIPlayer::isSupplySourceAttacked(), the constant SCAN_RATE is set to 10 with a comment stating "don't scan more often than every 10 seconds". However, this value is used directly with frame numbers without multiplying by LOGICFRAMES_PER_SECOND, resulting in a 10-frame window (~0.33 seconds) instead of the intended 10-second window.
File: GeneralsGameCode/Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp
const Int SCAN_RATE = 10; // don't scan more often than every 10 seconds.
How SCAN_RATE is Used
Player attack recency check:
if (m_player->getAttackedFrame() + SCAN_RATE < curFrame) {
return false; // haven't been attacked recently.
}
Object damage recency check:
if (body->getLastDamageTimestamp() + SCAN_RATE > curFrame) {
m_attackedSupplyCenter = obj->getID();
return true;
}
Expected vs Actual Behavior
| Aspect |
Expected |
Actual |
| Attack memory window |
10 seconds (300 frames) |
0.33 seconds (10 frames) |
| AI reaction to harvester harassment |
Consistent defense response |
AI "forgets" attack between enemy shots |
Calculation
- Game logic runs at 30 FPS (
LOGICFRAMES_PER_SECOND = 30)
SCAN_RATE = 10 frames
- 10 frames / 30 FPS = 0.33 seconds
Impact on Gameplay
Most units fire every 2-3 seconds. If an enemy attacks the AI's harvester:
- Frame 1000: Enemy hits harvester.
getLastDamageTimestamp() = 1000
- Frame 1011 (~0.36 sec later): AI checks
isSupplySourceAttacked()
1000 + 10 = 1010, but 1010 < 1011 → AI thinks attack is over
- AI does not send guards, while harvester continues to be destroyed
The AI only detects an attack if the check happens within 0.33 seconds of the last hit. With typical AI script intervals of 5+ seconds, the probability of detection is approximately 6-7%.
Comparison with Other Timers in AIPlayer.cpp
All other second-based timers in the same file correctly use multiplication:
m_teamTimer = m_teamSeconds * LOGICFRAMES_PER_SECOND
m_structureTimer = TheAI->getAiData()->m_structureSeconds * LOGICFRAMES_PER_SECOND
team->m_frameStarted + 60 * LOGICFRAMES_PER_SECOND
Only SCAN_RATE is missing the multiplier.
Suggested Fix
const Int SCAN_RATE = 10 * LOGICFRAMES_PER_SECOND; // 300 frames = 10 seconds
Reproduction Steps
- Start a skirmish game against an AI opponent (any difficulty)
- Wait for the AI to build harvesters and begin collecting resources
- Send a single slow-firing unit (e.g., Tank, Missile Defender) to attack the AI's harvester
- Observe that the AI rarely or never sends units to defend the harvester
- The harvester is destroyed with minimal AI response
Note: This bug is in the source code logic and affects AI behavior probabilistically. The AI will occasionally respond if the timing aligns with the 0.33-second window, but in most cases it will not react appropriately to sustained harassment.
Additional Context
No response
Prerequisites
Game Version
Bug Description
In
AIPlayer::isSupplySourceAttacked(), the constantSCAN_RATEis set to10with a comment stating "don't scan more often than every 10 seconds". However, this value is used directly with frame numbers without multiplying byLOGICFRAMES_PER_SECOND, resulting in a 10-frame window (~0.33 seconds) instead of the intended 10-second window.File:
GeneralsGameCode/Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cppHow SCAN_RATE is Used
Player attack recency check:
Object damage recency check:
Expected vs Actual Behavior
Calculation
LOGICFRAMES_PER_SECOND = 30)SCAN_RATE = 10framesImpact on Gameplay
Most units fire every 2-3 seconds. If an enemy attacks the AI's harvester:
getLastDamageTimestamp() = 1000isSupplySourceAttacked()1000 + 10 = 1010, but1010 < 1011→ AI thinks attack is overThe AI only detects an attack if the check happens within 0.33 seconds of the last hit. With typical AI script intervals of 5+ seconds, the probability of detection is approximately 6-7%.
Comparison with Other Timers in AIPlayer.cpp
All other second-based timers in the same file correctly use multiplication:
m_teamTimer = m_teamSeconds * LOGICFRAMES_PER_SECONDm_structureTimer = TheAI->getAiData()->m_structureSeconds * LOGICFRAMES_PER_SECONDteam->m_frameStarted + 60 * LOGICFRAMES_PER_SECONDOnly
SCAN_RATEis missing the multiplier.Suggested Fix
Reproduction Steps
Note: This bug is in the source code logic and affects AI behavior probabilistically. The AI will occasionally respond if the timing aligns with the 0.33-second window, but in most cases it will not react appropriately to sustained harassment.
Additional Context
No response