1 #include "HImage.h"
2 #include "Handle_Items.h"
3 #include "Edit_Sys.h"
4 #include "TileDat.h"
5 #include "VSurface.h"
6 #include "VObject.h"
7 #include "MouseSystem.h"
8 #include "Input.h"
9 #include "SysUtil.h"
10 #include "Font.h"
11 #include "Font_Control.h"
12 #include "EditScreen.h"
13 #include "SelectWin.h"
14 #include "VObject_Blitters.h"
15 #include "Interface_Items.h"
16 #include "Text.h"
17 #include "Action_Items.h"
18 #include "World_Items.h"
19 #include "EditorDefines.h"
20 #include "EditorItems.h"
21 #include "EditorMercs.h"
22 #include "Weapons.h"
23 #include "Editor_Taskbar_Utils.h"
24 #include "WordWrap.h"
25 #include "Item_Statistics.h"
26 #include "Simple_Render_Utils.h"
27 #include "WorldMan.h"
28 #include "Random.h"
29 #include "Pits.h"
30 #include "Keys.h"
31 #include "Debug.h"
32 #include "Items.h"
33 #include "MemMan.h"
34
35 #include "ContentManager.h"
36 #include "GameInstance.h"
37 #include "MagazineModel.h"
38 #include "WeaponModels.h"
39
40 #include <string_theory/format>
41 #include <string_theory/string>
42
43
44 #define NUMBER_TRIGGERS 27
45 #define PRESSURE_ACTION_ID (NUMBER_TRIGGERS - 1)
46
47 extern ITEM_POOL *gpEditingItemPool;
48
49 INT32 giDefaultExistChance = 100;
50
51 struct IPListNode
52 {
53 INT16 sGridNo;
54 IPListNode* next;
55 };
56
57 static IPListNode* pIPHead = NULL;
58
59 static IPListNode* gpCurrItemPoolNode = NULL;
60 ITEM_POOL *gpItemPool = NULL;
61
62
63 static void ShowItemCursor(INT32 iMapIndex);
64
65
BuildItemPoolList(void)66 void BuildItemPoolList(void)
67 {
68 KillItemPoolList();
69
70 IPListNode** anchor = &pIPHead;
71 for (UINT16 i = 0; i < WORLD_MAX; ++i)
72 {
73 if (GetItemPool(i, 0) == NULL) continue;
74
75 ShowItemCursor(i);
76
77 IPListNode* const n = new IPListNode{};
78 n->sGridNo = i;
79 n->next = NULL;
80
81 *anchor = n;
82 anchor = &n->next;
83 }
84 gpCurrItemPoolNode = pIPHead;
85 SpecifyItemToEdit(NULL, -1);
86 }
87
88
89 static void HideItemCursor(INT32 iMapIndex);
90
91
KillItemPoolList()92 void KillItemPoolList()
93 {
94 IPListNode *pIPCurr;
95 pIPCurr = pIPHead;
96 while( pIPCurr )
97 {
98 HideItemCursor( pIPCurr->sGridNo );
99 pIPHead = pIPHead->next;
100 delete pIPCurr;
101 pIPCurr = pIPHead;
102 }
103 pIPHead = NULL;
104 }
105
106 //Contains global information about the editor items
107 //May be expanded to encapsulate the entire editor later.
108 EditorItemsInfo eInfo;
109
110 //Does some precalculations regarding the number of each item type, so that it
111 //isn't calculated every time a player changes categories.
EntryInitEditorItemsInfo()112 void EntryInitEditorItemsInfo()
113 {
114 INT32 i;
115 eInfo.uiBuffer = 0;
116 eInfo.fActive = 0;
117 eInfo.sScrollIndex = 0;
118 eInfo.sSelItemIndex = 0;
119 eInfo.sHilitedItemIndex = -1;
120 eInfo.sNumItems = 0;
121 eInfo.pusItemIndex = NULL;
122 if( eInfo.fGameInit )
123 { //This only gets called one time in game execution.
124 eInfo = EditorItemsInfo{};
125 eInfo.sHilitedItemIndex = -1;
126 eInfo.uiItemType = TBAR_MODE_ITEM_WEAPONS;
127 //Pre-calculate the number of each item type.
128 eInfo.sNumTriggers = NUMBER_TRIGGERS;
129 for( i=0; i < MAXITEMS; i++ )
130 {
131 const ItemModel* item = GCM->getItem(i);
132 if( GCM->getItem(i)->getFlags() & ITEM_NOT_EDITOR )
133 continue;
134 if( i == SWITCH || i == ACTION_ITEM )
135 {
136
137 }
138 else switch( item->getItemClass() )
139 {
140 case IC_GUN:
141 case IC_BLADE:
142 case IC_THROWN:
143 case IC_LAUNCHER:
144 case IC_THROWING_KNIFE:
145 eInfo.sNumWeapons++;
146 break;
147 case IC_PUNCH:
148 if ( i != NOTHING )
149 {
150 eInfo.sNumWeapons++;
151 }
152 break;
153 case IC_AMMO:
154 eInfo.sNumAmmo++;
155 break;
156 case IC_ARMOUR:
157 eInfo.sNumArmour++;
158 break;
159 case IC_GRENADE:
160 case IC_BOMB:
161 eInfo.sNumExplosives++;
162 break;
163 case IC_MEDKIT:
164 case IC_KIT:
165 case IC_FACE:
166 case IC_MISC:
167 case IC_MONEY:
168 if( eInfo.sNumEquipment1 < 30 )
169 eInfo.sNumEquipment1++;
170 else if( eInfo.sNumEquipment2 < 30 )
171 eInfo.sNumEquipment2++;
172 else
173 eInfo.sNumEquipment3++;
174 break;
175 //case IC_KEY:
176 // eInfo.sNumKeys++;
177 // break;
178 }
179 }
180 eInfo.sNumKeys = NUM_KEYS;
181 }
182 }
183
184
DrawItemCentered(const ItemModel * item,SGPVSurface * const vs,INT32 x,INT32 const y,INT16 const outline)185 static void DrawItemCentered(const ItemModel * item, SGPVSurface* const vs, INT32 x, INT32 const y, INT16 const outline)
186 {
187 // Calculate the center position of the graphic in a 60 pixel wide area.
188 SGPVObject const& vo = GetInterfaceGraphicForItem(item);
189 UINT const gfx = item->getGraphicNum();
190 ETRLEObject const& e = vo.SubregionProperties(gfx);
191 x += (60 - e.usWidth) / 2 - e.sOffsetX;
192 BltVideoObjectOutline(vs, &vo, gfx, x, y, outline);
193 }
194
195
InitEditorItemsInfo(ToolbarMode const uiItemType)196 void InitEditorItemsInfo(ToolbarMode const uiItemType)
197 {
198 SGPRect SaveRect, NewRect;
199 INT16 i, x, y;
200 UINT16 usCounter;
201 ST::string pStr;
202 BOOLEAN fTypeMatch;
203 INT32 iEquipCount = 0;
204
205 // Check to make sure that there isn't already a valid eInfo
206 if( eInfo.fActive )
207 {
208 if( eInfo.uiItemType == uiItemType )
209 { //User clicked on the same item classification -- ignore
210 return;
211 }
212 else
213 { //User selected a different item classification -- delete it first.
214 ClearEditorItemsInfo();
215 ClearTaskbarRegion(100, 0, 480, 80);
216 }
217 }
218 else
219 {
220 //Clear the menu area, so that the buffer doesn't get corrupted.
221 ClearTaskbarRegion(100, 0, 480, 80);
222 }
223 EnableEditorRegion( ITEM_REGION_ID );
224
225 eInfo.uiItemType = uiItemType;
226 eInfo.fActive = TRUE;
227 //Begin initialization of data.
228 switch(uiItemType)
229 {
230 case TBAR_MODE_ITEM_WEAPONS:
231 eInfo.sNumItems = eInfo.sNumWeapons;
232 eInfo.sScrollIndex = eInfo.sSaveWeaponsScrollIndex;
233 eInfo.sSelItemIndex = eInfo.sSaveSelWeaponsIndex;
234 break;
235 case TBAR_MODE_ITEM_AMMO:
236 eInfo.sNumItems = eInfo.sNumAmmo;
237 eInfo.sScrollIndex = eInfo.sSaveAmmoScrollIndex;
238 eInfo.sSelItemIndex = eInfo.sSaveSelAmmoIndex;
239 break;
240 case TBAR_MODE_ITEM_ARMOUR:
241 eInfo.sNumItems = eInfo.sNumArmour;
242 eInfo.sScrollIndex = eInfo.sSaveArmourScrollIndex;
243 eInfo.sSelItemIndex = eInfo.sSaveSelArmourIndex;
244 break;
245 case TBAR_MODE_ITEM_EXPLOSIVES:
246 eInfo.sNumItems = eInfo.sNumExplosives;
247 eInfo.sScrollIndex = eInfo.sSaveExplosivesScrollIndex;
248 eInfo.sSelItemIndex = eInfo.sSaveSelExplosivesIndex;
249 break;
250 case TBAR_MODE_ITEM_EQUIPMENT1:
251 eInfo.sNumItems = eInfo.sNumEquipment1;
252 eInfo.sScrollIndex = eInfo.sSaveEquipment1ScrollIndex;
253 eInfo.sSelItemIndex = eInfo.sSaveSelEquipment1Index;
254 break;
255 case TBAR_MODE_ITEM_EQUIPMENT2:
256 eInfo.sNumItems = eInfo.sNumEquipment2;
257 eInfo.sScrollIndex = eInfo.sSaveEquipment2ScrollIndex;
258 eInfo.sSelItemIndex = eInfo.sSaveSelEquipment2Index;
259 break;
260 case TBAR_MODE_ITEM_EQUIPMENT3:
261 eInfo.sNumItems = eInfo.sNumEquipment3;
262 eInfo.sScrollIndex = eInfo.sSaveEquipment3ScrollIndex;
263 eInfo.sSelItemIndex = eInfo.sSaveSelEquipment3Index;
264 break;
265 case TBAR_MODE_ITEM_TRIGGERS:
266 eInfo.sNumItems = eInfo.sNumTriggers;
267 eInfo.sScrollIndex = eInfo.sSaveTriggersScrollIndex;
268 eInfo.sSelItemIndex = eInfo.sSaveSelTriggersIndex;
269 break;
270 case TBAR_MODE_ITEM_KEYS:
271 eInfo.sNumItems = eInfo.sNumKeys;
272 eInfo.sScrollIndex = eInfo.sSaveKeysScrollIndex;
273 eInfo.sSelItemIndex = eInfo.sSaveSelKeysIndex;
274 break;
275 default:
276 //error
277 return;
278 }
279 //Allocate memory to store all the item pointers.
280 eInfo.pusItemIndex = new UINT16[eInfo.sNumItems]{};
281
282 //Disable the appropriate scroll buttons based on the saved scroll index if applicable
283 //Left most scroll position
284 DetermineItemsScrolling();
285 //calculate the width of the buffer based on the number of items.
286 //every pair of items (odd rounded up) requires 60 pixels for width.
287 //the minimum buffer size is 420. Height is always 80 pixels.
288 const INT16 w = (eInfo.sNumItems > 12 ? (eInfo.sNumItems + 1) / 2 * 60 : 360);
289 const INT16 h = 80;
290 // Create item buffer
291
292 //!!!Memory check. Create the item buffer
293 eInfo.uiBuffer = AddVideoSurface(w, h, PIXEL_DEPTH);
294
295 //copy a blank chunk of the editor interface to the new buffer.
296 for (i = 0; i < w; i += 60)
297 {
298 SGPBox const r = { 100, EDITOR_TASKBAR_POS_Y, 60, 80 };
299 BltVideoSurface(eInfo.uiBuffer, FRAME_BUFFER, i, 0, &r);
300 }
301
302 x = 0;
303 y = 0;
304 usCounter = 0;
305 NewRect.iTop = 0;
306 NewRect.iBottom = h;
307 NewRect.iLeft = 0;
308 NewRect.iRight = w;
309 GetClippingRect(&SaveRect);
310 SetClippingRect(&NewRect);
311 if( eInfo.uiItemType == TBAR_MODE_ITEM_KEYS )
312 { //Keys use a totally different method for determining
313 for( i = 0; i < eInfo.sNumItems; i++ )
314 {
315 UINT16 const item_id = KEY_1 + LockTable[i].usKeyItem;
316
317 //Store these item pointers for later when rendering selected items.
318 eInfo.pusItemIndex[i] = item_id;
319
320 SetFontDestBuffer(eInfo.uiBuffer);
321
322 pStr = ST::format("{}", LockTable[i].ubEditorName);
323 DisplayWrappedString(x, y + 25, 60, 2, SMALLCOMPFONT, FONT_WHITE, pStr, FONT_BLACK, CENTER_JUSTIFIED | MARK_DIRTY);
324
325 DrawItemCentered(GCM->getItem(item_id), eInfo.uiBuffer, x, y + 2, SGP_TRANSPARENT);
326
327 //cycle through the various slot positions (0,0), (0,40), (60,0), (60,40), (120,0)...
328 if( y == 0 )
329 {
330 y = 40;
331 }
332 else
333 {
334 y = 0;
335 x += 60;
336 }
337 }
338 }
339 else for( i = 0; i < eInfo.sNumItems; i++ )
340 {
341
342 fTypeMatch = FALSE;
343 while( usCounter<MAXITEMS && !fTypeMatch )
344 {
345 const ItemModel * item = GCM->getItem(usCounter);
346 if( GCM->getItem(usCounter)->getFlags() & ITEM_NOT_EDITOR )
347 {
348 usCounter++;
349 continue;
350 }
351 if( eInfo.uiItemType == TBAR_MODE_ITEM_TRIGGERS )
352 {
353 if( i < PRESSURE_ACTION_ID )
354 usCounter = ( i % 2 ) ? ACTION_ITEM : SWITCH;
355 else
356 usCounter = ACTION_ITEM;
357 fTypeMatch = TRUE;
358 item = GCM->getItem(usCounter);
359 }
360 else switch( item->getItemClass() )
361 {
362 case IC_GUN:
363 case IC_BLADE:
364 case IC_LAUNCHER:
365 case IC_THROWN:
366 case IC_THROWING_KNIFE:
367 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_WEAPONS;
368 break;
369 case IC_PUNCH:
370 if ( i != NOTHING )
371 {
372 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_WEAPONS;
373 }
374 else
375 {
376 fTypeMatch = FALSE;
377 }
378 break;
379 case IC_AMMO:
380 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_AMMO;
381 break;
382 case IC_ARMOUR:
383 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_ARMOUR;
384 break;
385 case IC_GRENADE:
386 case IC_BOMB:
387 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EXPLOSIVES;
388 break;
389 case IC_MEDKIT:
390 case IC_KIT:
391 case IC_FACE:
392 case IC_MISC:
393 case IC_MONEY:
394 if( usCounter == ACTION_ITEM || usCounter == SWITCH )
395 break;
396 if( iEquipCount < 30 )
397 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EQUIPMENT1;
398 else if( iEquipCount < 60 )
399 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EQUIPMENT2;
400 else
401 fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EQUIPMENT3;
402 iEquipCount++;
403 break;
404 }
405 if( fTypeMatch )
406 {
407 //Store these item pointers for later when rendering selected items.
408 eInfo.pusItemIndex[i] = usCounter;
409
410 SetFontDestBuffer(eInfo.uiBuffer);
411
412 if( eInfo.uiItemType != TBAR_MODE_ITEM_TRIGGERS )
413 {
414 pStr = ItemNames[usCounter];
415 }
416 else
417 {
418 if( i == PRESSURE_ACTION_ID )
419 {
420 pStr = "Pressure Action";
421 }
422 else if( i < 2 )
423 {
424 if( usCounter == SWITCH )
425 pStr = "Panic Trigger1";
426 else
427 pStr = "Panic Action1";
428 }
429 else if( i < 4 )
430 {
431 if( usCounter == SWITCH )
432 pStr = "Panic Trigger2";
433 else
434 pStr = "Panic Action2";
435 }
436 else if( i < 6 )
437 {
438 if( usCounter == SWITCH )
439 pStr = "Panic Trigger3";
440 else
441 pStr = "Panic Action3";
442 }
443 else
444 {
445 if( usCounter == SWITCH )
446 pStr = ST::format("Trigger{}", (i - 4) / 2);
447 else
448 pStr = ST::format("Action{}", (i - 4) / 2);
449 }
450 }
451 DisplayWrappedString(x, y + 25, 60, 2, SMALLCOMPFONT, FONT_WHITE, pStr, FONT_BLACK, CENTER_JUSTIFIED | MARK_DIRTY);
452
453 DrawItemCentered(item, eInfo.uiBuffer, x, y + 2, SGP_TRANSPARENT);
454
455 //cycle through the various slot positions (0,0), (0,40), (60,0), (60,40), (120,0)...
456 if( y == 0 )
457 {
458 y = 40;
459 }
460 else
461 {
462 y = 0;
463 x += 60;
464 }
465 }
466 usCounter++;
467 }
468 }
469 SetFontDestBuffer(FRAME_BUFFER);
470 SetClippingRect(&SaveRect);
471 gfRenderTaskbar = TRUE;
472 }
473
DetermineItemsScrolling()474 void DetermineItemsScrolling()
475 {
476 if( !eInfo.sScrollIndex )
477 DisableEditorButton( ITEMS_LEFTSCROLL );
478 else
479 EnableEditorButton( ITEMS_LEFTSCROLL );
480 //Right most scroll position. Calculated by taking every pair of numItems rounded up,
481 //and subtracting 7 (because a scroll index 0 is disabled if there are <=12 items,
482 //index 1 for <=14 items, index 2 for <=16 items...
483 if( eInfo.sScrollIndex == MAX( ((eInfo.sNumItems+1)/2)-6, 0 ) )
484 DisableEditorButton( ITEMS_RIGHTSCROLL );
485 else
486 EnableEditorButton( ITEMS_RIGHTSCROLL );
487 }
488
489
490 static UINT16 CountNumberOfEditorPlacementsInWorld(UINT16 usEInfoIndex, UINT16* pusQuantity);
491
492
drawItemWithOutline(INT16 min_idx,INT16 end_idx,INT16 scroll_idx,INT16 itemIndex,INT16 const outline)493 static void drawItemWithOutline(INT16 min_idx, INT16 end_idx, INT16 scroll_idx, INT16 itemIndex, INT16 const outline)
494 {
495 if (min_idx <= itemIndex && itemIndex < end_idx)
496 {
497 INT16 const x = (itemIndex / 2 - scroll_idx) * 60 + 110;
498 INT16 const y = EDITOR_TASKBAR_POS_Y + (itemIndex % 2) * 40;
499 const ItemModel *item = GCM->getItem(eInfo.pusItemIndex[itemIndex]);
500 DrawItemCentered(item, FRAME_BUFFER, x, y + 2, outline);
501 }
502 }
503
504
RenderEditorItemsInfo()505 void RenderEditorItemsInfo()
506 {
507 if (!eInfo.fActive) return;
508
509 if ((gusMouseXPos < 110) || (480 < gusMouseXPos) ||
510 (gusMouseYPos < EDITOR_TASKBAR_POS_Y) || (EDITOR_TASKBAR_POS_Y + 80 < gusMouseYPos))
511 { // Mouse has moved out of the items display region -- so nothing can be highlighted.
512 eInfo.sHilitedItemIndex = -1;
513 }
514
515 INT16 const scroll_idx = eInfo.sScrollIndex;
516
517 SGPBox const r = { (UINT16)(60 * scroll_idx), 0, 360, 80 };
518 BltVideoSurface(FRAME_BUFFER, eInfo.uiBuffer, 110, EDITOR_TASKBAR_POS_Y, &r);
519
520 /* Calculate the min and max index that is currently shown. This determines
521 * if the highlighted and/or selected items are drawn with the outlines. */
522 INT16 const min_idx = scroll_idx * 2;
523 INT16 const end_idx = MIN(min_idx + 12, eInfo.sNumItems);
524
525 // Draw the hilighted and selected items if applicable.
526 if (eInfo.pusItemIndex)
527 {
528 drawItemWithOutline(min_idx, end_idx, scroll_idx, eInfo.sHilitedItemIndex, Get16BPPColor(FROMRGB(250, 250, 0)));
529 drawItemWithOutline(min_idx, end_idx, scroll_idx, eInfo.sSelItemIndex, Get16BPPColor(FROMRGB(250, 0, 0)));
530 }
531
532 // Draw the numbers of each visible item that currently resides in the world.
533 for (INT16 i = min_idx; i < end_idx; ++i)
534 {
535 UINT16 quantity;
536 UINT16 const n_items = CountNumberOfEditorPlacementsInWorld(i, &quantity);
537 if (n_items == 0) continue;
538
539 INT16 const x = (i / 2 - scroll_idx) * 60 + 110;
540 INT16 const y = EDITOR_TASKBAR_POS_Y + (i % 2) * 40;
541 SetFontAttributes(FONT10ARIAL, FONT_YELLOW);
542 if (n_items == quantity)
543 {
544 MPrint(x + 12, y + 4, ST::format("{}", n_items));
545 }
546 else
547 {
548 MPrint(x + 12, y + 4, ST::format("{}({})", n_items, quantity));
549 }
550 }
551 }
552
553
ClearEditorItemsInfo()554 void ClearEditorItemsInfo()
555 {
556 if( eInfo.uiBuffer )
557 {
558 DeleteVideoSurface(eInfo.uiBuffer);
559 eInfo.uiBuffer = 0;
560 }
561 if( eInfo.pusItemIndex )
562 {
563 delete[] eInfo.pusItemIndex;
564 eInfo.pusItemIndex = NULL;
565 }
566 DisableEditorRegion( ITEM_REGION_ID );
567 eInfo.fActive = 0;
568 eInfo.sNumItems = 0;
569 //save the highlighted selections
570 switch( eInfo.uiItemType )
571 {
572 case TBAR_MODE_ITEM_WEAPONS:
573 eInfo.sSaveSelWeaponsIndex = eInfo.sSelItemIndex;
574 eInfo.sSaveWeaponsScrollIndex = eInfo.sScrollIndex;
575 break;
576 case TBAR_MODE_ITEM_AMMO:
577 eInfo.sSaveSelAmmoIndex = eInfo.sSelItemIndex;
578 eInfo.sSaveAmmoScrollIndex = eInfo.sScrollIndex;
579 break;
580 case TBAR_MODE_ITEM_ARMOUR:
581 eInfo.sSaveSelArmourIndex = eInfo.sSelItemIndex;
582 eInfo.sSaveArmourScrollIndex = eInfo.sScrollIndex;
583 break;
584 case TBAR_MODE_ITEM_EXPLOSIVES:
585 eInfo.sSaveSelExplosivesIndex = eInfo.sSelItemIndex;
586 eInfo.sSaveExplosivesScrollIndex = eInfo.sScrollIndex;
587 break;
588 case TBAR_MODE_ITEM_EQUIPMENT1:
589 eInfo.sSaveSelEquipment1Index = eInfo.sSelItemIndex;
590 eInfo.sSaveEquipment1ScrollIndex = eInfo.sScrollIndex;
591 break;
592 case TBAR_MODE_ITEM_EQUIPMENT2:
593 eInfo.sSaveSelEquipment2Index = eInfo.sSelItemIndex;
594 eInfo.sSaveEquipment2ScrollIndex = eInfo.sScrollIndex;
595 break;
596 case TBAR_MODE_ITEM_EQUIPMENT3:
597 eInfo.sSaveSelEquipment3Index = eInfo.sSelItemIndex;
598 eInfo.sSaveEquipment3ScrollIndex = eInfo.sScrollIndex;
599 break;
600 case TBAR_MODE_ITEM_TRIGGERS:
601 eInfo.sSaveSelTriggersIndex = eInfo.sSelItemIndex;
602 eInfo.sSaveTriggersScrollIndex = eInfo.sScrollIndex;
603 break;
604 case TBAR_MODE_ITEM_KEYS:
605 eInfo.sSaveSelKeysIndex = eInfo.sSelItemIndex;
606 eInfo.sSaveKeysScrollIndex = eInfo.sScrollIndex;
607 break;
608 default:
609 break;
610 }
611 }
612
613
614 static void FindNextItemOfSelectedType(void);
615
616
HandleItemsPanel(UINT16 usScreenX,UINT16 usScreenY,INT8 bEvent)617 void HandleItemsPanel( UINT16 usScreenX, UINT16 usScreenY, INT8 bEvent )
618 {
619 INT16 sIndex;
620 UINT16 usQuantity;
621 //Calc base index from scrolling index
622 sIndex = eInfo.sScrollIndex * 2;
623 //Determine if the index is in the first row or second row from mouse YPos.
624 if( usScreenY >= EDITOR_TASKBAR_POS_Y + 40 )
625 sIndex++;
626 //Add the converted mouse's XPos into a relative index;
627 //Calc: starting from 110, for every 60 pixels, add 2 to the index
628 sIndex += ((usScreenX-110)/60) * 2;
629 switch( bEvent )
630 {
631 case GUI_MOVE_EVENT:
632 if( sIndex < eInfo.sNumItems )
633 {
634 if( eInfo.sHilitedItemIndex != sIndex )
635 gfRenderTaskbar = TRUE;
636 //this index will now highlight in yellow.
637 eInfo.sHilitedItemIndex = sIndex;
638 }
639 break;
640 case GUI_LCLICK_EVENT:
641 if( sIndex < eInfo.sNumItems )
642 {
643 //this index will now highlight in red.
644 if( eInfo.sSelItemIndex != sIndex )
645 gfRenderTaskbar = TRUE;
646 eInfo.sSelItemIndex = sIndex;
647 if( gfMercGetItem )
648 {
649 gfMercGetItem = FALSE;
650 gusMercsNewItemIndex = eInfo.pusItemIndex[ eInfo.sSelItemIndex ];
651 SetMercEditingMode( MERC_INVENTORYMODE );
652 ClearEditorItemsInfo();
653 }
654 }
655 break;
656 case GUI_RCLICK_EVENT:
657 if( gfMercGetItem )
658 {
659 gfMercGetItem = FALSE;
660 gusMercsNewItemIndex = 0xffff;
661 SetMercEditingMode( MERC_INVENTORYMODE );
662 ClearEditorItemsInfo();
663 }
664 else if( sIndex < eInfo.sNumItems )
665 {
666 eInfo.sSelItemIndex = sIndex;
667 gfRenderTaskbar = TRUE;
668 if( CountNumberOfEditorPlacementsInWorld( eInfo.sSelItemIndex, &usQuantity ) )
669 {
670 FindNextItemOfSelectedType();
671 }
672 }
673 break;
674 }
675 }
676
677
ShowItemCursor(INT32 const iMapIndex)678 static void ShowItemCursor(INT32 const iMapIndex)
679 {
680 for (LEVELNODE const* n = gpWorldLevelData[iMapIndex].pTopmostHead; n; n = n->pNext)
681 {
682 if (n->usIndex == SELRING) return;
683 }
684 AddTopmostToTail(iMapIndex, SELRING1);
685 }
686
687
HideItemCursor(INT32 iMapIndex)688 static void HideItemCursor(INT32 iMapIndex)
689 {
690 RemoveTopmost( iMapIndex, SELRING1 );
691 }
692
693
TriggerAtGridNo(INT16 sGridNo)694 static BOOLEAN TriggerAtGridNo(INT16 sGridNo)
695 {
696 return ItemTypeExistsAtLocation(sGridNo, SWITCH, 0, 0);
697 }
698
699
CalcItemFrequency(UINT16 const item_idx)700 static INT8 CalcItemFrequency(UINT16 const item_idx)
701 {
702 return
703 item_idx < 2 ? PANIC_FREQUENCY :
704 item_idx < 4 ? PANIC_FREQUENCY_2 :
705 item_idx < 6 ? PANIC_FREQUENCY_3 :
706 FIRST_MAP_PLACED_FREQUENCY + (item_idx - 4) / 2;
707 }
708
709
AddSelectedItemToWorld(INT16 sGridNo)710 void AddSelectedItemToWorld(INT16 sGridNo)
711 {
712 // Extract the currently selected item.
713 SpecifyItemToEdit(NULL, -1);
714
715 OBJECTTYPE tempObject;
716 if (eInfo.uiItemType == TBAR_MODE_ITEM_KEYS)
717 {
718 CreateKeyObject(&tempObject, 1, (UINT8)eInfo.sSelItemIndex);
719 }
720 else
721 {
722 CreateItem(eInfo.pusItemIndex[eInfo.sSelItemIndex], 100, &tempObject);
723 }
724
725 Visibility bVisibility = INVISIBLE;
726 UINT16 usFlags = 0;
727 switch (tempObject.usItem)
728 {
729 case MINE:
730 if (bVisibility == BURIED) usFlags |= WORLD_ITEM_ARMED_BOMB;
731 break;
732
733 case MONEY:
734 case SILVER:
735 case GOLD:
736 tempObject.bStatus[0] = 100;
737 tempObject.uiMoneyAmount = 100 + Random(19901);
738 break;
739
740 case OWNERSHIP:
741 tempObject.ubOwnerProfile = NO_PROFILE;
742 bVisibility = BURIED;
743 break;
744
745 case SWITCH:
746 // Restricted to one action per gridno.
747 if (TriggerAtGridNo(sGridNo)) return;
748 bVisibility = BURIED;
749 tempObject.bStatus[0] = 100;
750 tempObject.ubBombOwner = 1;
751 tempObject.bFrequency = CalcItemFrequency(eInfo.sSelItemIndex);
752 usFlags |= WORLD_ITEM_ARMED_BOMB;
753 break;
754
755 case ACTION_ITEM:
756 bVisibility = BURIED;
757 tempObject.bStatus[0] = 100;
758 tempObject.ubBombOwner = 1;
759 tempObject.bTrap = gbDefaultBombTrapLevel;
760 if (eInfo.sSelItemIndex < PRESSURE_ACTION_ID)
761 {
762 tempObject.bDetonatorType = BOMB_REMOTE;
763 tempObject.bFrequency = CalcItemFrequency(eInfo.sSelItemIndex);
764 }
765 else
766 {
767 tempObject.bDetonatorType = BOMB_PRESSURE;
768 tempObject.bDelay = 0;
769 }
770 ChangeActionItem(&tempObject, gbActionItemIndex);
771 tempObject.fFlags |= OBJECT_ARMED_BOMB;
772 switch (gbActionItemIndex)
773 {
774 case ACTIONITEM_SMPIT: Add3X3Pit(sGridNo); break;
775 case ACTIONITEM_LGPIT: Add5X5Pit(sGridNo); break;
776 }
777 usFlags |= WORLD_ITEM_ARMED_BOMB;
778 break;
779 }
780
781 INT32 const iItemIndex = InternalAddItemToPool(&sGridNo, &tempObject, bVisibility, 0, usFlags, 0);
782 WORLDITEM& wi = GetWorldItem(iItemIndex);
783 wi.ubNonExistChance = (tempObject.usItem == OWNERSHIP ? 0 : 100 - giDefaultExistChance);
784
785 OBJECTTYPE& obj = wi.o;
786 const ItemModel * item = GCM->getItem(obj.usItem);
787 if (item->isAmmo())
788 {
789 UINT8 const mag_size = item->asAmmo()->capacity;
790 obj.ubShotsLeft[0] = Random(2) ? mag_size : Random(mag_size);
791 }
792 else
793 {
794 obj.bStatus[0] = 70 + Random(26);
795 }
796 if (item->isGun())
797 {
798 obj.ubGunShotsLeft = obj.usItem == ROCKET_LAUNCHER ? 1 : Random(GCM->getWeapon(obj.usItem)->ubMagSize);
799 }
800
801 for (ITEM_POOL* ip = GetItemPool(sGridNo, 0); Assert(ip), ip; ip = ip->pNext)
802 {
803 if (&GetWorldItem(ip->iItemIndex).o != &obj) continue;
804 gpItemPool = ip;
805 break;
806 }
807
808 SpecifyItemToEdit(&obj, sGridNo);
809
810 //Get access to the itempool.
811 //search for a current node in list containing same mapindex
812 IPListNode** anchor = &pIPHead;
813 for (IPListNode* i = pIPHead; i; i = i->next)
814 {
815 anchor = &i->next;
816 if (i->sGridNo == sGridNo)
817 {
818 //found one, so we don't need to add it
819 gpCurrItemPoolNode = i;
820 return;
821 }
822 }
823 //there isn't one, so we will add it now.
824 ShowItemCursor(sGridNo);
825
826 IPListNode* const n = new IPListNode{};
827 n->next = 0;
828 n->sGridNo = sGridNo;
829 *anchor = n;
830 gpCurrItemPoolNode = n;
831 }
832
833
HandleRightClickOnItem(INT16 sGridNo)834 void HandleRightClickOnItem( INT16 sGridNo )
835 {
836 ITEM_POOL *pItemPool;
837 IPListNode *pIPCurr;
838
839 if( gsItemGridNo == sGridNo )
840 { //Clicked on the same gridno as the selected item. Automatically select the next
841 //item in the same pool.
842 pItemPool = gpItemPool->pNext;
843 if( !pItemPool )
844 { //currently selected item was last node, so select the head node even if it is the same.
845 pItemPool = GetItemPool(sGridNo, 0);
846 }
847 }
848 else
849 {
850 pItemPool = GetItemPool(sGridNo, 0);
851 if (pItemPool == NULL)
852 {
853 // possibly relocate selected item to this gridno?
854 return;
855 }
856 }
857
858 gpItemPool = pItemPool;
859
860 //set up the item pool pointer to point to the same mapindex node
861 pIPCurr = pIPHead;
862 gpCurrItemPoolNode = NULL;
863 while( pIPCurr )
864 {
865 if( pIPCurr->sGridNo == sGridNo )
866 {
867 gpCurrItemPoolNode = pIPCurr;
868 break;
869 }
870 pIPCurr = pIPCurr->next;
871 }
872 Assert( gpCurrItemPoolNode );
873 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
874 SpecifyItemToEdit(&wi.o, wi.sGridNo);
875 }
876
877
878
DeleteSelectedItem()879 void DeleteSelectedItem()
880 {
881 SpecifyItemToEdit( NULL, -1 );
882 //First, check to see if there even is a currently selected item.
883 if( iCurrentTaskbar == TASK_MERCS )
884 {
885 DeleteSelectedMercsItem();
886 return;
887 }
888 if( gpItemPool )
889 { //Okay, we have a selected item...
890 //save the mapindex
891 if( gpItemPool->pNext )
892 {
893 WORLDITEM& wi = GetWorldItem(gpItemPool->pNext->iItemIndex);
894 SpecifyItemToEdit(&wi.o, wi.sGridNo);
895 }
896 //remove the item
897 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
898 INT16 const sGridNo = wi.sGridNo;
899 if (wi.o.usItem == ACTION_ITEM)
900 {
901 switch (wi.o.bActionValue)
902 {
903 case ACTION_ITEM_SMALL_PIT: Remove3X3Pit(sGridNo); break;
904 case ACTION_ITEM_LARGE_PIT: Remove5X5Pit(sGridNo); break;
905 }
906 }
907 if( gpEditingItemPool == gpItemPool )
908 gpEditingItemPool = NULL;
909 RemoveItemFromPool(wi);
910 gpItemPool = NULL;
911 //determine if there are still any items at this location
912 gpItemPool = GetItemPool(sGridNo, 0);
913 if (gpItemPool == NULL)
914 { //no items left, so remove the node from the list.
915 IPListNode *pIPPrev, *pIPCurr;
916 pIPCurr = pIPHead;
917 pIPPrev = NULL;
918 while( pIPCurr )
919 {
920 if( pIPCurr->sGridNo == sGridNo )
921 {
922 if( pIPPrev ) //middle of list
923 pIPPrev->next = pIPCurr->next;
924 else //head of list
925 pIPHead = pIPHead->next;
926 //move the curr item pool to the next one.
927 if( pIPCurr->next )
928 gpCurrItemPoolNode = pIPCurr->next;
929 else
930 gpCurrItemPoolNode = pIPHead;
931 if( gpCurrItemPoolNode )
932 {
933 gpItemPool = GetItemPool(gpCurrItemPoolNode->sGridNo, 0);
934 Assert( gpItemPool );
935 }
936 //remove node
937 HideItemCursor( sGridNo );
938 delete pIPCurr;
939 pIPCurr = NULL;
940 return;
941 }
942 pIPPrev = pIPCurr;
943 pIPCurr = pIPCurr->next;
944 }
945 }
946 }
947 }
948
ShowSelectedItem()949 void ShowSelectedItem()
950 {
951 if( gpItemPool )
952 {
953 GetWorldItem(gpItemPool->iItemIndex).bVisible = INVISIBLE;
954 }
955 }
956
HideSelectedItem()957 void HideSelectedItem()
958 {
959 if( gpItemPool )
960 {
961 GetWorldItem(gpItemPool->iItemIndex).bVisible = HIDDEN_ITEM;
962 }
963 }
964
SelectNextItemPool()965 void SelectNextItemPool()
966 {
967 if( !gpCurrItemPoolNode )
968 return;
969 //remove the current hilight.
970 if( gpItemPool )
971 {
972 MarkMapIndexDirty(GetWorldItem(gpItemPool->iItemIndex).sGridNo);
973 }
974
975 //go to the next node. If at end of list, choose pIPHead
976 if( gpCurrItemPoolNode->next )
977 gpCurrItemPoolNode = gpCurrItemPoolNode->next;
978 else
979 gpCurrItemPoolNode = pIPHead;
980 //get the item pool at this node's gridno.
981 gpItemPool = GetItemPool(gpCurrItemPoolNode->sGridNo, 0);
982 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
983 MarkMapIndexDirty(wi.sGridNo);
984 SpecifyItemToEdit(&wi.o, wi.sGridNo);
985 if( gsItemGridNo != -1 )
986 {
987 CenterScreenAtMapIndex( gsItemGridNo );
988 }
989 }
990
SelectNextItemInPool()991 void SelectNextItemInPool()
992 {
993 if( gpItemPool )
994 {
995 if( gpItemPool->pNext )
996 {
997 gpItemPool = gpItemPool->pNext;
998 }
999 else
1000 {
1001 gpItemPool = GetItemPool(GetWorldItem(gpItemPool->iItemIndex).sGridNo, 0);
1002 }
1003 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1004 SpecifyItemToEdit(&wi.o, wi.sGridNo);
1005 MarkWorldDirty();
1006 }
1007 }
1008
SelectPrevItemInPool()1009 void SelectPrevItemInPool()
1010 {
1011 if (!gpItemPool) return;
1012
1013 for (ITEM_POOL* i = GetItemPool(GetWorldItem(gpItemPool->iItemIndex).sGridNo, 0);; i = i->pNext)
1014 {
1015 if (i->pNext != gpItemPool && i->pNext != NULL) continue;
1016
1017 gpItemPool = i;
1018 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1019 SpecifyItemToEdit(&wi.o, wi.sGridNo);
1020 MarkWorldDirty();
1021 break;
1022 }
1023 }
1024
1025
1026 static void SelectNextItemOfType(UINT16 usItem);
1027 static void SelectNextKeyOfType(UINT8 ubKeyID);
1028 static void SelectNextPressureAction(void);
1029 static void SelectNextTriggerWithFrequency(UINT16 usItem, INT8 bFrequency);
1030
1031
1032 /* Finds and selects the next item when right clicking on an item type.
1033 * Only works if the item actually exists in the world. */
FindNextItemOfSelectedType(void)1034 static void FindNextItemOfSelectedType(void)
1035 {
1036 UINT16 usItem;
1037 usItem = eInfo.pusItemIndex[ eInfo.sSelItemIndex ];
1038 if( usItem == ACTION_ITEM || usItem == SWITCH )
1039 {
1040 if( eInfo.sSelItemIndex < PRESSURE_ACTION_ID )
1041 {
1042 INT8 const bFrequency = CalcItemFrequency(eInfo.sSelItemIndex);
1043 SelectNextTriggerWithFrequency( usItem, bFrequency );
1044 }
1045 else
1046 {
1047 SelectNextPressureAction();
1048 }
1049 }
1050 else if( GCM->getItem( usItem )->isKey() )
1051 {
1052 SelectNextKeyOfType( (UINT8)eInfo.sSelItemIndex );
1053 }
1054 else
1055 {
1056 SelectNextItemOfType( usItem );
1057 }
1058 }
1059
1060
SelectNextItemOfType(UINT16 usItem)1061 static void SelectNextItemOfType(UINT16 usItem)
1062 {
1063 IPListNode *curr;
1064 if( gpItemPool )
1065 {
1066 curr = pIPHead;
1067 while( curr )
1068 { //skip quickly to the same gridno as the item pool
1069 if (curr->sGridNo == GetWorldItem(gpItemPool->iItemIndex).sGridNo)
1070 {
1071 gpItemPool = gpItemPool->pNext;
1072 while( gpItemPool )
1073 {
1074 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1075 OBJECTTYPE& o = wi.o;
1076 if (o.usItem == usItem)
1077 {
1078 SpecifyItemToEdit(&o, wi.sGridNo);
1079 CenterScreenAtMapIndex( gsItemGridNo );
1080 return; //success! (another item in same itempool)
1081 }
1082 gpItemPool = gpItemPool->pNext;
1083 }
1084 curr = curr->next;
1085 break;
1086 }
1087 curr = curr->next;
1088 }
1089 while( curr )
1090 { //search to the end of the list
1091 gpItemPool = GetItemPool(curr->sGridNo, 0);
1092 while( gpItemPool )
1093 {
1094 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1095 OBJECTTYPE& o = wi.o;
1096 if (o.usItem == usItem)
1097 {
1098 SpecifyItemToEdit(&o, wi.sGridNo);
1099 CenterScreenAtMapIndex( gsItemGridNo );
1100 return; //success! (found another item before reaching the end of the list)
1101 }
1102 gpItemPool = gpItemPool->pNext;
1103 }
1104 curr = curr->next;
1105 }
1106 }
1107 curr = pIPHead;
1108 while( curr )
1109 { //search to the end of the list
1110 gpItemPool = GetItemPool(curr->sGridNo, 0);
1111 while( gpItemPool )
1112 {
1113 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1114 OBJECTTYPE& o = wi.o;
1115 if (o.usItem == usItem)
1116 {
1117 SpecifyItemToEdit(&o, wi.sGridNo);
1118 CenterScreenAtMapIndex( gsItemGridNo );
1119 return; //success! (found first item in the list)
1120 }
1121 gpItemPool = gpItemPool->pNext;
1122 }
1123 curr = curr->next;
1124 }
1125 }
1126
1127
SelectNextKeyOfType(UINT8 ubKeyID)1128 static void SelectNextKeyOfType(UINT8 ubKeyID)
1129 {
1130 IPListNode *curr;
1131 if( gpItemPool )
1132 {
1133 curr = pIPHead;
1134 while( curr )
1135 { //skip quickly to the same gridno as the item pool
1136 if (curr->sGridNo == GetWorldItem(gpItemPool->iItemIndex).sGridNo)
1137 {
1138 gpItemPool = gpItemPool->pNext;
1139 while( gpItemPool )
1140 {
1141 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1142 OBJECTTYPE& o = wi.o;
1143 if (GCM->getItem(o.usItem)->isKey() && o.ubKeyID == ubKeyID)
1144 {
1145 SpecifyItemToEdit(&o, wi.sGridNo);
1146 CenterScreenAtMapIndex( gsItemGridNo );
1147 return; //success! (another item in same itempool)
1148 }
1149 gpItemPool = gpItemPool->pNext;
1150 }
1151 curr = curr->next;
1152 break;
1153 }
1154 curr = curr->next;
1155 }
1156 while( curr )
1157 { //search to the end of the list
1158 gpItemPool = GetItemPool(curr->sGridNo, 0);
1159 while( gpItemPool )
1160 {
1161 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1162 OBJECTTYPE& o = wi.o;
1163 if (GCM->getItem(o.usItem)->isKey() && o.ubKeyID == ubKeyID)
1164 {
1165 SpecifyItemToEdit(&o, wi.sGridNo);
1166 CenterScreenAtMapIndex( gsItemGridNo );
1167 return; //success! (found another item before reaching the end of the list)
1168 }
1169 gpItemPool = gpItemPool->pNext;
1170 }
1171 curr = curr->next;
1172 }
1173 }
1174 curr = pIPHead;
1175 while( curr )
1176 { //search to the end of the list
1177 gpItemPool = GetItemPool(curr->sGridNo, 0);
1178 while( gpItemPool )
1179 {
1180 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1181 OBJECTTYPE& o = wi.o;
1182 if (GCM->getItem(o.usItem)->isKey() && o.ubKeyID == ubKeyID)
1183 {
1184 SpecifyItemToEdit(&o, wi.sGridNo);
1185 CenterScreenAtMapIndex( gsItemGridNo );
1186 return; //success! (found first item in the list)
1187 }
1188 gpItemPool = gpItemPool->pNext;
1189 }
1190 curr = curr->next;
1191 }
1192 }
1193
1194
SelectNextTriggerWithFrequency(UINT16 usItem,INT8 bFrequency)1195 static void SelectNextTriggerWithFrequency(UINT16 usItem, INT8 bFrequency)
1196 {
1197 IPListNode *curr;
1198 if( gpItemPool )
1199 {
1200 curr = pIPHead;
1201 while( curr )
1202 { //skip quickly to the same gridno as the item pool
1203 if (curr->sGridNo == GetWorldItem(gpItemPool->iItemIndex).sGridNo)
1204 {
1205 gpItemPool = gpItemPool->pNext;
1206 while( gpItemPool )
1207 {
1208 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1209 OBJECTTYPE& o = wi.o;
1210 if (o.usItem == usItem && o.bFrequency == bFrequency)
1211 {
1212 SpecifyItemToEdit(&o, wi.sGridNo);
1213 CenterScreenAtMapIndex( gsItemGridNo );
1214 return; //success! (another item in same itempool)
1215 }
1216 gpItemPool = gpItemPool->pNext;
1217 }
1218 curr = curr->next;
1219 break;
1220 }
1221 curr = curr->next;
1222 }
1223 while( curr )
1224 { //search to the end of the list
1225 gpItemPool = GetItemPool(curr->sGridNo, 0);
1226 while( gpItemPool )
1227 {
1228 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1229 OBJECTTYPE& o = wi.o;
1230 if (o.usItem == usItem && o.bFrequency == bFrequency)
1231 {
1232 SpecifyItemToEdit(&o, wi.sGridNo);
1233 CenterScreenAtMapIndex( gsItemGridNo );
1234 return; //success! (found another item before reaching the end of the list)
1235 }
1236 gpItemPool = gpItemPool->pNext;
1237 }
1238 curr = curr->next;
1239 }
1240 }
1241 curr = pIPHead;
1242 while( curr )
1243 { //search to the end of the list
1244 gpItemPool = GetItemPool(curr->sGridNo, 0);
1245 while( gpItemPool )
1246 {
1247 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1248 OBJECTTYPE& o = wi.o;
1249 if (o.usItem == usItem && o.bFrequency == bFrequency)
1250 {
1251 SpecifyItemToEdit(&o, wi.sGridNo);
1252 CenterScreenAtMapIndex( gsItemGridNo );
1253 return; //success! (found first item in the list)
1254 }
1255 gpItemPool = gpItemPool->pNext;
1256 }
1257 curr = curr->next;
1258 }
1259 }
1260
1261
SelectNextPressureAction(void)1262 static void SelectNextPressureAction(void)
1263 {
1264 IPListNode *curr;
1265 if( gpItemPool )
1266 {
1267 curr = pIPHead;
1268 while( curr )
1269 { //skip quickly to the same gridno as the item pool
1270 if (curr->sGridNo == GetWorldItem(gpItemPool->iItemIndex).sGridNo)
1271 {
1272 gpItemPool = gpItemPool->pNext;
1273 while( gpItemPool )
1274 {
1275 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1276 OBJECTTYPE& o = wi.o;
1277 if (o.usItem == ACTION_ITEM && o.bDetonatorType == BOMB_PRESSURE)
1278 {
1279 SpecifyItemToEdit(&o, wi.sGridNo);
1280 CenterScreenAtMapIndex( gsItemGridNo );
1281 return; //success! (another item in same itempool)
1282 }
1283 gpItemPool = gpItemPool->pNext;
1284 }
1285 curr = curr->next;
1286 break;
1287 }
1288 curr = curr->next;
1289 }
1290 while( curr )
1291 { //search to the end of the list
1292 gpItemPool = GetItemPool(curr->sGridNo, 0);
1293 while( gpItemPool )
1294 {
1295 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1296 OBJECTTYPE& o = wi.o;
1297 if (o.usItem == ACTION_ITEM && o.bDetonatorType == BOMB_PRESSURE)
1298 {
1299 SpecifyItemToEdit(&o, wi.sGridNo);
1300 CenterScreenAtMapIndex( gsItemGridNo );
1301 return; //success! (found another item before reaching the end of the list)
1302 }
1303 gpItemPool = gpItemPool->pNext;
1304 }
1305 curr = curr->next;
1306 }
1307 }
1308 curr = pIPHead;
1309 while( curr )
1310 { //search to the end of the list
1311 gpItemPool = GetItemPool(curr->sGridNo, 0);
1312 while( gpItemPool )
1313 {
1314 WORLDITEM& wi = GetWorldItem(gpItemPool->iItemIndex);
1315 OBJECTTYPE& o = wi.o;
1316 if (o.usItem == ACTION_ITEM && o.bDetonatorType == BOMB_PRESSURE)
1317 {
1318 SpecifyItemToEdit(&o, wi.sGridNo);
1319 CenterScreenAtMapIndex( gsItemGridNo );
1320 return; //success! (found first item in the list)
1321 }
1322 gpItemPool = gpItemPool->pNext;
1323 }
1324 curr = curr->next;
1325 }
1326 }
1327
1328
CountNumberOfItemPlacementsInWorld(UINT16 usItem,UINT16 * pusQuantity)1329 static UINT16 CountNumberOfItemPlacementsInWorld(UINT16 usItem, UINT16* pusQuantity)
1330 {
1331 IPListNode *pIPCurr;
1332 INT16 num = 0;
1333 *pusQuantity = 0;
1334 pIPCurr = pIPHead;
1335 while( pIPCurr )
1336 {
1337 const ITEM_POOL* pItemPool = GetItemPool(pIPCurr->sGridNo, 0);
1338 while( pItemPool )
1339 {
1340 OBJECTTYPE const& o = GetWorldItem(pItemPool->iItemIndex).o;
1341 if (o.usItem == usItem)
1342 {
1343 num++;
1344 *pusQuantity += o.ubNumberOfObjects;
1345 }
1346 pItemPool = pItemPool->pNext;
1347 }
1348 pIPCurr = pIPCurr->next;
1349 }
1350 return num;
1351 }
1352
1353
CountNumberOfItemsWithFrequency(UINT16 usItem,INT8 bFrequency)1354 static UINT16 CountNumberOfItemsWithFrequency(UINT16 usItem, INT8 bFrequency)
1355 {
1356 IPListNode *pIPCurr;
1357 UINT16 num = 0;
1358 pIPCurr = pIPHead;
1359 while( pIPCurr )
1360 {
1361 const ITEM_POOL* pItemPool = GetItemPool(pIPCurr->sGridNo, 0);
1362 while( pItemPool )
1363 {
1364 OBJECTTYPE const& o = GetWorldItem(pItemPool->iItemIndex).o;
1365 if (o.usItem == usItem && o.bFrequency == bFrequency)
1366 {
1367 num++;
1368 }
1369 pItemPool = pItemPool->pNext;
1370 }
1371 pIPCurr = pIPCurr->next;
1372 }
1373 return num;
1374 }
1375
1376
CountNumberOfPressureActionsInWorld(void)1377 static UINT16 CountNumberOfPressureActionsInWorld(void)
1378 {
1379 IPListNode *pIPCurr;
1380 UINT16 num = 0;
1381 pIPCurr = pIPHead;
1382 while( pIPCurr )
1383 {
1384 const ITEM_POOL* pItemPool = GetItemPool(pIPCurr->sGridNo, 0);
1385 while( pItemPool )
1386 {
1387 OBJECTTYPE const& o = GetWorldItem(pItemPool->iItemIndex).o;
1388 if (o.usItem == ACTION_ITEM && o.bDetonatorType == BOMB_PRESSURE)
1389 {
1390 num++;
1391 }
1392 pItemPool = pItemPool->pNext;
1393 }
1394 pIPCurr = pIPCurr->next;
1395 }
1396 return num;
1397 }
1398
1399
1400 static UINT16 CountNumberOfKeysOfTypeInWorld(UINT8 ubKeyID);
1401
1402
1403 //Simply counts the number of items in the world. This is used for display purposes.
CountNumberOfEditorPlacementsInWorld(UINT16 usEInfoIndex,UINT16 * pusQuantity)1404 static UINT16 CountNumberOfEditorPlacementsInWorld(UINT16 usEInfoIndex, UINT16* pusQuantity)
1405 {
1406 UINT16 usNumPlacements;
1407 if( eInfo.uiItemType == TBAR_MODE_ITEM_TRIGGERS )
1408 { //find identical items with same frequency
1409 if( usEInfoIndex < PRESSURE_ACTION_ID )
1410 {
1411 INT8 bFrequency = CalcItemFrequency(usEInfoIndex);
1412 usNumPlacements = CountNumberOfItemsWithFrequency( eInfo.pusItemIndex[usEInfoIndex], bFrequency );
1413 *pusQuantity = usNumPlacements;
1414 }
1415 else
1416 {
1417 usNumPlacements = CountNumberOfPressureActionsInWorld();
1418 *pusQuantity = usNumPlacements;
1419 }
1420 }
1421 else if( eInfo.uiItemType == TBAR_MODE_ITEM_KEYS )
1422 {
1423 usNumPlacements = CountNumberOfKeysOfTypeInWorld( (UINT8)usEInfoIndex );
1424 *pusQuantity = usNumPlacements;
1425 }
1426 else
1427 {
1428 usNumPlacements = CountNumberOfItemPlacementsInWorld( eInfo.pusItemIndex[ usEInfoIndex], pusQuantity );
1429 }
1430 return usNumPlacements;
1431 }
1432
1433
CountNumberOfKeysOfTypeInWorld(UINT8 ubKeyID)1434 static UINT16 CountNumberOfKeysOfTypeInWorld(UINT8 ubKeyID)
1435 {
1436 IPListNode *pIPCurr;
1437 INT16 num = 0;
1438 pIPCurr = pIPHead;
1439 while( pIPCurr )
1440 {
1441 const ITEM_POOL* pItemPool = GetItemPool(pIPCurr->sGridNo, 0);
1442 while( pItemPool )
1443 {
1444 OBJECTTYPE const& o = GetWorldItem(pItemPool->iItemIndex).o;
1445 if (GCM->getItem(o.usItem)->isKey() && o.ubKeyID == ubKeyID)
1446 {
1447 num++;
1448 }
1449 pItemPool = pItemPool->pNext;
1450 }
1451 pIPCurr = pIPCurr->next;
1452 }
1453 return num;
1454 }
1455
1456
DisplayItemStatistics()1457 void DisplayItemStatistics()
1458 {
1459 if (!eInfo.fActive) return;
1460 if (!eInfo.pusItemIndex) return;
1461
1462 // If there is nothing else currently highlited by the mouse, use the selected item.
1463 INT16 const highlited = eInfo.sHilitedItemIndex;
1464 INT16 const idx = highlited != -1 ? highlited : eInfo.sSelItemIndex;
1465 UINT8 const foreground = idx == highlited ? FONT_LTRED : FONT_YELLOW;
1466 ST::string item_name = ItemNames[eInfo.pusItemIndex[idx]];
1467 DisplayWrappedString(2, EDITOR_TASKBAR_POS_Y + 41, 97, 2, SMALLCOMPFONT, foreground, item_name, FONT_BLACK, CENTER_JUSTIFIED);
1468 }
1469