diff --git a/.gitignore b/.gitignore index 275e8b6..57d436a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,3 @@ logs/ **/*.log **/*.exe **/*.out* -resources/databases/* -games/* -**/*.o -ArcadeMachine -**/.DS_Store diff --git a/Program.cpp b/Program.cpp new file mode 100644 index 0000000..5dd8084 --- /dev/null +++ b/Program.cpp @@ -0,0 +1,37 @@ +#include "include/ArcadeMachine.h" + +int main() +{ + // Load all resources + set_resources_path("resources" ARCADE_MACHINE_PATH_SEP); + load_resource_bundle("bundle", "resources.txt"); + + // Instantiate Arcade Machine + ArcadeMachine Arcade; + + // Open window and toggle border off. + open_window("arcade-machine", ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + window_toggle_border("arcade-machine"); + + // Do you want to play the intros and fetch new games? + bool play_intro = true; + bool load_games = true; + + // Play Thoth Tech intro + if (play_intro) + { + Arcade.playThothTechIntro(); + Arcade.playArcadeTeamIntro(); + } + // Play SplashKit intro + if (load_games) Arcade.playSplashKitIntro(); + + // Prepare the main menu + Arcade.prepareMainMenu(); + // Draw the main menu + Arcade.mainMenu(); + + free_resource_bundle("bundle"); + + return 0; +} \ No newline at end of file diff --git a/README.md b/README.md index 292c839..d1bd8c5 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,7 @@ # arcade-machine An application to showcase and execute Splashkit games - -## Pre-requisites (all operating systems) - +## Install + Install the [SplashKit](https://splashkit.io) SDK using the [guide](https://splashkit.io/articles/installation/) - -## Pre-requisites (mingw32 / Windows) - -+ Install `make` using `pacman -S make` - -## Building arcade machine (using the Makefile) -Compiling Arcade Machine using the Makefile allows incremental building of changed objects. - -```bash -cd arcade-machine -make -./ArcadeMachine -``` - -Subsequent builds (as you change code) can be completed by using just `make`. If you need to run a clean build again, you can use `make clean` first proceeded by `make`. - -## Building arcade machine (manually) -+ Compile the application with the command ```skm clang++ src/* -Iinclude -lstdc++fs -o test``` +## Run ++ Compile the application with the command ```skm clang++ Program.cpp -Iinclude -lstdc++fs -o test``` + Run the application ```./test``` diff --git a/include/ArcadeMachine.h b/include/ArcadeMachine.h index 24fc101..3047485 100644 --- a/include/ArcadeMachine.h +++ b/include/ArcadeMachine.h @@ -1,6 +1,16 @@ #ifndef ARCADE_MACHINE_H #define ARCADE_MACHINE_H +#ifdef _WIN32 +#define ARCADE_MACHINE_PATH_SEP "\\" +#else +#define ARCADE_MACHINE_PATH_SEP "/" +#endif + +#define ARCADE_MACHINE_SCALING_FACTOR 1 +#define ARCADE_MACHINE_RES_X 1920 * ARCADE_MACHINE_SCALING_FACTOR +#define ARCADE_MACHINE_RES_Y 1080 * ARCADE_MACHINE_SCALING_FACTOR + // Arcade Machine Class #include "Button.h" #include "MenuButton.h" @@ -19,6 +29,7 @@ #include #include #include +#include // Define number of rows and columns in grid #define ROWS 7 @@ -58,17 +69,14 @@ class ArcadeMachine /// SplashKit Production intro Splashscreen m_introSplashkit; - /// SplashKit about screen - AboutScreen m_aboutScreen; - /// Main Menu Selector Selector m_selectorMainMenu; /// Games Menu Selector Selector m_selectorGamesMenu; - /// Instance of Grid Layout - GridLayout m_grid; + /// Instance of Grid + Grid m_grid; /// Mouse pointer point_2d m_mouse; @@ -84,32 +92,351 @@ class ArcadeMachine // Helper function to load developer names into m_arcadeTeamDeveloperNames // Called in the constructor - void loadDeveloperNames(const char* filePath); + void loadDeveloperNames(const char* filePath) + { + std::string line; + std::ifstream developerNamesFile(filePath); + if (developerNamesFile.fail()) + { + std::cout << "error: unable to open developer names file\n"; + } + else if (developerNamesFile.is_open()) + { + while(std::getline(developerNamesFile, line)) + { + m_arcadeTeamDeveloperNames.push_back(line); + } + } + + developerNamesFile.close(); + } - /// Check for Options menu exit - bool m_exitOptions = false; public: // Default Constructor - ArcadeMachine(); + ArcadeMachine() + { + // Instantiate fundamental objects + Helper helper; + ConfigData config; + Selector cursor("cursor"); + Splashscreen introThothTech("intro_thoth_tech"); + + // load developer names into m_arcadeTeamDeveloperNames + loadDeveloperNames("developer_names.txt"); + + Splashscreen introArcadeMachineTeam("intro_arcade_team"); + Splashscreen introSplashkit("intro_splashkit"); + // Set objects to private properties + this->m_helper = helper; + this->m_config = config; + this->m_selectorMainMenu = cursor; + this->m_introThothTech = introThothTech; + this->m_introArcadeTeam = introArcadeMachineTeam; + this->m_introSplashkit = introSplashkit; + } // Destructor - ~ArcadeMachine(); + ~ArcadeMachine(){} // Getters auto get_configs() const -> const vector& { return this->m_configs; } - void mainMenu(); // Starts the Main Menu - void gamesMenu(); // Starts the Games Menu - void optionsMenu(); // Starts the Options Menu - void buttonClicked(point_2d point); // Checks for buttons clicked - void drawMainMenu(); - void prepareMainMenu(); - void playThothTechIntro(); - void playArcadeTeamIntro(); - void playSplashKitIntro(); // Draws the Splashkit Productions logo to the screen and fetches new games from Git repo - void printConfigs(); - void exitProgram(); + /** + Starts the Main Menu + */ + void mainMenu() + { + while (! quit_requested()) + { + process_events(); + clear_screen(); + drawMainMenu(); + buttonClicked(this->m_mouse); + refresh_screen(60); + } + } + + /** + Starts the Games Menu + */ + void gamesMenu() + { + // Instantiate new menu + Menu menu(this->m_configs); + bool overlayActive = menu.getOverlayState(); + write_line("got configs"); + menu.createGrid(); + menu.createButtons(); + menu.createTip(); + write_line("got buttons"); + write_line("set image"); + this->m_gameBtns = menu.getButtons(); + + while ((!key_typed(ESCAPE_KEY) && !overlayActive) || overlayActive) + { + //write_line("into while"); + overlayActive = menu.getOverlayState(); + process_events(); + clear_screen(); + // Get mouse position + this->m_mouse = mouse_position(); + // Draw games menu + menu.drawMenuPage(); + // Check input + // this->_action = this->m_selectorGamesMenu.check_key_input(this->_games_btns); + refresh_screen(60); + } + } + + /** + Starts the Options Menu + */ + void optionsMenu() + { + Option options; + Audio *audio = new Audio(); + bool has_background_music = false; + + while (!key_down(ESCAPE_KEY)) + { + process_events(); + clear_screen(); + + options.updateOption(); + + if(!has_background_music) + { + audio->playMusic(options.getCurrentMusic(), options.getVolume()); + has_background_music=false; + } + + if(options.isChangeMusic()) + { + has_background_music=false; + } + + if(options.isChangeVoLume()) + { + audio->setVolume(options.getVolume()); + } + refresh_screen(60); + } + fade_music_out(500); + } + + /** + Checks for buttons clicked + + @param point The mouse pointer location on screen + */ + void buttonClicked(point_2d point) + { + // Play + if ( this->m_action == "play" || (sprite_at(this->m_menuBtns[0]->btn(), point) && mouse_clicked(LEFT_BUTTON)) ) + { + gamesMenu(); + write_line("Play button clicked"); + } + + // Options + else if ( this->m_action == "options" || (sprite_at(this->m_menuBtns[1]->btn(), point) && mouse_clicked(LEFT_BUTTON)) ) + { + optionsMenu(); + write_line("Options button clicked"); + } + + // Exit + else if ( this->m_action == "exit" || (sprite_at(this->m_menuBtns[2]->btn(), point) && mouse_clicked(LEFT_BUTTON)) ) + { + write_line("Exit button clicked"); + exitProgram(); + } + } + + /** + Draws the Main Menu + */ + void drawMainMenu() + { + // Get mouse position + this->m_mouse = mouse_position(); + this->m_grid.drawGrid(); + // Draw cursor + draw_sprite(this->m_selectorMainMenu.getCursor()); + // Get button postions + Cell play = this->m_grid.getCell(2, 10); + Cell options = this->m_grid.getCell(3, 10); + Cell exit = this->m_grid.getCell(4, 10); + // Arcade Machine title + draw_text("Arcade", COLOR_BLACK, "font_title", 100, 1180, 100); + draw_text("Machine", COLOR_BLACK, "font_title", 100, 1150, 200); + // Draw text on buttons + draw_text("play!", COLOR_BLACK, "font_btn", 70, play.button->x() + (play.button->centreX()/2) + 5, play.button->y() + 5); + draw_text("options", COLOR_BLACK, "font_btn", 70, options.button->x() + (options.button->centreX()/2) - 20, options.button->y() + 5); + draw_text("exit", COLOR_BLACK, "font_btn", 70, exit.button->x() + (exit.button->centreX()/2) + 20, exit.button->y() + 5); + + // Check input in selector class. + this->m_menuButtonNode = this->m_selectorMainMenu.checkKeyInput(this->m_menuButtonNode); + + // Check input in selector class. + this->m_action = this->m_selectorMainMenu.checkForSelection(this->m_menuButtonNode); + } + + /** + Prepares the Main Menu + */ + void prepareMainMenu() + { + // Get the data from the config files. + this->m_configs = this->m_helper.ConfigDataList(); + // Initialise grid + Grid grid(ROWS, COLS); + this->m_grid = grid; + // Create menu buttons + Button *play = new MenuButton(Button::PLAY, 1.5); + Button *opts = new MenuButton(Button::OPTS, 1.5); + Button *exit = new MenuButton(Button::EXIT, 1.5); + + // Add menu buttons to local vector + this->m_menuBtns.push_back(play); + this->m_menuBtns.push_back(opts); + this->m_menuBtns.push_back(exit); + + // Fetch menu background + bitmap thoth = bitmap_named("thoth"); + // Update grid cells with assets + this->m_grid.setBackground(thoth); + + // Make new nodes with buttons. + this->m_menuButtonNode = new ButtonNode(play); + this->m_menuButtonNode->addAfter(new ButtonNode(opts)); + this->m_menuButtonNode->addBefore(new ButtonNode(exit)); + + // Update grid with nodes. + this->m_grid.updateCell(m_menuButtonNode->button, 2, 10); + this->m_grid.updateCell(m_menuButtonNode->getPrev()->button, 4, 10); + this->m_grid.updateCell(m_menuButtonNode->getNext()->button, 3, 10); + + // Play main menu music + if (this->m_playMusic) play_music("music_mainmenu"); + } + + /// Plays the Thoth Tech splashscreen animation + void playThothTechIntro() + { + // Set fade increment (opacity) + double alpha = 1.0; + // Set iterations + int i = 60; + // Play Thoth Tech Company sound + play_sound_effect("intro_thoth"); + + while(i != 0) + { + process_events(); + clear_screen(); + // Draw logo + m_introThothTech.drawTitlePage(); + // Fill screen with white at alpha value (opacity) + fill_rectangle(rgba_color(1.0, 1.0, 1.0, alpha), 0, 0, ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + // Decrement i and alpha + i--; alpha = alpha - 0.05; + // If alpha is == 0, hold image for 1.5 seconds + if (abs(alpha - 0.0) < 1e-9) + { + delay(2000); + /* After this has happened, the alpha value will continue into the negatives + The colour function continues to accept negative alpha values, + effectively creating a fade out animation for the remainder of the while loop + */ + } + refresh_screen(60); + delay(50); + } + } + + /// Plays the Arcade Team splashscreen animation + void playArcadeTeamIntro() + { + // Set fade increment (opacity) + double alpha = 1.0; + // Set iterations + int i = 60; + // Play Thoth Tech Company sound + play_sound_effect("intro_coin"); + + while(i != 0) + { + process_events(); + clear_screen(); + + // Draw logo + m_introArcadeTeam.drawTitlePage(); + + int developerNameSpacing = 32; + int developerNameIndex = 0; + for (const auto& developerName : m_arcadeTeamDeveloperNames) + { + draw_text(developerName, + COLOR_BLACK, + "font_text", + 26, + (ARCADE_MACHINE_RES_X / 2) - 180, + (ARCADE_MACHINE_RES_Y / 2) + 220 + developerNameSpacing * developerNameIndex); + developerNameIndex++; + } + + // Fill screen with white at alpha value (opacity) + fill_rectangle(rgba_color(1.0, 1.0, 1.0, alpha), 0, 0, ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + // Decrement i and alpha + i--; alpha = alpha - 0.05; + // If alpha is == 0, hold image for 1.5 seconds + if (abs(alpha - 0.0) < 1e-9) + { + play_sound_effect("intro_start"); + delay(2000); + /* After this has happened, the alpha value will continue into the negatives + The colour function continues to accept negative alpha values, + effectively creating a fade out animation for the remainder of the while loop + */ + } + refresh_screen(60); + delay(50); + } + } + + /** + Draws the Splashkit Productions logo to the screen and + fetches new games from Git repo + */ + void playSplashKitIntro() + { + // Pull the most recent version of the arcade-games repo. + do + { + // Draw SplashKit productions screen + this->m_introSplashkit.drawTitlePage(); + draw_text("Loading...", COLOR_SLATE_GRAY, "font_text", 60, ARCADE_MACHINE_RES_X / 2 - 100, ARCADE_MACHINE_RES_Y / 2 + 350); + refresh_screen(); + + } while (!this->m_config.getFromGit("https://github.com/thoth-tech/arcade-games.git", "games")); + } + + /** + Print config data to console + */ + void printConfigs() + { + this->m_config.printConfigData(); + } + /** + Abort this application + */ + void exitProgram() + { + exit(EXIT_SUCCESS); + } }; #endif \ No newline at end of file diff --git a/include/Audio.h b/include/Audio.h index e5e1677..3941222 100644 --- a/include/Audio.h +++ b/include/Audio.h @@ -4,69 +4,19 @@ #include class Audio { - private: - vector m_music; - int songId; public: - Audio() - { - getAllMusic(); - } - - void setSongId(int id) - { - this->songId = id; - } - - void playMusic(float volume) - { - play_music(m_music[0]); - set_music_volume(volume); - setSongId(0); - } - - void playMusic(std::string song, float volume) - { - play_music(song); - set_music_volume(volume); - music thisSong = music_named(song); - - for (int i = 0; i < this->m_music.size(); i++) - { - if (thisSong == this->m_music[i]) - { - write_line("same song"); - setSongId(i); - } - } - } + Audio(){} void playMusic(int _currentMusic, float volume) { play_music(std::to_string(_currentMusic)); set_music_volume(volume); - } - - void playNextSong() - { - if (this->songId != (this->m_music.size() - 1)) - play_music(this->m_music[this->songId + 1]); - else - play_music(this->m_music[0]); - } + } void setVolume(float volume) { set_music_volume(volume); - } - - void getAllMusic() - { - m_music.push_back(music_named("music_mainmenu")); - m_music.push_back(music_named("1")); - m_music.push_back(music_named("2")); - m_music.push_back(music_named("3")); - } + } }; #endif diff --git a/include/Button.h b/include/Button.h index 855cb2a..5b6aa8c 100644 --- a/include/Button.h +++ b/include/Button.h @@ -10,89 +10,191 @@ * This abstract class is the base class for all derived classes * Contains three overloaded contructors and three virtual methods */ -class Button -{ -public: - /// This buttons ID - int m_id; - - /// This buttons bitmap - bitmap m_pic; - - /// This buttons sprite - sprite m_btn; - - /// This buttons position in the window as point_2d in pixels (x,y) - point_2d m_btnLocation; - - /// This buttons position in the window in pixels - int m_x; - int m_y; - - /// This buttons centre point offset width in pixels - int m_centreX; - - /// This buttons centre point offset height in pixels - int m_centreY; - std::string m_btnColor; - - /// This buttons hightlight bitmap - bitmap m_btnHighlightPic; - - /// This buttons hightlight layer - std::string m_btnHighlightText; - - /// This buttons image path - std::string m_imagePath; - - /// Getters: - auto id() const -> const int& { return m_id; } - auto pic() const -> const bitmap& { return m_pic; } - auto btn() const -> const sprite& { return m_btn; } - auto location() const -> const point_2d& { return m_btnLocation; } - auto x() const -> const int& { return m_x; } - auto y() const -> const int& { return m_y; } - auto centreX() const -> const int& { return m_centreX; } - auto centreY() const -> const int& { return m_centreY; } - auto color() const -> const std::string& { return m_btnColor; } - - /// Setters - void setId(int id) { this->m_id = id; } - void setX(const int &x) { this->m_x = x; } - void setY(const int &y) { this->m_y = y; } - - /** - * @brief Enumeration of button types - * - */ - enum Color - { - PLAY, - EXIT, - OPTS, - GAME, - HOME, - SOUND, - DISPLAY, - STATS - }; - - // Default Constructor - Button() {} - Button(Color c, float scale = 1); - Button(Color c, float x, float y, int xCell, int yCell, float scale = 1); - Button(Color c, std::string image, float scale = 1); - - // Destructor - virtual ~Button() {} - - // Virtual fucntions - virtual void btnImage(std::string image) = 0; - virtual void drawButton() = 0; - virtual std::string action(std::string keyinput = "") = 0; - - std::string btn_color(Color c); - +class Button { + public: + /// This buttons ID + int m_id; + /// This buttons bitmap + bitmap m_pic; + /// This buttons sprite + sprite m_btn; + /// This buttons position in the window as point_2d in pixels (x,y) + point_2d m_btnLocation; + /// This buttons position in the window in pixels + int m_x; + int m_y; + /// This buttons centre point offset width in pixels + int m_centreX; + /// This buttons centre point offset height in pixels + int m_centreY; + std::string m_btnColor; + /// This buttons hightlight bitmap + bitmap m_btnHighlightPic; + /// This buttons hightlight layer + std::string m_btnHighlightText; + /// This buttons image path + std::string m_imagePath; + + /// Getters: + auto id() const -> const int& { return m_id; } + auto pic() const -> const bitmap& { return m_pic; } + auto btn() const -> const sprite& { return m_btn; } + auto location() const -> const point_2d& { return m_btnLocation; } + auto x() const -> const int& { return m_x; } + auto y() const -> const int& { return m_y; } + auto centreX() const -> const int& { return m_centreX; } + auto centreY() const -> const int& { return m_centreY; } + auto color() const -> const std::string& { return m_btnColor; } + + /// Setters + void setId(int id) { this->m_id = id; } + void setX(const int &x){ this->m_x = x; } + void setY(const int &y){ this->m_y = y; } + + /** + * @brief Enumeration of button types + * + */ + enum Color + { + PLAY, + EXIT, + OPTS, + GAME, + HOME + }; + + // Default Constructor + Button(){} + + /** + * @brief First Overloaded Constructor + * + * @param c button type / color + * @param scale size multiplier + * + */ + Button(Color c, float scale = 1) + { + // button color + this->m_btnColor = btn_color(c); + // load button color image + //this->_pic = load_bitmap(to_string(c), this->_color); + // create sprite from image + this->m_btn = create_sprite(this->m_btnColor); + // add hightlight layer to sprite + sprite_add_layer(this->m_btn, this->m_btnHighlightPic, this->m_btnHighlightText); + // scale the sprite + sprite_set_scale(this->m_btn, scale); + // get the centre points of the sprite + this->m_centreX = sprite_width(this->m_btn) /2; + this->m_centreY = sprite_height(this->m_btn)/2; + } + + /** + * @brief Second Overloaded Constructor + * Calculates the position in the window + * + * @param c button type / color + * @param x intended x-axis position in window + * @param y intended y-axis position in window + * @param xCell size of cell x-dimension + * @param yCell size of cell y-dimension + * @param scale size multiplier + */ + Button(Color c, float x, float y, int xCell, int yCell, float scale = 1) + { + // button color + this->m_btnColor = btn_color(c); + // create sprite from image + this->m_btn = create_sprite(this->m_btnColor); + // add hightlight layer to sprite + sprite_add_layer(this->m_btn, this->m_btnHighlightPic, this->m_btnHighlightText); + // get the centre points of the sprite + this->m_centreX = sprite_width(this->m_btn) /2; + this->m_centreY = sprite_height(this->m_btn)/2; + // store the intended location + this->m_x = x * xCell; m_y = y * yCell; + // set button sprite centre point to intended location + sprite_set_x(this->m_btn, this->m_x - this->m_centreX); + sprite_set_y(this->m_btn, this->m_y - this->m_centreY); + // store the window centre point of button as location + this->m_btnLocation = center_point(this->m_btn); + // scale the sprite + sprite_set_scale(this->m_btn, scale); + } + + /** + * @brief Third Overloaded Constructor + * Calculates the position in the window + * + * @param c button type / color + * @param image bitmap name + * @param scale size multiplier + */ + Button(Color c, std::string image, float scale = 1) + { + // button color + this->m_btnColor = btn_color(c); + // create bitmap from string + //this->_pic = load_bitmap(image, image); + // create sprite from image + this->m_btn = create_sprite(image); + // add hightlight layer to sprite + sprite_add_layer(this->m_btn, this->m_btnHighlightPic, this->m_btnHighlightText); + // scale the sprite + sprite_set_scale(this->m_btn, scale); + // get the centre points of the sprite + this->m_centreX = sprite_width(this->m_btn) /2; + this->m_centreY = sprite_height(this->m_btn)/2; + } + + // Destructor + ~Button(){} + + // Virtual fucntions + virtual void btnImage(std::string image) = 0; + virtual void drawButton() = 0; + virtual std::string action(std::string keyinput = "") = 0; + + /** + * @brief Gets the filepath to the requested color (image) + * + * @param c + * @return * string + */ + std::string btn_color(Color c) + { + std::string filepath = "buttons/"; + switch(c) + { + case PLAY: + m_btnHighlightPic = bitmap_named("play_hghlt"); + m_btnHighlightText = "play_highlight"; + return "btn_play"; + break; + case EXIT: + m_btnHighlightPic = bitmap_named("exit_hghlt"); + m_btnHighlightText = "exit_highlight"; + return "btn_exit"; + break; + case OPTS: + m_btnHighlightPic = bitmap_named("options_hghlt"); + m_btnHighlightText = "options_highlight"; + return "btn_opts"; + break; + case GAME: + m_btnHighlightPic = bitmap_named("game_hghlt"); + m_btnHighlightText = "game_highlight"; + return ""; + case HOME: + return filepath + "Gray/home.png"; + break; + default: + return "btn_play"; + break; + } + } }; #endif \ No newline at end of file diff --git a/include/ButtonNode.h b/include/ButtonNode.h index a537fae..48fc2e3 100644 --- a/include/ButtonNode.h +++ b/include/ButtonNode.h @@ -3,7 +3,6 @@ #include "ConfigData.h" #include "Button.h" -#include "GameData.h" class ButtonNode { @@ -14,7 +13,6 @@ class ButtonNode public: Button *button; ConfigData config; - GameData stats; /** * @brief construct a new Button Node object diff --git a/include/ConfigData.h b/include/ConfigData.h index 348cc99..3e3ee54 100644 --- a/include/ConfigData.h +++ b/include/ConfigData.h @@ -1,95 +1,261 @@ #ifndef ARCADE_MACHINE_CONFIG_DATA_H #define ARCADE_MACHINE_CONFIG_DATA_H +#include +#include #include +#include #include -#include "splashkit.h" - -struct s_ExecutablePath { - std::string path; - std::string file; - std::string filePath; -}; +#include /** -* @brief Parses the configuration data from config.txt files to a data object -* -*/ -class ConfigData -{ -private: - /// This configs ID - int m_id; - - /// The repository - std::string m_repo; - - /// This programming language this game was written in - std::string m_language; - - /// The thumbnail image of this game - std::string m_image; - - /// The title of this game - std::string m_title; - - /// The genre of this game - std::string m_genre; - - /// The MPA classification rating of this game - std::string m_rating; - - /// Th author/creator of this game - std::string m_author; - - /// The path to the Windows executable of this game - std::string m_win_exe; - - /// The path to the Linux binaries of this game - std::string m_lin_exe; - - /// The path to the MacOS binaries of this game - std::string m_mac_exe; - - /// The folder this game is inside - std::string m_folder; - - /// A descritpion of the game - std::string m_description; - -public: - ConfigData() {} - ConfigData(std::string configFile); - - //Setters: - auto setId(int &i) { m_id = i; } - auto setFolder(std::string &dir) { m_folder = dir; } - // Getters: - auto id() const -> const int& { return m_id; } - auto repo() const -> const std::string& { return m_repo; } - auto language() const -> const std::string& { return m_language; } - auto image() const -> const std::string& { return m_image; } - auto title() const -> const std::string& { return m_title; } - auto genre() const -> const std::string& { return m_genre; } - auto rating() const -> const std::string& { return m_rating; } - auto author() const -> const std::string& { return m_author; } - auto win_exe() const -> const std::string& { return m_win_exe; } - auto lin_exe() const -> const std::string& { return m_lin_exe; } - auto mac_exe() const -> const std::string& { return m_mac_exe; } - auto folder() const -> const std::string& { return m_folder; } - auto description() const -> const std::string& { return m_description; } - - std::ifstream openFile(std::string file); - std::vector readTxt(std::ifstream file); - void collectConfigData(std::vector configs = std::vector()); - json readJson(std::string filepath); - void collectJsonData(json json_configs = {}); - bool getFromGit(std::string url, const char* dir); - void renameDir(const char* dir); - void deleteDir(std::string dir); - void printConfigData(); - struct s_ExecutablePath getExecutablePath(); + * @brief Parses the configuration data from config.txt files to a data object + * + */ +class ConfigData{ + private: + /// This configs ID + int m_id; + /// The repository + std::string m_repo; + /// This programming language this game was written in + std::string m_language; + /// The thumbnail image of this game + std::string m_image; + /// The title of this game + std::string m_title; + /// The genre of this game + std::string m_genre; + /// The MPA classification rating of this game + std::string m_rating; + /// Th author/creator of this game + std::string m_author; + /// The path to the executable of this game + std::string m_exe; + /// The folder this game is inside + std::string m_folder; + /// A descritpion of the game + std::string m_description; + public: + /** + * @brief Default Constructor + * + * @return A new Config Data object + */ + ConfigData(){} + + /** + * @brief Construct a new Config Data object + * + * @param configFile The config.txt file + */ + ConfigData(std::string configFile) + { + collectConfigData(readTxt(openFile(configFile))); + } + + //Setters: + auto setId(int &i) { m_id = i; } + auto setFolder(std::string &dir) { m_folder = dir; } + // Getters: + auto id() const -> const int& { return m_id; } + auto repo() const -> const std::string& { return m_repo; } + auto language() const -> const std::string& { return m_language; } + auto image() const -> const std::string& { return m_image; } + auto title() const -> const std::string& { return m_title; } + auto genre() const -> const std::string& { return m_genre; } + auto rating() const -> const std::string& { return m_rating; } + auto author() const -> const std::string& { return m_author; } + auto exe() const -> const std::string& { return m_exe; } + auto folder() const -> const std::string& { return m_folder; } + auto description() const -> const std::string& { return m_description; } + + /** + * @brief Generic open file function + * + * @param file The config.txt file + * @return A file as ifstream object + */ + std::ifstream openFile(std::string file) + { + std::ifstream configFile; + + configFile.open(file); + + if(configFile.fail()) + { + std::cerr << "Error Opening File" << std::endl; + exit(1); + } + + return configFile; + } + + /** + * @brief Reads the contents of a file, ignoring comments indicated by '#' + * + * @param file The ifstream object + * @return An array of data from a text file + */ + std::vector readTxt(std::ifstream file) + { + std::vector configItems; + std::string line; + char c = '#'; + char s = ' '; + + while(getline(file, line)){ + if(line[0] != c && line[0] != s) + { + configItems.push_back(line); + } + } + + return configItems; + } + + /** + * @brief Populates this config data with the data from the given array + * + * @param configs a vector of strings + * @return * void + */ + void collectConfigData(std::vector configs = std::vector()) + { + std::smatch sm; + + if (configs.size() > 0) + { + for (int i = 0; i < configs.size(); i++) + { + const std::string s = configs[i]; + + if(std::regex_search(s.begin(), s.end(), sm, std::regex("(.*)=(.*)"))) + { + if (sm[1] == "title") this->m_title = sm[2]; + else if (sm[1] == "author") this->m_author = sm[2]; + else if (sm[1] == "genre") this->m_genre = sm[2]; + else if (sm[1] == "description") this->m_description = sm[2]; + else if (sm[1] == "rating") this->m_rating = sm[2]; + else if (sm[1] == "language") this->m_language = sm[2]; + else if (sm[1] == "image") this->m_image = sm[2]; + else if (sm[1] == "executable") this->m_exe = sm[2]; + else if (sm[1] == "repository") this->m_repo = sm[2]; + } + } + } + } + + /** + * @brief Parses a json filepath as string to json object + * + * @param filepath + * @return json + */ + json readJson(std::string filepath) + { + json config_items = json_from_file(filepath); + return config_items; + } + + /** + * @brief Add json strings to data object + * + * @param json_configs + */ + void collectJsonData(json json_configs = {}) + { + this->m_repo = json_read_string(json_configs, "repo"); + this->m_language = json_read_string(json_configs, "language"); + this->m_image = json_read_string(json_configs, "image"); + this->m_title = json_read_string(json_configs, "title"); + this->m_genre = json_read_string(json_configs, "genre"); + this->m_rating = json_read_string(json_configs, "rating"); + this->m_author = json_read_string(json_configs, "author"); + this->m_exe = json_read_string(json_configs, "exe"); + } + + /** + * @brief Clones a git repository given the URL and proposed directory name + * + * @param url git repository url + * @param dir directory to clone to + * @return true + * @return false + */ + bool getFromGit(std::string url, const char* dir) + { + struct stat info; + + if (stat(dir, &info) != 0){ + system(("git clone " + url + " " + dir).c_str()); + } else { + std::string d = dir; + system(("git -C " + d + " pull " + url).c_str()); + } + + return true; + } + + /** + * @brief Change the name of a directory + * + * @param dir directory to change the name of + */ + void renameDir(const char* dir) + { + std::string error; + int n = m_title.length(); + char name[n+1]; + strcpy(name, m_title.c_str()); + try{ + rename(dir, name); + throw(error); + } catch(std::string error) { + std::cerr << "Name cannot be changed" << std::endl; + std::cerr << error << std::endl; + exit(1); + } + } + /** + * @brief Delete a directory + * + * @param dir name of direcotry to delete + */ + void deleteDir(std::string dir) + { + std::string error; + try{ + system(("rmdir -s -q " + dir).c_str()); + throw(error); + } catch (std::string e) { + std::cerr << "Name cannot be changed" << std::endl; + std::cerr << error << std::endl; + } + } + + /** + * @brief Print the data contained in this object + * + */ + void printConfigData() + { + std::string i = std::to_string(id()); + write_line("========================"); + write_line("ID: " + i); + write_line("Title = " + title()); + write_line("Author = " + author()); + write_line("Genre = " + genre()); + write_line("Description = " + description()); + write_line("Rating = " + rating()); + write_line("Repo = " + repo()); + write_line("Language = " + language()); + write_line("Image = " + image()); + write_line("Exe = " + exe()); + write_line("Folder = " + folder()); + write_line("========================"); + } }; #endif \ No newline at end of file diff --git a/include/GameScreenButton.h b/include/GameScreenButton.h index 516174d..cf0f596 100644 --- a/include/GameScreenButton.h +++ b/include/GameScreenButton.h @@ -2,23 +2,42 @@ #define ARCADE_MACHINE_GAME_SCREEN_BUTTON_H #include +#include "splashkit.h" #include "Button.h" /** -* @brief Buttons created for Game Screen Menu -* -* Derived from abstract Button class -*/ -class GameScreenButton : public Button -{ -public: - GameScreenButton(Color c, float scale = 1); - GameScreenButton(Color c, std::string s, float scale = 1); + * @brief Buttons created for Game Screen Menu + * + * Derived from abstract Button class + */ +class GameScreenButton : public Button { + public: + // First constructor + GameScreenButton(Color c, float scale = 1) : Button(c, scale){} + // Third constructor + GameScreenButton(Color c, std::string s, float scale = 1) : Button(c, s, scale){} - void btnImage(std::string image) override; - void drawButton() override; - std::string action(std::string keyinput = "") override; + /** + * @brief returns the action + * + * @param keyinput + * @return string + */ + std::string action(std::string keyinput = "") + { + return keyinput; + } + void btnImage(std::string image) + { + this->m_btnColor = image; + } + + // draw button to screen + void drawButton() + { + draw_sprite(this->m_btn); + } }; #endif \ No newline at end of file diff --git a/include/GridLayout.h b/include/GridLayout.h index 3377398..9ebc1b9 100644 --- a/include/GridLayout.h +++ b/include/GridLayout.h @@ -6,13 +6,15 @@ #include "Button.h" #include "Cell.h" -class GridLayout +void write(std::string text); + +class Grid { private: // Stores the bitmap used for the background bitmap m_background = nullptr; // Stores the number of columns per row - int* m_colsArray; + int *m_colsArray; // Different number of columns per row? bool _useColsArray = false; // Columns, used if number of columns is fixed @@ -20,7 +22,7 @@ class GridLayout // Rows int _rows; // Stores information for each cell (bitmap, span) - Cell* _grid; + Cell *_grid; // Number of cells int _cells = 0; // Scale the bitmap to fill the cell @@ -29,34 +31,490 @@ class GridLayout bool _gridEmpty = true; public: - GridLayout() {} - GridLayout(int rows, int cols, bool scaleToFit = false); - GridLayout(int rows, int colsArray[], bool scaleToFit = false); - - void destroy(); // clears memory - - void setBackground(bitmap bmp); - - drawing_options bitmapScaleOpt(int bmpWidth, int bmpHeight, double cellWidth, double cellHeight, int span); - - void drawCells(); - void drawGrid(); - - int findCell(int row, int col); - Cell getCell(int row, int col); - - void updateCell(const bitmap &bmp, int row, int col, int span = 1, bool centre = true); - void updateCell(const sprite &sprite, int row, int col, int span = 1, bool centre = true); - void updateCell(Button *button, int row, int col, int span = 1, bool centre = true); - - void updateAllCells(bitmap bmp, bool centre = true); - void updateAllCells(sprite sprite, bool centre = true); - - void drawLayout(); - void clearGrid(); - point_2d findCellFromLoc(int x, int y); - void clearCell(int row, int col); + // Constructor: + // Default Constructor + Grid(){} + /** + * Construct a new grid object with a fixed number of columns/rows + * @param rows number of rows + * @param cols number of columns + * @param scaleToFit scale the image to fill the cell + */ + Grid(int rows, int cols, bool scaleToFit = false) + { + _scaleToFit = scaleToFit; + _rows = rows; + _cols = cols; + // Calculate number of cells + _cells = _rows * _cols; + // Initialise the grid + _grid = new Cell[_cells]; + } + /** + * @brief Construct a new Grid object with a dynamic number of columns per row + * + * @param rows number of rows + * @param colsArray array of columns per row + * @param scaleToFit scale the image to fill the cell + */ + Grid(int rows, int colsArray[], bool scaleToFit = false) + { + // Add check to ensure that the length of colsArray matches the number of rows + _scaleToFit = scaleToFit; + _useColsArray = true; + _rows = rows; + m_colsArray = colsArray; + // Calculate the number of cells in the grid + for (size_t i = 0; i < rows; i++) + { + // Sum of all columns in each row + _cells += colsArray[i]; + } + // Initialise the grid + _grid = new Cell[_cells]; + } + + /** + * @brief Set the background + * + * @param bmp bitmap to use as background + */ + void setBackground(bitmap bmp) + { + m_background = bmp; + } + + /** + * @brief Calculate the bitmap scaling factor + * + * @param bmp bitmap to scale + * @param cellWidth width of the cell + * @param cellHeight height of the cell + * @return drawing_options options + */ + drawing_options bitmapScaleOpt(int bmpWidth, int bmpHeight, double cellWidth, double cellHeight, int span) + { + return option_scale_bmp((cellWidth / bmpWidth) * span, cellHeight / bmpHeight); + } + + /** + * @brief Draw the cell boundaries, to help with placement + * + */ + void drawCells() + { + // Vertical offset between each cell + double yOffset = current_window_height() / _rows; + // Horizontal offset between each cell + double xOffset; + // Calculate the horizontal offset + // If the number of columns is fixed + if (!_useColsArray) + xOffset = current_window_width() / _cols; + + int index = 0; + // Iterate over rows + for (size_t i = 0; i < _rows; i++) + { + // If user specified dynamic number of columns per row + if (_useColsArray) + { + // Update horizontal offset each row + xOffset = current_window_width() / m_colsArray[i]; + // Number of columns to iterate over + _cols = m_colsArray[i]; + } + // Iterate over columns + for (size_t j = 0; j < _cols; j++) + { + draw_rectangle(COLOR_BLACK, (double)(xOffset * j), (double)(yOffset * i), (double)xOffset, (double)yOffset); + ++index; + } + } + } + + /** + * @brief Draw the items in the grid + * + */ + void drawGrid() + { + if (m_background) + draw_bitmap(m_background, 0, 0); + // Vertical offset between each cell + double yOffset = current_window_height() / _rows; + // Horizontal offset between each cell + double xOffset; + // How many iterations to skip, (if a cell spans multiple columns) + int skipIterations = 0; + drawing_options options = option_defaults(); + // Calculate the horizontal offset + // If the number of columns is fixed + if (!_useColsArray) + xOffset = current_window_width() / _cols; + + int index = 0; + // Iterate over rows + for (size_t i = 0; i < _rows; i++) + { + // If user specified dynamic number of columns per row + if (_useColsArray) + { + // Update horizontal offset each row + xOffset = current_window_width() / m_colsArray[i]; + // Number of columns to iterate over + _cols = m_colsArray[i]; + } + // Iterate over columns + for (size_t j = 0; j < _cols; j++) + { + // Skip an iteration + if (skipIterations > 0) + { + --skipIterations; + ++index; + continue; + } + // If the cell is not empty + if (_grid[index].cellType != EMPTY) + { + double x = (xOffset * j); + double y = (yOffset * i); + + // Draw object into cell, centre using dimensions + switch (_grid[index].cellType) + { + case BMP: + if (_scaleToFit) + { + options = bitmapScaleOpt(bitmap_width(_grid[index].bmp), bitmap_height(_grid[index].bmp), xOffset, yOffset, _grid[index].span); + } + if (_grid[index].centre) + { + x = x + (((xOffset * _grid[index].span) - bitmap_width(_grid[index].bmp)) / 2); + y = y + ((yOffset - bitmap_height(_grid[index].bmp)) / 2); + } + draw_bitmap(_grid[index].bmp, x, y, options); + break; + case SPT: + if (_scaleToFit) + write("ScaleToFit: Feature not currently available with use of sprites.\n"); + if (_grid[index].centre) + { + x = x + (((xOffset * _grid[index].span) - sprite_width(_grid[index].spr)) / 2); + y = y + ((yOffset - sprite_height(_grid[index].spr)) / 2); + } + draw_sprite(_grid[index].spr, x, y); + break; + case BTN: + if (_scaleToFit) + write("ScaleToFit: Feature not currently available with use of sprites.\n"); + if (_grid[index].centre) + { + x = x + ((xOffset * _grid[index].span) / 2) - _grid[index].button->centreX(); + y = y + _grid[index].button->centreY(); + } + sprite_set_x(_grid[index].button->btn(), x); + sprite_set_y(_grid[index].button->btn(), y); + // Update Button position of button + this->_grid[index].button->setX(sprite_x(this->_grid[index].button->btn())); + this->_grid[index].button->setY(sprite_y(this->_grid[index].button->btn())); + // Draw Button + _grid[index].button->drawButton(); + break; + default: + break; + } + } + // If the cell spans multiple columns, skip the next iterations + if (_grid[index].span > 1) + { + skipIterations = _grid[index].span - 1; + } + ++index; + } + } + } + + /** + * @brief Find a cell in the grid using row/col + * + * @param row row of the cell + * @param col column of the cell + * @return int index of the cell + */ + int findCell(int row, int col) + { + int cellNum = 0; + // Selected row is out of bounds + if (_rows <= row) + { + throw std::out_of_range("Row index out of range"); + return -1; + } + // Dynamic number of columns/row + if (_useColsArray) + { + // Selected column is out of bounds + if (m_colsArray[row] < col + 1) + { + throw std::out_of_range("Column index out of range"); + return -1; + } + // Calculate the cell number + for (size_t i = 0; i < row; i++) + { + cellNum += m_colsArray[i]; + } + + cellNum += col; + } + // Selected column is out of bounds + else if (_cols <= col) + { + throw std::out_of_range("Column index out of range"); + return -1; + } + // Fixed number of columns + else + { + // Calculate the cell number + cellNum = row * _cols + col; + } + return cellNum; + } + + /** + * @brief Get a cell from the grid using row/col + * + * @param row row of the cell + * @param col column of the cell + * @return cell* pointer to the cell + */ + Cell getCell(int row, int col) + { + return _grid[findCell(row, col)]; + } + + /** + * @brief Update a cell with a specified bitmap + * + * @param bmp bitmap to update the cell with + * @param row row of the cell + * @param col column of the cell + * @param span number of columns the bitmap spans + * @param centre whether the bitmap should be centered + */ + void updateCell(const bitmap &bmp, int row, int col, int span = 1, bool centre = true) + { + _gridEmpty=false; + // Stores the index of the cell + int cellNum = findCell(row, col); + // Selected row is out of bounds + // Update the cell + _grid[cellNum].cellType = BMP; + _grid[cellNum].spr = NULL; + _grid[cellNum].bmp = bmp; + _grid[cellNum].button = NULL; + _grid[cellNum].span = span; + _grid[cellNum].centre = centre; + } + + /** + * @brief Update a cell with a specified sprite + * + * @param sprite sprite to update the cell with + * @param row row of the cell + * @param col column of the cell + * @param span number of columns the sprite spans + * @param centre whether the sprite should be centered + */ + void updateCell(const sprite &sprite, int row, int col, int span = 1, bool centre = true) + { + _gridEmpty=false; + // Stores the index of the cell + int cellNum = findCell(row, col); + // Selected row is out of bounds + // Update the cell + _grid[cellNum].cellType = SPT; + _grid[cellNum].spr = sprite; + _grid[cellNum].bmp = NULL; + _grid[cellNum].button = NULL; + _grid[cellNum].span = span; + _grid[cellNum].centre = centre; + } + + /** + * @brief Update a cell with a specified button + * + * @param button button to update the cell with + * @param row row of the cell + * @param col column of the cell + * @param span number of columns the button spans + * @param centre whether the button should be centered + */ + void updateCell(Button *button, int row, int col, int span = 1, bool centre = true) + { + _gridEmpty=false; + // Stores the index of the cell + int cellNum = findCell(row, col); + // Selected row is out of bounds + // Update the cell + _grid[cellNum].cellType = BTN; + _grid[cellNum].spr = NULL; + _grid[cellNum].bmp = NULL; + _grid[cellNum].button = button; + _grid[cellNum].span = span; + _grid[cellNum].centre = centre; + } + + /** + * @brief Update all cells with a specified bitmap + * + * @param bmp bitmap to update the cells with + * @param centre whether the bitmaps should be centered + */ + void updateAllCells(bitmap bmp, bool centre = true) + { + _gridEmpty=false; + // Iterate over all the cells + for (size_t i = 0; i < _cells; i++) + { + // Update bitmap + _grid[i].cellType = BMP; + _grid[i].spr = NULL; + _grid[i].bmp = bmp; + _grid[i].centre = centre; + } + } + + /** + * @brief Update all cells with a specified sprite + * + * @param sprite sprite to update the cells with + * @param centre whether the sprites should be centered + */ + void updateAllCells(sprite sprite, bool centre = true) + { + _gridEmpty=false; + // Iterate over all the cells + for (size_t i = 0; i < _cells; i++) + { + // Update bitmap + _grid[i].cellType = SPT; + _grid[i].spr = sprite; + _grid[i].bmp = NULL; + _grid[i].centre = centre; + } + } + + /** + * @brief Log the dimensions of the cells to console + * + */ + void drawLayout() + { + int colWidth = 0; + write("Drawing layout\n"); + write("rowHeight: " + std::to_string(current_window_height() / _rows) + "\n"); + if (!_useColsArray) + colWidth = current_window_width() / _cols; + for (size_t i = 0; i < _rows; i++) + { + if (_useColsArray) + { + colWidth = current_window_width() / m_colsArray[i]; + _cols = m_colsArray[i]; + } + write("Row " + std::to_string(i) + " (colWidth: " + std::to_string(colWidth) + "): "); + for (size_t j = 0; j < _cols; j++) + { + string content = "[" + std::to_string(i) + "," + std::to_string(j) + "]"; + write(content); + } + write("\n"); + } + } + + /** + * @brief Clear the grid + * + */ + void clearGrid() + { + if (_gridEmpty) + return; + // Iterate over all the cells + for (size_t i = 0; i < _cells; i++) + { + // Reset cell to default + _grid[i].cellType = EMPTY; + _grid[i].spr = NULL; + _grid[i].bmp = NULL; + _grid[i].span = 1; + _grid[i].centre = true; + } + // Grid is now empty + _gridEmpty = true; + } + /** + * @brief Find the nearest cells row/col from x, y coordinates + * + * @param x x-coordinate (px) + * @param y y-coordinate (px) + * @return point_2d + */ + point_2d findCellFromLoc(int x, int y) + { + int rowNum; + // Selected row is out of bounds + int yOffset = current_window_height() / _rows; + int runningSum = 0; + for (int i = 0; i < _rows; i++) + { + if (y >= runningSum && y < runningSum + yOffset) + { + rowNum = i; + } + runningSum += yOffset; + } + int xOffset; + if (_useColsArray) + xOffset = (current_window_width() / m_colsArray[rowNum]); + else + xOffset = (current_window_width()/_cols); + runningSum = 0; + int colNum; + for (int i = 0; i < _cols; i++) + { + if (x >= runningSum && x < runningSum + xOffset) + { + colNum = i; + } + runningSum += xOffset; + } + point_2d point; + point.x = colNum; + point.y = rowNum; + return point; + } + /** + * @brief Clear the cell + * + */ + void clearCell(int row, int col) + { + // Gets the index of the cell + int cellNum = findCell(row, col); + // Clear cell + _grid[cellNum].cellType = EMPTY; + _grid[cellNum].spr = NULL; + _grid[cellNum].bmp = NULL; + _grid[cellNum].button = NULL; + _grid[cellNum].span = 1; + _grid[cellNum].centre = true; + } }; #endif \ No newline at end of file diff --git a/include/Helper.h b/include/Helper.h index 1f67fb9..29ff710 100644 --- a/include/Helper.h +++ b/include/Helper.h @@ -1,16 +1,9 @@ #ifndef ARCADE_MACHINE_HELPER_H #define ARCADE_MACHINE_HELPER_H -#if __cplusplus >= 201703L -#include -namespace fs = std::filesystem; -#else -#define _LIBCPP_NO_EXPERIMENTAL_DEPRECATION_WARNING_FILESYSTEM #include -namespace fs = std::experimental::filesystem; -#endif -#include "GridLayout.h" +namespace fs = std::experimental::filesystem; // Remove definition to use JSON config. #define ARCADE_MACHINE_USE_TEXT_CONFIG @@ -97,7 +90,7 @@ class Helper { * * @param grid */ - void resetScreen(GridLayout grid) + void resetScreen(Grid grid) { process_events(); clear_screen(COLOR_DARK_SLATE_GRAY); @@ -120,7 +113,7 @@ class Helper { int rows = 5; int cols = 5; //#rows, #cols, ScaletoFit - GridLayout grid(rows, cols, true); + Grid grid(rows, cols, true); //Grid grid(rows,colsArray, true); resetScreen(grid); diff --git a/include/Menu.h b/include/Menu.h index 6e0d7a5..13ca330 100644 --- a/include/Menu.h +++ b/include/Menu.h @@ -1,18 +1,10 @@ #ifndef ARCADE_MACHINE_MENU_H #define ARCADE_MACHINE_MENU_H -#include "ConfigData.h" #include "Tip.h" #include "Selector.h" -#include "GameData.h" -#include "Database.h" -#include "Rating.h" -#include "Table.h" -#include "GridLayout.h" #include "Button.h" #include "MenuButton.h" -#include "GameScreenButton.h" -#include "Configuration.h" #ifdef _WIN32 #include @@ -27,9 +19,6 @@ class Menu { std::string m_background = "games_dashboard"; // Vector to store the config data of each game std::vector m_games; - Database *m_db; - Rating m_rating; - GameData m_gameData; #ifdef _WIN32 // Contains info about newly created process and thread @@ -44,8 +33,6 @@ class Menu { LPCSTR m_gameDir; // m_handle for game window. HWND m_handle; -#else - int m_processId; #endif // Used to find x centre of screen @@ -65,7 +52,7 @@ class Menu { // Vector to store game images std::vector m_gameImages; // Menu grid - GridLayout m_grid; + Grid m_grid; Tip *m_tip; ButtonNode *m_button = nullptr; bool m_overlayActive = false; @@ -99,38 +86,473 @@ class Menu { bool m_menuSliding = false; public: - Menu(); - Menu(std::vector configs); - ~Menu(); + Menu(){} + + Menu(std::vector configs) + { + this->m_games = configs; + +#ifdef _WIN32 + m_handle = FindWindowA(NULL, "arcade-machine"); +#endif + } + ~Menu(){} // Getters auto getButtons() const -> const std::vector { return this->m_btns; } bool getOverlayState() { return m_overlayActive; } - std::vector getGameSprites(std::vector configs); // gets game images from the config files and returns vector + /** + * @brief Gets the game images from the config files and returns vector of game images. + * + * @param configs Vector of config data. + * @return vector of game images. + */ + std::vector getGameSprites(std::vector configs) + { + std::vector gameImages; + + for (int i = 0; i < configs.size(); i++) + { + // Get image dir and image name from games vector. + std::string image = configs[i].folder() + "/" + configs[i].image(); + gameImages.push_back(image); + } + + return gameImages; + } + - void createGrid(); // Create a GridLayout object - void createButtons(); // Create a list of games - void createTip(); // create a tip to display to the user. - void updateCarousel(); - void carouselHandler(); - void drawMenuPage(); - void updateSlide(sprite buttonSprite, int position); // Method to update the sprite positions and draw sprite. - void drawUpdateSlideLeft(); // Slide the game buttons on left key input. - void drawUpdateSlideRight(); // Slide the game buttons on right key input. - void drawOverlay(ConfigData config, GameData stats); // Draw an overlay over the game, using data from the config. + /** + * @brief Create a grid object + */ + void createGrid() + { + // Instantiate grid object + Grid grid(8, 14); + this->m_grid = grid; + // Update the background + this->m_grid.setBackground(bitmap_named(this->m_background)); + } + + /** + * @brief Create a list of games. + * + */ + void createButtons() + { + // Call function to get game images. + m_gameImages = getGameSprites(m_games); + + for (int i = 0; i < m_gameImages.size(); i++) + { + if (i == 0) + { + this->m_button = new ButtonNode(new GameScreenButton(Button::GAME, m_gameImages[0])); + this->m_button->config = m_games[0]; + } + else + { + std::string image = m_gameImages[i]; + this->m_button->addBefore(new ButtonNode(new GameScreenButton(Button::GAME, image))); + this->m_button->getPrev()->config = m_games[i]; + } + } + } + + /** + * @brief create a tip to display to the user. + */ + void createTip() + { + bitmap bmpTip = bitmap_named("information"); + bitmap_set_cell_details(bmpTip, 50, 50, 4, 3, 12); + animation_script infoScript = animation_script_named("info-script"); + animation anim = create_animation(infoScript, "rotate"); + drawing_options opt = option_with_animation(anim); + + std::string tipText[3] = { + "Use the left and right arrow keys to cycle through the carousel", + "Press escape to return to the main menu", + "Press enter to start the game" + }; + this->m_tip = new Tip(tipText[rand()%3], bmpTip, anim, opt, 3000, 25); + } + + /** + * @brief draw the game buttons to the window, using the carousel layout + */ + void updateCarousel() + { + // If menu is sliding then clear the grid. + if (this->m_menuSliding) + { + this->m_grid.clearGrid(); + } + else { + if (this->m_button && !this->m_inGame) + { + this->m_grid.updateCell(this->m_button->getPrev()->button, 2, 0, 1, false); + this->m_grid.updateCell(this->m_button->button, 2, 5, 1, false); + this->m_grid.updateCell(this->m_button->getNext()->button, 2, 10, 1, false); + } + } + } + + /** + * @brief m_handle carousel input + */ + void carouselHandler() + { + /// Check for input in selector class. + this->m_button = this->m_selectorGamesMenu.checkKeyInput(this->m_button, m_gameMenu); + this->m_action = this->m_selectorGamesMenu.checkForSelection(this->m_button, m_gameMenu); #ifdef _WIN32 - bool focusWindow(std::string windowName, int timeout = 2000); - void startGame(LPCSTR gamePath, LPSTR gameExe, LPCSTR gameDirectory); -#else - void startGame(struct s_ExecutablePath path); + checkGameExit(); #endif - void checkGameExit(); - void backToGamesMenu(); // Fade back to games menu - void fade(double alphaStart, double alphaEnd, double alphaStep); + if (this->m_button) + { + if (this->m_action == "escape" && m_overlayActive) + { + m_overlayActive = false; + } + else if (this->m_action == "return") + { + if (m_overlayActive) + { +#ifdef _WIN32 + // Get game path + m_gamePath = (this->m_button->config.folder() + "/" + this->m_button->config.exe()).c_str(); + // Get executable name + m_gameExe = strdup(this->m_button->config.exe().c_str()); + // Get game directory + m_gameDir = this->m_button->config.folder().c_str(); +#endif + + // Set the center of the game + this->m_x = m_centreX; + this->m_y = m_centreY; + + // fade to black + fade(0, 1, 0.1); + + // fill with black + fill_rectangle(rgba_color(0.0, 0.0, 0.0, 1.0), 0, 0, ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + // clear grid + this->m_grid.clearGrid(); + // set new background + this->m_grid.setBackground(bitmap_named("in_game_bgnd")); + //turn off overlay + this->m_overlayActive = false; + // turn off menu music + fade_music_out(1000); + // fade back in + fade(1, 0, 0.1); + +#ifdef _WIN32 + // Call method to open game executable + startGame(m_gamePath, m_gameExe, m_gameDir); +#endif + + return; + } + m_overlayActive = true; + } + } + } + + /** + * @brief draw the menu page + */ + void drawMenuPage() + { + // if the game has ended, go back to games menu + if(!this->m_inGame && this->m_gameStarted) + { + this->m_gameStarted = false; + backToGamesMenu(); + } + + this->m_grid.drawGrid(); + + // Wait for selector to key input to determine slide direction. + if (m_selectorGamesMenu.getSlideLeft()) + drawUpdateSlideLeft(); + else if (m_selectorGamesMenu.getSlideRight()) + drawUpdateSlideRight(); + + if (m_overlayActive && !m_menuSliding) + drawOverlay(m_button->config); + this->m_tip->draw(); + + updateCarousel(); + carouselHandler(); + } + + /** + * @brief Method to update the sprite positions and draw sprite. + * + * @param buttonSprite The buttons sprite. + * @param position The position to move the sprite. + * @return ** void + */ + void updateSlide(sprite buttonSprite, int position) + { + // Show the base layer of sprite. + sprite_show_layer(buttonSprite, 0); + // Set the x position of sprite. + sprite_set_x(buttonSprite, position); + // Set the y position of sprite. + sprite_set_y(buttonSprite, m_posY); + // draw sprite to screen. + draw_sprite(buttonSprite); + // Updatse sprite. + update_sprite(buttonSprite); + } + + /** + * @brief Slide the game buttons on left key input. + * + * @return ** void + */ + void drawUpdateSlideLeft() + { + this->m_menuSliding = true; + + // Get sprites of buttons on display. + this->m_newButton1 = this->m_button->getNext()->button->btn(); + this->m_newButton2 = this->m_button->button->btn(); + this->m_newButton3 = this->m_button->getPrev()->button->btn(); + + // Increment the x position of sprite. + this->m_pos1 += m_speed; + this->m_pos2 += m_speed; + this->m_pos3 += m_speed; + + // Update and draw sprite. + updateSlide(this->m_newButton1, this->m_pos1); + updateSlide(this->m_newButton2, this->m_pos2); + updateSlide(this->m_newButton3, this->m_pos3); + + // If sprite reaches position. + if (this->m_pos1 > 1300) + { + // Set selector bool back to false. + m_selectorGamesMenu.setSlideLeft(false); + this->m_menuSliding = false; + // Reset positions. + this->m_pos1 = m_posX; + this->m_pos2 = this->m_pos1 - m_posX; + this->m_pos3 = this->m_pos2 - m_posX; + } + } + + /** + * @brief Slide the game buttons on right key input. + * + * @return ** void + */ + void drawUpdateSlideRight() + { + this->m_menuSliding = true; + + // Get sprites of buttons on display. + this->m_newButton1 = this->m_button->getPrev()->button->btn(); + this->m_newButton2 = this->m_button->button->btn(); + this->m_newButton3 = this->m_button->getNext()->button->btn(); + + // Decrease the x position of sprite. + this->m_pos1 -= m_speed; + this->m_pos4 -= m_speed; + this->m_pos5 -= m_speed; + + // Update and draw sprite. + updateSlide(this->m_newButton1, this->m_pos1); + updateSlide(this->m_newButton2, this->m_pos4); + updateSlide(this->m_newButton3, this->m_pos5); + + if (this->m_pos1 <= 20) + { + // Set selector bool back to false. + m_selectorGamesMenu.setSlideRight(false); + this->m_menuSliding = false; + // Reset positions. + this->m_pos1 = m_posX; + this->m_pos4 = m_posX * 2; + this->m_pos5 = m_posX * 3; + } + } + /** + * @brief Draw an overlay over the game, using data from the config. + * + * @param config the game config. + */ + void drawOverlay(ConfigData config) + { + int xOffset = (current_window_width() / 2) + (current_window_width() / 14); + int yStart = current_window_height() / 6; + int yOffset = current_window_height() / 40; + + fill_rectangle(rgba_color(0.0, 0.0, 0.0, 0.8), (current_window_width() / 2), 0, (current_window_width() / 2), current_window_height()); + draw_text(config.title(), COLOR_WHITE, "font_title", yOffset * 3, xOffset, yStart); + yStart += yOffset * 3; + draw_text("Author: " + config.author(), COLOR_WHITE, "font_text", yOffset, xOffset, yStart + (1 * yOffset)); + draw_text("Genre: " + config.genre(), COLOR_WHITE, "font_text", yOffset, xOffset, yStart + (2 * yOffset)); + draw_text("Language: " + config.language(), COLOR_WHITE, "font_text", yOffset, xOffset, yStart + (3 * yOffset)); + draw_text("Rating: " + config.rating(), COLOR_WHITE, "font_text", yOffset, xOffset, yStart + (4 * yOffset)); + draw_text("Repository: " + config.repo(), COLOR_WHITE, "font_text", yOffset, xOffset, yStart + (5 * yOffset)); + } + +#ifdef _WIN32 + /** + * @brief Find the game window and bring it to focus, if it exists + * + * @param windowName the name of the window + * @param timeout time in ms to search for the window + * @return true/false if window was found. + */ + bool focusWindow(std::string windowName, int timeout = 2000) + { + LPCSTR gameWindow = windowName.c_str(); + HWND gameWindowHandle = NULL; + + int timeElapsed; + auto startTime = std::chrono::steady_clock::now(); + + //Find the window m_handle + do { + gameWindowHandle = FindWindowEx(NULL,NULL,NULL, gameWindow); + timeElapsed = std::chrono::duration_cast(std::chrono::steady_clock::now() - startTime).count(); + delay(250); + } + while (gameWindowHandle == NULL && timeElapsed <= timeout); + + //Maximise the Window + if (gameWindowHandle != NULL) + { + ShowWindow(gameWindowHandle, SW_SHOWMAXIMIZED); + return true; + } + else + { + write_line("Unable to find gameWindow m_handle"); + return false; + } + return true; + } + + /** + * @brief Starts up the selected game by starting a new process. + * + * @param gamePath The filepath of the game to open. + * @param gameExe The executable of the game. + * @param gameDirectory // The directory of the game. + * @return ** void + */ + void startGame(LPCSTR gamePath,LPSTR gameExe, LPCSTR gameDirectory) + { + if (!this->m_inGame) + { + // Additional info + STARTUPINFOA startupInfo; + + // Set the size of the structures + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + ZeroMemory(&m_processInfo, sizeof(m_processInfo)); + + // Start the program up + WINBOOL gameProcess = CreateProcessA + ( + gamePath, // the path + gameExe, // Command line + NULL, // Process m_handle not inheritable + NULL, // Thread m_handle not inheritable + FALSE, // Set m_handle inheritance to FALSE + NORMAL_PRIORITY_CLASS, // Don't open file in a separate console + NULL, // Use parent's environment block + gameDirectory, // Use parent's starting directory + &startupInfo, // Pointer to STARTUPINFO structure + &m_processInfo // Pointer to PROCESS_INFORMATION structure + ); + + OpenProcess(PROCESS_QUERY_INFORMATION,TRUE, gameProcess); + + std::string windowName = gameExe; + //Remove the extension from the application name (.exe) + windowName = windowName.substr(0, windowName.find(".")); + //Focus the window + focusWindow(windowName); + + this->m_inGame = true; + } + } + + /** + * @brief Waits for game to exit. + * + * @return ** void + */ + void checkGameExit() + { + if (this->m_inGame == true) + { + this->m_gameStarted = true; + // Check if game has been exited. + this->m_programExit + = GetExitCodeProcess(m_processInfo.hProcess, &m_exitCode); + if ((this->m_programExit + ) && (STILL_ACTIVE != m_exitCode)) + { + this->m_inGame = false; + } + } + } +#endif + + /** + * @brief Fade back to games menu + */ + void backToGamesMenu() + { + // fade to black + fade(0, 1, 0.1); + fill_rectangle(rgba_color(0.0, 0.0, 0.0, 1.0), 0, 0, ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + this->m_grid.setBackground(bitmap_named("games_dashboard")); + // fade to normal + fade(1, 0, 0.1); + } + + /** + * @brief Creates a fading effect + * + * @param alphaStart The starting alpha value. + * @param alphaEnd The ending alpha value. + * @param alphaStep The alpha value to increment/decrement by. + */ + void fade(double alphaStart, double alphaEnd, double alphaStep) + { + if (alphaStart > alphaEnd) + alphaStep = -abs(alphaStep); + // Calculate the number of steps required to complete the fade. + double difference = abs(alphaEnd - alphaStart); + int steps = difference / abs(alphaStep); + + for (int i = 0; i < steps; i++) + { + clear_screen(); + this->m_grid.drawGrid(); + // Alpha value manipulates to the opacity of the rectangle. + fill_rectangle(rgba_color(0.0, 0.0, 0.0, alphaStart), 0, 0, ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + // Update the alpha value. + alphaStart += alphaStep; + refresh_screen(60); + delay(50); + } + } }; #endif \ No newline at end of file diff --git a/include/MenuButton.h b/include/MenuButton.h index 4e65e03..e9130ce 100644 --- a/include/MenuButton.h +++ b/include/MenuButton.h @@ -2,24 +2,61 @@ #define ARCADE_MACHINE_MENU_BUTTON_H #include +#include "splashkit.h" #include "Button.h" /** -* @brief Buttons created for the main opening Menu Screen -* -* Derived from abstract Button class -*/ -class MenuButton : public Button -{ -public: - MenuButton(Color c, float scale = 1); + * @brief Buttons created for the main opening Menu Screen + * + * Derived from abstract Button class + */ +class MenuButton : public Button { + public: + // First constructor + MenuButton(Color c, float scale = 1) : Button(c, scale){} - void getButtonImage(std::string image) {} //? + /** + * @brief The action of this button + * Called when the selector receives input for this button + * + * @param keyinput + * @return string + */ + std::string action(std::string keyinput = "") + { + if (this->color() == btn_color(Button::PLAY)) + { + // go to this screen + write_line("Play"); + return "play"; + } + if (this->color() == btn_color(Button::EXIT)) + { + // go to this screen + write_line("Exit"); + return "exit"; + } + if (this->color() == btn_color(Button::OPTS)) + { + // go to this screen + write_line("Options"); + return "options"; + } + return keyinput; + } - void btnImage(std::string image) override {} - void drawButton() override; - std::string action(std::string keyinput = "") override; + /** + * @brief Draws button to screen + * + * @return * void + */ + void drawButton() + { + draw_sprite(this->m_btn); + } + void getButtonImage(std::string image) {}; + void btnImage(std::string image) { } }; #endif \ No newline at end of file diff --git a/include/Option.h b/include/Option.h index dcd4824..be378d1 100644 --- a/include/Option.h +++ b/include/Option.h @@ -1,57 +1,224 @@ #ifndef ARCADE_MACHINE_OPTION_H #define ARCADE_MACHINE_OPTION_H -#include "AboutScreen.h" #include "splashkit.h" -#include -#include "GridLayout.h" -#include "Button.h" -#include "ArcadeMachine.h" -#include "Selector.h" -#include "OptionsScreenButton.h" -#include "Audio.h" // Options class -class Option -{ -private: - int m_displayStyle = 1; - int _selector = 1; - bool m_isSelected = false; - int m_currentMusic = 1; - int m_insideSeletor = 1; - float m_volume = 40; - bool m_isOptionOpen = true; - - GridLayout m_grid; - Audio m_audio; - std::vector m_optionsBtns; - ButtonNode *m_optionsButtonNode = nullptr; - Selector m_selectorOptionsMenu; - point_2d m_mouse; - std::string m_action; - AboutScreen m_aboutScreen; - -public: - Option(); - - void createOptionsButtons(); - void drawOptionsMenu(); - bool checkAction(); - void soundMenu(); - - - float getVolume(); - int getCurrentMusic(); - void setCurrentMusic(); - void volumeControl(); - void changeDisplay(); - void playAboutScreen(); - bool isChangeMusic(); - bool isChangeVoLume(); - void changeSelector(); - void updateOption(); - void drawIntinialHub(); + +using namespace std; +using std::vector; + +class Option{ + private: + int m_displayStyle=1; + int _selector=1; + bool m_isSelected=false; + int m_currentMusic=1; + int m_insideSeletor=1; + float m_volume=40; + bool m_isOptionOpen=true; + + public: + Option(){} + + float getVolume() + { + return m_volume / 100; + } + + void volumeControl() + { + if(_selector == 2 && m_isSelected && key_typed(RIGHT_KEY) && m_volume < 100 && m_insideSeletor == 1) + m_volume += 20; + + if(_selector ==2 && m_isSelected && key_typed(LEFT_KEY) && m_volume > 0 && m_insideSeletor == 1) + m_volume -= 20; + } + + void setCurrentMusic() + { + if(_selector == 2 && m_isSelected && m_volume < 100 && m_insideSeletor == 2) + { + if(key_typed(LEFT_KEY) && m_currentMusic > 1) + m_currentMusic = m_currentMusic-1; + if(key_typed(RIGHT_KEY) && m_currentMusic < 3) + m_currentMusic = m_currentMusic+1; + } + } + + int getCurrentMusic() + { + return m_currentMusic; + } + + void changeDisplay() + { + if(_selector == 3 && m_isSelected) + { + switch (m_displayStyle) { + case 1: + if (key_typed(RIGHT_KEY)) + m_displayStyle = 2; + else if (key_typed(DOWN_KEY)) + m_displayStyle = 3; + break; + case 2: + if (key_typed(DOWN_KEY)) + m_displayStyle = 4; + else if(key_typed(LEFT_KEY)) + m_displayStyle = 1; + break; + case 3: + if (key_typed(UP_KEY)) + m_displayStyle = 1; + else if (key_typed(RIGHT_KEY)) + m_displayStyle = 4; + break; + } + } + } + bool isChangeMusic() + { + if(_selector==2&&m_isSelected&&m_volume<100&&m_insideSeletor==2&&(key_typed(LEFT_KEY)||key_typed(RIGHT_KEY))){return true;} + else return false; + } + bool isChangeVoLume() + { + if(_selector==2&&m_isSelected&&m_volume<100&&m_insideSeletor==1&&(key_typed(LEFT_KEY)||key_typed(RIGHT_KEY))){return true;} + else return false; + } + + void changeSelector() + { + if(key_typed(P_KEY)) + { + if(m_isSelected==false) + { + m_isSelected=true; + } + else if(m_isSelected==true) + { + m_isSelected=false; + } + + } + + if(key_typed(DOWN_KEY)&&_selector<4 && !m_isSelected) + { + _selector=_selector+1; + } + + if(key_typed(UP_KEY)&&_selector>1 && !m_isSelected) + { + _selector=_selector-1; + } + + if(_selector==2&&m_isSelected==true&&m_insideSeletor==1) + { + if(key_typed(DOWN_KEY)) + { + m_insideSeletor=2; + } + } + + if(_selector==2&&m_isSelected==true&&m_insideSeletor==2) + { + if(key_typed(UP_KEY)) + { + m_insideSeletor=1; + } + } + } + + void updateOption() + { + if(m_isOptionOpen) + { + changeSelector(); + changeDisplay(); + setCurrentMusic(); + volumeControl(); + drawIntinialHub(); + } + + // if(!_isOptionOpen&&key_typed(O_KEY)){_isOptionOpen=true;} + // if(_isOptionOpen&&key_typed(O_KEY)){_isOptionOpen=false;} + + if(key_typed(O_KEY)) + { + m_isOptionOpen=!m_isOptionOpen; + } + } + + void drawIntinialHub() + { + draw_bitmap("back_ground", bitmap_width("backCurrentGame"), 0); + + if(_selector==1) + { + draw_bitmap("backCurrentGame", 0, 0); + draw_bitmap("sound_notSelected", 0, bitmap_height("backCurrentGame")); + // draw_bitmap(); + draw_bitmap("backMenu_notSelected", 0, 3*bitmap_height("backCurrentGame")); + draw_text("RESET DEFAULT", color_red(), 400, 100); + draw_text("yes", color_red(), 300, 300); + draw_text("no", color_red(), 700, 300); + } + + if(_selector==2) + { + draw_bitmap("changeSound",0,bitmap_height("backCurrentGame")); + draw_bitmap("backGame_notSelected",0,0); + // draw_bitmap(); + draw_bitmap("backMenu_notSelected",0,3*bitmap_height("backCurrentGame")); + + fill_rectangle(color_red(),bitmap_width("backCurrentGame")+200,200,screen_width()-bitmap_width("backCurrentGame")-400,50); + double a=screen_width()-bitmap_width("backCurrentGame")-400; + double b=a/100*m_volume; + fill_rectangle(color_yellow(),bitmap_width("backCurrentGame")+200,200,b,50); + draw_text(std::to_string(screen_width()-bitmap_width("backCurrentGame")),color_red(),300,300); + + if (m_insideSeletor==1) + { + draw_text("CHANGE SOUND",color_white(),bitmap_width("backCurrentGame")+100,200); + } + + if(m_insideSeletor==2) + { + draw_text("CHANGE MUSIC",color_white(),bitmap_width("backCurrentGame")+100,500); + draw_text(std::to_string(m_currentMusic),color_white(),bitmap_width("backCurrentGame")+250,500); + } + } + + if(_selector==3) + { + double x=screen_width(); + double y=screen_height(); + double a=bitmap_width("backCurrentGame"); + double rec_width=(x-a-450)/2; + double rec_height=(y-450)/2; + double first_column_x=150+a; + double secong_column_x=a+300+rec_width; + double first_row_y=150; + double second_row_y=300+rec_height; + fill_rectangle(color_white(),first_column_x,first_row_y,rec_width,rec_height); + fill_rectangle(color_white(),secong_column_x,first_row_y,rec_width,rec_height); + fill_rectangle(color_white(),first_column_x,second_row_y,rec_width,rec_height); + fill_rectangle(color_white(),secong_column_x,second_row_y,rec_width,rec_height); + if(m_displayStyle==1){fill_rectangle(color_red(),first_column_x,first_row_y,rec_width,rec_height);} + if(m_displayStyle==2){fill_rectangle(color_red(),secong_column_x,first_row_y,rec_width,rec_height);} + if(m_displayStyle==3){fill_rectangle(color_red(),first_column_x,second_row_y,rec_width,rec_height);} + if(m_displayStyle==4){fill_rectangle(color_red(),secong_column_x,second_row_y,rec_width,rec_height);} + } + + if(_selector==4) + { + draw_bitmap("backMenu",0,3*bitmap_height("backCurrentGame")); + draw_bitmap("sound_notSelected",0,bitmap_height("backCurrentGame")); + // draw_bitmap(); + draw_bitmap("backGame_notSelected",0,0); + } + } }; #endif \ No newline at end of file diff --git a/include/Selector.h b/include/Selector.h index 4ac8799..d048893 100644 --- a/include/Selector.h +++ b/include/Selector.h @@ -6,39 +6,186 @@ #include class Selector { -private: - /// Checks first button. - bool m_isFirstButton = true; - /// Splashscreen cursor sprite. - sprite m_cursorSprite; - /// Checks if game menu currently sliding left. - bool m_isSlidingLeft = false; - /// Checks if game menu currently sliding right. - bool m_isSlidingRight = false; - /// Checks if current button is from game menu. - bool m_isFromGameMenu; - /// Check to render cursor on Selector - bool m_renderCursor = false; - -public: - Selector() {} - Selector(const std::string &cursor); - - // Properties used to detect if game menu slide is occurring. - auto getSlideLeft() const -> const bool& { return this->m_isSlidingLeft; } - auto getSlideRight() const -> const bool& { return this->m_isSlidingRight; } - auto setSlideLeft(bool left) { this->m_isSlidingLeft = left; } - auto setSlideRight(bool right) { this->m_isSlidingRight = right; } - auto setRenderCursor(bool cursor) { this->m_renderCursor = cursor; } - // Return the cursor sprite - sprite getCursor() { return this->m_cursorSprite; } - - ButtonNode* checkKeyInput(ButtonNode* buttonNode, bool isFromGameMenu = false); - std::string checkForSelection(ButtonNode* buttonNode, bool isFromGameMenu = false); - void highlightFirst(ButtonNode* buttonNode); - void highlightButton(ButtonNode* buttonNode, std::string direction); - void setNoRenderCursor(); - + private: + /// Checks first button. + bool m_isFirstButton = true; + /// Splashscreen cursor sprite. + sprite m_cursorSprite; + /// Checks if game menu currently sliding left. + bool m_isSlidingLeft = false; + /// Checks if game menu currently sliding right. + bool m_isSlidingRight = false; + /// Checks if current button is from game menu. + bool m_isFromGameMenu; + + public: + Selector(){} + Selector(const string &cursor) + { + bitmap cur = bitmap_named("cursor"); + this->m_cursorSprite = create_sprite(cur); + } + + // Properties used to detect if game menu slide is occurring. + auto getSlideLeft() const -> const bool& { return this->m_isSlidingLeft; } + auto getSlideRight() const -> const bool& { return this->m_isSlidingRight; } + auto setSlideLeft(bool left) { m_isSlidingLeft = left; } + auto setSlideRight(bool right) { m_isSlidingRight = right; } + + // Return the cursor sprite + sprite getCursor() + { + return this->m_cursorSprite; + } + + /** + * @brief Checks key input, determining whether from game menu or splashscreen and updates that button. + * + * @param buttonNode The current button that is selected. + * @param gameMenu Checking if from game menu. + * @return ButtonNode* + */ + ButtonNode* checkKeyInput(ButtonNode* buttonNode, bool isFromGameMenu = false) + { + this->m_isFromGameMenu = isFromGameMenu; + + // Highlight play button on start. + if (m_isFirstButton) + highlightFirst(buttonNode); + + // If it is the game menu only allow left/right arrows selection. + if (isFromGameMenu) + { + // Check to ensure menu isn't currently sliding. + if (! m_isSlidingLeft && ! m_isSlidingRight) + { + // Slide left. + if (key_typed(LEFT_KEY) && !key_typed(RIGHT_KEY)) + { + m_isSlidingLeft = true; + // Previous button becomes current button. + buttonNode = buttonNode->getPrev(); + // Highlight the center/current button. + highlightButton(buttonNode, "prev"); + } + // Slide right. + if (key_typed(RIGHT_KEY) && !key_typed(LEFT_KEY)) + { + m_isSlidingRight = true; + // Next button becomes current button. + buttonNode = buttonNode->getNext(); + // Highlight the center/current button. + highlightButton(buttonNode, "next"); + } + } + } + else + { + // Move the selector up. + if (key_typed(UP_KEY)) + { + // Previous button becomes current button. + buttonNode = buttonNode->getPrev(); + // Highlight the current button. + highlightButton(buttonNode, "prev"); + // move cursor + sprite_set_y(this->m_cursorSprite, sprite_y(buttonNode->button->btn())); + } + // Move the selector down. + if (key_typed(DOWN_KEY)) + { + // next button becomes current button. + buttonNode = buttonNode->getNext(); + // Highlight the current button. + highlightButton(buttonNode, "next"); + // Move cursor. + sprite_set_y(this->m_cursorSprite, sprite_y(buttonNode->button->btn())); + } + } + + return buttonNode; + } + + /** + * @brief Checks for selection of a button. + * + * @param buttonNode The current button that is selected. + * @param gameMenu Check if selection is coming from game menu. + * @return ** string + */ + string checkForSelection(ButtonNode* buttonNode, bool isFromGameMenu = false) + { + // If selection not from game menu. + if (! isFromGameMenu) + { + // Return key returns the action of the selected button. + if (key_typed(RETURN_KEY)) + return buttonNode->button->action(); + } + else + { + // Return key returns the action of the selected button. + if (key_typed(RETURN_KEY)) + return buttonNode->button->action("return"); + + // Escape key returns the action of the selected button. + if (key_typed(ESCAPE_KEY)) + return buttonNode->button->action("escape"); + } + + return ""; + } + + /** + * @brief Highlights the first button upon page load. + * + * @param buttonNode The current selected button. + * @return void + */ + void highlightFirst(ButtonNode* buttonNode) + { + // Get the current buttons sprite. + sprite currentSprite = buttonNode->button->m_btn; + // Toggle on the highlight layer. + sprite_toggle_layer_visible(currentSprite, 1); + + // Set start location of cursor. + if (! m_isFromGameMenu) + { + sprite_set_x(this->m_cursorSprite, sprite_x(buttonNode->button->btn()) - 200); + sprite_set_y(this->m_cursorSprite, sprite_y(buttonNode->button->btn())); + } + + m_isFirstButton = false; + } + + /** + * @brief Highlights the current selected button. + * + * @param buttonNode The current button. + * @param direction The direction the selector is moving. + * @return ** void + */ + void highlightButton(ButtonNode* buttonNode, string direction) + { + // Sprite to store the previous sprite. + sprite prevSprite = nullptr; + + // Get current sprite. + sprite currentSprite = buttonNode->button->m_btn; + + // Toggle current sprites highlight layer. + sprite_toggle_layer_visible(currentSprite, 1); + + // Get previous sprite by checking direction of movement. + if (direction == "prev") + prevSprite = buttonNode->getNext()->button->btn(); + else + prevSprite = buttonNode->getPrev()->button->btn(); + + // Toggle previous sprite highlight layer off. + sprite_toggle_layer_visible(prevSprite, 1); + } }; #endif \ No newline at end of file diff --git a/include/Splashscreen.h b/include/Splashscreen.h index 972362d..8fdf476 100644 --- a/include/Splashscreen.h +++ b/include/Splashscreen.h @@ -16,8 +16,9 @@ class Splashscreen // Overloaded constructor Splashscreen(std::string bitmap) { this->m_bmp = bitmap; } - void drawTitlePage(); - + void drawTitlePage() { + draw_bitmap(this->m_bmp, 0, 0); + } }; #endif \ No newline at end of file diff --git a/include/Tip.h b/include/Tip.h index 19ce759..f7e33ef 100644 --- a/include/Tip.h +++ b/include/Tip.h @@ -3,9 +3,6 @@ #include "splashkit.h" -#include -#include - // Font size #define FONT_SIZE 20 // Width of the border @@ -60,17 +57,147 @@ class Tip int m_i = 30; - void calculatePosition(); - + /** + * @brief calculate the positioning of the container + * + */ + void calculatePosition() + { + switch (m_loc) + { + case TOPLEFT: + m_xOffset = WBORDER_OFFSET; + m_yOffset = WBORDER_OFFSET; + break; + case TOPRIGHT: + m_xOffset = screen_width() - m_containerWidth - WBORDER_OFFSET; + m_yOffset = WBORDER_OFFSET; + break; + case TOPCENTER: + m_xOffset = screen_width() / 2 - m_containerWidth / 2; + m_yOffset = WBORDER_OFFSET; + break; + case BOTRIGHT: + m_xOffset = screen_width() - m_containerWidth - WBORDER_OFFSET; + m_yOffset = screen_height() - m_containerHeight - WBORDER_OFFSET; + break; + case BOTLEFT: + m_xOffset = WBORDER_OFFSET; + m_yOffset = screen_height() - m_containerHeight - WBORDER_OFFSET; + break; + case BOTCENTER: + m_xOffset = screen_width() / 2 - m_containerWidth / 2; + m_yOffset = screen_height() - m_containerHeight - WBORDER_OFFSET; + default: + break; + } + } public: - Tip() {} - Tip(string text, bitmap image, int duration = 3000, int charsPerLine = 30, location loc = TOPCENTER); - Tip(string text, bitmap image, animation anim, drawing_options opt, int duration = 3000, int charsPerLine = 30, location loc = TOPCENTER); + Tip(){}; + /** + * @brief Construct a new Tip object + * + * @param text the text to be displayed + * @param image the image to be displayed + * @param duration the duration of the tip + * @param charsPerLine the number of characters per line + * @param loc the location of the container + */ + Tip(string text, bitmap image, int duration = 3000, int charsPerLine = 30, location loc = TOPCENTER) + { + this->m_text = text; + this->m_textLength = text.length(); + this->m_charsPerLine = charsPerLine; + this->m_image = image; + this->m_loc = loc; + this->m_duration = duration; + + //Initialise bitmap + m_bmpWidth = bitmap_width(image); + m_bmpHeight = bitmap_height(image); + //Calculate number of lines + m_numLines = m_textLength / charsPerLine; + //Calculate container height based off lines of text + m_containerHeight = m_numLines * FONT_SIZE + FONT_SIZE + 2 * CONTENT_BUFFER; + //If the bitmap is bigger than the container, resize + if (m_containerHeight < m_bmpHeight) + m_containerHeight = 2*CONTENT_BUFFER + m_bmpHeight; + //Calculate container width based on number of characters per line and bitmap width + m_containerWidth = charsPerLine * 9 + 3 * CONTENT_BUFFER + m_bmpWidth; + + calculatePosition(); + }; + /** + * @brief Construct a new Tip object + * + * @param text the text to be displayed + * @param image the image to be displayed + * @param anim the animation to be displayed + * @param opt the drawing options for the bitmap + * @param duration the duration of the tip + * @param charsPerLine the number of characters per line + * @param loc the location of the container + */ + Tip(string text, bitmap image, animation anim, drawing_options opt, int duration = 3000, int charsPerLine = 30, location loc = TOPCENTER) + { + this->m_text = text; + this->m_textLength = text.length(); + this->m_charsPerLine = charsPerLine; + this->m_image = image; + this->m_anim = anim; + this->m_opt = opt; + this->m_loc = loc; + this-> m_duration = duration; + //Initialise bitmap + m_bmpWidth = bitmap_cell_width(image); + m_bmpHeight = bitmap_cell_height(image); + //Calculate number of lines + m_numLines = m_textLength / charsPerLine; + //Calculate container height based off lines of text + m_containerHeight = m_numLines * FONT_SIZE + FONT_SIZE + 2 * CONTENT_BUFFER; + //If the bitmap is bigger than the container, resize + if (m_containerHeight < m_bmpHeight) + m_containerHeight = 2*CONTENT_BUFFER + m_bmpHeight; + //Calculate container width based on number of characters per line and bitmap width + m_containerWidth = charsPerLine * 9 + 3 * CONTENT_BUFFER + m_bmpWidth; + + calculatePosition(); + }; ~Tip(); + + /** + * @brief draw the tip + * + */ + void draw() + { + //Initialise startTime upon first draw + if (m_startTime.time_since_epoch().count() == 0) + m_startTime = std::chrono::steady_clock::now(); + + //The tip has been visible for more than the specified duration, stop drawing + if (std::chrono::duration_cast(std::chrono::steady_clock::now() - m_startTime).count() > 3000) + return; + + //Draw border rectangle + //NOTE: Variable i is used to scale the rectangle, each function call, animating the border. + fill_rectangle(rgba_color(0.0, 67.5, 75.7, 0.30), m_xOffset - BORDER_WIDTH, m_yOffset - BORDER_WIDTH, (m_containerWidth + (BORDER_WIDTH * 2)) / (m_i / 2), m_containerHeight + (BORDER_WIDTH * 2)); + if (m_i != 2) + m_i--; - void draw(); + //Draw container rectangle + fill_rectangle(COLOR_BLACK, m_xOffset, m_yOffset, m_containerWidth, m_containerHeight); + //Draw icon + draw_bitmap(m_image, m_xOffset + CONTENT_BUFFER, m_yOffset - m_bmpWidth / 2 + m_containerHeight / 2, m_opt); + //Draw the text + for (int i = 0; i < m_numLines + 1; i++) + draw_text(m_text.substr(i * m_charsPerLine, m_charsPerLine), COLOR_WHITE, "font_text", FONT_SIZE, m_xOffset + CONTENT_BUFFER * 2 + (m_bmpWidth), (FONT_SIZE * i) + m_yOffset + CONTENT_BUFFER); + //Update the animation + if (m_anim) + update_animation(m_anim); + } }; #endif \ No newline at end of file diff --git a/resources/bundles/resources.txt b/resources/bundles/resources.txt index a632c5d..06c5c16 100644 --- a/resources/bundles/resources.txt +++ b/resources/bundles/resources.txt @@ -5,36 +5,20 @@ BITMAP,intro_thoth_tech,backgrounds/intro_thoth.bmp BITMAP,intro_arcade_team,backgrounds/intro_arcade_team.png BITMAP,games_dashboard,backgrounds/retro.bmp BITMAP,back_ground,back_ground.jpg -BITMAP,in_game_bgnd,backgrounds/arcade-screen-background.png -BITMAP,rating_bg,backgrounds/rating_background.png -BITMAP,options_thoth,backgrounds/options_thoth.bmp +BITMAP,in_game_bgnd,backgrounds/arcade-screen-frame.png -// * BUTTONS */ -// Main Menu Buttons +// Buttons BITMAP,btn_play,buttons/btn_play.png BITMAP,btn_exit,buttons/btn_exit.png BITMAP,btn_opts,buttons/btn_options.png BITMAP,play_hghlt,buttons/btn_play_hghlt.png BITMAP,exit_hghlt,buttons/btn_exit_hghlt.png BITMAP,options_hghlt,buttons/btn_options_hghlt.png - -// Game Screen Buttons BITMAP,game_hghlt,buttons/btn_game_hghlt.png - -// Options Screen Buttons -BITMAP,opts_home,buttons/Gray/Home.png -BITMAP,opts_sound,buttons/Gray/MusicOn.png -BITMAP,opts_display,buttons/Gray/Settings.png -BITMAP,opts_stats,buttons/Gray/Ranking.png -BITMAP,opts_home_hghlt,buttons/Red/Home.png -BITMAP,opts_sound_hghlt,buttons/Red/MusicOn.png -BITMAP,opts_display_hghlt,buttons/Red/Settings.png -BITMAP,opts_stats_hghlt,buttons/Red/Ranking.png - BITMAP,backCurrentGame,buttons/Red/Settings.png BITMAP,backGame_notSelected,buttons/Yellow/Settings.png -BITMAP,changeSound,buttons/Red/MusicOn.png -BITMAP,sound_notSelected,buttons/Yellow/MusicOn.png +BITMAP,changeSound,buttons/Red/Music On.png +BITMAP,sound_notSelected,buttons/Yellow/Music On.png BITMAP,backMenu_notSelected,buttons/Yellow/Home.png BITMAP,backMenu,buttons/Red/Home.png @@ -44,16 +28,10 @@ BITMAP,cursor,splashkit_cursor.png // Tip Icons BITMAP,information,information.png -//Rating symbol -BITMAP,star-gold,star-gold.png -BITMAP,star-black,star-black.png - // Fonts FONT,font_btn,MindPlay.ttf FONT,font_title,Retroking.ttf FONT,font_text,Roboto-Regular.ttf -FONT,font_star,Stars.ttf -FONT,font_about,PressStart2P.ttf // Sounds SOUND,intro_thoth,intro1.wav @@ -65,7 +43,6 @@ MUSIC,music_mainmenu,main_menu.mp3 MUSIC, 1, 1.mp3 MUSIC, 2, 2.mp3 MUSIC, 3, 3.mp3 -MUSIC, music_about, insert-no-coins.ogg // Animations ANIM,info-script,information.txt diff --git a/resources/images/arcade machine screen2.png b/resources/images/arcade machine screen2.png new file mode 100644 index 0000000..3badba1 Binary files /dev/null and b/resources/images/arcade machine screen2.png differ diff --git a/resources/images/buttons/Gray/Music Off.png b/resources/images/buttons/Gray/Music Off.png new file mode 100644 index 0000000..1840e8d Binary files /dev/null and b/resources/images/buttons/Gray/Music Off.png differ diff --git a/resources/images/buttons/Gray/Music On.png b/resources/images/buttons/Gray/Music On.png new file mode 100644 index 0000000..0fe9509 Binary files /dev/null and b/resources/images/buttons/Gray/Music On.png differ diff --git a/resources/images/buttons/Red/Music On.png b/resources/images/buttons/Red/Music On.png new file mode 100644 index 0000000..27680af Binary files /dev/null and b/resources/images/buttons/Red/Music On.png differ diff --git a/resources/images/buttons/Yellow/Music On.png b/resources/images/buttons/Yellow/Music On.png new file mode 100644 index 0000000..987924d Binary files /dev/null and b/resources/images/buttons/Yellow/Music On.png differ diff --git a/src/program.cpp b/src/program.cpp index 8dac613..a0db783 100644 --- a/src/program.cpp +++ b/src/program.cpp @@ -1,5 +1,8 @@ #include "Configuration.h" #include "ArcadeMachine.h" +#include "wtypes.h" +#include +using namespace std; #define PLAY_INTRO true #define LOAD_GAMES true @@ -12,8 +15,14 @@ int main(void) // Instantiate Arcade Machine ArcadeMachine Arcade; + // Full screen + RECT desktop; + const HWND hDesktop = GetDesktopWindow(); + GetWindowRect(hDesktop, &desktop); + int horizontal = desktop.right; + int vertical = desktop.bottom; // Open window and toggle border off. - open_window("arcade-machine", ARCADE_MACHINE_RES_X, ARCADE_MACHINE_RES_Y); + open_window("arcade-machine", horizontal, vertical); window_toggle_border("arcade-machine"); #if PLAY_INTRO == true