
init(delay)
{
	self endon("killed_player");
	self endon("disconnect");

	if(self.ex_air_strike) return;

	self notify("end_airstrike");
	wait(0.1);
	self endon("end_airstrike");
	
	self.ex_air_strike = true;

	// wait the first
	if(!isDefined(delay)) delay = level.ex_rank_airstrike_first;
	wait delay;

	// check for napalm
	self.ex_napalm = false;

	switch(level.ex_rank_wmdtype)
	{
		case 1: // random rank
			if(self.pers["rank"] >= level.ex_rank_napalm && randomInt(100) < 50) self.ex_napalm = true;
			break;

		case 2: // allowed random
			if(level.ex_rank_allow_napalm)
			{
				if(!level.ex_rank_allow_airstrike) self.ex_napalm = true;
				else if(randomInt(100) < 50) self.ex_napalm = true;
			}
			break;

		default: // fixed rank
			if(self.pers["rank"] >= level.ex_rank_napalm) self.ex_napalm = true;
			break;
	}

	while(self.ex_air_strike)
	{
		// let them know the airstrike is available
		if(!self.ex_napalm) self iprintlnbold(&"AIRSTRIKE_READY");
			else self iprintlnbold(&"AIRSTRIKE_READY_NAPALM");
		self teamsound("airstk_ready",1);
			
		// set up the screen icon
		self hudnotify();

		// monitor for binocular fire
		self thread waitforuse();
		
		// wait until they use airstrike
		self waittill("airstrike_over");

		if(level.ex_planes_flak && level.ex_flakison)
		{
			level notify("stop_flak");
			level.ex_flakison = false;
		}

		if(level.ex_rank_wmdtype >= 1) break;
		self iprintlnbold(&"AIRSTRIKE_WAIT");
		self teamsound("airstk_reload",3);

		// now wait for one interval
		wait(level.ex_rank_airstrike_int + randomint(10));
	}

	// stop airstrikes
	self.ex_air_strike = false;
	self notify("airstrike_over");
	self notify("end_airstrike");
}	

waitforuse()
{
	self endon("end_waitforuse");
	self endon("killed_player");

	for(;;)
	{
		self waittill("binocular_enter");
		self thread waitforbinocuse();
		self thread binochinthud();
		wait .2;
	}
}

waitforbinocuse()
{
	// kill dupes
	self notify("binocular_exit");
	wait(0.1);
	self endon("binocular_exit");
	self endon("airstrike_fired");
	self endon("killed_player");

	// wait for use button to be pressed
	calling = false;
	for(;;)
	{
		if(isPlayer(self) && self useButtonPressed() && !calling)
		{
			calling = true;
			self thread callradio();
			self waittill ("radiodone");
		}
		wait .01;						
	}
}

callradio()
{
	self endon("killed_player");

	self iprintlnbold(&"AIRSTRIKE_RADIO_IN");

	targetpos = gettargetposition();
	friendly = friendlyInstrikezone(targetpos);

	self teamsound("airstk_firemission",3.6);
	for(i = 1; i < 4; i++) self teamsound("airstk_" + randomInt(8),0.6);
	self teamsound("airstk_pointfuse",3);

	if(isDefined(targetpos) && isDefined(friendly) && friendly == false)
	{
		self iprintlnbold(&"AIRSTRIKE_ONWAY");
		self teamsound("airstk_ontheway",4);

		// player has used weapon
		self.usedweapons = true;

		airstrike = spawn("script_origin",targetpos);
		airstrike thread firebarrage(self);

		if(level.ex_air_raid) airstrike thread extreme\_ex_utils::playSoundLoc("air_raid",targetpos);

		// clear hud icon
		self hudnotifyremove();

		// clear hint icon
		if(isDefined(self.ex_binocular_hint)) self.ex_binocular_hint destroy();

		// notify threads
		self notify("end_waitforuse");
		self notify ("airstrike_fired");
	}
	else if(!isDefined(targetpos) && !isDefined(friendly))
	{
		friendly = undefined;
		self iprintlnbold(&"AIRSTRIKE_NOT_VALID");
		self teamsound("airstk_novalid",3);
	}
	else if(isDefined(friendly) && friendly == true)
	{
		friendly = undefined;
		self iprintlnbold(&"AIRSTRIKE_FRIENDLY_WARNING");
		self teamsound("airstk_frndly",3);
	}
	else if(isDefined(targetpos) && !isDefined(friendly))
	{
		friendly = undefined;
		self iprintlnbold(&"AIRSTRIKE_TO_CLOSE_WARNING");
		self teamsound("airstk_tooclose",3);
	}

	self notify("radiodone");
}

gettargetposition()
{
	startOrigin = self getEye() + (0,0,20);
	forward = anglesToForward(self getplayerangles());
	forward = [[level.ex_vectorscale]](forward, 100000);
	endOrigin = startOrigin + forward;

	trace = bulletTrace( startOrigin, endOrigin, false, self );
	if(trace["fraction"] == 1.0 || trace["surfacetype"] == "default") return (undefined);
		else return (trace["position"]);
}

firebarrage(owner)
{
	// drop flare
	if(level.ex_rank_wmd_flare) playfx(level.ex_effect["flare_indicator"], self.origin);

	if(level.ex_planes_flak && !level.ex_flakison)
	{
		for(flak=0;flak<level.ex_flakfx;flak++)
			thread extreme\_ex_skyeffects::flakfx();
	}

	wait 5;

	// create planes
	apflangle = randomInt(360);
	self thread createplanes(self.origin , apflangle, owner);

	owner teamsound("pilot_cmg_target", 4);
	wait 3;
	if(level.ex_planes_flak && level.ex_flakison)
		owner teamsound("flack_hang_on", 3);

	self waittill("planes_finished");

	if(level.ex_planes_flak && level.ex_flakison)
	{
		level notify("stop_flak");
		level.ex_flakison = false;
	}

	owner notify ("airstrike_over");
	self delete();
}

calcShellPos(targetPos)
{
	shellPos = undefined;
	iterations = 0;

	while(!isDefined(shellPos) && iterations < 5)
	{
		shellPos = targetPos;
		angle = randomFloat(360);
		radius = randomFloat(level.ex_rank_airstrike_radius);
		randomOffset = (cos(angle) * radius, sin(angle) * radius, 0);
		shellPos += randomOffset;
		startOrigin = shellPos + (0, 0, 800);
		endOrigin = shellPos + (0, 0, -2048);

		trace = bulletTrace( startOrigin, endOrigin, true, undefined );
		if(trace["fraction"] < 1.0) shellPos = trace["position"];
			else shellPos = undefined;

		iterations++;
	}

	if(!isDefined(shellPos)) shellPos = targetPos;
	return shellPos;
}

createplanes(targetpos, apflangle, owner)
{
	thread extreme\_ex_logfile::Log("Planes");

	level endon("ex_stop_ambient_fx");

	// pick a plane team
	if(isPlayer(owner))
	{
		if(owner.pers["team"] == "axis") apteam = 0;
			else apteam = 1;
	}
	else return;

	// set team definition
	if(apteam == 0) level.apteam = "axis";
		else level.apteam = "allies";

	// set planes in sky values
	if(apteam == 0) level.ex_pallieapinsky++;
		else level.ex_paxisapinsky++;
				
	x = targetpos[0];
	y = targetpos[1];
	z = level.ex_mapArea_Max[2] - randomint(150);
	if(level.ex_planes_altitude && (level.ex_planes_altitude <= z))
		z = level.ex_planes_altitude;
	droppos = (x,y,z);

	stenpos = getPlaneStartEnd( droppos, apflangle );
	stenpos2 = getPlaneStartEnd( (stenpos[1]), apflangle );
	if(stenpos2[2] == 1) stenpos[1] = stenpos2[1];

	apstpoint = stenpos[0];
	apenpoint = stenpos[1];
	apposinmap = (x,y,z);

	// plane models
	if(apteam == 0)
		apmodel = "xmodel/vehicle_condor";
	else
		apmodel = "xmodel/mebelle1";

	// plane sounds
	apsoundchoice = [];
	apsoundchoice[0] = "stuka_flyby_1";
	apsoundchoice[1] = "stuka_flyby_2";

	apsound = apsoundchoice[randomInt(apsoundchoice.size)];

	planetype = 1; // bomber

	plane = spawn("script_model", apstpoint);
	plane setModel(apmodel);
	plane.angles = plane.angles + (0, apflangle, 0);
	if(apteam == 1) plane rotateyaw(90, .1);

	plane.apdivesound = spawn("script_model", apstpoint - (0,50,0));
	plane.apdivesound linkto(plane);

	plane.apsound = spawn("script_model", apstpoint - (0,50,0));
	plane.apsound linkto(plane);
	plane.apsound playloopsound(apsound);

	// flighttime
	apspeed = 30; // bombers
	flighttime = calcTime(apstpoint, apenpoint, apspeed);
	plane moveto(apenpoint, flighttime);

	plane.isbombing = false;
	// 10% chance plane will crash, but
	// - only condor, because memphis has axis mixed up. Looks silly.
	// - only if there weren't enough crashes already (4 max)
	planecrash = false;
	if(randomInt(100) < 10 && level.ex_planescrashed < 4 && apteam == 0) planecrash = true;

	for(i = 0; i < flighttime; i += 0.1)
	{
		if(!plane.isbombing && (distance(droppos, plane.origin) < 1500) )
		{
			owner teamsound("fire_away",1);
			plane thread bombsetup(owner, targetpos);
			plane.isbombing = true;
		}

		if(planecrash && !plane.isbombing && (distance(level.ex_playArea_Centre, plane.origin) < 3000) )
		{
			owner teamsound("airstk_vbc",1);
			plane thread airplanecrash(apspeed+10, apteam, self);
			return;
		}
		wait(0.1);
	}

	if(isDefined(plane.apsound))
	{
		plane.apsound stopLoopSound();
		plane.apsound delete();
	}

	if(isDefined(plane.apdivesound))
	{
		plane.apdivesound stopLoopSound();
		plane.apdivesound delete();
	}

	if(apteam == 0) level.ex_paxisapinsky--;
		else level.ex_pallieapinsky--;

	if(isDefined(plane)) plane delete();
	self notify("planes_finished");
}

bombsetup(owner, targetpos)
{
	bombcount = 0;
	bombno = randomInt(3) + 4;

	while(bombcount < bombno)
	{
		self thread dropabomb(owner, targetpos);
		bombcount++;
		wait 0.2;
	}
}

dropabomb(owner, targetpos)
{
	if(!isDefined(self)) return;

	startpoint = self.origin;
	forwardvector = anglestoforward(self.angles);
	endorigin = startpoint + ([[level.ex_vectormulti]](forwardvector,360));

	// get the impact point
	trace = bulletTrace(startpoint,(endorigin[0],endorigin[1],endorigin[2] - 2048), false, self);
	if(trace["fraction"] < 1) endorigin = trace["position"];
		else return; // if no proper position then skip the bomb

	// bomb falltime
	falltime = calcTime(startpoint, endorigin, 20);
	//if(falltime < 0.5) return; // abort

	// spawn the bomb and drop it
	bomb = spawn("script_model", startpoint);
	bomb.angles = self.angles + (-180 + randomint(50),0,0);
	bomb setModel("xmodel/prop_stuka_bomb");
	bomb moveto(endorigin + (0,0, -100), falltime);

	// play the incoming sound falling sound
	bombsfx[0] = "mortar_incoming1";
	bombsfx[1] = "mortar_incoming2";
	bombsfx[2] = "mortar_incoming3";
	bombsfx[3] = "mortar_incoming4";
	bombsfx[4] = "mortar_incoming5";
	bs = randomInt(bombsfx.size);
	bomb playsound(bombsfx[bs]);

	// wait for it to hit
	wait falltime;

	// do the damage
	if(isPlayer(owner))
	{
		if(isDefined(owner.ex_napalm) && owner.ex_napalm == true)
			bomb thread extreme\_ex_utils::scriptedfxradiusdamage(owner, undefined, "MOD_GRENADE", "planebomb_mp", level.ex_planebomb_radius, 500, 400, "plane_bomb", undefined, true, true, true, "napalm");
		else
			bomb thread extreme\_ex_utils::scriptedfxradiusdamage(owner, undefined, "MOD_GRENADE", "planebomb_mp", level.ex_planebomb_radius, 500, 400, "plane_bomb", undefined, true, true, true);
	}
	else
		bomb thread extreme\_ex_utils::scriptedfxradiusdamage(self, undefined, "MOD_GRENADE", "planebomb_mp", level.ex_planebomb_radius, 500, 400, "plane_bomb", undefined, true, true, true);

	// play the explosion sound
	bombsfx[0] = "mortar_explosion1";
	bombsfx[1] = "mortar_explosion2";
	bombsfx[2] = "mortar_explosion3";
	bombsfx[3] = "mortar_explosion4";
	bombsfx[4] = "mortar_explosion5";
	bs = randomInt(bombsfx.size);
	bomb playsound(bombsfx[bs]);

	bomb hide();
	wait 1;
	bomb delete();
}

getPlaneStartEnd(targetpos, angle)
{
	forwardvector = anglestoforward( (0, angle, 0) );
	backpos = targetpos + ([[level.ex_vectormulti]](forwardvector, -30000));
	frontpos = targetpos + ([[level.ex_vectormulti]](forwardvector, 30000));
	fronthit = 0;

	trace = bulletTrace(targetpos, backpos, false, undefined);
	if(trace["fraction"] != 1) start = trace["position"];
		else start = backpos;

	trace = bulletTrace(targetpos, frontpos, false, undefined);
	if(trace["fraction"] != 1)
	{
		endpoint = trace["position"];
		fronthit = 1;
	}
	else endpoint = frontpos;

	startpos = start + ([[level.ex_vectormulti]](forwardvector, -3000));
	endpoint = endpoint + ([[level.ex_vectormulti]](forwardvector, 3000));
	stenpos[0] = startpos;
	stenpos[1] = endpoint;
	stenpos[2] = fronthit;
	return stenpos;
}

friendlyInStrikeZone(targetPos)
{
	// return if friendly fire check has been disabled
	if(level.ex_frndly == 0) return false;

	// dont need to check friendly if gametype is not teamplay
	if(!level.ex_teamplay) return false;

	if(!isDefined(targetPos)) return (undefined);

	if(distance(targetPos, self.origin) <= 1000) return (undefined);

	// check if players in the same team are in targetzone
	players = getentarray("player", "classname");
	for(i = 0; i < players.size; i++)
	{
		if(isPlayer(self) && isPlayer(players[i]))
		{
			if(players[i].sessionstate == "playing" && players[i].pers["team"] == self.pers["team"])
			{
				if(distance(targetpos, players[i].origin) <= 1000)
					return true;
			}
		}
	}
	return false;
}

calcTime(startpos, endpos, speedvalue)
{
	distunit = 1;	// Metres
	speedunit = 1; // Metres per second
	distvalue = distance(startpos, endpos);
	distvalue = int(distvalue * 0.0254); // convert to metres
	timeinsec = (distvalue * distunit) / (speedvalue * speedunit);
	if(timeinsec <= 0) timeinsec = 0.1;
	return timeinsec;
}

airplanecrash(speed, apteam, owner)
{
	thread extreme\_ex_logfile::Log("airplanecrash");

	level.ex_planescrashed++;

	playfx(level.ex_effect["flak_flash"], self.origin - (0, 200, 0));
	self playsound("plane_explosion_2");
	wait 0.2;
	playfx(level.ex_effect["flak_smoke"], self.origin - (0, 200, 0));
	wait 0.2;
	playfx(level.ex_effect["flak_dust"], self.origin - (0, 200, 0));
	wait 0.5;
	playfx(level.ex_effect["plane_smoke"], self.origin);
	wait 0.05;
	playfx(level.ex_effect["plane_smoke"], self.origin);
	wait 0.05;
	self.apdivesound playloopsound("plane_dive");
	playfx(level.ex_effect["plane_explosion"], self.origin);
	self playsound("plane_explosion_2");
	wait 0.5;
	playfx(level.ex_effect["plane_explosion"], self.origin);

	self.fire = 0;
	if(randomInt(100) > 50) self.fire = 1;

	angle = self.angles;
	pitch = randomInt(25) + 25;
	yaw = randomInt(50) - 25;
	roll = 200 - randomInt(400);
	angle_start = angle + (pitch,yaw,0);
	forwardvector = anglestoforward(angle_start);

	endpoint = self.origin + ([[level.ex_vectormulti]](forwardvector,30000));
	trace=bulletTrace(self.origin,endpoint, true, undefined);
	if(trace["fraction"] < 1 && trace["position"][2] > level.ex_mapArea_Min[2] - 500) endpoint = trace["position"];
		else endpoint = self.origin + ([[level.ex_vectormulti]](forwardvector,100));

	falltime = calcTime(self.origin, endpoint, speed);
	rotdone = "notdone";

	if(falltime > 1)
	{
		start_turn = 0;

		self moveto(endpoint,falltime);

		np = (pitch/(0.5/0.05));
		ny = (yaw/(0.5/0.05));
		nr = (roll/(falltime/0.05));

		for(i=0;i<falltime;i+=0.05)
		{
			if(i <= 0.5)
			{
				self.angles += (np,0,0);
				self.angles += (0,ny,0);
				self.angles += (0,0,nr);
			}
			else if(start_turn == 0)
			{
				start_turn = 1;
				self.angles += (0,0,nr);
			}

			playfx(level.ex_effect["plane_smoke"], self.origin);

			if(self.fire == 1) playfx(level.ex_effect["plane_smoke"], self.origin);

			wait 0.05;
		}
	}

	wait 0.05;

	self stoploopsound();

	if(isDefined(self.apsound))
	{
		self.apsound stopLoopSound();
		self.apsound delete();
	}

	if(isDefined(self.apdivesound))
	{
		self.apdivesound stopLoopSound();
		self.apdivesound delete();
	}

	// explosions
	hm = randomInt(2) + 1; // how many explosions, at least 1

	for(i = 0; i < 2;i++)
	{
		for(exp=0; exp < hm; exp++)
		{
			// play the incoming sound falling sound
			planecrashsfx[0] = "plane_explosion_1";
			planecrashsfx[1] = "plane_explosion_2";
			planecrashsfx[2] = "plane_explosion_3";
			pc = randomInt(planecrashsfx.size);
			self playsound(planecrashsfx[pc]);
	
			// * do the damage *
			self thread extreme\_ex_utils::scriptedfxradiusdamage(self, undefined, "MOD_EXPLOSIVE", "plane_mp", level.ex_planecrash_radius, 500, 300, "plane_explosion", undefined, true, true, true);
	
			playfx(level.ex_effect["plane_smoke"], self.origin);
	
			if(exp != 2)
			{
				wait 0.3;
				wait randomFloat(1);
			}
			else
			{
				wait 0.3;
				wait randomFloat(2);
			}
		}
	}

	if(apteam == 0) level.ex_paxisapinsky--;
		else level.ex_pallieapinsky--;

	self planeCrashSmokeFX();
	self delete();
	owner notify("planes_finished");
}

planeCrashSmokeFX()
{
	self thread extreme\_ex_utils::hotSpot(self.origin, 120, "MOD_EXPLOSIVE", "plane_mp");

	for(i = 0; i < 25; i++)
	{
		playfx(level.ex_effect["planecrash_fire"], self.origin);
		wait 0.5;
		playfx(level.ex_effect["planecrash_ball"], self.origin);
		wait 0.5;
	}

	self notify("endhotspot");
	self hide();
	thread planeCrashDelayedSmokeFX(self.origin);
}

planeCrashDelayedSmokeFX(position)
{
	for(i = 0; i < 25; i++)
	{
		playfx(level.ex_effect["planecrash_smoke"], position);
		wait 0.5;
		if(i < 15) playfx(level.ex_effect["planecrash_ball"], position);
		wait 0.5;
	}
}

teamsound(nat,wt)
{
	if (self.pers["team"] == "allies")
	{
		switch(game["allies"])
		{
			case "american":
				self playLocalSound("us_"+nat);
				wait(wt);
				break;
			case "british":
				self playLocalSound("uk_"+nat);
				wait(wt);
				break;
			default:
				self playLocalSound("ru_"+nat);
				wait(wt);
				break;
		}
	}
	else if (self.pers["team"] == "axis")
	{
		switch(game["axis"])
		{
			case "german":
				self playLocalSound("ge_"+nat);
				wait(wt);
				break;
		}
	}
}

hudnotify()
{
	self endon("killed_player");

	self hudnotifyremove();

	self.airstrike_available_icon = newClientHudElem(self);
	self.airstrike_available_icon.alignX = "center";
	self.airstrike_available_icon.alignY = "middle";
	self.airstrike_available_icon.x = 120;
	self.airstrike_available_icon.y = 390;
	self.airstrike_available_icon.alpha = 1;
	self.airstrike_available_icon setShader(game["airstrike"], 32, 32);

	if(!isDefined(self.ex_binocular_hint))
	{
		self.ex_binocular_hint = newClientHudElem( self );
		self.ex_binocular_hint.alignX = "center";
		self.ex_binocular_hint.alignY = "middle";
		self.ex_binocular_hint.fontScale = 1;
		self.ex_binocular_hint.x = 320;
		self.ex_binocular_hint.y = 410;

		self.ex_binocular_hint.sort = 5;
		self.ex_binocular_hint setText(&"WMD_ACTIVATE_HINT");

		// do not show hint if planting a tripwire
		if(level.ex_tweapon)
		{
			if(!self.ex_plantwire && !self.ex_defusewire && !isDefined(self.ex_actimer) || self [[level.ex_getstance]](false) != 2 && !isDefined(self.ex_actimer))
				self.ex_binocular_hint.alpha = 1;
			else self.ex_binocular_hint.alpha = 0;
		}
		else self.ex_binocular_hint.alpha = 1;
	}
}

hudnotifyremove()
{
	if(isDefined(self.airstrike_available_icon)) self.airstrike_available_icon destroy();
}

binochinthud()
{
	self endon("binocular_exit");

	if(!isDefined(self.ex_binocular_hint))
	{
		self.ex_binocular_hint = newClientHudElem( self );
		self.ex_binocular_hint.alignX = "center";
		self.ex_binocular_hint.alignY = "top";
		self.ex_binocular_hint.fontScale = 1;
		self.ex_binocular_hint.x = 320;
		self.ex_binocular_hint.y = 425;
		self.ex_binocular_hint.alpha = 1;
		self.ex_binocular_hint.sort = 5;
	}

	if(!self.ex_napalm) self.ex_binocular_hint setText(&"WMD_AIRSTRIKE_HINT");
		else self.ex_binocular_hint setText(&"WMD_NAPALM_HINT");

	self thread airstrike_binocularhint_hud_destroy2();
}

airstrike_binocularhint_hud_destroy()
{
	if(isDefined(self.ex_binocular_hint)) self.ex_binocular_hint destroy();
}

airstrike_binocularhint_hud_destroy2()
{
	self endon("binocular_enter");
	self endon("killed_player");
	self waittill("binocular_exit");

	if(isDefined(self.ex_binocular_hint)) self.ex_binocular_hint destroy();
}
