#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 <string.h>
#endif
//include "fblend.h"

#include "defs.h"
#include "console.h"

#include "ai_waypt.h"

#include "ai.h"
#include "engine.h"
#include "gfx.h"
#include "math.h"
#include "maths.h"
#include "render.h"
#include "rules.h"
#include "weapons.h"

HUD hud;

void gameViewport::drawLevel()
{
   int i, j, k, l, m, n;
   int offsx, offsy;

   if (pScroll)
   {
      offsx = background->w - paneSizeX;
      offsx = (offsx * CurX) / (mapSizeX - paneSizeX);
      offsy = background->h - paneSizeY;
      offsy = (offsy * CurY) / (mapSizeY - paneSizeY);
      blit(background, leftpane, offsx, offsy, 0, 0, paneSizeX, paneSizeY);
      if (parallax)
      {
         offsx = parallax->w - paneSizeX;
         offsx = (offsx * CurX) / (mapSizeX - paneSizeX);
         offsy = parallax->h - paneSizeY;
         offsy = (offsy * CurY) / (mapSizeY - paneSizeY);
         masked_blit(parallax, leftpane, offsx, offsy, 0, 0, paneSizeX, paneSizeY);
      }
   }
   else
      blit(background, leftpane, CurX, CurY, 0, 0, paneSizeX, paneSizeY);

   Weather.effectRun();
   Weather.PreDraw(this);
   masked_blit(level, leftpane, CurX, CurY, 0, 0, paneSizeX, paneSizeY);
   Weather.PostDraw(this);

   if (snipingEnabled)
   {
      if (SnipX < 0)
      {
         i = 0;
         k = -SnipX;
         m = 49 + SnipX;
      }
      else
      {
         i = SnipX;
         k = 0;
         m = 49;
      }
      if (SnipY < 0)
      {
         j = 0;
         l = -SnipY;
         n = 49 + SnipY;
      }
      else
      {
         j = SnipY;
         l = 0;
         n = 49;
      }
   
      if (pScroll)
      {
         offsx = background->w - 49;
         offsx = (offsx * i) / (mapSizeX - 49);
         offsy = background->h - 49;
         offsy = (offsy * j) / (mapSizeY - 49);
         blit(background, sniping, offsx, offsy, k, l, m, n);
         if (parallax)
         {
            offsx = parallax->w - 49;
            offsx = (offsx * i) / (mapSizeX - 49);
            offsy = parallax->h - 49;
            offsy = (offsy * j) / (mapSizeY - 49);
            masked_blit(parallax, sniping, offsx, offsy, k, l, m, n);
         }
      }
      else
         blit(background, sniping, i, j, k, l, m, n);
      masked_blit(level, sniping, i, j, k, l, m, n);
   }
}

void gameViewport::drawLights()
{
   BITMAP *light;

   if (lights)
   {
      light = create_sub_bitmap(lights, CurX, CurY, paneSizeX, paneSizeY);
      set_add_blender(0, 0, 0, 128);
      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
      draw_trans_sprite(leftpane, light, 0, 0);
      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      destroy_bitmap(light);
   }
}

void HUD::drawViewports()
{
   Console.checkObsolete();

   Console.draw(buffer, 0, 150 * (stretch == RENDER_GL3 ? 2 : (stretch == RENDER_STRETCH2 ? 2 : 1)));

   for (int i=0; i<numViewports; i++)
   {
      viewport[i].draw();
      if (worms[viewport[i].player].flashEnd > gameTime)
      {
         if (worms[viewport[i].player].flashEnd - gameTime < GP_FLASH_DIM)
         {
	      blit(viewport[i].leftpane, buffer,
	       0, 0, viewport[i].paneX, viewport[i].paneY,
	       viewport[i].paneSizeX, viewport[i].leftpane->h);
	      set_trans_blender(0, 0, 0, ((worms[viewport[i].player].flashEnd - gameTime)*255/GP_FLASH_DIM));
	      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
	      rectfill(buffer, viewport[i].paneX, viewport[i].paneY,
	       viewport[i].paneX + viewport[i].paneSizeX - 1,
	       viewport[i].paneY + viewport[i].paneSizeY - 1, GP_FLASH_COLOR);
	      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
//            fblend_fade_to_color(viewport[i].leftpane, buffer,
//		viewport[i].paneX, viewport[i].paneY, GP_FLASH_COLOR,
//		255 - ((worms[viewport[i].player].flashEnd - gameTime)*255/GP_FLASH_DIM));
         }
         else
         {
            rectfill(buffer, viewport[i].paneX, viewport[i].paneY, viewport[i].paneX+viewport[i].paneSizeX-1, viewport[i].paneY+viewport[i].leftpane->h, GP_FLASH_COLOR);
         }
      }
      else
         blit(viewport[i].leftpane, buffer, 0, 0, viewport[i].paneX, viewport[i].paneY, viewport[i].paneSizeX, viewport[i].leftpane->h);
   }

   for (int i=0; i<numViewports; i++)
   {
      if (viewport[i].snipingEnabled)
      if (weapons[worms[viewport[i].player].weaps[worms[viewport[i].player].curWeap]].laserSight != -1)
      if ((weapons[worms[viewport[i].player].weaps[worms[viewport[i].player].curWeap]].laserSight != -3) ||
          (worms[viewport[i].player].flags[FL_SHOW_CAM]))
      {
         set_alpha_blender();
         draw_trans_sprite(viewport[i].sniping, sniper, 0, 0);
         drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
         blit(viewport[i].sniping, buffer, 0, 0, viewport[i].snipingX, viewport[i].snipingY, 49, 49);
      }
   }
}

void gameViewport::drawWPs()
{
   int i;

   if (!drawWayPts)
      return;

   i = findNearestPt(worms[player].coords.x, worms[player].coords.y);
   if (i != -1)
   if (waypts[i].active)
   {
      if (waypts[i].links[0].link != -1)
      {
         line(leftpane, waypts[i].x - CurX, waypts[i].y - CurY, waypts[waypts[i].links[0].link].x - CurX, waypts[waypts[i].links[0].link].y - CurY, GC_PINK);
      }
      if (waypts[i].links[1].link != -1)
      {
         line(leftpane, waypts[i].x - CurX, waypts[i].y - CurY, waypts[waypts[i].links[1].link].x - CurX, waypts[waypts[i].links[1].link].y - CurY, GC_PINK);
      }
      if (waypts[i].links[2].link != -1)
      {
         line(leftpane, waypts[i].x - CurX, waypts[i].y - CurY, waypts[waypts[i].links[2].link].x - CurX, waypts[waypts[i].links[2].link].y - CurY, GC_PINK);
      }
      if (waypts[i].links[3].link != -1)
      {
         line(leftpane, waypts[i].x - CurX, waypts[i].y - CurY, waypts[waypts[i].links[3].link].x - CurX, waypts[waypts[i].links[3].link].y - CurY, GC_PINK);
      }
   }
}

void gameViewport::drawWorms()
{
   int cx, cy, i;
   int frame;
   BITMAP *tempWorm;
   BITMAP *tempWeap;
   BITMAP *tempWrm2;
   BITMAP *tempWp2;
   BITMAP *safeAura;
   int tr;
   bool weapSkin;

   safeAura = create_sub_bitmap(imgSpecial, Engine.wormSafeSprite*16, 0, 16, 16);

   if (drawWayPts)
   {
      for (i=0; i<1000; i++)
      if (waypts[i].active)
      {
         sprintf(buf, "%d", i);
         drawTextCenter(leftpane, buf, waypts[i].x - CurX, waypts[i].y - CurY - 10);
         putpixel(leftpane, waypts[i].x - CurX, waypts[i].y - CurY, GC_WHITE);
      }
      else break;
   }

   for (i=0; i < GP_NUM_PLAYERS; i++)
   if (worms[i].active)
   if (worms[i].health>0)
   if ((!tactics) || (i == player) || (visible(&worms[player], worms[i].coords.x, worms[i].coords.y + Engine.wormFireOffs)))
   {
      weapSkin = (weapons[worms[i].weaps[worms[i].curWeap]].wormSkin != 0);
      if (drawWayPts)
      {
         cx = worms[i].aiPath ? worms[i].aiPath : findNearestPt(worms[i].coords.x, worms[i].coords.y);
         if (cx != -1)
         {
            line(leftpane, worms[i].coords.x - CurX, worms[i].coords.y - CurY, waypts[cx].x - CurX, waypts[cx].y - CurY, GC_WHITE);
         }
      }
      frame = worms[i].frame * 9 + getFrame(worms[i].aim);
      tempWorm = create_sub_bitmap(wormSkins, frame*16, i*16, 16, 16);
      if (weapSkin)
      {
         tempWeap = create_sub_bitmap(weapons[worms[i].weaps[worms[i].curWeap]].wormSkin, frame*16, 0, 16, 16);
      }
      if (worms[i].dir == DIR_RIGHT)
      {
         if ((gameType == GAME_PRD) && (worms[i].flags[FL_PREDATOR]))
         {
            tr = INT(HYPOT(worms[i].vel.x, worms[i].vel.y)*255.0/3000.0);
            if (tr>255) tr = 255;
            set_trans_blender(0, 0, 0, tr);
            drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
            draw_trans_sprite(leftpane, tempWorm, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (weapSkin)
               draw_trans_sprite(leftpane, tempWeap, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (snipingEnabled)
            {
               draw_trans_sprite(sniping, tempWorm, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
               if (weapSkin)
                  draw_trans_sprite(sniping, tempWeap, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
            }
            drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
         }
         else
         {
            draw_sprite(leftpane, tempWorm, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (weapSkin)
               draw_sprite(leftpane, tempWeap, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (snipingEnabled)
            {
               draw_sprite(sniping, tempWorm, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
               if (weapSkin)
                  draw_sprite(sniping, tempWeap, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
            }
         }
         if (worms[i].show_fire > gameTime)
         {
            frame = getFrame(worms[i].aim);
            cx = worms[i].coords.x - CurX + Engine.firePos[frame].x;
            cy = worms[i].coords.y + Engine.wormFireOffs - CurY + Engine.firePos[frame].y;
            draw_sprite(leftpane, fire[frame], cx - 8, cy - 8);
            cx = worms[i].coords.x - SnipX + Engine.firePos[frame].x;
            cy = worms[i].coords.y + Engine.wormFireOffs - SnipY + Engine.firePos[frame].y;
            if (snipingEnabled)
               draw_sprite(sniping, fire[frame], cx - 8, cy - 8);
         }
      }
      else
      {
         if ((gameType == GAME_PRD) && (worms[i].flags[FL_PREDATOR]))
         {
            tr = INT(HYPOT(worms[i].vel.x, worms[i].vel.y)*255.0/2000.0);
            if (tr>255) tr = 255;
            tempWrm2 = create_bitmap(16, 16);
            clear_to_color(tempWrm2, 255*256*256+255);
            draw_sprite_h_flip(tempWrm2, tempWorm, 0, 0);
            tempWp2 = create_bitmap(16, 16);
            clear_to_color(tempWp2, 255*256*256+255);
            draw_sprite_h_flip(tempWp2, tempWeap, 0, 0);
            set_trans_blender(0, 0, 0, tr);
            drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
            draw_trans_sprite(leftpane, tempWrm2, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (weapSkin)
               draw_trans_sprite(leftpane, tempWp2, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (snipingEnabled)
            {
               draw_trans_sprite(sniping, tempWrm2, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
               if (weapSkin)
                  draw_trans_sprite(sniping, tempWp2, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
            }
            drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
            destroy_bitmap(tempWrm2);
         }
         else
         {
            draw_sprite_h_flip(leftpane, tempWorm, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (weapSkin)
               draw_sprite_h_flip(leftpane, tempWeap, worms[i].coords.x - CurX - 8, worms[i].coords.y - CurY);
            if (snipingEnabled)
            {
               draw_sprite_h_flip(sniping, tempWorm, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
               if (weapSkin)
                  draw_sprite_h_flip(sniping, tempWeap, worms[i].coords.x - SnipX - 8, worms[i].coords.y - SnipY);
            }
         }   
         if (worms[i].show_fire > gameTime)
         {
            frame = getFrame(worms[i].aim);
            cx = worms[i].coords.x - CurX - Engine.firePos[frame].x;
            cy = worms[i].coords.y + Engine.wormFireOffs - CurY + Engine.firePos[frame].y;
            draw_sprite_h_flip(leftpane, fire[frame], cx - 8, cy - 8);
            cx = worms[i].coords.x - SnipX - Engine.firePos[frame].x;
            cy = worms[i].coords.y + Engine.wormFireOffs - SnipY + Engine.firePos[frame].y;
            if (snipingEnabled)
               draw_sprite_h_flip(sniping, fire[frame], cx - 8, cy - 8);
         }
      }
      if (worms[i].localTime + Engine.wormSafeTime > gameTime)
      {
         set_alpha_blender();
         draw_trans_sprite(leftpane, safeAura, worms[i].coords.x - CurX - 8, worms[i].coords.y + (Engine.wormSizeYMin + Engine.wormSizeYMax)/2 - CurY - 8);
         drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      }
      destroy_bitmap(tempWorm);
      if (weapSkin)
         destroy_bitmap(tempWeap);
   }
   destroy_bitmap(safeAura);
}

void gameViewport::drawWeaps()
{
   int i;
   int frame;
   BITMAP *tempWeap;
   weapon_obj *tmpWeap;

   for (i=0; i < GP_MAX_OBJECTS; i++)
   if (weapon_objs[i].active)
   if ((!tactics) || (weapon_objs[i].visible) || (weapon_objs[i].visible = visible(&worms[player], weapon_objs[i].CurX, weapon_objs[i].CurY)))
   {
      tmpWeap = &weapon_objs[i];
      if (tmpWeap->weap->frame != -1)
      {
         if (tmpWeap->weap->weapType == WEAP_DIRECTIONAL)
         {
            frame = tmpWeap->weap->frame + tmpWeap->frame;
         }
         else
         {
            frame = tmpWeap->weap->frame + ((tmpWeap->frame)/tmpWeap->weap->animDelay)%(tmpWeap->weap->numFrames);
         }
         tempWeap = create_sub_bitmap(imgWeap, frame*7, 0, 7, 7);
         if ((tmpWeap->VelX >= 0) || (tmpWeap->weap->weapType == WEAP_DIRECTIONAL))
         {
            draw_sprite(leftpane, tempWeap, tmpWeap->CurX - CurX - 3, tmpWeap->CurY - CurY - 3);
            if (snipingEnabled)
               draw_sprite(sniping, tempWeap, tmpWeap->CurX - SnipX - 3, tmpWeap->CurY - SnipY - 3);
         }
         else
         {
            draw_sprite_h_flip(leftpane, tempWeap, tmpWeap->CurX - CurX - 3, tmpWeap->CurY - CurY - 3);
            if (snipingEnabled)
               draw_sprite_h_flip(sniping, tempWeap, tmpWeap->CurX - SnipX - 3, tmpWeap->CurY - SnipY - 3);
         }
         destroy_bitmap(tempWeap);
      }
      else
      {
         if (tmpWeap->weap->weapType != WEAP_VECTOR)
         {
            putpixel(leftpane, tmpWeap->CurX - CurX, tmpWeap->CurY - CurY, tmpWeap->weap->bulletColor);
            if (snipingEnabled)
               putpixel(sniping, tmpWeap->CurX - SnipX, tmpWeap->CurY - SnipY, tmpWeap->weap->bulletColor);
         }
         else
         {
            set_trans_blender(0, 0, 0, 128);
            drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
            line(leftpane, tmpWeap->OX - CurX, tmpWeap->OY - CurY, tmpWeap->CurX - CurX, tmpWeap->CurY - CurY, tmpWeap->weap->bulletColor);
            if (snipingEnabled)
               line(sniping, tmpWeap->OX - SnipX, tmpWeap->OY - SnipY, tmpWeap->CurX - SnipX, tmpWeap->CurY - SnipY, tmpWeap->weap->bulletColor);
            drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
         }
      }
   }
}

void gameViewport::drawShrap()
{
   int i;
   int frame;
   BITMAP *tempWeap;
   wobj_obj *tmpObj;

   for (i=0; i < GP_MAX_WOBJECTS; i++)
   if (wobj_objs[i].active)
   if ((!tactics) || (wobj_objs[i].visible) || (wobj_objs[i].visible = visible(&worms[player], wobj_objs[i].CurX, wobj_objs[i].CurY)))
   {
      tmpObj = &wobj_objs[i];
      if (tmpObj->obj->frame != -1)
      {
         frame = tmpObj->obj->frame + (tmpObj->frame/tmpObj->obj->animDelay)%(tmpObj->obj->numFrames);
         tempWeap = create_sub_bitmap(imgWeap, frame*7, 0, 7, 7);
         if (tmpObj->VelX >= 0)
         {
            draw_sprite(leftpane, tempWeap, tmpObj->CurX - CurX - 3, tmpObj->CurY - CurY - 3);
            if (snipingEnabled)
               draw_sprite(sniping, tempWeap, tmpObj->CurX - SnipX - 3, tmpObj->CurY - SnipY - 3);
         }
         else
         {
            draw_sprite_h_flip(leftpane, tempWeap, tmpObj->CurX - CurX - 3, tmpObj->CurY - CurY - 3);
            if (snipingEnabled)
               draw_sprite_h_flip(sniping, tempWeap, tmpObj->CurX - SnipX - 3, tmpObj->CurY - SnipY - 3);
         }
         destroy_bitmap(tempWeap);
      }
      else
      {
         if (tmpObj->obj->num == dirtObject)
         {
            set_trans_blender(0, 0, 0, 255 - 2*(gameTime - tmpObj->localTime));
            drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
            putpixel(leftpane, tmpObj->CurX - CurX, tmpObj->CurY - CurY, tmpObj->frame);
            if (snipingEnabled)
               putpixel(sniping, tmpObj->CurX - SnipX, tmpObj->CurY - SnipY, tmpObj->frame);
            drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
         }
         else if (tmpObj->obj->drawOnGround)
         {
            set_trans_blender(0, 0, 0, 128);
            drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
            putpixel(leftpane, tmpObj->CurX - CurX, tmpObj->CurY - CurY, tmpObj->obj->bulletColor);
            if (snipingEnabled)
               putpixel(sniping, tmpObj->CurX - SnipX, tmpObj->CurY - SnipY, tmpObj->obj->bulletColor);
            drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
         }
         else
         {
            putpixel(leftpane, tmpObj->CurX - CurX, tmpObj->CurY - CurY, tmpObj->obj->bulletColor);
            if (snipingEnabled)
               putpixel(sniping, tmpObj->CurX - SnipX, tmpObj->CurY - SnipY, tmpObj->obj->bulletColor);
         }
      }
   }
}

void gameViewport::drawSpecials()
{
   int cx, cy, i;
   int frame;
   BITMAP *tempSpec;
   special_obj *tmpSpec;

   set_alpha_blender();
   for (i=0; i < GP_MAX_SPECIALS; i++)
   if (special_objs[i].active)
   if ((!tactics) || (*special_objs[i].visible))
   {
      tmpSpec = &special_objs[i];
      frame = tmpSpec->spec->frame + ((gameTime - tmpSpec->localTime)/tmpSpec->spec->animDelay)%(tmpSpec->spec->numFrames);
      tempSpec = create_sub_bitmap(imgSpecial, frame*16, 0, 16, 16);
      draw_trans_sprite(leftpane, tempSpec, tmpSpec->x - CurX - 8, tmpSpec->y - CurY - 8);
      if (snipingEnabled)
         draw_trans_sprite(sniping, tempSpec, tmpSpec->x - SnipX - 8, tmpSpec->y - SnipY - 8);
      destroy_bitmap(tempSpec);
   }
   drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
}

void drawRope(BITMAP *bmp, int x, int y, int d)
{
   putpixel(bmp, x, y, lineColors[lineColor]);
   lineColor++;
   if (lineColor >= lineNumColors)
      lineColor = 0;
}

void gameViewport::drawLines()
{
   int i;
   BITMAP *tempHook;
   
   tempHook = create_sub_bitmap(imgSpecial, lineHook*16, 0, 16, 16);
   for (i=0; i < GP_NUM_PLAYERS; i++)
   if (worms[i].active)
   {
      if (worms[i].flags[FL_ROPE_OUT])
      {
         lineColor = 0;
         do_line(leftpane, worms[i].ropecoords.x - CurX, worms[i].ropecoords.y - CurY, worms[i].coords.x - CurX, worms[i].coords.y + Engine.wormFireOffs - CurY, GC_LINE_COLOR, drawRope);
         draw_sprite(leftpane, tempHook, worms[i].ropecoords.x - CurX - 1, worms[i].ropecoords.y - CurY - 1);
         if (snipingEnabled)
         {
            do_line(sniping, worms[i].ropecoords.x - SnipX, worms[i].ropecoords.y - SnipY, worms[i].coords.x - SnipX, worms[i].coords.y + Engine.wormFireOffs - SnipY, GC_LINE_COLOR, drawRope);
            draw_sprite(sniping, tempHook, worms[i].ropecoords.x - SnipX - 1, worms[i].ropecoords.y - SnipY - 1);
         }
      }
   }
   destroy_bitmap(tempHook);
}

int tmpX, tmpY, tmpW;
int tmpSnipX, tmpSnipY;

void calcAim(int x, int y)
{
   tmpSnipX = x-24;
   tmpSnipY = y-24;
}

void drawSight(BITMAP *bmp, int x, int y, int d)
{
   int i;
   
   if (lineColor)
   if (((getpixel(material,x,y) & MAT_BLOCK_WEAP) == MAT_BLOCK_WEAP) || (x<0) || (x>=mapSizeX) || (y<0) || (y>=mapSizeY) )
   {
      calcAim(x, y);
      lineColor = 0;
   }
   if (lineColor)
   {
      for (i=0; i<GP_NUM_PLAYERS; i++)
      if (worms[i].active)
      if (worms[i].health > 0)
      if (i != tmpW)
      {
         if (abs(x - worms[i].coords.x) < Engine.wormSizeX+1)
         if (worms[i].coords.y + Engine.wormSizeYMin <= y)
         if (worms[i].coords.y + Engine.wormSizeYMax >= y)
         {
            calcAim(x, y);
            lineColor = 0;
            worms[tmpW].flags[FL_CROSSHAIR] = 1;
            break;
         }
      }
   }
   if (lineColor)
   if (RANDOM()>200)
   if (d != -2)
   {
      putpixel(bmp, x-tmpX, y-tmpY, d);
   }
}

void gameViewport::drawSightLines()
{
   int i;
   long cx,cy;
   
   for (i=0; i < GP_NUM_PLAYERS; i++)
   if (worms[i].active)
   if (worms[i].health>0)
   {
      worms[i].flags[FL_CROSSHAIR] = 0;
      if (weapons[worms[i].weaps[worms[i].curWeap]].laserSight != -1)
      if (weapons[worms[i].weaps[worms[i].curWeap]].laserSight != -3)
      if (worms[i].next_fire[worms[i].curWeap] <= gameTime)
      if (worms[i].ammo[worms[i].curWeap] > 0)
      {
         if (worms[i].dir == DIR_RIGHT)
         {
            cx = worms[i].coords.x + (Engine.laserDist * cosinus[worms[i].aim])/1000;
         }
         else
         {
            cx = worms[i].coords.x - (Engine.laserDist * cosinus[worms[i].aim])/1000;
         }
         cy = worms[i].coords.y + Engine.wormFireOffs + (Engine.laserDist * sinus[worms[i].aim])/1000;
         lineColor = 1; // this is a hack

         tmpX = CurX;
         tmpY = CurY;
         tmpW = i;
         set_trans_blender(0, 0, 0, 127);
         calcAim(cx, cy);
         drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
         do_line(leftpane, worms[i].coords.x, worms[i].coords.y + Engine.wormFireOffs, cx, cy, weapons[worms[i].weaps[worms[i].curWeap]].laserSight, drawSight);
         if (i == player)
         {
            SnipX = tmpSnipX;
            SnipY = tmpSnipY;
         }
         drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
      }
   }
}

void HUD::drawStatus()
{
   int i;
   BITMAP *tmpWorm;

   drawText(buffer, "Wurmz! - serv0r status", 10, 0);
   drawText(buffer, "Game mode:", 210, 0);
   drawText(buffer, gameNames[gameType], 220, 10);
   drawText(buffer, teamplayNames[teamplay], 220, 20);
   drawText(buffer, tacticalNames[tactics], 220, 30);
   drawText(buffer, "Level:", 210, 40);
   drawText(buffer, mapName, 220, 50);

   if (gameType != GAME_KTA)
   {
      drawText(buffer, "Frag limit:", 210, 60);
      sprintf(buf, "%d", fragLimit);
      drawText(buffer, buf, 220, 70);
   }
   else
   {
      drawText(buffer, "Life limit:", 210, 60);
      sprintf(buf, "%d", lifeLimit);
      drawText(buffer, buf, 220, 70);
   }

   drawText(buffer, "Press F1 to start.", 10, 190);

   if (isServer)
   {
      drawText(buffer, "Server address:", 210, 150);
      drawText(buffer, localAddr, 220, 160);
   }
   
   for (i=0; i<GP_MAX_PLAYERS; i++)
   {
      if (worms[i].active)
      {
         circlefill(buffer, 37, 32+i*20, 4, wormColors[worms[i].team]);
         circle(buffer, 37, 32+i*20, 4, GC_WHITE);
         tmpWorm = create_sub_bitmap(wormSkins, 13*16, i*16, 16, 16);
         draw_sprite(buffer, tmpWorm, 10, 28+i*20);
         destroy_bitmap(tmpWorm);
         if (worms[i].active > 1)
            draw_sprite(buffer, fire[4], 10 + Engine.firePos[4].x , 20+i*20 + Engine.wormFireOffs + Engine.firePos[4].y);
         drawText(buffer, worms[i].name, 45, 30+i*20);
      }
      else
      {
         drawText(buffer, "[WAITING]", 45, 30+i*20);
      }
   }
}

void gameViewport::drawRadar()
{
   if (!drawMap) return;
   if (worms[player].health <= 0)
      return;

   set_trans_blender(0, 0, 0, hudAlpha);
   drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);

   int offsetX = worms[player].coords.x - CurX;
   int offsetY = worms[player].coords.y + Engine.wormFireOffs - CurY;

   for (int i = 0; i < GP_NUM_PLAYERS; i++)
   if (i != player)
   if (!tactics || (worms[i].team = worms[player].team))
   if ((gameType != GAME_PRD) || (!worms[i].flags[FL_PREDATOR]))
   if (worms[i].active)
   if (worms[i].health>0)
   if ((abs(worms[i].coords.x - CurX - paneSizeX/2) > paneSizeX/2)
   || (abs(worms[i].coords.y - CurY - paneSizeY/2) > paneSizeY/2))
   {
      int distX = worms[i].coords.x - worms[player].coords.x;
      int distY = worms[i].coords.y - worms[player].coords.y;
      
      if (distX == 0) distX = 1;
      if (distY == 0) distY = 1;

      int point[2];

      if (distX < 0)
      {
         point[0] = -offsetX+1;
         point[1] = (distY * point[0]) / distX;
         if ((point[1] > paneSizeY-offsetY-1)
         || (point[1] < -offsetY))
         if (distY < 0)
         {
            point[1] = -offsetY+1;
            point[0] = (distX * point[1]) / distY;
         }
         else
         {
            point[1] = paneSizeY-offsetY-2;
            point[0] = (distX * point[1]) / distY;
         }
      }
      else
      {
         point[0] = paneSizeX-offsetX-2;
         point[1] = (distY * point[0]) / distX;
         if ((point[1] > paneSizeY-offsetY-1)
         || (point[1] < -offsetY))
         if (distY < 0)
         {
            point[1] = -offsetY+1;
            point[0] = (distX * point[1]) / distY;
         }
         else
         {
            point[1] = paneSizeY-offsetY-2;
            point[0] = (distX * point[1]) / distY;
         }
      }
      
      circlefill(leftpane, offsetX + point[0], offsetY + point[1], 1, wormColors[worms[i].team]);
   }

   drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
};

void gameViewport::drawHud()
{
   int lin, i;
   BITMAP *tmpWorm;
   BITMAP *tempIcon;

   if (worms[player].health>0)
   if (worms[player].buttons[GK_CHANGE])
   {
      drawTextCenter(leftpane, weapons[worms[player].weaps[worms[player].curWeap]].name, worms[player].coords.x - CurX, worms[player].coords.y - CurY - 5);
   }
   if ((gameType != GAME_PRD) && (!tactics))
   for (i=0; i<GP_NUM_PLAYERS; i++)
   if (i != player)
   if (worms[i].active)
   if (worms[i].health>0)
   if (worms[i].coords.y - CurY < paneSizeY)
   if (worms[i].coords.y - CurY >= 0)
   if (worms[i].coords.x - CurX < paneSizeX)
   if (worms[i].coords.x - CurX >= 0)
   {
      drawTextCenter(leftpane, worms[i].name, worms[i].coords.x - CurX, worms[i].coords.y - CurY - 7);
      lin = (30 * worms[i].health) / GP_WORM_HEALTH;
      hline(leftpane, worms[i].coords.x - CurX - 15, worms[i].coords.y - CurY - 1, worms[i].coords.x - CurX - 15+lin, GC_HEALTH);
      if (lin < 30)
      hline(leftpane, worms[i].coords.x - CurX - 14+lin, worms[i].coords.y - CurY - 1, worms[i].coords.x - CurX + 15, GC_RELOAD);
   }
   if ((key[KEY_TAB]) || (gameOver))
   {
      sortScores();
      lin = 0;
      if (gameOver)
         drawTextCenter(leftpane, "Game Over", paneSizeX/2, 5);

      drawText(leftpane, "NAME", 65-paneX, 10-paneY);
      if (gameType == GAME_KTA)
         drawText(leftpane, "LIVES", 195-paneX, 10-paneY);
      drawText(leftpane, "SCORE", 245-paneX, 10-paneY);
      for (i=0; i<GP_NUM_PLAYERS; i++)
      {
         if (scores[i]->active)
         {
            set_trans_blender(0, 0, 0, 127);
            drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
            rectfill(leftpane, 30-paneX, 25+lin*15-paneY, 290-paneX, 38+lin*15-paneY, 0);
            drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
            tmpWorm = create_sub_bitmap(wormSkins, 13*16, scores[i]->num*16, 16, 16);
            draw_sprite(leftpane, tmpWorm, 45-paneX, 28+i*15-paneY);
            destroy_bitmap(tmpWorm);
            if (((gameType == GAME_TAG) || (gameType == GAME_PRD)) && (scores[i]->flags[FL_PREDATOR]))
               drawTextC(leftpane, scores[i]->name, 65-paneX, 30+lin*15-paneY);
            else
               drawText(leftpane, scores[i]->name, 65-paneX, 30+lin*15-paneY);
            sprintf(buf, "%d", scores[i]->lives);
            if (gameType == GAME_KTA)
               drawText(leftpane, buf, 195-paneX, 30+lin*15-paneY);
            sprintf(buf, "%d", scores[i]->kills);
            if (((gameType == GAME_TAG) || (gameType == GAME_PRD)) && (scores[i]->flags[FL_PREDATOR]))
               drawTextC(leftpane, buf, 245-paneX, 30+lin*15-paneY);
            else
               drawText(leftpane, buf, 245-paneX, 30+lin*15-paneY);
            lin++;
         }
      }
   }


   set_trans_blender(0, 0, 0, hudAlpha);
   drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
   tempIcon = create_sub_bitmap(imgWeap, 101*7, 0, 7, 7); // fixme
   draw_trans_sprite(leftpane, tempIcon, hBarX, hBarY);
   destroy_bitmap(tempIcon);
   tempIcon = create_sub_bitmap(imgWeap, 100*7, 0, 7, 7); // fixme
   draw_trans_sprite(leftpane, tempIcon, aBarX, aBarY);
   destroy_bitmap(tempIcon);

   rectfill(leftpane, hBarX+7, hBarY, hBarX+77, hBarY+4, 64*65536+64*256+64);
   rectfill(leftpane, aBarX+7, aBarY, aBarX+77, aBarY+4, 64*65536+64*256+64);

   if (worms[player].ammo[worms[player].curWeap]>0)
   {
      lin = (68 * worms[player].ammo[worms[player].curWeap]) / weapons[worms[player].weaps[worms[player].curWeap]].maxAmmo;
      i = (256*2 * (worms[player].ammo[worms[player].curWeap]) / weapons[worms[player].weaps[worms[player].curWeap]].maxAmmo);
      i = makecol(((512-i)>255 ? 255 : (512-i)), (i>255 ? 255 : i), 0);
      rectfill(leftpane, aBarX+8, aBarY+1, aBarX+8+lin, aBarY+3, i);
   }
   else if (!gameOver)
   {
      lin = (68 * worms[player].reload[worms[player].curWeap]) / weapons[worms[player].weaps[worms[player].curWeap]].loadTime;
      rectfill(leftpane, aBarX+8, aBarY+1, aBarX+8+lin, aBarY+3, GC_RELOAD);
   }
   if (worms[player].health>0)
   {
      lin = (68 * worms[player].health) / GP_WORM_HEALTH;
      i = (256*2 * (worms[player].health) / GP_WORM_HEALTH);
      i = makecol(((512-i)>255 ? 255 : (512-i)), (i>255 ? 255 : i), 0);
      rectfill(leftpane, hBarX+8, hBarY+1, hBarX+8+lin, hBarY+3, i);
   }
   else if (!gameOver)
   {
      if ((worms[player].lives>0) || (gameType != GAME_KTA))
      {
         lin = (68 * (GP_WORM_RESPAWN - (worms[player].next_spawn - gameTime))) / GP_WORM_RESPAWN;
         rectfill(leftpane, hBarX+8, hBarY+1, hBarX+8+lin, hBarY+3, GC_RELOAD);
      }
   }
   drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);

   if (worms[player].health <= 0)
   {
      if ((worms[player].lives>0) || (gameType != GAME_KTA))
      {
         drawTextCenter(leftpane, "Press CHANGE to respawn...", paneSizeX/2, paneSizeY/2 - 3);
         sprintf(buf, "%d lives left.", worms[player].lives);
         if (gameType == GAME_KTA)
         drawTextCenter(leftpane, buf, paneSizeX/2, paneSizeY/2 + 3);
      }
      else
      {
         drawTextCenter(leftpane, "Game over", paneSizeX/2, paneSizeY/2);
      }
   }
}

void gameViewport::drawCrosshair()
{
   int cx, cy;
   BITMAP *tempCross;
   tempCross = create_sub_bitmap(crosshair, worms[player].flags[FL_CROSSHAIR]*7, 0, 7, 7);

   if (worms[player].health<=0) return;

   if (worms[player].dir == DIR_RIGHT)
   {
         cx = worms[player].coords.x + (Engine.crosshairDist * cosinus[worms[player].aim])/1000;
   }
   else
   {
         cx = worms[player].coords.x - (Engine.crosshairDist * cosinus[worms[player].aim])/1000;
   }
   cy = worms[player].coords.y + Engine.wormFireOffs + (Engine.crosshairDist * sinus[worms[player].aim])/1000;
   draw_sprite(leftpane, tempCross, cx - CurX - 3, cy - CurY - 3);
   destroy_bitmap(tempCross);
}

#define outlineText() \
   if (outlineFonts) \
   for (j=0; j<temp->h; j++) \
   for (i=0; i<temp->w; i++) \
   { \
      if (getpixel(temp, i, j) == GC_PINK) \
      if (((i>0) && ((k = getpixel(temp, i-1, j)) != 0) && (k != GC_PINK)) \
      || ((j>0) && ((k = getpixel(temp, i, j-1)) != 0) && (k != GC_PINK)) \
      || ((i+1<temp->w) && ((k = getpixel(temp, i+1, j)) != 0) && (k != GC_PINK)) \
      || ((j+1<temp->h) && ((k = getpixel(temp, i, j+1)) != 0) && (k != GC_PINK))) \
      { \
         putpixel(temp, i, j, 0); \
      } \
   }


void drawText(BITMAP *bmp, char *text, int x, int y)
{
   BITMAP *temp;
   int i, j, k;
   temp = create_bitmap(4*strlen(text)+2, 6+2);
   clear_to_color(temp, GC_PINK);
   for (i=0; i<strlen(text); i++)
   {
//      blit(gameFont, temp, 4*((unsigned char)text[i]), 0, 4*i, 0, 4, 6);
      blit(gameFont, temp, 4*((unsigned char)text[i]), 0, 1+4*i, 1, 4, 6);
   }
   outlineText();
   draw_sprite(bmp, temp, x-1, y-1);
   destroy_bitmap(temp);
}

void drawTextC(BITMAP *bmp, char *text, int x, int y)
{
   BITMAP *temp;
   int i, j, k;
   temp = create_bitmap(4*strlen(text)+2, 6+2);
   clear_to_color(temp, GC_PINK);
   for (i=0; i<strlen(text); i++)
   {
      blit(gameFont, temp, 4*((unsigned char)text[i] | 128), 0, 4*i+1, 1, 4, 6);
   }
   outlineText();
   draw_sprite(bmp, temp, x-1, y-1);
   destroy_bitmap(temp);
}

void drawTextCenter(BITMAP *bmp, char *text, int x, int y)
{
   BITMAP *temp;
   int i, j, k;
   temp = create_bitmap(4*strlen(text)+2, 6+2);
   clear_to_color(temp, GC_PINK);
   for (i=0; i<strlen(text); i++)
   {
      blit(gameFont, temp, 4*((unsigned char)text[i]), 0, 4*i+1, 1, 4, 6);
   }
   outlineText();
   draw_sprite(bmp, temp, x-(2*strlen(text))-1, y-1);
   destroy_bitmap(temp);
}

int blend(int s, int e, int amount)
{
   int r,g,b;
   int r2,g2,b2;
   r = getr(s);
   g = getg(s);
   b = getb(s);
   r2 = getr(e);
   g2 = getg(e);
   b2 = getb(e);
   r = (r * (1000-amount) + r2 * amount) / 1000;
   g = (g * (1000-amount) + g2 * amount) / 1000;
   b = (b * (1000-amount) + b2 * amount) / 1000;
   return makecol(r,g,b);
}

void renderSkin(int num, int skin, int team)
{
   int i, j, c;
   int k;
   int colors[5], replace[5];

   colors[0] = makecol(60, 60, 144);
   colors[1] = makecol(80, 80, 192);
   colors[2] = makecol(104, 104, 248);
   colors[3] = makecol(136, 136, 244);
   colors[4] = makecol(180, 180, 244);
   replace[0] = blend(wormColors[team], 0, 400);
   replace[1] = blend(wormColors[team], 0, 200);
   replace[2] = wormColors[team];
   replace[3] = blend(wormColors[team], GC_WHITE, 200);
   replace[4] = blend(wormColors[team], GC_WHITE, 400);
   
   for (j=0; j<16; j++)
   for (i=0; i<432; i++)
   {
      k = getpixel(imgWorm, i, skin*16+j);
      for (c=0; c<5; c++)
      if (k == colors[c])
      {
         k = replace[c];
      }
      putpixel(wormSkins, i, num*16+j, k);
   }
}

void fadeIn()
{
   int i;
   for (i=0; i<16; i++)
   {

      blit(imgLoading, buffer, 0, 0, 0, 0, 320, 200);
      set_trans_blender(0, 0, 0, 255-i*16);
      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
      rectfill(buffer, 0, 0, 320, 200, 0);
      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);

//      fblend_fade_to_color(imgLoading, buffer, 0, 0, 0, i*16);

      while (!allowFrame) { rest(0); }
      allowFrame = 0;

      render();
   }
}

void fadeOut()
{
   int i;
   for (i=0; i<16; i++)
   {

      blit(imgLoading, buffer, 0, 0, 0, 0, 320, 200);
      set_trans_blender(0, 0, 0, i*16);
      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
      rectfill(buffer, 0, 0, 320, 200, 0);
      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);

//      fblend_fade_to_color(imgLoading, buffer, 0, 0, 0, 255 - i*16);

      while (!allowFrame) { rest(0); }
      allowFrame = 0;

      render();
   }
   clear_to_color(buffer,0);
   render();
}

