#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>
#include <math.h>
#endif

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

engine Engine;

weatherEngine Weather;

void weatherEngine::effectStart(int fx)
{
   int i;
   
   effect = fx;
   particles = NULL;
   if (numParticles > 0)
   {
      particles = (weatherParticle*)malloc(numParticles*sizeof(weatherParticle));
   }
   switch (effect)
   {
   case (FX_SNOW):
      for (i=0; i<numParticles; i++)
      {
         particles[i].x = abs(RANDOM())%VIEW_X;
         particles[i].y = abs(RANDOM())%VIEW_Y;
         particles[i].spd = 1+abs(RANDOM())%4;
      }
      break;
   case (FX_STORM):
      nextLightning = gameTime + abs(RANDOM())%lightningTime;
   case (FX_RAIN):
      for (i=0; i<numParticles; i++)
      {
         particles[i].x = abs(RANDOM())%VIEW_X;
         particles[i].y = abs(RANDOM())%25;
         particles[i].spd = 32+abs(RANDOM())%64;
      }
      break;
   }
}

void weatherEngine::effectStop()
{
   if (particles)
      free(particles);
}

void weatherEngine::effectRun()
{
   int i;

   switch (effect)
   {
   case (FX_SNOW):
      for (i=0; i<numParticles; i++)
      {
         if ((gameTime%particles[i].spd) == 0)
         particles[i].y += 1;
         if (particles[i].y >= VIEW_Y)
         {
            particles[i].y = 0;
         }
      }
      break;
   case (FX_STORM):
   case (FX_RAIN):
      for (i=0; i<numParticles; i++)
      {
         particles[i].y += 1;
         if (particles[i].y >= 25)
         {
            particles[i].x = abs(RANDOM())%VIEW_X;
            particles[i].y=0;
         }
      }
      break;
   }
}

void weatherEngine::lightning(BITMAP *where, int x, int y, int i, int dx)
{
   int part_int, d;
   
   while ((i>0) && (y<=150) && (x>1) && (x<319))
   {
   d = (i)*20+50;
   set_add_blender(0, 0, 0, (d)/2);
   putpixel(where, x+1, y, lightningColor);
   putpixel(where, x-1, y, lightningColor);
   putpixel(where, x, y-1, lightningColor);
   putpixel(where, x, y+1, lightningColor);
   set_add_blender(0, 0, 0, d);
   putpixel(where, x, y, lightningColor);
   
   if (((abs(RANDOM())%4==0) && (y>170)) ||
       ((abs(RANDOM())%4==0) && (abs(dx)>5)) ||
       (abs(RANDOM())%20==0))
    i--;

   y++;
   if (dx>0)
    x -= abs(RANDOM())%dx;
   else  if (dx<0)
    x += abs(RANDOM())%abs(dx);
    
   if (abs(dx)<2)
   {
      x -= abs(RANDOM())%5 + 1;
      dx = -2;
   }
   if (abs(dx)>5)
   {
      x += abs(RANDOM())%5 + 1;
      dx = 2;
   }
   
   if (((y>33) && (abs(RANDOM())%5==0)) ||
       ((i<4) && (abs(RANDOM())%2==0)))
   {
      part_int = abs(RANDOM())%10;
      d = 1 + abs(RANDOM())%9;
      lightning(where, x, y, (i*part_int)/10 - y/20, -(abs(RANDOM())%d));
      lightning(where, x, y, (i*(10-part_int))/10 - y/20, abs(RANDOM())%d);
   }
   }
}

void weatherEngine::PreDraw(gameViewport *where)
{
   int i;
   BITMAP *tempImg;
   
   switch (effect)
   {
   case (FX_SNOW):
      tempImg = create_sub_bitmap(imgSpecial, snowParticle*16, 0, 16, 16);
      set_alpha_blender();
      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
      for (i=0; i<numParticles; i++)
      {
         draw_trans_sprite(where->leftpane, tempImg,
//          (particles[i].x + VIEW_X - worms[localPlayer].CurX%VIEW_X) % VIEW_X-8,
          (particles[i].x + where->paneSizeX - where->CurX % where->paneSizeX + (gameTime / particles[i].spd)) % where->paneSizeX - 8,
          (particles[i].y + where->paneSizeY - where->CurY % where->paneSizeY) % where->paneSizeY - 8);
      }
      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      destroy_bitmap(tempImg);
      break;
   case (FX_STORM):
      if (nextLightning <= gameTime)
      {
         drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
         i = 0;
         while (abs(i)<=1)
            i = abs(RANDOM()) % 6 - 3;
         lightning(where->leftpane, abs(RANDOM())%180+70, 0, 10, i);
         drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
         i = abs(RANDOM()) % 10;
         if (worms[where->player].flashEnd < gameTime + i)
            worms[where->player].flashEnd = gameTime + i;
         nextLightning = gameTime + abs(RANDOM()) % lightningTime;
      }
   case (FX_RAIN):
      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
      for (i=0; i<numParticles; i++)
      if (particles[i].y<3)
      {
         set_add_blender(0, 0, 0, particles[i].spd - 8 * particles[i].y);
         line(where->leftpane, (particles[i].x + where->paneSizeX - where->CurX % where->paneSizeX) % where->paneSizeX,
          0,
          (particles[i].x + where->paneSizeX - where->CurX % where->paneSizeX) % where->paneSizeX-10,
          where->paneSizeY-1,
          rainColor);
      }
      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      break;
   }
}

void weatherEngine::PostDraw(gameViewport *where)
{
   int i;
   BITMAP *tempImg;
   
   switch (effect)
   {
   }
}

void worm::engineGravity(worm *tmpWorm)
{
   if (tmpWorm->active)
   if (tmpWorm->health>0)
   {
      if (tmpWorm->flags[FL_APPLY_GRAV])
      {
         tmpWorm->vel.y += Engine.wormGravity;
         if (tmpWorm->vel.y > Engine.wormTermVelocity) tmpWorm->vel.y = Engine.wormTermVelocity;
      }
      if (tmpWorm->flags[FL_ROPE_OUT])
      {
         if (!tmpWorm->flags[FL_ROPE_HIT])
         {
            tmpWorm->ropevel.y += Engine.rope.gravity;
         }
      }
   }
}

void weapon_obj::engineGravity(weapon_obj *tmpWeap)
{
#ifndef OLD_BOUNCE
   tmpWeap->VelY += tmpWeap->weap->gravity;
#endif
}

void wobj_obj::engineGravity(wobj_obj *tmpObj)
{
#ifndef OLD_BOUNCE
   tmpObj->VelY += tmpObj->obj->gravity;
#endif
}

void weapon_obj::engineTestBorders(weapon_obj *tmpWeap) {
   char explo;

   if (tmpWeap->active)
   {
      explo = 0;
#ifdef OLD_BOUNCE
      if ((tmpWeap->x <= 1000) && (tmpWeap->VelX < 0))
      {
         explo = 1;
         tmpWeap->x = 1000;
         tmpWeap->VelX = -(tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         tmpWeap->VelY = (tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
      }
      else if ((tmpWeap->x + 1000>= mapSizeX*1000) && (tmpWeap->VelX > 0))
      {
         explo = 1;
         tmpWeap->x = (mapSizeX - 1)*1000;
         tmpWeap->VelX = -(tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         tmpWeap->VelY = (tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
      }

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

      if ((tmpWeap->y <= 3000) && (tmpWeap->VelY <0))
      {
         explo = 1;
         tmpWeap->y = 3000;
         tmpWeap->VelX = (tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         tmpWeap->VelY = -(tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
      }
      else if ((tmpWeap->y + 4000 >= mapSizeY*1000) && (tmpWeap->VelY > 0))
      {
         explo = 1;
         tmpWeap->y = (mapSizeY - 4)*1000;
         tmpWeap->VelX = (tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         tmpWeap->VelY = -(tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
      }
#endif
      if ((explo) && (tmpWeap->weap->expOnGround))
      {
         weapExplode(tmpWeap);
      }
      else
      if ((explo) && (tmpWeap->weap->bounceExp != -1) && (HYPOT(tmpWeap->VelX, tmpWeap->VelY) >= tmpWeap->weap->bounceLimit))
         createSpecial(tmpWeap->owner, tmpWeap->CurX, tmpWeap->CurY, tmpWeap->weap->bounceExp, tmpWeap->weap, &tmpWeap->visible);
   }
}

void wobj_obj::engineTestBorders(wobj_obj *tmpObj)
{
   char explo;

   if (tmpObj->active)
   {
      explo = 0;
#ifdef OLD_BOUNCE
      if ((tmpObj->x <= 1000) && (tmpObj->VelX < 0))
      {
         explo = 1;
         tmpObj->x = 1000;
         tmpObj->VelX = -(tmpObj->VelX * tmpObj->obj->bounce)/1000;
         tmpObj->VelY = (tmpObj->VelY * tmpObj->obj->bounce)/1000;
      }
      else if ((tmpObj->x + 1000>= mapSizeX*1000) && (tmpObj->VelX > 0))
      {
         explo = 1;
         tmpObj->x = (mapSizeX - 1)*1000;
         tmpObj->VelX = -(tmpObj->VelX * tmpObj->obj->bounce)/1000;
         tmpObj->VelY = (tmpObj->VelY * tmpObj->obj->bounce)/1000;
      }

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

      if ((tmpObj->y <= 3000) && (tmpObj->VelY <0))
      {
         explo = 1;
         tmpObj->y = 3000;
         tmpObj->VelX = (tmpObj->VelX * tmpObj->obj->bounce)/1000;
         tmpObj->VelY = -(tmpObj->VelY * tmpObj->obj->bounce)/1000;
      }
      else if ((tmpObj->y + 4000 >= mapSizeY*1000) && (tmpObj->VelY > 0))
      {
         explo = 1;
         tmpObj->y = (mapSizeY - 4)*1000;
         tmpObj->VelX = (tmpObj->VelX * tmpObj->obj->bounce)/1000;
         tmpObj->VelY = -(tmpObj->VelY * tmpObj->obj->bounce)/1000;
      }
#endif
      if ((explo) && (tmpObj->obj->expOnGround))
         shrapExplode(tmpObj);
      else
      if ((explo) && (tmpObj->obj->bounceExp != -1) && (HYPOT(tmpObj->VelX, tmpObj->VelY) >= tmpObj->obj->bounceLimit))
         createSpecial(tmpObj->owner, tmpObj->CurX, tmpObj->CurY, tmpObj->obj->bounceExp, tmpObj->weap, &tmpObj->visible);
   }
}

void worm::engineTestBorders(worm *tmpWorm)
{
   if (tmpWorm->active)
   {
      if ((tmpWorm->pos.x <= 4000) && (tmpWorm->vel.x < 0))
      {
         tmpWorm->pos.x = 4000;
         tmpWorm->vel.x = -(tmpWorm->vel.x * Engine.wormBounceLeft)/1000;
      }
      else if ((tmpWorm->pos.x >= (mapSizeX-4)*1000) && (tmpWorm->vel.x > 0))
      {
         tmpWorm->pos.x = (mapSizeX - 4)*1000;
         tmpWorm->vel.x = -(tmpWorm->vel.x * Engine.wormBounceRight)/1000;
      }

      if ((tmpWorm->pos.y <= 0) && (tmpWorm->vel.y < 0))
      {
         tmpWorm->pos.y = 0;
         tmpWorm->vel.y = -(tmpWorm->vel.y * Engine.wormBounceUp)/1000;
      }
      else if ((tmpWorm->pos.y >= (mapSizeY-10)*1000) && (tmpWorm->vel.y > 0))
      {
         tmpWorm->pos.y = (mapSizeY - 10)*1000;
         tmpWorm->vel.y = -(tmpWorm->vel.y * Engine.wormBounceDown)/1000;
         if (tmpWorm->vel.y > -GP_NO_REBOUNCE)
         {
            tmpWorm->vel.y = 0;
         }
      }
   }
}

int collisionRecalcLeft(worm *tmpWorm)
{
   int tmp;
   int i, color;

   tmp = 0;
   for (i=Engine.wormSizeYMin; i<=Engine.wormSizeYMax; i++)
   {
      color = getpixel(material, tmpWorm->coords.x + Engine.wormSizeX, tmpWorm->coords.y + i);
      if ((color & MAT_BLOCK_WORM) == MAT_BLOCK_WORM) tmp++;
   }
   return tmp;
}

int collisionRecalcRight(worm *tmpWorm)
{
   int tmp;
   int i, color;

   tmp = 0;
   for (i=Engine.wormSizeYMin; i<=Engine.wormSizeYMax; i++)
   {
      color = getpixel(material, tmpWorm->coords.x - Engine.wormSizeX, tmpWorm->coords.y + i);
      if ((color & MAT_BLOCK_WORM) == MAT_BLOCK_WORM) tmp++;
   }
   return tmp;
}

void weapon_obj::engineWorldCollision(weapon_obj *tmpWeap)
{
   int rf_up, rf_down, rf_left, rf_right;
   int i, color;
   char explo;

   rf_up = 0;
   rf_down = 0;
   rf_left = 0;
   rf_right = 0;
   
   explo = 0;
   if (!tmpWeap->weap->expOnGround)
   {
#ifdef OLD_BOUNCE
      int newX = (tmpWeap->x + tmpWeap->VelX)/1000;
      int newY = (tmpWeap->y + tmpWeap->VelY)/1000;

      color = getpixel(material, newX, tmpWeap->CurY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
      {
         explo = 1;
         tmpWeap->x = tmpWeap->OldX;
         tmpWeap->VelX = -(tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         if (tmpWeap->weap->bounce != 1000)
            tmpWeap->VelY = (tmpWeap->VelY * 4)/5;
         if (tmpWeap->weap->bounce == 0)
            tmpWeap->VelY = 0;
      }

      color = getpixel(material, tmpWeap->CurX, newY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
      {
         explo = 1;
         tmpWeap->x = tmpWeap->OldX;
         tmpWeap->VelY = -(tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
         if (tmpWeap->weap->bounce != 1000)
            tmpWeap->VelX = (tmpWeap->VelX * 4)/5;
         if (tmpWeap->weap->bounce == 0)
            tmpWeap->VelX = 0;
      }

      color = getpixel(material, newX, newY);
      if ((color & MAT_BLOCK_WEAP) != MAT_BLOCK_WEAP)
      {
         if (tmpWeap->weap->weapType != WEAP_DIRECTIONAL)
            tmpWeap->frame++;
         tmpWeap->VelY += tmpWeap->weap->gravity;
      }
      else
      if (tmpWeap->weap->weapType != WEAP_DIRECTIONAL)
      if (tmpWeap->weap->animOnGround)
      {
         tmpWeap->frame++;
      }
#else
      for (i=-1; i<2; i++)
      {
         color = getpixel(material, tmpWeap->CurX+i, tmpWeap->CurY-2);
         if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) rf_down++;
      }

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

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

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

      if (((rf_left>rf_right) || (rf_left>2)) && (rf_left > 1) && (tmpWeap->VelX > 0))
      {
         explo = 1;
         tmpWeap->x = tmpWeap->OldX;
         tmpWeap->VelX = -(tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         if (tmpWeap->weap->bounce != 1000)
            tmpWeap->VelY = (tmpWeap->VelY * 4)/5;
         if (tmpWeap->weap->bounce == 0)
            tmpWeap->VelY = 0;
      }
      if (((rf_right>rf_left) || (rf_right>2)) && (rf_right > 1) && (tmpWeap->VelX < 0))
      {
         explo = 1;
         tmpWeap->x = tmpWeap->OldX;
         tmpWeap->VelX = -(tmpWeap->VelX * tmpWeap->weap->bounce)/1000;
         if (tmpWeap->weap->bounce != 1000)
            tmpWeap->VelY = (tmpWeap->VelY * 4)/5;
         if (tmpWeap->weap->bounce == 0)
            tmpWeap->VelY = 0;
      }
      if ((rf_up>1) && (tmpWeap->VelY > 0))
      {
         explo = 1;
         tmpWeap->y = tmpWeap->OldY;
         if (tmpWeap->weap->bounce != 1000)
            tmpWeap->VelX = (tmpWeap->VelX * 4)/5;
         if (tmpWeap->weap->bounce == 0)
            tmpWeap->VelX = 0;
         tmpWeap->VelY = -(tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
      }
      if ((rf_down>1) && (tmpWeap->VelY < 0))
      {
         explo = 1;
         tmpWeap->y = tmpWeap->OldY;
         if (tmpWeap->weap->bounce != 1000)
            tmpWeap->VelX = (tmpWeap->VelX * 4)/5;
         if (tmpWeap->weap->bounce == 0)
            tmpWeap->VelX = 0;
         tmpWeap->VelY = -(tmpWeap->VelY * tmpWeap->weap->bounce)/1000;
      }
      if (explo)
      {
         if (abs(tmpWeap->VelY) < GP_NO_REBOUNCE) tmpWeap->VelY = 0;
         if (abs(tmpWeap->VelX) < GP_NO_REBOUNCE) tmpWeap->VelX = 0;
      }
#endif
      if (explo)
      {
         if ((tmpWeap->weap->bounceExp != -1) && (HYPOT(tmpWeap->VelX, tmpWeap->VelY) >= tmpWeap->weap->bounceLimit))
            createSpecial(tmpWeap->owner, tmpWeap->CurX, tmpWeap->CurY, tmpWeap->weap->bounceExp, tmpWeap->weap, &tmpWeap->visible);
      }
   }

#ifndef OLD_BOUNCE
   if (tmpWeap->weap->weapType != WEAP_DIRECTIONAL)
   if ((tmpWeap->weap->animOnGround) || (rf_up == 0))
      tmpWeap->frame++;
#endif

   if (tmpWeap->weap->expOnGround)
   {
      color = getpixel(material, tmpWeap->CurX, tmpWeap->CurY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
         weapExplode(tmpWeap);
#ifdef OLD_BOUNCE
      else
      {
         tmpWeap->VelY += tmpWeap->weap->gravity;
         if (tmpWeap->weap->weapType != WEAP_DIRECTIONAL)
            tmpWeap->frame++;
      }
   }
#endif

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

void weapon_obj::engineWormCollision(weapon_obj *tmpWeap)
{
   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 - tmpWeap->CurX;
         distY = tmpWorm->coords.y + Engine.wormFireOffs - tmpWeap->CurY;
         if (
             (
              (tmpWeap->weap->detectDist == 0) &&
              (
               (abs(distX) <= Engine.wormSizeX+1) &&
               (tmpWorm->coords.y + Engine.wormSizeYMin <= tmpWeap->CurY) &&
               (tmpWorm->coords.y + Engine.wormSizeYMax >= tmpWeap->CurY)
              )
             ) ||
             (abs(distX) + abs(distY) <= tmpWeap->weap->detectDist)
            )
         {
            dist = HYPOT(tmpWeap->VelX, tmpWeap->VelY);
            if (dist == 0)
               dist = 1;
            velX = (tmpWeap->VelX * tmpWeap->weap->blowOnHit)/ dist;
            velY = (tmpWeap->VelY * tmpWeap->weap->blowOnHit)/ dist;
            tmpWorm->vel.x += velX;
            if (gameTime + tmpWeap->weap->freezeTime > tmpWorm->freezeEnd)
            {
               tmpWorm->freezeEnd = gameTime + tmpWeap->weap->freezeTime;
            }
            if ((tmpWorm->flags[FL_APPLY_GRAV]) || (velY<0))
               tmpWorm->vel.y += velY;
            if (tmpWeap->weap->damage)
               inflictDamage(tmpWeap->owner, tmpWorm, tmpWeap->weap->damage, tmpWeap->VelX*1000/dist, tmpWeap->VelY*1000/dist, tmpWeap->weap);
            if (tmpWeap->weap->remOnWorm)
            {
               if (tmpWeap->weap->expOnWorm)
               {
                  weapExplode(tmpWeap);
               }
               else
               {
                  tmpWeap->active = 0;
                  if (tmpWeap->weap->sndLoop)
                  {
                     Sound.stopSound(tmpWeap->sndChan);
                  }
               }
            }
         }
      }
   }
}

void weapon_obj::engineObjectCollision(weapon_obj *tmpWeap)
{
   int i, distX, distY;
   long velX, velY, dist;

   if (tmpWeap->weap->remOnObject)
   {
      for (i=0; i<GP_MAX_OBJECTS; i++)
      {
         weapon_obj *tmpObj;
         tmpObj = &weapon_objs[i];
         if (tmpObj->active)
         if (tmpObj->weap != tmpWeap->weap)
         {
            distX = tmpObj->CurX - tmpWeap->CurX;
            distY = tmpObj->CurY - tmpWeap->CurY;
            if (((tmpWeap->weap->detectDist == 0) && (abs(distX) + abs(distY) <= 1))
             || (abs(distX) + abs(distY) <= tmpWeap->weap->detectDist))
            {
               dist = HYPOT(tmpWeap->VelX, tmpWeap->VelY);
               if (dist == 0)
                  dist = 1;
               velX = (tmpWeap->VelX * tmpWeap->weap->blowOnHit)/ dist;
               velY = (tmpWeap->VelY * tmpWeap->weap->blowOnHit)/ dist;
               tmpObj->VelX += velX;
               tmpObj->VelY += velY;
               if (tmpWeap->weap->expOnObject)
               {
                  weapExplode(tmpWeap);
               }
               else
               {
                  tmpWeap->active = 0;
                  if (tmpWeap->weap->sndLoop)
                  {
                     Sound.stopSound(tmpWeap->sndChan);
                  }
               }
            }
         }
      }
      for (i=0; i<GP_MAX_WOBJECTS; i++)
      {
         wobj_obj *tmpObj;
         tmpObj = &wobj_objs[i];
         if (tmpObj->active)
         {
            distX = tmpObj->CurX - tmpWeap->CurX;
            distY = tmpObj->CurY - tmpWeap->CurY;
            if (((tmpWeap->weap->detectDist == 0) && (abs(distX) + abs(distY) <= 1))
             || (abs(distX) + abs(distY) <= tmpWeap->weap->detectDist))
            {
               dist = HYPOT(tmpWeap->VelX, tmpWeap->VelY);
               if (dist == 0)
                  dist = 1;
               velX = (tmpWeap->VelX * tmpWeap->weap->blowOnHit)/ dist;
               velY = (tmpWeap->VelY * tmpWeap->weap->blowOnHit)/ dist;
               tmpObj->VelX += velX;
               tmpObj->VelY += velY;
               if (tmpWeap->weap->expOnObject)
               {
                  weapExplode(tmpWeap);
               }
               else
               {
                  tmpWeap->active = 0;
                  if (tmpWeap->weap->sndLoop)
                  {
                     Sound.stopSound(tmpWeap->sndChan);
                  }
               }
            }
         }
      }
   }
}

void wobj_obj::engineWorldCollision(wobj_obj *tmpObj)
{
   int rf_up, rf_down, rf_left, rf_right;
   int i, color;
   char explo;

   rf_up = 0;
   rf_down = 0;
   rf_left = 0;
   rf_right = 0;

   explo = 0;

   if (!tmpObj->obj->drawOnGround)
   {
   if (!tmpObj->obj->expOnGround)
   {
#ifdef OLD_BOUNCE
      int newX = (tmpObj->x + tmpObj->VelX)/1000;
      int newY = (tmpObj->y + tmpObj->VelY)/1000;

      color = getpixel(material, newX, tmpObj->CurY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
      {
         explo = 1;
         tmpObj->x = tmpObj->OldX;
         tmpObj->VelX = -(tmpObj->VelX * tmpObj->obj->bounce)/1000;
         if (tmpObj->obj->bounce != 1000)
            tmpObj->VelY = (tmpObj->VelY * 4)/5;
      }

      color = getpixel(material, tmpObj->CurX, newY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
      {
         explo = 1;
         tmpObj->x = tmpObj->OldX;
         tmpObj->VelY = -(tmpObj->VelY * tmpObj->obj->bounce)/1000;
         if (tmpObj->obj->bounce != 1000)
            tmpObj->VelX = (tmpObj->VelX * 4)/5;
      }

      color = getpixel(material, newX, newY);
      if ((color & MAT_BLOCK_WEAP) != MAT_BLOCK_WEAP)
      {
         if (tmpObj->obj->frame != -1)
            tmpObj->frame++;
         tmpObj->VelY += tmpObj->obj->gravity;
      }
      else
      if (tmpObj->obj->frame != -1)
      if (tmpObj->obj->animOnGround)
      {
         tmpObj->frame++;
      }
#else
      for (i=-1; i<2; i++)
      {
         color = getpixel(material, tmpObj->CurX+i, tmpObj->CurY-3);
         if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) rf_down++;
      }

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

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

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

      if (((rf_left>rf_right) || (rf_left>2)) && (rf_left > 1) && (tmpObj->VelX > 0))
      {
         explo = 1;
         tmpObj->x = tmpObj->OldX;
         tmpObj->VelX = -(tmpObj->VelX * tmpObj->obj->bounce)/1000;
         tmpObj->VelY = (tmpObj->VelY * tmpObj->obj->bounce)/1000;
      }
      if (((rf_right>rf_left) || (rf_right>2)) && (rf_right > 1) && (tmpObj->VelX < 0))
      {
         explo = 1;
         tmpObj->x = tmpObj->OldX;
         tmpObj->VelX = -(tmpObj->VelX * tmpObj->obj->bounce)/1000;
         tmpObj->VelY = (tmpObj->VelY * tmpObj->obj->bounce)/1000;
      }
      if ((rf_up>1) && (tmpObj->VelY > 0))
      {
         explo = 1;
         tmpObj->y = tmpObj->OldY;
         tmpObj->VelY = -(tmpObj->VelY * tmpObj->obj->bounce)/1000;
         tmpObj->VelX = (tmpObj->VelX * tmpObj->obj->bounce)/1000;
      }
      if ((rf_down>1) && (tmpObj->VelY < 0))
      {
         explo = 1;
         tmpObj->y = tmpObj->OldY;
         tmpObj->VelY = -(tmpObj->VelY * tmpObj->obj->bounce)/1000;
         tmpObj->VelX = (tmpObj->VelX * tmpObj->obj->bounce)/1000;
      }
      if (abs(tmpObj->VelY) < GP_NO_REBOUNCE) tmpObj->VelY = 0;
      if (abs(tmpObj->VelX) < GP_NO_REBOUNCE) tmpObj->VelX = 0;
#endif
      if ((explo) && (tmpObj->obj->bounceExp != -1) && (HYPOT(tmpObj->VelX, tmpObj->VelY) >= tmpObj->obj->bounceLimit))
         createSpecial(tmpObj->owner, tmpObj->CurX, tmpObj->CurY, tmpObj->obj->bounceExp, tmpObj->weap, &tmpObj->visible);
   }
#ifdef OLD_BOUNCE
   else
   {
      if (tmpObj->obj->frame != -1)
      {
         tmpObj->frame++;
      }
   }
#else
   if ((tmpObj->obj->animOnGround) || (rf_up == 0))
   if (tmpObj->obj->frame != -1)
      tmpObj->frame++;
#endif

   if (tmpObj->obj->expOnGround)
   {
      color = getpixel(material, tmpObj->CurX, tmpObj->CurY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
         shrapExplode(tmpObj);
#ifdef OLD_BOUNCE
      else
         tmpObj->VelY += tmpObj->obj->gravity;
#endif
         
   }
   }
   else //blood
   {
      color = getpixel(material, tmpObj->CurX, tmpObj->CurY);
      if ((color & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP)
      {
         shrapExplode(tmpObj);
         set_trans_blender(0, 0, 0, 127);
         drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
         putpixel(level,  tmpObj->CurX, tmpObj->CurY,  tmpObj->obj->bulletColor);
         drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      }
#ifdef OLD_BOUNCE
      else
         tmpObj->VelY += tmpObj->obj->gravity;
#endif
   }
   tmpObj->OldX = tmpObj->x;
   tmpObj->OldY = tmpObj->y;
}

void wobj_obj::engineWormCollision(wobj_obj *tmpObj)
{
   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 - tmpObj->CurX;
         distY = tmpWorm->coords.y + Engine.wormFireOffs - tmpObj->CurY;
         if (
             (
              (tmpObj->obj->detectDist == 0) &&
              (
               (abs(distX) <= Engine.wormSizeX+1) &&
               (tmpWorm->coords.y + Engine.wormSizeYMin <= tmpObj->CurY) &&
               (tmpWorm->coords.y + Engine.wormSizeYMax >= tmpObj->CurY)
              )
             ) ||
             (abs(distX) + abs(distY) <= tmpObj->obj->detectDist)
            )
         {
            dist = INT(HYPOT(tmpObj->VelX, tmpObj->VelY));
            if (dist == 0)
               dist = 1;
            velX = (tmpObj->VelX * tmpObj->obj->blowOnHit) / dist;
            velY = (tmpObj->VelY * tmpObj->obj->blowOnHit) / dist;
            tmpWorm->vel.x += velX;
            if (gameTime + tmpObj->obj->freezeTime > tmpWorm->freezeEnd)
            {
               tmpWorm->freezeEnd = gameTime + tmpObj->obj->freezeTime;
            }
            if ((tmpWorm->flags[FL_APPLY_GRAV]) || (velY<0))
               tmpWorm->vel.y += velY;
            if (tmpObj->obj->damage)
               inflictDamage(tmpObj->owner, tmpWorm, tmpObj->obj->damage, tmpObj->VelX*1000/dist, tmpObj->VelY*1000/dist, tmpObj->weap);
            if (tmpObj->obj->remOnWorm)
            {
               if (tmpObj->obj->expOnWorm)
               {
                  shrapExplode(tmpObj);
               }
               else
               {
                  tmpObj->active = 0;
                  if (tmpObj->obj->sndLoop)
                  {
                     Sound.stopSound(tmpObj->sndChan);
                  }
               }
            }
         }
      }
   }
}

void worm::engineWorldCollision(worm *tmpWorm)
{
   int rf_up, rf_down, rf_left, rf_right, rf_jump, rf_kick, rf_slide, rf_hurt;
   int i, color;

   rf_left = collisionRecalcLeft(tmpWorm);
   rf_right = collisionRecalcRight(tmpWorm);

   rf_down = 0;
   for (i=(-Engine.wormSizeX); i<=Engine.wormSizeX; i++)
   {
      color = getpixel(material, tmpWorm->coords.x + i, tmpWorm->coords.y + (Engine.wormSizeYMin-1));
      if ((color & MAT_BLOCK_WORM) == MAT_BLOCK_WORM) rf_down++;
   }
   rf_jump = 0;
   rf_kick = 0;
   rf_up = 0;
   rf_slide = 0;
   rf_hurt = 0;
   for (i=(-Engine.wormSizeX); i<=Engine.wormSizeX; i++)
   {
      color = getpixel(material, tmpWorm->coords.x + i, tmpWorm->coords.y + (Engine.wormSizeYMax+1));
      if ((color & MAT_BLOCK_WORM) == MAT_BLOCK_WORM) rf_up++;
      if ((color & MAT_WORM_JUMP) == MAT_WORM_JUMP) rf_jump++;
      if ((color & MAT_WORM_KICK) == MAT_WORM_KICK) rf_kick++;
      if ((color & MAT_WORM_SLIDE) == MAT_WORM_SLIDE) rf_slide++;
      if ((color & MAT_WORM_HURT) == MAT_WORM_HURT) rf_hurt++;
   }

   if ((rf_down < 2*Engine.wormSizeX) && (rf_up > 0) && ((rf_left > 0) || (rf_right > 0)))
   {
      tmpWorm->pos.y-=1000;
      tmpWorm->coords.y--;
      rf_left = collisionRecalcLeft(tmpWorm);
      rf_right = collisionRecalcRight(tmpWorm);
   }      
   if ((rf_down > 0) && (rf_up < 2*Engine.wormSizeX) && ((rf_left > 0) || (rf_right > 0)))
   {
      tmpWorm->pos.y+=1000;
      tmpWorm->coords.y++;
      rf_left = collisionRecalcLeft(tmpWorm);
      rf_right = collisionRecalcRight(tmpWorm);
   }

   if (rf_jump)
   {
      tmpWorm->vel.y -= GP_JUMPER_FORCE;
   }

   if (rf_kick)
   {
      tmpWorm->vel.y = GP_KICKER_FORCE;
   }
   
   if (gameTime % Engine.hurtMaterialFrequency == 0)
   if (rf_hurt)
   {
      inflictDamage(tmpWorm, tmpWorm, Engine.hurtMaterialAmount, 0, 0, 0);
   }
   
   if (
       (
        (rf_left > rf_right) ||
        (rf_left >= (Engine.wormSizeYMax - Engine.wormSizeYMin))
       ) &&
       (rf_left > 1) &&
       (tmpWorm->vel.x > 0)
      )
   {
      tmpWorm->pos.x = tmpWorm->oldpos.x;
      tmpWorm->vel.x = -(tmpWorm->vel.x * Engine.wormBounceRight)/1000;
      if (tmpWorm->vel.x > -GP_NO_REBOUNCE) tmpWorm->vel.x = 0;
   }
   else if (
            (
             (rf_right > rf_left) ||
             (rf_right >= (Engine.wormSizeYMax - Engine.wormSizeYMin))
            ) &&
            (rf_right > 1) &&
            (tmpWorm->vel.x < 0)
           )
   {
      tmpWorm->pos.x = tmpWorm->oldpos.x;
      tmpWorm->vel.x = -(tmpWorm->vel.x * Engine.wormBounceLeft)/1000;
      if (tmpWorm->vel.x < GP_NO_REBOUNCE) tmpWorm->vel.x = 0;
   }

   if (
       (
        (rf_up > rf_down) ||
        (rf_up > 2*Engine.wormSizeX)
//        (rf_up > 0)
       ) &&
       (tmpWorm->vel.y > 0)
      )
   {
      tmpWorm->vel.y = -(tmpWorm->vel.y * Engine.wormBounceDown)/1000;
      if (tmpWorm->vel.y > -GP_NO_REBOUNCE) tmpWorm->vel.y = 0;
   }
   else
   if (
       (
        (rf_down > rf_up) ||
        (rf_down > 2*Engine.wormSizeX)
//        (rf_down > 0)
       ) &&
       (tmpWorm->vel.y < 0)
      )
   {
      tmpWorm->vel.y = -(tmpWorm->vel.y * Engine.wormBounceUp)/1000;
      if (tmpWorm->vel.y < GP_NO_REBOUNCE) tmpWorm->vel.y = 0;
   }

   if ((rf_up < 2*Engine.wormSizeX) || (rf_slide > 0)) {
      tmpWorm->flags[FL_APPLY_GRAV] = 1;
   }
   else
   {
      tmpWorm->flags[FL_APPLY_GRAV] = 0;
   }
//   tmpWorm->flags[FL_JUMP_OK] = (Engine.wormJumpOnGroungCheck ? ((rf_up >= Engine.wormSizeX) || ((rf_up == Engine.wormSizeX) && (rf_down < Engine.wormSizeX))) && (rf_slide == 0) : 1);
   tmpWorm->flags[FL_JUMP_OK] = (Engine.wormJumpOnGroungCheck ? (rf_up > 0) && (rf_slide == 0) : 1);
   tmpWorm->oldpos.x = tmpWorm->pos.x;
   tmpWorm->oldpos.y = tmpWorm->pos.y;
}

void worm::engineRopeCollision(worm *tmpWorm)
{
   int color, tmp;

   if (!tmpWorm->flags[FL_GOT_ENEMY])
   {
      if (tmpWorm->ropecoords.x <= 0)
      {
         tmpWorm->rope.x = 0;
         tmpWorm->flags[FL_ROPE_HIT] = 1;
      }
      else if (tmpWorm->ropecoords.x >= mapSizeX - 1)
      {
         tmpWorm->rope.x = (mapSizeX - 1)*1000;
         tmpWorm->flags[FL_ROPE_HIT] = 1;
      }
      else if (tmpWorm->ropecoords.y <= 0)
      {
         tmpWorm->rope.y = 0;
         tmpWorm->flags[FL_ROPE_HIT] = 1;
      }
      else if (tmpWorm->ropecoords.y >= mapSizeY - 1)
      {
         tmpWorm->rope.y = (mapSizeY - 1)*1000;
         tmpWorm->flags[FL_ROPE_HIT] = 1;
      }
      else
      {
         color = getpixel(material, tmpWorm->ropecoords.x, tmpWorm->ropecoords.y);
         tmpWorm->flags[FL_ROPE_HIT] = ((color & MAT_BLOCK_ROPE) == MAT_BLOCK_ROPE);
      }
      if (Engine.rope.sticky)
      {
         traceRope(tmpWorm->coords.x, tmpWorm->coords.y, tmpWorm->ropecoords.x, tmpWorm->ropecoords.y);
         if (!trace)
         {
            tmpWorm->ropecoords.x = traceX;
            tmpWorm->rope.x = traceX * 1000;
            tmpWorm->ropecoords.y = traceY;
            tmpWorm->rope.y = traceY * 1000;
            tmpWorm->flags[FL_ROPE_HIT] = 1;   
         }   
      }   
   }
   if (tmpWorm->flags[FL_ROPE_HIT])
   { 
      tmpWorm->flags[FL_ROPE_PULL] = 1;
   }
   else
   {
      for (tmp=0; tmp < GP_NUM_PLAYERS; tmp++)
      if (worms[tmp].active)
      if (worms[tmp].health>0)
      {
         if (tmp != tmpWorm->num)
         {
            if (
                (abs(worms[tmp].coords.x - tmpWorm->ropecoords.x) < Engine.wormSizeX+1) &&
                (worms[tmp].coords.y + Engine.wormSizeYMin <= tmpWorm->ropecoords.y) &&
                (worms[tmp].coords.y + Engine.wormSizeYMax >= tmpWorm->ropecoords.y)
               )
            {
               tmpWorm->flags[FL_ROPE_HIT] = 1;
               tmpWorm->flags[FL_GOT_ENEMY] = 1;
               tmpWorm->lineCatch = &worms[tmp];
            }
         }
      }
   }
   if (!tmpWorm->flags[FL_ROPE_HIT])
   {
      tmpWorm->ropeLen = Engine.rope.defaultLen;
   }
   else
   {
      tmpWorm->ropevel.x = tmpWorm->ropevel.y = 0;
   }
}

void worm::engineRopePull(worm *tmpWorm)
{
   worm *prey;
   long dist;
   long aimX;
   long aimY;
   long px, py;

   if (tmpWorm->flags[FL_GOT_ENEMY])
   {
      prey = tmpWorm->lineCatch;

      px = prey->pos.x - tmpWorm->pos.x;
      py = prey->pos.y - tmpWorm->pos.y;

      long forcex = (px << 2) / 3;
      long forcey = (py << 2) / 3;

      long ninjaropecurlen = (HYPOT(px, py) + 1) << 3;

      long ninjaropeforcex = (1000 * -forcex) / ninjaropecurlen;
      long ninjaropeforcey = (1000 * -forcey) / ninjaropecurlen;

      tmpWorm->rope.x = prey->pos.x;
      tmpWorm->rope.y = prey->pos.y + Engine.wormFireOffs * 1000;

      if (ninjaropecurlen > (1000 * tmpWorm->ropeLen) << 5)
      {
         prey->vel.x += ninjaropeforcex;
         prey->vel.y += ninjaropeforcey;
      }
   }

   px = tmpWorm->rope.x - tmpWorm->pos.x;
   py = tmpWorm->rope.y - tmpWorm->pos.y;

   long forcex = (px << 2) / 3;
   long forcey = (py << 2) / 3;

	long ninjaropecurlen = (HYPOT(px, py) + 1) << 3;

   long ninjaropeforcex = (1000 * forcex) / ninjaropecurlen;
   long ninjaropeforcey = (1000 * forcey) / ninjaropecurlen;

   if (ninjaropecurlen > (1000 * tmpWorm->ropeLen) << 5)
   {
      tmpWorm->vel.x += ninjaropeforcex;
      tmpWorm->vel.y += ninjaropeforcey;
   }
}

void enginePhysics() {
   int i, j;
   worm *tmpWorm;

   for (i=0;i < GP_NUM_PLAYERS;i++)
   {
      tmpWorm = &worms[i];
      if (tmpWorm->active)
      if (tmpWorm->health>0)
      if (tmpWorm->freezeEnd <= gameTime)
      {
         if (!tmpWorm->flags[FL_APPLY_GRAV])
         {
            tmpWorm->vel.x = (tmpWorm->vel.x * Engine.wormFriction)/1000;
            if ((tmpWorm->vel.x < GP_NO_REBOUNCE) && (tmpWorm->vel.x > -GP_NO_REBOUNCE)) tmpWorm->vel.x = 0;
         }
         tmpWorm->pos.y += tmpWorm->vel.y;
         tmpWorm->pos.x += tmpWorm->vel.x;

      }
   }

   for (i=0;i < GP_NUM_PLAYERS;i++)
   {
      tmpWorm = &worms[i];
      if (tmpWorm->active)
      {
         if (tmpWorm->flags[FL_ROPE_OUT])
         {
            if (tmpWorm->flags[FL_ROPE_PULL])
            {
               tmpWorm->engineRopePull(tmpWorm);
            }
            for (j=0; j<Engine.rope.physicsLoop; j++)
            {
               tmpWorm->engineRopeCollision(tmpWorm);
               if (!tmpWorm->flags[FL_ROPE_HIT])
               {
                  tmpWorm->rope.x += tmpWorm->ropevel.x;
                  tmpWorm->rope.y += tmpWorm->ropevel.y;
               }
               tmpWorm->ropecoords.x = (tmpWorm->rope.x)/1000;
               tmpWorm->ropecoords.y = (tmpWorm->rope.y)/1000;
            }
         }

         if (tmpWorm->freezeEnd > gameTime)
         {
            tmpWorm->vel.x = 0;
            tmpWorm->vel.y = 0;
            tmpWorm->oldpos = tmpWorm->pos;

            tmpWorm->coords.x = (tmpWorm->pos.x)/1000;
            tmpWorm->coords.y = (tmpWorm->pos.y)/1000;
         }
         else
         {
            tmpWorm->engineTestBorders(tmpWorm);
   
            tmpWorm->coords.x = (tmpWorm->pos.x)/1000;
            tmpWorm->coords.y = (tmpWorm->pos.y)/1000;
   
            tmpWorm->engineWorldCollision(tmpWorm);
   
            tmpWorm->coords.x = (tmpWorm->pos.x)/1000;
            tmpWorm->coords.y = (tmpWorm->pos.y)/1000;
            if ((tmpWorm->buttons[GK_JUMP]) && (tmpWorm->flags[FL_CAN_JUMP]) && (tmpWorm->flags[FL_JUMP_OK]))
            {
               tmpWorm->vel.y -= Engine.wormJumpForce;
            }
         
            tmpWorm->engineGravity(tmpWorm);
         }
      }
   }
}

/* TODO (#1#): Remove on ground
               dla broni i obiektów */

void engineWeapPhysics() {
   int i, j;
   weapon_obj *tmpWeap;
   
   for (i=0;i < GP_MAX_OBJECTS;i++)
   if (weapon_objs[i].active)
   {
      tmpWeap = &weapon_objs[i];
      tmpWeap->OX = (tmpWeap->CurX);
      tmpWeap->OY = (tmpWeap->CurY);

      for (j=0; j<tmpWeap->weap->physicsLoop; j++)
      {
         tmpWeap->y += tmpWeap->VelY;
         tmpWeap->x += tmpWeap->VelX;

         tmpWeap->engineTestBorders(tmpWeap);

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

         tmpWeap->engineWorldCollision(tmpWeap);

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

         tmpWeap->engineWormCollision(tmpWeap);
         tmpWeap->engineObjectCollision(tmpWeap);
         tmpWeap->engineGravity(tmpWeap);
         
         if (!tmpWeap->active)
            break;

         if (tmpWeap->weap->weapType == WEAP_LASER)
         {
            if (tmpWeap->weap->spawnSpecial != -1)
            {
               tmpWeap->countSTrail++;
               if (tmpWeap->countSTrail >= tmpWeap->weap->specialDelay)
               {
                  createSpecial(tmpWeap->owner, tmpWeap->CurX, tmpWeap->CurY, tmpWeap->weap->spawnSpecial, tmpWeap->weap, &tmpWeap->visible);
                  tmpWeap->countSTrail = 0;
               }
            }
            if (tmpWeap->weap->spawnWobj != -1)
            {
               tmpWeap->countTrail++;
               if (tmpWeap->countTrail >= tmpWeap->weap->wobjDelay)
               {
                  if (tmpWeap->weap->wobjAffect)
                  {
                     fireShrapnel(tmpWeap->owner, tmpWeap->x, tmpWeap->y, (tmpWeap->VelX * tmpWeap->weap->wobjAffect)/1000, (tmpWeap->VelY * tmpWeap->weap->wobjAffect)/1000, tmpWeap->weap->spawnWobj, tmpWeap->weap, 1);
                  }
                  else
                  {
                     fireShrapnel(tmpWeap->owner, tmpWeap->x, tmpWeap->y, 0, 0, tmpWeap->weap->spawnWobj, tmpWeap->weap, 1);
                  }
                  tmpWeap->countTrail = 0;
               }
            }
         }
      }
   }
}

void engineShrapPhysics() {
   int i, j;
   wobj_obj *tmpObj;

   for (i=0;i < GP_MAX_WOBJECTS;i++)
   if (wobj_objs[i].active)
   {
      tmpObj = &wobj_objs[i];
      for (j=0; j<tmpObj->obj->physicsLoop; j++)
      {
         tmpObj->y += tmpObj->VelY;
         tmpObj->x += tmpObj->VelX;

         tmpObj->engineTestBorders(tmpObj);

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

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

         tmpObj->engineWormCollision(tmpObj);
         tmpObj->engineGravity(tmpObj);

         if (!tmpObj->active)
            break;
      }
   }
}

void engineWeapTiming()
{
   int i, aim, dir;
   weapon_obj *tmpWeap;
   worm *local;

   for (i=0; i<numViewports; i++)
   {
      local = &worms[viewport[i].player];
      local->flags[FL_SHOW_CAM] = 0;
      for (i=0;i < GP_MAX_OBJECTS;i++)
      if (weapon_objs[i].active)
      {
         tmpWeap = &weapon_objs[i];
         
         if (tmpWeap->owner == local)
         if (viewport[i].snipingEnabled)
         if (tmpWeap->weap == &weapons[local->weaps[local->curWeap]])
         if (tmpWeap->weap->laserSight == -3)
         {
            local->flags[FL_SHOW_CAM] = 1;
            viewport[i].SnipX = tmpWeap->CurX - 24;
            viewport[i].SnipY = tmpWeap->CurY - 24;
         }
      }
   }

   for (i=0;i < GP_MAX_OBJECTS;i++)
   if (weapon_objs[i].active)
   {
      tmpWeap = &weapon_objs[i];
      
      if (tmpWeap->weap->sndLoop)
      {
         Sound.updateSound(tmpWeap->sndChan, tmpWeap->CurX, tmpWeap->CurY, tmpWeap->VelX * tmpWeap->weap->physicsLoop, tmpWeap->VelY * tmpWeap->weap->physicsLoop);
      }
      
      if (tmpWeap->weap->weapType == WEAP_DIRECTIONAL)
      {
         tmpWeap->frame = calcFrame(tmpWeap->VelX, tmpWeap->VelY);
      }

      if (tmpWeap->weap->drunk)
      {
         aim = (abs(RANDOM())*33)/1000;
         dir = abs(RANDOM())%2;
         if (dir == 0) dir = -1;
         tmpWeap->VelX += (cosinus[aim] * dir * tmpWeap->weap->drunk) / 1000;
         tmpWeap->VelY += (sinus[aim] * tmpWeap->weap->drunk) / 1000;
      }
      if (tmpWeap->weap->accel)
      {
         if (tmpWeap->dir == DIR_RIGHT)
            tmpWeap->VelX += (cosinus[tmpWeap->aim] * tmpWeap->weap->accel) / 1000;
         else
            tmpWeap->VelX -= (cosinus[tmpWeap->aim] * tmpWeap->weap->accel) / 1000;
         tmpWeap->VelY += (sinus[tmpWeap->aim] * tmpWeap->weap->accel) / 1000;
      }
      if (tmpWeap->weap->multiply != 1000)
      {
         tmpWeap->VelX = (tmpWeap->VelX * tmpWeap->weap->multiply) / 1000;
         tmpWeap->VelY = (tmpWeap->VelY * tmpWeap->weap->multiply) / 1000;
      }

      if (tmpWeap->weap->timeToExplode)
      {
         if (tmpWeap->localTime + tmpWeap->weap->timeToExplode <= gameTime)
         {
            weapExplode(tmpWeap);
         }
      }

      if (tmpWeap->weap->homingDist)
      {
         int i, curTarget = -1;
         long curDist = tmpWeap->weap->homingDist;
         for (i = 0; i < GP_NUM_PLAYERS; i++)
         if (worms[i].active)
         if (worms[i].health > 0)
         if (i != tmpWeap->owner->num)
         {
            long dist = HYPOT((worms[i].pos.x - tmpWeap->x) / 1000, (worms[i].pos.y - tmpWeap->y) / 1000);
            if (dist < curDist)
            {
               if (visible(&worms[i], tmpWeap->CurX, tmpWeap->CurY))
               {
                  curTarget = i;
                  curDist = dist;
               }
            }
         }
         if (curTarget != -1)
         {
            if (curDist == 0) curDist = 1;
            long xDist = ((worms[curTarget].pos.x - tmpWeap->x) * tmpWeap->weap->homingSpeed) / curDist / 1000;
            long yDist = ((worms[curTarget].pos.y + 1000 * Engine.wormFireOffs - tmpWeap->y) * tmpWeap->weap->homingSpeed) / curDist / 1000;
            tmpWeap->VelX = (tmpWeap->VelX * tmpWeap->weap->homingAccel) / 1000 + xDist;
            tmpWeap->VelY = (tmpWeap->VelY * tmpWeap->weap->homingAccel) / 1000 + yDist;
         }
      }

      if (tmpWeap->weap->weapType != WEAP_LASER)
      {
         if (tmpWeap->weap->spawnSpecial != -1)
         if (abs(gameTime - tmpWeap->localTime)%tmpWeap->weap->specialDelay == 0)
         {
            createSpecial(tmpWeap->owner, tmpWeap->CurX, tmpWeap->CurY, tmpWeap->weap->spawnSpecial, tmpWeap->weap, &tmpWeap->visible);
         }
   
         if (tmpWeap->weap->spawnWobj != -1)
         if (abs(gameTime - tmpWeap->localTime)%tmpWeap->weap->wobjDelay == tmpWeap->weap->wobjDelay-1)
         {
            if (tmpWeap->weap->wobjAffect)
            {
               fireShrapnel(tmpWeap->owner, tmpWeap->x, tmpWeap->y, (tmpWeap->VelX * tmpWeap->weap->wobjAffect)/1000, (tmpWeap->VelY * tmpWeap->weap->wobjAffect)/1000, tmpWeap->weap->spawnWobj, tmpWeap->weap, 1);
            }
            else
            {
               fireShrapnel(tmpWeap->owner, tmpWeap->x, tmpWeap->y, 0, 0, tmpWeap->weap->spawnWobj, tmpWeap->weap, 1);
            }
         }
      }
   }
}

void engineShrapTiming()
{
   int i, aim, dir;
   wobj_obj *tmpObj;

   for (i=0;i < GP_MAX_WOBJECTS;i++)
   if (wobj_objs[i].active)
   {
      tmpObj = &wobj_objs[i];

      if (tmpObj->obj->sndLoop)
      {
         Sound.updateSound(tmpObj->sndChan, tmpObj->CurX, tmpObj->CurY, tmpObj->VelX * tmpObj->obj->physicsLoop, tmpObj->VelY * tmpObj->obj->physicsLoop);
      }

      if (tmpObj->obj->drunk)
      {
         aim = (abs(RANDOM())*33)/1000;
         dir = abs(RANDOM())%2;
         if (dir == 0) dir = -1;
         tmpObj->VelX += (cosinus[aim] * dir * tmpObj->obj->drunk) / 1000;
         tmpObj->VelY += (sinus[aim] * tmpObj->obj->drunk) / 1000;
      }
      if (tmpObj->obj->multiply != 1000)
      {
         tmpObj->VelX = (tmpObj->VelX * tmpObj->obj->multiply) / 1000;
         tmpObj->VelY = (tmpObj->VelY * tmpObj->obj->multiply) / 1000;
      }

      if (tmpObj->obj->timeToExplode)
      {
         if (tmpObj->localTime+tmpObj->obj->timeToExplode <= gameTime)
         {
            shrapExplode(tmpObj);
         }
      }
      if (tmpObj->obj->spawnSpecial != -1)
      if ((gameTime - tmpObj->localTime)%tmpObj->obj->specialDelay == 0)
         createSpecial(tmpObj->owner, tmpObj->CurX, tmpObj->CurY, tmpObj->obj->spawnSpecial, tmpObj->weap, &tmpObj->visible);
      if (tmpObj->obj->spawnWobj != -1)
      if ((gameTime - tmpObj->localTime)%tmpObj->obj->wobjDelay == tmpObj->obj->wobjDelay-1)
      {
         if (tmpObj->obj->wobjAffect)
         {
            fireShrapnel(tmpObj->owner, tmpObj->x, tmpObj->y, tmpObj->VelX * tmpObj->obj->wobjAffect, tmpObj->VelY * tmpObj->obj->wobjAffect, tmpObj->obj->spawnWobj, tmpObj->weap, 1);
         }
         else
         {
            fireShrapnel(tmpObj->owner, tmpObj->x, tmpObj->y, 0, 0, tmpObj->obj->spawnWobj, tmpObj->weap, 1);
         }                                    
      }
   }
}

void engineSpecialTiming()
{
   int i;
   special_obj *tmpSpec;

   for (i=0;i < GP_MAX_SPECIALS;i++)
   if (special_objs[i].active)
   {
      tmpSpec = &special_objs[i];
      if (tmpSpec->localTime+(tmpSpec->spec->numFrames * tmpSpec->spec->animDelay) <= gameTime)
      {
         tmpSpec->active = 0;
      }
   }
}

void cutHole(BITMAP *hole, int x, int y)
{
   int i, j, k, l;
   for (j=0; j<16; j++)
   for (i=0; i<16; i++)
   if (((getpixel(material,x+i-8,y+j-8) & MAT_DESTROYABLE) == MAT_DESTROYABLE) && ((k = getpixel(hole,i,j)) != GC_PINK))
   {
      if (k == GC_WHITE)
      {
         putpixel(material,x+i-8,y+j-8,0);
         l = getpixel(level,x+i-8,y+j-8);
         if (abs(RANDOM())<dirtChance)
         if (x+i-8<mapSizeX)
         if (x+i-8>=0)
         if (y+j-8<mapSizeY)
         if (y+j-8>=0)
         {
            k = fireShrapnel(0, (x+i-8)*1000, (y+j-8)*1000, 0, 0, dirtObject, 0, 1);
            if (k != -1)
            {
               wobj_objs[k].frame = l;
            }
         }
         putpixel(level,x+i-8,y+j-8,GC_PINK);
      }
      else
      if (getpixel(level,x+i-8,y+j-8) != GC_PINK)
      {
         set_trans_blender(0, 0, 0, 127);
         drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
         putpixel(level,x+i-8,y+j-8,k);
         drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      }
   }
}

void drawHole(BITMAP *hole, int x, int y)
{
   int i, j, k, l;
   for (j=0; j<16; j++)
   for (i=0; i<16; i++)
   if (getpixel(level,x+i-8,y+j-8) != GC_PINK)
   {
      set_write_alpha_blender();
      k = getpixel(hole, i, j);
      set_trans_blender(0, 0, 0, k);
      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
      putpixel(level,x+i-8,y+j-8,getpixel(hole, i, j));
   }
   drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
}

