/* ** 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/staticnetworkobject.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 1/15/02 1:02p $* * * * $Revision:: 15 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "staticnetworkobject.h" #include "staticanimphys.h" #include "doors.h" #include "elevator.h" #include "bitpackids.h" #include "combat.h" #include "damageablestaticphys.h" #include "explosion.h" #include "vistable.h" #include "networkobjectmgr.h" #include "apppackettypes.h" //////////////////////////////////////////////////////////////// // Forward declarations //////////////////////////////////////////////////////////////// DynamicVectorClass StaticNetworkObjectClass::StaticNetworkObjectList; //////////////////////////////////////////////////////////////// // // StaticNetworkObjectClass // //////////////////////////////////////////////////////////////// StaticNetworkObjectClass::StaticNetworkObjectClass (void) : PhysObj (NULL), AnimationMode (0), LoopStart (0), LoopEnd (0), CurrFrame (0), TargetFrame (0) { // // Add ourselves to the master list // StaticNetworkObjectList.Add (this); // // Make sure we don't try to create the object on the client // //TSS2001d Clear_Object_Dirty_Bits (); Set_Object_Dirty_Bit (BIT_RARE, true); Set_App_Packet_Type(APPPACKETTYPE_STATIC); } //////////////////////////////////////////////////////////////// // // ~StaticNetworkObjectClass // //////////////////////////////////////////////////////////////// StaticNetworkObjectClass::~StaticNetworkObjectClass (void) { REF_PTR_RELEASE (PhysObj); // // Remove ourselves from the master list // int index = StaticNetworkObjectList.ID (this); if (index != -1) { StaticNetworkObjectList.Delete (index); } return ; } //////////////////////////////////////////////////////////////// // // Get_World_Position // //////////////////////////////////////////////////////////////// bool StaticNetworkObjectClass::Get_World_Position (Vector3 &pos) const { bool retval = false; // // Return the physic object's position // if (PhysObj != NULL) { PhysObj->Get_Position (&pos); retval = true; } return retval; } //////////////////////////////////////////////////////////////// // // Get_Vis_ID - return the vis ID for this object // //////////////////////////////////////////////////////////////// int StaticNetworkObjectClass::Get_Vis_ID (void) { if (PhysObj != NULL) { return PhysObj->Get_Vis_Object_ID (); } return -1; } //////////////////////////////////////////////////////////////// // // Initialize // //////////////////////////////////////////////////////////////// void StaticNetworkObjectClass::Initialize (StaticAnimPhysClass *phys_obj) { REF_PTR_SET (PhysObj, phys_obj); // // Update the network ID // int network_id = 0; if (PhysObj != NULL) { network_id = PhysObj->Get_ID (); // // Copy the object's state // AnimCollisionManagerClass &anim_mgr = PhysObj->Get_Animation_Manager (); AnimationMode = anim_mgr.Get_Animation_Mode (); CurrFrame = anim_mgr.Get_Current_Frame (); TargetFrame = anim_mgr.Get_Target_Frame (); LoopStart = anim_mgr.Get_Loop_Start (); LoopEnd = anim_mgr.Get_Loop_End (); } //WWASSERT(network_id >= NETID_STATIC_OBJECT_MIN && network_id <= NETID_STATIC_OBJECT_MAX); Set_Network_ID (network_id); return ; } //////////////////////////////////////////////////////////////// // // Network_Think // //////////////////////////////////////////////////////////////// void StaticNetworkObjectClass::Network_Think (void) { NetworkObjectClass::Network_Think (); if (PhysObj != NULL) { AnimCollisionManagerClass &anim_mgr = PhysObj->Get_Animation_Manager (); // // Set the dirty bit if the state has changed // if ( AnimationMode != anim_mgr.Get_Animation_Mode () || TargetFrame != anim_mgr.Get_Target_Frame () || LoopStart != anim_mgr.Get_Loop_Start () || LoopEnd != anim_mgr.Get_Loop_End ()) { AnimationMode = anim_mgr.Get_Animation_Mode (); CurrFrame = anim_mgr.Get_Current_Frame (); TargetFrame = anim_mgr.Get_Target_Frame (); LoopStart = anim_mgr.Get_Loop_Start (); LoopEnd = anim_mgr.Get_Loop_End (); Set_Object_Dirty_Bit (BIT_RARE, true); } } return ; } /* //////////////////////////////////////////////////////////////////////////// // // Compute_Object_Priority - TSS092201 // ///////////////////////////////////////////////////////////////////////////// float StaticNetworkObjectClass::Compute_Object_Priority (int client_id, const Vector3 &client_pos) { float priority = 1.0F; if (Get_Object_Dirty_Bit (client_id, BIT_RARE) == false) { priority = NetworkObjectClass::Compute_Object_Priority (client_id, client_pos); } // // Priority value is cached, update the cached value. // Set_Cached_Priority(priority); return priority; } */ //////////////////////////////////////////////////////////////// // // Generate_Static_Network_Objects // //////////////////////////////////////////////////////////////// void StaticNetworkObjectClass::Generate_Static_Network_Objects (void) { RefPhysListIterator iterator = COMBAT_SCENE->Get_Static_Anim_Object_Iterator (); // // Loop over all the static anim objects in the world // for (iterator.First (); iterator.Is_Done () == false; iterator.Next ()) { StaticAnimPhysClass *phys_obj = (StaticAnimPhysClass *)iterator.Peek_Obj (); if (phys_obj != NULL) { // // Make a new network object for this phys obj // StaticNetworkObjectClass *network_obj = NULL; if (phys_obj->As_ElevatorPhysClass () != NULL) { network_obj = new ElevatorNetworkObjectClass; } else if (phys_obj->As_DoorPhysClass () != NULL) { network_obj = new DoorNetworkObjectClass; } else if (phys_obj->As_DamageableStaticPhysClass () != NULL) { network_obj = new DSAPONetworkObjectClass; } else { network_obj = new StaticNetworkObjectClass; } // // Initialize the network object // network_obj->Initialize (phys_obj); } } return ; } //////////////////////////////////////////////////////////////// // // Free_Static_Network_Objects // //////////////////////////////////////////////////////////////// void StaticNetworkObjectClass::Free_Static_Network_Objects (void) { DynamicVectorClass temp_list = StaticNetworkObjectList; // // Loop over and free all the entries // for (int index = temp_list.Count () - 1; index >= 0; index --) { StaticNetworkObjectClass *object = temp_list[index]; if (object != NULL) { delete object; } } // // Make sure the list has been completely cleared // WWASSERT (StaticNetworkObjectList.Count () == 0); return ; } //////////////////////////////////////////////////////////////// // // Import_Rare // //////////////////////////////////////////////////////////////// void StaticNetworkObjectClass::Import_Rare (BitStreamClass &packet) { NetworkObjectClass::Import_Rare (packet); // // Read the data // packet.Get (AnimationMode); packet.Get (LoopStart); packet.Get (LoopEnd); packet.Get (CurrFrame); packet.Get (TargetFrame); if (PhysObj != NULL) { AnimCollisionManagerClass &anim_mgr = PhysObj->Get_Animation_Manager (); // // Set the data // anim_mgr.Set_Target_Frame (TargetFrame); anim_mgr.Set_Current_Frame (CurrFrame); anim_mgr.Set_Loop_Start (LoopStart); anim_mgr.Set_Loop_End (LoopEnd); anim_mgr.Set_Animation_Mode ((AnimCollisionManagerClass::AnimModeType)AnimationMode); } return ; } //////////////////////////////////////////////////////////////// // // Export_Rare // //////////////////////////////////////////////////////////////// void StaticNetworkObjectClass::Export_Rare (BitStreamClass &packet) { NetworkObjectClass::Export_Rare (packet); // // Export information about the animation // packet.Add (AnimationMode); packet.Add (LoopStart); packet.Add (LoopEnd); packet.Add (CurrFrame); packet.Add (TargetFrame); return ; } //////////////////////////////////////////////////////////////// // // DoorNetworkObjectClass // //////////////////////////////////////////////////////////////// DoorNetworkObjectClass::DoorNetworkObjectClass (void) : DoorState (0) { Set_App_Packet_Type(APPPACKETTYPE_DOOR); } //////////////////////////////////////////////////////////////// // // Initialize // //////////////////////////////////////////////////////////////// void DoorNetworkObjectClass::Initialize (StaticAnimPhysClass *phys_obj) { StaticNetworkObjectClass::Initialize (phys_obj); // // Get a pointer to the door // if (PhysObj != NULL) { DoorPhysClass *door = PhysObj->As_DoorPhysClass (); if (door != NULL) { // // Copy the object's state // DoorState = door->State; Set_Object_Dirty_Bit (BIT_RARE, true); } } return ; } //////////////////////////////////////////////////////////////// // // Network_Think // //////////////////////////////////////////////////////////////// void DoorNetworkObjectClass::Network_Think (void) { NetworkObjectClass::Network_Think (); // // Get a pointer to the door // if (PhysObj != NULL) { DoorPhysClass *door = PhysObj->As_DoorPhysClass (); if (door != NULL) { // // Set the dirty bit if the state has changed // if (DoorState != door->State) { DoorState = door->State; if (DoorState == STATE_OPENING_DOOR || DoorState == STATE_CLOSING_DOOR || DoorState == STATE_ACCESS_DENIED) { Set_Object_Dirty_Bit (BIT_RARE, true); } } } } return ; } //////////////////////////////////////////////////////////////// // // Import_Rare // //////////////////////////////////////////////////////////////// void DoorNetworkObjectClass::Import_Rare (BitStreamClass &packet) { // // Note: As a network optimization, we don't call the base // class. Doors are now state based and don't need to // transmit frame numbers. // // // Read the new state // packet.Get (DoorState, BITPACK_DOOR_STATE); // // Get a pointer to the door // if (PhysObj != NULL) { DoorPhysClass *door = PhysObj->As_DoorPhysClass (); if (door != NULL) { // // Set the new state // bool success = door->Set_State (DoorState); if (!success) { WWDEBUG_SAY(("DoorNetworkObjectClass::Import_Rare -- Door state changed failed to apply\n")); } } } return ; } //////////////////////////////////////////////////////////////// // // Export_Rare // //////////////////////////////////////////////////////////////// void DoorNetworkObjectClass::Export_Rare (BitStreamClass &packet) { // // Note: As a network optimization, we don't call the base // class. Doors are now state based and don't need to // transmit frame numbers. // // // Send the state // packet.Add (DoorState, BITPACK_DOOR_STATE); return ; } //////////////////////////////////////////////////////////////// // // Get_Description // //////////////////////////////////////////////////////////////// void DoorNetworkObjectClass::Get_Description (StringClass & description) { StringClass line; StringClass state_string; switch (DoorState) { case STATE_CLOSED_DOOR: state_string = "STATE_CLOSED_DOOR"; break; case STATE_OPENED_DOOR: state_string = "STATE_OPENED_DOOR"; break; case STATE_OPENING_DOOR: state_string = "STATE_OPENING_DOOR"; break; case STATE_CLOSING_DOOR: state_string = "STATE_CLOSING_DOOR"; break; case STATE_ACCESS_DENIED: state_string = "STATE_ACCESS_DENIED"; break; default: DIE; } line.Format("DoorState: %d (%s)\n", DoorState, state_string); description += line; if (PhysObj != NULL) { DoorPhysClass *door = PhysObj->As_DoorPhysClass (); if (door != NULL) { line.Format("Timer: %-5.2f\n", door->Timer); description += line; line.Format("CheckTimer: %-5.2f\n", door->CheckTimer); description += line; line.Format("State: %d\n", door->State); description += line; line.Format("ORP: %d\n", door->OpenRequestPending); description += line; line.Format("LockState: %d\n", door->LockState); description += line; } } } //////////////////////////////////////////////////////////////// // // ElevatorNetworkObjectClass // //////////////////////////////////////////////////////////////// ElevatorNetworkObjectClass::ElevatorNetworkObjectClass (void) : State (0), DoorStateTop (0), DoorStateBottom (0) { Set_App_Packet_Type(APPPACKETTYPE_ELEVATOR); } //////////////////////////////////////////////////////////////// // // Initialize // //////////////////////////////////////////////////////////////// void ElevatorNetworkObjectClass::Initialize (StaticAnimPhysClass *phys_obj) { //bug NetworkObjectClass::Network_Think (); StaticNetworkObjectClass::Initialize (phys_obj); // // Get a pointer to the elevator // if (PhysObj != NULL) { ElevatorPhysClass *elevator = PhysObj->As_ElevatorPhysClass (); if (elevator != NULL) { // // Copy the object's state // State = elevator->State; DoorStateTop = elevator->DoorStates[ElevatorPhysClass::TOP]; DoorStateBottom = elevator->DoorStates[ElevatorPhysClass::BOTTOM]; } } return ; } //////////////////////////////////////////////////////////////// // // Network_Think // //////////////////////////////////////////////////////////////// void ElevatorNetworkObjectClass::Network_Think (void) { NetworkObjectClass::Network_Think (); //StaticNetworkObjectClass::Network_Think (); // // Get a pointer to the elevator // if (PhysObj != NULL) { ElevatorPhysClass *elevator = PhysObj->As_ElevatorPhysClass (); if (elevator != NULL) { // // Set the dirty bit if the state has changed // if ( State != elevator->State || DoorStateTop != elevator->DoorStates[ElevatorPhysClass::TOP] || DoorStateBottom != elevator->DoorStates[ElevatorPhysClass::BOTTOM]) { State = elevator->State; DoorStateTop = elevator->DoorStates[ElevatorPhysClass::TOP]; DoorStateBottom = elevator->DoorStates[ElevatorPhysClass::BOTTOM]; Set_Object_Dirty_Bit (BIT_RARE, true); } } } return ; } //////////////////////////////////////////////////////////////// // // Import_Rare // //////////////////////////////////////////////////////////////// void ElevatorNetworkObjectClass::Import_Rare (BitStreamClass &packet) { // // Note: As a network optimization, we don't call the base // class. Doors are now state based and don't need to // transmit frame numbers. // //StaticNetworkObjectClass::Import_Rare (packet); // // Read the new state data // packet.Get (State); packet.Get (DoorStateTop); packet.Get (DoorStateBottom); // // Get a pointer to the elevator // if (PhysObj != NULL) { ElevatorPhysClass *elevator = PhysObj->As_ElevatorPhysClass (); if (elevator != NULL) { // // Switch to the new states // elevator->Set_State (State); elevator->Set_Door_State (ElevatorPhysClass::TOP, DoorStateTop); elevator->Set_Door_State (ElevatorPhysClass::BOTTOM, DoorStateBottom); } } return ; } //////////////////////////////////////////////////////////////// // // Export_Rare // //////////////////////////////////////////////////////////////// void ElevatorNetworkObjectClass::Export_Rare (BitStreamClass &packet) { // // Note: As a network optimization, we don't call the base // class. Doors are now state based and don't need to // transmit frame numbers. // //StaticNetworkObjectClass::Export_Rare (packet); // // Transmit the state information // packet.Add (State); packet.Add (DoorStateTop); packet.Add (DoorStateBottom); return ; } //////////////////////////////////////////////////////////////// // // Get_Description // //////////////////////////////////////////////////////////////// void ElevatorNetworkObjectClass::Get_Description (StringClass & description) { StringClass line; StringClass state_string; // // State // switch (State) { case ElevatorPhysClass::STATE_DOWN: state_string = "STATE_DOWN"; break; case ElevatorPhysClass::STATE_MOVING_UP: state_string = "STATE_MOVING_UP"; break; case ElevatorPhysClass::STATE_UP: state_string = "STATE_UP"; break; case ElevatorPhysClass::STATE_MOVING_DOWN: state_string = "STATE_MOVING_DOWN"; break; default: DIE; } line.Format("State: %d (%s)\n", State, state_string); description += line; // // DoorStateTop // switch (DoorStateTop) { case ElevatorPhysClass::DOOR_STATE_NORMAL: state_string = "DOOR_STATE_NORMAL"; break; case ElevatorPhysClass::DOOR_STATE_UNLOCKED: state_string = "DOOR_STATE_UNLOCKED"; break; case ElevatorPhysClass::DOOR_STATE_ACCESS_DENIED: state_string = "DOOR_STATE_ACCESS_DENIED"; break; default: DIE; } line.Format("DoorStateTop: %d (%s)\n", DoorStateTop, state_string); description += line; // // DoorStateBottom // switch (DoorStateBottom) { case ElevatorPhysClass::DOOR_STATE_NORMAL: state_string = "DOOR_STATE_NORMAL"; break; case ElevatorPhysClass::DOOR_STATE_UNLOCKED: state_string = "DOOR_STATE_UNLOCKED"; break; case ElevatorPhysClass::DOOR_STATE_ACCESS_DENIED: state_string = "DOOR_STATE_ACCESS_DENIED"; break; default: DIE; } line.Format("DoorStateBottom: %d (%s)\n", DoorStateBottom, state_string); description += line; if (PhysObj != NULL) { ElevatorPhysClass *elevator = PhysObj->As_ElevatorPhysClass (); if (elevator != NULL) { line.Format("State: %d\n", elevator->State); description += line; line.Format("DoorStates[0]: %d\n", elevator->DoorStates[0]); description += line; line.Format("DoorStates[1]: %d\n", elevator->DoorStates[1]); description += line; line.Format("CheckTimer: %-5.2f\n", elevator->CheckTimer); description += line; line.Format("PrevFrame: %-5.2f\n", elevator->PrevFrame); description += line; line.Format("IsCallTimerSet: %d\n", elevator->IsCallTimerSet); description += line; line.Format("CallTimer: %-5.2f\n", elevator->CallTimer); description += line; line.Format("TriggerRequest: %d\n", elevator->TriggerRequest); description += line; } } } //////////////////////////////////////////////////////////////// // // DSAPONetworkObjectClass // //////////////////////////////////////////////////////////////// DSAPONetworkObjectClass::DSAPONetworkObjectClass (void) : Health (0) { Set_App_Packet_Type(APPPACKETTYPE_DSAPO); } //////////////////////////////////////////////////////////////// // // Initialize // //////////////////////////////////////////////////////////////// void DSAPONetworkObjectClass::Initialize (StaticAnimPhysClass *phys_obj) { StaticNetworkObjectClass::Initialize (phys_obj); // // Get a pointer to the object // if (PhysObj != NULL) { DamageableStaticPhysClass *phys_obj = PhysObj->As_DamageableStaticPhysClass (); if (phys_obj != NULL) { // // Copy the object's state // Health = phys_obj->DefenseObject.Get_Health (); } } return ; } //////////////////////////////////////////////////////////////// // // Network_Think // //////////////////////////////////////////////////////////////// void DSAPONetworkObjectClass::Network_Think (void) { StaticNetworkObjectClass::Network_Think (); // // Get a pointer to the object // if (PhysObj != NULL) { DamageableStaticPhysClass *phys_obj = PhysObj->As_DamageableStaticPhysClass (); if (phys_obj != NULL) { // // Set the dirty bit if the state has changed // if (Health > 0 && phys_obj->DefenseObject.Get_Health () <= 0) { Health = phys_obj->DefenseObject.Get_Health (); Set_Object_Dirty_Bit (BIT_RARE, true); } } } return ; } //////////////////////////////////////////////////////////////// // // Import_Rare // //////////////////////////////////////////////////////////////// void DSAPONetworkObjectClass::Import_Rare (BitStreamClass &packet) { DamageableStaticPhysClass *phys_obj = PhysObj->As_DamageableStaticPhysClass (); if (phys_obj == NULL) { return ; } // // Get the health BEFORE we import the data // float old_health = phys_obj->DefenseObject.Get_Health(); // // Import the data // StaticNetworkObjectClass::Import_Rare (packet); phys_obj->DefenseObject.Import (packet); // // Did the health drop below 0? // if ((old_health > 0) && (phys_obj->DefenseObject.Get_Health () <= 0)) { // // Anim data should already been sent // phys_obj->Get_Animation_Manager().Set_Animation_Mode (AnimCollisionManagerClass::ANIMATE_TARGET); // // Play the explosion // const DamageableStaticPhysDefClass *definition = phys_obj->Get_DamageableStaticPhysDef (); if (definition->KilledExplosion != 0) { ExplosionManager::Create_Explosion_At (definition->KilledExplosion, phys_obj->Get_Transform (), NULL); } } return ; } //////////////////////////////////////////////////////////////// // // Export_Rare // //////////////////////////////////////////////////////////////// void DSAPONetworkObjectClass::Export_Rare (BitStreamClass &packet) { DamageableStaticPhysClass *phys_obj = PhysObj->As_DamageableStaticPhysClass (); if (phys_obj == NULL) { return ; } // // Just export the defense object's settings // StaticNetworkObjectClass::Export_Rare (packet); phys_obj->DefenseObject.Export (packet); return ; }