From d1c02773bf2ca24e70a46297efef0a4742f585ec Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Sat, 9 Feb 2013 22:06:02 +0000
Subject: [PATCH] [Add] Enemies are now propperly parsed. [Add] Chance for
 pirate to come and rampage. [Fix] Actually got that buffer overflow in ai.

---
 dat/fleet.xml           |  11 ++-
 dat/ship.xml            |   2 +-
 dat/ssys.xml            |   7 +-
 scripts/ai/merchant.lua |  26 +++---
 scripts/ai/pirate.lua   |  71 +++++++++++++++++
 scripts/ai/test.lua     |  33 ++++----
 src/ai.c                |  36 +++++++--
 src/faction.c           | 170 +++++++++++++++++++++++++++++++---------
 src/pilot.c             |   4 +-
 src/player.c            |   2 +-
 src/space.c             |   2 +-
 11 files changed, 283 insertions(+), 81 deletions(-)
 create mode 100644 scripts/ai/pirate.lua

diff --git a/dat/fleet.xml b/dat/fleet.xml
index fc5de4b..756a9ce 100644
--- a/dat/fleet.xml
+++ b/dat/fleet.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Fleets>
-  <fleet name="Pirate">
+  <fleet name="Enemy Test">
     <ai>test</ai>
     <faction>Independent</faction>
       <pilots>
-        <pilot chance='100'>Pirate</pilot>
+        <pilot chance='100'>Enemy Test</pilot>
       </pilots>
   </fleet>
   <fleet name="Merchant Ship">
@@ -24,4 +24,11 @@
       <pilot chance='60'>Merchant Ship</pilot>
     </pilots>
   </fleet>
+  <fleet name="Pirate">
+    <ai>pirate</ai>
+    <faction>Pirate</faction>
+    <pilots>
+      <pilot chance='100'>Merchant Ship</pilot>
+    </pilots>
+  </fleet>
 </Fleets>
diff --git a/dat/ship.xml b/dat/ship.xml
index e73cf36..7c31634 100644
--- a/dat/ship.xml
+++ b/dat/ship.xml
@@ -26,7 +26,7 @@
 			<outfit quantity='3'>laser</outfit>
 		</outfits>
   </ship>
-	<ship name="Pirate">
+	<ship name="Enemy Test">
 		<GFX>ship1</GFX>
 		<class>1</class>
 		<movement>
diff --git a/dat/ssys.xml b/dat/ssys.xml
index 93f9d94..2833111 100644
--- a/dat/ssys.xml
+++ b/dat/ssys.xml
@@ -14,12 +14,13 @@
 			<planet>KonoSphere</planet>
 		</planets>
 		<fleets>
-			<fleet chance="100">Merchant Ship</fleet>
+			<fleet chance="100">Enemy Test</fleet>
+      <fleet chance="60">Pirate</fleet>
 			<fleet chance="60">Merchant Ship</fleet>
 			<fleet chance="50">Merchant Ship</fleet>
 			<fleet chance="40">Merchant Ship</fleet>
-			<fleet chance="50">Pirate</fleet>
-			<fleet chance="40">Pirate</fleet>
+			<fleet chance="50">Sml Merchant Convoy</fleet>
+			<fleet chance="40">Sml Merchant Convoy</fleet>
 		</fleets>
 </ssys>
 	<ssys name="KonoSys">
diff --git a/scripts/ai/merchant.lua b/scripts/ai/merchant.lua
index d070991..d5ace3c 100644
--- a/scripts/ai/merchant.lua
+++ b/scripts/ai/merchant.lua
@@ -4,7 +4,7 @@ control_rate = 2
 -- Required "control" function.
 function control()
   if taskname() == "none" then
-    local planet = getrndplanet()
+    planet = getrndplanet()
     pushtask(0, "go", planet)
   end
 end
@@ -13,13 +13,13 @@ end
 function attacked(attacker)
   if taskname() ~= "runaway" then
     -- Let's have some messages.
-    if attacker == player then
-      local msg = rng(0,4)
-      if msg == 0 then say("ARGH! Please don't hurt me.")
-      elseif msg == 1 then say("HEY! We are simply a merchant vessle.")
-      elseif msg == 2 then say("LEAVE! ME! ALONE!")
-      end
+    num = rng(0,3)
+    if num == 0 then msg = "Mayday! We are under attack!"
+    elseif num == 1 then msg = "Requesting assistance! Some scoundral is attacking us!"
+    elseif num == 2 then msg = "Merchant vessle under attack here! HALP!"
     end
+    if msg then broadcast(msg) end
+
     -- So bravely run away!
     pushtask(0, "runaway", attacker)
   end
@@ -27,17 +27,17 @@ end
 
 -- Runs away.
 function runaway()
-  local target = gettargetid()
-  local dir = face(target, 1)
+  target = gettargetid()
+  dir = face(target, 1)
   accel()
 end
 
 -- Fly to the target.
 function go()
-  local target = gettarget()
-  local dir = face(target)
-  local dist = getdist(target)
-  local bdist = minbrakedist()
+  target = gettarget()
+  dir = face(target)
+  dist = getdist(target)
+  bdist = minbrakedist()
   if dir < 10 and dist > bdist then
     accel()
   elseif dir < 10 and dist < bdist then
diff --git a/scripts/ai/pirate.lua b/scripts/ai/pirate.lua
new file mode 100644
index 0000000..6ac186e
--- /dev/null
+++ b/scripts/ai/pirate.lua
@@ -0,0 +1,71 @@
+--Required control rate.
+control_rate = 2
+
+-- Required "control" function.
+function control()
+  if taskname() ~= "attack" then
+    enemy = getenemy()
+    if enemy ~= -1 then
+      pushtask(0, "attack", enemy)
+    else
+      pushtask(0, "fly")
+    end
+  end
+end
+
+-- Required "attacked" function
+function attacked(attacker)
+  task = taskname()
+  if task ~= "attack" and task ~= "runaway" then
+    taunt()
+    pushtask(0, "attack", attacker)
+  elseif task == "attack" then
+    if gettargetid() ~= attacker then
+      pushtask(0, "attack", attacker)
+    end
+  end
+end
+
+function taunt()
+  num = rng(0,4)
+  if num == 0 then msg = "How dare you attack me?!"
+  elseif num == 1 then msg = "Aha! You think you can beat ME?!"
+  elseif num == 2 then msg = "JUST! DIE!"
+  elseif num == 3 then msg = "Ohh, You're not going to enjoy this!"
+  end
+  if msg then comm(attacker, msg) end
+end
+
+-- Run away.
+function runaway()
+  target = gettargerid()
+  dir = face(target, 1)
+  accel()
+end
+
+-- Attack.
+function attack()
+  target = gettargetid()
+  dir = face(target)
+  dist = getdist(getpos(target))
+
+  if parmor() < 70 then
+    poptask()
+    pushtask(0, "runaway", target)
+  elseif dir < 10 and dist > 300 then
+    accel()
+  elseif dir < 10 and dist < 300 then
+    shoot()
+  end
+end
+
+-- Fly to the player.
+function fly()
+  target = 0
+  dir = face(target)
+  dist = getdist(getpos(target))
+  if dir < 10 and dist > 300 then
+    accel()
+  end
+end
+
diff --git a/scripts/ai/test.lua b/scripts/ai/test.lua
index 0f96661..0437106 100644
--- a/scripts/ai/test.lua
+++ b/scripts/ai/test.lua
@@ -10,32 +10,33 @@ end
 
 -- Required "attacked" function.
 function attacked(attacker)
-  if taskname() ~= "attack" and task ~= "runaway" then
+  task = taskname()
+  if task ~= "attack" and task ~= "runaway" then
     -- Let's have some taunts.
-    if attacker == player then
-      local msg = rng(0,4)
-      if msg == 0 then say("You will never kill me!")
-      elseif msg == 1 then say("DIE!")
-      elseif msg == 2 then say("You won't survive!")
-      elseif msg == 3 then say("I hate you!")
-      end
+      num = rng(0,4)
+      if num == 0 then msg = "You will never kill me!"
+      elseif num == 1 then msg = "DIE!"
+      elseif num == 2 then msg = "You won't survive!"
+      elseif num == 3 then msg = "I hate you!"
     end
+    if msg then comm(attacker, msg) end
+
     pushtask(0, "attack", attacker)
   end
 end
 
 -- Runs away.
 function runaway()
-  local target = gettargetid()
-  local dir = face(target, 1)
+  target = gettargetid()
+  dir = face(target, 1)
   accel()
 end
 
 -- Attack
 function attack()
-  local target = gettargetid()
-  local dir = face(target)
-  local dist = getdist(getpos(target))
+  target = gettargetid()
+  dir = face(target)
+  dist = getdist(getpos(target))
 
   if parmor() < 70 then
     poptask()
@@ -49,9 +50,9 @@ end
 
 -- Fly to the player.
 function fly()
-  local target = 0
-  local dir = face(target)
-  local dist = getdist(getpos(target))
+  target = 0
+  dir = face(target)
+  dist = getdist(getpos(target))
   if dir < 10 and dist > 300 then
     accel()
   end
diff --git a/src/ai.c b/src/ai.c
index 108da4a..ece568d 100644
--- a/src/ai.c
+++ b/src/ai.c
@@ -68,6 +68,10 @@ static int nprofiles = 0;
 // Current AI Lua interpreter.
 static lua_State* L = NULL;
 
+// Extern pilot hacks.
+extern Pilot** pilot_stack;
+extern int pilots;
+
 static int ai_minbrakedist(lua_State* L); // Minimal breaking distance.
 static int ai_accel(lua_State* L); // Accelerate.
 
@@ -111,7 +115,8 @@ static int ai_settimer(lua_State* L);           // settimer(number, number)
 static int ai_timeup(lua_State* L);             // bool timeup(number)
 // Misc.
 static int ai_createvect(lua_State* L);         // createvect(number, number)
-static int ai_say(lua_State* L);                // say(string)
+static int ai_comm(lua_State* L);               // comm(string)
+static int ai_broadcast(lua_State* L);          // broadcast(string)
 static int ai_rng(lua_State* L);                // rng(number, number)
 
 // Current pilot "thinking" and assorted variables.
@@ -211,7 +216,8 @@ static int ai_loadProfile(char* filename) {
   lua_register(L, "timeup",             ai_timeup);
   // Misc.
   lua_register(L, "createvect",         ai_createvect);
-  lua_register(L, "say",                ai_say);
+  lua_register(L, "comm",               ai_comm);
+  lua_register(L, "broadcast",          ai_broadcast);
   lua_register(L, "rng",                ai_rng);
 
 
@@ -583,7 +589,17 @@ static int ai_shoot(lua_State* L) {
 
 // Get the nearest enemy.
 static int ai_getenemy(lua_State* L) {
-  lua_pushnumber(L,1);
+  int i, p;
+  double d, td;
+  for(p = -1, i = 0; i < pilots; i++)
+    if(areEnemies(cur_pilot->faction, pilot_stack[i]->faction)) {
+      td = vect_dist(&pilot_stack[i]->solid->pos, &cur_pilot->solid->pos);
+      if((p == -1) || (td < d)) {
+        d = td;
+        p = pilot_stack[i]->id;
+      }
+    }
+  lua_pushnumber(L,p);
   return 1;
 }
 
@@ -623,11 +639,21 @@ static int ai_createvect(lua_State* L) {
 }
 
 // Have the pilot say something to player.
-static int ai_say(lua_State* L) {
+static int ai_comm(lua_State* L) {
+  MIN_ARGS(2);
+  
+  if(lua_isnumber(L,1) && (lua_tonumber(L,1)==PLAYER_ID) && lua_isstring(L,2))
+    player_message("Comm: %s> \"%s\"", cur_pilot->name, lua_tostring(L,2));
+
+  return 0;
+}
+
+// Broadcasts to the entire area.
+static int ai_broadcast(lua_State* L) {
   MIN_ARGS(1);
 
   if(lua_isstring(L, 1))
-    player_message("Comm: %s> \"%s\"", cur_pilot->name, lua_tostring(L, 1));
+    player_message("Broadcast: %s> \"%s\"", cur_pilot->name, lua_tostring(L, 1));
 
   return 0;
 }
diff --git a/src/faction.c b/src/faction.c
index 7079123..82a6f68 100644
--- a/src/faction.c
+++ b/src/faction.c
@@ -22,9 +22,21 @@
 Faction* faction_stack = NULL;
 int nfactions = 0;
 
+// Save alliance.
+typedef struct {
+  char* name;
+  Faction** factions;
+  int nfactions;
+} Alliance;
+
+// Stack of alliances.
+static Alliance* alliances = NULL;
+static int nalliances = 0;
+
 static Faction* faction_parse(xmlNodePtr parent);
 static void alliance_parse(xmlNodePtr parent);
 static void enemies_parse(xmlNodePtr parent);
+static Alliance* alliance_get(char* name);
 
 // Return the faction of name "name".
 Faction* faction_get(const char* name) {
@@ -39,6 +51,19 @@ Faction* faction_get(const char* name) {
   return NULL;
 }
 
+// Return the alliance of name 'name'.
+static Alliance* alliance_get(char* name) {
+  int i;
+  for(i = 0; i < nalliances; i++)
+    if(strcmp(alliances[i].name, name)==0)
+      break;
+
+  if(i != nalliances)
+    return alliances+i;
+
+  return NULL;
+}
+
 // Return 1 if Faction a and b are enemies.
 int areEnemies(Faction* a, Faction* b) {
   int i = 0;
@@ -82,67 +107,127 @@ static Faction* faction_parse(xmlNodePtr parent) {
 
 // We set allies/enemies here, in the faction_stack.
 static void alliance_parse(xmlNodePtr parent) {
-  Faction** f = NULL;
-  int i, j, n, m;
-  i = 0;
-  char* name = NULL;
+  Alliance* a;
+  int* i, j, n, m;
   xmlNodePtr node, cur;
 
   node = parent->xmlChildrenNode;
 
   do {
-    if(node->type == XML_NODE_START) {
-      if(strcmp((char*)node->name, XML_ALLIANCE_TAG)==0) {
-        name = (char*)xmlGetProp(node, (xmlChar*)"name");
+    if((node->type == XML_NODE_START) && (strcmp((char*)node->name, XML_ALLIANCE_TAG)==0)) {
+      // Allocate a new alliance.
+      alliances = realloc(alliances, sizeof(Alliance)*(++nalliances));
+      alliances[nalliances-1].name = (char*)xmlGetProp(node,(xmlChar*)"name");
+      alliances[nalliances-1].factions = NULL;
+      alliances[nalliances-1].nfactions = 0;
+      
+      // Parse the current alliance's allies.
+      cur = node->xmlChildrenNode;
+      do {
+        if(strcmp((char*)cur->name, "ally")==0) {
+          // Add the faction (and pointers to make things simple).
+          a = alliances + nalliances-1;
+          i = &a->nfactions;
+          (*i)++;
 
-        // Parse the current alliance's allies.
-        cur = node->xmlChildrenNode;
-        do {
-          if(strcmp((char*)cur->name, "ally")==0) {
-            f = realloc(f, (++i)*sizeof(Faction*));
-            f[i-1] = faction_get((char*)cur->children->content);
-            if(f[i-1] == NULL)
-              WARN("Faction %s in alliance %s does not exist in "FACTION_DATA,
-                    (char*)cur->children->content, name);
-          }
-        }while((cur = cur->next));
+          // Load the faction.
+          a->factions = realloc(a->factions, (*i)*sizeof(Faction*));
+          a->factions[(*i)-1] = faction_get((char*)cur->children->content);
+
+          if(a->factions[(*i)-1] == NULL)
+            WARN("Faction %s in alliance %s does not exist in "FACTION_DATA,
+                  (char*)cur->children->content, a->name);
+        }
+      } while((cur = cur->next));
 
         // Set the crap needed by faction_stack.
-        for(j = 0; j < i; j++) {
-          f[j]->nallies += i-1;
-          f[j]->allies = realloc(f[j]->allies, f[j]->nallies*sizeof(Faction*));
-          for(n = 0, m = 0; n < i; n++, m++) {
+        for(j = 0; j < (*i); j++) {
+          a->factions[j]->nallies += (*i)-1;
+          a->factions[j]->allies = realloc(a->factions[j]->allies, a->factions[j]->nallies*sizeof(Faction*));
+          for(n = 0, m = 0; n < (*i); n++, m++) {
             // Add as ally for all factions exept self.
             if(n == j) m--;
-            else if(n != j) f[j]->allies[f[j]->nallies-i+1+m] = f[n];
+            else if(n != j) 
+              a->factions[j]->allies[a->factions[j]->nallies-(*i)+1+m] = a->factions[n];
           }
         }
-        // Free up some memory.
-        if(f) {
-          free(f);
-          f = NULL;
-          i = 0;
-        }
-        if(name) free(name);
       }
-    }
   } while((node = node->next));
 }
 
 static void enemies_parse(xmlNodePtr parent) {
-  xmlNodePtr node;
+  xmlNodePtr node, cur;
+  Faction*** f;
+  Alliance* a;
+  int i, *j, n, m, x, y, z, e;
+  char* type;
+
+  i = 0;
+  f = NULL;
+  j = NULL;
 
   node = parent->xmlChildrenNode;
 
   do {
-    if(node->type == XML_NODE_START) {
-      if(strcmp((char*)node->name, XML_ENEMIES_TAG)==0) {
-      
-      }
+    if((node->type == XML_NODE_START) && (strcmp((char*)node->name, XML_ENEMIES_TAG)==0)) {
+      cur = node->xmlChildrenNode;
+      do {
+        if(strcmp((char*)cur->name,"enemy")==0) {
+          type = (char*)xmlGetProp(cur, (xmlChar*)"type");
+
+          i++;
+          j = realloc(j, sizeof(int)*i);
+          f = realloc(f, sizeof(Faction**)*i);
+
+          if(strcmp(type, "alliance")==0) {
+            // Enemy thing is an alliance.
+            a = alliance_get((char*)cur->children->content);
+            if(a == NULL)
+              WARN("Alliance %s not found in stack", (char*)cur->children->content);
+            j[i-1] = a->nfactions;
+            f[i-1] = a->factions;
+          }
+          else if(strcmp(type,"faction")==0) {
+            // Enemy thing is only a faction.
+            j[i-1] = 1;
+            f[i-1] = malloc(sizeof(Faction*));
+            f[i-1][0] = faction_get((char*)cur->children->content);
+            if(f[i-1][0] == NULL)
+              WARN("Faction %s not found in stack", (char*)cur->children->content);
+          }
+          free(type);
+        }
+      } while((cur = cur->next));
+        // Now actually parse and load up the enemies.
+        for(n = 0; n < i; n++) {
+          for(m = 0; m < j[n]; m++) {
+            // Faction.
+            // Add all the faction enemies to nenemies and alloc.
+            for(e = 0, x = 0; x < i; x++)
+              if(x != n) e += j[x]; // Store the total enemies.
+                // Now allocate the memory.
+                f[n][m]->nenemies += e;
+            f[n][m]->enemies = realloc(f[n][m]->enemies, sizeof(Faction*)*f[n][m]->nenemies);
+           
+            // Add the actualy enemies.
+            for(x = 0, z = 0; x < i; x++)
+              if(x != n)
+                // Make sure it's not from the same group.
+                if(x != n)
+                  for(y = 0; y < j[x]; y++, z++)
+                    f[n][m]->enemies[f[n][m]->nenemies-e+z]=f[x][y];
+          }
+        }
+        // Free al the temp memory.
+        for(x = 0; x < i; x++)
+          if(j[x]==1) free(f[x]); // Free the single malloced factions.
+      free(f); // Free the rest.
+      free(j);
     }
   } while((node = node->next));
 }
 
+
 // Load all the factions.
 int factions_load(void) {
   uint32_t bufsize;
@@ -184,19 +269,30 @@ int factions_load(void) {
   free(buf);
   xmlCleanupParser();
 
-  DEBUG("Loaded %d factions%c", nfactions, (nfactions==1)?' ':'s');
+  DEBUG("Loaded %d faction%c", nfactions, (nfactions==1)?' ':'s');
 
   return 0;
 }
 
 void factions_free(void) {
   int i;
+  // Free alliances.
+  for(i = 0; i < nalliances; i++) {
+    free(alliances[i].name);
+    free(alliances[i].factions);
+  }
+  free(alliances);
+  alliances = NULL;
+  nalliances = 0;
+
+  // Free factions.
   for(i = 0; i < nfactions; i++) {
     free(faction_stack[i].name);
     if(faction_stack[i].nallies > 0)  free(faction_stack[i].allies);
     if(faction_stack[i].nenemies > 0) free(faction_stack[i].enemies);
   }
   free(faction_stack);
+  faction_stack = NULL;
   nfactions = 0;
 }
 
diff --git a/src/pilot.c b/src/pilot.c
index 1024d35..3b7e442 100644
--- a/src/pilot.c
+++ b/src/pilot.c
@@ -23,7 +23,7 @@
 static unsigned int pilot_id = 0;
 
 // Stack of pilots - yes, they come in stacks now.
-Pilot** pilot_stack = NULL; // Not static, it is used in player.c and weapon.c
+Pilot** pilot_stack = NULL; // Not static, it is used in player.c and weapon.c and ai.c
 int pilots = 0;
 extern Pilot* player;
 
@@ -389,7 +389,7 @@ int fleet_load(void) {
   free(buf);
   xmlCleanupParser();
 
-  DEBUG("Loaded %d fleets%c", nfleets, (nfleets==1)?' ':'s');
+  DEBUG("Loaded %d fleet%c", nfleets, (nfleets==1)?' ':'s');
 
   return 0;
 }
diff --git a/src/player.c b/src/player.c
index 0fcb03e..8d0734c 100644
--- a/src/player.c
+++ b/src/player.c
@@ -312,7 +312,7 @@ int gui_init(void) {
         VY(gui.pos_frame) + gui.gfx_frame->h - 10);   // y.
 
   // -- Bars.
-  gui.shield.w = gui.armor.w = gui.energy.w = 128;
+  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
diff --git a/src/space.c b/src/space.c
index 594d406..7284ad1 100644
--- a/src/space.c
+++ b/src/space.c
@@ -355,7 +355,7 @@ static StarSystem* system_parse(const xmlNodePtr parent) {
   MELEMENT(flags&FLAG_ASTEROIDSSET, "asteroids"); // Can be 0.
   MELEMENT(flags&FLAG_INTEFERENCESET, "inteference");
 #undef MELEMENT
-  DEBUG("Loaded Star System '%s' with %d Planets%s", tmp->name, tmp->nplanets, (tmp->nplanets > 1) ? "s" : "");
+  DEBUG("Loaded Star System '%s' with %d Planet%c", tmp->name, tmp->nplanets, (tmp->nplanets == 1) ? ' ' : 's');
 
   return tmp;
 }