1 #include "c_cvars.h"
2 #include "m_classes.h"
3 #include "wl_def.h"
4 #include "wl_menu.h"
5 #include "wl_play.h"
6 #include "id_sd.h"
7 #include "id_vl.h"
8 #include "id_vh.h"
9 #include "id_us.h"
10 #include "textures/textures.h"
11 #include "g_mapinfo.h"
12 #include "v_palette.h"
13 #include "colormatcher.h"
14
15 bool Menu::close = false;
16 FTexture *Menu::cursor = NULL;
17 unsigned int Menu::lastIndexDrawn = 0;
18
getTextColor() const19 EColorRange MenuItem::getTextColor() const
20 {
21 static const GameInfo::EFontColors colors[2][4] =
22 {
23 {GameInfo::MENU_DISABLED, GameInfo::MENU_LABEL, GameInfo::MENU_HIGHLIGHT, GameInfo::MENU_INVALID},
24 {GameInfo::MENU_DISABLED, GameInfo::MENU_SELECTION, GameInfo::MENU_HIGHLIGHTSELECTION, GameInfo::MENU_INVALIDSELECTION}
25 };
26 return gameinfo.FontColors[colors[isSelected()][getActive()]];
27 }
28
MenuItem(const char string[80],MENU_LISTENER_PROTOTYPE (activateListener))29 MenuItem::MenuItem(const char string[80], MENU_LISTENER_PROTOTYPE(activateListener)) :
30 activateListener(activateListener), enabled(true), highlight(false),
31 picture(NULL), pictureX(-1), pictureY(-1), visible(true),
32 activateSound("menu/activate")
33 {
34 setText(string);
35 }
36
activate()37 void MenuItem::activate()
38 {
39 if(activateListener != NULL)
40 activateListener(menu->getCurrentPosition());
41 }
42
draw()43 void MenuItem::draw()
44 {
45 if(picture)
46 VWB_DrawGraphic(picture, pictureX == -1 ? menu->getX() + 32 : pictureX, pictureY == -1 ? PrintY : pictureY, MENU_CENTER);
47
48 US_Print(BigFont, getString(), getTextColor());
49 PrintX = menu->getX() + menu->getIndent();
50 }
51
isSelected() const52 bool MenuItem::isSelected() const
53 {
54 return !menu->isAnimating() && menu->getIndex(menu->getCurrentPosition()) == this;
55 }
56
setPicture(const char * picture,int x,int y)57 void MenuItem::setPicture(const char* picture, int x, int y)
58 {
59 this->picture = TexMan(picture);
60 pictureX = x;
61 pictureY = y;
62 }
63
setText(const char string[80])64 void MenuItem::setText(const char string[80])
65 {
66 height = 13;
67 strcpy(this->string, string);
68 for(unsigned int i = 0;i < 80;i++)
69 {
70 if(string[i] == '\n')
71 height += 13;
72 else if(string[i] == '\0')
73 break;
74 }
75 }
76
LabelMenuItem(const char string[36])77 LabelMenuItem::LabelMenuItem(const char string[36]) : MenuItem(string)
78 {
79 setEnabled(false);
80 }
81
draw()82 void LabelMenuItem::draw()
83 {
84 int oldWindowX = WindowX;
85 int oldWindowY = WindowY;
86 WindowX = menu->getX();
87 WindowW = menu->getWidth();
88 US_CPrint(BigFont, string, gameinfo.FontColors[GameInfo::MENU_TITLE]);
89 WindowX = oldWindowX;
90 WindowY = oldWindowY;
91 }
92
BooleanMenuItem(const char string[36],bool & value,MENU_LISTENER_PROTOTYPE (activateListener))93 BooleanMenuItem::BooleanMenuItem(const char string[36], bool &value, MENU_LISTENER_PROTOTYPE(activateListener)) : MenuItem(string, activateListener), value(value)
94 {
95 }
96
activate()97 void BooleanMenuItem::activate()
98 {
99 value ^= 1;
100 MenuItem::activate();
101 }
102
draw()103 void BooleanMenuItem::draw()
104 {
105 static FTexture *selected = TexMan("M_SELCT"), *deselected = TexMan("M_NSELCT");
106 if (value)
107 VWB_DrawGraphic (selected, PrintX - 24, PrintY + 3, MENU_CENTER);
108 else
109 VWB_DrawGraphic (deselected, PrintX - 24, PrintY + 3, MENU_CENTER);
110 MenuItem::draw();
111 }
112
MenuSwitcherMenuItem(const char string[36],Menu & menu,MENU_LISTENER_PROTOTYPE (activateListener))113 MenuSwitcherMenuItem::MenuSwitcherMenuItem(const char string[36], Menu &menu, MENU_LISTENER_PROTOTYPE(activateListener)) : MenuItem(string, activateListener), menu(menu)
114 {
115 }
116
activate()117 void MenuSwitcherMenuItem::activate()
118 {
119 // If there is an activateListener then use it to determine if the menu should switch
120 if(activateListener == NULL || activateListener(MenuItem::menu->getCurrentPosition()))
121 {
122 MenuFadeOut();
123 menu.show();
124 if(!Menu::areMenusClosed())
125 {
126 MenuItem::menu->draw();
127 MenuFadeIn();
128 }
129 }
130 }
131
SliderMenuItem(int & value,int width,int max,const char begString[36],const char endString[36],MENU_LISTENER_PROTOTYPE (activateListener))132 SliderMenuItem::SliderMenuItem(int &value, int width, int max, const char begString[36], const char endString[36], MENU_LISTENER_PROTOTYPE(activateListener)) : MenuItem(endString, activateListener), value(value), width(width), max(max)
133 {
134 strcpy(this->begString, begString);
135 }
136
draw()137 void SliderMenuItem::draw()
138 {
139 US_Print(BigFont, begString, getTextColor());
140 PrintX += 8;
141
142 unsigned int bx = PrintX, by = PrintY+1, bw = width, bh = 10;
143 MenuToRealCoords(bx, by, bw, bh, MENU_CENTER);
144
145 DrawWindow(PrintX, PrintY+1, width, 10, MENUWIN_BACKGROUND, MENUWIN_BOTBORDER, MENUWIN_TOPBORDER);
146
147 //calc position
148 int x = int(ceil((double(width-20)/double(max))*double(value)));
149 x -= x+20 >= width ? 1 : 0;
150
151 bx = PrintX + x;
152 by = PrintY + 1;
153 bw = 20;
154 bh = 10;
155 MenuToRealCoords(bx, by, bw, bh, MENU_CENTER);
156
157 DrawWindow(PrintX + x, PrintY + 1, 20, 10, MENUWINHGLT_BACKGROUND, MENUWINHGLT_BOTBORDER, MENUWINHGLT_TOPBORDER);
158
159 PrintX += width+8;
160 MenuItem::draw();
161 }
162
left()163 void SliderMenuItem::left()
164 {
165 value -= value > 0 ? 1 : 0;
166 if(activateListener != NULL)
167 activateListener(menu->getCurrentPosition());
168 SD_PlaySound("menu/move1");
169 }
170
right()171 void SliderMenuItem::right()
172 {
173 value += value < max ? 1 : 0;
174 if(activateListener != NULL)
175 activateListener(menu->getCurrentPosition());
176 SD_PlaySound("menu/move1");
177 }
178
MultipleChoiceMenuItem(MENU_LISTENER_PROTOTYPE (changeListener),const char ** options,unsigned int numOptions,int curOption)179 MultipleChoiceMenuItem::MultipleChoiceMenuItem(MENU_LISTENER_PROTOTYPE(changeListener), const char** options, unsigned int numOptions, int curOption) : MenuItem("", changeListener),
180 curOption(curOption), numOptions(numOptions)
181 {
182 // Copy all of the options
183 this->options = new char *[numOptions];
184 for(unsigned int i = 0;i < numOptions;i++)
185 {
186 if(options[i] == NULL)
187 this->options[i] = NULL;
188 else
189 {
190 this->options[i] = new char[strlen(options[i])+1];
191 strcpy(this->options[i], options[i]);
192 }
193 }
194
195 // clamp current option
196 if(curOption < 0)
197 curOption = 0;
198 else if((unsigned)curOption >= numOptions)
199 curOption = numOptions-1;
200
201 while(options[curOption] == NULL)
202 {
203 curOption--; // Easier to go backwards
204 if(curOption < 0)
205 curOption = numOptions-1;
206 }
207
208 if(numOptions > 0)
209 setText(options[curOption]);
210 }
211
~MultipleChoiceMenuItem()212 MultipleChoiceMenuItem::~MultipleChoiceMenuItem()
213 {
214 for(unsigned int i = 0;i < numOptions;i++)
215 delete[] options[i];
216 delete[] options;
217 }
218
activate()219 void MultipleChoiceMenuItem::activate()
220 {
221 right();
222 }
223
draw()224 void MultipleChoiceMenuItem::draw()
225 {
226 DrawWindow(PrintX, PrintY, menu->getWidth()-menu->getIndent()-menu->getX(), BigFont->GetHeight(), BKGDCOLOR, BKGDCOLOR, BKGDCOLOR);
227 MenuItem::draw();
228 }
229
left()230 void MultipleChoiceMenuItem::left()
231 {
232 do
233 {
234 curOption--;
235 if(curOption < 0)
236 curOption = numOptions-1;
237 }
238 while(options[curOption] == NULL);
239 setText(options[curOption]);
240 if(activateListener != NULL)
241 activateListener(curOption);
242 SD_PlaySound("menu/move1");
243 }
244
right()245 void MultipleChoiceMenuItem::right()
246 {
247 do
248 {
249 curOption++;
250 if((unsigned)curOption >= numOptions)
251 curOption = 0;
252 }
253 while(options[curOption] == NULL);
254 setText(options[curOption]);
255 if(activateListener != NULL)
256 activateListener(curOption);
257 SD_PlaySound("menu/move1");
258 }
259
TextInputMenuItem(const FString & text,unsigned int max,MENU_LISTENER_PROTOTYPE (preeditListener),MENU_LISTENER_PROTOTYPE (posteditListener),bool clearFirst)260 TextInputMenuItem::TextInputMenuItem(const FString &text, unsigned int max, MENU_LISTENER_PROTOTYPE(preeditListener), MENU_LISTENER_PROTOTYPE(posteditListener), bool clearFirst) : MenuItem("", posteditListener), clearFirst(clearFirst), max(max), preeditListener(preeditListener)
261 {
262 setValue(text);
263 }
264
activate()265 void TextInputMenuItem::activate()
266 {
267 if(preeditListener == NULL || preeditListener(menu->getCurrentPosition()))
268 {
269 PrintY = menu->getHeight(menu->getCurrentPosition()) + menu->getY() + 1 + (5-SmallFont->GetHeight()/2);
270 char* buffer = new char[max+1];
271 bool accept = US_LineInput(SmallFont,
272 menu->getX() + menu->getIndent() + 2,
273 PrintY, buffer,
274 clearFirst ? "" : getValue(), true, max, menu->getWidth() - menu->getIndent() - 16,
275 BKGDCOLOR, getTextColor()
276 );
277
278 if(accept)
279 setValue(buffer);
280 delete[] buffer;
281 if(accept)
282 MenuItem::activate();
283 else
284 {
285 SD_PlaySound("menu/escape");
286 PrintY = menu->getHeight(menu->getCurrentPosition()) + menu->getY();
287 draw();
288 }
289 }
290 }
291
draw()292 void TextInputMenuItem::draw()
293 {
294 int color = ColorMatcher.Pick(V_LogColorFromColorRange(getTextColor()));
295
296 DrawWindow(menu->getX() + menu->getIndent(), PrintY, menu->getWidth() - menu->getIndent() - 12, 11, BKGDCOLOR, color, color);
297 PrintX = menu->getX() + menu->getIndent() + 2;
298 PrintY += 1 + (5-SmallFont->GetHeight()/2);
299 US_Print(SmallFont, getValue(), getTextColor());
300 }
301
302 int ControlMenuItem::column = 0;
303 const char* const ControlMenuItem::keyNames[512] =
304 {
305 "?","?","?","?","?","?","?","?", // 0
306 "BkSp","Tab","?","?","?","Ret","?","?", // 8
307 "?","?","?","Paus","?","?","?","?", // 16
308 "?","?","?","Esc","?","?","?","?", // 24
309 "Spce","!","\"","#","$","?","&","'", // 32
310 "(",")","*","+",",","-",".","/", // 40
311 "0","1","2","3","4","5","6","7", // 48
312 "8","9",":",";","<","=",">","?", // 56
313 "@","A","B","C","D","E","F","G", // 64
314 "H","I","J","K","L","M","N","O", // 72
315 "P","Q","R","S","T","U","V","W", // 80
316 "X","Y","Z","[","\\","]","^","_", // 88
317 "`","a","b","c","d","e","f","h", // 96
318 "h","i","j","k","l","m","n","o", // 104
319 "p","q","r","s","t","u","v","w", // 112
320 "x","y","z","{","|","}","~","Del", // 120
321 "?","?","?","?","?","?","?","?", // 128
322 "?","?","?","?","?","?","?","?", // 136
323 "?","?","?","?","?","?","?","?", // 144
324 "?","?","?","?","?","?","?","?", // 152
325 "?","?","?","?","?","?","?","?", // 160
326 "?","?","?","?","?","?","?","?", // 168
327 "?","?","?","?","?","?","?","?", // 176
328 "?","?","?","?","?","?","?","?", // 184
329 "?","?","?","?","?","?","?","?", // 192
330 "?","?","?","?","?","?","?","?", // 200
331 "?","?","?","?","?","?","?","?", // 208
332 "?","?","?","?","?","?","?","?", // 216
333 "?","?","?","?","?","?","?","?", // 224
334 "?","?","?","?","?","?","?","?", // 232
335 "?","?","?","?","?","?","?","?", // 240
336 "?","?","?","?","?","?","?","?", // 248
337 "KP0","KP1","KP2","KP3","KP4","KP5","KP6","KP7", // 256
338 "KP8","KP9","Perd","Divd","Mult","Plus","Mins","Entr", // 264
339 "Equl","Up","Down","Rght","Left","Ins","Home","End", // 272
340 "PgUp","PgDn","F1","F2","F3","F4","F5","F6", // 280
341 "F7","F8","F9","F10","F11","F12","F13","F14", // 288
342 "F15","?","?","?","NmLk","CpLk","ScLk","RShf", // 296
343 "Shft","RCtl","Ctrl","RAlt","Alt","RMet","Meta","Supr", // 304
344 "RSpr","Mode","Comp","Help","PrtS","Brk","Pwr","Euro", // 312
345 "Undo","?" // 320
346 };
347
ControlMenuItem(ControlScheme & button)348 ControlMenuItem::ControlMenuItem(ControlScheme &button) : MenuItem(button.name), button(button)
349 {
350 }
351
activate()352 void ControlMenuItem::activate()
353 {
354 if(mouseenabled)
355 {
356 // Check for mouse up
357 ControlInfo ci;
358 do { ReadAnyControl(&ci); } while(ci.button0 || ci.button1);
359 }
360
361 DrawWindow(160 + (52*column), PrintY + 1, 50 - 2, 11, MENUWIN_BACKGROUND, MENUWIN_BOTBORDER, MENUWINHGLT_TOPBORDER);
362 PrintX = 162 + (52*column);
363 US_Print(BigFont, "???");
364 VW_UpdateScreen();
365
366 IN_ClearKeysDown();
367 ControlInfo ci;
368 bool exit = false;
369 int btn = 0;
370 while(!exit)
371 {
372 SDL_Delay(5);
373
374 switch(column)
375 {
376 default:
377 case 0:
378 if(LastScan != 0)
379 {
380 ControlScheme::setKeyboard(controlScheme, button.button, LastScan);
381 ShootSnd();
382 IN_ClearKeysDown();
383 exit = true;
384 }
385 break;
386 case 1:
387 {
388 if(!mouseenabled)
389 {
390 exit = true;
391 break;
392 }
393
394 btn = IN_MouseButtons();
395 for(int i = 0;btn != 0 && i < 32;i++)
396 {
397 if(btn & (1<<i))
398 {
399 ControlScheme::setMouse(controlScheme, button.button, i);
400 exit = true;
401 }
402 }
403
404 break;
405 }
406 case 2:
407 if(!IN_JoyPresent())
408 {
409 exit = true;
410 break;
411 }
412
413 btn = IN_JoyButtons();
414 if(btn != 0)
415 {
416 for(int i = 0;btn != 0 && i < 32;i++)
417 {
418 if(btn & (1<<i))
419 {
420 ControlScheme::setJoystick(controlScheme, button.button, i);
421 exit = true;
422 }
423 }
424 }
425 else
426 {
427 btn = IN_JoyAxes();
428 for(int i = 0;btn != 0 && i < 32;i++)
429 {
430 if(btn & (1<<i))
431 {
432 ControlScheme::setJoystick(controlScheme, button.button, i+32);
433 exit = true;
434 }
435 }
436 }
437 break;
438 }
439
440 ReadAnyControl(&ci);
441 if(LastScan == sc_Escape)
442 break;
443 }
444
445 PrintX = menu->getX() + menu->getIndent();
446
447 MenuItem::activate();
448
449 // Setting one vale could affect another
450 menu->draw();
451
452 if(mouseenabled)
453 {
454 // Check for mouse up
455 ControlInfo ci;
456 do { ReadAnyControl(&ci); } while(ci.button0 || ci.button1);
457 }
458 }
459
draw()460 void ControlMenuItem::draw()
461 {
462 extern int SDL2Backconvert(int sc);
463
464 DrawWindow(159, PrintY, ((52)*3) - 1, 13, BKGDCOLOR, BKGDCOLOR, BKGDCOLOR);
465 if(isSelected())
466 DrawWindow(160 + (52*column), PrintY + 1, 50 - 2, 11, MENUWIN_BACKGROUND, MENUWIN_BOTBORDER, MENUWIN_TOPBORDER);
467
468 US_Print(BigFont, getString(), getTextColor());
469
470 const int key = SDL2Backconvert(button.keyboard);
471
472 if(button.keyboard >= 0 && button.keyboard < 512 && keyNames[key])
473 {
474 PrintX = 162;
475 US_Print(BigFont, keyNames[key], getTextColor());
476 }
477 if(button.mouse != -1)
478 {
479 PrintX = 214;
480 char btn[8];
481 sprintf(btn, "MS%d", button.mouse);
482 US_Print(BigFont, btn, getTextColor());
483 }
484 if(button.joystick != -1)
485 {
486 PrintX = 266;
487 char btn[8];
488 if(button.joystick < 32)
489 sprintf(btn, "JY%d", button.joystick);
490 else
491 sprintf(btn, "A%d%c", (button.joystick-32)/2, (button.joystick&1) ? 'D' : 'U');
492 US_Print(BigFont, btn, getTextColor());
493 }
494
495 PrintX = menu->getX() + menu->getIndent();
496 }
497
left()498 void ControlMenuItem::left()
499 {
500 if(column != 0)
501 column--;
502 }
503
right()504 void ControlMenuItem::right()
505 {
506 if(column != 2)
507 column++;
508 }
509
drawGunHalfStep(int x,int y)510 void Menu::drawGunHalfStep(int x, int y)
511 {
512 VWB_DrawGraphic (cursor, x, y-2, MENU_CENTER);
513 VW_UpdateScreen ();
514 SD_PlaySound ("menu/move1");
515 SDL_Delay (8 * 100 / 7);
516 }
517
eraseGun(int x,int y)518 void Menu::eraseGun(int x, int y)
519 {
520 int gx = x, gy = y, gw = cursor->GetScaledWidth(), gh = cursor->GetScaledHeight();
521 MenuToRealCoords(gx, gy, gw, gh, MENU_CENTER);
522 VWB_Clear(BKGDCOLOR, gx, gy, gx+gw, gy+gh);
523 }
524
Menu(int x,int y,int w,int indent,MENU_LISTENER_PROTOTYPE (entryListener))525 Menu::Menu(int x, int y, int w, int indent, MENU_LISTENER_PROTOTYPE(entryListener)) :
526 entryListener(entryListener), animating(false), controlHeaders(false),
527 curPos(0), headPicture(NULL), headTextInStripes(false),
528 headPictureIsAlternate(false), height(0), indent(indent), x(x), y(y), w(w),
529 itemOffset(0)
530 {
531 for(unsigned int i = 0;i < 36;i++)
532 headText[i] = '\0';
533 }
~Menu()534 Menu::~Menu()
535 {
536 clear();
537 }
538
addItem(MenuItem * item)539 void Menu::addItem(MenuItem *item)
540 {
541 item->setMenu(this);
542 items.Push(item);
543 if(item->isVisible() && !item->isEnabled() && (signed)countItems()-1 == curPos)
544 curPos++;
545 height += item->getHeight();
546 }
547
clear()548 void Menu::clear()
549 {
550 for(unsigned int i = 0;i < items.Size();i++)
551 delete items[i];
552 items.Delete(0, items.Size());
553 }
554
closeMenus(bool close)555 void Menu::closeMenus(bool close)
556 {
557 if(close)
558 {
559 MenuFadeOut();
560 VWB_Clear(ColorMatcher.Pick(RPART(gameinfo.MenuFadeColor), GPART(gameinfo.MenuFadeColor), BPART(gameinfo.MenuFadeColor)),
561 0, 0, screenWidth, screenHeight);
562 }
563
564 Menu::close = close;
565 }
566
countItems() const567 unsigned int Menu::countItems() const
568 {
569 unsigned int num = 0;
570 for(unsigned int i = 0;i < items.Size();i++)
571 {
572 if(items[i]->isVisible())
573 num++;
574 }
575 return num;
576 }
577
getHeight(int position) const578 int Menu::getHeight(int position) const
579 {
580 // Make sure we have the position we think we have.
581 if(position != -1)
582 {
583 for(unsigned int i = 0;i < items.Size() && i < (unsigned)position;i++)
584 {
585 if(!items[i]->isVisible())
586 position++;
587 }
588 }
589
590 unsigned int num = 0;
591 unsigned int ignore = itemOffset;
592 for(unsigned int i = 0;i < items.Size();i++)
593 {
594 if((unsigned)position == i)
595 break;
596
597 if(items[i]->isVisible())
598 {
599 if(getY() + num + items[i]->getHeight() + 6 >= 200)
600 break;
601
602 if(ignore)
603 --ignore;
604 else
605 num += items[i]->getHeight();
606 }
607 }
608 if(position >= 0)
609 return num;
610 return num + 6;
611 }
612
getIndex(int index) const613 MenuItem *Menu::getIndex(int index) const
614 {
615 unsigned int idx = 0;
616 for(idx = 0;idx < items.Size() && index >= 0;idx++)
617 {
618 if(items[idx]->isVisible())
619 index--;
620 }
621 idx--;
622 return idx >= items.Size() ? items[items.Size()-1] : items[idx];
623 }
624
drawMenu() const625 void Menu::drawMenu() const
626 {
627 if(cursor == NULL)
628 cursor = TexMan("M_CURS1");
629
630 lastIndexDrawn = 0;
631
632 WindowX = PrintX = getX() + getIndent();
633 WindowY = PrintY = getY();
634 WindowW = 320;
635 WindowH = 200;
636
637 PrintY = getY();
638 int y = getY();
639 int selectedY = getY(); // See later
640
641 unsigned int count = countItems();
642 for (unsigned int i = itemOffset; i < count; i++)
643 {
644 if(i == (unsigned)curPos)
645 selectedY = y;
646 else
647 {
648 PrintY = y;
649 if(PrintY + getIndex(i)->getHeight() + 6 >= 200)
650 break;
651 getIndex(i)->draw();
652 lastIndexDrawn = i;
653 }
654 y += getIndex(i)->getHeight();
655 }
656
657 // In order to draw the skill menu correctly we need to draw the selected option now
658 if(curPos < (signed)count && curPos >= (signed)itemOffset)
659 {
660 PrintY = selectedY;
661 getIndex(curPos)->draw();
662 if(curPos > (signed)lastIndexDrawn)
663 lastIndexDrawn = curPos;
664 }
665 }
666
draw() const667 void Menu::draw() const
668 {
669 static FTexture * const mcontrol = TexMan("M_MCONTL");
670 ClearMScreen();
671 if(headPicture && !headPictureIsAlternate)
672 {
673 DrawStripes(10);
674 VWB_DrawGraphic(headPicture, 160-headPicture->GetScaledWidth()/2, 0, MENU_TOP);
675 }
676 VWB_DrawGraphic(mcontrol, 160-mcontrol->GetScaledWidth()/2, 200-mcontrol->GetScaledHeight(), MENU_BOTTOM);
677
678 WindowX = 0;
679 WindowW = 320;
680 PrintY = getY() - 22;
681 if(controlHeaders)
682 {
683 PrintX = getX() + getIndent();
684 US_Print(BigFont, "Control", gameinfo.FontColors[GameInfo::MENU_TITLE]);
685 PrintX = 168;
686 US_Print(BigFont, "Key", gameinfo.FontColors[GameInfo::MENU_TITLE]);
687 PrintX = 220;
688 US_Print(BigFont, "Mse", gameinfo.FontColors[GameInfo::MENU_TITLE]);
689 PrintX = 272;
690 US_Print(BigFont, "Joy", gameinfo.FontColors[GameInfo::MENU_TITLE]);
691 }
692 else
693 {
694 if(headTextInStripes)
695 {
696 DrawStripes(10);
697 PrintY = 15;
698 }
699
700 if(headPictureIsAlternate && headPicture)
701 VWB_DrawGraphic(headPicture, 160-headPicture->GetScaledWidth()/2, PrintY, MENU_CENTER);
702 else
703 US_CPrint(BigFont, headText, gameinfo.FontColors[GameInfo::MENU_TITLE]);
704 }
705
706 DrawWindow(getX() - 8, getY() - 3, getWidth(), getHeight(), BKGDCOLOR);
707 drawMenu();
708
709 if(!isAnimating() && countItems() > 0)
710 VWB_DrawGraphic (cursor, x - 4, y + getHeight(curPos) - 2, MENU_CENTER);
711 VW_UpdateScreen ();
712 }
713
handle()714 int Menu::handle()
715 {
716 char key;
717 static int redrawitem = 1, lastitem = -1;
718 int x, y, basey, exit, shape;
719 uint32_t lastBlinkTime;
720 ControlInfo ci;
721
722 if(close)
723 return -1;
724
725 x = getX() - 4;
726 basey = getY();
727 y = basey + getHeight(curPos);
728
729 if (redrawitem)
730 {
731 PrintX = getX() + getIndent();
732 PrintY = getY() + getHeight(curPos);
733 getIndex(curPos)->draw();
734 }
735 VW_UpdateScreen ();
736
737 shape = 0;
738 exit = 0;
739 lastBlinkTime = GetTimeCount ();
740 IN_ClearKeysDown ();
741
742 do
743 {
744 //
745 // CHANGE GUN SHAPE
746 //
747 if (getIndent() != 0 && lastBlinkTime != GetTimeCount())
748 {
749 lastBlinkTime = GetTimeCount();
750 TexMan.UpdateAnimations(lastBlinkTime*14);
751
752 cursor = TexMan("M_CURS1");
753 draw();
754 }
755 else SDL_Delay(5);
756
757 CheckPause ();
758
759 //
760 // SEE IF ANY KEYS ARE PRESSED FOR INITIAL CHAR FINDING
761 //
762 key = LastASCII;
763 if (key)
764 {
765 int ok = 0;
766
767 if (key >= 'a')
768 key -= 'a' - 'A';
769
770 for (unsigned int i = curPos + 1; i < countItems(); i++)
771 if (getIndex(i)->isEnabled() && getIndex(i)->getString()[0] == key)
772 {
773 curPos = i;
774 ok = 1;
775 SD_PlaySound("menu/move1");
776 IN_ClearKeysDown ();
777 break;
778 }
779
780 //
781 // DIDN'T FIND A MATCH FIRST TIME THRU. CHECK AGAIN.
782 //
783 if (!ok)
784 {
785 for (int i = 0; i < curPos; i++)
786 {
787 if (getIndex(i)->isEnabled() && getIndex(i)->getString()[0] == key)
788 {
789 curPos = i;
790 SD_PlaySound("menu/move1");
791 IN_ClearKeysDown ();
792 break;
793 }
794 }
795 }
796 }
797
798 if(LastScan == SDLx_SCANCODE(DELETE))
799 {
800 handleDelete();
801 LastScan = 0;
802
803 // Leave menu if we delete everything.
804 if(countItems() == 0)
805 {
806 lastitem = curPos; // Prevent redrawing
807 exit = 2;
808 }
809 }
810
811 //
812 // GET INPUT
813 //
814 ReadAnyControl (&ci);
815 switch (ci.dir)
816 {
817 default:
818 break;
819
820 ////////////////////////////////////////////////
821 //
822 // MOVE UP
823 //
824 case dir_North:
825 {
826 if(countItems() <= 1)
827 break;
828
829 //
830 // MOVE TO NEXT AVAILABLE SPOT
831 //
832 int oldPos = curPos;
833 do
834 {
835 if (curPos == 0)
836 {
837 curPos = countItems() - 1;
838 itemOffset = curPos - lastIndexDrawn;
839 }
840 else if (itemOffset > 0 && (unsigned)curPos == itemOffset+1)
841 {
842 --itemOffset;
843 --curPos;
844 }
845 else
846 --curPos;
847 }
848 while (!getIndex(curPos)->isEnabled());
849
850 if(oldPos - curPos == 1)
851 {
852 animating = true;
853 draw();
854 drawGunHalfStep(x, getY() + getHeight(oldPos) - 6);
855 animating = false;
856 }
857 draw();
858 SD_PlaySound("menu/move2");
859 //
860 // WAIT FOR BUTTON-UP OR DELAY NEXT MOVE
861 //
862 TicDelay (20);
863 break;
864 }
865
866 ////////////////////////////////////////////////
867 //
868 // MOVE DOWN
869 //
870 case dir_South:
871 {
872 if(countItems() <= 1)
873 break;
874
875 int oldPos = curPos;
876 do
877 {
878 unsigned int lastPos = countItems() - 1;
879 if ((unsigned)curPos == lastPos)
880 {
881 curPos = 0;
882 itemOffset = 0;
883 }
884 else if (lastIndexDrawn != lastPos && (unsigned)curPos >= lastIndexDrawn-1)
885 {
886 ++itemOffset;
887 ++curPos;
888 }
889 else
890 ++curPos;
891 }
892 while (!getIndex(curPos)->isEnabled());
893
894 if(oldPos - curPos == -1)
895 {
896 animating = true;
897 draw();
898 drawGunHalfStep(x, getY() + getHeight(oldPos) + 6);
899 animating = false;
900 }
901 draw();
902 SD_PlaySound("menu/move2");
903 //
904 // WAIT FOR BUTTON-UP OR DELAY NEXT MOVE
905 //
906 TicDelay (20);
907 break;
908 }
909 case dir_West:
910 getIndex(curPos)->left();
911 PrintX = getX() + getIndent();
912 PrintY = getY() + getHeight(curPos);
913 getIndex(curPos)->draw();
914 VW_UpdateScreen();
915 TicDelay(20);
916 break;
917 case dir_East:
918 getIndex(curPos)->right();
919 PrintX = getX() + getIndent();
920 PrintY = getY() + getHeight(curPos);
921 getIndex(curPos)->draw();
922 VW_UpdateScreen();
923 TicDelay(20);
924 break;
925 }
926
927 if (ci.button0 || Keyboard[sc_Space] || Keyboard[sc_Enter])
928 exit = 1;
929
930 if ((ci.button1 && !Keyboard[sc_Alt]) || Keyboard[sc_Escape])
931 exit = 2;
932
933 }
934 while (!exit);
935
936
937 IN_ClearKeysDown ();
938
939 //
940 // ERASE EVERYTHING
941 //
942 if (lastitem != curPos)
943 {
944 PrintX = getX() + getIndent();
945 PrintY = getY() + getHeight(curPos);
946 getIndex(curPos)->draw();
947 redrawitem = 1;
948 }
949 else
950 redrawitem = 0;
951
952 VW_UpdateScreen ();
953
954 lastitem = curPos;
955 switch (exit)
956 {
957 case 1:
958 if(getIndex(curPos)->playActivateSound())
959 SD_PlaySound (getIndex(curPos)->getActivateSound());
960 getIndex(curPos)->activate();
961 PrintX = getX() + getIndent();
962 PrintY = getY() + getHeight(curPos);
963 if(!Menu::areMenusClosed())
964 {
965 getIndex(curPos)->draw();
966 VW_UpdateScreen();
967 }
968
969 // Check for mouse up
970 do { ReadAnyControl(&ci); } while(ci.button0);
971 return curPos;
972
973 case 2:
974 SD_PlaySound("menu/escape");
975
976 // Check for mouse up
977 do { ReadAnyControl(&ci); } while(ci.button1);
978 return -1;
979 }
980
981 return 0; // JUST TO SHUT UP THE ERROR MESSAGES!
982 }
983
setCurrentPosition(int position)984 void Menu::setCurrentPosition(int position)
985 {
986 unsigned int count;
987
988 if(position <= 0) // At start
989 {
990 curPos = 0;
991 itemOffset = 0;
992 }
993 else if((unsigned) position >= (count = countItems())-1) // At end
994 {
995 curPos = count-1;
996 itemOffset = curPos;
997 unsigned int accumulatedHeight = getY() + getIndex(itemOffset)->getHeight() + 6;
998 while(accumulatedHeight < 200)
999 {
1000 if(itemOffset == 0)
1001 break;
1002
1003 accumulatedHeight += getIndex(--itemOffset)->getHeight();
1004 }
1005 if(accumulatedHeight >= 200)
1006 ++itemOffset;
1007 }
1008 else // Somewhere in the middle
1009 {
1010 curPos = position;
1011 itemOffset = curPos;
1012 unsigned int accumulatedHeight = getY() + getIndex(itemOffset)->getHeight() + 6;
1013 unsigned int lastIndex = curPos;
1014 while(accumulatedHeight < 200)
1015 {
1016 if(lastIndex < items.Size()-1)
1017 {
1018 accumulatedHeight += getIndex(++lastIndex)->getHeight();
1019 if(accumulatedHeight >= 200)
1020 break;
1021 }
1022
1023 if(itemOffset > 0)
1024 accumulatedHeight += getIndex(--itemOffset)->getHeight();
1025 else
1026 break;
1027 }
1028 if(accumulatedHeight >= 200)
1029 ++itemOffset;
1030 }
1031 }
1032
setHeadPicture(const char * picture,bool isAlt)1033 void Menu::setHeadPicture(const char* picture, bool isAlt)
1034 {
1035 FTextureID picID = TexMan.CheckForTexture(picture, FTexture::TEX_Any);
1036 if(picID.isValid())
1037 {
1038 headPicture = TexMan(picID);
1039 headPictureIsAlternate = isAlt;
1040 }
1041 }
1042
setHeadText(const char text[36],bool drawInStripes)1043 void Menu::setHeadText(const char text[36], bool drawInStripes)
1044 {
1045 strcpy(headText, text);
1046 headTextInStripes = drawInStripes;
1047 }
1048
show()1049 void Menu::show()
1050 {
1051 if(Menu::areMenusClosed())
1052 return;
1053
1054 if(entryListener != NULL)
1055 entryListener(0);
1056
1057 if(countItems() == 0) // Do nothing.
1058 return;
1059 if(curPos >= (signed)countItems())
1060 curPos = countItems()-1;
1061
1062 draw();
1063 MenuFadeIn();
1064 WaitKeyUp();
1065
1066 int item = 0;
1067 while((item = handle()) != -1);
1068
1069 if(!Menu::areMenusClosed())
1070 MenuFadeOut ();
1071 }
1072