1
0
Fork 0
forked from Simnation/Main
Main/resources/[standalone]/rpemotes-reborn-1.8.5/client/Emote.lua

1154 lines
38 KiB
Lua
Raw Normal View History

2025-06-07 08:51:21 +02:00
-- 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)