script_name("CamHack")
script_author("sanek a.k.a Maks_Fender, edited by ANIKI")
require "lib.moonloader"
local mad = require "MoonAdditions"

-- GLOBAL VARIABLES AND CONSTANTS
local camHack_posX, camHack_posY, camHack_posZ = 0, 0, 0 -- Camera’s positional coordinates
local camHack_angZ, camHack_angY = 0, 0 -- Camera’s orientation angles (Z for horizontal rotation, Y for vertical)
local camHack_speed = 1.0 -- Speed at which the camera moves
local camHack_flyModeActive = 0 -- Fly mode status: 0 = inactive, 1 = active
local camHack_collisionEnabled = true -- Toggle for collision checking

-- Collision settings
local camHack_checkSolidObjects = true
local camHack_checkVehicles = true
local camHack_checkPeds = true
local camHack_checkObjects = true
local camHack_checkParticles = false
local camHack_seeThroughObjects = false
local camHack_ignoreSomeObjects = false
local camHack_allowShootThrough = false
local camHack_collisionBufferDistance = 0.5 -- Distance to keep from objects when colliding

-- Function to check camera collision and adjust position if necessary
local function isCameraColliding(destX, destY, destZ)
    if not camHack_collisionEnabled then
        return false, nil, destX, destY, destZ
    end

    local result, colPoint = processLineOfSight(
        camHack_posX, camHack_posY, camHack_posZ,
        destX, destY, destZ,
        camHack_checkSolidObjects, camHack_checkVehicles, camHack_checkPeds,
        camHack_checkObjects, camHack_checkParticles, camHack_seeThroughObjects,
        camHack_ignoreSomeObjects, camHack_allowShootThrough
    )
    if result then
        -- Calculate the distance to the collision point
        local collisionDist = math.sqrt((colPoint.pos[1] - camHack_posX)^2 + (colPoint.pos[2] - camHack_posY)^2 + (colPoint.pos[3] - camHack_posZ)^2)
        if collisionDist > camHack_collisionBufferDistance then
            local dirX, dirY, dirZ = (destX - camHack_posX), (destY - camHack_posY), (destZ - camHack_posZ)
            local scaleFactor = (collisionDist - camHack_collisionBufferDistance) / collisionDist
            return true, colPoint, destX - dirX * scaleFactor, destY - dirY * scaleFactor, destZ - dirZ * scaleFactor
        else
            return true, colPoint, camHack_posX, camHack_posY, camHack_posZ
        end
    end
    return false, colPoint, destX, destY, destZ
end

-- Main loop, runs continuously while the script is active
function main()
    while true do
        wait(0) -- Allows the script to run on every frame
        
        -- Check if Fly Mode is inactive
        if camHack_flyModeActive == 0 then
            -- Check for the key combination to activate Fly Mode (C + 1)
            if isKeyDown(VK_C) and isKeyDown(VK_1) then
                -- Enter Fly Mode: Disable radar and HUD, set initial camera position and angle
                displayRadar(false)
                displayHud(false)
                camHack_posX, camHack_posY, camHack_posZ = getCharCoordinates(playerPed)
                camHack_angZ = -getCharHeading(playerPed) -- Invert to match the player heading
                camHack_angY = 0.0
                setFixedCameraPosition(camHack_posX, camHack_posY, camHack_posZ, 0.0, 0.0, 0.0) -- Set initial camera position
                lockPlayerControl(true) -- Prevent player control while in Fly Mode
                camHack_flyModeActive = 1 -- Fly Mode is now active
            end
        else
            -- Handle Fly Mode if active
            if not sampIsChatInputActive() and not isSampfuncsConsoleActive() then
                -- Update camera orientation based on mouse movement
                local offMouX, offMouY = getPcMouseMovement()
                camHack_angZ = (camHack_angZ + offMouX / 4.0) % 360 -- Horizontal angle
                camHack_angY = math.max(math.min(camHack_angY + offMouY / 4.0, 89.0), -89.0) -- Vertical angle within bounds

                -- Calculate the movement vector based on the camera’s orientation
                local radZ, radY = math.rad(camHack_angZ), math.rad(camHack_angY)
                local sinZ, cosZ, sinY, cosY = math.sin(radZ), math.cos(radZ), math.sin(radY), math.cos(radY)
                local movX, movY, movZ = cosY * sinZ, cosY * cosZ, sinY

                local newX, newY, newZ = camHack_posX, camHack_posY, camHack_posZ

                -- Move the camera forward or backward (W/S keys)
                if isKeyDown(VK_W) then
                    newX = camHack_posX + movX * camHack_speed
                    newY = camHack_posY + movY * camHack_speed
                    newZ = camHack_posZ + movZ * camHack_speed
                elseif isKeyDown(VK_S) then
                    newX = camHack_posX - movX * camHack_speed
                    newY = camHack_posY - movY * camHack_speed
                    newZ = camHack_posZ - movZ * camHack_speed
                end

                -- Move the camera left or right (A/D keys)
                if isKeyDown(VK_A) or isKeyDown(VK_D) then
                    local sideOffset = isKeyDown(VK_A) and -math.pi / 2 or math.pi / 2 -- Determine the direction based on the key
                    local sideZ = math.sin(math.rad(camHack_angZ) + sideOffset)
                    local sideX = math.cos(math.rad(camHack_angZ) + sideOffset)
                    newX = camHack_posX + sideZ * camHack_speed
                    newY = camHack_posY + sideX * camHack_speed
                end

                -- Move the camera up or down (Space/Shift keys)
                if isKeyDown(VK_SPACE) then
                    newZ = camHack_posZ + camHack_speed
                elseif isKeyDown(VK_SHIFT) then
                    newZ = camHack_posZ - camHack_speed
                end

                -- Check for collisions and adjust position
                local collision, colPoint, adjX, adjY, adjZ = isCameraColliding(newX, newY, newZ)
                camHack_posX, camHack_posY, camHack_posZ = adjX, adjY, adjZ

                -- Update camera position and point it in the direction of movement
                setFixedCameraPosition(camHack_posX, camHack_posY, camHack_posZ, 0.0, 0.0, 0.0)
                pointCameraAtPoint(camHack_posX + movX, camHack_posY + movY, camHack_posZ + movZ, 2)

                -- Adjust camera speed (Plus/Minus keys)
                if isKeyDown(187) then -- Increase speed with the ‘Plus’ key
                    camHack_speed = camHack_speed + 0.01
                elseif isKeyDown(189) then -- Decrease speed with the ‘Minus’ key
                    camHack_speed = math.max(camHack_speed - 0.01, 0.01) -- Prevent speed from becoming negative or zero
                end

                -- Debug info
                local collisionText = collision and string.format(
                    "~g~~h~~h~~h~Collision Detected~n~" ..
                    "~w~Entity: ~p~~h~%d~n~" ..
                    "~w~Type: ~p~~h~%d~p~~h~ ~w~Surface: ~p~~h~%d~n~" ..
                    "~w~Position: ~p~~h~(%.2f, %.2f, %.2f)~n~" ..
                    "~w~Normal: ~p~~h~(%.2f, %.2f, %.2f)",
                    colPoint.entity, colPoint.entityType, colPoint.surfaceType[1],
                    colPoint.pos[1], colPoint.pos[2], colPoint.pos[3],
                    colPoint.normal[1], colPoint.normal[2], colPoint.normal[3]
                ) or "~r~~h~~h~~h~No Collision"

                -- Formatting CamHack Debug Info
                local text = string.format(
                    "~b~~h~~h~~h~CamHack Debug Info~n~" ..
                    "~w~Position:~n~" ..
                    " ~p~~h~(%.2f, %.2f, %.2f)~n~" ..
                    "~w~Direction:~n~" ..
                    " ~p~~h~(%.2f, %.2f, %.2f)~n~" ..
                    "~w~Angle:~n~" ..
                    " ~p~~h~Z: %.2f| ~p~~h~Y: %.2f|~n~" ..
                    "~w~Speed:~n~" ..
                    " ~p~~h~%.2f units/sec~n~" ..
                    "~w~Collision Status:~n~" ..
                    " %s",
                    camHack_posX, camHack_posY, camHack_posZ,     -- Position coordinates
                    camHack_posX + movX, camHack_posY + movY, camHack_posZ + movZ, -- Approximate direction
                    camHack_angZ, camHack_angY,                   -- Angles Z and Y
                    camHack_speed,                                -- Camera speed
                    collisionText                                 -- Collision text
                )

                mad.draw_text(text, 45, 240, mad.font_style.BEFORE_TEXT, 0.40, 1.0, mad.font_align.LEFT, 9999, true, false, 255, 255, 255, 255, 1, 2, 0, 0, 0, 150)

                -- Check for Fly Mode deactivation (C + 2)
                if isKeyDown(VK_C) and isKeyDown(VK_2) then
                    -- Exit Fly Mode: Restore normal player control and camera behavior
                    lockPlayerControl(false)
                    restoreCameraJumpcut()
                    setCameraBehindPlayer()
                    camHack_flyModeActive = 0 -- Fly Mode is now inactive
                end
            end
        end
    end
end

-- Intercept player chat bubble events
local sampev = require "lib.samp.events"
-- Modify the chat bubble display duration while in Fly Mode
function sampev.onPlayerChatBubble(id, col, dist, dur, msg)
    if camHack_flyModeActive == 1 then return {id, col, 1488, dur, msg} end -- Extend bubble duration in Fly Mode
end


проверка коллизии работает очень криво