Files
ox_lib/imports/zones/client.lua
T

423 lines
10 KiB
Lua
Raw Normal View History

local glm = require 'glm'
2022-05-07 11:47:39 +10:00
Zones = {}
local function makeTriangles(t)
local t1, t2
if t[3] and t[4] then
t1 = mat(t[1], t[2], t[3])
t2 = mat(t[2], t[3], t[4])
else
t1 = mat(t[1], t[2], t[3] or t[4])
end
return t1, t2
end
2022-04-29 00:41:21 +10:00
local function getTriangles(polygon)
local triangles = {}
if polygon:isConvex() then
for i = 2, #polygon - 1 do
triangles[#triangles + 1] = mat(polygon[1], polygon[i], polygon[i + 1])
end
return triangles
end
2022-04-29 00:41:21 +10:00
local points = {}
local sides = {}
local horizontals = {}
2022-04-29 00:41:21 +10:00
for i = 1, #polygon do
2022-05-11 13:38:27 +10:00
local h
local point = polygon[i]
local unique = true
for j = 1, #horizontals do
if point.y == horizontals[j][1].y then
h = j
horizontals[j][#horizontals[j] + 1] = point
unique = false
break
end
end
if unique then
h = #horizontals + 1
horizontals[h] = { point }
end
sides[i] = { polygon[i], polygon[i + 1] or polygon[1] }
points[polygon[i]] = { side = i, horizontal = h, uses = 0 }
2022-04-29 00:41:21 +10:00
end
2022-05-11 13:38:27 +10:00
local extremes = { polygon.projectToAxis(polygon, vec(1, 0, 0)) }
for i = 1, #horizontals do
local horizontal = horizontals[i]
local hLineStart, hLineEnd = vec(extremes[1], horizontal[1].yz), vec(extremes[2], horizontal[1].yz)
for j = 1, #sides do
local sideStart, sideEnd = sides[j][1], sides[j][2]
local bool, d, d2 = glm.segment.intersectsSegment(hLineStart, hLineEnd, sideStart, sideEnd)
if d > 0.001 and d < 0.999 and d2 > 0.001 and d2 < 0.999 then
local newPoint = glm.segment.getPoint(hLineStart, hLineEnd, d)
local valid
for l = 1, #horizontal do
local point = horizontal[l]
valid = polygon.contains(polygon, (point + newPoint) / 2, 0.1)
if valid then
for m = 1, #sides do
local sideStart, sideEnd = sides[m][1], sides[m][2]
local bool, d, d2 = glm.segment.intersectsSegment(point, newPoint, sides[m][1], sides[m][2])
if d > 0.001 and d < 0.999 and d2 > 0.001 and d2 < 0.999 then
valid = false
break
2022-04-29 00:41:21 +10:00
end
end
end
if valid then
horizontals[i][#horizontals[i] + 1] = newPoint
sides[j][#sides[j] + 1] = newPoint
points[newPoint] = { side = j, horizontal = i, uses = 0 }
break
2022-04-29 00:41:21 +10:00
end
end
end
end
end
local function up(a, b)
2022-04-29 00:41:21 +10:00
return a.y > b.y
end
local function down(a, b)
2022-04-29 00:41:21 +10:00
return a.y < b.y
end
local function right(a, b)
return a.x > b.x
end
local function left(a, b)
return a.x < b.x
end
2022-04-29 00:41:21 +10:00
for i = 1, #sides do
local side = sides[i]
---@type number | function
2022-04-29 00:41:21 +10:00
local direction = side[1].y - side[2].y
direction = direction > 0 and up or down
2022-04-29 00:41:21 +10:00
table.sort(side, direction)
2022-04-29 00:41:21 +10:00
for j = 1, #side - 1 do
local a, b = side[j], side[j + 1]
local aData, bData = points[a], points[b]
local aPos, bPos
if aData.horizontal ~= bData.horizontal then
local aHorizontal, bHorizontal = horizontals[aData.horizontal], horizontals[bData.horizontal]
local c, d
if aHorizontal[2] then
---@type number | function
local direction = a.x - (a.x ~= aHorizontal[1].x and aHorizontal[1].x or aHorizontal[2].x)
direction = direction > 0 and right or left
table.sort(aHorizontal, direction)
for l = 1, #aHorizontal do
if a == aHorizontal[l] then
aPos = l
elseif c and aPos then
break
2022-04-29 00:41:21 +10:00
else
c = aHorizontal[l]
2022-04-29 00:41:21 +10:00
end
end
end
if bHorizontal[2] then
---@type number | function
local direction = b.x - (b.x ~= bHorizontal[1].x and bHorizontal[1].x or bHorizontal[2].x)
direction = direction > 0 and right or left
table.sort(bHorizontal, direction)
for l = 1, #bHorizontal do
if b == bHorizontal[l] then
bPos = l
elseif bPos and d then
break
2022-04-29 00:41:21 +10:00
else
d = bHorizontal[l]
2022-04-29 00:41:21 +10:00
end
end
end
if aData.uses < 2 then
if c and d then
if points[c].side ~= points[d].side then
local done
for l = aPos > 1 and aPos - 1 or 1, aPos > #aHorizontal and aPos + 1 or #aHorizontal do
c = aHorizontal[l]
if c ~= a then
for m = bPos > 1 and bPos - 1 or 1, bPos > #bHorizontal and bPos + 1 or #bHorizontal do
d = bHorizontal[m]
local sideDifference = points[c].side - points[d].side
if d ~= b and sideDifference >= -1 and sideDifference <= 1 then
done = true
break
end
end
end
if done then break end
2022-04-29 00:41:21 +10:00
end
end
c = polygon.contains(polygon, (a + c) / 2, 0.1) and c or nil
d = polygon.contains(polygon, (b + d) / 2, 0.1) and d or nil
2022-04-29 00:41:21 +10:00
end
if c and not d then
for l = aPos > 1 and aPos - 1 or 1, aPos < #aHorizontal and aPos + 1 or #aHorizontal do
c = aHorizontal[l]
if c and c ~= a then
local sideDifference = bData.side - points[c].side
if sideDifference >= -1 and sideDifference <= 1 then
break
end
end
2022-04-29 00:41:21 +10:00
end
elseif d and not c then
for l = bPos > 1 and bPos - 1 or 1, bPos < #bHorizontal and bPos + 1 or #bHorizontal do
d = aHorizontal[l]
if d and d ~= b then
local sideDifference = aData.side - points[d].side
if sideDifference >= -1 and sideDifference <= 1 then
break
end
2022-04-29 00:41:21 +10:00
end
end
end
if c or d then
local t = { a, b, c, d }
nTriangles = #triangles
triangles[nTriangles + 1], triangles[nTriangles + 2] = makeTriangles(t)
if c and d then
for i = 1, #t do
points[t[i]].uses += 1
end
else
for k, v in pairs(t) do
points[v].uses += 2
end
end
end
2022-04-29 00:41:21 +10:00
end
else
aData.uses += 1
bData.uses += 1
2022-04-29 00:41:21 +10:00
end
end
end
return triangles
end
local function removeZone(self)
2022-05-07 11:47:39 +10:00
Zones[self.id] = nil
end
2022-04-25 21:33:56 +10:00
local inside = {}
2022-09-11 12:06:30 +10:00
local insideCount = 0
local tick
2022-09-11 12:06:30 +10:00
local glm_polygon_contains = glm.polygon.contains
CreateThread(function()
while true do
2022-09-11 12:06:30 +10:00
if insideCount ~= 0 then
table.wipe(inside)
insideCount = 0
end
2022-09-11 11:06:38 +10:00
local coords = GetEntityCoords(cache.ped)
cache.coords = coords
2022-05-07 11:47:39 +10:00
for _, zone in pairs(Zones) do
zone.distance = #(zone.coords - coords)
2022-09-11 12:27:23 +10:00
local radius, contains = zone.radius
2022-09-11 12:27:23 +10:00
if radius then
contains = zone.distance < radius
else
contains = glm_polygon_contains(zone.polygon, coords, zone.thickness / 4)
end
if contains then
if not zone.insideZone then
zone.insideZone = true
if zone.onEnter then
zone:onEnter()
end
end
2022-04-25 21:33:56 +10:00
if zone.inside then
insideCount += 1
inside[insideCount] = zone
end
else
if zone.insideZone then
zone.insideZone = false
if zone.onExit then
zone:onExit()
end
2022-04-25 21:33:56 +10:00
end
if zone.debug then
insideCount += 1
inside[insideCount] = zone
end
end
end
if not tick then
2022-09-11 12:06:30 +10:00
if insideCount ~= 0 then
tick = SetInterval(function()
for i = 1, insideCount do
local zone = inside[i]
if zone.debug then
zone:debug()
if zone.inside and zone.insideZone then
zone:inside()
end
else
zone:inside()
end
end
end)
end
elseif insideCount == 0 then
tick = ClearInterval(tick)
end
Wait(300)
end
end)
local DrawLine = DrawLine
local DrawPoly = DrawPoly
local function debugPoly(self)
for i = 1, #self.triangles do
local triangle = self.triangles[i]
DrawPoly(triangle[1].x, triangle[1].y, triangle[1].z, triangle[2].x, triangle[2].y, triangle[2].z, triangle[3].x,
triangle[3].y, triangle[3].z, 255, 42, 24, 100)
DrawPoly(triangle[2].x, triangle[2].y, triangle[2].z, triangle[1].x, triangle[1].y, triangle[1].z, triangle[3].x,
triangle[3].y, triangle[3].z, 255, 42, 24, 100)
2022-04-27 17:13:26 +10:00
end
for i = 1, #self.polygon do
local thickness = vec(0, 0, self.thickness / 2)
local a = self.polygon[i] + thickness
local b = self.polygon[i] - thickness
local c = (self.polygon[i + 1] or self.polygon[1]) + thickness
local d = (self.polygon[i + 1] or self.polygon[1]) - thickness
DrawLine(a.x, a.y, a.z, b.x, b.y, b.z, 255, 42, 24, 225)
DrawLine(a.x, a.y, a.z, c.x, c.y, c.z, 255, 42, 24, 225)
DrawLine(b.x, b.y, b.z, d.x, d.y, d.z, 255, 42, 24, 225)
end
2022-04-27 17:13:26 +10:00
end
2022-05-06 14:28:45 +10:00
local function debugSphere(self)
DrawMarker(28, self.coords.x, self.coords.y, self.coords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, self.radius, self.radius,
self.radius, 255, 42, 24, 100, false, false, 0, true, false, false, false)
2022-05-06 14:28:45 +10:00
end
2022-04-27 17:09:32 +10:00
local function contains(self, coords)
2022-05-14 12:34:12 +10:00
return glm_polygon_contains(self.polygon, coords, self.thickness / 4)
end
2022-05-06 14:28:45 +10:00
local function insideSphere(self, coords)
return #(self.coords - coords) < self.radius
2022-04-27 17:09:32 +10:00
end
local function convertToVector(coords)
local _type = type(coords)
if _type ~= 'vector3' then
if _type == 'table' then
return vec3(coords[1] or coords.x, coords[2] or coords.y, coords[3] or coords.z)
end
error(("expected type 'vector3' or 'table' (received %s)"):format(_type))
end
return coords
end
lib.zones = {
poly = function(data)
2022-05-07 11:47:39 +10:00
data.id = #Zones + 1
data.thickness = data.thickness or 4
local pointN = #data.points
local points = table.create(pointN, 0)
for i = 1, pointN do
points[i] = convertToVector(data.points[i])
end
data.polygon = glm.polygon.new(points)
2022-05-07 11:19:27 +10:00
data.coords = data.polygon:centroid()
data.remove = removeZone
2022-04-27 17:09:32 +10:00
data.contains = contains
if data.debug then
data.triangles = getTriangles(data.polygon)
data.debug = debugPoly
end
2022-05-07 11:47:39 +10:00
Zones[data.id] = data
2022-04-26 23:06:23 +10:00
return data
end,
2022-04-26 23:06:23 +10:00
box = function(data)
2022-05-07 11:47:39 +10:00
data.id = #Zones + 1
data.coords = convertToVector(data.coords)
data.size = data.size and convertToVector(data.size) / 2 or vec3(2)
data.thickness = data.size.z * 2 or 4
2022-04-27 17:11:36 +10:00
data.rotation = quat(data.rotation or 0, vec3(0, 0, 1))
data.polygon = (data.rotation * glm.polygon.new({
vec3(data.size.x, data.size.y, 0),
vec3(-data.size.x, data.size.y, 0),
vec3(-data.size.x, -data.size.y, 0),
vec3(data.size.x, -data.size.y, 0),
2022-05-07 11:19:27 +10:00
}) + data.coords)
2022-04-26 23:06:23 +10:00
data.remove = removeZone
2022-04-27 17:09:32 +10:00
data.contains = contains
2022-04-26 23:06:23 +10:00
if data.debug then
data.triangles = { makeTriangles({ data.polygon[1], data.polygon[2], data.polygon[4], data.polygon[3] }) }
data.debug = debugPoly
end
2022-05-07 11:47:39 +10:00
Zones[data.id] = data
2022-04-25 21:33:56 +10:00
return data
2022-05-06 14:28:45 +10:00
end,
sphere = function(data)
2022-05-07 11:47:39 +10:00
data.id = #Zones + 1
data.coords = convertToVector(data.coords)
2022-05-06 14:28:45 +10:00
data.radius = (data.radius or 2) + 0.0
data.remove = removeZone
data.contains = insideSphere
if data.debug then
data.debug = debugSphere
end
2022-05-07 11:47:39 +10:00
Zones[data.id] = data
2022-05-06 14:28:45 +10:00
return data
end,
}
return lib.zones