diff --git a/README.md b/README.md index 2aa60f4..fe56daf 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Made by Avilchiis for the community, you can download it [here](https://github.c * Open Stash * Open Trunk * Play Sound +* Player Control * Refuel Vehicle * Remove Money * Remove Stress diff --git a/client/controlplayer.lua b/client/controlplayer.lua new file mode 100644 index 0000000..65bae7d --- /dev/null +++ b/client/controlplayer.lua @@ -0,0 +1,280 @@ +local isControlling = false +local targetServerId = nil +local savedCoords = nil + +local isBeingControlled = false +local currentMoveState = nil + +local WALK_SPEED = 1.0 +local RUN_SPEED = 2.0 +local MOVE_DISTANCE = 2.0 + +local function stopControl() + if not isControlling then return end + + isControlling = false + + local myPed = PlayerPedId() + + NetworkSetInSpectatorMode(false, myPed) + SetMinimapInSpectatorMode(false, myPed) + + SetEntityVisible(myPed, true, false) + SetEntityAlpha(myPed, 255, false) + SetEntityCollision(myPed, true, true) + + DoScreenFadeOut(300) + Wait(400) + + if savedCoords then + SetEntityCoords(myPed, savedCoords.x, savedCoords.y, savedCoords.z, false, false, false, false) + savedCoords = nil + end + + targetServerId = nil + + DoScreenFadeIn(500) + + TriggerServerEvent('ps-adminmenu:server:stopControl') +end + +local function startControl(target) + local targetPlayer = GetPlayerFromServerId(target) + + if targetPlayer == -1 then + TriggerEvent('chat:addMessage', { args = { '^1ERROR', 'Player is not online.' } }) + return + end + + if target == GetPlayerServerId(PlayerId()) then + TriggerEvent('chat:addMessage', { args = { '^1ERROR', 'You cannot control yourself.' } }) + return + end + + local targetPed = GetPlayerPed(targetPlayer) + if not DoesEntityExist(targetPed) then + TriggerEvent('chat:addMessage', { args = { '^1ERROR', 'Player is not online.' } }) + return + end + + local myPed = PlayerPedId() + savedCoords = GetEntityCoords(myPed) + + DoScreenFadeOut(300) + Wait(400) + + SetEntityVisible(myPed, false, false) + SetEntityAlpha(myPed, 0, false) + SetEntityCollision(myPed, false, false) + + local targetCoords = GetEntityCoords(targetPed) + SetEntityCoords(myPed, targetCoords.x, targetCoords.y, targetCoords.z + 1.0, false, false, false, false) + Wait(200) + + NetworkSetInSpectatorMode(true, targetPed) + SetMinimapInSpectatorMode(true, targetPed) + + isControlling = true + targetServerId = target + + DoScreenFadeIn(500) + + CreateThread(function() + local lastSend = 0 + local SEND_INTERVAL = 50 + + while isControlling do + Wait(0) + + local tPlayer = GetPlayerFromServerId(targetServerId) + if tPlayer == -1 then + stopControl() + break + end + local tPed = GetPlayerPed(tPlayer) + if not DoesEntityExist(tPed) then + stopControl() + break + end + + local forward = IsDisabledControlPressed(0, 32) + local backward = IsDisabledControlPressed(0, 33) + local left = IsDisabledControlPressed(0, 34) + local right = IsDisabledControlPressed(0, 35) + local sprint = IsDisabledControlPressed(0, 21) + local jump = IsDisabledControlJustPressed(0, 22) + + local isMoving = forward or backward or left or right + + local camRot = GetGameplayCamRot(2) + local camHeading = camRot.z + + local now = GetGameTimer() + if now - lastSend >= SEND_INTERVAL then + lastSend = now + + TriggerServerEvent('ps-adminmenu:server:sendMovement', { + forward = forward, + backward = backward, + left = left, + right = right, + sprint = sprint, + jump = jump, + heading = camHeading, + moving = isMoving + }) + end + end + end) +end + +local function calculateMoveDirection(heading, forward, backward, left, right) + local angle = math.rad(-heading) + local dirX, dirY = 0.0, 0.0 + + if forward then + dirX = dirX + math.sin(angle) + dirY = dirY + math.cos(angle) + end + if backward then + dirX = dirX - math.sin(angle) + dirY = dirY - math.cos(angle) + end + if left then + dirX = dirX + math.cos(angle) + dirY = dirY - math.sin(angle) + end + if right then + dirX = dirX - math.cos(angle) + dirY = dirY + math.sin(angle) + end + + local len = math.sqrt(dirX * dirX + dirY * dirY) + if len > 0 then + dirX = dirX / len + dirY = dirY / len + end + + return dirX, dirY +end + +CreateThread(function() + while true do + Wait(0) + + if isBeingControlled and currentMoveState then + local ped = PlayerPedId() + local state = currentMoveState + + if state.moving then + local coords = GetEntityCoords(ped) + local dirX, dirY = calculateMoveDirection( + state.heading, state.forward, state.backward, state.left, state.right + ) + + local speed = state.sprint and RUN_SPEED or WALK_SPEED + local targetX = coords.x + dirX * MOVE_DISTANCE + local targetY = coords.y + dirY * MOVE_DISTANCE + local targetZ = coords.z + + local moveHeading = math.deg(math.atan(dirX, dirY)) + if moveHeading < 0 then moveHeading = moveHeading + 360.0 end + + TaskGoStraightToCoord(ped, targetX, targetY, targetZ, speed, -1, moveHeading, 0.5) + else + if not IsPedStill(ped) then + ClearPedTasks(ped) + end + end + + if state.jump then + TaskJump(ped, true) + end + end + end +end) + +RegisterNetEvent('ps-adminmenu:client:receiveMovement', function(moveState) + currentMoveState = moveState +end) + +RegisterNetEvent('ps-adminmenu:client:youAreControlled', function(state) + isBeingControlled = state + local ped = PlayerPedId() + + if state then + else + ClearPedTasks(ped) + currentMoveState = nil + end +end) + +CreateThread(function() + while true do + Wait(0) + if isBeingControlled then + DisableControlAction(0, 30, true) + DisableControlAction(0, 31, true) + DisableControlAction(0, 21, true) + DisableControlAction(0, 22, true) + DisableControlAction(0, 23, true) + DisableControlAction(0, 36, true) + DisableControlAction(0, 44, true) + DisableControlAction(0, 37, true) + DisableControlAction(0, 24, true) + DisableControlAction(0, 25, true) + DisableControlAction(0, 140, true) + DisableControlAction(0, 141, true) + DisableControlAction(0, 142, true) + DisableControlAction(0, 257, true) + DisableControlAction(0, 263, true) + DisableControlAction(0, 264, true) + end + end +end) + +RegisterNetEvent('ps-adminmenu:client:startControl', function(target) + startControl(target) +end) + +RegisterNetEvent('ps-adminmenu:client:stopControl', function() + stopControl() +end) + +RegisterCommand('controlplayer', function(source, args) + if isControlling then + stopControl() + return + end + + local target = tonumber(args[1]) + if not target then + TriggerEvent('chat:addMessage', { args = { '^3INFO', '/controlplayer [player id]' } }) + return + end + + TriggerServerEvent('ps-adminmenu:server:requestControl', target) +end, false) + +CreateThread(function() + while true do + Wait(0) + if isControlling then + if IsControlJustPressed(0, 200) then + stopControl() + end + end + end +end) + +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() ~= resourceName then return end + if isControlling then + stopControl() + end + if isBeingControlled then + isBeingControlled = false + currentMoveState = nil + ClearPedTasks(PlayerPedId()) + end +end) diff --git a/server/controlplayer.lua b/server/controlplayer.lua new file mode 100644 index 0000000..72168ef --- /dev/null +++ b/server/controlplayer.lua @@ -0,0 +1,106 @@ +local activeControls = {} + +local function getSelectedDataEntry(selectedData, aliases) + if type(selectedData) ~= 'table' then return nil end + + local keys = type(aliases) == 'table' and aliases or { aliases } + for _, key in ipairs(keys) do + if key and selectedData[key] ~= nil then + return selectedData[key] + end + end + + return nil +end + +function GetSelectedDataValue(selectedData, aliases, defaultValue) + local entry = getSelectedDataEntry(selectedData, aliases) + if entry == nil then return defaultValue end + + if type(entry) == 'table' then + if entry.value ~= nil then return entry.value end + if entry.label ~= nil then return entry.label end + end + + return entry +end + +RegisterNetEvent('ps-adminmenu:server:requestControl', function(data, selectedData) + local data = CheckDataFromKey(data) + if not data or not CheckPerms(source, data.perms) then return end + + local src = source + + local targetId = tonumber(GetSelectedDataValue(selectedData, { "Player", "Oyuncu" })) + if not targetId then return end + + if activeControls[src] then + TriggerClientEvent('ps-adminmenu:client:stopControl', src) + return + end + + local targetName = GetPlayerName(targetId) + if not targetName then + TriggerClientEvent('chat:addMessage', src, { args = { '^1ERROR', 'Player is not online.' } }) + return + end + + if src == targetId then + TriggerClientEvent('chat:addMessage', src, { args = { '^1ERROR', 'You cannot control yourself.' } }) + return + end + + activeControls[src] = targetId + TriggerClientEvent('ps-adminmenu:client:youAreControlled', targetId, true) + TriggerClientEvent('ps-adminmenu:client:startControl', src, targetId) +end) + +RegisterNetEvent('ps-adminmenu:server:sendMovement', function(moveState) + local src = source + local targetId = activeControls[src] + + if not targetId then return end + if not GetPlayerName(targetId) then return end + + TriggerClientEvent('ps-adminmenu:client:receiveMovement', targetId, moveState) +end) + +RegisterNetEvent('ps-adminmenu:server:stopControl', function() + local src = source + local targetId = activeControls[src] + + if targetId then + TriggerClientEvent('ps-adminmenu:client:youAreControlled', targetId, false) + + activeControls[src] = nil + end +end) + +AddEventHandler('playerDropped', function() + local src = source + + local targetId = activeControls[src] + if targetId then + TriggerClientEvent('ps-adminmenu:client:youAreControlled', targetId, false) + activeControls[src] = nil + end + + for controller, target in pairs(activeControls) do + if target == src then + activeControls[controller] = nil + TriggerClientEvent('ps-adminmenu:client:stopControl', controller) + break + end + end +end) + +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() ~= resourceName then return end + + for controller, targetId in pairs(activeControls) do + TriggerClientEvent('ps-adminmenu:client:youAreControlled', targetId, false) + TriggerClientEvent('ps-adminmenu:client:stopControl', controller) + end + + activeControls = {} +end) diff --git a/shared/config.lua b/shared/config.lua index 0b9cbfd..b448c78 100644 --- a/shared/config.lua +++ b/shared/config.lua @@ -57,6 +57,15 @@ Config.Actions = { }, }, + ["control_player"] = { + label = "Control Player", + perms = "god", + dropdown = { + { label = "Player", option = "dropdown", data = "players" }, + { label = "Confirm", option = "button", type = "server", event = "ps-adminmenu:server:requestControl" }, + }, + }, + ["change_weather"] = { label = "Change Weather", perms = "mod", @@ -673,6 +682,12 @@ Config.PlayerActions = { event = "ps-adminmenu:server:BringPlayer", perms = "mod", }, + ["controlPlayer"] = { + label = "Control Player", + event = "ps-adminmenu:server:requestControl", + perms = "god", + type = "server" + }, ["revivePlayer"] = { label = "Revive Player", event = "ps-adminmenu:server:Revive",