diff --git a/game.cpp b/game.cpp index c80a2e1..e93676e 100644 --- a/game.cpp +++ b/game.cpp @@ -64,6 +64,27 @@ void Game::loadTextures() texmgr.loadTexture("background", "media/background.png"); } +void Game::loadFonts() +{ + sf::Font font; + font.loadFromFile("media/font.ttf"); + this->fonts["main_font"] = font; + + return; +} + +void Game::loadStylesheets() +{ + this->stylesheets["button"] = GuiStyle(&this->fonts.at("main_font"), 1, + sf::Color(0xc6,0xc6,0xc6), sf::Color(0x94,0x94,0x94), sf::Color(0x00,0x00,0x00), + sf::Color(0x61,0x61,0x61), sf::Color(0x94,0x94,0x94), sf::Color(0x00,0x00,0x00)); + this->stylesheets["text"] = GuiStyle(&this->fonts.at("main_font"), 0, + sf::Color(0x00,0x00,0x00,0x00), sf::Color(0x00,0x00,0x00), sf::Color(0xff,0xff,0xff), + sf::Color(0x00,0x00,0x00,0x00), sf::Color(0x00,0x00,0x00), sf::Color(0xff,0x00,0x00)); + + return; +} + void Game::pushState(GameState* state) { this->states.push(state); @@ -116,7 +137,9 @@ Game::Game() { this->loadTextures(); this->loadTiles(); - + this->loadFonts(); + this->loadStylesheets(); + this->window.create(sf::VideoMode(800, 600), "City Builder"); this->window.setFramerateLimit(60); diff --git a/game.hpp b/game.hpp index d83f6fc..d6e62d2 100644 --- a/game.hpp +++ b/game.hpp @@ -8,6 +8,7 @@ #include "texture_manager.hpp" #include "tile.hpp" +#include "gui.hpp" class GameState; @@ -17,6 +18,8 @@ class Game void loadTextures(); void loadTiles(); + void loadStylesheets(); + void loadFonts(); public: @@ -29,6 +32,8 @@ class Game sf::Sprite background; std::map tileAtlas; + std::map stylesheets; + std::map fonts; void pushState(GameState* state); void popState(); diff --git a/game_state_start.cpp b/game_state_start.cpp index 3543c5d..6dd3cda 100644 --- a/game_state_start.cpp +++ b/game_state_start.cpp @@ -9,9 +9,11 @@ void GameStateStart::draw(const float dt) this->game->window.setView(this->view); this->game->window.clear(sf::Color::Black); - this->game->window.draw(this->game->background); + this->game->window.draw(this->game->background); - return; + for(auto gui : this->guiSystem) this->game->window.draw(gui.second); + + return; } void GameStateStart::update(const float dt) @@ -22,6 +24,8 @@ void GameStateStart::handleInput() { sf::Event event; + sf::Vector2f mousePos = this->game->window.mapPixelToCoords(sf::Mouse::getPosition(this->game->window), this->view); + while(this->game->window.pollEvent(event)) { switch(event.type) @@ -36,16 +40,39 @@ void GameStateStart::handleInput() case sf::Event::Resized: { this->view.setSize(event.size.width, event.size.height); - this->game->background.setPosition(this->game->window.mapPixelToCoords(sf::Vector2i(0, 0))); + this->game->background.setPosition(this->game->window.mapPixelToCoords(sf::Vector2i(0, 0), this->view)); + sf::Vector2f pos = sf::Vector2f(event.size.width, event.size.height); + pos *= 0.5f; + pos = this->game->window.mapPixelToCoords(sf::Vector2i(pos), this->view); + this->guiSystem.at("menu").setPosition(pos); this->game->background.setScale( float(event.size.width) / float(this->game->background.getTexture()->getSize().x), float(event.size.height) / float(this->game->background.getTexture()->getSize().y)); break; } + /* Highlight menu items */ + case sf::Event::MouseMoved: + { + this->guiSystem.at("menu").highlight(this->guiSystem.at("menu").getEntry(mousePos)); + break; + } + /* Click on menu items */ + case sf::Event::MouseButtonPressed: + { + if(event.mouseButton.button == sf::Mouse::Left) + { + std::string msg = this->guiSystem.at("menu").activate(mousePos); + + if(msg == "load_game") + { + this->loadgame(); + } + } + break; + } case sf::Event::KeyPressed: { if(event.key.code == sf::Keyboard::Escape) this->game->window.close(); - else if(event.key.code == sf::Keyboard::Space) this->loadgame(); break; } default: break; @@ -69,4 +96,11 @@ GameStateStart::GameStateStart(Game* game) this->view.setSize(pos); pos *= 0.5f; this->view.setCenter(pos); + + this->guiSystem.emplace("menu", Gui(sf::Vector2f(192, 32), 4, false, game->stylesheets.at("button"), + { std::make_pair("Load Game", "load_game") })); + + this->guiSystem.at("menu").setPosition(pos); + this->guiSystem.at("menu").setOrigin(96, 32*1/2); + this->guiSystem.at("menu").show(); } diff --git a/game_state_start.hpp b/game_state_start.hpp index 4b3894e..fea7be1 100644 --- a/game_state_start.hpp +++ b/game_state_start.hpp @@ -2,8 +2,11 @@ #define GAME_STATE_START_HPP #include +#include +#include #include "game_state.hpp" +#include "gui.hpp" class GameStateStart : public GameState { @@ -11,8 +14,10 @@ class GameStateStart : public GameState sf::View view; - void loadgame(); - + std::map guiSystem; + + void loadgame(); + public: virtual void draw(const float dt); diff --git a/gui.cpp b/gui.cpp new file mode 100644 index 0000000..cfd66ed --- /dev/null +++ b/gui.cpp @@ -0,0 +1,135 @@ +#include +#include + +#include "gui.hpp" + +sf::Vector2f Gui::getSize() +{ + return sf::Vector2f(this->dimensions.x, this->dimensions.y * this->entries.size()); +} + +int Gui::getEntry(const sf::Vector2f mousePos) +{ + /* If there are no entries then outside the menu */ + if(entries.size() == 0) return -1; + if(!this->visible) return -1; + + for(int i = 0; i < this->entries.size(); ++i) + { + /* Translate point to use the entry's local coordinates*/ + sf::Vector2f point = mousePos; + point += this->entries[i].shape.getOrigin(); + point -= this->entries[i].shape.getPosition(); + + if(point.x < 0 || point.x > this->entries[i].shape.getScale().x*this->dimensions.x) continue; + if(point.y < 0 || point.y > this->entries[i].shape.getScale().y*this->dimensions.y) continue; + return i; + } + + return -1; +} + +void Gui::setEntryText(int entry, std::string text) +{ + if(entry >= entries.size() || entry < 0) return; + + entries[entry].text.setString(text); + + return; +} + +void Gui::setDimensions(sf::Vector2f dimensions) +{ + this->dimensions = dimensions; + + for(auto& entry : entries) + { + entry.shape.setSize(dimensions); + entry.text.setCharacterSize(dimensions.y-style.borderSize-padding); + } + + return; +} + +void Gui::draw(sf::RenderTarget& target, sf::RenderStates states) const +{ + if(!visible) return; + + /* Draw each entry of the menu */ + for(auto entry : this->entries) + { + /* Draw the entry */ + target.draw(entry.shape); + target.draw(entry.text); + } + + return; +} + +void Gui::show() +{ + sf::Vector2f offset(0.0f, 0.0f); + + this->visible = true; + + /* Draw each entry of the menu */ + for(auto& entry : this->entries) + { + /* Set the origin of the entry */ + sf::Vector2f origin = this->getOrigin(); + origin -= offset; + entry.shape.setOrigin(origin); + entry.text.setOrigin(origin); + + /* Compute the position of the entry */ + entry.shape.setPosition(this->getPosition()); + entry.text.setPosition(this->getPosition()); + + if(this->horizontal) offset.x += this->dimensions.x; + else offset.y += this->dimensions.y; + } + + return; +} + +void Gui::hide() +{ + this->visible = false; + + return; +} + +/* Highlights an entry of the menu */ +void Gui::highlight(const int entry) +{ + for(int i = 0; i < entries.size(); ++i) + { + if(i == entry) + { + entries[i].shape.setFillColor(style.bodyHighlightCol); + entries[i].shape.setOutlineColor(style.borderHighlightCol); + entries[i].text.setColor(style.textHighlightCol); + } + else + { + entries[i].shape.setFillColor(style.bodyCol); + entries[i].shape.setOutlineColor(style.borderCol); + entries[i].text.setColor(style.textCol); + } + } + + return; +} + +/* Return the message bound to the entry */ +std::string Gui::activate(const int entry) +{ + if(entry == -1) return "null"; + return entries[entry].message; +} + +std::string Gui::activate(sf::Vector2f mousePos) +{ + int entry = this->getEntry(mousePos); + return this->activate(entry); +} diff --git a/gui.hpp b/gui.hpp new file mode 100644 index 0000000..bc9b3f6 --- /dev/null +++ b/gui.hpp @@ -0,0 +1,137 @@ +#ifndef GUI_HPP +#define GUI_HPP + +#include +#include +#include + +class GuiStyle +{ + public: + + sf::Color bodyCol; + sf::Color bodyHighlightCol; + sf::Color borderCol; + sf::Color borderHighlightCol; + sf::Color textCol; + sf::Color textHighlightCol; + sf::Font* font; + + float borderSize; + + GuiStyle(sf::Font* font, float borderSize, + sf::Color bodyCol, sf::Color borderCol, sf::Color textCol, + sf::Color bodyHighlightCol, sf::Color borderHighlightCol, sf::Color textHighlightCol) + { + this->bodyCol = bodyCol; + this->borderCol = borderCol; + this->textCol = textCol; + this->bodyHighlightCol = bodyHighlightCol; + this->borderHighlightCol = borderHighlightCol; + this->textHighlightCol = textHighlightCol; + this->font = font; + this->borderSize = borderSize; + } + GuiStyle() { } +}; + +class GuiEntry +{ + public: + + /* Handles appearance of the entry */ + sf::RectangleShape shape; + + /* String returned when the entry is activated */ + std::string message; + + /* Text displayed on the entry */ + sf::Text text; + + GuiEntry(const std::string& message, sf::RectangleShape shape, sf::Text text) + { + this->message = message; + this->shape = shape; + this->text = text; + } + GuiEntry() { } +}; + +class Gui : public sf::Transformable, public sf::Drawable +{ + private: + + /* If true the menu entries will be horizontally, not vertically, adjacent */ + bool horizontal; + + GuiStyle style; + + sf::Vector2f dimensions; + + int padding; + + public: + + std::vector entries; + + bool visible; + + /* Constructor */ + Gui(sf::Vector2f dimensions, int padding, bool horizontal, + GuiStyle& style, std::vector> entries) + { + visible = false; + this->horizontal = horizontal; + this->style = style; + this->dimensions = dimensions; + this->padding = padding; + + /* Construct the background shape */ + sf::RectangleShape shape; + shape.setSize(dimensions); + shape.setFillColor(style.bodyCol); + shape.setOutlineThickness(-style.borderSize); + shape.setOutlineColor(style.borderCol); + + /* Construct each gui entry */ + for(auto entry : entries) + { + /* Construct the text */ + sf::Text text; + text.setString(entry.first); + text.setFont(*style.font); + text.setColor(style.textCol); + text.setCharacterSize(dimensions.y-style.borderSize-padding); + + this->entries.push_back(GuiEntry(entry.second, shape, text)); + } + } + + sf::Vector2f getSize(); + + /* Return the entry that the mouse is hovering over. Returns + * -1 if the mouse if outside of the Gui */ + int getEntry(const sf::Vector2f mousePos); + + /* Change the text of an entry */ + void setEntryText(int entry, std::string text); + + /* Change the entry dimensions */ + void setDimensions(sf::Vector2f dimensions); + + /* Draw the menu */ + virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; + + void show(); + + void hide(); + + /* Highlights an entry of the menu */ + void highlight(const int entry); + + /* Return the message bound to the entry */ + std::string activate(const int entry); + std::string activate(const sf::Vector2f mousePos); +}; + +#endif /* GUI_HPP */ diff --git a/media/font.ttf b/media/font.ttf new file mode 100644 index 0000000..a007071 Binary files /dev/null and b/media/font.ttf differ