1 #include "Directories.h"
2 #include "FileMan.h"
3 #include "Font.h"
4 #include "Handle_Items.h"
5 #include "Isometric_Utils.h"
6 #include "LoadSaveData.h"
7 #include "LoadSaveObjectType.h"
8 #include "Local.h"
9 #include "HImage.h"
10 #include "Map_Screen_Interface_Bottom.h"
11 #include "Text_Utils.h"
12 #include "TileDef.h"
13 #include "Timer_Control.h"
14 #include "VObject.h"
15 #include "SysUtil.h"
16 #include "Overhead.h"
17 #include "MouseSystem.h"
18 #include "Button_System.h"
19 #include "Interface.h"
20 #include "VSurface.h"
21 #include "Input.h"
22 #include "Handle_UI.h"
23 #include "RenderWorld.h"
24 #include "Cursors.h"
25 #include "Radar_Screen.h"
26 #include "Font_Control.h"
27 #include "Render_Dirty.h"
28 #include "Interface_Panels.h"
29 #include "Animation_Control.h"
30 #include "Soldier_Control.h"
31 #include "PathAI.h"
32 #include "Weapons.h"
33 #include "Faces.h"
34 #include "MapScreen.h"
35 #include "Message.h"
36 #include "Text.h"
37 #include "Cursor_Control.h"
38 #include "Interface_Cursors.h"
39 #include "Interface_Utils.h"
40 #include "Interface_Items.h"
41 #include "WordWrap.h"
42 #include "Interface_Control.h"
43 #include "VObject_Blitters.h"
44 #include "World_Items.h"
45 #include "Points.h"
46 #include "Physics.h"
47 #include "Finances.h"
48 #include "UI_Cursors.h"
49 #include "ShopKeeper_Interface.h"
50 #include "Dialogue_Control.h"
51 #include "English.h"
52 #include "Keys.h"
53 #include "StrategicMap.h"
54 #include "Arms_Dealer_Init.h"
55 #include "Soldier_Macros.h"
56 #include "Game_Clock.h"
57 #include "Squads.h"
58 #include "LaptopSave.h"
59 #include "MessageBoxScreen.h"
60 #include "GameSettings.h"
61 #include "Map_Screen_Interface_Map_Inventory.h"
62 #include "Quests.h"
63 #include "Map_Screen_Interface.h"
64 #include "Campaign_Types.h"
65 #include "OppList.h"
66 #include "LOS.h"
67 #include "Map_Screen_Interface_Map.h"
68 #include "JAScreens.h"
69 #include "ScreenIDs.h"
70 #include "Video.h"
71 #include "MemMan.h"
72 #include "Debug.h"
73 #include "Items.h"
74 #include "UILayout.h"
75 
76 #include "CalibreModel.h"
77 #include "ContentManager.h"
78 #include "GameInstance.h"
79 #include "MagazineModel.h"
80 #include "Soldier.h"
81 #include "WeaponModels.h"
82 #include "policy/GamePolicy.h"
83 #include "Logger.h"
84 #include "MercProfile.h"
85 
86 #include <string_theory/format>
87 #include <string_theory/string>
88 
89 #include <algorithm>
90 #include <iterator>
91 
92 #define ITEMDESC_FONT					BLOCKFONT2
93 #define ITEMDESC_FONTSHADOW2				32
94 
95 #define ITEMDESC_FONTAPFORE				218
96 #define ITEMDESC_FONTHPFORE				24
97 #define ITEMDESC_FONTBSFORE				125
98 #define ITEMDESC_FONTHEFORE				75
99 #define ITEMDESC_FONTHEAPFORE				76
100 
101 #define ITEMDESC_AMMO_FORE				209
102 
103 #define ITEMDESC_FONTHIGHLIGHT				FONT_MCOLOR_WHITE
104 
105 #define STATUS_BAR_SHADOW				FROMRGB( 140, 136,  119 )
106 #define STATUS_BAR					FROMRGB( 201, 172,  133 )
107 #define DESC_STATUS_BAR_SHADOW				STATUS_BAR_SHADOW
108 #define DESC_STATUS_BAR				STATUS_BAR
109 
110 #define INV_BAR_DX					5
111 #define INV_BAR_DY					21
112 
113 #define RENDER_ITEM_NOSTATUS				20
114 #define RENDER_ITEM_ATTACHMENT1			200
115 
116 #define ITEM_STATS_WIDTH				26
117 #define ITEM_STATS_HEIGHT				8
118 #define MAX_STACK_POPUP_WIDTH				6
119 
120 #define ITEMDESC_START_X				214
121 #define ITEMDESC_START_Y				1 + INV_INTERFACE_START_Y
122 #define ITEMDESC_HEIGHT				133
123 #define ITEMDESC_WIDTH					320
124 #define MAP_ITEMDESC_HEIGHT				268
125 #define MAP_ITEMDESC_WIDTH				272
126 #define ITEMDESC_ITEM_X				(8 + gsInvDescX)
127 #define ITEMDESC_ITEM_Y				(11 + gsInvDescY)
128 
129 #define CAMO_REGION_HEIGHT				75
130 #define CAMO_REGION_WIDTH				75
131 
132 #define BULLET_SING_X					(222 + gsInvDescX)
133 #define BULLET_SING_Y					(49 + gsInvDescY)
134 #define BULLET_BURST_X					(263 + gsInvDescX)
135 #define BULLET_BURST_Y					(49 + gsInvDescY)
136 #define BULLET_WIDTH					3
137 
138 #define MAP_BULLET_SING_X				(77 + gsInvDescX)
139 #define MAP_BULLET_SING_Y				(135 + gsInvDescY)
140 #define MAP_BULLET_BURST_X				(117 + gsInvDescX)
141 #define MAP_BULLET_BURST_Y				(135 + gsInvDescY)
142 
143 static const SGPBox g_itemdesc_desc_box            = { 11,  80, 301,  0 };
144 static const SGPBox g_itemdesc_pros_cons_box       = { 11, 110, 301, 10 };
145 static const SGPBox g_itemdesc_item_status_box     = {  6,  60,   2, 51 };
146 
147 static const SGPBox g_map_itemdesc_desc_box        = { 23, 170, 220,  0 };
148 static const SGPBox g_map_itemdesc_pros_cons_box   = { 23, 230, 220, 10 };
149 static const SGPBox g_map_itemdesc_item_status_box = { 18,  54,   2, 42 };
150 
151 #define DOTDOTDOT "..."
152 #define COMMA_AND_SPACE ", "
153 
154 #define ITEM_PROS_AND_CONS( usItem )			( ( GCM->getItem(usItem)->isGun()) )
155 
156 #define ITEMDESC_AMMO_TEXT_X				3
157 #define ITEMDESC_AMMO_TEXT_Y				2
158 #define ITEMDESC_AMMO_TEXT_WIDTH			31
159 
160 #define ITEM_BAR_HEIGHT				20
161 
162 #define ITEM_FONT					TINYFONT1
163 
164 #define EXCEPTIONAL_DAMAGE				30
165 #define EXCEPTIONAL_WEIGHT				20
166 #define EXCEPTIONAL_RANGE				300
167 #define EXCEPTIONAL_MAGAZINE				30
168 #define EXCEPTIONAL_AP_COST				7
169 #define EXCEPTIONAL_BURST_SIZE				5
170 #define EXCEPTIONAL_RELIABILITY			2
171 #define EXCEPTIONAL_REPAIR_EASE			2
172 
173 #define BAD_DAMAGE					23
174 #define BAD_WEIGHT					45
175 #define BAD_RANGE					150
176 #define BAD_MAGAZINE					10
177 #define BAD_AP_COST					11
178 #define BAD_RELIABILITY				-2
179 #define BAD_REPAIR_EASE				-2
180 
181 #define KEYRING_X      496
182 #define KEYRING_Y      (INV_INTERFACE_START_Y + 106)
183 #define MAP_KEYRING_X (STD_SCREEN_X + 217)
184 #define MAP_KEYRING_Y (STD_SCREEN_Y + 271)
185 #define KEYRING_WIDTH   29
186 #define KEYRING_HEIGHT  23
187 #define TACTICAL_INVENTORY_KEYRING_GRAPHIC_OFFSET_X 215
188 //enum used for the money buttons
189 enum
190 {
191 	M_1000,
192 	M_100,
193 	M_10,
194 	M_DONE,
195 };
196 
197 BOOLEAN gfAddingMoneyToMercFromPlayersAccount;
198 
199 MOUSE_REGION gInvDesc;
200 
201 OBJECTTYPE *gpItemPointer;
202 OBJECTTYPE gItemPointer;
203 BOOLEAN gfItemPointerDifferentThanDefault = FALSE;
204 SOLDIERTYPE *gpItemPointerSoldier;
205 INT8 gbItemPointerSrcSlot;
206 static UINT16 gusItemPointer = 255;
207 static UINT32 guiNewlyPlacedItemTimer = 0;
208 static BOOLEAN gfBadThrowItemCTGH;
209 BOOLEAN gfDontChargeAPsToPickup = FALSE;
210 static BOOLEAN gbItemPointerLocateGood = FALSE;
211 
212 // ITEM DESCRIPTION BOX STUFF
213 static SGPVObject *guiItemDescBox;
214 static SGPVObject *guiMapItemDescBox;
215 static SGPVObject *guiItemGraphic;
216 static SGPVObject *guiMoneyGraphicsForDescBox;
217 static SGPVObject *guiBullet;
218 BOOLEAN gfInItemDescBox = FALSE;
219 static UINT32 guiCurrentItemDescriptionScreen=0;
220 OBJECTTYPE *gpItemDescObject = NULL;
221 static BOOLEAN gfItemDescObjectIsAttachment = FALSE;
222 static ST::string gzItemName;
223 static ST::string gzItemDesc;
224 static ST::string gzItemPros;
225 static ST::string gzItemCons;
226 static INT16 gsInvDescX;
227 static INT16 gsInvDescY;
228 static UINT8 gubItemDescStatusIndex;
229 static BUTTON_PICS *giItemDescAmmoButtonImages;
230 static GUIButtonRef giItemDescAmmoButton;
231 static SOLDIERTYPE *gpItemDescSoldier;
232 static BOOLEAN fItemDescDelete = FALSE;
233 MOUSE_REGION gItemDescAttachmentRegions[4];
234 static MOUSE_REGION gProsAndConsRegions[2];
235 
236 static GUIButtonRef guiMoneyButtonBtn[MAX_ATTACHMENTS];
237 static BUTTON_PICS *guiMoneyButtonImage;
238 static BUTTON_PICS *guiMoneyDoneButtonImage;
239 
240 static UINT16 gusOriginalAttachItem[MAX_ATTACHMENTS];
241 static UINT8 gbOriginalAttachStatus[MAX_ATTACHMENTS];
242 static SOLDIERTYPE *gpAttachSoldier;
243 
244 #define gMoneyButtonLoc				(g_ui.m_moneyButtonLoc)
245 #define gMapMoneyButtonLoc				(g_ui.m_MoneyButtonLocMap)
246 static const MoneyLoc gMoneyButtonOffsets[] = { { 0, 0 }, { 34, 0 }, { 0, 32 }, { 34, 32 }, { 8, 22 } };
247 
248 
249 // number of keys on keyring, temp for now
250 #define NUMBER_KEYS_ON_KEYRING				28
251 #define KEY_RING_ROW_WIDTH				7
252 #define MAP_KEY_RING_ROW_WIDTH				4
253 
254 // ITEM STACK POPUP STUFF
255 static BOOLEAN gfInItemStackPopup = FALSE;
256 static SGPVObject* guiItemPopupBoxes;
257 static OBJECTTYPE* gpItemPopupObject;
258 static INT16 gsItemPopupX;
259 static INT16 gsItemPopupY;
260 static MOUSE_REGION gItemPopupRegions[8];
261 static MOUSE_REGION gKeyRingRegions[NUMBER_KEYS_ON_KEYRING];
262 BOOLEAN gfInKeyRingPopup = FALSE;
263 static UINT8 gubNumItemPopups = 0;
264 static MOUSE_REGION gItemPopupRegion;
265 static INT16 gsItemPopupInvX;
266 static INT16 gsItemPopupInvY;
267 static INT16 gsItemPopupInvWidth;
268 static INT16 gsItemPopupInvHeight;
269 
270 static INT16 gsKeyRingPopupInvX;
271 static INT16 gsKeyRingPopupInvY;
272 static INT16 gsKeyRingPopupInvWidth;
273 static INT16 gsKeyRingPopupInvHeight;
274 
275 
276 SOLDIERTYPE *gpItemPopupSoldier;
277 
278 // inventory description done button for mapscreen
279 GUIButtonRef giMapInvDescButton;
280 
281 
282 static BOOLEAN gfItemPopupRegionCallbackEndFix = FALSE;
283 
284 
285 struct INV_DESC_STATS
286 {
287 	INT16 sX;
288 	INT16 sY;
289 	INT16 sValDx;
290 };
291 
292 
293 static const SGPBox gMapDescNameBox = {  7, 65, 247, 8 };
294 static const SGPBox gDescNameBox    = { 16, 67, 291, 8 };
295 
296 static const SGPBox g_desc_item_box_map = { 23, 10, 124, 48 };
297 static const SGPBox g_desc_item_box     = {  9,  9, 117, 55 };
298 
299 static const INV_DESC_STATS gWeaponStats[] =
300 {
301 	{ 202, 25, 83 },
302 	{ 202, 15, 83 },
303 	{ 265, 40, 20 },
304 	{ 202, 40, 32 },
305 	{ 202, 50, 32 },
306 	{ 265, 50, 20 },
307 	{ 234, 50,  0 },
308 	{ 290, 50,  0 }
309 };
310 
311 
312 // displayed AFTER the mass/weight/"Kg" line
313 static const INV_DESC_STATS gMoneyStats[] =
314 {
315 	{ 202, 14, 78 },
316 	{ 212, 25, 78 },
317 	{ 202, 40, 78 },
318 	{ 212, 51, 78 }
319 };
320 
321 // displayed AFTER the mass/weight/"Kg" line
322 static const INV_DESC_STATS gMapMoneyStats[] =
323 {
324 	{ 51,  97, 45 },
325 	{ 61, 107, 75 },
326 	{ 51, 125, 45 },
327 	{ 61, 135, 70 }
328 };
329 
330 
331 static const INV_DESC_STATS gMapWeaponStats[] =
332 {
333 	{  72 - 20,      20 + 80 + 8, 86 },
334 	{  72 - 20,      20 + 80 - 2, 86 },
335 	{  72 - 20 + 65, 40 + 80 + 4, 21 },
336 	{  72 - 20,      40 + 80 + 4, 30 },
337 	{  72 - 20,      53 + 80 + 2, 30 },
338 	{  72 - 20 + 65, 53 + 80 + 2, 25 },
339 	{  86,           53 + 80 + 2,  0 },
340 	{ 145,           53 + 80 + 2,  0 }
341 };
342 
343 
344 struct AttachmentGfxInfo
345 {
346 	SGPBox   item_box; // Bounding box of the item relative to a slot
347 	SGPBox   bar_box;  // Bounding box of the status bar relative to a slot
348 	SGPPoint slot[4];
349 };
350 
351 static const AttachmentGfxInfo g_attachment_info =
352 {
353 	{ 7, 0, 28, 25 },
354 	{ 2, 2,  2, 22 },
355 	{
356 		{ 128, 10 }, { 162, 10 },
357 		{ 128, 36 }, { 162, 36 }
358 	}
359 };
360 
361 static const AttachmentGfxInfo g_map_attachment_info =
362 {
363 	{ 6, 0, 31, 25 },
364 	{ 1, 1,  2, 23 },
365 	{
366 		{ 170,  8 }, { 208,  8 },
367 		{ 170, 34 }, { 208, 34 }
368 	}
369 };
370 
371 
372 static BOOLEAN gfItemDescHelpTextOffset = FALSE;
373 
374 
375 // A STRUCT USED INTERNALLY FOR INV SLOT REGIONS
376 struct INV_REGIONS
377 {
378 	INT16 w;
379 	INT16 h;
380 };
381 
382 // ARRAY FOR INV PANEL INTERFACE ITEM POSITIONS (sX,sY get set via InitInvSlotInterface() )
383 static INV_REGIONS const gSMInvData[] =
384 {
385 #define M(w, h) { w, h }
386 	M(HEAD_INV_SLOT_WIDTH, HEAD_INV_SLOT_HEIGHT), // HELMETPOS
387 	M(VEST_INV_SLOT_WIDTH, VEST_INV_SLOT_HEIGHT), // VESTPOS
388 	M(LEGS_INV_SLOT_WIDTH, LEGS_INV_SLOT_HEIGHT), // LEGPOS,
389 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // HEAD1POS
390 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // HEAD2POS
391 	M(BIG_INV_SLOT_WIDTH,  BIG_INV_SLOT_HEIGHT ), // HANDPOS,
392 	M(BIG_INV_SLOT_WIDTH,  BIG_INV_SLOT_HEIGHT ), // SECONDHANDPOS
393 	M(BIG_INV_SLOT_WIDTH,  BIG_INV_SLOT_HEIGHT ), // BIGPOCK1
394 	M(BIG_INV_SLOT_WIDTH,  BIG_INV_SLOT_HEIGHT ), // BIGPOCK2
395 	M(BIG_INV_SLOT_WIDTH,  BIG_INV_SLOT_HEIGHT ), // BIGPOCK3
396 	M(BIG_INV_SLOT_WIDTH,  BIG_INV_SLOT_HEIGHT ), // BIGPOCK4
397 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK1
398 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK2
399 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK3
400 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK4
401 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK5
402 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK6
403 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  ), // SMALLPOCK7
404 	M(SM_INV_SLOT_WIDTH,   SM_INV_SLOT_HEIGHT  )  // SMALLPOCK8
405 #undef M
406 };
407 
408 
409 struct REMOVE_MONEY
410 {
411 	UINT32 uiTotalAmount;
412 	UINT32 uiMoneyRemaining;
413 	UINT32 uiMoneyRemoving;
414 };
415 static REMOVE_MONEY gRemoveMoney;
416 
417 static MOUSE_REGION gSMInvRegion[NUM_INV_SLOTS];
418 static MOUSE_REGION gKeyRingPanel;
419 static MOUSE_REGION gSMInvCamoRegion;
420 static INT8 gbCompatibleAmmo[NUM_INV_SLOTS];
421 INT8 gbInvalidPlacementSlot[ NUM_INV_SLOTS ];
422 static UINT16 us16BPPItemCyclePlacedItemColors[20];
423 static SGPVObject* guiBodyInvVO[4][2];
424 static SGPVObject* guiGoldKeyVO;
425 INT8 gbCompatibleApplyItem = FALSE;
426 
427 
428 static SGPVObject *guiMapInvSecondHandBlockout;
429 static SGPVObject *guiSecItemHiddenVO;
430 static SGPVObject *guiGUNSM;
431 static SGPVObject *guiP1ITEMS;
432 static SGPVObject *guiP2ITEMS;
433 static SGPVObject *guiP3ITEMS;
434 
435 
AttemptToAddSubstring(ST::string & zDest,const ST::string & zTemp,UINT32 * puiStringLength,UINT32 uiPixLimit)436 static BOOLEAN AttemptToAddSubstring(ST::string& zDest, const ST::string& zTemp, UINT32* puiStringLength, UINT32 uiPixLimit)
437 {
438 	UINT32 uiRequiredStringLength, uiTempStringLength;
439 
440 	uiTempStringLength = StringPixLength( zTemp, ITEMDESC_FONT );
441 	uiRequiredStringLength = *puiStringLength + uiTempStringLength;
442 	if (!zDest.empty())
443 	{
444 		uiRequiredStringLength += StringPixLength( COMMA_AND_SPACE, ITEMDESC_FONT );
445 	}
446 	if (uiRequiredStringLength < uiPixLimit)
447 	{
448 		if (!zDest.empty())
449 		{
450 			zDest += COMMA_AND_SPACE;
451 		}
452 		zDest += zTemp;
453 		*puiStringLength = uiRequiredStringLength;
454 		return( TRUE );
455 	}
456 	else
457 	{
458 		zDest += DOTDOTDOT;
459 		return( FALSE );
460 	}
461 }
462 
463 
GenerateProsString(ST::string & zItemPros,const OBJECTTYPE & o,UINT32 uiPixLimit)464 static void GenerateProsString(ST::string& zItemPros, const OBJECTTYPE& o, UINT32 uiPixLimit)
465 {
466 	UINT32 uiStringLength = 0;
467 	ST::string zTemp;
468 	UINT16 usItem = o.usItem;
469 	UINT8 ubWeight;
470 
471 	zItemPros = ST::null;
472 
473 	ubWeight = GCM->getItem(usItem)->getWeight();
474 	if (GCM->getItem(usItem)->getItemClass() == IC_GUN)
475 	{
476 		ubWeight += GCM->getItem(o.usGunAmmoItem)->getWeight();
477 	}
478 
479 	if (GCM->getItem(usItem)->getWeight() <= EXCEPTIONAL_WEIGHT)
480 	{
481 		zTemp = g_langRes->Message[STR_LIGHT];
482 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
483 		{
484 			return;
485 		}
486 	}
487 
488 	if (GCM->getItem(usItem)->getPerPocket() >= 1) // fits in a small pocket
489 	{
490 		zTemp = g_langRes->Message[STR_SMALL];
491 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
492 		{
493 			return;
494 		}
495 	}
496 
497 	if (GunRange(o) >= EXCEPTIONAL_RANGE)
498 	{
499 		zTemp = g_langRes->Message[STR_LONG_RANGE];
500 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
501 		{
502 			return;
503 		}
504 	}
505 
506 	if (GCM->getWeapon(usItem)->ubImpact >= EXCEPTIONAL_DAMAGE)
507 	{
508 		zTemp = g_langRes->Message[STR_HIGH_DAMAGE];
509 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
510 		{
511 			return;
512 		}
513 	}
514 
515 	if (BaseAPsToShootOrStab(DEFAULT_APS, DEFAULT_AIMSKILL, *gpItemDescObject) <= EXCEPTIONAL_AP_COST)
516 	{
517 		zTemp = g_langRes->Message[STR_QUICK_FIRING];
518 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
519 		{
520 			return;
521 		}
522 	}
523 
524 	if (GCM->getWeapon(usItem)->ubShotsPerBurst >= EXCEPTIONAL_BURST_SIZE || usItem == G11)
525 	{
526 		zTemp = g_langRes->Message[STR_FAST_BURST];
527 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
528 		{
529 			return;
530 		}
531 	}
532 
533 	if (GCM->getWeapon(usItem)->ubMagSize > EXCEPTIONAL_MAGAZINE)
534 	{
535 		zTemp = g_langRes->Message[STR_LARGE_AMMO_CAPACITY];
536 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
537 		{
538 			return;
539 		}
540 	}
541 
542 	if ( GCM->getItem(usItem)->getReliability() >= EXCEPTIONAL_RELIABILITY )
543 	{
544 		zTemp = g_langRes->Message[STR_RELIABLE];
545 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
546 		{
547 			return;
548 		}
549 	}
550 
551 	if ( GCM->getItem(usItem)->getRepairEase() >= EXCEPTIONAL_REPAIR_EASE )
552 	{
553 		zTemp = g_langRes->Message[STR_EASY_TO_REPAIR];
554 		if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) )
555 		{
556 			return;
557 		}
558 	}
559 
560 	if ( zItemPros[0] == 0 )
561 	{
562 		// empty string, so display "None"
563 		if ( ! AttemptToAddSubstring( zItemPros, g_langRes->Message[ STR_NONE ], &uiStringLength, uiPixLimit ) )
564 		{
565 			return;
566 		}
567 	}
568 }
569 
570 
GenerateConsString(ST::string & zItemCons,const OBJECTTYPE & o,UINT32 uiPixLimit)571 static void GenerateConsString(ST::string& zItemCons, const OBJECTTYPE& o, UINT32 uiPixLimit)
572 {
573 	UINT32 uiStringLength = 0;
574 	ST::string zTemp;
575 	UINT8 ubWeight;
576 	UINT16 usItem = o.usItem;
577 
578 	zItemCons = ST::null;
579 
580 	// calculate the weight of the item plus ammunition but not including any attachments
581 	ubWeight = GCM->getItem(usItem)->getWeight();
582 	if (GCM->getItem(usItem)->getItemClass() == IC_GUN)
583 	{
584 		ubWeight += GCM->getItem(o.usGunAmmoItem)->getWeight();
585 	}
586 
587 	if (ubWeight >= BAD_WEIGHT)
588 	{
589 		zTemp = g_langRes->Message[STR_HEAVY];
590 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
591 		{
592 			return;
593 		}
594 	}
595 
596 	if (GunRange(o) <= BAD_RANGE)
597 	{
598 		zTemp = g_langRes->Message[STR_SHORT_RANGE];
599 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
600 		{
601 			return;
602 		}
603 	}
604 
605 	if (GCM->getWeapon(usItem)->ubImpact <= BAD_DAMAGE)
606 	{
607 		zTemp = g_langRes->Message[STR_LOW_DAMAGE];
608 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
609 		{
610 			return;
611 		}
612 	}
613 
614 	if (BaseAPsToShootOrStab(DEFAULT_APS, DEFAULT_AIMSKILL, *gpItemDescObject) >= BAD_AP_COST)
615 	{
616 		zTemp = g_langRes->Message[STR_SLOW_FIRING];
617 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
618 		{
619 			return;
620 		}
621 	}
622 
623 	if (GCM->getWeapon(usItem)->ubShotsPerBurst == 0)
624 	{
625 		zTemp = g_langRes->Message[STR_NO_BURST];
626 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
627 		{
628 			return;
629 		}
630 	}
631 
632 	if (GCM->getWeapon(usItem)->ubMagSize < BAD_MAGAZINE)
633 	{
634 		zTemp = g_langRes->Message[STR_SMALL_AMMO_CAPACITY];
635 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
636 		{
637 			return;
638 		}
639 	}
640 
641 	if ( GCM->getItem(usItem)->getReliability() <= BAD_RELIABILITY )
642 	{
643 		zTemp = g_langRes->Message[STR_UNRELIABLE];
644 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
645 		{
646 			return;
647 		}
648 	}
649 
650 	if ( GCM->getItem(usItem)->getRepairEase() <= BAD_REPAIR_EASE )
651 	{
652 		zTemp = g_langRes->Message[STR_HARD_TO_REPAIR];
653 		if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) )
654 		{
655 			return;
656 		}
657 	}
658 
659 
660 	if ( zItemCons[0] == 0 )
661 	{
662 		// empty string, so display "None"
663 		if ( ! AttemptToAddSubstring( zItemCons, g_langRes->Message[ STR_NONE ], &uiStringLength, uiPixLimit ) )
664 		{
665 			return;
666 		}
667 	}
668 }
669 
670 
InitInvSlotInterface(INV_REGION_DESC const * const pRegionDesc,INV_REGION_DESC const * const pCamoRegion,MOUSE_CALLBACK const INVMoveCallback,MOUSE_CALLBACK const INVClickCallback,MOUSE_CALLBACK const INVMoveCamoCallback,MOUSE_CALLBACK const INVClickCamoCallback)671 void InitInvSlotInterface(INV_REGION_DESC const* const pRegionDesc, INV_REGION_DESC const* const pCamoRegion, MOUSE_CALLBACK const INVMoveCallback, MOUSE_CALLBACK const INVClickCallback, MOUSE_CALLBACK const INVMoveCamoCallback, MOUSE_CALLBACK const INVClickCamoCallback)
672 {
673 	// Load all four body type images
674 	guiBodyInvVO[0][0] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_normal_male.sti");
675 	guiBodyInvVO[0][1] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_normal_male_h.sti");
676 	guiBodyInvVO[1][0] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_figure_large_male.sti");
677 	guiBodyInvVO[1][1] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_figure_large_male_h.sti");
678 	guiBodyInvVO[2][0] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_normal_male.sti");
679 	guiBodyInvVO[2][1] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_normal_male.sti");
680 	guiBodyInvVO[3][0] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_figure_female.sti");
681 	guiBodyInvVO[3][1] = AddVideoObjectFromFile(INTERFACEDIR "/inventory_figure_female_h.sti");
682 
683 	// Add gold key graphic
684 	guiGoldKeyVO = AddVideoObjectFromFile(INTERFACEDIR "/gold_key_button.sti");
685 
686 	// Add camo region
687 	UINT16 const x = pCamoRegion->uX;
688 	UINT16 const y = pCamoRegion->uY;
689 	MSYS_DefineRegion(&gSMInvCamoRegion, x, y, x + CAMO_REGION_WIDTH, y + CAMO_REGION_HEIGHT, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, INVMoveCamoCallback, INVClickCamoCallback);
690 
691 	// Add regions for inventory slots
692 	for (INT32 i = 0; i != NUM_INV_SLOTS; ++i)
693 	{
694 		// Set inventory pocket coordinates from the table passed in
695 		INT16       const  x = pRegionDesc[i].uX;
696 		INT16       const  y = pRegionDesc[i].uY;
697 		INV_REGIONS const& r = gSMInvData[i];
698 		MOUSE_REGION&      m = gSMInvRegion[i];
699 		MSYS_DefineRegion(&m, x, y, x + r.w, y + r.h, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, INVMoveCallback, INVClickCallback);
700 		MSYS_SetRegionUserData(&m, 0, i);
701 	}
702 
703 	std::fill(std::begin(gbCompatibleAmmo), std::end(gbCompatibleAmmo), 0);
704 }
705 
706 
InitKeyRingInterface(MOUSE_CALLBACK KeyRingClickCallback)707 void InitKeyRingInterface(MOUSE_CALLBACK KeyRingClickCallback)
708 {
709 	MSYS_DefineRegion(&gKeyRingPanel, KEYRING_X, KEYRING_Y, KEYRING_X + KEYRING_WIDTH, KEYRING_Y + KEYRING_HEIGHT, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, KeyRingClickCallback);
710 	gKeyRingPanel.SetFastHelpText(TacticalStr[KEYRING_HELP_TEXT]);
711 }
712 
713 
InitMapKeyRingInterface(MOUSE_CALLBACK KeyRingClickCallback)714 void InitMapKeyRingInterface( MOUSE_CALLBACK KeyRingClickCallback )
715 {
716 	MSYS_DefineRegion(&gKeyRingPanel, MAP_KEYRING_X, MAP_KEYRING_Y, MAP_KEYRING_X + KEYRING_WIDTH, MAP_KEYRING_Y + KEYRING_HEIGHT, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, KeyRingClickCallback);
717 	gKeyRingPanel.SetFastHelpText(TacticalStr[KEYRING_HELP_TEXT]);
718 }
719 
720 
EnableKeyRing(BOOLEAN fEnable)721 static void EnableKeyRing(BOOLEAN fEnable)
722 {
723 	if ( fEnable )
724 	{
725 		gKeyRingPanel.Enable();
726 	}
727 	else
728 	{
729 		gKeyRingPanel.Disable();
730 	}
731 }
732 
733 
ShutdownKeyRingInterface(void)734 void ShutdownKeyRingInterface( void )
735 {
736 	MSYS_RemoveRegion( &gKeyRingPanel );
737 }
738 
DisableInvRegions(BOOLEAN fDisable)739 void DisableInvRegions( BOOLEAN fDisable )
740 {
741 	INT32 cnt;
742 
743 	for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
744 	{
745 		if ( fDisable )
746 		{
747 			gSMInvRegion[cnt].Disable();
748 		}
749 		else
750 		{
751 			gSMInvRegion[cnt].Enable();
752 		}
753 	}
754 
755 	if ( fDisable )
756 	{
757 		gSMInvCamoRegion.Disable();
758 		gSM_SELMERCMoneyRegion.Disable();
759 		EnableKeyRing( FALSE );
760 	}
761 	else
762 	{
763 		gSMInvCamoRegion.Enable();
764 		gSM_SELMERCMoneyRegion.Enable();
765 		EnableKeyRing( TRUE );
766 	}
767 
768 }
769 
770 
ShutdownInvSlotInterface()771 void ShutdownInvSlotInterface()
772 {
773 	// Remove all body type panels
774 	for (SGPVObject* (*i)[2] = guiBodyInvVO; i != endof(guiBodyInvVO); ++i)
775 	{
776 		FOR_EACH(SGPVObject*, k, *i) DeleteVideoObject(*k);
777 	}
778 
779 	DeleteVideoObject(guiGoldKeyVO);
780 
781 	FOR_EACH(MOUSE_REGION, i, gSMInvRegion)
782 	{
783 		MSYS_RemoveRegion(&*i);
784 	}
785 
786 	MSYS_RemoveRegion(&gSMInvCamoRegion);
787 }
788 
789 
RenderInvBodyPanel(const SOLDIERTYPE * pSoldier,INT16 sX,INT16 sY)790 void RenderInvBodyPanel(const SOLDIERTYPE* pSoldier, INT16 sX, INT16 sY)
791 {
792 	// Blit body inv, based on body type
793 	INT8 bSubImageIndex = gbCompatibleApplyItem;
794 
795 	BltVideoObject(guiSAVEBUFFER, guiBodyInvVO[pSoldier->ubBodyType][bSubImageIndex], 0, sX, sY);
796 }
797 
798 
INVRenderINVPanelItem(SOLDIERTYPE const & s,INT16 const pocket,DirtyLevel const dirty_level)799 static void INVRenderINVPanelItem(SOLDIERTYPE const& s, INT16 const pocket, DirtyLevel const dirty_level)
800 {
801 	guiCurrentItemDescriptionScreen = guiCurrentScreen;
802 	bool       const  in_map = guiCurrentScreen == MAP_SCREEN;
803 	OBJECTTYPE const& o      = s.inv[pocket];
804 	MOUSE_REGION&     r      = gSMInvRegion[pocket];
805 
806 	bool   hatch_out = false;
807 	UINT16 outline   = SGP_TRANSPARENT;
808 	if (dirty_level == DIRTYLEVEL2)
809 	{
810 		ST::string buf = GetHelpTextForItem(o);
811 		r.SetFastHelpText(buf);
812 
813 		// If it's the second hand and this hand cannot contain anything, remove the
814 		// second hand position graphic
815 		if (pocket == SECONDHANDPOS && GCM->getItem(s.inv[HANDPOS].usItem)->isTwoHanded())
816 		{
817 			if (in_map)
818 			{
819 				BltVideoObject(guiSAVEBUFFER, guiMapInvSecondHandBlockout, 0, STD_SCREEN_X + 14, STD_SCREEN_Y + 218);
820 				RestoreExternBackgroundRect(STD_SCREEN_X + 14, STD_SCREEN_Y + 218, 102, 24);
821 			}
822 			else
823 			{
824 				INT32 const x = 217;
825 				INT32 const y = INV_INTERFACE_START_Y + 108;
826 				BltVideoObject(guiSAVEBUFFER, guiSecItemHiddenVO, 0, x, y);
827 				RestoreExternBackgroundRect(x, y, 72, 28);
828 			}
829 		}
830 
831 		// Check for compatibility with magazines
832 		if (gbCompatibleAmmo[pocket]) outline = Get16BPPColor(FROMRGB(255, 255, 255));
833 	}
834 
835 	INT16 const x = r.X();
836 	INT16 const y = r.Y();
837 
838 	// Now render as normal
839 	DirtyLevel const render_dirty_level =
840 		s.bNewItemCount[pocket] <= 0 ||
841 		gsCurInterfacePanel != SM_PANEL ||
842 		fInterfacePanelDirty == DIRTYLEVEL2 ? dirty_level :
843 		DIRTYLEVEL0; // We have a new item and we are in the right panel
844 	INVRenderItem(guiSAVEBUFFER, &s, o, x, y, r.W(), r.H(), render_dirty_level, 0, outline);
845 
846 	if (gbInvalidPlacementSlot[pocket])
847 	{
848 		if (pocket != SECONDHANDPOS && !gfSMDisableForItems)
849 		{
850 			// We are in inv panel and our guy is not = cursor guy
851 			hatch_out = true;
852 		}
853 	}
854 	else
855 	{
856 		if (guiCurrentScreen == SHOPKEEPER_SCREEN &&
857 			ShouldSoldierDisplayHatchOnItem(s.ubProfile, pocket))
858 		{
859 			hatch_out = true;
860 		}
861 	}
862 
863 	if (hatch_out)
864 	{
865 		SGPVSurface* const dst = in_map ? guiSAVEBUFFER : FRAME_BUFFER;
866 		DrawHatchOnInventory(dst, x, y, r.W(), r.H());
867 	}
868 
869 	if (o.usItem != NOTHING)
870 	{
871 		// Add item status bar
872 		DrawItemUIBarEx(o, 0, x - INV_BAR_DX, y + INV_BAR_DY, ITEM_BAR_HEIGHT, Get16BPPColor(STATUS_BAR),
873 				Get16BPPColor(STATUS_BAR_SHADOW), guiSAVEBUFFER);
874 	}
875 }
876 
877 
HandleRenderInvSlots(SOLDIERTYPE const & s,DirtyLevel const dirty_level)878 void HandleRenderInvSlots(SOLDIERTYPE const& s, DirtyLevel const dirty_level)
879 {
880 	if (InItemDescriptionBox() || InItemStackPopup() || InKeyRingPopup()) return;
881 
882 	for (INT32 i = 0; i != NUM_INV_SLOTS; ++i)
883 	{
884 		INVRenderINVPanelItem(s, i, dirty_level);
885 	}
886 
887 	if (KeyExistsInKeyRing(s, ANYKEY))
888 	{
889 		// blit gold key here?
890 		INT32 x;
891 		INT32 y;
892 		if (guiCurrentItemDescriptionScreen == MAP_SCREEN)
893 		{
894 			x = MAP_KEYRING_X;
895 			y = MAP_KEYRING_Y;
896 		}
897 		else
898 		{
899 			x = KEYRING_X;
900 			y = KEYRING_Y;
901 		}
902 		BltVideoObject(guiSAVEBUFFER, guiGoldKeyVO, 0, x, y);
903 		RestoreExternBackgroundRect(x, y, KEYRING_WIDTH, KEYRING_HEIGHT);
904 	}
905 }
906 
907 
CompatibleAmmoForGun(const OBJECTTYPE * pTryObject,const OBJECTTYPE * pTestObject)908 static bool CompatibleAmmoForGun(const OBJECTTYPE* pTryObject, const OBJECTTYPE* pTestObject)
909 {
910 	if ( ( GCM->getItem(pTryObject->usItem)->isAmmo() ) )
911 	{
912 		return GCM->getWeapon( pTestObject->usItem )->matches(GCM->getItem(pTryObject->usItem)->asAmmo()->calibre);
913 	}
914 	return false;
915 }
916 
917 
CompatibleGunForAmmo(const OBJECTTYPE * pTryObject,const OBJECTTYPE * pTestObject)918 static bool CompatibleGunForAmmo(const OBJECTTYPE* pTryObject, const OBJECTTYPE* pTestObject)
919 {
920 	if ( ( GCM->getItem(pTryObject->usItem)->isGun()) )
921 	{
922 		return GCM->getWeapon( pTryObject->usItem )->matches(GCM->getItem(pTestObject->usItem)->asAmmo()->calibre);
923 	}
924 	return false;
925 }
926 
927 
CompatibleItemForApplyingOnMerc(const OBJECTTYPE * const test)928 static BOOLEAN CompatibleItemForApplyingOnMerc(const OBJECTTYPE* const test)
929 {
930 	// ATE: If in mapscreen, return false always....
931 	if (fInMapMode) return FALSE;
932 
933 	switch (test->usItem)
934 	{
935 		// ATE: Would be nice to have flag here to check for these types....
936 		case CAMOUFLAGEKIT:
937 		case ADRENALINE_BOOSTER:
938 		case REGEN_BOOSTER:
939 		case SYRINGE_3:
940 		case SYRINGE_4:
941 		case SYRINGE_5:
942 		case ALCOHOL:
943 		case WINE:
944 		case BEER:
945 		case CANTEEN:
946 		case JAR_ELIXIR:
947 			return TRUE;
948 
949 		default: return FALSE;
950 	}
951 }
952 
953 
SoldierContainsAnyCompatibleStuff(const SOLDIERTYPE * const s,const OBJECTTYPE * const test)954 static BOOLEAN SoldierContainsAnyCompatibleStuff(const SOLDIERTYPE* const s, const OBJECTTYPE* const test)
955 {
956 	const UINT16 item_class = GCM->getItem(test->usItem)->getItemClass();
957 	if (item_class & IC_GUN)
958 	{
959 		CFOR_EACH_SOLDIER_INV_SLOT(i, *s)
960 		{
961 			if (CompatibleAmmoForGun(i, test)) return TRUE;
962 		}
963 	}
964 	else if (item_class & IC_AMMO)
965 	{
966 		CFOR_EACH_SOLDIER_INV_SLOT(i, *s)
967 		{
968 			if (CompatibleGunForAmmo(i, test)) return TRUE;
969 		}
970 	}
971 
972 	// ATE: Put attachment checking here.....
973 
974 	return FALSE;
975 }
976 
977 
HandleAnyMercInSquadHasCompatibleStuff(const OBJECTTYPE * const o)978 void HandleAnyMercInSquadHasCompatibleStuff(const OBJECTTYPE* const o)
979 {
980 	const INT32 squad = CurrentSquad();
981 	if (squad == NUMBER_OF_SQUADS) return;
982 
983 	FOR_EACH_IN_SQUAD(i, squad)
984 	{
985 		SOLDIERTYPE const* const s = *i;
986 		FACETYPE*          const f = s->face;
987 		Assert(f || s->uiStatusFlags & SOLDIER_VEHICLE);
988 		if (f == NULL) continue;
989 
990 		if (o == NULL)
991 		{
992 			f->fCompatibleItems = FALSE;
993 		}
994 		else if (SoldierContainsAnyCompatibleStuff(s, o))
995 		{
996 			f->fCompatibleItems = TRUE;
997 		}
998 	}
999 }
1000 
1001 
HandleCompatibleAmmoUIForMapScreen(const SOLDIERTYPE * pSoldier,INT32 bInvPos,BOOLEAN fOn,BOOLEAN fFromMerc)1002 BOOLEAN HandleCompatibleAmmoUIForMapScreen(const SOLDIERTYPE* pSoldier, INT32 bInvPos, BOOLEAN fOn, BOOLEAN fFromMerc)
1003 {
1004 	BOOLEAN fFound = FALSE;
1005 	INT32 cnt;
1006 
1007 	const OBJECTTYPE* pTestObject;
1008 	if (!fFromMerc)
1009 	{
1010 		pTestObject = &( pInventoryPoolList[ bInvPos ].o );
1011 	}
1012 	else
1013 	{
1014 		if ( bInvPos == NO_SLOT )
1015 		{
1016 			pTestObject = NULL;
1017 		}
1018 		else
1019 		{
1020 			pTestObject = &(pSoldier->inv[ bInvPos ]);
1021 		}
1022 	}
1023 
1024 	// ATE: If pTest object is NULL, test only for existence of syringes, etc...
1025 	if ( pTestObject == NULL )
1026 	{
1027 		for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1028 		{
1029 			if (CompatibleItemForApplyingOnMerc(&pSoldier->inv[cnt]))
1030 			{
1031 				if ( fOn != gbCompatibleAmmo[ cnt ] )
1032 				{
1033 					fFound = TRUE;
1034 				}
1035 
1036 				// IT's an OK calibere ammo, do something!
1037 				// Render Item with specific color
1038 				gbCompatibleAmmo[ cnt ] = fOn;
1039 
1040 			}
1041 		}
1042 
1043 
1044 		if ( gpItemPointer != NULL )
1045 		{
1046 			if ( CompatibleItemForApplyingOnMerc( gpItemPointer ) )
1047 			{
1048 				// OK, Light up portrait as well.....
1049 				if ( fOn )
1050 				{
1051 					gbCompatibleApplyItem = TRUE;
1052 				}
1053 				else
1054 				{
1055 					gbCompatibleApplyItem = FALSE;
1056 				}
1057 
1058 				fFound = TRUE;
1059 			}
1060 		}
1061 
1062 		if ( fFound )
1063 		{
1064 			fInterfacePanelDirty = DIRTYLEVEL2;
1065 		}
1066 
1067 		return( fFound );
1068 	}
1069 
1070 	if ((!(GCM->getItem(pTestObject->usItem)->getFlags() & ITEM_HIDDEN_ADDON)))
1071 	{
1072 		// First test attachments, which almost any type of item can have....
1073 		for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1074 		{
1075 			OBJECTTYPE const& o = pSoldier->inv[cnt];
1076 
1077 			if (GCM->getItem(o.usItem)->getFlags() & ITEM_HIDDEN_ADDON)
1078 			{
1079 				// don't consider for UI purposes
1080 				continue;
1081 			}
1082 
1083 			UINT16 const a = o.usItem;
1084 			UINT16 const b = pTestObject->usItem;
1085 			if (ValidAttachment(a, b) ||
1086 				ValidAttachment(b, a) ||
1087 				ValidLaunchable(b, a) ||
1088 				ValidLaunchable(a, b))
1089 			{
1090 				if ( fOn != gbCompatibleAmmo[ cnt ] )
1091 				{
1092 					fFound = TRUE;
1093 				}
1094 
1095 				// IT's an OK calibere ammo, do something!
1096 				// Render Item with specific color
1097 				gbCompatibleAmmo[ cnt ] = fOn;
1098 			}
1099 		}
1100 	}
1101 
1102 
1103 	if ( ( GCM->getItem(pTestObject->usItem)->isGun()) )
1104 	{
1105 		for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1106 		{
1107 			if (CompatibleAmmoForGun(&pSoldier->inv[cnt], pTestObject))
1108 			{
1109 				if ( fOn != gbCompatibleAmmo[ cnt ] )
1110 				{
1111 					fFound = TRUE;
1112 				}
1113 
1114 				// IT's an OK calibere ammo, do something!
1115 				// Render Item with specific color
1116 				gbCompatibleAmmo[ cnt ] = fOn;
1117 			}
1118 		}
1119 	}
1120 	else if( ( GCM->getItem(pTestObject->usItem)->isAmmo() ) )
1121 	{
1122 		for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1123 		{
1124 			if (CompatibleGunForAmmo(&pSoldier->inv[cnt], pTestObject))
1125 			{
1126 				if ( fOn != gbCompatibleAmmo[ cnt ] )
1127 				{
1128 					fFound = TRUE;
1129 				}
1130 
1131 				// IT's an OK calibere ammo, do something!
1132 				// Render Item with specific color
1133 				gbCompatibleAmmo[ cnt ] = fOn;
1134 
1135 			}
1136 		}
1137 	}
1138 
1139 
1140 	return( fFound );
1141 }
1142 
HandleCompatibleAmmoUIForMapInventory(SOLDIERTYPE * pSoldier,INT32 bInvPos,INT32 iStartSlotNumber,BOOLEAN fOn,BOOLEAN fFromMerc)1143 BOOLEAN HandleCompatibleAmmoUIForMapInventory( SOLDIERTYPE *pSoldier, INT32 bInvPos, INT32 iStartSlotNumber, BOOLEAN fOn, BOOLEAN fFromMerc   )
1144 {
1145 	// CJC: ATE, needs fixing here!
1146 
1147 	BOOLEAN fFound = FALSE;
1148 	INT32 cnt;
1149 	OBJECTTYPE *pObject, *pTestObject ;
1150 
1151 	if (!fFromMerc)
1152 	{
1153 		pTestObject = &( pInventoryPoolList[ iStartSlotNumber + bInvPos ].o);
1154 	}
1155 	else
1156 	{
1157 		if ( bInvPos == NO_SLOT )
1158 		{
1159 			pTestObject = NULL;
1160 		}
1161 		else
1162 		{
1163 			pTestObject = &(pSoldier->inv[ bInvPos ]);
1164 		}
1165 	}
1166 
1167 	// First test attachments, which almost any type of item can have....
1168 	for ( cnt = 0; cnt < MAP_INVENTORY_POOL_SLOT_COUNT; cnt++ )
1169 	{
1170 		pObject = &( pInventoryPoolList[ iStartSlotNumber + cnt ].o );
1171 
1172 		if ( GCM->getItem(pObject->usItem)->getFlags() & ITEM_HIDDEN_ADDON )
1173 		{
1174 			// don't consider for UI purposes
1175 			continue;
1176 		}
1177 
1178 		if ( ValidAttachment( pObject->usItem, pTestObject->usItem ) ||
1179 			ValidAttachment( pTestObject->usItem, pObject->usItem ) ||
1180 			ValidLaunchable( pTestObject->usItem, pObject->usItem ) ||
1181 			ValidLaunchable( pObject->usItem, pTestObject->usItem ) )
1182 		{
1183 			if ( fOn != fMapInventoryItemCompatable[ cnt ] )
1184 			{
1185 				fFound = TRUE;
1186 			}
1187 
1188 			// IT's an OK calibere ammo, do something!
1189 			// Render Item with specific color
1190 			fMapInventoryItemCompatable[ cnt ] = fOn;
1191 		}
1192 	}
1193 
1194 
1195 	if( ( GCM->getItem(pTestObject->usItem)->isGun()) )
1196 	{
1197 		for ( cnt = 0; cnt < MAP_INVENTORY_POOL_SLOT_COUNT; cnt++ )
1198 		{
1199 			pObject = &( pInventoryPoolList[ iStartSlotNumber + cnt ].o );
1200 
1201 			if ( CompatibleAmmoForGun( pObject, pTestObject ) )
1202 			{
1203 				if ( fOn != fMapInventoryItemCompatable[ cnt ] )
1204 				{
1205 					fFound = TRUE;
1206 				}
1207 
1208 				// IT's an OK calibere ammo, do something!
1209 				// Render Item with specific color
1210 				fMapInventoryItemCompatable[ cnt ] = fOn;
1211 			}
1212 		}
1213 	}
1214 	else if( ( GCM->getItem(pTestObject->usItem)->isAmmo() ) )
1215 	{
1216 		for ( cnt = 0; cnt < MAP_INVENTORY_POOL_SLOT_COUNT; cnt++ )
1217 		{
1218 			pObject = &( pInventoryPoolList[ iStartSlotNumber + cnt ].o );
1219 
1220 			if ( CompatibleGunForAmmo( pObject, pTestObject ) )
1221 			{
1222 				if ( fOn != fMapInventoryItemCompatable[ cnt ] )
1223 				{
1224 					fFound = TRUE;
1225 				}
1226 
1227 				// IT's an OK calibere ammo, do something!
1228 				// Render Item with specific color
1229 				fMapInventoryItemCompatable[ cnt ] = fOn;
1230 
1231 			}
1232 		}
1233 	}
1234 
1235 
1236 	return( fFound );
1237 }
1238 
1239 
InternalHandleCompatibleAmmoUI(const SOLDIERTYPE * pSoldier,const OBJECTTYPE * pTestObject,BOOLEAN fOn)1240 BOOLEAN InternalHandleCompatibleAmmoUI(const SOLDIERTYPE* pSoldier, const OBJECTTYPE* pTestObject, BOOLEAN fOn)
1241 {
1242 	BOOLEAN fFound = FALSE;
1243 	INT32 cnt;
1244 	//BOOLEAN fFoundAttachment = FALSE;
1245 
1246 	// ATE: If pTest object is NULL, test only for existence of syringes, etc...
1247 	if ( pTestObject == NULL )
1248 	{
1249 		for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1250 		{
1251 			if ( CompatibleItemForApplyingOnMerc(&pSoldier->inv[cnt]))
1252 			{
1253 				if ( fOn != gbCompatibleAmmo[ cnt ] )
1254 				{
1255 					fFound = TRUE;
1256 				}
1257 
1258 				// IT's an OK calibere ammo, do something!
1259 				// Render Item with specific color
1260 				gbCompatibleAmmo[ cnt ] = fOn;
1261 
1262 			}
1263 		}
1264 
1265 
1266 		if ( gpItemPointer != NULL )
1267 		{
1268 			if ( CompatibleItemForApplyingOnMerc( gpItemPointer ) )
1269 			{
1270 				// OK, Light up portrait as well.....
1271 				if ( fOn )
1272 				{
1273 					gbCompatibleApplyItem = TRUE;
1274 				}
1275 				else
1276 				{
1277 					gbCompatibleApplyItem = FALSE;
1278 				}
1279 
1280 				fFound = TRUE;
1281 			}
1282 		}
1283 
1284 		if ( fFound )
1285 		{
1286 			fInterfacePanelDirty = DIRTYLEVEL2;
1287 		}
1288 
1289 		return( fFound );
1290 	}
1291 
1292 	// First test attachments, which almost any type of item can have....
1293 	for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1294 	{
1295 		OBJECTTYPE const& o = pSoldier->inv[cnt];
1296 
1297 		if (GCM->getItem(o.usItem)->getFlags() & ITEM_HIDDEN_ADDON)
1298 		{
1299 			// don't consider for UI purposes
1300 			continue;
1301 		}
1302 
1303 		UINT16 const a = o.usItem;
1304 		UINT16 const b = pTestObject->usItem;
1305 		if (ValidAttachment(a, b) ||
1306 			ValidAttachment(b, a) ||
1307 			ValidLaunchable(b, a) ||
1308 			ValidLaunchable(a, b) )
1309 		{
1310 			//fFoundAttachment = TRUE;
1311 
1312 			if ( fOn != gbCompatibleAmmo[ cnt ] )
1313 			{
1314 				fFound = TRUE;
1315 			}
1316 
1317 			// IT's an OK calibere ammo, do something!
1318 			// Render Item with specific color
1319 			gbCompatibleAmmo[ cnt ] = fOn;
1320 		}
1321 	}
1322 
1323 	//if ( !fFoundAttachment )
1324 	//{
1325 		if( ( GCM->getItem(pTestObject->usItem)->isGun()) )
1326 		{
1327 			for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1328 			{
1329 				if (CompatibleAmmoForGun(&pSoldier->inv[cnt], pTestObject))
1330 				{
1331 					if ( fOn != gbCompatibleAmmo[ cnt ] )
1332 					{
1333 						fFound = TRUE;
1334 					}
1335 
1336 					// IT's an OK calibere ammo, do something!
1337 					// Render Item with specific color
1338 					gbCompatibleAmmo[ cnt ] = fOn;
1339 				}
1340 			}
1341 		}
1342 
1343 		else if( ( GCM->getItem(pTestObject->usItem)->isAmmo() ) )
1344 		{
1345 			for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1346 			{
1347 				if (CompatibleGunForAmmo(&pSoldier->inv[cnt], pTestObject))
1348 				{
1349 					if ( fOn != gbCompatibleAmmo[ cnt ] )
1350 					{
1351 						fFound = TRUE;
1352 					}
1353 
1354 					// IT's an OK calibere ammo, do something!
1355 					// Render Item with specific color
1356 					gbCompatibleAmmo[ cnt ] = fOn;
1357 
1358 				}
1359 			}
1360 		}
1361 		else if ( CompatibleItemForApplyingOnMerc( pTestObject ) )
1362 		{
1363 			//If we are currently NOT in the Shopkeeper interface
1364 			if (guiCurrentScreen != SHOPKEEPER_SCREEN)
1365 			{
1366 				fFound = TRUE;
1367 				gbCompatibleApplyItem = fOn;
1368 			}
1369 		}
1370 	//}
1371 
1372 
1373 	if ( !fFound )
1374 	{
1375 		for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1376 		{
1377 			if ( gbCompatibleAmmo[ cnt ] )
1378 			{
1379 				fFound = TRUE;
1380 				gbCompatibleAmmo[ cnt ] = FALSE;
1381 			}
1382 
1383 			if ( gbCompatibleApplyItem )
1384 			{
1385 				fFound = TRUE;
1386 				gbCompatibleApplyItem = FALSE;
1387 			}
1388 		}
1389 	}
1390 
1391 	if ( fFound )
1392 	{
1393 		fInterfacePanelDirty = DIRTYLEVEL2;
1394 	}
1395 
1396 	return( fFound );
1397 
1398 }
1399 
1400 
ResetCompatibleItemArray()1401 void ResetCompatibleItemArray()
1402 {
1403 	FOR_EACH(INT8, i, gbCompatibleAmmo) *i = FALSE;
1404 }
1405 
1406 
HandleCompatibleAmmoUI(const SOLDIERTYPE * pSoldier,INT8 bInvPos,BOOLEAN fOn)1407 BOOLEAN HandleCompatibleAmmoUI(const SOLDIERTYPE* pSoldier, INT8 bInvPos, BOOLEAN fOn)
1408 {
1409 	INT32 cnt;
1410 
1411 	//if we are in the shopkeeper interface
1412 	const OBJECTTYPE* pTestObject;
1413 	if (guiCurrentScreen == SHOPKEEPER_SCREEN)
1414 	{
1415 		// if the inventory position is -1, this is a flag from the Shopkeeper interface screen
1416 		//indicating that we are to use a different object to do the search
1417 		if( bInvPos == -1 )
1418 		{
1419 			if( fOn )
1420 			{
1421 				if( gpHighLightedItemObject )
1422 				{
1423 					pTestObject = gpHighLightedItemObject;
1424 					//gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
1425 				}
1426 				else
1427 					return( FALSE );
1428 			}
1429 			else
1430 			{
1431 				gpHighLightedItemObject = NULL;
1432 
1433 				for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1434 				{
1435 					if ( gbCompatibleAmmo[ cnt ] )
1436 					{
1437 						gbCompatibleAmmo[ cnt ] = FALSE;
1438 					}
1439 				}
1440 
1441 				gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;
1442 				return( TRUE );
1443 			}
1444 		}
1445 		else
1446 		{
1447 			if( fOn )
1448 			{
1449 				pTestObject = &(pSoldier->inv[ bInvPos ]);
1450 				gpHighLightedItemObject = pTestObject;
1451 			}
1452 			else
1453 			{
1454 				pTestObject = &(pSoldier->inv[ bInvPos ]);
1455 				gpHighLightedItemObject = NULL;
1456 				gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;
1457 			}
1458 		}
1459 	}
1460 	else
1461 	{
1462 		//if( fOn )
1463 
1464 		if ( bInvPos == NO_SLOT )
1465 		{
1466 			pTestObject = NULL;
1467 		}
1468 		else
1469 		{
1470 			pTestObject = &(pSoldier->inv[ bInvPos ]);
1471 		}
1472 
1473 	}
1474 
1475 	return( InternalHandleCompatibleAmmoUI( pSoldier, pTestObject, fOn ) );
1476 
1477 }
1478 
1479 
HandleNewlyAddedItems(SOLDIERTYPE & s,DirtyLevel * const dirty_level)1480 void HandleNewlyAddedItems(SOLDIERTYPE& s, DirtyLevel* const dirty_level)
1481 {
1482 	// If item description up, stop
1483 	if (gfInItemDescBox) return;
1484 
1485 	for (UINT32 i = 0; i != NUM_INV_SLOTS; ++i)
1486 	{
1487 		INT8& new_item_count = s.bNewItemCount[i];
1488 		if (new_item_count == -2)
1489 		{ // Stop
1490 			*dirty_level   = DIRTYLEVEL2;
1491 			new_item_count = 0;
1492 		}
1493 
1494 		if (new_item_count <= 0) continue;
1495 		OBJECTTYPE const& o        = s.inv[i];
1496 		if (o.usItem == NOTHING) continue;
1497 		MOUSE_REGION const& r      = gSMInvRegion[i];
1498 		UINT16       const  colour = us16BPPItemCyclePlacedItemColors[s.bNewItemCycleCount[i]];
1499 		INVRenderItem(guiSAVEBUFFER, &s, o, r.X(), r.Y(), r.W(), r.H(), DIRTYLEVEL2, 0, colour);
1500 	}
1501 }
1502 
1503 
CheckForAnyNewlyAddedItems(SOLDIERTYPE * pSoldier)1504 void CheckForAnyNewlyAddedItems(SOLDIERTYPE *pSoldier )
1505 {
1506 	UINT32 cnt;
1507 
1508 	// OK, l0ok for any new...
1509 	for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
1510 	{
1511 		if ( pSoldier->bNewItemCount[ cnt ] == -1 )
1512 		{
1513 			pSoldier->bNewItemCount[ cnt ]	= NEW_ITEM_CYCLES - 1;
1514 		}
1515 	}
1516 }
1517 
1518 
DegradeNewlyAddedItems()1519 void DegradeNewlyAddedItems( )
1520 {
1521 	// If time done
1522 	const UINT32 uiTime = GetJA2Clock();
1523 	if (uiTime - guiNewlyPlacedItemTimer <= 100) return;
1524 
1525 	guiNewlyPlacedItemTimer = uiTime;
1526 
1527 	for (UINT32 cnt2 = 0; cnt2 < NUM_TEAM_SLOTS; ++cnt2)
1528 	{
1529 		SOLDIERTYPE* const s = GetPlayerFromInterfaceTeamSlot(cnt2);
1530 		if (s == NULL) continue;
1531 
1532 		for (UINT32 cnt = 0; cnt < NUM_INV_SLOTS; ++cnt)
1533 		{
1534 			if (s->bNewItemCount[cnt] == 0) continue;
1535 
1536 			// Decrement all the time!
1537 			s->bNewItemCycleCount[cnt]--;
1538 			if (s->bNewItemCycleCount[cnt] != 0) continue;
1539 
1540 			// OK, cycle down....
1541 			s->bNewItemCount[cnt]--;
1542 			if (s->bNewItemCount[cnt] == 0)
1543 			{
1544 				// Stop...
1545 				s->bNewItemCount[cnt] = -2;
1546 			}
1547 			else
1548 			{
1549 				// Reset!
1550 				s->bNewItemCycleCount[cnt] = NEW_ITEM_CYCLE_COUNT;
1551 			}
1552 		}
1553 	}
1554 }
1555 
GetAttachmentHintColor(const OBJECTTYPE * o)1556 UINT8 GetAttachmentHintColor(const OBJECTTYPE* o) {
1557 	return FindAttachmentByClass(o, IC_LAUNCHER) == NO_SLOT ? FONT_GREEN : FONT_YELLOW;
1558 }
1559 
1560 
INVRenderItem(SGPVSurface * const buffer,SOLDIERTYPE const * const s,OBJECTTYPE const & o,INT16 const sX,INT16 const sY,INT16 const sWidth,INT16 const sHeight,DirtyLevel const dirty_level,UINT8 const ubStatusIndex,INT16 const outline_colour)1561 void INVRenderItem(SGPVSurface* const buffer, SOLDIERTYPE const* const s, OBJECTTYPE const& o, INT16 const sX, INT16 const sY, INT16 const sWidth, INT16 const sHeight, DirtyLevel const dirty_level, UINT8 const ubStatusIndex, INT16 const outline_colour)
1562 {
1563 	if (o.usItem    == NOTHING)     return;
1564 	if (dirty_level == DIRTYLEVEL0) return;
1565 
1566 	const ItemModel * item =
1567 		ubStatusIndex < RENDER_ITEM_ATTACHMENT1 ? GCM->getItem(o.usItem) :
1568 		GCM->getItem(o.usAttachItem[ubStatusIndex - RENDER_ITEM_ATTACHMENT1]);
1569 
1570 	if (dirty_level == DIRTYLEVEL2)
1571 	{
1572 		// Center the object in the slot
1573 		SGPVObject  const& item_vo = GetInterfaceGraphicForItem(item);
1574 		UINT8       const  gfx_idx = item->getGraphicNum();
1575 		ETRLEObject const& e       = item_vo.SubregionProperties(gfx_idx);
1576 		INT16       const  cx      = sX + (sWidth  - e.usWidth)  / 2 - e.sOffsetX;
1577 		INT16       const  cy      = sY + (sHeight - e.usHeight) / 2 - e.sOffsetY;
1578 
1579 		if (gamepolicy(f_draw_item_shadow))
1580 		{
1581 			BltVideoObjectOutlineShadow(buffer, &item_vo, gfx_idx, cx - 2, cy + 2);
1582 		}
1583 		BltVideoObjectOutline(      buffer, &item_vo, gfx_idx, cx,     cy, outline_colour);
1584 
1585 		if (buffer == FRAME_BUFFER)
1586 		{
1587 			InvalidateRegion(sX, sY, sX + sWidth, sY + sHeight);
1588 		}
1589 		else
1590 		{
1591 			RestoreExternBackgroundRect(sX, sY, sWidth, sHeight);
1592 		}
1593 	}
1594 
1595 	if (ubStatusIndex < RENDER_ITEM_ATTACHMENT1)
1596 	{
1597 		SetFont(ITEM_FONT);
1598 		SetFontBackground(FONT_MCOLOR_BLACK);
1599 
1600 		if (item->getItemClass() == IC_GUN && o.usItem != ROCKET_LAUNCHER)
1601 		{
1602 			// Display free rounds remianing
1603 			UINT8 colour;
1604 			switch (o.ubGunAmmoType)
1605 			{
1606 				case AMMO_AP:
1607 				case AMMO_SUPER_AP: colour = ITEMDESC_FONTAPFORE;   break;
1608 				case AMMO_HP:       colour = ITEMDESC_FONTHPFORE;   break;
1609 				case AMMO_BUCKSHOT: colour = ITEMDESC_FONTBSFORE;   break;
1610 				case AMMO_HE:       colour = ITEMDESC_FONTHEFORE;   break;
1611 				case AMMO_HEAT:     colour = ITEMDESC_FONTHEAPFORE; break;
1612 				default:            colour = FONT_MCOLOR_DKGRAY;    break;
1613 			}
1614 			SetFontForeground(colour);
1615 
1616 			const INT16 sNewX = sX + 1;
1617 			const INT16 sNewY = sY + sHeight - 10;
1618 			if (buffer == guiSAVEBUFFER)
1619 			{
1620 				RestoreExternBackgroundRect(sNewX, sNewY, 20, 15);
1621 			}
1622 			GPrintInvalidate(sNewX, sNewY, ST::format("{}", o.ubGunShotsLeft));
1623 
1624 			// Display 'JAMMED' if we are jammed
1625 			if (o.bGunAmmoStatus < 0)
1626 			{
1627 				SetFontForeground(FONT_MCOLOR_RED);
1628 
1629 				ST::string jammed =
1630 					sWidth >= BIG_INV_SLOT_WIDTH - 10 ?
1631 						TacticalStr[JAMMED_ITEM_STR] :
1632 						TacticalStr[SHORT_JAMMED_GUN];
1633 
1634 				INT16 cx;
1635 				INT16 cy;
1636 				FindFontCenterCoordinates(sX, sY, sWidth, sHeight, jammed, ITEM_FONT, &cx, &cy);
1637 				GPrintInvalidate(cx, cy, jammed);
1638 			}
1639 		}
1640 		else if (ubStatusIndex != RENDER_ITEM_NOSTATUS && o.ubNumberOfObjects > 1)
1641 		{
1642 			// Display # of items
1643 			SetFontForeground(FONT_GRAY4);
1644 
1645 			ST::string pStr = ST::format("{}", o.ubNumberOfObjects);
1646 
1647 			const UINT16 uiStringLength = StringPixLength(pStr, ITEM_FONT);
1648 			const INT16  sNewX          = sX + sWidth - uiStringLength - 4;
1649 			const INT16  sNewY          = sY + sHeight - 10;
1650 
1651 			if (buffer == guiSAVEBUFFER)
1652 			{
1653 				RestoreExternBackgroundRect(sNewX, sNewY, 15, 15);
1654 			}
1655 			GPrintInvalidate(sNewX, sNewY, pStr);
1656 		}
1657 
1658 		if (ItemHasAttachments(o))
1659 		{
1660 			SetFontForeground(GetAttachmentHintColor(&o));
1661 
1662 			ST::string attach_marker = "*";
1663 			UINT16         const uiStringLength = StringPixLength(attach_marker, ITEM_FONT);
1664 			INT16          const sNewX          = sX + sWidth - uiStringLength - 4;
1665 			INT16          const sNewY          = sY;
1666 
1667 			if (buffer == guiSAVEBUFFER)
1668 			{
1669 				RestoreExternBackgroundRect(sNewX, sNewY, 15, 15);
1670 			}
1671 			GPrintInvalidate(sNewX, sNewY, attach_marker);
1672 		}
1673 
1674 		if (s && &o == &s->inv[HANDPOS] && GCM->getItem(o.usItem)->getItemClass() == IC_GUN && s->bWeaponMode != WM_NORMAL)
1675 		{
1676 			SetFontForeground(FONT_DKRED);
1677 
1678 			ST::string mode_marker = s->bWeaponMode == WM_BURST ? "*" : "+";
1679 			UINT16         const uiStringLength = StringPixLength(mode_marker, ITEM_FONT);
1680 			INT16          const sNewX          = sX + sWidth - uiStringLength - 4;
1681 			INT16          const sNewY          = sY + 13; // rather arbitrary
1682 
1683 			if (buffer == guiSAVEBUFFER)
1684 			{
1685 				RestoreExternBackgroundRect(sNewX, sNewY, 15, 15);
1686 			}
1687 			GPrintInvalidate(sNewX, sNewY, mode_marker);
1688 		}
1689 	}
1690 }
1691 
1692 
InItemDescriptionBox()1693 BOOLEAN InItemDescriptionBox( )
1694 {
1695 	return( gfInItemDescBox );
1696 }
1697 
CycleItemDescriptionItem()1698 void CycleItemDescriptionItem( )
1699 {
1700 	INT16 usOldItem;
1701 
1702 	// Delete old box...
1703 	DeleteItemDescriptionBox( );
1704 
1705 	// Make new item....
1706 	usOldItem = gpItemDescSoldier->inv[ HANDPOS ].usItem;
1707 
1708 	if ( _KeyDown( SHIFT ) )
1709 	{
1710 		usOldItem--;
1711 
1712 		if ( usOldItem < 0 )
1713 		{
1714 			usOldItem = MAXITEMS-1;
1715 		}
1716 	}
1717 	else
1718 	{
1719 		usOldItem++;
1720 
1721 		if ( usOldItem > MAXITEMS )
1722 		{
1723 			usOldItem = 0;
1724 		}
1725 	}
1726 
1727 	CreateItem( (UINT16)usOldItem, 100, &( gpItemDescSoldier->inv[ HANDPOS ] ) );
1728 
1729 	InternalInitItemDescriptionBox( &( gpItemDescSoldier->inv[ HANDPOS ] ), 214, (INT16)(INV_INTERFACE_START_Y + 1 ), gubItemDescStatusIndex, gpItemDescSoldier );
1730 }
1731 
1732 
InitItemDescriptionBox(SOLDIERTYPE * pSoldier,UINT8 ubPosition,INT16 sX,INT16 sY,UINT8 ubStatusIndex)1733 void InitItemDescriptionBox(SOLDIERTYPE* pSoldier, UINT8 ubPosition, INT16 sX, INT16 sY, UINT8 ubStatusIndex)
1734 {
1735 	OBJECTTYPE *pObject;
1736 
1737 	//DEF:
1738 	//if we are in the shopkeeper screen, and we are to use the
1739 	if( guiCurrentScreen == SHOPKEEPER_SCREEN && ubPosition == 255 )
1740 	{
1741 		pObject = pShopKeeperItemDescObject;
1742 	}
1743 
1744 	//else use item from the hand position
1745 	else
1746 	{
1747 		pObject = &(pSoldier->inv[ ubPosition ] );
1748 	}
1749 
1750 	InternalInitItemDescriptionBox(pObject, sX, sY, ubStatusIndex, pSoldier);
1751 }
1752 
1753 
InitKeyItemDescriptionBox(SOLDIERTYPE * const pSoldier,const UINT8 ubPosition,const INT16 sX,const INT16 sY)1754 void InitKeyItemDescriptionBox(SOLDIERTYPE* const pSoldier, const UINT8 ubPosition, const INT16 sX, const INT16 sY)
1755 {
1756 	OBJECTTYPE *pObject;
1757 
1758 	AllocateObject( &pObject );
1759 	CreateKeyObject( pObject, pSoldier->pKeyRing[ ubPosition ].ubNumber ,pSoldier->pKeyRing[ ubPosition ].ubKeyID );
1760 
1761 	InternalInitItemDescriptionBox(pObject, sX, sY, 0, pSoldier);
1762 }
1763 
1764 
SetAttachmentTooltips(void)1765 static void SetAttachmentTooltips(void)
1766 {
1767 	for (UINT i = 0; i < MAX_ATTACHMENTS; ++i)
1768 	{
1769 		const UINT16 attachment = gpItemDescObject->usAttachItem[i];
1770 		ST::string tip = (attachment != NOTHING ? ItemNames[attachment] : g_langRes->Message[STR_ATTACHMENTS]);
1771 		gItemDescAttachmentRegions[i].SetFastHelpText(tip);
1772 	}
1773 }
1774 
1775 
1776 static void BtnMoneyButtonCallback(GUI_BUTTON* btn, INT32 reason);
1777 static void ItemDescAmmoCallback(GUI_BUTTON* btn, INT32 reason);
1778 static void ItemDescAttachmentsCallback(MOUSE_REGION* pRegion, INT32 iReason);
1779 static void ItemDescCallback(MOUSE_REGION* pRegion, INT32 iReason);
1780 static void ItemDescDoneButtonCallback(GUI_BUTTON* btn, INT32 reason);
1781 static void ReloadItemDesc(void);
1782 
1783 
InternalInitItemDescriptionBox(OBJECTTYPE * const o,const INT16 sX,const INT16 sY,const UINT8 ubStatusIndex,SOLDIERTYPE * const s)1784 void InternalInitItemDescriptionBox(OBJECTTYPE* const o, const INT16 sX, const INT16 sY, const UINT8 ubStatusIndex, SOLDIERTYPE* const s)
1785 {
1786 	// Set the current screen
1787 	guiCurrentItemDescriptionScreen = guiCurrentScreen;
1788 	const BOOLEAN in_map = (guiCurrentItemDescriptionScreen == MAP_SCREEN);
1789 
1790 	gsInvDescX = sX;
1791 	gsInvDescY = sY;
1792 
1793 	gpItemDescObject       = o;
1794 	gubItemDescStatusIndex = ubStatusIndex;
1795 	gpItemDescSoldier      = s;
1796 	fItemDescDelete        = FALSE;
1797 
1798 	// Build a mouse region here that is over any others.....
1799 	if (in_map)
1800 	{
1801 		MSYS_DefineRegion(&gInvDesc, gsInvDescX, gsInvDescY, gsInvDescX + MAP_ITEMDESC_WIDTH, gsInvDescY + MAP_ITEMDESC_HEIGHT, MSYS_PRIORITY_HIGHEST - 2, CURSOR_NORMAL, MSYS_NO_CALLBACK, ItemDescCallback);
1802 
1803 		giMapInvDescButton = QuickCreateButtonImg(INTERFACEDIR "/itemdescdonebutton.sti", 0, 1, gsInvDescX + 204, gsInvDescY + 107, MSYS_PRIORITY_HIGHEST, ItemDescDoneButtonCallback);
1804 
1805 		fShowDescriptionFlag = TRUE;
1806 	}
1807 	else
1808 	{
1809 		MSYS_DefineRegion(&gInvDesc, gsInvDescX, gsInvDescY, gsInvDescX + ITEMDESC_WIDTH, gsInvDescY + ITEMDESC_HEIGHT, MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ItemDescCallback);
1810 	}
1811 
1812 	if (GCM->getItem(o->usItem)->isGun()&& o->usItem != ROCKET_LAUNCHER)
1813 	{
1814 		ST::string pStr = ST::format("{}/{}", o->ubGunShotsLeft, GCM->getWeapon(o->usItem)->ubMagSize);
1815 
1816 		INT32 img;
1817 		switch (o->ubGunAmmoType)
1818 		{
1819 			case AMMO_AP:
1820 			case AMMO_SUPER_AP: img = 5; break;
1821 			case AMMO_HP:       img = 9; break;
1822 			default:            img = 1; break;
1823 		}
1824 		BUTTON_PICS* const ammo_img = LoadButtonImage(INTERFACEDIR "/infobox.sti", img + 3, img, -1, img + 2, -1);
1825 		giItemDescAmmoButtonImages = ammo_img;
1826 
1827 		const INT16         h  = GetDimensionsOfButtonPic(ammo_img)->h;
1828 		const SGPBox* const xy = (in_map ? &g_desc_item_box_map: &g_desc_item_box);
1829 		const INT16         x  = gsInvDescX + xy->x;
1830 		const INT16         y  = gsInvDescY + xy->y + xy->h - h; // align with bottom
1831 		const INT16         text_col   = ITEMDESC_AMMO_FORE;
1832 		const INT16         shadow_col = FONT_MCOLOR_BLACK;
1833 		GUIButtonRef  const ammo_btn   = CreateIconAndTextButton(ammo_img, pStr, TINYFONT1, text_col, shadow_col, text_col, shadow_col, x, y, MSYS_PRIORITY_HIGHEST, ItemDescAmmoCallback);
1834 		giItemDescAmmoButton = ammo_btn;
1835 
1836 		// Disable the eject button, if we are being init from the shop keeper
1837 		// screen and this is a dealer item we are getting info from
1838 		if (guiCurrentScreen == SHOPKEEPER_SCREEN && pShopKeeperItemDescObject)
1839 		{
1840 			ammo_btn->SpecifyDisabledStyle(GUI_BUTTON::DISABLED_STYLE_HATCHED);
1841 			DisableButton(ammo_btn);
1842 		}
1843 		else
1844 		{
1845 			ammo_btn->SetFastHelpText(g_langRes->Message[STR_EJECT_AMMO]);
1846 		}
1847 
1848 		INT16 usX;
1849 		INT16 usY;
1850 		FindFontCenterCoordinates(ITEMDESC_AMMO_TEXT_X, ITEMDESC_AMMO_TEXT_Y, ITEMDESC_AMMO_TEXT_WIDTH, GetFontHeight(TINYFONT1), pStr, TINYFONT1, &usX, &usY);
1851 		ammo_btn->SpecifyTextOffsets(usX, usY, TRUE);
1852 	}
1853 
1854 	if (ITEM_PROS_AND_CONS(o->usItem))
1855 	{
1856 		INT16         const pros_cons_indent = __max(StringPixLength(gzProsLabel, ITEMDESC_FONT), StringPixLength(gzConsLabel, ITEMDESC_FONT)) + 10;
1857 		const SGPBox* const box              = (in_map ? &g_map_itemdesc_pros_cons_box : &g_itemdesc_pros_cons_box);
1858 		UINT16        const x                = box->x + pros_cons_indent + gsInvDescX;
1859 		UINT16              y                = box->y                    + gsInvDescY;
1860 		UINT16        const w                = box->w - pros_cons_indent;
1861 		UINT16        const h                = GetFontHeight(ITEMDESC_FONT);
1862 		for (INT32 i = 0; i < 2; ++i)
1863 		{
1864 			// Add region for pros/cons help text
1865 			MOUSE_REGION* const r = &gProsAndConsRegions[i];
1866 			MSYS_DefineRegion(r, x, y, x + w - 1, y + h - 1, MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ItemDescCallback);
1867 			y += box->h;
1868 
1869 			ST::string label;
1870 			// use temp variable to prevent an initial comma from being displayed
1871 			ST::string FullItemTemp;
1872 			if (i == 0)
1873 			{
1874 				label = gzProsLabel;
1875 				GenerateProsString(FullItemTemp, *o, 1000);
1876 			}
1877 			else
1878 			{
1879 				label = gzConsLabel;
1880 				GenerateConsString(FullItemTemp, *o, 1000);
1881 			}
1882 			ST::string text = ST::format("{} {}", label, FullItemTemp);
1883 			r->SetFastHelpText(text);
1884 		}
1885 	}
1886 
1887 	// Load graphic
1888 	guiItemDescBox    = AddVideoObjectFromFile(INTERFACEDIR "/infobox.sti");
1889 	guiMapItemDescBox = AddVideoObjectFromFile(INTERFACEDIR "/iteminfoc.sti");
1890 	guiBullet         = AddVideoObjectFromFile(INTERFACEDIR "/bullet.sti");
1891 
1892 	if (o->usItem != MONEY)
1893 	{
1894 		const AttachmentGfxInfo* const agi = (in_map ? &g_map_attachment_info : &g_attachment_info);
1895 		for (INT32 i = 0; i < MAX_ATTACHMENTS; ++i)
1896 		{
1897 			// Build a mouse region here that is over any others.....
1898 			const UINT16        x = agi->item_box.x + agi->slot[i].iX + gsInvDescX;
1899 			const UINT16        y = agi->item_box.y + agi->slot[i].iY + gsInvDescY;
1900 			const UINT16        w = agi->item_box.w;
1901 			const UINT16        h = agi->item_box.h;
1902 			MOUSE_REGION* const r = &gItemDescAttachmentRegions[i];
1903 			MSYS_DefineRegion(r, x, y, x + w, y + h, MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ItemDescAttachmentsCallback);
1904 			MSYS_SetRegionUserData(r, 0, i);
1905 		}
1906 		SetAttachmentTooltips();
1907 	}
1908 	else
1909 	{
1910 		gRemoveMoney = REMOVE_MONEY{};
1911 		gRemoveMoney.uiTotalAmount    = o->uiMoneyAmount;
1912 		gRemoveMoney.uiMoneyRemaining = o->uiMoneyAmount;
1913 		gRemoveMoney.uiMoneyRemoving  = 0;
1914 
1915 		guiMoneyGraphicsForDescBox = AddVideoObjectFromFile(INTERFACEDIR "/info_bil.sti");
1916 
1917 		// Create buttons for the money
1918 		guiMoneyButtonImage = LoadButtonImage(INTERFACEDIR "/info_bil.sti", 1, 2);
1919 		const MoneyLoc* const loc = (in_map ? &gMapMoneyButtonLoc : &gMoneyButtonLoc);
1920 		INT32 i;
1921 		for (i = 0; i < MAX_ATTACHMENTS - 1; i++)
1922 		{
1923 			guiMoneyButtonBtn[i] = CreateIconAndTextButton(
1924 				guiMoneyButtonImage, gzMoneyAmounts[i], BLOCKFONT2,
1925 				5, DEFAULT_SHADOW,
1926 				5, DEFAULT_SHADOW,
1927 				loc->x + gMoneyButtonOffsets[i].x, loc->y + gMoneyButtonOffsets[i].y, MSYS_PRIORITY_HIGHEST,
1928 				BtnMoneyButtonCallback
1929 			);
1930 			guiMoneyButtonBtn[i]->SetUserData(i);
1931 		}
1932 		if (gRemoveMoney.uiTotalAmount < 1000) DisableButton(guiMoneyButtonBtn[M_1000]);
1933 		if (gRemoveMoney.uiTotalAmount <  100) DisableButton(guiMoneyButtonBtn[M_100]);
1934 		if (gRemoveMoney.uiTotalAmount <   10) DisableButton(guiMoneyButtonBtn[M_10]);
1935 
1936 		// Create the Done button
1937 		guiMoneyDoneButtonImage = UseLoadedButtonImage(guiMoneyButtonImage, 3, 4);
1938 		guiMoneyButtonBtn[i] = CreateIconAndTextButton(
1939 			guiMoneyDoneButtonImage, gzMoneyAmounts[i], BLOCKFONT2,
1940 			5, DEFAULT_SHADOW,
1941 			5, DEFAULT_SHADOW,
1942 			loc->x + gMoneyButtonOffsets[i].x, loc->y + gMoneyButtonOffsets[i].y, MSYS_PRIORITY_HIGHEST,
1943 			BtnMoneyButtonCallback
1944 		);
1945 		guiMoneyButtonBtn[i]->SetUserData(i);
1946 	}
1947 
1948 	fInterfacePanelDirty = DIRTYLEVEL2;
1949 	gfInItemDescBox      = TRUE;
1950 
1951 	ReloadItemDesc();
1952 
1953 	gpAttachSoldier = (gpItemPointer ? gpItemPointerSoldier : s);
1954 	// Store attachments that item originally had
1955 	for (INT32 i = 0; i < MAX_ATTACHMENTS; ++i)
1956 	{
1957 		gusOriginalAttachItem[i]  = o->usAttachItem[i];
1958 		gbOriginalAttachStatus[i] = o->bAttachStatus[i];
1959 	}
1960 
1961 	if (gpItemPointer != NULL && !gfItemDescHelpTextOffset && !CheckFact(FACT_ATTACHED_ITEM_BEFORE, 0))
1962 	{
1963 		ST::string text;
1964 		if (!(GCM->getItem(o->usItem)->getFlags() & ITEM_HIDDEN_ADDON) && (
1965 			ValidAttachment(gpItemPointer->usItem, o->usItem) ||
1966 			ValidLaunchable(gpItemPointer->usItem, o->usItem) ||
1967 			ValidMerge(gpItemPointer->usItem, o->usItem)))
1968 		{
1969 			text = g_langRes->Message[STR_ATTACHMENT_HELP];
1970 		}
1971 		else
1972 		{
1973 			text = g_langRes->Message[STR_ATTACHMENT_INVALID_HELP];
1974 		}
1975 		SetUpFastHelpRegion(69 + gsInvDescX, 12 + gsInvDescY, 170, text);
1976 
1977 		StartShowingInterfaceFastHelpText();
1978 
1979 		SetFactTrue(FACT_ATTACHED_ITEM_BEFORE);
1980 		gfItemDescHelpTextOffset = TRUE;
1981 	}
1982 }
1983 
1984 
ReloadItemDesc(void)1985 static void ReloadItemDesc(void)
1986 {
1987 	guiItemGraphic = LoadTileGraphicForItem(GCM->getItem(gpItemDescObject->usItem));
1988 
1989 	//
1990 	// Load name, desc
1991 	//
1992 
1993 	//if the player is extracting money from the players account, use a different item name and description
1994 	UINT16 Item = gpItemDescObject->usItem;
1995 	if (Item == MONEY && gfAddingMoneyToMercFromPlayersAccount)
1996 	{
1997 		Item = MONEY_FOR_PLAYERS_ACCOUNT;
1998 	}
1999 	gzItemName = ItemNames[Item];
2000 	gzItemDesc = LoadItemInfo(Item);
2001 }
2002 
2003 
ItemDescAmmoCallback(GUI_BUTTON * const btn,INT32 const reason)2004 static void ItemDescAmmoCallback(GUI_BUTTON*  const btn, INT32 const reason)
2005 {
2006 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2007 	{
2008 		if (gpItemPointer) return;
2009 		if (!EmptyWeaponMagazine(gpItemDescObject, &gItemPointer)) return;
2010 
2011 		SetItemPointer(&gItemPointer, gpItemDescSoldier);
2012 		fInterfacePanelDirty = DIRTYLEVEL2;
2013 
2014 		btn->SpecifyText("0");
2015 
2016 		if (guiCurrentItemDescriptionScreen == MAP_SCREEN)
2017 		{
2018 			SetMapCursorItem();
2019 			fTeamPanelDirty = TRUE;
2020 		}
2021 		else
2022 		{
2023 			// if in SKI, load item into SKI's item pointer
2024 			if (guiCurrentScreen == SHOPKEEPER_SCREEN)
2025 			{
2026 				// pick up bullets from weapon into cursor (don't try to sell)
2027 				BeginSkiItemPointer(PLAYERS_INVENTORY, -1, FALSE);
2028 			}
2029 			fItemDescDelete = TRUE;
2030 		}
2031 	}
2032 }
2033 
2034 
DoAttachment(void)2035 static void DoAttachment(void)
2036 {
2037 	if (AttachObject(gpItemDescSoldier, gpItemDescObject, gpItemPointer, gubItemDescStatusIndex))
2038 	{
2039 		if (gpItemPointer->usItem == NOTHING)
2040 		{
2041 			// attachment attached, merge item consumed, etc
2042 
2043 			if (fInMapMode)
2044 			{
2045 				MAPEndItemPointer( );
2046 			}
2047 			else
2048 			{
2049 				// End Item pickup
2050 				gpItemPointer = NULL;
2051 				EnableSMPanelButtons( TRUE , TRUE );
2052 
2053 				gSMPanelRegion.ChangeCursor(CURSOR_NORMAL);
2054 				SetCurrentCursorFromDatabase( CURSOR_NORMAL );
2055 
2056 				//if we are currently in the shopkeeper interface
2057 				if (guiCurrentScreen == SHOPKEEPER_SCREEN)
2058 				{
2059 					//Clear out the moving cursor
2060 					gMoveingItem = INVENTORY_IN_SLOT{};
2061 
2062 					//change the curosr back to the normal one
2063 					SetSkiCursor( CURSOR_NORMAL );
2064 				}
2065 			}
2066 		}
2067 
2068 		if ( gpItemDescObject->usItem == NOTHING )
2069 		{
2070 			// close desc panel panel
2071 			DeleteItemDescriptionBox();
2072 		}
2073 		else
2074 		{
2075 			SetAttachmentTooltips();
2076 		}
2077 		//Dirty interface
2078 		fInterfacePanelDirty = DIRTYLEVEL2;
2079 
2080 		ReloadItemDesc( );
2081 	}
2082 
2083 	// re-evaluate repairs
2084 	gfReEvaluateEveryonesNothingToDo = TRUE;
2085 }
2086 
2087 
PermanantAttachmentMessageBoxCallBack(MessageBoxReturnValue const ubExitValue)2088 static void PermanantAttachmentMessageBoxCallBack(MessageBoxReturnValue const ubExitValue)
2089 {
2090 	if ( ubExitValue == MSG_BOX_RETURN_YES )
2091 	{
2092 		DoAttachment();
2093 	}
2094 	// else do nothing
2095 }
2096 
2097 
ItemDescAttachmentsCallback(MOUSE_REGION * pRegion,INT32 iReason)2098 static void ItemDescAttachmentsCallback(MOUSE_REGION* pRegion, INT32 iReason)
2099 {
2100 	UINT32 uiItemPos;
2101 	static BOOLEAN fRightDown = FALSE;
2102 
2103 	if ( gfItemDescObjectIsAttachment )
2104 	{
2105 		// screen out completely
2106 		return;
2107 	}
2108 
2109 	uiItemPos = MSYS_GetRegionUserData( pRegion, 0 );
2110 
2111 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2112 	{
2113 		// if the item being described belongs to a shopkeeper, ignore attempts to pick it up / replace it
2114 		if (guiCurrentScreen == SHOPKEEPER_SCREEN && pShopKeeperItemDescObject)
2115 		{
2116 			return;
2117 		}
2118 
2119 		// Try to place attachment if something is in our hand
2120 		// require as many APs as to reload
2121 		if ( gpItemPointer != NULL )
2122 		{
2123 			// nb pointer could be NULL because of inventory manipulation in mapscreen from sector inv
2124 			if ( !gpItemPointerSoldier || EnoughPoints( gpItemPointerSoldier, AP_RELOAD_GUN, 0, TRUE ) )
2125 			{
2126 				if ( (GCM->getItem(gpItemPointer->usItem)->getFlags() & ITEM_INSEPARABLE) && ValidAttachment( gpItemPointer->usItem, gpItemDescObject->usItem ) )
2127 				{
2128 					DoScreenIndependantMessageBox(g_langRes->Message[STR_PERMANENT_ATTACHMENT], MSG_BOX_FLAG_YESNO, PermanantAttachmentMessageBoxCallBack);
2129 					return;
2130 				}
2131 
2132 				DoAttachment();
2133 			}
2134 		}
2135 		else
2136 		{
2137 			// ATE: Make sure we have enough AP's to drop it if we pick it up!
2138 			if ( EnoughPoints( gpItemDescSoldier, ( AP_RELOAD_GUN + AP_PICKUP_ITEM ), 0, TRUE ) )
2139 			{
2140 				// Get attachment if there is one
2141 				// The follwing function will handle if no attachment is here
2142 				if ( RemoveAttachment( gpItemDescObject, (UINT8)uiItemPos, &gItemPointer ) )
2143 				{
2144 					SetItemPointer(&gItemPointer, gpItemDescSoldier);
2145 
2146 					//if( guiCurrentScreen == MAP_SCREEN )
2147 					if( guiCurrentItemDescriptionScreen == MAP_SCREEN )
2148 					{
2149 						SetMapCursorItem();
2150 						fTeamPanelDirty=TRUE;
2151 					}
2152 
2153 					//if we are currently in the shopkeeper interface
2154 					else if (guiCurrentScreen == SHOPKEEPER_SCREEN)
2155 					{
2156 						// pick up attachment from item into cursor (don't try to sell)
2157 						BeginSkiItemPointer( PLAYERS_INVENTORY, -1, FALSE );
2158 					}
2159 
2160 					//Dirty interface
2161 					fInterfacePanelDirty = DIRTYLEVEL2;
2162 
2163 					// re-evaluate repairs
2164 					gfReEvaluateEveryonesNothingToDo = TRUE;
2165 
2166 					UpdateItemHatches();
2167 					SetAttachmentTooltips();
2168 				}
2169 			}
2170 		}
2171 	}
2172 	else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
2173 	{
2174 		fRightDown = TRUE;
2175 	}
2176 	else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP && fRightDown )
2177 	{
2178 		static OBJECTTYPE Object2;
2179 
2180 		fRightDown = FALSE;
2181 
2182 		if ( gpItemDescObject->usAttachItem[ uiItemPos ] != NOTHING )
2183 		{
2184 			BOOLEAN fShopkeeperItem = FALSE;
2185 
2186 			// remember if this is a shopkeeper's item we're viewing ( pShopKeeperItemDescObject will get nuked on deletion )
2187 			if (guiCurrentScreen == SHOPKEEPER_SCREEN && pShopKeeperItemDescObject)
2188 			{
2189 				fShopkeeperItem = TRUE;
2190 			}
2191 
2192 			DeleteItemDescriptionBox( );
2193 
2194 			CreateItem(gpItemDescObject->usAttachItem[uiItemPos], gpItemDescObject->bAttachStatus[uiItemPos], &Object2);
2195 
2196 			gfItemDescObjectIsAttachment = TRUE;
2197 			InternalInitItemDescriptionBox(&Object2, gsInvDescX, gsInvDescY, 0, gpItemDescSoldier);
2198 
2199 			if (fShopkeeperItem)
2200 			{
2201 				pShopKeeperItemDescObject = &Object2;
2202 				StartSKIDescriptionBox();
2203 			}
2204 		}
2205 	}
2206 }
2207 
2208 
GetObjectImprint(OBJECTTYPE const & o)2209 static ST::string GetObjectImprint(OBJECTTYPE const& o)
2210 {
2211 	return !HasObjectImprint(o) ? ST::null :
2212 		o.ubImprintID == NO_PROFILE + 1 ? pwMiscSectorStrings[3] :
2213 		GetProfile(o.ubImprintID).zNickname;
2214 }
2215 
2216 
HighlightIf(const BOOLEAN cond)2217 static void HighlightIf(const BOOLEAN cond)
2218 {
2219 	SetFontForeground(cond ? ITEMDESC_FONTHIGHLIGHT : 5);
2220 }
2221 
2222 
RenderItemDescriptionBox(void)2223 void RenderItemDescriptionBox(void)
2224 {
2225 	if (!gfInItemDescBox) return;
2226 
2227 	ST::string pStr;
2228 	INT16   usX;
2229 	INT16   usY;
2230 
2231 	OBJECTTYPE const& obj    = *gpItemDescObject;
2232 	BOOLEAN    const  in_map = guiCurrentItemDescriptionScreen == MAP_SCREEN;
2233 	INT16      const  dx     = gsInvDescX;
2234 	INT16      const  dy     = gsInvDescY;
2235 
2236 	SGPVObject const* const box_gfx = in_map ? guiMapItemDescBox : guiItemDescBox;
2237 	BltVideoObject(guiSAVEBUFFER, box_gfx, 0, dx, dy);
2238 
2239 	// Display the money 'separating' border
2240 	if (obj.usItem == MONEY)
2241 	{
2242 		// Render the money Boxes
2243 		MoneyLoc const& xy = in_map ? gMapMoneyButtonLoc : gMoneyButtonLoc;
2244 		INT32    const  x  = xy.x + gMoneyButtonOffsets[0].x - 1;
2245 		INT32    const  y  = xy.y + gMoneyButtonOffsets[0].y;
2246 		BltVideoObject(guiSAVEBUFFER, guiMoneyGraphicsForDescBox, 0, x, y);
2247 	}
2248 
2249 	{
2250 		// Display item
2251 		// center in slot, remove offsets
2252 		ETRLEObject const& e  = guiItemGraphic->SubregionProperties(0);
2253 		SGPBox      const& xy = in_map ? g_desc_item_box_map: g_desc_item_box;
2254 		INT32       const  x  = dx + xy.x + (xy.w - e.usWidth)  / 2 - e.sOffsetX;
2255 		INT32       const  y  = dy + xy.y + (xy.h - e.usHeight) / 2 - e.sOffsetY;
2256 		if (gamepolicy(f_draw_item_shadow))
2257 		{
2258 			BltVideoObjectOutlineShadow(guiSAVEBUFFER, guiItemGraphic, 0, x - 2, y + 2);
2259 		}
2260 		BltVideoObject(guiSAVEBUFFER, guiItemGraphic, 0, x, y);
2261 	}
2262 
2263 	{ // Display status
2264 		SGPBox const& box = in_map ? g_map_itemdesc_item_status_box : g_itemdesc_item_status_box;
2265 		INT16  const  x   = box.x + dx;
2266 		INT16  const  y   = box.y + dy;
2267 		INT16  const  h   = box.h;
2268 		DrawItemUIBarEx(obj, gubItemDescStatusIndex, x, y, h, Get16BPPColor(DESC_STATUS_BAR), Get16BPPColor(DESC_STATUS_BAR_SHADOW), guiSAVEBUFFER);
2269 	}
2270 
2271 	bool hatch_out_attachments = gfItemDescObjectIsAttachment; // if examining attachment, always hatch out attachment slots
2272 	if (OBJECTTYPE const* const ptr_obj = gpItemPointer)
2273 	{
2274 		if (GCM->getItem(ptr_obj->usItem)->getFlags() & ITEM_HIDDEN_ADDON || (
2275 			!ValidItemAttachment(&obj, ptr_obj->usItem, FALSE) &&
2276 			!ValidMerge(ptr_obj->usItem, obj.usItem) &&
2277 			!ValidLaunchable(ptr_obj->usItem, obj.usItem)))
2278 		{
2279 			hatch_out_attachments = TRUE;
2280 		}
2281 	}
2282 
2283 	{
2284 		// Display attachments
2285 		AttachmentGfxInfo const& agi = in_map ? g_map_attachment_info : g_attachment_info;
2286 		for (INT32 i = 0; i < MAX_ATTACHMENTS; ++i)
2287 		{
2288 			INT16 const x = dx + agi.slot[i].iX;
2289 			INT16 const y = dy + agi.slot[i].iY;
2290 
2291 			if (obj.usAttachItem[i] != NOTHING)
2292 			{
2293 				INT16 const item_x = agi.item_box.x + x;
2294 				INT16 const item_y = agi.item_box.y + y;
2295 				INT16 const item_w = agi.item_box.w;
2296 				INT16 const item_h = agi.item_box.h;
2297 				INVRenderItem(guiSAVEBUFFER, NULL, obj, item_x, item_y, item_w, item_h, DIRTYLEVEL2, RENDER_ITEM_ATTACHMENT1 + i, SGP_TRANSPARENT);
2298 
2299 				INT16 const bar_x = agi.bar_box.x + x;
2300 				INT16 const bar_h = agi.bar_box.h;
2301 				INT16 const bar_y = agi.bar_box.y + y + bar_h - 1;
2302 				DrawItemUIBarEx(obj, DRAW_ITEM_STATUS_ATTACHMENT1 + i, bar_x, bar_y, bar_h, Get16BPPColor(STATUS_BAR), Get16BPPColor(STATUS_BAR_SHADOW), guiSAVEBUFFER);
2303 			}
2304 
2305 			if (hatch_out_attachments)
2306 			{
2307 				UINT16 const hatch_w = agi.item_box.x + agi.item_box.w;
2308 				UINT16 const hatch_h = agi.item_box.y + agi.item_box.h;
2309 				DrawHatchOnInventory(guiSAVEBUFFER, x, y, hatch_w, hatch_h);
2310 			}
2311 		}
2312 	}
2313 
2314 	const ItemModel * item = GCM->getItem(obj.usItem);
2315 
2316 	if (item->isGun())
2317 	{
2318 		// display bullets for ROF
2319 		{
2320 			INT32 const x = in_map ? MAP_BULLET_SING_X : BULLET_SING_X;
2321 			INT32 const y = in_map ? MAP_BULLET_SING_Y : BULLET_SING_Y;
2322 			BltVideoObject(guiSAVEBUFFER, guiBullet, 0, x, y);
2323 		}
2324 
2325 		const WeaponModel * w = GCM->getWeapon(obj.usItem);
2326 		if (w->ubShotsPerBurst > 0)
2327 		{
2328 			INT32       x = in_map ? MAP_BULLET_BURST_X : BULLET_BURST_X;
2329 			INT32 const y = in_map ? MAP_BULLET_BURST_Y : BULLET_BURST_Y;
2330 			for (INT32 i = w->ubShotsPerBurst; i != 0; --i)
2331 			{
2332 				BltVideoObject(guiSAVEBUFFER, guiBullet, 0, x, y);
2333 				x += BULLET_WIDTH + 1;
2334 			}
2335 		}
2336 	}
2337 
2338 	{
2339 		INT16 const w = in_map ? MAP_ITEMDESC_WIDTH  : ITEMDESC_WIDTH;
2340 		INT16 const h = in_map ? MAP_ITEMDESC_HEIGHT : ITEMDESC_HEIGHT;
2341 		RestoreExternBackgroundRect(dx, dy, w, h);
2342 	}
2343 
2344 	// Render font desc
2345 	SetFontAttributes(ITEMDESC_FONT, FONT_FCOLOR_WHITE);
2346 
2347 	{
2348 		// Render name
2349 		SGPBox const& xy = in_map ? gMapDescNameBox : gDescNameBox;
2350 		MPrint(dx + xy.x, dy + xy.y, gzItemName);
2351 	}
2352 
2353 	SetFontShadow(ITEMDESC_FONTSHADOW2);
2354 
2355 	{
2356 		SGPBox const& box = in_map ? g_map_itemdesc_desc_box : g_itemdesc_desc_box;
2357 		DisplayWrappedString(dx + box.x, dy + box.y, box.w, 2, ITEMDESC_FONT, FONT_BLACK, gzItemDesc, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
2358 	}
2359 
2360 	if (ITEM_PROS_AND_CONS(obj.usItem))
2361 	{
2362 		{
2363 			const WeaponModel * w = GCM->getWeapon(obj.usItem);
2364 			if (w->calibre->index != NOAMMO)
2365 			{
2366 				ST::string name = *w->calibre->getName();
2367 				pStr += ST::format("{} ", name);
2368 			}
2369 			pStr += ST::format("{}", WeaponType[w->ubWeaponType]);
2370 			ST::string imprint = GetObjectImprint(obj);
2371 			if (!imprint.empty())
2372 			{
2373 				// Add name noting imprint
2374 				pStr += ST::format(" ({})", imprint);
2375 			}
2376 
2377 			SGPBox const& xy = in_map ? gMapDescNameBox : gDescNameBox;
2378 			FindFontRightCoordinates(dx + xy.x, dy + xy.y, xy.w, xy.h, pStr, ITEMDESC_FONT, &usX, &usY);
2379 			MPrint(usX, usY, pStr);
2380 		}
2381 
2382 		{
2383 			SGPBox const& box = in_map ? g_map_itemdesc_pros_cons_box : g_itemdesc_pros_cons_box;
2384 			INT32         x   = box.x + dx;
2385 			INT32  const  y   = box.y + dy;
2386 			INT32         w   = box.w;
2387 			INT32  const  h   = box.h;
2388 
2389 			SetFontForeground(FONT_MCOLOR_DKWHITE2);
2390 			SetFontShadow(DEFAULT_SHADOW);
2391 			MPrint(x, y,     gzProsLabel);
2392 			MPrint(x, y + h, gzConsLabel);
2393 
2394 			SetFontForeground(FONT_BLACK);
2395 			SetFontShadow(ITEMDESC_FONTSHADOW2);
2396 
2397 			INT16 const pros_cons_indent = __max(StringPixLength(gzProsLabel, ITEMDESC_FONT), StringPixLength(gzConsLabel, ITEMDESC_FONT)) + 10;
2398 			x += pros_cons_indent;
2399 			w -= pros_cons_indent + StringPixLength(DOTDOTDOT, ITEMDESC_FONT);
2400 
2401 			GenerateProsString(gzItemPros, obj, w);
2402 			MPrint(x, y, gzItemPros);
2403 
2404 			GenerateConsString(gzItemCons, obj, w);
2405 			MPrint(x, y + h, gzItemCons);
2406 		}
2407 	}
2408 
2409 	// Calculate total weight of item and attachments
2410 	float fWeight = CalculateObjectWeight(&obj) / 10.f;
2411 	if (!gGameSettings.fOptions[TOPTION_USE_METRIC_SYSTEM]) fWeight *= 2.2f;
2412 	if (fWeight < 0.1) fWeight = 0.1f;
2413 
2414 	SetFontShadow(DEFAULT_SHADOW);
2415 
2416 	// Render, stat  name
2417 	if (item->isWeapon())
2418 	{
2419 		SetFontForeground(6);
2420 
2421 		INV_DESC_STATS const* const ids = in_map ? gMapWeaponStats : gWeaponStats;
2422 
2423 		//LABELS
2424 		MPrint(dx + ids[0].sX, dy + ids[0].sY, st_format_printf(gWeaponStatsDesc[0], GetWeightUnitString())); // mass
2425 		if (item->getItemClass() & (IC_GUN | IC_LAUNCHER))
2426 		{
2427 			MPrint(dx + ids[2].sX, dy + ids[2].sY, gWeaponStatsDesc[3]); // range
2428 		}
2429 		if (!(item->isLauncher()) && obj.usItem != ROCKET_LAUNCHER)
2430 		{
2431 			MPrint(dx + ids[3].sX, dy + ids[3].sY, gWeaponStatsDesc[4]); // damage
2432 		}
2433 		MPrint(dx + ids[4].sX, dy + ids[4].sY, gWeaponStatsDesc[5]); // APs
2434 		if (item->isGun())
2435 		{
2436 			MPrint(dx + ids[6].sX, dy + ids[6].sY, gWeaponStatsDesc[6]); // = (sic)
2437 		}
2438 		MPrint(dx + ids[1].sX, dy + ids[1].sY, gWeaponStatsDesc[1]); // status
2439 
2440 		const WeaponModel * w = GCM->getWeapon(obj.usItem);
2441 		if (w->ubShotsPerBurst > 0)
2442 		{
2443 			MPrint(dx + ids[7].sX, dy + ids[7].sY, gWeaponStatsDesc[6]); // = (sic)
2444 		}
2445 
2446 		//Status
2447 		SetFontForeground(5);
2448 		pStr = ST::format("{2d}%", obj.bGunStatus);
2449 		FindFontRightCoordinates(dx + ids[1].sX + ids[1].sValDx, dy + ids[1].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2450 		MPrint(usX, usY, pStr);
2451 
2452 		//Weight
2453 		HighlightIf(fWeight <= EXCEPTIONAL_WEIGHT / 10);
2454 		pStr = ST::format("{1.1f}", fWeight);
2455 		FindFontRightCoordinates(dx + ids[0].sX + ids[0].sValDx, dy + ids[0].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2456 		MPrint(usX, usY, pStr);
2457 
2458 		if (item->getItemClass() & (IC_GUN | IC_LAUNCHER))
2459 		{
2460 			// Range
2461 			UINT16 const range = GunRange(obj);
2462 			HighlightIf(range >= EXCEPTIONAL_RANGE);
2463 			pStr = ST::format("{2d}", range / 10);
2464 			FindFontRightCoordinates(dx + ids[2].sX + ids[2].sValDx, dy + ids[2].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2465 			MPrint(usX, usY, pStr);
2466 		}
2467 
2468 		if (!(item->isLauncher()) && obj.usItem != ROCKET_LAUNCHER)
2469 		{
2470 			// Damage
2471 			HighlightIf(w->ubImpact >= EXCEPTIONAL_DAMAGE);
2472 			pStr = ST::format("{2d}", w->ubImpact);
2473 			FindFontRightCoordinates(dx + ids[3].sX + ids[3].sValDx, dy + ids[3].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2474 			MPrint(usX, usY, pStr);
2475 		}
2476 
2477 		UINT8 const ubAttackAPs = BaseAPsToShootOrStab(DEFAULT_APS, DEFAULT_AIMSKILL, obj);
2478 
2479 		//APs
2480 		HighlightIf(ubAttackAPs <= EXCEPTIONAL_AP_COST);
2481 		pStr = ST::format("{2d}", ubAttackAPs);
2482 		FindFontRightCoordinates(dx + ids[4].sX + ids[4].sValDx, dy + ids[4].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2483 		MPrint(usX, usY, pStr);
2484 
2485 		if (w->ubShotsPerBurst > 0)
2486 		{
2487 			HighlightIf(w->ubShotsPerBurst >= EXCEPTIONAL_BURST_SIZE || obj.usItem == G11);
2488 			pStr = ST::format("{2d}", ubAttackAPs + CalcAPsToBurst(DEFAULT_APS, obj));
2489 			FindFontRightCoordinates(dx + ids[5].sX + ids[5].sValDx, dy + ids[5].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2490 			MPrint(usX, usY, pStr);
2491 		}
2492 	}
2493 	else if (obj.usItem == MONEY)
2494 	{
2495 		SetFontForeground(FONT_WHITE);
2496 
2497 		{
2498 			// Display the total amount of money
2499 			pStr = SPrintMoney(in_map && gfAddingMoneyToMercFromPlayersAccount ? LaptopSaveInfo.iCurrentBalance : gRemoveMoney.uiTotalAmount);
2500 			SGPBox const& xy = in_map ? gMapDescNameBox : gDescNameBox;
2501 			FindFontRightCoordinates(dx + xy.x, dy + xy.y, xy.w, xy.h, pStr, BLOCKFONT2, &usX, &usY);
2502 			MPrint(usX, usY, pStr);
2503 		}
2504 
2505 		{
2506 			// Display the 'Separate' text
2507 			SetFontForeground(in_map ? 5 : 6);
2508 			MoneyLoc const&       xy    = in_map ? gMapMoneyButtonLoc : gMoneyButtonLoc;
2509 			ST::string label = !in_map && gfAddingMoneyToMercFromPlayersAccount ? gzMoneyAmounts[5] : gzMoneyAmounts[4];
2510 			MPrint(xy.x + gMoneyButtonOffsets[4].x, xy.y + gMoneyButtonOffsets[4].y, label);
2511 		}
2512 
2513 		SetFontForeground(6);
2514 
2515 		INV_DESC_STATS const* const xy = in_map ? gMapMoneyStats : gMoneyStats;
2516 
2517 		if (!in_map && gfAddingMoneyToMercFromPlayersAccount)
2518 		{
2519 			MPrint(dx + xy[0].sX, dy + xy[0].sY, gMoneyStatsDesc[MONEY_DESC_PLAYERS]);           // current ...
2520 			MPrint(dx + xy[1].sX, dy + xy[1].sY, gMoneyStatsDesc[MONEY_DESC_BALANCE]);           // ... balance
2521 			MPrint(dx + xy[2].sX, dy + xy[2].sY, gMoneyStatsDesc[MONEY_DESC_AMOUNT_2_WITHDRAW]); // amount to ...
2522 			MPrint(dx + xy[3].sX, dy + xy[3].sY, gMoneyStatsDesc[MONEY_DESC_TO_WITHDRAW]);       // ... widthdraw
2523 		}
2524 		else
2525 		{
2526 			MPrint(dx + xy[0].sX, dy + xy[0].sY, gMoneyStatsDesc[MONEY_DESC_AMOUNT]);         // amount ...
2527 			MPrint(dx + xy[1].sX, dy + xy[1].sY, gMoneyStatsDesc[MONEY_DESC_REMAINING]);      // ... remaining
2528 			MPrint(dx + xy[2].sX, dy + xy[2].sY, gMoneyStatsDesc[MONEY_DESC_AMOUNT_2_SPLIT]); // amount ...
2529 			MPrint(dx + xy[3].sX, dy + xy[3].sY, gMoneyStatsDesc[MONEY_DESC_TO_SPLIT]);       // ... to split
2530 		}
2531 
2532 		SetFontForeground(5);
2533 
2534 		// Get length of string
2535 		UINT16 const uiRightLength = 35;
2536 
2537 		//Display the total amount of money remaining
2538 		pStr = SPrintMoney(gRemoveMoney.uiMoneyRemaining);
2539 		if (in_map)
2540 		{
2541 			UINT16 const uiStringLength = StringPixLength(pStr, ITEMDESC_FONT);
2542 			INT16  const sStrX          = dx + xy[1].sX + xy[1].sValDx + (uiRightLength - uiStringLength);
2543 			MPrint(sStrX, dy + xy[1].sY, pStr);
2544 		}
2545 		else
2546 		{
2547 			FindFontRightCoordinates(dx + xy[1].sX + xy[1].sValDx, dy + xy[1].sY, ITEM_STATS_WIDTH - 3, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2548 			MPrint(usX, usY, pStr);
2549 		}
2550 
2551 		//Display the total amount of money removing
2552 		pStr = SPrintMoney(gRemoveMoney.uiMoneyRemoving);
2553 		if (in_map)
2554 		{
2555 			UINT16 const uiStringLength = StringPixLength(pStr, ITEMDESC_FONT);
2556 			INT16  const sStrX          = dx + xy[3].sX + xy[3].sValDx + (uiRightLength - uiStringLength);
2557 			MPrint(sStrX, dy + xy[3].sY, pStr);
2558 		}
2559 		else
2560 		{
2561 			FindFontRightCoordinates(dx + xy[3].sX + xy[3].sValDx, dy + xy[3].sY, ITEM_STATS_WIDTH - 3, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2562 			MPrint(usX, usY, pStr);
2563 		}
2564 	}
2565 	else if (item->getItemClass() == IC_MONEY)
2566 	{
2567 		SetFontForeground(FONT_FCOLOR_WHITE);
2568 		pStr = SPrintMoney(obj.uiMoneyAmount);
2569 		SGPBox const& xy = in_map ? gMapDescNameBox : gDescNameBox;
2570 		FindFontRightCoordinates(dx + xy.x, dy + xy.y, xy.w, xy.h, pStr, BLOCKFONT2, &usX, &usY);
2571 		MPrint(usX, usY, pStr);
2572 	}
2573 	else
2574 	{
2575 		//Labels
2576 		SetFontForeground(6);
2577 
2578 		INV_DESC_STATS const* const ids = in_map ? gMapWeaponStats : gWeaponStats;
2579 
2580 		// amount for ammunition, status otherwise
2581 		ST::string label = GCM->getItem(gpItemDescObject->usItem)->isAmmo() ? gWeaponStatsDesc[2] : gWeaponStatsDesc[1];
2582 		MPrint(dx + ids[1].sX, dy + ids[1].sY, label);
2583 
2584 		//Weight
2585 		MPrint(dx + ids[0].sX, dy + ids[0].sY, st_format_printf(gWeaponStatsDesc[0], GetWeightUnitString()));
2586 
2587 		// Values
2588 		SetFontForeground(5);
2589 
2590 		if (item->isAmmo())
2591 		{
2592 			// Ammo - print amount
2593 			pStr = ST::format("{}/{}", obj.ubShotsLeft[0], item->asAmmo()->capacity);
2594 			FindFontRightCoordinates(dx + ids[1].sX + ids[1].sValDx, dy + ids[1].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2595 			MPrint(usX, usY, pStr);
2596 		}
2597 		else
2598 		{
2599 			// Status
2600 			pStr = ST::format("{2d}%", obj.bStatus[gubItemDescStatusIndex]);
2601 			FindFontRightCoordinates(dx + ids[1].sX + ids[1].sValDx, dy + ids[1].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2602 			MPrint(usX, usY, pStr);
2603 		}
2604 
2605 		//Weight
2606 		pStr = ST::format("{1.1f}", fWeight);
2607 		FindFontRightCoordinates(dx + ids[0].sX + ids[0].sValDx, dy + ids[0].sY, ITEM_STATS_WIDTH, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2608 		MPrint(usX, usY, pStr);
2609 
2610 		if (InKeyRingPopup() || item->isKey())
2611 		{
2612 			SetFontForeground(6);
2613 
2614 			INT32 const x  = dx + ids[3].sX;
2615 			INT32 const y0 = dy + ids[3].sY;
2616 			INT32 const y1 = y0 + GetFontHeight(BLOCKFONT) + 2;
2617 
2618 			// build description for keys .. the sector found
2619 			MPrint(x, y0, sKeyDescriptionStrings[0]);
2620 			MPrint(x, y1, sKeyDescriptionStrings[1]);
2621 
2622 			KEY const& key = KeyTable[obj.ubKeyID];
2623 
2624 			SetFontForeground(5);
2625 			ST::string sTempString = GetShortSectorString(SECTORX(key.usSectorFound), SECTORY(key.usSectorFound));
2626 			FindFontRightCoordinates(x, y0, 110, ITEM_STATS_HEIGHT, sTempString, BLOCKFONT2, &usX, &usY);
2627 			MPrint(usX, usY, sTempString);
2628 
2629 			pStr = ST::format("{}", key.usDateFound);
2630 			FindFontRightCoordinates(x, y1, 110, ITEM_STATS_HEIGHT, pStr, BLOCKFONT2, &usX, &usY);
2631 			MPrint(usX, usY, pStr);
2632 		}
2633 	}
2634 }
2635 
2636 
HandleItemDescriptionBox(DirtyLevel * const dirty_level)2637 void HandleItemDescriptionBox(DirtyLevel* const dirty_level)
2638 {
2639 	if ( fItemDescDelete )
2640 	{
2641 		DeleteItemDescriptionBox( );
2642 		fItemDescDelete = FALSE;
2643 		*dirty_level = DIRTYLEVEL2;
2644 	}
2645 
2646 }
2647 
2648 
DeleteItemDescriptionBox()2649 void DeleteItemDescriptionBox( )
2650 {
2651 	INT32 cnt, cnt2;
2652 	BOOLEAN	fFound, fAllFound;
2653 
2654 	if (!gfInItemDescBox) return;
2655 
2656 	//DEF:
2657 
2658 	//Used in the shopkeeper interface
2659 	if (guiCurrentScreen == SHOPKEEPER_SCREEN)
2660 	{
2661 		DeleteShopKeeperItemDescBox();
2662 	}
2663 
2664 	// check for any AP costs
2665 	if (gTacticalStatus.uiFlags & INCOMBAT)
2666 	{
2667 		if (gpAttachSoldier)
2668 		{
2669 			// check for change in attachments, starting with removed attachments
2670 			fAllFound = TRUE;
2671 			for ( cnt = 0; cnt < MAX_ATTACHMENTS; cnt++ )
2672 			{
2673 				if ( gusOriginalAttachItem[ cnt ] != NOTHING )
2674 				{
2675 					fFound = FALSE;
2676 					for ( cnt2 = 0; cnt2 < MAX_ATTACHMENTS; cnt2++ )
2677 					{
2678 						if ( (gusOriginalAttachItem[ cnt ] == gpItemDescObject->usAttachItem[ cnt2 ]) && (gpItemDescObject->bAttachStatus[ cnt2 ] == gbOriginalAttachStatus[ cnt ]) )
2679 						{
2680 							fFound = TRUE;
2681 						}
2682 					}
2683 					if (!fFound)
2684 					{
2685 						// charge APs
2686 						fAllFound = FALSE;
2687 						break;
2688 					}
2689 				}
2690 			}
2691 
2692 			if (fAllFound)
2693 			{
2694 				// nothing was removed; search for attachment added
2695 				for ( cnt = 0; cnt < MAX_ATTACHMENTS; cnt++ )
2696 				{
2697 					if ( gpItemDescObject->usAttachItem[ cnt ] != NOTHING )
2698 					{
2699 						fFound = FALSE;
2700 						for ( cnt2 = 0; cnt2 < MAX_ATTACHMENTS; cnt2++ )
2701 						{
2702 							if ( (gpItemDescObject->usAttachItem[ cnt ] == gusOriginalAttachItem[ cnt2 ]) && (gbOriginalAttachStatus[ cnt2 ] == gpItemDescObject->bAttachStatus[ cnt ]) )
2703 							{
2704 								fFound = TRUE;
2705 							}
2706 						}
2707 						if (!fFound)
2708 						{
2709 							// charge APs
2710 							fAllFound = FALSE;
2711 							break;
2712 						}
2713 					}
2714 				}
2715 			}
2716 
2717 			if (!fAllFound)
2718 			{
2719 				DeductPoints( gpAttachSoldier, AP_RELOAD_GUN, 0 );
2720 			}
2721 		}
2722 	}
2723 
2724 	DeleteVideoObject(guiItemDescBox);
2725 	DeleteVideoObject(guiMapItemDescBox);
2726 	DeleteVideoObject(guiBullet);
2727 	DeleteVideoObject(guiItemGraphic);
2728 
2729 	gfInItemDescBox = FALSE;
2730 
2731 	if( guiCurrentItemDescriptionScreen == MAP_SCREEN )
2732 	{
2733 		RemoveButton( giMapInvDescButton );
2734 	}
2735 
2736 	// Remove region
2737 	MSYS_RemoveRegion( &gInvDesc);
2738 
2739 
2740 	if( gpItemDescObject->usItem != MONEY )
2741 	{
2742 		for ( cnt = 0; cnt < MAX_ATTACHMENTS; cnt++ )
2743 		{
2744 			MSYS_RemoveRegion( &gItemDescAttachmentRegions[cnt]);
2745 		}
2746 	}
2747 	else
2748 	{
2749 		UnloadButtonImage( guiMoneyButtonImage );
2750 		UnloadButtonImage( guiMoneyDoneButtonImage );
2751 		for ( cnt = 0; cnt < MAX_ATTACHMENTS; cnt++ )
2752 		{
2753 			RemoveButton( guiMoneyButtonBtn[cnt] );
2754 		}
2755 	}
2756 
2757 	if ( ITEM_PROS_AND_CONS( gpItemDescObject->usItem ) )
2758 	{
2759 		MSYS_RemoveRegion( &gProsAndConsRegions[0] );
2760 		MSYS_RemoveRegion( &gProsAndConsRegions[1] );
2761 	}
2762 
2763 	if(( ( GCM->getItem(gpItemDescObject->usItem)->isGun()) && gpItemDescObject->usItem != ROCKET_LAUNCHER ) )
2764 	{
2765 		// Remove button
2766 		UnloadButtonImage( giItemDescAmmoButtonImages );
2767 		RemoveButton( giItemDescAmmoButton );
2768 	}
2769 	if( guiCurrentItemDescriptionScreen == MAP_SCREEN )
2770 	{
2771 		fCharacterInfoPanelDirty=TRUE;
2772 		fMapPanelDirty = TRUE;
2773 		fTeamPanelDirty = TRUE;
2774 		fMapScreenBottomDirty = TRUE;
2775 	}
2776 
2777 	if (InKeyRingPopup())
2778 	{
2779 		DeleteKeyObject(gpItemDescObject);
2780 		gpItemDescObject = NULL;
2781 		fShowDescriptionFlag = FALSE;
2782 		fInterfacePanelDirty = DIRTYLEVEL2;
2783 		return;
2784 	}
2785 
2786 	fShowDescriptionFlag = FALSE;
2787 	fInterfacePanelDirty = DIRTYLEVEL2;
2788 
2789 	if( gpItemDescObject->usItem == MONEY )
2790 	{
2791 		//if there is no money remaining
2792 		if( gRemoveMoney.uiMoneyRemaining == 0 && !gfAddingMoneyToMercFromPlayersAccount )
2793 		{
2794 			//get rid of the money in the slot
2795 			*gpItemDescObject = OBJECTTYPE{};
2796 			gpItemDescObject = NULL;
2797 		}
2798 	}
2799 
2800 	if( gfAddingMoneyToMercFromPlayersAccount )
2801 		gfAddingMoneyToMercFromPlayersAccount = FALSE;
2802 
2803 	gfItemDescObjectIsAttachment = FALSE;
2804 }
2805 
2806 
InternalBeginItemPointer(SOLDIERTYPE * pSoldier,OBJECTTYPE * pObject,INT8 bHandPos)2807 void InternalBeginItemPointer( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObject, INT8 bHandPos )
2808 {
2809 	//BOOLEAN fOk;
2810 
2811 	// If not null return
2812 	if ( gpItemPointer != NULL )
2813 	{
2814 		return;
2815 	}
2816 
2817 	// Copy into cursor...
2818 	gItemPointer = *pObject;
2819 
2820 	// Dirty interface
2821 	fInterfacePanelDirty = DIRTYLEVEL2;
2822 	SetItemPointer(&gItemPointer, pSoldier);
2823 	gbItemPointerSrcSlot = bHandPos;
2824 	gbItemPointerLocateGood = TRUE;
2825 
2826 	CheckForDisabledForGiveItem( );
2827 
2828 	EnableSMPanelButtons( FALSE, TRUE );
2829 
2830 	gfItemPointerDifferentThanDefault = FALSE;
2831 
2832 	// re-evaluate repairs
2833 	gfReEvaluateEveryonesNothingToDo = TRUE;
2834 }
2835 
BeginItemPointer(SOLDIERTYPE * pSoldier,UINT8 ubHandPos)2836 void BeginItemPointer( SOLDIERTYPE *pSoldier, UINT8 ubHandPos )
2837 {
2838 	BOOLEAN fOk;
2839 	OBJECTTYPE pObject;
2840 
2841 	pObject = OBJECTTYPE{};
2842 
2843 	if (_KeyDown( SHIFT ))
2844 	{
2845 		// Remove all from soldier's slot
2846 		fOk = RemoveObjectFromSlot( pSoldier, ubHandPos, &pObject );
2847 	}
2848 	else
2849 	{
2850 		GetObjFrom( &(pSoldier->inv[ubHandPos]), 0, &pObject );
2851 		fOk = (pObject.ubNumberOfObjects == 1);
2852 	}
2853 	if (fOk)
2854 	{
2855 		InternalBeginItemPointer( pSoldier, &pObject, ubHandPos );
2856 	}
2857 }
2858 
2859 
BeginKeyRingItemPointer(SOLDIERTYPE * pSoldier,UINT8 ubKeyRingPosition)2860 void BeginKeyRingItemPointer( SOLDIERTYPE *pSoldier, UINT8 ubKeyRingPosition )
2861 {
2862 	BOOLEAN fOk;
2863 
2864 	// If not null return
2865 	if ( gpItemPointer != NULL )
2866 	{
2867 		return;
2868 	}
2869 
2870 	if (_KeyDown( SHIFT ))
2871 	{
2872 		// Remove all from soldier's slot
2873 		fOk = RemoveKeysFromSlot( pSoldier, ubKeyRingPosition, pSoldier->pKeyRing[ ubKeyRingPosition ].ubNumber, &gItemPointer );
2874 	}
2875 	else
2876 	{
2877 		RemoveKeyFromSlot( pSoldier, ubKeyRingPosition, &gItemPointer );
2878 		fOk = (gItemPointer.ubNumberOfObjects == 1);
2879 	}
2880 
2881 
2882 	if (fOk)
2883 	{
2884 		// ATE: Look if we are a BLOODIED KNIFE, and change if so, making guy scream...
2885 
2886 		// Dirty interface
2887 		fInterfacePanelDirty = DIRTYLEVEL2;
2888 		SetItemPointer(&gItemPointer, pSoldier);
2889 		gbItemPointerSrcSlot = ubKeyRingPosition;
2890 
2891 		if (fInMapMode) SetMapCursorItem();
2892 	}
2893 	else
2894 	{
2895 		//Debug mesg
2896 	}
2897 
2898 
2899 
2900 	gfItemPointerDifferentThanDefault = FALSE;
2901 }
2902 
EndItemPointer()2903 void EndItemPointer( )
2904 {
2905 	if ( gpItemPointer != NULL )
2906 	{
2907 		gpItemPointer = NULL;
2908 		gbItemPointerSrcSlot = NO_SLOT;
2909 		gSMPanelRegion.ChangeCursor(CURSOR_NORMAL);
2910 		MSYS_SetCurrentCursor( CURSOR_NORMAL );
2911 
2912 		if (guiCurrentScreen == SHOPKEEPER_SCREEN)
2913 		{
2914 			gMoveingItem = INVENTORY_IN_SLOT{};
2915 			SetSkiCursor( CURSOR_NORMAL );
2916 		}
2917 		else
2918 		{
2919 			EnableSMPanelButtons( TRUE , TRUE );
2920 		}
2921 
2922 		gbItemPointerLocateGood = FALSE;
2923 
2924 		// re-evaluate repairs
2925 		gfReEvaluateEveryonesNothingToDo = TRUE;
2926 	}
2927 }
2928 
DrawItemFreeCursor()2929 void DrawItemFreeCursor( )
2930 {
2931 	SetMouseCursorFromCurrentItem();
2932 	gSMPanelRegion.ChangeCursor(EXTERN_CURSOR);
2933 }
2934 
2935 
SoldierCanSeeCatchComing(const SOLDIERTYPE * pSoldier,INT16 sSrcGridNo)2936 static BOOLEAN SoldierCanSeeCatchComing(const SOLDIERTYPE* pSoldier, INT16 sSrcGridNo)
2937 {
2938 	return( TRUE );
2939 	/*
2940 	INT32 cnt;
2941 	INT8  bDirection, bTargetDirection;
2942 
2943 	bTargetDirection = (INT8)GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, sSrcGridNo );
2944 
2945 	// Look 3 directions Clockwise from what we are facing....
2946 	bDirection = pSoldier->bDirection;
2947 
2948 	for ( cnt = 0; cnt < 3; cnt++ )
2949 	{
2950 		if ( bDirection == bTargetDirection )
2951 		{
2952 			return( TRUE );
2953 		}
2954 
2955 		bDirection = OneCDirection(bDirection);
2956 	}
2957 
2958 	// Look 3 directions CounterClockwise from what we are facing....
2959 	bDirection = pSoldier->bDirection;
2960 
2961 	for ( cnt = 0; cnt < 3; cnt++ )
2962 	{
2963 		if ( bDirection == bTargetDirection )
2964 		{
2965 			return( TRUE );
2966 		}
2967 
2968 		bDirection = OneCCDirection(bDirection);
2969 	}
2970 
2971 	// If here, nothing good can happen!
2972 	return( FALSE );*/
2973 
2974 }
2975 
DrawItemTileCursor()2976 void DrawItemTileCursor( )
2977 {
2978 	INT16 sAPCost;
2979 	BOOLEAN fRecalc;
2980 	INT16 sFinalGridNo;
2981 	UINT32 uiCursorId = CURSOR_ITEM_GOOD_THROW;
2982 	BOOLEAN fGiveItem = FALSE;
2983 	INT16 sActionGridNo;
2984 	static UINT32 uiOldCursorId = 0;
2985 	static UINT16 usOldMousePos = 0;
2986 	INT16 sEndZ = 0;
2987 	INT16 sDist;
2988 	INT8 bLevel;
2989 
2990 	GridNo usMapPos = GetMouseMapPos();
2991 	if (usMapPos != NOWHERE)
2992 	{
2993 		// Force mouse position to guy...
2994 		if (gUIFullTarget != NULL) usMapPos = gUIFullTarget->sGridNo;
2995 
2996 		gusCurMousePos = usMapPos;
2997 
2998 		if( gusCurMousePos != usOldMousePos )
2999 		{
3000 			gfItemPointerDifferentThanDefault = FALSE;
3001 		}
3002 
3003 		// Save old one..
3004 		usOldMousePos = gusCurMousePos;
3005 
3006 		// Default to turning adjacent area gridno off....
3007 		gfUIHandleShowMoveGrid = FALSE;
3008 
3009 		// If we are over a talkable guy, set flag
3010 		if (GetValidTalkableNPCFromMouse(TRUE, FALSE, TRUE) != NULL)
3011 		{
3012 			fGiveItem = TRUE;
3013 		}
3014 
3015 
3016 		// OK, if different than default, change....
3017 		if ( gfItemPointerDifferentThanDefault )
3018 		{
3019 			if (fGiveItem)
3020 			{
3021 				// We are targeting a talkable NPC and are using the alternative cursor to
3022 				// throw instead.
3023 				fGiveItem = FALSE;
3024 			}
3025 			else
3026 			{
3027 				// We are using the alternative cursor and the target is not a talkable NPC
3028 				// Only use the give item cursor if the target is a merc.
3029 				fGiveItem = GetValidTalkableNPCFromMouse(TRUE, TRUE, FALSE) != NULL;
3030 			}
3031 		}
3032 
3033 		// Get recalc and cursor flags
3034 		MouseMoveState uiCursorFlags;
3035 		fRecalc = GetMouseRecalcAndShowAPFlags( &uiCursorFlags, NULL );
3036 
3037 		// OK, if we begin to move, reset the cursor...
3038 		if (uiCursorFlags != MOUSE_STATIONARY)
3039 		{
3040 			EndPhysicsTrajectoryUI( );
3041 		}
3042 
3043 		// Get Pyth spaces away.....
3044 		sDist = PythSpacesAway( gpItemPointerSoldier->sGridNo, gusCurMousePos );
3045 
3046 		// If we are here and we are not selected, select!
3047 		// ATE Design discussion propably needed here...
3048 		SelectSoldier(gpItemPointerSoldier, SELSOLDIER_NONE);
3049 
3050 		// ATE: if good for locate, locate to selected soldier....
3051 		if ( gbItemPointerLocateGood )
3052 		{
3053 			gbItemPointerLocateGood = FALSE;
3054 			LocateSoldier(GetSelectedMan(), FALSE);
3055 		}
3056 
3057 		if ( !fGiveItem )
3058 		{
3059 			if ( UIHandleOnMerc( FALSE ) && usMapPos != gpItemPointerSoldier->sGridNo )
3060 			{
3061 				// We are on a guy.. check if they can catch or not....
3062 				const SOLDIERTYPE* const tgt = gUIFullTarget;
3063 				if (tgt != NULL)
3064 				{
3065 					// Are they on our team?
3066 					// ATE: Can't be an EPC
3067 					if (tgt->bTeam == OUR_TEAM && !AM_AN_EPC(tgt) && !(tgt->uiStatusFlags & SOLDIER_VEHICLE))
3068 					{
3069 						if ( sDist <= PASSING_ITEM_DISTANCE_OKLIFE )
3070 						{
3071 							// OK, on a valid pass
3072 							gfUIMouseOnValidCatcher = 4;
3073 							gUIValidCatcher         = tgt;
3074 						}
3075 						else
3076 						{
3077 							// Can they see the throw?
3078 							if (SoldierCanSeeCatchComing(tgt, gpItemPointerSoldier->sGridNo))
3079 							{
3080 								// OK, set global that this buddy can see catch...
3081 								gfUIMouseOnValidCatcher = TRUE;
3082 								gUIValidCatcher         = tgt;
3083 							}
3084 						}
3085 					}
3086 				}
3087 			}
3088 
3089 			// We're going to toss it!
3090 			if ( gTacticalStatus.uiFlags & INCOMBAT )
3091 			{
3092 				gfUIDisplayActionPoints = TRUE;
3093 				gUIDisplayActionPointsOffX = 15;
3094 				gUIDisplayActionPointsOffY = 15;
3095 			}
3096 
3097 			// If we are tossing...
3098 			if (  (sDist <= 1 && gfUIMouseOnValidCatcher == 0) || gfUIMouseOnValidCatcher == 4 )
3099 			{
3100 				gsCurrentActionPoints = AP_PICKUP_ITEM;
3101 			}
3102 			else
3103 			{
3104 				gsCurrentActionPoints = AP_TOSS_ITEM;
3105 			}
3106 
3107 		}
3108 		else
3109 		{
3110 			const SOLDIERTYPE* const tgt = gUIFullTarget;
3111 			if (tgt != NULL)
3112 			{
3113 				UIHandleOnMerc( FALSE );
3114 
3115 				// OK, set global that this buddy can see catch...
3116 				gfUIMouseOnValidCatcher = 2;
3117 				gUIValidCatcher = tgt;
3118 
3119 				// If this is a robot, change to say 'reload'
3120 				if (tgt->uiStatusFlags & SOLDIER_ROBOT)
3121 				{
3122 					gfUIMouseOnValidCatcher = 3;
3123 				}
3124 
3125 				if (uiCursorFlags == MOUSE_STATIONARY)
3126 				{
3127 					// Find adjacent gridno...
3128 					sActionGridNo = FindAdjacentGridEx(gpItemPointerSoldier, gusCurMousePos, NULL, NULL, FALSE, FALSE);
3129 					if ( sActionGridNo == -1 )
3130 					{
3131 						sActionGridNo = gusCurMousePos;
3132 					}
3133 
3134 					// Display location...
3135 					gsUIHandleShowMoveGridLocation = sActionGridNo;
3136 					gfUIHandleShowMoveGrid = TRUE;
3137 
3138 
3139 					// Get AP cost
3140 					if (tgt->uiStatusFlags & SOLDIER_ROBOT)
3141 					{
3142 						sAPCost = GetAPsToReloadRobot(gpItemPointerSoldier, tgt);
3143 					}
3144 					else
3145 					{
3146 						sAPCost = GetAPsToGiveItem( gpItemPointerSoldier, sActionGridNo );
3147 					}
3148 
3149 					gsCurrentActionPoints = sAPCost;
3150 				}
3151 
3152 				// Set value
3153 				if ( gTacticalStatus.uiFlags & INCOMBAT )
3154 				{
3155 					gfUIDisplayActionPoints = TRUE;
3156 					gUIDisplayActionPointsOffX = 15;
3157 					gUIDisplayActionPointsOffY = 15;
3158 				}
3159 			}
3160 		}
3161 
3162 
3163 		if ( fGiveItem )
3164 		{
3165 			uiCursorId = CURSOR_ITEM_GIVE;
3166 		}
3167 		else
3168 		{
3169 			// How afar away are we?
3170 			if ( sDist <= 1 && gfUIMouseOnValidCatcher == 0 )
3171 			{
3172 				// OK, we want to drop.....
3173 
3174 				// Write the word 'drop' on cursor...
3175 				SetIntTileLocationText(pMessageStrings[MSG_DROP]);
3176 			}
3177 			else
3178 			{
3179 				if ( usMapPos == gpItemPointerSoldier->sGridNo )
3180 				{
3181 					EndPhysicsTrajectoryUI( );
3182 				}
3183 				else if ( gfUIMouseOnValidCatcher == 4 )
3184 				{
3185 					// ATE: Don't do if we are passing....
3186 				}
3187 				else
3188 				// ( sDist > PASSING_ITEM_DISTANCE_OKLIFE )
3189 				{
3190 					// Write the word 'drop' on cursor...
3191 					if ( gfUIMouseOnValidCatcher == 0 )
3192 					{
3193 						SetIntTileLocationText(pMessageStrings[MSG_THROW]);
3194 					}
3195 
3196 					gfUIHandlePhysicsTrajectory = TRUE;
3197 
3198 					if ( fRecalc && usMapPos != gpItemPointerSoldier->sGridNo )
3199 					{
3200 						if ( gfUIMouseOnValidCatcher )
3201 						{
3202 							switch (gAnimControl[gUIValidCatcher->usAnimState].ubHeight)
3203 							{
3204 								case ANIM_STAND:
3205 
3206 									sEndZ = 150;
3207 									break;
3208 
3209 								case ANIM_CROUCH:
3210 
3211 									sEndZ = 80;
3212 									break;
3213 
3214 								case ANIM_PRONE:
3215 
3216 									sEndZ = 10;
3217 									break;
3218 							}
3219 
3220 							if (gUIValidCatcher->bLevel > 0) sEndZ = 0;
3221 						}
3222 
3223 						// Calculate chance to throw here.....
3224 						if ( !CalculateLaunchItemChanceToGetThrough( gpItemPointerSoldier, gpItemPointer, usMapPos, (INT8)gsInterfaceLevel, (INT16)( ( gsInterfaceLevel * 256 ) + sEndZ ), &sFinalGridNo, FALSE, &bLevel, TRUE ) )
3225 						{
3226 							gfBadThrowItemCTGH = TRUE;
3227 						}
3228 						else
3229 						{
3230 							gfBadThrowItemCTGH = FALSE;
3231 						}
3232 
3233 						BeginPhysicsTrajectoryUI( sFinalGridNo, bLevel, gfBadThrowItemCTGH );
3234 					}
3235 				}
3236 
3237 				if ( gfBadThrowItemCTGH )
3238 				{
3239 					uiCursorId = CURSOR_ITEM_BAD_THROW;
3240 				}
3241 			}
3242 		}
3243 
3244 		//Erase any cursor in viewport
3245 		//gViewportRegion.ChangeCursor(VIDEO_NO_CURSOR);
3246 
3247 		// Get tile graphic fro item
3248 		UINT16 const usIndex = GetTileGraphicForItem(GCM->getItem(gpItemPointer->usItem));
3249 
3250 		// ONly load if different....
3251 		if ( usIndex != gusItemPointer || uiOldCursorId != uiCursorId )
3252 		{
3253 			// OK, Tile database gives me subregion and video object to use...
3254 			const TILE_ELEMENT* const te = &gTileDatabase[usIndex];
3255 			SetExternVOData(uiCursorId, te->hTileSurface, te->usRegionIndex);
3256 			gusItemPointer = usIndex;
3257 			uiOldCursorId = uiCursorId;
3258 		}
3259 
3260 		gViewportRegion.ChangeCursor(uiCursorId);
3261 	}
3262 }
3263 
3264 
IsValidAmmoToReloadRobot(SOLDIERTYPE const & s,OBJECTTYPE const & ammo)3265 static bool IsValidAmmoToReloadRobot(SOLDIERTYPE const& s, OBJECTTYPE const& ammo)
3266 {
3267 	OBJECTTYPE const& weapon = s.inv[HANDPOS];
3268 	if (!CompatibleAmmoForGun(&ammo, &weapon))
3269 	{
3270 		ST::string name = *GCM->getWeapon(weapon.usItem)->calibre->getName();
3271 		ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, st_format_printf(TacticalStr[ROBOT_NEEDS_GIVEN_CALIBER_STR], name));
3272 		return false;
3273 	}
3274 	return true;
3275 }
3276 
3277 
HandleItemPointerClick(UINT16 usMapPos)3278 BOOLEAN HandleItemPointerClick( UINT16 usMapPos )
3279 {
3280 	// Determine what to do
3281 	UINT8 ubDirection;
3282 	UINT16 usItem;
3283 	INT16 sAPCost;
3284 	UINT8 ubThrowActionCode=0;
3285 	INT16 sEndZ = 0;
3286 	OBJECTTYPE TempObject;
3287 	INT16 sGridNo;
3288 	INT16 sDist;
3289 	INT16 sDistVisible;
3290 
3291 
3292 	if ( SelectedGuyInBusyAnimation( ) )
3293 	{
3294 		return( FALSE );
3295 	}
3296 
3297 	if (g_ui_message_overlay != NULL)
3298 	{
3299 		EndUIMessage( );
3300 		return( FALSE );
3301 	}
3302 
3303 	// Don't allow if our soldier is a # of things...
3304 	if ( AM_AN_EPC( gpItemPointerSoldier ) || gpItemPointerSoldier->bLife < OKLIFE || gpItemPointerSoldier->bOverTerrainType == DEEP_WATER )
3305 	{
3306 		return( FALSE );
3307 	}
3308 
3309 	// This implies we have no path....
3310 	if ( gsCurrentActionPoints == 0 )
3311 	{
3312 		ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ NO_PATH ] );
3313 		return( FALSE );
3314 	}
3315 
3316 	if (gUIFullTarget != NULL)
3317 	{
3318 		// Force mouse position to guy...
3319 		usMapPos = gUIFullTarget->sGridNo;
3320 
3321 		if (gAnimControl[gUIFullTarget->usAnimState].uiFlags & ANIM_MOVING)
3322 		{
3323 			return( FALSE );
3324 		}
3325 
3326 	}
3327 
3328 	// Check if we have APs....
3329 	if ( !EnoughPoints( gpItemPointerSoldier, gsCurrentActionPoints, 0, TRUE ) )
3330 	{
3331 		if ( gfDontChargeAPsToPickup && gsCurrentActionPoints == AP_PICKUP_ITEM )
3332 		{
3333 
3334 		}
3335 		else
3336 		{
3337 			return( FALSE );
3338 		}
3339 	}
3340 
3341 	// SEE IF WE ARE OVER A TALKABLE GUY!
3342 	SOLDIERTYPE* const tgt = gUIFullTarget;
3343 	BOOLEAN fGiveItem = tgt != NULL && IsValidTalkableNPC(tgt, TRUE, FALSE, TRUE);
3344 
3345 	// OK, if different than default, change....
3346 	if ( gfItemPointerDifferentThanDefault )
3347 	{
3348 		if (fGiveItem)
3349 		{
3350 			// We are targeting a talkable NPC and are using the alternative cursor to
3351 			// throw instead.
3352 			fGiveItem = FALSE;
3353 		}
3354 		else
3355 		{
3356 			// We are using the alternative cursor and the target is not a talkable NPC
3357 			// Only give an item if the target is a merc.
3358 			fGiveItem = tgt != NULL && IsValidTalkableNPC(tgt, TRUE, TRUE, TRUE);
3359 		}
3360 	}
3361 
3362 
3363 	// Get Pyth spaces away.....
3364 	sDist = PythSpacesAway( gpItemPointerSoldier->sGridNo, gusCurMousePos );
3365 
3366 
3367 	if ( fGiveItem )
3368 	{
3369 		usItem = gpItemPointer->usItem;
3370 
3371 		// If the target is a robot,
3372 		if (tgt->uiStatusFlags & SOLDIER_ROBOT)
3373 		{
3374 			// Charge APs to reload robot!
3375 			sAPCost = GetAPsToReloadRobot(gpItemPointerSoldier, tgt);
3376 		}
3377 		else
3378 		{
3379 			// Calculate action point costs!
3380 			sAPCost = GetAPsToGiveItem( gpItemPointerSoldier, usMapPos );
3381 		}
3382 
3383 		// Place it back in our hands!
3384 
3385 		TempObject = *gpItemPointer;
3386 
3387 		if ( gbItemPointerSrcSlot != NO_SLOT )
3388 		{
3389 			PlaceObject( gpItemPointerSoldier, gbItemPointerSrcSlot, gpItemPointer );
3390 			fInterfacePanelDirty = DIRTYLEVEL2;
3391 		}
3392 		/*
3393 		//if the user just clicked on an arms dealer
3394 		if (IsMercADealer(tgt->ubProfile))
3395 		{
3396 			if ( EnoughPoints( gpItemPointerSoldier, sAPCost, 0, TRUE ) )
3397 			{
3398 				//Enter the shopkeeper interface
3399 				EnterShopKeeperInterfaceScreen(tgt->ubProfile);
3400 
3401 				EndItemPointer( );
3402 			}
3403 
3404 			return( TRUE );
3405 		}*/
3406 
3407 		if ( EnoughPoints( gpItemPointerSoldier, sAPCost, 0, TRUE ) )
3408 		{
3409 			// If we are a robot, check if this is proper item to reload!
3410 			if (tgt->uiStatusFlags & SOLDIER_ROBOT)
3411 			{
3412 				// Check if we can reload robot....
3413 				if (IsValidAmmoToReloadRobot(*tgt, TempObject))
3414 				{
3415 					INT16 sActionGridNo;
3416 					UINT8 ubDirection;
3417 					INT16 sAdjustedGridNo;
3418 
3419 					// Walk up to him and reload!
3420 					// See if we can get there to stab
3421 					sActionGridNo = FindAdjacentGridEx(gpItemPointerSoldier, tgt->sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE);
3422 
3423 					if ( sActionGridNo != -1 && gbItemPointerSrcSlot != NO_SLOT )
3424 					{
3425 							// Make a temp object for ammo...
3426 							gpItemPointerSoldier->pTempObject  = new OBJECTTYPE{};
3427 							*gpItemPointerSoldier->pTempObject = TempObject;
3428 
3429 							// Remove from soldier's inv...
3430 							RemoveObjs( &( gpItemPointerSoldier->inv[ gbItemPointerSrcSlot ] ), 1 );
3431 
3432 							gpItemPointerSoldier->sPendingActionData2  = sAdjustedGridNo;
3433 							gpItemPointerSoldier->uiPendingActionData1 = gbItemPointerSrcSlot;
3434 							gpItemPointerSoldier->bPendingActionData3  = ubDirection;
3435 							gpItemPointerSoldier->ubPendingActionAnimCount = 0;
3436 
3437 							// CHECK IF WE ARE AT THIS GRIDNO NOW
3438 							if ( gpItemPointerSoldier->sGridNo != sActionGridNo )
3439 							{
3440 								SoldierSP soldier = GetSoldier(gpItemPointerSoldier);
3441 
3442 								soldier->setPendingAction(MERC_RELOADROBOT);
3443 
3444 								// WALK UP TO DEST FIRST
3445 								EVENT_InternalGetNewSoldierPath( gpItemPointerSoldier, sActionGridNo, gpItemPointerSoldier->usUIMovementMode, FALSE, FALSE );
3446 							}
3447 							else
3448 							{
3449 								EVENT_SoldierBeginReloadRobot( gpItemPointerSoldier, sAdjustedGridNo, ubDirection, gbItemPointerSrcSlot );
3450 							}
3451 
3452 							// OK, set UI
3453 							SetUIBusy(gpItemPointerSoldier);
3454 					}
3455 
3456 				}
3457 
3458 				gfDontChargeAPsToPickup = FALSE;
3459 				EndItemPointer( );
3460 			}
3461 			else
3462 			{
3463 				//if (gbItemPointerSrcSlot != NO_SLOT )
3464 				{
3465 					// Give guy this item.....
3466 					SoldierGiveItem(gpItemPointerSoldier, tgt, &TempObject, gbItemPointerSrcSlot);
3467 
3468 					gfDontChargeAPsToPickup = FALSE;
3469 					EndItemPointer( );
3470 
3471 					// If we are giving it to somebody not on our team....
3472 					if (tgt->ubProfile != NO_PROFILE && !MercProfile(tgt->ubProfile).isPlayerMerc() && !RPC_RECRUITED(tgt))
3473 					{
3474 						SetEngagedInConvFromPCAction( gpItemPointerSoldier );
3475 					}
3476 				}
3477 			}
3478 		}
3479 
3480 		return( TRUE );
3481 	}
3482 
3483 	// CHECK IF WE ARE NOT ON THE SAME GRIDNO
3484 	if (sDist <= 1 &&
3485 			(gUIFullTarget == NULL || gUIFullTarget == gpItemPointerSoldier))
3486 	{
3487 		// Check some things here....
3488 		// 1 ) are we at the exact gridno that we stand on?
3489 		if ( usMapPos == gpItemPointerSoldier->sGridNo )
3490 		{
3491 			// Drop
3492 			if ( !gfDontChargeAPsToPickup )
3493 			{
3494 				// Deduct points
3495 				DeductPoints( gpItemPointerSoldier, AP_PICKUP_ITEM, 0 );
3496 			}
3497 
3498 			SoldierDropItem( gpItemPointerSoldier, gpItemPointer );
3499 		}
3500 		else
3501 		{
3502 			// Try to drop in an adjacent area....
3503 			// 1 ) is this not a good OK destination
3504 			// this will sound strange, but this is OK......
3505 			if ( !NewOKDestination( gpItemPointerSoldier, usMapPos, FALSE, gpItemPointerSoldier->bLevel ) || FindBestPath( gpItemPointerSoldier, usMapPos, gpItemPointerSoldier->bLevel, WALKING, NO_COPYROUTE, 0 ) == 1 )
3506 			{
3507 				// Drop
3508 				if ( !gfDontChargeAPsToPickup )
3509 				{
3510 					// Deduct points
3511 					DeductPoints( gpItemPointerSoldier, AP_PICKUP_ITEM, 0 );
3512 				}
3513 
3514 				// Play animation....
3515 				// Don't show animation of dropping item, if we are not standing
3516 
3517 
3518 
3519 				switch ( gAnimControl[ gpItemPointerSoldier->usAnimState ].ubHeight )
3520 				{
3521 					case ANIM_STAND:
3522 						gpItemPointerSoldier->pTempObject = new OBJECTTYPE{};
3523 						*gpItemPointerSoldier->pTempObject = *gpItemPointer;
3524 						gpItemPointerSoldier->sPendingActionData2 = usMapPos;
3525 
3526 						// Turn towards.....gridno
3527 						EVENT_SetSoldierDesiredDirectionForward(gpItemPointerSoldier, (INT8)GetDirectionFromGridNo(usMapPos, gpItemPointerSoldier));
3528 
3529 						EVENT_InitNewSoldierAnim( gpItemPointerSoldier, DROP_ADJACENT_OBJECT, 0 , FALSE );
3530 						break;
3531 
3532 					case ANIM_CROUCH:
3533 					case ANIM_PRONE:
3534 						AddItemToPool(usMapPos, gpItemPointer, VISIBLE, gpItemPointerSoldier->bLevel, 0 , -1);
3535 						NotifySoldiersToLookforItems( );
3536 						break;
3537 				}
3538 			}
3539 			else
3540 			{
3541 				// Drop in place...
3542 				if ( !gfDontChargeAPsToPickup )
3543 				{
3544 					// Deduct points
3545 					DeductPoints( gpItemPointerSoldier, AP_PICKUP_ITEM, 0 );
3546 				}
3547 
3548 				SoldierDropItem( gpItemPointerSoldier, gpItemPointer );
3549 			}
3550 		}
3551 	}
3552 	else
3553 	{
3554 		sGridNo = usMapPos;
3555 
3556 		SOLDIERTYPE* const pSoldier = gUIFullTarget;
3557 		if (sDist <= PASSING_ITEM_DISTANCE_OKLIFE &&
3558 			pSoldier != NULL &&
3559 			pSoldier->bTeam == OUR_TEAM &&
3560 			!AM_AN_EPC(pSoldier) &&
3561 			!(pSoldier->uiStatusFlags & SOLDIER_VEHICLE))
3562 		{
3563 			// OK, do the transfer...
3564 			{
3565 				{
3566 					if ( !EnoughPoints( pSoldier, 3, 0, TRUE ) ||
3567 						!EnoughPoints( gpItemPointerSoldier, 3, 0, TRUE ) )
3568 					{
3569 						return( FALSE );
3570 					}
3571 
3572 					sDistVisible = DistanceVisible( pSoldier, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, gpItemPointerSoldier->sGridNo, gpItemPointerSoldier->bLevel );
3573 
3574 					// Check LOS....
3575 					if ( !SoldierTo3DLocationLineOfSightTest( pSoldier, gpItemPointerSoldier->sGridNo,  gpItemPointerSoldier->bLevel, 3, (UINT8) sDistVisible, TRUE ) )
3576 					{
3577 						return( FALSE );
3578 					}
3579 
3580 					// Charge AP values...
3581 					DeductPoints( pSoldier, 3, 0 );
3582 					DeductPoints( gpItemPointerSoldier, 3, 0 );
3583 
3584 					usItem = gpItemPointer->usItem;
3585 
3586 					// try to auto place object....
3587 					if ( AutoPlaceObject( pSoldier, gpItemPointer, TRUE ) )
3588 					{
3589 						ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, st_format_printf(pMessageStrings[ MSG_ITEM_PASSED_TO_MERC ], ShortItemNames[ usItem ], pSoldier->name) );
3590 
3591 						// Check if it's the same now!
3592 						if ( gpItemPointer->ubNumberOfObjects == 0 )
3593 						{
3594 							EndItemPointer( );
3595 						}
3596 
3597 						// OK, make guys turn towards each other and do animation...
3598 						{
3599 							UINT8 ubFacingDirection;
3600 
3601 							// Get direction to face.....
3602 							ubFacingDirection = (UINT8)GetDirectionFromGridNo( gpItemPointerSoldier->sGridNo, pSoldier );
3603 
3604 							// Stop merc first....
3605 							EVENT_StopMerc(pSoldier);
3606 
3607 							// If we are standing only...
3608 							if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && !MercInWater( pSoldier ) )
3609 							{
3610 								// Turn to face, then do animation....
3611 								EVENT_SetSoldierDesiredDirection( pSoldier, ubFacingDirection );
3612 								pSoldier->fTurningUntilDone = TRUE;
3613 								pSoldier->usPendingAnimation = PASS_OBJECT;
3614 							}
3615 
3616 							if ( gAnimControl[ gpItemPointerSoldier->usAnimState ].ubEndHeight == ANIM_STAND && !MercInWater( gpItemPointerSoldier ) )
3617 							{
3618 								EVENT_SetSoldierDesiredDirection(gpItemPointerSoldier, OppositeDirection(ubFacingDirection));
3619 								gpItemPointerSoldier->fTurningUntilDone = TRUE;
3620 								gpItemPointerSoldier->usPendingAnimation = PASS_OBJECT;
3621 							}
3622 						}
3623 
3624 						return( TRUE );
3625 					}
3626 					else
3627 					{
3628 						ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, st_format_printf(pMessageStrings[ MSG_NO_ROOM_TO_PASS_ITEM ], ShortItemNames[ usItem ], pSoldier->name) );
3629 						return( FALSE );
3630 					}
3631 				}
3632 			}
3633 		}
3634 		else
3635 		{
3636 			// CHECK FOR VALID CTGH
3637 			if ( gfBadThrowItemCTGH )
3638 			{
3639 				ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ CANNOT_THROW_TO_DEST_STR ] );
3640 				return( FALSE );
3641 			}
3642 
3643 			// Deduct points
3644 			//DeductPoints( gpItemPointerSoldier, AP_TOSS_ITEM, 0 );
3645 			gpItemPointerSoldier->fDontChargeTurningAPs = TRUE;
3646 			// Will be dome later....
3647 
3648 			ubThrowActionCode = NO_THROW_ACTION;
3649 
3650 			// OK, CHECK FOR VALID THROW/CATCH
3651 			// IF OVER OUR GUY...
3652 			SOLDIERTYPE* target = NULL;
3653 			if (pSoldier != NULL)
3654 			{
3655 				if (pSoldier->bTeam == OUR_TEAM && pSoldier->bLife >= OKLIFE && !AM_AN_EPC(pSoldier) &&
3656 					!(pSoldier->uiStatusFlags & SOLDIER_VEHICLE))
3657 				{
3658 					// OK, on our team,
3659 
3660 					// How's our direction?
3661 					if ( SoldierCanSeeCatchComing( pSoldier, gpItemPointerSoldier->sGridNo ) )
3662 					{
3663 						// Setup as being the catch target
3664 						ubThrowActionCode = THROW_TARGET_MERC_CATCH;
3665 						target            = pSoldier;
3666 
3667 						sGridNo = pSoldier->sGridNo;
3668 
3669 						switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
3670 						{
3671 							case ANIM_STAND:
3672 								sEndZ = 150;
3673 								break;
3674 
3675 							case ANIM_CROUCH:
3676 								sEndZ = 80;
3677 								break;
3678 
3679 							case ANIM_PRONE:
3680 								sEndZ = 10;
3681 								break;
3682 						}
3683 
3684 						if ( pSoldier->bLevel > 0 )
3685 						{
3686 							sEndZ = 0;
3687 						}
3688 
3689 						// Get direction
3690 						ubDirection = (UINT8)GetDirectionFromGridNo( gpItemPointerSoldier->sGridNo, pSoldier );
3691 
3692 						// ATE: Goto stationary...
3693 						SoldierGotoStationaryStance( pSoldier );
3694 
3695 						// Set direction to turn...
3696 						EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
3697 					}
3698 				}
3699 			}
3700 
3701 			// CHANGE DIRECTION AT LEAST
3702 			ubDirection = (UINT8)GetDirectionFromGridNo( sGridNo, gpItemPointerSoldier );
3703 			EVENT_SetSoldierDesiredDirection( gpItemPointerSoldier, ubDirection );
3704 			gpItemPointerSoldier->fTurningUntilDone = TRUE;
3705 
3706 			// Increment attacker count...
3707 			gTacticalStatus.ubAttackBusyCount++;
3708 			SLOGD("INcremtning ABC: Throw item to %d", gTacticalStatus.ubAttackBusyCount);
3709 
3710 			// Given our gridno, throw grenate!
3711 			CalculateLaunchItemParamsForThrow(gpItemPointerSoldier, sGridNo, gpItemPointerSoldier->bLevel, gsInterfaceLevel * 256 + sEndZ, gpItemPointer, 0, ubThrowActionCode, target);
3712 
3713 			// OK, goto throw animation
3714 			HandleSoldierThrowItem( gpItemPointerSoldier, usMapPos );
3715 		}
3716 	}
3717 
3718 	gfDontChargeAPsToPickup = FALSE;
3719 	EndItemPointer( );
3720 
3721 
3722 	return( TRUE );
3723 }
3724 
3725 
InItemStackPopup()3726 BOOLEAN InItemStackPopup( )
3727 {
3728 	return( gfInItemStackPopup );
3729 }
3730 
3731 
InKeyRingPopup()3732 BOOLEAN InKeyRingPopup( )
3733 {
3734 	return( gfInKeyRingPopup );
3735 }
3736 
3737 
3738 static void ItemPopupFullRegionCallback(MOUSE_REGION* pRegion, INT32 iReason);
3739 static void ItemPopupRegionCallback(MOUSE_REGION* pRegion, INT32 iReason);
3740 
3741 
InitItemStackPopup(SOLDIERTYPE * const pSoldier,UINT8 const ubPosition,INT16 const sInvX,INT16 const sInvY,INT16 const sInvWidth,INT16 const sInvHeight)3742 void InitItemStackPopup(SOLDIERTYPE* const pSoldier, UINT8 const ubPosition, INT16 const sInvX, INT16 const sInvY, INT16 const sInvWidth, INT16 const sInvHeight)
3743 {
3744 	SGPRect aRect;
3745 	UINT8 ubLimit;
3746 	UINT8 ubCols;
3747 	UINT8 ubRows;
3748 	INT32 cnt;
3749 
3750 	// Set some globals
3751 	gsItemPopupInvX = sInvX;
3752 	gsItemPopupInvY = sInvY;
3753 	gsItemPopupInvWidth = sInvWidth;
3754 	gsItemPopupInvHeight = sInvHeight;
3755 
3756 
3757 	gpItemPopupSoldier = pSoldier;
3758 
3759 
3760 	// Determine # of items
3761 	gpItemPopupObject = &(pSoldier->inv[ ubPosition ] );
3762 	ubLimit = ItemSlotLimit( gpItemPopupObject->usItem, ubPosition );
3763 
3764 	// Return if #objects not >1
3765 	if (ubLimit < 1) return;
3766 
3767 	if( ubLimit > MAX_STACK_POPUP_WIDTH )
3768 	{
3769 		ubCols = MAX_STACK_POPUP_WIDTH;
3770 		ubRows = ubLimit / MAX_STACK_POPUP_WIDTH;
3771 	} else {
3772 		ubCols = ubLimit;
3773 		ubRows = 0;
3774 	}
3775 
3776 	// Load graphics
3777 	guiItemPopupBoxes = AddVideoObjectFromFile(INTERFACEDIR "/extra_inventory.sti");
3778 
3779 	// Get size
3780 	ETRLEObject const& pTrav        = guiItemPopupBoxes->SubregionProperties(0);
3781 	UINT16      const  usPopupWidth = pTrav.usWidth;
3782 	UINT16      const  usPopupHeight = pTrav.usHeight;
3783 
3784 	// Get Width, Height
3785 	INT16 gsItemPopupWidth = ubCols * usPopupWidth;
3786 	INT16 gsItemPopupHeight = ubRows * usPopupHeight;
3787 	gubNumItemPopups = ubLimit;
3788 
3789 	// Calculate X,Y, first center
3790 	MOUSE_REGION const& r = gSMInvRegion[ubPosition];
3791 	INT16 sCenX = r.X() - (gsItemPopupWidth / 2 + r.W() / 2);
3792 	INT16 sCenY	= r.Y()- (gsItemPopupHeight / 2 + r.H() / 2);
3793 
3794 	// Limit it to window for item desc
3795 	if ( sCenX < gsItemPopupInvX )
3796 	{
3797 		sCenX = gsItemPopupInvX;
3798 	}
3799 	if ( ( sCenX + gsItemPopupWidth ) > ( gsItemPopupInvX + gsItemPopupInvWidth ) )
3800 	{
3801 		sCenX = gsItemPopupInvX + gsItemPopupInvWidth - gsItemPopupWidth;
3802 	}
3803 	if ( sCenY < gsItemPopupInvY )
3804 	{
3805 		sCenY = gsItemPopupInvY;
3806 	}
3807 	if ( sCenY + gsItemPopupHeight > ( gsItemPopupInvY + gsItemPopupInvHeight ) )
3808 	{
3809 		sCenY = gsItemPopupInvY + gsItemPopupInvHeight - gsItemPopupHeight;
3810 	}
3811 
3812 	// Cap it at 0....
3813 	if ( sCenX < 0 )
3814 	{
3815 		sCenX = 0;
3816 	}
3817 	if ( sCenY < 0 )
3818 	{
3819 		sCenY = 0;
3820 	}
3821 
3822 	// Set
3823 	gsItemPopupX	= sCenX;
3824 	gsItemPopupY	= sCenY;
3825 
3826 	for ( cnt = 0; cnt < gubNumItemPopups; cnt++ )
3827 	{
3828 		UINT32 row = cnt / MAX_STACK_POPUP_WIDTH;
3829 		UINT32 col = cnt % MAX_STACK_POPUP_WIDTH;
3830 
3831 		// Build a mouse region here that is over any others.....
3832 		MSYS_DefineRegion(&gItemPopupRegions[cnt], sCenX + col * usPopupWidth, sCenY + row * usPopupHeight, sCenX + (col + 1) * usPopupWidth, sCenY + (row+1) * usPopupHeight, MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ItemPopupRegionCallback);
3833 		MSYS_SetRegionUserData( &gItemPopupRegions[cnt], 0, cnt );
3834 
3835 		//OK, for each item, set dirty text if applicable!
3836 		gItemPopupRegions[cnt].SetFastHelpText(ItemNames[pSoldier->inv[ubPosition].usItem]);
3837 		gfItemPopupRegionCallbackEndFix = FALSE;
3838 	}
3839 
3840 
3841 	// Build a mouse region here that is over any others.....
3842 	MSYS_DefineRegion(&gItemPopupRegion, gsItemPopupInvX, gsItemPopupInvY, gsItemPopupInvX + gsItemPopupInvWidth, gsItemPopupInvY + gsItemPopupInvHeight, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ItemPopupFullRegionCallback);
3843 
3844 
3845 	//Disable all faces
3846 	SetAllAutoFacesInactive( );
3847 
3848 
3849 	fInterfacePanelDirty = DIRTYLEVEL2;
3850 
3851 	gfInItemStackPopup = TRUE;
3852 
3853 	if( guiCurrentItemDescriptionScreen != MAP_SCREEN )
3854 	{
3855 		EnableSMPanelButtons( FALSE, FALSE );
3856 	}
3857 
3858 	//Reserict mouse cursor to panel
3859 	aRect.iTop = sInvY;
3860 	aRect.iLeft = sInvX;
3861 	aRect.iBottom = sInvY + sInvHeight;
3862 	aRect.iRight = sInvX + sInvWidth;
3863 
3864 	RestrictMouseCursor( &aRect );
3865 }
3866 
3867 
3868 static void DeleteItemStackPopup(void);
3869 
3870 
EndItemStackPopupWithItemInHand(void)3871 static void EndItemStackPopupWithItemInHand(void)
3872 {
3873 	if ( gpItemPointer != NULL )
3874 	{
3875 		DeleteItemStackPopup( );
3876 	}
3877 }
3878 
RenderItemStackPopup(BOOLEAN fFullRender)3879 void RenderItemStackPopup( BOOLEAN fFullRender )
3880 {
3881 	if ( gfInItemStackPopup )
3882 	{
3883 
3884 		//Disable all faces
3885 		SetAllAutoFacesInactive( );
3886 
3887 		// Shadow Area
3888 		if ( fFullRender )
3889 		{
3890 			FRAME_BUFFER->ShadowRect(gsItemPopupInvX, gsItemPopupInvY, gsItemPopupInvX + gsItemPopupInvWidth, gsItemPopupInvY + gsItemPopupInvHeight);
3891 		}
3892 
3893 	}
3894 	// TAKE A LOOK AT THE VIDEO OBJECT SIZE ( ONE OF TWO SIZES ) AND CENTER!
3895 	ETRLEObject const& pTrav  = guiItemPopupBoxes->SubregionProperties(0);
3896 	UINT32 const usWidth = pTrav.usWidth;
3897 	UINT32 const usHeight = pTrav.usHeight;
3898 
3899 	for (UINT32 cnt = 0; cnt < gubNumItemPopups; cnt++)
3900 	{
3901 		UINT32 row = cnt / MAX_STACK_POPUP_WIDTH;
3902 		UINT32 col = cnt % MAX_STACK_POPUP_WIDTH;
3903 
3904 		BltVideoObject(FRAME_BUFFER, guiItemPopupBoxes, 0, gsItemPopupX + col * usWidth, gsItemPopupY + row * usHeight);
3905 
3906 		if ( cnt < gpItemPopupObject->ubNumberOfObjects )
3907 		{
3908 			INT16 sX = gsItemPopupX + col * usWidth + 11;
3909 			INT16 sY = gsItemPopupY + row * usHeight + 3;
3910 
3911 			INVRenderItem(FRAME_BUFFER, NULL, *gpItemPopupObject, sX, sY, 29, 23, DIRTYLEVEL2, RENDER_ITEM_NOSTATUS, SGP_TRANSPARENT);
3912 
3913 			// Do status bar here...
3914 			INT16 sNewX = gsItemPopupX + col * usWidth + 7;
3915 			INT16 sNewY = gsItemPopupY + row * usHeight + INV_BAR_DY + 3;
3916 			DrawItemUIBarEx(*gpItemPopupObject, cnt, sNewX, sNewY, ITEM_BAR_HEIGHT, Get16BPPColor(STATUS_BAR), Get16BPPColor(STATUS_BAR_SHADOW), FRAME_BUFFER);
3917 		}
3918 	}
3919 
3920 	//RestoreExternBackgroundRect( gsItemPopupInvX, gsItemPopupInvY, gsItemPopupInvWidth, gsItemPopupInvHeight );
3921 	InvalidateRegion( gsItemPopupInvX, gsItemPopupInvY, gsItemPopupInvX + gsItemPopupInvWidth, gsItemPopupInvY + gsItemPopupInvHeight );
3922 
3923 }
3924 
3925 
DeleteItemStackPopup(void)3926 static void DeleteItemStackPopup(void)
3927 {
3928 	INT32 cnt;
3929 
3930 	DeleteVideoObject(guiItemPopupBoxes);
3931 
3932 	MSYS_RemoveRegion( &gItemPopupRegion);
3933 
3934 
3935 	gfInItemStackPopup = FALSE;
3936 
3937 	for ( cnt = 0; cnt < gubNumItemPopups; cnt++ )
3938 	{
3939 		MSYS_RemoveRegion( &gItemPopupRegions[cnt]);
3940 	}
3941 
3942 
3943 	fInterfacePanelDirty = DIRTYLEVEL2;
3944 
3945 	if( guiCurrentItemDescriptionScreen != MAP_SCREEN )
3946 	{
3947 		EnableSMPanelButtons( TRUE, FALSE );
3948 	}
3949 
3950 	FreeMouseCursor( );
3951 
3952 }
3953 
3954 
InitKeyRingPopup(SOLDIERTYPE * const pSoldier,INT16 const sInvX,INT16 const sInvY,INT16 const sInvWidth,INT16 const sInvHeight)3955 void InitKeyRingPopup(SOLDIERTYPE* const pSoldier, INT16 const sInvX, INT16 const sInvY, INT16 const sInvWidth, INT16 const sInvHeight)
3956 {
3957 	SGPRect aRect;
3958 	INT16 sKeyRingItemWidth = 0;
3959 	INT16 sOffSetY = 0, sOffSetX = 0;
3960 
3961 	if( guiCurrentScreen == MAP_SCREEN )
3962 	{
3963 		gsKeyRingPopupInvX = STD_SCREEN_X + 0;
3964 		sKeyRingItemWidth = MAP_KEY_RING_ROW_WIDTH;
3965 		sOffSetX = 40;
3966 		sOffSetY = 15;
3967 	}
3968 	else
3969 	{
3970 		// Set some globals
3971 		gsKeyRingPopupInvX = sInvX + TACTICAL_INVENTORY_KEYRING_GRAPHIC_OFFSET_X;
3972 		sKeyRingItemWidth = KEY_RING_ROW_WIDTH;
3973 		sOffSetY = 8;
3974 	}
3975 
3976 	gsKeyRingPopupInvY = sInvY;
3977 	gsKeyRingPopupInvWidth = sInvWidth;
3978 	gsKeyRingPopupInvHeight = sInvHeight;
3979 
3980 
3981 	gpItemPopupSoldier = pSoldier;
3982 
3983 	// Load graphics
3984 	guiItemPopupBoxes = AddVideoObjectFromFile(INTERFACEDIR "/extra_inventory.sti");
3985 
3986 	// Get size
3987 	ETRLEObject const& pTrav         = guiItemPopupBoxes->SubregionProperties(0);
3988 	UINT16      const  usPopupWidth  = pTrav.usWidth;
3989 	UINT16      const  usPopupHeight = pTrav.usHeight;
3990 
3991 	for (INT32 cnt = 0; cnt < NUMBER_KEYS_ON_KEYRING; cnt++)
3992 	{
3993 		// Build a mouse region here that is over any others.....
3994 		MSYS_DefineRegion(&gKeyRingRegions[cnt],
3995 			gsKeyRingPopupInvX + (cnt % sKeyRingItemWidth      * usPopupWidth)  + sOffSetX, // top left
3996 			sInvY              + (cnt / sKeyRingItemWidth      * usPopupHeight) + sOffSetY, // top right
3997 			gsKeyRingPopupInvX + (cnt % sKeyRingItemWidth + 1) * usPopupWidth   + sOffSetX, // bottom left
3998 			sInvY              + (cnt / sKeyRingItemWidth + 1) * usPopupHeight  + sOffSetY, // bottom right
3999 			MSYS_PRIORITY_HIGHEST,
4000 			MSYS_NO_CURSOR, MSYS_NO_CALLBACK, KeyRingSlotInvClickCallback
4001 		);
4002 		MSYS_SetRegionUserData( &gKeyRingRegions[cnt], 0, cnt );
4003 		//gfItemPopupRegionCallbackEndFix = FALSE;
4004 	}
4005 
4006 
4007 	// Build a mouse region here that is over any others.....
4008 	MSYS_DefineRegion(&gItemPopupRegion, sInvX, sInvY, sInvX + sInvWidth, sInvY + sInvHeight, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ItemPopupFullRegionCallback);
4009 
4010 
4011 	//Disable all faces
4012 	SetAllAutoFacesInactive( );
4013 
4014 
4015 	fInterfacePanelDirty = DIRTYLEVEL2;
4016 
4017 	if( guiCurrentItemDescriptionScreen != MAP_SCREEN )
4018 	{
4019 		EnableSMPanelButtons( FALSE , FALSE );
4020 	}
4021 
4022 	gfInKeyRingPopup = TRUE;
4023 
4024 	//Reserict mouse cursor to panel
4025 	aRect.iTop = sInvY;
4026 	aRect.iLeft = sInvX;
4027 	aRect.iBottom = sInvY + sInvHeight;
4028 	aRect.iRight = sInvX + sInvWidth;
4029 
4030 	RestrictMouseCursor( &aRect );
4031 }
4032 
4033 
RenderKeyRingPopup(const BOOLEAN fFullRender)4034 void RenderKeyRingPopup(const BOOLEAN fFullRender)
4035 {
4036 	const INT16 dx = gsKeyRingPopupInvX;
4037 	const INT16 dy = gsKeyRingPopupInvY;
4038 
4039 	if (gfInKeyRingPopup)
4040 	{
4041 		SetAllAutoFacesInactive();
4042 
4043 		if (fFullRender)
4044 		{
4045 			FRAME_BUFFER->ShadowRect(0, dy, dx + gsKeyRingPopupInvWidth, dy + gsKeyRingPopupInvHeight);
4046 		}
4047 	}
4048 
4049 	OBJECTTYPE o;
4050 	o = OBJECTTYPE{};
4051 	o.bStatus[0] = 100;
4052 
4053 	ETRLEObject const& pTrav = guiItemPopupBoxes->SubregionProperties(0);
4054 	UINT32      const  box_w = pTrav.usWidth;
4055 	UINT32      const  box_h = pTrav.usHeight;
4056 
4057 	INT16 offset_x;
4058 	INT16 offset_y;
4059 	INT16 key_ring_cols;
4060 	if (guiCurrentScreen == MAP_SCREEN)
4061 	{
4062 		offset_x      = 40;
4063 		offset_y      = 15;
4064 		key_ring_cols = MAP_KEY_RING_ROW_WIDTH;
4065 	}
4066 	else
4067 	{
4068 		offset_x      = 0;
4069 		offset_y      = 8;
4070 		key_ring_cols = KEY_RING_ROW_WIDTH;
4071 	}
4072 
4073 	const KEY_ON_RING* const key_ring = gpItemPopupSoldier->pKeyRing;
4074 	for (UINT32 i = 0; i < NUMBER_KEYS_ON_KEYRING; ++i)
4075 	{
4076 		const UINT x = dx + offset_x + i % key_ring_cols * box_w;
4077 		const UINT y = dy + offset_y + i / key_ring_cols * box_h;
4078 
4079 		BltVideoObject(FRAME_BUFFER, guiItemPopupBoxes, 0, x, y);
4080 
4081 		const KEY_ON_RING* const key = &key_ring[i];
4082 		if (key->ubKeyID == INVALID_KEY_NUMBER || key->ubNumber == 0) continue;
4083 
4084 		o.ubNumberOfObjects = key->ubNumber;
4085 		o.usItem            = FIRST_KEY + LockTable[key->ubKeyID].usKeyItem;
4086 
4087 		DrawItemUIBarEx(o, 0, x + 7, y + 24, ITEM_BAR_HEIGHT, Get16BPPColor(STATUS_BAR), Get16BPPColor(STATUS_BAR_SHADOW), FRAME_BUFFER);
4088 		INVRenderItem(FRAME_BUFFER, NULL, o, x + 8, y, box_w - 8, box_h - 2, DIRTYLEVEL2, 0, SGP_TRANSPARENT);
4089 	}
4090 
4091 	InvalidateRegion(dx, dy, dx + gsKeyRingPopupInvWidth, dy + gsKeyRingPopupInvHeight);
4092 }
4093 
4094 
DeleteKeyRingPopup(void)4095 void DeleteKeyRingPopup(void)
4096 {
4097 	if (!gfInKeyRingPopup) return;
4098 
4099 	DeleteVideoObject(guiItemPopupBoxes);
4100 
4101 	MSYS_RemoveRegion(&gItemPopupRegion);
4102 
4103 	gfInKeyRingPopup = FALSE;
4104 
4105 	for (INT32 i = 0; i < NUMBER_KEYS_ON_KEYRING; i++)
4106 	{
4107 		MSYS_RemoveRegion(&gKeyRingRegions[i]);
4108 	}
4109 
4110 	fInterfacePanelDirty = DIRTYLEVEL2;
4111 
4112 	if (guiCurrentItemDescriptionScreen != MAP_SCREEN)
4113 	{
4114 		EnableSMPanelButtons(TRUE, FALSE);
4115 	}
4116 
4117 	FreeMouseCursor();
4118 }
4119 
4120 
GetInterfaceGraphicForItem(const ItemModel * item)4121 SGPVObject const& GetInterfaceGraphicForItem(const ItemModel *item)
4122 {
4123 	// CHECK SUBCLASS
4124 	switch (item->getGraphicType())
4125 	{
4126 		case 0:
4127 			return *guiGUNSM;
4128 		case 1:
4129 			return *guiP1ITEMS;
4130 		case 2:
4131 			return *guiP2ITEMS;
4132 		default:
4133 			return *guiP3ITEMS;
4134 	}
4135 }
4136 
4137 
GetTileGraphicForItem(const ItemModel * item)4138 UINT16 GetTileGraphicForItem(const ItemModel * item)
4139 {
4140 	UINT32 Type;
4141 	switch (item->getGraphicType())
4142 	{
4143 		case 0:
4144 			Type = GUNS;
4145 			break;
4146 		case 1:
4147 			Type = P1ITEMS;
4148 			break;
4149 		case 2:
4150 			Type = P2ITEMS;
4151 			break;
4152 		default:
4153 			Type = P3ITEMS;
4154 			break;
4155 	}
4156 	return GetTileIndexFromTypeSubIndex(Type, item->getGraphicNum() + 1);
4157 }
4158 
4159 
LoadTileGraphicForItem(const ItemModel * item)4160 SGPVObject* LoadTileGraphicForItem(const ItemModel * item)
4161 {
4162 	const char* Prefix;
4163 	switch (item->getGraphicType())
4164 	{
4165 		case 0:
4166 			Prefix = "gun";
4167 			break;
4168 		case 1:
4169 			Prefix = "p1item";
4170 			break;
4171 		case 2:
4172 			Prefix = "p2item";
4173 			break;
4174 		default:
4175 			Prefix = "p3item";
4176 			break;
4177 	}
4178 
4179 	//Load item
4180 	SGPFILENAME ImageFile;
4181 	sprintf(ImageFile, BIGITEMSDIR "/%s%02d.sti", Prefix, item->getGraphicNum());
4182 	return AddVideoObjectFromFile(ImageFile);
4183 }
4184 
4185 
ItemDescCallback(MOUSE_REGION * pRegion,INT32 iReason)4186 static void ItemDescCallback(MOUSE_REGION* pRegion, INT32 iReason)
4187 {
4188 	static BOOLEAN fRightDown = FALSE, fLeftDown = FALSE;
4189 
4190 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
4191 	{
4192 		fLeftDown = TRUE;
4193 	}
4194 	else if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4195 	{
4196 		if ( fLeftDown )
4197 		{
4198 			fLeftDown = FALSE;
4199 
4200 			//Only exit the screen if we are NOT in the money interface.  Only the DONE button should exit the money interface.
4201 			if( gpItemDescObject->usItem != MONEY )
4202 			{
4203 				DeleteItemDescriptionBox( );
4204 			}
4205 		}
4206 	}
4207 	else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
4208 	{
4209 		fRightDown = TRUE;
4210 	}
4211 	else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4212 	{
4213 		if ( fRightDown )
4214 		{
4215 			fRightDown = FALSE;
4216 
4217 			//Only exit the screen if we are NOT in the money interface.  Only the DONE button should exit the money interface.
4218 			//if( gpItemDescObject->usItem != MONEY )
4219 			{
4220 				DeleteItemDescriptionBox( );
4221 			}
4222 		}
4223 	}
4224 }
4225 
4226 
4227 static void RemoveMoney(void);
4228 
4229 
ItemDescDoneButtonCallback(GUI_BUTTON * btn,INT32 reason)4230 static void ItemDescDoneButtonCallback(GUI_BUTTON *btn, INT32 reason)
4231 {
4232 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4233 	{
4234 		if (gpItemDescObject->usItem == MONEY) RemoveMoney();
4235 		DeleteItemDescriptionBox();
4236 	}
4237 
4238 	if (reason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4239 	{
4240 		DeleteItemDescriptionBox();
4241 	}
4242 }
4243 
4244 
ItemPopupRegionCallback(MOUSE_REGION * pRegion,INT32 iReason)4245 static void ItemPopupRegionCallback(MOUSE_REGION* pRegion, INT32 iReason)
4246 {
4247 	UINT32 uiItemPos;
4248 
4249 	uiItemPos = MSYS_GetRegionUserData( pRegion, 0 );
4250 
4251 	// TO ALLOW ME TO DELETE REGIONS IN CALLBACKS!
4252 	if ( gfItemPopupRegionCallbackEndFix )
4253 	{
4254 		return;
4255 	}
4256 
4257 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
4258 	{
4259 
4260 		//If one in our hand, place it
4261 		if ( gpItemPointer != NULL )
4262 		{
4263 			if ( !PlaceObjectAtObjectIndex( gpItemPointer, gpItemPopupObject, (UINT8)uiItemPos ) )
4264 			{
4265 				if (fInMapMode)
4266 				{
4267 					MAPEndItemPointer( );
4268 				}
4269 				else
4270 				{
4271 					gpItemPointer = NULL;
4272 					gSMPanelRegion.ChangeCursor(CURSOR_NORMAL);
4273 					SetCurrentCursorFromDatabase( CURSOR_NORMAL );
4274 
4275 					if (guiCurrentScreen == SHOPKEEPER_SCREEN)
4276 					{
4277 						gMoveingItem = INVENTORY_IN_SLOT{};
4278 						SetSkiCursor( CURSOR_NORMAL );
4279 					}
4280 				}
4281 
4282 				// re-evaluate repairs
4283 				gfReEvaluateEveryonesNothingToDo = TRUE;
4284 			}
4285 
4286 			//Dirty interface
4287 			//fInterfacePanelDirty = DIRTYLEVEL2;
4288 			//RenderItemStackPopup( FALSE );
4289 		}
4290 		else
4291 		{
4292 			if ( uiItemPos < gpItemPopupObject->ubNumberOfObjects )
4293 			{
4294 				// Here, grab an item and put in cursor to swap
4295 				//RemoveObjFrom( OBJECTTYPE * pObj, UINT8 ubRemoveIndex )
4296 				GetObjFrom( gpItemPopupObject, (UINT8)uiItemPos, &gItemPointer );
4297 
4298 				if (fInMapMode)
4299 				{
4300 					// pick it up
4301 					InternalMAPBeginItemPointer( gpItemPopupSoldier );
4302 				}
4303 				else
4304 				{
4305 					SetItemPointer(&gItemPointer, gpItemPopupSoldier);
4306 				}
4307 
4308 				//if we are in the shop keeper interface
4309 				if (guiCurrentScreen == SHOPKEEPER_SCREEN)
4310 				{
4311 					// pick up stacked item into cursor and try to sell it ( unless CTRL is held down )
4312 					BeginSkiItemPointer(PLAYERS_INVENTORY, -1, !_KeyDown(CTRL));
4313 
4314 					// if we've just removed the last one there
4315 					if ( gpItemPopupObject->ubNumberOfObjects == 0 )
4316 					{
4317 						// we must immediately get out of item stack popup, because the item has been deleted
4318 						// (memset to 0), and errors like a right bringing up an item description for item 0
4319 						// could happen then.  ARM.
4320 						DeleteItemStackPopup( );
4321 					}
4322 				}
4323 
4324 				// re-evaluate repairs
4325 				gfReEvaluateEveryonesNothingToDo = TRUE;
4326 
4327 				//Dirty interface
4328 				//RenderItemStackPopup( FALSE );
4329 				//fInterfacePanelDirty = DIRTYLEVEL2;
4330 			}
4331 		}
4332 
4333 		UpdateItemHatches();
4334 	}
4335 	else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4336 	{
4337 		// Get Description....
4338 		// Some global stuff here - for esc, etc
4339 		//Remove
4340 		gfItemPopupRegionCallbackEndFix = TRUE;
4341 
4342 
4343 		DeleteItemStackPopup( );
4344 
4345 		if ( !InItemDescriptionBox( ) )
4346 		{
4347 			// RESTORE BACKGROUND
4348 			RestoreExternBackgroundRect( gsItemPopupInvX, gsItemPopupInvY, gsItemPopupInvWidth, gsItemPopupInvHeight );
4349 			if ( guiCurrentItemDescriptionScreen == MAP_SCREEN )
4350 			{
4351 				MAPInternalInitItemDescriptionBox( gpItemPopupObject, (UINT8)uiItemPos, gpItemPopupSoldier );
4352 			}
4353 			else
4354 			{
4355 				InternalInitItemDescriptionBox( gpItemPopupObject, (INT16) ITEMDESC_START_X, (INT16) ITEMDESC_START_Y, (UINT8)uiItemPos, gpItemPopupSoldier );
4356 			}
4357 		}
4358 
4359 
4360 	}
4361 }
4362 
4363 
ItemPopupFullRegionCallback(MOUSE_REGION * pRegion,INT32 iReason)4364 static void ItemPopupFullRegionCallback(MOUSE_REGION* pRegion, INT32 iReason)
4365 {
4366 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4367 	{
4368 		if ( InItemStackPopup( ) )
4369 		{
4370 			// End stack popup and retain pointer
4371 			EndItemStackPopupWithItemInHand( );
4372 		}
4373 		else if( InKeyRingPopup() )
4374 		{
4375 			// end pop up with key in hand
4376 			DeleteKeyRingPopup( );
4377 			fTeamPanelDirty = TRUE;
4378 
4379 		}
4380 	}
4381 	else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4382 	{
4383 		if ( InItemStackPopup( ) )
4384 		{
4385 			DeleteItemStackPopup( );
4386 			fTeamPanelDirty = TRUE;
4387 		}
4388 		else
4389 		{
4390 			DeleteKeyRingPopup( );
4391 			fTeamPanelDirty = TRUE;
4392 		}
4393 	}
4394 }
4395 
4396 #define NUM_PICKUP_SLOTS				6
4397 
4398 struct ITEM_PICKUP_MENU_STRUCT
4399 {
4400 	ITEM_POOL *pItemPool;
4401 	INT16 sX;
4402 	INT16 sY;
4403 	INT16 sWidth;
4404 	INT16 sHeight;
4405 	INT8 bScrollPage;
4406 	INT32 ubScrollAnchor;
4407 	INT32 ubTotalItems;
4408 	INT32 bCurSelect;
4409 	UINT8 bNumSlotsPerPage;
4410 	SGPVObject* uiPanelVo;
4411 	BUTTON_PICS* iUpButtonImages;
4412 	BUTTON_PICS* iDownButtonImages;
4413 	BUTTON_PICS* iAllButtonImages;
4414 	BUTTON_PICS* iCancelButtonImages;
4415 	BUTTON_PICS* iOKButtonImages;
4416 	GUIButtonRef iUpButton;
4417 	GUIButtonRef iDownButton;
4418 	GUIButtonRef iAllButton;
4419 	GUIButtonRef iOKButton;
4420 	GUIButtonRef iCancelButton;
4421 	BOOLEAN fDirtyLevel;
4422 	BOOLEAN fHandled;
4423 	INT16 sGridNo;
4424 	INT8 bZLevel;
4425 	INT16 sButtomPanelStartY;
4426 	SOLDIERTYPE *pSoldier;
4427 	INT32 items[NUM_PICKUP_SLOTS];
4428 	MOUSE_REGION Regions[ NUM_PICKUP_SLOTS ];
4429 	MOUSE_REGION BackRegions;
4430 	MOUSE_REGION BackRegion;
4431 	BOOLEAN *pfSelectedArray;
4432 	OBJECTTYPE CompAmmoObject;
4433 	BOOLEAN fAllSelected;
4434 };
4435 
4436 #define ITEMPICK_UP_X					55
4437 #define ITEMPICK_UP_Y					5
4438 #define ITEMPICK_DOWN_X				111
4439 #define ITEMPICK_DOWN_Y				5
4440 #define ITEMPICK_ALL_X					79
4441 #define ITEMPICK_ALL_Y					6
4442 #define ITEMPICK_OK_X					16
4443 #define ITEMPICK_OK_Y					6
4444 #define ITEMPICK_CANCEL_X				141
4445 #define ITEMPICK_CANCEL_Y				6
4446 
4447 #define ITEMPICK_START_X_OFFSET			10
4448 
4449 #define ITEMPICK_GRAPHIC_X				10
4450 #define ITEMPICK_GRAPHIC_Y				12
4451 #define ITEMPICK_GRAPHIC_YSPACE			26
4452 
4453 #define ITEMPICK_TEXT_X				56
4454 #define ITEMPICK_TEXT_Y				22
4455 #define ITEMPICK_TEXT_YSPACE				26
4456 #define ITEMPICK_TEXT_WIDTH				109
4457 
4458 
4459 static ITEM_PICKUP_MENU_STRUCT gItemPickupMenu;
4460 BOOLEAN gfInItemPickupMenu = FALSE;
4461 
4462 
4463 // STUFF FOR POPUP ITEM INFO BOX
SetItemPickupMenuDirty(BOOLEAN fDirtyLevel)4464 void SetItemPickupMenuDirty( BOOLEAN fDirtyLevel )
4465 {
4466 	gItemPickupMenu.fDirtyLevel = fDirtyLevel;
4467 }
4468 
4469 
4470 static void CalculateItemPickupMenuDimensions(void);
4471 static void ItemPickMenuMouseClickCallback(MOUSE_REGION* pRegion, INT32 iReason);
4472 static void ItemPickMenuMouseMoveCallback(MOUSE_REGION* pRegion, INT32 iReason);
4473 static void ItemPickupAll(GUI_BUTTON* btn, INT32 reason);
4474 static void ItemPickupCancel(GUI_BUTTON* btn, INT32 reason);
4475 static void ItemPickupOK(GUI_BUTTON* btn, INT32 reason);
4476 static void ItemPickupScrollDown(GUI_BUTTON* btn, INT32 reason);
4477 static void ItemPickupScrollUp(GUI_BUTTON* btn, INT32 reason);
4478 static void SetupPickupPage(INT8 bPage);
4479 
4480 
InitializeItemPickupMenu(SOLDIERTYPE * const pSoldier,INT16 const sGridNo,ITEM_POOL * const pItemPool,INT8 const bZLevel)4481 void InitializeItemPickupMenu(SOLDIERTYPE* const pSoldier, INT16 const sGridNo, ITEM_POOL* const pItemPool, INT8 const bZLevel)
4482 {
4483 	EraseInterfaceMenus(TRUE);
4484 	LocateSoldier(pSoldier, FALSE);
4485 
4486 	ITEM_PICKUP_MENU_STRUCT& menu = gItemPickupMenu;
4487 	menu = ITEM_PICKUP_MENU_STRUCT{};
4488 	menu.pItemPool = pItemPool;
4489 
4490 	InterruptTime();
4491 	PauseGame();
4492 	LockPauseState(LOCK_PAUSE_ITEM_PICKUP);
4493 	PauseTime(TRUE);
4494 
4495 	// Alrighty, cancel lock UI if we havn't done so already
4496 	UnSetUIBusy(pSoldier);
4497 
4498 	// Change to INV panel if not there already...
4499 	SetNewPanel(pSoldier);
4500 
4501 	//Determine total #
4502 	INT32 cnt = 0;
4503 	for (ITEM_POOL* i = pItemPool; i; i = i->pNext)
4504 	{
4505 		if (!ItemPoolOKForDisplay(i, bZLevel)) continue;
4506 		++cnt;
4507 	}
4508 	menu.ubTotalItems = (UINT8)cnt;
4509 
4510 	// Determine # of slots per page
4511 	menu.bNumSlotsPerPage = menu.ubTotalItems < NUM_PICKUP_SLOTS ?
4512 		menu.ubTotalItems : NUM_PICKUP_SLOTS;
4513 
4514 	menu.uiPanelVo = AddVideoObjectFromFile(INTERFACEDIR "/itembox.sti");
4515 
4516 	menu.pfSelectedArray = new BOOLEAN[menu.ubTotalItems]{};
4517 
4518 	CalculateItemPickupMenuDimensions();
4519 
4520 	// First get mouse xy screen location
4521 	INT16 sX = gusMouseXPos;
4522 	INT16 sY = gusMouseYPos;
4523 
4524 	// CHECK FOR LEFT/RIGHT
4525 	if (sX + menu.sWidth > SCREEN_WIDTH)
4526 	{
4527 		sX = SCREEN_WIDTH - menu.sWidth - ITEMPICK_START_X_OFFSET;
4528 	}
4529 	else
4530 	{
4531 		sX = sX + ITEMPICK_START_X_OFFSET;
4532 	}
4533 
4534 	// Now check for top
4535 	// Center in the y
4536 	INT16 const sCenterYVal = menu.sHeight / 2;
4537 
4538 	sY -= sCenterYVal;
4539 	if (sY < gsVIEWPORT_WINDOW_START_Y)
4540 	{
4541 		sY = gsVIEWPORT_WINDOW_START_Y;
4542 	}
4543 
4544 	// Check for bottom
4545 	if (sY + menu.sHeight > gsVIEWPORT_WINDOW_END_Y)
4546 	{
4547 		sY = gsVIEWPORT_WINDOW_END_Y - menu.sHeight;
4548 	}
4549 
4550 	menu.sX           = sX;
4551 	menu.sY           = sY;
4552 	menu.bCurSelect   = 0;
4553 	menu.pSoldier     = pSoldier;
4554 	menu.fHandled     = FALSE;
4555 	menu.sGridNo      = sGridNo;
4556 	menu.bZLevel      = bZLevel;
4557 	menu.fAllSelected = FALSE;
4558 
4559 	//Load images for buttons
4560 	BUTTON_PICS* const pics  = LoadButtonImage(INTERFACEDIR "/itembox.sti", 5, 10);
4561 	menu.iUpButtonImages     = pics;
4562 	menu.iDownButtonImages   = UseLoadedButtonImage(pics, 7, 12);
4563 	menu.iAllButtonImages    = UseLoadedButtonImage(pics, 6, 11);
4564 	menu.iCancelButtonImages = UseLoadedButtonImage(pics, 8, 13);
4565 	menu.iOKButtonImages     = UseLoadedButtonImage(pics, 4,  9);
4566 
4567 	// Build a mouse region here that is over any others.....
4568 	MSYS_DefineRegion(&menu.BackRegion, 532, 367, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST, CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
4569 
4570 	// Build a mouse region here that is over any others.....
4571 	MSYS_DefineRegion(&menu.BackRegions, sX, sY, menu.sX + menu.sWidth, sY + menu.sHeight, MSYS_PRIORITY_HIGHEST, CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
4572 
4573 	INT16 const by = sY + menu.sButtomPanelStartY;
4574 
4575 	// Create buttons
4576 	if (menu.bNumSlotsPerPage == NUM_PICKUP_SLOTS && menu.ubTotalItems > NUM_PICKUP_SLOTS)
4577 	{
4578 		menu.iUpButton = QuickCreateButton(menu.iUpButtonImages, sX + ITEMPICK_UP_X, by + ITEMPICK_UP_Y, MSYS_PRIORITY_HIGHEST, ItemPickupScrollUp);
4579 		menu.iUpButton->SetFastHelpText(ItemPickupHelpPopup[1]);
4580 
4581 		menu.iDownButton = QuickCreateButton(menu.iDownButtonImages, sX + ITEMPICK_DOWN_X, by + ITEMPICK_DOWN_Y, MSYS_PRIORITY_HIGHEST, ItemPickupScrollDown);
4582 		menu.iDownButton->SetFastHelpText(ItemPickupHelpPopup[3]);
4583 	}
4584 
4585 	menu.iOKButton = QuickCreateButton(menu.iOKButtonImages, sX + ITEMPICK_OK_X, by + ITEMPICK_OK_Y, MSYS_PRIORITY_HIGHEST, ItemPickupOK);
4586 	menu.iOKButton->SetFastHelpText(ItemPickupHelpPopup[0]);
4587 
4588 	menu.iAllButton = QuickCreateButton(menu.iAllButtonImages, sX + ITEMPICK_ALL_X, by + ITEMPICK_ALL_Y, MSYS_PRIORITY_HIGHEST, ItemPickupAll);
4589 	menu.iAllButton->SetFastHelpText(ItemPickupHelpPopup[2]);
4590 
4591 	menu.iCancelButton = QuickCreateButton(menu.iCancelButtonImages, sX + ITEMPICK_CANCEL_X, by + ITEMPICK_CANCEL_Y, MSYS_PRIORITY_HIGHEST, ItemPickupCancel);
4592 	menu.iCancelButton->SetFastHelpText(ItemPickupHelpPopup[4]);
4593 
4594 	DisableButton(menu.iOKButton);
4595 
4596 	// Create regions
4597 	INT16 const sCenX = sX;
4598 	INT16       sCenY = sY + ITEMPICK_GRAPHIC_Y;
4599 	for (INT32 i = 0; i < menu.bNumSlotsPerPage; ++i)
4600 	{
4601 		MOUSE_REGION* const r = &menu.Regions[i];
4602 		MSYS_DefineRegion(r, sCenX, sCenY + 1, sCenX + menu.sWidth, sCenY + ITEMPICK_GRAPHIC_YSPACE, MSYS_PRIORITY_HIGHEST, CURSOR_NORMAL, ItemPickMenuMouseMoveCallback, ItemPickMenuMouseClickCallback);
4603 		MSYS_SetRegionUserData(r, 0, i);
4604 
4605 		sCenY += ITEMPICK_GRAPHIC_YSPACE;
4606 	}
4607 
4608 	SetupPickupPage(0);
4609 
4610 	gfInItemPickupMenu = TRUE;
4611 	gfIgnoreScrolling  = TRUE;
4612 
4613 	HandleAnyMercInSquadHasCompatibleStuff(NULL);
4614 	gSelectSMPanelToMerc = pSoldier;
4615 	ReEvaluateDisabledINVPanelButtons();
4616 	DisableTacticalTeamPanelButtons(TRUE);
4617 }
4618 
4619 
SetupPickupPage(INT8 bPage)4620 static void SetupPickupPage(INT8 bPage)
4621 {
4622 	INT32 cnt, iStart, iEnd;
4623 	ITEM_POOL *pTempItemPool;
4624 	INT16 sValue;
4625 
4626 	// Reset page slots
4627 	FOR_EACH(INT32, i, gItemPickupMenu.items)
4628 	{
4629 		*i = -1;
4630 	}
4631 
4632 	// Get lower bound
4633 	iStart = bPage * NUM_PICKUP_SLOTS;
4634 	if ( iStart > gItemPickupMenu.ubTotalItems )
4635 	{
4636 		return;
4637 	}
4638 
4639 
4640 	iEnd   = iStart + NUM_PICKUP_SLOTS;
4641 	if ( iEnd >= gItemPickupMenu.ubTotalItems )
4642 	{
4643 		iEnd = gItemPickupMenu.ubTotalItems;
4644 	}
4645 
4646 	// Setup slots!
4647 	// These slots contain an inventory pool pointer for each slot...
4648 	pTempItemPool = gItemPickupMenu.pItemPool;
4649 
4650 	// ATE: Patch fix here for crash :(
4651 	// Clear help text!
4652 	for ( cnt = 0; cnt < NUM_PICKUP_SLOTS; cnt++ )
4653 	{
4654 		gItemPickupMenu.Regions[cnt].SetFastHelpText(ST::null);
4655 	}
4656 
4657 	for ( cnt = 0; cnt < iEnd; )
4658 	{
4659 		// Move to the closest one that can be displayed....
4660 		while( !ItemPoolOKForDisplay( pTempItemPool, gItemPickupMenu.bZLevel ) )
4661 		{
4662 			pTempItemPool = pTempItemPool->pNext;
4663 		}
4664 
4665 		if ( cnt >= iStart )
4666 		{
4667 			INT32 const item = pTempItemPool->iItemIndex;
4668 			gItemPickupMenu.items[cnt - iStart] = item;
4669 
4670 			OBJECTTYPE const& o = GetWorldItem(item).o;
4671 
4672 			sValue = o.bStatus[0];
4673 
4674 			// Adjust for ammo, other thingys..
4675 			ST::string pStr;
4676 			if (GCM->getItem(o.usItem)->isAmmo() || GCM->getItem(o.usItem)->isKey())
4677 			{
4678 				pStr = ST::null;
4679 			}
4680 			else
4681 			{
4682 				pStr = ST::format("{}%", sValue);
4683 			}
4684 
4685 			gItemPickupMenu.Regions[cnt - iStart].SetFastHelpText(pStr);
4686 		}
4687 
4688 		cnt++;
4689 
4690 		pTempItemPool = pTempItemPool->pNext;
4691 	}
4692 
4693 	gItemPickupMenu.bScrollPage = bPage;
4694 	gItemPickupMenu.ubScrollAnchor = (UINT8)iStart;
4695 
4696 	if ( gItemPickupMenu.bNumSlotsPerPage == NUM_PICKUP_SLOTS && gItemPickupMenu.ubTotalItems > NUM_PICKUP_SLOTS )
4697 	{
4698 		// Setup enabled/disabled buttons
4699 		EnableButton(gItemPickupMenu.iUpButton, bPage > 0);
4700 		// Setup enabled/disabled buttons
4701 		EnableButton(gItemPickupMenu.iDownButton, iEnd < gItemPickupMenu.ubTotalItems);
4702 	}
4703 	SetItemPickupMenuDirty( DIRTYLEVEL2 );
4704 
4705 }
4706 
4707 
CalculateItemPickupMenuDimensions(void)4708 static void CalculateItemPickupMenuDimensions(void)
4709 {
4710 	// Build background
4711 	INT16 sY = 0;
4712 
4713 	for (INT32 cnt = 0; cnt < gItemPickupMenu.bNumSlotsPerPage; cnt++)
4714 	{
4715 		// Add height of object
4716 		UINT16 usSubRegion = (cnt == 0 ? 0 : 1);
4717 		ETRLEObject const& ETRLEProps = gItemPickupMenu.uiPanelVo->SubregionProperties(usSubRegion);
4718 		sY += ETRLEProps.usHeight;
4719 	}
4720 	gItemPickupMenu.sButtomPanelStartY = sY;
4721 
4722 	// Do end
4723 	ETRLEObject const& ETRLEProps = gItemPickupMenu.uiPanelVo->SubregionProperties(2);
4724 	sY += ETRLEProps.usHeight;
4725 
4726 	// Set height, width
4727 	gItemPickupMenu.sHeight = sY;
4728 	gItemPickupMenu.sWidth  = ETRLEProps.usWidth;
4729 }
4730 
4731 
RenderItemPickupMenu()4732 void RenderItemPickupMenu()
4733 {
4734 	ST::string pStr;
4735 
4736 	if (!gfInItemPickupMenu) return;
4737 
4738 	ITEM_PICKUP_MENU_STRUCT& menu = gItemPickupMenu;
4739 	if (menu.fDirtyLevel != DIRTYLEVEL2) return;
4740 
4741 	MarkButtonsDirty();
4742 
4743 	// Build background
4744 	INT16 sX = menu.sX;
4745 	INT16 sY = menu.sY;
4746 
4747 	for (INT32 cnt = 0; cnt < menu.bNumSlotsPerPage; ++cnt)
4748 	{
4749 		UINT16 const usSubRegion = (cnt == 0 ? 0 : 1);
4750 
4751 		BltVideoObject(FRAME_BUFFER, menu.uiPanelVo, usSubRegion, sX, sY);
4752 
4753 		// Add height of object
4754 		ETRLEObject const& ETRLEProps = menu.uiPanelVo->SubregionProperties(usSubRegion);
4755 		sY += ETRLEProps.usHeight;
4756 	}
4757 
4758 	// Do end
4759 	UINT16 const gfx =
4760 		menu.bNumSlotsPerPage == NUM_PICKUP_SLOTS &&
4761 		menu.ubTotalItems     >  NUM_PICKUP_SLOTS ?
4762 			2 : 3;
4763 	BltVideoObject(FRAME_BUFFER, menu.uiPanelVo, gfx, sX, sY);
4764 
4765 	// Render items....
4766 	sX = menu.sX + ITEMPICK_GRAPHIC_X;
4767 	sY = menu.sY + ITEMPICK_GRAPHIC_Y;
4768 
4769 	SetFont(ITEMDESC_FONT);
4770 	SetFontBackground(FONT_MCOLOR_BLACK);
4771 	SetFontShadow(ITEMDESC_FONTSHADOW2);
4772 
4773 	{
4774 		SGPVSurface::Lock l(FRAME_BUFFER);
4775 		UINT16* const pDestBuf         = l.Buffer<UINT16>();
4776 		UINT32  const uiDestPitchBYTES = l.Pitch();
4777 
4778 		UINT16 const outline_col = Get16BPPColor(FROMRGB(255, 255, 0));
4779 		for (INT32 cnt = 0; cnt < menu.bNumSlotsPerPage; ++cnt)
4780 		{
4781 			INT32 const world_item = menu.items[cnt];
4782 			if (world_item == -1) continue;
4783 
4784 			// Get item to render
4785 			OBJECTTYPE const& o    = GetWorldItem(world_item).o;
4786 			const ItemModel * item = GCM->getItem(o.usItem);
4787 
4788 			UINT16              const usItemTileIndex = GetTileGraphicForItem(item);
4789 			TILE_ELEMENT const* const te              = &gTileDatabase[usItemTileIndex];
4790 
4791 			// ATE: Adjust to basic shade.....
4792 			te->hTileSurface->CurrentShade(4);
4793 
4794 			UINT16 const outline = menu.pfSelectedArray[cnt + menu.ubScrollAnchor] ? outline_col : SGP_TRANSPARENT;
4795 			Blt8BPPDataTo16BPPBufferOutline(pDestBuf, uiDestPitchBYTES, te->hTileSurface, sX, sY, te->usRegionIndex, outline);
4796 
4797 			if (o.ubNumberOfObjects > 1)
4798 			{
4799 				SetFontAttributes(ITEM_FONT, FONT_GRAY4);
4800 
4801 				pStr = ST::format("{}", o.ubNumberOfObjects);
4802 
4803 				INT16 sFontX;
4804 				INT16 sFontY;
4805 				FindFontRightCoordinates(sX - 4, sY + 14, 42, 1, pStr, ITEM_FONT, &sFontX, &sFontY);
4806 				MPrintBuffer(pDestBuf, uiDestPitchBYTES, sFontX, sFontY, pStr);
4807 				SetFont(ITEMDESC_FONT);
4808 			}
4809 
4810 			if (ItemHasAttachments(o))
4811 			{
4812 				// Render attachment symbols
4813 				SetFontForeground(GetAttachmentHintColor(&o));
4814 				SetFontShadow(DEFAULT_SHADOW);
4815 				ST::string AttachMarker = "*";
4816 				UINT16         const uiStringLength = StringPixLength(AttachMarker, ITEM_FONT);
4817 				INT16          const sNewX          = sX + 43 - uiStringLength - 4;
4818 				INT16          const sNewY          = sY + 2;
4819 				MPrintBuffer(pDestBuf, uiDestPitchBYTES, sNewX, sNewY, AttachMarker);
4820 			}
4821 
4822 			if (menu.bCurSelect == cnt + menu.ubScrollAnchor)
4823 			{
4824 				SetFontForeground(FONT_WHITE);
4825 				SetFontShadow(DEFAULT_SHADOW);
4826 			}
4827 			else
4828 			{
4829 				SetFontForeground(FONT_BLACK);
4830 				SetFontShadow(ITEMDESC_FONTSHADOW2);
4831 			}
4832 
4833 			// Render name
4834 			if (item->getItemClass() == IC_MONEY)
4835 			{
4836 				ST::string pStr2 = SPrintMoney(o.uiMoneyAmount);
4837 				pStr = ST::format("{} ({})", ItemNames[o.usItem], pStr2);
4838 			}
4839 			else
4840 			{
4841 				pStr = ShortItemNames[o.usItem];
4842 			}
4843 			INT16 sFontX;
4844 			INT16 sFontY;
4845 			INT16 const x = ITEMPICK_TEXT_X + menu.sX;
4846 			INT16 const y = ITEMPICK_TEXT_Y + menu.sY + ITEMPICK_TEXT_YSPACE * cnt;
4847 			FindFontCenterCoordinates(x, y, ITEMPICK_TEXT_WIDTH, 1, pStr, ITEMDESC_FONT, &sFontX, &sFontY);
4848 			MPrintBuffer(pDestBuf, uiDestPitchBYTES, sFontX, sFontY, pStr);
4849 
4850 			sY += ITEMPICK_GRAPHIC_YSPACE;
4851 		}
4852 	}
4853 
4854 	SetFontShadow(DEFAULT_SHADOW);
4855 	InvalidateRegion(menu.sX, menu.sY, menu.sX + menu.sWidth, menu.sY + menu.sHeight);
4856 	menu.fDirtyLevel = 0;
4857 }
4858 
4859 
RemoveItemPickupMenu()4860 void RemoveItemPickupMenu( )
4861 {
4862 	INT32 cnt;
4863 
4864 	if ( gfInItemPickupMenu )
4865 	{
4866 		gfSMDisableForItems = FALSE;
4867 
4868 		HandleAnyMercInSquadHasCompatibleStuff(NULL);
4869 
4870 		UnLockPauseState();
4871 		UnPauseGame();
4872 		// UnPause timers as well....
4873 		PauseTime( FALSE );
4874 
4875 		// Unfreese guy!
4876 		gItemPickupMenu.pSoldier->fPauseAllAnimation = FALSE;
4877 
4878 		DeleteVideoObject(gItemPickupMenu.uiPanelVo);
4879 
4880 		// Remove buttons
4881 		if ( gItemPickupMenu.bNumSlotsPerPage == NUM_PICKUP_SLOTS && gItemPickupMenu.ubTotalItems > NUM_PICKUP_SLOTS )
4882 		{
4883 			RemoveButton( gItemPickupMenu.iUpButton );
4884 			RemoveButton( gItemPickupMenu.iDownButton );
4885 		}
4886 		RemoveButton( gItemPickupMenu.iAllButton );
4887 		RemoveButton( gItemPickupMenu.iOKButton );
4888 		RemoveButton( gItemPickupMenu.iCancelButton );
4889 
4890 		// Remove button images
4891 		UnloadButtonImage( gItemPickupMenu.iUpButtonImages );
4892 		UnloadButtonImage( gItemPickupMenu.iDownButtonImages );
4893 		UnloadButtonImage( gItemPickupMenu.iAllButtonImages );
4894 		UnloadButtonImage( gItemPickupMenu.iCancelButtonImages );
4895 		UnloadButtonImage( gItemPickupMenu.iOKButtonImages );
4896 
4897 		MSYS_RemoveRegion( &(gItemPickupMenu.BackRegions ) );
4898 		MSYS_RemoveRegion( &(gItemPickupMenu.BackRegion ) );
4899 
4900 		// Remove regions
4901 		for ( cnt = 0; cnt < gItemPickupMenu.bNumSlotsPerPage; cnt++ )
4902 		{
4903 			MSYS_RemoveRegion( &(gItemPickupMenu.Regions[cnt]));
4904 		}
4905 
4906 		// Free selection list...
4907 		delete[] gItemPickupMenu.pfSelectedArray;
4908 		gItemPickupMenu.pfSelectedArray = NULL;
4909 
4910 
4911 		// Set cursor back to normal mode...
4912 		guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
4913 
4914 		// Rerender world
4915 		SetRenderFlags( RENDER_FLAG_FULL );
4916 
4917 		gfInItemPickupMenu = FALSE;
4918 
4919 		//gfSMDisableForItems = FALSE;
4920 		EnableSMPanelButtons( TRUE , TRUE );
4921 		gfSMDisableForItems = FALSE;
4922 
4923 		fInterfacePanelDirty = DIRTYLEVEL2;
4924 
4925 		// Turn off Ignore scrolling
4926 		gfIgnoreScrolling = FALSE;
4927 		DisableTacticalTeamPanelButtons( FALSE );
4928 		gSelectSMPanelToMerc = gpSMCurrentMerc;
4929 	}
4930 }
4931 
4932 
ItemPickupScrollUp(GUI_BUTTON * btn,INT32 reason)4933 static void ItemPickupScrollUp(GUI_BUTTON* btn, INT32 reason)
4934 {
4935 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4936 	{
4937 		SetupPickupPage( (UINT8)( gItemPickupMenu.bScrollPage - 1 ) );
4938 	}
4939 }
4940 
4941 
ItemPickupScrollDown(GUI_BUTTON * btn,INT32 reason)4942 static void ItemPickupScrollDown(GUI_BUTTON* btn, INT32 reason)
4943 {
4944 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4945 	{
4946 		SetupPickupPage( (UINT8)( gItemPickupMenu.bScrollPage + 1 ) );
4947 	}
4948 }
4949 
4950 
ItemPickupAll(GUI_BUTTON * btn,INT32 reason)4951 static void ItemPickupAll(GUI_BUTTON* btn, INT32 reason)
4952 {
4953 	INT32 cnt;
4954 
4955 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4956 	{
4957 		gItemPickupMenu.fAllSelected = !gItemPickupMenu.fAllSelected;
4958 
4959 
4960 		// OK, pickup item....
4961 		//gItemPickupMenu.fHandled = TRUE;
4962 		// Tell our soldier to pickup this item!
4963 		//SoldierGetItemFromWorld( gItemPickupMenu.pSoldier, ITEM_PICKUP_ACTION_ALL, gItemPickupMenu.sGridNo, gItemPickupMenu.bZLevel, NULL );
4964 		for ( cnt = 0; cnt < gItemPickupMenu.ubTotalItems; cnt++ )
4965 		{
4966 			gItemPickupMenu.pfSelectedArray[ cnt ] = gItemPickupMenu.fAllSelected;
4967 		}
4968 
4969 		EnableButton(gItemPickupMenu.iOKButton, gItemPickupMenu.fAllSelected);
4970 	}
4971 }
4972 
4973 
ItemPickupOK(GUI_BUTTON * btn,INT32 reason)4974 static void ItemPickupOK(GUI_BUTTON* btn, INT32 reason)
4975 {
4976 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4977 	{
4978 		// OK, pickup item....
4979 		gItemPickupMenu.fHandled = TRUE;
4980 
4981 		// Tell our soldier to pickup this item!
4982 		SoldierGetItemFromWorld( gItemPickupMenu.pSoldier, ITEM_PICKUP_SELECTION, gItemPickupMenu.sGridNo, gItemPickupMenu.bZLevel, gItemPickupMenu.pfSelectedArray );
4983 	}
4984 }
4985 
4986 
ItemPickupCancel(GUI_BUTTON * btn,INT32 reason)4987 static void ItemPickupCancel(GUI_BUTTON* btn, INT32 reason)
4988 {
4989 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4990 	{
4991 		// OK, pickup item....
4992 		gItemPickupMenu.fHandled = TRUE;
4993 	}
4994 }
4995 
4996 
ItemPickMenuMouseMoveCallback(MOUSE_REGION * const pRegion,INT32 const iReason)4997 static void ItemPickMenuMouseMoveCallback(MOUSE_REGION* const pRegion, INT32 const iReason)
4998 {
4999 	static BOOLEAN bChecked = FALSE;
5000 
5001 	if (iReason & MSYS_CALLBACK_REASON_MOVE)
5002 	{
5003 		UINT32 const uiItemPos = MSYS_GetRegionUserData(pRegion, 0);
5004 		INT32  const bPos      = uiItemPos + gItemPickupMenu.ubScrollAnchor;
5005 		if (bPos >= gItemPickupMenu.ubTotalItems) return;
5006 
5007 		gItemPickupMenu.bCurSelect = bPos;
5008 
5009 		if (bChecked) return;
5010 
5011 		// Show compatible ammo
5012 		INT32      const  item = gItemPickupMenu.items[uiItemPos];
5013 		OBJECTTYPE const& o    = GetWorldItem(item).o;
5014 
5015 		gItemPickupMenu.CompAmmoObject = o;
5016 
5017 		HandleAnyMercInSquadHasCompatibleStuff(0); // Turn off first
5018 		InternalHandleCompatibleAmmoUI(gpSMCurrentMerc, &gItemPickupMenu.CompAmmoObject, TRUE);
5019 		HandleAnyMercInSquadHasCompatibleStuff(&o);
5020 
5021 		SetItemPickupMenuDirty(DIRTYLEVEL2);
5022 
5023 		bChecked = TRUE;
5024 	}
5025 	else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE)
5026 	{
5027 		gItemPickupMenu.bCurSelect = 255;
5028 
5029 		InternalHandleCompatibleAmmoUI(gpSMCurrentMerc, &gItemPickupMenu.CompAmmoObject, FALSE);
5030 		HandleAnyMercInSquadHasCompatibleStuff(NULL);
5031 
5032 		SetItemPickupMenuDirty(DIRTYLEVEL2);
5033 
5034 		bChecked = FALSE;
5035 	}
5036 }
5037 
5038 
ItemPickMenuMouseClickCallback(MOUSE_REGION * const pRegion,INT32 const iReason)5039 static void ItemPickMenuMouseClickCallback(MOUSE_REGION* const pRegion, INT32 const iReason)
5040 {
5041 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
5042 	{
5043 		INT32 const item_pos = MSYS_GetRegionUserData(pRegion, 0) + gItemPickupMenu.ubScrollAnchor;
5044 		if (item_pos >= gItemPickupMenu.ubTotalItems) return;
5045 
5046 		BOOLEAN& selected = gItemPickupMenu.pfSelectedArray[item_pos];
5047 		selected = !selected;
5048 
5049 		// Loop through all and set /unset OK
5050 		bool enable = false;
5051 		for (UINT8 i = 0; i < gItemPickupMenu.ubTotalItems; ++i)
5052 		{
5053 			if (!gItemPickupMenu.pfSelectedArray[i]) continue;
5054 			enable = true;
5055 			break;
5056 		}
5057 		EnableButton(gItemPickupMenu.iOKButton, enable);
5058 	}
5059 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_UP)
5060 	{
5061 		INT8 const page = gItemPickupMenu.bScrollPage;
5062 		if (page > 0) SetupPickupPage(page - 1);
5063 	}
5064 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
5065 	{
5066 		INT8 const page = gItemPickupMenu.bScrollPage;
5067 		if ((page + 1) * NUM_PICKUP_SLOTS < gItemPickupMenu.ubTotalItems)
5068 		{
5069 			SetupPickupPage(page + 1);
5070 		}
5071 	}
5072 }
5073 
5074 
HandleItemPickupMenu()5075 BOOLEAN HandleItemPickupMenu( )
5076 {
5077 
5078 	if ( !gfInItemPickupMenu )
5079 	{
5080 		return( FALSE );
5081 	}
5082 
5083 	if ( gItemPickupMenu.fHandled )
5084 	{
5085 		RemoveItemPickupMenu( );
5086 	}
5087 
5088 	return( gItemPickupMenu.fHandled );
5089 }
5090 
5091 
BtnMoneyButtonCallback(GUI_BUTTON * const btn,INT32 const reason)5092 static void BtnMoneyButtonCallback(GUI_BUTTON* const btn, INT32 const reason)
5093 {
5094 	if (reason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
5095 	{
5096 		btn->uiFlags |= BUTTON_CLICKED_ON;
5097 	}
5098 
5099 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
5100 	{
5101 		UINT32      amount   = 0;
5102 		UINT8 const ubButton = btn->GetUserData();
5103 		switch (ubButton)
5104 		{
5105 			case M_1000: amount = 1000; break;
5106 			case M_100:  amount =  100; break;
5107 			case M_10:   amount =   10; break;
5108 
5109 			case M_DONE:
5110 				RemoveMoney();
5111 				DeleteItemDescriptionBox();
5112 				break;
5113 		}
5114 
5115 		if (amount != 0 && gRemoveMoney.uiMoneyRemaining >= amount)
5116 		{
5117 			if (gfAddingMoneyToMercFromPlayersAccount && gRemoveMoney.uiMoneyRemoving + amount > MAX_MONEY_PER_SLOT)
5118 			{
5119 				ScreenID const exit_screen = guiCurrentScreen == SHOPKEEPER_SCREEN ?
5120 					SHOPKEEPER_SCREEN : GAME_SCREEN;
5121 				DoMessageBox(MSG_BOX_BASIC_STYLE, gzMoneyWithdrawMessageText[MONEY_TEXT_WITHDRAW_MORE_THEN_MAXIMUM], exit_screen, MSG_BOX_FLAG_OK, NULL, NULL);
5122 				return;
5123 			}
5124 
5125 			gRemoveMoney.uiMoneyRemaining -= amount;
5126 			gRemoveMoney.uiMoneyRemoving  += amount;
5127 
5128 			RenderItemDescriptionBox( );
5129 			for (INT8 i = 0; i < MAX_ATTACHMENTS; ++i)
5130 			{
5131 				MarkAButtonDirty(guiMoneyButtonBtn[i]);
5132 			}
5133 		}
5134 	}
5135 
5136 	if (reason & MSYS_CALLBACK_REASON_RBUTTON_UP)
5137 	{
5138 		btn->uiFlags &= ~BUTTON_CLICKED_ON;
5139 
5140 		UINT32      amount   = 0;
5141 		UINT8 const ubButton = btn->GetUserData();
5142 		switch (ubButton)
5143 		{
5144 			case M_1000: amount = 1000; break;
5145 			case M_100:  amount =  100; break;
5146 			case M_10:   amount =   10; break;
5147 		}
5148 
5149 		if (amount != 0 && gRemoveMoney.uiMoneyRemoving >= amount)
5150 		{
5151 			gRemoveMoney.uiMoneyRemaining += amount;
5152 			gRemoveMoney.uiMoneyRemoving  -= amount;
5153 		}
5154 
5155 		RenderItemDescriptionBox();
5156 		for (INT8 i = 0; i < MAX_ATTACHMENTS; ++i)
5157 		{
5158 			MarkAButtonDirty(guiMoneyButtonBtn[i]);
5159 		}
5160 	}
5161 }
5162 
5163 
RemoveMoney(void)5164 static void RemoveMoney(void)
5165 {
5166 	if( gRemoveMoney.uiMoneyRemoving != 0 )
5167 	{
5168 		//if we are in the shop keeper interface
5169 		if (guiCurrentScreen == SHOPKEEPER_SCREEN)
5170 		{
5171 			INVENTORY_IN_SLOT InvSlot;
5172 
5173 			InvSlot = INVENTORY_IN_SLOT{};
5174 
5175 			InvSlot.fActive = TRUE;
5176 			InvSlot.sItemIndex = MONEY;
5177 			InvSlot.bSlotIdInOtherLocation = -1;
5178 
5179 			//Remove the money from the money in the pocket
5180 			gpItemDescObject->uiMoneyAmount = gRemoveMoney.uiMoneyRemaining;
5181 
5182 				//Create an item to get the money that is being removed
5183 			CreateMoney(gRemoveMoney.uiMoneyRemoving, &InvSlot.ItemObject);
5184 
5185 			InvSlot.ubIdOfMercWhoOwnsTheItem = gpItemDescSoldier->ubProfile;
5186 
5187 			//if we are removing money from the players account
5188 			if( gfAddingMoneyToMercFromPlayersAccount )
5189 			{
5190 				gpItemDescObject->uiMoneyAmount = gRemoveMoney.uiMoneyRemoving;
5191 
5192 				//take the money from the player
5193 				AddTransactionToPlayersBook ( TRANSFER_FUNDS_TO_MERC, gpSMCurrentMerc->ubProfile, GetWorldTotalMin() , -(INT32)( gpItemDescObject->uiMoneyAmount ) );
5194 			}
5195 
5196 			gMoveingItem = InvSlot;
5197 
5198 			gItemPointer = InvSlot.ItemObject;
5199 			SetItemPointer(&gItemPointer, gpSMCurrentMerc);
5200 
5201 			// Set mouse
5202 			SetSkiCursor( EXTERN_CURSOR );
5203 
5204 			//Restrict the cursor to the proper area
5205 			RestrictSkiMouseCursor();
5206 		}
5207 		else
5208 		{
5209 			CreateMoney( gRemoveMoney.uiMoneyRemoving, &gItemPointer );
5210 			SetItemPointer(&gItemPointer, gpItemDescSoldier);
5211 
5212 			//Remove the money from the money in the pocket
5213 			//if we are removing money from the players account
5214 			if( gfAddingMoneyToMercFromPlayersAccount )
5215 			{
5216 				gpItemDescObject->uiMoneyAmount = gRemoveMoney.uiMoneyRemoving;
5217 
5218 				//take the money from the player
5219 				AddTransactionToPlayersBook ( TRANSFER_FUNDS_TO_MERC, gpSMCurrentMerc->ubProfile, GetWorldTotalMin() , -(INT32)( gpItemDescObject->uiMoneyAmount ) );
5220 			}
5221 			else
5222 				gpItemDescObject->uiMoneyAmount = gRemoveMoney.uiMoneyRemaining;
5223 
5224 
5225 
5226 			if( guiCurrentItemDescriptionScreen == MAP_SCREEN )
5227 			{
5228 				SetMapCursorItem();
5229 				fTeamPanelDirty=TRUE;
5230 			}
5231 
5232 		}
5233 	}
5234 
5235 	//if( gfAddingMoneyToMercFromPlayersAccount )
5236 	//	gfAddingMoneyToMercFromPlayersAccount = FALSE;
5237 }
5238 
5239 
GetHelpTextForItem(const OBJECTTYPE & obj)5240 ST::string GetHelpTextForItem(const OBJECTTYPE& obj)
5241 {
5242 	ST::string dst;
5243 	UINT16 const usItem = obj.usItem;
5244 	if (usItem == MONEY)
5245 	{
5246 		dst = SPrintMoney(obj.uiMoneyAmount);
5247 	}
5248 	else if (GCM->getItem(usItem)->getItemClass() == IC_MONEY)
5249 	{
5250 		// alternate money like silver or gold
5251 		ST::string pStr2 = SPrintMoney(obj.uiMoneyAmount);
5252 		dst = ST::format("{} ({})", ItemNames[usItem], pStr2);
5253 	}
5254 	else if (usItem == NOTHING)
5255 	{
5256 		dst = ST::null;
5257 	}
5258 	else
5259 	{
5260 		dst = ST::format("{}", ItemNames[usItem]);
5261 		if (!gGameOptions.fGunNut && GCM->getItem(usItem)->getItemClass() == IC_GUN)
5262 		{
5263 			const CalibreModel * calibre = GCM->getWeapon(usItem)->calibre;
5264 			if (calibre->showInHelpText)
5265 			{
5266 				ST::string name = *calibre->getName();
5267 				dst += ST::format(" ({})", name);
5268 			}
5269 		}
5270 
5271 		ST::string imprint = GetObjectImprint(obj);
5272 		if (!imprint.empty())
5273 		{
5274 			dst += ST::format(" [{}]", imprint);
5275 		}
5276 
5277 		// Add attachment string....
5278 		ST::string first_prefix = " (";
5279 		ST::string prefix       = first_prefix;
5280 		FOR_EACH(UINT16 const, i, obj.usAttachItem)
5281 		{
5282 			UINT16 const attachment = *i;
5283 			if (attachment == NOTHING) continue;
5284 
5285 			dst += ST::format("{}{}", prefix, ItemNames[attachment]);
5286 			prefix = ",\n";
5287 		}
5288 		if (prefix != first_prefix)
5289 		{
5290 			dst += ST::format("{}", pMessageStrings[MSG_END_ATTACHMENT_LIST]);
5291 		}
5292 	}
5293 	return dst;
5294 }
5295 
5296 
CancelItemPointer()5297 void CancelItemPointer( )
5298 {
5299 	// ATE: If we have an item pointer end it!
5300 	if ( gpItemPointer != NULL )
5301 	{
5302 		if ( gbItemPointerSrcSlot != NO_SLOT )
5303 		{
5304 			// Place it back in our hands!
5305 			PlaceObject( gpItemPointerSoldier, gbItemPointerSrcSlot, gpItemPointer );
5306 
5307 			// ATE: This could potnetially swap!
5308 			// Make sure # of items is 0, if not, auto place somewhere else...
5309 			if ( gpItemPointer->ubNumberOfObjects > 0 )
5310 			{
5311 				if ( !AutoPlaceObject( gpItemPointerSoldier, gpItemPointer, FALSE ) )
5312 				{
5313 					// Alright, place of the friggen ground!
5314 					AddItemToPool(gpItemPointerSoldier->sGridNo, gpItemPointer, VISIBLE, gpItemPointerSoldier->bLevel, 0 , -1);
5315 					NotifySoldiersToLookforItems( );
5316 				}
5317 			}
5318 		}
5319 		else
5320 		{
5321 			// We drop it here.....
5322 			AddItemToPool(gpItemPointerSoldier->sGridNo, gpItemPointer, VISIBLE, gpItemPointerSoldier->bLevel, 0 , -1);
5323 			NotifySoldiersToLookforItems( );
5324 		}
5325 		EndItemPointer( );
5326 	}
5327 }
5328 
5329 
LoadItemCursorFromSavedGame(HWFILE const f)5330 void LoadItemCursorFromSavedGame(HWFILE const f)
5331 {
5332 	BYTE data[44];
5333 	FileRead(f, data, sizeof(data));
5334 
5335 	BOOLEAN      active;
5336 	SOLDIERTYPE* soldier;
5337 	DataReader d{data};
5338 	ExtractObject(d, &gItemPointer);
5339 	EXTR_SOLDIER(d, soldier)
5340 	EXTR_U8(     d, gbItemPointerSrcSlot)
5341 	EXTR_BOOL(   d, active)
5342 	EXTR_SKIP(   d, 5)
5343 	Assert(d.getConsumed() == lengthof(data));
5344 
5345 	if (active)
5346 	{
5347 		SetItemPointer(&gItemPointer, soldier);
5348 		ReEvaluateDisabledINVPanelButtons();
5349 	}
5350 	else
5351 	{
5352 		gpItemPointer        = 0;
5353 		gpItemPointerSoldier = 0;
5354 	}
5355 }
5356 
5357 
SaveItemCursorToSavedGame(HWFILE const f)5358 void SaveItemCursorToSavedGame(HWFILE const f)
5359 {
5360 	BYTE  data[44];
5361 	DataWriter d{data};
5362 	InjectObject(d, &gItemPointer);
5363 	INJ_SOLDIER(d, gpItemPointerSoldier)
5364 	INJ_U8(     d, gbItemPointerSrcSlot)
5365 	INJ_BOOL(   d, gpItemPointer != 0)
5366 	INJ_SKIP(   d, 5)
5367 	Assert(d.getConsumed() == lengthof(data));
5368 
5369 	FileWrite(f, data, sizeof(data));
5370 }
5371 
5372 
UpdateItemHatches(void)5373 void UpdateItemHatches(void)
5374 {
5375 	SOLDIERTYPE *pSoldier = NULL;
5376 
5377 	if (fInMapMode)
5378 	{
5379 		if (fShowInventoryFlag) pSoldier = GetSelectedInfoChar();
5380 	}
5381 	else
5382 	{
5383 		pSoldier = gpSMCurrentMerc;
5384 	}
5385 
5386 	if ( pSoldier != NULL )
5387 	{
5388 		ReevaluateItemHatches(pSoldier, FALSE);
5389 	}
5390 }
5391 
5392 
SetMouseCursorFromItem(UINT16 const item_idx)5393 void SetMouseCursorFromItem(UINT16 const item_idx)
5394 {
5395 	const ItemModel * item = GCM->getItem(item_idx);
5396 	SGPVObject const& vo   = GetInterfaceGraphicForItem(item);
5397 	SetExternMouseCursor(vo, item->getGraphicNum());
5398 	SetCurrentCursorFromDatabase(EXTERN_CURSOR);
5399 }
5400 
5401 
SetMouseCursorFromCurrentItem()5402 void SetMouseCursorFromCurrentItem()
5403 {
5404 	SetMouseCursorFromItem(gpItemPointer->usItem);
5405 }
5406 
5407 
SetItemPointer(OBJECTTYPE * const o,SOLDIERTYPE * const s)5408 void SetItemPointer(OBJECTTYPE* const o, SOLDIERTYPE* const s)
5409 {
5410 	gpItemPointer        = o;
5411 	gpItemPointerSoldier = s;
5412 }
5413 
5414 
LoadInterfaceItemsGraphics()5415 void LoadInterfaceItemsGraphics()
5416 {
5417 	guiMapInvSecondHandBlockout = AddVideoObjectFromFile(INTERFACEDIR "/map_inv_2nd_gun_cover.sti");
5418 	guiSecItemHiddenVO          = AddVideoObjectFromFile(INTERFACEDIR "/secondary_gun_hidden.sti");
5419 	guiGUNSM                    = AddVideoObjectFromFile(INTERFACEDIR "/mdguns.sti");    // interface gun pictures
5420 	guiP1ITEMS                  = AddVideoObjectFromFile(INTERFACEDIR "/mdp1items.sti"); // interface item pictures
5421 	guiP2ITEMS                  = AddVideoObjectFromFile(INTERFACEDIR "/mdp2items.sti"); // interface item pictures
5422 	guiP3ITEMS                  = AddVideoObjectFromFile(INTERFACEDIR "/mdp3items.sti"); // interface item pictures
5423 
5424 	// Build a sawtooth black-white-black colour gradient
5425 	size_t const length = lengthof(us16BPPItemCyclePlacedItemColors);
5426 	for (INT32 i = 0; i != length / 2; ++i)
5427 	{
5428 		UINT32 const l = 25 * (i + 1);
5429 		UINT16 const c = Get16BPPColor(FROMRGB(l, l, l));
5430 		us16BPPItemCyclePlacedItemColors[i]              = c;
5431 		us16BPPItemCyclePlacedItemColors[length - i - 1] = c;
5432 	}
5433 }
5434 
5435 
DeleteInterfaceItemsGraphics()5436 void DeleteInterfaceItemsGraphics()
5437 {
5438 	DeleteVideoObject(guiMapInvSecondHandBlockout);
5439 	DeleteVideoObject(guiSecItemHiddenVO);
5440 	DeleteVideoObject(guiGUNSM);
5441 	DeleteVideoObject(guiP1ITEMS);
5442 	DeleteVideoObject(guiP2ITEMS);
5443 	DeleteVideoObject(guiP3ITEMS);
5444 }
5445