zombiesurvival-evolved/gamemodes/zombiesurvival/gamemode/skillweb/cl_skillweb.lua

1331 lines
36 KiB
Lua
Raw Permalink Normal View History

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