building.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635
  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/building.cpp $*
  25. * *
  26. * $Author:: Greg_h $*
  27. * *
  28. * $Modtime:: 6/20/02 3:09p $*
  29. * *
  30. * $Revision:: 68 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  34. #include "building.h"
  35. #include "simpledefinitionfactory.h"
  36. #include "persistfactory.h"
  37. #include "combatchunkid.h"
  38. #include "debug.h"
  39. #include "wwphysids.h"
  40. #include "meshmdl.h"
  41. #include "gameobjmanager.h"
  42. #include "wwaudio.h"
  43. #include "audiblesound.h"
  44. #include "sound3d.h"
  45. #include "translateobj.h"
  46. #include "translatedb.h"
  47. #include "combat.h"
  48. #include "messagewindow.h"
  49. #include "buildingmonitor.h"
  50. #include "basecontroller.h"
  51. #include "bitpackids.h"
  52. #include "soldier.h"
  53. #include "playerdata.h"
  54. #include "encyclopediamgr.h"
  55. #include "apppackettypes.h"
  56. #include "wwprofile.h"
  57. /////////////////////////////////////////////////////////////////////////////
  58. // Namespaces
  59. /////////////////////////////////////////////////////////////////////////////
  60. using namespace BuildingConstants;
  61. /////////////////////////////////////////////////////////////////////////////
  62. //
  63. // Constants
  64. //
  65. /////////////////////////////////////////////////////////////////////////////
  66. static const char * BULDING_TYPE_NAMES[TYPE_COUNT + 1] =
  67. {
  68. "<None>",
  69. "Power Plant",
  70. "Soldier Factory",
  71. "Vehicle Factory",
  72. "Refinery",
  73. "Com Center",
  74. "Repair Bay",
  75. "Shrine",
  76. "Helipad",
  77. "Conyard",
  78. "Base Defense",
  79. };
  80. static const char * BULDING_TEAM_NAMES[BASE_COUNT] =
  81. {
  82. "GDI",
  83. "NOD"
  84. };
  85. //
  86. // Class statics
  87. //
  88. bool BuildingGameObj::CanRepairBuildings = true;
  89. /************************************************************************************************
  90. **
  91. ** BuildingGameObjDef
  92. **
  93. ************************************************************************************************/
  94. SimplePersistFactoryClass<BuildingGameObjDef, CHUNKID_GAME_OBJECT_DEF_BUILDING> _BuildingGameObjDefPersistFactory;
  95. DECLARE_DEFINITION_FACTORY(BuildingGameObjDef, CLASSID_GAME_OBJECT_DEF_BUILDING, "<Generic Building>") _BuildingGameObjDefDefFactory;
  96. /////////////////////////////////////////////////////////////////////////////
  97. //
  98. // BuildingGameObjDef
  99. //
  100. /////////////////////////////////////////////////////////////////////////////
  101. BuildingGameObjDef::BuildingGameObjDef (void) :
  102. MCTSkin(0),
  103. Type(TYPE_NONE),
  104. GDIDamageReportID(0),
  105. NodDamageReportID(0),
  106. GDIDestroyReportID(0),
  107. NodDestroyReportID(0)
  108. {
  109. EDITABLE_PARAM( BuildingGameObjDef, ParameterClass::TYPE_STRING, MeshPrefix );
  110. #ifdef PARAM_EDITING_ON
  111. int skin_type_counter;
  112. EnumParameterClass * mct_skin_param = new EnumParameterClass( (int*)&MCTSkin );
  113. mct_skin_param->Set_Name("MCTSkin");
  114. for ( skin_type_counter = 0; skin_type_counter < ArmorWarheadManager::Get_Num_Armor_Types(); skin_type_counter++ ) {
  115. mct_skin_param->Add_Value(ArmorWarheadManager::Get_Armor_Name(skin_type_counter), skin_type_counter);
  116. }
  117. GENERIC_EDITABLE_PARAM(BuildingGameObjDef,mct_skin_param);
  118. #endif
  119. //
  120. // Configure the building type parameter
  121. //
  122. #ifdef PARAM_EDITING_ON
  123. EnumParameterClass *building_type_param = new EnumParameterClass( (int*)&Type );
  124. building_type_param->Set_Name("Building Type");
  125. for ( int index = TYPE_NONE; index < TYPE_COUNT; index++ ) {
  126. building_type_param->Add_Value(BULDING_TYPE_NAMES[index+1], index);
  127. }
  128. GENERIC_EDITABLE_PARAM(BuildingGameObjDef,building_type_param);
  129. #endif
  130. EDITABLE_PARAM(BuildingGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, GDIDamageReportID);
  131. EDITABLE_PARAM(BuildingGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, NodDamageReportID);
  132. EDITABLE_PARAM(BuildingGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, GDIDestroyReportID);
  133. EDITABLE_PARAM(BuildingGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, NodDestroyReportID);
  134. }
  135. /////////////////////////////////////////////////////////////////////////////
  136. //
  137. // Get_Class_ID
  138. //
  139. /////////////////////////////////////////////////////////////////////////////
  140. uint32
  141. BuildingGameObjDef::Get_Class_ID (void) const
  142. {
  143. return CLASSID_GAME_OBJECT_DEF_BUILDING;
  144. }
  145. /////////////////////////////////////////////////////////////////////////////
  146. //
  147. // Create
  148. //
  149. /////////////////////////////////////////////////////////////////////////////
  150. PersistClass *
  151. BuildingGameObjDef::Create (void) const
  152. {
  153. BuildingGameObj * obj = new BuildingGameObj;
  154. obj->Init( *this );
  155. return obj;
  156. }
  157. /////////////////////////////////////////////////////////////////////////////
  158. // Save/Load constants
  159. /////////////////////////////////////////////////////////////////////////////
  160. enum {
  161. CHUNKID_DEF_PARENT = 207011030,
  162. CHUNKID_DEF_VARIABLES,
  163. MICROCHUNKID_DEF_MESHPREFIX = 1,
  164. MICROCHUNKID_DEF_MCTSKIN,
  165. MICROCHUNKID_DEF_BUILDING_TYPE,
  166. LEGACY_MICROCHUNKID_DEF_BUILDING_TEAM,
  167. MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID,
  168. MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID,
  169. MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID,
  170. MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID,
  171. };
  172. /////////////////////////////////////////////////////////////////////////////
  173. //
  174. // Save
  175. //
  176. /////////////////////////////////////////////////////////////////////////////
  177. bool
  178. BuildingGameObjDef::Save (ChunkSaveClass &csave)
  179. {
  180. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  181. DamageableGameObjDef::Save( csave );
  182. csave.End_Chunk();
  183. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  184. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_MESHPREFIX, MeshPrefix );
  185. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_MCTSKIN, MCTSkin );
  186. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_BUILDING_TYPE, Type );
  187. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID, GDIDamageReportID );
  188. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID, NodDamageReportID );
  189. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID, GDIDestroyReportID );
  190. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID, NodDestroyReportID );
  191. csave.End_Chunk();
  192. return true;
  193. }
  194. /////////////////////////////////////////////////////////////////////////////
  195. //
  196. // Load
  197. //
  198. /////////////////////////////////////////////////////////////////////////////
  199. bool
  200. BuildingGameObjDef::Load (ChunkLoadClass &cload)
  201. {
  202. int legacy_team = -1;
  203. while (cload.Open_Chunk()) {
  204. switch(cload.Cur_Chunk_ID()) {
  205. case CHUNKID_DEF_PARENT:
  206. DamageableGameObjDef::Load( cload );
  207. break;
  208. case CHUNKID_DEF_VARIABLES:
  209. while (cload.Open_Micro_Chunk()) {
  210. switch(cload.Cur_Micro_Chunk_ID()) {
  211. READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_MESHPREFIX, MeshPrefix );
  212. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_MCTSKIN, MCTSkin );
  213. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_BUILDING_TYPE, Type );
  214. READ_MICRO_CHUNK( cload, LEGACY_MICROCHUNKID_DEF_BUILDING_TEAM, legacy_team );
  215. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID, GDIDamageReportID );
  216. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID, NodDamageReportID );
  217. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID, GDIDestroyReportID );
  218. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID, NodDestroyReportID );
  219. default:
  220. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  221. break;
  222. }
  223. cload.Close_Micro_Chunk();
  224. }
  225. break;
  226. default:
  227. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  228. break;
  229. }
  230. cload.Close_Chunk();
  231. }
  232. if ( legacy_team != -1 ) {
  233. DefaultPlayerType = PLAYERTYPE_GDI;
  234. if ( legacy_team == BuildingConstants::LEGACY_TEAM_NOD ) {
  235. DefaultPlayerType = PLAYERTYPE_NOD;
  236. }
  237. }
  238. return true;
  239. }
  240. /////////////////////////////////////////////////////////////////////////////
  241. //
  242. // Get_Factory
  243. //
  244. /////////////////////////////////////////////////////////////////////////////
  245. const PersistFactoryClass &
  246. BuildingGameObjDef::Get_Factory (void) const
  247. {
  248. return _BuildingGameObjDefPersistFactory;
  249. }
  250. int BuildingGameObjDef::Get_Damage_Report(int team) const
  251. {
  252. if (PLAYERTYPE_GDI == team) {
  253. return GDIDamageReportID;
  254. } else if (PLAYERTYPE_NOD == team) {
  255. return NodDamageReportID;
  256. }
  257. return 0;
  258. }
  259. int BuildingGameObjDef::Get_Destroy_Report(int team) const
  260. {
  261. if (PLAYERTYPE_GDI == team) {
  262. return GDIDestroyReportID;
  263. } else if (PLAYERTYPE_NOD == team) {
  264. return NodDestroyReportID;
  265. }
  266. return 0;
  267. }
  268. /************************************************************************************************
  269. **
  270. ** BuildingGameObj
  271. **
  272. ************************************************************************************************/
  273. SimplePersistFactoryClass<BuildingGameObj, CHUNKID_GAME_OBJECT_BUILDING> _BuildingGameObjPersistFactory;
  274. /////////////////////////////////////////////////////////////////////////////
  275. //
  276. // Get_Factory
  277. //
  278. /////////////////////////////////////////////////////////////////////////////
  279. const PersistFactoryClass &
  280. BuildingGameObj::Get_Factory (void) const
  281. {
  282. return _BuildingGameObjPersistFactory;
  283. }
  284. /////////////////////////////////////////////////////////////////////////////
  285. //
  286. // BuildingGameObj
  287. //
  288. /////////////////////////////////////////////////////////////////////////////
  289. BuildingGameObj::BuildingGameObj (void) :
  290. Position (0, 0, 0),
  291. IsPowerOn (true),
  292. CurrentState (-1),
  293. AnnouncementSphere (Vector3 (0, 0, 0), 1.0F),
  294. CollectionSphere (Vector3 (0, 0, 0), 50.0F),
  295. CurrentAnnouncement (NULL),
  296. IsDestroyed (false),
  297. BuildingMonitor (NULL),
  298. BaseController (NULL)
  299. {
  300. GameObjManager::Add_Building (this);
  301. Update_State ();
  302. Set_App_Packet_Type(APPPACKETTYPE_BUILDING);
  303. }
  304. /////////////////////////////////////////////////////////////////////////////
  305. //
  306. // ~BuildingGameObj
  307. //
  308. /////////////////////////////////////////////////////////////////////////////
  309. BuildingGameObj::~BuildingGameObj (void)
  310. {
  311. Stop_Current_Announcement();
  312. //
  313. // Free the building monitor (if we have one)
  314. //
  315. if (BuildingMonitor != NULL) {
  316. Remove_Observer (BuildingMonitor);
  317. delete BuildingMonitor;
  318. BuildingMonitor = NULL;
  319. }
  320. GameObjManager::Remove_Building( this );
  321. Reset_Components();
  322. return ;
  323. }
  324. /////////////////////////////////////////////////////////////////////////////
  325. //
  326. // Init
  327. //
  328. /////////////////////////////////////////////////////////////////////////////
  329. void BuildingGameObj::Init( void )
  330. {
  331. Init( Get_Definition() );
  332. }
  333. /////////////////////////////////////////////////////////////////////////////
  334. //
  335. // Init
  336. //
  337. /////////////////////////////////////////////////////////////////////////////
  338. void
  339. BuildingGameObj::Init (const BuildingGameObjDef &definition)
  340. {
  341. DamageableGameObj::Init (definition);
  342. return ;
  343. }
  344. /////////////////////////////////////////////////////////////////////////////
  345. //
  346. // Get_Definition
  347. //
  348. /////////////////////////////////////////////////////////////////////////////
  349. const BuildingGameObjDef &
  350. BuildingGameObj::Get_Definition (void) const
  351. {
  352. return (const BuildingGameObjDef &)BaseGameObj::Get_Definition();
  353. }
  354. /////////////////////////////////////////////////////////////////////////////
  355. // Save/Load constants
  356. /////////////////////////////////////////////////////////////////////////////
  357. enum {
  358. CHUNKID_PARENT = 207011120,
  359. CHUNKID_VARIABLES,
  360. MICROCHUNKID_POSITION = 1,
  361. MICROCHUNKID_ISPOWERON,
  362. MICROCHUNKID_COLLECTION_SPHERE
  363. };
  364. /////////////////////////////////////////////////////////////////////////////
  365. //
  366. // Save
  367. //
  368. /////////////////////////////////////////////////////////////////////////////
  369. bool
  370. BuildingGameObj::Save (ChunkSaveClass &csave)
  371. {
  372. csave.Begin_Chunk (CHUNKID_PARENT);
  373. DamageableGameObj::Save (csave);
  374. csave.End_Chunk ();
  375. csave.Begin_Chunk (CHUNKID_VARIABLES);
  376. WRITE_MICRO_CHUNK (csave, MICROCHUNKID_POSITION, Position);
  377. WRITE_MICRO_CHUNK (csave, MICROCHUNKID_ISPOWERON, IsPowerOn);
  378. WRITE_MICRO_CHUNK (csave, MICROCHUNKID_COLLECTION_SPHERE, CollectionSphere);
  379. csave.End_Chunk ();
  380. return true;
  381. }
  382. /////////////////////////////////////////////////////////////////////////////
  383. //
  384. // Load
  385. //
  386. /////////////////////////////////////////////////////////////////////////////
  387. bool
  388. BuildingGameObj::Load (ChunkLoadClass &cload)
  389. {
  390. while (cload.Open_Chunk ()) {
  391. switch (cload.Cur_Chunk_ID ()) {
  392. case CHUNKID_PARENT:
  393. DamageableGameObj::Load (cload);
  394. break;
  395. case CHUNKID_VARIABLES:
  396. while (cload.Open_Micro_Chunk ()) {
  397. switch (cload.Cur_Micro_Chunk_ID ()) {
  398. READ_MICRO_CHUNK (cload, MICROCHUNKID_POSITION, Position);
  399. READ_MICRO_CHUNK (cload, MICROCHUNKID_ISPOWERON, IsPowerOn);
  400. READ_MICRO_CHUNK (cload, MICROCHUNKID_COLLECTION_SPHERE, CollectionSphere);
  401. default:
  402. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  403. break;
  404. }
  405. cload.Close_Micro_Chunk();
  406. }
  407. break;
  408. default:
  409. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  410. break;
  411. }
  412. cload.Close_Chunk ();
  413. }
  414. // Fix player_type (legacy)
  415. Set_Player_Type( Get_Definition().DefaultPlayerType );
  416. //
  417. // Hack to get legacy code to work...
  418. //
  419. if (CollectionSphere.Center.X == 0 && CollectionSphere.Center.Y == 0 && CollectionSphere.Center.Z == 0) {
  420. CollectionSphere.Center = Position;
  421. }
  422. return true;
  423. }
  424. /////////////////////////////////////////////////////////////////////////////
  425. //
  426. // Enable_Power
  427. //
  428. /////////////////////////////////////////////////////////////////////////////
  429. void
  430. BuildingGameObj::Enable_Power (bool onoff)
  431. {
  432. if ( IsPowerOn != onoff ) {
  433. // Notify the observers
  434. const GameObjObserverList & observer_list = Get_Observers();
  435. for( int index = 0; index < observer_list.Count(); index++ ) {
  436. observer_list[ index ]->Custom( this, CUSTOM_EVENT_BUILDING_POWER_CHANGED, onoff, NULL );
  437. }
  438. }
  439. IsPowerOn = onoff;
  440. Update_State ();
  441. //
  442. // Mark the object as dirty so it's state will be mirrored on the client
  443. //
  444. if (CombatManager::I_Am_Server ()) {
  445. Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  446. }
  447. return ;
  448. }
  449. void BuildingGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin )
  450. {
  451. /*
  452. ** If we're already completely damaged, just return
  453. */
  454. if ( DefenseObject.Get_Health() <= 0 ) {
  455. return;
  456. }
  457. // if gameplay not permitted, skip
  458. if ( !CombatManager::Is_Gameplay_Permitted() ) {
  459. return;
  460. }
  461. //
  462. // TSS102501
  463. //
  464. if ( !CanRepairBuildings && DefenseObject.Is_Repair(damager) ) {
  465. return;
  466. }
  467. float old_health = Get_Defense_Object()->Get_Health();
  468. DamageableGameObj::Apply_Damage( damager, scale, alternate_skin );
  469. if ( old_health != Get_Defense_Object()->Get_Health() ) {
  470. Set_Object_Dirty_Bit( NetworkObjectClass::BIT_OCCASIONAL, true );
  471. }
  472. // WWDEBUG_SAY(("Applied damage to a building (prefix=%s) new health = %f\r\n",Get_Definition().MeshPrefix,DefenseObject.Get_Health()));
  473. // Stats
  474. if ( DefenseObject.Get_Health() <= 0 ) {
  475. if ( damager.Get_Owner() != NULL &&
  476. damager.Get_Owner()->As_SoldierGameObj() != NULL &&
  477. damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data() != NULL ) {
  478. damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data()->Stats_Add_Building_Destroyed();
  479. }
  480. //
  481. // Reveal this building to the encyclopedia
  482. //
  483. EncyclopediaMgrClass::Reveal_Object (this);
  484. }
  485. /*
  486. ** If our health status changed states, switch all of our aggregates
  487. */
  488. Update_State();
  489. }
  490. void BuildingGameObj::Apply_Damage_Building(const OffenseObjectClass & offense, StaticPhysClass * component )
  491. {
  492. /*
  493. ** If the MCT is being damaged, use an alternate skin type
  494. */
  495. ArmorType skin = DefenseObject.Get_Skin();
  496. if (component->Get_Factory().Chunk_ID() == PHYSICS_CHUNKID_BUILDINGAGGREGATE) {
  497. if (((BuildingAggregateClass *)component)->Is_MCT()) {
  498. skin = Get_Definition().MCTSkin;
  499. }
  500. }
  501. /*
  502. ** Apply the damage
  503. */
  504. Apply_Damage( offense, 1.0, skin );
  505. }
  506. void BuildingGameObj::Apply_Damage_Building( const OffenseObjectClass & offense, bool mct_damage )
  507. {
  508. /*
  509. ** If the MCT is being damaged, use an alternate skin type
  510. */
  511. ArmorType skin = DefenseObject.Get_Skin();
  512. if ( mct_damage ) {
  513. skin = Get_Definition().MCTSkin;
  514. }
  515. /*
  516. ** Apply the damage
  517. */
  518. Apply_Damage( offense, 1.0, skin );
  519. }
  520. /////////////////////////////////////////////////////////////////////////////
  521. //
  522. // Is_Interior_Mesh_Name
  523. //
  524. /////////////////////////////////////////////////////////////////////////////
  525. bool
  526. BuildingGameObj::Is_Interior_Mesh_Name (const char * name)
  527. {
  528. // (gth) Renegade day 120 patch, accept tiles as building "parts"
  529. #if 0
  530. char * meshname = strchr(name,'.');
  531. if (meshname != NULL) {
  532. return strchr(meshname,'#') != NULL;
  533. } else {
  534. return false;
  535. }
  536. #else
  537. return strchr(name,'#') != NULL;
  538. #endif
  539. }
  540. /////////////////////////////////////////////////////////////////////////////
  541. //
  542. // Is_Exterior_Mesh_Name
  543. //
  544. /////////////////////////////////////////////////////////////////////////////
  545. bool
  546. BuildingGameObj::Is_Exterior_Mesh_Name (const char * name)
  547. {
  548. // (gth) Renegade day 120 patch, accept tiles as building "parts"
  549. #if 0
  550. char * meshname = strchr(name,'.');
  551. if (meshname != NULL) {
  552. return strchr(meshname,'^') != NULL;
  553. } else {
  554. return false;
  555. }
  556. #else
  557. return strchr(name,'^') != NULL;
  558. #endif
  559. }
  560. /////////////////////////////////////////////////////////////////////////////
  561. //
  562. // Name_Prefix_Matches_This_Building
  563. //
  564. /////////////////////////////////////////////////////////////////////////////
  565. bool
  566. BuildingGameObj::Name_Prefix_Matches_This_Building (const char * name)
  567. {
  568. bool retval = false;
  569. if (name != NULL) {
  570. StringClass prefex(Get_Definition().MeshPrefix,true);
  571. char * meshname = strchr(name,'.');
  572. if (meshname != NULL) {
  573. meshname++;
  574. retval = (strnicmp(meshname,prefex,strlen(prefex)) == 0);
  575. } else {
  576. retval = (strnicmp(name,prefex,strlen(prefex)) == 0);
  577. }
  578. }
  579. return retval;
  580. }
  581. /////////////////////////////////////////////////////////////////////////////
  582. //
  583. // Set_Normalized_Health
  584. //
  585. /////////////////////////////////////////////////////////////////////////////
  586. void
  587. BuildingGameObj::Set_Normalized_Health (float health)
  588. {
  589. //
  590. // Note: We convert from normalized to absolute values
  591. //
  592. DefenseObject.Set_Health (health * DefenseObject.Get_Health_Max ());
  593. Update_State ();
  594. WWDEBUG_SAY(("Building State Set:\r\n"));
  595. WWDEBUG_SAY((" prefix = %s mesh count = %d aggregate count = %d light count = %d\r\n",
  596. Get_Definition().MeshPrefix,
  597. InteriorMeshes.Count() + ExteriorMeshes.Count(),
  598. Aggregates.Count(),
  599. PowerOnLights.Count() + PowerOffLights.Count()));
  600. }
  601. /////////////////////////////////////////////////////////////////////////////
  602. //
  603. // Update_State
  604. //
  605. /////////////////////////////////////////////////////////////////////////////
  606. void
  607. BuildingGameObj::Update_State (bool force_update)
  608. {
  609. /*
  610. ** Evaluate our current state
  611. */
  612. float health_percentage = 100.0f * DefenseObject.Get_Health()/DefenseObject.Get_Health_Max();
  613. int health_state = BuildingStateClass::Percentage_To_Health_State(health_percentage);
  614. int new_state = BuildingStateClass::Compose_State(health_state,IsPowerOn);
  615. /*
  616. ** If the state has changed, inform all of our components
  617. */
  618. if ((new_state != CurrentState) || (force_update == true)) {
  619. int old_health_state = BuildingStateClass::Get_Health_State( CurrentState );
  620. if (health_state != old_health_state) {
  621. int event = CUSTOM_EVENT_BUILDING_DAMAGED;
  622. if (health_state < old_health_state) {
  623. event = CUSTOM_EVENT_BUILDING_REPAIRED;
  624. }
  625. const GameObjObserverList & observer_list = Get_Observers();
  626. for( int index = 0; index < observer_list.Count(); index++ ) {
  627. observer_list[ index ]->Custom( this, event, health_percentage, NULL );
  628. }
  629. }
  630. CurrentState = new_state;
  631. //
  632. // Mark the object as dirty so it's state will be mirrored on the client
  633. //
  634. if (CombatManager::I_Am_Server ()) {
  635. Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  636. }
  637. /*
  638. ** Aggregates: inform all aggregates of the new state
  639. */
  640. RefMultiListIterator<BuildingAggregateClass> aggregate_iterator(&Aggregates);
  641. for (aggregate_iterator.First(); !aggregate_iterator.Is_Done(); aggregate_iterator.Next()) {
  642. aggregate_iterator.Peek_Obj()->Set_Current_State(CurrentState,force_update);
  643. }
  644. /*
  645. ** Interior meshes: switch to alternate materials if either the power is out or
  646. ** the building is at zero health
  647. */
  648. Enable_Alternate_Materials(InteriorMeshes,((health_state == BuildingStateClass::HEALTH_0) || (!IsPowerOn)));
  649. /*
  650. ** Exterior meshes: use alternate materials if the building is destroyed
  651. */
  652. Enable_Alternate_Materials(ExteriorMeshes,(health_state == BuildingStateClass::HEALTH_0));
  653. /*
  654. ** Lights: enable the lights which match the current power status, invalidate all
  655. ** lighting caches within the bounds of the lights that changed.
  656. */
  657. bool disable = ((IsPowerOn == false) || (health_state == BuildingStateClass::HEALTH_0));
  658. disable &= (!PowerOffLights.Is_Empty()); // for lights, power is not disabled if there are not power-off lights
  659. AABoxClass light_bounds(Position,Vector3(1,1,1));
  660. RefMultiListIterator<LightPhysClass> light_iterator(&PowerOnLights);
  661. for (light_iterator.First(); !light_iterator.Is_Done(); light_iterator.Next()) {
  662. light_iterator.Peek_Obj()->Set_Disabled(disable);
  663. light_bounds.Add_Box(light_iterator.Peek_Obj()->Peek_Model()->Get_Bounding_Box());
  664. }
  665. disable = !disable;
  666. for (light_iterator.First(&PowerOffLights); !light_iterator.Is_Done(); light_iterator.Next()) {
  667. light_iterator.Peek_Obj()->Set_Disabled(disable);
  668. light_bounds.Add_Box(light_iterator.Peek_Obj()->Peek_Model()->Get_Bounding_Box());
  669. }
  670. PhysicsSceneClass::Get_Instance()->Invalidate_Lighting_Caches(light_bounds);
  671. }
  672. return ;
  673. }
  674. /////////////////////////////////////////////////////////////////////////////
  675. //
  676. // Play_Announcement
  677. //
  678. /////////////////////////////////////////////////////////////////////////////
  679. void
  680. BuildingGameObj::Play_Announcement(int text_id, bool broadcast)
  681. {
  682. Stop_Current_Announcement();
  683. // Lookup the translation object from the strings database
  684. TDBObjClass* translate_obj = TranslateDBClass::Find_Object(text_id);
  685. if (translate_obj) {
  686. const WCHAR* string = translate_obj->Get_String();
  687. int sound_def_id = (int)translate_obj->Get_Sound_ID();
  688. float duration = 2.0F;
  689. // Play the sound effect
  690. bool display_text = true;
  691. if (sound_def_id > 0) {
  692. // Create the sound object
  693. CurrentAnnouncement = WWAudioClass::Get_Instance()->Create_Sound(sound_def_id);
  694. if (CurrentAnnouncement) {
  695. duration = (CurrentAnnouncement->Get_Duration() / 1000.0F);
  696. if (broadcast == false) {
  697. // Tweak the radii of the sound so it can be heard throughout the building
  698. CurrentAnnouncement->Set_DropOff_Radius(AnnouncementSphere.Radius);
  699. Sound3DClass* sound3D = CurrentAnnouncement->As_Sound3DClass();
  700. if (sound3D) {
  701. sound3D->Set_Max_Vol_Radius(AnnouncementSphere.Radius * 0.9F);
  702. }
  703. // Play the sound effect at the annoucement position for the building
  704. CurrentAnnouncement->Set_Transform(Matrix3D(AnnouncementSphere.Center));
  705. display_text = (CurrentAnnouncement->Is_Sound_Culled() == false);
  706. }
  707. CurrentAnnouncement->Add_To_Scene();
  708. }
  709. }
  710. // Display the text on the screen
  711. if (display_text && string) {
  712. float message_duration = max(duration, 5.0F);
  713. CombatManager::Get_Message_Window()->Add_Message(string, Vector3(1, 1, 1), NULL, message_duration);
  714. }
  715. }
  716. }
  717. /////////////////////////////////////////////////////////////////////////////
  718. //
  719. // Stop_Current_Announcement
  720. //
  721. /////////////////////////////////////////////////////////////////////////////
  722. void
  723. BuildingGameObj::Stop_Current_Announcement (void)
  724. {
  725. if (CurrentAnnouncement) {
  726. // Stop the sound object and remove it from the world
  727. CurrentAnnouncement->Stop();
  728. CurrentAnnouncement->Remove_From_Scene();
  729. CurrentAnnouncement->Release_Ref();
  730. CurrentAnnouncement = NULL;
  731. }
  732. }
  733. /////////////////////////////////////////////////////////////////////////////
  734. //
  735. // Initialize_Building
  736. //
  737. /////////////////////////////////////////////////////////////////////////////
  738. void
  739. BuildingGameObj::Initialize_Building (void)
  740. {
  741. RefMultiListIterator<StaticPhysClass> mesh_iterator (&InteriorMeshes);
  742. //
  743. // Generate a bounding box that contains all the building interior meshes
  744. //
  745. AABoxClass bounding_box (Position, Vector3 (1, 1, 1));
  746. for (mesh_iterator.First (); !mesh_iterator.Is_Done (); mesh_iterator.Next ()) {
  747. bounding_box.Add_Box (mesh_iterator.Peek_Obj ()->Peek_Model ()->Get_Bounding_Box());
  748. }
  749. //
  750. // Turn the bounding box into a sphere that we can use to play building
  751. // announcement sound effects in
  752. //
  753. AnnouncementSphere.Center = bounding_box.Center;
  754. AnnouncementSphere.Radius = max (bounding_box.Extent.X, bounding_box.Extent.Y);
  755. AnnouncementSphere.Radius = max (AnnouncementSphere.Radius, bounding_box.Extent.Z);
  756. //
  757. // If we can find a base for our team, then add ourselves to it.
  758. //
  759. BaseControllerClass *base = BaseControllerClass::Find_Base ( Get_Player_Type() );
  760. if (base != NULL) {
  761. base->Add_Building (this);
  762. }
  763. return ;
  764. }
  765. /////////////////////////////////////////////////////////////////////////////
  766. //
  767. // Reset_Components
  768. //
  769. /////////////////////////////////////////////////////////////////////////////
  770. void
  771. BuildingGameObj::Reset_Components (void)
  772. {
  773. RefMultiListIterator<StaticPhysClass> mesh_iterator(&InteriorMeshes);
  774. for (mesh_iterator.First(); !mesh_iterator.Is_Done(); mesh_iterator.Next()) {
  775. WWASSERT(mesh_iterator.Peek_Obj()->Get_Observer() == this);
  776. mesh_iterator.Peek_Obj()->Set_Observer(NULL);
  777. }
  778. for (mesh_iterator.First(&ExteriorMeshes); !mesh_iterator.Is_Done(); mesh_iterator.Next()) {
  779. WWASSERT(mesh_iterator.Peek_Obj()->Get_Observer() == this);
  780. mesh_iterator.Peek_Obj()->Set_Observer(NULL);
  781. }
  782. RefMultiListIterator<BuildingAggregateClass> agg_iterator(&Aggregates);
  783. for (agg_iterator.First(); !agg_iterator.Is_Done(); agg_iterator.Next()) {
  784. WWASSERT(agg_iterator.Peek_Obj()->Get_Observer() == this);
  785. agg_iterator.Peek_Obj()->Set_Observer(NULL);
  786. }
  787. InteriorMeshes.Reset_List();
  788. ExteriorMeshes.Reset_List();
  789. PowerOnLights.Reset_List();
  790. PowerOffLights.Reset_List();
  791. Aggregates.Reset_List();
  792. }
  793. /////////////////////////////////////////////////////////////////////////////
  794. //
  795. // Add_Mesh
  796. //
  797. /////////////////////////////////////////////////////////////////////////////
  798. void
  799. BuildingGameObj::Add_Mesh (StaticPhysClass * terrain)
  800. {
  801. WWASSERT(terrain != NULL);
  802. if (Is_Interior_Mesh_Name(terrain->Peek_Model()->Get_Name())) {
  803. WWASSERT(terrain->Get_Observer() == NULL);
  804. InteriorMeshes.Add(terrain);
  805. terrain->Set_Observer(this);
  806. } else if (Is_Exterior_Mesh_Name(terrain->Peek_Model()->Get_Name())) {
  807. WWASSERT(terrain->Get_Observer() == NULL);
  808. ExteriorMeshes.Add(terrain);
  809. terrain->Set_Observer(this);
  810. }
  811. }
  812. /////////////////////////////////////////////////////////////////////////////
  813. //
  814. // Remove_Mesh
  815. //
  816. /////////////////////////////////////////////////////////////////////////////
  817. void
  818. BuildingGameObj::Remove_Mesh (StaticPhysClass * terrain)
  819. {
  820. WWASSERT(terrain != NULL);
  821. if (Is_Interior_Mesh_Name(terrain->Peek_Model()->Get_Name())) {
  822. WWASSERT(terrain->Get_Observer() == this);
  823. terrain->Set_Observer(NULL);
  824. InteriorMeshes.Remove(terrain);
  825. } else if (Is_Exterior_Mesh_Name(terrain->Peek_Model()->Get_Name())) {
  826. WWASSERT(terrain->Get_Observer() == this);
  827. terrain->Set_Observer(NULL);
  828. ExteriorMeshes.Remove(terrain);
  829. }
  830. }
  831. /////////////////////////////////////////////////////////////////////////////
  832. //
  833. // Add_Aggregate
  834. //
  835. /////////////////////////////////////////////////////////////////////////////
  836. void
  837. BuildingGameObj::Add_Aggregate (BuildingAggregateClass * aggregate)
  838. {
  839. WWASSERT(aggregate != NULL);
  840. Aggregates.Add(aggregate);
  841. WWASSERT(aggregate->Get_Observer() == NULL);
  842. aggregate->Set_Observer(this);
  843. }
  844. /////////////////////////////////////////////////////////////////////////////
  845. //
  846. // Remove_Aggregate
  847. //
  848. /////////////////////////////////////////////////////////////////////////////
  849. void
  850. BuildingGameObj::Remove_Aggregate (BuildingAggregateClass * aggregate)
  851. {
  852. WWASSERT(aggregate != NULL);
  853. WWASSERT(aggregate->Get_Observer() == this);
  854. aggregate->Set_Observer(NULL);
  855. Aggregates.Remove(aggregate);
  856. }
  857. /////////////////////////////////////////////////////////////////////////////
  858. //
  859. // Add_Light
  860. //
  861. /////////////////////////////////////////////////////////////////////////////
  862. void
  863. BuildingGameObj::Add_Light (LightPhysClass * light)
  864. {
  865. WWASSERT(light != NULL);
  866. if (light->Get_Group_ID() == 0) {
  867. PowerOnLights.Add(light);
  868. } else {
  869. PowerOffLights.Add(light);
  870. }
  871. }
  872. /////////////////////////////////////////////////////////////////////////////
  873. //
  874. // Enable_Alternate_Materials
  875. //
  876. /////////////////////////////////////////////////////////////////////////////
  877. void
  878. BuildingGameObj::Enable_Alternate_Materials (RefMultiListClass<StaticPhysClass> & models, bool onoff)
  879. {
  880. RefMultiListIterator<StaticPhysClass> it(&models);
  881. for (it.First(); !it.Is_Done(); it.Next()) {
  882. Enable_Alternate_Materials(it.Peek_Obj()->Peek_Model(),onoff);
  883. }
  884. }
  885. /////////////////////////////////////////////////////////////////////////////
  886. //
  887. // Enable_Alternate_Materials
  888. //
  889. /////////////////////////////////////////////////////////////////////////////
  890. void
  891. BuildingGameObj::Enable_Alternate_Materials (RenderObjClass * model,bool onoff)
  892. {
  893. if (model == NULL) return;
  894. for (int i=0; i<model->Get_Num_Sub_Objects(); i++) {
  895. RenderObjClass * subobj = model->Get_Sub_Object(i);
  896. Enable_Alternate_Materials(subobj,onoff);
  897. REF_PTR_RELEASE(subobj);
  898. }
  899. if (model->Class_ID() == RenderObjClass::CLASSID_MESH) {
  900. MeshModelClass * mesh_model = ((MeshClass *)model)->Get_Model();
  901. mesh_model->Enable_Alternate_Material_Description(onoff);
  902. REF_PTR_RELEASE(mesh_model);
  903. }
  904. }
  905. /////////////////////////////////////////////////////////////////////////////
  906. //
  907. // CnC_Initialize
  908. //
  909. /////////////////////////////////////////////////////////////////////////////
  910. void
  911. BuildingGameObj::CnC_Initialize (BaseControllerClass *base)
  912. {
  913. BaseController = base;
  914. //
  915. // Create an observer that we can use to monitor the building's health
  916. //
  917. BuildingMonitor = new BuildingMonitorClass;
  918. BuildingMonitor->Set_Building (this);
  919. Add_Observer (BuildingMonitor);
  920. return ;
  921. }
  922. /////////////////////////////////////////////////////////////////////////////
  923. //
  924. // On_Damaged
  925. //
  926. /////////////////////////////////////////////////////////////////////////////
  927. void
  928. BuildingGameObj::On_Damaged (void)
  929. {
  930. //
  931. // Notify the controller that the building has been damaged
  932. //
  933. if (BaseController != NULL) {
  934. BaseController->On_Building_Damaged (this);
  935. }
  936. return ;
  937. }
  938. /////////////////////////////////////////////////////////////////////////////
  939. //
  940. // On_Destroyed
  941. //
  942. /////////////////////////////////////////////////////////////////////////////
  943. void
  944. BuildingGameObj::On_Destroyed (void)
  945. {
  946. IsDestroyed = true;
  947. //
  948. // Notify the controller that the building has been destroyed
  949. //
  950. if (BaseController != NULL) {
  951. BaseController->On_Building_Destroyed (this);
  952. }
  953. //
  954. // Mark the object as dirty so it's state will be mirrored on the client
  955. //
  956. if (CombatManager::I_Am_Server ()) {
  957. Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  958. }
  959. //
  960. // Reveal this building to the encyclopedia
  961. //
  962. EncyclopediaMgrClass::Reveal_Object (this);
  963. return ;
  964. }
  965. /////////////////////////////////////////////////////////////////////////////
  966. //
  967. // Import_Rare
  968. //
  969. /////////////////////////////////////////////////////////////////////////////
  970. void
  971. BuildingGameObj::Import_Rare (BitStreamClass &packet)
  972. {
  973. DamageableGameObj::Import_Rare (packet);
  974. //
  975. // Read the state information from the server
  976. //
  977. bool is_destroyed = IsDestroyed;
  978. bool is_power_on = IsPowerOn;
  979. int current_state = CurrentState;
  980. packet.Get (is_destroyed);
  981. packet.Get (is_power_on);
  982. packet.Get (current_state, BITPACK_BUILDING_STATE);
  983. //
  984. // Is the power state of the building changing?
  985. //
  986. if (is_power_on != IsPowerOn) {
  987. Enable_Power (is_power_on);
  988. }
  989. //
  990. // Update our health if we are destroyed
  991. //
  992. if (is_destroyed) {
  993. Get_Defense_Object()->Set_Health(0.0f);
  994. }
  995. //
  996. // Recalculate our state (if necessary)
  997. //
  998. if (current_state != CurrentState) {
  999. Update_State ();
  1000. }
  1001. //
  1002. // Become destroyed (if necessary)
  1003. //
  1004. if (is_destroyed && IsDestroyed == false) {
  1005. On_Destroyed ();
  1006. }
  1007. return ;
  1008. }
  1009. /////////////////////////////////////////////////////////////////////////////
  1010. //
  1011. // Export_Rare
  1012. //
  1013. /////////////////////////////////////////////////////////////////////////////
  1014. void
  1015. BuildingGameObj::Export_Rare (BitStreamClass &packet)
  1016. {
  1017. DamageableGameObj::Export_Rare (packet);
  1018. //
  1019. // Transmit the state information
  1020. //
  1021. packet.Add (IsDestroyed);
  1022. packet.Add (IsPowerOn);
  1023. packet.Add (CurrentState, BITPACK_BUILDING_STATE);
  1024. return ;
  1025. }
  1026. /////////////////////////////////////////////////////////////////////////////
  1027. //
  1028. // Set_Precision
  1029. //
  1030. /////////////////////////////////////////////////////////////////////////////
  1031. void
  1032. BuildingGameObj::Set_Precision (void)
  1033. {
  1034. cEncoderList::Set_Precision (BITPACK_BUILDING_STATE, -1, (int)BuildingStateClass::STATE_COUNT);
  1035. return ;
  1036. }
  1037. /////////////////////////////////////////////////////////////////////////////
  1038. //
  1039. // Collect_Building_Components
  1040. //
  1041. /////////////////////////////////////////////////////////////////////////////
  1042. void
  1043. BuildingGameObj::Collect_Building_Components (void)
  1044. {
  1045. Reset_Components ();
  1046. //
  1047. // Iterate through all static objects in the level, assigning the building components to
  1048. // the appropriate building controller
  1049. //
  1050. RefPhysListIterator staticobj_iterator = PhysicsSceneClass::Get_Instance()->Get_Static_Object_Iterator();
  1051. for (staticobj_iterator.First(); !staticobj_iterator.Is_Done(); staticobj_iterator.Next()) {
  1052. StaticPhysClass * obj = staticobj_iterator.Peek_Obj()->As_StaticPhysClass ();
  1053. WWASSERT (obj != NULL);
  1054. WWASSERT (obj->Peek_Model() != NULL);
  1055. const char *obj_name = obj->Peek_Model()->Get_Name();
  1056. if (obj->Get_Factory().Chunk_ID() == PHYSICS_CHUNKID_BUILDINGAGGREGATE) {
  1057. //
  1058. // Does this aggregate match the prefix that this building is expecting?
  1059. //
  1060. if (Name_Prefix_Matches_This_Building (obj_name)) {
  1061. //
  1062. // Calculate how far the object is from the center of this building's
  1063. // collection sphere
  1064. //
  1065. Vector3 obj_pos = obj->Peek_Model()->Get_Bounding_Box().Center;
  1066. float dist2 = (CollectionSphere.Center - obj_pos).Length2 ();
  1067. //
  1068. // Is this object within the collection sphere?
  1069. //
  1070. if (dist2 <= (CollectionSphere.Radius * CollectionSphere.Radius)) {
  1071. // Check for an existing building owner
  1072. CombatPhysObserverClass * old_observer = (CombatPhysObserverClass *)obj->Get_Observer();
  1073. BuildingGameObj * existing_building = NULL;
  1074. if ( old_observer != NULL ) {
  1075. existing_building = old_observer->As_BuildingGameObj();
  1076. }
  1077. if ( existing_building != NULL ) {
  1078. // Another building already has this
  1079. float existing_dist2 = (existing_building->CollectionSphere.Center - obj_pos).Length2 ();
  1080. if ( dist2 < existing_dist2 ) {
  1081. // But we are closer, so remove from the existing
  1082. existing_building->Remove_Aggregate ((BuildingAggregateClass *)obj);
  1083. // and Take him
  1084. Add_Aggregate ((BuildingAggregateClass *)obj);
  1085. }
  1086. } else {
  1087. // We are the first/only
  1088. Add_Aggregate ((BuildingAggregateClass *)obj);
  1089. }
  1090. }
  1091. }
  1092. } else if (Is_Interior_Mesh_Name (obj_name) || Is_Exterior_Mesh_Name (obj_name)) {
  1093. //
  1094. // Does this mesh match the prefix that this building is expecting?
  1095. //
  1096. if (Name_Prefix_Matches_This_Building (obj_name)) {
  1097. //
  1098. // Calculate how far the object is from the center of this building's
  1099. // collection sphere
  1100. //
  1101. Vector3 obj_pos = obj->Peek_Model()->Get_Bounding_Box().Center;
  1102. float dist2 = (CollectionSphere.Center - obj_pos).Length2 ();
  1103. //
  1104. // Is this object within the collection sphere?
  1105. //
  1106. if (dist2 <= (CollectionSphere.Radius * CollectionSphere.Radius)) {
  1107. // Check for an existing building owner
  1108. CombatPhysObserverClass * old_observer = (CombatPhysObserverClass *)obj->Get_Observer();
  1109. BuildingGameObj * existing_building = NULL;
  1110. if ( old_observer != NULL ) {
  1111. existing_building = old_observer->As_BuildingGameObj();
  1112. }
  1113. if ( existing_building != NULL ) {
  1114. // Another building already has this
  1115. float existing_dist2 = (existing_building->CollectionSphere.Center - obj_pos).Length2 ();
  1116. if ( dist2 < existing_dist2 ) {
  1117. // But we are closer, so remove from the existing
  1118. existing_building->Remove_Mesh (obj);
  1119. // and Take him
  1120. Add_Mesh (obj);
  1121. }
  1122. } else {
  1123. // We are the first/only
  1124. Add_Mesh (obj);
  1125. }
  1126. }
  1127. }
  1128. }
  1129. }
  1130. //
  1131. // Iterate through all static lights in the level, assigning each building light to
  1132. // the appropriate building controller
  1133. //
  1134. RefPhysListIterator staticlight_iterator = PhysicsSceneClass::Get_Instance ()->Get_Static_Light_Iterator ();
  1135. for (staticlight_iterator.First (); !staticlight_iterator.Is_Done (); staticlight_iterator.Next ()) {
  1136. LightPhysClass * light = staticlight_iterator.Peek_Obj()->As_LightPhysClass();
  1137. WWASSERT(light != NULL);
  1138. WWASSERT(light->Peek_Model() != NULL);
  1139. //
  1140. // Does this light match the prefix that this building is expecting?
  1141. //
  1142. if (Name_Prefix_Matches_This_Building (light->Get_Name ())) {
  1143. //
  1144. // Calculate how far the light is from the center of this building's
  1145. // collection sphere
  1146. //
  1147. Vector3 light_pos;
  1148. light->Get_Position (&light_pos);
  1149. float dist2 = (CollectionSphere.Center - light_pos).Length2 ();
  1150. //
  1151. // Is this light within the collection sphere?
  1152. //
  1153. if (dist2 <= (CollectionSphere.Radius * CollectionSphere.Radius)) {
  1154. Add_Light (light);
  1155. }
  1156. }
  1157. }
  1158. //
  1159. // Update the building's state
  1160. //
  1161. Initialize_Building ();
  1162. Update_State (true);
  1163. return ;
  1164. }
  1165. /////////////////////////////////////////////////////////////////////////////
  1166. //
  1167. // Export_Creation
  1168. //
  1169. /////////////////////////////////////////////////////////////////////////////
  1170. void
  1171. BuildingGameObj::Export_Creation (BitStreamClass &packet)
  1172. {
  1173. DamageableGameObj::Export_Creation (packet);
  1174. //
  1175. // Send the object's position
  1176. //
  1177. Vector3 position (0, 0, 0);
  1178. Get_Position (&position);
  1179. packet.Add (position.X, BITPACK_WORLD_POSITION_X);
  1180. packet.Add (position.Y, BITPACK_WORLD_POSITION_Y);
  1181. packet.Add (position.Z, BITPACK_WORLD_POSITION_Z);
  1182. //
  1183. // Send the collection sphere's definition
  1184. //
  1185. packet.Add (CollectionSphere.Center.X, BITPACK_WORLD_POSITION_X);
  1186. packet.Add (CollectionSphere.Center.Y, BITPACK_WORLD_POSITION_Y);
  1187. packet.Add (CollectionSphere.Center.Z, BITPACK_WORLD_POSITION_Z);
  1188. packet.Add (CollectionSphere.Radius, BITPACK_BUILDING_RADIUS);
  1189. return ;
  1190. }
  1191. /////////////////////////////////////////////////////////////////////////////
  1192. //
  1193. // Import_Creation
  1194. //
  1195. /////////////////////////////////////////////////////////////////////////////
  1196. void
  1197. BuildingGameObj::Import_Creation (BitStreamClass &packet)
  1198. {
  1199. DamageableGameObj::Import_Creation (packet);
  1200. //
  1201. // Read the object's position
  1202. //
  1203. Vector3 position (0, 0, 0);
  1204. packet.Get (position.X, BITPACK_WORLD_POSITION_X);
  1205. packet.Get (position.Y, BITPACK_WORLD_POSITION_Y);
  1206. packet.Get (position.Z, BITPACK_WORLD_POSITION_Z);
  1207. Set_Position (position);
  1208. //
  1209. // Read the collection sphere's definition
  1210. //
  1211. packet.Get (CollectionSphere.Center.X, BITPACK_WORLD_POSITION_X);
  1212. packet.Get (CollectionSphere.Center.Y, BITPACK_WORLD_POSITION_Y);
  1213. packet.Get (CollectionSphere.Center.Z, BITPACK_WORLD_POSITION_Z);
  1214. packet.Get (CollectionSphere.Radius, BITPACK_BUILDING_RADIUS);
  1215. //
  1216. // Now initialize the object
  1217. //
  1218. Collect_Building_Components ();
  1219. Update_State ();
  1220. Initialize_Building ();
  1221. return ;
  1222. }
  1223. /*
  1224. ////////////////////////////////////////////////////////////////////////////
  1225. //
  1226. // Compute_Object_Priority
  1227. //
  1228. /////////////////////////////////////////////////////////////////////////////
  1229. float
  1230. BuildingGameObj::Compute_Object_Priority (int client_id, const Vector3 &client_pos)
  1231. {
  1232. float priority = 1.0F;
  1233. if (Get_Object_Dirty_Bit (client_id, BIT_RARE) == false) {
  1234. priority = DamageableGameObj::Compute_Object_Priority (client_id, client_pos);
  1235. }
  1236. //
  1237. // Priority value is cached, update the cached value.
  1238. //
  1239. Set_Cached_Priority(priority);
  1240. return priority;
  1241. }
  1242. */
  1243. ////////////////////////////////////////////////////////////////////////////
  1244. //
  1245. // Get_Description
  1246. //
  1247. /////////////////////////////////////////////////////////////////////////////
  1248. void
  1249. //BuildingGameObj::Get_Extended_Information(StringClass & description)
  1250. BuildingGameObj::Get_Description(StringClass & description)
  1251. {
  1252. //
  1253. // Construct a diagnostic string
  1254. //
  1255. StringClass line(0,true);
  1256. line.Format("ID: %d\n", Get_ID());
  1257. description += line;
  1258. line.Format("Name: %s\n", Get_Definition().Get_Name());
  1259. description += line;
  1260. line.Format("Team: %d\n", Get_Player_Type());
  1261. description += line;
  1262. Vector3 position;
  1263. Get_Position(&position);
  1264. line.Format("POS: %-5.2f, %-5.2f, %-5.2f\n", position.X, position.Y, position.Z);
  1265. description += line;
  1266. if (Get_Defense_Object() != NULL) {
  1267. line.Format("HLTH: %-5.2f\n", Get_Defense_Object()->Get_Health());
  1268. description += line;
  1269. }
  1270. line.Format("HIB: %d\n", Is_Hibernating());
  1271. description += line;
  1272. line.Format("Destr: %d\n", IsDestroyed);
  1273. description += line;
  1274. line.Format("Power: %d\n", IsPowerOn);
  1275. description += line;
  1276. line.Format("ISC: %d\n", Get_Import_State_Count());
  1277. description += line;
  1278. }
  1279. ////////////////////////////////////////////////////////////////////////////
  1280. //
  1281. // Find_Closest_Poly_For_Model
  1282. //
  1283. /////////////////////////////////////////////////////////////////////////////
  1284. void
  1285. BuildingGameObj::Find_Closest_Poly_For_Model
  1286. (
  1287. RenderObjClass * model,
  1288. const Vector3 & pos,
  1289. float * distance2
  1290. )
  1291. {
  1292. if (model == NULL) {
  1293. return ;
  1294. }
  1295. //
  1296. // Convert the point from world space to object space
  1297. //
  1298. Vector3 obj_space_point;
  1299. Matrix3D::Inverse_Transform_Vector (model->Get_Transform (), pos, &obj_space_point);
  1300. for (int index = 0; index < model->Get_Num_Sub_Objects (); index++) {
  1301. RenderObjClass *sub_obj = model->Get_Sub_Object (index);
  1302. //
  1303. // Recurse into this sub-object
  1304. //
  1305. if (sub_obj != NULL) {
  1306. Find_Closest_Poly_For_Model (sub_obj, pos, distance2);
  1307. }
  1308. REF_PTR_RELEASE (sub_obj);
  1309. }
  1310. //
  1311. // Is this object a mesh? If so, then do a distance check against all its polygons
  1312. //
  1313. if (model->Class_ID () == RenderObjClass::CLASSID_MESH) {
  1314. MeshModelClass *mesh_model = ((MeshClass *)model)->Peek_Model ();
  1315. if (mesh_model != NULL) {
  1316. const TriIndex *tri_array = mesh_model->Get_Polygon_Array ();
  1317. Vector3 *vert_array = mesh_model->Get_Vertex_Array ();
  1318. //
  1319. // Check each polygon to see which is the closest
  1320. //
  1321. int poly_count = mesh_model->Get_Polygon_Count ();
  1322. for (index = 0; index < poly_count; index ++) {
  1323. int vert1 = tri_array[index][0];
  1324. int vert2 = tri_array[index][1];
  1325. int vert3 = tri_array[index][2];
  1326. //
  1327. // Compute the distance from the center of this polygon to the
  1328. //
  1329. //
  1330. Vector3 poly_center = (vert_array[vert1] + vert_array[vert2] + vert_array[vert3]) * 0.33F;
  1331. Vector3 delta = (poly_center - obj_space_point);
  1332. float dist2 = delta.Length2 ();
  1333. if (dist2 < (*distance2)) {
  1334. (*distance2) = dist2;
  1335. }
  1336. }
  1337. }
  1338. }
  1339. return ;
  1340. }
  1341. ////////////////////////////////////////////////////////////////////////////
  1342. //
  1343. // Find_Closest_Poly
  1344. //
  1345. /////////////////////////////////////////////////////////////////////////////
  1346. void
  1347. BuildingGameObj::Find_Closest_Poly (const Vector3 &pos, float *distance2)
  1348. {
  1349. (*distance2) = 9999.0F;
  1350. RefMultiListIterator<StaticPhysClass> int_iterator (&InteriorMeshes);
  1351. RefMultiListIterator<StaticPhysClass> ext_iterator (&ExteriorMeshes);
  1352. //
  1353. // Check all the interior meshes first
  1354. //
  1355. for (int_iterator.First (); !int_iterator.Is_Done (); int_iterator.Next ()) {
  1356. Find_Closest_Poly_For_Model (int_iterator.Peek_Obj ()->Peek_Model (), pos, distance2);
  1357. }
  1358. //
  1359. // Now, check all the exterior meshes
  1360. //
  1361. for (ext_iterator.First (); !ext_iterator.Is_Done (); ext_iterator.Next ()) {
  1362. Find_Closest_Poly_For_Model (ext_iterator.Peek_Obj ()->Peek_Model (), pos, distance2);
  1363. }
  1364. return ;
  1365. }