From 4f95ed3fc0aa70a1ab82369c3e28405ad0cedbd2 Mon Sep 17 00:00:00 2001
From: Tim Schubert <tim.schubert@tu-bs.de>
Date: Tue, 19 Jan 2016 22:11:23 +0100
Subject: [PATCH] Added conquering and resource spending.

---
 src/Gameplay.cpp | 113 +++++++++++++++++++++++++++++++++++++++--------
 src/Gameplay.hpp |  57 +++++++++++++++++++++---
 src/Main.cpp     |  10 ++---
 src/Main.hpp     |   5 ++-
 4 files changed, 155 insertions(+), 30 deletions(-)

diff --git a/src/Gameplay.cpp b/src/Gameplay.cpp
index 34b67a8..2540164 100644
--- a/src/Gameplay.cpp
+++ b/src/Gameplay.cpp
@@ -9,11 +9,6 @@ Player *FieldMeta::get_owner()
     return this->owner;
 }
 
-void FieldMeta::set_owner(Player *player)
-{
-    this->owner = player;
-}
-
 Field FieldMeta::get_field() { return this->field; }
 
 void FieldMeta::render(SDL_Renderer *renderer, Layout *layout)
@@ -31,7 +26,9 @@ void FieldMeta::render(SDL_Renderer *renderer, Layout *layout)
         vx[i] = (Sint16) polygon[i].x;
         vy[i] = (Sint16) polygon[i].y;
     }
-    filledPolygonRGBA(renderer, vx, vy, 6, color.r, color.g, color.b, 0x22);
+    if ((*this->owner) == Player::default_player)
+        color = {0x77, 0x77, 0x77, 0x77};
+    filledPolygonRGBA(renderer, vx, vy, 6, color.r, color.g, color.b, 0x33);
     SDL_Color inverse;
     inverse.r = (Uint8) (0xff - color.r);
     inverse.g = (Uint8) (0xff - color.g);
@@ -95,20 +92,23 @@ Resource FieldMeta::get_resources()
     return this->resources;
 }
 
-Resource FieldMeta::get_resources_of_cluster()
+Resource FieldMeta::get_resources_of_cluster(Cluster cluster)
 {
     Resource res = {0, 0, 0};
-    std::unordered_set<FieldMeta *> *cluster = new std::unordered_set<FieldMeta *>();
-    this->get_cluster(cluster);
-    for (FieldMeta *elem : *cluster)
+    for (FieldMeta *elem : cluster)
     {
         Resource r_plus = elem->get_resources();
         res += r_plus;
     }
-    delete 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)
@@ -132,22 +132,22 @@ bool FieldMeta::upgrade(Upgrade upgrade)
             std::cout << "Unknown update: " << upgrade;
             break;
     }
-    if (cluster_resources > costs)
+    Resource remaining_costs = this->consume_resources_of_cluster(costs);
+    Resource neutral = {0, 0, 0};
+    if (remaining_costs == neutral)
     {
-        this->resources -= costs;
         this->upgrades |= upgrade;
+        return true;
     }
     return false;
 }
 
-std::unordered_set<FieldMeta *> *FieldMeta::get_cluster(std::unordered_set<FieldMeta *> *visited)
+Cluster FieldMeta::get_cluster(Cluster visited)
 {
-    assert(visited != nullptr);
-
-    if (visited->find(this) != visited->end()) // already been here before
+    if (visited.find(this) != visited.end()) // already been here before
         return visited;
 
-    visited->insert(this);
+    visited.insert(this);
 
     for (Uint8 i = 0; i < 6; i++)
     {
@@ -160,3 +160,80 @@ std::unordered_set<FieldMeta *> *FieldMeta::get_cluster(std::unordered_set<Field
     }
     return visited;
 }
+
+Resource FieldMeta::consume_resources_of_cluster(Resource costs)
+{
+    Cluster cluster = this->get_cluster();
+    Resource cluster_resources = this->get_resources_of_cluster(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;
+    }
+    return costs; // > {0, 0, 0} means there were not enough resources
+}
+
+void FieldMeta::set_owner(Player *player)
+{
+    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
+    {
+        return false;
+    }
+    if (*(meta->get_owner()) == this->default_player) // still to be had
+    {
+        meta->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();
+    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
+        {
+            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();
+        // 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);
+        return true;
+    }
+    return false;
+}
+
+int FieldMeta::get_offense()
+{
+    return this->offense;
+}
+
+int FieldMeta::get_defense()
+{
+    return this->defense;
+}
diff --git a/src/Gameplay.hpp b/src/Gameplay.hpp
index 49bc46c..b547827 100644
--- a/src/Gameplay.hpp
+++ b/src/Gameplay.hpp
@@ -9,6 +9,8 @@
 #include <SDL2/SDL_render.h>
 #include "Grid.hpp"
 
+
+#ifndef Upgrade
 typedef enum
 {
     UPGRADE_FIRST_UPGRADE = 0,
@@ -17,6 +19,7 @@ typedef enum
     UPGRADE_REGENERATION_3,
     UPGRADE_REPRODUCTION
 } Upgrade;
+#endif
 
 #ifndef Tagged
 
@@ -96,6 +99,8 @@ public:
         return descriptor.str();
     }
 
+    bool fight(Field field);
+
     static Player default_player;
 };
 
@@ -125,9 +130,21 @@ struct Resource
 
     Resource &operator-=(const Resource &rhs)
     {
-        this->circle -= rhs.circle;
-        this->triangle -= rhs.triangle;
-        this->square -= rhs.square;
+        if (this->circle < rhs.circle)
+            this->circle = 0;
+        else
+            this->circle -= rhs.circle;
+
+        if (this->triangle < rhs.triangle)
+            this->triangle = 0;
+        else
+            this->triangle -= rhs.triangle;
+
+        if (this->square < rhs.square)
+            this->square = 0;
+        else
+            this->square -= rhs.square;
+
         return *this;
     }
 
@@ -180,10 +197,18 @@ struct Resource
     inline bool operator<=(const Resource &rhs) const { return !(*this > rhs); }
 
     inline bool operator>=(const Resource &rhs) const { return !(*this < rhs); }
+
+    bool operator==(const Resource &rhs) const
+    {
+        return (this->circle == rhs.circle && this->triangle == rhs.triangle && this->square == rhs.square);
+    }
+
+    inline bool operator!=(const Resource &rhs) const { return !(*this == rhs); }
 };
 
 #endif
 
+
 #ifndef FieldMeta
 
 class FieldMeta
@@ -201,21 +226,34 @@ public:
         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();
+
+    int get_defense();
+
+    static FieldMeta *get_meta(Field field);
+
     Field get_field();
 
     Player *get_owner();
 
     void set_owner(Player *player);
 
+    //void set_owner(Player *player);
+
     void render(SDL_Renderer *renderer, Layout *layout);
 
     Resource get_resources();
 
     Resource get_resources_of_cluster();
 
-    std::unordered_set<FieldMeta *> *get_cluster(std::unordered_set<FieldMeta *> *visited);
+    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();
 
@@ -223,6 +261,8 @@ public:
 
     bool upgrade(Upgrade upgrade);
 
+    Resource consume_resources_of_cluster(Resource costs);
+
 private:
     const Field field;
     static std::unordered_map<Field, FieldMeta *> fields;
@@ -230,6 +270,13 @@ private:
     Uint32 upgrades;
     Resource resources_base; // without upgrades applied, used as basis of regeneration
     Resource resources; // actual current resources
+    int offense;
+    int defense;
 };
 
-#endif
\ No newline at end of file
+#endif
+
+#ifndef Cluster
+typedef std::unordered_set<FieldMeta *> Cluster;
+#endif
+
diff --git a/src/Main.cpp b/src/Main.cpp
index 0e14d5b..3ccc42f 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -58,7 +58,7 @@ void Game::toggle_fullscreen()
         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, dm.h);
+        this->grid->update_box(dm.w - SIDEBOX_WIDTH, dm.h);
     }
     else
     {
@@ -135,7 +135,7 @@ int Game::game_loop()
         if (move_timer->get_timer() > 10)
         {
             move_timer->reset_timer();
-            SDL_Point move_by = {(this->move[1] - this->move[3]) * 10, (this->move[0] - this->move[2]) * 10};
+            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)
@@ -155,7 +155,7 @@ int Game::game_loop()
             {
                 quit = true;
             }
-            if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEWHEEL)
+            if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEWHEEL || event.type == SDL_MOUSEBUTTONDOWN)
             {
                 grid->handle_event(&event);
             }
@@ -205,8 +205,8 @@ int main(int, char **)
         SDL_Quit();
         return -1;
     }
-    Layout *layout = new Layout(pointy_orientation, GRID_SIZE, {5 * SCREEN_WIDTH / 12, 5 * SCREEN_HEIGTH / 12},
-                                {0, 0, 5 * SCREEN_WIDTH / 6, 5 * SCREEN_HEIGTH / 6});
+    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;
diff --git a/src/Main.hpp b/src/Main.hpp
index bd3f696..7ce1fb5 100644
--- a/src/Main.hpp
+++ b/src/Main.hpp
@@ -10,7 +10,8 @@
 const Sint16 GRID_SIZE = 4;
 const int SCREEN_WIDTH = 800;
 const int SCREEN_HEIGTH = 600;
-char TITLE[] = "Bob - Battles of Bacteria";
+const int SIDEBOX_WIDTH = 200;
+const char TITLE[] = "Bob - Battles of Bacteria";
 #endif
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
@@ -49,7 +50,7 @@ private:
     SDL_Renderer *renderer;
     HexagonGrid *grid;
     // only deal with meta information
-    std::unordered_set<FieldMeta *> fields_meta;
+    Cluster fields_meta;
     Layout *layout;
     bool move[4];
     bool full_screen;