-- Масштабирование для меню local function GetScale() local scale = ScrH() / 1080 return math.Clamp(scale, 0.5, 2.0) end local function ScaleSize(val) return math.max(1, math.Round(val * GetScale())) end local function ScalePos(val) return math.Round(val * GetScale()) end local function CreateMenuFonts() surface.CreateFont("ixMenuButtonFont_Adaptive", { font = "Roboto", size = math.max(ScaleSize(18), 12), weight = 500, antialias = true }) surface.CreateFont("ixMenuButtonHugeFont_Adaptive", { font = "Roboto", size = math.max(ScaleSize(32), 20), weight = 700, antialias = true }) end CreateMenuFonts() hook.Add("OnScreenSizeChanged", "CharMenu_UpdateFonts", CreateMenuFonts) local CharMenu = {} function CharMenu:Init() if IsValid(ix.gui.characterMenu) then ix.gui.characterMenu:Remove() end self:SetSize(ScrW(), ScrH()) self:SetPos(0, 0) -- Переменные для совместимости self.bClosing = false self.currentAlpha = 255 self.bMenuShouldClose = false -- Проверяем наличие персонажа self:CheckCharacterAndLoad() ix.gui.characterMenu = self ix.gui.menu = self -- Для совместимости self:MakePopup() self:SetKeyboardInputEnabled(true) self:SetMouseInputEnabled(true) end function CharMenu:CheckCharacterAndLoad() -- Ждем немного пока загрузятся данные персонажей timer.Simple(0.5, function() if not IsValid(self) then return end local hasCharacter = LocalPlayer().GetCharacter and LocalPlayer():GetCharacter() if hasCharacter then -- Если персонаж уже загружен, закрываем меню print("Персонаж уже загружен, закрываем меню") self:Close(true) return end local characters = ix.characters if (#characters > 0) then local firstChar = characters[1] if not firstChar then return end net.Start("ixCharacterChoose") net.WriteUInt(firstChar, 32) net.SendToServer() else -- Если персонажей нет, показываем выбор стороны print("Персонажей нет, показываем выбор фракции") self:ShowFactionSelection() end end) end function CharMenu:ShowFactionSelection() -- Очищаем предыдущие панели if IsValid(self.currentPanel) then self.currentPanel:Remove() end -- Создаем панель выбора фракции self.currentPanel = vgui.Create("DPanel", self) self.currentPanel:SetSize(ScrW(), ScrH()) self.currentPanel:SetPos(0, 0) -- Фон local backgroundImage = vgui.Create("DImage", self.currentPanel) backgroundImage:SetSize(ScrW(), ScrH()) backgroundImage:SetPos(0, 0) backgroundImage:SetImage("ft_ui/military/vnu/charcreate/bg.png") -- Верхняя панель CreateMenuFonts() local topBar = vgui.Create("DPanel", self.currentPanel) topBar:SetSize(ScrW() + 2, ScaleSize(100)) topBar:SetPos(-1, 0) topBar.Paint = function(panel, w, h) surface.SetDrawColor(Color(13, 13, 13)) surface.DrawRect(0, 0, w, h) end -- Логотип local logo = vgui.Create("DImage", self.currentPanel) logo:SetSize(ScaleSize(150), ScaleSize(53)) logo:SetPos(ScrW()/2 - ScaleSize(75), ScalePos(23)) logo:SetImage("ft_ui/military/vnu/charcreate/logo.png") -- Заголовок local title = vgui.Create("DLabel", self.currentPanel) title:SetText("ВЫБОР СТОРОНЫ") title:SetFont("ixMenuButtonHugeFont_Adaptive") title:SetTextColor(color_white) title:SizeToContents() title:SetPos(ScrW()/2 - title:GetWide()/2, ScalePos(187)) -- Панель ВСУ local ukrainePanel = vgui.Create("DPanel", self.currentPanel) ukrainePanel:SetSize(ScaleSize(425), ScaleSize(500)) ukrainePanel:SetPos(ScrW()/2 - ScaleSize(425) - ScaleSize(25), ScalePos(310)) ukrainePanel.Paint = function(panel, w, h) surface.SetDrawColor(Color(0, 0, 0, 180)) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawOutlinedRect(0, 0, w, h, 2) end -- Панель России local russiaPanel = vgui.Create("DPanel", self.currentPanel) russiaPanel:SetSize(ScaleSize(425), ScaleSize(500)) russiaPanel:SetPos(ScrW()/2 + ScaleSize(25), ScalePos(310)) russiaPanel.Paint = function(panel, w, h) surface.SetDrawColor(Color(0, 0, 0, 180)) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawOutlinedRect(0, 0, w, h, 2) end -- Изображения фракций local ukraineImage = vgui.Create("DImage", self.currentPanel) ukraineImage:SetSize(ScaleSize(200), ScaleSize(200)) ukraineImage:SetPos(ScrW()/2 - ScaleSize(425) - ScaleSize(25) + ScaleSize(113), ScalePos(333)) ukraineImage:SetImage("ft_ui/military/vnu/charcreate/ukraine.png") local russiaImage = vgui.Create("DImage", self.currentPanel) russiaImage:SetSize(ScaleSize(295), ScaleSize(200)) russiaImage:SetPos(ScrW()/2 + ScaleSize(25) + ScaleSize(65), ScalePos(333)) russiaImage:SetImage("ft_ui/military/vnu/charcreate/russia.png") -- Кнопки выбора local ukraineButton = vgui.Create("DButton", self.currentPanel) ukraineButton:SetSize(ScaleSize(425), ScaleSize(75)) ukraineButton:SetPos(ScrW()/2 - ScaleSize(425) - ScaleSize(25), ScalePos(818)) ukraineButton:SetText("") ukraineButton.Paint = function(panel, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(Color(29, 29, 29)) surface.DrawOutlinedRect(0, 0, w, h, 3) draw.SimpleText("УКРАИНА", "ixMenuButtonHugeFont_Adaptive", w/2, h/2, Color(255, 255, 255, 218), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) if panel:IsHovered() then surface.SetDrawColor(Color(255, 255, 255, 30)) surface.DrawRect(0, 0, w, h) end end ukraineButton.DoClick = function() self:ShowCharacterCreation(FACTION_UKRAINE) end local russiaButton = vgui.Create("DButton", self.currentPanel) russiaButton:SetSize(ScaleSize(425), ScaleSize(75)) russiaButton:SetPos(ScrW()/2 + ScaleSize(25), ScalePos(818)) russiaButton:SetText("") russiaButton.Paint = function(panel, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(Color(29, 29, 29)) surface.DrawOutlinedRect(0, 0, w, h, 3) draw.SimpleText("РОССИЯ", "ixMenuButtonHugeFont_Adaptive", w/2, h/2, Color(255, 255, 255, 218), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) if panel:IsHovered() then surface.SetDrawColor(Color(255, 255, 255, 30)) surface.DrawRect(0, 0, w, h) end end russiaButton.DoClick = function() self:ShowCharacterCreation(FACTION_RUSSIAN) end end function CharMenu:ShowCharacterCreation(faction) -- Очищаем предыдущие панели if IsValid(self.currentPanel) then self.currentPanel:Remove() end self.currentPanel = vgui.Create("DPanel", self) self.currentPanel:SetSize(ScrW(), ScrH()) self.currentPanel:SetPos(0, 0) -- Фон local backgroundImage = vgui.Create("DImage", self.currentPanel) backgroundImage:SetSize(ScrW(), ScrH()) backgroundImage:SetPos(0, 0) backgroundImage:SetImage("ft_ui/military/vnu/charcreate/bg.png") -- Верхняя панель CreateMenuFonts() local topBar = vgui.Create("DPanel", self.currentPanel) topBar:SetSize(ScrW() + 2, ScaleSize(100)) topBar:SetPos(-1, 0) topBar.Paint = function(panel, w, h) surface.SetDrawColor(Color(13, 13, 13)) surface.DrawRect(0, 0, w, h) end -- Логотип local logo = vgui.Create("DImage", self.currentPanel) logo:SetSize(ScaleSize(150), ScaleSize(53)) logo:SetPos(ScrW()/2 - ScaleSize(75), ScalePos(23)) logo:SetImage("ft_ui/military/vnu/charcreate/logo.png") -- Основная панель local mainPanel = vgui.Create("DPanel", self.currentPanel) mainPanel:SetSize(ScaleSize(1500), ScaleSize(750)) mainPanel:SetPos(ScrW()/2 - ScaleSize(750), ScalePos(199)) mainPanel.Paint = function(panel, w, h) surface.SetDrawColor(Color(13, 13, 13, 200)) surface.DrawRect(0, 0, w, h) end -- Заголовки local title = vgui.Create("DLabel", self.currentPanel) title:SetText("СОЗДАНИЕ ПЕРСОНАЖА") title:SetFont("ixMenuButtonHugeFont_Adaptive") title:SetTextColor(color_white) title:SizeToContents() title:SetPos(ScrW()/2 - title:GetWide()/2, ScalePos(224)) -- Разделители local divider1 = vgui.Create("DPanel", self.currentPanel) divider1:SetSize(ScaleSize(770), ScaleSize(2)) divider1:SetPos(ScrW()/2 - ScaleSize(385), ScalePos(314)) divider1.Paint = function(panel, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawRect(0, 0, w, h) end -- Поля ввода local startX = ScrW()/2 - ScaleSize(420) -- Имя local nameLabel = vgui.Create("DLabel", self.currentPanel) nameLabel:SetText("Ваше имя") nameLabel:SetFont("ixMenuButtonFont_Adaptive") nameLabel:SetTextColor(color_white) nameLabel:SizeToContents() nameLabel:SetPos(startX, ScalePos(360)) local nameEntry = vgui.Create("DTextEntry", self.currentPanel) nameEntry:SetSize(ScaleSize(265), ScaleSize(50)) nameEntry:SetPos(startX, ScalePos(401)) nameEntry:SetFont("ixMenuButtonFont_Adaptive") nameEntry:SetTextColor(Color(255, 255, 255, 230)) nameEntry:SetDrawBackground(false) nameEntry.Paint = function(panel, w, h) surface.SetDrawColor(Color(23, 23, 23)) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawOutlinedRect(0, 0, w, h, 1) panel:DrawTextEntryText(Color(255, 255, 255, 230), Color(30, 130, 255), Color(255, 255, 255, 230)) end -- Фамилия local lastNameLabel = vgui.Create("DLabel", self.currentPanel) lastNameLabel:SetText("Ваша фамилия") lastNameLabel:SetFont("ixMenuButtonFont_Adaptive") lastNameLabel:SetTextColor(color_white) lastNameLabel:SizeToContents() lastNameLabel:SetPos(startX + ScaleSize(285), ScalePos(360)) local lastNameEntry = vgui.Create("DTextEntry", self.currentPanel) lastNameEntry:SetSize(ScaleSize(265), ScaleSize(50)) lastNameEntry:SetPos(startX + ScaleSize(285), ScalePos(401)) lastNameEntry:SetFont("ixMenuButtonFont_Adaptive") lastNameEntry:SetTextColor(Color(255, 255, 255, 230)) lastNameEntry:SetDrawBackground(false) lastNameEntry.Paint = nameEntry.Paint -- Позывной local callSignLabel = vgui.Create("DLabel", self.currentPanel) callSignLabel:SetText("Ваш позывной") callSignLabel:SetFont("ixMenuButtonFont_Adaptive") callSignLabel:SetTextColor(color_white) callSignLabel:SizeToContents() callSignLabel:SetPos(startX + ScaleSize(570), ScalePos(360)) local callSignEntry = vgui.Create("DTextEntry", self.currentPanel) callSignEntry:SetSize(ScaleSize(265), ScaleSize(50)) callSignEntry:SetPos(startX + ScaleSize(570), ScalePos(401)) callSignEntry:SetFont("ixMenuButtonFont_Adaptive") callSignEntry:SetTextColor(Color(255, 255, 255, 230)) callSignEntry:SetDrawBackground(false) callSignEntry.Paint = nameEntry.Paint -- Кнопка создания local createButton = vgui.Create("DButton", self.currentPanel) createButton:SetSize(ScaleSize(300), ScaleSize(80)) createButton:SetPos(ScrW()/2 - ScaleSize(150), ScrH() - ScaleSize(120)) createButton:SetText("") createButton.Paint = function(panel, w, h) surface.SetDrawColor(Color(1, 67, 29)) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(Color(29, 29, 29)) surface.DrawOutlinedRect(0, 0, w, h, 3) draw.SimpleText("СОЗДАТЬ", "ixMenuButtonHugeFont_Adaptive", w/2, h/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) if panel:IsHovered() then surface.SetDrawColor(Color(255, 255, 255, 30)) surface.DrawRect(0, 0, w, h) end end createButton.DoClick = function() local name = nameEntry:GetText() local lastName = lastNameEntry:GetText() local callSign = callSignEntry:GetText() if name == "" or lastName == "" or callSign == "" then print("Ошибка: Заполните все поля!") return end local fullName = name .. " " .. lastName .. " '" .. callSign .. "'" print("Создание персонажа: " .. fullName) self:CreateCharacter(faction, fullName) end self.selectedFaction = faction end function CharMenu:CreateCharacter(faction, name) if self.awaitingResponse then print("Уже ожидаем ответ от сервера...") return end -- Подготавливаем данные для создания персонажа local payload = { name = name, faction = faction, model = "models/player/group01/male_04.mdl", description = "Солдат" } print("Отправка запроса на создание персонажа...") PrintTable(payload) -- Отправляем запрос на создание персонажа net.Start("ixCharacterCreate") net.WriteUInt(table.Count(payload), 8) for k, v in pairs(payload) do net.WriteString(k) net.WriteType(v) end net.SendToServer() -- Ждем ответа от сервера self.awaitingResponse = true -- Таймаут на случай проблем с соединением timer.Create("CharCreateTimeout", 10, 1, function() if IsValid(ix.gui.characterMenu) and ix.gui.characterMenu.awaitingResponse then ix.gui.characterMenu.awaitingResponse = false Derma_Message("Таймаут создания персонажа! Проверьте соединение.", "Ошибка", "OK") end end) end -- Методы для совместимости с Helix function CharMenu:IsClosing() return self.bClosing or false end function CharMenu:GetAlpha() return self.currentAlpha or 255 end function CharMenu:GetCharacterOverview() -- Заглушка для совместимости return nil end function CharMenu:Close(bFromMenu) self.bClosing = true if ix.gui.loading then ix.gui.loading:Remove() end self:Remove() end function CharMenu:Paint(w, h) -- Черный фон surface.SetDrawColor(Color(0, 0, 0, 255)) surface.DrawRect(0, 0, w, h) end vgui.Register("ixCharMenu", CharMenu, "EditablePanel") -- Исправляем CalcView hook hook.Add("CalcView", "MilitaryRPMenuFix", function(ply, origin, angles, fov) local menu = ix.gui.characterMenu or ix.gui.menu if IsValid(menu) and not menu:IsClosing() then -- Возвращаем стандартный вид, чтобы избежать ошибок return { origin = origin, angles = angles, fov = fov, drawviewer = false } end end) -- Глобальные сетевые обработчики (устанавливаются один раз при загрузке файла) net.Receive("ixCharacterLoad", function() local characters = net.ReadUInt(8) local charList = {} for i = 1, characters do charList[i] = net.ReadUInt(32) end ix.characters = charList print("Получен список персонажей: ", table.concat(charList, ", ")) -- Если меню открыто, обновляем его if IsValid(ix.gui.characterMenu) then ix.gui.characterMenu:CheckCharacterAndLoad() end end) net.Receive("ixCharacterAuthed", function() timer.Remove("CharCreateTimeout") local id = net.ReadUInt(32) local characters = net.ReadUInt(8) local charList = {} for i = 1, characters do charList[i] = net.ReadUInt(32) end ix.characters = charList print("Персонаж успешно создан! ID: " .. id) print("Теперь доступно персонажей: " .. characters) -- Автоматически выбираем созданного персонажа if id then print("Автовыбор персонажа ID: " .. id) net.Start("ixCharacterChoose") net.WriteUInt(id, 32) net.SendToServer() end -- Закрываем меню если оно открыто if IsValid(ix.gui.characterMenu) then ix.gui.characterMenu.awaitingResponse = false ix.gui.characterMenu:Close(true) end end) net.Receive("ixCharacterAuthFailed", function() timer.Remove("CharCreateTimeout") local fault = net.ReadString() local args = net.ReadTable() print("Ошибка создания персонажа: " .. fault) PrintTable(args) -- Показываем ошибку пользователю if IsValid(ix.gui.characterMenu) then ix.gui.characterMenu.awaitingResponse = false Derma_Message("Ошибка создания персонажа: " .. fault, "Ошибка", "OK") end end)