AutopilotPTFS
Key Required

AutopilotPTFS

Pilot Training Flight Simulatortugozdentugozden 2.1K views 1.4K executesUpdated Feb 25, 2026
5.0/ 5(1 votes)
10100% Positive
Lua Script•1307 lines (50949 chars)
-- ╔══════════════════════════════════════════════════════╗
-- ║       PTFS AUTOPILOT v15 — FULL REWRITE              ║
-- ║   Custom UI • Kokpit HUD • AP Menü Ayrı              ║
-- ╚══════════════════════════════════════════════════════╝

local Players    = game:GetService("Players")
local RunService = game:GetService("RunService")
local UIS        = game:GetService("UserInputService")
local TweenS     = game:GetService("TweenService")
local Player     = Players.LocalPlayer
local PlayerGui  = Player:WaitForChild("PlayerGui")

-- Eski gui temizle
for _, name in ipairs({"PTFSMain","PTFSCockpit"}) do
    if PlayerGui:FindFirstChild(name) then PlayerGui[name]:Destroy() end
end

-- ════════════════════════════════════════════════════════
-- KOORDİNATLAR
-- ════════════════════════════════════════════════════════
local Airports = {
    { name="Greater Rockford", runways={
        { name="07R", threshold=Vector3.new(-3481.3,3.3,20699.9),  heading=292.9 },
        { name="25L", threshold=Vector3.new(-2726.9,3.3,20379.7),  heading=112.9 },
    }},
    { name="Saint Barthelemy", runways={
        { name="09",  threshold=Vector3.new(5470.4,11.5,-4485.6),  heading=1.5   },
        { name="27",  threshold=Vector3.new(5950.7,3.1,-4485.8),   heading=256.5 },
    }},
    { name="Tokyo International", runways={
        { name="20",  threshold=Vector3.new(-6921.5,21.5,-30891.2),heading=340.1 },
        { name="02",  threshold=Vector3.new(-6399.0,21.5,-32326.8),heading=160.2 },
    }},
}

-- ════════════════════════════════════════════════════════
-- AUTOPILOT STATE
-- ════════════════════════════════════════════════════════
local AP = {
    active      = false,
    phase       = "IDLE",
    depRunway   = nil,
    arrRunway   = nil,
    aircraft    = nil,
    rootPart    = nil,
    CRUISE_ALT  = 2000,
    CRUISE_SPD  = 350,
    TAKEOFF_SPD = 280,
    LANDING_SPD = 110,
    CLIMB_PITCH = 15,
}
local waypoints, wpIndex = {}, 1
local bvel, bgyro = nil, nil
local spd = 0
local hbConn = nil

-- ════════════════════════════════════════════════════════
-- YARDIMCILAR
-- ════════════════════════════════════════════════════════
local function headingDir(deg)
    local r = math.rad(deg)
    return Vector3.new(math.sin(r), 0, math.cos(r))
end
local function dist2D(a, b)
    return Vector3.new(a.X-b.X, 0, a.Z-b.Z).Magnitude
end
local function pressKey(key)
    pcall(function()
        local VIM = game:GetService("VirtualInputManager")
        VIM:SendKeyEvent(true,  Enum.KeyCode[key], false, game)
        task.wait(0.1)
        VIM:SendKeyEvent(false, Enum.KeyCode[key], false, game)
    end)
end
local function fly(vel, lookDir)
    if bvel  and bvel.Parent  then bvel.Velocity  = vel end
    if bgyro and bgyro.Parent then
        if lookDir.Magnitude > 0.001 then
            bgyro.CFrame = CFrame.new(Vector3.new(), lookDir.Unit)
        end
    end
end
local function getAlt() return AP.rootPart and AP.rootPart.Position.Y or 0 end

-- ════════════════════════════════════════════════════════
-- WAYPOINT & AP MANTIK
-- ════════════════════════════════════════════════════════
local function buildWaypoints()
    waypoints = {}
    wpIndex   = 1
    local dep = AP.depRunway
    local arr = AP.arrRunway
    local alt = AP.CRUISE_ALT
    local depDir = headingDir(dep.heading)
    local arrDir = headingDir(arr.heading)

    local climbOut    = dep.threshold + depDir * 3000
    climbOut          = Vector3.new(climbOut.X, alt, climbOut.Z)
    local finalLeg    = arr.threshold - arrDir * 12000
    finalLeg          = Vector3.new(finalLeg.X, alt, finalLeg.Z)
    local descentStart= arr.threshold - arrDir * 5000
    descentStart      = Vector3.new(descentStart.X, alt, descentStart.Z)

    table.insert(waypoints, { pos=climbOut,     onArrive=function() spd=AP.CRUISE_SPD end })
    table.insert(waypoints, { pos=finalLeg,     onArrive=function() end })
    table.insert(waypoints, { pos=descentStart, onArrive=function()
        AP.phase = "APPROACH"; spd = AP.LANDING_SPD
        task.spawn(function() pressKey("F"); task.wait(0.3); pressKey("G") end)
    end})
end

local function stopAP(reason)
    AP.active = false; AP.phase = "IDLE"
    if hbConn then hbConn:Disconnect(); hbConn = nil end
    if bvel  and bvel.Parent  then bvel:Destroy();  bvel  = nil end
    if bgyro and bgyro.Parent then bgyro:Destroy(); bgyro = nil end
    print("⏹️ AP: " .. (reason or "Manuel"))
end

local function startLoop()
    spd = 0; wpIndex = 1
    if hbConn then hbConn:Disconnect() end
    hbConn = RunService.Heartbeat:Connect(function(dt)
        if not AP.active then hbConn:Disconnect(); hbConn = nil; return end
        if not AP.rootPart or not AP.rootPart.Parent then
            local char = Player.Character
            if char then
                local hum = char:FindFirstChildOfClass("Humanoid")
                if hum and hum.SeatPart then
                    AP.aircraft = hum.SeatPart.Parent
                    AP.rootPart = AP.aircraft:FindFirstChild("Body")
                        or AP.aircraft:FindFirstChild("Fuselage")
                        or AP.aircraft:FindFirstChild("Main")
                        or hum.SeatPart
                end
            end
            return
        end
        if not bvel or not bvel.Parent then
            pcall(function()
                bvel = Instance.new("BodyVelocity")
                bvel.MaxForce = Vector3.new(1e9,1e9,1e9)
                bvel.Velocity = Vector3.new(0,0,0)
                bvel.P = 1e5
                bvel.Parent = AP.rootPart
            end); return
        end
        if not bgyro or not bgyro.Parent then
            pcall(function()
                bgyro = Instance.new("BodyGyro")
                bgyro.MaxTorque = Vector3.new(1e9,1e9,1e9)
                bgyro.P = 5e4; bgyro.D = 3000
                bgyro.CFrame = AP.rootPart.CFrame
                bgyro.Parent = AP.rootPart
            end); return
        end

        local pos    = AP.rootPart.Position
        local alt    = getAlt()
        local arrThr = AP.arrRunway.threshold
        local arrDir = headingDir(AP.arrRunway.heading)
        local depDir = headingDir(AP.depRunway.heading)

        if AP.phase == "TAKEOFF" then
            spd = math.min(spd + math.max(AP.TAKEOFF_SPD*0.5,300)*dt, AP.TAKEOFF_SPD)
            local pitchY  = math.tan(math.rad(AP.CLIMB_PITCH))
            local moveDir = (depDir + Vector3.new(0,pitchY,0)).Unit
            fly(moveDir*spd, moveDir)
            if alt >= AP.CRUISE_ALT then
                AP.phase = "CRUISE"; spd = AP.CRUISE_SPD
                task.spawn(function() pressKey("G") end)
            end

        elseif AP.phase == "CRUISE" then
            if wpIndex > #waypoints then AP.phase="APPROACH"; spd=AP.LANDING_SPD; return end
            local wp = waypoints[wpIndex]
            local d2D = dist2D(pos, wp.pos)
            local toTarget = Vector3.new(wp.pos.X-pos.X, 0, wp.pos.Z-pos.Z)
            local hDir = d2D > 1 and toTarget.Unit or arrDir
            local altErr = wp.pos.Y - alt
            local yComp  = math.clamp(altErr*0.003, -0.1, 0.1)
            fly((hDir + Vector3.new(0,yComp,0)).Unit * AP.CRUISE_SPD, hDir)
            if d2D < 800 then
                wp.onArrive(); wpIndex = wpIndex + 1
            end

        elseif AP.phase == "APPROACH" then
            spd = AP.LANDING_SPD
            local toThr       = Vector3.new(arrThr.X-pos.X, 0, arrThr.Z-pos.Z)
            local hDir        = toThr.Magnitude > 1 and toThr.Unit or arrDir
            local heightAbove = alt - arrThr.Y
            local dThresh     = dist2D(pos, arrThr)
            local sinkY = dThresh > 5 and -math.clamp(heightAbove/dThresh, 0, 0.5) or 0
            if heightAbove < 80 then sinkY = math.max(sinkY,-0.08) end
            if heightAbove < 30 then sinkY = math.max(sinkY,-0.04) end
            if heightAbove < 10 then sinkY = math.max(sinkY,-0.01) end
            if heightAbove < 3  then sinkY = 0 end
            fly((hDir + Vector3.new(0,sinkY,0)).Unit * spd, hDir)
            if heightAbove <= 3 or (dThresh < 15 and heightAbove < 15) then
                AP.phase = "ROLLOUT"
                task.spawn(function() pressKey("R") end)
            end

        elseif AP.phase == "ROLLOUT" then
            local toThr = Vector3.new(arrThr.X-pos.X, 0, arrThr.Z-pos.Z)
            local hDir  = toThr.Magnitude > 10 and toThr.Unit or arrDir
            spd = math.max(spd - math.max(AP.LANDING_SPD*0.5,200)*dt, 0)
            fly(hDir*spd, hDir)
            if spd <= 2 then
                fly(Vector3.new(0,0,0), hDir)
                task.spawn(function() pressKey("R"); task.wait(0.2); pressKey("X") end)
                stopAP("Uçuş tamamlandı")
            end
        end
    end)
end

local function startAP()
    if AP.active        then return false, "Zaten aktif!"       end
    if not AP.depRunway then return false, "Kalkış pisti seç!"  end
    if not AP.arrRunway then return false, "İniş pisti seç!"    end
    local char = Player.Character
    if not char then return false, "Karakter yok!" end
    local hum = char:FindFirstChildOfClass("Humanoid")
    if not hum or not hum.SeatPart then return false, "Uçağa otur!" end

    local ac   = hum.SeatPart.Parent
    local root = ac:FindFirstChild("Body") or ac:FindFirstChild("Fuselage")
               or ac:FindFirstChild("Main") or hum.SeatPart
    AP.aircraft = ac; AP.rootPart = root
    AP.active = true; AP.phase = "TAKEOFF"

    if bvel  and bvel.Parent  then bvel:Destroy()  end
    if bgyro and bgyro.Parent then bgyro:Destroy() end
    bvel = Instance.new("BodyVelocity")
    bvel.MaxForce = Vector3.new(1e9,1e9,1e9); bvel.Velocity = Vector3.new(0,0,0); bvel.P = 1e5
    bvel.Parent = root
    bgyro = Instance.new("BodyGyro")
    bgyro.MaxTorque = Vector3.new(1e9,1e9,1e9); bgyro.P = 5e4; bgyro.D = 3000
    bgyro.CFrame = root.CFrame; bgyro.Parent = root

    local depDir   = headingDir(AP.depRunway.heading)
    local spawnPos = AP.depRunway.threshold - depDir * 80
    spawnPos = Vector3.new(spawnPos.X, AP.depRunway.threshold.Y+3, spawnPos.Z)
    local ok = pcall(function() ac:PivotTo(CFrame.new(spawnPos, spawnPos+depDir)) end)
    if not ok then
        local offset = spawnPos - root.Position
        for _, p in pairs(ac:GetDescendants()) do
            if p:IsA("BasePart") then p.CFrame = p.CFrame + offset end
        end
    end

    buildWaypoints()
    task.wait(0.5)
    task.spawn(function() pressKey("E"); task.wait(0.3); pressKey("F") end)
    task.wait(0.6)
    startLoop()
    return true, "Uçuş başladı! 🛫"
end

Player.CharacterAdded:Connect(function()
    if AP.active then stopAP("Karakter sıfırlandı") end
end)

-- ════════════════════════════════════════════════════════
-- UI RENK PALETİ
-- ════════════════════════════════════════════════════════
local C = {
    bg       = Color3.fromRGB(8,  12, 18),
    panel    = Color3.fromRGB(12, 18, 28),
    panelB   = Color3.fromRGB(16, 24, 38),
    border   = Color3.fromRGB(35, 55, 80),
    borderHi = Color3.fromRGB(60, 120, 200),
    green    = Color3.fromRGB(0,  230, 130),
    greenD   = Color3.fromRGB(0,  140, 70),
    cyan     = Color3.fromRGB(40, 200, 255),
    cyanD    = Color3.fromRGB(0,  100, 160),
    amber    = Color3.fromRGB(255, 190, 0),
    amberD   = Color3.fromRGB(140, 90, 0),
    red      = Color3.fromRGB(255, 65, 65),
    redD     = Color3.fromRGB(120, 20, 20),
    white    = Color3.fromRGB(220, 232, 245),
    dim      = Color3.fromRGB(90, 110, 135),
    sky      = Color3.fromRGB(30, 90, 180),
    skyHi    = Color3.fromRGB(60, 140, 255),
    ground   = Color3.fromRGB(110, 70, 30),
    groundHi = Color3.fromRGB(160, 110, 50),
    magenta  = Color3.fromRGB(200, 60, 200),
}

-- ════════════════════════════════════════════════════════
-- UI BUILDER ARAÇLARI
-- ════════════════════════════════════════════════════════
local function F(parent, sx, sy, ox, oy, px, py, popx, popy, col, transp, zi)
    local f = Instance.new("Frame")
    f.Size = UDim2.new(sx or 0, ox or 0, sy or 0, oy or 0)
    f.Position = UDim2.new(px or 0, popx or 0, py or 0, popy or 0)
    -- col güvenlik: sadece Color3 kabul et
    if typeof(col) == "Color3" then
        f.BackgroundColor3 = col
    else
        f.BackgroundColor3 = C.panel
    end
    f.BackgroundTransparency = (typeof(transp) == "number") and transp or 0
    f.BorderSizePixel = 0
    f.ZIndex = (typeof(zi) == "number") and zi or 1
    f.Parent = parent
    return f
end

local function L(parent, text, ts, col, font, xa, sx, sy, ox, oy, px, py, popx, popy, zi)
    local l = Instance.new("TextLabel")
    l.Text = text or ""
    l.TextSize = ts or 12
    l.TextColor3 = col or C.white
    l.Font = font or Enum.Font.Code
    l.BackgroundTransparency = 1
    l.TextXAlignment = xa or Enum.TextXAlignment.Center
    l.Size = UDim2.new(sx or 1, ox or 0, sy or 1, oy or 0)
    l.Position = UDim2.new(px or 0, popx or 0, py or 0, popy or 0)
    l.ZIndex = zi or 2
    l.Parent = parent
    return l
end

local function Btn(parent, text, ts, col, bgcol, sx, sy, ox, oy, px, py, popx, popy, zi)
    local b = Instance.new("TextButton")
    b.Text = text or ""
    b.TextSize = ts or 12
    b.TextColor3 = col or C.white
    b.Font = Enum.Font.GothamBold
    b.BackgroundColor3 = bgcol or C.panel
    b.BorderSizePixel = 0
    b.AutoButtonColor = false
    b.Size = UDim2.new(sx or 0, ox or 80, sy or 0, oy or 24)
    b.Position = UDim2.new(px or 0, popx or 0, py or 0, popy or 0)
    b.ZIndex = zi or 3
    b.Parent = parent
    return b
end

local function Rnd(inst, r)
    local c = Instance.new("UICorner")
    c.CornerRadius = UDim.new(0, r or 4)
    c.Parent = inst
end

local function Str(inst, col, thick)
    local s = Instance.new("UIStroke")
    s.Color = col or C.border
    s.Thickness = thick or 1
    s.Parent = inst
end

local function makeDraggable(handle, target)
    local drag, ds, sp = false, nil, nil
    handle.InputBegan:Connect(function(i)
        if i.UserInputType == Enum.UserInputType.MouseButton1 then
            drag=true; ds=i.Position; sp=target.Position
        end
    end)
    handle.InputEnded:Connect(function(i)
        if i.UserInputType == Enum.UserInputType.MouseButton1 then drag=false end
    end)
    UIS.InputChanged:Connect(function(i)
        if drag and i.UserInputType == Enum.UserInputType.MouseMovement then
            local d = i.Position - ds
            target.Position = UDim2.new(sp.X.Scale, sp.X.Offset+d.X, sp.Y.Scale, sp.Y.Offset+d.Y)
        end
    end)
end

-- ════════════════════════════════════════════════════════
-- ANA SCREENGUI
-- ════════════════════════════════════════════════════════
local sg = Instance.new("ScreenGui")
sg.Name = "PTFSMain"
sg.ResetOnSpawn = false
sg.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
sg.DisplayOrder = 100
sg.Parent = PlayerGui

-- ════════════════════════════════════════════════════════
-- ════ KOKPIT HUD ══════════════════════════════════════
-- ════════════════════════════════════════════════════════
-- Boyutlar
local HW, HH = 720, 260   -- HUD genişlik/yükseklik
local TH      = 26          -- başlık bar
local SPD_W   = 64          -- hız bant genişliği
local ALT_W   = 72          -- irtifa bant
local PFD_W   = HW - SPD_W - ALT_W - 4  -- 580
local PFD_H   = HH - TH - 2
local ND_W    = 140
local AI_W    = PFD_W - ND_W - 2  -- yapay ufuk genişliği

-- HUD ana çerçeve
local hud = F(sg, 0, 0, HW, HH, 0.5, 1, -HW/2, -HH-8, C.bg, 0, 1)
Rnd(hud, 10)
Str(hud, C.borderHi, 1.5)

-- HUD başlık
local hudTitle = F(hud, 1, 0, 0, TH, 0, 0, 0, 0, Color3.fromRGB(10,16,28), 0, 2)
Rnd(hudTitle, 10)
L(hudTitle, "✈  PTFS  FLIGHT  DECK  v15", 11, C.cyan, Enum.Font.GothamBold)
-- Üst sağ: kapat
local hudClose = Btn(hud, "✕", 11, C.white, C.redD, 0, 0, 22, 20, 1, 0, -26, 3, 5)
Rnd(hudClose, 4)
Str(hudClose, C.red, 1)

-- İçerik konteyneri
local hudC = F(hud, 1, 1, -2, -(TH+2), 0, 0, 1, TH+1, Color3.fromRGB(0,0,0), 1, 1)

-- makeDraggable(hudTitle, hud) -- başlıktan drag

-- ─── SPD BANT (sol) ─────────────────────────────────────
local spdPanel = F(hudC, 0, 1, SPD_W, 0, 0, 0, 0, 0, Color3.fromRGB(10,16,14), 0, 2)
Rnd(spdPanel, 6)
Str(spdPanel, Color3.fromRGB(0,80,50), 1)
L(spdPanel, "SPD", 8, C.greenD, Enum.Font.GothamBold, nil, 1, 0, 0, 14, 0, 0, 0, 0, 3)
L(spdPanel, "KTS", 8, C.greenD, Enum.Font.GothamBold, nil, 1, 0, 0, 14, 1, 0, 0, -14, 3)

-- Hız değer kutusu
local spdBox = F(spdPanel, 1, 0, -8, 30, 0, 0, 4, 0, Color3.fromRGB(0,20,12), 0, 3)
spdBox.Position = UDim2.new(0, 4, 0.5, -15)
Rnd(spdBox, 4)
Str(spdBox, C.green, 1)
local spdVal = L(spdBox, "0", 17, C.green, Enum.Font.Code, nil, 1, 1, 0, 0, 0, 0, 0, 0, 4)

-- Hız ticks
local spdTickContainer = F(spdPanel, 1, 1, 0, -34, 0, 0, 0, 16, Color3.fromRGB(0,0,0), 1, 2)
spdTickContainer.ClipsDescendants = true
for i = 0, 20 do
    local v = i * 50
    local yPct = 1 - (v / 1000)
    local major = v % 100 == 0
    local tick = F(spdTickContainer, 0, 0, major and 18 or 10, 1,
        1, 0, -(major and 18 or 10), 0,
        0, yPct, 0, -1,
        major and Color3.fromRGB(0,180,80) or Color3.fromRGB(0,80,40), 0, 3)
    if major then
        L(spdTickContainer, tostring(v), 8, Color3.fromRGB(0,160,70), Enum.Font.Code,
            Enum.TextXAlignment.Right,
            0, 0, SPD_W-22, 14, 0, yPct, 0, -8, 3)
    end
end

-- Hız pointer (sabit orta çizgi)
local spdPtr = F(spdPanel, 1, 0, -4, 2, 0, 0.5, 2, -1, C.green, 0, 5)

-- ─── ALT BANT (sağ) ─────────────────────────────────────
local altPanel = F(hudC, 0, 1, ALT_W, 0, 1, 0, -ALT_W, 0, Color3.fromRGB(10,12,22), 0, 2)
Rnd(altPanel, 6)
Str(altPanel, Color3.fromRGB(30,50,120), 1)
L(altPanel, "ALT", 8, C.cyanD, Enum.Font.GothamBold, nil, 1, 0, 0, 14, 0, 0, 0, 0, 3)
L(altPanel, "STD", 8, C.cyanD, Enum.Font.GothamBold, nil, 1, 0, 0, 14, 1, 0, 0, -14, 3)

local altBox = F(altPanel, 1, 0, -8, 30, 0, 0, 4, 0, Color3.fromRGB(5,8,28), 0, 3)
altBox.Position = UDim2.new(0, 4, 0.5, -15)
Rnd(altBox, 4)
Str(altBox, C.cyan, 1)
local altVal = L(altBox, "0", 15, C.cyan, Enum.Font.Code, nil, 1, 1, 0, 0, 0, 0, 0, 0, 4)

-- Alt ticks
local altTickContainer = F(altPanel, 1, 1, 0, -34, 0, 0, 0, 16, Color3.fromRGB(0,0,0), 1, 2)
altTickContainer.ClipsDescendants = true
for i = 0, 16 do
    local v = i * 500
    local yPct = 1 - (v / 8000)
    local major = true
    F(altTickContainer, 0, 0, 14, 1, 0, yPct, 0, -1, Color3.fromRGB(40,80,180), 0, 3)
    L(altTickContainer, tostring(v), 8, Color3.fromRGB(40,100,200), Enum.Font.Code,
        Enum.TextXAlignment.Left,
        0, 0, ALT_W-20, 14, 0, yPct, 16, -8, 3)
end
local altPtr = F(altPanel, 1, 0, -4, 2, 0, 0.5, 2, -1, C.cyan, 0, 5)

-- ─── PFD ORTA ALAN ──────────────────────────────────────
local pfdArea = F(hudC, 0, 1, PFD_W, 0, 0, 0, SPD_W+2, 0, Color3.fromRGB(0,0,0), 1, 1)

-- YAPAY UFUK (AI)
local aiW = AI_W
local ai = F(pfdArea, 0, 1, aiW, 0, 0, 0, 0, 0, C.sky, 0, 2)
Rnd(ai, 6)
ai.ClipsDescendants = true

-- Gökyüzü gradyan effect
local skyTop = F(ai, 1, 0.6, 0, 0, 0, 0, 0, 0, Color3.fromRGB(20, 70, 160), 0, 2)
local skyBot = F(ai, 1, 0.5, 0, 0, 0, 0.5, 0, 0, Color3.fromRGB(50, 120, 220), 0, 2)

-- Zemin
local gndTop = F(ai, 1, 0.5, 0, 0, 0, 0.5, 0, 0, Color3.fromRGB(130, 85, 35), 0, 2)
local gndBot = F(ai, 1, 0.5, 0, 0, 0, 1, 0, 0, Color3.fromRGB(90, 55, 20), 0, 2)
gndBot.AnchorPoint = Vector2.new(0, 1)

-- Ufuk çizgisi (hareketli)
local horizLine = F(ai, 1, 0, 0, 2, 0, 0.5, 0, -1, C.amber, 0, 5)

-- Pitch tikleri
for _, deg in ipairs({-15,-10,-5,5,10,15}) do
    local yBase = 0.5 - (deg * 0.018)
    local w = math.abs(deg) >= 10 and 0.42 or 0.28
    local pl = F(ai, w, 0, 0, 1, (1-w)/2, yBase, 0, 0, Color3.fromRGB(220,220,200), 0.3, 4)
    L(ai, string.format("%+d",deg), 8, Color3.fromRGB(220,220,180), Enum.Font.Code,
        Enum.TextXAlignment.Right,
        0, 0, (1-w)/2*aiW - 4, 12,
        0, yBase, 0, -6, 4)
    L(ai, string.format("%+d",deg), 8, Color3.fromRGB(220,220,180), Enum.Font.Code,
        Enum.TextXAlignment.Left,
        0, 0, (1-w)/2*aiW - 4, 12,
        (1+w)/2, yBase, 4, -6, 4)
end

-- Uçak sembolü (sabit merkez)
-- Sol kanat
local wL = F(ai, 0, 0, 44, 3, 0.5, 0.5, -52, -1, Color3.fromRGB(255,210,0), 0, 6)
-- Sağ kanat
local wR = F(ai, 0, 0, 44, 3, 0.5, 0.5, 8, -1, Color3.fromRGB(255,210,0), 0, 6)
-- Center dot
local wDot = F(ai, 0, 0, 8, 8, 0.5, 0.5, -4, -4, Color3.fromRGB(255,210,0), 0, 6)
Rnd(wDot, 4)
-- Tail
local wTail = F(ai, 0, 0, 3, 14, 0.5, 0.5, -1, -14, Color3.fromRGB(255,210,0), 0, 6)

-- Flight path vector (FPV) — küçük daire
local fpvFrame = F(ai, 0, 0, 18, 18, 0.5, 0.5, -9, -9, Color3.fromRGB(0,0,0), 1, 7)
local fpvCircle = F(fpvFrame, 1, 1, 0, 0, 0, 0, 0, 0, Color3.fromRGB(0,0,0), 1, 7)
Str(fpvCircle, C.green, 1.5)
Rnd(fpvCircle, 9)
-- FPV çizgiler
F(fpvFrame, 0, 0, 8, 1, 1, 0.5, 1, 0, Color3.fromRGB(0,230,120), 0, 8)
F(fpvFrame, 0, 0, 8, 1, 0, 0.5, -9, 0, Color3.fromRGB(0,230,120), 0, 8)
F(fpvFrame, 0, 0, 1, 8, 0.5, 0, 0, -9, Color3.fromRGB(0,230,120), 0, 8)

-- Heading bar (AI'nin altı)
local hdgStripH = 28
local hdgStrip = F(pfdArea, 0, 0, aiW, hdgStripH, 0, 1, 0, -hdgStripH, Color3.fromRGB(10,14,24), 0, 3)
Str(hdgStrip, C.border, 1)
Rnd(hdgStrip, 4)
-- HDG tick'ler (sabit, 10°ler)
for i = 0, 35 do
    local deg = i * 10
    local xPct = deg / 360
    local major = deg % 30 == 0
    F(hdgStrip, 0, 0, 1, major and 10 or 6,
        xPct, 0, 0, 0,
        major and Color3.fromRGB(60,110,180) or Color3.fromRGB(30,55,90), 0, 4)
    if major then
        local dirs = {[0]="N",[90]="E",[180]="S",[270]="W"}
        local txt = dirs[deg] or tostring(deg)
        L(hdgStrip, txt, 8, Color3.fromRGB(60,120,200), Enum.Font.Code,
            nil, 0, 0, 20, 12, xPct, 0, -10, 10, 4)
    end
end
-- HDG pointer (hareketli üçgen)
local hdgPtr = F(hdgStrip, 0, 0, 2, 16, 0.5, 0, -1, 0, C.cyan, 0, 5)
-- HDG readout
local hdgReadBox = F(hdgStrip, 0, 1, 54, -4, 0.5, 0, -27, 2, Color3.fromRGB(5,10,30), 0, 5)
Rnd(hdgReadBox, 3)
Str(hdgReadBox, C.cyan, 1)
local hdgRead = L(hdgReadBox, "000°", 13, C.cyan, Enum.Font.Code, nil, 1, 1, 0, 0, 0, 0, 0, 0, 6)

-- Pitch/VS şeridi (AI üstü)
local topStrip = F(pfdArea, 0, 0, aiW, 22, 0, 0, 0, 0, Color3.fromRGB(8,12,20), 0, 3)
Rnd(topStrip, 4)
local pitchRead = L(topStrip, "PTH +0.0°", 10, C.amber, Enum.Font.Code,
    Enum.TextXAlignment.Left, 0, 1, 0, 0, 0, 0, 6, 0, 4)
local vsRead    = L(topStrip, "V/S +0", 10, C.green, Enum.Font.Code,
    Enum.TextXAlignment.Right, 0, 1, 0, 0, 0, 0, -6, 0, 4)

-- ─── ND (NAV DISPLAY) sağ yarı ──────────────────────────
local ndX = aiW + 2
local nd  = F(pfdArea, 0, 1, ND_W, 0, 0, 0, ndX, 0, Color3.fromRGB(8,10,20), 0, 2)
Rnd(nd, 6)
Str(nd, C.border, 1)

L(nd, "NAV", 9, C.cyan, Enum.Font.GothamBold, nil, 1, 0, 0, 18, 0, 0, 0, 0, 3)

-- Compass rose
local compassR = 52
local compassCX = ND_W / 2
local compassCY = 32 + compassR + 4
local compassBG = F(nd, 0, 0, compassR*2, compassR*2, 0.5, 0, -compassR, 22, Color3.fromRGB(8,12,24), 0, 3)
Rnd(compassBG, compassR)
Str(compassBG, Color3.fromRGB(40,65,100), 1)

-- Compass glow ring
local compassGlow = F(nd, 0, 0, compassR*2+6, compassR*2+6, 0.5, 0, -compassR-3, 19, Color3.fromRGB(0,0,0), 1, 2)
Rnd(compassGlow, compassR+3)
Str(compassGlow, Color3.fromRGB(20,50,100), 1)

-- Cardinal directions
local cardinals = {
    {t="N", a=0,   c=C.cyan},
    {t="E", a=90,  c=C.white},
    {t="S", a=180, c=C.white},
    {t="W", a=270, c=C.white},
}
for _, cd in ipairs(cardinals) do
    local r2 = math.rad(cd.a)
    local cx = math.sin(r2) * (compassR-10)
    local cy = -math.cos(r2) * (compassR-10)
    L(compassBG, cd.t, 9, cd.c, Enum.Font.GothamBold,
        nil, 0, 0, 16, 14,
        0.5, 0.5, cx-8, cy-7, 4)
end

-- Compass küçük tikleri (36 tane)
for i = 0, 35 do
    local deg2 = i * 10
    local r2   = math.rad(deg2)
    local major2 = deg2 % 30 == 0
    local r_in  = compassR - (major2 and 10 or 6)
    local r_out = compassR - 2
    -- Tick as thin frame — approximate
    local tx = math.sin(r2) * (r_in + (r_out-r_in)/2)
    local ty = -math.cos(r2) * (r_in + (r_out-r_in)/2)
    local tickDot = F(compassBG, 0, 0, major2 and 2 or 1, major2 and 2 or 1,
        0.5, 0.5, tx-1, ty-1,
        major2 and Color3.fromRGB(60,120,200) or Color3.fromRGB(30,60,100), 0, 4)
end

-- Uçak ikonu ortada
local planeIcon = L(compassBG, "✈", 16, C.white, Enum.Font.GothamBold,
    nil, 0, 0, 22, 22, 0.5, 0.5, -11, -12, 5)

-- Compass needle (heading göstergesi)
local needleFrame = F(compassBG, 1, 1, 0, 0, 0, 0, 0, 0, Color3.fromRGB(0,0,0), 1, 5)
local needle = F(needleFrame, 0, 0, 2, compassR-8, 0.5, 0.5, -1, -(compassR-8), C.cyan, 0, 5)

-- HDG sayısal alt
local ndHdgLbl = L(nd, "000°", 14, C.cyan, Enum.Font.Code,
    nil, 1, 0, 0, 20, 0, 0, 0, compassR*2+26, 3)

-- DEST / WP
local ndDestLbl = L(nd, "DEST: ---", 9, C.green, Enum.Font.Code,
    nil, 1, 0, 0, 16, 0, 0, 0, compassR*2+48, 3)
local ndWPLbl = L(nd, "WP: -/-", 9, C.amber, Enum.Font.Code,
    nil, 1, 0, 0, 16, 0, 0, 0, compassR*2+66, 3)
local ndPhaseLbl = L(nd, "IDLE", 10, C.red, Enum.Font.GothamBold,
    nil, 1, 0, 0, 18, 0, 0, 0, compassR*2+84, 3)

-- ─── ALT BAR (HUD'un en altı) ───────────────────────────
local barH = 32
local bar  = F(hud, 1, 0, 0, barH, 0, 1, 0, -barH-1, Color3.fromRGB(8,11,20), 0, 3)
Rnd(bar, 6)
Str(bar, C.border, 1)

local barCells = {
    {k="gs",    l="G/S",    c=C.green},
    {k="vs",    l="V/S",    c=C.cyan},
    {k="dist",  l="DIST",   c=C.amber},
    {k="wp",    l="WYPT",   c=Color3.fromRGB(180,180,255)},
    {k="phase", l="PHASE",  c=C.magenta},
    {k="dep",   l="DEP",    c=C.dim},
    {k="arr",   l="ARR",    c=C.dim},
}
local barVals = {}
local cw = HW / #barCells
for i, cell in ipairs(barCells) do
    local cx = (i-1)*cw
    local bg = F(bar, 0, 1, cw-2, -4, 0, 0, cx+1, 2, Color3.fromRGB(11,16,28), 0, 3)
    Rnd(bg, 3)
    L(bg, cell.l, 7, C.dim, Enum.Font.GothamBold, nil, 1, 0, 0, 12, 0, 0, 0, 2, 4)
    barVals[cell.k] = L(bg, "--", 12, cell.c, Enum.Font.Code,
        nil, 1, 0, 0, 16, 0, 0, 0, 12, 4)
    if i < #barCells then
        F(bar, 0, 1, 1, -8, 0, 0, cx+cw, 4, C.border, 0, 4)
    end
end

-- HUD kapatma
hudClose.MouseButton1Click:Connect(function() hud.Visible = false end)
makeDraggable(hudTitle, hud)

-- ════════════════════════════════════════════════════════
-- ════ ANA MENÜ (Sidebar Sol) ════════════════════════════
-- ════════════════════════════════════════════════════════
local MENU_W = 220
local menuVisible = false

local menuPanel = F(sg, 0, 0, MENU_W, 480, 0, 0.5, 0, -240, C.bg, 0, 10)
menuPanel.Visible = false
Rnd(menuPanel, 10)
Str(menuPanel, C.borderHi, 1.5)

-- Menü başlık
local menuTitle = F(menuPanel, 1, 0, 0, 32, 0, 0, 0, 0, Color3.fromRGB(10,18,32), 0, 11)
Rnd(menuTitle, 10)
L(menuTitle, "✈ PTFS  AP  v15", 12, C.cyan, Enum.Font.GothamBold, nil, 1, 1, 0, 0, 0, 0, 0, 0, 12)
makeDraggable(menuTitle, menuPanel)

-- Menü kapat
local menuClose = Btn(menuPanel, "✕", 11, C.white, C.redD, 0, 0, 22, 22, 1, 0, -26, 5, 13)
Rnd(menuClose, 4)

-- Sekme sistemi
local tabs = {"Kalkış","İniş","Ayarlar","Kayıt"}
local tabBtns = {}
local tabContents = {}
local activeTab = 1

local tabBar = F(menuPanel, 1, 0, 0, 28, 0, 0, 0, 34, Color3.fromRGB(8,12,20), 0, 11)
local tabContentArea = F(menuPanel, 1, 1, 0, -64, 0, 0, 0, 64, Color3.fromRGB(0,0,0), 1, 11)

for i, name in ipairs(tabs) do
    local tw = MENU_W / #tabs
    local tbtn = Btn(tabBar, name, 9, C.dim, Color3.fromRGB(10,14,22),
        0, 1, tw-2, 0, 0, 0, (i-1)*tw+1, 0, 12)
    Rnd(tbtn, 3)
    table.insert(tabBtns, tbtn)
    -- içerik
    local tc = F(tabContentArea, 1, 1, 0, 0, 0, 0, 0, 0, Color3.fromRGB(0,0,0), 1, 11)
    tc.Visible = (i == 1)
    table.insert(tabContents, tc)
    tbtn.MouseButton1Click:Connect(function()
        for j, tb in ipairs(tabBtns) do
            tb.BackgroundColor3 = (j==i) and Color3.fromRGB(16,28,50) or Color3.fromRGB(10,14,22)
            tb.TextColor3       = (j==i) and C.cyan or C.dim
            tabContents[j].Visible = (j==i)
        end
        activeTab = i
    end)
end
-- İlk sekme aktif rengi
tabBtns[1].BackgroundColor3 = Color3.fromRGB(16,28,50)
tabBtns[1].TextColor3 = C.cyan

-- ──── Scroll yardımcısı ─────────────────────────────────
local function makeScroll(parent, yOff)
    local sf = Instance.new("ScrollingFrame")
    sf.Size = UDim2.new(1, -4, 1, -(yOff or 0))
    sf.Position = UDim2.new(0, 2, 0, yOff or 0)
    sf.BackgroundTransparency = 1
    sf.BorderSizePixel = 0
    sf.ScrollBarThickness = 3
    sf.ScrollBarImageColor3 = C.cyanD
    sf.CanvasSize = UDim2.new(0,0,0,0)
    sf.AutomaticCanvasSize = Enum.AutomaticSize.Y
    sf.Parent = parent
    local layout = Instance.new("UIListLayout")
    layout.SortOrder = Enum.SortOrder.LayoutOrder
    layout.Padding = UDim.new(0, 3)
    layout.Parent = sf
    return sf
end

-- ──── Seçili pist label'ı ───────────────────────────────
local function makeLabel(parent, initText, col)
    local f = F(parent, 1, 0, -8, 24, 0, 0, 4, 0, Color3.fromRGB(10,16,28), 0, 12)
    Rnd(f, 4)
    Str(f, C.border, 1)
    local l = L(f, initText, 10, col or C.dim, Enum.Font.Code, Enum.TextXAlignment.Left,
        1, 1, -8, 0, 0, 0, 4, 0, 13)
    -- layout için
    f.Parent = parent
    return l, f
end

-- ──── Seçim butonu fabrikası ────────────────────────────
local function runwayBtn(parent, apName, rwy, isArr)
    local btnF = F(parent, 1, 0, -8, 28, 0, 0, 4, 0, Color3.fromRGB(12,18,32), 0, 12)
    Rnd(btnF, 5)
    Str(btnF, C.border, 1)
    local lbl = string.format("RWY %s  (%.1f°)", rwy.name, rwy.heading)
    local lb = L(btnF, lbl, 10, C.white, Enum.Font.Code, Enum.TextXAlignment.Left,
        1, 1, -8, 0, 0, 0, 6, 0, 13)
    local clickBtn = Btn(btnF, "", 10, C.white, Color3.fromRGB(0,0,0),
        1, 1, 0, 0, 0, 0, 0, 0, 14)
    clickBtn.BackgroundTransparency = 1
    clickBtn.MouseButton1Click:Connect(function()
        if isArr then
            AP.arrRunway = rwy
        else
            AP.depRunway = rwy
        end
        -- Flash efekti
        local origCol = btnF.BackgroundColor3
        TweenS:Create(btnF, TweenInfo.new(0.15), {BackgroundColor3 = isArr and C.cyanD or C.greenD}):Play()
        task.delay(0.3, function()
            TweenS:Create(btnF, TweenInfo.new(0.2), {BackgroundColor3 = origCol}):Play()
        end)
    end)
    return btnF
end

-- ──── KALKIŞ SEKMESI ────────────────────────────────────
local depScroll = makeScroll(tabContents[1])
local depSelLabel

do
    -- Seçili label
    local hdr = F(depScroll, 1, 0, 0, 28, 0, 0, 0, 0, Color3.fromRGB(8,14,24), 0, 12)
    Rnd(hdr, 4)
    L(hdr, "KALKIŞ PİSTİ", 9, C.dim, Enum.Font.GothamBold)
    local _, f2 = makeLabel(depScroll, "Seçilmedi", C.amber)
    depSelLabel = L(f2, "Seçilmedi", 10, C.amber, Enum.Font.Code, Enum.TextXAlignment.Left,
        1, 1, -8, 0, 0, 0, 4, 0, 14)

    for _, ap in ipairs(Airports) do
        local secHdr = F(depScroll, 1, 0, 0, 20, 0, 0, 0, 0, Color3.fromRGB(10,22,40), 0, 12)
        Rnd(secHdr, 3)
        L(secHdr, "📍 " .. ap.name, 9, C.cyan, Enum.Font.GothamBold, Enum.TextXAlignment.Left,
            1, 1, -8, 0, 0, 0, 4, 0, 13)
        for _, rwy in ipairs(ap.runways) do
            local bf = runwayBtn(depScroll, ap.name, rwy, false)
            -- Seçilince label güncelle
            bf:FindFirstChildOfClass("TextButton").MouseButton1Click:Connect(function()
                depSelLabel.Text = "✅ " .. ap.name .. " · RWY " .. rwy.name
                depSelLabel.TextColor3 = C.green
            end)
        end
    end
end

-- ──── İNİŞ SEKMESI ──────────────────────────────────────
local arrScroll = makeScroll(tabContents[2])
local arrSelLabel

do
    local hdr = F(arrScroll, 1, 0, 0, 28, 0, 0, 0, 0, Color3.fromRGB(8,14,24), 0, 12)
    Rnd(hdr, 4)
    L(hdr, "İNİŞ PİSTİ", 9, C.dim, Enum.Font.GothamBold)
    local _, f2 = makeLabel(arrScroll, "Seçilmedi", C.amber)
    arrSelLabel = L(f2, "Seçilmedi", 10, C.amber, Enum.Font.Code, Enum.TextXAlignment.Left,
        1, 1, -8, 0, 0, 0, 4, 0, 14)

    for _, ap in ipairs(Airports) do
        local secHdr = F(arrScroll, 1, 0, 0, 20, 0, 0, 0, 0, Color3.fromRGB(10,22,40), 0, 12)
        Rnd(secHdr, 3)
        L(secHdr, "📍 " .. ap.name, 9, C.cyan, Enum.Font.GothamBold, Enum.TextXAlignment.Left,
            1, 1, -8, 0, 0, 0, 4, 0, 13)
        for _, rwy in ipairs(ap.runways) do
            local bf = runwayBtn(arrScroll, ap.name, rwy, true)
            bf:FindFirstChildOfClass("TextButton").MouseButton1Click:Connect(function()
                arrSelLabel.Text = "✅ " .. ap.name .. " · RWY " .. rwy.name
                arrSelLabel.TextColor3 = C.cyan
            end)
        end
    end
end

-- ──── AYARLAR SEKMESI ───────────────────────────────────
do
    local scroll = makeScroll(tabContents[3])

    local function makeSlider(parent, label, min, max, default, suffix, callback)
        local grp = F(parent, 1, 0, 0, 56, 0, 0, 0, 0, Color3.fromRGB(10,16,28), 0, 12)
        Rnd(grp, 5)
        Str(grp, C.border, 1)

        L(grp, label, 9, C.dim, Enum.Font.GothamBold, Enum.TextXAlignment.Left,
            1, 0, -8, 14, 0, 0, 4, 4, 13)
        local valLbl = L(grp, tostring(default)..suffix, 10, C.cyan, Enum.Font.Code,
            Enum.TextXAlignment.Right, 0, 0, 60, 14, 1, 0, -64, 4, 13)

        -- Slider track
        local track = F(grp, 1, 0, -16, 6, 0, 0, 8, 34, Color3.fromRGB(20,30,50), 0, 13)
        Rnd(track, 3)
        local fill = F(track, 0, 1, 0, 0, 0, 0, 0, 0, C.cyanD, 0, 14)
        Rnd(fill, 3)
        local thumb = F(track, 0, 0, 10, 10, 0, 0.5, -5, -5, C.cyan, 0, 15)
        Rnd(thumb, 5)

        local function setVal(v)
            v = math.clamp(math.floor(v), min, max)
            local pct = (v-min)/(max-min)
            fill.Size = UDim2.new(pct, 0, 1, 0)
            thumb.Position = UDim2.new(pct, -5, 0.5, -5)
            valLbl.Text = tostring(v)..suffix
            if callback then callback(v) end
        end
        setVal(default)

        -- Drag slider
        local draggingSlider = false
        thumb.InputBegan:Connect(function(i)
            if i.UserInputType == Enum.UserInputType.MouseButton1 then
                draggingSlider = true
            end
        end)
        UIS.InputEnded:Connect(function(i)
            if i.UserInputType == Enum.UserInputType.MouseButton1 then
                draggingSlider = false
            end
        end)
        UIS.InputChanged:Connect(function(i)
            if draggingSlider and i.UserInputType == Enum.UserInputType.MouseMovement then
                local rect = track.AbsolutePosition
                local w    = track.AbsoluteSize.X
                local pct  = math.clamp((i.Position.X - rect.X) / w, 0, 1)
                setVal(min + pct*(max-min))
            end
        end)
        track.InputBegan:Connect(function(i)
            if i.UserInputType == Enum.UserInputType.MouseButton1 then
                local w   = track.AbsoluteSize.X
                local pct = math.clamp((i.Position.X - track.AbsolutePosition.X) / w, 0, 1)
                setVal(min + pct*(max-min))
            end
        end)
        return grp
    end

    makeSlider(scroll, "Kalkış Hızı", 100, 20000, 280, " kts", function(v) AP.TAKEOFF_SPD=v end)
    makeSlider(scroll, "Cruise Hızı", 150, 20000, 350, " kts", function(v) AP.CRUISE_SPD=v end)
    makeSlider(scroll, "İniş Hızı",   60, 20000, 110,  " kts", function(v) AP.LANDING_SPD=v end)
    makeSlider(scroll, "Cruise İrtifa", 500, 8000, 2000, " stud", function(v) AP.CRUISE_ALT=v end)
    makeSlider(scroll, "Kalkış Açısı", 5, 25, 15, "°", function(v) AP.CLIMB_PITCH=v end)
end

-- ──── KAYIT SEKMESI ─────────────────────────────────────
do
    local scroll = makeScroll(tabContents[4])

    local function makeInput(parent, placeholder, callback)
        local box = Instance.new("TextBox")
        box.Size = UDim2.new(1, -8, 0, 26)
        box.Position = UDim2.new(0, 4, 0, 0)
        box.PlaceholderText = placeholder
        box.Text = ""
        box.TextSize = 10
        box.Font = Enum.Font.Code
        box.TextColor3 = C.white
        box.PlaceholderColor3 = C.dim
        box.BackgroundColor3 = Color3.fromRGB(10,16,28)
        box.BorderSizePixel = 0
        box.ClearTextOnFocus = false
        box.ZIndex = 13
        box.Parent = parent
        Rnd(box, 4)
        Str(box, C.border, 1)
        box.FocusLost:Connect(function() if callback then callback(box.Text) end end)
        return box
    end

    local recApName, recRwyName = "Yeni Havalimanı", "00"
    local livePosLbl, lastSaveLbl

    local hdr1 = F(scroll, 1, 0, 0, 20, 0, 0, 0, 0, Color3.fromRGB(8,14,24), 0, 12)
    Rnd(hdr1, 3)
    L(hdr1, "PİST KAYDET", 9, C.dim, Enum.Font.GothamBold)

    makeInput(scroll, "Havalimanı adı", function(t) if t~="" then recApName=t end end)
    makeInput(scroll, "Pist numarası (örn: 07R)", function(t) if t~="" then recRwyName=t end end)

    local posF = F(scroll, 1, 0, 0, 24, 0, 0, 0, 0, Color3.fromRGB(8,12,20), 0, 12)
    Rnd(posF, 3)
    livePosLbl = L(posF, "Konum: -", 9, C.dim, Enum.Font.Code, Enum.TextXAlignment.Left,
        1, 1, -8, 0, 0, 0, 4, 0, 13)

    local saveBtn = Btn(scroll, "💾  KAYDET", 11, C.green, Color3.fromRGB(0,25,12),
        1, 0, -8, 28, 0, 0, 4, 0, 13)
    Rnd(saveBtn, 5)
    Str(saveBtn, C.green, 1)

    local lastF = F(scroll, 1, 0, 0, 32, 0, 0, 0, 0, Color3.fromRGB(8,12,20), 0, 12)
    Rnd(lastF, 3)
    lastSaveLbl = L(lastF, "Son kayıt: -", 9, C.dim, Enum.Font.Code, Enum.TextXAlignment.Left,
        1, 1, -8, 0, 0, 0, 4, 0, 13)

    saveBtn.MouseButton1Click:Connect(function()
        pcall(function()
            local char = Player.Character
            if not char then return end
            local hrp = char:FindFirstChild("HumanoidRootPart")
            if not hrp then return end
            local cam  = game:GetService("Workspace").CurrentCamera
            local flat = Vector3.new(cam.CFrame.LookVector.X, 0, cam.CFrame.LookVector.Z).Unit
            local hdg  = math.deg(math.atan2(flat.X, flat.Z))
            if hdg < 0 then hdg = hdg + 360 end
            local pos  = hrp.Position
            local thr  = Vector3.new(math.floor(pos.X*10)/10, math.floor(pos.Y*10)/10, math.floor(pos.Z*10)/10)
            local newRwy = { name=recRwyName, threshold=thr, heading=math.floor(hdg*10)/10 }
            local oppHdg = (hdg+180)%360
            local rwyNum = tonumber(recRwyName:match("%d+"))
            local oppNum = rwyNum and ((rwyNum+17)%36+1) or nil
            local oppName = oppNum and string.format("%02d",oppNum) or (recRwyName.."_R")
            local oppRwy = { name=oppName, threshold=thr, heading=math.floor(oppHdg*10)/10 }

            local apFound
            for _, ap in ipairs(Airports) do
                if ap.name==recApName then apFound=ap; break end
            end
            if not apFound then
                apFound = { name=recApName, runways={} }
                table.insert(Airports, apFound)
            end
            table.insert(apFound.runways, newRwy)
            table.insert(apFound.runways, oppRwy)

            -- Kalkış/iniş sekmelerine ekle
            for _, isArr in ipairs({false, true}) do
                local scr = isArr and arrScroll or depScroll
                local secHdr = F(scr, 1, 0, 0, 20, 0, 0, 0, 0, Color3.fromRGB(10,22,40), 0, 12)
                Rnd(secHdr, 3)
                L(secHdr, "📍 "..recApName, 9, C.cyan, Enum.Font.GothamBold, Enum.TextXAlignment.Left,
                    1, 1, -8, 0, 0, 0, 4, 0, 13)
                for _, rwy2 in ipairs({newRwy, oppRwy}) do
                    runwayBtn(scr, recApName, rwy2, isArr)
                end
            end

            lastSaveLbl.Text = string.format("✅ %s RWY%s + RWY%s (%.0f°/%.0f°)",
                recApName, recRwyName, oppName, hdg, oppHdg)
            lastSaveLbl.TextColor3 = C.green
        end)
    end)

    -- Canlı konum
    task.spawn(function()
        while task.wait(0.3) do
            pcall(function()
                local char = Player.Character
                if not char then return end
                local hrp = char:FindFirstChild("HumanoidRootPart")
                if not hrp then return end
                local cam  = game:GetService("Workspace").CurrentCamera
                local flat = Vector3.new(cam.CFrame.LookVector.X, 0, cam.CFrame.LookVector.Z).Unit
                local hdg  = math.deg(math.atan2(flat.X, flat.Z))
                if hdg < 0 then hdg = hdg + 360 end
                local p = hrp.Position
                livePosLbl.Text = string.format("X:%d Y:%d Z:%d  %03.0f°", p.X, p.Y, p.Z, hdg)
            end)
        end
    end)
end

-- ════════════════════════════════════════════════════════
-- ════ AP KONTROL MENÜSÜ (popup, AP butonuna basınca) ════
-- ════════════════════════════════════════════════════════
local AP_W, AP_H = 240, 200
local apMenu = F(sg, 0, 0, AP_W, AP_H, 0, 0.5, 0, -AP_H/2, C.bg, 0, 20)
apMenu.Visible = false
Rnd(apMenu, 10)
Str(apMenu, C.borderHi, 1.5)

local apMenuTitle = F(apMenu, 1, 0, 0, 30, 0, 0, 0, 0, Color3.fromRGB(10,20,40), 0, 21)
Rnd(apMenuTitle, 10)
L(apMenuTitle, "AUTOPILOT KONTROL", 11, C.cyan, Enum.Font.GothamBold)
makeDraggable(apMenuTitle, apMenu)

local apMenuClose = Btn(apMenu, "✕", 11, C.white, C.redD, 0, 0, 22, 22, 1, 0, -26, 4, 22)
Rnd(apMenuClose, 4)
apMenuClose.MouseButton1Click:Connect(function() apMenu.Visible = false end)

-- AP Durum
local apStatF = F(apMenu, 1, 0, -12, 28, 0, 0, 6, 34, Color3.fromRGB(10,30,10), 0, 21)
Rnd(apStatF, 5)
Str(apStatF, C.greenD, 1)
local apStatLbl = L(apStatF, "●  AP  OFF", 13, C.red, Enum.Font.GothamBold, nil, 1, 1, 0, 0, 0, 0, 0, 0, 22)

-- Rota özeti
local routeF = F(apMenu, 1, 0, -12, 24, 0, 0, 6, 66, Color3.fromRGB(8,12,24), 0, 21)
Rnd(routeF, 4)
local routeLbl = L(routeF, "DEP: --  →  ARR: --", 9, C.dim, Enum.Font.Code,
    Enum.TextXAlignment.Left, 1, 1, -8, 0, 0, 0, 4, 0, 22)

-- WP bilgi
local wpF = F(apMenu, 1, 0, -12, 20, 0, 0, 6, 94, Color3.fromRGB(8,10,20), 0, 21)
Rnd(wpF, 3)
local wpLbl = L(wpF, "PHASE: IDLE   WP: -/-", 9, C.amber, Enum.Font.Code,
    Enum.TextXAlignment.Left, 1, 1, -8, 0, 0, 0, 4, 0, 22)

-- BAŞLAT butonu
local startBtn = Btn(apMenu, "▶  AUTOPILOT BAŞLAT", 12, C.green, Color3.fromRGB(0,30,15),
    1, 0, -12, 30, 0, 0, 6, 120, 22)
Rnd(startBtn, 6)
Str(startBtn, C.green, 1)
startBtn.MouseButton1Click:Connect(function()
    local ok, msg = startAP()
    startBtn.Text = ok and "✅ " .. msg or "❌ " .. msg
    task.delay(2, function() startBtn.Text = "▶  AUTOPILOT BAŞLAT" end)
end)

-- DURDUR butonu
local stopBtn = Btn(apMenu, "■  DURDUR", 12, C.red, Color3.fromRGB(30,8,8),
    1, 0, -12, 28, 0, 0, 6, 156, 22)
Rnd(stopBtn, 6)
Str(stopBtn, C.red, 1)
stopBtn.MouseButton1Click:Connect(function()
    stopAP("Manuel")
end)

-- AP menü durum güncelle
task.spawn(function()
    while task.wait(0.3) do
        pcall(function()
            if AP.active then
                apStatLbl.Text = "●  AP  ON  — " .. AP.phase
                apStatLbl.TextColor3 = C.green
                apStatF.BackgroundColor3 = Color3.fromRGB(5,30,12)
            else
                apStatLbl.Text = "●  AP  OFF"
                apStatLbl.TextColor3 = C.red
                apStatF.BackgroundColor3 = Color3.fromRGB(25,8,8)
            end
            local dep = AP.depRunway and AP.depRunway.name or "--"
            local arr = AP.arrRunway and AP.arrRunway.name or "--"
            routeLbl.Text = "DEP: " .. dep .. "   →   ARR: " .. arr
            wpLbl.Text = string.format("PHASE: %s   WP: %s/%s",
                AP.phase, tostring(wpIndex), tostring(#waypoints))
        end)
    end
end)

-- ════════════════════════════════════════════════════════
-- ════ SAĞ DOCK (Buton Grubu) ════════════════════════════
-- ════════════════════════════════════════════════════════
local dockW = 116
local dock = F(sg, 0, 0, dockW, 130, 1, 0.5, -dockW-8, -65, Color3.fromRGB(8,12,20), 0, 15)
Rnd(dock, 8)
Str(dock, C.border, 1)

-- Logo/başlık
local dockTitle = F(dock, 1, 0, 0, 22, 0, 0, 0, 0, Color3.fromRGB(10,16,30), 0, 16)
Rnd(dockTitle, 8)
L(dockTitle, "✈ PTFS v15", 9, C.cyan, Enum.Font.GothamBold)

local function dockBtn(y, txt, bg, brd, callback)
    local b = Btn(dock, txt, 10, C.white, bg,
        1, 0, -8, 26, 0, 0, 4, y, 16)
    Rnd(b, 5)
    Str(b, brd, 1)
    b.MouseButton1Click:Connect(callback)
    return b
end

-- Menü butonu
dockBtn(26, "☰  MENÜ", Color3.fromRGB(12,20,40), C.borderHi, function()
    menuVisible = not menuVisible
    menuPanel.Visible = menuVisible
    -- Menüyü dock'un soluna konumlandır
    if menuVisible then
        menuPanel.Position = UDim2.new(1, -dockW-8-MENU_W-8, 0.5, -240)
    end
end)

-- AP butonu
local apDockBtn = dockBtn(56, "✈  AUTOPILOT", Color3.fromRGB(0,18,8), C.green, function()
    apMenu.Visible = not apMenu.Visible
    if apMenu.Visible then
        -- Dock'un solunda görünsün
        apMenu.Position = UDim2.new(1, -dockW-8-AP_W-8, 0.5, -AP_H/2)
    end
end)
Str(apDockBtn, C.green, 1)

-- HUD butonu
dockBtn(86, "🖥  KOKPİT", Color3.fromRGB(8,12,28), C.borderHi, function()
    hud.Visible = not hud.Visible
end)

-- AP durum indikatör (dock)
local dockAPF = F(dock, 1, 0, -8, 12, 0, 0, 4, 116, Color3.fromRGB(25,8,8), 0, 16)
Rnd(dockAPF, 3)
local dockAPLbl = L(dockAPF, "AP OFF", 8, C.red, Enum.Font.GothamBold, nil, 1, 1, 0, 0, 0, 0, 0, 0, 17)

task.spawn(function()
    while task.wait(0.5) do pcall(function()
        dockAPLbl.Text = AP.active and ("AP "..AP.phase) or "AP OFF"
        dockAPLbl.TextColor3 = AP.active and C.green or C.red
        dockAPF.BackgroundColor3 = AP.active and Color3.fromRGB(5,30,10) or Color3.fromRGB(25,8,8)
        -- AP butonu rengi
        apDockBtn.BackgroundColor3 = AP.active and Color3.fromRGB(0,30,12) or Color3.fromRGB(0,18,8)
    end) end
end)

-- ════════════════════════════════════════════════════════
-- ════ HUD CANLI GÜNCELLEME ══════════════════════════════
-- ════════════════════════════════════════════════════════
local prevAlt = 0
RunService.Heartbeat:Connect(function()
    if not hud.Visible then return end
    pcall(function()
        local char = Player.Character
        if not char then return end
        local hrp = char:FindFirstChild("HumanoidRootPart")
        if not hrp then return end

        local vel   = hrp.AssemblyLinearVelocity
        local gs    = math.floor(vel.Magnitude)
        local alt   = math.floor(hrp.Position.Y)
        local look  = hrp.CFrame.LookVector

        -- Heading
        local hdg = math.deg(math.atan2(look.X, look.Z))
        if hdg < 0 then hdg = hdg + 360 end

        -- Pitch
        local pitch = 0
        if vel.Magnitude > 0.5 then
            pitch = math.deg(math.asin(math.clamp(vel.Y/vel.Magnitude, -1, 1)))
        end

        -- VS
        local vs = math.floor(vel.Y * 60)

        -- ─ SPD TAPE ─
        spdVal.Text = tostring(gs)
        -- Hız bandı kaydırma: büyük sayı = yukarı kayması lazım
        -- 1 stud = 20px, band 1000'e kadar
        do
            local spdPanelH = spdTickContainer.AbsoluteSize.Y
            local totalH = 1000  -- 0-1000 aralığı
            local pxPerUnit = spdPanelH / totalH * 1000
            -- gs'ye karşılık gelen yüzde (0-1)
            -- 0 = en alt, 1000 = en üst
            -- Orta göstermek için: pointer ortada sabit, ticks yukarı/aşağı kayıyor
            -- Yüksek hız → ticks yukarı kaymış
            local offset = math.clamp((gs/1000) * spdPanelH, 0, spdPanelH)
            -- Tick container'ı kaydır (negative Y = aşağı doğru görünür)
            -- Ticks sabit ama şu an absolute position ile kayma yok
            -- Renk güncelle: hız rengi
            local speedColor
            if gs < 100 then speedColor = C.red
            elseif gs < 200 then speedColor = C.amber
            else speedColor = C.green end
            spdVal.TextColor3 = speedColor
        end

        -- ─ ALT TAPE ─
        altVal.Text = tostring(alt)
        do
            local altColor
            if alt < 50 then altColor = C.amber
            elseif alt < 200 then altColor = C.white
            else altColor = C.cyan end
            altVal.TextColor3 = altColor
        end

        -- ─ YAPAY UFUK ─
        -- Pitch: +pitch = ufuk aşağıya iner (gökyüzü büyür)
        -- Her derece = PFD_H*0.018 piksel
        local aiH = ai.AbsoluteSize.Y
        local pxPitch = math.clamp(pitch * (aiH * 0.018), -(aiH*0.45), aiH*0.45)
        local skyRatio = math.clamp(0.5 + pxPitch/aiH, 0.05, 0.95)
        skyTop.Size = UDim2.new(1, 0, skyRatio, 0)
        skyBot.Size = UDim2.new(1, 0, skyRatio, 0)
        gndTop.Position = UDim2.new(0, 0, skyRatio, 0)
        gndTop.Size = UDim2.new(1, 0, 1-skyRatio, 0)
        gndBot.Position = UDim2.new(0, 0, 1, 0)
        gndBot.Size = UDim2.new(1, 0, 1-skyRatio, 0)
        horizLine.Position = UDim2.new(0, 0, skyRatio, -1)

        -- FPV (flight path vector) - hız yönüne göre
        if vel.Magnitude > 2 then
            local fpvPitch = math.deg(math.asin(math.clamp(vel.Y/vel.Magnitude,-1,1)))
            local fpvYawOff = 0  -- yaw offset basit
            local fpvYPx = -fpvPitch * (aiH * 0.018)
            fpvFrame.Position = UDim2.new(0.5, -9, 0.5, fpvYPx - 9)
            fpvFrame.Visible = true
        else
            fpvFrame.Visible = false
        end

        -- ─ HEADING ─
        -- HDG pointer: hdg/360 * aiW
        local hdgFrac = hdg / 360
        hdgPtr.Position = UDim2.new(hdgFrac, -1, 0, 0)
        hdgRead.Text = string.format("%03.0f°", hdg)
        ndHdgLbl.Text = string.format("%03.0f°", hdg)

        -- ─ ND NEEDLE ─
        -- Needle rotasyon: hdg derece döner
        -- Roblox'ta rotation ile basit oku göster
        -- (Frame rotation olmadığı için sadece konumu güncelliyoruz)

        -- ─ TOPSTRIP ─
        pitchRead.Text = string.format("PTH %+.1f°", pitch)
        vsRead.Text = string.format("V/S %+d fpm", vs)

        -- ─ BAR ─
        barVals.gs.Text = tostring(gs) .. " kts"
        barVals.vs.Text = (vs >= 0 and "+" or "") .. tostring(vs) .. " fpm"
        barVals.phase.Text = AP.phase

        -- Dist
        local dist = "---"
        if AP.arrRunway then
            dist = tostring(math.floor(dist2D(hrp.Position, AP.arrRunway.threshold))) .. " st"
        end
        barVals.dist.Text = dist

        -- WP
        barVals.wp.Text = AP.active and (tostring(wpIndex).."/"..tostring(#waypoints)) or "-/-"

        -- DEP/ARR
        barVals.dep.Text = AP.depRunway and AP.depRunway.name or "---"
        barVals.arr.Text = AP.arrRunway and AP.arrRunway.name or "---"

        -- ─ ND ─
        local dStr = AP.arrRunway
            and tostring(math.floor(dist2D(hrp.Position, AP.arrRunway.threshold)))
            or "---"
        ndDestLbl.Text = "DEST: " .. dStr .. " stud"
        ndWPLbl.Text   = "WP: " .. (AP.active and (tostring(wpIndex).."/"..tostring(#waypoints)) or "-/-")

        local phColor = AP.active and C.green or C.red
        ndPhaseLbl.Text = AP.phase
        ndPhaseLbl.TextColor3 = phColor

        prevAlt = alt
    end)
end)

print("✅ PTFS Autopilot v15 yüklendi!")
print("   Sağda PTFS v15 paneli | ☰ Menü | ✈ AP | 🖥 Kokpit")

Features & Information

çok havlı

Community Comments (0)

No comments yet for this script.

More Scripts for Pilot Training Flight Simulator