diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h index 9defb85d715..463688119f5 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h @@ -418,6 +418,8 @@ class W3DModelDraw : public DrawModule, public ObjectDrawInterface virtual Real getAnimationScrubScalar() const; #endif + virtual void setNeedUpdateTurretPositioning(Bool set); + virtual ObjectDrawInterface* getObjectDrawInterface() override { return this; } virtual const ObjectDrawInterface* getObjectDrawInterface() const override { return this; } @@ -508,6 +510,9 @@ class W3DModelDraw : public DrawModule, public ObjectDrawInterface Bool m_pauseAnimation; Int m_animationMode; + Bool m_needUpdateTurretPosition; + Bool m_doHandleRecoil; + void adjustAnimation(const ModelConditionInfo* prevState, Real prevAnimFraction); Real getCurrentAnimFraction() const; void applyCorrectModelStateAnimation(); diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index 3b46fe5a5e4..874a4341eba 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -1742,6 +1742,8 @@ W3DModelDraw::W3DModelDraw(Thing *thing, const ModuleData* moduleData) : DrawMod } m_needRecalcBoneParticleSystems = false; m_fullyObscuredByShroud = false; + m_needUpdateTurretPosition = true; + m_doHandleRecoil = true; // only validate the current time-of-day and weather conditions by default. getW3DModelDrawModuleData()->validateStuffForTimeAndWeather(getDrawable(), @@ -2419,7 +2421,7 @@ void W3DModelDraw::stopClientParticleSystems() */ void W3DModelDraw::handleClientTurretPositioning() { - if (!m_curState || !(m_curState->m_validStuff & ModelConditionInfo::TURRETS_VALID)) + if (!m_curState || !(m_curState->m_validStuff & ModelConditionInfo::TURRETS_VALID) || !m_needUpdateTurretPosition) return; for (int tslot = 0; tslot < MAX_TURRETS; ++tslot) @@ -2493,14 +2495,18 @@ void W3DModelDraw::handleClientTurretPositioning() @todo fix me someday (srj) */ +// TheSuperHackers @performance IamInnocent 01/01/26 - Adjust Turret Positioning, Recoil, and Muzzle to only Update when Necessary void W3DModelDraw::handleClientRecoil() { const W3DModelDrawModuleData* d = getW3DModelDrawModuleData(); - if (!(m_curState->m_validStuff & ModelConditionInfo::BARRELS_VALID)) + if (!(m_curState->m_validStuff & ModelConditionInfo::BARRELS_VALID) || !m_doHandleRecoil) { return; } + // Set the Requirement of Recoil Update to False first, if there is any recoil while checking, it is set to True. + m_doHandleRecoil = FALSE; + // do recoil, if any for (int wslot = 0; wslot < WEAPONSLOT_COUNT; ++wslot) { @@ -2520,6 +2526,8 @@ void W3DModelDraw::handleClientRecoil() Bool hidden = recoils[i].m_state != WeaponRecoilInfo::RECOIL_START; //DEBUG_LOG(("adjust muzzleflash %08lx for Draw %08lx state %s to %d at frame %d",subObjToHide,this,m_curState->m_description.str(),hidden?1:0,TheGameLogic->getFrame())); barrels[i].setMuzzleFlashHidden(m_renderObject, hidden); + if(!hidden) + m_doHandleRecoil = TRUE; // There's more recoil, need to update } const Real TINY_RECOIL = 0.01f; @@ -2544,6 +2552,7 @@ void W3DModelDraw::handleClientRecoil() { recoils[i].m_state = WeaponRecoilInfo::SETTLE; } + m_doHandleRecoil = TRUE; // There's more recoil, need to update break; case WeaponRecoilInfo::SETTLE: @@ -2553,6 +2562,7 @@ void W3DModelDraw::handleClientRecoil() recoils[i].m_shift = 0.0f; recoils[i].m_state = WeaponRecoilInfo::IDLE; } + m_doHandleRecoil = TRUE; // There's more recoil, need to update break; } @@ -3753,6 +3763,8 @@ Bool W3DModelDraw::handleWeaponFireFX(WeaponSlotType wslot, Int specificBarrelTo if (info.m_recoilBone || info.m_muzzleFlashBone) { + m_doHandleRecoil = TRUE; + //DEBUG_LOG(("START muzzleflash %08lx for Draw %08lx state %s at frame %d",info.m_muzzleFlashBone,this,m_curState->m_description.str(),TheGameLogic->getFrame())); WeaponRecoilInfo& recoil = m_weaponRecoilInfoVec[wslot][specificBarrelToUse]; recoil.m_state = WeaponRecoilInfo::RECOIL_START; @@ -3895,6 +3907,8 @@ void W3DModelDraw::rebuildWeaponRecoilInfo(const ModelConditionInfo* state) } } } + // Resetting Model calls for this function, everytime new recoil Info is configured, need to configure to check for new recoil or Muzzle + m_doHandleRecoil = TRUE; } //------------------------------------------------------------------------------------------------- @@ -4005,6 +4019,12 @@ void W3DModelDraw::updateSubObjects() } } +//------------------------------------------------------------------------------------------------- +void W3DModelDraw::setNeedUpdateTurretPositioning(Bool set) +{ + m_needUpdateTurretPosition = set; +} + // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ @@ -4264,6 +4284,9 @@ void W3DModelDraw::loadPostProcess() // extend base class DrawModule::loadPostProcess(); + m_needUpdateTurretPosition = TRUE; + m_doHandleRecoil = TRUE; + } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Include/Common/DrawModule.h b/GeneralsMD/Code/GameEngine/Include/Common/DrawModule.h index d2595fc4954..0184b43d83d 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/DrawModule.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/DrawModule.h @@ -188,6 +188,8 @@ class ObjectDrawInterface virtual void setSelectable(Bool selectable) = 0; + virtual void setNeedUpdateTurretPositioning(Bool set) = 0; + /** This call says, "I want the current animation (if any) to take n frames to complete a single cycle". If it's a looping anim, each loop will take n frames. someday, we may want to add the option to insert diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h index e0e4707f93c..00b51a0b8f8 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h @@ -518,6 +518,8 @@ class Drawable : public Thing, Real getAnimationScrubScalar() const; // lorenzen // returns 0 to 1... where are we between start and finish? #endif + void setNeedUpdateTurretPositioning(Bool set); + UnsignedInt getExpirationDate() const { return m_expirationDate; } void setExpirationDate(UnsignedInt frame) { m_expirationDate = frame; } diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 29d391b81a3..0771605ad6b 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -637,6 +637,8 @@ class Object : public Thing, public Snapshot // player. These are friend_s for player. void friend_adjustPowerForPlayer( Bool incoming ); + void setNeedUpdateTurretPositioning(Bool set); + protected: void setOrRestoreTeam( Team* team, Bool restoring ); @@ -818,6 +820,8 @@ class Object : public Thing, public Snapshot Bool m_singleUseCommandUsed; Bool m_isReceivingDifficultyBonus; + Bool m_turretNeedPositioning; + }; // deleteInstance is not meant to be used with Object in order to require the use of TheGameLogic->destroyObject() diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp index 9985fa0e8ce..66fe026f656 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp @@ -751,6 +751,17 @@ Real Drawable::getAnimationScrubScalar() const // lorenzen } #endif +//------------------------------------------------------------------------------------------------- +void Drawable::setNeedUpdateTurretPositioning(Bool set) +{ + for (DrawModule** dm = getDrawModules(); *dm; ++dm) + { + ObjectDrawInterface* di = (*dm)->getObjectDrawInterface(); + if (di) + di->setNeedUpdateTurretPositioning(set); + } +} + //------------------------------------------------------------------------------------------------- Int Drawable::getPristineBonePositions(const char* boneNamePrefix, Int startIndex, Coord3D* positions, Matrix3D* transforms, Int maxBones) const { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cpp index 78395baf998..ef33181e3bc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cpp @@ -585,6 +585,7 @@ void TurretAI::setTurretTargetObject( Object *victim, Bool forceAttacking ) if (sid != TURRETAI_AIM && sid != TURRETAI_FIRE) m_turretStateMachine->setState( TURRETAI_AIM ); m_victimInitialTeam = victim->getTeam(); + getOwner()->setNeedUpdateTurretPositioning(TRUE); } else { @@ -625,6 +626,7 @@ void TurretAI::setTurretTargetPosition( const Coord3D* pos ) if (sid != TURRETAI_AIM && sid != TURRETAI_FIRE) m_turretStateMachine->setState( TURRETAI_AIM ); m_victimInitialTeam = nullptr; + getOwner()->setNeedUpdateTurretPositioning(TRUE); } else { @@ -639,6 +641,7 @@ void TurretAI::setTurretTargetPosition( const Coord3D* pos ) void TurretAI::recenterTurret() { m_turretStateMachine->setState( TURRETAI_RECENTER ); + getOwner()->setNeedUpdateTurretPositioning(TRUE); } //---------------------------------------------------------------------------------------------------------- @@ -746,6 +749,7 @@ UpdateSleepTime TurretAI::updateTurretAI() //------------------------------------------------------------------------------------------------- void TurretAI::setTurretEnabled( Bool enabled ) { + if (enabled) getOwner()->setNeedUpdateTurretPositioning(TRUE); if (enabled && !m_enabled) { // be sure we wake up! @@ -1153,6 +1157,8 @@ StateReturnType TurretAIAimTurretState::update() pitchAlignedToNemesis = turret->friend_turnTowardsPitch(desiredPitch, 1.0f); } + obj->setNeedUpdateTurretPositioning(!turnAlignedToNemesis || !pitchAlignedToNemesis); + // For now, we require that we're within range before we can successfully exit the AIM state, // and move into the FIRE state. if (turnAlignedToNemesis && pitchAlignedToNemesis && @@ -1217,12 +1223,14 @@ StateReturnType TurretAIRecenterTurretState::update() if( getMachineOwner()->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION)) return STATE_CONTINUE;//ML so that under-construction base-defenses do not re-center while under construction - TurretAI* turret = getTurretAI(); Bool angleAligned = turret->friend_turnTowardsAngle(turret->getNaturalTurretAngle(), 0.5f, 0.0f); Bool pitchAligned = turret->friend_turnTowardsPitch(turret->getNaturalTurretPitch(), 0.5f); - if( angleAligned && pitchAligned ) + Bool turretAligned = angleAligned && pitchAligned; + getMachineOwner()->setNeedUpdateTurretPositioning(!turretAligned); + + if( turretAligned ) return STATE_SUCCESS; return STATE_CONTINUE; @@ -1374,7 +1382,10 @@ StateReturnType TurretAIIdleScanState::update() Bool angleAligned = getTurretAI()->friend_turnTowardsAngle(getTurretAI()->getNaturalTurretAngle() + m_desiredAngle, 0.5f, 0.0f); Bool pitchAligned = getTurretAI()->friend_turnTowardsPitch(getTurretAI()->getNaturalTurretPitch(), 0.5f); - if( angleAligned && pitchAligned ) + Bool turretAligned = angleAligned && pitchAligned; + getMachineOwner()->setNeedUpdateTurretPositioning(!turretAligned); + + if( turretAligned ) return STATE_SUCCESS; return STATE_CONTINUE; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 0bd898aae75..2d187ba6881 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -497,6 +497,8 @@ Object::Object( const ThingTemplate *tt, const ObjectStatusMaskType &objectStatu m_soleHealingBenefactorID = INVALID_ID; ///< who is the only other object that can give me this non-stacking heal benefit? m_soleHealingBenefactorExpirationFrame = 0; ///< on what frame can I accept healing (thus to switch) from a new benefactor + m_turretNeedPositioning = FALSE; + // TheSuperHackers @bugfix Mauller/xezon 02/08/2025 sendObjectCreated needs calling before CreateModule's are initialized to prevent drawable related crashes // This predominantly occurs with the veterancy create module when the chemical suits upgrade is unlocked as it tries to set the terrain decal. @@ -4531,6 +4533,7 @@ void Object::loadPostProcess() else m_containedBy = nullptr; + setNeedUpdateTurretPositioning(TRUE); } //------------------------------------------------------------------------------------------------- @@ -6552,3 +6555,13 @@ ObjectID Object::calculateCountermeasureToDivertTo( const Object& victim ) } return INVALID_ID; } + +//------------------------------------------------------------------------------------------------- +void Object::setNeedUpdateTurretPositioning(Bool set) +{ + if(m_turretNeedPositioning != set && getDrawable()) + { + m_turretNeedPositioning = set; + getDrawable()->setNeedUpdateTurretPositioning(set); + } +} diff --git a/GeneralsReplays b/GeneralsReplays index 0711bf9d1b6..af0c61ccdcc 160000 --- a/GeneralsReplays +++ b/GeneralsReplays @@ -1 +1 @@ -Subproject commit 0711bf9d1b60aeaaad98a52edd9b7b1c16be4937 +Subproject commit af0c61ccdccbf06750b0ecd2de99fe65842bfc1a