local vector3d = require 'vector3d'
local ffi = require 'ffi'
local SAMemory = require 'SAMemory'
local raknet = require 'samp.raknet'
local http = require 'socket.http'
local ltn12 = require 'ltn12'
local widgets = require 'widgets'

local Core = {}

--================================================---
--  LOCAL CONSTANTS & FFI
--================================================---
local WIDGET_FIRE = 1
local WIDGET_LOOK = 4
local WIDGET_JUMP = 31
local BONE = { HEAD = 5, CHEST = 3, GROIN = 2 }

local gta = ffi.load('GTASA')
local cslap_offset = { x = 0.0, y = 0.0, z = 0.5 }
local crasher_camModes = { 7, 8, 34, 45, 46, 51, 65 }
local api_url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&dt=t&tl="

local pAnimationWalk = {
    'WALK_PLAYER', 'GUNCROUCHFWD', 'GUNCROUCHBWD', 'GUNMOVE_BWD', 'GUNMOVE_FWD', 'GUNMOVE_L', 'GUNMOVE_R',
    'RUN_GANG1', 'JOG_FEMALEA', 'JOG_MALEA', 'RUN_CIVI', 'RUN_CSAW', 'RUN_FAT', 'RUN_FATOLD', 'RUN_OLD',
    'RUN_ROCKET', 'RUN_WUZI', 'SPRINT_WUZI', 'WALK_ARMED', 'WALK_CIVI', 'WALK_CSAW', 'WALK_DRUNK',
    'WALK_FAT', 'WALK_FATOLD', 'WALK_GANG1', 'WALK_GANG2', 'WALK_OLD', 'WALK_SHUFFLE', 'WALK_START',
    'WALK_START_ARMED', 'WALK_START_CSAW', 'WALK_START_ROCKET', 'WALK_WUZI', 'WOMAN_WALKBUSY',
    'WOMAN_WALKFATOLD', 'WOMAN_WALKNORM', 'WOMAN_WALKOLD', 'WOMAN_RUNFATOLD', 'WOMAN_WALKPRO',
    'WOMAN_WALKSEXY', 'WOMAN_WALKSHOP', 'RUN_1ARMED', 'RUN_ARMED', 'RUN_PLAYER', 'WALK_ROCKET',
    'CLIMB_IDLE', 'MUSCLESPRINT', 'CLIMB_PULL', 'CLIMB_STAND', 'CLIMB_STAND_FINISH', 'SWIM_BREAST',
    'SWIM_CRAWL', 'SWIM_DIVE_UNDER', 'SWIM_GLIDE', 'MUSCLERUN', 'WOMAN_RUN', 'WOMAN_RUNBUSY',
    'WOMAN_RUNPANIC', 'WOMAN_RUNSEXY', 'SPRINT_CIVI', 'SPRINT_PANIC', 'SWAT_RUN', 'FATSPRINT'
}
local pGunFireAnimations = {
    "COLT45_FIRE", "COLT45_FIRE_POOR", "SILENCED_FIRE", "SILENCED_CROUCH_FIRE", "UZI_FIRE", "UZI_FIRE_POOR",
    "UZI_CROUCHFIRE", "TEC_FIRE", "TEC_crouchfire", "PYTHON_FIRE", "PYTHON_FIRE_POOR", "PYTHON_CROUCHFIRE",
    "RIFLE_FIRE", "RIFLE_FIRE_POOR", "RIFLE_CROUCHFIRE", "SHOTGUN_FIRE", "SHOTGUN_FIRE_POOR",
    "SHOTGUN_CROUCHFIRE", "AK47_FIRE", "AK47_CROUCHFIRE", "M4_FIRE", "M4_CROUCHFIRE", "FLAME_FIRE",
    "FLAME_FIRE2", "ROCKET_FIRE", "ROCKET_IDLE", "ROCKET_RUN", "ROCKET_WALK", "WALK_start_rocket",
    "WEAPON_sniper", "sniper_fire"
}
local weapon_list = {
    { name = "Pistol", id = 22 }, { name = "Silenced Pistol", id = 23 }, { name = "Desert Eagle", id = 24 },
    { name = "Shotgun", id = 25 }, { name = "Sawn-off Shotgun", id = 26 }, { name = "Combat Shotgun", id = 27 },
    { name = "Micro-SMG (Uzi)", id = 28 }, { name = "MP5", id = 29 }, { name = "Tec-9", id = 32 },
    { name = "AK-47", id = 30 }, { name = "M4", id = 31 }, { name = "Rifle", id = 33 },
    { name = "Sniper Rifle", id = 34 }, { name = "Rocket Launcher", id = 35 }, { name = "Heat-Seeking Rocket", id = 36 },
    { name = "Flamethrower", id = 37 }, { name = "Minigun", id = 38 }, { name = "Molotov Cocktail", id = 18 },
    { name = "Grenade", id = 16 }, { name = "Satchel Charge", id = 39 }, { name = "Brass Knuckles", id = 1 },
    { name = "Golf Club", id = 2 }, { name = "Nightstick", id = 3 }, { name = "Knife", id = 4 },
    { name = "Baseball Bat", id = 5 }, { name = "Shovel", id = 6 }, { name = "Pool Cue", id = 7 },
    { name = "Katana", id = 8 }, { name = "Chainsaw", id = 9 }, { name = "Dildo", id = 10 },
    { name = "Vibrator", id = 11 }, { name = "Flowers", id = 14 }, { name = "Cane", id = 15 },
    { name = "Spraycan", id = 41 }, { name = "Fire Extinguisher", id = 42 }, { name = "Camera", id = 43 },
    { name = "Parachute", id = 46 },
}

--================================================---
--  THREAD MANAGEMENT
--================================================---

-- This function is called from the main loader script once the game is ready.
function Core.start_threads(state)
    -- All loops now accept the state table
    lua_thread.create(function() Core.main_update_loop(state) end)
    lua_thread.create(function() Core.noclip_multi_loop(state) end)
    lua_thread.create(function() Core.camera_aimbot_loop(state) end)
    lua_thread.create(function() Core.animation_refresher_loop(state) end)
    lua_thread.create(function() Core.esp_render_loop(state) end)
    lua_thread.create(function() Core.teleport_loop(state) end)
    lua_thread.create(function() Core.renderInfoBar(state) end)
    lua_thread.create(function() Core.crasher_loop(state) end)
    lua_thread.create(function() Core.silent_aim_line_loop(state) end)
end

--================================================---
--  MAIN UPDATE LOOPS
--================================================---

function Core.main_update_loop(state)
    local cheats = state.cheats

    while true do
        wait(0)

        if isPlayerPlaying(PLAYER_PED) and not isCharDead(PLAYER_PED) then
            if cheats.airBrake[0] then
                Core.airbrake_logic(state)
                state.runtime.airbrake_was_active = true
            elseif state.runtime.airbrake_was_active then
                if state.runtime.airbrake_last_car and doesVehicleExist(state.runtime.airbrake_last_car) and state.runtime.airbrake_was_in_car then
                    freezeCarPosition(state.runtime.airbrake_last_car, false)
                    setCarCollision(state.runtime.airbrake_last_car, true)
                end
                if not state.runtime.airbrake_was_in_car then
                    freezeCharPosition(PLAYER_PED, false)
                    setCharCollision(PLAYER_PED, true)
                end
                state.runtime.airbrake_was_in_car = false
                state.runtime.airbrake_was_active = false
            end

            if isCharInAnyCar(PLAYER_PED) then
                local car = storeCarCharIsInNoSave(PLAYER_PED)

                if cheats.godModeCar[0] then
                    setCarProofs(car, true, true, true, true, true)
                else
                    setCarProofs(car, false, false, false, false, false)
                end

                if cheats.speedHack[0] and isWidgetPressed(2) and isVehicleOnAllWheels(car) then
                    local vector = { getCarSpeedVector(car) }
                    local heading = getCarHeading(car)
                    local turbo = cheats.speedValue[0] * 0.002
                    local force = { turbo, turbo, turbo }
                    local Sin, Cos = math.sin(-math.rad(heading)), math.cos(-math.rad(heading))

                    if vector[1] > -0.01 and vector[1] < 0.01 then force[1] = 0.0 end
                    if vector[2] > -0.01 and vector[2] < 0.01 then force[2] = 0.0 end
                    if vector[3] < 0 then force[3] = -force[3] end
                    if vector[3] > -2 and vector[3] < 15 then force[3] = 0.0 end
                    if Sin > 0 and vector[1] < 0 then force[1] = -force[1] end
                    if Sin < 0 and vector[1] > 0 then force[1] = -force[1] end
                    if Cos > 0 and vector[2] < 0 then force[2] = -force[2] end
                    if Cos < 0 and vector[2] > 0 then force[2] = -force[2] end

                    applyForceToCar(car, force[1] * Sin, force[2] * Cos, force[3] / 2, 0.0, 0.0, 0.0)
                end

                if cheats.driftMode[0] then
                    if isCharInAnyCar(playerPed) then
                        local car = storeCarCharIsInNoSave(playerPed)
                        local speed = getCarSpeed(car)
                        isCarInAirProper(car)
                        setCarCollision(car, true)

                        if isWidgetPressed(2) and isVehicleOnAllWheels(car) and doesVehicleExist(car) and speed > 5.0 then
                            setCarCollision(car, false)
                            if isCarInAirProper(car) then
                                setCarCollision(car, true)
                            end
                            if isWidgetPressed(5) or isWidgetPressed(WIDGET_VEHICLE_STEER_LEFT) then
                                addToCarRotationVelocity(car, 0, 0, 0.15)
                            elseif isWidgetPressed(6) or isWidgetPressed(WIDGET_VEHICLE_STEER_RIGHT) then
                                addToCarRotationVelocity(car, 0, 0, -0.15)
                            end
                        end
                    end
                end

                if cheats.nitrohack[0] and isWidgetPressed(30) then
                    giveNonPlayerCarNitro(car)
                    while isWidgetPressed(30) do
                        wait(0)
                    end
                end

                if cheats.noflip[0] and not isVehicleOnAllWheels(car) then
                    if not cheats._noflip_timer then cheats._noflip_timer = 0 end
                    cheats._noflip_timer = cheats._noflip_timer + 1
                    if cheats._noflip_timer >= 100 then
                        local x, y, z = getCarCoordinates(car)
                        setCarCoordinates(car, x, y, z + 1.5)
                        setCarHeading(car, getCarHeading(car))
                        cheats._noflip_timer = 0
                    end
                else
                    cheats._noflip_timer = 0
                end

                if cheats.rponce[0] then
                    local ok, id = sampGetVehicleIdByCarHandle(car)
                    if ok then
                        local data = Core.samp_create_sync_data('vehicle', true)
                        data.vehicleId = id
                        data.position.x, data.position.y, data.position.z = getCarCoordinates(car)
                        data.vehicleHealth = 1000
                        data.send()
                    end
                    setCarHealth(car, 1000)
                    fixCar(car)
                    sampAddChatMessage("[Cheats] Kendaraan diperbaiki!", 0x00FF00)
                    cheats.rponce[0] = false
                end

            else -- On Foot
                if cheats.noReload[0] and isCharShooting(PLAYER_PED) then
                    local weapon = getCurrentCharWeapon(PLAYER_PED)
                    Core.sendWeaponRPC(weapon)
                end

                if cheats.cbugde[0] and isCharShooting(PLAYER_PED) and isWidgetPressed(1) then
                    taskDuck(PLAYER_PED, 0)
                    wait(math.floor(cheats.cbugsp[0]))
                    clearCharTasksImmediately(PLAYER_PED)
                end

                if cheats.jumpBoost[0] then
                    if isWidgetDoubletapped(WIDGET_JUMP) then
                        local vx, vy, vz = getCharVelocity(PLAYER_PED)
                        setCharVelocity(PLAYER_PED, vx, vy, cheats.jumpPower[0])
                    end
                end

                if cheats.noFall[0] and (isCharPlayingAnim(PLAYER_PED, 'KO_SKID_BACK') or isCharPlayingAnim(PLAYER_PED, 'FALL_COLLAPSE')) then
                    clearCharTasksImmediately(PLAYER_PED)
                end
            end

            if cheats.reloadspd[0] and isPlayerPlaying(PLAYER_PED) then
                setPlayerFastReload(playerHandle, true)
                local reloadAnims = {
                    "TEC_RELOAD", "COLT45_RELOAD", "COLT45_CROUCHRELOAD", "SAWNOFF_RELOAD", "PYTHON_RELOAD",
                    "PYTHON_CROUCHRELOAD", "RIFLE_LOAD", "RIFLE_CROUCHLOAD", "SILENCED_RELOAD", "UZI_RELOAD", "UZI_CROUCHRELOAD"
                }
                for _, anim in ipairs(reloadAnims) do
                    setCharAnimSpeed(PLAYER_PED, anim, cheats.reloadspeed[0])
                end
            else
                setPlayerFastReload(playerHandle, false)
            end
        end
    end
end

function Core.noclip_multi_loop(state)
    while true do
        wait(250) -- Optimized wait time

        if state.cheats.noclipMulti[0] and isCharInAnyCar(PLAYER_PED) then
            for _, handle in ipairs(getAllVehicles()) do
                local car = getCarCharIsUsing(PLAYER_PED)
                if handle ~= car then
                    setCarCollision(handle, false)
                end
            end
        end
    end
end

function Core.animation_refresher_loop(state)
    while true do
        wait(500)

        if isPlayerPlaying(PLAYER_PED) then
            if state.cheats.fastWalk[0] then Core.toggle_fast_walk(state, true) end
            if state.cheats.fastFire[0] then Core.toggle_fast_fire(state, true) end
            if state.cheats.antiStun[0] then Core.toggle_anti_stun(state, true) end
            if state.cheats.cjRun[0] then Core.toggle_cj_run(state, true) end
        end
    end
end

--================================================---
--  TOGGLE & CHEAT FUNCTIONS
--================================================---

function Core.set_animation_speed_for_list(anim_list, speed)
    if isPlayerPlaying(PLAYER_PED) then
        for _, anim in ipairs(anim_list) do
            setCharAnimSpeed(PLAYER_PED, anim, speed)
        end
    end
end

function Core.toggle_fast_walk(state, enable)
    local speed = enable and state.cheats.fastWalkSpeed[0] or 1.0
    Core.set_animation_speed_for_list(pAnimationWalk, speed)
end

function Core.toggle_fast_fire(state, enable)
    local speed = enable and state.cheats.fastFireSpeed[0] or 1.0
    Core.set_animation_speed_for_list(pGunFireAnimations, speed)
end

function Core.toggle_anti_stun(state, enable)
    if isPlayerPlaying(PLAYER_PED) then
        setCharUsesUpperbodyDamageAnimsOnly(playerHandle, enable)
    end
end

function Core.toggle_cj_run(state, enable)
    if isPlayerPlaying(PLAYER_PED) and enable then
        setAnimGroupForChar(PLAYER_PED, "PLAYER")
    end
end

function Core.toggle_infinity_run(state, enable)
    if isPlayerPlaying(PLAYER_PED) then
        setPlayerNeverGetsTired(playerHandle, enable)
    end
end

function Core.toggle_invisible(state, enable)
    if enable then
        sampAddChatMessage("{00FF00}[ZuphProjects] Invisible ON.", -1)
    else
        sampAddChatMessage("{FF0000}[ZuphProjects] Invisible OFF.", -1)
    end
end

function Core.toggle_god_mode(state, enable)
    if isPlayerPlaying(PLAYER_PED) then
        setCharProofs(PLAYER_PED, enable, enable, enable, enable, enable)
    end
end

function Core.toggle_god_mode_car(state, enable)
    if not enable and isCharInAnyCar(PLAYER_PED) then
        local car = storeCarCharIsInNoSave(PLAYER_PED)
        setCarProofs(car, false, false, false, false, false)
    end
end

--================================================---
--  AIRBRAKE FUNCTIONS
--================================================---

function Core.airbrake_getMoveSpeed(heading, speed)
    local rad_heading = -math.rad(heading)
    return math.sin(rad_heading) * speed, math.cos(rad_heading) * speed
end

function Core.airbrake_setPlayerCarCoordinatesFixed(x, y, z)
    local ox, oy, oz = getCharCoordinates(PLAYER_PED)
    setCharCoordinates(PLAYER_PED, ox, oy, oz)
    local nx, ny, nz = getCharCoordinates(PLAYER_PED)
    local xoff, yoff, zoff = nx - ox, ny - oy, nz - oz
    setCharCoordinates(PLAYER_PED, x - xoff, y - yoff, z - zoff)
end

function Core.airbrake_processSpecialWidgets(state)
    local delta = 0
    local speed = state.cheats.airBrakeSpeed[0]
    if isWidgetPressed(WIDGET_ZOOM_IN) then delta = delta + speed / 2 end
    if isWidgetPressed(WIDGET_ZOOM_OUT) then delta = delta - speed / 2 end
    return delta
end

function Core.airbrake_logic(state)
    local runtime = state.runtime
    local x1, y1, z1 = getActiveCameraCoordinates()
    local x, y, z = getActiveCameraPointAt()
    local angle = -math.rad(getHeadingFromVector2d(x - x1, y - y1))
    local speed = state.cheats.airBrakeSpeed[0]

    if isCharInAnyCar(PLAYER_PED) then
        local car = storeCarCharIsInNoSave(PLAYER_PED)
        if car ~= runtime.airbrake_last_car and runtime.airbrake_last_car and doesVehicleExist(runtime.airbrake_last_car) and runtime.airbrake_was_in_car then
            freezeCarPosition(runtime.airbrake_last_car, false)
            setCarCollision(runtime.airbrake_last_car, true)
        end
        runtime.airbrake_was_in_car = true
        runtime.airbrake_last_car = car
        freezeCarPosition(car, true)
        setCarCollision(car, false)

        local result, var_1, var_2 = isWidgetPressedEx(WIDGET_VEHICLE_STEER_ANALOG, 0)
        local intensity_x = result and var_1 / 127 or 0
        local intensity_y = result and var_2 / 127 or 0

        local cx, cy, cz = getCharCoordinates(PLAYER_PED)
        cx = cx - (math.sin(angle) * speed * intensity_y)
        cy = cy - (math.cos(angle) * speed * intensity_y)
        cx = cx + (math.cos(angle) * speed * intensity_x)
        cy = cy - (math.sin(angle) * speed * intensity_x)
        cz = cz + Core.airbrake_processSpecialWidgets(state)

        Core.airbrake_setPlayerCarCoordinatesFixed(cx, cy, cz)
        setCarHeading(car, math.deg(-angle))

        if intensity_x ~= 0 then restoreCameraJumpcut() end
    else
        if runtime.airbrake_was_in_car and runtime.airbrake_last_car and doesVehicleExist(runtime.airbrake_last_car) then
            freezeCarPosition(runtime.airbrake_last_car, false)
            setCarCollision(runtime.airbrake_last_car, true)
        end
        runtime.airbrake_was_in_car = false
        freezeCharPosition(PLAYER_PED, true)
        setCharCollision(PLAYER_PED, false)

        local result, var_1, var_2 = isWidgetPressedEx(WIDGET_PED_MOVE, 0)
        local intensity_x = result and var_1 / 127 or 0
        local intensity_y = result and var_2 / 127 or 0

        local cx, cy, cz = getCharCoordinates(PLAYER_PED)
        cx = cx - (math.sin(angle) * speed * intensity_y)
        cy = cy - (math.cos(angle) * speed * intensity_y)
        cx = cx + (math.cos(angle) * speed * intensity_x)
        cy = cy - (math.sin(angle) * speed * intensity_x)
        cz = cz + Core.airbrake_processSpecialWidgets(state)

        setCharCoordinatesNoOffset(PLAYER_PED, cx, cy, cz)
        setCharHeading(PLAYER_PED, math.deg(-angle))

        if intensity_x ~= 0 then restoreCameraJumpcut() end
    end
end

--================================================---
--  ESP & RENDERING FUNCTIONS
--================================================---

function Core.esp_render_loop(state)
    local esp = state.esp
    while true do
        wait(0)

        if not esp.showEsp[0] and not esp.vehShow[0] then
            wait(150)
        else
            local color = Core.getEspColorARGB(state)
            local startX = tonumber(esp.lineStartX and esp.lineStartX[0]) or 0
            local startY = tonumber(esp.lineStartY and esp.lineStartY[0]) or 0
            local MAX_DIST = 300.0

            if esp.showEsp[0] then
                local chars = getAllChars()
                local px, py, pz = getCharCoordinates(PLAYER_PED)

                for _, ped in ipairs(chars) do
                    if ped ~= PLAYER_PED and doesCharExist(ped) and not isCharDead(ped) and isCharOnScreen(ped) then
                        local ok, id = sampGetPlayerIdByCharHandle(ped)
                        if ok and sampIsPlayerConnected(id) then
                            local headX, headY, headZ = Core.getBonePosition(ped, 4)
                            local footX, footY, footZ = getCharCoordinates(ped)
                            local dist = getDistanceBetweenCoords3d(px, py, pz, footX, footY, footZ)

                            if dist <= MAX_DIST then
                                local sxTop, syTop = convert3DCoordsToScreen(headX, headY, headZ + 0.3)
                                local sxBottom, syBottom = convert3DCoordsToScreen(footX, footY, footZ - 0.9)
                                if sxTop and syTop then
                                    if esp.showLine[0] then renderDrawLine(startX, startY, sxTop, syTop, 2.0, color) end
                                    if esp.showBox[0] then Core.renderNative3DBox(ped, color, 1.5) end
                                    if esp.showSkeleton[0] then Core.renderNativeSkeleton(ped, color) end
                                    if esp.showDist[0] and sxBottom and syBottom then
                                        local distLabel = string.format("%.1f m", dist)
                                        renderFontDrawText(state.render.esp_font, distLabel, sxTop - 20, syBottom + 5, color)
                                    end
                                    if esp.showBar[0] then Core.renderNativeBars(ped, color) end
                                    if esp.showNametag[0] then
                                        local name = sampGetPlayerNickname(id)
                                        if sampIsPlayerPaused(id) then name = "[AFK] " .. name end
                                        local device = state.runtime.players[id] or "Unknown"
                                        local text = string.format("%s [%d] [%s]", name, id, device)
                                        local text_w = Core.getRenderTextSize(text, state.render.esp_font)
                                        local name_color = bit.bor(bit.band(sampGetPlayerColor(id), 0xFFFFFF), 0xFF000000)
                                        renderFontDrawText(state.render.esp_font, text, sxTop - text_w / 2, syTop - 18, name_color)
                                    end
                                end
                            end
                        end
                    end
                end
            end

            if esp.vehShow[0] then
                local px, py, pz = getCharCoordinates(PLAYER_PED)
                local vehicles = getAllVehicles()
                for _, veh in ipairs(vehicles) do
                    if doesVehicleExist(veh) and isCarOnScreen(veh) then
                        local x, y, z = getCarCoordinates(veh)
                        local dist = getDistanceBetweenCoords3d(px, py, pz, x, y, z)
                        if dist <= 300.0 then
                            local sx, sy = convert3DCoordsToScreen(x, y, z + 1.0)
                            if sx and sy then
                                if esp.vehShowLine[0] then renderDrawLine(startX, startY, sx, sy, 1.0, color) end
                                if esp.vehShowBox[0] then Core.renderNativeVehBox(veh, color) end
                                local text_y_offset = -30
                                if esp.vehShowName[0] then
                                    local name = Core.getVehName(getCarModel(veh))
                                    renderFontDrawText(state.render.esp_font, name, sx + 5, sy + text_y_offset, -1)
                                    text_y_offset = text_y_offset + 12
                                end
                                if esp.vehShowId[0] then
                                    local id_text = "ID: " .. getCarModel(veh)
                                    renderFontDrawText(state.render.esp_font, id_text, sx + 5, sy + text_y_offset, -1)
                                    text_y_offset = text_y_offset + 12
                                end
                                if esp.vehShowHp[0] then
                                    local hp = getCarHealth(veh)
                                    local hp_text = string.format("HP: %d", hp)
                                    renderFontDrawText(state.render.esp_font, hp_text, sx + 5, sy + text_y_offset, -1)
                                    text_y_offset = text_y_offset + 12
                                end
                                if esp.vehShowDist[0] then
                                    local dlabel = string.format("%.1fm", dist)
                                    renderFontDrawText(state.render.esp_font, dlabel, sx + 5, sy + text_y_offset, color)
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end

function Core.safeDrawLine(p1, p2, thickness, color)
    if p1 and p2 and p1.x and p1.y and p2.x and p2.y then
        renderDrawLine(p1.x, p1.y, p2.x, p2.y, thickness, color)
    end
end

function Core.renderNative3DBox(ped, color, thickness)
    if not doesCharExist(ped) then return end
    local tx, ty, tz = getCharCoordinates(ped)
    local foot = { tx, ty, tz - 1.0 }
    local headHeight, w = 1.8, 0.4
    local pts3d = {
        { foot[1] - w, foot[2] - w, foot[3] },
        { foot[1] + w, foot[2] - w, foot[3] },
        { foot[1] - w, foot[2] + w, foot[3] },
        { foot[1] + w, foot[2] + w, foot[3] },
        { foot[1] - w, foot[2] - w, foot[3] + headHeight },
        { foot[1] + w, foot[2] - w, foot[3] + headHeight },
        { foot[1] - w, foot[2] + w, foot[3] + headHeight },
        { foot[1] + w, foot[2] + w, foot[3] + headHeight }
    }
    local pts2d = {}
    for i = 1, 8 do
        local sx, sy = convert3DCoordsToScreen(pts3d[i][1], pts3d[i][2], pts3d[i][3])
        if sx and sy then pts2d[i] = { x = sx, y = sy } end
    end
    for _, line in ipairs({ { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 4 }, { 5, 6 }, { 5, 7 }, { 6, 8 }, { 7, 8 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, { 4, 8 } }) do
        Core.safeDrawLine(pts2d[line[1]], pts2d[line[2]], thickness, color)
    end
end

function Core.renderNativeVehBox(veh, color)
    local corners = { { 1.5, 3, 1 }, { 1.5, -3, 1 }, { -1.5, -3, 1 }, { -1.5, 3, 1 }, { 1.5, 3, -1 }, { 1.5, -3, -1 }, { -1.5, -3, -1 }, { -1.5, 3, -1 } }
    local boxCorners = {}
    for _, offset in ipairs(corners) do
        local wx, wy, wz = getOffsetFromCarInWorldCoords(veh, offset[1], offset[2], offset[3])
        local scx, scy = convert3DCoordsToScreen(wx, wy, wz)
        if not scx or not scy then return end
        table.insert(boxCorners, { x = scx, y = scy })
    end
    for i = 1, 4 do
        local ni = i == 4 and 1 or i + 1
        renderDrawLine(boxCorners[i].x, boxCorners[i].y, boxCorners[ni].x, boxCorners[ni].y, 1.0, color)
        renderDrawLine(boxCorners[i].x, boxCorners[i].y, boxCorners[i + 4].x, boxCorners[i + 4].y, 1.0, color)
    end
    for i = 5, 8 do
        local ni = i == 8 and 5 or i + 1
        renderDrawLine(boxCorners[i].x, boxCorners[i].y, boxCorners[ni].x, boxCorners[ni].y, 1.0, color)
    end
end

function Core.renderNativeBars(ped, hp_color, ap_color)
    if not doesCharExist(ped) or not isCharOnScreen(ped) then return end
    local ok, id = sampGetPlayerIdByCharHandle(ped)
    if not ok then return end

    local hp = math.floor((sampGetPlayerHealth(id) or 0) + 0.5)
    local ap = math.floor((sampGetPlayerArmor(id) or 0) + 0.5)
    hp_color = hp_color or 0xFFFF0000
    ap_color = ap_color or 0xFF00FFFF
    local border_color, text_color = 0xFF000000, 0xFFFFFFFF
    local px, py, pz = getCharCoordinates(ped)
    local footZ, headHeight, w = pz - 1.0, 1.8, 0.4
    local default_font = renderCreateFont("Arial", 12, 0)

    -- HP Bar
    local bx1, by1 = convert3DCoordsToScreen(px + w, py + w, footZ)
    local bx2, by2 = convert3DCoordsToScreen(px + w, py + w, footZ + headHeight)
    if bx1 and by1 and bx2 and by2 then
        renderDrawLine(bx1, by1, bx2, by2, 6.0, border_color)
        if hp > 0 then
            local sx1, sy1 = convert3DCoordsToScreen(px + w, py + w, footZ)
            local sx2, sy2 = convert3DCoordsToScreen(px + w, py + w, footZ + (headHeight * (hp / 100)))
            if sx1 and sy1 and sx2 and sy2 then renderDrawLine(sx1, sy1, sx2, sy2, 4.0, hp_color) end
        end
        renderFontDrawText(default_font, "HP", bx1 + 8, (by1 + by2) / 2, text_color)
    end

    -- AP Bar
    local abx1, aby1 = convert3DCoordsToScreen(px - w, py - w, footZ)
    local abx2, aby2 = convert3DCoordsToScreen(px - w, py - w, footZ + headHeight)
    if abx1 and aby1 and abx2 and aby2 then
        renderDrawLine(abx1, aby1, abx2, aby2, 6.0, border_color)
        if ap >= 1 then
            local sx3, sy3 = convert3DCoordsToScreen(px - w, py - w, footZ)
            local sx4, sy4 = convert3DCoordsToScreen(px - w, py - w, footZ + (headHeight * (ap / 100)))
            if sx3 and sy3 and sx4 and sy4 then renderDrawLine(sx3, sy3, sx4, sy4, 4.0, ap_color) end
        end
        renderFontDrawText(default_font, "AP", abx1 - 22, (aby1 + aby2) / 2, text_color)
    end
end

function Core.renderNativeSkeleton(ped, color)
    local bone_skeleton = { 3, 4, 5, 51, 52, 41, 42, 31, 32, 33, 21, 22, 23, 2 }
    for _, bone in ipairs(bone_skeleton) do
        local x1, y1, z1 = Core.getBonePosition(ped, bone)
        local x2, y2, z2 = Core.getBonePosition(ped, bone + 1)
        local sx1, sy1 = convert3DCoordsToScreen(x1, y1, z1)
        local sx2, sy2 = convert3DCoordsToScreen(x2, y2, z2)
        if sx1 and sy1 and sx2 and sy2 then
            renderDrawLine(sx1, sy1, sx2, sy2, 2.0, color)
        end
    end
end

function Core.getRenderTextSize(text, font)
    if renderGetFontDrawTextLength then
        return renderGetFontDrawTextLength(font, text)
    end
    if renderGetFontDrawTextSize then
        return renderGetFontDrawTextSize(font, text)
    end
    return #text * 5
end

--================================================---
--  AIMHACK FUNCTIONS
--================================================---

function Core.camera_aimbot_loop(state)
    local aimhack = state.aimhack
    local runtime = state.runtime

    while true do
        wait(0)
        local mode = 0
        if SAMemory and SAMemory.camera and SAMemory.camera.aCams and SAMemory.camera.aCams[0] then
            mode = SAMemory.camera.aCams[0].nMode
        end

        if aimhack.camera_enabled[0] and (mode == 7 or mode == 53) then
            local targetIsStillValid = false
            if runtime.aim_currentTarget and doesCharExist(runtime.aim_currentTarget) and not isCharDead(runtime.aim_currentTarget) and isCharOnScreen(runtime.aim_currentTarget) then
                local pPos = vector3d(getCharCoordinates(PLAYER_PED))
                local tx, ty, tz = getCharCoordinates(runtime.aim_currentTarget)
                if isLineOfSightClear(pPos.x, pPos.y, pPos.z, tx, ty, tz, true, false, false, true, false, false, false) then
                    targetIsStillValid = true
                end
            end

            if not targetIsStillValid then
                runtime.aim_currentTarget = Core.findBestTarget(state)
            end

            if runtime.aim_currentTarget then
                local tx, ty, tz = Core.getBonePosition(runtime.aim_currentTarget, Core.getSelectedBone(state))
                if tx then
                    local point = vector3d(tx, ty, tz)
                    if mode == 7 then
                        Core.aimAtPointWithSniperScope(point)
                    elseif mode == 53 then
                        Core.aimAtPointWithM16(point)
                    end
                end
            end
        else
            runtime.aim_currentTarget = nil
        end
    end
end

function Core.findBestTarget(state)
    local aimhack = state.aimhack
    local bestTarget, bestScore = nil, 99999
    local pPos = vector3d(getCharCoordinates(PLAYER_PED))
    local sW, sH = getScreenResolution()
    local cX, cY = sW / 2, sH / 2
    local bone = Core.getSelectedBone(state)
    local fov = (sW / 3) * (aimhack.maxFOV[0] / 2.5)

    local chars = getAllChars()
    for _, ped in ipairs(chars) do
        if ped ~= PLAYER_PED and doesCharExist(ped) and not isCharDead(ped) and isCharOnScreen(ped) then
            local ok, id = sampGetPlayerIdByCharHandle(ped)
            if ok and sampIsPlayerConnected(id) then
                local tx, ty, tz = Core.getBonePosition(ped, bone)
                if tx then
                    local tPos = vector3d(tx, ty, tz)
                    local d3D = (pPos - tPos):length()
                    if d3D < aimhack.maxDistance[0] and isLineOfSightClear(pPos.x, pPos.y, pPos.z, tx, ty, tz, true, false, false, true, false, false, false) then
                        local on, sX, sY = convert3DCoordsToScreenEx(tx, ty, tz)
                        if on then
                            local dCross = math.sqrt((sX - cX) ^ 2 + (sY - cY) ^ 2)
                            local in_fov = not aimhack.showFOV[0] or (dCross < fov)
                            if in_fov then
                                local score = aimhack.targetPriority[0] == 0 and (fov > 0 and dCross / fov or dCross) or d3D
                                if score < bestScore then
                                    bestScore, bestTarget = score, ped
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    return bestTarget
end

function Core.getTargetInFOV(state)
    local aimhack = state.aimhack
    local bestTarget, bestScore = nil, 99999
    local pPos = vector3d(getCharCoordinates(PLAYER_PED))
    local sW, sH = getScreenResolution()
    local cX, cY = sW / 2, sH / 2
    local bone = Core.getSelectedBone(state)
    local maxRange = Core.getWeaponRange(getCurrentCharWeapon(PLAYER_PED))
    local fov = (sW / 3) * (aimhack.maxFOV[0] / 2.5)

    for _, ped in ipairs(getAllChars()) do
        if ped ~= PLAYER_PED and doesCharExist(ped) and not isCharDead(ped) and isCharOnScreen(ped) then
            local ok, id = sampGetPlayerIdByCharHandle(ped)
            if ok and sampIsPlayerConnected(id) then
                local tx, ty, tz = Core.getBonePosition(ped, bone)
                if tx then
                    local tPos = vector3d(tx, ty, tz)
                    local d3D = (pPos - tPos):length()
                    if d3D < maxRange then
                        local visible = not aimhack.antiWall[0] or isLineOfSightClear(pPos.x, pPos.y, pPos.z, tx, ty, tz, true, false, false, true, false, false, false)
                        if visible then
                            local on, sX, sY = convert3DCoordsToScreenEx(tx, ty, tz)
                            if on then
                                local dCross = math.sqrt((sX - cX) ^ 2 + (sY - cY) ^ 2)
                                local in_fov = not aimhack.showFOV[0] or (dCross < fov)
                                if in_fov then
                                    local score = aimhack.targetPriority[0] == 0 and (fov > 0 and dCross / fov or dCross) or d3D
                                    if score < bestScore then bestScore, bestTarget = score, ped end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    return bestTarget
end

function Core.silent_aim_line_loop(state)
    local tarId = -1
    while true do
        wait(0)
        if state.aimhack.silent_enabled[0] and state.runtime.esp_line_enabled[0] then
            local targetPed = Core.getTargetInFOV(state)
            if targetPed then
                local result, id = sampGetPlayerIdByCharHandle(targetPed)
                tarId = result and id or -1
            else
                tarId = -1
            end

            if tarId ~= -1 then
                local ok, ped = sampGetCharHandleBySampPlayerId(tarId)
                if ok and ped and doesCharExist(ped) and not isCharDead(ped) then
                    local tx, ty, tz = Core.getBonePosition(ped, Core.getSelectedBone(state))
                    local spX1, spY1 = Core.getCrosshairPositionOnScreen()
                    local spX2, spY2 = convert3DCoordsToScreen(tx, ty, tz)
                    if spX2 and spY2 then renderDrawLine(spX1, spY1, spX2, spY2, 2.0, 0xFF00FF00) end
                end
            end
        end
    end
end

--================================================---
--  TELEPORT FUNCTIONS
--================================================---

function Core.startTeleport(state, mode)
    local runtime = state.runtime
    if runtime.teleport_state then
        sampAddChatMessage("Teleport sedang berjalan.", 0xFF0000FF)
        return
    end

    local found, tX, tY, tZ
    if mode == 'chp' then
        sampAddChatMessage("Mencari Checkpoint...", 0xFFFF00FF)
        found, tX, tY, tZ = Core.getCheckPoint(state)
    else
        sampAddChatMessage("Mencari Marker...", 0xFFFF00FF)
        found, tX, tY, tZ = Core.getMapMarker()
    end

    if found then
        runtime.tp_coords.cx, runtime.tp_coords.cy, runtime.tp_coords.cz = getCharCoordinates(PLAYER_PED)
        runtime.tp_coords.bx, runtime.tp_coords.by, runtime.tp_coords.bz = tX, tY, tZ
        if not (type(runtime.tp_coords.cx) == 'number' and type(runtime.tp_coords.bx) == 'number') then
            sampAddChatMessage("Error: Koordinat tidak valid.", 0xFF0000FF)
            return
        end
        runtime.teleport_distance = getDistanceBetweenCoords3d(runtime.tp_coords.cx, runtime.tp_coords.cy, runtime.tp_coords.cz, runtime.tp_coords.bx, runtime.tp_coords.by, runtime.tp_coords.bz)
        runtime.teleport_active, runtime.teleport_state = true, true
        runtime.teleport_one_percent = runtime.teleport_distance > 0 and runtime.teleport_distance / 100 or 0
        runtime.teleport_timer = os.clock()
        sampAddChatMessage("Teleport dimulai!", 0x00FF00FF)
    else
        local msg = (mode == 'chp') and "Checkpoint tidak ditemukan!" or "Marker tidak ditemukan!"
        sampAddChatMessage(msg, 0xFF0000FF)
    end
end

function Core.teleport_loop(state)
    while true do
        wait(0)
        if state.runtime.teleport_active then
            if not isCharInAnyCar(PLAYER_PED) then
                Core.onfoot_teleport_step(state)
            else
                Core.incar_teleport_step(state)
            end
        end
    end
end

function Core.onfoot_teleport_step(state)
    local runtime = state.runtime
    local coords = runtime.tp_coords
    runtime.teleport_distance = getDistanceBetweenCoords3d(coords.cx, coords.cy, coords.cz, coords.bx, coords.by, coords.bz)
    local height = coords.bz > coords.cz

    for i = 1, math.floor(65 / 2) do
        if runtime.teleport_distance >= 20 then
            local vec_x, vec_y, vec_z = coords.bx - coords.cx, coords.by - coords.cy, coords.bz - coords.cz
            local length = math.sqrt(vec_x ^ 2 + vec_y ^ 2 + vec_z ^ 2)
            if length > 0 then
                vec_x, vec_y, vec_z = vec_x / length, vec_y / length, vec_z / length
            end

            coords.cx = coords.cx + vec_x * 2
            coords.cy = coords.cy + vec_y * 2
            coords.cz = coords.cz + vec_z * 2
            Core.send_onfoot_sync(coords.cx, coords.cy, coords.cz, height)
            runtime.teleport_distance = getDistanceBetweenCoords3d(coords.cx, coords.cy, coords.cz, coords.bx, coords.by, coords.bz)
            if math.random(0, 5) == 5 then
                wait(state.ini.teleport_settings.Sleep_Onfoot)
            end
        else
            wait(500)
            Core.send_onfoot_sync(coords.bx, coords.by, coords.bz, height)
            setCharCoordinates(PLAYER_PED, coords.bx, coords.by, coords.bz)
            runtime.teleport_active, runtime.teleport_state = false, false
            sampAddChatMessage("Teleport selesai.", 0x00FF00FF)
            return
        end
    end
    wait(state.ini.teleport_settings.LoopWait_Onfoot)
end

function Core.incar_teleport_step(state)
    local runtime = state.runtime
    local coords = runtime.tp_coords
    runtime.teleport_distance = getDistanceBetweenCoords3d(coords.cx, coords.cy, coords.cz, coords.bx, coords.by, coords.bz)
    local height = coords.bz > coords.cz

    for i = 1, math.floor(150 / 5) do
        if runtime.teleport_distance >= 20 then
            local vec_x, vec_y, vec_z = coords.bx - coords.cx, coords.by - coords.cy, coords.bz - coords.cz
            local length = math.sqrt(vec_x ^ 2 + vec_y ^ 2 + vec_z ^ 2)
            if length > 0 then
                vec_x, vec_y, vec_z = vec_x / length, vec_y / length, vec_z / length
            end

            coords.cx = coords.cx + vec_x * 5
            coords.cy = coords.cy + vec_y * 5
            coords.cz = coords.cz + vec_z * 5
            Core.send_incar_sync(coords.cx, coords.cy, coords.cz, height)
            runtime.teleport_distance = getDistanceBetweenCoords3d(coords.cx, coords.cy, coords.cz, coords.bx, coords.by, coords.bz)
            if math.random(0, 5) == 5 then
                wait(state.ini.teleport_settings.Sleep_Incar)
            end
        else
            wait(1000)
            Core.send_incar_sync(coords.bx, coords.by, coords.bz, height)
            setCharCoordinates(1, coords.bx, coords.by, coords.bz)
            runtime.teleport_active, runtime.teleport_state = false, false
            sampAddChatMessage("Teleport selesai.", 0x00FF00FF)
            return
        end
    end
    wait(state.ini.teleport_settings.LoopWait_Incar)
end

--================================================---
--  AUTODRIVE FUNCTIONS
--================================================---

function Core.startAutoPilot(state, mode)
    local ap = state.ap
    if ap.active.autopilot_on[0] then
        printStringNow("~r~Autopilot sudah aktif!", 1000)
        return
    end
    if not isCharInAnyCar(PLAYER_PED) then
        sampAddChatMessage("{FF0000}[AutoDrive]: Anda harus di dalam mobil.", -1)
        return
    end

    ap.active.autopilot_on[0] = true
    ap.active.status_window[0] = true
    ap.status.mode = mode
    sampAddChatMessage("{00FF00}[AutoDrive]: Diaktifkan. Mengemudi...", -1)

    lua_thread.create(function()
        while ap.active.autopilot_on[0] do
            wait(250)
            if not isCharInAnyCar(PLAYER_PED) then
                Core.stopAutoPilot(state, "~y~Anda keluar dari mobil.")
                break
            end

            local car = storeCarCharIsInNoSave(PLAYER_PED)
            local pX, pY, pZ = getCharCoordinates(PLAYER_PED)

            if ap.status.mode == 'master' then
                if state.runtime.checkpointCoords.active then
                    ap.status.target = { x = state.runtime.checkpointCoords.x, y = state.runtime.checkpointCoords.y, z = state.runtime.checkpointCoords.z }
                else
                    Core.stopAutoPilot(state, "~g~Rute selesai!")
                    break
                end
            end

            if ap.status.target then
                ap.status.target_dist = getDistanceBetweenCoords3d(pX, pY, pZ, ap.status.target.x, ap.status.target.y, ap.status.target.z)
                local stop_dist = (ap.status.mode == 'waypoint') and 10.0 or 8.0

                if ap.status.target_dist > stop_dist then
                    taskCarDriveToCoord(PLAYER_PED, car, ap.status.target.x, ap.status.target.y, ap.status.target.z, ap.settings.speed[0], ap.settings.ride_type[0], 0, ap.settings.drive_type[0])
                else
                    if ap.status.mode == 'master' then
                        printStringNow("~y~Checkpoint tercapai, menunggu berikutnya...", 1000)
                    else
                        Core.stopAutoPilot(state, "~g~Tujuan tercapai!")
                        break
                    end
                end
            else
                Core.stopAutoPilot(state, "~r~Target tidak ditemukan.")
                break
            end
        end
    end)
end

function Core.stopAutoPilot(state, message)
    local ap = state.ap
    if not ap.active.autopilot_on[0] then return end

    ap.status.mode = 'none'
    ap.status.target = nil
    ap.active.autopilot_on[0] = false
    ap.active.status_window[0] = false

    if isCharInAnyCar(PLAYER_PED) then
        clearCharTasks(PLAYER_PED)
        taskWarpCharIntoCarAsDriver(PLAYER_PED, storeCarCharIsInNoSave(PLAYER_PED))
    else
        freezeCharPosition(PLAYER_PED, false)
        setCharCollision(PLAYER_PED, true)
    end
    if message then printStringNow(message, 2000) end
end

function Core.getTargetBlipCoordinatesFixed(callback)
    lua_thread.create(function()
        local s, x, y, z = getTargetBlipCoordinates()
        if not s then
            callback(false)
            return
        end
        requestCollision(x, y)
        loadScene(x, y, z)
        wait(150)
        local groundZ = getGroundZFor3dCoord(x, y, z + 50.0)
        callback(true, x, y, groundZ)
    end)
end

--================================================---
--  SAMP EVENT HANDLERS
--================================================---

function Core.onSetPlayerHealth(state, health)
    if state.cheats.godMode[0] or state.runtime.crasher_active[0] or state.runtime.rvanka_active[0] then return false end
    if state.cheats.rponce[0] then
        state.cheats.rponce[0] = false
        return false
    end
end

function Core.onSendTakeDamage(state, playerId, damage, weapon, bodypart)
    if state.cheats.godMode[0] and playerId == sampGetPlayerIdByCharHandle(PLAYER_PED) then
        return false
    end
end

function Core.onSendDamageVehicle(state, vehicleId, panel, door, light, tire)
    if state.cheats.godModeCar[0] then
        return false
    end
end

function Core.onSendPlayerSync(state, data)
    if state.cheats.invisible[0] then data.position.z = data.position.z - 150.0 end
    if state.runtime.teleport_state or state.runtime.crasher_active[0] then return false end

    if state.runtime.rvanka_active[0] then
        local cx, cy, _ = getActiveCameraCoordinates()
        local tx, ty, _ = getActiveCameraPointAt()
        if cx and cy and tx and ty then
            local angle = getHeadingFromVector2d(tx - cx, ty - cy)
            data.moveSpeed = { x = math.sin(-math.rad(angle)) * state.runtime.rvanka_speed[0], y = math.cos(-math.rad(angle)) * state.runtime.rvanka_speed[0], z = 0 }
        end
    end

    if state.cheats.airBrake[0] then
        local speed = state.cheats.airBrakeSpeed[0]
        local mx, my = Core.airbrake_getMoveSpeed(getCharHeading(PLAYER_PED), speed > 1 and 1 or speed)
        data.moveSpeed.x, data.moveSpeed.y = mx, my
    end

    if state.cheats.godMode[0] then data.health, data.armor = 100, 100 end

    if state.cheats.antibh[0] and isWidgetPressed(WIDGET_JUMP) and (data.surfingVehicleId == 65535) then
        data.keysData = bit.band(data.keysData, bit.bnot(0x200))
        return false
    end

    if state.weapon_manager.WEAPON_BYPASS_STATE then
        if state.weapon_manager.WEAPON_BYPASS_PACKET < 3 then
            sampSendTakeDamage(0, 1.3, 0, 3)
            state.weapon_manager.WEAPON_BYPASS_PACKET = state.weapon_manager.WEAPON_BYPASS_PACKET + 1
            data.position.z, data.moveSpeed.z = tonumber('NaN'), 0
            sampSendTakeDamage(0, 1.3, 0, 3)
        else
            state.weapon_manager.WEAPON_BYPASS_PACKET = 0
            data.weapon = 0
        end
    end
end

function Core.onSendVehicleSync(state, data)
    if state.runtime.teleport_state then return false end

    local runtime = state.runtime
    if runtime.cslap_main_enabled[0] and runtime.cslap_pzdc_active[0] then
        local targetId = runtime.cslap_target_id[0]
        if sampIsPlayerConnected(targetId) then
            local ok, handle = sampGetCharHandleBySampPlayerId(targetId)
            if ok and doesCharExist(handle) then
                local x, y, z = getCharCoordinates(handle)
                data.position = { x = x + cslap_offset.x, y = y + cslap_offset.y, z = z + cslap_offset.z }
                data.moveSpeed = { x = 0.0, y = 0.0, z = 0.4 }
            end
        end
    elseif state.cheats.airBrake[0] then
        local speed = state.cheats.airBrakeSpeed[0]
        local mx, my = Core.airbrake_getMoveSpeed(getCharHeading(PLAYER_PED), speed > 2 and 2 or speed)
        data.moveSpeed.x, data.moveSpeed.y = mx, my
    end

    if state.cheats.godMode[0] then data.playerHealth = 100 end
    if state.cheats.godModeCar[0] then data.vehicleHealth = 1000 end
end

function Core.onSendBulletSync(state, data)
    if not state.aimhack.silent_enabled[0] or not data.weaponId or state.runtime.rvanka_active[0] then return end

    local targetPed = Core.getTargetInFOV(state)
    if targetPed then
        local result, id = sampGetPlayerIdByCharHandle(targetPed)
        if result and not isCharDead(targetPed) then
            Core.sendWeaponSync(id)
            local tx, ty, tz = Core.getBonePosition(targetPed, Core.getSelectedBone(state))
            local px, py, pz = getCharCoordinates(PLAYER_PED)
            local randX, randY, randZ = math.random(-3, 3) / 100, math.random(-3, 3) / 100, math.random(-3, 3) / 100
            data.origin = { x = px + randX, y = py + randY, z = pz + 0.5 }
            data.target = { x = tx + randX, y = ty + randY, z = tz + randZ }
            data.center = { x = randX, y = randY, z = randZ }
            data.targetType, data.targetId = 1, id
            sampSendGiveDamage(id, Core.getWeapon().damage, data.weaponId, Core.getSelectedBone(state) + 2)
        end
    end
end

-- Other Events
function Core.onSetCheckpoint(state, position, size)
    state.runtime.checkpointCoords = { active = true, x = position.x, y = position.y, z = position.z }
end

function Core.onDisableCheckpoint(state)
    state.runtime.checkpointCoords.active = false
end

function Core.onSetRaceCheckpoint(state, type, position, nextPosition, radius)
    state.runtime.checkpointCoords = { active = true, x = position.x, y = position.y, z = position.z }
end

function Core.onDisableRaceCheckpoint(state)
    state.runtime.checkpointCoords.active = false
end

function Core.onServerMessage(state, color, text)
    if state.runtime.enabled_translator[0] and text and text ~= "" then
        lua_thread.create(function()
            local url = api_url .. state.runtime.target_lang .. "&q=" .. Core.urlencode(text)
            local body = {}
            local res, code = http.request { url = url, sink = ltn12.sink.table(body) }
            if res and code == 200 then
                local translated = Core.parseGoogleTranslate(table.concat(body))
                if translated and translated ~= text then
                    if not state.runtime.show_translator_log[0] then
                        sampAddChatMessage("[T]: " .. translated, 0xAAAAFFFF)
                    end
                    table.insert(state.runtime.translator_log, { original = text, translated = translated })
                    if #state.runtime.translator_log > 50 then table.remove(state.runtime.translator_log, 1) end
                end
            end
        end)
    end
end

-- Blocking Events
function Core.onSetPlayerPos(state) if state.runtime.teleport_state or state.runtime.crasher_active[0] then return false end end
function Core.onClearPlayerAnimation(state) if state.runtime.teleport_state or state.runtime.crasher_active[0] then return false end end
function Core.onSetVehiclePosition(state) if state.runtime.teleport_state or state.runtime.crasher_active[0] then return false end end
function Core.onPlayerDeathNotification(state, killer, killed, reason) if state.cheats.godMode[0] and killed == sampGetPlayerIdByCharHandle(PLAYER_PED) then return false end end
function Core.onSetCameraBehindPlayer(state) if state.runtime.crasher_active[0] or state.runtime.rvanka_active[0] then return false end end
function Core.onTogglePlayerControllable(state, isControllable) if state.runtime.crasher_active[0] or state.runtime.rvanka_active[0] then return false end end
function Core.onSetPlayerArmour(state, armour) if state.runtime.crasher_active[0] then return false end end
function Core.onSendRequestSpawn(state) if state.runtime.rvanka_active[0] then return false end end
function Core.onSendRequestClass(state, classId) if state.cheats.godMode[0] or state.runtime.rvanka_active[0] then return false end end
function Core.onSendWastedNotification(state) if state.runtime.rvanka_active[0] then return false end end

-- Device Detection Events
function Core.onUnoccupiedSync(state, id, data) state.runtime.players[id] = "PC" end
function Core.onPlayerSync(state, id, data)
    if data.keysData == 160 or (data.specialAction ~= 0 and data.specialAction ~= 1) then
        state.runtime.players[id] = "PC"
    end
    if data.leftRightKeys and data.leftRightKeys ~= 128 and data.leftRightKeys ~= 65408 then
        state.runtime.players[id] = "Mobile"
    elseif state.runtime.players[id] ~= "Mobile" then
        state.runtime.players[id] = "PC"
    end
    if data.upDownKeys and data.upDownKeys ~= 128 and data.upDownKeys ~= 65408 then
        state.runtime.players[id] = "Mobile"
    elseif state.runtime.players[id] ~= "Mobile" then
        state.runtime.players[id] = "PC"
    end
end
function Core.onVehicleSync(state, id, vehid, data)
    if data.leftRightKeys and data.leftRightKeys ~= 0 and data.leftRightKeys ~= 128 and data.leftRightKeys ~= 65408 then
        state.runtime.players[id] = "Mobile"
    end
end
function Core.onPlayerQuit(state, id) state.runtime.players[id] = nil end

--================================================---
--  UTILITY & HELPER FUNCTIONS
--================================================---

function Core.openLink(link)
    gta._Z12AND_OpenLinkPKc(link)
end

-- UPDATER
function Core.cekUpdateOnline(state)
    lua_thread.create(function()
        local body, code = http.request(state.updater.version_url)
        if code == 200 and body then
            state.updater.latest_version = body:match("([%d%.]+)") or "?.?.?"
            if Core.compareVersions(state.updater.script_version, state.updater.latest_version) < 0 then
                state.updater.update_available = true
                state.gui.showUpdatePrompt[0] = true
            else
                state.updater.update_available = false
                state.gui.showUpToDatePopup[0] = true
            end
        else
            sampAddChatMessage("[Updater] Gagal mengecek versi. HTTP: " .. tostring(code), 0xFF0000)
        end
    end)
end

function Core.updateScript(state)
    lua_thread.create(function()
        sampAddChatMessage("[Updater] Mengunduh update...", 0x00FF00)
        local body, code = http.request(state.updater.script_url)
        if code == 200 and body then
            local f = io.open(thisScript().path, "w")
            if f then
                f:write(body)
                f:close()
                sampAddChatMessage("[Updater] Update berhasil ke v" .. state.updater.latest_version .. ". Reloading...", 0x00FF00)
                wait(500)
                reloadScripts()
            else
                sampAddChatMessage("[Updater] Gagal menyimpan script!", 0xFF0000)
            end
        else
            sampAddChatMessage("[Updater] Gagal mengunduh file. HTTP: " .. tostring(code), 0xFF0000)
        end
    end)
end

function Core.compareVersions(v1, v2)
    local a, b = {}, {}
    for n in v1:gmatch("(%d+)") do table.insert(a, tonumber(n)) end
    for n in v2:gmatch("(%d+)") do table.insert(b, tonumber(n)) end
    for i = 1, math.max(#a, #b) do
        local x, y = a[i] or 0, b[i] or 0
        if x < y then
            return -1
        elseif x > y then
            return 1
        end
    end
    return 0
end

-- WEAPON MANAGER
function Core.get_weapon_list()
    return weapon_list
end

function Core.give_selected_weapon(state)
    if isCharInAnyCar(PLAYER_PED) then
        sampAddChatMessage("{FF0000}Tidak bisa memberi senjata di dalam kendaraan.", -1)
        return
    end
    local wm_state = state.weapon_manager
    local selected_weapon = weapon_list[wm_state.selected_weapon_idx[0] + 1]
    if selected_weapon then
        local weapon_id, ammo_count = selected_weapon.id, wm_state.ammo[0]
        local model_id = getWeapontypeModel(weapon_id)
        if model_id > 0 then
            requestModel(model_id)
            loadAllModelsNow()
        end
        giveWeaponToChar(PLAYER_PED, weapon_id, ammo_count)
        sampAddChatMessage(string.format("{00FF00}Memberikan %s dengan %d ammo.", selected_weapon.name, ammo_count), -1)
    end
end

-- CRASHER
function Core.crasher_loop(state)
    local runtime = state.runtime
    while true do
        wait(300)
        if runtime.crasher_active[0] then
            local data = Core.crasher_create_sync_data("player")
            data.keysData, data.weapon = 128, 40
            if runtime.crasher_step then
                data.position.x, data.position.y = 200000.0, 200000.0
            end
            data.send()
            runtime.crasher_step = not runtime.crasher_step

            local aim = Core.crasher_create_sync_data("aim")
            aim.camMode = crasher_camModes[runtime.crasher_myCamMode]
            aim.send()
            runtime.crasher_myCamMode = runtime.crasher_myCamMode + 1
            if runtime.crasher_myCamMode > #crasher_camModes then
                runtime.crasher_myCamMode = 1
            end
            printStringNow("CRASH ATTACK MODE - " .. runtime.crasher_myCamMode, 1000)
        end
    end
end

-- GENERAL UTILITIES
function Core.join_argb(a, r, g, b)
    return bit.bor(b, bit.lshift(g, 8), bit.lshift(r, 16), bit.lshift(a, 24))
end

function Core.rainbow(s, a, o)
    local c = os.clock() + (o or 0)
    local r = math.floor(math.sin(c * s) * 127 + 128)
    local g = math.floor(math.sin(c * s + 2) * 127 + 128)
    local b = math.floor(math.sin(c * s + 4) * 127 + 128)
    return r, g, b, a
end

function Core.getBonePosition(ped, bone)
    local ptr = ffi.cast('void*', getCharPointer(ped))
    local pos = ffi.new("RwV3d[1]")
    gta._ZN4CPed15GetBonePositionER5RwV3djb(ptr, pos, bone, false)
    return pos[0].x, pos[0].y, pos[0].z
end

function Core.getCheckPoint(state)
    if state.runtime.checkpointCoords.active then
        return true, state.runtime.checkpointCoords.x, state.runtime.checkpointCoords.y, state.runtime.checkpointCoords.z
    end
    return false, 0, 0, 0
end

function Core.getMapMarker()
    return getTargetBlipCoordinates()
end

function Core.getSelectedBone(state)
    local c = state.aimhack.selectedBoneInt[0]
    if c == 0 then
        return BONE.HEAD
    elseif c == 2 then
        return BONE.GROIN
    end
    return BONE.CHEST
end

function Core.getCrosshairPositionOnScreen()
    local sw, sh = getScreenResolution()
    local cx = sw * (0.5 + 0.013)
    local cy = sh * (0.5 - 0.05)
    return cx, cy
end

function Core.getCameraRotation()
    local cam = SAMemory.camera
    return cam.aCams[0].fHorizontalAngle, cam.aCams[0].fVerticalAngle
end

function Core.setCameraRotation(phi, theta)
    local cam = SAMemory.camera
    cam.aCams[0].fHorizontalAngle = phi
    cam.aCams[0].fVerticalAngle = theta
end

function Core.convertCartesianCoordinatesToSpherical(point)
    local cam = vector3d(getActiveCameraCoordinates())
    local vec = point - cam
    local r = vec:length()
    if r == 0 then return 0, 0 end
    local phi = math.atan2(vec.y, vec.x)
    local theta = math.acos(vec.z / r)
    if phi > 0 then
        phi = phi - math.pi
    else
        phi = phi + math.pi
    end
    theta = math.pi / 2 - theta
    return phi, theta
end

function Core.aimAtPointWithSniperScope(point)
    local phi, theta = Core.convertCartesianCoordinatesToSpherical(point)
    Core.setCameraRotation(phi, theta)
end

function Core.getCrosshairRotation(dist)
    dist = dist or 10
    local x, y = Core.getCrosshairPositionOnScreen()
    local p3d = vector3d(convertScreenCoordsToWorld3D(x, y, dist))
    return Core.convertCartesianCoordinatesToSpherical(p3d)
end

function Core.aimAtPointWithM16(point)
    local pPhi, pTheta = Core.convertCartesianCoordinatesToSpherical(point)
    local cPhi, cTheta = Core.getCameraRotation()
    local xPhi, xTheta = Core.getCrosshairRotation()
    local rPhi = cPhi + (pPhi - xPhi)
    local rTheta = cTheta + (pTheta - xTheta)
    Core.setCameraRotation(rPhi, rTheta)
end

-- TRANSLATOR UTILS
function Core.urlencode(str)
    if not str then return "" end
    str = str:gsub("\n", "\r\n"):gsub("([^%w%-_.~])", function(c)
        return string.format("%%%02X", string.byte(c))
    end)
    return str
end

function Core.parseGoogleTranslate(json)
    if not json then return nil end
    local match = json:match('%[%[%["(.-)"')
    if match then
        return match:gsub('\\"', '"'):gsub('\\n', '\n')
    end
    return nil
end

-- INFO BAR
function Core.renderInfoBar(state)
    while true do
        wait(0)
        local runtime = state.runtime
        if state.ini.teleport_settings.RenderBar_State and runtime.teleport_state then
            local r, g, b, a = Core.rainbow(0.5, 250, 1)
            local resX, resY = getScreenResolution()
            local x, y = resX / 2, resY / 1.5
            if runtime.teleport_state and runtime.teleport_one_percent > 0 then
                local time = os.clock() - runtime.teleport_timer
                local percent = 100 - math.floor(runtime.teleport_distance / runtime.teleport_one_percent)
                percent = math.min(math.max(percent, 0), 100)
                renderFontDrawText(state.render.font, string.format('Teleporting... | %d%% | %.1fs', percent, time), x, y, Core.join_argb(a, r, g, b))
            end
        end
    end
end

-- TROLL UTILS
function Core.safeStoreCar(char)
    if char and doesCharExist(char) then
        local ok, veh = pcall(storeCarCharIsInNoSave, char)
        if ok and veh and doesVehicleExist(veh) then
            return veh
        end
    end
    return nil
end

function Core.troll_isValidId(id)
    return id >= 0 and id <= 1999
end

function Core.troll_getCarHandle(id)
    local ok, car = sampGetCarHandleBySampVehicleId(id)
    return ok and car or nil
end

function Core.troll_tpToCar(car)
    local x, y, z = getCarCoordinates(car)
    setCharCoordinates(PLAYER_PED, x, y, z + 1.0)
end

function Core.troll_seatDriver(id)
    lua_thread.create(function()
        local car = Core.troll_getCarHandle(id)
        if not car then
            sampAddChatMessage("[Troll] Mobil " .. id .. " tidak ditemukan.", 0xFF0000)
            return
        end
        Core.troll_tpToCar(car)
        sampSendEnterVehicle(id, false)
        wait(500)
        taskWarpCharIntoCarAsDriver(PLAYER_PED, car)
        wait(200)
    end)
end

function Core.troll_seatPassenger(id)
    lua_thread.create(function()
        local car = Core.troll_getCarHandle(id)
        if not car then
            sampAddChatMessage("[Troll] Mobil " .. id .. " tidak ditemukan.", 0xFF0000)
            return
        end
        Core.troll_tpToCar(car)
        sampSendEnterVehicle(id, true)
        wait(500)
        taskWarpCharIntoCarAsPassenger(PLAYER_PED, car, 0)
        wait(200)
    end)
end

function Core.troll_unlockDoors(id)
    local car = Core.troll_getCarHandle(id)
    if car then
        lockCarDoors(car, 0)
        sampAddChatMessage("[Troll] Pintu mobil " .. id .. " telah dibuka.", 0x00FF00)
    else
        sampAddChatMessage("[Troll] Mobil tidak ditemukan.", 0xFF0000)
    end
end

function Core.troll_buryPlayerCar(targetId)
    lua_thread.create(function()
        local ok, ped = sampGetCharHandleBySampPlayerId(targetId)
        if not ok or not doesCharExist(ped) then
            sampAddChatMessage('[Troll] Player ID tidak valid.', 0xFF0000)
            return
        end
        local targetVeh = Core.safeStoreCar(ped)
        local myVeh = Core.safeStoreCar(PLAYER_PED)
        if not targetVeh then
            sampAddChatMessage('[Troll] Target tidak di dalam mobil!', 0xFF0000)
            return
        elseif not myVeh then
            sampAddChatMessage('[Troll] Kamu harus di dalam mobil!', 0xFF0000)
            return
        end
        sampAddChatMessage('[Troll] Mengubur mobil player ' .. targetId .. '...', 0xFFFF00)
        warpCharIntoCar(PLAYER_PED, targetVeh)
        wait(400)
        local cx, cy, cz = getCarCoordinates(targetVeh)
        setCarCoordinates(targetVeh, cx, cy, cz - 15)
        sampAddChatMessage('[Troll] Mobil target dikubur!', 0x00FF00)
        wait(400)
        warpCharIntoCar(PLAYER_PED, myVeh)
        sampAddChatMessage('[Troll] Proses selesai.', 0x00FF00)
    end)
end

function Core.sendWeaponRPC(weaponId)
    local bs = raknetNewBitStream()
    raknetBitStreamWriteInt32(bs, weaponId)
    raknetBitStreamWriteInt32(bs, 0)
    raknetEmulRpcReceiveBitStream(22, bs)
    raknetDeleteBitStream(bs)
end

-- SYNC DATA UTILS
function Core.send_incar_sync(x, y, z, height)
    local d = Core.samp_create_sync_data("vehicle")
    d.quaternion[1], d.quaternion[2], d.quaternion[3] = math.random(-1, 1), math.random(-1, 1), math.random(-1, 1)
    d.moveSpeed = { 0, 0, height and 0.25 or -0.25 }
    d.vehicleHealth = 1500
    d.playerHealth = getCharHealth(PLAYER_PED)
    d.landingGearState = true
    d.position = { x, y, z }
    d.send()
end

function Core.send_onfoot_sync(x, y, z, height)
    local d = Core.samp_create_sync_data("player")
    d.quaternion[1], d.quaternion[3] = math.random(-1, 1), math.random(-1, 1)
    d.moveSpeed = { 0.7, 0.7, height and -0.7 or 0.7 }
    d.specialAction = 4
    d.animationId = 1018
    d.animationFlags = 12211
    d.armor = getCharArmour(PLAYER_PED)
    d.weapon = getCurrentCharWeapon(PLAYER_PED)
    d.health = getCharHealth(PLAYER_PED)
    d.position = { x, y, z }
    d.send()
end

function Core.samp_create_sync_data(sync_type, copy)
    local traits = {
        player = { "PlayerSyncData", raknet.PACKET.PLAYER_SYNC, sampStorePlayerOnfootData },
        vehicle = { "VehicleSyncData", raknet.PACKET.VEHICLE_SYNC, sampStorePlayerIncarData }
    }
    local info = traits[sync_type]
    local d = ffi.new("struct " .. info[1], {})
    local p = tonumber(ffi.cast("uintptr_t", ffi.new("struct " .. info[1] .. "*", d)))

    if copy then
        local f = info[3]
        if f then
            local _, id
            if copy == true then
                _, id = sampGetPlayerIdByCharHandle(PLAYER_PED)
            else
                id = tonumber(copy)
            end
            if id then f(id, p) end
        end
    end

    local s = function()
        local b = raknetNewBitStream()
        raknetBitStreamWriteInt8(b, info[2])
        raknetBitStreamWriteBuffer(b, p, ffi.sizeof(d))
        raknetSendBitStream(b, 0, 3, 1)
        raknetDeleteBitStream(b)
    end

    return setmetatable({ send = s }, {
        __index = function(t, i) return d[i] end,
        __newindex = function(t, i, v) d[i] = v end
    })
end

function Core.crasher_create_sync_data(sync, copy)
    local t = {
        player = { 'PlayerSyncData', raknet.PACKET.PLAYER_SYNC, sampStorePlayerOnfootData },
        vehicle = { 'VehicleSyncData', raknet.PACKET.VEHICLE_SYNC, sampStorePlayerIncarData },
        passenger = { 'PassengerSyncData', raknet.PACKET.PASSENGER_SYNC, sampStorePlayerPassengerData },
        aim = { 'AimSyncData', raknet.PACKET.AIM_SYNC, sampStorePlayerAimData },
        trailer = { 'TrailerSyncData', raknet.PACKET.TRAILER_SYNC, sampStorePlayerTrailerData },
        unoccupied = { 'UnoccupiedSyncData', raknet.PACKET.UNOCCUPIED_SYNC, nil },
        bullet = { 'BulletSyncData', raknet.PACKET.BULLET_SYNC, nil },
        spectator = { 'SpectatorSyncData', raknet.PACKET.SPECTATOR_SYNC, nil }
    }
    local i = t[sync]
    local d = ffi.new('struct ' .. i[1], {})
    local p = tonumber(ffi.cast('uintptr_t', ffi.new('struct ' .. i[1] .. '*', d)))

    if copy then
        local f = i[3]
        if f then
            local _, id
            if copy == true then
                _, id = sampGetPlayerIdByCharHandle(PLAYER_PED)
            else
                id = tonumber(copy)
            end
            if id then f(id, p) end
        end
    end

    local s = function()
        local b = raknetNewBitStream()
        raknetBitStreamWriteInt8(b, i[2])
        raknetBitStreamWriteBuffer(b, p, ffi.sizeof(d))
        raknetSendBitStreamEx(b, require('sampfuncs').HIGH_PRIORITY, require('sampfuncs').UNRELIABLE_SEQUENCED, 1)
        raknetDeleteBitStream(b)
    end

    return setmetatable({ send = s }, {
        __index = function(t, k) return d[k] end,
        __newindex = function(t, k, v) d[k] = v end
    })
end

-- SILENT AIM WEAPON UTILS
local silent_weapons = {}

function Core.initWeapon(id, name, dist, damage)
    silent_weapons[id] = { id = id, name = name, dist = dist, damage = damage }
end

Core.initWeapon(22, "Colt 45", 35.0, 8.25)
Core.initWeapon(23, "Silenced 9mm", 35.0, 13.2)
Core.initWeapon(24, "Desert Eagle", 35.0, 46.2)
Core.initWeapon(25, "Shotgun", 40.0, 30)
Core.initWeapon(26, "Sawnoff Shotgun", 35.0, 30)
Core.initWeapon(27, "Combat Shotgun", 40.0, 30)
Core.initWeapon(28, "Uzi", 35.0, 6.6)
Core.initWeapon(29, "MP5", 45.0, 8.25)
Core.initWeapon(30, "AK-47", 70.0, 9.9)
Core.initWeapon(31, "M4", 90.0, 9.9)
Core.initWeapon(32, "Tec-9", 35.0, 6.6)
Core.initWeapon(33, "Country Rifle", 95.0, 24.75)
Core.initWeapon(34, "Sniper Rifle", 320.0, 41)
Core.initWeapon(38, "Minigun", 75.0, 46.2)

local last_target = -1

function Core.sendWeaponSync(id)
    if last_target == id then return end

    local w = Core.getWeapon()
    if not w then return end

    local b = raknetNewBitStream()
    raknetBitStreamWriteInt8(b, 204)
    raknetBitStreamWriteInt16(b, id)
    raknetBitStreamWriteInt16(b, 65535)
    raknetBitStreamWriteInt8(b, getWeapontypeSlot(w.id))
    raknetBitStreamWriteInt8(b, w.id)
    raknetBitStreamWriteInt16(b, getAmmoInCharWeapon(PLAYER_PED, w.id))
    raknetSendBitStream(b)
    raknetDeleteBitStream(b)
    last_target = id
end

function Core.getWeapon()
    return silent_weapons[getCurrentCharWeapon(PLAYER_PED)]
end

function Core.getWeaponRange(weaponId)
    local r = {
        [22] = 35.0, [23] = 35.0, [24] = 35.0, [25] = 40.0, [26] = 35.0,
        [27] = 40.0, [28] = 50.0, [29] = 50.0, [30] = 90.0, [31] = 90.0,
        [32] = 50.0, [33] = 100.0, [34] = 300.0
    }
    return r[weaponId] or 50.0
end

-- MISC
function Core.HSVtoRGB(h, s, v)
    if s == 0 then return v, v, v end
    local i = math.floor(h * 6)
    local f = (h * 6) - i
    local p = v * (1 - s)
    local q = v * (1 - s * f)
    local t = v * (1 - s * (1 - f))
    i = i % 6
    if i == 0 then
        return v, t, p
    elseif i == 1 then
        return q, v, p
    elseif i == 2 then
        return p, v, t
    elseif i == 3 then
        return p, q, v
    elseif i == 4 then
        return t, p, v
    else
        return v, p, q
    end
end

function Core.getEspColorARGB(state)
    local esp = state.esp
    if esp.useRainbow[0] then
        state.runtime.rainbowHue = state.runtime.rainbowHue + 0.005
        if state.runtime.rainbowHue > 1 then state.runtime.rainbowHue = 0 end
        local r, g, b = Core.HSVtoRGB(state.runtime.rainbowHue, 1, 1)
        return Core.join_argb(255, r * 255, g * 255, b * 255)
    else
        return Core.join_argb(math.floor(esp.colorFloat[3] * 255), math.floor(esp.colorFloat[0] * 255), math.floor(esp.colorFloat[1] * 255), math.floor(esp.colorFloat[2] * 255))
    end
end

local vehNames = {
    [400] = "Landstalker", [401] = "Bravura", [402] = "Buffalo", [403] = "Linerunner", [404] = "Perenniel",
    [405] = "Sentinel", [406] = "Dumper", [407] = "Firetruck", [408] = "Trashmaster", [409] = "Stretch",
    [410] = "Manana", [411] = "Infernus", [412] = "Voodoo", [413] = "Pony", [414] = "Mule", [415] = "Cheetah",
    [416] = "Ambulance", [417] = "Leviathan", [418] = "Moonbeam", [419] = "Esperanto", [420] = "Taxi",
    [421] = "Washington", [422] = "Bobcat", [423] = "Mr Whoopee", [424] = "BF Injection", [425] = "Hunter",
    [426] = "Premier", [427] = "Enforcer", [428] = "Securicar", [429] = "Banshee", [430] = "Predator",
    [431] = "Bus", [432] = "Rhino", [433] = "Barracks", [434] = "Hotknife", [435] = "Artic Trailer 1",
    [436] = "Previon", [437] = "Coach", [438] = "Cabbie", [439] = "Stallion", [440] = "Rumpo",
    [441] = "RC Bandit", [442] = "Romero", [443] = "Packer", [444] = "Monster", [445] = "Admiral",
    [446] = "Squalo", [447] = "Seasparrow", [448] = "Pizzaboy", [449] = "Tram", [450] = "Artic Trailer 2",
    [451] = "Turismo", [452] = "Speeder", [453] = "Reefer", [454] = "Tropic", [455] = "Flatbed",
    [456] = "Yankee", [457] = "Caddy", [458] = "Solair", [459] = "Berkley's RC Van", [460] = "Skimmer",
    [461] = "PCJ-600", [462] = "Faggio", [463] = "Freeway", [464] = "RC Baron", [465] = "RC Raider",
    [466] = "Glendale", [467] = "Oceanic", [468] = "Sanchez", [469] = "Sparrow", [470] = "Patriot",
    [471] = "Quad", [472] = "Coastguard", [473] = "Dinghy", [474] = "Hermes", [475] = "Sabre",
    [476] = "Rustler", [477] = "ZR-350", [478] = "Walton", [479] = "Regina", [480] = "Comet", [481] = "BMX",
    [482] = "Burrito", [483] = "Camper", [484] = "Marquis", [485] = "Baggage", [486] = "Dozer",
    [487] = "Maverick", [488] = "News Chopper", [489] = "Rancher", [490] = "FBI Rancher", [491] = "Virgo",
    [492] = "Greenwood", [493] = "Jetmax", [494] = "Hotring", [495] = "Sandking", [496] = "Blista Compact",
    [497] = "Police Maverick", [498] = "Boxville", [499] = "Benson", [500] = "Mesa", [501] = "RC Goblin",
    [502] = "Hotring A", [503] = "Hotring B", [504] = "Bloodring Banger", [505] = "Rancher Lure",
    [506] = "Super GT", [507] = "Elegant", [508] = "Journey", [509] = "Bike", [510] = "Mountain Bike",
    [511] = "Beagle", [512] = "Cropduster", [513] = "Stuntplane", [514] = "Petrol Tanker", [515] = "Roadtrain",
    [516] = "Nebula", [517] = "Majestic", [518] = "Buccaneer", [519] = "Shamal", [520] = "Hydra",
    [521] = "FCR-900", [522] = "NRG-500", [523] = "HPV1000", [524] = "Cement Truck", [525] = "Tow Truck",
    [526] = "Fortune", [527] = "Cadrona", [528] = "FBI Truck", [529] = "Willard", [530] = "Forklift",
    [531] = "Tractor", [532] = "Combine", [533] = "Feltzer", [534] = "Remington", [535] = "Slamvan",
    [536] = "Blade", [537] = "Freight", [538] = "Streak", [539] = "Vortex", [540] = "Vincent",
    [541] = "Bullet", [542] = "Clover", [543] = "Sadler", [544] = "Firetruck LA", [545] = "Hustler",
    [546] = "Intruder", [547] = "Primo", [548] = "Cargobob", [549] = "Tampa", [550] = "Sunrise",
    [551] = "Merit", [552] = "Utility Van", [553] = "Nevada", [554] = "Yosemite", [555] = "Windsor",
    [556] = "Monster A", [557] = "Monster B", [558] = "Uranus", [559] = "Jester", [560] = "Sultan",
    [561] = "Stratum", [562] = "Elegy", [563] = "Raindance", [564] = "RC Tiger", [565] = "Flash",
    [566] = "Tahoma", [567] = "Savanna", [568] = "Bandito", [569] = "Freight Flat", [570] = "Streak Carriage",
    [571] = "Kart", [572] = "Mower", [573] = "Duneride", [574] = "Sweeper", [575] = "Broadway",
    [576] = "Tornado", [577] = "AT-400", [578] = "DFT-30", [579] = "Huntley", [580] = "Stafford",
    [581] = "BF-400", [582] = "Newsvan", [583] = "Tug", [584] = "Trailer Livestock", [585] = "Emperor",
    [586] = "Wayfarer", [587] = "Euros", [588] = "Hotdog", [589] = "Club", [590] = "Trailer Farm",
    [591] = "Trailer Utility", [592] = "Andromada", [593] = "Dodo", [594] = "RC Cam", [595] = "Launch",
    [596] = "Police LS", [597] = "Police SF", [598] = "Police LV", [599] = "Ranger", [600] = "Picador",
    [601] = "S.W.A.T. Van", [602] = "Alpha", [603] = "Phoenix", [604] = "Glendale Damaged",
    [605] = "Sadler Damaged", [606] = "Baggage Trailer A", [607] = "Baggage Trailer B", [608] = "Tug Stairs Trailer",
    [609] = "Boxburg", [610] = "Farm Trailer", [611] = "Utility Trailer"
}

function Core.getVehName(model)
    return vehNames[model] or ("ID:" .. model)
end

return Core