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