/*! \file attack.cpp
    \brief All the routines for fighting in ASC
*/


/*
    This file is part of Advanced Strategic Command; http://www.asc-hq.de
    Copyright (C) 1994-2005  Martin Bickel  and  Marc Schellenberger

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING. If not, write to the 
    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
    Boston, MA  02111-1307  USA
*/



#include <stdio.h>
#include "tpascal.inc"
#include "typen.h"
#include "buildingtype.h"
#include "vehicletype.h"
#include "attack.h"
#include "newfont.h"
#include "basegfx.h"
#include "spfst.h"
#include "dlg_box.h"
#include "sgstream.h"
#include "events.h"
#include "loaders.h"
#include "gameoptions.h"
#include "viewcalculation.h"

#include "mapalgorithms.h"

#ifdef sgmain
 #include "soundList.h"
#endif





bool  AttackFormula :: checkHemming ( pvehicle     d_eht,  int     direc )
{ 
   pvehicle     s_eht;

   int x = d_eht->xpos;
   int y = d_eht->ypos; 
   getnextfield(x, y, direc);
   pfield fld = getfield(x,y);

   if ( fld )
      s_eht = fld->vehicle;
   else 
      s_eht = NULL;

   if ( !s_eht )
      return false;

   if ( s_eht->height >= chtieffliegend || d_eht->height >= chtieffliegend )
      return false;

   return attackpossible2n ( s_eht, d_eht );
} 


float AttackFormula :: getHemmingFactor ( int relDir )
{
   const float  maxHemmingFactor = 1.4;  // = +140% !
   const float hemming[sidenum-1]  = { 4, 11, 16, 11, 4 };
   const float maxHemmingSum = 46;
   relDir  %= 6;
   if ( relDir < 0 )
      relDir += sidenum;

   if ( relDir == 5 )
      displaymessage("float AttackFormula :: getHemmingFactor - invalid direction", 1 );
      
   return hemming[relDir]*maxHemmingFactor/maxHemmingSum;
}


float AttackFormula :: strength_hemming ( int  ax,  int ay,  pvehicle d_eht )
{
   float hemm = 0;
   int attackDir = getdirection(ax,ay,d_eht->xpos,d_eht->ypos);
   for ( int i = 0; i < sidenum-1; i++) {

      int direc = i+1 + (attackDir-sidenum/2);
      if (direc < 0)
         direc += sidenum;

      if (direc >= sidenum)
         direc -= sidenum;

      if ( checkHemming (d_eht,direc ))
         hemm += getHemmingFactor(i);
   }

   return  hemm + 1;
}


float AttackFormula :: strength_damage ( int damage )
{
   return 1 - (2.0 * float(damage) / 300.0);
}

float AttackFormula :: strength_experience ( int experience )
{
	float e =		(experience < 0)
				?	0	
				:	experience ;
   return e/maxunitexperience * 2.875 / actmap->getgameparameter( cgp_experienceDivisorAttack );
}

float AttackFormula :: defense_experience ( int experience )
{
   float e =		(experience < 0)
				?	0
				:	experience ;

   return e/maxunitexperience * 1.15 / actmap->getgameparameter( cgp_experienceDivisorDefense );
}

float AttackFormula :: strength_attackbonus ( int abonus )
{
   float a = abonus;
   return a/8;
}


float AttackFormula :: defense_defensebonus ( int defensebonus )
{
   float d = defensebonus;
   return d/8;
}



tfight :: tfight ( void )
{
   synchronedisplay = 0;
}


void tfight :: calc ( void )
{
   int damagefactor = actmap->getgameparameter ( cgp_attackPower );
   const float armordivisor = 5;


   if ( av.strength ) { 
      float absstrength = float(av.strength )
                          * ( 1 + strength_experience ( av.experience ) + strength_attackbonus ( av.attackbonus ) )
                          * strength_damage ( av.damage ) 
                          * dv.hemming;

      float absdefense = float(dv.armor / armordivisor )
                          * ( 1 + defense_defensebonus ( dv.defensebonus ) + defense_experience ( dv.experience ) );

      int w = int(dv.damage + absstrength / absdefense * 1000 / damagefactor );

      if (dv.damage > w ) 
         displaymessage("fatal error at attack: \ndecrease of damage d!",1);

      if (dv.damage == w )
         w = dv.damage+1;

      if (w > 100) 
         dv.damage = 100; 
      else 
         dv.damage = w; 

      if ( av.weapcount > 0 )
         av.weapcount--;
      else
         av.weapcount = 0;

      if ( dv.damage >= 100 ) {
         av.experience += 2;
         if ( av.experience > maxunitexperience )
            av.experience = maxunitexperience;

      } else
        if ( av.experience < maxunitexperience )
           av.experience++;
   } 

   if ( dv.strength ) { 
      float absstrength = float(dv.strength )
                          * ( 1 + strength_experience ( dv.experience ) + strength_attackbonus ( dv.attackbonus ) )
                          * strength_damage ( dv.damage ) ;

      float absdefense = float(av.armor / armordivisor)
                          * ( 1 + defense_defensebonus ( av.defensebonus )+ defense_experience ( av.experience ));

      int w = int(av.damage + absstrength / absdefense * 1000 / damagefactor );

      if (av.damage > w ) 
         displaymessage("fatal error at attack: \ndecrease of damage a!",1);

      if (w > 100) 
         av.damage = 100; 
      else 
         av.damage = w; 


      if ( dv.weapcount > 0 )
         dv.weapcount--;
      else
         dv.weapcount = 0;

      if ( av.damage >= 100 ) {
         dv.experience += 2;
         if ( dv.experience > maxunitexperience )
            dv.experience = maxunitexperience;

      } else
        if ( dv.experience < maxunitexperience )
           dv.experience++;
   } 

   if ( av.kamikaze ) 
      av.damage = 100;

}


int positions[][4] = {{ 459,    312,    468,      412  },
                      { 471,    312,    480,      412  },
                      { 483,    312,    492,      412  },
                      { 494,    312,    504,      412  },
                      { 510,    312,    531,      412  },
                      { 542,    312,    563,      412  },
                      { 569,    312,    578,      412  },
                      { 581,    312,    590,      412  },
                      { 593,    312,    602,      412  },
                      { 605,    312,    614,      412  }};

void tfight :: paintbar ( int num, int val, int col )
{
   if ( val ) 
      bar ( agmp->resolutionx - ( 640 - positions[num][0] ) , positions[num][3] - ( val - 1), agmp->resolutionx - ( 640 - positions[num][2] ), positions[num][3], col );

}

void tfight :: paintline ( int num, int val, int col )
{
   if ( val ) 
      line ( agmp->resolutionx - ( 640 - positions[num][0] ) , positions[num][3] - ( val - 1), agmp->resolutionx - ( 640 - positions[num][2] ), positions[num][3] - ( val - 1), col );

}



#define maxdefenseshown 24
#define maxattackshown 24

void tunitattacksunit::calcdisplay( int ad, int dd ) {
  #ifdef sgmain
  SoundList::getInstance().playSound ( SoundList::shooting, _attackingunit->getWeapon(av.weapnum)->getScalarWeaponType(), false, _attackingunit->getWeapon(av.weapnum)->soundLabel );
  #endif
  tfight::calcdisplay(ad,dd);
  #ifdef sgmain
   if ( av.damage >= 100  )
      SoundList::getInstance().playSound( SoundList::unitExplodes , 0, false, _attackingunit->typ->killSoundLabel );
   if ( dv.damage >= 100 )
      SoundList::getInstance().playSound( SoundList::unitExplodes , 0, false, _attackedunit->typ->killSoundLabel );


  #endif
}
  
void tfight :: calcdisplay ( int ad, int dd )
{

   collategraphicoperations cgo ( agmp->resolutionx - ( 640 - 450), 211, agmp->resolutionx - ( 640 - 623 ), 426 );

   if ( !icons.attack.orgbkgr ) {
      icons.attack.orgbkgr = new char [ imagesize ( agmp->resolutionx - ( 640 - 450), 211, agmp->resolutionx - ( 640 - 623 ), 426 ) ];
   }
   getimage ( agmp->resolutionx - ( 640 - 451 ), 211, agmp->resolutionx - ( 640 - 624 ), 426, icons.attack.orgbkgr );

   int bk = 156;

   putimage ( agmp->resolutionx - ( 640 - 450 ), 211, icons.attack.bkgr );
   paintimages ( agmp->resolutionx - ( 640 - 481 ), 226, agmp->resolutionx - ( 640 - 563 ), 226 );

   int ac = 20 + av.color * 8;
   int dc = 20 + dv.color * 8;

   // ursprungs-heilheit zeichnen 
   paintbar ( 4, 100 - av.damage, ac );
   int avd = av.damage;

   paintbar ( 5, 100 - dv.damage, dc );
   int dvd = dv.damage;


   // einkeilung;

   paintbar ( 6, int( 100 *  (dv.hemming - 1 ) / 4), dc );

   if ( av.attackbonus > 0 )
      if ( av.attackbonus > maxattackshown )
         paintbar ( 0, 100, ac - 2 );
      else
         paintbar ( 0, 100 * av.attackbonus / maxattackshown, ac );
   else
      if ( av.attackbonus < -maxattackshown )
         paintbar ( 0, 100 , yellow );
      else
         paintbar ( 0, -100 * av.attackbonus / maxattackshown, yellow );


   if ( av.defensebonus > 0 )
      if ( av.defensebonus > maxdefenseshown )
         paintbar ( 1, 100, ac - 2 );
      else
         paintbar ( 1, 100 * av.defensebonus / maxdefenseshown, ac );
   else
      if ( av.defensebonus < -maxdefenseshown )
         paintbar ( 1, 100, yellow );
      else
         paintbar ( 1, -100 * av.defensebonus / maxdefenseshown, yellow );




   if ( dv.attackbonus > 0 )
      if ( dv.attackbonus > maxattackshown )
         paintbar ( 9, 100, dc - 2 );
      else
         paintbar ( 9, 100 * dv.attackbonus / maxattackshown, dc );
   else
      if ( dv.attackbonus < -maxattackshown )
         paintbar ( 9, 100 , yellow );
      else
         paintbar ( 9, -100 * dv.attackbonus / maxattackshown, yellow );


   if ( dv.defensebonus > 0 )
      if ( dv.defensebonus > maxdefenseshown )
         paintbar ( 8, 100, dc - 2 );
      else
         paintbar ( 8, 100 * dv.defensebonus / maxdefenseshown, dc );
   else
      if ( dv.defensebonus < -maxdefenseshown )
         paintbar ( 8, 100, yellow );
      else
         paintbar ( 8, -100 * dv.defensebonus / maxdefenseshown, yellow );

   cgo.off();

   int t = ticker;
   calc();


   int time1 = CGameOptions::Instance()->attackspeed1;
   if ( time1 <= 0 )
      time1 = 30;

   int time2 = CGameOptions::Instance()->attackspeed2;
   if ( time2 <= 0 )
      time2 = 50;

   int time3 = CGameOptions::Instance()->attackspeed3;
   if ( time3 <= 0 )
      time3 = 30;



   do {
      releasetimeslice();
   } while ( t + time1 > ticker ); /* enddo */




   if ( ad != -1 )
      av.damage = ad;
   if ( dd != -1 )
      dv.damage = dd;



   int steps;
   if ( av.damage - avd > dv.damage - dvd )
      steps = av.damage - avd ;
   else
      steps = dv.damage - dvd;

   int d1 = avd;
   int d2 = dvd;

   int starttime = ticker;
   while ( ticker < starttime + time2 ) {
      int finished = 1000 * (ticker - starttime) / time2;
      int newpos1 = avd + steps * finished / 1000;
      int newpos2 = dvd + steps * finished / 1000;
      cgo.on();

      int i;
      if ( avd != av.damage )
         for ( i = d1; i <= newpos1 && i <= av.damage; i++ )
            paintline ( 4, 100 - i, bk );

      d1 = i;

      int j;
      if ( dvd != dv.damage )
         for ( j = d2; j <= newpos2 && j <= dv.damage; j++ )
            paintline ( 5, 100 - j, bk );

      d2 = j;

      cgo.off();
   }
   for ( int i = 0; i < av.damage; i++ )
      paintline ( 4, 100 - i, bk );

   for ( int i = 0; i < dv.damage; i++ )
      paintline ( 5, 100 - i, bk );

   t = ticker;
   do {
      releasetimeslice();
   } while ( t + time3 > ticker ); /* enddo */
   putimage ( agmp->resolutionx - ( 640 - 451 ), 211, icons.attack.orgbkgr );
}




tunitattacksunit :: tunitattacksunit ( pvehicle &attackingunit, pvehicle &attackedunit, bool respond, int weapon )
{
   setup ( attackingunit, attackedunit, respond, weapon );
}

void tunitattacksunit :: setup ( pvehicle &attackingunit, pvehicle &attackedunit, bool respond, int weapon )
{
   _attackingunit = attackingunit;
   _attackedunit  = attackedunit;

   _pattackingunit = &attackingunit;
   _pattackedunit  = &attackedunit;

   int dist = beeline ( attackingunit->xpos, attackingunit->ypos, attackedunit->xpos, attackedunit->ypos );
   int _weapon;

   if ( weapon == -1 ) {
      pattackweap atw = attackpossible( attackingunit, attackedunit->xpos, attackedunit->ypos );
      int n = -1;
      int s = 0;
      for ( int i = 0; i < atw->count; i++ )
         if ( atw->strength[i] > s ) {
            s = atw->strength[i];
            n = i;
         }

      _weapon = atw->num[n];

      delete atw;

   } else
      _weapon  = weapon;

   const SingleWeapon* weap = attackingunit->getWeapon(_weapon);

   av.strength = int( attackingunit->weapstrength[_weapon]
                      * weapDist.getWeapStrength(weap, dist, attackingunit->height, attackedunit->height )
                      * attackingunit->typ->weapons.weapon[_weapon].targetingAccuracy[attackedunit->typ->movemalustyp] / 100 );
   av.armor  = attackingunit->armor;
   av.damage     = attackingunit->damage;
   av.experience  = attackingunit->experience;
   av.hemming    = 1;
   av.weapnum    = _weapon;
   av.weapcount  = attackingunit->ammo [ _weapon ];
   av.color      = attackingunit->getOwner();
   av.initiative = attackingunit->typ->initiative;
   av.kamikaze   = attackingunit->typ->functions & cfkamikaze;

   pfield field = getfield ( attackingunit->xpos, attackingunit->ypos );

   if ( attackingunit->height <= chfahrend ) {
      if ( dist <= maxmalq )
         av.attackbonus  = field->getattackbonus();
      else
         av.attackbonus = 0;
      av.defensebonus = field->getdefensebonus();
   } else {
      av.attackbonus = 0;
      av.defensebonus = 0;
   }


   if ( dist <= maxmalq  &&  respond  && !av.kamikaze  && fieldvisiblenow( field, attackedunit->getOwner()) ) {
      AttackWeap atw;
      attackpossible2n ( attackedunit, attackingunit, &atw );
      int n = -1;
      int s = 0;
      for ( int i = 0; i < atw.count; i++ )
         if ( atw.strength[i] > s ) {
            s = atw.strength[i];
            n = i;
         }

      if ( n < 0 )
         respond = 0;
      else
         dv.weapnum = atw.num [ n ];

   } else
      respond = 0;

   if ( attackedunit->typ->functions & cfkamikaze )
      respond = 0;

   if ( respond ) {
      weap = attackedunit->getWeapon( dv.weapnum );

      dv.strength  = int( attackedunit->weapstrength[ dv.weapnum ]
                          * weapDist.getWeapStrength(weap, dist, attackedunit->height, attackingunit->height )
                          * attackedunit->typ->weapons.weapon[ dv.weapnum ].targetingAccuracy[attackingunit->typ->movemalustyp] / 100 );
      field = getfield ( attackedunit->xpos, attackedunit->ypos );
      dv.attackbonus  = field->getattackbonus();
      _respond = 1;

      dv.weapcount   = attackedunit->ammo [ dv.weapnum ];

   } else {
      dv.strength = 0;
      dv.attackbonus = 0;
      _respond = 0;
   }


   dv.armor = attackedunit->armor;
   dv.damage    = attackedunit->damage;
   dv.experience = attackedunit->experience;
   if ( dist <= maxmalq && attackingunit->height < chtieffliegend )
      dv.hemming = strength_hemming ( attackingunit->xpos, attackingunit->ypos, attackedunit );
   else
      dv.hemming = 1;


   if ( attackedunit->height <= chfahrend )
      dv.defensebonus = getfield ( attackedunit->xpos, attackedunit->ypos ) -> getdefensebonus();
   else
      dv.defensebonus = 0;

   dv.color      = attackedunit->getOwner();
   dv.initiative = attackedunit->typ->initiative;
}



void tunitattacksunit :: setresult ( void )
{
   _attackingunit->experience = av.experience;
   _attackingunit->ammo[ av.weapnum ] = av.weapcount;

//   if ( _attackingunit->reactionfire.getStatus() >= Vehicle::ReactionFire::ready )
//      _attackingunit->reactionfire.enemiesAttackable &= 0xff ^ ( 1 <<  dv.color );

   _attackingunit->postAttack();

   _attackedunit->damage    = dv.damage;
   _attackingunit->damage    = av.damage;

   if ( _respond ) {
      _attackedunit->experience = dv.experience;
      _attackedunit->ammo[ dv.weapnum ] = dv.weapcount;
   }

   /* If the attacking vehicle was destroyed, remove it */
   if ( _attackingunit->damage >= 100 ) {
     delete *_pattackingunit;
     *_pattackingunit = NULL;
   }

   /* If the attacked vehicle was destroyed, remove it */
   if ( _attackedunit->damage >= 100 ) {
     delete *_pattackedunit;
     *_pattackedunit = NULL;
   }
   actmap->time.set ( actmap->time.turn(), actmap->time.move()+1);
}

void tunitattacksunit :: paintimages ( int xa, int ya, int xd, int yd )
{
   putrotspriteimage ( xa, ya, _attackingunit->typ->picture[0], _attackingunit->color );
   putrotspriteimage ( xd, yd, _attackedunit ->typ->picture[0], _attackedunit->color  );
};








tunitattacksbuilding :: tunitattacksbuilding ( pvehicle attackingunit, int x, int y, int weapon )
{
   setup ( attackingunit, x, y, weapon );
}


void tunitattacksbuilding :: setup ( pvehicle attackingunit, int x, int y, int weapon )
{
   _attackingunit = attackingunit;
   _x = x;
   _y = y;
   _attackedbuilding  = getfield ( x, y ) -> building;

   int dist = beeline ( attackingunit->xpos, attackingunit->ypos, x, y );
   int _weapon;

   if ( weapon == -1 ) {
      pattackweap atw = attackpossible( attackingunit, x, y );
      int n = -1;
      int s = 0;
      for ( int i = 0; i < atw->count; i++ )
         if ( atw->strength[i] > s ) {
            s = atw->strength[i];
            n = i;
         }
   
      _weapon = atw->num[n];
   
      delete atw;
   } else
      _weapon  = weapon;

   const SingleWeapon *weap = &attackingunit->typ->weapons.weapon[_weapon];
   av.strength  = int( attackingunit->weapstrength[_weapon]
                       * weapDist.getWeapStrength(weap, dist, attackingunit->height, _attackedbuilding->typ->buildingheight )
                       * attackingunit->typ->weapons.weapon[_weapon].targetingAccuracy[cmm_building] / 100 );

   av.armor = attackingunit->armor;
   av.damage    = attackingunit->damage;
   av.experience = attackingunit->experience;
   av.hemming    = 1;
   av.weapnum    = _weapon;
   av.weapcount  = attackingunit->ammo [ _weapon ];
   av.color      = attackingunit->getOwner();
   av.initiative = attackingunit->typ->initiative;
   av.kamikaze   = attackingunit->typ->functions & cfkamikaze;

   pfield field = getfield ( attackingunit->xpos, attackingunit->ypos );

   if ( attackingunit->height <= chfahrend ) {
      av.defensebonus = field->getdefensebonus();
      if ( dist <= maxmalq )
         av.attackbonus  = field->getattackbonus();
      else
         av.attackbonus = 0;
   } else {
      av.defensebonus = 0;
      av.attackbonus  = 0;
   }


   dv.strength = 0;
   dv.attackbonus = 0;


   dv.armor = _attackedbuilding->getArmor();
   dv.damage    = _attackedbuilding->damage;
   dv.experience = 0;
   dv.hemming    = 1;

   dv.defensebonus = 0; // getfield ( x, y ) -> getdefensebonus();
   dv.color      = _attackedbuilding->getOwner();
   dv.initiative = 0;
   dv.kamikaze = 0;


}

void tunitattacksbuilding::calcdisplay( int ad, int dd ) {
  #ifdef sgmain
   SoundList::getInstance().playSound( SoundList::shooting  , _attackingunit->getWeapon(av.weapnum)->getScalarWeaponType(), false, _attackingunit->getWeapon(av.weapnum)->soundLabel );
  #endif
  tfight::calcdisplay(ad,dd);
  #ifdef sgmain
   if ( dv.damage >= 100 )
      SoundList::getInstance().playSound( SoundList::buildingCollapses );
  #endif
}


void tunitattacksbuilding :: setresult ( void )
{
   // _attackingunit->experience = av.experience;
   _attackingunit->ammo[ av.weapnum ] = av.weapcount;

   _attackingunit->postAttack();

   _attackingunit->damage    = av.damage;
   _attackedbuilding->damage    = dv.damage;

   /* Remove the attacking unit if it was destroyed */
   if ( _attackingunit->damage >= 100 ) {
      delete _attackingunit;
      _attackingunit = NULL;
   }


   /* Remove attacked building if it was destroyed */
   if ( _attackedbuilding->damage >= 100 ) {
     delete _attackedbuilding ;
     _attackedbuilding = NULL;
   }

   actmap->time.set ( actmap->time.turn(), actmap->time.move()+1);
}

void tunitattacksbuilding :: paintimages ( int xa, int ya, int xd, int yd )
{
   putrotspriteimage ( xa, ya, _attackingunit->typ->picture[0], _attackingunit->color );
   putrotspriteimage ( xd, yd, getfield ( _x, _y ) -> picture , _attackedbuilding->color  );
};




tmineattacksunit :: tmineattacksunit ( pfield mineposition, int minenum, pvehicle &attackedunit )
{
   setup ( mineposition, minenum, attackedunit );
}

void tmineattacksunit :: setup ( pfield mineposition, int minenum, pvehicle &attackedunit )
{
   if ( mineposition->mines.empty() )
      displaymessage(" tmineattacksunit :: setup \n no mine to attack !\n",2 );

   if ( attackedunit->height >= chtieffliegend )
      displaymessage(" tmineattacksunit :: setup \n mine attacks flying unit!\n",2 );


   _mineposition = mineposition;
   _attackedunit = attackedunit;
   _pattackedunit = &attackedunit;


   _minenum = minenum;

   if ( minenum == -1 ) {
      int cnt = 1;
      av.strength = 0;
      for ( tfield::MineContainer::iterator m = mineposition->mines.begin(); m != mineposition->mines.end(); m++ )
         if ( m->attacksunit ( attackedunit )) {
            int strength = m->strength;
            if ( m->type == cmantipersonnelmine   &&  (attackedunit->typ->movemalustyp ==  cmm_trooper ) )
               strength *= 2;

            for ( int j = 1; j < cnt; j++ )
               strength = strength * 2 / 3;

            av.strength += strength;
            cnt++;
         }
   } else {
      Mine& m = mineposition->getMine ( minenum );
      av.strength = m.strength;
      if ( m.type  == cmantipersonnelmine   &&  (attackedunit->typ->movemalustyp ==  cmm_trooper ) )
         av.strength *= 2;
   }

   av.armor = 1;
   av.damage = 0;
   av.experience = 0;
   av.defensebonus = 0;
   av.hemming    = 1;
   av.weapnum = 0;
   av.weapcount = 1;
   av.color = 8;
   av.initiative = 256;
   av.attackbonus = 8;
   av.kamikaze = 0;

   dv.strength = 0;
   dv.armor = attackedunit->armor;
   dv.damage    = attackedunit->damage;
   dv.experience = attackedunit->experience;
   dv.defensebonus = 0;
   dv.hemming    = 1;
   dv.weapnum = 0;
   dv.weapcount = 0;
   dv.color = attackedunit->getOwner();
   dv.initiative = attackedunit->typ->initiative;
   dv.attackbonus = 0;
   dv.kamikaze = 0;
}

void tmineattacksunit::calcdisplay( int ad, int dd ) {
  #ifdef sgmain
  SoundList::getInstance().playSound( SoundList::shooting , 1 );
  #endif
  tfight::calcdisplay(ad,dd);
  #ifdef sgmain
   if ( dv.damage >= 100 )
      SoundList::getInstance().playSound( SoundList::unitExplodes , 0, false, _attackedunit->typ->killSoundLabel );
  #endif
}


void tmineattacksunit :: setresult ( void )
{
   if ( _minenum == -1 ) {
      for ( tfield::MineContainer::iterator m = _mineposition->mines.begin(); m != _mineposition->mines.end(); )
         if ( m->attacksunit ( _attackedunit ))
            m = _mineposition->mines.erase ( m );
         else
            m++;
   } else
      _mineposition->removemine ( _minenum );

   _attackedunit->damage = dv.damage;

   /* Remove the mined vehicle if it was destroyed */
   if ( _attackedunit->damage >= 100 ) {
     delete *_pattackedunit;
     *_pattackedunit = NULL;
   }

}

void tmineattacksunit :: paintimages ( int xa, int ya, int xd, int yd )
{
   if ( _minenum == -1 ) {
      tfield::MineContainer::iterator m = _mineposition->mines.begin();
      while ( ! m->attacksunit  ( _attackedunit ))
         m++;

      putspriteimage    ( xa, ya, getmineadress ( m->type ));
   } else
      putspriteimage    ( xa, ya, getmineadress ( _mineposition->getMine(_minenum).type ));

   putrotspriteimage ( xd, yd, _attackedunit->typ->picture[0], _attackedunit->color  );
}




tunitattacksobject :: tunitattacksobject ( pvehicle attackingunit, int obj_x, int obj_y, int weapon )
{
   setup ( attackingunit, obj_x, obj_y, weapon );
}

void tunitattacksobject :: setup ( pvehicle attackingunit, int obj_x, int obj_y, int weapon )
{

   _x = obj_x;
   _y = obj_y;

   targetField = getfield ( obj_x, obj_y );

   _attackingunit = attackingunit;

   int dist = beeline ( attackingunit->xpos, attackingunit->ypos, obj_x, obj_y );

   for ( tfield::ObjectContainer::reverse_iterator o = targetField->objects.rbegin(); o != targetField->objects.rend(); o++ )
      if ( o->typ->armor > 0 ) {
         _obji = &(*o);
         break;
      }

   int _weapon;

   if ( weapon == -1 ) {

      pattackweap atw = attackpossible( attackingunit, obj_x, obj_y );
      int n = -1;
      int s = 0;
      for ( int i = 0; i < atw->count; i++ )
         if ( atw->strength[i] > s ) {
            s = atw->strength[i];
            n = i;
         }

      _weapon = atw->num[n];

      delete atw;

   } else
      _weapon  = weapon;

   const SingleWeapon *weap = &attackingunit->typ->weapons.weapon[weapon];
   av.strength  = int( attackingunit->weapstrength[weapon]
                       * weapDist.getWeapStrength(weap, dist, attackingunit->height, _obji->typ->getEffectiveHeight() )
                       * attackingunit->typ->weapons.weapon[_weapon].targetingAccuracy[cmm_building] / 100 );

   av.armor = attackingunit->armor;
   av.damage    = attackingunit->damage;
   av.experience = attackingunit->experience;
   av.weapnum    = _weapon;
   av.weapcount   = attackingunit->ammo [ _weapon ];
   av.kamikaze   = attackingunit->typ->functions & cfkamikaze;

   pfield field2 = getfield ( attackingunit->xpos, attackingunit->ypos );

   if ( attackingunit->height <= chfahrend ) {
      av.defensebonus = field2->getdefensebonus();
      if ( dist <= maxmalq )
         av.attackbonus  = field2->getattackbonus();
      else
         av.attackbonus = 0;
   } else {
      av.defensebonus = 0;
      av.attackbonus  = 0;
   }


   dv.strength = 0;
   dv.attackbonus = 0;

   dv.armor = _obji->typ->armor;
   dv.damage    = _obji->damage;
   dv.experience = 0;

   dv.defensebonus = 0; // field  -> getdefensebonus();
   dv.hemming    = 1;
   dv.color = 8 ;
   dv.kamikaze = 0;

}

void tunitattacksobject::calcdisplay( int ad, int dd ) {
  #ifdef sgmain
  SoundList::getInstance().playSound ( SoundList::shooting, _attackingunit->getWeapon(av.weapnum)->getScalarWeaponType(), false, _attackingunit->getWeapon(av.weapnum)->soundLabel );
  #endif
  tfight::calcdisplay(ad,dd);
}


void tunitattacksobject :: setresult ( void )
{
   // _attackingunit->experience = av.experience;
   _attackingunit->ammo[ av.weapnum ] = av.weapcount;

   _attackingunit->postAttack();

   _obji->damage    = dv.damage;
   _attackingunit->damage    = av.damage;

   /* Remove the object if it was destroyed */
   if ( _obji->damage >= 100 ) {
     getfield ( _x, _y )-> removeobject ( _obji->typ );
   }

   /* Remove the attacking unit if it was destroyed */
   if ( _attackingunit->damage >= 100 ) {
      delete _attackingunit;
      _attackingunit = NULL;
   }


   actmap->time.set ( actmap->time.turn(), actmap->time.move()+1);

}


void tunitattacksobject :: paintimages ( int xa, int ya, int xd, int yd )
{
   putrotspriteimage ( xa, ya, _attackingunit->typ->picture[0], _attackingunit->color );
   _obji->typ->display ( xd - 5, yd - 5 );
}









pattackweap  attackpossible( const pvehicle     angreifer, int x, int y)
{
  pattackweap atw = new AttackWeap;

  memset(atw, 0, sizeof(*atw));


   if ((x < 0) || (y < 0) || (x >= actmap->xsize) || (y >= actmap->ysize))
      return atw;
   if (angreifer == NULL)
      return atw;
   if (angreifer->typ->weapons.count == 0)
      return atw;

   pfield efield = getfield(x,y);

   if ( efield->vehicle ) {
      if (fieldvisiblenow(efield, angreifer->color/8))
         attackpossible2n ( angreifer, efield->vehicle, atw );
   }
   else
      if (efield->building != NULL) {
         if (getdiplomaticstatus2(efield->building->color, angreifer->color) == cawar || efield->building->color == 8*8 )
            for (int i = 0; i < angreifer->typ->weapons.count ; i++)
               if (angreifer->typ->weapons.weapon[i].shootable() )
                  if (angreifer->typ->weapons.weapon[i].offensive() )
                     if ( angreifer->typ->weapons.weapon[i].targetingAccuracy[cmm_building] > 0 ) {
                        int tm = efield->building->typ->buildingheight;
                        if (tm & angreifer->typ->weapons.weapon[i].targ) {
                           if (fieldvisiblenow(efield, angreifer->color/8)) {
                              int d = beeline(angreifer->xpos,angreifer->ypos,x,y);
                              if (d <= angreifer->typ->weapons.weapon[i].maxdistance)
                                 if (d >= angreifer->typ->weapons.weapon[i].mindistance) {
                                    if (angreifer->height & angreifer->typ->weapons.weapon[i].sourceheight)
                                       if ( angreifer->typ->weapons.weapon[i].efficiency[6 + getheightdelta ( log2( angreifer->height), log2(tm))] )
                                          if (angreifer->ammo[i] > 0) {
                                             atw->strength[atw->count ] = angreifer->weapstrength[i];
                                             atw->typ[atw->count ] = 1 << angreifer->typ->weapons.weapon[i].getScalarWeaponType() ;
                                             atw->num[atw->count ] = i;
                                             atw->target = AttackWeap::building;
                                             atw->count++;
                                          }

                                 }
                           }
                        }
                     }
      }

   if ( efield->objects.size() ) {
      int n = 0;
      for ( tfield::ObjectContainer::iterator j = efield->objects.begin(); j != efield->objects.end(); j++ )
         if ( j->typ->armor > 0 )
            n++;

      if ( n > 0 )
         if ((efield->vehicle == NULL) && ( efield->building == NULL)) {
            bool found = false;
            for ( tfield::ObjectContainer::reverse_iterator j = efield->objects.rbegin(); j != efield->objects.rend(); ++j ) {
               for ( int i = 0; i <= angreifer->typ->weapons.count - 1; i++)
                  if (angreifer->typ->weapons.weapon[i].shootable() )
                     if ( angreifer->typ->weapons.weapon[i].getScalarWeaponType() == cwcannonn ||
                          angreifer->typ->weapons.weapon[i].getScalarWeaponType() == cwlasern ||
                          angreifer->typ->weapons.weapon[i].getScalarWeaponType() == cwcruisemissile ||
                          angreifer->typ->weapons.weapon[i].getScalarWeaponType() == cwairmissilen ||
                          angreifer->typ->weapons.weapon[i].getScalarWeaponType() == cwgroundmissilen ||
                          angreifer->typ->weapons.weapon[i].getScalarWeaponType() == cwbombn ) {
                        if ( angreifer->typ->weapons.weapon[i].targetingAccuracy[cmm_building] )
                              if (fieldvisiblenow(efield, angreifer->color/8)) {
                                 int d = beeline(angreifer->xpos,angreifer->ypos,x,y);
                                 if (d <= angreifer->typ->weapons.weapon[i].maxdistance)
                                    if (d >= angreifer->typ->weapons.weapon[i].mindistance) {
                                       if (angreifer->height & angreifer->typ->weapons.weapon[i].sourceheight )
                                          if ( angreifer->typ->weapons.weapon[i].targ & j->typ->getEffectiveHeight() )
                                             if ( angreifer->typ->weapons.weapon[i].efficiency[6 + getheightdelta ( log2( angreifer->height), log2(j->typ->getEffectiveHeight()))] )
                                                if (angreifer->ammo[i] > 0) {
                                                   atw->strength[atw->count ] = angreifer->weapstrength[i];
                                                   atw->num[atw->count ] = i;
                                                   atw->typ[atw->count ] = 1 << angreifer->typ->weapons.weapon[i].getScalarWeaponType();
                                                   atw->target = AttackWeap::object;
                                                   atw->count++;
                                                   found = true;
                                                }

                                    }
                              }

                     }
               if ( found )
                  return atw;
            }

         }
   }

   return atw;
}


bool attackpossible2u( const pvehicle attacker, const pvehicle target, pattackweap atw, int uheight )
{
   if ( uheight == -1 )
      uheight = target->height;

   pvehicle angreifer = attacker;
   pvehicle verteidiger = target;
   int result = false;
   if ( atw )
      atw->count = 0;

   if ( !angreifer )
     return false ;

   if ( !verteidiger )
     return false ;

   if (angreifer->typ->weapons.count == 0)
     return false ;

   if ( getdiplomaticstatus2 ( angreifer->color, verteidiger->color ) == cawar )
      for ( int i = 0; i < angreifer->typ->weapons.count ; i++)
         for ( int h = 0; h < 8; h++ )
            if ( uheight & (1<<h))
               if (angreifer->typ->weapons.weapon[i].shootable() )
                  if (angreifer->typ->weapons.weapon[i].offensive() )
                     if ( (1<<h) & angreifer->typ->weapons.weapon[i].targ )
                        if (angreifer->height & angreifer->typ->weapons.weapon[i].sourceheight )
                           if ( angreifer->typ->weapons.weapon[i].targetingAccuracy[ verteidiger->typ->movemalustyp] > 0 )
                              if ( angreifer->typ->weapons.weapon[i].efficiency[6 + getheightdelta ( log2( angreifer->height), h)] )
                                 if (angreifer->ammo[i] > 0) {
                                    result = true;
                                    if ( atw ) {
                                       atw->strength[atw->count] = angreifer->weapstrength[i] * angreifer->typ->weapons.weapon[i].targetingAccuracy[ verteidiger->typ->movemalustyp] / 100;
                                       atw->num[atw->count ] = i;
                                       atw->typ[atw->count ] = 1 << angreifer->typ->weapons.weapon[i].getScalarWeaponType();
                                       atw->target = AttackWeap::vehicle;
                                       atw->count++;
                                    }
                                 }

   return result;
}



bool attackpossible28( const pvehicle attacker, const pvehicle target, pattackweap atw )
{
   pvehicle angreifer = attacker;
   pvehicle verteidiger = target;

   int result = false;
   if ( atw )
      atw->count = 0;

   if (angreifer == NULL)
     return false ;

   if (verteidiger == NULL)
     return false ;

   if (angreifer->typ->weapons.count == 0)
     return false ;

   if ( getdiplomaticstatus2 ( angreifer->color, verteidiger->color ) == cawar )
      for ( int i = 0; i < angreifer->typ->weapons.count ; i++)
         if (angreifer->typ->weapons.weapon[i].shootable() )
            if (angreifer->typ->weapons.weapon[i].offensive() )
               if (verteidiger->height & angreifer->typ->weapons.weapon[i].targ )
                  if (minmalq <= angreifer->typ->weapons.weapon[i].maxdistance)
                     if (minmalq >= angreifer->typ->weapons.weapon[i].mindistance)
                        if (angreifer->height & angreifer->typ->weapons.weapon[i].sourceheight )
                           if ( angreifer->typ->weapons.weapon[i].targetingAccuracy[ verteidiger->typ->movemalustyp ] > 0)
                              if ( angreifer->typ->weapons.weapon[i].efficiency[6 + getheightdelta ( log2( angreifer->height), log2(verteidiger->height))] )
                                 if (angreifer->ammo[i] > 0) {
                                    result =  true;
                                    if ( atw ) {
                                       atw->strength[atw->count] = angreifer->weapstrength[i] * angreifer->typ->weapons.weapon[i].targetingAccuracy[ verteidiger->typ->movemalustyp] / 100;
                                       atw->num[atw->count ] = i;
                                       atw->typ[atw->count ] = 1 << angreifer->typ->weapons.weapon[i].getScalarWeaponType();
                                       atw->target = AttackWeap::vehicle;
                                       atw->count++;
                                    }
                                 }

   return result;
}


bool attackpossible2n( const pvehicle attacker, const pvehicle target, pattackweap atw )
{
   pvehicle angreifer = attacker;
   pvehicle verteidiger = target;

   int result = false;
   if ( atw )
      atw->count = 0;

   if (angreifer == NULL)
     return false ;

   if (verteidiger == NULL)
     return false ;

   if (angreifer->typ->weapons.count == 0)
     return false ;

   int dist = beeline ( angreifer, verteidiger );
   if ( getdiplomaticstatus2 ( angreifer->color, verteidiger->color ) == cawar )
      if ( !angreifer->attacked )
         if ( !angreifer->typ->wait || !angreifer->hasMoved() || angreifer->reactionfire.getStatus() == Vehicle::ReactionFire::ready)
            for ( int i = 0; i < angreifer->typ->weapons.count ; i++)
               if (angreifer->typ->weapons.weapon[i].shootable() )
                  if (angreifer->typ->weapons.weapon[i].offensive() )
                     if (verteidiger->height & angreifer->typ->weapons.weapon[i].targ )
                        if (dist <= angreifer->typ->weapons.weapon[i].maxdistance)
                           if (dist >= angreifer->typ->weapons.weapon[i].mindistance)
                              if (angreifer->height & angreifer->typ->weapons.weapon[i].sourceheight )
                                 if ( angreifer->typ->weapons.weapon[i].efficiency[6 + getheightdelta ( log2( angreifer->height), log2(verteidiger->height))] )
                                    if ( angreifer->typ->weapons.weapon[i].targetingAccuracy[ verteidiger->typ->movemalustyp ] > 0)
                                       if (angreifer->ammo[i] > 0) {
                                          result = true;
                                          if ( atw ) {
                                             atw->strength[atw->count] = angreifer->weapstrength[i] * angreifer->typ->weapons.weapon[i].targetingAccuracy[ verteidiger->typ->movemalustyp ] / 100;
                                             atw->num[atw->count ] = i;
                                             atw->typ[atw->count ] = 1 << angreifer->typ->weapons.weapon[i].getScalarWeaponType();
                                             atw->target = AttackWeap::vehicle;
                                             atw->count++;
                                          }
                                       }


   return result;
}

bool vehicleplattfahrbar( const pvehicle     vehicle,
                           const pfield        field)
{
   return false;
/*
   if (vehicle == NULL)
      return ( false );
   if (field == NULL)
      return ( false );
   if (field->vehicle == NULL)
      return ( false );

   if ((vehicle->color != field->vehicle->color) &&
      (vehicle->height == chfahrend) && 
      (field->vehicle->height == chfahrend) && 
      (field->vehicle->functions & cftrooper) && 
      (vehicle->weight() >= fusstruppenplattfahrgewichtsfaktor * field->vehicle->weight()))
      return ( true ); 
   return ( false );
*/
} 


float WeapDist::getWeapStrength ( const SingleWeapon* weap, int dist, int attacker_height, int defender_height, int reldiff  )
{
/*
  int         translat[31]  = { 6, 255, 1, 3, 2, 4, 0, 5, 255, 255, 6, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
                                 255, 255, 255, 255, 255, 255};
*/

   if ( !weap )
      return 0;

   int scalar = weap->getScalarWeaponType();
   if ( scalar >= 31 || scalar < 0 )
      return 0;

   if ( scalar == 1 )     // mine
      return 1;

      /*
   int typ = translat[ scalar ];
   if ( typ == 255 ) {
      displaymessage("tweapdist::getweapstrength: invalid type ", 1 );
      return 1;
   }
   */


   if ( weap->maxdistance == 0 )
      return 0;

   if ( weap->minstrength == 0 )
      return 0;

   if ( reldiff == -1) {
      if ( dist < weap->mindistance || dist > weap->maxdistance ) {
         displaymessage("tweapdist::getweapstrength: invalid range: \n min = %d ; max = %d ; req = %d ",1, weap->mindistance, weap->maxdistance, dist);
         return 0;
      }

      if ( weap->maxdistance - weap->mindistance != 0 )
         reldiff = 255 * (dist - weap->mindistance) / ( weap->maxdistance - weap->mindistance) ;
      else
         reldiff = 0;
   }

//   int minstrength = 255 - 255 * weap->minstrength / weap->maxstrength;

//   int relstrength = 255 - ( 255 - data[typ][reldiff] ) * minstrength / ( 255 - data[typ][255] );

   float relpos = float(reldiff) / 255.0;

/*   if ( weap->maxstrength && weap->minstrength )
      relpos /= float(weap->maxstrength) / float(weap->minstrength);

   return 1.0 - relpos;*/


   float relstrength = weap->maxstrength - relpos * ( weap->maxstrength - weap->minstrength );

   int heightEff = 100;
   if ( attacker_height != -1 && defender_height != -1 ) {
      int hd = getheightdelta ( log2 ( attacker_height ), log2 ( defender_height ));
      heightEff = weap->efficiency[6+hd];
   }

   return relstrength * heightEff / float(weap->maxstrength * 100) ;

/*   if ( attacker_height != -1 && defender_height!= -1 ) {
      int hd = getheightdelta ( log2 ( attacker_height ), log2 ( defender_height ));
      return relstrength * weap->efficiency[6+hd] / 100 ;
   } else
      return relstrength ;
      */
}

WeapDist weapDist;

