Загрузка данных


-- REALISTIC CAMERA - FIXED
-- CTRL = бег | F = переключение камеры

local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local player = Players.LocalPlayer

-- НАСТРОЙКИ
local WALK_SPEED = 16
local SPRINT_SPEED = 25

local NORMAL_FOV = 90
local SPRINT_FOV = 120

-- ПОКАЧИВАНИЕ ПРИ ХОДЬБЕ
local WALK_X = 0.045
local WALK_Y = 0.065

-- ПОКАЧИВАНИЕ ПРИ БЕГЕ
local RUN_X = 0.09
local RUN_Y = 0.13

local BOB_SMOOTHNESS = 9
local FOV_SMOOTHNESS = 5

-- СОСТОЯНИЕ
local character
local humanoid

local firstPerson = true
local sprintHeld = false

local bobTime = 0
local sprintTime = 0
local fatigue = 0

local currentOffset = Vector3.zero
local currentRoll = 0

-- КАМЕРА
local function setCameraMode(enabled)
    firstPerson = enabled

    if enabled then
        player.CameraMode = Enum.CameraMode.LockFirstPerson
        player.CameraMinZoomDistance = 0.5
        player.CameraMaxZoomDistance = 0.5
    else
        player.CameraMode = Enum.CameraMode.Classic
        player.CameraMinZoomDistance = 0.5
        player.CameraMaxZoomDistance = 12
    end
end

-- СКОРОСТЬ
local function updateSpeed()
    if not humanoid or humanoid.Health <= 0 then
        return
    end

    humanoid.WalkSpeed = sprintHeld and SPRINT_SPEED or WALK_SPEED
end

-- ПЕРСОНАЖ
local function setupCharacter(char)
    character = char
    humanoid = char:WaitForChild("Humanoid")

    sprintHeld = false
    bobTime = 0
    sprintTime = 0
    fatigue = 0

    currentOffset = Vector3.zero
    currentRoll = 0

    humanoid.WalkSpeed = WALK_SPEED

    setCameraMode(firstPerson)
end

-- КЛАВИАТУРА
UIS.InputBegan:Connect(function(input, processed)
    if input.KeyCode == Enum.KeyCode.LeftControl
        or input.KeyCode == Enum.KeyCode.RightControl then

        sprintHeld = true
        updateSpeed()
        return
    end

    if processed then
        return
    end

    if input.KeyCode == Enum.KeyCode.F then
        setCameraMode(not firstPerson)
    end
end)

UIS.InputEnded:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.LeftControl
        or input.KeyCode == Enum.KeyCode.RightControl then

        sprintHeld = false
        updateSpeed()
    end
end)

UIS.WindowFocusReleased:Connect(function()
    sprintHeld = false
    updateSpeed()
end)

-- ПОДДЕРЖАНИЕ СКОРОСТИ
RunService.Heartbeat:Connect(function()
    if humanoid and humanoid.Health > 0 then
        local target = sprintHeld and SPRINT_SPEED or WALK_SPEED

        if humanoid.WalkSpeed ~= target then
            humanoid.WalkSpeed = target
        end
    end
end)

-- КАМЕРА: ВАЖНО — ПОСЛЕ СТАНДАРТНОГО ОБНОВЛЕНИЯ ROBLOX
pcall(function()
    RunService:UnbindFromRenderStep("RealisticCameraFixed")
end)

RunService:BindToRenderStep(
    "RealisticCameraFixed",
    Enum.RenderPriority.Camera.Value + 1,
    function(dt)

        local camera = workspace.CurrentCamera

        if not camera then
            return
        end

        local alive = humanoid and humanoid.Health > 0
        local moving = alive
            and humanoid.MoveDirection.Magnitude > 0.05

        local running = moving and sprintHeld

        -- FOV
        local targetFOV = sprintHeld and SPRINT_FOV or NORMAL_FOV
        local fovAlpha = 1 - math.exp(-FOV_SMOOTHNESS * dt)

        camera.FieldOfView +=
            (targetFOV - camera.FieldOfView) * fovAlpha

        -- УСТАЛОСТЬ
        if running then
            sprintTime += dt
        else
            sprintTime = 0
        end

        local targetFatigue = math.clamp(
            (sprintTime - 90) / 30,
            0,
            1
        )

        local fatigueAlpha = 1 - math.exp(-1.5 * dt)

        fatigue += (targetFatigue - fatigue) * fatigueAlpha

        -- ПОКАЧИВАНИЕ
        local targetOffset = Vector3.zero
        local targetRoll = 0

        if firstPerson and moving then
            local frequency = sprintHeld and 12 or 8
            bobTime += dt * frequency

            local x = sprintHeld and RUN_X or WALK_X
            local y = sprintHeld and RUN_Y or WALK_Y

            local fatigueMultiplier = 1 + fatigue * 1.5

            targetOffset = Vector3.new(
                math.cos(bobTime * 0.5) * x * fatigueMultiplier,
                math.abs(math.sin(bobTime)) * y * fatigueMultiplier,
                0
            )

            -- ДЫХАНИЕ ПРИ ДЛИТЕЛЬНОМ БЕГЕ
            if running then
                targetOffset += Vector3.new(
                    0,
                    math.sin(sprintTime * 2.5) * fatigue * 0.035,
                    0
                )

                targetRoll = math.sin(bobTime * 0.5)
                    * math.rad(1.2)
                    * fatigue
            end
        else
            bobTime = 0
        end

        -- ПЛАВНОСТЬ
        local alpha = 1 - math.exp(-BOB_SMOOTHNESS * dt)

        currentOffset = currentOffset:Lerp(targetOffset, alpha)

        currentRoll += (targetRoll - currentRoll) * alpha

        -- ПРИМЕНЯЕМ КАМЕРУ ПОСЛЕ ОБНОВЛЕНИЯ ROBLOX
        camera.CFrame = camera.CFrame
            * CFrame.new(currentOffset)
            * CFrame.Angles(0, 0, currentRoll)
    end
)

-- ЗАПУСК
player.CharacterAdded:Connect(setupCharacter)

if player.Character then
    setupCharacter(player.Character)
end