diff --git a/source/code/BlockGame.cpp b/source/code/BlockGame.cpp index b8e10be..6ff4914 100644 --- a/source/code/BlockGame.cpp +++ b/source/code/BlockGame.cpp @@ -52,7 +52,7 @@ static string itoa2(int num) { /** * This function tells how long the game may pause the rise of the blocks. * This decreases as the game progresses. - * @param stops In out variable. If larger than the max allowed + * @param stops In out variable. If larger than the max allowed * @param ticks Ticks since the game was started * @param gameStartedAt Tick the game was started at. */ @@ -62,7 +62,7 @@ static void capMaxStops(int& stops, unsigned int ticks, unsigned int gameStarted return; } unsigned int gameTime = gameStartedAt - ticks; - + int maxSeconds = 10-( (gameTime/1000) % 30 ); if (maxSeconds < 3) { maxSeconds = 1; @@ -234,7 +234,6 @@ bool BlockGame::GetIsWinner() const { return hasWonTheGame; } - void BlockGame::NewGame(const BlockGameStartInfo& s) { replayInfo.startInfo = s; replayInfo.actions.clear(); @@ -277,7 +276,7 @@ void BlockGame::NewGame(const BlockGameStartInfo& s) { } } for (int j=0; j<6; j++) { - board[j][0] = 5; + board[j][0] = 5; } } this->singlePuzzle = s.singlePuzzle; @@ -1832,7 +1831,7 @@ void BlockGame::DoAction (const BlockGameAction& action) { } if (action.action == BlockGameAction::Action::UPDATE && replayInfo.actions.size() > 0 && replayInfo.actions.back().action == action.action) { replayInfo.actions.back() = action; - } + } else { replayInfo.actions.push_back(action); } @@ -1894,7 +1893,7 @@ void BlockGame::PopSendGarbage(std::vector& poppedData) { this->garbageSendQueue.clear(); } -void BlockGame::GetMouseCursor(bool& pressed, int& x, int&y) const { +void BlockGame::GetMouseCursor(bool& pressed, int& x, int& y) const { if (mouse_cursorx < 0 || mouse_cursory < 0 || mouse_cursorx >=6 || mouse_cursory > 13) { pressed = false; x = 0; @@ -1913,7 +1912,7 @@ void BlockGame::MouseDown(int x, int y) { } mouse_cursorx = x; mouse_cursory = y; -} +} void BlockGame::MouseMove(int x) { if (AI_Enabled) { diff --git a/source/code/MenuSystem.cpp b/source/code/MenuSystem.cpp index b2b599e..ffe826f 100644 --- a/source/code/MenuSystem.cpp +++ b/source/code/MenuSystem.cpp @@ -90,19 +90,19 @@ bool Button::isPopOnRun() const { return popOnRun; } -static void drawToScreen(const Button &b) { +static void drawToScreen(const Button& b) { if (b.marked) { globalData.spriteHolder->GetSprite(menu_marked).Draw(globalData.screen, SDL_GetTicks(), b.x, b.y); } else { globalData.spriteHolder->GetSprite(menu_unmarked).Draw(globalData.screen, SDL_GetTicks(), b.x, b.y); } - + standardButton.thefont->draw(globalData.screen, b.x+standardButton.xsize/2,b.y+standardButton.ysize/2-standardButton.thefont->getHeight("%s", b.label.c_str())/2, NFont::CENTER, "%s", b.label.c_str()); } -static bool isClicked(const Button &b, int x,int y) { +static bool isClicked(const Button& b, int x,int y) { if ( x >= b.x && y >= b.y && x<= b.x+standardButton.xsize && y <= b.y + standardButton.ysize) { return true; } @@ -302,7 +302,7 @@ void Menu::Draw(SDL_Renderer* target) { placeButtons(); drawSelf(target); } -void Menu::ProcessInput(const SDL_Event& event, bool &processed) { +void Menu::ProcessInput(const SDL_Event& event, bool& processed) { if (isUpEvent(event)) { marked--; if (marked<0) { diff --git a/source/code/ReplayPlayer.cpp b/source/code/ReplayPlayer.cpp new file mode 100644 index 0000000..a01c583 --- /dev/null +++ b/source/code/ReplayPlayer.cpp @@ -0,0 +1,54 @@ +/* +=========================================================================== +blockattack - Block Attack - Rise of the Blocks +Copyright (C) 2005-2017 Poul Sander + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +http://www.blockattack.net +=========================================================================== +*/ + +#include "ReplayPlayer.hpp" +#include "global.hpp" +#include "common.h" +#include "MenuSystem.h" + +ReplayPlayer::ReplayPlayer() { +} + +ReplayPlayer::~ReplayPlayer() { +} + +bool ReplayPlayer::IsActive() { + return isActive; +} + +void ReplayPlayer::Draw(SDL_Renderer* target) { + DrawBackground(target); +} + +void ReplayPlayer::ProcessInput(const SDL_Event& event, bool& processed) { + UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); + + if (isConfirmEvent(event) || isEscapeEvent(event)) { + isActive = false; + processed = true; + } +} + +void ReplayPlayer::Update() { + +} diff --git a/source/code/ReplayPlayer.hpp b/source/code/ReplayPlayer.hpp new file mode 100644 index 0000000..0c3cc75 --- /dev/null +++ b/source/code/ReplayPlayer.hpp @@ -0,0 +1,44 @@ +/* +=========================================================================== +blockattack - Block Attack - Rise of the Blocks +Copyright (C) 2005-2017 Poul Sander + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +http://www.blockattack.net +=========================================================================== +*/ + +#ifndef REPLAYPLAYER_HPP +#define REPLAYPLAYER_HPP + +#include "sago/GameStateInterface.hpp" + +class ReplayPlayer : public sago::GameStateInterface { +public: + ReplayPlayer(); + ReplayPlayer(const ReplayPlayer& orig) = delete; + virtual ~ReplayPlayer(); + + bool IsActive() override; + void Draw(SDL_Renderer* target) override; + void ProcessInput(const SDL_Event& event, bool &processed) override; + void Update() override; +private: + bool isActive = true; +}; + +#endif /* REPLAYPLAYER_HPP */ + diff --git a/source/code/ScoresDisplay.cpp b/source/code/ScoresDisplay.cpp index d21c826..dd6bad5 100644 --- a/source/code/ScoresDisplay.cpp +++ b/source/code/ScoresDisplay.cpp @@ -171,7 +171,7 @@ void ScoresDisplay::Draw(SDL_Renderer*) { void ScoresDisplay::ProcessInput(const SDL_Event& event, bool& processed) { UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); - + if (isLeftEvent(event)) { page++; if (page>=numberOfPages) { diff --git a/source/code/gamecontroller.cpp b/source/code/gamecontroller.cpp index 0e05d9e..0ca6b98 100644 --- a/source/code/gamecontroller.cpp +++ b/source/code/gamecontroller.cpp @@ -54,14 +54,14 @@ static const char* GameControllerGetName(SDL_GameController* gamecontroller) { static std::string GetGuidAsHex(const SDL_JoystickGUID& guid) { std::string ret; char buffer[3]; - for(size_t j = 0; j < 16; j++) { + for (size_t j = 0; j < 16; j++) { snprintf(buffer, sizeof(buffer), "%02X", guid.data[j]); ret += buffer; } return ret; } -static std::map gamecontrollers_assigned; +static std::map gamecontrollers_assigned; static int GetNextPlayerByGui(const SDL_JoystickGUID& guid) { Config::getInstance()->setDefault("gc_AllToOnePlayer", "0"); @@ -75,7 +75,7 @@ static int GetNextPlayerByGui(const SDL_JoystickGUID& guid) { int player = Config::getInstance()->getInt(configName) + gamecontrollers_assigned[guidAsHex]; gamecontrollers_assigned[guidAsHex]++; //Next controller with same guid should be assigned to different player. //std::cout << "Guid: " << guidAsHex << "\n"; - + if (player%2==0) { return 2; //Even number means player 2 } @@ -95,7 +95,7 @@ void InitGameControllers() { for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { controller = SDL_GameControllerOpen(i); - SDL_Joystick *j = SDL_GameControllerGetJoystick(controller); + SDL_Joystick* j = SDL_GameControllerGetJoystick(controller); SDL_JoystickID instanceId = SDL_JoystickInstanceID(j); SDL_JoystickGUID guid = SDL_JoystickGetGUID(j); int assingToPlayer = GetNextPlayerByGui(guid); diff --git a/source/code/global.hpp b/source/code/global.hpp index 6dffbca..997dfb0 100644 --- a/source/code/global.hpp +++ b/source/code/global.hpp @@ -34,7 +34,7 @@ void MainMenu(); void ResetFullscreen(); void RunGameState(sago::GameStateInterface& state ); -enum class Gametype { SinglePlayerEndless=0, SinglePlayerTimeTrial=1, StageClear=2, Puzzle=3, SinglePlayerVs=4, TwoPlayerTimeTrial=10, TwoPlayerVs=11 }; +enum class Gametype { SinglePlayerEndless=0, SinglePlayerTimeTrial=1, StageClear=2, Puzzle=3, SinglePlayerVs=4, TwoPlayerTimeTrial=10, TwoPlayerVs=11, Replay=100 }; int runGame(Gametype gametype,int level); bool OpenDialogbox(int x, int y, std::string& name, const std::string& header); @@ -55,6 +55,7 @@ struct GlobalData { bool MusicEnabled; //true if background music is enabled bool SoundEnabled; //true if sound effects is enabled bool bFullscreen; //true if game is running fullscreen + std::string replayArgument; //Name of the replay to play (if given as a commandline argument) std::string player1name; std::string player2name; SDL_Renderer *screen = nullptr; //The whole screen; diff --git a/source/code/levelselect.cpp b/source/code/levelselect.cpp index 821d669..a6018c8 100644 --- a/source/code/levelselect.cpp +++ b/source/code/levelselect.cpp @@ -97,7 +97,7 @@ int PuzzleLevelSelect(int Type) { SDL_Event event; while ( SDL_PollEvent(&event) ) { UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); - + if ( event.type == SDL_QUIT ) { Config::getInstance()->setShuttingDown(5); levelNr = -1; diff --git a/source/code/main.cpp b/source/code/main.cpp index 5ba9a26..3a6ebb1 100644 --- a/source/code/main.cpp +++ b/source/code/main.cpp @@ -287,18 +287,18 @@ void DrawBackground(SDL_Renderer* target) { * @param mousey */ void UpdateMouseCoordinates(const SDL_Event& event, int& mousex, int& mousey) { - switch(event.type) { - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - mousex = event.button.x; - mousey = event.button.y; - break; - case SDL_MOUSEMOTION: - mousex = event.motion.x; - mousey = event.motion.y; - break; - default: - break; + switch (event.type) { + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + mousex = event.button.x; + mousey = event.button.y; + break; + case SDL_MOUSEMOTION: + mousex = event.motion.x; + mousey = event.motion.y; + break; + default: + break; } } @@ -583,6 +583,7 @@ static TextManager theTextManager; #include "BlockGameSdl.inc" #include "sago/SagoMisc.hpp" +#include "ReplayPlayer.hpp" @@ -754,7 +755,6 @@ void DrawEverything(int xsize, int ysize,BlockGameSdl* theGame, BlockGameSdl* th DrawIMG(stageBobble,globalData.screen,theGame->GetTopX()+280,theGame->GetTopY()+650+50*(theGame->GetStageClearLimit()-theGame->GetLinesCleared())-theGame->GetPixels()-1); } //player1 finnish, player2 start - //DrawIMG(boardBackBack,globalData.screen,theGame2->GetTopX()-60,theGame2->GetTopY()-68); if (!editorMode /*&& !singlePuzzle*/ ) { /* *If single player mode (and not VS) @@ -1036,10 +1036,11 @@ static void ParseArguments(int argc, char* argv[], globalConfig& conf) { ("no-auto-scale", "Do not automatically auto scale") ("puzzle-level-file", boost::program_options::value(), "Sets the default puzzle file to load") ("puzzle-single-level", boost::program_options::value(), "Start the specific puzzle level directly") + ("play-replay", boost::program_options::value(), "Start a replay") ("bind-text-domain", boost::program_options::value(), SPrintStringF("Overwrites the bind text domain used for finding translations. " - "Default: \"%s\"", LOCALEDIR).c_str()) + "Default: \"%s\"", LOCALEDIR).c_str()) ("homepath", boost::program_options::value(), SPrintStringF("Set the home folder where settings are saved. The directory will be created if it does not exist." - " Default: \"%s\"", getPathToSaveFiles().c_str()).c_str()) + " Default: \"%s\"", getPathToSaveFiles().c_str()).c_str()) ; boost::program_options::variables_map vm; @@ -1071,8 +1072,8 @@ static void ParseArguments(int argc, char* argv[], globalConfig& conf) { } if (vm.count("help")) { cout << SPrintStringF("Block Attack - Rise of the blocks %s\n\n" - "Block Attack - Rise of the Blocks is a puzzle/blockfall game inspired by Tetris Attack for the SNES.\n\n" - "%s\n\n", VERSION_NUMBER, "www.blockattack.net"); + "Block Attack - Rise of the Blocks is a puzzle/blockfall game inspired by Tetris Attack for the SNES.\n\n" + "%s\n\n", VERSION_NUMBER, "www.blockattack.net"); cout << "Usage: "<< commandname << " [OPTION]..." << "\n"; cout << desc << "\n"; cout << "Examples:" << "\n"; @@ -1114,13 +1115,16 @@ static void ParseArguments(int argc, char* argv[], globalConfig& conf) { singlePuzzle = true; singlePuzzleNr = vm["puzzle-single-level"].as(); } - if(vm.count("no-auto-scale")) { + if (vm.count("no-auto-scale")) { conf.autoScale = false; } if (vm.count("puzzle-level-file")) { conf.puzzleName = vm["puzzle-level-file"].as(); } - + if (vm.count("play-replay")) { + globalData.replayArgument = vm["play-replay"].as(); + } + } //Warning: the arguments to main must be "int argc, char* argv[]" NO CONST! or SDL_main will fail to find it @@ -1301,7 +1305,7 @@ int main(int argc, char* argv[]) { BlockGameSdl theGame2 = BlockGameSdl(globalData.xsize-500, 100); player1 = &theGame; player2 = &theGame2; - + BlockGameAction a; a.action = BlockGameAction::Action::SET_GAME_OVER; theGame.DoAction(a); @@ -1327,6 +1331,10 @@ int main(int argc, char* argv[]) { if (singlePuzzle) { runGame(Gametype::Puzzle, singlePuzzleNr); } + else if (globalData.replayArgument.length()) { + ReplayPlayer rp; + RunGameState(rp); + } else { //game loop MainMenu(); @@ -1388,7 +1396,6 @@ int main(int argc, char* argv[]) { return 0; } - int runGame(Gametype gametype, int level) { globalData.theTopScoresEndless = Highscore("endless"); globalData.theTopScoresTimeTrial = Highscore("timetrial"); @@ -1414,7 +1421,7 @@ int runGame(Gametype gametype, int level) { theGame2.name = globalData.player2name; bool mustsetupgame = true; - + if (singlePuzzle) { LoadPuzzleStages(); BlockGameStartInfo s; @@ -1437,60 +1444,59 @@ int runGame(Gametype gametype, int level) { registerTTHighscorePlayer1 = false; registerTTHighscorePlayer2 = false; switch (gametype) { - case Gametype::SinglePlayerTimeTrial: - StartSinglePlayerTimeTrial(); - break; - case Gametype::StageClear: { - int myLevel = PuzzleLevelSelect(1); - if (myLevel == -1) { - return 1; - } - BlockGameStartInfo s; - s.ticks = SDL_GetTicks(); - s.stageClear = true; - s.level = myLevel; - theGame.NewGame(s); - DrawBackground(globalData.screen); - twoPlayers =false; - BlockGameAction a; - a.action = BlockGameAction::Action::SET_GAME_OVER; - theGame2.DoAction(a); - theGame.name = globalData.player1name; - theGame2.name = globalData.player2name; + case Gametype::SinglePlayerTimeTrial: + StartSinglePlayerTimeTrial(); + break; + case Gametype::StageClear: { + int myLevel = PuzzleLevelSelect(1); + if (myLevel == -1) { + return 1; + } + BlockGameStartInfo s; + s.ticks = SDL_GetTicks(); + s.stageClear = true; + s.level = myLevel; + theGame.NewGame(s); + DrawBackground(globalData.screen); + twoPlayers =false; + BlockGameAction a; + a.action = BlockGameAction::Action::SET_GAME_OVER; + theGame2.DoAction(a); + theGame.name = globalData.player1name; + theGame2.name = globalData.player2name; + } + break; + case Gametype::Puzzle: + if (StartSinglePlayerPuzzle()) { + return 1; } break; - case Gametype::Puzzle: - if (StartSinglePlayerPuzzle()) { - return 1; - } - break; - case Gametype::SinglePlayerVs: - { - //1 player - Vs mode - int theAIlevel = level; //startSingleVs(); - BlockGameStartInfo startInfo; - startInfo.ticks = SDL_GetTicks(); - startInfo.vsMode = true; - startInfo.vsAI = true; - startInfo.level = theAIlevel; - theGame.NewGame(startInfo); - startInfo.AI = true; - theGame2.NewGame(startInfo); - DrawBackground(globalData.screen); - twoPlayers = true; //Single player, but AI plays - theGame.name = globalData.player1name; - theGame2.name = globalData.player2name; - } - break; - case Gametype::TwoPlayerTimeTrial: - StarTwoPlayerTimeTrial(); - break; - case Gametype::TwoPlayerVs: - StartTwoPlayerVs(); - break; - case Gametype::SinglePlayerEndless: - default: - StartSinglePlayerEndless(); + case Gametype::SinglePlayerVs: { + //1 player - Vs mode + int theAIlevel = level; //startSingleVs(); + BlockGameStartInfo startInfo; + startInfo.ticks = SDL_GetTicks(); + startInfo.vsMode = true; + startInfo.vsAI = true; + startInfo.level = theAIlevel; + theGame.NewGame(startInfo); + startInfo.AI = true; + theGame2.NewGame(startInfo); + DrawBackground(globalData.screen); + twoPlayers = true; //Single player, but AI plays + theGame.name = globalData.player1name; + theGame2.name = globalData.player2name; + } + break; + case Gametype::TwoPlayerTimeTrial: + StarTwoPlayerTimeTrial(); + break; + case Gametype::TwoPlayerVs: + StartTwoPlayerVs(); + break; + case Gametype::SinglePlayerEndless: + default: + StartSinglePlayerEndless(); }; mustsetupgame = false; DrawBackground(globalData.screen); @@ -1523,7 +1529,7 @@ int runGame(Gametype gametype, int level) { Config::getInstance()->setShuttingDown(5); done = 1; } - + if (theGame.isGameOver() && isEscapeEvent(event)) { done = 1; } @@ -1703,7 +1709,7 @@ int runGame(Gametype gametype, int level) { } mouseDownX = event.button.x; mouseDownY = event.button.y; - } + } if (event.button.button == SDL_BUTTON_RIGHT) { bool pressed = false; int x = 0; @@ -1768,7 +1774,7 @@ int runGame(Gametype gametype, int level) { } } } - + } } //while event PollEvent - read keys @@ -1792,15 +1798,15 @@ int runGame(Gametype gametype, int level) { if (stageButtonStatus != SBdontShow && (globalData.mousex > theGame.GetTopX()+cordNextButton.x) && (globalData.mousex < theGame.GetTopX()+cordNextButton.x+cordNextButton.xsize) && (globalData.mousey > theGame.GetTopY()+cordNextButton.y) - && (globalData.mousey < theGame.GetTopY()+cordNextButton.y+cordNextButton.ysize)) { + && (globalData.mousey < theGame.GetTopY()+cordNextButton.y+cordNextButton.ysize)) { //Clicked the next button after a stage clear or puzzle nextLevel(theGame, SDL_GetTicks()); } - + if (stageButtonStatus != SBdontShow && (globalData.mousex > theGame.GetTopX()+cordRetryButton .x) &&(globalData.mousex < theGame.GetTopX()+cordRetryButton.x+cordRetryButton.xsize) &&(globalData.mousey > theGame.GetTopY()+cordRetryButton.y) - &&(globalData.mousey < theGame.GetTopY()+cordRetryButton.y+cordRetryButton.ysize)) { + &&(globalData.mousey < theGame.GetTopY()+cordRetryButton.y+cordRetryButton.ysize)) { //Clicked the retry button retryLevel(theGame, SDL_GetTicks()); } diff --git a/source/code/replayhandler.cpp b/source/code/replayhandler.cpp index 657e7f2..e213f35 100644 --- a/source/code/replayhandler.cpp +++ b/source/code/replayhandler.cpp @@ -49,6 +49,26 @@ static void SaveReplayToFile(const SavedReplayStruct& sr, const std::string& fil sago::WriteFileContent(filename.c_str(), ss.str()); } +static void LoadReplayFromPhysFile(SavedReplayStruct& sr, const std::string& filename) { + std::string filecontent = sago::GetFileContent(filename.c_str()); + std::stringstream ss(filecontent); + { + cereal::JSONInputArchive archive(ss); + archive(cereal::make_nvp("savedReplay", sr)); + } +} + +void LoadReplay(const std::string& filename, BlockGameInfo& game1, BlockGameInfo& game2) { + SavedReplayStruct sr; + LoadReplayFromPhysFile(sr, filename); + if (sr.playerInfo.size() > 0) { + game1 = sr.playerInfo.at(0); + } + if (sr.playerInfo.size() > 1) { + game2 = sr.playerInfo.at(1); + } +} + void SaveReplay(const BlockGameInfo& game1) { SavedReplayStruct sr; sr.numberOfPlayers = 1;