1 #include "Interface_Panels.h"
2 #include "LoadSaveData.h"
3 #include "Types.h"
4 #include "Squads.h"
5 #include "Strategic_Pathing.h"
6 #include "StrategicMap.h"
7 #include "Faces.h"
8 #include "Strategic_Movement.h"
9 #include "Assignments.h"
10 #include "Overhead.h"
11 #include "Interface.h"
12 #include "Vehicles.h"
13 #include "Map_Screen_Helicopter.h"
14 #include "Soldier_Profile.h"
15 #include "Debug.h"
16 #include "JAScreens.h"
17 #include "Soldier_Macros.h"
18 #include "ScreenIDs.h"
19 #include "FileMan.h"
20
21 #include <algorithm>
22 #include <stdexcept>
23
24 // squad array
25 SOLDIERTYPE *Squad[ NUMBER_OF_SQUADS ][ NUMBER_OF_SOLDIERS_PER_SQUAD ];
26
27 // list of dead guys for squads...in id values -> -1 means no one home
28 INT16 sDeadMercs[ NUMBER_OF_SQUADS ][ NUMBER_OF_SOLDIERS_PER_SQUAD ];
29
30 // the movement group ids
31 INT8 SquadMovementGroups[ NUMBER_OF_SQUADS ];
32
33 BOOLEAN fExitingVehicleToSquad = FALSE;
34
35
36 INT32 iCurrentTacticalSquad = FIRST_SQUAD;
37
InitSquads(void)38 void InitSquads( void )
39 {
40 // init the squad lists to NULL ptrs.
41 INT32 iCounter =0;
42
43 // null each list of ptrs.
44 for( iCounter = 0; iCounter < NUMBER_OF_SQUADS; iCounter++ )
45 {
46 FOR_EACH_SLOT_IN_SQUAD(i, iCounter)
47 {
48 *i = 0;
49 }
50
51 // create mvt groups
52 GROUP* const g = CreateNewPlayerGroupDepartingFromSector(1, 1);
53 g->fPersistant = TRUE;
54 SquadMovementGroups[iCounter] = g->ubGroupID;
55 }
56
57 for (int i = 0; i < NUMBER_OF_SQUADS; ++i)
58 {
59 std::fill_n(sDeadMercs[i], NUMBER_OF_SOLDIERS_PER_SQUAD, -1);
60 }
61 }
62
IsThisSquadFull(INT8 bSquadValue)63 BOOLEAN IsThisSquadFull( INT8 bSquadValue )
64 {
65 // run through entries in the squad list, make sure there is a free entry
66 FOR_EACH_SLOT_IN_SQUAD(i, bSquadValue)
67 {
68 if (!*i) return FALSE;
69 }
70 return TRUE;
71 }
72
GetFirstEmptySquad(void)73 INT8 GetFirstEmptySquad( void )
74 {
75 UINT8 ubCounter = 0;
76
77 for( ubCounter = 0; ubCounter < NUMBER_OF_SQUADS; ubCounter++ )
78 {
79 if (SquadIsEmpty(ubCounter)) return ubCounter;
80 }
81
82 // not found - none are completely empty (shouldn't ever happen!)
83 throw std::logic_error("Found no empty squad");
84 }
85
86
87 static BOOLEAN CopyPathOfSquadToCharacter(SOLDIERTYPE* pCharacter, INT8 bSquadValue);
88
89
AddCharacterToSquad(SOLDIERTYPE * const s,INT8 const bSquadValue)90 BOOLEAN AddCharacterToSquad(SOLDIERTYPE* const s, INT8 const bSquadValue)
91 {
92 // add character to squad...return success or failure
93 // run through list of people in squad, find first free slo
94
95 if (fExitingVehicleToSquad) return FALSE;
96
97 // ATE: If any vehicle exists in this squad AND we're not set to
98 // a driver or or passenger, when return false
99 if (DoesVehicleExistInSquad(bSquadValue))
100 {
101 // We're not allowing anybody to go on a vehicle if they are not passengers!
102 // NB: We obviously need to make sure that REAL passengers have their
103 // flags set before adding them to a squad!
104 if (!(s->uiStatusFlags & (SOLDIER_PASSENGER | SOLDIER_DRIVER | SOLDIER_VEHICLE)))
105 {
106 return FALSE;
107 }
108 }
109
110 // if squad is on the move, can't add someone
111 if (IsThisSquadOnTheMove(bSquadValue)) return FALSE;
112
113 FOR_EACH_SLOT_IN_SQUAD(i, bSquadValue)
114 {
115 SOLDIERTYPE const* const t = *i;
116 // check if on current squad and current slot?
117 // 'successful of sorts, if there, then he's 'added'
118 if (t == s) return TRUE;
119
120 if (t) continue;
121 // free slot, add here
122
123 // check if squad empty, if not check sector x,y,z are the same as this guys
124 INT16 sX;
125 INT16 sY;
126 INT8 bZ;
127 if (SectorSquadIsIn(bSquadValue, &sX, &sY, &bZ) &&
128 (s->sSectorX != sX || s->sSectorY != sY || s->bSectorZ != bZ))
129 {
130 return FALSE;
131 }
132
133 RemoveCharacterFromSquads(s);
134
135 // copy path of squad to this char
136 CopyPathOfSquadToCharacter(s, bSquadValue);
137
138 // check if old mvt group
139 if (s->ubGroupID != 0)
140 {
141 // in valid group, remove from that group
142 RemovePlayerFromGroup(*s);
143
144 // character not on a reserved group
145 if (s->bAssignment >= ON_DUTY && s->bAssignment != VEHICLE)
146 {
147 // if valid group, delete it
148 GROUP* const pGroup = GetGroup(s->ubGroupID);
149 if (pGroup) RemoveGroup(*pGroup);
150 }
151 }
152
153 GROUP& g = *GetGroup(SquadMovementGroups[bSquadValue]);
154 if (s->bAssignment != VEHICLE || s->iVehicleId == -1)
155 {
156 AddPlayerToGroup(g, *s);
157 SetGroupSectorValue(s->sSectorX, s->sSectorY, s->bSectorZ, g);
158 }
159 else if (InHelicopter(*s))
160 {
161 // if creating a new squad from guys exiting the chopper
162 BOOLEAN const fNewSquad = SquadIsEmpty(bSquadValue);
163
164 RemoveSoldierFromHelicopter(s);
165
166 AddPlayerToGroup(g, *s);
167 SetGroupSectorValue(s->sSectorX, s->sSectorY, s->bSectorZ, g);
168
169 // if we've just started a new squad
170 if (fNewSquad)
171 {
172 // set mvt group for
173 GROUP const* const pGroup = GetGroup(GetHelicopter().ubMovementGroup);
174 Assert(pGroup);
175 if (pGroup)
176 {
177 // set where it is and where it's going, then make it arrive there. Don't check for battle
178 PlaceGroupInSector(g, pGroup->ubPrevX, pGroup->ubPrevY, pGroup->ubSectorX, pGroup->ubSectorY, pGroup->ubSectorZ, false); // XXX TODO001D
179 }
180 }
181 }
182 else
183 {
184 fExitingVehicleToSquad = TRUE;
185 // remove from vehicle
186 TakeSoldierOutOfVehicle(s);
187 fExitingVehicleToSquad = FALSE;
188
189 AddPlayerToGroup(g, *s);
190 SetGroupSectorValue(s->sSectorX, s->sSectorY, s->bSectorZ, g);
191 }
192
193 *i = s;
194
195 if (s->bAssignment != bSquadValue)
196 {
197 // check to see if we should wake them up
198 if (s->fMercAsleep) SetMercAwake(s, FALSE, FALSE);
199 SetTimeOfAssignmentChangeForMerc(s);
200 }
201
202 // set squad value
203 ChangeSoldiersAssignment(s, bSquadValue);
204
205 if (SquadIsEmpty(iCurrentTacticalSquad)) SetCurrentSquad(bSquadValue, TRUE);
206
207 if (bSquadValue == iCurrentTacticalSquad) CheckForAndAddMercToTeamPanel(s);
208
209 if (s == GetSelectedMan()) SetCurrentSquad(bSquadValue, TRUE);
210
211 return TRUE;
212 }
213
214 return FALSE;
215 }
216
217
218 // find the first slot we can fit the guy in
AddCharacterToAnySquad(SOLDIERTYPE * const pCharacter)219 void AddCharacterToAnySquad(SOLDIERTYPE* const pCharacter)
220 {
221 // add character to any squad, if character is assigned to a squad, returns TRUE
222 INT8 bCounter = 0;
223 INT8 bFirstEmptySquad = -1;
224
225
226 // remove them from current squad
227 RemoveCharacterFromSquads( pCharacter ); // REDUNDANT AddCharacterToSquad()
228
229 // first look for a compatible NON-EMPTY squad (don't start new squad if we don't have to)
230 for( bCounter = 0; bCounter < NUMBER_OF_SQUADS; bCounter++ )
231 {
232 if (!SquadIsEmpty(bCounter))
233 {
234 if (AddCharacterToSquad(pCharacter, bCounter))
235 {
236 return;
237 }
238 }
239 else
240 {
241 if ( bFirstEmptySquad == -1 )
242 {
243 bFirstEmptySquad = bCounter;
244 }
245 }
246 }
247
248 // no non-empty compatible squads were found
249
250 // try the first empty one (and there better be one)
251 if ( bFirstEmptySquad != -1 )
252 {
253 if (AddCharacterToSquad(pCharacter, bFirstEmptySquad))
254 {
255 return;
256 }
257 }
258
259 throw std::logic_error("Failed to add character to any squad");
260 }
261
262
263 // find the first slot we can fit the guy in
AddCharacterToUniqueSquad(SOLDIERTYPE * const s)264 INT8 AddCharacterToUniqueSquad(SOLDIERTYPE* const s)
265 {
266 RemoveCharacterFromSquads(s); // REDUNDANT AddCharacterToSquad()
267 INT8 const squad = GetFirstEmptySquad();
268 AddCharacterToSquad(s, squad);
269 return squad;
270 }
271
272
SquadIsEmpty(INT8 bSquadValue)273 BOOLEAN SquadIsEmpty( INT8 bSquadValue )
274 {
275 // run through this squad's slots and find if they ALL are empty
276 FOR_EACH_IN_SQUAD(i, bSquadValue)
277 {
278 return FALSE;
279 }
280 return TRUE;
281 }
282
283
284 static BOOLEAN AddDeadCharacterToSquadDeadGuys(SOLDIERTYPE* pSoldier, INT32 iSquadValue);
285 static void RebuildSquad(INT8 bSquadValue);
286 static void UpdateCurrentlySelectedMerc(SOLDIERTYPE* pSoldier, INT8 bSquadValue);
287
288
289 // find and remove character from any squad
RemoveCharacterFromSquads(SOLDIERTYPE * const s)290 BOOLEAN RemoveCharacterFromSquads(SOLDIERTYPE* const s)
291 {
292 for (INT32 squad = 0; squad < NUMBER_OF_SQUADS; ++squad)
293 {
294 FOR_EACH_SLOT_IN_SQUAD(i, squad)
295 {
296 if (*i != s) continue;
297 *i = 0;
298
299 // Release memory for his personal path, but don't clear his group's
300 // path/waypoints (pass in groupID -1). Just because one guy leaves a
301 // group is no reason to cancel movement for the rest of the group.
302 s->pMercPath = ClearStrategicPathList(s->pMercPath, -1);
303
304 RemovePlayerFromGroup(*s);
305 s->ubGroupID = 0;
306
307 if (s->fBetweenSectors && s->uiStatusFlags & SOLDIER_VEHICLE)
308 {
309 GROUP& g = *CreateNewPlayerGroupDepartingFromSector(s->sSectorX, s->sSectorY);
310 AddPlayerToGroup(g, *s);
311 }
312
313 RebuildSquad(squad);
314
315 if (s->bLife == 0)
316 {
317 AddDeadCharacterToSquadDeadGuys(s, squad);
318 }
319
320 if (!(gTacticalStatus.uiFlags & LOADING_SAVED_GAME) && guiCurrentScreen == GAME_SCREEN)
321 {
322 UpdateCurrentlySelectedMerc(s, squad);
323 }
324
325 return TRUE;
326 }
327 }
328
329 // not found
330 return FALSE;
331 }
332
333
NumberOfPeopleInSquad(INT8 bSquadValue)334 INT8 NumberOfPeopleInSquad( INT8 bSquadValue )
335 {
336 INT8 bSquadCount = 0;
337
338 if( bSquadValue == NO_CURRENT_SQUAD )
339 {
340 return( 0 );
341 }
342
343 // find number of characters in particular squad.
344 FOR_EACH_IN_SQUAD(i, bSquadValue)
345 {
346 ++bSquadCount;
347 }
348
349 // return number found
350 return( bSquadCount );
351 }
352
NumberOfNonEPCsInSquad(INT8 bSquadValue)353 INT8 NumberOfNonEPCsInSquad( INT8 bSquadValue )
354 {
355 INT8 bSquadCount = 0;
356
357 if( bSquadValue == NO_CURRENT_SQUAD )
358 {
359 return( 0 );
360 }
361
362 // find number of characters in particular squad.
363 FOR_EACH_IN_SQUAD(i, bSquadValue)
364 {
365 // valid slot?
366 if (!AM_AN_EPC(*i)) ++bSquadCount;
367 }
368
369 // return number found
370 return( bSquadCount );
371 }
372
IsRobotControllerInSquad(INT8 bSquadValue)373 BOOLEAN IsRobotControllerInSquad( INT8 bSquadValue )
374 {
375 if( bSquadValue == NO_CURRENT_SQUAD )
376 {
377 return( 0 );
378 }
379
380 // find number of characters in particular squad.
381 FOR_EACH_IN_SQUAD(i, bSquadValue)
382 {
383 // valid slot?
384 if (ControllingRobot(*i)) return TRUE;
385 }
386
387 // return number found
388 return( FALSE );
389 }
390
391
SectorSquadIsIn(const INT8 bSquadValue,INT16 * const sMapX,INT16 * const sMapY,INT8 * const sMapZ)392 BOOLEAN SectorSquadIsIn(const INT8 bSquadValue, INT16* const sMapX, INT16* const sMapY, INT8* const sMapZ)
393 {
394 // returns if there is anyone on the squad and what sector ( strategic ) they are in
395 Assert( bSquadValue < ON_DUTY );
396
397 FOR_EACH_IN_SQUAD(i, bSquadValue)
398 {
399 SOLDIERTYPE const* const s = *i;
400 // if valid soldier, get current sector and return
401 *sMapX = s->sSectorX;
402 *sMapY = s->sSectorY;
403 *sMapZ = s->bSectorZ;
404 return TRUE;
405 }
406
407 // return there is no squad
408 return ( FALSE );
409 }
410
411
CopyPathOfSquadToCharacter(SOLDIERTYPE * pCharacter,INT8 bSquadValue)412 static BOOLEAN CopyPathOfSquadToCharacter(SOLDIERTYPE* pCharacter, INT8 bSquadValue)
413 {
414 // copy path from squad to character
415 FOR_EACH_IN_SQUAD(i, bSquadValue)
416 {
417 SOLDIERTYPE const* const t = *i;
418 if (t == pCharacter) continue;
419 // valid character, copy paths
420 ClearStrategicPathList(pCharacter->pMercPath, 0);
421 pCharacter->pMercPath = CopyPaths(t->pMercPath);
422 // return success
423 return TRUE;
424 }
425
426 // return failure
427 return ( FALSE );
428 }
429
430
CopyPathOfCharacterToSquad(SOLDIERTYPE * const pCharacter,const INT8 bSquadValue)431 void CopyPathOfCharacterToSquad(SOLDIERTYPE* const pCharacter, const INT8 bSquadValue)
432 {
433 // copy path of this character to members of squad
434 FOR_EACH_IN_SQUAD(i, bSquadValue)
435 {
436 SOLDIERTYPE* const t = *i;
437 if (t == pCharacter) continue;
438 // valid character, copy paths
439 ClearStrategicPathList(t->pMercPath, -1);
440 t->pMercPath = CopyPaths(pCharacter->pMercPath);
441 }
442 }
443
444
445
CurrentSquad(void)446 INT32 CurrentSquad( void )
447 {
448 // returns which squad is current squad
449
450 return( iCurrentTacticalSquad );
451 }
452
SetCurrentSquad(INT32 iCurrentSquad,BOOLEAN fForce)453 BOOLEAN SetCurrentSquad( INT32 iCurrentSquad, BOOLEAN fForce )
454 {
455 // set the current tactical squad
456
457 // ARM: can't call SetCurrentSquad() in mapscreen, it calls SelectSoldier(), that will initialize interface panels!!!
458 // ATE: Adjusted conditions a bit ( sometimes were not getting selected )
459 if ( guiCurrentScreen == LAPTOP_SCREEN || guiCurrentScreen == MAP_SCREEN )
460 {
461 return( FALSE );
462 }
463
464 // ATE; Added to allow us to have NO current squad
465 if ( iCurrentSquad == NO_CURRENT_SQUAD )
466 {
467 // set current squad and return success
468 iCurrentTacticalSquad = iCurrentSquad;
469
470 // cleat list
471 RemoveAllPlayersFromSlot( );
472
473 // set all auto faces inactive
474 SetAllAutoFacesInactive( );
475
476 return( FALSE );
477 }
478
479
480 // check if valid value passed
481 if( ( iCurrentSquad >= NUMBER_OF_SQUADS )||( iCurrentSquad < 0 ) )
482 {
483 // no
484 return ( FALSE );
485 }
486
487 // check if squad is current
488 if( iCurrentSquad == iCurrentTacticalSquad && !fForce )
489 {
490 return ( TRUE );
491 }
492
493 // set current squad and return success
494 iCurrentTacticalSquad = iCurrentSquad;
495
496 // cleat list
497 RemoveAllPlayersFromSlot( );
498
499 // set all auto faces inactive
500 SetAllAutoFacesInactive( );
501
502 if (iCurrentSquad != NO_CURRENT_SQUAD)
503 {
504 FOR_EACH_IN_SQUAD(i, iCurrentSquad)
505 {
506 // squad set, now add soldiers in
507 CheckForAndAddMercToTeamPanel(*i);
508 }
509 }
510
511 // check if the currently selected guy is on this squad, if not, get the first one on the new squad
512 const SOLDIERTYPE* const sel = GetSelectedMan();
513 if (sel == NULL || sel->bAssignment != iCurrentSquad)
514 {
515 // ATE: Changed this to FALSE for acknowledgement sounds.. sounds bad if just starting/entering sector..
516 SelectSoldier(Squad[iCurrentSquad][0], SELSOLDIER_FORCE_RESELECT);
517 }
518
519 return ( TRUE );
520 }
521
RebuildCurrentSquad(void)522 void RebuildCurrentSquad( void )
523 {
524 // rebuilds current squad to reset faces in tactical
525
526 // check if valid value passed
527 if( ( iCurrentTacticalSquad >= NUMBER_OF_SQUADS ) || ( iCurrentTacticalSquad < 0 ) )
528 {
529 // no
530 return;
531 }
532
533 // set default squad..just inc ase we no longer have a valid squad
534 SetDefaultSquadOnSectorEntry( TRUE );
535
536 // cleat list
537 RemoveAllPlayersFromSlot( );
538
539 // set all auto faces inactive
540 SetAllAutoFacesInactive( );
541
542 gfPausedTacticalRenderInterfaceFlags = DIRTYLEVEL2;
543
544 if( iCurrentTacticalSquad != NO_CURRENT_SQUAD )
545 {
546 FOR_EACH_IN_SQUAD(i, iCurrentTacticalSquad)
547 {
548 // squad set, now add soldiers in
549 CheckForAndAddMercToTeamPanel(*i);
550 }
551
552 for (INT32 iCounter = 0; iCounter < NUMBER_OF_SOLDIERS_PER_SQUAD; ++iCounter)
553 {
554 const INT16 dead_id = sDeadMercs[iCurrentTacticalSquad][iCounter];
555 if (dead_id == -1) continue;
556
557 SOLDIERTYPE* const dead_soldier = FindSoldierByProfileIDOnPlayerTeam(dead_id);
558 if (!dead_soldier) continue;
559
560 // squad set, now add soldiers in
561 CheckForAndAddMercToTeamPanel(dead_soldier);
562 }
563 }
564 }
565
566
ExamineCurrentSquadLights(void)567 void ExamineCurrentSquadLights( void )
568 {
569 // OK, we should add lights for any guy currently bInSector who is not bad OKLIFE...
570 FOR_EACH_IN_TEAM(s, OUR_TEAM)
571 {
572 if (s->bInSector && s->bLife >= OKLIFE) PositionSoldierLight(s);
573 }
574 }
575
576
IsSquadOnCurrentTacticalMap(INT32 iCurrentSquad)577 BOOLEAN IsSquadOnCurrentTacticalMap( INT32 iCurrentSquad )
578 {
579 // check to see if this squad is on the current map
580
581 // check if valid value passed
582 if( ( iCurrentSquad >= NUMBER_OF_SQUADS ) || ( iCurrentSquad < 0 ) )
583 {
584 // no
585 return ( FALSE );
586 }
587
588 // go through memebrs of squad...if anyone on this map, return true
589 FOR_EACH_IN_SQUAD(i, iCurrentSquad)
590 {
591 SOLDIERTYPE const* const s = *i;
592 // ATE; Added more checks here for being in sector ( fBetweenSectors and SectorZ )
593 if (s->sSectorX == gWorldSectorX &&
594 s->sSectorY == gWorldSectorY &&
595 s->bSectorZ == gbWorldSectorZ &&
596 !s->fBetweenSectors)
597 {
598 return( TRUE );
599 }
600 }
601
602 return( FALSE );
603 }
604
605
SetDefaultSquadOnSectorEntry(BOOLEAN fForce)606 void SetDefaultSquadOnSectorEntry( BOOLEAN fForce )
607 {
608 INT32 iCounter = 0;
609 // check if selected squad is in current sector, if so, do nothing, if not...first first case that they are
610
611 if (IsSquadOnCurrentTacticalMap(iCurrentTacticalSquad))
612 {
613 // is in sector, leave
614 return;
615 }
616
617 //otherwise...
618
619 // find first squad availiable
620 for( iCounter = 0; iCounter < NUMBER_OF_SQUADS; iCounter++ )
621 {
622 if (IsSquadOnCurrentTacticalMap(iCounter))
623 {
624 // squad in sector...set as current
625 SetCurrentSquad( iCounter, fForce );
626
627 return;
628 }
629 }
630
631 // If here, set to no current squad
632 SetCurrentSquad( NO_CURRENT_SQUAD, FALSE );
633 }
634
GetLastSquadActive(void)635 INT32 GetLastSquadActive( void )
636 {
637 // find id of last squad in the list with active mercs in it
638 INT32 iCounter = 0;
639 INT32 iLastSquad = 0;
640
641 for( iCounter = 0; iCounter < NUMBER_OF_SQUADS; iCounter++ )
642 {
643 FOR_EACH_IN_SQUAD(i, iCounter)
644 {
645 iLastSquad = iCounter;
646 break;
647 }
648 }
649
650 return ( iLastSquad );
651 }
652
653
SaveSquadInfoToSavedGameFile(HWFILE const f)654 void SaveSquadInfoToSavedGameFile(HWFILE const f)
655 {
656 // Save the squad info to the Saved Game File
657 BYTE data[NUMBER_OF_SQUADS * NUMBER_OF_SOLDIERS_PER_SQUAD * 12];
658 DataWriter d{data};
659 for (INT32 squad = 0; squad < NUMBER_OF_SQUADS; ++squad)
660 {
661 FOR_EACH_SLOT_IN_SQUAD(slot, squad)
662 {
663 SOLDIERTYPE const* const s = *slot;
664 INJ_I16(d, s ? s->ubID : -1)
665 INJ_SKIP(d, 10)
666 }
667 }
668 Assert(d.getConsumed() == lengthof(data));
669 FileWrite(f, data, sizeof(data));
670
671 // Save all the squad movement IDs
672 FileWrite(f, SquadMovementGroups, sizeof(SquadMovementGroups));
673 }
674
675
LoadSquadInfoFromSavedGameFile(HWFILE const f)676 void LoadSquadInfoFromSavedGameFile(HWFILE const f)
677 {
678 // Load in the squad info
679 BYTE data[NUMBER_OF_SQUADS * NUMBER_OF_SOLDIERS_PER_SQUAD * 12];
680 DataReader d{data};
681 FileRead(f, data, sizeof(data));
682 for (INT32 squad = 0; squad != NUMBER_OF_SQUADS; ++squad)
683 {
684 FOR_EACH_SLOT_IN_SQUAD(slot, squad)
685 {
686 INT16 id;
687 EXTR_I16(d, id)
688 EXTR_SKIP(d, 10)
689 *slot = id != -1 ? &GetMan(id) : 0;
690 }
691 }
692 Assert(d.getConsumed() == lengthof(data));
693
694 // Load in the Squad movement IDs
695 FileRead(f, SquadMovementGroups, sizeof(SquadMovementGroups));
696 }
697
698
IsThisSquadOnTheMove(INT8 bSquadValue)699 BOOLEAN IsThisSquadOnTheMove( INT8 bSquadValue )
700 {
701 FOR_EACH_IN_SQUAD(i, bSquadValue)
702 {
703 return (*i)->fBetweenSectors;
704 }
705 return FALSE;
706 }
707
708
709 // rebuild this squad after someone has been removed, to 'squeeze' together any empty spots
RebuildSquad(INT8 const squad_id)710 static void RebuildSquad(INT8 const squad_id)
711 {
712 SOLDIERTYPE** const squad = Squad[squad_id];
713 SOLDIERTYPE** const end = endof(Squad[squad_id]);
714 SOLDIERTYPE** dst = squad;
715 for (SOLDIERTYPE** i = squad; i != end; ++i)
716 {
717 if (!*i) continue;
718 *dst++ = *i;
719 }
720 for (; dst != end; ++dst) *dst = 0;
721 }
722
723
724 // update current merc selected in tactical
UpdateCurrentlySelectedMerc(SOLDIERTYPE * pSoldier,INT8 bSquadValue)725 static void UpdateCurrentlySelectedMerc(SOLDIERTYPE* pSoldier, INT8 bSquadValue)
726 {
727 // if this squad is the current one and and the psoldier is the currently selected soldier, get rid of 'em
728 if( bSquadValue != iCurrentTacticalSquad )
729 {
730 return;
731 }
732
733 // Are we the selected guy?
734 if (GetSelectedMan() == pSoldier)
735 {
736 SOLDIERTYPE* const next = FindNextActiveAndAliveMerc(pSoldier, FALSE, FALSE);
737 if (next != pSoldier)
738 {
739 SelectSoldier(next, SELSOLDIER_NONE);
740 }
741 else
742 {
743 SetSelectedMan(NULL);
744
745 // ATE: Make sure we are in TEAM panel at this point!
746 SetCurrentInterfacePanel( TEAM_PANEL );
747 }
748 }
749 }
750
751
IsDeadGuyOnSquad(const ProfileID pid,const INT8 squad)752 static BOOLEAN IsDeadGuyOnSquad(const ProfileID pid, const INT8 squad)
753 {
754 for (INT32 i = 0; i < NUMBER_OF_SOLDIERS_PER_SQUAD; ++i)
755 {
756 if (sDeadMercs[squad][i] == pid) return TRUE;
757 }
758 return FALSE;
759 }
760
761
762 static BOOLEAN IsDeadGuyOnAnySquad(SOLDIERTYPE* pSoldier);
763
764
AddDeadCharacterToSquadDeadGuys(SOLDIERTYPE * pSoldier,INT32 iSquadValue)765 static BOOLEAN AddDeadCharacterToSquadDeadGuys(SOLDIERTYPE* pSoldier, INT32 iSquadValue)
766 {
767 INT32 iCounter = 0;
768
769 // is dead guy in any squad
770 if (IsDeadGuyOnAnySquad(pSoldier)) return TRUE;
771
772 if (IsDeadGuyOnSquad(pSoldier->ubProfile, iSquadValue)) return TRUE;
773
774 // now insert the guy
775 for( iCounter = 0; iCounter < NUMBER_OF_SOLDIERS_PER_SQUAD; iCounter++ )
776 {
777 const INT16 dead_id = sDeadMercs[iSquadValue][iCounter];
778 if (dead_id == -1 || FindSoldierByProfileIDOnPlayerTeam(dead_id) == NULL)
779 {
780 sDeadMercs[iSquadValue][iCounter] = pSoldier->ubProfile;
781 }
782 }
783
784 // no go
785 return( FALSE );
786 }
787
788
IsDeadGuyOnAnySquad(SOLDIERTYPE * pSoldier)789 static BOOLEAN IsDeadGuyOnAnySquad(SOLDIERTYPE* pSoldier)
790 {
791 // squad?
792 for (INT32 iCounterA = 0; iCounterA < NUMBER_OF_SQUADS; ++iCounterA)
793 {
794 if (IsDeadGuyOnSquad(pSoldier->ubProfile, iCounterA)) return TRUE;
795 }
796
797 return ( FALSE );
798 }
799
800
SoldierIsDeadAndWasOnSquad(SOLDIERTYPE * pSoldier,INT8 bSquadValue)801 BOOLEAN SoldierIsDeadAndWasOnSquad( SOLDIERTYPE *pSoldier, INT8 bSquadValue )
802 {
803 return
804 bSquadValue != NO_CURRENT_SQUAD &&
805 IsDeadGuyOnSquad(pSoldier->ubProfile, bSquadValue);
806 }
807
808
ResetDeadSquadMemberList(INT32 const iSquadValue)809 void ResetDeadSquadMemberList(INT32 const iSquadValue)
810 {
811 std::fill_n(sDeadMercs[ iSquadValue ], NUMBER_OF_SOLDIERS_PER_SQUAD, -1);
812 }
813
814
815 // this passed soldier on the current squad int he tactical map
IsMercOnCurrentSquad(const SOLDIERTYPE * pSoldier)816 BOOLEAN IsMercOnCurrentSquad(const SOLDIERTYPE* pSoldier)
817 {
818 // current squad valid?
819 if( iCurrentTacticalSquad >= NUMBER_OF_SQUADS )
820 {
821 // no
822 return( FALSE );
823 }
824
825 FOR_EACH_SLOT_IN_SQUAD(i, iCurrentTacticalSquad)
826 {
827 if (*i == pSoldier) return TRUE;
828 }
829
830 return( FALSE );
831 }
832
NumberOfPlayerControllableMercsInSquad(INT8 bSquadValue)833 INT8 NumberOfPlayerControllableMercsInSquad( INT8 bSquadValue )
834 {
835 INT8 bSquadCount = 0;
836
837 if( bSquadValue == NO_CURRENT_SQUAD )
838 {
839 return( 0 );
840 }
841
842 // find number of characters in particular squad.
843 FOR_EACH_IN_SQUAD(i, bSquadValue)
844 {
845 SOLDIERTYPE const* const pSoldier = *i;
846 //Kris: This breaks the CLIENT of this function, tactical traversal.
847 // Do NOT check for EPCS or ROBOT here.
848 //if ( !AM_AN_EPC( pSoldier ) && !AM_A_ROBOT( pSoldier ) &&
849 if( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
850 {
851 bSquadCount++;
852 }
853 }
854
855 // return number found
856 return( bSquadCount );
857 }
858
859
DoesVehicleExistInSquad(INT8 bSquadValue)860 BOOLEAN DoesVehicleExistInSquad( INT8 bSquadValue )
861 {
862 if( bSquadValue == NO_CURRENT_SQUAD )
863 {
864 return( FALSE );
865 }
866
867 // find number of characters in particular squad.
868 FOR_EACH_IN_SQUAD(i, bSquadValue)
869 {
870 if ((*i)->uiStatusFlags & SOLDIER_VEHICLE) return TRUE;
871 }
872
873 return(FALSE );
874 }
875
876
CheckSquadMovementGroups()877 void CheckSquadMovementGroups()
878 {
879 FOR_EACH(INT8, i, SquadMovementGroups)
880 {
881 if (GetGroup(*i)) continue;
882
883 // recreate group
884 GROUP* const g = CreateNewPlayerGroupDepartingFromSector(1, 1);
885 g->fPersistant = TRUE;
886 *i = g->ubGroupID;
887 }
888 }
889