--[[
Strategic Remote Gun Placement
by Jeff Griffin (GeoGriffin)

Name: LUA Scripted Strategic Remote Gun Placement
Type: Scripted Weapon
Script Version: 1.0
GM Version: 8.4 build 013
Short Description: Strategic Remote Gun Placements, or SRGPs, are
capable of firing an explosive projectile over a long distance.
Long Description: SRGPs use a sophisticated laser-tracking technology
to determine the target of a projectile. Using simple trajectory
calculations, the SRGP adjusts the elevation and rotation angle
using a rotating base according to the distance and location ofthe
target. At long distances, however, small inaccuracies must be
compensated for.
]]

--projectile = "models/props_junk/watermelon01.mdl";
projectile = "models/props_junk/Rock001a.mdl"; --the projectile model
breakable = false; --is the projectile model breakable? (true/false)
turnSpeed = math.rad(.3); --degrees per frame
gunForce = 12000; --force

-- Don't touch (if you have no idea what you are doing).

_OpenScript( "functions_vector.lua" );

MyIndex	= 0;
Owner = 0;
CurrentTime = 0;

gunPlacements = {}
wepMode = 1;
wepFire = false;
aimCoords = nil;
gravity = 600;
oldOwner = Owner;
	
function onInit()
	--_SWEPSetSound(MyIndex, "single_shot", "Weapon_RPG.Single");
end

function pyth_func(x, y)
	x = math.pow(x, 2);
	y = math.pow(y, 2);
	return math.pow(x+y, .5);
end

function cleanUp()
	for i = 1, table.getn(gunPlacements) do
	    for a = 1, table.getn(gunPlacements[1].projs) do
	        _EntRemove(gunPlacements[1].projs[1].id);
	        table.remove(gunPlacements, 1);
	    end
	    _EntRemove(gunPlacements[1].id);
	    table.remove(gunPlacements, 1);
	end
end

function onThink()
	if (oldOwner ~= Owner) then wepMode = 1; cleanUp(); end
	if (wepMode == 2) then
		local i = 1;
		local maxI = table.getn(gunPlacements);
		while i <= maxI do
			if (_EntExists(gunPlacements[i].id) == false) then
				table.remove(gunPlacements, i);
				i = i - 1;
				maxI = maxI - 1;
			else
				if (gunPlacements[i].aimAng == nil) then
					local gunAng = _EntGetAng(gunPlacements[i].id);
					local gunDirAng = math.atan(gunAng.y/gunAng.x);
					if (gunAng.y < 0 and gunAng.x < 0) then
						gunDirAng = gunDirAng - math.pi;
					elseif (gunAng.y > 0 and gunAng.x < 0) then
						gunDirAng = gunDirAng + math.pi;
					end
					gunPlacements[i].aimAng = gunDirAng;
				end

				if (gunPlacements[i].destAimAng ~= nil and gunPlacements[i].aimAng ~= gunPlacements[i].destAimAng) then
					local aimAng = gunPlacements[i].aimAng;
					local destAimAng = gunPlacements[i].destAimAng;
					--[[if (aimAng < 0) then aimAng = aimAng + (2 * math.pi); end
					if (destAimAng < 0) then destAimAng = destAimAng + (2 * math.pi); end

					if (aimAng + turnSpeed < destAimAng) then
						aimAng = aimAng + turnSpeed;
					elseif (aimAng < destAimAng) then
						aimAng = destAimAng;
					elseif (aimAng - turnSpeed > destAimAng) then
						aimAng = aimAng - turnSpeed;
					elseif (aimAng > destAimAng) then
						aimAng = destAimAng;
					end

					if (aimAng > math.pi) then aimAng = aimAng - (2 * math.pi); end]]

					if (aimAng - destAimAng > 0 and aimAng - destAimAng < turnSpeed) then
						aimAng = destAimAng;
					elseif ((aimAng - destAimAng < 0 and aimAng - destAimAng > -turnSpeed) or
					(aimAng - destAimAng > math.pi and aimAng - destAimAng < math.pi + turnSpeed)) then
						aimAng = destAimAng;
					elseif (aimAng - destAimAng > 0 and aimAng - destAimAng < math.pi or aimAng - destAimAng < -(math.pi)) then
						aimAng = aimAng - turnSpeed;
					elseif (aimAng - destAimAng < 0 or aimAng - destAimAng > math.pi) then
						aimAng = aimAng + turnSpeed;
					end

					if (aimAng > math.pi) then aimAng = aimAng - (2 * math.pi); end
					if (aimAng < -(math.pi)) then aimAng = aimAng + (2 * math.pi); end

					gunPlacements[i].aimAng = aimAng;
					_EntSetAngAngle(gunPlacements[i].id, vector3(0, math.deg(aimAng), 180));
				end

				if (gunPlacements[i].wepFire == true and gunPlacements[i].destAimAng ~= nil and
				gunPlacements[i].aimAng == gunPlacements[i].destAimAng) then
					local proj = _EntCreate("prop_physics_multiplayer");
					_EntPrecacheModel(projectile);
					_EntSetModel(proj, projectile);
					local startPos = vecAdd(_EntGetPos(gunPlacements[i].id), vector3(0, 0, 25));
					_EntSetPos(proj, startPos);

					local dist = pyth_func(gunPlacements[i].destAimPos.x - startPos.x, gunPlacements[i].destAimPos.y - startPos.y);
					--local dZ = gunPlacements[i].destAimPos.z - startPos.z;
					--_Msg(string.format("( (%f * %f) / (2 * %f^2) ) = %f / %f = %f\n", gravity, dist, gunForce/10, gravity * dist,
					--math.pow(gunForce/10, 2), (gravity * dist) / (math.pow(gunForce/10, 2)) ));
					--_Msg(string.format("asin(%f) = %f\n", (gravity * dist) / (math.pow(gunForce/10, 2)),
					--math.asin((gravity * dist) / (math.pow(gunForce/10, 2))) ));
					local trajEqu = (gravity * dist) / (2 * math.pow(gunForce / 8, 2));
					local elevAng;
					if (trajEqu > 1) then
						elevAng = math.pi / 4;
						_Msg(string.format("trajEqu: %f > 1\n", trajEqu));
					else
						elevAng = math.pi / 2 - math.asin(trajEqu);
						_Msg(string.format("trajEqu: %f < 1\n", trajEqu));
					end
					_Msg(string.format("elevAng = %f\n", elevAng));
					local aimAng = math.rad(_EntGetAngAngle(gunPlacements[i].id).y);
					local dirVec = vector3(math.cos(aimAng) * math.cos(elevAng), math.sin(aimAng) * math.cos(elevAng), math.sin(elevAng));

					_EntSpawn(proj);
					_PhysApplyForce(proj, vecMul(dirVec, vector3(gunForce, gunForce, gunForce)));
					_EntFire(proj, "physdamagescale", 1000, 0);
					_PhysEnableDrag(proj, false);

					--[[local spread = 70;
					for b = -1, 1 do
						for c = -1, 1 do
							for d = 0, 1 do
								local location = vecAdd(_EntGetPos(gunPlacements[i].id), vector3(b * spread, c * spread, d * spread));
								_EffectInit();
									--_EffectSetEnt(gunPlacements[i].projs[i].id);
									_EffectSetOrigin(location);
									_EffectSetStart(location);
									_EffectSetScale(1);
									_EffectSetMagnitude(1);
								_EffectDispatch("explosion");
							end
						end
					end]]

					local gunExplosion = _EntCreate("env_explosion");
					_EntSetKeyValue(gunExplosion, "iMagnitude", "0");
					--_EntSetKeyValue(gunExplosion, "iRadiusOverride", "200");
					_EntSetPos(gunExplosion, startPos);
					_EntSpawn(gunExplosion);
					_EntFire(gunExplosion, "Explode", "", 0);

					local projTable = {id = proj, fireTime = _CurTime(), firePos = startPos, fireAng = elevAng};

					table.insert(gunPlacements[i].projs, projTable);
					gunPlacements[i].wepFire = false;
				end

				local a = 1;
				local max = table.getn(gunPlacements[i].projs);
				while a <= max do
					--_Msg(string.format("%d - %d\n", a, max));
					if (_EntExists(gunPlacements[i].projs[a].id) ~= true) then
						--[[local spread = 70;
						for b = -1, 1 do
							for c = -1, 1 do
								for d = 0, 1 do
									local location = vecAdd(gunPlacements[i].projs[a].lastPos, vector3(b * spread, c * spread, d * spread));
									_EffectInit();
										_EffectSetOrigin(location);
										_EffectSetStart(location);
										_EffectSetScale(1);
										_EffectSetMagnitude(1);
									_EffectDispatch("explosion");
								end
							end
						end]]
						local location = gunPlacements[i].projs[a].lastPos;
						local projExplosion = _EntCreate("env_explosion");
						_EntSetKeyValue(projExplosion, "iMagnitude", "150");
						--_EntSetKeyValue(projExplosion, "iRadiusOverride", "200");
						_EntSetPos(projExplosion, location);
						_EntSpawn(projExplosion);
						_EntFire(projExplosion, "Explode", "", 0);

						--[[local time = gunPlacements[i].projs[a].lastTime - gunPlacements[i].projs[a].fireTime;
						local calcGravity = (2 * ((initialVelocity / 10 * math.sin(gunPlacements[i].projs[a].fireAng)) * time -
						(gunPlacements[i].projs[a].lastPos.z - gunPlacements[i].projs[a].firePos.z))) / math.pow(time, 2);

						_Msg(string.format("%f\n", calcGravity));
						--gravity = calcGravity;]]

						table.remove(gunPlacements[i].projs, a);
						max = max - 1;
						a = a - 1;
					else
						gunPlacements[i].projs[a].lastPos = _EntGetPos(gunPlacements[i].projs[a].id);
						gunPlacements[i].projs[a].lastTime = _CurTime();
						if (breakable == false and _CurTime() - gunPlacements[i].projs[a].fireTime > .1) then
							local projPos = _EntGetPos(gunPlacements[i].projs[a].id);
							_TraceLine(projPos, vector3(0, 0, -1), 4096, gunPlacements[i].projs[a].id);
							if (projPos.z - _TraceEndPos().z < 30) then
								_EntRemove(gunPlacements[i].projs[a].id);
							end
						end
					end
					a = a + 1;
				end
			end
			i = i + 1;
  		end
	end
	oldOwner = Owner;
end
	
function onPrimaryAttack()
	if (_PlayerInfo(Owner, "alive") == false) then return; end
	
	if (wepMode == 1) then
		local gunPlacement = _EntCreate("prop_physics");
		
		if (gunPlacement > 0) then
			_EntPrecacheModel("models/Combine_turrets/ground_turret.mdl");
			_EntSetModel(gunPlacement, "models/Combine_turrets/ground_turret.mdl");
			local plyAng = _PlayerGetShootAng(Owner);
			local plyDirAng = math.atan(plyAng.y/plyAng.x);
			if (plyAng.y < 0 and plyAng.x < 0) then
				plyDirAng = plyDirAng - math.pi;
			elseif (plyAng.y > 0 and plyAng.x < 0) then
				plyDirAng = plyDirAng + math.pi;
			end
			local addVec = vector3(math.cos(plyDirAng), math.sin(plyDirAng), 0);
			addVec = vecMul(addVec, vector3(50, 50, 0));
			_EntSetPos(gunPlacement, vecAdd(_EntGetPos(Owner), addVec));
			_EntSetAngAngle(gunPlacement, vector3(0, math.deg(plyDirAng), 180));
			--_EntSetOwner(gunPlacement, Owner);
			
			_EntSpawn(gunPlacement);
			_PhysSetMass(gunPlacement, 1000);
			
			table.insert(gunPlacements, {id = gunPlacement, projs = {}, wepFire = false})
		end
	elseif (wepMode == 2) then
		_TraceLine(_PlayerGetShootPos(Owner), _PlayerGetShootAng(Owner), 4096, Owner);
		aimCoords = _TraceEndPos();
		for i = 1, table.getn(gunPlacements) do
			local gunpos = _EntGetPos(gunPlacements[i].id);
			local dY = aimCoords.y - gunpos.y;
			local dX = aimCoords.x - gunpos.x;
			local dirAngle = math.atan(dY/dX);
			if (dY < 0 and dX < 0) then
				dirAngle = dirAngle - math.pi;
			elseif (dY > 0 and dX < 0) then
				dirAngle = dirAngle + math.pi;
			end
			gunPlacements[i].destAimAng = dirAngle;
			gunPlacements[i].destAimPos = aimCoords;
			gunPlacements[i].wepFire = true;
		end
	end
end
	
	
function onSecondaryAttack()		
	if (_PlayerInfo(Owner, "alive") == false) then return; end
	
	if (wepMode == 1) then
		wepMode = 2;
	elseif (wepMode == 2) then
		wepMode = 1;
	end
end

function getPrintName() return "Strategic Remote Gun Placement"; end
function getClassName() return "weapon_srgp"; end

function getViewModel() return "models/Weapons/v_slam.mdl"; end
function getWorldModel() return "models/Weapons/v_slam.mdl"; end
function getAnimPrefix() return "rpg"; end

function getWeaponSwapHands() return false; end

function getWeaponFOV() return 80; end

function getWeaponSlot() return 5;	 end
function getWeaponSlotPos() return 6; end

function getFiresUnderwater() return true; end

function getReloadsSingly() return false; end

function getDamage() return 0; end

function getPrimaryShotDelay() return 0.4; end
function getSecondaryShotDelay() return 0.4; end

function getPrimaryIsAutomatic() return false; end
function getSecondaryIsAutomatic() return false; end

function getMaxClipPrimary() return -1; end
function getMaxClipSecondary() return -1; end

function getDefClipPrimary() return 1; end
function getDefClipSecondary() return 1; end

function getPrimaryScriptOverride() return 1; end
function getSecondaryScriptOverride() return 1; end

function getBulletSpread() return vector3(0, 0, 0); end
function getViewKick() return vector3(0, 0, 0); end
function getViewKickRandom() return vector3(0, 0, 0); end

function getBulletSpreadSecondary() return vector3(0, 0, 0); end
function getViewKickSecondary() return vector3(0, 0, 0); end
function getViewKickRandomSecondary() return vector3(0, 0, 0); end

function getPrimaryAmmoType() return "pistol"; end
function getSecondaryAmmoType() return "pistol"; end
