/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: BattleBusSlowDeathBehavior.cpp ///////////////////////////////////////////////////////////////// // Author: Graham Smallwood, June 2003 // Desc: Death for the Battle Bus. Can do a fake slow death before the real one if triggered intentionally /////////////////////////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/Xfer.h" #include "GameLogic/Module/BattleBusSlowDeathBehavior.h" #include "Common/ModelState.h" #include "GameClient/FXList.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Object.h" #include "GameLogic/ObjectCreationList.h" #include "GameLogic/Module/AIUpdate.h" #include "GameLogic/Module/ContainModule.h" #include "GameLogic/Module/PhysicsUpdate.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif enum { GROUND_CHECK_DELAY = 10, ///< Check for colliding with the ground only after this long, to prevent hitting on the way up EMPTY_HULK_CHECK_DELAY = 15 ///< Check for empty hulk every this often instead of every frame }; /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BattleBusSlowDeathBehaviorModuleData::BattleBusSlowDeathBehaviorModuleData( void ) { m_fxStartUndeath = NULL; m_oclStartUndeath = NULL; m_fxHitGround = NULL; m_oclHitGround = NULL; m_throwForce = 1.0f; m_percentDamageToPassengers = 0.0f; m_emptyHulkDestructionDelay = 0; } // end BattleBusSlowDeathBehaviorModuleData // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ /*static*/ void BattleBusSlowDeathBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p ) { SlowDeathBehaviorModuleData::buildFieldParse( p ); static const FieldParse dataFieldParse[] = { { "FXStartUndeath", INI::parseFXList, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_fxStartUndeath ) }, { "OCLStartUndeath", INI::parseObjectCreationList, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_oclStartUndeath ) }, { "FXHitGround", INI::parseFXList, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_fxHitGround ) }, { "OCLHitGround", INI::parseObjectCreationList, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_oclHitGround ) }, { "ThrowForce", INI::parseReal, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_throwForce ) }, { "PercentDamageToPassengers", INI::parsePercentToReal, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_percentDamageToPassengers ) }, { "EmptyHulkDestructionDelay", INI::parseDurationUnsignedInt, NULL, offsetof( BattleBusSlowDeathBehaviorModuleData, m_emptyHulkDestructionDelay ) }, { 0, 0, 0, 0 } }; p.add( dataFieldParse ); } // end buildFieldParse /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BattleBusSlowDeathBehavior::BattleBusSlowDeathBehavior( Thing *thing, const ModuleData *moduleData ) : SlowDeathBehavior( thing, moduleData ) { m_isRealDeath = FALSE; m_isInFirstDeath = FALSE; m_groundCheckFrame = 0; m_penaltyDeathFrame = 0; } // end BattleBusSlowDeathBehavior // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ BattleBusSlowDeathBehavior::~BattleBusSlowDeathBehavior( void ) { } // end ~BattleBusSlowDeathBehavior // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BattleBusSlowDeathBehavior::onDie( const DamageInfo *damageInfo ) { m_isRealDeath = TRUE;// Set beforehand because onDie could result in picking us to beginSlowDeath m_isInFirstDeath = FALSE; // and clear this incase we died while in the alternate death SlowDeathBehavior::onDie(damageInfo); } // end onDie // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ void BattleBusSlowDeathBehavior::beginSlowDeath( const DamageInfo *damageInfo ) { Object *me = getObject(); const BattleBusSlowDeathBehaviorModuleData * data = getBattleBusSlowDeathBehaviorModuleData(); if( !m_isRealDeath ) { // We can intercept and do our extra slow death if this is not from a real onDie m_isInFirstDeath = TRUE; m_groundCheckFrame = TheGameLogic->getFrame() + GROUND_CHECK_DELAY; // First do the special effects FXList::doFXObj(data->m_fxStartUndeath, me ); ObjectCreationList::create(data->m_oclStartUndeath, me, NULL ); if( me->getAI() ) { // Then stop what we were doing me->getAI()->aiIdle(CMD_FROM_AI); } if( me->getPhysics() ) { // Then stop physically me->getPhysics()->clearAcceleration(); me->getPhysics()->scrubVelocity2D(0); // Then get chucked in the air Coord3D throwForce; throwForce.x = 0; throwForce.y = 0; throwForce.z = data->m_throwForce; me->getPhysics()->applyShock(&throwForce); me->getPhysics()->applyRandomRotation(); } // And finally hit those inside for some damage if( me->getContain() ) me->getContain()->processDamageToContained(data->m_percentDamageToPassengers); setWakeFrame(getObject(), UPDATE_SLEEP_NONE); } else { // If a real death, we aren't allowed to do anything SlowDeathBehavior::beginSlowDeath( damageInfo ); } } // end beginSlowDeath // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ UpdateSleepTime BattleBusSlowDeathBehavior::update( void ) { Object *me = getObject(); const BattleBusSlowDeathBehaviorModuleData * data = getBattleBusSlowDeathBehaviorModuleData(); if( m_isInFirstDeath ) { // First death means we are doing our throw up in the air conversion if( (m_groundCheckFrame< TheGameLogic->getFrame() ) && !me->isAboveTerrain() ) { // We're done since we hit the ground m_isInFirstDeath = FALSE; // Do the special FX FXList::doFXObj(data->m_fxHitGround, me ); ObjectCreationList::create(data->m_oclHitGround, me, NULL ); me->setModelConditionState(MODELCONDITION_SECOND_LIFE); // And stop us forever if( me->getAI() ) { // Stop doing anything again (could try to move while in air) me->getAI()->aiIdle(CMD_FROM_AI); } if( me->getPhysics() ) { // And stop physically again me->getPhysics()->clearAcceleration(); me->getPhysics()->scrubVelocity2D(0); } me->setDisabled(DISABLED_HELD); // We can only sleep if we don't have to watch out for being empty. if( data->m_emptyHulkDestructionDelay == 0 ) return UPDATE_SLEEP_FOREVER; else return UPDATE_SLEEP_NONE; // None, so we check for empty as soon as possible } return UPDATE_SLEEP_NONE;// None, since we are waiting to be able to check for ground collision } else if( m_isRealDeath ) { // If a real death, we aren't allowed to do anything return SlowDeathBehavior::update(); } else { // If neither death is active, we must be awake to check for emptiness const ContainModuleInterface *contain = me->getContain(); // Safety, no need to be awake if no special case to wait for if( contain == NULL ) return UPDATE_SLEEP_FOREVER; if( m_penaltyDeathFrame != 0 ) { // We have been timed for death, see if we have re-filled first if( contain->getContainCount() > 0 ) { m_penaltyDeathFrame = 0; return UPDATE_SLEEP(EMPTY_HULK_CHECK_DELAY); } else if( TheGameLogic->getFrame() > m_penaltyDeathFrame ) { // No salvation, see if the timer is up to be killed. Penalty prevents effects, Extra 4 prevents this die module from recalling. // Can't use Suicided death because a Terrorist actually inflicts Suicide deaths on others. me->kill(DAMAGE_PENALTY, DEATH_EXTRA_4); return UPDATE_SLEEP_FOREVER;// Forever, since we are dead and some other death module is in charge } else { return UPDATE_SLEEP(EMPTY_HULK_CHECK_DELAY); } } else { // We are not marked for death, so see if we should be if( contain->getContainCount() == 0 ) { m_penaltyDeathFrame = TheGameLogic->getFrame() + data->m_emptyHulkDestructionDelay; } return UPDATE_SLEEP(EMPTY_HULK_CHECK_DELAY);// Stay awake regardless } } } // end update // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void BattleBusSlowDeathBehavior::crc( Xfer *xfer ) { // extend base class SlowDeathBehavior::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void BattleBusSlowDeathBehavior::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class SlowDeathBehavior::xfer( xfer ); xfer->xferBool( &m_isRealDeath ); xfer->xferBool( &m_isInFirstDeath ); xfer->xferUnsignedInt( &m_groundCheckFrame ); xfer->xferUnsignedInt( &m_penaltyDeathFrame ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void BattleBusSlowDeathBehavior::loadPostProcess( void ) { // extend base class SlowDeathBehavior::loadPostProcess(); } // end loadPostProcess