1 /*
2  * Copyright 2010, 2011, 2012, 2013, 2014, 2016 Peter Olsson
3  *
4  * This file is part of Brum Brum Rally.
5  *
6  * Brum Brum Rally is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Brum Brum Rally is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "StateManager.h"
21 #include "GameState.h"
22 #include "Path.h"
23 #include "KeyboardDriver.h"
24 #include "KeyboardDevice.h"
25 #include "MouseDevice.h"
26 #include "JoystickDevice.h"
27 #include "JoystickDriver.h"
28 #include "NearestNeighbor.h"
29 #include "Bilinear.h"
30 #include "BilinearLessBlur.h"
31 #include "Hqx.h"
32 #include "Network.h"
33 #include "background.h"
34 #include <algorithm>
35 #include <fstream>
36 #include <cassert>
37 #include <cstdlib>
38 #include <cctype>
39 
40 const int GAME_WIDTH = 320;
41 const int GAME_HEIGHT = 240;
42 
StateManager()43 StateManager::StateManager()
44 :	currentState(0),
45 	stateChanged(false),
46 	screenZoom(1),
47 	screenPaddingX(0),
48 	screenPaddingY(0),
49 	fullscreen(0),
50 	xAxis(0),
51 	yAxis(0),
52 	bpp(0),
53 	network(),
54 	lastMenuEvent()
55 {
56 	// Load settings.
57 	Path settingsPath(configPath() + "/settings");
58 	std::ifstream in(settingsPath.c_str());
59 	Settings::load(in);
60 
61 	fullscreen = Settings::graphics.fullscreen;
62 
63 	switch (Settings::graphics.scaler)
64 	{
65 	case 0:
66 	default:
67 		scaler = new NearestNeighbor;
68 		break;
69 	case 1:
70 		scaler = new Bilinear;
71 		break;
72 	case 2:
73 		scaler = new Hqx;
74 		break;
75 	case 3:
76 		scaler = new BilinearLessBlur;
77 		break;
78 	}
79 
80 	// Make sure bit depth is supported by the scaler.
81 	const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo();
82 	bpp = videoInfo->vfmt->BitsPerPixel;
83 	if (!scaler->isValidBpp(bpp))
84 	{
85 		bpp = 32;
86 	}
87 
88 	// set the fullscreen dimensions
89 	if (Settings::graphics.fullscreenWidth == 0 || Settings::graphics.fullscreenHeight == 0)
90 	{
91 		screenWidth[1]  = videoInfo->current_w;
92 		screenHeight[1] = videoInfo->current_h;
93 	}
94 	else
95 	{
96 		screenWidth[1]  = std::max(Settings::graphics.fullscreenWidth,  GAME_WIDTH);
97 		screenHeight[1] = std::max(Settings::graphics.fullscreenHeight, GAME_HEIGHT);
98 	}
99 
100 	// set the window dimensions
101 	int maxZoom = std::min(videoInfo->current_w / GAME_WIDTH, videoInfo->current_h / GAME_HEIGHT);
102 	screenZoom = std::max(maxZoom - 1, 1);
103 	screenWidth[0] = GAME_WIDTH * screenZoom;
104 	screenHeight[0] = GAME_HEIGHT * screenZoom;
105 
106 	updateVideoMode();
107 
108 	screenBuffer = Surface(GAME_WIDTH, GAME_HEIGHT);
109 	background = createBackground(),
110 
111 	// Read input devices
112 	devices.push_back(new KeyboardDevice());
113 	devices.push_back(new MouseDevice());
114 	int nJoysticks = SDL_NumJoysticks();
115 	for (int i = 0; i < nJoysticks; ++i)
116 	{
117 		devices.push_back(new JoystickDevice(i));
118 	}
119 
120 	// load human drivers
121 	drivers.resize(8);
122 	if (in)
123 	{
124 		std::string deviceName;
125 		for (std::size_t i = 0; i < 8 && std::getline(in, deviceName); ++i)
126 		{
127 			for (std::size_t j = 0; j < devices.size(); ++j)
128 			{
129 				if (devices[j]->getName() == deviceName)
130 				{
131 					Driver* driver = devices[j]->createDriver();
132 					driver->load(in);
133 					driver->setDeviceIndex(j);
134 					drivers[i] = driver;
135 					break;
136 				}
137 			}
138 			if (!drivers[i])
139 			{
140 				// Ignore key line.
141 				in.ignore(1000, '\n');
142 			}
143 		}
144 	}
145 
146 	for (std::size_t i = 0; i < 8; ++i)
147 	{
148 		if (!drivers[i])
149 		{
150 			Driver* driver = 0;
151 			switch (i)
152 			{
153 			case 0:
154 				driver = new KeyboardDriver(SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT);
155 				break;
156 			case 1:
157 				driver = new KeyboardDriver(SDLK_w, SDLK_s, SDLK_a, SDLK_d);
158 				break;
159 			case 2:
160 				driver = new KeyboardDriver(SDLK_i, SDLK_k, SDLK_j, SDLK_l);
161 				break;
162 			case 3:
163 				driver = new KeyboardDriver(SDLK_t, SDLK_g, SDLK_f, SDLK_h);
164 				break;
165 			case 4:
166 				driver = new KeyboardDriver(SDLK_KP8, SDLK_KP5, SDLK_KP4, SDLK_KP6);
167 				break;
168 			case 5:
169 				driver = new KeyboardDriver(SDLK_HOME, SDLK_END, SDLK_DELETE, SDLK_PAGEDOWN);
170 				break;
171 			case 6:
172 				driver = new KeyboardDriver(SDLK_F4, SDLK_F3, SDLK_F1, SDLK_F2);
173 				break;
174 			case 7:
175 				driver = new KeyboardDriver(SDLK_F10, SDLK_F9, SDLK_F7, SDLK_F8);
176 				break;
177 			}
178 			driver->setDeviceIndex(0);
179 			drivers[i] = driver;
180 		}
181 	}
182 
183 	forceHideFullscreenPointer();
184 }
185 
resize(int width,int height)186 void StateManager::resize(int width, int height)
187 {
188 	screenWidth[fullscreen] = std::max(GAME_WIDTH, width);
189 	screenHeight[fullscreen] = std::max(GAME_HEIGHT, height);
190 	scaler->update(screenWidth[fullscreen], screenHeight[fullscreen]);
191 	updateVideoMode();
192 }
193 
updateVideoMode()194 void StateManager::updateVideoMode()
195 {
196 	scaler->update(screenWidth[fullscreen], screenHeight[fullscreen]);
197 
198 	// update SDL window
199 	Uint32 flags = SDL_SWSURFACE|(fullscreen?SDL_FULLSCREEN|SDL_NOFRAME:SDL_RESIZABLE);
200 	SDL_Surface* screen = SDL_SetVideoMode(screenWidth[fullscreen], screenHeight[fullscreen], bpp, flags);
201 	if (!screen)
202 	{
203 		if (fullscreen)
204 		{
205 			// This could happen if the user has a weird fullscreen
206 			// resolution so switching to window mode might help.
207 			fullscreen = false;
208 			updateVideoMode();
209 			return;
210 		}
211 		else
212 		{
213 			// I have no idea why this happened so we better quit now.
214 			std::exit(EXIT_FAILURE);
215 		}
216 	}
217 
218 	pointer.setFullScreen(fullscreen);
219 	pointer.setScaling(Settings::graphics.scalePointer > (fullscreen ? 0 : 1));
220 	pointer.setType(Settings::graphics.pointer);
221 
222 	// updateWholeScreen() will have to be called afterwards if needed
223 }
224 
updateScreen()225 void StateManager::updateScreen()
226 {
227 	// draw background
228 	assert(currentState);
229 	currentState->background.draw(screenBuffer,0,0);
230 
231 	pointer.update(!currentState->menu.isVisible());
232 
233 	std::vector<RenderObject*>& renderObjects = currentState->renderObjects;
234 	std::vector<RenderObject*>::iterator it;
235 
236 	Rect rect1;
237 	Rect rect2;
238 	for (it = renderObjects.begin(); it != renderObjects.end(); ++it)
239 	{
240 		RenderObject* ro = *it;
241 		ro->draw(screenBuffer);
242 		if (ro->isUpdated())
243 		{
244 			ro->updateRects(rect1, rect2);
245 			addUpdateRect(rect1);
246 			addUpdateRect(rect2);
247 		}
248 	}
249 
250 	pointer.draw(screenBuffer);
251 	if (pointer.isUpdated())
252 	{
253 		pointer.updateRects(rect1, rect2);
254 		addUpdateRect(rect1);
255 		addUpdateRect(rect2);
256 	}
257 
258 	std::list<Rect>::iterator rit;
259 	for (rit = updateRects.begin(); rit != updateRects.end(); ++rit)
260 	{
261 		SDL_Rect rect;
262 
263 		rect.x = (rit->x1);
264 		rect.y = (rit->y1);
265 		rect.w = (rit->x2 - rit->x1);
266 		rect.h = (rit->y2 - rit->y1);
267 
268 		scaler->render(screenBuffer, &rect);
269 
270 		updateSDLRects.push_back(rect);
271 	}
272 
273 	if (!updateSDLRects.empty())
274 	{
275 		SDL_UpdateRects(SDL_GetVideoSurface(), updateRects.size(), &updateSDLRects[0]);
276 		updateSDLRects.clear();
277 		updateRects.clear();
278 	}
279 }
280 
updateWholeScreen()281 void StateManager::updateWholeScreen()
282 {
283 	// Fill screen with black
284 	SDL_FillRect(SDL_GetVideoSurface(), 0, 0);
285 
286 	// draw background
287 	assert(currentState);
288 	currentState->background.draw(screenBuffer, 0, 0);
289 
290 	// draw render objects
291 	std::vector<RenderObject*>& renderObjects = currentState->renderObjects;
292 	std::vector<RenderObject*>::iterator it;
293 	Rect rect1;
294 	Rect rect2;
295 	for (it = renderObjects.begin(); it != renderObjects.end(); ++it)
296 	{
297 		RenderObject* ro = *it;
298 		ro->draw(screenBuffer);
299 		if (ro->isUpdated())
300 		{
301 			// we need to call updateRects even if we don't use the
302 			// rects to update them.
303 			ro->updateRects(rect1, rect2);
304 		}
305 	}
306 
307 	pointer.draw(screenBuffer);
308 	if (pointer.isUpdated())
309 	{
310 		pointer.updateRects(rect1, rect2);
311 	}
312 
313 	scaler->render(screenBuffer, 0);
314 
315 	SDL_Flip(SDL_GetVideoSurface());
316 }
317 
addUpdateRect(Rect & rect)318 void StateManager::addUpdateRect(Rect& rect)
319 {
320 	if (rect.x1 < 0)
321 	{
322 		rect.x1 = 0;
323 	}
324 	if (rect.y1 < 0)
325 	{
326 		rect.y1 = 0;
327 	}
328 	if (rect.x2 > GAME_WIDTH)
329 	{
330 		rect.x2 = GAME_WIDTH;
331 	}
332 	if (rect.y2 > GAME_HEIGHT)
333 	{
334 		rect.y2 = GAME_HEIGHT;
335 	}
336 
337 	if (rect.x1 >= rect.x2 || rect.y1 >= rect.y2)
338 	{
339 		return;
340 	}
341 
342 	std::list<Rect>::iterator it;
343 	for (it = updateRects.begin(); it != updateRects.end(); ++it)
344 	{
345 		if (overlap(rect, *it))
346 		{
347 			rect.combine(*it);
348 
349 			updateRects.erase(it);
350 
351 			// start loop from start
352 			// because the new rect can be much larger
353 			it = updateRects.begin();
354 		}
355 	}
356 	updateRects.push_front(rect);
357 }
358 
getDriver(int index)359 Driver* StateManager::getDriver(int index)
360 {
361 	assert(static_cast<unsigned>(index) < drivers.size());
362 	return drivers[index];
363 }
364 
getDevice(int index)365 InputDevice* StateManager::getDevice(int index)
366 {
367 	assert(static_cast<unsigned>(index) < devices.size());
368 	return devices[index];
369 }
370 
getDeviceNames() const371 std::vector<std::string> StateManager::getDeviceNames() const
372 {
373 	std::vector<std::string> names;
374 	for (std::size_t i = 0; i < devices.size(); ++i)
375 	{
376 		names.push_back(devices[i]->getName());
377 	}
378 	return names;
379 }
380 
setDriver(int i,Driver * driver)381 void StateManager::setDriver(int i, Driver* driver)
382 {
383 	delete drivers[i];
384 	drivers[i] = driver;
385 }
386 
cleanUp()387 void StateManager::cleanUp()
388 {
389 	// Save settings.
390 	Path settingsPath(configPath() + "/settings");
391 	std::ofstream out(settingsPath.c_str());
392 	Settings::save(out);
393 
394 	// Save (and delete) human drivers.
395 	for (std::size_t i = 0; i < drivers.size(); ++i)
396 	{
397 		drivers[i]->save(out);
398 		delete drivers[i];
399 	}
400 
401 	for (std::size_t i = 0; i < devices.size(); ++i)
402 	{
403 		delete devices[i];
404 	}
405 
406 	delete network;
407 }
408 
getInstance()409 StateManager& StateManager::getInstance()
410 {
411 	static StateManager instance;
412 	return instance;
413 }
414 
run()415 void StateManager::run()
416 {
417 	SDL_Event event;
418 	while (currentState)
419 	{
420 		stateChanged = false;
421 
422 		// handle events
423 		while (SDL_PollEvent(&event))
424 		{
425 			handleEvent(event);
426 			if (stateChanged)
427 			{
428 				break;
429 			}
430 		}
431 		if (stateChanged)
432 		{
433 			// this one is needed because the other check inside the event
434 			// loop doesn't break out of the main loop
435 			continue;
436 		}
437 
438 		// update state
439 		currentState->update();
440 		if (stateChanged)
441 		{
442 			continue;
443 		}
444 		// render
445 		updateScreen();
446 
447 		// sleep
448 		int st = currentState->sleepTime();
449 		if (st > 0)
450 		{
451 			if (network)
452 			{
453 				network->sleep(st);
454 			}
455 			else
456 			{
457 				SDL_Delay(st);
458 			}
459 		}
460 	}
461 }
462 
handleEvent(const SDL_Event & event)463 void StateManager::handleEvent(const SDL_Event& event)
464 {
465 	switch(event.type)
466 	{
467 	case SDL_KEYDOWN:
468 		if (SDL_GetModState() & KMOD_ALT)
469 		{
470 			switch (event.key.keysym.sym)
471 			{
472 			case SDLK_F4:
473 				quit();
474 				return;
475 			case SDLK_RETURN:
476 				toggleFullscreen();
477 				return;
478 			default:
479 				;
480 			}
481 		}
482 		switch (event.key.keysym.sym)
483 		{
484 		case SDLK_F11:
485 			toggleFullscreen();
486 			return;
487 		default:
488 			;
489 		}
490 		break;
491 	case SDL_QUIT:
492 		quit();
493 		return;
494 	case SDL_VIDEORESIZE:
495 		resize(event.resize.w, event.resize.h);
496 		updateWholeScreen();
497 		return;
498 	case SDL_VIDEOEXPOSE:
499 		updateWholeScreen();
500 		return;
501 	case SDL_MOUSEMOTION:
502 		pointer.move(scaler->dst2src_x(event.motion.x), scaler->dst2src_y(event.motion.y), currentState->menu.isInteractive());
503 		break;
504 	case SDL_ACTIVEEVENT:
505 		if (event.active.state & SDL_APPMOUSEFOCUS)
506 		{
507 			if (event.active.gain)
508 			{
509 				pointer.show();
510 			}
511 			else
512 			{
513 				pointer.hide(true);
514 			}
515 		}
516 		break;
517 	default:
518 		;
519 	}
520 
521 	currentState->onEvent(event);
522 }
523 
quit()524 void StateManager::quit()
525 {
526 	delete currentState;
527 	currentState = 0;
528 	stateChanged = true;
529 }
530 
enter(GameState * newState)531 void StateManager::enter(GameState* newState)
532 {
533 	newState->setStateInfo(oldStateInfo.empty() ? 0 : oldStateInfo[0]);
534 
535 	if  (lastMenuEvent.type != MENU_MOUSE_CLICK1)
536 	{
537 		pointer.hide();
538 	}
539 
540 	oldStateInfo.clear();
541 	delete currentState;
542 	currentState = newState;
543 
544 	stateChanged = true;
545 	if (currentState)
546 	{
547 		updateWholeScreen();
548 	}
549 }
550 
enter(GameState * newState,int x)551 void StateManager::enter(GameState* newState, int x)
552 {
553 	int stateInfo = 0;
554 	if (x > 0)
555 	{
556 		assert(x == 1);
557 		if (currentState)
558 		{
559 			oldStateInfo.push_back(currentState->getStateInfo());
560 		}
561 	}
562 	else if (x < 0)
563 	{
564 		if (newState)
565 		{
566 			std::size_t i = oldStateInfo.size() + x;
567 			assert(i < oldStateInfo.size());
568 			stateInfo = oldStateInfo[i];
569 			oldStateInfo.resize(i);
570 		}
571 	}
572 	newState->setStateInfo(stateInfo);
573 
574 	if  (lastMenuEvent.type != MENU_MOUSE_CLICK1)
575 	{
576 		pointer.hide();
577 	}
578 
579 	delete currentState;
580 	currentState = newState;
581 
582 	stateChanged = true;
583 	if (currentState)
584 	{
585 		updateWholeScreen();
586 	}
587 }
588 
589 // When entering a new state we sometimes want to use the old content of
590 // the screen as a background. This is useful for ingame menus. Call this
591 // method before creating the state object to make use of this.
screenToBackgound()592 void StateManager::screenToBackgound()
593 {
594 	assert(currentState);
595 	screenBuffer.draw(currentState->background, 0, 0);
596 }
597 
toggleFullscreen()598 void StateManager::toggleFullscreen()
599 {
600 	fullscreen = !fullscreen;
601 	updateVideoMode();
602 	updateWholeScreen();
603 
604 	forceHideFullscreenPointer();
605 }
606 
forceHideFullscreenPointer()607 void StateManager::forceHideFullscreenPointer()
608 {
609 	if (fullscreen)
610 	{
611 		// It doesn't look nice getting that big pointer in the middle
612 		// of the screen so lets just hide the pointer when switching to
613 		// fullscreen mode.
614 		SDL_Event e;
615 		while (SDL_PollEvent(&e));
616 		pointer.hide();
617 	}
618 }
619 
getMenuEvent(const SDL_Event & event)620 MenuEvent StateManager::getMenuEvent(const SDL_Event& event)
621 {
622 	MenuEvent e = MenuEvent(); // Zero initialized.
623 	switch (event.type)
624 	{
625 	case SDL_KEYDOWN:
626 		switch (event.key.keysym.sym)
627 		{
628 		case SDLK_DOWN:
629 			e.type = MENU_NEXT;
630 			break;
631 		case SDLK_UP:
632 			e.type = MENU_PREV;
633 			break;
634 		case SDLK_LEFT:
635 			e.type = MENU_DEC;
636 			break;
637 		case SDLK_RIGHT:
638 			e.type = MENU_INC;
639 			break;
640 		case SDLK_RETURN:
641 		case SDLK_KP_ENTER:
642 			e.type = MENU_ACTION;
643 			break;
644 		case SDLK_ESCAPE:
645 			e.type = MENU_ESC;
646 			break;
647 		case SDLK_HOME:
648 			e.type = MENU_MIN;
649 			break;
650 		case SDLK_END:
651 			e.type = MENU_MAX;
652 			break;
653 		case SDLK_PAGEUP:
654 			e.type = MENU_FIRST;
655 			break;
656 		case SDLK_PAGEDOWN:
657 			e.type = MENU_LAST;
658 			break;
659 		case SDLK_BACKSPACE:
660 			e.type = MENU_ERASE;
661 			break;
662 		case SDLK_DELETE:
663 			e.type = MENU_DELETE;
664 			break;
665 		default:
666 			e.c = unicode2char(event.key.keysym.unicode);
667 			if (std::isprint(e.c))
668 			{
669 				e.type = MENU_TEXT;
670 			}
671 		}
672 		break;
673 	case SDL_JOYAXISMOTION:
674 	{
675 		int newAxis = 0;
676 		if (event.jaxis.value > AXIS_THRESHOLD)
677 		{
678 			newAxis = 1;
679 		}
680 		else if (std::abs(event.jaxis.value) < AXIS_THRESHOLD / 2)
681 		{
682 			newAxis = 0;
683 		}
684 		else if (event.jaxis.value < -AXIS_THRESHOLD)
685 		{
686 			newAxis = -1;
687 		}
688 
689 		if (event.jaxis.axis & 1)
690 		{
691 			if (newAxis != yAxis)
692 			{
693 				if (newAxis == 1)
694 				{
695 					e.type = MENU_NEXT;
696 				}
697 				else if (newAxis == -1)
698 				{
699 					e.type = MENU_PREV;
700 				}
701 				yAxis = newAxis;
702 			}
703 		}
704 		else
705 		{
706 			if (newAxis != xAxis)
707 			{
708 				if (newAxis == 1)
709 				{
710 					e.type = MENU_INC;
711 				}
712 				else if (newAxis == -1)
713 				{
714 					e.type = MENU_DEC;
715 				}
716 				xAxis = newAxis;
717 			}
718 		}
719 		break;
720 	}
721 	case SDL_JOYHATMOTION:
722 		switch (event.jhat.value)
723 		{
724 		case SDL_HAT_DOWN:
725 			e.type = MENU_NEXT;
726 			break;
727 		case SDL_HAT_UP:
728 			e.type = MENU_PREV;
729 			break;
730 		case SDL_HAT_LEFT:
731 			e.type = MENU_DEC;
732 			break;
733 		case SDL_HAT_RIGHT:
734 			e.type = MENU_INC;
735 			break;
736 		}
737 		break;
738 	case SDL_JOYBUTTONDOWN:
739 		switch (event.jbutton.button)
740 		{
741 		case 0:
742 			e.type = MENU_ACTION;
743 			break;
744 		case 1:
745 			e.type = MENU_ESC;
746 			break;
747 		}
748 		break;
749 	case SDL_MOUSEMOTION:
750 		e.type = MENU_MOUSE_MOVE;
751 		e.x = scaler->dst2src_x(event.motion.x);
752 		e.y = scaler->dst2src_y(event.motion.y);
753 		break;
754 	case SDL_MOUSEBUTTONDOWN:
755 		if (!pointer.isVisible())
756 		{
757 			return e;
758 		}
759 		switch (event.button.button)
760 		{
761 		case SDL_BUTTON_LEFT:
762 			e.type = MENU_MOUSE_CLICK1;
763 			break;
764 		case SDL_BUTTON_RIGHT:
765 			e.type = MENU_MOUSE_CLICK2;
766 			break;
767 		case SDL_BUTTON_WHEELUP:
768 			e.type = MENU_INC;
769 			break;
770 		case SDL_BUTTON_WHEELDOWN:
771 			e.type = MENU_DEC;
772 			break;
773 		default:
774 			e.type = MENU_ESC;
775 		}
776 		e.x = scaler->dst2src_x(event.motion.x);
777 		e.y = scaler->dst2src_y(event.motion.y);
778 		break;
779 	case SDL_MOUSEBUTTONUP:
780 		e.type = MENU_MOUSE_RELEASE;
781 		break;
782 	}
783 	lastMenuEvent = e;
784 	return e;
785 }
786 
forgetLastMenuEvent()787 void StateManager::forgetLastMenuEvent()
788 {
789 	lastMenuEvent.type = MENU_UNKNOWN;
790 }
791 
updateGraphicsSettings(const Settings::Graphics & gfx)792 void StateManager::updateGraphicsSettings(const Settings::Graphics& gfx)
793 {
794 	// This function is not very efficient because it calls
795 	// updateVideoMode() and updateWholeScreen() multiple times.
796 
797 	updateScaler(gfx.scaler);
798 	updateStretch(gfx.stretch, gfx.keepAspectRatio);
799 	updatePointerType(gfx.pointer);
800 	updatePointerScaling(gfx.scalePointer);
801 
802 	// These settings only have an effect when the game is started.
803 	Settings::graphics.fullscreen = gfx.fullscreen;
804 	Settings::graphics.fullscreenWidth = gfx.fullscreenWidth;
805 	Settings::graphics.fullscreenHeight = gfx.fullscreenHeight;
806 }
807 
updateScaler(int newScaler)808 void StateManager::updateScaler(int newScaler)
809 {
810 	if (newScaler != Settings::graphics.scaler)
811 	{
812 		delete scaler;
813 		switch (newScaler)
814 		{
815 		case 0:
816 		default:
817 			scaler = new NearestNeighbor;
818 			break;
819 		case 1:
820 			scaler = new Bilinear;
821 			break;
822 		case 2:
823 			scaler = new Hqx;
824 			break;
825 		case 3:
826 			scaler = new BilinearLessBlur;
827 			break;
828 		}
829 
830 		const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo();
831 		bpp = videoInfo->vfmt->BitsPerPixel;
832 		if (!scaler->isValidBpp(bpp))
833 		{
834 			bpp = 32;
835 		}
836 		updateVideoMode();
837 
838 		// Make sure the buffer has the correct bit depth.
839 		screenBuffer = Surface(GAME_WIDTH, GAME_HEIGHT);
840 
841 		Settings::graphics.scaler = newScaler;
842 		updateWholeScreen();
843 	}
844 }
845 
updateStretch(bool newStretch,bool newKeepAspectRatio)846 void StateManager::updateStretch(bool newStretch, bool newKeepAspectRatio)
847 {
848 	if (newStretch         != Settings::graphics.stretch ||
849 	    newKeepAspectRatio != Settings::graphics.keepAspectRatio)
850 	{
851 		Settings::graphics.stretch = newStretch;
852 		Settings::graphics.keepAspectRatio = newKeepAspectRatio;
853 		updateVideoMode();
854 		updateWholeScreen();
855 	}
856 }
857 
updatePointerType(const std::string & pointerType)858 void StateManager::updatePointerType(const std::string& pointerType)
859 {
860 	if (pointerType != Settings::graphics.pointer)
861 	{
862 		pointer.setType(pointerType);
863 		Settings::graphics.pointer = pointerType;
864 
865 		// Make sure offset is updated correctly.
866 		int x;
867 		int y;
868 		SDL_GetMouseState(&x, &y);
869 		pointer.move(scaler->dst2src_x(x), scaler->dst2src_y(y));
870 
871 		updateWholeScreen();
872 	}
873 }
874 
updatePointerScaling(int scalePointer)875 void StateManager::updatePointerScaling(int scalePointer)
876 {
877 	if (scalePointer != Settings::graphics.scalePointer)
878 	{
879 		pointer.setScaling(scalePointer > (fullscreen ? 0 : 1));
880 		pointer.setType(Settings::graphics.pointer);
881 		Settings::graphics.scalePointer = scalePointer;
882 		updateWholeScreen();
883 	}
884 }
885 
886 
887 // The purpose of this function is to give the StateManager
888 // responsibility to delete the network.
setNetwork(Network * net)889 void StateManager::setNetwork(Network* net)
890 {
891 	delete network;
892 	network = net;
893 }
894 
getBackground() const895 Surface StateManager::getBackground() const
896 {
897 	return background;
898 }
899 
getLastMenuEvent() const900 const MenuEvent& StateManager::getLastMenuEvent() const
901 {
902 	return lastMenuEvent;
903 }
904 
unicode2char(Uint16 unicode)905 char unicode2char(Uint16 unicode)
906 {
907 	if (unicode < 128)
908 	{
909 		return std::tolower(static_cast<char>(unicode));
910 	}
911 	switch (unicode)
912 	{
913 	case 192:
914 	case 193:
915 	case 194:
916 	case 195:
917 	case 196:
918 	case 197:
919 	case 198:
920 	case 224:
921 	case 225:
922 	case 226:
923 	case 227:
924 	case 228:
925 	case 229:
926 	case 230:
927 		return 'a';
928 	case 199:
929 	case 231:
930 		return 'c';
931 	case 200:
932 	case 201:
933 	case 202:
934 	case 203:
935 	case 232:
936 	case 233:
937 	case 234:
938 	case 235:
939 		return 'e';
940 	case 204:
941 	case 205:
942 	case 206:
943 	case 207:
944 	case 236:
945 	case 237:
946 	case 238:
947 	case 239:
948 		return 'i';
949 	case 208:
950 	case 240:
951 		return 'd';
952 	case 209:
953 	case 241:
954 		return 'n';
955 	case 210:
956 	case 211:
957 	case 212:
958 	case 213:
959 	case 214:
960 	case 216:
961 	case 242:
962 	case 243:
963 	case 244:
964 	case 245:
965 	case 246:
966 	case 248:
967 		return 'o';
968 	case 217:
969 	case 218:
970 	case 219:
971 	case 220:
972 	case 249:
973 	case 250:
974 	case 251:
975 	case 252:
976 		return 'u';
977 	case 221:
978 	case 253:
979 	case 255:
980 		return 'y';
981 	}
982 
983 	return '\0';
984 }
985