add sborka
This commit is contained in:
357
garrysmod/gamemodes/militaryrp/plugins/military_id/cl_plugin.lua
Normal file
357
garrysmod/gamemodes/militaryrp/plugins/military_id/cl_plugin.lua
Normal file
@@ -0,0 +1,357 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
-- Цветовая схема (как в арсенале)
|
||||
local COLOR_BG_DARK = Color(3, 5, 4)
|
||||
local COLOR_BG_MEDIUM = Color(8, 12, 10)
|
||||
local COLOR_BG_LIGHT = Color(12, 18, 14)
|
||||
local COLOR_PRIMARY = Color(27, 94, 32)
|
||||
local COLOR_PRIMARY_DARK = Color(15, 60, 18)
|
||||
local COLOR_ACCENT = Color(56, 102, 35)
|
||||
local COLOR_TEXT_PRIMARY = Color(165, 214, 167)
|
||||
local COLOR_TEXT_SECONDARY = Color(102, 187, 106)
|
||||
local COLOR_DANGER = Color(136, 14, 14)
|
||||
local COLOR_WARNING = Color(191, 130, 0)
|
||||
local COLOR_BORDER = Color(15, 60, 18, 60)
|
||||
|
||||
-- Вспомогательная функция для рисования кругов
|
||||
if not draw.Circle then
|
||||
function draw.Circle(x, y, radius, color)
|
||||
local segmentCount = math.max(16, radius)
|
||||
surface.SetDrawColor(color or color_white)
|
||||
|
||||
local circle = {}
|
||||
for i = 0, segmentCount do
|
||||
local angle = math.rad((i / segmentCount) * 360)
|
||||
table.insert(circle, {
|
||||
x = x + math.cos(angle) * radius,
|
||||
y = y + math.sin(angle) * radius
|
||||
})
|
||||
end
|
||||
surface.DrawPoly(circle)
|
||||
end
|
||||
end
|
||||
|
||||
if not surface.DrawCircle then
|
||||
function surface.DrawCircle(x, y, radius, color)
|
||||
draw.Circle(x, y, radius, color)
|
||||
end
|
||||
end
|
||||
|
||||
-- Открытие опроса
|
||||
net.Receive("ixMilitaryID_OpenQuiz", function()
|
||||
PLUGIN:OpenQuizMenu()
|
||||
end)
|
||||
|
||||
-- Просмотр документа
|
||||
net.Receive("ixMilitaryID_ViewDocument", function()
|
||||
local data = net.ReadTable()
|
||||
PLUGIN:ShowDocument(data)
|
||||
end)
|
||||
|
||||
-- UI для опроса
|
||||
function PLUGIN:OpenQuizMenu()
|
||||
if IsValid(self.quizFrame) then
|
||||
self.quizFrame:Remove()
|
||||
end
|
||||
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
local frame = vgui.Create("DFrame")
|
||||
frame:SetSize(scrW, scrH)
|
||||
frame:SetPos(0, 0)
|
||||
frame:SetTitle("")
|
||||
frame:SetDraggable(false)
|
||||
frame:ShowCloseButton(false)
|
||||
frame:MakePopup()
|
||||
|
||||
-- Градиентный фон с зеленым оттенком (как в арсенале)
|
||||
frame.Paint = function(s, w, h)
|
||||
-- Основной фон
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
-- Градиент сверху
|
||||
local gradHeight = 300
|
||||
for i = 0, gradHeight do
|
||||
local alpha = (1 - i/gradHeight) * 40
|
||||
surface.SetDrawColor(COLOR_PRIMARY.r, COLOR_PRIMARY.g, COLOR_PRIMARY.b, alpha)
|
||||
surface.DrawRect(0, i, w, 1)
|
||||
end
|
||||
|
||||
-- Верхняя панель
|
||||
surface.SetDrawColor(COLOR_BG_MEDIUM)
|
||||
surface.DrawRect(0, 0, w, 100)
|
||||
|
||||
-- Акцентная линия
|
||||
surface.SetDrawColor(COLOR_PRIMARY)
|
||||
surface.DrawRect(0, 100, w, 3)
|
||||
|
||||
-- Декоративные элементы
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
for i = 0, w, 200 do
|
||||
surface.DrawRect(i, 0, 1, 100)
|
||||
end
|
||||
|
||||
-- Заголовок
|
||||
draw.SimpleText("◆ ВОЕННЫЙ ОПРОСНИК ◆", "ixMenuButtonFont", w/2, 35, COLOR_ACCENT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText("ПРОХОЖДЕНИЕ ВОЕННОЙ КОМИССИИ", "ixSmallFont", w/2, 68, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
-- Кнопка закрытия
|
||||
local closeBtn = vgui.Create("DButton", frame)
|
||||
closeBtn:SetText("")
|
||||
closeBtn:SetSize(42, 42)
|
||||
closeBtn:SetPos(scrW - 60, 18)
|
||||
closeBtn.Paint = function(s, w, h)
|
||||
local col = s:IsHovered() and COLOR_DANGER or Color(COLOR_DANGER.r * 0.7, COLOR_DANGER.g * 0.7, COLOR_DANGER.b * 0.7)
|
||||
|
||||
draw.RoundedBox(4, 0, 0, w, h, col)
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
surface.SetDrawColor(COLOR_TEXT_PRIMARY)
|
||||
surface.DrawLine(w*0.3, h*0.3, w*0.7, h*0.7)
|
||||
surface.DrawLine(w*0.7, h*0.3, w*0.3, h*0.7)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(255, 255, 255, 30)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Color(255, 255, 255, 30))
|
||||
end
|
||||
end
|
||||
closeBtn.DoClick = function()
|
||||
frame:Close()
|
||||
end
|
||||
|
||||
-- Основная панель контента
|
||||
local contentPanel = vgui.Create("DPanel", frame)
|
||||
contentPanel:SetSize(1000, scrH - 240)
|
||||
contentPanel:SetPos((scrW - 1000) / 2, 140)
|
||||
contentPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG_MEDIUM)
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
end
|
||||
|
||||
local currentQuestion = 1
|
||||
local answers = {}
|
||||
|
||||
-- Панель прогресса
|
||||
local progressPanel = vgui.Create("DPanel", contentPanel)
|
||||
progressPanel:Dock(TOP)
|
||||
progressPanel:DockMargin(0, 0, 0, 10)
|
||||
progressPanel:SetTall(60)
|
||||
progressPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, COLOR_BG_LIGHT)
|
||||
surface.SetDrawColor(COLOR_PRIMARY)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
-- Прогресс бар
|
||||
local progress = currentQuestion / #self.quizQuestions
|
||||
local barWidth = (w - 40) * progress
|
||||
draw.RoundedBox(4, 20, h - 15, w - 40, 8, COLOR_BG_DARK)
|
||||
draw.RoundedBox(4, 20, h - 15, barWidth, 8, COLOR_ACCENT)
|
||||
|
||||
-- Текст прогресса
|
||||
draw.SimpleText("ВОПРОС " .. currentQuestion .. " ИЗ " .. #self.quizQuestions, "ixSmallFont", w/2, 20, COLOR_TEXT_PRIMARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
-- Панель вопроса
|
||||
local questionPanel = vgui.Create("DPanel", contentPanel)
|
||||
questionPanel:Dock(TOP)
|
||||
questionPanel:DockMargin(0, 0, 0, 20)
|
||||
questionPanel:SetTall(100)
|
||||
questionPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, COLOR_BG_LIGHT)
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
end
|
||||
|
||||
local questionLabel = vgui.Create("DLabel", questionPanel)
|
||||
questionLabel:Dock(FILL)
|
||||
questionLabel:DockMargin(20, 20, 20, 20)
|
||||
questionLabel:SetFont("DermaLarge")
|
||||
questionLabel:SetTextColor(COLOR_TEXT_PRIMARY)
|
||||
questionLabel:SetWrap(true)
|
||||
questionLabel:SetContentAlignment(5)
|
||||
|
||||
-- Панель ответов
|
||||
local answersScroll = vgui.Create("DScrollPanel", contentPanel)
|
||||
answersScroll:Dock(FILL)
|
||||
answersScroll:DockMargin(0, 0, 0, 0)
|
||||
|
||||
local sbar = answersScroll:GetVBar()
|
||||
sbar:SetHideButtons(true)
|
||||
function sbar:Paint(w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, COLOR_BG_DARK)
|
||||
end
|
||||
function sbar.btnGrip:Paint(w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, COLOR_PRIMARY)
|
||||
end
|
||||
|
||||
local function ShowQuestion(index)
|
||||
if index > #self.quizQuestions then
|
||||
-- Все вопросы пройдены
|
||||
net.Start("ixMilitaryID_SubmitQuiz")
|
||||
net.WriteTable(answers)
|
||||
net.SendToServer()
|
||||
frame:Close()
|
||||
return
|
||||
end
|
||||
|
||||
local question = self.quizQuestions[index]
|
||||
questionLabel:SetText(question.question)
|
||||
|
||||
answersScroll:Clear()
|
||||
|
||||
for i, answer in ipairs(question.answers) do
|
||||
local btn = vgui.Create("DButton", answersScroll)
|
||||
btn:Dock(TOP)
|
||||
btn:DockMargin(20, 10, 20, 10)
|
||||
btn:SetTall(60)
|
||||
btn:SetText("")
|
||||
|
||||
local isHovered = false
|
||||
btn.Paint = function(s, w, h)
|
||||
local bgColor = isHovered and COLOR_PRIMARY_DARK or COLOR_BG_LIGHT
|
||||
draw.RoundedBox(6, 0, 0, w, h, bgColor)
|
||||
|
||||
-- Рамка
|
||||
surface.SetDrawColor(isHovered and COLOR_ACCENT or COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
-- Акцентная линия слева
|
||||
draw.RoundedBoxEx(6, 0, 0, 5, h, COLOR_PRIMARY, true, false, true, false)
|
||||
|
||||
-- Номер ответа
|
||||
draw.SimpleText(tostring(i), "DermaLarge", 30, h/2, COLOR_ACCENT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
-- Текст ответа
|
||||
draw.SimpleText(answer, "ixSmallFont", 60, h/2, COLOR_TEXT_PRIMARY, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
if isHovered then
|
||||
surface.SetDrawColor(255, 255, 255, 20)
|
||||
draw.RoundedBox(6, 0, 0, w, h, Color(255, 255, 255, 20))
|
||||
end
|
||||
end
|
||||
|
||||
btn.OnCursorEntered = function() isHovered = true end
|
||||
btn.OnCursorExited = function() isHovered = false end
|
||||
|
||||
btn.DoClick = function()
|
||||
answers[index] = i
|
||||
currentQuestion = currentQuestion + 1
|
||||
ShowQuestion(currentQuestion)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ShowQuestion(1)
|
||||
|
||||
self.quizFrame = frame
|
||||
end
|
||||
|
||||
-- UI для просмотра документа
|
||||
function PLUGIN:ShowDocument(data)
|
||||
if IsValid(self.documentFrame) then
|
||||
self.documentFrame:Remove()
|
||||
end
|
||||
|
||||
-- Отладка: выводим полученные данные
|
||||
print("[MilitaryID] ShowDocument called with data:")
|
||||
PrintTable(data or {})
|
||||
|
||||
local frame = vgui.Create("DFrame")
|
||||
frame:SetSize(500, 400)
|
||||
frame:SetTitle("")
|
||||
frame:Center()
|
||||
frame:ShowCloseButton(false)
|
||||
frame:MakePopup()
|
||||
frame.Paint = function(s, w, h)
|
||||
-- Фон документа
|
||||
draw.RoundedBox(8, 0, 0, w, h, Color(230, 220, 200))
|
||||
|
||||
-- Рамка
|
||||
surface.SetDrawColor(100, 50, 0)
|
||||
surface.DrawOutlinedRect(5, 25, w - 10, h - 30, 3)
|
||||
|
||||
-- Заголовок
|
||||
draw.SimpleText("ВОЕННЫЙ БИЛЕТ", "DermaLarge", w/2, 40, Color(100, 50, 0), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
local infoPanel = vgui.Create("DPanel", frame)
|
||||
infoPanel:SetPos(30, 80)
|
||||
infoPanel:SetSize(440, 280)
|
||||
infoPanel.Paint = nil
|
||||
|
||||
local yPos = 0
|
||||
local fields = {
|
||||
{"Имя:", data and data.name or "Не указано"},
|
||||
{"Фракция:", data and data.faction or "Не указано"},
|
||||
{"Подразделение:", data and data.subdivision or "Не указано"},
|
||||
{"Специализация:", data and data.specialization or "Не указано"},
|
||||
{"Звание:", data and data.rank or "Не указано"},
|
||||
{"Розыск:", (data and data.wanted_fsb and data.wanted_sbu) and "В РОЗЫСКЕ ФСБ И СБУ" or (data and data.wanted_fsb) and "В РОЗЫСКЕ ФСБ" or (data and data.wanted_sbu) and "В РОЗЫСКЕ СБУ" or "Чист"}
|
||||
}
|
||||
|
||||
for _, field in ipairs(fields) do
|
||||
local label = vgui.Create("DLabel", infoPanel)
|
||||
label:SetPos(0, yPos)
|
||||
label:SetFont("DermaDefaultBold")
|
||||
label:SetText(field[1])
|
||||
label:SetTextColor(Color(50, 50, 50))
|
||||
label:SizeToContents()
|
||||
|
||||
local value = vgui.Create("DLabel", infoPanel)
|
||||
value:SetPos(150, yPos)
|
||||
value:SetFont("DermaDefault")
|
||||
value:SetText(field[2])
|
||||
|
||||
-- Make the "Wanted" value red if wanted
|
||||
if field[1] == "Розыск:" and data and (data.wanted_fsb or data.wanted_sbu) then
|
||||
value:SetTextColor(Color(200, 0, 0))
|
||||
value:SetFont("DermaDefaultBold")
|
||||
else
|
||||
value:SetTextColor(Color(0, 0, 0))
|
||||
end
|
||||
|
||||
value:SizeToContents()
|
||||
|
||||
yPos = yPos + 35
|
||||
end
|
||||
|
||||
-- Visual Wanted Stamp
|
||||
if data and (data.wanted_fsb or data.wanted_sbu) then
|
||||
local stamp = vgui.Create("DPanel", frame)
|
||||
stamp:SetSize(180, 60)
|
||||
stamp:SetPos(300, 280)
|
||||
stamp.Paint = function(s, w, h)
|
||||
local x, y = s:LocalToScreen(0, 0)
|
||||
local matrix = Matrix()
|
||||
matrix:Translate(Vector(x + w / 2, y + h / 2, 0))
|
||||
matrix:Rotate(Angle(0, 15, 0))
|
||||
matrix:Translate(Vector(-(x + w / 2), -(y + h / 2), 0))
|
||||
|
||||
cam.PushModelMatrix(matrix)
|
||||
surface.SetDrawColor(200, 0, 0, 150)
|
||||
surface.DrawOutlinedRect(x, y, w, h, 4)
|
||||
draw.SimpleText("В РОЗЫСКЕ", "DermaLarge", x + w / 2, y + h / 2, Color(200, 0, 0, 200), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
cam.PopModelMatrix()
|
||||
end
|
||||
end
|
||||
|
||||
local closeBtn = vgui.Create("DButton", frame)
|
||||
closeBtn:SetPos(175, 350)
|
||||
closeBtn:SetSize(150, 30)
|
||||
closeBtn:SetText("Закрыть")
|
||||
closeBtn.DoClick = function()
|
||||
frame:Close()
|
||||
end
|
||||
|
||||
frame.OnRemove = function()
|
||||
net.Start("ixMilitaryID_StopView")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
self.documentFrame = frame
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "Военкомат"
|
||||
ENT.Category = "[FT] Система документов"
|
||||
ENT.Spawnable = true
|
||||
ENT.AdminOnly = true
|
||||
ENT.RenderGroup = RENDERGROUP_BOTH
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function ENT:Initialize()
|
||||
self:SetModel("models/props_lab/monitor02.mdl")
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_NONE)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Use(activator, caller)
|
||||
if not IsValid(activator) or not activator:IsPlayer() then
|
||||
return
|
||||
end
|
||||
|
||||
local character = activator:GetCharacter()
|
||||
if not character then
|
||||
return
|
||||
end
|
||||
|
||||
-- Проверяем, есть ли уже военный билет
|
||||
local inventory = character:GetInventory()
|
||||
if inventory then
|
||||
for _, item in pairs(inventory:GetItems()) do
|
||||
if item.uniqueID == "military_id" then
|
||||
activator:Notify("У вас уже есть военный билет!")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Открываем опросник
|
||||
net.Start("ixMilitaryID_OpenQuiz")
|
||||
net.Send(activator)
|
||||
end
|
||||
else
|
||||
function ENT:Draw()
|
||||
self:DrawModel()
|
||||
|
||||
local plugin = ix.plugin.Get("military_id")
|
||||
if not plugin then return end
|
||||
|
||||
local pos = self:GetPos() + self:GetUp() * 30
|
||||
local ang = self:GetAngles()
|
||||
|
||||
ang:RotateAroundAxis(ang:Forward(), 90)
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
|
||||
cam.Start3D2D(pos, ang, 0.1)
|
||||
draw.SimpleTextOutlined(
|
||||
plugin.terminalText or "ВОЕНКОМАТ",
|
||||
"DermaLarge",
|
||||
0, 0,
|
||||
Color(255, 255, 255),
|
||||
TEXT_ALIGN_CENTER,
|
||||
TEXT_ALIGN_CENTER,
|
||||
2,
|
||||
Color(0, 0, 0)
|
||||
)
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,129 @@
|
||||
ITEM.name = "Военный билет"
|
||||
ITEM.description = "Документ, удостоверяющий военную службу."
|
||||
ITEM.model = "models/props_c17/paper01.mdl"
|
||||
ITEM.width = 1
|
||||
ITEM.height = 1
|
||||
ITEM.category = "Документы"
|
||||
|
||||
-- Функция предъявления документа
|
||||
ITEM.functions.Show = {
|
||||
name = "Предъявить",
|
||||
tip = "Показать военный билет игроку перед собой",
|
||||
icon = "icon16/eye.png",
|
||||
OnRun = function(item)
|
||||
if CLIENT then return false end
|
||||
|
||||
local client = item.player
|
||||
|
||||
-- Кулдаун
|
||||
if (client.ixNextIDShow or 0) > CurTime() then
|
||||
client:Notify("Подождите " .. math.ceil(client.ixNextIDShow - CurTime()) .. " сек. перед повторным показом")
|
||||
return false
|
||||
end
|
||||
|
||||
-- Трассировка взгляда от лица игрока (на сервере)
|
||||
local trace = client:GetEyeTrace()
|
||||
local target = trace.Entity
|
||||
|
||||
-- Проверяем что смотрим на игрока и он близко
|
||||
if IsValid(target) and target:IsPlayer() and target ~= client and target:GetPos():Distance(client:GetPos()) <= 200 then
|
||||
-- Проверка, не смотрит ли уже цель чужой военник
|
||||
if target.ixIsViewingID then
|
||||
client:Notify("Этот человек уже изучает другой документ")
|
||||
return false
|
||||
end
|
||||
|
||||
local character = client:GetCharacter()
|
||||
if not character then return false end
|
||||
|
||||
local factionTable = ix.faction.Get(character:GetFaction())
|
||||
local podrData = factionTable and factionTable.Podr and factionTable.Podr[character:GetPodr()]
|
||||
local specData = factionTable and factionTable.Spec and factionTable.Spec[character:GetSpec()]
|
||||
|
||||
local data = {
|
||||
name = character:GetName(),
|
||||
faction = factionTable and factionTable.name or "Неизвестно",
|
||||
subdivision = podrData and podrData.name or "Неизвестно",
|
||||
specialization = specData and specData.name or "Неизвестно",
|
||||
rank = client.GetRankName and client:GetRankName() or "Неизвестно",
|
||||
wanted = character:GetData("wanted", false),
|
||||
wanted_fsb = character:GetData("wanted_fsb", false),
|
||||
wanted_sbu = character:GetData("wanted_sbu", false)
|
||||
}
|
||||
|
||||
-- Устанавливаем статус просмотра для цели
|
||||
target.ixIsViewingID = true
|
||||
-- Ставим кулдаун отправителю
|
||||
client.ixNextIDShow = CurTime() + 5
|
||||
|
||||
-- Отправляем данные документа целевому игроку
|
||||
net.Start("ixMilitaryID_ViewDocument")
|
||||
net.WriteTable(data)
|
||||
net.Send(target)
|
||||
|
||||
client:Notify("Вы показали военный билет игроку " .. target:GetName())
|
||||
else
|
||||
client:Notify("Посмотрите на игрока поблизости")
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(item)
|
||||
return !IsValid(item.entity)
|
||||
end
|
||||
}
|
||||
|
||||
-- Функция просмотра своего документа
|
||||
ITEM.functions.View = {
|
||||
name = "Просмотреть",
|
||||
tip = "Посмотреть содержимое военного билета",
|
||||
icon = "icon16/page_white_text.png",
|
||||
OnRun = function(item)
|
||||
local client = item.player
|
||||
|
||||
if SERVER then
|
||||
local character = client:GetCharacter()
|
||||
if not character then return false end
|
||||
|
||||
local factionTable = ix.faction.Get(character:GetFaction())
|
||||
local podrData = factionTable and factionTable.Podr and factionTable.Podr[character:GetPodr()]
|
||||
local specData = factionTable and factionTable.Spec and factionTable.Spec[character:GetSpec()]
|
||||
|
||||
local data = {
|
||||
name = character:GetName(),
|
||||
faction = factionTable and factionTable.name or "Неизвестно",
|
||||
subdivision = podrData and podrData.name or "Неизвестно",
|
||||
specialization = specData and specData.name or "Неизвестно",
|
||||
rank = client.GetRankName and client:GetRankName() or "Неизвестно",
|
||||
wanted = character:GetData("wanted", false),
|
||||
wanted_fsb = character:GetData("wanted_fsb", false),
|
||||
wanted_sbu = character:GetData("wanted_sbu", false)
|
||||
}
|
||||
|
||||
net.Start("ixMilitaryID_ViewDocument")
|
||||
net.WriteTable(data)
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(item)
|
||||
return !IsValid(item.entity)
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- Запрет на выброс документа
|
||||
ITEM.noDrop = true
|
||||
|
||||
-- Кастомное отображение в инвентаре
|
||||
if CLIENT then
|
||||
function ITEM:PaintOver(item, w, h)
|
||||
-- Отображаем маленькую иконку документа
|
||||
surface.SetDrawColor(200, 200, 150, 255)
|
||||
surface.DrawRect(w - 16, h - 16, 12, 12)
|
||||
|
||||
draw.SimpleText("ID", "DermaDefaultBold", w - 10, h - 10, Color(100, 50, 0), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
PLUGIN.quizQuestions = {
|
||||
{
|
||||
question = "Какова ваша основная задача в подразделении?",
|
||||
answers = {
|
||||
"Выполнение боевых задач",
|
||||
"Охрана объектов",
|
||||
"Медицинская поддержка",
|
||||
"Техническое обслуживание",
|
||||
"Разведка и наблюдение"
|
||||
},
|
||||
correctAnswer = 1 -- Индекс правильного ответа (необязательно, можно убрать проверку)
|
||||
},
|
||||
{
|
||||
question = "Вы готовы соблюдать воинскую дисциплину?",
|
||||
answers = {
|
||||
"Да, готов",
|
||||
"Нет, не готов"
|
||||
},
|
||||
correctAnswer = 1
|
||||
},
|
||||
{
|
||||
question = "Ваш опыт службы:",
|
||||
answers = {
|
||||
"Новобранец",
|
||||
"Опытный боец",
|
||||
"Ветеран",
|
||||
"Офицер запаса",
|
||||
"Без опыта"
|
||||
}
|
||||
},
|
||||
{
|
||||
question = "Почему вы хотите вступить в ряды вооруженных сил?",
|
||||
answers = {
|
||||
"Защита Родины",
|
||||
"Карьерный рост",
|
||||
"Семейная традиция",
|
||||
"Долг перед страной"
|
||||
}
|
||||
},
|
||||
{
|
||||
question = "Готовы ли вы следовать приказам командования?",
|
||||
answers = {
|
||||
"Да, безусловно",
|
||||
"Да, но с оговорками",
|
||||
"Нет"
|
||||
},
|
||||
correctAnswer = 1
|
||||
}
|
||||
}
|
||||
|
||||
-- Минимальный балл для прохождения (если используется проверка)
|
||||
PLUGIN.minimumScore = 3
|
||||
|
||||
-- Текст на терминале
|
||||
PLUGIN.terminalText = "ВОЕНКОМАТ\nНажмите E для прохождения опроса"
|
||||
@@ -0,0 +1,17 @@
|
||||
PLUGIN.name = "Military ID"
|
||||
PLUGIN.author = "RefoselTeamWork"
|
||||
PLUGIN.description = "Система военных билетов с опросом"
|
||||
|
||||
ix.util.Include("sh_config.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixMilitaryID_ShowToPlayer")
|
||||
util.AddNetworkString("ixMilitaryID_ViewDocument")
|
||||
util.AddNetworkString("ixMilitaryID_OpenQuiz")
|
||||
util.AddNetworkString("ixMilitaryID_SubmitQuiz")
|
||||
util.AddNetworkString("ixMilitaryID_EditDocument")
|
||||
util.AddNetworkString("ixMilitaryID_SaveEdit")
|
||||
util.AddNetworkString("ixMilitaryID_StopView")
|
||||
end
|
||||
263
garrysmod/gamemodes/militaryrp/plugins/military_id/sv_plugin.lua
Normal file
263
garrysmod/gamemodes/militaryrp/plugins/military_id/sv_plugin.lua
Normal file
@@ -0,0 +1,263 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
-- Обработка отправки опроса
|
||||
net.Receive("ixMilitaryID_SubmitQuiz", function(len, client)
|
||||
local answers = net.ReadTable()
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if not character then return end
|
||||
|
||||
-- Проверка правильности ответов (опционально)
|
||||
local score = 0
|
||||
for i, answer in ipairs(answers) do
|
||||
local question = PLUGIN.quizQuestions[i]
|
||||
if question.correctAnswer and answer == question.correctAnswer then
|
||||
score = score + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Если есть минимальный балл
|
||||
if PLUGIN.minimumScore and score < PLUGIN.minimumScore then
|
||||
client:Notify("Вы не прошли опрос. Попробуйте снова.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Создание военного билета
|
||||
local inventory = character:GetInventory()
|
||||
if not inventory or isnumber(inventory) then
|
||||
client:Notify("Инвентарь еще не загрузился, подождите немного...")
|
||||
return
|
||||
end
|
||||
|
||||
-- Проверяем, есть ли уже военный билет
|
||||
for _, item in pairs(inventory:GetItems()) do
|
||||
if item.uniqueID == "military_id" then
|
||||
client:Notify("У вас уже есть военный билет!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Выдаем пустой военный билет (данные заполнятся при первом просмотре)
|
||||
inventory:Add("military_id")
|
||||
client:Notify("Вы получили военный билет!")
|
||||
end)
|
||||
|
||||
-- Показать документ другому игроку
|
||||
net.Receive("ixMilitaryID_ShowToPlayer", function(len, client)
|
||||
local targetIndex = net.ReadUInt(16)
|
||||
local itemID = net.ReadUInt(32)
|
||||
print("Received ShowToPlayer request: targetIndex=" .. targetIndex .. ", itemID=" .. itemID)
|
||||
local target = Entity(targetIndex)
|
||||
if not IsValid(target) or not target:IsPlayer() or target:GetPos():Distance(client:GetPos()) > 200 then
|
||||
client:Notify("Игрок слишком далеко!")
|
||||
return
|
||||
end
|
||||
|
||||
if (client.ixNextIDShow or 0) > CurTime() then
|
||||
client:Notify("Подождите " .. math.ceil(client.ixNextIDShow - CurTime()) .. " сек. перед повторным показом")
|
||||
return
|
||||
end
|
||||
|
||||
if target.ixIsViewingID then
|
||||
client:Notify("Этот человек уже изучает другой документ")
|
||||
return
|
||||
end
|
||||
|
||||
local item = ix.item.instances[itemID]
|
||||
if not item or item.player ~= client then
|
||||
return
|
||||
end
|
||||
|
||||
-- Берем актуальные данные персонажа
|
||||
local character = client:GetCharacter()
|
||||
if not character then return end
|
||||
|
||||
local factionTable = ix.faction.Get(character:GetFaction())
|
||||
local podrData = factionTable and factionTable.Podr and factionTable.Podr[character:GetPodr()]
|
||||
local specData = factionTable and factionTable.Spec and factionTable.Spec[character:GetSpec()]
|
||||
|
||||
local data = {
|
||||
name = character:GetName(),
|
||||
faction = factionTable and factionTable.name or "Неизвестно",
|
||||
subdivision = podrData and podrData.name or "Неизвестно",
|
||||
specialization = specData and specData.name or "Неизвестно",
|
||||
rank = client.GetRankName and client:GetRankName() or "Неизвестно",
|
||||
wanted = character:GetData("wanted", false)
|
||||
}
|
||||
|
||||
|
||||
-- Устанавливаем статус просмотра для цели
|
||||
target.ixIsViewingID = true
|
||||
-- Ставим кулдаун отправителю
|
||||
client.ixNextIDShow = CurTime() + 5
|
||||
|
||||
-- Отправляем данные документа целевому игроку
|
||||
net.Start("ixMilitaryID_ViewDocument")
|
||||
net.WriteTable(data)
|
||||
net.Send(target)
|
||||
|
||||
client:Notify("Вы предъявили военный билет игроку " .. target:Name())
|
||||
target:Notify(client:Name() .. " предъявил вам военный билет")
|
||||
end)
|
||||
|
||||
-- Сохранение отредактированного документа
|
||||
net.Receive("ixMilitaryID_SaveEdit", function(len, client)
|
||||
if not client:IsAdmin() then
|
||||
client:Notify("У вас нет прав для редактирования!")
|
||||
return
|
||||
end
|
||||
|
||||
local itemID = net.ReadUInt(32)
|
||||
local newData = net.ReadTable()
|
||||
|
||||
local item = ix.item.instances[itemID]
|
||||
if not item or item.uniqueID ~= "military_id" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Обновляем данные
|
||||
for key, value in pairs(newData) do
|
||||
item:SetData(key, value)
|
||||
end
|
||||
|
||||
client:Notify("Военный билет успешно отредактирован!")
|
||||
|
||||
-- Если владелец онлайн, уведомляем его
|
||||
if IsValid(item.player) and item.player ~= client then
|
||||
item.player:Notify("Ваш военный билет был отредактирован администратором")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Сброс состояния просмотра документа
|
||||
net.Receive("ixMilitaryID_StopView", function(len, client)
|
||||
client.ixIsViewingID = nil
|
||||
end)
|
||||
|
||||
function PLUGIN:PlayerDisconnected(client)
|
||||
client.ixIsViewingID = nil
|
||||
end
|
||||
|
||||
function PLUGIN:OnCharacterLoaded(character)
|
||||
local ply = character:GetPlayer()
|
||||
if IsValid(ply) then
|
||||
ply:SetNWBool("ix_wanted", character:GetData("wanted", false))
|
||||
end
|
||||
end
|
||||
|
||||
-- Консольная команда для предъявления военного билета другому игроку
|
||||
concommand.Add("show_military_id_to_player", function(ply)
|
||||
local character = ply:GetCharacter()
|
||||
if not character then
|
||||
ply:Notify("У вас нет активного персонажа!")
|
||||
return
|
||||
end
|
||||
|
||||
-- Проверяем наличие военного билета в инвентаре
|
||||
local inventory = character:GetInventory()
|
||||
local hasItem = false
|
||||
if inventory then
|
||||
for _, item in pairs(inventory:GetItems()) do
|
||||
if item.uniqueID == "military_id" then
|
||||
hasItem = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not hasItem then
|
||||
ply:Notify("У вас нет военного билета! Вам нужно его получить.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Трассировка взгляда от лица игрока
|
||||
local trace = ply:GetEyeTrace()
|
||||
local target = trace.Entity
|
||||
|
||||
-- Проверяем что смотрим на игрока и он близко
|
||||
if IsValid(target) and target:IsPlayer() and target ~= ply and target:GetPos():Distance(ply:GetPos()) <= 200 then
|
||||
if (ply.ixNextIDShow or 0) > CurTime() then
|
||||
ply:Notify("Подождите " .. math.ceil(ply.ixNextIDShow - CurTime()) .. " сек. перед повторным показом")
|
||||
return
|
||||
end
|
||||
|
||||
if target.ixIsViewingID then
|
||||
ply:Notify("Этот человек уже изучает другой документ")
|
||||
return
|
||||
end
|
||||
local factionTable = ix.faction.Get(character:GetFaction())
|
||||
local podrData = factionTable and factionTable.Podr and factionTable.Podr[character:GetPodr()]
|
||||
local specData = factionTable and factionTable.Spec and factionTable.Spec[character:GetSpec()]
|
||||
|
||||
local data = {
|
||||
name = character:GetName(),
|
||||
faction = factionTable and factionTable.name or "Неизвестно",
|
||||
subdivision = podrData and podrData.name or "Неизвестно",
|
||||
specialization = specData and specData.name or "Неизвестно",
|
||||
rank = ply.GetRankName and ply:GetRankName() or "Неизвестно",
|
||||
wanted = character:GetData("wanted", false),
|
||||
wanted_fsb = character:GetData("wanted_fsb", false),
|
||||
wanted_sbu = character:GetData("wanted_sbu", false)
|
||||
}
|
||||
|
||||
|
||||
-- Устанавливаем статус просмотра для цели
|
||||
target.ixIsViewingID = true
|
||||
-- Ставим кулдаун отправителю
|
||||
ply.ixNextIDShow = CurTime() + 5
|
||||
|
||||
-- Отправляем данные документа целевому игроку
|
||||
net.Start("ixMilitaryID_ViewDocument")
|
||||
net.WriteTable(data)
|
||||
net.Send(target)
|
||||
|
||||
ply:Notify("Вы показали военный билет игроку " .. target:GetName())
|
||||
else
|
||||
ply:Notify("Посмотрите на игрока поблизости")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Консольная команда для показа военного билета себе
|
||||
concommand.Add("show_military_id", function(ply)
|
||||
local character = ply:GetCharacter()
|
||||
if not character then
|
||||
ply:Notify("У вас нет активного персонажа!")
|
||||
return
|
||||
end
|
||||
|
||||
-- Проверяем наличие военного билета в инвентаре
|
||||
local inventory = character:GetInventory()
|
||||
local hasItem = false
|
||||
if inventory then
|
||||
for _, item in pairs(inventory:GetItems()) do
|
||||
if item.uniqueID == "military_id" then
|
||||
hasItem = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not hasItem then
|
||||
ply:Notify("У вас нет военного билета! Вам нужно его получить.")
|
||||
return
|
||||
end
|
||||
|
||||
local factionTable = ix.faction.Get(character:GetFaction())
|
||||
local podrData = factionTable and factionTable.Podr and factionTable.Podr[character:GetPodr()]
|
||||
local specData = factionTable and factionTable.Spec and factionTable.Spec[character:GetSpec()]
|
||||
|
||||
local data = {
|
||||
name = character:GetName(),
|
||||
faction = factionTable and factionTable.name or "Неизвестно",
|
||||
subdivision = podrData and podrData.name or "Неизвестно",
|
||||
specialization = specData and specData.name or "Неизвестно",
|
||||
rank = ply.GetRankName and ply:GetRankName() or "Неизвестно",
|
||||
wanted = character:GetData("wanted", false),
|
||||
wanted_fsb = character:GetData("wanted_fsb", false),
|
||||
wanted_sbu = character:GetData("wanted_sbu", false)
|
||||
}
|
||||
|
||||
|
||||
net.Start("ixMilitaryID_ViewDocument")
|
||||
net.WriteTable(data)
|
||||
net.Send(ply)
|
||||
end)
|
||||
Reference in New Issue
Block a user