#include "spiderbot_weapons.qh"

#ifdef SVQC

void spiderbot_rocket_artillery(entity this)
{
	this.nextthink = time;
	UpdateCSQCProjectile(this);
}

void spiderbot_rocket_unguided(entity this)
{
	this.nextthink = time;

	vector olddir = normalize(this.velocity);
	vector newdir = normalize(this.pos1 - this.origin) + randomvec() * autocvar_g_vehicle_spiderbot_rocket_noise;
	this.velocity = normalize(olddir + newdir * autocvar_g_vehicle_spiderbot_rocket_turnrate) * autocvar_g_vehicle_spiderbot_rocket_speed;

	UpdateCSQCProjectile(this);

	if (IS_DEAD(this.owner) || this.cnt < time || vdist(this.pos1 - this.origin, <, 16))
		this.use(this, NULL, NULL);
}

void spiderbot_rocket_guided(entity this)
{
	this.nextthink  = time;

	if (!this.realowner.vehicle)
		setthink(this, spiderbot_rocket_unguided);

	crosshair_trace(this.realowner);
	vector olddir = normalize(this.velocity);
	vector newdir = normalize(trace_endpos - this.origin) + randomvec() * autocvar_g_vehicle_spiderbot_rocket_noise;
	this.velocity = normalize(olddir + newdir * autocvar_g_vehicle_spiderbot_rocket_turnrate) * autocvar_g_vehicle_spiderbot_rocket_speed;

	UpdateCSQCProjectile(this);

	if (IS_DEAD(this.owner) || this.cnt < time)
		this.use(this, NULL, NULL);
}

void spiderbot_guide_release(entity this)
{
	bool done_trace = false;
	IL_EACH(g_projectiles, it.realowner == this.owner && getthink(it) == spiderbot_rocket_guided,
	{
		if (!done_trace) // something exists, let's trace!
		{
			done_trace = true;
			crosshair_trace(this.owner);
		}

		it.pos1 = trace_endpos;
		setthink(it, spiderbot_rocket_unguided);
	});
}

float spiberbot_calcartillery_flighttime;
vector spiberbot_calcartillery(vector org, vector tgt, float ht)
{
	float grav  = PHYS_GRAVITY(NULL);
	float zdist = tgt.z - org.z;
	float sdist = vlen(tgt - org - zdist * '0 0 1');
	vector sdir = normalize(tgt - org - zdist * '0 0 1');

	// how high do we need to go?
	float jumpheight = fabs(ht);
	if (zdist > 0)
		jumpheight += zdist;

	// push so high...
	float vz = sqrt(2 * grav * jumpheight); // NOTE: sqrt(positive)!

	// we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
	if (ht < 0 && zdist < 0)
		vz = -vz;

	vector solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
	// ALWAYS solvable because jumpheight >= zdist
	if (!solution.z)
		solution.y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
	if (zdist == 0)
		solution.x = solution.y; // solution.x is 0 in this case, so don't use it, but rather use solution.y (which will be sqrt(0.5 * jumpheight / grav), actually)

	if (zdist < 0)
	{
		// down-jump
		if (ht < 0)
		{
			// almost straight line type
			// jump apex is before the jump
			// we must take the larger one
			spiberbot_calcartillery_flighttime = solution.y;
		}
		else
		{
			// regular jump
			// jump apex is during the jump
			// we must take the larger one too
			spiberbot_calcartillery_flighttime = solution.y;
		}
	}
	else
	{
		// up-jump
		if (ht < 0)
		{
			// almost straight line type
			// jump apex is after the jump
			// we must take the smaller one
			spiberbot_calcartillery_flighttime = solution.x;
		}
		else
		{
			// regular jump
			// jump apex is during the jump
			// we must take the larger one
			spiberbot_calcartillery_flighttime = solution.y;
		}
	}
	float vs = sdist / spiberbot_calcartillery_flighttime;

	// finally calculate the velocity
	return sdir * vs + '0 0 1' * vz;
}

void spiderbot_rocket_do(entity this)
{
	if (this.wait != -10)
	{
		if (PHYS_INPUT_BUTTON_ATCK2(this.owner) && STAT(VEHICLESTAT_W2MODE, this) == SBRM_GUIDE)
		{
			if (this.wait == 1
			&& (this.tur_head.frame == 9 || this.tur_head.frame == 1))
			{
				if (this.gun2.cnt < time && this.tur_head.frame == 9)
					this.tur_head.frame = 1;

				return;
			}
			this.wait = 1;
		}
		else
		{
			if (this.wait)
				spiderbot_guide_release(this);

			this.wait = 0;
		}
	}

	if (this.gun2.cnt > time)
		return;

	if (this.tur_head.frame >= 9)
	{
		this.tur_head.frame = 1;
		this.wait = 0;
	}

	if (this.wait != -10
	&& !PHYS_INPUT_BUTTON_ATCK2(this.owner))
		return;

	if (weaponLocked(this.owner) || weaponUseForbidden(this.owner))
		return;

	entity rocket = NULL;
	vector v = gettaginfo(this.tur_head, gettagindex(this.tur_head, "tag_fire"));

	switch (STAT(VEHICLESTAT_W2MODE, this))
	{
		case SBRM_VOLLY:
		{
			rocket = vehicles_projectile(this, EFFECT_SPIDERBOT_ROCKETLAUNCH, SND_VEH_SPIDERBOT_ROCKET_FIRE,
				v, normalize(randomvec() * autocvar_g_vehicle_spiderbot_rocket_spread + v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
				autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
				DEATH_VH_SPID_ROCKET.m_id, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, true, this.owner);
			crosshair_trace(this.owner);
			float _dist = (random() * autocvar_g_vehicle_spiderbot_rocket_radius) + vlen(v - trace_endpos);
			_dist -= random() * autocvar_g_vehicle_spiderbot_rocket_radius;
			rocket.nextthink = time + (_dist / autocvar_g_vehicle_spiderbot_rocket_speed);
			setthink(rocket, vehicles_projectile_explode_think);

			if (PHYS_INPUT_BUTTON_ATCK2(this.owner) && this.tur_head.frame == 1)
				this.wait = -10;
			break;
		}
		case SBRM_GUIDE:
			rocket = vehicles_projectile(this, EFFECT_SPIDERBOT_ROCKETLAUNCH, SND_VEH_SPIDERBOT_ROCKET_FIRE,
				v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
				autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
				DEATH_VH_SPID_ROCKET.m_id, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, false, this.owner);
			crosshair_trace(this.owner);
			rocket.pos1 = trace_endpos;
			rocket.nextthink = time;
			setthink(rocket, spiderbot_rocket_guided);
			break;
		case SBRM_ARTILLERY:
		{
			rocket = vehicles_projectile(this, EFFECT_SPIDERBOT_ROCKETLAUNCH, SND_VEH_SPIDERBOT_ROCKET_FIRE,
				v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
				autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
				DEATH_VH_SPID_ROCKET.m_id, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, true, this.owner);

			crosshair_trace(this.owner);

			rocket.pos1   = trace_endpos + randomvec() * (0.75 * autocvar_g_vehicle_spiderbot_rocket_radius);
			rocket.pos1.z = trace_endpos.z;

			traceline(v, v + '0 0 1' * max_shot_distance, MOVE_WORLDONLY, this);
			float h1 = 0.75 * vlen(v - trace_endpos);

			//v = trace_endpos;
			traceline(v, rocket.pos1 + '0 0 1' * max_shot_distance, MOVE_WORLDONLY, this);
			float h2 = 0.75 * vlen(rocket.pos1 - v);

			rocket.velocity = spiberbot_calcartillery(v, rocket.pos1, (h1 < h2 ? h1 : h2));
			set_movetype(rocket, MOVETYPE_TOSS);
			rocket.gravity = 1;
			//setthink(rocket, spiderbot_rocket_artillery);
			break;
		}
	}
	rocket.classname = "spiderbot_rocket";

	rocket.cnt = time + autocvar_g_vehicle_spiderbot_rocket_lifetime;

	++this.tur_head.frame;
	if (this.tur_head.frame == 9)
		this.attack_finished_single[0] = autocvar_g_vehicle_spiderbot_rocket_reload;
	else
		this.attack_finished_single[0] = (STAT(VEHICLESTAT_W2MODE, this) == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire;

	this.gun2.cnt = time + this.attack_finished_single[0];
}

#endif // SVQC
