1 #include "Button_System.h"
2 #include "Directories.h"
3 #include "Font.h"
4 #include "HImage.h"
5 #include "Input.h"
6 #include "Overhead_Map.h"
7 #include "TileDef.h"
8 #include "VSurface.h"
9 #include "WorldDat.h"
10 #include "Random.h"
11 #include "SysUtil.h"
12 #include "Font_Control.h"
13 #include "SelectWin.h"
14 #include "EditorDefines.h"
15 #include "Editor_Taskbar_Utils.h"
16 #include "MemMan.h"
17 #include "VObject.h"
18 #include "VObject_Blitters.h"
19 #include "WorldDef.h"
20 #include "UILayout.h"
21 
22 #include <string_theory/format>
23 #include <string_theory/string>
24 
25 #include <stdexcept>
26 
27 
28 // defines for DisplaySpec.ubType
29 #define DISPLAY_TEXT    1
30 #define DISPLAY_GRAPHIC 2
31 
32 #define DISPLAY_ALL_OBJECTS 0xFFFF
33 
34 
35 struct DisplaySpec
36 {
37 	UINT8 ubType;
38 	union
39 	{
40 		struct
41 		{
42 			HVOBJECT hVObject;
43 			UINT16   usStart;
44 			UINT16   usEnd;
45 			UINT32   uiObjIndx;
46 		};
47 		struct
48 		{
49 			UINT16* pString;
50 		};
51 	};
52 };
53 
54 
55 struct DisplayList
56 {
57 	HVOBJECT     hObj;
58 	UINT16       uiIndex;
59 	INT16        iX;
60 	INT16        iY;
61 	INT16        iWidth;
62 	INT16        iHeight;
63 	UINT32       uiObjIndx;
64 	BOOLEAN      fChosen;
65 	DisplayList* pNext;
66 };
67 
68 
69 extern BOOLEAN fDontUseRandom;
70 
71 
72 static BOOLEAN gfRenderSquareArea = FALSE;
73 static INT16 iStartClickX;
74 static INT16 iStartClickY;
75 static INT16 iEndClickX;
76 static INT16 iEndClickY;
77 
78 
79 #define CANCEL_ICON		0
80 #define UP_ICON		1
81 #define DOWN_ICON		2
82 #define OK_ICON		3
83 
84 static INT32 iButtonIcons[4];
85 static GUIButtonRef iSelectWin;
86 static GUIButtonRef iCancelWin;
87 static GUIButtonRef iScrollUp;
88 static GUIButtonRef iScrollDown;
89 static GUIButtonRef iOkWin;
90 
91 BOOLEAN fAllDone=FALSE;
92 static BOOLEAN fButtonsPresent = FALSE;
93 
94 static SGPPoint SelWinSpacing;
95 static SGPBox   g_sel_win_box;
96 
97 //These definitions help define the start and end of the various wall indices.
98 //This needs to be maintained if the walls change.
99 #define WALL_LAST_WALL_OFFSET            30
100 #define WALL_FIRST_AFRAME_OFFSET         31
101 #define WALL_LAST_AFRAME_OFFSET          34
102 #define WALL_FIRST_WINDOW_OFFSET         35
103 #define WALL_LAST_WINDOW_OFFSET          46
104 #define WALL_FIRST_BROKEN_WALL_OFFSET    47
105 #define WALL_LAST_BROKEN_WALL_OFFSET     54
106 #define WALL_FIRST_APPENDED_WALL_OFFSET  55
107 #define WALL_LAST_APPENDED_WALL_OFFSET   56
108 #define WALL_FIRST_WEATHERED_WALL_OFFSET 57
109 #define WALL_LAST_WEATHERED_WALL_OFFSET  64
110 
111 //I've added these definitions to add readability, and minimize conversion time for changes
112 //incase there are new values, etc.
113 #define OSTRUCTS_NUMELEMENTS				( LASTOSTRUCT - FIRSTFULLSTRUCT + 22 )
114 #define OSTRUCTS1_NUMELEMENTS				5
115 #define OSTRUCTS2_NUMELEMENTS				12
116 #define BANKSLIST_NUMELEMENTS				5
117 #define ROADSLIST_NUMELEMENTS				1
118 #define DEBRISLIST_NUMELEMENTS				( LASTDEBRIS - DEBRISROCKS + 2 + 1 ) //+1 for ANOTHERDEBRIS
119 
120 #define SINGLEWALL_NUMELEMENTS				( (LASTWALL-FIRSTWALL+1) * 2 )
121 #define SINGLEDOOR_NUMELEMENTS				(( LASTDOOR - FIRSTDOOR + 1 ) * 5)
122 #define SINGLEWINDOW_NUMELEMENTS			( LASTWALL - FIRSTWALL + 1 )
123 #define SINGLEROOF_NUMELEMENTS				( (LASTROOF-FIRSTROOF+1) + (LASTSLANTROOF-FIRSTSLANTROOF+1) + \
124 								(LASTWALL-FIRSTWALL+1) + (SECONDONROOF-FIRSTONROOF+1) )
125 #define SINGLENEWROOF_NUMELEMENTS			(LASTROOF-FIRSTROOF+1)
126 #define SINGLEBROKENWALL_NUMELEMENTS			( (LASTDECORATIONS-FIRSTDECORATIONS+1) + (LASTWALL-FIRSTWALL+1)*2 )
127 #define SINGLEDECOR_NUMELEMENTS			( LASTISTRUCT - FIRSTISTRUCT + 1 )
128 #define SINGLEDECAL_NUMELEMENTS			( LASTWALLDECAL - FIRSTWALLDECAL + EIGTHWALLDECAL - FIFTHWALLDECAL + 3 )
129 #define SINGLEFLOOR_NUMELEMENTS			( LASTFLOOR - FIRSTFLOOR + 1 )
130 #define SINGLETOILET_NUMELEMENTS			( EIGHTISTRUCT - FIFTHISTRUCT + 1 )
131 //	the final 2 used to be (LASTSLANTROOF-FIRSTSLANTROOF+1)
132 #define ROOM_NUMELEMENTS				( (LASTWALL-FIRSTWALL+1) + (LASTFLOOR-FIRSTFLOOR+1) + \
133 								(LASTROOF-FIRSTROOF+1) + (2 ))
134 
135 
136 //This is a special case for trees which may have varying numbers.  There was a problem
137 //in which we loaded a new tileset which had one less tree in it.  When we called BuildSelectionWindow(),
138 //it would crash because it thought there was an extra tree which was now invalid.
139 static UINT16 gusNumOStructs = 0;
140 
141 // List of objects to display in the selection window
142 static DisplaySpec OStructs[OSTRUCTS_NUMELEMENTS];
143 static DisplaySpec OStructs1[OSTRUCTS1_NUMELEMENTS];
144 static DisplaySpec OStructs2[OSTRUCTS2_NUMELEMENTS];
145 static DisplaySpec BanksList[BANKSLIST_NUMELEMENTS];
146 static DisplaySpec RoadsList[ROADSLIST_NUMELEMENTS];
147 static DisplaySpec DebrisList[DEBRISLIST_NUMELEMENTS];
148 static DisplaySpec SingleWall[SINGLEWALL_NUMELEMENTS];
149 static DisplaySpec SingleDoor[SINGLEDOOR_NUMELEMENTS];
150 static DisplaySpec SingleWindow[SINGLEWINDOW_NUMELEMENTS];
151 static DisplaySpec SingleRoof[SINGLEROOF_NUMELEMENTS];
152 static DisplaySpec SingleNewRoof[SINGLENEWROOF_NUMELEMENTS];
153 static DisplaySpec SingleBrokenWall[SINGLEBROKENWALL_NUMELEMENTS];
154 static DisplaySpec SingleDecor[SINGLEDECOR_NUMELEMENTS];
155 static DisplaySpec SingleDecal[SINGLEDECAL_NUMELEMENTS];
156 static DisplaySpec SingleFloor[SINGLEFLOOR_NUMELEMENTS];
157 static DisplaySpec SingleToilet[SINGLETOILET_NUMELEMENTS];
158 static DisplaySpec Room[ROOM_NUMELEMENTS];
159 
160 //These are all of the different selection lists.  Changing the max_selections will
161 //change the number of selections values you can have at a time.  This is Bret's gay code,
162 //though I've cleaned it up a lot.
163 Selections	SelOStructs[MAX_SELECTIONS]				=		{ {FIRSTFULLSTRUCT, 0, 1} };	// Default selections
164 Selections	SelOStructs1[MAX_SELECTIONS]				=		{ {FOURTHOSTRUCT, 0, 1} };	// Default selections
165 Selections	SelOStructs2[MAX_SELECTIONS]				=		{ {THIRDOSTRUCT, 0, 1} };	// Default selections
166 Selections	SelBanks[MAX_SELECTIONS]					=		{ {FIRSTCLIFF, 0, 1} };
167 Selections	SelRoads[MAX_SELECTIONS]					=		{ {FIRSTROAD, 0, 1} };
168 Selections	SelDebris[MAX_SELECTIONS]					=		{ {DEBRISROCKS, 0, 1} };
169 Selections	SelSingleWall[MAX_SELECTIONS]			=		{ {FIRSTWALL, 0, 1} };
170 Selections	SelSingleDoor[MAX_SELECTIONS]			=		{ {FIRSTDOOR, 0, 1} };
171 Selections	SelSingleWindow[MAX_SELECTIONS]		=		{ {FIRSTWALL, 44, 1} };
172 Selections  SelSingleRoof[MAX_SELECTIONS] =				{ {FIRSTROOF, 0, 1} };
173 Selections  SelSingleNewRoof[MAX_SELECTIONS] =		{ {FIRSTROOF, 0, 1} };
174 Selections  SelSingleBrokenWall[MAX_SELECTIONS] = { {FIRSTDECORATIONS, 0, 1} };
175 Selections	SelSingleDecor[MAX_SELECTIONS]=				{ {FIRSTISTRUCT, 0, 1} };
176 Selections	SelSingleDecal[MAX_SELECTIONS]	=		{ {FIRSTWALLDECAL, 0, 1} };
177 Selections  SelSingleFloor[MAX_SELECTIONS] =			{ {FIRSTFLOOR, 0, 1} };
178 Selections  SelSingleToilet[MAX_SELECTIONS] =			{ {FIFTHISTRUCT, 0, 1} };
179 Selections	SelRoom[MAX_SELECTIONS]			=					{ {FIRSTWALL, 0, 1} };
180 
181 // Number of objects currently in the selection list
182 INT32				iNumOStructsSelected = 1;
183 INT32				iNumOStructs1Selected = 1;
184 INT32				iNumOStructs2Selected = 1;
185 INT32				iNumBanksSelected = 1;
186 INT32				iNumRoadsSelected = 1;
187 INT32				iNumDebrisSelected = 1;
188 
189 INT32				iNumWallsSelected = 1;
190 INT32				iNumDoorsSelected = 1;
191 INT32				iNumWindowsSelected = 1;
192 INT32				iNumDecorSelected = 1;
193 INT32				iNumDecalsSelected = 1;
194 INT32				iNumBrokenWallsSelected = 1;
195 INT32				iNumFloorsSelected = 1;
196 INT32       iNumToiletsSelected = 1;
197 INT32				iNumRoofsSelected = 1;
198 INT32				iNumNewRoofsSelected = 1;
199 INT32				iNumRoomsSelected = 1;
200 
201 // Holds the previous selection list when a selection window is up. Used for canceling the selection window
202 static Selections OldSelList[MAX_SELECTIONS];
203 static INT32      iOldNumSelList;
204 
205 // Global pointers for selection list
206 Selections  *pSelList;
207 INT32				*pNumSelList;
208 
209 // Global used to indicate which selection to use (changes with the PGUP/PGDWN keys in editor)
210 INT32				iCurBank = 0;
211 
212 static DisplayList* pDispList;
213 static INT16 iTopWinCutOff;
214 
215 static UINT16 const SelWinFillColor        = 0x0000; // Black
216 static UINT16 const SelWinHilightFillColor = 0x000D; // A kind of medium dark blue
217 
218 
219 static BOOLEAN BuildDisplayWindow(DisplaySpec const*, UINT16 usNumSpecs, DisplayList** pDisplayList, SGPBox const* area, SGPPoint const* pSpacing);
220 static void CnclClkCallback(GUI_BUTTON* button, INT32 reason);
221 static void DwnClkCallback(GUI_BUTTON* button, INT32 reason);
222 static void OkClkCallback(GUI_BUTTON* button, INT32 reason);
223 static void SelWinClkCallback(GUI_BUTTON* button, INT32 reason);
224 static void UpClkCallback(GUI_BUTTON* button, INT32 reason);
225 
226 
MakeButton(UINT idx,const char * gfx,INT16 y,INT16 h,GUI_CALLBACK click,const ST::string & help)227 static GUIButtonRef MakeButton(UINT idx, const char* gfx, INT16 y, INT16 h, GUI_CALLBACK click, const ST::string& help)
228 {
229 	INT32 img = LoadGenericButtonIcon(gfx);
230 	iButtonIcons[idx] = img;
231 	GUIButtonRef const btn = CreateIconButton(img, 0, SCREEN_WIDTH - 40, y, 40, h, MSYS_PRIORITY_HIGH, click);
232 	btn->SetFastHelpText(help);
233 	return btn;
234 }
235 
236 
237 // Create a selection window of the given type.
CreateJA2SelectionWindow(SelectWindow const sWhat)238 void CreateJA2SelectionWindow(SelectWindow const sWhat)
239 {
240 	DisplaySpec *pDSpec; // XXX HACK000E
241 	UINT16 usNSpecs;
242 
243 	fAllDone = FALSE;
244 
245 	DisableEditorTaskbar( );
246 
247 	iSelectWin = CreateHotSpot(0, 0, SCREEN_WIDTH - 40, TASKBAR_Y, MSYS_PRIORITY_HIGH, SelWinClkCallback);
248 
249 	INT16 y;
250 	iOkWin      = MakeButton(OK_ICON,     EDITORDIR "/checkmark.sti",   y  =  0, 40, OkClkCallback,   "Accept selections");
251 	iCancelWin  = MakeButton(CANCEL_ICON, EDITORDIR "/bigx.sti",        y += 40, 40, CnclClkCallback, "Cancel selections");
252 	INT16 const h = (TASKBAR_Y - 40 - 40) / 2;
253 	iScrollUp   = MakeButton(UP_ICON,     EDITORDIR "/lguparrow.sti",   y += 40,  h, UpClkCallback,   "Scroll window up");
254 	iScrollDown = MakeButton(DOWN_ICON,   EDITORDIR "/lgdownarrow.sti", y += h,   h, DwnClkCallback,  "Scroll window down");
255 
256 	fButtonsPresent = TRUE;
257 
258 	SelWinSpacing.iX = 2;
259 	SelWinSpacing.iY = 2;
260 
261 	iTopWinCutOff = 15;
262 
263 	g_sel_win_box.x =  1;
264 	g_sel_win_box.y = 15;
265 	g_sel_win_box.w = SCREEN_WIDTH - g_sel_win_box.x - 40;
266 	g_sel_win_box.h = TASKBAR_Y    - g_sel_win_box.y;
267 
268 	switch( sWhat )
269 	{
270 		case SELWIN_OSTRUCTS:
271 			pDSpec = OStructs;
272 			usNSpecs = gusNumOStructs;//OSTRUCTS_NUMELEMENTS;
273 			pSelList = SelOStructs;
274 			pNumSelList = &iNumOStructsSelected;
275 			break;
276 
277 		case SELWIN_OSTRUCTS1:
278 			pDSpec = OStructs1;
279 			usNSpecs = OSTRUCTS1_NUMELEMENTS;
280 			pSelList = SelOStructs1;
281 			pNumSelList = &iNumOStructs1Selected;
282 			break;
283 
284 		case SELWIN_OSTRUCTS2:
285 			pDSpec = OStructs2;
286 			usNSpecs = OSTRUCTS2_NUMELEMENTS;
287 			pSelList = SelOStructs2;
288 			pNumSelList = &iNumOStructs2Selected;
289 			break;
290 
291 		case SELWIN_BANKS:
292 			pDSpec = BanksList;
293 			usNSpecs = BANKSLIST_NUMELEMENTS;
294 			pSelList = SelBanks;
295 			pNumSelList = &iNumBanksSelected;
296 			break;
297 
298 		case SELWIN_ROADS:
299 			pDSpec = RoadsList;
300 			usNSpecs = ROADSLIST_NUMELEMENTS;
301 			pSelList = SelRoads;
302 			pNumSelList = &iNumRoadsSelected;
303 			break;
304 
305 		case SELWIN_DEBRIS:
306 			pDSpec = DebrisList;
307 			usNSpecs = DEBRISLIST_NUMELEMENTS;
308 			pSelList = SelDebris;
309 			pNumSelList = &iNumDebrisSelected;
310 			break;
311 
312 		case SELWIN_SINGLEWALL:
313 			pDSpec = SingleWall;
314 			usNSpecs = SINGLEWALL_NUMELEMENTS;
315 			pSelList = SelSingleWall;
316 			pNumSelList = &iNumWallsSelected;
317 			break;
318 		case SELWIN_SINGLEDOOR:
319 			pDSpec = SingleDoor;
320 			usNSpecs = SINGLEDOOR_NUMELEMENTS;
321 			pSelList = SelSingleDoor;
322 			pNumSelList = &iNumDoorsSelected;
323 			break;
324 		case SELWIN_SINGLEWINDOW:
325 			pDSpec = SingleWindow;
326 			usNSpecs = SINGLEWINDOW_NUMELEMENTS;
327 			pSelList = SelSingleWindow;
328 			pNumSelList = &iNumWindowsSelected;
329 			break;
330 		case SELWIN_SINGLEROOF:
331 			pDSpec = SingleRoof;
332 			usNSpecs = SINGLEROOF_NUMELEMENTS;
333 			pSelList = SelSingleRoof;
334 			pNumSelList = &iNumRoofsSelected;
335 			break;
336 		case SELWIN_SINGLENEWROOF:
337 			pDSpec = SingleNewRoof;
338 			usNSpecs = SINGLENEWROOF_NUMELEMENTS;
339 			pSelList = SelSingleNewRoof;
340 			pNumSelList = &iNumNewRoofsSelected;
341 			break;
342 		case SELWIN_SINGLEBROKENWALL:
343 			pDSpec = SingleBrokenWall;
344 			usNSpecs = SINGLEBROKENWALL_NUMELEMENTS;
345 			pSelList = SelSingleBrokenWall;
346 			pNumSelList = &iNumBrokenWallsSelected;
347 			break;
348 		case SELWIN_SINGLEDECOR:
349 			pDSpec = SingleDecor;
350 			usNSpecs = SINGLEDECOR_NUMELEMENTS;
351 			pSelList = SelSingleDecor;
352 			pNumSelList = &iNumDecorSelected;
353 			break;
354 		case SELWIN_SINGLEDECAL:
355 			pDSpec = SingleDecal;
356 			usNSpecs = SINGLEDECAL_NUMELEMENTS;
357 			pSelList = SelSingleDecal;
358 			pNumSelList = &iNumDecalsSelected;
359 			break;
360 		case SELWIN_SINGLEFLOOR:
361 			pDSpec = SingleFloor;
362 			usNSpecs = SINGLEFLOOR_NUMELEMENTS;
363 			pSelList = SelSingleFloor;
364 			pNumSelList = &iNumFloorsSelected;
365 			break;
366 		case SELWIN_SINGLETOILET:
367 			pDSpec = SingleToilet;
368 			usNSpecs = SINGLETOILET_NUMELEMENTS;
369 			pSelList = SelSingleToilet;
370 			pNumSelList = &iNumToiletsSelected;
371 			break;
372 		case SELWIN_ROOM:
373 			pDSpec = Room;
374 			usNSpecs = ROOM_NUMELEMENTS;
375 			pSelList = SelRoom;
376 			pNumSelList = &iNumRoomsSelected;
377 			break;
378 
379 		default: abort(); // HACK000E
380 	}
381 
382 	BuildDisplayWindow(pDSpec, usNSpecs, &pDispList, &g_sel_win_box, &SelWinSpacing);
383 }
384 
385 
InitDisplayGfx(DisplaySpec * const ds,const HVOBJECT vo,const UINT16 start,const UINT16 end,const UINT32 obj_idx)386 static void InitDisplayGfx(DisplaySpec* const ds, const HVOBJECT vo, const UINT16 start, const UINT16 end, const UINT32 obj_idx)
387 {
388 	ds->ubType    = DISPLAY_GRAPHIC;
389 	ds->hVObject  = vo;
390 	ds->usStart   = start;
391 	ds->usEnd     = end;
392 	ds->uiObjIndx = obj_idx;
393 }
394 
395 
InitDisplayGfxFromTileData(DisplaySpec * const ds,const UINT16 start,const UINT16 end,const UINT32 obj_idx)396 static void InitDisplayGfxFromTileData(DisplaySpec* const ds, const UINT16 start, const UINT16 end, const UINT32 obj_idx)
397 {
398 	InitDisplayGfx(ds, TileElemFromTileType(obj_idx)->hTileSurface, start, end, obj_idx);
399 }
400 
401 
InitDisplayGfxAllFromTileData(DisplaySpec * const ds,const UINT32 obj_idx)402 static void InitDisplayGfxAllFromTileData(DisplaySpec* const ds, const UINT32 obj_idx)
403 {
404 	InitDisplayGfxFromTileData(ds, DISPLAY_ALL_OBJECTS, 0, obj_idx);
405 }
406 
407 
408 //The selection window method is initialized here.  This is where all the graphics for all
409 //the categories are organized and loaded.  If you wish to move things around, then this is
410 //where the initialization part is done.  I have also changed this from previously being loaded
411 //every single time you go into a selection window which was redundant and CPU consuming.
InitJA2SelectionWindow(void)412 void InitJA2SelectionWindow( void )
413 {
414 	INT32 iCount;
415 	INT32 iCount2;
416 	INT32 iCount3;
417 
418 	pDispList = NULL;
419 
420 	// Init the display spec lists for the types of selection windows
421 
422 	// Trees & bushes (The tree button in the "terrain" toolbar)
423 	for ( iCount3 = 0, iCount = 0; iCount < (LASTOSTRUCT - FIRSTFULLSTRUCT + 1); iCount++ )
424 	{
425 		const HVOBJECT hVObject = TileElemFromTileType(FIRSTFULLSTRUCT + iCount)->hTileSurface;
426 		UINT16 const usETRLEObjects = hVObject->SubregionCount();
427 
428 		for ( iCount2 = 0; iCount2 < usETRLEObjects; iCount2 += 3, iCount3++)
429 		{
430 			InitDisplayGfx(&OStructs[iCount3], hVObject, iCount2, iCount2, FIRSTFULLSTRUCT + iCount);
431 		}
432 	}
433 
434 	InitDisplayGfxAllFromTileData(&OStructs[iCount3], SIXTHOSTRUCT);
435 
436 	gusNumOStructs = (UINT16)iCount3 + 1;
437 
438 	// Rocks & barrels! (the "1" button in the "terrain" toolbar)
439 	InitDisplayGfxAllFromTileData(&OStructs1[0], FOURTHOSTRUCT);
440 
441 	for ( iCount = 0; iCount < (THIRDOSTRUCT - FIRSTOSTRUCT); iCount++ )
442 	{
443 		InitDisplayGfxAllFromTileData(&OStructs1[iCount +  1], FIRSTOSTRUCT + iCount);
444 	}
445 
446 	// Other junk! (the "2" button in the "terrain" toolbar)
447 	InitDisplayGfxAllFromTileData(&OStructs2[ 0], THIRDOSTRUCT);
448 	InitDisplayGfxAllFromTileData(&OStructs2[ 1], FIFTHOSTRUCT);
449 	InitDisplayGfxAllFromTileData(&OStructs2[ 2], SEVENTHOSTRUCT);
450 	InitDisplayGfxAllFromTileData(&OStructs2[ 3], EIGHTOSTRUCT);
451 	InitDisplayGfxAllFromTileData(&OStructs2[ 4], FIRSTVEHICLE);
452 	InitDisplayGfxAllFromTileData(&OStructs2[ 5], SECONDVEHICLE);
453 	InitDisplayGfxAllFromTileData(&OStructs2[ 6], FIRSTDEBRISSTRUCT);
454 	InitDisplayGfxAllFromTileData(&OStructs2[ 7], SECONDDEBRISSTRUCT);
455 	InitDisplayGfxAllFromTileData(&OStructs2[ 8], FIRSTLARGEEXPDEBRIS);
456 	InitDisplayGfxAllFromTileData(&OStructs2[ 9], SECONDLARGEEXPDEBRIS);
457 	InitDisplayGfxAllFromTileData(&OStructs2[10], NINTHOSTRUCT);
458 	InitDisplayGfxAllFromTileData(&OStructs2[11], TENTHOSTRUCT);
459 
460 	// River banks and cliffs (the "river" button on the "terrain" toolbar)
461 	InitDisplayGfxAllFromTileData(&BanksList[0], ANIOSTRUCT);
462 	InitDisplayGfxAllFromTileData(&BanksList[1], FIRSTCLIFF);
463 	InitDisplayGfxAllFromTileData(&BanksList[2], FIRSTCLIFFHANG);
464 	InitDisplayGfxAllFromTileData(&BanksList[3], FIRSTROAD);
465 	InitDisplayGfxAllFromTileData(&BanksList[4], FENCESTRUCT);
466 
467 	InitDisplayGfxAllFromTileData(&RoadsList[0], FIRSTROAD);
468 
469 	// Debris (the "bent can" button on the "terrain", and "buildings" toolbars)
470 	for ( iCount = 0; iCount < (LASTDEBRIS - DEBRISROCKS + 1); iCount++ )
471 	{
472 		InitDisplayGfxAllFromTileData(&DebrisList[iCount], DEBRISROCKS + iCount);
473 	}
474 	// Add one more for new misc debris
475 	InitDisplayGfxAllFromTileData(&DebrisList[iCount++], DEBRIS2MISC);
476 	//Add yet another one...
477 	InitDisplayGfxAllFromTileData(&DebrisList[iCount],   ANOTHERDEBRIS);
478 
479 	// Rooms
480 	for ( iCount = 0; iCount < (LASTWALL - FIRSTWALL + 1); iCount++ )
481 	{
482 		InitDisplayGfxFromTileData(&Room[iCount], 0, 0, FIRSTWALL + iCount);
483 	}
484 	for ( iCount2 = 0; iCount2 < (LASTFLOOR - FIRSTFLOOR + 1); iCount2++, iCount++ )
485 	{
486 		InitDisplayGfxFromTileData(&Room[iCount], 0, 0, FIRSTFLOOR + iCount2);
487 	}
488 	for ( iCount2 = 0; iCount2 < (LASTROOF - FIRSTROOF + 1); iCount2++, iCount++ )
489 	{
490 		InitDisplayGfxFromTileData(&Room[iCount], 0, 0, FIRSTROOF + iCount2);
491 	}
492 	for ( iCount2 = 0; iCount2 < 2/*(LASTSLANTROOF - FIRSTSLANTROOF + 1)*/; iCount2++, iCount++ )
493 	{
494 		InitDisplayGfxFromTileData(&Room[iCount], 0, 0, FIRSTSLANTROOF + iCount2);
495 	}
496 
497 	//Walls
498 	for ( iCount = 0, iCount2 = 0; iCount < (LASTWALL - FIRSTWALL + 1); iCount++, iCount2+=2 )
499 	{
500 		InitDisplayGfxFromTileData(&SingleWall[iCount2],     0,                               WALL_LAST_WALL_OFFSET,          FIRSTWALL + iCount);
501 		//New appended walls
502 		InitDisplayGfxFromTileData(&SingleWall[iCount2 + 1], WALL_FIRST_APPENDED_WALL_OFFSET, WALL_LAST_APPENDED_WALL_OFFSET, FIRSTWALL + iCount);
503 	}
504 
505 	//Doors
506 	for ( iCount = 0, iCount2 = 0; iCount < (LASTDOOR - FIRSTDOOR + 1); iCount++, iCount2+=5)
507 	{
508 		//closed
509 		InitDisplayGfxFromTileData(&SingleDoor[iCount2],      0,  0, FIRSTDOOR + iCount);
510 		//open, closed
511 		InitDisplayGfxFromTileData(&SingleDoor[iCount2 + 1],  4,  5, FIRSTDOOR + iCount);
512 		//open, closed
513 		InitDisplayGfxFromTileData(&SingleDoor[iCount2 + 2],  9, 10, FIRSTDOOR + iCount);
514 		//open, closed
515 		InitDisplayGfxFromTileData(&SingleDoor[iCount2 + 3], 14, 15, FIRSTDOOR + iCount);
516 		//open
517 		InitDisplayGfxFromTileData(&SingleDoor[iCount2 + 4], 19, 19, FIRSTDOOR + iCount);
518 	}
519 	//Windows
520 	for ( iCount = 0; iCount < (LASTWALL - FIRSTWALL + 1); iCount++ )
521 	{
522 		InitDisplayGfxFromTileData(&SingleWindow[iCount], WALL_FIRST_WINDOW_OFFSET, WALL_LAST_WINDOW_OFFSET, FIRSTWALL + iCount);
523 	}
524 	//Roofs and slant roofs
525 	for ( iCount = 0; iCount < (LASTROOF - FIRSTROOF + 1); iCount++ )
526 	{	//Flat roofs
527 		InitDisplayGfxAllFromTileData(&SingleRoof[iCount], FIRSTROOF + iCount);
528 	}
529 	for ( iCount2 = 0; iCount2 < (LASTSLANTROOF - FIRSTSLANTROOF + 1); iCount2++, iCount++ )
530 	{	//Slanted roofs
531 		InitDisplayGfxAllFromTileData(&SingleRoof[iCount], FIRSTSLANTROOF + iCount2);
532 	}
533 	for( iCount2 = 0; iCount2 < (LASTWALL - FIRSTWALL + 1); iCount2++, iCount++ )
534 	{	//A-Frames
535 		InitDisplayGfxFromTileData(&SingleRoof[iCount], WALL_FIRST_AFRAME_OFFSET, WALL_LAST_AFRAME_OFFSET, FIRSTWALL + iCount2);
536 	}
537 	for( iCount2 = 0; iCount2 < (SECONDONROOF - FIRSTONROOF + 1); iCount2++, iCount++ )
538 	{	//On roofs
539 		InitDisplayGfxAllFromTileData(&SingleRoof[iCount], FIRSTONROOF + iCount2);
540 	}
541 
542 	//New replacement roofs
543 	for ( iCount = 0; iCount < (LASTROOF - FIRSTROOF + 1); iCount++ )
544 	{	//Flat roofs
545 		InitDisplayGfxFromTileData(&SingleNewRoof[iCount], 9, 9, FIRSTROOF + iCount);
546 	}
547 
548 	//Broken walls
549 	for ( iCount = 0; iCount < (LASTDECORATIONS - FIRSTDECORATIONS + 1); iCount++ )
550 	{	//Old obsolete wall decals, but should be replaced with multitiled decals such as banners, etc.
551 		InitDisplayGfxAllFromTileData(&SingleBrokenWall[iCount], FIRSTDECORATIONS + iCount);
552 	}
553 	for( iCount2 = 0; iCount2 < (LASTWALL - FIRSTWALL + 1); iCount2++, iCount++ )
554 	{	//Broken walls
555 		InitDisplayGfxFromTileData(&SingleBrokenWall[iCount], WALL_FIRST_BROKEN_WALL_OFFSET, WALL_LAST_BROKEN_WALL_OFFSET, FIRSTWALL + iCount2);
556 	}
557 	for( iCount2 = 0; iCount2 < (LASTWALL - FIRSTWALL + 1); iCount2++, iCount++ )
558 	{	//Cracked and smudged walls
559 		InitDisplayGfxFromTileData(&SingleBrokenWall[iCount], WALL_FIRST_WEATHERED_WALL_OFFSET, WALL_LAST_WEATHERED_WALL_OFFSET, FIRSTWALL + iCount2);
560 	}
561 
562 	// Decorations
563 	for ( iCount = 0; iCount < (LASTISTRUCT - FIRSTISTRUCT + 1); iCount++ )
564 	{
565 		InitDisplayGfxAllFromTileData(&SingleDecor[iCount], FIRSTISTRUCT + iCount);
566 	}
567 
568 	// Wall decals
569 	for ( iCount = 0; iCount < (LASTWALLDECAL - FIRSTWALLDECAL + 1); iCount++ )
570 	{
571 		InitDisplayGfxAllFromTileData(&SingleDecal[iCount], FIRSTWALLDECAL + iCount);
572 	}
573 	for ( iCount2 = 0; iCount2 < (EIGTHWALLDECAL - FIFTHWALLDECAL + 1); iCount++, iCount2++ )
574 	{
575 		InitDisplayGfxAllFromTileData(&SingleDecal[iCount], FIFTHWALLDECAL + iCount2);
576 	}
577 	InitDisplayGfxAllFromTileData(&SingleDecal[iCount], FIRSTSWITCHES);
578 
579 	//Floors
580 	for ( iCount = 0; iCount < (LASTFLOOR - FIRSTFLOOR + 1); iCount++ )
581 	{
582 		InitDisplayGfxFromTileData(&SingleFloor[iCount], 0, 7, FIRSTFLOOR + iCount);
583 	}
584 
585 	//Toilets
586 	for ( iCount = 0; iCount < (EIGHTISTRUCT - FIFTHISTRUCT + 1); iCount++ )
587 	{
588 		InitDisplayGfxAllFromTileData(&SingleToilet[iCount], FIFTHISTRUCT + iCount);
589 	}
590 }
591 
592 
593 static DisplayList* TrashList(DisplayList* pNode);
594 
595 
596 //----------------------------------------------------------------------------------------------
597 //	ShutdownJA2SelectionWindow
598 //
599 //	Unloads selection window button images and makes sure any display list that may remain in memory
600 //	is removed.
601 //
ShutdownJA2SelectionWindow(void)602 void ShutdownJA2SelectionWindow( void )
603 {
604 	INT16 x;
605 
606 	for (x = 0; x < 4; x++)
607 		UnloadGenericButtonIcon( (INT16)iButtonIcons[x] );
608 
609 	if (pDispList != NULL)
610 	{
611 		pDispList = TrashList(pDispList);
612 	}
613 	gfRenderWorld = TRUE;
614 }
615 
616 
617 //----------------------------------------------------------------------------------------------
618 //	RemoveJA2SelectionWindow
619 //
620 //	Removes the selection window from the screen.
621 //
RemoveJA2SelectionWindow(void)622 void RemoveJA2SelectionWindow( void )
623 {
624 	RemoveButton(iSelectWin);
625 	RemoveButton(iCancelWin);
626 	RemoveButton(iScrollUp);
627 	RemoveButton(iScrollDown);
628 	RemoveButton(iOkWin);
629 
630 	gfRenderSquareArea = FALSE;
631 
632 	if (pDispList != NULL)
633 	{
634 		pDispList = TrashList(pDispList);
635 	}
636 	gfRenderTaskbar = TRUE;
637 
638 	gfOverheadMapDirty = TRUE;
639 	EnableEditorTaskbar( );
640 }
641 
642 
643 //	Free the current display list for the selection window.
TrashList(DisplayList * pNode)644 static DisplayList* TrashList(DisplayList* pNode)
645 {
646 	if (pNode == NULL)
647 		return(NULL);
648 
649 	if (pNode->pNext != NULL)
650 		pNode->pNext = TrashList(pNode->pNext);
651 
652 	if (pNode->pNext == NULL)
653 		delete pNode;
654 
655 	return(NULL);
656 }
657 
658 
659 static void DrawSelections(void);
660 
661 
662 //----------------------------------------------------------------------------------------------
663 //	RenderSelectionWindow
664 //
665 //	Displays the current selection window
666 //
RenderSelectionWindow(void)667 void RenderSelectionWindow( void )
668 {
669 	INT32 iSX,iSY,iEX,iEY;
670 	UINT16 usFillColor;
671 	static UINT8 usFillGreen = 0;
672 	static UINT8 usDir = 5;
673 
674 
675 	if (!fButtonsPresent)
676 		return;
677 
678 	ColorFillVideoSurfaceArea(FRAME_BUFFER, 0, 0, SCREEN_WIDTH - 40, TASKBAR_Y, GetGenericButtonFillColor());
679 	DrawSelections( );
680 	MarkButtonsDirty();
681 	RenderButtons( );
682 
683 	if ( gfRenderSquareArea )
684 	{
685 		GUIButtonRef const button = iSelectWin;
686 		if (!button) return;
687 
688 		if (ABS(iStartClickX - button->MouseX()) > 9 ||
689 				ABS(iStartClickY - (button->MouseY() + iTopWinCutOff - (INT16)g_sel_win_box.y)) > 9)
690 		{
691 //			iSX = (INT32)iStartClickX;
692 //			iEX = (INT32)button->MouseX();
693 //			iSY = (INT32)iStartClickY;
694 //			iEY = (INT32)(button->MouseY() + iTopWinCutOff - (INT16)g_sel_win_box.y);
695 
696 			iSX = iStartClickX;
697 			iSY = iStartClickY - iTopWinCutOff + g_sel_win_box.y;
698 			iEX = gusMouseXPos;
699 			iEY = gusMouseYPos;
700 
701 
702 			if (iEX < iSX) Swap(iEX, iSX);
703 			if (iEY < iSY) Swap(iEY, iSY);
704 
705 			iEX = MIN( iEX, 600 );
706 			iSY = MAX(g_sel_win_box.y, iSY);
707 			iEY = MIN( 359, iEY );
708 			iEY = MAX(g_sel_win_box.y, iEY);
709 
710 			usFillColor = Get16BPPColor(FROMRGB(255, usFillGreen, 0));
711 			usFillGreen += usDir;
712 			if( usFillGreen > 250 )
713 				usDir = 251;
714 			else if( usFillGreen < 5 )
715 				usDir = 5;
716 
717 			ColorFillVideoSurfaceArea(FRAME_BUFFER, iSX, iSY, iEX, iSY+1, usFillColor );
718 			ColorFillVideoSurfaceArea(FRAME_BUFFER, iSX, iEY, iEX, iEY+1, usFillColor );
719 			ColorFillVideoSurfaceArea(FRAME_BUFFER, iSX, iSY, iSX+1, iEY, usFillColor );
720 			ColorFillVideoSurfaceArea(FRAME_BUFFER, iEX, iSY, iEX+1, iEY, usFillColor );
721 		}
722 	}
723 }
724 
725 
726 static void AddToSelectionList(DisplayList* pNode);
727 static BOOLEAN RemoveFromSelectionList(DisplayList* pNode);
728 
729 
730 //	Button callback function for the main selection window. Checks if user clicked on an image,
731 //	if so selects or de-selects that object. Also handles the multi-object selection (left-click
732 //	and drag to get the selection rectangle)
SelWinClkCallback(GUI_BUTTON * button,INT32 reason)733 static void SelWinClkCallback(GUI_BUTTON* button, INT32 reason)
734 {
735 	DisplayList *pNode;
736 	BOOLEAN fDone;
737 	INT16 iClickX,iClickY, iYInc, iXInc;
738 
739 	if (!button->Enabled()) return;
740 
741 	iClickX = button->MouseX();
742 	iClickY = button->MouseY() + iTopWinCutOff - (INT16)g_sel_win_box.y;
743 
744 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
745 	{
746 		button->uiFlags |= BUTTON_CLICKED_ON;
747 		iStartClickX = iClickX;
748 		iStartClickY = iClickY;
749 		gfRenderSquareArea = TRUE;
750 	}
751 	else if (reason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
752 	{
753 		button->uiFlags |= BUTTON_CLICKED_ON;
754 
755 		if ( gfRenderSquareArea )
756 		{
757 			gfRenderSquareArea = FALSE;
758 			return;
759 		}
760 
761 		// Code to figure out what image user wants goes here
762 		pNode = pDispList;
763 
764 		fDone = FALSE;
765 		while( (pNode != NULL) && !fDone )
766 		{
767 			if ( (iClickX >= pNode->iX) && (iClickX < (pNode->iX + pNode->iWidth)) &&
768 					(iClickY >= pNode->iY) && (iClickY < (pNode->iY + pNode->iHeight)) )
769 			{
770 				fDone = TRUE;
771 				if ( RemoveFromSelectionList(pNode) )
772 					pNode->fChosen = FALSE;
773 			}
774 			else
775 				pNode = pNode->pNext;
776 		}
777 	}
778 	else if (reason & MSYS_CALLBACK_REASON_RBUTTON_UP )
779 	{
780 		button->uiFlags &= (~BUTTON_CLICKED_ON);
781 	}
782 	else if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
783 	{
784 		button->uiFlags &= (~BUTTON_CLICKED_ON);
785 
786 		if( !gfRenderSquareArea )
787 			return;
788 
789 		iEndClickX = iClickX;
790 		iEndClickY = iClickY;
791 
792 		gfRenderSquareArea = FALSE;
793 
794 		if (iEndClickX < iStartClickX) Swap(iEndClickX, iStartClickX);
795 		if (iEndClickY < iStartClickY) Swap(iEndClickY, iStartClickY);
796 
797 		iXInc = iYInc = 1;
798 		for( iClickY = iStartClickY; iClickY <= iEndClickY; iClickY += iYInc )
799 		{
800 			iYInc = 1;
801 			for( iClickX = iStartClickX; iClickX <= iEndClickX; iClickX += iXInc )
802 			{
803 				iXInc = 1;
804 				// Code to figure out what image user wants goes here
805 				pNode = pDispList;
806 
807 				fDone = FALSE;
808 				while( (pNode != NULL) && !fDone )
809 				{
810 					if ( (iClickX >= pNode->iX) && (iClickX < (pNode->iX + pNode->iWidth)) &&
811 							(iClickY >= pNode->iY) && (iClickY < (pNode->iY + pNode->iHeight)) )
812 					{
813 						fDone = TRUE;
814 						AddToSelectionList(pNode);
815 						pNode->fChosen = TRUE;
816 						iXInc = (pNode->iX + pNode->iWidth) - iClickX;
817 						if ( iYInc < ((pNode->iY + pNode->iHeight) - iClickY) )
818 							iYInc = (pNode->iY + pNode->iHeight) - iClickY;
819 					}
820 					else
821 						pNode = pNode->pNext;
822 				}
823 			}
824 		}
825 	}
826 	else if (reason & MSYS_CALLBACK_REASON_WHEEL_UP)
827 	{
828 		ScrollSelWinUp();
829 	}
830 	else if (reason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
831 	{
832 		ScrollSelWinDown();
833 	}
834 }
835 
836 
837 /* When a selection window is up, the file information of the picture is
838  * displayed at the top of the screen. */
DisplaySelectionWindowGraphicalInformation()839 void DisplaySelectionWindowGraphicalInformation()
840 {
841 	UINT16      const  x = gusMouseXPos;
842 	UINT16      const  y = gusMouseYPos + iTopWinCutOff - (UINT16)g_sel_win_box.y;
843 	DisplayList const* i = pDispList;
844 	for (; i; i = i->pNext)
845 	{
846 		if (x < i->iX || i->iX + i->iWidth  <= x) continue;
847 		if (y < i->iY || i->iY + i->iHeight <= y) continue;
848 		break;
849 	}
850 	SetFont(FONT12POINT1);
851 	SetFontForeground(FONT_WHITE);
852 	if (i)
853 	{
854 		UINT32     const obj_idx  = i->uiObjIndx;
855 		const ST::string name     = gTileSurfaceName[obj_idx];
856 		auto res = GetAdjustedTilesetResource(giCurrentTilesetID, obj_idx);
857 		if (!res.isDefaultTileset())
858 		{
859 			MPrint(2, 2, ST::format("File:  {}, subindex:  {} ({})", res.resourceFileName, i->uiIndex, name));
860 		}
861 		else
862 		{
863 			TILESET const& generic = gTilesets[res.tilesetID];
864 			MPrint(2, 2, ST::format("{}[{}] is from default tileset {} ({})", res.resourceFileName, i->uiIndex, generic.zName, name));
865 		}
866 	}
867 	MPrint(350, 2, ST::format("Current Tileset:  {}", gTilesets[giCurrentTilesetID].zName));
868 }
869 
870 
871 //----------------------------------------------------------------------------------------------
872 //	AddToSelectionList
873 //
874 //	Add an object in the display list to the selection list. If the object already exists in the
875 //	selection list, then it's count is incremented.
876 //
AddToSelectionList(DisplayList * pNode)877 static void AddToSelectionList(DisplayList* pNode)
878 {
879 	for (INT32 iIndex = 0; iIndex < *pNumSelList; ++iIndex)
880 	{
881 		if ( pNode->uiObjIndx == pSelList[ iIndex ].uiObject &&
882 			pNode->uiIndex == pSelList[ iIndex ].usIndex )
883 		{
884 			// Was already in the list, so bump up the count
885 			++pSelList[iIndex].sCount;
886 			return;
887 		}
888 	}
889 
890 	// Wasn't in the list, so add to end (if space available)
891 	if ( (*pNumSelList) < MAX_SELECTIONS )
892 	{
893 		pSelList[ (*pNumSelList) ].uiObject = pNode->uiObjIndx;
894 		pSelList[ (*pNumSelList) ].usIndex = pNode->uiIndex;
895 		pSelList[ (*pNumSelList) ].sCount = 1;
896 
897 		(*pNumSelList)++;
898 	}
899 }
900 
901 
902 
903 //----------------------------------------------------------------------------------------------
904 //	ClearSelectionList
905 //
906 //	Removes everything from the current selection list
907 //
ClearSelectionList(void)908 BOOLEAN ClearSelectionList( void )
909 {
910 	INT32 iIndex;
911 	DisplayList *pNode;
912 
913 
914 	if (pNumSelList == NULL )
915 		return( FALSE );
916 
917 	pNode = pDispList;
918 	while ( pNode != NULL )
919 	{
920 		pNode->fChosen = FALSE;
921 		pNode = pNode->pNext;
922 	}
923 
924 	for (iIndex = 0; iIndex < (*pNumSelList); iIndex++ )
925 		pSelList[ iIndex ].sCount = 0;
926 
927 	(*pNumSelList) = 0;
928 	return( TRUE );
929 }
930 
931 
932 //	Removes the object given n a display list from the selection list. If the objects count is
933 //	greater than one, then the count is decremented and the object remains in the list.
RemoveFromSelectionList(DisplayList * pNode)934 static BOOLEAN RemoveFromSelectionList(DisplayList* pNode)
935 {
936 	// Abort if no entries in list (pretend we removed a node)
937 	if ( (*pNumSelList) <= 0 )
938 		return( TRUE );
939 
940 	for (INT32 iIndex = 0; iIndex < *pNumSelList; ++iIndex)
941 	{
942 		if ( pNode->uiObjIndx == pSelList[ iIndex ].uiObject &&
943 			pNode->uiIndex == pSelList[ iIndex ].usIndex )
944 		{
945 			if (--pSelList[iIndex].sCount <= 0)
946 			{
947 				// Squash the list to remove old entry
948 				for ( ; iIndex < ((*pNumSelList) - 1); iIndex++ )
949 					pSelList[ iIndex ] = pSelList[ iIndex + 1 ];
950 
951 				(*pNumSelList)--;
952 				return TRUE;
953 			}
954 			break;
955 		}
956 	}
957 
958 	return FALSE;
959 }
960 
961 
962 //----------------------------------------------------------------------------------------------
963 //	GetRandomSelection
964 //
965 //	Randomly selects an item in the selection list. The object counts are taken into account so
966 //	that objects with higher counts will be chosen more often.
967 //
GetRandomSelection(void)968 INT32 GetRandomSelection( void )
969 {
970 	INT32 iRandNum, iTotalCounts;
971 	INT32 iIndex, iSelectedIndex, iNextCount;
972 
973 	if ( fDontUseRandom )
974 	{
975 		fDontUseRandom = FALSE;
976 		return( iCurBank );
977 	}
978 
979 	iTotalCounts = 0;
980 	for (iIndex = 0; iIndex < (*pNumSelList); iIndex++ )
981 		iTotalCounts += (INT32)pSelList[ iIndex ].sCount;
982 
983 	iRandNum = Random( iTotalCounts );
984 
985 	iSelectedIndex = -1;
986 	iNextCount = 0;
987 	for (iIndex = 0; iIndex < (*pNumSelList) && iSelectedIndex == -1; iIndex++ )
988 	{
989 		iNextCount += (INT32)pSelList[ iIndex ].sCount;
990 		if ( iRandNum < iNextCount )
991 			iSelectedIndex = iIndex;
992 	}
993 
994 	return ( iSelectedIndex );
995 }
996 
997 
998 //	Verifies if a particular display list object exists in the current selection list.
IsInSelectionList(const DisplayList * pNode)999 static BOOLEAN IsInSelectionList(const DisplayList* pNode)
1000 {
1001 	for (INT32 iIndex = 0; iIndex < *pNumSelList; iIndex++)
1002 	{
1003 		if (pNode->uiObjIndx == pSelList[iIndex].uiObject &&
1004 				pNode->uiIndex   == pSelList[iIndex].usIndex)
1005 		{
1006 			return TRUE;
1007 		}
1008 	}
1009 
1010 	return FALSE;
1011 }
1012 
1013 
1014 /* Find an occurance of a particular display list object in the current
1015  * selection list. Returns the corresponding selection list entry. */
FindInSelectionList(DisplayList const & n)1016 static Selections const& FindInSelectionList(DisplayList const& n)
1017 {
1018 	Selections const* const end = pSelList + *pNumSelList;
1019 	for (Selections const* i = pSelList; i != end; ++i)
1020 	{
1021 		Selections const& sel = *i;
1022 		if (n.uiObjIndx != sel.uiObject) continue;
1023 		if (n.uiIndex   != sel.usIndex)  continue;
1024 		return sel;
1025 	}
1026 	throw std::logic_error("node not in selection list");
1027 }
1028 
1029 
1030 //	Copies the current selection list to a save buffer. Used in case we want to cancel a
1031 //	selection window.
SaveSelectionList(void)1032 static void SaveSelectionList(void)
1033 {
1034 	INT32 iIndex;
1035 
1036 	for (iIndex = 0; iIndex < MAX_SELECTIONS; iIndex++ )
1037 		OldSelList[ iIndex ] = pSelList[ iIndex ];
1038 
1039 	iOldNumSelList = (*pNumSelList);
1040 }
1041 
1042 
1043 //----------------------------------------------------------------------------------------------
1044 //	RestoreSelectionList
1045 //
1046 //	Copies the selection list in the save buffer back to the current selection list.
1047 //
RestoreSelectionList(void)1048 void RestoreSelectionList( void )
1049 {
1050 	INT32 iIndex;
1051 
1052 	for (iIndex = 0; iIndex < MAX_SELECTIONS; iIndex++ )
1053 		pSelList[ iIndex ] = OldSelList[ iIndex ];
1054 
1055 	(*pNumSelList) = iOldNumSelList;
1056 }
1057 
1058 
1059 //	Button callback function for the selection window's OK button
OkClkCallback(GUI_BUTTON * button,INT32 reason)1060 static void OkClkCallback(GUI_BUTTON* button, INT32 reason)
1061 {
1062 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1063 	{
1064 		fAllDone = TRUE;
1065 	}
1066 }
1067 
1068 
1069 //----------------------------------------------------------------------------------------------
1070 //	CnclClkCallback
1071 //
1072 //	Button callback function for the selection window's CANCEL button
1073 //
CnclClkCallback(GUI_BUTTON * button,INT32 reason)1074 static void CnclClkCallback(GUI_BUTTON* button, INT32 reason)
1075 {
1076 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1077 	{
1078 		fAllDone = TRUE;
1079 		RestoreSelectionList();
1080 	}
1081 }
1082 
1083 
1084 //	Button callback function for scrolling the selection window up
UpClkCallback(GUI_BUTTON * button,INT32 reason)1085 static void UpClkCallback(GUI_BUTTON* button, INT32 reason)
1086 {
1087 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP) ScrollSelWinUp();
1088 }
1089 
1090 
1091 /* Perform the calculations required to actually scroll a selection window up by
1092  * one line. */
ScrollSelWinUp(void)1093 void ScrollSelWinUp(void)
1094 {
1095 	INT16 iCutOff = iTopWinCutOff;
1096 	for (DisplayList* i = pDispList; i; i = i->pNext)
1097 	{
1098 		iCutOff = i->iY;
1099 		if (i->iY < iTopWinCutOff) break;
1100 	}
1101 	iTopWinCutOff = iCutOff;
1102 }
1103 
1104 
1105 /* Performs the actual calculations for scrolling a selection window down. */
ScrollSelWinDown(void)1106 void ScrollSelWinDown(void)
1107 {
1108 	INT16 iCutOff = iTopWinCutOff;
1109 	for (DisplayList* i = pDispList; i; i = i->pNext)
1110 	{
1111 		if (i->iY <= iTopWinCutOff) break;
1112 		iCutOff = i->iY;
1113 	}
1114 	iTopWinCutOff = iCutOff;
1115 }
1116 
1117 
1118 //	Button callback function to scroll the selection window down.
DwnClkCallback(GUI_BUTTON * button,INT32 reason)1119 static void DwnClkCallback(GUI_BUTTON* button, INT32 reason)
1120 {
1121 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP) ScrollSelWinDown();
1122 }
1123 
1124 
1125 static void DisplayWindowFunc(DisplayList*, INT16 top_cut_off, SGPBox const* area);
1126 
1127 
1128 //	Displays the objects in the display list to the selection window.
DrawSelections(void)1129 static void DrawSelections(void)
1130 {
1131 	SGPRect					ClipRect, NewRect;
1132 
1133 	NewRect.iLeft   = g_sel_win_box.x;
1134 	NewRect.iTop    = g_sel_win_box.y;
1135 	NewRect.iRight  = g_sel_win_box.x + g_sel_win_box.w - 1;
1136 	NewRect.iBottom = g_sel_win_box.y + g_sel_win_box.h - 1;
1137 
1138 	GetClippingRect(&ClipRect);
1139 	SetClippingRect(&NewRect);
1140 
1141 	SetFont( gpLargeFontType1 );
1142 	SetFontShade(LARGEFONT1, FONT_SHADE_GREY_165);
1143 
1144 	DisplayWindowFunc(pDispList, iTopWinCutOff, &g_sel_win_box);
1145 
1146 	SetFontShade(LARGEFONT1, FONT_SHADE_NEUTRAL);
1147 
1148 	SetClippingRect(&ClipRect);
1149 }
1150 
1151 
1152 /* Create a display list from a display specification list.  Also set variables
1153  * up for properly scrolling the window etc. */
BuildDisplayWindow(DisplaySpec const * const pDisplaySpecs,UINT16 const usNumSpecs,DisplayList ** const pDisplayList,SGPBox const * const area,SGPPoint const * const pSpacing)1154 static BOOLEAN BuildDisplayWindow(DisplaySpec const* const pDisplaySpecs, UINT16 const usNumSpecs, DisplayList** const pDisplayList, SGPBox const* const area, SGPPoint const* const pSpacing)
1155 try
1156 {
1157 	SaveSelectionList();
1158 
1159 	INT32  x     = area->x;
1160 	INT32  y     = area->y;
1161 	UINT16 max_h = 0; // Maximum height in current row
1162 	for (DisplaySpec const* ds = pDisplaySpecs; ds != pDisplaySpecs + usNumSpecs; ++ds)
1163 	{
1164 		if (ds->ubType != DISPLAY_GRAPHIC) continue;
1165 
1166 		SGPVObject* const vo = ds->hVObject;
1167 		if (!vo) return FALSE;
1168 
1169 		UINT16 usETRLEStart = ds->usStart;
1170 		UINT16 usETRLEEnd   = ds->usEnd;
1171 		if (usETRLEStart == DISPLAY_ALL_OBJECTS)
1172 		{
1173 			usETRLEStart = 0;
1174 			usETRLEEnd   = vo->SubregionCount() - 1;
1175 		}
1176 
1177 		if (usETRLEStart         >  usETRLEEnd) return FALSE;
1178 		if (vo->SubregionCount() <= usETRLEEnd) return FALSE;
1179 
1180 		for (UINT16 usETRLELoop = usETRLEStart; usETRLELoop <= usETRLEEnd; ++usETRLELoop)
1181 		{
1182 			ETRLEObject const& e = vo->SubregionProperties(usETRLELoop);
1183 
1184 			if (x + e.usWidth > area->x + area->w)
1185 			{
1186 				x  = area->x;
1187 				y += max_h + pSpacing->iY;
1188 				max_h = 0;
1189 			}
1190 
1191 			DisplayList* const n = new DisplayList{};
1192 			n->hObj      = vo;
1193 			n->uiIndex   = usETRLELoop;
1194 			n->iX        = x;
1195 			n->iY        = y;
1196 			n->iWidth    = e.usWidth;
1197 			n->iHeight   = e.usHeight;
1198 			n->pNext     = *pDisplayList;
1199 			n->uiObjIndx = ds->uiObjIndx;
1200 			n->fChosen   = IsInSelectionList(n);
1201 
1202 			*pDisplayList = n;
1203 
1204 			if (max_h < e.usHeight) max_h = e.usHeight;
1205 
1206 			x += e.usWidth + pSpacing->iX;
1207 		}
1208 	}
1209 
1210 	return TRUE;
1211 }
1212 catch (...) { return FALSE; }
1213 
1214 
1215 /* Blit the actual object images in the display list on the selection window.
1216  * The objects that have been selected (in the selection list) are highlighted
1217  * and the count placed in the upper left corner of the image. */
DisplayWindowFunc(DisplayList * const n,INT16 const top_cut_off,SGPBox const * const area)1218 static void DisplayWindowFunc(DisplayList* const n, INT16 const top_cut_off, SGPBox const* const area)
1219 {
1220 	if (!n)                  return;
1221 	if (n->iY < top_cut_off) return;
1222 
1223 	DisplayWindowFunc(n->pNext, top_cut_off, area);
1224 
1225 	INT16 const x = n->iX;
1226 	INT16 const y = n->iY + area->y - top_cut_off;
1227 	if (y > area->y + area->h) return;
1228 
1229 	UINT16 const fill_colour = n->fChosen ? SelWinHilightFillColor : SelWinFillColor;
1230 	ColorFillVideoSurfaceArea(FRAME_BUFFER, x, y, x + n->iWidth, y + n->iHeight, fill_colour);
1231 
1232 	SGPVObject* const  vo = n->hObj;
1233 	ETRLEObject const& e  = vo->SubregionProperties(n->uiIndex);
1234 	vo->CurrentShade(DEFAULT_SHADE_LEVEL);
1235 	BltVideoObject(FRAME_BUFFER, vo, n->uiIndex, x - e.sOffsetX, y - e.sOffsetY);
1236 
1237 	if (n->fChosen)
1238 	{
1239 		INT16 const count = FindInSelectionList(*n).sCount;
1240 		if (count != 0) GPrint(x, y, ST::format("{}", count));
1241 	}
1242 }
1243