1331 lines
36 KiB
Lua
1331 lines
36 KiB
Lua
|
local draw_SimpleText = draw.SimpleText
|
||
|
|
||
|
local render_SetBlend = render.SetBlend
|
||
|
local render_DrawBeam = render.DrawBeam
|
||
|
local render_ModelMaterialOverride = render.ModelMaterialOverride
|
||
|
local render_SetColorModulation = render.SetColorModulation
|
||
|
local render_SuppressEngineLighting = render.SuppressEngineLighting
|
||
|
|
||
|
net.Receive("zs_skills_notify", function(length)
|
||
|
if GAMEMODE.SkillWeb then
|
||
|
GAMEMODE.SkillWeb:DisplayMessage(net.ReadString(), net.ReadBool() and COLOR_RED or COLOR_GREEN)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skill_is_desired", function(length)
|
||
|
local skillid = net.ReadUInt(16)
|
||
|
local yesno = net.ReadBool()
|
||
|
|
||
|
if MySelf:IsValid() then
|
||
|
MySelf:SetSkillDesired(skillid, yesno)
|
||
|
|
||
|
if GAMEMODE.SkillWeb and GAMEMODE.SkillWeb:IsValid() then
|
||
|
surface.PlaySound("zombiesurvival/ui/misc" .. (yesno and 2 or 1) .. ".ogg")
|
||
|
|
||
|
GAMEMODE.SkillWeb:UpdateQuickStats()
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skill_is_unlocked", function(length)
|
||
|
local skillid = net.ReadUInt(16)
|
||
|
local yesno = net.ReadBool()
|
||
|
|
||
|
if MySelf:IsValid() then
|
||
|
MySelf:SetSkillUnlocked(skillid, yesno)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skills_active", function(length)
|
||
|
local t = {}
|
||
|
|
||
|
for skillid in pairs(GAMEMODE.Skills) do
|
||
|
if net.ReadBool() then
|
||
|
t[skillid] = true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if MySelf:IsValid() then
|
||
|
MySelf:ApplySkills(t)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skills_init", function(length)
|
||
|
GAMEMODE.ReceivedInitialSkills = true
|
||
|
|
||
|
local unlocked = {}
|
||
|
local desired = {}
|
||
|
local active = {}
|
||
|
|
||
|
for skillid in pairs(GAMEMODE.Skills) do
|
||
|
if net.ReadBool() then
|
||
|
unlocked[skillid] = true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for skillid in pairs(GAMEMODE.Skills) do
|
||
|
if net.ReadBool() then
|
||
|
desired[skillid] = true
|
||
|
end
|
||
|
end
|
||
|
if net.ReadBool() then
|
||
|
for skillid in pairs(GAMEMODE.Skills) do
|
||
|
if net.ReadBool() then
|
||
|
active[skillid] = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if MySelf:IsValid() then
|
||
|
MySelf:SetUnlockedSkills(unlocked)
|
||
|
MySelf:SetDesiredActiveSkills(desired)
|
||
|
MySelf:ApplySkills(active)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skills_desired", function(length)
|
||
|
local t = {}
|
||
|
|
||
|
for skillid in pairs(GAMEMODE.Skills) do
|
||
|
if net.ReadBool() then
|
||
|
t[#t + 1] = skillid
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if MySelf:IsValid() then
|
||
|
MySelf:SetDesiredActiveSkills(t)
|
||
|
end
|
||
|
|
||
|
if GAMEMODE.SkillWeb and GAMEMODE.SkillWeb:IsValid() then
|
||
|
GAMEMODE.SkillWeb:UpdateQuickStats()
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skills_unlocked", function(length)
|
||
|
local t = {}
|
||
|
|
||
|
for skillid in pairs(GAMEMODE.Skills) do
|
||
|
if net.ReadBool() then
|
||
|
t[#t + 1] = skillid
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if MySelf:IsValid() then
|
||
|
MySelf:SetUnlockedSkills(t)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
net.Receive("zs_skills_nextreset", function(length)
|
||
|
GAMEMODE.NextSkillReset = net.ReadUInt(32)
|
||
|
|
||
|
if GAMEMODE.SkillWeb and GAMEMODE.SkillWeb:IsValid() then
|
||
|
local days = math.floor(GAMEMODE.NextSkillReset / 86400)
|
||
|
local btn = GAMEMODE.SkillWeb.Reset
|
||
|
|
||
|
btn:SetText(GAMEMODE.NextSkillReset <= 0 and "Reset" or (days .. " days left"))
|
||
|
btn:SetDisabled(GAMEMODE.NextSkillReset > 0)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
GM.SavedSkillLoadouts = {}
|
||
|
hook.Add("Initialize", "LoadSkillLoadouts", function()
|
||
|
if file.Exists(GAMEMODE.SkillLoadoutsFile, "DATA") then
|
||
|
GAMEMODE.SavedSkillLoadouts = Deserialize(file.Read(GAMEMODE.SkillLoadoutsFile)) or {}
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
local function UpdateDropDown(dropdown)
|
||
|
dropdown:Clear()
|
||
|
|
||
|
for i, cart in ipairs(GAMEMODE.SavedSkillLoadouts) do
|
||
|
dropdown:AddChoice(cart[1])
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function SaveSkillLoadout(name)
|
||
|
for i, cart in ipairs(GAMEMODE.SavedSkillLoadouts) do
|
||
|
if string.lower(cart[1]) == string.lower(name) then
|
||
|
cart[1] = name
|
||
|
cart[2] = MySelf:GetDesiredActiveSkills()
|
||
|
|
||
|
file.Write(GAMEMODE.SkillLoadoutsFile, Serialize(GAMEMODE.SavedSkillLoadouts))
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
GAMEMODE.SavedSkillLoadouts[#GAMEMODE.SavedSkillLoadouts + 1] = {name, MySelf:GetDesiredActiveSkills()}
|
||
|
file.Write(GAMEMODE.SkillLoadoutsFile, Serialize(GAMEMODE.SavedSkillLoadouts))
|
||
|
end
|
||
|
|
||
|
-- For getting the positions of skill webs. See skillwebgrid.png
|
||
|
-- The file is not used in the gamemode so feel free to edit it.
|
||
|
function GM:GenerateFromSkillWebGrid()
|
||
|
local mat = Material("zombiesurvival/skillwebgrid.png")
|
||
|
local w, h = mat:Width(), mat:Height()
|
||
|
local prelines = {}
|
||
|
local lines = {}
|
||
|
local col
|
||
|
|
||
|
for y = 1, h do
|
||
|
for x = 1, w do
|
||
|
col = mat:GetColor(x - 1, y - 1)
|
||
|
if col.r ~= 255 or col.g ~= 255 or col.b ~= 255 then
|
||
|
local id = col.r + col.g * 255 + col.b * 65052
|
||
|
local lx, ly = math.ceil(x - w / 2), math.floor(y - h / 2)
|
||
|
|
||
|
if lines[id] then
|
||
|
print(string.format("-- WARNING: Skill ID %d already exists (pixel %d, %d)", id, x, y))
|
||
|
end
|
||
|
|
||
|
prelines[id] = string.format("SKILL_%d = %d",
|
||
|
id,
|
||
|
id
|
||
|
)
|
||
|
lines[id] = string.format(
|
||
|
"GM:AddSkill(SKILL_%d, \"\", \"\",\n%s%d,%s%d,%s{})",
|
||
|
id,
|
||
|
string.rep("\t", 16),
|
||
|
lx,
|
||
|
string.rep("\t", 3),
|
||
|
ly,
|
||
|
string.rep("\t", 3)
|
||
|
)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
print(table.concat(prelines, "\n"))
|
||
|
print("")
|
||
|
print(table.concat(lines, "\n"))
|
||
|
end
|
||
|
|
||
|
local hoveredskill
|
||
|
|
||
|
local REMORT_SKILL = {Name = "Remort", Description = "Go even further beyond.\nLose all skills, experience, skill points, and levels.\nStart at level 1 but with 1 extra skill point.\nCan remort multiple times for multiple extra skill points."}
|
||
|
|
||
|
local PANEL = {}
|
||
|
|
||
|
AccessorFunc( PANEL, "vCamPos", "CamPos" )
|
||
|
AccessorFunc( PANEL, "fFOV", "FOV" )
|
||
|
AccessorFunc( PANEL, "vLookatPos", "LookAt" )
|
||
|
AccessorFunc( PANEL, "aLookAngle", "LookAng" )
|
||
|
AccessorFunc( PANEL, "colAmbientLight", "AmbientLight" )
|
||
|
|
||
|
PANEL.CreationTime = 0
|
||
|
PANEL.DesiredZoom = 5000
|
||
|
PANEL.ZoomChange = 0
|
||
|
PANEL.DesiredTree = 0
|
||
|
PANEL.ShadeAlpha = 0
|
||
|
PANEL.ShadeVelocity = 0
|
||
|
|
||
|
local offsets = {
|
||
|
[TREE_HEALTHTREE] = {0, 16},
|
||
|
[TREE_SPEEDTREE] = {0, -12},
|
||
|
[TREE_GUNTREE] = {13, -7},
|
||
|
[TREE_MELEETREE] = {13, 8},
|
||
|
[TREE_BUILDINGTREE] = {-14, 8},
|
||
|
[TREE_SUPPORTTREE] = {-13, -7}
|
||
|
}
|
||
|
|
||
|
function PANEL:Init()
|
||
|
local allskills = GAMEMODE.Skills
|
||
|
local node
|
||
|
|
||
|
self.LastPaint = RealTime()
|
||
|
self.DirectionalLight = {}
|
||
|
self.FarZ = 32000
|
||
|
|
||
|
self:SetCamPos( Vector( 15000, 0, 0 ) )
|
||
|
self:SetLookAt( Vector( 0, 0, 0 ) )
|
||
|
self:SetFOV(6)
|
||
|
|
||
|
self:SetAmbientLight( Color( 50, 50, 50 ) )
|
||
|
|
||
|
self:SetDirectionalLight( BOX_TOP, color_white )
|
||
|
self:SetDirectionalLight( BOX_FRONT, color_white )
|
||
|
|
||
|
self.SkillNodes = {}
|
||
|
for id, skill in pairs(allskills) do
|
||
|
if not skill.Trinket then
|
||
|
node = ClientsideModel("models/dav0r/hoverball.mdl", RENDER_GROUP_OPAQUE_ENTITY)
|
||
|
if IsValid(node) then
|
||
|
node:SetNoDraw(true)
|
||
|
node:SetPos(Vector(0, (skill.x + offsets[skill.Tree][1]) * 20, (skill.y + offsets[skill.Tree][2]) * 20))
|
||
|
if skill.Disabled then
|
||
|
node:SetModelScale(0.46, 0)
|
||
|
else
|
||
|
node:SetModelScale(0.66, 0)
|
||
|
end
|
||
|
|
||
|
node.Skill = skill
|
||
|
node.SkillID = id
|
||
|
self.SkillNodes[id] = node
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Create the special remort node
|
||
|
node = ClientsideModel("models/Gibs/HGIBS.mdl", RENDER_GROUP_OPAQUE_ENTITY)
|
||
|
if IsValid(node) then
|
||
|
node:SetNoDraw(true)
|
||
|
node:SetPos(Vector(0, 0, 10))
|
||
|
node:SetModelScale(1.5, 0)
|
||
|
|
||
|
node.Skill = REMORT_SKILL
|
||
|
node.SkillID = -1
|
||
|
self.SkillNodes[-1] = node
|
||
|
end
|
||
|
|
||
|
local top = vgui.Create("Panel", self)
|
||
|
top:SetSize(ScrW(), 256)
|
||
|
top:SetMouseInputEnabled(false)
|
||
|
|
||
|
local skillname = vgui.Create("DLabel", top)
|
||
|
skillname:SetFont("ZSHUDFont")
|
||
|
skillname:SetTextColor(COLOR_WHITE)
|
||
|
skillname:SetContentAlignment(8)
|
||
|
skillname:Dock(TOP)
|
||
|
|
||
|
local desc = {}
|
||
|
for i=1, 5 do
|
||
|
local skilldesc = vgui.Create("DLabel", top)
|
||
|
skilldesc:SetFont("ZSHUDFontSmall")
|
||
|
skilldesc:SetTextColor(COLOR_GRAY)
|
||
|
skilldesc:SetContentAlignment(8)
|
||
|
skilldesc:Dock(TOP)
|
||
|
table.insert(desc, skilldesc)
|
||
|
end
|
||
|
|
||
|
local screenscale = BetterScreenScale()
|
||
|
|
||
|
local bottomleft = vgui.Create("DEXRoundedPanel", self)
|
||
|
bottomleft:DockPadding(10, 10, 10, 10)
|
||
|
bottomleft:SetSize(190 * screenscale, 130 * screenscale)
|
||
|
|
||
|
local bottomleftup = vgui.Create("DEXRoundedPanel", self)
|
||
|
bottomleftup:DockPadding(10, 10, 10, 10)
|
||
|
bottomleftup:SetSize(190 * screenscale, 120 * screenscale)
|
||
|
|
||
|
local quickstats = {}
|
||
|
for i=1,3 do
|
||
|
local hpstat = vgui.Create("DLabel", bottomleftup)
|
||
|
hpstat:SetFont("ZSHUDFontSmaller")
|
||
|
hpstat:SetTextColor(COLOR_WHITE)
|
||
|
hpstat:SetContentAlignment(8)
|
||
|
hpstat:Dock(TOP)
|
||
|
hpstat:SizeToContents()
|
||
|
hpstat:SetText("---")
|
||
|
table.insert(quickstats, hpstat)
|
||
|
end
|
||
|
|
||
|
local dropdown = vgui.Create("DComboBox", bottomleft)
|
||
|
dropdown:Dock(TOP)
|
||
|
dropdown:SetMouseInputEnabled(true)
|
||
|
dropdown:SetTextColor(color_black)
|
||
|
|
||
|
local delbtn = vgui.Create("DButton", bottomleft)
|
||
|
delbtn:SetFont("ZSHUDFontSmallest")
|
||
|
delbtn:SetText("Delete")
|
||
|
delbtn:SizeToContents()
|
||
|
delbtn:SetTall(bottomleft:GetTall() / 5)
|
||
|
delbtn:Dock(BOTTOM)
|
||
|
delbtn.DoClick = function(me)
|
||
|
surface.PlaySound("zombiesurvival/ui/misc1.ogg")
|
||
|
|
||
|
local delloadout
|
||
|
for k, v in pairs(GAMEMODE.SavedSkillLoadouts) do
|
||
|
if v[1] == dropdown:GetSelected() then
|
||
|
delloadout = k
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if not delloadout then return end
|
||
|
|
||
|
table.remove(GAMEMODE.SavedSkillLoadouts, delloadout)
|
||
|
file.Write(GAMEMODE.SkillLoadoutsFile, Serialize(GAMEMODE.SavedSkillLoadouts))
|
||
|
surface.PlaySound("buttons/button19.wav")
|
||
|
|
||
|
UpdateDropDown(dropdown)
|
||
|
end
|
||
|
|
||
|
local savebtn = vgui.Create("DButton", bottomleft)
|
||
|
savebtn:SetFont("ZSHUDFontSmallest")
|
||
|
savebtn:SetText("Save")
|
||
|
savebtn:SizeToContents()
|
||
|
savebtn:SetTall(bottomleft:GetTall() / 5)
|
||
|
savebtn:Dock(BOTTOM)
|
||
|
savebtn.DoClick = function(me)
|
||
|
surface.PlaySound("zombiesurvival/ui/misc1.ogg")
|
||
|
|
||
|
local frame = Derma_StringRequest("Save skill loadout", "Enter a name for this skill loadout.", "Name",
|
||
|
function(strTextOut)
|
||
|
SaveSkillLoadout(strTextOut)
|
||
|
UpdateDropDown(dropdown)
|
||
|
|
||
|
self:DisplayMessage("Skill loadout '" .. strTextOut .."' saved!", COLOR_GREEN)
|
||
|
end,
|
||
|
function(strTextOut) end,
|
||
|
"OK", "Cancel")
|
||
|
|
||
|
frame:GetChildren()[5]:GetChildren()[2]:SetTextColor(Color(30, 30, 30))
|
||
|
end
|
||
|
|
||
|
UpdateDropDown(dropdown)
|
||
|
|
||
|
local loadbtn = vgui.Create("DButton", bottomleft)
|
||
|
loadbtn:SetFont("ZSHUDFontSmallest")
|
||
|
loadbtn:SetText("Load")
|
||
|
loadbtn:SizeToContents()
|
||
|
loadbtn:SetTall(bottomleft:GetTall() / 5)
|
||
|
loadbtn:Dock(BOTTOM)
|
||
|
loadbtn.DoClick = function(me)
|
||
|
surface.PlaySound("zombiesurvival/ui/misc1.ogg")
|
||
|
|
||
|
local newloadout, nlname
|
||
|
for _, v in pairs(GAMEMODE.SavedSkillLoadouts) do
|
||
|
if v[1] == dropdown:GetSelected() then
|
||
|
newloadout = v[2]
|
||
|
nlname = v[1]
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if not newloadout then return end
|
||
|
|
||
|
net.Start("zs_skill_set_desired")
|
||
|
net.WriteTable(newloadout)
|
||
|
net.SendToServer()
|
||
|
|
||
|
self:DisplayMessage("Skill loadout '" .. nlname .."' loaded!", COLOR_GREEN)
|
||
|
end
|
||
|
|
||
|
local bottomlefttop = vgui.Create("DEXRoundedPanel", self)
|
||
|
bottomlefttop:DockPadding(10, 10, 10, 10)
|
||
|
bottomlefttop:SetSize(160 * screenscale, 130 * screenscale)
|
||
|
|
||
|
local activateall = vgui.Create("DButton", bottomlefttop)
|
||
|
activateall:SetFont("ZSHUDFontSmallest")
|
||
|
activateall:SetText("Activate All")
|
||
|
activateall:SizeToContents()
|
||
|
activateall:SetTall(activateall:GetTall())
|
||
|
activateall:Dock(TOP)
|
||
|
activateall.DoClick = function(me)
|
||
|
surface.PlaySound("zombiesurvival/ui/misc1.ogg")
|
||
|
|
||
|
if #MySelf:GetUnlockedSkills() == 0 then
|
||
|
self:DisplayMessage("You have no skills to activate!", COLOR_RED)
|
||
|
else
|
||
|
self:DisplayMessage("All unlocked skills activated.", COLOR_GREEN)
|
||
|
end
|
||
|
|
||
|
net.Start("zs_skills_all_desired")
|
||
|
net.WriteBool(true)
|
||
|
net.SendToServer()
|
||
|
end
|
||
|
|
||
|
local deactivateall = vgui.Create("DButton", bottomlefttop)
|
||
|
deactivateall:SetFont("ZSHUDFontSmallest")
|
||
|
deactivateall:SetText("Deactivate All")
|
||
|
deactivateall:SizeToContents()
|
||
|
deactivateall:SetTall(deactivateall:GetTall())
|
||
|
deactivateall:DockMargin(0, 5, 0, 0)
|
||
|
deactivateall:Dock(TOP)
|
||
|
deactivateall.DoClick = function(me)
|
||
|
surface.PlaySound("zombiesurvival/ui/misc1.ogg")
|
||
|
|
||
|
if #MySelf:GetUnlockedSkills() == 0 then
|
||
|
self:DisplayMessage("You have no skills to deactivate!", COLOR_RED)
|
||
|
else
|
||
|
self:DisplayMessage("All unlocked skills deactivated.", COLOR_RED)
|
||
|
end
|
||
|
|
||
|
net.Start("zs_skills_all_desired")
|
||
|
net.WriteBool(false)
|
||
|
net.SendToServer()
|
||
|
end
|
||
|
|
||
|
local resettime = GAMEMODE.NextSkillReset or 0
|
||
|
local days = math.floor(resettime / 86400)
|
||
|
|
||
|
local reset = vgui.Create("DButton", bottomlefttop)
|
||
|
reset:SetFont("ZSHUDFontSmaller")
|
||
|
reset:SetText(resettime <= 0 and "Reset" or (days .. " days left"))
|
||
|
reset:SetDisabled(resettime > 0)
|
||
|
reset:SizeToContents()
|
||
|
reset:SetTall(reset:GetTall())
|
||
|
reset:DockMargin(0, 5, 0, 0)
|
||
|
reset:Dock(TOP)
|
||
|
reset.DoClick = function(me)
|
||
|
Derma_Query(
|
||
|
"Reset all skills and refund SP?\nYou can only do this once per week.",
|
||
|
"Warning",
|
||
|
"OK",
|
||
|
function() net.Start("zs_skills_reset") net.SendToServer() end,
|
||
|
"Cancel",
|
||
|
function() end
|
||
|
)
|
||
|
end
|
||
|
|
||
|
local topright = vgui.Create("DEXRoundedPanel", self)
|
||
|
topright:SetSize(160 * screenscale, 64 * screenscale)
|
||
|
topright:DockPadding(10, 10, 10, 10)
|
||
|
|
||
|
local quit = vgui.Create("DButton", topright)
|
||
|
quit:SetText("Quit")
|
||
|
quit:SetFont("ZSHUDFont")
|
||
|
quit:Dock(FILL)
|
||
|
quit.DoClick = function()
|
||
|
self:Remove()
|
||
|
end
|
||
|
|
||
|
local bottom = vgui.Create("DEXRoundedPanel", self)
|
||
|
bottom:SetSize(600 * screenscale, math.Clamp(84 * screenscale, 70, 125))
|
||
|
bottom:DockPadding(10, 10, 10, 10)
|
||
|
|
||
|
local spremaining = vgui.Create("DEXChangingLabel", bottom)
|
||
|
spremaining:SetChangeFunction(function()
|
||
|
return "Unused skill points: "..MySelf:GetZSSPRemaining()
|
||
|
end, true)
|
||
|
spremaining:SetChangedFunction(function()
|
||
|
if MySelf:GetZSSPRemaining() >= 1 then
|
||
|
spremaining:SetTextColor(COLOR_GRAY)
|
||
|
else
|
||
|
spremaining:SetTextColor(COLOR_RED)
|
||
|
end
|
||
|
end)
|
||
|
spremaining:SetFont("ZSHUDFontSmall")
|
||
|
spremaining:SetContentAlignment(5)
|
||
|
spremaining:Dock(TOP)
|
||
|
|
||
|
local expbar = vgui.Create("Panel", bottom)
|
||
|
expbar.Paint = function(me, w, h)
|
||
|
GAMEMODE:DrawXPBar(0, 2 - screenscale * 2, w, h, w, 1, 0.95, MySelf:GetZSLevel())
|
||
|
end
|
||
|
expbar:SetContentAlignment(5)
|
||
|
expbar:Dock(BOTTOM)
|
||
|
|
||
|
local contextmenu = vgui.Create("Panel", self)
|
||
|
contextmenu:SetSize(128 * screenscale, 128 * screenscale)
|
||
|
contextmenu:SetVisible(false)
|
||
|
|
||
|
local button = vgui.Create("DButton", contextmenu)
|
||
|
button:SetText("Activate")
|
||
|
button:SetFont("ZSHUDFontSmall")
|
||
|
button:SetDisabled(false)
|
||
|
button:SetSize(128 * screenscale, 32 * screenscale)
|
||
|
button:AlignTop()
|
||
|
button:CenterHorizontal()
|
||
|
button.DoClick = function(me)
|
||
|
local skillid = contextmenu.SkillID
|
||
|
local name = allskills[skillid].Name
|
||
|
if MySelf:IsSkillDesired(skillid) then
|
||
|
net.Start("zs_skill_is_desired")
|
||
|
net.WriteUInt(skillid, 16)
|
||
|
net.WriteBool(false)
|
||
|
net.SendToServer()
|
||
|
|
||
|
self:DisplayMessage(name.." deactivated.")
|
||
|
elseif MySelf:IsSkillUnlocked(skillid) then
|
||
|
net.Start("zs_skill_is_desired")
|
||
|
net.WriteUInt(skillid, 16)
|
||
|
net.WriteBool(true)
|
||
|
net.SendToServer()
|
||
|
|
||
|
self:DisplayMessage(name.." activated.", COLOR_DARKGREEN)
|
||
|
else
|
||
|
net.Start("zs_skill_is_unlocked")
|
||
|
net.WriteUInt(skillid, 16)
|
||
|
net.WriteBool(true)
|
||
|
net.SendToServer()
|
||
|
|
||
|
self:DisplayMessage(name.." unlocked and activated!", COLOR_GREEN)
|
||
|
end
|
||
|
|
||
|
contextmenu:SetVisible(false)
|
||
|
end
|
||
|
contextmenu.Button = button
|
||
|
|
||
|
local messagebox = vgui.Create("Panel", self)
|
||
|
messagebox:SetSize(850 * screenscale, 48)
|
||
|
messagebox.Paint = function(me, w, h)
|
||
|
PaintGenericFrame(me, 0, 0, w, h, 16)
|
||
|
end
|
||
|
messagebox:SetKeyboardInputEnabled(false)
|
||
|
messagebox:SetMouseInputEnabled(false)
|
||
|
messagebox:SetZPos(-100)
|
||
|
local messagetext = vgui.Create("DLabel", messagebox)
|
||
|
messagetext:SetTextColor(COLOR_GRAY)
|
||
|
messagetext:SetText("")
|
||
|
messagetext:SetFont("ZSHUDFontSmall")
|
||
|
messagetext:SetContentAlignment(5)
|
||
|
messagetext:Dock(FILL)
|
||
|
messagetext:SetKeyboardInputEnabled(false)
|
||
|
messagetext:SetMouseInputEnabled(false)
|
||
|
messagetext:SetZPos(-200)
|
||
|
messagebox:SetVisible(false)
|
||
|
|
||
|
local warningtext = vgui.Create("DLabel", self)
|
||
|
warningtext:SetTextColor(COLOR_RED)
|
||
|
warningtext:SetFont("ZSHUDFontSmall")
|
||
|
warningtext:SetText("Changes applied on respawn!")
|
||
|
warningtext:SizeToContents()
|
||
|
warningtext:SetKeyboardInputEnabled(false)
|
||
|
warningtext:SetMouseInputEnabled(false)
|
||
|
|
||
|
self:GenerateParticles()
|
||
|
|
||
|
self.Top = top
|
||
|
self.BottomLeft = bottomleft
|
||
|
self.BottomLeftTop = bottomlefttop
|
||
|
self.BottomLeftUp = bottomleftup
|
||
|
self.QuickStats = quickstats
|
||
|
self.Bottom = bottom
|
||
|
self.TopRight = topright
|
||
|
self.SkillName = skillname
|
||
|
self.SkillDesc = desc
|
||
|
self.ContextMenu = contextmenu
|
||
|
self.MessageBox = messagebox
|
||
|
self.MessageText = messagetext
|
||
|
self.WarningText = warningtext
|
||
|
self.LoadoutsDrop = dropdown
|
||
|
self.Reset = reset
|
||
|
|
||
|
top:SetAlpha(0)
|
||
|
|
||
|
self.AmbientSound = CreateSound(MySelf, "zombiesurvival/skilltree_ambiance.ogg")
|
||
|
self.AmbientSound:PlayEx(0, 60)
|
||
|
self.AmbientSound:ChangeVolume(0, 0)
|
||
|
self.AmbientSound:ChangeVolume(0.66, 1.5)
|
||
|
|
||
|
self:DockMargin(0, 0, 0, 0)
|
||
|
self:DockPadding(0, 0, 0, 0)
|
||
|
self:Dock(FILL)
|
||
|
|
||
|
self:InvalidateLayout()
|
||
|
|
||
|
self.CreationTime = RealTime()
|
||
|
|
||
|
self:MakePopup()
|
||
|
self:SetMouseInputEnabled(true)
|
||
|
self:SetKeyboardInputEnabled(false)
|
||
|
|
||
|
self:UpdateQuickStats()
|
||
|
|
||
|
net.Start("zs_skills_refunded")
|
||
|
net.SendToServer()
|
||
|
end
|
||
|
|
||
|
function PANEL:UpdateQuickStats()
|
||
|
local skillmodifiers = {}
|
||
|
local gm_modifiers = GAMEMODE.SkillModifiers
|
||
|
for skillid in pairs(table.ToAssoc(MySelf:GetDesiredActiveSkills())) do
|
||
|
local modifiers = gm_modifiers[skillid]
|
||
|
if modifiers then
|
||
|
for modid, amount in pairs(modifiers) do
|
||
|
skillmodifiers[modid] = (skillmodifiers[modid] or 0) + amount
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for i=1,3 do
|
||
|
local prefix = i == 1 and "Health" or i == 2 and "Speed" or "Worth"
|
||
|
local val = i == 2 and SPEED_NORMAL or 100
|
||
|
self.QuickStats[i]:SetText(prefix .. " : " .. (val + (skillmodifiers[i] or 0)))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PANEL:DisplayMessage(msg, col)
|
||
|
self.MessageText:SetText(msg)
|
||
|
self.MessageText:SetTextColor(col or COLOR_GRAY)
|
||
|
|
||
|
self.MessageBox:SetVisible(true)
|
||
|
|
||
|
timer.Create("SKillWebMessageRemove", 4, 1, function() if self:IsValid() then self.MessageBox:SetVisible(false) end end)
|
||
|
end
|
||
|
|
||
|
PANEL.NextWarningThink = 0
|
||
|
function PANEL:Think()
|
||
|
local time = RealTime()
|
||
|
|
||
|
if self.AmbientSound and time >= self.CreationTime + 1 then
|
||
|
self.AmbientSound:PlayEx(0.66, 60 + CurTime() % 0.1)
|
||
|
end
|
||
|
|
||
|
if time < self.NextWarningThink then return end
|
||
|
self.NextWarningThink = time + 0.1
|
||
|
|
||
|
local display_warning = false
|
||
|
local desired = table.ToAssoc(MySelf:GetDesiredActiveSkills())
|
||
|
local active = MySelf:GetActiveSkills()
|
||
|
for k, v in pairs(desired) do
|
||
|
if v and not active[k] then
|
||
|
display_warning = true
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
for k, v in pairs(active) do
|
||
|
if v and not desired[k] then
|
||
|
display_warning = true
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if display_warning ~= self.WarningText:IsVisible() then
|
||
|
self.WarningText:SetVisible(display_warning)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PANEL:GenerateParticles()
|
||
|
local particles = {}
|
||
|
local particle
|
||
|
for i=1, 140 do
|
||
|
-- struct: Position, Roll, Roll rate, Size, Alpha
|
||
|
particle = {}
|
||
|
particle[1] = Vector(math.Rand(-1524, -32), math.Rand(-610, 610), math.Rand(-610, 610))
|
||
|
particle[2] = math.Rand(0, 360)
|
||
|
particle[3] = math.Rand(-5, 5)
|
||
|
particle[4] = math.Rand(180, 240)
|
||
|
particle[5] = math.Rand(30, 90)
|
||
|
particles[i] = particle
|
||
|
end
|
||
|
|
||
|
self.Particles = particles
|
||
|
end
|
||
|
|
||
|
function PANEL:GenerateSplines()
|
||
|
if GAMEMODE.SkillsSplined then return end
|
||
|
|
||
|
-- for skillid, skill in pairs(GAMEMODE.Skills) do
|
||
|
-- if skill.Curves then
|
||
|
-- skill.Splines = {}
|
||
|
-- local pos_a = self.SkillNodes[1][skillid]:GetPos()
|
||
|
-- pos_a.x = 0
|
||
|
-- for connectid, curve in pairs(skill.Curves) do
|
||
|
-- local cm
|
||
|
-- local splines = {}
|
||
|
-- local pos_b = self.SkillNodes[1][connectid]:GetPos()
|
||
|
-- pos_b.x = 0
|
||
|
--
|
||
|
-- table.insert(splines, pos_a)
|
||
|
-- for mu=0, 0.9, 0.1 do
|
||
|
-- cm = CatmullInterpolate(pos_a, pos_a, curve * 20, pos_b, mu, 1)
|
||
|
-- cm.x = -16
|
||
|
-- table.insert(splines, cm)
|
||
|
-- end
|
||
|
-- for mu=0, 0.9, 0.1 do
|
||
|
-- cm = CatmullInterpolate(pos_a, curve * 20, pos_b, pos_b, mu, 1)
|
||
|
-- cm.x = -16
|
||
|
-- table.insert(splines, cm)
|
||
|
-- end
|
||
|
-- table.insert(splines, pos_b)
|
||
|
-- skill.Splines[connectid] = splines
|
||
|
-- end
|
||
|
-- skill.Curves = nil
|
||
|
-- end
|
||
|
-- end
|
||
|
|
||
|
GAMEMODE.SkillsSplined = true
|
||
|
end
|
||
|
|
||
|
function PANEL:PerformLayout()
|
||
|
self.Top:AlignTop(8)
|
||
|
self.Top:CenterHorizontal()
|
||
|
|
||
|
self.BottomLeftTop:AlignLeft(ScrH() * 0.2)
|
||
|
self.BottomLeftTop:AlignBottom(10)
|
||
|
|
||
|
self.BottomLeftUp:AlignLeft(10)
|
||
|
self.BottomLeftUp:AlignBottom(ScrW() * 0.08)
|
||
|
|
||
|
self.BottomLeft:AlignLeft(10)
|
||
|
self.BottomLeft:AlignBottom(10)
|
||
|
|
||
|
self.Bottom:AlignBottom(10)
|
||
|
self.Bottom:CenterHorizontal()
|
||
|
|
||
|
self.TopRight:AlignRight(10)
|
||
|
self.TopRight:AlignTop(10)
|
||
|
|
||
|
self.MessageBox:CenterHorizontal()
|
||
|
self.MessageBox:AlignTop(ScrH() * 0.65)
|
||
|
|
||
|
self.WarningText:AlignTop(32)
|
||
|
self.WarningText:AlignLeft(32)
|
||
|
end
|
||
|
|
||
|
function PANEL:SetDirectionalLight(iDirection, color)
|
||
|
self.DirectionalLight[iDirection] = color
|
||
|
end
|
||
|
|
||
|
local camera_velocity = Vector(0, 0, 0)
|
||
|
function PANEL:DoEdgeScroll(deltatime)
|
||
|
if not system.HasFocus() then return end
|
||
|
|
||
|
local mx, my = gui.MousePos()
|
||
|
local edge = math.min(w, h) * 0.035
|
||
|
local scrolldir = Vector(0, 0, 0)
|
||
|
local campos = self.vCamPos
|
||
|
|
||
|
if mx <= edge and mx >= 0 then
|
||
|
scrolldir.y = scrolldir.y - 1
|
||
|
elseif mx >= w - edge and mx <= w then
|
||
|
scrolldir.y = scrolldir.y + 1
|
||
|
end
|
||
|
|
||
|
if my <= edge and my >= 0 then
|
||
|
scrolldir.z = scrolldir.z + 1
|
||
|
elseif my >= h - edge and my <= h then
|
||
|
scrolldir.z = scrolldir.z - 1
|
||
|
end
|
||
|
|
||
|
scrolldir:Normalize()
|
||
|
|
||
|
if scrolldir.y ~= 0 or scrolldir.z ~= 0 and self.ContextMenu and self.ContextMenu:IsVisible() then
|
||
|
self.ContextMenu:SetVisible(false)
|
||
|
end
|
||
|
|
||
|
camera_velocity = LerpVector(deltatime * (scrolldir.y == 0 and scrolldir.z == 0 and 3 or 1), camera_velocity, scrolldir)
|
||
|
|
||
|
if camera_velocity.y ~= 0 or camera_velocity.z ~= 0 then
|
||
|
campos = campos + deltatime * edge * 12 * camera_velocity
|
||
|
campos.y = math.Clamp(campos.y, -262, 262)
|
||
|
campos.z = math.Clamp(campos.z, -262, 310)
|
||
|
|
||
|
self:SetCamPos(campos)
|
||
|
self.vLookatPos:Set(campos)
|
||
|
self.vLookatPos.x = 0
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local nodecolors = {
|
||
|
[TREE_HEALTHTREE] = {1.75, 3, 5},
|
||
|
[TREE_SPEEDTREE] = {2, 2, 5},
|
||
|
[TREE_SUPPORTTREE] = {3, 1.5, 6},
|
||
|
[TREE_BUILDINGTREE] = {2, 6, 3},
|
||
|
[TREE_MELEETREE] = {1.5, 7, 7},
|
||
|
[TREE_GUNTREE] = {5, 2, 2}
|
||
|
}
|
||
|
|
||
|
local matBeam = Material("effects/laser1")
|
||
|
local matGlow = Material("sprites/glow04_noz")
|
||
|
local matSmoke = Material("particles/smokey")
|
||
|
local matWhite = Material("models/debug/debugwhite")
|
||
|
local colBeam = Color(0, 0, 0)
|
||
|
local colBeam2 = Color(255, 255, 255)
|
||
|
local colSmoke = Color(140, 160, 185, 160)
|
||
|
local colGlow = Color(0, 0, 0)
|
||
|
function PANEL:Paint(w, h)
|
||
|
local realtime = RealTime()
|
||
|
local lifetime = realtime - self.CreationTime
|
||
|
local dt = realtime - self.LastPaint
|
||
|
local can_remort = MySelf:CanSkillsRemort()
|
||
|
local skillid, skill, nodepos, selected
|
||
|
local col, connectskill, othernode, othernodepos
|
||
|
local add, pos_a, pos_b, sat
|
||
|
local size, desc, --[[curve, desired_curve]] ang
|
||
|
--local dir = Vector(0, 0, 0)
|
||
|
|
||
|
self:DoEdgeScroll(dt)
|
||
|
|
||
|
local campos = self.vCamPos
|
||
|
--campos.x = Lerp(math.min(lifetime * 0.1, (realtime - self.ZoomChange) * 4), campos.x, self.DesiredZoom)
|
||
|
campos.x = math.Approach(campos.x, self.DesiredZoom, dt * 13500)
|
||
|
self:SetCamPos(campos)
|
||
|
|
||
|
surface.SetDrawColor(0, 0, 0, 252)
|
||
|
surface.DrawRect(0, 0, w, h)
|
||
|
|
||
|
ang = self.aLookAngle
|
||
|
if not ang then
|
||
|
ang = (self.vLookatPos - self.vCamPos):Angle()
|
||
|
end
|
||
|
local to_camera = ang:Forward() * -1
|
||
|
--ang.roll = math.sin(realtime / 4) * 1.8
|
||
|
|
||
|
local mx, my = gui.MousePos()
|
||
|
local aimvector = util.AimVector(ang, self.fFOV, mx, my, w, h)
|
||
|
local intersectpos = util.IntersectRayWithPlane(self.vCamPos, aimvector, self:GetLookAt(), Vector(-1, 0, 0))
|
||
|
|
||
|
cam.Start3D( self.vCamPos, ang, self.fFOV, 0, 0, w, h, 5, self.FarZ )
|
||
|
cam.IgnoreZ( true )
|
||
|
|
||
|
render_SuppressEngineLighting( true )
|
||
|
render.SetLightingOrigin( vector_origin )
|
||
|
render.ResetModelLighting( self.colAmbientLight.r / 255, self.colAmbientLight.g / 255, self.colAmbientLight.b / 255 )
|
||
|
|
||
|
for i=0, 6 do
|
||
|
col = self.DirectionalLight[ i ]
|
||
|
if col then
|
||
|
render.SetModelLighting( i, col.r / 255, col.g / 255, col.b / 255 )
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local particles = self.Particles
|
||
|
render.SetMaterial(matSmoke)
|
||
|
for i, particle in pairs(particles) do
|
||
|
particle[2] = particle[2] + particle[3] * dt
|
||
|
colSmoke.a = particle[5]
|
||
|
render.DrawQuadEasy(particle[1], to_camera, particle[4], particle[4], colSmoke, particle[2])
|
||
|
end
|
||
|
|
||
|
local skillnodes = self.SkillNodes
|
||
|
local campost = self.vCamPos
|
||
|
local camheightadj = (campost.x ^ 2) * 1.0035 -- Don't render nodes outside our view.
|
||
|
|
||
|
render.SetMaterial(matBeam)
|
||
|
for id, node in pairs(skillnodes) do
|
||
|
if IsValid(node) then
|
||
|
nodepos = node:GetPos()
|
||
|
|
||
|
if (nodepos - campost):LengthSqr() > camheightadj then
|
||
|
continue
|
||
|
end
|
||
|
|
||
|
skill = node.Skill
|
||
|
|
||
|
if skill.Disabled then
|
||
|
continue
|
||
|
end
|
||
|
|
||
|
if skill.Connections then
|
||
|
for connectid, _ in pairs(skill.Connections) do
|
||
|
connectskill = GAMEMODE.Skills[connectid]
|
||
|
if id < connectid and (not connectskillskill or connectskill and not connectskill.Disabled) then -- Nodes are double linked so only draw one half-edge
|
||
|
othernode = skillnodes[connectid]
|
||
|
if IsValid(othernode) then
|
||
|
othernodepos = othernode:GetPos()
|
||
|
local beamsize = 4
|
||
|
if MySelf:IsSkillUnlocked(node.SkillID) or MySelf:IsSkillUnlocked(connectid) then
|
||
|
colBeam.r = 32
|
||
|
colBeam.g = 128
|
||
|
colBeam.b = 255
|
||
|
elseif MySelf:SkillCanUnlock(node.SkillID) or MySelf:SkillCanUnlock(connectid) then
|
||
|
colBeam.r = 255
|
||
|
colBeam.g = 192
|
||
|
colBeam.b = 0
|
||
|
else
|
||
|
colBeam.r = 128
|
||
|
colBeam.g = 40
|
||
|
colBeam.b = 40
|
||
|
|
||
|
beamsize = 2
|
||
|
end
|
||
|
|
||
|
if hoveredskill == node.SkillID or hoveredskill == connectid then
|
||
|
add = math.abs(math.sin(realtime * math.pi)) * 120
|
||
|
colBeam.r = math.min(colBeam.r + add, 255)
|
||
|
colBeam.g = math.min(colBeam.g + add, 255)
|
||
|
colBeam.b = math.min(colBeam.b + add, 255)
|
||
|
|
||
|
colBeam.a = 180
|
||
|
colBeam2.a = 255
|
||
|
else
|
||
|
colBeam.a = 110
|
||
|
colBeam2.a = 190
|
||
|
end
|
||
|
|
||
|
pos_a = nodepos + Vector(-16, 0, 0)
|
||
|
pos_b = othernodepos + Vector(-16, 0, 0)
|
||
|
splines = skill.Splines and skill.Splines[connectid]
|
||
|
if splines then
|
||
|
local numsplines = #splines
|
||
|
render_StartBeam(#splines)
|
||
|
for i, spline in ipairs(splines) do
|
||
|
render.AddBeam(spline, 4, i / numsplines, colBeam2)
|
||
|
end
|
||
|
render_EndBeam()
|
||
|
render_StartBeam(#splines)
|
||
|
for i, spline in ipairs(splines) do
|
||
|
render.AddBeam(spline, 12, i / numsplines, colBeam)
|
||
|
end
|
||
|
render_EndBeam()
|
||
|
else
|
||
|
render_DrawBeam(pos_a, pos_b, beamsize, 0, 1, colBeam2)
|
||
|
render_DrawBeam(pos_a, pos_b, 16, 0, 1, colBeam)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local oldskill = hoveredskill
|
||
|
hoveredskill = nil
|
||
|
|
||
|
local angle = (realtime * 180) % 360
|
||
|
|
||
|
for id, node in pairs(skillnodes) do
|
||
|
if IsValid(node) then
|
||
|
nodepos = node:GetPos()
|
||
|
if (nodepos - campost):LengthSqr() > camheightadj then
|
||
|
continue
|
||
|
end
|
||
|
|
||
|
skillid = node.SkillID
|
||
|
skill = node.Skill
|
||
|
selected = not skill.Disabled and intersectpos and nodepos:DistToSqr(intersectpos) <= 36 -- 6^2
|
||
|
|
||
|
cam.Start3D2D(node:GetPos() - to_camera * 8, Angle(0, 90, 90), 0.09)
|
||
|
surface.DisableClipping(true)
|
||
|
DisableClipping(true)
|
||
|
|
||
|
if selected then
|
||
|
hoveredskill = skillid
|
||
|
|
||
|
sat = 1 - math.abs(math.sin(realtime * math.pi)) * 0.25
|
||
|
else
|
||
|
sat = 1
|
||
|
end
|
||
|
|
||
|
local notunlockable = false
|
||
|
local divs = nodecolors[skill.Tree]
|
||
|
|
||
|
if skill.Disabled or (skillid == -1 and not can_remort) then
|
||
|
render_SetColorModulation(sat / 6, sat / 6, sat / 6)
|
||
|
elseif skillid == -1 then
|
||
|
render_SetColorModulation(sat, sat, sat)
|
||
|
elseif skillid < -1 then
|
||
|
local tbl = particlecolors[-skillid - 1]
|
||
|
|
||
|
render_SetColorModulation(tbl.r/255, tbl.g/255, tbl.b/255)
|
||
|
elseif MySelf:IsSkillDesired(skillid) then
|
||
|
render_SetColorModulation(sat / 4, sat / 4, sat / 2)
|
||
|
elseif MySelf:IsSkillUnlocked(skillid) then
|
||
|
render_SetColorModulation(sat, sat, sat)
|
||
|
elseif MySelf:SkillCanUnlock(skillid) then
|
||
|
render_SetColorModulation(sat, sat / 1.25, sat / 4)
|
||
|
else
|
||
|
render_SetColorModulation(sat / divs[1] / 1.25, sat / divs[2] / 1.25, sat / divs[3] / 1.25)
|
||
|
notunlockable = true
|
||
|
end
|
||
|
|
||
|
render_ModelMaterialOverride(matWhite)
|
||
|
render_SetBlend(0.95)
|
||
|
|
||
|
node:DrawModel()
|
||
|
|
||
|
render_SetBlend(1)
|
||
|
render_ModelMaterialOverride()
|
||
|
|
||
|
render_SetColorModulation(1, 1, 1)
|
||
|
|
||
|
if self.DesiredZoom < 9500 then
|
||
|
local colo = skill.Disabled and COLOR_DARKGRAY or selected and color_white or notunlockable and COLOR_MIDGRAY or COLOR_GRAY
|
||
|
|
||
|
draw_SimpleText(skill.Name, skillid <= -1 and "ZS3D2DFont2Big" or "ZS3D2DFont2", 0, 0, colo, TEXT_ALIGN_CENTER)
|
||
|
end
|
||
|
|
||
|
DisableClipping(false)
|
||
|
surface.DisableClipping(false)
|
||
|
cam.End3D2D()
|
||
|
|
||
|
render.SetMaterial(matGlow)
|
||
|
if skillid == -1 then
|
||
|
if can_remort then
|
||
|
render.DrawQuadEasy(nodepos, to_camera, 32, 32, color_white, angle)
|
||
|
end
|
||
|
elseif not skill.Disabled then
|
||
|
colGlow.r = sat * 255 colGlow.g = sat * 255 colGlow.b = sat * 255
|
||
|
if MySelf:IsSkillDesired(skillid) then
|
||
|
colGlow.r = colGlow.r / 4
|
||
|
colGlow.g = colGlow.g / 4
|
||
|
elseif not MySelf:IsSkillUnlocked(skillid) then
|
||
|
if MySelf:SkillCanUnlock(skillid) then
|
||
|
colGlow.g = colGlow.g / 1.5
|
||
|
colGlow.b = 0
|
||
|
else
|
||
|
colGlow.r = colGlow.r / divs[1]
|
||
|
colGlow.g = colGlow.g / divs[2]
|
||
|
colGlow.b = colGlow.b / divs[3]
|
||
|
end
|
||
|
end
|
||
|
size = selected and 40 or 27
|
||
|
render.DrawQuadEasy(nodepos, to_camera, size, size, colGlow, angle)
|
||
|
angle = angle + 45
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if intersectpos then
|
||
|
intersectpos = intersectpos + Vector(16, 0, 0)
|
||
|
render.SetMaterial(matGlow)
|
||
|
render.DrawQuadEasy(intersectpos, to_camera, 12, 12, color_white, realtime * 90)
|
||
|
end
|
||
|
|
||
|
render_SuppressEngineLighting(false)
|
||
|
|
||
|
cam.IgnoreZ(false)
|
||
|
cam.End3D()
|
||
|
|
||
|
if oldskill ~= hoveredskill then
|
||
|
self.Top:Stop()
|
||
|
|
||
|
if hoveredskill then
|
||
|
skill = hoveredskill < -1 and TREE_SKILLS[-hoveredskill - 1] or hoveredskill == -1 and REMORT_SKILL or GAMEMODE.Skills[hoveredskill]
|
||
|
self.SkillName:SetText(skill.Name)
|
||
|
self.SkillName:SizeToContents()
|
||
|
|
||
|
desc = string.Explode("\n", skill.Description)
|
||
|
local txt, colid
|
||
|
for i=1, 5 do
|
||
|
txt = desc[i] or " "
|
||
|
if txt:sub(1, 1) == "^" then
|
||
|
colid = tonumber(txt:sub(2, 2)) or 0
|
||
|
txt = txt:sub(3)
|
||
|
self.SkillDesc[i]:SetTextColor(util.ColorIDToColor(colid, COLOR_GRAY))
|
||
|
else
|
||
|
self.SkillDesc[i]:SetTextColor(COLOR_GRAY)
|
||
|
end
|
||
|
self.SkillDesc[i]:SetText(txt)
|
||
|
self.SkillDesc[i]:SizeToContents()
|
||
|
end
|
||
|
|
||
|
surface.PlaySound("zombiesurvival/ui/misc1.ogg")
|
||
|
|
||
|
self.Top:SetAlpha(0)
|
||
|
self.Top:AlphaTo(255, 0.15)
|
||
|
else
|
||
|
self.Top:AlphaTo(0, 0.15)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
self.LastPaint = realtime
|
||
|
|
||
|
local fgalpha = 255 - lifetime * 100
|
||
|
if fgalpha > 0 then
|
||
|
surface.SetDrawColor(0, 0, 0, fgalpha)
|
||
|
surface.DrawRect(0, 0, w, h)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function PANEL:OnMousePressed(mc)
|
||
|
if mc == MOUSE_LEFT then
|
||
|
local contextmenu = self.ContextMenu
|
||
|
|
||
|
if hoveredskill then
|
||
|
local mx, my = gui.MousePos()
|
||
|
local can_remort = MySelf:CanSkillsRemort()
|
||
|
contextmenu:SetPos(mx - contextmenu:GetWide() / 2, my - contextmenu:GetTall() / 2)
|
||
|
|
||
|
if hoveredskill == -1 and can_remort then
|
||
|
Derma_Query(
|
||
|
"Are you ABSOLUTELY sure you want to remort?\nYou will revert to level 1, lose all skills, but have 1 extra SP.\nThis cannot be undone!",
|
||
|
"Warning",
|
||
|
"OK",
|
||
|
function() net.Start("zs_skills_remort") net.SendToServer() end,
|
||
|
"Cancel",
|
||
|
function() end
|
||
|
)
|
||
|
|
||
|
return
|
||
|
elseif hoveredskill == -1 then
|
||
|
self:DisplayMessage("You need to be level 50 to remort!", COLOR_RED)
|
||
|
surface.PlaySound("buttons/button8.wav")
|
||
|
|
||
|
return
|
||
|
elseif MySelf:IsSkillDesired(hoveredskill) then
|
||
|
if GAMEMODE.Skills[hoveredskill].AlwaysActive then
|
||
|
self:DisplayMessage("You can't deactivate this skill!", COLOR_RED)
|
||
|
surface.PlaySound("buttons/button8.wav")
|
||
|
|
||
|
return
|
||
|
end
|
||
|
|
||
|
contextmenu.Button:SetText("Deactivate")
|
||
|
elseif MySelf:IsSkillUnlocked(hoveredskill) then
|
||
|
contextmenu.Button:SetText("Activate")
|
||
|
elseif MySelf:SkillCanUnlock(hoveredskill) then
|
||
|
if MySelf:GetZSSPRemaining() >= 1 then
|
||
|
contextmenu.Button:SetText("Unlock")
|
||
|
else
|
||
|
self:DisplayMessage("You need SP to unlock this skill!", COLOR_RED)
|
||
|
surface.PlaySound("buttons/button8.wav")
|
||
|
|
||
|
return
|
||
|
end
|
||
|
else
|
||
|
self:DisplayMessage("You need to unlock an adjacent skill and meet any listed requirements!", COLOR_RED)
|
||
|
surface.PlaySound("buttons/button8.wav")
|
||
|
|
||
|
return
|
||
|
end
|
||
|
|
||
|
contextmenu.SkillID = hoveredskill
|
||
|
|
||
|
contextmenu:SetVisible(true)
|
||
|
else
|
||
|
contextmenu:SetVisible(false)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PANEL:OnMouseWheeled(delta)
|
||
|
self.DesiredZoom = math.Clamp(self.DesiredZoom - delta * 500, 2500, 25000)
|
||
|
end
|
||
|
|
||
|
function PANEL:OnRemove()
|
||
|
for _, node in pairs(self.SkillNodes) do
|
||
|
if IsValid(node) then
|
||
|
node:Remove()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local snd = self.AmbientSound
|
||
|
snd:FadeOut(1.5)
|
||
|
timer.Simple(1.5, function() if snd then snd:Stop() end end)
|
||
|
end
|
||
|
|
||
|
vgui.Register("ZSSkillWeb", PANEL, "Panel")
|
||
|
|
||
|
function GM:DrawXPBar(x, y, w, h, xpw, barwm, hm, level)
|
||
|
local barw = xpw * barwm
|
||
|
local xp = MySelf:GetZSXP()
|
||
|
local progress = GAMEMODE:ProgressForXP(xp)
|
||
|
local rlevel = MySelf:GetZSRemortLevel()
|
||
|
local append = ""
|
||
|
if rlevel > 0 then
|
||
|
append = " // R.Level "..rlevel
|
||
|
end
|
||
|
|
||
|
surface.SetDrawColor(0, 0, 0, 220)
|
||
|
surface.DrawRect(x, y, barw, 4)
|
||
|
|
||
|
surface.SetDrawColor(10, 200, 10, 160)
|
||
|
surface.DrawRect(x, y, barw * progress, 2)
|
||
|
surface.SetDrawColor(0, 170, 0, 160)
|
||
|
surface.DrawRect(x, y + 2, barw * progress, 2)
|
||
|
|
||
|
if level == GAMEMODE.MaxLevel then
|
||
|
draw_SimpleText("Level MAX"..append, "ZSXPBar", xpw / 2, h / 2 + y, COLOR_GREEN, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||
|
else
|
||
|
if progress > 0 then
|
||
|
local lx = x + barw * progress - 1
|
||
|
surface.SetDrawColor(255, 255, 255, 20 + math.abs(math.sin(RealTime() * 2)) * 170)
|
||
|
surface.DrawLine(lx, y - 2, lx, y + 7)
|
||
|
surface.SetDrawColor(255, 255, 255, 160)
|
||
|
surface.DrawLine(x, y - 1, x, y + 5)
|
||
|
lx = x + barw - 1
|
||
|
surface.DrawLine(lx, y - 1, lx, y + 5)
|
||
|
end
|
||
|
|
||
|
draw_SimpleText("Level "..level..append, "ZSXPBar", x, h / 2 + y, COLOR_WHITE, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||
|
draw_SimpleText(string.CommaSeparate(xp).." / "..string.CommaSeparate(GAMEMODE:XPForLevel(level + 1)).." XP", "ZSXPBar", x + barw, h / 2 + y, COLOR_WHITE, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
PANEL = {}
|
||
|
|
||
|
PANEL.PlayerLevel = 0
|
||
|
|
||
|
function PANEL:Init()
|
||
|
self:SetMouseInputEnabled(false)
|
||
|
self:SetKeyboardInputEnabled(false)
|
||
|
end
|
||
|
|
||
|
function PANEL:PerformLayout()
|
||
|
local screenscale = BetterScreenScale()
|
||
|
|
||
|
self:SetSize(350 * screenscale, 36 * screenscale)
|
||
|
if GAMEMODE.GameStatePanel and GAMEMODE.GameStatePanel:IsValid() then
|
||
|
self:MoveBelow(GAMEMODE.GameStatePanel, screenscale * 32)
|
||
|
else
|
||
|
self:AlignTop(400)
|
||
|
end
|
||
|
self:AlignLeft()
|
||
|
end
|
||
|
|
||
|
function PANEL:Think()
|
||
|
local new_level = MySelf:GetZSLevel()
|
||
|
|
||
|
if new_level ~= self.PlayerLevel then
|
||
|
if new_level ~= 1 and self.FirstLevelChange then
|
||
|
GAMEMODE:CenterNotify(translate.Format("you_ascended_to_level_x", new_level))
|
||
|
surface.PlaySound("weapons/physcannon/energy_disintegrate"..math.random(4, 5)..".wav")
|
||
|
else
|
||
|
self.FirstLevelChange = true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
self.PlayerLevel = new_level
|
||
|
end
|
||
|
|
||
|
local matGradientLeft = CreateMaterial("gradient-l", "UnlitGeneric", {["$basetexture"] = "vgui/gradient-l", ["$vertexalpha"] = "1", ["$vertexcolor"] = "1", ["$ignorez"] = "1", ["$nomip"] = "1"})
|
||
|
local colFlash = Color(255, 255, 255)
|
||
|
function PANEL:Paint(w, h)
|
||
|
surface.SetDrawColor(0, 0, 0, 180)
|
||
|
surface.DrawRect(0, 0, w * 0.4, h)
|
||
|
surface.SetMaterial(matGradientLeft)
|
||
|
surface.DrawTexturedRect(w * 0.4, 0, w * 0.6, h)
|
||
|
|
||
|
local xpw = w * 0.85
|
||
|
local x = xpw * 0.03
|
||
|
local y = h * 0.15
|
||
|
GAMEMODE:DrawXPBar(x, y, w, h, xpw, 0.9, 0.85, self.PlayerLevel)
|
||
|
|
||
|
local sp = MySelf:GetZSSPRemaining()
|
||
|
if sp > 0 then
|
||
|
colFlash.a = 90 + math.abs(math.sin(RealTime() * 2)) * 160
|
||
|
draw_SimpleText(sp.." SP", "ZSHUDFontSmallest", w - 2, h / 2, colFlash, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
vgui.Register("ZSExperienceHUD", PANEL, "Panel")
|
||
|
|
||
|
function GM:ToggleSkillWeb()
|
||
|
if self.SkillWeb and self.SkillWeb:IsValid() then
|
||
|
self.SkillWeb:Remove()
|
||
|
self.SkillWeb = nil
|
||
|
return
|
||
|
end
|
||
|
|
||
|
self.SkillWeb = vgui.Create("ZSSkillWeb")
|
||
|
end
|
||
|
|
||
|
local meta = FindMetaTable("Player")
|
||
|
if not meta then return end
|
||
|
|
||
|
function meta:SetSkillDesired(skillid, desired)
|
||
|
local desiredskills = self:GetDesiredActiveSkills()
|
||
|
|
||
|
if desired then
|
||
|
if self:IsSkillUnlocked(skillid) and not self:IsSkillDesired(skillid) then
|
||
|
table.insert(desiredskills, skillid)
|
||
|
end
|
||
|
else
|
||
|
table.RemoveByValue(desiredskills, skillid)
|
||
|
end
|
||
|
|
||
|
self:SetDesiredActiveSkills(desiredskills)
|
||
|
end
|
||
|
|
||
|
function meta:SetSkillUnlocked(skillid, unlocked)
|
||
|
local unlockedskills = self:GetUnlockedSkills()
|
||
|
|
||
|
if self:IsSkillUnlocked(skillid) ~= unlocked then
|
||
|
if unlocked then
|
||
|
table.insert(unlockedskills, skillid)
|
||
|
else
|
||
|
table.RemoveByValue(unlockedskills, skillid)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
self:SetUnlockedSkills(unlockedskills)
|
||
|
end
|
||
|
|
||
|
function meta:SetDesiredActiveSkills(skills, nosend)
|
||
|
self.DesiredActiveSkills = table.ToKeyValues(skills)
|
||
|
end
|
||
|
|
||
|
function meta:SetActiveSkills(skills, nosend)
|
||
|
self.ActiveSkills = table.ToAssoc(skills)
|
||
|
end
|
||
|
|
||
|
function meta:SetUnlockedSkills(skills, nosend)
|
||
|
self.UnlockedSkills = table.ToKeyValues(skills)
|
||
|
end
|