[Add] Not your boring main menu.
This commit is contained in:
		
							parent
							
								
									b1bdb86b76
								
							
						
					
					
						commit
						e72cc987ff
					
				
							
								
								
									
										34
									
								
								assets/menu_background_snippets.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								assets/menu_background_snippets.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					map -sV 10.0.2.15
 | 
				
			||||||
 | 
					ls -la /etc/
 | 
				
			||||||
 | 
					ps aux | grep 'sshd'
 | 
				
			||||||
 | 
					gcc exploit.c -o exploit
 | 
				
			||||||
 | 
					SELECT * FROM users;
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					while(true){};
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					sudo rm -rf /
 | 
				
			||||||
 | 
					netstat -anp
 | 
				
			||||||
 | 
					arp -a
 | 
				
			||||||
 | 
					ssh root@192.168.1.1
 | 
				
			||||||
 | 
					git commit -m 'Initial commit'
 | 
				
			||||||
 | 
					hydra -l user -P wordlist.txt ftp://...
 | 
				
			||||||
 | 
					Hello, world.
 | 
				
			||||||
 | 
					All your base are belong to us
 | 
				
			||||||
 | 
					// TODO: fix this later
 | 
				
			||||||
 | 
					42
 | 
				
			||||||
 | 
					1337
 | 
				
			||||||
 | 
					zerocool
 | 
				
			||||||
 | 
					acidburn
 | 
				
			||||||
 | 
					cereal_killer
 | 
				
			||||||
 | 
					ping -c 4 google.com
 | 
				
			||||||
 | 
					traceroute 8.8.8.8
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<a>
 | 
				
			||||||
 | 
					password123
 | 
				
			||||||
 | 
					GOD
 | 
				
			||||||
 | 
					Have you tried turning it off and on again?
 | 
				
			||||||
 | 
					Connection established.
 | 
				
			||||||
 | 
					SYN/ACK
 | 
				
			||||||
 | 
					Firewall breach detected.
 | 
				
			||||||
 | 
					Decrypting /etc/shadow...
 | 
				
			||||||
 | 
					root::0:0:root:/root:/bin/bash
 | 
				
			||||||
@ -1,13 +1,17 @@
 | 
				
			|||||||
#include <cstdio>
 | 
					#include <cstdio>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					#include <chrono>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../server/src/network_manager.h"
 | 
				
			||||||
#include "game_state.h"
 | 
					#include "game_state.h"
 | 
				
			||||||
#include "gfx/shape_renderer.h"
 | 
					#include "gfx/shape_renderer.h"
 | 
				
			||||||
#include "gfx/txt_renderer.h"
 | 
					#include "gfx/txt_renderer.h"
 | 
				
			||||||
#include "terminal.h"
 | 
					#include "terminal.h"
 | 
				
			||||||
#include "ui/desktop.h"
 | 
					#include "ui/desktop.h"
 | 
				
			||||||
#include "ui/ui_window.h"
 | 
					#include "ui/ui_window.h"
 | 
				
			||||||
 | 
					#include <ui/main_menu.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GameState::_init_desktop(void) {
 | 
					void GameState::_init_desktop(void) {
 | 
				
			||||||
  _desktop = std::make_unique<Desktop>();
 | 
					  _desktop = std::make_unique<Desktop>();
 | 
				
			||||||
@ -18,15 +22,41 @@ void GameState::_init_desktop(void) {
 | 
				
			|||||||
  _desktop->add_window(std::move(term_window));
 | 
					  _desktop->add_window(std::move(term_window));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GameState::_run_server(void) {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    NetworkManager server;
 | 
				
			||||||
 | 
					    server.start(1337);
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Server's start() method is non-blocking, but NetworkManager
 | 
				
			||||||
 | 
					     * object must be kept alive. We'll just loop forever and let the OS
 | 
				
			||||||
 | 
					     * clean up the thread when main exits ;)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    while(true) {
 | 
				
			||||||
 | 
					      std::this_thread::sleep_for(std::chrono::seconds(5));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch(const std::exception& e) {
 | 
				
			||||||
 | 
					    fprintf(stderr, "Single-player server thread exception: %s\n", e.what());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GameState::GameState(void) : _current_screen(Screen::MAIN_MENU) {}
 | 
					GameState::GameState(void) : _current_screen(Screen::MAIN_MENU) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GameState::~GameState(void) = default;
 | 
					GameState::~GameState(void) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GameState::init(void) {
 | 
					void GameState::init(int screen_width, int screen_height) {
 | 
				
			||||||
  /* Create and connect the network client. */
 | 
					  /* Create and connect the network client. */
 | 
				
			||||||
  _network = std::make_unique<ClientNetwork>();
 | 
					  _network = std::make_unique<ClientNetwork>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* TODO: For now, connect immediately, later, trigger by menu option. */
 | 
					  _main_menu = std::make_unique<MainMenu>(screen_width, screen_height);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GameState::start_single_player_now(int screen_width, int screen_height) {
 | 
				
			||||||
 | 
					  fprintf(stdout, "Starting in single-player mode...\n");
 | 
				
			||||||
 | 
					  std::thread server_thread(&GameState::_run_server, this);
 | 
				
			||||||
 | 
					  server_thread.detach();
 | 
				
			||||||
 | 
					  std::this_thread::sleep_for(std::chrono::milliseconds(200));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _network = std::make_unique<ClientNetwork>();
 | 
				
			||||||
  if(!_network->connect("127.0.0.1", 1337)) {
 | 
					  if(!_network->connect("127.0.0.1", 1337)) {
 | 
				
			||||||
    /* TODO: Handle connection failure. */
 | 
					    /* TODO: Handle connection failure. */
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -37,7 +67,9 @@ void GameState::init(void) {
 | 
				
			|||||||
void GameState::handle_event(SDL_Event* event) {
 | 
					void GameState::handle_event(SDL_Event* event) {
 | 
				
			||||||
  switch(_current_screen) {
 | 
					  switch(_current_screen) {
 | 
				
			||||||
  case Screen::MAIN_MENU:
 | 
					  case Screen::MAIN_MENU:
 | 
				
			||||||
    /* TODO: */
 | 
					    if(_main_menu) {
 | 
				
			||||||
 | 
					      _main_menu->handle_event(event);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
  case Screen::BOOTING:
 | 
					  case Screen::BOOTING:
 | 
				
			||||||
    /* TODO: */
 | 
					    /* TODO: */
 | 
				
			||||||
@ -52,13 +84,33 @@ void GameState::handle_event(SDL_Event* event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void GameState::update(void) {
 | 
					void GameState::update(void) {
 | 
				
			||||||
  switch(_current_screen) {
 | 
					  switch(_current_screen) {
 | 
				
			||||||
  case Screen::MAIN_MENU:
 | 
					  case Screen::MAIN_MENU: {
 | 
				
			||||||
    /* TODO: */
 | 
					    if(!_main_menu) break;
 | 
				
			||||||
 | 
					    Screen next_screen = _main_menu->update();
 | 
				
			||||||
 | 
					    if(next_screen != Screen::MAIN_MENU) {
 | 
				
			||||||
 | 
					      _current_screen = next_screen;
 | 
				
			||||||
 | 
					      _main_menu.reset(); /* Free mem. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if(_current_screen == Screen::BOOTING) {
 | 
				
			||||||
 | 
					        fprintf(stdout, "Starting in single-player mode...\n");
 | 
				
			||||||
 | 
					        std::thread server_thread(&GameState::_run_server, this);
 | 
				
			||||||
 | 
					          server_thread.detach();
 | 
				
			||||||
 | 
					          std::this_thread::sleep_for(std::chrono::milliseconds(200));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!_network->connect("127.0.0.1", 1337)) {
 | 
				
			||||||
 | 
					          /* TODO: Handle connection failure. */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* TODO: transition to book screen. */
 | 
				
			||||||
 | 
					        _current_screen = Screen::DESKTOP;
 | 
				
			||||||
 | 
					        _init_desktop();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  case Screen::BOOTING:
 | 
					  case Screen::BOOTING:
 | 
				
			||||||
    /* TODO: */
 | 
					    /* TODO: */
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
  case Screen::DESKTOP:
 | 
					  case Screen::DESKTOP: {
 | 
				
			||||||
    std::string server_msg;
 | 
					    std::string server_msg;
 | 
				
			||||||
      while(_network->poll_message(server_msg)) {
 | 
					      while(_network->poll_message(server_msg)) {
 | 
				
			||||||
        UIWindow* focused_window = _desktop->get_focused_window();
 | 
					        UIWindow* focused_window = _desktop->get_focused_window();
 | 
				
			||||||
@ -92,6 +144,7 @@ void GameState::update(void) {
 | 
				
			|||||||
        _desktop->update();
 | 
					        _desktop->update();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,7 +152,9 @@ void GameState::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer
 | 
				
			|||||||
                       int screen_height, bool show_cursor) {
 | 
					                       int screen_height, bool show_cursor) {
 | 
				
			||||||
  switch(_current_screen) {
 | 
					  switch(_current_screen) {
 | 
				
			||||||
  case Screen::MAIN_MENU:
 | 
					  case Screen::MAIN_MENU:
 | 
				
			||||||
    /* TODO: */
 | 
					    if(_main_menu) {
 | 
				
			||||||
 | 
					      _main_menu->render(shape_renderer, txt_renderer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    break;
 | 
					    break;
 | 
				
			||||||
  case Screen::BOOTING:
 | 
					  case Screen::BOOTING:
 | 
				
			||||||
    /* TODO: */
 | 
					    /* TODO: */
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ClientNetwork;
 | 
					class ClientNetwork;
 | 
				
			||||||
class Desktop;
 | 
					class Desktop;
 | 
				
			||||||
 | 
					class MainMenu;
 | 
				
			||||||
class ShapeRenderer;
 | 
					class ShapeRenderer;
 | 
				
			||||||
class TextRenderer;
 | 
					class TextRenderer;
 | 
				
			||||||
union SDL_Event;
 | 
					union SDL_Event;
 | 
				
			||||||
@ -19,7 +20,8 @@ public:
 | 
				
			|||||||
  GameState(void);
 | 
					  GameState(void);
 | 
				
			||||||
  ~GameState(void);
 | 
					  ~GameState(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void init(void);
 | 
					  void init(int screen_width, int screen_height);
 | 
				
			||||||
 | 
					  void start_single_player_now(int screen_width, int screen_height);
 | 
				
			||||||
  void handle_event(SDL_Event* event);
 | 
					  void handle_event(SDL_Event* event);
 | 
				
			||||||
  void update(void);
 | 
					  void update(void);
 | 
				
			||||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
					  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
				
			||||||
@ -28,7 +30,9 @@ public:
 | 
				
			|||||||
private:
 | 
					private:
 | 
				
			||||||
  std::unique_ptr<ClientNetwork> _network;
 | 
					  std::unique_ptr<ClientNetwork> _network;
 | 
				
			||||||
  std::unique_ptr<Desktop>       _desktop;
 | 
					  std::unique_ptr<Desktop>       _desktop;
 | 
				
			||||||
 | 
					  std::unique_ptr<MainMenu>      _main_menu;
 | 
				
			||||||
  Screen                         _current_screen;
 | 
					  Screen                         _current_screen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _init_desktop(void);
 | 
					  void _init_desktop(void);
 | 
				
			||||||
 | 
					  void _run_server(void);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,47 +1,17 @@
 | 
				
			|||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <cstdio>
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
#include <GL/glew.h>
 | 
					#include <GL/glew.h>
 | 
				
			||||||
#include <SDL3/SDL.h>
 | 
					#include <SDL3/SDL.h>
 | 
				
			||||||
#include <SDL3/SDL_keyboard.h>
 | 
					#include <SDL3/SDL_keyboard.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* For single player mode. */
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
#include <thread>
 | 
					 | 
				
			||||||
#include <chrono>
 | 
					 | 
				
			||||||
#include "../../server/src/network_manager.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "gfx/shape_renderer.h"
 | 
					#include "gfx/shape_renderer.h"
 | 
				
			||||||
#include "gfx/txt_renderer.h"
 | 
					#include "gfx/txt_renderer.h"
 | 
				
			||||||
#include "game_state.h"
 | 
					#include "game_state.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void run_server(void) {
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    NetworkManager server;
 | 
					 | 
				
			||||||
    server.start(1337);
 | 
					 | 
				
			||||||
    /*
 | 
					 | 
				
			||||||
     * Server's start() method is non-blocking, but NetworkManager
 | 
					 | 
				
			||||||
     * object must be kept alive. We'll just loop forever and let the OS
 | 
					 | 
				
			||||||
     * clean up the thread when main exits ;)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    while(true) {
 | 
					 | 
				
			||||||
      std::this_thread::sleep_for(std::chrono::seconds(5));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } catch(const std::exception& e) {
 | 
					 | 
				
			||||||
    fprintf(stderr, "Single-player server thread exception: %s\n", e.what());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const int SCREEN_WIDTH  = 1280;
 | 
					const int SCREEN_WIDTH  = 1280;
 | 
				
			||||||
const int SCREEN_HEIGHT = 720;
 | 
					const int SCREEN_HEIGHT = 720;
 | 
				
			||||||
int main(int argc, char** argv) {
 | 
					int main(int argc, char** argv) {
 | 
				
			||||||
  if(argc > 1 && std::string(argv[1]) == "-sp") {
 | 
					 | 
				
			||||||
    fprintf(stdout, "Starting in single-player mode...\n");
 | 
					 | 
				
			||||||
    std::thread server_thread(run_server);
 | 
					 | 
				
			||||||
    server_thread.detach();
 | 
					 | 
				
			||||||
    fprintf(stdout, "Waiting for server initialise...\n");
 | 
					 | 
				
			||||||
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /* Init SDL. */
 | 
					  /* Init SDL. */
 | 
				
			||||||
  if(!SDL_Init(SDL_INIT_VIDEO)) {
 | 
					  if(!SDL_Init(SDL_INIT_VIDEO)) {
 | 
				
			||||||
    printf("SDL could not initialise! SDL_ERROR: %s\n", SDL_GetError());
 | 
					    printf("SDL could not initialise! SDL_ERROR: %s\n", SDL_GetError());
 | 
				
			||||||
@ -104,7 +74,11 @@ int main(int argc, char** argv) {
 | 
				
			|||||||
  ShapeRenderer* shape_renderer_instance = new ShapeRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
					  ShapeRenderer* shape_renderer_instance = new ShapeRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  auto game_state = std::make_unique<GameState>();
 | 
					  auto game_state = std::make_unique<GameState>();
 | 
				
			||||||
  game_state->init();
 | 
					  if(argc > 1 && std::string(argv[1]) == "-sp") {
 | 
				
			||||||
 | 
					    game_state->start_single_player_now(SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    game_state->init(SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* timer for cursor blink. */
 | 
					  /* timer for cursor blink. */
 | 
				
			||||||
  Uint32 last_blink_time = 0;
 | 
					  Uint32 last_blink_time = 0;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										142
									
								
								client/src/ui/main_menu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								client/src/ui/main_menu.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					#include <SDL3/SDL_events.h>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "gfx/shape_renderer.h"
 | 
				
			||||||
 | 
					#include "gfx/txt_renderer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "main_menu.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Get a random snippet form loaded word list. */
 | 
				
			||||||
 | 
					const std::string& get_random_snippet(const std::vector<std::string>& snippets) {
 | 
				
			||||||
 | 
					  /* If empty, return a default to avoid a crash? */
 | 
				
			||||||
 | 
					  if(snippets.empty()) {
 | 
				
			||||||
 | 
					    static const std::string empty_str = "ERROR: snippets file not found";
 | 
				
			||||||
 | 
					    return empty_str;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  int index = rand() % snippets.size();
 | 
				
			||||||
 | 
					  return snippets[index];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MainMenu::MainMenu(int screen_width, int screen_height) :
 | 
				
			||||||
 | 
					    _screen_width(screen_width), _screen_height(screen_height),
 | 
				
			||||||
 | 
					    _next_screen(Screen::MAIN_MENU) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Load snippets form file. */
 | 
				
			||||||
 | 
					  std::ifstream snippet_file("assets/menu_background_snippets.txt");
 | 
				
			||||||
 | 
					  if(!snippet_file.is_open()) {
 | 
				
			||||||
 | 
					    printf("ERROR: Failed to open assets/menu_background_snippets.txt\n");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    std::string line;
 | 
				
			||||||
 | 
					    while(std::getline(snippet_file, line)) {
 | 
				
			||||||
 | 
					      if(!line.empty()) {
 | 
				
			||||||
 | 
					        _snippets.push_back(line);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Initialise buttons. */
 | 
				
			||||||
 | 
					  const int button_width  = 200;
 | 
				
			||||||
 | 
					  const int button_height = 50;
 | 
				
			||||||
 | 
					  const int center_x = (screen_width/2) - (button_width/2);
 | 
				
			||||||
 | 
					  const int center_y = (screen_height/2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _buttons.push_back({
 | 
				
			||||||
 | 
					    "Single-Player",
 | 
				
			||||||
 | 
					    { center_x, center_y + 30, button_width, button_height },
 | 
				
			||||||
 | 
					    Screen::BOOTING, /* This will trigger the booting screen. */
 | 
				
			||||||
 | 
					    false
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  _buttons.push_back({
 | 
				
			||||||
 | 
					    "Online",
 | 
				
			||||||
 | 
					    { center_x, center_y - 30, button_width, button_height },
 | 
				
			||||||
 | 
					    Screen::BOOTING,
 | 
				
			||||||
 | 
					    false
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Initialise animated background. */
 | 
				
			||||||
 | 
					  srand(time(NULL));
 | 
				
			||||||
 | 
					  for(int i = 0; i < 100; ++i) {
 | 
				
			||||||
 | 
					    _background_text.push_back({
 | 
				
			||||||
 | 
					      get_random_snippet(_snippets),
 | 
				
			||||||
 | 
					      (float)(rand() % screen_width),
 | 
				
			||||||
 | 
					      (float)(rand() % screen_height),
 | 
				
			||||||
 | 
					      (float)(rand() % 50 + 20) / 100.0f /* Random speed. */
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MainMenu::~MainMenu(void) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainMenu::handle_event(SDL_Event* event) {
 | 
				
			||||||
 | 
					  if(event->type == SDL_EVENT_MOUSE_MOTION) {
 | 
				
			||||||
 | 
					    int mouse_x = event->motion.x;
 | 
				
			||||||
 | 
					    int mouse_y = event->motion.y;
 | 
				
			||||||
 | 
					    for(auto& button : _buttons) {
 | 
				
			||||||
 | 
					      /* Invert mouse_y for UI coordinate system. */
 | 
				
			||||||
 | 
					      int inverted_y = _screen_height - mouse_y;
 | 
				
			||||||
 | 
					      button.is_hovered = (mouse_x >= button.rect.x
 | 
				
			||||||
 | 
					        && mouse_x <= button.rect.x + button.rect.w
 | 
				
			||||||
 | 
					        && inverted_y >= button.rect.y
 | 
				
			||||||
 | 
					        && inverted_y <= button.rect.y + button.rect.h);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
 | 
				
			||||||
 | 
					    for(const auto& button : _buttons) {
 | 
				
			||||||
 | 
					      if(button.is_hovered) {
 | 
				
			||||||
 | 
					        _next_screen = button.action;
 | 
				
			||||||
 | 
					        break; /* Once clicked button found, exit. */
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Screen MainMenu::update(void) {
 | 
				
			||||||
 | 
					  _update_background();
 | 
				
			||||||
 | 
					  return _next_screen;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainMenu::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer) {
 | 
				
			||||||
 | 
					  _render_background(txt_renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Button colours. */
 | 
				
			||||||
 | 
					  const Color button_color        = { 0.1f, 0.15f, 0.2f };
 | 
				
			||||||
 | 
					  const Color button_hover_color  = { 0.2f, 0.25f, 0.3f };
 | 
				
			||||||
 | 
					  const Color text_color          = { 0.8f, 0.8f,  0.8f };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for(const auto& button : _buttons) {
 | 
				
			||||||
 | 
					    /* Draw button background. */
 | 
				
			||||||
 | 
					    if(button.is_hovered) {
 | 
				
			||||||
 | 
					      shape_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w,
 | 
				
			||||||
 | 
					                                button.rect.h, button_hover_color);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      shape_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w,
 | 
				
			||||||
 | 
					                                button.rect.h, button_color);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Draw button text centered.
 | 
				
			||||||
 | 
					     * TODO: Calculate text width for perfect centering.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    txt_renderer->render_text(button.label.c_str(), button.rect.x + 50,
 | 
				
			||||||
 | 
					                              button.rect.y + 18, 1.0f, text_color);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainMenu::_update_background(void) {
 | 
				
			||||||
 | 
					  for(auto& line : _background_text) {
 | 
				
			||||||
 | 
					    line.y -= line.speed;
 | 
				
			||||||
 | 
					    if(line.y < 0) {
 | 
				
			||||||
 | 
					      line.y = _screen_height;
 | 
				
			||||||
 | 
					      line.x = (float)(rand() % _screen_width);
 | 
				
			||||||
 | 
					      line.text = get_random_snippet(_snippets);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainMenu::_render_background(TextRenderer* txt_renderer) {
 | 
				
			||||||
 | 
					  const Color background_text_color = { 0.0f, 0.35f, 0.15f }; /* Dark green. */
 | 
				
			||||||
 | 
					  for(const auto& line : _background_text) {
 | 
				
			||||||
 | 
					    txt_renderer->render_text(line.text.c_str(), line.x, line.y, 1.0f, 
 | 
				
			||||||
 | 
					                              background_text_color);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								client/src/ui/main_menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								client/src/ui/main_menu.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "gfx/txt_renderer.h"
 | 
				
			||||||
 | 
					#include "gfx/shape_renderer.h"
 | 
				
			||||||
 | 
					#include "gfx/types.h"
 | 
				
			||||||
 | 
					#include "game_state.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					union SDL_Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MenuButton {
 | 
				
			||||||
 | 
					  std::string label;
 | 
				
			||||||
 | 
					  Rect rect;
 | 
				
			||||||
 | 
					  Screen action; /* Change state. */
 | 
				
			||||||
 | 
					  bool is_hovered = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MainMenu {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  MainMenu(int screen_width, int screen_height);
 | 
				
			||||||
 | 
					  ~MainMenu(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_event(SDL_Event* event);
 | 
				
			||||||
 | 
					  Screen update(void);
 | 
				
			||||||
 | 
					  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  void _update_background(void);
 | 
				
			||||||
 | 
					  void _render_background(TextRenderer* txdt_renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* For animated background. */
 | 
				
			||||||
 | 
					  struct ScrollingText {
 | 
				
			||||||
 | 
					    std::string text;
 | 
				
			||||||
 | 
					    float x;
 | 
				
			||||||
 | 
					    float y;
 | 
				
			||||||
 | 
					    float speed;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::vector<ScrollingText> _background_text;
 | 
				
			||||||
 | 
					  int _screen_height;
 | 
				
			||||||
 | 
					  int _screen_width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Buttons. */
 | 
				
			||||||
 | 
					  std::vector<MenuButton>  _buttons;
 | 
				
			||||||
 | 
					  std::vector<std::string> _snippets;
 | 
				
			||||||
 | 
					  Screen                   _next_screen;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user