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