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