Refactoring, Exception handling and Events.
This commit is contained in:
parent
b159f83c63
commit
a3df4a3189
14 changed files with 1377 additions and 916 deletions
136
src/Bob.cpp
Normal file
136
src/Bob.cpp
Normal file
|
@ -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;
|
||||
}
|
91
src/Bob.hpp
Normal file
91
src/Bob.hpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <unordered_set>
|
||||
#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
|
|
@ -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})
|
24
src/Events.cpp
Normal file
24
src/Events.cpp
Normal file
|
@ -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);
|
||||
}
|
39
src/Events.hpp
Normal file
39
src/Events.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <SDL2/SDL.h>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#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
|
42
src/Exceptions.hpp
Normal file
42
src/Exceptions.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#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
|
319
src/Gameplay.cpp
319
src/Gameplay.cpp
|
@ -1,23 +1,12 @@
|
|||
#include "Gameplay.hpp"
|
||||
|
||||
Player Player::default_player = Player("Default");
|
||||
|
||||
std::unordered_map<Field, FieldMeta *> FieldMeta::fields = std::unordered_map<Field, FieldMeta *>();
|
||||
|
||||
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<Point> polygon = field_to_polygon(this->field, layout);
|
||||
std::vector<Point> 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<Point> polygon = some_field.field_to_polygon_normalized(this->layout);
|
||||
assert(polygon.size() > 5);
|
||||
Sint16 x[6];
|
||||
Sint16 y[6];
|
||||
for (std::pair<Field, FieldMeta *> 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;
|
||||
}
|
||||
|
|
343
src/Gameplay.hpp
343
src/Gameplay.hpp
|
@ -7,106 +7,16 @@
|
|||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <SDL2/SDL_pixels.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <bitset>
|
||||
#include "Grid.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
#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<NUM_UPGRADES> 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<Upgrade>
|
||||
{
|
||||
size_t operator()(const Upgrade &f) const
|
||||
{
|
||||
int i = static_cast<int>(f);
|
||||
hash<int> int_hash;
|
||||
return int_hash(f);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#ifndef FieldMeta
|
||||
const std::unordered_map<Upgrade, Resource> 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<Field, FieldMeta *> 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<FieldMeta *> cluster);
|
||||
|
||||
std::unordered_set<FieldMeta *> get_cluster(
|
||||
std::unordered_set<FieldMeta *> visited = std::unordered_set<FieldMeta *>());
|
||||
|
||||
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<Field, FieldMeta *> 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<FieldMeta *> Cluster;
|
||||
#endif
|
||||
|
||||
class Grid
|
||||
{
|
||||
|
||||
public:
|
||||
Grid(Layout *layout_)
|
||||
: layout(layout_)
|
||||
{
|
||||
Field f = {0, 0, 0};
|
||||
std::unordered_map<Field, FieldMeta *> fields = std::unordered_map<Field, FieldMeta *>();
|
||||
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<Field, FieldMeta *> 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
|
||||
|
|
237
src/Grid.cpp
237
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<Point> field_to_polygon(const Field field, const Layout *layout)
|
||||
std::vector<Point> Field::field_to_polygon(const Layout *layout) const
|
||||
{
|
||||
std::vector<Point> corners = field_to_polygon_normalized(field, layout);
|
||||
Point center = field_to_point(field, layout);
|
||||
std::vector<Point> 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<Point> field_to_polygon(const Field field, const Layout *layout)
|
|||
return corners;
|
||||
}
|
||||
|
||||
std::vector<Point> field_to_polygon_normalized(const Field field, const Layout *layout)
|
||||
std::vector<Point> Field::field_to_polygon_normalized(const Layout *layout) const
|
||||
{
|
||||
std::vector<Point> corners;
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
|
@ -202,131 +123,3 @@ std::vector<Point> 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<Point> 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;
|
||||
}
|
367
src/Grid.hpp
367
src/Grid.hpp
|
@ -7,8 +7,9 @@
|
|||
#include <assert.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#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<Point> field_to_polygon_normalized(const Layout *layout) const;
|
||||
|
||||
std::vector<Point> 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<Field> 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<Field> 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<Point> field_to_polygon_normalized(const Field field, const Layout *layout);
|
||||
|
||||
std::vector<Point> field_to_polygon(const Field field, const Layout *layout);
|
||||
|
||||
|
||||
#ifndef Grid
|
||||
|
||||
class Grid
|
||||
{
|
||||
protected:
|
||||
std::unordered_set<Field> *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<Field>();
|
||||
};
|
||||
|
||||
~Grid()
|
||||
{
|
||||
delete this->fields;
|
||||
}
|
||||
|
||||
std::unordered_set<Field> *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
|
||||
|
|
174
src/Gui.cpp
Normal file
174
src/Gui.cpp
Normal file
|
@ -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});
|
||||
}
|
194
src/Gui.hpp
Normal file
194
src/Gui.hpp
Normal file
|
@ -0,0 +1,194 @@
|
|||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#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<ButtonInfoBox *> *upgrades_list;
|
||||
UpgradeInfoBox *upgrade_info;
|
||||
};
|
||||
|
||||
TTF_Font *load_font_from_file(std::string path_to_file);
|
||||
|
||||
#endif
|
225
src/Main.cpp
225
src/Main.cpp
|
@ -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;
|
||||
}
|
97
src/Main.hpp
97
src/Main.hpp
|
@ -1,97 +0,0 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include "Gameplay.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
#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);
|
Loading…
Add table
Add a link
Reference in a new issue