/* ** Command & Conquer Renegade(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 . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/beacongameobj.cpp $* * * * $Author:: Greg_h $* * * * $Modtime:: 6/14/02 1:59p $* * * * $Revision:: 41 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "beacongameobj.h" #include "debug.h" #include "phys.h" #include "combat.h" #include "soldier.h" #include "persistfactory.h" #include "combatchunkid.h" #include "weaponmanager.h" #include "simpledefinitionfactory.h" #include "wwhack.h" #include "decophys.h" #include "assets.h" #include "gameobjmanager.h" #include "wwaudio.h" #include "wwprofile.h" #include "cinematicgameobj.h" #include "basecontroller.h" #include "playertype.h" #include "colmath.h" #include "wwaudio.h" #include "audiblesound.h" #include "translateobj.h" #include "translatedb.h" #include "messagewindow.h" #include "weaponbag.h" #include "objlibrary.h" #include "hudinfo.h" #include "explosion.h" #include "backgroundmgr.h" #include "weathermgr.h" #include "persistentgameobjobserver.h" #include "apppackettypes.h" #include "weapons.h" //////////////////////////////////////////////////////////////// // Hacks //////////////////////////////////////////////////////////////// DECLARE_FORCE_LINK (Beacon) //////////////////////////////////////////////////////////////// // Editable and persist factories //////////////////////////////////////////////////////////////// SimplePersistFactoryClass _BeaconGameObjDefPersistFactory; SimplePersistFactoryClass _BeaconGameObjPersistFactory; DECLARE_DEFINITION_FACTORY (BeaconGameObjDef, CLASSID_GAME_OBJECT_DEF_BEACON, "Beacon") _BeaconGameObjDefDefFactory; //////////////////////////////////////////////////////////////// // Save/Load constants //////////////////////////////////////////////////////////////// enum { CHUNKID_MONITOR_PARENT = 0x07250256, }; enum { CHUNKID_DEF_PARENT = 0x02190435, CHUNKID_DEF_VARIABLES, MICROCHUNKID_DEF_BROADCAST_TIME = 1, MICROCHUNKID_DEF_ARM_TIME, MICROCHUNKID_DEF_DISARM_TIME, MICROCHUNKID_DEF_DETONATE_TIME, MICROCHUNKID_DEF_ARMED_SOUNDID, MICROCHUNKID_DEF_DISARMING_TEXTID, MICROCHUNKID_DEF_DISARMED_TEXTID, MICROCHUNKID_DEF_ARMING_TEXTID, MICROCHUNKID_DEF_POST_CINEMATIC_DEFID, MICROCHUNKID_DEF_ARM_INTERRUPT_TEXTID, MICROCHUNKID_DEF_DISARM_INTERRUPT_TEXTID, MICROCHUNKID_DEF_ARMING_ANIM_NAME, MICROCHUNKID_DEF_PRE_CINEMATIC_DEFID, MICROCHUNKID_DEF_EXPLOSION_DEFID, MICROCHUNKID_DEF_POST_DETONATE_TIME, MICROCHUNKID_DEF_PRE_DETONATE_CINEMATIC_DELAY, MICROCHUNKID_DEF_IS_NUKE, }; enum { CHUNKID_PARENT = 0x0219043, CHUNKID_VARIABLES, CHUNKID_OWNER, CHUNKID_CINEMATIC, MICROCHUNKID_STATE = 1, MICROCHUNKID_STATE_TIMER, MICROCHUNKID_DETONATE_TIMER, MICROCHUNKID_PRE_DETONATE_TIMER, MICROCHUNKID_IS_ARMED, }; //////////////////////////////////////////////////////////////// // // BeaconGameObjDef // //////////////////////////////////////////////////////////////// BeaconGameObjDef::BeaconGameObjDef (void) : BroadcastToAllTime (5.0F), ArmTime (10.0F), DisarmTime (10.0F), PreDetonateCinematicDelay(0), DetonateTime (30.0F), PostDetonateTime (10.0F), ArmedSoundDefID (0), DisarmingTextID (0), DisarmedTextID (0), ArmingTextID (0), ArmingInterruptedTextID (0), DisarmingInterruptedTextID (0), PreDetonateCinematicDefID (0), PostDetonateCinematicDefID (0), ExplosionDefID (0), IsNuke( true ) { // // Editable support // EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_STRING, ArmingAnimationName); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_FLOAT, BroadcastToAllTime); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_FLOAT, ArmTime); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_FLOAT, DisarmTime); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_FLOAT, PreDetonateCinematicDelay); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_FLOAT, DetonateTime); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_FLOAT, PostDetonateTime); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, ArmedSoundDefID); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, ArmingTextID); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, ArmingInterruptedTextID); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, DisarmingTextID); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, DisarmingInterruptedTextID); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, DisarmedTextID); EDITABLE_PARAM (BeaconGameObjDef, ParameterClass::TYPE_BOOL, IsNuke); #ifdef PARAM_EDITING_ON GenericDefParameterClass *param = new GenericDefParameterClass (&PreDetonateCinematicDefID); param->Set_Class_ID (CLASSID_GAME_OBJECT_DEF_CINEMATIC); param->Set_Name ("Pre-Detonate Cinematic Obj"); GENERIC_EDITABLE_PARAM (BeaconGameObjDef, param) param = new GenericDefParameterClass (&PostDetonateCinematicDefID); param->Set_Class_ID (CLASSID_GAME_OBJECT_DEF_CINEMATIC); param->Set_Name ("Post-Detonate Cinematic Obj"); GENERIC_EDITABLE_PARAM (BeaconGameObjDef, param) param = new GenericDefParameterClass (&ExplosionDefID); param->Set_Class_ID (CLASSID_DEF_EXPLOSION); param->Set_Name ("Explosion Obj"); GENERIC_EDITABLE_PARAM (BeaconGameObjDef, param) #endif //PARAM_EDITING_ON return ; } //////////////////////////////////////////////////////////////// // // ~BeaconGameObjDef // //////////////////////////////////////////////////////////////// BeaconGameObjDef::~BeaconGameObjDef (void) { return ; } //////////////////////////////////////////////////////////////// // // Get_Class_ID // //////////////////////////////////////////////////////////////// uint32 BeaconGameObjDef::Get_Class_ID (void) const { return CLASSID_GAME_OBJECT_DEF_BEACON; } //////////////////////////////////////////////////////////////// // // Create // //////////////////////////////////////////////////////////////// PersistClass * BeaconGameObjDef::Create (void) const { BeaconGameObj *beacon = new BeaconGameObj; beacon->Init (*this); return beacon; } //////////////////////////////////////////////////////////////// // // Create // //////////////////////////////////////////////////////////////// bool BeaconGameObjDef::Save (ChunkSaveClass &csave) { csave.Begin_Chunk (CHUNKID_DEF_PARENT); SimpleGameObjDef::Save (csave); csave.End_Chunk (); csave.Begin_Chunk (CHUNKID_DEF_VARIABLES); WRITE_MICRO_CHUNK_WWSTRING (csave, MICROCHUNKID_DEF_ARMING_ANIM_NAME, ArmingAnimationName); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_BROADCAST_TIME, BroadcastToAllTime); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_ARM_TIME, ArmTime); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_DISARM_TIME, DisarmTime); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_PRE_DETONATE_CINEMATIC_DELAY,PreDetonateCinematicDelay); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_DETONATE_TIME, DetonateTime); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_POST_DETONATE_TIME, PostDetonateTime); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_ARMED_SOUNDID, ArmedSoundDefID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_DISARMING_TEXTID, DisarmingTextID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_DISARMED_TEXTID, DisarmedTextID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_ARMING_TEXTID, ArmingTextID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_ARM_INTERRUPT_TEXTID, ArmingInterruptedTextID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_DISARM_INTERRUPT_TEXTID, DisarmingInterruptedTextID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_PRE_CINEMATIC_DEFID, PreDetonateCinematicDefID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_POST_CINEMATIC_DEFID, PostDetonateCinematicDefID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_EXPLOSION_DEFID, ExplosionDefID); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_IS_NUKE, IsNuke); csave.End_Chunk (); return true; } //////////////////////////////////////////////////////////////// // // Load // //////////////////////////////////////////////////////////////// bool BeaconGameObjDef::Load (ChunkLoadClass &cload) { while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case CHUNKID_DEF_PARENT: SimpleGameObjDef::Load (cload); break; case CHUNKID_DEF_VARIABLES: Load_Variables (cload); break; default: Debug_Say (("Unrecognized Beacon Def chunkID\n")); break; } cload.Close_Chunk (); } return true; } //////////////////////////////////////////////////////////////// // // Load_Variables // //////////////////////////////////////////////////////////////// void BeaconGameObjDef::Load_Variables (ChunkLoadClass &cload) { while (cload.Open_Micro_Chunk ()) { switch (cload.Cur_Micro_Chunk_ID ()) { READ_MICRO_CHUNK_WWSTRING (cload, MICROCHUNKID_DEF_ARMING_ANIM_NAME, ArmingAnimationName); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_BROADCAST_TIME, BroadcastToAllTime); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_ARM_TIME, ArmTime); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_DISARM_TIME, DisarmTime); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_PRE_DETONATE_CINEMATIC_DELAY, PreDetonateCinematicDelay); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_DETONATE_TIME, DetonateTime); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_POST_DETONATE_TIME, PostDetonateTime); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_ARMED_SOUNDID, ArmedSoundDefID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_DISARMING_TEXTID, DisarmingTextID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_DISARMED_TEXTID, DisarmedTextID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_ARMING_TEXTID, ArmingTextID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_ARM_INTERRUPT_TEXTID, ArmingInterruptedTextID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_DISARM_INTERRUPT_TEXTID, DisarmingInterruptedTextID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_PRE_CINEMATIC_DEFID, PreDetonateCinematicDefID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_POST_CINEMATIC_DEFID, PostDetonateCinematicDefID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_EXPLOSION_DEFID, ExplosionDefID); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_IS_NUKE, IsNuke); default: Debug_Say (("Unrecognized Beacon Def Variable chunkID\n")); break; } cload.Close_Micro_Chunk(); } return ; } //////////////////////////////////////////////////////////////// // // Get_Factory // //////////////////////////////////////////////////////////////// const PersistFactoryClass & BeaconGameObjDef::Get_Factory (void) const { return _BeaconGameObjDefPersistFactory; } //////////////////////////////////////////////////////////////// // // BeaconGameObj // //////////////////////////////////////////////////////////////// BeaconGameObj::BeaconGameObj (void) : StateTimer (0), PreDetonateTimer (0), DetonateTimer (0), WarningTimer(0.0f), State (0), IsArmed (false), WeaponDefinition (NULL), MessageSound (NULL), ArmedSound (NULL), OwnerBackup( NULL ) { Set_App_Packet_Type(APPPACKETTYPE_BEACON); return ; } //////////////////////////////////////////////////////////////// // // ~BeaconGameObj // //////////////////////////////////////////////////////////////// BeaconGameObj::~BeaconGameObj (void) { Stop_Current_Message_Sound (); Stop_Armed_Sound (); Stop_Owner_Animation (); CinematicObject = NULL; return ; } //////////////////////////////////////////////////////////////// // // Get_Factory // //////////////////////////////////////////////////////////////// const PersistFactoryClass & BeaconGameObj::Get_Factory (void) const { return _BeaconGameObjPersistFactory; } //////////////////////////////////////////////////////////////// // // Init // //////////////////////////////////////////////////////////////// void BeaconGameObj::Init( void ) { Init( Get_Definition() ); } //////////////////////////////////////////////////////////////// // // Init // //////////////////////////////////////////////////////////////// void BeaconGameObj::Init (const BeaconGameObjDef &definition) { SimpleGameObj::Init (definition); // // Make the object so its targettable and it collides with the terrain // Peek_Physical_Object()->Set_Collision_Group (TERRAIN_AND_BULLET_COLLISION_GROUP); return ; } //////////////////////////////////////////////////////////////// // // Get_Definition // //////////////////////////////////////////////////////////////// const BeaconGameObjDef & BeaconGameObj::Get_Definition (void) const { return (const BeaconGameObjDef &)BaseGameObj::Get_Definition (); } //////////////////////////////////////////////////////////////// // // Init_Beacon // //////////////////////////////////////////////////////////////// void BeaconGameObj::Init_Beacon ( const WeaponDefinitionClass * definition, SoldierGameObj * owner, const Vector3 & position ) { WeaponDefinition = definition; Owner = owner; Set_Position (position); if ( owner ) { OwnerBackup = owner->Get_Player_Data(); } // // Become part of the same team as the player who is // dropping us // if (Owner != NULL) { Set_Player_Type (Owner.Get_Ptr ()->As_SmartGameObj ()->Get_Player_Type ()); } return ; } //////////////////////////////////////////////////////////////// // // Save // //////////////////////////////////////////////////////////////// bool BeaconGameObj::Save (ChunkSaveClass &csave) { csave.Begin_Chunk (CHUNKID_PARENT); SimpleGameObj::Save (csave); csave.End_Chunk (); csave.Begin_Chunk (CHUNKID_VARIABLES); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_STATE, State); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_STATE_TIMER, StateTimer); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DETONATE_TIMER, DetonateTimer); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_PRE_DETONATE_TIMER, PreDetonateTimer); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_IS_ARMED, IsArmed); csave.End_Chunk (); // // Save the owner (if necessary) // if (Owner != NULL) { csave.Begin_Chunk (CHUNKID_OWNER); Owner.Save (csave); csave.End_Chunk (); } if (CinematicObject != NULL) { csave.Begin_Chunk (CHUNKID_CINEMATIC); CinematicObject.Save (csave); csave.End_Chunk (); } return true; } //////////////////////////////////////////////////////////////// // // Load // //////////////////////////////////////////////////////////////// bool BeaconGameObj::Load (ChunkLoadClass &cload) { while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case CHUNKID_PARENT: SimpleGameObj::Load (cload); break; case CHUNKID_VARIABLES: Load_Variables (cload); break; case CHUNKID_OWNER: Owner.Load (cload); break; case CHUNKID_CINEMATIC: CinematicObject.Load (cload); break; default: Debug_Say (("Unrecognized Beacon chunkID\n")); break; } cload.Close_Chunk(); } return true; } //////////////////////////////////////////////////////////////// // // Load_Variables // //////////////////////////////////////////////////////////////// void BeaconGameObj::Load_Variables (ChunkLoadClass &cload) { while (cload.Open_Micro_Chunk ()) { switch (cload.Cur_Micro_Chunk_ID ()) { READ_MICRO_CHUNK (cload, MICROCHUNKID_STATE, State); READ_MICRO_CHUNK (cload, MICROCHUNKID_STATE_TIMER, StateTimer); READ_MICRO_CHUNK (cload, MICROCHUNKID_DETONATE_TIMER, DetonateTimer); READ_MICRO_CHUNK (cload, MICROCHUNKID_PRE_DETONATE_TIMER, PreDetonateTimer); READ_MICRO_CHUNK (cload, MICROCHUNKID_IS_ARMED, IsArmed); default: Debug_Say (("Unrecognized Beacon Variable chunkID\n")); break; } cload.Close_Micro_Chunk(); } return ; } //////////////////////////////////////////////////////////////// // // Think // //////////////////////////////////////////////////////////////// void BeaconGameObj::Think (void) { WWPROFILE ("Beacon Think"); Restore_Owner(); // // Let the state think a little // Update_State (); // // Check to see if we need to break out of the // arming or disarming state // if ( CombatManager::I_Am_Server() && State == STATE_ARMING ) { // // Was the owner interrupted while performing his action? // if (Was_Owner_Interrupted ()) { Stop_Owner_Animation (); // // Let the player know that the arming operation was cancelled // Display_Message (Get_Definition ().ArmingInterruptedTextID); // // If the arming state was interrupted, then // return the ammo that created the beacon to its owner. // SoldierGameObj *soldier = Get_Owner (); if (soldier != NULL) { WeaponBagClass *weapon_bag = soldier->Get_Weapon_Bag (); weapon_bag->Add_Weapon (WeaponDefinition, 1, false); // // If the user is still holding the beacon weapon then stop its firing // sound. // WeaponClass *curr_weapon = weapon_bag->Get_Weapon (); if (curr_weapon != NULL && curr_weapon->Get_ID () == WeaponDefinition->Get_ID ()) { curr_weapon->Stop_Firing_Sound (); } } // // Now destroy ourselves // Set_Delete_Pending (); } } return ; } //////////////////////////////////////////////////////////////// // // Get_Information // //////////////////////////////////////////////////////////////// void BeaconGameObj::Get_Information (StringClass &string) { SimpleGameObj::Get_Information( string ); return ; } //////////////////////////////////////////////////////////////// // // Start_Cinematic // //////////////////////////////////////////////////////////////// void BeaconGameObj::Start_Cinematic ( int id ) { // // Create the cinematic controller // if ( id != 0 ) { PhysicalGameObj *game_obj = ObjectLibraryManager::Create_Object ( id ); if (game_obj != NULL) { game_obj->Start_Observers (); // // Position the cinematic controller in the world // Vector3 position; Get_Position (&position); game_obj->Set_Position (position); CinematicObject = game_obj; Debug_Say(( "Beacon Cinematic Started\n" )); } } return ; } //////////////////////////////////////////////////////////////// // // Stop_Armed_Sound // //////////////////////////////////////////////////////////////// void BeaconGameObj::Stop_Armed_Sound (void) { if (ArmedSound != NULL) { ArmedSound->Remove_From_Scene (); REF_PTR_RELEASE (ArmedSound); } return ; } //////////////////////////////////////////////////////////////// // // Set_State // //////////////////////////////////////////////////////////////// void BeaconGameObj::Set_State (int state) { if (State == state) { return ; } Restore_Owner(); if ( CombatManager::I_Am_Server() ) { // // "Dirty" the object for networking // Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); } bool is_nuke = !!Get_Definition().IsNuke; switch (state) { case STATE_NULL: Stop_Armed_Sound (); Display_Message (Get_Definition ().ArmingInterruptedTextID); break; case STATE_ARMING: StateTimer = Get_Definition ().ArmTime; Display_Message (Get_Definition ().ArmingTextID); Start_Owner_Animation (); break; case STATE_ARMED: if (IsArmed == false) { Stop_Owner_Animation (); // // Set the detonation timer // DetonateTimer = Get_Definition ().DetonateTime; WarningTimer = Get_Definition().BroadcastToAllTime; // // Create the "armed" sound // ArmedSound = WWAudioClass::Get_Instance ()->Create_Continuous_Sound (Get_Definition ().ArmedSoundDefID); if (ArmedSound != NULL) { // // Insert the sound object into the world // ArmedSound->Set_Transform (Get_Transform ()); ArmedSound->Add_To_Scene (); } // Only on Server if ( CombatManager::I_Am_Server() ) { // switch to pre detonate weather Debug_Say(( "Pre-Detonate weather override\n" )); if ( is_nuke ) { BackgroundMgrClass::Override_Sky_Tint ( 0.8f, DetonateTimer/2 ); WeatherMgrClass::Override_Wind (0, 3, 1, DetonateTimer/2 ); } else { BackgroundMgrClass::Override_Clouds(1.0f, 1.0f, DetonateTimer/2); BackgroundMgrClass::Override_Lightning( 0.8f, 0.2f, 0.8f, 0, 1.0f, DetonateTimer/2); WeatherMgrClass::Override_Precipitation (WeatherMgrClass::PRECIPITATION_RAIN, 2.0f, DetonateTimer/2); } } // Notify base controllers the beacon is armed BaseControllerClass* base = BaseControllerClass::Find_Base(Get_Player_Type()); if (base) { base->On_Beacon_Armed(this); } PreDetonateTimer = max( Get_Definition ().PreDetonateCinematicDelay, 0.001f ); IsArmed = true; } break; case STATE_DISARMED: { Display_Message (Get_Definition ().DisarmedTextID); Stop_Armed_Sound (); Stop_Owner_Animation (); // Only on Server if ( CombatManager::I_Am_Server() ) { // cancel weather override Debug_Say(( "Cancelling weather override\n" )); if ( is_nuke ) { BackgroundMgrClass::Restore_Sky_Tint ( 5 ); WeatherMgrClass::Restore_Wind( 5 ); } else { BackgroundMgrClass::Restore_Clouds( 5 ); BackgroundMgrClass::Restore_Lightning( 5 ); WeatherMgrClass::Restore_Precipitation( 5 ); } } // Notify base that the beacon is disarmed BaseControllerClass* base = BaseControllerClass::Find_Base(Get_Player_Type()); if (base) { base->On_Beacon_Disarmed(this); } // Stop cinematic if ( CinematicObject.Get_Ptr() != NULL ) { CinematicObject.Get_Ptr()->Set_Delete_Pending(); CinematicObject = NULL; } Set_Delete_Pending (); break; } case STATE_DETONATING: Stop_Armed_Sound (); Debug_Say(( "Detonate!\n" )); IsArmed = false; StateTimer = Get_Definition ().PostDetonateTime; // Only on Server if ( CombatManager::I_Am_Server() ) { // Create the cinematic controller Start_Cinematic( Get_Definition ().PostDetonateCinematicDefID ); // switch to post detonate weather Debug_Say(( "Post-Detonate weather override\n" )); if ( is_nuke ) { WeatherMgrClass::Override_Precipitation (WeatherMgrClass::PRECIPITATION_ASH, 0.3f); } /* // Create the explosion, if not in the enemy base if ( !Is_In_Enemy_Base() && Get_Definition ().ExplosionDefID != 0 ) { Create_Explosion (); } */ } // Create the explosion, if not in the enemy base // if ( !Is_In_Enemy_Base() && Get_Definition ().ExplosionDefID != 0 ) { if ( Get_Definition ().ExplosionDefID != 0 ) { Create_Explosion (); } // Hide our model if ( Peek_Model() ) { Peek_Model()->Set_Hidden( true ); } break; } State = state; return ; } //////////////////////////////////////////////////////////////// // // Update_State // //////////////////////////////////////////////////////////////// void BeaconGameObj::Update_State (void) { StateTimer -= TimeManager::Get_Frame_Seconds (); if (IsArmed) { // // Tweak the pitch of the armed sound // if (ArmedSound != NULL) { float percent = (1.0F - DetonateTimer / Get_Definition ().DetonateTime); ArmedSound->Set_Pitch_Factor (1.0F + (percent * 5.0F)); } // // Check to see if we've exploded // DetonateTimer -= TimeManager::Get_Frame_Seconds (); if (DetonateTimer <= 0) { Set_State (STATE_DETONATING); } if ( PreDetonateTimer != 0 ) { PreDetonateTimer -= TimeManager::Get_Frame_Seconds (); if ( PreDetonateTimer <= 0 ) { PreDetonateTimer = 0; // Only on Server if ( CombatManager::I_Am_Server() ) { Start_Cinematic( Get_Definition ().PreDetonateCinematicDefID ); } } } } // // Handle each state independently // switch (State) { case STATE_NULL: break; case STATE_ARMING: { // // Update the action bar in the HUD // float percent = (1.0F - StateTimer / Get_Definition ().ArmTime); HUDInfo::Set_Action_Status_Value (percent); // // Did the player successfully arm the beacon? // if (StateTimer <= 0) { Set_State (STATE_ARMED); } } break; case STATE_ARMED: if (WarningTimer != 0.0f) { WarningTimer -= TimeManager::Get_Frame_Seconds(); if (WarningTimer <= 0.0f) { WarningTimer = 0.0f; // Notify base controller beacon is disarmed BaseControllerClass* base = BaseControllerClass::Find_Base(Get_Player_Type()); if (base) { base->On_Beacon_Warning(this); } } } break; case STATE_DISARMED: break; case STATE_DETONATING: // Wait for post-detonate timer if (StateTimer <= 0) { // Only on Server if ( CombatManager::I_Am_Server() ) { if ( CombatManager::Does_Beacon_Placement_Ends_Game() && Is_In_Enemy_Base() ) { BaseControllerClass *base = Get_Enemy_Base (); if (base != NULL) { // // Destroy the enemy base // base->Destroy_Base (); base->Set_Beacon_Destroyed_Base(true); } } // cancel weather override Debug_Say(( "restore weather override\n" )); bool is_nuke = !!Get_Definition().IsNuke; if ( is_nuke ) { BackgroundMgrClass::Restore_Sky_Tint ( 5 ); WeatherMgrClass::Restore_Wind( 5 ); WeatherMgrClass::Restore_Precipitation( 5 ); } else { BackgroundMgrClass::Restore_Clouds( 5 ); BackgroundMgrClass::Restore_Lightning( 5 ); WeatherMgrClass::Restore_Precipitation( 5 ); } } Set_Delete_Pending(); } break; } return ; } //////////////////////////////////////////////////////////////// // // Get_Enemy_Base // //////////////////////////////////////////////////////////////// BaseControllerClass * BeaconGameObj::Get_Enemy_Base (void) { // // Lookup the team that this object is associated with // int player_type = PLAYERTYPE_GDI; if (Get_Player_Type () == PLAYERTYPE_GDI) { player_type = PLAYERTYPE_NOD; } return BaseControllerClass::Find_Base (player_type); } //////////////////////////////////////////////////////////////// // // Can_Place_Here // //////////////////////////////////////////////////////////////// bool BeaconGameObj::Can_Place_Here (const Vector3 &position) { // always return true; return true; } //////////////////////////////////////////////////////////////// // // Is_In_Enemy_Base (CnC mode only) // //////////////////////////////////////////////////////////////// bool BeaconGameObj::Is_In_Enemy_Base( void ) { bool retval = false; // // Lookup the enemy's base // BaseControllerClass *base = Get_Enemy_Base (); if (base != NULL) { // // Check to see if the point is inside the beacon's zone // Vector3 position; Get_Position (&position); const OBBoxClass &zone = base->Get_Beacon_Zone (); if (CollisionMath::Overlap_Test (zone, position) != CollisionMath::OUTSIDE) { retval = true; } } return retval; } //////////////////////////////////////////////////////////////// // // Stop_Current_Message_Sound // //////////////////////////////////////////////////////////////// void BeaconGameObj::Stop_Current_Message_Sound (void) { if (MessageSound != NULL) { MessageSound->Remove_From_Scene (); REF_PTR_RELEASE (MessageSound); } return ; } //////////////////////////////////////////////////////////////// // // Display_Message // //////////////////////////////////////////////////////////////// void BeaconGameObj::Display_Message (int text_id) { Stop_Current_Message_Sound (); // // Lookup the translation object from the strings database // TDBObjClass *translate_obj = TranslateDBClass::Find_Object (text_id); if (translate_obj != NULL) { const WCHAR *string = translate_obj->Get_String (); int sound_def_id = (int)translate_obj->Get_Sound_ID (); float duration = 2.0F; // // Play the sound effect // bool display_text = true; if (sound_def_id > 0) { // // Create the sound object // MessageSound = WWAudioClass::Get_Instance ()->Create_Sound (sound_def_id); if (MessageSound != NULL) { duration = (MessageSound->Get_Duration () / 1000.0F); // // Play the sound effect at the lcoation of the beacon // MessageSound->Set_Transform (Get_Transform ()); MessageSound->Add_To_Scene (); display_text = (MessageSound->Is_Sound_Culled () == false); } } // // Display the text on the screen // if (display_text && string != NULL) { float message_duration = max (duration, 5.0F); CombatManager::Get_Message_Window ()->Add_Message (string, Vector3 (1, 1, 1), NULL, message_duration); } } return ; } //////////////////////////////////////////////////////////////// // // Begin_Arming // //////////////////////////////////////////////////////////////// void BeaconGameObj::Begin_Arming (void) { // // Switch states // Set_State (STATE_ARMING); return ; } //////////////////////////////////////////////////////////////// // // Start_Owner_Animation // //////////////////////////////////////////////////////////////// void BeaconGameObj::Start_Owner_Animation (void) { Restore_Owner(); // // Release control of the human's animation (if possible) // SoldierGameObj *soldier = Get_Owner (); if (soldier != NULL) { // // Play the animation looped until we detect its time to stop // soldier->Set_Animation (Get_Definition ().ArmingAnimationName, true, 0); } // Only show the HUD if the owner if the star if ( soldier == COMBAT_STAR ) { HUDInfo::Display_Action_Status_Bar (true); } return ; } //////////////////////////////////////////////////////////////// // // Stop_Owner_Animation // //////////////////////////////////////////////////////////////// void BeaconGameObj::Stop_Owner_Animation (void) { Restore_Owner(); // // Release control of the human's animation (if possible) // SoldierGameObj *soldier = Get_Owner (); if (soldier != NULL) { // // Stop the animation // soldier->Set_Animation (NULL); } // Only show the HUD if the owner if the star if ( soldier == COMBAT_STAR ) { HUDInfo::Display_Action_Status_Bar (false); } return ; } //////////////////////////////////////////////////////////////// // // Was_Owner_Interrupted // //////////////////////////////////////////////////////////////// bool BeaconGameObj::Was_Owner_Interrupted (void) { Restore_Owner(); bool retval = false; // // The owner is interrupted if he's dead // SoldierGameObj *soldier = Get_Owner (); if (soldier == NULL || soldier->Is_Dead ()) { retval = true; } else { // // The owner is also interrupted if he moves // // // Check each movement control // ControlClass &control = soldier->Get_Control (); // Ignore up/down and turning // for (int index = 0; index < ControlClass::ANALOG_CONTROL_COUNT; index ++) { for (int index = 0; index < ControlClass::ANALOG_MOVE_LEFT+1; index ++) { // // Check this input to see if the soldier is moving // if (control.Get_Analog ((ControlClass::AnalogControl)index) != 0) { retval = true; break; } } // // Check each "boolean" control (jumping, etc) // if (retval == false) { for ( index = ControlClass::BOOLEAN_ONE_TIME_FIRST; index <= ControlClass::BOOLEAN_DROP_FLAG; index ++) { // // Check this input to see if the soldier is moving // if (control.Get_Boolean ((ControlClass::BooleanControl)index)) { retval = true; break; } } } } return retval; } //////////////////////////////////////////////////////////////// // // Get_Owner // //////////////////////////////////////////////////////////////// SoldierGameObj * BeaconGameObj::Get_Owner (void) { SoldierGameObj *soldier = NULL; // // Dig the soldier pointer out of the referencer object // if (Owner != NULL) { PhysicalGameObj *physical_obj = Owner.Get_Ptr ()->As_PhysicalGameObj (); if (physical_obj != NULL) { soldier = physical_obj->As_SoldierGameObj (); } } return soldier; } //////////////////////////////////////////////////////////////// // // Completely_Damaged // //////////////////////////////////////////////////////////////// void BeaconGameObj::Completely_Damaged (const OffenseObjectClass &damager) { if (!Is_Delete_Pending()) { Set_State (STATE_DISARMED); Set_Delete_Pending (); } } //////////////////////////////////////////////////////////////// // // Create_Explosion // //////////////////////////////////////////////////////////////// void BeaconGameObj::Create_Explosion (void) { if (CombatManager::I_Am_Server()) { Restore_Owner(); } Debug_Say (("Create Explosion\n")); // // Create the explosion... // Vector3 position; Get_Position (&position); // // (gth) don't explode if the owner is gone // if (Get_Owner() != NULL) { ExplosionManager::Create_Explosion_At (Get_Definition ().ExplosionDefID, Get_Transform (), Get_Owner ()); if (CombatManager::I_Am_Server()) { // // Look-up the explosion that this beacon will create // ExplosionDefinitionClass *explosion_def = (ExplosionDefinitionClass *)DefinitionMgrClass::Find_Definition (Get_Definition ().ExplosionDefID); if (explosion_def != NULL) { float outter_radius = explosion_def->DamageRadius; float outter_radius2 = outter_radius * outter_radius; // // Loop over all the buildings in the level // SLNode *obj_node = NULL; for ( obj_node = GameObjManager::Get_Building_Game_Obj_List()->Head (); obj_node != NULL; obj_node = obj_node->Next ()) { WWASSERT (obj_node->Data () != NULL); BuildingGameObj *building = obj_node->Data (); if (building != NULL) { // // Check to see if any part of the building is inside our damage sphere // float dist2 = 0; building->Find_Closest_Poly (position, &dist2); if (dist2 <= outter_radius2) { // // Determine the strength of the explosion at this location // float percent = (WWMath::Sqrt (dist2) / outter_radius); percent = 1.0F - WWMath::Clamp (percent, 0.0F, 1.0F); float strength = percent * explosion_def->DamageStrength; // // Apply the damage to this building // OffenseObjectClass offense (strength, explosion_def->DamageWarhead, Get_Owner ()); building->Apply_Damage_Building (offense, true); } } } } } } return ; } /* ** */ void BeaconGameObj::Export_Rare( BitStreamClass &packet ) { Restore_Owner(); SimpleGameObj::Export_Rare( packet ); packet.Add( State ); int owner_id = 0; if ( Get_Owner() ) { owner_id = Get_Owner()->Get_ID(); } packet.Add( owner_id ); } /* ** */ void BeaconGameObj::Import_Rare( BitStreamClass &packet ) { SimpleGameObj::Import_Rare( packet ); int state; packet.Get( state ); int owner_id; packet.Get( owner_id ); if ( owner_id != 0 ) { Owner = GameObjManager::Find_SmartGameObj( owner_id ); } Set_State( state ); return ; } void BeaconGameObj::Restore_Owner( void ) { if ( Get_Owner() == NULL && OwnerBackup != NULL ) { // Try and find a smart game obj with the same playerdata SLNode * smart_objnode; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { SmartGameObj * obj = smart_objnode->Data(); if ( obj->Get_Player_Data() == OwnerBackup ) { Owner = obj; Debug_Say(( "Found Beacon owner\n" )); } } if ( Get_Owner() == NULL ) { Debug_Say(( "Didn't find Beacon owner\n" )); } } }