diff --git a/data/sprites/menu.sprite b/data/sprites/menu.sprite new file mode 100644 index 0000000..adc68eb --- /dev/null +++ b/data/sprites/menu.sprite @@ -0,0 +1,22 @@ +{ + +"menu_marked" : { + "texture" : "menu_marked", + "topx" : 0, + "topy" : 0, + "height" : 60, + "width" : 600, + "number_of_frames" : 1, + "frame_time" : 1 +}, +"menu_unmarked" : { + "texture" : "menu_unmarked", + "topx" : 0, + "topy" : 0, + "height" : 60, + "width" : 600, + "number_of_frames" : 1, + "frame_time" : 1 +} + +} diff --git a/src/MenuSystem.cpp b/src/MenuSystem.cpp new file mode 100644 index 0000000..b383a93 --- /dev/null +++ b/src/MenuSystem.cpp @@ -0,0 +1,350 @@ +/* +=========================================================================== +blockattack - Block Attack - Rise of the Blocks +Copyright (C) 2005-2012 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 +https://blockattack.net +=========================================================================== +*/ + + +#include "MenuSystem.h" +#include "common.h" +#include "global.hpp" + +static int oldmousex = 0; +static int oldmousey = 0; + +const char* const menu_marked = "menu_marked"; +const char* const menu_unmarked = "menu_unmarked"; + +ButtonGfx standardButton; + +void ButtonGfx::setSurfaces() { + this->xsize = globalData.spriteHolder->GetSprite(menu_marked).GetWidth(); + this->ysize = globalData.spriteHolder->GetSprite(menu_marked).GetHeight(); + if (globalData.verboseLevel) { + std::cout << "Surfaces set, size: " << this->xsize << " , " << this->ysize << "\n"; + } +} + +sago::SagoTextField* ButtonGfx::getLabel(const std::string& text) { + const auto& theLabel = labels.find(text); + if (theLabel != labels.end()) { + return labels[text].get(); + } + std::shared_ptr newField = std::make_shared(); + newField->SetHolder(&globalData.spriteHolder->GetDataHolder()); + newField->SetFont("freeserif"); + newField->SetFontSize(30); + newField->SetOutline(1, {64,64,64,255}); + newField->SetText(text); + labels[text] = newField; + return labels[text].get(); +} + +Button::Button() { + label = ""; + marked = false; + action = nullptr; + popOnRun = false; +} + +Button::~Button() { +} + +Button::Button(const Button& b) { + label = b.label; + marked = b.marked; + action = b.action; + popOnRun = false; +} + +void Button::setLabel(const std::string& text) { + label = text; +} + +void Button::setAction(void (*action2run)(void)) { + action = action2run; +} + +void Button::doAction() { + if (action) { + action(); + return; + } + std::cerr << "Warning: button \"" << label << "\" has no action assigned!"; +} + +void Button::setPopOnRun(bool popOnRun) { + this->popOnRun = popOnRun; +} + +bool Button::isPopOnRun() const { + return popOnRun; +} + +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.getLabel(b.label)->Draw(globalData.screen, b.x+standardButton.xsize/2,b.y+standardButton.ysize/2, + sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center); + //standardButton.thefont->drawCenterAlsoHeight(globalData.screen, b.x+standardButton.xsize/2,b.y+standardButton.ysize/2, b.label); +} + + +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; + } + return false; +} + +void Menu::drawSelf(SDL_Renderer* target) { + for (const Button* b : buttons) { + drawToScreen(*b); + } + drawToScreen(exit); + standardButton.getLabel(title)->Draw(target, 50, 50); +} + + +void Menu::placeButtons() { + int nextY = 100; + int X = 50; + for (Button* it : buttons) { + X = (globalData.xsize - standardButton.xsize)/2; + it->x = X; + it->y = nextY; + nextY += standardButton.ysize+10; + } + exit.x = X; + exit.y = nextY; +} + +void Menu::addButton(Button* b) { + buttons.push_back(b); + b->marked = false; + placeButtons(); +} + +Menu::Menu(SDL_Renderer* screen) { + this->screen = screen; + buttons = std::vector(10); + isSubmenu = true; + exit.setLabel( _("Back") ); +} + +Menu::Menu(SDL_Renderer* screen,bool submenu) { + this->screen = screen; + buttons = std::vector(0); + isSubmenu = submenu; + if (isSubmenu) { + exit.setLabel( _("Back") ); + } + else { + exit.setLabel( _("Exit") ); + } +} + +Menu::Menu(SDL_Renderer* screen, const std::string& title, bool submenu) { + this->screen = screen; + buttons = std::vector(0); + isSubmenu = submenu; + this->title = title; + if (isSubmenu) { + exit.setLabel(_("Back") ); + } + else { + exit.setLabel(_("Exit") ); + } +} + +bool isUpEvent(const SDL_Event& event) { + if ( event.type == SDL_KEYDOWN ) { + if (event.key.keysym.sym == SDLK_UP) { + return true; + } + } + return false; +} + +bool isDownEvent(const SDL_Event& event) { + if ( event.type == SDL_KEYDOWN ) { + if (event.key.keysym.sym == SDLK_DOWN) { + return true; + } + } + return false; +} + +bool isLeftEvent(const SDL_Event& event) { + if ( event.type == SDL_KEYDOWN ) { + if (event.key.keysym.sym == SDLK_LEFT) { + return true; + } + } + return false; +} + +bool isRightEvent(const SDL_Event& event) { + if ( event.type == SDL_KEYDOWN ) { + if (event.key.keysym.sym == SDLK_RIGHT) { + return true; + } + } + return false; +} + +bool isEscapeEvent(const SDL_Event& event) { + if ( event.type == SDL_KEYDOWN ) { + if ( event.key.keysym.sym == SDLK_ESCAPE ) { + return true; + } + } + if (event.type == SDL_CONTROLLERBUTTONDOWN) { + if (event.cbutton.button == SDL_CONTROLLER_BUTTON_Y || event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK ) { + return true; + } + } + return false; +} + +bool isConfirmEvent(const SDL_Event& event) { + if ( event.type == SDL_KEYDOWN ) { + if (event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER ) { + return true; + } + } + if (event.type == SDL_CONTROLLERBUTTONDOWN) { + if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A || event.cbutton.button == SDL_CONTROLLER_BUTTON_B ) { + return true; + } + } + return false; +} + + +bool Menu::IsActive() { + return running; +} +void Menu::Draw(SDL_Renderer* target) { + placeButtons(); + drawSelf(target); +#if DEBUG + static unsigned long int Frames; + static unsigned long int Ticks; + static char FPS[10]; + static sago::SagoTextField fpsField; + sagoTextSetBlueFont(fpsField); + Frames++; + if (SDL_GetTicks() >= Ticks + 1000) { + if (Frames > 999) { + Frames=999; + } + snprintf(FPS, sizeof(FPS), "%lu fps", Frames); + Frames = 0; + Ticks = SDL_GetTicks(); + } + fpsField.SetText(FPS); + fpsField.Draw(globalData.screen, 800, 4); +#endif +} +void Menu::ProcessInput(const SDL_Event& event, bool& processed) { + if (isUpEvent(event)) { + marked--; + if (marked<0) { + marked = buttons.size(); //not -1, since exit is after the last element in the list + } + processed = true; + } + + if (isDownEvent(event)) { + marked++; + if (marked> (int)buttons.size()) { + marked = 0; + } + processed = true; + } + + if (isEscapeEvent(event) && isSubmenu) { + running = false; + processed = true; + } + + if (isConfirmEvent(event)) { + if (marked < (int)buttons.size()) { + buttons.at(marked)->doAction(); + if (buttons.at(marked)->isPopOnRun()) { + running = false; + } + } + if (marked == (int)buttons.size()) { + running = false; + } + processed = true; + } +} + +void Menu::Update() { + for (int i=0; i<(int)buttons.size(); i++) { + buttons.at(i)->marked = (i == marked); + } + exit.marked = (marked == (int)buttons.size()); + Uint8 buttonState = SDL_GetMouseState(nullptr,nullptr); + // If the mouse button is released, make bMouseUp equal true + if ( (buttonState&SDL_BUTTON(1))==0) { + bMouseUp=true; + } + + if (abs(globalData.mousex-oldmousex)>5 || abs(globalData.mousey-oldmousey)>5) { + for (int i=0; i< (int)buttons.size(); ++i) { + if (isClicked(*buttons.at(i), globalData.mousex, globalData.mousey)) { + marked = i; + } + } + if (isClicked(exit, globalData.mousex, globalData.mousey)) { + marked = buttons.size(); + } + oldmousex = globalData.mousex; + oldmousey = globalData.mousey; + } + + //mouse clicked + if ( (buttonState&SDL_BUTTON(1) )==SDL_BUTTON(1) && bMouseUp) { + bMouseUp = false; + for (int i=0; i< (int)buttons.size(); ++i) { + if (isClicked(*buttons.at(i), globalData.mousex, globalData.mousey)) { + buttons.at(i)->doAction(); + if (buttons.at(i)->isPopOnRun()) { + running = false; + } + //Quit here to ensure that we do not continue checking buttons after we have done the action. + return; + } + } + if (isClicked(exit, globalData.mousex, globalData.mousey)) { + running = false; + } + } +} + diff --git a/src/MenuSystem.h b/src/MenuSystem.h new file mode 100644 index 0000000..cf3016f --- /dev/null +++ b/src/MenuSystem.h @@ -0,0 +1,144 @@ +/* +=========================================================================== +blockattack - Block Attack - Rise of the Blocks +Copyright (C) 2005-2012 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://blockattack.net +=========================================================================== +*/ + + +#ifndef _MENUSYSTEM_H +#define _MENUSYSTEM_H + +#include +#include "SDL.h" +#include +#include +#include "sago/SagoSprite.hpp" +#include "sago/GameStateInterface.hpp" +#include +#include "sago/SagoTextField.hpp" + +//The ButtonGfx object hold common media for all buttons, so we can reskin them by only changeing one pointer +struct ButtonGfx +{ + //The size of the buttons, so we don't have to ask w and h from the SDL Surfaces each time + int xsize = 0; + int ysize = 0; + sago::SagoTextField* getLabel(const std::string& text); + void setSurfaces(); +private: + std::map > labels; +}; + +extern ButtonGfx standardButton; + +//A button +class Button +{ +private: + //Pointer to a callback function. + void (*action)(void); + + //If true the menu should also be closed then the button is clicked + bool popOnRun = false; + +public: + //The label. This is written on the button + std::string label; + //Is the button marked? + bool marked = false; + //Where is the button on the screen + int x = 0; + int y = 0; + + Button(); + Button(const Button& b); + virtual ~Button(); + + + //Set the text to write on the button + void setLabel(const std::string& text); + //Set the action to run + void setAction(void (*action2run)(void)); + + virtual void doAction(); //Run the callback function + void setPopOnRun(bool popOnRun); + bool isPopOnRun() const; + + //May hold any other information the callback might need + int iGeneric1 = 0; +}; + + +class Menu : public sago::GameStateInterface +{ +private: + std::vector buttons; //Vector holder the buttons + Button exit; //The exit button is special since it does not have a callback function + bool isSubmenu = false; //True if the menu is a submenu + int marked = 0; //The index of the marked button (for keyboard up/down) + bool running = true; //The menu is running. The menu will terminate then this is false + SDL_Renderer *screen = nullptr; //Pointer to the screen to draw to + std::string title; + void drawSelf(SDL_Renderer* target); //Private function to draw the screen + void placeButtons(); //Rearanges the buttons to the correct place. + bool bMouseUp = false; +public: + //numberOfItems is the expected numberOfItems for vector initialization + //SubMenu is true by default + Menu(SDL_Renderer *screen,bool isSubmenu); + Menu(SDL_Renderer *screen); + Menu(SDL_Renderer *screen, const std::string& title, bool isSubmenu); + virtual ~Menu() {} + + //Add a button to the menu + void addButton(Button *b); + + bool IsActive() override; + void Draw(SDL_Renderer* target) override; + void ProcessInput(const SDL_Event& event, bool &processed) override; + void Update() override; +}; + +class FileMenu +{ +private: + std::string pm_path; + std::string pm_fileending; + bool pm_hidden_files = false; +public: + FileMenu(const std::string& path, const std::string& fileending, bool hidden_files = false); + + std::string getFile(SDL_Surface **screen); +}; + +bool isUpEvent(const SDL_Event& event); + +bool isDownEvent(const SDL_Event& event); + +bool isLeftEvent(const SDL_Event& event); + +bool isRightEvent(const SDL_Event& event); + +bool isEscapeEvent(const SDL_Event& event); + +bool isConfirmEvent(const SDL_Event& event); + +#endif /* _MENUSYSTEM_H */ + diff --git a/src/global.hpp b/src/global.hpp new file mode 100644 index 0000000..b1d73d0 --- /dev/null +++ b/src/global.hpp @@ -0,0 +1,30 @@ +/* +=========================================================================== + * Saland Adventures +Copyright (C) 2014-2018 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 +https://github.com/sago007/saland +=========================================================================== +*/ + +#ifndef GLOBAL_HPP +#define GLOBAL_HPP + +#include "saland/globals.hpp" + +#endif /* GLOBAL_HPP */ + diff --git a/src/saland.cpp b/src/saland.cpp index a4ec3bd..38d7339 100644 --- a/src/saland.cpp +++ b/src/saland.cpp @@ -45,6 +45,8 @@ https://github.com/sago007/saland #include "sagotmx/tmx_struct.h" #include "sago/SagoTextBox.hpp" +#include "MenuSystem.h" + #include "common.h" #include "os.hpp" #include "version.h" @@ -141,6 +143,11 @@ void RunGameState(sago::GameStateInterface& state ) { } } +void startWorld() { + Game g; + RunGameState(g); +} + void runGame() { int posX = 100, posY = 100, width = 1024, height = 768; SDL_Init(SDL_INIT_VIDEO); @@ -157,8 +164,13 @@ void runGame() { TitleScreen ts; RunGameState(ts); - Game g; - RunGameState(g); + standardButton.setSurfaces(); + Menu m(globalData.screen, false); + Button bStart; + bStart.setLabel("Start"); + bStart.setAction(startWorld); + m.addButton(&bStart); + RunGameState(m); SDL_DestroyRenderer(globalData.screen); SDL_DestroyWindow(win); diff --git a/src/saland/globals.hpp b/src/saland/globals.hpp index 93dafe1..6b68f96 100644 --- a/src/saland/globals.hpp +++ b/src/saland/globals.hpp @@ -32,6 +32,10 @@ struct GlobalData { SDL_Renderer* screen = nullptr; std::unique_ptr spriteHolder; sago::SagoDataHolder* dataHolder; + int verboseLevel = 0; + int mousex = 0; + int mousey = 0; + int xsize = 1024; }; extern GlobalData globalData;