Main/resources/[standalone]/rpemotes-reborn/client/EmoteMenu.lua

522 lines
18 KiB
Lua
Raw Normal View History

2025-06-07 08:51:21 +02:00
local isSearching = false
local rightPosition = { x = 1430, y = 200 }
local leftPosition = { x = 0, y = 200 }
local menuPosition = { x = 0, y = 200 }
local menuHeader = "shopui_title_sm_hangar"
if GetAspectRatio() > 2.0 then
rightPosition = { x = 1200, y = 100 }
leftPosition = { x = -250, y = 100 }
end
if Config.MenuPosition then
if Config.MenuPosition == "left" then
menuPosition = leftPosition
elseif Config.MenuPosition == "right" then
menuPosition = rightPosition
end
end
if Config.CustomMenuEnabled then
local txd = CreateRuntimeTxd('Custom_Menu_Head')
CreateRuntimeTextureFromImage(txd, 'Custom_Menu_Head', 'header.png')
menuHeader = "Custom_Menu_Head"
end
local _menuPool = NativeUI.CreatePool()
local mainMenu = NativeUI.CreateMenu(Config.MenuTitle or "", "", menuPosition["x"], menuPosition["y"], menuHeader, menuHeader)
_menuPool:Add(mainMenu)
2025-06-12 03:36:12 +02:00
local sharemenu, shareddancemenu, infomenu
2025-06-07 08:51:21 +02:00
local EmoteTable = {}
local DanceTable = {}
local AnimalTable = {}
2025-06-12 03:36:12 +02:00
local PropTable = {}
2025-06-07 08:51:21 +02:00
local WalkTable = {}
local FaceTable = {}
local ShareTable = {}
function AddEmoteMenu(menu)
local submenu = _menuPool:AddSubMenu(menu, Translate('emotes'), "", true, true)
if Config.Search then
submenu:AddItem(NativeUI.CreateItem(Translate('searchemotes'), ""))
table.insert(EmoteTable, Translate('searchemotes'))
end
local dancemenu = _menuPool:AddSubMenu(submenu, Translate('danceemotes'), "", true, true)
local animalmenu
if Config.AnimalEmotesEnabled then
animalmenu = _menuPool:AddSubMenu(submenu, Translate('animalemotes'), "", true, true)
table.insert(EmoteTable, Translate('animalemotes'))
end
local propmenu = _menuPool:AddSubMenu(submenu, Translate('propemotes'), "", true, true)
table.insert(EmoteTable, Translate('danceemotes'))
table.insert(EmoteTable, Translate('danceemotes'))
if Config.SharedEmotesEnabled then
sharemenu = _menuPool:AddSubMenu(submenu, Translate('shareemotes'),
Translate('shareemotesinfo'), true, true)
shareddancemenu = _menuPool:AddSubMenu(sharemenu, Translate('sharedanceemotes'), "", true, true)
table.insert(ShareTable, 'none')
table.insert(EmoteTable, Translate('shareemotes'))
end
2025-06-12 03:36:12 +02:00
if Config.Keybinding then
2025-06-07 08:51:21 +02:00
table.insert(EmoteTable, "keybinds")
submenu:AddItem(NativeUI.CreateItem(Translate('keybinds'), Translate('keybindsinfo') .. " /emotebind [~y~num4-9~w~] [~g~emotename~w~]"))
end
for a, b in PairsByKeys(RP.Emotes) do
local x, y, z = table.unpack(b)
submenu:AddItem(NativeUI.CreateItem(z, "/e (" .. a .. ")"))
table.insert(EmoteTable, a)
end
for a, b in PairsByKeys(RP.Dances) do
2025-06-12 03:36:12 +02:00
local name = '🤼 ' .. b[3]
dancemenu:AddItem(NativeUI.CreateItem(name, "/e (" .. a .. ")"))
2025-06-07 08:51:21 +02:00
if Config.SharedEmotesEnabled then
2025-06-12 03:36:12 +02:00
shareddancemenu:AddItem(NativeUI.CreateItem(name, "/nearby (" .. a .. ")"))
2025-06-07 08:51:21 +02:00
end
table.insert(DanceTable, a)
end
if Config.AnimalEmotesEnabled then
for a, b in PairsByKeys(RP.AnimalEmotes) do
2025-06-12 03:36:12 +02:00
local name = '🐶 ' .. b[3]
animalmenu:AddItem(NativeUI.CreateItem(name, "/e (" .. a .. ")"))
2025-06-07 08:51:21 +02:00
table.insert(AnimalTable, a)
end
end
if Config.SharedEmotesEnabled then
for a, b in PairsByKeys(RP.Shared) do
2025-06-12 03:36:12 +02:00
local name = b[3]
local shareitem = NativeUI.CreateItem(name, "/nearby (~g~" .. a .. "~w~)" .. (otheremotename and " " .. Translate('makenearby') .. " (~y~" .. otheremotename .. "~w~)" or ""))
2025-06-07 08:51:21 +02:00
sharemenu:AddItem(shareitem)
table.insert(ShareTable, a)
end
end
for a, b in PairsByKeys(RP.PropEmotes) do
2025-06-12 03:36:12 +02:00
local name = '📦 ' .. b[3]
2025-06-07 08:51:21 +02:00
local propitem = b.AnimationOptions.PropTextureVariations and
2025-06-12 03:36:12 +02:00
NativeUI.CreateListItem(name, b.AnimationOptions.PropTextureVariations, 1, "/e (" .. a .. ")") or
NativeUI.CreateItem(name, "/e (" .. a .. ")")
2025-06-07 08:51:21 +02:00
propmenu:AddItem(propitem)
2025-06-12 03:36:12 +02:00
table.insert(PropTable, a)
2025-06-07 08:51:21 +02:00
end
-- Ped Emote on Change Index
2025-06-12 03:36:12 +02:00
dancemenu.OnIndexChange = function(_, newindex)
2025-06-07 08:51:21 +02:00
ClearPedTaskPreview()
2025-06-12 03:36:12 +02:00
EmoteMenuStartClone(DanceTable[newindex], "Dances")
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
propmenu.OnIndexChange = function(_, newindex)
2025-06-07 08:51:21 +02:00
ClearPedTaskPreview()
2025-06-12 03:36:12 +02:00
EmoteMenuStartClone(PropTable[newindex], "PropEmotes")
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
submenu.OnIndexChange = function(_, newindex)
if newindex > 5 then
2025-06-07 08:51:21 +02:00
ClearPedTaskPreview()
2025-06-12 03:36:12 +02:00
EmoteMenuStartClone(EmoteTable[newindex], "Emotes")
2025-06-07 08:51:21 +02:00
end
end
2025-06-12 03:36:12 +02:00
dancemenu.OnMenuClosed = function()
2025-06-07 08:51:21 +02:00
ClearPedTaskPreview()
end
2025-06-12 03:36:12 +02:00
dancemenu.OnItemSelect = function(_, _, index)
EmoteMenuStart(DanceTable[index], "Dances")
2025-06-07 08:51:21 +02:00
end
if Config.AnimalEmotesEnabled then
2025-06-12 03:36:12 +02:00
animalmenu.OnItemSelect = function(_, _, index)
EmoteMenuStart(AnimalTable[index], "AnimalEmotes")
2025-06-07 08:51:21 +02:00
end
end
if Config.SharedEmotesEnabled then
2025-06-12 03:36:12 +02:00
sharemenu.OnItemSelect = function(_, _, index)
2025-06-07 08:51:21 +02:00
if ShareTable[index] ~= 'none' then
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
2025-06-12 03:36:12 +02:00
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), ShareTable[index])
2025-06-07 08:51:21 +02:00
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
end
end
2025-06-12 03:36:12 +02:00
shareddancemenu.OnItemSelect = function(_, _, index)
2025-06-07 08:51:21 +02:00
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
2025-06-12 03:36:12 +02:00
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), DanceTable[index], 'Dances')
2025-06-07 08:51:21 +02:00
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
end
end
2025-06-12 03:36:12 +02:00
propmenu.OnItemSelect = function(_, _, index)
EmoteMenuStart(PropTable[index], "PropEmotes")
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
propmenu.OnListSelect = function(_, item, itemIndex, listIndex)
EmoteMenuStart(PropTable[itemIndex], "PropEmotes", item:IndexToItem(listIndex).Value)
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
submenu.OnItemSelect = function(_, _, index)
2025-06-07 08:51:21 +02:00
if Config.Search and EmoteTable[index] == Translate('searchemotes') then
EmoteMenuSearch(submenu)
2025-06-12 03:36:12 +02:00
else
EmoteMenuStart(EmoteTable[index], "Emotes")
2025-06-07 08:51:21 +02:00
end
end
2025-06-12 03:36:12 +02:00
submenu.OnMenuClosed = function()
2025-06-07 08:51:21 +02:00
if not isSearching then
ClosePedMenu()
end
end
end
if Config.Search then
local ignoredCategories = {
["Walks"] = true,
["Expressions"] = true,
["Shared"] = not Config.SharedEmotesEnabled
}
function EmoteMenuSearch(lastMenu)
ClosePedMenu()
AddTextEntry("PM_NAME_CHALL", Translate('searchinputtitle'))
DisplayOnscreenKeyboard(1, "PM_NAME_CHALL", "", "", "", "", "", 30)
while UpdateOnscreenKeyboard() == 0 do
DisableAllControlActions(0)
Wait(100)
end
local input = GetOnscreenKeyboardResult()
if input ~= nil then
local results = {}
2025-06-12 03:36:12 +02:00
for a, b in pairs(RP) do
if not ignoredCategories[b.category] then
if string.find(string.lower(a), string.lower(input)) or (b[3] ~= nil and string.find(string.lower(b[3]), string.lower(input))) then
table.insert(results, { table = b.category, name = a, data = b })
2025-06-07 08:51:21 +02:00
end
end
end
if #results > 0 then
isSearching = true
local searchMenu = _menuPool:AddSubMenu(lastMenu, string.format('%s '..Translate('searchmenudesc')..' ~r~%s~w~', #results, input), "", true, true)
local sharedDanceMenu
if Config.SharedEmotesEnabled then
sharedDanceMenu = _menuPool:AddSubMenu(searchMenu, Translate('sharedanceemotes'), "", true, true)
end
table.sort(results, function(a, b) return a.name < b.name end)
for k, v in pairs(results) do
local desc = ""
if v.table == "Shared" then
local otheremotename = v.data[4]
if otheremotename == nil then
desc = "/nearby (~g~" .. v.name .. "~w~)"
else
desc = "/nearby (~g~" .. v.name .. "~w~) " .. Translate('makenearby') .. " (~y~" .. otheremotename .. "~w~)"
end
else
2025-06-12 03:36:12 +02:00
desc = "/e (" .. v.name .. ")"
2025-06-07 08:51:21 +02:00
end
if v.data.AnimationOptions and v.data.AnimationOptions.PropTextureVariations then
searchMenu:AddItem(NativeUI.CreateListItem(v.data[3], v.data.AnimationOptions.PropTextureVariations, 1, desc))
else
searchMenu:AddItem(NativeUI.CreateItem(v.data[3], desc))
end
if v.table == "Dances" and Config.SharedEmotesEnabled then
sharedDanceMenu:AddItem(NativeUI.CreateItem(v.data[3], ""))
end
end
2025-06-12 03:36:12 +02:00
searchMenu.OnMenuChanged = function()
2025-06-07 08:51:21 +02:00
isSearching = false
ShowPedMenu()
end
2025-06-12 03:36:12 +02:00
searchMenu.OnIndexChange = function(_, newindex)
2025-06-07 08:51:21 +02:00
local data = results[newindex]
ClearPedTaskPreview()
2025-06-12 03:36:12 +02:00
EmoteMenuStartClone(data.name, data.data.category)
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
searchMenu.OnItemSelect = function(_, _, index)
2025-06-07 08:51:21 +02:00
local data = results[index]
if data == Translate('sharedanceemotes') then return end
2025-06-12 03:36:12 +02:00
if data.table == "Shared" then
2025-06-07 08:51:21 +02:00
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
2025-06-12 03:36:12 +02:00
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), data.name)
2025-06-07 08:51:21 +02:00
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
2025-06-12 03:36:12 +02:00
else
EmoteMenuStart(data.name, data.data.category)
2025-06-07 08:51:21 +02:00
end
end
2025-06-12 03:36:12 +02:00
searchMenu.OnListSelect = function(_, item, itemIndex, listIndex)
EmoteMenuStart(results[itemIndex].name, "PropEmotes", item:IndexToItem(listIndex).Value)
2025-06-07 08:51:21 +02:00
end
if Config.SharedEmotesEnabled then
if #sharedDanceMenu.Items > 0 then
2025-06-12 03:36:12 +02:00
table.insert(results, 1, Translate('sharedanceemotes'))
sharedDanceMenu.OnItemSelect = function(_, _, index)
2025-06-07 08:51:21 +02:00
if not LocalPlayer.state.canEmote then return end
local data = results[index]
local target, distance = GetClosestPlayer()
if (distance ~= -1 and distance < 3) then
2025-06-12 03:36:12 +02:00
TriggerServerEvent("rpemotes:server:requestEmote", GetPlayerServerId(target), data.name, 'Dances')
2025-06-07 08:51:21 +02:00
SimpleNotify(Translate('sentrequestto') .. GetPlayerName(target))
else
SimpleNotify(Translate('nobodyclose'))
end
end
else
sharedDanceMenu:Clear()
2025-06-12 03:36:12 +02:00
searchMenu:RemoveItemAt(1)
2025-06-07 08:51:21 +02:00
end
end
searchMenu.OnMenuClosed = function()
searchMenu:Clear()
lastMenu:RemoveItemAt(#lastMenu.Items)
_menuPool:RefreshIndex()
results = {}
end
_menuPool:RefreshIndex()
_menuPool:CloseAllMenus()
searchMenu:Visible(true)
ShowPedMenu()
else
SimpleNotify(string.format(Translate('searchnoresult')..' ~r~%s~w~', input))
end
end
end
end
function AddCancelEmote(menu)
local newitem = NativeUI.CreateItem(Translate('cancelemote'), Translate('cancelemoteinfo'))
menu:AddItem(newitem)
newitem.Activated = function()
EmoteCancel()
DestroyAllProps()
end
end
ShowPedPreview = function(menu)
2025-06-12 03:36:12 +02:00
menu.OnItemSelect = function(_, _, index)
if index == 1 then
2025-06-07 08:51:21 +02:00
isSearching = false
ShowPedMenu()
elseif index == 4 then
ShowPedMenu(true)
end
end
end
function AddWalkMenu(menu)
local submenu = _menuPool:AddSubMenu(menu, Translate('walkingstyles'), "", true, true)
local walkreset = NativeUI.CreateItem(Translate('normalreset'), Translate('resetdef'))
submenu:AddItem(walkreset)
table.insert(WalkTable, Translate('resetdef'))
local sortedWalks = {}
for a, b in PairsByKeys(RP.Walks) do
2025-06-12 03:36:12 +02:00
if b[1] == "move_m@injured" then
table.insert(sortedWalks, 1, {label = a, anim = b[1]})
2025-06-07 08:51:21 +02:00
else
2025-06-12 03:36:12 +02:00
table.insert(sortedWalks, {label = a, anim = b[1]})
2025-06-07 08:51:21 +02:00
end
end
for _, walk in ipairs(sortedWalks) do
submenu:AddItem(NativeUI.CreateItem(walk.label, "/walk (" .. string.lower(walk.label) .. ")"))
2025-06-12 03:36:12 +02:00
table.insert(WalkTable, walk.label)
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
submenu.OnItemSelect = function(_, item, index)
2025-06-07 08:51:21 +02:00
if item == walkreset then
ResetWalk()
DeleteResourceKvp("walkstyle")
else
WalkMenuStart(WalkTable[index])
end
end
end
function AddFaceMenu(menu)
local submenu = _menuPool:AddSubMenu(menu, Translate('moods'), "", true, true)
local facereset = NativeUI.CreateItem(Translate('normalreset'), Translate('resetdef'))
submenu:AddItem(facereset)
table.insert(FaceTable, "")
for name, data in PairsByKeys(RP.Expressions) do
local faceitem = NativeUI.CreateItem(data[2] or name, "")
submenu:AddItem(faceitem)
table.insert(FaceTable, name)
end
2025-06-12 03:36:12 +02:00
submenu.OnIndexChange = function(_, newindex)
EmoteMenuStartClone(FaceTable[newindex], "Expressions")
2025-06-07 08:51:21 +02:00
end
2025-06-12 03:36:12 +02:00
submenu.OnItemSelect = function(_, item, index)
if item == facereset then
DeleteResourceKvp("Expressions")
2025-06-07 08:51:21 +02:00
ClearFacialIdleAnimOverride(PlayerPedId())
2025-06-12 03:36:12 +02:00
else
EmoteMenuStart(FaceTable[index], "Expressions")
2025-06-07 08:51:21 +02:00
end
end
2025-06-12 03:36:12 +02:00
submenu.OnMenuClosed = function()
ClosePedMenu()
end
2025-06-07 08:51:21 +02:00
end
function AddInfoMenu(menu)
2025-06-12 03:36:12 +02:00
infomenu = _menuPool:AddSubMenu(menu, Translate('infoupdate'), "~h~~y~The RPEmotes Developers~h~~y~", true, true)
2025-06-07 08:51:21 +02:00
for _,v in ipairs(Config.Credits) do
local item = NativeUI.CreateItem(v.title,v.subtitle or "")
infomenu:AddItem(item)
end
end
function OpenEmoteMenu()
if IsEntityDead(PlayerPedId()) then
-- show in chat
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
-- show in chat
TriggerEvent('chat:addMessage', {
color = {255, 0, 0},
multiline = true,
args = {"RPEmotes", Translate('swimming')}
})
return
end
if _menuPool:IsAnyMenuOpen() then
_menuPool:CloseAllMenus()
else
mainMenu:Visible(true)
ProcessMenu()
end
end
2025-06-12 03:36:12 +02:00
CreateThread(function()
LoadAddonEmotes()
AddEmoteMenu(mainMenu)
AddCancelEmote(mainMenu)
if Config.PreviewPed then
ShowPedPreview(mainMenu)
end
if Config.WalkingStylesEnabled then
AddWalkMenu(mainMenu)
end
if Config.ExpressionsEnabled then
AddFaceMenu(mainMenu)
end
AddInfoMenu(mainMenu)
_menuPool:RefreshIndex()
2025-06-07 08:51:21 +02:00
2025-06-12 03:36:12 +02:00
local newRP = {}
for emoteType, content in pairs(RP) do
for emoteName, emoteData in pairs(content) do
local shouldRemove = false
if Config.AdultEmotesDisabled and emoteData.AdultAnimation then
shouldRemove = true
end
if newRP[emoteName] then
print('WARNING - Duplicate emote name found: ' .. emoteName .. ' in ' .. emoteType .. ' and ' .. newRP[emoteName].category)
end
if shouldRemove then
elseif type(emoteData) == "table" then
newRP[emoteName] = {}
for k, v in pairs(emoteData) do
newRP[emoteName][k] = v
end
newRP[emoteName].category = emoteType
else
newRP[emoteName] = { emoteData }
newRP[emoteName].category = emoteType
end
end
newRP[emoteType] = nil
end
RP = newRP
CONVERTED = true
end)
2025-06-07 08:51:21 +02:00
local isMenuProcessing = false
function ProcessMenu()
if isMenuProcessing then return end
isMenuProcessing = true
while _menuPool:IsAnyMenuOpen() do
_menuPool:ProcessMenus()
Wait(0)
end
isMenuProcessing = false
end
-- While ped is dead, don't show menus
CreateThread(function()
while true do
Wait(500)
if IsEntityDead(PlayerPedId()) then
_menuPool:CloseAllMenus()
end
if (IsPedSwimming(PlayerPedId()) or IsPedSwimmingUnderWater(PlayerPedId())) and not Config.AllowInWater then
-- cancel emote, destroy props and close menu
if IsInAnimation then
EmoteCancel()
end
_menuPool:CloseAllMenus()
end
end
end)