local J=script.Parent.Parent.Parent local M,o,G,A,R,w,d,K,I,h,B,Q,m,D,g,y,O,L,n,u,c,q,s local t,a,Z=0,0,0 local P local i={}local b,l,N,E,x,C,T,z=false,false,false,false,true,0,0,false local Y=script.Parent local F=Y.Panel local v=1 local p=0 local H=math.abs local r={engine={key};landing={key};spdup={byte=0;down=false},spddwn={byte=0,down=false}}function waitFor(J,M)if M then for M,o in pairs(M)do while not J:findFirstChild(o)do wait()end end elseif J:IsA("\079\098\106\101\099\116\086\097\108\117\101")then while not J.Value do wait()end end end function fixVars()g=g<-90 and-90 or g>90 and 90 or g n=n<.01 and.01 or n>1 and 1 or n L=L>y and y or L h=h<.01 and.01 or h>y and y or h q=q-100<c and c+100 or q q=s and q+R.Position.y or q c=s and c+R.Position.y or c r.engine.key=r.engine.key==""and"\101"or r.engine.key r.spdup.byte=r.spdup.byte==0 and 17 or r.spdup.byte r.spddwn.byte=r.spddwn.byte==0 and 18 or r.spddwn.byte r.landing.key=r.landing.key==""and"\103"or r.landing.key end function getVars()M=script.Parent.Plane.Value waitFor(M,{"\077\097\105\110\080\097\114\116\115";"\079\116\104\101\114\080\097\114\116\115","\069\068\073\084\095\084\072\069\083\069";"\068\101\097\100"})o,A=M.MainParts,M.EDIT_THESE waitFor(M.Parent,{"\065\110\105\109\097\116\105\111\110\080\097\114\116\115"})G=M.Parent.AnimationParts waitFor(o,{"\077\097\105\110","\077\097\105\110\083\101\097\116";"\076\097\110\100\105\110\103\071\101\097\114"})R,K,I=o.Main,o.MainSeat,o.LandingGear waitFor(R,{"\077\111\118\101";"\071\121\114\111"})w,gyro=R.Move,R.Gyro h,B,Q,m,D,g,y,O,L,n,u,c,q,s=H(A.Acceleration.Value),A.CanCrash.Value,H(A.CanCrash.Force.Value),A.CanCrash.SpinSpeed.Value,A.CanCrash.VisualFX.Value,A.MaxBank.Value,H(A.MaxSpeed.Value),H(A.SpeedDifferential.Value),H(A.StallSpeed.Value),H(A.ThrottleIncrease.Value),A.AltitudeRestrictions.Value,A.AltitudeRestrictions.MinAltitude.Value,A.AltitudeRestrictions.MaxAltitude.Value,A.AltitudeRestrictions.SetByOrigin.Value r.engine.key=((A.Hotkeys.Engine.Value:gmatch("\037\097"))()):lower()r.landing.key=((A.Hotkeys.Gear.Value:gmatch("\037\097"))()):lower()local J,d=A.Hotkeys.SpeedUp.Value:lower(),A.Hotkeys.SpeedDown.Value:lower()r.spdup.byte=J=="\097\114\114\111\119\107\101\121\117\112"and 17 or J=="\097\114\114\111\119\107\101\121\100\111\119\110"and 18 or((J:gmatch("\037\097"))()):byte()r.spddwn.byte=d=="\097\114\114\111\119\107\101\121\117\112"and 17 or d=="\097\114\114\111\119\107\101\121\100\111\119\110"and 18 or((d:gmatch("\037\097"))()):byte()fixVars()M.Dead.Changed:connect(function()if M.Dead.Value and not E then E,l,N=true,false,false R.Fire.Enabled,R.Smoke.Enabled=A.CanCrash.VisualFX.Value,A.CanCrash.VisualFX.Value w.maxForce=Vector3.new(0,0,0)gyro.D=1000.0 while b and not M.Dead.Stop.Value do gyro.cframe=gyro.cframe*CFrame.Angles(0,0,math.rad(m))wait()end end end)end function getGear(J)for J,M in pairs(J:GetChildren())do if M:IsA("\066\097\115\101\080\097\114\116")then local J,o,G=Instance.new("\078\117\109\098\101\114\086\097\108\117\101",M),Instance.new("\078\117\109\098\101\114\086\097\108\117\101",M),Instance.new("\066\111\111\108\086\097\108\117\101",M)J.Name,o.Name,G.Name="\084\114\097\110\115","\082\101\102","\067\111\108\108\105\100\101"J.Value,o.Value,G.Value=M.Transparency,M.Reflectance,M.CanCollide table.insert(i,M)end getGear(M)end end function getLowestPoint()if#i==0 then p=(R.Position.y+5)+R.Size.y/2 return end for J,M in pairs(i)do local o=R.Position.y-(M.CFrame*CFrame.new(M.Size.x/2,0,0)).y local G=R.Position.y-(M.CFrame*CFrame.new(-(M.Size.x/2),0,0)).y local A=R.Position.y-(M.CFrame*CFrame.new(0,M.Size.y/2,0)).y local w=R.Position.y-(M.CFrame*CFrame.new(0,-(M.Size.y/2),0)).y local d=R.Position.y-(M.CFrame*CFrame.new(0,0,M.Size.z/2)).y local K=R.Position.y-(M.CFrame*CFrame.new(0,0,-(M.Size.z/2))).y local I=math.max(o,G,A,w,d,K)+5 p=I>p and I or p end end function guiSetup()local J=0 F.Controls.Position=UDim2.new(0,0,0,-(F.Controls.AbsolutePosition.y+F.Controls.AbsoluteSize.y))F.ControlsButton.MouseButton1Click:connect(function()J=J==0 and 1 or 0 if J==0 then F.Controls:TweenPosition(UDim2.new(0,0,0,-(F.Controls.AbsolutePosition.y+F.Controls.AbsoluteSize.y)),"\073\110","\083\105\110\101",.35)else F.Controls.Visible=true F.Controls:TweenPosition(UDim2.new(0,0,1.5,0),"\079\117\116","\066\097\099\107",.5)end end)F.Controls.C1.Text=r.engine.key:upper().." \075\101\121"F.Controls.C2.Text=r.spdup.byte==17 and"\085\080 \065\114\114\111\119 \075\101\121"or r.spdup.byte==18 and"\068\079\087\078 \065\114\114\111\119 \075\101\121"or(string.char(r.spdup.byte)):upper().." \075\101\121"F.Controls.C3.Text=r.spddwn.byte==17 and"\085\080 \065\114\114\111\119 \075\101\121"or r.spddwn.byte==18 and"\068\079\087\078 \065\114\114\111\119 \075\101\121"or(string.char(r.spddwn.byte)):upper().." \075\101\121"F.Controls.C4.Text=r.landing.key:upper().." \075\101\121"end waitFor(script.Parent,{"\080\108\097\110\101";"\068\101\115\101\108\101\099\116\048","\068\101\115\101\108\101\099\116\049","\067\117\114\114\101\110\116\083\101\108\101\099\116"})waitFor(script.Parent.Plane)getVars()getGear(I)getLowestPoint()guiSetup()
-- 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()
