#include "allegro.h"

#ifdef _WIN32
#include "winalleg.h"

#include <winsock.h>
#else
#define SOCKET_ERROR -1
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#endif

#include "defs.h"
#include "console.h"
#include "engine.h"
#include "items.h"
#include "rules.h"
#include "sound.h"
#include "weapons.h"
#include "gfx.h"
#include "ai.h"

int medKitPickup;
int medKitRespawn;
int flagPickup;
int flagRespawn;

int itemBlowup;
int itemExplosion;
int padHit;

extern char always1;

void interact::collision(worm *player)
{
   switch (type)
   {
   case ITEM_MEDKIT:
      ((mediKit*)this)->collision(player);
      break;
   case ITEM_WEAPPACK:
      ((weapPack*)this)->collision(player);
      break;
   case ITEM_FLAG:
      ((ctfFlag*)this)->collision(player);
      break;
   case ITEM_DOMINATION:
      ((domFlag*)this)->collision(player);
      break;
   case ITEM_KICKERPAD:
      ((jumpPad*)this)->collision(player);
      break;
   }
};

void interact::blow(worm *player)
{
   switch (type)
   {
   case ITEM_MEDKIT:
      ((mediKit*)this)->blow(player);
      break;
   case ITEM_WEAPPACK:
      ((weapPack*)this)->blow(player);
      break;
//   case ITEM_FLAG:
//      ((ctfFlag*)this)->blow(player);
//      break;
//   case ITEM_DOMINATION:
//      ((domFlag*)this)->blow(player);
//      break;
//   case ITEM_KICKERPAD:
//      ((jumpPad*)this)->blow(player);
//      break;
   }
};

void interact::ownerDead(worm *killer)
{
   switch (type)
   {
//   case ITEM_MEDKIT:
//      ((mediKit*)this)->ownerDead(killer);
//      break;
//   case ITEM_WEAPPACK:
//      ((weapPack*)this)->ownerDead(killer);
//      break;
   case ITEM_FLAG:
      ((ctfFlag*)this)->ownerDead(killer);
      break;
//   case ITEM_DOMINATION:
//      ((domFlag*)this)->ownerDead(killer);
//      break;
//   case ITEM_KICKERPAD:
//      ((jumpPad*)this)->ownerDead(killer);
//      break;
   }
}

void interact::think()
{
   switch (type)
   {
   case ITEM_MEDKIT:
      ((mediKit*)this)->think();
      break;
   case ITEM_WEAPPACK:
      ((weapPack*)this)->think();
      break;
   case ITEM_FLAG:
      ((ctfFlag*)this)->think();
      break;
   case ITEM_DOMINATION:
      ((domFlag*)this)->think();
      break;
   case ITEM_KICKERPAD:
      ((jumpPad*)this)->think();
      break;
   }
};

void interact::draw(gameViewport *where)
{
   switch (type)
   {
   case ITEM_MEDKIT:
      ((mediKit*)this)->draw(where);
      break;
   case ITEM_WEAPPACK:
      ((weapPack*)this)->draw(where);
      break;
   case ITEM_FLAG:
      ((ctfFlag*)this)->draw(where);
      break;
   case ITEM_DOMINATION:
      ((domFlag*)this)->draw(where);
      break;
//   case ITEM_KICKERPAD:
//      ((jumpPad*)this)->draw(where);
//      break;
   }
};

void removeItem(interact *item)
{
   interact *item2;
   
   if (item->prev)
      item->prev->next = item->next;
   if (item->next)
      item->next->prev = item->prev;
   if (item == itemList)
      itemList = item->next;
   item2 = item->next;
   delete item;
   item = item2;
}

void interact::start()
{
   if ((this->type == ITEM_FLAG) && (gameType != GAME_CTF))
   {
      removeItem(this);
      return;
   }
   else if ((this->type == ITEM_DOMINATION) && (gameType != GAME_DOM))
   {
      removeItem(this);
      return;
   }

   this->VelX = this->VelY = 0;
   this->OldX = this->x = (long)this->spawnX * 1000;
   this->OldY = this->y = (long)this->spawnY * 1000;
   this->CurX = this->spawnX;
   this->CurY = this->spawnY;
   this->owner = NULL;
   this->localTime = gameTime;
};

void interact::engineGravity()
{
   this->VelY += this->gravity;
}

void interact::engineTestBorders()
{
   char explo;

      explo = 0;
      if ((this->x <= 3000) && (this->VelX < 0))
      {
         explo = 1;
         this->x = 3000;
         this->VelX = -(this->VelX * this->bounce)/1000;
         this->VelY = (this->VelY * this->bounce)/1000;
      }
      else if ((this->x + 4000>= mapSizeX*1000) && (this->VelX > 0))
      {
         explo = 1;
         this->x = (mapSizeX - 4)*1000;
         this->VelX = -(this->VelX * this->bounce)/1000;
         this->VelY = (this->VelY * this->bounce)/1000;
      }

      if ((this->y <= 3000) && (this->VelY <0))
      {
         explo = 1;
         this->y = 3000;
         this->VelX = (this->VelX * this->bounce)/1000;
         this->VelY = -(this->VelY * this->bounce)/1000;
      }
      else if ((this->y + 4000 >= mapSizeY*1000) && (this->VelY > 0))
      {
         explo = 1;
         this->y = (mapSizeY - 4)*1000;
         this->VelX = (this->VelX * this->bounce)/1000;
         this->VelY = -(this->VelY * this->bounce)/1000;
      }
}

void interact::engineWorldCollision()
{
   int rf_up, rf_down, rf_left, rf_right;
   int i, color;

   rf_up = 0;

   rf_down = 0;
   for (i=-1; i<2; i++)
   {
      color = getpixel(material, this->CurX+i, this->CurY-3);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) rf_down++;
   }

   for (i=-1; i<2; i++)
   {
      color = getpixel(material, this->CurX+i, this->CurY+1);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) rf_up++;
   }

   rf_left = 0;
   for (i=-2; i<1; i++)
   {
      color = getpixel(material, this->CurX+2, this->CurY+i);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) rf_left++;
   }

   rf_right = 0;
   for (i=-2; i<1; i++)
   {
      color = getpixel(material, this->CurX-2, this->CurY+i);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) rf_right++;
   }

   if (((rf_left>rf_right) || (rf_left>2)) && (rf_left > 1) && (this->VelX > 0))
   {
      this->x = this->OldX;
      this->VelX = -(this->VelX * this->bounce)/1000;
      this->VelY = (this->VelY * this->bounce)/1000;
   }
   if (((rf_right>rf_left) || (rf_right>2)) && (rf_right > 1) && (this->VelX < 0))
   {
      this->x = this->OldX;
      this->VelX = -(this->VelX * this->bounce)/1000;
      this->VelY = (this->VelY * this->bounce)/1000;
   }
   if ((rf_up>1) && (this->VelY > 0))
   {
      this->y = this->OldY;
      this->VelY = -(this->VelY * this->bounce)/1000;
      this->VelX = (this->VelX * this->bounce)/1000;
   }
   if ((rf_down>1) && (this->VelY < 0))
   {
      this->y = this->OldY;
      this->VelY = -(this->VelY * this->bounce)/1000;
      this->VelX = (this->VelX * this->bounce)/1000;
   }
   if (abs(this->VelY) < GP_NO_REBOUNCE) this->VelY = 0;
   if (abs(this->VelX) < GP_NO_REBOUNCE) this->VelX = 0;

   this->OldX = this->x;
   this->OldY = this->y;
}

void interact::engineWormCollision()
{
   int i, distX, distY;
   long velX, velY, dist;
   worm *tmpWorm;

   for (i=0; i<GP_NUM_PLAYERS; i++)
   {
      tmpWorm = &worms[i];
      if (tmpWorm->active)
      if (tmpWorm->health>0)
      {
         distX = tmpWorm->coords.x - this->CurX;
         distY = tmpWorm->coords.y + Engine.wormFireOffs - this->CurY;
         if (
             (
              (this->detectDist == 0) &&
              (
               (abs(distX) <= Engine.wormSizeX+1) &&
               (tmpWorm->coords.y + Engine.wormSizeYMin - 1 <= this->CurY) &&
               (tmpWorm->coords.y + Engine.wormSizeYMax + 1 >= this->CurY)
              )
             ) ||
             (abs(distX) + abs(distY) <= this->detectDist)
            )
         {
            this->collision(tmpWorm);
         }
      }
   }
}

void interact::physics()
{
   if (this->type == ITEM_TEAMSPAWN)
      return;

   this->y += this->VelY;
   this->x += this->VelX;

   this->engineTestBorders();

   this->CurX = (this->x)/1000;
   this->CurY = (this->y)/1000;

   this->engineWorldCollision();
      
   this->CurX = (this->x)/1000;
   this->CurY = (this->y)/1000;

   this->engineWormCollision();
   this->engineGravity();
};

void ItemPhysics()
{
   interact *tmpItem;
   tmpItem = itemList;
   while (tmpItem)
   {
      tmpItem->physics();
      tmpItem = tmpItem->next;
   }
};

void ItemsThink()
{
   interact *tmpItem;
   tmpItem = itemList;
   while (tmpItem)
   {
      tmpItem->think();
      tmpItem = tmpItem->next;
   }
};

void gameViewport::ItemsDraw()
{
   interact *tmpItem;
   tmpItem = itemList;
   while (tmpItem)
   {
      tmpItem->draw(this);
      tmpItem = tmpItem->next;
   }
};

