/*
+-------+
|Weapons|
+-------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Scratch                                      Http://www.admdev.com/scratch |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Handles the main weapons code, impulse commands and other general crap     |
| that has to do with guns and violence.						     |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
*/

float() crandom =
{
	return 2*(random() - 0.5);
};

void() T_BulletTouch =
{
	local vector dir;
	local entity oself;

	if (other == self.owner || other.solid == SOLID_TRIGGER)
		return;

	if (!other.takedamage)
	{
		if (other.dethtype == 4) // uzi death
		{
			oself = self;
			self = other;
			UziDeathCheck(); // corpdeth.qc
			self = oself;
			spawn_touchblood (9);
		}
		else
		{
			WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
			WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
			WriteCoord (MSG_BROADCAST, self.origin_x);
			WriteCoord (MSG_BROADCAST, self.origin_y);
			WriteCoord (MSG_BROADCAST, self.origin_z);
		}
	}
	else
	{
		if (random() < 0.4 && other.armorvalue < 1) //2 in 5 times
		{
			self.angles_x = self.angles_x * -1; //account for error in makevectors with non-player entities
			makevectors(self.angles);
			ThrowBloodSplat(v_forward, self.origin, other);
		}
		if (other.armorvalue > 0)
			spawn_touchshield(9);
		else
			spawn_touchblood (9);

		if (self.bullettype == 0) T_Damage (other, self, self.owner, 9); // Pistol
		if (self.bullettype == 1) T_Damage (other, self, self.owner, 12); // Shotgun
		if (self.bullettype == 2) T_Damage (other, self, self.owner, 8); // Sub Machine Gun
	}
	remove(self);
};

void()	shotty_muz1	=	[0,		shotty_muz2] {self.nextthink = time + 0.05;};
void()	shotty_muz2	=	[1,		shotty_muz3] {self.nextthink = time + 0.05;};
void()	shotty_muz3	=	[2,		shotty_muz4] {self.nextthink = time + 0.05;};
void()	shotty_muz4	=	[3,		shotty_muz5] {self.nextthink = time + 0.05;};
void()	shotty_muz5	=	[4,		SUB_Remove] {self.nextthink = time + 0.05;};

void(float u, float r, float f) GunFlare =
{
	local entity flare;

	makevectors (self.v_angle);
	flare = spawn ();
	flare.owner = self;
	flare.movetype = MOVETYPE_NOCLIP;
	flare.solid = SOLID_NOT;
	flare.classname = "flare";
	setmodel (flare, "progs/shotmuz.spr");
	setsize (flare, '0 0 0', '0 0 0');             
	setorigin (flare, self.origin + v_forward*f + v_right*r + v_up*u);
	flare.think = shotty_muz1;
	flare.nextthink = time + 0.01;
};

void(float u, float r, float f) MuzzleFlash =
{
	local entity flash;

	makevectors (self.v_angle);
	flash = spawn ();
	flash.owner = self;
	flash.movetype = MOVETYPE_NOCLIP;
	flash.solid = SOLID_NOT;
	flash.classname = "flash";
	setmodel (flash, "progs/muzflash.spr");
	setsize (flash, '0 0 0', '0 0 0');
	setorigin (flash, self.origin + v_forward*f + v_right*r + v_up*u);
	flash.think = SUB_Remove;
	flash.nextthink = time + 0.04;
};

//*====*
//Knife
//*====*

void() W_FireKnife =
{
	local	vector	source;
	local	vector	org;
	local vector dir;

	makevectors (self.v_angle);
	source = self.origin + '0 0 16';
	traceline (source, source + v_forward*94, FALSE, self);
	if (trace_fraction == 1.0) return;
	
	org = trace_endpos - v_forward*4;

	if (trace_ent.takedamage)
	{
	    if(random() < 0.4 && trace_ent.armorvalue < 1) //only 2 in 5 times
	        ThrowBloodSplat(dir, trace_endpos, trace_ent);

		if (trace_ent.armorvalue > 0)
			SpawnShieldParticle  (org, '0 0 0', 20);
		else
			SpawnBlood (org, '0 0 0', 20);
		T_Damage (trace_ent, self, self, 20 + random()*18);
	}
};

//*====*
//Shotgun
//*====*

void() W_Shotgun =
{
	local entity newmis;

	makevectors (self.v_angle);
	newmis = spawn ();
	newmis.owner = self;
	self.punchangle_x = -4;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;
	newmis.velocity = aim(self, 3500);
	newmis.velocity = newmis.velocity * 3500;
	newmis.velocity = ((newmis.velocity + ((v_right * 600) * crandom ())) + ((v_up * 220) * crandom ())); // velo before ang
	newmis.angles = vectoangles(newmis.velocity);
	newmis.touch = T_BulletTouch;
	newmis.classname = "projectile";

	if (cvar("host_framerate") == 0.01)
		setmodel (newmis, "progs/sbullets.mdl"); // slowmo bullets
	else
		setmodel (newmis, "progs/bullet.mdl");

	setsize (newmis, '0 0 0', '0 0 0');             
	setorigin (newmis, self.origin + v_forward*12 + '0 0 16');
	newmis.bullettype = 1;
};

//*====*
//Pistol
//*====*

void() W_Pistol =
{
	local entity newmis;

	makevectors (self.v_angle);
	newmis = spawn ();
	newmis.owner = self;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;
	newmis.velocity = aim(self, 2000);
	newmis.velocity = newmis.velocity * 2000;
	newmis.angles = vectoangles(newmis.velocity);
	newmis.touch = T_BulletTouch;
	newmis.classname = "projectile";

	if (cvar("host_framerate") == 0.01)
		setmodel (newmis, "progs/sbullets.mdl"); // slowmo bullets
	else
		setmodel (newmis, "progs/bullet.mdl");

	setsize (newmis, '0 0 0', '0 0 0');             
	setorigin (newmis, self.origin + v_forward*8 + v_right*6 + '0 0 16');
	newmis.bullettype = 0;
};

void() W_Pistol2 =
{
	local entity newmis;

	makevectors (self.v_angle);
	newmis = spawn ();
	newmis.owner = self;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;
	newmis.velocity = aim(self, 2000);
	newmis.velocity = newmis.velocity * 2000;
	newmis.angles = vectoangles(newmis.velocity);
	newmis.touch = T_BulletTouch;
	newmis.classname = "projectile";

	if (cvar("host_framerate") == 0.01)
		setmodel (newmis, "progs/sbullets.mdl"); // slowmo bullets
	else
		setmodel (newmis, "progs/bullet.mdl");

	setsize (newmis, '0 0 0', '0 0 0');             
	setorigin (newmis, self.origin + v_forward*8 - v_right*6 + '0 0 16');
	newmis.bullettype = 0;
};

//*====*
//Sub Machine Gun
//*====*

void() W_MachineGun =
{
	local entity newmis;

	makevectors (self.v_angle);
	newmis = spawn ();
	newmis.owner = self;
	newmis.movetype = MOVETYPE_FLYMISSILE;
	newmis.solid = SOLID_BBOX;
	newmis.velocity = aim(self, 2750);
	newmis.velocity = newmis.velocity * 2750;
	newmis.velocity = ((newmis.velocity + ((v_right * 98) * crandom ()) * self.subgunspread) + ((v_up * 98) * crandom ()) * self.subgunspread);
	newmis.angles = vectoangles(newmis.velocity);
	newmis.touch = T_BulletTouch;
	newmis.classname = "projectile";

	if (cvar("host_framerate") == 0.01)
		setmodel (newmis, "progs/sbullets.mdl"); // slowmo bullets
	else
		setmodel (newmis, "progs/bullet.mdl");

	setsize (newmis, '0 0 0', '0 0 0');             
	setorigin (newmis, self.origin + v_forward*12 + '0 0 16');
	newmis.bullettype = 2;
};

//*====*
//Grenade
//*====*

void(entity center) shake =
{
	local entity head, selected;

	selected = world;
	head = findradius (center.origin, 500);
	while (head)
	{
		if (head.health)
		{
			selected = head;
			if ((selected != world))
				selected.punchangle_x = (crandom () * 5);
		}
		head = head.chain;
	}
};

void() ThrowChunk_Think =
{
	if (self.attack_finished < time || self.flags & FL_ONGROUND)
		remove(self);
	self.nextthink = time + 0.1;
};

void() ThrowChunk =
{
	local	entity new;

	new = spawn();

	makevectors(self.angles);
	setorigin (new, self.origin);

	if (random() < 0.35)
		setmodel (new, "progs/wchunk1.mdl");
	else if (random() < 0.65)
		setmodel (new, "progs/wchunk2.mdl");
	else
		setmodel (new, "progs/wchunk3.mdl");

	setsize (new, '0 0 0', '0 0 0');

	new.velocity_x = random()*200;
	new.velocity_y = random()*200;
	new.velocity_z = random()*500+250;

	new.movetype = MOVETYPE_TOSS;
	new.solid = SOLID_NOT;
	new.avelocity_x = random()*600;
	new.avelocity_y = random()*600;
	new.avelocity_z = random()*600;
	new.attack_finished = time + 6 + random()*10; // time till removal
	new.think = ThrowChunk_Think;
	new.nextthink = time + 0.5;
};

void()	s_gexplode1	=	[0,		s_gexplode2] {self.nextthink = time + 0.05;shake(self);};
void()	s_gexplode2	=	[1,		s_gexplode3] {self.nextthink = time + 0.05;};
void()	s_gexplode3	=	[2,		s_gexplode4] {self.nextthink = time + 0.05;};
void()	s_gexplode4	=	[3,		s_gexplode5] {self.nextthink = time + 0.05;shake(self);};
void()	s_gexplode5	=	[4,		s_gexplode6] {self.nextthink = time + 0.05;};
void()	s_gexplode6	=	[5,		s_gexplode7] {self.nextthink = time + 0.05;};
void()	s_gexplode7	=	[6,		s_gexplode8] {self.nextthink = time + 0.05;shake(self);};
void()	s_gexplode8	=	[7,		s_gexplode9] {self.nextthink = time + 0.05;};
void()	s_gexplode9	=	[8,		s_gexplode10] {self.nextthink = time + 0.05;};
void()	s_gexplode10	=	[9,		s_gexplode11] {self.nextthink = time + 0.05;shake(self);};
void()	s_gexplode11	=	[10,		s_gexplode12] {self.nextthink = time + 0.05;};
void()	s_gexplode12	=	[11,		s_gexplode13] {self.nextthink = time + 0.05;};
void()	s_gexplode13	=	[12,		s_gexplode14] {self.nextthink = time + 0.05;shake(self);};
void()	s_gexplode14	=	[13,		s_gexplode15] {self.nextthink = time + 0.05;};
void()	s_gexplode15	=	[14,		s_gexplode16] {self.nextthink = time + 0.05;};
void()	s_gexplode16	=	[15,		SUB_Remove] {self.nextthink = time + 0.05;shake(self);};

void() GrenadeExplosion =
{
	ThrowChunk();
	ThrowChunk();
	ThrowChunk();
	ThrowChunk();
	setorigin (self, self.origin + v_up*18);
	self.movetype = MOVETYPE_NOCLIP;
	self.velocity = '0 0 0';
	self.velocity_z = 100;
	self.touch = SUB_Null;
	setmodel (self, "progs/explode1.spr");
	self.solid = SOLID_NOT;
	s_gexplode1 ();
};


void() GrenadeExplode =
{
	GrenadeExplosion();
	T_RadiusDamage (self, self.owner, 195, world);
	WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_EXPLOSION);
	WriteCoord (MSG_BROADCAST, self.origin_x);
	WriteCoord (MSG_BROADCAST, self.origin_y);
	WriteCoord (MSG_BROADCAST, self.origin_z);
};

void() GrenadeThink =
{
	if (self.flags & FL_ONGROUND)
	{
		self.nextthink = time + 0.7;
		self.think = GrenadeExplode;
		return;
	}
	if (self.velocity == '0 0 0')
		self.avelocity = '0 0 0';
	self.nextthink = time + 0.1;
};

void() GrenadeTouch =
{
	if (other.solid == SOLID_BSP)
	{
		sound (self, CHAN_WEAPON, "weapons/grenbnc.wav", 1, ATTN_NORM);
		self.movetype = MOVETYPE_TOSS;
	}
};

void() W_Grenade =
{
	local	entity missile;
	
	makevectors (self.v_angle);
	missile = spawn ();
	missile.owner = self;
	missile.movetype = MOVETYPE_BOUNCE;
	missile.solid = SOLID_BBOX;
	missile.classname = "projectile"; // projectile classname will cause frikbots to avoid
		
	if (self.v_angle_x)
		missile.velocity = v_forward*500 + v_up * 125;
	else
	{
		missile.velocity = aim(self, 10000);
		missile.velocity = missile.velocity * 500;
		missile.velocity_z = 125;
	}

	missile.avelocity = '300 300 300';
	missile.angles = vectoangles(missile.velocity);
	missile.touch = GrenadeTouch;
	missile.think = GrenadeThink;
	missile.nextthink = time + 0.1;

	setmodel (missile, "progs/grenade.mdl");
	setsize (missile, '0 0 0', '0 0 0');		
	setorigin (missile, self.origin);
};

//called from player.qc
void() ThrowCasing =
{
	local entity casing;
	local vector dir;

	makevectors (self.v_angle);

	casing = spawn();
	casing.owner = self;
	casing.movetype = MOVETYPE_BOUNCE;
	casing.solid = SOLID_BBOX;
	casing.angles = vectoangles(casing.velocity);
	casing.velocity = aim(self, 120);

	if (casing.owner.weapon == W_SHOTGUN)
		casing.skin = 0;
	else
		casing.skin = 1;

	casing.velocity = casing.velocity + v_right * -80;
      casing.velocity_z = casing.velocity_z + 120;
	
	casing.touch = SUB_Remove;
	casing.avelocity = '-600 600 -600';

	setmodel (casing, "progs/casing.mdl");
	setsize (casing, '0 0 0', '0 0 0');     
	setorigin (casing, self.origin + v_forward*12 + '0 0 16');
};

void() W_SetCurrentAmmo =
{
	self.items = self.items - ( self.items & (IT_NAILS | IT_SHELLS));
	
	if (self.weapon == W_KNIFE)
	{
		self.currentammo = 0;
	}
	else if (self.weapon == W_PISTOL)
	{
		self.currentammo = self.ammo_nails;
		self.items = self.items | IT_NAILS;
	}
	else if (self.weapon == W_DOUBLEPISTOL)
	{
		self.currentammo = self.ammo_nails;
		self.items = self.items | IT_NAILS;
	}
	else if (self.weapon == W_SHOTGUN)
	{
		self.currentammo = self.ammo_shells;
		self.items = self.items | IT_SHELLS;
	}
	else if (self.weapon == W_SUBMACHINE)
	{
		self.currentammo = self.ammo_nails;
		self.items = self.items | IT_NAILS;
	}
};

float() W_BestWeapon =
{
	local	float	it;
	
	it = self.items;

	if (self.ammo_nails >= 1 && (it & W_SUBMACHINE))
		return W_SUBMACHINE;
	if(self.ammo_shells >= 1 && (it & W_SHOTGUN))
		return W_SHOTGUN;
	if(self.ammo_nails >= 2 && (it & W_DOUBLEPISTOL))
		return W_DOUBLEPISTOL;
	if(self.ammo_nails >= 1 && (it & W_PISTOL))
		return W_PISTOL;
	return W_KNIFE;
};

float() W_CheckNoAmmo =
{
	if (self.currentammo > 0) return TRUE;

	if (self.weapon == W_KNIFE) return TRUE;
	
	self.weapon = W_BestWeapon ();
	self.checkwepstop_finished = time + 0.4;
	player_grabwep1();

	W_SetCurrentAmmo ();
	
	// drop the weapon down
	return FALSE;
};

void() FireWeapons =
{
	local float fspeed, bspeed, sspeed, mspeed, ggrav;

	if (self.attack_finished > time || self.checkwepstop_finished > time || self.deadflag != 0 || self.view_ofs == '0 0 0' || world.model == "maps/start.bsp")
		return;

	// Cheat prevention / gameplay checking
	fspeed = cvar("cl_forwardspeed");
	bspeed = cvar("cl_backspeed");
	sspeed = cvar("cl_sidespeed");
	mspeed = cvar("sv_maxspeed");
	ggrav = cvar("sv_gravity");

	if (bspeed != 50 || sspeed != 100 || fspeed != 100 || ggrav != 400 || mspeed != 200)
		error("players speed or world gravity has been tampered with!\n");
	// Cheat prevention / gameplay checking

	if (!W_CheckNoAmmo()) return;

	makevectors(self.v_angle);

	if (self.weapon == W_KNIFE) // Knife
	{
		if (random() < 0.50)
			player_stab1();
		else
			player_slash1();
		self.attack_finished = time + 0.5;
	}
	else if (self.weapon == W_PISTOL) // Pistol
	{
		if (self.holdfire == FALSE)
		{
			W_Pistol();
			if (self.velocity_x || self.velocity_y) player_pistol1();
			else player_pistols1();
			self.attack_finished = time + 0.2;
			self.currentammo = self.ammo_nails = self.ammo_nails - 1;
			if (self.ishuman) // frikbots ALWAYS use autofire
				self.holdfire = TRUE;
		}
	}
	else if (self.weapon == W_DOUBLEPISTOL) // Double Pistol
	{
		if (self.holdfire == FALSE)
		{
			if (self.akimboshot == 0)
			{
				player_doublepistol1();
				self.akimboshot = 1;
			}
			else
			{
				player_doublepistol3();
				self.akimboshot = 0;
			}
			if (self.ishuman) // frikbots ALWAYS use autofire
				self.holdfire = TRUE;
			self.attack_finished = time + 0.15;
		}
	}
	else if (self.weapon == W_SHOTGUN) // Shotgun
	{
		player_shotgun1();
		self.attack_finished = time + 1.2;
		self.currentammo = self.ammo_shells = self.ammo_shells - 1;
	}
	else if (self.weapon == W_SUBMACHINE) // Sub Machine Gun
	{
		player_machinegun1();
		self.attack_finished = time + 0.1;
		if (self.subgunspread > 4) // cap
			self.subgunspread = 4;
		else
			self.subgunspread = self.subgunspread + 0.3; // spread over time
		self.currentammo = self.ammo_nails = self.ammo_nails - 2;
	}
};

void() ChangeWeapons =
{
	local	float	it, am, fl;

	it = self.items;
	am = 0;
	
	if (self.attack_finished > time || self.checkwepstop_finished > time || self.deadflag != 0 || self.view_ofs == '0 0 0')
		return;

	if (self.impulse == 1)
	{
		fl = W_KNIFE;
	}
	else if (self.impulse == 2)
	{
		fl = W_PISTOL;
		if (self.ammo_nails < 1)
			am = 1;
	}
	else if (self.impulse == 3)
	{
		fl = W_DOUBLEPISTOL;
		if (self.ammo_nails < 2)
			am = 1;
	}
	else if (self.impulse == 4)
	{
		fl = W_SHOTGUN;
		if (self.ammo_shells < 1)
			am = 1;
	}
	else if (self.impulse == 5)
	{
		fl = W_SUBMACHINE;
		if (self.ammo_nails < 1)
			am = 1;
	}
	self.impulse = 0;

	if (!(self.items & fl))
	{	// don't have the weapon or the item
		sprint (self, "You don't have that weapon.\n");
		return;
	}
	if (self.weapon != fl && !am)
	{
		self.checkwepstop_finished = time + 0.4;
		player_grabwep1();
	}
	
	if (am)
	{	// don't have the ammo
		sprint (self, "Ammo depleted.\n");
		return;
	}

//
// set weapon, set ammo
//
	self.weapon = fl;	
	W_SetCurrentAmmo ();
};

/*
============
CycleWeaponReverseCommand

Go to the prev weapon with ammo
============
*/
void() CycleWeaponReverseCommand =
{
	local	float	it, am;
	
	it = self.items;
	self.impulse = 0;

	am = 0;

	if (self.attack_finished > time || self.checkwepstop_finished > time || self.deadflag != 0 || self.view_ofs == '0 0 0')
		return;

	if (self.weapon == W_SUBMACHINE)
	{
		self.weapon = W_SHOTGUN;
		if (self.ammo_shells < 1)
			am = 1;
	}
	else if (self.weapon == W_SHOTGUN)
	{
		self.weapon = W_DOUBLEPISTOL;
		if (self.ammo_nails < 2)
			am = 1;
	}
	else if (self.weapon == W_DOUBLEPISTOL)
	{
		self.weapon = W_PISTOL;
		if (self.ammo_nails < 1)
			am = 1;
	}
	else if (self.weapon == W_PISTOL)
	{
		self.weapon = W_KNIFE;
	}
	else if (self.weapon == W_KNIFE)
	{
		self.weapon = W_SUBMACHINE;
		if (self.ammo_nails < 1)
			am = 1;
	}

	if ((it & self.weapon) && am == 0)
	{
		W_SetCurrentAmmo();
		self.checkwepstop_finished = time + 0.4;
		player_grabwep1();
	}
	else CycleWeaponReverseCommand();
};


/*
============
CycleWeaponForwardCommand

Go to the next weapon with ammo
============
*/
void() CycleWeaponForwardCommand =
{
	local	float	it, am;
	
	it = self.items;
	self.impulse = 0;

	am = 0;

	if (self.attack_finished > time || self.checkwepstop_finished > time || self.deadflag != 0 || self.view_ofs == '0 0 0')
		return;

	if (self.weapon == W_KNIFE)
	{
		self.weapon = W_PISTOL;
		if (self.ammo_nails < 1)
			am = 1;
	}
	else if (self.weapon == W_PISTOL)
	{
		self.weapon = W_DOUBLEPISTOL;
		if (self.ammo_nails < 2)
			am = 1;
	}
	else if (self.weapon == W_DOUBLEPISTOL)
	{
		self.weapon = W_SHOTGUN;
		if (self.ammo_shells < 1)
			am = 1;
	}
	else if (self.weapon == W_SHOTGUN)
	{
		self.weapon = W_SUBMACHINE;
		if (self.ammo_nails < 1)
			am = 1;
	}
	else if (self.weapon == W_SUBMACHINE)
	{
		self.weapon = W_KNIFE;
	}

	if ((it & self.weapon) && am == 0)
	{
		W_SetCurrentAmmo();
		self.checkwepstop_finished = time + 0.4;
		player_grabwep1();
	}
	else CycleWeaponForwardCommand();
};

void() GrenadeCheck = // check if player can throw grenade
{
	if (self.attack_finished > time || self.checkwepstop_finished > time || self.deadflag != 0 || self.view_ofs == '0 0 0' || world.model == "maps/start.bsp")
		return;

	if (!self.ammo_rockets)
	{
		if (self.ishuman)
			centerprint (self, "\n\n\n\n\n\nNo Grenades . . .\n");
		return;
	}
	self.attack_finished = time + 0.8;
	self.ammo_rockets = self.ammo_rockets - 1;
	player_grenade1();
};

void() ImpulseCommands =
{
	if (self.impulse >= 1 && self.impulse <= 5) ChangeWeapons ();
	if (self.impulse == 11) CycleWeaponForwardCommand();
	if (self.impulse == 12) CycleWeaponReverseCommand();
	if (self.impulse == 6) GrenadeCheck();
	if (self.impulse == 20) MenuDown();
	if (self.impulse == 21) MenuUp();
	if (self.impulse == 22) MenuSelect();
	BotImpulses ();
	self.impulse = 0;
};