504 lines
17 KiB
Lua
504 lines
17 KiB
Lua
-- CONFIG --
|
|
|
|
local config_select = true -- true = select the target, false = auto target the player in front
|
|
|
|
-- END OF CONFIG --
|
|
|
|
local dogBreeds = { 'Rottweiler', 'Husky', 'Retriever', 'Shepherd' }
|
|
local dogBHash = { 'a_c_rottweiler', 'a_c_husky', 'a_c_retriever', 'a_c_shepherd' }
|
|
|
|
local k9 = nil
|
|
local k9Name = nil
|
|
|
|
local blipk9 = nil
|
|
|
|
local selectedDogIndex = 1
|
|
local currentDogIndex = 1
|
|
|
|
local open = false
|
|
local main = RageUI.CreateMenu("K9 Manager", "Actions")
|
|
main.Closed = function()
|
|
open = false
|
|
end
|
|
|
|
function openK9Menu()
|
|
if open then
|
|
open = false
|
|
RageUI.Visible(main, false)
|
|
return
|
|
else
|
|
open = true
|
|
RageUI.Visible(main, true)
|
|
Citizen.CreateThread(function()
|
|
while open do
|
|
RageUI.IsVisible(main, function()
|
|
if k9 == nil then
|
|
-- create a button to rename the dog
|
|
RageUI.Button("Dog's name", nil, { RightLabel = k9Name }, true, {
|
|
onSelected = function()
|
|
local result = KeyboardInput("Dog's name")
|
|
if result ~= nil then
|
|
k9Name = result
|
|
end
|
|
end,
|
|
})
|
|
-- create a button to select the dog breed
|
|
RageUI.List("Dog breed", dogBreeds, selectedDogIndex, nil, {}, true, {
|
|
onListChange = function(Index)
|
|
selectedDogIndex = Index
|
|
currentDogIndex = Index
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Spawn the dog", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
if k9Name == nil then
|
|
ShowNotification("You have to name your dog first!")
|
|
else
|
|
-- Spawning
|
|
RequestModel(GetHashKey(dogBHash[currentDogIndex]))
|
|
while not HasModelLoaded(GetHashKey(dogBHash[currentDogIndex])) do
|
|
Citizen.Wait(1)
|
|
end
|
|
|
|
local pos = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, 2.0, 0.0)
|
|
local heading = GetEntityHeading(GetPlayerPed(-1))
|
|
local _, groundZ = GetGroundZFor_3dCoord(pos.x, pos.y, pos.z, false);
|
|
|
|
k9 = CreatePed(28, GetHashKey(dogBHash[currentDogIndex]), pos.x, pos.y, groundZ + 1, heading, true, true)
|
|
|
|
GiveWeaponToPed(k9, GetHashKey('WEAPON_ANIMAL'), true, true)
|
|
TaskSetBlockingOfNonTemporaryEvents(k9, true)
|
|
SetPedFleeAttributes(k9, 0, false)
|
|
SetPedCombatAttributes(k9, 3, true)
|
|
SetPedCombatAttributes(k9, 5, true)
|
|
SetPedCombatAttributes(k9, 46, true)
|
|
-- make it not attack the owner in any condition
|
|
SetPedAsGroupLeader(k9, GetPedGroupIndex(PlayerPedId()))
|
|
SetPedAsGroupMember(k9, GetPedGroupIndex(PlayerPedId()))
|
|
SetPedNeverLeavesGroup(k9, true)
|
|
|
|
|
|
blipk9 = AddBlipForEntity(k9)
|
|
SetBlipAsFriendly(blipk9, true)
|
|
SetBlipDisplay(blipk9, 2)
|
|
SetBlipShowCone(blipk9, true)
|
|
SetBlipAsShortRange(blipk9, false)
|
|
|
|
BeginTextCommandSetBlipName("STRING")
|
|
AddTextComponentString(k9Name)
|
|
EndTextCommandSetBlipName(blipk9)
|
|
|
|
Command_Follow(k9)
|
|
|
|
end
|
|
end,
|
|
})
|
|
|
|
else
|
|
if IsPedDeadOrDying(k9, true) then
|
|
ShowNotification(k9Name .. " was killed!")
|
|
k9 = nil
|
|
k9Name = nil
|
|
RemoveBlip(blipk9)
|
|
blipk9 = nil
|
|
end
|
|
|
|
RageUI.Button("Sit", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_Sit(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Follow / Call", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_Follow(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Don't move", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_Stay(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Bark", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_Bark(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Lay down", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_Lay(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Beg", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_beg(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Give paw", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
Command_paw(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Attack", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
if isAttacking then
|
|
isAttacking = false
|
|
ClearPedTasksImmediately(k9)
|
|
else
|
|
if config_select then
|
|
select_and_attackK9(k9)
|
|
else
|
|
attackK9(k9)
|
|
end
|
|
end
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Enter the car", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
EnterVehicle(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Exit the car", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
ExitVehicle(k9)
|
|
end,
|
|
})
|
|
|
|
RageUI.Button("Dismiss the dog", nil, { RightLabel = "→" }, true, {
|
|
onSelected = function()
|
|
DismissDog(k9)
|
|
end,
|
|
})
|
|
end
|
|
end)
|
|
Wait(1)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
Keys.Register('K', 'K', 'Menu K9', function()
|
|
openK9Menu()
|
|
end)
|
|
|
|
local isAttacking = false
|
|
function attackK9(ped)
|
|
DetachEntity(ped)
|
|
|
|
if IsPlayerFreeAiming(PlayerId()) then
|
|
local _, target = GetEntityPlayerIsFreeAimingAt(PlayerId())
|
|
ClearPedTasks(ped)
|
|
if IsEntityAPed(target) and target ~= PlayerPedId() then
|
|
isAttacking = true
|
|
TaskCombatPed(ped, target, 0, 16)
|
|
CreateThread(function()
|
|
while isAttacking and not IsPedDeadOrDying(target, true) do
|
|
SetPedMoveRateOverride(ped, 1.25)
|
|
Citizen.Wait(0)
|
|
end
|
|
end)
|
|
end
|
|
else
|
|
local target = GetPedInFront()
|
|
ClearPedTasks(ped)
|
|
if IsEntityAPed(target) and target ~= PlayerPedId() then
|
|
isAttacking = true
|
|
TaskCombatPed(ped, target, 0, 16)
|
|
|
|
CreateThread(function()
|
|
while isAttacking and not IsPedDeadOrDying(target, true) do
|
|
SetPedMoveRateOverride(ped, 1.25)
|
|
Citizen.Wait(0)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
function select_and_attackK9(ped)
|
|
DetachEntity(ped)
|
|
local target = nil
|
|
|
|
local player = GetAllPlayersInArea(GetEntityCoords(ped), 5.0)
|
|
for k, v in pairs(player) do
|
|
if v == PlayerId() then
|
|
table.remove(player, k)
|
|
end
|
|
end
|
|
|
|
if player ~= nil then
|
|
if next(player) then
|
|
inChoice = true
|
|
StartChoicePlayerK9(player)
|
|
if selectedPlayer ~= nil then
|
|
target = GetPlayerPed(selectedPlayer)
|
|
end
|
|
end
|
|
else
|
|
ShowNotification("No player nearby")
|
|
end
|
|
|
|
if target == nil then
|
|
ShowNotification("No player selected")
|
|
return
|
|
end
|
|
ClearPedTasks(ped)
|
|
|
|
if IsEntityAPed(target) and target ~= PlayerPedId() then
|
|
isAttacking = true
|
|
TaskCombatPed(ped, target, 0, 16)
|
|
|
|
CreateThread(function()
|
|
while isAttacking and not IsPedDeadOrDying(target, true) do
|
|
SetPedMoveRateOverride(ped, 1.25)
|
|
Citizen.Wait(0)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
function Command_Sit(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
RequestAnimDict("creatures@rottweiler@amb@world_dog_sitting@idle_a")
|
|
while not HasAnimDictLoaded("creatures@rottweiler@amb@world_dog_sitting@idle_a") do
|
|
Citizen.Wait(1)
|
|
end
|
|
TaskPlayAnim(ped, "creatures@rottweiler@amb@world_dog_sitting@idle_a", "idle_b", 8.0, -4.0, -1, 1, 0.0)
|
|
|
|
end
|
|
|
|
function Command_Stay(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
RequestAnimDict("amb@lo_res_idles@")
|
|
while not HasAnimDictLoaded("amb@lo_res_idles@") do
|
|
Citizen.Wait(1)
|
|
end
|
|
TaskPlayAnim(ped, "amb@lo_res_idles@", "creatures_world_rottweiler_standing_lo_res_base", 8.0, -4.0, -1, 1, 0.0)
|
|
|
|
end
|
|
|
|
function Command_paw(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
RequestAnimDict("creatures@rottweiler@tricks@")
|
|
while not HasAnimDictLoaded("creatures@rottweiler@tricks@") do
|
|
Citizen.Wait(1)
|
|
end
|
|
TaskPlayAnim(ped, "creatures@rottweiler@tricks@", "paw_right_loop", 8.0, -4.0, -1, 1, 0.0)
|
|
|
|
end
|
|
|
|
function Command_beg(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
RequestAnimDict("creatures@rottweiler@tricks@")
|
|
while not HasAnimDictLoaded("creatures@rottweiler@tricks@") do
|
|
Citizen.Wait(1)
|
|
end
|
|
TaskPlayAnim(ped, "creatures@rottweiler@tricks@", "beg_loop", 8.0, -4.0, -1, 1, 0.0)
|
|
|
|
end
|
|
|
|
function Command_Follow(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
DetachEntity(ped)
|
|
|
|
TaskFollowToOffsetOfEntity(ped, GetPlayerPed(-1), 0.5, 0.0, 0.0, 7.0, -1, 0.2, true)
|
|
|
|
end
|
|
|
|
function Command_Bark(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
RequestAnimDict("creatures@rottweiler@amb@world_dog_barking@idle_a")
|
|
while not HasAnimDictLoaded("creatures@rottweiler@amb@world_dog_barking@idle_a") do
|
|
Citizen.Wait(1)
|
|
end
|
|
TaskPlayAnim(ped, "creatures@rottweiler@amb@world_dog_barking@idle_a", "idle_a", 8.0, -4.0, -1, 1, 0.0)
|
|
|
|
end
|
|
|
|
function Command_Lay(ped)
|
|
ClearPedTasks(ped)
|
|
|
|
RequestAnimDict("creatures@rottweiler@amb@sleep_in_kennel@")
|
|
while not HasAnimDictLoaded("creatures@rottweiler@amb@sleep_in_kennel@") do
|
|
Citizen.Wait(1)
|
|
end
|
|
TaskPlayAnim(ped, "creatures@rottweiler@amb@sleep_in_kennel@", "sleep_in_kennel", 8.0, -4.0, -1, 1, 0.0)
|
|
|
|
end
|
|
|
|
function Command_StartTrack(dog, player)
|
|
|
|
local target = GetPlayerPed(GetPlayerFromServerId(tonumber(player)))
|
|
|
|
TaskFollowToOffsetOfEntity(dog, target, 0.5, 0.0, 0.0, 6.0, -1, 0.2, true)
|
|
|
|
end
|
|
|
|
function EnterVehicle(ped)
|
|
|
|
if IsPedInAnyVehicle(PlayerPedId(), false) then
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
|
|
local vehHeading = GetEntityHeading(vehicle)
|
|
|
|
TaskGoToEntity(ped, vehicle, -1, 0.5, 100, 1073741824, 0)
|
|
TaskAchieveHeading(ped, vehHeading, -1)
|
|
|
|
RequestAnimDict("creatures@rottweiler@in_vehicle@van")
|
|
RequestAnimDict("creatures@rottweiler@amb@world_dog_sitting@base")
|
|
|
|
while not HasAnimDictLoaded("creatures@rottweiler@in_vehicle@van") or
|
|
not HasAnimDictLoaded("creatures@rottweiler@amb@world_dog_sitting@base") do
|
|
Citizen.Wait(1)
|
|
end
|
|
|
|
TaskPlayAnim(ped, "creatures@rottweiler@in_vehicle@van", "get_in", 8.0, -4.0, -1, 2, 0.0)
|
|
Citizen.Wait(700)
|
|
ClearPedTasks(ped)
|
|
AttachEntityToEntity(ped, vehicle, GetEntityBoneIndexByName(vehicle, "seat_pside_r"), 0.0, 0.0, 0.25)
|
|
TaskPlayAnim(ped, "creatures@rottweiler@amb@world_dog_sitting@base", "base", 8.0, -4.0, -1, 2, 0.0)
|
|
|
|
else
|
|
ShowNotification("You have to be in a vehicle to do that!")
|
|
end
|
|
|
|
end
|
|
|
|
function ExitVehicle(ped)
|
|
|
|
local vehicle = GetEntityAttachedTo(ped)
|
|
local vehPos = GetEntityCoords(vehicle)
|
|
local forwardX = GetEntityForwardVector(vehicle).x * 3.7
|
|
local forwardY = GetEntityForwardVector(vehicle).y * 3.7
|
|
local _, groundZ = GetGroundZFor_3dCoord(vehPos.x, vehPos.y, vehPos.z, 0)
|
|
|
|
ClearPedTasks(ped)
|
|
DetachEntity(ped)
|
|
|
|
SetEntityCoords(ped, vehPos.x - forwardX, vehPos.y - forwardY, groundZ)
|
|
|
|
Command_Follow(ped)
|
|
|
|
end
|
|
|
|
function DismissDog(ped)
|
|
|
|
ClearPedTasks(ped)
|
|
|
|
DeletePed(ped)
|
|
|
|
blipk9 = nil
|
|
k9 = nil
|
|
k9Name = nil
|
|
RemoveBlip(blipk9)
|
|
|
|
end
|
|
|
|
function GetPedInFront()
|
|
local player = PlayerId()
|
|
local plyPed = GetPlayerPed(player)
|
|
local plyPos = GetEntityCoords(plyPed, false)
|
|
local plyOffset = GetOffsetFromEntityInWorldCoords(plyPed, 0.0, 5.0, 0.0)
|
|
local rayHandle = StartShapeTestCapsule(plyPos.x, plyPos.y, plyPos.z, plyOffset.x, plyOffset.y, plyOffset.z, 1.0, 12
|
|
, plyPed, 7)
|
|
local _, _, _, _, ped = GetShapeTestResult(rayHandle)
|
|
return ped
|
|
end
|
|
|
|
function StartChoicePlayerK9(players)
|
|
selectedPlayer = nil
|
|
ShowNotification("Press ~g~E~s~ to confirm\nPress ~b~L~s~ to change target\nPress ~r~X~s~ to cancel")
|
|
local timer = GetGameTimer() + 10000
|
|
while inChoice do
|
|
if next(players) then
|
|
local mCoors = GetEntityCoords(GetPlayerPed(players[1]))
|
|
DrawMarker(20, mCoors.x, mCoors.y, mCoors.z + 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.3, 0.3, 0.3, 255, 255,
|
|
255, 120, 0, 1, 2, 0, nil, nil, 0)
|
|
if GetGameTimer() > timer then
|
|
ShowNotification("~r~Timeout was reached")
|
|
inChoice = false
|
|
return
|
|
elseif IsControlJustPressed(0, 51) then -- E
|
|
selectedPlayer = players[1]
|
|
inChoice = false
|
|
return
|
|
elseif IsControlJustPressed(0, 182) then -- L
|
|
table.remove(players, 1)
|
|
if next(players) then
|
|
timer = GetGameTimer() + 10000
|
|
end
|
|
elseif IsControlJustPressed(0, 73) then -- X
|
|
ShowNotification("~r~You canceled the choice")
|
|
selectedPlayer = nil
|
|
inChoice = false
|
|
return
|
|
end
|
|
else
|
|
ShowNotification("~r~No players found")
|
|
selectedPlayer = nil
|
|
inChoice = false
|
|
return
|
|
end
|
|
Wait(0)
|
|
end
|
|
end
|
|
|
|
function KeyboardInput(text)
|
|
local result = nil
|
|
AddTextEntry("CUSTOM_AMOUNT", text)
|
|
DisplayOnscreenKeyboard(1, "CUSTOM_AMOUNT", '', "", '', '', '', 255)
|
|
while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do
|
|
Wait(1)
|
|
end
|
|
if UpdateOnscreenKeyboard() ~= 2 then
|
|
result = GetOnscreenKeyboardResult()
|
|
Citizen.Wait(1)
|
|
else
|
|
Citizen.Wait(1)
|
|
end
|
|
return result
|
|
end
|
|
|
|
function ShowNotification(text)
|
|
AddTextEntry('core:notif', text)
|
|
BeginTextCommandThefeedPost('core:notif')
|
|
AddTextComponentSubstringPlayerName(text)
|
|
EndTextCommandThefeedPostTicker(true, true)
|
|
end
|
|
|
|
function GetAllPlayersInArea(coords, zone)
|
|
local playersInArea = {}
|
|
if zone == nil then
|
|
zone = 150.0
|
|
end
|
|
for k, v in pairs(GetActivePlayers()) do
|
|
local pPed = GetPlayerPed(v)
|
|
local pCoords = GetEntityCoords(pPed)
|
|
if GetDistanceBetweenCoords(pCoords, coords, false) <= zone then
|
|
table.insert(playersInArea, v)
|
|
end
|
|
end
|
|
return playersInArea
|
|
end |