projectile.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWPhys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/projectile.cpp $*
  25. * *
  26. * Author:: Greg_h *
  27. * *
  28. * $Modtime:: 12/20/01 5:14p $*
  29. * *
  30. * $Revision:: 60 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "projectile.h"
  36. #include "pscene.h"
  37. #include "lineseg.h"
  38. #include "physcon.h"
  39. #include "physcoltest.h"
  40. #include "rendobj.h"
  41. #include "persistfactory.h"
  42. #include "wwphysids.h"
  43. #include "simpledefinitionfactory.h"
  44. #include "wwdebug.h"
  45. #include "wwhack.h"
  46. #include "wwprofile.h"
  47. DECLARE_FORCE_LINK(projectile);
  48. /***********************************************************************************************
  49. **
  50. ** ProjectileClass Implementation
  51. **
  52. ***********************************************************************************************/
  53. /*
  54. ** Declare a PersistFactory for ProjectileClasses
  55. */
  56. SimplePersistFactoryClass<ProjectileClass,PHYSICS_CHUNKID_PROJECTILE> _ProjectileFactory;
  57. /*
  58. ** ChunkID's used by ProjectileClass
  59. */
  60. enum
  61. {
  62. PROJECTILE_CHUNK_MOVEABLEPHYS = 0x03308765, // parent class data
  63. PROJECTILE_CHUNK_VARIABLES,
  64. PROJECTILE_VARIABLE_POSITION = 0x00,
  65. PROJECTILE_VARIABLE_VELOCITY,
  66. PROJECTILE_VARIABLE_COLLIDESONMOVE,
  67. PROJECTILE_VARIABLE_ORIENTATIONMODE,
  68. PROJECTILE_VARIABLE_TUMBLEAXIS,
  69. PROJECTILE_VARIABLE_TUMBLERATE,
  70. PROJECTILE_VARIABLE_LIFETIME,
  71. PROJECTILE_VARIABLE_BOUNCECOUNT,
  72. };
  73. ProjectileClass::ProjectileClass(void) :
  74. CollidesOnMove(true),
  75. OrientationMode(ORIENTATION_ALIGNED),
  76. TumbleAxis(1,0,0),
  77. TumbleRate(DEG_TO_RADF(10.0f)),
  78. Lifetime(2.0f),
  79. BounceCount(0)
  80. {
  81. State.Position.Set(0,0,0);
  82. State.Velocity.Set(0,0,0);
  83. // turn off lighting for all projectiles
  84. Enable_Is_Pre_Lit(true);
  85. }
  86. void ProjectileClass::Init(const ProjectileDefClass & def)
  87. {
  88. MoveablePhysClass::Init(def);
  89. CollidesOnMove = def.CollidesOnMove;
  90. OrientationMode = def.OrientationMode;
  91. TumbleAxis = def.TumbleAxis;
  92. TumbleRate = def.TumbleRate;
  93. Lifetime = def.Lifetime;
  94. BounceCount = def.BounceCount;
  95. State.Position.Set(0,0,0);
  96. State.Velocity.Set(0,0,0);
  97. if (TumbleAxis.Length2() == 0.0f) {
  98. TumbleAxis.Set(0,0,1);
  99. } else {
  100. TumbleAxis.Normalize();
  101. }
  102. // turn off lighting for all projectiles
  103. Enable_Is_Pre_Lit(true);
  104. }
  105. ProjectileClass::~ProjectileClass(void)
  106. {
  107. }
  108. const AABoxClass & ProjectileClass::Get_Bounding_Box(void) const
  109. {
  110. assert(Model);
  111. return Model->Get_Bounding_Box();
  112. }
  113. const Matrix3D & ProjectileClass::Get_Transform(void) const
  114. {
  115. assert(Model);
  116. return Model->Get_Transform();
  117. }
  118. void ProjectileClass::Set_Transform(const Matrix3D & tm)
  119. {
  120. tm.Get_Translation(&State.Position);
  121. assert(Model);
  122. Model->Set_Transform(tm);
  123. // TODO: don't use the actual bounds? Just use the position and a pre-calced extent
  124. Update_Cull_Box();
  125. }
  126. void ProjectileClass::Set_Orientation_Mode_Tumbling(void)
  127. {
  128. OrientationMode = ORIENTATION_TUMBLING;
  129. TumbleAxis.Set(0,0,0);
  130. while (TumbleAxis.Length() <= 0.001f) {
  131. TumbleAxis.X = WWMath::Random_Float();
  132. TumbleAxis.Y = WWMath::Random_Float();
  133. TumbleAxis.Z = WWMath::Random_Float();
  134. }
  135. TumbleAxis.Normalize();
  136. TumbleRate = WWMath::Random_Float((float)DEG_TO_RAD(2.0f),(float)DEG_TO_RAD(10.0f));
  137. }
  138. void ProjectileClass::Set_Orientation_Mode_Tumbling(const Vector3 & axis,float rate)
  139. {
  140. OrientationMode = ORIENTATION_TUMBLING;
  141. TumbleAxis = axis;
  142. TumbleAxis.Normalize();
  143. TumbleRate = rate;
  144. }
  145. void ProjectileClass::Set_Orientation_Mode_Aligned_Fixed(void)
  146. {
  147. OrientationMode = ORIENTATION_ALIGNED_FIXED;
  148. if (OrientationMode == ORIENTATION_ALIGNED_FIXED) {
  149. Matrix3D tm;
  150. tm.Obj_Look_At(State.Position,State.Position + State.Velocity,0.0f);
  151. Model->Set_Transform(tm);
  152. }
  153. }
  154. void ProjectileClass::Set_Lifetime( float time )
  155. {
  156. Lifetime = time;
  157. }
  158. float ProjectileClass::Get_Lifetime( void )
  159. {
  160. return Lifetime;
  161. }
  162. void ProjectileClass::Set_Bounce_Count(int count)
  163. {
  164. BounceCount = count;
  165. }
  166. int ProjectileClass::Get_Bounce_Count(void)
  167. {
  168. return BounceCount;
  169. }
  170. void ProjectileClass::Integrate(float dt)
  171. {
  172. float accel = PhysicsConstants::GravityAcceleration.Z * GravScale;
  173. State.Position.X += State.Velocity.X * dt;
  174. State.Position.Y += State.Velocity.Y * dt;
  175. State.Position.Z += 0.5f * accel * dt * dt + State.Velocity.Z * dt;
  176. State.Velocity.Z += accel * dt;
  177. }
  178. void ProjectileClass::Timestep(float dt)
  179. {
  180. WWPROFILE("Projectile::Timestep");
  181. const int MAX_BUMPS = 5;
  182. if (Is_User_Control_Enabled()) {
  183. return;
  184. }
  185. if (dt == 0.0f) {
  186. return;
  187. }
  188. WWASSERT(State.Position.Is_Valid());
  189. WWASSERT(State.Velocity.Is_Valid());
  190. if ( CollidesOnMove ) {
  191. WWPROFILE("Move and Collide");
  192. /*
  193. ** Repeat until we eat all of the time
  194. */
  195. float remaining_time = dt;
  196. float timestep;
  197. int bumps = 0;
  198. /*
  199. ** This is that last object we choose to ignore
  200. */
  201. PhysClass *blocker = NULL;
  202. while ((remaining_time > 0.0f) && (bumps < MAX_BUMPS) && (BounceCount >= 0)) {
  203. timestep = remaining_time;
  204. bumps++;
  205. /*
  206. ** Integrate the state of the object for some amount
  207. ** of time: timestep
  208. */
  209. ProjectileStateStruct oldstate = State;
  210. Integrate(timestep);
  211. /*
  212. ** Check for collisions in the path of the object
  213. */
  214. CastResultStruct res;
  215. LineSegClass ray(oldstate.Position,State.Position);
  216. PhysRayCollisionTestClass raytest(ray,&res,Get_Collision_Group(),COLLISION_TYPE_PROJECTILE);
  217. {
  218. WWPROFILE("Raycast");
  219. Inc_Ignore_Counter();
  220. PhysicsSceneClass::Get_Instance()->Cast_Ray(raytest);
  221. Dec_Ignore_Counter();
  222. }
  223. /*
  224. ** If the result was a "startbad", just do the whole step
  225. */
  226. if (raytest.Result->StartBad) {
  227. remaining_time -= timestep;
  228. } else {
  229. /*
  230. ** If there was a collision, cut the timestep so that
  231. ** we integrate right up to the collision
  232. */
  233. if (raytest.Result->Fraction < 1.0f) {
  234. timestep = raytest.Result->Fraction * timestep;
  235. remaining_time -= timestep;
  236. /*
  237. ** Compute the point of collision, stored in ContactPoint of the CastResStruct
  238. ** so that observers have it.
  239. */
  240. ray.Compute_Point(raytest.Result->Fraction,&(raytest.Result->ContactPoint));
  241. /*
  242. ** Reset and integrate up to that point
  243. */
  244. State = oldstate;
  245. Integrate(0.99f * timestep);
  246. /*
  247. ** Notify the parties involved
  248. */
  249. WWASSERT(raytest.CollidedPhysObj != NULL);
  250. CollisionReactionType reaction = COLLISION_REACTION_DEFAULT;
  251. CollisionEventClass event;
  252. event.CollisionResult = raytest.Result;
  253. event.CollidedRenderObj = raytest.CollidedRenderObj;
  254. {
  255. WWPROFILE("Callbacks");
  256. event.OtherObj = raytest.CollidedPhysObj;
  257. reaction |= Collision_Occurred(event);
  258. event.OtherObj = this;
  259. reaction |= raytest.CollidedPhysObj->Collision_Occurred(event);
  260. }
  261. if ( !(reaction & COLLISION_REACTION_NO_BOUNCE) ) {
  262. /*
  263. ** Perform collision processing and loop
  264. */
  265. float dot = Vector3::Dot_Product(State.Velocity,raytest.Result->Normal);
  266. State.Velocity -= (1.0f + Elasticity)*dot*raytest.Result->Normal;
  267. /*
  268. ** If we are using ORIENTATION_MODE_ALIGNED_FIXED, update the
  269. ** transform whenever we bounce
  270. */
  271. if (OrientationMode == ORIENTATION_ALIGNED_FIXED) {
  272. Matrix3D tm;
  273. tm.Obj_Look_At(State.Position,State.Position + State.Velocity,0.0f);
  274. Model->Set_Transform(tm);
  275. }
  276. /*
  277. ** Decrement the bounce count!
  278. */
  279. BounceCount--;
  280. } else {
  281. /*
  282. ** We were requested to fly through. Mark the current blocker as ignore
  283. ** so we collide with him no more this pass
  284. */
  285. Integrate(0.02*timestep);
  286. if ( blocker ) { // Stop ignoring the last blocker
  287. blocker->Dec_Ignore_Counter();
  288. }
  289. blocker = raytest.CollidedPhysObj;
  290. if ( blocker ) { // Start ignoring this blocker
  291. blocker->Inc_Ignore_Counter();
  292. }
  293. }
  294. /*
  295. ** If requested to stop, progress no more
  296. */
  297. if ( reaction & COLLISION_REACTION_STOP_MOTION ) {
  298. remaining_time = 0;
  299. State.Velocity = Vector3(0,0,0);
  300. }
  301. } else {
  302. remaining_time -= timestep;
  303. }
  304. }
  305. }
  306. if ( blocker ) { // Stop ignoring the last blocker
  307. blocker->Dec_Ignore_Counter();
  308. }
  309. } else {
  310. Integrate(dt);
  311. }
  312. Update_Transform(dt);
  313. Update_Cull_Box();
  314. DEBUG_RENDER_AXES(Get_Transform(),Vector3(1.0f,1.0f,1.0f));
  315. /*
  316. ** Decrement our life, check our bounce count
  317. */
  318. Lifetime -= dt;
  319. if ((Lifetime < 0.0f) || (BounceCount < 0)) {
  320. Expire();
  321. }
  322. }
  323. bool ProjectileClass::Cast_Ray(PhysRayCollisionTestClass & raytest)
  324. {
  325. assert(Model);
  326. if (Model->Cast_Ray(raytest)) {
  327. raytest.CollidedPhysObj = this;
  328. return true;
  329. }
  330. return false;
  331. }
  332. void ProjectileClass::Update_Transform(float dt)
  333. {
  334. WWASSERT(Model);
  335. // Update the cached transformation matrix
  336. Matrix3D tm;
  337. switch(OrientationMode) {
  338. case ORIENTATION_ALIGNED:
  339. // compute a matrix aligned to the path
  340. tm.Obj_Look_At(State.Position,State.Position + State.Velocity,0.0f);
  341. Model->Set_Transform(tm);
  342. break;
  343. case ORIENTATION_ALIGNED_FIXED:
  344. case ORIENTATION_FIXED:
  345. // just update the position
  346. Model->Set_Position(State.Position);
  347. break;
  348. case ORIENTATION_TUMBLING:
  349. // update the position, tumble a little
  350. tm = Model->Get_Transform();
  351. tm.Set_Translation(State.Position);
  352. Matrix3D::Multiply(tm,Matrix3D(TumbleAxis,TumbleRate*dt),&tm);
  353. Model->Set_Transform(tm);
  354. break;
  355. };
  356. Update_Visibility_Status();
  357. }
  358. const PersistFactoryClass & ProjectileClass::Get_Factory (void) const
  359. {
  360. return _ProjectileFactory;
  361. }
  362. bool ProjectileClass::Save (ChunkSaveClass &csave)
  363. {
  364. csave.Begin_Chunk(PROJECTILE_CHUNK_MOVEABLEPHYS);
  365. MoveablePhysClass::Save(csave);
  366. csave.End_Chunk();
  367. csave.Begin_Chunk(PROJECTILE_CHUNK_VARIABLES);
  368. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_POSITION,State.Position);
  369. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_VELOCITY,State.Velocity);
  370. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_COLLIDESONMOVE,CollidesOnMove);
  371. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_ORIENTATIONMODE,OrientationMode);
  372. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_TUMBLEAXIS,TumbleAxis);
  373. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_TUMBLERATE,TumbleRate);
  374. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_LIFETIME,Lifetime);
  375. WRITE_MICRO_CHUNK(csave,PROJECTILE_VARIABLE_BOUNCECOUNT,BounceCount);
  376. csave.End_Chunk();
  377. return true;
  378. }
  379. bool ProjectileClass::Load (ChunkLoadClass &cload)
  380. {
  381. while (cload.Open_Chunk()) {
  382. switch(cload.Cur_Chunk_ID())
  383. {
  384. case PROJECTILE_CHUNK_MOVEABLEPHYS:
  385. MoveablePhysClass::Load(cload);
  386. break;
  387. case PROJECTILE_CHUNK_VARIABLES:
  388. while (cload.Open_Micro_Chunk()) {
  389. switch(cload.Cur_Micro_Chunk_ID()) {
  390. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_POSITION,State.Position);
  391. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_VELOCITY,State.Velocity);
  392. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_COLLIDESONMOVE,CollidesOnMove);
  393. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_ORIENTATIONMODE,OrientationMode);
  394. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_TUMBLEAXIS,TumbleAxis);
  395. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_TUMBLERATE,TumbleRate);
  396. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_LIFETIME,Lifetime);
  397. READ_MICRO_CHUNK(cload,PROJECTILE_VARIABLE_BOUNCECOUNT,BounceCount);
  398. }
  399. cload.Close_Micro_Chunk();
  400. }
  401. break;
  402. default:
  403. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  404. break;
  405. }
  406. cload.Close_Chunk();
  407. }
  408. return true;
  409. }
  410. /***********************************************************************************************
  411. **
  412. ** ProjectileDefClass Implementation
  413. **
  414. ***********************************************************************************************/
  415. /*
  416. ** Declare a PersistFactory for ProjectileDefClasses, this allows them to save and load
  417. */
  418. SimplePersistFactoryClass<ProjectileDefClass,PHYSICS_CHUNKID_PROJECTILEDEF> _ProjectileDefFactory;
  419. /*
  420. ** Declare a definition factory. This exposes ProjectileDef to the editor.
  421. */
  422. DECLARE_DEFINITION_FACTORY(ProjectileDefClass, CLASSID_PROJECTILEDEF, "Projectile") _ProjectileDefDefFactory;
  423. /*
  424. ** Chunk ID's used by MoveablePhysDefClass
  425. */
  426. enum
  427. {
  428. PROJECTILEDEF_CHUNK_MOVEABLEPHYSDEF = 0x01210011, // (parent class)
  429. PROJECTILEDEF_CHUNK_VARIABLES,
  430. PROJECTILEDEF_VARIABLE_COLLIDESONMOVE = 0x00,
  431. PROJECTILEDEF_VARIABLE_ORIENTATIONMODE,
  432. PROJECTILEDEF_VARIABLE_TUMBLEAXIS,
  433. PROJECTILEDEF_VARIABLE_TUMBLERATE,
  434. PROJECTILEDEF_VARIABLE_LIFETIME,
  435. PROJECTILEDEF_VARIABLE_BOUNCECOUNT,
  436. };
  437. ProjectileDefClass::ProjectileDefClass(void) :
  438. CollidesOnMove(true),
  439. OrientationMode(ProjectileClass::ORIENTATION_ALIGNED),
  440. TumbleAxis(1,2,1),
  441. TumbleRate(DEG_TO_RADF(10.0f)),
  442. Lifetime(2.0f),
  443. BounceCount(0)
  444. {
  445. #ifdef PARAM_EDITING_ON
  446. // make our parameters editable!
  447. EDITABLE_PARAM(ProjectileDefClass, ParameterClass::TYPE_BOOL, CollidesOnMove);
  448. EnumParameterClass *param = new EnumParameterClass(&OrientationMode);
  449. param->Set_Name ("OrientationMode");
  450. param->Add_Value("ALIGNED",ProjectileClass::ORIENTATION_ALIGNED);
  451. param->Add_Value("FIXED",ProjectileClass::ORIENTATION_FIXED);
  452. param->Add_Value("TUMBLE",ProjectileClass::ORIENTATION_TUMBLING);
  453. GENERIC_EDITABLE_PARAM(ProjectileDefClass,param)
  454. FLOAT_EDITABLE_PARAM(ProjectileDefClass, TumbleAxis.X, 0.0f, 10.0f);
  455. FLOAT_EDITABLE_PARAM(ProjectileDefClass, TumbleAxis.Y, 0.0f, 10.0f);
  456. FLOAT_EDITABLE_PARAM(ProjectileDefClass, TumbleAxis.Z, 0.0f, 10.0f);
  457. ANGLE_EDITABLE_PARAM(ProjectileDefClass, TumbleRate, DEG_TO_RADF(0.1f), DEG_TO_RADF(90.0f));
  458. FLOAT_EDITABLE_PARAM(ProjectileDefClass, Lifetime, 0.01f, 100.0f);
  459. INT_EDITABLE_PARAM(ProjectileDefClass, BounceCount, 0, 32);
  460. #endif
  461. }
  462. const PersistFactoryClass & ProjectileDefClass::Get_Factory (void) const
  463. {
  464. return _ProjectileDefFactory;
  465. }
  466. uint32 ProjectileDefClass::Get_Class_ID (void) const
  467. {
  468. return CLASSID_PROJECTILEDEF;
  469. }
  470. PersistClass * ProjectileDefClass::Create(void) const
  471. {
  472. ProjectileClass * obj = NEW_REF(ProjectileClass,());
  473. obj->Init(*this);
  474. return obj;
  475. }
  476. bool ProjectileDefClass::Save(ChunkSaveClass &csave)
  477. {
  478. csave.Begin_Chunk(PROJECTILEDEF_CHUNK_MOVEABLEPHYSDEF);
  479. MoveablePhysDefClass::Save(csave);
  480. csave.End_Chunk();
  481. csave.Begin_Chunk(PROJECTILEDEF_CHUNK_VARIABLES);
  482. WRITE_MICRO_CHUNK(csave,PROJECTILEDEF_VARIABLE_COLLIDESONMOVE,CollidesOnMove);
  483. WRITE_MICRO_CHUNK(csave,PROJECTILEDEF_VARIABLE_ORIENTATIONMODE,OrientationMode);
  484. WRITE_MICRO_CHUNK(csave,PROJECTILEDEF_VARIABLE_TUMBLEAXIS,TumbleAxis);
  485. WRITE_MICRO_CHUNK(csave,PROJECTILEDEF_VARIABLE_TUMBLERATE,TumbleRate);
  486. WRITE_MICRO_CHUNK(csave,PROJECTILEDEF_VARIABLE_LIFETIME,Lifetime);
  487. WRITE_MICRO_CHUNK(csave,PROJECTILEDEF_VARIABLE_BOUNCECOUNT,BounceCount);
  488. csave.End_Chunk();
  489. return true;
  490. }
  491. bool ProjectileDefClass::Load(ChunkLoadClass &cload)
  492. {
  493. while (cload.Open_Chunk()) {
  494. switch(cload.Cur_Chunk_ID()) {
  495. case PROJECTILEDEF_CHUNK_MOVEABLEPHYSDEF:
  496. MoveablePhysDefClass::Load(cload);
  497. break;
  498. case PROJECTILEDEF_CHUNK_VARIABLES:
  499. while (cload.Open_Micro_Chunk()) {
  500. switch(cload.Cur_Micro_Chunk_ID()) {
  501. READ_MICRO_CHUNK(cload,PROJECTILEDEF_VARIABLE_COLLIDESONMOVE,CollidesOnMove);
  502. READ_MICRO_CHUNK(cload,PROJECTILEDEF_VARIABLE_ORIENTATIONMODE,OrientationMode);
  503. READ_MICRO_CHUNK(cload,PROJECTILEDEF_VARIABLE_TUMBLEAXIS,TumbleAxis);
  504. READ_MICRO_CHUNK(cload,PROJECTILEDEF_VARIABLE_TUMBLERATE,TumbleRate);
  505. READ_MICRO_CHUNK(cload,PROJECTILEDEF_VARIABLE_LIFETIME,Lifetime);
  506. READ_MICRO_CHUNK(cload,PROJECTILEDEF_VARIABLE_BOUNCECOUNT,BounceCount);
  507. }
  508. cload.Close_Micro_Chunk();
  509. }
  510. break;
  511. default:
  512. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  513. break;
  514. }
  515. cload.Close_Chunk();
  516. }
  517. return true;
  518. }
  519. bool ProjectileDefClass::Is_Type(const char * type_name)
  520. {
  521. if (stricmp(type_name,ProjectileDefClass::Get_Type_Name()) == 0) {
  522. return true;
  523. } else {
  524. return MoveablePhysDefClass::Is_Type(type_name);
  525. }
  526. }