terrobj.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. //---------------------------------------------------------------------------
  2. //
  3. // terrobj.cpp -- File contains the misc terrain object code
  4. //
  5. // MechCommander 2
  6. //
  7. //---------------------------------------------------------------------------//
  8. // Copyright (C) Microsoft Corporation. All rights reserved. //
  9. //===========================================================================//
  10. //---------------------------------------------------------------------------
  11. // Include Files
  12. #ifndef MCLIB_H
  13. #include "mclib.h"
  14. #endif
  15. #ifndef TERROBJ_H
  16. #include "terrobj.h"
  17. #endif
  18. #ifndef GAMESOUND_H
  19. #include "gamesound.h"
  20. #endif
  21. #ifndef SOUNDS_H
  22. #include "sounds.h"
  23. #endif
  24. #ifndef MOVE_H
  25. #include "move.h"
  26. #endif
  27. #ifndef TEAM_H
  28. #include "team.h"
  29. #endif
  30. #ifndef COLLSN_H
  31. #include "collsn.h"
  32. #endif
  33. #ifndef MULTPLYR_H
  34. #include "multplyr.h"
  35. #endif
  36. #ifndef OBJMGR_H
  37. #include "objmgr.h"
  38. #endif
  39. #ifndef CARNAGE_H
  40. #include "carnage.h"
  41. #endif
  42. #ifndef DOBJNUM_H
  43. #include "dobjnum.h"
  44. #endif
  45. //#include "ObjectAppearance.h"
  46. extern unsigned long NextIdNumber;
  47. extern float worldUnitsPerMeter;
  48. extern bool drawExtents;
  49. extern bool somethingOnFire;
  50. extern bool useOldProject;
  51. extern bool MLRVertexLimitReached;
  52. inline float agsqrt( float _a, float _b )
  53. {
  54. return sqrt(_a*_a + _b*_b);
  55. }
  56. #define TREE_FALL_RATE 15.0f
  57. #define TREE_FALL_ACCEL 5.0f;
  58. char lastName[256];
  59. extern MidLevelRenderer::MLRClipper * theClipper;
  60. extern bool useNonWeaponEffects;
  61. //---------------------------------------------------------------------------
  62. // class TerrainObjectType
  63. //---------------------------------------------------------------------------
  64. GameObjectPtr TerrainObjectType::createInstance (void) {
  65. TerrainObjectPtr result = new TerrainObject;
  66. if (!result)
  67. return NULL;
  68. result->init(true, this);
  69. //result->setIdNumber(NextIdNumber++);
  70. return(result);
  71. }
  72. //---------------------------------------------------------------------------
  73. void TerrainObjectType::init (void) {
  74. objectTypeClass = TERRAINOBJECT_TYPE;
  75. objectClass = TERRAINOBJECT;
  76. subType = TERROBJ_NONE;
  77. damageLevel = 0.0;
  78. collisionOffsetX = 0;
  79. collisionOffsetY = 0;
  80. setImpassable = false;
  81. xImpasse = 0;
  82. yImpasse = 0;
  83. extentRadius = -1.0;
  84. explDmg = 0.0;
  85. explRad = 0.0;
  86. fireTypeHandle = -1;
  87. }
  88. //---------------------------------------------------------------------------
  89. void TerrainObjectType::destroy (void)
  90. {
  91. ObjectType::destroy();
  92. }
  93. //---------------------------------------------------------------------------
  94. void TerrainObjectType::initMiscTerrObj (long objTypeNum) {
  95. //---------------------------------------------------------------------
  96. // This function is here to maintain compatibility with MC1. The values
  97. // used for the various miscTerrainTypes are hardcoded, based on
  98. // MC1 ship values. If we need to modify 'em, then try adding new
  99. // object types to the packet file!
  100. explosionObject = 0xFFFFFFFF;
  101. destroyedObject = 0xFFFFFFFF;
  102. extentRadius = -1.0;
  103. keepMe = true;
  104. iconNumber = -1;
  105. teamId = -1;
  106. if (objTypeNum == ObjectTypeManager::bridgeTypeHandle) {
  107. subType = TERROBJ_BRIDGE;
  108. damageLevel = 100.0;
  109. }
  110. else if (objTypeNum == ObjectTypeManager::forestTypeHandle) {
  111. subType = TERROBJ_FOREST;
  112. damageLevel = 100.0;
  113. fireTypeHandle = 1;
  114. }
  115. else if (objTypeNum == ObjectTypeManager::wallHeavyTypeHandle) {
  116. subType = TERROBJ_WALL_HEAVY;
  117. damageLevel = 100.0;
  118. }
  119. else if (objTypeNum == ObjectTypeManager::wallMediumTypeHandle) {
  120. subType = TERROBJ_WALL_MEDIUM;
  121. damageLevel = 100.0;
  122. }
  123. else if (objTypeNum == ObjectTypeManager::wallLightTypeHandle) {
  124. subType = TERROBJ_WALL_LIGHT;
  125. damageLevel = 100.0;
  126. }
  127. else
  128. Fatal(objTypeNum, " TerrainObjectType.init: bad MiscTerrainObj num ");
  129. }
  130. //---------------------------------------------------------------------------
  131. long TerrainObjectType::init (FilePtr objFile, unsigned long fileSize) {
  132. long result = 0;
  133. FitIniFile bldgFile;
  134. result = bldgFile.open(objFile, fileSize);
  135. if (result != NO_ERR)
  136. return(result);
  137. //----------------------------------------------
  138. // Read in the data needed for the TerrainObject
  139. subType = TERROBJ_NONE;
  140. result = bldgFile.seekBlock("TerrainObjectData");
  141. if (result != NO_ERR) {
  142. result = bldgFile.seekBlock("TreeData");
  143. if (result != NO_ERR)
  144. return(result);
  145. subType = TERROBJ_TREE;
  146. objectClass = TREE;
  147. }
  148. unsigned long dmgLevel;
  149. result = bldgFile.readIdULong("DmgLevel",dmgLevel);
  150. if (result != NO_ERR)
  151. return(result);
  152. damageLevel = (float)dmgLevel;
  153. result = bldgFile.readIdLong("CollisionOffsetX",collisionOffsetX);
  154. if (result != NO_ERR)
  155. collisionOffsetX = 0;
  156. result = bldgFile.readIdLong("CollisionOffsetY",collisionOffsetY);
  157. if (result != NO_ERR)
  158. collisionOffsetY = 0;
  159. long setImpass;
  160. result = bldgFile.readIdLong("SetImpassable",setImpass);
  161. setImpassable = false;
  162. if (result == NO_ERR)
  163. setImpassable = setImpass ? true : false;
  164. result = bldgFile.readIdLong("XImpasse",xImpasse);
  165. if (result != NO_ERR)
  166. xImpasse = 0;
  167. result = bldgFile.readIdLong("YImpasse",yImpasse);
  168. if (result != NO_ERR)
  169. yImpasse = 0;
  170. float realExtent = 0.0;
  171. result = bldgFile.readIdFloat("ExtentRadius", realExtent);
  172. if (result != NO_ERR)
  173. realExtent = -1.0;
  174. result = bldgFile.readIdFloat("ExplosionRadius",explRad);
  175. //-----------------------------------------------------------------
  176. // if this fails, explosion radius is not set and no splash damage.
  177. if (result != NO_ERR)
  178. explRad = 0.0;
  179. result = bldgFile.readIdFloat("ExplosionDamage",explDmg);
  180. // if this fails, explosion damage is not set and no splash damage.
  181. if (result != NO_ERR)
  182. explDmg = 0.0;
  183. //-------------------------------------------------------
  184. // Initialize the base object Type from the current file.
  185. result = ObjectType::init(&bldgFile);
  186. extentRadius = realExtent;
  187. return(result);
  188. }
  189. //---------------------------------------------------------------------------
  190. bool TerrainObjectType::handleCollision (GameObjectPtr collidee, GameObjectPtr collider) {
  191. if (MPlayer && !MPlayer->isServer())
  192. return(true);
  193. switch (subType) {
  194. case TERROBJ_NONE:
  195. //-------------------------------------------------------
  196. // The Building ceases to exist when its effect is done.
  197. // always return FALSE or the collision will make it
  198. // go away! We may want to play a sound effect here.
  199. switch (collider->getObjectClass()) {
  200. case EXPLOSION:
  201. case BATTLEMECH:
  202. case GROUNDVEHICLE:
  203. if (!collider->isMover() || (collider->isMover() && ((MoverPtr)collider)->pathLocks))
  204. {
  205. WeaponShotInfo shot;
  206. shot.init(0, -1, collidee->getDamageLevel(), 0, 0);
  207. collidee->handleWeaponHit(&shot, (MPlayer != NULL));
  208. }
  209. break;
  210. }
  211. break;
  212. case TERROBJ_TREE:
  213. //-------------------------------------------------------
  214. // Trees are magical. If a mech hits one, it goes down
  215. // and is replaced by its last frame. In other words,
  216. // play the animation and stop on last frame.
  217. //
  218. // When a tree falls, we change its frame_of_ref to match
  219. // the direction it should fall in from the nominal world frame.
  220. //
  221. switch (collider->getObjectClass()) {
  222. case EXPLOSION:
  223. case BATTLEMECH:
  224. case GROUNDVEHICLE:
  225. if (!collider->isMover() || (collider->isMover() && ((MoverPtr)collider)->pathLocks))
  226. {
  227. TerrainObjectPtr tree = (TerrainObjectPtr)collidee;
  228. if (!tree->getFlag(OBJECT_FLAG_FALLEN) && !tree->getFlag(OBJECT_FLAG_FALLING)) {
  229. tree->setFlag(OBJECT_FLAG_FALLING, true);
  230. float fallAngle = collidee->relFacingTo(collider->getPosition());
  231. collidee->rotate(fallAngle,0.0f);
  232. //------------------------------------------------------
  233. // Tree has fallen. You may no longer collide with it.
  234. tree->setTangible(false);
  235. }
  236. }
  237. break;
  238. }
  239. break;
  240. case TERROBJ_BRIDGE:
  241. case TERROBJ_FOREST:
  242. case TERROBJ_WALL_HEAVY:
  243. case TERROBJ_WALL_MEDIUM:
  244. break;
  245. case TERROBJ_WALL_LIGHT:
  246. switch (collider->getObjectClass()) {
  247. case BATTLEMECH:
  248. case GROUNDVEHICLE: {
  249. WeaponShotInfo shotInfo;
  250. shotInfo.init(collider->getWatchID(), -1, 250.0, 0, 0);
  251. if (MPlayer) {
  252. if (MPlayer->isServer()) {
  253. collidee->handleWeaponHit(&shotInfo, true);
  254. }
  255. }
  256. else
  257. collidee->handleWeaponHit(&shotInfo);
  258. }
  259. break;
  260. }
  261. break;
  262. }
  263. return(true);
  264. }
  265. //---------------------------------------------------------------------------
  266. bool TerrainObjectType::handleDestruction (GameObjectPtr collidee, GameObjectPtr collider) {
  267. TerrainObjectPtr me = (TerrainObjectPtr)collidee;
  268. if (me->getObjectType()->getSubType() == TERROBJ_FOREST) {
  269. me->openSubAreas();
  270. }
  271. return(false);
  272. }
  273. //***************************************************************************
  274. // class TerrainObject
  275. //***************************************************************************
  276. void TerrainObject::rotate (float yaw, float pitch)
  277. {
  278. rotation = yaw;
  279. pitchAngle = pitch;
  280. }
  281. void TerrainObject::updateDebugWindow (GameDebugWindow* debugWindow) {
  282. debugWindow->clear();
  283. char s[128];
  284. //-------------------------------------------------------
  285. // For now, show the floating help text if we have one...
  286. if (((ObjectAppearance*)appearance)->objectNameId != -1) {
  287. char myName[255];
  288. cLoadString(((ObjectAppearance*)appearance)->objectNameId, myName, 254);
  289. debugWindow->print(myName);
  290. }
  291. else
  292. debugWindow->print("<no name>");
  293. sprintf(s, "team: %d, handle: %d, partID: %d %s", getTeamId(), getHandle(), getPartId(), getFlag(OBJECT_FLAG_CAPTURABLE) ? "[C]" : " ");
  294. debugWindow->print(s);
  295. sprintf(s, "objType: %d", getObjectType()->whatAmI());
  296. debugWindow->print(s);
  297. sprintf(s, "damage: %.2f/%.2f", getDamage(), getDamageLevel());
  298. debugWindow->print(s);
  299. sprintf(s, "pos: [%d, %d](area = %d)", cellPositionRow, cellPositionCol, GlobalMoveMap[0]->calcArea(cellPositionRow, cellPositionCol));
  300. debugWindow->print(s);
  301. sprintf(s, "footprint:[%d,%d]:[%d,%d]", cellFootprint[0], cellFootprint[1], cellFootprint[2], cellFootprint[3]);
  302. debugWindow->print(s);
  303. if (numSubAreas0 > 0) {
  304. sprintf(s, "subAreas:");
  305. for (long i = 0; i < numSubAreas0; i++) {
  306. char tempStr[15];
  307. sprintf(tempStr, " %d", subAreas0[i]);
  308. strcat(s, tempStr);
  309. }
  310. strcat(s, " *");
  311. for (i = 0; i < numSubAreas1; i++) {
  312. char tempStr[15];
  313. sprintf(tempStr, " %d", subAreas1[i]);
  314. strcat(s, tempStr);
  315. }
  316. debugWindow->print(s);
  317. }
  318. }
  319. //---------------------------------------------------------------------------
  320. bool TerrainObject::isVisible (void) {
  321. //----------------------------------------------------------------------
  322. // This function is the meat and potatoes of the object cull system.
  323. // Its job is to determine if the object is on screen or not.
  324. // It does this by transforming the position for each active camera to
  325. // its screen coords and saving them. It then checks each set of coords
  326. // to see if they are in the viewport of each camera. Returned value
  327. // is number of windows that object can be seen in.
  328. bool isVisible = false; //land->getVertexScreenPos(blockNumber, vertexNumber, screenPos);
  329. if (appearance)
  330. isVisible = appearance->recalcBounds();
  331. if (isVisible) {
  332. windowsVisible = turn;
  333. return(true);
  334. }
  335. return(false);
  336. }
  337. //---------------------------------------------------------------------------
  338. char* TerrainObject::getName (void) {
  339. /*
  340. static char* terrainObjectNames[NUM_TERROBJ_SUBTYPES] = {
  341. "Nothing",
  342. "Tree",
  343. "Bridge",
  344. "Forest",
  345. "Heavy Wall",
  346. "Medium Wall",
  347. "Light Wall"
  348. };
  349. TerrainObjectTypePtr type = (TerrainObjectTypePtr)getObjectType();
  350. return(terrainObjectNames[type->subType]);
  351. */
  352. if (((ObjectAppearance*)appearance)->objectNameId != -1) {
  353. cLoadString(((ObjectAppearance*)appearance)->objectNameId, lastName, 254);
  354. return(lastName);
  355. }
  356. return(NULL);
  357. }
  358. //---------------------------------------------------------------------------
  359. float TerrainObject::getStatusRating (void) {
  360. float curDamage = getDamage();
  361. float maxHealth = getDamageLevel();
  362. float rating = (maxHealth - curDamage) / maxHealth;
  363. if (rating < 0.0)
  364. rating = 0.0;
  365. return(rating);
  366. }
  367. //---------------------------------------------------------------------------
  368. #define BRIDGE_OFFSET 60
  369. long TerrainObject::update (void) {
  370. if (getFlag(OBJECT_FLAG_JUSTCREATED))
  371. {
  372. setFlag(OBJECT_FLAG_JUSTCREATED, false);
  373. setFlag(OBJECT_FLAG_TILECHANGED, false);
  374. TerrainObjectTypePtr type = (TerrainObjectTypePtr)ObjectManager->getObjectType(typeHandle);
  375. switch (type->subType) {
  376. case TERROBJ_NONE:
  377. case TERROBJ_TREE:
  378. {
  379. setTangible(true);
  380. }
  381. break;
  382. case TERROBJ_BRIDGE:
  383. if (!GameMap->getPassable(cellPositionRow, cellPositionCol)) {
  384. damage = type->getDamageLevel();
  385. setStatus(OBJECT_STATUS_DESTROYED);
  386. }
  387. break;
  388. case TERROBJ_FOREST:
  389. case TERROBJ_WALL_HEAVY:
  390. case TERROBJ_WALL_MEDIUM:
  391. case TERROBJ_WALL_LIGHT:
  392. if (GameMap->getPassable(cellPositionRow, cellPositionCol)) {
  393. damage = type->getDamageLevel();
  394. setStatus(OBJECT_STATUS_DESTROYED);
  395. }
  396. break;
  397. }
  398. }
  399. //-------------------------------------------
  400. // Handle power out.
  401. if (powerSupply && (ObjectManager->getByWatchID(powerSupply)->getStatus() == OBJECT_STATUS_DESTROYED))
  402. appearance->setLightsOut(true);
  403. if (appearance)
  404. {
  405. if (getFlag(OBJECT_FLAG_FALLING))
  406. {
  407. if (fallRate == 0.0f)
  408. {
  409. if (useSound && soundSystem)
  410. soundSystem->playDigitalSample(TREEFALL, getPosition(), true);
  411. fallRate = TREE_FALL_RATE;
  412. }
  413. else
  414. fallRate += TREE_FALL_ACCEL;
  415. pitchAngle -= (frameLength * fallRate);
  416. if (pitchAngle < -85.0f)
  417. {
  418. setFlag(OBJECT_FLAG_FALLEN,true);
  419. setFlag(OBJECT_FLAG_FALLING,false);
  420. }
  421. }
  422. appearance->setObjectParameters(position,rotation,FALSE,getTeamId(),Team::getRelation(getTeamId(), Team::home->getId()));
  423. appearance->setMoverParameters(pitchAngle);
  424. bool inView = appearance->recalcBounds();
  425. if (inView)
  426. {
  427. windowsVisible = turn;
  428. appearance->update();
  429. if (bldgDustPoofEffect && bldgDustPoofEffect->IsExecuted())
  430. {
  431. Stuff::Point3D actualPosition;
  432. Stuff::LinearMatrix4D shapeOrigin;
  433. Stuff::LinearMatrix4D localToWorld;
  434. actualPosition.x = -position.x;
  435. actualPosition.y = position.z;
  436. actualPosition.z = position.y;
  437. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  438. shapeOrigin.BuildTranslation(actualPosition);
  439. Stuff::OBB boundingBox;
  440. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,&boundingBox);
  441. bool result = bldgDustPoofEffect->Execute(&info);
  442. if (!result)
  443. {
  444. bldgDustPoofEffect->Kill();
  445. delete bldgDustPoofEffect;
  446. bldgDustPoofEffect = NULL;
  447. }
  448. }
  449. }
  450. }
  451. return(1);
  452. }
  453. //---------------------------------------------------------------------------
  454. void TerrainObject::render (void) {
  455. if (!getFlag(OBJECT_FLAG_JUSTCREATED))
  456. {
  457. }
  458. if (appearance->canBeSeen())
  459. {
  460. if (getSelected())
  461. {
  462. TerrainObjectTypePtr type = (TerrainObjectTypePtr)getObjectType();
  463. float barStatus = 1.0;
  464. float totalDmgLvl = type->getDamageLevel();
  465. if (totalDmgLvl > 0.0)
  466. barStatus -= getDamage() / totalDmgLvl;
  467. if (barStatus < 0.0)
  468. barStatus = 0.0;
  469. DWORD color = 0xff7f7f7f;
  470. appearance->setBarColor(color);
  471. appearance->setBarStatus(barStatus);
  472. }
  473. //For debug purposes only. Will crash pause!! Sorry Heidi!
  474. // if (windowsVisible != turn)
  475. // STOP(("Rendering without an update!"));
  476. appearance->setVisibility(true,true);
  477. appearance->render();
  478. //------------------------------------------------
  479. // Render GOSFX
  480. gosFX::Effect::DrawInfo drawInfo;
  481. drawInfo.m_clipper = theClipper;
  482. MidLevelRenderer::MLRState mlrState;
  483. mlrState.SetDitherOn();
  484. mlrState.SetTextureCorrectionOn();
  485. mlrState.SetZBufferCompareOn();
  486. mlrState.SetZBufferWriteOn();
  487. drawInfo.m_state = mlrState;
  488. drawInfo.m_clippingFlags = 0x0;
  489. if (bldgDustPoofEffect && bldgDustPoofEffect->IsExecuted())
  490. {
  491. Stuff::Point3D actualPosition;
  492. Stuff::LinearMatrix4D shapeOrigin;
  493. Stuff::LinearMatrix4D localToWorld;
  494. actualPosition.x = -position.x;
  495. actualPosition.y = position.z;
  496. actualPosition.z = position.y;
  497. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  498. shapeOrigin.BuildTranslation(actualPosition);
  499. drawInfo.m_parentToWorld = &shapeOrigin;
  500. if (!MLRVertexLimitReached)
  501. bldgDustPoofEffect->Draw(&drawInfo);
  502. }
  503. }
  504. setSelected(false); //ALWAYS reset the selected flags. GUI needs this to work!
  505. setTargeted( false ); //ALWAYS do it here, too! Otherwise things may draw FUNNY!
  506. }
  507. //---------------------------------------------------------------------------
  508. void TerrainObject::renderShadows (void)
  509. {
  510. if (getFlag(OBJECT_FLAG_FALLING) || getFlag(OBJECT_FLAG_FALLEN))
  511. return; //No shadows on fallen trees.
  512. if (appearance->canBeSeen())
  513. {
  514. appearance->renderShadows();
  515. }
  516. setSelected(false); //ALWAYS reset the selected flags. GUI needs this to work!
  517. setTargeted( false ); //ALWAYS do it here, too! Otherwise things may draw FUNNY!
  518. }
  519. //---------------------------------------------------------------------------
  520. void TerrainObject::destroy (void)
  521. {
  522. if (cellsCovered)
  523. {
  524. numCellsCovered = 0;
  525. systemHeap->Free(cellsCovered);
  526. cellsCovered = NULL;
  527. }
  528. if (subAreas0)
  529. {
  530. ObjectTypeManager::objectCache->Free(subAreas0);
  531. subAreas0 = NULL;
  532. }
  533. if (subAreas1)
  534. {
  535. ObjectTypeManager::objectCache->Free(subAreas1);
  536. subAreas1 = NULL;
  537. }
  538. //-----------------------------------------------------
  539. // This will free any memory the Building is using.
  540. if (appearance)
  541. {
  542. delete appearance;
  543. appearance = NULL;
  544. }
  545. }
  546. //---------------------------------------------------------------------------
  547. void TerrainObject::setDamage (long newDamage) {
  548. damage = (float)newDamage;
  549. TerrainObjectTypePtr type = (TerrainObjectTypePtr)getObjectType();
  550. switch (type->subType)
  551. {
  552. case TERROBJ_TREE:
  553. case TERROBJ_NONE:
  554. break;
  555. }
  556. //---------------------------------------------
  557. // Code needs to go in here to fix appearance
  558. if (damage >= getDamageLevel())
  559. {
  560. setStatus(OBJECT_STATUS_DESTROYED);
  561. if (appearance)
  562. appearance->setObjStatus(OBJECT_STATUS_DESTROYED);
  563. }
  564. }
  565. //---------------------------------------------------------------------------
  566. void TerrainObject::init (bool create, ObjectTypePtr objType) {
  567. GameObject::init(true, objType);
  568. setFlag(OBJECT_FLAG_JUSTCREATED, true);
  569. if (((TerrainObjectTypePtr)objType)->subType == TERROBJ_TREE)
  570. {
  571. //-------------------------------------------------------------
  572. // The appearance is initialized here using data from the type
  573. // Need an MLR appearance class
  574. char *appearanceName = objType->getAppearanceTypeName();
  575. //--------------------------------------------------------------
  576. // New code!!!
  577. // We need to append the sprite type to the appearance num now.
  578. // The MechEdit tool does not assume a sprite type, nor should it.
  579. // MechCmdr2 features much simpler objects which only use 1 type of sprite!
  580. long appearanceType = (TREED_TYPE << 24);
  581. AppearanceTypePtr terrainObjectAppearanceType = appearanceTypeList->getAppearance(appearanceType,appearanceName);
  582. if (!terrainObjectAppearanceType)
  583. {
  584. //------------------------------------------------------
  585. // LOAD a dummy appearance until real ones are available
  586. // for this building!
  587. terrainObjectAppearanceType = appearanceTypeList->getAppearance(appearanceType,"TREE");
  588. gosASSERT(terrainObjectAppearanceType != NULL);
  589. }
  590. appearance = new TreeAppearance;
  591. gosASSERT(appearance != NULL);
  592. appearance->init((TreeAppearanceType*)terrainObjectAppearanceType, (GameObjectPtr)this);
  593. }
  594. else
  595. {
  596. //-------------------------------------------------------------
  597. // The appearance is initialized here using data from the type
  598. // Need an MLR appearance class
  599. char *appearanceName = objType->getAppearanceTypeName();
  600. //--------------------------------------------------------------
  601. // New code!!!
  602. // We need to append the sprite type to the appearance num now.
  603. // The MechEdit tool does not assume a sprite type, nor should it.
  604. // MechCmdr2 features much simpler objects which only use 1 type of sprite!
  605. long appearanceType = (BLDG_TYPE << 24);
  606. AppearanceTypePtr terrainObjectAppearanceType = appearanceTypeList->getAppearance(appearanceType,appearanceName);
  607. if (!terrainObjectAppearanceType)
  608. {
  609. //------------------------------------------------------
  610. // LOAD a dummy appearance until real ones are available
  611. // for this building!
  612. terrainObjectAppearanceType = appearanceTypeList->getAppearance(appearanceType,"TESTOBJ");
  613. }
  614. appearance = new BldgAppearance;
  615. gosASSERT(appearance != NULL);
  616. //--------------------------------------------------------------
  617. // The only appearance type for buildings is MLR_APPEARANCE.
  618. gosASSERT(terrainObjectAppearanceType->getAppearanceClass() == BLDG_TYPE);
  619. appearance->init((BldgAppearanceType*)terrainObjectAppearanceType, (GameObjectPtr)this);
  620. }
  621. if (objType->getExtentRadius() > 0.0)
  622. setTangible(true);
  623. objectClass = TERRAINOBJECT;
  624. switch (((TerrainObjectTypePtr)objType)->subType) {
  625. case TERROBJ_NONE:
  626. if (((TerrainObjectTypePtr)objType)->getDamageLevel() == 0.0) {
  627. //--------------------------------------------------------
  628. // We are already destroyed. Used for extraction Markers
  629. setTangible(false);
  630. setStatus(OBJECT_STATUS_DESTROYED);
  631. }
  632. break;
  633. case TERROBJ_TREE:
  634. objectClass = TREE;
  635. setFlag(OBJECT_FLAG_DAMAGED, false);
  636. break;
  637. case TERROBJ_BRIDGE:
  638. case TERROBJ_FOREST:
  639. case TERROBJ_WALL_HEAVY:
  640. case TERROBJ_WALL_MEDIUM:
  641. case TERROBJ_WALL_LIGHT:
  642. setTangible(false);
  643. objectClass = BRIDGE;
  644. break;
  645. }
  646. }
  647. //---------------------------------------------------------------------------
  648. void TerrainObject::killFire (void) {
  649. }
  650. //---------------------------------------------------------------------------
  651. void TerrainObject::lightOnFire (float timeToBurn)
  652. {
  653. }
  654. #define DUST_POOF_ID 32
  655. //---------------------------------------------------------------------------
  656. long TerrainObject::handleWeaponHit (WeaponShotInfoPtr shotInfo, bool addMultiplayChunk) {
  657. if (!shotInfo)
  658. return(NO_ERR);
  659. if (addMultiplayChunk)
  660. MPlayer->addWeaponHitChunk(this, shotInfo);
  661. if (!getFlag(OBJECT_FLAG_DAMAGED))
  662. {
  663. float curDamage = getDamage();
  664. TerrainObjectTypePtr type = (TerrainObjectTypePtr)getObjectType();
  665. switch (type->subType)
  666. {
  667. case TERROBJ_NONE:
  668. curDamage += shotInfo->damage;
  669. if (curDamage > type->getDamageLevel())
  670. curDamage = type->getDamageLevel();
  671. setDamage(curDamage);
  672. if (curDamage >= type->getDamageLevel())
  673. {
  674. setFlag(OBJECT_FLAG_DAMAGED, true);
  675. curDamage = type->getDamageLevel();
  676. setStatus(OBJECT_STATUS_DESTROYED);
  677. setTangible(false);
  678. appearance->markLOS(true);
  679. appearance->setObjStatus(OBJECT_STATUS_DESTROYED);
  680. appearance->setLightsOut(true);
  681. appearance->recalcBounds();
  682. appearance->update();
  683. appearance->markLOS();
  684. if (!shotInfo->attackerWID && (shotInfo->masterId == -1)) //Somebody stepped on me.
  685. {
  686. //--------------------------------------------
  687. //Play a Dust Poof.
  688. if (useNonWeaponEffects)
  689. {
  690. unsigned flags = gosFX::Effect::ExecuteFlag;
  691. Check_Object(gosFX::EffectLibrary::Instance);
  692. gosFX::Effect::Specification* gosEffectSpec = gosFX::EffectLibrary::Instance->Find(weaponEffects->GetEffectName(DUST_POOF_ID));
  693. if (gosEffectSpec)
  694. {
  695. bldgDustPoofEffect = gosFX::EffectLibrary::Instance->MakeEffect(gosEffectSpec->m_effectID, flags);
  696. gosASSERT(bldgDustPoofEffect != NULL);
  697. }
  698. MidLevelRenderer::MLRTexturePool::Instance->LoadImages();
  699. }
  700. if (bldgDustPoofEffect)
  701. {
  702. Stuff::Point3D actualPosition;
  703. Stuff::LinearMatrix4D shapeOrigin;
  704. Stuff::LinearMatrix4D localToWorld;
  705. actualPosition.x = -position.x;
  706. actualPosition.y = position.z;
  707. actualPosition.z = position.y;
  708. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  709. shapeOrigin.BuildTranslation(actualPosition);
  710. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,NULL);
  711. bldgDustPoofEffect->Start(&info);
  712. }
  713. }
  714. }
  715. break;
  716. case TERROBJ_TREE:
  717. setDamage(curDamage + 1.0);
  718. if (curDamage > type->getDamageLevel())
  719. {
  720. setFlag(OBJECT_FLAG_DAMAGED, true);
  721. curDamage = type->getDamageLevel();
  722. setStatus(OBJECT_STATUS_DESTROYED);
  723. appearance->markLOS(true);
  724. appearance->setObjStatus(OBJECT_STATUS_DESTROYED);
  725. appearance->setLightsOut(true);
  726. appearance->recalcBounds();
  727. appearance->update();
  728. appearance->markLOS();
  729. }
  730. break;
  731. case TERROBJ_FOREST:
  732. {
  733. curDamage += shotInfo->damage;
  734. if (curDamage >= type->getDamageLevel())
  735. {
  736. type->createExplosion(position, 0, 0);
  737. setStatus(OBJECT_STATUS_DESTROYED);
  738. openSubAreas();
  739. }
  740. setDamage(curDamage);
  741. }
  742. break;
  743. case TERROBJ_WALL_HEAVY:
  744. case TERROBJ_WALL_MEDIUM:
  745. case TERROBJ_WALL_LIGHT:
  746. curDamage += shotInfo->damage;
  747. if (curDamage >= type->getDamageLevel())
  748. {
  749. type->createExplosion(position, 0, 0);
  750. setStatus(OBJECT_STATUS_DESTROYED);
  751. if (type->subType == TERROBJ_WALL_LIGHT)
  752. soundSystem->playDigitalSample(BREAKINGFENCE, getPosition(), true);
  753. }
  754. setDamage(curDamage);
  755. break;
  756. }
  757. }
  758. return(NO_ERR);
  759. }
  760. //---------------------------------------------------------------------------
  761. void TerrainObject::setTerrainPosition (const Stuff::Vector3D& position, const Stuff::Vector2DOf<long>& numbers)
  762. {
  763. setPosition( position );
  764. vertexNumber = numbers.x;
  765. blockNumber = numbers.y;
  766. }
  767. void TerrainObject::setRotation( float rot )
  768. {
  769. rotation = rot;
  770. ((ObjectAppearance*)appearance)->rotation = rot;
  771. }
  772. //---------------------------------------------------------------------------
  773. void TerrainObject::calcCellFootprint (Stuff::Vector3D& pos) {
  774. short cellList[MAX_CELL_COORDS];
  775. cellList[0] = MAX_CELL_COORDS;
  776. long numCoords = appearance->calcCellsCovered(pos, cellList);
  777. long minRow = 10000;
  778. long minCol = 10000;
  779. long maxRow = 0;
  780. long maxCol = 0;
  781. long curCoord = 0;
  782. while (curCoord < numCoords) {
  783. if (cellList[curCoord] < minRow)
  784. minRow = cellList[curCoord];
  785. if (cellList[curCoord] > maxRow)
  786. maxRow = cellList[curCoord];
  787. curCoord++;
  788. if (cellList[curCoord] < minCol)
  789. minCol = cellList[curCoord];
  790. if (cellList[curCoord] > maxCol)
  791. maxCol = cellList[curCoord];
  792. curCoord++;
  793. }
  794. if (numCoords)
  795. {
  796. cellFootprint[0] = minRow;
  797. cellFootprint[1] = minCol;
  798. cellFootprint[2] = maxRow;
  799. cellFootprint[3] = maxCol;
  800. land->cellToWorld(cellFootprint[0], cellFootprint[1], vectorFootprint[0]);
  801. land->cellToWorld(cellFootprint[0], cellFootprint[3], vectorFootprint[1]);
  802. land->cellToWorld(cellFootprint[2], cellFootprint[3], vectorFootprint[2]);
  803. land->cellToWorld(cellFootprint[2], cellFootprint[1], vectorFootprint[3]);
  804. }
  805. else
  806. {
  807. cellFootprint[0] = 0;
  808. cellFootprint[1] = 0;
  809. cellFootprint[2] = 0;
  810. cellFootprint[3] = 0;
  811. vectorFootprint[0].Zero();
  812. vectorFootprint[1].Zero();
  813. vectorFootprint[2].Zero();
  814. vectorFootprint[3].Zero();
  815. }
  816. }
  817. //---------------------------------------------------------------------------
  818. long TerrainObject::getLineOfSightNodes (long eyeCellRow, long eyeCellCol, long* cells) {
  819. cells[0] = cellFootprint[0];
  820. cells[1] = cellFootprint[1];
  821. cells[2] = cellFootprint[0];
  822. cells[3] = cellFootprint[3];
  823. cells[4] = cellFootprint[2];
  824. cells[5] = cellFootprint[3];
  825. cells[6] = cellFootprint[2];
  826. cells[7] = cellFootprint[1];
  827. return(4);
  828. }
  829. //---------------------------------------------------------------------------
  830. void TerrainObject::calcSubAreas (long numCells, short cells[MAX_GAME_OBJECT_CELLS][2]) {
  831. numCellsCovered = numCells;
  832. if (numCellsCovered) {
  833. cellsCovered = (short*)systemHeap->Malloc(4 * numCellsCovered);
  834. if (cellsCovered) {
  835. short* curCoord = cellsCovered;
  836. for (long j = 0; j < numCellsCovered; j++) {
  837. *curCoord++ = cells[j][0];
  838. *curCoord++ = cells[j][1];
  839. }
  840. }
  841. numSubAreas0 = 0;
  842. short* curCoord = cellsCovered;
  843. for (long i = 0; i < numCellsCovered; i++)
  844. {
  845. long r = *curCoord++;
  846. long c = *curCoord++;
  847. long area = GlobalMoveMap[0]->calcArea(r, c);
  848. bool addIt = true;
  849. for (long j = 0; j < numSubAreas0; j++)
  850. if (subAreas0[j] == area)
  851. {
  852. addIt = false;
  853. break;
  854. }
  855. if (addIt)
  856. {
  857. if (!subAreas0)
  858. {
  859. subAreas0 = (short *)ObjectTypeManager::objectCache->Malloc(sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  860. memset(subAreas0,0,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  861. }
  862. subAreas0[numSubAreas0++] = area;
  863. }
  864. }
  865. numSubAreas1 = 0;
  866. curCoord = cellsCovered;
  867. for (i = 0; i < numCellsCovered; i++)
  868. {
  869. long r = *curCoord++;
  870. long c = *curCoord++;
  871. long area = GlobalMoveMap[1]->calcArea(r, c);
  872. bool addIt = true;
  873. for (long j = 0; j < numSubAreas1; j++)
  874. if (subAreas1[j] == area)
  875. {
  876. addIt = false;
  877. break;
  878. }
  879. if (addIt)
  880. {
  881. if (!subAreas1)
  882. {
  883. subAreas1 = (short *)ObjectTypeManager::objectCache->Malloc(sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  884. memset(subAreas1,0,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  885. }
  886. subAreas1[numSubAreas1++] = area;
  887. }
  888. }
  889. for (i = 0; i < numSubAreas0; i++)
  890. GlobalMoveMap[0]->setAreaOwnerWID(subAreas0[i], getWatchID());
  891. for (i = 0; i < numSubAreas1; i++)
  892. GlobalMoveMap[1]->setAreaOwnerWID(subAreas1[i], getWatchID());
  893. }
  894. }
  895. //---------------------------------------------------------------------------
  896. void TerrainObject::markMoveMap (bool passable) {
  897. short* curCoord = cellsCovered;
  898. for (long i = 0; i < numCellsCovered; i++) {
  899. long r = *curCoord++;
  900. long c = *curCoord++;
  901. GameMap->setPassable(r, c, passable);
  902. if (passable)
  903. GameMap->setLocalHeight(r, c, 0.0f);
  904. }
  905. }
  906. //---------------------------------------------------------------------------
  907. void TerrainObject::openSubAreas (void) {
  908. markMoveMap(true);
  909. for (long i = 0; i < numSubAreas0; i++)
  910. GlobalMoveMap[0]->openArea(subAreas0[i]);
  911. for (i = 0; i < numSubAreas1; i++)
  912. GlobalMoveMap[1]->openArea(subAreas1[i]);
  913. }
  914. //---------------------------------------------------------------------------
  915. void TerrainObject::closeSubAreas (void) {
  916. markMoveMap(false);
  917. for (long i = 0; i < numSubAreas0; i++)
  918. GlobalMoveMap[0]->closeArea(subAreas0[i]);
  919. for (i = 0; i < numSubAreas1; i++)
  920. GlobalMoveMap[1]->closeArea(subAreas1[i]);
  921. }
  922. //---------------------------------------------------------------------------
  923. void TerrainObject::setSubAreasTeamId (long id) {
  924. for (long i = 0; i < numSubAreas0; i++)
  925. GlobalMoveMap[0]->setAreaTeamID(subAreas0[i], id);
  926. for (i = 0; i < numSubAreas1; i++)
  927. GlobalMoveMap[1]->setAreaTeamID(subAreas1[i], id);
  928. }
  929. //---------------------------------------------------------------------------
  930. bool TerrainObject::calcAdjacentAreaCell (long moveLevel, long areaID, long& adjRow, long& adjCol) {
  931. if (areaID == -1) {
  932. short* curCoord = cellsCovered;
  933. for (long i = 0; i < numCellsCovered; i++) {
  934. long cellRow = *curCoord++;
  935. long cellCol = *curCoord++;
  936. long adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow - 1, cellCol);
  937. if (adjArea > -1) {
  938. adjRow = cellRow - 1;
  939. adjCol = cellCol;
  940. return(true);
  941. }
  942. adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow, cellCol + 1);
  943. if (adjArea > -1) {
  944. adjRow = cellRow;
  945. adjCol = cellCol + 1;
  946. return(true);
  947. }
  948. adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow + 1, cellCol);
  949. if (adjArea > -1) {
  950. adjRow = cellRow + 1;
  951. adjCol = cellCol;
  952. return(true);
  953. }
  954. adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow, cellCol - 1);
  955. if (adjArea > -1) {
  956. adjRow = cellRow;
  957. adjCol = cellCol - 1;
  958. return(true);
  959. }
  960. }
  961. }
  962. else {
  963. short* curCoord = cellsCovered;
  964. for (long i = 0; i < numCellsCovered; i++) {
  965. long cellRow = *curCoord++;
  966. long cellCol = *curCoord++;
  967. long adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow - 1, cellCol);
  968. if (adjArea == areaID) {
  969. adjRow = cellRow - 1;
  970. adjCol = cellCol;
  971. return(true);
  972. }
  973. adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow, cellCol + 1);
  974. if (adjArea == areaID) {
  975. adjRow = cellRow;
  976. adjCol = cellCol + 1;
  977. return(true);
  978. }
  979. adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow + 1, cellCol);
  980. if (adjArea == areaID) {
  981. adjRow = cellRow + 1;
  982. adjCol = cellCol;
  983. return(true);
  984. }
  985. adjArea = GlobalMoveMap[moveLevel]->calcArea(cellRow, cellCol - 1);
  986. if (adjArea == areaID) {
  987. adjRow = cellRow;
  988. adjCol = cellCol - 1;
  989. return(true);
  990. }
  991. }
  992. }
  993. return(false);
  994. }
  995. //***************************************************************************
  996. void TerrainObject::Save (PacketFilePtr file, long packetNum)
  997. {
  998. TerrainObjectData data;
  999. CopyTo(&data);
  1000. //PacketNum incremented in ObjectManager!!
  1001. file->writePacket(packetNum,(MemoryPtr)&data,sizeof(TerrainObjectData),STORAGE_TYPE_ZLIB);
  1002. }
  1003. //***************************************************************************
  1004. void TerrainObject::CopyTo (TerrainObjectData *data)
  1005. {
  1006. data->damage = damage;
  1007. data->vertexNumber = vertexNumber;
  1008. data->blockNumber = blockNumber;
  1009. data->pitchAngle = pitchAngle;
  1010. data->fallRate = fallRate;
  1011. data->powerSupply = powerSupply;
  1012. memcpy(data->cellFootprint,cellFootprint,sizeof(short) * 4);
  1013. memcpy(data->vectorFootprint,vectorFootprint,sizeof(Stuff::Vector3D) * 4);
  1014. data->numSubAreas0 = numSubAreas0;
  1015. data->numSubAreas1 = numSubAreas1;
  1016. if (subAreas0)
  1017. memcpy(data->subAreas0,subAreas0,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1018. else
  1019. memset(data->subAreas0,0,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1020. if (subAreas1)
  1021. memcpy(data->subAreas1,subAreas1,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1022. else
  1023. memset(data->subAreas1,0,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1024. data->listID = listID;
  1025. data->numCellsCovered = numCellsCovered;
  1026. if (numCellsCovered >= 162)
  1027. STOP(("Object %d covers too many cells in Save/Load!!",getObjectType()->getObjTypeNum()));
  1028. memcpy(data->cellsCovered,cellsCovered, sizeof(short) * numCellsCovered * 2);
  1029. GameObject::CopyTo(dynamic_cast<GameObjectData *>(data));
  1030. }
  1031. //---------------------------------------------------------------------------
  1032. void TerrainObject::Load (TerrainObjectData *data)
  1033. {
  1034. GameObject::Load(dynamic_cast<GameObjectData *>(data));
  1035. damage = data->damage;
  1036. vertexNumber = data->vertexNumber;
  1037. blockNumber = data->blockNumber;
  1038. pitchAngle = data->pitchAngle;
  1039. fallRate = data->fallRate;
  1040. powerSupply = data->powerSupply;
  1041. memcpy(cellFootprint,data->cellFootprint,sizeof(short) * 4);
  1042. memcpy(vectorFootprint,data->vectorFootprint,sizeof(Stuff::Vector3D) * 4);
  1043. numSubAreas0 = data->numSubAreas0;
  1044. numSubAreas1 = data->numSubAreas1;
  1045. if (numSubAreas0)
  1046. {
  1047. subAreas0 = (short *)ObjectTypeManager::objectCache->Malloc(sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1048. memcpy(subAreas0,data->subAreas0,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1049. }
  1050. if (numSubAreas1)
  1051. {
  1052. subAreas1 = (short *)ObjectTypeManager::objectCache->Malloc(sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1053. memcpy(subAreas1,data->subAreas1,sizeof(short) * MAX_SPECIAL_SUB_AREAS);
  1054. }
  1055. listID = data->listID;
  1056. numCellsCovered = data->numCellsCovered;
  1057. if (numCellsCovered)
  1058. {
  1059. cellsCovered = (short*)systemHeap->Malloc(sizeof(short) * numCellsCovered * 2);
  1060. memcpy(cellsCovered, data->cellsCovered,sizeof(short) * numCellsCovered * 2);
  1061. }
  1062. }
  1063. //***************************************************************************