-- Welcome to the variable museum:
local player = script.Parent.Parent.Parent
local plane,mainParts,info,main,move,gryo,seat,accel,canCrash,crashForce,crashSpin,crashVisual,maxBank,maxSpeed,speedVary,stallSpeed,throttleInc,altRestrict,altMin,altMax,altSet
local desiredSpeed,currentSpeed,realSpeed = 0,0,0
local mouseSave
local gearParts = {}
local flying,on,dead,gear,throttle = false,false,false,true,0
local gui = script.Parent.Parent.Plane
local panel = script.Parent.Panel
local lowestPoint = 0
local A = math.abs	-- Creating a shortcut for the function

local keys = {
	engine={key};
	spdup={byte=0;down=false};
	spddwn={byte=0;down=false};
}

function gears()
	if script.Parent.Plane.Value.Animations.Value.Gears.Value.Value == true then
		panel.Gear.Value.Text = "On"
	else if script.Parent.Plane.Value.Animations.Value.Gears.Value.Value == false then
		panel.Gear.Value.Text = "Off"
	end
	end
end

function flaps()
		panel.Flaps.Value.Text = script.Parent.Plane.Value.Animations.Value.Flaps.Value.Value
end

function rthrust()
		if script.Parent.Plane.Value.Animations.Value.RThrust.Value.Value == true then
		panel.RThrust.Value.Text = "On"
	else if script.Parent.Plane.Value.Animations.Value.RThrust.Value.Value == false then
		panel.RThrust.Value.Text = "Off"
	end
	end
end

function spoilers()
		if script.Parent.Plane.Value.Animations.Value.Spoilers.Value.Value == true then
		panel.Spoilers.Value.Text = "On"
	else if script.Parent.Plane.Value.Animations.Value.Spoilers.Value.Value == false then
		panel.Spoilers.Value.Text = "Off"
	end
	end
end

function freeze()
	if script.Parent.Plane.Value.MainParts.Main.Anchored == true then
		panel.Frozen.Value.Text = "On"
	else if script.Parent.Plane.Value.MainParts.Main.Anchored == false then
		panel.Frozen.Value.Text = "Off"
	end
	end
end

function engine()
	if script.Parent.Plane.Value.MainParts.Main.Engine.Value == true then
		script.Parent.Panel.Engines.BackgroundColor3 = Color3.new(85/255, 255/255, 0)
		script.Parent.Panel.Engines.BorderColor3 = Color3.new(0, 85/255, 0)
		on = (not on)
	end
end
script.Parent.Plane.Value.Animations.Value.Gears.Value.Changed:connect(gears)
script.Parent.Plane.Value.Animations.Value.Flaps.Value.Changed:connect(flaps)
script.Parent.Plane.Value.Animations.Value.RThrust.Value.Changed:connect(rthrust)
script.Parent.Plane.Value.Animations.Value.Spoilers.Value.Changed:connect(spoilers)
script.Parent.Plane.Value.MainParts.Main.Changed:connect(freeze)
function waitFor(parent,array)	-- Backup system to wait for objects to 'load'
	if (array) then
		for _,name in pairs(array) do
			while (not parent:findFirstChild(name)) do wait() end	-- If the object is found right away, no time will be spent waiting. That's why 'while' loops work better than 'repeat' in this case
		end
	elseif (parent:IsA("ObjectValue")) then
		while (not parent.Value) do wait() end
	end
end

function fixVars()	-- Correct your mistakes to make sure the plane still flies correctly!
	maxBank = (maxBank < -90 and -90 or maxBank > 90 and 90 or maxBank)
	throttleInc = (throttleInc < 0.01 and 0.01 or throttleInc > 1 and 1 or throttleInc)
	stallSpeed = (stallSpeed > maxSpeed and maxSpeed or stallSpeed)
	accel = (accel < 0.01 and 0.01 or accel > maxSpeed and maxSpeed or accel)
	altMax = ((altMax-100) < altMin and (altMin+100) or altMax)
	altMax = (altSet and (altMax+main.Position.y) or altMax)
	altMin = (altSet and (altMin+main.Position.y) or altMin)
	keys.engine.key = (keys.engine.key == "" and "e" or keys.engine.key)
	keys.spdup.byte = (keys.spdup.byte == 0 and 17 or keys.spdup.byte)
	keys.spddwn.byte = (keys.spddwn.byte == 0 and 18 or keys.spddwn.byte)
end

function getVars()	-- Since this plane kit is supposed to make you avoid scripting altogether, I have to go the extra mile and write a messy function to account for all those object variables
	plane = script.Parent.Plane.Value
	waitFor(plane,{"MainParts","OtherParts","EDIT_THESE","Dead"})
	mainParts = plane.MainParts
	info = plane.EDIT_THESE
	waitFor(mainParts,{"Main","MainSeat"})
	main = mainParts.Main
	seat = mainParts.MainSeat
	waitFor(main,{"Move","Gyro"})
	move = main.Move
	gyro = main.Gyro
	accel,canCrash,crashForce,crashSpin,crashVisual,maxBank,maxSpeed,speedVary,stallSpeed,throttleInc,altRestrict,altMin,altMax,altSet =	-- Quickest way to assign tons of variables
		A(info.Acceleration.Value),info.CanCrash.Value,A(info.CanCrash.Force.Value),info.CanCrash.SpinSpeed.Value,info.CanCrash.VisualFX.Value,
		info.MaxBank.Value,A(info.MaxSpeed.Value),A(info.SpeedDifferential.Value),A(info.StallSpeed.Value),A(info.ThrottleIncrease.Value),
		info.AltitudeRestrictions.Value,info.AltitudeRestrictions.MinAltitude.Value,info.AltitudeRestrictions.MaxAltitude.Value,info.AltitudeRestrictions.SetByOrigin.Value
	keys.engine.key = info.Hotkeys.Engine.Value:gmatch("%a")():lower()
	local sU,sD = info.Hotkeys.SpeedUp.Value:lower(),info.Hotkeys.SpeedDown.Value:lower()
	keys.spdup.byte = (sU == "arrowkeyup" and 17 or sU == "arrowkeydown" and 18 or sU:gmatch("%a")():byte())	-- Ternary operations use logical figures to avoid 'if' statements
	keys.spddwn.byte = (sD == "arrowkeyup" and 17 or sD == "arrowkeydown" and 18 or sD:gmatch("%a")():byte())
	fixVars()
	plane.Dead.Changed:connect(function()
		if ((plane.Dead.Value) and (not dead)) then	-- DIE!
			dead,flying,on = true,false,false
			main.Fire.Enabled,main.Smoke.Enabled = info.CanCrash.VisualFX.Value,info.CanCrash.VisualFX.Value
			move.maxForce = Vector3.new(0,0,0)
			gyro.D = 1e3
			while (not plane.Dead.Stop.Value) do
				gyro.cframe = (gyro.cframe*CFrame.Angles(0,0,math.rad(crashSpin)))
				wait()
			end
		end
	end)
end


function getLowestPoint()	-- Plane will use LowestPoint to determine where to look to make sure the plane is either flying or on the ground
	if (#gearParts == 0) then
		lowestPoint = (main.Position.y+5+(main.Size.y/2))
		return
	end
	for _,v in pairs(gearParts) do	-- Not very efficient, but it basically does what I designed it to do:
		local _0 = (main.Position.y-(v.CFrame*CFrame.new((v.Size.x/2),0,0)).y)
		local _1 = (main.Position.y-(v.CFrame*CFrame.new(-(v.Size.x/2),0,0)).y)
		local _2 = (main.Position.y-(v.CFrame*CFrame.new(0,(v.Size.y/2),0)).y)
		local _3 = (main.Position.y-(v.CFrame*CFrame.new(0,-(v.Size.y/2),0)).y)
		local _4 = (main.Position.y-(v.CFrame*CFrame.new(0,0,(v.Size.z/2))).y)
		local _5 = (main.Position.y-(v.CFrame*CFrame.new(0,0,-(v.Size.z/2))).y)
		local n = (math.max(_0,_1,_2,_3,_4,_5)+5)
		lowestPoint = (n > lowestPoint and n or lowestPoint)
	end
end

function guiSetup()	-- Setting up the GUI buttons and such
	local cur = 0
	gears()
	flaps()
	rthrust()
	spoilers()
	freeze()
	engine()
end

getVars()
getLowestPoint()
guiSetup()
script.Parent.Name = "Plane"


function updateGui(taxiing,stalling)
	panel.Title.Text = info.PlaneName.Value
	panel.StallSpeed.Value.Text = info.StallSpeed.Value
	panel.Stall.Visible = (not taxiing and stalling)
	if ((realSpeed > -10000) and (realSpeed < 10000)) then
		panel.Speed.Value.Text = tostring(math.floor(realSpeed+0.5))
		main.Speed.Value = tostring(math.floor(realSpeed+0.5))
	end
	panel.Altitude.Value.Text = tostring(math.floor(main.Position.y+0.5))
	main.Altitude.Value = tostring(math.floor(main.Position.y+0.5))
	panel.Throttle.Bar.Amount.Size = UDim2.new(throttle,0,1,0)
end

function taxi()	-- Check to see if the plane is on the ground or not
	return (currentSpeed <= stallSpeed and game.Workspace:findPartOnRay(Ray.new(main.Position,Vector3.new(0,-lowestPoint,0)),plane))	-- Make sure plane is on a surface
end

function stall()	-- Originally set as a giant ternary operation, but got WAY too complex, so I decided to break it down for my own sanity
	if ((altRestrict) and (main.Position.y > altMax)) then return true end
	local diff = ((realSpeed-stallSpeed)/200)
	diff = (diff > 0.9 and 0.9 or diff)
	local check = {	-- Table placed here so I could easily add new 'checks' at ease. If anything in this table is 'true,' then the plane will be considered to be taxiing
		(currentSpeed <= stallSpeed);
		(main.CFrame.lookVector.y > (realSpeed < stallSpeed and -1 or -diff));
	}
	for _,c in pairs(check) do
		if (not c) then return false end
	end
	return true
end

function fly(m)	-- Main function that controls all of the flying stuff. Very messy.
	flying = true
	local pos,t = main.Position,time()
	local lastStall = false
	while ((flying) and (not dead)) do
		realSpeed = ((pos-main.Position).magnitude/(time()-t))	-- Calculate "real" speed
		pos,t = main.Position,time()
		local max = (maxSpeed+(-main.CFrame.lookVector.y*speedVary))	-- Speed variety based on the pitch of the aircraft
		desiredSpeed = (max*(on and throttle or 0))	-- Find speed based on throttle
		local change = (desiredSpeed > currentSpeed and 1 or -1)	-- Decide between accelerating or decelerating
		currentSpeed = (currentSpeed+(accel*change))	-- Calculate new speed