Merge pull request #176 from TheOrderFivem/behavior-replacement
Stripped away inefficient behavior system
This commit is contained in:
13 files changed
+265
-955
No files matched your search
@@ -1,18 +1,80 @@
|
||||
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")
|
||||
Behaviors = Behaviors or Require("lib/entities/shared/behaviors.lua")
|
||||
local Entities = {} -- Stores entity data received from server
|
||||
ClientEntity = {
|
||||
Invoked = {}
|
||||
} -- Renamed from BaseEntity
|
||||
ClientEntity.Behaviors = Behaviors
|
||||
All = {},
|
||||
Invoked = {},
|
||||
OnCreates = {}
|
||||
}
|
||||
----------------------------------------
|
||||
|
||||
--------------------
|
||||
---@alias ClientEntityId string|number
|
||||
---@alias EntityHandle number
|
||||
|
||||
---@class ClientEntityAnim
|
||||
---@field dict string
|
||||
---@field name string
|
||||
---@field blendIn number|nil
|
||||
---@field blendOut number|nil
|
||||
---@field duration number|nil
|
||||
---@field flags number|nil
|
||||
---@field playbackRate number|nil
|
||||
---@field onComplete function|nil
|
||||
|
||||
---@class ClientEntityAttach
|
||||
---@field target string|number Target reference. String may be a Bridge.Entity id; number may be a player server id; either may also be a FiveM entity handle.
|
||||
---@field bone number|nil
|
||||
---@field offset vector3|nil
|
||||
---@field rotation vector3|nil
|
||||
|
||||
---@class EntityData
|
||||
---@field id ClientEntityId|nil If omitted, one is generated.
|
||||
---@field coords vector3
|
||||
---@field entityType 'object'|'ped'|'vehicle'
|
||||
---@field model string|number
|
||||
---@field heading number|nil
|
||||
---@field rotation vector3|nil
|
||||
---@field spawnDistance number|nil
|
||||
---@field freeze boolean|nil
|
||||
---@field invincible boolean|nil
|
||||
---@field targets table|nil Target options passed to Bridge.Target.AddLocalEntity.
|
||||
---@field anim ClientEntityAnim|nil
|
||||
---@field attach ClientEntityAttach|nil
|
||||
---@field OnSpawn fun(entityData: EntityData)|nil
|
||||
---@field OnRemove fun(entityData: EntityData)|nil
|
||||
---@field spawned EntityHandle|nil FiveM entity handle created when the entry is in-range.
|
||||
|
||||
--- Set the entity and apply any relevant properties such as freezing and targets
|
||||
--- @param id The entities id
|
||||
--- @return nil
|
||||
local function SetEntityProperties(id)
|
||||
local entityData = ClientEntity.Get(id)
|
||||
if not entityData then return end
|
||||
local entity = entityData.spawned
|
||||
if not entity or not DoesEntityExist(entity) then return end
|
||||
|
||||
if entityData.freeze == nil or entityData.freeze then
|
||||
FreezeEntityPosition(entity, true)
|
||||
end
|
||||
if entityData.invincible then
|
||||
SetEntityInvincible(entity, entityData.invincible)
|
||||
end
|
||||
if entityData.targets then
|
||||
ClientEntity.SetTargets(id, entityData.targets)
|
||||
end
|
||||
if entityData.anim then
|
||||
ClientEntity.SetAnim(id, entityData.anim)
|
||||
end
|
||||
if entityData.attach then
|
||||
ClientEntity.SetAttach(id, entityData.attach)
|
||||
end
|
||||
end
|
||||
|
||||
--- Spawns an entity
|
||||
--- @param entityData EntityData
|
||||
local function SpawnEntity(entityData)
|
||||
if entityData.spawned and DoesEntityExist(entityData.spawned) then return end -- Already spawned
|
||||
if entityData.model then
|
||||
if entityData.model then
|
||||
local loaded, model = Utility.LoadModel(entityData.model)
|
||||
if not loaded then
|
||||
print(string.format("[ClientEntity] Failed to load model %s for entity %s", entityData.model, entityData.id))
|
||||
@@ -33,27 +95,21 @@ local function SpawnEntity(entityData)
|
||||
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
|
||||
entityData.spawned = entity
|
||||
SetEntityProperties(entityData.id)
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
end
|
||||
if entityData.OnSpawn then
|
||||
pcall(function (...)
|
||||
return entityData.OnSpawn(entityData)
|
||||
end)
|
||||
end
|
||||
Behaviors.Trigger("OnSpawn", entityData)
|
||||
if not entityData.OnSpawn then return end
|
||||
pcall(function (...)
|
||||
return entityData.OnSpawn(entityData)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Removes an entity
|
||||
--- @param entityData EntityData
|
||||
local function RemoveEntity(entityData)
|
||||
entityData = entityData and entityData.args or entityData
|
||||
if not entityData then return end
|
||||
Behaviors.Trigger("OnRemove", entityData)
|
||||
if entityData.OnRemove then
|
||||
pcall(function (...)
|
||||
return entityData.OnRemove(entityData)
|
||||
@@ -67,70 +123,53 @@ local function RemoveEntity(entityData)
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateEntity(_entityData)
|
||||
local entityData = ClientEntity.Get(_entityData.id)
|
||||
if not entityData then return end
|
||||
if entityData.OnUpdate then
|
||||
pcall(function (...)
|
||||
return entityData.OnUpdate(entityData)
|
||||
end)
|
||||
end
|
||||
Behaviors.Trigger("OnUpdate", entityData)
|
||||
local coords = vector3(entityData.coords.x, entityData.coords.y, entityData.coords.z)
|
||||
entityData.oldCoords = entityData.oldCoords and vector3(entityData.oldCoords.x, entityData.oldCoords.y, entityData.oldCoords.z) or coords
|
||||
local dist = #(coords - entityData.oldCoords)
|
||||
if entityData.oldCoords and dist > 0.5 then
|
||||
-- if entityData.spawned then
|
||||
-- SetEntityCoords(entityData.spawned, coords.x, coords.y, coords.z, false, false, false, false)
|
||||
-- end
|
||||
-- ClientEntity.UpdateCoords(entityData.id, entityData.coords)
|
||||
if entityData.OnMove then
|
||||
pcall(function (...)
|
||||
return entityData.OnMove(entityData)
|
||||
end)
|
||||
end
|
||||
Behaviors.Trigger("OnMove", entityData, entityData.oldCoords, entityData.coords)
|
||||
end
|
||||
--- Registers a ClientEntity definition and a proximity trigger that spawns/despawns it.
|
||||
---
|
||||
--- Note: When in-range, the spawned FiveM entity handle is stored on `entityData.spawned`.
|
||||
--- @param entityData EntityData
|
||||
--- @return table Registered point/entity entry (the value stored in ClientEntity.All[entityData.id]).
|
||||
function ClientEntity.Create(entityData)
|
||||
entityData.id = entityData.id or Ids.CreateUniqueId(ClientEntity.All)
|
||||
if ClientEntity.All[entityData.id] then return ClientEntity.All[entityData.id] end
|
||||
entityData.rotation = entityData.rotation or vector3(0.0, 0.0, entityData.heading or 0.0)
|
||||
entityData.invoked = entityData.invoked or GetInvokingResource() or "community_bridge"
|
||||
|
||||
if not entityData.oldRotation or entityData.oldRotation ~= entityData.rotation then
|
||||
if entityData.spawned and DoesEntityExist(entityData.spawned) 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)
|
||||
for k, v in pairs(ClientEntity.OnCreates) do
|
||||
if entityData[k] ~= nil then
|
||||
local success, results = pcall(function()
|
||||
return v(entityData)
|
||||
end)
|
||||
if success then
|
||||
entityData = results or entityData
|
||||
end
|
||||
end
|
||||
entityData.oldRotation = entityData.rotation
|
||||
end
|
||||
|
||||
if entityData.freeze ~= nil and entityData.spawned and IsEntityPositionFrozen(entityData.spawned) then
|
||||
FreezeEntityPosition(entityData.spawned, entityData.freeze)
|
||||
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
|
||||
entityData.rotation = entityData.rotation or vector3(0.0, 0.0, entityData.heading or 0.0)
|
||||
entityData.oldCoords = entityData.oldCoords or vector3(0.0, 0.0, 0.0)
|
||||
entityData.oldRotation = entityData.rotation
|
||||
entityData.invoked = entityData.invoked or GetInvokingResource() or "community_bridge"
|
||||
local entityPoint = Point.Register(entityData.id, entityData.coords, entityData.spawnDistance or 50.0, entityData, SpawnEntity, RemoveEntity, UpdateEntity)
|
||||
Entities[entityData.id] = entityPoint
|
||||
|
||||
ClientEntity.All[entityData.id] = entityPoint
|
||||
ClientEntity.Invoked[entityData.invoked] = ClientEntity.Invoked[entityData.invoked] or {}
|
||||
ClientEntity.Invoked[entityData.invoked][entityData.id] = entityData
|
||||
Behaviors.Trigger("OnCreate", entityPoint)
|
||||
|
||||
for k, v in pairs(entityPoint) do
|
||||
ClientEntity.SetKey(entityData.id, k, v)
|
||||
end
|
||||
return entityPoint
|
||||
end
|
||||
|
||||
--Depricated use ClientEntity.Create instead
|
||||
--- Registers an entity and spawns it in the world if not already spawned.
|
||||
--- Gets the entity that was spawned or nil if player is not in range
|
||||
---@param id ClientEntityId
|
||||
---@return EntityHandle|nil
|
||||
function ClientEntity.GetEntity(id)
|
||||
local entityData = ClientEntity.Get(id)
|
||||
if not entityData then return nil end
|
||||
return entityData.spawned
|
||||
end
|
||||
---Deprecated use ClientEntity.Create instead
|
||||
ClientEntity.Register = ClientEntity.Create
|
||||
|
||||
|
||||
--- Registers multiple entities.
|
||||
--- @param entities table<number, EntityData> List of ClientEntity definitions.
|
||||
--- @return table<ClientEntityId, table> Map of ClientEntity id to registered entries.
|
||||
function ClientEntity.CreateBulk(entities)
|
||||
local registeredEntities = {}
|
||||
for _, entityData in pairs(entities) do
|
||||
@@ -139,96 +178,148 @@ function ClientEntity.CreateBulk(entities)
|
||||
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.
|
||||
--- Unregisters a ClientEntity registration and deletes its spawned FiveM entity (if any).
|
||||
--- @param id ClientEntityId ClientEntity id (key used in ClientEntity.All / Point system), not a FiveM entity handle.
|
||||
function ClientEntity.Destroy(id)
|
||||
local entityData = Entities[id]
|
||||
local entityData = ClientEntity.All[id]
|
||||
if not entityData then return end
|
||||
Behaviors.Trigger("OnDestroy", entityData)
|
||||
Point.Remove(id)
|
||||
RemoveEntity(entityData)
|
||||
Entities[id] = nil
|
||||
RemoveEntity(entityData)
|
||||
ClientEntity.All[id] = nil
|
||||
end
|
||||
ClientEntity.Unregister = ClientEntity.Destroy
|
||||
|
||||
function ClientEntity.RegisterBehavior(id, behavior)
|
||||
Behaviors.Create(id, behavior)
|
||||
end
|
||||
|
||||
--- Sets a key-value pair for a client entity.
|
||||
---@param id any
|
||||
---@param key any
|
||||
---@param value any
|
||||
---@return nil
|
||||
function ClientEntity.Set(id, key, value)
|
||||
--- Sets a property on a registered ClientEntity entry.
|
||||
--- If the entry defines an On<Key> handler (example: key='coords' -> OnCoords), it will be called.
|
||||
--- @param id ClientEntityId ClientEntity id (key used in ClientEntity.All), not a FiveM entity handle.
|
||||
--- @param key string Field name on the stored entity data table.
|
||||
--- @param value any Value to set.
|
||||
--- @return nil
|
||||
function ClientEntity.SetKey(id, key, value)
|
||||
local entityData = ClientEntity.Get(id)
|
||||
if not entityData then return print(string.format("[ClientEntity] SetKey: Entity %s does not exist", id)) end
|
||||
|
||||
local oldData = entityData[key]
|
||||
if oldData == value then return end
|
||||
if entityData.OnSet then
|
||||
pcall(function (...)
|
||||
return entityData.OnSet(entityData, key, value, oldData)
|
||||
local handlerKey = "On" .. key:sub(1, 1):upper() .. key:sub(2)
|
||||
local handler = rawget(entityData, handlerKey)
|
||||
entityData[key] = value
|
||||
if handler then
|
||||
pcall(function()
|
||||
handler(entityData, key, value, oldData)
|
||||
end)
|
||||
end
|
||||
if value then
|
||||
entityData[key] = value
|
||||
Behaviors.Trigger("OnSet", entityData, key, value, oldData)
|
||||
return
|
||||
end
|
||||
Behaviors.Trigger("OnSet", entityData, key, value, oldData)
|
||||
entityData[key] = nil
|
||||
|
||||
end
|
||||
|
||||
--- Gets a registered ClientEntity entry.
|
||||
--- @param id ClientEntityId ClientEntity id (key used in ClientEntity.All), not a FiveM entity handle.
|
||||
--- @return EntityData|nil The registered entry; when spawned, entityData.spawned is the FiveM entity handle.
|
||||
function ClientEntity.Get(id)
|
||||
return Entities[id]
|
||||
return ClientEntity.All[id]
|
||||
end
|
||||
|
||||
--- Gets all registered ClientEntity entries keyed by ClientEntity id.
|
||||
--- @return table<ClientEntityId, EntityData>
|
||||
function ClientEntity.GetAll()
|
||||
return Entities
|
||||
return ClientEntity.All
|
||||
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)
|
||||
--- Plays or stops an animation on the spawned FiveM entity for a ClientEntity.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param anim ClientEntityAnim|nil Animation data; pass nil to stop.
|
||||
function ClientEntity.SetAnim(id, anim)
|
||||
local entityData = ClientEntity.Get(id)
|
||||
if not entityData then return print(string.format("[ClientEntity] SetAnim: Entity %s does not exist", id)) end
|
||||
local entity = entityData.spawned
|
||||
if not entity or not DoesEntityExist(entity) then return end
|
||||
if not anim then return Bridge.Anim.Stop(id) end
|
||||
Bridge.Anim.Play(id, entity, anim.dict, anim.name, anim.blendIn or 8.0, anim.blendOut or -8.0, anim.duration, anim.flags, anim.playbackRate or 0.0, anim.onComplete)
|
||||
end
|
||||
|
||||
function ClientEntity.SetOnSpawn(id, func)
|
||||
local entityData = Entities[id]
|
||||
if not entityData then
|
||||
print(string.format("[ClientEntity] SetOnSpawn: Entity %s does not exist", id))
|
||||
--- Attaches the spawned FiveM entity for a ClientEntity to a target, or detaches when nil.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param attachData ClientEntityAttach|nil Attachment data; pass nil to detach.
|
||||
function ClientEntity.SetAttach(id, attachData)
|
||||
local entityData = ClientEntity.Get(id)
|
||||
if not entityData then return print(string.format("[ClientEntity] Attach: Entity %s does not exist", id)) end
|
||||
local entity = entityData.spawned
|
||||
if not attachData then
|
||||
if not entityData.spawned or not IsEntityAttached(entityData.spawned) then return end
|
||||
DetachEntity(entityData.spawned, true, true)
|
||||
return
|
||||
end
|
||||
entityData.OnSpawn = func
|
||||
end
|
||||
|
||||
function ClientEntity.SetOnRemove(id, func)
|
||||
local entityData = Entities[id]
|
||||
if not entityData then
|
||||
print(string.format("[ClientEntity] SetOnRemove: Entity %s does not exist", id))
|
||||
return
|
||||
local targetEntity = attachData.target
|
||||
if type(targetEntity) == "string" then
|
||||
targetEntity = Bridge.Entity.Get(targetEntity)?.spawned or targetEntity
|
||||
else
|
||||
local player = GetPlayerFromServerId(targetEntity)
|
||||
if player and player ~= -1 then
|
||||
targetEntity = GetPlayerPed(player)
|
||||
end
|
||||
end
|
||||
entityData.OnRemove = func
|
||||
targetEntity = tonumber(targetEntity)
|
||||
if not targetEntity then return end
|
||||
local bone = attachData.bone or 0
|
||||
local offset = attachData.offset or vector3(0.0, 0.0, 0.0)
|
||||
local rotation = attachData.rotation or vector3(0.0, 0.0, 0.0)
|
||||
AttachEntityToEntity(entity, targetEntity, GetPedBoneIndex(targetEntity, bone), offset.x, offset.y, offset.z, rotation.x, rotation.y, rotation.z, true, true, false, true, 1, true)
|
||||
end
|
||||
|
||||
--- Adds target options to the spawned FiveM entity for a ClientEntity.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param options table Target config passed to Bridge.Target.AddLocalEntity.
|
||||
function ClientEntity.SetTargets(id, options)
|
||||
local entityData = ClientEntity.Get(id)
|
||||
if not entityData then return end
|
||||
Bridge.Target.RemoveLocalEntity(entityData.spawned)
|
||||
entityData.targets = options
|
||||
if not options then return end
|
||||
Bridge.Target.AddLocalEntity(entityData.spawned, options)
|
||||
end
|
||||
|
||||
--- Updates the stored coords for a ClientEntity and updates its proximity trigger.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param coords vector3 New coords.
|
||||
function ClientEntity.UpdateCoords(id, coords)
|
||||
local entityData = Entities[id]
|
||||
local entityData = ClientEntity.All[id]
|
||||
if not entityData then
|
||||
print(string.format("[ClientEntity] UpdateCoords: Entity %s does not exist", id))
|
||||
return
|
||||
end
|
||||
entityData.coords = coords
|
||||
Point.UpdateCoords(id, coords)
|
||||
Point.UpdateCoords(id, coords)
|
||||
if entityData.spawned and DoesEntityExist(entityData.spawned) then
|
||||
SetEntityCoords(entityData.spawned, coords.x, coords.y, coords.z, false, false, false, true)
|
||||
end
|
||||
end
|
||||
|
||||
--- Updates the rotation for a registered entity
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param rotation number|string
|
||||
--- @return nil
|
||||
function ClientEntity.UpdateRotation(id, rotation)
|
||||
local entityData = ClientEntity.All[id]
|
||||
if not entityData then
|
||||
print(string.format("[ClientEntity] UpdateRotation: Entity %s does not exist", id))
|
||||
return
|
||||
end
|
||||
if type(rotation) == "number" or type(rotation) == "string" then
|
||||
rotation = vector3(0.0, 0.0, tonumber(rotation))
|
||||
end
|
||||
entityData.rotation = rotation
|
||||
if entityData.spawned and DoesEntityExist(entityData.spawned) then
|
||||
SetEntityRotation(entityData.spawned, rotation.x, rotation.y, rotation.z, 2, true)
|
||||
end
|
||||
end
|
||||
|
||||
--- Changes the model used by a ClientEntity and respawns it if currently spawned.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param model string|number Model name or hash.
|
||||
function ClientEntity.ChangeModel(id, model)
|
||||
local entityData = Entities[id]
|
||||
local entityData = ClientEntity.All[id]
|
||||
if not entityData then return print(string.format("[ClientEntity] ChangeModel: Entity %s does not exist", id)) end
|
||||
|
||||
entityData.model = model
|
||||
@@ -238,51 +329,78 @@ function ClientEntity.ChangeModel(id, model)
|
||||
SpawnEntity(entityData)
|
||||
end
|
||||
|
||||
--- Hooks onto entity creation by checking for a property on the entityData. Includes ServerEntity creations.
|
||||
--- func = function(entityData) return modifiedEntityData end
|
||||
--- @param property any property to look for when creating the entity
|
||||
--- @param func any
|
||||
function ClientEntity.SetOnCreate(property, func)
|
||||
ClientEntity.OnCreates[property] = func
|
||||
end
|
||||
|
||||
--- Sets the OnSpawn callback for a ClientEntity.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param func fun(entityData: EntityData) Called when in-range spawn happens; entityData.spawned is the FiveM entity handle.
|
||||
function ClientEntity.SetOnSpawn(id, func)
|
||||
local entityData = ClientEntity.All[id]
|
||||
if not entityData then
|
||||
print(string.format("[ClientEntity] SetOnSpawn: Entity %s does not exist", id))
|
||||
return
|
||||
end
|
||||
entityData.OnSpawn = func
|
||||
end
|
||||
|
||||
--- Sets the OnRemove callback for a ClientEntity.
|
||||
--- @param id ClientEntityId ClientEntity id.
|
||||
--- @param func fun(entityData: EntityData) Called when out-of-range removal happens (and before deleting the spawned entity).
|
||||
function ClientEntity.SetOnRemove(id, func)
|
||||
local entityData = ClientEntity.All[id]
|
||||
if not entityData then
|
||||
print(string.format("[ClientEntity] SetOnRemove: Entity %s does not exist", id))
|
||||
return
|
||||
end
|
||||
entityData.OnRemove = func
|
||||
end
|
||||
|
||||
-- Network Event Handlers
|
||||
RegisterNetEvent("community_bridge:client:CreateEntity", function(entityData)
|
||||
local src = source
|
||||
if src ~= 65535 then return end -- Server only
|
||||
ClientEntity.Create(entityData)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("community_bridge:client:CreateEntities", function(entities)
|
||||
local src = source
|
||||
if src ~= 65535 then return end -- Server only
|
||||
ClientEntity.CreateBulk(entities)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("community_bridge:client:DeleteEntity", function(id)
|
||||
ClientEntity.Unregister(id)
|
||||
local src = source
|
||||
if src ~= 65535 then return end -- Server only
|
||||
ClientEntity.Destroy(id)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("community_bridge:client:UpdateEntity", function(id, data)
|
||||
local src = source
|
||||
if src ~= 65535 then return end -- Server only
|
||||
for k, v in pairs(data) do
|
||||
ClientEntity.Set(id, k, v)
|
||||
ClientEntity.SetKey(id, k, v)
|
||||
end
|
||||
end)
|
||||
|
||||
local Loaded = false
|
||||
AddEventHandler('community_bridge:Client:OnPlayerLoaded', function(resourceName)
|
||||
if Loaded then return end
|
||||
Loaded = true
|
||||
end)
|
||||
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if resourceName ~= GetCurrentResourceName() then return end
|
||||
if Loaded then return end
|
||||
Loaded = true
|
||||
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
|
||||
for id, entityData in pairs(ClientEntity.All) do
|
||||
Point.Remove(id)
|
||||
RemoveEntity(entityData)
|
||||
end
|
||||
Entities = {} -- Clear local cache
|
||||
ClientEntity.All = {}
|
||||
else
|
||||
for id, entityData in pairs(ClientEntity.Invoked[resourceName] or {}) do
|
||||
Point.Remove(id) -- Clean up point registration
|
||||
RemoveEntity(entityData) -- Clean up spawned game entity
|
||||
Entities[id] = nil
|
||||
Point.Remove(id)
|
||||
RemoveEntity(entityData)
|
||||
ClientEntity.All[id] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
||||
Behaviors = Behaviors or Require("lib/entities/shared/behaviors.lua")
|
||||
|
||||
local Entities = {}
|
||||
ServerEntity = {} -- Renamed from EntityRelay
|
||||
ServerEntity.Behaviors = Behaviors
|
||||
local Invoked = {}
|
||||
|
||||
--- Creates a server-side representation of an entity and notifies clients.
|
||||
@@ -32,7 +30,6 @@ end
|
||||
function ServerEntity.Create(data)
|
||||
local self = ServerEntity.New(data.id, data.entityType, data.model, data.coords, data.rotation or vector3(0.0, 0.0, data.heading or 0.0), data)
|
||||
if not self then return nil end
|
||||
Behaviors.Trigger("OnCreate", self)
|
||||
TriggerClientEvent("community_bridge:client:CreateEntity", -1, self)
|
||||
return self
|
||||
end
|
||||
@@ -43,7 +40,6 @@ function ServerEntity.CreateBulk(entities)
|
||||
local id = entityData.id or Ids.CreateUniqueId(Entities)
|
||||
local entity = ServerEntity.New(id, entityData.entityType, entityData.model, entityData.coords, entityData.rotation, entityData)
|
||||
createdEntities[id] = entity
|
||||
Behaviors.Trigger("OnCreate", entity)
|
||||
end
|
||||
TriggerClientEvent("community_bridge:client:CreateEntities", -1, createdEntities)
|
||||
return createdEntities
|
||||
@@ -54,7 +50,7 @@ end
|
||||
function ServerEntity.Destroy(id)
|
||||
if not Entities[id] then return end
|
||||
ServerEntity.Remove(id)
|
||||
TriggerClientEvent("community_bridge:client:DeleteEntity", -1, id)
|
||||
TriggerClientEvent("community_bridge:client:DeleteEntity", -1, id)
|
||||
end
|
||||
ServerEntity.Delete = ServerEntity.Destroy
|
||||
|
||||
@@ -91,9 +87,6 @@ function ServerEntity.Set(id, data)
|
||||
return true
|
||||
end
|
||||
|
||||
function ServerEntity.RegisterBehavior(id, behavior)
|
||||
Behaviors.Create(id, behavior)
|
||||
end
|
||||
|
||||
-- Clean up entities associated with a stopped resource
|
||||
AddEventHandler('onResourceStop', function(resourceName)
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
Behaviors = {
|
||||
All = {},
|
||||
isSetup = false,
|
||||
invoked = {}
|
||||
}
|
||||
|
||||
Behaviors.init = {
|
||||
"anim",
|
||||
"attach",
|
||||
'clothing',
|
||||
'follow',
|
||||
'particles',
|
||||
'scenarios',
|
||||
'shop',
|
||||
'stash',
|
||||
'targets',
|
||||
'weapon'
|
||||
}
|
||||
|
||||
function Behaviors.Init()
|
||||
if Behaviors.isSetup then return end
|
||||
Behaviors.isSetup = true
|
||||
for k, v in pairs(Behaviors.init) do
|
||||
Require(string.format("lib/entities/shared/behaviors/%s.lua", v))
|
||||
end
|
||||
end
|
||||
|
||||
function Behaviors.Create(behaviorId, behavior)
|
||||
if not behaviorId or not behavior then return end
|
||||
Behaviors.All = Behaviors.All or {}
|
||||
local invoking = GetInvokingResource() or "community_bridge"
|
||||
Behaviors.All[invoking] = Behaviors.All[invoking] or {}
|
||||
Behaviors.All[invoking][behaviorId] = behavior
|
||||
end
|
||||
|
||||
function Behaviors.Get(behaviorId)
|
||||
local invoking = GetInvokingResource() or "community_bridge"
|
||||
return Behaviors.All[invoking] and Behaviors.All[invoking][behaviorId]
|
||||
end
|
||||
|
||||
function Behaviors.Remove(behaviorId)
|
||||
local invoking = GetInvokingResource() or "community_bridge"
|
||||
if not Behaviors.All[invoking] or not Behaviors.All[invoking][behaviorId] then return end
|
||||
Behaviors.All[invoking][behaviorId] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
function Behaviors.Trigger(actionName, clientEntityData, ...)
|
||||
if not clientEntityData or not actionName then return end
|
||||
local invoking = clientEntityData.invoked or GetInvokingResource() or "community_bridge"
|
||||
for property, behavior in pairs(Behaviors.All[invoking] or {}) do
|
||||
local hasBehaviorArgs = Behaviors.Has(property, clientEntityData) -- this is everything that's contained inside the object's individual property
|
||||
if hasBehaviorArgs and behavior[actionName] then
|
||||
local success, result = pcall(behavior[actionName], clientEntityData, ...)
|
||||
if not success then
|
||||
print(string.format("[ClientEntity] Behavior %s failed: %s", property, result))
|
||||
end
|
||||
end
|
||||
end
|
||||
for property, behavior in pairs(Behaviors.All['community_bridge'] or {}) do
|
||||
local hasBehaviorArgs = Behaviors.Has(property, clientEntityData) -- this is everything that's contained inside the object's individual property
|
||||
if hasBehaviorArgs and behavior[actionName] and not Behaviors.All[invoking]?[actionName] then
|
||||
local success, result = pcall(behavior[actionName], clientEntityData, ...)
|
||||
if not success then
|
||||
print(string.format("[ClientEntity] Behavior %s failed: %s", property, result))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Behaviors.Inherit(behaviorId, clientEntityData, defaultData)
|
||||
local invoking = GetInvokingResource() or "community_bridge"
|
||||
if not Behaviors.All[invoking] or not Behaviors.All[invoking][behaviorId] then
|
||||
print(string.format("[ClientEntity] Behavior %s does not exist", behaviorId))
|
||||
return false
|
||||
end
|
||||
if not clientEntityData or not clientEntityData.id then
|
||||
print("[ClientEntity] Invalid client entity data provided for inheritance")
|
||||
return false
|
||||
end
|
||||
clientEntityData[behaviorId] = defaultData or {} -- Mark the entity as having this behavior
|
||||
return true
|
||||
end
|
||||
|
||||
function Behaviors.Has(behaviorId, clientEntityData)
|
||||
return clientEntityData and clientEntityData[behaviorId]
|
||||
end
|
||||
|
||||
function Behaviors.Cleanup(resourceName)
|
||||
if not resourceName or not Behaviors.All[resourceName] then return end
|
||||
for _, behaviorId in ipairs(Behaviors.All[resourceName]) do
|
||||
Behaviors.Remove(behaviorId)
|
||||
end
|
||||
Behaviors.All[resourceName] = nil
|
||||
end
|
||||
|
||||
AddEventHandler("onResourceStop", function(resourceName)
|
||||
Behaviors.Cleanup(resourceName)
|
||||
if resourceName ~= GetCurrentResourceName() then return end
|
||||
for invoking, behaviors in pairs(Behaviors.All) do
|
||||
for behaviorId, _ in pairs(behaviors) do
|
||||
Behaviors.Remove(behaviorId)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if IsDuplicityVersion() then
|
||||
RegisterNetEvent("onResourceStart", function(resourceName)
|
||||
if resourceName ~= GetCurrentResourceName() then return end
|
||||
Wait(500)
|
||||
Behaviors.Init()
|
||||
end)
|
||||
else
|
||||
RegisterNetEvent("onClientResourceStart", function(resource)
|
||||
if resource ~= GetCurrentResourceName() then return end
|
||||
Wait(500)
|
||||
Behaviors.Init()
|
||||
end)
|
||||
end
|
||||
|
||||
return Behaviors
|
||||
@@ -1,62 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
local Animation = {
|
||||
property = "anim",
|
||||
default = {
|
||||
flags = 49,
|
||||
duration = -1
|
||||
}
|
||||
}
|
||||
-- Anim.Play(id, entity, animDict, animName, blendIn, blendOut, duration, flag, playbackRate, onComplete)
|
||||
function Animation.Play(entityData)
|
||||
if entityData.entityType ~= "ped" then return end
|
||||
local entity = entityData.spawned
|
||||
if not entity then return end
|
||||
local dict = entityData.anim?.dict
|
||||
local name = entityData.anim?.name
|
||||
local onComplete = entityData.anim?.onComplete and function() entityData.anim.onComplete(entityData) end
|
||||
if not dict or not name then return end
|
||||
local flags = entityData.anim.flags or Animation.default.flags
|
||||
local duration = entityData.anim.duration or Animation.default.duration
|
||||
entityData.anim.id = Bridge.Anim.Play(nil, entity, dict, name, 8.0, -8.0, duration, flags, 0.0, onComplete)
|
||||
Bridge.Entity.Set(entityData.id, "anim", entityData.anim)
|
||||
entityData.oldAnim = entityData.anim
|
||||
Bridge.Entity.Set(entityData.id, "oldAnim", entityData.oldAnim)
|
||||
-- SetTimeout(1000, function()
|
||||
-- if entityData.anim then
|
||||
-- entityData.anim.disable = false
|
||||
-- Bridge.Entity.Set(entityData.id, "anim", entityData.anim)
|
||||
-- end
|
||||
-- end)
|
||||
end
|
||||
|
||||
function Animation.OnSpawn(entityData)
|
||||
print("Spawning animation for entity:", entityData.id)
|
||||
if not entityData.spawned or not entityData.anim then return end
|
||||
Animation.Play(entityData)
|
||||
entityData.anim.disable = false
|
||||
TaskSetBlockingOfNonTemporaryEvents(entityData.spawned, true)
|
||||
SetBlockingOfNonTemporaryEvents(entityData.spawned, true)
|
||||
Bridge.Entity.Set(entityData.id, "anim", entityData.anim)
|
||||
end
|
||||
|
||||
function Animation.OnRemove(entityData)
|
||||
if not entityData.spawned or not entityData.anim then return end
|
||||
entityData.oldAnim = nil
|
||||
entityData.anim.disable = true
|
||||
Bridge.Entity.Set(entityData.id, "anim", entityData.anim)
|
||||
Bridge.Entity.Set(entityData.id, "oldAnim", entityData.oldAnim)
|
||||
end
|
||||
|
||||
function Animation.OnUpdate(entityData)
|
||||
if not entityData.spawned or not entityData.anim then return end
|
||||
if entityData.anim.disable then return end
|
||||
if not IsEntityPlayingAnim(entityData.spawned, entityData.anim.dict, entityData.anim.name, 3) then
|
||||
Bridge.Anim.Stop(entityData.anim?.id)
|
||||
ClearPedTasksImmediately(entityData.spawned)
|
||||
Wait(100)
|
||||
Animation.Play(entityData)
|
||||
end
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("anim", Animation)
|
||||
return Animation
|
||||
@@ -1,52 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
local Attach = {
|
||||
property = "attach",
|
||||
default = {
|
||||
bone = 0,
|
||||
offset = vector3(0.0, 0.0, 0.0),
|
||||
rotation = vector3(0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Attach.OnSpawn(entityData)
|
||||
if not entityData.spawned or not entityData.attach then return end
|
||||
local entity = entityData.spawned
|
||||
local targetEntity = entityData.attach.target
|
||||
if type(targetEntity) == "string" then
|
||||
targetEntity = Bridge.Entity.Get(targetEntity)?.spawned or targetEntity
|
||||
else
|
||||
local player = GetPlayerFromServerId(targetEntity)
|
||||
if player and player ~= -1 then
|
||||
targetEntity = GetPlayerPed(player)
|
||||
end
|
||||
end
|
||||
targetEntity = tonumber(targetEntity)
|
||||
if not targetEntity then return end
|
||||
local bone = entityData.attach.bone or Attach.default.bone
|
||||
local offset = entityData.attach.offset or Attach.default.offset
|
||||
local rotation = entityData.attach.rotation or Attach.default.rotation
|
||||
AttachEntityToEntity(entity, targetEntity, GetPedBoneIndex(targetEntity, bone), offset.x, offset.y, offset.z, rotation.x, rotation.y, rotation.z, true, true, false, true, 1, true)
|
||||
Bridge.Entity.Set(entityData.id, "attach", entityData.attach)
|
||||
end
|
||||
|
||||
function Attach.Detach(entityData)
|
||||
if not entityData.spawned or not IsEntityAttached(entityData.spawned) then return end
|
||||
DetachEntity(entityData.spawned, true, true)
|
||||
Bridge.Entity.Set(entityData.id, "attach", entityData.attach)
|
||||
end
|
||||
|
||||
function Attach.OnUpdate(entityData)
|
||||
if not entityData.spawned then return end
|
||||
if entityData.attach.disable then
|
||||
return Attach.Detach(entityData)
|
||||
end
|
||||
local entity = entityData.spawned
|
||||
local coords = GetEntityCoords(entity)
|
||||
Bridge.Entity.Set(entityData.id, "coords", vector3(coords.x, coords.y, coords.z))
|
||||
if IsEntityAttached(entity) then return end
|
||||
Attach.OnSpawn(entityData)
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("attach", Attach)
|
||||
return Attach
|
||||
@@ -1,82 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
local Clothing = {
|
||||
property = "clothing",
|
||||
default = {
|
||||
components = {},
|
||||
props = {}
|
||||
}
|
||||
}
|
||||
|
||||
function Clothing.ChangeClothes(entityData)
|
||||
if entityData.entityType ~= "ped" then return end
|
||||
local entity = entityData.spawned
|
||||
if not entity or not DoesEntityExist(entity) then return end
|
||||
local clothing = entityData.clothing.target
|
||||
if clothing then
|
||||
if type(clothing) == "number" then
|
||||
local player = GetPlayerFromServerId(clothing)
|
||||
if player and player ~= -1 then
|
||||
clothing = Bridge.Clothing.GetAppearance(GetPlayerPed(player))
|
||||
end
|
||||
elseif type(clothing) == "string" then
|
||||
local targetEntityData = Bridge.Entity.Get(clothing)
|
||||
if targetEntityData then
|
||||
clothing = Bridge.Clothing.GetAppearance(targetEntityData.spawned)
|
||||
end
|
||||
end
|
||||
else
|
||||
clothing = entityData.clothing
|
||||
end
|
||||
|
||||
Bridge.Clothing.SetAppearance(entity, clothing)
|
||||
Bridge.Entity.Set(entityData.id, "clothing", clothing)
|
||||
entityData.oldClothing = clothing
|
||||
Bridge.Entity.Set(entityData.id, "oldClothing", clothing)
|
||||
|
||||
end
|
||||
|
||||
|
||||
function Clothing.OnSpawn(entityData)
|
||||
if not entityData.spawned or not entityData.clothing then return end
|
||||
Clothing.ChangeClothes(entityData)
|
||||
entityData.clothing.updating = false
|
||||
end
|
||||
|
||||
function Clothing.OnRemove(entityData)
|
||||
if not entityData.spawned or not entityData.clothing then return end
|
||||
entityData.oldClothing = nil
|
||||
entityData.clothing.updating = true
|
||||
end
|
||||
|
||||
function Clothing.OnUpdate(entityData)
|
||||
if not entityData.spawned or not entityData.clothing then return end
|
||||
if entityData.clothing.updating then return end
|
||||
entityData.clothing.updating = true
|
||||
if not entityData.oldClothing then
|
||||
Clothing.ChangeClothes(entityData)
|
||||
return
|
||||
end
|
||||
for componentId, component in pairs(entityData.clothing.components or {}) do
|
||||
local oldComponent = entityData.oldClothing.components and entityData.oldClothing.components[componentId]
|
||||
if not oldComponent or oldComponent.drawable ~= component.drawable or oldComponent.texture ~= component.texture then
|
||||
Clothing.ChangeClothes(entityData)
|
||||
return
|
||||
end
|
||||
end
|
||||
for propId, prop in pairs(entityData.clothing.props or {}) do
|
||||
local oldProp = entityData.oldClothing.props and entityData.oldClothing.props[propId]
|
||||
if not oldProp or oldProp.drawable ~= prop.drawable or oldProp.texture ~= prop.texture then
|
||||
Clothing.ChangeClothes(entityData)
|
||||
return
|
||||
end
|
||||
end
|
||||
SetTimeout(1000, function()
|
||||
if entityData.clothing then
|
||||
entityData.clothing.updating = false
|
||||
Bridge.Entity.Set(entityData.id, "clothing", entityData.clothing)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("clothing", Clothing)
|
||||
return Clothing
|
||||
@@ -1,75 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
local Follow = {
|
||||
All = {},
|
||||
property = "follow",
|
||||
default = {
|
||||
speed = 1.0,
|
||||
distance = 2.0
|
||||
}
|
||||
}
|
||||
|
||||
function Follow.PedWalkToPos(entityData)
|
||||
if not entityData?.follow?.target or entityData?.entityType ~= "ped" then return end
|
||||
local entity = entityData.spawned
|
||||
local targetCoords = entityData.follow.target
|
||||
if not targetCoords then return end
|
||||
if type(targetCoords) == "number" then
|
||||
local player = GetPlayerFromServerId(targetCoords)
|
||||
if player and player ~= -1 then
|
||||
targetCoords = GetEntityCoords(GetPlayerPed(player))
|
||||
else
|
||||
targetCoords = GetEntityCoords(targetCoords)
|
||||
end
|
||||
elseif type(targetCoords) == "string" then
|
||||
local targetEntityData = Bridge.Entity.Get(targetCoords)
|
||||
if targetEntityData then
|
||||
targetCoords = GetEntityCoords(targetEntityData.spawned)
|
||||
end
|
||||
end
|
||||
local entityPos = GetEntityCoords(entity)
|
||||
local distance = #(targetCoords - entityPos)
|
||||
if distance > (entityData.follow.distance or Follow.default.distance) then
|
||||
if entityData.anim then
|
||||
entityData.anim.disable = true
|
||||
Bridge.Entity.Set(entityData.id, "anim", entityData.anim)
|
||||
end
|
||||
if entityData.scenarios then
|
||||
entityData.scenarios.disable = true
|
||||
Bridge.Entity.Set(entityData.id, "scenarios", entityData.scenarios)
|
||||
end
|
||||
local speed = entityData.follow.speed or Follow.default.speed
|
||||
local heading = GetHeadingFromVector_2d(targetCoords.x - entityPos.x, targetCoords.y - entityPos.y)
|
||||
TaskGoStraightToCoord(entity, targetCoords.x, targetCoords.y, targetCoords.z, speed, -1, heading, 0.0)
|
||||
entityData.coords = GetEntityCoords(entity)
|
||||
entityData.follow.walking = true
|
||||
Bridge.Entity.Set(entityData.id, "coords", entityData.coords - vector3(0,0,1))
|
||||
Bridge.Entity.Set(entityData.id, "follow", entityData.follow)
|
||||
if entityData.follow.OnExit then entityData.follow.OnExit(entityData) end
|
||||
return false
|
||||
end
|
||||
if not entityData.follow.walking then return end
|
||||
entityData.follow.walking = false
|
||||
Bridge.Entity.Set(entityData.id, "follow", entityData.follow)
|
||||
if entityData.anim then
|
||||
entityData.anim.disable = false
|
||||
Bridge.Entity.Set(entityData.id, "anim", entityData.anim)
|
||||
end
|
||||
if entityData.scenarios then
|
||||
entityData.scenarios.disable = false
|
||||
Bridge.Entity.Set(entityData.id, "scenarios", entityData.scenarios)
|
||||
end
|
||||
ClearPedTasks(entity)
|
||||
if entityData.follow.OnEnter then entityData.follow.OnEnter(entityData) end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function Follow.OnUpdate(entityData)
|
||||
assert(entityData?.id, "Entity ID is required")
|
||||
local entity = entityData.spawned
|
||||
if not entity or not DoesEntityExist(entity) then return end
|
||||
return Follow.PedWalkToPos(entityData)
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("follow", Follow)
|
||||
return Follow
|
||||
@@ -1,94 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
Utility = Utility or Bridge.Utility
|
||||
local ParticlesBehaviors = {
|
||||
property = "particles",
|
||||
default = {
|
||||
dict = "cut_exile3",
|
||||
ptfx = "cs_ex3_wheel_spin",
|
||||
offset = vector3(0, 0, 0),
|
||||
rotation = vector3(0, 0, 0),
|
||||
size = 1.0,
|
||||
color = vector3(255, 255, 255),
|
||||
loopLength = 5000,
|
||||
}
|
||||
}
|
||||
|
||||
function ParticlesBehaviors.Start(entityData)
|
||||
for _, particleData in ipairs(entityData.particles) do
|
||||
local position = vector3(entityData.coords.x, entityData.coords.y, entityData.coords.z)
|
||||
local rotation = vector3(entityData.rotation.x, entityData.rotation.y, entityData.rotation.z)
|
||||
position = position + (particleData.offset?.xyz or vector3(0, 0, 0))
|
||||
rotation = rotation + (particleData.rotation?.xyz or vector3(0, 0, 0))
|
||||
particleData.playing = Bridge.Particle.Play(
|
||||
particleData.dict or ParticlesBehaviors.default.dict,
|
||||
particleData.ptfx or ParticlesBehaviors.default.ptfx,
|
||||
position,
|
||||
rotation,
|
||||
particleData.size or ParticlesBehaviors.default.size,
|
||||
particleData.color or ParticlesBehaviors.default.color,
|
||||
particleData.looped,
|
||||
particleData.loopLength or ParticlesBehaviors.default.loopLength
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function ParticlesBehaviors.OnSpawn(entityData)
|
||||
if not entityData.particles then return end
|
||||
if entityData.particles.dict then -- single particle
|
||||
entityData.particles = {entityData.particles}
|
||||
end
|
||||
ParticlesBehaviors.Start(entityData)
|
||||
end
|
||||
|
||||
|
||||
function ParticlesBehaviors.OnRemove(entityData)
|
||||
if not entityData.particles then return end
|
||||
for _, particleData in ipairs(entityData.particles) do
|
||||
if particleData.playing then
|
||||
Bridge.Particle.Stop(particleData.playing)
|
||||
particleData.playing = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ParticlesBehaviors.OnMove(entityData)
|
||||
if not entityData.particles then return end
|
||||
for _, particleData in ipairs(entityData.particles) do
|
||||
if particleData.playing then
|
||||
Particle.Stop(particleData.playing)
|
||||
particleData.playing = nil
|
||||
local position = vector3(entityData.coords.x, entityData.coords.y, entityData.coords.z)
|
||||
local rotation = vector3(entityData.rotation.x, entityData.rotation.y, entityData.rotation.z)
|
||||
position = position + (particleData.offset?.xyz or vector3(0, 0, 0))
|
||||
rotation = rotation + (particleData.rotation?.xyz or vector3(0, 0, 0))
|
||||
particleData.playing = Bridge.Particle.Play(
|
||||
particleData.dict or ParticlesBehaviors.default.dict,
|
||||
particleData.ptfx or ParticlesBehaviors.default.ptfx,
|
||||
position,
|
||||
rotation,
|
||||
particleData.size or ParticlesBehaviors.default.size,
|
||||
particleData.color or ParticlesBehaviors.default.color,
|
||||
particleData.looped,
|
||||
particleData.loopLength or ParticlesBehaviors.default.loopLength
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ParticlesBehaviors.OnSet(entityData, key, value, oldValue)
|
||||
if key ~= "particles" then return end
|
||||
if oldValue then
|
||||
ParticlesBehaviors.OnRemove(entityData)
|
||||
end
|
||||
if value then
|
||||
if value.dict then -- single particle
|
||||
entityData.particles = {value}
|
||||
else
|
||||
entityData.particles = value
|
||||
end
|
||||
ParticlesBehaviors.Start(entityData)
|
||||
end
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("particles", ParticlesBehaviors)
|
||||
return ParticlesBehaviors
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
if IsDuplicityVersion() then return end
|
||||
local Scenarios = {
|
||||
property = "scenarios",
|
||||
default = {
|
||||
introClip = true,
|
||||
duration = -1
|
||||
}
|
||||
}
|
||||
-- scenarios.Play(id, entity, scenariosDict, scenariosName, blendIn, blendOut, duration, flag, playbackRate, onComplete)
|
||||
function Scenarios.Play(entityData)
|
||||
if entityData.entityType ~= "ped" then return end
|
||||
local entity = entityData.spawned
|
||||
if not entity then return end
|
||||
local name = entityData.scenarios?.name
|
||||
if not name then return end
|
||||
local duration = entityData.scenarios?.duration or Scenarios.default.duration
|
||||
local introClip = entityData.scenarios?.introClip or Scenarios.default.introClip
|
||||
TaskStartScenarioInPlace(entity, name, duration, introClip)
|
||||
if duration < 0 then return end
|
||||
SetTimeout(duration, function()
|
||||
if not entityData.scenarios then return end
|
||||
ClearPedTasks(entity)
|
||||
if not entityData.scenarios.onComplete then return end
|
||||
entityData.scenarios.disabled = true
|
||||
Bridge.Entity.Set(entityData.id, "scenarios", entityData.scenarios)
|
||||
entityData.scenarios.onComplete(entityData)
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
function Scenarios.OnSpawn(entityData)
|
||||
if not entityData.spawned or not entityData.scenarios then return end
|
||||
Scenarios.Play(entityData)
|
||||
end
|
||||
|
||||
function Scenarios.OnRemove(entityData)
|
||||
if not entityData.spawned or not entityData.scenarios then return end
|
||||
ClearPedTasks(entityData.spawned)
|
||||
end
|
||||
|
||||
function Scenarios.OnUpdate(entityData)
|
||||
if not entityData.spawned or not entityData.scenarios then return end
|
||||
if entityData.scenarios.disable then return end
|
||||
if IsPedUsingScenario(entityData.spawned, entityData.scenarios.name) then return end
|
||||
local entities = GetGamePool('CObject')
|
||||
for _, obj in ipairs(entities or {}) do
|
||||
obj = tonumber(obj)
|
||||
local entityCoords = GetEntityCoords(obj)
|
||||
local coords = vector3(entityData.coords.x, entityData.coords.y, entityData.coords.z)
|
||||
if entityCoords then
|
||||
local distance = #(entityCoords - coords)
|
||||
if distance < 2.0 then
|
||||
SetEntityAsMissionEntity(obj, false, false)
|
||||
DeleteObject(obj)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
Scenarios.Play(entityData)
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("scenarios", Scenarios)
|
||||
return Scenarios
|
||||
@@ -1,58 +0,0 @@
|
||||
local Shop = {
|
||||
property = "shop",
|
||||
default = {
|
||||
label = "Default Shop Label",
|
||||
items = {
|
||||
{item = "water", price = 10}
|
||||
},
|
||||
target = {
|
||||
label = "Open Shop",
|
||||
icon = "fa-solid fa-box"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Shop.OnCreate(entityData)
|
||||
local shop = entityData.shop
|
||||
if not shop then return end
|
||||
if IsDuplicityVersion() then
|
||||
local label = shop.label or Shop.default.label
|
||||
local items = shop.items or Shop.default.items
|
||||
local coords = entityData.coords
|
||||
local groups = shop.groups
|
||||
Bridge.Inventory.RegisterShop(label, items, nil, groups)
|
||||
return
|
||||
end
|
||||
entityData.targets = entityData.targets or {}
|
||||
print(json.encode(entityData.targets))
|
||||
table.insert(entityData.targets,
|
||||
{
|
||||
label = shop.target.label or "Shop",
|
||||
icon = shop.target.icon or "fa-solid fa-box",
|
||||
onSelect = function()
|
||||
TriggerServerEvent("community_bridge:server:OpenShop", entityData.id)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
if not IsDuplicityVersion() then return Bridge.Entity.RegisterBehavior("shop", Shop) end
|
||||
|
||||
RegisterNetEvent("community_bridge:server:OpenShop", function(id)
|
||||
local src = source
|
||||
if not src then return end
|
||||
local entityData = Bridge.Entity.Get(id)
|
||||
if not entityData or not entityData.shop then
|
||||
return print(string.format("[Shop] OpenShop: Entity %s does not exist or has no shop", id))
|
||||
end
|
||||
local coords = entityData.coords
|
||||
if not coords then return end
|
||||
local distance = #(GetEntityCoords(GetPlayerPed(src)) - coords)
|
||||
if distance > 3.0 then
|
||||
return print(string.format("[Shop] OpenShop: Player %s is too far from entity %s", src, id))
|
||||
end
|
||||
Bridge.Inventory.OpenShop(src, id)
|
||||
end)
|
||||
|
||||
|
||||
return Shop
|
||||
@@ -1,61 +0,0 @@
|
||||
local Stash = {
|
||||
property = "stash",
|
||||
default = {
|
||||
label = "default_stash_label",
|
||||
slots = 20,
|
||||
maxWeight = 100000,
|
||||
target = {
|
||||
label = "Open Stash",
|
||||
icon = "fa-solid fa-box",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Stash.OnCreate(entityData)
|
||||
if not entityData.stash then return end
|
||||
if IsDuplicityVersion() then
|
||||
local label = entityData.stash.label or Stash.default.label
|
||||
local slots = entityData.stash.slots or Stash.default.slots
|
||||
local weight = entityData.stash.maxWeight or Stash.default.maxWeight
|
||||
local owner = entityData.stash.owner
|
||||
local groups = entityData.stash.groups
|
||||
Bridge.Inventory.RegisterStash(entityData.id, label, slots, weight, owner, groups)
|
||||
return
|
||||
end
|
||||
entityData.targets = entityData.targets or {}
|
||||
entityData.targets['stash'] = {
|
||||
label = entityData.stash.target.label or Stash.default.target.label,
|
||||
icon = entityData.stash.target.icon or Stash.default.target.icon,
|
||||
description = entityData.stash.target.description,
|
||||
groups = entityData.stash.target.groups,
|
||||
canInteract = function(_)
|
||||
local entData = Bridge.Entity.Get(entityData.id)
|
||||
return not entData.stash.disable
|
||||
end,
|
||||
onSelect = function()
|
||||
TriggerServerEvent("community_bridge:server:OpenStash", entityData.id)
|
||||
end
|
||||
}
|
||||
|
||||
Bridge.Entity.Set(entityData.id, 'targets', entityData.targets)
|
||||
end
|
||||
|
||||
if not IsDuplicityVersion() then return Bridge.Entity.RegisterBehavior("stash", Stash) end
|
||||
|
||||
RegisterNetEvent("community_bridge:server:OpenStash", function(id)
|
||||
local src = source
|
||||
if not src then return end
|
||||
local entityData = Bridge.Entity.Get(id)
|
||||
if not entityData or not entityData.stash then
|
||||
return print(string.format("[Stash] OpenStash: Entity %s does not exist or has no stash", id))
|
||||
end
|
||||
local coords = vector3(entityData.coords.x, entityData.coords.y, entityData.coords.z)
|
||||
if not coords then return end
|
||||
local distance = #(GetEntityCoords(GetPlayerPed(src)) - coords)
|
||||
if distance > 3.0 then
|
||||
return print(string.format("[Stash] OpenStash: Player %s is too far from entity %s", src, id))
|
||||
end
|
||||
Bridge.Inventory.OpenStash(src, "stash", id)
|
||||
end)
|
||||
|
||||
Bridge.Entity.RegisterBehavior("stash", Stash)
|
||||
@@ -1,78 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
local Targets = {
|
||||
property = "targets",
|
||||
default = {
|
||||
label = "Default Target Label",
|
||||
distance = 2,
|
||||
size = vector3(2.0, 2.0, 2.0),
|
||||
}
|
||||
}
|
||||
|
||||
function Targets.OnSpawn(entityData)
|
||||
if not entityData.targets then return end
|
||||
if entityData.targets?.label then
|
||||
local temp = entityData.targets
|
||||
entityData.targets = {temp}
|
||||
end
|
||||
print("Spawned targets:", entityData.targets)
|
||||
local numberedTargets = {}
|
||||
for k, v in pairs(entityData.targets) do
|
||||
local onSelect = v.onSelect
|
||||
if onSelect then
|
||||
v.onSelect = function(entity)
|
||||
onSelect(entityData, entity)
|
||||
end
|
||||
end
|
||||
table.insert(numberedTargets, v)
|
||||
end
|
||||
table.sort(numberedTargets, function(a, b) return (a.label < b.label) end)
|
||||
if not entityData.spawned then
|
||||
local size = Targets.default.size
|
||||
Bridge.Target.AddBoxZone(entityData.id, entityData.coords, entityData.size, entityData.heading, numberedTargets, entityData.debug)
|
||||
else
|
||||
Bridge.Target.AddLocalEntity(entityData.spawned, numberedTargets)
|
||||
end
|
||||
entityData.oldTargets = entityData.targets
|
||||
Bridge.Entity.Set(entityData.id, "oldTargets", entityData.oldTargets)
|
||||
end
|
||||
|
||||
function Targets.OnRemove(entityData)
|
||||
if not entityData.oldTargets then return end
|
||||
if not entityData.spawned then
|
||||
return Bridge.Target.RemoveZone(entityData.id)
|
||||
end
|
||||
Bridge.Target.RemoveLocalEntity(entityData.spawned)
|
||||
end
|
||||
|
||||
function Targets.OnUpdate(entityData)
|
||||
if not entityData.oldTargets then return end
|
||||
local doesntMatch = false
|
||||
for k, v in pairs(entityData.targets) do
|
||||
if not entityData.oldTargets or not entityData.oldTargets[k] then
|
||||
doesntMatch = true
|
||||
break
|
||||
end
|
||||
local old = entityData.oldTargets[k]
|
||||
if old.label ~= v.label
|
||||
or old.distance ~= v.distance
|
||||
or old.description ~= v.description
|
||||
then
|
||||
doesntMatch = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(entityData.oldTargets) do
|
||||
if not entityData.targets or not entityData.targets[k] then
|
||||
doesntMatch = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if doesntMatch then
|
||||
Targets.OnRemove(entityData)
|
||||
Wait(100)
|
||||
Targets.OnSpawn(entityData)
|
||||
end
|
||||
end
|
||||
Bridge.Entity.RegisterBehavior("targets", Targets)
|
||||
return Targets
|
||||
@@ -1,54 +0,0 @@
|
||||
if IsDuplicityVersion() then return end
|
||||
local Weapon = {
|
||||
property = "weapon",
|
||||
default = {
|
||||
name = "WEAPON_UNARMED",
|
||||
ammo = 0
|
||||
}
|
||||
}
|
||||
|
||||
function Weapon.GiveWeapon(entityData)
|
||||
if entityData.entityType ~= "ped" then return end
|
||||
local entity = entityData.spawned
|
||||
|
||||
if not entity or not DoesEntityExist(entity) then return end
|
||||
if entityData.weapon?.name then
|
||||
local ammo = entityData.weapon.ammo or 0
|
||||
local hash = GetHashKey(entityData.weapon.name)
|
||||
GiveWeaponToPed(entity, hash, ammo, false, true)
|
||||
TaskSwapWeapon(entity, true)
|
||||
end
|
||||
entityData.oldWeapon = entityData.weapon
|
||||
Bridge.Entity.Set(entityData.id, "oldWeapon", entityData.oldWeapon)
|
||||
end
|
||||
|
||||
function Weapon.OnSpawn(entityData)
|
||||
if not entityData.spawned or not entityData.weapon then return end
|
||||
Weapon.GiveWeapon(entityData)
|
||||
entityData.weapon.updating = false
|
||||
end
|
||||
|
||||
function Weapon.OnRemove(entityData)
|
||||
if not entityData.spawned or not entityData.weapon then return end
|
||||
entityData.oldWeapon = nil
|
||||
entityData.weapon.updating = true
|
||||
end
|
||||
|
||||
function Weapon.OnUpdate(entityData)
|
||||
if not entityData.spawned or not entityData.weapon then return end
|
||||
if entityData.weapon.updating then return end
|
||||
entityData.weapon.updating = true
|
||||
if not entityData.oldWeapon or entityData.oldWeapon.name ~= entityData.weapon.name then
|
||||
Weapon.GiveWeapon(entityData)
|
||||
end
|
||||
SetTimeout(1000, function()
|
||||
if entityData.weapon then
|
||||
entityData.weapon.updating = false
|
||||
Bridge.Entity.Set(entityData.id, "weapon", entityData.weapon)
|
||||
end
|
||||
end)
|
||||
Bridge.Entity.Set(entityData.id, "weapon", entityData.weapon)
|
||||
end
|
||||
|
||||
Bridge.Entity.RegisterBehavior("weapon", Weapon)
|
||||
return Weapon
|
||||
Reference in new issue
Block a user