-- Welcome to the variable museum:
local player = script.Parent.Parent.Parent
local plane,mainParts,animationParts,info,main,move,gryo,seat,landingGear,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 selected,flying,on,dead,gear,throttle,feThrottle,cruising = false,false,false,false,true,0,0,false
local gui = script.Parent
local panel = gui.Panel
local camtype = 1
local lowestPoint = 0
local A = math.abs	-- Creating a shortcut for the function

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

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)
	keys.landing.key = (keys.landing.key == "" and "g" or keys.landing.key)
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,info = plane.MainParts,plane.EDIT_THESE
	waitFor(plane.Parent,{"AnimationParts"})
	animationParts = plane.Parent.AnimationParts
	waitFor(mainParts,{"Main","MainSeat","LandingGear"})
	main,seat,landingGear = mainParts.Main,mainParts.MainSeat,mainParts.LandingGear
	waitFor(main,{"Move","Gyro"})
	move,gyro = main.Move,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()
	keys.landing.key = info.Hotkeys.Gear.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 ((selected) and (not plane.Dead.Stop.Value)) do
				gyro.cframe = (gyro.cframe*CFrame.Angles(0,0,math.rad(crashSpin)))
				wait()
			end
		end
	end)
end

function getGear(parent)	-- Very common way to scan through every descendant of a model:
	for _,v in pairs(parent:GetChildren()) do
		if (v:IsA("BasePart")) then
			local t,r,c = Instance.new("NumberValue",v),Instance.new("NumberValue",v),Instance.new("BoolValue",v)	-- Saving original properties
			t.Name,r.Name,c.Name = "Trans","Ref","Collide"
			t.Value,r.Value,c.Value = v.Transparency,v.Reflectance,v.CanCollide
			table.insert(gearParts,v)
		end
		getGear(v)
	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
	panel.Controls.Position = UDim2.new(0,0,0,-(panel.Controls.AbsolutePosition.y+panel.Controls.AbsoluteSize.y))
	panel.ControlsButton.MouseButton1Click:connect(function()
		cur = (cur == 0 and 1 or 0)
		if (cur == 0) then
			panel.Controls:TweenPosition(UDim2.new(0,0,0,-(panel.Controls.AbsolutePosition.y+panel.Controls.AbsoluteSize.y)),"In","Sine",0.35)
		else
			panel.Controls.Visible = true
			panel.Controls:TweenPosition(UDim2.new(0,0,1.5,0),"Out","Back",0.5)
		end
	end)
	panel.Controls.C1.Text = (keys.engine.key:upper() .. " Key")
	panel.Controls.C2.Text = (keys.spdup.byte == 17 and "UP Arrow Key" or keys.spdup.byte == 18 and "DOWN Arrow Key" or (string.char(keys.spdup.byte):upper() .. " Key"))
	panel.Controls.C3.Text = (keys.spddwn.byte == 17 and "UP Arrow Key" or keys.spddwn.byte == 18 and "DOWN Arrow Key" or (string.char(keys.spddwn.byte):upper() .. " Key"))
	panel.Controls.C4.Text = (keys.landing.key:upper() .. " Key")
end

waitFor(script.Parent,{"Plane","Deselect0","Deselect1","CurrentSelect"})
waitFor(script.Parent.Plane)
getVars()
getGear(landingGear)
getLowestPoint()
guiSetup()