diff --git a/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h index be20d29c292..959993f9e29 100644 --- a/Core/GameEngine/Include/Common/GameDefines.h +++ b/Core/GameEngine/Include/Common/GameDefines.h @@ -20,9 +20,6 @@ #include "WWLib/WWDefines.h" -// Note: Retail compatibility must not be broken before this project officially does. -// Use RETAIL_COMPATIBLE_CRC and RETAIL_COMPATIBLE_XFER_SAVE to guard breaking changes. - #ifndef PRESERVE_BUILDING_RESUMPTION_DELAY #define PRESERVE_BUILDING_RESUMPTION_DELAY (0) // The fix for this unfavorable behavior was approved by the Game Design Committee. #endif @@ -91,6 +88,17 @@ #define PRESERVE_RETAIL_PARTICLES (1) // Preserve original look of particles present in retail Generals 1.08 and Zero Hour 1.04 #endif +#ifndef PRESERVE_RETAIL_NUKE_MISSILE_OUTER_RADIUS_SEARCH +#define PRESERVE_RETAIL_NUKE_MISSILE_OUTER_RADIUS_SEARCH (0) // The fix for this unfavorable behavior was approved by the Game Design Committee. +#endif + +#ifndef PRESERVE_RETAIL_NUKE_MISSILE_OUTER_RADIUS_DAMAGE +#define PRESERVE_RETAIL_NUKE_MISSILE_OUTER_RADIUS_DAMAGE (1) +#endif + +// Note: Retail compatibility must not be broken before this project officially does. +// Use RETAIL_COMPATIBLE_CRC and RETAIL_COMPATIBLE_XFER_SAVE to guard breaking changes. + #ifndef RETAIL_COMPATIBLE_CRC #define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04 #endif diff --git a/Core/Libraries/Include/Lib/BaseType.h b/Core/Libraries/Include/Lib/BaseType.h index 8b5e760aff4..2f5bc1e70f7 100644 --- a/Core/Libraries/Include/Lib/BaseType.h +++ b/Core/Libraries/Include/Lib/BaseType.h @@ -320,6 +320,19 @@ struct Coord2D sub(a); } + Coord2D operator+() const + { + return *this; + } + + Coord2D operator-() const + { + Coord2D c; + c.x = -x; + c.y = -y; + return c; + } + void set( const Coord2D &a ) { x = a.x; @@ -447,6 +460,19 @@ struct ICoord2D sub(a); } + ICoord2D operator+() const + { + return *this; + } + + ICoord2D operator-() const + { + ICoord2D c; + c.x = -x; + c.y = -y; + return c; + } + void set( const ICoord2D &a ) { x = a.x; @@ -577,6 +603,20 @@ struct Coord3D sub(a); } + Coord3D operator+() const + { + return *this; + } + + Coord3D operator-() const + { + Coord3D c; + c.x = -x; + c.y = -y; + c.z = -z; + return c; + } + void set( const Coord3D &a ) { x = a.x; @@ -670,6 +710,20 @@ struct ICoord3D sub(a); } + ICoord3D operator+() const + { + return *this; + } + + ICoord3D operator-() const + { + ICoord3D c; + c.x = -x; + c.y = -y; + c.z = -z; + return c; + } + void set( const ICoord3D &a ) { x = a.x; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileSlowDeathUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileSlowDeathUpdate.cpp index 8a3e971fea4..1d23b3cbd63 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileSlowDeathUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileSlowDeathUpdate.cpp @@ -349,6 +349,7 @@ void NeutronMissileSlowDeathBehavior::doBlast( const BlastInfo *blastInfo ) damageInfo.in.m_amount = blastInfo->minDamage; // scan objects around us and do damage to objects we have "passed over" and are behind us + // TheSuperHackers @todo Optimize this function. Iterates through the blasts even if they apply zero damage. if( blastInfo->outerRadius ) { #if defined(RTS_DEBUG) @@ -359,9 +360,17 @@ void NeutronMissileSlowDeathBehavior::doBlast( const BlastInfo *blastInfo ) } #endif +#if RETAIL_COMPATIBLE_CRC || PRESERVE_RETAIL_NUKE_MISSILE_OUTER_RADIUS_SEARCH + const DistanceCalculationType dc = FROM_CENTER_2D; +#else + // TheSuperHackers @bugfix xezon 16/08/2026 From FROM_CENTER_2D, + // because objects that reach into the outer radius should also receive damage. + const DistanceCalculationType dc = FROM_BOUNDINGSPHERE_3D; +#endif + ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( missilePos, blastInfo->outerRadius, - FROM_CENTER_2D, + dc, nullptr ); MemoryPoolObjectHolder hold( iter ); Object *other; @@ -374,10 +383,26 @@ void NeutronMissileSlowDeathBehavior::doBlast( const BlastInfo *blastInfo ) // get other position otherPos = other->getPosition(); +#if RETAIL_COMPATIBLE_CRC || PRESERVE_RETAIL_NUKE_MISSILE_OUTER_RADIUS_DAMAGE + // compute vector from the missile to other object + forceVector = *otherPos - *missilePos; +#else // compute vector from the missile to other object - forceVector.x = otherPos->x - missilePos->x; - forceVector.y = otherPos->y - missilePos->y; - forceVector.z = otherPos->z - missilePos->z; + // TheSuperHackers @tweak xezon 16/08/2026 No longer calculate the force vector to the center of the object, + // but to half way between the closest edge and the center of the object. This way a more appropriate + // damage value is sampled for a large structure inside the damage fall off range. + const Coord3D missileToObjectCenter = *otherPos - *missilePos; + + Coord3D missileToObjectEdge; + ThePartitionManager->getVectorTo(other, missilePos, dc, missileToObjectEdge); + + // flip direction + missileToObjectEdge = -missileToObjectEdge; + + // take the average between the edge and center vectors + forceVector = missileToObjectEdge + missileToObjectCenter; + forceVector.scale(0.5f); +#endif // try to topple other object other->topple( &forceVector, blastInfo->toppleSpeed, TOPPLE_OPTIONS_NO_BOUNCE |