Teedle Scripts Documentation

Configuration

Configuration for Advanced AOP.

Configuration

Advanced AOP is configured through config.lua. Below is the current configuration file.

Config File

config.lua
Config = {}

--[[
    Teedle AOP Resource
    Functional AOP + Peacetime system for FiveM.

    REQUIRED:
    - PolyZone must be started before this resource.

    PERMISSIONS:
    - ACE permissions are recommended.
    - Discord staff roles should be mapped to ACE groups through your Discord permissions resource.
]]

Config.Framework = 'qbcore' -- 'qbcore' or 'standalone'

Config.Command = 'aop'
Config.PeacetimeCommand = 'pt'

Config.UseAcePermissions = true
Config.AcePermission = 'teedle.aop'

-- Optional fallback identifiers. Leave empty if you only want ACE.
Config.AdminIdentifiers = {
    -- 'license:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    -- 'discord:123456789012345678',
}

-- Optional QBCore job permission fallback.
-- Example: ['police'] = 8 means police grade 8+ can use AOP commands.
Config.AllowedJobs = {
    -- ['police'] = 8,
}

Config.Permissions = {
    -- true  = staff with ACE/allowed job can ignore AOP enforcement.
    -- false = staff are treated like normal players, useful for testing or stricter staff rules.
    staffBypass = false,
}


Config.QBCoreSpawnProtection = {
    -- Keeps AOP from teleporting/pushing players while qb-spawn/qb-apartments/qb-clothing
    -- are doing new-character setup. This prevents new characters from being ripped out of
    -- their selected apartment before they can create clothing and properly exit.
    enabled = true,

    -- Applied immediately when the AOP resource starts on a QBCore client.
    -- This prevents AOP from grabbing a brand-new character before qb-spawn/apartments opens.
    -- It is cleared early when the player leaves the apartment.
    initialStartupSeconds = 0,

    -- Applied when QBCore reports the player loaded. New-character flows can take a while.
    -- It is cleared early when the player leaves the apartment.
    onPlayerLoadedSeconds = 8,

    -- Applied while the spawn selector/apartment selector is open.
    whileSpawnUISeconds = 900,

    -- Applied after the spawn UI closes or apartment spawn events fire.
    -- Keep this short because apartment/clothing protection will cover first-character setup.
    afterSpawnSeconds = 12,

    -- Applied when clothing/character customization opens.
    clothingSeconds = 900,

    -- Applied when qb-apartments tells the server the player is inside an apartment.
    -- This is cleared as soon as qb-apartments flips their inside metadata back to false.
    apartmentInsideSeconds = 900,

    -- Applied right after the player exits their apartment into the city.
    -- After this short buffer, normal AOP enforcement resumes.
    afterApartmentExitSeconds = 10,

    -- If false, the resource will NOT auto-correct location directly after qb-spawn closes.
    -- This should stay false for QBCore apartment/first-character flows.
    correctAfterSpawn = true,
}

Config.Display = {
    enabled = true,
    x = 0.670,
    y = 1.325,
    scale = 0.42
}

Config.Notify = {
    -- 'auto' tries qb-core, mythic_notify, then native GTA notification/chat.
    type = 'auto',

    -- Since this resource has its own AOP NUI warning screen, these are off by default.
    -- Turn one on only if you also want qb-core/native/mythic notifications for that action.
    useOutOfAOP = false,
    useBorder = false,
    useTeleport = false,

    -- Peacetime still uses small notifications by default because it is separate from the AOP screen.
    usePeacetime = true,
}

Config.TextUI = {
    -- Fallback DrawText card used only when Config.WarningScreen.enabled is false.
    enabled = true,

    title = 'OUT OF AOP',
    message = 'You are outside the current AOP. Return back to the active patrol area or face staff punishments.',
    subMessage = 'Current AOP: %s',

    blockedTitle = 'AOP BORDER',
    blockedMessage = 'You cannot cross into the restricted area.',

    teleportTitle = 'RETURNING TO AOP',
    teleportMessage = 'You are being moved back to the current AOP.',

    countdownLabel = 'Teleporting in',

    x = 0.5,
    y = 0.215,
    width = 0.62,
    height = 0.205,
    titleScale = 0.82,
    messageScale = 0.37,
    countdownScale = 0.58,
    background = true
}

-- Warning UI screen type. Keep this simple:
-- 1 = main/top warning UI
-- 2 = compact bottom-right warning UI
Config.ScreenType = 1

Config.WarningScreen = {
    enabled = true,

    -- true keeps the screen visible the whole time someone is out of AOP.
    persistentWhileOutOfAOP = true,

    -- true shows the teleport timer on the screen.
    showCountdown = true,

    -- true darkens and blurs the game view behind the warning card.
    blurBackground = false,

    -- true adds a subtle vignette and red pulse.
    pulse = false,

    -- true plays a warning beep when the warning first opens.
    playSound = true,

    -- How often the NUI countdown text updates. Lower = smoother, but no need to go crazy.
    updateIntervalMs = 250,

    -- These are auto-selected by Config.ScreenType, but can stay here as fallback values.
    position = 'top', -- ScreenType 1 = top, ScreenType 2 = bottom_right
    backdropOpacity = 0.00,

    -- You said you do NOT want a separate AOP BORDER screen.
    -- When false, hitting the border only uses the normal notification/pushback behavior.
    showBorderScreen = false,

    -- Text shown on the NUI screen.
    outTitle = 'OUT OF AOP',
    outSubtitle = 'You are outside the current Area of Patrol.',
    outBody = 'Return to the active patrol area immediately or you will be moved back automatically.',

    borderTitle = 'AOP BORDER',
    borderSubtitle = 'Restricted patrol boundary.',
    borderBody = 'You cannot cross into this restricted area.',

    teleportTitle = 'RETURNING TO AOP',
    teleportSubtitle = 'Moving you back to the active patrol area.',
    teleportBody = 'You ignored the AOP boundary or spawned outside the active patrol zone.',

    countdownLabel = 'Teleporting in',
    currentAopLabel = 'Current AOP',
    statusLabel = 'Status',
}

Config.Debug = {
    -- Shows PolyZone debug polygons when supported. Keep false on live servers.
    debugPoly = false
}

Config.BoundaryVisuals = {
    enabled = true,
    drawGroundLines = true,

    -- Solid wall draws filled red panels from the ground into the sky.
    -- This looks much more like a real barrier than thin DrawLine-only borders.
    drawSolidWall = true,
    -- 'dense_lines' is the most reliable FiveM wall rendering. It draws a heavy red grid from ground to sky.
    -- 'poly' can look smoother on some servers, but dense_lines is harder to miss.
    wallRenderMode = 'poly', -- 'poly' or 'dense_lines'
    wallHeight = 360.0,
    wallBottomOffset = -4.0,
    wallPanelLength = 10.0,
    wallPanelAlpha = 150,

    -- Dense wall controls. Lower spacing = more solid looking, but more draw calls.
    verticalLineSpacing = 5.0,
    horizontalLineSpacing = 6.0,
    sideThickness = 3,
    sideOffset = 0.35,

    -- Extra red grid lines on the wall make the edge easier to see while driving.
    drawWallGrid = true,
    wallGridSpacing = 8.0,

    -- DrawLine itself cannot be thickened in FiveM, so this adds a few nearby lines
    -- to fake a thicker glowing border at ground level.
    groundLineThickness = 4,
    groundLineOffset = 0.18,

    maxDrawDistance = 2600.0,
    color = { r = 255, g = 0, b = 0, a = 255 }
}

Config.MapBlips = {
    enabled = true,

    -- Native GTA/FiveM cannot draw a real striped polygon on the pause map.
    -- This uses many overlapping red radius blips to make the restricted area look filled instead of dotted.
    cleanOutlineOnly = false,
    showAreaBlips = false,
    showBoundaryDots = false,
    showBoundaryMarkers = true,
    -- Better pause-map fill: stacked native area blips that behave more like a filled restricted zone.
    showHazardArea = true,

    -- Legacy circle fill. Keep false unless you prefer the dotted/circle look.
    showHazardFill = false,

    markerSpacing = 220.0,
    markerSprite = 1,
    markerScale = 0.34,
    markerName = 'AOP Boundary',

    hazardAreaStripHeight = 175.0,
    hazardAreaSampleSpacing = 135.0,
    hazardAreaAlpha = 52,
    hazardAreaMaxBlips = 120,

    hazardGridSpacing = 220.0,
    hazardGridRadius = 260.0,
    hazardGridAlpha = 34,
    hazardGridMaxBlips = 850,
    hazardGridName = 'Restricted AOP Area',

    -- Legacy settings, only used if you manually turn showAreaBlips/showBoundaryDots back on.
    areaAlpha = 25,
    boundaryAlpha = 75,
    boundaryDotSpacing = 300.0,
    boundaryDotRadius = 12.0,
    color = 1 -- GTA blip color 1 = red
}

Config.DefaultCommand = 'state'
Config.KillswitchCode = 'off'

Config.MoveTimerSeconds = 120 -- Time before auto action if player remains out of AOP.
Config.WarningIntervalSeconds = 10
Config.TimerTickSeconds = 1
Config.SpeedWarningMph = 100

Config.Enforcement = {
    -- Hard border blocks normal players from crossing from the allowed AOP side into the banned/out-of-AOP side.
    -- If a player spawns/loads already outside AOP and no safe position is known yet, they get warnings/timer handling.
    hardBorder = true,

    -- standalone default behavior: physically push/teleport players.
    standaloneMode = 'pushback', -- 'pushback', 'teleport', or 'warn'

    -- qbcore default behavior: warning first, but hardBorder still blocks crossing at the wall.
    qbcoreMode = 'pushback', -- 'warn', 'pushback', or 'teleport'

    -- If true, QBCore players who remain out of AOP longer than Config.MoveTimerSeconds get teleported.
    qbcoreTeleportAfterTimer = true,

    -- If true, standalone players who remain out of AOP longer than Config.MoveTimerSeconds get teleported.
    standaloneTeleportAfterTimer = true,

    -- Attempts to correct location after QBCore finishes loading/spawning the player.
    qbcoreCheckAfterSpawn = false,
    qbcoreSpawnCheckDelaySeconds = 3,

    -- Legacy fallback. Config.Permissions.staffBypass is the main setting now.
    staffBypass = false,

    -- If someone is already outside AOP after a resource restart and no last-valid spot exists,
    -- teleport is the only reliable way to stop them from driving deeper into the banned side.
    noLastValidAction = 'warn', -- 'teleport' or 'warn'

    -- Jail/prison zone bypass.
    jailBypass = true
}

Config.DefaultTeleport = vector3(2042.7, 3445.5, 44.69)

Config.TeleportsByClientAop = {
    statewide = vector3(2042.7, 3445.5, 44.69),
    blaine = vector3(2042.7, 3445.5, 44.69),
    santos = vector3(161.45, -987.28, 31.3),
}

-- /aop command options.
-- clientAop decides what large out-of-AOP zone gets created:
--   statewide = only banned sub-zone enforcement
--   blaine    = Los Santos is out of AOP
--   santos    = Blaine County is out of AOP
-- banned can be nil, sandy, paleto, legion, grove
Config.AOPOptions = {
    ['state'] = { label = 'Statewide', clientAop = 'statewide', banned = nil },
    ['bc'] = { label = 'Blaine County', clientAop = 'blaine', banned = nil },
    ['ls'] = { label = 'Los Santos', clientAop = 'santos', banned = nil },

    ['sb'] = { label = 'Statewide | Sandy Banned', clientAop = 'statewide', banned = 'sandy' },
    ['pb'] = { label = 'Statewide | Paleto Banned', clientAop = 'statewide', banned = 'paleto' },
    ['lg'] = { label = 'Statewide | Legion Banned', clientAop = 'statewide', banned = 'legion' },
    ['gb'] = { label = 'Statewide | Grove Banned', clientAop = 'statewide', banned = 'grove' },

    ['bcsb'] = { label = 'Blaine County | Sandy Banned', clientAop = 'blaine', banned = 'sandy' },
    ['bcpb'] = { label = 'Blaine County | Paleto Banned', clientAop = 'blaine', banned = 'paleto' },
    ['lslg'] = { label = 'Los Santos | Legion Banned', clientAop = 'santos', banned = 'legion' },
    ['lsgb'] = { label = 'Los Santos | Grove Banned', clientAop = 'santos', banned = 'grove' },
}

-- Zone definitions.
-- IMPORTANT: length/width are the real PolyZone dimensions.
-- The old resource used options.scale, but BoxZone ignores that for size.
Config.ZoneDefinitions = {
    jailZone = {
        center = vector3(1688.0, 2585.8, 52.57),
        length = 70.0,
        width = 70.0,
        options = { name = 'jailZone', heading = 40, debugPoly = false }
    },

    sandy = {
        center = vector3(1800.8, 3760.57, 35.59),
        length = 430.0,
        width = 320.0,
        options = { name = 'sandy', heading = 30, debugPoly = false }
    },

    paleto = {
        center = vector3(-158.0, 6345.6, 31.57),
        length = 1100.0,
        width = 900.0,
        options = { name = 'paleto', heading = 40, debugPoly = false }
    },

    legion = {
        center = vector3(195.4, -936.6, 36.2),
        length = 340.0,
        width = 330.0,
        options = { name = 'legion', heading = 250, debugPoly = false }
    },

    grove = {
        center = vector3(105.0, -1940.6, 20.57),
        length = 145.0,
        width = 120.0,
        options = { name = 'grove', heading = 47, debugPoly = false }
    },

    -- Out-of-AOP area when current AOP is Blaine County.
    -- This represents the Los Santos side below the county line.
    blaine = {
        center = vector3(920.0, -660.0, 55.0),
        length = 5600.0,
        width = 4500.0,
        options = { name = 'blaine', heading = 338, debugPoly = false }
    },

    -- Out-of-AOP area when current AOP is Los Santos.
    -- This represents the Blaine County side above the county line.
    ls = {
        center = vector3(260.0, 3400.0, 80.0),
        length = 6500.0,
        width = 4400.0,
        options = { name = 'ls', heading = 338, debugPoly = false }
    },
}

-- More accurate large out-of-AOP shapes for county-line AOPs.
-- These are used when PolyZone is available. BoxZone fallback remains in Config.ZoneDefinitions.
Config.PolygonDefinitions = {
    -- Banned sub-zones use polygon shapes matching the visible red wall lines.
    -- This keeps enforcement lined up with what players actually see.
    sandy = {
        points = {
            vector2(1659.27, 3505.70),
            vector2(2092.28, 3755.65),
            vector2(1942.33, 4015.53),
            vector2(1509.33, 3765.51),
        },
        options = { name = 'sandy', minZ = -100.0, maxZ = 1000.0, debugGrid = false }
    },

    paleto = {
        points = {
            vector2(-347.42, 5795.03),
            vector2(417.02, 6437.58),
            vector2(33.03, 6895.95),
            vector2(-732.02, 6251.81),
        },
        options = { name = 'paleto', minZ = -100.0, maxZ = 1000.0, debugGrid = false }
    },

    legion = {
        points = {
            vector2(164.34, -776.28),
            vector2(322.21, -833.77),
            vector2(226.40, -1096.88),
            vector2(68.60, -1039.43),
        },
        options = { name = 'legion', minZ = -100.0, maxZ = 1000.0, debugGrid = false }
    },

    grove = {
        points = {
            vector2(51.99, -1942.58),
            vector2(103.17, -1887.61),
            vector2(158.00, -1938.75),
            vector2(106.91, -1993.53),
        },
        options = { name = 'grove', minZ = -100.0, maxZ = 1000.0, debugGrid = false }
    },

    -- When AOP is Blaine County, this polygon represents the Los Santos/out-of-AOP side of the border.
    blaine = {
        points = {
            vector2(-3300.0, 3350.0),
            vector2(-2712.2, 3067.07),
            vector2(-2592.4, 3018.27),
            vector2(-1306.17, 2498.27),
            vector2(-1283.17, 2490.27),
            vector2(-800.17, 2295.27),
            vector2(-704.17, 2255.27),
            vector2(-209.17, 2055.27),
            vector2(-82.17, 2005.27),
            vector2(-50.17, 1991.27),
            vector2(73.0, 1944.1),
            vector2(444.8, 1791.1),
            vector2(524.5, 1761.6),
            vector2(1036.2, 1552.2),
            vector2(1305.5, 1443.4),
            vector2(1696.1, 1285.6),
            vector2(2028.7, 1153.3),
            vector2(2685.4, 885.9),
            vector2(4500.0, -4000.0),
            vector2(-4500.0, -4000.0),
            vector2(-4500.0, 3800.0),
        },
        options = { name = 'blaine', minZ = -100.0, maxZ = 1000.0, debugGrid = false }
    },

    -- When AOP is Los Santos, this polygon represents the Blaine County/out-of-AOP side of the border.
    ls = {
        points = {
            vector2(-3300.0, 3350.0),
            vector2(-2711.1, 2534.4),
            vector2(-2477.1, 2435.57),
            vector2(-2074.19, 2277.17),
            vector2(-1472.6, 2035.28),
            vector2(-854.8, 1785.6),
            vector2(-757.3, 1746.2),
            vector2(262.57, 1334.22),
            vector2(1206.57, 951.78),
            vector2(1221.9, 945.5),
            vector2(1302.9, 901.2),
            vector2(1560.8, 809.7),
            vector2(2518.0, 423.0),
            vector2(2615.11, 383.7),
            vector2(4500.0, 4500.0),
            vector2(-4500.0, 4500.0),
        },
        options = { name = 'ls', minZ = -100.0, maxZ = 1000.0, debugGrid = false }
    }
}

Config.BoundaryLines = {
    sandy = {
        {1659.27, 3505.70, 36.91, 2092.28, 3755.65, 34.13},
        {1659.27, 3505.70, 36.91, 1509.33, 3765.51, 35.15},
        {2092.28, 3755.65, 34.13, 1942.33, 4015.53, 33.29},
        {1509.33, 3765.51, 35.15, 1942.33, 4015.53, 33.29},
    },

    paleto = {
        {-347.42, 5795.03, 38.22, -732.02, 6251.81, 24.78},
        {-347.42, 5795.03, 38.22, 417.02, 6437.58, 31.41},
        {-732.02, 6251.81, 24.78, 33.03, 6895.95, 30.12},
        {417.02, 6437.58, 31.41, 33.03, 6895.95, 30.12},
    },

    legion = {
        {164.34, -776.28, 32.72, 322.21, -833.77, 30.29},
        {164.34, -776.28, 32.72, 68.6, -1039.43, 30.46},
        {322.21, -833.77, 30.29, 226.4, -1096.88, 30.29},
        {226.4, -1096.88, 30.29, 68.6, -1039.43, 30.46},
    },

    grove = {
        {51.99, -1942.58, 22.49, 103.17, -1887.61, 22.04},
        {106.91, -1993.53, 21.3, 51.99, -1942.58, 22.49},
        {103.17, -1887.61, 22.04, 158.0, -1938.75, 20.28},
        {106.91, -1993.53, 21.3, 158.0, -1938.75, 20.28},
    },

    blaine = {
        {-2712.2, 3067.07, 13.83, -2592.4, 3018.27, 16.53},
        {-2592.4, 3018.27, 16.53, -1306.17, 2498.27, 22.53},
        {-1306.17, 2498.27, 22.53, -1283.17, 2490.27, 21.53},
        {-1283.17, 2490.27, 21.53, -800.17, 2295.27, 78.53},
        {-800.17, 2295.27, 78.53, -704.17, 2255.27, 81.53},
        {-704.17, 2255.27, 81.53, -209.17, 2055.27, 142.53},
        {-209.17, 2055.27, 142.53, -82.17, 2005.27, 180.53},
        {-82.17, 2005.27, 180.53, -50.17, 1991.27, 180.53},
        {-50.17, 1991.27, 180.53, 73.0, 1944.1, 169.3},
        {73.0, 1944.1, 169.3, 444.8, 1791.1, 231.3},
        {444.8, 1791.1, 231.3, 473.1, 1779.1, 229.1},
        {473.1, 1779.1, 229.1, 524.5, 1761.6, 227.2},
        {524.5, 1761.6, 227.2, 1020.7, 1558.4, 171.6},
        {1020.7, 1558.4, 171.6, 1036.2, 1552.2, 171.2},
        {1036.2, 1552.2, 171.2, 1282.0, 1452.8, 99.4},
        {1282.0, 1452.8, 99.4, 1305.5, 1443.4, 99.5},
        {1305.5, 1443.4, 99.5, 1515.8, 1358.8, 97.7},
        {1562.2, 1339.6, 96.0, 1515.8, 1358.8, 97.7},
        {1562.2, 1339.6, 96.0, 1653.3, 1303.4, 86.3},
        {1653.3, 1303.4, 86.3, 1696.1, 1285.6, 86.7},
        {1696.1, 1285.6, 86.7, 1751.1, 1263.3, 133.7},
        {1751.1, 1263.3, 133.7, 1766.4, 1257.4, 135.2},
        {1766.4, 1257.4, 135.2, 2028.7, 1153.3, 192.4},
        {2028.7, 1153.3, 192.4, 2000.4, 1162.6, 189.3},
        {2000.4, 1162.6, 189.3, 2358.8, 1017.8, 80.0},
        {2358.8, 1017.8, 80.0, 2391.3, 1004.7, 84.8},
        {2391.3, 1004.7, 84.8, 2489.4, 965.1, 85.6},
        {2489.4, 965.1, 85.6, 2574.4, 930.7, 83.3},
        {2574.4, 930.7, 83.3, 2685.4, 885.9, 77.0},
    },

    ls = {
        {-2711.1, 2534.4, 20.0, -2477.1, 2435.57, 18.43},
        {-2477.1, 2435.57, 18.43, -2074.19, 2277.17, 40.85},
        {-2074.19, 2277.17, 40.85, -1472.6, 2035.28, 64.38},
        {-1472.6, 2035.28, 64.38, -854.8, 1785.6, 173.6},
        {-854.8, 1785.6, 173.6, -838.2, 1777.2, 173.8},
        {-838.2, 1777.2, 173.8, -757.3, 1746.2, 201.3},
        {-757.3, 1746.2, 201.3, 262.57, 1334.22, 239.43},
        {262.57, 1334.22, 239.43, 1206.57, 951.78, 147.03},
        {1206.57, 951.78, 147.03, 1221.9, 945.5, 143.6},
        {1221.9, 945.5, 143.6, 1280.6, 905.9, 106.8},
        {1280.6, 905.9, 106.8, 1302.9, 901.2, 105.8},
        {1302.9, 901.2, 105.8, 1479.2, 842.4, 77.73},
        {1479.2, 842.4, 77.73, 1560.8, 809.7, 77.13},
        {1560.8, 809.7, 77.13, 2518.0, 423.0, 113.9},
        {2518.0, 423.0, 113.9, 2615.11, 383.7, 109.13},
    },
}

-- Backwards compatibility for old files that used lowercase config.
config = Config
Next AOP Screens

Last updated August 6, 2026

© 2026 Teedle Scripts. All rights reserved. Built for FiveM roleplay communities.