diff --git a/init.lua b/init.lua index 533efb5..1c6163b 100644 --- a/init.lua +++ b/init.lua @@ -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 diff --git a/lib/anim/client/client.lua b/lib/anim/client/client.lua new file mode 100644 index 0000000..b809317 --- /dev/null +++ b/lib/anim/client/client.lua @@ -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 \ No newline at end of file diff --git a/lib/batch/shared/events.lua b/lib/batch/shared/events.lua new file mode 100644 index 0000000..f88fb86 --- /dev/null +++ b/lib/batch/shared/events.lua @@ -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 \ No newline at end of file diff --git a/lib/entities/client/client_entity.lua b/lib/entities/client/client_entity.lua index 59af37b..29c8e82 100644 --- a/lib/entities/client/client_entity.lua +++ b/lib/entities/client/client_entity.lua @@ -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 diff --git a/lib/entities/client/client_entity_actions.lua b/lib/entities/client/client_entity_actions.lua index b8ffd67..31b66b3 100644 --- a/lib/entities/client/client_entity_actions.lua +++ b/lib/entities/client/client_entity_actions.lua @@ -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 diff --git a/lib/entities/client/client_entity_actions_ext.lua b/lib/entities/client/client_entity_actions_ext.lua index 73dc7b5..8c03bad 100644 --- a/lib/entities/client/client_entity_actions_ext.lua +++ b/lib/entities/client/client_entity_actions_ext.lua @@ -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 diff --git a/lib/init.lua b/lib/init.lua index 3d3df81..b877f42 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -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 diff --git a/lib/markers/client/markers.lua b/lib/markers/client/markers.lua index c83dc60..fe42f79 100644 --- a/lib/markers/client/markers.lua +++ b/lib/markers/client/markers.lua @@ -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) diff --git a/lib/markers/server/server.lua b/lib/markers/server/server.lua index c009c7d..5a31e9f 100644 --- a/lib/markers/server/server.lua +++ b/lib/markers/server/server.lua @@ -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 diff --git a/lib/utility/shared/ids.lua b/lib/utility/shared/ids.lua index f0ae840..e7cdba8 100644 --- a/lib/utility/shared/ids.lua +++ b/lib/utility/shared/ids.lua @@ -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 diff --git a/modules/framework/_default/server.lua b/modules/framework/_default/server.lua new file mode 100644 index 0000000..4fff4d4 --- /dev/null +++ b/modules/framework/_default/server.lua @@ -0,0 +1,2 @@ +Framework = Framework or {} + diff --git a/modules/framework/es_extended/client.lua b/modules/framework/es_extended/client.lua index ea14754..99b070a 100644 --- a/modules/framework/es_extended/client.lua +++ b/modules/framework/es_extended/client.lua @@ -3,7 +3,6 @@ if GetResourceState('es_extended') ~= 'started' then return end ESX = exports["es_extended"]:getSharedObject() -Framework = {} Framework.GetFrameworkName = function() return 'es_extended' diff --git a/modules/framework/es_extended/server.lua b/modules/framework/es_extended/server.lua index 4a1cfea..cb85e2f 100644 --- a/modules/framework/es_extended/server.lua +++ b/modules/framework/es_extended/server.lua @@ -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) diff --git a/modules/framework/qb-core/client.lua b/modules/framework/qb-core/client.lua index f1ab2e9..9d70865 100644 --- a/modules/framework/qb-core/client.lua +++ b/modules/framework/qb-core/client.lua @@ -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 diff --git a/modules/framework/qb-core/server.lua b/modules/framework/qb-core/server.lua index fa9028f..d3824c8 100644 --- a/modules/framework/qb-core/server.lua +++ b/modules/framework/qb-core/server.lua @@ -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 diff --git a/modules/framework/qbx_core/client.lua b/modules/framework/qbx_core/client.lua index da1b25c..e1d0f51 100644 --- a/modules/framework/qbx_core/client.lua +++ b/modules/framework/qbx_core/client.lua @@ -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' diff --git a/modules/framework/qbx_core/server.lua b/modules/framework/qbx_core/server.lua index aa614a6..7c36db3 100644 --- a/modules/framework/qbx_core/server.lua +++ b/modules/framework/qbx_core/server.lua @@ -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