---- Traitor equipment menu local GetTranslation = LANG.GetTranslation local GetPTranslation = LANG.GetParamTranslation -- Buyable weapons are loaded automatically. Buyable items are defined in -- equip_items_shd.lua local Equipment = nil function GetEquipmentForRole(role) -- need to build equipment cache? if not Equipment then -- start with all the non-weapon goodies local tbl = table.Copy(EquipmentItems) -- find buyable weapons to load info from for k, v in pairs(weapons.GetList()) do if v and v.CanBuy then local data = v.EquipMenuData or {} local base = { id = v.Classname, name = v.PrintName or "Unnamed", limited = v.LimitedStock, kind = v.Kind or WEAPON_NONE, slot = (v.Slot or 0) + 1, material = v.Icon or "VGUI/ttt/icon_id", -- the below should be specified in EquipMenuData, in which case -- these values are overwritten type = "Type not specified", model = "models/weapons/w_bugbait.mdl", desc = "No description specified." }; -- Force material to nil so that model key is used when we are -- explicitly told to do so (ie. material is false rather than nil). if data.modelicon then base.material = nil end table.Merge(base, data) -- add this buyable weapon to all relevant equipment tables for _, r in pairs(v.CanBuy) do table.insert(tbl[r], base) end end end -- mark custom items for r, is in pairs(tbl) do for _, i in pairs(is) do if i and i.id then i.custom = not table.HasValue(DefaultEquipment[r], i.id) end end end Equipment = tbl end return Equipment and Equipment[role] or {} end local function ItemIsWeapon(item) return not tonumber(item.id) end local function CanCarryWeapon(item) return LocalPlayer():CanCarryType(item.kind) end local color_bad = Color(220, 60, 60, 255) local color_good = Color(0, 200, 0, 255) -- Creates tabel of labels showing the status of ordering prerequisites local function PreqLabels(form) local tbl = {} tbl.credits = vgui.Create("DLabel", form) tbl.credits:SetToolTip(GetTranslation("equip_help_cost")) tbl.credits.Check = function(s, sel) local credits = LocalPlayer():GetCredits() return credits > 0, GetPTranslation("equip_cost", {num = credits}) end tbl.owned = vgui.Create("DLabel", form) tbl.owned:SetToolTip(GetTranslation("equip_help_carry")) tbl.owned.Check = function(s, sel) if ItemIsWeapon(sel) and (not CanCarryWeapon(sel)) then return false, GetPTranslation("equip_carry_slot", {slot = sel.slot}) elseif (not ItemIsWeapon(sel)) and LocalPlayer():HasEquipmentItem(sel.id) then return false, GetTranslation("equip_carry_own") else return true, GetTranslation("equip_carry") end end tbl.bought = vgui.Create("DLabel", form) tbl.bought:SetToolTip(GetTranslation("equip_help_stock")) tbl.bought.Check = function(s, sel) if sel.limited and LocalPlayer():HasBought(tostring(sel.id)) then return false, GetTranslation("equip_stock_deny") else return true, GetTranslation("equip_stock_ok") end end for k, pnl in pairs(tbl) do pnl:SetFont("TabLarge") form:AddItem(pnl) end return function(selected) local allow = true for k, pnl in pairs(tbl) do local result, text = pnl:Check(selected) pnl:SetTextColor(result and color_good or color_bad) pnl:SetText(text) allow = allow and result end return allow end end -- quick, very basic override of DPanelSelect local PANEL = {} local function DrawSelectedEquipment(pnl) surface.SetDrawColor(255, 200, 0, 255) surface.DrawOutlinedRect(0, 0, pnl:GetWide(), pnl:GetTall()) end function PANEL:SelectPanel(pnl) self.BaseClass.SelectPanel(self, pnl) if pnl then pnl.PaintOver = DrawSelectedEquipment end end vgui.Register("EquipSelect", PANEL, "DPanelSelect") -- need an OnTabChanged hook in propertysheet local PANEL = {} function PANEL:OnTabChanged(old, new) end function PANEL:SetActiveTab(new) local old = self.m_pActiveTab if old == new then return end if old then self.animFade:Start( self:GetFadeTime(), { OldTab = old, NewTab = new } ) end self.m_pActiveTab = new self:OnTabChanged(old, new) self:InvalidateLayout() end vgui.Register("EquipPropertySheet", PANEL, "DPropertySheet") local SafeTranslate = LANG.TryTranslation local color_darkened = Color(255,255,255, 80) -- TODO: make set of global role colour defs, these are same as wepswitch local color_slot = { [ROLE_TRAITOR] = Color(180, 50, 40, 255), [ROLE_DETECTIVE] = Color(50, 60, 180, 255) }; local eqframe = nil local function TraitorMenuPopup() local ply = LocalPlayer() if not IsValid(ply) or not ply:IsActiveSpecial() then return end -- Close any existing traitor menu if eqframe and ValidPanel(eqframe) then eqframe:Close() end local credits = ply:GetCredits() local can_order = credits > 0 local dframe = vgui.Create("DFrame") local w, h = 500, 350 dframe:SetSize(w, h) dframe:Center() dframe:SetTitle(GetTranslation("equip_title")) dframe:SetVisible(true) dframe:ShowCloseButton(true) dframe:SetMouseInputEnabled(true) dframe:SetDeleteOnClose(true) --dframe.OnKeyCodePressed = util.BasicKeyHandler local m = 5 local dsheet = vgui.Create("EquipPropertySheet", dframe) dsheet:SetPos(0,0) dsheet:StretchToParent(m,m + 25,m,m) local padding = dsheet:GetPadding() local dequip = vgui.Create("DPanel", dsheet) dequip:SetPaintBackground(false) dequip:StretchToParent(padding,padding,padding,padding) -- Determine if we already have equipment local owned_ids = {} for _, wep in pairs(ply:GetWeapons()) do if ValidEntity(wep) and wep:IsEquipment() then table.insert(owned_ids, wep:GetClass()) end end -- Stick to one value for no equipment if #owned_ids == 0 then owned_ids = nil end --- Construct icon listing local dlist = vgui.Create("EquipSelect", dequip) dlist:SetPos(0,0) dlist:SetSize(154, h - 70) dlist:EnableVerticalScrollbar(true) dlist:EnableHorizontal(true) dlist:SetPadding(4) local items = GetEquipmentForRole(ply:GetRole()) local to_select = nil for k, item in pairs(items) do local ic = nil -- Create icon panel if item.material then if item.custom then -- Custom marker icon ic = vgui.Create("LayeredIcon", dlist) local marker = vgui.Create("DImage") marker:SetImage("VGUI/ttt/custom_marker") marker.PerformLayout = function(s) s:AlignBottom(2) s:AlignRight(2) s:SetSize(16, 16) end marker:SetTooltip(GetTranslation("equip_custom")) ic:AddLayer(marker) ic:EnableMousePassthrough(marker) elseif not ItemIsWeapon(item) then ic = vgui.Create("SimpleIcon", dlist) else ic = vgui.Create("LayeredIcon", dlist) end -- Slot marker icon if ItemIsWeapon(item) then local slot = vgui.Create("SimpleIconLabelled") slot:SetIcon("VGUI/ttt/slotcap") slot:SetIconColor(color_slot[ply:GetRole()] or COLOR_GREY) slot:SetIconSize(16) slot:SetIconText(item.slot) slot:SetIconProperties(COLOR_WHITE, "UiBold", {opacity=220, offset=1}, {8.5, 7}) ic:AddLayer(slot) ic:EnableMousePassthrough(slot) end ic:SetIconSize(64) ic:SetIcon(item.material) elseif item.model then ic = vgui.Create("SpawnIcon", dlist) ic:SetModel(item.model) else ErrorNoHalt("Equipment item does not have model or material specified: " .. tostring(item) .. "\n") end ic.item = item local tip = SafeTranslate(item.name) .. " (" .. SafeTranslate(item.type) .. ")" ic:SetTooltip(tip) -- If we cannot order this item, darken it if ((not can_order) or -- already owned table.HasValue(owned_ids, item.id) or (tonumber(item.id) and ply:HasEquipmentItem(tonumber(item.id))) or -- already carrying a weapon for this slot (ItemIsWeapon(item) and (not CanCarryWeapon(item))) or -- already bought the item before (item.limited and ply:HasBought(tostring(item.id)))) then ic:SetIconColor(color_darkened) end dlist:AddPanel(ic) end local dlistw = 154 local bw, bh = 100, 25 local dih = h - bh - 25 local diw = w - dlistw - m*5 local dinfobg = vgui.Create("DPanel", dequip) dinfobg:SetPaintBackground(false) dinfobg:SetSize(diw, dih) dinfobg:SetPos(dlistw + m, 0) local dinfo = vgui.Create("DForm", dinfobg) dinfo:SetName(GetTranslation("equip_spec_title")) dinfo:SetPos(0,0) dinfo:SetSize(diw, 140) dinfo:SetPadding(8) dinfo:SetSpacing(5) dinfo:SetAutoSize(false) local di_name = vgui.Create("DLabel", dinfo) di_name:SetText(GetTranslation("equip_spec_name")) local di_type = vgui.Create("DLabel", dinfo) di_type:SetText(GetTranslation("equip_spec_type")) local di_desc = vgui.Create("DLabel", dinfo) di_desc:SetText(GetTranslation("equip_spec_desc")) local dfields = {} for _, k in pairs({"name", "type", "desc"}) do dfields[k] = vgui.Create("DLabel", dinfo) dfields[k]:SetFont("TabLarge") dfields[k]:SetContentAlignment( 7 ) end dinfo:AddItem(di_name, dfields.name) dinfo:AddItem(di_type, dfields.type) dinfo:AddItem(di_desc, dfields.desc) local iw, ih = dinfo:GetSize() local dhelp = vgui.Create("DForm", dinfobg) dhelp:SetName(GetTranslation("equip_status")) dhelp:SetPos(0, ih + m) dhelp:SetSize(diw, dih - ih - m*2) dhelp:SetSpacing(4) dhelp:SetPadding(8) local update_preqs = PreqLabels(dhelp) dhelp:SizeToContents() local dconfirm = vgui.Create("DButton", dinfobg) dconfirm:SetPos(0, dinfobg:GetTall() - bh*2 + m) dconfirm:SetSize(bw, bh) dconfirm:SetDisabled(true) dconfirm:SetText(GetTranslation("equip_confirm")) dsheet:AddSheet(GetTranslation("equip_tabtitle"), dequip, "gui/silkicons/bomb", false, false, "Traitor equipment menu") -- Item control if ply:HasEquipmentItem(EQUIP_RADAR) then local dradar = RADAR.CreateMenu(dsheet, dframe) dsheet:AddSheet(GetTranslation("radar_name"), dradar, "gui/silkicons/magnifier", false,false, "Radar control") end if ply:HasEquipmentItem(EQUIP_DISGUISE) then local ddisguise = DISGUISE.CreateMenu(dsheet) dsheet:AddSheet(GetTranslation("disg_name"), ddisguise, "gui/silkicons/user", false,false, "Disguise control") end -- Weapon/item control if IsValid(ply.radio) or ply:HasWeapon("weapon_ttt_radio") then local dradio = TRADIO.CreateMenu(dsheet) dsheet:AddSheet(GetTranslation("radio_name"), dradio, "gui/silkicons/sound", false,false, "Radio control") end -- Credit transferring if credits > 0 then local dtransfer = CreateTransferMenu(dsheet) dsheet:AddSheet(GetTranslation("xfer_name"), dtransfer, "gui/silkicons/folder_go", false,false, "Transfer credits") end -- couple panelselect with info dlist.OnActivePanelChanged = function(self, _, new) for k,v in pairs(new.item) do if dfields[k] then dfields[k]:SetText(SafeTranslate(v)) dfields[k]:SizeToContents() end end -- Trying to force everything to update to -- the right size is a giant pain, so just -- force a good size. dfields.desc:SetTall(70) can_order = update_preqs(new.item) dconfirm:SetDisabled(not can_order) end -- select first dlist:SelectPanel(to_select or dlist:GetItems()[1]) -- prep confirm action dconfirm.DoClick = function() local pnl = dlist.SelectedPanel if not pnl or not pnl.item then return end local choice = pnl.item RunConsoleCommand("ttt_order_equipment", choice.id) dframe:Close() end -- update some basic info, may have changed in another tab -- specifically the number of credits in the preq list dsheet.OnTabChanged = function(s, old, new) if not IsValid(new) then return end if new:GetPanel() == dequip then can_order = update_preqs(dlist.SelectedPanel.item) dconfirm:SetDisabled(not can_order) end end local dcancel = vgui.Create("DButton", dframe) dcancel:SetPos(w - 10 - bw, h - bh - 14) dcancel:SetSize(bw, bh) dcancel:SetDisabled(false) dcancel:SetText(GetTranslation("close")) dcancel.DoClick = function() dframe:Close() end dframe:MakePopup() dframe:SetKeyboardInputEnabled(false) eqframe = dframe end concommand.Add("ttt_cl_traitorpopup", TraitorMenuPopup) local function ForceCloseTraitorMenu(ply, cmd, args) if ValidPanel(eqframe) then eqframe:Close() end end concommand.Add("ttt_cl_traitorpopup_close", ForceCloseTraitorMenu) function GM:OnContextMenuOpen() local r = GetRoundState() if r == ROUND_ACTIVE and not (LocalPlayer():GetTraitor() or LocalPlayer():GetDetective()) then return elseif r == ROUND_POST or r == ROUND_PREP then CLSCORE:Reopen() return end RunConsoleCommand("ttt_cl_traitorpopup") end local function ReceiveEquipment(um) local ply = LocalPlayer() if not ValidEntity(ply) then return end ply.equipment_items = um:ReadShort() end usermessage.Hook("equipment", ReceiveEquipment) local function ReceiveCredits(um) local ply = LocalPlayer() if not ValidEntity(ply) then return end ply.equipment_credits = um:ReadChar() end usermessage.Hook("credits", ReceiveCredits) local r = 0 local function ReceiveBought(um) local ply = LocalPlayer() if not ValidEntity(ply) then return end ply.bought = {} local num = um:ReadShort() for i=1,num do local s = um:ReadString() if s != "" then table.insert(ply.bought, s) end end -- This usermessage sometimes fails to contain the last weapon that was -- bought, even though resending then works perfectly. Possibly a bug in -- bf_read. Anyway, this hack is a workaround: we just request a new umsg. if num != #ply.bought and r < 10 then -- r is an infinite loop guard RunConsoleCommand("ttt_resend_bought") r = r + 1 else r = 0 end end usermessage.Hook("bought", ReceiveBought)