/* ** 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/gameobjmanager.cpp $* * * * $Author:: Byon_g $* * * * $Modtime:: 12/21/01 3:43p $* * * * $Revision:: 76 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "gameobjmanager.h" #include "wwdebug.h" #include "smartgameobj.h" #include "combatchunkid.h" #include "saveloadsubsystem.h" #include "persistfactory.h" #include "debug.h" #include "combat.h" #include "scripts.h" #include "soldier.h" #include "building.h" #include "wwphysids.h" #include "scriptzone.h" #include "wwprofile.h" #include "networkobjectmgr.h" #include "networkobjectmgr.h" #include "vehicle.h" #include "persistentgameobjobserver.h" #include "weapons.h" /* ** Create an instance of the game object manager list. Since all ** memeber functions are static, no one needs to see it */ SList GameObjManager::GameObjList; SList GameObjManager::SmartGameObjList; // list of all render game objs SList GameObjManager::StarGameObjList; SList GameObjManager::BuildingGameObjList; bool GameObjManager::CinematicFreezeActive; /* ** */ void GameObjManager::Init(void) { Destroy_All(); CinematicFreezeActive = false; } void GameObjManager::Shutdown(void) { Destroy_All(); PersistentGameObjObserverManager::Reset(); } /* ** */ enum { CHUNKID_OBJECTS = 916991653, CHUNKID_VARIABLES, MICROCHUNKID_GENERATED_ID = 1, MICROCHUNKID_GLOBAL_SIGHT_RANGE_SCALE, MICROCHUNKID_CINEMATIC_FREEZE, }; bool GameObjManager::Save( ChunkSaveClass &csave ) { csave.Begin_Chunk( CHUNKID_OBJECTS ); // Allow each object in the master list to save SLNode *objnode; for ( objnode = GameObjManager::Get_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) { BaseGameObj * obj = objnode->Data(); csave.Begin_Chunk( obj->Get_Factory().Chunk_ID() ); obj->Get_Factory().Save( csave, obj ); csave.End_Chunk(); } csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_VARIABLES ); int next_id = NetworkObjectMgrClass::Get_Current_Dynamic_ID (); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_GENERATED_ID, next_id ); float scale = SmartGameObj::Get_Global_Sight_Range_Scale(); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_GLOBAL_SIGHT_RANGE_SCALE, scale ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CINEMATIC_FREEZE, CinematicFreezeActive ); csave.End_Chunk(); return true; } bool GameObjManager::Load( ChunkLoadClass &cload ) { float sight_scale = 1.0f; while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_OBJECTS: while (cload.Open_Chunk()) { PersistFactoryClass * factory = SaveLoadSystemClass::Find_Persist_Factory( cload.Cur_Chunk_ID() ); if ( factory ) { factory->Load( cload ); } cload.Close_Chunk(); } break; case CHUNKID_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_GLOBAL_SIGHT_RANGE_SCALE, sight_scale ); READ_MICRO_CHUNK( cload, MICROCHUNKID_CINEMATIC_FREEZE, CinematicFreezeActive ); case MICROCHUNKID_GENERATED_ID: { /*TSS091001*/ int next_id = NETID_DYNAMIC_OBJECT_MIN; LOAD_MICRO_CHUNK( cload, next_id ) NetworkObjectMgrClass::Set_New_Dynamic_ID( next_id ); Debug_Say(( "NetworkObjectMgrClass::Set_New_Dynamic_ID to %d\n", next_id )); /**/ break; } default: Debug_Say(( "Unrecognized GameObjManager Variable chunkID %d\n", cload.Cur_Micro_Chunk_ID() )); break; } cload.Close_Micro_Chunk(); } break; default: Debug_Say(( "Unrecognized GameObjManager chunkID %d\n", cload.Cur_Chunk_ID() )); break; } cload.Close_Chunk(); } SmartGameObj::Set_Global_Sight_Range_Scale( sight_scale ); return true; } void GameObjManager::Add( BaseGameObj *obj ) { // Make sure we have no duplicate IDs PhysicalGameObj *pobj = obj->As_PhysicalGameObj(); if ( pobj ) { WWASSERT( Find_PhysicalGameObj(pobj->Get_ID()) == NULL ); } // Cinematic scripts wanted objects not to progress on the frame they were created, // and wanted the ability to set a frame number without haveing it bumped. // So, make new things at the head of the list, so the oldest thinks last. // GameObjList.Add_Tail( obj ); GameObjList.Add_Head( obj ); } void GameObjManager::Init_All() { #if 0 SLNode *objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { objnode->Data()->Init(); } #endif } /* ** GameObjectManager::Destroy_All ** This static routine destroys all objects */ void GameObjManager::Destroy_All() // Destroy each object in the list { ScriptManager::Enable_Script_Creation( false ); // Disable new scripts SLNode *objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { objnode->Data()->Set_Delete_Pending(); } #pragma message ("Disabling Think and Post_Think in GameObjManager::Destroy_All()") #if 0 Think(); Post_Think(); #endif NetworkObjectMgrClass::Delete_Pending (); // we do it twice in case any new objects got created for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { objnode->Data()->Set_Delete_Pending(); } #if 0 Think(); Post_Think(); #endif NetworkObjectMgrClass::Delete_Pending (); WWASSERT( GameObjList.Head() == NULL ); WWASSERT( SmartGameObjList.Head() == NULL ); WWASSERT( StarGameObjList.Head() == NULL ); WWASSERT( BuildingGameObjList.Head() == NULL ); ScriptManager::Enable_Script_Creation( true ); // turn it back on } /* ** GameObjectManager::Update_Control() ** This routine allows each SmartGameObject to generate input controls */ int GameObjManager::Generate_Control() { SLNode *objnode; for ( objnode = SmartGameObjList.Head(); objnode; objnode = objnode->Next()) { // Don't genreate_control when cinematic frozen if ( Is_Cinematic_Freeze_Active() && objnode->Data()->Is_Cinematic_Freeze_Enabled() ) { continue; } if ( !objnode->Data()->Is_Hibernating() ) { objnode->Data()->Generate_Control(); } } return 0; } int _AwakeSoldiers = 0; int _HibernatingSoldiers = 0; /* ** This static routine allows each GameObject to think */ int GameObjManager::Think() { // Allow each object in the master list to think SLNode *objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { // Don't think when cinematic frozen if ( Is_Cinematic_Freeze_Active() && objnode->Data()->Is_Cinematic_Freeze_Enabled() ) { // Stop the physics motion if ( objnode->Data()->As_SmartGameObj() != NULL ) { objnode->Data()->As_SmartGameObj()->Reset_Controller(); // And the weapon (flames, etc) if ( objnode->Data()->As_SmartGameObj()->Get_Weapon() != NULL ) { objnode->Data()->As_SmartGameObj()->Get_Weapon()->Deselect(); } } continue; } if ( !objnode->Data()->Is_Hibernating() ) { objnode->Data()->Think(); } if ( objnode->Data()->As_SmartGameObj() ) { if ( objnode->Data()->As_SmartGameObj()->As_SoldierGameObj() ) { if ( objnode->Data()->Is_Hibernating() ) { _HibernatingSoldiers++; } else { _AwakeSoldiers++; } } } } return 0; } /* ** GameObjectManager::PostThink() ** This static routine allows each GameObject to think after the rest */ int GameObjManager::Post_Think() { // Allow each object in the master list to think SLNode *objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { // Don't post_think when cinematic frozen if ( Is_Cinematic_Freeze_Active() && objnode->Data()->Is_Cinematic_Freeze_Enabled() ) { continue; } if ( !objnode->Data()->Is_Hibernating() && objnode->Data()->Is_Post_Think_Allowed() ) { objnode->Data()->Post_Think(); } } //Destroy_Pending(); GameObjObserverManager::Delete_Pending(); ScriptManager::Destroy_Pending(); return 0; } /* ** searches the commando with this client id */ SoldierGameObj * GameObjManager::Find_Soldier_Of_Client_ID(int client_id) { { WWPROFILE( "FSOC id" ); for ( SLNode * objnode = Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) { SoldierGameObj * p_soldier = objnode->Data()->As_SoldierGameObj(); if (p_soldier != NULL && !p_soldier->Is_Delete_Pending() && p_soldier->Get_Control_Owner() == client_id) { return p_soldier; } } } return NULL; } /* ** searches for a player commando other than my own */ SoldierGameObj * GameObjManager::Find_Different_Player_Soldier(int my_id) { for ( SLNode * objnode = Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) { SoldierGameObj * p_soldier = objnode->Data()->As_SoldierGameObj(); if (p_soldier != NULL && !p_soldier->Is_Delete_Pending() && p_soldier->Is_Human_Controlled() && p_soldier->Get_Control_Owner() != my_id) { return p_soldier; } } return NULL; } SoldierGameObj * GameObjManager::Find_Soldier_Of_Player_Type(int player_type) { for ( SLNode * objnode = Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) { SoldierGameObj * p_soldier = objnode->Data()->As_SoldierGameObj(); if (p_soldier != NULL && !p_soldier->Is_Delete_Pending() && p_soldier->Get_Player_Type() == player_type) { return p_soldier; } } return NULL; } /* ** searches the game object list for the object with this id */ PhysicalGameObj * GameObjManager::Find_PhysicalGameObj( int id ) { SLNode * objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { PhysicalGameObj *obj = objnode->Data()->As_PhysicalGameObj(); if ( obj && (obj->Get_ID() == id) ) { return obj; // found it } } return NULL; // Not found } /* ** searches the game object list for the object with this id */ ScriptableGameObj * GameObjManager::Find_ScriptableGameObj( int id ) { SLNode * objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { ScriptableGameObj *obj = objnode->Data()->As_ScriptableGameObj(); if ( obj && (obj->Get_ID() == id) ) { return obj; // found it } } return NULL; // Not found } /* ** searches the game object list for a vehicle occupied by the given soldier. */ VehicleGameObj * GameObjManager::Find_Vehicle_Occupied_By( SoldierGameObj * p_soldier ) { WWASSERT(p_soldier != NULL); SLNode * objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { VehicleGameObj *obj = objnode->Data()->As_VehicleGameObj(); //if ( obj && (obj->Get_Driver() == p_soldier) ) { if ( obj && obj->Contains_Occupant( p_soldier) ) { return obj; // found it } } return NULL; // Not found } /* ** searches the smart game object list for the object with this id */ SmartGameObj * GameObjManager::Find_SmartGameObj( int id ) { SLNode * objnode; for ( objnode = SmartGameObjList.Head(); objnode; objnode = objnode->Next()) { SmartGameObj *obj = objnode->Data(); if ( obj->Is_Delete_Pending() ) continue; // Perhaps not find things that will be dieing? if ( obj->Get_ID() == id ) { return obj; // found it } } return NULL; // Not found } /* ** Buildings */ void GameObjManager::Init_Buildings( void ) { /* ** Ask each building to build its list of aggregates, meshes, and lights */ SLNode *objnode = NULL; for ( objnode = BuildingGameObjList.Head(); objnode; objnode = objnode->Next() ) { BuildingGameObj *obj = objnode->Data()->As_BuildingGameObj(); if (obj != NULL) { obj->Collect_Building_Components(); } } return ; } /* ** Update_Building_Collection_Spheres */ void GameObjManager::Update_Building_Collection_Spheres( void ) { SLNode *objnode = NULL; for ( objnode = BuildingGameObjList.Head(); objnode; objnode = objnode->Next()) { BuildingGameObj *obj = objnode->Data()->As_BuildingGameObj(); if (obj != NULL) { // // Get some information about the current building // const char *prefix = obj->Get_Name_Prefix (); float max_radius = 50.0F; Vector3 position; obj->Get_Position (&position); // // Test this building with all other buildings that have the same prefix // SLNode *test_node = NULL; for ( test_node = BuildingGameObjList.Head(); test_node; test_node = test_node->Next()) { BuildingGameObj *test_obj = test_node->Data()->As_BuildingGameObj(); // // Is this a building with the same prefix? // if ( test_obj != NULL && test_obj != obj && ::stricmp (test_obj->Get_Name_Prefix (), prefix) == 0) { // // Get the test object's position // Vector3 test_position; test_obj->Get_Position (&test_position); // // Minimize the collection radius for this building (if necessary) // float distance = (position - test_position).Length (); max_radius = min (distance, max_radius); } } // // Set this object's collection radius // obj->CollectionSphere.Radius = max_radius; } } return ; } /* ** Debug_Set_All_Building_States */ void GameObjManager::Debug_Set_All_Building_States(float health_percentage,bool power_on) { SLNode * objnode; for ( objnode = BuildingGameObjList.Head(); objnode; objnode = objnode->Next()) { BuildingGameObj *building = objnode->Data()->As_BuildingGameObj(); building->Enable_Power(power_on); building->Set_Normalized_Health(health_percentage / 100.0f); } } /* ** Environment Zones */ bool GameObjManager::Is_In_Environment_Zone( Vector3 & pos ) { WWPROFILE( "Is_In_Environment_Zone" ); // Allow each object in the master list to think SLNode *objnode; for ( objnode = GameObjList.Head(); objnode; objnode = objnode->Next()) { ScriptableGameObj * scriptable = objnode->Data()->As_ScriptableGameObj(); if ( scriptable ) { ScriptZoneGameObj * script_zone = scriptable->As_ScriptZoneGameObj(); if ( script_zone && script_zone->Is_Environment_Zone() ) { if ( CollisionMath::Overlap_Test( script_zone->Get_Bounding_Box(), pos ) == CollisionMath::INSIDE ) { return true; } } } } return false; } /* ** GameObjectManager::Destroy_Pending ** This static routine destroys all objects with destroy bit set */ /*void GameObjManager::Destroy_Pending() { // Remove the wishing to be dead ones SLNode *objnode; for ( objnode = GameObjList.Head(); objnode; ) { WWASSERT(objnode != NULL); BaseGameObj *obj = objnode->Data(); objnode = objnode->Next(); WWASSERT(obj != NULL); if ( obj->Is_Destroy() ) { // If I'm a server, notify all others to destroy this object if this is a physical game obj! if ( CombatManager::I_Am_Server() ) { CombatManager::Object_Destroyed(obj); } delete obj; } } }*/