/* ** 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/vehiclefactorygameobj.cpp $* * * * $Author:: Byon_g $* * * * $Modtime:: 3/19/02 10:44a $* * * * $Revision:: 26 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "vehiclefactorygameobj.h" #include "basecontroller.h" #include "vehicle.h" #include "wwhack.h" #include "simpledefinitionfactory.h" #include "persistfactory.h" #include "definitionmgr.h" #include "combatchunkid.h" #include "debug.h" #include "scriptzone.h" #include "wwprofile.h" #include "basecontroller.h" #include "doors.h" #include "objlibrary.h" #include "combat.h" #include "soldier.h" #include "pathfind.h" #include "waypath.h" #include "gameobjmanager.h" #include "mapmgr.h" const float UNITIALIZED_TIMER = -100.0F; const int DEFAULT_MAX_VEHICLES_PER_TEAM = 8; //////////////////////////////////////////////////////////////// // Hacks //////////////////////////////////////////////////////////////// DECLARE_FORCE_LINK (VehicleFactory) //////////////////////////////////////////////////////////////// // Editable and persist factories //////////////////////////////////////////////////////////////// SimplePersistFactoryClass _VehicleFactoryGameObjDefPersistFactory; SimplePersistFactoryClass _VehicleFactoryGameObjPersistFactory; DECLARE_DEFINITION_FACTORY (VehicleFactoryGameObjDef, CLASSID_GAME_OBJECT_DEF_VEHICLE_FACTORY, "Vehicle Factory") _VehicleFactoryGameObjDefDefFactory (false); //////////////////////////////////////////////////////////////// // Static members //////////////////////////////////////////////////////////////// int VehicleFactoryGameObj::MaxVehiclesPerTeam = DEFAULT_MAX_VEHICLES_PER_TEAM; //////////////////////////////////////////////////////////////// // Save/Load constants //////////////////////////////////////////////////////////////// enum { CHUNKID_DEF_PARENT = 0x02200638, CHUNKID_DEF_VARIABLES, MICROCHUNKID_DEF_UNUSED = 1, MICROCHUNKID_DEF_PADCLEARINGWARHEAD, MICROCHUNKID_DEF_TOTALBUILDINGTIME, }; enum { CHUNKID_PARENT = 0x0219043, CHUNKID_VARIABLES, MICROCHUNKID_CREATION_TM = 1, MICROCHUNKID_IS_BUSY, MICROCHUNKID_GENERATION_TIME, MICROCHUNKID_GENERATING_VEHICLE_ID, }; //////////////////////////////////////////////////////////////// // // VehicleFactoryGameObjDef // //////////////////////////////////////////////////////////////// VehicleFactoryGameObjDef::VehicleFactoryGameObjDef (void) : PadClearingWarhead(25), // default to "DEATH" TotalBuildingTime(12.0f) { #ifdef PARAM_EDITING_ON EnumParameterClass *param = new EnumParameterClass( &PadClearingWarhead ); param->Set_Name ( "Pad Clearing Warhead" ); for (int i = 0; i < ArmorWarheadManager::Get_Num_Warhead_Types(); i++) { param->Add_Value ( ArmorWarheadManager::Get_Warhead_Name( i ), i ); } GENERIC_EDITABLE_PARAM(VehicleFactoryGameObjDef,param) #endif return ; } //////////////////////////////////////////////////////////////// // // ~VehicleFactoryGameObjDef // //////////////////////////////////////////////////////////////// VehicleFactoryGameObjDef::~VehicleFactoryGameObjDef (void) { return ; } //////////////////////////////////////////////////////////////// // // Get_Class_ID // //////////////////////////////////////////////////////////////// uint32 VehicleFactoryGameObjDef::Get_Class_ID (void) const { return CLASSID_GAME_OBJECT_DEF_VEHICLE_FACTORY; } //////////////////////////////////////////////////////////////// // // Create // //////////////////////////////////////////////////////////////// PersistClass * VehicleFactoryGameObjDef::Create (void) const { VehicleFactoryGameObj *building = new VehicleFactoryGameObj; building->Init (*this); return building; } //////////////////////////////////////////////////////////////// // // Create // //////////////////////////////////////////////////////////////// bool VehicleFactoryGameObjDef::Save (ChunkSaveClass &csave) { csave.Begin_Chunk (CHUNKID_DEF_PARENT); BuildingGameObjDef::Save (csave); csave.End_Chunk (); csave.Begin_Chunk (CHUNKID_DEF_VARIABLES); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_PADCLEARINGWARHEAD, PadClearingWarhead); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_DEF_TOTALBUILDINGTIME, TotalBuildingTime); csave.End_Chunk (); return true; } //////////////////////////////////////////////////////////////// // // Load // //////////////////////////////////////////////////////////////// bool VehicleFactoryGameObjDef::Load (ChunkLoadClass &cload) { while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case CHUNKID_DEF_PARENT: BuildingGameObjDef::Load (cload); break; case CHUNKID_DEF_VARIABLES: Load_Variables (cload); break; default: Debug_Say (("Unrecognized Vehicle Factory Def chunkID\n")); break; } cload.Close_Chunk (); } return true; } //////////////////////////////////////////////////////////////// // // Load_Variables // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObjDef::Load_Variables (ChunkLoadClass &cload) { while (cload.Open_Micro_Chunk ()) { switch (cload.Cur_Micro_Chunk_ID ()) { READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_PADCLEARINGWARHEAD, PadClearingWarhead); READ_MICRO_CHUNK (cload, MICROCHUNKID_DEF_TOTALBUILDINGTIME, TotalBuildingTime); default: Debug_Say (("Unrecognized Vehicle Factory Def Variable chunkID\n")); break; } cload.Close_Micro_Chunk(); } return ; } //////////////////////////////////////////////////////////////// // // Get_Factory // //////////////////////////////////////////////////////////////// const PersistFactoryClass & VehicleFactoryGameObjDef::Get_Factory (void) const { return _VehicleFactoryGameObjDefPersistFactory; } //////////////////////////////////////////////////////////////// // // VehicleFactoryGameObj // //////////////////////////////////////////////////////////////// VehicleFactoryGameObj::VehicleFactoryGameObj (void) : CreationTM (1), IsBusy (false), GenerationTime (0), GeneratingVehicleID (0), GeneratingRegion (Vector3 (0, 0, 0), Vector3 (0, 0, 0)), LastDeliveryPath (0), EndTimer (UNITIALIZED_TIMER) { return ; } //////////////////////////////////////////////////////////////// // // ~VehicleFactoryGameObj // //////////////////////////////////////////////////////////////// VehicleFactoryGameObj::~VehicleFactoryGameObj (void) { return ; } //////////////////////////////////////////////////////////////// // // Get_Factory // //////////////////////////////////////////////////////////////// const PersistFactoryClass & VehicleFactoryGameObj::Get_Factory (void) const { return _VehicleFactoryGameObjPersistFactory; } //////////////////////////////////////////////////////////////// // // Init // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Init (void) { Init (Get_Definition ()); return ; } //////////////////////////////////////////////////////////////// // // Init // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Init (const VehicleFactoryGameObjDef &definition) { BuildingGameObj::Init (definition); return ; } //////////////////////////////////////////////////////////////// // // Get_Definition // //////////////////////////////////////////////////////////////// const VehicleFactoryGameObjDef & VehicleFactoryGameObj::Get_Definition (void) const { return (const VehicleFactoryGameObjDef &)BaseGameObj::Get_Definition (); } //////////////////////////////////////////////////////////////// // // Save // //////////////////////////////////////////////////////////////// bool VehicleFactoryGameObj::Save (ChunkSaveClass &csave) { csave.Begin_Chunk (CHUNKID_PARENT); BuildingGameObj::Save (csave); csave.End_Chunk (); csave.Begin_Chunk (CHUNKID_VARIABLES); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_CREATION_TM, CreationTM); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_IS_BUSY, IsBusy); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_GENERATION_TIME, GenerationTime); WRITE_MICRO_CHUNK (csave, MICROCHUNKID_GENERATING_VEHICLE_ID, GeneratingVehicleID); csave.End_Chunk (); return true; } //////////////////////////////////////////////////////////////// // // Load // //////////////////////////////////////////////////////////////// bool VehicleFactoryGameObj::Load (ChunkLoadClass &cload) { while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case CHUNKID_PARENT: BuildingGameObj::Load (cload); break; case CHUNKID_VARIABLES: Load_Variables (cload); break; default: Debug_Say (("Unrecognized Vehicle Factory chunkID\n")); break; } cload.Close_Chunk(); } return true; } //////////////////////////////////////////////////////////////// // // Load_Variables // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Load_Variables (ChunkLoadClass &cload) { while (cload.Open_Micro_Chunk ()) { switch (cload.Cur_Micro_Chunk_ID ()) { READ_MICRO_CHUNK (cload, MICROCHUNKID_CREATION_TM, CreationTM); READ_MICRO_CHUNK (cload, MICROCHUNKID_IS_BUSY, IsBusy); READ_MICRO_CHUNK (cload, MICROCHUNKID_GENERATION_TIME, GenerationTime); READ_MICRO_CHUNK (cload, MICROCHUNKID_GENERATING_VEHICLE_ID, GeneratingVehicleID); default: Debug_Say (("Unrecognized Vehicle Factory Variable chunkID\n")); break; } cload.Close_Micro_Chunk(); } return ; } //////////////////////////////////////////////////////////////// // // CnC_Initialize // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::CnC_Initialize (BaseControllerClass *base) { BuildingGameObj::CnC_Initialize (base); // // Get the building's "position" // Vector3 pos; Get_Position (&pos); // // Find the closest vehicle construction zone to the building // ScriptZoneGameObj *zone = NULL; zone = ScriptZoneGameObj::Find_Closest_Zone (pos, ZoneConstants::TYPE_VEHICLE_CONSTRUCTION); if (zone != NULL) { GeneratingRegion = zone->Get_Bounding_Box (); // // Get rid of the zone (if the game doesn't need it anymore) // if (zone->Get_Observers ().Count () == 0) { zone->Set_Delete_Pending (); } } // // Let the base know it can generate vehicles // if (BaseController != NULL) { BaseController->Set_Can_Generate_Vehicles (true); } return ; } //////////////////////////////////////////////////////////////// // // Think // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Think (void) { WWPROFILE ("Vehicle Factory Think"); if (EndTimer > UNITIALIZED_TIMER) { EndTimer -= TimeManager::Get_Frame_Seconds (); if (EndTimer < 0) { // // Handle the end-condition // On_Generation_Complete (); EndTimer = UNITIALIZED_TIMER; } } BuildingGameObj::Think (); return ; } //////////////////////////////////////////////////////////////// // // Is_Available_For_Purchase // // In addition to checking whether the factory is busy or // destroyed, this function checks whether the team has // already created its maximum number of vehicles. // //////////////////////////////////////////////////////////////// bool VehicleFactoryGameObj::Is_Available_For_Purchase (void) const { int team_vehicle_count = Get_Team_Vehicle_Count(); return (Is_Available() & (team_vehicle_count < Get_Max_Vehicles_Per_Team())); } //////////////////////////////////////////////////////////////// // // Is_Available_For_Purchase // // Counts the number of vehicles active for this factory's team // (not including the harvester!) // //////////////////////////////////////////////////////////////// int VehicleFactoryGameObj::Get_Team_Vehicle_Count(void) const { int team_vehicle_count = 0; VehicleGameObj * harvy = NULL; BaseControllerClass * base = BaseControllerClass::Find_Base(Get_Player_Type()); if (base != NULL) { harvy = base->Get_Harvester_Vehicle(); } SLNode * smart_objnode; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { VehicleGameObj * obj = smart_objnode->Data()->As_VehicleGameObj(); if ( (obj != NULL) && (obj != harvy) && (obj->Peek_Physical_Object() != NULL) && (obj->Peek_Physical_Object()->As_VehiclePhysClass() != NULL) ) { if (obj->Get_Definition().Get_Default_Player_Type() == Get_Player_Type()) { team_vehicle_count++; } } } return team_vehicle_count; } //////////////////////////////////////////////////////////////// // // On_Generation_Complete // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::On_Generation_Complete (void) { // // Notify the base that we just created a vehicle // if (Vehicle != NULL) { // // Shut off the action making the vehicle drive off the pad. // (if it didn't make it by now, give up anyway) // VehicleGameObj * vehicle_obj = Vehicle.Get_Ptr ()->As_VehicleGameObj(); if (vehicle_obj != NULL) { ActionClass * action = vehicle_obj->Get_Action(); if (action != NULL) { // Change the arrived distance so the vehicles think // they've arrived at their destination (complete the action) action->Get_Parameters().Set_Movement(Vector3(0,0,0), 0.0f, 1000.0f); } BaseController->On_Vehicle_Generated (vehicle_obj); } } // // Reset our variables // GeneratingVehicleID = 0; GenerationTime = 0; IsBusy = false; Set_Object_Dirty_Bit (BIT_RARE, true); return ; } //////////////////////////////////////////////////////////////// // // On_Destroyed // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::On_Destroyed (void) { BuildingGameObj::On_Destroyed (); // // Switch off the ability to build vehicles // if (BaseController != NULL && CombatManager::I_Am_Server ()) { BaseController->Set_Can_Generate_Vehicles (false); } return ; } //////////////////////////////////////////////////////////////// // // Request_Vehicle // //////////////////////////////////////////////////////////////// bool VehicleFactoryGameObj::Request_Vehicle (int definition_id, float generation_time,SoldierGameObj * purchaser) { bool retval = false; // // Start the generation if possible // if (IsBusy == false && definition_id != 0) { GeneratingVehicleID = definition_id; GenerationTime = generation_time; IsBusy = true; Purchaser = purchaser; EndTimer = Get_Definition().Get_Total_Building_Time(); Begin_Generation (); Set_Object_Dirty_Bit (BIT_RARE, true); } return retval; } //////////////////////////////////////////////////////////////// // // Destroy_Blocking_Objects // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Destroy_Blocking_Objects (void) { if (CombatManager::I_Am_Server () == false) { return ; } // // Collect the dynamic physics objects in the generation region // NonRefPhysListClass objs_to_kill; PhysicsSceneClass::Get_Instance ()->Collect_Objects (GeneratingRegion, false, true, &objs_to_kill); // // Loop over all the objects // NonRefPhysListIterator it (&objs_to_kill); for (it.First (); !it.Is_Done (); it.Next ()) { PhysicalGameObj *gameobj = NULL; // // Get the game object from this physics object // if (it.Peek_Obj()->Get_Observer () != NULL) { gameobj = ((CombatPhysObserverClass *)it.Peek_Obj()->Get_Observer())->As_PhysicalGameObj(); } // // Kill the object // if (gameobj != NULL && gameobj != Vehicle && gameobj->As_ArmedGameObj () != NULL) { // // Destroy the object // OffenseObjectClass offense_obj (10000.0F, Get_Definition().Get_Pad_Clearing_Warhead()); gameobj->Apply_Damage_Extended( offense_obj ); } } return ; } //////////////////////////////////////////////////////////////// // // Deliver_Vehicle, // Gives the vehicle an action to follow one of the delivery // waypaths out of the construction zone. // //////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Deliver_Vehicle(void) { // // Give the vehicle a command to drive to one of the delivery points // PathfindClass * pathfind = PathfindClass::Get_Instance(); AABoxClass box; box.Center = GeneratingRegion.Center; box.Extent = GeneratingRegion.Extent; if (pathfind != NULL) { WaypathClass * path = pathfind->Get_Waypath_Starting_In_Box(box,LastDeliveryPath); if (path != NULL) { LastDeliveryPath = (LastDeliveryPath + 1) % pathfind->Count_Waypaths_Starting_In_Box(box); if ((Vehicle.Get_Ptr() != NULL) && (Vehicle.Get_Ptr()->As_VehicleGameObj() != NULL)) { VehicleGameObj * vehicle = Vehicle.Get_Ptr ()->As_VehicleGameObj(); ActionClass * action = vehicle->Get_Action(); if (action != NULL) { ActionParamsStruct parameters; parameters.Priority = 1; parameters.Set_Movement(Vector3(0,0,0), 1.0f, 1.0f); parameters.WaypathID = path->Get_ID(); parameters.ShutdownEngineOnArrival = true; action->Goto(parameters); vehicle->Set_Vehicle_Delivered(); BaseController->On_Vehicle_Delivered(vehicle); } } REF_PTR_RELEASE(path); } } } //////////////////////////////////////////////////////////////// // // Create_Vehicle // //////////////////////////////////////////////////////////////// VehicleGameObj * VehicleFactoryGameObj::Create_Vehicle (void) { if (CombatManager::I_Am_Server () == false) { return NULL; } VehicleGameObj *vehicle = NULL; if (GeneratingVehicleID != 0) { // // Create the vehicle object // PhysicalGameObj *physical_obj = ObjectLibraryManager::Create_Object (GeneratingVehicleID); // // If this is a VTOL vehicle, see if we should let them have it... // if ((physical_obj != NULL) && (physical_obj->Peek_Physical_Object() != NULL)) { if ( (MapMgrClass::Are_VTOL_Vehicles_Enabled() == false) && (physical_obj->Peek_Physical_Object()->As_VTOLVehicleClass() != NULL) ) { physical_obj->Set_Delete_Pending(); physical_obj = NULL; } } // // If everything is ok, proceed // if (physical_obj != NULL && physical_obj->As_VehicleGameObj () != NULL) { vehicle = physical_obj->As_VehicleGameObj (); vehicle->Start_Observers(); Vehicle = vehicle; } } return vehicle; } ///////////////////////////////////////////////////////////////////////////// // // Import_Rare // ///////////////////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Import_Rare (BitStreamClass &packet) { BuildingGameObj::Import_Rare (packet); // // Read the state information from the server // packet.Get (IsBusy); return ; } ///////////////////////////////////////////////////////////////////////////// // // Export_Rare // ///////////////////////////////////////////////////////////////////////////// void VehicleFactoryGameObj::Export_Rare (BitStreamClass &packet) { BuildingGameObj::Export_Rare (packet); // // Transmit the state information // packet.Add (IsBusy); return ; }