first commit

This commit is contained in:
Fierelier 2021-03-11 12:02:13 +01:00
commit a7466b2e68
10 changed files with 31723 additions and 0 deletions

93
MinHeap.lua Normal file
View File

@ -0,0 +1,93 @@
MinHeap = {}
MinHeap.__index = MinHeap
local floor = math.floor
function MinHeap.new()
return setmetatable({ n = 0 }, MinHeap)
end
function MinHeap:insertvalue(num)
self[self.n] = num
self.n = self.n + 1
local child = self.n - 1
local parent, temp
while child > 0 do
parent = floor((child - 1)/2)
if self[parent] <= self[child] then
break
end
temp = self[parent]
self[parent] = self[child]
self[child] = temp
child = parent
end
return true
end
function MinHeap:findindex(num, root)
root = root or 0
if root >= self.n or num < self[root] then
return false
end
if num == self[root] then
return root
end
return self:findindex(num, root*2 + 1) or self:findindex(num, root*2 + 2)
end
function MinHeap:deleteindex(index)
if index < 0 or index >= self.n then
return false
end
local deleted = self[index]
self[index] = self[self.n-1]
self[self.n-1] = nil
self.n = self.n - 1
local parent = index
local child, temp
while true do
child = parent*2 + 1
if child >= self.n then
break
end
if child < self.n - 1 and self[child+1] < self[child] then
child = child + 1
end
if self[parent] <= self[child] then
break
end
temp = self[parent]
self[parent] = self[child]
self[child] = temp
parent = child
end
return deleted
end
function MinHeap:deletevalue(num)
local index = self:findindex(num)
if not index then
return false
end
return self:deleteindex(index)
end
function MinHeap:size()
return self.n
end
function MinHeap:empty()
return self.n == 0
end
function MinHeap:__tostring(index, depth)
index = index or 0
depth = depth or 0
if index >= self.n then
return ''
end
return (' '):rep(depth) .. tostring(self[index]) .. '\n' .. self:__tostring(index*2+1, depth+1) .. self:__tostring(index*2+2, depth+1)
end

189
ai.client.lua Normal file
View File

@ -0,0 +1,189 @@
function aiGetClosestNode(x,y,z,path)
local cDist = nil
local cNode = nil
local cNodeID = nil
for i,node in ipairs(path) do
local dist = getDistanceBetweenPoints3D(x,y,z,node.x,node.y,node.z)
if cDist == nil then
cDist = dist
cNodeID = i
cNode = node
end
if dist < cDist then
cDist = dist
cNodeID = i
cNode = node
end
end
return cNodeID,cNode
end
function aiGetNextNode(path,nodeID,minDist)
minDist = minDist or 0
local prevX = path[nodeID].x
local prevY = path[nodeID].y
local prevZ = path[nodeID].z
local dist = 0
nodeID = nodeID + 1
local length = #path
while nodeID <= length do
local x = path[nodeID].x
local y = path[nodeID].y
local z = path[nodeID].z
dist = dist + getDistanceBetweenPoints3D(x,y,z,prevX,prevY,prevZ)
if dist >= minDist then
return nodeID,path[nodeID]
end
prevX = x
prevY = y
prevZ = z
nodeID = nodeID + 1
end
nodeID = length
return nodeID,path[nodeID]
end
function handleGpsAi(driver)
local gx = getElementData(driver,"gps-ai.x")
local gy = getElementData(driver,"gps-ai.y")
local gz = getElementData(driver,"gps-ai.z")
local veh = getPedOccupiedVehicle(driver)
local x,y,z = getElementPosition(veh)
local rx,ry,rz = getElementRotation(veh)
local speed = getElementSpeed(veh)
if not getElementData(driver,"gps-ai.path") then
local path = calculatePathByCoords(x,y,z,gx,gy,gz)
if not path then return end
setElementData(driver,"gps-ai.path",path,false)
end
local path = getElementData(driver,"gps-ai.path")
local closestNodeID,closestNode = aiGetClosestNode(x,y,z,path)
local checkNodeOneID,checkNodeOne = aiGetNextNode(path,closestNodeID,20 + ((speed * 0.15) * (speed * 0.01)))
local checkNodeTwoID,checkNodeTwo = aiGetNextNode(path,closestNodeID,20 + ((speed * 0.4) * (speed * 0.01)))
--[[ hit = processLineOfSight(x,y,z,checkNodeOne.x,checkNodeOne.y,checkNodeOne.z + 0.5,true,false,false,true,false,false,false,false,nil,false,false)
if hit then
checkNodeOneID,checkNodeOne = aiGetNextNode(path,closestNodeID)
end ]]--
local reqzOne = findRotation(x,y,checkNodeOne.x,checkNodeOne.y)
local reqzTwo = findRotation(x,y,checkNodeTwo.x,checkNodeTwo.y)
reqzOneLocal = rz - reqzOne
if reqzOneLocal > 180 then reqzOneLocal = reqzOneLocal - 360 end
if reqzOneLocal < -180 then reqzOneLocal = reqzOneLocal + 360 end
reqzTwoLocal = rz - reqzTwo
if reqzTwoLocal > 180 then reqzTwoLocal = reqzTwoLocal - 360 end
if reqzTwoLocal < -180 then reqzTwoLocal = reqzTwoLocal + 360 end
local steeringAngleOne = reqzOneLocal
if steeringAngleOne < 0 then
steeringAngleOne = 0 - steeringAngleOne
end
steeringAmountOne = steeringAngleOne / 180
local steeringAngleTwo = reqzTwoLocal
if steeringAngleTwo < 0 then
steeringAngleTwo = 0 - steeringAngleTwo
end
steeringAmountTwo = steeringAngleTwo / 180
local brake = false
--[[ if steeringAngleOne > steeringAngleTwo then
regzTwo = regzOne
regzTwoLocal = regzOneLocal
steeringAngleTwo = steeringAngleOne
steeringAmountTwo = steeringAmountOne
brake = true
end ]]--
local maxSpeed = getElementData(driver,"gps-ai.maxSpeed")
if steeringAngleTwo > 10 then
local owo = steeringAmountTwo * 2.5
if owo > 1 then owo = 1 end
local nMaxSpeed = 15 + (130 * (1 - owo))
if nMaxSpeed < maxSpeed then maxSpeed = nMaxSpeed end
end
if brake then maxSpeed = maxSpeed * 0.5 end
if speed < maxSpeed then
setPedControlState(driver,"accelerate",true)
setPedAnalogControlState(driver,"brake_reverse",0)
else
setPedControlState(driver,"accelerate",false)
end
if speed > maxSpeed + 5 then
setPedAnalogControlState(driver,"brake_reverse",0.8)
end
local currentSteer = getPedAnalogControlState(driver,"vehicle_right") - getPedAnalogControlState(driver,"vehicle_left")
local steerSpeed = 0.2
if reqzOneLocal < 0 then
currentSteer = currentSteer - steerSpeed
end
if reqzOneLocal > 0 then
currentSteer = currentSteer + steerSpeed
end
steerLimit = steeringAngleOne / 10
if steerLimit < 0.4 then steerLimit = 0.4 end
if steerLimit > 1 then steerLimit = 1 end
if currentSteer > steerLimit then currentSteer = steerLimit end
if currentSteer < 0 - steerLimit then currentSteer = 0 - steerLimit end
if currentSteer > 0 then
setPedAnalogControlState(driver,"vehicle_left",0)
setPedAnalogControlState(driver,"vehicle_right",currentSteer)
end
if currentSteer < 0 then
setPedAnalogControlState(driver,"vehicle_right",0)
setPedAnalogControlState(driver,"vehicle_left",0 - currentSteer)
end
-- DEBUG --
local resx,resy = guiGetScreenSize()
dxDrawLine3D(x,y,z,checkNodeOne.x,checkNodeOne.y,checkNodeOne.z + 1,tocolor(255,0,0,128),10)
dxDrawLine3D(x,y,z,checkNodeTwo.x,checkNodeTwo.y,checkNodeTwo.z + 1,tocolor(255,255,0,128),10)
local debugText = ""
debugText = debugText .. "Required Z 1: " ..reqzOne
debugText = debugText .. "\nRequired Z 2: " ..reqzTwo
debugText = debugText .. "\n"
debugText = debugText .. "\nRequired Z 1 - LOCAL: " ..reqzOneLocal
debugText = debugText .. "\nMax speed: " ..maxSpeed.. " (" ..steeringAmountTwo.. ")"
local px,py,pz = getElementPosition(driver)
local dbx,dby,dbz = getScreenFromWorldPosition(px,py,pz + 1)
dxDrawText(debugText,dbx + 1,dby + 1,dbx + 1,dby + 1,tocolor(0,0,0),1,"default-bold","center","center",false,true,true)
dxDrawText(debugText,dbx,dby,dbx,dby,tocolor(255,255,0),1,"default-bold","center","center",false,true,true)
end
function handleGpsAiFrame()
for _,ped in ipairs(getElementsByType("ped")) do
if getElementData(ped,"gps-ai.x") then
handleGpsAi(ped)
end
end
end
addEventHandler("onClientRender",root,handleGpsAiFrame)
function getElementSpeed(theElement)
return (Vector3(getElementVelocity(theElement)) * 180).length
end
function findRotation( x1, y1, x2, y2 )
local t = -math.deg( math.atan2( x2 - x1, y2 - y1 ) )
return t < 0 and t + 360 or t
end

17
ai.server.lua Normal file
View File

@ -0,0 +1,17 @@
addCommandHandler('drive', function(player, command, tox, toy, toz, maxSpeed)
if not getPedOccupiedVehicle(player) or getPedOccupiedVehicleSeat(player) ~= 0 then
outputChatBox("You need to be driving a vehicle.",player,255,0,0)
--return
end
x,y,z = getElementPosition(player)
veh = getPedOccupiedVehicle(player)
maxSpeed = maxSpeed or 50
driver = createPed(107,x,y,z)
setElementData(driver,"gps-ai.x",tox)
setElementData(driver,"gps-ai.y",toy)
setElementData(driver,"gps-ai.z",toz)
setElementData(driver,"gps-ai.maxSpeed",tonumber(maxSpeed))
warpPedIntoVehicle(player,veh,1)
warpPedIntoVehicle(driver,veh,0)
end)

87
client.lua Normal file
View File

@ -0,0 +1,87 @@
local floor = math.floor
addCommandHandler('path',
function(command, node1, node2)
if not tonumber(node1) or not tonumber(node2) then
outputChatBox("Usage: /path node1 node2", 255, 0, 0)
return
end
local path = server.calculatePathByNodeIDs(tonumber(node1), tonumber(node2))
if not path then
outputConsole('No path found')
return
end
server.spawnPlayer(getLocalPlayer(), path[1].x, path[1].y, path[1].z)
fadeCamera(true)
setCameraTarget(getLocalPlayer())
removeLinePoints ( )
table.each(getElementsByType('marker'), destroyElement)
for i,node in ipairs(path) do
createMarker(node.x, node.y, node.z, 'corona', 5, 50, 0, 255, 200)
addLinePoint ( node.x, node.y )
end
end
)
addCommandHandler('path2',
function(command, tox, toy, toz)
if not tonumber(tox) or not tonumber(toy) then
outputChatBox("Usage: /path2 x y z (z is optional)", 255, 0, 0)
return
end
local x,y,z = getElementPosition(getLocalPlayer())
local path = calculatePathByCoords(x, y, z, tox, toy, toz)
if not path then
outputConsole('No path found')
return
end
removeLinePoints ( )
table.each(getElementsByType('marker'), destroyElement)
for i,node in ipairs(path) do
createMarker(node.x, node.y, node.z, 'corona', 5, 50, 0, 255, 200)
addLinePoint ( node.x, node.y )
end
end
)
local function getAreaID(x, y)
return math.floor((y + 3000)/750)*8 + math.floor((x + 3000)/750)
end
local function getNodeByID(db, nodeID)
local areaID = floor(nodeID / 65536)
return db[areaID][nodeID]
end
--[[
addEventHandler('onClientRender', getRootElement(),
function()
local db = vehicleNodes
local camX, camY, camZ = getCameraMatrix()
local x, y, z = getElementPosition(getLocalPlayer())
local areaID = getAreaID(x, y)
local drawn = {}
for id,node in pairs(db[areaID]) do
if getDistanceBetweenPoints3D(x, y, z, node.x, node.y, z) < 300 then
--[/[
local screenX, screenY = getScreenFromWorldPosition(node.x, node.y, node.z)
if screenX then
dxDrawText(tostring(id), screenX - 10, screenY - 5)
end
--]/]
--[/[
for neighbourid,distance in pairs(node.neighbours) do
if not drawn[neighbourid .. '-' .. id] then
local neighbour = getNodeByID(db, neighbourid)
dxDrawLine3D(node.x, node.y, node.z + 1, neighbour.x, neighbour.y, neighbour.z + 1, tocolor(0, 0, 200, 255), 3)
drawn[id .. '-' .. neighbourid] = true
end
end
--]/]
end
end
end
)
--]]

109
gps.lua Normal file
View File

@ -0,0 +1,109 @@
local root = getRootElement()
local floor = math.floor
local allowedRPC = {
calculatePathByCoords = true,
calculatePathByNodeIDs = true,
spawnPlayer = true
}
local function getAreaID(x, y)
return floor((y + 3000)/750)*8 + floor((x + 3000)/750)
end
local function getNodeByID(db, nodeID)
local areaID = floor(nodeID / 65536)
return db[areaID][nodeID]
end
local function findNodeClosestToPoint(db, x, y, z)
local areaID = getAreaID(x, y)
local minDist, minNode
local nodeX, nodeY, dist
for id,node in pairs(db[areaID]) do
nodeX, nodeY = node.x, node.y
dist = (x - nodeX)*(x - nodeX) + (y - nodeY)*(y - nodeY)
if not minDist or dist < minDist then
minDist = dist
minNode = node
end
end
return minNode
end
local function calculatePath(db, nodeFrom, nodeTo)
local next = next
local g = { [nodeFrom] = 0 } -- { node = g }
local hcache = {} -- { node = h }
local parent = {} -- { node = parent }
local openheap = MinHeap.new()
local function h(node)
if hcache[node] then
return hcache[node]
end
local x, y, z = node.x - nodeTo.x, node.y - nodeTo.y, node.z - nodeTo.z
hcache[node] = x*x + y*y + z*z
return hcache[node]
end
local nodeMT = {
__lt = function(a, b)
return g[a] + h(a) < g[b] + h(b)
end,
__le = function(a, b)
if not g[a] or not g[b] then
outputConsole(debug.traceback())
end
return g[a] + h(a) <= g[b] + h(b)
end
}
setmetatable(nodeFrom, nodeMT)
openheap:insertvalue(nodeFrom)
local current
while not openheap:empty() do
current = openheap:deleteindex(0)
if current == nodeTo then
break
end
local successors = {}
for id,distance in pairs(current.neighbours) do
local successor = getNodeByID(db, id)
local successor_g = g[current] + distance*distance
if not g[successor] or g[successor] > successor_g then
setmetatable(successor, nodeMT)
g[successor] = successor_g
openheap:insertvalue(successor)
parent[successor] = current
end
end
end
if current == nodeTo then
local path = {}
repeat
table.insert(path, 1, current)
current = parent[current]
until not current
return path
else
return false
end
end
function calculatePathByCoords(x1, y1, z1, x2, y2, z2)
return calculatePath(vehicleNodes, findNodeClosestToPoint(vehicleNodes, x1, y1, z1), findNodeClosestToPoint(vehicleNodes, x2, y2, z2))
end
function calculatePathByNodeIDs(node1, node2)
node1 = getNodeByID(vehicleNodes, node1)
node2 = getNodeByID(vehicleNodes, node2)
if node1 and node2 then
return calculatePath(vehicleNodes, node1, node2)
else
return false
end
end

128
linedrawer.lua Normal file
View File

@ -0,0 +1,128 @@
local ENABLE_FAILISH_ATTEMPT_AT_ANTI_ALIASING = false
local OVERLAY_WIDTH = 256
local OVERLAY_HEIGHT = 256
local OVERLAY_LINE_WIDTH = 5
local OVERLAY_LINE_COLOR = tocolor ( 0, 200, 0, 255 )
local OVERLAY_LINE_AA = tocolor ( 0, 200, 0, 200 )
local linePoints = { }
local renderStuff = { }
function removeLinePoints ( )
linePoints = { }
for name, data in pairs ( renderStuff ) do
unloadTile ( name )
end
end
function addLinePoint ( posX, posY )
-- Calculate the row and column of the radar tile we will be targeting
local row = 11 - math.floor ( ( posY + 3000 ) / 500 )
local col = math.floor ( ( posX + 3000 ) / 500 )
-- If it's off the map, don't bother
if row < 0 or row > 11 or col < 0 or col > 11 then
return false
end
-- Check the start position of the tile
local startX = col * 500 - 3000
local startY = 3000 - row * 500
-- Now get the tile position (We don't want to calculate this for every point on render)
local tileX = ( posX - startX ) / 500 * OVERLAY_WIDTH
local tileY = ( startY - posY ) / 500 * OVERLAY_HEIGHT
-- Now calulcate the ID and get the name of the tile
local id = col + row * 12
local name = string.format ( "radar%02d", id )
-- Make sure the line point table exists
if not linePoints [ name ] then
linePoints [ name ] = { }
end
-- Now add this point
table.insert ( linePoints[name], { posX = tileX, posY = tileY } )
-- Success!
return true
end
function loadTile ( name )
-- Create our fabulous shader. Abort on failure
local shader = dxCreateShader ( "overlay.fx" )
if not shader then
return false
end
-- Create a render target. Again, abort on failure (don't forget to delete the shader!)
local rt = dxCreateRenderTarget ( OVERLAY_WIDTH, OVERLAY_HEIGHT, true )
if not rt then
destroyElement ( shader )
return false
end
-- Mix 'n match
dxSetShaderValue ( shader, "gOverlay", rt )
-- Start drawing
dxSetRenderTarget ( rt )
-- Get the points involved, and get the starting position
local points = linePoints [ name ]
local prevX, prevY = points [ 1 ].posX, points [ 1 ] .posY
-- Loop through all points we have to draw, and draw them
for index, point in ipairs ( points ) do
local newX = point.posX
local newY = point.posY
if ENABLE_FAILISH_ATTEMPT_AT_ANTI_ALIASING then
dxDrawLine ( prevX - 1, prevY - 1, newX - 1, newY - 1, OVERLAY_LINE_AA, OVERLAY_LINE_WIDTH )
dxDrawLine ( prevX + 1, prevY - 1, newX + 1, newY - 1, OVERLAY_LINE_AA, OVERLAY_LINE_WIDTH )
dxDrawLine ( prevX - 1, prevY + 1, newX - 1, newY + 1, OVERLAY_LINE_AA, OVERLAY_LINE_WIDTH )
dxDrawLine ( prevX + 1, prevY + 1, newX + 1, newY + 1, OVERLAY_LINE_AA, OVERLAY_LINE_WIDTH )
end
dxDrawLine ( prevX, prevY, newX, newY, OVERLAY_LINE_COLOR, OVERLAY_LINE_WIDTH )
prevX = newX
prevY = newY
end
-- Now let's show our fabulous work to the commoners!
engineApplyShaderToWorldTexture ( shader, name )
-- Store the stuff in memories
renderStuff [ name ] = { shader = shader, rt = rt }
-- We won
return true
end
function unloadTile ( name )
destroyElement ( renderStuff[name].shader )
destroyElement ( renderStuff[name].rt )
renderStuff[name] = nil
return true
end
addEventHandler ( "onClientHUDRender", getRootElement ( ),
function ( )
local visibleTileNames = table.merge ( engineGetVisibleTextureNames ( "radar??" ), engineGetVisibleTextureNames ( "radar???" ) )
for name, data in pairs ( renderStuff ) do
if not table.find ( visibleTileNames, name ) then
unloadTile ( name )
end
end
for index, name in ipairs ( visibleTileNames ) do
if linePoints [ name ] and not renderStuff [ name ] then
loadTile ( name )
end
end
end
)

17
meta.xml Normal file
View File

@ -0,0 +1,17 @@
<meta>
<info author="arc_"/>
<!-- <script src="vehiclenodes_.lua" type="client"/> -->
<script src="util.lua" type="client"/>
<script src="client.lua" type="client"/>
<script src="linedrawer.lua" type="client"/>
<file src="overlay.fx"/>
<script src="vehiclenodes.lua" type="client"/>
<script src="MinHeap.lua" type="client"/>
<script src="gps.lua" type="client"/>
<script src="ai.server.lua" type="server"/>
<script src="ai.client.lua" type="client"/>
</meta>

20
overlay.fx Normal file
View File

@ -0,0 +1,20 @@
texture gOverlay;
technique TexOverlay
{
pass P0
{
// We don't do a damn thing here
}
pass P1
{
// Draw the overlay on top, if we have one
Texture[0] = gOverlay;
// Make sure we can use alpha. This shader wouldn't have much use without it
AlphaBlendEnable = TRUE;
SrcBlend = SRCALPHA;
DestBlend = INVSRCALPHA;
}
}

89
util.lua Normal file
View File

@ -0,0 +1,89 @@
local CRs = {}
local _resume = coroutine.resume
function coroutine.resume(cr, ...)
local ret = { _resume(cr, ...) }
if coroutine.status(cr) == 'dead' then
CRs[CRs[cr]] = nil
CRs[cr] = nil
end
if not ret[1] then
outputDebugString(ret[2], 1)
return false
end
table.remove(ret, 1)
return unpack(ret)
end
local serverMT = {}
function serverMT:__index(fnName)
return function(...)
triggerServerEvent('onServerCallback', getLocalPlayer(), CRs[coroutine.running()], fnName, ...)
return coroutine.yield()
end
end
server = setmetatable({}, serverMT)
addEvent('onServerCallbackReply', true)
addEventHandler('onServerCallbackReply', getResourceRootElement(getThisResource()),
function(crID, ...)
coroutine.resume(CRs[crID], ...)
end,
false
)
local function wrapHandler(fn)
return function(...)
local cr = coroutine.create(fn)
local id = #CRs + 1
CRs[id] = cr
CRs[cr] = id
coroutine.resume(cr, ...)
end
end
local _addEventHandler = addEventHandler
function addEventHandler(event, elem, fn, getPropagated)
return _addEventHandler(
event,
elem,
(event == 'onClientRender' or event == 'onClientPreRender') and fn or wrapHandler(fn),
getPropagated
)
end
local _addCommandHandler = addCommandHandler
function addCommandHandler(command, fn)
return _addCommandHandler(command, wrapHandler(fn))
end
function table.each(t, callback, ...)
for k,v in pairs(t) do
callback(v, ...)
end
return t
end
function table.merge ( ... )
local ret = { }
for index, tbl in ipairs ( {...} ) do
for index, val in ipairs ( tbl ) do
table.insert ( ret, val )
end
end
return ret
end
function table.find ( tbl, val )
for index, value in ipairs ( tbl ) do
if value == val then
return index
end
end
return false
end

30974
vehiclenodes.lua Normal file

File diff suppressed because it is too large Load Diff