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