scriptablegameobj.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  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/scriptablegameobj.cpp $*
  25. * *
  26. * $Author:: Byon_g $*
  27. * *
  28. * $Modtime:: 11/12/01 4:20p $*
  29. * *
  30. * $Revision:: 37 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "scriptablegameobj.h"
  36. #include "damage.h"
  37. #include "scripts.h"
  38. #include "debug.h"
  39. #include "explosion.h"
  40. #include "assets.h"
  41. #include "combatsound.h"
  42. #include "matrix3d.h"
  43. #include "phys.h"
  44. #include "smartgameobj.h"
  45. #include "soldier.h"
  46. #include "animcontrol.h"
  47. #include "chunkio.h"
  48. #include "saveload.h"
  49. #include "combat.h"
  50. #include "persistfactory.h"
  51. #include "combatchunkid.h"
  52. #include "parameter.h"
  53. #include "radar.h"
  54. #include "playertype.h"
  55. #include "matinfo.h"
  56. #include "gameobjmanager.h"
  57. #include "pscene.h"
  58. #include "soundsceneobj.h"
  59. #include "wwprofile.h"
  60. /*
  61. ** ScriptableGameObjDef - Defintion class for a ScriptableGameObj
  62. */
  63. ScriptableGameObjDef::ScriptableGameObjDef( void )
  64. {
  65. SCRIPTLIST_PARAM (ScriptableGameObjDef, "Scripts", ScriptNameList, ScriptParameterList);
  66. }
  67. enum {
  68. CHUNKID_DEF_PARENT = 627001056,
  69. CHUNKID_DEF_VARIABLES,
  70. XXX_MICROCHUNKID_DEF_TYPE = 1,
  71. MICROCHUNKID_DEF_SCRIPT_NAME,
  72. MICROCHUNKID_DEF_SCRIPT_PARAMETERS,
  73. };
  74. bool ScriptableGameObjDef::Save( ChunkSaveClass & csave )
  75. {
  76. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  77. BaseGameObjDef::Save( csave );
  78. csave.End_Chunk();
  79. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  80. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  81. for ( int i = 0; i < ScriptNameList.Count(); i++ ) {
  82. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_SCRIPT_NAME, ScriptNameList[i] );
  83. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_SCRIPT_PARAMETERS, ScriptParameterList[i] );
  84. }
  85. csave.End_Chunk();
  86. return true;
  87. }
  88. bool ScriptableGameObjDef::Load( ChunkLoadClass &cload )
  89. {
  90. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  91. StringClass str;
  92. while (cload.Open_Chunk()) {
  93. switch(cload.Cur_Chunk_ID()) {
  94. case CHUNKID_DEF_PARENT:
  95. BaseGameObjDef::Load( cload );
  96. break;
  97. case CHUNKID_DEF_VARIABLES:
  98. while (cload.Open_Micro_Chunk()) {
  99. switch(cload.Cur_Micro_Chunk_ID()) {
  100. case MICROCHUNKID_DEF_SCRIPT_NAME:
  101. LOAD_MICRO_CHUNK_WWSTRING( cload, str );
  102. ScriptNameList.Add( str );
  103. break;
  104. case MICROCHUNKID_DEF_SCRIPT_PARAMETERS:
  105. LOAD_MICRO_CHUNK_WWSTRING( cload, str );
  106. ScriptParameterList.Add( str );
  107. break;
  108. default:
  109. Debug_Say(( "Unhandled Variable Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  110. break;
  111. }
  112. cload.Close_Micro_Chunk();
  113. }
  114. break;
  115. default:
  116. Debug_Say(( "Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  117. break;
  118. }
  119. cload.Close_Chunk();
  120. }
  121. WWASSERT( ScriptNameList.Count() == ScriptParameterList.Count() );
  122. return true;
  123. }
  124. /*
  125. ** Game Object Observer Timer (used in Scripts)
  126. */
  127. class GameObjObserverTimerClass {
  128. public:
  129. GameObjObserverTimerClass( int observer_id = 0, float time = 0, int timer_id = 0 )
  130. { ObserverID = observer_id; RemainingTime = time; TimerID = timer_id; }
  131. bool Save( ChunkSaveClass & csave );
  132. bool Load( ChunkLoadClass & cload );
  133. bool Update( void ) { RemainingTime -= TimeManager::Get_Frame_Seconds(); return RemainingTime <= 0; }
  134. bool Expired( void ) { return RemainingTime <= 0; }
  135. int ObserverID;
  136. float RemainingTime;
  137. int TimerID;
  138. };
  139. enum {
  140. CHUNKID_TIMER_VARIABLES = 922991755,
  141. CHUNKID_TIMER_SENDER,
  142. MICROCHUNKID_REMAINING_TIME = 1,
  143. MICROCHUNKID_TIMER_ID,
  144. MICROCHUNKID_OBSERVER_ID,
  145. MICROCHUNKID_TYPE,
  146. MICROCHUNKID_PARAM,
  147. };
  148. bool GameObjObserverTimerClass::Save( ChunkSaveClass & csave )
  149. {
  150. csave.Begin_Chunk( CHUNKID_TIMER_VARIABLES );
  151. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_REMAINING_TIME, RemainingTime );
  152. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TIMER_ID, TimerID );
  153. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OBSERVER_ID, ObserverID );
  154. csave.End_Chunk();
  155. return true;
  156. }
  157. bool GameObjObserverTimerClass::Load( ChunkLoadClass & cload )
  158. {
  159. cload.Open_Chunk();
  160. WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_TIMER_VARIABLES );
  161. while (cload.Open_Micro_Chunk()) {
  162. switch(cload.Cur_Micro_Chunk_ID()) {
  163. READ_MICRO_CHUNK( cload, MICROCHUNKID_REMAINING_TIME, RemainingTime );
  164. READ_MICRO_CHUNK( cload, MICROCHUNKID_TIMER_ID, TimerID );
  165. READ_MICRO_CHUNK( cload, MICROCHUNKID_OBSERVER_ID, ObserverID );
  166. default:
  167. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  168. break;
  169. }
  170. cload.Close_Micro_Chunk();
  171. }
  172. cload.Close_Chunk();
  173. return true;
  174. }
  175. /*
  176. ** Game Object Custom Timer (used in Scripts)
  177. */
  178. class GameObjCustomTimerClass {
  179. public:
  180. GameObjCustomTimerClass( ScriptableGameObj *sender = NULL, float time = 0, int type = 0, int param = 0 ) :
  181. RemainingTime( time ), Type( type ), Param( param) { if ( sender != NULL ) Sender = sender; }
  182. bool Save( ChunkSaveClass & csave );
  183. bool Load( ChunkLoadClass & cload );
  184. bool Update( void ) { RemainingTime -= TimeManager::Get_Frame_Seconds(); return RemainingTime <= 0; }
  185. bool Expired( void ) { return RemainingTime <= 0; }
  186. float RemainingTime;
  187. GameObjReference Sender;
  188. int Type;
  189. int Param;
  190. };
  191. bool GameObjCustomTimerClass::Save( ChunkSaveClass & csave )
  192. {
  193. csave.Begin_Chunk( CHUNKID_TIMER_VARIABLES );
  194. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_REMAINING_TIME, RemainingTime );
  195. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TYPE, Type );
  196. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PARAM, Param );
  197. csave.End_Chunk();
  198. if ( Sender != NULL ) {
  199. csave.Begin_Chunk( CHUNKID_TIMER_SENDER );
  200. Sender.Save( csave );
  201. csave.End_Chunk();
  202. }
  203. return true;
  204. }
  205. bool GameObjCustomTimerClass::Load( ChunkLoadClass & cload )
  206. {
  207. while (cload.Open_Chunk()) {
  208. switch(cload.Cur_Chunk_ID()) {
  209. case CHUNKID_TIMER_VARIABLES:
  210. while (cload.Open_Micro_Chunk()) {
  211. switch(cload.Cur_Micro_Chunk_ID()) {
  212. READ_MICRO_CHUNK( cload, MICROCHUNKID_REMAINING_TIME, RemainingTime );
  213. READ_MICRO_CHUNK( cload, MICROCHUNKID_TYPE, Type );
  214. READ_MICRO_CHUNK( cload, MICROCHUNKID_PARAM, Param );
  215. default:
  216. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  217. break;
  218. }
  219. cload.Close_Micro_Chunk();
  220. }
  221. break;
  222. case CHUNKID_TIMER_SENDER:
  223. Sender.Load( cload );
  224. break;
  225. default:
  226. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  227. break;
  228. }
  229. cload.Close_Chunk();
  230. }
  231. return true;
  232. }
  233. /*
  234. ** ScriptableGameObj
  235. */
  236. ScriptableGameObj::ScriptableGameObj( void ) :
  237. ReferenceableGameObj( this ),
  238. ObserverCreatedPending( false )
  239. {
  240. }
  241. ScriptableGameObj::~ScriptableGameObj( void )
  242. {
  243. Remove_All_Observers();
  244. /*
  245. ** Delete the ObserverTimerList. ST - 6/11/2001 9:20PM
  246. */
  247. while (ObserverTimerList.Count()) {
  248. delete ObserverTimerList[0];
  249. ObserverTimerList.Delete(0);
  250. }
  251. /*
  252. ** Delete the CustomTimerList. ST - 6/11/2001 9:20PM
  253. */
  254. while (CustomTimerList.Count()) {
  255. delete CustomTimerList[0];
  256. CustomTimerList.Delete(0);
  257. }
  258. }
  259. /*
  260. **
  261. */
  262. void ScriptableGameObj::Init( const ScriptableGameObjDef & definition )
  263. {
  264. BaseGameObj::Init( definition );
  265. Copy_Settings( definition );
  266. return ;
  267. }
  268. /*
  269. **
  270. */
  271. void ScriptableGameObj::Copy_Settings( const ScriptableGameObjDef & definition )
  272. {
  273. //
  274. // Only assign scripts on the server
  275. //
  276. if (CombatManager::I_Am_Server()) {
  277. //
  278. // Attach the scripts
  279. //
  280. WWASSERT( definition.ScriptNameList.Count() == definition.ScriptParameterList.Count() );
  281. for ( int i = 0; i < definition.ScriptNameList.Count(); i++ ) {
  282. ScriptClass* script = ScriptManager::Create_Script( definition.ScriptNameList[i] );
  283. if (script) {
  284. script->Set_Parameters_String( definition.ScriptParameterList[i] );
  285. // Don't call Add observer, because we don't want Created called yet.
  286. // Start_Observers should be called later, which will call Created
  287. Insert_Observer(script);
  288. }
  289. }
  290. }
  291. return ;
  292. }
  293. /*
  294. **
  295. */
  296. void ScriptableGameObj::Re_Init( const ScriptableGameObjDef & definition )
  297. {
  298. //
  299. // Remove all currently running scripts
  300. //
  301. Remove_All_Observers();
  302. //
  303. // Copy any internal settings from the definition
  304. //
  305. Copy_Settings( definition );
  306. //
  307. // Reset our definition pointer
  308. //
  309. BaseGameObj::Init( definition );
  310. return ;
  311. }
  312. /*
  313. **
  314. */
  315. void ScriptableGameObj::Post_Re_Init( void )
  316. {
  317. //
  318. // Start the new scripts executing
  319. //
  320. Start_Observers();
  321. return ;
  322. }
  323. const ScriptableGameObjDef & ScriptableGameObj::Get_Definition( void ) const
  324. {
  325. return (const ScriptableGameObjDef &)BaseGameObj::Get_Definition();
  326. }
  327. void ScriptableGameObj::Set_Delete_Pending( void )
  328. {
  329. if ( !Is_Delete_Pending() ) {
  330. if ( CombatManager::Are_Observers_Active() ) {
  331. const GameObjObserverList & observer_list = Get_Observers();
  332. for( int index = 0; index < observer_list.Count(); index++ ) {
  333. observer_list[ index ]->Destroyed( this );
  334. }
  335. }
  336. if ( this == COMBAT_STAR ) {
  337. CombatManager::Star_Killed();
  338. }
  339. BaseGameObj::Set_Delete_Pending ();
  340. }
  341. }
  342. /*
  343. ** ScriptableGameObj Save and Load
  344. */
  345. enum {
  346. CHUNKID_PARENT = 627001122,
  347. CHUNKID_VARIABLES,
  348. CHUNKID_REFERENCEABLE,
  349. CHUNKID_CUSTOM_TIMER,
  350. CHUNKID_OBSERVER_TIMER,
  351. MICROCHUNKID_REFERENCEABLE_PTR = 1,
  352. MICROCHUNKID_GAME_OBJ_OBSERVER_PTR,
  353. MICROCHUNKID_OBSERVER_CREATED_PENDING,
  354. };
  355. bool ScriptableGameObj::Save( ChunkSaveClass & csave )
  356. {
  357. csave.Begin_Chunk( CHUNKID_PARENT );
  358. BaseGameObj::Save( csave );
  359. csave.End_Chunk();
  360. csave.Begin_Chunk( CHUNKID_REFERENCEABLE );
  361. ReferenceableGameObj::Save( csave );
  362. csave.End_Chunk();
  363. csave.Begin_Chunk( CHUNKID_VARIABLES );
  364. ReferenceableGameObj * referenceable_ptr = (ReferenceableGameObj *)this;
  365. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_REFERENCEABLE_PTR, referenceable_ptr );
  366. const GameObjObserverList & observer_list = Get_Observers();
  367. for( int index = 0; index < observer_list.Count(); index++ ) {
  368. void * game_obj_observer_ptr = observer_list[ index ];
  369. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_GAME_OBJ_OBSERVER_PTR, game_obj_observer_ptr );
  370. }
  371. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OBSERVER_CREATED_PENDING, ObserverCreatedPending );
  372. csave.End_Chunk();
  373. int i;
  374. for ( i = 0; i < ObserverTimerList.Count(); i++ ) {
  375. csave.Begin_Chunk( CHUNKID_OBSERVER_TIMER );
  376. ObserverTimerList[i]->Save( csave );
  377. csave.End_Chunk();
  378. }
  379. for ( i = 0; i < CustomTimerList.Count(); i++ ) {
  380. csave.Begin_Chunk( CHUNKID_CUSTOM_TIMER );
  381. CustomTimerList[i]->Save( csave );
  382. csave.End_Chunk();
  383. }
  384. return true;
  385. }
  386. bool ScriptableGameObj::Load( ChunkLoadClass &cload )
  387. {
  388. ReferenceableGameObj * referenceable_ptr = NULL;
  389. WWASSERT( Observers.Count() == 0 );
  390. while (cload.Open_Chunk()) {
  391. switch(cload.Cur_Chunk_ID()) {
  392. case CHUNKID_PARENT:
  393. BaseGameObj::Load( cload );
  394. break;
  395. case CHUNKID_REFERENCEABLE:
  396. ReferenceableGameObj::Load( cload );
  397. break;
  398. case CHUNKID_VARIABLES:
  399. while (cload.Open_Micro_Chunk()) {
  400. switch(cload.Cur_Micro_Chunk_ID()) {
  401. READ_MICRO_CHUNK( cload, MICROCHUNKID_REFERENCEABLE_PTR, referenceable_ptr );
  402. READ_MICRO_CHUNK( cload, MICROCHUNKID_OBSERVER_CREATED_PENDING, ObserverCreatedPending );
  403. case MICROCHUNKID_GAME_OBJ_OBSERVER_PTR:
  404. GameObjObserverClass * ptr;
  405. cload.Read(&ptr,sizeof(ptr));
  406. Observers.Add( ptr );
  407. break;
  408. default:
  409. Debug_Say(( "Unhandled Variable Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  410. break;
  411. }
  412. cload.Close_Micro_Chunk();
  413. }
  414. break;
  415. case CHUNKID_OBSERVER_TIMER:
  416. GameObjObserverTimerClass * otimer;
  417. otimer = new GameObjObserverTimerClass();
  418. otimer->Load( cload );
  419. ObserverTimerList.Add( otimer );
  420. break;
  421. case CHUNKID_CUSTOM_TIMER:
  422. GameObjCustomTimerClass * ctimer;
  423. ctimer = new GameObjCustomTimerClass();
  424. ctimer->Load( cload );
  425. CustomTimerList.Add( ctimer );
  426. break;
  427. default:
  428. Debug_Say(( "Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  429. break;
  430. }
  431. cload.Close_Chunk();
  432. }
  433. // Request a remap on all the observers
  434. for ( int ob_idx = 0; ob_idx < Observers.Count(); ob_idx++ ) {
  435. REQUEST_POINTER_REMAP( (void **)&(Observers[ ob_idx ]) );
  436. }
  437. WWASSERT(referenceable_ptr != NULL);
  438. if (referenceable_ptr != NULL) {
  439. SaveLoadSystemClass::Register_Pointer(referenceable_ptr , (ReferenceableGameObj *)this);
  440. }
  441. SaveLoadSystemClass::Register_Post_Load_Callback(this);
  442. return true;
  443. }
  444. void ScriptableGameObj::On_Post_Load( void )
  445. {
  446. BaseGameObj::On_Post_Load();
  447. // Delete any NULL pointers
  448. GameObjObserverList & observer_list = (GameObjObserverList &)Get_Observers();
  449. for( int index = 0; index < observer_list.Count(); index++ ) {
  450. if ( observer_list[ index ] == NULL ) {
  451. observer_list.Delete( index );
  452. index--;
  453. }
  454. }
  455. if ( CombatManager::Is_First_Load() ) {
  456. ObserverCreatedPending = true;
  457. }
  458. }
  459. /*
  460. **
  461. */
  462. void ScriptableGameObj::Start_Observers( void )
  463. {
  464. // If we just came from the editor, call created on all out observers
  465. const GameObjObserverList & observer_list = Get_Observers();
  466. for( int index = 0; index < observer_list.Count(); index++ ) {
  467. observer_list[ index ]->Created( this );
  468. }
  469. }
  470. void ScriptableGameObj::Add_Observer( GameObjObserverClass * observer )
  471. {
  472. WWASSERT(observer != NULL);
  473. Insert_Observer( observer );
  474. // Don't call created if in the editor
  475. if ( CombatManager::Are_Observers_Active() ) {
  476. observer->Created( this );
  477. }
  478. }
  479. void ScriptableGameObj::Insert_Observer( GameObjObserverClass * observer )
  480. {
  481. WWASSERT(observer != NULL);
  482. observer->Attach( this );
  483. Observers.Add( observer );
  484. }
  485. void ScriptableGameObj::Remove_Observer( GameObjObserverClass * observer )
  486. {
  487. Observers.Delete( observer );
  488. observer->Detach( this );
  489. // if observer is a script, if will be deleted soon after this
  490. }
  491. void ScriptableGameObj::Remove_All_Observers(void)
  492. {
  493. while(Observers.Count() != 0) {
  494. Remove_Observer(Observers[0]);
  495. }
  496. }
  497. void ScriptableGameObj::Start_Observer_Timer( int observer_id, float duration, int timer_id )
  498. {
  499. ObserverTimerList.Add( new GameObjObserverTimerClass( observer_id, duration, timer_id ) );
  500. }
  501. void ScriptableGameObj::Start_Custom_Timer( ScriptableGameObj * from, float delay, int type, int param )
  502. {
  503. CustomTimerList.Add( new GameObjCustomTimerClass( from, delay, type, param ) );
  504. }
  505. void ScriptableGameObj::Think( void )
  506. {
  507. if (Is_Always_Dirty()) {
  508. #pragma message ("Forcing game objects to be network dirty for updates.\n")
  509. Set_Object_Dirty_Bit (NetworkObjectClass::BIT_FREQUENT, true);
  510. }
  511. if ( ObserverCreatedPending ) {
  512. Start_Observers();
  513. ObserverCreatedPending = false;
  514. }
  515. BaseGameObj::Think();
  516. }
  517. void ScriptableGameObj::Post_Think( void )
  518. {
  519. BaseGameObj::Post_Think();
  520. WWPROFILE( "Scriptable PostThink" );
  521. // Note: The cinematic script comes later in the obj list then the things it creates.
  522. // This means that objects the script creates when it thinks (via timers) dont think
  523. // (bump animation forward) until the next frame. Be wary of changing this order.
  524. // Check Timers
  525. int i;
  526. for ( i = ObserverTimerList.Count() - 1; i >= 0; i-- ) {
  527. if ( ObserverTimerList[i]->Update() ) {
  528. // Debug_Say(( "Timer Expired for %d\n", ObserverTimerList[i]->ObserverID ));
  529. bool found = false;
  530. WWASSERT( ObserverTimerList[i]->ObserverID != 0 );
  531. const GameObjObserverList & observer_list = Get_Observers();
  532. for( int index = 0; index < observer_list.Count(); index++ ) {
  533. if ( observer_list[ index ]->Get_ID() == ObserverTimerList[i]->ObserverID ) {
  534. observer_list[ index ]->Timer_Expired( this, ObserverTimerList[i]->TimerID );
  535. found = true;
  536. }
  537. }
  538. if ( !found ) {
  539. Debug_Say(( "Failed to find observer id %d for timer expired....\n", ObserverTimerList[i]->ObserverID ));
  540. const GameObjObserverList & observer_list = Get_Observers();
  541. for( int index = 0; index < observer_list.Count(); index++ ) {
  542. Debug_Say(( "have %d\n", observer_list[ index ]->Get_ID() ));
  543. }
  544. }
  545. delete ObserverTimerList[i];
  546. ObserverTimerList.Delete( i );
  547. }
  548. }
  549. for ( i = CustomTimerList.Count() - 1; i >= 0; i-- ) {
  550. if ( CustomTimerList[i]->Update() ) {
  551. ScriptableGameObj *sender = CustomTimerList[i]->Sender;
  552. const GameObjObserverList & observer_list = Get_Observers();
  553. for( int index = 0; index < observer_list.Count(); index++ ) {
  554. observer_list[ index ]->Custom( this, CustomTimerList[i]->Type, CustomTimerList[i]->Param, sender );
  555. }
  556. delete CustomTimerList[i];
  557. CustomTimerList.Delete( i );
  558. }
  559. }
  560. }
  561. //------------------------------------------------------------------------------------
  562. void ScriptableGameObj::Get_Information( StringClass & string )
  563. {
  564. // If we just came from the editor, call created on all out observers
  565. const GameObjObserverList & observer_list = Get_Observers();
  566. for( int index = 0; index < observer_list.Count(); index++ ) {
  567. StringClass temp;
  568. temp.Format( "%d:%s\n", observer_list[ index ]->Get_ID(), observer_list[ index ]->Get_Name() );
  569. string += temp;
  570. }
  571. }
  572. //------------------------------------------------------------------------------------
  573. void ScriptableGameObj::On_Sound_Ended( SoundSceneObjClass *sound_obj )
  574. {
  575. if ( sound_obj == NULL ) {
  576. return ;
  577. }
  578. int sound_id = sound_obj->Get_ID();
  579. //
  580. // Notify all observers
  581. //
  582. const GameObjObserverList & observer_list = Get_Observers();
  583. for( int index = 0; index < observer_list.Count(); index++ ) {
  584. //
  585. // Send a custom event to this observer notifying it that this sound has ended
  586. //
  587. observer_list[ index ]->Custom( this, CUSTOM_EVENT_SOUND_ENDED, sound_id, NULL );
  588. }
  589. return ;
  590. }
  591. /*
  592. **
  593. */
  594. void ScriptableGameObj::Export_Creation( BitStreamClass &packet )
  595. {
  596. BaseGameObj::Export_Creation( packet );
  597. return ;
  598. }
  599. /*
  600. **
  601. */
  602. void ScriptableGameObj::Import_Creation( BitStreamClass &packet )
  603. {
  604. BaseGameObj::Import_Creation( packet );
  605. //
  606. // Ensure we don't have any scripts running
  607. //
  608. Remove_All_Observers();
  609. return ;
  610. }