mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 15:06:02 +01:00
Sometimes people send the incorrect types, and it's apparently on us (???) to allow them to do the wrong thing and catch their mistakes for them.
443 lines
15 KiB
Lua
443 lines
15 KiB
Lua
local glm = require 'glm'
|
|
|
|
---@class CZone
|
|
---@field id number
|
|
---@field coords vector3
|
|
---@field distance number
|
|
---@field remove fun()
|
|
---@field contains fun(self: CZone, coords?: vector3): boolean
|
|
---@field onEnter fun(self: CZone)?
|
|
---@field onExit fun(self: CZone)?
|
|
---@field inside fun(self: CZone)?
|
|
---@field [string] any
|
|
|
|
---@type { [number]: CZone }
|
|
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
|
|
|
|
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
|
|
|
|
local points = {}
|
|
local sides = {}
|
|
local horizontals = {}
|
|
for i = 1, #polygon do
|
|
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 }
|
|
end
|
|
|
|
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
|
|
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
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function up(a, b)
|
|
return a.y > b.y
|
|
end
|
|
|
|
local function down(a, b)
|
|
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
|
|
|
|
for i = 1, #sides do
|
|
local side = sides[i]
|
|
|
|
---@type number | function
|
|
local direction = side[1].y - side[2].y
|
|
direction = direction > 0 and up or down
|
|
table.sort(side, direction)
|
|
|
|
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
|
|
else
|
|
c = aHorizontal[l]
|
|
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
|
|
else
|
|
d = bHorizontal[l]
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
end
|
|
else
|
|
aData.uses += 1
|
|
bData.uses += 1
|
|
end
|
|
end
|
|
end
|
|
return triangles
|
|
end
|
|
|
|
local function removeZone(self)
|
|
Zones[self.id] = nil
|
|
end
|
|
|
|
local inside = {}
|
|
local insideCount = 0
|
|
local tick
|
|
|
|
local glm_polygon_contains = glm.polygon.contains
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
if insideCount ~= 0 then
|
|
table.wipe(inside)
|
|
insideCount = 0
|
|
end
|
|
|
|
local coords = GetEntityCoords(cache.ped)
|
|
cache.coords = coords
|
|
|
|
for _, zone in pairs(Zones) do
|
|
zone.distance = #(zone.coords - coords)
|
|
local radius, contains = zone.radius
|
|
|
|
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
|
|
|
|
if zone.inside or zone.debug then
|
|
insideCount += 1
|
|
inside[insideCount] = zone
|
|
end
|
|
else
|
|
if zone.insideZone then
|
|
zone.insideZone = false
|
|
|
|
if zone.onExit then
|
|
zone:onExit()
|
|
end
|
|
end
|
|
|
|
if zone.debug then
|
|
insideCount += 1
|
|
inside[insideCount] = zone
|
|
end
|
|
end
|
|
end
|
|
|
|
if not tick then
|
|
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)
|
|
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)
|
|
DrawPoly(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z, 255, 42, 24, 100)
|
|
DrawPoly(c.x, c.y, c.z, b.x, b.y, b.z, a.x, a.y, a.z, 255, 42, 24, 100)
|
|
DrawPoly(b.x, b.y, b.z, c.x, c.y, c.z, d.x, d.y, d.z, 255, 42, 24, 100)
|
|
DrawPoly(d.x, d.y, d.z, c.x, c.y, c.z, b.x, b.y, b.z, 255, 42, 24, 100)
|
|
end
|
|
end
|
|
|
|
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)
|
|
end
|
|
|
|
local function contains(self, coords)
|
|
return glm_polygon_contains(self.polygon, coords, self.thickness / 4)
|
|
end
|
|
|
|
local function insideSphere(self, coords)
|
|
return #(self.coords - coords) < self.radius
|
|
end
|
|
|
|
local function convertToVector(coords)
|
|
local _type = type(coords)
|
|
|
|
if _type ~= 'vector3' then
|
|
if _type == 'table' or _type == 'vector4' 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 = {
|
|
---@return CZone
|
|
poly = function(data)
|
|
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)
|
|
data.coords = data.polygon:centroid()
|
|
data.remove = removeZone
|
|
data.contains = contains
|
|
|
|
if data.debug then
|
|
data.triangles = getTriangles(data.polygon)
|
|
data.debug = debugPoly
|
|
end
|
|
|
|
Zones[data.id] = data
|
|
return data
|
|
end,
|
|
|
|
---@return CZone
|
|
box = function(data)
|
|
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
|
|
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),
|
|
}) + data.coords)
|
|
data.remove = removeZone
|
|
data.contains = contains
|
|
|
|
if data.debug then
|
|
data.triangles = { makeTriangles({ data.polygon[1], data.polygon[2], data.polygon[4], data.polygon[3] }) }
|
|
data.debug = debugPoly
|
|
end
|
|
|
|
Zones[data.id] = data
|
|
return data
|
|
end,
|
|
|
|
---@return CZone
|
|
sphere = function(data)
|
|
data.id = #Zones + 1
|
|
data.coords = convertToVector(data.coords)
|
|
data.radius = (data.radius or 2) + 0.0
|
|
data.remove = removeZone
|
|
data.contains = insideSphere
|
|
|
|
if data.debug then
|
|
data.debug = debugSphere
|
|
end
|
|
|
|
Zones[data.id] = data
|
|
return data
|
|
end,
|
|
}
|
|
|
|
return lib.zones
|