/* * Copyright 2010 OpenXcom Developers. * * This file is part of OpenXcom. * * OpenXcom is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenXcom is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenXcom. If not, see . */ #include "Game.h" #include "SDL_mixer.h" #include "State.h" #include "Screen.h" #include "Language.h" #include "../Interface/Cursor.h" #include "../Interface/FpsCounter.h" #include "../Resource/ResourcePack.h" #include "../Ruleset/Ruleset.h" #include "../Savegame/SavedGame.h" #include "Palette.h" #include "Action.h" #include "Exception.h" namespace OpenXcom { /** * Starts up SDL with all the subsystems and SDL_mixer for audio processing, * creates the display screen and sets up the cursor. * @param title Title of the game window. * @param width Width of the display screen. * @param height Height of the display screen. * @param bpp Bits-per-pixel of the display screen. * @warning Currently the game is designed for 8bpp, so there's no telling what'll * happen if you use a different value. */ Game::Game(const std::string &title, int width, int height, int bpp) : _screen(0), _cursor(0), _lang(0), _states(), _deleted(), _res(0), _save(0), _rules(0), _quit(false), _init(false) { // Initialize SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { throw Exception(SDL_GetError()); } // Initialize SDL_mixer if (Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 1024) != 0) { throw Exception(Mix_GetError()); } Mix_AllocateChannels(16); // Set the window caption SDL_WM_SetCaption(title.c_str(), 0); // Set up keyboard events SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_EnableUNICODE(1); // Create display _screen = new Screen(width, height, bpp); // Create cursor _cursor = new Cursor(9, 13); _cursor->setColor(Palette::blockOffset(15)+12); // Create fps counter _fpsCounter = new FpsCounter(15, 5, 0, 0); } /** * Deletes the display screen, cursor, states and shuts down all the SDL subsystems. */ Game::~Game() { Mix_HaltChannel(-1); for (std::list::iterator i = _states.begin(); i != _states.end(); i++) { delete *i; } delete _cursor; delete _lang; delete _res; delete _rules; delete _save; delete _screen; delete _fpsCounter; Mix_CloseAudio(); SDL_Quit(); } /** * The state machine takes care of passing all the events from SDL to the * active state, running any code within and blitting all the states and * cursor to the screen. This is run indefinitely until the game quits. */ void Game::run() { while (!_quit) { // Clean up states while (!_deleted.empty()) { delete _deleted.back(); _deleted.pop_back(); } // Initialize active state if (!_init) { _states.back()->init(); _init = true; } // Process events while (SDL_PollEvent(&_event)) { if (_event.type == SDL_QUIT) { _quit = true; } else { Action action = Action(&_event, _screen->getXScale(), _screen->getYScale()); _screen->handle(&action); _cursor->handle(&action); _fpsCounter->handle(&action); _states.back()->handle(&action); } } // Process logic _fpsCounter->think(); _states.back()->think(); // Process rendering if (_init) { _screen->clear(); std::list::iterator i = _states.end(); do { i--; } while(i != _states.begin() && !(*i)->isScreen()); for (; i != _states.end(); i++) { (*i)->blit(); } _fpsCounter->blit(_screen->getSurface()); _cursor->blit(_screen->getSurface()); } _screen->flip(); SDL_Delay(0); } } /** * Stops the state machine and the game is shut down. */ void Game::quit() { _quit = true; } /** * Returns the display screen used by the game. * @return Pointer to the screen. */ Screen *const Game::getScreen() const { return _screen; } /** * Returns the mouse cursor used by the game. * @return Pointer to the cursor. */ Cursor *const Game::getCursor() const { return _cursor; } /** * Replaces a certain amount of colors in the palettes of the game's * screen and resources. * @param colors Pointer to the set of colors. * @param firstcolor Offset of the first color to replace. * @param ncolors Amount of colors to replace. */ void Game::setPalette(SDL_Color *colors, int firstcolor, int ncolors) { _screen->setPalette(colors, firstcolor, ncolors); _cursor->setPalette(colors, firstcolor, ncolors); _cursor->draw(); _fpsCounter->setPalette(colors, firstcolor, ncolors); if (_res != 0) { _res->setPalette(colors, firstcolor, ncolors); } } /** * Pops all the states currently in stack and pushes in the new state. * A shortcut for cleaning up all the old states when they're not necessary * like in one-way transitions. * @param state Pointer to the new state. */ void Game::setState(State *state) { while (!_states.empty()) { popState(); } pushState(state); _init = false; } /** * Pushes a new state into the top of the stack and initializes it. * The new state will be used once the next game cycle starts. * @param state Pointer to the new state. */ void Game::pushState(State *state) { _states.push_back(state); _init = false; } /** * Pops the last state from the top of the stack. Since states * can't actually be deleted mid-cycle, it's moved into a separate queue * which is cleared at the start of every cycle, so the transition * is seamless. */ void Game::popState() { _deleted.push_back(_states.back()); _states.pop_back(); _init = false; } /** * Returns the language currently in use by the game. * @return Pointer to the language. */ Language *const Game::getLanguage() const { return _lang; } /** * Changes the language currently in use by the game. * @param lang Pointer to the language. */ void Game::setLanguage(Language *lang) { delete _lang; _lang = lang; } /** * Returns the resource pack currently in use by the game. * @return Pointer to the resource pack. */ ResourcePack *const Game::getResourcePack() const { return _res; } /** * Sets a new resource pack for the game to use. * @param res Pointer to the resource pack. */ void Game::setResourcePack(ResourcePack *res) { _res = res; } /** * Returns the saved game currently in use by the game. * @return Pointer to the saved game. */ SavedGame *const Game::getSavedGame() const { return _save; } /** * Sets a new saved game for the game to use. * @param save Pointer to the saved game. */ void Game::setSavedGame(SavedGame *save) { delete _save; _save = save; } /** * Returns the ruleset currently in use by the game. * @return Pointer to the ruleset. */ Ruleset *const Game::getRuleset() const { return _rules; } /** * Sets a new ruleset for the game to use. * @param rules Pointer to the ruleset. */ void Game::setRuleset(Ruleset *rules) { delete _rules; _rules = rules; } }