1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom 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  * OpenXcom 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 OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "State.h"
20 #include "InteractiveSurface.h"
21 #include "Game.h"
22 #include "Screen.h"
23 #include "Surface.h"
24 #include "Font.h"
25 #include "Language.h"
26 #include "LocalizedText.h"
27 #include "Palette.h"
28 #include "../Resource/ResourcePack.h"
29 #include "../Interface/Window.h"
30 #include "../Interface/Text.h"
31 #include "../Interface/TextButton.h"
32 #include "../Interface/TextEdit.h"
33 #include "../Interface/TextList.h"
34 #include "../Interface/ArrowButton.h"
35 #include "../Interface/Slider.h"
36 #include "../Interface/ComboBox.h"
37 #include "../Interface/Cursor.h"
38 #include "../Interface/FpsCounter.h"
39 
40 namespace OpenXcom
41 {
42 
43 /**
44  * Initializes a brand new state with no child elements.
45  * By default states are full-screen.
46  * @param game Pointer to the core game.
47  */
State(Game * game)48 State::State(Game *game) : _game(game), _surfaces(), _screen(true), _modal(0)
49 {
50 	// initialize palette to all black
51 	memset(_palette, 0, sizeof(_palette));
52 }
53 
54 /**
55  * Deletes all the child elements contained in the state.
56  */
~State()57 State::~State()
58 {
59 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i < _surfaces.end(); ++i)
60 	{
61 		delete *i;
62 	}
63 }
64 
65 /**
66  * Adds a new child surface for the state to take care of,
67  * giving it the game's display palette. Once associated,
68  * the state handles all of the surface's behaviour
69  * and management automatically.
70  * @param surface Child surface.
71  * @note Since visible elements can overlap one another,
72  * they have to be added in ascending Z-Order to be blitted
73  * correctly onto the screen.
74  */
add(Surface * surface)75 void State::add(Surface *surface)
76 {
77 	// Set palette
78 	surface->setPalette(_palette);
79 
80 	// Set default text resources
81 	if (_game->getLanguage() && _game->getResourcePack())
82 		surface->initText(_game->getResourcePack()->getFont("FONT_BIG"), _game->getResourcePack()->getFont("FONT_SMALL"), _game->getLanguage());
83 
84 	_surfaces.push_back(surface);
85 }
86 
87 /**
88  * Returns whether this is a full-screen state.
89  * This is used to optimize the state machine since full-screen
90  * states automatically cover the whole screen, (whether they
91  * actually use it all or not) so states behind them can be
92  * safely ignored since they'd be covered up.
93  * @return True if it's a screen, False otherwise.
94  */
isScreen() const95 bool State::isScreen() const
96 {
97 	return _screen;
98 }
99 
100 /**
101  * Toggles the full-screen flag. Used by windows to
102  * keep the previous screen in display while the window
103  * is still "popping up".
104  */
toggleScreen()105 void State::toggleScreen()
106 {
107 	_screen = !_screen;
108 }
109 
110 /**
111  * Initializes the state and its child elements. This is
112  * used for settings that have to be reset every time the
113  * state is returned to focus (eg. palettes), so can't
114  * just be put in the constructor (remember there's a stack
115  * of states, so they can be created once while being
116  * repeatedly switched back into focus).
117  */
init()118 void State::init()
119 {
120 	_game->getScreen()->setPalette(_palette);
121 	_game->getCursor()->setPalette(_palette);
122 	_game->getCursor()->draw();
123 	_game->getFpsCounter()->setPalette(_palette);
124 	_game->getFpsCounter()->draw();
125 	if (_game->getResourcePack() != 0)
126 	{
127 		_game->getResourcePack()->setPalette(_palette);
128 	}
129 }
130 
131 /**
132  * Runs any code the state needs to keep updating every
133  * game cycle, like timers and other real-time elements.
134  */
think()135 void State::think()
136 {
137 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
138 		(*i)->think();
139 }
140 
141 /**
142  * Takes care of any events from the core game engine,
143  * and passes them on to its InteractiveSurface child elements.
144  * @param action Pointer to an action.
145  */
handle(Action * action)146 void State::handle(Action *action)
147 {
148 	if (!_modal)
149 	{
150 		for (std::vector<Surface*>::reverse_iterator i = _surfaces.rbegin(); i != _surfaces.rend(); ++i)
151 		{
152 			InteractiveSurface* j = dynamic_cast<InteractiveSurface*>(*i);
153 			if (j != 0)
154 				j->handle(action, this);
155 		}
156 	}
157 	else
158 	{
159 		_modal->handle(action, this);
160 	}
161 }
162 
163 /**
164  * Blits all the visible Surface child elements onto the
165  * display screen, by order of addition.
166  */
blit()167 void State::blit()
168 {
169 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
170 		(*i)->blit(_game->getScreen()->getSurface());
171 }
172 
173 /**
174  * Hides all the Surface child elements on display.
175  */
hideAll()176 void State::hideAll()
177 {
178 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
179 			(*i)->setHidden(true);
180 }
181 
182 /**
183  * Shows all the hidden Surface child elements.
184  */
showAll()185 void State::showAll()
186 {
187 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
188 		(*i)->setHidden(false);
189 }
190 
191 /**
192  * Resets the status of all the Surface child elements,
193  * like unpressing buttons.
194  */
resetAll()195 void State::resetAll()
196 {
197 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
198 	{
199 		InteractiveSurface *s = dynamic_cast<InteractiveSurface*>(*i);
200 		if (s != 0)
201 		{
202 			s->unpress(this);
203 			//s->setFocus(false);
204 		}
205 	}
206 }
207 
208 /**
209  * Get the localized text for dictionary key @a id.
210  * This function forwards the call to Language::getString(const std::string &).
211  * @param id The dictionary key to search for.
212  * @return A reference to the localized text.
213  */
tr(const std::string & id) const214 const LocalizedText &State::tr(const std::string &id) const
215 {
216 	return _game->getLanguage()->getString(id);
217 }
218 
219 /**
220  * Get a modifiable copy of the localized text for dictionary key @a id.
221  * This function forwards the call to Language::getString(const std::string &, unsigned).
222  * @param id The dictionary key to search for.
223  * @param n The number to use for the proper version.
224  * @return A copy of the localized text.
225  */
tr(const std::string & id,unsigned n) const226 LocalizedText State::tr(const std::string &id, unsigned n) const
227 {
228 	return _game->getLanguage()->getString(id, n);
229 }
230 
231 /**
232  * centers all the surfaces on the screen.
233  */
centerAllSurfaces()234 void State::centerAllSurfaces()
235 {
236 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
237 	{
238 		(*i)->setX((*i)->getX() + _game->getScreen()->getDX());
239 		(*i)->setY((*i)->getY() + _game->getScreen()->getDY());
240 	}
241 }
242 
243 /**
244  * drop all the surfaces by half the screen height
245  */
lowerAllSurfaces()246 void State::lowerAllSurfaces()
247 {
248 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
249 	{
250 		(*i)->setY((*i)->getY() + _game->getScreen()->getDY() / 2);
251 	}
252 }
253 
254 /**
255  * switch all the colours to something a little more battlescape appropriate.
256  */
applyBattlescapeTheme()257 void State::applyBattlescapeTheme()
258 {
259 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
260 	{
261 		Window* window = dynamic_cast<Window*>(*i);
262 		if (window)
263 		{
264 			window->setColor(Palette::blockOffset(0)-1);
265 			window->setHighContrast(true);
266 			window->setBackground(_game->getResourcePack()->getSurface("TAC00.SCR"));
267 		}
268 		Text* text = dynamic_cast<Text*>(*i);
269 		if (text)
270 		{
271 			text->setColor(Palette::blockOffset(0)-1);
272 			text->setHighContrast(true);
273 		}
274 		TextButton* button = dynamic_cast<TextButton*>(*i);
275 		if (button)
276 		{
277 			button->setColor(Palette::blockOffset(0)-1);
278 			button->setHighContrast(true);
279 		}
280 		TextEdit* edit = dynamic_cast<TextEdit*>(*i);
281 		if (edit)
282 		{
283 			edit->setColor(Palette::blockOffset(0)-1);
284 			edit->setHighContrast(true);
285 		}
286 		TextList* list = dynamic_cast<TextList*>(*i);
287 		if (list)
288 		{
289 			list->setColor(Palette::blockOffset(0)-1);
290 			list->setArrowColor(Palette::blockOffset(0));
291 			list->setHighContrast(true);
292 		}
293 		ArrowButton *arrow = dynamic_cast<ArrowButton*>(*i);
294 		if (arrow)
295 		{
296 			arrow->setColor(Palette::blockOffset(0));
297 		}
298 		Slider *slider = dynamic_cast<Slider*>(*i);
299 		if (slider)
300 		{
301 			slider->setColor(Palette::blockOffset(0)-1);
302 			slider->setHighContrast(true);
303 		}
304 		ComboBox *combo = dynamic_cast<ComboBox*>(*i);
305 		if (combo)
306 		{
307 			combo->setColor(Palette::blockOffset(0)-1);
308 			combo->setArrowColor(Palette::blockOffset(0));
309 			combo->setHighContrast(true);
310 		}
311 	}
312 }
313 
314 /**
315  * redraw all the text-type surfaces.
316  */
redrawText()317 void State::redrawText()
318 {
319 	for (std::vector<Surface*>::iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
320 	{
321 		Text* text = dynamic_cast<Text*>(*i);
322 		TextButton* button = dynamic_cast<TextButton*>(*i);
323 		TextEdit* edit = dynamic_cast<TextEdit*>(*i);
324 		TextList* list = dynamic_cast<TextList*>(*i);
325 		if (text || button || edit || list)
326 		{
327 			(*i)->draw();
328 		}
329 	}
330 }
331 
332 /**
333  * Changes the current modal surface. If a surface is modal,
334  * then only that surface can receive events. This is used
335  * when an element needs to take priority over everything else,
336  * eg. focus.
337  * @param surface Pointer to modal surface, NULL for no modal.
338  */
setModal(InteractiveSurface * surface)339 void State::setModal(InteractiveSurface *surface)
340 {
341 	_modal = surface;
342 }
343 
344 /**
345  * Replaces a certain amount of colors in the state's palette.
346  * @param colors Pointer to the set of colors.
347  * @param firstcolor Offset of the first color to replace.
348  * @param ncolors Amount of colors to replace.
349  * @param immediately Apply changes immediately, otherwise wait in case of multiple setPalettes.
350  */
setPalette(SDL_Color * colors,int firstcolor,int ncolors,bool immediately)351 void State::setPalette(SDL_Color *colors, int firstcolor, int ncolors, bool immediately)
352 {
353 	if (colors)
354 	{
355 		memcpy(_palette + firstcolor, colors, ncolors * sizeof(SDL_Color));
356 	}
357 	if (immediately)
358 	{
359 		_game->getCursor()->setPalette(_palette);
360 		_game->getCursor()->draw();
361 		_game->getFpsCounter()->setPalette(_palette);
362 		_game->getFpsCounter()->draw();
363 		if (_game->getResourcePack() != 0)
364 		{
365 			_game->getResourcePack()->setPalette(_palette);
366 		}
367 	}
368 }
369 
370 /**
371  * Loads palettes from the game resources into the state.
372  * @param palette String ID of the palette to load.
373  * @param backpals BACKPALS.DAT offset to use.
374  */
setPalette(const std::string & palette,int backpals)375 void State::setPalette(const std::string &palette, int backpals)
376 {
377 	setPalette(_game->getResourcePack()->getPalette(palette)->getColors(), 0, 256, false);
378 	if (backpals != -1)
379 		setPalette(_game->getResourcePack()->getPalette("BACKPALS.DAT")->getColors(Palette::blockOffset(backpals)), Palette::backPos, 16, false);
380 	setPalette(NULL); // delay actual update to the end
381 }
382 
383 /**
384  * Returns the state's 8bpp palette.
385  * @return Pointer to the palette's colors.
386  */
getPalette()387 SDL_Color *const State::getPalette()
388 {
389 	return _palette;
390 }
391 
392 /**
393  * Each state will probably need its own resize handling,
394  * so this space intentionally left blank
395  * @param dX delta of X;
396  * @param dY delta of Y;
397  */
resize(int & dX,int & dY)398 void State::resize(int &dX, int &dY)
399 {
400 	recenter(dX, dY);
401 }
402 
403 /**
404  * Re-orients all the surfaces in the state.
405  * @param dX delta of X;
406  * @param dY delta of Y;
407  */
recenter(int dX,int dY)408 void State::recenter(int dX, int dY)
409 {
410 	for (std::vector<Surface*>::const_iterator i = _surfaces.begin(); i != _surfaces.end(); ++i)
411 	{
412 		(*i)->setX((*i)->getX() + dX / 2);
413 		(*i)->setY((*i)->getY() + dY / 2);
414 	}
415 }
416 
417 }
418