scriptzone.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/scriptzone.cpp $*
  25. * *
  26. * $Author:: Byon_g $*
  27. * *
  28. * $Modtime:: 1/18/02 10:44a $*
  29. * *
  30. * $Revision:: 18 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "scriptzone.h"
  36. #include "gameobjmanager.h"
  37. #include "scripts.h"
  38. #include "colmath.h"
  39. #include "colmathinlines.h"
  40. #include "debug.h"
  41. #include "persistfactory.h"
  42. #include "combatchunkid.h"
  43. #include "wwhack.h"
  44. #include "simpledefinitionfactory.h"
  45. #include "combat.h"
  46. #include "smartgameobj.h"
  47. #include "playertype.h"
  48. #include "wwprofile.h"
  49. #include "pscene.h"
  50. #include "phys.h"
  51. #include "soldier.h"
  52. /*
  53. ** ScriptZoneGameObjDef
  54. */
  55. DECLARE_FORCE_LINK( Zone )
  56. /*
  57. ** Namespaces
  58. */
  59. using namespace ZoneConstants;
  60. /*
  61. ** Constants
  62. */
  63. static const char * ZONE_TYPE_NAMES[TYPE_COUNT] =
  64. {
  65. "Default",
  66. "Capture The Flag",
  67. "Vehicle Construction",
  68. "Vehicle Repair",
  69. "Tiberium Field",
  70. "Beacon",
  71. "GDI Tiberium Field",
  72. "NOD Tiberium Field",
  73. };
  74. /*
  75. ** Editable and Save/Load system stuff
  76. */
  77. SimplePersistFactoryClass<ScriptZoneGameObjDef, CHUNKID_GAME_OBJECT_DEF_SCRIPT_ZONE> _ScriptZoneGameObjDefPersistFactory;
  78. DECLARE_DEFINITION_FACTORY(ScriptZoneGameObjDef, CLASSID_GAME_OBJECT_DEF_SCRIPT_ZONE, "Script Zone") _ScriptZoneGameObjDefDefFactory;
  79. ScriptZoneGameObjDef::ScriptZoneGameObjDef( void ) :
  80. Color( 0, 0.7F, 0 ),
  81. CheckStarsOnly( true ),
  82. ZoneType( TYPE_DEFAULT ),
  83. IsEnvironmentZone( false )
  84. {
  85. EDITABLE_PARAM( ScriptZoneGameObjDef, ParameterClass::TYPE_COLOR, Color );
  86. EDITABLE_PARAM( ScriptZoneGameObjDef, ParameterClass::TYPE_BOOL, CheckStarsOnly );
  87. EDITABLE_PARAM( ScriptZoneGameObjDef, ParameterClass::TYPE_BOOL, IsEnvironmentZone );
  88. //
  89. // Configure the zone type parameter
  90. //
  91. #ifdef PARAM_EDITING_ON
  92. EnumParameterClass *zone_type_param = new EnumParameterClass( (int*)&ZoneType );
  93. zone_type_param->Set_Name( "Zone Type" );
  94. for ( int index = 0; index < TYPE_COUNT; index++ ) {
  95. zone_type_param->Add_Value( ZONE_TYPE_NAMES[index], index );
  96. }
  97. GENERIC_EDITABLE_PARAM(ScriptZoneGameObjDef,zone_type_param);
  98. #endif
  99. }
  100. uint32 ScriptZoneGameObjDef::Get_Class_ID (void) const
  101. {
  102. return CLASSID_GAME_OBJECT_DEF_SCRIPT_ZONE;
  103. }
  104. PersistClass * ScriptZoneGameObjDef::Create( void ) const
  105. {
  106. ScriptZoneGameObj * obj = new ScriptZoneGameObj;
  107. obj->Init( *this );
  108. return obj;
  109. }
  110. enum {
  111. XXXCHUNKID_DEF_PARENT_OLD = 1111991132,
  112. CHUNKID_DEF_VARIABLES,
  113. CHUNKID_DEF_PARENT,
  114. MICROCHUNKID_DEF_IS_CTF_ZONE = 1,
  115. MICROCHUNKID_DEF_ZONE_COLOR,
  116. MICROCHUNKID_DEF_CHECK_STARS_ONLY,
  117. MICROCHUNKID_DEF_ZONE_TYPE,
  118. MICROCHUNKID_DEF_IS_ENVIRONMENT_ZONE,
  119. };
  120. bool ScriptZoneGameObjDef::Save( ChunkSaveClass & csave )
  121. {
  122. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  123. ScriptableGameObjDef::Save( csave );
  124. csave.End_Chunk();
  125. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  126. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ZONE_COLOR, Color );
  127. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_CHECK_STARS_ONLY, CheckStarsOnly );
  128. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ZONE_TYPE, ZoneType );
  129. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IS_ENVIRONMENT_ZONE, IsEnvironmentZone );
  130. csave.End_Chunk();
  131. return true;
  132. }
  133. bool ScriptZoneGameObjDef::Load( ChunkLoadClass &cload )
  134. {
  135. while (cload.Open_Chunk()) {
  136. switch(cload.Cur_Chunk_ID()) {
  137. case CHUNKID_DEF_PARENT:
  138. ScriptableGameObjDef::Load( cload );
  139. break;
  140. case CHUNKID_DEF_VARIABLES:
  141. while (cload.Open_Micro_Chunk()) {
  142. switch(cload.Cur_Micro_Chunk_ID()) {
  143. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ZONE_COLOR, Color );
  144. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_CHECK_STARS_ONLY, CheckStarsOnly );
  145. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ZONE_TYPE, ZoneType );
  146. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IS_ENVIRONMENT_ZONE, IsEnvironmentZone );
  147. default:
  148. Debug_Say(( "Unrecognized ZoneDef Variable chunkID\n" ));
  149. break;
  150. }
  151. cload.Close_Micro_Chunk();
  152. }
  153. break;
  154. default:
  155. Debug_Say(( "Unrecognized ZoneDef chunkID\n" ));
  156. break;
  157. }
  158. cload.Close_Chunk();
  159. }
  160. return true;
  161. }
  162. const PersistFactoryClass & ScriptZoneGameObjDef::Get_Factory (void) const
  163. {
  164. return _ScriptZoneGameObjDefPersistFactory;
  165. }
  166. /*
  167. ** ScriptZoneGameObj
  168. */
  169. SimplePersistFactoryClass<ScriptZoneGameObj, CHUNKID_GAME_OBJECT_SCRIPT_ZONE> _ScriptZoneGameObjPersistFactory;
  170. ScriptZoneGameObj::ScriptZoneGameObj( void ) :
  171. PlayerType( PLAYERTYPE_NEUTRAL )
  172. {
  173. }
  174. ScriptZoneGameObj::~ScriptZoneGameObj( void )
  175. {
  176. GameObjReference * ref;
  177. while ( (ref = InsideList.Remove_Head() ) != NULL ) {
  178. *ref = NULL;
  179. delete ref;
  180. }
  181. }
  182. const PersistFactoryClass & ScriptZoneGameObj::Get_Factory (void) const
  183. {
  184. return _ScriptZoneGameObjPersistFactory;
  185. }
  186. /*
  187. **
  188. */
  189. void ScriptZoneGameObj::Init( void )
  190. {
  191. Init( Get_Definition() );
  192. }
  193. void ScriptZoneGameObj::Init( const ScriptZoneGameObjDef & definition )
  194. {
  195. ScriptableGameObj::Init( definition );
  196. }
  197. const ScriptZoneGameObjDef & ScriptZoneGameObj::Get_Definition( void ) const
  198. {
  199. return (const ScriptZoneGameObjDef &)BaseGameObj::Get_Definition();
  200. }
  201. /*
  202. ** ScriptZoneGameObj Save and Load
  203. */
  204. enum {
  205. CHUNKID_PARENT_OLD = 922991806,
  206. CHUNKID_VARIABLES,
  207. CHUNKID_INSIDE_LIST,
  208. CHUNKID_INSIDE_LIST_ENTRY,
  209. CHUNKID_PARENT,
  210. MICROCHUNKID_BOUNDING_BOX = 1,
  211. MICROCHUNKID_PLAYER_TYPE,
  212. };
  213. bool ScriptZoneGameObj::Save( ChunkSaveClass & csave )
  214. {
  215. csave.Begin_Chunk( CHUNKID_PARENT );
  216. ScriptableGameObj::Save( csave );
  217. csave.End_Chunk();
  218. csave.Begin_Chunk( CHUNKID_VARIABLES );
  219. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BOUNDING_BOX, BoundingBox );
  220. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PLAYER_TYPE, PlayerType );
  221. csave.End_Chunk();
  222. csave.Begin_Chunk( CHUNKID_INSIDE_LIST );
  223. SLNode<GameObjReference> *pobjrefnode;
  224. for ( pobjrefnode = InsideList.Head(); pobjrefnode; pobjrefnode = pobjrefnode->Next() ) {
  225. GameObjReference *ref = pobjrefnode->Data();
  226. if ( *ref != NULL ) {
  227. csave.Begin_Chunk( CHUNKID_INSIDE_LIST_ENTRY );
  228. ref->Save( csave );
  229. csave.End_Chunk();
  230. }
  231. }
  232. csave.End_Chunk();
  233. return true;
  234. }
  235. bool ScriptZoneGameObj::Load( ChunkLoadClass &cload )
  236. {
  237. while (cload.Open_Chunk()) {
  238. switch(cload.Cur_Chunk_ID()) {
  239. case CHUNKID_PARENT:
  240. ScriptableGameObj::Load( cload );
  241. break;
  242. case CHUNKID_VARIABLES:
  243. while (cload.Open_Micro_Chunk()) {
  244. switch(cload.Cur_Micro_Chunk_ID()) {
  245. READ_MICRO_CHUNK( cload, MICROCHUNKID_BOUNDING_BOX, BoundingBox );
  246. READ_MICRO_CHUNK( cload, MICROCHUNKID_PLAYER_TYPE, PlayerType );
  247. default:
  248. Debug_Say(( "Unrecognized ScriptZoneGameObj Variable chunkID\n" ));
  249. break;
  250. }
  251. cload.Close_Micro_Chunk();
  252. }
  253. break;
  254. case CHUNKID_INSIDE_LIST:
  255. WWASSERT( InsideList.Get_Count()== 0 );
  256. while (cload.Open_Chunk()) {
  257. WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_INSIDE_LIST_ENTRY );
  258. GameObjReference * ref = new GameObjReference;
  259. ref->Load( cload );
  260. InsideList.Add_Head( ref );
  261. cload.Close_Chunk();
  262. }
  263. break;
  264. default:
  265. Debug_Say(( "Unrecognized ScriptZoneGameObj chunkID\n" ));
  266. break;
  267. }
  268. cload.Close_Chunk();
  269. }
  270. // Legacy Fix
  271. switch( PlayerType ) {
  272. case 2: PlayerType = PLAYERTYPE_NEUTRAL; break; // Remap TEAM2
  273. case 3: PlayerType = PLAYERTYPE_NEUTRAL; break; // Remap TEAM3
  274. case 4: PlayerType = PLAYERTYPE_NOD; break; // Remap NOD
  275. case 5: PlayerType = PLAYERTYPE_GDI; break; // Remap GDI
  276. }
  277. return true;
  278. }
  279. /*
  280. **
  281. */
  282. void ScriptZoneGameObj::Think()
  283. {
  284. ScriptableGameObj::Think();
  285. if ( Get_Observers().Count == 0 && Get_Definition().ZoneType != TYPE_CTF ) {
  286. return;
  287. }
  288. WWPROFILE( "ScriptZone Think" );
  289. // check current objects for exiting
  290. SLNode<GameObjReference> *pobjrefnode;
  291. for ( pobjrefnode = InsideList.Head(); pobjrefnode; ) {
  292. SmartGameObj * obj = (SmartGameObj*)pobjrefnode->Data()->Get_Ptr();
  293. SLNode<GameObjReference> * next = pobjrefnode->Next();
  294. if ( obj == NULL ) {
  295. Debug_Say(( "Object died inside me\n" ));
  296. GameObjReference * ref = pobjrefnode->Data();
  297. InsideList.Remove( ref );
  298. *ref = NULL;
  299. delete ref;
  300. } else if ( !Inside_Me( obj ) ) {
  301. const GameObjObserverList & observer_list = Get_Observers();
  302. for( int index = 0; index < observer_list.Count(); index++ ) {
  303. observer_list[ index ]->Exited( this, (PhysicalGameObj*)obj );
  304. }
  305. GameObjReference * ref = pobjrefnode->Data();
  306. InsideList.Remove( ref );
  307. *ref = NULL;
  308. delete ref;
  309. }
  310. pobjrefnode = next;
  311. }
  312. // If only gathering stars...
  313. if (Get_Definition().CheckStarsOnly ) {
  314. WWPROFILE( "Star Enter" );
  315. // check all stars for entering
  316. SLNode<SoldierGameObj> *objnode;
  317. for ( objnode = GameObjManager::Get_Star_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
  318. SoldierGameObj * obj = objnode->Data();
  319. if ( obj && Inside_Me( obj ) && !In_List( obj ) ) {
  320. Entered( obj );
  321. }
  322. }
  323. } else {
  324. WWPROFILE( "All Enter" );
  325. // Collect the dynamic physics objects overlapping this zone
  326. NonRefPhysListClass objs_in_zone;
  327. PhysicsSceneClass::Get_Instance()->Collect_Objects( BoundingBox, false, true, &objs_in_zone );
  328. NonRefPhysListIterator it(&objs_in_zone);
  329. for (it.First(); !it.Is_Done(); it.Next()) {
  330. if ( it.Peek_Obj()->Get_Observer() != NULL ) {
  331. PhysicalGameObj * gameobj = ((CombatPhysObserverClass *)it.Peek_Obj()->Get_Observer())->As_PhysicalGameObj();
  332. if (gameobj != NULL) {
  333. SmartGameObj *obj = gameobj->As_SmartGameObj();
  334. if ( obj && Inside_Me( obj ) && !In_List( obj ) ) {
  335. Entered( obj );
  336. }
  337. }
  338. }
  339. }
  340. }
  341. }
  342. /*
  343. **
  344. */
  345. void ScriptZoneGameObj::Entered( SmartGameObj * obj )
  346. {
  347. const GameObjObserverList & observer_list = Get_Observers();
  348. for( int index = 0; index < observer_list.Count(); index++ ) {
  349. observer_list[ index ]->Entered( this, obj );
  350. }
  351. // Create a new reference and add it to our list
  352. GameObjReference * ref = new GameObjReference;
  353. *ref = obj;
  354. InsideList.Add_Head( ref );
  355. }
  356. /*
  357. **
  358. */
  359. bool ScriptZoneGameObj::In_List( SmartGameObj * obj )
  360. {
  361. WWASSERT( obj != NULL );
  362. SLNode<GameObjReference> *pobjrefnode;
  363. for ( pobjrefnode = InsideList.Head(); pobjrefnode; pobjrefnode = pobjrefnode->Next() ) {
  364. if ( obj == *pobjrefnode->Data() ) {
  365. return true;
  366. }
  367. }
  368. return false;
  369. }
  370. /*
  371. **
  372. */
  373. bool ScriptZoneGameObj::Inside_Me( const SmartGameObj * obj )
  374. {
  375. if ( obj && obj->Peek_Physical_Object() ) { // hack to hide zones
  376. Vector3 pos;
  377. obj->Get_Position(&pos);
  378. return (CollisionMath::Overlap_Test(BoundingBox,pos) == CollisionMath::INSIDE);
  379. }
  380. return false;
  381. }
  382. /*
  383. **
  384. */
  385. ScriptZoneGameObj *ScriptZoneGameObj::Find_Closest_Zone (const Vector3 &pos, ZoneConstants::ZoneType type)
  386. {
  387. SList<BaseGameObj> *list = GameObjManager::Get_Game_Obj_List ();
  388. float closest_dist2 = 999999.0F;
  389. ScriptZoneGameObj *closest_zone = NULL;
  390. //
  391. // Loop over all the objects looking for zones
  392. //
  393. SLNode<BaseGameObj> *node = NULL;
  394. for (node = list->Head (); node != NULL; node = node->Next ()) {
  395. BaseGameObj *game_obj = node->Data ();
  396. //
  397. // Is this a building on our team?
  398. //
  399. if (game_obj != NULL && game_obj->As_ScriptableGameObj () != NULL) {
  400. ScriptZoneGameObj *zone = game_obj->As_ScriptableGameObj ()->As_ScriptZoneGameObj ();
  401. if (zone != NULL) {
  402. //
  403. // Is the type of zone we are looking for?
  404. //
  405. if (zone->Get_Definition ().Get_Type () == type) {
  406. float dist2 = (pos - zone->Get_Bounding_Box ().Center).Length2 ();
  407. //
  408. // Is this the closest zone we've found yet?
  409. //
  410. if (dist2 < closest_dist2) {
  411. closest_dist2 = dist2;
  412. closest_zone = zone;
  413. }
  414. }
  415. }
  416. }
  417. }
  418. return closest_zone;
  419. }
  420. /*
  421. **
  422. */
  423. int ScriptZoneGameObj::Count_Team_Members_Inside( int player_type )
  424. {
  425. SLNode<GameObjReference> *pobjrefnode;
  426. int count = 0;
  427. for ( pobjrefnode = InsideList.Head(); pobjrefnode; pobjrefnode = pobjrefnode->Next() ) {
  428. SmartGameObj * obj = (SmartGameObj*)pobjrefnode->Data()->Get_Ptr();
  429. if ( obj->Get_Player_Type() == player_type ) {
  430. count++;
  431. }
  432. }
  433. return count;
  434. }
  435. /*
  436. **
  437. */
  438. /*
  439. bool ScriptZoneGameObj::Exists_On_Client( void ) const
  440. {
  441. bool retval = false;
  442. //
  443. // Allow the C&C mode zone's to exist on the client
  444. //
  445. switch (Get_Definition ().Get_Type ())
  446. {
  447. case TYPE_VEHICLE_CONSTRUCTION:
  448. case TYPE_VEHICLE_REPAIR:
  449. case TYPE_TIBERIUM_FIELD:
  450. case TYPE_BEACON:
  451. retval = false;
  452. break;
  453. }
  454. return retval;
  455. }
  456. */
  457. /*
  458. if (Get_Definition().ZoneType == TYPE_CTF && obj->As_SoldierGameObj() != NULL) {
  459. CombatManager::Soldier_Enters_Pedestal(obj->As_SoldierGameObj(), Get_Player_Type());
  460. }
  461. */