-- You probably shouldn't touch these. local ChosenDict = "" local ChosenAnimOptions = false local PlayerGender = "male" local PlayerProps = {} local PlayerParticles = {} 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 IsInAnimation = false CurrentAnimationName = nil CurrentTextureVariation = nil InHandsup = false -- Remove emotes if needed local emoteTypes = { Shared = '🤼 ', Dances = '', AnimalEmotes = '🐶 ', Emotes = '', PropEmotes = '📦 ' } for emoteType, prefix in pairs(emoteTypes) do for emoteName, emoteData in pairs(RP[emoteType]) do if prefix ~= '' then emoteData[3] = prefix..emoteData[3] end local shouldRemove = false if Config.AdultEmotesDisabled and emoteData.AdultAnimation then shouldRemove = true elseif emoteData[1] and not ((emoteData[1] == 'Scenario') or (emoteData[1] == 'ScenarioObject') or (emoteData[1] == 'MaleScenario')) and not DoesAnimDictExist(emoteData[1]) then shouldRemove = true end if shouldRemove then RP[emoteType][emoteName] = nil end end end if not Config.AnimalEmotesEnabled then RP.AnimalEmotes = {} end local function RunAnimationThread() local playerId = 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(playerId) then EmoteCancel() end if not Config.AllowPunching then DisableControlAction(2, 140, true) DisableControlAction(2, 141, true) DisableControlAction(2, 142, true) end end if PtfxPrompt and ChosenAnimOptions then sleep = 0 if not PtfxNotif then SimpleNotify(ChosenAnimOptions.PtfxInfo) PtfxNotif = true end if IsControlPressed(0, 47) then PtfxStart() Wait(ChosenAnimOptions.PtfxWait) if ChosenAnimOptions.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 if Config.EnableCancelKeybind then RegisterKeyMapping("emotecancel", Translate('register_cancel_emote'), "keyboard", Config.CancelEmoteKey) end ----------------------------------------------------------------------------------------------------- -- Commands / Events -------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- CreateThread(function() TriggerEvent('chat:addSuggestion', '/e', Translate('play_emote'), { { name = "emotename", help = Translate('help_command') }, { name = "texturevariation", help = Translate('help_variation') } }) TriggerEvent('chat:addSuggestion', '/emote', Translate('play_emote'), { { name = "emotename", help = Translate('help_command') }, { name = "texturevariation", help = Translate('help_variation') } }) if Config.Keybinding then TriggerEvent('chat:addSuggestion', '/emotebind', Translate('link_emote_keybind'), { { name = "key", help = "num4, num5, num6, num7. num8, num9. Numpad 4-9!" }, { name = "emotename", help = Translate('help_command') } }) TriggerEvent('chat:addSuggestion', '/emotebinds', Translate('show_emote_keybind')) TriggerEvent('chat:addSuggestion', '/emotedelete', Translate('remove_emote_keybind'), { { name = "key", help = "num4, num5, num6, num7. num8, num9. Numpad 4-9!" } }) end TriggerEvent('chat:addSuggestion', '/emotemenu', Translate('open_menu_emote')) TriggerEvent('chat:addSuggestion', '/emotes', Translate('show_list_emote')) TriggerEvent('chat:addSuggestion', '/emotecancel', Translate('cancel_emote')) end) RegisterCommand('e', function(source, args, raw) EmoteCommandStart(source, args, raw) end, false) RegisterCommand('emote', function(source, args, raw) EmoteCommandStart(source, args, raw) end, false) if Config.Keybinding then RegisterCommand('emotebind', function(source, args, raw) EmoteBindStart(source, args, raw) end, false) RegisterCommand('emotebinds', function(source, args, raw) ListKeybinds() end, false) RegisterCommand('emotedelete', function(source, args) DeleteEmote(args) end, false) end if Config.MenuKeybindEnabled then RegisterCommand('emoteui', function() OpenEmoteMenu() end, false) RegisterKeyMapping("emoteui", Translate('register_open_menu'), "keyboard", Config.MenuKeybind) else RegisterCommand('emotemenu', function() OpenEmoteMenu() end, false) end RegisterCommand('emotes', function() EmotesOnCommand() end, false) RegisterCommand('emotecancel', function() EmoteCancel() end, false) local disableHandsupControls = { [36] = true, -- INPUT_DUCK [44] = true, -- INPUT_COVER [53] = true, -- INPUT_WEAPON_SPECIAL [54] = true, -- INPUT_WEAPON_SPECIAL_TWO [59] = true, -- INPUT_VEH_MOVE_LR [60] = true, -- INPUT_VEH_MOVE_UD [61] = true, -- INPUT_VEH_MOVE_UP_ONLY [62] = true, -- INPUT_VEH_MOVE_DOWN_ONLY [63] = true, -- INPUT_VEH_MOVE_LEFT_ONLY [64] = true, -- INPUT_VEH_MOVE_RIGHT_ONLY [65] = true, -- INPUT_VEH_SPECIAL [66] = true, -- INPUT_VEH_GUN_LR [67] = true, -- INPUT_VEH_GUN_UD [69] = true, -- INPUT_VEH_ATTACK [70] = true, -- INPUT_VEH_ATTACK2 [71] = true, -- INPUT_VEH_ACCELERATE [72] = true, -- INPUT_VEH_BRAKE [73] = true, -- INPUT_VEH_DUCK [74] = true, -- INPUT_VEH_HEADLIGHT [77] = true, -- INPUT_VEH_HOTWIRE_LEFT [78] = true, -- INPUT_VEH_HOTWIRE_RIGHT [80] = true, -- INPUT_VEH_CIN_CAM [86] = true, -- INPUT_VEH_HORN [91] = true, -- INPUT_VEH_PASSENGER_AIM [102] = true, -- INPUT_VEH_JUMP [104] = true, -- INPUT_VEH_SHUFFLE [105] = true, -- INPUT_VEH_DROP_PROJECTILE [136] = true, -- INPUT_VEH_PUSHBIKE_PEDAL [137] = true, -- INPUT_VEH_PUSHBIKE_SPRINT [139] = true, -- INPUT_VEH_PUSHBIKE_REAR_BRAKE [140] = true, -- INPUT_MELEE_ATTACK_LIGHT [141] = true, -- INPUT_MELEE_ATTACK_HEAVY [142] = true, -- INPUT_MELEE_ATTACK_ALTERNATE [143] = true, -- INPUT_MELEE_BLOCK [337] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_TOGGLE [338] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_LEFT [339] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_RIGHT [340] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_UP [341] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_DOWN [342] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_UD [343] = true, -- INPUT_VEH_HYDRAULICS_CONTROL_LR [351] = true, -- INPUT_VEH_ROCKET_BOOST [354] = true, -- INPUT_VEH_BIKE_WINGS [357] = true, -- INPUT_VEH_TRANSFORM [345] = true, -- INPUT_VEH_MELEE_HOLD [346] = true, -- INPUT_VEH_MELEE_LEFT [347] = true, -- INPUT_VEH_MELEE_RIGHT } local playerId = PlayerId() local function HandsUpLoop() CreateThread(function() while InHandsup do if disableHandsupControls then for control, state in pairs(disableHandsupControls) do DisableControlAction(0, control, state) end end if IsPlayerAiming(playerId) then ClearPedSecondaryTask(PlayerPedId()) CreateThread(function() Wait(350) InHandsup = false end) end Wait(0) end end) end if Config.HandsupEnabled then local function ToggleHandsUp(commandType) RegisterCommand(commandType, function() if IsPedInAnyVehicle(PlayerPedId(), false) and not Config.HandsupKeybindInCarEnabled and not InHandsup then return end Handsup() end, false) end if Config.HoldToHandsUp then ToggleHandsUp('+handsup') ToggleHandsUp('-handsup') else ToggleHandsUp('handsup') end function Handsup() local playerPed = PlayerPedId() if not IsPedHuman(playerPed) then return end if IsInActionWithErrorMessage() then return end InHandsup = not InHandsup if InHandsup then LocalPlayer.state:set('currentEmote', 'handsup', true) DestroyAllProps() local dict = "random@mugging3" RequestAnimDict(dict) while not HasAnimDictLoaded(dict) do Wait(0) end TaskPlayAnim(PlayerPedId(), dict, "handsup_standing_base", 3.0, 3.0, -1, 49, 0, false, IsThisModelABike(GetEntityModel(GetVehiclePedIsIn(PlayerPedId(), false))) and 4127 or false, false) HandsUpLoop() else LocalPlayer.state:set('currentEmote', nil, true) ClearPedSecondaryTask(PlayerPedId()) if Config.PersistentEmoteAfterHandsup and IsInAnimation then local emote = RP.Emotes[CurrentAnimationName] or RP.PropEmotes[CurrentAnimationName] or RP.Dances[CurrentAnimationName] or RP.AnimalEmotes[CurrentAnimationName] if not emote then return end Wait(400) DestroyAllProps() OnEmotePlay(emote, CurrentAnimationName, CurrentTextureVariation) end end end TriggerEvent('chat:addSuggestion', '/handsup', Translate('handsup')) if Config.HandsupKeybindEnabled then RegisterKeyMapping("handsup", Translate('register_handsup'), "keyboard", Config.HandsupKeybind) end local function IsPlayerInHandsUp() return InHandsup end exports('IsPlayerInHandsUp', IsPlayerInHandsUp) end AddEventHandler('onResourceStop', function(resource) if resource == GetCurrentResourceName() then local ped = PlayerPedId() ClosePedMenu() DestroyAllProps() ClearPedTasksImmediately(ped) DetachEntity(ped, true, false) ResetPedMovementClipset(ped, 0.8) end end) ----------------------------------------------------------------------------------------------------- ------ Functions and stuff -------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- 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` } 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 -- Don't cancel if we are in an exit emote if InExitEmote then return end local ped = PlayerPedId() if not CanCancel and force ~= true 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 IsInAnimation then if LocalPlayer.state.ptfx then PtfxStop() end DetachEntity(ped, true, false) CancelSharedEmote() if ChosenAnimOptions and ChosenAnimOptions.ExitEmote then -- If the emote exit type is not specified, it defaults to Emotes local options = ChosenAnimOptions local ExitEmoteType = options.ExitEmoteType or "Emotes" -- Checks that the exit emote actually exists if not RP[ExitEmoteType] or not RP[ExitEmoteType][options.ExitEmote] then DebugPrint("Exit emote was invalid") IsInAnimation = false ClearPedTasks(ped) return end OnEmotePlay(RP[ExitEmoteType][options.ExitEmote], ExitEmoteType) DebugPrint("Playing exit animation") -- Check that the exit emote has a duration, and if so, set InExitEmote variable local animationOptions = RP[ExitEmoteType][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 PtfxThis(asset) while not HasNamedPtfxAssetLoaded(asset) do RequestNamedPtfxAsset(asset) Wait(10) end UseParticleFxAsset(asset) end function PtfxStart() LocalPlayer.state:set('ptfx', true, true) end function PtfxStop() LocalPlayer.state:set('ptfx', false, true) end AddStateBagChangeHandler('ptfx', '', function(bagName, key, value, _unused, replicated) local plyId = tonumber(bagName:gsub('player:', ''), 10) -- We stop here if we don't need to go further -- We don't need to start or stop the ptfx twice if (PlayerParticles[plyId] and value) or (not PlayerParticles[plyId] and not value) then return end -- Only allow ptfx change on players local ply = GetPlayerFromServerId(plyId) if ply == 0 then return end local plyPed = GetPlayerPed(ply) if not DoesEntityExist(plyPed) then return end local stateBag = Player(plyId).state if value then -- Start ptfx local asset = stateBag.ptfxAsset local name = stateBag.ptfxName local offset = stateBag.ptfxOffset local rot = stateBag.ptfxRot local boneIndex = stateBag.ptfxBone and GetPedBoneIndex(plyPed, stateBag.ptfxBone) or GetEntityBoneIndexByName(name, "VFX") local scale = stateBag.ptfxScale or 1 local color = stateBag.ptfxColor local propNet = stateBag.ptfxPropNet local entityTarget = plyPed if propNet then local propObj = NetToObj(propNet) if DoesEntityExist(propObj) then entityTarget = propObj end end PtfxThis(asset) PlayerParticles[plyId] = StartNetworkedParticleFxLoopedOnEntityBone(name, entityTarget, offset.x, offset.y, offset.z, rot.x, rot.y, rot.z, boneIndex, scale + 0.0, false, false, false) if color then if color[1] and type(color[1]) == 'table' then local randomIndex = math.random(1, #color) color = color[randomIndex] end SetParticleFxLoopedAlpha(PlayerParticles[plyId], color.A) SetParticleFxLoopedColour(PlayerParticles[plyId], color.R / 255, color.G / 255, color.B / 255, false) end DebugPrint("Started PTFX: " .. PlayerParticles[plyId]) else DebugPrint("Stopped PTFX: " .. PlayerParticles[plyId]) StopParticleFxLooped(PlayerParticles[plyId], false) RemoveNamedPtfxAsset(stateBag.ptfxAsset) PlayerParticles[plyId] = nil end end) function EmotesOnCommand(source, args, raw) local EmotesCommand = "" for a in PairsByKeys(RP.Emotes) do EmotesCommand = EmotesCommand .. "" .. a .. ", " end EmoteChatMessage(EmotesCommand) EmoteChatMessage(Translate('emotemenucmd')) end function EmoteMenuStart(name, category, textureVariation) if category == "dances" then if RP.Dances[name] ~= nil then OnEmotePlay(RP.Dances[name], name) end elseif category == "animals" then if RP.AnimalEmotes[name] ~= nil then CheckAnimalAndOnEmotePlay(RP.AnimalEmotes[name], name) end elseif category == "props" then if RP.PropEmotes[name] ~= nil then OnEmotePlay(RP.PropEmotes[name], name, textureVariation) end elseif category == "emotes" then if RP.Emotes[name] ~= nil then OnEmotePlay(RP.Emotes[name], name) end elseif category == "expression" then if RP.Expressions[name] ~= nil then SetPlayerPedExpression(RP.Expressions[name][1], true) end end end function EmoteMenuStartClone(name, category) if category == "dances" then if RP.Dances[name] then OnEmotePlayClone(RP.Dances[name]) end elseif category == "props" then if RP.PropEmotes[name] then OnEmotePlayClone(RP.PropEmotes[name]) end elseif category == "emotes" then if RP.Emotes[name] then OnEmotePlayClone(RP.Emotes[name]) end elseif category == "expression" then if RP.Expressions[name] then SetFacialIdleAnimOverride(ClonedPed, RP.Expressions[name][1], 0) end end end function EmoteCommandStart(source, args, raw) 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 elseif name == "help" then EmotesOnCommand() return end local emote = RP.Emotes[name] or RP.Dances[name] or RP.AnimalEmotes[name] or RP.PropEmotes[name] or RP.Expressions[name] or RP.Exits[name] if emote then if RP.AnimalEmotes[name] then if Config.AnimalEmotesEnabled then CheckAnimalAndOnEmotePlay(RP.AnimalEmotes[name], name) else EmoteChatMessage(Translate('animaldisabled')) end return end if RP.PropEmotes[name] and RP.PropEmotes[name].AnimationOptions.PropTextureVariations then if #args > 1 then local textureVariation = tonumber(args[2]) if (RP.PropEmotes[name].AnimationOptions.PropTextureVariations[textureVariation] ~= nil) then OnEmotePlay(RP.PropEmotes[name], name, textureVariation - 1) return else local str = "" for k, v in ipairs(RP.PropEmotes[name].AnimationOptions.PropTextureVariations) do str = str .. string.format("\n(%s) - %s", k, v.Name) end EmoteChatMessage(string.format(Translate('invalidvariation'), str), true) OnEmotePlay(RP.PropEmotes[name], name, 0) return end end end OnEmotePlay(emote, name) else EmoteChatMessage("'" .. name .. "' " .. Translate('notvalidemote') .. "") end end end function CheckAnimalAndOnEmotePlay(emoteData, 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(emoteData, name) else EmoteChatMessage(Translate('notvalidpet')) end end ---@param isClone boolean | nil 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 function AddProp(prop1, bone, off1, off2, off3, rot1, rot2, rot3, textureVariation, isClone) local target = isClone and ClonedPed or PlayerPedId() local x, y, z = table.unpack(GetEntityCoords(target)) if not IsModelValid(prop1) then DebugPrint(tostring(prop1) .. " is not a valid model!") return false end if not HasModelLoaded(prop1) then LoadPropDict(prop1) end attachedProp = CreateObject(joaat(prop1), x, y, z + 0.2, not isClone, true, true) if textureVariation ~= nil then SetObjectTextureVariation(attachedProp, textureVariation) end if isClone then AttachEntityToEntity(attachedProp, target, GetPedBoneIndex(target, bone), off1, off2, off3, rot1, rot2, rot3, true, true, false, true, 1, true) table.insert(PreviewPedProps, attachedProp) else AttachEntityToEntity(attachedProp, target, GetPedBoneIndex(target, bone), off1, off2, off3, rot1, rot2, rot3, true, true, false, true, 1, true) table.insert(PlayerProps, attachedProp) end SetModelAsNoLongerNeeded(prop1) DebugPrint("Added prop to " .. (isClone and "clone" or "player")) return true end function CheckGender() local playerPed = PlayerPedId() if GetEntityModel(playerPed) == joaat("mp_f_freemode_01") then PlayerGender = "female" else PlayerGender = "male" end DebugPrint("Set gender as = (" .. PlayerGender .. ")") end function OnEmotePlay(emoteData, name, textureVariation) if not LocalPlayer.state.canEmote then return end cleanScenarioObjects(false) InVehicle = IsPedInAnyVehicle(PlayerPedId(), true) Pointing = false if not Config.AllowedInCars and InVehicle then return end if not DoesEntityExist(PlayerPedId()) then return false 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(emoteData, 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 ChosenAnimOptions?.ExitEmote and animOption and animOption.ExitEmote then if not (animOption and ChosenAnimOptions.ExitEmote == animOption.ExitEmote) and RP.Exits[ChosenAnimOptions.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 ChosenAnimOptions = animOption if Config.DisarmPlayer then if IsPedArmed(PlayerPedId(), 7) then SetCurrentPedWeapon(PlayerPedId(), joaat('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 -- Default movement type 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, 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(animOption.Prop, animOption.PropBone, PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6, textureVariation, false) then return end if animOption.SecondProp then SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(animOption.SecondPropPlacement) if not AddProp(animOption.SecondProp, animOption.SecondPropBone, SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6, textureVariation, false) 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(emoteData) 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 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("'" .. ename .. "' " .. Translate('notvalidemote') .. "") return end local movementType = 0 -- Default movement type 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, 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(animOption.Prop, animOption.PropBone, PropPl1, PropPl2, PropPl3, PropPl4, PropPl5, PropPl6, nil, true) then return end if animOption.SecondProp then local SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6 = table.unpack(animOption.SecondPropPlacement) if not AddProp(animOption.SecondProp, animOption.SecondPropBone, SecondPropPl1, SecondPropPl2, SecondPropPl3, SecondPropPl4, SecondPropPl5, SecondPropPl6, nil, true) then DestroyAllProps(true) return end end end end function PlayExitAndEnterEmote(emoteName, 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 ChosenAnimOptions?.ExitEmote then -- If the emote exit type is not spesifed it defaults to Emotes local options = ChosenAnimOptions local ExitEmoteType = options.ExitEmoteType or "Emotes" -- Checks that the exit emote actually exists if not RP[ExitEmoteType] or not RP[ExitEmoteType][options.ExitEmote] then DebugPrint("Exit emote was invalid") ClearPedTasks(ped) IsInAnimation = false return end OnEmotePlay(RP[ExitEmoteType][options.ExitEmote], ExitEmoteType) DebugPrint("Playing exit animation") -- Check that the exit emote has a duration, and if so, set InExitEmote variable local animationOptions = RP[ExitEmoteType][options.ExitEmote].AnimationOptions if animationOptions and animationOptions.EmoteDuration then InExitEmote = true SetTimeout(animationOptions.EmoteDuration, function() InExitEmote = false DestroyAllProps(true) ClearPedTasks(ped) OnEmotePlay(emoteName, name, textureVariation) ExitAndPlay = false end) return end else ClearPedTasks(ped) IsInAnimation = false ExitAndPlay = false DestroyAllProps(true) OnEmotePlay(emoteName, name, CurrentTextureVariation) end end exports("EmoteCommandStart", function(emoteName, textureVariation) EmoteCommandStart(nil, { emoteName, textureVariation }, nil) end) exports("EmoteCancel", EmoteCancel) exports("CanCancelEmote", function(State) CanCancel = State == true end) exports('IsPlayerInAnim', function() return LocalPlayer.state.currentEmote end) exports('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) local emote = RP.Emotes[CurrentAnimationName] or RP.PropEmotes[CurrentAnimationName] or RP.Dances[CurrentAnimationName] or RP.AnimalEmotes[CurrentAnimationName] if not emote then return end ClearPedTasks(PlayerPedId()) DestroyAllProps() OnEmotePlay(emote, 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 local emote = RP.Emotes[CurrentAnimationName] or RP.PropEmotes[CurrentAnimationName] or RP.Dances[CurrentAnimationName] or RP.AnimalEmotes[CurrentAnimationName] if not emote then return end isBumpingPed = false ClearPedTasks(PlayerPedId()) Wait(125) DestroyAllProps() OnEmotePlay(emote, CurrentAnimationName, CurrentTextureVariation) end)