diff --git a/Core/GameEngine/Include/GameLogic/Damage.h b/Core/GameEngine/Include/GameLogic/Damage.h index 0334c32f27f..c7ea0b191a8 100644 --- a/Core/GameEngine/Include/GameLogic/Damage.h +++ b/Core/GameEngine/Include/GameLogic/Damage.h @@ -270,6 +270,7 @@ class DamageInfoInput : public Snapshot m_deathType = DEATH_NORMAL; m_amount = 0; m_kill = FALSE; + m_enterSecondLife = FALSE; m_shockWaveVector.zero(); m_shockWaveAmount = 0.0f; @@ -286,6 +287,7 @@ class DamageInfoInput : public Snapshot DeathType m_deathType; ///< if this kills us, death type to be used Real m_amount; ///< # value of how much damage to inflict Bool m_kill; ///< will always cause object to die regardless of damage. + Bool m_enterSecondLife; // These are used for damage causing shockwave, forcing units affected to be pushed around Coord3D m_shockWaveVector; ///< This represents the incoming damage vector diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BattleBusSlowDeathBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BattleBusSlowDeathBehavior.h index 3993191f350..a7fa9b9444f 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BattleBusSlowDeathBehavior.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/BattleBusSlowDeathBehavior.h @@ -75,6 +75,7 @@ class BattleBusSlowDeathBehavior : public SlowDeathBehavior // slow death methods virtual void onDie( const DamageInfo *damageInfo ) override; virtual void beginSlowDeath( const DamageInfo *damageInfo ) override; + virtual Bool isRealDeath() const override { return m_isRealDeath; } virtual UpdateSleepTime update() override; protected: diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlowDeathBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlowDeathBehavior.h index 67cfccc5d31..cd5c39cbd0e 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlowDeathBehavior.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SlowDeathBehavior.h @@ -117,6 +117,7 @@ class SlowDeathBehaviorInterface virtual void beginSlowDeath( const DamageInfo *damageInfo ) = 0; virtual Int getProbabilityModifier( const DamageInfo *damageInfo ) const = 0; virtual Bool isDieApplicable(const DamageInfo *damageInfo) const = 0; + virtual Bool isRealDeath() const = 0; }; //------------------------------------------------------------------------------------------------- @@ -151,13 +152,17 @@ class SlowDeathBehavior : public UpdateModule, // SlowDeathBehaviorInterface virtual void beginSlowDeath( const DamageInfo *damageInfo ) override; virtual Int getProbabilityModifier( const DamageInfo *damageInfo ) const override; - virtual Bool isDieApplicable(const DamageInfo *damageInfo) const override { return getSlowDeathBehaviorModuleData()->m_dieMuxData.isDieApplicable(getObject(), damageInfo); } + virtual Bool isDieApplicable(const DamageInfo *damageInfo) const override; + virtual Bool isRealDeath() const override { return true; } + + static Int computeTotalSlowDeathProbability(const Object *obj, const DamageInfo *damageInfo); protected: void doPhaseStuff(SlowDeathPhaseType sdphase); Bool isSlowDeathActivated() const { return (m_flags & (1<m_probabilityModifier + overkillModifier, 1 ); } +Bool SlowDeathBehavior::isDieApplicable(const DamageInfo *damageInfo) const +{ + if (!getSlowDeathBehaviorModuleData()->m_dieMuxData.isDieApplicable(getObject(), damageInfo)) + return false; + +#if !RETAIL_COMPATIBLE_CRC + if (damageInfo->in.m_enterSecondLife != canEnterSecondLife()) + return false; +#endif + + return true; +} + //------------------------------------------------------------------------------------------------- static void calcRandomForce(Real minMag, Real maxMag, Real minPitch, Real maxPitch, Coord3D& force) { @@ -490,23 +503,13 @@ void SlowDeathBehavior::onDie( const DamageInfo *damageInfo ) // deselect this unit for all players. TheGameLogic->deselectObject(obj, PLAYERMASK_ALL, TRUE); - Int total = 0; - BehaviorModule** update = obj->getBehaviorModules(); - for (; *update; ++update) - { - SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface(); - if (sdu != nullptr && sdu->isDieApplicable(damageInfo)) - { - total += sdu->getProbabilityModifier( damageInfo ); - } - } + const Int total = computeTotalSlowDeathProbability(obj, damageInfo); DEBUG_ASSERTCRASH(total > 0, ("Hmm, this is wrong")); - // this returns a value from 1...total, inclusive Int roll = GameLogicRandomValue(1, total); - for (/* UpdateModuleInterface** */ update = obj->getBehaviorModules(); *update; ++update) + for (BehaviorModule** update = obj->getBehaviorModules(); *update; ++update) { SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface(); if (sdu != nullptr && sdu->isDieApplicable(damageInfo)) @@ -523,6 +526,24 @@ void SlowDeathBehavior::onDie( const DamageInfo *damageInfo ) DEBUG_CRASH(("We should never get here")); } + +// ------------------------------------------------------------------------------------------------ +Int SlowDeathBehavior::computeTotalSlowDeathProbability(const Object *obj, const DamageInfo *damageInfo) +{ + Int total = 0; + + for (BehaviorModule** update = obj->getBehaviorModules(); *update; ++update) + { + SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface(); + if (sdu != nullptr && sdu->isDieApplicable(damageInfo)) + { + total += sdu->getProbabilityModifier( damageInfo ); + } + } + + return total; +} + // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp index a6101fae710..df143477c85 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/UndeadBody.cpp @@ -75,8 +75,7 @@ UndeadBody::~UndeadBody() // ------------------------------------------------------------------------------------------------ void UndeadBody::attemptDamage( DamageInfo *damageInfo ) { - // If we are on our first life, see if this damage will kill us. If it will, bind it to one hitpoint - // remaining, then go ahead and take it. + // If we are on our first life, see if this damage will kill us. Bool shouldStartSecondLife = FALSE; if( damageInfo->in.m_damageType != DAMAGE_UNRESISTABLE @@ -90,50 +89,50 @@ void UndeadBody::attemptDamage( DamageInfo *damageInfo ) && IsHealthDamagingDamage(damageInfo->in.m_damageType) ) { - damageInfo->in.m_amount = min( damageInfo->in.m_amount, getHealth() - 1 ); shouldStartSecondLife = TRUE; } - ActiveBody::attemptDamage(damageInfo); - // After we take it (which allows for damaging special effects), we will do our modifications to the body module if( shouldStartSecondLife ) - startSecondLife(damageInfo); + { + if( !startSecondLife(damageInfo) ) + { +#if !RETAIL_COMPATIBLE_CRC + damageInfo->in.m_kill = true; + damageInfo->in.m_enterSecondLife = false; + ActiveBody::attemptDamage(damageInfo); +#endif + } + } + else + { + ActiveBody::attemptDamage(damageInfo); + } } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ -void UndeadBody::startSecondLife(DamageInfo *damageInfo) +Bool UndeadBody::startSecondLife(DamageInfo *damageInfo) { - const UndeadBodyModuleData *data = getUndeadBodyModuleData(); - - // Flag module as no longer intercepting damage - m_isSecondLife = TRUE; +#if RETAIL_COMPATIBLE_CRC + applySecondLife(damageInfo); +#endif - // Modify ActiveBody's max health and initial health - setMaxHealth(data->m_secondLifeMaxHealth, FULLY_HEAL); + damageInfo->in.m_enterSecondLife = TRUE; - // Set Armor set flag to use second life armor - setArmorSetFlag(ARMORSET_SECOND_LIFE); - - // Fire the Slow Death module. The fact that this is not the result of an onDie will cause the special behavior - Int total = 0; - BehaviorModule** update = getObject()->getBehaviorModules(); - for( ; *update; ++update ) - { - SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface(); - if (sdu != nullptr && sdu->isDieApplicable(damageInfo) ) - { - total += sdu->getProbabilityModifier( damageInfo ); - } - } - DEBUG_ASSERTCRASH(total > 0, ("Hmm, this is wrong")); + const Int total = SlowDeathBehavior::computeTotalSlowDeathProbability(getObject(), damageInfo); +#if !RETAIL_COMPATIBLE_CRC + if (total == 0) + return false; +#endif // this returns a value from 1...total, inclusive Int roll = GameLogicRandomValue(1, total); - for( update = getObject()->getBehaviorModules(); *update; ++update) + // Fire one of the Slow Death modules with Second Life at random. + // The fact that this is not the result of an onDie will cause the special behavior. + for (BehaviorModule** update = getObject()->getBehaviorModules(); *update; ++update) { SlowDeathBehaviorInterface* sdu = (*update)->getSlowDeathBehaviorInterface(); if (sdu != nullptr && sdu->isDieApplicable(damageInfo)) @@ -141,14 +140,37 @@ void UndeadBody::startSecondLife(DamageInfo *damageInfo) roll -= sdu->getProbabilityModifier( damageInfo ); if (roll <= 0) { +#if !RETAIL_COMPATIBLE_CRC + applySecondLife(damageInfo); +#endif sdu->beginSlowDeath(damageInfo); - return; + return true; } } } + return false; } +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +void UndeadBody::applySecondLife(DamageInfo *damageInfo) +{ + // In second life, bind it to one hit point remaining, then go ahead and take it + damageInfo->in.m_amount = min(damageInfo->in.m_amount, getHealth() - 1); + + // Damage first to apply hit effects + ActiveBody::attemptDamage(damageInfo); + + // Flag module as no longer intercepting damage + m_isSecondLife = TRUE; + + // Modify ActiveBody's max health and initial health + setMaxHealth(getUndeadBodyModuleData()->m_secondLifeMaxHealth, FULLY_HEAL); + + // Set Armor set flag to use second life armor + setArmorSetFlag(ARMORSET_SECOND_LIFE); +} // ------------------------------------------------------------------------------------------------ /** CRC */