1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5 
6 This file is part of NBlood.
7 
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include "build.h"
24 #include "compat.h"
25 #include "mouse.h"
26 #include "common_game.h"
27 #include "blood.h"
28 #include "config.h"
29 #include "gamemenu.h"
30 #include "globals.h"
31 #include "gui.h"
32 #include "inifile.h"
33 #include "levels.h"
34 #include "menu.h"
35 #include "qav.h"
36 #include "resource.h"
37 #include "view.h"
38 
39 CMenuTextMgr gMenuTextMgr;
40 CGameMenuMgr gGameMenuMgr;
41 
42 extern CGameMenuItemPicCycle itemSorryPicCycle;
43 extern CGameMenuItemQAV itemBloodQAV;
44 
CMenuTextMgr()45 CMenuTextMgr::CMenuTextMgr()
46 {
47     at0 = -1;
48 }
49 
50 static char buffer[21][45];
51 
DrawText(const char * pString,int nFont,int x,int y,int nShade,int nPalette,bool shadow)52 void CMenuTextMgr::DrawText(const char *pString, int nFont, int x, int y, int nShade, int nPalette, bool shadow )
53 {
54     viewDrawText(nFont, pString, x, y, nShade, nPalette, 0, shadow);
55 }
56 
GetFontInfo(int nFont,const char * pString,int * pXSize,int * pYSize)57 void CMenuTextMgr::GetFontInfo(int nFont, const char *pString, int *pXSize, int *pYSize)
58 {
59     if (nFont < 0 || nFont >= 5)
60         return;
61     viewGetFontInfo(nFont, pString, pXSize, pYSize);
62 }
63 
64 bool CGameMenuMgr::m_bInitialized = false;
65 bool CGameMenuMgr::m_bActive = false;
66 bool CGameMenuMgr::m_bScanning = false;
67 
CGameMenuMgr()68 CGameMenuMgr::CGameMenuMgr()
69 {
70     dassert(!m_bInitialized);
71     m_bInitialized = true;
72     Clear();
73 }
74 
~CGameMenuMgr()75 CGameMenuMgr::~CGameMenuMgr()
76 {
77     m_bInitialized = false;
78     Clear();
79 }
80 
InitializeMenu(void)81 void CGameMenuMgr::InitializeMenu(void)
82 {
83     if (pActiveMenu)
84     {
85         CGameMenuEvent event;
86         event.at0 = kMenuEventInit;
87         event.at2 = 0;
88         pActiveMenu->Event(event);
89     }
90 }
91 
DeInitializeMenu(void)92 void CGameMenuMgr::DeInitializeMenu(void)
93 {
94     if (pActiveMenu)
95     {
96         CGameMenuEvent event;
97         event.at0 = kMenuEventDeInit;
98         event.at2 = 0;
99         pActiveMenu->Event(event);
100     }
101 }
102 
Push(CGameMenu * pMenu,int nItem)103 bool CGameMenuMgr::Push(CGameMenu *pMenu, int nItem)
104 {
105     if (nMenuPointer == 0)
106     {
107         m_mouselastactivity = -M_MOUSETIMEOUT;
108         m_mousewake_watchpoint = 0;
109         mouseLockToWindow(0);
110         mouseMoveToCenter();
111         mouseReadAbs(&m_prevmousepos, &g_mouseAbs);
112     }
113     dassert(pMenu != NULL);
114     if (nMenuPointer == 8)
115         return false;
116     pActiveMenu = pMenuStack[nMenuPointer] = pMenu;
117     nMenuPointer++;
118     if (nItem >= 0)
119         pMenu->SetFocusItem(nItem);
120     m_bActive = true;
121     gInputMode = INPUT_MODE_1;
122     InitializeMenu();
123     m_menuchange_watchpoint = 1;
124     m_mousecaught = 1;
125     return true;
126 }
127 
Pop(void)128 void CGameMenuMgr::Pop(void)
129 {
130     if (nMenuPointer > 0)
131     {
132         DeInitializeMenu();
133         nMenuPointer--;
134         if (nMenuPointer == 0)
135             Deactivate();
136         else
137             pActiveMenu = pMenuStack[nMenuPointer-1];
138 
139         m_menuchange_watchpoint = 1;
140     }
141     m_mousecaught = 1;
142 }
143 
PostPop(void)144 void CGameMenuMgr::PostPop(void)
145 {
146     m_postPop = true;
147 }
148 
Draw(void)149 void CGameMenuMgr::Draw(void)
150 {
151     if (pActiveMenu)
152     {
153         pActiveMenu->Draw();
154         viewUpdatePages();
155     }
156 
157     if (m_postPop)
158     {
159         Pop();
160         m_postPop = false;
161     }
162 
163     int32_t mousestatus = mouseReadAbs(&m_mousepos, &g_mouseAbs);
164     if (mousestatus && g_mouseClickState == MOUSE_PRESSED)
165         m_mousedownpos = m_mousepos;
166 
167     int16_t mousetile = 1043; // red arrow
168     if (tilesiz[mousetile].x > 0 && mousestatus)
169     {
170         if (!MOUSEACTIVECONDITION)
171             m_mousewake_watchpoint = 1;
172 
173         if (MOUSEACTIVECONDITIONAL(mouseAdvanceClickState()) || m_mousepos.x != m_prevmousepos.x || m_mousepos.y != m_prevmousepos.y)
174         {
175             m_prevmousepos = m_mousepos;
176             m_mouselastactivity = (int)totalclock;
177         }
178         else
179             m_mousewake_watchpoint = 0;
180 
181         m_mousecaught = 0;
182     }
183     else
184     {
185         m_mouselastactivity = -M_MOUSETIMEOUT;
186 
187         m_mousewake_watchpoint = 0;
188     }
189 
190     // Display the mouse cursor, except on touch devices.
191     if (MOUSEACTIVECONDITION)
192     {
193         vec2_t cursorpos = { m_mousepos.x + (7 << 16), m_mousepos.y + (6 << 16) };
194 
195         if ((unsigned) mousetile < MAXTILES)
196         {
197             int32_t scale = 65536;
198             int16_t rotate = 768;
199             uint32_t stat = 2|4|8;
200             int8_t alpha = MOUSEALPHA; //CURSORALPHA;
201             rotatesprite_fs_alpha(cursorpos.x, cursorpos.y, scale, rotate, mousetile, 0, 0, stat, alpha);
202         }
203     }
204     else
205         g_mouseClickState = MOUSE_IDLE;
206 }
207 
Clear(void)208 void CGameMenuMgr::Clear(void)
209 {
210     pActiveMenu = NULL;
211     memset(pMenuStack, 0, sizeof(pMenuStack));
212     nMenuPointer = 0;
213     m_postPop = false;
214 }
215 
Process(void)216 void CGameMenuMgr::Process(void)
217 {
218     if (!pActiveMenu)
219         return;
220 
221     if (m_menuchange_watchpoint > 0)
222         m_menuchange_watchpoint++;
223 
224     CGameMenuEvent event;
225     event.at0 = 0;
226     event.at2 = 0;
227     char key;
228     if (!pActiveMenu->MouseEvent(event) && (key = keyGetScan()) != 0 )
229     {
230         keyFlushScans();
231         keyFlushChars();
232         event.at2 = key;
233         switch (key)
234         {
235         case sc_Escape:
236             event.at0 = kMenuEventEscape;
237             break;
238         case sc_Tab:
239             if (keystatus[sc_LeftShift] || keystatus[sc_RightShift])
240                 event.at0 = kMenuEventUp;
241             else
242                 event.at0 = kMenuEventDown;
243             break;
244         case sc_UpArrow:
245         case sc_kpad_8:
246             event.at0 = kMenuEventUp;
247             gGameMenuMgr.m_mouselastactivity = -M_MOUSETIMEOUT;
248             break;
249         case sc_DownArrow:
250         case sc_kpad_2:
251             event.at0 = kMenuEventDown;
252             gGameMenuMgr.m_mouselastactivity = -M_MOUSETIMEOUT;
253             break;
254         case sc_Enter:
255         case sc_kpad_Enter:
256             event.at0 = kMenuEventEnter;
257             break;
258         case sc_Space:
259             event.at0 = kMenuEventSpace;
260             break;
261         case sc_LeftArrow:
262         case sc_kpad_4:
263             event.at0 = kMenuEventLeft;
264             break;
265         case sc_RightArrow:
266         case sc_kpad_6:
267             event.at0 = kMenuEventRight;
268             break;
269         case sc_Delete:
270         case sc_kpad_Period:
271             event.at0 = kMenuEventDelete;
272             break;
273         case sc_BackSpace:
274             event.at0 = kMenuEventBackSpace;
275             break;
276         default:
277             event.at0 = kMenuEventKey;
278             break;
279         }
280     }
281     if (pActiveMenu->Event(event))
282         Pop();
283 
284     if (m_menuchange_watchpoint >= 3)
285         m_menuchange_watchpoint = 0;
286 }
287 
Deactivate(void)288 void CGameMenuMgr::Deactivate(void)
289 {
290     Clear();
291     keyFlushScans();
292     keyFlushChars();
293     m_bActive = false;
294 
295     mouseLockToWindow(1);
296     gInputMode = INPUT_MODE_0;
297 }
298 
MouseOutsideBounds(vec2_t const * const pos,const int32_t x,const int32_t y,const int32_t width,const int32_t height)299 bool CGameMenuMgr::MouseOutsideBounds(vec2_t const * const pos, const int32_t x, const int32_t y, const int32_t width, const int32_t height)
300 {
301     return pos->x < x || pos->x >= x + width || pos->y < y || pos->y >= y + height;
302 }
303 
CGameMenu()304 CGameMenu::CGameMenu()
305 {
306     m_nItems = 0;
307     m_nFocus = at8 = -1;
308     atc = 0;
309 }
310 
CGameMenu(int unk)311 CGameMenu::CGameMenu(int unk)
312 {
313     m_nItems = 0;
314     m_nFocus = at8 = -1;
315     atc = unk;
316 }
317 
~CGameMenu()318 CGameMenu::~CGameMenu()
319 {
320     if (!atc)
321         return;
322     for (int i = 0; i < m_nItems; i++)
323     {
324         if (pItemList[i] != &itemBloodQAV && pItemList[i] != &itemSorryPicCycle)
325             delete pItemList[i];
326     }
327 }
328 
InitializeItems(CGameMenuEvent & event)329 void CGameMenu::InitializeItems(CGameMenuEvent &event)
330 {
331     for (int i = 0; i < m_nItems; i++)
332     {
333         pItemList[i]->Event(event);
334     }
335 }
336 
Draw(void)337 void CGameMenu::Draw(void)
338 {
339     for (int i = 0; i < m_nItems; i++)
340     {
341         if (pItemList[i]->pPreDrawCallback)
342             pItemList[i]->pPreDrawCallback(pItemList[i]);
343         if (i == m_nFocus || (i != m_nFocus && !pItemList[i]->bNoDraw))
344             pItemList[i]->Draw();
345     }
346 }
347 
Event(CGameMenuEvent & event)348 bool CGameMenu::Event(CGameMenuEvent &event)
349 {
350     if (m_nItems <= 0)
351         return true;
352     switch (event.at0)
353     {
354     case kMenuEventInit:
355     case kMenuEventDeInit:
356         if (at8 >= 0)
357             m_nFocus = at8;
358         InitializeItems(event);
359         return false;
360     }
361     if (m_nFocus < 0)
362         return true;
363     return pItemList[m_nFocus]->Event(event);
364 }
365 
Add(CGameMenuItem * pItem,bool active)366 void CGameMenu::Add(CGameMenuItem *pItem, bool active)
367 {
368     dassert(pItem != NULL);
369     dassert(m_nItems < kMaxGameMenuItems);
370     pItemList[m_nItems] = pItem;
371     pItem->pMenu = this;
372     if (active)
373         m_nFocus = at8 = m_nItems;
374     m_nItems++;
375 }
376 
SetFocusItem(int nItem)377 void CGameMenu::SetFocusItem(int nItem)
378 {
379     dassert(nItem >= 0 && nItem < m_nItems && nItem < kMaxGameMenuItems);
380     if (CanSelectItem(nItem))
381         m_nFocus = at8 = nItem;
382 }
383 
SetFocusItem(CGameMenuItem * pItem)384 void CGameMenu::SetFocusItem(CGameMenuItem *pItem)
385 {
386     for (int i = 0; i < m_nItems; i++)
387         if (pItemList[i] == pItem)
388         {
389             SetFocusItem(i);
390             break;
391         }
392 }
393 
CanSelectItem(int nItem)394 bool CGameMenu::CanSelectItem(int nItem)
395 {
396     dassert(nItem >= 0 && nItem < m_nItems && nItem < kMaxGameMenuItems);
397     return pItemList[nItem]->bCanSelect && pItemList[nItem]->bEnable;
398 }
399 
FocusPrevItem(void)400 void CGameMenu::FocusPrevItem(void)
401 {
402     dassert(m_nFocus >= -1 && m_nFocus < m_nItems && m_nFocus < kMaxGameMenuItems);
403     int t = m_nFocus;
404     do
405     {
406         m_nFocus--;
407         if (m_nFocus < 0)
408             m_nFocus += m_nItems;
409         if (CanSelectItem(m_nFocus))
410             break;
411     } while(t != m_nFocus);
412 }
413 
FocusNextItem(void)414 void CGameMenu::FocusNextItem(void)
415 {
416     dassert(m_nFocus >= -1 && m_nFocus < m_nItems && m_nFocus < kMaxGameMenuItems);
417     int t = m_nFocus;
418     do
419     {
420         m_nFocus++;
421         if (m_nFocus >= m_nItems)
422             m_nFocus = 0;
423         if (CanSelectItem(m_nFocus))
424             break;
425     } while(t != m_nFocus);
426 }
427 
IsFocusItem(CGameMenuItem * pItem)428 bool CGameMenu::IsFocusItem(CGameMenuItem *pItem)
429 {
430     if (m_nFocus < 0)
431         return false;
432     dassert(m_nFocus >= 0 && m_nFocus < m_nItems && m_nFocus < kMaxGameMenuItems);
433     return pItemList[m_nFocus] == pItem;
434 }
435 
MouseEvent(CGameMenuEvent & event)436 bool CGameMenu::MouseEvent(CGameMenuEvent &event)
437 {
438     if (m_nItems <= 0 || m_nFocus < 0)
439         return true;
440     return pItemList[m_nFocus]->MouseEvent(event);
441 }
442 
CGameMenuItem()443 CGameMenuItem::CGameMenuItem()
444 {
445     m_pzText = NULL;
446     m_nX = m_nY = m_nWidth = 0;
447     bCanSelect = 1;
448     bEnable = 1;
449     m_nFont = -1;
450     pMenu = NULL;
451     bNoDraw = 0;
452     pPreDrawCallback = NULL;
453 }
454 
~CGameMenuItem()455 CGameMenuItem::~CGameMenuItem()
456 {
457 }
458 
Event(CGameMenuEvent & event)459 bool CGameMenuItem::Event(CGameMenuEvent &event)
460 {
461     switch (event.at0)
462     {
463     case kMenuEventEscape:
464         return true;
465     case kMenuEventUp:
466         pMenu->FocusPrevItem();
467         break;
468     case kMenuEventDown:
469         pMenu->FocusNextItem();
470         break;
471     }
472     return false;
473 }
474 
MouseEvent(CGameMenuEvent & event)475 bool CGameMenuItem::MouseEvent(CGameMenuEvent &event)
476 {
477     event.at0 = kMenuEventNone;
478     if (MOUSEINACTIVECONDITIONAL(MOUSE_GetButtons()&LEFT_MOUSE))
479     {
480         event.at0 = kMenuEventEnter;
481         MOUSE_ClearButton(LEFT_MOUSE);
482     }
483     else if (MOUSE_GetButtons()&RIGHT_MOUSE)
484     {
485         event.at0 = kMenuEventEscape;
486         MOUSE_ClearButton(RIGHT_MOUSE);
487     }
488 #if 0
489     else if (MOUSEINACTIVECONDITIONAL((MOUSE_GetButtons()&LEFT_MOUSE) && (MOUSE_GetButtons()&WHEELUP_MOUSE)))
490     {
491         MOUSE_ClearButton(WHEELUP_MOUSE);
492         event.bAutoAim = kMenuEventScrollLeft;
493     }
494     else if (MOUSEINACTIVECONDITIONAL((MOUSE_GetButtons()&LEFT_MOUSE) && (MOUSE_GetButtons()&WHEELDOWN_MOUSE)))
495     {
496         MOUSE_ClearButton(WHEELDOWN_MOUSE);
497         event.bAutoAim = kMenuEventScrollRight;
498     }
499 #endif
500     else if (MOUSE_GetButtons()&WHEELUP_MOUSE)
501     {
502         MOUSE_ClearButton(WHEELUP_MOUSE);
503         event.at0 = kMenuEventUp;
504     }
505     else if (MOUSE_GetButtons()&WHEELDOWN_MOUSE)
506     {
507         MOUSE_ClearButton(WHEELDOWN_MOUSE);
508         event.at0 = kMenuEventDown;
509     }
510     return event.at0 != kMenuEventNone;
511 }
512 
CGameMenuItemText()513 CGameMenuItemText::CGameMenuItemText()
514 {
515     m_pzText = 0;
516     bEnable = 0;
517 }
518 
CGameMenuItemText(const char * a1,int a2,int a3,int a4,int a5)519 CGameMenuItemText::CGameMenuItemText(const char *a1, int a2, int a3, int a4, int a5)
520 {
521     m_nWidth = 0;
522     m_pzText = a1;
523     m_nFont = a2;
524     m_nX = a3;
525     m_nY = a4;
526     at20 = a5;
527     bEnable = 0;
528 }
529 
Draw(void)530 void CGameMenuItemText::Draw(void)
531 {
532     if (m_pzText)
533     {
534         int width;
535         int x = m_nX;
536         switch (at20)
537         {
538         case 1:
539             gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
540             x = m_nX-width/2;
541             break;
542         case 2:
543             gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
544             x = m_nX-width;
545             break;
546         }
547         gMenuTextMgr.DrawText(m_pzText,m_nFont, x, m_nY, -128, 0, false);
548     }
549 }
550 
CGameMenuItemTitle()551 CGameMenuItemTitle::CGameMenuItemTitle()
552 {
553     m_pzText = 0;
554     bEnable = 0;
555 }
556 
CGameMenuItemTitle(const char * a1,int a2,int a3,int a4,int a5)557 CGameMenuItemTitle::CGameMenuItemTitle(const char *a1, int a2, int a3, int a4, int a5)
558 {
559     m_nWidth = 0;
560     m_pzText = a1;
561     m_nFont = a2;
562     m_nX = a3;
563     m_nY = a4;
564     at20 = a5;
565     bEnable = 0;
566 }
567 
Draw(void)568 void CGameMenuItemTitle::Draw(void)
569 {
570     if (m_pzText)
571     {
572         int height;
573         gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
574         if (at20 >= 0)
575             rotatesprite(320<<15, m_nY<<16, 65536, 0, at20, -128, 0, 78, 0, 0, xdim-1, ydim-1);
576         viewDrawText(m_nFont, m_pzText, m_nX, m_nY-height/2, -128, 0, 1, false);
577     }
578 }
579 
CGameMenuItemZBool()580 CGameMenuItemZBool::CGameMenuItemZBool()
581 {
582     at20 = false;
583     m_pzText = 0;
584     at21 = "On";
585     at25 = "Off";
586 }
587 
CGameMenuItemZBool(const char * a1,int a2,int a3,int a4,int a5,bool a6,void (* a7)(CGameMenuItemZBool *),const char * a8,const char * a9)588 CGameMenuItemZBool::CGameMenuItemZBool(const char *a1, int a2, int a3, int a4, int a5, bool a6, void(*a7)(CGameMenuItemZBool *), const char *a8, const char *a9)
589 {
590     m_pzText = a1;
591     m_nFont = a2;
592     m_nX = a3;
593     m_nY = a4;
594     m_nWidth = a5;
595     at20 = a6;
596     at29 = a7;
597     if (!a8)
598         at21 = "On";
599     else
600         at21 = a8;
601     if (!a9)
602         at25 = "Off";
603     else
604         at25 = a9;
605 }
606 
Draw(void)607 void CGameMenuItemZBool::Draw(void)
608 {
609     int shade = bEnable ? 32 : 48;
610     int pal = bEnable ? 0 : 5;
611     if (pMenu->IsFocusItem(this))
612         shade = 32-((int)totalclock&63);
613     if (m_pzText)
614         gMenuTextMgr.DrawText(m_pzText, m_nFont, m_nX, m_nY, shade, pal, false);
615     const char *value = at20 ? at21 : at25;
616     int width, height;
617     gMenuTextMgr.GetFontInfo(m_nFont, value, &width, &height);
618     gMenuTextMgr.DrawText(value, m_nFont, m_nWidth-1+m_nX-width, m_nY, shade, pal, false);
619     int mx = m_nX<<16;
620     int my = m_nY<<16;
621     int mw = m_nWidth<<16;
622     int mh = height<<16;
623     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
624     {
625         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
626         {
627             pMenu->SetFocusItem(this);
628         }
629 
630         if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, mx, my, mw, mh))
631         {
632             pMenu->SetFocusItem(this);
633 
634             CGameMenuEvent event = { kMenuEventEnter, 0 };
635 
636             gGameMenuMgr.m_mousecaught = 1;
637 
638             if (Event(event))
639                 gGameMenuMgr.PostPop();
640         }
641     }
642 }
643 
Event(CGameMenuEvent & event)644 bool CGameMenuItemZBool::Event(CGameMenuEvent &event)
645 {
646     switch (event.at0)
647     {
648     case kMenuEventEnter:
649     case kMenuEventSpace:
650         at20 = !at20;
651         if (at29)
652             at29(this);
653         return false;
654     }
655     return CGameMenuItem::Event(event);
656 }
657 
CGameMenuItemChain()658 CGameMenuItemChain::CGameMenuItemChain()
659 {
660     m_pzText = NULL;
661     at24 = NULL;
662     at28 = -1;
663     at2c = NULL;
664     at30 = 0;
665 }
666 
CGameMenuItemChain(const char * a1,int a2,int a3,int a4,int a5,int a6,CGameMenu * a7,int a8,void (* a9)(CGameMenuItemChain *),int a10)667 CGameMenuItemChain::CGameMenuItemChain(const char *a1, int a2, int a3, int a4, int a5, int a6, CGameMenu *a7, int a8, void(*a9)(CGameMenuItemChain *), int a10)
668 {
669     m_pzText = a1;
670     m_nFont = a2;
671     m_nX = a3;
672     m_nY = a4;
673     m_nWidth = a5;
674     at20 = a6;
675     at24 = a7;
676     at28 = a8;
677     at2c = a9;
678     at30 = a10;
679 }
680 
Draw(void)681 void CGameMenuItemChain::Draw(void)
682 {
683     if (!m_pzText) return;
684     int shade = bEnable ? 32 : 48;
685     int pal = bEnable ? 0 : 5;
686     if (pMenu->IsFocusItem(this))
687         shade = 32-((int)totalclock&63);
688     int width, height;
689     int x = m_nX;
690     int y = m_nY;
691     gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, &height);
692     switch (at20)
693     {
694     case 1:
695         x = m_nX+m_nWidth/2-width/2;
696         break;
697     case 2:
698         x = m_nX+m_nWidth-1-width;
699         break;
700     case 0:
701     default:
702         break;
703     }
704     gMenuTextMgr.DrawText(m_pzText, m_nFont, x, m_nY, shade, pal, true);
705     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, x<<16, y<<16, width<<16, height<<16)))
706     {
707         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, x<<16, y<<16, width<<16, height<<16)))
708         {
709             pMenu->SetFocusItem(this);
710         }
711 
712         if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, x<<16, y<<16, width<<16, height<<16))
713         {
714             pMenu->SetFocusItem(this);
715 
716             CGameMenuEvent event = { kMenuEventEnter, 0 };
717 
718             gGameMenuMgr.m_mousecaught = 1;
719 
720             if (Event(event))
721                 gGameMenuMgr.PostPop();
722         }
723     }
724 }
725 
Event(CGameMenuEvent & event)726 bool CGameMenuItemChain::Event(CGameMenuEvent &event)
727 {
728     switch (event.at0)
729     {
730     case kMenuEventEnter:
731         if (at2c)
732             at2c(this);
733         if (at24)
734             gGameMenuMgr.Push(at24, at28);
735         return false;
736     }
737     return CGameMenuItem::Event(event);
738 }
739 
CGameMenuItem7EA1C()740 CGameMenuItem7EA1C::CGameMenuItem7EA1C()
741 {
742     m_pzText = NULL;
743     at24 = NULL;
744     at28 = -1;
745     at2c = NULL;
746     at30 = 0;
747     at34 = NULL;
748     at38[0] = 0;
749     at48[0] = 0;
750 }
751 
CGameMenuItem7EA1C(const char * a1,int a2,int a3,int a4,int a5,const char * a6,const char * a7,int a8,int a9,void (* a10)(CGameMenuItem7EA1C *),int a11)752 CGameMenuItem7EA1C::CGameMenuItem7EA1C(const char *a1, int a2, int a3, int a4, int a5, const char *a6, const char *a7, int a8, int a9, void(*a10)(CGameMenuItem7EA1C *), int a11)
753 {
754     m_pzText = a1;
755     m_nFont = a2;
756     m_nX = a3;
757     m_nY = a4;
758     m_nWidth = a5;
759     at20 = a8;
760     at28 = a9;
761     at2c = a10;
762     at30 = a11;
763     strncpy(at38, a6, 15);
764     strncpy(at48, a7, 15);
765 }
766 
Draw(void)767 void CGameMenuItem7EA1C::Draw(void)
768 {
769     if (!m_pzText) return;
770     int shade = bEnable ? 32 : 48;
771     int pal = bEnable ? 0 : 5;
772     if (pMenu->IsFocusItem(this))
773         shade = 32-((int)totalclock&63);
774     int width;
775     int x = m_nX;
776     switch (at20)
777     {
778     case 1:
779         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
780         x = m_nX+m_nWidth/2-width/2;
781         break;
782     case 2:
783         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
784         x = m_nX+m_nWidth-1-width;
785         break;
786     case 0:
787     default:
788         break;
789     }
790     gMenuTextMgr.DrawText(m_pzText, m_nFont, x, m_nY, shade, pal, true);
791 }
792 
Setup(void)793 void CGameMenuItem7EA1C::Setup(void)
794 {
795     if (!at34 || !at24)
796         return;
797     if (!at34->SectionExists(at48))
798         return;
799     const char *title = at34->GetKeyString(at48, "Title", at48);
800     at24->Add(new CGameMenuItemTitle(title, 1, 160, 20, 2038), false);
801     at24->Add(&itemSorryPicCycle, true);
802     int y = 40;
803     for (int i = 0; i < 21; i++)
804     {
805         sprintf(buffer[i], "Line%d", i+1);
806         if (!at34->KeyExists(at48, buffer[i]))
807             break;
808         const char *line = at34->GetKeyString(at48, buffer[i], NULL);
809         if (line)
810         {
811             if (*line == 0)
812             {
813                 y += 10;
814                 continue;
815             }
816             at24->Add(new CGameMenuItemText(line, 1, 160, y, 1), false);
817             y += 20;
818         }
819     }
820     at24->Add(&itemBloodQAV, false);
821 }
822 
Event(CGameMenuEvent & event)823 bool CGameMenuItem7EA1C::Event(CGameMenuEvent &event)
824 {
825     switch (event.at0)
826     {
827     case kMenuEventEnter:
828     {
829         if (at2c)
830             at2c(this);
831         if (at24)
832             delete at24;
833         at24 = new CGameMenu(1);
834         DICTNODE *pRes = gGuiRes.Lookup(at38, "MNU");
835         if (pRes)
836         {
837             at34 = new IniFile(gGuiRes.Load(pRes));
838             Setup();
839         }
840         if (at24)
841             gGameMenuMgr.Push(at24, at28);
842         return false;
843     }
844     case kMenuEventDeInit:
845         if (at34)
846         {
847             delete at34;
848             at34 = NULL;
849         }
850         if (at24)
851         {
852             delete at24;
853             at24 = NULL;
854         }
855         return false;
856     }
857     return CGameMenuItem::Event(event);
858 }
859 
CGameMenuItem7EE34()860 CGameMenuItem7EE34::CGameMenuItem7EE34()
861 {
862     m_pzText = NULL;
863     at28 = NULL;
864     at20 = -1;
865     at2c = NULL;
866 }
867 
CGameMenuItem7EE34(const char * a1,int a2,int a3,int a4,int a5,int a6)868 CGameMenuItem7EE34::CGameMenuItem7EE34(const char *a1, int a2, int a3, int a4, int a5, int a6)
869 {
870     m_pzText = NULL;
871     at28 = NULL;
872     at20 = -1;
873     at2c = NULL;
874     m_nFont = a2;
875     m_nX = a3;
876     m_pzText = a1;
877     m_nY = a4;
878     m_nWidth = a5;
879     at24 = a6;
880 }
881 
Draw(void)882 void CGameMenuItem7EE34::Draw(void)
883 {
884     if (!m_pzText) return;
885     int shade = bEnable ? 32 : 48;
886     int pal = bEnable ? 0 : 5;
887     if (pMenu->IsFocusItem(this))
888         shade = 32-((int)totalclock&63);
889     int width;
890     int x = m_nX;
891     switch (at24)
892     {
893     case 1:
894         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
895         x = m_nX+m_nWidth/2-width/2;
896         break;
897     case 2:
898         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
899         x = m_nX+m_nWidth-1-width;
900         break;
901     case 0:
902     default:
903         break;
904     }
905     gMenuTextMgr.DrawText(m_pzText, m_nFont, x, m_nY, shade, pal, true);
906 }
907 
908 extern void SetVideoModeOld(CGameMenuItemChain *pItem);
909 
Setup(void)910 void CGameMenuItem7EE34::Setup(void)
911 {
912     if (!at28)
913         return;
914     at28->Add(new CGameMenuItemTitle("Video Mode", 1, 160, 20, 2038), false);
915     if (!at2c)
916     {
917         at2c = new CGameMenu(1);
918         at2c->Add(new CGameMenuItemTitle(" Mode Change ", 1, 160, 20, 2038), false);
919         at2c->Add(&itemSorryPicCycle, true);
920         CGameMenuItem *pItem1 = new CGameMenuItemText("VIDEO MODE WAS SET", 1, 160, 90, 1);
921         CGameMenuItem *pItem2 = new CGameMenuItemText("NOT ALL MODES Work correctly", 1, 160, 110, 1);
922         CGameMenuItem *pItem3 = new CGameMenuItemText("Press ESC to exit", 3, 160, 140, 1);
923         at2c->Add(pItem1, false);
924         pItem1->bEnable = 0;
925         at2c->Add(pItem2, false);
926         pItem2->bEnable = 0;
927         at2c->Add(pItem3, true);
928         pItem3->bEnable = 1;
929         at2c->Add(&itemBloodQAV, false);
930     }
931     sprintf(buffer[0], "640 x 480 (default)");
932     int y = 40;
933     at28->Add(new CGameMenuItemChain(buffer[0], 3, 0, y, 320, 1, at2c, -1, SetVideoModeOld, validmodecnt), true);
934     y += 20;
935     for (int i = 0; i < validmodecnt && i < 20; i++)
936     {
937         sprintf(buffer[i+1], "%d x %d", validmode[i].xdim, validmode[i].ydim);
938         at28->Add(new CGameMenuItemChain(buffer[i+1], 3, 0, y, 320, 1, at2c, -1, SetVideoModeOld, i), false);
939         if (validmodecnt > 10)
940             y += 7;
941         else
942             y += 15;
943     }
944     at28->Add(&itemBloodQAV, false);
945 }
946 
Event(CGameMenuEvent & event)947 bool CGameMenuItem7EE34::Event(CGameMenuEvent &event)
948 {
949     switch (event.at0)
950     {
951     case kMenuEventEnter:
952         if (at28)
953             delete at28;
954         at28 = new CGameMenu(1);
955         Setup();
956         if (at28)
957             gGameMenuMgr.Push(at28, at20);
958         return false;
959     case kMenuEventDeInit:
960         if (at28)
961         {
962             delete at28;
963             at28 = 0;
964         }
965         return false;
966     }
967     return CGameMenuItem::Event(event);
968 }
969 
CGameMenuItemChain7F2F0()970 CGameMenuItemChain7F2F0::CGameMenuItemChain7F2F0()
971 {
972     at34 = -1;
973 }
974 
CGameMenuItemChain7F2F0(char * a1,int a2,int a3,int a4,int a5,int a6,CGameMenu * a7,int a8,void (* a9)(CGameMenuItemChain *),int a10)975 CGameMenuItemChain7F2F0::CGameMenuItemChain7F2F0(char *a1, int a2, int a3, int a4, int a5, int a6, CGameMenu *a7, int a8, void(*a9)(CGameMenuItemChain *), int a10) :
976     CGameMenuItemChain(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
977 {
978     at34 = a10;
979 }
980 
Event(CGameMenuEvent & event)981 bool CGameMenuItemChain7F2F0::Event(CGameMenuEvent &event)
982 {
983     switch (event.at0)
984     {
985     case kMenuEventEnter:
986         if (at34 > -1)
987         {
988             gGameOptions.nEpisode = at34;
989             Bstrcpy(gGameOptions.szUserMap, "\0");
990         }
991         return CGameMenuItemChain::Event(event);
992     }
993     return CGameMenuItem::Event(event);
994 }
995 
CGameMenuItemBitmap()996 CGameMenuItemBitmap::CGameMenuItemBitmap()
997 {
998     m_pzText = NULL;
999 }
1000 
CGameMenuItemBitmap(const char * a1,int a2,int a3,int a4,int a5)1001 CGameMenuItemBitmap::CGameMenuItemBitmap(const char *a1, int a2, int a3, int a4, int a5)
1002 {
1003     m_pzText = a1;
1004     m_nFont = a2;
1005     m_nX = a3;
1006     m_nY = a4;
1007     at20 = a5;
1008 }
1009 
Draw(void)1010 void CGameMenuItemBitmap::Draw(void)
1011 {
1012     int shade = bEnable ? 32 : 48;
1013     int pal = bEnable ? 0 : 5;
1014     if (bEnable && pMenu->IsFocusItem(this))
1015         shade = 32-((int)totalclock&63);
1016     int x = m_nX;
1017     int y = m_nY;
1018     if (m_pzText)
1019     {
1020         int height;
1021         gMenuTextMgr.DrawText(m_pzText, m_nFont, x, y, shade, pal, false);
1022         gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
1023         y += height + 2;
1024     }
1025     rotatesprite(x<<15,y<<15, 65536, 0, at20, 0, 0, 82, 0, 0, xdim-1,ydim-1);
1026 }
1027 
Event(CGameMenuEvent & event)1028 bool CGameMenuItemBitmap::Event(CGameMenuEvent &event)
1029 {
1030     if (bEnable && pMenu->IsFocusItem(this))
1031         pMenu->FocusNextItem();
1032     return CGameMenuItem::Event(event);
1033 }
1034 
CGameMenuItemBitmapLS()1035 CGameMenuItemBitmapLS::CGameMenuItemBitmapLS()
1036 {
1037     m_pzText = NULL;
1038 }
1039 
CGameMenuItemBitmapLS(const char * a1,int a2,int a3,int a4,int a5)1040 CGameMenuItemBitmapLS::CGameMenuItemBitmapLS(const char *a1, int a2, int a3, int a4, int a5)
1041 {
1042     at24 = -1;
1043     m_pzText = a1;
1044     m_nFont = a2;
1045     m_nX = a3;
1046     m_nY = a4;
1047     at28 = a5;
1048 }
1049 
Draw(void)1050 void CGameMenuItemBitmapLS::Draw(void)
1051 {
1052     int shade = bEnable ? 32 : 48;
1053     int pal = bEnable ? 0 : 5;
1054     if (bEnable && pMenu->IsFocusItem(this))
1055         shade = 32-((int)totalclock&63);
1056     int x = m_nX;
1057     int y = m_nY;
1058     if (m_pzText)
1059     {
1060         int height;
1061         gMenuTextMgr.DrawText(m_pzText, m_nFont, x, y, shade, pal, false);
1062         gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
1063         y += height + 2;
1064     }
1065     char stat;
1066     int16_t ang;
1067     int picnum;
1068     if (at24 == -1)
1069     {
1070         stat = 66;
1071         ang = 0;
1072         picnum = at28;
1073     }
1074     else
1075     {
1076         ang = 512;
1077         stat = 70;
1078         picnum = at24;
1079     }
1080     rotatesprite(200<<15,215<<15,32768, ang, picnum, 0, 0, stat, 0, 0, xdim-1, ydim-1);
1081 }
1082 
Event(CGameMenuEvent & event)1083 bool CGameMenuItemBitmapLS::Event(CGameMenuEvent &event)
1084 {
1085     if (bEnable && pMenu->IsFocusItem(this))
1086         pMenu->FocusNextItem();
1087     return CGameMenuItem::Event(event);
1088 }
1089 
CGameMenuItemKeyList()1090 CGameMenuItemKeyList::CGameMenuItemKeyList()
1091 {
1092     m_pzText = NULL;
1093     m_nFont = 3;
1094     m_nX = 0;
1095     m_nY = 0;
1096     nRows = 0;
1097     nTopDelta = 0;
1098     nFocus = 0;
1099     nGameFuncs = 0;
1100     bScan = false;
1101 }
1102 
CGameMenuItemKeyList(const char * a1,int a2,int a3,int a4,int a5,int a6,int a7,void (* a8)(CGameMenuItemKeyList *))1103 CGameMenuItemKeyList::CGameMenuItemKeyList(const char *a1, int a2, int a3, int a4, int a5, int a6, int a7, void(*a8)(CGameMenuItemKeyList *))
1104 {
1105     nTopDelta = 0;
1106     nFocus = 0;
1107     bScan = false;
1108     m_pzText = a1;
1109     m_nFont = a2;
1110     m_nX = a3;
1111     m_nY = a4;
1112     m_nWidth = a5;
1113     nRows = a6;
1114     pCallback = a8;
1115     nGameFuncs = a7;
1116 }
1117 
Scan(void)1118 void CGameMenuItemKeyList::Scan(void)
1119 {
1120     KB_FlushKeyboardQueue();
1121     KB_FlushKeyboardQueueScans();
1122     KB_ClearKeysDown();
1123     KB_LastScan = 0;
1124     bScan = true;
1125 }
1126 
1127 extern uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];
Draw(void)1128 void CGameMenuItemKeyList::Draw(void)
1129 {
1130     char buffer[40], buffer2[40];
1131     int width, height;
1132     int shade;
1133     gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
1134     int y = m_nY;
1135     int k = nFocus - nTopDelta;
1136     int nNewFocus = nFocus;
1137     bool bClick = false;
1138     for (int i = 0; i < nRows; i++, y += height, k++)
1139     {
1140         char key1, key2;
1141         key1 = KeyboardKeys[k][0];
1142         key2 = KeyboardKeys[k][1];
1143         const char *sKey1 = key1 == sc_Tilde ? "Tilde" : KB_ScanCodeToString(key1);
1144         const char *sKey2 = key2 == sc_Tilde ? "Tilde" : KB_ScanCodeToString(key2);
1145         sprintf(buffer, "%s", CONFIG_FunctionNumToName(k));
1146         if (key2 == 0 || key2 == 0xff)
1147         {
1148             if (key1 == 0 || key1 == 0xff)
1149                 sprintf(buffer2, "????");
1150             else
1151                 sprintf(buffer2, "%s", sKey1);
1152         }
1153         else
1154             sprintf(buffer2, "%s or %s", sKey1, sKey2);
1155 
1156         if (k == nFocus)
1157         {
1158             shade = 32;
1159             if (pMenu->IsFocusItem(this))
1160                 shade = 32-((int)totalclock&63);
1161             viewDrawText(3, buffer, m_nX, y, shade, 0, 0, false);
1162             const char *sVal;
1163             if (bScan && ((int)totalclock & 32))
1164                 sVal = "____";
1165             else
1166                 sVal = buffer2;
1167             gMenuTextMgr.GetFontInfo(m_nFont, sVal, &width, 0);
1168             viewDrawText(m_nFont, sVal, m_nX+m_nWidth-1-width, y, shade, 0, 0, false);
1169         }
1170         else
1171         {
1172             viewDrawText(3, buffer, m_nX, y, 24, 0, 0, false);
1173             gMenuTextMgr.GetFontInfo(m_nFont, buffer2, &width, 0);
1174             viewDrawText(m_nFont, buffer2, m_nX+m_nWidth-1-width, y, 24, 0, 0, false);
1175         }
1176         int mx = m_nX<<16;
1177         int my = y<<16;
1178         int mw = m_nWidth<<16;
1179         int mh = height<<16;
1180         if (!bScan && bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
1181         {
1182             if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
1183             {
1184                 nNewFocus = k;
1185             }
1186 
1187             if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, mx, my, mw, mh))
1188             {
1189                 nNewFocus = k;
1190                 bClick = true;
1191             }
1192         }
1193     }
1194     nTopDelta += nNewFocus-nFocus;
1195     nFocus = nNewFocus;
1196     if (bClick)
1197     {
1198         CGameMenuEvent event = { kMenuEventEnter, 0 };
1199 
1200         gGameMenuMgr.m_mousecaught = 1;
1201 
1202         if (Event(event))
1203             gGameMenuMgr.PostPop();
1204     }
1205 }
1206 
Event(CGameMenuEvent & event)1207 bool CGameMenuItemKeyList::Event(CGameMenuEvent &event)
1208 {
1209     if (bScan)
1210     {
1211         if (KB_LastScan && KB_LastScan != sc_Pause)
1212         {
1213             if (KB_KeyWaiting())
1214                 KB_GetCh();
1215             char key1, key2;
1216             extern uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];
1217             key1 = KeyboardKeys[nFocus][0];
1218             key2 = KeyboardKeys[nFocus][1];
1219             if (key1 > 0 && key2 != KB_LastScan)
1220                 key2 = key1;
1221             key1 = KB_LastScan;
1222             if (key1 == key2)
1223                 key2 = 0;
1224             uint8_t oldKey[2];
1225             oldKey[0] = KeyboardKeys[nFocus][0];
1226             oldKey[1] = KeyboardKeys[nFocus][1];
1227             KeyboardKeys[nFocus][0] = key1;
1228             KeyboardKeys[nFocus][1] = key2;
1229             CONFIG_MapKey(nFocus, key1, oldKey[0], key2, oldKey[1]);
1230             KB_FlushKeyboardQueue();
1231             KB_FlushKeyboardQueueScans();
1232             KB_ClearKeysDown();
1233             keyFlushScans();
1234             keyFlushChars();
1235             bScan = 0;
1236         }
1237         return false;
1238     }
1239     switch (event.at0)
1240     {
1241     case kMenuEventUp:
1242         if (event.at2 == sc_Tab || nFocus == 0)
1243         {
1244             pMenu->FocusPrevItem();
1245             return false;
1246         }
1247         nFocus--;
1248         if (nTopDelta > 0)
1249             nTopDelta--;
1250         return false;
1251     case kMenuEventDown:
1252         if (event.at2 == sc_Tab || nFocus == nGameFuncs-1)
1253         {
1254             pMenu->FocusNextItem();
1255             return false;
1256         }
1257         nFocus++;
1258         if (nTopDelta+1 < nRows)
1259             nTopDelta++;
1260         return false;
1261     case kMenuEventEnter:
1262         if (pCallback)
1263             pCallback(this);
1264         Scan();
1265         return false;
1266     case kMenuEventBackSpace:
1267     case kMenuEventDelete:
1268         uint8_t oldKey[2];
1269         oldKey[0] = KeyboardKeys[nFocus][0];
1270         oldKey[1] = KeyboardKeys[nFocus][1];
1271         KeyboardKeys[nFocus][0] = 0;
1272         KeyboardKeys[nFocus][1] = 0;
1273         CONFIG_MapKey(nFocus, 0, oldKey[0], 0, oldKey[1]);
1274         return false;
1275     case kMenuEventScrollUp:
1276         if (nFocus-nTopDelta > 0)
1277         {
1278             nTopDelta++;
1279             if (nTopDelta>0)
1280             {
1281                 nFocus--;
1282                 nTopDelta--;
1283             }
1284         }
1285         return false;
1286     case kMenuEventScrollDown:
1287         if (nFocus-nTopDelta+nRows < nGameFuncs)
1288         {
1289             nTopDelta--;
1290             if (nTopDelta+1 < nRows)
1291             {
1292                 nFocus++;
1293                 nTopDelta++;
1294             }
1295         }
1296         return false;
1297     }
1298     return CGameMenuItem::Event(event);
1299 }
1300 
MouseEvent(CGameMenuEvent & event)1301 bool CGameMenuItemKeyList::MouseEvent(CGameMenuEvent &event)
1302 {
1303     event.at0 = kMenuEventNone;
1304     if (MOUSEACTIVECONDITIONAL(MOUSE_GetButtons()&WHEELUP_MOUSE))
1305     {
1306         gGameMenuMgr.m_mouselastactivity = (int)totalclock;
1307         MOUSE_ClearButton(WHEELUP_MOUSE);
1308         event.at0 = kMenuEventScrollUp;
1309     }
1310     else if (MOUSEACTIVECONDITIONAL(MOUSE_GetButtons()&WHEELDOWN_MOUSE))
1311     {
1312         gGameMenuMgr.m_mouselastactivity = (int)totalclock;
1313         MOUSE_ClearButton(WHEELDOWN_MOUSE);
1314         event.at0 = kMenuEventScrollDown;
1315     }
1316     else
1317         return CGameMenuItem::MouseEvent(event);
1318     return event.at0 != kMenuEventNone;
1319 }
1320 
CGameMenuItemSlider()1321 CGameMenuItemSlider::CGameMenuItemSlider()
1322 {
1323     m_pzText = NULL;
1324     m_nFont = -1;
1325     m_nX = 0;
1326     m_nY = 0;
1327     nValue = 0;
1328     nRangeLow = 0;
1329     nStep = 0;
1330     pCallback = NULL;
1331     pValue = NULL;
1332     nSliderTile = 2204;
1333     nCursorTile = 2028;
1334     nShowValue = kMenuSliderNone;
1335 }
1336 
CGameMenuItemSlider(const char * _pzText,int _nFont,int _nX,int _nY,int _nWidth,int _nValue,int _nRangeLow,int _nRangeHigh,int _nStep,void (* _pCallback)(CGameMenuItemSlider *),int _nSliderTile,int _nCursorTile,int _nShowValue)1337 CGameMenuItemSlider::CGameMenuItemSlider(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, int _nValue, int _nRangeLow, int _nRangeHigh, int _nStep, void(*_pCallback)(CGameMenuItemSlider *), int _nSliderTile, int _nCursorTile, int _nShowValue)
1338 {
1339     m_pzText = _pzText;
1340     m_nFont = _nFont;
1341     m_nX = _nX;
1342     m_nY = _nY;
1343     m_nWidth = _nWidth;
1344     nRangeLow = _nRangeLow;
1345     nRangeHigh = _nRangeHigh;
1346     nStep = _nStep;
1347     nValue = ClipRange(_nValue, nRangeLow, nRangeHigh);
1348     pCallback = _pCallback;
1349     nSliderTile = 2204;
1350     nCursorTile = 2028;
1351     if (_nSliderTile >= 0)
1352         nSliderTile = _nSliderTile;
1353     if (_nCursorTile >= 0)
1354         nCursorTile = _nCursorTile;
1355     nShowValue = _nShowValue;
1356 }
1357 
CGameMenuItemSlider(const char * _pzText,int _nFont,int _nX,int _nY,int _nWidth,int * pnValue,int _nRangeLow,int _nRangeHigh,int _nStep,void (* _pCallback)(CGameMenuItemSlider *),int _nSliderTile,int _nCursorTile,int _nShowValue)1358 CGameMenuItemSlider::CGameMenuItemSlider(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, int *pnValue, int _nRangeLow, int _nRangeHigh, int _nStep, void(*_pCallback)(CGameMenuItemSlider *), int _nSliderTile, int _nCursorTile, int _nShowValue)
1359 {
1360     m_pzText = _pzText;
1361     m_nFont = _nFont;
1362     m_nX = _nX;
1363     m_nY = _nY;
1364     m_nWidth = _nWidth;
1365     nRangeLow = _nRangeLow;
1366     nRangeHigh = _nRangeHigh;
1367     nStep = _nStep;
1368     dassert(pnValue != NULL);
1369     pValue = pnValue;
1370     nValue = ClipRange(*pnValue, nRangeLow, nRangeHigh);
1371     pCallback = _pCallback;
1372     nSliderTile = 2204;
1373     nCursorTile = 2028;
1374     if (_nSliderTile >= 0)
1375         nSliderTile = _nSliderTile;
1376     if (_nCursorTile >= 0)
1377         nCursorTile = _nCursorTile;
1378     nShowValue = _nShowValue;
1379 }
1380 
Draw(void)1381 void CGameMenuItemSlider::Draw(void)
1382 {
1383     char buffer[16];
1384     int height;
1385     nValue = pValue ? *pValue : nValue;
1386     gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
1387     int shade = bEnable ? 32 : 48;
1388     int shade2 = bEnable ? 0 : 16;
1389     int pal = bEnable ? 0 : 5;
1390     if (pMenu->IsFocusItem(this))
1391         shade = 32-((int)totalclock&63);
1392     if (m_pzText)
1393         gMenuTextMgr.DrawText(m_pzText, m_nFont, m_nX, m_nY, shade, pal, false);
1394     int sliderX = m_nX+m_nWidth-1-tilesiz[nSliderTile].x/2;
1395     rotatesprite(sliderX<<16, (m_nY+height/2)<<16, 65536, 0, nSliderTile, shade2, pal, 10, 0, 0, xdim-1, ydim-1);
1396     int nRange = nRangeHigh - nRangeLow;
1397     dassert(nRange > 0);
1398     int value = nValue - nRangeLow;
1399     int width = tilesiz[nSliderTile].x-8;
1400     int cursorX = sliderX + ksgn(nStep)*(value * width / nRange - width / 2);
1401     rotatesprite(cursorX<<16, (m_nY+height/2)<<16, 65536, 0, nCursorTile, shade2, pal, 10, 0, 0, xdim-1, ydim-1);
1402 
1403     buffer[0] = 0;
1404     switch (nShowValue)
1405     {
1406     case kMenuSliderNone:
1407         break;
1408     case kMenuSliderValue:
1409         sprintf(buffer, "%i ", nValue);
1410         break;
1411     case kMenuSliderPercent:
1412         sprintf(buffer, "%i%% ", roundscale(value, 100, nRange));
1413         break;
1414     case kMenuSliderQ16:
1415         snprintf(buffer, 16, "%.3f ", nValue/65536.f);
1416         break;
1417     }
1418     int valueWidth;
1419     gMenuTextMgr.GetFontInfo(m_nFont, buffer, &valueWidth, NULL);
1420     int valueX = m_nX+m_nWidth-1-tilesiz[nSliderTile].x-valueWidth;
1421     gMenuTextMgr.DrawText(buffer, m_nFont, valueX, m_nY, 32, 0, false);
1422 
1423     int mx = m_nX;
1424     int my = m_nY;
1425     int mw = m_nWidth;
1426     int mh = height;
1427     if (height < tilesiz[nSliderTile].y)
1428     {
1429         my -= (tilesiz[nSliderTile].y-height)/2;
1430         height = tilesiz[nSliderTile].y;
1431     }
1432     mx <<= 16;
1433     my <<= 16;
1434     mw <<= 16;
1435     mh <<= 16;
1436 
1437     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
1438     {
1439         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
1440         {
1441             pMenu->SetFocusItem(this);
1442         }
1443 
1444         if (!gGameMenuMgr.m_mousecaught && (g_mouseClickState == MOUSE_PRESSED || g_mouseClickState == MOUSE_HELD))
1445         {
1446             pMenu->SetFocusItem(this);
1447 
1448             int sliderx = m_nX+m_nWidth-1-tilesiz[nSliderTile].x;
1449             int sliderwidth = tilesiz[nSliderTile].x;
1450             int regionwidth = sliderwidth-8;
1451             int regionx = sliderx+(sliderwidth-regionwidth)/2;
1452             sliderx <<= 16;
1453             sliderwidth <<= 16;
1454             regionwidth <<= 16;
1455             regionx <<= 16;
1456 
1457             // region between the x-midline of the slidepoint at the extremes slides proportionally
1458             if (!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, regionx, my, regionwidth, mh))
1459             {
1460                 int dx = (gGameMenuMgr.m_mousepos.x - (regionx+regionwidth/2))*ksgn(nStep);
1461                 nValue = nRangeLow + roundscale(dx+regionwidth/2, nRange, regionwidth);
1462                 nValue = ClipRange(nValue, nRangeLow, nRangeHigh);
1463                 if (pCallback)
1464                     pCallback(this);
1465                 gGameMenuMgr.m_mousecaught = 1;
1466             }
1467             // region outside the x-midlines clamps to the extremes
1468             else if (!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, sliderx, my, sliderwidth, mh))
1469             {
1470                 if ((gGameMenuMgr.m_mousepos.x-(regionx+regionwidth/2))*ksgn(nStep) > 0)
1471                     nValue = nRangeHigh;
1472                 else
1473                     nValue = nRangeLow;
1474                 if (pCallback)
1475                     pCallback(this);
1476                 gGameMenuMgr.m_mousecaught = 1;
1477             }
1478         }
1479     }
1480 }
1481 
Event(CGameMenuEvent & event)1482 bool CGameMenuItemSlider::Event(CGameMenuEvent &event)
1483 {
1484     nValue = pValue ? *pValue : nValue;
1485     switch (event.at0)
1486     {
1487     case kMenuEventUp:
1488         pMenu->FocusPrevItem();
1489         return false;
1490     case kMenuEventDown:
1491         pMenu->FocusNextItem();
1492         return false;
1493     case kMenuEventLeft:
1494         if (nStep > 0)
1495             nValue = DecBy(nValue, nStep);
1496         else
1497             nValue = IncBy(nValue, -nStep);
1498         nValue = ClipRange(nValue, nRangeLow, nRangeHigh);
1499         if (pCallback)
1500             pCallback(this);
1501         return false;
1502     case kMenuEventRight:
1503         if (nStep >= 0)
1504             nValue = IncBy(nValue, nStep);
1505         else
1506             nValue = DecBy(nValue, -nStep);
1507         nValue = ClipRange(nValue, nRangeLow, nRangeHigh);
1508         if (pCallback)
1509             pCallback(this);
1510         return false;
1511     case kMenuEventEnter:
1512         if (pCallback)
1513             pCallback(this);
1514         return false;
1515     }
1516     return CGameMenuItem::Event(event);
1517 }
1518 
MouseEvent(CGameMenuEvent & event)1519 bool CGameMenuItemSlider::MouseEvent(CGameMenuEvent &event)
1520 {
1521     event.at0 = kMenuEventNone;
1522     if (MOUSEINACTIVECONDITIONAL((MOUSE_GetButtons()&LEFT_MOUSE) && (MOUSE_GetButtons()&WHEELUP_MOUSE)))
1523     {
1524         MOUSE_ClearButton(WHEELUP_MOUSE);
1525         event.at0 = kMenuEventLeft;
1526     }
1527     else if (MOUSEINACTIVECONDITIONAL((MOUSE_GetButtons()&LEFT_MOUSE) && (MOUSE_GetButtons()&WHEELDOWN_MOUSE)))
1528     {
1529         MOUSE_ClearButton(WHEELDOWN_MOUSE);
1530         event.at0 = kMenuEventRight;
1531     }
1532     else if (MOUSE_GetButtons()&RIGHT_MOUSE)
1533     {
1534         MOUSE_ClearButton(RIGHT_MOUSE);
1535         event.at0 = kMenuEventEscape;
1536     }
1537     else if (MOUSE_GetButtons()&WHEELUP_MOUSE)
1538     {
1539         MOUSE_ClearButton(WHEELUP_MOUSE);
1540         MOUSE_ClearButton(LEFT_MOUSE);
1541         event.at0 = kMenuEventUp;
1542     }
1543     else if (MOUSE_GetButtons()&WHEELDOWN_MOUSE)
1544     {
1545         MOUSE_ClearButton(WHEELDOWN_MOUSE);
1546         MOUSE_ClearButton(LEFT_MOUSE);
1547         event.at0 = kMenuEventDown;
1548     }
1549     return event.at0 != kMenuEventNone;
1550 }
1551 
CGameMenuItemSliderFloat()1552 CGameMenuItemSliderFloat::CGameMenuItemSliderFloat()
1553 {
1554     m_pzText = NULL;
1555     m_nFont = -1;
1556     m_nX = 0;
1557     m_nY = 0;
1558     fValue = 0;
1559     fRangeLow = 0;
1560     fStep = 0;
1561     pCallback = NULL;
1562     pValue = NULL;
1563     nSliderTile = 2204;
1564     nCursorTile = 2028;
1565     nShowValue = kMenuSliderNone;
1566 }
1567 
CGameMenuItemSliderFloat(const char * _pzText,int _nFont,int _nX,int _nY,int _nWidth,float _fValue,float _fRangeLow,float _fRangeHigh,float _fStep,void (* _pCallback)(CGameMenuItemSliderFloat *),int _nSliderTile,int _nCursorTile,int _nShowValue)1568 CGameMenuItemSliderFloat::CGameMenuItemSliderFloat(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, float _fValue, float _fRangeLow, float _fRangeHigh, float _fStep, void(*_pCallback)(CGameMenuItemSliderFloat *), int _nSliderTile, int _nCursorTile, int _nShowValue)
1569 {
1570     m_pzText = _pzText;
1571     m_nFont = _nFont;
1572     m_nX = _nX;
1573     m_nY = _nY;
1574     m_nWidth = _nWidth;
1575     fRangeLow = _fRangeLow;
1576     fRangeHigh = _fRangeHigh;
1577     fStep = _fStep;
1578     fValue = ClipRangeF(_fValue, fRangeLow, fRangeHigh);
1579     pCallback = _pCallback;
1580     nSliderTile = 2204;
1581     nCursorTile = 2028;
1582     if (_nSliderTile >= 0)
1583         nSliderTile = _nSliderTile;
1584     if (_nCursorTile >= 0)
1585         nCursorTile = _nCursorTile;
1586     nShowValue = _nShowValue;
1587 }
1588 
CGameMenuItemSliderFloat(const char * _pzText,int _nFont,int _nX,int _nY,int _nWidth,float * pnValue,float _fRangeLow,float _fRangeHigh,float _fStep,void (* _pCallback)(CGameMenuItemSliderFloat *),int _nSliderTile,int _nCursorTile,int _nShowValue)1589 CGameMenuItemSliderFloat::CGameMenuItemSliderFloat(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, float *pnValue, float _fRangeLow, float _fRangeHigh, float _fStep, void(*_pCallback)(CGameMenuItemSliderFloat *), int _nSliderTile, int _nCursorTile, int _nShowValue)
1590 {
1591     m_pzText = _pzText;
1592     m_nFont = _nFont;
1593     m_nX = _nX;
1594     m_nY = _nY;
1595     m_nWidth = _nWidth;
1596     fRangeLow = _fRangeLow;
1597     fRangeHigh = _fRangeHigh;
1598     fStep = _fStep;
1599     dassert(pnValue != NULL);
1600     pValue = pnValue;
1601     fValue = ClipRangeF(*pnValue, fRangeLow, fRangeHigh);
1602     pCallback = _pCallback;
1603     nSliderTile = 2204;
1604     nCursorTile = 2028;
1605     if (_nSliderTile >= 0)
1606         nSliderTile = _nSliderTile;
1607     if (_nCursorTile >= 0)
1608         nCursorTile = _nCursorTile;
1609     nShowValue = _nShowValue;
1610 }
1611 
Draw(void)1612 void CGameMenuItemSliderFloat::Draw(void)
1613 {
1614     char buffer[16];
1615     int height;
1616 
1617     fValue = pValue ? *pValue : fValue;
1618     gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
1619     int shade = bEnable ? 32 : 48;
1620     int shade2 = bEnable ? 0 : 16;
1621     int pal = bEnable ? 0 : 5;
1622     if (pMenu->IsFocusItem(this))
1623         shade = 32-((int)totalclock&63);
1624     if (m_pzText)
1625         gMenuTextMgr.DrawText(m_pzText, m_nFont, m_nX, m_nY, shade, pal, false);
1626     int sliderX = m_nX+m_nWidth-1-tilesiz[nSliderTile].x/2;
1627     rotatesprite(sliderX<<16, (m_nY+height/2)<<16, 65536, 0, nSliderTile, shade2, pal, 10, 0, 0, xdim-1, ydim-1);
1628     float fRange = fRangeHigh - fRangeLow;
1629     dassert(fRange > 0);
1630     float value = fValue - fRangeLow;
1631     int width = tilesiz[nSliderTile].x-8;
1632     int cursorX = sliderX + (int)(ksgnf(fStep)*(value * width / fRange - width / 2));
1633     rotatesprite(cursorX<<16, (m_nY+height/2)<<16, 65536, 0, nCursorTile, shade2, pal, 10, 0, 0, xdim-1, ydim-1);
1634 
1635     buffer[0] = 0;
1636     switch (nShowValue)
1637     {
1638     case kMenuSliderNone:
1639         break;
1640     case kMenuSliderValue:
1641         snprintf(buffer, 16, "%.3f ", fValue);
1642         break;
1643     case kMenuSliderPercent:
1644         snprintf(buffer, 16, "%.3f%% ", value*100.f/fRange);
1645         break;
1646     }
1647     int valueWidth;
1648     gMenuTextMgr.GetFontInfo(m_nFont, buffer, &valueWidth, NULL);
1649     int valueX = m_nX+m_nWidth-1-tilesiz[nSliderTile].x-valueWidth;
1650     gMenuTextMgr.DrawText(buffer, m_nFont, valueX, m_nY, 32, 0, false);
1651 
1652     int mx = m_nX;
1653     int my = m_nY;
1654     int mw = m_nWidth;
1655     int mh = height;
1656     if (height < tilesiz[nSliderTile].y)
1657     {
1658         my -= (tilesiz[nSliderTile].y-height)/2;
1659         height = tilesiz[nSliderTile].y;
1660     }
1661     mx <<= 16;
1662     my <<= 16;
1663     mw <<= 16;
1664     mh <<= 16;
1665 
1666     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
1667     {
1668         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
1669         {
1670             pMenu->SetFocusItem(this);
1671         }
1672 
1673         if (!gGameMenuMgr.m_mousecaught && (g_mouseClickState == MOUSE_PRESSED || g_mouseClickState == MOUSE_HELD))
1674         {
1675             pMenu->SetFocusItem(this);
1676 
1677             int sliderx = m_nX+m_nWidth-1-tilesiz[nSliderTile].x;
1678             int sliderwidth = tilesiz[nSliderTile].x;
1679             int regionwidth = sliderwidth-8;
1680             int regionx = sliderx+(sliderwidth-regionwidth)/2;
1681             sliderx <<= 16;
1682             sliderwidth <<= 16;
1683             regionwidth <<= 16;
1684             regionx <<= 16;
1685 
1686             // region between the x-midline of the slidepoint at the extremes slides proportionally
1687             if (!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, regionx, my, regionwidth, mh))
1688             {
1689                 int dx = (gGameMenuMgr.m_mousepos.x - (regionx+regionwidth/2))*ksgnf(fStep);
1690                 fValue = fRangeLow + (dx+regionwidth/2) * fRange / regionwidth;
1691                 fValue = ClipRangeF(fValue, fRangeLow, fRangeHigh);
1692                 if (pCallback)
1693                     pCallback(this);
1694                 gGameMenuMgr.m_mousecaught = 1;
1695             }
1696             // region outside the x-midlines clamps to the extremes
1697             else if (!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, sliderx, my, sliderwidth, mh))
1698             {
1699                 if ((gGameMenuMgr.m_mousepos.x-(regionx+regionwidth/2))*ksgnf(fStep) > 0)
1700                     fValue = fRangeHigh;
1701                 else
1702                     fValue = fRangeLow;
1703                 if (pCallback)
1704                     pCallback(this);
1705                 gGameMenuMgr.m_mousecaught = 1;
1706             }
1707         }
1708     }
1709 }
1710 
Event(CGameMenuEvent & event)1711 bool CGameMenuItemSliderFloat::Event(CGameMenuEvent &event)
1712 {
1713     fValue = pValue ? *pValue : fValue;
1714     switch (event.at0)
1715     {
1716     case kMenuEventUp:
1717         pMenu->FocusPrevItem();
1718         return false;
1719     case kMenuEventDown:
1720         pMenu->FocusNextItem();
1721         return false;
1722     case kMenuEventLeft:
1723         if (fStep > 0)
1724             fValue -= fStep;
1725         else
1726             fValue += fStep;
1727         fValue = ClipRangeF(fValue, fRangeLow, fRangeHigh);
1728         if (pCallback)
1729             pCallback(this);
1730         return false;
1731     case kMenuEventRight:
1732         if (fStep >= 0)
1733             fValue += fStep;
1734         else
1735             fValue -= fStep;
1736         fValue = ClipRangeF(fValue, fRangeLow, fRangeHigh);
1737         if (pCallback)
1738             pCallback(this);
1739         return false;
1740     case kMenuEventEnter:
1741         if (pCallback)
1742             pCallback(this);
1743         return false;
1744     }
1745     return CGameMenuItem::Event(event);
1746 }
1747 
CGameMenuItemZEdit()1748 CGameMenuItemZEdit::CGameMenuItemZEdit()
1749 {
1750     m_pzText = NULL;
1751     m_nFont = -1;
1752     m_nX = 0;
1753     m_nY = 0;
1754     at20 = NULL;
1755     at24 = 0;
1756     at32 = 0;
1757     at2c = 0;
1758     at30 = 0;
1759     at28 = 0;
1760     at31 = 1;
1761 }
1762 
CGameMenuItemZEdit(const char * a1,int a2,int a3,int a4,int a5,char * a6,int a7,char a8,void (* a9)(CGameMenuItemZEdit *,CGameMenuEvent *),int a10)1763 CGameMenuItemZEdit::CGameMenuItemZEdit(const char *a1, int a2, int a3, int a4, int a5, char *a6, int a7, char a8, void(*a9)(CGameMenuItemZEdit *, CGameMenuEvent *), int a10)
1764 {
1765     at30 = 0;
1766     at31 = 1;
1767     m_pzText = a1;
1768     m_nFont = a2;
1769     m_nX = a3;
1770     m_nY = a4;
1771     m_nWidth = a5;
1772     at20 = a6;
1773     at24 = a7;
1774     at32 = a8;
1775     at2c = a9;
1776     at28 = a10;
1777 }
1778 
AddChar(char ch)1779 void CGameMenuItemZEdit::AddChar(char ch)
1780 {
1781     int i = strlen(at20);
1782     if (i + 1 < at24)
1783     {
1784         at20[i] = ch;
1785         at20[i + 1] = 0;
1786     }
1787 }
1788 
BackChar(void)1789 void CGameMenuItemZEdit::BackChar(void)
1790 {
1791     int i = strlen(at20);
1792     if (i > 0)
1793         at20[i - 1] = 0;
1794 }
1795 
Draw(void)1796 void CGameMenuItemZEdit::Draw(void)
1797 {
1798     int height, width, textWidth = 0;
1799     gMenuTextMgr.GetFontInfo(m_nFont, NULL, &width, &height);
1800     if (at20)
1801         gMenuTextMgr.GetFontInfo(m_nFont, at20, &textWidth, NULL);
1802     int shade = bEnable ? 32 : 48;
1803     int pal = bEnable ? 0 : 5;
1804     if (pMenu->IsFocusItem(this))
1805         shade = 32-((int)totalclock&63);
1806     if (at30)
1807         shade = -128;
1808     if (m_pzText)
1809         gMenuTextMgr.DrawText(m_pzText, m_nFont, m_nX, m_nY, shade, pal, false);
1810     int x = m_nX+m_nWidth-1-textWidth;//(at24+1)*width;
1811     if (at20 && *at20)
1812     {
1813         int width;
1814         gMenuTextMgr.GetFontInfo(m_nFont, at20, &width, NULL);
1815         int shade2;
1816         if (at32)
1817         {
1818             if (at30)
1819                 shade2 = -128;
1820             else
1821                 shade2 = shade;
1822         }
1823         else
1824         {
1825             if (at30)
1826                 shade2 = shade;
1827             else
1828                 shade2 = 32;
1829         }
1830         gMenuTextMgr.DrawText(at20, m_nFont, x, m_nY, shade2, pal, false);
1831         x += width;
1832     }
1833     if (at30 && ((int)totalclock & 32))
1834         gMenuTextMgr.DrawText("_", m_nFont, x, m_nY, shade, 0, false);
1835 
1836     int mx = m_nX<<16;
1837     int my = m_nY<<16;
1838     int mw = m_nWidth<<16;
1839     int mh = height<<16;
1840 
1841     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
1842     {
1843         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
1844         {
1845             pMenu->SetFocusItem(this);
1846         }
1847 
1848         if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, mx, my, mw, mh))
1849         {
1850             pMenu->SetFocusItem(this);
1851 
1852             CGameMenuEvent event = { kMenuEventEnter, 0 };
1853 
1854             gGameMenuMgr.m_mousecaught = 1;
1855 
1856             if (Event(event))
1857                 gGameMenuMgr.PostPop();
1858         }
1859     }
1860 }
1861 
Event(CGameMenuEvent & event)1862 bool CGameMenuItemZEdit::Event(CGameMenuEvent &event)
1863 {
1864     static char buffer[256];
1865     // Hack
1866     if (event.at2 == sc_kpad_2 || event.at2 == sc_kpad_4 || event.at2 == sc_kpad_6 || event.at2 == sc_kpad_8)
1867         event.at0 = kMenuEventKey;
1868     switch (event.at0)
1869     {
1870     case kMenuEventEscape:
1871         if (at30)
1872         {
1873             strncpy(at20, buffer, at24);
1874             at20[at24-1] = 0;
1875             at30 = 0;
1876             return false;
1877         }
1878         return true;
1879     case kMenuEventEnter:
1880         if (!at31)
1881         {
1882             if (at2c)
1883                 at2c(this, &event);
1884             return false;
1885         }
1886         if (at30)
1887         {
1888             if (at2c)
1889                 at2c(this, &event);
1890             at30 = 0;
1891             return false;
1892         }
1893         strncpy(buffer, at20, at24);
1894         buffer[at24-1] = 0;
1895         at30 = 1;
1896         return false;
1897     case kMenuEventBackSpace:
1898         if (at30)
1899             BackChar();
1900         return false;
1901     case kMenuEventKey:
1902     case kMenuEventSpace:
1903     {
1904         char key;
1905         if (event.at2 < 128)
1906         {
1907             if (keystatus[sc_LeftShift] || keystatus[sc_RightShift])
1908                 key = g_keyAsciiTableShift[event.at2];
1909             else
1910                 key = g_keyAsciiTable[event.at2];
1911             if (at30 && (isalnum(key) || ispunct(key) || isspace(key)))
1912             {
1913                 AddChar(key);
1914                 return false;
1915             }
1916         }
1917         return CGameMenuItem::Event(event);
1918     }
1919     case kMenuEventUp:
1920         if (at30)
1921             return false;
1922         return CGameMenuItem::Event(event);
1923     case kMenuEventDown:
1924         if (at30)
1925             return false;
1926         return CGameMenuItem::Event(event);
1927     }
1928     return CGameMenuItem::Event(event);
1929 }
1930 
CGameMenuItemZEditBitmap()1931 CGameMenuItemZEditBitmap::CGameMenuItemZEditBitmap()
1932 {
1933     m_pzText = NULL;
1934     m_nFont = -1;
1935     m_nX = 0;
1936     m_nY = 0;
1937     at20 = NULL;
1938     at24 = 0;
1939     at36 = 0;
1940     at30 = NULL;
1941     at2c = NULL;
1942     bScan = 0;
1943     at28 = 0;
1944     at37 = 0;
1945     at35 = 1;
1946 }
1947 
CGameMenuItemZEditBitmap(char * a1,int a2,int a3,int a4,int a5,char * a6,int a7,char a8,void (* a9)(CGameMenuItemZEditBitmap *,CGameMenuEvent *),int a10)1948 CGameMenuItemZEditBitmap::CGameMenuItemZEditBitmap(char *a1, int a2, int a3, int a4, int a5, char *a6, int a7, char a8, void(*a9)(CGameMenuItemZEditBitmap *, CGameMenuEvent *), int a10)
1949 {
1950     at2c = NULL;
1951     bScan = 0;
1952     at35 = 1;
1953     at37 = 0;
1954     m_pzText = a1;
1955     m_nFont = a2;
1956     m_nX = a3;
1957     m_nY = a4;
1958     m_nWidth = a5;
1959     at20 = a6;
1960     at24 = a7;
1961     at36 = a8;
1962     at30 = a9;
1963     at28 = a10;
1964 }
1965 
AddChar(char ch)1966 void CGameMenuItemZEditBitmap::AddChar(char ch)
1967 {
1968     int i = strlen(at20);
1969     if (i + 1 < at24)
1970     {
1971         at20[i] = ch;
1972         at20[i + 1] = 0;
1973     }
1974 }
1975 
BackChar(void)1976 void CGameMenuItemZEditBitmap::BackChar(void)
1977 {
1978     int i = strlen(at20);
1979     if (i > 0)
1980         at20[i - 1] = 0;
1981 }
1982 
Draw(void)1983 void CGameMenuItemZEditBitmap::Draw(void)
1984 {
1985     int height, width;
1986     gMenuTextMgr.GetFontInfo(m_nFont, NULL, &width, &height);
1987     int shade = bEnable ? 32 : 48;
1988     int pal = bEnable ? 0 : 5;
1989     if (pMenu->IsFocusItem(this))
1990     {
1991         shade = 32-((int)totalclock&63);
1992         char buffer[32];
1993         snprintf(buffer, sizeof(buffer), "DIFFICULTY: %s", zDiffStrings[restoreGameDifficulty[at28]]);
1994         gMenuTextMgr.DrawText(buffer, m_nFont, 20, 50, 32, 0, true);
1995     }
1996     at2c->at24 = -1;
1997     if (bScan)
1998         shade = -128;
1999     if (m_pzText)
2000         gMenuTextMgr.DrawText(m_pzText, m_nFont, m_nX, m_nY, shade, pal, false);
2001     int x = m_nX+m_nWidth-1-(at24+1)*width;
2002     if (at20 && *at20)
2003     {
2004         int width;
2005         gMenuTextMgr.GetFontInfo(m_nFont, at20, &width, NULL);
2006         int shade2;
2007         if (at36)
2008         {
2009             if (bScan)
2010                 shade2 = -128;
2011             else
2012                 shade2 = shade;
2013         }
2014         else
2015         {
2016             if (bScan)
2017                 shade2 = shade;
2018             else
2019                 shade2 = 32;
2020         }
2021         gMenuTextMgr.DrawText(at20, m_nFont, x, m_nY, shade2, 0, false);
2022         x += width;
2023     }
2024     if (bScan && ((int)totalclock & 32))
2025         gMenuTextMgr.DrawText("_", m_nFont, x, m_nY, shade, pal, false);
2026 
2027     int mx = m_nX<<16;
2028     int my = m_nY<<16;
2029     int mw = m_nWidth<<16;
2030     int mh = height<<16;
2031 
2032     if (!gGameMenuMgr.m_bScanning && bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
2033     {
2034         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
2035         {
2036             pMenu->SetFocusItem(this);
2037         }
2038 
2039         if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, mx, my, mw, mh))
2040         {
2041             pMenu->SetFocusItem(this);
2042 
2043             CGameMenuEvent event = { kMenuEventEnter, 0 };
2044 
2045             gGameMenuMgr.m_mousecaught = 1;
2046 
2047             if (Event(event))
2048                 gGameMenuMgr.PostPop();
2049         }
2050     }
2051 }
2052 
Event(CGameMenuEvent & event)2053 bool CGameMenuItemZEditBitmap::Event(CGameMenuEvent &event)
2054 {
2055     static char buffer[256];
2056     // Hack
2057     if (event.at2 == sc_kpad_2 || event.at2 == sc_kpad_4 || event.at2 == sc_kpad_6 || event.at2 == sc_kpad_8)
2058         event.at0 = kMenuEventKey;
2059     switch (event.at0)
2060     {
2061     case kMenuEventEscape:
2062         if (bScan)
2063         {
2064             strncpy(at20, buffer, at24);
2065             at20[at24-1] = 0;
2066             bScan = 0;
2067             gGameMenuMgr.m_bScanning = false;
2068             gSaveGameActive = false;
2069             return false;
2070         }
2071         gSaveGameActive = true;
2072         return true;
2073     case kMenuEventEnter:
2074         if (!at35 || bScan)
2075         {
2076             if (at30)
2077                 at30(this, &event);
2078             if (bScan)
2079             {
2080                 bScan = 0;
2081                 gGameMenuMgr.m_bScanning = false;
2082             }
2083             gSaveGameActive = false;
2084             KB_ClearKeyDown(sc_Enter);
2085             KB_ClearKeyDown(sc_kpad_Enter);
2086             return false;
2087         }
2088         strncpy(buffer, at20, at24);
2089         if (at37)
2090             at20[0] = 0;
2091         buffer[at24-1] = 0;
2092         bScan = 1;
2093         gGameMenuMgr.m_bScanning = true;
2094         return false;
2095     case kMenuEventBackSpace:
2096         if (bScan)
2097             BackChar();
2098         return false;
2099     case kMenuEventKey:
2100     case kMenuEventSpace:
2101     {
2102         char key;
2103         if (bScan && event.at2 < 128)
2104         {
2105             if (keystatus[sc_LeftShift] || keystatus[sc_RightShift])
2106                 key = g_keyAsciiTableShift[event.at2];
2107             else
2108                 key = g_keyAsciiTable[event.at2];
2109             if (at30 && (isalnum(key) || ispunct(key) || isspace(key)))
2110             {
2111                 AddChar(key);
2112                 return false;
2113             }
2114         }
2115         return CGameMenuItem::Event(event);
2116     }
2117     case kMenuEventUp:
2118         if (bScan)
2119             return false;
2120         return CGameMenuItem::Event(event);
2121     case kMenuEventDown:
2122         if (bScan)
2123             return false;
2124         return CGameMenuItem::Event(event);
2125     }
2126     return CGameMenuItem::Event(event);
2127 }
2128 
CGameMenuItemQAV()2129 CGameMenuItemQAV::CGameMenuItemQAV()
2130 {
2131     at20 = NULL;
2132     at24 = NULL;
2133     at28 = 0;
2134     bEnable = 0;
2135 }
2136 
CGameMenuItemQAV(const char * a1,int a2,int a3,int a4,const char * a5,bool widescreen,bool clearbackground)2137 CGameMenuItemQAV::CGameMenuItemQAV(const char *a1, int a2, int a3, int a4, const char *a5, bool widescreen, bool clearbackground)
2138 {
2139     m_nWidth = 0;
2140     m_pzText = a1;
2141     m_nFont = a2;
2142     m_nY = a4;
2143     at20 = a5;
2144     m_nX = a3;
2145     bEnable = 0;
2146     bWideScreen = widescreen;
2147     bClearBackground = clearbackground;
2148 }
2149 
Draw(void)2150 void CGameMenuItemQAV::Draw(void)
2151 {
2152     if (bClearBackground)
2153         videoClearScreen(0);
2154     if (at24)
2155     {
2156         ClockTicks backFC = gFrameClock;
2157         gFrameClock = totalclock;
2158         int nTicks = (int)totalclock - at30;
2159         at30 = (int)totalclock;
2160         at2c -= nTicks;
2161         if (at2c <= 0 || at2c > at28->at10)
2162         {
2163             at2c = at28->at10;
2164         }
2165         at28->Play(at28->at10 - at2c - nTicks, at28->at10 - at2c, -1, NULL);
2166         int wx1, wy1, wx2, wy2;
2167         wx1 = windowxy1.x;
2168         wy1 = windowxy1.y;
2169         wx2 = windowxy2.x;
2170         wy2 = windowxy2.y;
2171         windowxy1.x = 0;
2172         windowxy1.y = 0;
2173         windowxy2.x = xdim-1;
2174         windowxy2.y = ydim-1;
2175         if (bWideScreen)
2176         {
2177             int xdim43 = scale(ydim, 4, 3);
2178             int nCount = (xdim+xdim43-1)/xdim43;
2179             int backX = at28->x;
2180             for (int i = 0; i < nCount; i++)
2181             {
2182                 at28->Draw(at28->at10 - at2c, 10+kQavOrientationLeft, 0, 0);
2183                 at28->x += 320;
2184             }
2185             at28->x = backX;
2186         }
2187         else
2188             at28->Draw(at28->at10 - at2c, 10, 0, 0);
2189 
2190         windowxy1.x = wx1;
2191         windowxy1.y = wy1;
2192         windowxy2.x = wx2;
2193         windowxy2.y = wy2;
2194         gFrameClock = backFC;
2195     }
2196 
2197     if (bEnable && !gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED)
2198     {
2199         pMenu->SetFocusItem(this);
2200 
2201         CGameMenuEvent event = { kMenuEventEnter, 0 };
2202 
2203         gGameMenuMgr.m_mousecaught = 1;
2204 
2205         if (Event(event))
2206             gGameMenuMgr.PostPop();
2207     }
2208 }
2209 
Event(CGameMenuEvent & event)2210 bool CGameMenuItemQAV::Event(CGameMenuEvent &event)
2211 {
2212     switch (event.at0)
2213     {
2214     case kMenuEventLeft:
2215     case kMenuEventBackSpace:
2216         pMenu->FocusPrevItem();
2217         return false;
2218     case kMenuEventRight:
2219     case kMenuEventEnter:
2220     case kMenuEventSpace:
2221         pMenu->FocusNextItem();
2222         return false;
2223     case kMenuEventInit:
2224         if (at20)
2225         {
2226             if (!at28)
2227             {
2228                 at24 = gSysRes.Lookup(at20, "QAV");
2229                 if (!at24)
2230                     ThrowError("Could not load QAV %s\n", at20);
2231                 at28 = (QAV*)gSysRes.Lock(at24);
2232                 at28->nSprite = -1;
2233                 at28->x = m_nX;
2234                 at28->y = m_nY;
2235                 at28->Preload();
2236                 at2c = at28->at10;
2237                 at30 = (int)totalclock;
2238                 return false;
2239             }
2240             gSysRes.Lock(at24);
2241         }
2242         return false;
2243     case kMenuEventDeInit:
2244         if (at20 && at28)
2245         {
2246             gSysRes.Unlock(at24);
2247             if (at24->lockCount == 0)
2248                 at28 = NULL;
2249         }
2250         return false;
2251     }
2252     return CGameMenuItem::Event(event);
2253 }
2254 
Reset(void)2255 void CGameMenuItemQAV::Reset(void)
2256 {
2257     at2c = at28->at10;
2258     at30 = (int)totalclock;
2259 }
2260 
CGameMenuItemZCycleSelect()2261 CGameMenuItemZCycleSelect::CGameMenuItemZCycleSelect()
2262 {
2263     m_pzText = NULL;
2264     m_nFont = 3;
2265     m_nX = 0;
2266     m_nY = 0;
2267     m_nRows = 0;
2268     m_nTopDelta = 0;
2269     m_nFocus = 0;
2270     m_nItems = 0;
2271     m_pzStrings = NULL;
2272     m_pReturn = NULL;
2273 }
2274 
CGameMenuItemZCycleSelect(const char * pzText,int nFont,int nX,int nY,int nWidth,int nRows,int nItems,const char ** pzStrings,int * pReturn,void (* pCallback)(CGameMenuItemZCycleSelect *))2275 CGameMenuItemZCycleSelect::CGameMenuItemZCycleSelect(const char *pzText, int nFont, int nX, int nY, int nWidth, int nRows, int nItems, const char **pzStrings, int *pReturn, void(*pCallback)(CGameMenuItemZCycleSelect *))
2276 {
2277     m_nTopDelta = 0;
2278     m_nFocus = 0;
2279     m_pzText = pzText;
2280     m_nFont = nFont;
2281     m_nX = nX;
2282     m_nY = nY;
2283     m_nWidth = nWidth;
2284     m_nRows = nRows;
2285     m_pCallback = pCallback;
2286     m_nItems = nItems;
2287     m_pzStrings = pzStrings;
2288     m_pReturn = pReturn;
2289 }
2290 
Draw(void)2291 void CGameMenuItemZCycleSelect::Draw(void)
2292 {
2293     int height;
2294     int shade;
2295     gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
2296     int y = m_nY;
2297     int k = m_nFocus - m_nTopDelta;
2298     int nNewFocus = m_nFocus;
2299     bool bClick = false;
2300     for (int i = 0; i < m_nRows; i++, y += height, k++)
2301     {
2302         if (k == m_nFocus)
2303         {
2304             shade = 32;
2305             if (pMenu->IsFocusItem(this))
2306                 shade = 32-((int)totalclock&63);
2307             viewDrawText(3, m_pzStrings[k], m_nX, y, shade, 0, 0, false);
2308         }
2309         else
2310         {
2311             viewDrawText(3, m_pzStrings[k], m_nX, y, 24, 0, 0, false);
2312         }
2313         int mx = m_nX<<16;
2314         int my = y<<16;
2315         int mw = m_nWidth<<16;
2316         int mh = height<<16;
2317         if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
2318         {
2319             if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
2320             {
2321                 nNewFocus = k;
2322             }
2323 
2324             if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, mx, my, mw, mh))
2325             {
2326                 nNewFocus = k;
2327                 bClick = true;
2328             }
2329         }
2330     }
2331     m_nTopDelta += nNewFocus-m_nFocus;
2332     m_nFocus = nNewFocus;
2333     if (bClick)
2334     {
2335         CGameMenuEvent event = { kMenuEventEnter, 0 };
2336 
2337         gGameMenuMgr.m_mousecaught = 1;
2338 
2339         if (Event(event))
2340             gGameMenuMgr.PostPop();
2341     }
2342 }
2343 
Event(CGameMenuEvent & event)2344 bool CGameMenuItemZCycleSelect::Event(CGameMenuEvent &event)
2345 {
2346     switch (event.at0)
2347     {
2348     case kMenuEventUp:
2349         if (event.at2 == sc_Tab || m_nFocus == 0)
2350         {
2351             pMenu->FocusPrevItem();
2352             return false;
2353         }
2354         m_nFocus--;
2355         if (m_nTopDelta > 0)
2356             m_nTopDelta--;
2357         return false;
2358     case kMenuEventDown:
2359         if (event.at2 == sc_Tab || m_nFocus == m_nItems-1)
2360         {
2361             pMenu->FocusNextItem();
2362             return false;
2363         }
2364         m_nFocus++;
2365         if (m_nTopDelta+1 < m_nRows)
2366             m_nTopDelta++;
2367         return false;
2368     case kMenuEventEnter:
2369         if (m_pCallback)
2370             m_pCallback(this);
2371         *m_pReturn = m_nFocus;
2372         return true;
2373     case kMenuEventScrollUp:
2374         if (m_nFocus-m_nTopDelta > 0)
2375         {
2376             m_nTopDelta++;
2377             if (m_nTopDelta>0)
2378             {
2379                 m_nFocus--;
2380                 m_nTopDelta--;
2381             }
2382         }
2383         return false;
2384     case kMenuEventScrollDown:
2385         if (m_nFocus-m_nTopDelta+m_nRows < m_nItems)
2386         {
2387             m_nTopDelta--;
2388             if (m_nTopDelta+1 < m_nRows)
2389             {
2390                 m_nFocus++;
2391                 m_nTopDelta++;
2392             }
2393         }
2394         return false;
2395     }
2396     return CGameMenuItem::Event(event);
2397 }
2398 
MouseEvent(CGameMenuEvent & event)2399 bool CGameMenuItemZCycleSelect::MouseEvent(CGameMenuEvent &event)
2400 {
2401     event.at0 = kMenuEventNone;
2402     if (MOUSEACTIVECONDITIONAL(MOUSE_GetButtons()&WHEELUP_MOUSE))
2403     {
2404         gGameMenuMgr.m_mouselastactivity = (int)totalclock;
2405         MOUSE_ClearButton(WHEELUP_MOUSE);
2406         event.at0 = kMenuEventScrollUp;
2407     }
2408     else if (MOUSEACTIVECONDITIONAL(MOUSE_GetButtons()&WHEELDOWN_MOUSE))
2409     {
2410         gGameMenuMgr.m_mouselastactivity = (int)totalclock;
2411         MOUSE_ClearButton(WHEELDOWN_MOUSE);
2412         event.at0 = kMenuEventScrollDown;
2413     }
2414     else
2415         return CGameMenuItem::MouseEvent(event);
2416     return event.at0 != kMenuEventNone;
2417 }
2418 
CGameMenuItemZCycle()2419 CGameMenuItemZCycle::CGameMenuItemZCycle()
2420 {
2421     m_pzText = NULL;
2422     m_nFocus = 0;
2423     m_nItems = 0;
2424     m_pCallback = NULL;
2425     m_pCallbackSelect = NULL;
2426     m_pMenuSelect = NULL;
2427     m_pItemSelectTitle = NULL;
2428     m_pItemSelect = NULL;
2429     m_nMenuSelectReturn = -1;
2430 }
2431 
CGameMenuItemZCycle(const char * a1,int a2,int a3,int a4,int a5,int a6,void (* a7)(CGameMenuItemZCycle *),const char ** a8,int a9,int a10,bool bMenu,void (* pCallbackSelect)(CGameMenuItemZCycleSelect *))2432 CGameMenuItemZCycle::CGameMenuItemZCycle(const char *a1, int a2, int a3, int a4, int a5, int a6, void(*a7)(CGameMenuItemZCycle *), const char **a8, int a9, int a10, bool bMenu, void(*pCallbackSelect)(CGameMenuItemZCycleSelect*))
2433 {
2434     m_pzText = a1;
2435     m_nFont = a2;
2436     m_nX = a3;
2437     m_nY = a4;
2438     m_nFocus = 0;
2439     m_nWidth = a5;
2440     m_nAlign = a6;
2441     m_pCallback = a7;
2442     m_pCallbackSelect = pCallbackSelect;
2443     m_nItems = 0;
2444     m_bMenu = bMenu;
2445     m_pMenuSelect = NULL;
2446     m_pItemSelectTitle = NULL;
2447     m_pItemSelect = NULL;
2448     m_nMenuSelectReturn = -1;
2449     SetTextArray(a8, a9, a10);
2450 }
2451 
~CGameMenuItemZCycle()2452 CGameMenuItemZCycle::~CGameMenuItemZCycle()
2453 {
2454     m_pzText = NULL;
2455     m_nFocus = 0;
2456     m_nItems = 0;
2457     m_pCallback = NULL;
2458     m_pCallbackSelect = NULL;
2459     m_pMenuSelect = NULL;
2460     m_pItemSelectTitle = NULL;
2461     m_pItemSelect = NULL;
2462     m_nMenuSelectReturn = -1;
2463     memset(m_pzStrings, 0, sizeof(m_pzStrings));
2464 }
2465 
Draw(void)2466 void CGameMenuItemZCycle::Draw(void)
2467 {
2468     int width = 0, height = 0;
2469     int shade = bEnable ? 32 : 48;
2470     int pal = bEnable ? 0 : 5;
2471     if (pMenu->IsFocusItem(this))
2472         shade = 32-((int)totalclock&63);
2473     int x = m_nX;
2474     int y = m_nY;
2475 
2476     if (m_nMenuSelectReturn != -1)
2477     {
2478         m_nFocus = m_nMenuSelectReturn;
2479         if (m_pCallback)
2480             m_pCallback(this);
2481         m_nMenuSelectReturn = -1;
2482     }
2483 
2484     if (m_pzText)
2485     {
2486         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, &height);
2487         switch (m_nAlign)
2488         {
2489         case 1:
2490             x = m_nX+m_nWidth/2-width/2;
2491             break;
2492         case 2:
2493             x = m_nX+m_nWidth-1-width;
2494             break;
2495         case 0:
2496         default:
2497             break;
2498         }
2499         gMenuTextMgr.DrawText(m_pzText, m_nFont, x, y, shade, pal, false);
2500     }
2501     const char *pzText;
2502     if (!m_nItems)
2503         pzText = "????";
2504     else
2505         pzText = m_pzStrings[m_nFocus];
2506     dassert(pzText != NULL);
2507     gMenuTextMgr.GetFontInfo(m_nFont, pzText, &width, NULL);
2508     gMenuTextMgr.DrawText(pzText, m_nFont, m_nX + m_nWidth - 1 - width, y, shade, pal, false);
2509     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, x<<16, y<<16, m_nWidth<<16, height<<16)))
2510     {
2511         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, x<<16, y<<16, m_nWidth<<16, height<<16)))
2512         {
2513             pMenu->SetFocusItem(this);
2514         }
2515 
2516         if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, x<<16, y<<16, m_nWidth<<16, height<<16))
2517         {
2518             pMenu->SetFocusItem(this);
2519 
2520             CGameMenuEvent event = { kMenuEventEnter, 0 };
2521 
2522             gGameMenuMgr.m_mousecaught = 1;
2523 
2524             if (Event(event))
2525                 gGameMenuMgr.PostPop();
2526         }
2527     }
2528 }
2529 
Event(CGameMenuEvent & event)2530 bool CGameMenuItemZCycle::Event(CGameMenuEvent &event)
2531 {
2532     switch (event.at0)
2533     {
2534     case kMenuEventEnter:
2535         if (m_bMenu)
2536         {
2537             if (m_pMenuSelect)
2538             {
2539                 delete m_pMenuSelect;
2540                 m_pMenuSelect = NULL;
2541             }
2542             if (m_pItemSelectTitle)
2543             {
2544                 delete m_pItemSelectTitle;
2545                 m_pItemSelectTitle = NULL;
2546             }
2547             if (m_pItemSelect)
2548             {
2549                 delete m_pItemSelect;
2550                 m_pItemSelect = NULL;
2551             }
2552             m_pMenuSelect = new CGameMenu();
2553             dassert(m_pMenuSelect != NULL);
2554             strncpy(m_zTitle, m_pzText, kMaxTitleLength);
2555             int l = strlen(m_zTitle);
2556             if (l > 0 && m_zTitle[l-1] == ':')
2557                 l--;
2558             m_zTitle[l] = 0;
2559             m_pItemSelectTitle = new CGameMenuItemTitle(m_zTitle, 1, 160, 20, 2038);
2560             dassert(m_pItemSelectTitle != NULL);
2561             m_pItemSelect = new CGameMenuItemZCycleSelect("", 3, 100, 40, 100, 16, m_nItems, m_pzStrings, &m_nMenuSelectReturn, m_pCallbackSelect);
2562             dassert(m_pItemSelect != NULL);
2563             m_pMenuSelect->Add(m_pItemSelectTitle, false);
2564             m_pMenuSelect->Add(m_pItemSelect, true);
2565             m_pMenuSelect->Add(&itemBloodQAV, false);
2566             gGameMenuMgr.Push(m_pMenuSelect, -1);
2567             return false;
2568         }
2569         fallthrough__;
2570     case kMenuEventRight:
2571     case kMenuEventSpace:
2572         Next();
2573         if (m_pCallback)
2574             m_pCallback(this);
2575         return false;
2576     case kMenuEventLeft:
2577         Prev();
2578         if (m_pCallback)
2579             m_pCallback(this);
2580         return false;
2581     case kMenuEventDeInit:
2582         if (m_pMenuSelect)
2583         {
2584             delete m_pMenuSelect;
2585             m_pMenuSelect = NULL;
2586         }
2587         if (m_pItemSelectTitle)
2588         {
2589             delete m_pItemSelectTitle;
2590             m_pItemSelectTitle = NULL;
2591         }
2592         if (m_pItemSelect)
2593         {
2594             delete m_pItemSelect;
2595             m_pItemSelect = NULL;
2596         }
2597         return false;
2598     }
2599     return CGameMenuItem::Event(event);
2600 }
2601 
Add(const char * pItem,bool active)2602 void CGameMenuItemZCycle::Add(const char *pItem, bool active)
2603 {
2604     dassert(pItem != NULL);
2605     dassert(m_nItems < kMaxGameCycleItems);
2606     m_pzStrings[m_nItems] = pItem;
2607     if (active)
2608         m_nFocus = m_nItems;
2609     m_nItems++;
2610 }
2611 
Next(void)2612 void CGameMenuItemZCycle::Next(void)
2613 {
2614     if (m_nItems > 0)
2615     {
2616         m_nFocus++;
2617         if (m_nFocus >= m_nItems)
2618             m_nFocus = 0;
2619     }
2620 }
2621 
Prev(void)2622 void CGameMenuItemZCycle::Prev(void)
2623 {
2624     if (m_nItems > 0)
2625     {
2626         m_nFocus--;
2627         if (m_nFocus < 0)
2628             m_nFocus += m_nItems;
2629     }
2630 }
2631 
Clear(void)2632 void CGameMenuItemZCycle::Clear(void)
2633 {
2634     m_nItems = m_nFocus = 0;
2635     memset(m_pzStrings, 0, sizeof(m_pzStrings));
2636 }
2637 
SetTextArray(const char ** pTextArray,int nTextPtrCount,int nIndex)2638 void CGameMenuItemZCycle::SetTextArray(const char **pTextArray, int nTextPtrCount, int nIndex)
2639 {
2640     Clear();
2641     dassert(nTextPtrCount <= kMaxGameCycleItems);
2642     for (int i = 0; i < nTextPtrCount; i++)
2643         Add(pTextArray[i], false);
2644     SetTextIndex(nIndex);
2645 }
2646 
SetTextIndex(int nIndex)2647 void CGameMenuItemZCycle::SetTextIndex(int nIndex)
2648 {
2649     m_nFocus = ClipRange(nIndex, 0, m_nItems);
2650 }
2651 
CGameMenuItemYesNoQuit()2652 CGameMenuItemYesNoQuit::CGameMenuItemYesNoQuit()
2653 {
2654     m_pzText = NULL;
2655     m_nRestart = 0;
2656 }
2657 
CGameMenuItemYesNoQuit(const char * a1,int a2,int a3,int a4,int a5,int a6,int a7)2658 CGameMenuItemYesNoQuit::CGameMenuItemYesNoQuit(const char *a1, int a2, int a3, int a4, int a5, int a6, int a7)
2659 {
2660     m_pzText = a1;
2661     m_nFont = a2;
2662     m_nX = a3;
2663     m_nY = a4;
2664     m_nWidth = a5;
2665     at20 = a6;
2666     m_nRestart = a7;
2667 }
2668 
Draw(void)2669 void CGameMenuItemYesNoQuit::Draw(void)
2670 {
2671     if (!m_pzText) return;
2672     int shade = 32;
2673     if (pMenu->IsFocusItem(this))
2674         shade = 32-((int)totalclock&63);
2675     int width;
2676     int x = m_nX;
2677     switch (at20)
2678     {
2679     case 1:
2680         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
2681         x = m_nX+m_nWidth/2-width/2;
2682         break;
2683     case 2:
2684         gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
2685         x = m_nX+m_nWidth-1-width;
2686         break;
2687     case 0:
2688     default:
2689         break;
2690     }
2691     gMenuTextMgr.DrawText(m_pzText, m_nFont, x, m_nY, shade, 0, true);
2692 
2693     if (bEnable && !gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED)
2694     {
2695         pMenu->SetFocusItem(this);
2696 
2697         CGameMenuEvent event = { kMenuEventEnter, 0 };
2698 
2699         gGameMenuMgr.m_mousecaught = 1;
2700 
2701         if (Event(event))
2702             gGameMenuMgr.PostPop();
2703     }
2704 }
2705 
2706 extern void Restart(CGameMenuItemChain *pItem);
2707 extern void Quit(CGameMenuItemChain *pItem);
2708 
Event(CGameMenuEvent & event)2709 bool CGameMenuItemYesNoQuit::Event(CGameMenuEvent &event)
2710 {
2711     switch (event.at0)
2712     {
2713     case kMenuEventKey:
2714         if (event.at2 == sc_Y)
2715         {
2716             if (m_nRestart)
2717                 Restart(NULL);
2718             else
2719                 Quit(NULL);
2720         }
2721         else if (event.at2 == sc_N)
2722             gGameMenuMgr.Pop();
2723         return false;
2724     case kMenuEventEnter:
2725         if (m_nRestart)
2726             Restart(NULL);
2727         else
2728             Quit(NULL);
2729         return false;
2730     }
2731     return CGameMenuItem::Event(event);
2732 }
2733 
CGameMenuItemPicCycle()2734 CGameMenuItemPicCycle::CGameMenuItemPicCycle()
2735 {
2736     m_pzText = NULL;
2737     at24 = 0;
2738     m_nItems = 0;
2739     atb0 = 0;
2740     at2c = 0;
2741     atb4 = 0;
2742 }
2743 
CGameMenuItemPicCycle(int a1,int a2,void (* a3)(CGameMenuItemPicCycle *),int * a4,int a5,int a6)2744 CGameMenuItemPicCycle::CGameMenuItemPicCycle(int a1, int a2, void(*a3)(CGameMenuItemPicCycle *), int *a4, int a5, int a6)
2745 {
2746     m_nWidth = 0;
2747     at24 = 0;
2748     m_nItems = 0;
2749     m_nX = a1;
2750     m_nY = a2;
2751     atb0 = a3;
2752     atb4 = 0;
2753     SetPicArray(a4, a5, a6);
2754 }
2755 
Draw(void)2756 void CGameMenuItemPicCycle::Draw(void)
2757 {
2758     videoSetViewableArea(0, 0, xdim - 1, ydim - 1);
2759     if (atb4)
2760         rotatesprite(0, 0, 65536, 0, atb4, 0, 0, 82, 0, 0, xdim - 1, ydim - 1);
2761     if (at30[at24])
2762         rotatesprite(0, 0, 65536, 0, at30[at24], 0, 0, 82, 0, 0, xdim - 1, ydim - 1);
2763 }
2764 
Event(CGameMenuEvent & event)2765 bool CGameMenuItemPicCycle::Event(CGameMenuEvent &event)
2766 {
2767     switch (event.at0)
2768     {
2769     case kMenuEventRight:
2770     case kMenuEventEnter:
2771     case kMenuEventSpace:
2772         Next();
2773         if (atb0)
2774             atb0(this);
2775         return false;
2776     case kMenuEventLeft:
2777         Prev();
2778         if (atb0)
2779             atb0(this);
2780         return false;
2781     }
2782     return CGameMenuItem::Event(event);
2783 }
2784 
Add(int nItem,bool active)2785 void CGameMenuItemPicCycle::Add(int nItem, bool active)
2786 {
2787     dassert(m_nItems < kMaxPicCycleItems);
2788     at30[m_nItems] = nItem;
2789     if (active)
2790         at24 = m_nItems;
2791     m_nItems++;
2792 }
2793 
Next(void)2794 void CGameMenuItemPicCycle::Next(void)
2795 {
2796     if (m_nItems > 0)
2797     {
2798         at24++;
2799         if (at24 >= m_nItems)
2800             at24 = 0;
2801     }
2802 }
2803 
Prev(void)2804 void CGameMenuItemPicCycle::Prev(void)
2805 {
2806     if (m_nItems > 0)
2807     {
2808         at24--;
2809         if (at24 < 0)
2810             at24 += m_nItems;
2811     }
2812 }
2813 
Clear(void)2814 void CGameMenuItemPicCycle::Clear(void)
2815 {
2816     m_nItems = at24 = 0;
2817     memset(at30, 0, sizeof(at30));
2818     at2c = 0;
2819 }
2820 
SetPicArray(int * pArray,int nTileCount,int nIndex)2821 void CGameMenuItemPicCycle::SetPicArray(int *pArray, int nTileCount, int nIndex)
2822 {
2823     Clear();
2824     at2c = 0;
2825     dassert(nTileCount <= kMaxPicCycleItems);
2826     for (int i = 0; i < nTileCount; i++)
2827         Add(pArray[i], false);
2828     SetPicIndex(nIndex);
2829 }
2830 
SetPicIndex(int nIndex)2831 void CGameMenuItemPicCycle::SetPicIndex(int nIndex)
2832 {
2833     at24 = ClipRange(nIndex, 0, m_nItems);
2834 }
2835 
CGameMenuItemPassword()2836 CGameMenuItemPassword::CGameMenuItemPassword()
2837 {
2838     at37 = 0;
2839     m_pzText = NULL;
2840     at36 = 0;
2841     at32 = 0;
2842     at5b = 0;
2843 }
2844 
CGameMenuItemPassword(const char * a1,int a2,int a3,int a4)2845 CGameMenuItemPassword::CGameMenuItemPassword(const char *a1, int a2, int a3, int a4)
2846 {
2847     at37 = 0;
2848     m_nWidth = 0;
2849     at36 = 0;
2850     at32 = 0;
2851     at5b = 0;
2852     m_pzText = a1;
2853     m_nFont = a2;
2854     m_nX = a3;
2855     m_nY = a4;
2856 }
2857 
2858 const char *kCheckPasswordMsg = "ENTER PASSWORD: ";
2859 const char *kOldPasswordMsg = "ENTER OLD PASSWORD: ";
2860 const char *kNewPasswordMsg = "ENTER NEW PASSWORD: ";
2861 const char *kInvalidPasswordMsg = "INVALID PASSWORD.";
2862 
Draw(void)2863 void CGameMenuItemPassword::Draw(void)
2864 {
2865     bool focus = pMenu->IsFocusItem(this);
2866     int shade = 32;
2867     int shadef = 32-((int)totalclock&63);
2868     int width;
2869     switch (at37)
2870     {
2871     case 1:
2872     case 2:
2873     case 3:
2874         switch (at37)
2875         {
2876         case 1:
2877             strcpy(at3b, kCheckPasswordMsg);
2878             break;
2879         case 2:
2880             strcpy(at3b, kOldPasswordMsg);
2881             break;
2882         case 3:
2883             strcpy(at3b, kNewPasswordMsg);
2884             break;
2885         }
2886         for (int i = 0; i < at32; i++)
2887             strcat(at3b, "*");
2888         strcat(at3b, "_");
2889         gMenuTextMgr.GetFontInfo(m_nFont, at3b, &width, NULL);
2890         gMenuTextMgr.DrawText(at3b, m_nFont, m_nX-width/2, m_nY+20, shadef, 0, false);
2891         shadef = 32;
2892         break;
2893     case 4:
2894         if (((int)totalclock - at5b) & 32)
2895         {
2896             gMenuTextMgr.GetFontInfo(m_nFont, kInvalidPasswordMsg, &width, NULL);
2897             gMenuTextMgr.DrawText(kInvalidPasswordMsg, m_nFont, m_nX - width / 2, m_nY + 20, shade, 0, false);
2898         }
2899         if (at5b && totalclock-at5b > 256)
2900         {
2901             at5b = 0;
2902             at37 = 0;
2903         }
2904         break;
2905     }
2906     gMenuTextMgr.GetFontInfo(m_nFont, m_pzText, &width, NULL);
2907     gMenuTextMgr.DrawText(m_pzText, m_nFont, m_nX-width/2, m_nY, focus ? shadef : shade, 0, false);
2908 }
2909 
Event(CGameMenuEvent & event)2910 bool CGameMenuItemPassword::Event(CGameMenuEvent &event)
2911 {
2912     switch (at37)
2913     {
2914     case 0:
2915     case 4:
2916         if (event.at0 == kMenuEventEnter)
2917         {
2918             at29[0] = 0;
2919             if (strcmp(at20, ""))
2920                 at37 = 2;
2921             else
2922                 at37 = 3;
2923             return false;
2924         }
2925         return CGameMenuItem::Event(event);
2926     case 1:
2927     case 2:
2928     case 3:
2929         switch (event.at0)
2930         {
2931         case kMenuEventEnter:
2932             switch (at37)
2933             {
2934             case 1:
2935                 at36 = strcmp(at20,at29) == 0;
2936                 if (at36)
2937                     at37 = 0;
2938                 else
2939                     at37 = 4;
2940                 if (!at36)
2941                 {
2942                     at5b = (int)totalclock;
2943                     pMenu->FocusPrevItem();
2944                 }
2945                 else
2946                 {
2947                     at5f->at20 = 0;
2948                     at5f->Draw();
2949                     gbAdultContent = false;
2950                     // NUKE-TODO:
2951                     //CONFIG_WriteAdultMode();
2952                     pMenu->FocusPrevItem();
2953                 }
2954                 return false;
2955             case 2:
2956                 at36 = strcmp(at20,at29) == 0;
2957                 if (at36)
2958                     at37 = 0;
2959                 else
2960                     at37 = 4;
2961                 if (at36)
2962                 {
2963                     strcpy(at20, "");
2964                     strcpy(gzAdultPassword, "");
2965                     // NUKE-TODO:
2966                     //CONFIG_WriteAdultMode();
2967                     at37 = 0;
2968                 }
2969                 else
2970                     at5b = (int)totalclock;
2971                 return false;
2972             case 3:
2973                 strcpy(at20, at29);
2974                 strcpy(at20, gzAdultPassword);
2975                 strcpy(gzAdultPassword, "");
2976                 // NUKE-TODO:
2977                 //CONFIG_WriteAdultMode();
2978                 at37 = 0;
2979                 return false;
2980             }
2981             break;
2982         case kMenuEventEscape:
2983             at37 = 0;
2984             Draw();
2985             return false;
2986         case kMenuEventKey:
2987             if (at32 < 8)
2988             {
2989                 char key = Btoupper(g_keyAsciiTable[event.at2]);
2990                 if (isalnum(key) || ispunct(key) || isspace(key))
2991                 {
2992                     at29[at32++] = key;
2993                     at29[at32] = 0;
2994                 }
2995             }
2996             return false;
2997         case kMenuEventBackSpace:
2998             if (at32 > 0)
2999                 at29[--at32] = 0;
3000             return false;
3001         case kMenuEventLeft:
3002         case kMenuEventRight:
3003         case kMenuEventSpace:
3004             return false;
3005         }
3006     }
3007     return CGameMenuItem::Event(event);
3008 }
3009 
CGameMenuFileSelect(const char * _pzText,int _nFont,int _x,int _y,int _nWidth,const char * _startdir,const char * _pattern,char * _destination,void (* _onFileSelectedEventHandler)(),const char _doPop)3010 CGameMenuFileSelect::CGameMenuFileSelect(const char* _pzText, int _nFont, int _x, int _y, int _nWidth, const char* _startdir, const char* _pattern, char* _destination, void(*_onFileSelectedEventHandler)(), const char _doPop)
3011 {
3012     m_pzText = _pzText;
3013     m_nFont = _nFont;
3014     m_nX = _x;
3015     m_nY = _y;
3016     m_nWidth = _nWidth;
3017     startdir = _startdir;
3018     pattern = _pattern;
3019     destination = _destination;
3020     onFileSelectedEventHandler = _onFileSelectedEventHandler;
3021     doPop = _doPop;
3022 }
3023 
xdim_from_320_16(int32_t x)3024 static int32_t xdim_from_320_16(int32_t x)
3025 {
3026     const int32_t screenwidth = scale(240<<16, xdim, ydim);
3027     return scale(x + (screenwidth>>1) - (160<<16), xdim, screenwidth);
3028 }
ydim_from_200_16(int32_t y)3029 static int32_t ydim_from_200_16(int32_t y)
3030 {
3031     y = mulscale16(y + rotatesprite_y_offset - (200<<15), rotatesprite_yxaspect) + (200<<15);
3032     return scale(y, ydim, 200<<16);
3033 }
3034 
Menu_BlackRectangle(int32_t x,int32_t y,int32_t width,int32_t height,int32_t orientation)3035 static void Menu_BlackRectangle(int32_t x, int32_t y, int32_t width, int32_t height, int32_t orientation)
3036 {
3037     const int shadow_pal = 5;
3038     const int32_t xscale = divscale16(width, tilesiz[0].x<<16), yscale = divscale16(height, tilesiz[0].y<<16);
3039 
3040     rotatesprite_(x, y, max(xscale, yscale), 0, 0, 127, shadow_pal, (orientation&(1|32))|2|8|16, 0, 0, xdim_from_320_16(x), ydim_from_200_16(y), xdim_from_320_16(x + width), ydim_from_200_16(y + height));
3041 }
3042 
3043 static char tempbuf[1024];
3044 #define USERMAPENTRYLENGTH 25
3045 
Menu_Run_AbbreviateNameIntoBuffer(const char * name,int32_t entrylength)3046 static void Menu_Run_AbbreviateNameIntoBuffer(const char* name, int32_t entrylength)
3047 {
3048     int32_t len = Bstrlen(name);
3049     Bstrncpy(tempbuf, name, ARRAY_SIZE(tempbuf));
3050     if (len > entrylength)
3051     {
3052         len = entrylength-3;
3053         tempbuf[len] = 0;
3054         while (len < entrylength)
3055             tempbuf[len++] = '.';
3056     }
3057     tempbuf[len] = 0;
3058 }
3059 
Draw(void)3060 void CGameMenuFileSelect::Draw(void)
3061 {
3062     int height, width;
3063     vec2_t format[2] = { 40, 45, 164, 45 };
3064 
3065     gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
3066 
3067     // black translucent background underneath file lists
3068     Menu_BlackRectangle((m_nX<<16) + (36<<16), (m_nY<<16) + (42<<16), 248<<16, 123<<16, 1|32);
3069 
3070     // path
3071     Bsnprintf(tempbuf, sizeof(tempbuf), "Path: %s", destination);
3072     gMenuTextMgr.DrawText(tempbuf, m_nFont, m_nX+40, m_nY+32, 0, 0, 0);
3073 
3074     int const maxRows = (162 - 40) / height;
3075     bool bClick = false;
3076 
3077     for (int i = 0; i < 2; ++i)
3078     {
3079         if (findhigh[i])
3080         {
3081             BUILDVFS_FIND_REC *dir;
3082             int32_t y = 0;
3083 
3084             int32_t rows = 0;
3085             for (dir = findhigh[i]->usera; dir; dir = dir->next, rows++)
3086             {
3087             }
3088             y = format[i].y;
3089 
3090             int32_t row = 0;
3091 
3092             for (dir = findhigh[i]->usera; dir; dir = dir->next, row++)
3093             {
3094                 bool const bSelected = dir == findhigh[i] && currentList == i;
3095 
3096                 // pal = dir->source==BUILDVFS_SOURCE_ZIP ? 8 : 2
3097 
3098                 Menu_Run_AbbreviateNameIntoBuffer(dir->name, USERMAPENTRYLENGTH);
3099 
3100                 const int32_t thisx = format[i].x;
3101                 const int32_t thisr = row - nTopDelta[i];
3102                 const int32_t thisy = y + thisr * height;
3103 
3104                 if (0 <= thisr && thisr < maxRows)
3105                 {
3106                     gMenuTextMgr.GetFontInfo(m_nFont, tempbuf, &width, &height);
3107                     viewDrawText(m_nFont, tempbuf, thisx, thisy, bSelected ? 32-((int)totalclock&63) : 32, 0, 0, 0);
3108                     int mx = thisx<<16;
3109                     int my = thisy<<16;
3110                     int mw = width<<16;
3111                     int mh = height<<16;
3112                     if (bEnable && MOUSEACTIVECONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousepos, mx, my, mw, mh)))
3113                     {
3114                         if (MOUSEWATCHPOINTCONDITIONAL(!gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_prevmousepos, mx, my, mw, mh)))
3115                         {
3116                             findhigh[i] = dir;
3117                             currentList = i;
3118                         }
3119 
3120                         if (!gGameMenuMgr.m_mousecaught && g_mouseClickState == MOUSE_RELEASED && !gGameMenuMgr.MouseOutsideBounds(&gGameMenuMgr.m_mousedownpos, mx, my, mw, mh))
3121                         {
3122                             findhigh[i] = dir;
3123                             currentList = i;
3124                             gGameMenuMgr.m_mousecaught = 1;
3125                             bClick = true;
3126                         }
3127                     }
3128                 }
3129             }
3130         }
3131     }
3132     if (bClick)
3133     {
3134         gGameMenuMgr.m_mousecaught = 1;
3135 
3136         if (Select())
3137             gGameMenuMgr.PostPop();
3138     }
3139 }
3140 
klistbookends(BUILDVFS_FIND_REC * start)3141 void klistbookends(BUILDVFS_FIND_REC *start)
3142 {
3143     auto end = start;
3144 
3145     if (!start)
3146         return;
3147 
3148     while (start->prev)
3149         start = start->prev;
3150 
3151     while (end->next)
3152         end = end->next;
3153 
3154     int i = 0;
3155 
3156     for (auto n = start; n; n = n->next)
3157     {
3158         n->type = i; // overload this...
3159         n->usera = start;
3160         n->userb = end;
3161         i++;
3162     }
3163 }
3164 
Event(CGameMenuEvent & event)3165 bool CGameMenuFileSelect::Event(CGameMenuEvent &event)
3166 {
3167     switch (event.at0)
3168     {
3169     case kMenuEventInit:
3170         FileSelectInit();
3171         break;
3172     case kMenuEventDeInit:
3173         fnlist_clearnames(&fnlist);
3174         break;
3175     case kMenuEventEscape:
3176         destination[0] = 0;
3177         return true;
3178     case kMenuEventEnter:
3179         return Select();
3180     case kMenuEventKey:
3181         switch (event.at2)
3182         {
3183         case sc_Home:
3184             findhigh[currentList] = findhigh[currentList]->usera;
3185             break;
3186         case sc_End:
3187             findhigh[currentList] = findhigh[currentList]->userb;
3188             break;
3189         }
3190         MovementVerify();
3191         return false;
3192     case kMenuEventUp:
3193         if (findhigh[currentList] != NULL)
3194         {
3195             if (findhigh[currentList]->prev)
3196                 findhigh[currentList] = findhigh[currentList]->prev;
3197             else
3198                 findhigh[currentList] = findhigh[currentList]->userb;
3199         }
3200         MovementVerify();
3201         return false;
3202     case kMenuEventDown:
3203         if (findhigh[currentList] != NULL)
3204         {
3205             if (findhigh[currentList]->next)
3206                 findhigh[currentList] = findhigh[currentList]->next;
3207             else
3208                 findhigh[currentList] = findhigh[currentList]->usera;
3209         }
3210         MovementVerify();
3211         return false;
3212     case kMenuEventLeft:
3213     case kMenuEventRight:
3214         if ((currentList ? fnlist.numdirs : fnlist.numfiles) > 0)
3215             currentList = !currentList;
3216         MovementVerify();
3217         return false;
3218     case kMenuEventScrollUp:
3219         if (findhigh[currentList] != NULL)
3220         {
3221             if (findhigh[currentList]->prev)
3222             {
3223                 findhigh[currentList] = findhigh[currentList]->prev;
3224                 nTopDelta[currentList]--;
3225             }
3226         }
3227         MovementVerify();
3228         return false;
3229     case kMenuEventScrollDown:
3230         if (findhigh[currentList] != NULL)
3231         {
3232             if (findhigh[currentList]->next)
3233             {
3234                 findhigh[currentList] = findhigh[currentList]->next;
3235                 nTopDelta[currentList]++;
3236             }
3237         }
3238         MovementVerify();
3239         return false;
3240     default:
3241         break;
3242     }
3243     return CGameMenuItem::Event(event);
3244 }
3245 
Select(void)3246 bool CGameMenuFileSelect::Select(void)
3247 {
3248     if (!findhigh[currentList])
3249         return false;
3250 
3251     char name[BMAX_PATH];
3252     Bstrcpy(name, findhigh[currentList]->name);
3253     if (!Bstrcmp(name, ".."))
3254     {
3255         SetDestinationToParentDir();
3256     }
3257     else
3258     {
3259         RemoveFilenameFromDestination();
3260         Bstrcat(destination, name);
3261     }
3262 
3263     if (currentList == 0)
3264     {
3265         Bstrcat(destination, "/");
3266         Bcorrectfilename(destination, 1);
3267 
3268         FileSelectInit();
3269         return false;
3270     }
3271 
3272     if (onFileSelectedEventHandler)
3273         onFileSelectedEventHandler();
3274 
3275     if (!doPop)
3276         return false;
3277 
3278     return true;
3279 }
3280 
FileSelectInit(void)3281 void CGameMenuFileSelect::FileSelectInit(void)
3282 {
3283     fnlist_clearnames(&fnlist);
3284 
3285     if (destination[0] == 0)
3286     {
3287         BDIR * usermaps = Bopendir(startdir);
3288         if (usermaps)
3289         {
3290             Bclosedir(usermaps);
3291             Bstrcpy(destination, startdir);
3292         }
3293         else
3294             Bstrcpy(destination, "./");
3295     }
3296     Bcorrectfilename(destination, 1);
3297 
3298     fnlist_getnames(&fnlist, destination, pattern, 0, 0);
3299     findhigh[0] = fnlist.finddirs;
3300     findhigh[1] = fnlist.findfiles;
3301 
3302     for (int i = 0; i < 2; ++i)
3303     {
3304         nTopDelta[i] = 0;
3305         klistbookends(findhigh[i]);
3306     }
3307 
3308     currentList = 0;
3309     if (findhigh[1])
3310         currentList = 1;
3311 
3312     KB_FlushKeyboardQueue();
3313     KB_FlushKeyboardQueueScans();
3314 }
3315 
MovementVerify(void)3316 void CGameMenuFileSelect::MovementVerify(void)
3317 {
3318     int height;
3319     if (!findhigh[currentList])
3320         return;
3321     gMenuTextMgr.GetFontInfo(m_nFont, NULL, NULL, &height);
3322     int32_t rows = 0;
3323     for (auto dir = findhigh[currentList]->usera; dir; dir = dir->next, rows++)
3324     {
3325     }
3326     int const maxRows = (162 - 40) / height;
3327     int item = findhigh[currentList]->type - nTopDelta[currentList];
3328     if (item < 0)
3329         nTopDelta[currentList] += item;
3330     else if (item >= maxRows)
3331         nTopDelta[currentList] += item - maxRows + 1;
3332     if (nTopDelta[currentList] > rows - maxRows)
3333         nTopDelta[currentList] = rows - maxRows;
3334     if (nTopDelta[currentList] < 0)
3335         nTopDelta[currentList] = 0;
3336 }
3337 
MouseEvent(CGameMenuEvent & event)3338 bool CGameMenuFileSelect::MouseEvent(CGameMenuEvent &event)
3339 {
3340     event.at0 = kMenuEventNone;
3341     if (MOUSEACTIVECONDITIONAL(MOUSE_GetButtons()&WHEELUP_MOUSE))
3342     {
3343         gGameMenuMgr.m_mouselastactivity = (int)totalclock;
3344         MOUSE_ClearButton(WHEELUP_MOUSE);
3345         event.at0 = kMenuEventScrollUp;
3346     }
3347     else if (MOUSEACTIVECONDITIONAL(MOUSE_GetButtons()&WHEELDOWN_MOUSE))
3348     {
3349         gGameMenuMgr.m_mouselastactivity = (int)totalclock;
3350         MOUSE_ClearButton(WHEELDOWN_MOUSE);
3351         event.at0 = kMenuEventScrollDown;
3352     }
3353     else
3354         return CGameMenuItem::MouseEvent(event);
3355     return event.at0 != kMenuEventNone;
3356 }
3357 
RemoveFilenameFromDestination(void)3358 void CGameMenuFileSelect::RemoveFilenameFromDestination(void)
3359 {
3360     int lastSlashIndex = -1;
3361     int i = 0;
3362     while (destination[i] != 0)
3363     {
3364         if (destination[i] == '/')
3365             lastSlashIndex = i;
3366         i++;
3367     }
3368 
3369     char newDestination[BMAX_PATH];
3370     for (i = 0; i <= lastSlashIndex; i++)
3371         newDestination[i] = destination[i];
3372     newDestination[i] = 0;
3373 
3374     Bstrcpy(destination, newDestination);
3375 }
3376 
SetDestinationToParentDir(void)3377 void CGameMenuFileSelect::SetDestinationToParentDir(void)
3378 {
3379     int lastSlashIndex = -1;
3380     int previousSlashIndex = -1;
3381     int i = 0;
3382     while (destination[i] != 0)
3383     {
3384         if (destination[i] == '/')
3385         {
3386             if (lastSlashIndex != -1)
3387             {
3388                 previousSlashIndex = lastSlashIndex;
3389                 lastSlashIndex = i;
3390             }
3391             else
3392             {
3393                 lastSlashIndex = i;
3394             }
3395         }
3396         i++;
3397     }
3398 
3399     char newDestination[BMAX_PATH];
3400     for (i = 0; i <= previousSlashIndex; i++)
3401         newDestination[i] = destination[i];
3402     newDestination[i] = 0;
3403 
3404     Bstrcpy(destination, newDestination);
3405 }
3406