//=============================================================================
// Blaze Gun version 1.1
//
// 11/27/96 by Jonathan E. Wright <nelno@interpath.com>
//
// all original code, not based on anything except the original Quake C
// code from id software.
//=============================================================================

// How it works:
// The flame is simply your basic flame model turned 90 degress on it's side
// when it is shot.  When it impacts something that has health > 0 and DAMAGE_AIM
// it sets it's location to that entity's origin, and it's next think becomes
// blaze_stick.  blaze_stick is responsible for following the burning entity,
// checking for water, determining if there is another entity nearby to burn
// and burning the entity every so often.  The time counter for each burn is kept
// in the flame's attack_finished field, so the entity does not have to be burnt
// each time the flame follows it.  When the entity dies, or the flame is submersed
// in water the flame is removed and no more damage is done from that flame.

// if you have solid bodies that are respawnable (ie. in the BodyQue) you
// need to call blaze_switchentity when the bodies are copied or else a
// respawned entity can come back with flames on it.

/*
================
macros
================
*/
$frame nailatt1 nailatt2

/*
================
function prototypes
================
*/

void () player_run;
void () BecomeExplosion;
void () SuperDamageSound;

/*
================
global vars
================
*/

// keeps track of the number of flames so that a maximum of flames
// will not be exceeded, causing Quake to bog down

float   current_flames;

/*
============
functions
============
*/

void () BlazePrecache =
{
  precache_model2 ("progs/flame2.mdl");
  precache_sound2 ("blhit1.wav");
  precache_sound2 ("blshot1.wav");
  precache_sound2 ("blhiss1.wav");
};

/*
===============
blaze_extinguishentity

called when a respawnable entity is removed without being killed (ie.
when Cujo is deactivated).  Prevents the respawned entity from burning.
===============
*/

void (entity old_ent) blaze_extinguishentity =
{
  local   entity  head, temp;

  head = nextent (world);

  while (head)
  {
    head = find (head, classname, "fire");

    if ((head != world) && (head.enemy == old_ent))
    {
      temp = nextent (head);
      remove (head);
      head = temp;
    }
    else if (head != world) head = nextent (head);
  }
};


/*
===============
blaze_switchentity

called when an entities corpse is copied to the body que, prevents the
respawning entity from being still engulfed in flames while the body
can still burn
===============
*/

void (entity old_ent, entity new_ent) blaze_switchentity =
{
  local   entity  head;

  head = nextent (world);

  while (head)
  {
    head = find (head, classname, "fire");

    if ((head != world) && (head.enemy == old_ent))
    {
      head.enemy = new_ent;
      setorigin (head, new_ent.origin);
    }
    else if (head != world) head = nextent (head);
  }
};

/*
===============
blaze_spread

called by blaze_stick.

creates a new flame entity which will stick to and burn entity "fuel"
===============
*/

void () blaze_stick;

void (entity fuel) blaze_spread =
{
  local   entity   new_flame;

  // don't catch the same entity on fire again!
  // don't catch anything that can't take damage
  if ((fuel.takedamage != DAMAGE_AIM) || (current_flames >= 30))
  {
     return;
  }
  else if ((fuel.classname == "player") && (random () > 0.75))
  {
    return;
  }
  else if (random () > 0.5)
  {
    return;
  }

  current_flames = current_flames + 1;

  if (fuel.classname == "player")
  {
    bprint (fuel.netname);
    bprint (" is on fire!!\n");
  }
//  else if (fuel.classname == "cujo")
  //  sprint (fuel.owner, "Your dog is aflame!!\n");

  new_flame = spawn ();

  // set the sucker on fire!
  new_flame.enemy = fuel;
  new_flame.solid = SOLID_NOT;
  new_flame.angles_x = 0;

  new_flame.movetype = MOVETYPE_NONE;
  new_flame.effects = EF_DIMLIGHT;
  new_flame.takedamage = DAMAGE_NO;
  new_flame.classname = "blaze";
  new_flame.targetname = "";

  new_flame.think = blaze_stick;
  new_flame.nextthink = time;
  new_flame.frame = 1; //added by bondo

  // always points back to the player who fired the original flame
  // so monsters get mad at player, not at flames!
  new_flame.owner = self.owner;
  setmodel (new_flame, "progs/flame2.mdl");
  setsize (new_flame, '2 2 2', '2 2 2');
  setorigin (new_flame, fuel.origin);
};

/*
===============
blaze_findfuel

called by blaze_stick

searches for the closest entity within a certain radius.  If a qualifying entity is
found, and a random chance met, the entity is caught on fire by calling blaze_spread.

Notice there is a greater chance for players to be caught on fire, since they tend to
move around quite a bit more than monsters, and are less likely to remain near a flame
===============
*/

void (float dist) blaze_findfuel =
{
  local   entity   head;
  local   entity   fuel;
  local   float    lastd;

  // anything dist units away can catch flame
  head = findradius (self.origin, dist);
  fuel = world;

  while (head)
  {
    if ((head != self.enemy) && (head.takedamage == DAMAGE_AIM))
    {
      // find the distance to the current entity, so we can choose the closest
      // entity within the radius
      lastd = vlen (self.origin - head.origin);
      if (lastd < dist)
      {
        fuel = head;
        dist = lastd;
      }
    }
    head = head.chain;
  }

  if ((fuel != world) && (random () < 0.1))
  {
    blaze_spread (fuel);
  }
};

/*
===============
blaze_stick

called every frame as the flame's think function.  responsible for control
the flame once it has attached to an entity -- not in flight from the blaze gun!

checks for water submersion each frame
===============
*/

void () burn_entity;

void () blaze_stick =
{
  local   vector   org;
  local   vector   fuelsize, xv, yv, zv;
  local   float    pc;
  local   float    average_width;

  pc = pointcontents (self.origin);
  if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME))
  {
    // water extinguished the flame
    sound (self, CHAN_WEAPON, "blhiss1.wav", 1, ATTN_NORM);
    // reset inflictor so enities don't keep going for water
    self.enemy.dmg_inflictor = self.enemy;
    current_flames = current_flames - 1;
    remove (self);
    return;
  }

  // a dead player -- put out the flames before they respawn!
  // changed to flame search in CopyBodyToQue

  //if ((self.enemy.deadflag == DEAD_RESPAWNABLE))
  if (self.enemy.health < 1)
  {
    current_flames = current_flames - 1;
    remove (self);
    return;
  }


  fuelsize = self.enemy.size;

  // it's really the average of x_width and y_width * 2;
  average_width = (fuelsize_x + fuelsize_y) * 0.5;
  blaze_findfuel (average_width);

  // make the flames dance, baby! dance!

  if (self.enemy.classname == "monster_shambler")
    pc = 0.4;
  else
    pc = 0.1;

  fuelsize_x = fuelsize_x * pc * random ();
  if (random () < 0.5)
    fuelsize_x = fuelsize_x * -1;
  fuelsize_y = fuelsize_y * pc * random ();
  if (random () < 0.5)
    fuelsize_y = fuelsize_y * -1;
  fuelsize_z = fuelsize_z * pc * random ();
  if ((random () < 0.5) && (self.enemy.classname != "monster_shambler"))
    fuelsize_z = fuelsize_z * -1;

  org = self.enemy.origin + fuelsize;
  setorigin (self, org);

  if (self.velocity == '0 0 0') self.avelocity = '0 0 0';

//added by bondo to limit burning
	self.bondotime = self.bondotime + 1;
	if (self.bondotime < 60)
	  self.think = blaze_stick;
	else
		self.think = SUB_Remove;
		
  self.nextthink = time + 0.1; //changed from self.nextthink = time; by bondo

  // targetname is used as a flag here to determine if the entity
  // has already been determined to be dead and the attack_finished time
  // already increased -- without the flag, the flames won't burn out
  // because the attack_finished time will always be increasing

  if ((self.enemy.health <= 0) && (self.targetname == "") &&
       (self.enemy.classname != "monster_zombie"))
//       (self.enemy.classname != "player"))
  {
    self.attack_finished = 7 * random () + time;
    self.targetname = "already_dead";
//    bprint (self.enemy.classname);
//    bprint (" is dead and burning\n");
  }

  if (self.attack_finished <= time) burn_entity ();
};

/*
===============
burn_entity -- called by the flame

called each time attack_finished <= time, currently every second
responsible for damaging the entity the flame is attached to

zombies are a special case and recieve quite a bit more damage than
a normal monster or player so that a few flames and sometimes a single
flame, can gib them
===============
*/

void () burn_entity =
{
  if ((self.enemy.health <= 0) || (self.enemy.deadflag == DEAD_RESPAWNABLE))
  {
    current_flames = current_flames - 1;
    remove (self);
    return;
  }

  if (self.enemy.classname != "monster_zombie")
    T_Damage (self.enemy, self, self.owner, 20 * random () + 10);
  else
    T_Damage (self.enemy, self, self.owner, 50 * random () + 10);

  if (self.enemy.classname == "player") {
    centerprint (self.enemy, "You're on fire!\nFind some water!");
	if (random () > 0.500)
		sound (self.enemy,CHAN_VOICE,"player/lburn1.wav",TRUE,ATTN_NORM);
	else
		sound (self.enemy,CHAN_VOICE,"player/lburn2.wav",TRUE,ATTN_NORM);
    }

//  self.think = blaze_stick;
//  self.nextthink = time;
  self.attack_finished = time + 1;
};

/*
===============
blaze_watercheck

called as the flame's thin function from the time it is fired until it hits
and is removed or catches and entity on fire
===============
*/

void() blaze_watercheck =
{
  local float  pc;

  pc = pointcontents (self.origin);
  if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME) || (self.attack_finished <= time))
  {
    sound (self, CHAN_WEAPON, "blhiss1.wav", 1, ATTN_NORM);
    // reset inflictor so enities don't keep going for water

    // make the little grey particles
    WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
    WriteByte (MSG_BROADCAST, TE_GUNSHOT);
    WriteCoord (MSG_BROADCAST, self.origin_x);
    WriteCoord (MSG_BROADCAST, self.origin_y);
    WriteCoord (MSG_BROADCAST, self.origin_z);

    remove (self);
    return;
  }

  self.think = blaze_watercheck;
  self.nextthink = time;
};

/*
===============
blaze_touch

the touch function of an in-flight flame.  responsible for setting an
entity aflame when it hits it (if the random chance is met)
===============
*/

void() blaze_touch =
{
  local vector org;
 local float  pc;

  pc = pointcontents (self.origin);
  if (pc == CONTENT_SKY)
  {
    remove (self);
    return;
  }
  else if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME))
  {
    remove (self);
    return;
  }

  sound (self, CHAN_WEAPON, "blhit1.wav", 1, ATTN_NORM);

	if (self.owner.nailguntype == 3)
  		setmodel (self, "progs/flame2.mdl");


  org = self.origin - 8*normalize(self.velocity);

  if ((other.health) && (other.takedamage == DAMAGE_AIM))
    T_Damage (other, self, self.owner, 15);
  else
  {
    // makes a little debris appear for one frame
    WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
    WriteByte (MSG_BROADCAST, TE_GUNSHOT);
    WriteCoord (MSG_BROADCAST, org_x);
    WriteCoord (MSG_BROADCAST, org_y);
    WriteCoord (MSG_BROADCAST, org_z);
  }

  // does some radius damage, < 1/3 that of a rocket
  T_RadiusDamage (self, self.owner, 35, other);

// debugging purposes
/*
  bprint ("health=");
  bprint (ftos (other.health));
  if (other.takedamage == DAMAGE_AIM)
    bprint (", DAMAGE_AIM\n");
  else
    bprint (", no damage\n");
*/
  if ((other.health) && (other.takedamage == DAMAGE_AIM))
  {
    if ((random () < 0.6) && (current_flames < 30))
    {
      // set the sucker on fire!
      self.enemy = other;
// used for debugging deathmatch respawns
//      self.enemy = self.owner;
      self.solid = SOLID_NOT;
      self.movetype = MOVETYPE_NONE;
      self.origin = self.enemy.origin;
      self.think = blaze_stick;
      self.nextthink = time;
      self.attack_finished = time + 1;
      self.classname = "blaze";
      self.targetname = "";
      // point flame upright
      self.angles_x = 0;
      current_flames = current_flames + 1;

      return;
    }
  }

  BecomeExplosion ();
};


/*
===============
blaze_launch

the main firing routine, determines where the flame shoots from and where it will
go to, determines it's velocity, etc.
===============
*/
float () crandom;
//was void(float ox) blaze_launch
//void(float ox, float oy) blaze_launch =
void() blaze_launch =
{
  local   entity   old;
  local   vector   start, finish, dir;
  local   float    pc;

  start = self.origin + '0 0 16'; // + v_right*ox + v_up * oy; //was start = self.origin + '0 0 16' + v_right*ox;

  makevectors (self.v_angle);

  if (self.ammo_nails < 1)
  {
    self.weapon = W_BestWeapon ();
    W_SetCurrentAmmo ();
    return;
  }

	if (self.weapon == IT_NAILGUN)
		self.currentammo = self.ammo_nails = self.ammo_nails - 1;
		
  makevectors (self.v_angle);		// is this still used

//  finish = start + v_forward*600;
  //dir = finish - start;

        dir = aim (self, 1000);
        dir_x = dir_x + crandom() * 0.16;
        dir_y = dir_y + crandom() * 0.16;
        dir_z = dir_z + crandom() * 0.08;

	// launch the flame
		sound (self, CHAN_WEAPON, "blshot1.wav", 1, ATTN_NORM);

  dir = normalize(dir);

/*
	if (self.weapon == IT_SUPER_NAILGUN)
	{
	  if (random() < 0.33)
  		dir = dir + v_right * 0.1;
  	else if (random() < 0.67)
  		dir = dir + v_right * -0.1;
  	}
*/	

  newmis = spawn();
  newmis.owner = self;
  newmis.movetype = MOVETYPE_FLYMISSILE; //changed from MOVETYPE_TOSS by Bondo
  newmis.solid = SOLID_BBOX;
  newmis.effects = EF_DIMLIGHT;
  newmis.takedamage = DAMAGE_NO;
  newmis.classname = "blaze";
  newmis.frame = 1; //added by bondo

  setmodel (newmis, "progs/flame2.mdl");
  setsize (newmis, '2 2 2', '2 2 2');

  setorigin (newmis, start);

  // add player's velocity to the flames velocity for maximum effect
  newmis.velocity = (self.velocity * 0.5) + (dir * 1000);
  newmis.angles = vectoangles(newmis.velocity); //original code
  // turn the flame on it's side, widest end away from player
  newmis.angles_x = newmis.angles_x + 90;

  // give the player a little kickback
  // self.velocity = self.velocity - (dir * 125);

  // will burn for 8 seconds
  newmis.attack_finished = time + 8;
  // continously check for water
  newmis.nextthink = time + 0.1;
  newmis.think = blaze_watercheck;
  newmis.touch = blaze_touch;

//  launch_flame (start, dir);
  self.punchangle_x = -2;
};

//this is the code I modified
/*
void () blaze_attack1 = [103, blaze_attack2]
{
  self.effects = self.effects | EF_MUZZLEFLASH;

  if (!self.button0)
  {
    player_run ();
    return;
  }

  self.weaponframe = self.weaponframe + 1;
  if (self.weaponframe == 9) self.weaponframe = 1;

  SuperDamageSound();

   if (self.weapon == IT_NAILGUN)
      blaze_launch (4,0); // was blaze_launch(4);
   else
      blaze_launch (0,0); // was blaze_launch(4);

  self.attack_finished = time + 0.3;
};

//============================================================================

void () blaze_attack2 = [104, blaze_attack3]
{
  if (!self.button0)
  {
    player_run ();
    return;
  }

  self.weaponframe = self.weaponframe + 1;
  if (self.weaponframe == 9) self.weaponframe = 1;

  self.attack_finished = time + 0.3;
   if (self.weapon == IT_SUPER_NAILGUN) //added
      blaze_launch (0,0); //added

};

//============================================================================

void () blaze_attack3 = [103, blaze_attack4]
{
  self.effects = self.effects | EF_MUZZLEFLASH;

  if (!self.button0)
  {
    player_run ();
    return;
  }

  self.weaponframe = self.weaponframe + 1;
  if (self.weaponframe == 9) self.weaponframe = 1;

  SuperDamageSound();

   if (self.weapon == IT_NAILGUN)
      blaze_launch (-4,0); // was blaze_launch(4);
   else
      blaze_launch (0,0); //was blaze_launch (-4);

  self.attack_finished = time + 0.3;
};

//============================================================================

void () blaze_attack4 = [104, blaze_attack1]
{
  if (!self.button0)
  {
    player_run ();
    return;
  }

  self.weaponframe = self.weaponframe + 1;
  if (self.weaponframe == 9) self.weaponframe = 1;

  self.attack_finished = time + 0.3;
   if (self.weapon == IT_SUPER_NAILGUN) //added
      blaze_launch (0,0); //added

};
*/

