1 #include "Map_Screen_Interface.h"
2 #include "ContentManager.h"
3 #include "Dialogue_Control.h"
4 #include "Directories.h"
5 #include "EMail.h"
6 #include "FileMan.h"
7 #include "Finances.h"
8 #include "Font.h"
9 #include "Font_Control.h"
10 #include "Game_Clock.h"
11 #include "Game_Event_Hook.h"
12 #include "Game_Init.h"
13 #include "GameInstance.h"
14 #include "GameSettings.h"
15 #include "HImage.h"
16 #include "Interface_Items.h"
17 #include "Isometric_Utils.h"
18 #include "Items.h"
19 #include "JAScreens.h"
20 #include "Keys.h"
21 #include "Line.h"
22 #include "LoadSaveObjectType.h"
23 #include "Map_Screen_Helicopter.h"
24 #include "Map_Screen_Interface_Border.h"
25 #include "Map_Screen_Interface_Bottom.h"
26 #include "Map_Screen_Interface_Map_Inventory.h"
27 #include "MapScreen.h"
28 #include "MercPortrait.h"
29 #include "Message.h"
30 #include "Overhead.h"
31 #include "PopUpBox.h"
32 #include "PreBattle_Interface.h"
33 #include "Queen_Command.h"
34 #include "Quests.h"
35 #include "Render_Dirty.h"
36 #include "Render_Fun.h"
37 #include "RenderWorld.h"
38 #include "SamSiteModel.h"
39 #include "ShippingDestinationModel.h"
40 #include "Soldier_Macros.h"
41 #include "SoundMan.h"
42 #include "Squads.h"
43 #include "Strategic.h"
44 #include "StrategicMap_Secrets.h"
45 #include "Strategic_Mines.h"
46 #include "Strategic_Pathing.h"
47 #include "SysUtil.h"
48 #include "Tactical_Placement_GUI.h"
49 #include "Tactical_Save.h"
50 #include "Text.h"
51 #include "Timer_Control.h"
52 #include "Video.h"
53 #include "VObject.h"
54 #include "VSurface.h"
55 #include "WordWrap.h"
56 #include <algorithm>
57 #include <iterator>
58 #include <string_theory/format>
59 #include <string_theory/string>
60 struct FACETYPE;
61 struct PopUpBox;
62
63 // number of LINKED LISTS for sets of leave items (each slot holds an unlimited # of items)
64 #define NUM_LEAVE_LIST_SLOTS 20
65
66 #define SELECTED_CHAR_ARROW_X (STD_SCREEN_X + 8)
67
68 #define SIZE_OF_UPDATE_BOX 20
69
70 // as deep as the map goes
71 #define MAX_DEPTH_OF_MAP 3
72
73
74 // number of merc columns for four wide mode
75 #define NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE 4
76
77 // number of merc columns for 2 wide mode
78 #define NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE 2
79
80 // number needed for 4 wide mode to activate
81 #define NUMBER_OF_MERCS_FOR_FOUR_WIDTH_UPDATE_PANEL 4
82
83 #define DBL_CLICK_DELAY_FOR_MOVE_MENU 200
84
85
86 #define REASON_FOR_SOLDIER_UPDATE_OFFSET_Y (14)
87
88 #define MAX_MAPSCREEN_FAST_HELP 100
89
90 #define VEHICLE_ONLY FALSE
91 #define AND_ALL_ON_BOARD TRUE
92
93
94 // the regions int he movemenu
95 enum{
96 SQUAD_REGION = 0,
97 VEHICLE_REGION,
98 SOLDIER_REGION,
99 DONE_REGION,
100 CANCEL_REGION,
101 OTHER_REGION,
102 };
103
104
105 // waiting list for update box
106 INT32 iUpdateBoxWaitingList[ MAX_CHARACTER_COUNT ];
107
108
109 struct FASTHELPREGION
110 {
111 ST::string FastHelpText;
112 INT32 iX;
113 INT32 iY;
114 INT32 iW;
115 };
116
117
118 static FASTHELPREGION pFastHelpMapScreenList[MAX_MAPSCREEN_FAST_HELP];
119
120 // the move menu region
121 static MOUSE_REGION gMoveMenuRegion[MAX_POPUP_BOX_STRING_COUNT];
122
123 static MOUSE_REGION gMapScreenHelpTextMask;
124
125 static BOOLEAN fScreenMaskForMoveCreated = FALSE;
126
127 static ST::string gsCustomErrorString;
128
129 BOOLEAN fShowUpdateBox = FALSE;
130 static BOOLEAN fInterfaceFastHelpTextActive = FALSE;
131 BOOLEAN fReBuildCharacterList = FALSE;
132 static INT32 giSizeOfInterfaceFastHelpTextList = 0;
133
134 //Animated sector locator icon variables.
135 INT16 gsSectorLocatorX;
136 INT16 gsSectorLocatorY;
137 UINT8 gubBlitSectorLocatorCode; //color
138 static SGPVObject* guiSectorLocatorGraphicID;
139 // the animate time per frame in milliseconds
140 #define ANIMATED_BATTLEICON_FRAME_TIME 80
141 #define MAX_FRAME_COUNT_FOR_ANIMATED_BATTLE_ICON 12
142
143
144 // number of mercs in sector capable of moving
145 static INT32 giNumberOfSoldiersInSectorMoving = 0;
146
147 // number of squads capable of moving
148 static INT32 giNumberOfSquadsInSectorMoving = 0;
149
150 // number of vehicles in sector moving
151 static INT32 giNumberOfVehiclesInSectorMoving = 0;
152
153 // the list of soldiers that are moving
154 static SOLDIERTYPE* pSoldierMovingList[MAX_CHARACTER_COUNT];
155 static bool fSoldierIsMoving[MAX_CHARACTER_COUNT];
156
157 static SOLDIERTYPE* pUpdateSoldierBox[SIZE_OF_UPDATE_BOX];
158
159 static SGPVObject* giUpdateSoldierFaces[SIZE_OF_UPDATE_BOX];
160
161 // the squads thata re moving
162 static INT32 iSquadMovingList[NUMBER_OF_SQUADS];
163 static INT32 fSquadIsMoving[NUMBER_OF_SQUADS];
164
165 // the vehicles thata re moving
166 static INT32 iVehicleMovingList[NUMBER_OF_SQUADS];
167 static INT32 fVehicleIsMoving[NUMBER_OF_SQUADS];
168
169 static MOUSE_REGION gMoveBoxScreenMask;
170
171
172 BOOLEAN fShowMapScreenMovementList = FALSE;
173
174
175 MapScreenCharacterSt gCharactersList[ MAX_CHARACTER_COUNT+1];
176
177 MOUSE_REGION gMapStatusBarsRegion;
178
179 static UpdateBoxReason iReasonForSoldierUpDate = NO_REASON_FOR_UPDATE;
180
181
182 // disable team info panels due to battle roster
183 BOOLEAN fDisableDueToBattleRoster = FALSE;
184
185 // track old contract times
186 static INT32 iOldContractTimes[MAX_CHARACTER_COUNT];
187
188 // position of pop up box
189 INT32 giBoxY = 0;
190
191 static MOUSE_REGION gContractIconRegion;
192 static MOUSE_REGION gInsuranceIconRegion;
193 static MOUSE_REGION gDepositIconRegion;
194
195 // general line..current and old
196 INT32 giHighLine=-1;
197
198 // assignment's line...glow box
199 INT32 giAssignHighLine=-1;
200
201 // destination plot line....glow box
202 INT32 giDestHighLine=-1;
203
204 // contract selection glow box
205 INT32 giContractHighLine = -1;
206
207 // the sleep column glow box
208 INT32 giSleepHighLine = -1;
209
210 // pop up box textures
211 SGPVSurface* guiPOPUPTEX;
212 SGPVObject* guiPOPUPBORDERS;
213
214 // the currently selected character arrow
215 static SGPVObject* guiSelectedCharArrow;
216
217 static GUIButtonRef guiUpdatePanelButtons[2];
218
219 // the update panel
220 SGPVObject* guiUpdatePanelTactical;
221
222 struct MERC_LEAVE_ITEM
223 {
224 OBJECTTYPE o;
225 MERC_LEAVE_ITEM* pNext;
226 };
227
228 // the leave item list
229 static MERC_LEAVE_ITEM* gpLeaveListHead[NUM_LEAVE_LIST_SLOTS];
230
231 // holds ids of mercs who left stuff behind
232 static UINT32 guiLeaveListOwnerProfileId[NUM_LEAVE_LIST_SLOTS];
233
234 // timers for double click
235 static INT32 giDblClickTimersForMoveBoxMouseRegions[MAX_POPUP_BOX_STRING_COUNT];
236
237 UINT32 guiSectorLocatorBaseTime = 0;
238
239
240 // which menus are we showing
241 BOOLEAN fShowAssignmentMenu = FALSE;
242 BOOLEAN fShowTrainingMenu = FALSE;
243 BOOLEAN fShowAttributeMenu = FALSE;
244 BOOLEAN fShowSquadMenu = FALSE;
245 BOOLEAN fShowContractMenu = FALSE;
246
247 static BOOLEAN fRebuildMoveBox = FALSE;
248
249 // at least one merc was hired at some time
250 BOOLEAN gfAtLeastOneMercWasHired = FALSE;
251
252
InitalizeVehicleAndCharacterList(void)253 void InitalizeVehicleAndCharacterList( void )
254 {
255 // will init the vehicle and character lists to zero
256 std::fill(std::begin(gCharactersList), std::end(gCharactersList), MapScreenCharacterSt{});
257 }
258
259
SetEntryInSelectedCharacterList(INT8 bEntry)260 void SetEntryInSelectedCharacterList( INT8 bEntry )
261 {
262 Assert( ( bEntry >= 0 ) && ( bEntry < MAX_CHARACTER_COUNT ) );
263 gCharactersList[bEntry].selected = TRUE;
264 }
265
266
ResetEntryForSelectedList(INT8 bEntry)267 void ResetEntryForSelectedList( INT8 bEntry )
268 {
269 Assert( ( bEntry >= 0 ) && ( bEntry < MAX_CHARACTER_COUNT ) );
270 gCharactersList[bEntry].selected = FALSE;
271 }
272
273
ResetSelectedListForMapScreen(void)274 void ResetSelectedListForMapScreen( void )
275 {
276 // set all the entries int he selected list to false
277 for (size_t i = 0; i != MAX_CHARACTER_COUNT; ++i)
278 {
279 gCharactersList[i].selected = FALSE;
280 }
281
282 // if we still have a valid dude selected
283 if (GetSelectedInfoChar() != NULL)
284 {
285 // then keep him selected
286 SetEntryInSelectedCharacterList( bSelectedInfoChar );
287 }
288 }
289
290
IsEntryInSelectedListSet(INT8 bEntry)291 BOOLEAN IsEntryInSelectedListSet( INT8 bEntry )
292 {
293 Assert( ( bEntry >= 0 ) && ( bEntry < MAX_CHARACTER_COUNT ) );
294 return gCharactersList[bEntry].selected;
295 }
296
297
ToggleEntryInSelectedList(INT8 bEntry)298 void ToggleEntryInSelectedList( INT8 bEntry )
299 {
300 Assert( ( bEntry >= 0 ) && ( bEntry < MAX_CHARACTER_COUNT ) );
301 MapScreenCharacterSt* const c = &gCharactersList[bEntry];
302 c->selected = !c->selected;
303 }
304
305
BuildSelectedListFromAToB(INT8 bA,INT8 bB)306 void BuildSelectedListFromAToB( INT8 bA, INT8 bB )
307 {
308 INT8 bStart =0, bEnd = 0;
309
310 // run from a to b..set slots as selected
311
312 if( bA > bB )
313 {
314 bStart = bB;
315 bEnd = bA;
316 }
317 else
318 {
319 bStart = bA;
320 bEnd = bB;
321 }
322
323 // run through list and set all intermediaries to true
324
325 for( ; bStart <= bEnd; bStart++ )
326 {
327 SetEntryInSelectedCharacterList( bStart );
328 }
329 }
330
331
MultipleCharacterListEntriesSelected(void)332 BOOLEAN MultipleCharacterListEntriesSelected( void )
333 {
334 UINT8 ubSelectedCnt = 0;
335
336 // check if more than one person is selected in the selected list
337 CFOR_EACH_SELECTED_IN_CHAR_LIST(c)
338 {
339 ++ubSelectedCnt;
340 }
341
342 if( ubSelectedCnt > 1 )
343 {
344 return( TRUE );
345 }
346 else
347 {
348 return( FALSE );
349 }
350 }
351
352
ResetAssignmentsForMercsTrainingUnpaidSectorsInSelectedList()353 void ResetAssignmentsForMercsTrainingUnpaidSectorsInSelectedList()
354 {
355 CFOR_EACH_IN_CHAR_LIST(c)
356 {
357 SOLDIERTYPE* const pSoldier = c->merc;
358 if( pSoldier->bAssignment == TRAIN_TOWN )
359 {
360 if (!SectorInfo[SECTOR(pSoldier->sSectorX, pSoldier->sSectorY)].fMilitiaTrainingPaid)
361 {
362 ResumeOldAssignment( pSoldier );
363 }
364 }
365 }
366 }
367
368
ResetAssignmentOfMercsThatWereTrainingMilitiaInThisSector(INT16 sSectorX,INT16 sSectorY)369 void ResetAssignmentOfMercsThatWereTrainingMilitiaInThisSector( INT16 sSectorX, INT16 sSectorY )
370 {
371 CFOR_EACH_IN_CHAR_LIST(c)
372 {
373 SOLDIERTYPE* const pSoldier = c->merc;
374 if( pSoldier->bAssignment == TRAIN_TOWN )
375 {
376 if( ( pSoldier->sSectorX == sSectorX ) && ( pSoldier->sSectorY == sSectorY ) && ( pSoldier->bSectorZ == 0 ) )
377 {
378 ResumeOldAssignment( pSoldier );
379 }
380 }
381 }
382 }
383
384
385 static BOOLEAN CanSoldierMoveWithVehicleId(const SOLDIERTYPE* s, INT32 iVehicle1Id);
386
387
388 // check if the members of the selected list move with this guy... are they in the same mvt group?
DeselectSelectedListMercsWhoCantMoveWithThisGuy(const SOLDIERTYPE * const pSoldier)389 void DeselectSelectedListMercsWhoCantMoveWithThisGuy(const SOLDIERTYPE* const pSoldier)
390 {
391 INT32 iCounter = 0;
392
393 // deselect any other selected mercs that can't travel together with pSoldier
394 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
395 {
396 const MapScreenCharacterSt* const c = &gCharactersList[iCounter];
397 if (!c->selected) continue;
398
399 const SOLDIERTYPE* const pSoldier2 = c->merc;
400 if (pSoldier2 == NULL) continue;
401
402 // skip the guy we are
403 if (pSoldier == pSoldier2) continue;
404
405 // NOTE ABOUT THE VEHICLE TESTS BELOW:
406 // Vehicles and foot squads can't plot movement together!
407 // The ETAs are different, and unlike squads, vehicles can't travel everywhere!
408 // However, different vehicles CAN plot together, since they all travel at the same rates now
409
410 // if anchor guy is IN a vehicle
411 if( pSoldier->bAssignment == VEHICLE )
412 {
413 if ( !CanSoldierMoveWithVehicleId( pSoldier2, pSoldier->iVehicleId ) )
414 {
415 // reset entry for selected list
416 ResetEntryForSelectedList( ( INT8 )iCounter );
417 }
418 }
419 // if anchor guy IS a vehicle
420 else if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
421 {
422 if ( !CanSoldierMoveWithVehicleId( pSoldier2, pSoldier->bVehicleID ) )
423 {
424 // reset entry for selected list
425 ResetEntryForSelectedList( ( INT8 )iCounter );
426 }
427 }
428 // if this guy is IN a vehicle
429 else if( pSoldier2->bAssignment == VEHICLE )
430 {
431 if ( !CanSoldierMoveWithVehicleId( pSoldier, pSoldier2->iVehicleId ) )
432 {
433 // reset entry for selected list
434 ResetEntryForSelectedList( ( INT8 )iCounter );
435 }
436 }
437 // if this guy IS a vehicle
438 else if ( pSoldier2->uiStatusFlags & SOLDIER_VEHICLE )
439 {
440 if ( !CanSoldierMoveWithVehicleId( pSoldier, pSoldier2->bVehicleID ) )
441 {
442 // reset entry for selected list
443 ResetEntryForSelectedList( ( INT8 )iCounter );
444 }
445 }
446 // reject those not a squad (vehicle handled above)
447 else if( pSoldier2->bAssignment >= ON_DUTY )
448 {
449 ResetEntryForSelectedList( ( INT8 )iCounter );
450 }
451 else
452 {
453 // reject those not in the same sector
454 if( ( pSoldier->sSectorX != pSoldier2->sSectorX ) ||
455 ( pSoldier->sSectorY != pSoldier2->sSectorY ) ||
456 ( pSoldier->bSectorZ != pSoldier2->bSectorZ ) )
457 {
458 ResetEntryForSelectedList( ( INT8 )iCounter );
459 }
460
461 // if either is between sectors, they must be in the same movement group
462 if ( ( pSoldier->fBetweenSectors || pSoldier2->fBetweenSectors ) &&
463 ( pSoldier->ubGroupID != pSoldier2->ubGroupID ) )
464 {
465 ResetEntryForSelectedList( ( INT8 )iCounter );
466 }
467 }
468
469 // different movement groups in same sector is OK, even if they're not travelling together
470 }
471 }
472
473
474 static BOOLEAN AnyMercInSameSquadOrVehicleIsSelected(const SOLDIERTYPE* s);
475
476
SelectUnselectedMercsWhoMustMoveWithThisGuy(void)477 void SelectUnselectedMercsWhoMustMoveWithThisGuy( void )
478 {
479 INT32 iCounter = 0;
480
481 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
482 {
483 const MapScreenCharacterSt* const c = &gCharactersList[iCounter];
484 if (c->selected) continue;
485
486 const SOLDIERTYPE* const pSoldier = c->merc;
487 if (pSoldier == NULL) continue;
488
489 // if on a squad or in a vehicle
490 if ( ( pSoldier->bAssignment < ON_DUTY ) || ( pSoldier->bAssignment == VEHICLE ) )
491 {
492 // and a member of that squad or vehicle is selected
493 if ( AnyMercInSameSquadOrVehicleIsSelected( pSoldier ) )
494 {
495 // then also select this guy
496 SetEntryInSelectedCharacterList( ( INT8 ) iCounter );
497 }
498 }
499 }
500 }
501
502
AnyMercInSameSquadOrVehicleIsSelected(const SOLDIERTYPE * const pSoldier)503 static BOOLEAN AnyMercInSameSquadOrVehicleIsSelected(const SOLDIERTYPE* const pSoldier)
504 {
505 CFOR_EACH_SELECTED_IN_CHAR_LIST(c)
506 {
507 const SOLDIERTYPE* const pSoldier2 = c->merc;
508
509 // if they have the same assignment
510 if( pSoldier->bAssignment == pSoldier2->bAssignment )
511 {
512 // same squad?
513 if ( pSoldier->bAssignment < ON_DUTY )
514 {
515 return ( TRUE );
516 }
517
518 // same vehicle?
519 if ( ( pSoldier->bAssignment == VEHICLE ) && ( pSoldier->iVehicleId == pSoldier2->iVehicleId ) )
520 {
521 return ( TRUE );
522 }
523 }
524
525 // target guy is in a vehicle, and this guy IS that vehicle
526 if( ( pSoldier->bAssignment == VEHICLE ) && ( pSoldier2->uiStatusFlags & SOLDIER_VEHICLE ) &&
527 ( pSoldier->iVehicleId == pSoldier2->bVehicleID ) )
528 {
529 return ( TRUE );
530 }
531
532 // this guy is in a vehicle, and the target guy IS that vehicle
533 if( ( pSoldier2->bAssignment == VEHICLE ) && ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) &&
534 ( pSoldier2->iVehicleId == pSoldier->bVehicleID ) )
535 {
536 return ( TRUE );
537 }
538 }
539
540 return ( FALSE );
541 }
542
543
544
RestoreBackgroundForAssignmentGlowRegionList(void)545 void RestoreBackgroundForAssignmentGlowRegionList( void )
546 {
547 static INT32 iOldAssignmentLine = -1;
548
549 // will restore the background region of the assignment list after a glow has ceased
550 // ( a _LOST_MOUSE reason to the assignment region mvt callback handler )
551
552 if (fShowAssignmentMenu)
553 {
554 // force update
555 ForceUpDateOfBox( ghAssignmentBox );
556 ForceUpDateOfBox( ghEpcBox );
557 ForceUpDateOfBox( ghRemoveMercAssignBox );
558 if (fShowSquadMenu)
559 {
560 ForceUpDateOfBox( ghSquadBox );
561 }
562 else if (fShowTrainingMenu)
563 {
564 ForceUpDateOfBox( ghTrainingBox );
565 }
566
567 }
568
569 if( fDisableDueToBattleRoster )
570 {
571 return;
572 }
573
574 if( iOldAssignmentLine != giAssignHighLine )
575 {
576 // restore background
577 RestoreExternBackgroundRect( ASSIGN_X, Y_START - 1, ASSIGN_WIDTH, ( INT16 )( ( ( MAX_CHARACTER_COUNT + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) );
578
579 // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!!
580 fTeamPanelDirty = TRUE;
581
582 // set old to current
583 iOldAssignmentLine = giAssignHighLine;
584 }
585 }
586
RestoreBackgroundForDestinationGlowRegionList(void)587 void RestoreBackgroundForDestinationGlowRegionList( void )
588 {
589 static INT32 iOldDestinationLine = -1;
590
591 // will restore the background region of the destinationz list after a glow has ceased
592 // ( a _LOST_MOUSE reason to the assignment region mvt callback handler )
593
594 if( fDisableDueToBattleRoster )
595 {
596 return;
597 }
598
599 if( iOldDestinationLine != giDestHighLine )
600 {
601 // restore background
602 RestoreExternBackgroundRect( DEST_ETA_X, Y_START - 1, DEST_ETA_WIDTH, ( INT16 )( ( ( MAX_CHARACTER_COUNT + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) );
603
604 // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!!
605 fTeamPanelDirty = TRUE;
606
607 // set old to current
608 iOldDestinationLine = giDestHighLine;
609 }
610 }
611
RestoreBackgroundForContractGlowRegionList(void)612 void RestoreBackgroundForContractGlowRegionList( void )
613 {
614 static INT32 iOldContractLine = -1;
615
616 // will restore the background region of the destinationz list after a glow has ceased
617 // ( a _LOST_MOUSE reason to the assignment region mvt callback handler )
618
619 if( fDisableDueToBattleRoster )
620 {
621 return;
622 }
623
624 if( iOldContractLine != giContractHighLine )
625 {
626 // restore background
627 RestoreExternBackgroundRect( TIME_REMAINING_X, Y_START - 1, TIME_REMAINING_WIDTH, ( INT16 )( ( ( MAX_CHARACTER_COUNT + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ) ;
628
629 // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!!
630 fTeamPanelDirty = TRUE;
631
632 // set old to current
633 iOldContractLine = giContractHighLine;
634 }
635 }
636
637
RestoreBackgroundForSleepGlowRegionList(void)638 void RestoreBackgroundForSleepGlowRegionList( void )
639 {
640 static INT32 iOldSleepHighLine = -1;
641
642 // will restore the background region of the sleep list after a glow has ceased
643 // ( a _LOST_MOUSE reason to the assignment region mvt callback handler )
644
645 if( fDisableDueToBattleRoster )
646 {
647 return;
648 }
649
650 if( iOldSleepHighLine != giSleepHighLine )
651 {
652 // restore background
653 RestoreExternBackgroundRect( SLEEP_X, Y_START - 1, SLEEP_WIDTH, ( INT16 )( ( ( MAX_CHARACTER_COUNT + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ) ;
654
655 // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!!
656 fTeamPanelDirty = TRUE;
657
658 // set old to current
659 iOldSleepHighLine = giSleepHighLine;
660 }
661 }
662
PlayGlowRegionSound(void)663 void PlayGlowRegionSound( void )
664 {
665 // play a new message sound, if there is one playing, do nothing
666 static UINT32 uiSoundId = 0;
667
668 if( uiSoundId != 0 )
669 {
670 // is sound playing?..don't play new one
671 if (SoundIsPlaying(uiSoundId))
672 {
673 return;
674 }
675 }
676
677 // otherwise no sound playing, play one
678 uiSoundId = PlayJA2SampleFromFile(SOUNDSDIR "/glowclick.wav", MIDVOLUME, 1, MIDDLEPAN);
679 }
680
681
682
CharacterIsGettingPathPlotted(INT16 const sCharNumber)683 BOOLEAN CharacterIsGettingPathPlotted(INT16 const sCharNumber)
684 {
685 // valid character number?
686 if( ( sCharNumber < 0 ) || ( sCharNumber >= MAX_CHARACTER_COUNT ) )
687 {
688 return( FALSE );
689 }
690
691 // is the character a valid one?
692 if (gCharactersList[sCharNumber].merc == NULL) return FALSE;
693
694 // if the highlighted line character is also selected
695 if ( ( ( giDestHighLine != -1 ) && IsEntryInSelectedListSet ( ( INT8 ) giDestHighLine ) ) ||
696 ( ( bSelectedDestChar != -1 ) && IsEntryInSelectedListSet ( bSelectedDestChar ) ) )
697 {
698 // then ALL selected lines will be affected
699 if( IsEntryInSelectedListSet( ( INT8 ) sCharNumber ) )
700 {
701 return( TRUE );
702 }
703 }
704 else
705 {
706 // if he is *the* selected dude
707 if( bSelectedDestChar == sCharNumber )
708 {
709 return ( TRUE );
710 }
711
712 // ONLY the highlighted line will be affected
713 if ( sCharNumber == giDestHighLine )
714 {
715 return( TRUE );
716 }
717 }
718
719 return ( FALSE );
720 }
721
IsCharacterSelectedForAssignment(INT16 sCharNumber)722 BOOLEAN IsCharacterSelectedForAssignment( INT16 sCharNumber )
723 {
724 // valid character number?
725 if( ( sCharNumber < 0 ) || ( sCharNumber >= MAX_CHARACTER_COUNT ) )
726 {
727 return( FALSE );
728 }
729
730 // is the character a valid one?
731 if (gCharactersList[sCharNumber].merc == NULL) return FALSE;
732
733 // if the highlighted line character is also selected
734 if ( ( giAssignHighLine != -1 ) && IsEntryInSelectedListSet ( ( INT8 ) giAssignHighLine ) )
735 {
736 // then ALL selected lines will be affected
737 if( IsEntryInSelectedListSet( ( INT8 ) sCharNumber ) )
738 {
739 return( TRUE );
740 }
741 }
742 else
743 {
744 // ONLY the highlighted line will be affected
745 if ( sCharNumber == giAssignHighLine )
746 {
747 return( TRUE );
748 }
749 }
750
751 return( FALSE );
752 }
753
754
755
IsCharacterSelectedForSleep(INT16 sCharNumber)756 BOOLEAN IsCharacterSelectedForSleep( INT16 sCharNumber )
757 {
758 // valid character number?
759 if( ( sCharNumber < 0 ) || ( sCharNumber >= MAX_CHARACTER_COUNT ) )
760 {
761 return( FALSE );
762 }
763
764 // is the character a valid one?
765 if (gCharactersList[sCharNumber].merc == NULL) return FALSE;
766
767 // if the highlighted line character is also selected
768 if ( ( giSleepHighLine != -1 ) && IsEntryInSelectedListSet ( ( INT8 ) giSleepHighLine ) )
769 {
770 // then ALL selected lines will be affected
771 if( IsEntryInSelectedListSet( ( INT8 ) sCharNumber ) )
772 {
773 return( TRUE );
774 }
775 }
776 else
777 {
778 // ONLY the highlighted line will be affected
779 if ( sCharNumber == giSleepHighLine )
780 {
781 return( TRUE );
782 }
783 }
784
785 return( FALSE );
786 }
787
DisableTeamInfoPanels(void)788 void DisableTeamInfoPanels( void )
789 {
790 // disable team info panel
791 fDisableDueToBattleRoster = TRUE;
792 }
793
794
EnableTeamInfoPanels(void)795 void EnableTeamInfoPanels( void )
796 {
797 // enable team info panel
798 fDisableDueToBattleRoster = FALSE;
799 }
800
801
DoMapMessageBoxWithRect(MessageBoxStyleID ubStyle,const ST::string & str,ScreenID uiExitScreen,MessageBoxFlags usFlags,MSGBOX_CALLBACK ReturnCallback,const SGPBox * centering_rect)802 void DoMapMessageBoxWithRect(MessageBoxStyleID ubStyle, const ST::string& str, ScreenID uiExitScreen, MessageBoxFlags usFlags, MSGBOX_CALLBACK ReturnCallback, const SGPBox* centering_rect)
803 { // reset the highlighted line
804 giHighLine = -1;
805 DoMessageBox(ubStyle, str, uiExitScreen, usFlags, ReturnCallback, centering_rect);
806 }
807
808
DoMapMessageBox(MessageBoxStyleID ubStyle,const ST::string & str,ScreenID uiExitScreen,MessageBoxFlags usFlags,MSGBOX_CALLBACK ReturnCallback)809 void DoMapMessageBox(MessageBoxStyleID ubStyle, const ST::string& str, ScreenID uiExitScreen, MessageBoxFlags usFlags, MSGBOX_CALLBACK ReturnCallback)
810 {
811 // reset the highlighted line
812 giHighLine = -1;
813
814 // do message box and return
815 SGPBox const centering_rect = { 0, 0, SCREEN_WIDTH, INV_INTERFACE_START_Y };
816 DoMessageBox(ubStyle, str, uiExitScreen, usFlags, ReturnCallback, ¢ering_rect);
817 }
818
819
820
GoDownOneLevelInMap(void)821 void GoDownOneLevelInMap( void )
822 {
823 JumpToLevel( iCurrentMapSectorZ + 1 );
824 }
825
826
GoUpOneLevelInMap(void)827 void GoUpOneLevelInMap( void )
828 {
829 JumpToLevel( iCurrentMapSectorZ - 1 );
830 }
831
832
JumpToLevel(INT32 iLevel)833 void JumpToLevel( INT32 iLevel )
834 {
835 if (gfPreBattleInterfaceActive) return;
836
837 // disable level-changes while in inventory pool (for keyboard equivalents!)
838 if( fShowMapInventoryPool )
839 return;
840
841 if (bSelectedDestChar != -1 || fPlotForHelicopter)
842 {
843 AbortMovementPlottingMode( );
844 }
845
846 if( iLevel < 0 )
847 {
848 iLevel = 0;
849 }
850
851 if( iLevel > MAX_DEPTH_OF_MAP )
852 {
853 iLevel = MAX_DEPTH_OF_MAP;
854 }
855
856 // set current sector Z to level passed
857 ChangeSelectedMapSector( sSelMapX, sSelMapY, ( INT8 )iLevel );
858 }
859
860
861 // check against old contract times, update as nessacary
CheckAndUpdateBasedOnContractTimes(void)862 void CheckAndUpdateBasedOnContractTimes( void )
863 {
864 INT32 iCounter = 0;
865 INT32 iTimeRemaining = 0;
866
867 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
868 {
869 const SOLDIERTYPE* const s = gCharactersList[iCounter].merc;
870 if (s == NULL) continue;
871
872 // what kind of merc
873 if (s->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC)
874 {
875 // amount of time left on contract
876 iTimeRemaining = s->iEndofContractTime-GetWorldTotalMin();
877 if(iTimeRemaining >60*24)
878 {
879 // more than a day, display in green
880 iTimeRemaining/=(60*24);
881
882 // check if real change in contract time
883 if( iTimeRemaining != iOldContractTimes[ iCounter ])
884 {
885 iOldContractTimes[ iCounter ] = iTimeRemaining;
886
887 // dirty screen
888 fTeamPanelDirty = TRUE;
889 fCharacterInfoPanelDirty = TRUE;
890 }
891 }
892 else
893 {
894 // less than a day, display hours left in red
895 iTimeRemaining/=60;
896
897 // check if real change in contract time
898 if( iTimeRemaining != iOldContractTimes[ iCounter ])
899 {
900 iOldContractTimes[ iCounter ] = iTimeRemaining;
901 // dirty screen
902 fTeamPanelDirty = TRUE;
903 fCharacterInfoPanelDirty = TRUE;
904 }
905 }
906 }
907 else if (s->ubWhatKindOfMercAmI == MERC_TYPE__MERC)
908 {
909 iTimeRemaining = s->iTotalContractLength;
910
911 if( iTimeRemaining != iOldContractTimes[ iCounter ])
912 {
913 iOldContractTimes[ iCounter ] = iTimeRemaining;
914
915 // dirty screen
916 fTeamPanelDirty = TRUE;
917 fCharacterInfoPanelDirty = TRUE;
918 }
919 }
920 }
921 }
922
923
HandleDisplayOfSelectedMercArrows()924 void HandleDisplayOfSelectedMercArrows()
925 {
926 if (!GetSelectedInfoChar()) return;
927 if (fShowInventoryFlag) return;
928
929 { // Blit one by the selected merc
930 INT16 y = Y_START + bSelectedInfoChar * (Y_SIZE + 2) - 1;
931 if (bSelectedInfoChar >= FIRST_VEHICLE) y += 6;
932 BltVideoObject(guiSAVEBUFFER, guiSelectedCharArrow, 0,SELECTED_CHAR_ARROW_X, y);
933 }
934
935 // now run through the selected list of guys, an arrow for each
936 UINT8 const dest_group = bSelectedDestChar != -1 ? gCharactersList[bSelectedDestChar].merc->ubGroupID : 0;
937 for (UINT8 i = 0; i != MAX_CHARACTER_COUNT; ++i)
938 {
939 SOLDIERTYPE const* const s = gCharactersList[i].merc;
940 if (!s) continue;
941
942 // Is he in the selected list or in the same mvt group as this guy?
943 if (!IsEntryInSelectedListSet(i) && (s->ubGroupID == 0 || s->ubGroupID != dest_group)) continue;
944
945 INT16 y = Y_START + i * (Y_SIZE + 2) - 1;
946 if (i >= FIRST_VEHICLE) y += 6;
947 BltVideoObject(guiSAVEBUFFER, guiSelectedCharArrow, 0, SELECTED_CHAR_ARROW_X, y);
948 }
949 }
950
951
GetMoraleString(SOLDIERTYPE const & s)952 ST::string GetMoraleString(SOLDIERTYPE const& s)
953 {
954 return
955 s.uiStatusFlags & SOLDIER_DEAD ? pMoralStrings[5] :
956 s.bMorale > 80 ? pMoralStrings[0] :
957 s.bMorale > 65 ? pMoralStrings[1] :
958 s.bMorale > 35 ? pMoralStrings[2] :
959 s.bMorale > 20 ? pMoralStrings[3] :
960 pMoralStrings[4];
961 }
962
963
964 // NOTE: This doesn't use the "LeaveList" system at all!
HandleLeavingOfEquipmentInCurrentSector(SOLDIERTYPE & s)965 void HandleLeavingOfEquipmentInCurrentSector(SOLDIERTYPE& s)
966 {
967 // Just drop the stuff in the current sector
968 GridNo gridno;
969 bool const here = s.sSectorX == gWorldSectorX && s.sSectorY == gWorldSectorY && s.bSectorZ == gbWorldSectorZ;
970 if (here)
971 {
972 // ATE: Mercs can have a gridno of NOWHERE
973 gridno = s.sGridNo;
974 if (gridno == NOWHERE)
975 {
976 gridno = RandomGridNo();
977
978 GridNo tmp_gridno = FindNearestAvailableGridNoForItem(gridno, 5);
979 if (tmp_gridno == NOWHERE) tmp_gridno = FindNearestAvailableGridNoForItem(gridno, 15);
980 if (tmp_gridno != NOWHERE) gridno = tmp_gridno;
981 }
982 }
983 else
984 {
985 // ATE: Use insertion gridno if not nowhere and insertion is gridno
986 if (s.ubStrategicInsertionCode == INSERTION_CODE_GRIDNO && s.usStrategicInsertionData != NOWHERE)
987 {
988 gridno = s.usStrategicInsertionData;
989 }
990 else
991 {
992 gridno = RandomGridNo();
993 }
994 }
995
996 FOR_EACH_SOLDIER_INV_SLOT(i, s)
997 {
998 if (i->ubNumberOfObjects == 0) continue;
999
1000 if (here)
1001 {
1002 AddItemToPool(gridno, i, VISIBLE, s.bLevel, WORLD_ITEM_REACHABLE, 0);
1003 }
1004 else
1005 {
1006 AddItemsToUnLoadedSector(s.sSectorX, s.sSectorY, s.bSectorZ, gridno, 1, i, s.bLevel, WOLRD_ITEM_FIND_SWEETSPOT_FROM_GRIDNO | WORLD_ITEM_REACHABLE, 0, VISIBLE);
1007 }
1008 }
1009
1010 DropKeysInKeyRing(s, gridno, s.bLevel, VISIBLE, false, 0, false);
1011 }
1012
1013
1014 static INT32 SetUpDropItemListForMerc(SOLDIERTYPE&);
1015
1016
HandleMercLeavingEquipment(SOLDIERTYPE & s,bool const in_drassen)1017 void HandleMercLeavingEquipment(SOLDIERTYPE& s, bool const in_drassen)
1018 {
1019 // Stash the items into a linked list hanging of a free "leave item list" slot
1020 INT32 const slot = SetUpDropItemListForMerc(s);
1021 if (slot != -1)
1022 { // Post event to drop it there 6 hours later
1023 StrategicEventKind const e = in_drassen ? EVENT_MERC_LEAVE_EQUIP_IN_DRASSEN : EVENT_MERC_LEAVE_EQUIP_IN_OMERTA;
1024 AddStrategicEvent(e, GetWorldTotalMin() + 6 * 60, slot);
1025 }
1026 else
1027 { // Otherwise there's no free slots left (shouldn't ever happen)
1028 AssertMsg(FALSE, "HandleMercLeavingEquipment: No more free slots, equipment lost");
1029 }
1030 }
1031
1032
1033 static void FreeLeaveListSlot(UINT32 uiSlotIndex);
1034
1035
HandleEquipmentLeft(UINT32 const slot_idx,INT const sector,GridNo const grid)1036 static void HandleEquipmentLeft(UINT32 const slot_idx, INT const sector, GridNo const grid)
1037 {
1038 Assert(slot_idx < NUM_LEAVE_LIST_SLOTS);
1039
1040 if (MERC_LEAVE_ITEM* i = gpLeaveListHead[slot_idx])
1041 {
1042 ST::string sString;
1043 ST::string town = GCM->getTownLocative(GetTownIdForSector(sector));
1044 int const x = SECTORX(sector);
1045 char const y = SECTORY(sector) - 1 + 'A';
1046 ProfileID const id = guiLeaveListOwnerProfileId[slot_idx];
1047 if (id != NO_PROFILE)
1048 {
1049 sString = st_format_printf(str_left_equipment, GetProfile(id).zNickname, town, y, x);
1050 }
1051 else
1052 {
1053 sString = ST::format("A departing merc has left their equipment in {} ({c}{}).", town, y, x);
1054 }
1055 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, sString);
1056
1057 bool const is_sector_loaded = gWorldSectorX == SECTORX(sector) && gWorldSectorY == SECTORY(sector) && gbWorldSectorZ == 0;
1058 for (; i; i = i->pNext)
1059 {
1060 if (is_sector_loaded)
1061 {
1062 AddItemToPool(grid, &i->o, VISIBLE, 0, WORLD_ITEM_REACHABLE, 0);
1063 }
1064 else
1065 { // Given this slot value, add to sector item list
1066 AddItemsToUnLoadedSector(SECTORX(sector), SECTORY(sector), 0, grid, 1, &i->o, 0, WORLD_ITEM_REACHABLE, 0, VISIBLE);
1067 }
1068 }
1069 }
1070
1071 FreeLeaveListSlot(slot_idx);
1072 }
1073
1074
HandleEquipmentLeftInOmerta(const UINT32 uiSlotIndex)1075 void HandleEquipmentLeftInOmerta(const UINT32 uiSlotIndex)
1076 {
1077 HandleEquipmentLeft(uiSlotIndex, START_SECTOR, START_SECTOR_LEAVE_EQUIP_GRIDNO);
1078 }
1079
1080
HandleEquipmentLeftInDrassen(const UINT32 uiSlotIndex)1081 void HandleEquipmentLeftInDrassen(const UINT32 uiSlotIndex)
1082 {
1083 auto primaryAirport = GCM->getPrimaryShippingDestination();
1084 HandleEquipmentLeft(uiSlotIndex, primaryAirport->getDeliverySector(), 10433);
1085 }
1086
1087
InitLeaveList(void)1088 void InitLeaveList( void )
1089 {
1090 INT32 iCounter = 0;
1091
1092 // init leave list with NULLS/zeroes
1093 for( iCounter = 0; iCounter < NUM_LEAVE_LIST_SLOTS; iCounter++ )
1094 {
1095 gpLeaveListHead[ iCounter ] = NULL;
1096 guiLeaveListOwnerProfileId[ iCounter ] = NO_PROFILE;
1097 }
1098 }
1099
1100
ShutDownLeaveList(void)1101 void ShutDownLeaveList( void )
1102 {
1103 INT32 iCounter = 0;
1104
1105 for( iCounter = 0; iCounter < NUM_LEAVE_LIST_SLOTS; iCounter++ )
1106 {
1107 // go through nodes and free them
1108 if ( gpLeaveListHead[ iCounter ] != NULL )
1109 {
1110 FreeLeaveListSlot( iCounter );
1111 }
1112 }
1113 }
1114
1115
AddItemToLeaveIndex(const OBJECTTYPE * const o,const UINT32 uiSlotIndex)1116 void AddItemToLeaveIndex(const OBJECTTYPE* const o, const UINT32 uiSlotIndex)
1117 {
1118 Assert(uiSlotIndex < NUM_LEAVE_LIST_SLOTS);
1119
1120 if (o == NULL) return;
1121
1122 MERC_LEAVE_ITEM* const mli = new MERC_LEAVE_ITEM{};
1123 mli->o = *o;
1124 mli->pNext = NULL;
1125
1126 // Append node to list
1127 MERC_LEAVE_ITEM** anchor = &gpLeaveListHead[uiSlotIndex];
1128 while (*anchor != NULL) anchor = &(*anchor)->pNext;
1129 *anchor = mli;
1130 }
1131
1132
1133 // release memory for all items in this slot's leave item list
FreeLeaveListSlot(UINT32 uiSlotIndex)1134 static void FreeLeaveListSlot(UINT32 uiSlotIndex)
1135 {
1136 MERC_LEAVE_ITEM *pCurrent = NULL, *pTemp = NULL;
1137
1138 Assert( uiSlotIndex < NUM_LEAVE_LIST_SLOTS );
1139
1140 pCurrent = gpLeaveListHead[ uiSlotIndex ];
1141
1142 // go through nodes and free them
1143 while( pCurrent )
1144 {
1145 pTemp = pCurrent->pNext;
1146 delete pCurrent;
1147 pCurrent = pTemp;
1148 }
1149
1150 gpLeaveListHead[ uiSlotIndex ] = NULL;
1151 }
1152
1153
1154 // first free slot in equip leave list
FindFreeSlotInLeaveList(void)1155 static INT32 FindFreeSlotInLeaveList(void)
1156 {
1157 INT32 iCounter = 0;
1158
1159 for( iCounter = 0; iCounter < NUM_LEAVE_LIST_SLOTS; iCounter++ )
1160 {
1161 if( gpLeaveListHead[ iCounter ] == NULL )
1162 {
1163 return( iCounter );
1164 }
1165 }
1166
1167 return( -1 );
1168 }
1169
1170
1171 static void SetUpMercAboutToLeaveEquipment(UINT32 ubProfileId, UINT32 uiSlotIndex);
1172
1173
1174 // Set up a drop list for this grunt, remove items from inventory and profile
SetUpDropItemListForMerc(SOLDIERTYPE & s)1175 static INT32 SetUpDropItemListForMerc(SOLDIERTYPE& s)
1176 {
1177 INT32 const slot = FindFreeSlotInLeaveList();
1178 if (slot == -1) return -1;
1179
1180 CFOR_EACH_SOLDIER_INV_SLOT(i, s)
1181 {
1182 if (i->ubNumberOfObjects == 0) continue;
1183
1184 // Make a linked list of the items left behind, with the ptr to its head in this free slot
1185 AddItemToLeaveIndex(i, slot);
1186 // Store owner's profile id for the items added to this leave slot index
1187 SetUpMercAboutToLeaveEquipment(s.ubProfile, slot);
1188 }
1189
1190 /* ATE: Added this to drop keyring keys - the 2nd last paramter says to add it
1191 * to a leave list; the gridno, level and visiblity are ignored */
1192 DropKeysInKeyRing(s, NOWHERE, 0, VISIBILITY_0, true, slot, false);
1193
1194 // Zero out profiles
1195 MERCPROFILESTRUCT& p = GetProfile(s.ubProfile);
1196 std::fill(std::begin(p.bInvStatus), std::end(p.bInvStatus), 0);
1197 std::fill(std::begin(p.bInvNumber), std::end(p.bInvNumber), 0);
1198 std::fill(std::begin(p.inv), std::end(p.inv), 0);
1199
1200 return slot;
1201 }
1202
1203
1204 // store owner's profile id for the items added to this leave slot index
SetUpMercAboutToLeaveEquipment(UINT32 ubProfileId,UINT32 uiSlotIndex)1205 static void SetUpMercAboutToLeaveEquipment(UINT32 ubProfileId, UINT32 uiSlotIndex)
1206 {
1207 Assert( uiSlotIndex < NUM_LEAVE_LIST_SLOTS );
1208
1209 // store the profile ID of this merc in the same slot that the items are gonna be dropped in
1210 guiLeaveListOwnerProfileId[ uiSlotIndex ] = ubProfileId;
1211
1212 }
1213
1214
HandleGroupAboutToArrive(void)1215 void HandleGroupAboutToArrive( void )
1216 {
1217 // reblit map to change the color of the "people in motion" marker
1218 fMapPanelDirty = TRUE;
1219
1220 DoDeadIsDeadSaveIfNecessary();
1221
1222 // ARM - commented out - don't see why this is needed
1223 // fTeamPanelDirty = TRUE;
1224 // fCharacterInfoPanelDirty = TRUE;
1225 }
1226
1227
CreateMapStatusBarsRegion(void)1228 void CreateMapStatusBarsRegion( void )
1229 {
1230
1231 // create the status region over the bSelectedCharacter info region, to get quick rundown of merc's status
1232 MSYS_DefineRegion( &gMapStatusBarsRegion, BAR_INFO_X - 3, BAR_INFO_Y - 42,(INT16)( BAR_INFO_X + 17), (INT16)(BAR_INFO_Y ), MSYS_PRIORITY_HIGH + 5,
1233 MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
1234 }
1235
1236
RemoveMapStatusBarsRegion(void)1237 void RemoveMapStatusBarsRegion( void )
1238 {
1239
1240 // remove the bSelectedInfoCharacter helath, breath and morale bars info region
1241 MSYS_RemoveRegion( &gMapStatusBarsRegion );
1242 }
1243
1244
UpdateCharRegionHelpText(void)1245 void UpdateCharRegionHelpText(void)
1246 {
1247 SOLDIERTYPE const* const s = GetSelectedInfoChar();
1248
1249 // health/energy/morale
1250 ST::string status;
1251 if (!s || s->bLife == 0)
1252 {
1253 status = ST::null;
1254 }
1255 else if (s->bAssignment == ASSIGNMENT_POW)
1256 {
1257 // POW - stats unknown
1258 status = ST::format("{}: ??, {}: ??, {}: ??",
1259 pMapScreenStatusStrings[0],
1260 pMapScreenStatusStrings[1],
1261 pMapScreenStatusStrings[2]
1262 );
1263 }
1264 else if (AM_A_ROBOT(s))
1265 {
1266 // robot (condition only)
1267 status = ST::format("{}: {}/{}",
1268 pMapScreenStatusStrings[3], s->bLife, s->bLifeMax
1269 );
1270 }
1271 else if (s->uiStatusFlags & SOLDIER_VEHICLE)
1272 {
1273 // vehicle (condition/fuel)
1274 status = ST::format("{}: {}/{}, {}: {}/{}",
1275 pMapScreenStatusStrings[3], s->bLife, s->bLifeMax,
1276 pMapScreenStatusStrings[4], s->bBreath, s->bBreathMax
1277 );
1278 }
1279 else
1280 {
1281 // person (health/energy/morale)
1282 ST::string morale = GetMoraleString(*s);
1283 status = ST::format("{}: {}/{}, {}: {}/{}, {}: {}",
1284 pMapScreenStatusStrings[0], s->bLife, s->bLifeMax,
1285 pMapScreenStatusStrings[1], s->bBreath, s->bBreathMax,
1286 pMapScreenStatusStrings[2], morale
1287 );
1288 }
1289 gMapStatusBarsRegion.SetFastHelpText(status);
1290
1291 // update contract button help text
1292 EnableButton(giMapContractButton, s && CanExtendContractForSoldier(s));
1293 }
1294
1295
1296 // find this merc in the mapscreen list and set as selected
FindAndSetThisContractSoldier(SOLDIERTYPE * pSoldier)1297 void FindAndSetThisContractSoldier( SOLDIERTYPE *pSoldier )
1298 {
1299 INT32 iCounter = 0;
1300
1301 fShowContractMenu = FALSE;
1302
1303 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
1304 {
1305 const SOLDIERTYPE* const s = gCharactersList[iCounter].merc;
1306 if (s == NULL) continue;
1307
1308 if (s == pSoldier)
1309 {
1310 ChangeSelectedInfoChar( ( INT8 )iCounter, TRUE );
1311 bSelectedContractChar = ( INT8 )iCounter;
1312 fShowContractMenu = TRUE;
1313
1314 // create
1315 RebuildContractBoxForMerc( pSoldier );
1316
1317 fTeamPanelDirty = TRUE;
1318 fCharacterInfoPanelDirty = TRUE;
1319 }
1320 }
1321 }
1322
1323
HandleMAPUILoseCursorFromOtherScreen(void)1324 void HandleMAPUILoseCursorFromOtherScreen( void )
1325 {
1326 // rerender map without cursors
1327 fMapPanelDirty = TRUE;
1328
1329 if ( fInMapMode )
1330 {
1331 RenderMapRegionBackground( );
1332 }
1333 }
1334
1335
UpdateMapScreenAssignmentPositions(void)1336 void UpdateMapScreenAssignmentPositions( void )
1337 {
1338 // set the position of the pop up boxes
1339
1340 if( guiCurrentScreen != MAP_SCREEN )
1341 {
1342 return;
1343 }
1344
1345
1346 if( bSelectedAssignChar == -1 )
1347 {
1348 if (!gfPreBattleInterfaceActive) giBoxY = 0;
1349 return;
1350 }
1351
1352 if (gCharactersList[bSelectedAssignChar].merc == NULL)
1353 {
1354 if (!gfPreBattleInterfaceActive) giBoxY = 0;
1355 return;
1356 }
1357
1358 if( gfPreBattleInterfaceActive )
1359 {
1360 // do nothing
1361 }
1362 else
1363 {
1364 giBoxY = ( Y_START + ( bSelectedAssignChar ) * ( Y_SIZE + 2 ) );
1365
1366 /* ARM: Removed this - refreshes fine without it, apparently
1367 // make sure the menus don't overlap the map screen bottom panel (but where did 102 come from?)
1368 if( giBoxY >= ( MAP_BOTTOM_Y - 102 ) )
1369 giBoxY = MAP_BOTTOM_Y - 102;
1370 */
1371 }
1372
1373
1374
1375 AssignmentPosition.iY = giBoxY;
1376
1377 AttributePosition.iY = TrainPosition.iY = AssignmentPosition.iY + ( GetFontHeight( MAP_SCREEN_FONT ) + 2 )* ASSIGN_MENU_TRAIN;
1378
1379 VehiclePosition.iY = AssignmentPosition.iY + ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_VEHICLE;
1380 SquadPosition.iY = AssignmentPosition.iY;
1381
1382 if( fShowAssignmentMenu )
1383 {
1384 SetBoxY(ghAssignmentBox, giBoxY);
1385 SetBoxY(ghEpcBox, giBoxY);
1386 }
1387
1388 if( fShowAttributeMenu )
1389 {
1390 SetBoxY(ghAttributeBox, giBoxY + (GetFontHeight(MAP_SCREEN_FONT) + 2) * ASSIGN_MENU_TRAIN);
1391 }
1392
1393 if( fShowRepairMenu )
1394 {
1395 SetBoxY(ghRepairBox, giBoxY + (GetFontHeight(MAP_SCREEN_FONT) + 2) * ASSIGN_MENU_REPAIR);
1396 }
1397 }
1398
1399
RandomMercInGroupSaysQuote(GROUP const & g,UINT16 const quote_num)1400 void RandomMercInGroupSaysQuote(GROUP const& g, UINT16 const quote_num)
1401 {
1402 /* If traversing tactically, don't do this, unless time compression was
1403 * required for some reason (don't go to sector) */
1404 if ((gfTacticalTraversal || g.ubSectorZ > 0) && !IsTimeBeingCompressed())
1405 {
1406 return;
1407 }
1408
1409 // Choose somebody in group
1410 SOLDIERTYPE* mercs_in_group[20];
1411 UINT8 n_mercs = 0;
1412 CFOR_EACH_PLAYER_IN_GROUP(i, &g)
1413 {
1414 SOLDIERTYPE& s = *i->pSoldier;
1415 if (s.bLife < OKLIFE) continue;
1416 if (IsMechanical(s)) continue;
1417 if (AM_AN_EPC(&s)) continue;
1418 if (s.fMercAsleep) continue;
1419 mercs_in_group[n_mercs++] = &s;
1420 }
1421
1422 if (n_mercs > 0)
1423 {
1424 SOLDIERTYPE& chosen = *mercs_in_group[Random(n_mercs)];
1425 TacticalCharacterDialogue(&chosen, quote_num);
1426 }
1427 }
1428
1429
GetNumberOfPeopleInCharacterList(void)1430 INT32 GetNumberOfPeopleInCharacterList( void )
1431 {
1432 // get the number of valid mercs in the mapscreen character list
1433 INT32 count = 0;
1434 CFOR_EACH_IN_CHAR_LIST(c) ++count;
1435 return count;
1436 }
1437
1438
ValidSelectableCharForNextOrPrev(SOLDIERTYPE const & s)1439 static bool ValidSelectableCharForNextOrPrev(SOLDIERTYPE const& s)
1440 {
1441 bool const holding_item = gpItemPointer || fMapInventoryItem;
1442 return
1443 /* If showing merc inventory or holding an item, then the new guy must have
1444 * accessible inventory */
1445 ((!holding_item && !fShowInventoryFlag) || MapCharacterHasAccessibleInventory(s)) &&
1446 (!holding_item || MapscreenCanPassItemToChar(&s));
1447 }
1448
1449
MapscreenCanPassItemToChar(const SOLDIERTYPE * const pNewSoldier)1450 BOOLEAN MapscreenCanPassItemToChar(const SOLDIERTYPE* const pNewSoldier)
1451 {
1452 SOLDIERTYPE *pOldSoldier;
1453
1454 // assumes we're holding an item
1455 Assert(gpItemPointer || fMapInventoryItem);
1456
1457 // if in a hostile sector, disallow
1458 if ( gTacticalStatus.fEnemyInSector )
1459 {
1460 return( FALSE );
1461 }
1462
1463 // if showing sector inventory, and the item came from there
1464 if ( fShowMapInventoryPool && !gpItemPointerSoldier && fMapInventoryItem )
1465 {
1466 // disallow passing items to anyone not in that sector
1467 if ( pNewSoldier->sSectorX != sSelMapX ||
1468 pNewSoldier->sSectorY != sSelMapY ||
1469 pNewSoldier->bSectorZ != ( INT8 )( iCurrentMapSectorZ ) )
1470 {
1471 return( FALSE );
1472 }
1473
1474 if ( pNewSoldier->fBetweenSectors )
1475 {
1476 return( FALSE );
1477 }
1478 }
1479
1480
1481 // if we know who it came from
1482 if ( gpItemPointerSoldier )
1483 {
1484 pOldSoldier = gpItemPointerSoldier;
1485 }
1486 else
1487 {
1488 // it came from either the currently selected merc, or the sector inventory
1489 if (fMapInventoryItem)
1490 {
1491 pOldSoldier = NULL;
1492 }
1493 else
1494 {
1495 pOldSoldier = GetSelectedInfoChar();
1496 }
1497 }
1498
1499
1500 // if another merc had it previously
1501 if ( pOldSoldier != NULL )
1502 {
1503 // disallow passing items to a merc not in the same sector
1504 if ( pNewSoldier->sSectorX != pOldSoldier->sSectorX ||
1505 pNewSoldier->sSectorY != pOldSoldier->sSectorY ||
1506 pNewSoldier->bSectorZ != pOldSoldier->bSectorZ )
1507 {
1508 return( FALSE );
1509 }
1510
1511 // if on the road
1512 if ( pNewSoldier->fBetweenSectors )
1513 {
1514 // other guy must also be on the road...
1515 if ( !pOldSoldier->fBetweenSectors )
1516 {
1517 return( FALSE );
1518 }
1519
1520 // only exchanges between those is the same squad or vehicle are permitted
1521 if ( pNewSoldier->bAssignment != pOldSoldier->bAssignment )
1522 {
1523 return( FALSE );
1524 }
1525
1526 // if in vehicles, make sure it's the same one
1527 if ( ( pNewSoldier->bAssignment == VEHICLE ) && ( pNewSoldier->iVehicleId != pOldSoldier->iVehicleId ) )
1528 {
1529 return( FALSE );
1530 }
1531 }
1532 }
1533
1534
1535 // passed all tests
1536 return( TRUE );
1537 }
1538
1539
1540
GoToNextCharacterInList(void)1541 void GoToNextCharacterInList( void )
1542 {
1543 INT32 iCounter = 0, iCount = 0;
1544
1545 if (fShowDescriptionFlag) return;
1546
1547 if (bSelectedDestChar != -1 || fPlotForHelicopter)
1548 {
1549 AbortMovementPlottingMode( );
1550 }
1551
1552 // is the current guy invalid or the first one?
1553 if( ( bSelectedInfoChar == -1 )|| ( bSelectedInfoChar == MAX_CHARACTER_COUNT ) )
1554 {
1555 iCount = 0;
1556 }
1557 else
1558 {
1559 iCount = bSelectedInfoChar + 1;
1560 }
1561
1562 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
1563 {
1564 const SOLDIERTYPE* const s = gCharactersList[iCount].merc;
1565 if (s != NULL &&
1566 iCount < MAX_CHARACTER_COUNT &&
1567 ValidSelectableCharForNextOrPrev(*s))
1568 {
1569 ChangeSelectedInfoChar( ( INT8 )iCount, TRUE );
1570 break;
1571 }
1572 else
1573 {
1574 iCount++;
1575
1576 if( iCount >= MAX_CHARACTER_COUNT )
1577 {
1578 iCount = 0;
1579 }
1580 }
1581 }
1582 }
1583
1584
GoToPrevCharacterInList(void)1585 void GoToPrevCharacterInList( void )
1586 {
1587 INT32 iCounter = 0, iCount = 0;
1588
1589 if (fShowDescriptionFlag) return;
1590
1591 if (bSelectedDestChar != -1 || fPlotForHelicopter)
1592 {
1593 AbortMovementPlottingMode( );
1594 }
1595
1596 // is the current guy invalid or the first one?
1597 if( ( bSelectedInfoChar == -1 ) || ( bSelectedInfoChar == 0 ) )
1598 {
1599 iCount = MAX_CHARACTER_COUNT;
1600 }
1601 else
1602 {
1603 iCount = bSelectedInfoChar - 1;
1604 }
1605
1606 // now run through the list and find first prev guy
1607 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
1608 {
1609 const SOLDIERTYPE* const s = gCharactersList[iCount].merc;
1610 if (s != NULL &&
1611 iCount < MAX_CHARACTER_COUNT &&
1612 ValidSelectableCharForNextOrPrev(*s))
1613 {
1614 ChangeSelectedInfoChar( ( INT8 )iCount, TRUE );
1615 break;
1616 }
1617 else
1618 {
1619 iCount--;
1620
1621 if( iCount < 0 )
1622 {
1623 // was FIRST_VEHICLE
1624 iCount = MAX_CHARACTER_COUNT;
1625 }
1626 }
1627 }
1628 }
1629
1630
HandleMinerEvent(ProfileID const ubMinerProfileID,INT16 const sQuoteNumber,BOOLEAN const fForceMapscreen)1631 void HandleMinerEvent(ProfileID const ubMinerProfileID, INT16 const sQuoteNumber, BOOLEAN const fForceMapscreen)
1632 {
1633 BOOLEAN fFromMapscreen = FALSE;
1634
1635
1636 if ( guiCurrentScreen == MAP_SCREEN )
1637 {
1638 fFromMapscreen = TRUE;
1639 }
1640 else
1641 {
1642 // if transition to mapscreen is required
1643 if ( fForceMapscreen )
1644 {
1645 // switch to mapscreen so we can flash the mine sector the guy is talking about
1646 LeaveTacticalScreen(MAP_SCREEN);
1647 fFromMapscreen = TRUE;
1648 }
1649 }
1650
1651 if ( fFromMapscreen )
1652 {
1653 // if not showing map surface level
1654 if ( iCurrentMapSectorZ != 0 )
1655 {
1656 // switch to it, because the miner locators wouldn't show up if we're underground while they speak
1657 ChangeSelectedMapSector( sSelMapX, sSelMapY, 0 );
1658 }
1659
1660 fMapPanelDirty = TRUE;
1661 }
1662
1663 CharacterDialogue(ubMinerProfileID, sQuoteNumber, GetExternalNPCFace(ubMinerProfileID), DIALOGUE_EXTERNAL_NPC_UI, FALSE);
1664 }
1665
1666
1667 static void StopMapScreenHelpText(void);
1668 static void StopShowingInterfaceFastHelpText(void);
1669
1670
ShutDownUserDefineHelpTextRegions(void)1671 void ShutDownUserDefineHelpTextRegions( void )
1672 {
1673 // dirty the tactical panel
1674 fInterfacePanelDirty = DIRTYLEVEL2;
1675 SetRenderFlags( RENDER_FLAG_FULL );
1676
1677 //dirty the map panel
1678 StopMapScreenHelpText( );
1679
1680 //r eset tactical flag too
1681 StopShowingInterfaceFastHelpText( );
1682 }
1683
1684
SetUpFastHelpRegion(INT32 x,INT32 y,INT32 width,const ST::string & str)1685 void SetUpFastHelpRegion(INT32 x, INT32 y, INT32 width, const ST::string& str)
1686 {
1687 FASTHELPREGION* fhr = &pFastHelpMapScreenList[0];
1688 fhr->iX = x;
1689 fhr->iY = y;
1690 fhr->iW = width;
1691 fhr->FastHelpText = str;
1692 giSizeOfInterfaceFastHelpTextList = 1;
1693 }
1694
1695
1696 static void DisplayFastHelpRegions(FASTHELPREGION* pRegion, INT32 iSize);
1697 static void SetUpShutDownMapScreenHelpTextScreenMask(void);
1698
1699
1700 // handle the actual showing of the interface fast help text
HandleShowingOfTacticalInterfaceFastHelpText(void)1701 void HandleShowingOfTacticalInterfaceFastHelpText( void )
1702 {
1703 static BOOLEAN fTextActive = FALSE;
1704
1705 if( fInterfaceFastHelpTextActive )
1706 {
1707 DisplayFastHelpRegions(pFastHelpMapScreenList, giSizeOfInterfaceFastHelpTextList);
1708
1709 PauseGame();
1710
1711 // lock out the screen
1712 SetUpShutDownMapScreenHelpTextScreenMask( );
1713
1714 gfIgnoreScrolling = TRUE;
1715
1716 // the text is active
1717 fTextActive = TRUE;
1718
1719 }
1720 else if (!fInterfaceFastHelpTextActive && fTextActive)
1721 {
1722 fTextActive = FALSE;
1723 UnPauseGame();
1724 gfIgnoreScrolling = FALSE;
1725
1726 // shut down
1727 ShutDownUserDefineHelpTextRegions( );
1728 }
1729 }
1730
1731 // start showing fast help text
StartShowingInterfaceFastHelpText(void)1732 void StartShowingInterfaceFastHelpText( void )
1733 {
1734 fInterfaceFastHelpTextActive = TRUE;
1735 }
1736
1737
1738 // stop showing interface fast help text
StopShowingInterfaceFastHelpText(void)1739 static void StopShowingInterfaceFastHelpText(void)
1740 {
1741 fInterfaceFastHelpTextActive = FALSE;
1742 }
1743
1744
1745 // is the interface text up?
IsTheInterfaceFastHelpTextActive(void)1746 BOOLEAN IsTheInterfaceFastHelpTextActive( void )
1747 {
1748 return( fInterfaceFastHelpTextActive );
1749 }
1750
1751
1752 static void DisplayUserDefineHelpTextRegions(FASTHELPREGION* pRegion);
1753
1754
1755 // display all the regions in the list
DisplayFastHelpRegions(FASTHELPREGION * pRegion,INT32 iSize)1756 static void DisplayFastHelpRegions(FASTHELPREGION* pRegion, INT32 iSize)
1757 {
1758 INT32 iCounter = 0;
1759
1760 // run through and show all the regions
1761 for( iCounter = 0; iCounter < iSize; iCounter++ )
1762 {
1763 DisplayUserDefineHelpTextRegions( &( pRegion[ iCounter ] ) );
1764 }
1765 }
1766
1767
1768 // show one region
DisplayUserDefineHelpTextRegions(FASTHELPREGION * pRegion)1769 static void DisplayUserDefineHelpTextRegions(FASTHELPREGION* pRegion)
1770 {
1771 INT32 iX,iY,iW,iH;
1772
1773 iX = pRegion->iX;
1774 iY = pRegion->iY;
1775 // get the width and height of the string
1776 iW = (INT32)( pRegion->iW ) + 14;
1777 iH = IanWrappedStringHeight(pRegion->iW, 0, FONT10ARIAL, pRegion->FastHelpText);
1778
1779 // tack on the outer border
1780 iH += 14;
1781
1782 // gone not far enough?
1783 if ( iX < 0 )
1784 iX = 0;
1785
1786 // gone too far
1787 if ( ( pRegion->iX + iW ) >= SCREEN_WIDTH )
1788 iX = (SCREEN_WIDTH - iW - 4);
1789
1790 // what about the y value?
1791 iY = (INT32)pRegion->iY - ( iH * 3 / 4);
1792
1793 // not far enough
1794 if (iY < 0)
1795 iY = 0;
1796
1797 // too far
1798 if ( (iY + iH) >= SCREEN_HEIGHT )
1799 iY = (SCREEN_HEIGHT - iH - 15);
1800
1801 { SGPVSurface::Lock l(FRAME_BUFFER);
1802 SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
1803 UINT16* const pDestBuf = l.Buffer<UINT16>();
1804 RectangleDraw(TRUE, iX + 1, iY + 1, iX + iW - 1, iY + iH - 1, Get16BPPColor(FROMRGB( 65, 57, 15)), pDestBuf);
1805 RectangleDraw(TRUE, iX, iY, iX + iW - 2, iY + iH - 2, Get16BPPColor(FROMRGB(227, 198, 88)), pDestBuf);
1806 }
1807 FRAME_BUFFER->ShadowRect(iX + 2, iY + 2, iX + iW - 3, iY + iH - 3);
1808 FRAME_BUFFER->ShadowRect(iX + 2, iY + 2, iX + iW - 3, iY + iH - 3);
1809
1810 iH = DisplayWrappedString(iX + 10, iY + 6, pRegion->iW, 0, FONT10ARIAL, FONT_BEIGE, pRegion->FastHelpText, FONT_NEARBLACK, MARK_DIRTY);
1811
1812 InvalidateRegion( iX, iY, (iX + iW) , (iY + iH + 20 ) );
1813 }
1814
1815
1816 // stop the help text in mapscreen
StopMapScreenHelpText(void)1817 static void StopMapScreenHelpText(void)
1818 {
1819 fTeamPanelDirty = TRUE;
1820 fMapPanelDirty = TRUE;
1821 fCharacterInfoPanelDirty = TRUE;
1822 fMapScreenBottomDirty = TRUE;
1823
1824 SetUpShutDownMapScreenHelpTextScreenMask( );
1825 }
1826
1827
1828 static void MapScreenHelpTextScreenMaskBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
1829
1830
SetUpShutDownMapScreenHelpTextScreenMask(void)1831 static void SetUpShutDownMapScreenHelpTextScreenMask(void)
1832 {
1833 static BOOLEAN fCreated = FALSE;
1834
1835 // create or destroy the screen mask as needed
1836 if (fInterfaceFastHelpTextActive && !fCreated)
1837 {
1838 MSYS_DefineRegion(&gMapScreenHelpTextMask, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MapScreenHelpTextScreenMaskBtnCallback);
1839 fCreated = TRUE;
1840 }
1841 else if (!fInterfaceFastHelpTextActive && fCreated)
1842 {
1843 MSYS_RemoveRegion( &gMapScreenHelpTextMask );
1844
1845 fCreated = FALSE;
1846 }
1847 }
1848
1849
MapScreenHelpTextScreenMaskBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)1850 static void MapScreenHelpTextScreenMaskBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
1851 {
1852 if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP )
1853 {
1854 // stop showing
1855 ShutDownUserDefineHelpTextRegions( );
1856 }
1857 else if( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP )
1858 {
1859 // stop showing
1860 ShutDownUserDefineHelpTextRegions( );
1861 }
1862 }
1863
1864
IsSoldierSelectedForMovement(SOLDIERTYPE const & s)1865 static bool IsSoldierSelectedForMovement(SOLDIERTYPE const& s)
1866 {
1867 // run through the list and turn this soldiers value on
1868 for (INT32 i = 0; i != giNumberOfSoldiersInSectorMoving; ++i)
1869 {
1870 if (pSoldierMovingList[i] != &s) continue;
1871 if (!fSoldierIsMoving[i]) continue; // XXX break?
1872 return true;
1873 }
1874 return false;
1875 }
1876
1877
IsSquadSelectedForMovement(INT32 const squad_no)1878 static bool IsSquadSelectedForMovement(INT32 const squad_no)
1879 {
1880 for (INT32 i = 0; i != giNumberOfSquadsInSectorMoving; ++i)
1881 {
1882 if (iSquadMovingList[i] != squad_no) continue;
1883 if (!fSquadIsMoving[i]) continue; // XXX break?
1884 return true;
1885 }
1886 return false;
1887 }
1888
1889
IsVehicleSelectedForMovement(INT32 const vehicle_id)1890 static bool IsVehicleSelectedForMovement(INT32 const vehicle_id)
1891 {
1892 for (INT32 i = 0; i != giNumberOfVehiclesInSectorMoving; ++i)
1893 {
1894 if (iVehicleMovingList[i] != vehicle_id) continue;
1895 if (!fVehicleIsMoving[i]) continue; // XXX break?
1896 return true;
1897 }
1898 return false;
1899 }
1900
1901
SelectSoldierForMovement(SOLDIERTYPE const & s)1902 static void SelectSoldierForMovement(SOLDIERTYPE const& s)
1903 {
1904 for (INT32 i = 0; i != giNumberOfSoldiersInSectorMoving; ++i)
1905 {
1906 if (pSoldierMovingList[i] != &s) continue;
1907 // Turn the selected soldier on
1908 fSoldierIsMoving[i] = true;
1909 break;
1910 }
1911 }
1912
1913
DeselectSoldierForMovement(SOLDIERTYPE const & s)1914 static void DeselectSoldierForMovement(SOLDIERTYPE const& s)
1915 {
1916 for (INT32 i = 0; i != giNumberOfSoldiersInSectorMoving; ++i)
1917 {
1918 if (pSoldierMovingList[i] != &s) continue;
1919 // Turn the selected soldier off
1920 fSoldierIsMoving[i] = false;
1921 break;
1922 }
1923 }
1924
1925
1926 static BOOLEAN CanMoveBoxSoldierMoveStrategically(SOLDIERTYPE* pSoldier, BOOLEAN fShowErrorMessage);
1927
1928
SelectSquadForMovement(INT32 const squad_no)1929 static void SelectSquadForMovement(INT32 const squad_no)
1930 {
1931 // Run through squad list and set them on
1932 for (INT32 i = 0; i != giNumberOfSquadsInSectorMoving; ++i)
1933 {
1934 if (iSquadMovingList[i] != squad_no) continue;
1935
1936 // Try to select everyone in squad
1937 bool some_cant_move = false;
1938 bool first_failure = true;
1939 FOR_EACH_IN_SQUAD(k, squad_no)
1940 {
1941 SOLDIERTYPE& s = **k;
1942 if (!s.bActive) continue;
1943 /* Is he able and allowed to move? Report only the first reason for
1944 * failure encountered */
1945 if (CanMoveBoxSoldierMoveStrategically(&s, first_failure))
1946 {
1947 SelectSoldierForMovement(s);
1948 }
1949 else
1950 {
1951 some_cant_move = true;
1952 first_failure = false;
1953 }
1954 }
1955
1956 if (!some_cant_move) fSquadIsMoving[i] = TRUE;
1957 break;
1958 }
1959 }
1960
1961
DeselectSquadForMovement(INT32 const squad_no)1962 static void DeselectSquadForMovement(INT32 const squad_no)
1963 {
1964 // Run through squad list and set them off
1965 for (INT32 i = 0; i != giNumberOfSquadsInSectorMoving; ++i)
1966 {
1967 if (iSquadMovingList[i] != squad_no) continue;
1968
1969 fSquadIsMoving[i] = FALSE;
1970
1971 // Now deselect everyone in squad
1972 FOR_EACH_IN_SQUAD(k, squad_no)
1973 {
1974 SOLDIERTYPE& s = **k;
1975 if (!s.bActive) continue;
1976 DeselectSoldierForMovement(s);
1977 }
1978 break;
1979 }
1980 }
1981
1982
AllSoldiersInSquadSelected(INT32 const squad_no)1983 static bool AllSoldiersInSquadSelected(INT32 const squad_no)
1984 {
1985 // Is everyone on this squad moving?
1986 for (INT32 i = 0; i != giNumberOfSoldiersInSectorMoving; ++i)
1987 {
1988 if (pSoldierMovingList[i]->bAssignment != squad_no) continue;
1989 if (fSoldierIsMoving[i]) continue;
1990 return false;
1991 }
1992 return true;
1993 }
1994
1995
SelectVehicleForMovement(INT32 const vehicle_id,BOOLEAN const and_all_on_board)1996 static void SelectVehicleForMovement(INT32 const vehicle_id, BOOLEAN const and_all_on_board)
1997 {
1998 // Run through vehicle list and set them on
1999 for (INT32 i = 0; i != giNumberOfVehiclesInSectorMoving; ++i)
2000 {
2001 if (iVehicleMovingList[i] != vehicle_id) continue;
2002
2003 bool first_failure = true;
2004 bool has_driver = false;
2005 VEHICLETYPE const& v = pVehicleList[vehicle_id];
2006 CFOR_EACH_PASSENGER(v, k)
2007 {
2008 SOLDIERTYPE& s = **k;
2009
2010 if (and_all_on_board)
2011 { // Try to select everyone in vehicle
2012 if (s.bActive)
2013 {
2014 // Is he able and allowed to move?
2015 if (CanMoveBoxSoldierMoveStrategically(&s, first_failure))
2016 {
2017 SelectSoldierForMovement(s);
2018 }
2019 else
2020 {
2021 first_failure = false;
2022 }
2023 }
2024 }
2025
2026 if (IsSoldierSelectedForMovement(s)) has_driver = true;
2027 }
2028
2029 /* Vehicle itself can only move if at least one passenger can move and is
2030 * moving */
2031 if (has_driver) fVehicleIsMoving[i] = TRUE;
2032 break;
2033 }
2034 }
2035
2036
DeselectVehicleForMovement(INT32 const vehicle_id)2037 static void DeselectVehicleForMovement(INT32 const vehicle_id)
2038 {
2039 // run through vehicle list and set them off
2040 for (INT32 i = 0; i != giNumberOfVehiclesInSectorMoving; ++i)
2041 {
2042 if (iVehicleMovingList[i] != vehicle_id) continue;
2043
2044 fVehicleIsMoving[i] = FALSE;
2045
2046 // Now deselect everyone in vehicle
2047 VEHICLETYPE const& v = pVehicleList[vehicle_id];
2048 CFOR_EACH_PASSENGER(v, i)
2049 {
2050 SOLDIERTYPE& s = **i;
2051 if (s.bActive) DeselectSoldierForMovement(s);
2052 }
2053 break;
2054 }
2055 }
2056
2057
HowManyMovingSoldiersInVehicle(INT32 const vehicle_id)2058 static INT32 HowManyMovingSoldiersInVehicle(INT32 const vehicle_id)
2059 {
2060 INT32 n = 0;
2061 for (INT32 i = 0; i != giNumberOfSoldiersInSectorMoving; ++i)
2062 {
2063 SOLDIERTYPE const& s = *pSoldierMovingList[i];
2064 if (s.bAssignment != VEHICLE) continue;
2065 if (s.iVehicleId != vehicle_id) continue;
2066 if (!fSoldierIsMoving[i]) continue;
2067 ++n;
2068 }
2069 return n;
2070 }
2071
2072
HowManyMovingSoldiersInSquad(INT32 const squad_no)2073 static INT32 HowManyMovingSoldiersInSquad(INT32 const squad_no)
2074 {
2075 INT32 n = 0;
2076 for (INT32 i = 0; i != giNumberOfSoldiersInSectorMoving; ++i)
2077 {
2078 if (pSoldierMovingList[i]->bAssignment != squad_no) continue;
2079 if (!fSoldierIsMoving[i]) continue;
2080 ++n;
2081 }
2082 return n;
2083 }
2084
2085
AddSoldierToMovingLists(SOLDIERTYPE & s)2086 static void AddSoldierToMovingLists(SOLDIERTYPE& s)
2087 {
2088 for (INT32 i = 0; i != MAX_CHARACTER_COUNT; ++i)
2089 {
2090 SOLDIERTYPE*& slot = pSoldierMovingList[i];
2091 if (slot == &s) return;
2092
2093 if (slot) continue;
2094 // Found a free slot
2095 slot = &s;
2096 fSoldierIsMoving[i] = false;
2097 ++giNumberOfSoldiersInSectorMoving;
2098 return;
2099 }
2100 }
2101
2102
AddSquadToMovingLists(INT32 const squad_no)2103 static void AddSquadToMovingLists(INT32 const squad_no)
2104 {
2105 if (squad_no == -1) return; // Invalid squad
2106
2107 for(INT32 i = 0; i < NUMBER_OF_SQUADS; ++i)
2108 {
2109 INT32& slot = iSquadMovingList[i];
2110 if (slot == squad_no) return;
2111
2112 if (slot != -1) continue;
2113 // Found a free slot
2114 slot = squad_no;
2115 fSquadIsMoving[i] = FALSE;
2116 ++giNumberOfSquadsInSectorMoving;
2117 return;
2118 }
2119 }
2120
2121
AddVehicleToMovingLists(INT32 const vehicle_id)2122 static void AddVehicleToMovingLists(INT32 const vehicle_id)
2123 {
2124 if (vehicle_id == -1) return;
2125
2126 for (INT32 i = 0; i != NUMBER_OF_SQUADS; ++i)
2127 {
2128 INT32& slot = iVehicleMovingList[i];
2129 if (slot == vehicle_id) return;
2130
2131 if (slot != -1) continue;
2132 // Found a free slot
2133 slot = vehicle_id;
2134 fVehicleIsMoving[i] = FALSE;
2135 ++giNumberOfVehiclesInSectorMoving;
2136 return;
2137 }
2138 }
2139
2140
2141 // The alternate mapscreen movement system
InitializeMovingLists()2142 static void InitializeMovingLists()
2143 {
2144 giNumberOfSoldiersInSectorMoving = 0;
2145 giNumberOfSquadsInSectorMoving = 0;
2146 giNumberOfVehiclesInSectorMoving = 0;
2147
2148 // Init the soldiers
2149 for (INT32 i = 0; i != MAX_CHARACTER_COUNT; ++i)
2150 {
2151 pSoldierMovingList[i] = 0;
2152 fSoldierIsMoving[i] = false;
2153 }
2154
2155 // Init the squads
2156 for (INT32 i = 0; i != NUMBER_OF_SQUADS; ++i)
2157 {
2158 iSquadMovingList[i] = -1;
2159 fSquadIsMoving[i] = FALSE;
2160 }
2161
2162 // Init the vehicles
2163 for (INT32 i = 0; i != NUMBER_OF_SQUADS; ++i)
2164 {
2165 iVehicleMovingList[i] = -1;
2166 fVehicleIsMoving[i] = FALSE;
2167 }
2168 }
2169
2170
IsAnythingSelectedForMoving()2171 static bool IsAnythingSelectedForMoving()
2172 {
2173 for (INT32 i = 0; i != MAX_CHARACTER_COUNT; ++i)
2174 {
2175 if (!pSoldierMovingList[i]) continue;
2176 if (!fSoldierIsMoving[i]) continue;
2177 return true;
2178 }
2179
2180 for (INT32 i = 0; i != NUMBER_OF_SQUADS; ++i)
2181 {
2182 if (iSquadMovingList[i] == -1) continue;
2183 if (!fSquadIsMoving[i]) continue;
2184 return true;
2185 }
2186
2187 for (INT32 i = 0; i != NUMBER_OF_SQUADS; ++i)
2188 {
2189 if (iVehicleMovingList[i] == -1) continue;
2190 if (!fVehicleIsMoving[i]) continue;
2191 return true;
2192 }
2193
2194 return false;
2195 }
2196
2197
2198 static void BuildMouseRegionsForMoveBox(void);
2199 static void ClearMouseRegionsForMoveBox(void);
2200 static void CreatePopUpBoxForMovementBox(void);
2201
2202
CreateDestroyMovementBox(INT16 sSectorX,INT16 sSectorY,INT16 sSectorZ)2203 void CreateDestroyMovementBox( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
2204 {
2205 static BOOLEAN fCreated = FALSE;
2206
2207
2208 // not allowed for underground movement!
2209 Assert( sSectorZ == 0 );
2210
2211 if (fShowMapScreenMovementList && !fCreated)
2212 {
2213 fCreated = TRUE;
2214
2215 // create the box and mouse regions
2216 CreatePopUpBoxForMovementBox( );
2217 BuildMouseRegionsForMoveBox( );
2218 CreateScreenMaskForMoveBox( );
2219 fMapPanelDirty = TRUE;
2220 }
2221 else if (!fShowMapScreenMovementList && fCreated)
2222 {
2223 fCreated = FALSE;
2224
2225 // destroy the box and mouse regions
2226 ClearMouseRegionsForMoveBox( );
2227 RemoveBox( ghMoveBox );
2228 ghMoveBox = NO_POPUP_BOX;
2229 RemoveScreenMaskForMoveBox( );
2230 fMapPanelDirty = TRUE;
2231 fMapScreenBottomDirty = TRUE; // really long move boxes can overlap bottom panel
2232 }
2233
2234 }
2235
2236
SetUpMovingListsForSector(INT16 const x,INT16 const y,INT16 const z)2237 void SetUpMovingListsForSector(INT16 const x, INT16 const y, INT16 const z)
2238 {
2239 // Not allowed for underground movement
2240 Assert(z == 0);
2241
2242 InitializeMovingLists();
2243
2244 /* Note that Skyrider can't be moved using the move box, and won't appear
2245 * because the helicoprer is not in the char list */
2246 CFOR_EACH_IN_CHAR_LIST(i)
2247 {
2248 SOLDIERTYPE& s = *i->merc;
2249 if (s.bAssignment == IN_TRANSIT) continue;
2250 if (s.bAssignment == ASSIGNMENT_POW) continue;
2251 if (s.sSectorX != x || s.sSectorY != y || s.bSectorZ != z) continue;
2252
2253 if (s.uiStatusFlags & SOLDIER_VEHICLE)
2254 {
2255 VEHICLETYPE const& v = GetVehicle(s.bVehicleID);
2256 // If it can move (can't be empty)
2257 if (GetNumberInVehicle(v) == 0) continue;
2258 AddVehicleToMovingLists(s.bVehicleID);
2259 }
2260 else
2261 {
2262 // Alive, not aboard Skyrider (airborne or not!)
2263 if (s.bLife < OKLIFE) continue;
2264 if (InHelicopter(s)) continue;
2265 AddSoldierToMovingLists(s);
2266 if (s.bAssignment < ON_DUTY) // If on a squad
2267 { // Add squad (duplicates ok, they're ignored inside the function)
2268 AddSquadToMovingLists(s.bAssignment);
2269 }
2270 }
2271 }
2272
2273 fShowMapScreenMovementList = TRUE;
2274 CreateDestroyMovementBox(x, y, z);
2275 }
2276
2277
2278 static void AddStringsToMoveBox(PopUpBox*);
2279
2280
CreatePopUpBoxForMovementBox(void)2281 static void CreatePopUpBoxForMovementBox(void)
2282 {
2283 SGPPoint const MovePosition = { (UINT16)(STD_SCREEN_X + 450), (UINT16)(STD_SCREEN_Y + 100) };
2284
2285 // create the pop up box and mouse regions for movement list
2286 PopUpBox* const box = CreatePopUpBox(MovePosition, POPUP_BOX_FLAG_RESIZE, FRAME_BUFFER, guiPOPUPBORDERS, guiPOPUPTEX, 6, 6, 4, 4, 2);
2287 ghMoveBox = box;
2288
2289 AddStringsToMoveBox(box);
2290
2291 SetBoxFont( box, MAP_SCREEN_FONT);
2292 SetBoxHighLight( box, FONT_WHITE);
2293 SetBoxForeground(box, FONT_LTGREEN);
2294 SetBoxBackground(box, FONT_BLACK);
2295 SetBoxShade( box, FONT_BLACK);
2296
2297 // make the header line WHITE
2298 SetBoxLineForeground(box, 0, FONT_WHITE);
2299
2300 // make the done and cancel lines YELLOW
2301 const UINT32 line_count = GetNumberOfLinesOfTextInBox(box);
2302 SetBoxLineForeground(box, line_count - 1, FONT_YELLOW);
2303 if (IsAnythingSelectedForMoving()) SetBoxLineForeground(box, line_count - 2, FONT_YELLOW);
2304
2305 ResizeBoxToText(box);
2306
2307 // adjust position to try to keep it in the map area as good as possible
2308 SGPBox const& area = GetBoxArea(box);
2309 if (area.x + area.w >= MAP_VIEW_START_X + MAP_VIEW_WIDTH)
2310 {
2311 SetBoxX(box, MAX(MAP_VIEW_START_X, MAP_VIEW_START_X + MAP_VIEW_WIDTH - area.w));
2312 }
2313 if (area.y + area.h >= MAP_VIEW_START_Y + MAP_VIEW_HEIGHT)
2314 {
2315 SetBoxY(box, MAX(MAP_VIEW_START_Y, MAP_VIEW_START_Y + MAP_VIEW_HEIGHT - area.h));
2316 }
2317 }
2318
2319
2320 static BOOLEAN AllOtherSoldiersInListAreSelected(void);
2321
2322
AddStringsToMoveBox(PopUpBox * const box)2323 static void AddStringsToMoveBox(PopUpBox* const box)
2324 {
2325 INT32 iCount = 0, iCountB = 0;
2326 BOOLEAN fFirstOne = TRUE;
2327
2328 // clear all the strings out of the box
2329 RemoveAllBoxStrings(box);
2330
2331 // add title
2332 ST::string sStringB = GetShortSectorString(sSelMapX, sSelMapY);
2333 ST::string sString = st_format_printf(pMovementMenuStrings[0], sStringB);
2334 AddMonoString(box, sString);
2335
2336
2337 // blank line
2338 AddMonoString(box, ST::null);
2339
2340
2341 // add squads
2342 for( iCount = 0; iCount < giNumberOfSquadsInSectorMoving; iCount++ )
2343 {
2344 // add this squad, now add all the grunts in it
2345 sString = ST::format(fSquadIsMoving[iCount] ? "*{}*" : "{}", pSquadMenuStrings[iSquadMovingList[iCount]]);
2346 AddMonoString(box, sString);
2347
2348 // now add all the grunts in it
2349 for( iCountB = 0; iCountB < giNumberOfSoldiersInSectorMoving; iCountB++ )
2350 {
2351 if( pSoldierMovingList[ iCountB ] -> bAssignment == iSquadMovingList[ iCount ] )
2352 {
2353 // add mercs in squads
2354 if (IsSoldierSelectedForMovement(*pSoldierMovingList[iCountB]))
2355 {
2356 sString = ST::format(" *{}*", pSoldierMovingList[ iCountB ]->name);
2357 }
2358 else
2359 {
2360 sString = ST::format(" {}", pSoldierMovingList[ iCountB ]->name);
2361 }
2362 AddMonoString(box, sString);
2363 }
2364 }
2365 }
2366
2367
2368 // add vehicles
2369 for( iCount = 0; iCount < giNumberOfVehiclesInSectorMoving; iCount++ )
2370 {
2371 // add this vehicle
2372 sString = ST::format(fVehicleIsMoving[iCount] ? "*{}*" : "{}", pVehicleStrings[pVehicleList[iVehicleMovingList[iCount]].ubVehicleType]);
2373 AddMonoString(box, sString);
2374
2375 // now add all the grunts in it
2376 for( iCountB = 0; iCountB < giNumberOfSoldiersInSectorMoving; iCountB++ )
2377 {
2378 if( ( pSoldierMovingList[ iCountB ] -> bAssignment == VEHICLE ) &&( pSoldierMovingList[ iCountB ] -> iVehicleId == iVehicleMovingList[ iCount ] ) )
2379 {
2380 // add mercs in vehicles
2381 if (IsSoldierSelectedForMovement(*pSoldierMovingList[iCountB]))
2382 {
2383 sString = ST::format(" *{}*", pSoldierMovingList[ iCountB ]->name);
2384 }
2385 else
2386 {
2387 sString = ST::format(" {}", pSoldierMovingList[ iCountB ]->name);
2388 }
2389 AddMonoString(box, sString);
2390 }
2391 }
2392 }
2393
2394
2395 fFirstOne = TRUE;
2396
2397 // add "other" soldiers heading, once, if there are any
2398 for( iCount = 0; iCount < giNumberOfSoldiersInSectorMoving; iCount++ )
2399 {
2400 // not on duty, not in a vehicle
2401 if( ( pSoldierMovingList[ iCount ]->bAssignment >= ON_DUTY ) && ( pSoldierMovingList[ iCount ]->bAssignment != VEHICLE ) )
2402 {
2403 if ( fFirstOne )
2404 {
2405 // add OTHER header line
2406 sString = ST::format(AllOtherSoldiersInListAreSelected() ? "*{}*" : "{}", pMovementMenuStrings[3]);
2407 AddMonoString(box, sString);
2408
2409 fFirstOne = FALSE;
2410 }
2411
2412 // add OTHER soldiers (not on duty nor in a vehicle)
2413 sString = ST::format(IsSoldierSelectedForMovement(*pSoldierMovingList[iCount]) ? " *{} ( {} )*" : " {} ( {} )", pSoldierMovingList[iCount]->name, pAssignmentStrings[pSoldierMovingList[iCount]->bAssignment]);
2414 AddMonoString(box, sString);
2415 }
2416 }
2417
2418
2419 // blank line
2420 AddMonoString(box, ST::null);
2421
2422
2423 if ( IsAnythingSelectedForMoving() )
2424 {
2425 // add PLOT MOVE line
2426 AddMonoString(box, pMovementMenuStrings[1]);
2427 }
2428 else
2429 {
2430 // blank line
2431 AddMonoString(box, ST::null);
2432 }
2433
2434 // add cancel line
2435 AddMonoString(box, pMovementMenuStrings[2]);
2436 }
2437
2438
MakeRegionBlank(const INT32 i,const UINT16 x,const UINT16 y,const UINT16 w,const UINT16 h)2439 static void MakeRegionBlank(const INT32 i, const UINT16 x, const UINT16 y, const UINT16 w, const UINT16 h)
2440 {
2441 MSYS_DefineRegion(&gMoveMenuRegion[i], x, y + h * i, x + w, y + h * (i + 1), MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
2442 }
2443
2444
2445 static void MoveMenuBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
2446 static void MoveMenuMvtCallback(MOUSE_REGION* pRegion, INT32 iReason);
2447
2448
MakeRegion(const INT32 i,const UINT16 x,const UINT16 y,const UINT16 w,const UINT16 h,const UINT32 val_a,const UINT32 val_b)2449 static void MakeRegion(const INT32 i, const UINT16 x, const UINT16 y, const UINT16 w, const UINT16 h, const UINT32 val_a, const UINT32 val_b)
2450 {
2451 MOUSE_REGION* const r = &gMoveMenuRegion[i];
2452 MSYS_DefineRegion(r, x, y + h * i, x + w, y + h * (i + 1), MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MoveMenuMvtCallback, MoveMenuBtnCallback);
2453 MSYS_SetRegionUserData(r, 0, i);
2454 MSYS_SetRegionUserData(r, 1, val_a);
2455 MSYS_SetRegionUserData(r, 2, val_b);
2456 }
2457
2458
BuildMouseRegionsForMoveBox(void)2459 static void BuildMouseRegionsForMoveBox(void)
2460 {
2461 SGPBox const& area = GetBoxArea(ghMoveBox);
2462 INT32 const x = area.x;
2463 INT32 const y = area.y + GetTopMarginSize(ghMoveBox) - 2; // -2 to improve highlighting accuracy between lines
2464 INT32 const w = area.w;
2465 INT32 const h = GetLineSpace(ghMoveBox) + GetFontHeight(GetBoxFont(ghMoveBox));
2466 INT32 i = 0; // Region index
2467
2468 MakeRegionBlank(i++, x, y, w, h); // Box heading
2469 MakeRegionBlank(i++, x, y, w, h); // Blank line
2470
2471 // Define regions for squad lines
2472 for (INT32 iCount = 0; iCount < giNumberOfSquadsInSectorMoving; ++iCount)
2473 {
2474 MakeRegion(i++, x, y, w, h, SQUAD_REGION, iCount);
2475
2476 for (INT32 iCountB = 0; iCountB < giNumberOfSoldiersInSectorMoving; ++iCountB)
2477 {
2478 if (pSoldierMovingList[iCountB]->bAssignment == iSquadMovingList[iCount])
2479 {
2480 MakeRegion(i++, x, y, w, h, SOLDIER_REGION, iCountB);
2481 }
2482 }
2483 }
2484
2485 // Define regions for vehicle lines
2486 for (INT32 iCount = 0; iCount < giNumberOfVehiclesInSectorMoving; ++iCount)
2487 {
2488 MakeRegion(i++, x, y, w, h, VEHICLE_REGION, iCount);
2489
2490 for (INT32 iCountB = 0; iCountB < giNumberOfSoldiersInSectorMoving; ++iCountB)
2491 {
2492 if (pSoldierMovingList[iCountB]->bAssignment == VEHICLE &&
2493 pSoldierMovingList[iCountB]->iVehicleId == iVehicleMovingList[iCount])
2494 {
2495 MakeRegion(i++, x, y, w, h, SOLDIER_REGION, iCountB);
2496 }
2497 }
2498 }
2499
2500 // Define regions for "other" soldiers
2501 BOOLEAN fDefinedOtherRegion = FALSE;
2502 for (INT32 iCount = 0; iCount < giNumberOfSoldiersInSectorMoving; ++iCount)
2503 {
2504 // this guy is not in a squad or vehicle
2505 if (pSoldierMovingList[iCount]->bAssignment >= ON_DUTY &&
2506 pSoldierMovingList[iCount]->bAssignment != VEHICLE)
2507 {
2508 // this line gets place only once...
2509 if (!fDefinedOtherRegion)
2510 {
2511 MakeRegion(i++, x, y, w, h, OTHER_REGION, 0);
2512 fDefinedOtherRegion = TRUE;
2513 }
2514 MakeRegion(i++, x, y, w, h, SOLDIER_REGION, iCount);
2515 }
2516 }
2517
2518 Assert(i == 2 /* heading + blank line */ + giNumberOfSquadsInSectorMoving + giNumberOfVehiclesInSectorMoving + giNumberOfSoldiersInSectorMoving + (fDefinedOtherRegion ? 1 : 0));
2519
2520 MakeRegionBlank(i++, x, y, w, h); // blank line
2521
2522 if (IsAnythingSelectedForMoving())
2523 {
2524 MakeRegion(i++, x, y, w, h, DONE_REGION, 0); // DONE line
2525 }
2526 else
2527 {
2528 MakeRegionBlank(i++, x, y, w, h); // blank line
2529 }
2530
2531 MakeRegion(i++, x, y, w, h, CANCEL_REGION, 0); // CANCEL line
2532 }
2533
2534
ClearMouseRegionsForMoveBox(void)2535 static void ClearMouseRegionsForMoveBox(void)
2536 {
2537 INT32 iCounter = 0;
2538
2539 // run through list of mouse regions
2540 for( iCounter = 0; iCounter < ( INT32 )GetNumberOfLinesOfTextInBox( ghMoveBox ); iCounter++ )
2541 {
2542 // remove this region
2543 MSYS_RemoveRegion( &gMoveMenuRegion[ iCounter ] );
2544 }
2545 }
2546
2547
MoveMenuMvtCallback(MOUSE_REGION * pRegion,INT32 iReason)2548 static void MoveMenuMvtCallback(MOUSE_REGION* pRegion, INT32 iReason)
2549 {
2550 // mvt callback handler for move box line regions
2551 INT32 iValue = -1;
2552
2553
2554 iValue = MSYS_GetRegionUserData( pRegion, 0 );
2555
2556 if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
2557 {
2558 // highlight string
2559 HighLightBoxLine( ghMoveBox, iValue );
2560 }
2561 else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
2562 {
2563 // unhighlight all strings in box
2564 UnHighLightBox( ghMoveBox );
2565 }
2566 }
2567
2568
2569 static void DeselectAllOtherSoldiersInList(void);
2570 static void HandleMoveoutOfSectorMovementTroops(void);
2571 static void SelectAllOtherSoldiersInList(void);
2572
2573
MoveMenuBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)2574 static void MoveMenuBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
2575 {
2576 // btn callback handler for move box line regions
2577 INT32 iMoveBoxLine = -1, iRegionType = -1, iListIndex = -1, iClickTime = 0;
2578 SOLDIERTYPE *pSoldier = NULL;
2579
2580
2581 iMoveBoxLine = MSYS_GetRegionUserData( pRegion, 0 );
2582 iRegionType = MSYS_GetRegionUserData( pRegion, 1 );
2583 iListIndex = MSYS_GetRegionUserData( pRegion, 2 );
2584 iClickTime = GetJA2Clock();
2585
2586 if( ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) )
2587 {
2588 if( iClickTime - giDblClickTimersForMoveBoxMouseRegions[ iMoveBoxLine ] < DBL_CLICK_DELAY_FOR_MOVE_MENU )
2589 {
2590 // dbl click, and something is selected?
2591 if ( IsAnythingSelectedForMoving() )
2592 {
2593 // treat like DONE
2594 HandleMoveoutOfSectorMovementTroops( );
2595 return;
2596 }
2597 }
2598 else
2599 {
2600 giDblClickTimersForMoveBoxMouseRegions[ iMoveBoxLine ] = iClickTime;
2601
2602 if( iRegionType == SQUAD_REGION )
2603 {
2604 // is the squad moving
2605 if (fSquadIsMoving[iListIndex])
2606 {
2607 // squad stays
2608 DeselectSquadForMovement( iSquadMovingList[ iListIndex ] );
2609 }
2610 else
2611 {
2612 // squad goes
2613 SelectSquadForMovement( iSquadMovingList[ iListIndex ] );
2614 }
2615 }
2616 else if( iRegionType == VEHICLE_REGION )
2617 {
2618 // is the vehicle moving
2619 if (fVehicleIsMoving[iListIndex])
2620 {
2621 // vehicle stays
2622 DeselectVehicleForMovement( iVehicleMovingList[ iListIndex ] );
2623 }
2624 else
2625 {
2626 // vehicle goes
2627 SelectVehicleForMovement( iVehicleMovingList[ iListIndex ], AND_ALL_ON_BOARD );
2628 }
2629 }
2630 else if( iRegionType == OTHER_REGION )
2631 {
2632 if (AllOtherSoldiersInListAreSelected())
2633 {
2634 // deselect all others in the list
2635 DeselectAllOtherSoldiersInList( );
2636 }
2637 else
2638 {
2639 // select all others in the list
2640 SelectAllOtherSoldiersInList( );
2641 }
2642 }
2643 else if( iRegionType == SOLDIER_REGION )
2644 {
2645 pSoldier = pSoldierMovingList[ iListIndex ];
2646
2647 if ( pSoldier->fBetweenSectors )
2648 {
2649 // we don't allow mercs to change squads or get out of vehicles between sectors, easiest way to handle this
2650 // is to prevent any toggling of individual soldiers on the move at the outset.
2651 DoScreenIndependantMessageBox( pMapErrorString[ 41 ], MSG_BOX_FLAG_OK, NULL );
2652 return;
2653 }
2654
2655 // if soldier is currently selected to move
2656 if (IsSoldierSelectedForMovement(*pSoldier))
2657 {
2658 // change him to NOT move instead
2659
2660 if( pSoldier->bAssignment == VEHICLE )
2661 {
2662 // if he's the only one left moving in the vehicle, deselect whole vehicle
2663 if( HowManyMovingSoldiersInVehicle( pSoldier->iVehicleId ) == 1 )
2664 {
2665 // whole vehicle stays
2666 DeselectVehicleForMovement( pSoldier->iVehicleId );
2667 }
2668 else
2669 {
2670 // soldier is staying behind
2671 DeselectSoldierForMovement(*pSoldier);
2672 }
2673 }
2674 else if( pSoldier->bAssignment < ON_DUTY )
2675 {
2676 // if he's the only one left moving in the squad, deselect whole squad
2677 if( HowManyMovingSoldiersInSquad( pSoldier->bAssignment ) == 1 )
2678 {
2679 // whole squad stays
2680 DeselectSquadForMovement( pSoldier->bAssignment );
2681 }
2682 else
2683 {
2684 // soldier is staying behind
2685 DeselectSoldierForMovement(*pSoldier);
2686 }
2687 }
2688 else
2689 {
2690 // soldier is staying behind
2691 DeselectSoldierForMovement(*pSoldier);
2692 }
2693 }
2694 else // currently NOT moving
2695 {
2696 // is he able & allowed to move? (Errors with a reason are reported within)
2697 if ( CanMoveBoxSoldierMoveStrategically( pSoldier, TRUE ) )
2698 {
2699 // change him to move instead
2700 SelectSoldierForMovement(*pSoldier);
2701
2702 if( pSoldier->bAssignment < ON_DUTY )
2703 {
2704 // if everyone in the squad is now selected, select the squad itself
2705 if( AllSoldiersInSquadSelected( pSoldier->bAssignment ) )
2706 {
2707 SelectSquadForMovement( pSoldier->bAssignment );
2708 }
2709 }
2710 /* ARM: it's more flexible without this - player can take the vehicle along or not without having to exit it.
2711 else if( pSoldier->bAssignment == VEHICLE )
2712 {
2713 // his vehicle MUST also go while he's moving, but not necessarily others on board
2714 SelectVehicleForMovement( pSoldier->iVehicleId, VEHICLE_ONLY );
2715 }
2716 */
2717 }
2718 }
2719 }
2720 else if( iRegionType == DONE_REGION )
2721 {
2722 // is something selected?
2723 if ( IsAnythingSelectedForMoving() )
2724 {
2725 HandleMoveoutOfSectorMovementTroops( );
2726 return;
2727 }
2728 }
2729 else if( iRegionType == CANCEL_REGION )
2730 {
2731 fShowMapScreenMovementList = FALSE;
2732 return;
2733 }
2734 else
2735 {
2736 SLOGA("MoveMenuBtnCallback: Invalid regionType %d, moveBoxLine %d", iRegionType, iMoveBoxLine);
2737 return;
2738 }
2739
2740 fRebuildMoveBox = TRUE;
2741 fTeamPanelDirty = TRUE;
2742 fMapPanelDirty = TRUE;
2743 fCharacterInfoPanelDirty = TRUE;
2744 MarkAllBoxesAsAltered( );
2745 }
2746 }
2747 }
2748
2749
2750 static MoveError CanCharacterMoveInStrategic(SOLDIERTYPE&);
2751
2752
CanMoveBoxSoldierMoveStrategically(SOLDIERTYPE * pSoldier,BOOLEAN fShowErrorMessage)2753 static BOOLEAN CanMoveBoxSoldierMoveStrategically(SOLDIERTYPE* pSoldier, BOOLEAN fShowErrorMessage)
2754 {
2755 // valid soldier?
2756 Assert( pSoldier );
2757 Assert( pSoldier->bActive );
2758
2759 MoveError const ret = CanCharacterMoveInStrategic(*pSoldier);
2760 if (ret == ME_OK) return TRUE;
2761
2762 if (fShowErrorMessage) ReportMapScreenMovementError(ret);
2763 return FALSE;
2764 }
2765
2766
SelectAllOtherSoldiersInList(void)2767 static void SelectAllOtherSoldiersInList(void)
2768 {
2769 INT32 iCounter = 0;
2770 BOOLEAN fSomeCantMove = FALSE;
2771
2772
2773 for( iCounter = 0; iCounter < giNumberOfSoldiersInSectorMoving; iCounter++ )
2774 {
2775 if( ( pSoldierMovingList[ iCounter ] ->bAssignment >= ON_DUTY ) && ( pSoldierMovingList[ iCounter ] ->bAssignment != VEHICLE ) )
2776 {
2777 if ( CanMoveBoxSoldierMoveStrategically( pSoldierMovingList[ iCounter ], FALSE ) )
2778 {
2779 fSoldierIsMoving[ iCounter ] = true;
2780 }
2781 else
2782 {
2783 fSomeCantMove = TRUE;
2784 }
2785 }
2786 }
2787
2788 if ( fSomeCantMove )
2789 {
2790 // can't - some of the OTHER soldiers can't move
2791 ReportMapScreenMovementError( 46 );
2792 }
2793 }
2794
2795
DeselectAllOtherSoldiersInList(void)2796 static void DeselectAllOtherSoldiersInList(void)
2797 {
2798 INT32 iCounter = 0;
2799
2800 for( iCounter = 0; iCounter < giNumberOfSoldiersInSectorMoving; iCounter++ )
2801 {
2802 if( ( pSoldierMovingList[ iCounter ] ->bAssignment >= ON_DUTY ) && ( pSoldierMovingList[ iCounter ] ->bAssignment != VEHICLE ) )
2803 {
2804 fSoldierIsMoving[ iCounter ] = false;
2805 }
2806 }
2807 }
2808
2809
2810 static INT8 FindSquadThatSoldierCanJoin(SOLDIERTYPE* pSoldier);
2811 static void HandleSettingTheSelectedListOfMercs(void);
2812
2813
HandleMoveoutOfSectorMovementTroops(void)2814 static void HandleMoveoutOfSectorMovementTroops(void)
2815 {
2816 INT32 iCounter = 0;
2817 SOLDIERTYPE *pSoldier = 0;
2818 INT32 iSquadNumber = -1;
2819 BOOLEAN fCheckForCompatibleSquad = FALSE;
2820
2821
2822 // cancel move box
2823 fShowMapScreenMovementList = FALSE;
2824
2825
2826 for( iCounter = 0; iCounter < giNumberOfSoldiersInSectorMoving; iCounter++ )
2827 {
2828 pSoldier = pSoldierMovingList[ iCounter ];
2829
2830 fCheckForCompatibleSquad = FALSE;
2831
2832 // if he is on a valid squad
2833 if( pSoldier->bAssignment < ON_DUTY )
2834 {
2835 // if he and his squad are parting ways (soldier is staying behind, but squad is leaving, or vice versa)
2836 if( fSoldierIsMoving[ iCounter ] != IsSquadSelectedForMovement( pSoldier->bAssignment ) )
2837 {
2838 // split the guy from his squad to any other compatible squad
2839 fCheckForCompatibleSquad = TRUE;
2840 }
2841 }
2842 // if in a vehicle
2843 else if( pSoldier->bAssignment == VEHICLE )
2844 {
2845 // if he and his vehicle are parting ways (soldier is staying behind, but vehicle is leaving, or vice versa)
2846 if( fSoldierIsMoving[ iCounter ] != IsVehicleSelectedForMovement( pSoldier->iVehicleId ) )
2847 {
2848 // split the guy from his vehicle to any other compatible squad
2849 fCheckForCompatibleSquad = TRUE;
2850 }
2851 }
2852 else // on his own - not on a squad or in a vehicle
2853 {
2854 // if he's going anywhere
2855 if( fSoldierIsMoving[ iCounter ] )
2856 {
2857 // find out if anyone is going with this guy...see if he can tag along
2858 fCheckForCompatibleSquad = TRUE;
2859 }
2860 }
2861
2862
2863 if ( fCheckForCompatibleSquad )
2864 {
2865 // look for a squad that's doing the same thing as this guy is and has room for him
2866 iSquadNumber = FindSquadThatSoldierCanJoin( pSoldier );
2867 if( iSquadNumber != -1 )
2868 {
2869 if ( !AddCharacterToSquad( pSoldier, ( INT8 )( iSquadNumber ) ) )
2870 {
2871 SLOGA("HandleMoveoutOfSectorMovementTroops: AddCharacterToSquad %d failed, iCounter %d", iSquadNumber, iCounter);
2872 // toggle whether he's going or not to try and recover somewhat gracefully
2873 fSoldierIsMoving[ iCounter ] = !fSoldierIsMoving[ iCounter ];
2874 }
2875 }
2876 else
2877 {
2878 // no existing squad is compatible, will have to start his own new squad
2879 iSquadNumber = AddCharacterToUniqueSquad( pSoldier );
2880
2881 // It worked. Add his new squad to the "moving squads" list so others can join it, too!
2882 AddSquadToMovingLists( iSquadNumber );
2883
2884 // If this guy is moving
2885 if( fSoldierIsMoving[ iCounter ] )
2886 {
2887 // mark this new squad as moving too, so those moving can join it
2888 SelectSquadForMovement( iSquadNumber );
2889 }
2890 }
2891 }
2892
2893 }
2894
2895 // now actually set the list
2896 HandleSettingTheSelectedListOfMercs( );
2897 }
2898
2899
HandleSettingTheSelectedListOfMercs(void)2900 static void HandleSettingTheSelectedListOfMercs(void)
2901 {
2902 BOOLEAN fFirstOne = TRUE;
2903 INT32 iCounter = 0;
2904 BOOLEAN fSelected;
2905
2906 // reset the selected character
2907 bSelectedDestChar = -1;
2908
2909 // run through the list of grunts
2910 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
2911 {
2912 const SOLDIERTYPE* const pSoldier = gCharactersList[iCounter].merc;
2913 if (pSoldier == NULL) continue;
2914
2915 if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
2916 {
2917 fSelected = IsVehicleSelectedForMovement( pSoldier->bVehicleID );
2918 }
2919 else
2920 {
2921 fSelected = IsSoldierSelectedForMovement(*pSoldier);
2922 }
2923
2924 // is he/she selected for movement?
2925 if( fSelected )
2926 {
2927 // yes, are they the first one to be selected?
2928 if (fFirstOne)
2929 {
2930 // yes, then set them as the destination plotting character for movement arrow purposes
2931 fFirstOne = FALSE;
2932
2933 bSelectedDestChar = ( INT8 )iCounter;
2934 // make DEST column glow
2935 giDestHighLine = iCounter;
2936
2937 ChangeSelectedInfoChar( ( INT8 ) iCounter, TRUE );
2938 }
2939
2940 // add this guy to the selected list of grunts
2941 SetEntryInSelectedCharacterList( ( INT8 )iCounter );
2942 }
2943 }
2944
2945 if( bSelectedDestChar != -1 )
2946 {
2947 // set cursor
2948 SetUpCursorForStrategicMap( );
2949 fTeamPanelDirty = TRUE;
2950 fMapPanelDirty = TRUE;
2951 fCharacterInfoPanelDirty = TRUE;
2952
2953 DeselectSelectedListMercsWhoCantMoveWithThisGuy(gCharactersList[bSelectedDestChar].merc);
2954
2955 // remember the current paths for all selected characters so we can restore them if need be
2956 RememberPreviousPathForAllSelectedChars();
2957 }
2958 }
2959
2960
AllOtherSoldiersInListAreSelected(void)2961 static BOOLEAN AllOtherSoldiersInListAreSelected(void)
2962 {
2963 INT32 iCounter = 0, iCount = 0;
2964
2965 for( iCounter = 0; iCounter < giNumberOfSoldiersInSectorMoving; iCounter++ )
2966 {
2967 if( ( pSoldierMovingList[ iCounter ]->bAssignment >= ON_DUTY ) && ( pSoldierMovingList[ iCounter ]->bAssignment >= VEHICLE ) )
2968 {
2969 if (!fSoldierIsMoving[iCounter])
2970 {
2971 return( FALSE );
2972 }
2973
2974 iCount++;
2975 }
2976 }
2977
2978 // some merc on other assignments and no result?
2979 if( iCount )
2980 {
2981 return( TRUE );
2982 }
2983
2984 return( FALSE );
2985 }
2986
2987
IsThisSquadInThisSector(const INT16 sSectorX,const INT16 sSectorY,const INT8 bSectorZ,const INT8 bSquadValue)2988 static BOOLEAN IsThisSquadInThisSector(const INT16 sSectorX, const INT16 sSectorY, const INT8 bSectorZ, const INT8 bSquadValue)
2989 {
2990 INT16 sX;
2991 INT16 sY;
2992 INT8 bZ;
2993 return
2994 SectorSquadIsIn(bSquadValue, &sX, &sY, &bZ) &&
2995 sSectorX == sX &&
2996 sSectorY == sY &&
2997 bSectorZ == bZ &&
2998 !IsThisSquadOnTheMove(bSquadValue);
2999 }
3000
3001
FindSquadThatSoldierCanJoin(SOLDIERTYPE * pSoldier)3002 static INT8 FindSquadThatSoldierCanJoin(SOLDIERTYPE* pSoldier)
3003 {
3004 // look for a squad that isn't full that can take this character
3005 INT8 bCounter = 0;
3006
3007 // run through the list of squads
3008 for( bCounter = 0; bCounter < NUMBER_OF_SQUADS; bCounter++ )
3009 {
3010 // is this squad in this sector
3011 if( IsThisSquadInThisSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ, bCounter ) )
3012 {
3013 // does it have room?
3014 if (!IsThisSquadFull(bCounter))
3015 {
3016 // is it doing the same thing as the soldier is (staying or going) ?
3017 if (IsSquadSelectedForMovement(bCounter) == IsSoldierSelectedForMovement(*pSoldier))
3018 {
3019 // go ourselves a match, then
3020 return( bCounter );
3021 }
3022 }
3023 }
3024 }
3025
3026 return( -1 );
3027 }
3028
ReBuildMoveBox(void)3029 void ReBuildMoveBox( void )
3030 {
3031 // check to see if we need to rebuild the movement box and mouse regions
3032 if (!fRebuildMoveBox) return;
3033
3034 // reset the fact
3035 fRebuildMoveBox = FALSE;
3036 fTeamPanelDirty = TRUE;
3037 fMapPanelDirty = TRUE;
3038 fCharacterInfoPanelDirty = TRUE;
3039
3040 // stop showing the box
3041 fShowMapScreenMovementList = FALSE;
3042 CreateDestroyMovementBox( sSelMapX, sSelMapY, ( INT16 )iCurrentMapSectorZ );
3043
3044 // show the box
3045 fShowMapScreenMovementList = TRUE;
3046 CreateDestroyMovementBox( sSelMapX, sSelMapY, ( INT16 )iCurrentMapSectorZ );
3047 ShowBox( ghMoveBox );
3048 MarkAllBoxesAsAltered( );
3049 }
3050
3051
3052 static void MoveScreenMaskBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
3053
3054
CreateScreenMaskForMoveBox(void)3055 void CreateScreenMaskForMoveBox( void )
3056 {
3057 if (!fScreenMaskForMoveCreated)
3058 {
3059 // set up the screen mask
3060 MSYS_DefineRegion(&gMoveBoxScreenMask, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 4, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MoveScreenMaskBtnCallback);
3061
3062 fScreenMaskForMoveCreated = TRUE;
3063 }
3064 }
3065
RemoveScreenMaskForMoveBox(void)3066 void RemoveScreenMaskForMoveBox( void )
3067 {
3068 if (fScreenMaskForMoveCreated)
3069 {
3070 // remove the screen mask
3071 MSYS_RemoveRegion( &gMoveBoxScreenMask );
3072 fScreenMaskForMoveCreated = FALSE;
3073 }
3074 }
3075
3076
MoveScreenMaskBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)3077 static void MoveScreenMaskBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
3078 {
3079 // btn callback handler for move box screen mask region
3080 if( ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) )
3081 {
3082 fShowMapScreenMovementList = FALSE;
3083 }
3084 else if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP )
3085 {
3086 sSelectedMilitiaTown = 0;
3087
3088 // are we showing the update box
3089 if( fShowUpdateBox )
3090 {
3091 fShowUpdateBox = FALSE;
3092 }
3093 }
3094 }
3095
3096
ResetSoldierUpdateBox(void)3097 static void ResetSoldierUpdateBox(void)
3098 {
3099 INT32 iCounter = 0;
3100
3101 // delete any loaded faces
3102 for( iCounter = 0; iCounter < SIZE_OF_UPDATE_BOX; iCounter++ )
3103 {
3104 if( pUpdateSoldierBox[ iCounter ] != NULL )
3105 {
3106 DeleteVideoObject(giUpdateSoldierFaces[iCounter]);
3107 }
3108 }
3109
3110 if( giMercPanelImage != 0 )
3111 {
3112 DeleteVideoObject(giMercPanelImage);
3113 }
3114
3115 // reset the soldier ptrs in the update box
3116 for( iCounter = 0; iCounter < SIZE_OF_UPDATE_BOX; iCounter++ )
3117 {
3118 pUpdateSoldierBox[ iCounter ] = NULL;
3119 }
3120 }
3121
3122
GetNumberOfMercsInUpdateList()3123 INT32 GetNumberOfMercsInUpdateList()
3124 {
3125 INT32 n = 0;
3126 FOR_EACH(SOLDIERTYPE* const, i, pUpdateSoldierBox)
3127 {
3128 if (*i) ++n;
3129 }
3130 return n;
3131 }
3132
3133
3134 static void AddSoldierToUpdateBox(SOLDIERTYPE*);
3135
3136
AddSoldierToWaitingListQueue(SOLDIERTYPE & s)3137 void AddSoldierToWaitingListQueue(SOLDIERTYPE& s)
3138 {
3139 class DialogueEventUpdateBoxAddSoldier : public CharacterDialogueEvent
3140 {
3141 public:
3142 DialogueEventUpdateBoxAddSoldier(SOLDIERTYPE& s) : CharacterDialogueEvent(s) {}
3143
3144 bool Execute()
3145 {
3146 AddSoldierToUpdateBox(&soldier_);
3147 return false;
3148 }
3149 };
3150
3151 DialogueEvent::Add(new DialogueEventUpdateBoxAddSoldier(s));
3152 }
3153
3154
AddReasonToWaitingListQueue(UpdateBoxReason const reason)3155 void AddReasonToWaitingListQueue(UpdateBoxReason const reason)
3156 {
3157 class DialogueEventUpdateBoxSetReason : public DialogueEvent
3158 {
3159 public:
3160 DialogueEventUpdateBoxSetReason(UpdateBoxReason const reason) : reason_(reason) {}
3161
3162 bool Execute()
3163 {
3164 iReasonForSoldierUpDate = reason_;
3165 return false;
3166 }
3167
3168 private:
3169 UpdateBoxReason const reason_;
3170 };
3171
3172 DialogueEvent::Add(new DialogueEventUpdateBoxSetReason(reason));
3173 }
3174
3175
AddDisplayBoxToWaitingQueue(void)3176 void AddDisplayBoxToWaitingQueue(void)
3177 {
3178 class DialogueEventUpdateBoxShow : public DialogueEvent
3179 {
3180 public:
3181 bool Execute()
3182 {
3183 fShowUpdateBox = TRUE;
3184 return false;
3185 }
3186 };
3187
3188 DialogueEvent::Add(new DialogueEventUpdateBoxShow());
3189 }
3190
3191
AddSoldierToUpdateBox(SOLDIERTYPE * const pSoldier)3192 static void AddSoldierToUpdateBox(SOLDIERTYPE* const pSoldier)
3193 {
3194 INT32 iCounter = 0;
3195
3196 // going to load face
3197
3198 if( pSoldier->bLife == 0 )
3199 {
3200 return;
3201 }
3202
3203 if (!pSoldier->bActive) return;
3204
3205 // if update
3206 if( pUpdateSoldierBox[ iCounter ] == NULL )
3207 {
3208 giMercPanelImage = AddVideoObjectFromFile(INTERFACEDIR "/panels.sti");
3209 }
3210
3211 // run thought list of update soldiers
3212 for( iCounter = 0; iCounter < SIZE_OF_UPDATE_BOX; iCounter++ )
3213 {
3214 // find a free slot
3215 if( pUpdateSoldierBox[ iCounter ] == NULL )
3216 {
3217 // add to box
3218 pUpdateSoldierBox[ iCounter ] = pSoldier;
3219 giUpdateSoldierFaces[iCounter] = Load65Portrait(GetProfile(pSoldier->ubProfile));
3220 return;
3221 }
3222 }
3223 }
3224
3225
3226 static void CreateDestroyUpdatePanelButtons(INT32 iX, INT32 iY, BOOLEAN fFourWideMode);
3227 static void RenderSoldierSmallFaceForUpdatePanel(INT32 iIndex, INT32 iX, INT32 iY);
3228
3229
DisplaySoldierUpdateBox()3230 void DisplaySoldierUpdateBox( )
3231 {
3232 INT32 iNumberOfMercsOnUpdatePanel = 0;
3233 INT32 iNumberHigh = 0, iNumberWide = 0;
3234 INT32 iUpdatePanelWidth = 0, iUpdatePanelHeight = 0;
3235 INT32 iX = 0, iY = 0;
3236 INT32 iFaceX = 0, iFaceY = 0;
3237 BOOLEAN fFourWideMode = FALSE;
3238 INT32 iCounter = 0;
3239 INT32 iUpperLimit = 0;
3240
3241 if (!fShowUpdateBox) return;
3242
3243 // get the number of mercs
3244 iNumberOfMercsOnUpdatePanel = GetNumberOfMercsInUpdateList( );
3245
3246 if( iNumberOfMercsOnUpdatePanel == 0 )
3247 {
3248 // nobody home
3249 fShowUpdateBox = FALSE;
3250 // unpause
3251 UnPauseDialogueQueue( );
3252 return;
3253 }
3254
3255 giSleepHighLine = -1;
3256 giDestHighLine = -1;
3257 giContractHighLine = -1;
3258 giAssignHighLine = -1;
3259
3260
3261 //InterruptTime();
3262 PauseGame( );
3263 LockPauseState(LOCK_PAUSE_DISPLAY_SOLDIER_UPDATE);
3264
3265 PauseDialogueQueue( );
3266
3267 // do we have enough for 4 wide, or just 2 wide?
3268 if( iNumberOfMercsOnUpdatePanel > NUMBER_OF_MERCS_FOR_FOUR_WIDTH_UPDATE_PANEL )
3269 {
3270 fFourWideMode = TRUE;
3271 }
3272
3273 // get number of rows
3274 iNumberHigh = ( fFourWideMode ? iNumberOfMercsOnUpdatePanel / NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE
3275 : iNumberOfMercsOnUpdatePanel / NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE);
3276
3277 //number of columns
3278 iNumberWide = ( fFourWideMode ? NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE
3279 : NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE );
3280
3281
3282
3283 // get the height and width of the box .. will need to add in stuff for borders, lower panel...etc
3284 if( fFourWideMode )
3285 {
3286 // do we need an extra row for left overs
3287 if( iNumberOfMercsOnUpdatePanel % NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE )
3288 {
3289 iNumberHigh++;
3290 }
3291 }
3292 else
3293 {
3294 // do we need an extra row for left overs
3295 if( iNumberOfMercsOnUpdatePanel % NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE )
3296 {
3297 iNumberHigh++;
3298 }
3299 }
3300
3301 // round off
3302 if( fFourWideMode )
3303 {
3304 if(iNumberOfMercsOnUpdatePanel % NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE )
3305 {
3306 iNumberOfMercsOnUpdatePanel += NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE - ( iNumberOfMercsOnUpdatePanel % NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE );
3307 }
3308 }
3309 else
3310 {
3311 if(iNumberOfMercsOnUpdatePanel % NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE )
3312 {
3313 iNumberOfMercsOnUpdatePanel += NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE - ( iNumberOfMercsOnUpdatePanel % NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE );
3314 }
3315 }
3316
3317 iUpdatePanelWidth = iNumberWide * TACT_WIDTH_OF_UPDATE_PANEL_BLOCKS;
3318
3319 iUpdatePanelHeight = ( iNumberHigh + 1 ) * TACT_HEIGHT_OF_UPDATE_PANEL_BLOCKS;
3320
3321 // get the x,y offsets on the screen of the panel
3322 iX = STD_SCREEN_X + 290 + ( 336 - iUpdatePanelWidth ) / 2;
3323
3324 // iY = 28 + ( 288 - iUpdatePanelHeight ) / 2;
3325
3326 // Have the bottom of the box ALWAYS a set distance from the bottom of the map ( so user doesnt have to move mouse far )
3327 iY = STD_SCREEN_Y + 280 - iUpdatePanelHeight;
3328
3329 const SGPVObject* const hBackGroundHandle = guiUpdatePanelTactical;
3330
3331 //Display the 2 TOP corner pieces
3332 BltVideoObject( guiSAVEBUFFER, hBackGroundHandle, 0, iX-4, iY - 4);
3333 BltVideoObject( guiSAVEBUFFER, hBackGroundHandle, 2, iX+iUpdatePanelWidth, iY - 4);
3334
3335 if( fFourWideMode )
3336 {
3337 //Display 2 vertical lines starting at the bottom
3338 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 3, iX - 4, iY + iUpdatePanelHeight - 3 - 70);
3339 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 5, iX + iUpdatePanelWidth, iY + iUpdatePanelHeight - 3 - 70);
3340
3341 //Display the 2 bottom corner pieces
3342 BltVideoObject( guiSAVEBUFFER, hBackGroundHandle, 0, iX-4, iY + iUpdatePanelHeight - 3);
3343 BltVideoObject( guiSAVEBUFFER, hBackGroundHandle, 2, iX+iUpdatePanelWidth, iY + iUpdatePanelHeight - 3);
3344 }
3345
3346
3347 SetFontDestBuffer(guiSAVEBUFFER);
3348
3349 iUpperLimit = fFourWideMode ? ( iNumberOfMercsOnUpdatePanel + NUMBER_OF_MERC_COLUMNS_FOR_FOUR_WIDE_MODE ) : ( iNumberOfMercsOnUpdatePanel + NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE );
3350
3351 //need to put the background down first
3352 for( iCounter = 0; iCounter < iUpperLimit; iCounter++ )
3353 {
3354 // blt the face and name
3355
3356 // get the face x and y
3357 iFaceX = iX + ( iCounter % iNumberWide ) * TACT_UPDATE_MERC_FACE_X_WIDTH;
3358 iFaceY = iY + ( iCounter / iNumberWide ) * TACT_UPDATE_MERC_FACE_X_HEIGHT;
3359
3360 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 20, iFaceX, iFaceY);
3361 }
3362
3363 //loop through the mercs to be displayed
3364 for( iCounter = 0; iCounter < ( iNumberOfMercsOnUpdatePanel <= NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE ? NUMBER_OF_MERC_COLUMNS_FOR_TWO_WIDE_MODE : iNumberOfMercsOnUpdatePanel ); iCounter++ )
3365 {
3366 //
3367 // blt the face and name
3368 //
3369
3370 // get the face x and y
3371 iFaceX = iX + ( iCounter % iNumberWide ) * TACT_UPDATE_MERC_FACE_X_WIDTH;
3372 iFaceY = iY + ( iCounter / iNumberWide ) * TACT_UPDATE_MERC_FACE_X_HEIGHT + REASON_FOR_SOLDIER_UPDATE_OFFSET_Y;
3373
3374 // now get the face
3375 if( pUpdateSoldierBox[ iCounter ] )
3376 {
3377 iFaceX += TACT_UPDATE_MERC_FACE_X_OFFSET;
3378 iFaceY += TACT_UPDATE_MERC_FACE_Y_OFFSET;
3379
3380 // there is a face
3381 RenderSoldierSmallFaceForUpdatePanel( iCounter, iFaceX, iFaceY );
3382
3383 // display the mercs name
3384 DrawTextToScreen(pUpdateSoldierBox[iCounter]->name, iFaceX - 5, iFaceY + 31, 57, TINYFONT1, FONT_LTRED, FONT_BLACK, CENTER_JUSTIFIED);
3385 }
3386 }
3387
3388 // the button container box
3389 if( fFourWideMode )
3390 {
3391 //def: 3/1/99 WAS SUBINDEX 6,
3392 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 19, iX - 4 + TACT_UPDATE_MERC_FACE_X_WIDTH, iY + iNumberHigh * TACT_UPDATE_MERC_FACE_X_HEIGHT + REASON_FOR_SOLDIER_UPDATE_OFFSET_Y+3);
3393
3394 // ATE: Display string for time compression
3395 DisplayWrappedString(iX, iY + iNumberHigh * TACT_UPDATE_MERC_FACE_X_HEIGHT + 5 + REASON_FOR_SOLDIER_UPDATE_OFFSET_Y + 3, iUpdatePanelWidth, 0, MAP_SCREEN_FONT, FONT_WHITE, gzLateLocalizedString[STR_LATE_49], FONT_BLACK, CENTER_JUSTIFIED);
3396 }
3397 else
3398 {
3399 //def: 3/1/99 WAS SUBINDEX 6,
3400 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 19, iX - 4 , iY + iNumberHigh * TACT_UPDATE_MERC_FACE_X_HEIGHT + REASON_FOR_SOLDIER_UPDATE_OFFSET_Y+3);
3401
3402 // ATE: Display string for time compression
3403 DisplayWrappedString(iX, iY + iNumberHigh * TACT_UPDATE_MERC_FACE_X_HEIGHT + 5 + REASON_FOR_SOLDIER_UPDATE_OFFSET_Y + 3, iUpdatePanelWidth, 0, MAP_SCREEN_FONT, FONT_WHITE, gzLateLocalizedString[STR_LATE_49], FONT_BLACK, CENTER_JUSTIFIED);
3404 }
3405
3406 // now wrap the border
3407 for( iCounter = 0; iCounter < iNumberHigh ; iCounter++ )
3408 {
3409 // the sides
3410 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 3, iX - 4, iY + ( iCounter ) * TACT_UPDATE_MERC_FACE_X_HEIGHT);
3411 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 5, iX + iUpdatePanelWidth, iY + ( iCounter ) * TACT_UPDATE_MERC_FACE_X_HEIGHT);
3412 }
3413
3414 //big horizontal line
3415 for( iCounter = 0; iCounter < iNumberWide; iCounter++ )
3416 {
3417 // the top bottom
3418 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 1, iX + TACT_UPDATE_MERC_FACE_X_WIDTH * (iCounter ) , iY - 4);
3419 BltVideoObject( guiSAVEBUFFER , hBackGroundHandle, 1, iX + TACT_UPDATE_MERC_FACE_X_WIDTH * (iCounter ) , iY + iUpdatePanelHeight - 3);
3420 }
3421
3422 //Display the reason for the update box
3423 DisplayWrappedString(iX, iY + (fFourWideMode ? 6 : 3), iUpdatePanelWidth, 0, MAP_SCREEN_FONT, FONT_WHITE, pUpdateMercStrings[iReasonForSoldierUpDate], FONT_BLACK, CENTER_JUSTIFIED);
3424
3425 SetFontDestBuffer(FRAME_BUFFER);
3426
3427 // restore extern background rect
3428 RestoreExternBackgroundRect( ( INT16 )( iX - 5 ), ( INT16 )( iY - 5 ), ( INT16 )( iUpdatePanelWidth + 10 ), ( INT16 )(iUpdatePanelHeight + 6 ) );
3429
3430 CreateDestroyUpdatePanelButtons( iX, ( iY + iUpdatePanelHeight - 18 ), fFourWideMode );
3431 MarkAButtonDirty( guiUpdatePanelButtons[ 0 ] );
3432 MarkAButtonDirty( guiUpdatePanelButtons[ 1 ] );
3433 }
3434
3435
MakeButton(UINT idx,INT16 x,INT16 y,GUI_CALLBACK click,const ST::string & text,const ST::string & help_text)3436 static void MakeButton(UINT idx, INT16 x, INT16 y, GUI_CALLBACK click, const ST::string& text, const ST::string& help_text)
3437 {
3438 GUIButtonRef const btn = QuickCreateButtonImg(INTERFACEDIR "/group_confirm_tactical.sti", 7, 8, x, y, MSYS_PRIORITY_HIGHEST - 1, click);
3439 guiUpdatePanelButtons[idx] = btn;
3440 btn->SpecifyGeneralTextAttributes(text, MAP_SCREEN_FONT, FONT_MCOLOR_BLACK, FONT_BLACK);
3441 btn->SetFastHelpText(help_text);
3442 }
3443
3444
3445 static void ContinueUpdateButtonCallback(GUI_BUTTON* btn, INT32 reason);
3446 static void StopUpdateButtonCallback(GUI_BUTTON* btn, INT32 reason);
3447
3448
CreateDestroyUpdatePanelButtons(INT32 iX,INT32 iY,BOOLEAN fFourWideMode)3449 static void CreateDestroyUpdatePanelButtons(INT32 iX, INT32 iY, BOOLEAN fFourWideMode)
3450 {
3451 static BOOLEAN fCreated = FALSE;
3452
3453 if (fShowUpdateBox && !fCreated)
3454 {
3455 // set to created
3456 fCreated = TRUE;
3457
3458 fShowAssignmentMenu = FALSE;
3459 fShowContractMenu = FALSE;
3460
3461 INT16 x = iX;
3462 if (fFourWideMode) x += TACT_UPDATE_MERC_FACE_X_WIDTH;
3463 MakeButton(0, x, iY, ContinueUpdateButtonCallback, pUpdatePanelButtons[0], gzLateLocalizedString[STR_LATE_51]);
3464 MakeButton(1, x + TACT_UPDATE_MERC_FACE_X_WIDTH, iY, StopUpdateButtonCallback, pUpdatePanelButtons[1], gzLateLocalizedString[STR_LATE_52]);
3465 }
3466 else if (!fShowUpdateBox && fCreated)
3467 {
3468 // set to uncreated
3469 fCreated = FALSE;
3470
3471 // get rid of the buttons and images
3472 RemoveButton( guiUpdatePanelButtons[ 0 ] );
3473 RemoveButton( guiUpdatePanelButtons[ 1 ] );
3474
3475 // unpause
3476 UnPauseDialogueQueue( );
3477 }
3478 }
3479
3480
CreateDestroyTheUpdateBox(void)3481 void CreateDestroyTheUpdateBox( void )
3482 {
3483 static BOOLEAN fCreated = FALSE;
3484
3485 if (!fCreated && fShowUpdateBox)
3486 {
3487 if( GetNumberOfMercsInUpdateList( ) == 0 )
3488 {
3489 fShowUpdateBox = FALSE;
3490 return;
3491 }
3492
3493 fCreated = TRUE;
3494
3495 //InterruptTime();
3496 // create screen mask
3497 CreateScreenMaskForMoveBox( );
3498
3499 // lock it paused
3500 PauseGame();
3501 LockPauseState(LOCK_PAUSE_CREATE_SOLDIER_UPDATE);
3502
3503 // display the box
3504 DisplaySoldierUpdateBox( );
3505
3506 // Do beep
3507 PlayJA2SampleFromFile(SOUNDSDIR "/newbeep.wav", MIDVOLUME, 1, MIDDLEPAN);
3508 }
3509 else if (fCreated && !fShowUpdateBox)
3510 {
3511 fCreated = FALSE;
3512
3513 UnLockPauseState( );
3514 UnPauseGame();
3515
3516 // dirty screen
3517 fMapPanelDirty = TRUE;
3518 fTeamPanelDirty = TRUE;
3519 fCharacterInfoPanelDirty = TRUE;
3520
3521 // remove screen mask
3522 RemoveScreenMaskForMoveBox( );
3523
3524 ResetSoldierUpdateBox( );
3525
3526 CreateDestroyUpdatePanelButtons( 0, 0, FALSE );
3527 }
3528 }
3529
3530
RenderSoldierSmallFaceForUpdatePanel(INT32 iIndex,INT32 iX,INT32 iY)3531 static void RenderSoldierSmallFaceForUpdatePanel(INT32 iIndex, INT32 iX, INT32 iY)
3532 {
3533 INT32 iStartY = 0;
3534 SOLDIERTYPE *pSoldier = NULL;
3535
3536 // fill the background for the info bars black
3537 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+36, iY+2, iX+44, iY+30, 0 );
3538
3539 // put down the background
3540 BltVideoObject(guiSAVEBUFFER, giMercPanelImage, 0, iX, iY);
3541
3542 // grab the face
3543 BltVideoObject(guiSAVEBUFFER, giUpdateSoldierFaces[iIndex], 0, iX + 2, iY + 2);
3544
3545 //HEALTH BAR
3546 pSoldier = pUpdateSoldierBox[ iIndex ];
3547
3548 // is the merc alive?
3549 if( !pSoldier->bLife )
3550 return;
3551
3552
3553 //yellow one for bleeding
3554 iStartY = iY + 29 - 27*pSoldier->bLifeMax/100;
3555 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+36, iStartY, iX+37, iY+29, Get16BPPColor( FROMRGB( 107, 107, 57 ) ) );
3556 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+37, iStartY, iX+38, iY+29, Get16BPPColor( FROMRGB( 222, 181, 115 ) ) );
3557
3558 //pink one for bandaged.
3559 iStartY += 27*pSoldier->bBleeding/100;
3560 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+36, iStartY, iX+37, iY+29, Get16BPPColor( FROMRGB( 156, 57, 57 ) ) );
3561 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+37, iStartY, iX+38, iY+29, Get16BPPColor( FROMRGB( 222, 132, 132 ) ) );
3562
3563 //red one for actual health
3564 iStartY = iY + 29 - 27*pSoldier->bLife/100;
3565 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+36, iStartY, iX+37, iY+29, Get16BPPColor( FROMRGB( 107, 8, 8 ) ) );
3566 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+37, iStartY, iX+38, iY+29, Get16BPPColor( FROMRGB( 206, 0, 0 ) ) );
3567
3568 //BREATH BAR
3569 iStartY = iY + 29 - 27*pSoldier->bBreathMax/100;
3570 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+39, iStartY, iX+40, iY+29, Get16BPPColor( FROMRGB( 8, 8, 132 ) ) );
3571 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+40, iStartY, iX+41, iY+29, Get16BPPColor( FROMRGB( 8, 8, 107 ) ) );
3572
3573 //MORALE BAR
3574 iStartY = iY + 29 - 27*pSoldier->bMorale/100;
3575 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+42, iStartY, iX+43, iY+29, Get16BPPColor( FROMRGB( 8, 156, 8 ) ) );
3576 ColorFillVideoSurfaceArea( guiSAVEBUFFER, iX+43, iStartY, iX+44, iY+29, Get16BPPColor( FROMRGB( 8, 107, 8 ) ) );
3577 }
3578
3579
ContinueUpdateButtonCallback(GUI_BUTTON * btn,INT32 reason)3580 static void ContinueUpdateButtonCallback(GUI_BUTTON *btn, INT32 reason)
3581 {
3582 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
3583 {
3584 EndUpdateBox(TRUE); // restart time compression
3585 }
3586 }
3587
3588
StopUpdateButtonCallback(GUI_BUTTON * btn,INT32 reason)3589 static void StopUpdateButtonCallback(GUI_BUTTON *btn, INT32 reason)
3590 {
3591 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
3592 {
3593 EndUpdateBox(FALSE); // stop time compression
3594 }
3595 }
3596
3597
EndUpdateBox(BOOLEAN fContinueTimeCompression)3598 void EndUpdateBox( BOOLEAN fContinueTimeCompression )
3599 {
3600 fShowUpdateBox = FALSE;
3601
3602 CreateDestroyTheUpdateBox();
3603
3604 if ( fContinueTimeCompression )
3605 {
3606 StartTimeCompression();
3607 }
3608 else
3609 {
3610 StopTimeCompression();
3611 }
3612 }
3613
InitTimersForMoveMenuMouseRegions()3614 void InitTimersForMoveMenuMouseRegions()
3615 {
3616 FOR_EACH(INT32, i, giDblClickTimersForMoveBoxMouseRegions) *i = 0;
3617 }
3618
3619
UpdateHelpTextForMapScreenMercIcons()3620 void UpdateHelpTextForMapScreenMercIcons()
3621 {
3622 const SOLDIERTYPE* const s = GetSelectedInfoChar();
3623
3624 // if merc is an AIM merc
3625 ST::string contract = s && s->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ?
3626 zMarksMapScreenText[21] : ST::null;
3627 gContractIconRegion.SetFastHelpText(contract);
3628
3629 // if merc has life insurance
3630 ST::string insurance = s && s->usLifeInsurance > 0 ?
3631 zMarksMapScreenText[3] : ST::null;
3632 gInsuranceIconRegion.SetFastHelpText(insurance);
3633
3634 // if merc has a medical deposit
3635 ST::string medical = s && s->usMedicalDeposit > 0 ?
3636 zMarksMapScreenText[12] : ST::null;
3637 gDepositIconRegion.SetFastHelpText(medical);
3638 }
3639
3640
CreateDestroyInsuranceMouseRegionForMercs(BOOLEAN fCreate)3641 void CreateDestroyInsuranceMouseRegionForMercs( BOOLEAN fCreate )
3642 {
3643 static BOOLEAN fCreated = FALSE;
3644
3645 if (!fCreated && fCreate)
3646 {
3647 MSYS_DefineRegion( &gContractIconRegion, CHAR_ICON_X, CHAR_ICON_CONTRACT_Y, CHAR_ICON_X + CHAR_ICON_WIDTH, CHAR_ICON_CONTRACT_Y + CHAR_ICON_HEIGHT,
3648 MSYS_PRIORITY_HIGH - 1, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
3649
3650 MSYS_DefineRegion( &gInsuranceIconRegion, CHAR_ICON_X, CHAR_ICON_CONTRACT_Y + CHAR_ICON_SPACING, CHAR_ICON_X + CHAR_ICON_WIDTH, CHAR_ICON_CONTRACT_Y + CHAR_ICON_SPACING + CHAR_ICON_HEIGHT,
3651 MSYS_PRIORITY_HIGH - 1, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
3652
3653 MSYS_DefineRegion( &gDepositIconRegion, CHAR_ICON_X, CHAR_ICON_CONTRACT_Y + ( 2 * CHAR_ICON_SPACING ), CHAR_ICON_X + CHAR_ICON_WIDTH, CHAR_ICON_CONTRACT_Y + ( 2 * CHAR_ICON_SPACING ) + CHAR_ICON_HEIGHT,
3654 MSYS_PRIORITY_HIGH - 1, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
3655
3656 fCreated = TRUE;
3657 }
3658 else if (fCreated && !fCreate)
3659 {
3660 MSYS_RemoveRegion( &gContractIconRegion );
3661 MSYS_RemoveRegion( &gInsuranceIconRegion );
3662 MSYS_RemoveRegion( &gDepositIconRegion );
3663 fCreated = FALSE;
3664 }
3665 }
3666
3667
HandleTimeCompressWithTeamJackedInAndGearedToGo(void)3668 BOOLEAN HandleTimeCompressWithTeamJackedInAndGearedToGo( void )
3669 {
3670 // check a team is ready to go
3671 if( !( AnyMercsHired( ) ) )
3672 {
3673 // no mercs, leave
3674 return( FALSE );
3675 }
3676
3677 // make sure the game just started
3678 if (!DidGameJustStart()) return FALSE;
3679
3680 // Select starting sector.
3681 ChangeSelectedMapSector(SECTORX(START_SECTOR), SECTORY(START_SECTOR), 0);
3682
3683 // load starting sector
3684 try
3685 {
3686 SetCurrentWorldSector(SECTORX(START_SECTOR), SECTORY(START_SECTOR), 0);
3687 }
3688 catch (...) /* XXX exception should probably propagate; caller ignores return value */
3689 {
3690 return FALSE;
3691 }
3692
3693 //Setup variables in the PBI for this first battle. We need to support the
3694 //non-persistant PBI in case the user goes to mapscreen.
3695 gfBlitBattleSectorLocator = TRUE;
3696 gubPBSectorX = SECTORX(START_SECTOR);
3697 gubPBSectorY = SECTORY(START_SECTOR);
3698 gubPBSectorZ = 0;
3699 gubEnemyEncounterCode = ENTERING_ENEMY_SECTOR_CODE;
3700
3701 InitHelicopterEntranceByMercs( );
3702
3703 FadeInGameScreen( );
3704
3705 SetUpShutDownMapScreenHelpTextScreenMask( );
3706
3707 // Add e-mail message
3708 AddEmail(ENRICO_CONGRATS,ENRICO_CONGRATS_LENGTH,MAIL_ENRICO, GetWorldTotalMin() );
3709
3710
3711 return( TRUE );
3712 }
3713
3714
NotifyPlayerWhenEnemyTakesControlOfImportantSector(INT16 const x,INT16 const y,INT8 const z)3715 void NotifyPlayerWhenEnemyTakesControlOfImportantSector(INT16 const x, INT16 const y, INT8 const z)
3716 {
3717 // There is nothing important to player below ground
3718 if (z != 0) return;
3719
3720 ST::string sector_desc = GetSectorIDString(x, y, z, TRUE);
3721
3722 ST::string buf;
3723 if (IsThisSectorASAMSector(x, y, z))
3724 {
3725 buf = st_format_printf(pMapErrorString[15], sector_desc);
3726 }
3727 else
3728 {
3729 UINT8 const sector = SECTOR(x, y);
3730 INT8 const mine_id = GetMineIndexForSector(sector);
3731 if (mine_id != -1 &&
3732 GetMaxDailyRemovalFromMine(mine_id) > 0 &&
3733 SpokenToHeadMiner(mine_id))
3734 { // There is a mine and it was producing for us
3735 // Get how much we now will get from the mines
3736 INT32 const income = GetProjectedTotalDailyIncome();
3737 ST::string income_string = SPrintMoney(income);
3738 buf = st_format_printf(pMapErrorString[16], sector_desc, income_string);
3739 }
3740 else
3741 {
3742 INT8 const town_id = GetTownIdForSector(sector);
3743 if (town_id == BLANK_SECTOR) return;
3744 // San Mona isn't important
3745 if (town_id == SAN_MONA) return;
3746
3747 buf = st_format_printf(pMapErrorString[25], sector_desc);
3748 }
3749 }
3750 DoScreenIndependantMessageBox(buf, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
3751 }
3752
3753
NotifyPlayerOfInvasionByEnemyForces(INT16 const x,INT16 const y,INT8 const z,MSGBOX_CALLBACK const return_callback)3754 void NotifyPlayerOfInvasionByEnemyForces(INT16 const x, INT16 const y, INT8 const z, MSGBOX_CALLBACK const return_callback)
3755 {
3756 if (z != 0) return;
3757
3758 // If enemy controlled anyways, leave
3759 if (StrategicMap[CALCULATE_STRATEGIC_INDEX(x, y)].fEnemyControlled) return;
3760
3761 ST::string buf;
3762 ST::string sector_desc;
3763
3764 // check if SAM site here
3765 if (IsThisSectorASAMSector(x, y, z))
3766 {
3767 sector_desc = GetShortSectorString(x, y);
3768 buf = st_format_printf(pMapErrorString[22], sector_desc);
3769 DoScreenIndependantMessageBox(buf, MSG_BOX_FLAG_OK, return_callback);
3770 }
3771 else if (GetTownIdForSector(SECTOR(x, y)) != BLANK_SECTOR)
3772 {
3773 sector_desc = GetSectorIDString(x, y, z, TRUE);
3774 buf = st_format_printf(pMapErrorString[23], sector_desc);
3775 DoScreenIndependantMessageBox(buf, MSG_BOX_FLAG_OK, return_callback);
3776 }
3777 else
3778 {
3779 sector_desc = GetShortSectorString(x, y);
3780 buf = st_format_printf(pMapErrorString[24], sector_desc);
3781 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, buf);
3782 }
3783 }
3784
3785
CanCharacterMoveInStrategic(SOLDIERTYPE & s)3786 static MoveError CanCharacterMoveInStrategic(SOLDIERTYPE& s)
3787 {
3788 Assert(s.bActive);
3789
3790 /* NOTE: Check for the most permanent conditions first, and the most easily
3791 * remedied ones last. In case several cases apply, only the reason found
3792 * first will be given, so make it a good one. */
3793
3794 // Still in transit?
3795 if (IsCharacterInTransit(s)) return ME_TRANSIT;
3796 // A POW?
3797 if (s.bAssignment == ASSIGNMENT_POW) return ME_POW;
3798 // Underground? (can't move strategically, must use tactical traversal)
3799 if (s.bSectorZ != 0) return ME_UNDERGROUND;
3800
3801 // Vehicle checks
3802 if (s.uiStatusFlags & SOLDIER_VEHICLE)
3803 {
3804 VEHICLETYPE const& v = GetVehicle(s.bVehicleID);
3805 // Empty (needs a driver!)?
3806 if (GetNumberInVehicle(v) == 0) return ME_VEHICLE_EMPTY;
3807 // Too damaged?
3808 if (s.bLife < OKLIFE) return ME_VEHICLE_DAMAGED;
3809 // Out of fuel?
3810 if (s.sBreathRed == 0) return ME_VEHICLE_NO_GAS;
3811 }
3812 else // Non-vehicle
3813 {
3814 // Dead?
3815 if (s.bLife <= 0)
3816 {
3817 gsCustomErrorString = st_format_printf(pMapErrorString[35], s.name);
3818 return ME_CUSTOM;
3819 }
3820
3821 // Too injured?
3822 if (s.bLife < OKLIFE)
3823 {
3824 gsCustomErrorString = st_format_printf(pMapErrorString[33], s.name);
3825 return ME_CUSTOM;
3826 }
3827 }
3828
3829 /* If merc is in a particular sector, not somewhere in between, and he's NOT
3830 * flying above it all in a working helicopter */
3831 if (!s.fBetweenSectors && !SoldierAboardAirborneHeli(s))
3832 {
3833 // And that sector is loaded
3834 if (s.sSectorX == gWorldSectorX &&
3835 s.sSectorY == gWorldSectorY &&
3836 s.bSectorZ == gbWorldSectorZ)
3837 {
3838 // In combat?
3839 if (gTacticalStatus.uiFlags & INCOMBAT) return ME_COMBAT;
3840 // Hostile sector?
3841 if (gTacticalStatus.fEnemyInSector) return ME_ENEMY;
3842 // Bloodcat ambush?
3843 if (gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE && HostileBloodcatsPresent()) return ME_COMBAT;
3844 }
3845
3846 // Not necessarily loaded - if there are any hostiles there
3847 if (NumHostilesInSector(s.sSectorX, s.sSectorY, s.bSectorZ) > 0) return ME_ENEMY;
3848 }
3849
3850 // If in L12 museum, and the museum alarm went off, and Eldin still around
3851 if (s.sSectorX == 12 &&
3852 s.sSectorY == MAP_ROW_L &&
3853 s.bSectorZ == 0 &&
3854 !s.fBetweenSectors && GetProfile(ELDIN).bMercStatus != MERC_IS_DEAD)
3855 {
3856 UINT8 const room = GetRoom(s.sGridNo);
3857 if (22 <= room && room <= 41)
3858 {
3859 CFOR_EACH_IN_TEAM(s, OUR_TEAM)
3860 {
3861 if (FindObj(s, CHALICE) != ITEM_NOT_FOUND) return ME_MUSEUM;
3862 }
3863 }
3864 }
3865
3866 // On assignment, other than just in a VEHICLE?
3867 if (s.bAssignment >= ON_DUTY && s.bAssignment != VEHICLE) return ME_BUSY;
3868
3869 /* If he's walking/driving, and so tired that he would just stop the group
3870 * anyway in the next sector, or already asleep and can't be awakened */
3871 if (PlayerSoldierTooTiredToTravel(s))
3872 {
3873 gsCustomErrorString = st_format_printf(pMapErrorString[43], s.name);
3874 return ME_CUSTOM;
3875 }
3876
3877 if (AM_A_ROBOT(&s))
3878 { // Going alone?
3879 if ((s.bAssignment == VEHICLE && !IsRobotControllerInVehicle(s.iVehicleId)) ||
3880 (s.bAssignment < ON_DUTY && !IsRobotControllerInSquad(s.bAssignment)))
3881 {
3882 return ME_ROBOT_ALONE;
3883 }
3884 }
3885 else if (s.ubWhatKindOfMercAmI == MERC_TYPE__EPC) // an Escorted NPC?
3886 {
3887 // Going alone?
3888 if ((s.bAssignment == VEHICLE && GetNumberOfNonEPCsInVehicle(s.iVehicleId) == 0) ||
3889 (s.bAssignment < ON_DUTY && NumberOfNonEPCsInSquad(s.bAssignment) == 0))
3890 {
3891 MERCPROFILESTRUCT const& p = GetProfile(s.ubProfile);
3892 ST::string text = p.bSex == MALE ? pMapErrorString[6] : pMapErrorString[7];
3893 gsCustomErrorString = st_format_printf(text, s.name);
3894 return ME_CUSTOM;
3895 }
3896 }
3897
3898 // Find out if this particular character can't move for some reason
3899 bool problem_exists = false;
3900 switch (s.ubProfile)
3901 {
3902 case MARIA:
3903 // Maria can't move if she's in sector C5
3904 if (SECTOR(s.sSectorX, s.sSectorY) == SEC_C5) problem_exists = true;
3905 break;
3906 }
3907
3908 if (problem_exists)
3909 { // Inform user this specific merc cannot be moved out of the sector
3910 gsCustomErrorString = st_format_printf(pMapErrorString[29], s.name);
3911 return ME_CUSTOM;
3912 }
3913
3914 // Passed all checks - this character may move strategically!
3915 return ME_OK;
3916 }
3917
3918
CanEntireMovementGroupMercIsInMove(SOLDIERTYPE & s)3919 MoveError CanEntireMovementGroupMercIsInMove(SOLDIERTYPE& s)
3920 {
3921 // First check the requested character himself
3922 MoveError const ret = CanCharacterMoveInStrategic(s);
3923 if (ret != ME_OK) return ret; // Failed no point checking anyone else
3924
3925 // Now check anybody who would be traveling with him
3926
3927 /* Even if group is 0 (not that that should happen, should it?) still loop
3928 * through for other mercs selected to move */
3929 GROUP const* const g = GetSoldierGroup(s);
3930
3931 /* If anyone in the merc's group or also selected cannot move for whatever
3932 * reason return an error */
3933 CFOR_EACH_IN_CHAR_LIST(c)
3934 {
3935 SOLDIERTYPE& other = *c->merc;
3936 if (&other == &s) continue; // Skip the same guy we did already
3937
3938 /* Is he in the same movement group (i.e. squad), or is he still selected to
3939 * go with us (legal?) */
3940 if (GetSoldierGroup(other) != g && !c->selected) continue;
3941
3942 MoveError const ret = CanCharacterMoveInStrategic(other);
3943 // Cannot move, fail, and don't bother checking anyone else, either
3944 if (ret != ME_OK) return ret;
3945 }
3946
3947 // Everybody can move
3948 return ME_OK;
3949 }
3950
3951
ReportMapScreenMovementError(const INT8 bErrorNumber)3952 void ReportMapScreenMovementError(const INT8 bErrorNumber)
3953 {
3954 // a customized message?
3955 ST::string text = (bErrorNumber != ME_CUSTOM ? pMapErrorString[bErrorNumber] : gsCustomErrorString);
3956 DoMapMessageBox(MSG_BOX_BASIC_STYLE, text, MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
3957 }
3958
3959
3960 // we are checking to see if we need to in fact rebuild the characterlist for mapscreen
HandleRebuildingOfMapScreenCharacterList(void)3961 void HandleRebuildingOfMapScreenCharacterList( void )
3962 {
3963 // check if we need to rebuild the list?
3964 if( fReBuildCharacterList )
3965 {
3966 // do the actual rebuilding
3967 ReBuildCharactersList( );
3968
3969 // reset the flag
3970 fReBuildCharacterList = FALSE;
3971 }
3972 }
3973
3974
RequestToggleTimeCompression(void)3975 void RequestToggleTimeCompression( void )
3976 {
3977 if( !IsTimeBeingCompressed( ) )
3978 {
3979 StartTimeCompression();
3980 }
3981 else // currently compressing
3982 {
3983 StopTimeCompression();
3984 }
3985 }
3986
3987
RequestIncreaseInTimeCompression(void)3988 void RequestIncreaseInTimeCompression( void )
3989 {
3990 if ( IsTimeBeingCompressed( ) )
3991 {
3992 IncreaseGameTimeCompressionRate();
3993 }
3994 else
3995 {
3996 /*
3997 // start compressing
3998 StartTimeCompression();
3999 */
4000 // ARM Change: start over at 5x compression
4001 SetGameTimeCompressionLevel( TIME_COMPRESS_5MINS );
4002 }
4003 }
4004
4005
4006
RequestDecreaseInTimeCompression(void)4007 void RequestDecreaseInTimeCompression( void )
4008 {
4009 if ( IsTimeBeingCompressed( ) )
4010 {
4011 DecreaseGameTimeCompressionRate();
4012 }
4013 else
4014 {
4015 // check that we can
4016 if ( !AllowedToTimeCompress( ) )
4017 {
4018 // not allowed to compress time
4019 TellPlayerWhyHeCantCompressTime();
4020 return;
4021 }
4022
4023 // ARM Change: do nothing
4024 /*
4025 // if compression mode is set, just restart time so player can see it
4026 if ( giTimeCompressMode > TIME_COMPRESS_X1 )
4027 {
4028 StartTimeCompression();
4029 }
4030 */
4031 }
4032 }
4033
4034
CanSoldierMoveWithVehicleId(const SOLDIERTYPE * const pSoldier,const INT32 iVehicle1Id)4035 static BOOLEAN CanSoldierMoveWithVehicleId(const SOLDIERTYPE* const pSoldier, const INT32 iVehicle1Id)
4036 {
4037 INT32 iVehicle2Id = -1;
4038 VEHICLETYPE *pVehicle1, *pVehicle2;
4039
4040
4041 Assert( iVehicle1Id != -1 );
4042
4043 // if soldier is IN a vehicle
4044 if( pSoldier->bAssignment == VEHICLE )
4045 {
4046 iVehicle2Id = pSoldier->iVehicleId;
4047 }
4048 else
4049 // if soldier IS a vehicle
4050 if( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
4051 {
4052 iVehicle2Id = pSoldier->bVehicleID;
4053 }
4054
4055
4056 // if also (in) a vehicle
4057 if ( iVehicle2Id != -1 )
4058 {
4059 // if it's the same vehicle
4060 if ( iVehicle1Id == iVehicle2Id )
4061 {
4062 return( TRUE );
4063 }
4064
4065 // helicopter can't move together with ground vehicles!
4066 if ( ( iVehicle1Id == iHelicopterVehicleId ) || ( iVehicle2Id == iHelicopterVehicleId ) )
4067 {
4068 return( FALSE );
4069 }
4070
4071 pVehicle1 = &( pVehicleList[ iVehicle1Id ] );
4072 pVehicle2 = &( pVehicleList[ iVehicle2Id ] );
4073
4074 // as long as they're in the same location, amd neither is between sectors, different vehicles is also ok
4075 if( ( pVehicle1->sSectorX == pVehicle2->sSectorX ) &&
4076 ( pVehicle1->sSectorY == pVehicle2->sSectorY ) &&
4077 ( pVehicle1->sSectorZ == pVehicle2->sSectorZ ) &&
4078 !pVehicle1->fBetweenSectors &&
4079 !pVehicle2->fBetweenSectors )
4080 {
4081 return( TRUE );
4082 }
4083 }
4084
4085
4086 // not in/is a vehicle, or in a different vehicle that isn't in the same location
4087 return ( FALSE );
4088 }
4089
4090
SaveLeaveItemList(HWFILE const f)4091 void SaveLeaveItemList(HWFILE const f)
4092 {
4093 for (INT32 i = 0; i < NUM_LEAVE_LIST_SLOTS; ++i)
4094 {
4095 MERC_LEAVE_ITEM const* const head = gpLeaveListHead[i];
4096 if (head)
4097 {
4098 // Save to specify that a node DOES exist
4099 BOOLEAN const node_exists = TRUE;
4100 FileWrite(f, &node_exists, sizeof(BOOLEAN));
4101
4102 // Save number of items
4103 UINT32 n_items = 0;
4104 for (MERC_LEAVE_ITEM const* i = head; i; i = i->pNext)
4105 {
4106 ++n_items;
4107 }
4108 FileWrite(f, &n_items, sizeof(UINT32));
4109
4110 for (MERC_LEAVE_ITEM const* i = head; i; i = i->pNext)
4111 {
4112 BYTE data[40];
4113 DataWriter d{data};
4114 InjectObject(d, &i->o);
4115 INJ_SKIP(d, 4)
4116 Assert(d.getConsumed() == lengthof(data));
4117
4118 FileWrite(f, data, sizeof(data));
4119 }
4120 }
4121 else
4122 {
4123 // Save to specify that a node DOENST exist
4124 BOOLEAN const node_exists = FALSE;
4125 FileWrite(f, &node_exists, sizeof(BOOLEAN));
4126 }
4127 }
4128
4129 // Save the leave list profile IDs
4130 for (INT32 i = 0; i < NUM_LEAVE_LIST_SLOTS; ++i)
4131 {
4132 FileWrite(f, &guiLeaveListOwnerProfileId[i], sizeof(UINT32));
4133 }
4134 }
4135
4136
LoadLeaveItemList(HWFILE const f)4137 void LoadLeaveItemList(HWFILE const f)
4138 {
4139 ShutDownLeaveList();
4140 InitLeaveList();
4141
4142 for (INT32 i = 0; i < NUM_LEAVE_LIST_SLOTS; ++i)
4143 {
4144 // Load flag which specifies whether a node exists
4145 BOOLEAN node_exists;
4146 FileRead(f, &node_exists, sizeof(BOOLEAN));
4147 if (!node_exists) continue;
4148
4149 // Load the number specifing how many items there are in the list
4150 UINT32 n_items;
4151 FileRead(f, &n_items, sizeof(UINT32));
4152
4153 MERC_LEAVE_ITEM** anchor = &gpLeaveListHead[i];
4154 for (UINT32 n = n_items; n != 0; --n)
4155 {
4156 MERC_LEAVE_ITEM* const li = new MERC_LEAVE_ITEM{};
4157
4158 BYTE data[40];
4159 FileRead(f, data, sizeof(data));
4160
4161 DataReader d{data};
4162 ExtractObject(d, &li->o);
4163 EXTR_SKIP(d, 4)
4164 Assert(d.getConsumed() == lengthof(data));
4165
4166 // Append node to list
4167 *anchor = li;
4168 anchor = &li->pNext;
4169 }
4170 }
4171
4172 // Load the leave list profile IDs
4173 for (INT32 i = 0; i < NUM_LEAVE_LIST_SLOTS; ++i)
4174 {
4175 FileRead(f, &guiLeaveListOwnerProfileId[i], sizeof(UINT32));
4176 }
4177 }
4178
4179
TurnOnSectorLocator(UINT8 ubProfileID)4180 void TurnOnSectorLocator( UINT8 ubProfileID )
4181 {
4182 Assert( ubProfileID != NO_PROFILE );
4183
4184 const SOLDIERTYPE* const pSoldier = FindSoldierByProfileID(ubProfileID);
4185 if( pSoldier )
4186 {
4187 gsSectorLocatorX = pSoldier->sSectorX;
4188 gsSectorLocatorY = pSoldier->sSectorY;
4189 }
4190 else
4191 {
4192 // if it's Skyrider (when he's not on our team), and his chopper has been setup
4193 if ( ( ubProfileID == SKYRIDER ) && fSkyRiderSetUp )
4194 {
4195 // if helicopter position is being shown, don't do this, too, cause the helicopter icon is on top and it looks
4196 // like crap. I tried moving the heli icon blit to before, but that screws up it's blitting.
4197 if ( !fShowAircraftFlag )
4198 {
4199 // can't use his profile, he's where his chopper is
4200 VEHICLETYPE const& v = GetHelicopter();
4201 gsSectorLocatorX = v.sSectorX;
4202 gsSectorLocatorY = v.sSectorY;
4203 }
4204 else
4205 {
4206 return;
4207 }
4208 }
4209 else
4210 {
4211 gsSectorLocatorX = gMercProfiles[ ubProfileID ].sSectorX;
4212 gsSectorLocatorY = gMercProfiles[ ubProfileID ].sSectorY;
4213 }
4214 }
4215 gubBlitSectorLocatorCode = LOCATOR_COLOR_YELLOW;
4216 }
4217
4218
TurnOffSectorLocator()4219 void TurnOffSectorLocator()
4220 {
4221 gubBlitSectorLocatorCode = LOCATOR_COLOR_NONE;
4222 fMapPanelDirty = TRUE;
4223 }
4224
4225
4226
HandleBlitOfSectorLocatorIcon(INT16 sSectorX,INT16 sSectorY,INT16 sSectorZ,UINT8 ubLocatorID)4227 void HandleBlitOfSectorLocatorIcon( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ, UINT8 ubLocatorID )
4228 {
4229 static UINT8 ubFrame = 0;
4230 UINT8 ubBaseFrame = 0;
4231 UINT32 uiTimer = 0;
4232 INT16 sScreenX, sScreenY;
4233
4234
4235 // blits at 0,0 had been observerd...
4236 Assert( ( sSectorX >= 1 ) && ( sSectorX <= 16 ) );
4237 Assert( ( sSectorY >= 1 ) && ( sSectorY <= 16 ) );
4238 Assert( ( sSectorZ >= 0 ) && ( sSectorZ <= 3 ) );
4239
4240 if( sSectorZ != iCurrentMapSectorZ )
4241 { //if the z level of the map screen renderer is different than the
4242 //sector z that we wish to locate, then don't render it
4243 return;
4244 }
4245
4246 // if showing sector inventory, don't do this
4247 if( fShowMapInventoryPool )
4248 {
4249 return;
4250 }
4251
4252 switch( ubLocatorID )
4253 {
4254 // grab zoomed out icon
4255 case LOCATOR_COLOR_RED:
4256 ubBaseFrame = 0;
4257 ubFrame = (UINT8)(ubFrame % 13);
4258 break;
4259 case LOCATOR_COLOR_YELLOW:
4260 ubBaseFrame = 13;
4261 ubFrame = (UINT8)(13 + (ubFrame % 13) );
4262 break;
4263 default:
4264 //not supported
4265 return;
4266 }
4267
4268 //Convert the sector value into screen values.
4269 GetScreenXYFromMapXY( sSectorX, sSectorY, &sScreenX, &sScreenY );
4270 // make sure we are on the border
4271 if( sScreenX < MAP_GRID_X )
4272 {
4273 sScreenX = MAP_GRID_X;
4274 }
4275 sScreenY--; //Carterism ritual
4276 if( sScreenY < MAP_GRID_Y )
4277 {
4278 sScreenY = MAP_GRID_Y;
4279 }
4280
4281 uiTimer = GetJA2Clock();
4282
4283 // if first time in, reset value
4284 if( guiSectorLocatorBaseTime == 0 )
4285 {
4286 guiSectorLocatorBaseTime = GetJA2Clock( );
4287 }
4288
4289 // check if enough time has passed to update the frame counter
4290 if( ANIMATED_BATTLEICON_FRAME_TIME < ( uiTimer - guiSectorLocatorBaseTime ) )
4291 {
4292 guiSectorLocatorBaseTime = uiTimer;
4293 ubFrame++;
4294
4295 if( ubFrame > ubBaseFrame + MAX_FRAME_COUNT_FOR_ANIMATED_BATTLE_ICON )
4296 {
4297 ubFrame = ubBaseFrame;
4298 }
4299 }
4300
4301 RestoreExternBackgroundRect( (INT16)(sScreenX + 1), (INT16)(sScreenY - 1), MAP_GRID_X , MAP_GRID_Y );
4302
4303 // blit object to frame buffer
4304 BltVideoObject(FRAME_BUFFER, guiSectorLocatorGraphicID, ubFrame, sScreenX, sScreenY);
4305
4306 // invalidate region on frame buffer
4307 InvalidateRegion(sScreenX, sScreenY, sScreenX + MAP_GRID_X + 1, sScreenY + MAP_GRID_Y + 1);
4308 }
4309
4310
MakeDialogueEventShowContractMenu(SOLDIERTYPE & s)4311 void MakeDialogueEventShowContractMenu(SOLDIERTYPE& s)
4312 {
4313 class CharacterDialogueEventShowContractMenu : public CharacterDialogueEvent
4314 {
4315 public:
4316 CharacterDialogueEventShowContractMenu(SOLDIERTYPE& s) : CharacterDialogueEvent(s) {}
4317
4318 bool Execute()
4319 {
4320 if (!MayExecute()) return true;
4321
4322 // ATE: THis is working with MARK'S STUFF :(
4323 // Need this stuff so that bSelectedInfoChar is set...
4324 SOLDIERTYPE const& s = soldier_;
4325 SetInfoChar(&s);
4326 fShowContractMenu = TRUE;
4327 RebuildContractBoxForMerc(&s);
4328 bSelectedContractChar = bSelectedInfoChar;
4329
4330 return false;
4331 }
4332 };
4333
4334 DialogueEvent::Add(new CharacterDialogueEventShowContractMenu(s));
4335 }
4336
4337
CheckIfSalaryIncreasedAndSayQuote(SOLDIERTYPE * pSoldier,BOOLEAN fTriggerContractMenu)4338 BOOLEAN CheckIfSalaryIncreasedAndSayQuote( SOLDIERTYPE *pSoldier, BOOLEAN fTriggerContractMenu )
4339 {
4340 Assert( pSoldier );
4341
4342 // OK, check if their price has gone up
4343 if( pSoldier->fContractPriceHasIncreased )
4344 {
4345 if ( fTriggerContractMenu )
4346 {
4347 // have him say so first - post the dialogue event with the contract menu event
4348 MakeDialogueEventEnterMapScreen();
4349 HandleImportantMercQuote( pSoldier, QUOTE_MERC_GONE_UP_IN_PRICE );
4350 MakeDialogueEventShowContractMenu(*pSoldier);
4351 }
4352 else
4353 {
4354 // now post the dialogue event and the contratc menu event
4355 HandleImportantMercQuote( pSoldier, QUOTE_MERC_GONE_UP_IN_PRICE );
4356 }
4357
4358 pSoldier->fContractPriceHasIncreased = FALSE;
4359
4360 // said quote / triggered contract menu
4361 return( TRUE );
4362 }
4363 else
4364 {
4365 // nope, nothing to do
4366 return( FALSE );
4367 }
4368 }
4369
4370
LoadMapScreenInterfaceGraphics()4371 void LoadMapScreenInterfaceGraphics()
4372 {
4373 guiSectorLocatorGraphicID = AddVideoObjectFromFile(INTERFACEDIR "/hilite.sti");
4374 guiSelectedCharArrow = AddVideoObjectFromFile(INTERFACEDIR "/selectedchararrow.sti");
4375 }
4376
4377
DeleteMapScreenInterfaceGraphics()4378 void DeleteMapScreenInterfaceGraphics()
4379 {
4380 DeleteVideoObject(guiSectorLocatorGraphicID);
4381 DeleteVideoObject(guiSelectedCharArrow);
4382 }
4383