target got a print if onSelect or action are nil also added groups in fixoptions for sleepless client entities using return for loadmodel
249 lines
9.4 KiB
Lua
249 lines
9.4 KiB
Lua
Utility = Utility or Require("lib/utility/client/utility.lua")
|
|
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
|
Point = Point or Require("lib/points/client/points.lua")
|
|
ClientEntityActions = ClientEntityActions or Require("lib/entities/client/client_entity_actions_ext.lua") -- Added
|
|
|
|
local Entities = {} -- Stores entity data received from server
|
|
ClientEntity = {} -- Renamed from BaseEntity
|
|
|
|
local function SpawnEntity(entityData)
|
|
entityData = entityData and entityData.args
|
|
if entityData.spawned and DoesEntityExist(entityData.spawned) then return end -- Already spawned
|
|
-- for k, v in pairs(entityData.args) do
|
|
-- print(string.format("SpawnEntity %s: %s", k, v))
|
|
-- end
|
|
local loaded, model = Utility.GetModel(entityData.model)
|
|
if not loaded then
|
|
print(string.format("[ClientEntity] Failed to load model %s for entity %s", entityData.model, entityData.id))
|
|
return
|
|
end
|
|
|
|
local entity = nil
|
|
local coords = entityData.coords
|
|
local rotation = entityData.rotation or vector3(0.0, 0.0, entityData.heading or 0.0) -- Default rotation if not provided
|
|
|
|
if entityData.entityType == 'object' then
|
|
entity = CreateObject(model, coords.x, coords.y, coords.z, false, false, false)
|
|
SetEntityRotation(entity, rotation.x, rotation.y, rotation.z, 2, true)
|
|
elseif entityData.entityType == 'ped' then
|
|
entity = CreatePed(4, model, coords.x, coords.y, coords.z, type(rotation) == 'number' and rotation or rotation.z, false, false)
|
|
elseif entityData.entityType == 'vehicle' then
|
|
entity = CreateVehicle(model, coords.x, coords.y, coords.z, type(rotation) == 'number' and rotation or rotation.z, false, false)
|
|
else
|
|
print(string.format("[ClientEntity] Unknown entity type '%s' for entity %s", entityData.entityType, entityData.id))
|
|
end
|
|
if entity and model then
|
|
entityData.spawned = entity
|
|
SetModelAsNoLongerNeeded(model)
|
|
SetEntityAsMissionEntity(entity, true, true)
|
|
FreezeEntityPosition(entity, true)
|
|
else
|
|
SetModelAsNoLongerNeeded(model)
|
|
end
|
|
if entityData.OnSpawn then
|
|
entityData.OnSpawn(entityData)
|
|
end
|
|
end
|
|
|
|
local function RemoveEntity(entityData)
|
|
entityData = entityData and entityData.args or entityData
|
|
if not entityData then return end
|
|
ClientEntityActions.StopAction(entityData.id)
|
|
if entityData.spawned and DoesEntityExist(entityData.spawned) then
|
|
local entityHandle = entityData.spawned
|
|
entityData.spawned = nil
|
|
SetEntityAsMissionEntity(entityHandle, false, false)
|
|
DeleteEntity(entityHandle)
|
|
end
|
|
if entityData.OnRemove then
|
|
entityData.OnRemove(entityData)
|
|
end
|
|
end
|
|
|
|
--- Registers an entity received from the server and sets up proximity spawning.
|
|
-- @param entityData table Data received from the server via 'community_bridge:client:CreateEntity'
|
|
function ClientEntity.Create(entityData)
|
|
entityData.id = entityData.id or Ids.CreateUniqueId(Entities)
|
|
if Entities[entityData.id] then return Entities[entityData.id] end -- Already registered
|
|
Entities[entityData.id] = entityData
|
|
return Point.Register(entityData.id, entityData.coords, entityData.spawnDistance or 50.0, entityData, SpawnEntity, RemoveEntity, function() end)
|
|
end
|
|
|
|
--Depricated use ClientEntity.Create instead
|
|
--- Registers an entity and spawns it in the world if not already spawned.
|
|
ClientEntity.Register = ClientEntity.Create
|
|
|
|
|
|
function ClientEntity.CreateBulk(entities)
|
|
local registeredEntities = {}
|
|
for _, entityData in pairs(entities) do
|
|
local entity = ClientEntity.Create(entityData)
|
|
registeredEntities[entity.id] = entity
|
|
end
|
|
return registeredEntities
|
|
end
|
|
|
|
-- Depricated use ClientEntity.CreateBulk instead
|
|
ClientEntity.RegisterBulk = ClientEntity.CreateBulk
|
|
|
|
|
|
|
|
--- Unregisters an entity and removes it from the world if spawned.
|
|
-- @param id string|number The ID of the entity to unregister.
|
|
function ClientEntity.Destroy(id)
|
|
local entityData = Entities[id]
|
|
if not entityData then return end
|
|
|
|
Point.Remove(id)
|
|
RemoveEntity(entityData)
|
|
Entities[id] = nil
|
|
end
|
|
ClientEntity.Unregister = ClientEntity.Destroy
|
|
|
|
|
|
--- Updates the data for a registered entity.
|
|
-- @param id string|number The ID of the entity to update.
|
|
-- @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))
|
|
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
|
|
needsPointUpdate = true
|
|
end
|
|
if key == 'spawnDistance' and entityData.spawnDistance ~= value then
|
|
needsPointUpdate = true
|
|
end
|
|
entityData[key] = value
|
|
end
|
|
|
|
-- If entity is currently spawned, apply updates
|
|
if entityData.spawned and DoesEntityExist(entityData.spawned) then
|
|
if data.coords then
|
|
SetEntityCoords(entityData.spawned, entityData.coords.x, entityData.coords.y, entityData.coords.z, false, false, false, true)
|
|
end
|
|
if data.rotation then
|
|
if entityData.entityType == 'object' then
|
|
SetEntityRotation(entityData.spawned, entityData.rotation.x, entityData.rotation.y, entityData.rotation.z, 2, true)
|
|
else -- Ped/Vehicle heading
|
|
SetEntityHeading(entityData.spawned, type(entityData.rotation) == 'number' and entityData.rotation or entityData.rotation.z)
|
|
end
|
|
end
|
|
if data.freeze ~= nil then
|
|
FreezeEntityPosition(entityData.spawned, data.freeze)
|
|
end
|
|
-- Add other updatable properties as needed
|
|
end
|
|
|
|
-- Update Point registration if coords or distance changed
|
|
if needsPointUpdate then
|
|
Point.Remove(id)
|
|
Point.Register(
|
|
entityData.id,
|
|
entityData.coords,
|
|
entityData.spawnDistance or 50.0,
|
|
SpawnEntity,
|
|
RemoveEntity,
|
|
nil,
|
|
entityData
|
|
)
|
|
end
|
|
|
|
if entityData.OnUpdate and type(entityData.OnUpdate) == 'function' then
|
|
entityData.OnUpdate(entityData, data)
|
|
end
|
|
end
|
|
|
|
function ClientEntity.Get(id)
|
|
return Entities[id]
|
|
end
|
|
|
|
function ClientEntity.GetAll()
|
|
return Entities
|
|
end
|
|
|
|
function ClientEntity.RegisterAction(name, func)
|
|
ClientEntityActions.RegisterAction(name, func)
|
|
end
|
|
|
|
function ClientEntity.OnCreate(_type, func)
|
|
ClientEntity.OnCreates = ClientEntity.OnCreates or {}
|
|
if not ClientEntity.OnCreates[_type] then
|
|
ClientEntity.OnCreates[_type] = {}
|
|
end
|
|
table.insert(ClientEntity.OnCreates[_type], func)
|
|
end
|
|
|
|
-- Network Event Handlers
|
|
RegisterNetEvent("community_bridge:client:CreateEntity", function(entityData)
|
|
ClientEntity.Create(entityData)
|
|
end)
|
|
|
|
RegisterNetEvent("community_bridge:client:CreateEntities", function(entities)
|
|
ClientEntity.CreateBulk(entities)
|
|
end)
|
|
|
|
RegisterNetEvent("community_bridge:client:DeleteEntity", function(id)
|
|
ClientEntity.Unregister(id)
|
|
end)
|
|
|
|
RegisterNetEvent("community_bridge:client:UpdateEntity", function(id, data)
|
|
ClientEntity.Update(id, data)
|
|
end)
|
|
|
|
-- New handler for entity actions
|
|
RegisterNetEvent("community_bridge:client:TriggerEntityAction", function(entityId, actionName, ...)
|
|
local entityData = Entities[entityId]
|
|
-- Check if entity exists locally (it doesn't need to be spawned to queue actions)
|
|
if entityData then
|
|
if actionName == "Stop" then
|
|
ClientEntityActions.StopAction(entityId)
|
|
elseif actionName == "Skip" then
|
|
ClientEntityActions.SkipAction(entityId)
|
|
else
|
|
print(string.format("[ClientEntity] Triggering action '%s' for entity %s", actionName, entityId))
|
|
local currentAction = ClientEntityActions.ActionQueue[entityId] and ClientEntityActions.ActionQueue[entityId][1]
|
|
ClientEntityActions.QueueAction(entityData, actionName, ...)
|
|
end
|
|
-- else
|
|
-- Optional: Log if action received but entity doesn't exist locally at all
|
|
-- print(string.format("[ClientEntity] Received action '%s' for non-existent entity %s.", actionName, entityId))
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent("community_bridge:client:TriggerEntityActions", function(entityId, actions, endPosition)
|
|
local entityData = Entities[entityId]
|
|
if entityData then
|
|
for _, actionData in pairs(actions) do
|
|
local actionName = actionData.name
|
|
local actionParams = actionData.params
|
|
if actionName == "Stop" then
|
|
ClientEntityActions.StopAction(entityId)
|
|
elseif actionName == "Skip" then
|
|
ClientEntityActions.SkipAction(entityId)
|
|
else
|
|
local currentAction = ClientEntityActions.ActionQueue[entityId] and ClientEntityActions.ActionQueue[entityId][1]
|
|
ClientEntityActions.QueueAction(entityData, actionName, table.unpack(actionParams))
|
|
end
|
|
end
|
|
else
|
|
print(string.format("[ClientEntity] Received actions for non-existent entity %s.", entityId))
|
|
end
|
|
end)
|
|
|
|
-- Resource Stop Cleanup
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
if resourceName == GetCurrentResourceName() then
|
|
for id, entityData in pairs(Entities) do
|
|
Point.Remove(id) -- Clean up point registration
|
|
RemoveEntity(entityData) -- Clean up spawned game entity
|
|
end
|
|
Entities = {} -- Clear local cache
|
|
end
|
|
end)
|
|
|
|
return ClientEntity
|