diff options
Diffstat (limited to 'gamemode/zones.lua')
| -rw-r--r-- | gamemode/zones.lua | 612 |
1 files changed, 0 insertions, 612 deletions
diff --git a/gamemode/zones.lua b/gamemode/zones.lua deleted file mode 100644 index 5d6d781..0000000 --- a/gamemode/zones.lua +++ /dev/null @@ -1,612 +0,0 @@ -do return end -local version = 1.20 -- Older versions will not run if a newer version is used in another script. ---[[ - ZONES - by Bobbleheadbob with help from Zeh Matt - 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 example file for documentation. - When a zone is loaded into the game, the "OnZoneLoaded" hook is called serverside. See the example file for documentation. - When a player edits a zone's properties, the "ShowZoneOptions" hook is called clientside. See the example file for documentation. - - 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, bit = table, math, Vector, pairs, ipairs, ents, bit - -if zones then - local diff = math.abs(math.floor(version)-math.floor(zones.version)) > 0 - if diff then - ErrorNoHalt("WARNING! Two scripts use VERY different versions of the zones API. Please tell one of them to update their script!\n") - end - if zones.version > version then - if diff then - print("The outdated version of zones is located at: "..debug.getinfo(1,"S").short_src) - end - print("A new version of zones exists. Using version "..zones.version.." instead of "..version) - return - elseif zones.version < version then - if diff then - print("The outdated version of zones is located at: "..debug.getinfo(zones.RegisterClass,"S").short_src) - end - 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 {} -zones.Map = zones.Map 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) - local c = zones.Cache[self][class or "___"] - if c then return unpack(c) end - local z,id = zones.GetZoneAt(self:GetPos(), class) - zones.Cache[self][class or "___"] = {z,id} - return z,id -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. - - local nearby = zones.GetNearbyZones(pos) - - for k,zone in pairs(nearby) do - - if class and class != zone.class then continue end - if not pos:WithinAABox(zone.bounds.mins, zone.bounds.maxs) then - continue - end - - for k1, points in pairs(zone.points) do - 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 = {} - local nearby = zones.GetNearbyZones(pos) - for k,zone in pairs(nearby) do - if class and class != zone.class then continue end - if not pos:WithinAABox(zone.bounds.mins,zone.bounds.maxs) then continue end - for k1, points in pairs(zone.points) do - 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: -local mapMins = -16000 -local mapMaxs = 16000 -local mapSize = 32000 -local chunkSize = 128 - -local function GetZoneIndex(pos) - - local x = pos.x + mapMaxs - local y = pos.y + mapMaxs - local z = pos.z + mapMaxs - - local idxX = math.floor(x / chunkSize) - local idxY = math.floor(y / chunkSize) - local idxZ = math.floor(z / chunkSize) - local idx = bit.bor(bit.lshift(idxX, 24), bit.lshift(idxY, 14), idxZ) - - return idx - -end - -local function Floor(x,to) - return math.floor(x / to) * to -end -local function Ceil(x,to) - return math.ceil(x / to) * to -end - -function zones.CreateZoneMapping() - zones.Map = {} - for _, zone in pairs(zones.List) do - local mins = zone.bounds.mins - local maxs = zone.bounds.maxs - for x = Floor(mins.x,chunkSize), Ceil(maxs.x + 1,chunkSize), chunkSize do - for y = Floor(mins.y,chunkSize), Ceil(maxs.y + 1,chunkSize), chunkSize do - for z = Floor(mins.z,chunkSize), Ceil(maxs.z + 1,chunkSize), chunkSize do - local idx = GetZoneIndex(Vector(x, y, z)) - zones.Map[idx] = zones.Map[idx] or {} - table.insert(zones.Map[idx], zone) - end - end - end - end -end - -function zones.GetNearbyZones(pos) - //This system isn't working. - -- local idx = GetZoneIndex(pos) - -- return zones.Map[idx] or {} - return zones.List -end - -zones.Cache = {} -local function ClearCache() - for k,v in pairs(player.GetAll()) do - zones.Cache[v] = {} - end -end -ClearCache() -hook.Add("Tick","zones_cache",ClearCache) - -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 - - hook.Run("OnZoneLoaded",v,v.class,k) - 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() - zones.CreateZoneMapping() - 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,true) - - zones.List[id] = zone - hook.Run("OnZoneCreated",zone,zone.class,id) - - zones.Sync() - - - return zone, id - - end - - function zones.CalcBounds(zone,newZone) - local mins,maxs = Vector(10000000,10000000,10000000), Vector(-10000000,-10000000,-10000000) - for areanum,area in pairs(zone.points)do - for k,pos in pairs(area) do - maxs.x = math.max(pos.x, maxs.x) - maxs.y = math.max(pos.y, maxs.y) - maxs.z = math.max(pos.z+zone.height[areanum], maxs.z) - mins.x = math.min(pos.x, mins.x) - mins.y = math.min(pos.y, mins.y) - mins.z = math.min(pos.z, mins.z) - end - end - zone.bounds = {["mins"]=mins,["maxs"]=maxs} - if not newZone then - hook.Run("OnZoneChanged",zone,zone.class,zones.GetID(zone)) - end - end - - function zones.Remove(id) - hook.Run("OnZoneRemoved",zones.List[id],zones.List[id].class,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) - - zones.CalcBounds(zto) - zones.Remove(from) - - hook.Run("OnZoneMerged",zto,zto.class,to,zfrom,zfrom.class,from) - - zones.Sync() - - end - - function zones.Split(id,areanum) - local zone = zones.List[id] - local pts, h, bound = zone.points[areanum], zone.height[areanum] - - table.remove(zone.points,areanum) - table.remove(zone.height,areanum) - - if #zone.points == 0 then - zones.Remove(id) - end - - local new = table.Copy(zone) - new.points = {pts} - new.height = {h} - - local id = table.maxn(zones.List)+1 - zones.List[id] = new - - zones.CalcBounds(zone) - zones.CalcBounds(new) - - hook.Run("OnZoneSplit",new,new.class,id,zone,id) - - zones.Sync() - - return new,id - - end - - function zones.ChangeClass(id,class) - local zone,new = zones.List[id],{} - new.points = zone.points - new.height = zone.height - new.bounds = zone.bounds - new.class = class - - zones.List[id] = new - - hook.Run("OnZoneCreated",new,class,id) - - zones.Sync() - end - - - local mapMins = -16000 - local mapMaxs = 16000 - local mapSize = 32000 - local chunkSize = 128 - - function zones.GetZoneIndex(pos) - - local x = math.Remap(pos.x, mapMins, mapMaxs, 0, mapSize) - local y = math.Remap(pos.y, mapMins, mapMaxs, 0, mapSize) - local z = math.Remap(pos.z, mapMins, mapMaxs, 0, mapSize) - - local idxX = math.floor(x / chunkSize) - local idxY = math.floor(y / chunkSize) - local idxZ = math.floor(z / chunkSize) - local idx = bit.bor(bit.lshift(idxX, 24), bit.lshift(idxY, 14), idxZ) - - return idx - - end - - hook.Add("InitPostEntity","zones_load",function() - zones.LoadZones() - end) - hook.Add("PlayerInitialSpawn","zones_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.ChangeClass(id,class) - - end) - -else - net.Receive("zones_sync",function(len) - zones.List = net.ReadTable() - zones.CreateZoneMapping() - end) - - function zones.ShowOptions(id) - - local zone = zones.List[id] - local class = zone.class - - local frame = vgui.Create("DFrame") - zones.optionsFrame = frame - 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) - frame.content:DockPadding(5,5,5,5) - - 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) - frame.content:DockPadding(5,5,5,5) - - 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. - - local ray = { - x1 = point.x, - y1 = point.y, - x2 = 100000, - y2 = 100000 - } - - local inside = false - - local line = { - x1 = 0, - y1 = 0, - x2 = 0, - y2 = 0 - } - - //Perform ray test - for k1, v in pairs(poly) do - - local v2 = poly[k1+1] - if not v2 then - v2 = poly[1] - end - - line["x1"] = v.x - line["y1"] = v.y - line["x2"] = v2.x - line["y2"] = v2.y - - if Intersect(ray,line) then - inside = !inside - end - - end - - return inside -end |
