1 #include "Editor_Callback_Prototypes.h"
2 #include "HImage.h"
3 #include "Handle_UI.h"
4 #include "Interface.h"
5 #include "Local.h"
6 #include "Edit_Sys.h"
7 #include "TileDat.h"
8 #include "VSurface.h"
9 #include "VObject.h"
10 #include "MouseSystem.h"
11 #include "Button_System.h"
12 #include "Line.h"
13 #include "Input.h"
14 #include "SysUtil.h"
15 #include "Font.h"
16 #include "Font_Control.h"
17 #include "EditScreen.h"
18 #include "SelectWin.h"
19 #include "Video.h"
20 #include "Interface_Items.h"
21 #include "Text.h"
22 #include "World_Items.h"
23 #include "WorldMan.h"
24 #include "WorldDef.h"
25 #include "Overhead.h"
26 #include "RenderWorld.h"
27 #include "Animation_Data.h"
28 #include "Animation_Control.h"
29 #include "EditorDefines.h"
30 #include "EditorMercs.h"
31 #include "EditorTerrain.h" //for access to TerrainTileDrawMode
32 #include "Soldier_Create.h" //The stuff that connects the editor generated information
33 //to the saved map and the game itself.
34 #include "Inventory_Choosing.h"
35 #include "Soldier_Init_List.h"
36 #include "StrategicMap.h"
37 #include "Soldier_Add.h"
38 #include "Soldier_Control.h"
39 #include "Soldier_Profile_Type.h"
40 #include "Soldier_Profile.h"
41 #include "Text_Input.h"
42 #include "Random.h"
43 #include "WordWrap.h"
44 #include "EditorItems.h"
45 #include "Editor_Taskbar_Utils.h"
46 #include "Exit_Grids.h"
47 #include "Editor_Undo.h"
48 #include "Item_Statistics.h"
49 #include "Simple_Render_Utils.h"
50 #include "Map_Information.h"
51 #include "Isometric_Utils.h"
52 #include "Render_Dirty.h"
53 #include "PopupMenu.h"
54 #include "Scheduling.h"
55 #include "Timer_Control.h"
56 #include "MemMan.h"
57 #include "Items.h"
58 #include "Debug.h"
59 #include "Message.h"
60
61 #include "ContentManager.h"
62 #include "GameInstance.h"
63
64 #include <string_theory/format>
65 #include <string_theory/string>
66
67
68 //--------------------------------------------------
69 // NON_CIV_GROUP,
70 // REBEL_CIV_GROUP,
71 // KINGPIN_CIV_GROUP,
72 // SANMONA_ARMS_GROUP,
73 // ANGELS_GROUP,
74 // NUM_CIV_GROUPS
75 const ST::string gszCivGroupNames[NUM_CIV_GROUPS] =
76 {
77 "NONE",
78 "REBEL",
79 "KINGPIN",
80 "SANMONA ARMS",
81 "ANGELS",
82
83 "BEGGARS",
84 "TOURISTS",
85 "ALMA MIL",
86 "DOCTORS",
87 "COUPLE1",
88
89 "HICKS",
90 "WARDEN",
91 "JUNKYARD",
92 "FACTORY KIDS",
93 "QUEENS",
94 "UNUSED15",
95 "UNUSED16",
96 "UNUSED17",
97 "UNUSED18",
98 "UNUSED19",
99 };
100
101 //--------------------------------------------------
102
103 // SCHEDULE_ACTION_NONE,
104 // SCHEDULE_ACTION_LOCKDOOR,
105 // SCHEDULE_ACTION_UNLOCKDOOR,
106 // SCHEDULE_ACTION_OPENDOOR,
107 // SCHEDULE_ACTION_CLOSEDOOR,
108 // SCHEDULE_ACTION_GRIDNO,
109 // SCHEDULE_ACTION_LEAVESECTOR,
110 // SCHEDULE_ACTION_ENTERSECTOR,
111 // SCHEDULE_ACTION_STAYINSECTOR,
112 // SCHEDULE_ACTION_SLEEP,
113 const ST::string gszScheduleActions[NUM_SCHEDULE_ACTIONS] =
114 {
115 "No action",
116 "Lock door",
117 "Unlock door",
118 "Open door",
119 "Close door",
120 "Move to gridno",
121 "Leave sector",
122 "Enter sector",
123 "Stay in sector",
124 "Sleep",
125 "Ignore this!"
126 };
127
128
129 enum
130 {
131 SCHEDULE_INSTRUCTIONS_NONE,
132 SCHEDULE_INSTRUCTIONS_DOOR1,
133 SCHEDULE_INSTRUCTIONS_DOOR2,
134 SCHEDULE_INSTRUCTIONS_GRIDNO,
135 SCHEDULE_INSTRUCTIONS_SLEEP,
136 NUM_SCHEDULE_INSTRUCTIONS
137 };
138
139 BOOLEAN gfSingleAction = FALSE;
140 BOOLEAN gfUseScheduleData2 = FALSE;
141 UINT8 gubCurrentScheduleActionIndex = 0;
142 SCHEDULENODE gCurrSchedule;
143 UINT8 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_NONE;
144
145
146 //array which keeps track of which item is in which slot. This is dependant on the selected merc, so
147 //these temp values must be updated when different mercs are selected, and reset when a merc detailed
148 //placement is created.
149 OBJECTTYPE *gpMercSlotItem[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
150 //Because we only support these nine slots, they aren't continuous values, so this array helps
151 //processing functions by referring to this array to get the appropriate slot.
152 INT8 gbMercSlotTypes[9] = { HELMETPOS, VESTPOS, LEGPOS, HANDPOS, SECONDHANDPOS,
153 BIGPOCK1POS, BIGPOCK2POS, BIGPOCK3POS, BIGPOCK4POS };
154 //returns the usItem index of specified slot in the currently selected merc.
155 #define GetSelectedMercSlotItemIndex( x ) (gpSelected->pDetailedPlacement->Inv[gbMercSlotTypes[x]].usItem)
156 #define GetSelectedMercSlot( x ) (&gpSelected->pDetailedPlacement->Inv[gbMercSlotTypes[x]])
157 //values indicating which merc inventory slot is hilited and which slot is selected.
158 INT8 gbCurrHilite = -1;
159 INT8 gbCurrSelect = -1;
160
161 //internal merc variables
162 BASIC_SOLDIERCREATE_STRUCT gTempBasicPlacement;
163 SOLDIERCREATE_STRUCT gTempDetailedPlacement;
164
165 SOLDIERTYPE* g_selected_merc;
166 INT16 gsSelectedMercGridNo;
167 SOLDIERINITNODE *gpSelected;
168
169 UINT8 gubCurrMercMode = MERC_TEAMMODE;
170 static UINT8 gubPrevMercMode = MERC_NOMODE;
171 UINT8 gubLastDetailedMercMode = MERC_GENERALMODE;
172 INT8 gbDefaultOrders = STATIONARY;
173 INT8 gbDefaultAttitude = DEFENSIVE;
174 INT8 gbDefaultRelativeEquipmentLevel = 2;
175 INT8 gbDefaultRelativeAttributeLevel = 2;
176 INT8 gbDefaultDirection = NORTHWEST;
177 INT8 gubSoldierClass = SOLDIER_CLASS_ARMY;
178 UINT8 gubCivGroup = NON_CIV_GROUP;
179
180 SOLDIERTYPE *pTempSoldier;
181 BOOLEAN gfRoofPlacement;
182
183 //Below are all flags that have to do with editing detailed placement mercs:
184
185 //Determines if the user is allowed to edit merc colors. User must specifically
186 //click on the checkbox by the colors to confirm that colors will be specified. If
187 //not, the colors will be randomly generated upon creation in the game.
188 BOOLEAN gfCanEditMercColors = FALSE;
189 //A rendering flag that is set whenever a full update of the merc editing information
190 //needs to be done.
191 BOOLEAN gfRenderMercInfo = FALSE;
192 //When the user specifies a profile index for the merc, all editing is disabled. This is
193 //because the profile contains all of the information. When this happens, all of the editing
194 //buttons are disabled, but you are allowed to view stats, inventory, etc., as well as specify
195 //orders, attitude, and direction.
196 BOOLEAN gfCanEditMercs = TRUE;
197 //When in inventory mode, this flag is set when the user wishes to get an item, which requires hooking
198 //into the item editing features. This is processed during the editor update, which in turn, sets the
199 //new mode.
200 BOOLEAN gfMercGetItem = FALSE;
201 //As soon as an item is selected, the items index is stored here, so the item can be copied into the
202 //slot for editing and rendering purposes. This is a temp store value only when leaving the editor items
203 //mode.
204 UINT16 gusMercsNewItemIndex = 0xffff;
205
206 INT32 iEditWhichStat = -1;
207 INT32 iEditMercMode = EDIT_MERC_NONE;
208 INT32 iEditColorStart[EDIT_NUM_COLORS];
209
210 BOOLEAN gfShowPlayers = TRUE;
211 BOOLEAN gfShowEnemies = TRUE;
212 BOOLEAN gfShowCreatures = TRUE;
213 BOOLEAN gfShowRebels = TRUE;
214 BOOLEAN gfShowCivilians = TRUE;
215
216
217 //information for bodytypes.
218 static SoldierBodyType const bEnemyArray[] = { BODY_RANDOM, REGMALE, BIGMALE, STOCKYMALE, REGFEMALE, TANK_NW, TANK_NE };
219 static SoldierBodyType const bCreatureArray[] = { BLOODCAT, LARVAE_MONSTER, INFANT_MONSTER, YAF_MONSTER, YAM_MONSTER, ADULTFEMALEMONSTER, AM_MONSTER, QUEENMONSTER };
220 static SoldierBodyType const bRebelArray[] = { BODY_RANDOM, FATCIV, MANCIV, REGMALE, BIGMALE, STOCKYMALE, REGFEMALE };
221 static SoldierBodyType const bCivArray[] = { BODY_RANDOM, FATCIV, MANCIV, MINICIV, DRESSCIV, HATKIDCIV, KIDCIV, REGMALE, BIGMALE, STOCKYMALE, REGFEMALE, HUMVEE, ELDORADO, ICECREAMTRUCK, JEEP, CRIPPLECIV, ROBOTNOWEAPON, COW };
222 static SoldierBodyType gbCurrCreature = BLOODCAT;
223
224
GameInitEditorMercsInfo()225 void GameInitEditorMercsInfo()
226 {
227 INT32 i;
228 //Initialize the placement list
229 InitSoldierInitList();
230 gMapInformation.ubNumIndividuals = 0;
231 gCurrSchedule = SCHEDULENODE{};
232 for( i = 0; i < 4; i++ )
233 {
234 gCurrSchedule.usTime[i] = 0xffff;
235 gCurrSchedule.usData1[i] = 0xffff;
236 gCurrSchedule.usData2[i] = 0xffff;
237 }
238 }
239
GameShutdownEditorMercsInfo()240 void GameShutdownEditorMercsInfo()
241 {
242 UseEditorAlternateList();
243 KillSoldierInitList();
244 UseEditorOriginalList();
245 KillSoldierInitList();
246 }
247
EntryInitEditorMercsInfo()248 void EntryInitEditorMercsInfo()
249 {
250 INT32 x, iCurStart = 0;
251 iEditColorStart[0] = 0;
252 for ( x = 1; x < EDIT_NUM_COLORS; x++ )
253 {
254 iCurStart += gubpNumReplacementsPerRange[ x - 1 ];
255 iEditColorStart[x] = iCurStart;
256 }
257 g_selected_merc = NULL;
258 gsSelectedMercGridNo = 0;
259
260 gfCanEditMercs = TRUE;
261 }
262
263
ProcessMercEditing(void)264 void ProcessMercEditing(void)
265 {
266 if (iEditMercMode != EDIT_MERC_NEXT_COLOR) return;
267
268 // Handle changes to the merc colors
269 SOLDIERTYPE& s = *g_selected_merc;
270 ST::string* soldier_pal;
271 ST::string* placement_pal;
272 UINT8 ubType;
273 switch (iEditWhichStat)
274 {
275 case 0:
276 case 1:
277 ubType = EDIT_COLOR_HEAD;
278 soldier_pal = &s.HeadPal;
279 placement_pal = &gpSelected->pDetailedPlacement->HeadPal;
280 break;
281
282 case 2:
283 case 3:
284 ubType = EDIT_COLOR_SKIN;
285 soldier_pal = &s.SkinPal;
286 placement_pal = &gpSelected->pDetailedPlacement->SkinPal;
287 break;
288
289 case 4:
290 case 5:
291 ubType = EDIT_COLOR_VEST;
292 soldier_pal = &s.VestPal;
293 placement_pal = &gpSelected->pDetailedPlacement->VestPal;
294 break;
295
296 case 6:
297 case 7:
298 ubType = EDIT_COLOR_PANTS;
299 soldier_pal = &s.PantsPal;
300 placement_pal = &gpSelected->pDetailedPlacement->PantsPal;
301 break;
302
303 default:
304 iEditMercMode = EDIT_MERC_NONE;
305 return;
306 }
307
308 UINT8 ubPaletteRep = GetPaletteRepIndexFromID(*soldier_pal);
309 const INT32 start = iEditColorStart[ubType];
310 const UINT8 range = gubpNumReplacementsPerRange[ubType];
311 if (iEditWhichStat & 1)
312 {
313 ubPaletteRep = (ubPaletteRep < start + range - 1 ? ubPaletteRep + 1 : start);
314 }
315 else
316 {
317 ubPaletteRep = (ubPaletteRep > start ? ubPaletteRep - 1 : start + range - 1);
318 }
319
320 soldier_pal->set(gpPalRep[ubPaletteRep].ID);
321 placement_pal->set(gpPalRep[ubPaletteRep].ID);
322 CreateSoldierPalettes(s);
323
324 iEditMercMode = EDIT_MERC_NONE;
325 }
326
327
AddMercToWorld(INT32 iMapIndex)328 void AddMercToWorld( INT32 iMapIndex )
329 {
330 INT32 i;
331
332 gTempBasicPlacement = BASIC_SOLDIERCREATE_STRUCT{};
333
334 //calculate specific information based on the team.
335 SoldierBodyType body = BODY_RANDOM;
336 switch( iDrawMode )
337 {
338 case DRAW_MODE_ENEMY:
339 gTempBasicPlacement.bTeam = ENEMY_TEAM;
340 gTempBasicPlacement.ubSoldierClass = gubSoldierClass;
341 break;
342 case DRAW_MODE_CREATURE:
343 gTempBasicPlacement.bTeam = CREATURE_TEAM;
344 body = gbCurrCreature;
345 break;
346 case DRAW_MODE_REBEL:
347 gTempBasicPlacement.bTeam = MILITIA_TEAM;
348 break;
349 case DRAW_MODE_CIVILIAN:
350 gTempBasicPlacement.bTeam = CIV_TEAM;
351 gTempBasicPlacement.ubCivilianGroup = gubCivGroup;
352 if (giCurrentTilesetID == CAVES_1)
353 {
354 gTempBasicPlacement.ubSoldierClass = SOLDIER_CLASS_MINER;
355 }
356 break;
357 }
358 gTempBasicPlacement.bBodyType = body;
359
360 if( IsLocationSittable( iMapIndex, gfRoofPlacement ) )
361 {
362 SOLDIERINITNODE *pNode;
363
364 //Set up some general information.
365 gTempBasicPlacement.fDetailedPlacement = FALSE;
366 gTempBasicPlacement.fPriorityExistance = FALSE;
367 gTempBasicPlacement.usStartingGridNo = (UINT16)iMapIndex;
368 gTempBasicPlacement.bOrders = gbDefaultOrders;
369 gTempBasicPlacement.bAttitude = gbDefaultAttitude;
370 gTempBasicPlacement.bRelativeAttributeLevel = gbDefaultRelativeAttributeLevel;
371 gTempBasicPlacement.bRelativeEquipmentLevel = gbDefaultRelativeEquipmentLevel;
372 gTempBasicPlacement.bDirection = gbDefaultDirection;
373
374 //Generate detailed placement information given the temp placement information.
375 CreateDetailedPlacementGivenBasicPlacementInfo( &gTempDetailedPlacement, &gTempBasicPlacement );
376
377 //Set the sector information -- probably unnecessary.
378 gTempDetailedPlacement.sSectorX = gWorldSectorX;
379 gTempDetailedPlacement.sSectorY = gWorldSectorY;
380
381 //Create the soldier, but don't place it yet.
382 SOLDIERTYPE* const pSoldier = TacticalCreateSoldier(gTempDetailedPlacement);
383 if (pSoldier != NULL)
384 {
385 pSoldier->bVisible = 1;
386 pSoldier->bLastRenderVisibleValue = 1;
387 //Set up the soldier in the list, so we can track the soldier in the
388 //future (saving, loading, strategic AI)
389 pNode = AddBasicPlacementToSoldierInitList(gTempBasicPlacement);
390 pNode->pSoldier = pSoldier;
391
392 //Add the soldier to physically appear on the map now.
393 AddSoldierToSectorNoCalculateDirection(pSoldier);
394 IndicateSelectedMerc(pSoldier->ubID);
395
396 //Move him to the roof if intended and possible.
397 if( gfRoofPlacement && FlatRoofAboveGridNo( iMapIndex ) )
398 {
399 gpSelected->pBasicPlacement->fOnRoof = TRUE;
400 if( gpSelected->pDetailedPlacement )
401 gpSelected->pDetailedPlacement->fOnRoof = TRUE;
402 SetSoldierHeight( gpSelected->pSoldier, SECOND_LEVEL_Z_OFFSET );
403 }
404 UnclickEditorButtons( FIRST_MERCS_INVENTORY_BUTTON, LAST_MERCS_INVENTORY_BUTTON );
405 for( i = FIRST_MERCS_INVENTORY_BUTTON; i <= LAST_MERCS_INVENTORY_BUTTON; i++ )
406 {
407 SetEnemyDroppableStatus( gbMercSlotTypes[i-FIRST_MERCS_INVENTORY_BUTTON], FALSE );
408 }
409 }
410 }
411 }
412
413
414 static SOLDIERTYPE* IsMercHere(INT32 iMapIndex);
415
416
HandleRightClickOnMerc(INT32 iMapIndex)417 void HandleRightClickOnMerc( INT32 iMapIndex )
418 {
419 const SOLDIERTYPE* const this_merc = IsMercHere(iMapIndex);
420 if (this_merc != NULL)
421 {
422 if (g_selected_merc != this_merc)
423 { // We want to edit a new merc (or different merc)
424 //We need to avoid the editing of player mercs.
425 if (!FindSoldierInitNodeBySoldier(*this_merc))
426 return; //this is a player merc (which isn't in the list), or an error in logic.
427 IndicateSelectedMerc(this_merc->ubID);
428 }
429 }
430 else if (g_selected_merc != NULL && IsLocationSittable(iMapIndex, gfRoofPlacement)) // We want to move the selected merc to this new location.
431 {
432 RemoveAllObjectsOfTypeRange( gsSelectedMercGridNo, CONFIRMMOVE, CONFIRMMOVE );
433 EVENT_SetSoldierPosition(gpSelected->pSoldier, iMapIndex, SSP_NONE);
434 if( gfRoofPlacement && FlatRoofAboveGridNo( iMapIndex ) )
435 {
436 gpSelected->pBasicPlacement->fOnRoof = TRUE;
437 if( gpSelected->pDetailedPlacement )
438 gpSelected->pDetailedPlacement->fOnRoof = TRUE;
439 SetSoldierHeight( gpSelected->pSoldier, SECOND_LEVEL_Z_OFFSET );
440 }
441 else
442 {
443 gpSelected->pBasicPlacement->fOnRoof = FALSE;
444 if( gpSelected->pDetailedPlacement )
445 gpSelected->pDetailedPlacement->fOnRoof = FALSE;
446 SetSoldierHeight( gpSelected->pSoldier, 0.0 );
447 }
448 gsSelectedMercGridNo = (INT16)iMapIndex;
449 gpSelected->pBasicPlacement->usStartingGridNo = gsSelectedMercGridNo;
450 if( gpSelected->pDetailedPlacement )
451 gpSelected->pDetailedPlacement->sInsertionGridNo = gsSelectedMercGridNo;
452 AddObjectToHead( gsSelectedMercGridNo, CONFIRMMOVE1 );
453 }
454 }
455
ResetAllMercPositions()456 void ResetAllMercPositions()
457 {
458 //Remove all of the alternate placements (editor takes precedence)
459 UseEditorAlternateList();
460 FOR_EACH_SOLDIERINITNODE_SAFE(curr)
461 {
462 gpSelected = curr;
463 RemoveSoldierNodeFromInitList( gpSelected );
464 }
465 //Now, remove any existing original list mercs, then readd them.
466 UseEditorOriginalList();
467 FOR_EACH_SOLDIERINITNODE(curr)
468 {
469 if( curr->pSoldier )
470 {
471 TacticalRemoveSoldier(*curr->pSoldier);
472 curr->pSoldier = NULL;
473 }
474 //usMapIndex = gpSelected->pBasicPlacement->usStartingGridNo;
475 //if( gpSelected->pSoldier )
476 //{
477 // EVENT_SetSoldierPosition(gpSelected->pSoldier, usMapIndex, SSP_NONE);
478 // if( gpSelected->pBasicPlacement->fOnRoof )
479 // SetSoldierHeight( gpSelected->pSoldier, SECOND_LEVEL_Z_OFFSET );
480 // SetMercDirection( gpSelected->pBasicPlacement->bDirection );
481 //}
482 }
483 AddSoldierInitListTeamToWorld(ENEMY_TEAM);
484 AddSoldierInitListTeamToWorld(CREATURE_TEAM);
485 AddSoldierInitListTeamToWorld(MILITIA_TEAM);
486 AddSoldierInitListTeamToWorld(CIV_TEAM);
487 gpSelected = NULL;
488 g_selected_merc = NULL;
489 }
490
AddMercWaypoint(UINT32 iMapIndex)491 void AddMercWaypoint( UINT32 iMapIndex )
492 {
493 INT32 iNum;
494 // index 0 isn't used
495 if ( iActionParam == 0 )
496 return;
497
498 if (g_selected_merc == NULL || g_selected_merc->ubID <= gTacticalStatus.Team[OUR_TEAM].bLastID || g_selected_merc->ubID >= MAXMERCS)
499 return;
500
501 if ( iActionParam > gpSelected->pSoldier->bPatrolCnt )
502 {
503 // Fill up missing waypoints with same value as new one
504 for(iNum = gpSelected->pSoldier->bPatrolCnt + 1; iNum <= iActionParam; iNum++)
505 {
506 gpSelected->pBasicPlacement->sPatrolGrid[iNum] = (INT16)iMapIndex;
507 if( gpSelected->pDetailedPlacement )
508 gpSelected->pDetailedPlacement->sPatrolGrid[iNum] = (INT16)iMapIndex;
509 gpSelected->pSoldier->usPatrolGrid[iNum] = (UINT16)iMapIndex;
510 }
511
512 gpSelected->pBasicPlacement->bPatrolCnt = (INT8)iActionParam;
513 if( gpSelected->pDetailedPlacement )
514 gpSelected->pDetailedPlacement->bPatrolCnt = (INT8)iActionParam;
515 gpSelected->pSoldier->bPatrolCnt = (INT8) iActionParam;
516 gpSelected->pSoldier->bNextPatrolPnt = 1;
517 }
518 else
519 {
520 // Set this way point
521 gpSelected->pBasicPlacement->sPatrolGrid[iActionParam] = (INT16)iMapIndex;
522 if( gpSelected->pDetailedPlacement )
523 gpSelected->pDetailedPlacement->sPatrolGrid[iActionParam] = (INT16)iMapIndex;
524 gpSelected->pSoldier->usPatrolGrid[iActionParam] = (UINT16)iMapIndex;
525 }
526 gfRenderWorld = TRUE;
527 }
528
EraseMercWaypoint()529 void EraseMercWaypoint()
530 {
531 INT32 iNum;
532 // index 0 isn't used
533 if ( iActionParam == 0 )
534 return;
535
536 if (g_selected_merc == NULL || g_selected_merc->ubID <= gTacticalStatus.Team[OUR_TEAM].bLastID || g_selected_merc->ubID >= MAXMERCS)
537 return;
538
539 // Fill up missing areas
540 if ( iActionParam > gpSelected->pSoldier->bPatrolCnt )
541 return;
542
543 for(iNum = iActionParam; iNum < gpSelected->pSoldier->bPatrolCnt; iNum++)
544 {
545 gpSelected->pBasicPlacement->sPatrolGrid[iNum] = gpSelected->pBasicPlacement->sPatrolGrid[iNum+1];
546 if( gpSelected->pDetailedPlacement )
547 gpSelected->pDetailedPlacement->sPatrolGrid[iNum] = gpSelected->pDetailedPlacement->sPatrolGrid[iNum+1];
548 gpSelected->pSoldier->usPatrolGrid[iNum] = gpSelected->pSoldier->usPatrolGrid[iNum+1];
549 }
550
551 gpSelected->pBasicPlacement->bPatrolCnt--;
552 if( gpSelected->pDetailedPlacement )
553 gpSelected->pDetailedPlacement->bPatrolCnt--;
554 gpSelected->pSoldier->bPatrolCnt--;
555 gfRenderWorld = TRUE;
556 }
557
558
559 /* Checks for a soldier at the given map coordinates. If there is one, it
560 * returns it, otherwise it returns NULL. */
IsMercHere(INT32 iMapIndex)561 static SOLDIERTYPE* IsMercHere(INT32 iMapIndex)
562 {
563 FOR_EACH_SOLDIER(s)
564 {
565 if (s->sGridNo == iMapIndex) return s;
566 }
567 return NULL;
568 }
569
570
571 //----------------------------------------------------------------------------------------------
572 //----------------------------------------------------------------------------------------------
573 // The following are the button callback functions for the merc editing pages
574 //----------------------------------------------------------------------------------------------
575 //----------------------------------------------------------------------------------------------
576
577
MercsToggleColorModeCallback(GUI_BUTTON * btn,INT32 reason)578 void MercsToggleColorModeCallback( GUI_BUTTON *btn, INT32 reason )
579 {
580 if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
581 {
582 if (btn->Clicked())
583 {
584 EnableEditorButtons( FIRST_MERCS_COLOR_BUTTON, LAST_MERCS_COLOR_BUTTON );
585 gpSelected->pDetailedPlacement->fVisible = TRUE;
586 gpSelected->pDetailedPlacement->HeadPal = gpSelected->pSoldier->HeadPal;
587 gpSelected->pDetailedPlacement->SkinPal = gpSelected->pSoldier->SkinPal;
588 gpSelected->pDetailedPlacement->VestPal = gpSelected->pSoldier->VestPal;
589 gpSelected->pDetailedPlacement->PantsPal = gpSelected->pSoldier->PantsPal;
590 }
591 else //button is unchecked.
592 {
593 DisableEditorButtons( FIRST_MERCS_COLOR_BUTTON, LAST_MERCS_COLOR_BUTTON );
594 gpSelected->pDetailedPlacement->fVisible = FALSE;
595 }
596 gfRenderMercInfo = TRUE;
597 gfRenderTaskbar = TRUE;
598 }
599 }
600
MercsSetColorsCallback(GUI_BUTTON * btn,INT32 reason)601 void MercsSetColorsCallback( GUI_BUTTON *btn, INT32 reason )
602 {
603 INT32 iBtn;
604 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
605 {
606 iEditWhichStat = -1;
607 for ( iBtn = FIRST_MERCS_COLOR_BUTTON; iBtn <= LAST_MERCS_COLOR_BUTTON; iBtn++ )
608 {
609 if (btn == iEditorButton[iBtn])
610 {
611 iEditWhichStat = iBtn - FIRST_MERCS_COLOR_BUTTON;
612 iEditMercMode = EDIT_MERC_NEXT_COLOR;
613 gfRenderMercInfo = TRUE;
614 return;
615 }
616 }
617 }
618 }
619
620
621 static void ChangeBodyType(INT8 bOffset);
622
623
MercsSetBodyTypeCallback(GUI_BUTTON * btn,INT32 reason)624 void MercsSetBodyTypeCallback( GUI_BUTTON *btn, INT32 reason )
625 {
626 if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
627 {
628 gfRenderMercInfo = TRUE;
629 if (btn == iEditorButton[MERCS_BODYTYPE_DOWN])
630 ChangeBodyType( 1 ); //next
631 else
632 ChangeBodyType( -1 ); //previous
633 }
634 }
635
636
637 static void ShowEditMercColorSet(UINT8 ubPaletteRep, INT16 sSet);
638
639
640 // Displays the palette of the given merc (used by the edit merc color page)
ShowEditMercPalettes(SOLDIERTYPE * pSoldier)641 static void ShowEditMercPalettes(SOLDIERTYPE* pSoldier)
642 {
643 UINT8 ubPaletteRep;
644 if( !pSoldier )
645 ubPaletteRep = 0xff;
646
647 if( pSoldier )
648 {
649 if (pSoldier->HeadPal.empty())
650 ubPaletteRep = 0xff;
651 else
652 ubPaletteRep = GetPaletteRepIndexFromID(pSoldier->HeadPal);
653 }
654 ShowEditMercColorSet( ubPaletteRep, 0 );
655
656 if( pSoldier )
657 {
658 if (pSoldier->SkinPal.empty())
659 ubPaletteRep = 0xff;
660 else
661 ubPaletteRep = GetPaletteRepIndexFromID(pSoldier->SkinPal);
662 }
663 ShowEditMercColorSet( ubPaletteRep, 1 );
664
665 if( pSoldier )
666 {
667 if (pSoldier->VestPal.empty())
668 ubPaletteRep = 0xff;
669 else
670 ubPaletteRep = GetPaletteRepIndexFromID(pSoldier->VestPal);
671 }
672 ShowEditMercColorSet( ubPaletteRep, 2 );
673
674 if( pSoldier )
675 {
676 if (pSoldier->PantsPal.empty())
677 ubPaletteRep = 0xff;
678 else
679 ubPaletteRep = GetPaletteRepIndexFromID(pSoldier->PantsPal);
680 }
681 ShowEditMercColorSet( ubPaletteRep, 3 );
682 }
683
684
685 // Displays a single palette set. (used by ShowEditMercPalettes)
ShowEditMercColorSet(UINT8 ubPaletteRep,INT16 sSet)686 static void ShowEditMercColorSet(UINT8 ubPaletteRep, INT16 sSet)
687 {
688 UINT16 us16BPPColor, usFillColorDark, usFillColorLight;
689 UINT8 cnt1;
690 UINT8 ubSize;
691 INT16 sUnitSize;
692 INT16 sLeft, sTop, sRight, sBottom;
693
694 if( ubPaletteRep == 0xff )
695 ubSize = 16;
696 else
697 ubSize = gpPalRep[ ubPaletteRep ].ubPaletteSize;
698
699 sUnitSize = 128 / (INT16)(ubSize);
700
701 sTop = EDITOR_TASKBAR_POS_Y + 4 + (sSet * 24);
702 sBottom = sTop + 20;
703 sLeft = 230;
704 sRight = 359;
705
706 usFillColorDark = Get16BPPColor(FROMRGB(24, 61, 81));
707 usFillColorLight = Get16BPPColor(FROMRGB(136, 138, 135));
708
709 // Draw color bar window area
710 ColorFillVideoSurfaceArea(FRAME_BUFFER, sLeft, sTop, sRight, sBottom, usFillColorDark );
711 ColorFillVideoSurfaceArea(FRAME_BUFFER, sLeft + 1, sTop + 1, sRight, sBottom, usFillColorLight );
712 InvalidateRegion( sLeft, sTop, sRight, sBottom );
713
714 sTop++;
715 sBottom--;
716 sLeft++;
717 sRight = sLeft + sUnitSize;
718
719 // Draw the color bar
720 for ( cnt1 = 0; cnt1 < ubSize; cnt1++ )
721 {
722 if (cnt1 == (ubSize - 1) )
723 sRight = 358;
724 if( ubPaletteRep == 0xff )
725 us16BPPColor = Get16BPPColor( FROMRGB( (16 - cnt1)*10, (16 - cnt1)*10, (16 - cnt1)*10 ) );
726 else
727 {
728 const SGPPaletteEntry* Clr = &gpPalRep[ubPaletteRep].rgb[cnt1];
729 us16BPPColor = Get16BPPColor(FROMRGB(Clr->r, Clr->g, Clr->b));
730 }
731 ColorFillVideoSurfaceArea( FRAME_BUFFER, sLeft, sTop, sRight, sBottom, us16BPPColor );
732
733 sLeft += sUnitSize;
734 sRight += sUnitSize;
735 }
736 }
737
738
739 //----------------------------------------------------------------------------------------------
740 // DisplayWayPoints
741 //
742 // Displays the way points of the currently selected merc.
743 //
DisplayWayPoints(void)744 void DisplayWayPoints(void)
745 {
746 INT16 sX,sY;
747 INT16 sXMapPos,sYMapPos;
748 INT16 sScreenX,sScreenY;
749 FLOAT ScrnX,ScrnY,dOffsetX,dOffsetY;
750 INT8 bPoint;
751 INT16 sGridNo;
752
753 const SOLDIERTYPE* const pSoldier = g_selected_merc;
754 if (pSoldier == NULL || pSoldier->ubID <= gTacticalStatus.Team[OUR_TEAM].bLastID || pSoldier->ubID >= MAXMERCS)
755 return;
756
757 // point 0 is not used!
758 for ( bPoint = 1; bPoint <= pSoldier->bPatrolCnt; bPoint++ )
759 {
760 // Get the next point
761 sGridNo = (INT16)pSoldier->usPatrolGrid[bPoint];
762
763 // Can we see it?
764 if ( !GridNoOnVisibleWorldTile( sGridNo ) )
765 continue;
766
767 if((sGridNo < 0) || (sGridNo > WORLD_MAX))
768 continue;
769
770 // Convert it's location to screen coordinates
771 ConvertGridNoToXY( sGridNo, &sXMapPos, &sYMapPos );
772
773 dOffsetX = (FLOAT)(sXMapPos * CELL_X_SIZE) - gsRenderCenterX;
774 dOffsetY = (FLOAT)(sYMapPos * CELL_Y_SIZE) - gsRenderCenterY;
775
776 FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &ScrnX, &ScrnY);
777
778 sScreenX = ( g_ui.m_tacticalMapCenterX ) + (INT16)ScrnX;
779 sScreenY = ( g_ui.m_tacticalMapCenterY ) + (INT16)ScrnY;
780
781 // Adjust for tiles height factor!
782 sScreenY -= gpWorldLevelData[sGridNo].sHeight;
783 // Bring it down a touch
784 sScreenY += 5;
785
786 if( sScreenY <= 355 )
787 {
788 // Shown it on screen!
789 UINT8 background;
790 if( pSoldier->bLevel == 1 )
791 {
792 sScreenY -= 68;
793 background = FONT_LTBLUE;
794 }
795 else
796 {
797 background = FONT_LTRED;
798 }
799 SetFontAttributes(TINYFONT1, FONT_WHITE, DEFAULT_SHADOW, background);
800 ST::string buf = ST::format("{}", bPoint);
801 FindFontCenterCoordinates(sScreenX, sScreenY, 1, 1, buf, TINYFONT1, &sX, &sY);
802 MPrint(sX, sY, buf);
803 }
804 }
805 }
806
807
SetMercOrders(INT8 bOrders)808 void SetMercOrders( INT8 bOrders )
809 {
810 gpSelected->pSoldier->bOrders = bOrders;
811 gpSelected->pBasicPlacement->bOrders = bOrders;
812 UnclickEditorButtons( FIRST_MERCS_ORDERS_BUTTON, LAST_MERCS_ORDERS_BUTTON );
813 ClickEditorButton( FIRST_MERCS_ORDERS_BUTTON + bOrders );
814 gbDefaultOrders = bOrders;
815 }
816
SetMercAttitude(INT8 bAttitude)817 void SetMercAttitude( INT8 bAttitude )
818 {
819 gpSelected->pSoldier->bAttitude = bAttitude;
820 gpSelected->pBasicPlacement->bAttitude = bAttitude;
821 UnclickEditorButtons( FIRST_MERCS_ATTITUDE_BUTTON, LAST_MERCS_ATTITUDE_BUTTON );
822 ClickEditorButton( FIRST_MERCS_ATTITUDE_BUTTON + bAttitude );
823 gbDefaultAttitude = bAttitude;
824 }
825
SetMercDirection(INT8 bDirection)826 void SetMercDirection( INT8 bDirection )
827 {
828 UnclickEditorButtons( FIRST_MERCS_DIRECTION_BUTTON, LAST_MERCS_DIRECTION_BUTTON );
829 ClickEditorButton( FIRST_MERCS_DIRECTION_BUTTON + bDirection );
830
831 gbDefaultDirection = bDirection;
832 gpSelected->pBasicPlacement->bDirection = bDirection;
833
834 // ATE: Changed these to call functions....
835 EVENT_SetSoldierDirection( gpSelected->pSoldier, bDirection );
836 EVENT_SetSoldierDesiredDirection( gpSelected->pSoldier, bDirection );
837
838 ConvertAniCodeToAniFrame( gpSelected->pSoldier, 0 );
839 }
840
SetMercRelativeEquipment(INT8 bLevel)841 void SetMercRelativeEquipment( INT8 bLevel )
842 {
843 gpSelected->pBasicPlacement->bRelativeEquipmentLevel = bLevel;
844
845 UnclickEditorButtons( FIRST_MERCS_REL_EQUIPMENT_BUTTON, LAST_MERCS_REL_EQUIPMENT_BUTTON );
846 ClickEditorButton( FIRST_MERCS_REL_EQUIPMENT_BUTTON + bLevel );
847 gbDefaultRelativeEquipmentLevel = bLevel;
848 }
849
SetMercRelativeAttributes(INT8 bLevel)850 void SetMercRelativeAttributes( INT8 bLevel )
851 {
852 gpSelected->pBasicPlacement->bRelativeAttributeLevel = bLevel;
853 //We also have to modify the existing soldier incase the user wishes to enter game.
854 ModifySoldierAttributesWithNewRelativeLevel( gpSelected->pSoldier, bLevel );
855
856 UnclickEditorButtons( FIRST_MERCS_REL_ATTRIBUTE_BUTTON, LAST_MERCS_REL_ATTRIBUTE_BUTTON );
857 ClickEditorButton( FIRST_MERCS_REL_ATTRIBUTE_BUTTON + bLevel );
858 gbDefaultRelativeAttributeLevel = bLevel;
859 }
860
861
862 static void UpdateMercItemSlots(void);
863 static void SetMercEditability(BOOLEAN fEditable);
864
865
IndicateSelectedMerc(INT16 sID)866 void IndicateSelectedMerc( INT16 sID )
867 {
868 SOLDIERINITNODE *prev;
869 INT8 bTeam;
870
871 //If we are trying to select a merc that is already selected, ignore.
872 if( sID >= 0 && sID == gsSelectedMercGridNo )
873 return;
874
875 //first remove the cursor of the previous merc.
876 //NOTE: It doesn't matter what the value is, even if a merc isn't selected.
877 //There is no need to validate the gridNo value, because it is always valid.
878 RemoveAllObjectsOfTypeRange( gsSelectedMercGridNo, CONFIRMMOVE, CONFIRMMOVE );
879
880 //This is very important, because clearing the merc editing mode actually,
881 //updates the edited merc. If this call isn't here, it is possible to update the
882 //newly selected merc with the wrong information.
883 SetMercEditingMode( MERC_NOMODE );
884
885 bTeam = -1;
886
887 //determine selection method
888 switch( sID )
889 {
890 case SELECT_NEXT_MERC:
891 prev = gpSelected;
892 if (g_selected_merc == NULL || !gpSelected)
893 { //if no merc selected, then select the first one in list.
894 gpSelected = gSoldierInitHead;
895 }
896 else
897 { //validate this merc in the list.
898 if( gpSelected->next )
899 { //select the next merc in the list
900 gpSelected = gpSelected->next;
901 }
902 else
903 { //we are at the end of the list, so select the first merc in the list.
904 gpSelected = gSoldierInitHead;
905 }
906 }
907 if( !gpSelected ) //list is empty
908 {
909 SetMercEditability( TRUE );
910 SetMercEditingMode( MERC_TEAMMODE );
911 return;
912 }
913 while( gpSelected != prev )
914 {
915 if( !gpSelected )
916 {
917 gpSelected = gSoldierInitHead;
918 continue;
919 }
920 if( gpSelected->pSoldier && gpSelected->pSoldier->bVisible == 1 )
921 { //we have found a visible soldier, so select him.
922 break;
923 }
924 gpSelected = gpSelected->next;
925 }
926 //we have a valid merc now.
927 break;
928 case SELECT_NO_MERC:
929 SetMercEditability( TRUE );
930 gpSelected = NULL;
931 g_selected_merc = NULL;
932 gsSelectedGridNo = 0;
933 SetMercEditingMode( MERC_TEAMMODE );
934 return; //we already deselected the previous merc.
935 case SELECT_NEXT_ENEMY:
936 bTeam = ENEMY_TEAM;
937 break;
938 case SELECT_NEXT_CREATURE:
939 bTeam = CREATURE_TEAM;
940 break;
941 case SELECT_NEXT_REBEL:
942 bTeam = MILITIA_TEAM;
943 break;
944 case SELECT_NEXT_CIV:
945 bTeam = CIV_TEAM;
946 break;
947 default:
948 //search for the merc with the specific ID.
949 gpSelected = FindSoldierInitNodeWithID( (UINT8)sID );
950 if( !gpSelected )
951 {
952 g_selected_merc = NULL;
953 gsSelectedGridNo = 0;
954 SetMercEditability( TRUE );
955 SetMercEditingMode( MERC_TEAMMODE );
956 return; //Invalid merc ID.
957 }
958 break;
959 }
960 if( bTeam != -1 )
961 { //We are searching for the next occurence of a particular team.
962 prev = gpSelected;
963 if (g_selected_merc == NULL || !gpSelected)
964 { //if no merc selected, then select the first one in list.
965 gpSelected = gSoldierInitHead;
966 }
967 else
968 { //validate this merc in the list.
969 if( gpSelected->next )
970 { //select the next merc in the list
971 gpSelected = gpSelected->next;
972 }
973 else
974 { //we are at the end of the list, so select the first merc in the list.
975 gpSelected = gSoldierInitHead;
976 }
977 }
978 if( !gpSelected ) //list is empty
979 {
980 SetMercEditability( TRUE );
981 SetMercEditingMode( MERC_TEAMMODE );
982 return;
983 }
984 while( gpSelected != prev )
985 {
986 if( !gpSelected )
987 {
988 gpSelected = gSoldierInitHead;
989 continue;
990 }
991 if( gpSelected->pSoldier && gpSelected->pSoldier->bVisible == 1 && gpSelected->pSoldier->bTeam == bTeam )
992 { //we have found a visible soldier on the desired team, so select him.
993 break;
994 }
995 gpSelected = gpSelected->next;
996 }
997 if( !gpSelected )
998 return;
999 if( gpSelected == prev )
1000 { //we have cycled through the list already, so choose the same guy (if he is on the desired team)...
1001 if( !gpSelected->pSoldier || gpSelected->pSoldier->bVisible != 1 || gpSelected->pSoldier->bTeam != bTeam )
1002 {
1003 SetMercEditability( TRUE );
1004 SetMercEditingMode( MERC_TEAMMODE );
1005 return;
1006 }
1007 }
1008 }
1009 //if we made it this far, then we have a new merc cursor indicator to draw.
1010 if( gpSelected->pSoldier )
1011 gsSelectedMercGridNo = gpSelected->pSoldier->sGridNo;
1012 else
1013 {
1014 SetMercEditability( TRUE );
1015 SetMercEditingMode( MERC_TEAMMODE );
1016 return;
1017 }
1018 g_selected_merc = gpSelected->pSoldier;
1019 AddObjectToHead( gsSelectedMercGridNo, CONFIRMMOVE1 );
1020
1021 //If the merc has a valid profile, then turn off editability
1022 SOLDIERCREATE_STRUCT const* const dp = gpSelected->pDetailedPlacement;
1023 SetMercEditability(!dp || dp->ubProfile == NO_PROFILE);
1024
1025 if( sID < 0 )
1026 { //We want to center the screen on the next merc, and update the interface.
1027 gsRenderCenterX = (INT16)gpSelected->pSoldier->dXPos;
1028 gsRenderCenterY = (INT16)gpSelected->pSoldier->dYPos;
1029 gfRenderWorld = TRUE;
1030 }
1031
1032 //update the merc item slots to reflect what the merc currently has.
1033 UpdateMercItemSlots();
1034
1035 //Whatever the case, we want to update the gui to press the appropriate buttons
1036 //depending on the merc's attributes.
1037 //Click the appropriate team button
1038 UnclickEditorButton( MERCS_ENEMY );
1039 UnclickEditorButton( MERCS_CREATURE );
1040 UnclickEditorButton( MERCS_REBEL );
1041 UnclickEditorButton( MERCS_CIVILIAN );
1042 switch( gpSelected->pSoldier->bTeam )
1043 {
1044 case ENEMY_TEAM: ClickEditorButton( MERCS_ENEMY ); iDrawMode = DRAW_MODE_ENEMY; break;
1045 case CREATURE_TEAM: ClickEditorButton( MERCS_CREATURE ); iDrawMode = DRAW_MODE_CREATURE; break;
1046 case MILITIA_TEAM: ClickEditorButton( MERCS_REBEL ); iDrawMode = DRAW_MODE_REBEL; break;
1047 case CIV_TEAM: ClickEditorButton( MERCS_CIVILIAN ); iDrawMode = DRAW_MODE_CIVILIAN; break;
1048 }
1049 //Update the editing mode
1050 if( gpSelected->pDetailedPlacement )
1051 SetMercEditingMode( gubLastDetailedMercMode );
1052 else
1053 SetMercEditingMode( MERC_BASICMODE );
1054 //Determine which team button to press.
1055 gfRenderMercInfo = TRUE;
1056 //These calls will set the proper button states, even though it redundantly
1057 //assigns the soldier with the same orders/attitude.
1058 SetMercOrders( gpSelected->pSoldier->bOrders );
1059 SetMercAttitude( gpSelected->pSoldier->bAttitude );
1060 SetMercDirection( gpSelected->pSoldier->bDirection );
1061 if( gpSelected->pBasicPlacement->fPriorityExistance )
1062 ClickEditorButton( MERCS_PRIORITYEXISTANCE_CHECKBOX );
1063 else
1064 UnclickEditorButton( MERCS_PRIORITYEXISTANCE_CHECKBOX );
1065 if( gpSelected->pBasicPlacement->fHasKeys )
1066 ClickEditorButton( MERCS_HASKEYS_CHECKBOX );
1067 else
1068 UnclickEditorButton( MERCS_HASKEYS_CHECKBOX );
1069 if( gpSelected->pSoldier->ubProfile == NO_PROFILE )
1070 {
1071 SetMercRelativeEquipment( gpSelected->pBasicPlacement->bRelativeEquipmentLevel );
1072 SetMercRelativeAttributes( gpSelected->pBasicPlacement->bRelativeAttributeLevel );
1073 SetEnemyColorCode( gpSelected->pBasicPlacement->ubSoldierClass );
1074 }
1075 if( iDrawMode == DRAW_MODE_CIVILIAN )
1076 {
1077 ChangeCivGroup( gpSelected->pSoldier->ubCivilianGroup );
1078 }
1079 }
1080
DeleteSelectedMerc()1081 void DeleteSelectedMerc()
1082 {
1083 if (g_selected_merc != NULL)
1084 {
1085 RemoveSoldierNodeFromInitList( gpSelected );
1086 gpSelected = NULL;
1087 g_selected_merc = NULL;
1088 gfRenderWorld = TRUE;
1089 if( TextInputMode() )
1090 KillTextInputMode();
1091 IndicateSelectedMerc( SELECT_NO_MERC );
1092 }
1093 }
1094
1095
1096 static ST::string CalcStringForValue(INT32 iValue, UINT32 uiMax);
1097
1098
SetupTextInputForMercProfile(void)1099 static void SetupTextInputForMercProfile(void)
1100 {
1101 ST::string str;
1102 INT16 sNum;
1103
1104 InitTextInputModeWithScheme( DEFAULT_SCHEME );
1105
1106 sNum = gpSelected->pDetailedPlacement->ubProfile;
1107 if( sNum == NO_PROFILE )
1108 str = ST::null;
1109 else
1110 str = CalcStringForValue(gpSelected->pDetailedPlacement->ubProfile, NUM_PROFILES);
1111 AddTextInputField( 200, EDITOR_TASKBAR_POS_Y + 70, 30, 20, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1112 }
1113
1114
SetupTextInputForMercAttributes(void)1115 static void SetupTextInputForMercAttributes(void)
1116 {
1117 ST::string str;
1118
1119 InitTextInputModeWithScheme( DEFAULT_SCHEME );
1120
1121 str = CalcStringForValue(gpSelected->pDetailedPlacement->bExpLevel, 100);
1122 AddTextInputField( 200, EDITOR_TASKBAR_POS_Y + 5, 20, 15, MSYS_PRIORITY_NORMAL, str, 1, INPUTTYPE_NUMERICSTRICT );
1123 str = CalcStringForValue(gpSelected->pDetailedPlacement->bLife, 100);
1124 AddTextInputField( 200, EDITOR_TASKBAR_POS_Y + 30, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1125 str = CalcStringForValue(gpSelected->pDetailedPlacement->bLifeMax, 100);
1126 AddTextInputField( 200, EDITOR_TASKBAR_POS_Y + 55, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1127 str = CalcStringForValue(gpSelected->pDetailedPlacement->bMarksmanship, 100);
1128 AddTextInputField( 200, EDITOR_TASKBAR_POS_Y + 80, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1129 str = CalcStringForValue(gpSelected->pDetailedPlacement->bStrength, 100);
1130 AddTextInputField( 300, EDITOR_TASKBAR_POS_Y + 5, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1131 str = CalcStringForValue(gpSelected->pDetailedPlacement->bAgility, 100);
1132 AddTextInputField( 300, EDITOR_TASKBAR_POS_Y + 30, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1133 str = CalcStringForValue(gpSelected->pDetailedPlacement->bDexterity, 100);
1134 AddTextInputField( 300, EDITOR_TASKBAR_POS_Y + 55, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1135 str = CalcStringForValue(gpSelected->pDetailedPlacement->bWisdom, 100);
1136 AddTextInputField( 300, EDITOR_TASKBAR_POS_Y + 80, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1137 str = CalcStringForValue(gpSelected->pDetailedPlacement->bLeadership, 100);
1138 AddTextInputField( 400, EDITOR_TASKBAR_POS_Y + 5, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1139 str = CalcStringForValue(gpSelected->pDetailedPlacement->bExplosive, 100);
1140 AddTextInputField( 400, EDITOR_TASKBAR_POS_Y + 30, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1141 str = CalcStringForValue(gpSelected->pDetailedPlacement->bMedical, 100);
1142 AddTextInputField( 400, EDITOR_TASKBAR_POS_Y + 55, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1143 str = CalcStringForValue(gpSelected->pDetailedPlacement->bMechanical, 100);
1144 AddTextInputField( 400, EDITOR_TASKBAR_POS_Y + 80, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1145 str = CalcStringForValue(gpSelected->pDetailedPlacement->bMorale, 100);
1146 AddTextInputField( 500, EDITOR_TASKBAR_POS_Y + 5, 20, 15, MSYS_PRIORITY_NORMAL, str, 3, INPUTTYPE_NUMERICSTRICT );
1147
1148 if( !gfCanEditMercs )
1149 DisableAllTextFields();
1150 }
1151
1152
1153 //In the merc editing, all detailed placement values for generated attributes are set to -1.
1154 //When making a generated attribute static, we then set the value to its applicable value.
1155 //This function is similar to the itoa function except that -1 is converted to a null string.
CalcStringForValue(INT32 iValue,UINT32 uiMax)1156 static ST::string CalcStringForValue(INT32 iValue, UINT32 uiMax)
1157 {
1158 if( iValue < 0 ) //a blank string is determined by a negative value.
1159 return ST::null;
1160 else if( (UINT32)iValue > uiMax ) //higher than max attribute value, so convert it to the max.
1161 return ST::format("{}", uiMax);
1162 else //this is a valid static value, so convert it to a string.
1163 return ST::format("{}", iValue);
1164 }
1165
1166
ExtractAndUpdateMercAttributes(void)1167 static void ExtractAndUpdateMercAttributes(void)
1168 {
1169 //If we have just deleted the merc's detailed placement in the editor, we don't
1170 //need to extract the information
1171 if( !gpSelected->pDetailedPlacement )
1172 return;
1173
1174 //It just so happens that GetNumericStrict...() will return -1 for any blank fields.
1175 //-1 values in the detailed placement work nicely, because that signifies that specific
1176 //field isn't static. Any other value becomes static, and static values override any
1177 //generated values.
1178 gpSelected->pDetailedPlacement->bExpLevel = (INT8)MIN( GetNumericStrictValueFromField( 0 ), 100 );
1179 gpSelected->pDetailedPlacement->bLife = (INT8)MIN( GetNumericStrictValueFromField( 1 ), 100 );
1180 gpSelected->pDetailedPlacement->bLifeMax = (INT8)MIN( GetNumericStrictValueFromField( 2 ), 100 );
1181 gpSelected->pDetailedPlacement->bMarksmanship = (INT8)MIN( GetNumericStrictValueFromField( 3 ), 100 );
1182 gpSelected->pDetailedPlacement->bStrength = (INT8)MIN( GetNumericStrictValueFromField( 4 ), 100 );
1183 gpSelected->pDetailedPlacement->bAgility = (INT8)MIN( GetNumericStrictValueFromField( 5 ), 100 );
1184 gpSelected->pDetailedPlacement->bDexterity = (INT8)MIN( GetNumericStrictValueFromField( 6 ), 100 );
1185 gpSelected->pDetailedPlacement->bWisdom = (INT8)MIN( GetNumericStrictValueFromField( 7 ), 100 );
1186 gpSelected->pDetailedPlacement->bLeadership = (INT8)MIN( GetNumericStrictValueFromField( 8 ), 100 );
1187 gpSelected->pDetailedPlacement->bExplosive = (INT8)MIN( GetNumericStrictValueFromField( 9 ), 100 );
1188 gpSelected->pDetailedPlacement->bMedical = (INT8)MIN( GetNumericStrictValueFromField( 10 ), 100 );
1189 gpSelected->pDetailedPlacement->bMechanical = (INT8)MIN( GetNumericStrictValueFromField( 11 ), 100 );
1190 gpSelected->pDetailedPlacement->bMorale = (INT8)MIN( GetNumericStrictValueFromField( 11 ), 100 );
1191
1192 //make sure that experience level ranges between 1 and 9
1193 if( gpSelected->pDetailedPlacement->bExpLevel != -1 )
1194 gpSelected->pDetailedPlacement->bExpLevel = MAX( MIN( gpSelected->pDetailedPlacement->bExpLevel , 9 ), 1 );
1195
1196 //no such thing as a life max of 0
1197 if( !gpSelected->pDetailedPlacement->bLifeMax )
1198 gpSelected->pDetailedPlacement->bLifeMax = 1;
1199
1200 //make sure the life doesn't exceed the maxlife...
1201 if( gpSelected->pDetailedPlacement->bLifeMax != -1 && gpSelected->pDetailedPlacement->bLife != -1 &&
1202 gpSelected->pDetailedPlacement->bLife > gpSelected->pDetailedPlacement->bLifeMax )
1203 {
1204 gpSelected->pDetailedPlacement->bLife = gpSelected->pDetailedPlacement->bLifeMax;
1205 }
1206
1207 //update the soldier
1208 UpdateSoldierWithStaticDetailedInformation( gpSelected->pSoldier, gpSelected->pDetailedPlacement );
1209 }
1210
1211
ExtractAndUpdateMercProfile(void)1212 static void ExtractAndUpdateMercProfile(void)
1213 {
1214 INT16 sNum;
1215 static INT16 sPrev = NO_PROFILE;
1216
1217 //If we have just deleted the merc's detailed placement in the editor, we don't
1218 //need to extract the information
1219 if( !gpSelected->pDetailedPlacement )
1220 return;
1221
1222 //if the string is blank, returning -1, then set the value to NO_PROFILE
1223 //because ubProfile is unsigned.
1224 sNum = (INT16)MIN( GetNumericStrictValueFromField( 0 ), NUM_PROFILES );
1225 if( sNum == -1 )
1226 {
1227 gpSelected->pDetailedPlacement->ubProfile = NO_PROFILE;
1228 gpSelected->pDetailedPlacement->fCopyProfileItemsOver = FALSE;
1229 SetMercEditability( TRUE );
1230 }
1231 else if( sPrev != sNum )
1232 {
1233 gpSelected->pDetailedPlacement->ubProfile = (UINT8)sNum;
1234 gpSelected->pDetailedPlacement->fCopyProfileItemsOver = TRUE;
1235 gpSelected->pBasicPlacement->fPriorityExistance = TRUE;
1236 ClickEditorButton( MERCS_PRIORITYEXISTANCE_CHECKBOX );
1237 SetMercEditability( FALSE );
1238 }
1239 else
1240 return;
1241
1242 UpdateSoldierWithStaticDetailedInformation( gpSelected->pSoldier, gpSelected->pDetailedPlacement );
1243 if( gpSelected->pSoldier->bTeam == CIV_TEAM )
1244 {
1245 ChangeCivGroup( gpSelected->pSoldier->ubCivilianGroup );
1246 }
1247 }
1248
1249
SetupTextInputForMercSchedule(void)1250 static void SetupTextInputForMercSchedule(void)
1251 {
1252 InitTextInputModeWithScheme( DEFAULT_SCHEME );
1253 AddUserInputField( NULL );
1254 AddTextInputField(268, EDITOR_TASKBAR_POS_Y + 13, 36, 16, MSYS_PRIORITY_NORMAL, ST::null, 6, INPUTTYPE_24HOURCLOCK);
1255 SetExclusive24HourTimeValue( 1, gCurrSchedule.usTime[0] );
1256 AddTextInputField(268, EDITOR_TASKBAR_POS_Y + 34, 36, 16, MSYS_PRIORITY_NORMAL, ST::null, 6, INPUTTYPE_24HOURCLOCK);
1257 SetExclusive24HourTimeValue( 2, gCurrSchedule.usTime[1] );
1258 AddTextInputField(268, EDITOR_TASKBAR_POS_Y + 55, 36, 16, MSYS_PRIORITY_NORMAL, ST::null, 6, INPUTTYPE_24HOURCLOCK);
1259 SetExclusive24HourTimeValue( 3, gCurrSchedule.usTime[2] );
1260 AddTextInputField(268, EDITOR_TASKBAR_POS_Y + 76, 36, 16, MSYS_PRIORITY_NORMAL, ST::null, 6, INPUTTYPE_24HOURCLOCK);
1261 SetExclusive24HourTimeValue( 4, gCurrSchedule.usTime[3] );
1262 }
1263
1264
1265 static void UpdateScheduleInfo(void);
1266
1267
ExtractAndUpdateMercSchedule()1268 void ExtractAndUpdateMercSchedule()
1269 {
1270 INT32 i;
1271 BOOLEAN fValidSchedule = FALSE;
1272 BOOLEAN fScheduleNeedsUpdate = FALSE;
1273 SCHEDULENODE *pNext = NULL;
1274 if( !gpSelected )
1275 return;
1276 //extract all of the fields into a temp schedulenode.
1277 //gScheduleNode = SCHEDULENODE{};
1278 for( i = 0; i < 4; i++ )
1279 {
1280 gCurrSchedule.usTime[i] = GetExclusive24HourTimeValueFromField( (UINT8)(i+1) );
1281 gCurrSchedule.ubAction[i] = iEditorButton[MERCS_SCHEDULE_ACTION1 + i]->GetUserData();
1282 if( gCurrSchedule.ubAction[i] )
1283 fValidSchedule = TRUE;
1284 }
1285
1286 if( !gpSelected->pSoldier->ubScheduleID )
1287 { //The soldier doesn't actually have a schedule yet, so create one if necessary (not blank)
1288 if( fValidSchedule )
1289 { //create a new schedule
1290 if( SortSchedule( &gCurrSchedule ) )
1291 fScheduleNeedsUpdate = TRUE;
1292 CopyScheduleToList( &gCurrSchedule, gpSelected );
1293 ShowEditorButton( MERCS_GLOWSCHEDULE );
1294 HideEditorButton( MERCS_SCHEDULE );
1295 }
1296 }
1297 else
1298 {
1299 SCHEDULENODE *pSchedule;
1300 pSchedule = GetSchedule( gpSelected->pSoldier->ubScheduleID );
1301 if( !pSchedule )
1302 {
1303 gpSelected->pSoldier->ubScheduleID = 0;
1304 gpSelected->pDetailedPlacement->ubScheduleID = 0;
1305 HideEditorButton( MERCS_GLOWSCHEDULE );
1306 ShowEditorButton( MERCS_SCHEDULE );
1307 return;
1308 }
1309 if( fValidSchedule )
1310 { //overwrite the existing schedule with the new one.
1311 gCurrSchedule.ubScheduleID = gpSelected->pSoldier->ubScheduleID;
1312 if( SortSchedule( &gCurrSchedule ) )
1313 fScheduleNeedsUpdate = TRUE;
1314 pNext = pSchedule->next;
1315 *pSchedule = gCurrSchedule;
1316 pSchedule->next = pNext;
1317 }
1318 else
1319 { //remove the existing schedule, as the new one is blank.
1320 DeleteSchedule( pSchedule->ubScheduleID );
1321 gpSelected->pSoldier->ubScheduleID = 0;
1322 gpSelected->pDetailedPlacement->ubScheduleID = 0;
1323 HideEditorButton( MERCS_GLOWSCHEDULE );
1324 ShowEditorButton( MERCS_SCHEDULE );
1325 }
1326 }
1327 if( fScheduleNeedsUpdate )
1328 { //The schedule was sorted, so update the gui.
1329 UpdateScheduleInfo();
1330 }
1331 SetActiveField( 0 );
1332 }
1333
ExtractCurrentMercModeInfo(BOOLEAN fKillTextInputMode)1334 void ExtractCurrentMercModeInfo( BOOLEAN fKillTextInputMode )
1335 {
1336 //This happens if we deleted a merc
1337 if (g_selected_merc == NULL) return;
1338 //Extract and update mercs via text fields if applicable
1339 switch( gubCurrMercMode )
1340 {
1341 case MERC_ATTRIBUTEMODE:
1342 ExtractAndUpdateMercAttributes();
1343 break;
1344 case MERC_PROFILEMODE:
1345 ExtractAndUpdateMercProfile();
1346 break;
1347 case MERC_SCHEDULEMODE:
1348 ExtractAndUpdateMercSchedule();
1349 break;
1350 default:
1351 fKillTextInputMode = FALSE;
1352 break;
1353 }
1354 if( fKillTextInputMode )
1355 KillTextInputMode();
1356 }
1357
InitDetailedPlacementForMerc()1358 void InitDetailedPlacementForMerc()
1359 {
1360 Assert( !gpSelected->pDetailedPlacement );
1361
1362 gpSelected->pDetailedPlacement = new SOLDIERCREATE_STRUCT{};
1363 gpSelected->pBasicPlacement->fDetailedPlacement = TRUE;
1364 gpSelected->pBasicPlacement->fPriorityExistance = FALSE;
1365 CreateStaticDetailedPlacementGivenBasicPlacementInfo( gpSelected->pDetailedPlacement, gpSelected->pBasicPlacement );
1366
1367 ClearCurrentSchedule();
1368
1369 //update the soldier
1370 UpdateSoldierWithStaticDetailedInformation ( gpSelected->pSoldier, gpSelected->pDetailedPlacement );
1371 }
1372
KillDetailedPlacementForMerc()1373 void KillDetailedPlacementForMerc()
1374 {
1375 Assert( gpSelected->pDetailedPlacement );
1376 delete gpSelected->pDetailedPlacement;
1377 gpSelected->pDetailedPlacement = NULL;
1378 gpSelected->pBasicPlacement->fDetailedPlacement = FALSE;
1379 SetMercEditability( TRUE );
1380 }
1381
1382
ChangeBodyType(INT8 const offset)1383 static void ChangeBodyType(INT8 const offset)
1384 {
1385 Assert(offset == -1 || offset == 1); // only +/-1 allowed
1386
1387 gfRenderTaskbar = TRUE;
1388 gfRenderMercInfo = TRUE;
1389
1390 SOLDIERINITNODE const& sel = *gpSelected;
1391 BASIC_SOLDIERCREATE_STRUCT& bp = *sel.pBasicPlacement;
1392 // Select next body type depending on offset
1393 SoldierBodyType const* body_types; // HACK000E
1394 INT32 n; // HACK000E
1395 switch (bp.bTeam)
1396 {
1397 case ENEMY_TEAM: body_types = bEnemyArray; n = lengthof(bEnemyArray); break;
1398 case CREATURE_TEAM: body_types = bCreatureArray; n = lengthof(bCreatureArray); break;
1399 case MILITIA_TEAM: body_types = bRebelArray; n = lengthof(bRebelArray); break;
1400 case CIV_TEAM: body_types = bCivArray; n = lengthof(bCivArray); break;
1401 default: abort(); // HACK000E
1402 }
1403 INT32 next = 0; // XXX HACK000E
1404 for (INT32 i = 0; i != n; ++i)
1405 {
1406 if (body_types[i] != bp.bBodyType) continue;
1407 next = i + offset;
1408 if (next >= n) next = 0;
1409 else if (next < 0) next = n - 1;
1410 break;
1411 }
1412 SoldierBodyType const body_type = body_types[next];
1413
1414 SOLDIERTYPE& s = *sel.pSoldier;
1415 // Set the new bodytype into the and update the soldier info
1416 if (body_type != BODY_RANDOM)
1417 {
1418 s.ubBodyType = body_type;
1419 // Set the flags based on the bodytype
1420 s.uiStatusFlags &= ~(SOLDIER_VEHICLE | SOLDIER_ROBOT | SOLDIER_ANIMAL | SOLDIER_MONSTER);
1421 switch (body_type)
1422 {
1423 case ADULTFEMALEMONSTER:
1424 case AM_MONSTER:
1425 case YAF_MONSTER:
1426 case YAM_MONSTER:
1427 case LARVAE_MONSTER:
1428 case INFANT_MONSTER:
1429 case QUEENMONSTER: s.uiStatusFlags |= SOLDIER_MONSTER; break;
1430
1431 case BLOODCAT:
1432 case COW:
1433 case CROW: s.uiStatusFlags |= SOLDIER_ANIMAL; break;
1434
1435 case ROBOTNOWEAPON: s.uiStatusFlags |= SOLDIER_ROBOT; break;
1436
1437 case HUMVEE:
1438 case ELDORADO:
1439 case ICECREAMTRUCK:
1440 case JEEP:
1441 case TANK_NW:
1442 case TANK_NE: s.uiStatusFlags |= SOLDIER_VEHICLE; break;
1443 default:
1444 break;
1445 }
1446 SetSoldierAnimationSurface(&s, s.usAnimState);
1447 }
1448 // Update the placement's info as well.
1449 bp.bBodyType = body_type;
1450 if (SOLDIERCREATE_STRUCT* const dp = sel.pDetailedPlacement) dp->bBodyType = body_type;
1451 if (s.bTeam == CREATURE_TEAM)
1452 {
1453 gbCurrCreature = body_type;
1454 AssignCreatureInventory(&s);
1455 }
1456 CreateSoldierPalettes(s);
1457 }
1458
1459
SetMercEditability(BOOLEAN fEditable)1460 static void SetMercEditability(BOOLEAN fEditable)
1461 {
1462 gfRenderMercInfo = TRUE;
1463 if( fEditable == gfCanEditMercs )
1464 return;
1465 gfCanEditMercs = fEditable;
1466 if( gfCanEditMercs )
1467 { //enable buttons to allow editing
1468 EnableEditorButtons( MERCS_EQUIPMENT_BAD, MERCS_ATTRIBUTES_GREAT );
1469 EnableEditorButtons( FIRST_MERCS_COLORMODE_BUTTON, LAST_MERCS_COLORMODE_BUTTON );
1470 if( gpSelected && gpSelected->pDetailedPlacement && !gpSelected->pDetailedPlacement->fVisible )
1471 UnclickEditorButton( MERCS_TOGGLECOLOR_BUTTON );
1472 EnableEditorButton( MERCS_PRIORITYEXISTANCE_CHECKBOX );
1473 EnableEditorButton( MERCS_CIVILIAN_GROUP );
1474 }
1475 else
1476 { //disable buttons to prevent editing
1477 DisableEditorButtons( MERCS_EQUIPMENT_BAD, MERCS_ATTRIBUTES_GREAT );
1478 DisableEditorButtons( FIRST_MERCS_COLORMODE_BUTTON, LAST_MERCS_COLORMODE_BUTTON );
1479 ClickEditorButton( MERCS_TOGGLECOLOR_BUTTON );
1480 DisableEditorButton( MERCS_PRIORITYEXISTANCE_CHECKBOX );
1481 DisableEditorButton( MERCS_CIVILIAN_GROUP );
1482 }
1483 }
1484
1485
1486 //There are 4 exclusive entry points in a map. Only one of each type can exist on a
1487 //map, and these points are used to validate the map by attempting to connect the four
1488 //points together. If one of the points is isolated, then the map will be rejected. It
1489 //isn't necessary to specify all four points. You wouldn't want to specify a north point if
1490 //there isn't going to be any traversing to adjacent maps from that side.
SpecifyEntryPoint(UINT32 iMapIndex)1491 void SpecifyEntryPoint( UINT32 iMapIndex )
1492 {
1493 INT16 *psEntryGridNo;
1494 BOOLEAN fErasing = FALSE;
1495 if( iDrawMode >= DRAW_MODE_ERASE )
1496 {
1497 iDrawMode -= DRAW_MODE_ERASE;
1498 fErasing = TRUE;
1499 }
1500 switch( iDrawMode )
1501 {
1502 case DRAW_MODE_NORTHPOINT: psEntryGridNo = &gMapInformation.sNorthGridNo; break;
1503 case DRAW_MODE_WESTPOINT: psEntryGridNo = &gMapInformation.sWestGridNo; break;
1504 case DRAW_MODE_EASTPOINT: psEntryGridNo = &gMapInformation.sEastGridNo; break;
1505 case DRAW_MODE_SOUTHPOINT: psEntryGridNo = &gMapInformation.sSouthGridNo; break;
1506 case DRAW_MODE_CENTERPOINT: psEntryGridNo = &gMapInformation.sCenterGridNo; break;
1507 case DRAW_MODE_ISOLATEDPOINT: psEntryGridNo = &gMapInformation.sIsolatedGridNo; break;
1508 default: return;
1509 }
1510 if( !fErasing )
1511 {
1512 if( *psEntryGridNo >= 0 )
1513 {
1514 AddToUndoList( *psEntryGridNo );
1515 RemoveAllTopmostsOfTypeRange( *psEntryGridNo, FIRSTPOINTERS, FIRSTPOINTERS );
1516 }
1517 *psEntryGridNo = (UINT16)iMapIndex;
1518 ValidateEntryPointGridNo( psEntryGridNo );
1519 AddToUndoList( *psEntryGridNo );
1520 AddTopmostToTail( *psEntryGridNo, FIRSTPOINTERS2 );
1521 }
1522 else
1523 {
1524 if (FindTypeInTopmostLayer(iMapIndex, FIRSTPOINTERS))
1525 {
1526 AddToUndoList( iMapIndex );
1527 RemoveAllTopmostsOfTypeRange( iMapIndex, FIRSTPOINTERS, FIRSTPOINTERS );
1528 *psEntryGridNo = -1;
1529 }
1530 //restore the drawmode
1531 iDrawMode += DRAW_MODE_ERASE;
1532 }
1533 }
1534
1535
1536 static void AddNewItemToSelectedMercsInventory(BOOLEAN fCreate);
1537 static void DetermineScheduleEditability(void);
1538
1539
SetMercEditingMode(UINT8 ubNewMode)1540 void SetMercEditingMode( UINT8 ubNewMode )
1541 {
1542 //We need to update the taskbar for the buttons that were erased.
1543 gfRenderTaskbar = TRUE;
1544
1545 //set up the new mode values.
1546 if( gubCurrMercMode >= MERC_GENERALMODE )
1547 gubLastDetailedMercMode = gubCurrMercMode;
1548
1549 //Depending on the mode we were just in, we may want to extract and update the
1550 //merc first. Then we change modes...
1551 ExtractCurrentMercModeInfo( TRUE );
1552
1553 //Change modes now.
1554 gubPrevMercMode = gubCurrMercMode;
1555 gubCurrMercMode = ubNewMode;
1556
1557 //Hide all of the merc buttons except the team buttons which are static.
1558 HideEditorButtons( FIRST_MERCS_BASICMODE_BUTTON, LAST_MERCS_BUTTON );
1559
1560 switch( gubPrevMercMode )
1561 {
1562 case MERC_GETITEMMODE:
1563 EnableEditorButtons( TAB_TERRAIN, TAB_OPTIONS );
1564 HideEditorButtons( FIRST_MERCS_GETITEM_BUTTON, LAST_MERCS_GETITEM_BUTTON );
1565 AddNewItemToSelectedMercsInventory( TRUE );
1566 break;
1567 case MERC_INVENTORYMODE:
1568 HideItemStatsPanel();
1569 DisableEditorRegion( MERC_REGION_ID );
1570 break;
1571 case MERC_GENERALMODE:
1572 EnableEditorButton( MERCS_APPEARANCE );
1573 break;
1574 case MERC_SCHEDULEMODE:
1575 //ClearCurrentSchedule();
1576 break;
1577 }
1578
1579 //If we leave the merc tab, then we want to update editable fields such as
1580 //attributes, which was just handled above, then turn everything off, and exit.
1581 if( ubNewMode == MERC_NOMODE )
1582 {
1583 HideEditorButtons( FIRST_MERCS_BUTTON, LAST_MERCS_TEAMMODE_BUTTON );
1584 HideEditorButtons( MERCS_SCHEDULE, MERCS_GLOWSCHEDULE );
1585 return;
1586 }
1587 if( gubPrevMercMode == MERC_NOMODE || gubPrevMercMode == MERC_GETITEMMODE )
1588 {
1589 ShowEditorButtons( FIRST_MERCS_BUTTON, LAST_MERCS_TEAMMODE_BUTTON );
1590 }
1591
1592 //Release the currently selected merc if you just selected a new team.
1593 if (g_selected_merc != NULL && ubNewMode == MERC_TEAMMODE)
1594 {
1595 //attempt to weed out conditions where we select a team that matches the currently
1596 //selected merc. We don't want to deselect him in this case.
1597 if( (gpSelected->pSoldier->bTeam == ENEMY_TEAM && iDrawMode == DRAW_MODE_ENEMY) ||
1598 (gpSelected->pSoldier->bTeam == CREATURE_TEAM && iDrawMode == DRAW_MODE_CREATURE) ||
1599 (gpSelected->pSoldier->bTeam == MILITIA_TEAM && iDrawMode == DRAW_MODE_REBEL) ||
1600 (gpSelected->pSoldier->bTeam == CIV_TEAM && iDrawMode == DRAW_MODE_CIVILIAN) )
1601 { //Same team, so don't deselect merc. Instead, keep the previous editing mode
1602 //because we are still editing this merc.
1603 gubCurrMercMode = gubPrevMercMode;
1604 //if we don't have a detailed placement, auto set to basic mode.
1605 if( !gpSelected->pDetailedPlacement )
1606 gubCurrMercMode = MERC_BASICMODE;
1607 }
1608 else
1609 {
1610 //Different teams, so deselect the current merc and the detailed checkbox if applicable.
1611 IndicateSelectedMerc( SELECT_NO_MERC );
1612 ShowEditorButtons( FIRST_MERCS_BUTTON, LAST_MERCS_TEAMMODE_BUTTON );
1613 UnclickEditorButton( MERCS_DETAILEDCHECKBOX );
1614 }
1615 }
1616
1617 ShowButton( iEditorButton[ MERCS_NEXT ] );
1618 if (g_selected_merc != NULL) ShowButton(iEditorButton[MERCS_DELETE]);
1619
1620 if( gubCurrMercMode > MERC_TEAMMODE )
1621 { //Add the basic buttons if applicable.
1622 ShowEditorButtons( FIRST_MERCS_BASICMODE_BUTTON, LAST_MERCS_BASICMODE_BUTTON );
1623 }
1624 if( gubCurrMercMode > MERC_BASICMODE )
1625 { //Add the detailed buttons if applicable.
1626 ClickEditorButton( MERCS_DETAILEDCHECKBOX );
1627 ShowEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1628 }
1629 else
1630 UnclickEditorButton( MERCS_DETAILEDCHECKBOX );
1631 //Now we are setting up the button states for the new mode, as well as show the
1632 //applicable buttons for the detailed placement modes.
1633 if( (gubCurrMercMode == MERC_APPEARANCEMODE && iDrawMode == DRAW_MODE_CREATURE) ||
1634 (gubCurrMercMode == MERC_SCHEDULEMODE && iDrawMode != DRAW_MODE_CIVILIAN) )
1635 {
1636 gubCurrMercMode = MERC_GENERALMODE;
1637 }
1638 switch( gubCurrMercMode )
1639 {
1640 case MERC_GETITEMMODE:
1641 DisableEditorButtons( TAB_TERRAIN, TAB_OPTIONS );
1642 EnableEditorButton( TAB_MERCS );
1643 HideEditorButtons( FIRST_MERCS_BUTTON, LAST_MERCS_TEAMMODE_BUTTON );
1644 HideEditorButtons( MERCS_SCHEDULE, MERCS_GLOWSCHEDULE );
1645 ShowEditorButtons( FIRST_MERCS_GETITEM_BUTTON, LAST_MERCS_GETITEM_BUTTON );
1646 InitEditorItemsInfo( eInfo.uiItemType );
1647 ClickEditorButton( ITEMS_WEAPONS + eInfo.uiItemType - TBAR_MODE_ITEM_WEAPONS );
1648 break;
1649 case MERC_INVENTORYMODE:
1650 UpdateMercItemSlots();
1651 ShowItemStatsPanel();
1652 if( gbCurrSelect == -1 )
1653 SpecifyItemToEdit( NULL, -1 );
1654 else
1655 SpecifyItemToEdit( gpMercSlotItem[ gbCurrSelect ], -1 );
1656 HideEditorButtons( MERCS_DELETE, MERCS_NEXT );
1657 ShowEditorButtons( FIRST_MERCS_INVENTORY_BUTTON, LAST_MERCS_INVENTORY_BUTTON );
1658 EnableEditorRegion( MERC_REGION_ID );
1659 UnclickEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1660 ClickEditorButton( MERCS_INVENTORY );
1661 break;
1662 case MERC_BASICMODE:
1663 ShowEditorButtons( FIRST_MERCS_GENERAL_BUTTON, LAST_MERCS_GENERAL_BUTTON );
1664 if( iDrawMode == DRAW_MODE_CREATURE )
1665 { //Set up alternate general mode. This one doesn't allow you to specify relative attributes
1666 //but requires you to specify a body type.
1667 HideEditorButtons( FIRST_MERCS_REL_EQUIPMENT_BUTTON, LAST_MERCS_REL_EQUIPMENT_BUTTON );
1668 ShowEditorButtons( FIRST_MERCS_BODYTYPE_BUTTON, LAST_MERCS_BODYTYPE_BUTTON );
1669 }
1670 if( iDrawMode != DRAW_MODE_ENEMY )
1671 HideEditorButtons( FIRST_MERCS_COLORCODE_BUTTON, LAST_MERCS_COLORCODE_BUTTON );
1672 if( iDrawMode == DRAW_MODE_CIVILIAN )
1673 ShowEditorButton( MERCS_CIVILIAN_GROUP );
1674 break;
1675 case MERC_GENERALMODE:
1676 ShowEditorButtons( FIRST_MERCS_GENERAL_BUTTON, LAST_MERCS_GENERAL_BUTTON );
1677 UnclickEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1678 ClickEditorButton( MERCS_GENERAL );
1679 if( iDrawMode == DRAW_MODE_CREATURE )
1680 { //Set up alternate general mode. This one doesn't allow you to specify relative equipment
1681 //but requires you to specify a body type.
1682 HideEditorButtons( FIRST_MERCS_REL_EQUIPMENT_BUTTON, LAST_MERCS_REL_EQUIPMENT_BUTTON );
1683 ShowEditorButtons( FIRST_MERCS_BODYTYPE_BUTTON, LAST_MERCS_BODYTYPE_BUTTON );
1684 DisableEditorButton( MERCS_APPEARANCE );
1685 }
1686 if( iDrawMode != DRAW_MODE_ENEMY )
1687 HideEditorButtons( FIRST_MERCS_COLORCODE_BUTTON, LAST_MERCS_COLORCODE_BUTTON );
1688 if( iDrawMode == DRAW_MODE_CIVILIAN )
1689 ShowEditorButton( MERCS_CIVILIAN_GROUP );
1690 break;
1691 case MERC_ATTRIBUTEMODE:
1692 UnclickEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1693 ClickEditorButton( MERCS_ATTRIBUTES );
1694 SetupTextInputForMercAttributes();
1695 break;
1696 case MERC_APPEARANCEMODE:
1697 ShowEditorButtons( FIRST_MERCS_COLORMODE_BUTTON, LAST_MERCS_COLORMODE_BUTTON );
1698 UnclickEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1699 ClickEditorButton( MERCS_APPEARANCE );
1700 if( gfCanEditMercs && gpSelected && gpSelected->pDetailedPlacement )
1701 {
1702 if( !gpSelected->pDetailedPlacement->fVisible )
1703 {
1704 UnclickEditorButton( MERCS_TOGGLECOLOR_BUTTON );
1705 DisableEditorButtons( FIRST_MERCS_COLOR_BUTTON, LAST_MERCS_COLOR_BUTTON );
1706 }
1707 else
1708 {
1709 ClickEditorButton( MERCS_TOGGLECOLOR_BUTTON );
1710 EnableEditorButtons( FIRST_MERCS_COLOR_BUTTON, LAST_MERCS_COLOR_BUTTON );
1711 }
1712 }
1713 break;
1714 case MERC_PROFILEMODE:
1715 UnclickEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1716 ClickEditorButton( MERCS_PROFILE );
1717 SetupTextInputForMercProfile();
1718 break;
1719 case MERC_SCHEDULEMODE:
1720 ShowEditorButtons( MERCS_SCHEDULE_ACTION1, MERCS_SCHEDULE_VARIANCE4 );
1721 ShowEditorButton( MERCS_SCHEDULE_CLEAR );
1722 UnclickEditorButtons( FIRST_MERCS_PRIORITYMODE_BUTTON, LAST_MERCS_PRIORITYMODE_BUTTON );
1723 ClickEditorButton( MERCS_SCHEDULE );
1724 SetupTextInputForMercSchedule();
1725 UpdateScheduleInfo();
1726 DetermineScheduleEditability();
1727 }
1728 //Show or hide the schedule buttons
1729 if( gpSelected && gubCurrMercMode != MERC_GETITEMMODE )
1730 {
1731 if( gpSelected->pDetailedPlacement && gpSelected->pDetailedPlacement->ubScheduleID )
1732 {
1733 HideEditorButton( MERCS_SCHEDULE );
1734 ShowEditorButton( MERCS_GLOWSCHEDULE );
1735 }
1736 else
1737 {
1738 HideEditorButton( MERCS_GLOWSCHEDULE );
1739 if( gpSelected->pDetailedPlacement )
1740 {
1741 ShowEditorButton( MERCS_SCHEDULE );
1742 if( gpSelected->pSoldier->bTeam == CIV_TEAM )
1743 EnableEditorButton( MERCS_SCHEDULE );
1744 else
1745 DisableEditorButton( MERCS_SCHEDULE );
1746 }
1747 else
1748 {
1749 HideEditorButton( MERCS_SCHEDULE );
1750 }
1751 }
1752 }
1753 else
1754 {
1755 HideEditorButtons( MERCS_SCHEDULE, MERCS_GLOWSCHEDULE );
1756 }
1757 }
1758
1759
DisplayBodyTypeInfo(void)1760 static void DisplayBodyTypeInfo(void)
1761 {
1762 ST::string str;
1763 switch( gpSelected->pBasicPlacement->bBodyType )
1764 {
1765 case BODY_RANDOM: str = "Random"; break;
1766 case REGMALE: str = "Reg Male"; break;
1767 case BIGMALE: str = "Big Male"; break;
1768 case STOCKYMALE: str = "Stocky Male"; break;
1769 case REGFEMALE: str = "Reg Female"; break;
1770 case TANK_NE: str = "NE Tank"; break;
1771 case TANK_NW: str = "NW Tank"; break;
1772 case FATCIV: str = "Fat Civilian"; break;
1773 case MANCIV: str = "M Civilian"; break;
1774 case MINICIV: str = "Miniskirt"; break;
1775 case DRESSCIV: str = "F Civilian"; break;
1776 case HATKIDCIV: str = "Kid w/ Hat"; break;
1777 case HUMVEE: str = "Humvee"; break;
1778 case ELDORADO: str = "Eldorado"; break;
1779 case ICECREAMTRUCK: str = "Icecream Truck"; break;
1780 case JEEP: str = "Jeep"; break;
1781 case KIDCIV: str = "Kid Civilian"; break;
1782 case COW: str = "Domestic Cow"; break;
1783 case CRIPPLECIV: str = "Cripple"; break;
1784 case ROBOTNOWEAPON: str = "Unarmed Robot"; break;
1785 case LARVAE_MONSTER: str = "Larvae"; break;
1786 case INFANT_MONSTER: str = "Infant"; break;
1787 case YAF_MONSTER: str = "Yng F Monster"; break;
1788 case YAM_MONSTER: str = "Yng M Monster"; break;
1789 case ADULTFEMALEMONSTER: str = "Adt F Monster"; break;
1790 case AM_MONSTER: str = "Adt M Monster"; break;
1791 case QUEENMONSTER: str = "Queen Monster"; break;
1792 case BLOODCAT: str = "Bloodcat"; break;
1793 }
1794 DrawEditorInfoBox(str, FONT10ARIAL, 490, 4, 70, 20);
1795 }
1796
1797
1798 static void RenderMercInventoryPanel(void);
1799
1800
UpdateMercsInfo()1801 void UpdateMercsInfo()
1802 {
1803 if( !gfRenderMercInfo )
1804 return;
1805
1806 //We are rendering it now, so signify that it has been done, so
1807 //it doesn't get rendered every frame.
1808 gfRenderMercInfo = FALSE;
1809
1810 switch( gubCurrMercMode )
1811 {
1812 case MERC_GETITEMMODE:
1813 RenderEditorItemsInfo();
1814 break;
1815 case MERC_INVENTORYMODE:
1816 if( gfMercGetItem )
1817 SetMercEditingMode( MERC_GETITEMMODE );
1818 else
1819 RenderMercInventoryPanel();
1820 break;
1821 case MERC_BASICMODE:
1822 case MERC_GENERALMODE:
1823 BltVideoObject(FRAME_BUFFER, guiExclamation, 0, 188, EDITOR_TASKBAR_POS_Y + 3);
1824 BltVideoObject(FRAME_BUFFER, guiKeyImage, 0, 186, EDITOR_TASKBAR_POS_Y + 28);
1825 SetFontAttributes(SMALLCOMPFONT, FONT_YELLOW);
1826 MPrint(240, EDITOR_TASKBAR_POS_Y + 3, " --=ORDERS=-- ");
1827 MPrint(240, EDITOR_TASKBAR_POS_Y + 59, "--=ATTITUDE=--");
1828 if( iDrawMode == DRAW_MODE_CREATURE )
1829 {
1830 DisplayBodyTypeInfo();
1831 SetFont( SMALLCOMPFONT );
1832 SetFontForeground( FONT_LTBLUE );
1833 MPrint(493, EDITOR_TASKBAR_POS_Y + 56, "RELATIVE");
1834 MPrint(480, EDITOR_TASKBAR_POS_Y + 62, "ATTRIBUTES");
1835 }
1836 else
1837 {
1838 SetFontForeground( FONT_LTGREEN );
1839 MPrint(480, EDITOR_TASKBAR_POS_Y + 3, "RELATIVE");
1840 MPrint(480, EDITOR_TASKBAR_POS_Y + 11, "EQUIPMENT");
1841 SetFontForeground( FONT_LTBLUE );
1842 MPrint(530, EDITOR_TASKBAR_POS_Y + 3, "RELATIVE");
1843 MPrint(530, EDITOR_TASKBAR_POS_Y + 11, "ATTRIBUTES");
1844 }
1845 if( iDrawMode == DRAW_MODE_ENEMY )
1846 {
1847 SetFont( FONT10ARIAL );
1848 SetFontForeground( FONT_YELLOW );
1849 MPrint(590, EDITOR_TASKBAR_POS_Y + 51, "Army");
1850 MPrint(590, EDITOR_TASKBAR_POS_Y + 65, "Admin");
1851 MPrint(590, EDITOR_TASKBAR_POS_Y + 79, "Elite");
1852 }
1853 break;
1854 case MERC_ATTRIBUTEMODE:
1855 SetFontAttributes(FONT10ARIAL, FONT_YELLOW);
1856 MPrint(225, EDITOR_TASKBAR_POS_Y + 10, "Exp. Level");
1857 MPrint(225, EDITOR_TASKBAR_POS_Y + 35, "Life");
1858 MPrint(225, EDITOR_TASKBAR_POS_Y + 60, "LifeMax");
1859 MPrint(225, EDITOR_TASKBAR_POS_Y + 85, "Marksmanship");
1860 MPrint(325, EDITOR_TASKBAR_POS_Y + 10, "Strength");
1861 MPrint(325, EDITOR_TASKBAR_POS_Y + 35, "Agility");
1862 MPrint(325, EDITOR_TASKBAR_POS_Y + 60, "Dexterity");
1863 MPrint(325, EDITOR_TASKBAR_POS_Y + 85, "Wisdom");
1864 MPrint(425, EDITOR_TASKBAR_POS_Y + 10, "Leadership");
1865 MPrint(425, EDITOR_TASKBAR_POS_Y + 35, "Explosives");
1866 MPrint(425, EDITOR_TASKBAR_POS_Y + 60, "Medical");
1867 MPrint(425, EDITOR_TASKBAR_POS_Y + 85, "Mechanical");
1868 MPrint(525, EDITOR_TASKBAR_POS_Y + 10, "Morale");
1869 break;
1870
1871 case MERC_APPEARANCEMODE:
1872 {
1873 SOLDIERCREATE_STRUCT const& dp = *gpSelected->pDetailedPlacement;
1874 UINT8 const foreground =
1875 dp.fVisible || dp.ubProfile != NO_PROFILE ? FONT_YELLOW : FONT_DKYELLOW;
1876 SetFontAttributes(FONT10ARIAL, foreground);
1877
1878 MPrint(396, EDITOR_TASKBAR_POS_Y + 4, "Hair color:");
1879 MPrint(396, EDITOR_TASKBAR_POS_Y + 28, "Skin color:");
1880 MPrint(396, EDITOR_TASKBAR_POS_Y + 52, "Vest color:");
1881 MPrint(396, EDITOR_TASKBAR_POS_Y + 76, "Pant color:");
1882
1883 SetFont( SMALLCOMPFONT );
1884 SetFontForeground( FONT_BLACK );
1885 if( gpSelected->pDetailedPlacement->fVisible || gpSelected->pDetailedPlacement->ubProfile != NO_PROFILE )
1886 {
1887 MPrintEditor(396, 14, ST::format("{} ", gpSelected->pSoldier->HeadPal));
1888 MPrintEditor(396, 38, ST::format("{} ", gpSelected->pSoldier->SkinPal));
1889 MPrintEditor(396, 62, ST::format("{} ", gpSelected->pSoldier->VestPal));
1890 MPrintEditor(396, 86, ST::format("{} ", gpSelected->pSoldier->PantsPal));
1891 ShowEditMercPalettes( gpSelected->pSoldier );
1892 }
1893 else
1894 {
1895 MPrint(396, EDITOR_TASKBAR_POS_Y + 14, "RANDOM");
1896 MPrint(396, EDITOR_TASKBAR_POS_Y + 38, "RANDOM");
1897 MPrint(396, EDITOR_TASKBAR_POS_Y + 62, "RANDOM");
1898 MPrint(396, EDITOR_TASKBAR_POS_Y + 86, "RANDOM");
1899 ShowEditMercPalettes( NULL ); //will display grey scale to signify random
1900 }
1901 DisplayBodyTypeInfo();
1902 break;
1903 }
1904
1905 case MERC_PROFILEMODE:
1906 { //scope trick
1907 ST::string tempStr = ST::format(
1908 "By specifying a profile index, all of the information will be extracted from the profile "
1909 "and override any values that you have edited. It will also disable the editing features "
1910 "though, you will still be able to view stats, etc. Pressing ENTER will automatically "
1911 "extract the number you have typed. A blank field will clear the profile. The current "
1912 "number of profiles range from 0 to {}.", NUM_PROFILES);
1913 SetFontShadow(FONT_NEARBLACK);
1914 DisplayWrappedString(180, EDITOR_TASKBAR_POS_Y + 10, 400, 2, FONT10ARIAL, 146, tempStr, FONT_BLACK, LEFT_JUSTIFIED);
1915 SetFont( FONT12POINT1 );
1916 if( gpSelected->pDetailedPlacement->ubProfile == NO_PROFILE )
1917 {
1918 SetFontForeground( FONT_GRAY3 );
1919 MPrintEditor(240, 75, "Current Profile: n/a ");
1920 }
1921 else
1922 {
1923 SetFontForeground( FONT_WHITE );
1924 ClearTaskbarRegion(240, 75, 580, 85);
1925 MPrint(240, EDITOR_TASKBAR_POS_Y + 75, ST::format("Current Profile: {}", gMercProfiles[gpSelected->pDetailedPlacement->ubProfile].zName));
1926 }
1927 }
1928 break;
1929 case MERC_SCHEDULEMODE:
1930 SetFontAttributes(FONT10ARIAL, FONT_WHITE);
1931 switch( gpSelected->pSoldier->bOrders )
1932 {
1933 case STATIONARY: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "STATIONARY"); break;
1934 case ONCALL: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "ON CALL"); break;
1935 case ONGUARD: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "ON GUARD"); break;
1936 case SEEKENEMY: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "SEEK ENEMY"); break;
1937 case CLOSEPATROL: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "CLOSE PATROL"); break;
1938 case FARPATROL: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "FAR PATROL"); break;
1939 case POINTPATROL: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "POINT PATROL"); break;
1940 case RNDPTPATROL: MPrint(430, EDITOR_TASKBAR_POS_Y + 3, "RND PT PATROL"); break;
1941 }
1942 SetFontForeground( FONT_YELLOW );
1943 MPrint(186, EDITOR_TASKBAR_POS_Y + 3, "Action");
1944 MPrint(268, EDITOR_TASKBAR_POS_Y + 3, "Time");
1945 MPrint(309, EDITOR_TASKBAR_POS_Y + 3, "V");
1946 MPrint(331, EDITOR_TASKBAR_POS_Y + 3, "GridNo 1");
1947 MPrint(381, EDITOR_TASKBAR_POS_Y + 3, "GridNo 2");
1948 MPrint(172, EDITOR_TASKBAR_POS_Y + 16, "1)");
1949 MPrint(172, EDITOR_TASKBAR_POS_Y + 37, "2)");
1950 MPrint(172, EDITOR_TASKBAR_POS_Y + 58, "3)");
1951 MPrint(172, EDITOR_TASKBAR_POS_Y + 79, "4)");
1952 if( gubScheduleInstructions )
1953 {
1954 ST::string str;
1955 ST::string keyword;
1956 ColorFillVideoSurfaceArea( FRAME_BUFFER, 431, EDITOR_TASKBAR_POS_Y + 28, 590, EDITOR_TASKBAR_POS_Y + 90, Get16BPPColor( FROMRGB( 32, 45, 72 ) ) );
1957 switch( gCurrSchedule.ubAction[ gubCurrentScheduleActionIndex ] )
1958 {
1959 case SCHEDULE_ACTION_LOCKDOOR: keyword = "lock"; break;
1960 case SCHEDULE_ACTION_UNLOCKDOOR: keyword = "unlock"; break;
1961 case SCHEDULE_ACTION_OPENDOOR: keyword = "open"; break;
1962 case SCHEDULE_ACTION_CLOSEDOOR: keyword = "close"; break;
1963 }
1964 switch( gubScheduleInstructions )
1965 {
1966 case SCHEDULE_INSTRUCTIONS_DOOR1:
1967 str = ST::format("Click on the gridno adjacent to the door that you wish to {}.", keyword);
1968 break;
1969 case SCHEDULE_INSTRUCTIONS_DOOR2:
1970 str = ST::format("Click on the gridno where you wish to move after you {} the door.", keyword);
1971 break;
1972 case SCHEDULE_INSTRUCTIONS_GRIDNO:
1973 str = "Click on the gridno where you wish to move to.";
1974 break;
1975 case SCHEDULE_INSTRUCTIONS_SLEEP:
1976 str = "Click on the gridno where you wish to sleep at. Person will automatically return to original position after waking up.";
1977 default:
1978 return;
1979 }
1980 str += " Hit ESC to abort entering this line in the schedule.";
1981 DisplayWrappedString(436, EDITOR_TASKBAR_POS_Y + 32, 149, 2, FONT10ARIAL, FONT_YELLOW, str, FONT_BLACK, LEFT_JUSTIFIED);
1982 }
1983 break;
1984 }
1985 }
1986
1987 //When a detailed placement merc is in the inventory panel, there is a overall region
1988 //blanketing this panel. As the user moves the mouse around and clicks, etc., this function
1989 //is called by the region callback functions to handle these cases. The event types are defined
1990 //in Editor Taskbar Utils.h. Here are the internal functions...
1991
1992 SGPRect mercRects[9] =
1993 {
1994 { 75, 0, 104, 19 }, //head
1995 { 75, 22, 104, 41 }, //body
1996 { 76, 73, 105, 92 }, //legs
1997 { 26, 43, 78, 62 }, //left hand
1998 { 104, 42, 156, 61 }, //right hand
1999 { 180, 6, 232, 25 }, //pack 1
2000 { 180, 29, 232, 48 }, //pack 2
2001 { 180, 52, 232, 71 }, //pack 3
2002 { 180, 75, 232, 94 } //pack 4
2003 };
2004
2005
PointInRect(SGPRect * pRect,INT32 x,INT32 y)2006 static BOOLEAN PointInRect(SGPRect* pRect, INT32 x, INT32 y)
2007 {
2008 return( x >= pRect->iLeft && x <= pRect->iRight && y >= pRect->iTop && y <= pRect->iBottom );
2009 }
2010
2011
DrawRect(SGPRect * pRect,INT16 color)2012 static void DrawRect(SGPRect* pRect, INT16 color)
2013 {
2014 SGPVSurface::Lock l(FRAME_BUFFER);
2015 SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
2016 RectangleDraw(TRUE, pRect->iLeft + MERCPANEL_X, pRect->iTop + MERCPANEL_Y, pRect->iRight + MERCPANEL_X, pRect->iBottom + MERCPANEL_Y, color, l.Buffer<UINT16>());
2017 }
2018
2019
RenderSelectedMercsInventory(void)2020 static void RenderSelectedMercsInventory(void)
2021 {
2022 INT32 i;
2023 INT32 xp, yp;
2024 UINT8 ubFontColor;
2025 if (g_selected_merc == NULL) return;
2026 for( i = 0; i < 9; i++ )
2027 {
2028 if( gpMercSlotItem[i] )
2029 { //Render the current image.
2030 xp = mercRects[ i ].iLeft + 4 + MERCPANEL_X;
2031 yp = mercRects[ i ].iTop + MERCPANEL_Y;
2032 BltVideoSurface(FRAME_BUFFER, guiMercInvPanelBuffers[i], xp, yp, NULL);
2033 //Render the text
2034 switch( i )
2035 {
2036 case 2: //legs (to the right of the box, but move it down to make room for right hand text)
2037 xp = mercRects[i].iRight + 2;
2038 yp = mercRects[i].iTop + 8;
2039 break;
2040 case 3: //left hand (underneath box and to the left -- obscurred by checkbox)
2041 xp = mercRects[i].iLeft - 20;
2042 yp = mercRects[i].iBottom + 2;
2043 break;
2044 case 4: //right hand (underneath box)
2045 xp = mercRects[i].iLeft;
2046 yp = mercRects[i].iBottom + 2;
2047 break;
2048 default: //normal cases (to the right of the box)
2049 xp = mercRects[i].iRight + 2;
2050 yp = mercRects[i].iTop;
2051 break;
2052 }
2053 xp += MERCPANEL_X;
2054 yp += MERCPANEL_Y;
2055 if( i == gbCurrSelect )
2056 ubFontColor = FONT_LTRED;
2057 else if( i == gbCurrHilite )
2058 ubFontColor = FONT_YELLOW;
2059 else
2060 ubFontColor = FONT_WHITE;
2061 ST::string ItemName = ItemNames[gpMercSlotItem[i]->usItem];
2062 DisplayWrappedString(xp, yp, 60, 2, SMALLCOMPFONT, ubFontColor, ItemName, 0, LEFT_JUSTIFIED);
2063 }
2064 }
2065 }
2066
DeleteSelectedMercsItem()2067 void DeleteSelectedMercsItem()
2068 {
2069 if( gbCurrSelect != -1 )
2070 {
2071 gusMercsNewItemIndex = 0;
2072 AddNewItemToSelectedMercsInventory( TRUE );
2073 }
2074 }
2075
2076 //This function does two main things:
2077 // 1) Allows a new item to be created via usItem and assigned to the currently selected merc.
2078 // 2) Converts the image from interface size to the smaller panel used by the editor. The slots
2079 // in the editor are approximately 80% of that size. This involves scaling calculations. These
2080 // images are saved in individual slots are are blitted to the screen during rendering, not here.
2081 // NOTE: Step one can be skipped (when selecting an existing merc). By setting the
AddNewItemToSelectedMercsInventory(BOOLEAN fCreate)2082 static void AddNewItemToSelectedMercsInventory(BOOLEAN fCreate)
2083 {
2084 UINT16 uDstWidth, uDstHeight;
2085 float rScalar, rWidthScalar, rHeightScalar;
2086 BOOLEAN fUnDroppable;
2087
2088 if( fCreate )
2089 {
2090 /*
2091 if( gpMercSlotItem[ gbCurrSelect ] && gpMercSlotItem[ gbCurrSelect ]->usItem == gusMercsNewItemIndex )
2092 { //User selected same item, so ignore.
2093 gusMercsNewItemIndex = 0xffff;
2094 return;
2095 }
2096 */
2097 if( gusMercsNewItemIndex == 0xffff )
2098 { //User selected no item, so ignore.
2099 return;
2100 }
2101 //Create the item, and set up the slot.
2102 fUnDroppable = gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ].fFlags & OBJECT_UNDROPPABLE ? TRUE : FALSE;
2103
2104 if ( GCM->getItem(gusMercsNewItemIndex)->getItemClass() == IC_KEY )
2105 {
2106 CreateKeyObject( &gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ], 1, (UINT8) eInfo.sSelItemIndex );
2107 }
2108 else
2109 {
2110 CreateItem( gusMercsNewItemIndex, 100, &gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ] );
2111 }
2112 if( fUnDroppable )
2113 {
2114 gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ].fFlags |= OBJECT_UNDROPPABLE;
2115 }
2116
2117 //randomize the status on non-ammo items.
2118 if( !(GCM->getItem(gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ].usItem)->isAmmo()) )
2119 gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ].bStatus[0] = (INT8)(80 + Random( 21 ));
2120
2121 if( gusMercsNewItemIndex )
2122 {
2123 gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ].fFlags |= OBJECT_NO_OVERWRITE;
2124 }
2125 }
2126 //allow the slot to point to the selected merc's inventory for editing/rendering purposes.
2127 gpMercSlotItem[ gbCurrSelect ] = &gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ gbCurrSelect ] ];
2128
2129 if( !fCreate )
2130 { //it is possible to have a null item which we don't want to blit! Also, we need to set the
2131 //new item index, so that it can extract the item's image using that.
2132 gusMercsNewItemIndex = gpMercSlotItem[ gbCurrSelect ]->usItem;
2133 if( !gpMercSlotItem[ gbCurrSelect ] )
2134 return;
2135 }
2136 //GOAL:
2137 //From here on, we are going to first render the new item into a temp buffer, then crop the image in
2138 //the buffer and scale it down to fit into it's associated slot in the panel (which on average will
2139 //require scaling the item by 80%). We have to do a bunch of calculations to override the offsets, etc.
2140 //Each slot has it's own smaller version buffer, and this is what gets drawn when the rendering happens.
2141
2142 //assign the buffers
2143 SGPVSurface* const uiSrcID = guiMercTempBuffer;
2144 SGPVSurface* const uiDstID = guiMercInvPanelBuffers[gbCurrSelect];
2145
2146 //build the rects
2147 uDstWidth = gbCurrSelect < 3 ? MERCINV_SMSLOT_WIDTH : MERCINV_LGSLOT_WIDTH;
2148 uDstHeight = MERCINV_SLOT_HEIGHT;
2149 SGPRect SrcRect;
2150 SGPRect DstRect;
2151 SrcRect.iLeft = 0;
2152 SrcRect.iTop = 0;
2153 SrcRect.iRight = 60;
2154 SrcRect.iBottom = 25;
2155 DstRect.iLeft = 0;
2156 DstRect.iTop = 0;
2157 DstRect.iRight = uDstWidth;
2158 DstRect.iBottom = uDstHeight;
2159
2160 //clear both buffers (fill with black to erase previous graphic)
2161 ColorFillVideoSurfaceArea( uiSrcID, SrcRect.iLeft, SrcRect.iTop, SrcRect.iRight, SrcRect.iBottom, 0 );
2162 ColorFillVideoSurfaceArea( uiDstID, DstRect.iLeft, DstRect.iTop, DstRect.iRight, DstRect.iBottom, 0 );
2163
2164 //if the index is 0, then there is no item.
2165 if( !gusMercsNewItemIndex )
2166 return;
2167
2168 //now draw the fullsize item into the temp buffer
2169 const ItemModel * item = GCM->getItem(gusMercsNewItemIndex);
2170 SGPVObject const& vo = GetInterfaceGraphicForItem(item);
2171 BltVideoObjectOutline(uiSrcID, &vo, item->getGraphicNum(), 0, 0, SGP_TRANSPARENT);
2172
2173 //crop the source image
2174 ETRLEObject const& pObject = vo.SubregionProperties(item->getGraphicNum());
2175 UINT16 const uSrcWidth = pObject.usWidth;
2176 UINT16 const uSrcHeight = pObject.usHeight;
2177 SGPBox const src_rect =
2178 {
2179 (UINT16)(SrcRect.iLeft + pObject.sOffsetX),
2180 (UINT16)(SrcRect.iTop + pObject.sOffsetY),
2181 uSrcWidth,
2182 uSrcHeight
2183 };
2184
2185 //if the source image width is less than 30 (small slot), then modify the DstRect.
2186 if( uSrcWidth < 30 )
2187 uDstWidth = MERCINV_SMSLOT_WIDTH;
2188 else
2189 uDstWidth = MERCINV_LGSLOT_WIDTH;
2190
2191 //compare the sizes of the cropped image to the destination buffer size, and calculate the
2192 //scalar value. It is possible to have scalars > 1.0, in which case, we change it to 1.0 and
2193 //use the other value.
2194 rWidthScalar = (float)uDstWidth/(float)uSrcWidth;
2195 if( rWidthScalar > 1.0 )
2196 rWidthScalar = 1.0;
2197 rHeightScalar = (float)uDstHeight/(float)uSrcHeight;
2198 if( rHeightScalar > 1.0 )
2199 rHeightScalar = 1.0;
2200
2201 //determine which scalar to use.
2202 if( rWidthScalar == 1.0 )
2203 rScalar = rHeightScalar;
2204 else if( rHeightScalar == 1.0 )
2205 rScalar = rWidthScalar ;
2206 else
2207 rScalar = MAX( rWidthScalar, rHeightScalar );
2208
2209 //apply the scalar to the destination width and height
2210 uDstWidth = (UINT16)( uSrcWidth * rScalar );
2211 uDstHeight = (UINT16)( uSrcHeight * rScalar );
2212
2213 //sometimes it is possible to scale too big, so clip if applicable
2214 if( uDstWidth > MERCINV_LGSLOT_WIDTH )
2215 uDstWidth = MERCINV_LGSLOT_WIDTH;
2216 else if( gbCurrSelect < 3 && uDstWidth > MERCINV_SMSLOT_WIDTH )
2217 uDstWidth = MERCINV_SMSLOT_WIDTH;
2218 if( uDstHeight > MERCINV_SLOT_HEIGHT )
2219 uDstHeight = MERCINV_SLOT_HEIGHT;
2220
2221 //use the new width and height values to calculate the new dest rect (center the item)
2222 SGPBox const dst_rect =
2223 {
2224 (UINT16)((DstRect.iRight - DstRect.iLeft - uDstWidth) / 2),
2225 (UINT16)((DstRect.iBottom - DstRect.iTop - uDstHeight) / 2),
2226 uDstWidth,
2227 uDstHeight
2228 };
2229
2230 //scale the item down to the smaller buffer.
2231 BltStretchVideoSurface(uiDstID, uiSrcID, &src_rect, &dst_rect);
2232
2233 //invalidate the mercs new item index
2234 gusMercsNewItemIndex = 0xffff;
2235 }
2236
2237
RenderMercInventoryPanel(void)2238 static void RenderMercInventoryPanel(void)
2239 {
2240 INT32 x;
2241 //Draw the graphical panel
2242 BltVideoObject(FRAME_BUFFER, guiMercInventoryPanel, 0, MERCPANEL_X, MERCPANEL_Y);
2243 //Mark the buttons dirty, so they don't disappear.
2244 for( x = FIRST_MERCS_INVENTORY_BUTTON; x <= LAST_MERCS_INVENTORY_BUTTON; x++ )
2245 {
2246 MarkAButtonDirty( iEditorButton[ x ] );
2247 }
2248 RenderButtons();
2249 if( gbCurrHilite != -1 )
2250 DrawRect( &mercRects[ gbCurrHilite ], Get16BPPColor( FROMRGB( 200, 200, 0 ) ) );
2251 if( gbCurrSelect != -1 )
2252 DrawRect( &mercRects[ gbCurrSelect ], Get16BPPColor( FROMRGB( 200, 0, 0 ) ) );
2253 RenderSelectedMercsInventory();
2254 InvalidateRegion( MERCPANEL_X, MERCPANEL_Y, 475, EDITOR_TASKBAR_POS_Y + 100 );
2255 UpdateItemStatsPanel();
2256 }
2257
2258
2259 //This function is called by the move and click callback functions for the region blanketing the
2260 //9 slots in the inventory panel. It passes the event type as well as the relative x and y positions
2261 //which are processed here. This basically checks for new changes in hilighting and selections, which
2262 //will set the rendering flag, and getitem flag if the user wishes to choose an item.
HandleMercInventoryPanel(INT16 sX,INT16 sY,INT8 bEvent)2263 void HandleMercInventoryPanel( INT16 sX, INT16 sY, INT8 bEvent )
2264 {
2265 INT8 x;
2266 if( !gfCanEditMercs && bEvent == GUI_RCLICK_EVENT )
2267 { //if we are dealing with a profile merc, we can't allow editing
2268 //of items, but we can look at them. So, treat all right clicks
2269 //as if they were left clicks.
2270 bEvent = GUI_LCLICK_EVENT;
2271 }
2272 switch( bEvent )
2273 {
2274 case GUI_MOVE_EVENT:
2275 //user is moving the mouse around the panel, so determine which slot
2276 //needs to be hilighted yellow.
2277 for( x = 0; x < 9; x++ )
2278 {
2279 if( PointInRect( &mercRects[x], sX, sY ) )
2280 {
2281 if( gbCurrHilite != x ) //only render if the slot isn't the same one.
2282 gfRenderMercInfo = TRUE;
2283 gbCurrHilite = x;
2284 return;
2285 }
2286 }
2287 //if we don't find a valid slot, then we need to turn it off.
2288 if( gbCurrHilite != -1 )
2289 { //only turn off if it isn't already off. This avoids unecessary rendering.
2290 gbCurrHilite = -1;
2291 gfRenderMercInfo = TRUE;
2292 }
2293 break;
2294 case GUI_LCLICK_EVENT:
2295 case GUI_RCLICK_EVENT:
2296 //The user has clicked in the inventory panel. Determine if he clicked in
2297 //a slot. Left click selects the slot for editing, right clicking enables
2298 //the user to choose an item for that slot.
2299 for( x = 0; x < 9; x++ )
2300 {
2301 if( PointInRect( &mercRects[x], sX, sY ) )
2302 {
2303 if( gbCurrSelect != x ) //only if it isn't the same slot.
2304 {
2305 gfRenderMercInfo = TRUE;
2306 if( bEvent == GUI_LCLICK_EVENT )
2307 SpecifyItemToEdit( gpMercSlotItem[ x ], -1 );
2308 }
2309 if( bEvent == GUI_RCLICK_EVENT ) //user r-clicked, so enable item choosing
2310 gfMercGetItem = TRUE;
2311 gbCurrSelect = x;
2312 return;
2313 }
2314 }
2315 break;
2316 }
2317 }
2318
2319
2320 static void SetDroppableCheckboxesBasedOnMercsInventory(void);
2321
2322
2323 /* When a new merc is selected, this function sets up all of the information
2324 * for the slots, selections, and hilites. */
UpdateMercItemSlots(void)2325 static void UpdateMercItemSlots(void)
2326 {
2327 INT8 x;
2328 if( !gpSelected->pDetailedPlacement )
2329 {
2330 for( x = 0; x < 9; x++ )
2331 {
2332 gpMercSlotItem[ x ] = NULL;
2333 }
2334 }
2335 else
2336 {
2337 if( gpSelected->pDetailedPlacement->ubProfile != NO_PROFILE )
2338 {
2339 memcpy( gpSelected->pDetailedPlacement->Inv, gpSelected->pSoldier->inv, sizeof( OBJECTTYPE ) * NUM_INV_SLOTS );
2340 }
2341 for( x = 0; x < 9; x++ )
2342 {
2343 //Set the curr select and the addnewitem function will handle the rest, including rebuilding
2344 //the nine slot buffers, etc.
2345 gbCurrSelect = x;
2346 AddNewItemToSelectedMercsInventory( FALSE );
2347 }
2348 }
2349 SetDroppableCheckboxesBasedOnMercsInventory();
2350 SpecifyItemToEdit( NULL, -1 );
2351 gbCurrSelect = -1;
2352 gbCurrHilite = -1;
2353 }
2354
2355
SetDroppableCheckboxesBasedOnMercsInventory(void)2356 static void SetDroppableCheckboxesBasedOnMercsInventory(void)
2357 {
2358 OBJECTTYPE *pItem;
2359 INT32 i;
2360 if( gpSelected && gpSelected->pDetailedPlacement )
2361 {
2362 for( i = 0; i < 9; i++ )
2363 {
2364 pItem = &gpSelected->pDetailedPlacement->Inv[ gbMercSlotTypes[ i ] ];
2365 if( pItem->fFlags & OBJECT_UNDROPPABLE )
2366 { //check box is clear
2367 UnclickEditorButton( MERCS_HEAD_SLOT + i );
2368 }
2369 else
2370 {
2371 ClickEditorButton( MERCS_HEAD_SLOT + i );
2372 }
2373 }
2374 }
2375 }
2376
2377
SetEnemyColorCode(UINT8 const colour_code)2378 void SetEnemyColorCode(UINT8 const colour_code)
2379 {
2380 SOLDIERINITNODE const& sel = *gpSelected;
2381 SOLDIERCREATE_STRUCT* const dp = sel.pDetailedPlacement;
2382 if (dp && dp->ubProfile != NO_PROFILE) return;
2383
2384 UnclickEditorButtons(FIRST_MERCS_COLORCODE_BUTTON, LAST_MERCS_COLORCODE_BUTTON);
2385 char const* vest;
2386 char const* pants;
2387 switch (colour_code)
2388 {
2389 case SOLDIER_CLASS_ARMY:
2390 ClickEditorButton(MERCS_ARMY_CODE);
2391 vest = "REDVEST";
2392 pants = "GREENPANTS";
2393 break;
2394
2395 case SOLDIER_CLASS_ADMINISTRATOR:
2396 ClickEditorButton(MERCS_ADMIN_CODE);
2397 vest = "BLUEVEST";
2398 pants = "BLUEPANTS";
2399 break;
2400
2401 case SOLDIER_CLASS_ELITE:
2402 ClickEditorButton(MERCS_ELITE_CODE);
2403 vest = "BLACKSHIRT";
2404 pants = "BLACKPANTS";
2405 break;
2406
2407 case SOLDIER_CLASS_MINER:
2408 vest = "greyVEST";
2409 pants = "BEIGEPANTS";
2410 break;
2411
2412 default: return;
2413 }
2414 gubSoldierClass = colour_code;
2415 sel.pBasicPlacement->ubSoldierClass = colour_code;
2416 if (dp) dp->ubSoldierClass = colour_code;
2417 SOLDIERTYPE& s = *sel.pSoldier;
2418 s.VestPal = vest;
2419 s.PantsPal = pants;
2420 CreateSoldierPalettes(s);
2421 }
2422
2423
SetEnemyDroppableStatus(UINT32 uiSlot,BOOLEAN fDroppable)2424 void SetEnemyDroppableStatus( UINT32 uiSlot, BOOLEAN fDroppable )
2425 {
2426 if( gpSelected )
2427 {
2428 if( fDroppable )
2429 {
2430 if( gpSelected->pDetailedPlacement )
2431 gpSelected->pDetailedPlacement->Inv[ uiSlot ].fFlags &= (~OBJECT_UNDROPPABLE);
2432 if( gpSelected->pSoldier )
2433 gpSelected->pSoldier->inv[ uiSlot ].fFlags &= (~OBJECT_UNDROPPABLE);
2434 }
2435 else
2436 {
2437 if( gpSelected->pDetailedPlacement )
2438 gpSelected->pDetailedPlacement->Inv[ uiSlot ].fFlags |= OBJECT_UNDROPPABLE;
2439 if( gpSelected->pSoldier )
2440 gpSelected->pSoldier->inv[ uiSlot ].fFlags |= OBJECT_UNDROPPABLE;
2441 }
2442 }
2443 if( gbCurrSelect != -1 && uiSlot == (UINT32)gbMercSlotTypes[ gbCurrSelect ] )
2444 {
2445 if( gpMercSlotItem[ gbCurrSelect ]->usItem == NOTHING )
2446 SpecifyItemToEdit( gpMercSlotItem[ gbCurrSelect ], -1 );
2447 }
2448 }
2449
ChangeCivGroup(UINT8 ubNewCivGroup)2450 void ChangeCivGroup( UINT8 ubNewCivGroup )
2451 {
2452 Assert( ubNewCivGroup < NUM_CIV_GROUPS );
2453 if( gubCivGroup == ubNewCivGroup )
2454 return;
2455 gubCivGroup = ubNewCivGroup;
2456 if( gpSelected && gpSelected->pSoldier )
2457 {
2458 gpSelected->pBasicPlacement->ubCivilianGroup = gubCivGroup;
2459 if( gpSelected->pDetailedPlacement )
2460 gpSelected->pDetailedPlacement->ubCivilianGroup = gubCivGroup;
2461 gpSelected->pSoldier->ubCivilianGroup = gubCivGroup;
2462 }
2463 //Adjust the text on the button
2464 iEditorButton[MERCS_CIVILIAN_GROUP]->SpecifyText(gszCivGroupNames[gubCivGroup]);
2465 }
2466
2467
2468 static void RenderCurrentSchedule(void);
2469
2470
RenderMercStrings()2471 void RenderMercStrings()
2472 {
2473 CFOR_EACH_SOLDIERINITNODE(curr)
2474 {
2475 SOLDIERTYPE const* const s = curr->pSoldier;
2476 if (!s || s->bVisible != 1) continue;
2477
2478 INT16 sX;
2479 INT16 sY;
2480 INT16 sXPos;
2481 INT16 sYPos;
2482 GetSoldierAboveGuyPositions(s, &sXPos, &sYPos, FALSE);
2483
2484 // Display name
2485 SetFontAttributes(TINYFONT1, FONT_WHITE);
2486 if (s->ubProfile != NO_PROFILE)
2487 {
2488 FindFontCenterCoordinates(sXPos, sYPos, 80, 1, s->name, TINYFONT1, &sX, &sY);
2489 if (sY < 352)
2490 {
2491 GDirtyPrint(sX, sY, s->name);
2492 }
2493 sYPos += 10;
2494 }
2495
2496 // Render the health text
2497 ST::string health = GetSoldierHealthString(s);
2498 SetFontForeground(FONT_RED);
2499 FindFontCenterCoordinates(sXPos, sYPos, 80, 1, health, TINYFONT1, &sX, &sY);
2500 if (sY < 352)
2501 {
2502 GDirtyPrint(sX, sY, health);
2503 }
2504 sYPos += 10;
2505
2506 SetFontForeground(FONT_GRAY2);
2507 ST::string str = ST::format("Slot #{}", s->ubID);
2508 FindFontCenterCoordinates(sXPos, sYPos, 80, 1, str, TINYFONT1, &sX, &sY);
2509 if (sY < 352)
2510 {
2511 GDirtyPrint(sX, sY, str);
2512 }
2513 sYPos += 10;
2514
2515 // Make sure the placement has at least one waypoint.
2516 BASIC_SOLDIERCREATE_STRUCT const& bp = *curr->pBasicPlacement;
2517 bool const has_patrol_order = bp.bOrders == RNDPTPATROL || bp.bOrders == POINTPATROL;
2518 bool const has_patrol_points = bp.bPatrolCnt != 0;
2519 if (has_patrol_order != has_patrol_points)
2520 {
2521 SetFontForeground(GetJA2Clock() % 1000 < 500 ? FONT_DKRED : FONT_RED);
2522 ST::string msg = has_patrol_points ?
2523 "Waypoints with no patrol orders" :
2524 "Patrol orders with no waypoints";
2525 FindFontCenterCoordinates(sXPos, sYPos, 80, 1, msg, TINYFONT1, &sX, &sY);
2526 if (sY < 352)
2527 {
2528 GDirtyPrint(sX, sY, msg);
2529 }
2530 }
2531 }
2532 if (gubCurrMercMode == MERC_SCHEDULEMODE)
2533 {
2534 RenderCurrentSchedule();
2535 }
2536 }
2537
2538
SetMercTeamVisibility(INT8 bTeam,BOOLEAN fVisible)2539 void SetMercTeamVisibility( INT8 bTeam, BOOLEAN fVisible )
2540 {
2541 INT8 bVisible;
2542 bVisible = fVisible ? 1 : -1;
2543 CFOR_EACH_SOLDIERINITNODE(curr)
2544 {
2545 if( curr->pBasicPlacement->bTeam == bTeam )
2546 {
2547 if( curr->pSoldier )
2548 {
2549 curr->pSoldier->bLastRenderVisibleValue = bVisible;
2550 curr->pSoldier->bVisible = bVisible;
2551 }
2552 }
2553 }
2554 if( gpSelected && gpSelected->pSoldier && gpSelected->pSoldier->bTeam == bTeam && !fVisible )
2555 {
2556 IndicateSelectedMerc( SELECT_NO_MERC );
2557 }
2558 }
2559
2560
DetermineScheduleEditability(void)2561 static void DetermineScheduleEditability(void)
2562 {
2563 INT32 i;
2564 EnableEditorButtons( MERCS_SCHEDULE_ACTION1, MERCS_SCHEDULE_DATA4B );
2565 EnableTextFields( 1, 4 );
2566 for( i = 0; i < 4; i++ )
2567 {
2568 switch( gCurrSchedule.ubAction[i] )
2569 {
2570 case SCHEDULE_ACTION_NONE:
2571 case SCHEDULE_ACTION_LEAVESECTOR:
2572 EnableEditorButton( MERCS_SCHEDULE_ACTION1 + i );
2573 EnableEditorButton( MERCS_SCHEDULE_VARIANCE1 + i );
2574 HideEditorButton( MERCS_SCHEDULE_DATA1A + i );
2575 HideEditorButton( MERCS_SCHEDULE_DATA1B + i );
2576 break;
2577 case SCHEDULE_ACTION_LOCKDOOR:
2578 case SCHEDULE_ACTION_UNLOCKDOOR:
2579 case SCHEDULE_ACTION_OPENDOOR:
2580 case SCHEDULE_ACTION_CLOSEDOOR:
2581 EnableEditorButton( MERCS_SCHEDULE_ACTION1 + i );
2582 EnableEditorButton( MERCS_SCHEDULE_VARIANCE1 + i );
2583 ShowEditorButton( MERCS_SCHEDULE_DATA1A + i );
2584 ShowEditorButton( MERCS_SCHEDULE_DATA1B + i );
2585 EnableEditorButton( MERCS_SCHEDULE_DATA1A + i );
2586 EnableEditorButton( MERCS_SCHEDULE_DATA1B + i );
2587 break;
2588 case SCHEDULE_ACTION_GRIDNO:
2589 case SCHEDULE_ACTION_ENTERSECTOR:
2590 case SCHEDULE_ACTION_SLEEP:
2591 EnableEditorButton( MERCS_SCHEDULE_ACTION1 + i );
2592 EnableEditorButton( MERCS_SCHEDULE_VARIANCE1 + i );
2593 ShowEditorButton( MERCS_SCHEDULE_DATA1A + i );
2594 HideEditorButton( MERCS_SCHEDULE_DATA1B + i );
2595 EnableEditorButton( MERCS_SCHEDULE_DATA1A + i );
2596 break;
2597 case SCHEDULE_ACTION_STAYINSECTOR:
2598 DisableTextField( (UINT8)(i+1) );
2599 EnableEditorButton( MERCS_SCHEDULE_ACTION1 + i );
2600 DisableEditorButton( MERCS_SCHEDULE_VARIANCE1 + i );
2601 HideEditorButton( MERCS_SCHEDULE_DATA1A + i );
2602 HideEditorButton( MERCS_SCHEDULE_DATA1B + i );
2603 break;
2604 }
2605 }
2606 }
2607
CancelCurrentScheduleAction()2608 void CancelCurrentScheduleAction()
2609 {
2610 UpdateScheduleAction( SCHEDULE_ACTION_NONE );
2611 DetermineScheduleEditability();
2612 }
2613
RegisterCurrentScheduleAction(INT32 iMapIndex)2614 void RegisterCurrentScheduleAction( INT32 iMapIndex )
2615 {
2616 MarkWorldDirty();
2617 ST::string str = ST::format("{}", iMapIndex);
2618 if( gfUseScheduleData2 )
2619 {
2620 if( gfSingleAction )
2621 return;
2622 iDrawMode = DRAW_MODE_PLAYER + gpSelected->pBasicPlacement->bTeam;
2623 gCurrSchedule.usData2[ gubCurrentScheduleActionIndex ] = (UINT16)iMapIndex;
2624 iEditorButton[MERCS_SCHEDULE_DATA1B + gubCurrentScheduleActionIndex]->SpecifyText(str);
2625 DetermineScheduleEditability();
2626 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_NONE;
2627 gfRenderTaskbar = TRUE;
2628 gfUseScheduleData2 = FALSE;
2629 }
2630 else
2631 {
2632 switch( gCurrSchedule.ubAction[ gubCurrentScheduleActionIndex ] )
2633 {
2634 case SCHEDULE_ACTION_LOCKDOOR:
2635 case SCHEDULE_ACTION_UNLOCKDOOR:
2636 case SCHEDULE_ACTION_OPENDOOR:
2637 case SCHEDULE_ACTION_CLOSEDOOR:
2638 if( gfSingleAction )
2639 {
2640 gfSingleAction = FALSE;
2641 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_NONE;
2642 gfRenderTaskbar = TRUE;
2643 DetermineScheduleEditability();
2644 break;
2645 }
2646 DisableEditorButton( MERCS_SCHEDULE_DATA1A + gubCurrentScheduleActionIndex );
2647 EnableEditorButton( MERCS_SCHEDULE_DATA1B + gubCurrentScheduleActionIndex );
2648 gfUseScheduleData2 = TRUE;
2649 iDrawMode = DRAW_MODE_SCHEDULEACTION;
2650 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_DOOR2;
2651 gfRenderTaskbar = TRUE;
2652 break;
2653 case SCHEDULE_ACTION_GRIDNO:
2654 case SCHEDULE_ACTION_ENTERSECTOR:
2655 case SCHEDULE_ACTION_SLEEP:
2656 iDrawMode = DRAW_MODE_PLAYER + gpSelected->pBasicPlacement->bTeam;
2657 DetermineScheduleEditability();
2658 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_NONE;
2659 gfRenderTaskbar = TRUE;
2660 break;
2661 case SCHEDULE_ACTION_LEAVESECTOR:
2662 case SCHEDULE_ACTION_STAYINSECTOR:
2663 case SCHEDULE_ACTION_NONE:
2664 break;
2665 }
2666 gCurrSchedule.usData1[ gubCurrentScheduleActionIndex ] = (UINT16)iMapIndex;
2667 iEditorButton[MERCS_SCHEDULE_DATA1A + gubCurrentScheduleActionIndex]->SpecifyText(str);
2668 }
2669 }
2670
StartScheduleAction()2671 void StartScheduleAction()
2672 {
2673 switch( gCurrSchedule.ubAction[ gubCurrentScheduleActionIndex ] )
2674 {
2675 case SCHEDULE_ACTION_NONE:
2676 EnableEditorButtons( MERCS_SCHEDULE_ACTION1, MERCS_SCHEDULE_DATA4B );
2677 EnableTextFields( 1, 4 );
2678 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_NONE;
2679 gfRenderTaskbar = TRUE;
2680 gCurrSchedule.usData1[ gubCurrentScheduleActionIndex ] = 0xffff;
2681 gCurrSchedule.usData2[ gubCurrentScheduleActionIndex ] = 0xffff;
2682 break;
2683 case SCHEDULE_ACTION_LOCKDOOR:
2684 case SCHEDULE_ACTION_UNLOCKDOOR:
2685 case SCHEDULE_ACTION_OPENDOOR:
2686 case SCHEDULE_ACTION_CLOSEDOOR:
2687 //First disable everything -- its easier that way.
2688 ShowEditorButton( MERCS_SCHEDULE_DATA1A + gubCurrentScheduleActionIndex );
2689 ShowEditorButton( MERCS_SCHEDULE_DATA1B + gubCurrentScheduleActionIndex );
2690 DisableEditorButtons( MERCS_SCHEDULE_ACTION1, MERCS_SCHEDULE_DATA4B );
2691 DisableTextFields( 1, 4 );
2692 EnableEditorButton( MERCS_SCHEDULE_DATA1A + gubCurrentScheduleActionIndex );
2693 gfUseScheduleData2 = FALSE;
2694 iDrawMode = DRAW_MODE_SCHEDULEACTION;
2695 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_DOOR1;
2696 gfRenderTaskbar = TRUE;
2697 break;
2698 case SCHEDULE_ACTION_GRIDNO:
2699 case SCHEDULE_ACTION_ENTERSECTOR:
2700 case SCHEDULE_ACTION_SLEEP:
2701 ShowEditorButton( MERCS_SCHEDULE_DATA1A + gubCurrentScheduleActionIndex );
2702 HideEditorButton( MERCS_SCHEDULE_DATA1B + gubCurrentScheduleActionIndex );
2703 DisableEditorButtons( MERCS_SCHEDULE_ACTION1, MERCS_SCHEDULE_DATA4B );
2704 DisableTextFields( 1, 4 );
2705 EnableEditorButton( MERCS_SCHEDULE_DATA1A + gubCurrentScheduleActionIndex );
2706 gfUseScheduleData2 = FALSE;
2707 iDrawMode = DRAW_MODE_SCHEDULEACTION;
2708 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_GRIDNO;
2709 gfRenderTaskbar = TRUE;
2710 gCurrSchedule.usData2[ gubCurrentScheduleActionIndex ] = 0xffff;
2711 break;
2712 case SCHEDULE_ACTION_LEAVESECTOR:
2713 case SCHEDULE_ACTION_STAYINSECTOR:
2714 gubScheduleInstructions = SCHEDULE_INSTRUCTIONS_NONE;
2715 gfRenderTaskbar = TRUE;
2716 gCurrSchedule.usData1[ gubCurrentScheduleActionIndex ] = 0xffff;
2717 gCurrSchedule.usData2[ gubCurrentScheduleActionIndex ] = 0xffff;
2718 break;
2719 }
2720 MarkWorldDirty();
2721 }
2722
UpdateScheduleAction(UINT8 ubNewAction)2723 void UpdateScheduleAction( UINT8 ubNewAction )
2724 {
2725 gCurrSchedule.ubAction[ gubCurrentScheduleActionIndex ] = ubNewAction;
2726 iEditorButton[MERCS_SCHEDULE_ACTION1 + gubCurrentScheduleActionIndex]->SpecifyText(gszScheduleActions[ubNewAction]);
2727 iEditorButton[MERCS_SCHEDULE_ACTION1 + gubCurrentScheduleActionIndex]->SetUserData(ubNewAction);
2728 //Now, based on this action, disable the other buttons
2729 StartScheduleAction();
2730 gfSingleAction = FALSE;
2731 }
2732
2733 // 0:1A, 1:1B, 2:2A, 3:2B, ...
FindScheduleGridNo(UINT8 ubScheduleData)2734 void FindScheduleGridNo( UINT8 ubScheduleData )
2735 {
2736 INT32 iMapIndex; // XXX HACK000E
2737 switch( ubScheduleData )
2738 {
2739 case 0: //1a
2740 iMapIndex = gCurrSchedule.usData1[0];
2741 break;
2742 case 1: //1b
2743 iMapIndex = gCurrSchedule.usData2[0];
2744 break;
2745 case 2: //2a
2746 iMapIndex = gCurrSchedule.usData1[1];
2747 break;
2748 case 3: //2b
2749 iMapIndex = gCurrSchedule.usData2[1];
2750 break;
2751 case 4: //3a
2752 iMapIndex = gCurrSchedule.usData1[2];
2753 break;
2754 case 5: //3b
2755 iMapIndex = gCurrSchedule.usData2[2];
2756 break;
2757 case 6: //4a
2758 iMapIndex = gCurrSchedule.usData1[3];
2759 break;
2760 case 7: //4b
2761 iMapIndex = gCurrSchedule.usData2[3];
2762 break;
2763 default:
2764 SLOGA("FindScheduleGridNo passed incorrect dataID." );
2765 abort(); // HACK000E
2766 }
2767 if( iMapIndex != 0xffff )
2768 {
2769 CenterScreenAtMapIndex( iMapIndex );
2770 }
2771 }
2772
ClearCurrentSchedule()2773 void ClearCurrentSchedule()
2774 {
2775 UINT8 i;
2776 gCurrSchedule = SCHEDULENODE{};
2777 for( i = 0; i < 4; i++ )
2778 {
2779 iEditorButton[MERCS_SCHEDULE_ACTION1 + i]->SetUserData(0);
2780 iEditorButton[MERCS_SCHEDULE_ACTION1 + i]->SpecifyText("No action");
2781 gCurrSchedule.usTime[i] = 0xffff;
2782 SetExclusive24HourTimeValue( (UINT8)(i+1), gCurrSchedule.usTime[ i ] ); //blanks the field
2783 gCurrSchedule.usData1[i] = 0xffff;
2784 iEditorButton[MERCS_SCHEDULE_DATA1A + i]->SpecifyText(ST::null);
2785 gCurrSchedule.usData2[i] = 0xffff;
2786 iEditorButton[MERCS_SCHEDULE_DATA1B + i]->SpecifyText(ST::null);
2787 }
2788 //Remove the variance stuff
2789 gCurrSchedule.usFlags = 0;
2790 UnclickEditorButtons( MERCS_SCHEDULE_VARIANCE1, MERCS_SCHEDULE_VARIANCE4 );
2791
2792 gubCurrentScheduleActionIndex = 0;
2793 DetermineScheduleEditability();
2794 gfRenderTaskbar = TRUE;
2795 MarkWorldDirty();
2796 }
2797
2798
RenderCurrentSchedule(void)2799 static void RenderCurrentSchedule(void)
2800 {
2801 FLOAT dOffsetX, dOffsetY;
2802 FLOAT ScrnX, ScrnY;
2803 INT32 i;
2804 INT32 iMapIndex;
2805 INT16 sXMapPos, sYMapPos;
2806 INT16 sScreenX, sScreenY;
2807 INT16 sX, sY;
2808 for( i = 0; i < 8; i++ )
2809 {
2810 if( i % 2 )
2811 iMapIndex = gCurrSchedule.usData2[ i / 2 ];
2812 else
2813 iMapIndex = gCurrSchedule.usData1[ i / 2 ];
2814
2815 if( iMapIndex == 0xffff )
2816 continue;
2817
2818 // Convert it's location to screen coordinates
2819 ConvertGridNoToXY( (INT16)iMapIndex, &sXMapPos, &sYMapPos );
2820
2821 dOffsetX = (FLOAT)(sXMapPos * CELL_X_SIZE) - gsRenderCenterX;
2822 dOffsetY = (FLOAT)(sYMapPos * CELL_Y_SIZE) - gsRenderCenterY;
2823
2824 FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &ScrnX, &ScrnY);
2825
2826 sScreenX = ( g_ui.m_tacticalMapCenterX ) + (INT16)ScrnX;
2827 sScreenY = ( g_ui.m_tacticalMapCenterY ) + (INT16)ScrnY;
2828
2829 // Adjust for tiles height factor!
2830 sScreenY -= gpWorldLevelData[ iMapIndex ].sHeight;
2831 // Bring it down a touch
2832 sScreenY += 5;
2833
2834 if( sScreenY <= 355 )
2835 {
2836 // Shown it on screen!
2837 SetFontAttributes(TINYFONT1, FONT_WHITE, DEFAULT_SHADOW, FONT_LTKHAKI);
2838 ST::string str = ST::format("{}{c}", i / 2 + 1, 'A' + i % 2);
2839 FindFontCenterCoordinates(sScreenX, sScreenY, 1, 1, str, TINYFONT1, &sX, &sY);
2840 MPrint(sX, sY, str);
2841 }
2842 }
2843 }
2844
2845
UpdateScheduleInfo(void)2846 static void UpdateScheduleInfo(void)
2847 {
2848 INT32 i;
2849 SCHEDULENODE *pSchedule;
2850 if( gpSelected->pSoldier->ubScheduleID )
2851 {
2852 pSchedule = GetSchedule( gpSelected->pSoldier->ubScheduleID );
2853 if( !pSchedule )
2854 {
2855 return;
2856 }
2857 for( i = 0; i < 4; i++ )
2858 { //Update the text and buttons
2859 iEditorButton[MERCS_SCHEDULE_ACTION1 + i]->SetUserData(pSchedule->ubAction[i]);
2860 iEditorButton[MERCS_SCHEDULE_ACTION1 + i]->SpecifyText(gszScheduleActions[pSchedule->ubAction[i]]);
2861 ST::string str;
2862 if( pSchedule->usData1[i] != 0xffff )
2863 str = ST::format("{}", pSchedule->usData1[i]);
2864 iEditorButton[MERCS_SCHEDULE_DATA1A + i]->SpecifyText(str);
2865 str = ST::null;
2866 if( pSchedule->usData2[i] != 0xffff )
2867 str = ST::format("{}", pSchedule->usData2[i]);
2868 iEditorButton[MERCS_SCHEDULE_DATA1B + i]->SpecifyText(str);
2869 if( gubCurrMercMode == MERC_SCHEDULEMODE )
2870 { //Update the text input fields too!
2871 SetExclusive24HourTimeValue( (UINT8)(i+1), pSchedule->usTime[i] );
2872 }
2873 }
2874
2875 //Check or uncheck the checkbox buttons based on the schedule's status.
2876 UnclickEditorButtons( MERCS_SCHEDULE_VARIANCE1, MERCS_SCHEDULE_VARIANCE4 );
2877 if( pSchedule->usFlags & SCHEDULE_FLAGS_VARIANCE1 )
2878 ClickEditorButton( MERCS_SCHEDULE_VARIANCE1 );
2879 if( pSchedule->usFlags & SCHEDULE_FLAGS_VARIANCE2 )
2880 ClickEditorButton( MERCS_SCHEDULE_VARIANCE2 );
2881 if( pSchedule->usFlags & SCHEDULE_FLAGS_VARIANCE3 )
2882 ClickEditorButton( MERCS_SCHEDULE_VARIANCE3 );
2883 if( pSchedule->usFlags & SCHEDULE_FLAGS_VARIANCE4 )
2884 ClickEditorButton( MERCS_SCHEDULE_VARIANCE4 );
2885
2886 //Copy the schedule over to the current global schedule used for editing purposes.
2887 gCurrSchedule = *pSchedule;
2888 DetermineScheduleEditability();
2889 }
2890 else
2891 {
2892 ClearCurrentSchedule();
2893 }
2894 }
2895
2896
2897 static BOOLEAN gfSaveBuffer = FALSE;
2898 static BASIC_SOLDIERCREATE_STRUCT gSaveBufferBasicPlacement;
2899 static SOLDIERCREATE_STRUCT gSaveBufferDetailedPlacement;
2900
2901
CopyMercPlacement(INT32 iMapIndex)2902 void CopyMercPlacement( INT32 iMapIndex )
2903 {
2904 if (g_selected_merc == NULL)
2905 {
2906 ScreenMsg( FONT_MCOLOR_LTRED, MSG_INTERFACE, "Placement not copied because no placement selected." );
2907 return;
2908 }
2909 gfSaveBuffer = TRUE;
2910 gSaveBufferBasicPlacement = *gpSelected->pBasicPlacement;
2911 if( gSaveBufferBasicPlacement.fDetailedPlacement )
2912 {
2913 gSaveBufferDetailedPlacement = *gpSelected->pDetailedPlacement;
2914 }
2915 ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, "Placement copied." );
2916 }
2917
PasteMercPlacement(INT32 iMapIndex)2918 void PasteMercPlacement( INT32 iMapIndex )
2919 {
2920 SOLDIERCREATE_STRUCT tempDetailedPlacement;
2921 INT32 i;
2922
2923 if( !gfSaveBuffer )
2924 {
2925 ScreenMsg( FONT_MCOLOR_LTRED, MSG_INTERFACE, "Placement not pasted as no placement is saved in buffer." );
2926 return;
2927 }
2928
2929 gTempBasicPlacement = gSaveBufferBasicPlacement;
2930
2931 //calculate specific information based on the team.
2932 SoldierBodyType body = BODY_RANDOM;
2933 switch( iDrawMode )
2934 {
2935 case DRAW_MODE_ENEMY:
2936 gTempBasicPlacement.bTeam = ENEMY_TEAM;
2937 gTempBasicPlacement.ubSoldierClass = gubSoldierClass;
2938 break;
2939 case DRAW_MODE_CREATURE:
2940 gTempBasicPlacement.bTeam = CREATURE_TEAM;
2941 body = gbCurrCreature;
2942 break;
2943 case DRAW_MODE_REBEL:
2944 gTempBasicPlacement.bTeam = MILITIA_TEAM;
2945 break;
2946 case DRAW_MODE_CIVILIAN:
2947 gTempBasicPlacement.bTeam = CIV_TEAM;
2948 gTempBasicPlacement.ubCivilianGroup = gubCivGroup;
2949 if (giCurrentTilesetID == CAVES_1)
2950 {
2951 gTempBasicPlacement.ubSoldierClass = SOLDIER_CLASS_MINER;
2952 }
2953 break;
2954 }
2955 gTempBasicPlacement.bBodyType = body;
2956
2957 if( IsLocationSittable( iMapIndex, gfRoofPlacement ) )
2958 {
2959 SOLDIERINITNODE *pNode;
2960
2961 //Set up some general information.
2962 //gTempBasicPlacement.fDetailedPlacement = TRUE;
2963 gTempBasicPlacement.usStartingGridNo = (UINT16)iMapIndex;
2964
2965 //Generate detailed placement information given the temp placement information.
2966 if( gTempBasicPlacement.fDetailedPlacement )
2967 {
2968 gTempDetailedPlacement = gSaveBufferDetailedPlacement;
2969 }
2970 else
2971 {
2972 CreateDetailedPlacementGivenBasicPlacementInfo( &gTempDetailedPlacement, &gTempBasicPlacement );
2973 }
2974
2975 //Set the sector information -- probably unnecessary.
2976 gTempDetailedPlacement.sSectorX = gWorldSectorX;
2977 gTempDetailedPlacement.sSectorY = gWorldSectorY;
2978
2979 if( gTempBasicPlacement.fDetailedPlacement )
2980 {
2981 CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( &tempDetailedPlacement, &gTempDetailedPlacement, &gTempBasicPlacement );
2982 }
2983 else
2984 {
2985 tempDetailedPlacement = gTempDetailedPlacement;
2986 }
2987
2988 //Create the soldier, but don't place it yet.
2989 SOLDIERTYPE* const pSoldier = TacticalCreateSoldier(tempDetailedPlacement);
2990 if (pSoldier != NULL)
2991 {
2992 pSoldier->bVisible = 1;
2993 pSoldier->bLastRenderVisibleValue = 1;
2994 //Set up the soldier in the list, so we can track the soldier in the
2995 //future (saving, loading, strategic AI)
2996 pNode = AddBasicPlacementToSoldierInitList(gTempBasicPlacement);
2997 pNode->pSoldier = pSoldier;
2998 if( gSaveBufferBasicPlacement.fDetailedPlacement )
2999 { //Add the static detailed placement information in the same newly created node as the basic placement.
3000 //read static detailed placement from file
3001 //allocate memory for new static detailed placement
3002 gTempBasicPlacement.fDetailedPlacement = TRUE;
3003 gTempBasicPlacement.fPriorityExistance = gSaveBufferBasicPlacement.fPriorityExistance;
3004 pNode->pDetailedPlacement = new SOLDIERCREATE_STRUCT{};
3005 //copy the file information from temp var to node in list.
3006 *pNode->pDetailedPlacement = gSaveBufferDetailedPlacement;
3007 }
3008
3009 //Add the soldier to physically appear on the map now.
3010 AddSoldierToSectorNoCalculateDirection(pSoldier);
3011 IndicateSelectedMerc(pSoldier->ubID);
3012
3013 //Move him to the roof if intended and possible.
3014 if( gfRoofPlacement && FlatRoofAboveGridNo( iMapIndex ) )
3015 {
3016 gpSelected->pBasicPlacement->fOnRoof = TRUE;
3017 if( gpSelected->pDetailedPlacement )
3018 gpSelected->pDetailedPlacement->fOnRoof = TRUE;
3019 SetSoldierHeight( gpSelected->pSoldier, SECOND_LEVEL_Z_OFFSET );
3020 }
3021 UnclickEditorButtons( FIRST_MERCS_INVENTORY_BUTTON, LAST_MERCS_INVENTORY_BUTTON );
3022 for( i = FIRST_MERCS_INVENTORY_BUTTON; i <= LAST_MERCS_INVENTORY_BUTTON; i++ )
3023 {
3024 SetEnemyDroppableStatus( gbMercSlotTypes[i-FIRST_MERCS_INVENTORY_BUTTON], FALSE );
3025 }
3026 ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, "Placement pasted." );
3027 }
3028 else
3029 {
3030 ScreenMsg( FONT_MCOLOR_LTRED, MSG_INTERFACE, "Placement not pasted as the maximum number of placements for this team is already used." );
3031 }
3032 }
3033 }
3034