[ML] (error) SphereTest.lua: ...d originalSA by kichiro\moonloader\lib\SphereLibrary.lua:23: attempt to index local 'pos2' (a number value)
stack traceback:
	...d originalSA by kichiro\moonloader\lib\SphereLibrary.lua: in function 'calculateDistance'
	...d originalSA by kichiro\moonloader\lib\SphereLibrary.lua:48: in function 'isWithinRenderDistance'
	...d originalSA by kichiro\moonloader\lib\SphereLibrary.lua:228: in function 'updateRender'
	...\cleaned originalSA by kichiro\moonloader\SphereTest.lua:36: in function <...\cleaned originalSA by kichiro\moonloader\SphereTest.lua:3>
[ML] (error) SphereTest.lua: Script died due to an error. (057C6A34)
```lua
local Vector3D = require("vector3d")
local SphereLibrary = {}
local spheres = {}
local nextSphereId = 1
local mathRad = math.rad
local TWO_PI, PI = mathRad(360), mathRad(180)
local MAX_DETAIL_LEVEL = 20
local DEFAULT_DETAIL_LEVEL = 5
local DEFAULT_COLOR = 0xFFFFFF
local MAX_RENDER_DISTANCE = 1000
local debugMode = false
local function clamp(value, min, max) -- Utility function to clamp a value between min and max
    if value < min then return min end
    if value > max then return max end
    return value
end

local function isValidPed(ped) -- Checks if the character (ped) exists
    return ped ~= nil and doesCharExist(ped)
end

local function calculateDistance(pos1, pos2) -- Calculates the distance between two positions
    local dx = pos1.x - pos2.x
    local dy = pos1.y - pos2.y
    local dz = pos1.z - pos2.z
    return math.sqrt(dx * dx + dy * dy + dz * dz)
end

local function safeGetCharCoordinates(pPed) -- Safely gets character coordinates
    local success, x, y, z = pcall(getCharCoordinates, pPed)
    if success and x and y and z then
        return {
            x = x,
            y = y,
            z = z
        }
    else
        if debugMode then print("Error: Failed to get character coordinates for pPed:", pPed) end
        return {
            x = 0,
            y = 0,
            z = 0
        }
    end
end

local function isWithinRenderDistance(sphereCenter, cameraPos) -- Checks if the sphere is within the render distance from the camera
    local distance = calculateDistance(sphereCenter, cameraPos)
    return distance <= MAX_RENDER_DISTANCE
end

local function addVectors(v1, v2) -- Adds two vectors
    return Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z)
end

local function updatePosition(sphere) -- Updates the sphere's position if it follows a character
    if sphere.followPlayer then
        if sphere.pPed and isValidPed(sphere.pPed) then
            local coords = safeGetCharCoordinates(sphere.pPed)
            sphere.center = Vector3D(coords.x, coords.y, coords.z)
        else
            if debugMode then print("Warning: Invalid pPed in sphere ID:", sphere.id) end
        end
    end
end

local function calculatePoint(sphere, phi, theta) -- Calculates a point on the sphere's surface
    local sinPhi = math.sin(phi)
    local x = sphere.radius * sinPhi * math.cos(theta)
    local y = sphere.radius * sinPhi * math.sin(theta)
    local z = sphere.radius * math.cos(phi)
    return addVectors(Vector3D(x, y, z), sphere.center)
end

local function renderLine(point1, point2, width, color, sphereId) -- Renders a line between two 3D points on screen
    local visible1, screenX1, screenY1 = convert3DCoordsToScreenEx(point1.x, point1.y, point1.z)
    local visible2, screenX2, screenY2 = convert3DCoordsToScreenEx(point2.x, point2.y, point2.z)
    if visible1 and visible2 then
        renderDrawLine(screenX1, screenY1, screenX2, screenY2, width, color)
    else
        if debugMode then print(string.format("Line not rendered: Point1 visible=%s, Point2 visible=%s for sphere ID: %s", tostring(visible1), tostring(visible2), sphereId)) end
    end
end

local function renderSphere(sphere) -- Renders the sphere by drawing lines between points
    local stepTheta = TWO_PI / sphere.detailLevel
    local stepPhi = PI / sphere.detailLevel
    local numThetaSteps = sphere.detailLevel
    local numPhiSteps = sphere.detailLevel
    for i = 0, numThetaSteps do
        local theta = i * stepTheta
        for j = 1, numPhiSteps - 1 do
            local phi = j * stepPhi
            local point1 = calculatePoint(sphere, phi, theta)
            local point2 = calculatePoint(sphere, phi, theta + stepTheta)
            local point3 = calculatePoint(sphere, phi + stepPhi, theta)
            renderLine(point1, point2, 1, sphere.color, sphere.id)
            renderLine(point1, point3, 1, sphere.color, sphere.id)
        end
    end
end

local function isPointInSphere(point, center, radius) -- Checks if a point is inside a sphere
    local dx, dy, dz = point.x - center.x, point.y - center.y, point.z - center.z
    return (dx * dx + dy * dy + dz * dz) <= (radius * radius)
end

local function getAllObjects() -- Functions to retrieve all entities in the game world
    local objects = {}
    local maxObjects = 1000 -- Adjust this number based on the maximum possible objects
    for handle = 1, maxObjects do
        if doesObjectExist(handle) then -- Checks if the object handle is valid
            table.insert(objects, handle)
        end
    end
    return objects
end

local function getAllChars()
    local chars = {}
    local maxChars = 256 -- Adjust this number based on the maximum possible characters/peds
    for handle = 0, maxChars - 1 do
        if doesCharExist(handle) then -- Checks if the character handle is valid
            table.insert(chars, handle)
        end
    end
    return chars
end

local function getAllVehicles()
    local vehicles = {}
    local maxVehicles = 500 -- Adjust this number based on the maximum possible vehicles
    for handle = 1, maxVehicles do
        if doesVehicleExist(handle) then -- Checks if the vehicle handle is valid
            table.insert(vehicles, handle)
        end
    end
    return vehicles
end

function SphereLibrary.collectEntitiesInSphere(sphereId) -- SphereLibrary function to collect entities within a sphere
    local sphere = spheres[sphereId]
    if not sphere then
        print(string.format("Warning: Invalid sphereId '%s' provided to collectEntitiesInSphere.", sphereId))
        return {
            objects = {},
            players = {},
            cars = {}
        }
    end

    local entitiesInside = {
        objects = {},
        players = {},
        cars = {}
    }

    local function addEntities(getAllEntities, getCoords, getId, entityKey)
        local allEntities = getAllEntities()
        if type(allEntities) ~= "table" then
            print(string.format("Error: %s did not return a table.", tostring(getAllEntities)))
            return
        end

        for _, entity in ipairs(allEntities) do
            local coords = getCoords(entity)
            if coords then
                local entityPoint = Vector3D(coords.x, coords.y, coords.z)
                if isPointInSphere(entityPoint, sphere.center, sphere.radius) then
                    local success, entityId = pcall(getId, entity)
                    if success and entityId then
                        table.insert(entitiesInside[entityKey], entityId)
                    elseif debugMode then
                        print(string.format("Error: Failed to get ID for entity in '%s'.", entityKey))
                    end
                end
            else
                if debugMode then print(string.format("Warning: Failed to get coordinates for entity in '%s'.", entityKey)) end
            end
        end
    end

    addEntities(getAllObjects, getObjectCoordinates, sampGetObjectSampIdByHandle, "objects")
    addEntities(getAllChars, getCharCoordinates, sampGetPlayerIdByCharHandle, "players")
    addEntities(getAllVehicles, getCarCoordinates, sampGetVehicleIdByCarHandle, "cars")
    if next(entitiesInside.objects) == nil and next(entitiesInside.players) == nil and next(entitiesInside.cars) == nil and debugMode then print(string.format("Info: No entities found within sphere ID: %s.", sphereId)) end
    return entitiesInside
end

function SphereLibrary.createSphere(playerPed, radius, followPlayer, centerX, centerY, centerZ, detailLevel, color) -- SphereLibrary function to create a new sphere
    if type(radius) ~= "number" or radius <= 0 then error("Invalid radius provided to createSphere. Must be a positive number.") end
    if followPlayer and not isValidPed(playerPed) then error("Invalid playerPed provided to createSphere.") end
    centerX = (type(centerX) == "number") and centerX or 0
    centerY = (type(centerY) == "number") and centerY or 0
    centerZ = (type(centerZ) == "number") and centerZ or 0
    detailLevel = (type(detailLevel) == "number" and detailLevel > 0) and clamp(math.floor(detailLevel), 1, MAX_DETAIL_LEVEL) or DEFAULT_DETAIL_LEVEL
    color = (type(color) == "number") and color or DEFAULT_COLOR
    local sphereId = nextSphereId
    nextSphereId = nextSphereId + 1
    spheres[sphereId] = {
        id = sphereId,
        pPed = playerPed,
        radius = radius,
        followPlayer = followPlayer,
        center = Vector3D(centerX, centerY, centerZ),
        detailLevel = detailLevel,
        color = color
    }

    if debugMode then print(string.format("Sphere created with ID: %s", sphereId)) end
    return sphereId
end

function SphereLibrary.removeSphere(sphereId) -- SphereLibrary function to remove a sphere
    if spheres[sphereId] then
        spheres[sphereId] = nil
        if debugMode then print(string.format("Sphere with ID: %s removed.", sphereId)) end
    else
        print(string.format("Warning: Attempted to remove a non-existent sphereId '%s'.", sphereId))
    end
end

function SphereLibrary.updateRender() -- SphereLibrary function to update and render spheres
    local cameraCoords = getActiveCameraCoordinates()
    for _, currentSphere in pairs(spheres) do
        updatePosition(currentSphere)
        if cameraCoords then
            if isWithinRenderDistance(currentSphere.center, cameraCoords) and isPointOnScreen(currentSphere.center.x, currentSphere.center.y, currentSphere.center.z) then renderSphere(currentSphere) end
        else
            if isPointOnScreen(currentSphere.center.x, currentSphere.center.y, currentSphere.center.z) then renderSphere(currentSphere) end
        end
    end
end

function SphereLibrary.setDebugMode(enabled) -- SphereLibrary function to enable or disable debug mode
    debugMode = enabled and true or false
    print(string.format("Debug mode %s.", debugMode and "enabled" or "disabled"))
end
return SphereLibrary
```