group.cpp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. //******************************************************************************************
  2. //
  3. // group.cpp - This file contains the MoverGroup Class header
  4. //
  5. // MechCOmmander 2
  6. //
  7. //---------------------------------------------------------------------------//
  8. // Copyright (C) Microsoft Corporation. All rights reserved. //
  9. //===========================================================================//
  10. #ifndef MCLIB_H
  11. #include "mclib.h"
  12. #endif
  13. #ifndef GROUP_H
  14. #include "group.h"
  15. #endif
  16. #ifndef GAMEOBJ_H
  17. #include "gameobj.h"
  18. #endif
  19. #ifndef MOVER_H
  20. #include "mover.h"
  21. #endif
  22. #ifndef OBJMGR_H
  23. #include "objmgr.h"
  24. #endif
  25. #ifndef GVEHICL_H
  26. #include "gvehicl.h"
  27. #endif
  28. #ifndef UNITDESG_H
  29. #include "unitdesg.h"
  30. #endif
  31. #ifndef TACORDR_H
  32. #include "tacordr.h"
  33. #endif
  34. #ifndef MOVE_H
  35. #include "move.h"
  36. #endif
  37. //---------------------------------------------------------------------------
  38. extern float DelayedOrderTime;
  39. extern long tileMulMAPCELL_DIM[MAX_MAP_CELL_WIDTH];
  40. //#define MAX_GROUPMOVE_OFFSETS 4
  41. /*
  42. long GroupMoveOffsetsIndex[MAX_GROUPMOVE_OFFSETS] = {0, 1, 3, 6};
  43. float GroupMoveOffsets[10][2] = {
  44. // 2-member Group
  45. {180.0, 50.0},
  46. // 3-member Group
  47. {-135.0, 50.0},
  48. {135.0, 50.0},
  49. // 4-member Group
  50. {-135.0, 50.0},
  51. {135.0, 50.0},
  52. {180.0, 50.0},
  53. // 5-member Group
  54. {-135.0, 50.0},
  55. {135.0, 50.0},
  56. {180.0, 50.0},
  57. {180.0, 75.0}
  58. };
  59. */
  60. extern char OverlayIsBridge[NUM_OVERLAY_TYPES];
  61. extern PriorityQueuePtr openList;
  62. GoalMapNode* MoverGroup::goalMap = NULL;
  63. //***************************************************************************
  64. // MOVERGROUP class
  65. //***************************************************************************
  66. void* MoverGroup::operator new (size_t ourSize) {
  67. void* result = systemHeap->Malloc(ourSize);
  68. return(result);
  69. }
  70. //---------------------------------------------------------------------------
  71. void MoverGroup::operator delete (void* us) {
  72. systemHeap->Free(us);
  73. }
  74. //---------------------------------------------------------------------------
  75. void MoverGroup::destroy (void) {
  76. }
  77. //---------------------------------------------------------------------------
  78. bool MoverGroup::add (MoverPtr mover) {
  79. if (numMovers == MAX_MOVERGROUP_COUNT) {
  80. Fatal(0, " MoverGroup.add: Group too big ");
  81. //----------------------------------------
  82. // Should we choose to remove the fatal...
  83. return(false);
  84. }
  85. moverWIDs[numMovers++] = mover->getWatchID();
  86. mover->setGroupId(id, true);
  87. return(true);
  88. }
  89. //---------------------------------------------------------------------------
  90. bool MoverGroup::remove (MoverPtr mover) {
  91. GameObjectWatchID moverWID = mover->getWatchID();
  92. if (moverWID == pointWID) {
  93. disband();
  94. return(true);
  95. }
  96. else {
  97. for (long i = 0; i < numMovers; i++)
  98. if (moverWIDs[i] == moverWID) {
  99. mover->setGroupId(-1, true);
  100. moverWIDs[i] = moverWIDs[numMovers - 1];
  101. moverWIDs[numMovers - 1] = 0;
  102. numMovers--;
  103. return(true);
  104. }
  105. }
  106. return(false);
  107. }
  108. //---------------------------------------------------------------------------
  109. bool MoverGroup::isMember (MoverPtr mover) {
  110. if (!mover)
  111. return(false);
  112. GameObjectWatchID moverWID = mover->getWatchID();
  113. for (long i = 0; i < numMovers; i++)
  114. if (moverWIDs[i] == moverWID)
  115. return(true);
  116. return(false);
  117. }
  118. //---------------------------------------------------------------------------
  119. long MoverGroup::disband (void) {
  120. for (long i = 0; i < numMovers; i++) {
  121. MoverPtr mover = getMover(i);
  122. mover->setGroupId(-1, true);
  123. }
  124. #ifdef USE_IFACE
  125. if (pointHandle)
  126. theInterface->setPoint(pointHandle, false);
  127. #endif
  128. pointWID = 0;
  129. numMovers = 0;
  130. return(NO_ERR);
  131. }
  132. //---------------------------------------------------------------------------
  133. long MoverGroup::setPoint (MoverPtr mover) {
  134. if (isMember(mover)) {
  135. #ifdef USE_IFACE
  136. if (pointHandle)
  137. theInterface->setPoint(pointHandle, false);
  138. #endif
  139. pointWID = mover->getWatchID();
  140. #ifdef USE_IFACE
  141. theInterface->setPoint(mover->getPartId(), true);
  142. #endif
  143. }
  144. return(NO_ERR);
  145. }
  146. //---------------------------------------------------------------------------
  147. MoverPtr MoverGroup::getPoint (void) {
  148. return(dynamic_cast<MoverPtr>(ObjectManager->getByWatchID(pointWID)));
  149. }
  150. //---------------------------------------------------------------------------
  151. MoverPtr MoverGroup::getMover (long i) {
  152. MoverPtr mover = dynamic_cast<MoverPtr>(ObjectManager->getByWatchID(moverWIDs[i]));
  153. if (!mover)
  154. Fatal(0, " MoverGroup.getMover: NULL mover ");
  155. return(mover);
  156. }
  157. //---------------------------------------------------------------------------
  158. MoverPtr MoverGroup::selectPoint (bool excludePoint) {
  159. for (long i = 0; i < numMovers; i++)
  160. if (!excludePoint || (moverWIDs[i] != pointWID)) {
  161. MoverPtr mover = getMover(i);
  162. MechWarriorPtr pilot = mover->getPilot();
  163. if (pilot && pilot->alive()) {
  164. //----------------------------------------
  165. // Found a legitimate point, so set him...
  166. setPoint(mover);
  167. return(mover);
  168. }
  169. }
  170. //-----------------------
  171. // No legitimate point...
  172. setPoint(NULL);
  173. return(NULL);
  174. }
  175. //---------------------------------------------------------------------------
  176. long MoverGroup::getMovers (MoverPtr* moverList) {
  177. if (numMovers > 0)
  178. for (long i = 0; i < numMovers; i++)
  179. moverList[i] = getMover(i);
  180. return(numMovers);
  181. }
  182. //---------------------------------------------------------------------------
  183. MechWarriorPtr MoverGroup::getPointPilot (void) {
  184. if (pointWID)
  185. return(ObjectManager->getByWatchID(pointWID)->getPilot());
  186. return(NULL);
  187. }
  188. //---------------------------------------------------------------------------
  189. void MoverGroup::statusCount (long* statusTally) {
  190. for (long i = 0; i < numMovers; i++) {
  191. MoverPtr mover = getMover(i);
  192. MechWarriorPtr pilot = mover->getPilot();
  193. if (!mover->getExists())
  194. statusTally[8]++;
  195. else if (!mover->getAwake())
  196. statusTally[7]++;
  197. else if (pilot && (pilot->getStatus() == WARRIOR_STATUS_WITHDRAWN))
  198. statusTally[6]++;
  199. else
  200. statusTally[mover->getStatus()]++;
  201. }
  202. }
  203. //---------------------------------------------------------------------------
  204. void MoverGroup::addToGUI (bool visible) {
  205. #ifdef USE_IFACE
  206. for (long i = 0; i < numMovers; i++)
  207. theInterface->AddMech(movers[i]->getPartId(), id, movers[i]->getAwake(), visible);
  208. #endif
  209. }
  210. //---------------------------------------------------------------------------
  211. inline bool inMapBounds (long r, long c, long mapHeight, long mapWidth) {
  212. return((r >= 0) && (r < mapHeight) && (c >= 0) && (c < mapWidth));
  213. }
  214. //---------------------------------------------------------------------------
  215. #define JUMPMAP_TILE_DIM 3
  216. #define JUMPMAP_CELL_DIM MAPCELL_DIM * JUMPMAP_TILE_DIM
  217. char CellSpiralIncrement[JUMPMAP_CELL_DIM * JUMPMAP_CELL_DIM * 2] = {
  218. -1, 0,
  219. 0, 1,
  220. 1, 0,
  221. 1, 0,
  222. 0, -1,
  223. 0, -1,
  224. -1, 0,
  225. -1, 0,
  226. -1, 0,
  227. 0, 1,
  228. 0, 1,
  229. 0, 1,
  230. 1, 0,
  231. 1, 0,
  232. 1, 0,
  233. 1, 0,
  234. 0, -1,
  235. 0, -1,
  236. 0, -1,
  237. 0, -1,
  238. -1, 0,
  239. -1, 0,
  240. -1, 0,
  241. -1, 0,
  242. -1, 0,
  243. 0, 1,
  244. 0, 1,
  245. 0, 1,
  246. 0, 1,
  247. 0, 1,
  248. 1, 0,
  249. 1, 0,
  250. 1, 0,
  251. 1, 0,
  252. 1, 0,
  253. 1, 0,
  254. 0, -1,
  255. 0, -1,
  256. 0, -1,
  257. 0, -1,
  258. 0, -1,
  259. 0, -1,
  260. -1, 0,
  261. -1, 0,
  262. -1, 0,
  263. -1, 0,
  264. -1, 0,
  265. -1, 0,
  266. -1, 0,
  267. 0, 1,
  268. 0, 1,
  269. 0, 1,
  270. 0, 1,
  271. 0, 1,
  272. 0, 1,
  273. 0, 1,
  274. 1, 0,
  275. 1, 0,
  276. 1, 0,
  277. 1, 0,
  278. 1, 0,
  279. 1, 0,
  280. 1, 0,
  281. 1, 0,
  282. 0, -1,
  283. 0, -1,
  284. 0, -1,
  285. 0, -1,
  286. 0, -1,
  287. 0, -1,
  288. 0, -1,
  289. 0, -1,
  290. -1, 0,
  291. -1, 0,
  292. -1, 0,
  293. -1, 0,
  294. -1, 0,
  295. -1, 0,
  296. -1, 0,
  297. -1, 0
  298. };
  299. //---------------------------------------------------------------------------
  300. void MoverGroup::sortMovers (long numMoversInGroup, MoverPtr* moverList, Stuff::Vector3D destination) {
  301. Mover::sortList->clear();
  302. for (long i = 0; i < numMoversInGroup; i++) {
  303. long index = -1;
  304. float dist = (float)3.48E+37;
  305. if (moverList[i]) {
  306. index = i;
  307. Stuff::Vector3D resultVector;
  308. resultVector.Subtract(moverList[i]->getPosition(), destination);
  309. dist = resultVector.GetLength();
  310. }
  311. Mover::sortList->setId(i, index);
  312. Mover::sortList->setValue(i, dist);
  313. }
  314. Mover::sortList->sort(false);
  315. for (i = 0; i < numMoversInGroup; i++) {
  316. long moverIndex = Mover::sortList->getId(i);
  317. if (moverIndex != -1)
  318. moverList[moverIndex]->selectionIndex = i;
  319. }
  320. }
  321. //---------------------------------------------------------------------------
  322. #define GOALMAP_DIM 61
  323. long MoverGroup::calcMoveGoals (Stuff::Vector3D goal, long numMovers, Stuff::Vector3D* goalList) {
  324. if ( !numMovers ) // 07/24/HKG: crashes if no movers
  325. return 0;
  326. if (!goalMap) {
  327. goalMap = (GoalMapNode*)systemHeap->Malloc(sizeof(GoalMapNode) * GOALMAP_DIM * GOALMAP_DIM);
  328. if (!goalMap)
  329. Fatal(0, " MoverGroup.calcMoveGoals: unable to malloc goalMap ");
  330. }
  331. long goalRow, goalCol;
  332. land->worldToCell(goal, goalRow, goalCol);
  333. long topLeftRow = goalRow - GOALMAP_DIM / 2;
  334. long topLeftCol = goalCol - GOALMAP_DIM / 2;
  335. for (long r = 0; r < GOALMAP_DIM; r++)
  336. for (long c = 0; c < GOALMAP_DIM; c++) {
  337. if (!inMapBounds(topLeftRow + r, topLeftCol + c, GameMap->height, GameMap->width))
  338. continue;
  339. long index = r * GOALMAP_DIM + c;
  340. goalMap[index].cost = GameMap->getPassable(topLeftRow + r, topLeftCol + c) ? 100 : COST_BLOCKED;
  341. goalMap[index].flags = GOALFLAG_AVAILABLE + GOALFLAG_NO_NEIGHBORS;
  342. goalMap[index].g = 0;
  343. }
  344. //-----------------------------------------------
  345. // If we haven't already, create the OPEN list...
  346. if (!openList) {
  347. openList = new PriorityQueue;
  348. gosASSERT(openList != NULL);
  349. openList->init(5000);
  350. }
  351. long curRow = GOALMAP_DIM / 2;
  352. long curCol = GOALMAP_DIM / 2;
  353. GoalMapNode* curMapNode = &goalMap[curRow * GOALMAP_DIM + curCol];
  354. //-----------------------------------------------------------------
  355. // Put the START (the goal, in this case) on the empty OPEN list...
  356. PQNode initialVertex;
  357. initialVertex.key = curMapNode->g;
  358. initialVertex.id = curRow * GOALMAP_DIM + curCol;
  359. initialVertex.row = curRow;
  360. initialVertex.col = curCol;
  361. openList->clear();
  362. openList->insert(initialVertex);
  363. curMapNode->setFlag(GOALFLAG_OPEN);
  364. long numGoalsFound = 0;
  365. while (!openList->isEmpty()) {
  366. //----------------------
  367. // Grab the best node...
  368. PQNode bestPQNode;
  369. openList->remove(bestPQNode);
  370. curRow = bestPQNode.row;
  371. curCol = bestPQNode.col;
  372. GoalMapNode* bestMapNode = &goalMap[curRow * GOALMAP_DIM + curCol];
  373. bestMapNode->clearFlag(GOALFLAG_OPEN);
  374. //----------------------------
  375. // Now, close the best node...
  376. bestMapNode->setFlag(GOALFLAG_CLOSED);
  377. bool markNeighbors = false;
  378. //------------------------------
  379. // Have we found a valid goal...
  380. if (bestMapNode->cost < COST_BLOCKED)
  381. if (bestMapNode->flags & GOALFLAG_NO_NEIGHBORS)
  382. if (bestMapNode->flags & GOALFLAG_AVAILABLE) {
  383. bestMapNode->clearFlag(GOALFLAG_AVAILABLE);
  384. land->cellToWorld(topLeftRow + curRow, topLeftCol + curCol, goalList[numGoalsFound++]);
  385. markNeighbors = true;
  386. }
  387. if (numGoalsFound == numMovers)
  388. break;
  389. if (markNeighbors) {
  390. static long cellShift[8][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
  391. for (long i = 0; i < 8; i++) {
  392. long nRow = curRow + cellShift[i][0];
  393. long nCol = curRow + cellShift[i][1];
  394. if (inMapBounds(nRow, nCol, GOALMAP_DIM, GOALMAP_DIM))
  395. goalMap[nRow * GOALMAP_DIM + nCol].clearFlag(GOALFLAG_NO_NEIGHBORS);
  396. }
  397. }
  398. long bestNodeG = bestMapNode->g;
  399. for (long dir = 0; dir < 4; dir++) {
  400. //------------------------------------------------------------
  401. // First, make sure this is a legit direction to go. We do NOT
  402. // move diagonally, when checking these cells...
  403. //------------------------------------------------------------
  404. // Now, process this direction. First, calc the cell to check,
  405. // offset from the current cell...
  406. static long cellShift[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
  407. long succRow = curRow + cellShift[dir][0];
  408. long succCol = curCol + cellShift[dir][1];
  409. //--------------------------------
  410. // If it's on the map, check it...
  411. if (inMapBounds(succRow, succCol, GOALMAP_DIM, GOALMAP_DIM)) {
  412. GoalMapNode* succMapNode = &goalMap[succRow * GOALMAP_DIM + succCol];
  413. if (succMapNode->cost < COST_BLOCKED) {
  414. long succNodeG = bestNodeG + succMapNode->cost;
  415. if ((succMapNode->flags & (GOALFLAG_OPEN + GOALFLAG_CLOSED)) == 0) {
  416. //-------------------------------------------------
  417. // This node is neither OPEN nor CLOSED, so toss it
  418. // into the OPEN list...
  419. //succMapNode->parent = dirToParent;
  420. succMapNode->g = succNodeG;
  421. PQNode succPQNode;
  422. succPQNode.key = succMapNode->g;
  423. succPQNode.id = succRow * GOALMAP_DIM + succCol;
  424. succPQNode.row = succRow;
  425. succPQNode.col = succCol;
  426. openList->insert(succPQNode);
  427. succMapNode->setFlag(MOVEFLAG_OPEN);
  428. }
  429. }
  430. }
  431. }
  432. }
  433. return(numGoalsFound);
  434. }
  435. //---------------------------------------------------------------------------
  436. #define DEBUGJUMPGOALS TRUE
  437. long MoverGroup::calcJumpGoals (Stuff::Vector3D goal, long numMovers, Stuff::Vector3D* goalList, GameObjectPtr DFATarget) {
  438. long numJumping = 0;
  439. //-----------------------------
  440. // First, build the jump map...
  441. long jumpMap[JUMPMAP_CELL_DIM][JUMPMAP_CELL_DIM];
  442. //------------------------------------------------------------
  443. // The initial goal tile is placed at the center of the map...
  444. long goalCell[2] = {0, 0};
  445. land->worldToCell(goal, goalCell[0], goalCell[1]);
  446. long mapCellUL[2] = {0, 0};
  447. mapCellUL[0] = goalCell[0] - JUMPMAP_CELL_DIM / 2;
  448. mapCellUL[1] = goalCell[1] - JUMPMAP_CELL_DIM / 2;
  449. // -1 = OPEN
  450. // -2 = BLOCKED
  451. // 0 thru # = already selected for that # mover in the group
  452. for (long r = 0; r < JUMPMAP_CELL_DIM; r++)
  453. for (long c = 0; c < JUMPMAP_CELL_DIM; c++) {
  454. long cellRow = mapCellUL[0] + r;
  455. long cellCol = mapCellUL[1] + c;
  456. if (GameMap->inBounds(cellRow, cellCol)) {
  457. MapCellPtr mapCell = GameMap->getCell(cellRow, cellCol);
  458. //-----------------------
  459. // Tile (terrain) type...
  460. //long tileType = curTile.getTileType();
  461. if (!mapCell->getPassable())
  462. jumpMap[r][c] = -2;
  463. else
  464. jumpMap[r][c] = -1;
  465. #ifdef USE_OVERLAYS_IN_MC2
  466. long overlay = mapCell->getOverlay();
  467. if (OverlayIsBridge[overlay]) {
  468. switch (overlay) {
  469. case OVERLAY_WATER_BRIDGE_NS:
  470. case OVERLAY_RAILROAD_WATER_BRIDGE_NS:
  471. jumpMap[row][col] = -2;
  472. jumpMap[row][col + 2] = -2;
  473. jumpMap[row + 1][col] = -2;
  474. jumpMap[row + 1][col + 2] = -2;
  475. jumpMap[row + 2][col] = -2;
  476. jumpMap[row + 2][col + 2] = -2;
  477. break;
  478. case OVERLAY_WATER_BRIDGE_EW:
  479. case OVERLAY_RAILROAD_WATER_BRIDGE_EW:
  480. jumpMap[row][col] = -2;
  481. jumpMap[row][col + 1] = -2;
  482. jumpMap[row][col + 2] = -2;
  483. jumpMap[row + 2][col] = -2;
  484. jumpMap[row + 2][col + 1] = -2;
  485. jumpMap[row + 2][col + 2] = -2;
  486. break;
  487. case OVERLAY_WATER_BRIDGE_NS_DESTROYED:
  488. case OVERLAY_RAILROAD_WATER_BRIDGE_NS_DESTROYED:
  489. case OVERLAY_WATER_BRIDGE_EW_DESTROYED:
  490. case OVERLAY_RAILROAD_WATER_BRIDGE_EW_DESTROYED:
  491. jumpMap[row][col] = -2;
  492. jumpMap[row][col + 1] = -2;
  493. jumpMap[row][col + 2] = -2;
  494. jumpMap[row + 1][col] = -2;
  495. jumpMap[row + 1][col + 1] = -2;
  496. jumpMap[row + 1][col + 2] = -2;
  497. jumpMap[row + 2][col] = -2;
  498. jumpMap[row + 2][col + 1] = -2;
  499. jumpMap[row + 2][col + 2] = -2;
  500. break;
  501. }
  502. }
  503. #endif
  504. }
  505. else
  506. jumpMap[r][c] = -2;
  507. }
  508. long moverCount = ObjectManager->getNumMovers();
  509. for (long i = 0; i < moverCount; i++) {
  510. MoverPtr mover = ObjectManager->getMover(i);
  511. if ((mover->getObjectClass() != ELEMENTAL) && (mover != DFATarget) && !mover->isDisabled()) {
  512. long mapCellRow, mapCellCol;
  513. mover->getCellPosition(mapCellRow, mapCellCol);
  514. mapCellRow -= mapCellUL[0];
  515. mapCellCol -= mapCellUL[1];
  516. if (inMapBounds(mapCellRow, mapCellCol, JUMPMAP_CELL_DIM, JUMPMAP_CELL_DIM))
  517. jumpMap[mapCellRow][mapCellCol] = -2;
  518. }
  519. }
  520. #ifdef _DEBUG
  521. #if DEBUGJUMPGOALS
  522. char debugStr[256];
  523. sprintf(debugStr, "GROUP JUMP(%.2f,%.2f,%.2f)--UL = %d,%d: ", goal.x, goal.y, goal.z,mapCellUL[0], mapCellUL[1]);
  524. #endif
  525. #endif
  526. //-----------------------------------------------------------------
  527. // Now, for each jumper, select a closest cell to the goal, mark it
  528. // as theirs and close it...
  529. for (i = 0; i < numMovers; i++) {
  530. //long startCellRow = 0;
  531. //long startCellCol = 0;
  532. long curCellRow = goalCell[0] - mapCellUL[0];
  533. long curCellCol = goalCell[1] - mapCellUL[1];
  534. bool notFound = true;
  535. long spiralIndex = 0;
  536. while (notFound) {
  537. if (jumpMap[curCellRow][curCellCol] == -1) {
  538. // Should check to see if the cell is within range...
  539. //----------------------------------
  540. // Found an open cell, so take it...
  541. jumpMap[curCellRow][curCellCol] = i;
  542. land->cellToWorld(mapCellUL[0] + curCellRow, mapCellUL[1] + curCellCol, goalList[i]);
  543. numJumping++;
  544. notFound = false;
  545. #ifdef _DEBUG
  546. #if DEBUGJUMPGOALS
  547. char s[30];
  548. sprintf(s, "[%d,%d] ", curCellRow, curCellCol);
  549. strcat(debugStr, s);
  550. #endif
  551. #endif
  552. }
  553. else {
  554. //-------------------------------------
  555. // Go to the next cell in our search...
  556. do {
  557. if (spiralIndex == (JUMPMAP_CELL_DIM * JUMPMAP_CELL_DIM * 2)) {
  558. goalList[i].x = -99999.0;
  559. goalList[i].y = -99999.0;
  560. goalList[i].z = -99999.0;
  561. notFound = false;
  562. break;
  563. }
  564. curCellRow += CellSpiralIncrement[spiralIndex++];
  565. curCellCol += CellSpiralIncrement[spiralIndex++];
  566. } while (!inMapBounds(curCellRow, curCellCol, JUMPMAP_CELL_DIM, JUMPMAP_CELL_DIM));
  567. }
  568. }
  569. }
  570. return(numJumping);
  571. }
  572. //---------------------------------------------------------------------------
  573. long MoverGroup::calcJumpGoals (Stuff::Vector3D goal, Stuff::Vector3D* goalList, GameObjectPtr DFATarget) {
  574. return(calcJumpGoals(goal, numMovers, goalList, DFATarget));
  575. }
  576. //---------------------------------------------------------------------------
  577. long MoverGroup::handleTacticalOrder (TacticalOrder tacOrder, long priority, Stuff::Vector3D* jumpGoalList, bool queueGroupOrder) {
  578. if (numMovers == 0)
  579. return(NO_ERR);
  580. if (queueGroupOrder)
  581. tacOrder.pack(NULL, NULL);
  582. //bool processOrder = true;
  583. bool isJump = false;
  584. bool isMove = false;
  585. Stuff::Vector3D goalList[MAX_MOVERGROUP_COUNT];
  586. Stuff::Vector3D location = tacOrder.getWayPoint(0);
  587. //MoverPtr pointVehicle = getPoint();
  588. if (tacOrder.code == TACTICAL_ORDER_ATTACK_OBJECT)
  589. if (tacOrder.attackParams.method == ATTACKMETHOD_DFA) {
  590. //-------------------------------------------------
  591. // Let's just make it a move/jump order, for now...
  592. tacOrder.code = TACTICAL_ORDER_JUMPTO_OBJECT;
  593. tacOrder.moveParams.wait = false;
  594. tacOrder.moveParams.wayPath.mode[0] = TRAVEL_MODE_SLOW;
  595. GameObjectPtr target = ObjectManager->getByWatchID(tacOrder.targetWID);
  596. Assert(tacOrder.targetWID != 0, 0, " DFA AttackObject WID is 0 ");
  597. if (!target)
  598. return(NO_ERR);
  599. tacOrder.setWayPoint(0, target->getPosition());
  600. }
  601. if (tacOrder.code == TACTICAL_ORDER_JUMPTO_OBJECT) {
  602. tacOrder.code = TACTICAL_ORDER_JUMPTO_POINT;
  603. GameObjectPtr target = ObjectManager->get(tacOrder.targetWID);
  604. Assert(tacOrder.targetWID != 0, 0, " DFA AttackObject WID is 0 ");
  605. if (!target)
  606. return(NO_ERR);
  607. tacOrder.setWayPoint(0, target->getPosition());
  608. }
  609. //vector_3d offsetTable[MAX_GROUPMOVE_OFFSETS];
  610. //long numOffsets = 0;
  611. switch (tacOrder.code) {
  612. case TACTICAL_ORDER_WAIT:
  613. break;
  614. case TACTICAL_ORDER_MOVETO_POINT:
  615. case TACTICAL_ORDER_MOVETO_OBJECT: {
  616. Fatal(0, "Need to support jumpGoalList (and goalList) for MOVETO as well in mc2 ");
  617. isMove = true;
  618. //-----------------------------------------------------------
  619. // Sort by distance to destination. Their selectionIndex will
  620. // be set to modify this goal...
  621. SortListPtr list = Mover::sortList;
  622. if (list) {
  623. list->clear(false);
  624. long moverCount = 0;
  625. for (long i = 0; i < numMovers; i++) {
  626. MoverPtr mover = getMover(i);
  627. Assert(mover != NULL, moverWIDs[i], " MoverGroup.handleTacticalOrder: NULL mover ");
  628. if (!mover->isDisabled()) {
  629. list->setId(moverCount, i);
  630. list->setValue(moverCount, mover->distanceFrom(location));
  631. moverCount++;
  632. }
  633. }
  634. list->sort(false);
  635. //--------------------------------
  636. // Let's build the offset table...
  637. /*
  638. numOffsets = moverCount - 1;
  639. if (numOffsets > MAX_GROUPMOVE_OFFSETS)
  640. numOffsets = MAX_GROUPMOVE_OFFSETS;
  641. long offsetsStart = GroupMoveOffsetsIndex[numOffsets - 1];
  642. for (i = 0; i < numOffsets; i++)
  643. offsetTable[i] = relativePositionToPoint(location, GroupMoveOffsets[offsetsStart + i][0], GroupMoveOffsets[offsetsStart + i][1], RELPOS_FLAG_PASSABLE_START);
  644. */
  645. //-----------------------------------
  646. // Now, calc the order of movement...
  647. long curIndex = 1;
  648. for (i = 0; i < moverCount; i++) {
  649. MoverPtr mover = getMover(list->getId(i));
  650. if (mover->getWatchID() == pointWID)
  651. mover->selectionIndex = 0;
  652. else
  653. mover->selectionIndex = curIndex++;
  654. }
  655. }
  656. }
  657. break;
  658. case TACTICAL_ORDER_JUMPTO_POINT:
  659. case TACTICAL_ORDER_JUMPTO_OBJECT: {
  660. //-----------------------------------------------------------
  661. // Sort by distance to destination. Their selectionIndex will
  662. // be set to modify this goal...
  663. isJump = true;
  664. //-------------------------------------------------------------------------
  665. // We can assume that all movers in this group are jump-capable. Otherwise,
  666. // the group wouldn't be allowed to jump by the interface. In addition,
  667. // we KNOW that all movers in this group can jump to the selected
  668. // goal (of course, they won't due to terrain and crowding)...
  669. GameObjectPtr target = ObjectManager->getByWatchID(tacOrder.targetWID);
  670. if (jumpGoalList)
  671. for (long j = 0; j < numMovers; j++)
  672. goalList[j] = jumpGoalList[j];
  673. else
  674. calcJumpGoals(tacOrder.getWayPoint(0), goalList, target);
  675. for (long i = 0; i < numMovers; i++) {
  676. MoverPtr mover = getMover(i);
  677. bool canJump = (goalList[i].x > -99000.0);
  678. if (canJump)
  679. mover->selectionIndex = 0;
  680. else
  681. mover->selectionIndex = -2;
  682. }
  683. }
  684. break;
  685. case TACTICAL_ORDER_TRAVERSE_PATH:
  686. case TACTICAL_ORDER_PATROL_PATH:
  687. case TACTICAL_ORDER_ESCORT:
  688. case TACTICAL_ORDER_FOLLOW:
  689. case TACTICAL_ORDER_GUARD:
  690. case TACTICAL_ORDER_STOP:
  691. case TACTICAL_ORDER_POWERUP:
  692. case TACTICAL_ORDER_POWERDOWN:
  693. case TACTICAL_ORDER_WAYPOINTS_DONE:
  694. case TACTICAL_ORDER_EJECT:
  695. case TACTICAL_ORDER_ATTACK_OBJECT:
  696. case TACTICAL_ORDER_ATTACK_POINT:
  697. case TACTICAL_ORDER_HOLD_FIRE:
  698. case TACTICAL_ORDER_WITHDRAW:
  699. case TACTICAL_ORDER_CAPTURE:
  700. case TACTICAL_ORDER_LOAD_INTO_CARRIER:
  701. case TACTICAL_ORDER_REFIT:
  702. case TACTICAL_ORDER_RECOVER:
  703. case TACTICAL_ORDER_GETFIXED:
  704. break;
  705. default: {
  706. char s[256];
  707. sprintf(s, "Unit::handleTacticalOrder->Bad TacOrder Code (%d)", tacOrder.code);
  708. Assert(false, tacOrder.code, s);
  709. return(1);
  710. }
  711. }
  712. tacOrder.unitOrder = true;
  713. for (long i = 0; i < numMovers; i++) {
  714. MoverPtr mover = getMover(i);
  715. if (mover && !mover->isDisabled()) {
  716. if (mover->selectionIndex == -2) {
  717. mover->selectionIndex = -1;
  718. continue;
  719. }
  720. tacOrder.selectionIndex = mover->selectionIndex;
  721. if (tacOrder.selectionIndex != -1) {
  722. if (isMove)
  723. tacOrder.setWayPoint(0, location);
  724. else if (isJump)
  725. tacOrder.setWayPoint(0, goalList[i]);
  726. tacOrder.delayedTime = scenarioTime + (mover->selectionIndex * DelayedOrderTime);
  727. }
  728. switch (tacOrder.origin) {
  729. case ORDER_ORIGIN_PLAYER:
  730. if (queueGroupOrder)
  731. mover->getPilot()->addQueuedTacOrder(tacOrder);
  732. else {
  733. if (mover->getPilot()->getNumTacOrdersQueued())
  734. //This is a hack to simply trigger the execution of
  735. //the queued orders. The current order is ignored (and
  736. //is simply used for this trigger)...
  737. mover->getPilot()->executeTacOrderQueue();
  738. else
  739. mover->getPilot()->setPlayerTacOrder(tacOrder);
  740. }
  741. break;
  742. case ORDER_ORIGIN_COMMANDER:
  743. mover->getPilot()->setGeneralTacOrder(tacOrder);
  744. break;
  745. case ORDER_ORIGIN_SELF:
  746. mover->getPilot()->setAlarmTacOrder(tacOrder, priority);
  747. break;
  748. }
  749. mover->selectionIndex = -1;
  750. }
  751. }
  752. return(NO_ERR);
  753. }
  754. //---------------------------------------------------------------------------
  755. // TAC ORDERS
  756. //---------------------------------------------------------------------------
  757. long MoverGroup::orderMoveToPoint (bool setTacOrder, long origin, Stuff::Vector3D location, unsigned long params) {
  758. long result = TACORDER_FAILURE;
  759. for (long i = 0; i < numMovers; i++) {
  760. Assert(getMover(i) != NULL, 0, " MoverGroup.orderMoveToPoint: NULL mover ");
  761. MechWarriorPtr pilot = getMover(i)->getPilot();
  762. if (pilot)
  763. result = pilot->orderMoveToPoint(true, setTacOrder, origin, location, -1, params);
  764. }
  765. return(result);
  766. }
  767. //---------------------------------------------------------------------------
  768. long MoverGroup::orderMoveToObject (bool setTacOrder, long origin, GameObjectPtr target, long fromArea, unsigned long params) {
  769. long result = TACORDER_FAILURE;
  770. for (long i = 0; i < numMovers; i++) {
  771. Assert(getMover(i) != NULL, 0, " MoverGroup.orderMoveToObject: NULL mover ");
  772. MechWarriorPtr pilot = getMover(i)->getPilot();
  773. if (pilot)
  774. result = pilot->orderMoveToObject(true, setTacOrder, origin, target, fromArea, -1, params);
  775. }
  776. return(result);
  777. }
  778. //---------------------------------------------------------------------------
  779. long MoverGroup::orderTraversePath (long origin, WayPathPtr wayPath, unsigned long params) {
  780. long result = TACORDER_FAILURE;
  781. for (long i = 0; i < numMovers; i++) {
  782. Assert(getMover(i) != NULL, 0, " MoverGroup.orderTraversePath: NULL mover ");
  783. MechWarriorPtr pilot = getMover(i)->getPilot();
  784. if (pilot)
  785. result = pilot->orderTraversePath(true, true, origin, wayPath, params);
  786. }
  787. return(result);
  788. }
  789. //---------------------------------------------------------------------------
  790. long MoverGroup::orderPatrolPath (long origin, WayPathPtr wayPath) {
  791. long result = TACORDER_FAILURE;
  792. for (long i = 0; i < numMovers; i++) {
  793. Assert(getMover(i) != NULL, 0, " MoverGroup.orderPatrolPath: NULL mover ");
  794. MechWarriorPtr pilot = getMover(i)->getPilot();
  795. if (pilot)
  796. result = pilot->orderPatrolPath(true, true, origin, wayPath);
  797. }
  798. return(result);
  799. }
  800. //---------------------------------------------------------------------------
  801. long MoverGroup::orderPowerDown (long origin) {
  802. long result = TACORDER_FAILURE;
  803. for (long i = 0; i < numMovers; i++) {
  804. Assert(getMover(i) != NULL, 0, " MoverGroup.orderPowerDown: NULL mover ");
  805. MechWarriorPtr pilot = getMover(i)->getPilot();
  806. if (pilot)
  807. result = pilot->orderPowerDown(true, origin);
  808. }
  809. return(result);
  810. }
  811. //---------------------------------------------------------------------------
  812. long MoverGroup::orderPowerUp (long origin) {
  813. long result = TACORDER_FAILURE;
  814. for (long i = 0; i < numMovers; i++) {
  815. Assert(getMover(i) != NULL, 0, " MoverGroup.orderPowerUp: NULL mover ");
  816. MechWarriorPtr pilot = getMover(i)->getPilot();
  817. if (pilot)
  818. result = pilot->orderPowerUp(true, origin);
  819. }
  820. return(result);
  821. }
  822. //---------------------------------------------------------------------------
  823. long MoverGroup::orderAttackObject (long origin, GameObjectPtr target, long attackType, long attackMethod, long attackRange, long aimLocation, long fromArea, unsigned long params) {
  824. long result = TACORDER_FAILURE;
  825. for (long i = 0; i < numMovers; i++) {
  826. Assert(getMover(i) != NULL, 0, " MoverGroup.orderAttackObject: NULL mover ");
  827. MechWarriorPtr pilot = getMover(i)->getPilot();
  828. if (pilot)
  829. result = pilot->orderAttackObject(true, origin, target, attackType, attackMethod, attackRange, aimLocation, fromArea, params);
  830. }
  831. return(result);
  832. }
  833. //---------------------------------------------------------------------------
  834. long MoverGroup::orderWithdraw (long origin, Stuff::Vector3D location) {
  835. long result = TACORDER_FAILURE;
  836. for (long i = 0; i < numMovers; i++) {
  837. Assert(getMover(i) != NULL, 0, " MoverGroup.orderWithdraw: NULL mover ");
  838. MechWarriorPtr pilot = getMover(i)->getPilot();
  839. if (pilot)
  840. result = pilot->orderWithdraw(true, origin, location);
  841. }
  842. return(result);
  843. }
  844. //---------------------------------------------------------------------------
  845. long MoverGroup::orderEject (long origin) {
  846. long result = TACORDER_FAILURE;
  847. for (long i = 0; i < numMovers; i++) {
  848. Assert(getMover(i) != NULL, 0, " MoverGroup.orderEject: NULL mover ");
  849. MechWarriorPtr pilot = getMover(i)->getPilot();
  850. if (pilot)
  851. result = pilot->orderEject(true, true, origin);
  852. }
  853. return(result);
  854. }
  855. //---------------------------------------------------------------------------
  856. // COMBAT EVENTS
  857. //---------------------------------------------------------------------------
  858. void MoverGroup::triggerAlarm (long alarmCode, unsigned long triggerId) {
  859. for (long i = 0; i < numMovers; i++) {
  860. MechWarriorPtr pilot = getMover(i)->getPilot();
  861. if (pilot)
  862. pilot->triggerAlarm(alarmCode, triggerId);
  863. }
  864. }
  865. //---------------------------------------------------------------------------
  866. long MoverGroup::handleMateCrippled (unsigned long mateWID) {
  867. triggerAlarm(PILOT_ALARM_FRIENDLY_VEHICLE_CRIPPLED, mateWID);
  868. return(NO_ERR);
  869. }
  870. //---------------------------------------------------------------------------
  871. long MoverGroup::handleMateDisabled (unsigned long mateWID) {
  872. //triggerAlarm(PILOT_ALARM_DEATH_OF_MATE, mateId);
  873. return(NO_ERR);
  874. }
  875. //---------------------------------------------------------------------------
  876. long MoverGroup::handleMateDestroyed (unsigned long mateWID) {
  877. triggerAlarm(PILOT_ALARM_DEATH_OF_MATE, mateWID);
  878. return(NO_ERR);
  879. }
  880. //---------------------------------------------------------------------------
  881. long MoverGroup::handleMateEjected (unsigned long mateWID) {
  882. //triggerAlarm(PILOT_ALARM_DEATH_OF_MATE, mateId);
  883. return(NO_ERR);
  884. }
  885. //---------------------------------------------------------------------------
  886. void MoverGroup::handleMateFiredWeapon (unsigned long mateWID) {
  887. triggerAlarm(PILOT_ALARM_MATE_FIRED_WEAPON, mateWID);
  888. }
  889. //***************************************************************************
  890. void MoverGroup::copyTo (MoverGroupData &data)
  891. {
  892. data.id = id;
  893. data.numMovers = numMovers;
  894. memcpy(data.moverWIDs,moverWIDs,sizeof(GameObjectWatchID) * MAX_MOVERGROUP_COUNT);
  895. data.pointWID = pointWID;
  896. data.disbandOnNoPoint = disbandOnNoPoint;
  897. }
  898. //***************************************************************************
  899. void MoverGroup::init (MoverGroupData &data)
  900. {
  901. id = data.id;
  902. numMovers = data.numMovers;
  903. memcpy(moverWIDs,data.moverWIDs,sizeof(GameObjectWatchID) * MAX_MOVERGROUP_COUNT);
  904. pointWID = data.pointWID;
  905. disbandOnNoPoint = data.disbandOnNoPoint;
  906. }
  907. //***************************************************************************