clusterIGC.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. /*
  2. ** Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved.
  3. **
  4. ** File: clusterIGC.cpp
  5. **
  6. ** Author:
  7. **
  8. ** Description:
  9. ** Implementation of the CclusterIGC class. This file was initially created by
  10. ** the ATL wizard for the core object.
  11. **
  12. ** Clusters are distinct places in the trek universe: nothing happening in one
  13. ** cluster can directly affect things in another cluster. Clusters (in addition to
  14. ** the ships, missiles, etc.) have a collection of static objects that represent things
  15. ** like planets.
  16. **
  17. ** History:
  18. */
  19. // clusterIGC.cpp : Implementation of CclusterIGC
  20. #include "pch.h"
  21. #include "clusterIGC.h"
  22. #include "modelIGC.h"
  23. #include <stdlib.h>
  24. #include <math.h>
  25. /////////////////////////////////////////////////////////////////////////////
  26. // CclusterIGC
  27. HRESULT CclusterIGC::Initialize(ImissionIGC* pMission, Time now, const void* data, int dataSize)
  28. {
  29. assert (pMission);
  30. m_pMission = pMission;
  31. ZRetailAssert (data && (dataSize == sizeof(m_data)));
  32. m_data = *((DataClusterIGC*)data);
  33. m_nPass = m_data.clusterID;
  34. m_lastUpdate = now;
  35. pMission->AddCluster(this);
  36. m_pClusterSite = pMission->GetIgcSite()->CreateClusterSite(this);
  37. if (m_data.posterName[0] != '\0')
  38. m_pClusterSite->SetEnvironmentGeo(m_data.posterName);
  39. if (m_data.planetName[0] != '\0')
  40. {
  41. Vector position;
  42. double sinLatitude = double(m_data.planetSinLatitude - 0.5);
  43. double cosLatitude = sqrt(1.0 - sinLatitude * sinLatitude);
  44. double longitude = double(m_data.planetLongitude * (2.0f * pi));
  45. position.x = float(cos(longitude) * cosLatitude);
  46. position.y = float(sin(longitude) * cosLatitude);
  47. position.z = float(sinLatitude);
  48. m_pClusterSite->AddPoster(m_data.planetName,
  49. position,
  50. float(m_data.planetRadius));
  51. }
  52. return S_OK;
  53. }
  54. void CclusterIGC::Terminate(void)
  55. {
  56. {
  57. //Models remove themselves from the list when terminated
  58. ModelLinkIGC* l;
  59. while (l = m_models.first()) //Not ==
  60. {
  61. l->data()->Terminate();
  62. }
  63. }
  64. m_kdrStatic.flush();
  65. m_kdrMoving.flush();
  66. assert (m_modelsPickable.n() == 0);
  67. assert (m_modelsCastRay.n() == 0);
  68. assert (m_stations.n() == 0);
  69. assert (m_models.n() == 0);
  70. assert (m_probes.n() == 0);
  71. assert (m_warps.n() == 0);
  72. assert (m_treasures.n() == 0);
  73. assert (m_asteroids.n() == 0);
  74. assert (m_mines.n() == 0);
  75. assert (m_pClusterSite);
  76. m_pClusterSite->Terminate();
  77. m_pClusterSite = NULL;
  78. m_pMission->DeleteCluster(this);
  79. }
  80. void CclusterIGC::Update(Time now)
  81. {
  82. if (now > m_lastUpdate)
  83. {
  84. float dt = now - m_lastUpdate;
  85. bool bStarted = m_pMission->GetMissionStage() == STAGE_STARTED;
  86. if (bStarted)
  87. {
  88. {
  89. //Have any stations launch docked drones
  90. for (StationLinkIGC* l = m_stations.first();
  91. (l != NULL);
  92. l = l->next())
  93. {
  94. IstationIGC* pstation = l->data();
  95. ShipLinkIGC* pslNext;
  96. for (ShipLinkIGC* psl = pstation->GetShips()->first();
  97. (psl != NULL);
  98. psl = pslNext)
  99. {
  100. IshipIGC* pship = psl->data();
  101. pslNext = psl->next(); //Get the next link now since the ship may launch
  102. if (pship->GetAutopilot() && (pship->GetPilotType() < c_ptPlayer))
  103. {
  104. //Docked non-players on autopilot never are observers/parents
  105. assert (pship->GetParentShip() == NULL);
  106. assert (pship->GetChildShips()->n() == 0);
  107. if (pship->OkToLaunch(now))
  108. pship->SetStation(NULL);
  109. }
  110. }
  111. }
  112. }
  113. {
  114. m_fCost = m_pMission->GetFloatConstant(c_fcidBaseClusterCost);
  115. float costLifepod = m_pMission->GetFloatConstant(c_fcidLifepodCost);
  116. float costTurret = m_pMission->GetFloatConstant(c_fcidTurretCost);
  117. float costPlayer = m_pMission->GetFloatConstant(c_fcidPlayerCost);
  118. float costDrone = m_pMission->GetFloatConstant(c_fcidDroneCost);
  119. //Have miners and builders do any pre-plotted moves. Allow ships to suicide.
  120. ShipLinkIGC* lNext;
  121. for (ShipLinkIGC* l = m_ships.first();
  122. (l != NULL);
  123. l = lNext)
  124. {
  125. IshipIGC* s = l->data();
  126. lNext = l->next();
  127. if (s->GetPilotType() < c_ptPlayer)
  128. m_fCost += costDrone;
  129. else if (s->GetParentShip() != NULL)
  130. m_fCost += costTurret;
  131. else
  132. {
  133. IhullTypeIGC* pht = s->GetBaseHullType();
  134. assert (pht);
  135. m_fCost += pht->HasCapability(c_habmLifepod)
  136. ? costLifepod
  137. : costPlayer;
  138. }
  139. s->PreplotShipMove(now);
  140. }
  141. if (m_fCost > 0.0f)
  142. {
  143. m_fCost *= dt / m_pMission->GetFloatConstant(c_fcidClusterDivisor);
  144. }
  145. {
  146. //Have all ships on autopilot plot their moves. Allow ships to suicide.
  147. ShipLinkIGC* lNext;
  148. for (ShipLinkIGC* l = m_ships.first();
  149. (l != NULL);
  150. l = lNext)
  151. {
  152. IshipIGC* s = l->data();
  153. lNext = l->next();
  154. s->PlotShipMove(now);
  155. }
  156. }
  157. }
  158. {
  159. //Have all ships execute their moves
  160. for (ShipLinkIGC* l = m_ships.first();
  161. (l != NULL);
  162. l = l->next())
  163. {
  164. IshipIGC* s = l->data();
  165. if (s->GetParentShip() == NULL)
  166. {
  167. s->ExecuteShipMove(now);
  168. }
  169. }
  170. }
  171. }
  172. else
  173. m_fCost = 0.0f;
  174. {
  175. //Call the update method on all the contained models
  176. //models might self-terminate in the update and nuke earlier models in the update loop
  177. //NYI debugging variables
  178. //ObjectType oldObjectType = NA;
  179. //ObjectType newObjectType = NA;
  180. ModelLinkIGC* lNext;
  181. for (ModelLinkIGC* l = m_models.first();
  182. (l != NULL);
  183. l = lNext)
  184. {
  185. //oldObjectType = newObjectType;
  186. //newObjectType = l->data()->GetObjectType();
  187. lNext = l->next();
  188. l->data()->Update(now);
  189. }
  190. }
  191. if (m_data.activeF && bStarted)
  192. {
  193. {
  194. //Update the bounding boxes for all moving objects & projectiles
  195. for (ModelLinkIGC* l = m_models.first();
  196. (l != NULL);
  197. l = l->next())
  198. {
  199. l->data()->SetBB(m_lastUpdate, now, dt);
  200. }
  201. m_tMax = dt;
  202. }
  203. m_kdrStatic.update();
  204. m_kdrMoving.update();
  205. {
  206. //Cast rays through the KD tree for each object
  207. for (ModelLinkIGC* l = m_modelsCastRay.first();
  208. (l != NULL);
  209. l = l->next())
  210. {
  211. ImodelIGC* m = l->data();
  212. HitTest* ht = m->GetHitTest();
  213. if (!ht->GetDeadF())
  214. {
  215. m_kdrStatic.test(ht, &m_collisions);
  216. m_kdrMoving.test(ht, &m_collisions);
  217. }
  218. }
  219. }
  220. //Sort the collisions by the time they occur
  221. m_collisions.sort(0);
  222. //Process each collision (in order)
  223. {
  224. m_tOffset = 0.0f;
  225. for (m_collisionID = 0; (m_collisionID < m_collisions.n()); m_collisionID++)
  226. {
  227. const CollisionEntry& entry = m_collisions[m_collisionID];
  228. if (!(entry.m_pHitTest1->GetDeadF() || entry.m_pHitTest2->GetDeadF()))
  229. {
  230. Time timeCollision = m_lastUpdate + (m_tOffset + entry.m_tCollision);
  231. ImodelIGC* pModelHitTest1 = (ImodelIGC*)(entry.m_pHitTest1->GetData());
  232. assert (pModelHitTest1);
  233. ImodelIGC* pModelHitTest2 = (ImodelIGC*)(entry.m_pHitTest2->GetData());
  234. assert (pModelHitTest2);
  235. //Give each participant in the collision a chance to handle the collision
  236. //but give the "1st" model first dibs.
  237. if ((pModelHitTest1->GetCluster() == this) &&
  238. (pModelHitTest2->GetCluster() == this))
  239. {
  240. pModelHitTest1->HandleCollision(timeCollision, entry.m_tCollision, entry, pModelHitTest2);
  241. }
  242. }
  243. }
  244. m_collisions.purge();
  245. }
  246. {
  247. //Apply any damage from mines
  248. //Kids always follow parents in the ship list, so go from back to front
  249. //so that the killing a parent doesn't mean hitting dead elements in the list
  250. ShipLinkIGC* lTxen;
  251. for (ShipLinkIGC* l = m_ships.last();
  252. (l != NULL);
  253. l = lTxen)
  254. {
  255. IshipIGC* s = l->data();
  256. lTxen = l->txen();
  257. s->ApplyMineDamage();
  258. }
  259. }
  260. //Move each object & projectile
  261. {
  262. for (ModelLinkIGC* l = m_models.first();
  263. (l != NULL);
  264. l = l->next())
  265. {
  266. l->data()->Move();
  267. }
  268. }
  269. if ((m_nPass++) % c_nPassesPerUpdate == 0)
  270. {
  271. for (ModelLinkIGC* l = m_models.first();
  272. (l != NULL);
  273. l = l->next())
  274. {
  275. l->data()->UpdateSeenBySide();
  276. }
  277. m_pMission->GetIgcSite()->ClusterUpdateEvent(this);
  278. }
  279. }
  280. //Draw and resolve any explosions
  281. if (m_nExplosions != 0)
  282. {
  283. const int c_maxDmgs = 500;
  284. IdamageIGC* pdmgs[c_maxDmgs];
  285. int nDmgs = 0;
  286. //Copy the list of models in the sector that can be damaged into
  287. for (ModelLinkIGC* l = m_modelsPickable.first();
  288. (l != NULL);
  289. l = l->next())
  290. {
  291. ImodelIGC* pmodel = l->data();
  292. ObjectType type = pmodel->GetObjectType();
  293. //Not everything that can take damage can be affected by an explosion.
  294. if ((type == OT_ship) || (type == OT_asteroid) ||
  295. (type == OT_station) || (type == OT_missile) || (type == OT_probe))
  296. {
  297. pmodel->AddRef();
  298. pdmgs[nDmgs++] = (IdamageIGC*)pmodel;
  299. if (nDmgs == c_maxDmgs)
  300. break;
  301. }
  302. }
  303. ImineIGC* pmines[c_maxDmgs];
  304. int nMines = 0;
  305. {
  306. for (MineLinkIGC* l = m_mines.first(); (l != NULL); l = l->next())
  307. {
  308. ImineIGC* pm = l->data();
  309. pm->AddRef();
  310. pmines[nMines++] = pm;
  311. if (nMines == c_maxDmgs)
  312. break;
  313. }
  314. }
  315. int i = 0;
  316. do
  317. {
  318. ExplosionData& e = m_explosions[i];
  319. m_pClusterSite->AddExplosion(e.position, e.radius, e.explosionType);
  320. float dt = (e.time - m_lastUpdate) - m_tOffset;
  321. //Now, the painful part: applying damage to everything in the sector that could be hit
  322. {
  323. for (int j = 0; (j < nDmgs); j++)
  324. {
  325. IdamageIGC* pTarget = pdmgs[j];
  326. if (pTarget->GetCluster() == this) //Make sure it wasn't already destroyed
  327. {
  328. //The target is still around
  329. Vector p = pTarget->GetPosition() + dt * pTarget->GetVelocity();
  330. float d = (e.position - p).Length() - pTarget->GetRadius();
  331. if (d < e.radius)
  332. {
  333. float amount = e.amount;
  334. if (d > 0.0f)
  335. {
  336. float f = 1.0f - (d / e.radius);
  337. amount *= f * f;
  338. }
  339. pTarget->ReceiveDamage(e.damageType | c_dmgidNoWarn | c_dmgidNoDebris,
  340. amount,
  341. e.time,
  342. p, e.position,
  343. e.launcher);
  344. }
  345. }
  346. }
  347. }
  348. {
  349. for (int j = 0; (j < nMines); j++)
  350. {
  351. ImineIGC* pTarget = pmines[j];
  352. if (pTarget->GetCluster() == this) //Make sure it wasn't already destroyed
  353. {
  354. //The target is still around
  355. const Vector& p = pTarget->GetPosition();
  356. float d = (e.position - p).Length() - pTarget->GetRadius();
  357. if (d < e.radius)
  358. {
  359. float amount = e.amount;
  360. if (d > 0.0f)
  361. {
  362. float f = 1.0f - (d / e.radius);
  363. amount *= f * f;
  364. }
  365. pTarget->ReduceStrength(amount);
  366. }
  367. }
  368. }
  369. }
  370. if (e.launcher)
  371. e.launcher->Release();
  372. }
  373. while (++i < m_nExplosions);
  374. //Release all the cached pointers
  375. {
  376. while (--nDmgs >= 0)
  377. pdmgs[nDmgs]->Release();
  378. }
  379. {
  380. while (--nMines >= 0)
  381. pmines[nMines]->Release();
  382. }
  383. m_nExplosions = 0;
  384. }
  385. m_lastUpdate = now;
  386. }
  387. }
  388. int CclusterIGC::Export(void* data) const
  389. {
  390. if (data)
  391. {
  392. *((DataClusterIGC*)data) = m_data;
  393. //Selectively lie here: exported clusters are always inactive by default.
  394. ((DataClusterIGC*)data)->activeF = false;
  395. }
  396. return sizeof(m_data);
  397. }
  398. void CclusterIGC::AddModel(ImodelIGC* modelNew)
  399. {
  400. assert (modelNew);
  401. ZVerify(m_models.first(modelNew));
  402. modelNew->AddRef();
  403. m_pClusterSite->AddThingSite(modelNew->GetThingSite());
  404. {
  405. //Add the model to the collision set
  406. HitTest* ht = modelNew->GetHitTest();
  407. ModelAttributes mt = modelNew->GetAttributes();
  408. if ((mt & c_mtNotPickable) == 0)
  409. {
  410. ZVerify(m_modelsPickable.last(modelNew));
  411. modelNew->AddRef();
  412. }
  413. if (mt & c_mtCastRay)
  414. {
  415. ZVerify(m_modelsCastRay.last(modelNew));
  416. modelNew->AddRef();
  417. }
  418. if (mt & c_mtHitable)
  419. {
  420. ((mt & c_mtStatic)
  421. ? m_kdrStatic
  422. : m_kdrMoving).addHitTest(ht);
  423. }
  424. else
  425. ht->SetDeadF(false);
  426. ZRetailAssert(!ht->GetDeadF());
  427. }
  428. }
  429. void CclusterIGC::DeleteModel(ImodelIGC* modelOld)
  430. {
  431. assert (modelOld);
  432. {
  433. //Add the model to the collision set
  434. HitTest* ht = modelOld->GetHitTest();
  435. ModelAttributes mt = modelOld->GetAttributes();
  436. if ((mt & c_mtNotPickable) == 0)
  437. {
  438. DeleteIbaseIGC((BaseListIGC*)&m_modelsPickable, modelOld);
  439. }
  440. if (mt & c_mtCastRay)
  441. {
  442. DeleteIbaseIGC((BaseListIGC*)&m_modelsCastRay, modelOld);
  443. }
  444. if (mt & c_mtHitable)
  445. {
  446. ((mt & c_mtStatic)
  447. ? m_kdrStatic
  448. : m_kdrMoving).deleteHitTest(ht);
  449. }
  450. else
  451. ht->SetDeadF(true);
  452. ZRetailAssert(ht->GetDeadF());
  453. }
  454. m_pClusterSite->DeleteThingSite(modelOld->GetThingSite());
  455. DeleteIbaseIGC((BaseListIGC*)&m_models, modelOld);
  456. }
  457. ImodelIGC* CclusterIGC::GetModel(const char* name) const
  458. {
  459. assert (name);
  460. for (ModelLinkIGC* l = m_models.first();
  461. (l != NULL);
  462. l = l->next())
  463. {
  464. ImodelIGC* m = l->data();
  465. if (_stricmp(m->GetName(), name) == 0)
  466. {
  467. return m;
  468. }
  469. }
  470. return NULL;
  471. }
  472. void CclusterIGC::RecalculateCollisions(float tOffset,
  473. ImodelIGC* pModel1,
  474. ImodelIGC* pModel2)
  475. {
  476. //Update the stop positions for the hit tests (& update their bounding boxes)
  477. assert ((pModel1->GetAttributes() & c_mtStatic) == 0);
  478. HitTest* pHitTest1 = pModel1->GetHitTest();
  479. if (pHitTest1)
  480. {
  481. if (pHitTest1->GetDeadF())
  482. pHitTest1 = NULL;
  483. else
  484. pHitTest1->SetStopPosition();
  485. }
  486. HitTest* pHitTest2;
  487. if ((pModel2 == NULL) || (pModel2->GetAttributes() & c_mtStatic))
  488. pHitTest2 = NULL;
  489. else
  490. {
  491. pHitTest2 = pModel2->GetHitTest();
  492. if (pHitTest2)
  493. {
  494. if (pHitTest2->GetDeadF())
  495. pHitTest2 = NULL;
  496. else
  497. pHitTest2->SetStopPosition();
  498. }
  499. }
  500. if (pHitTest1 || pHitTest2)
  501. {
  502. m_tOffset += tOffset;
  503. //Don't bother recalculating collisions if we are already outside the window in which collisions can occur.
  504. if (m_tOffset <= m_tMax)
  505. {
  506. {
  507. //Move all objects to their positions at the time of the collision
  508. for (ModelLinkIGC* l = m_models.first();
  509. (l != NULL);
  510. l = l->next())
  511. {
  512. ImodelIGC* m = l->data();
  513. HitTest* pht = m->GetHitTest();
  514. //Move the models not involved in the collision
  515. if ((m != pModel1) && (m != pModel2))
  516. m->Move(tOffset);
  517. else
  518. pht->AdjustTimes(tOffset);
  519. //Update the bounding boxes for everything
  520. pht->UpdateBB();
  521. }
  522. }
  523. //Remove any collisions that involved either object involved in this collision
  524. m_collisions.flush(m_collisionID + 1, pHitTest1, pHitTest2);
  525. {
  526. //Check for any collisions between either ship and the rest of the stuff in the cluster
  527. for (ModelLinkIGC* l = m_models.first();
  528. (l != NULL);
  529. l = l->next())
  530. {
  531. ImodelIGC* m = l->data();
  532. if ((m != pModel1) && (m != pModel2) && (m->GetAttributes() & (c_mtCastRay | c_mtHitable)))
  533. {
  534. HitTest* ht = m->GetHitTest();
  535. if (!ht->GetDeadF())
  536. {
  537. if (ht->GetID() != c_htidStaticObject)
  538. {
  539. //ht is not a static object, so it can always go first (which also handles
  540. //the problem that projectiles (which ht might be) go before ships).
  541. //prevent collisions between projectiles and their launcher.
  542. if (pHitTest1 &&
  543. (ht->GetNoHit() != pHitTest1) &&
  544. (pHitTest1->GetNoHit() != ht))
  545. ht->Collide(pHitTest1, &m_collisions);
  546. if (pHitTest2 &&
  547. (ht->GetNoHit() != pHitTest2) &&
  548. (pHitTest2->GetNoHit() != ht))
  549. ht->Collide(pHitTest2, &m_collisions);
  550. }
  551. else
  552. {
  553. //in collisions between static objects and non-static objects, the
  554. //non-static object is always first
  555. if (pHitTest1 &&
  556. (ht->GetNoHit() != pHitTest1) &&
  557. (pHitTest1->GetNoHit() != ht))
  558. pHitTest1->Collide(ht, &m_collisions);
  559. if (pHitTest2 &&
  560. (ht->GetNoHit() != pHitTest2) &&
  561. (pHitTest2->GetNoHit() != ht))
  562. pHitTest2->Collide(ht, &m_collisions);
  563. }
  564. }
  565. }
  566. }
  567. }
  568. //Resort the collisions of the yet to be handled collisions
  569. m_collisions.sort(m_collisionID + 1);
  570. }
  571. }
  572. }
  573. IbuildingEffectIGC* CclusterIGC::CreateBuildingEffect(Time now,
  574. IasteroidIGC* pasteroid,
  575. IstationIGC* pstation,
  576. IshipIGC* pshipBuilder,
  577. float radiusAsteroid,
  578. float radiusStation,
  579. const Vector& positionStart,
  580. const Vector& positionStop)
  581. {
  582. DataBuildingEffectIGC dbe;
  583. dbe.timeStart = now;
  584. dbe.pasteroid = pasteroid;
  585. dbe.pstation = pstation;
  586. dbe.pcluster = this;
  587. dbe.pshipBuilder = pshipBuilder;
  588. dbe.radiusAsteroid = radiusAsteroid;
  589. dbe.radiusStation = radiusStation;
  590. dbe.positionStart = positionStart;
  591. dbe.positionStop = positionStop;
  592. IbuildingEffectIGC* pbe = (IbuildingEffectIGC*)(m_pMission->CreateObject(now, OT_buildingEffect, &dbe, sizeof(dbe)));
  593. assert (pbe);
  594. pbe->Release();
  595. return pbe;
  596. }