From a3df4a31898c7b24198a4126dbb9acd9ab572b1d Mon Sep 17 00:00:00 2001 From: Tim Schubert Date: Sat, 23 Jan 2016 03:27:32 +0100 Subject: [PATCH] Refactoring, Exception handling and Events. --- src/Bob.cpp | 136 +++++++++++++++++ src/Bob.hpp | 91 +++++++++++ src/CMakeLists.txt | 5 +- src/Events.cpp | 24 +++ src/Events.hpp | 39 +++++ src/Exceptions.hpp | 42 ++++++ src/Gameplay.cpp | 319 +++++++++++++++++++++++++-------------- src/Gameplay.hpp | 343 +++++++++++++++++++++++++++--------------- src/Grid.cpp | 237 ++--------------------------- src/Grid.hpp | 367 +++++++++++++++++++++++++++++---------------- src/Gui.cpp | 174 +++++++++++++++++++++ src/Gui.hpp | 194 ++++++++++++++++++++++++ src/Main.cpp | 225 --------------------------- src/Main.hpp | 97 ------------ 14 files changed, 1377 insertions(+), 916 deletions(-) create mode 100644 src/Bob.cpp create mode 100644 src/Bob.hpp create mode 100644 src/Events.cpp create mode 100644 src/Events.hpp create mode 100644 src/Exceptions.hpp create mode 100644 src/Gui.cpp create mode 100644 src/Gui.hpp delete mode 100644 src/Main.cpp delete mode 100644 src/Main.hpp diff --git a/src/Bob.cpp b/src/Bob.cpp new file mode 100644 index 0000000..f5fb1bb --- /dev/null +++ b/src/Bob.cpp @@ -0,0 +1,136 @@ +#include "Bob.hpp" + +void Game::handle_event(SDL_Event *event) +{ + switch (event->type) + { + case (SDL_QUIT): + quit = true; + break; // ignore input + case SDL_WINDOWEVENT: + if (event->window.windowID == this->window->get_window_id()) + { + switch (event->window.event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + this->grid->update_box({event->window.data1, event->window.data2}); + break; + default: + break; + } + } + break; + case SDL_MOUSEMOTION: + grid->handle_event(event); + break; + case SDL_MOUSEWHEEL: + grid->handle_event(event); + break; + case SDL_MOUSEBUTTONDOWN: + grid->handle_event(event); + break; + case SDL_KEYDOWN: + switch (event->key.keysym.sym) + { + case SDLK_w: + this->move[0] = true; + break; + case SDLK_a: + this->move[1] = true; + break; + case SDLK_s: + this->move[2] = true; + break; + case SDLK_d: + this->move[3] = true; + break; + case SDLK_f: + this->grid->update_box(this->window->toggle_fullscreen()); + break; + case SDLK_ESCAPE: + this->quit = true; + break; + default: + break; + } + break; + case SDL_KEYUP: + switch (event->key.keysym.sym) + { + case SDLK_w: + this->move[0] = false; + break; + case SDLK_a: + this->move[1] = false; + break; + case SDLK_s: + this->move[2] = false; + break; + case SDLK_d: + this->move[3] = false; + break; + default: + break; + } + break; + default: + break; + } +} + +int Game::game_loop() +{ + this->frame_timer->start_timer(); + this->move_timer->start_timer(); + double fps; + Uint32 frame_counter = 0; + while (!this->quit) + { + if (move_timer->get_timer() > 16) + { + move_timer->reset_timer(); + SDL_Point move_by = {(this->move[1] - this->move[3]) * 20, (this->move[0] - this->move[2]) * 20}; + this->grid->move(move_by); + } + if (frame_timer->get_timer() > 1000.0) + { + fps = frame_counter / (frame_timer->reset_timer() / 1000.0); + frame_counter = 0; + std::cout << fps << " fps" << std::endl; + } + SDL_Event event; + while (SDL_PollEvent(&event)) + { + this->handle_event(&event); + } + this->renderer->set_draw_color({0x0, 0x0, 0x0, 0xf}); + this->renderer->clear(); + this->renderer->set_draw_color({0xff, 0xff, 0xff, 0xff}); + this->render(); + this->renderer->present(); + frame_counter++; + } + this->renderer->clear(); + return 0; +} + +void Game::render() +{ + SDL_Renderer *renderer = this->renderer->get_renderer(); + this->grid->render(renderer); +} + +int main(int, char **) +{ + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + logSDLError(std::cerr, "SDL_init"); + } +// register_events(4); + SDL_Rect window_dimensions = {SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGTH}; + Game *game = new Game(&window_dimensions, 6); + int exit_status = game->game_loop(); + delete game; + SDL_Quit(); + return exit_status; +} \ No newline at end of file diff --git a/src/Bob.hpp b/src/Bob.hpp new file mode 100644 index 0000000..555c243 --- /dev/null +++ b/src/Bob.hpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include "Exceptions.hpp" +#include "Gameplay.hpp" +//#include "Events.hpp" +#include "Gui.hpp" + +#ifndef _BOB_H +#define _BOB_H + +const int SCREEN_WIDTH = 800; +const int SCREEN_HEIGTH = 600; +const int SIDEBAR_WIDTH = 200; +const std::string TITLE = "Bob - Battles of Bacteria"; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +Uint32 rmask = 0xff000000; +Uint32 gmask = 0x00ff0000; +Uint32 bmask = 0x0000ff00; +Uint32 amask = 0x000000ff; +#else +Uint32 rmask = 0x000000ff; +Uint32 gmask = 0x0000ff00; +Uint32 bmask = 0x00ff0000; +Uint32 amask = 0xff000000; +#endif + +class Game +{ + +public: + Game(SDL_Rect *window_dimensions, Sint16 size) + { + this->layout = new Layout(pointy_orientation, size, + {window_dimensions->w / 2, window_dimensions->h / 2}, + {0, 0, window_dimensions->w - SIDEBAR_WIDTH, window_dimensions->h}); + this->grid = new HexagonGrid(size, this->layout); + for (int i = 0; i < 4; i++) + { + this->move[i] = false; + } + this->quit = false; + try + { + this->window = new Window(TITLE, window_dimensions, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); + this->renderer = new Renderer(this->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + SDL_Rect side_bar_dimensions = {window_dimensions->x - SIDEBAR_WIDTH, 0, SIDEBAR_WIDTH, + window_dimensions->y}; + } + catch (const SDL_Exception &sdl_except) + { + std::cerr << sdl_except.what() << " happened when constructing game" << std::endl; + } + this->frame_timer = new Timer(); + this->move_timer = new Timer(); + } + + ~Game() + { + delete this->move_timer; + delete this->frame_timer; + // delete this->side_bar; + delete this->grid; + delete this->renderer; + delete this->window; + delete this->layout; + } + + void render(); + + void handle_event(SDL_Event *event); + + int game_loop(); + +private: + Window *window; + Renderer *renderer; + HexagonGrid *grid; + Layout *layout; + // SideBar *side_bar; + bool move[4]; + bool quit; + Timer *frame_timer; + Timer *move_timer; +}; + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b90f94f..1cccb73 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,2 @@ - -add_executable(Bob Main.cpp Main.hpp Grid.cpp Grid.hpp Gameplay.cpp Gameplay.hpp Gui.hpp Gui.cpp) -target_link_libraries(Bob ${SDL2_LIB} ${SDL2_GFX_LIB} ${SDL2_TTF_LIB} ${Boost_LIBRARIES}) +add_executable(Bob Bob.cpp Bob.hpp Grid.cpp Grid.hpp Gameplay.cpp Gameplay.hpp Gui.hpp Gui.cpp Events.cpp Exceptions.hpp) +target_link_libraries(Bob ${SDL2_LIB} ${SDL2_GFX_LIB} ${SDL2_TTF_LIB} ${Boost_LIBRARIES}) \ No newline at end of file diff --git a/src/Events.cpp b/src/Events.cpp new file mode 100644 index 0000000..5dc04c5 --- /dev/null +++ b/src/Events.cpp @@ -0,0 +1,24 @@ +#include "Events.hpp" + +void Timer::start_timer() +{ + this->timer_started = SDL_GetTicks(); +} + +Uint32 Timer::get_timer() +{ + Uint32 ticks_passed = SDL_GetTicks() - this->timer_started; + return ticks_passed; +} + +Uint32 Timer::reset_timer() +{ + Uint32 ticks_passed = this->get_timer(); + this->timer_started = SDL_GetTicks(); + return ticks_passed; +} + +Uint32 register_events(Uint32 n) +{ + return SDL_RegisterEvents(n); +} \ No newline at end of file diff --git a/src/Events.hpp b/src/Events.hpp new file mode 100644 index 0000000..f8a99ad --- /dev/null +++ b/src/Events.hpp @@ -0,0 +1,39 @@ +#include +#include +#include "Grid.hpp" +#include "Gameplay.hpp" + +#ifndef _EVENTS_H +#define _EVENTS_H + +class Timer +{ +private: + Uint32 timer_started; +public: + void start_timer(); + + Uint32 get_timer(); + + Uint32 reset_timer(); +}; + +struct NextTurn +{ + boost::uuids::uuid player; + Uint32 time_elapsed; +}; + +struct MarkerUpdate +{ + FieldMeta *field; +}; + +struct FieldUpdate +{ + FieldMeta *field; +}; + +Uint32 register_events(Uint32 n); + +#endif \ No newline at end of file diff --git a/src/Exceptions.hpp b/src/Exceptions.hpp new file mode 100644 index 0000000..198aacd --- /dev/null +++ b/src/Exceptions.hpp @@ -0,0 +1,42 @@ +#include + +#ifndef BOB_EXCEPTIONS_H +#define BOB_EXCEPTIONS_H + +class SDL_Exception : public std::exception +{ +public: + virtual const char *what() const throw() + { + return "SDL_Exception"; + } +}; + +class SDL_WindowException : public SDL_Exception +{ +public: + virtual const char *what() const throw() + { + return "SDL_WindowException"; + } +}; + +class SDL_RendererException : public SDL_Exception +{ +public: + virtual const char *what() const throw() + { + return "SDL_RendererException"; + } +}; + +class SDL_TextureException : public SDL_Exception +{ +public: + virtual const char *what() const throw() + { + return "SDL_TextureException"; + } +}; + +#endif //BOB_EXCEPTIONS_H diff --git a/src/Gameplay.cpp b/src/Gameplay.cpp index 2540164..e1aeb8d 100644 --- a/src/Gameplay.cpp +++ b/src/Gameplay.cpp @@ -1,23 +1,12 @@ #include "Gameplay.hpp" -Player Player::default_player = Player("Default"); - -std::unordered_map FieldMeta::fields = std::unordered_map(); - -Player *FieldMeta::get_owner() -{ - return this->owner; -} - -Field FieldMeta::get_field() { return this->field; } - void FieldMeta::render(SDL_Renderer *renderer, Layout *layout) { - Point precise_location = field_to_point(this->field, layout); + Point precise_location = this->field.field_to_point(layout); SDL_Point location; location.x = (int) precise_location.x; location.y = (int) precise_location.y; - std::vector polygon = field_to_polygon(this->field, layout); + std::vector polygon = this->field.field_to_polygon(layout); SDL_Color color = this->owner->get_color(); Sint16 vx[6]; Sint16 vy[6]; @@ -26,7 +15,7 @@ void FieldMeta::render(SDL_Renderer *renderer, Layout *layout) vx[i] = (Sint16) polygon[i].x; vy[i] = (Sint16) polygon[i].y; } - if ((*this->owner) == Player::default_player) + if (this->owner == this->grid->get_default_player()) color = {0x77, 0x77, 0x77, 0x77}; filledPolygonRGBA(renderer, vx, vy, 6, color.r, color.g, color.b, 0x33); SDL_Color inverse; @@ -70,32 +59,21 @@ void FieldMeta::render(SDL_Renderer *renderer, Layout *layout) } } - void FieldMeta::regenerate_resources() { - resources = resources_base; - switch (this->upgrades) - { - case (UPGRADE_REGENERATION_1): - resources *= 2; - case (UPGRADE_REGENERATION_2): - resources *= 2; - case (UPGRADE_REGENERATION_3): - resources *= 2; - default: - break; - } + this->resources = resources_base; + if (this->upgrades[Regeneration_1]) + this->resources *= 2; + if (this->upgrades[Regeneration_2]) + this->resources *= 2; + if (this->upgrades[Regeneration_3]) + this->resources *= 2; } -Resource FieldMeta::get_resources() -{ - return this->resources; -} - -Resource FieldMeta::get_resources_of_cluster(Cluster cluster) +Resource Grid::get_resources_of_cluster(const Cluster *cluster) { Resource res = {0, 0, 0}; - for (FieldMeta *elem : cluster) + for (FieldMeta *elem : *cluster) { Resource r_plus = elem->get_resources(); res += r_plus; @@ -103,137 +81,252 @@ Resource FieldMeta::get_resources_of_cluster(Cluster cluster) return res; } -Resource FieldMeta::get_resources_of_cluster() -{ - Cluster cluster = this->get_cluster(); - return this->get_resources_of_cluster(cluster); -} - -Uint32 FieldMeta::get_upgrades() { return this->upgrades; } - bool FieldMeta::upgrade(Upgrade upgrade) { // check available resources for cluster and consume resources - Resource cluster_resources = this->get_resources_of_cluster(); - Resource costs; - switch (upgrade) + Cluster *cluster = this->grid->get_cluster(this); + Resource cluster_resources = this->grid->get_resources_of_cluster(this->grid->get_cluster(this, cluster)); + Resource remaining_costs; + try { - case UPGRADE_REGENERATION_1: - costs = {(1 << 1), (1 << 1), (1 << 1)}; - break; - case UPGRADE_REGENERATION_2: - costs = {(1 << 2), (1 << 2), (1 << 2)}; - break; - case UPGRADE_REGENERATION_3: - costs = {(1 << 3), (1 << 3), (1 << 3)}; - - break; - default: - std::cout << "Unknown update: " << upgrade; - break; + Resource costs = UPGRADE_COSTS.at(upgrade); + remaining_costs = this->grid->consume_resources_of_cluster(cluster, costs); } - Resource remaining_costs = this->consume_resources_of_cluster(costs); - Resource neutral = {0, 0, 0}; + catch (const std::out_of_range &oor) + { + std::cerr << "Out of Range exception: " << oor.what() << std::endl; + remaining_costs = {1, 1, 1}; + } + static const Resource neutral = {0, 0, 0}; if (remaining_costs == neutral) { - this->upgrades |= upgrade; - return true; + this->upgrades[upgrade] = true; } - return false; + return this->upgrades[upgrade]; } -Cluster FieldMeta::get_cluster(Cluster visited) +FieldMeta *FieldMeta::get_neighbor(Uint8 direction) { - if (visited.find(this) != visited.end()) // already been here before + return this->grid->get_neighbor(this, direction); +} + +FieldMeta *HexagonGrid::get_neighbor(FieldMeta *meta, Uint8 direction) +{ + Field neighbor_field = meta->get_field().get_neighbor(direction); + try + { + return this->fields.at(neighbor_field); + } + catch (const std::out_of_range &oor) + { + std::cerr << "Tried to look up non-existing field: " << neighbor_field << std::endl; + } + return nullptr; +} + +Cluster *HexagonGrid::get_cluster(FieldMeta *field, Cluster *visited) +{ + if (visited == nullptr) + visited = new Cluster(); + if (visited->find(field) != visited->end()) // already been here before return visited; - - visited.insert(this); - + else + visited->insert(field); for (Uint8 i = 0; i < 6; i++) { - auto neighbor_pair = FieldMeta::fields.find(hex_neighbor(i, this->get_field())); - FieldMeta *neighbor = neighbor_pair->second; - if (neighbor->get_owner() != this->get_owner()) // ignore meta if field not owned by specified owner + FieldMeta *neighbor = this->get_neighbor(field, i); + if (neighbor->get_owner() != field->get_owner()) // ignore meta if field not owned by specified owner return visited; - else - return neighbor->get_cluster(visited); + this->get_cluster(neighbor, visited); } return visited; } -Resource FieldMeta::consume_resources_of_cluster(Resource costs) +Resource Grid::consume_resources_of_cluster(Cluster *cluster, Resource costs) { - Cluster cluster = this->get_cluster(); - Resource cluster_resources = this->get_resources_of_cluster(cluster); - for (FieldMeta *meta : cluster) + for (FieldMeta *meta : *cluster) { // mind the "special" definition of -=, only byte of what you can chew or leave nothing behind Resource tmp = costs; - costs -= meta->resources; - meta->resources -= tmp; + costs -= meta->get_resources(); + meta->consume_resources(tmp); } + delete cluster; return costs; // > {0, 0, 0} means there were not enough resources } -void FieldMeta::set_owner(Player *player) +bool Player::fight(FieldMeta *field) { - this->owner = player; -} - -FieldMeta *FieldMeta::get_meta(Field field) -{ - auto pair = fields.find(field); - if (pair != fields.end()) - { - return pair->second; - } - return nullptr; // no meta-information (there is no field on the map at this location) -} - -bool Player::fight(Field field) -{ - FieldMeta *meta = FieldMeta::get_meta(field); - if (meta == nullptr || *this == *(meta->get_owner())) // attacked field outside of the map or friendly fire + if (*this == *(field->get_owner())) // attacked field outside of the map or friendly fire { return false; } - if (*(meta->get_owner()) == this->default_player) // still to be had + if (field->get_owner() == field->get_grid()->get_default_player()) // still to be had { - meta->set_owner(this); + field->set_owner(this); return true; } // defending player's defense against attacking player's offense - int power_level = meta->get_defense(); // it's over 9000 - Field center = meta->get_field(); + int power_level = field->get_defense(); // it's over 9000 for (Uint8 i = 0; i < 6; i++) { - Field neighbor = hex_neighbor(i, center); - FieldMeta *neighbor_meta = FieldMeta::get_meta(neighbor); - if (neighbor_meta == nullptr) // there is no neighbor in this direction + FieldMeta *neighbor = field->get_neighbor(i); + if (neighbor == nullptr) // there is no neighbor in this direction { continue; } - if (*(neighbor_meta->get_owner()) == *this) // comparison by UUID - power_level -= neighbor_meta->get_offense(); - else if (*(neighbor_meta->get_owner()) == *(meta->get_owner())) - power_level += neighbor_meta->get_defense(); + if (*(neighbor->get_owner()) == *this) // comparison by UUID + power_level -= neighbor->get_offense(); + else if (*(neighbor->get_owner()) == *(field->get_owner())) + power_level += neighbor->get_defense(); // else ignore, field / player not part of the fight (e.g. default player) } if (power_level < 0) // attacking player has won { - meta->set_owner(this); + field->set_owner(this); return true; } return false; } -int FieldMeta::get_offense() +void FieldMeta::handle_event(const SDL_Event *event) { - return this->offense; + this->regenerate_resources(); } -int FieldMeta::get_defense() +bool HexagonGrid::render(SDL_Renderer *renderer) { - return this->defense; + Field some_field = {0, 0, 0}; + std::vector polygon = some_field.field_to_polygon_normalized(this->layout); + assert(polygon.size() > 5); + Sint16 x[6]; + Sint16 y[6]; + for (std::pair const &elem : this->fields) + { + Field field = elem.first; + Point center = field.field_to_point(this->layout); + for (uint8_t i = 0; i < 6; i++) + { + x[i] = (Sint16) (center.x + polygon[i].x); + y[i] = (Sint16) (center.y + polygon[i].y); + } + const SDL_Color color = {0xff, 0xff, 0xff, 0xff}; + polygonRGBA(renderer, x, y, 6, color.r, color.g, color.b, color.a); + if (elem.first == this->marker->get_field()) + { + filledPolygonRGBA(renderer, x, y, 6, 0x77, 0x77, 0x77, 0x77); + } + elem.second->render(renderer, this->layout); + } + return true; +} + +void Grid::handle_event(SDL_Event *event) +{ + if (event->type == SDL_MOUSEWHEEL) + { + SDL_Point mouse = {0, 0}; + SDL_GetMouseState(&mouse.x, &mouse.y); + int scroll = this->layout->size / 10 * event->wheel.y; + double old_size = this->layout->size; + SDL_Point old_origin = this->layout->origin; + if (old_size + scroll < 10) + { + this->layout->size = 10; + } + else if (old_size + scroll > 100) + { + this->layout->size = 100; + } + else + { + this->layout->size += scroll; + } + this->move(((1.0 - (double) this->layout->size / old_size) * (mouse - old_origin))); + } + if (event->type == SDL_MOUSEMOTION) + { + if (this->panning) + { + SDL_Point mouse = {0, 0}; + SDL_GetMouseState(&mouse.x, &mouse.y); + Point marker_pos = this->marker->get_field().field_to_point(this->layout); + SDL_Point p; + p.x = (int) marker_pos.x; + p.y = (int) marker_pos.y; + this->move(mouse - p); + } + this->update_marker(); + } + if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) + { + this->panning = !(this->panning); + } +} + +void Grid::move(SDL_Point m) +{ + this->layout->origin = this->layout->origin + m; + // check if some part is inside layout->box + if (!on_rectangle(&layout->box)) + this->layout->origin = this->layout->origin - m; + this->update_marker(); +} + +void Grid::update_marker() +{ + SDL_Point m = {0, 0}; + SDL_GetMouseState(&(m.x), &(m.y)); + Point p = {0.0, 0.0}; + p.x = m.x; + p.y = m.y; + FieldMeta *n_marker = this->point_to_field(p); + if (n_marker != nullptr) + this->marker = n_marker; +} + +FieldMeta *Grid::point_to_field(const Point p) +{ + Field field = p.point_to_field(this->layout); + FieldMeta *meta = nullptr; + try + { + meta = this->fields.at(field); + } + catch (const std::out_of_range &oor) + { + //std::cerr << "Tried to access non-existant field " << field << ": " << oor.what() << std::endl; + } + return meta; + +} + +bool Grid::on_rectangle(SDL_Rect *rect) +{ + // check if center inside rect for ANY field + for (const auto &pair : this->fields) + { + Point precise_p = pair.first.field_to_point(layout); + SDL_Point p; + p.x = (int) precise_p.x; + p.y = (int) precise_p.y; + if (p.x > rect->x && p.y > rect->y && p.x < (rect->x + rect->w) && p.y < (rect->y + rect->h)) + { + return true; + } + } + return false; +} + +void Grid::update_box(SDL_Point dimensions) +{ + this->layout->box.w = dimensions.x; + this->layout->box.h = dimensions.y; +} + +std::ostream &operator<<(std::ostream &os, const Field &rhs) +{ + os << "(" << rhs.x << "," << rhs.y << ","; + return os; } diff --git a/src/Gameplay.hpp b/src/Gameplay.hpp index b547827..38b80a4 100644 --- a/src/Gameplay.hpp +++ b/src/Gameplay.hpp @@ -7,106 +7,16 @@ #include #include #include +#include #include "Grid.hpp" +#include +#ifndef _GAMEPLAY_H +#define _GAMEPLAY_H -#ifndef Upgrade -typedef enum -{ - UPGRADE_FIRST_UPGRADE = 0, - UPGRADE_REGENERATION_1, - UPGRADE_REGENERATION_2, - UPGRADE_REGENERATION_3, - UPGRADE_REPRODUCTION -} Upgrade; -#endif +#define NUM_UPGRADES 10 -#ifndef Tagged - -class Tagged -{ -private: - boost::uuids::uuid tag; - - int state; -public: - // construct new - Tagged() - : tag(boost::uuids::random_generator()()), state(0) { } - - // construct with state - explicit Tagged(int state) - : tag(boost::uuids::random_generator()()), state(state) { } - - // clone - Tagged(Tagged const &rhs) - : tag(rhs.tag), state(rhs.state) { } - - bool operator==(Tagged const &rhs) const - { - return tag == rhs.tag; - } - - bool operator!=(Tagged const &rhs) const - { - return !(tag == rhs.tag); - } - - int get_state() const { return state; } - - void set_state(int new_state) { state = new_state; } - - boost::uuids::uuid get_tag() { return tag; } - - void set_tag(boost::uuids::uuid uuid_) { tag = uuid_; } -}; - -#endif - -#ifndef Player - -class Player : public Tagged -{ -private: - SDL_Color color; - std::string name; - -public: - Player(std::string name_) - : name(name_), Tagged() - { - // use the last 24 bits of the tag for the color - boost::uuids::uuid id = get_tag(); - uint8_t *data = id.data; - this->color = {data[13], data[14], data[15], 0xff}; - } - - Player(std::string name_, boost::uuids::uuid tag_) - : Tagged(), name(name_) - { - this->set_tag(tag_); - } - - SDL_Color get_color() { return color; } - - std::string get_name() - { - std::ostringstream descriptor; - boost::uuids::uuid id = get_tag(); - uint8_t *data = id.data; - Uint16 number = (data[14] << 8) | (data[15]); - descriptor << this->name << " (" << std::hex << number << ")"; - return descriptor.str(); - } - - bool fight(Field field); - - static Player default_player; -}; - -#endif - -#ifndef Resource +typedef std::bitset UpgradeFlags; struct Resource { @@ -206,15 +116,124 @@ struct Resource inline bool operator!=(const Resource &rhs) const { return !(*this == rhs); } }; -#endif +enum Upgrade +{ + First_Upgrade = 0, + Regeneration_1, + Regeneration_2, + Regeneration_3, + Reproduction_1, + Reproduction_2, + Reproduction_3 +}; +namespace std +{ + template<> + struct hash + { + size_t operator()(const Upgrade &f) const + { + int i = static_cast(f); + hash int_hash; + return int_hash(f); + } + }; +} -#ifndef FieldMeta +const std::unordered_map UPGRADE_COSTS( + { + {Regeneration_1, {4, 4, 4}}, + {Regeneration_2, {8, 8, 8}}, + {Regeneration_3, {16, 16, 16}}, + {Reproduction_1, {4, 4, 4}}, + {Reproduction_2, {8, 8, 8}}, + {Reproduction_3, {16, 16, 16}} + } +); + +class Tagged +{ +private: + boost::uuids::uuid tag; + + int state; +public: + // construct new + Tagged() + : tag(boost::uuids::random_generator()()), state(0) { } + + // construct with state + explicit Tagged(int state) + : tag(boost::uuids::random_generator()()), state(state) { } + + // clone + Tagged(Tagged const &rhs) + : tag(rhs.tag), state(rhs.state) { } + + bool operator==(Tagged const &rhs) const + { + return tag == rhs.tag; + } + + bool operator!=(Tagged const &rhs) const + { + return !(tag == rhs.tag); + } + + int get_state() const { return state; } + + void set_state(int new_state) { state = new_state; } + + boost::uuids::uuid get_tag() { return tag; } + + void set_tag(boost::uuids::uuid uuid_) { tag = uuid_; } +}; + +class Player; + +class FieldMeta; + +class Player : public Tagged +{ + +public: + Player(std::string name_) + : name(name_), Tagged() + { + // use the last 24 bits of the tag for the color + boost::uuids::uuid id = get_tag(); + uint8_t *data = id.data; + this->color = {data[13], data[14], data[15], 0xff}; + } + + SDL_Color get_color() { return color; } + + std::string get_name() + { + std::ostringstream descriptor; + boost::uuids::uuid id = get_tag(); + uint8_t *data = id.data; + Uint16 number = (data[14] << 8) | (data[15]); + descriptor << this->name << " (" << std::hex << number << ")"; + return descriptor.str(); + } + + bool fight(FieldMeta *field); + +private: + SDL_Color color; + std::string name; +}; + +class Grid; + +class HexagonGrid; class FieldMeta { public: - FieldMeta(Field field_, Player *owner_ = &(Player::default_player)) + FieldMeta(Field field_, Player *owner_) : field(field_), owner(owner_) { this->upgrades = 0; @@ -224,59 +243,135 @@ public: this->resources_base.circle = distro(rng); this->resources_base.triangle = distro(rng); this->resources_base.square = distro(rng); - std::pair pair(field, this); - this->fields.insert(pair); this->offense = 1; this->defense = 1; } - int get_offense(); + HexagonGrid *get_grid() { return this->grid; } - int get_defense(); + int get_offense() { return this->offense; } - static FieldMeta *get_meta(Field field); + int get_defense() { return this->defense; } - Field get_field(); + Field get_field() { return this->field; } - Player *get_owner(); + Player *get_owner() { return this->owner; } - void set_owner(Player *player); - - //void set_owner(Player *player); + void set_owner(Player *player) { this->owner = player; } void render(SDL_Renderer *renderer, Layout *layout); - Resource get_resources(); + Resource get_resources() { return this->resources; } - Resource get_resources_of_cluster(); + UpgradeFlags get_upgrades() { return this->upgrades; } - Resource get_resources_of_cluster(std::unordered_set cluster); - - std::unordered_set get_cluster( - std::unordered_set visited = std::unordered_set()); - - Uint32 get_upgrades(); + void consume_resources(Resource costs) { this->resources -= costs; } void regenerate_resources(); bool upgrade(Upgrade upgrade); - Resource consume_resources_of_cluster(Resource costs); + void handle_event(const SDL_Event *event); + + FieldMeta *get_neighbor(Uint8 direction); private: const Field field; - static std::unordered_map fields; + HexagonGrid *grid; Player *owner; - Uint32 upgrades; + UpgradeFlags upgrades; Resource resources_base; // without upgrades applied, used as basis of regeneration Resource resources; // actual current resources int offense; int defense; }; -#endif - -#ifndef Cluster typedef std::unordered_set Cluster; -#endif +class Grid +{ + +public: + Grid(Layout *layout_) + : layout(layout_) + { + Field f = {0, 0, 0}; + std::unordered_map fields = std::unordered_map(); + this->default_player = new Player("Default"); + this->marker = new FieldMeta(f, default_player); + }; + + ~Grid() + { + for (auto const &elem : this->fields) + { + delete elem.second; + } + } + + Player *get_default_player() { return this->default_player; } + + void move(SDL_Point move); + + void update_marker(); + + virtual bool render(SDL_Renderer *renderer) { return false; }; + + void handle_event(SDL_Event *event); + + void update_box(SDL_Point dimensions); + + Resource get_resources_of_cluster(const Cluster *cluster); + + virtual FieldMeta *get_neighbor(FieldMeta *field, Uint8 direction) = 0; + + virtual Cluster *get_cluster(FieldMeta *field, Cluster *visited = nullptr) = 0; + + Resource consume_resources_of_cluster(Cluster *cluster, Resource costs); + + FieldMeta *point_to_field(const Point p); + +protected: + Player *default_player; + std::unordered_map fields; + Layout *layout; + FieldMeta *marker; + bool panning; + + bool on_rectangle(SDL_Rect *rect); +}; + +class HexagonGrid : public Grid +{ +public: + HexagonGrid(Sint16 grid_radius, Layout *layout) + : Grid(layout), radius(grid_radius) + { + // first lower half, then upper half + for (Sint16 x = -grid_radius; x <= grid_radius; x++) + { + Sint16 y_l = (-grid_radius > -x - grid_radius) ? -grid_radius : -x - grid_radius; + Sint16 y_u = (grid_radius < -x + grid_radius) ? grid_radius : -x + grid_radius; + for (Sint16 y = y_l; y <= y_u; y++) + { + Sint16 z = -x - y; + Field new_field = {x, y, z}; + FieldMeta *meta = new FieldMeta(new_field, default_player); + this->fields.insert({new_field, meta}); + } + } + } + + FieldMeta *get_neighbor(FieldMeta *field, Uint8 direction); + + Cluster *get_cluster(FieldMeta *field, Cluster *visited = nullptr); + + bool render(SDL_Renderer *renderer); + + Sint16 get_radius() { return radius * layout->size; } + +private: + Sint16 radius; +}; + +#endif diff --git a/src/Grid.cpp b/src/Grid.cpp index b2191b0..0219323 100644 --- a/src/Grid.cpp +++ b/src/Grid.cpp @@ -43,82 +43,7 @@ double operator!(SDL_Point left) return length; } -Point operator+(Point left, Point right) -{ - return Point(left.x + right.x, left.y + right.y); -} - -Point operator-(Point left, Point right) -{ - return Point(left.x - right.x, left.y - right.y); -} - -Point operator*(Point left, Point right) -{ - return Point(left.x * right.x, left.y * right.y); -} - -Point operator/(Point left, Point right) -{ - return Point(left.x / right.x, left.y / right.y); -} - -Point operator*(double left, Point right) -{ - return Point(left * right.x, left * right.y); -} - -Point operator*(Point left, double right) -{ - return Point(left.x * right, left.y * right); -} - -Point operator/(double left, Point right) -{ - return Point(left * right.x, left * right.y); -} - -Point operator/(Point left, double right) -{ - return Point(left.x * right, left.y * right); -} - -double operator!(Point left) -{ - return std::sqrt(left.x * left.x + left.y * left.y); -} - -bool operator==(Field left, Field right) -{ - return left.x == right.x && left.y == right.y; -} - -bool operator!=(Field left, Field right) -{ - return left == right; -} - -Field operator+(Field left, Field right) -{ - return Field(left.x + right.x, left.y + right.y, left.z + right.z); -} - -Field operator-(Field left, Field right) -{ - return Field(left.x - right.x, left.y - right.y, left.z - right.z); -} - -Field operator*(Field left, Field right) -{ - return Field(left.x * right.x, left.y * right.y, left.z * right.z); -} - -Field operator/(Field left, Field right) -{ - return cubic_round(left.x / right.x, left.y / right.y, right.z / left.z); -} - -Field cubic_round(double x, double y, double z) +Field Field::cubic_round(double x, double y, double z) { Sint8 round_x = (Sint8) std::round(x); Sint8 round_y = (Sint8) std::round(y); @@ -139,38 +64,34 @@ Field cubic_round(double x, double y, double z) return Field(round_x, round_y, round_z); } -int cubic_distance(Field a, Field b) -{ - return (abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z)) / 2; -} -Field hex_direction(Uint8 direction) +Field Field::hex_direction(Uint8 direction) { assert (0 <= direction && direction <= 5); return hex_directions[direction]; } -Field hex_neighbor(Uint8 direction, Field f) +Field Field::get_neighbor(Uint8 direction) const { - return hex_direction(direction) + f; + return hex_direction(direction) + *this; } -Point field_to_point(const Field f, const Layout *layout) +Point Field::field_to_point(const Layout *layout) const { const Orientation m = layout->orientation; - double x = (m.f0 * f.x + m.f1 * f.y) * layout->size; - double y = (m.f2 * f.x + m.f3 * f.y) * layout->size; + double x = (m.f0 * this->x + m.f1 * this->y) * layout->size; + double y = (m.f2 * this->x + m.f3 * this->y) * layout->size; return {x + layout->origin.x, y + layout->origin.y}; } -Field point_to_field(Point point, const Layout *layout) +Field Point::point_to_field(const Layout *layout) const { const Orientation m = layout->orientation; - double rel_x = (point.x - layout->origin.x) / layout->size; - double rel_y = (point.y - layout->origin.y) / layout->size; + double rel_x = (this->x - layout->origin.x) / layout->size; + double rel_y = (this->y - layout->origin.y) / layout->size; double x = m.b0 * rel_x + m.b1 * rel_y; double y = m.b2 * rel_x + m.b3 * rel_y; - return cubic_round(x, y, -x - y); + return Field::cubic_round(x, y, -x - y); } Point field_corner_offset(Uint8 corner, const Layout *layout) @@ -181,10 +102,10 @@ Point field_corner_offset(Uint8 corner, const Layout *layout) return {x, y}; } -std::vector field_to_polygon(const Field field, const Layout *layout) +std::vector Field::field_to_polygon(const Layout *layout) const { - std::vector corners = field_to_polygon_normalized(field, layout); - Point center = field_to_point(field, layout); + std::vector corners = this->field_to_polygon_normalized(layout); + Point center = this->field_to_point(layout); for (Point &p : corners) { p = p + center; @@ -192,7 +113,7 @@ std::vector field_to_polygon(const Field field, const Layout *layout) return corners; } -std::vector field_to_polygon_normalized(const Field field, const Layout *layout) +std::vector Field::field_to_polygon_normalized(const Layout *layout) const { std::vector corners; for (uint8_t i = 0; i < 6; i++) @@ -202,131 +123,3 @@ std::vector field_to_polygon_normalized(const Field field, const Layout * } return corners; } - -HexagonGrid::HexagonGrid(Sint16 grid_radius, Layout *layout) - : Grid(layout), radius(grid_radius) -{ - // first lower half, then upper half - for (Sint16 x = -grid_radius; x <= grid_radius; x++) - { - Sint16 y_l = (-grid_radius > -x - grid_radius) ? -grid_radius : -x - grid_radius; - Sint16 y_u = (grid_radius < -x + grid_radius) ? grid_radius : -x + grid_radius; - for (Sint16 y = y_l; y <= y_u; y++) - { - Sint16 z = -x - y; - Field new_field = {x, y, z}; - this->fields->insert(new_field); - } - } -} - -bool HexagonGrid::render(SDL_Renderer *renderer) -{ - Field some_field = {0, 0, 0}; - std::vector polygon = field_to_polygon_normalized(some_field, this->layout); - assert(polygon.size() > 5); - Sint16 x[6]; - Sint16 y[6]; - for (const Field &elem : *(this->fields)) - { - Point center = field_to_point(elem, this->layout); - for (uint8_t i = 0; i < 6; i++) - { - x[i] = (Sint16) (center.x + polygon[i].x); - y[i] = (Sint16) (center.y + polygon[i].y); - } - const SDL_Color color = {0xff, 0xff, 0xff, 0xff}; - polygonRGBA(renderer, x, y, 6, color.r, color.g, color.b, color.a); - if (elem == this->marker) - { - filledPolygonRGBA(renderer, x, y, 6, 0x77, 0x77, 0x77, 0x77); - } - } - return true; -} - -void Grid::handle_event(SDL_Event *event) -{ - if (event->type == SDL_MOUSEWHEEL) - { - SDL_Point mouse = {0, 0}; - SDL_GetMouseState(&mouse.x, &mouse.y); - int scroll = this->layout->size / 10 * event->wheel.y; - double old_size = this->layout->size; - SDL_Point old_origin = this->layout->origin; - if (old_size + scroll < 10) - { - this->layout->size = 10; - } - else if (old_size + scroll > 100) - { - this->layout->size = 100; - } - else - { - this->layout->size += scroll; - - } - this->move(((1.0 - (double) this->layout->size / old_size) * (mouse - old_origin))); - } - if (event->type == SDL_MOUSEMOTION) - { - if (this->panning) - { - SDL_Point mouse = {0, 0}; - SDL_GetMouseState(&mouse.x, &mouse.y); - Point marker_pos = field_to_point(this->marker, this->layout); - SDL_Point p; - p.x = (int) marker_pos.x; - p.y = (int) marker_pos.y; - this->move(mouse - p); - } - this->update_marker(); - } - if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) - { - this->panning = !(this->panning); - } -} - -void Grid::move(SDL_Point m) -{ - this->layout->origin = this->layout->origin + m; - // check if some part is inside layout->box - if (!on_rectangle(&layout->box)) - this->layout->origin = this->layout->origin - m; - this->update_marker(); -} - -void Grid::update_marker() -{ - SDL_Point m = {0, 0}; - SDL_GetMouseState(&(m.x), &(m.y)); - Point p = {0.0, 0.0}; - p.x = m.x; - p.y = m.y; - this->marker = point_to_field(p, this->layout); -} - -bool Grid::on_rectangle(SDL_Rect *rect) -{ - // check if center inside rect for ANY field - for (const Field &f : *(this->fields)) - { - Point precise_p = field_to_point(f, layout); - SDL_Point p; - p.x = (int) precise_p.x; - p.y = (int) precise_p.y; - if (p.x > rect->x && p.y > rect->y && p.x < (rect->x + rect->w) && p.y < (rect->y + rect->h)) - { - return true; - } - } - return false; -} - -void Grid::update_box(int x, int y) -{ - this->layout->box.w = x; - this->layout->box.h = y; -} \ No newline at end of file diff --git a/src/Grid.hpp b/src/Grid.hpp index 44de88d..1036f1c 100644 --- a/src/Grid.hpp +++ b/src/Grid.hpp @@ -7,8 +7,9 @@ #include #include -#ifndef POINT_OPERATORS -#define POINT_OPERATORS +#ifndef _GRID_H +#define _GRID_H + SDL_Point operator+(SDL_Point left, SDL_Point right); SDL_Point operator-(SDL_Point left, SDL_Point right); @@ -27,39 +28,6 @@ SDL_Point operator/(SDL_Point left, double right); double operator!(SDL_Point left); -#endif - -#ifndef Point -struct Point -{ - double x; - double y; - - Point(double x_, double y_) : x(x_), y(y_) - { } -}; - -#endif - -Point operator+(Point left, Point right); - -Point operator-(Point left, Point right); - -Point operator*(Point left, Point right); - -Point operator/(Point left, Point right); - -Point operator*(double left, Point right); - -Point operator*(Point left, double right); - -Point operator/(double left, Point right); - -Point operator/(Point left, double right); - -double operator!(Point left); - -#ifndef Orientation struct Orientation { // cubic to point @@ -76,31 +44,128 @@ struct Orientation { } }; -const Orientation pointy_orientation = Orientation(sqrt(3.0), sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, - sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, - 0.5); -const Orientation flat_orientation = Orientation(3.0 / 2.0, 0.0, sqrt(3.0) / 2.0, sqrt(3.0), - 2.0 / 3.0, 0.0, -1.0 / 3.0, sqrt(3.0) / 3.0, - 0); -#endif - -#ifndef Layout +const Orientation pointy_orientation = Orientation(sqrt(3.0), sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, sqrt(3.0) / 3.0, + -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5); +const Orientation flat_orientation = Orientation(3.0 / 2.0, 0.0, sqrt(3.0) / 2.0, sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, + sqrt(3.0) / 3.0, 0); struct Layout { const Orientation orientation; - Uint16 size; + Sint16 size; SDL_Point origin; SDL_Rect box; - Layout(Orientation orientation_, Uint16 size_, SDL_Point origin_, SDL_Rect box_) + Layout(Orientation orientation_, Sint16 size_, SDL_Point origin_, SDL_Rect box_) : orientation(orientation_), size(size_), origin(origin_), box(box_) { } }; -#endif +struct Field; + +struct Point +{ + double x; + double y; + + Point(double x_, double y_) : x(x_), y(y_) { } + + bool operator==(const Point &rhs) const + { + return (this->x == rhs.x && this->y == rhs.y); + } + + inline bool operator!=(const Point &rhs) const + { + return !(*this == rhs); + } + + Point &operator+=(const Point &rhs) + { + this->x += rhs.x; + this->y += rhs.y; + return *this; + } + + friend Point operator+(Point lhs, const Point &rhs) + { + lhs += rhs; + return lhs; + } + + Point &operator-=(const Point &rhs) + { + this->x -= rhs.x; + this->y -= rhs.y; + return *this; + } + + friend Point operator-(Point lhs, const Point &rhs) + { + lhs -= rhs; + return rhs; + } + + Point &operator*=(const Point &rhs) + { + this->x *= rhs.x; + this->y *= rhs.y; + return *this; + } + + friend Point operator*(Point lhs, const Point &rhs) + { + lhs *= rhs; + return rhs; + } + + Point &operator/=(const Point &rhs) + { + this->x /= rhs.x; + this->y /= rhs.y; + return *this; + } + + friend Point operator/(Point lhs, const Point &rhs) + { + lhs /= rhs; + return lhs; + } + + Point &operator*=(const double &rhs) + { + this->x *= rhs; + this->y *= rhs; + return *this; + } + + friend Point operator*(Point lhs, const double &rhs) + { + lhs *= rhs; + return lhs; + } + + Point &operator/=(const double &rhs) + { + this->x /= rhs; + this->y /= rhs; + return *this; + } + + friend Point operator/(Point lhs, const double &rhs) + { + lhs /= rhs; + return lhs; + } + + double operator!() + { + return std::sqrt(this->x * this->x + this->y * this->y); + } + + Field point_to_field(const Layout *layout) const; +}; -#ifndef Field struct Field { Sint16 x, y, z; @@ -109,14 +174,132 @@ struct Field { assert(x + y + z == 0); } + + bool operator==(const Field &rhs) const + { + return (this->x == rhs.x && this->y == rhs.y); + } + + inline bool operator!=(const Field &rhs) const + { + return !(*this == rhs); + } + + Field &operator+=(const Field &rhs) + { + this->x += rhs.x; + this->y += rhs.y; + this->z += rhs.z; + return *this; + } + + friend Field operator+(Field lhs, const Field &rhs) + { + lhs += rhs; + return lhs; + } + + Field &operator-=(const Field &rhs) + { + this->x -= rhs.x; + this->y -= rhs.y; + this->z -= rhs.z; + return *this; + } + + friend Field operator-(Field lhs, const Field &rhs) + { + lhs -= rhs; + return rhs; + } + + Field &operator*=(const Field &rhs) + { + this->x *= rhs.x; + this->y *= rhs.y; + this->z *= rhs.z; + return *this; + } + + friend Field operator*(Field lhs, const Field &rhs) + { + lhs *= rhs; + return rhs; + } + + Field &operator/=(const Field &rhs) + { + double x = this->x / rhs.x; + double y = this->y / rhs.y; + double z = this->z / rhs.z; + Field f = cubic_round(x, y, z); + *this = f; + return *this; + } + + friend Field operator/(Field lhs, const Field &rhs) + { + lhs /= rhs; + return lhs; + } + + Field &operator*=(const double &rhs) + { + double x = this->x * rhs; + double y = this->y * rhs; + double z = this->z * rhs; + Field f = cubic_round(x, y, z); + *this = f; + return *this; + } + + friend Field operator*(Field lhs, const double &rhs) + { + lhs *= rhs; + return lhs; + } + + Field &operator/=(const double &rhs) + { + double x = this->x / rhs; + double y = this->y / rhs; + double z = this->z / rhs; + Field f = cubic_round(x, y, z); + *this = f; + return *this; + } + + friend Field operator/(Field lhs, const double &rhs) + { + lhs /= rhs; + return lhs; + } + + int operator&(const Field &rhs) + { + return (abs(this->x - rhs.x) + abs(this->y - rhs.y) + abs(this->z - rhs.z)) / 2; + } + + friend std::ostream &operator<<(std::ostream &os, const Field &rhs); + + Field get_neighbor(Uint8 direction) const; + + Point field_to_point(const Layout *layout) const; + + std::vector field_to_polygon_normalized(const Layout *layout) const; + + std::vector field_to_polygon(const Layout *layout) const; + + static Field cubic_round(double x, double y, double z); + + static Field hex_direction(Uint8 direction); }; // from upper right corner -const std::vector hex_directions = { - Field(1, 0, -1), Field(0, 1, -1), Field(-1, 1, 0), Field(-1, 0, 1), Field(-1, 1, 0) -}; +const std::vector hex_directions = {Field(1, 0, -1), Field(0, 1, -1), Field(-1, 1, 0), Field(-1, 0, 1), + Field(-1, 1, 0)}; -Field hex_direction(Uint8 direction); +Point field_corner_offset(Uint8 corner, const Layout *layout); namespace std { @@ -135,84 +318,4 @@ namespace std }; } -Field hex_neighbor(Uint8 direction, Field f); - -Field cubic_round(double x, double y, double z); - -int cubic_distance(Field a, Field b); - -bool operator==(Field left, Field right); - -bool operator!=(Field left, Field right); - -Field operator+(Field left, Field right); - -Field operator-(Field left, Field right); - -Field operator*(Field left, Field right); - -Field operator/(Field left, Field right); -#endif - -Point field_to_point(const Field f, const Layout *layout); - -Field point_to_field(Point point, const Layout *layout); - -Point field_corner_offset(Uint8 corner, const Layout *layout); - -std::vector field_to_polygon_normalized(const Field field, const Layout *layout); - -std::vector field_to_polygon(const Field field, const Layout *layout); - - -#ifndef Grid - -class Grid -{ -protected: - std::unordered_set *fields; - Layout *layout; - Field marker; - bool panning; - bool on_rectangle(SDL_Rect *rect); -public: - Grid(Layout *layout_) - : layout(layout_), marker(0, 0, 0) - { - this->fields = new std::unordered_set(); - }; - - ~Grid() - { - delete this->fields; - } - - std::unordered_set *get_fields() { return this->fields; } - - void move(SDL_Point move); - - void update_marker(); - - virtual bool render(SDL_Renderer *renderer) = 0; - - void handle_event(SDL_Event *event); - - void update_box(int x, int y); -}; - -#endif - -#ifndef HexagonGrid -class HexagonGrid : public Grid -{ -private: - Sint16 radius; -public: - HexagonGrid(Sint16 grid_radius, Layout *layout); - - bool render(SDL_Renderer *renderer); - - Sint16 get_radius() { return radius * layout->size; } -}; - #endif diff --git a/src/Gui.cpp b/src/Gui.cpp new file mode 100644 index 0000000..a403c75 --- /dev/null +++ b/src/Gui.cpp @@ -0,0 +1,174 @@ +#include "Gui.hpp" + +TTF_Font *load_font_from_file(std::string path_to_file) +{ + TTF_Font *font = TTF_OpenFont(path_to_file.c_str(), 12); // what about memory leaks? + if (font == nullptr) + { + std::cerr << "Failed to load TTF!" << TTF_GetError() << std::endl; + return font; + } + return font; +} + +bool TextInfoBox::load_text(std::string text) +{ + SDL_DestroyTexture(this->texture); + SDL_Surface *surface = TTF_RenderUTF8_Solid(this->font, text.c_str(), color); + if (surface == nullptr) + { + std::cerr << "Unable to render text to surface! " << TTF_GetError() << std::endl; + } + else if (surface->w > this->dimensions.w || surface->h > this->dimensions.h) + { + std::cerr << "Overfull TextBox!" << SDL_GetError() << std::endl; + } + else + { + this->create_texture_from_surface(surface); + } + SDL_FreeSurface(surface); + return (this->texture != nullptr); +} + + +bool InfoBox::create_texture_from_surface(SDL_Surface *surface) +{ + SDL_SetRenderTarget(this->renderer->get_renderer(), this->texture); + this->texture = SDL_CreateTextureFromSurface(this->renderer->get_renderer(), surface); + if (this->texture == nullptr) + { + std::cerr << "Unable to render texture from surface!" << SDL_GetError() << std::endl; + } + SDL_SetRenderTarget(this->renderer->get_renderer(), nullptr); // reset the render target + return (this->texture != nullptr); +} + +bool InfoBox::render(SDL_Renderer *renderer, const SDL_Rect target) +{ + if (this->visible) + { + if (!SDL_RenderCopy(renderer, this->texture, &(this->dimensions), &target)) + { + std::cerr << "Failed to render TextBox Texture!" << SDL_GetError() << std::endl; + return false; + } + } + return true; +} + +void logSDLError(std::ostream &os, const std::string &msg) +{ + os << msg << " error:" << SDL_GetError() << std::endl; +} + +SDL_Point Window::toggle_fullscreen() +{ + SDL_DisplayMode dm; + SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(this->window), &dm); + if (!this->fullscreen) + { + this->fullscreen = true; + SDL_SetWindowSize(this->window, dm.w, dm.h); + SDL_SetWindowFullscreen(this->window, SDL_WINDOW_FULLSCREEN); + } + else + { + this->fullscreen = false; + SDL_SetWindowFullscreen(this->window, 0); + SDL_SetWindowSize(this->window, this->initial_dimensions->w, this->initial_dimensions->h); + SDL_SetWindowPosition(this->window, this->initial_dimensions->x, this->initial_dimensions->y); + } + SDL_Point window_size = {0, 0}; + SDL_GetWindowSize(window, &(window_size.x), &(window_size.y)); + return window_size; +} + +bool Window::position_inside_window(SDL_Point position) +{ + SDL_DisplayMode dm; + SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(this->window), &dm); + return position.x > 0 && position.x < dm.w && position.y > 0 && position.y < dm.h; +} + +int Window::get_window_id() +{ + return SDL_GetWindowID(this->window); +} + +void Renderer::set_draw_color(SDL_Color color) +{ + SDL_SetRenderDrawColor(this->renderer, color.r, color.g, color.b, color.a); +} + +void Renderer::clear() +{ + SDL_RenderClear(this->renderer); +} + +void Renderer::present() +{ + SDL_RenderPresent(this->renderer); +} + +void TextInfoBox::handle_event(const SDL_Event *event) +{ +} + +void SideBar::handle_event(const SDL_Event *event) +{ + std::ostringstream output; + Cluster *cluster = this->field->get_grid()->get_cluster(this->field); + Resource cluster_resources = this->field->get_grid()->get_resources_of_cluster(cluster); + Resource field_resources = this->field->get_resources(); + MarkerUpdate *update = (MarkerUpdate *) event->user.data1; +/* switch (event->type) + { + case (BOB_FIELD_UPDATE_EVENT): + output << this->field->get_owner()->get_name() << std::endl; + output << "● " << cluster_resources.circle << " (" << field_resources.circle << ")" << std::endl; + output << "▲ " << cluster_resources.triangle << " (" << field_resources.triangle << ")" << std::endl; + output << "■ " << cluster_resources.square << " (" << field_resources.square << ")" << std::endl; + this->field_info->load_text(output.str()); + break; + case (BOB_UPDATE_MARKER_EVENT): + this->field = update->field; + break; + default: + break; + }*/ +} + +void FieldInfoBox::handle_event(const SDL_Event *event) +{ +} + +void ButtonInfoBox::handle_event(const SDL_Event *event) +{ + switch (event->type) + { + case (SDL_MOUSEBUTTONDOWN): + this->upgrade_box->set_visible(!(this->upgrade_box->get_visible())); + break; + default: + break; + } +}; + +void UpgradeInfoBox::handle_event(const SDL_Event *event) +{ + TextInfoBox::handle_event(event); +} + +void SideBar::render(SDL_Renderer *renderer) +{ + SDL_Rect f_dimensions = this->field_info->get_dimensions(); + this->field_info->render(renderer, {this->dimensions.x, this->dimensions.y, f_dimensions.w, f_dimensions.h}); + for (auto &elem : *upgrades_list) + { + f_dimensions = elem->get_dimensions(); + elem->render(renderer, {this->dimensions.x, this->dimensions.y, f_dimensions.w, f_dimensions.h}); + } + f_dimensions = this->upgrade_info->get_dimensions(); + this->upgrade_info->render(renderer, {this->dimensions.x, this->dimensions.y, f_dimensions.w, f_dimensions.h}); +} \ No newline at end of file diff --git a/src/Gui.hpp b/src/Gui.hpp new file mode 100644 index 0000000..d56445b --- /dev/null +++ b/src/Gui.hpp @@ -0,0 +1,194 @@ +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" +#include +#include +#include +#include +#include +#include +#include "Exceptions.hpp" +#include "Gameplay.hpp" +#include "Events.hpp" + +#ifndef _GUI_H +#define _GUI_H + +void logSDLError(std::ostream &os, const std::string &msg); + +class Window +{ +private: + SDL_Window *window; + const SDL_Rect *initial_dimensions; + bool fullscreen; +public: + Window(std::string title, SDL_Rect *dimensions, Uint32 flags) + { + this->window = SDL_CreateWindow(title.c_str(), dimensions->x, dimensions->y, dimensions->w, dimensions->h, + flags); + if (this->window == nullptr) + { + SDL_DestroyWindow(this->window); + throw SDL_WindowException(); + } + this->initial_dimensions = dimensions; + } + + ~Window() + { + SDL_DestroyWindow(this->window); + } + + SDL_Window *get_window() { return this->window; } + + SDL_Point toggle_fullscreen(); + + bool position_inside_window(SDL_Point position); + + int get_window_id(); +}; + +class Renderer +{ +public: + Renderer(Window *window, int index, Uint32 flags) + { + this->renderer = SDL_CreateRenderer(window->get_window(), index, flags); + if (renderer == nullptr) + { + SDL_DestroyRenderer(this->renderer); + throw SDL_RendererException(); + } + } + + ~Renderer() + { + SDL_DestroyRenderer(this->renderer); + } + + SDL_Renderer *get_renderer() { return this->renderer; } + + void set_draw_color(SDL_Color color); + + void clear(); + + void present(); + +private: + SDL_Renderer *renderer; +}; + +class InfoBox +{ +public: + InfoBox(Renderer *renderer_, SDL_Rect dimensions_, SDL_Color color_) + : renderer(renderer_), color(color_), dimensions(dimensions_) + { + this->texture = SDL_CreateTexture(renderer->get_renderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + dimensions.w, dimensions.h); + if (this->texture == nullptr) + { + SDL_DestroyTexture(texture); + throw SDL_TextureException(); + } + this->visible = false; + } + + ~InfoBox() + { + SDL_DestroyTexture(texture); + } + + virtual void handle_event(const SDL_Event *event) const = 0; + + bool render(SDL_Renderer *renderer, const SDL_Rect target); + + void set_visible(bool visibility) { this->visible = visibility; } + + bool get_visible() { return this->visible; } + + SDL_Renderer *get_renderer() { return this->renderer->get_renderer(); } + + SDL_Rect get_dimensions() { return this->dimensions; } + +protected: + SDL_Rect dimensions; + SDL_Texture *texture; // buffered texture + Renderer *renderer; + SDL_Color color; + bool visible; + + bool create_texture_from_surface(SDL_Surface *surface); +}; + +class TextInfoBox : public InfoBox +{ +public: + TextInfoBox(Renderer *renderer, SDL_Rect dimensions, SDL_Color color, TTF_Font *font_) + : InfoBox(renderer, dimensions, color), font(font_) { } + + virtual void handle_event(const SDL_Event *event); + + bool load_text(std::string text); + +protected: + TTF_Font *font; +}; + +class FieldInfoBox : public TextInfoBox +{ +public: + FieldInfoBox(Renderer *renderer, SDL_Rect dimensions, FieldMeta *field_, SDL_Color color, TTF_Font *font) + : TextInfoBox(renderer, dimensions, color, font), field(field_) { } + + void handle_event(const SDL_Event *event); + +private: + FieldMeta *field; +}; + +class UpgradeInfoBox : public TextInfoBox +{ +public: + UpgradeInfoBox(Renderer *renderer, SDL_Rect dimensions, FieldMeta *field_, SDL_Color color, TTF_Font *font) + : TextInfoBox(renderer, dimensions, color, font) { } + + void handle_event(const SDL_Event *event); +}; + +class ButtonInfoBox : public TextInfoBox +{ +public: + ButtonInfoBox(Renderer *renderer, SDL_Rect dimensions, FieldMeta *field_, SDL_Color color, TTF_Font *font, + UpgradeInfoBox *upgrade_box_) + : TextInfoBox(renderer, dimensions, color, font), upgrade_box(upgrade_box_) { } + + void handle_event(const SDL_Event *event); + +private: + UpgradeInfoBox *upgrade_box; +}; + +class SideBar +{ +public: + SideBar(Renderer *renderer, SDL_Rect dimensions_, SDL_Color color) + : dimensions(dimensions_) + { + } + + void handle_event(const SDL_Event *event); + + void render(SDL_Renderer *renderer); + +private: + SDL_Rect dimensions; + FieldMeta *field; + FieldInfoBox *field_info; + std::vector *upgrades_list; + UpgradeInfoBox *upgrade_info; +}; + +TTF_Font *load_font_from_file(std::string path_to_file); + +#endif \ No newline at end of file diff --git a/src/Main.cpp b/src/Main.cpp deleted file mode 100644 index 3ccc42f..0000000 --- a/src/Main.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "Main.hpp" - - -void logSDLError(std::ostream &os, const std::string &msg) -{ - os << msg << " error:" << SDL_GetError() << std::endl; -} - -SDL_Window *init_window() -{ - - SDL_Window *window = SDL_CreateWindow(TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - SCREEN_WIDTH, SCREEN_HEIGTH, SDL_WINDOW_OPENGL); - if (window == NULL) - { - logSDLError(std::cout, "SDL_CreateWindow"); - SDL_DestroyWindow(window); - SDL_Quit(); - } - return window; -} - -SDL_Renderer *init_renderer(SDL_Window *window) -{ - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (renderer == NULL) - { - logSDLError(std::cout, "SDL_CreateRenderer"); - - } - return renderer; -} - -void Timer::start_timer() -{ - this->timer_started = SDL_GetTicks(); -} - -Uint32 Timer::get_timer() -{ - Uint32 ticks_passed = SDL_GetTicks() - this->timer_started; - return ticks_passed; -} - -Uint32 Timer::reset_timer() -{ - Uint32 ticks_passed = this->get_timer(); - this->timer_started = SDL_GetTicks(); - return ticks_passed; -} - -void Game::toggle_fullscreen() -{ - SDL_DisplayMode dm; - SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(this->window), &dm); - if (!this->full_screen) - { - this->full_screen = true; - SDL_SetWindowSize(this->window, dm.w, dm.h); - SDL_SetWindowFullscreen(this->window, SDL_WINDOW_FULLSCREEN); - this->grid->update_box(dm.w - SIDEBOX_WIDTH, dm.h); - } - else - { - this->full_screen = false; - SDL_SetWindowFullscreen(this->window, 0); - SDL_SetWindowSize(this->window, SCREEN_WIDTH, SCREEN_HEIGTH); - SDL_SetWindowPosition(this->window, dm.w / 4, dm.h / 4); - this->grid->update_box(SCREEN_WIDTH, SCREEN_HEIGTH); - } -} - -void Game::handle_event(SDL_Event *event) -{ - if (event->type == SDL_KEYDOWN) - { - SDL_Keycode key = event->key.keysym.sym; - if (key == SDLK_w) - this->move[0] = true; - if (key == SDLK_a) - this->move[1] = true; - if (key == SDLK_s) - this->move[2] = true; - if (key == SDLK_d) - this->move[3] = true; - if (key == SDLK_f) - { - toggle_fullscreen(); - } - if (key == SDLK_ESCAPE) - { - this->quit = true; - } - } - if (event->type == SDL_KEYUP) - { - SDL_Keycode key = event->key.keysym.sym; - if (key == SDLK_w) - this->move[0] = false; - if (key == SDLK_a) - this->move[1] = false; - if (key == SDLK_s) - this->move[2] = false; - if (key == SDLK_d) - this->move[3] = false; - } - if (event->type == SDL_WINDOWEVENT) - { - if (event->window.windowID == SDL_GetWindowID(this->window)) - { - switch (event->window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - { - this->grid->update_box(event->window.data1, event->window.data2); - break; - } - default: - break; - } - } - } -} - -int Game::game_loop() -{ - Timer *frame_timer = new Timer(); - frame_timer->start_timer(); - Timer *move_timer = new Timer(); - move_timer->start_timer(); - double fps; - Uint32 frame_counter = 0; - while (!this->quit) - { - if (move_timer->get_timer() > 10) - { - move_timer->reset_timer(); - SDL_Point move_by = {(this->move[1] - this->move[3]) * 20, (this->move[0] - this->move[2]) * 20}; - this->grid->move(move_by); - } - if (frame_timer->get_timer() > 1000.0) - { - fps = frame_counter / (frame_timer->reset_timer() / 1000.0); - frame_counter = 0; - std::cout << fps << " fps" << std::endl; - } - SDL_Event event; - while (SDL_PollEvent(&event)) - { - if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) - { - this->handle_event(&event); - } - if (event.type == SDL_QUIT) - { - quit = true; - } - if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEWHEEL || event.type == SDL_MOUSEBUTTONDOWN) - { - grid->handle_event(&event); - } - } - SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); - - SDL_RenderClear(renderer); - SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff); - this->render(renderer); - SDL_RenderPresent(renderer); - frame_counter++; - } - SDL_RenderClear(renderer); - delete move_timer; - delete frame_timer; - return 0; -} - -void Game::render(SDL_Renderer *renderer) -{ - this->grid->render(renderer); - for (FieldMeta *meta : this->fields_meta) - { - meta->render(renderer, this->layout); - } -} - -int main(int, char **) -{ - if (SDL_Init(SDL_INIT_VIDEO) != 0) - { - logSDLError(std::cout, "SDL_init"); - return -1; - } - SDL_Window *window = init_window(); - if (!window) - { - SDL_DestroyWindow(window); - SDL_Quit(); - return -1; - } - SDL_Renderer *renderer = init_renderer(window); - if (!renderer) - { - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - return -1; - } - Layout *layout = new Layout(pointy_orientation, 20, {SCREEN_WIDTH / 2, SCREEN_HEIGTH / 2}, - {0, 0, SCREEN_WIDTH - SIDEBOX_WIDTH, SCREEN_HEIGTH}); - Game *game = new Game(window, renderer, layout); - int exit_status = game->game_loop(); - delete game; - delete layout; - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - return exit_status; -} - -bool position_in_window(SDL_Point position, SDL_Window *window) -{ - SDL_DisplayMode dm; - SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(window), &dm); - return position.x > 0 && position.x < dm.w && position.y > 0 && position.y < dm.h; -} diff --git a/src/Main.hpp b/src/Main.hpp deleted file mode 100644 index 6842272..0000000 --- a/src/Main.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include -#include -#include "Gameplay.hpp" -#include - -#ifndef DEFAULTS -const Sint16 GRID_SIZE = 4; -const int SCREEN_WIDTH = 800; -const int SCREEN_HEIGTH = 600; -const int SIDEBOX_WIDTH = 200; -const char TITLE[] = "Bob - Battles of Bacteria"; -#endif - -#if SDL_BYTEORDER == SDL_BIG_ENDIAN -Uint32 rmask = 0xff000000; -Uint32 gmask = 0x00ff0000; -Uint32 bmask = 0x0000ff00; -Uint32 amask = 0x000000ff; -#else -Uint32 rmask = 0x000000ff; -Uint32 gmask = 0x0000ff00; -Uint32 bmask = 0x00ff0000; -Uint32 amask = 0xff000000; -#endif - -#ifndef Timer - -class Timer -{ -private: - Uint32 timer_started; -public: - void start_timer(); - - Uint32 get_timer(); - - Uint32 reset_timer(); -}; - -#endif - -#ifndef Game -class Game -{ -private: - SDL_Window *window; - SDL_Renderer *renderer; - HexagonGrid *grid; - // only deal with meta information - Cluster fields_meta; - Layout *layout; - bool move[4]; - bool full_screen; - bool quit; - -public: - Game(SDL_Window *window_, SDL_Renderer *renderer_, Layout *layout_) - : window(window_), renderer(renderer_), layout(layout_) - { - this->grid = new HexagonGrid(GRID_SIZE, layout); - for (int i = 0; i < 4; i++) - { - this->move[i] = false; - } - this->quit = false; - // create meta information for every field on the grid - for (const Field &elem : *(grid->get_fields())) - { - FieldMeta *meta = new FieldMeta(elem); - fields_meta.insert(meta); - } - } - - ~Game() - { - for (const FieldMeta *elem : this->fields_meta) - { - delete elem; - } - delete this->grid; - } - - void render(SDL_Renderer *renderer); - - void toggle_fullscreen(); - - void handle_event(SDL_Event *event); - - int game_loop(); -}; - -#endif - -bool position_in_window(SDL_Point position, SDL_Window *window);