From acf4150079e9e4c669ef45d8fb25a10b9fb66ed4 Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Fri, 8 Feb 2013 18:29:07 +0000
Subject: [PATCH] [Add] Per pixel collision. [Fix] Fleets now start at random
 location.     -- Still needs improvement.

---
 scripts/ai/test.lua |  4 +--
 src/ai.c            |  6 +++-
 src/collision.c     | 50 +++++++++++++++++++++++++++
 src/collision.h     |  7 ++++
 src/opengl.c        | 83 ++++++++++++++++++++++++++++++++++++++++++++-
 src/opengl.h        |  7 +++-
 src/physics.c       |  7 ++++
 src/physics.h       |  1 +
 src/pilot.c         | 10 ++----
 src/space.c         | 15 +++++---
 src/space.h         |  2 ++
 src/weapon.c        | 19 +++++++----
 12 files changed, 188 insertions(+), 23 deletions(-)
 create mode 100644 src/collision.c
 create mode 100644 src/collision.h

diff --git a/scripts/ai/test.lua b/scripts/ai/test.lua
index 7b36389..5678824 100644
--- a/scripts/ai/test.lua
+++ b/scripts/ai/test.lua
@@ -11,9 +11,9 @@ function attack()
   target = 0
   dir = face(target)
   dist = getdist(getpos(target))
-  if dir < 10 and dist > 100 then
+  if dir < 10 and dist > 300 then
     accel()
-  elseif dir < 10 and dist < 100 then
+  elseif dir < 10 and dist < 300 then
     shoot()
   end
 end
diff --git a/src/ai.c b/src/ai.c
index 3e7642f..6b2feea 100644
--- a/src/ai.c
+++ b/src/ai.c
@@ -164,7 +164,11 @@ void ai_exit(void) {
 // Heart of hearts of the ai!! Brains of the pilot.
 void ai_think(Pilot* pilot) {
   cur_pilot = pilot; // Set current pilot being processed.
-  pilot_acc = pilot_turn = 0.; // Clean up some variables.
+  
+  // Clean up some variables.
+  pilot_acc = pilot_turn = 0.;
+  //pilot_primary = 0;
+
   if(cur_pilot->task == NULL)
     // Idle git!
     AI_LCALL("control");
diff --git a/src/collision.c b/src/collision.c
new file mode 100644
index 0000000..7fb6e5c
--- /dev/null
+++ b/src/collision.c
@@ -0,0 +1,50 @@
+#include "main.h"
+#include "log.h"
+#include "collision.h"
+
+// Collide sprite at (asx, asy) int 'at' at pos 'ap' with sprite at (bsx,bsy) in 'bt' at 'bp'
+// at   - Texture a.
+// asx  - Position of x of sprite a.
+// asy  - Position of y of sprite a.
+// ap   - Position in space of sprite a.
+// bt   - Texture b.
+// bsx  - Position of x of sprite b.
+// bsy  - Position of y of sprite b.
+// bp   - Position in space of sprite b.
+int CollideSprite(const gl_texture* at, const int asx, const int asy, const Vec2* ap,
+      const gl_texture* bt, const int bsx, const int bsy, const Vec2* bp) {
+  
+  int x,y;
+
+  // a - cube coords.
+  int ax1 = (int)VX(*ap) - (int)(at->sw)/2;
+  int ay1 = (int)VY(*ap) - (int)(at->sh)/2;
+  int ax2 = ax1 + (int)(at->sw) - 1;
+  int ay2 = ay1 + (int)(at->sh) - 1;
+
+  // b - cube coords.
+  int bx1 = (int)VX(*bp) - (int)(bt->sw)/2;
+  int by1 = (int)VY(*bp) - (int)(bt->sh)/2;
+  int bx2 = bx1 + (int)(bt->sw) - 1;
+  int by2 = by1 + (int)(bt->sh) - 1;
+  
+  // Check if bounding boxes intersect.
+  if((bx2 < ax1) || (ax2 < bx1)) return 0;
+  if((by2 < ay1) || (ay2 < by1)) return 0;
+
+  // Define the remaining binding box.
+  int inter_x0 = MAX(ax1, bx1);
+  int inter_x1 = MIN(ax2, bx2);
+  int inter_y0 = MAX(ay1, by1);
+  int inter_y1 = MIN(ay2, by2);
+
+  for(y = inter_y0; y <= inter_y1; y++)
+    for(x = inter_x0; x <= inter_x1; x++)
+      // Computer offsets for surface before passing to TransparentPixel test.
+      if((!gl_isTrans(at, asx*(int)(at->sw) + x-ax1, asy*(int)(at->sh) + y-ay1)) &&
+            (!gl_isTrans(bt, bsx*(int)(bt->sw) + x-bx1, bsy*(int)(bt->sh) + y-by1)))
+        return 1;
+
+  return 0;
+}
+
diff --git a/src/collision.h b/src/collision.h
new file mode 100644
index 0000000..1d9425b
--- /dev/null
+++ b/src/collision.h
@@ -0,0 +1,7 @@
+#pragma once
+#include "opengl.h"
+#include "physics.h"
+
+int CollideSprite(const gl_texture* at, const int asx, const int asy, const Vec2* ap,
+      const gl_texture* bt, const int bsx, const int bsy, const Vec2* bp);
+
diff --git a/src/opengl.c b/src/opengl.c
index 355f6d5..d7e053f 100644
--- a/src/opengl.c
+++ b/src/opengl.c
@@ -32,6 +32,8 @@ gl_font gl_defFont;
 
 // Misc.
 static int SDL_VFlipSurface(SDL_Surface* surface);
+static int SDL_IsTrans(SDL_Surface* s, int x, int y);
+static uint8_t* SDL_MapTrans(SDL_Surface* s);
 static int pot(int n);
 // gl_texture.
 static GLuint gl_loadSurface(SDL_Surface* surface, int* rw, int* rh);
@@ -76,6 +78,57 @@ static int SDL_VFlipSurface(SDL_Surface* surface) {
   return 0;
 }
 
+// Return true if position (x,y) of s is transparent.
+static int SDL_IsTrans(SDL_Surface* s, int x, int y) {
+  int bpp = s->format->BytesPerPixel;
+  // p is the address to the pixel we want to retrieve.
+  Uint8* p = (Uint8*)s->pixels + y * s->pitch + x*bpp;
+
+  Uint32 pixelcolor = 0;
+
+  switch(bpp) {
+    case 1:
+      pixelcolor = *p;
+      break;
+    case 2:
+      pixelcolor = *(Uint16*)p;
+      break;
+    case 3:
+      if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
+        pixelcolor = p[0] << 16 | p[1] << 8 | p[2];
+      else
+        pixelcolor = p[0] | p[1] << 8 | p[2] << 16;
+      break;
+    case 4:
+      pixelcolor = *(Uint32*)p;
+      break;
+  }
+  // Test whetehr the pixels color is equal to color of 
+  //transparent pixels for that surface.
+  return (pixelcolor == s->format->colorkey);
+}
+
+// Map the surface transparancy.
+// Return 0 on success.
+static uint8_t* SDL_MapTrans(SDL_Surface* s) {
+  // Allocate memory for just enough bits to hold all the data we need.
+  int size = s->w*s->h/8 + ((s->w*s->h%8)?1:0);
+  uint8_t* t = malloc(size);
+  bzero(t, size); // *must* be set to zero.
+
+  if(t == NULL) {
+    WARN("Out of memeory");
+    return NULL;
+  }
+
+  int i, j;
+  for(i = 0; i < s->h; i++)
+    for(j = 0; j < s->w; j++) // Set each bit to be 1 if not transparent or 0 if it is.
+      t[(i*s->w+j)/8] |= (SDL_IsTrans(s,j,i)) ? 0 : (1<<((i*s->w+j)%8));
+
+  return t;
+}
+
 // ================
 // TEXTURE!
 // ================
@@ -187,12 +240,16 @@ gl_texture* gl_loadImage(SDL_Surface* surface) {
   texture->sw = texture->w;
   texture->sh = texture->h;
   
+  texture->trans = NULL;
+
   return texture;
 }
 
 // Load the image directly as an opengl texture.
 gl_texture* gl_newImage(const char* path) {
   SDL_Surface* tmp, *surface;
+  gl_texture* t;
+  void* trans = NULL;
   uint32_t filesize;
   char* buf = pack_readfile(DATA, (char*)path, &filesize);
   if(buf == NULL) {
@@ -216,11 +273,18 @@ gl_texture* gl_newImage(const char* path) {
 
   SDL_FreeSurface(tmp); // Free the temp surface.
 
+  SDL_LockSurface(surface);
+  trans = SDL_MapTrans(surface);
+  SDL_UnlockSurface(surface);
+
   if(SDL_VFlipSurface(surface)) {
     WARN("Error flipping surface");
     return NULL;
   }
-  return gl_loadImage(surface);
+  t = gl_loadImage(surface);
+  t->trans = trans;
+
+  return t;
 }
 
 // Load the texture immediately, but also set is as a sprite.
@@ -238,9 +302,26 @@ gl_texture* gl_newSprite(const char* path, const int sx, const int sy) {
 // Free the texture.
 void gl_freeTexture(gl_texture* texture) {
   glDeleteTextures(1, &texture->texture);
+  if(texture->trans) free(texture->trans);
   free(texture);
 }
 
+// Return true if pixel at pos (x,y) is transparent.
+int gl_isTrans(const gl_texture* t, const int x, const int y) {
+  return !(t->trans[(y*(int)(t->w)+x)/8] & (1<<((y*(int)(t->w)+x)%8)));
+}
+
+// Set x and y to be the appropriate sprite for gl_texture using dir.
+void gl_getSpriteFromDir(int* x, int* y, const gl_texture* t, const double dir) {
+  int s = (int)(dir / (2.0*M_PI / (t->sy*t->sx)));
+
+  // Make sure the sprite is "in range".
+  if(s > (int)(t->sy*t->sx)-1) s = s % (int)(t->sy*t->sx);
+
+  *x = s % (int)t->sx;
+  *y = s / (int)t->sy;
+}
+
 // ================
 // BLITTING!
 // ================
diff --git a/src/opengl.h b/src/opengl.h
index da45e6e..7981103 100644
--- a/src/opengl.h
+++ b/src/opengl.h
@@ -34,7 +34,8 @@ typedef struct {
   double rw, rh;      // Size of POT surface.
   double sx, sy;      // Number of sprites on x and y axes.
   double sw, sh;      // Size of each sprite.
-  GLuint texture; // The opengl texture itself.
+  GLuint texture;     // The opengl texture itself.
+  uint8_t* trans;     // Maps the transparency.
 } gl_texture;
 
 // Font info.
@@ -66,3 +67,7 @@ void gl_print(const gl_font* ft_font, const Vec2* pos, const char* fmt, ...);
 int gl_init(void);
 void gl_exit(void);
 
+// Misc.
+int gl_isTrans(const gl_texture* t, const int x, const int y);
+void gl_getSpriteFromDir(int* x, int* y, const gl_texture* t, const double dir);
+
diff --git a/src/physics.c b/src/physics.c
index e53ce8d..6b7ba13 100644
--- a/src/physics.c
+++ b/src/physics.c
@@ -57,6 +57,13 @@ double vect_angle(const Vec2* ref, const Vec2* v) {
   return ANGLE(VX(*v)-VX(*ref), VY(*v)-VY(*ref));
 }
 
+void vect_cadd(Vec2* v, const double x, const double y) {
+  v->x -= x;
+  v->y -= y;
+  v->mod = MOD(v->x, v->y);
+  v->angle = ANGLE(v->x, v->y);
+}
+
 
 // ================
 // SOLID!
diff --git a/src/physics.h b/src/physics.h
index b95877d..9a2b109 100644
--- a/src/physics.h
+++ b/src/physics.h
@@ -28,6 +28,7 @@ void vect_pset(Vec2* v, const double mod, const double angle);
 void vectcpy(Vec2* dest, const Vec2* src);
 void vectnull(Vec2* v);
 double vect_angle(const Vec2* ref, const Vec2* v);
+void vect_cadd(Vec2* v, const double x, const double y);
 
 // Describe any solid in 2D space.
 struct Solid {
diff --git a/src/pilot.c b/src/pilot.c
index e025219..c63c46e 100644
--- a/src/pilot.c
+++ b/src/pilot.c
@@ -120,16 +120,12 @@ void pilot_hit(Pilot* p, const double damage_shield, const double damage_armor)
 
 // Render the pilot.
 void pilot_render(Pilot* p) {
-  int sprite;
-  gl_texture* t = p->ship->gfx_space;
+  int sx, sy;
 
   // Get the sprite corresponding to the direction facing.
-  sprite = (int)(p->solid->dir / (2.0*M_PI / (t->sy * t->sx)));
-  
-  // Ugly hack to make sure it's always "inbound".
-  if(sprite > (int)(t->sy*t->sx)-1) sprite = (int)(t->sy*t->sx)-1;
+  gl_getSpriteFromDir(&sx, &sy, p->ship->gfx_space, p->solid->dir);
 
-  gl_blitSprite(t, &p->solid->pos, sprite % (int)t->sx, sprite / (int)t->sy);
+  gl_blitSprite(p->ship->gfx_space, &p->solid->pos, sx, sy);
 }
 
 // Update the pilot.
diff --git a/src/space.c b/src/space.c
index bf8077f..ae47341 100644
--- a/src/space.c
+++ b/src/space.c
@@ -196,6 +196,8 @@ static PlanetClass planetclass_get(const char a) {
 // Init the system.
 void space_init(const char* sysname) {
   int i, j;
+  Vec2 v, vn;
+
   for(i = 0; i < nsystems; i++)
     if(strcmp(sysname, systems[i].name)==0)
       break;
@@ -211,17 +213,22 @@ void space_init(const char* sysname) {
     stars[i].y = (double)RNG(-STAR_BUF, gl_screen.h + STAR_BUF);
   }
   // Set up fleets -> pilots.
+  vectnull(&vn);
   for(i = 0; i < cur_system->nfleets; i++)
-    if(RNG(0,100) <= cur_system->fleets[i].chance) // Check fleet.
+    if(RNG(0,100) <= cur_system->fleets[i].chance) {// Check fleet.
+      vect_pset(&v, 2*RNG(MIN_HYPERSPACE_DIST/2, MIN_HYPERSPACE_DIST), RNG(0, 360)*M_PI/180.);
       for(j = 0; j < cur_system->fleets[i].fleet->npilots; j++)
-        if(RNG(0,100) <= cur_system->fleets[i].fleet->pilots[j].chance)
+        if(RNG(0,100) <= cur_system->fleets[i].fleet->pilots[j].chance) {
+          vect_cadd(&v, RNG(-50, 50), RNG(-50, 50));
           pilot_create(cur_system->fleets[i].fleet->pilots[j].ship,
                 cur_system->fleets[i].fleet->pilots[j].name,
                 cur_system->fleets[i].fleet->faction,
-                RNG(0,360),
-                NULL,
+                vect_angle(&v,&vn),
+                &v,
                 NULL,
                 0);
+        }
+    }
 }
 
 // Load the planets of name 'name'.
diff --git a/src/space.h b/src/space.h
index d4f77e9..6d18bd3 100644
--- a/src/space.h
+++ b/src/space.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#define MIN_HYPERSPACE_DIST 1500
+
 // Load/Exit.
 void space_init(const char* sysname);
 int space_load(void);
diff --git a/src/weapon.c b/src/weapon.c
index ed497d3..8cb34bb 100644
--- a/src/weapon.c
+++ b/src/weapon.c
@@ -8,6 +8,7 @@
 #include "log.h"
 #include "rng.h"
 #include "pilot.h"
+#include "collision.h"
 #include "weapon.h"
 
 // Some stuff from pilot.
@@ -102,23 +103,27 @@ void weapons_update(const double dt, WeaponLayer layer) {
 
 // Render the weapons.
 static void weapon_render(const Weapon* w) {
-  int sprite;
-  gl_texture* t = w->outfit->gfx_space;
+  int sx, sy;
 
   // Get the sprite corresponding to the direction facing.
-  sprite = (int)(w->solid->dir / (2.0*M_PI / (t->sy*t->sx)));
+  gl_getSpriteFromDir(&sx, &sy, w->outfit->gfx_space, w->solid->dir);
 
-  gl_blitSprite(t, &w->solid->pos, sprite % (int)t->sx, sprite / (int)t->sy);
+  gl_blitSprite(w->outfit->gfx_space, &w->solid->pos, sx, sy);
 }
 
 // Update the weapon.
 static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) {
-  int i;
+  int i, wsx, wsy, psx, psy;
+  gl_getSpriteFromDir(&wsx, &wsy, w->outfit->gfx_space, w->solid->dir);
+
   for(i = 0; i < pilots; i++) {
+    gl_getSpriteFromDir(&psx, &psy, pilot_stack[i]->ship->gfx_space,
+          pilot_stack[i]->solid->dir);
+
     if((w->parent != pilot_stack[i]->id) && // The pilot hasn't shoot it.
           !areAllies(pilot_get(w->parent)->faction, pilot_stack[i]->faction) &&
-          (DIST(w->solid->pos, pilot_stack[i]->solid->pos) < (PILOT_SIZE_APROX *
-          w->outfit->gfx_space->sw/2. + pilot_stack[i]->ship->gfx_space->sw/2.))) {
+          CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos,
+          pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) {
       pilot_hit(pilot_stack[i], w->outfit->damage_shield, w->outfit->damage_armor);
       weapon_destroy(w, layer);
       return;