From e1b23f928beffbe93b4ebc3c8dabd5d2ffde8e8c Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Tue, 12 Feb 2013 19:34:47 +0000
Subject: [PATCH] [Change] GUI's are now saved in XML and thrown into data pack
 file.   -- This allows for multiple gui's.   -- Each ship can also have it's
 own gui if it so chooses.

---
 dat/gui.xml                               |  48 ++++
 dat/ship.xml                              |   2 +
 gfx/gui/{frame.png => simple.png}         | Bin
 gfx/gui/{frame.xcf => simple.xcf}         | Bin
 gfx/gui/{pilot.png => simple_pilot.png}   | Bin
 gfx/gui/{planet.png => simple_planet.png} | Bin
 src/pilot.c                               |   4 +-
 src/player.c                              | 292 +++++++++++++++++-----
 src/ship.c                                |   2 +
 src/ship.h                                |   3 +
 10 files changed, 290 insertions(+), 61 deletions(-)
 create mode 100644 dat/gui.xml
 rename gfx/gui/{frame.png => simple.png} (100%)
 rename gfx/gui/{frame.xcf => simple.xcf} (100%)
 rename gfx/gui/{pilot.png => simple_pilot.png} (100%)
 rename gfx/gui/{planet.png => simple_planet.png} (100%)

diff --git a/dat/gui.xml b/dat/gui.xml
new file mode 100644
index 0000000..7a13dbb
--- /dev/null
+++ b/dat/gui.xml
@@ -0,0 +1,48 @@
+<GUIs>
+	<gui name="simple" gfx="simple">
+		<radar type="rectangle">
+			<w>128</w>
+			<h>128</h>
+			<x>11</x>
+			<y>10</y>
+		</radar>
+		<health>
+			<shield>
+				<w>129</w>
+				<h>10</h>
+				<x>10</x>
+				<y>201</y>
+			</shield>
+			<armor>
+				<w>129</w>
+				<h>10</h>
+				<x>10</x>
+				<y>218</y>
+			</armor>
+			<energy>
+				<w>129</w>
+				<h>10</h>
+				<x>10</x>
+				<y>236</y>
+			</energy>
+		</health>
+		<target>
+			<gfx>
+				<x>10</x>
+				<y>256</y>                                                                
+			</gfx>
+			<name>
+				<x>16</x>
+				<y>261</y>
+			</name>
+			<faction>
+				<x>16</x>
+				<y>274</y>
+			</faction>
+			<health>
+				<x>16</x>
+				<y>334</y>
+			</health>
+		</target>
+	</gui>
+</GUIs>
diff --git a/dat/ship.xml b/dat/ship.xml
index 698c3a1..3a30994 100644
--- a/dat/ship.xml
+++ b/dat/ship.xml
@@ -2,6 +2,7 @@
 <Ships>
 	<ship name="Merchant Ship">
 		<GFX>ship</GFX>
+    <GUI>simple</GUI>
 		<class>1</class>
 		<movement>
 			<thrust>400</thrust>
@@ -28,6 +29,7 @@
   </ship>
 	<ship name="Enemy Test">
 		<GFX>ship1</GFX>
+    <GUI>simple</GUI>
 		<class>1</class>
 		<movement>
 			<thrust>180</thrust>
diff --git a/gfx/gui/frame.png b/gfx/gui/simple.png
similarity index 100%
rename from gfx/gui/frame.png
rename to gfx/gui/simple.png
diff --git a/gfx/gui/frame.xcf b/gfx/gui/simple.xcf
similarity index 100%
rename from gfx/gui/frame.xcf
rename to gfx/gui/simple.xcf
diff --git a/gfx/gui/pilot.png b/gfx/gui/simple_pilot.png
similarity index 100%
rename from gfx/gui/pilot.png
rename to gfx/gui/simple_pilot.png
diff --git a/gfx/gui/planet.png b/gfx/gui/simple_planet.png
similarity index 100%
rename from gfx/gui/planet.png
rename to gfx/gui/simple_planet.png
diff --git a/src/pilot.c b/src/pilot.c
index 32a0947..7b2ecf4 100644
--- a/src/pilot.c
+++ b/src/pilot.c
@@ -33,8 +33,9 @@ static int nfleets = 0;
 
 // External.
 extern void ai_destroy(Pilot* p); // Ai.
-extern void player_think(Pilot* pilot); // Player.c
 extern void ai_think(Pilot* pilot); // Ai.c
+extern void player_think(Pilot* pilot); // Player.c
+extern int gui_load(const char* name); // Player.c
 // Internal.
 static void pilot_update(Pilot* pilot, const double dt);
 void pilot_render(Pilot* pilot);
@@ -242,6 +243,7 @@ void pilot_init(Pilot* pilot, Ship* ship, char* name, Faction* faction, AI_Profi
     pilot->render = NULL;
     pilot_setFlag(pilot, PILOT_PLAYER); // It's a player!
     player = pilot;
+    gui_load(pilot->ship->gui); // Load the GUI.
   } else {
     pilot->think = ai_think;
     pilot->render = pilot_render;
diff --git a/src/player.c b/src/player.c
index f5fb256..3c7e0eb 100644
--- a/src/player.c
+++ b/src/player.c
@@ -11,11 +11,11 @@
 #define XML_NODE_START  1
 #define XML_NODE_TEXT   3
 
-#define XML_GUI_ID  "GUIs" // XML section identifier.
-#define XML_GUI_ID  "gui"
+#define XML_GUI_ID    "GUIs" // XML section identifier.
+#define XML_GUI_TAG   "gui"
 
-#define GUI_DATA    "../dat/gui.xml"
-#define GUI_GFX     "../gfx/gui/"
+#define GUI_DATA      "../dat/gui.xml"
+#define GUI_GFX       "../gfx/gui/"
 
 #define POW2(x) ((x)*(x))
 
@@ -125,9 +125,10 @@ static Msg* msg_stack;
 // External.
 extern void pilot_render(Pilot* pilot); // Extern is in Pilot.*
 // Internal.
-//TODO: Gui shit.
-void gui_renderPilot(Pilot* p);
-void gui_renderBar(Color* c, Vec2* p, Rect* r, double w);
+static void rect_parse(const xmlNodePtr parent, double* x, double* y, double* w, double* h);
+static int gui_parse(const xmlNodePtr parent, const char* name);
+static void gui_renderPilot(const Pilot* p);
+static void gui_renderBar(const Color* c, const Vec2* p, const Rect* r, const double w);
 
 void player_message(const char* fmt, ...) {
   va_list ap;
@@ -135,11 +136,13 @@ void player_message(const char* fmt, ...) {
 
   if(fmt == NULL) return; // Message not valid.
 
+  // Copy old messages back.
   for(i = 1; i < msg_max; i++)
     if(msg_stack[msg_max-i-1].str[0] != '\0') {
       strcpy(msg_stack[msg_max-i].str, msg_stack[msg_max-i-1].str);
       msg_stack[msg_max-i].t = msg_stack[msg_max-i-1].t;
     }
+    // Add the new one.
     va_start(ap, fmt);
     vsprintf(msg_stack[0].str, fmt, ap);
     va_end(ap);
@@ -266,7 +269,7 @@ void player_render(void) {
 }
 
 // Renders a pilot.
-void gui_renderPilot(Pilot* p) {
+static void gui_renderPilot(const Pilot* p) {
   int x, y, sx, sy;
 
   x = (p->solid->pos.x - player->solid->pos.x) / gui.radar.res;
@@ -294,7 +297,7 @@ void gui_renderPilot(Pilot* p) {
 }
 
 // Render a bar.
-void gui_renderBar(Color* c, Vec2* p, Rect* r, double w) {
+static void gui_renderBar(const Color* c, const Vec2* p, const Rect* r, const double w) {
   int x, y, sx, sy;
 
   glBegin(GL_QUADS);
@@ -314,63 +317,232 @@ void gui_renderBar(Color* c, Vec2* p, Rect* r, double w) {
 int gui_init(void) {
   // Font.
   gl_fontInit(&gui.smallFont, NULL, 10);
-  // -- Targeting.
-  gui.gfx_targetPilot = gl_newSprite(GFX_GUI_TARG_PILOT, 2, 2);
-  gui.gfx_targetPlanet = gl_newSprite(GFX_GUI_TARG_PLANET, 2, 2);
-  // -- Frame.
-  gui.gfx_frame = gl_newImage(GFX_GUI_FRAME);
-  vect_csetmin(&gui.pos_frame,
-        gl_screen.w - gui.gfx_frame->w,   // x.
-        gl_screen.h - gui.gfx_frame->h);  // y.
-  gui_xoff = -gui.gfx_frame->w/2.; // Offset is only horizontal and on the right side.
-
   // -- Radar.
   gui.radar.res = RADAR_RES_DEFAULT;
-  gui.radar.w   = 128.;
-  gui.radar.h   = 128.;
-  gui.radar.shape = RADAR_RECT; //RADAR_CIRCLE;
-  vect_csetmin(&gui.pos_radar,
-        VX(gui.pos_frame) + 11,                       // x
-        VY(gui.pos_frame) + gui.gfx_frame->h - 10);   // y.
-
-  // -- Bars.
-  gui.shield.w = gui.armor.w = gui.energy.w = 129;
-  gui.shield.h = gui.armor.h = gui.energy.h = 10;
-  vect_csetmin(&gui.pos_shield,
-        VX(gui.pos_frame) + 10,                       // x
-        VY(gui.pos_frame) + gui.gfx_frame->h - 201);  // y.
-  
-  vect_csetmin(&gui.pos_armor,
-        VX(gui.pos_frame) + 10,                       // x
-        VY(gui.pos_frame) + gui.gfx_frame->h - 218);  // y.
-  
-  vect_csetmin(&gui.pos_energy,
-        VX(gui.pos_frame) + 10,                       // x
-        VY(gui.pos_frame) + gui.gfx_frame->h - 236);  // y.
-
-  // Target.
-  vect_csetmin(&gui.pos_target,
-        VX(gui.pos_frame) + 10,
-        VY(gui.pos_frame) + gui.gfx_frame->h - 256 - SHIP_TARGET_H);
-
-  vect_csetmin(&gui.pos_target_name,
-        VX(gui.pos_target) + 10,
-        VY(gui.pos_target) + SHIP_TARGET_H - 10 - gl_defFont.h);
-
-  vect_csetmin(&gui.pos_target_faction,
-        VX(gui.pos_target_name),
-        VY(gui.pos_target_name) - gui.smallFont.h - 4);
-
-  vect_csetmin(&gui.pos_target_health,
-        VX(gui.pos_target) + 10,
-        VY(gui.pos_target) +10);
-
-  // Message system.
-  vect_csetmin(&gui.pos_msg, 20, 30);
+  // -- messages.
+  vect_csetmin(&gui.pos_msg, 20,30);
   msg_stack = calloc(msg_max, sizeof(Msg));
 
   return 0;
+}
 
+// Attempts to load the actual gui.
+int gui_load(const char* name) {
+  uint32_t bufsize;
+  char* buf = pack_readfile(DATA, GUI_DATA, &bufsize);
+  char* tmp;
+  int found = 0;
+
+  xmlNodePtr node;
+  xmlDocPtr doc = xmlParseMemory(buf, bufsize);
+
+  node = doc->xmlChildrenNode;
+  if(strcmp((char*)node->name, XML_GUI_ID)) {
+    ERR("Malformed '"GUI_DATA"' file: missing root element '"XML_GUI_ID"'");
+    return -1;
+  }
+
+  node = node->xmlChildrenNode; // First system node.
+  if(node == NULL) {
+    ERR("Malformed '"GUI_DATA"' file: does not contain elements");
+    return -1;
+  }
+
+  do {
+    if(node->type == XML_NODE_START && strcmp((char*)node->name, XML_GUI_TAG)==0) {
+      tmp = (char*)xmlGetProp(node, (xmlChar*)"name"); // Mallocs.
+
+      // Is this the gui we are looking for?
+      if(strcmp(tmp, name)==0) {
+        found = 1;
+
+        // Parse the xml node.
+        if(gui_parse(node, name)) WARN("Trouble loading GUI '%s'", name);
+        free(tmp);
+        break;
+      }
+      free(tmp);
+    }
+  } while((node = node->next));
+
+  xmlFreeDoc(doc);
+  free(buf);
+  xmlCleanupParser();
+
+  if(!found) {
+    WARN("GUI '%s' not found in '"GUI_DATA"'",name);
+    return -1;
+  }
+  return 0;
+}
+
+static void rect_parse(const xmlNodePtr parent, double* x, double* y, double* w, double* h) {
+  xmlNodePtr cur;
+  int param;
+
+  param = 0;
+
+  cur = parent->children;
+  
+  do {
+    if(strcmp((char*)cur->name, "x")==0) {
+      if(x != NULL) {
+        *x = (double)atoi((char*)cur->children->content);
+        param |= (1<<0);
+      } else
+        WARN("Extra parameter 'x' found for GUI node '%s'", parent->name);
+    }
+    else if(strcmp((char*)cur->name, "y")==0) {
+      if(y != NULL) {
+        *y = (double)atoi((char*)cur->children->content);
+        param |= (1<<1);
+      } else
+        WARN("Extra parameter 'y' found for GUI node '%s'", parent->name);
+    }
+    else if(strcmp((char*)cur->name, "w")==0) {
+      if(w != NULL) {
+        *w = (double)atoi((char*)cur->children->content);
+        param |= (1<<2);
+      } else
+        WARN("Extra parameter 'w' found for GUI node '%s'", parent->name);
+    }
+    else if(strcmp((char*)cur->name, "h")==0) {
+      if(h != NULL) {
+        *h = (double)atoi((char*)cur->children->content);
+        param |= (1<<3);
+      } else
+        WARN("Extra parameter 'h' found for GUI node '%s'", parent->name);
+    }
+  } while((cur = cur->next));
+
+  // Check to see if we got everything we asked for.
+  if(x && !(param & (1<<0)))
+    WARN("Missing parameter 'x' for GUI node '%s'", parent->name);
+  else if(y && !(param & (1<<1)))
+    WARN("Missing parameter 'y' for GUI node '%s'", parent->name);
+  else if(w && !(param & (1<<2)))
+    WARN("Missing parameter 'w' for GUI node '%s'", parent->name);
+  else if(h && !(param & (1<<3)))
+    WARN("Missing parameter 'h' for GUI node '%s'", parent->name);
+}
+
+// Parse a gui node.
+static int gui_parse(const xmlNodePtr parent, const char* name) {
+  xmlNodePtr cur, node;
+  double x, y;
+  char* tmp, *tmp2;
+
+  // Gfx.
+  // Set a property and not a node because it must be loaded first.
+  tmp2 = (char*)xmlGetProp(parent, (xmlChar*)"gfx");
+  if(tmp2 == NULL) {
+    ERR("GUI '%s' has no gfx property", name);
+    return -1;
+  }
+  // Load gfx.
+  tmp = malloc((strlen(tmp2)+strlen(GUI_GFX)+12) * sizeof(char));
+  // Frame.
+  snprintf(tmp, strlen(tmp2)+strlen(GUI_GFX)+5, GUI_GFX"%s.png", tmp2);
+  gui.gfx_frame = gl_newImage(tmp);
+  // Pilot.
+  snprintf(tmp, strlen(tmp2)+strlen(GUI_GFX)+11, GUI_GFX"%s_pilot.png", tmp2);
+  gui.gfx_targetPilot = gl_newSprite(tmp, 2, 2);
+  // Planet.
+  snprintf(tmp, strlen(tmp2)+strlen(GUI_GFX)+12, GUI_GFX"%s_planet.png", tmp2);
+  gui.gfx_targetPlanet = gl_newSprite(tmp, 2, 2);
+  free(tmp);
+  free(tmp2);
+
+  // Frame (based on gfx).
+  vect_csetmin(&gui.pos_frame,
+        gl_screen.w - gui.gfx_frame->w,  // x.
+        gl_screen.h - gui.gfx_frame->h); // h.
+
+  // For rendering the player. Displaces it a little so it's centered onscreen.
+  gui_xoff = - gui.gfx_frame->w/2.;
+
+  // Let's parse the data now.
+  node = parent->children;
+  do {
+    if(strcmp((char*)node->name, "radar")==0) {
+      tmp = (char*)xmlGetProp(node, (xmlChar*)"type");
+
+      // Make sure type is valid.
+      if(strcmp(tmp, "rectangle")==0) gui.radar.shape = RADAR_RECT;
+      else if(strcmp(tmp, "circle")==0) gui.radar.shape = RADAR_CIRCLE;
+      else {
+        WARN("Radar for GUI '%s' is missing 'type' tag or has invalid 'type' tag", name);
+        gui.radar.shape = RADAR_RECT;
+      }
+      free(tmp);
+
+      rect_parse(node, &x, &y, &gui.radar.w, &gui.radar.h);
+      vect_csetmin(&gui.pos_radar,
+            VX(gui.pos_frame) + x,
+            VY(gui.pos_frame) + gui.gfx_frame->h - y);
+    }
+    // Health bars.
+    else if(strcmp((char*)node->name, "health")==0) {
+      cur = node->children;
+      do {
+        if(strcmp((char*)cur->name, "shield")==0) {
+          rect_parse(cur, &x, &y, &gui.shield.w, &gui.shield.h);
+          vect_csetmin(&gui.pos_shield,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y);
+        }
+      
+        if(strcmp((char*)cur->name, "armor")==0) {
+          rect_parse(cur, &x, &y, &gui.armor.w, &gui.armor.h);
+          vect_csetmin(&gui.pos_armor,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y);
+        }
+      
+        if(strcmp((char*)cur->name, "energy")==0) {
+          rect_parse(cur, &x, &y, &gui.energy.w, &gui.energy.h);
+          vect_csetmin(&gui.pos_energy,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y);
+        }
+      } while((cur = cur->next));
+    }
+    // Target.
+    else if(strcmp((char*)node->name, "target")==0) {
+      cur = node->children;
+      do {
+        if(strcmp((char*)cur->name, "gfx")==0) {
+          rect_parse(cur, &x, &y, NULL, NULL);
+          vect_csetmin(&gui.pos_target,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y - SHIP_TARGET_H);
+        }
+      
+        if(strcmp((char*)cur->name, "name")==0) {
+          rect_parse(cur, &x, &y, NULL, NULL);
+          vect_csetmin(&gui.pos_target_name,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y - gl_defFont.h);
+        }
+      
+        if(strcmp((char*)cur->name, "faction")==0) {
+          rect_parse(cur, &x, &y, NULL, NULL);
+          vect_csetmin(&gui.pos_target_faction,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y - gui.smallFont.h);
+        }
+
+        if(strcmp((char*)cur->name, "health")==0) {
+          rect_parse(cur, &x, &y, NULL, NULL);
+          vect_csetmin(&gui.pos_target_health,
+          VX(gui.pos_frame) + x,
+          VY(gui.pos_frame) + gui.gfx_frame->h - y - gui.smallFont.h);
+        }
+      } while((cur = cur->next));
+    }
+  } while((node = node->next));
+
+  return 0;
 }
 
 // Free the GUI.
diff --git a/src/ship.c b/src/ship.c
index 417a121..39084b1 100644
--- a/src/ship.c
+++ b/src/ship.c
@@ -59,6 +59,8 @@ static Ship* ship_parse(xmlNodePtr parent) {
       tmp->gfx_target = gl_newImage(str);
 
     }
+    else if(strcmp((char*)node->name, "GUI")==0)
+      tmp->gui = strdup((char*)node->children->content);
     else if(strcmp((char*)node->name, "class")==0)
       tmp->class = atoi((char*)node->children->content);
     else if(strcmp((char*)node->name, "movement")==0) {
diff --git a/src/ship.h b/src/ship.h
index 0e7cbcd..0c52b32 100644
--- a/src/ship.h
+++ b/src/ship.h
@@ -34,6 +34,9 @@ typedef struct {
   // Graphics.
   gl_texture* gfx_space, *gfx_target;
 
+  // GUI interface.
+  char* gui;
+  
   // Characteristics.
   int crew;
   int mass;