aboutsummaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
authorBob Blackmon <bob.blackmon@ymail.com>2017-04-02 23:21:08 -0400
committerBob Blackmon <bob.blackmon@ymail.com>2017-04-02 23:21:08 -0400
commit49e018aee0b82e805660e819d7b7647d8ac9c07c (patch)
tree40df2d15206b7fe30d62caf30be17c40b2fa5457 /lua
parentb13ed9659114f5957f00f27ee6fe0f2d353d18c5 (diff)
downloadzones-49e018aee0b82e805660e819d7b7647d8ac9c07c.tar.gz
zones-49e018aee0b82e805660e819d7b7647d8ac9c07c.tar.bz2
zones-49e018aee0b82e805660e819d7b7647d8ac9c07c.zip
Changed folder
Diffstat (limited to 'lua')
-rw-r--r--lua/entities/ent_zone_point.lua132
-rw-r--r--lua/weapons/weapon_zone_designator.lua703
-rw-r--r--lua/zones.lua467
3 files changed, 1302 insertions, 0 deletions
diff --git a/lua/entities/ent_zone_point.lua b/lua/entities/ent_zone_point.lua
new file mode 100644
index 0000000..986221a
--- /dev/null
+++ b/lua/entities/ent_zone_point.lua
@@ -0,0 +1,132 @@
+
+AddCSLuaFile()
+
+DEFINE_BASECLASS( "base_anim" )
+
+ENT.PrintName = "Zone Point"
+ENT.Author = "Bobblehead"
+ENT.Information = "A point in the zone designator."
+ENT.Category = "Other"
+
+ENT.Editable = false
+ENT.Spawnable = false
+ENT.AdminOnly = false
+ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
+
+
+function ENT:Initialize()
+ self:SetModel("models/hunter/blocks/cube025x025x025.mdl")
+ self:SetCollisionGroup(COLLISION_GROUP_WORLD)
+ self:DrawShadow(false)
+
+ if SERVER then
+ self:SetUseType(SIMPLE_USE)
+ self:PhysicsInit(SOLID_BBOX)
+ self:GetPhysicsObject():EnableMotion(false)
+
+ self:GetPhysicsObject():SetMass(1)
+
+ else
+ self:SetRenderBoundsWS(self:GetPos(),self:GetPos()+Vector(0,0,self:GetTall()))
+ end
+end
+
+function ENT:SetupDataTables()
+ self:NetworkVar("Entity",0,"Next")
+ self:NetworkVar("Float",0,"Tall")
+ self:NetworkVar("Int",0,"ZoneID")
+ self:NetworkVar("String",0,"ZoneClass")
+ self:NetworkVar("Int",1,"AreaNumber")
+end
+
+function ENT:DrawTranslucent()
+ local wep = LocalPlayer():GetActiveWeapon()
+ if wep:IsValid() and wep:GetClass() == "weapon_zone_designator" then
+
+ if wep:GetZoneClass() == self:GetZoneClass() or GetConVarNumber("zone_filter") == 0 then
+ self:DrawModel()
+
+ local p = self:GetPos()
+ p.z = p.z + self:GetTall()
+ render.Model({model=self:GetModel(),pos=p,ang=angle_zero})
+
+ render.SetMaterial(Material("cable/cable2"))
+ render.DrawBeam( self:GetPos(), p, 1, 1, 0, color_white )
+
+ local next = self:GetNext()
+ if IsValid(next) then
+ local class = self:GetZoneClass()
+
+ render.DrawBeam( self:GetPos(), next:GetPos(), 1, 1, 0, color_white )
+
+ local n = next:GetPos()
+ n.z = n.z + next:GetTall()
+ render.DrawBeam( p, n, 1, 1, 0, color_white )
+
+ render.SetColorMaterial()
+ local col1 = table.Copy(zones.Classes[class])
+ col1.a = 80
+ local col2 = {a=80}
+ col2.r = col1.r * .5
+ col2.g = col1.g * .5
+ col2.b = col1.b * .5
+
+ render.DrawQuad(p,self:GetPos(),next:GetPos(),n,col1)
+ render.DrawQuad(n,next:GetPos(),self:GetPos(),p,col2)
+
+ local id = self:GetZoneID()
+ local classtxt = id != -1 and class .. " (# "..id..")" or class
+
+ local ang = (p-self:GetPos()):Cross(n-self:GetPos()):Angle()
+ ang:RotateAroundAxis(ang:Right(), 90)
+ ang:RotateAroundAxis(ang:Up(),-90)
+ cam.Start3D2D((n+self:GetPos())/2,ang,.2)
+ draw.SimpleText(classtxt,"DermaLarge",0,0,color_white,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
+ cam.End3D2D()
+
+ ang:RotateAroundAxis(Vector(0,0,1), 180)
+ cam.Start3D2D((n+self:GetPos())/2,ang,.2)
+ draw.SimpleText(classtxt,"DermaLarge",0,0,color_white,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
+ cam.End3D2D()
+
+ end
+ end
+ end
+end
+
+function ENT:Think()
+ if CLIENT then
+ local next = self:GetNext()
+ if IsValid(next) and next != self.resizedto then
+ self:SetRenderBoundsWS(self:GetPos(),next:GetPos()+Vector(0,0,next:GetTall()))
+ self.resizedto = next
+ end
+
+ local wep = LocalPlayer():GetActiveWeapon()
+ if wep:IsValid() and wep:GetClass() == "weapon_zone_designator" then
+ if LocalPlayer():GetEyeTrace().Entity == self then
+ self:SetColor(Color(255,0,0))
+ elseif wep:GetCurrentPoint() == self then
+ self:SetColor(Color(0,0,255))
+ else
+ self:SetColor(color_white)
+ end
+ end
+ else
+
+ if IsValid(self.Resizing) then
+
+ self:SetTall((self.Resizing:GetEyeTrace().HitPos - self:GetPos()).z)
+
+ end
+
+ end
+end
+
+function ENT:OnRemove()
+ if SERVER then
+ if IsValid(self:GetNext()) then
+ self:GetNext():Remove()
+ end
+ end
+end
diff --git a/lua/weapons/weapon_zone_designator.lua b/lua/weapons/weapon_zone_designator.lua
new file mode 100644
index 0000000..fd2c0c5
--- /dev/null
+++ b/lua/weapons/weapon_zone_designator.lua
@@ -0,0 +1,703 @@
+
+if CLIENT then
+ CreateClientConVar("zone_tall",200,false,true)
+ CreateClientConVar("zone_class","",false,true)
+ CreateClientConVar("zone_editmode",1,false,true)
+ CreateClientConVar("zone_filter",0,false,true)
+
+ surface.CreateFont( "zones_save", {
+ font = "Roboto", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
+ size = 20,
+ weight = 1000,
+ } )
+ surface.CreateFont( "zones_screen", {
+ font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
+ size = 20,
+ weight = 1000,
+
+ } )
+else
+ concommand.Add("zone_swep",function(p,c,a)
+ if p:IsAdmin() then
+ p:Give("weapon_zone_designator")
+ end
+ end)
+end
+
+if engine.ActiveGamemode() == "terrortown" then
+ SWEP.Base = "weapon_tttbase"
+ SWEP.Kind = WEAPON_EQUIP2
+end
+
+
+SWEP.NoSights = true
+
+SWEP.PrintName = "Zone Designator"
+SWEP.Author = "Bobblehead"
+SWEP.Purpose = "Creates zones. Reload for menu. Right click to remove a point/zone."
+
+SWEP.Slot = 5
+SWEP.SlotPos = 5
+
+SWEP.Spawnable = true
+SWEP.AdminOnly = true
+
+SWEP.HoldType = "pistol"
+SWEP.ViewModelFOV = 51
+SWEP.ViewModelFlip = false
+SWEP.UseHands = true
+SWEP.ViewModel = "models/weapons/c_toolgun.mdl"
+SWEP.WorldModel = "models/weapons/w_toolgun.mdl"
+SWEP.AutoSwitchFrom = true
+SWEP.AutoSwitchTo = true
+
+SWEP.Primary.ClipSize = -1
+SWEP.Primary.DefaultClip = -1
+SWEP.Primary.Automatic = false
+SWEP.Primary.Delay = .5
+SWEP.Primary.Ammo = "none"
+
+SWEP.Secondary.ClipSize = -1
+SWEP.Secondary.DefaultClip = -1
+SWEP.Secondary.Automatic = false
+SWEP.Secondary.Ammo = "none"
+
+
+function SWEP:SetupDataTables()
+ self:NetworkVar("Float",0,"Tall")
+ self:NetworkVar("String",0,"ZoneClass")
+ self:NetworkVar("Entity",0,"CurrentPoint")
+ self:NetworkVar("Int",0,"Mode")
+
+end
+
+function SWEP:Initialize()
+
+ self:SetTall(150)
+ self:SetMode(1)
+
+ self:SetCurrentPoint(NULL)
+end
+
+function SWEP:Deploy()
+ if SERVER then
+ local points = ents.FindByClass("ent_zone_point")
+ local ct = 0
+ for k,v in pairs(zones.List) do
+ for k2,v2 in pairs(v.points)do
+ for k3, v3 in pairs(v2) do
+ ct = ct + 1
+ end
+ end
+ end
+ if #points != ct then
+ zones.CreatePointEnts(points)
+ end
+ end
+end
+
+function SWEP:Holster()
+ if SERVER then
+ local none = true
+ for k, ply in pairs(player.GetAll()) do
+ if ply == self.Owner then continue end
+ local wep = ply:GetActiveWeapon()
+ if IsValid(wep) and wep:GetClass() == "weapon_zone_designator" then
+ none = false
+ break
+ end
+ end
+ if none then
+ for k,v in pairs(ents.FindByClass("ent_zone_point"))do
+ v:Remove()
+ end
+ end
+
+ self:SetCurrentPoint(NULL)
+
+ return true
+ end
+end
+
+function SWEP:DrawHUD()
+ local z,id = LocalPlayer():GetCurrentZone(GetConVarNumber("zone_filter") == 1 and self:GetZoneClass() or nil)
+ z = z and z.class.."(# "..id..")" or "None"
+ draw.SimpleText("Current Zone: "..z, "DermaLarge", 100,100)
+end
+
+function SWEP:PrimaryAttack()
+
+
+ self:SendWeaponAnim( ACT_VM_PRIMARYATTACK )
+
+ if SERVER then
+ self.Owner:EmitSound("buttons/button15.wav")
+ end
+
+ self:UpdateSettings()
+ local mode = self:GetMode()
+
+ if mode == 1 then //Create
+ if CLIENT then return end
+ self:PlacePoint()
+ elseif mode == 2 then //Merge
+ if CLIENT then return end
+ self:MergeZones()
+
+ elseif mode == 3 then //Edit
+ local curr = self:GetCurrentPoint()
+
+ if IsValid(curr) then
+ if CLIENT then return end
+ local next = curr
+ repeat
+
+ next.Resizing = nil
+
+ next = next:GetNext()
+
+ until ( next == curr )
+
+ zones.List[curr:GetZoneID()].height[curr:GetAreaNumber()] = curr:GetTall()
+ zones.CalcBounds(zones.List[id])
+
+ self:SetCurrentPoint(NULL)
+
+ elseif CLIENT then
+ local tr = self.Owner:GetEyeTrace()
+ if !tr.HitWorld and IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" then
+ zones.ShowOptions(tr.Entity:GetZoneID())
+ end
+
+ end
+
+ elseif mode == 4 then
+ if CLIENT then return end
+
+ local curr = self:GetCurrentPoint()
+ local tr = self.Owner:GetEyeTrace()
+
+ if !tr.HitWorld and IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" then
+
+ self:SetCurrentPoint(tr.Entity)
+
+ elseif IsValid(curr) then
+
+ local id,areanum = curr:GetZoneID(), curr:GetAreaNumber()
+ local next = curr:GetNext()
+
+ local new = ents.Create("ent_zone_point")
+ local p = curr:GetPos()
+ tr.HitPos.z = p.z
+ new:SetPos(tr.HitPos)
+ curr:SetNext(new)
+ -- self:GetCurrentPoint():DeleteOnRemove(new)
+
+ new.LastPoint = self:GetCurrentPoint()
+ self:SetCurrentPoint(new)
+ new:SetTall(curr:GetTall())
+ new:SetZoneClass(curr:GetZoneClass())
+ new:SetZoneID(id)
+ new:SetAreaNumber(areanum)
+ new:Spawn()
+ new:SetNext(next)
+ next.LastPoint = new
+
+
+ local n = new
+ local pts = {}
+ repeat
+ pts[#pts+1] = n:GetPos() - Vector(0,0,2)
+ n = n:GetNext()
+ until (n == new)
+
+
+ zones.List[id].points[areanum] = pts
+
+ zones.CalcBounds(zones.List[id])
+
+ zones.Sync()
+
+ end
+
+ end
+
+end
+
+function SWEP:SecondaryAttack()
+ self:SendWeaponAnim( ACT_VM_PRIMARYATTACK )
+
+ if CLIENT then return end
+
+ self.Owner:EmitSound("buttons/button16.wav")
+
+ local tr = self.Owner:GetEyeTrace()
+ local mode = self:GetMode()
+
+ if mode == 1 then //delete
+
+ if !tr.HitWorld and IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" then
+ local point = tr.Entity
+ local last = point.LastPoint
+
+ local id = point:GetZoneID()
+ if id != -1 then
+
+ if #zones.List[id].points > 1 then
+ id = select(2,zones.Split(id, point:GetAreaNumber()))
+ end
+
+ zones.Remove(id)
+
+ end
+
+ point:Remove()
+
+ self:SetCurrentPoint(last)
+
+ elseif self:GetCurrentPoint():IsValid() then
+ local point, last = self:GetCurrentPoint(), self:GetCurrentPoint().LastPoint
+ point:Remove()
+ self:SetCurrentPoint(last or NULL)
+ end
+
+ elseif mode == 2 then //split
+
+ if !tr.HitWorld and IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" then
+ local point = tr.Entity
+ local id = point:GetZoneID()
+
+ if #zones.List[id].points == 1 then
+ return
+ end
+
+ local zone, newid = zones.Split(id, point:GetAreaNumber())
+
+ local next = point
+ repeat
+
+ next:SetZoneID(newid)
+ next:SetAreaNumber(next:GetAreaNumber() - #zones.List[id].points)
+
+ next = next:GetNext()
+
+ until ( next == point )
+
+ end
+
+ elseif mode == 3 then //resize
+ local curr = self:GetCurrentPoint()
+
+ if IsValid(curr) then
+
+ local next = curr
+ repeat
+
+ next.Resizing = nil
+
+ next = next:GetNext()
+
+ until ( next == curr )
+
+ zones.List[curr:GetZoneID()].height[curr:GetAreaNumber()] = curr:GetTall()
+ zones.CalcBounds(zones.List[curr:GetZoneID()])
+
+ zones.Sync()
+
+ self:SetCurrentPoint(NULL)
+
+ else
+ if !tr.HitWorld and IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" then
+ local next = tr.Entity
+ repeat
+ next.Resizing = self.Owner
+
+ next = next:GetNext()
+
+
+ until ( next == tr.Entity )
+
+ self:SetCurrentPoint(tr.Entity)
+ end
+ end
+
+ elseif mode == 4 then //Remove a point
+
+ if !tr.HitWorld and IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" or IsValid(self:GetCurrentPoint()) then
+
+ local hit = tr.Entity:IsValid() and tr.Entity or self:GetCurrentPoint()
+ local id,areanum = hit:GetZoneID(), hit:GetAreaNumber()
+
+ if #zones.List[id].points[areanum] > 3 then
+
+ local last = hit.LastPoint
+ local next = hit:GetNext()
+
+ next.LastPoint = last
+ last:SetNext(next)
+
+ hit:SetNext(NULL)
+ hit.LastPoint = nil
+ hit:Remove()
+
+ local n = next
+ local pts = {}
+ repeat
+ pts[#pts+1] = n:GetPos() - Vector(0,0,2)
+ n = n:GetNext()
+ until (n == next)
+
+ zones.List[id].points[areanum] = pts
+
+ zones.CalcBounds(zones.List[id])
+
+ zones.Sync()
+
+ self:SetCurrentPoint(last)
+
+ end
+
+ end
+
+ end
+
+
+end
+
+local mx,my
+if CLIENT then
+ mx, my = ScrW()/2, ScrH()/2
+end
+
+function SWEP:Reload() --show menu.
+
+ self:UpdateSettings()
+ if SERVER then
+ if game.SinglePlayer() then
+ self:CallOnClient("Reload")
+ end
+ return
+ end
+
+ if IsValid(self.frame) then return end
+
+ input.SetCursorPos(mx,my)
+
+ self:OpenMenu()
+
+end
+
+function SWEP:PlacePoint() --mode == 1
+ local tr = self.Owner:GetEyeTrace()
+ if tr.HitWorld then
+
+ local next = ents.Create("ent_zone_point")
+
+ if IsValid(self:GetCurrentPoint()) then
+ local p = self:GetCurrentPoint():GetPos()
+ tr.HitPos.z = p.z
+ next:SetPos(tr.HitPos)
+ self:GetCurrentPoint():SetNext(next)
+ -- self:GetCurrentPoint():DeleteOnRemove(next)
+ else
+ next:SetPos(tr.HitPos+Vector(0,0,1))
+ end
+
+ next.LastPoint = self:GetCurrentPoint()
+ self:SetCurrentPoint(next)
+ next:SetTall(self:GetTall())
+ next:SetZoneClass(self:GetZoneClass())
+ next:SetZoneID(-1)
+ next:SetAreaNumber(1)
+ next:Spawn()
+
+ elseif tr.Entity:IsValid() and tr.Entity:GetClass() == "ent_zone_point" and tr.Entity != self:GetCurrentPoint() then
+ if IsValid(self:GetCurrentPoint()) then
+ local next = tr.Entity
+ if !IsValid(next.LastPoint) then
+
+ self:GetCurrentPoint():SetNext(next)
+
+ if IsValid(next:GetNext()) then //we've come full circle.
+
+ next.LastPoint = self:GetCurrentPoint()
+
+ local id = select(2,zones.CreateZoneFromPoint(self:GetCurrentPoint()))
+ -- self:GetCurrentPoint():DeleteOnRemove(next)
+ self:SetCurrentPoint(NULL)
+
+ local o = self.Owner
+ timer.Simple(.1,function() -- wait for it to sync.
+ if IsValid(o) then
+ o:SendLua("zones.ShowOptions("..id..")")
+ end
+ end)
+
+ end
+
+ end
+ end
+
+
+ end
+end
+
+function SWEP:MergeZones()
+
+ local tr = self.Owner:GetEyeTrace()
+ if tr.HitWorld then
+ self:SetCurrentPoint(NULL)
+
+ elseif IsValid(tr.Entity) and tr.Entity:GetClass() == "ent_zone_point" then
+
+ local curr = self:GetCurrentPoint()
+ local trent = tr.Entity
+ if not IsValid(curr) then
+ self:SetCurrentPoint(trent)
+
+ else
+ //Merge the zones.
+ if curr:GetZoneID() != trent:GetZoneID() then
+ local cid = curr:GetZoneID()
+
+ --Change the points.
+ for k,next in pairs(ents.FindByClass("ent_zone_point")) do
+ if next:GetZoneID() == cid then
+
+ next:SetZoneID(trent:GetZoneID())
+ next:SetAreaNumber(next:GetAreaNumber() + #zones.List[trent:GetZoneID()].points)
+ next:SetZoneClass(zones.List[trent:GetZoneID()].class)
+
+ end
+
+ end
+
+ self:SetCurrentPoint(NULL)
+
+ zones.Merge(cid,trent:GetZoneID()) --from, to
+
+ else
+ self:SetCurrentPoint(trent)
+
+ end
+
+ end
+
+ end
+end
+
+function SWEP:UpdateSettings()
+ --load convars
+ local new_tall = SERVER and self.Owner:GetInfoNum("zone_tall",200) or GetConVarNumber("zone_tall")
+ local new_class = SERVER and self.Owner:GetInfo("zone_class") or GetConVarString("zone_class")
+ local new_mode = SERVER and self.Owner:GetInfoNum("zone_editmode",1) or GetConVarNumber("zone_editmode")
+
+ if new_class == "" then
+ for k,v in pairs(zones.Classes)do
+ new_class = k
+ self.Owner:ConCommand('zone_class "'..k..'"')
+ break
+ end
+ end
+
+ --If changed, remove current building area
+ if new_class != self:GetZoneClass() or new_tall != self:GetTall() or new_mode != self:GetMode() then
+ self:ResetTool()
+ end
+ assert(new_class != "", "No class is set for the zone designator! Did you call zones.RegisterClass()?\n")
+
+ --apply convars
+ self:SetTall(new_tall)
+ self:SetZoneClass(new_class)
+ self:SetMode(new_mode)
+end
+
+function SWEP:ResetTool()
+
+ if self:GetMode() == 1 and SERVER then
+ local cur = self:GetCurrentPoint()
+ if IsValid(cur) and cur:GetZoneID() == -1 then
+
+ while IsValid(cur) do
+ local last = cur.LastPoint
+ cur:Remove()
+ cur = last
+ end
+
+ end
+
+ end
+ self:SetCurrentPoint(NULL)
+end
+
+function SWEP:OpenMenu()
+ local zc = self:GetZoneClass()
+
+ local frame = vgui.Create("DFrame")
+ self.frame = frame
+ frame:SetSize(300,400)
+ frame:Center()
+ frame:MakePopup()
+ frame:ShowCloseButton(false)
+ frame:SetTitle("Zone Designator Options")
+ function frame:Think()
+ if not input.IsButtonDown(_G["KEY_"..input.LookupBinding("+reload"):upper()]) then
+ mx,my = gui.MousePos()
+ self:Close()
+ end
+ end
+
+ local ztitle = vgui.Create("DLabel",frame)
+ ztitle:Dock(TOP)
+ ztitle:DockMargin(7,0,5,0)
+ ztitle:SetText("Zone Class:")
+ ztitle:SizeToContents()
+
+ local zclass = vgui.Create("DComboBox",frame)
+ zclass:Dock(TOP)
+ zclass:DockMargin(5,0,5,0)
+ for k,v in pairs(zones.Classes) do
+ zclass:AddChoice(k,nil,k==zc)
+ end
+ function zclass:OnSelect(i,class)
+ RunConsoleCommand("zone_class",class)
+ end
+
+ local filter = vgui.Create("DCheckBoxLabel",frame)
+ filter:Dock(TOP)
+ filter:DockMargin(6,5,5,5)
+ filter:SetText("Filter zones of a different class.")
+ filter:SizeToContents()
+ filter:SetConVar("zone_filter")
+
+ local height = vgui.Create( "DNumSlider", frame )
+ height:Dock(TOP)
+ height:DockMargin(7,0,0,0)
+ height:SetText( "Zone Height:" )
+ height:SetMin( 0 )
+ height:SetMax( 1000 )
+ height:SetDecimals( 0 )
+ height:SetConVar( "zone_tall" )
+
+ local modetitle = vgui.Create("DLabel",frame)
+ modetitle:Dock(TOP)
+ modetitle:DockMargin(7,0,5,0)
+ modetitle:SetText("Tool Mode:")
+ modetitle:SizeToContents()
+
+ local mode = vgui.Create("DListView",frame)
+ mode:Dock(TOP)
+ mode:DockMargin(5,0,5,5)
+ mode:AddColumn("Mode")
+ mode:AddColumn("Info"):SetFixedWidth(180)
+ mode:SetTall(200)
+ mode:SetMultiSelect(false)
+
+ mode:AddLine("Create / Delete","Create new zones")
+ mode:AddLine("Merge / Split","Turn two zones into one.")
+ mode:AddLine("Edit / Resize","Change zone properties.")
+ mode:AddLine("Insert / Remove","Insert points into existing zones.")
+ mode:SelectItem(mode:GetLine(self:GetMode()))
+ function mode:OnRowSelected(id)
+ RunConsoleCommand("zone_editmode",id)
+ end
+
+
+ local save = vgui.Create("DButton",frame)
+ save:Dock(BOTTOM)
+ save:DockMargin(5,0,5,5)
+ save:SetText("SAVE ALL ZONES")
+ save:SetFont("zones_save")
+ save:SetTextColor(color_black)
+ save:SetTall(50)
+ function save:DoClick()
+ Derma_Query("Are you sure you want to save? This can't be undone.", "Save Confirmation",
+ "Yes", function()
+ RunConsoleCommand("zone_save")
+ end,
+ "No", function()
+ end
+ )
+ end
+
+
+
+end
+
+local modes = {
+ { --create
+ "Create",
+ "Delete"
+ },
+ { --merge
+ "Merge",
+ "Split"
+ },
+ { --edit
+ "Edit",
+ "Resize"
+ },
+ { --insert
+ "Insert",
+ "Remove"
+ },
+}
+local lmb, rmb = Material("gui/lmb.png","unlitgeneric"), Material("gui/rmb.png","unlitgeneric")
+function SWEP:DrawScreen(x,y,w,h)
+ local mode = self:GetMode()
+ local txt = modes[mode]
+
+ draw.SimpleText(txt[1], "zones_screen", x+w/4, -h/5, _, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
+ draw.SimpleText(txt[2], "zones_screen", w+x-w/4, -h/5, _, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
+
+ surface.SetDrawColor(color_white)
+ surface.DrawRect(x+w/2-1,y+h*.125,2,h*.75)
+
+ surface.SetMaterial(lmb)
+ surface.DrawTexturedRect(x+w/4-16,y+h*.55-16,32,32)
+ surface.SetMaterial(rmb)
+ surface.DrawTexturedRect(x+w*.75-16,y+h*.55-16,32,32)
+
+end
+
+local function GetBoneOrientation(self,ent)
+ local bone, pos, ang
+ bone = ent:LookupBone("Hand")
+ if (!bone) then return end
+ pos, ang = Vector(0,0,0), Angle(0,0,0)
+ local m = ent:GetBoneMatrix(bone)
+ if (m) then
+ pos, ang = m:GetTranslation(), m:GetAngles()
+ end
+ if (IsValid(self.Owner) and self.Owner:IsPlayer() and
+ ent == self.Owner:GetViewModel() and self.ViewModelFlip) then
+ ang.r = -ang.r // Fixes mirrored models
+ end
+ return pos, ang
+end
+function SWEP:ViewModelDrawn()
+ local vm = self.Owner:GetViewModel()
+ if !IsValid(vm) then return end
+
+ local pos, ang = GetBoneOrientation(self, vm )
+ local offset = Vector(.08, -5.16, 3.43)
+ local offsetAng = Angle(180, 0, -46)
+ local size = 0.0159
+ local drawpos = pos + ang:Forward() * offset.x + ang:Right() * offset.y + ang:Up() * offset.z
+ ang:RotateAroundAxis(ang:Up(), offsetAng.y)
+ ang:RotateAroundAxis(ang:Right(), offsetAng.p)
+ ang:RotateAroundAxis(ang:Forward(), offsetAng.r)
+
+ cam.Start3D2D(drawpos, ang, size)
+ local x,y,w,h = -72,-72
+ local w,h = 2*-x, 2*-y
+ //Draw Background:
+ surface.SetDrawColor( 200, 200, 200, 255 )
+ surface.SetDrawColor( 40,40,40,255 )
+ surface.DrawRect( x, y, w, h )
+
+ //Draw foreground:
+ self:DrawScreen(x,y,w,h)
+
+ cam.End3D2D()
+end \ No newline at end of file
diff --git a/lua/zones.lua b/lua/zones.lua
new file mode 100644
index 0000000..80aaa91
--- /dev/null
+++ b/lua/zones.lua
@@ -0,0 +1,467 @@
+
+local version = 1.1 -- Older versions will not run if a newer version is used in another script.
+--[[
+ ZONES - by Bobbleheadbob
+ WARNING: If you edit any of these files, make them use a different namespace. Multiple scripts may depend on this library so modifying it can break other scripts.
+
+ Purpose:
+ For easy in-game designation of persistent polygonal zones which are used by any script.
+
+ How to Use:
+ All zones are saved in zones.List; see an example below.
+ Zone creation is handled with weapon_zone_designator and ent_zone_point, but can be done in code as well.
+ When a zone is created, changed, or removed all zones are synced to clients. When clients join they are also synced.
+ Any extra details can be saved to a zone. Everything is written to a txt file and is persistent to the map.
+
+ Since multiple scripts might use the zones system, don't assume that every zone is related to your script.
+ To register a zone class, use zones.RegisterClass(class, color); use a unique string like "Scriptname Room".
+ When a zone class is registered, admins can use the tool to create new ones.
+ When a new zone is created, the "OnZoneCreated" hook is called serverside. See the hook below for documentation.
+ When a player edits a zone's properties, the "ShowZoneOptions" hook is called clientside. See the hook below.
+
+ Use zones.FindByClass() to find all zones which are of a given class.
+ Use ply:GetCurrentZone() to find the zone that a player is standing in.
+
+ Installation:
+ This is a shared file so include it in any shared environment. Also include ent_zone_point and weapon_zone_designator as a shared ent and weapon.
+ You should not put this file directly in lua/autorun.
+
+ License:
+ YOU MAY use/edit this however you want, as long as you give proper attribution.
+ YOU MAY distribute this with other scripts whether they are paid or free.
+ YOU MAY NOT distribute this on its own. It must accompany another script.
+
+ Enjoy! ~Bobbleheadbob
+]]
+
+local table, math, Vector, pairs, ipairs, ents = table, math, Vector, pairs, ipairs, ents
+
+if zones then
+ if zones.version > version then
+ print("A new version of zones exists. Using version "..zones.version.." instead of "..version)
+ return
+ elseif zones.version < version then
+ print("A new version of zones exists. Using version "..version.." instead of "..zones.version)
+ end
+else
+ print("Loaded zones " ..version)
+end
+
+zones = zones or {}
+zones.version = version
+
+zones.Classes = zones.Classes or {}
+zones.List = zones.List or {}
+
+//Common interface functions:
+
+-- Registers a zone class which can then be created using weapon_zone_designator
+function zones.RegisterClass(class,color)
+ zones.Classes[class] = color
+end
+
+
+local plymeta = FindMetaTable("Player")
+--returns one of the zones a player is found in. Also returns that zone's ID. Class is optional to filter the search.
+function plymeta:GetCurrentZone(class)
+ return zones.GetZoneAt(self:GetPos(),class)
+end
+
+--returns a table of zones the player is in. Class is optional to filter the search.
+function plymeta:GetCurrentZones(class)
+ return zones.GetZonesAt(self:GetPos(),class)
+end
+
+function zones.GetZoneAt(pos,class) --works like above, except uses any point.
+ for k,zone in pairs(zones.List) do
+ if class and class != zone.class then continue end
+
+ for k1, points in pairs(zone.points) do
+ if not pos:WithinAABox(zone.bounds[k1].mins,zone.bounds[k1].maxs) then continue end
+ if zones.PointInPoly(pos,points) then
+ local z = points[1].z
+ if pos.z >= z and pos.z < z + zone.height[k1] then
+ return zone,k
+ end
+ end
+ end
+ end
+ return nil, -1
+end
+function zones.GetZonesAt(pos,class) --works like above, except uses any point.
+ local tbl = {}
+ for k,zone in pairs(zones.List) do
+ if class and class != zone.class then continue end
+ for k1, points in pairs(zone.points) do
+ if not pos:WithinAABox(zone.bounds[k1].mins,zone.bounds[k1].maxs) then continue end
+ if zones.PointInPoly(pos,points) then
+ local z = points[1].z
+ if pos.z >= z and pos.z < z + zone.height[k1] then
+ tbl[k] = zone
+ end
+ end
+ end
+ end
+ return tbl
+end
+
+--Gets a list of all zones which are of the specified class.
+function zones.FindByClass(class)
+ local tbl = {}
+
+ for k,v in pairs(zones.List) do
+ if v.class == class then
+ tbl[k] = v
+ end
+ end
+
+ return tbl
+end
+
+--Returns the numerical ID of a zone.
+function zones.GetID(zone)
+ return table.KeyFromValue(zones.List,zone)
+end
+
+
+//Getting into the meat of the API:
+if SERVER then
+ util.AddNetworkString("zones_sync")
+ util.AddNetworkString("zones_class")
+
+ function zones.SaveZones()
+ if not file.Exists("zones","DATA") then
+ file.CreateDir("zones")
+ end
+ file.Write("zones/"..game.GetMap():gsub("_","-"):lower()..".txt", util.TableToJSON(zones.List))
+ end
+ concommand.Add("zone_save",function(ply,c,a)
+ if not ply:IsAdmin() then return end
+ zones.SaveZones()
+ end)
+
+ function zones.LoadZones()
+ local tbl = file.Read("zones/"..game.GetMap():gsub("_","-"):lower()..".txt", "DATA")
+ zones.List = tbl and util.JSONToTable(tbl) or {}
+
+ //Update legacy files:
+ for k,v in pairs(zones.List)do
+ if not v.bounds then
+ zones.CalcBounds(v)
+ end
+ end
+ end
+
+ local sync = false
+ local syncply
+ function zones.Sync(ply)
+ sync = true
+ syncply = ply
+ end
+ hook.Add("Tick","zones_sync",function()
+ if sync then
+ net.Start("zones_sync")
+ net.WriteTable(zones.List)
+ if syncply then
+ net.Send(syncply)
+ syncply = nil
+ else
+ net.Broadcast()
+ end
+ sync = false
+ end
+ end)
+
+ function zones.CreateZoneFromPoint(ent)
+
+ local zone = {
+ points = {{}}, --only 1 area when creating a new zone.
+ height = {ent:GetTall()},
+ class = ent:GetZoneClass(),
+ bounds = {}
+ }
+
+ local id = table.maxn(zones.List) + 1
+ local cur = ent
+ repeat
+ local pos = cur:GetPos() - Vector(0,0,2)
+ zone.points[1][#zone.points[1]+1] = pos
+
+ cur:SetZoneID(id)
+ cur = cur:GetNext()
+
+ until (cur == ent)
+
+ zones.CalcBounds(zone)
+
+ zones.List[id] = zone
+ zones.Sync()
+
+ hook.Run("OnZoneCreated",zone,zone.class,id)
+
+ return zone, id
+
+ end
+
+ function zones.CalcBounds(zone)
+ zone.bounds = {}
+ for areanum,area in pairs(zone.points)do
+ local mins,maxs = Vector(10000000,10000000,area[1].z), Vector(-10000000,-10000000,area[1].z + zone.height[areanum])
+ for k,pos in pairs(area) do
+ maxs.x = math.max(pos.x, maxs.x)
+ maxs.y = math.max(pos.y, maxs.y)
+ mins.x = math.min(pos.x, mins.x)
+ mins.y = math.min(pos.y, mins.y)
+ end
+ zone.bounds[areanum] = {mins=mins,maxs=maxs}
+ end
+ end
+
+ function zones.Remove(id)
+ zones.List[id] = nil
+ zones.Sync()
+ end
+
+ function zones.CreatePointEnts(removeThese) --removeThese is optional.
+ for k,v in pairs(removeThese or ents.FindByClass("ent_zone_point")) do --remove old
+ v:Remove()
+ end
+
+ --create new
+ for id,zone in pairs(zones.List)do
+
+ for k, area in pairs(zone.points) do
+
+ local first
+ local curr
+ for k2,point in ipairs(area)do
+
+ local next = ents.Create("ent_zone_point")
+
+ if IsValid(curr) then
+ next:SetPos(point+Vector(0,0,1))
+ curr:SetNext(next)
+ -- curr:DeleteOnRemove(next)
+ else
+ first = next
+ next:SetPos(point+Vector(0,0,1))
+ end
+
+ next.LastPoint = curr
+ curr = next
+ next:SetTall(zone.height[k])
+ next:SetZoneClass(zone.class)
+ next:Spawn()
+ next:SetZoneID(id)
+ next:SetAreaNumber(k)
+
+ end
+
+ curr:SetNext(first)
+ -- curr:DeleteOnRemove(first)
+ first.LastPoint = curr
+
+ end
+ end
+
+ end
+
+ function zones.Merge(from,to)
+
+ local zfrom, zto = zones.List[from], zones.List[to]
+
+ table.Add(zto.points, zfrom.points)
+ table.Add(zto.height, zfrom.height)
+ table.Add(zto.bounds, zfrom.bounds)
+
+ zones.Remove(from)
+
+ zones.Sync()
+
+ end
+
+ function zones.Split(id,areanum)
+ local zone = zones.List[id]
+ local pts, h, bound = zone.points[areanum], zone.height[areanum], zone.bounds[areanum]
+
+ table.remove(zone.points,areanum)
+ table.remove(zone.height,areanum)
+ table.remove(zone.bounds,areanum)
+
+ if #zone.points == 0 then
+ zones.Remove(id)
+ end
+
+ local new = table.Copy(zone)
+ new.points = {pts}
+ new.height = {h}
+ new.bounds = {bound}
+
+ local id = table.maxn(zones.List)+1
+ zones.List[id] = new
+
+ zones.Sync()
+
+ return new,id
+
+ end
+
+ hook.Add("Initialize","claim_load",function()
+ zones.LoadZones()
+ end)
+ hook.Add("PlayerInitialSpawn","claim_sync",function(ply)
+ zones.Sync(ply)
+ end)
+
+ net.Receive("zones_class",function(len,ply)
+ if not ply:IsAdmin() then return end
+ local id = net.ReadFloat()
+ local class = net.ReadString()
+
+ for k,v in pairs(ents.FindByClass("ent_zone_point"))do
+ if v:GetZoneID() == id then
+ v:SetZoneClass(class)
+ end
+ end
+ zones.List[id].class = class
+
+ zones.Sync()
+ end)
+
+else
+ net.Receive("zones_sync",function(len)
+ zones.List = net.ReadTable()
+ end)
+ function zones.ShowOptions(id)
+
+ local zone = zones.List[id]
+ local class = zone.class
+
+ local frame = vgui.Create("DFrame")
+ frame:MakePopup()
+ frame:SetTitle("Zone Settings")
+
+ local ztitle = vgui.Create("DLabel",frame)
+ ztitle:Dock(TOP)
+ ztitle:DockMargin(2,0,5,5)
+ ztitle:SetText("Zone Class:")
+ ztitle:SizeToContents()
+
+ local zclass = vgui.Create("DComboBox",frame)
+ zclass:Dock(TOP)
+ zclass:DockMargin(0,0,0,5)
+ for k,v in pairs(zones.Classes) do
+ zclass:AddChoice(k,nil,k == class)
+ end
+ function zclass:OnSelect(i,class)
+ net.Start("zones_class")
+ net.WriteFloat(id)
+ net.WriteString(class)
+ net.SendToServer()
+
+ frame.content:Remove()
+
+ frame.content = vgui.Create("DPanel",frame)
+ frame.content:Dock(FILL)
+
+ local w,h = hook.Run("ShowZoneOptions",zone,class,frame.content,id,frame)
+ frame:SizeTo((w or 100)+8,(h or 2)+78, .2)
+ frame:MoveTo(ScrW()/2-((w or 292)+8)/2,ScrH()/2-((h or 422)+78)/2, .2)
+ end
+
+ frame.content = vgui.Create("DPanel",frame)
+ frame.content:Dock(FILL)
+
+ local w,h = hook.Run("ShowZoneOptions",zone,class,frame.content,id,frame)
+ frame:SetSize((w or 100)+8,(h or 2)+78)
+ frame:Center()
+
+ end
+
+end
+
+
+
+//returns the point of intersection between two infinite lines.
+local function IntersectPoint(line1, line2)
+
+ local x1,y1,x2,y2,x3,y3,x4,y4 = line1.x1,line1.y1,line1.x2,line1.y2,line2.x1,line2.y1,line2.x2,line2.y2
+
+ local m1,m2 = (y1-y2)/((x1-x2)+.001),(y3-y4)/((x3-x4)+.001) --get the slopes
+ local yint1,yint2 = (-m1*x1)+y1,(-m2*x3)+y3 --get the y-intercepts
+ local x = (yint1-yint2)/(m2-m1) --calculate x pos
+ local y = m1*x+yint1 --plug in x pos to get y pos
+
+ return x,y
+
+end
+//Returns a bool if two SEGEMENTS intersect or not.
+local function Intersect(line1, line2)
+
+ local x,y = IntersectPoint(line1, line2)
+ local sx,sy = tostring(x), tostring(y)
+ if (sx == "-inf" or sx == "inf" or sx == "nan") then
+ return false
+ end
+
+ local minx1, maxx1 = math.min(line1.x1,line1.x2)-.1, math.max(line1.x1,line1.x2)+.1
+ local minx2, maxx2 = math.min(line2.x1,line2.x2)-.1, math.max(line2.x1,line2.x2)+.1
+ local miny1, maxy1 = math.min(line1.y1,line1.y2)-.1, math.max(line1.y1,line1.y2)+.1
+ local miny2, maxy2 = math.min(line2.y1,line2.y2)-.1, math.max(line2.y1,line2.y2)+.1
+
+ if (x >= minx1) and (x <= maxx1) and (x >= minx2) and (x <= maxx2) then
+
+ if (y >= miny1) and (y <= maxy1) and (y >= miny2) and (y <= maxy2) then
+
+ --debugoverlay.Sphere( Vector(x,y,LocalPlayer():GetPos().z), 3, FrameTime()+.01, Color(255,0,0), true)
+
+ return true
+
+ end
+
+ end
+
+ return false
+
+end
+function zones.PointInPoly(point,poly) //True if point is within a polygon.
+
+ //Check validity
+ local lines = {}
+ local pcount = #poly
+ for k1=1, pcount do
+
+ local k2 = k1+1
+ if k2 > pcount then
+ k2 = 1
+ end
+
+ lines[k1] = {
+ x1 = poly[k1].x,
+ y1 = poly[k1].y,
+ x2 = poly[k2].x,
+ y2 = poly[k2].y,
+ valid = true
+ }
+
+ end
+
+ local ray = {
+ x1 = point.x,
+ y1 = point.y,
+ x2 = point.x + 10000,
+ y2 = point.y + 10000
+ }
+ local inside = false
+
+ //Do ray check.
+ for k,v in pairs(lines)do
+
+ if Intersect(ray,v) then
+ inside = !inside
+ end
+
+ end
+
+ return inside
+end