New features and cleanup
Added Networked Markers Added Anim module Added framework default file for things that are native to game related to player Started on a batch network event trigger to allow for single event call for multiple events Updated params notation for ids Removed prints from client_entity + actions + ext
This commit is contained in:
1 parent
91d69a6742
commit
38d0d5527a
17 files changed
+463
-130
No files matched your search
@@ -8,7 +8,7 @@ function Bridge.RegisterModule(moduleName, moduleTable)
|
||||
return
|
||||
end
|
||||
|
||||
local wrappedModule = {}
|
||||
local wrappedModule = Bridge[moduleName] or {}
|
||||
if type(moduleTable) == 'function' then
|
||||
Bridge[moduleName] = wrappedModule
|
||||
exports(moduleName, moduleTable)
|
||||
@@ -57,22 +57,28 @@ Bridge.RegisterModule("Shops", Shops)
|
||||
Bridge.RegisterModule("Housing", Housing)
|
||||
|
||||
--lib
|
||||
Bridge.RegisterModule("Tables", cLib.Tables)
|
||||
Bridge.RegisterModule("Math", cLib.Math)
|
||||
Bridge.RegisterModule("Prints", cLib.Prints)
|
||||
Bridge.RegisterModule("Callback", cLib.Callback)
|
||||
-- Bridge.RegisterModule("Tables", cLib.Tables)
|
||||
-- Bridge.RegisterModule("Math", cLib.Math)
|
||||
-- Bridge.RegisterModule("Prints", cLib.Prints)
|
||||
-- Bridge.RegisterModule("Callback", cLib.Callback)
|
||||
|
||||
--new
|
||||
-- --new
|
||||
Bridge.RegisterModule("Require", Require)
|
||||
Bridge.RegisterModule("Ids", cLib.Ids)
|
||||
Bridge.RegisterModule("ReboundEntities", cLib.ReboundEntities)
|
||||
Bridge.RegisterModule("LA", cLib.LA)
|
||||
Bridge.RegisterModule("Perlin", cLib.Perlin)
|
||||
Bridge.RegisterModule("Actions", cLib.Actions)
|
||||
Bridge.RegisterModule("Cache", cLib.Cache)
|
||||
Bridge.RegisterModule("Skills", Skills)
|
||||
-- Bridge.RegisterModule("Ids", cLib.Ids)
|
||||
-- Bridge.RegisterModule("ReboundEntities", cLib.ReboundEntities)
|
||||
-- Bridge.RegisterModule("LA", cLib.LA)
|
||||
-- Bridge.RegisterModule("Perlin", cLib.Perlin)
|
||||
-- Bridge.RegisterModule("Actions", cLib.Actions)
|
||||
-- Bridge.RegisterModule("Cache", cLib.Cache)
|
||||
-- Bridge.RegisterModule("Skills", Skills)
|
||||
|
||||
|
||||
for k, v in pairs(cLib) do
|
||||
if v then
|
||||
Bridge.RegisterModule(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
exports('Bridge', function()
|
||||
return Bridge
|
||||
end)
|
||||
@@ -81,13 +87,6 @@ end)
|
||||
-- ▄█▀ █▄▄ █▀▄ ▀▄▀ █▄▄ █▀▄
|
||||
if not IsDuplicityVersion() then goto client end
|
||||
|
||||
Bridge.RegisterModule("SQL", cLib.SQL)
|
||||
Bridge.RegisterModule("Logs", cLib.Logs)
|
||||
Bridge.RegisterModule("ItemsBuilder", cLib.ItemsBuilder)
|
||||
Bridge.RegisterModule("LootTables", cLib.LootTables)
|
||||
Bridge.RegisterModule("Version", Version)
|
||||
Bridge.RegisterModule("ServerEntity", cLib.ServerEntity)
|
||||
|
||||
|
||||
-- ▄▀▀ █ █ ██▀ █▄ █ ▀█▀
|
||||
-- ▀▄▄ █▄▄ █ █▄▄ █ ▀█ █
|
||||
@@ -106,18 +105,6 @@ Bridge.RegisterModule("Menu", Menu)
|
||||
Bridge.RegisterModule("Dialogue", Dialogue)
|
||||
Bridge.RegisterModule("Accessibility", Accessibility) -- new
|
||||
|
||||
Bridge.RegisterModule("Utility", cLib.Utility)
|
||||
Bridge.RegisterModule("Placeable", cLib.Placeable)--new
|
||||
Bridge.RegisterModule("Gizmo", cLib.Gizmo)
|
||||
Bridge.RegisterModule("Scaleform", cLib.Scaleform)
|
||||
Bridge.RegisterModule("Raycast", cLib.Raycast)
|
||||
Bridge.RegisterModule("PlaceableObject", cLib.PlaceableObject)
|
||||
Bridge.RegisterModule("Point", cLib.Point)
|
||||
Bridge.RegisterModule("Object", cLib.Object)
|
||||
Bridge.RegisterModule("Particle", cLib.Particle)
|
||||
Bridge.RegisterModule("ClientEntity", cLib.ClientEntity)
|
||||
Bridge.RegisterModule("ClientEntityActions", cLib.ClientEntityActions)
|
||||
|
||||
-- Fill the bridge tables with player data.
|
||||
function FillBridgeTables()
|
||||
PlayerLoaded = true
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
Anim = Anim or {}
|
||||
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
||||
Anim.Active = Anim.Active or {}
|
||||
Anim.isUpdateLoopRunning = Anim.isUpdateLoopRunning or false
|
||||
|
||||
--- This will request the animation dictionary.
|
||||
--- @param animDict string
|
||||
--- @return boolean
|
||||
function Anim.RequestDict(animDict)
|
||||
if not animDict or type(animDict) ~= "string" then
|
||||
return false
|
||||
end
|
||||
|
||||
if HasAnimDictLoaded(animDict) then
|
||||
return true
|
||||
end
|
||||
|
||||
RequestAnimDict(animDict)
|
||||
local timeout = GetGameTimer() + 2000
|
||||
while not HasAnimDictLoaded(animDict) and GetGameTimer() < timeout do
|
||||
Wait(50)
|
||||
end
|
||||
|
||||
return HasAnimDictLoaded(animDict)
|
||||
end
|
||||
|
||||
--- This will start the animation update loop.
|
||||
--- @return void
|
||||
function Anim.Start()
|
||||
if Anim.isUpdateLoopRunning then return end
|
||||
Anim.isUpdateLoopRunning = true
|
||||
CreateThread(function()
|
||||
while Anim.isUpdateLoopRunning do
|
||||
local idsToProcess = {}
|
||||
for idKey, _ in pairs(Anim.Active) do
|
||||
table.insert(idsToProcess, idKey)
|
||||
end
|
||||
|
||||
if #idsToProcess == 0 then
|
||||
Wait(750)
|
||||
else
|
||||
for _, id in ipairs(idsToProcess) do
|
||||
local animData = Anim.Active[id]
|
||||
|
||||
if animData then
|
||||
local entity = animData.entity
|
||||
local onComplete = animData.onComplete
|
||||
|
||||
if not DoesEntityExist(entity) then
|
||||
if onComplete then onComplete(false, "despawned") end
|
||||
Anim.Active[id] = nil
|
||||
elseif animData.status == "pending_task" then
|
||||
TaskPlayAnim(entity, animData.animDict, animData.animName, animData.blendIn, animData.blendOut, animData.duration, animData.flag, animData.playbackRate, false, false, false)
|
||||
animData.startTime = GetGameTimer()
|
||||
animData.animEndTime = animData.duration > 0 and (animData.startTime + animData.duration) or -1
|
||||
animData.status = "playing"
|
||||
|
||||
elseif animData.status == "playing" then
|
||||
local animationCompletedNaturally = false
|
||||
|
||||
if animData.duration == -1 then
|
||||
if not IsEntityPlayingAnim(entity, animData.animDict, animData.animName, 3) and GetEntityAnimCurrentTime(entity, animData.animDict, animData.animName) > 0.8 then
|
||||
animationCompletedNaturally = true
|
||||
end
|
||||
elseif animData.animEndTime ~= -1 and GetGameTimer() >= animData.animEndTime then
|
||||
animationCompletedNaturally = true
|
||||
end
|
||||
|
||||
if animationCompletedNaturally then
|
||||
if onComplete then onComplete(true, "completed") end
|
||||
Anim.Active[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Wait(100)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- This will play an animation on the specified ped.
|
||||
--- @param id string | nil
|
||||
--- @param entity number
|
||||
--- @param animDict string
|
||||
--- @param animName string
|
||||
--- @param blendIn number | nil
|
||||
--- @param blendOut number | nil
|
||||
--- @param duration number | nil
|
||||
--- @param flag number | nil
|
||||
--- @param playbackRate number | nil
|
||||
--- @param onComplete function | nil
|
||||
--- @return string | nil
|
||||
function Anim.Play(id, entity, animDict, animName, blendIn, blendOut, duration, flag, playbackRate, onComplete)
|
||||
local newId = id or Ids.CreateUniqueId(Anim.Active)
|
||||
if Anim.Active[newId] then
|
||||
if onComplete then
|
||||
onComplete(false, "id_in_use")
|
||||
end
|
||||
return newId
|
||||
end
|
||||
|
||||
if not entity or not DoesEntityExist(entity) or not IsEntityAPed(entity) then
|
||||
if onComplete then
|
||||
onComplete(false, "invalid_entity")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
if not Anim.RequestDict(animDict) then
|
||||
if onComplete then
|
||||
onComplete(false, "dict_load_failed")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
Anim.Active[newId] = {
|
||||
entity = entity,
|
||||
animDict = animDict,
|
||||
animName = animName,
|
||||
blendIn = blendIn or 8.0,
|
||||
blendOut = blendOut or -8.0,
|
||||
duration = duration or -1,
|
||||
flag = flag or 1,
|
||||
playbackRate = playbackRate or 0.0,
|
||||
onComplete = onComplete,
|
||||
status = "pending_task",
|
||||
startTime = 0,
|
||||
animEndTime = 0
|
||||
}
|
||||
|
||||
Anim.Start()
|
||||
return newId
|
||||
end
|
||||
|
||||
function Anim.Stop(id)
|
||||
if not id or not Anim.Active or not Anim.Active[id] then
|
||||
return false
|
||||
end
|
||||
|
||||
local animData = Anim.Active[id]
|
||||
|
||||
if animData.entity and DoesEntityExist(animData.entity) and IsEntityAPed(animData.entity) then
|
||||
if animData.status == "playing" or animData.status == "pending_task" then
|
||||
StopAnimTask(animData.entity, animData.animDict, animData.animName, 1.0)
|
||||
end
|
||||
end
|
||||
|
||||
if animData.onComplete then
|
||||
animData.onComplete(false, "stopped_by_id")
|
||||
end
|
||||
|
||||
Anim.Active[id] = nil
|
||||
local anyLeft = Anim.Active and next(Anim.Active) ~= nil
|
||||
if not anyLeft then
|
||||
Anim.isUpdateLoopRunning = false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return Anim
|
||||
@@ -0,0 +1,65 @@
|
||||
Batch = Batch or {}
|
||||
Batch.Event = Batch.Event or {}
|
||||
Batch.Event.Queued = Batch.Event.Queued or {}
|
||||
Batch.Event.IsQueued = Batch.Event.IsQueued or false
|
||||
|
||||
local SERVER = IsDuplicityVersion()
|
||||
|
||||
-- could do a callback from client to server back to client with a time stamp. Use that timestamp to generate some random string/number and use that fo
|
||||
-- this event name. Would help by masking from exploits. Thinking about making a module out of it
|
||||
|
||||
--- This is used to batch single events together to reduce network strain
|
||||
---
|
||||
if SERVER then
|
||||
function Batch.Event.Queue(src, event, ...)
|
||||
if src == -1 then
|
||||
src = GetPlayers()
|
||||
for k, v in pairs(src) do
|
||||
local strSrc = tostring(v)
|
||||
Batch.Event.Queued[strSrc] = Batch.Event.Queued[strSrc] or {}
|
||||
table.insert(Batch.Event.Queued[strSrc], {
|
||||
src = v,
|
||||
event = event,
|
||||
args = {...}
|
||||
})
|
||||
end
|
||||
else
|
||||
local strSrc = tostring(src)
|
||||
Batch.Event.Queued[strSrc] = Batch.Event.Queued[strSrc] or {}
|
||||
table.insert(Batch.Event.Queued[strSrc], {
|
||||
src = src,
|
||||
event = event,
|
||||
args = {...}
|
||||
})
|
||||
end
|
||||
|
||||
if Batch.Event.IsQueued then return end
|
||||
Batch.Event.IsQueued = true
|
||||
SetTimeout(100, function()
|
||||
for k, v in pairs(Batch.Event.Queued) do
|
||||
TriggerClientEvent('community_bridge:client:BatchEvents', v.src, v)
|
||||
end
|
||||
Batch.Event.IsQueued = false
|
||||
Batch.Event.Queued = {}
|
||||
end)
|
||||
end
|
||||
return Batch
|
||||
else
|
||||
|
||||
Batch.Event.Fire = function(array)
|
||||
local playerSrc = PlayerId()
|
||||
for k, v in pairs(array) do
|
||||
if v.src == playerSrc then
|
||||
local event = v.event
|
||||
local args = v.args
|
||||
TriggerEvent(event, table.unpack(args))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RegisterNetEvent('community_bridge:client:BatchEvents', function(array)
|
||||
Batch.Event.Fire(array)
|
||||
end)
|
||||
|
||||
return Batch
|
||||
end
|
||||
@@ -35,10 +35,11 @@ local function SpawnEntity(entityData)
|
||||
print(string.format("[ClientEntity] Unknown entity type '%s' for entity %s", entityData.entityType, entityData.id))
|
||||
end
|
||||
if entity then
|
||||
-- print(string.format("[ClientEntity] Spawned %s entity %s (GameID: %s)", entityData.entityType, entityData.id, entity))
|
||||
entityData.spawned = entity
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
SetEntityAsMissionEntity(entity, true, true) -- Keep entity from being deleted by game engine
|
||||
FreezeEntityPosition(entity, entityData.freeze or false) -- Optional freeze based on meta
|
||||
FreezeEntityPosition(entity, true) -- Optional freeze based on meta
|
||||
if entityData.OnSpawn and type(entityData.OnSpawn) == 'function' then
|
||||
entityData.OnSpawn(entityData)
|
||||
end
|
||||
@@ -75,7 +76,7 @@ function ClientEntity.Register(entityData)
|
||||
-- print(string.format("[ClientEntity] Registering %s entity %s", entityData.entityType, entityData.id))
|
||||
|
||||
-- Use Point system for proximity checks
|
||||
print(string.format("[ClientEntity] Registering entity %s at %s", entityData.id, json.encode(entityData)))
|
||||
-- print(string.format("[ClientEntity] Registering entity %s at %s", entityData.id, json.encode(entityData)))
|
||||
Point.Register(entityData.id, entityData.coords, entityData.spawnDistance or 50.0, entityData, SpawnEntity, RemoveEntity, function() end)
|
||||
end
|
||||
|
||||
@@ -96,13 +97,13 @@ end
|
||||
-- @param data table The data fields to update.
|
||||
function ClientEntity.Update(id, data)
|
||||
local entityData = Entities[id]
|
||||
print(string.format("[ClientEntity] Updating entity %s", id))
|
||||
-- print(string.format("[ClientEntity] Updating entity %s", id))
|
||||
if not entityData then return end
|
||||
|
||||
local needsPointUpdate = false
|
||||
for key, value in pairs(data) do
|
||||
if key == 'coords' and #(entityData.coords - value) > 0.1 then
|
||||
print(string.format("[ClientEntity] Updating coords for entity %s", id))
|
||||
-- print(string.format("[ClientEntity] Updating coords for entity %s", id))
|
||||
needsPointUpdate = true
|
||||
end
|
||||
if key == 'spawnDistance' and entityData.spawnDistance ~= value then
|
||||
@@ -176,7 +177,6 @@ end)
|
||||
-- New handler for entity actions
|
||||
RegisterNetEvent("community_bridge:client:TriggerEntityAction", function(entityId, actionName, ...)
|
||||
local entityData = Entities[entityId]
|
||||
print("here", json.encode(entityData))
|
||||
-- Check if entity exists locally (it doesn't need to be spawned to queue actions)
|
||||
if entityData then
|
||||
if actionName == "Stop" then
|
||||
|
||||
@@ -12,7 +12,6 @@ ClientEntityActions.RegisteredActions = {} -- New: Registry for action implemen
|
||||
-- @param entityId string|number The ID of the entity.
|
||||
function ClientEntityActions.ProcessNextAction(entityId)
|
||||
if ClientEntityActions.IsActionRunning [entityId] then return end -- Already running something
|
||||
print(string.format("[ClientEntityActions] Processing next action for entity %s", entityId))
|
||||
local queue = ClientEntityActions.ActionQueue[entityId]
|
||||
if not queue or #queue == 0 then return end -- Queue is empty
|
||||
|
||||
@@ -27,7 +26,6 @@ function ClientEntityActions.ProcessNextAction(entityId)
|
||||
|
||||
-- Look up the action in the registry
|
||||
local actionFunc = ClientEntityActions.RegisteredActions [nextAction.name]
|
||||
print(string.format("[ClientEntityActions] Action '%s' dequeued for entity %s", nextAction.name, entityId))
|
||||
if actionFunc then
|
||||
-- print(string.format("[ClientEntityActions] Starting action '%s' for entity %s", nextAction.name, entityId))
|
||||
ClientEntityActions.IsActionRunning [entityId] = true
|
||||
|
||||
@@ -4,7 +4,6 @@ LA = LA or Require("lib/utility/shared/la.lua")
|
||||
|
||||
--- Internal implementation for walking. Registered via RegisterAction.
|
||||
function DefaultActions.WalkTo(entityData, coords, speed, timeout)
|
||||
print("[ClientEntityActions] WalkTo action called", coords, speed, timeout)
|
||||
local entity = entityData.spawned
|
||||
local entityId = entityData.id -- Store ID locally for safety in thread
|
||||
|
||||
@@ -179,9 +178,7 @@ function DefaultActions.AttachProp(entityData, propModel, boneName, offsetPos, o
|
||||
end
|
||||
|
||||
local modelHash = Utility.GetEntityHashFromModel(propModel)
|
||||
print(modelHash, propModel)
|
||||
if not Utility.LoadModel(modelHash) then
|
||||
print(string.format("[ClientEntityActions] Failed to load prop model '%s' for entity %s", propModel, entityId))
|
||||
ClientEntityActions.IsActionRunning[entityId] = false
|
||||
ClientEntityActions.ProcessNextAction(entityId)
|
||||
return
|
||||
@@ -196,7 +193,6 @@ function DefaultActions.AttachProp(entityData, propModel, boneName, offsetPos, o
|
||||
if boneIndex == -1 then boneIndex = 0 end -- Default to root if bone not found or not ped
|
||||
offsetPos = offsetPos or vector3(0.0, 0.0, 0.0)
|
||||
offsetRot = offsetRot or vector3(0.0, 0.0, 0.0)
|
||||
print(string.format("[ClientEntityActions] Attaching prop '%s' to entity %s at bone %d", propModel, entityId, boneIndex))
|
||||
AttachEntityToEntity(prop, entity, boneIndex, offsetPos.x, offsetPos.y, offsetPos.z, offsetRot.x, offsetRot.y, offsetRot.z, false, useSoftPinning or false, collision or false, isPed or false, vertexIndex or 2, fixedRot == nil and true or fixedRot)
|
||||
entityData.props = entityData.props or {} -- Ensure props table exists
|
||||
table.insert(entityData.props, prop) -- Store the prop handle in the entity data
|
||||
@@ -351,6 +347,11 @@ function DefaultActions.Circle(entityData, radius, speed)
|
||||
-- ClientEntityActions.ProcessNextAction(entityId) -- Try next action if this one failed immediately
|
||||
end
|
||||
|
||||
function DefaultActions.Collisions(entityData, enable, keepPhysics)
|
||||
local entity = entityData.spawned
|
||||
SetEntityCollision(entity, enable, keepPhysics)
|
||||
end
|
||||
|
||||
for name, func in pairs(DefaultActions) do
|
||||
ClientEntityActions.RegisterAction(name, func)
|
||||
end
|
||||
|
||||
+4
-1
@@ -58,7 +58,7 @@ cLib.ItemsBuilder = ItemsBuilder or Require("lib/generators/server/ItemsBuilder.
|
||||
cLib.LootTables = LootTables or Require("lib/generators/server/lootTables.lua")
|
||||
cLib.Cache = Cache or Require("lib/cache/shared/cache.lua")
|
||||
cLib.ServerEntity = ServerEntity or Require("lib/entities/server/server_entity.lua")
|
||||
|
||||
cLib.Marker = Marker or Require("lib/markers/server/server.lua")
|
||||
if IsDuplicityVersion() then return cLib end
|
||||
::client::
|
||||
|
||||
@@ -74,4 +74,7 @@ cLib.Cache = Cache or Require("lib/cache/client/cache.lua")
|
||||
cLib.ClientEntity = ClientEntity or Require("lib/entities/client/client_entity.lua")
|
||||
cLib.ClientEntityActions = ClientEntityActions or Require("lib/entities/client/client_entity_actions.lua")
|
||||
cLib.ClientStateBag = ClientStateBag or Require("lib/statebags/client/client.lua")
|
||||
cLib.Marker = Marker or Require("lib/markers/client/markers.lua")
|
||||
cLib.Anim = Anim or Require("lib/anim/client/client.lua")
|
||||
|
||||
return cLib
|
||||
+134
-73
@@ -1,8 +1,10 @@
|
||||
Marker = {
|
||||
|
||||
}
|
||||
local Created = setmetatable({}, { __mode = "k" })
|
||||
local id = Require("lib/utility/shared/ids")
|
||||
---@diagnostic disable: duplicate-set-field
|
||||
Marker = {}
|
||||
local Created = setmetatable({}, { __mode = "k" }) --what is mode?
|
||||
local id = Ids or Require("lib/utility/shared/ids.lua")
|
||||
local point = Require("lib/points/client/points.lua")
|
||||
local loopRunning = false
|
||||
local Drawing = {}
|
||||
|
||||
--- This will validate the marker data.
|
||||
--- @param data table{position: vector3, offset: vector3, rotation: vector3, size: vector3, color: vector3, alpha: number, bobUpAndDown: boolean, marker: number, interaction: function}
|
||||
@@ -23,91 +25,150 @@ local function validateMarkerData(data)
|
||||
return true
|
||||
end
|
||||
|
||||
AddEventHandler("community_bridge:client:marker", function(data)
|
||||
if not validateMarkerData(data) then return end
|
||||
if source == "" then return end
|
||||
Marker.DrawMarker(data)
|
||||
end)
|
||||
|
||||
--- This will create a marker.
|
||||
--- @param data table{position: vector3, offset: vector3, rotation: vector3, size: vector3, color: vector3, alpha: number, bobUpAndDown: boolean, marker: number, interaction: function}
|
||||
---@return number
|
||||
function Marker.DrawMarker(data)
|
||||
--- @return string|nil
|
||||
function Marker.Create(data)
|
||||
local _id = data.id or id.Random()
|
||||
local position = data.position or vector3(0.0, 0.0, 0.0)
|
||||
local offset = data.offset or vector3(0.0, 0.0, 0.0)
|
||||
local marker = data.marker or 1
|
||||
local pos = vector3(position.x + offset.x, position.y + offset.y, position.z + offset.z)
|
||||
local rotation = data.rotation or vector3(0, 0, 0)
|
||||
local size = data.size or vector3(1.0, 1.0, 1.0)
|
||||
local color = data.color or vector3(0, 255, 0)
|
||||
local alpha = data.alpha or 255
|
||||
local bobUpAndDown = data.bobUpAndDown
|
||||
local interaction = data.interaction or false
|
||||
Created[_id] = {
|
||||
position = pos,
|
||||
rotation = rotation,
|
||||
size = size,
|
||||
color = color,
|
||||
alpha = alpha,
|
||||
bobUpAndDown =
|
||||
bobUpAndDown,
|
||||
marker = marker,
|
||||
offset = offset,
|
||||
interaction = interaction,
|
||||
if not validateMarkerData(data) then return end
|
||||
local basePosition = data.position or vector3(0.0, 0.0, 0.0)
|
||||
local baseOffset = data.offset or vector3(0.0, 0.0, 0.0)
|
||||
|
||||
local data = {
|
||||
id = _id,
|
||||
position = vector3(basePosition.x + baseOffset.x, basePosition.y + baseOffset.y, basePosition.z + baseOffset.z),
|
||||
offset = baseOffset,
|
||||
marker = data.marker or 1,
|
||||
rotation = data.rotation or vector3(0, 0, 0),
|
||||
size = data.size or vector3(1.0, 1.0, 1.0),
|
||||
color = data.color or vector3(0, 255, 0),
|
||||
alpha = data.alpha or 255,
|
||||
bobUpAndDown = data.bobUpAndDown,
|
||||
-- interaction = data.interaction or false, -- Uncomment if you re-implement interaction logic
|
||||
rotate = data.rotate,
|
||||
textureDict = data.textureDict,
|
||||
textureName = data.textureName,
|
||||
drawOnEnts = data.drawOnEnts,
|
||||
}
|
||||
Created[_id] = data
|
||||
|
||||
point.Register(
|
||||
_id, data.position, data.drawDistance or 50.0, data,
|
||||
function(_)
|
||||
if not Created[_id] then return point.Remove(_id) end
|
||||
Drawing[_id] = Created[_id]
|
||||
Marker.Run(Drawing[_id])
|
||||
end,
|
||||
function(markerData)
|
||||
Drawing[_id] = nil
|
||||
end
|
||||
)
|
||||
return _id
|
||||
end
|
||||
|
||||
function Marker.RemoveMarker(id)
|
||||
if Created[id] then
|
||||
--- This will remove a marker.
|
||||
--- @param id string
|
||||
--- @return boolean
|
||||
function Marker.Remove(id)
|
||||
if not Created[id] then return false end
|
||||
point.Remove(id)
|
||||
Drawing[id] = nil
|
||||
Created[id] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
--- This will remove all markers.
|
||||
--- @param id string
|
||||
function Marker.RemoveAll()
|
||||
for id, _ in pairs(Created) do
|
||||
point.Remove(id)
|
||||
Created[id] = nil
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function Marker.Draw()
|
||||
while true do
|
||||
Wait(0)
|
||||
if not next(Created) then
|
||||
Wait(1000) -- Reduce CPU usage when no markers are present
|
||||
goto continue
|
||||
end
|
||||
|
||||
for id, _ in pairs(Created) do
|
||||
if Created[id] then
|
||||
local data = Created[id]
|
||||
if #(data.position - GetEntityCoords(PlayerPedId())) > 100.0 then
|
||||
-- This will loop through all marker(points) in range and draw them.
|
||||
-- @param data table{marker: number, position: vector3, rotation: vector3, size: vector3, color: vector3, alpha: number, bobUpAndDown: boolean, faceCamera: boolean, rotate: boolean, textureDict: string, textureName: string, drawOnEnts: boolean}
|
||||
-- @return nil
|
||||
function Marker.Run(data)
|
||||
if loopRunning then return end
|
||||
loopRunning = true
|
||||
CreateThread(function()
|
||||
while loopRunning do
|
||||
for drawingId, drawingData in pairs(Drawing) do
|
||||
if not Created[drawingId] then
|
||||
Drawing[drawingId] = nil
|
||||
goto continue
|
||||
else
|
||||
DrawMarker(
|
||||
data.marker,
|
||||
data.position.x, data.position.y, data.position.z,
|
||||
0.0, 0.0, 0.0,
|
||||
data.rotation.x, data.rotation.y, data.rotation.z,
|
||||
data.size.x, data.size.y, data.size.z,
|
||||
data.color.x, data.color.y, data.color.z, data.alpha,
|
||||
data.bobUpAndDown, false, false
|
||||
)
|
||||
if data.interaction then
|
||||
local playerCoords = GetEntityCoords(PlayerPedId())
|
||||
local distance = #(data.position - playerCoords)
|
||||
if distance < 2.0 then
|
||||
if IsControlJustReleased(0, 38) then -- E key
|
||||
data.interaction(data.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
DrawMarker(
|
||||
data.marker,
|
||||
data.position.x, data.position.y, data.position.z,
|
||||
0.0, 0.0, 0.0,
|
||||
data.rotation.x, data.rotation.y, data.rotation.z,
|
||||
data.size.x, data.size.y, data.size.z,
|
||||
data.color.x, data.color.y, data.color.z, data.alpha,
|
||||
data.bobUpAndDown, data.faceCamera, false,
|
||||
data.rotate, data.textureDict, data.textureName, data.drawOnEnts
|
||||
)
|
||||
::continue::
|
||||
end
|
||||
Wait(3)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
Marker.Draw() -- Start the marker drawing loop
|
||||
RegisterNetEvent("community_bridge:Client:Marker", function(data)
|
||||
print("community_bridge:Client:Marker")
|
||||
Marker.Create(data)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("community_bridge:Client:MarkerRemove", function(id)
|
||||
Marker.Remove(id)
|
||||
end)
|
||||
|
||||
-- local loopRunning = false
|
||||
-- function Marker.Draw()
|
||||
-- if loopRunning then return end
|
||||
-- loopRunning = true
|
||||
-- while loopRunning do
|
||||
-- Wait(0)
|
||||
-- if not next(Created) then
|
||||
-- Wait(1000) -- Reduce CPU usage when no markers are present
|
||||
-- goto continue
|
||||
-- end
|
||||
|
||||
-- for id, _ in pairs(Created) do
|
||||
-- if Created[id] then
|
||||
-- local data = Created[id]
|
||||
-- if #(data.position - GetEntityCoords(PlayerPedId())) > 100.0 then
|
||||
-- goto continue
|
||||
-- else
|
||||
-- DrawMarker(
|
||||
-- data.marker,
|
||||
-- data.position.x, data.position.y, data.position.z,
|
||||
-- 0.0, 0.0, 0.0,
|
||||
-- data.rotation.x, data.rotation.y, data.rotation.z,
|
||||
-- data.size.x, data.size.y, data.size.z,
|
||||
-- data.color.x, data.color.y, data.color.z, data.alpha,
|
||||
-- data.bobUpAndDown, data.faceCamera, false,
|
||||
-- data.rotate, data.textureDict, data.textureName, data.drawOnEnts
|
||||
-- )
|
||||
-- -- if data.interaction then
|
||||
-- -- local playerCoords = GetEntityCoords(PlayerPedId())
|
||||
-- -- local distance = #(data.position - playerCoords)
|
||||
-- -- if distance < 2.0 then
|
||||
-- -- if IsControlJustReleased(0, 38) then -- E key
|
||||
-- -- data.interaction(data.id)
|
||||
-- -- end
|
||||
-- -- end
|
||||
-- -- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- ::continue::
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Marker.Draw() -- Start the marker drawing loop
|
||||
exports("Marker", Marker)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
local function RegisterNewMarker(data)
|
||||
---@diagnostic disable: duplicate-set-field
|
||||
local id = Ids or Require("lib/utility/shared/ids.lua")
|
||||
Marker = {}
|
||||
local Created = {}
|
||||
|
||||
function Marker.Create(data)
|
||||
if not data.position or not data.position.x or not data.position.y or not data.position.z then
|
||||
print("Invalid marker position. Must be a vector3 with x, y, and z coordinates.")
|
||||
return
|
||||
@@ -17,8 +22,37 @@ local function RegisterNewMarker(data)
|
||||
return
|
||||
end
|
||||
|
||||
TriggerClientEvent("community_bridge:client:marker", source, data)
|
||||
local _id = id.CreateUniqueId(Created)
|
||||
local data = {
|
||||
id = _id,
|
||||
position = data.position,
|
||||
offset = data.offset or vector3(0.0, 0.0, 0.0),
|
||||
marker = data.marker or 1,
|
||||
rotation = data.rotation or vector3(0, 0, 0),
|
||||
size = data.size or vector3(1.0, 1.0, 1.0),
|
||||
color = data.color or vector3(0, 255, 0),
|
||||
alpha = data.alpha or 255,
|
||||
bobUpAndDown = data.bobUpAndDown,
|
||||
-- interaction = data.interaction or false, -- Uncomment if you re-implement interaction logic
|
||||
rotate = data.rotate,
|
||||
textureDict = data.textureDict,
|
||||
textureName = data.textureName,
|
||||
drawOnEnts = data.drawOnEnts,
|
||||
}
|
||||
Created[_id] = data
|
||||
TriggerClientEvent("community_bridge:Client:Marker", -1, data)
|
||||
return _id
|
||||
end
|
||||
|
||||
RegisterNetEvent("community_bridge:server:createMarker", RegisterNewMarker)
|
||||
exports("RegisterNewMarker", RegisterNewMarker)
|
||||
function Marker.Remove(id)
|
||||
if not Created[id] then return end
|
||||
TriggerClientEvent("community_bridge:Client:MarkerRemove", -1, id)
|
||||
Created[id] = nil
|
||||
end
|
||||
|
||||
RegisterNetEvent("community_bridge:Server:MarkerRemove", function(id)
|
||||
Marker.Remove(id)
|
||||
end)
|
||||
|
||||
exports("Marker", Marker)
|
||||
return Marker
|
||||
@@ -2,9 +2,9 @@ Ids = Ids or {}
|
||||
|
||||
|
||||
---This will generate a unique id.
|
||||
---@param tbl table
|
||||
---@param len number
|
||||
---@param pattern string
|
||||
---@param tbl table | nil
|
||||
---@param len number | nil
|
||||
---@param pattern string | nil
|
||||
---@return string
|
||||
Ids.CreateUniqueId = function(tbl, len, pattern) -- both optional
|
||||
tbl = tbl or {} -- table to check uniqueness. Ids to check against must be the key to the tables value
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Framework = Framework or {}
|
||||
|
||||
@@ -3,7 +3,6 @@ if GetResourceState('es_extended') ~= 'started' then return end
|
||||
|
||||
ESX = exports["es_extended"]:getSharedObject()
|
||||
|
||||
Framework = {}
|
||||
|
||||
Framework.GetFrameworkName = function()
|
||||
return 'es_extended'
|
||||
|
||||
@@ -3,7 +3,7 @@ if GetResourceState('es_extended') ~= 'started' then return end
|
||||
|
||||
ESX = exports["es_extended"]:getSharedObject()
|
||||
|
||||
Framework = {}
|
||||
Framework = Framework or {}
|
||||
|
||||
Framework.GetFrameworkName = function()
|
||||
return 'es_extended'
|
||||
@@ -140,6 +140,17 @@ Framework.GetIsPlayerDead = function(src)
|
||||
return xPlayer.get("is_dead") or false
|
||||
end
|
||||
|
||||
Framework.RevivePlayer = function(src)
|
||||
src = tonumber(src)
|
||||
|
||||
if not src then return false end
|
||||
TriggerEvent('esx_ambulancejob:revive', src) -- FIXME: deprecate
|
||||
-- TriggerEvent('txAdmin:events:playerHealed', {
|
||||
-- target = src,
|
||||
-- })
|
||||
return true
|
||||
end
|
||||
|
||||
-- Framework.AddThirst(src, value)
|
||||
-- Adds the specified value from the player's thirst level.
|
||||
Framework.AddThirst = function(src, value)
|
||||
|
||||
@@ -4,7 +4,7 @@ if GetResourceState('qbx_core') == 'started' then return end
|
||||
|
||||
QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
Framework = {}
|
||||
Framework = Framework or {}
|
||||
|
||||
---This will get the name of the framework being used (if a supported framework).
|
||||
---@return string
|
||||
|
||||
@@ -4,7 +4,7 @@ if GetResourceState('qbx_core') == 'started' then return end
|
||||
|
||||
QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
Framework = {}
|
||||
Framework = Framework or {}
|
||||
Framework.Shared = QBCore.Shared
|
||||
Framework.GetFrameworkName = function()
|
||||
return 'qb-core'
|
||||
@@ -265,6 +265,17 @@ Framework.GetIsPlayerDead = function(src)
|
||||
return playerData.metadata.isdead or playerData.metadata.inlaststand or false
|
||||
end
|
||||
|
||||
Framework.RevivePlayer = function(src)
|
||||
src = tonumber(src)
|
||||
|
||||
if not src then return false end
|
||||
TriggerClientEvent('hospital:client:Revive', src) -- FIXME: deprecate
|
||||
-- TriggerEvent('txAdmin:events:playerHealed', {
|
||||
-- target = src,
|
||||
-- })
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
---This will get the thirst of a player
|
||||
---@param src any
|
||||
|
||||
@@ -3,7 +3,7 @@ if GetResourceState('qbx_core') ~= 'started' then return end
|
||||
|
||||
QBox = exports.qbx_core
|
||||
|
||||
Framework = {}
|
||||
Framework = Framework or {}
|
||||
|
||||
Framework.GetFrameworkName = function()
|
||||
return 'qbx_core'
|
||||
|
||||
@@ -3,7 +3,7 @@ if GetResourceState('qbx_core') ~= 'started' then return end
|
||||
|
||||
local QBox = exports.qbx_core
|
||||
|
||||
Framework = {}
|
||||
Framework = Framework or {}
|
||||
|
||||
---Returns the name of the framework being used (if a supported framework).
|
||||
---@return string
|
||||
|
||||
Reference in new issue
Block a user