#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 "defs.h"
#include "console.h"
#include "ai.h"
#include "engine.h"
#include "gfx.h"
#include "init.h"
#include "items.h"
#include "maths.h"
#include "player.h"
#include "random.h"
#include "rules.h"
#include "sound.h"
#include "weapons.h"

#include "ai_lua.h"

void spawnWeaponObject(long x, long y, worm *owner, char weap, long vx, long vy)
{
   int i = 0;
   while ((weapon_objs[i].active) && (i < GP_MAX_OBJECTS-1)) i++;

   weapon_objs[i].visible = 0;
   weapon_objs[i].active = 1;
   weapon_objs[i].OldX = weapon_objs[i].x = x;
   weapon_objs[i].OldY = weapon_objs[i].y = y;
   weapon_objs[i].CurX = x/1000;
   weapon_objs[i].CurY = y/1000;
   weapon_objs[i].weap = &weapons[weap];
   weapon_objs[i].owner = owner;
   weapon_objs[i].VelX = vx;
   weapon_objs[i].VelY = vy;
   weapon_objs[i].frame=0;
   weapon_objs[i].dir = owner->dir;
   weapon_objs[i].aim = owner->aim;
   weapon_objs[i].countTrail = 0;
   weapon_objs[i].countSTrail = 0;
   if (weapons[weap].weapType == WEAP_DIRECTIONAL)
   {
      weapon_objs[i].frame = calcFrame(vx,vy);
   }
   if (weapons[weap].randomExpTime>0)
   {
      weapon_objs[i].localTime = gameTime - (RANDOM() * weapons[weap].randomExpTime)/1000;
   }                                    
   else
   {
      weapon_objs[i].localTime = gameTime;
   }
   if (weapons[weap].sndLoop)
   {
      weapon_objs[i].sndChan = Sound.loopSound(weapons[weap].sndToLoop, x/1000, y/1000, vx, vy);
   }
}

long RANDOM()
{
   randomPos++;
   if (randomPos>=10000)
      randomPos=0;
   return randomTab[randomPos];
}

void fireWeapon(worm *owner)
{
   long cx, cy, vx, vy, rnd;
   int i;
   weapon *weap;

   long tm, tl, tvx, tvy;

   if (owner->ammo[owner->curWeap] <= 0) return;


   weap = &weapons[owner->weaps[owner->curWeap]];
   if (weap->soundNum != -1)
   {
      Sound.playSound(weap->soundNum, owner->coords.x, owner->coords.y);
   }

   if ((weap->ejectShells == 1) ||
       ((weap->ejectShells > 2) && (abs(RANDOM())%(weap->ejectShells - 1) == 0)))
   {
      fireShrapnel(owner, owner->pos.x, owner->pos.y + Engine.wormFireOffs * 1000, 0, -1000, shellObject, 0, 1);
   }
   
   for (i=0; i<weap->numObjects; i++)
   {
      rnd = (weap->speedVariation * abs(RANDOM()))/1000;
      if (owner->dir == DIR_RIGHT)
      {
         cx = owner->pos.x + Engine.weapSpawnDist * cosinus[owner->aim];
         vx = ((weap->initSpeed + rnd) * cosinus[owner->aim])/1000;
         if  (weap->spread>0)
            vx += (RANDOM() * weap->spread)/1000;
      }
      else
      {
         cx = owner->pos.x - Engine.weapSpawnDist * cosinus[owner->aim];
         vx = ((-weap->initSpeed + rnd) * cosinus[owner->aim])/1000;
         if  (weap->spread>0)
            vx -= (RANDOM() * weap->spread)/1000;
      }
      cy = owner->pos.y + Engine.wormFireOffs * 1000 + Engine.weapSpawnDist * sinus[owner->aim];
      vy = ((weap->initSpeed + rnd) * sinus[owner->aim])/1000;
      if  (weap->spread>0)
         vy += (RANDOM() * weap->spread)/1000;

      if (weap->affectedByWorm)
      {
#ifdef OLD_AIM
         tvx = (owner->vel.x * weap->affectedByWorm) / (weap->physicsLoop * 1000);
         tvy = (owner->vel.y * weap->affectedByWorm) / (weap->physicsLoop * 1000);
#else
         tl = (vx * owner->vel.x + vy * owner->vel.y)/1000;
         tm = (vx*vx + vy*vy)/1000;

         tvx = (vx * tl)/tm;
         tvy = (vy * tl)/tm;

         if ((tvx * vx<0) || (tvy * vy<0))
         {
            tvx /= 2;
            tvy /= 2;
         }
#endif
         vx+=tvx;
         vy+=tvy;
      }

      spawnWeaponObject(cx, cy, owner, owner->weaps[owner->curWeap], vx, vy);
   }
   if (owner->dir == DIR_RIGHT)
   {
      vx = -(weap->blowAway * cosinus[owner->aim])/1000;
   }
   else
   {
      vx = (weap->blowAway * cosinus[owner->aim])/1000;
   }
   vy = -(weap->blowAway * sinus[owner->aim])/1000;
   owner->vel.x += vx;
   if ((owner->flags[FL_APPLY_GRAV]) || (vy<0))
      owner->vel.y += vy;
   owner->next_fire[owner->curWeap] = gameTime + weap->fireDelay;
   owner->show_fire = gameTime + weap->showCone;
   owner->ammo[owner->curWeap]--;
}

void inflictDamage(worm *attacker, worm *prey, int amount, long vx, long vy, weapon *weap)
{
   int subamnt;
   subamnt = amount;
   if ((teamplay) && (attacker != prey) && (attacker->team == prey->team))
   if (subamnt > 0)
   subamnt = (subamnt * Engine.wormFriendlyFire)/1000;

   if (subamnt == 0) return;
   if (prey->health <= 0) return;

   if ((subamnt > 0) && (prey->localTime + Engine.wormSafeTime > gameTime))
    return;

   if (attacker != prey)
   if (attacker != prey->enemy)
   if ((!teamplay) || (prey->team != attacker->team))
   if (
       (!prey->enemy) ||
       (HYPOT(attacker->pos.x - prey->pos.x, attacker->pos.y - prey->pos.y) < HYPOT(prey->enemy->pos.x - prey->pos.x, prey->enemy->pos.y - prey->pos.y))
      )
      prey->enemy = attacker;
      
   if ((prey->health -= subamnt) <= 0)
   {
      prey->health = 0;
      playerDead(attacker, prey, weap);
      fireShrapnel(prey, prey->pos.x, prey->pos.y + Engine.wormFireOffs * 1000, 0, 0, fleshObject, 0, fleshCount);
      fireShrapnel(prey, prey->pos.x, prey->pos.y + Engine.wormFireOffs * 1000, 0, 0, bloodObject, 0, fleshCount);
      Sound.playSound(deathSndFirst + abs(RANDOM())%deathSndCount, prey->coords.x, prey->coords.y);
   }
   else
   {
      if (prey->health > GP_WORM_HEALTH)
         prey->health = GP_WORM_HEALTH;
      if (subamnt > 0)
      {
         fireShrapnel(prey, prey->pos.x, prey->pos.y + Engine.wormFireOffs * 1000, vx, vy, bloodObject, 0, (amount+1)/1);
         Sound.playSound(hurtSndFirst + abs(RANDOM())%hurtSndCount, prey->coords.x, prey->coords.y);
      }
   }
   if (prey->isAi)
      aiLuaOnDamage(prey, attacker, subamnt);
}

void dig(worm *owner)
{
   int cx, cy;
   int i;

   if (IMG_DIG_HOLE == -1)
      return;

   if (owner->dir == DIR_RIGHT)
   {
      cx = (owner->pos.x + GP_DIG_DIST * cosinus[owner->aim])/1000;
   }
   else
   {
       cx = (owner->pos.x - GP_DIG_DIST * cosinus[owner->aim])/1000;
   }
   cy = (owner->pos.y + GP_DIG_DIST * sinus[owner->aim])/1000 + Engine.wormFireOffs;

   cutHole(holes[IMG_DIG_HOLE], cx, cy);

   owner->next_dig = gameTime + GP_DIG_DELAY;
}

int createShrapnel(worm *owner, long x, long y, long vx, long vy, int type, weapon *weap)
{
   int i = 0;
   while ((wobj_objs[i].active) && (i < GP_MAX_WOBJECTS)) i++;
   if (i<GP_MAX_WOBJECTS)
   {
      wobj_objs[i].visible = 0;
      wobj_objs[i].active = 1;
      wobj_objs[i].OldX = wobj_objs[i].x = x;
      wobj_objs[i].OldY = wobj_objs[i].y = y;
      wobj_objs[i].CurX = wobj_objs[i].x/1000;
      wobj_objs[i].CurY = wobj_objs[i].y/1000;
      wobj_objs[i].frame = 0;
      wobj_objs[i].VelX = vx;
      wobj_objs[i].VelY = vy;
      wobj_objs[i].obj = &wobjs[type];
      wobj_objs[i].weap = weap;
      wobj_objs[i].owner = owner;
      if (wobjs[type].randomExpTime>0)
      {
         wobj_objs[i].localTime = gameTime - (RANDOM() * wobjs[type].randomExpTime)/1000;
      }
      else
      {
         wobj_objs[i].localTime = gameTime;
      }
      if (wobjs[type].sndLoop)
      {
         wobj_objs[i].sndChan = Sound.loopSound(wobjs[type].sndToLoop, x/1000, y/1000, vx, vy);
      }
      return i;
   }
   return -1;
}

int fireShrapnel(worm *owner, long x, long y, long vx, long vy, int type, weapon *weap, int count)
{
   int dir;
   int aim;
   long velx, vely, rnd;
   int i, j;

   j = -1;
   
   for (i=0; i<count; i++)
   {
      aim = (abs(RANDOM())*33)/1000;
      dir = abs(RANDOM())%2;
      rnd = (wobjs[type].speedVariation * abs(RANDOM()))/1000;
      if (dir == 0)
      {
         velx = (cosinus[aim] * (wobjs[type].initSpeed + rnd))/1000;
      }
      else
      {
         velx = -(cosinus[aim] * (wobjs[type].initSpeed + rnd))/1000;
      }
      velx += vx;
      vely = vy + (sinus[aim] * (wobjs[type].initSpeed + rnd))/1000;
      j = createShrapnel(owner, x+velx, y+vely, velx, vely, type, weap);
   }
   return j;
}

void createSpecial (worm *owner, int x, int y, int type, weapon *weap, char *vis)
{
   int i = 0;
   int distX, distY;
   long velX, velY, dist;
   interact *tempItem;

   if ((type < 0) || (type >= sizeof(specials)))
   {
      Console.WriteCF("Error: trying to spawn a non-existant special #%d", type);
      return;  
   }   

   while ((special_objs[i].active) && (i < GP_MAX_SPECIALS)) i++;
   if (i == GP_MAX_SPECIALS) return;

   special_objs[i].spec = &specials[type];
   special_objs[i].active = 1;
   special_objs[i].x = x;
   special_objs[i].y = y;
   special_objs[i].localTime = gameTime;
   special_objs[i].visible = vis;

   if (specials[type].soundNum != -1)
   {
      Sound.playSound(specials[type].soundNum + abs(RANDOM())%specials[type].numSounds, x, y);
   }

   if (specials[type].flashTime)
   for (int v=0; v<numViewports; v++)
   {
      distX = (x - viewport[v].CurX - viewport[v].paneSizeX/2);
      distY = (y - viewport[v].CurY - viewport[v].paneSizeY/2);

      if (worms[viewport[v].player].active)
      if (worms[viewport[v].player].health>0)
      if ((!tactics) || (visible(&worms[viewport[v].player], x, y)))
      if (abs(distX) < viewport[v].paneSizeX/2)
      if (abs(distY) < viewport[v].paneSizeY/2)
      if (gameTime + specials[type].flashTime > worms[viewport[v].player].flashEnd)
      {
         worms[viewport[v].player].flashEnd = gameTime + specials[type].flashTime;
      }
   
      if (specials[type].shakeTime)
      if (worms[viewport[v].player].active)
      if (worms[viewport[v].player].health>0)
      if (abs(distX) < viewport[v].paneSizeX/2)
      if (abs(distY) < viewport[v].paneSizeY/2)
      if (gameTime + specials[type].shakeTime > worms[viewport[v].player].shakeEnd)
      {
         worms[viewport[v].player].shakeEnd = gameTime + specials[type].shakeTime;
      }
   }

   tempItem = itemList;
   if (itemBlowup)
   if (specials[type].damage > 0)
   while (tempItem)
   {
      distX = tempItem->CurX - x;
      distY = tempItem->CurY - y;
      dist = abs(distX) + abs(distY);
      if (dist <= specials[type].detectDist)
      {
         tempItem->blow(owner);
      }
      tempItem = tempItem->next;
   }

   for (i=0; i<GP_NUM_PLAYERS; i++)
   if (worms[i].active)
   if (worms[i].health>0)
   {
      distX = worms[i].coords.x - x;
      distY = worms[i].coords.y + Engine.wormFireOffs - y;
      dist = abs(distX) + abs(distY);
      if (dist <= specials[type].detectDist)
      {
         if (dist == 0)
            dist = 1;
         if (gameTime + specials[type].freezeTime > worms[i].freezeEnd)
         {
            worms[i].freezeEnd = gameTime + specials[type].freezeTime;
         }
         velX = (distX * specials[type].blowOnHit) / dist;
         velY = (distY * specials[type].blowOnHit) / dist;
         inflictDamage(owner, &worms[i], specials[type].damage, velX, velY, weap);
         if (specials[type].pushWorms)
         {
            worms[i].vel.x += velX;
            if ((worms[i].flags[FL_APPLY_GRAV]) || (velY<0))
               worms[i].vel.y += velY;
         }
      }
   }

   if (specials[type].pushObjects)
   {
      for (i=0; i<GP_MAX_OBJECTS; i++)
      if (weapon_objs[i].active)
      {
         distX = weapon_objs[i].CurX - x;
         distY = weapon_objs[i].CurY - y;
         dist = abs(distX) + abs(distY);
         if (dist <= specials[type].detectDist)
         {
            if (dist == 0)
               dist = 1;
            velX = (distX * specials[type].blowOnHit) / dist;
            velY = (distY * specials[type].blowOnHit) / dist;
            weapon_objs[i].VelX += velX;
            weapon_objs[i].VelY += velY;
         }
      }
      for (i=0; i<GP_MAX_WOBJECTS; i++)
      if (wobj_objs[i].active)
      {
         distX = wobj_objs[i].CurX - x;
         distY = wobj_objs[i].CurY - y;
         dist = abs(distX) + abs(distY);
         if (dist <= specials[type].detectDist)
         {
            if (dist == 0)
               dist = 1;
            velX = (distX * specials[type].blowOnHit) / dist;
            velY = (distY * specials[type].blowOnHit) / dist;
            wobj_objs[i].VelX += velX;
            wobj_objs[i].VelY += velY;
         }
      }
   }
   if (specials[type].damage > 0)
   {
      for (i=0; i<GP_MAX_OBJECTS; i++)
      if (weapon_objs[i].active)
      {
         distX = weapon_objs[i].CurX - x;
         distY = weapon_objs[i].CurY - y;
         dist = abs(distX) + abs(distY);
         if (dist <= specials[type].detectDist)
         {
            if (weapon_objs[i].weap->expOnExp)
            {
               weapExplode(&weapon_objs[i]);
            }
         }
      }
      for (i=0; i<GP_MAX_WOBJECTS; i++)
      if (wobj_objs[i].active)
      {
         distX = wobj_objs[i].CurX - x;
         distY = wobj_objs[i].CurY - y;
         dist = abs(distX) + abs(distY);
         if (dist <= specials[type].detectDist)
         {
            if (wobj_objs[i].obj->expOnExp)
            {
               shrapExplode(&wobj_objs[i]);
            }
         }
      }
   }
   if (specials[type].hole != -1)
   {
      cutHole(holes[specials[type].hole],x,y);
   }
}

void weapExplode(weapon_obj *weap)
{
   if (!weap->active) return;
   weap->active = 0;
   if (weap->weap->sndLoop)
   {
      Sound.stopSound(weap->sndChan);
   }
   if (weap->weap->explosion != -1)
      createSpecial(weap->owner, weap->CurX, weap->CurY, weap->weap->explosion, weap->weap, &weap->visible);
   if (weap->weap->shrapCount > 0)
   {
      if (weap->weap->shrapAffected)
      {
         fireShrapnel(weap->owner, weap->OldX, weap->OldY, (weap->VelX * weap->weap->shrapAffected)/1000, (weap->VelY * weap->weap->shrapAffected)/1000, weap->weap->shrapnel, weap->weap, weap->weap->shrapCount);
      }
      else
      {
         fireShrapnel(weap->owner, weap->OldX, weap->OldY, 0, 0, weap->weap->shrapnel, weap->weap, weap->weap->shrapCount);
      }
   }
}

void shrapExplode(wobj_obj *obj)
{
   if (!obj->active) return;
   if (obj->obj->sndLoop)
   {
      Sound.stopSound(obj->sndChan);
   }
   if (obj->obj->shrapCount > 0)
   {
      if (obj->obj->shrapAffected)
      {
         fireShrapnel(obj->owner, obj->x, obj->y, (obj->VelX * obj->obj->shrapAffected)/1000, (obj->VelY * obj->obj->shrapAffected)/1000, obj->obj->shrapnel, obj->weap, obj->obj->shrapCount);
      }
      else
      {
         fireShrapnel(obj->owner, obj->x, obj->y, 0, 0, obj->obj->shrapnel, obj->weap, obj->obj->shrapCount);
      }
   }
   obj->active = 0;
   if (obj->obj->explosion != -1)
   {
      createSpecial(obj->owner, obj->CurX, obj->CurY, obj->obj->explosion, obj->weap, &obj->visible);
   }
}

void loadWeapons()
{
   int i, c;
   long range=0;
   long grav, vel, time = 0;
   char temp[256];
   int skinsLoaded = 0;
   
   weapCount = get_config_int("Weapons", "WeaponCount", 0);

   for (i=0; i<weapCount; i++)
   {
      sprintf(buf, "Weapon.%d", i);
      weapons[i].num = i;
      sprintf(weapons[i].name, "%s", get_config_string(buf, "Name", "Unknown"));
      weapons[i].affectedByWorm = get_config_int(buf, "AffectedByWorm", 0);
      weapons[i].maxAmmo = get_config_int(buf, "Ammo", 1);

      weapons[i].initSpeed = get_config_int(buf, "InitialSpeed", 1000);
      vel = weapons[i].initSpeed;
      weapons[i].speedVariation = get_config_int(buf, "SpeedVariation", 0);

      weapons[i].bounce = get_config_int(buf, "BounceFactor", 1000);
      weapons[i].fireDelay = get_config_int(buf, "FireDelay", 1);
      weapons[i].frame = get_config_int(buf, "FirstFrame", 0);
      weapons[i].numFrames = get_config_int(buf, "NumFrames", 1);
      weapons[i].animDelay = get_config_int(buf, "FrameDelay", 1);
      weapons[i].gravity = get_config_int(buf, "Gravity", 0);
      grav = abs(weapons[i].gravity);
      weapons[i].timeToExplode = get_config_int(buf, "TimeToExplode", 0);
      weapons[i].explosion = get_config_int(buf, "Explosion", -1);
      weapons[i].shrapnel = get_config_int(buf, "Shrapnel", 0);
      weapons[i].shrapCount = get_config_int(buf, "ShrapnelCount", -1);
      weapons[i].shrapAffected = get_config_int(buf, "AffectShrapnelSpeed", 0);
      weapons[i].expOnGround = get_config_int(buf, "ExplodeOnGround", 0);
      weapons[i].remOnWorm = get_config_int(buf, "RemoveOnWorm", 0);
      weapons[i].expOnWorm = get_config_int(buf, "ExplodeOnWorm", 0);
      weapons[i].spawnSpecial = get_config_int(buf, "SpecialTrail", -1);
      weapons[i].specialDelay = get_config_int(buf, "SpecialTrailDelay", 1);
      weapons[i].spawnWobj = get_config_int(buf, "ObjectTrail", -1);
      weapons[i].wobjDelay = get_config_int(buf, "ObjectTrailDelay", 1);
      weapons[i].wobjAffect = get_config_int(buf, "AffectObjectSpeed", 0);
      c = get_config_int(buf, "BulletColor", 0);
      weapons[i].bulletColor = makecol((c >> 16)&0xFF, (c >> 8)&0xFF, (c)&0xFF);
      weapons[i].spread = get_config_int(buf, "Spread", 0);
      weapons[i].numObjects = get_config_int(buf, "NumBullets", 1);
      weapons[i].blowAway = get_config_int(buf, "BlowAway", 0);
      weapons[i].blowOnHit = get_config_int(buf, "BlowOnHit", 0);
      weapons[i].showCone = get_config_int(buf, "ShowFireCone", 0);
      weapons[i].laserSight = get_config_int(buf, "LaserSight", -1);
      if (weapons[i].laserSight >= 0)
      {
         c = weapons[i].laserSight;
         weapons[i].laserSight = makecol((c >> 16)&0xFF, (c >> 8)&0xFF, (c)&0xFF);
      }
      weapons[i].loadTime = get_config_int(buf, "LoadTime", 0);
      weapons[i].loadTime = INT((weapons[i].loadTime * Engine.weapLoadTime) / 1000);
      if (weapons[i].loadTime == 0)
         weapons[i].loadTime = 1;
      weapons[i].randomExpTime = get_config_int(buf, "ExpTimeVariation", 0);
      weapons[i].weapType = get_config_int(buf, "WeaponType", 0);
      weapons[i].detectDist = get_config_int(buf, "DetectDist", 0);
      weapons[i].physicsLoop = get_config_int(buf, "PhysicsLoop", 1);
      weapons[i].damage = get_config_int(buf, "Damage", 0);
      weapons[i].soundNum = get_config_int(buf, "Sound", -1);
      weapons[i].animOnGround = get_config_int(buf, "AnimOnGround", 0);
      weapons[i].reloadSnd = get_config_int(buf, "ReloadSound", reloadSnd);
      weapons[i].ejectShells = get_config_int(buf, "EjectShells", 0);
      weapons[i].drunk = get_config_int(buf, "DrunkSpread", 0);
      weapons[i].accel = get_config_int(buf, "Acceleration", 0);
      weapons[i].multiply = get_config_int(buf, "SpeedMultiply", 1000);
      
      weapons[i].sndToLoop = get_config_int(buf, "PlaySound", -1);
      weapons[i].sndLoop = get_config_int(buf, "SoundLoop", 0);

      weapons[i].bounceExp = get_config_int(buf, "BounceExplosion", -1);
      weapons[i].bounceLimit = get_config_int(buf, "BounceLimit", 0);
      weapons[i].expOnObject = get_config_int(buf, "ExplodeOnObjects", 0);
      weapons[i].remOnObject = get_config_int(buf, "RemoveOnObjects", 0);

      weapons[i].expOnExp = get_config_int(buf, "ExplodeOnExplosion", 0);

      weapons[i].freezeTime = get_config_int(buf, "FreezeTime", 0);

      weapons[i].homingAccel = get_config_int(buf, "HomingAccel", 1000);
      weapons[i].homingDist = get_config_int(buf, "HomingDist", 0);
      weapons[i].homingSpeed = get_config_int(buf, "HomingSpeed", 100);

      sprintf(temp, "data/weap/%s", get_config_string(buf, "Skin", ""));

      if (strlen(temp) > 10)
      {
         weapons[i].wormSkin = load_bitmap(temp, backpal);
         if (!weapons[i].wormSkin)
         {
            Console.WriteF("Error loading custom skin file %s", temp);
         }
         skinsLoaded++;
      }

      range = 0;
      time = 0;

      if (grav == 0)
      {
         if (weapons[i].timeToExplode == 0)
         {
            range = 999999999;
         }
         else
         {
            int tmp;
            while (
                   (vel>0) &&
                   (
                    (weapons[i].timeToExplode == 0) ||
                    (time < weapons[i].timeToExplode + (weapons[i].randomExpTime/2))
                   )
                  )
            {
               range += vel * weapons[i].physicsLoop;

               vel += weapons[i].accel;
               vel = (vel * weapons[i].multiply) / 1000;
               time++;
            }
         }
      }
      else
      {
         int tmp;
         while (
                (vel>0) &&
                (
                 (weapons[i].timeToExplode == 0) ||
                 (time < weapons[i].timeToExplode + (weapons[i].randomExpTime/2))
                )
               )
         {
            for (tmp = 0; tmp<weapons[i].physicsLoop; tmp++)
            {
               range += vel;
               vel -= grav;
            }
            vel += weapons[i].accel;
            vel = (vel * weapons[i].multiply) / 1000;
            time++;
         }
      }
      weapons[i].botRange = range;
   }   
   Console.WriteF("Loaded %d weapons...", weapCount);
   Console.WriteF("Loaded %d custom weapon skins...", skinsLoaded);
}

void loadWobjects()
{
   int i, c;
   wobjCount = get_config_int("Weapons", "WeapObjCount", 0);

   for (i=0; i<wobjCount; i++)
   {
      sprintf(buf, "Object.%d", i);
      wobjs[i].num = i;
      wobjs[i].initSpeed = get_config_int(buf, "InitialSpeed", 1000);
      wobjs[i].speedVariation = get_config_int(buf, "SpeedVariation", 0);

      wobjs[i].blowOnHit = get_config_int(buf, "BlowOnHit", 0);
      wobjs[i].bounce = get_config_int(buf, "BounceFactor", 1000);
      wobjs[i].frame = get_config_int(buf, "FirstFrame", 0);
      wobjs[i].numFrames = get_config_int(buf, "NumFrames", 1);
      wobjs[i].animDelay = get_config_int(buf, "FrameDelay", 1);
      wobjs[i].gravity = get_config_int(buf, "Gravity", 0);
      wobjs[i].timeToExplode = get_config_int(buf, "TimeToExplode", 0);
      wobjs[i].explosion = get_config_int(buf, "Explosion", -1);
      wobjs[i].shrapnel = get_config_int(buf, "Shrapnel", 0);
      wobjs[i].shrapCount = get_config_int(buf, "ShrapnelCount", -1);
      wobjs[i].shrapAffected = get_config_int(buf, "AffectShrapnelSpeed", 0);
      wobjs[i].expOnGround = get_config_int(buf, "ExplodeOnGround", 0);
      wobjs[i].remOnWorm = get_config_int(buf, "RemoveOnWorm", 0);
      wobjs[i].expOnWorm = get_config_int(buf, "ExplodeOnWorm", 0);
      wobjs[i].spawnSpecial = get_config_int(buf, "SpecialTrail", -1);
      wobjs[i].specialDelay = get_config_int(buf, "SpecialTrailDelay", 1);
      wobjs[i].spawnWobj = get_config_int(buf, "ObjectTrail", -1);
      wobjs[i].wobjDelay = get_config_int(buf, "ObjectTrailDelay", 1);
      wobjs[i].wobjAffect = get_config_int(buf, "AffectObjectSpeed", 0);
      c = get_config_int(buf, "BulletColor", 0);
      wobjs[i].bulletColor = makecol((c >> 16)&0xFF, (c >> 8)&0xFF, (c)&0xFF);
      wobjs[i].randomExpTime = get_config_int(buf, "ExpTimeVariation", 0);
      wobjs[i].detectDist = get_config_int(buf, "DetectDist", 0);
      wobjs[i].physicsLoop = get_config_int(buf, "PhysicsLoop", 1);
      wobjs[i].damage = get_config_int(buf, "Damage", 0);
      wobjs[i].drawOnGround = get_config_int(buf, "DrawOnGround", 0);
      wobjs[i].animOnGround = get_config_int(buf, "AnimOnGround", 0);
      wobjs[i].drunk = get_config_int(buf, "DrunkSpread", 0);
      wobjs[i].multiply = get_config_int(buf, "SpeedMultiply", 1000);

      wobjs[i].sndToLoop = get_config_int(buf, "PlaySound", -1);
      wobjs[i].sndLoop = get_config_int(buf, "SoundLoop", 0);

      wobjs[i].bounceExp = get_config_int(buf, "BounceExplosion", -1);
      wobjs[i].bounceLimit = get_config_int(buf, "BounceLimit", 0);

      wobjs[i].expOnExp = get_config_int(buf, "ExplodeOnExplosion", 0);

      wobjs[i].freezeTime = get_config_int(buf, "FreezeTime", 0);

   }   

   Console.WriteF("Loaded %d objects...", wobjCount);
}

void loadSpecial()
{
   int i;
   specCount = get_config_int("Weapons", "SpecialObjCount", 0);

   for (i=0; i<specCount; i++)
   {
      sprintf(buf, "Special.%d", i);
      specials[i].num = i;
      specials[i].frame = get_config_int(buf, "FirstFrame", 0);
      specials[i].numFrames = get_config_int(buf, "NumFrames", 1);
      specials[i].animDelay = get_config_int(buf, "FrameDelay", 1);
      specials[i].hole = get_config_int(buf, "Hole", -1);
      specials[i].blowOnHit = get_config_int(buf, "BlowOnHit", 0);
      specials[i].detectDist = get_config_int(buf, "DetectDist", 0);
      specials[i].damage = get_config_int(buf, "Damage", 0);
      specials[i].soundNum = get_config_int(buf, "FirstSound", -1);
      specials[i].numSounds = get_config_int(buf, "NumSounds", 1);
      specials[i].flashTime = get_config_int(buf, "FlashTime", 0);
      specials[i].shakeTime = get_config_int(buf, "ShakeTime", 0);
      specials[i].pushObjects = get_config_int(buf, "PushObjects", 0);
      specials[i].pushWorms = get_config_int(buf, "PushWorms", 1);

      specials[i].freezeTime = get_config_int(buf, "FreezeTime", 0);
   }   
   Console.WriteF("Loaded %d special objects...", specCount);
}

