From 42e2e43eadf98045b4f9eaf13baf34238be0f7fd Mon Sep 17 00:00:00 2001
From: Anna Wiggins <annabunches@gmail.com>
Date: Wed, 6 Jul 2011 14:24:37 -0400
Subject: [PATCH] Huge refactor with the basic goal of reducing complexity in
 the Game class. Broke a couple of small things in the process - still working
 on fixing them back up

---
 Makefile       |   2 +-
 game.cpp       | 110 ++++++++++++++++++++++++++++---------------------
 game.h         |   5 ++-
 menubutton.cpp |  81 +++++++++++++++++++++++++++++++++---
 menubutton.h   |  31 +++++++++++---
 5 files changed, 169 insertions(+), 60 deletions(-)

diff --git a/Makefile b/Makefile
index acad14a..52591e3 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ OBJECTS=\
 main.o \
 drawutils.o mathutils.o timer.o itos.o \
 graph.o gamedata.o player.o vertex.o gamevertex.o \
-menubutton.o \
+entity.o menubutton.o \
 mainevent.o gamestate.o game.o titlescreen.o
 
 all: $(PROJECT)
diff --git a/game.cpp b/game.cpp
index 1ed832d..244ed5c 100644
--- a/game.cpp
+++ b/game.cpp
@@ -4,7 +4,7 @@
 #include "debug.h"
 #include "itos.h"
 #include <SDL.h>
-
+using std::pair;
 
 Game::Game(stack<GameState*>* state_stack, SDL_Surface* display)
     : GameState(state_stack, display)
@@ -76,10 +76,34 @@ bool Game::init()
 				     BUTTON_END_TURN));
 
 
+    for (list<MenuButton*>::iterator cursor = buttons.begin();
+	 cursor != buttons.end(); cursor++)
+    {
+	if (!(*cursor)->init())
+	{
+	    debug("Failed to initialize a button");
+	    throw StateExit();
+	}
+    }
+
+    mode_changed();
+
+
     return GameState::init();
 }
 
 
+void Game::iterate()
+{
+    // handle the button iterations
+    for (list<MenuButton*>::iterator cursor = buttons.begin();
+	 cursor != buttons.end(); cursor++)
+    {
+	(*cursor)->iterate();
+    }    
+}
+
+
 void Game::render()
 {
     int range = data.get_range();
@@ -142,7 +166,22 @@ void Game::render()
     }
 
     // draw the rest of the bottom menu
+    draw_menu_bars();
 
+    for (list<MenuButton*>::iterator cursor = buttons.begin();
+	 cursor != buttons.end(); cursor++)
+    {
+	(*cursor)->render(display);
+    }
+
+    draw_player_info();
+
+    SDL_Flip(display);
+}
+
+
+void Game::draw_menu_bars()
+{
     // horizontal line across the whole thing
     DrawUtils::draw_line(display, 0, display->h - 100,
 			 display->w, display->h - 100, 2, 0x000000);
@@ -155,51 +194,6 @@ void Game::render()
     DrawUtils::draw_line(display, display->w - 100, display->h - 100,
 			 display->w - 100, display->h, 2,
 			 0x000000);
-    
-    for (list<MenuButton*>::iterator cursor = buttons.begin();
-	 cursor != buttons.end(); cursor++)
-    {
-	MenuButton* button = *cursor;
-	
-	if (!(button->get_action() & BUTTON_END_TURN) &&
-	    (data.get_current_vertex() == NULL ||
-	     data.get_current_vertex()->player != data.get_turn()))
-	    continue;
-
-	if ((button->get_action() & (BUTTON_BUILD_ATTACKER |
-				     BUTTON_BUILD_DEFENDER |
-				     BUTTON_BUILD_PRODUCER)) &&
-	    data.get_mode() != MODE_BUILD)
-	    continue;
-
-	draw_button(button);
-    }
-
-    draw_player_info();
-
-    SDL_Flip(display);
-}
-
-
-void Game::draw_button(MenuButton* button)
-{
-    int colour = 0x000000;
-    ButtonAction action = button->get_action();
-    Mode mode = data.get_mode();
-    VertexType build_type = data.get_build_type();
-
-    // fixme - there's really got to be a better way...
-    if ((action == BUTTON_BUILD && mode == MODE_BUILD) ||
-	(action == BUTTON_ATTACK && mode == MODE_ATTACK) ||
-	(action == BUTTON_MOVE && mode == MODE_MOVE) ||
-	(action == BUTTON_BUILD_ATTACKER && build_type == VERTEX_ATTACKER) ||
-	(action == BUTTON_BUILD_DEFENDER && build_type == VERTEX_DEFENDER) ||
-	(action == BUTTON_BUILD_PRODUCER && build_type == VERTEX_PRODUCER)
-	)
-	colour = 0x0000ff;
-    else if (button->is_at(cursor_x, cursor_y)) colour = 0xff0000;
-
-    button->draw(display, colour);
 }
 
 
@@ -331,17 +325,24 @@ void Game::on_lbutton_down(int x, int y)
 	 cursor != buttons.end(); cursor++)
     {
 	MenuButton* button = *cursor;
-	if (button->is_at(x, y)) handle_button_press(button->get_action());
+	if (button->is_at(x, y))
+	{
+	    handle_button_press(button->get_action());
+	    mode_changed();
+	    return;
+	}
     }
 
     if (y > display->h - 110) return;
     if (!data.endgame()) data.handle_click(x, y);
+    mode_changed();
 }
 
 
 void Game::on_rbutton_down(int mX, int mY)
 {
     data.clear_current_vertex();
+    mode_changed();
 }
 
 
@@ -357,6 +358,8 @@ void Game::on_key_down(SDLKey sym, SDLMod mod, Uint16 unicode)
     else if (sym == SDLK_s || sym == SDLK_ESCAPE) data.set_mode(MODE_SELECT);
     else if (sym == SDLK_e) data.toggle_turn();
 
+    mode_changed();
+
 #ifdef DEBUG
     if (sym == SDLK_d && mod & (KMOD_ALT | KMOD_CTRL)) print_debug_info();
 #endif
@@ -377,3 +380,16 @@ void Game::print_debug_info()
     fprintf(stderr, "Turn: %s\n", data.get_turn()->get_name().c_str());
 }
 #endif
+
+
+void Game::mode_changed()
+{
+    // Set the state for each button
+    for (list<MenuButton*>::iterator cursor = buttons.begin();
+	 cursor != buttons.end(); cursor++)
+    {
+	(*cursor)->set_state(data.get_mode(), data.get_build_type(),
+			     data.get_current_vertex());
+    }
+
+}
diff --git a/game.h b/game.h
index 2e00db4..ce66f1a 100644
--- a/game.h
+++ b/game.h
@@ -28,7 +28,7 @@ class Game : public GameState
 
  protected:
     void render();
-    void iterate() {}
+    void iterate();
 
     // event handlers
     void on_lbutton_down(int x, int y);
@@ -39,11 +39,12 @@ class Game : public GameState
 
  private:
     void draw_stats(GameVertex* v);
-    void draw_button(MenuButton* button);
     void draw_node(GameVertex* v);
     void draw_player_info();
+    void draw_menu_bars();
 
     void handle_button_press(ButtonAction action);
+    void mode_changed();
 
     // data
     GameData data;
diff --git a/menubutton.cpp b/menubutton.cpp
index 69e8547..47bf824 100644
--- a/menubutton.cpp
+++ b/menubutton.cpp
@@ -3,7 +3,8 @@
 
 
 MenuButton::MenuButton(string text, TTF_Font* font, int x, int y,
-		       ButtonAction action)
+		       ButtonAction action, int colour, int hover_colour,
+		       int selected_colour, int background_colour)
 {
     this->text = text;
     this->x = x;
@@ -11,29 +12,99 @@ MenuButton::MenuButton(string text, TTF_Font* font, int x, int y,
     this->font = font;
     this->action = action;
 
+    this->colour = colour;
+    this->hover_colour = hover_colour;
+    this->selected_colour = selected_colour;
+    this->background_colour = background_colour;
+}
+
+
+bool MenuButton::init()
+{
     hover = false;
+    selected = false;
+    visible = true;
+    return true;
 }
 
 
-void MenuButton::set_hover(bool is_hovering)
+void MenuButton::set_selected(bool is_selected)
 {
-    hover = is_hovering;
+    selected = is_selected;
 }
 
 
-void MenuButton::draw(SDL_Surface* display, int colour, int background_colour)
+void MenuButton::set_visible(bool is_visible)
 {
+    visible = is_visible;
+}
+
+
+void MenuButton::iterate()
+{
+    int x, y;
+    SDL_GetMouseState(&x, &y);
+    if (is_at(x, y)) hover = true;
+    else hover = false;
+}
+
+
+void MenuButton::render(SDL_Surface* display)
+{
+    if (!visible) return;
+
+    int colour_to_use = colour;
+    if (selected) colour_to_use = selected_colour;
+    else if (hover) colour_to_use = hover_colour;
+
     SDL_Rect pen = {x, y, 80, 20};
 
     SDL_FillRect(display, &pen, background_colour);
 
     int temp_colour = 0x000000;
 
-    DrawUtils::draw_text(display, text, x + 40, y + 10, font, colour, 1, 1);
+    DrawUtils::draw_text(display, text, x + 40, y + 10, font, colour_to_use,
+			 1, 1);
 }
 
 
 bool MenuButton::is_at(int test_x, int test_y)
 {
+    if (!visible) return false;
     return test_x >= x && test_y >= y && test_x <= x + 100 && test_y <= y + 40;
 }
+
+
+void MenuButton::set_state(Mode mode, VertexType type, GameVertex* current)
+{
+    selected = false;
+    visible = false;
+
+    if (action & BUTTON_END_TURN)
+    {
+	visible = true;
+	return;
+    }
+
+    if ((mode & MODE_ATTACK && action & BUTTON_ATTACK) ||
+	(mode & MODE_MOVE && action & BUTTON_MOVE) ||
+	(mode & MODE_BUILD && action & BUTTON_BUILD) ||
+	(type & VERTEX_ATTACKER && action & BUTTON_BUILD_ATTACKER) ||
+	(type & VERTEX_DEFENDER && action & BUTTON_BUILD_DEFENDER) ||
+	(type & VERTEX_PRODUCER && action & BUTTON_BUILD_PRODUCER)
+	)
+	selected = true;
+
+    // If we have selected one of our vertices, and we're one of the three
+    // main buttons, be visible
+    if (current != NULL && action & (BUTTON_ATTACK | BUTTON_MOVE |
+				     BUTTON_BUILD))
+	visible = true;
+
+    // If we're one of the other three, and we're in MODE_BUILD, we're visible
+    if (mode & MODE_BUILD && action & (BUTTON_BUILD_ATTACKER |
+				       BUTTON_BUILD_DEFENDER | 
+				       BUTTON_BUILD_PRODUCER))
+	visible = true;
+}
+
diff --git a/menubutton.h b/menubutton.h
index 4d762f0..794316a 100644
--- a/menubutton.h
+++ b/menubutton.h
@@ -8,6 +8,9 @@
 #include <SDL.h>
 #include <SDL_ttf.h>
 #include <string>
+#include "entity.h"
+#include "gamedata.h"
+#include "gamevertex.h"
 
 using std::string;
 
@@ -17,27 +20,45 @@ enum ButtonAction {BUTTON_BUILD=0x1, BUTTON_ATTACK=0x2, BUTTON_MOVE=0x4,
 		   BUTTON_BUILD_ATTACKER=0x8, BUTTON_BUILD_DEFENDER=0x10,
 		   BUTTON_BUILD_PRODUCER=0x20, BUTTON_END_TURN=0x40};
 
-class MenuButton
+class MenuButton : public Entity
 {
  public:
     MenuButton(string text = "", TTF_Font* font = NULL, int x = 0, int y = 0,
-	       ButtonAction action = BUTTON_BUILD);
+	       ButtonAction action = BUTTON_BUILD, int colour=0x000000,
+	       int hover_colour=0xff0000, int selected_colour=0x0000ff,
+	       int background_colour=0x888888);
 
     // Selectable buttons change colour for the duration they are selected,
     // and should ignore clicks while selected
-    void set_hover(bool is_hovering);
-    void draw(SDL_Surface* display, int colour = 0x000000,
-	      int background_colour = 0x888888);
+    void set_selected(bool is_selected);
+    void set_visible(bool is_visible);
+
+    bool init();
+    void render(SDL_Surface* display);
+    void iterate();
 
     bool is_at(int test_x, int test_y);
     ButtonAction get_action() const { return action; }
 
+
+    // To get some complexity out of the Game class, let's teach MenuButton
+    // how to handle her own state, based on the current Mode and VertexType
+    // This isn't wonderful coupling, but it'll do
+    void set_state(Mode mode, VertexType type, GameVertex* current);
+
  private:
     string text;
     int x;
     int y;
 
+    int colour;
+    int hover_colour;
+    int selected_colour;
+    int background_colour;
+
     bool hover;
+    bool selected;
+    bool visible;
 
     TTF_Font* font;