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