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")
|
Utility = Utility or Require("lib/utility/client/utility.lua")
|
||||||
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
||||||
Point = Point or Require("lib/points/client/points.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 = {
|
ClientEntity = {
|
||||||
Invoked = {}
|
All = {},
|
||||||
} -- Renamed from BaseEntity
|
Invoked = {},
|
||||||
ClientEntity.Behaviors = Behaviors
|
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)
|
local function SpawnEntity(entityData)
|
||||||
if entityData.spawned and DoesEntityExist(entityData.spawned) then return end -- Already spawned
|
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)
|
local loaded, model = Utility.LoadModel(entityData.model)
|
||||||
if not loaded then
|
if not loaded then
|
||||||
print(string.format("[ClientEntity] Failed to load model %s for entity %s", entityData.model, entityData.id))
|
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
|
else
|
||||||
print(string.format("[ClientEntity] Unknown entity type '%s' for entity %s", entityData.entityType, entityData.id))
|
print(string.format("[ClientEntity] Unknown entity type '%s' for entity %s", entityData.entityType, entityData.id))
|
||||||
end
|
end
|
||||||
if entity and model then
|
entityData.spawned = entity
|
||||||
entityData.spawned = entity
|
SetEntityProperties(entityData.id)
|
||||||
SetModelAsNoLongerNeeded(model)
|
SetModelAsNoLongerNeeded(model)
|
||||||
SetEntityAsMissionEntity(entity, true, true)
|
|
||||||
FreezeEntityPosition(entity, true)
|
|
||||||
else
|
|
||||||
SetModelAsNoLongerNeeded(model)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if entityData.OnSpawn then
|
if not entityData.OnSpawn then return end
|
||||||
pcall(function (...)
|
pcall(function (...)
|
||||||
return entityData.OnSpawn(entityData)
|
return entityData.OnSpawn(entityData)
|
||||||
end)
|
end)
|
||||||
end
|
|
||||||
Behaviors.Trigger("OnSpawn", entityData)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Removes an entity
|
||||||
|
--- @param entityData EntityData
|
||||||
local function RemoveEntity(entityData)
|
local function RemoveEntity(entityData)
|
||||||
entityData = entityData and entityData.args or entityData
|
entityData = entityData and entityData.args or entityData
|
||||||
if not entityData then return end
|
if not entityData then return end
|
||||||
Behaviors.Trigger("OnRemove", entityData)
|
|
||||||
if entityData.OnRemove then
|
if entityData.OnRemove then
|
||||||
pcall(function (...)
|
pcall(function (...)
|
||||||
return entityData.OnRemove(entityData)
|
return entityData.OnRemove(entityData)
|
||||||
@@ -67,70 +123,53 @@ local function RemoveEntity(entityData)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function UpdateEntity(_entityData)
|
--- Registers a ClientEntity definition and a proximity trigger that spawns/despawns it.
|
||||||
local entityData = ClientEntity.Get(_entityData.id)
|
---
|
||||||
if not entityData then return end
|
--- Note: When in-range, the spawned FiveM entity handle is stored on `entityData.spawned`.
|
||||||
if entityData.OnUpdate then
|
--- @param entityData EntityData
|
||||||
pcall(function (...)
|
--- @return table Registered point/entity entry (the value stored in ClientEntity.All[entityData.id]).
|
||||||
return entityData.OnUpdate(entityData)
|
function ClientEntity.Create(entityData)
|
||||||
end)
|
entityData.id = entityData.id or Ids.CreateUniqueId(ClientEntity.All)
|
||||||
end
|
if ClientEntity.All[entityData.id] then return ClientEntity.All[entityData.id] end
|
||||||
Behaviors.Trigger("OnUpdate", entityData)
|
entityData.rotation = entityData.rotation or vector3(0.0, 0.0, entityData.heading or 0.0)
|
||||||
local coords = vector3(entityData.coords.x, entityData.coords.y, entityData.coords.z)
|
entityData.invoked = entityData.invoked or GetInvokingResource() or "community_bridge"
|
||||||
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
|
|
||||||
|
|
||||||
if not entityData.oldRotation or entityData.oldRotation ~= entityData.rotation then
|
for k, v in pairs(ClientEntity.OnCreates) do
|
||||||
if entityData.spawned and DoesEntityExist(entityData.spawned) then
|
if entityData[k] ~= nil then
|
||||||
if entityData.entityType == 'object' then
|
local success, results = pcall(function()
|
||||||
SetEntityRotation(entityData.spawned, entityData.rotation.x, entityData.rotation.y, entityData.rotation.z, 2, true)
|
return v(entityData)
|
||||||
else -- Ped/Vehicle heading
|
end)
|
||||||
SetEntityHeading(entityData.spawned, type(entityData.rotation) == 'number' and entityData.rotation or entityData.rotation.z)
|
if success then
|
||||||
|
entityData = results or entityData
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
entityData.oldRotation = entityData.rotation
|
|
||||||
end
|
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)
|
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] = ClientEntity.Invoked[entityData.invoked] or {}
|
||||||
ClientEntity.Invoked[entityData.invoked][entityData.id] = entityData
|
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
|
return entityPoint
|
||||||
end
|
end
|
||||||
|
|
||||||
--Depricated use ClientEntity.Create instead
|
--- Gets the entity that was spawned or nil if player is not in range
|
||||||
--- Registers an entity and spawns it in the world if not already spawned.
|
---@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
|
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)
|
function ClientEntity.CreateBulk(entities)
|
||||||
local registeredEntities = {}
|
local registeredEntities = {}
|
||||||
for _, entityData in pairs(entities) do
|
for _, entityData in pairs(entities) do
|
||||||
@@ -139,96 +178,148 @@ function ClientEntity.CreateBulk(entities)
|
|||||||
end
|
end
|
||||||
return registeredEntities
|
return registeredEntities
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Depricated use ClientEntity.CreateBulk instead
|
-- Depricated use ClientEntity.CreateBulk instead
|
||||||
ClientEntity.RegisterBulk = ClientEntity.CreateBulk
|
ClientEntity.RegisterBulk = ClientEntity.CreateBulk
|
||||||
|
|
||||||
--- Unregisters an entity and removes it from the world if spawned.
|
--- Unregisters a ClientEntity registration and deletes its spawned FiveM entity (if any).
|
||||||
-- @param id string|number The ID of the entity to unregister.
|
--- @param id ClientEntityId ClientEntity id (key used in ClientEntity.All / Point system), not a FiveM entity handle.
|
||||||
function ClientEntity.Destroy(id)
|
function ClientEntity.Destroy(id)
|
||||||
local entityData = Entities[id]
|
local entityData = ClientEntity.All[id]
|
||||||
if not entityData then return end
|
if not entityData then return end
|
||||||
Behaviors.Trigger("OnDestroy", entityData)
|
|
||||||
Point.Remove(id)
|
Point.Remove(id)
|
||||||
RemoveEntity(entityData)
|
RemoveEntity(entityData)
|
||||||
Entities[id] = nil
|
ClientEntity.All[id] = nil
|
||||||
end
|
end
|
||||||
ClientEntity.Unregister = ClientEntity.Destroy
|
ClientEntity.Unregister = ClientEntity.Destroy
|
||||||
|
|
||||||
function ClientEntity.RegisterBehavior(id, behavior)
|
--- Sets a property on a registered ClientEntity entry.
|
||||||
Behaviors.Create(id, behavior)
|
--- If the entry defines an On<Key> handler (example: key='coords' -> OnCoords), it will be called.
|
||||||
end
|
--- @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.
|
||||||
--- Sets a key-value pair for a client entity.
|
--- @param value any Value to set.
|
||||||
---@param id any
|
--- @return nil
|
||||||
---@param key any
|
function ClientEntity.SetKey(id, key, value)
|
||||||
---@param value any
|
|
||||||
---@return nil
|
|
||||||
function ClientEntity.Set(id, key, value)
|
|
||||||
local entityData = ClientEntity.Get(id)
|
local entityData = ClientEntity.Get(id)
|
||||||
if not entityData then return print(string.format("[ClientEntity] SetKey: Entity %s does not exist", id)) end
|
if not entityData then return print(string.format("[ClientEntity] SetKey: Entity %s does not exist", id)) end
|
||||||
|
|
||||||
local oldData = entityData[key]
|
local oldData = entityData[key]
|
||||||
if oldData == value then return end
|
local handlerKey = "On" .. key:sub(1, 1):upper() .. key:sub(2)
|
||||||
if entityData.OnSet then
|
local handler = rawget(entityData, handlerKey)
|
||||||
pcall(function (...)
|
entityData[key] = value
|
||||||
return entityData.OnSet(entityData, key, value, oldData)
|
if handler then
|
||||||
|
pcall(function()
|
||||||
|
handler(entityData, key, value, oldData)
|
||||||
end)
|
end)
|
||||||
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
|
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)
|
function ClientEntity.Get(id)
|
||||||
return Entities[id]
|
return ClientEntity.All[id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Gets all registered ClientEntity entries keyed by ClientEntity id.
|
||||||
|
--- @return table<ClientEntityId, EntityData>
|
||||||
function ClientEntity.GetAll()
|
function ClientEntity.GetAll()
|
||||||
return Entities
|
return ClientEntity.All
|
||||||
end
|
end
|
||||||
|
|
||||||
function ClientEntity.OnCreate(_type, func)
|
--- Plays or stops an animation on the spawned FiveM entity for a ClientEntity.
|
||||||
ClientEntity.OnCreates = ClientEntity.OnCreates or {}
|
--- @param id ClientEntityId ClientEntity id.
|
||||||
if not ClientEntity.OnCreates[_type] then
|
--- @param anim ClientEntityAnim|nil Animation data; pass nil to stop.
|
||||||
ClientEntity.OnCreates[_type] = {}
|
function ClientEntity.SetAnim(id, anim)
|
||||||
end
|
local entityData = ClientEntity.Get(id)
|
||||||
table.insert(ClientEntity.OnCreates[_type], func)
|
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
|
end
|
||||||
|
|
||||||
function ClientEntity.SetOnSpawn(id, func)
|
--- Attaches the spawned FiveM entity for a ClientEntity to a target, or detaches when nil.
|
||||||
local entityData = Entities[id]
|
--- @param id ClientEntityId ClientEntity id.
|
||||||
if not entityData then
|
--- @param attachData ClientEntityAttach|nil Attachment data; pass nil to detach.
|
||||||
print(string.format("[ClientEntity] SetOnSpawn: Entity %s does not exist", id))
|
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
|
return
|
||||||
end
|
end
|
||||||
entityData.OnSpawn = func
|
local targetEntity = attachData.target
|
||||||
end
|
if type(targetEntity) == "string" then
|
||||||
|
targetEntity = Bridge.Entity.Get(targetEntity)?.spawned or targetEntity
|
||||||
function ClientEntity.SetOnRemove(id, func)
|
else
|
||||||
local entityData = Entities[id]
|
local player = GetPlayerFromServerId(targetEntity)
|
||||||
if not entityData then
|
if player and player ~= -1 then
|
||||||
print(string.format("[ClientEntity] SetOnRemove: Entity %s does not exist", id))
|
targetEntity = GetPlayerPed(player)
|
||||||
return
|
end
|
||||||
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
|
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)
|
function ClientEntity.UpdateCoords(id, coords)
|
||||||
local entityData = Entities[id]
|
local entityData = ClientEntity.All[id]
|
||||||
if not entityData then
|
if not entityData then
|
||||||
print(string.format("[ClientEntity] UpdateCoords: Entity %s does not exist", id))
|
print(string.format("[ClientEntity] UpdateCoords: Entity %s does not exist", id))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
entityData.coords = coords
|
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
|
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)
|
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
|
if not entityData then return print(string.format("[ClientEntity] ChangeModel: Entity %s does not exist", id)) end
|
||||||
|
|
||||||
entityData.model = model
|
entityData.model = model
|
||||||
@@ -238,51 +329,78 @@ function ClientEntity.ChangeModel(id, model)
|
|||||||
SpawnEntity(entityData)
|
SpawnEntity(entityData)
|
||||||
end
|
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
|
-- Network Event Handlers
|
||||||
RegisterNetEvent("community_bridge:client:CreateEntity", function(entityData)
|
RegisterNetEvent("community_bridge:client:CreateEntity", function(entityData)
|
||||||
|
local src = source
|
||||||
|
if src ~= 65535 then return end -- Server only
|
||||||
ClientEntity.Create(entityData)
|
ClientEntity.Create(entityData)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNetEvent("community_bridge:client:CreateEntities", function(entities)
|
RegisterNetEvent("community_bridge:client:CreateEntities", function(entities)
|
||||||
|
local src = source
|
||||||
|
if src ~= 65535 then return end -- Server only
|
||||||
ClientEntity.CreateBulk(entities)
|
ClientEntity.CreateBulk(entities)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNetEvent("community_bridge:client:DeleteEntity", function(id)
|
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)
|
end)
|
||||||
|
|
||||||
RegisterNetEvent("community_bridge:client:UpdateEntity", function(id, data)
|
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
|
for k, v in pairs(data) do
|
||||||
ClientEntity.Set(id, k, v)
|
ClientEntity.SetKey(id, k, v)
|
||||||
end
|
end
|
||||||
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
|
-- Resource Stop Cleanup
|
||||||
AddEventHandler('onResourceStop', function(resourceName)
|
AddEventHandler('onResourceStop', function(resourceName)
|
||||||
if resourceName == GetCurrentResourceName() then
|
if resourceName == GetCurrentResourceName() then
|
||||||
for id, entityData in pairs(Entities) do
|
for id, entityData in pairs(ClientEntity.All) do
|
||||||
Point.Remove(id) -- Clean up point registration
|
Point.Remove(id)
|
||||||
RemoveEntity(entityData) -- Clean up spawned game entity
|
RemoveEntity(entityData)
|
||||||
end
|
end
|
||||||
Entities = {} -- Clear local cache
|
ClientEntity.All = {}
|
||||||
else
|
else
|
||||||
for id, entityData in pairs(ClientEntity.Invoked[resourceName] or {}) do
|
for id, entityData in pairs(ClientEntity.Invoked[resourceName] or {}) do
|
||||||
Point.Remove(id) -- Clean up point registration
|
Point.Remove(id)
|
||||||
RemoveEntity(entityData) -- Clean up spawned game entity
|
RemoveEntity(entityData)
|
||||||
Entities[id] = nil
|
ClientEntity.All[id] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
Ids = Ids or Require("lib/utility/shared/ids.lua")
|
||||||
Behaviors = Behaviors or Require("lib/entities/shared/behaviors.lua")
|
|
||||||
|
|
||||||
local Entities = {}
|
local Entities = {}
|
||||||
ServerEntity = {} -- Renamed from EntityRelay
|
ServerEntity = {} -- Renamed from EntityRelay
|
||||||
ServerEntity.Behaviors = Behaviors
|
|
||||||
local Invoked = {}
|
local Invoked = {}
|
||||||
|
|
||||||
--- Creates a server-side representation of an entity and notifies clients.
|
--- Creates a server-side representation of an entity and notifies clients.
|
||||||
@@ -32,7 +30,6 @@ end
|
|||||||
function ServerEntity.Create(data)
|
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)
|
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
|
if not self then return nil end
|
||||||
Behaviors.Trigger("OnCreate", self)
|
|
||||||
TriggerClientEvent("community_bridge:client:CreateEntity", -1, self)
|
TriggerClientEvent("community_bridge:client:CreateEntity", -1, self)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -43,7 +40,6 @@ function ServerEntity.CreateBulk(entities)
|
|||||||
local id = entityData.id or Ids.CreateUniqueId(Entities)
|
local id = entityData.id or Ids.CreateUniqueId(Entities)
|
||||||
local entity = ServerEntity.New(id, entityData.entityType, entityData.model, entityData.coords, entityData.rotation, entityData)
|
local entity = ServerEntity.New(id, entityData.entityType, entityData.model, entityData.coords, entityData.rotation, entityData)
|
||||||
createdEntities[id] = entity
|
createdEntities[id] = entity
|
||||||
Behaviors.Trigger("OnCreate", entity)
|
|
||||||
end
|
end
|
||||||
TriggerClientEvent("community_bridge:client:CreateEntities", -1, createdEntities)
|
TriggerClientEvent("community_bridge:client:CreateEntities", -1, createdEntities)
|
||||||
return createdEntities
|
return createdEntities
|
||||||
@@ -54,7 +50,7 @@ end
|
|||||||
function ServerEntity.Destroy(id)
|
function ServerEntity.Destroy(id)
|
||||||
if not Entities[id] then return end
|
if not Entities[id] then return end
|
||||||
ServerEntity.Remove(id)
|
ServerEntity.Remove(id)
|
||||||
TriggerClientEvent("community_bridge:client:DeleteEntity", -1, id)
|
TriggerClientEvent("community_bridge:client:DeleteEntity", -1, id)
|
||||||
end
|
end
|
||||||
ServerEntity.Delete = ServerEntity.Destroy
|
ServerEntity.Delete = ServerEntity.Destroy
|
||||||
|
|
||||||
@@ -91,9 +87,6 @@ function ServerEntity.Set(id, data)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerEntity.RegisterBehavior(id, behavior)
|
|
||||||
Behaviors.Create(id, behavior)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clean up entities associated with a stopped resource
|
-- Clean up entities associated with a stopped resource
|
||||||
AddEventHandler('onResourceStop', function(resourceName)
|
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