refactor(client/zones): add types and change indentation

This commit is contained in:
Linden
2022-09-14 08:08:00 +10:00
parent 9a17b9bac5
commit 62d56fa885

View File

@@ -1,223 +1,235 @@
local glm = require 'glm' 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)
---@field onEnter fun(self: CZone)?
---@field onExit fun(self: CZone)?
---@field inside fun(self: CZone)?
---@type { [number]: CZone }
Zones = {} Zones = {}
local function makeTriangles(t) local function makeTriangles(t)
local t1, t2 local t1, t2
if t[3] and t[4] then if t[3] and t[4] then
t1 = mat(t[1], t[2], t[3]) t1 = mat(t[1], t[2], t[3])
t2 = mat(t[2], t[3], t[4]) t2 = mat(t[2], t[3], t[4])
else else
t1 = mat(t[1], t[2], t[3] or t[4]) t1 = mat(t[1], t[2], t[3] or t[4])
end end
return t1, t2 return t1, t2
end end
local function getTriangles(polygon) local function getTriangles(polygon)
local triangles = {} local triangles = {}
if polygon:isConvex() then if polygon:isConvex() then
for i = 2, #polygon - 1 do for i = 2, #polygon - 1 do
triangles[#triangles + 1] = mat(polygon[1], polygon[i], polygon[i + 1]) triangles[#triangles + 1] = mat(polygon[1], polygon[i], polygon[i + 1])
end end
return triangles return triangles
end end
local points = {} local points = {}
local sides = {} local sides = {}
local horizontals = {} local horizontals = {}
for i = 1, #polygon do for i = 1, #polygon do
local h local h
local point = polygon[i] local point = polygon[i]
local unique = true local unique = true
for j = 1, #horizontals do for j = 1, #horizontals do
if point.y == horizontals[j][1].y then if point.y == horizontals[j][1].y then
h = j h = j
horizontals[j][#horizontals[j] + 1] = point horizontals[j][#horizontals[j] + 1] = point
unique = false unique = false
break break
end end
end end
if unique then if unique then
h = #horizontals + 1 h = #horizontals + 1
horizontals[h] = { point } horizontals[h] = { point }
end end
sides[i] = { polygon[i], polygon[i + 1] or polygon[1] } sides[i] = { polygon[i], polygon[i + 1] or polygon[1] }
points[polygon[i]] = { side = i, horizontal = h, uses = 0 } points[polygon[i]] = { side = i, horizontal = h, uses = 0 }
end end
local extremes = { polygon.projectToAxis(polygon, vec(1, 0, 0)) } local extremes = { polygon.projectToAxis(polygon, vec(1, 0, 0)) }
for i = 1, #horizontals do for i = 1, #horizontals do
local horizontal = horizontals[i] local horizontal = horizontals[i]
local hLineStart, hLineEnd = vec(extremes[1], horizontal[1].yz), vec(extremes[2], horizontal[1].yz) local hLineStart, hLineEnd = vec(extremes[1], horizontal[1].yz), vec(extremes[2], horizontal[1].yz)
for j = 1, #sides do for j = 1, #sides do
local sideStart, sideEnd = sides[j][1], sides[j][2] local sideStart, sideEnd = sides[j][1], sides[j][2]
local bool, d, d2 = glm.segment.intersectsSegment(hLineStart, hLineEnd, sideStart, sideEnd) 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 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 newPoint = glm.segment.getPoint(hLineStart, hLineEnd, d)
local valid local valid
for l = 1, #horizontal do for l = 1, #horizontal do
local point = horizontal[l] local point = horizontal[l]
valid = polygon.contains(polygon, (point + newPoint) / 2, 0.1) valid = polygon.contains(polygon, (point + newPoint) / 2, 0.1)
if valid then if valid then
for m = 1, #sides do for m = 1, #sides do
local sideStart, sideEnd = sides[m][1], sides[m][2] local sideStart, sideEnd = sides[m][1], sides[m][2]
local bool, d, d2 = glm.segment.intersectsSegment(point, newPoint, 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 if d > 0.001 and d < 0.999 and d2 > 0.001 and d2 < 0.999 then
valid = false valid = false
break break
end end
end end
end end
if valid then if valid then
horizontals[i][#horizontals[i] + 1] = newPoint horizontals[i][#horizontals[i] + 1] = newPoint
sides[j][#sides[j] + 1] = newPoint sides[j][#sides[j] + 1] = newPoint
points[newPoint] = { side = j, horizontal = i, uses = 0 } points[newPoint] = { side = j, horizontal = i, uses = 0 }
break break
end end
end end
end end
end end
end end
local function up(a, b) local function up(a, b)
return a.y > b.y return a.y > b.y
end end
local function down(a, b) local function down(a, b)
return a.y < b.y return a.y < b.y
end end
local function right(a, b) local function right(a, b)
return a.x > b.x return a.x > b.x
end end
local function left(a, b) local function left(a, b)
return a.x < b.x return a.x < b.x
end end
for i = 1, #sides do for i = 1, #sides do
local side = sides[i] local side = sides[i]
---@type number | function ---@type number | function
local direction = side[1].y - side[2].y local direction = side[1].y - side[2].y
direction = direction > 0 and up or down direction = direction > 0 and up or down
table.sort(side, direction) table.sort(side, direction)
for j = 1, #side - 1 do for j = 1, #side - 1 do
local a, b = side[j], side[j + 1] local a, b = side[j], side[j + 1]
local aData, bData = points[a], points[b] local aData, bData = points[a], points[b]
local aPos, bPos local aPos, bPos
if aData.horizontal ~= bData.horizontal then if aData.horizontal ~= bData.horizontal then
local aHorizontal, bHorizontal = horizontals[aData.horizontal], horizontals[bData.horizontal] local aHorizontal, bHorizontal = horizontals[aData.horizontal], horizontals[bData.horizontal]
local c, d local c, d
if aHorizontal[2] then if aHorizontal[2] then
---@type number | function ---@type number | function
local direction = a.x - (a.x ~= aHorizontal[1].x and aHorizontal[1].x or aHorizontal[2].x) local direction = a.x - (a.x ~= aHorizontal[1].x and aHorizontal[1].x or aHorizontal[2].x)
direction = direction > 0 and right or left direction = direction > 0 and right or left
table.sort(aHorizontal, direction) table.sort(aHorizontal, direction)
for l = 1, #aHorizontal do for l = 1, #aHorizontal do
if a == aHorizontal[l] then if a == aHorizontal[l] then
aPos = l aPos = l
elseif c and aPos then elseif c and aPos then
break break
else else
c = aHorizontal[l] c = aHorizontal[l]
end end
end end
end end
if bHorizontal[2] then if bHorizontal[2] then
---@type number | function ---@type number | function
local direction = b.x - (b.x ~= bHorizontal[1].x and bHorizontal[1].x or bHorizontal[2].x) local direction = b.x - (b.x ~= bHorizontal[1].x and bHorizontal[1].x or bHorizontal[2].x)
direction = direction > 0 and right or left direction = direction > 0 and right or left
table.sort(bHorizontal, direction) table.sort(bHorizontal, direction)
for l = 1, #bHorizontal do for l = 1, #bHorizontal do
if b == bHorizontal[l] then if b == bHorizontal[l] then
bPos = l bPos = l
elseif bPos and d then elseif bPos and d then
break break
else else
d = bHorizontal[l] d = bHorizontal[l]
end end
end end
end end
if aData.uses < 2 then if aData.uses < 2 then
if c and d then if c and d then
if points[c].side ~= points[d].side then if points[c].side ~= points[d].side then
local done local done
for l = aPos > 1 and aPos - 1 or 1, aPos > #aHorizontal and aPos + 1 or #aHorizontal do for l = aPos > 1 and aPos - 1 or 1, aPos > #aHorizontal and aPos + 1 or #aHorizontal do
c = aHorizontal[l] c = aHorizontal[l]
if c ~= a then if c ~= a then
for m = bPos > 1 and bPos - 1 or 1, bPos > #bHorizontal and bPos + 1 or #bHorizontal do for m = bPos > 1 and bPos - 1 or 1, bPos > #bHorizontal and bPos + 1 or #bHorizontal do
d = bHorizontal[m] d = bHorizontal[m]
local sideDifference = points[c].side - points[d].side local sideDifference = points[c].side - points[d].side
if d ~= b and sideDifference >= -1 and sideDifference <= 1 then if d ~= b and sideDifference >= -1 and sideDifference <= 1 then
done = true done = true
break break
end end
end end
end end
if done then break end if done then break end
end end
end end
c = polygon.contains(polygon, (a + c) / 2, 0.1) and c or nil 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 d = polygon.contains(polygon, (b + d) / 2, 0.1) and d or nil
end end
if c and not d then if c and not d then
for l = aPos > 1 and aPos - 1 or 1, aPos < #aHorizontal and aPos + 1 or #aHorizontal do for l = aPos > 1 and aPos - 1 or 1, aPos < #aHorizontal and aPos + 1 or #aHorizontal do
c = aHorizontal[l] c = aHorizontal[l]
if c and c ~= a then if c and c ~= a then
local sideDifference = bData.side - points[c].side local sideDifference = bData.side - points[c].side
if sideDifference >= -1 and sideDifference <= 1 then if sideDifference >= -1 and sideDifference <= 1 then
break break
end end
end end
end end
elseif d and not c then elseif d and not c then
for l = bPos > 1 and bPos - 1 or 1, bPos < #bHorizontal and bPos + 1 or #bHorizontal do for l = bPos > 1 and bPos - 1 or 1, bPos < #bHorizontal and bPos + 1 or #bHorizontal do
d = aHorizontal[l] d = aHorizontal[l]
if d and d ~= b then if d and d ~= b then
local sideDifference = aData.side - points[d].side local sideDifference = aData.side - points[d].side
if sideDifference >= -1 and sideDifference <= 1 then if sideDifference >= -1 and sideDifference <= 1 then
break break
end end
end end
end end
end end
if c or d then if c or d then
local t = { a, b, c, d } local t = { a, b, c, d }
nTriangles = #triangles nTriangles = #triangles
triangles[nTriangles + 1], triangles[nTriangles + 2] = makeTriangles(t) triangles[nTriangles + 1], triangles[nTriangles + 2] = makeTriangles(t)
if c and d then if c and d then
for i = 1, #t do for i = 1, #t do
points[t[i]].uses += 1 points[t[i]].uses += 1
end end
else else
for k, v in pairs(t) do for k, v in pairs(t) do
points[v].uses += 2 points[v].uses += 2
end end
end end
end end
end end
else else
aData.uses += 1 aData.uses += 1
bData.uses += 1 bData.uses += 1
end end
end end
end end
return triangles return triangles
end end
local function removeZone(self) local function removeZone(self)
Zones[self.id] = nil Zones[self.id] = nil
end end
local inside = {} local inside = {}
@@ -227,196 +239,199 @@ local tick
local glm_polygon_contains = glm.polygon.contains local glm_polygon_contains = glm.polygon.contains
CreateThread(function() CreateThread(function()
while true do while true do
if insideCount ~= 0 then if insideCount ~= 0 then
table.wipe(inside) table.wipe(inside)
insideCount = 0 insideCount = 0
end end
local coords = GetEntityCoords(cache.ped) local coords = GetEntityCoords(cache.ped)
cache.coords = coords cache.coords = coords
for _, zone in pairs(Zones) do for _, zone in pairs(Zones) do
zone.distance = #(zone.coords - coords) zone.distance = #(zone.coords - coords)
local radius, contains = zone.radius local radius, contains = zone.radius
if radius then if radius then
contains = zone.distance < radius contains = zone.distance < radius
else else
contains = glm_polygon_contains(zone.polygon, coords, zone.thickness / 4) contains = glm_polygon_contains(zone.polygon, coords, zone.thickness / 4)
end end
if contains then if contains then
if not zone.insideZone then if not zone.insideZone then
zone.insideZone = true zone.insideZone = true
if zone.onEnter then if zone.onEnter then
zone:onEnter() zone:onEnter()
end end
end end
if zone.inside then if zone.inside then
insideCount += 1 insideCount += 1
inside[insideCount] = zone inside[insideCount] = zone
end end
else else
if zone.insideZone then if zone.insideZone then
zone.insideZone = false zone.insideZone = false
if zone.onExit then if zone.onExit then
zone:onExit() zone:onExit()
end end
end end
if zone.debug then if zone.debug then
insideCount += 1 insideCount += 1
inside[insideCount] = zone inside[insideCount] = zone
end end
end end
end end
if not tick then if not tick then
if insideCount ~= 0 then if insideCount ~= 0 then
tick = SetInterval(function() tick = SetInterval(function()
for i = 1, insideCount do for i = 1, insideCount do
local zone = inside[i] local zone = inside[i]
if zone.debug then
zone:debug()
if zone.debug then if zone.inside and zone.insideZone then
zone:debug() zone:inside()
end
else
zone:inside()
end
end
end)
end
elseif insideCount == 0 then
tick = ClearInterval(tick)
end
if zone.inside and zone.insideZone then Wait(300)
zone:inside() end
end
else
zone:inside()
end
end
end)
end
elseif insideCount == 0 then
tick = ClearInterval(tick)
end
Wait(300)
end
end) end)
local DrawLine = DrawLine local DrawLine = DrawLine
local DrawPoly = DrawPoly local DrawPoly = DrawPoly
local function debugPoly(self) local function debugPoly(self)
for i = 1, #self.triangles do for i = 1, #self.triangles do
local triangle = self.triangles[i] 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, 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)
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
end ,
for i = 1, #self.polygon do triangle[3].y, triangle[3].z, 255, 42, 24, 100)
local thickness = vec(0, 0, self.thickness / 2) end
local a = self.polygon[i] + thickness for i = 1, #self.polygon do
local b = self.polygon[i] - thickness local thickness = vec(0, 0, self.thickness / 2)
local c = (self.polygon[i + 1] or self.polygon[1]) + thickness local a = self.polygon[i] + thickness
local d = (self.polygon[i + 1] or self.polygon[1]) - thickness local b = self.polygon[i] - thickness
DrawLine(a.x, a.y, a.z, b.x, b.y, b.z, 255, 42, 24, 225) local c = (self.polygon[i + 1] or self.polygon[1]) + thickness
DrawLine(a.x, a.y, a.z, c.x, c.y, c.z, 255, 42, 24, 225) local d = (self.polygon[i + 1] or self.polygon[1]) - thickness
DrawLine(b.x, b.y, b.z, d.x, d.y, d.z, 255, 42, 24, 225) DrawLine(a.x, a.y, a.z, b.x, b.y, b.z, 255, 42, 24, 225)
end 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
end end
local function debugSphere(self) 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, 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) self.radius, 255, 42, 24, 100, false, false, 0, true, false, false, false)
end end
local function contains(self, coords) local function contains(self, coords)
return glm_polygon_contains(self.polygon, coords, self.thickness / 4) return glm_polygon_contains(self.polygon, coords, self.thickness / 4)
end end
local function insideSphere(self, coords) local function insideSphere(self, coords)
return #(self.coords - coords) < self.radius return #(self.coords - coords) < self.radius
end end
local function convertToVector(coords) local function convertToVector(coords)
local _type = type(coords) local _type = type(coords)
if _type ~= 'vector3' then if _type ~= 'vector3' then
if _type == 'table' then if _type == 'table' then
return vec3(coords[1] or coords.x, coords[2] or coords.y, coords[3] or coords.z) return vec3(coords[1] or coords.x, coords[2] or coords.y, coords[3] or coords.z)
end end
error(("expected type 'vector3' or 'table' (received %s)"):format(_type)) error(("expected type 'vector3' or 'table' (received %s)"):format(_type))
end end
return coords return coords
end end
lib.zones = { lib.zones = {
poly = function(data) ---@return CZone
data.id = #Zones + 1 poly = function(data)
data.thickness = data.thickness or 4 data.id = #Zones + 1
data.thickness = data.thickness or 4
local pointN = #data.points local pointN = #data.points
local points = table.create(pointN, 0) local points = table.create(pointN, 0)
for i = 1, pointN do for i = 1, pointN do
points[i] = convertToVector(data.points[i]) points[i] = convertToVector(data.points[i])
end end
data.polygon = glm.polygon.new(points) data.polygon = glm.polygon.new(points)
data.coords = data.polygon:centroid()
data.remove = removeZone
data.contains = contains
data.coords = data.polygon:centroid() if data.debug then
data.remove = removeZone data.triangles = getTriangles(data.polygon)
data.contains = contains data.debug = debugPoly
end
if data.debug then Zones[data.id] = data
data.triangles = getTriangles(data.polygon) return data
data.debug = debugPoly end,
end
Zones[data.id] = data ---@return CZone
return data box = function(data)
end, 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
box = function(data) if data.debug then
data.id = #Zones + 1 data.triangles = { makeTriangles({ data.polygon[1], data.polygon[2], data.polygon[4], data.polygon[3] }) }
data.coords = convertToVector(data.coords) data.debug = debugPoly
data.size = data.size and convertToVector(data.size) / 2 or vec3(2) end
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 Zones[data.id] = data
data.triangles = { makeTriangles({ data.polygon[1], data.polygon[2], data.polygon[4], data.polygon[3] }) } return data
data.debug = debugPoly end,
end
Zones[data.id] = data ---@return CZone
return data sphere = function(data)
end, data.id = #Zones + 1
data.coords = convertToVector(data.coords)
data.radius = (data.radius or 2) + 0.0
data.remove = removeZone
data.contains = insideSphere
sphere = function(data) if data.debug then
data.id = #Zones + 1 data.debug = debugSphere
data.coords = convertToVector(data.coords) end
data.radius = (data.radius or 2) + 0.0
data.remove = removeZone
data.contains = insideSphere
if data.debug then Zones[data.id] = data
data.debug = debugSphere return data
end end,
Zones[data.id] = data
return data
end,
} }
return lib.zones return lib.zones