Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ac.log('Script: Police')
- local sim = ac.getSim()
- local car = ac.getCar(0) or error()
- if not car then return end
- local wheels = car.wheels or error()
- local uiState = ac.getUI()
- ui.setAsynchronousImagesLoading(true)
- local localTesting = ac.dirname() == 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\assettocorsa\\extension\\lua\\online'
- local initialisation = true
- -- Constants --
- local STEAMID = const(ac.getUserSteamID())
- local CSP_VERSION = const(ac.getPatchVersionCode())
- local CSP_MIN_VERSION = const(3116)
- local SHARED_PLAYER_DATA = const('__ACP_SHARED_PLAYER_DATA')
- local SHARED_EVENT_KEY = const('__ACP_PLAYER_SHARED_UPDATE')
- local CAR_ID = const(ac.getCarID(0))
- local CAR_NAME = const(ac.getCarName(0))
- local POLICE_CAR = const({ "ids_2022_ford_crown", "r34police_acp24" })
- local DRIVER_NAME = const(ac.getDriverName(0))
- ---@param carID string
- local function isPoliceCar(carID)
- for _, carName in ipairs(POLICE_CAR) do
- if carID == carName then
- return true
- end
- end
- return false
- end
- if CSP_VERSION < CSP_MIN_VERSION or not isPoliceCar(CAR_ID) then return end
- local DRIVER_NATION_CODE = const(ac.getDriverNationCode(0))
- local UNIT = "km/h"
- local UNIT_MULT = 1
- if DRIVER_NATION_CODE == "USA" or DRIVER_NATION_CODE == "GBR" then
- UNIT = "mph"
- UNIT_MULT = 0.621371
- end
- -- URL --
- local FIREBASE_URL = const('https://acp-server-97674-default-rtdb.firebaseio.com/')
- -- UI --
- local WINDOW_WIDTH = const(sim.windowWidth / uiState.uiScale)
- local WIDTH_DIV = const({
- _2 = WINDOW_WIDTH / 2,
- _3 = WINDOW_WIDTH / 3,
- _4 = WINDOW_WIDTH / 4,
- _5 = WINDOW_WIDTH / 5,
- _6 = WINDOW_WIDTH / 6,
- _10 = WINDOW_WIDTH / 10,
- _12 = WINDOW_WIDTH / 12,
- _15 = WINDOW_WIDTH / 15,
- _20 = WINDOW_WIDTH / 20,
- _25 = WINDOW_WIDTH / 25,
- _32 = WINDOW_WIDTH / 32,
- })
- local WINDOW_HEIGHT = const(sim.windowHeight / uiState.uiScale)
- local HEIGHT_DIV = const({
- _2 = WINDOW_HEIGHT / 2,
- _3 = WINDOW_HEIGHT / 3,
- _4 = WINDOW_HEIGHT / 4,
- _12 = WINDOW_HEIGHT / 12,
- _20 = WINDOW_HEIGHT / 20,
- _40 = WINDOW_HEIGHT / 40,
- _50 = WINDOW_HEIGHT / 50,
- _60 = WINDOW_HEIGHT / 60,
- _70 = WINDOW_HEIGHT / 70,
- _80 = WINDOW_HEIGHT / 80,
- })
- local FONT_MULT = const(WINDOW_HEIGHT / 1440)
- local HUD_IMG = {}
- local IMAGES = const({
- police = {
- url = "https://github.com/ele-sage/ACP-apps/raw/refs/heads/master/images/police.zip",
- hud = {
- "base.png",
- "arrest.png",
- "cams.png",
- "logs.png",
- "lost.png",
- "menu.png",
- "radar.png",
- },
- },
- })
- ---@param key string
- local function loadImages(key)
- web.loadRemoteAssets(IMAGES[key].url, function(err, data)
- if err then
- ac.error('Failed to load welcome images:', err)
- return
- end
- local path = data .. '/' .. key .. '/'
- local files = io.scanDir(path, "*")
- for i, file in ipairs(files) do
- if table.contains(IMAGES.police.hud, file) then
- local k = file:match('(.+)%..+')
- HUD_IMG[k] = path .. file
- end
- end
- end)
- end
- loadImages("police")
- local CAMERAS = const({
- {
- name = "BOBs SCRAPYARD",
- pos = vec3(-3564, 31.5, -103),
- dir = -8,
- fov = 60,
- },
- {
- name = "ARENA",
- pos = vec3(-2283, 115.5, 3284),
- dir = 128,
- fov = 70,
- },
- {
- name = "BANK",
- pos = vec3(-716, 151, 3556.4),
- dir = 12,
- fov = 95,
- },
- {
- name = "STREET RUNNERS",
- pos = vec3(-57.3, 103.5, 2935.5),
- dir = 16,
- fov = 67,
- },
- {
- name = "ROAD CRIMINALS",
- pos = vec3(-2332, 101.1, 3119.2),
- dir = 121,
- fov = 60,
- },
- {
- name = "RECKLESS RENEGADES",
- pos = vec3(-2993.7, -24.4, -601.7),
- dir = -64,
- fov = 60,
- },
- {
- name = "MOTION MASTERS",
- pos = vec3(-2120.4, -11.8, -1911.5),
- dir = 102,
- fov = 60,
- },
- })
- local MSG_ARREST = const({
- "`NAME` has been arrested for Speeding. The individual was driving a `CAR`.",
- "We have apprehended `NAME` for Speeding. The suspect was behind the wheel of a `CAR`.",
- "The driver of a `CAR`, identified as `NAME`, has been arrested for Speeding.",
- "`NAME` has been taken into custody for Illegal Racing. The suspect was driving a `CAR`.",
- "We have successfully apprehended `NAME` for Illegal Racing. The individual was operating a `CAR`.",
- "The driver of a `CAR`, identified as `NAME`, has been arrested for Illegal Racing.",
- "`NAME` has been apprehended for Speeding. The suspect was operating a `CAR` at the time of the arrest.",
- "We have successfully detained `NAME` for Illegal Racing. The individual was driving a `CAR`.",
- "`NAME` driving a `CAR` has been arrested for Speeding",
- "`NAME` driving a `CAR` has been arrested for Illegal Racing."
- })
- local MSG_LOST = const({
- "We've lost sight of the suspect. The vehicle involved is described as a `CAR` driven by `NAME`.",
- "Attention all units, we have lost visual contact with the suspect. The vehicle involved is a `CAR` driven by `NAME`.",
- "We have temporarily lost track of the suspect. The vehicle description is a `CAR` with `NAME` as the driver.",
- "Visual contact with the suspect has been lost. The suspect is driving a `CAR` and identified as `NAME`.",
- "We have lost the suspect's visual trail. The vehicle in question is described as a `CAR` driven by `NAME`.",
- "Suspect have been lost, Vehicle Description:`CAR` driven by `NAME`",
- "Visual contact with the suspect has been lost. The suspect is driving a `CAR` and identified as `NAME`.",
- "We have lost the suspect's visual trail. The vehicle in question is described as a `CAR` driven by `NAME`.",
- })
- local MSG_ENGAGE = const({
- "Control! I am engaging on a `CAR` traveling at `SPEED`",
- "Pursuit in progress! I am chasing a `CAR` exceeding `SPEED`",
- "Control, be advised! Pursuit is active on a `CAR` driving over `SPEED`",
- "Attention! Pursuit initiated! Im following a `CAR` going above `SPEED`",
- "Pursuit engaged! `CAR` driving at a high rate of speed over `SPEED`",
- "Attention all units, we have a pursuit in progress! Suspect driving a `CAR` exceeding `SPEED`",
- "Attention units! We have a suspect fleeing in a `CAR` at high speed, pursuing now at `SPEED`",
- "Engaging on a high-speed chase! Suspect driving a `CAR` exceeding `SPEED`!",
- "Attention all units! we have a pursuit in progress! Suspect driving a `CAR` exceeding `SPEED`",
- "High-speed chase underway, suspect driving `CAR` over `SPEED`",
- "Control, `CAR` exceeding `SPEED`, pursuit active.",
- "Engaging on a `CAR` exceeding `SPEED`, pursuit initiated."
- })
- local dataLoaded = {}
- dataLoaded['Settings'] = false
- dataLoaded['PlayerData'] = false
- ---@param key string
- local function removeUtf8Char(key)
- local newKey = ''
- for i = 1, #key do
- local c = key:sub(i, i)
- if c:byte() < 128 then
- newKey = newKey .. c
- end
- end
- newKey = newKey:match('^%s*(.-)%s*$')
- return newKey
- end
- local CAR_NAME_NO_UTF8 = removeUtf8Char(CAR_NAME)
- --------- Utils ------------
- ---@param keys string[]
- ---@param t table
- local function hasKeys(keys, t)
- for i = 1, #keys do
- if not t[keys[i]] then
- ac.error('Missing key:', keys[i])
- return false
- end
- end
- return true
- end
- ---@param number number
- ---@param decimal integer
- ---@return number
- local function truncate(number, decimal)
- local power = 10 ^ decimal
- return math.floor(number * power) / power
- end
- ---@param t table
- local function tableToVec3(t)
- return vec3(t[1], t[2], t[3])
- end
- ---@param t table
- local function tableToVec2(t)
- return vec2(t[1], t[2])
- end
- ---@param t table
- local function tableToRGBM(t)
- return rgbm(t[1], t[2], t[3], t[4])
- end
- ---@param err string
- ---@param response WebResponse
- ---@return boolean
- local function canProcessRequest(err, response)
- if err then
- ac.error('Failed to process request:', err)
- return false
- end
- return response.status == 200 and response.body ~= ''
- end
- ---@param response WebResponse
- ---@return boolean
- local function hasExistingData(response)
- return response.status == 200 and response.body ~= 'null'
- end
- ---@param v vec3
- ---@return vec3
- local function snapToTrack(v)
- if physics.raycastTrack(v, vDown, 20, v) == -1 then
- physics.raycastTrack(v, vUp, 20, v)
- end
- return v
- end
- ---@param time number
- ---@return string
- local function formatTime(time)
- local minutes = math.floor(time / 60)
- local seconds = math.floor(time % 60)
- local milliseconds = math.floor((time % 1) * 1000)
- return ('%02d:%02d.%03d'):format(minutes, seconds, milliseconds)
- end
- local DEFAULT_SETTINGS = const({
- essentialSize = 20,
- policeSize = 20,
- hudOffset = vec2(0, 0),
- fontSize = 20,
- current = 1,
- colorHud = rgbm(1, 0, 0, 1),
- timeMsg = 10,
- msgOffset = vec2(WIDTH_DIV._2, 10),
- fontSizeMSG = 30,
- menuPos = vec2(0, 0),
- unit = UNIT,
- unitMult = UNIT_MULT,
- starsSize = 20,
- starsPos = vec2(WINDOW_WIDTH, 0),
- })
- ---@class Settings
- ---@field essentialSize number
- ---@field policeSize number
- ---@field hudOffset vec2
- ---@field fontSize number
- ---@field current number
- ---@field colorHud rgbm
- ---@field timeMsg number
- ---@field msgOffset vec2
- ---@field fontSizeMSG number
- ---@field menuPos vec2
- ---@field unit string
- ---@field unitMult number
- ---@field starsSize number
- ---@field starsPos vec2
- local Settings = class('Settings')
- ---@return Settings
- function Settings.new()
- local settings = table.clone(DEFAULT_SETTINGS, true)
- setmetatable(settings, { __index = Settings })
- return settings
- end
- ---@param data table
- ---@return Settings
- function Settings.tryParse(data)
- local hudOffset = data.hudOffset and tableToVec2(data.hudOffset) or vec2(0, 0)
- local colorHud = data.colorHud and tableToRGBM(data.colorHud) or rgbm(1, 0, 0, 1)
- local msgOffset = data.msgOffset and tableToVec2(data.msgOffset) or vec2(WIDTH_DIV._2, 10)
- local menuPos = data.menuPos and tableToVec2(data.menuPos) or vec2(0, 0)
- local starsPos = data.starsPos and tableToVec2(data.starsPos) or vec2(WINDOW_WIDTH, 0)
- local settings = {
- essentialSize = data.essentialSize or 20,
- policeSize = data.policeSize or 20,
- hudOffset = hudOffset,
- fontSize = data.fontSize or 20,
- current = data.current or 1,
- colorHud = colorHud,
- timeMsg = data.timeMsg or 10,
- msgOffset = msgOffset,
- fontSizeMSG = data.fontSizeMSG or 30,
- menuPos = menuPos,
- unit = data.unit or UNIT,
- unitMult = data.unitMult or UNIT_MULT,
- starsSize = data.starsSize or 20,
- starsPos = starsPos,
- }
- setmetatable(settings, { __index = Settings })
- return settings
- end
- ---@param url string
- ---@param callback function
- function Settings.fetch(url, callback)
- if localTesting then
- local currentPath = ac.getFolder(ac.FolderID.ScriptOrigin)
- local file = io.open(currentPath .. '/response/settingsResponse.json', 'r')
- if not file then
- ac.error('Failed to open response.json')
- callback(Settings.new())
- return
- end
- local data = JSON.parse(file:read('*a'))
- file:close()
- local settings = Settings.tryParse(data)
- callback(settings)
- else
- web.get(url, function(err, response)
- if canProcessRequest(err, response) then
- if hasExistingData(response) then
- local data = JSON.parse(response.body)
- if data then
- local settings = Settings.tryParse(data)
- callback(settings)
- else
- ac.error('Failed to parse settings data.')
- callback(Settings.new())
- end
- else
- callback(Settings.new())
- end
- else
- ac.error('Failed to fetch settings:', err)
- callback(Settings.new())
- end
- end)
- end
- end
- ---@param callback function
- function Settings.allocate(callback)
- local url = FIREBASE_URL .. 'Settings/' .. STEAMID .. '.json'
- ac.log('Loading settings')
- Settings.fetch(url, function(settings)
- callback(settings)
- end)
- end
- ---@return table
- function Settings:export()
- local data = {}
- if self.essentialSize ~= DEFAULT_SETTINGS.essentialSize then
- data.essentialSize = self.essentialSize
- end
- if self.policeSize ~= DEFAULT_SETTINGS.policeSize then
- data.policeSize = self.policeSize
- end
- if self.hudOffset ~= DEFAULT_SETTINGS.hudOffset then
- data.hudOffset = { self.hudOffset.x, self.hudOffset.y }
- end
- if self.fontSize ~= DEFAULT_SETTINGS.fontSize then
- data.fontSize = self.fontSize
- end
- if self.current ~= DEFAULT_SETTINGS.current then
- data.current = self.current
- end
- if self.colorHud ~= DEFAULT_SETTINGS.colorHud then
- data.colorHud = { self.colorHud.r, self.colorHud.g, self.colorHud.b, self.colorHud.mult }
- end
- if self.timeMsg ~= DEFAULT_SETTINGS.timeMsg then
- data.timeMsg = self.timeMsg
- end
- if self.msgOffset ~= DEFAULT_SETTINGS.msgOffset then
- data.msgOffset = { self.msgOffset.x, self.msgOffset.y }
- end
- if self.fontSizeMSG ~= DEFAULT_SETTINGS.fontSizeMSG then
- data.fontSizeMSG = self.fontSizeMSG
- end
- if self.menuPos ~= DEFAULT_SETTINGS.menuPos then
- data.menuPos = { self.menuPos.x, self.menuPos.y }
- end
- if self.unit ~= DEFAULT_SETTINGS.unit then
- data.unit = self.unit
- end
- if self.unitMult ~= DEFAULT_SETTINGS.unitMult then
- data.unitMult = self.unitMult
- end
- if self.starsSize ~= DEFAULT_SETTINGS.starsSize then
- data.starsSize = self.starsSize
- end
- if self.starsPos ~= DEFAULT_SETTINGS.starsPos then
- data.starsPos = { self.starsPos.x, self.starsPos.y }
- end
- return data
- end
- function Settings:save()
- if localTesting then return end
- local str = '{"' .. STEAMID .. '": ' .. JSON.stringify(self:export()) .. '}'
- web.request('PATCH', FIREBASE_URL .. "Settings.json", str, function(err, response)
- if err then
- ac.error(err)
- return
- end
- end)
- end
- local patchCount = 0
- ---@class SectorStats
- ---@field name string
- ---@field records table<string, number>
- local SectorStats = class('SectorStats')
- ---@param name string
- ---@param data table
- ---@return SectorStats
- function SectorStats.tryParse(name, data)
- local records = {}
- for carName, time in pairs(data) do
- local nameWithoutUtf8 = removeUtf8Char(carName)
- records[nameWithoutUtf8] = time
- end
- local sectorStats = {
- name = name,
- records = records,
- }
- setmetatable(sectorStats, { __index = SectorStats })
- return sectorStats
- end
- ---@param name string
- ---@param data table
- ---@return SectorStats|nil
- function SectorStats.allocate(name, data)
- if type(data) == 'table' then
- local sectorStats = SectorStats.tryParse(name, data)
- if not sectorStats then
- ac.error('Failed to allocate sector stat')
- return nil
- end
- return sectorStats
- end
- if type(data) == 'number' then
- local records = {}
- records[CAR_NAME_NO_UTF8] = data
- local sectorStats = {
- name = name,
- records = records,
- }
- setmetatable(sectorStats, { __index = SectorStats })
- return sectorStats
- end
- ac.error('Failed to allocate sector stat')
- return nil
- end
- ---@param time number
- ---@return boolean
- function SectorStats:addRecord(time)
- if not self.records[CAR_NAME_NO_UTF8] or self.records[CAR_NAME_NO_UTF8] > time then
- self.records[CAR_NAME_NO_UTF8] = time
- return true
- end
- return false
- end
- ---@return table
- function SectorStats:export()
- local records = {}
- for carName, time in pairs(self.records) do
- records[carName] = truncate(time, 3)
- end
- return {
- [self.name] = records
- }
- end
- local lastRegister = {
- kms = 0,
- time = os.clock(),
- }
- ---@class Player
- ---@field name string
- ---@field sectors SectorStats[]
- ---@field sectorsFormated table<string, table<string, string>>
- ---@field arrests integer
- ---@field getaways integer
- ---@field thefts integer
- ---@field heists integer
- ---@field deliveries integer
- ---@field overtake integer
- ---@field wins integer
- ---@field losses integer
- ---@field elo integer
- ---@field kms number
- ---@field time number
- local Player = class('Player')
- ---@type Player | nil
- local player = nil
- local sharedPlayerLayout = {
- ac.StructItem.key(SHARED_PLAYER_DATA),
- hudColor = ac.StructItem.rgbm(),
- name = ac.StructItem.string(24),
- sectorsFormated = ac.StructItem.array(ac.StructItem.struct({
- name = ac.StructItem.string(16),
- records = ac.StructItem.array(ac.StructItem.string(50), 20)
- }), 5),
- arrests = ac.StructItem.uint16(),
- getaways = ac.StructItem.uint16(),
- thefts = ac.StructItem.uint16(),
- heists = ac.StructItem.uint16(),
- deliveries = ac.StructItem.uint16(),
- overtake = ac.StructItem.uint32(),
- wins = ac.StructItem.uint16(),
- losses = ac.StructItem.uint16(),
- elo = ac.StructItem.uint16(),
- kms = ac.StructItem.float(),
- time = ac.StructItem.float(),
- }
- ---@type Settings | nil
- local settings = nil
- local sharedPlayerData = ac.connect(sharedPlayerLayout, true, ac.SharedNamespace.ServerScript)
- local function updateSharedPlayerData()
- if not player then return end
- local hudC = rgbm.colors.red
- if settings then
- hudC = settings.colorHud
- end
- sharedPlayerData.hudColor = hudC
- sharedPlayerData.name = player.name
- sharedPlayerData.arrests = player.arrests
- sharedPlayerData.getaways = player.getaways
- sharedPlayerData.thefts = player.thefts
- sharedPlayerData.heists = player.heists
- sharedPlayerData.deliveries = player.deliveries
- sharedPlayerData.overtake = player.overtake
- sharedPlayerData.wins = player.wins
- sharedPlayerData.losses = player.losses
- sharedPlayerData.elo = player.elo
- sharedPlayerData.kms = player.kms
- sharedPlayerData.time = player.time
- sharedPlayerData.sectorsFormated = {}
- local i = 1
- table.forEach(player.sectorsFormated, function(v, k)
- sharedPlayerData.sectorsFormated[i].name = k .. '\0'
- for j, entry in ipairs(v) do
- local carName = string.sub(entry[1], 1, 45)
- sharedPlayerData.sectorsFormated[i].records[j] = carName .. ' - ' .. entry[2] .. '\0'
- end
- i = i + 1
- end)
- end
- ---@return Player
- function Player.new()
- local _player = {
- name = DRIVER_NAME,
- sectors = {},
- sectorsFormated = {},
- arrests = 0,
- getaways = 0,
- thefts = 0,
- heists = 0,
- deliveries = 0,
- overtake = 0,
- wins = 0,
- losses = 0,
- elo = 1200,
- kms = 0,
- time = 0,
- }
- setmetatable(_player, { __index = Player })
- return _player
- end
- ---@param data table
- ---@return Player
- function Player.tryParse(data)
- if not data then
- return Player.new()
- end
- local sectors = {}
- if data.sectors then
- for sectorName, sectorData in pairs(data.sectors) do
- local sector = SectorStats.allocate(sectorName, sectorData)
- if sector then
- table.insert(sectors, sector)
- end
- end
- end
- local _player = {
- name = DRIVER_NAME,
- sectors = sectors,
- sectorsFormated = {},
- arrests = data.arrests or 0,
- getaways = data.getaways or 0,
- thefts = data.thefts or 0,
- heists = data.heists or 0,
- deliveries = data.deliveries or 0,
- overtake = data.overtake or 0,
- wins = data.wins or 0,
- losses = data.losses or 0,
- elo = data.elo or 1200,
- kms = data.kms or 0,
- time = data.time or 0,
- }
- setmetatable(_player, { __index = Player })
- return _player
- end
- ---@param url string
- ---@param callback function
- function Player.fetch(url, callback)
- if localTesting then
- local currentPath = ac.getFolder(ac.FolderID.ScriptOrigin)
- local file = io.open(currentPath .. '/response/playerResponse.json', 'r')
- if not file then
- ac.error('Failed to open playerResponse.json')
- callback(Player.new())
- return
- end
- local data = JSON.parse(file:read('*a'))
- file:close()
- local _player = Player.tryParse(data)
- callback(_player)
- else
- web.get(url, function(err, response)
- if canProcessRequest(err, response) then
- if hasExistingData(response) then
- local data = JSON.parse(response.body)
- if data then
- local _player = Player.tryParse(data)
- callback(_player)
- else
- ac.error('Failed to parse player data.')
- callback(Player.new())
- end
- else
- callback(Player.new())
- end
- else
- ac.error('Failed to fetch player:', err)
- callback(Player.new())
- end
- end)
- end
- end
- ---@param callback function
- function Player.allocate(callback)
- local url = FIREBASE_URL .. 'Players/' .. STEAMID .. '.json'
- Player.fetch(url, function(_player)
- callback(_player)
- end)
- end
- function Player:sortSectors()
- for _, sector in ipairs(self.sectors) do
- local entries = {}
- for carName, time in pairs(sector.records) do
- table.insert(entries, { carName, time })
- end
- table.sort(entries, function(a, b)
- return a[2] < b[2]
- end)
- for i, entry in ipairs(entries) do
- entries[i][2] = formatTime(entry[2])
- end
- self.sectorsFormated[sector.name] = entries
- end
- end
- ---@return table
- function Player:export()
- local kms = truncate(car.distanceDrivenSessionKm - lastRegister.kms + self.kms, 3)
- local time = math.round(os.clock() - lastRegister.time + self.time, 0)
- local data = { name = self.name }
- if self.arrests > 0 then
- data.arrests = self.arrests
- end
- if self.getaways > 0 then
- data.getaways = self.getaways
- end
- if self.thefts > 0 then
- data.thefts = self.thefts
- end
- if self.heists > 0 then
- data.heists = self.heists
- end
- if self.deliveries > 0 then
- data.deliveries = self.deliveries
- end
- if self.overtake > 0 then
- data.overtake = self.overtake
- end
- if self.wins > 0 then
- data.wins = self.wins
- end
- if self.losses > 0 then
- data.losses = self.losses
- end
- if self.elo ~= 1200 then
- data.elo = self.elo
- end
- if kms > 0 then
- data.kms = kms
- end
- if time > 0 then
- data.time = time
- end
- lastRegister.kms = car.distanceDrivenSessionKm
- lastRegister.time = os.clock()
- local sectors = {}
- for _, sector in ipairs(self.sectors) do
- if not sector then
- break
- end
- local sectorData = sector:export()
- for k, v in pairs(sectorData) do
- sectors[k] = v
- end
- end
- if next(sectors) then
- data.sectors = sectors
- end
- self:sortSectors()
- updateSharedPlayerData()
- return data
- end
- function Player:save()
- local str = '{"' .. STEAMID .. '": ' .. JSON.stringify(self:export()) .. '}'
- if localTesting or patchCount > 40 then return end
- patchCount = patchCount + 1
- web.request('PATCH', FIREBASE_URL .. "Players.json", str, function(err, response)
- if err then
- ac.error(err)
- return
- end
- end)
- end
- local canRun = false
- local function shouldRun()
- if canRun then return true end
- local isDataLoaded = dataLoaded['Settings'] and dataLoaded['PlayerData']
- local hasNecessaryData = settings and player
- local hasMinVersion = CSP_VERSION >= CSP_MIN_VERSION
- if isDataLoaded and hasMinVersion and hasNecessaryData and isPoliceCar(CAR_ID) then
- canRun = true
- end
- return canRun
- end
- local settingsOpen = false
- local arrestLogsOpen = false
- local camerasOpen = false
- local imageSize = vec2(0,0)
- local pursuit = {
- suspect = nil,
- enable = false,
- maxDistance = 250000,
- minDistance = 40000,
- nextMessage = 30,
- level = 1,
- id = -1,
- timerArrest = 0,
- hasArrested = false,
- startedTime = 0,
- timeLostSight = 0,
- lostSight = false,
- engage = false,
- }
- local arrestations = {}
- local playerInRangeUI = {
- fontSize = {
- div_2 = 20 / 2,
- div_2_5 = 20 / 2.5,
- div_3 = 20 / 3,
- div_1_5 = 20 / 1.5,
- div_1_2 = 20 / 1.2,
- },
- window = {
- pos = vec2(0, 0),
- size = vec2(0, 0),
- },
- box = {
- pos1 = vec2(0, 0),
- pos2 = vec2(0, 0),
- offsetY = 0,
- },
- text = {
- pos = vec2(0, 0),
- size = vec2(0, 0),
- offsetY = 0,
- },
- header = {
- radar = {
- pos = vec2(0, 0),
- },
- nearby = {
- pos = vec2(0, 0),
- },
- },
- color = rgbm(1, 1, 1, 0.5),
- }
- local iconPos = {}
- local function onSettingsChange()
- settings:save()
- ac.log('Settings updated')
- end
- ---------------------------------------------------------------------------------------------- Firebase ----------------------------------------------------------------------------------------------
- local acpPolice = ac.OnlineEvent({
- message = ac.StructItem.string(110),
- messageType = ac.StructItem.int16(),
- yourIndex = ac.StructItem.int16(),
- }, function (sender, data)
- if data.yourIndex == car.sessionID and data.messageType == 0 and pursuit.suspect ~= nil and sender == pursuit.suspect then
- pursuit.hasArrested = true
- end
- end)
- local starsUI = {
- starsPos = vec2(0, 0),
- starsSize = vec2(0, 0),
- startSpace = 0,
- full = "https://acstuff.ru/images/icons_24/star_full.png",
- empty = "https://acstuff.ru/images/icons_24/star_empty.png",
- }
- local function updateStarsPos()
- starsUI.starsPos = vec2(settings.starsPos.x - settings.starsSize / 2, settings.starsPos.y + settings.starsSize / 2)
- starsUI.starsSize = vec2(settings.starsPos.x - settings.starsSize * 2, settings.starsPos.y + settings.starsSize * 2)
- starsUI.startSpace = settings.starsSize / 1.5
- end
- local function updateHudPos()
- imageSize = vec2(WINDOW_HEIGHT/80 * settings.policeSize, WINDOW_HEIGHT/80 * settings.policeSize)
- iconPos.arrest1 = vec2(imageSize.x - imageSize.x/12, imageSize.y/3.2)
- iconPos.arrest2 = vec2(imageSize.x/1.215, imageSize.y/5)
- iconPos.lost1 = vec2(imageSize.x - imageSize.x/12, imageSize.y/2.35)
- iconPos.lost2 = vec2(imageSize.x/1.215, imageSize.y/3.2)
- iconPos.logs1 = vec2(imageSize.x/1.215, imageSize.y/1.88)
- iconPos.logs2 = vec2(imageSize.x/1.39, imageSize.y/2.35)
- iconPos.menu1 = vec2(imageSize.x - imageSize.x/12, imageSize.y/1.88)
- iconPos.menu2 = vec2(imageSize.x/1.215, imageSize.y/2.35)
- iconPos.cams1 = vec2(imageSize.x/1.215, imageSize.y/2.35)
- iconPos.cams2 = vec2(imageSize.x/1.39, imageSize.y/3.2)
- settings.fontSize = settings.policeSize * FONT_MULT
- playerInRangeUI.fontSize.div_2 = settings.fontSize / 2
- playerInRangeUI.fontSize.div_2_5 = settings.fontSize / 2.5
- playerInRangeUI.fontSize.div_3 = settings.fontSize / 3
- playerInRangeUI.fontSize.div_1_5 = settings.fontSize / 1.5
- playerInRangeUI.fontSize.div_1_2 = settings.fontSize / 1.2
- playerInRangeUI.window.pos = vec2(settings.hudOffset.x + imageSize.x / 9.5, settings.hudOffset.y + imageSize.y / 5.3)
- playerInRangeUI.window.size = vec2(imageSize.x * 3 / 5, imageSize.y / 2.8)
- playerInRangeUI.box.pos1 = vec2(playerInRangeUI.window.size.x / 20, playerInRangeUI.window.size.y / 20)
- playerInRangeUI.box.pos2 = vec2(playerInRangeUI.window.size.x - playerInRangeUI.window.size.x / 20, playerInRangeUI.window.size.y / 20)
- playerInRangeUI.box.offsetY = playerInRangeUI.window.size.y / 10 + playerInRangeUI.box.pos2.y
- ui.pushDWriteFont("Orbitron;Weight=Bold")
- playerInRangeUI.header.radar.pos = vec2((playerInRangeUI.window.size.x - ui.measureDWriteText("RADAR ACTIVE", playerInRangeUI.fontSize.div_1_5).x) / 2, 0)
- ui.popDWriteFont()
- ui.pushDWriteFont("Orbitron;Weight=Regular")
- playerInRangeUI.header.nearby.pos = vec2((playerInRangeUI.window.size.x - ui.measureDWriteText("NEARBY VEHICULE SPEED SCANNING", playerInRangeUI.fontSize.div_2_5).x) / 2, playerInRangeUI.fontSize.div_1_2)
- ui.popDWriteFont()
- playerInRangeUI.text.pos = vec2(playerInRangeUI.window.size.x / 20, 0)
- playerInRangeUI.text.size = vec2(playerInRangeUI.window.size.x - playerInRangeUI.window.size.x / 10, playerInRangeUI.window.size.y / 5)
- ui.pushDWriteFont("Orbitron;Weight=Regular")
- playerInRangeUI.text.offsetY = (playerInRangeUI.window.size.y / 20 - ui.measureDWriteText("Player In Range", settings.fontSize / 20).y) / 2
- ui.popDWriteFont()
- end
- local function showStarsPursuit()
- local starsColor = rgbm(1, 1, 1, os.clock()%2 + 0.3)
- updateStarsPos()
- for i = 1, 5 do
- if i > pursuit.level/2 then
- ui.drawIcon(ui.Icons.StarEmpty, starsUI.starsPos, starsUI.starsSize, rgbm(1, 1, 1, 0.2))
- else
- ui.drawIcon(ui.Icons.StarFull, starsUI.starsPos, starsUI.starsSize, starsColor)
- end
- starsUI.starsPos.x = starsUI.starsPos.x - settings.starsSize - starsUI.startSpace
- starsUI.starsSize.x = starsUI.starsSize.x - settings.starsSize - starsUI.startSpace
- end
- end
- local showPreviewMsg = false
- local showPreviewStars = false
- COLORSMSGBG = rgbm(0.5,0.5,0.5,0.5)
- local function initsettings()
- imageSize = vec2(WINDOW_HEIGHT/80 * settings.policeSize, WINDOW_HEIGHT/80 * settings.policeSize)
- updateHudPos()
- updateStarsPos()
- end
- local function previewMSG()
- ui.beginTransparentWindow("previewMSG", vec2(0, 0), vec2(WINDOW_WIDTH, WINDOW_HEIGHT))
- ui.pushDWriteFont("Orbitron;Weight=800")
- local tSize = ui.measureDWriteText("Messages from Police when being chased", settings.fontSizeMSG)
- local uiOffsetX = settings.msgOffset.x - tSize.x/2
- local uiOffsetY = settings.msgOffset.y
- ui.drawRectFilled(vec2(uiOffsetX - 5, uiOffsetY-5), vec2(uiOffsetX + tSize.x + 5, uiOffsetY + tSize.y + 5), COLORSMSGBG)
- ui.dwriteDrawText("Messages from Police when being chased", settings.fontSizeMSG, vec2(uiOffsetX, uiOffsetY), rgbm.colors.cyan)
- ui.popDWriteFont()
- ui.endTransparentWindow()
- end
- local function previewStars()
- ui.beginTransparentWindow("previewStars", vec2(0, 0), vec2(WINDOW_WIDTH, WINDOW_HEIGHT))
- showStarsPursuit()
- ui.endTransparentWindow()
- end
- local function uiTab()
- ui.text('On Screen Message : ')
- settings.timeMsg = ui.slider('##' .. 'Time Msg On Screen', settings.timeMsg, 1, 15, 'Time Msg On Screen' .. ': %.0fs')
- settings.fontSizeMSG = ui.slider('##' .. 'Font Size MSG', settings.fontSizeMSG, 10, 50, 'Font Size' .. ': %.0f')
- ui.text('Stars : ')
- settings.starsPos.x = ui.slider('##' .. 'Stars Offset X', settings.starsPos.x, 0, WINDOW_WIDTH, 'Stars Offset X' .. ': %.0f')
- settings.starsPos.y = ui.slider('##' .. 'Stars Offset Y', settings.starsPos.y, 0, WINDOW_HEIGHT, 'Stars Offset Y' .. ': %.0f')
- settings.starsSize = ui.slider('##' .. 'Stars Size', settings.starsSize, 10, 50, 'Stars Size' .. ': %.0f')
- ui.newLine()
- ui.text('Offset : ')
- settings.msgOffset.y = ui.slider('##' .. 'Msg On Screen Offset Y', settings.msgOffset.y, 0, WINDOW_HEIGHT, 'Msg On Screen Offset Y' .. ': %.0f')
- settings.msgOffset.x = ui.slider('##' .. 'Msg On Screen Offset X', settings.msgOffset.x, 0, WINDOW_WIDTH, 'Msg On Screen Offset X' .. ': %.0f')
- ui.newLine()
- ui.text('Preview : ')
- ui.sameLine()
- if ui.button('Message') then
- showPreviewMsg = not showPreviewMsg
- showPreviewStars = false
- end
- ui.sameLine()
- if ui.button('Stars') then
- showPreviewStars = not showPreviewStars
- showPreviewMsg = false
- end
- if showPreviewMsg then previewMSG()
- elseif showPreviewStars then previewStars() end
- if ui.button('Offset X to center') then settings.msgOffset.x = WINDOW_WIDTH/2 end
- ui.newLine()
- end
- local function settingsWindow()
- imageSize = vec2(WINDOW_HEIGHT/80 * settings.policeSize, WINDOW_HEIGHT/80 * settings.policeSize)
- ui.dwriteTextAligned("settings", 40, ui.Alignment.Center, ui.Alignment.Center, vec2(WINDOW_WIDTH/6.5,60), false, rgbm.colors.white)
- ui.drawLine(vec2(0,60), vec2(WINDOW_WIDTH/6.5,60), rgbm.colors.white, 1)
- ui.newLine(20)
- ui.sameLine(10)
- ui.beginGroup()
- ui.text('Unit : ')
- ui.sameLine(160)
- if ui.selectable('mph', settings.unit == 'mph',_, ui.measureText('km/h')) then
- settings.unit = 'mph'
- settings.unitMult = 0.621371
- end
- ui.sameLine(200)
- if ui.selectable('km/h', settings.unit == 'km/h',_, ui.measureText('km/h')) then
- settings.unit = 'km/h'
- settings.unitMult = 1
- end
- ui.sameLine(WINDOW_WIDTH/6.5 - 120)
- if ui.button('Close', vec2(100, WINDOW_HEIGHT/50)) then
- settingsOpen = false
- onSettingsChange()
- end
- ui.text('HUD : ')
- settings.hudOffset.x = ui.slider('##' .. 'HUD Offset X', settings.hudOffset.x, 0, WINDOW_WIDTH, 'HUD Offset X' .. ': %.0f')
- settings.hudOffset.y = ui.slider('##' .. 'HUD Offset Y', settings.hudOffset.y, 0, WINDOW_HEIGHT, 'HUD Offset Y' .. ': %.0f')
- settings.policeSize = ui.slider('##' .. 'HUD Size', settings.policeSize, 10, 50, 'HUD Size' .. ': %.0f')
- settings.fontSize = settings.policeSize * FONT_MULT
- ui.setNextItemWidth(300)
- ui.newLine()
- uiTab()
- updateHudPos()
- ui.endGroup()
- end
- ---------------------------------------------------------------------------------------------- Utils ----------------------------------------------------------------------------------------------
- local function formatMessage(message)
- local msgToSend = message
- if pursuit.suspect == nil then
- msgToSend = string.gsub(msgToSend,"`CAR`", "No Car")
- msgToSend = string.gsub(msgToSend,"`NAME`", "No Name")
- msgToSend = string.gsub(msgToSend,"`SPEED`", "No Speed")
- return msgToSend
- end
- msgToSend = string.gsub(msgToSend,"`CAR`", string.gsub(string.gsub(ac.getCarName(pursuit.suspect.index), "%W", " "), " ", ""))
- msgToSend = string.gsub(msgToSend,"`NAME`", "@" .. ac.getDriverName(pursuit.suspect.index))
- msgToSend = string.gsub(msgToSend,"`SPEED`", string.format("%d ", ac.getCarSpeedKmh(pursuit.suspect.index) * settings.unitMult) .. settings.unit)
- return msgToSend
- end
- ---------------------------------------------------------------------------------------------- HUD ----------------------------------------------------------------------------------------------
- local policeLightsPos = {
- vec2(0,0),
- vec2(WINDOW_WIDTH/10,WINDOW_HEIGHT),
- vec2(WINDOW_WIDTH-WINDOW_WIDTH/10,0),
- vec2(WINDOW_WIDTH,WINDOW_HEIGHT)
- }
- local function showPoliceLights()
- local timing = math.floor(os.clock()*2 % 2)
- if timing == 0 then
- ui.drawRectFilledMultiColor(policeLightsPos[1], policeLightsPos[2], rgbm(1,0,0,0.5), rgbm(0,0,0,0), rgbm(0,0,0,0), rgbm(1,0,0,0.5))
- ui.drawRectFilledMultiColor(policeLightsPos[3], policeLightsPos[4], rgbm(0,0,0,0), rgbm(0,0,1,0.5), rgbm(0,0,1,0.5), rgbm(0,0,0,0))
- else
- ui.drawRectFilledMultiColor(policeLightsPos[1], policeLightsPos[2], rgbm(0,0,1,0.5), rgbm(0,0,0,0), rgbm(0,0,0,0), rgbm(0,0,1,0.5))
- ui.drawRectFilledMultiColor(policeLightsPos[3], policeLightsPos[4], rgbm(0,0,0,0), rgbm(1,0,0,0.5), rgbm(1,0,0,0.5), rgbm(0,0,0,0))
- end
- end
- local chaseLVL = {
- message = "",
- messageTimer = 0,
- color = rgbm.colors.white,
- }
- local function resetChase()
- pursuit.enable = false
- pursuit.nextMessage = 30
- pursuit.lostSight = false
- pursuit.timeLostSight = 2
- end
- local function lostSuspect()
- resetChase()
- pursuit.lostSight = false
- pursuit.timeLostSight = 0
- pursuit.level = 1
- ac.sendChatMessage(formatMessage(MSG_LOST[math.random(#MSG_LOST)]))
- pursuit.suspect = nil
- ac.setExtraSwitch(0, false)
- end
- local iconsColorOn = {
- [1] = rgbm.colors.red,
- [2] = rgbm.colors.white,
- [3] = rgbm.colors.white,
- [4] = rgbm.colors.white,
- [5] = rgbm.colors.white,
- [6] = rgbm.colors.white,
- }
- local playersInRange = {}
- local function drawImage()
- iconsColorOn[2] = rgbm.colors.white
- iconsColorOn[3] = rgbm.colors.white
- iconsColorOn[4] = rgbm.colors.white
- iconsColorOn[5] = rgbm.colors.white
- iconsColorOn[6] = rgbm.colors.white
- if ui.rectHovered(iconPos.arrest2, iconPos.arrest1) then
- iconsColorOn[2] = rgbm.colors.red
- if pursuit.suspect and pursuit.suspect.speedKmh < 50 and car.speedKmh < 20 and uiState.isMouseLeftKeyClicked then
- pursuit.hasArrested = true
- end
- elseif ui.rectHovered(iconPos.cams2, iconPos.cams1) then
- iconsColorOn[3] = rgbm.colors.red
- if uiState.isMouseLeftKeyClicked then
- if camerasOpen then camerasOpen = false
- else
- camerasOpen = true
- arrestLogsOpen = false
- if settingsOpen then
- onSettingsChange()
- settingsOpen = false
- end
- end
- end
- elseif ui.rectHovered(iconPos.lost2, iconPos.lost1) then
- iconsColorOn[4] = rgbm.colors.red
- if pursuit.suspect and uiState.isMouseLeftKeyClicked then
- lostSuspect()
- end
- elseif ui.rectHovered(iconPos.logs2, iconPos.logs1) then
- iconsColorOn[5] = rgbm.colors.red
- if uiState.isMouseLeftKeyClicked then
- if arrestLogsOpen then arrestLogsOpen = false
- else
- arrestLogsOpen = true
- camerasOpen = false
- if settingsOpen then
- onSettingsChange()
- settingsOpen = false
- end
- end
- end
- elseif ui.rectHovered(iconPos.menu2, iconPos.menu1) then
- iconsColorOn[6] = rgbm.colors.red
- if uiState.isMouseLeftKeyClicked then
- if settingsOpen then
- onSettingsChange()
- settingsOpen = false
- else
- settingsOpen = true
- arrestLogsOpen = false
- camerasOpen = false
- end
- end
- end
- ui.image(HUD_IMG.base, imageSize, rgbm.colors.white)
- ui.drawImage(HUD_IMG.radar, vec2(0,0), imageSize, iconsColorOn[1])
- ui.drawImage(HUD_IMG.arrest, vec2(0,0), imageSize, iconsColorOn[2])
- ui.drawImage(HUD_IMG.cams, vec2(0,0), imageSize, iconsColorOn[3])
- ui.drawImage(HUD_IMG.lost, vec2(0,0), imageSize, iconsColorOn[4])
- ui.drawImage(HUD_IMG.logs, vec2(0,0), imageSize, iconsColorOn[5])
- ui.drawImage(HUD_IMG.menu, vec2(0,0), imageSize, iconsColorOn[6])
- end
- local function playerSelected(suspect)
- if suspect.speedKmh > 50 then
- pursuit.suspect = suspect
- pursuit.nextMessage = 30
- pursuit.level = 1
- local msgToSend = "Officer " .. DRIVER_NAME .. " is chasing you. Run! "
- pursuit.startedTime = settings.timeMsg
- pursuit.engage = true
- acpPolice{message = msgToSend, messageType = 0, yourIndex = ac.getCar(pursuit.suspect.index).sessionID}
- ac.setExtraSwitch(0, true)
- end
- end
- local function hudInChase()
- ui.pushDWriteFont("Orbitron;Weight=Black")
- ui.sameLine(20)
- ui.beginGroup()
- ui.newLine(1)
- local textPursuit = "LVL : " .. math.floor(pursuit.level/2)
- ui.dwriteTextWrapped(ac.getDriverName(pursuit.suspect.index) .. '\n'
- .. string.gsub(string.gsub(ac.getCarName(pursuit.suspect.index), "%W", " "), " ", "")
- .. '\n' .. string.format("Speed: %d ", pursuit.suspect.speedKmh * settings.unitMult) .. settings.unit
- .. '\n' .. textPursuit, settings.fontSize/2, rgbm.colors.white)
- ui.dummy(vec2(imageSize.x/5,imageSize.y/20))
- ui.newLine(30)
- ui.sameLine()
- if ui.button('Cancel Chase', vec2(imageSize.x/5, imageSize.y/20)) then
- lostSuspect()
- end
- ui.endGroup()
- ui.popDWriteFont()
- end
- local function drawText()
- ui.pushDWriteFont("Orbitron;Weight=Bold")
- ui.dwriteDrawText("RADAR ACTIVE", playerInRangeUI.fontSize.div_1_5, playerInRangeUI.header.radar.pos, rgbm.colors.red)
- ui.popDWriteFont()
- ui.pushDWriteFont("Orbitron;Weight=Regular")
- ui.dwriteDrawText("NEARBY VEHICULE SPEED SCANNING", playerInRangeUI.fontSize.div_2_5, playerInRangeUI.header.nearby.pos, rgbm.colors.white)
- playerInRangeUI.box.pos1.y = playerInRangeUI.header.nearby.pos.y + playerInRangeUI.fontSize.div_1_2
- playerInRangeUI.box.pos2.y = playerInRangeUI.box.pos1.y + playerInRangeUI.box.offsetY
- for i = 1, #playersInRange do
- playerInRangeUI.color = rgbm(1,1,1,0.7)
- ui.drawRect(playerInRangeUI.box.pos1, playerInRangeUI.box.pos2, rgbm(1,1,1,0.1), 1)
- if ui.rectHovered(playerInRangeUI.box.pos1, playerInRangeUI.box.pos2) then
- playerInRangeUI.color = rgbm(1,0,0,1)
- if ui.mouseClicked(0) then
- playerSelected(playersInRange[i].player)
- end
- end
- playerInRangeUI.text.pos.x = (playerInRangeUI.window.size.x - ui.measureDWriteText(playersInRange[i].text, playerInRangeUI.fontSize.div_2).x) / 2
- playerInRangeUI.text.pos.y = playerInRangeUI.box.pos1.y + playerInRangeUI.text.offsetY
- ui.dwriteDrawText(playersInRange[i].text, settings.fontSize/2, vec2(playerInRangeUI.text.pos), playerInRangeUI.color)
- playerInRangeUI.box.pos1.y = playerInRangeUI.box.pos1.y + playerInRangeUI.box.offsetY
- playerInRangeUI.box.pos2.y = playerInRangeUI.box.pos1.y + playerInRangeUI.box.offsetY
- end
- ui.dummy(playerInRangeUI.box.pos1)
- ui.popDWriteFont()
- end
- local function radarUI()
- ui.toolWindow('radarText', playerInRangeUI.window.pos, playerInRangeUI.window.size, true, true, function ()
- if pursuit.suspect then hudInChase()
- else drawText() end
- end)
- ui.transparentWindow('radar', vec2(settings.hudOffset.x, settings.hudOffset.y), imageSize, true, function ()
- drawImage()
- end)
- end
- local function hidePlayers()
- local hideRange = 500
- for i = ac.getSim().carsCount - 1, 0, -1 do
- local playerCar = ac.getCar(i)
- if playerCar and playerCar.isConnected and ac.getCarBrand(i) ~= "DZST traffic" then
- if not isPoliceCar(ac.getCarID(i)) then
- if playerCar.position.x > car.position.x - hideRange and playerCar.position.z > car.position.z - hideRange and playerCar.position.x < car.position.x + hideRange and playerCar.position.z < car.position.z + hideRange then
- ac.hideCarLabels(i, false)
- else
- ac.hideCarLabels(i, true)
- end
- end
- end
- end
- end
- local RADAR_RANGE = 250
- local function radarUpdate()
- local previousSize = #playersInRange
- local j = 1
- for i, c in ac.iterateCars.serverSlots() do
- if not c.isHidingLabels and c.isConnected and not isPoliceCar(c:id()) then
- if c.position.x > car.position.x - RADAR_RANGE and c.position.z > car.position.z - RADAR_RANGE and c.position.x < car.position.x + RADAR_RANGE and c.position.z < car.position.z + RADAR_RANGE then
- playersInRange[j] = {}
- playersInRange[j].player = c
- playersInRange[j].text = string.sub(ac.getDriverName(c.index), 1, 20) .. string.format(" - %d ", c.speedKmh * settings.unitMult) .. settings.unit
- j = j + 1
- if j == 9 then break end
- end
- end
- end
- for i = j, previousSize do playersInRange[i] = nil end
- end
- ---------------------------------------------------------------------------------------------- Chase ----------------------------------------------------------------------------------------------
- local function inRange()
- local distance_x = pursuit.suspect.position.x - car.position.x
- local distance_z = pursuit.suspect.position.z - car.position.z
- local distanceSquared = distance_x * distance_x + distance_z * distance_z
- if(distanceSquared < pursuit.minDistance) then
- pursuit.enable = true
- pursuit.lostSight = false
- pursuit.timeLostSight = 2
- elseif (distanceSquared < pursuit.maxDistance) then resetChase()
- else
- if not pursuit.lostSight then
- pursuit.lostSight = true
- pursuit.timeLostSight = 2
- else
- pursuit.timeLostSight = pursuit.timeLostSight - ui.deltaTime()
- if pursuit.timeLostSight < 0 then lostSuspect() end
- end
- end
- end
- local function sendChatToSuspect()
- if pursuit.enable then
- if 0 < pursuit.nextMessage then
- pursuit.nextMessage = pursuit.nextMessage - ui.deltaTime()
- elseif pursuit.nextMessage < 0 then
- local nb = tostring(pursuit.level)
- acpPolice{message = nb, messageType = 1, yourIndex = ac.getCar(pursuit.suspect.index).sessionID}
- if pursuit.level < 10 then
- pursuit.level = pursuit.level + 1
- chaseLVL.messageTimer = settings.timeMsg
- chaseLVL.message = "CHASE LEVEL " .. math.floor(pursuit.level/2)
- if pursuit.level > 8 then
- chaseLVL.color = rgbm.colors.red
- elseif pursuit.level > 6 then
- chaseLVL.color = rgbm.colors.orange
- elseif pursuit.level > 4 then
- chaseLVL.color = rgbm.colors.yellow
- else
- chaseLVL.color = rgbm.colors.white
- end
- end
- pursuit.nextMessage = 30
- end
- end
- end
- local function showPursuitMsg()
- local text = ""
- if chaseLVL.messageTimer > 0 then
- chaseLVL.messageTimer = chaseLVL.messageTimer - ui.deltaTime()
- text = chaseLVL.message
- end
- if pursuit.startedTime > 0 then
- if pursuit.suspect then
- text = "You are chasing " .. ac.getDriverName(pursuit.suspect.index) .. " driving a " .. string.gsub(string.gsub(ac.getCarName(pursuit.suspect.index), "%W", " "), " ", "") .. " ! Get him! "
- end
- if pursuit.startedTime > 6 then showPoliceLights() end
- if pursuit.engage and pursuit.startedTime < 8 then
- ac.sendChatMessage(formatMessage(MSG_ENGAGE[math.random(#MSG_ENGAGE)]))
- pursuit.engage = false
- end
- end
- if text ~= "" then
- local textLenght = ui.measureDWriteText(text, settings.fontSizeMSG)
- local rectPos1 = vec2(settings.msgOffset.x - textLenght.x/2, settings.msgOffset.y)
- local rectPos2 = vec2(settings.msgOffset.x + textLenght.x/2, settings.msgOffset.y + settings.fontSizeMSG)
- local rectOffset = vec2(10, 10)
- if ui.time() % 1 < 0.5 then
- ui.drawRectFilled(rectPos1 - vec2(10,0), rectPos2 + rectOffset, COLORSMSGBG, 10)
- else
- ui.drawRectFilled(rectPos1 - vec2(10,0), rectPos2 + rectOffset, rgbm(0,0,0,0.5), 10)
- end
- ui.dwriteDrawText(text, settings.fontSizeMSG, rectPos1, chaseLVL.color)
- end
- end
- local function arrestSuspect()
- if pursuit.hasArrested and pursuit.suspect then
- local msgToSend = formatMessage(MSG_ARREST[math.random(#MSG_ARREST)])
- table.insert(arrestations, msgToSend .. os.date("\nDate of the Arrestation: %c"))
- ac.sendChatMessage(msgToSend .. "\nPlease Get Back Pit, GG!")
- pursuit.id = pursuit.suspect.sessionID
- player.arrests = player.arrests and player.arrests + 1 or 1
- pursuit.startedTime = 0
- pursuit.suspect = nil
- pursuit.timerArrest = 1
- player:save()
- elseif pursuit.hasArrested then
- if pursuit.timerArrest > 0 then
- pursuit.timerArrest = pursuit.timerArrest - ui.deltaTime()
- else
- acpPolice{message = "BUSTED!", messageType = 2, yourIndex = pursuit.id}
- pursuit.timerArrest = 0
- pursuit.suspect = nil
- pursuit.id = -1
- pursuit.hasArrested = false
- pursuit.startedTime = 0
- pursuit.enable = false
- pursuit.level = 1
- pursuit.nextMessage = 20
- pursuit.lostSight = false
- pursuit.timeLostSight = 0
- end
- end
- end
- local function chaseUpdate()
- if pursuit.startedTime > 0 then pursuit.startedTime = pursuit.startedTime - ui.deltaTime()
- else pursuit.startedTime = 0 end
- if pursuit.suspect then
- sendChatToSuspect()
- inRange()
- end
- arrestSuspect()
- end
- ---------------------------------------------------------------------------------------------- Menu ----------------------------------------------------------------------------------------------
- local function arrestLogsUI()
- ui.dwriteTextAligned("Arrestation Logs", 40, ui.Alignment.Center, ui.Alignment.Center, vec2(WINDOW_WIDTH/4,60), false, rgbm.colors.white)
- ui.drawLine(vec2(0,60), vec2(WINDOW_WIDTH/4,60), rgbm.colors.white, 1)
- ui.newLine(15)
- ui.sameLine(10)
- ui.beginGroup()
- local allMsg = ""
- ui.dwriteText("Click on the button next to the message you want to copy.", 15, rgbm.colors.white)
- ui.sameLine(WINDOW_WIDTH/4 - 120)
- if ui.button('Close', vec2(100, WINDOW_HEIGHT/50)) then arrestLogsOpen = false end
- for i = 1, #arrestations do
- if ui.smallButton("#" .. i .. ": ") then
- ui.setClipboardText(arrestations[i])
- end
- ui.sameLine()
- ui.dwriteTextWrapped(arrestations[i], 15, rgbm.colors.white)
- end
- if #arrestations == 0 then
- ui.dwriteText("No arrestation logs yet.", 15, rgbm.colors.white)
- end
- ui.newLine()
- if ui.button("Set all messages to ClipBoard") then
- for i = 1, #arrestations do
- allMsg = allMsg .. arrestations[i] .. "\n\n"
- end
- ui.setClipboardText(allMsg)
- end
- ui.endGroup()
- end
- local buttonPos = WINDOW_WIDTH/65
- local function camerasUI()
- ui.dwriteTextAligned("Surveillance Cameras", 40, ui.Alignment.Center, ui.Alignment.Center, vec2(WINDOW_WIDTH/6.5,60), false, rgbm.colors.white)
- ui.drawLine(vec2(0,60), vec2(WINDOW_WIDTH/6.5,60), rgbm.colors.white, 1)
- ui.newLine(20)
- ui.beginGroup()
- ui.sameLine(buttonPos)
- if ui.button('Close', vec2(WINDOW_WIDTH/6.5 - buttonPos*2,30)) then camerasOpen = false end
- ui.newLine()
- for i = 1, #CAMERAS do
- local h = math.rad(CAMERAS[i].dir + ac.getCompassAngle(vec3(0, 0, 1)))
- ui.newLine()
- ui.sameLine(buttonPos)
- if ui.button(CAMERAS[i].name, vec2(WINDOW_WIDTH/6.5 - buttonPos*2,30)) then
- ac.setCurrentCamera(ac.CameraMode.Free)
- ac.setCameraPosition(CAMERAS[i].pos)
- ac.setCameraDirection(vec3(math.sin(h), 0, math.cos(h)))
- ac.setCameraFOV(CAMERAS[i].fov)
- end
- end
- if ac.getSim().cameraMode == ac.CameraMode.Free then
- ui.newLine()
- ui.newLine()
- ui.sameLine(buttonPos)
- if ui.button('Police car camera', vec2(WINDOW_WIDTH/6.5 - buttonPos*2,30)) then ac.setCurrentCamera(ac.CameraMode.Cockpit) end
- end
- end
- local menuSize = {vec2(WINDOW_WIDTH/4, WINDOW_HEIGHT/3), vec2(WINDOW_WIDTH/6.4, WINDOW_HEIGHT/2.2)}
- local buttonPressed = false
- local function moveMenu()
- if ui.windowHovered() and ui.mouseDown() then buttonPressed = true end
- if ui.mouseReleased() then buttonPressed = false end
- if buttonPressed then settings.menuPos = settings.menuPos + ui.mouseDelta() end
- end
- ---------------------------------------------------------------------------------------------- updates ----------------------------------------------------------------------------------------------
- local initUiSize = false
- function script.drawUI()
- if not shouldRun() then return end
- if not initUiSize then
- initsettings()
- initUiSize = true
- end
- radarUI()
- if pursuit.suspect then showStarsPursuit() end
- showPursuitMsg()
- if settingsOpen then
- ui.toolWindow('settings', settings.menuPos, menuSize[2], true, function ()
- ui.childWindow('childsettings', menuSize[2], true, function () settingsWindow() moveMenu() end)
- end)
- elseif arrestLogsOpen then
- ui.toolWindow('ArrestLogs', settings.menuPos, menuSize[1], true, function ()
- ui.childWindow('childArrestLogs', menuSize[1], true, function () arrestLogsUI() moveMenu() end)
- end)
- elseif camerasOpen then
- ui.toolWindow('Cameras', settings.menuPos, menuSize[2], true, function ()
- ui.childWindow('childCameras', menuSize[2], true, function () camerasUI() moveMenu() end)
- end)
- end
- end
- local function loadSettings()
- Settings.allocate(function(allocatedSetting)
- ac.log("Settings Allocated")
- settings = allocatedSetting
- dataLoaded['Settings'] = true
- end)
- end
- local function loadPlayerData()
- Player.allocate(function(allocatedPlayer)
- if allocatedPlayer then
- player = allocatedPlayer
- player:sortSectors()
- dataLoaded['PlayerData'] = true
- updateSharedPlayerData()
- ac.log(player.arrests)
- end
- end)
- end
- local delay = 1
- function script.update(dt)
- if initialisation then
- initialisation = false
- loadSettings()
- loadPlayerData()
- end
- if not shouldRun() then return end
- if delay > 0 then delay = delay - dt end
- if delay < 0 then
- delay = 0
- updateSharedPlayerData()
- ac.broadcastSharedEvent(SHARED_EVENT_KEY, 'update')
- end
- radarUpdate()
- chaseUpdate()
- end
- ac.onCarJumped(0, function (carIndex)
- if isPoliceCar(CAR_ID) then
- if pursuit.suspect then lostSuspect() end
- end
- end)
- ui.registerOnlineExtra(ui.Icons.Settings, "Settings", nil, settingsWindow, nil, ui.OnlineExtraFlags.Tool, 'ui.WindowFlags.AlwaysAutoResize')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement