Main/resources/[standalone]/rpemotes-reborn/client/Emote.lua
2025-06-12 03:36:12 +02:00

895 lines
27 KiB
Lua

-- You probably shouldn't touch these.
IsInAnimation = false
CurrentAnimationName = nil
CurrentTextureVariation = nil
InHandsup = false
CONVERTED = false
local ChosenDict = ""
local CurrentAnimOptions = false
local PlayerGender = "male"
local PlayerProps = {}
local PreviewPedProps = {}
local PtfxNotif = false
local PtfxPrompt = false
local AnimationThreadStatus = false
local CheckStatus = false
local CanCancel = true
local InExitEmote = false
local ExitAndPlay = false
local EmoteCancelPlaying = false
local currentEmote = {}
local attachedProp
local scenarioObjects = {
`p_amb_coffeecup_01`,
`p_amb_joint_01`,
`p_cs_ciggy_01`,
`p_cs_ciggy_01b_s`,
`p_cs_clipboard`,
`prop_curl_bar_01`,
`p_cs_joint_01`,
`p_cs_joint_02`,
`prop_acc_guitar_01`,
`prop_amb_ciggy_01`,
`prop_amb_phone`,
`prop_beggers_sign_01`,
`prop_beggers_sign_02`,
`prop_beggers_sign_03`,
`prop_beggers_sign_04`,
`prop_bongos_01`,
`prop_cigar_01`,
`prop_cigar_02`,
`prop_cigar_03`,
`prop_cs_beer_bot_40oz_02`,
`prop_cs_paper_cup`,
`prop_cs_trowel`,
`prop_fib_clipboard`,
`prop_fish_slice_01`,
`prop_fishing_rod_01`,
`prop_fishing_rod_02`,
`prop_notepad_02`,
`prop_parking_wand_01`,
`prop_rag_01`,
`prop_scn_police_torch`,
`prop_sh_cigar_01`,
`prop_sh_joint_01`,
`prop_tool_broom`,
`prop_tool_hammer`,
`prop_tool_jackham`,
`prop_tennis_rack_01`,
`prop_weld_torch`,
`w_me_gclub`,
`p_amb_clipboard_01`
}
if not Config.AnimalEmotesEnabled then
RP.AnimalEmotes = {}
end
CreateThread(function()
LocalPlayer.state:set('canEmote', true, true)
end)
local function RunAnimationThread()
local pPed = PlayerPedId()
if AnimationThreadStatus then return end
AnimationThreadStatus = true
CreateThread(function()
local sleep
while AnimationThreadStatus and (IsInAnimation or PtfxPrompt) do
sleep = 500
if IsInAnimation then
sleep = 0
if IsPlayerAiming(pPed) then
EmoteCancel()
end
if not Config.AllowPunchingDuringEmote then
DisableControlAction(2, 140, true)
DisableControlAction(2, 141, true)
DisableControlAction(2, 142, true)
end
end
if PtfxPrompt and CurrentAnimOptions then
sleep = 0
if not PtfxNotif then
SimpleNotify(CurrentAnimOptions.PtfxInfo or Translate('ptfxinfo'))
PtfxNotif = true
end
if IsControlPressed(0, 47) then
PtfxStart()
Wait(CurrentAnimOptions.PtfxWait)
if CurrentAnimOptions.PtfxCanHold then
while IsControlPressed(0, 47) and IsInAnimation and AnimationThreadStatus do
Wait(5)
end
end
PtfxStop()
end
end
Wait(sleep)
end
end)
end
local function CheckStatusThread(dict, anim)
CreateThread(function()
if CheckStatus then
CheckStatus = false
Wait(10)
end
CheckStatus = true
while not IsEntityPlayingAnim(PlayerPedId(), dict, anim, 3) do
Wait(5)
end
while CheckStatus and IsInAnimation do
if not IsEntityPlayingAnim(PlayerPedId(), dict, anim, 3) then
DebugPrint("Animation ended")
DestroyAllProps()
EmoteCancel()
break
end
Wait(0)
end
end)
end
local function cleanScenarioObjects(isClone)
local ped = isClone and ClonedPed or PlayerPedId()
local playerCoords = GetEntityCoords(ped)
for i = 1, #scenarioObjects do
local deleteScenarioObject = GetClosestObjectOfType(playerCoords.x, playerCoords.y, playerCoords.z, 1.0,
scenarioObjects[i], false, true, true)
if DoesEntityExist(deleteScenarioObject) then
SetEntityAsMissionEntity(deleteScenarioObject, false, false)
DeleteObject(deleteScenarioObject)
end
end
end
function EmoteCancel(force)
LocalPlayer.state:set('currentEmote', nil, true)
EmoteCancelPlaying = true
if InExitEmote then
return
end
if not CanCancel and not force then return end
if ChosenDict == "MaleScenario" and IsInAnimation then
ClearPedTasksImmediately(PlayerPedId())
IsInAnimation = false
DebugPrint("Forced scenario exit")
elseif ChosenDict == "Scenario" and IsInAnimation then
ClearPedTasksImmediately(PlayerPedId())
IsInAnimation = false
DebugPrint("Forced scenario exit")
end
PtfxNotif = false
PtfxPrompt = false
Pointing = false
if IsInAnimation then
local ped = PlayerPedId()
if LocalPlayer.state.ptfx then
PtfxStop()
end
DetachEntity(ped, true, false)
CancelSharedEmote()
if CurrentAnimOptions and CurrentAnimOptions.ExitEmote then
local options = CurrentAnimOptions
local ExitEmoteType = options.ExitEmoteType or "Emotes"
if not RP[options.ExitEmote] then
DebugPrint("Exit emote was invalid")
IsInAnimation = false
ClearPedTasks(ped)
return
end
OnEmotePlay(options.ExitEmote)
DebugPrint("Playing exit animation")
local animationOptions = RP[options.ExitEmote].AnimationOptions
if animationOptions and animationOptions.EmoteDuration then
InExitEmote = true
SetTimeout(animationOptions.EmoteDuration, function()
InExitEmote = false
DestroyAllProps()
ClearPedTasks(ped)
EmoteCancelPlaying = false
end)
return
end
else
IsInAnimation = false
ClearPedTasks(ped)
EmoteCancelPlaying = false
end
DestroyAllProps()
end
cleanScenarioObjects(false)
AnimationThreadStatus = false
CheckStatus = false
end
function EmoteMenuStart(name, category, textureVariation)
local emote = RP[name]
if not emote then
return
end
if emote.category ~= category then
DebugPrint("Emote category mismatch : " .. emote.category .. " vs " .. category)
return
end
if category == "Expressions" then
SetPlayerPedExpression(name, true)
return
end
if emote.category == "AnimalEmotes" then
CheckAnimalAndOnEmotePlay(name)
return
end
OnEmotePlay(name, textureVariation)
end
function EmoteMenuStartClone(name, category)
if not Config.PreviewPed then return end
if not DoesEntityExist(ClonedPed) then return end
local emote = RP[name]
if not emote then
return
end
if emote.category ~= category then
DebugPrint("Emote category mismatch : " .. emote.category .. " vs " .. category)
return
end
if category == "Expressions" then
SetFacialIdleAnimOverride(ClonedPed, emote[1], true)
return
end
OnEmotePlayClone(name)
end
function EmoteCommandStart(args)
if #args > 0 then
if IsEntityDead(PlayerPedId()) or IsPedRagdoll(PlayerPedId()) or IsPedGettingUp(PlayerPedId()) or IsPedInMeleeCombat(PlayerPedId()) then
TriggerEvent('chat:addMessage', {
color = { 255, 0, 0 },
multiline = true,
args = { "RPEmotes", Translate('dead') }
})
return
end
if (IsPedSwimming(PlayerPedId()) or IsPedSwimmingUnderWater(PlayerPedId())) and not Config.AllowInWater then
TriggerEvent('chat:addMessage', {
color = { 255, 0, 0 },
multiline = true,
args = { "RPEmotes", Translate('swimming') }
})
return
end
local name = string.lower(args[1])
if name == "c" then
if IsInAnimation then
EmoteCancel()
else
EmoteChatMessage(Translate('nocancel'))
end
return
end
local emote = RP[name]
if emote then
if emote.category == "AnimalEmotes" then
if Config.AnimalEmotesEnabled then
CheckAnimalAndOnEmotePlay(name)
else
EmoteChatMessage(Translate('animaldisabled'))
end
return
end
if emote.category == "PropEmotes" and emote.AnimationOptions.PropTextureVariations then
if #args > 1 then
local textureVariation = tonumber(args[2])
if emote.AnimationOptions.PropTextureVariations[textureVariation] then
OnEmotePlay(name, textureVariation - 1)
return
else
local str = ""
for k, v in ipairs(emote.AnimationOptions.PropTextureVariations) do
str = str .. string.format("\n(%s) - %s", k, v.Name)
end
EmoteChatMessage(string.format(Translate('invalidvariation'), str), true)
OnEmotePlay(name, 0)
return
end
end
end
OnEmotePlay(name)
else
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
end
end
end
function CheckAnimalAndOnEmotePlay(name)
local playerPed = PlayerPedId()
local isValidPet = false
if string.sub(name, 1, 4) == "bdog" then
for _, model in ipairs(BigDogs) do
if IsPedModel(playerPed, GetHashKey(model)) then
isValidPet = true
break
end
end
elseif string.sub(name, 1, 4) == "sdog" then
for _, model in ipairs(SmallDogs) do
if IsPedModel(playerPed, GetHashKey(model)) then
isValidPet = true
break
end
end
end
if isValidPet then
OnEmotePlay(name)
else
EmoteChatMessage(Translate('notvalidpet'))
end
end
---@param isClone? boolean
function DestroyAllProps(isClone)
if isClone then
for _, v in pairs(PreviewPedProps) do
DeleteEntity(v)
end
PreviewPedProps = {}
else
for _, v in pairs(PlayerProps) do
DeleteEntity(v)
end
PlayerProps = {}
end
DebugPrint("Destroyed Props for " .. (isClone and "clone" or "player"))
end
---@param data table
---@return boolean
function AddProp(data)
assert(data.prop1, 'no prop1 passed')
assert(data.bone, 'no bone passed')
data.off1 = data.off1 or 0.0
data.off2 = data.off2 or 0.0
data.off3 = data.off3 or 0.0
data.rot1 = data.rot1 or 0.0
data.rot2 = data.rot2 or 0.0
data.rot3 = data.rot3 or 0.0
assert(data.noCollision == nil or type(data.noCollision) == "boolean", 'noCollision must be a boolean')
local target = data.isClone and ClonedPed or PlayerPedId()
local x, y, z = table.unpack(GetEntityCoords(target))
if not IsModelValid(data.prop1) then
DebugPrint(tostring(data.prop1) .. " is not a valid model!")
return false
end
LoadPropDict(data.prop1)
attachedProp = CreateObject(GetHashKey(data.prop1), x, y, z + 0.2, not data.isClone, true, true)
if data.textureVariation ~= nil then
SetObjectTextureVariation(attachedProp, data.textureVariation)
end
if data.noCollision then
SetEntityCollision(attachedProp, false, false)
end
AttachEntityToEntity(attachedProp, target, GetPedBoneIndex(target, data.bone), data.off1, data.off2, data.off3, data.rot1, data.rot2, data.rot3,
true, true, false, true, 1, true)
if data.isClone then
table.insert(PreviewPedProps, attachedProp)
else
table.insert(PlayerProps, attachedProp)
end
SetModelAsNoLongerNeeded(data.prop1)
DebugPrint("Added prop to " .. (data.isClone and "clone" or "player"))
return true
end
function CheckGender()
PlayerGender = "male"
if GetEntityModel(PlayerPedId()) == GetHashKey("mp_f_freemode_01") then
PlayerGender = "female"
end
DebugPrint("Set gender to " .. PlayerGender)
end
RegisterNetEvent('animations:ToggleCanDoAnims', function(value)
LocalPlayer.state:set('canEmote', value, true)
end)
function OnEmotePlay(name, textureVariation)
local emoteData = RP[name]
if not emoteData then
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
return
end
if not LocalPlayer.state.canEmote then return end
if not DoesEntityExist(PlayerPedId()) then
return false
end
cleanScenarioObjects(false)
InVehicle = IsPedInAnyVehicle(PlayerPedId(), true)
Pointing = false
if not Config.AllowEmoteInVehicle and InVehicle then
return
end
if Config.AdultEmotesDisabled and emoteData.AdultAnimation then
return EmoteChatMessage(Translate('adultemotedisabled'))
end
if InExitEmote then
return false
end
if Config.CancelPreviousEmote and IsInAnimation and not ExitAndPlay and not EmoteCancelPlaying then
ExitAndPlay = true
DebugPrint("Canceling previous emote and playing next emote")
PlayExitAndEnterEmote(name, textureVariation)
return
end
local animOption = emoteData.AnimationOptions
if InVehicle then
if animOption and animOption.NotInVehicle then
return EmoteChatMessage(Translate('not_in_a_vehicle'))
end
elseif animOption and animOption.onlyInVehicle then
return EmoteChatMessage(Translate('in_a_vehicle'))
end
if CurrentAnimOptions and CurrentAnimOptions.ExitEmote and animOption and animOption.ExitEmote then
if not (animOption and CurrentAnimOptions.ExitEmote == animOption.ExitEmote) and RP[CurrentAnimOptions.ExitEmote][2] ~= emoteData[2] then
return
end
end
if IsInActionWithErrorMessage() then
return false
end
ChosenDict = emoteData[1]
local anim = emoteData[2]
CurrentAnimationName = name
LocalPlayer.state:set('currentEmote', name, true)
CurrentTextureVariation = textureVariation
CurrentAnimOptions = animOption
if Config.DisarmPlayerOnEmote then
if IsPedArmed(PlayerPedId(), 7) then
SetCurrentPedWeapon(PlayerPedId(), GetHashKey('WEAPON_UNARMED'), true)
end
end
if animOption and animOption.Prop then
DestroyAllProps()
end
if ChosenDict == "MaleScenario" or ChosenDict == "Scenario" or ChosenDict == "ScenarioObject" then
if InVehicle then return end
CheckGender()
ClearPedTasks(PlayerPedId())
DestroyAllProps()
if ChosenDict == "MaleScenario" then
if PlayerGender == "male" then
TaskStartScenarioInPlace(PlayerPedId(), anim, 0, true)
DebugPrint("Playing scenario = (" .. anim .. ")")
else
EmoteCancel()
EmoteChatMessage(Translate('maleonly'))
return
end
elseif ChosenDict == "ScenarioObject" then
local BehindPlayer = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, -0.5, -0.5)
TaskStartScenarioAtPosition(PlayerPedId(), anim, BehindPlayer.x, BehindPlayer.y, BehindPlayer.z, GetEntityHeading(PlayerPedId()), 0, true, false)
DebugPrint("Playing scenario = (" .. anim .. ")")
else
TaskStartScenarioInPlace(PlayerPedId(), anim, 0, true)
DebugPrint("Playing scenario = (" .. anim .. ")")
end
IsInAnimation = true
RunAnimationThread()
return
end
-- Small delay at the start
if animOption and animOption.StartDelay then
Wait(animOption.StartDelay)
end
if not LoadAnim(ChosenDict) then
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
return
end
local movementType = 0
if InVehicle then
if animOption and animOption.FullBody then
movementType = 35
else
movementType = 51
end
elseif animOption then
if animOption.EmoteMoving then
movementType = 51
elseif animOption.EmoteLoop then
movementType = 1
elseif animOption.EmoteStuck then
movementType = 50
end
end
DebugPrint("Animation flag = (" .. movementType .. ")")
if animOption then
if animOption.PtfxAsset then
Ptfx1, Ptfx2, Ptfx3, Ptfx4, Ptfx5, Ptfx6, PtfxScale = table.unpack(animOption.PtfxPlacement)
PtfxNotif = false
PtfxPrompt = true
RunAnimationThread()
TriggerServerEvent("rpemotes:ptfx:sync", animOption.PtfxAsset, animOption.PtfxName, vector3(Ptfx1, Ptfx2, Ptfx3),
vector3(Ptfx4, Ptfx5, Ptfx6), animOption.PtfxBone, PtfxScale, animOption.PtfxColor)
else
PtfxPrompt = false
end
end
if IsPedUsingAnyScenario(PlayerPedId()) or IsPedActiveInScenario(PlayerPedId()) then
ClearPedTasksImmediately(PlayerPedId())
end
TaskPlayAnim(PlayerPedId(), ChosenDict, anim, animOption?.BlendInSpeed or 5.0, animOption?.BlendOutSpeed or 5.0, animOption?.EmoteDuration or -1, animOption?.Flag or movementType, 0, false, false,
false)
RemoveAnimDict(ChosenDict)
IsInAnimation = true
RunAnimationThread()
if not (animOption and animOption.Prop) then
CheckStatusThread(ChosenDict, anim)
end
local currentEmoteTable = emoteData
for _, tabledata in pairs(RP) do
for command, emotedata in pairs(tabledata) do
if emotedata == emoteData then
table.insert(currentEmoteTable, command)
break
end
end
end
currentEmote = currentEmoteTable
if animOption and animOption.Prop then
PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6 = table.unpack(animOption.PropPlacement)
Wait(animOption and animOption.EmoteDuration or 0)
if not AddProp({
prop1 = animOption.Prop,
bone = animOption.PropBone,
off1 = PropPl1, off2 = PropPl2, off3 = PropPl3,
rot1 = PropPl4, rot2 = PropPl5, rot3 = PropPl6,
textureVariation = textureVariation,
isClone = false,
noCollision = animOption.PropNoCollision
}) then return end
if animOption.SecondProp then
SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(animOption.SecondPropPlacement)
if not AddProp({
prop1 = animOption.SecondProp,
bone = animOption.SecondPropBone,
off1 = SecondPropPl1, off2 = SecondPropPl2, off3 = SecondPropPl3,
rot1 = SecondPropPl4, rot2 = SecondPropPl5, rot3 = SecondPropPl6,
textureVariation = textureVariation,
isClone = false,
noCollision = animOption.SecondPropNoCollision
}) then
DestroyAllProps()
return
end
end
-- Ptfx is on the prop, then we need to sync it
if not animOption then return end
if animOption.PtfxAsset and not animOption.PtfxNoProp then
TriggerServerEvent("rpemotes:ptfx:syncProp", ObjToNet(attachedProp))
end
end
end
function OnEmotePlayClone(name)
if not Config.PreviewPed then return end
cleanScenarioObjects(true)
if not DoesEntityExist(ClonedPed) then
return false
end
if InExitEmote then
return false
end
if Config.CancelPreviousEmote and not ExitAndPlay and not EmoteCancelPlaying then
ExitAndPlay = true
DebugPrint("Canceling previous emote and playing next emote")
return
end
local emoteData = RP[name]
local animOption = emoteData.AnimationOptions
local dict, anim = table.unpack(emoteData)
if animOption and animOption.Prop then
DestroyAllProps(true)
end
if dict == "MaleScenario" or dict == "Scenario" or dict == "ScenarioObject" then
CheckGender()
ClearPedTasks(ClonedPed)
DestroyAllProps(true)
if dict == "MaleScenario" then
if PlayerGender == "male" then
TaskStartScenarioInPlace(ClonedPed, anim, 0, true)
end
elseif dict == "ScenarioObject" then
local BehindPlayer = GetOffsetFromEntityInWorldCoords(ClonedPed, 0.0, -0.5, -0.5)
TaskStartScenarioAtPosition(ClonedPed, anim, BehindPlayer.x, BehindPlayer.y, BehindPlayer.z, GetEntityHeading(ClonedPed), 0, true, false)
elseif dict == "Scenario" then
TaskStartScenarioInPlace(ClonedPed, anim, 0, true)
end
return
end
if not LoadAnim(dict) then
EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "")
return
end
local movementType = 0
if animOption then
if animOption.EmoteMoving then
movementType = 51
elseif animOption.EmoteLoop then
movementType = 1
elseif animOption.EmoteStuck then
movementType = 50
end
end
if IsPedUsingAnyScenario(ClonedPed) or IsPedActiveInScenario(ClonedPed) then
ClearPedTasksImmediately(ClonedPed)
end
TaskPlayAnim(ClonedPed, dict, anim, 5.0, 5.0, animOption and animOption.EmoteDuration or -1, animOption?.Flag or movementType, 0, false, false, false)
RemoveAnimDict(dict)
if animOption and animOption.Prop then
local PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6 = table.unpack(animOption.PropPlacement)
Wait(animOption and animOption.EmoteDuration or 0)
if not AddProp({
prop1 = animOption.Prop,
bone = animOption.PropBone,
off1 = PropPl1, off2 = PropPl2, off3 = PropPl3,
rot1 = PropPl4, rot2 = PropPl5, rot3 = PropPl6,
isClone = true,
noCollision = animOption.PropNoCollision
}) then return end
if animOption.SecondProp then
local SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(animOption.SecondPropPlacement)
if not AddProp({
prop1 = animOption.SecondProp,
bone = animOption.SecondPropBone,
off1 = SecondPropPl1, off2 = SecondPropPl2, off3 = SecondPropPl3,
rot1 = SecondPropPl4, rot2 = SecondPropPl5, rot3 = SecondPropPl6,
isClone = true,
noCollision = animOption.SecondPropNoCollision
}) then
DestroyAllProps(true)
return
end
end
end
end
function PlayExitAndEnterEmote(name, textureVariation)
local ped = PlayerPedId()
if not CanCancel then return end
if ChosenDict == "MaleScenario" and IsInAnimation then
ClearPedTasksImmediately(ped)
IsInAnimation = false
DebugPrint("Forced scenario exit")
elseif ChosenDict == "Scenario" and IsInAnimation then
ClearPedTasksImmediately(ped)
IsInAnimation = false
DebugPrint("Forced scenario exit")
end
PtfxNotif = false
PtfxPrompt = false
Pointing = false
if LocalPlayer.state.ptfx then
PtfxStop()
end
DetachEntity(ped, true, false)
CancelSharedEmote()
if CurrentAnimOptions?.ExitEmote then
local options = CurrentAnimOptions or {}
if not RP[options.ExitEmote] then
DebugPrint("Exit emote was invalid")
ClearPedTasks(ped)
IsInAnimation = false
return
end
OnEmotePlay(options.ExitEmote)
DebugPrint("Playing exit animation")
local animationOptions = RP[options.ExitEmote].AnimationOptions
if animationOptions and animationOptions.EmoteDuration then
InExitEmote = true
SetTimeout(animationOptions.EmoteDuration, function()
InExitEmote = false
DestroyAllProps(true)
ClearPedTasks(ped)
OnEmotePlay(name, textureVariation)
ExitAndPlay = false
end)
return
end
else
ClearPedTasks(ped)
IsInAnimation = false
ExitAndPlay = false
DestroyAllProps(true)
OnEmotePlay(name, CurrentTextureVariation)
end
end
RegisterNetEvent('animations:client:EmoteCommandStart', function(args)
EmoteCommandStart(args)
end)
CreateExport("EmoteCommandStart", function(emoteName, textureVariation)
EmoteCommandStart({ emoteName, textureVariation })
end)
CreateExport("EmoteCancel", EmoteCancel)
CreateExport("CanCancelEmote", function(State)
CanCancel = State == true
end)
CreateExport('IsPlayerInAnim', function()
return LocalPlayer.state.currentEmote
end)
CreateExport('getCurrentEmote', function()
return currentEmote
end)
-- Door stuff
local openingDoor = false
AddEventHandler('CEventOpenDoor', function(unk1)
if unk1[1] ~= PlayerPedId() then return end
if ShowPed then
return
end
if not IsInAnimation then
return
end
if openingDoor then
return
end
openingDoor = true
while IsPedOpeningADoor(PlayerPedId()) do
Wait(100)
end
openingDoor = false
Wait(200)
ClearPedTasks(PlayerPedId())
DestroyAllProps()
OnEmotePlay(CurrentAnimationName, CurrentTextureVariation)
end)
local isBumpingPed = false
local timeout = 500
AddEventHandler("CEventPlayerCollisionWithPed", function(unk1)
if unk1[1] ~= PlayerPedId() then return end
if not IsInAnimation then
return
end
if isBumpingPed then
timeout = 500
return
end
isBumpingPed = true
timeout = 500
-- We wait a bit to avoid collision with the ped resetting the animation again
while timeout > 0 do
Wait(100)
timeout = timeout - 100
end
if not IsInAnimation then
return
end
isBumpingPed = false
ClearPedTasks(PlayerPedId())
Wait(125)
DestroyAllProps()
OnEmotePlay(CurrentAnimationName, CurrentTextureVariation)
end)
AddEventHandler('onResourceStop', function(resource)
if resource ~= GetCurrentResourceName() then return end
local ped = PlayerPedId()
ClosePedMenu()
DestroyAllProps()
ClearPedTasksImmediately(ped)
DetachEntity(ped, true, false)
ResetPedMovementClipset(ped, 0.8)
end)