Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
class BodyParticleSystem;
class ParticleSystemTemplate;

constexpr const UnsignedInt InvalidBodyTimestamp = ~0u; ///< damage or healing timestamp that has not been recorded yet

//-------------------------------------------------------------------------------------------------
/** Active body module */
//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -80,6 +82,7 @@ class ActiveBody : public BodyModule

virtual const DamageInfo *getLastDamageInfo() const override { return &m_lastDamageInfo; } ///< return info on last damage dealt to this object
virtual UnsignedInt getLastDamageTimestamp() const override { return m_lastDamageTimestamp; } ///< return frame of last damage dealt
virtual Bool hasLastDamageTimestamp() const override { return m_lastDamageTimestamp != InvalidBodyTimestamp; } ///< return whether a frame of last damage has been recorded
virtual UnsignedInt getLastHealingTimestamp() const override { return m_lastHealingTimestamp; } ///< return frame of last damage dealt
virtual ObjectID getClearableLastAttacker() const override { return (m_lastDamageCleared ? INVALID_ID : m_lastDamageInfo.in.m_sourceID); }
virtual void clearLastAttacker() override { m_lastDamageCleared = true; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class BodyModuleInterface

virtual const DamageInfo *getLastDamageInfo() const = 0;
virtual UnsignedInt getLastDamageTimestamp() const = 0;
virtual Bool hasLastDamageTimestamp() const = 0;
virtual UnsignedInt getLastHealingTimestamp() const = 0;
virtual ObjectID getClearableLastAttacker() const = 0;
virtual void clearLastAttacker() = 0;
Expand Down Expand Up @@ -213,6 +214,7 @@ class BodyModule : public BehaviorModule, public BodyModuleInterface

virtual const DamageInfo *getLastDamageInfo() const override { return nullptr; } ///< return info on last damage dealt to this object
virtual UnsignedInt getLastDamageTimestamp() const override { return 0; } ///< return frame of last damage dealt
virtual Bool hasLastDamageTimestamp() const override { return FALSE; } ///< return whether a frame of last damage has been recorded
virtual UnsignedInt getLastHealingTimestamp() const override { return 0; } ///< return frame of last healing dealt
virtual ObjectID getClearableLastAttacker() const override { return INVALID_ID; }
virtual void clearLastAttacker() override { }
Expand Down
23 changes: 18 additions & 5 deletions Generals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -928,20 +928,27 @@ void AIPlayer::guardSupplyCenter( Team *team, Int minSupplies )
//-------------------------------------------------------------------------------------------------
Bool AIPlayer::isSupplySourceAttacked()
{
const Int SCAN_RATE = 10; // don't scan more often than every 10 seconds.
// TheSuperHackers @bugfix WebbontheWeb 27/09/2026 No longer scans for supply source attacks for just the last 10 frames.
// A prior EA comment indicated that the intent was to look for 10 seconds into the attack history.
const Int REFRESH_RATE = 10;
#if RETAIL_COMPATIBLE_CRC
const Int SCAN_WINDOW = 10;
#else
const Int SCAN_WINDOW = 10 * LOGICFRAMES_PER_SECOND;
#endif
UnsignedInt curFrame = TheGameLogic->getFrame();
if (curFrame==0) {
m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
m_supplySourceAttackCheckFrame = curFrame + REFRESH_RATE;
return false; // can't be attacked on first frame.
}
m_attackedSupplyCenter = INVALID_ID;
if (curFrame < m_supplySourceAttackCheckFrame) {
return false;
}
if (m_player->getAttackedFrame()+SCAN_RATE < curFrame) {
if (m_player->getAttackedFrame() + SCAN_WINDOW < curFrame) {
return false; // haven't been attacked recently.
}
m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
m_supplySourceAttackCheckFrame = curFrame + REFRESH_RATE;

// Scan my units.
Player::PlayerTeamList::const_iterator it;
Expand All @@ -968,7 +975,13 @@ Bool AIPlayer::isSupplySourceAttacked()
if (info->out.m_noEffect) {
continue;
}
if (body->getLastDamageTimestamp() + SCAN_RATE > curFrame) {
#if !RETAIL_COMPATIBLE_CRC
// Ignore undamaged units.
if (!body->hasLastDamageTimestamp()) {
continue;
}
#endif
if (body->getLastDamageTimestamp() + SCAN_WINDOW > curFrame) {
// winner.
m_attackedSupplyCenter = obj->getID();
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ ActiveBody::ActiveBody( Thing *thing, const ModuleData* moduleData ) :
m_curArmorSet(nullptr),
m_frontCrushed(false),
m_backCrushed(false),
m_lastDamageTimestamp(0xffffffff),// So we don't think we just got damaged on the first frame
m_lastHealingTimestamp(0xffffffff),// So we don't think we just got healed on the first frame
m_lastDamageTimestamp(InvalidBodyTimestamp),// So we don't think we just got damaged on the first frame
m_lastHealingTimestamp(InvalidBodyTimestamp),// So we don't think we just got healed on the first frame
m_curDamageState(BODY_PRISTINE),
m_nextDamageFXTime(0),
m_lastDamageFXDone((DamageType)-1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
class BodyParticleSystem;
class ParticleSystemTemplate;

constexpr const UnsignedInt InvalidBodyTimestamp = ~0u; ///< damage or healing timestamp that has not been recorded yet

//-------------------------------------------------------------------------------------------------
/** Active body module */
//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -88,6 +90,7 @@ class ActiveBody : public BodyModule

virtual const DamageInfo *getLastDamageInfo() const override { return &m_lastDamageInfo; } ///< return info on last damage dealt to this object
virtual UnsignedInt getLastDamageTimestamp() const override { return m_lastDamageTimestamp; } ///< return frame of last damage dealt
virtual Bool hasLastDamageTimestamp() const override { return m_lastDamageTimestamp != InvalidBodyTimestamp; } ///< return whether a frame of last damage has been recorded
virtual UnsignedInt getLastHealingTimestamp() const override { return m_lastHealingTimestamp; } ///< return frame of last damage dealt
virtual ObjectID getClearableLastAttacker() const override { return (m_lastDamageCleared ? INVALID_ID : m_lastDamageInfo.in.m_sourceID); }
virtual void clearLastAttacker() override { m_lastDamageCleared = true; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class BodyModuleInterface

virtual const DamageInfo *getLastDamageInfo() const = 0;
virtual UnsignedInt getLastDamageTimestamp() const = 0;
virtual Bool hasLastDamageTimestamp() const = 0;
virtual UnsignedInt getLastHealingTimestamp() const = 0;
virtual ObjectID getClearableLastAttacker() const = 0;
virtual void clearLastAttacker() = 0;
Expand Down Expand Up @@ -229,6 +230,7 @@ class BodyModule : public BehaviorModule, public BodyModuleInterface

virtual const DamageInfo *getLastDamageInfo() const override { return nullptr; } ///< return info on last damage dealt to this object
virtual UnsignedInt getLastDamageTimestamp() const override { return 0; } ///< return frame of last damage dealt
virtual Bool hasLastDamageTimestamp() const override { return FALSE; } ///< return whether a frame of last damage has been recorded
virtual UnsignedInt getLastHealingTimestamp() const override { return 0; } ///< return frame of last healing dealt
virtual ObjectID getClearableLastAttacker() const override { return INVALID_ID; }
virtual void clearLastAttacker() override { }
Expand Down
23 changes: 18 additions & 5 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -935,20 +935,27 @@ void AIPlayer::guardSupplyCenter( Team *team, Int minSupplies )
//-------------------------------------------------------------------------------------------------
Bool AIPlayer::isSupplySourceAttacked()
{
const Int SCAN_RATE = 10; // don't scan more often than every 10 seconds.
// TheSuperHackers @bugfix WebbontheWeb 27/09/2026 No longer scans for supply source attacks for just the last 10 frames.
// Original EA comment: "don't scan more often than every 10 seconds."
Comment thread
WebbontheWeb marked this conversation as resolved.
const Int REFRESH_RATE = 10; // 10 frames.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
WebbontheWeb marked this conversation as resolved.
#if RETAIL_COMPATIBLE_CRC
const Int SCAN_WINDOW = 10;
#else
const Int SCAN_WINDOW = 10 * LOGICFRAMES_PER_SECOND; // 10 seconds of attack history.
Comment thread
WebbontheWeb marked this conversation as resolved.
#endif
UnsignedInt curFrame = TheGameLogic->getFrame();
if (curFrame==0) {
m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
m_supplySourceAttackCheckFrame = curFrame + REFRESH_RATE;
return false; // can't be attacked on first frame.
}
m_attackedSupplyCenter = INVALID_ID;
if (curFrame < m_supplySourceAttackCheckFrame) {
return false;
}
if (m_player->getAttackedFrame()+SCAN_RATE < curFrame) {
if (m_player->getAttackedFrame() + SCAN_WINDOW < curFrame) {
return false; // haven't been attacked recently.
}
m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
m_supplySourceAttackCheckFrame = curFrame + REFRESH_RATE;

// Scan my units.
Player::PlayerTeamList::const_iterator it;
Expand All @@ -975,7 +982,13 @@ Bool AIPlayer::isSupplySourceAttacked()
if (info->out.m_noEffect) {
continue;
}
if (body->getLastDamageTimestamp() + SCAN_RATE > curFrame) {
#if !RETAIL_COMPATIBLE_CRC
// Ignore undamaged units.
if (!body->hasLastDamageTimestamp()) {
continue;
}
#endif
if (body->getLastDamageTimestamp() + SCAN_WINDOW > curFrame) {
// winner.
m_attackedSupplyCenter = obj->getID();
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ ActiveBody::ActiveBody( Thing *thing, const ModuleData* moduleData ) :
m_curArmorSet(nullptr),
m_frontCrushed(false),
m_backCrushed(false),
m_lastDamageTimestamp(0xffffffff),// So we don't think we just got damaged on the first frame
m_lastHealingTimestamp(0xffffffff),// So we don't think we just got healed on the first frame
m_lastDamageTimestamp(InvalidBodyTimestamp),// So we don't think we just got damaged on the first frame
m_lastHealingTimestamp(InvalidBodyTimestamp),// So we don't think we just got healed on the first frame
m_curDamageState(BODY_PRISTINE),
m_nextDamageFXTime(0),
m_lastDamageFXDone((DamageType)-1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ Bool StealthUpdate::allowedToStealth( Object *stealthOwner ) const
#endif
{
//Can't stealth if we just took damage in the last frame or two.
if( self->getBodyModule()->getLastDamageTimestamp() != 0xffffffff )
if( self->getBodyModule()->hasLastDamageTimestamp() )
{
//But it's initialized to 0xffffffff so we don't think we took damage on the first frame.
//But it's initialized to InvalidBodyTimestamp so we don't think we took damage on the first frame.
return FALSE;
}
}
Expand Down