#include "allegro.h"

#include "Unit.h"
#include "UnitInput.h"
#include "UnitList.h"
#include "UnitTemplate.h"
#include "UnitCreate.h"
#include "Loader.h"
#include "gameConstants.h"
#include "MusicSound.h"
#include "tasmMath.h"
#include "Round.h"

#include <vector>
#include <string>
using namespace std;

Unit :: Unit (UnitList * u, UnitTemplate * uts, Unit * ownerUnit, fixed pX, fixed pY, fixed dA, fixed dS, int sub)
{
  //drawing
  posX = pX;
  posY = pY;
  drawAngle = dA;
  walkAngle = dA;
  drawScale = dS;

  units = u;
  ut = uts;

  uin = new UnitInput (uts -> uI, sub);

  unitType = new string (uts -> unitType);
  affiliation = uts -> affiliation;
  unitPropertyHitbox = uts -> unitPropertyHitbox;
  unitPropertyDefense = uts -> unitPropertyDefense;
  unitPropertyStatus = uts -> unitPropertyStatus;
  unitPropertyPlayer = uts -> unitPropertyPlayer;
  subUnitType = sub;
  maxHP = uts -> maxHP;
  hitPoints = maxHP;
  timeToLive = uts -> timeToLive;
  addedUnits = uts -> addedUnits;
  charAnim = charAnimIdle;

  //movement
  walking = false;
  walkMag = uts -> walkMag;
  kbVelX = itofix (0);
  kbVelY = itofix (0);
  kbForces = new vector <kbForce> (0);

  cantAttack1 = 0;
  cantAttack2 = 0;
  cantAttack4 = 0;
  cantMove = 0;

  animAttackNum = 0;
  removeThisUnit = false;

  //attack and damage
  hitKB = new kbForce (uts -> hitkbDir, uts -> hitkbMag, uts -> hitkbDur);
  ownUnit = ownerUnit;
  alignStyle = uts -> alignStyle;
  alignDist = uts -> alignDist;
  alignAngleMin = uts -> alignAngleMin;
  alignAngleMax = uts -> alignAngleMax;
  outOfBoundFrames = 0;
  damageTakenThisFrame = 0;
  damageDealt = uts -> damageDealt;
  costOfAttack = uts -> costOfAttack;

  goodStatusDamage = new vector <int> (*uts -> goodStatusDamage);
  badStatusDamage = new vector <int> (*uts -> badStatusDamage);

  goodStatus = new vector <int> (numGoodStatus, 0);
  badStatus = new vector <int> (numBadStatus, 0);

  juiceForPlayer = uts -> juiceForPlayer;
  juiceReward = 0;

  animFrame = 0;
  animFrameMax = 0;
  animCurrent = charAnimIdle;

  moveForwardCounter = 0;

  soundToPlay = soundToPlayNone;
  noMoreSfx = false;

  rnd = NULL;

  //subTypes:
  //   changes the size of the sprite
  //   changes the side that the unit can orbit around the owner (sizeScale * sqrt(pi) * radius)
  //   changes damage and juice properties.
  //PLACE AT END OF FUNCTION

  if (subUnitType == subUnitBig)
    {
      drawScale = fixmul (drawScale, ftofix (1.414));

      if (alignStyle == alignStyleDist)
	alignDist = fixmul (fixmul (alignDist, ftofix (1.414)), ftofix (1.77245));

      damageDealt *= 2;
      hitPoints *= 2;
      maxHP *= 2;
      juiceForPlayer = int(float(juiceForPlayer) * 1.2);
    }
  else if (subUnitType == subUnitBiggest)
    {
      drawScale = fixmul (drawScale, ftofix (2.0));

      if (alignStyle == alignStyleDist)
	alignDist = fixmul (fixmul (alignDist, ftofix (2.0)), ftofix (1.77245));

      damageDealt *= 4;
      hitPoints *= 4;
      maxHP *= 4;
      juiceForPlayer = int(float(juiceForPlayer) * 1.5);
    }
  else if (subUnitType == subUnitTiny)
    {
      drawScale = fixmul (drawScale, ftofix (0.5));

      if (alignStyle == alignStyleDist)
	alignDist = fixmul (fixmul (alignDist, ftofix (0.5)), ftofix (1.77245));

      walkMag *= 2;
      damageDealt /= 2;
      hitPoints /= 2;
      maxHP /= 2;
      juiceForPlayer = int(float(juiceForPlayer) * 0.5);
    }
  else if (subUnitType == subUnitElite)
    {
      drawScale = fixmul (drawScale, ftofix (0.9));

      if (alignStyle == alignStyleDist)
	alignDist = fixmul (fixmul (alignDist, ftofix (0.9)), ftofix (1.77245));

      damageDealt *= 2;
      walkMag *= 2;
      hitPoints *= 2;
      maxHP *= 2;
      juiceForPlayer = int(float(juiceForPlayer) * 0.8);
    }
}

void Unit :: draw (BITMAP * buffer, MusicSound * ms, bool gamePaused)
{
  //draws the unit, and plays sounds.

  //get next frame...if the game isn't paused
  BITMAP * pic = NULL;
  int soundPan = 128;
  int freq;
  fixed ratioSide = itofix (0);

  if (charAnim != getAnimCurrent ())
    { changeAnim (charAnim); }

  pic = getPic (!gamePaused);

  if (pic != NULL)
    pivot_scaled_sprite (buffer, 
			 pic,
			 fixtoi (posX), fixtoi (posY),
			 pic -> w / 2,
			 pic -> h / 2,
			 drawAngle, drawScale);

  //play sounds now
  if (soundToPlay != soundToPlayNone && ut -> soundsToPlay -> at (soundToPlay) != NULL)
    {
      if (rnd == NULL)
	soundPan = 128;
      else
	{
	  //sound placement...assume stereo config
	  ratioSide = fixdiv (posX, rnd -> getArenaSizeX ());
	  soundPan = int (fixtof (ratioSide) * 255.0);
	}

      freq = 1000;

      if (subUnitType == subUnitBig)
	freq = 750;
      else if (subUnitType == subUnitBiggest)
	freq = 500;
      else if (subUnitType == subUnitTiny)
	freq = 2000;

      if (youAreDead())
	soundToPlay = soundToPlayDead;

      if (!noMoreSfx)
	ms -> playSfx (ut -> soundsToPlay -> at (soundToPlay), soundPan, freq);

      if (soundToPlay == soundToPlayDead)
	noMoreSfx = true;

      soundToPlay = soundToPlayNone;
    }
}

void Unit :: decideAction ()
{
  //unit should process what the unit wants to do (move, attack, die, etc) based on unitInput, damage taken, etc etc  

  //unitInput takes user or AI input and resolves what the unit wants to do. Compares with unitHealth to prevent some activities (like attacking while stunned)

  if (uin != NULL)
    uin -> decideAction (this, units, rnd);
}

void Unit :: dealDamage ()
{
  //compares to units and deals damage to other units.
  //unitHitbox should check to see what targets it has hit, and tell those unitHitboxes they've been hit (here's damage, knockback, etc.)
  //assumes unitList is set.
//unitHitbox add up the damage taken at the end of the round. (and updates the position of the unit.)

  //Unit Properties:

  //first, just bounce off

  vector <Unit *> ::iterator the_iterator = units -> beginOfList ();
  vector <Unit *> ::iterator endOfUnitList = units -> endOfList ();
  fixed collideDist = ftofix (0.0);
  fixed distToUnit = ftofix (0.0);
  bool useOffensiveKB = false;
  int collisionResponse;

  while(the_iterator < endOfUnitList) {
    useOffensiveKB = false;
    collisionResponse = shouldCheckToGetHit (*the_iterator, useOffensiveKB);

    if (collisionResponse != collideIgnore)
      {
	//compare distances to centers. 
	if ((*the_iterator) != NULL)
	  {
	    distToUnit = calcDist (posX, posY,
				   (*the_iterator) -> getPosX (),
				   (*the_iterator) -> getPosY ());

	    collideDist = (getPicSize () + (*the_iterator) -> getPicSize ())/2;
	  }
	
	//If too close, deal damage and/or knockback
	if (distToUnit <= collideDist)
	  {
	    if (collisionResponse == collideKnockbackOnly || collisionResponse == collideBoth) getHitKnockback (*the_iterator, useOffensiveKB);

	    if (collisionResponse == collideDamageOnly || collisionResponse == collideBoth) getHitDamage (*the_iterator);
	  }
      }
    the_iterator ++;
  }
}

void Unit :: takeDamage ()
{
  fixed thisFrameMovementX = itofix (0);
  fixed thisFrameMovementY = itofix (0);

  int propertyHitbox = getUnitPropertyHitbox ();
  int propertyPlayer = getUnitPropertyPlayer ();

  //after taking hits from other units' dealdamage, actually takes damage.

  //damage
  if (propertyPlayer & unitPropertyPlayerInvincibleWhenHit && damageTakenThisFrame > 0) addGoodStatus (goodStatusInvicible, 15);

  hitPoints -= damageTakenThisFrame;
  reduceScore (damageTakenThisFrame);

  if (damageTakenThisFrame > 0 && soundToPlay != soundToPlayDead)
    soundToPlay = soundToPlayHit;

  damageTakenThisFrame = 0;

  if (hitPoints > maxHP)
    hitPoints = maxHP;

  //statusAilments
  int i, iMax;
  iMax = goodStatus -> size ();
  for (i = 0; i < iMax; i ++)
    {
      if (goodStatus -> at (i) > 0)
	{
	  goodStatus -> at (i)--;
	  if (goodStatus -> at (i) <= 0)
	    changeGoodStatusMax (i, 0);
	}
    }

  iMax = badStatus -> size ();
  for (i = 0; i < iMax; i ++)
    {
      if (badStatus -> at (i) > 0)
	{
	  badStatus -> at (i)--;
	  if (badStatus -> at (i) <= 0)
	    changeBadStatusMax (i, 0);
	}
    }
  
  //movement and knockback
  //   remember that the screen/cartesian y coordinates are flipped, we need to take the opposite of the vertical component

  if (cantAttack1 > 0)
    cantAttack1 --;
  if (cantAttack2 > 0)
    cantAttack2 --;
  if (cantAttack4 > 0)
    cantAttack4 --;

  if (moveForwardCounter > 0)
    {
      moveForwardCounter --;
      walking = true; 
      walkAngle = drawAngle;
    }

  fixed statusWalkMag = itofix (1);

  if (getBadStatus () -> at (badStatusSlow) > 0)
    statusWalkMag /= 2;
  else if (getGoodStatus () -> at (goodStatusSpeedBoost) > 0)
    statusWalkMag *= 2;

  if (walking)  
    thisFrameMovementX = fixmul (statusWalkMag, fixmul (walkMag, fixsin (walkAngle)));
  thisFrameMovementX += kbVelX;

  if (walking)
    thisFrameMovementY = -1 * fixmul (statusWalkMag, fixmul (walkMag, fixcos (walkAngle)));
  thisFrameMovementY += kbVelY;

  if (cantMove > 0)
  {
     cantMove --;
  }

  //check align style here
  alignStyleMove ();

  //check to see if the unit is still in the arena.
  //compare with arenaW to see what it should do when out of bounds:
  //Stop: don't let it get past the wall!
  //Ignore: go out of bounds. See if I care.
  //Bounce: Reverse knockback! Whee!
  //Push: Gradually apply a knockback force.
  fixed arenaX = rnd -> getArenaSizeX ();
  fixed arenaY = rnd -> getArenaSizeY ();
  int arenaW = rnd -> getArenaWall ();
  int outOfBoundsHoriz = 0;
  int outOfBoundsVert = 0;
  fixed wallPushMag;
  bool ignoreWallKnockback = false;
  bool stopKB = false;

  if (getUnitPropertyDefense () & unitPropertyDefenseIgnoreWallKnockback)
    ignoreWallKnockback = true;

  if (posX < itofix (0))
    {
      if (!ignoreWallKnockback && (arenaW == arenaWallStop || arenaW == arenaWallBounce || (arenaW == arenaWallPush && outOfBoundFrames >= 30)))
	{ posX = 0; stopKB = true; }
      outOfBoundsHoriz = -1;
    }
  else if (posX > arenaX)
    {
     if (!ignoreWallKnockback && (arenaW == arenaWallStop || arenaW == arenaWallBounce || (arenaW == arenaWallPush && outOfBoundFrames >= 30)))
	{ posX = arenaX; stopKB = true; }
      outOfBoundsHoriz = 1;
    }

  if (posY < 0)
    {
      if (!ignoreWallKnockback && (arenaW == arenaWallStop || arenaW == arenaWallBounce || (arenaW == arenaWallPush && outOfBoundFrames >= 30)))
	{ posY = 0; stopKB = true; }
      outOfBoundsVert = -1;
    }
  else if (posY > arenaY)
    {
      if (!ignoreWallKnockback && (arenaW == arenaWallStop || arenaW == arenaWallBounce || (arenaW == arenaWallPush && outOfBoundFrames >= 30)))
	{ posY = arenaY; stopKB = true; }
      outOfBoundsVert = 1;
    }

  //check unit properties against the wall here.
  if (outOfBoundsVert == 0 && outOfBoundsHoriz == 0)
    outOfBoundFrames = 0;

  if (!ignoreWallKnockback && (outOfBoundsVert != 0 || outOfBoundsHoriz != 0))
    {
      outOfBoundFrames++;
      wallPushMag = ftofix (1.0);
      if (arenaW == arenaWallPush)
	{
	  for (int i = 0; i < outOfBoundFrames; i ++)
	    wallPushMag = wallPushMag * 3 / 2;
      
	  if (outOfBoundsHoriz < 0)
	    addKnockBack (itofix (64), wallPushMag, 1, false);

	  if (outOfBoundsHoriz > 0)
	    addKnockBack (itofix (192), wallPushMag, 1, false);

	  if (outOfBoundsVert < 0)
	    addKnockBack (itofix (128), wallPushMag, 1, false);

	  if (outOfBoundsVert > 0)
	    addKnockBack (itofix (0), wallPushMag, 1, false);
	}
    }

  if (outOfBoundsVert != 0 || outOfBoundsHoriz != 0)
    {
      //take damage for going out of bounds. 10 HP/frame.
      hitPoints -= 10;
      reduceScore (10);

      if (propertyHitbox & unitPropertyHitboxDiesWhenHitsWall)
	killSelf ();
    }
      
  //add knockback forces
  vector <kbForce> ::iterator iter;
  iter = kbForces -> begin ();
  fixed kbcomponent;
  int heavyOrBouncyMult = 2;

  if (getUnitPropertyDefense () & unitPropertyDefenseHeavy)
    heavyOrBouncyMult = 4;

  if (getUnitPropertyDefense () & unitPropertyDefenseBouncy)
    heavyOrBouncyMult = 1;

  int kbMult3;

  while(iter < kbForces -> end()) {
    //bounce/smack against the walls if you must...get math and change the knockback angles themselves (reduce mag, too?)

    //stop: cancel the horizontal or vertical component, depending on which wall he slid into.

    if (stopKB)
      { (*iter).mag = ftofix (0.0); (*iter).duration = 0;}

    if (arenaW == arenaWallStop)
      {
	if (outOfBoundsHoriz != 0 && outOfBoundsHoriz != 0)
	  { (*iter).mag = ftofix (0.0); (*iter).duration = 0;}
	else if (outOfBoundsHoriz != 0)
	  { 
	    kbcomponent = fixmul ((*iter).mag, fixcos ((*iter).dir));
	    if (kbcomponent >= 0) { (*iter).dir = itofix (0);}
	    else { (*iter).dir = itofix (128);}
	  }
	else if (outOfBoundsVert != 0)
	  { 
	    kbcomponent = fixmul ((*iter).mag, fixsin ((*iter).dir));
	    if (kbcomponent >= 0) { (*iter).dir = itofix (64);}
	    else { (*iter).dir = itofix (192);}
	  }
      }

    //Bounce: 
    if (arenaW == arenaWallBounce && (outOfBoundsHoriz != 0 || outOfBoundsHoriz != 0))
      { (*iter).dir -= itofix (128); (*iter).mag = (*iter).mag * 3 / 4; }

    kbMult3 = heavyOrBouncyMult;

    if (!(*iter).affectedByArena)
      kbMult3 = 2;

    thisFrameMovementX += (2 / kbMult3) * fixmul ((*iter).mag, fixsin ((*iter).dir));
    thisFrameMovementY += (-2 / kbMult3) * fixmul ((*iter).mag, fixcos ((*iter).dir));

    iter ++;
  }

  posX += thisFrameMovementX;
  posY += thisFrameMovementY;
}

void Unit :: endOfRound ()
{
  //decide the animation to be used
  //here's the order: Dead Stun Taunt Attack1 Attack2 Attack4 Run Idle
  //still need to implement: Taunt

  if (youAreDead ())
    { killSelf (); }
  else if (badStatus -> at (badStatusStun) > 0)
    { charAnim = charAnimStun; }
  else if (animAttackNum > 0)
    {
      if (animAttackNum == 1)
	charAnim = charAnimAttack1;
      else if (animAttackNum == 2)
	charAnim = charAnimAttack2;
      else
	charAnim = charAnimAttack4;
    }
  else if (walking)
    { charAnim = charAnimRun; }
  else
    { charAnim = charAnimIdle; }

  //reset movement and knockback
  walking = false;
  kbVelX = itofix (0);
  kbVelY = itofix (0);
  fixed arenakb = rnd -> getArenaKB ();

  vector <kbForce> ::iterator iter;  
  iter = kbForces -> begin ();
  while(iter < kbForces -> end()) {
    //check to see if the duration <= 0; if so remove it
    //otherwise, decrement it
    if ((*iter).duration <= 0)
      {
	//ask the arena what it wants to do with expired knockback...
	//sliding mechanic: they should travel a total of 1 / (1-arenaKB) distance, assuming no cutoff.
	(*iter).mag = fixmul ((*iter).mag, arenakb);
	if ((*iter).mag < ftofix (1.0) || !(*iter).affectedByArena)
	  kbForces -> erase (iter);
      }
    else
      (*iter).duration--;

    iter ++;
  }
}

//mod this so you can collect all the units by index.
vector <AddUnitTemplate *> * Unit :: getAddedUnits (int index)
{
  //returns all of the AddedUnitTemplates with the attack number of index. You can | index to get multiple indecies.
  //if index == -1, returns all of the AddedUnitTemplates.

  vector <AddUnitTemplate *> * auts = new vector <AddUnitTemplate *> (0);

  for (int i = 0; i < int(addedUnits -> size ()); i ++)
    {
      if (index == -1 || (addedUnits -> at (i) -> attackNum & index))
	auts -> push_back (addedUnits -> at (i));
    }

  return auts;
}

void Unit :: setUnitList (UnitList * uuuul)
{
  units = uuuul;
}

void Unit :: setUnitTemplate (UnitTemplate * uuul)
{
  ut = uuul;
}

void Unit :: giveJuiceToPlayer (int & totalJuice)
{
  //gives juice to the player
  totalJuice += juiceReward;
  juiceReward = 0;
}

void Unit :: storeJuiceForPlayer (fixed mult)
{
  //tallies the amount of juice (hitpoints) for the player.
  juiceReward += fixtoi(mult * juiceForPlayer);
}

void Unit :: addUnit (vector <AddUnitTemplate *> * auts, bool chainCooldown, int suT)
{
  //adds the unit to the unitList via unitCreate.
  //this does not check the game rules to see if the unit can be legally placed. use canAddUnit () to determine this.

  fixed px = posX;
  fixed py = posY;
  int doAttack = 0;
  fixed fireAngle = drawAngle;
  UnitCreate * uuuC = NULL;
  int numShots = 0;
  fixed spaceDist = 0;
  fixed randomSweepMult = 0;
  int su = subUnitType;
  fixed offsetDist;

  AddUnitTemplate * aut = NULL;

  for (int i = 0; i < int(auts -> size ()); i ++)
    {
      aut = auts -> at (i);

      doAttack = aut -> attackNum;
      numShots = aut -> numToMake;

      //offset

      if (numShots != 0)
	spaceDist = fixdiv (aut -> angleSpread, itofix (numShots));

      for (int index = 0; index < numShots; index ++)
	{	       
	  fireAngle = aut -> offsetAngle + drawAngle;

	  //starting from the user, randomly burst outward.
	  randomSweepMult = ftofix (float(rand () - (INT_MAX/2)) / float(INT_MAX));
	  fireAngle += fixmul (randomSweepMult, aut -> randomSpread);
	  fireAngle += index * spaceDist;

	  if (suT != subUnitNone)
	    su = suT;
	  else
	    su = subUnitType;

	  offsetDist = aut -> offsetDist;

	  if (su == subUnitBig)
	    offsetDist = fixmul (offsetDist, ftofix (1.414));
	  else if (su == subUnitBiggest)
	    offsetDist = fixmul (offsetDist, ftofix (2.0));
	  else if (su == subUnitTiny)
	    offsetDist = fixmul (offsetDist, ftofix (0.5));
	  else if (su == subUnitElite)
	    offsetDist = fixmul (offsetDist, ftofix (0.7));

	  px = posX + fixmul (offsetDist, fixsin (fireAngle));
	  py = posY - fixmul (offsetDist, fixcos (fireAngle));
      
	  uuuC = new UnitCreate (*(aut -> unitType), 1, 10, unitCreateRulesAtTime, aut -> attackDelay, 0, px, py, fireAngle, su, this);
	  uuuC -> own = this;
	  units -> addUnitCreate (uuuC);
	}

      cantAttack1 += aut -> cantAttackTime1;
      cantAttack2 += aut -> cantAttackTime2;
      cantAttack4 += aut -> cantAttackTime4;
      cantMove += aut -> cantMoveTime;
  
      hitPoints -= aut -> costOfAttack;

      if (hitPoints < 0)
	hitPoints = 1;

      if (aut -> moveForward > 0)
	{ moveForwardCounter += aut -> moveForward; }
    }

  if (doAttack < 8) //if it's the 3 attacks...
    animAttackNum = doAttack;
  
  if (doAttack == 2)
    soundToPlay = soundToPlayAtt2;
  else if (doAttack == 4)
    soundToPlay = soundToPlayAtt3;
  else
    soundToPlay = soundToPlayAtt1;

  if (chainCooldown)
    {
      cantAttack1 += 10;
      cantAttack2 += 10;
      cantAttack4 += 10;
      cantMove += 10;
    }
}

bool Unit :: canAddUnit (vector <AddUnitTemplate *> * auts)
{
  //checks to see if the unit can be added.
  //checks for cantAttackTimes.

  AddUnitTemplate * aut = auts -> at (0);

  int attNum = aut -> attackNum;
  int attCost = 0;

  for (int i = 0; i < int(auts -> size ()); i ++)
    attCost += auts -> at (i) -> costOfAttack;

  if (attNum == 1 && cantAttack1 > 0) return false;
  if (attNum == 2 && cantAttack2 > 0) return false;
  if (attNum == 4 && cantAttack4 > 0) return false;
  if (attNum != 1 && attCost >= hitPoints)
    return false;
  if (attNum == 1 && !(getUnitPropertyPlayer () & unitPropertyPlayerIgnoreHealthFor1stAttack) && attCost >= hitPoints)
    return false;

  return true;
}

void Unit :: faceAngle (fixed ang)
{
  //sets walking and drawing in that direction.
  if (cantMove <= 0)
    {
      walkAngle = ang;
      drawAngle = ang;
    }
}

void Unit :: setWalkAngle (fixed ang)
{
  //sets walking in that direction.
  if (cantMove <= 0)
    walkAngle = ang;
}

void Unit :: setFaceAngle (fixed ang)
{
  //sets facing (drawing) in that direction.
  if (cantMove <= 0)
    drawAngle = ang;
}

void Unit :: walk (bool w)
{
  if (cantMove <= 0)
    walking = w;
}

fixed Unit :: getWalkAngle ()
{return walkAngle;}

fixed Unit :: getDrawAngle ()
{return drawAngle;}

bool Unit :: getWalking ()
{return walking;}

fixed Unit :: getPosX ()
{ return posX;}

fixed Unit :: getPosY ()
{ return posY;}

int Unit :: getAffiliation ()
{ return affiliation;}

string * Unit :: getUnitName ()
{ return unitType;}

fixed Unit :: getPicSize ()
{
  //gets the size of the current frame, multiplying it by the drawscale
  return fixmul (getFrameSize (), drawScale);
}

fixed Unit :: getDrawScale ()
{ return drawScale;}

int Unit :: shouldCheckToGetHit (Unit * targetUnit, bool & useOffensiveKB)
{
  //is it worth checking hit detection in the first place?
  //compare unitProperties and affiliations, etc
  //collisions can deal damage, knockback, or both.
  //also, check if you should use an offensive knockback (usually on enemies, not on allies)

  int ans = collideBoth;
  bool applyKnockback = true;
  bool applyDamage = true;

  //A unit can't hit itself
  if (this == targetUnit)
    return collideIgnore;

  if (getAnimCurrent () == charAnimDead || targetUnit -> getAnimCurrent () == charAnimDead)
    return collideIgnore;

  //getProperties
  //int thisPropertyPlayer = getUnitPropertyPlayer ();
  int thisPropertyHitbox = getUnitPropertyHitbox ();
  //int thisPropertyStatus = getUnitPropertyStatus ();
  int thisPropertyDefense = getUnitPropertyDefense ();

  //int targetPropertyPlayer = targetUnit -> getUnitPropertyPlayer ();

  int targetPropertyHitbox = targetUnit -> getUnitPropertyHitbox ();

  int targetPropertyDefense = targetUnit -> getUnitPropertyDefense ();

  //check affiliations against target properties
  int thisAffil = getAffiliation ();
  int targetAffil = targetUnit -> getAffiliation ();

  bool alliedTypes = false;
  //decide if they're allied units. Player and PlayerProjectile are allied, and so are Enemy and EnemyProjectile.
  if ((thisAffil == affiliationPlayer || thisAffil == affiliationPlayerProjectile) && (targetAffil == affiliationPlayer || targetAffil == affiliationPlayerProjectile))
    {
      alliedTypes = true;
    }

  if ((thisAffil == affiliationEnemy || thisAffil == affiliationEnemyProjectile) && (targetAffil == affiliationEnemy || targetAffil == affiliationEnemyProjectile))
    {
      alliedTypes = true;
    }

  //Here are the hit rules, listed by priority (ie you always take wall damage)
  //Wall Damage
  //Invincibility
  //NoKnockback / NoDamage
  //Indestructible (Unless Hits indestructible)
  //Hits _type When Stunned
  //Goes Through _type

  useOffensiveKB = true;

  if ((thisPropertyHitbox & unitPropertyHitboxGoesThroughAlly || targetPropertyHitbox & unitPropertyHitboxGoesThroughAlly) && alliedTypes)
    { applyDamage = false; applyKnockback = false; }

  if ((thisPropertyHitbox & unitPropertyHitboxGoesThroughOpponent || targetPropertyHitbox & unitPropertyHitboxGoesThroughOpponent) && !alliedTypes)
    { applyDamage = false; applyKnockback = false; }

  if (alliedTypes && !(thisPropertyHitbox & unitPropertyHitboxHitsAllyWhenStunned && getBadStatus () -> at (badStatusStun) > 0))
    { applyDamage = false; useOffensiveKB = false; }

  if (!alliedTypes && !(thisPropertyHitbox & unitPropertyHitboxHitsOpponentWhenStunned) && getBadStatus () -> at (badStatusStun) > 0)
    { applyDamage = false; useOffensiveKB = false; }

  if ((targetPropertyDefense & unitPropertyDefenseIndestructible) && !(thisPropertyHitbox & unitPropertyHitboxHitsIndestructible))
    {
      if (thisPropertyDefense & unitPropertyDefenseIndestructible)
	{applyDamage = false; useOffensiveKB = false;}
      else
	{applyKnockback = false; applyDamage = false; useOffensiveKB = false;}
    }

  if ((thisPropertyHitbox & unitPropertyHitboxHitsPlayer) && targetAffil == affiliationPlayer)
    	{applyKnockback = true; applyDamage = true; useOffensiveKB = true;}

  if (targetPropertyDefense & unitPropertyDefenseNoKnockBack)
    {applyKnockback = false; useOffensiveKB = false;}

  if (targetPropertyDefense & unitPropertyDefenseNoDamage)
    {applyDamage = false; useOffensiveKB = false;}

  if (targetUnit -> goodStatus -> at (goodStatusInvicible) > 0)
    { applyDamage = false; applyKnockback = false;}

  if (thisPropertyHitbox & unitPropertyHitboxDealsNoKnockback)
    { useOffensiveKB = false; applyKnockback = false;}

  if (applyDamage && applyKnockback)
    ans = collideBoth;
  else if (applyDamage && !applyKnockback)
    ans = collideDamageOnly;
  else if (!applyDamage && applyKnockback)
    ans = collideKnockbackOnly;
  else
    ans = collideIgnore;

  return ans;
}

void Unit :: getHitKnockback (Unit * targetUnit, bool useOffensiveKB)
{
  //thisUnit hit targetUnit. Deal knockback accordingly.
  //remember: >>>>>>we're affecting targetUnit<<<<<<, so apply damage and knockback to targetUnit. Test targetUnit's properties as well.
  //ok, knockback

  fixed hitkbmultiplier = ftofix (1.0);
  fixed distIntersected;

  //get the angle the collided at
  fixed angleCollide = calcAngle (getPosX (), 
				  getPosY (),
				  targetUnit -> getPosX (),
				  targetUnit -> getPosY (),
				  getWalkAngle ());

  //get the distance they intersected
  fixed magCollide = (getPicSize () + targetUnit -> getPicSize ())/2;

  distIntersected = calcDist (getPosX (), 
			  getPosY (),
			  targetUnit -> getPosX (),
			  targetUnit -> getPosY ());

  if (distIntersected > itofix (1))
    magCollide -= distIntersected;
  else
    {
      //they're right on top of each other! randomly vibrate them away
      magCollide = itofix (2);
      angleCollide = itofix (rand () % 256);
    }

  //halve it (since the other unit will do the same to you)
  magCollide /= 2;

  //apply knockback to the enemy unit. this Unit will push targetUnit forward.
  targetUnit -> addKnockBack (angleCollide, magCollide, 1, false);

  //add this unit's hitKB;

  if (subUnitType == subUnitBig)
    { hitkbmultiplier = ftofix (1.414); }
  else if (subUnitType == subUnitBiggest)
    { hitkbmultiplier = ftofix (2.0); }

  //targetUnit Dies when hits various affiliations
  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenPlayerProjectileHits && getAffiliation () == affiliationPlayerProjectile)
    targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenPlayerHits && getAffiliation () == affiliationPlayer)
    targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenEnemyHits && getAffiliation () == affiliationEnemy)
      targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenEnemyProjectileHits && getAffiliation () == affiliationEnemyProjectile)
    targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenNeutralHits && getAffiliation () == affiliationNeutral)
    targetUnit -> killSelf ();

  if (hitKB != NULL && useOffensiveKB)
    targetUnit -> addKnockBack (hitKB -> dir + angleCollide, fixmul (hitKB -> mag, hitkbmultiplier), fixtoi (hitkbmultiplier * hitKB -> duration));
}

void Unit :: getHitDamage (Unit * targetUnit)
{
  //this Unit hit targetUnit. Deal damage accordingly.
  //remember: >>>>>>>>>>>>we're affecting targetUnit,<<<<<<<<< so apply damage and knockback to targetUnit. Test targetUnit's properties as well.
  int i;
  fixed giveJuiceMult = 0;
  int dd = damageDealt;

  if ((targetUnit -> getAffiliation () == affiliationEnemy || targetUnit -> getAffiliation () == affiliationNeutral || targetUnit -> getAffiliation () == affiliationEnemyProjectile) && (getAffiliation () == affiliationPlayer || getAffiliation () == affiliationPlayerProjectile))
    giveJuiceMult = ftofix(1.0);

  if (getAffiliation () == affiliationPlayer && getGoodStatus () -> at (goodStatusInvicible) > 0)
    giveJuiceMult = 0;

  if ((getAffiliation () == affiliationEnemy || getAffiliation () == affiliationEnemyProjectile) && (targetUnit -> getAffiliation () == affiliationEnemy || targetUnit -> getAffiliation () == affiliationEnemyProjectile) && getBadStatus () -> at (badStatusStun) > 0)
    giveJuiceMult = ftofix(1.0);

  //targetUnit Dies when hits various affiliations
  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenPlayerProjectileHits && getAffiliation () == affiliationPlayerProjectile)
    targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenPlayerHits && getAffiliation () == affiliationPlayer)
    targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenEnemyHits && getAffiliation () == affiliationEnemy)
      targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenEnemyProjectileHits && getAffiliation () == affiliationEnemyProjectile)
    targetUnit -> killSelf ();

  if (targetUnit -> getUnitPropertyHitbox () & unitPropertyHitboxDiesWhenNeutralHits && getAffiliation () == affiliationNeutral)
    targetUnit -> killSelf ();

  //determine rules that generate juice
  if (getGoodStatus () -> at (goodStatusMoreDamage) > 0)
    {dd *= 2; giveJuiceMult = fixmul (giveJuiceMult, ftofix (2.0));}

  if (getBadStatus () -> at (badStatusLessDamage) > 0)
    {dd /= 2; giveJuiceMult = fixmul (giveJuiceMult, ftofix (0.5));}

  storeJuiceForPlayer (giveJuiceMult);
  targetUnit -> addDamage (dd);

  for (i = 0; i < int(goodStatusDamage -> size ()); i ++)
    targetUnit -> addGoodStatus (i, goodStatusDamage -> at (i));

  for (i = 0; i < int(badStatusDamage -> size ()); i ++)
    targetUnit -> addBadStatus (i, badStatusDamage -> at (i));
}

void Unit :: addDamage (int d)
{ damageTakenThisFrame += d;}

void Unit :: addGoodStatus (int index, int dur)
{ 
  int statusDur = dur;

  if (getUnitPropertyStatus () & unitPropertyStatusResistGoodAilments)
    statusDur /= 2;

  if (getUnitPropertyStatus () & unitPropertyStatusImmuneGoodAilments)
    statusDur = 0;

  if (index >= 0 && index < int(goodStatus -> size ()))
    goodStatus -> at (index) += statusDur;

  if (statusDur > 0)
    changeGoodStatusMax (index, goodStatus -> at (index));
}

void Unit :: changeGoodStatusMax (int index, int val)
{} //PlayerUnit has its own virtual function that does the real work.

void Unit :: addBadStatus (int index, int dur)
{ 
  int statusDur = dur;

  if (getUnitPropertyStatus () & unitPropertyStatusResistBadAilments)
    statusDur /= 2;

  if (getUnitPropertyStatus () & unitPropertyStatusImmuneBadAilments)
    statusDur = 0;

  if (index >= 0 && index < int(badStatus -> size ()))
    badStatus -> at (index) += statusDur;

  if (statusDur > 0)
    changeBadStatusMax (index, badStatus -> at (index));
}

void Unit :: clearGoodStatus (int index)
{
  goodStatus -> at (index) = 0;
  changeGoodStatusMax (index, goodStatus -> at (index));
}
 
void Unit :: clearBadStatus (int index)
{
  badStatus -> at (index) = 0;
  changeBadStatusMax (index, badStatus -> at (index));
}

void Unit :: changeBadStatusMax (int index, int val)
{}//PlayerUnit has its own virtual function that does the real work.

void Unit :: addKnockBack (fixed d, fixed m, int dur)
{ kbForces -> push_back (*new kbForce (d, m, dur));}

void Unit :: addKnockBack (fixed d, fixed m, int dur, bool b)
{ kbForces -> push_back (*new kbForce (d, m, dur, b));}

void Unit :: addKnockBack (kbForce * kb)
{ kbForces -> push_back (*kb);}

bool Unit :: youAreDead ()
{
  //checks to see if this unit is dead.

  if (charAnim == charAnimDead)
    return true;

  if (timeToLive >= 0)
    { timeToLive --; }

  if (timeToLive < 0 && timeToLive > -10)
    return true;

  if (hitPoints <= 0)
    return true;

  return false;
}

bool Unit :: getRemoveThisUnit ()
{ return removeThisUnit;}

int Unit :: getCharAnim ()
{ return charAnim;}

int Unit :: getUnitPropertyPlayer ()
{ return unitPropertyPlayer;}

int Unit :: getUnitPropertyHitbox ()
{ return unitPropertyHitbox;}

int Unit :: getUnitPropertyStatus ()
{ return unitPropertyStatus;}

int Unit :: getUnitPropertyDefense ()
{ return unitPropertyDefense;}

void Unit :: setRound (Round * r)
{ rnd = r;}

void Unit :: killSelf ()
{
  //use suicide bullet.
  vector <AddUnitTemplate *> * auts = NULL;
  if (charAnim != charAnimDead)
    {
      auts = getAddedUnits (8);
      if (auts -> size () > 0)
	addUnit (auts, false, subUnitNone);
    }

  //unit kills itself.
  charAnim = charAnimDead;
  soundToPlay = soundToPlayDead;
}

int Unit :: getHP ()
{ return hitPoints; }

int Unit :: getMaxHP ()
{ return maxHP; }

vector <int> * Unit :: getGoodStatus ()
{ return goodStatus;}

vector <int> * Unit :: getBadStatus ()
{ return badStatus;}

void Unit :: changeAnim (int index)
{
  //a new animation. Re-initialize everything.
  animationFinished = false;
  animFrame = 0;
  animFrameMax = ut -> getVecPics () -> at (index) -> size ();
  animCurrent = index;

  //reset number of loops
  ut -> numLoopsCurrent = ut -> numLoops -> at (index);

  if (ut -> holdFrameFor -> at (index) -> size () > 0)
    ut -> holdFrameForCurrent = ut -> holdFrameFor -> at (index) -> at (0);
  else
    ut -> holdFrameForCurrent = 0;
}

BITMAP * Unit :: getPic (bool incrementFrame)
{
  //usually called by a unit when drawing the next frame of animation.
  //it is assumed that vecPics -> at (charAnimIdle) -> at (0) points to a real BITMAP object.
  //The animation will loop itself if the index is charAnim Idle, Run, or Taunt.
  //The animation will stay at the final frame if the index is charAnim Attack1, Attack2, Attack4, Dead, Stun, or Taunt.

  //This function will load the next frame of animation into pic and loop if necessary.
  //if the animation has not been loaded, it will load vecPics -> at (charAnimIdle) -> at (0) into pic.

  bool loopAnim = false;
  bool changeHoldFrame = false;
  BITMAP * pic;

  //check to see if anything has been loaded for this type of animation
  if (animFrameMax <= 0) //nope, send the default
    {
      if (animCurrent == charAnimAttack1 || animCurrent == charAnimAttack2 || animCurrent == charAnimAttack4 || animCurrent == charAnimDead)
	{
	  animAttackNum = 0;
	  if (charAnim == charAnimDead)
	    removeThisUnit = true;
	}

      pic = ut -> getVecPics () -> at (charAnimIdle) -> at (0);
      return pic;
    }
  else
    {
      pic = ut -> getVecPics () -> at (animCurrent) -> at (animFrame); //get the next frame
    } 

  if (incrementFrame)
    {
      if (ut -> holdFrameForCurrent <= 0)
	{
	  animFrame ++; //increment the next frame
	  changeHoldFrame = true;
	}
      else
	ut -> holdFrameForCurrent --;
      
      //decide if you have to loop the animation
      if (animFrame >= animFrameMax)
	loopAnim = true;
      
      if (ut -> loopAfterFrame -> at (animCurrent) >= 0 && animFrame > ut -> loopAfterFrame -> at (animCurrent))
	loopAnim = true;
      
      if (loopAnim)
	{
	  //looping: decrement numLoops
	  ut -> numLoopsCurrent -= 1;
	  animFrame = ut -> loopToFrame -> at (animCurrent);
	  
	  if (animFrame <= -1 || ut -> numLoopsCurrent < 0)
	    {animFrame = animFrameMax - 1;}
	  
	  changeHoldFrame = true;

	  if (ut -> numLoopsCurrent < 0 && (animCurrent == charAnimAttack1 || animCurrent == charAnimAttack2 || animCurrent == charAnimAttack4 || animCurrent == charAnimDead))
	    {
	      animAttackNum = 0;
	      if (charAnim == charAnimDead)
		removeThisUnit = true;
	    }
	}
      
      if (changeHoldFrame)
	ut -> holdFrameForCurrent = ut -> holdFrameFor -> at (animCurrent) -> at (animFrame);
    }

  return pic;
}

bool Unit :: getAnimFinished ()
{
  return animationFinished;
}

int Unit :: getAnimCurrent ()
{
  return animCurrent;
}

fixed Unit :: getFrameSize ()
{
  //gets the current frame's size.
  //assumes it's square.

  BITMAP * frameTester = NULL;

  if (animFrameMax <= 0) //nope, send the default
    {
      frameTester = ut -> getVecPics () -> at (charAnimIdle) -> at (0);
    }
  else
    frameTester = ut -> getVecPics () -> at (animCurrent) -> at (animFrame);

  return fixmul (ftofix (0.8), fixsqrt (itofix (frameTester -> w * frameTester -> w + frameTester -> h * frameTester -> h)));
}

void Unit :: reduceScore (int scoreRedux)
{} //virtual function

void Unit :: alignStyleMove ()
{
  //checks the alignStyles to keep the unit in range.
  fixed distToOwner;
  fixed angleToOwner;
  //fixed angleDMin;
  //fixed angleDMax;
  bool needToCorrect = false;

  //don't try if the style is default, or there's no owning unit.
  if (alignStyle == alignStyleNormal || ownUnit == NULL)
    return;

  if (!units -> thisUnitExists (ownUnit))
    {ownUnit = NULL; return;}

  //ok, the owning unit exists.
  angleToOwner = calcAngle (ownUnit -> getPosX (),
			    ownUnit -> getPosY (),
			    getPosX (), 
			    getPosY (),
			    ownUnit -> getDrawAngle ());

  distToOwner = calcDist (ownUnit -> getPosX (),
			  ownUnit -> getPosY (),
			  getPosX (), 
			  getPosY ());

  if (alignStyle & alignStyleDist && distToOwner > alignDist)
    {
      distToOwner = alignDist;
      needToCorrect = true;
    }

  if (needToCorrect)
    {
      posX = ownUnit -> getPosX () + fixmul (distToOwner, fixsin (angleToOwner));
      posY = ownUnit -> getPosY () - fixmul (distToOwner, fixcos (angleToOwner));
    }
  return;
}

Unit :: ~Unit ()
{
}
