add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,876 @@
-- lua/autorun/mrp_handcuffs_ext.lua
-- Realistic Handcuffs Extension for Military RP
if SERVER then
util.AddNetworkString("MRP_Handcuffs_PerformJail")
util.AddNetworkString("RHC_Jailer_Menu")
util.AddNetworkString("MRP_Handcuffs_PerformUnjail")
util.AddNetworkString("MRP_Handcuffs_OpenInteract")
util.AddNetworkString("MRP_Handcuffs_DoInteract")
util.AddNetworkString("MRP_UpdateJailInfo")
-- Data persistence for cells
local cellFilePath = "mrp_jailcells.txt"
MRP_JailCells = {RF = {}, UK = {}}
if file.Exists(cellFilePath, "DATA") then
local data = util.JSONToTable(file.Read(cellFilePath, "DATA") or "{}") or {}
MRP_JailCells.RF = data.RF or {}
MRP_JailCells.UK = data.UK or {}
end
local function SaveCells()
file.Write(cellFilePath, util.TableToJSON(MRP_JailCells))
end
-- Helper to get player being looked at
local function GetTarget(ply)
local tr = ply:GetEyeTrace()
if IsValid(tr.Entity) and tr.Entity:IsPlayer() and tr.Entity:GetPos():DistToSqr(ply:GetPos()) < 100000 then
return tr.Entity
end
return nil
end
-- Console commands as fallback for setting cells
concommand.Add("mrp_setcellrf", function(ply, cmd, args)
if IsValid(ply) and not ply:IsSuperAdmin() then return end
local cellNum = tonumber(args[1])
if cellNum and cellNum >= 1 and cellNum <= 3 then
MRP_JailCells.RF[cellNum] = ply:GetPos()
SaveCells()
if IsValid(ply) then ply:ChatPrint("Позиция камеры РФ " .. cellNum .. " установлена на ваше местоположение.") else print("Set.") end
else
if IsValid(ply) then ply:ChatPrint("Использование: mrp_setcellrf [1-3] в консоли") end
end
end)
concommand.Add("mrp_setcelluk", function(ply, cmd, args)
if IsValid(ply) and not ply:IsSuperAdmin() then return end
local cellNum = tonumber(args[1])
if cellNum and cellNum >= 1 and cellNum <= 3 then
MRP_JailCells.UK[cellNum] = ply:GetPos()
SaveCells()
if IsValid(ply) then ply:ChatPrint("Позиция камеры УК " .. cellNum .. " установлена на ваше местоположение.") else print("Set.") end
else
if IsValid(ply) then ply:ChatPrint("Использование: mrp_setcelluk [1-3] в консоли") end
end
end)
-- Chat commands
hook.Add("PlayerSay", "MRP_Handcuffs_ChatCommands", function(ply, text)
local args = string.Explode(" ", string.lower(text))
local cmd = args[1]
if cmd == "/setcellrf" then
if not ply:IsSuperAdmin() then return "" end
local cellNum = tonumber(args[2])
if cellNum and cellNum >= 1 and cellNum <= 3 then
MRP_JailCells.RF[cellNum] = ply:GetPos()
SaveCells()
ply:ChatPrint("Позиция камеры РФ " .. cellNum .. " установлена на ваше местоположение.")
else
ply:ChatPrint("Использование: /setcellrf [1-3] (Смотрите на место или стойте там)")
end
return "" -- suppress chat
end
if cmd == "/setcelluk" then
if not ply:IsSuperAdmin() then return "" end
local cellNum = tonumber(args[2])
if cellNum and cellNum >= 1 and cellNum <= 3 then
MRP_JailCells.UK[cellNum] = ply:GetPos()
SaveCells()
ply:ChatPrint("Позиция камеры УК " .. cellNum .. " установлена на ваше местоположение.")
else
ply:ChatPrint("Использование: /setcelluk [1-3] (Смотрите на место или стойте там)")
end
return "" -- suppress chat
end
local function HasPoliceAccess(p)
return true
end
if cmd == "/gag" then
if not HasPoliceAccess(ply) then return "" end
local target = GetTarget(ply)
if target and target:GetNWBool("rhc_cuffed", false) then
local isGagged = target:GetNWBool("MRP_Gagged", false)
target:SetNWBool("MRP_Gagged", not isGagged)
ply:ChatPrint(target:Nick() .. " теперь " .. (not isGagged and "с кляпом" or "без кляпа") .. ".")
else
ply:ChatPrint("Вы должны смотреть на закованного игрока.")
end
return ""
elseif cmd == "/blind" then
if not HasPoliceAccess(ply) then return "" end
local target = GetTarget(ply)
if target and target:GetNWBool("rhc_cuffed", false) then
local isBlind = target:GetNWBool("MRP_Blindfolded", false)
target:SetNWBool("MRP_Blindfolded", not isBlind)
if not isBlind then
target:PrintMessage(HUD_PRINTCENTER, "Вам завязали глаза.")
end
ply:ChatPrint(target:Nick() .. " теперь " .. (not isBlind and "с завязанными глазами" or "без повязки на глазах") .. ".")
else
ply:ChatPrint("Вы должны смотреть на закованного игрока.")
end
return ""
end
end)
-- Mute gagged players in voice
hook.Add("PlayerCanHearPlayersVoice", "MRP_Handcuffs_GagVoice", function(listener, talker)
if IsValid(talker) and talker:GetNWBool("MRP_Gagged", false) then
return false, false
end
end)
-- Mute gagged players in text chat (Helix override)
hook.Add("PlayerSay", "MRP_Handcuffs_GagChat", function(ply, text)
if ply:GetNWBool("MRP_Gagged", false) then
ply:ChatPrint("Вы не можете говорить с кляпом во рту.")
return ""
end
end)
-- Sync drag state for visuals
timer.Create("MRP_Handcuffs_SyncDrag", 0.5, 0, function()
for _, ply in ipairs(player.GetAll()) do
if ply.Dragging and IsValid(ply.Dragging) then
ply:SetNWEntity("MRP_DraggingTarget", ply.Dragging)
else
ply:SetNWEntity("MRP_DraggingTarget", NULL)
end
end
end)
-- Interaction Menu and Move/Freeze logic
net.Receive("MRP_Handcuffs_DoInteract", function(len, ply)
-- Removing privilege check to allow anyone with weapon to interact
-- if ply.IsRHCWhitelisted and not ply:IsRHCWhitelisted() and not ply:IsAdmin() then return end
local target = net.ReadEntity()
local action = net.ReadString()
if not IsValid(target) or not target:IsPlayer() or not target:GetNWBool("rhc_cuffed", false) then return end
if target:GetPos():DistToSqr(ply:GetPos()) > 100000 then return end
if action == "gag" then
local isGagged = target:GetNWBool("MRP_Gagged", false)
target:SetNWBool("MRP_Gagged", not isGagged)
ply:ChatPrint(target:Nick() .. " теперь " .. (not isGagged and "с кляпом" or "без кляпа") .. ".")
elseif action == "blind" then
local isBlind = target:GetNWBool("MRP_Blindfolded", false)
target:SetNWBool("MRP_Blindfolded", not isBlind)
if not isBlind then target:PrintMessage(HUD_PRINTCENTER, "Вам завязали глаза.") end
ply:ChatPrint(target:Nick() .. " теперь " .. (not isBlind and "с завязанными глазами" or "без повязки на глазах") .. ".")
end
end)
hook.Add("KeyPress", "MRP_Handcuffs_MoveBind", function(ply, key)
if key == IN_USE then
-- Removing privilege check to allow anyone with weapon to interact
-- if ply.IsRHCWhitelisted and not ply:IsRHCWhitelisted() and not ply:IsAdmin() then return end
local target = GetTarget(ply)
if IsValid(target) and target:GetNWBool("rhc_cuffed", false) then
if ply:KeyDown(IN_WALK) then -- ALT + E
if target:GetMoveType() == MOVETYPE_NONE then
target:SetMoveType(MOVETYPE_WALK)
ply:ChatPrint("Разморожен " .. target:Nick())
else
target:SetMoveType(MOVETYPE_NONE)
local trace = util.QuickTrace(ply:EyePos(), ply:GetAimVector() * 80, {ply, target})
target:SetPos(trace.HitPos)
ply:ChatPrint("Заморожен и перемещен " .. target:Nick())
end
else -- Just E
net.Start("MRP_Handcuffs_OpenInteract")
net.WriteEntity(target)
net.WriteBool(target:GetNWBool("MRP_Gagged", false))
net.WriteBool(target:GetNWBool("MRP_Blindfolded", false))
net.Send(ply)
end
end
end
end)
-- Custom Jailer network receiver
net.Receive("MRP_Handcuffs_PerformJail", function(len, ply)
local dragged = net.ReadEntity()
local time = net.ReadUInt(32) -- up to 3600
local reason = net.ReadString()
local cellIndex = net.ReadUInt(8) -- 1, 2, or 3
-- validate
if not ply.LastJailerNPC or not IsValid(ply.LastJailerNPC) then return end
if ply:GetPos():DistToSqr(ply.LastJailerNPC:GetPos()) > 60000 then return end
local faction = ply.LastJailerFaction or "RF"
if not IsValid(dragged) or not dragged:IsPlayer() then return end
-- Validate distance
if dragged:GetPos():DistToSqr(ply:GetPos()) > 60000 then
ply:ChatPrint("Заключенный слишком далеко!")
return
end
time = math.Clamp(time, 1, 3600)
-- Force teleport to custom cell
if cellIndex >= 1 and cellIndex <= 3 and MRP_JailCells[faction] and MRP_JailCells[faction][cellIndex] then
ply:ChatPrint("Игрок " .. dragged:Nick() .. " (" .. dragged:SteamID() .. ") посажен в камеру " .. faction .. " на " .. time .. " сек. Причина: " .. reason)
-- Log the jailing
hook.Run("RHC_jailed", dragged, ply, time, reason)
-- Keep them cuffed in jail (weapon limits already applied by standalone logic)
timer.Simple(0.2, function()
if IsValid(dragged) then
dragged:SetPos(MRP_JailCells[faction][cellIndex])
if dragged.getChar and dragged:getChar() then
dragged:getChar():setData("mrp_jail_time", os.time() + time)
dragged:getChar():setData("mrp_jail_faction", faction)
dragged:getChar():setData("mrp_jail_cell", cellIndex)
dragged:getChar():setData("mrp_jail_reason", reason)
dragged:getChar():setData("mrp_jailer_name", ply:Nick())
dragged:getChar():setData("mrp_jailer_steam", ply:SteamID())
-- Sync to client
dragged:SetNWInt("MRP_JailTime", os.time() + time)
dragged:SetNWString("MRP_JailReason", reason)
dragged:SetNWString("MRP_JailerName", ply:Nick())
dragged:SetNWString("MRP_JailerSteam", ply:SteamID())
net.Start("MRP_UpdateJailInfo")
net.WriteEntity(dragged)
net.WriteInt(os.time() + time)
net.WriteString(reason)
net.WriteString(ply:Nick())
net.WriteString(ply:SteamID())
net.Broadcast()
end
-- Simple arrest unjail timer
local timerName = "MRP_Jail_" .. dragged:SteamID64()
timer.Create(timerName, time, 1, function()
if IsValid(dragged) then
dragged:Spawn()
-- Trigger uncuff manually
dragged:SetNWBool("rhc_cuffed", false)
dragged:SetNWBool("MRP_Gagged", false)
dragged:SetNWBool("MRP_Blindfolded", false)
dragged:StripWeapon("weapon_r_cuffed")
-- Explicitly reset bone angles
local CuffedBones = {
["ValveBiped.Bip01_R_UpperArm"] = true,
["ValveBiped.Bip01_L_UpperArm"] = true,
["ValveBiped.Bip01_R_Forearm"] = true,
["ValveBiped.Bip01_L_Forearm"] = true,
["ValveBiped.Bip01_R_Hand"] = true,
["ValveBiped.Bip01_L_Hand"] = true,
}
for boneName, _ in pairs(CuffedBones) do
local boneId = dragged:LookupBone(boneName)
if boneId then
dragged:ManipulateBoneAngles(boneId, Angle(0,0,0))
end
end
if dragged.RHC_SavedWeapons then
for _, class in ipairs(dragged.RHC_SavedWeapons) do
dragged:Give(class)
end
dragged.RHC_SavedWeapons = nil
end
if dragged.RHC_OldWalk then
dragged:SetWalkSpeed(dragged.RHC_OldWalk)
dragged:SetRunSpeed(dragged.RHC_OldRun)
dragged.RHC_OldWalk = nil
dragged.RHC_OldRun = nil
end
if dragged.getChar and dragged:getChar() then
dragged:getChar():setData("mrp_jail_time", nil)
dragged:getChar():setData("mrp_jail_faction", nil)
dragged:getChar():setData("mrp_jail_cell", nil)
dragged:getChar():setData("mrp_jail_reason", nil)
dragged:getChar():setData("mrp_jailer_name", nil)
dragged:getChar():setData("mrp_jailer_steam", nil)
dragged:SetNWInt("MRP_JailTime", 0)
dragged:SetNWString("MRP_JailReason", "")
dragged:SetNWString("MRP_JailerName", "")
dragged:SetNWString("MRP_JailerSteam", "")
net.Start("MRP_UpdateJailInfo")
net.WriteEntity(dragged)
net.WriteInt(0)
net.WriteString("")
net.WriteString("")
net.WriteString("")
net.Broadcast()
end
if dragged.RHC_SavedWeapons then
for _, class in ipairs(dragged.RHC_SavedWeapons) do dragged:Give(class) end
dragged.RHC_SavedWeapons = nil
end
if dragged.RHC_OldWalk then
dragged:SetWalkSpeed(dragged.RHC_OldWalk)
dragged:SetRunSpeed(dragged.RHC_OldRun)
end
dragged:ChatPrint("Ваш срок заключения подошел к концу.")
end
end)
end
end)
-- Drop drag
ply.Dragging = nil
dragged.DraggedBy = nil
ply:SetNWEntity("MRP_DraggingTarget", NULL)
else
ply:ChatPrint("Внимание: Позиция выбранной камеры не настроена! Администратор должен использовать /setcell" .. string.lower(faction) .. " " .. cellIndex)
end
end)
net.Receive("MRP_Handcuffs_PerformUnjail", function(len, ply)
local target = net.ReadEntity()
if not IsValid(target) or not target:IsPlayer() then return end
-- validate
if not ply.LastJailerNPC or not IsValid(ply.LastJailerNPC) then return end
if ply:GetPos():DistToSqr(ply.LastJailerNPC:GetPos()) > 60000 then return end
local timerName = "MRP_Jail_" .. target:SteamID64()
if timer.Exists(timerName) then
timer.Remove(timerName)
target:SetNWBool("rhc_cuffed", false)
target:SetNWBool("MRP_Gagged", false)
target:SetNWBool("MRP_Blindfolded", false)
target:StripWeapon("weapon_r_cuffed")
-- Explicitly reset bone angles
local CuffedBones = {
["ValveBiped.Bip01_R_UpperArm"] = true,
["ValveBiped.Bip01_L_UpperArm"] = true,
["ValveBiped.Bip01_R_Forearm"] = true,
["ValveBiped.Bip01_L_Forearm"] = true,
["ValveBiped.Bip01_R_Hand"] = true,
["ValveBiped.Bip01_L_Hand"] = true,
}
for boneName, _ in pairs(CuffedBones) do
local boneId = target:LookupBone(boneName)
if boneId then
target:ManipulateBoneAngles(boneId, Angle(0,0,0))
end
end
if target.RHC_SavedWeapons then
for _, class in ipairs(target.RHC_SavedWeapons) do
target:Give(class)
end
target.RHC_SavedWeapons = nil
end
if target.RHC_OldWalk then
target:SetWalkSpeed(target.RHC_OldWalk)
target:SetRunSpeed(target.RHC_OldRun)
target.RHC_OldWalk = nil
target.RHC_OldRun = nil
end
if target.getChar and target:getChar() then
target:getChar():setData("mrp_jail_time", nil)
target:getChar():setData("mrp_jail_faction", nil)
target:getChar():setData("mrp_jail_cell", nil)
end
-- Spawn the player naturally, just like after natural timeout
target:Spawn()
target:ChatPrint("Вас досрочно освободил " .. ply:Nick())
ply:ChatPrint("Вы освободили " .. target:Nick())
else
ply:ChatPrint("Похоже, этот игрок не в тюрьме.")
end
end)
-- Reconnect persistence
hook.Add("PlayerLoadedCharacter", "MRP_Handcuffs_LoadJail", function(ply, character, oldCharacter)
timer.Simple(1, function()
if not IsValid(ply) then return end
if not character or not character.getData then return end -- FIX
local jailTime = character:getData("mrp_jail_time", 0)
local faction = character:getData("mrp_jail_faction", "RF")
local cellIndex = character:getData("mrp_jail_cell", 1)
if jailTime > os.time() then
local remaining = jailTime - os.time()
-- Ensure they're cuffed
ply:SetNWBool("rhc_cuffed", true)
ply:Give("weapon_r_cuffed")
if MRP_JailCells[faction] and MRP_JailCells[faction][cellIndex] then
ply:SetPos(MRP_JailCells[faction][cellIndex])
end
ply:ChatPrint("Вы вернулись, чтобы отсидеть оставшиеся " .. remaining .. " секунд вашего срока.")
-- Sync jail info to client
ply:SetNWInt("MRP_JailTime", jailTime)
ply:SetNWString("MRP_JailReason", character:getData("mrp_jail_reason", "Не указана"))
ply:SetNWString("MRP_JailerName", character:getData("mrp_jailer_name", "Неизвестен"))
ply:SetNWString("MRP_JailerSteam", character:getData("mrp_jailer_steam", "Неизвестен"))
net.Start("MRP_UpdateJailInfo")
net.WriteEntity(ply)
net.WriteInt(jailTime)
net.WriteString(character:getData("mrp_jail_reason", "Не указана"))
net.WriteString(character:getData("mrp_jailer_name", "Неизвестен"))
net.WriteString(character:getData("mrp_jailer_steam", "Неизвестен"))
net.Broadcast()
-- Re-apply timer
local timerName = "MRP_Jail_" .. ply:SteamID64()
timer.Create(timerName, remaining, 1, function()
if IsValid(ply) then
ply:SetNWBool("rhc_cuffed", false)
ply:SetNWBool("MRP_Gagged", false)
ply:SetNWBool("MRP_Blindfolded", false)
ply:StripWeapon("weapon_r_cuffed")
if ply.getChar and ply:getChar() then
ply:getChar():setData("mrp_jail_time", nil)
ply:getChar():setData("mrp_jail_faction", nil)
ply:getChar():setData("mrp_jail_cell", nil)
end
-- Look for nearest appropriate jailer for un-jailing position
local classStr = (faction == "RF") and "rhc_jailer_rf" or "rhc_jailer_uk"
local npc = ents.FindByClass(classStr)[1]
if IsValid(npc) then
ply:Spawn()
end
ply:ChatPrint("Ваш срок заключения подошел к концу.")
end
end)
else
-- Timed out while offline or was free
if character:getData("mrp_jail_time") then
character:setData("mrp_jail_time", nil)
character:setData("mrp_jail_faction", nil)
character:setData("mrp_jail_cell", nil)
character:setData("mrp_jail_reason", nil)
character:setData("mrp_jailer_name", nil)
character:setData("mrp_jailer_steam", nil)
ply:SetNWInt("MRP_JailTime", 0)
ply:SetNWString("MRP_JailReason", "")
ply:SetNWString("MRP_JailerName", "")
ply:SetNWString("MRP_JailerSteam", "")
net.Start("MRP_UpdateJailInfo")
net.WriteEntity(ply)
net.WriteInt(0)
net.WriteString("")
net.WriteString("")
net.WriteString("")
net.Broadcast()
end
end
end)
end)
-- Reset cuff state on spawn if not jailed
hook.Add("PlayerSpawn", "MRP_Handcuffs_ResetOnSpawn", function(ply)
if ply:GetNWBool("rhc_cuffed", false) then
local character = ply.getChar and ply:getChar()
if not character or not character.getData or character:getData("mrp_jail_time", 0) <= os.time() then
-- Not jailed, reset cuff state
ply:SetNWBool("rhc_cuffed", false)
ply:SetNWBool("MRP_Gagged", false)
ply:SetNWBool("MRP_Blindfolded", false)
ply:StripWeapon("weapon_r_cuffed")
-- Reset bones
for boneName, _ in pairs({
["ValveBiped.Bip01_R_UpperArm"] = true,
["ValveBiped.Bip01_L_UpperArm"] = true,
["ValveBiped.Bip01_R_Forearm"] = true,
["ValveBiped.Bip01_L_Forearm"] = true,
["ValveBiped.Bip01_R_Hand"] = true,
["ValveBiped.Bip01_L_Hand"] = true,
}) do
local boneId = ply:LookupBone(boneName)
if boneId then
ply:ManipulateBoneAngles(boneId, Angle(0,0,0))
end
end
-- Restore weapons and speed if saved
if ply.RHC_SavedWeapons then
for _, class in ipairs(ply.RHC_SavedWeapons) do
ply:Give(class)
end
ply.RHC_SavedWeapons = nil
end
if ply.RHC_OldWalk then
ply:SetWalkSpeed(ply.RHC_OldWalk)
ply:SetRunSpeed(ply.RHC_OldRun)
ply.RHC_OldWalk = nil
ply.RHC_OldRun = nil
end
end
end
end)
end
if CLIENT then
local clientJailTime = 0
local clientJailReason = ""
local clientJailerName = ""
local clientJailerSteam = ""
net.Receive("MRP_UpdateJailInfo", function()
local ent = net.ReadEntity()
if ent == LocalPlayer() then
clientJailTime = net.ReadInt()
clientJailReason = net.ReadString()
clientJailerName = net.ReadString()
clientJailerSteam = net.ReadString()
end
end)
-- Blindfold Overlay
hook.Add("HUDPaint", "MRP_Handcuffs_BlindfoldHUD", function()
local ply = LocalPlayer()
if not IsValid(ply) then return end
if ply:GetNWBool("MRP_Blindfolded", false) then
surface.SetDrawColor(0, 0, 0, 255)
surface.DrawRect(0, 0, ScrW(), ScrH())
draw.SimpleText("У вас завязаны глаза", "Trebuchet24", ScrW()/2, ScrH()/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
end)
-- Jail Time HUD
hook.Add("HUDPaint", "MRP_Handcuffs_JailHUD", function()
if clientJailTime > os.time() then
local remaining = clientJailTime - os.time()
local minutes = math.floor(remaining / 60)
local seconds = remaining % 60
local timeStr = string.format("%02d:%02d", minutes, seconds)
draw.SimpleTextOutlined("Осталось сидеть: " .. timeStr, "Trebuchet24", ScrW()/2, ScrH() - 150, Color(255, 255, 100, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM, 2, Color(0, 0, 0, 255))
draw.SimpleTextOutlined("Причина: " .. clientJailReason, "Trebuchet18", ScrW()/2, ScrH() - 120, Color(255, 255, 100, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM, 2, Color(0, 0, 0, 255))
draw.SimpleTextOutlined("Посадил: " .. clientJailerName .. " (" .. clientJailerSteam .. ")", "Trebuchet18", ScrW()/2, ScrH() - 90, Color(255, 255, 100, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM, 2, Color(0, 0, 0, 255))
end
end)
-- Beam dragging visual
local matBeam = Material("cable/rope")
hook.Add("PostDrawTranslucentRenderables", "MRP_Handcuffs_DrawDrag", function()
for _, ply in ipairs(player.GetAll()) do
local target = ply:GetNWEntity("MRP_DraggingTarget")
if IsValid(target) then
local startPos = ply:GetPos() + Vector(0,0,40)
local endPos = target:GetPos() + Vector(0,0,40)
local wep = ply:GetActiveWeapon()
if IsValid(wep) then
local vm = ply:GetViewModel()
if ply == LocalPlayer() and not ply:ShouldDrawLocalPlayer() and IsValid(vm) then
local attach = vm:GetAttachment(1)
if attach then startPos = attach.Pos end
else
local attach = wep:GetAttachment(1)
if attach then
startPos = attach.Pos
else
local bone = ply:LookupBone("ValveBiped.Bip01_R_Hand")
if bone then
local bPos = ply:GetBonePosition(bone)
if bPos then startPos = bPos end
end
end
end
else
local bone = ply:LookupBone("ValveBiped.Bip01_R_Hand")
if bone then
local bPos = ply:GetBonePosition(bone)
if bPos then startPos = bPos end
end
end
local tBone = target:LookupBone("ValveBiped.Bip01_R_Hand")
if tBone then
local bPos = target:GetBonePosition(tBone)
if bPos then endPos = bPos end
end
render.SetMaterial(matBeam)
render.DrawBeam(startPos, endPos, 2, 0, startPos:Distance(endPos) / 10, color_white)
end
end
end)
-- Models
hook.Add("PostPlayerDraw", "MRP_Handcuffs_DrawModels", function(ply)
if not IsValid(ply) or not ply:Alive() then return end
local isGagged = ply:GetNWBool("MRP_Gagged", false)
local isBlind = ply:GetNWBool("MRP_Blindfolded", false)
if not isGagged and not isBlind then return end
local bone = ply:LookupBone("ValveBiped.Bip01_Head1")
if not bone then return end
local pos, ang = ply:GetBonePosition(bone)
if not pos or not ang then return end
if isGagged then
if not IsValid(gagModel) then
gagModel = ClientsideModel("models/kidnappers_kit/tape gag cross/tapegagcross.mdl")
if IsValid(gagModel) then gagModel:SetNoDraw(true) end
end
if IsValid(gagModel) then
local gagAng = ang * 1
gagAng:RotateAroundAxis(gagAng:Right(), -92.67)
gagAng:RotateAroundAxis(gagAng:Up(), -85.54)
gagAng:RotateAroundAxis(gagAng:Forward(), 0)
local offset = ang:Forward() * -59.4 + ang:Right() * 6.53 + ang:Up() * 2.77
gagModel:SetRenderOrigin(pos + offset)
gagModel:SetRenderAngles(gagAng)
gagModel:SetModelScale(1)
gagModel:DrawModel()
end
end
if isBlind then
if not IsValid(blindModel) then
blindModel = ClientsideModel("models/kidnappers_kit/tape blindfold/tapeblindfold.mdl")
if IsValid(blindModel) then blindModel:SetNoDraw(true) end
end
if IsValid(blindModel) then
local blindAng = ang * 1
blindAng:RotateAroundAxis(blindAng:Right(), -90)
blindAng:RotateAroundAxis(blindAng:Up(), -89.11)
blindAng:RotateAroundAxis(blindAng:Forward(), 0)
local offset = ang:Forward() * -49 + ang:Right() * 3.17 + ang:Up() * 0.4
blindModel:SetRenderOrigin(pos + offset)
blindModel:SetRenderAngles(blindAng)
blindModel:SetModelScale(0.92)
blindModel:DrawModel()
end
end
end)
timer.Simple(2, function()
net.Receive("RHC_Jailer_Menu", function()
local dragged = net.ReadEntity()
local faction = net.ReadString() -- "RF" or "UK"
if not IsValid(dragged) then
chat.AddText(Color(255,0,0), "Рядом нет закованных игроков!")
return
end
local factionTitle = (faction == "RF" or faction == "UK") and (" (" .. faction .. ")") or ""
local frame = vgui.Create("DFrame")
frame:SetSize(400, 320)
frame:Center()
frame:SetTitle("Посадить игрока: " .. dragged:Nick() .. factionTitle)
frame:MakePopup()
frame.Paint = function(self, w, h)
draw.RoundedBox(4, 0, 0, w, h, Color(40, 40, 40, 240))
draw.RoundedBox(4, 0, 0, w, 24, Color(30, 30, 30, 255))
end
local timeLabel = vgui.Create("DLabel", frame)
timeLabel:SetPos(20, 40)
timeLabel:SetText("Время (1-3600 сек):")
timeLabel:SizeToContents()
timeLabel:SetTextColor(color_white)
local timeText = vgui.Create("DTextEntry", frame)
timeText:SetPos(20, 60)
timeText:SetSize(360, 25)
timeText:SetText("300")
timeText:SetNumeric(true)
local reasonLabel = vgui.Create("DLabel", frame)
reasonLabel:SetPos(20, 100)
reasonLabel:SetText("Причина:")
reasonLabel:SizeToContents()
reasonLabel:SetTextColor(color_white)
local reasonText = vgui.Create("DTextEntry", frame)
reasonText:SetPos(20, 120)
reasonText:SetSize(360, 25)
reasonText:SetText("Причина не указана")
local cellLabel = vgui.Create("DLabel", frame)
cellLabel:SetPos(20, 160)
cellLabel:SetText("Выберите камеру:")
cellLabel:SizeToContents()
cellLabel:SetTextColor(color_white)
local cellCombo = vgui.Create("DComboBox", frame)
cellCombo:SetPos(20, 180)
cellCombo:SetSize(360, 25)
cellCombo:SetValue("Камера 1")
cellCombo:AddChoice("Камера 1", 1)
cellCombo:AddChoice("Камера 2", 2)
cellCombo:AddChoice("Камера 3", 3)
local submitBtn = vgui.Create("DButton", frame)
submitBtn:SetPos(20, 230)
submitBtn:SetSize(360, 40)
submitBtn:SetText("Посадить")
submitBtn:SetTextColor(color_white)
submitBtn.Paint = function(self, w, h)
local col = self:IsHovered() and Color(60, 120, 200) or Color(50, 100, 180)
draw.RoundedBox(4, 0, 0, w, h, col)
end
submitBtn.DoClick = function()
local time = tonumber(timeText:GetValue()) or 300
local reason = reasonText:GetValue()
local _, cellIndex = cellCombo:GetSelected()
cellIndex = cellIndex or 1
net.Start("MRP_Handcuffs_PerformJail")
net.WriteEntity(dragged)
net.WriteUInt(time, 32)
net.WriteString(reason)
net.WriteUInt(cellIndex, 8)
net.SendToServer()
frame:Close()
end
-- Unjail functionality
local unjailBtn = vgui.Create("DButton", frame)
unjailBtn:SetPos(20, 275)
unjailBtn:SetSize(360, 25)
unjailBtn:SetText("Показать список заключенных для освобождения")
unjailBtn:SetTextColor(Color(255,100,100))
unjailBtn.Paint = function(self, w, h)
draw.RoundedBox(4, 0, 0, w, h, Color(50, 20, 20))
end
unjailBtn.DoClick = function()
frame:Close()
local uFrame = vgui.Create("DFrame")
uFrame:SetSize(300, 400)
uFrame:Center()
uFrame:SetTitle("Досрочное освобождение")
uFrame:MakePopup()
uFrame.Paint = function(self, w, h)
draw.RoundedBox(4, 0, 0, w, h, Color(40, 40, 40, 240))
end
local list = vgui.Create("DListView", uFrame)
list:Dock(FILL)
list:AddColumn("Заключенные игроки")
for _, p in ipairs(player.GetAll()) do
if p:GetNWBool("rhc_cuffed", false) and not IsValid(p.DraggedBy) then
local line = list:AddLine(p:Nick())
line.PlayerObj = p
end
end
local relBtn = vgui.Create("DButton", uFrame)
relBtn:Dock(BOTTOM)
relBtn:SetTall(30)
relBtn:SetText("Освободить выбранного")
relBtn.DoClick = function()
local selected = list:GetSelectedLine()
if selected then
local plyObj = list:GetLine(selected).PlayerObj
if IsValid(plyObj) then
net.Start("MRP_Handcuffs_PerformUnjail")
net.WriteEntity(plyObj)
net.SendToServer()
end
uFrame:Close()
end
end
end
end)
end)
net.Receive("MRP_Handcuffs_OpenInteract", function()
local target = net.ReadEntity()
local isGagged = net.ReadBool()
local isBlind = net.ReadBool()
if not IsValid(target) then return end
local frame = vgui.Create("DFrame")
frame:SetSize(250, 140)
frame:Center()
frame:SetTitle("Взаимодействие: " .. target:Nick())
frame:MakePopup()
frame.Paint = function(self, w, h)
draw.RoundedBox(4, 0, 0, w, h, Color(40, 40, 40, 240))
draw.RoundedBox(4, 0, 0, w, 24, Color(30, 30, 30, 255))
end
local gagBtn = vgui.Create("DButton", frame)
gagBtn:Dock(TOP)
gagBtn:DockMargin(10, 10, 10, 0)
gagBtn:SetTall(35)
gagBtn:SetText(isGagged and "Снять кляп" or "Вставить кляп")
gagBtn:SetTextColor(color_white)
gagBtn.Paint = function(self, w, h)
draw.RoundedBox(4, 0, 0, w, h, self:IsHovered() and Color(60, 60, 60) or Color(80, 80, 80))
end
gagBtn.DoClick = function()
net.Start("MRP_Handcuffs_DoInteract")
net.WriteEntity(target)
net.WriteString("gag")
net.SendToServer()
frame:Close()
end
local blindBtn = vgui.Create("DButton", frame)
blindBtn:Dock(TOP)
blindBtn:DockMargin(10, 10, 10, 0)
blindBtn:SetTall(35)
blindBtn:SetText(isBlind and "Снять повязку" or "Надеть повязку")
blindBtn:SetTextColor(color_white)
blindBtn.Paint = function(self, w, h)
draw.RoundedBox(4, 0, 0, w, h, self:IsHovered() and Color(60, 60, 60) or Color(80, 80, 80))
end
blindBtn.DoClick = function()
net.Start("MRP_Handcuffs_DoInteract")
net.WriteEntity(target)
net.WriteString("blind")
net.SendToServer()
frame:Close()
end
end)
end

View File

@@ -0,0 +1,50 @@
-- lua/autorun/server/mrp_jailer_spawns.lua
if not SERVER then return end
local function SpawnJailers()
-- Remove existing jailers to prevent duplicates on lua refresh
for _, ent in ipairs(ents.FindByClass("rhc_jailer_rf")) do ent:Remove() end
for _, ent in ipairs(ents.FindByClass("rhc_jailer_uk")) do ent:Remove() end
-- Spawn RF Jailer
local rf_jailer = ents.Create("rhc_jailer_rf")
if IsValid(rf_jailer) then
rf_jailer:SetPos(Vector(10651.541992, 10210.736328, 193.868973))
rf_jailer:SetAngles(Angle(0, 90, 0))
rf_jailer:Spawn()
-- Drop to floor to be safe
local tr = util.TraceLine({
start = rf_jailer:GetPos() + Vector(0,0,10),
endpos = rf_jailer:GetPos() - Vector(0,0,100),
filter = rf_jailer
})
if tr.Hit then rf_jailer:SetPos(tr.HitPos) end
end
-- Spawn UK Jailer
local uk_jailer = ents.Create("rhc_jailer_uk")
if IsValid(uk_jailer) then
uk_jailer:SetPos(Vector(-12438.240234, -9908.939453, 181.868973))
uk_jailer:SetAngles(Angle(0, 180, 0))
uk_jailer:Spawn()
local tr = util.TraceLine({
start = uk_jailer:GetPos() + Vector(0,0,10),
endpos = uk_jailer:GetPos() - Vector(0,0,100),
filter = uk_jailer
})
if tr.Hit then uk_jailer:SetPos(tr.HitPos) end
end
end
hook.Add("InitPostEntity", "MRP_SpawnHandcuffsJailers", function()
timer.Simple(5, SpawnJailers) -- Wait a bit to ensure map is fully loaded
end)
-- Make it easy to respawn via command if accidentally deleted
concommand.Add("mrp_respawn_jailers", function(ply)
if IsValid(ply) and not ply:IsSuperAdmin() then return end
SpawnJailers()
if IsValid(ply) then ply:ChatPrint("NPC-тюремщики возрождены.") end
end)

View File

@@ -0,0 +1 @@
-- Cleared to fix "attempt to index global ENT" error

View File

@@ -0,0 +1,7 @@
if SERVER then
-- Suppress missing file errors
end
if CLIENT then
-- Suppress missing file errors
end