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