#include extreme\_ex_drm;
#include extreme\_ex_weapons;

init()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	if(drm_getCvarInt(level.weapons["flamethrower_axis"].server_allowcvar) == 0 || drm_getCvarInt(level.weapons["flamethrower_allies"].server_allowcvar) == 0)
	  return;

	flame_range = 500;
	
	self.floatingmodel = spawn("script_origin", GetTargetedPos(flame_range));
	self thread monitorFlamethrower(flame_range);

	while(true)
	{
		if(isAlive(self)) self.floatingmodel.origin = GetTargetedPos(flame_range);
		wait .05;
	}
}

monitorFlamethrower(flame_range)
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	flames = [];
	flames_max = 20;  // Maximum number of array elements (= spawned flames)
	flame_life = 1;   // Every flame entity lives one second
	flame_damage = 20; // Damage per flame (remember: several flames hit the target p/sec)
	flame_damage_radius = 100;

	flame_index = 0;
	flames_alloc = 10; // Number of array elements pre-allocated
	for(i = 1; i <= flames_alloc; i++)
	{
		flames[i] = spawnstruct();
		flames[i].allocated = false;
		flames[i].flame = undefined;
	}

	self.tankonback = undefined;

	while(true)
	{
		wait(.1);

		sWeapon = self getCurrentWeapon();

		// Check if player is (still) carrying a flamethrower (any slot)
		weapon1 = self getweaponslotweapon("primary");
		weapon2 = self getweaponslotweapon("primaryb");

		if(isFlamethrower(weapon1) || isFlamethrower(weapon2))
		{
			// Flamethrower found: attach the gas tank to the back if not attached already
			if(!isDefined(self.tankonback))
			{
				// Detach current weapon on back
				if(isDefined(self.weapononback))
				{
					self detach("xmodel/" + self.weapononback, "");
					self.weapononback = undefined;
				}
				self attach("xmodel/ft_tank", "j_spine4");
				self.tankonback = true;
			}
		}
		else
		{
			// No flamethrower (anymore): detach the tank if attached
			if(isDefined(self.tankonback))
			{
				self detach("xmodel/ft_tank", "j_spine4");
				self.tankonback = undefined;
			}
		}

		flame_index = 0;
		flame_refused = 0;
		
		while(self attackbuttonpressed() && isFlamethrower(sWeapon))
		{
			wait(.1);

			// Check if player is on turret
			if(isdefined(self.onturret)) continue;

			// Check distance to object in front of player. Too close = no flame
			trace = self GetEyeTrace(1000);
			trace_dist = distance(trace["position"], self.origin);
			if(trace_dist < 100)
			{
				flame_refused++;
				if(flame_refused == 1 || flame_refused%5 == 0) self playsound("ft_refuse");
				continue;
			}
			else flame_refused = 0;

			// Next flame. Check if it has an allocated array element
			flame_index++;
			if(flame_index > flames_alloc)
			{
				// If first flame is still alive, expand array if within limits
				if(flames[1].allocated && flames_alloc <= flames_max)
				{
					flames_alloc++;
					flames[flames_alloc] = spawnstruct();
					flames[flames_alloc].allocated = false;
					flames[flames_alloc].flame = undefined;
					//logprint("FLAMETHROWER DEBUG: flame array for player " + self.name + " increased to " + flames_alloc +".\n");
				}
				else flame_index = 1;
			}
			// Did we cycle a full array?
			if(flames[flame_index].allocated) continue;

			// Play flamethrower sound on 1st and every 5th flame
			if(flame_index == 1 || flame_index%5 == 0) self playsound("ft_fire");

			// Now get a target
			trace_entity = self;
			// Unfortunately not supported in MP: flame_start = self gettagorigin("tag_flash");
			flame_start = self getTargetedPos(65);
			flame_target = self.floatingmodel.origin;

			trace = bulletTrace(flame_start, flame_target, true, undefined);
			if(trace["fraction"] != 1 && isDefined(trace["entity"]))
			{
				trace_entity = trace["entity"];
				flame_target = trace_entity.origin;
			}
			else
			{
				trace = bulletTrace(flame_start, flame_target, false, undefined);
				if(trace["fraction"] != 1 && trace["surfacetype"] != "default")
					flame_target = trace["position"];
			}

			if(!isDefined(flame_target))
				flame_target = self.floatingmodel.origin;

			// Limit how many times a flame may duplicate itself while traveling
			trace_dist = distance(flame_start, flame_target);
			flame_loop = (flame_range / trace_dist) * 0.05;

			// Play impact and decal fx only on 10th flame (long distance shot). Play normal fx for other flames
			flame_fx = 0;
			if(flame_index%10 == 0) flame_fx = 1;

			flames[flame_index].allocated = true;
			flames[flame_index].flame = spawn("script_model", flame_start);
			flames[flame_index].flame setModel("xmodel/weapon_knife"); // Substitution model (always precached)
			flames[flame_index].flame.angles = self.angles;
			flames[flame_index].flame hide();
			flames[flame_index].flame thread showFlame(flames[flame_index], flame_fx, flame_loop, flame_target, flame_life);

			players = getentarray("player", "classname");
			for(i = 0; i < players.size; i++)
			{
				player = players[i];
					
				// Skip self
				if(player == self) continue;

				// Skip dead players, spectators and spawn protected players
				if(!isAlive(player) || player.sessionteam == "spectator" || player.ex_invulnerable) continue;

				// Respect friendly fire settings 0 (off) and 2 (reflect; it doesn't damage the attacker though)
				if(level.ex_teamplay && (level.friendlyfire == "0" || level.friendlyfire == "2"))
					if(player.pers["team"] == self.pers["team"]) continue;

				// If player is targeted and hit, set fixed damage and long burntime
				if(trace_entity == player)
				{
					iDamage = flame_damage;
					iBurntime = 10;
				}
				else
				{
					// Skip if player is not near flame target
					trace_dist = distance(flame_target, player.origin);
					if( !isAlive(player) || player.sessionstate != "playing" || trace_dist >= flame_damage_radius ) continue;

					// Otherwise, calculate damage and burntime (depending on distance)
					iDamage = int(flame_damage * (1 - (trace_dist / flame_damage_radius)));
					if(trace_dist <= (flame_damage_radius / 2)) iBurntime = 6;
						else iBurntime = 3;
				}

				// If player is already on fire, damage depends on flame index
				if(isDefined(player.ex_isonfire))
					iDamage = int(iDamage * (flame_index / 10));

				// Burn and damage the player
				if(iDamage < player.health)
				{

					player.health = player.health - iDamage;
					player thread burnPlayer(self, sWeapon, iBurntime);
					//player thread extreme\_ex_bleeding::doBleedPainSound();
					//player thread maps\mp\gametypes\_shellshock::shellshockOnDamage("MOD_PROJECTILE", iDamage);
					//player playrumble("damage_heavy");
				}
				else
					player thread [[level.callbackPlayerDamage]](self, self, iDamage, 1, "MOD_PROJECTILE", sWeapon, undefined, (0,0,1), "none", 0);
			}

			// Check if still holding the flamethrower
			sWeapon = self getCurrentWeapon();
		}
	}
}

showFlame(flame_pointer, flame_fx, flame_loop, flame_target, flame_life)
{
	self thread playFlameFX(flame_fx, flame_loop);

	self moveto(flame_target, flame_life);

	wait(flame_life);
	self delete();
	if(isDefined(flame_pointer)) flame_pointer.allocated = false;
}

playFlameFX(flame_fx, loopTime)
{
	fxName = level.ex_effect["ft_fire0"]; // fx without impact and decal

	count = 0;
	while(isDefined(self))
	{
		playfx(fxName, self.origin);
		wait(loopTime);
		count++;
		if(flame_fx && count == 10)
			fxName = level.ex_effect["ft_fire1"]; // fx with impact and decal
	}
}

getEyeTrace(num)
{
	self endon("disconnect");
	self endon("ex_dead");

	startOrigin = self getEye() + self getPlayerEyeOffset();
	forward = anglesToForward(self getplayerangles());
	forward = (forward[0] * num, forward[1] * num, forward[2] * num);
	endOrigin = startOrigin + forward;
	trace = bulletTrace(startOrigin, endOrigin, false, undefined);

	return trace;
}

getTargetedPos(num)
{
	self endon("disconnect");
	self endon("ex_dead");

	startOrigin = self getEye() + self getPlayerEyeOffset();
	forward = anglesToForward(self getplayerangles());
	forward = (forward[0] * num, forward[1] * num, forward[2] * num);
	endOrigin = startOrigin + forward;

	return endOrigin;
}

getPlayerEyeOffset()
{
	self endon("disconnect");
	self endon("ex_dead");

	offset = (0,0,16); // Stand
	if(self.ex_stance == 1) offset = (0,0,2);   // Crouch
	if(self.ex_stance == 2) offset = (0,0,-27); // Prone

	return offset;
}

burnPlayer(eAttacker, sWeapon, burntime)
{
	self endon("disconnect");
	self endon("ex_dead");

	if(isDefined(self.ex_isonfire)) return;
	self.ex_isonfire = 1;

	wait(0.5);
	if(randomint(100) > 10) self playsound("scream"); // 90% chance they scream

	tag = "j_spine1";
	burntime = burntime * 4; // loop is quarter of a second, so x4 to convert to seconds

	for(i = 0; i < burntime; i++)
	{
		// For every second on fire, player will lose some health
		if(burntime%4 == 0)
		{
			iDamage = 5;
			if(iDamage < self.health)
				self.health = self.health - iDamage;
			else
				self thread [[level.callbackPlayerDamage]](eAttacker, eAttacker, iDamage, 1, "MOD_PROJECTILE", sWeapon, undefined, (0,0,1), "none", 0);
		}

		switch(randomint(13))
		{
			case 0: tag = "j_hip_le"; break;
			case 1: tag = "j_hip_ri"; break;
			case 2: tag = "j_knee_le"; break;
			case 3: tag = "j_ankle_ri"; break;
			case 4: tag = "j_knee_ri"; break;
			case 5: tag = "j_spine1"; break;
			case 6:	tag = "j_wrist_ri"; break;
			case 7: tag = "j_head"; break;
			case 8: tag = "j_shoulder_le"; break;
			case 9: tag = "j_shoulder_ri"; break;
			case 10:tag = "j_elbow_le"; break;
			case 11: tag = "j_elbow_ri"; break;
			case 12: tag = "j_wrist_le"; break;
		}

		if(burntime == 4)
		{
			if(isDefined(self))
				self thread playBurnFX(tag, level.ex_effect["playerburn2"], .1);
		}
		else
		{
			if(isDefined(self))
			{
				playfxontag(level.ex_effect["playerburn2"], self, tag);
				playfxontag(level.ex_effect["playerburn"], self, "j_spine1");
			}
		}

		wait .25;
	}

	if(isAlive(self))
		self.ex_isonfire = undefined;
}

playBurnFX(tag, fxName, loopTime)
{
	self endon("disconnect");
	self endon("ex_dead");

	while(isdefined(self) && isDefined(self.ex_isonfire))
	{
		playfxOnTag(fxName, self, tag);
		wait(loopTime);
	}
}

isFlamethrower(weapon)
{
	switch(weapon)
	{
		case "flamethrower_axis":
		case "flamethrower_allies": return true;
	}

	return false;
}

tankExplosion(eVictim, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime)
{
	if(isDefined(eVictim.tankonback))
	{
		eVictim detach("xmodel/ft_tank", "j_spine4");
		eVictim.tankonback = undefined;

		explosion = spawn("script_origin", eVictim.origin);
		playfx(level.ex_effect["artillery"], explosion.origin);
		explosion playSound("artillery_explosion");
		eVictim [[level.ex_orignalPlayerDamage]](eAttacker, eAttacker, 100, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime);
		explosion delete();
	}
}
