diff --git a/lib/entities/client/client_entity.lua b/lib/entities/client/client_entity.lua index 2f36f9b..a61517a 100644 --- a/lib/entities/client/client_entity.lua +++ b/lib/entities/client/client_entity.lua @@ -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 List of ClientEntity definitions. +--- @return 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 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 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) diff --git a/lib/entities/server/server_entity.lua b/lib/entities/server/server_entity.lua index c135432..a629bc3 100644 --- a/lib/entities/server/server_entity.lua +++ b/lib/entities/server/server_entity.lua @@ -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) diff --git a/lib/entities/shared/behaviors.lua b/lib/entities/shared/behaviors.lua deleted file mode 100644 index 460bb56..0000000 --- a/lib/entities/shared/behaviors.lua +++ /dev/null @@ -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 diff --git a/lib/entities/shared/behaviors/anim.lua b/lib/entities/shared/behaviors/anim.lua deleted file mode 100644 index ee6e40a..0000000 --- a/lib/entities/shared/behaviors/anim.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/attach.lua b/lib/entities/shared/behaviors/attach.lua deleted file mode 100644 index 8b3f66f..0000000 --- a/lib/entities/shared/behaviors/attach.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/clothing.lua b/lib/entities/shared/behaviors/clothing.lua deleted file mode 100644 index 4ae11db..0000000 --- a/lib/entities/shared/behaviors/clothing.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/follow.lua b/lib/entities/shared/behaviors/follow.lua deleted file mode 100644 index addefb3..0000000 --- a/lib/entities/shared/behaviors/follow.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/particles.lua b/lib/entities/shared/behaviors/particles.lua deleted file mode 100644 index 3e02b1e..0000000 --- a/lib/entities/shared/behaviors/particles.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/scenarios.lua b/lib/entities/shared/behaviors/scenarios.lua deleted file mode 100644 index 760af6c..0000000 --- a/lib/entities/shared/behaviors/scenarios.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/shop.lua b/lib/entities/shared/behaviors/shop.lua deleted file mode 100644 index 306752c..0000000 --- a/lib/entities/shared/behaviors/shop.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/stash.lua b/lib/entities/shared/behaviors/stash.lua deleted file mode 100644 index badbebc..0000000 --- a/lib/entities/shared/behaviors/stash.lua +++ /dev/null @@ -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) \ No newline at end of file diff --git a/lib/entities/shared/behaviors/targets.lua b/lib/entities/shared/behaviors/targets.lua deleted file mode 100644 index c11afc4..0000000 --- a/lib/entities/shared/behaviors/targets.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/lib/entities/shared/behaviors/weapon.lua b/lib/entities/shared/behaviors/weapon.lua deleted file mode 100644 index 24c6b7c..0000000 --- a/lib/entities/shared/behaviors/weapon.lua +++ /dev/null @@ -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 \ No newline at end of file