add sborka
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
function AdvDupe2.ReceiveFile(data, autoSave)
|
||||
AdvDupe2.RemoveProgressBar()
|
||||
if not data then
|
||||
AdvDupe2.Notify("File was not saved! (No data)",NOTIFY_ERROR,5)
|
||||
return
|
||||
end
|
||||
local path
|
||||
if autoSave then
|
||||
if(LocalPlayer():GetInfo("advdupe2_auto_save_overwrite")~="0")then
|
||||
path = AdvDupe2.GetFilename(AdvDupe2.AutoSavePath, true)
|
||||
else
|
||||
path = AdvDupe2.GetFilename(AdvDupe2.AutoSavePath)
|
||||
end
|
||||
else
|
||||
path = AdvDupe2.GetFilename(AdvDupe2.SavePath)
|
||||
end
|
||||
|
||||
path = AdvDupe2.SanitizeFilename(path)
|
||||
local dupefile = file.Open(path, "wb", "DATA")
|
||||
if not dupefile then
|
||||
AdvDupe2.Notify("File was not saved! (Could not open file for writing)",NOTIFY_ERROR,5)
|
||||
return
|
||||
end
|
||||
dupefile:Write(data)
|
||||
dupefile:Close()
|
||||
|
||||
local errored = false
|
||||
if(LocalPlayer():GetInfo("advdupe2_debug_openfile")=="1")then
|
||||
if(not file.Exists(path, "DATA"))then AdvDupe2.Notify("File does not exist", NOTIFY_ERROR) return end
|
||||
|
||||
local readFile = file.Open(path, "rb", "DATA")
|
||||
if not readFile then AdvDupe2.Notify("File could not be read", NOTIFY_ERROR) return end
|
||||
local readData = readFile:Read(readFile:Size())
|
||||
readFile:Close()
|
||||
local success, dupe, _info, _moreinfo = AdvDupe2.Decode(readData)
|
||||
if(success)then
|
||||
AdvDupe2.Notify("DEBUG CHECK: File successfully opens. No EOF errors.")
|
||||
else
|
||||
AdvDupe2.Notify("DEBUG CHECK: " .. dupe, NOTIFY_ERROR)
|
||||
errored = true
|
||||
end
|
||||
end
|
||||
|
||||
local filename = string.StripExtension(string.GetFileFromFilename( path ))
|
||||
if autoSave then
|
||||
if(IsValid(AdvDupe2.FileBrowser.AutoSaveNode))then
|
||||
local add = true
|
||||
for i=1, #AdvDupe2.FileBrowser.AutoSaveNode.Files do
|
||||
if(filename==AdvDupe2.FileBrowser.AutoSaveNode.Files[i].Label:GetText())then
|
||||
add=false
|
||||
break
|
||||
end
|
||||
end
|
||||
if(add)then
|
||||
AdvDupe2.FileBrowser.AutoSaveNode:AddFile(filename)
|
||||
AdvDupe2.FileBrowser.Browser.pnlCanvas:Sort(AdvDupe2.FileBrowser.AutoSaveNode)
|
||||
end
|
||||
end
|
||||
else
|
||||
AdvDupe2.FileBrowser.Browser.pnlCanvas.ActionNode:AddFile(filename)
|
||||
AdvDupe2.FileBrowser.Browser.pnlCanvas:Sort(AdvDupe2.FileBrowser.Browser.pnlCanvas.ActionNode)
|
||||
end
|
||||
if not errored then
|
||||
AdvDupe2.Notify("File successfully saved!",NOTIFY_GENERIC, 5)
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("AdvDupe2_ReceiveFile", function()
|
||||
local autoSave = net.ReadBool()
|
||||
net.ReadStream(nil, function(data)
|
||||
AdvDupe2.ReceiveFile(data, autoSave)
|
||||
end)
|
||||
end)
|
||||
|
||||
AdvDupe2.Uploading = false
|
||||
function AdvDupe2.SendFile(name, data)
|
||||
net.Start("AdvDupe2_ReceiveFile")
|
||||
net.WriteString(name)
|
||||
AdvDupe2.Uploading = net.WriteStream(data, function()
|
||||
AdvDupe2.Uploading = nil
|
||||
AdvDupe2.File = nil
|
||||
AdvDupe2.RemoveProgressBar()
|
||||
end)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function AdvDupe2.UploadFile(ReadPath, ReadArea)
|
||||
if AdvDupe2.Uploading then AdvDupe2.Notify("Already opening file, please wait.", NOTIFY_ERROR) return end
|
||||
if(ReadArea==0)then
|
||||
ReadPath = AdvDupe2.DataFolder.."/"..ReadPath..".txt"
|
||||
elseif(ReadArea==1)then
|
||||
ReadPath = AdvDupe2.DataFolder.."/-Public-/"..ReadPath..".txt"
|
||||
else
|
||||
ReadPath = "adv_duplicator/"..ReadPath..".txt"
|
||||
end
|
||||
|
||||
if(not file.Exists(ReadPath, "DATA"))then AdvDupe2.Notify("File does not exist", NOTIFY_ERROR) return end
|
||||
|
||||
local read = file.Read(ReadPath)
|
||||
if not read then AdvDupe2.Notify("File could not be read", NOTIFY_ERROR) return end
|
||||
local name = string.Explode("/", ReadPath)
|
||||
name = name[#name]
|
||||
name = string.sub(name, 1, #name-4)
|
||||
|
||||
local success, dupe, info, moreinfo = AdvDupe2.Decode(read)
|
||||
if(success)then
|
||||
AdvDupe2.SendFile(name, read)
|
||||
|
||||
AdvDupe2.LoadGhosts(dupe, info, moreinfo, name)
|
||||
else
|
||||
AdvDupe2.Notify("File could not be decoded. ("..dupe..") Upload Canceled.", NOTIFY_ERROR)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,353 @@
|
||||
function AdvDupe2.LoadGhosts(dupe, info, moreinfo, name, preview)
|
||||
AdvDupe2.RemoveGhosts()
|
||||
AdvDupe2.Ghosting = true
|
||||
AdvDupe2.GhostToSpawn = {}
|
||||
local count = 0
|
||||
local time, desc, date, creator
|
||||
|
||||
if(info.ad1) then
|
||||
local z = dupe.HeadEnt.Z
|
||||
local Pos, Ang
|
||||
|
||||
time = moreinfo.Time or ""
|
||||
desc = info.Description or ""
|
||||
date = info.Date or ""
|
||||
creator = info.Creator or ""
|
||||
|
||||
AdvDupe2.HeadEnt = dupe.HeadEnt.Index
|
||||
AdvDupe2.HeadPos = dupe.HeadEnt.Pos
|
||||
AdvDupe2.HeadZPos = z
|
||||
AdvDupe2.HeadPos.Z = AdvDupe2.HeadPos.Z + z
|
||||
|
||||
for k, v in pairs(dupe.Entities) do
|
||||
if(v.SavedParentIdx) then
|
||||
if(not v.BuildDupeInfo) then v.BuildDupeInfo = {} end
|
||||
v.BuildDupeInfo.DupeParentID = v.SavedParentIdx
|
||||
Pos = v.LocalPos
|
||||
Ang = v.LocalAngle
|
||||
else
|
||||
Pos, Ang = nil, nil
|
||||
end
|
||||
|
||||
for i, p in pairs(v.PhysicsObjects) do
|
||||
p.Pos = Pos or p.LocalPos
|
||||
p.Pos.Z = p.Pos.Z - z
|
||||
p.Angle = Ang or p.LocalAngle
|
||||
p.LocalPos = nil
|
||||
p.LocalAngle = nil
|
||||
end
|
||||
|
||||
v.LocalPos = nil
|
||||
v.LocalAngle = nil
|
||||
AdvDupe2.GhostToSpawn[count] =
|
||||
{
|
||||
Model = v.Model,
|
||||
PhysicsObjects = v.PhysicsObjects
|
||||
}
|
||||
|
||||
if(AdvDupe2.HeadEnt == k) then
|
||||
AdvDupe2.HeadEnt = count
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
AdvDupe2.HeadOffset = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Pos
|
||||
AdvDupe2.HeadAngle = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Angle
|
||||
else
|
||||
time = info.time or ""
|
||||
desc = dupe.Description or ""
|
||||
date = info.date or ""
|
||||
creator = info.name or ""
|
||||
|
||||
AdvDupe2.HeadEnt = dupe.HeadEnt.Index
|
||||
AdvDupe2.HeadZPos = dupe.HeadEnt.Z
|
||||
AdvDupe2.HeadPos = dupe.HeadEnt.Pos
|
||||
AdvDupe2.HeadOffset = dupe.Entities[AdvDupe2.HeadEnt].PhysicsObjects[0].Pos
|
||||
AdvDupe2.HeadAngle = dupe.Entities[AdvDupe2.HeadEnt].PhysicsObjects[0].Angle
|
||||
|
||||
for k, v in pairs(dupe.Entities) do
|
||||
AdvDupe2.GhostToSpawn[count] =
|
||||
{
|
||||
Model = v.Model,
|
||||
PhysicsObjects = v.PhysicsObjects
|
||||
}
|
||||
|
||||
if(AdvDupe2.HeadEnt == k) then
|
||||
AdvDupe2.HeadEnt = count
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
if(not preview) then
|
||||
AdvDupe2.Info.File:SetText("File: "..name)
|
||||
AdvDupe2.Info.Creator:SetText("Creator: "..creator)
|
||||
AdvDupe2.Info.Date:SetText("Date: "..date)
|
||||
AdvDupe2.Info.Time:SetText("Time: "..time)
|
||||
AdvDupe2.Info.Size:SetText("Size: "..string.NiceSize(tonumber(info.size) or 0))
|
||||
AdvDupe2.Info.Desc:SetText("Desc: "..(desc or ""))
|
||||
AdvDupe2.Info.Entities:SetText("Entities: "..table.Count(dupe.Entities))
|
||||
AdvDupe2.Info.Constraints:SetText("Constraints: "..table.Count(dupe.Constraints))
|
||||
end
|
||||
|
||||
AdvDupe2.StartGhosting()
|
||||
AdvDupe2.Preview = preview
|
||||
end
|
||||
|
||||
function AdvDupe2.RemoveGhosts()
|
||||
if(AdvDupe2.Ghosting) then
|
||||
hook.Remove("Tick", "AdvDupe2_SpawnGhosts")
|
||||
AdvDupe2.Ghosting = false
|
||||
|
||||
if(not AdvDupe2.BusyBar) then
|
||||
AdvDupe2.RemoveProgressBar()
|
||||
end
|
||||
end
|
||||
|
||||
if(AdvDupe2.GhostEntities) then
|
||||
for k, v in pairs(AdvDupe2.GhostEntities) do
|
||||
if(IsValid(v))then
|
||||
v:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if(IsValid(AdvDupe2.HeadGhost))then
|
||||
AdvDupe2.HeadGhost:Remove()
|
||||
end
|
||||
|
||||
AdvDupe2.CurrentGhost = 1
|
||||
AdvDupe2.HeadGhost = nil
|
||||
AdvDupe2.GhostEntities = nil
|
||||
AdvDupe2.Preview = false
|
||||
end
|
||||
|
||||
--Creates a ghost from the given entity's table
|
||||
local function MakeGhostsFromTable(EntTable)
|
||||
|
||||
if(not EntTable) then return end
|
||||
if(not EntTable.Model or EntTable.Model:sub(-4,-1) ~= ".mdl") then
|
||||
EntTable.Model = "models/error.mdl"
|
||||
end
|
||||
|
||||
local GhostEntity = ClientsideModel(EntTable.Model, RENDERGROUP_TRANSLUCENT)
|
||||
|
||||
-- If there are too many entities we might not spawn..
|
||||
if not IsValid(GhostEntity) then
|
||||
AdvDupe2.RemoveGhosts()
|
||||
AdvDupe2.Notify("Too many entities to spawn ghosts!", NOTIFY_ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
GhostEntity:SetRenderMode( RENDERMODE_TRANSALPHA ) --Was broken, making ghosts invisible
|
||||
GhostEntity:SetColor( Color(255, 255, 255, 150) )
|
||||
GhostEntity.Phys = EntTable.PhysicsObjects[0]
|
||||
|
||||
if util.IsValidRagdoll(EntTable.Model) then
|
||||
local ref, parents, angs = {}, {}, {}
|
||||
|
||||
GhostEntity:SetupBones()
|
||||
for k, v in pairs(EntTable.PhysicsObjects) do
|
||||
local bone = GhostEntity:TranslatePhysBoneToBone(k)
|
||||
local bonp = GhostEntity:GetBoneParent(bone)
|
||||
if bonp == -1 then
|
||||
ref[bone] = GhostEntity:GetBoneMatrix(bone):GetInverseTR()
|
||||
else
|
||||
bonp = GhostEntity:TranslatePhysBoneToBone(GhostEntity:TranslateBoneToPhysBone(bonp))
|
||||
parents[bone] = bonp
|
||||
ref[bone] = GhostEntity:GetBoneMatrix(bone):GetInverseTR() * GhostEntity:GetBoneMatrix(bonp)
|
||||
end
|
||||
|
||||
local m = Matrix() m:SetAngles(v.Angle)
|
||||
angs[bone] = m
|
||||
end
|
||||
|
||||
for bone, ang in pairs( angs ) do
|
||||
if parents[bone] and angs[parents[bone]] then
|
||||
local localrotation = angs[parents[bone]]:GetInverseTR() * ang
|
||||
local m = ref[bone] * localrotation
|
||||
GhostEntity:ManipulateBoneAngles(bone, m:GetAngles())
|
||||
else
|
||||
local pos = GhostEntity:GetBonePosition(bone)
|
||||
GhostEntity:ManipulateBonePosition(bone, -pos)
|
||||
GhostEntity:ManipulateBoneAngles(bone, ref[bone]:GetAngles())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return GhostEntity
|
||||
end
|
||||
|
||||
local function StopGhosting()
|
||||
AdvDupe2.Ghosting = false
|
||||
hook.Remove( "Tick", "AdvDupe2_SpawnGhosts" )
|
||||
|
||||
if not BusyBar then AdvDupe2.RemoveProgressBar() end
|
||||
end
|
||||
|
||||
local function SpawnGhosts()
|
||||
local ghostsPerTick = GetConVar( "advdupe2_ghost_rate" ):GetInt()
|
||||
local ghostPercentLimit = GetConVar( "advdupe2_limit_ghost" ):GetFloat()
|
||||
|
||||
local finalGhost = math.min( AdvDupe2.TotalGhosts, math.max( math.Round( (ghostPercentLimit / 100) * AdvDupe2.TotalGhosts ), 0 ) )
|
||||
local finalGhostInFrame = math.min( AdvDupe2.CurrentGhost + ghostsPerTick - 1, finalGhost )
|
||||
|
||||
for i = AdvDupe2.CurrentGhost, finalGhostInFrame do
|
||||
local g = AdvDupe2.GhostToSpawn[i]
|
||||
if g and i ~= AdvDupe2.HeadEnt then AdvDupe2.GhostEntities[i] = MakeGhostsFromTable( g ) end
|
||||
end
|
||||
AdvDupe2.CurrentGhost = finalGhostInFrame + 1
|
||||
|
||||
AdvDupe2.UpdateGhosts( true )
|
||||
if not AdvDupe2.BusyBar then
|
||||
AdvDupe2.ProgressBar.Percent = (AdvDupe2.CurrentGhost / AdvDupe2.TotalGhosts) * 100
|
||||
end
|
||||
|
||||
if AdvDupe2.CurrentGhost > finalGhost then
|
||||
StopGhosting()
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("AdvDupe2_SendGhosts", function(len, ply, len2)
|
||||
AdvDupe2.RemoveGhosts()
|
||||
AdvDupe2.GhostToSpawn = {}
|
||||
AdvDupe2.HeadEnt = net.ReadInt(16)
|
||||
AdvDupe2.HeadZPos = net.ReadFloat()
|
||||
AdvDupe2.HeadPos = net.ReadVector()
|
||||
|
||||
local cache = {}
|
||||
for i = 1, net.ReadInt(16) do
|
||||
cache[i] = net.ReadString()
|
||||
end
|
||||
|
||||
for i = 1, net.ReadInt(16) do
|
||||
AdvDupe2.GhostToSpawn[i] =
|
||||
{
|
||||
Model = cache[net.ReadInt(16)],
|
||||
PhysicsObjects = {}
|
||||
}
|
||||
|
||||
for k = 0, net.ReadInt(8) do
|
||||
AdvDupe2.GhostToSpawn[i].PhysicsObjects[k] =
|
||||
{
|
||||
Angle = net.ReadAngle(),
|
||||
Pos = net.ReadVector()
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
AdvDupe2.CurrentGhost = 1
|
||||
AdvDupe2.GhostEntities = {}
|
||||
AdvDupe2.HeadGhost = MakeGhostsFromTable(AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt])
|
||||
AdvDupe2.HeadOffset = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Pos
|
||||
AdvDupe2.HeadAngle = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Angle
|
||||
AdvDupe2.GhostEntities[AdvDupe2.HeadEnt] = AdvDupe2.HeadGhost
|
||||
AdvDupe2.TotalGhosts = #AdvDupe2.GhostToSpawn
|
||||
|
||||
if(AdvDupe2.TotalGhosts > 1) then
|
||||
AdvDupe2.Ghosting = true
|
||||
|
||||
if(not AdvDupe2.BusyBar) then
|
||||
AdvDupe2.InitProgressBar("Ghosting: ")
|
||||
AdvDupe2.BusyBar = false
|
||||
end
|
||||
|
||||
hook.Add("Tick", "AdvDupe2_SpawnGhosts", SpawnGhosts)
|
||||
else
|
||||
AdvDupe2.Ghosting = false
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("AdvDupe2_AddGhost", function(len, ply, len2)
|
||||
local ghost = {Model = net.ReadString(), PhysicsObjects = {}}
|
||||
for k = 0, net.ReadInt(8) do
|
||||
ghost.PhysicsObjects[k] = {Angle = net.ReadAngle(), Pos = net.ReadVector()}
|
||||
end
|
||||
|
||||
AdvDupe2.GhostEntities[AdvDupe2.CurrentGhost] = MakeGhostsFromTable(ghost)
|
||||
AdvDupe2.CurrentGhost = AdvDupe2.CurrentGhost + 1
|
||||
end)
|
||||
|
||||
function AdvDupe2.StartGhosting()
|
||||
AdvDupe2.RemoveGhosts()
|
||||
if(not AdvDupe2.GhostToSpawn) then return end
|
||||
AdvDupe2.CurrentGhost = 1
|
||||
AdvDupe2.GhostEntities = {}
|
||||
AdvDupe2.Ghosting = true
|
||||
AdvDupe2.HeadGhost = MakeGhostsFromTable(AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt])
|
||||
AdvDupe2.GhostEntities[AdvDupe2.HeadEnt] = AdvDupe2.HeadGhost
|
||||
AdvDupe2.TotalGhosts = #AdvDupe2.GhostToSpawn
|
||||
|
||||
if AdvDupe2.TotalGhosts > 1 then
|
||||
if not AdvDupe2.BusyBar then
|
||||
AdvDupe2.InitProgressBar("Ghosting: ")
|
||||
AdvDupe2.BusyBar = false
|
||||
end
|
||||
hook.Add("Tick", "AdvDupe2_SpawnGhosts", SpawnGhosts)
|
||||
else
|
||||
AdvDupe2.Ghosting = false
|
||||
end
|
||||
end
|
||||
net.Receive("AdvDupe2_StartGhosting", function()
|
||||
AdvDupe2.StartGhosting()
|
||||
end)
|
||||
|
||||
net.Receive("AdvDupe2_RemoveGhosts", AdvDupe2.RemoveGhosts)
|
||||
|
||||
--Update the ghost's postion and angles based on where the player is looking and the offsets
|
||||
local Lheadpos, Lheadang = Vector(), Angle()
|
||||
function AdvDupe2.UpdateGhosts(force)
|
||||
if not IsValid(AdvDupe2.HeadGhost) then
|
||||
AdvDupe2.RemoveGhosts()
|
||||
AdvDupe2.Notify("Invalid ghost parent!", NOTIFY_ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local trace = LocalPlayer():GetEyeTrace()
|
||||
if (not trace.Hit) then return end
|
||||
|
||||
local originpos, originang, headpos, headang
|
||||
local worigin = GetConVar("advdupe2_offset_world"):GetBool()
|
||||
if(GetConVar("advdupe2_original_origin"):GetBool())then
|
||||
originang = Angle()
|
||||
originpos = Vector(AdvDupe2.HeadPos)
|
||||
headpos = AdvDupe2.HeadPos + AdvDupe2.HeadOffset
|
||||
headang = AdvDupe2.HeadAngle
|
||||
else
|
||||
local hangle = worigin and Angle(0,0,0) or AdvDupe2.HeadAngle
|
||||
local pz = math.Clamp(AdvDupe2.HeadZPos + GetConVar("advdupe2_offset_z"):GetFloat() or 0, -16000, 16000)
|
||||
local ap = math.Clamp(GetConVar("advdupe2_offset_pitch"):GetFloat() or 0, -180, 180)
|
||||
local ay = math.Clamp(GetConVar("advdupe2_offset_yaw" ):GetFloat() or 0, -180, 180)
|
||||
local ar = math.Clamp(GetConVar("advdupe2_offset_roll" ):GetFloat() or 0, -180, 180)
|
||||
originang = Angle(ap, ay, ar)
|
||||
originpos = Vector(trace.HitPos); originpos.z = originpos.z + pz
|
||||
headpos, headang = LocalToWorld(AdvDupe2.HeadOffset, hangle, originpos, originang)
|
||||
end
|
||||
|
||||
if math.abs(Lheadpos.x - headpos.x) > 0.01 or
|
||||
math.abs(Lheadpos.y - headpos.y) > 0.01 or
|
||||
math.abs(Lheadpos.z - headpos.z) > 0.01 or
|
||||
math.abs(Lheadang.p - headang.p) > 0.01 or
|
||||
math.abs(Lheadang.y - headang.y) > 0.01 or
|
||||
math.abs(Lheadang.r - headang.r) > 0.01 or force then
|
||||
|
||||
Lheadpos = headpos
|
||||
Lheadang = headang
|
||||
|
||||
AdvDupe2.HeadGhost:SetPos(headpos)
|
||||
AdvDupe2.HeadGhost:SetAngles(headang)
|
||||
|
||||
for k, ghost in ipairs(AdvDupe2.GhostEntities) do
|
||||
local phys = ghost.Phys
|
||||
|
||||
if phys then
|
||||
local pos, ang = LocalToWorld(phys.Pos, phys.Angle, originpos, originang)
|
||||
ghost:SetPos(pos)
|
||||
ghost:SetAngles(ang)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,564 @@
|
||||
--[[
|
||||
Title: Adv. Dupe 2 Codec
|
||||
|
||||
Desc: Dupe encoder/decoder.
|
||||
|
||||
Author: emspike
|
||||
|
||||
Version: 2.0
|
||||
]]
|
||||
|
||||
local REVISION = 5
|
||||
AdvDupe2.CodecRevision = REVISION
|
||||
AdvDupe2.MaxDupeSize = 32e6 -- 32 MB
|
||||
|
||||
include( "sh_codec_legacy.lua" )
|
||||
AddCSLuaFile( "sh_codec_legacy.lua" )
|
||||
|
||||
local pairs = pairs
|
||||
local error = error
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
local format = string.format
|
||||
local char = string.char
|
||||
local concat = table.concat
|
||||
local compress = util.Compress
|
||||
local decompress = util.Decompress
|
||||
|
||||
--[[
|
||||
Name: GenerateDupeStamp
|
||||
Desc: Generates an info table.
|
||||
Params: <player> ply
|
||||
Return: <table> stamp
|
||||
]]
|
||||
function AdvDupe2.GenerateDupeStamp(ply)
|
||||
local stamp = {}
|
||||
stamp.name = ply:GetName()
|
||||
stamp.time = os.date("%I:%M %p")
|
||||
stamp.date = os.date("%d %B %Y")
|
||||
stamp.timezone = os.date("%z")
|
||||
hook.Call("AdvDupe2_StampGenerated",GAMEMODE,stamp)
|
||||
return stamp
|
||||
end
|
||||
|
||||
function AdvDupe2.SanitizeFilename(filename)
|
||||
filename = string.gsub( filename, "[\":]", "_" )
|
||||
filename = string.gsub( filename, "%s+", " " )
|
||||
filename = string.gsub( filename, "%s*([\\/%.])%s*", "%1" )
|
||||
return filename
|
||||
end
|
||||
|
||||
local function makeInfo(tbl)
|
||||
local info = ""
|
||||
for k, v in pairs(tbl) do
|
||||
info = concat{info,k,"\1",v,"\1"}
|
||||
end
|
||||
return info.."\2"
|
||||
end
|
||||
|
||||
local AD2FF = "AD2F%s\n%s\n%s"
|
||||
|
||||
local tables, buff
|
||||
|
||||
local function noserializer() end
|
||||
|
||||
local enc = {}
|
||||
for i = 1, 255 do enc[i] = noserializer end
|
||||
|
||||
local function isArray(tbl)
|
||||
local ret = true
|
||||
local m = 0
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
m = m + 1
|
||||
if k ~= m or enc[TypeID(v)] == noserializer then
|
||||
ret = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
local function write(obj)
|
||||
enc[TypeID(obj)](obj)
|
||||
end
|
||||
|
||||
local len, tables, tablesLookup
|
||||
|
||||
enc[TYPE_TABLE] = function(obj) --table
|
||||
if not tablesLookup[obj] then
|
||||
tables = tables + 1
|
||||
tablesLookup[obj] = tables
|
||||
else
|
||||
buff:WriteByte(247)
|
||||
buff:WriteShort(tablesLookup[obj])
|
||||
return
|
||||
end
|
||||
|
||||
if isArray(obj) then
|
||||
buff:WriteByte(254)
|
||||
for i, v in pairs(obj) do
|
||||
write(v)
|
||||
end
|
||||
else
|
||||
buff:WriteByte(255)
|
||||
for k, v in pairs(obj) do
|
||||
if(enc[TypeID(k)] ~= noserializer and enc[TypeID(v)] ~= noserializer) then
|
||||
write(k)
|
||||
write(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
buff:WriteByte(246)
|
||||
end
|
||||
|
||||
enc[TYPE_BOOL] = function(obj) --boolean
|
||||
buff:WriteByte(obj and 253 or 252)
|
||||
end
|
||||
|
||||
enc[TYPE_NUMBER] = function(obj) --number
|
||||
buff:WriteByte(251)
|
||||
buff:WriteDouble(obj)
|
||||
end
|
||||
|
||||
enc[TYPE_VECTOR] = function(obj) --vector
|
||||
buff:WriteByte(250)
|
||||
buff:WriteDouble(obj.x)
|
||||
buff:WriteDouble(obj.y)
|
||||
buff:WriteDouble(obj.z)
|
||||
end
|
||||
|
||||
enc[TYPE_ANGLE] = function(obj) --angle
|
||||
buff:WriteByte(249)
|
||||
buff:WriteDouble(obj.p)
|
||||
buff:WriteDouble(obj.y)
|
||||
buff:WriteDouble(obj.r)
|
||||
end
|
||||
|
||||
enc[TYPE_STRING] = function(obj) --string
|
||||
len = #obj
|
||||
if len < 246 then
|
||||
buff:WriteByte(len)
|
||||
buff:Write(obj)
|
||||
else
|
||||
buff:WriteByte(248)
|
||||
buff:WriteULong(len)
|
||||
buff:Write(obj)
|
||||
end
|
||||
end
|
||||
|
||||
local function error_nodeserializer()
|
||||
buff:Seek(buff:Tell()-1)
|
||||
error(format("Couldn't find deserializer for type {typeid:%d}!", buff:ReadByte()))
|
||||
end
|
||||
|
||||
local reference = 0
|
||||
local read4, read5
|
||||
|
||||
do --Version 4
|
||||
local dec = {}
|
||||
for i = 1, 255 do dec[i] = error_nodeserializer end
|
||||
|
||||
local function read()
|
||||
local tt = buff:ReadByte()
|
||||
if not tt then
|
||||
error("Expected value, got EOF!")
|
||||
end
|
||||
if tt == 0 then
|
||||
return nil
|
||||
end
|
||||
return dec[tt]()
|
||||
end
|
||||
read4 = read
|
||||
|
||||
dec[255] = function() --table
|
||||
local t = {}
|
||||
local k
|
||||
reference = reference + 1
|
||||
local ref = reference
|
||||
repeat
|
||||
k = read()
|
||||
if k ~= nil then
|
||||
t[k] = read()
|
||||
end
|
||||
until (k == nil)
|
||||
tables[ref] = t
|
||||
return t
|
||||
end
|
||||
|
||||
dec[254] = function() --array
|
||||
local t = {}
|
||||
local k = 0
|
||||
local v
|
||||
reference = reference + 1
|
||||
local ref = reference
|
||||
repeat
|
||||
k = k + 1
|
||||
v = read()
|
||||
if(v ~= nil) then
|
||||
t[k] = v
|
||||
end
|
||||
|
||||
until (v == nil)
|
||||
tables[ref] = t
|
||||
return t
|
||||
end
|
||||
|
||||
dec[253] = function()
|
||||
return true
|
||||
end
|
||||
dec[252] = function()
|
||||
return false
|
||||
end
|
||||
dec[251] = function()
|
||||
return buff:ReadDouble()
|
||||
end
|
||||
dec[250] = function()
|
||||
return Vector(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
|
||||
end
|
||||
dec[249] = function()
|
||||
return Angle(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
|
||||
end
|
||||
dec[248] = function() --null-terminated string
|
||||
local start = buff:Tell()
|
||||
local slen = 0
|
||||
|
||||
while buff:ReadByte() ~= 0 do
|
||||
slen = slen + 1
|
||||
end
|
||||
|
||||
buff:Seek(start)
|
||||
|
||||
local retv = buff:Read(slen)
|
||||
if(not retv) then retv="" end
|
||||
buff:ReadByte()
|
||||
|
||||
return retv
|
||||
end
|
||||
dec[247] = function() --table reference
|
||||
reference = reference + 1
|
||||
return tables[buff:ReadShort()]
|
||||
end
|
||||
|
||||
for i = 1, 246 do dec[i] = function() return buff:Read(i) end end
|
||||
end
|
||||
|
||||
do --Version 5
|
||||
local dec = {}
|
||||
for i = 1, 255 do dec[i] = error_nodeserializer end
|
||||
|
||||
local function read()
|
||||
local tt = buff:ReadByte()
|
||||
if not tt then
|
||||
error("Expected value, got EOF!")
|
||||
end
|
||||
return dec[tt]()
|
||||
end
|
||||
read5 = read
|
||||
|
||||
dec[255] = function() --table
|
||||
local t = {}
|
||||
reference = reference + 1
|
||||
tables[reference] = t
|
||||
|
||||
for k in read do
|
||||
t[k] = read()
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
dec[254] = function() --array
|
||||
local t = {}
|
||||
reference = reference + 1
|
||||
tables[reference] = t
|
||||
|
||||
local k = 1
|
||||
for v in read do
|
||||
t[k] = v
|
||||
k = k + 1
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
dec[253] = function()
|
||||
return true
|
||||
end
|
||||
dec[252] = function()
|
||||
return false
|
||||
end
|
||||
dec[251] = function()
|
||||
return buff:ReadDouble()
|
||||
end
|
||||
dec[250] = function()
|
||||
return Vector(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
|
||||
end
|
||||
dec[249] = function()
|
||||
return Angle(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
|
||||
end
|
||||
dec[248] = function() -- Length>246 string
|
||||
local slen = buff:ReadULong()
|
||||
local retv = buff:Read(slen)
|
||||
if(not retv) then retv = "" end
|
||||
return retv
|
||||
end
|
||||
dec[247] = function() --table reference
|
||||
return tables[buff:ReadShort()]
|
||||
end
|
||||
dec[246] = function() --nil
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, 245 do dec[i] = function() return buff:Read(i) end end
|
||||
|
||||
dec[0] = function() return "" end
|
||||
end
|
||||
|
||||
local function serialize(tbl)
|
||||
tables = 0
|
||||
tablesLookup = {}
|
||||
|
||||
buff = file.Open("ad2temp.txt", "wb", "DATA")
|
||||
if not buff then error("Failed to open file data/ad2temp.txt for writing!") end
|
||||
write(tbl)
|
||||
buff:Close()
|
||||
|
||||
buff = file.Open("ad2temp.txt","rb","DATA")
|
||||
if not buff then error("Failed to open file data/ad2temp.txt for reading!") end
|
||||
local ret = buff:Read(buff:Size())
|
||||
buff:Close()
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
local function deserialize(str, read)
|
||||
|
||||
if(str == nil) then
|
||||
error("File could not be decompressed!")
|
||||
return {}
|
||||
end
|
||||
|
||||
tables = {}
|
||||
reference = 0
|
||||
buff = file.Open("ad2temp.txt","wb","DATA")
|
||||
if not buff then error("Failed to open file data/ad2temp.txt for writing!") end
|
||||
buff:Write(str)
|
||||
buff:Flush()
|
||||
buff:Close()
|
||||
|
||||
buff = file.Open("ad2temp.txt","rb", "DATA")
|
||||
if not buff then error("Failed to open file data/ad2temp.txt for reading!") end
|
||||
local success, tbl = pcall(read)
|
||||
buff:Close()
|
||||
|
||||
if success then
|
||||
return tbl
|
||||
else
|
||||
error(tbl)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Name: Encode
|
||||
Desc: Generates the string for a dupe file with the given data.
|
||||
Params: <table> dupe, <table> info, <function> callback, <...> args
|
||||
Return: runs callback(<string> encoded_dupe, <...> args)
|
||||
]]
|
||||
function AdvDupe2.Encode(dupe, info, callback, ...)
|
||||
local encodedTable = compress(serialize(dupe))
|
||||
info.check = "\r\n\t\n"
|
||||
info.size = #encodedTable
|
||||
|
||||
callback(AD2FF:format(char(REVISION), makeInfo(info), encodedTable),...)
|
||||
end
|
||||
|
||||
--seperates the header and body and converts the header to a table
|
||||
local function getInfo(str)
|
||||
local last = str:find("\2")
|
||||
if not last then
|
||||
error("Attempt to read AD2 file with malformed info block!")
|
||||
end
|
||||
local info = {}
|
||||
local ss = str:sub(1, last - 1)
|
||||
for k, v in ss:gmatch("(.-)\1(.-)\1") do
|
||||
info[k] = v
|
||||
end
|
||||
|
||||
if info.check ~= "\r\n\t\n" then
|
||||
if info.check == "\10\9\10" then
|
||||
error("Detected AD2 file corrupted in file transfer (newlines homogenized)(when using FTP, transfer AD2 files in image/binary mode, not ASCII/text mode)!")
|
||||
elseif info.check ~= nil then
|
||||
error("Detected AD2 file corrupted by newline replacements (copy/pasting the data in various editors can cause this!)")
|
||||
else
|
||||
error("Attempt to read AD2 file with malformed info block!")
|
||||
end
|
||||
end
|
||||
return info, str:sub(last+2)
|
||||
end
|
||||
|
||||
--decoders for individual versions go here
|
||||
local versions = {}
|
||||
|
||||
versions[1] = AdvDupe2.LegacyDecoders[1]
|
||||
versions[2] = AdvDupe2.LegacyDecoders[2]
|
||||
|
||||
versions[3] = function(encodedDupe)
|
||||
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
|
||||
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
|
||||
encodedDupe = encodedDupe:Replace("\r\n", "\n")
|
||||
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
|
||||
return versions[4](encodedDupe)
|
||||
end
|
||||
|
||||
versions[4] = function(encodedDupe)
|
||||
local info, dupestring = getInfo(encodedDupe:sub(7))
|
||||
return deserialize(decompress(dupestring, AdvDupe2.MaxDupeSize), read4), info
|
||||
end
|
||||
|
||||
versions[5] = function(encodedDupe)
|
||||
local info, dupestring = getInfo(encodedDupe:sub(7))
|
||||
return deserialize(decompress(dupestring, AdvDupe2.MaxDupeSize), read5), info
|
||||
end
|
||||
|
||||
function AdvDupe2.CheckValidDupe(dupe, info)
|
||||
if not dupe.HeadEnt then return false, "Missing HeadEnt table" end
|
||||
if not dupe.Entities then return false, "Missing Entities table" end
|
||||
if not dupe.Constraints then return false, "Missing Constraints table" end
|
||||
if not dupe.HeadEnt.Z then return false, "Missing HeadEnt.Z" end
|
||||
if not dupe.HeadEnt.Pos then return false, "Missing HeadEnt.Pos" end
|
||||
if not dupe.HeadEnt.Index then return false, "Missing HeadEnt.Index" end
|
||||
if not dupe.Entities[dupe.HeadEnt.Index] then return false, "Missing HeadEnt index ["..dupe.HeadEnt.Index.."] from Entities table" end
|
||||
for key, data in pairs(dupe.Entities) do
|
||||
if not data.PhysicsObjects then return false, "Missing PhysicsObject table from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
|
||||
if not data.PhysicsObjects[0] then return false, "Missing PhysicsObject[0] table from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
|
||||
if info.ad1 then -- Advanced Duplicator 1
|
||||
if not data.PhysicsObjects[0].LocalPos then return false, "Missing PhysicsObject[0].LocalPos from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
|
||||
if not data.PhysicsObjects[0].LocalAngle then return false, "Missing PhysicsObject[0].LocalAngle from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
|
||||
else -- Advanced Duplicator 2
|
||||
if not data.PhysicsObjects[0].Pos then return false, "Missing PhysicsObject[0].Pos from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
|
||||
if not data.PhysicsObjects[0].Angle then return false, "Missing PhysicsObject[0].Angle from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
|
||||
end
|
||||
end
|
||||
return true, dupe
|
||||
end
|
||||
|
||||
--[[
|
||||
Name: Decode
|
||||
Desc: Generates the table for a dupe from the given string. Inverse of Encode
|
||||
Params: <string> encodedDupe, <function> callback, <...> args
|
||||
Return: runs callback(<boolean> success, <table/string> tbl, <table> info)
|
||||
]]
|
||||
function AdvDupe2.Decode(encodedDupe)
|
||||
|
||||
local sig, rev = encodedDupe:match("^(....)(.)")
|
||||
|
||||
if not rev then
|
||||
return false, "Malformed dupe (wtf <5 chars long)!"
|
||||
end
|
||||
|
||||
rev = rev:byte()
|
||||
|
||||
if sig ~= "AD2F" then
|
||||
if sig == "[Inf" then --legacy support, ENGAGE (AD1 dupe detected)
|
||||
local success, tbl, info, moreinfo = pcall(AdvDupe2.LegacyDecoders[0], encodedDupe)
|
||||
|
||||
if success then
|
||||
info.ad1 = true
|
||||
info.size = #encodedDupe
|
||||
info.revision = 0
|
||||
|
||||
local index = tonumber(moreinfo.Head) or (istable(tbl.Entities) and next(tbl.Entities))
|
||||
if not index then return false, "Missing head index" end
|
||||
local pos
|
||||
if isstring(moreinfo.StartPos) then
|
||||
local spx,spy,spz = moreinfo.StartPos:match("^(.-),(.-),(.+)$")
|
||||
pos = Vector(tonumber(spx) or 0, tonumber(spy) or 0, tonumber(spz) or 0)
|
||||
else
|
||||
pos = Vector()
|
||||
end
|
||||
local z
|
||||
if isstring(moreinfo.HoldPos) then
|
||||
z = (tonumber(moreinfo.HoldPos:match("^.-,.-,(.+)$")) or 0)*-1
|
||||
else
|
||||
z = 0
|
||||
end
|
||||
tbl.HeadEnt = {
|
||||
Index = index,
|
||||
Pos = pos,
|
||||
Z = z
|
||||
}
|
||||
else
|
||||
ErrorNoHalt(tbl)
|
||||
end
|
||||
|
||||
if success then
|
||||
success, tbl = AdvDupe2.CheckValidDupe(tbl, info)
|
||||
end
|
||||
|
||||
return success, tbl, info, moreinfo
|
||||
else
|
||||
return false, "Unknown duplication format!"
|
||||
end
|
||||
elseif rev > REVISION then
|
||||
return false, format("Newer codec needed. (have rev %u, need rev %u) Update Advdupe2.",REVISION,rev)
|
||||
elseif rev < 1 then
|
||||
return false, format("Attempt to use an invalid format revision (rev %d)!", rev)
|
||||
else
|
||||
local success, tbl, info = pcall(versions[rev], encodedDupe)
|
||||
|
||||
if success then
|
||||
success, tbl = AdvDupe2.CheckValidDupe(tbl, info)
|
||||
end
|
||||
if success then
|
||||
info.revision = rev
|
||||
end
|
||||
|
||||
return success, tbl, info
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
|
||||
concommand.Add("advdupe2_to_json", function(_,_,arg)
|
||||
if not arg[1] then print("Need AdvDupe2 file name argument!") return end
|
||||
local readFileName = "advdupe2/"..arg[1]
|
||||
local writeFileName = "advdupe2/"..string.StripExtension(arg[1])..".json"
|
||||
|
||||
writeFileName = AdvDupe2.SanitizeFilename(writeFileName)
|
||||
|
||||
local readFile = file.Open(readFileName, "rb", "DATA")
|
||||
if not readFile then print("File could not be read or found! ("..readFileName..")") return end
|
||||
local readData = readFile:Read(readFile:Size())
|
||||
readFile:Close()
|
||||
local ok, tbl = AdvDupe2.Decode(readData)
|
||||
local writeFile = file.Open(writeFileName, "wb", "DATA")
|
||||
if not writeFile then print("File could not be written! ("..writeFileName..")") return end
|
||||
writeFile:Write(util.TableToJSON(tbl))
|
||||
writeFile:Close()
|
||||
print("File written! ("..writeFileName..")")
|
||||
end)
|
||||
|
||||
concommand.Add("advdupe2_from_json", function(_,_,arg)
|
||||
if not arg[1] then print("Need json file name argument!") return end
|
||||
local readFileName = "advdupe2/"..arg[1]
|
||||
local writeFileName = "advdupe2/"..string.StripExtension(arg[1])..".txt"
|
||||
|
||||
local readFile = file.Open(readFileName, "rb", "DATA")
|
||||
if not readFile then print("File could not be read or found! ("..readFileName..")") return end
|
||||
local readData = readFile:Read(readFile:Size())
|
||||
readFile:Close()
|
||||
|
||||
AdvDupe2.Encode(util.JSONToTable(readData), {}, function(data)
|
||||
local writeFile = file.Open(writeFileName, "wb", "DATA")
|
||||
if not writeFile then print("File could not be written! ("..writeFileName..")") return end
|
||||
writeFile:Write(data)
|
||||
writeFile:Close()
|
||||
print("File written! ("..writeFileName..")")
|
||||
end)
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,559 @@
|
||||
--[[
|
||||
Title: Adv. Dupe 2 Codec Legacy Support
|
||||
|
||||
Desc: Facilitates opening of dupes from AD1 and earlier AD2 versions.
|
||||
|
||||
Author: emspike
|
||||
|
||||
Version: 2.0
|
||||
]]
|
||||
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
local tonumber = tonumber
|
||||
local error = error
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
local format = string.format
|
||||
local char = string.char
|
||||
local byte = string.byte
|
||||
local sub = string.sub
|
||||
local gsub = string.gsub
|
||||
local find = string.find
|
||||
local gmatch = string.gmatch
|
||||
local match = string.match
|
||||
local concat = table.concat
|
||||
|
||||
--[[
|
||||
Name: GenerateDupeStamp
|
||||
Desc: Generates an info table.
|
||||
Params: <player> ply
|
||||
Return: <table> stamp
|
||||
]]
|
||||
function AdvDupe2.GenerateDupeStamp(ply)
|
||||
local stamp = {}
|
||||
stamp.name = ply:GetName()
|
||||
stamp.time = os.date("%I:%M %p")
|
||||
stamp.date = os.date("%d %B %Y")
|
||||
stamp.timezone = os.date("%z")
|
||||
hook.Call("AdvDupe2_StampGenerated",GAMEMODE,stamp)
|
||||
return stamp
|
||||
end
|
||||
|
||||
local AD2FF = "AD2F%s\n%s\n%s"
|
||||
|
||||
local decode_types_v1, decode_types_v2
|
||||
local tables = 0
|
||||
local str,pos
|
||||
local a,b,c,m,n,w,tblref
|
||||
|
||||
local function read_v2()
|
||||
local t = byte(str, pos+1)
|
||||
if t then
|
||||
local dt = decode_types_v2[t]
|
||||
if dt then
|
||||
pos = pos + 1
|
||||
return dt()
|
||||
else
|
||||
error(format("encountered invalid data type (%u)\n",t))
|
||||
end
|
||||
else
|
||||
error("expected value, got EOF\n")
|
||||
end
|
||||
end
|
||||
|
||||
decode_types_v2 = {
|
||||
[1 ] = function()
|
||||
error("expected value, got terminator\n")
|
||||
end,
|
||||
[2 ] = function() -- table
|
||||
|
||||
m = find(str, "\1", pos)
|
||||
if m then
|
||||
w = sub(str, pos+1, m-1)
|
||||
pos = m
|
||||
else
|
||||
error("expected table identifier, got EOF\n")
|
||||
end
|
||||
|
||||
local t = {}
|
||||
tables[w] = t
|
||||
|
||||
while true do
|
||||
if byte(str, pos+1) == 1 then
|
||||
pos = pos + 1
|
||||
return t
|
||||
else
|
||||
t[read_v2()] = read_v2()
|
||||
end
|
||||
end
|
||||
end,
|
||||
[3 ] = function() -- array
|
||||
|
||||
m = find(str, "\1", pos)
|
||||
if m then
|
||||
w = sub(str, pos+1, m-1)
|
||||
pos = m
|
||||
else
|
||||
error("expected table identifier, got EOF\n")
|
||||
end
|
||||
|
||||
local t, i = {}, 1
|
||||
|
||||
tables[w] = t
|
||||
|
||||
while true do
|
||||
if byte(str,pos+1) == 1 then
|
||||
pos = pos+1
|
||||
return t
|
||||
else
|
||||
t[i] = read_v2()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end,
|
||||
[4 ] = function() -- false boolean
|
||||
return false
|
||||
end,
|
||||
[5 ] = function() -- true boolean
|
||||
return true
|
||||
end,
|
||||
[6 ] = function() -- number
|
||||
m = find(str, "\1", pos)
|
||||
if m then
|
||||
a = tonumber(sub(str, pos+1, m-1)) or 0
|
||||
pos = m
|
||||
return a
|
||||
else
|
||||
error("expected number, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[7 ] = function() -- string
|
||||
m = find(str,"\1",pos)
|
||||
if m then
|
||||
w = sub(str, pos+1, m-1)
|
||||
pos = m
|
||||
return w
|
||||
else
|
||||
error("expected string, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[8 ] = function() -- Vector
|
||||
m,n = find(str,".-\1.-\1.-\1", pos)
|
||||
if m then
|
||||
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
|
||||
pos = n
|
||||
return Vector(tonumber(a), tonumber(b), tonumber(c))
|
||||
else
|
||||
error("expected vector, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[9 ] = function() -- Angle
|
||||
m,n = find(str, ".-\1.-\1.-\1", pos)
|
||||
if m then
|
||||
a,b,c = match(str, "^(.-)\1(.-)\1(.-)\1",pos+1)
|
||||
pos = n
|
||||
return Angle(tonumber(a), tonumber(b), tonumber(c))
|
||||
else
|
||||
error("expected angle, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[10 ] = function() -- Table Reference
|
||||
m = find(str,"\1",pos)
|
||||
if m then
|
||||
w = sub(str,pos+1,m-1)
|
||||
pos = m
|
||||
else
|
||||
error("expected table identifier, got EOF\n")
|
||||
end
|
||||
tblref = tables[w]
|
||||
|
||||
if tblref then
|
||||
return tblref
|
||||
else
|
||||
error(format("table identifier %s points to nil\n", w))
|
||||
end
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
|
||||
local function read_v1()
|
||||
local t = byte(str,pos+1)
|
||||
if t then
|
||||
local dt = decode_types_v1[t]
|
||||
if dt then
|
||||
pos = pos + 1
|
||||
return dt()
|
||||
else
|
||||
error(format("encountered invalid data type (%u)\n",t))
|
||||
end
|
||||
else
|
||||
error("expected value, got EOF\n")
|
||||
end
|
||||
end
|
||||
|
||||
decode_types_v1 = {
|
||||
[1 ] = function()
|
||||
error("expected value, got terminator\n")
|
||||
end,
|
||||
[2 ] = function() -- table
|
||||
local t = {}
|
||||
while true do
|
||||
if byte(str,pos+1) == 1 then
|
||||
pos = pos+1
|
||||
return t
|
||||
else
|
||||
t[read_v1()] = read_v1()
|
||||
end
|
||||
end
|
||||
end,
|
||||
[3 ] = function() -- array
|
||||
local t, i = {}, 1
|
||||
while true do
|
||||
if byte(str,pos+1) == 1 then
|
||||
pos = pos+1
|
||||
return t
|
||||
else
|
||||
t[i] = read_v1()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end,
|
||||
[4 ] = function() -- false boolean
|
||||
return false
|
||||
end,
|
||||
[5 ] = function() -- true boolean
|
||||
return true
|
||||
end,
|
||||
[6 ] = function() -- number
|
||||
m = find(str,"\1",pos)
|
||||
if m then
|
||||
a = tonumber(sub(str,pos+1,m-1)) or 0
|
||||
pos = m
|
||||
return a
|
||||
else
|
||||
error("expected number, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[7 ] = function() -- string
|
||||
m = find(str,"\1",pos)
|
||||
if m then
|
||||
w = sub(str,pos+1,m-1)
|
||||
pos = m
|
||||
return w
|
||||
else
|
||||
error("expected string, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[8 ] = function() -- Vector
|
||||
m,n = find(str,".-\1.-\1.-\1",pos)
|
||||
if m then
|
||||
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
|
||||
pos = n
|
||||
return Vector(tonumber(a), tonumber(b), tonumber(c))
|
||||
else
|
||||
error("expected vector, got EOF\n")
|
||||
end
|
||||
end,
|
||||
[9 ] = function() -- Angle
|
||||
m,n = find(str,".-\1.-\1.-\1",pos)
|
||||
if m then
|
||||
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
|
||||
pos = n
|
||||
return Angle(tonumber(a), tonumber(b), tonumber(c))
|
||||
else
|
||||
error("expected angle, got EOF\n")
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local function deserialize_v1(data)
|
||||
str = data
|
||||
pos = 0
|
||||
tables = {}
|
||||
return read_v1()
|
||||
end
|
||||
|
||||
local function deserialize_v2(data)
|
||||
str = data
|
||||
pos = 0
|
||||
tables = {}
|
||||
return read_v2()
|
||||
end
|
||||
|
||||
local function lzwDecode(encoded)
|
||||
local dictionary_length = 256
|
||||
local dictionary = {}
|
||||
for i = 0, 255 do
|
||||
dictionary[i] = char(i)
|
||||
end
|
||||
|
||||
local pos = 2
|
||||
local decompressed = {}
|
||||
local decompressed_length = 1
|
||||
|
||||
local index = byte(encoded)
|
||||
local word = dictionary[index]
|
||||
|
||||
decompressed[decompressed_length] = dictionary[index]
|
||||
|
||||
local entry
|
||||
local encoded_length = #encoded
|
||||
local firstbyte --of an index
|
||||
while pos <= encoded_length do
|
||||
firstbyte = byte(encoded,pos)
|
||||
if firstbyte > 252 then --now we know it's a length indicator for a multibyte index
|
||||
index = 0
|
||||
firstbyte = 256 - firstbyte
|
||||
|
||||
--[[if pos+firstbyte > encoded_length then --will test for performance impact
|
||||
error("expected index got EOF")
|
||||
end]]
|
||||
|
||||
for i = pos+firstbyte, pos+1, -1 do
|
||||
index = bit.bor(bit.lshift(index, 8), byte(encoded,i))
|
||||
end
|
||||
pos = pos + firstbyte + 1
|
||||
else
|
||||
index = firstbyte
|
||||
pos = pos + 1
|
||||
end
|
||||
entry = dictionary[index] or (word..sub(word,1,1))
|
||||
decompressed_length = decompressed_length + 1
|
||||
decompressed[decompressed_length] = entry
|
||||
dictionary[dictionary_length] = word..sub(entry,1,1)
|
||||
dictionary_length = dictionary_length + 1
|
||||
word = entry
|
||||
end
|
||||
return concat(decompressed)
|
||||
end
|
||||
|
||||
--http://en.wikipedia.org/wiki/Huffman_coding#Decompression
|
||||
|
||||
local invcodes = {[2]={[0]="\254"},[5]={[22]="\1",[11]="\2"},[6]={[13]="\7",[35]="\6",[37]="\5",[58]="\3",[31]="\8",[9]="\13",[51]="\9",[55]="\10",[57]="\4",[59]="\15"},[7]={[1]="\14",[15]="\16",[87]="\31",[89]="\30",[62]="\26",[17]="\27",[97]="\19",[19]="\43",[10]="\12",[39]="\33",[41]="\24",[82]="\40",[3]="\32",[46]="\41",[47]="\38",[94]="\25",[65]="\23",[50]="\39",[26]="\11",[7]="\28",[33]="\18",[61]="\17",[25]="\42"},[8]={[111]="\101",[162]="\29",[2]="\34",[133]="\21",[142]="\36",[5]="\20",[21]="\37",[170]="\44",[130]="\22",[66]="\35"},[9]={[241]="\121",[361]="\104",[365]="\184",[125]="\227",[373]="\198",[253]="\117",[381]="\57",[270]="\49",[413]="\80",[290]="\47",[294]="\115",[38]="\112",[429]="\74",[433]="\0",[437]="\48",[158]="\183",[453]="\107",[166]="\111",[469]="\182",[477]="\241",[45]="\86",[489]="\69",[366]="\100",[497]="\61",[509]="\76",[49]="\53",[390]="\78",[279]="\196",[283]="\70",[414]="\98",[53]="\55",[422]="\109",[233]="\79",[349]="\89",[369]="\52",[14]="\105",[238]="\56",[319]="\162",[323]="\83",[327]="\63",[458]="\65",[335]="\231",[339]="\225",[337]="\114",[347]="\193",[493]="\139",[23]="\209",[359]="\250",[490]="\68",[42]="\54",[63]="\91",[286]="\97",[254]="\50",[510]="\108",[109]="\73",[67]="\103",[255]="\122",[69]="\170",[70]="\110",[407]="\176",[411]="\119",[110]="\120",[83]="\146",[149]="\163",[151]="\224",[85]="\51",[155]="\177",[79]="\251",[27]="\118",[447]="\159",[451]="\228",[455]="\175",[383]="\174",[463]="\243",[467]="\157",[173]="\210",[475]="\167",[177]="\84",[90]="\45",[487]="\206",[93]="\226",[495]="\245",[207]="\64",[127]="\147",[191]="\155",[511]="\153",[195]="\208",[197]="\85",[199]="\178",[181]="\82",[102]="\116",[103]="\71",[285]="\144",[105]="\102",[211]="\199",[213]="\123",[301]="\66",[305]="\46",[219]="\137",[81]="\67",[91]="\88",[157]="\130",[325]="\95",[29]="\58",[231]="\201",[117]="\99",[341]="\222",[237]="\77",[239]="\211",[71]="\223"},[10]={[710]="\149",[245]="\60",[742]="\172",[774]="\81",[134]="\151",[917]="\145",[274]="\216",[405]="\242",[146]="\194",[838]="\246",[298]="\248",[870]="\189",[1013]="\150",[894]="\190",[326]="\244",[330]="\166",[334]="\217",[465]="\179",[346]="\59",[354]="\180",[966]="\212",[974]="\143",[370]="\148",[998]="\154",[625]="\138",[382]="\161",[194]="\141",[198]="\126",[402]="\96",[206]="\185",[586]="\129",[721]="\187",[610]="\135",[618]="\181",[626]="\72",[226]="\62",[454]="\127",[658]="\113",[462]="\164",[234]="\214",[474]="\140",[242]="\106",[714]="\188",[730]="\87",[498]="\237",[746]="\125",[754]="\229",[786]="\128",[202]="\93",[18]="\255",[810]="\173",[846]="\131",[74]="\192",[842]="\142",[977]="\252",[858]="\235",[78]="\134",[874]="\234",[882]="\90",[646]="\92",[1006]="\160",[126]="\165",[914]="\221",[718]="\94",[738]="\238",[638]="\197",[482]="\230",[34]="\220",[962]="\133",[6]="\213",[706]="\219",[986]="\171",[994]="\233",[866]="\200",[1010]="\247",[98]="\169",[518]="\236",[494]="\207",[230]="\205",[542]="\191",[501]="\202",[530]="\203",[450]="\204",[209]="\158",[106]="\186",[590]="\136",[218]="\232",[733]="\124",[309]="\168",[221]="\152",[757]="\240",[113]="\215",[114]="\156",[362]="\239",[486]="\132",[358]="\249",[262]="\75",[30]="\218",[821]="\195",[546]="\253"}}
|
||||
|
||||
local function huffmanDecode(encoded)
|
||||
|
||||
local h1,h2,h3 = byte(encoded, 1, 3)
|
||||
|
||||
if (not h3) or (#encoded < 4) then
|
||||
error("invalid input")
|
||||
end
|
||||
|
||||
local original_length = bit.bor(bit.lshift(h3,16), bit.lshift(h2,8), h1)
|
||||
local encoded_length = #encoded+1
|
||||
local decoded = {}
|
||||
local decoded_length = 0
|
||||
local buffer = 0
|
||||
local buffer_length = 0
|
||||
local code
|
||||
local code_len = 2
|
||||
local temp
|
||||
local pos = 4
|
||||
|
||||
while decoded_length < original_length do
|
||||
if code_len <= buffer_length then
|
||||
temp = invcodes[code_len]
|
||||
code = bit.band(buffer, bit.lshift(1, code_len)-1)
|
||||
if temp and temp[code] then --most of the time temp is nil
|
||||
decoded_length = decoded_length + 1
|
||||
decoded[decoded_length] = temp[code]
|
||||
buffer = bit.rshift(buffer, code_len)
|
||||
buffer_length = buffer_length - code_len
|
||||
code_len = 2
|
||||
else
|
||||
code_len = code_len + 1
|
||||
if code_len > 10 then
|
||||
error("malformed code")
|
||||
end
|
||||
end
|
||||
else
|
||||
buffer = bit.bor(buffer, bit.lshift(byte(encoded, pos), buffer_length))
|
||||
buffer_length = buffer_length + 8
|
||||
pos = pos + 1
|
||||
if pos > encoded_length then
|
||||
error("malformed code")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return concat(decoded)
|
||||
end
|
||||
|
||||
local function invEscapeSub(str)
|
||||
local escseq,body = match(str,"^(.-)\n(.-)$")
|
||||
|
||||
if not escseq then error("invalid input") end
|
||||
|
||||
return gsub(body,escseq,"\26")
|
||||
end
|
||||
|
||||
local dictionary
|
||||
local subtables
|
||||
|
||||
local function deserializeChunk(chunk)
|
||||
|
||||
local ctype,val = byte(chunk),sub(chunk,3)
|
||||
|
||||
if ctype == 89 then return dictionary[ val ]
|
||||
elseif ctype == 86 then
|
||||
local a,b,c = match(val,"^(.-),(.-),(.+)$")
|
||||
return Vector( tonumber(a), tonumber(b), tonumber(c) )
|
||||
elseif ctype == 65 then
|
||||
local a,b,c = match(val,"^(.-),(.-),(.+)$")
|
||||
return Angle( tonumber(a), tonumber(b), tonumber(c) )
|
||||
elseif ctype == 84 then
|
||||
local t = {}
|
||||
local tv = subtables[val]
|
||||
if not tv then
|
||||
tv = {}
|
||||
subtables[ val ] = tv
|
||||
end
|
||||
tv[#tv+1] = t
|
||||
return t
|
||||
elseif ctype == 78 then return tonumber(val)
|
||||
elseif ctype == 83 then return gsub(sub(val,2,-2),"»",";")
|
||||
elseif ctype == 66 then return val == "t"
|
||||
elseif ctype == 80 then return 1
|
||||
end
|
||||
|
||||
error(format("AD1 deserialization failed: invalid chunk (%u:%s)",ctype,val))
|
||||
|
||||
end
|
||||
|
||||
local function deserializeAD1(dupestring)
|
||||
|
||||
dupestring = dupestring:Replace("\r\n", "\n")
|
||||
local header, extraHeader, dupeBlock, dictBlock = dupestring:match("%[Info%]\n(.+)\n%[More Information%]\n(.+)\n%[Save%]\n(.+)\n%[Dict%]\n(.+)")
|
||||
|
||||
if not header then
|
||||
error("unknown duplication format")
|
||||
end
|
||||
|
||||
local info = {}
|
||||
for k,v in header:gmatch("([^\n:]+):([^\n]+)") do
|
||||
info[k] = v
|
||||
end
|
||||
|
||||
local moreinfo = {}
|
||||
for k,v in extraHeader:gmatch("([^\n:]+):([^\n]+)") do
|
||||
moreinfo[k] = v
|
||||
end
|
||||
|
||||
dictionary = {}
|
||||
for k,v in dictBlock:gmatch("(.-):\"(.-)\"\n") do
|
||||
dictionary[k] = v
|
||||
end
|
||||
|
||||
local dupe = {}
|
||||
for key,block in dupeBlock:gmatch("([^\n:]+):([^\n]+)") do
|
||||
|
||||
local tables = {}
|
||||
subtables = {}
|
||||
local head
|
||||
|
||||
for id,chunk in block:gmatch('(%w+){(.-)}') do
|
||||
|
||||
--check if this table is the trunk
|
||||
if byte(id) == 72 then
|
||||
id = sub(id,2)
|
||||
head = id
|
||||
end
|
||||
|
||||
tables[id] = {}
|
||||
|
||||
for kv in gmatch(chunk,'[^;]+') do
|
||||
|
||||
local k,v = match(kv,'(.-)=(.+)')
|
||||
|
||||
if k then
|
||||
k = deserializeChunk( k )
|
||||
v = deserializeChunk( v )
|
||||
|
||||
tables[id][k] = v
|
||||
else
|
||||
v = deserializeChunk( kv )
|
||||
local tid = tables[id]
|
||||
tid[#tid+1]=v
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--Restore table references
|
||||
for id,tbls in pairs( subtables ) do
|
||||
for _,tbl in pairs( tbls ) do
|
||||
if not tables[id] then error("attempt to reference a nonexistent table") end
|
||||
for k,v in pairs(tables[id]) do
|
||||
tbl[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dupe[key] = tables[ head ]
|
||||
|
||||
end
|
||||
|
||||
return dupe, info, moreinfo
|
||||
|
||||
end
|
||||
|
||||
--seperates the header and body and converts the header to a table
|
||||
local function getInfo(str)
|
||||
local last = str:find("\2")
|
||||
if not last then
|
||||
error("attempt to read AD2 file with malformed info block error 1")
|
||||
end
|
||||
local info = {}
|
||||
local ss = str:sub(1,last-1)
|
||||
for k,v in ss:gmatch("(.-)\1(.-)\1") do
|
||||
info[k] = v
|
||||
end
|
||||
if info.check ~= "\r\n\t\n" then
|
||||
if info.check == "\10\9\10" then
|
||||
error("detected AD2 file corrupted in file transfer (newlines homogenized)(when using FTP, transfer AD2 files in image/binary mode, not ASCII/text mode)")
|
||||
else
|
||||
error("attempt to read AD2 file with malformed info block error 2")
|
||||
end
|
||||
end
|
||||
return info, str:sub(last+2)
|
||||
end
|
||||
|
||||
--decoders for individual versions go here
|
||||
local versions = {}
|
||||
|
||||
versions[2] = function(encodedDupe)
|
||||
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
|
||||
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
|
||||
encodedDupe = encodedDupe:Replace("\r\n", "\n")
|
||||
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
|
||||
local info, dupestring = getInfo(encodedDupe:sub(7))
|
||||
return deserialize_v2(
|
||||
lzwDecode(
|
||||
huffmanDecode(
|
||||
invEscapeSub(dupestring)
|
||||
)
|
||||
)
|
||||
), info
|
||||
end
|
||||
|
||||
versions[1] = function(encodedDupe)
|
||||
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
|
||||
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
|
||||
encodedDupe = encodedDupe:Replace("\r\n", "\n")
|
||||
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
|
||||
local info, dupestring = getInfo(encodedDupe:sub(7))
|
||||
return deserialize_v1(
|
||||
lzwDecode(
|
||||
huffmanDecode(
|
||||
invEscapeSub(dupestring)
|
||||
)
|
||||
)
|
||||
), info
|
||||
end
|
||||
|
||||
versions[0] = deserializeAD1
|
||||
|
||||
AdvDupe2.LegacyDecoders = versions
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
--Save a file to the client
|
||||
local function SaveFile(ply, cmd, args)
|
||||
if(not ply.AdvDupe2 or not ply.AdvDupe2.Entities or next(ply.AdvDupe2.Entities)==nil)then AdvDupe2.Notify(ply,"Duplicator is empty, nothing to save.", NOTIFY_ERROR) return end
|
||||
if(not game.SinglePlayer() and CurTime()-(ply.AdvDupe2.FileMod or 0) < 0)then
|
||||
AdvDupe2.Notify(ply,"Cannot save at the moment. Please Wait...", NOTIFY_ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
if(ply.AdvDupe2.Pasting || ply.AdvDupe2.Downloading)then
|
||||
AdvDupe2.Notify(ply,"Advanced Duplicator 2 is busy.",NOTIFY_ERROR)
|
||||
return false
|
||||
end
|
||||
|
||||
ply.AdvDupe2.FileMod = CurTime()+tonumber(GetConVarString("AdvDupe2_FileModificationDelay")+2)
|
||||
|
||||
local name = string.Explode("/", args[1])
|
||||
ply.AdvDupe2.Name = name[#name]
|
||||
|
||||
net.Start("AdvDupe2_SetDupeInfo")
|
||||
net.WriteString(ply.AdvDupe2.Name)
|
||||
net.WriteString(ply:Nick())
|
||||
net.WriteString(os.date("%d %B %Y"))
|
||||
net.WriteString(os.date("%I:%M %p"))
|
||||
net.WriteString("")
|
||||
net.WriteString(args[2] or "")
|
||||
net.WriteString(table.Count(ply.AdvDupe2.Entities))
|
||||
net.WriteString(#ply.AdvDupe2.Constraints)
|
||||
net.Send(ply)
|
||||
|
||||
local Tab = {Entities = ply.AdvDupe2.Entities, Constraints = ply.AdvDupe2.Constraints, HeadEnt = ply.AdvDupe2.HeadEnt, Description=args[2]}
|
||||
|
||||
AdvDupe2.Encode( Tab, AdvDupe2.GenerateDupeStamp(ply), function(data)
|
||||
AdvDupe2.SendToClient(ply, data, false)
|
||||
end)
|
||||
end
|
||||
concommand.Add("AdvDupe2_SaveFile", SaveFile)
|
||||
|
||||
function AdvDupe2.SendToClient(ply, data, autosave)
|
||||
if(not IsValid(ply))then return end
|
||||
if #data > AdvDupe2.MaxDupeSize then
|
||||
AdvDupe2.Notify(ply,"Copied duplicator filesize is too big!",NOTIFY_ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
ply.AdvDupe2.Downloading = true
|
||||
AdvDupe2.InitProgressBar(ply,"Saving:")
|
||||
|
||||
net.Start("AdvDupe2_ReceiveFile")
|
||||
net.WriteBool(autosave)
|
||||
net.WriteStream(data, function()
|
||||
ply.AdvDupe2.Downloading = false
|
||||
end)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
function AdvDupe2.LoadDupe(ply,success,dupe,info,moreinfo)
|
||||
if(not IsValid(ply))then return end
|
||||
|
||||
if not success then
|
||||
AdvDupe2.Notify(ply,"Could not open "..dupe,NOTIFY_ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
if(not game.SinglePlayer())then
|
||||
if(tonumber(GetConVarString("AdvDupe2_MaxConstraints"))~=0 and #dupe["Constraints"]>tonumber(GetConVarString("AdvDupe2_MaxConstraints")))then
|
||||
AdvDupe2.Notify(ply,"Amount of constraints is greater than "..GetConVarString("AdvDupe2_MaxConstraints"),NOTIFY_ERROR)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
ply.AdvDupe2.Entities = {}
|
||||
ply.AdvDupe2.Constraints = {}
|
||||
ply.AdvDupe2.HeadEnt={}
|
||||
ply.AdvDupe2.Revision = info.revision
|
||||
|
||||
if(info.ad1)then
|
||||
|
||||
ply.AdvDupe2.HeadEnt.Index = tonumber(moreinfo.Head)
|
||||
local spx,spy,spz = moreinfo.StartPos:match("^(.-),(.-),(.+)$")
|
||||
ply.AdvDupe2.HeadEnt.Pos = Vector(tonumber(spx) or 0, tonumber(spy) or 0, tonumber(spz) or 0)
|
||||
local z = (tonumber(moreinfo.HoldPos:match("^.-,.-,(.+)$")) or 0)*-1
|
||||
ply.AdvDupe2.HeadEnt.Z = z
|
||||
ply.AdvDupe2.HeadEnt.Pos.Z = ply.AdvDupe2.HeadEnt.Pos.Z + z
|
||||
local Pos
|
||||
local Ang
|
||||
for k,v in pairs(dupe["Entities"])do
|
||||
Pos = nil
|
||||
Ang = nil
|
||||
if(v.SavedParentIdx)then
|
||||
if(not v.BuildDupeInfo)then v.BuildDupeInfo = {} end
|
||||
v.BuildDupeInfo.DupeParentID = v.SavedParentIdx
|
||||
Pos = v.LocalPos*1
|
||||
Ang = v.LocalAngle*1
|
||||
end
|
||||
for i,p in pairs(v.PhysicsObjects)do
|
||||
p.Pos = Pos or (p.LocalPos*1)
|
||||
p.Pos.Z = p.Pos.Z - z
|
||||
p.Angle = Ang or (p.LocalAngle*1)
|
||||
p.LocalPos = nil
|
||||
p.LocalAngle = nil
|
||||
p.Frozen = not p.Frozen -- adv dupe 2 does this wrong way
|
||||
end
|
||||
v.LocalPos = nil
|
||||
v.LocalAngle = nil
|
||||
end
|
||||
|
||||
ply.AdvDupe2.Entities = dupe["Entities"]
|
||||
ply.AdvDupe2.Constraints = dupe["Constraints"]
|
||||
|
||||
else
|
||||
ply.AdvDupe2.Entities = dupe["Entities"]
|
||||
ply.AdvDupe2.Constraints = dupe["Constraints"]
|
||||
ply.AdvDupe2.HeadEnt = dupe["HeadEnt"]
|
||||
end
|
||||
AdvDupe2.ResetOffsets(ply, true)
|
||||
end
|
||||
|
||||
local function AdvDupe2_ReceiveFile(len, ply)
|
||||
if not IsValid(ply) then return end
|
||||
if not ply.AdvDupe2 then ply.AdvDupe2 = {} end
|
||||
|
||||
ply.AdvDupe2.Name = string.match(net.ReadString(), "([%w_ ]+)") or "Advanced Duplication"
|
||||
|
||||
local stream = net.ReadStream(ply, function(data)
|
||||
if data then
|
||||
AdvDupe2.LoadDupe(ply, AdvDupe2.Decode(data))
|
||||
else
|
||||
AdvDupe2.Notify(ply, "Duplicator Upload Failed!", NOTIFY_ERROR, 5)
|
||||
end
|
||||
ply.AdvDupe2.Uploading = false
|
||||
end)
|
||||
|
||||
if ply.AdvDupe2.Uploading then
|
||||
if stream then
|
||||
stream:Remove()
|
||||
end
|
||||
AdvDupe2.Notify(ply, "Duplicator is Busy!", NOTIFY_ERROR, 5)
|
||||
elseif stream then
|
||||
ply.AdvDupe2.Uploading = true
|
||||
AdvDupe2.InitProgressBar(ply, "Uploading: ")
|
||||
end
|
||||
end
|
||||
net.Receive("AdvDupe2_ReceiveFile", AdvDupe2_ReceiveFile)
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
util.AddNetworkString("AdvDupe2_SendGhosts")
|
||||
util.AddNetworkString("AdvDupe2_AddGhost")
|
||||
|
||||
function AdvDupe2.SendGhost(ply, AddOne)
|
||||
net.Start("AdvDupe2_AddGhost")
|
||||
net.WriteString(AddOne.Model)
|
||||
net.WriteInt(#AddOne.PhysicsObjects, 8)
|
||||
for i=0, #AddOne.PhysicsObjects do
|
||||
net.WriteAngle(AddOne.PhysicsObjects[i].Angle)
|
||||
net.WriteVector(AddOne.PhysicsObjects[i].Pos)
|
||||
end
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
function AdvDupe2.SendGhosts(ply)
|
||||
if(not ply.AdvDupe2.Entities)then return end
|
||||
|
||||
local cache = {}
|
||||
local temp = {}
|
||||
local mdls = {}
|
||||
local cnt = 1
|
||||
local add = true
|
||||
local head
|
||||
|
||||
for k,v in pairs(ply.AdvDupe2.Entities)do
|
||||
temp[cnt] = v
|
||||
for i=1,#cache do
|
||||
if(cache[i]==v.Model)then
|
||||
mdls[cnt] = i
|
||||
add=false
|
||||
break
|
||||
end
|
||||
end
|
||||
if(add)then
|
||||
mdls[cnt] = table.insert(cache, v.Model)
|
||||
else
|
||||
add = true
|
||||
end
|
||||
if(k==ply.AdvDupe2.HeadEnt.Index)then
|
||||
head = cnt
|
||||
end
|
||||
cnt = cnt+1
|
||||
end
|
||||
|
||||
if(!head)then
|
||||
AdvDupe2.Notify(ply, "Invalid head entity for ghosts.", NOTIFY_ERROR);
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("AdvDupe2_SendGhosts")
|
||||
net.WriteInt(head, 16)
|
||||
net.WriteFloat(ply.AdvDupe2.HeadEnt.Z)
|
||||
net.WriteVector(ply.AdvDupe2.HeadEnt.Pos)
|
||||
net.WriteInt(#cache, 16)
|
||||
for i=1,#cache do
|
||||
net.WriteString(cache[i])
|
||||
end
|
||||
net.WriteInt(cnt-1, 16)
|
||||
for i=1, #temp do
|
||||
net.WriteInt(mdls[i], 16)
|
||||
net.WriteInt(#temp[i].PhysicsObjects, 8)
|
||||
for k=0, #temp[i].PhysicsObjects do
|
||||
net.WriteAngle(temp[i].PhysicsObjects[k].Angle)
|
||||
net.WriteVector(temp[i].PhysicsObjects[k].Pos)
|
||||
end
|
||||
end
|
||||
net.Send(ply)
|
||||
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
--[[
|
||||
Title: Miscellaneous
|
||||
|
||||
Desc: Contains miscellaneous (serverside) things AD2 needs to function that don't fit anywhere else.
|
||||
|
||||
Author: TB
|
||||
|
||||
Version: 1.0
|
||||
]]
|
||||
|
||||
--[[
|
||||
Name: SavePositions
|
||||
Desc: Save the position of the entities to prevent sagging on dupe.
|
||||
Params: <entity> Constraint
|
||||
Returns: nil
|
||||
]]
|
||||
|
||||
local function SavePositions( Constraint )
|
||||
|
||||
if IsValid(Constraint) then
|
||||
|
||||
if Constraint.BuildDupeInfo then return end
|
||||
local BuildDupeInfo = {}
|
||||
Constraint.BuildDupeInfo = BuildDupeInfo
|
||||
|
||||
local Ent1, Ent2
|
||||
if IsValid(Constraint.Ent) then
|
||||
if Constraint.Ent:GetPhysicsObjectCount()>1 then
|
||||
BuildDupeInfo.Ent1Ang = Constraint.Ent:GetAngles()
|
||||
else
|
||||
BuildDupeInfo.Ent1Ang = Constraint.Ent:GetPhysicsObject():GetAngles()
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid(Constraint.Ent1) then
|
||||
if Constraint.Ent1:GetPhysicsObjectCount()>1 then
|
||||
local Bone = Constraint.Ent1:GetPhysicsObjectNum(Constraint.Bone1)
|
||||
BuildDupeInfo.Ent1Ang = Constraint.Ent1:GetAngles()
|
||||
BuildDupeInfo.Ent1Pos = Constraint.Ent1:GetPos()
|
||||
BuildDupeInfo.Bone1 = Constraint.Bone1
|
||||
BuildDupeInfo.Bone1Pos = Bone:GetPos() - Constraint.Ent1:GetPos()
|
||||
BuildDupeInfo.Bone1Angle = Bone:GetAngles()
|
||||
else
|
||||
local Bone = Constraint.Ent1:GetPhysicsObject()
|
||||
BuildDupeInfo.Ent1Ang = Bone:GetAngles()
|
||||
BuildDupeInfo.Ent1Pos = Bone:GetPos()
|
||||
end
|
||||
|
||||
if IsValid(Constraint.Ent2) then
|
||||
if Constraint.Ent2:GetPhysicsObjectCount()>1 then
|
||||
local Bone = Constraint.Ent2:GetPhysicsObjectNum(Constraint.Bone2)
|
||||
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Constraint.Ent2:GetPos()
|
||||
BuildDupeInfo.Ent2Ang = Constraint.Ent2:GetAngles()
|
||||
BuildDupeInfo.Bone2 = Constraint.Bone2
|
||||
BuildDupeInfo.Bone2Pos = Bone:GetPos() - Constraint.Ent2:GetPos()
|
||||
BuildDupeInfo.Bone2Angle = Bone:GetAngles()
|
||||
else
|
||||
local Bone = Constraint.Ent2:GetPhysicsObject()
|
||||
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Bone:GetPos()
|
||||
BuildDupeInfo.Ent2Ang = Bone:GetAngles()
|
||||
end
|
||||
elseif IsValid(Constraint.Ent4) then
|
||||
if Constraint.Ent4:GetPhysicsObjectCount()>1 then
|
||||
local Bone = Constraint.Ent4:GetPhysicsObjectNum(Constraint.Bone4)
|
||||
BuildDupeInfo.Bone2 = Constraint.Bone4
|
||||
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Constraint.Ent4:GetPos()
|
||||
BuildDupeInfo.Ent2Ang = Constraint.Ent4:GetAngles()
|
||||
BuildDupeInfo.Bone2Pos = Bone:GetPos() - Constraint.Ent4:GetPos()
|
||||
BuildDupeInfo.Bone2Angle = Bone:GetAngles()
|
||||
else
|
||||
local Bone = Constraint.Ent4:GetPhysicsObject()
|
||||
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Bone:GetPos()
|
||||
BuildDupeInfo.Ent2Ang = Bone:GetAngles()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local function monitorConstraint(name)
|
||||
local oldFunc = constraint[name]
|
||||
constraint[name] = function(...)
|
||||
local Constraint, b, c = oldFunc(...)
|
||||
if Constraint and Constraint:IsValid() then
|
||||
SavePositions(Constraint)
|
||||
end
|
||||
return Constraint, b, c
|
||||
end
|
||||
end
|
||||
monitorConstraint("AdvBallsocket")
|
||||
monitorConstraint("Axis")
|
||||
monitorConstraint("Ballsocket")
|
||||
monitorConstraint("Elastic")
|
||||
monitorConstraint("Hydraulic")
|
||||
monitorConstraint("Keepupright")
|
||||
monitorConstraint("Motor")
|
||||
monitorConstraint("Muscle")
|
||||
monitorConstraint("Pulley")
|
||||
monitorConstraint("Rope")
|
||||
monitorConstraint("Slider")
|
||||
monitorConstraint("Weld")
|
||||
monitorConstraint("Winch")
|
||||
@@ -0,0 +1,145 @@
|
||||
include("shared.lua")
|
||||
|
||||
surface.CreateFont( "InfoRUS2", { font = "Enhanced Dot Digital-7", extended = true, size = 90, weight = 800, antialias = true })
|
||||
surface.CreateFont( "InfoRUS3", { font = "Enhanced Dot Digital-7", extended = true, size = 50, weight = 800, antialias = true })
|
||||
|
||||
local font = "InfoRUS2"
|
||||
|
||||
local sizetable = {
|
||||
[3] = {350, 0.5},
|
||||
[4] = {470, -11.5},
|
||||
[5] = {590, -11.5},
|
||||
[6] = {710, 0.5},
|
||||
[7] = {830, 0.5},
|
||||
[8] = {950, 0.5},
|
||||
}
|
||||
|
||||
function ENT:Initialize()
|
||||
|
||||
self.OldWide = self:GetWide()
|
||||
|
||||
self.frame = vgui.Create( "DPanel" )
|
||||
self.frame:SetSize( sizetable[self:GetWide()][1], 120 )
|
||||
self.frame.Text = self:GetText()
|
||||
self.frame.Type = self:GetType()
|
||||
self.frame.col = self:GetTColor()
|
||||
self.frame.damage = 0
|
||||
self.frame.appr = nil
|
||||
self.frame.FX = self:GetFX()
|
||||
self.frame.On = self:GetOn()
|
||||
self.frame.alfa = 0
|
||||
self.frame.speed = self:GetSpeed()
|
||||
self.frame:SetPaintedManually( true )
|
||||
self.frame.Paint = function(self,w,h)
|
||||
|
||||
if self.On <= 0 then
|
||||
if self.alfa < 1 then return end
|
||||
self.alfa = Lerp(FrameTime() * 5,self.alfa,0)
|
||||
else
|
||||
if self.FX > 0 then
|
||||
self.alfa = math.random(100,220)
|
||||
else
|
||||
self.alfa = 255
|
||||
end
|
||||
end
|
||||
|
||||
surface.DisableClipping( false )
|
||||
surface.SetFont(font)
|
||||
local ww,hh = surface.GetTextSize(self.Text)
|
||||
local multiplier = self.speed * 100
|
||||
|
||||
self.static = false
|
||||
|
||||
if self.damage < CurTime() and self.On then
|
||||
if self.Type == 1 then
|
||||
|
||||
local xs = (math.fmod(SysTime() * multiplier,w+ww)) - ww
|
||||
|
||||
draw.DrawText(self.Text,font,xs,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),0)
|
||||
elseif self.Type == 2 then
|
||||
|
||||
if !self.appr or self.appr > ww then
|
||||
self.appr = -w
|
||||
else
|
||||
self.appr = math.Approach(self.appr, ww+w, FrameTime() * multiplier)
|
||||
end
|
||||
|
||||
draw.DrawText(self.Text,font,self.appr * -1,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),0)
|
||||
else
|
||||
if !self.appr then
|
||||
self.appr = 0
|
||||
end
|
||||
|
||||
if w > ww then
|
||||
if self.Type == 3 then
|
||||
if self.appr < w-ww and !self.refl then
|
||||
self.appr = math.Approach(self.appr, ww+w, FrameTime() * multiplier)
|
||||
else
|
||||
if self.appr <= 0 then
|
||||
self.refl = nil
|
||||
else
|
||||
self.refl = true
|
||||
self.appr = math.Approach(self.appr, 0, FrameTime() * multiplier)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.static = true
|
||||
end
|
||||
else
|
||||
if self.appr > w-ww-50 and !self.refl then
|
||||
self.appr = math.Approach(self.appr, w-ww-50, FrameTime() * multiplier)
|
||||
else
|
||||
if self.appr >= 50 then
|
||||
self.refl = nil
|
||||
else
|
||||
self.refl = true
|
||||
self.appr = math.Approach(self.appr, 50, FrameTime() * multiplier)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.static then
|
||||
draw.DrawText(self.Text,font,w/2,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),1)
|
||||
else
|
||||
draw.DrawText(self.Text,font,self.appr,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),0)
|
||||
end
|
||||
end
|
||||
else
|
||||
draw.DrawText(self.Text,font,math.random(0,w-ww),10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, math.random(0,255)),0)
|
||||
end
|
||||
surface.DisableClipping( true )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
|
||||
self:DrawModel()
|
||||
|
||||
if self.frame then
|
||||
self.frame.Text = self:GetText()
|
||||
self.frame.Type = self:GetType()
|
||||
self.frame.col = self:GetTColor()
|
||||
self.frame.FX = self:GetFX()
|
||||
self.frame.On = self:GetOn()
|
||||
self.frame.damage = self:GetNWInt("LastDamaged")
|
||||
self.frame.speed = self:GetSpeed()
|
||||
end
|
||||
|
||||
local Pos = self:GetPos()
|
||||
local Ang = self:GetAngles()
|
||||
local hight = 12
|
||||
|
||||
if self.OldWide != self:GetWide() then
|
||||
self.frame:SetSize( sizetable[self:GetWide()][1], 120 )
|
||||
self.OldWide = self:GetWide()
|
||||
end
|
||||
|
||||
if self:GetWide() == 3 then
|
||||
hight = 6
|
||||
end
|
||||
|
||||
cam.Start3D2D(Pos + Ang:Up() * 1.1 - Ang:Right() * hight + Ang:Forward() * sizetable[self:GetWide()][2], Ang, 0.1)
|
||||
self.frame:PaintManual()
|
||||
cam.End3D2D()
|
||||
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
AddCSLuaFile("cl_init.lua")
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
util.AddNetworkString("LEDDamaged")
|
||||
|
||||
function ENT:Initialize()
|
||||
|
||||
self:SetText("Made by Mac with <3")
|
||||
self:SetTColor(Vector(2.55,2,0))
|
||||
self:SetType(1)
|
||||
self:SetSpeed(1.5)
|
||||
self:SetWide(6)
|
||||
self:SetFX(1)
|
||||
self:SetOn(1)
|
||||
|
||||
self:SetModel("models/squad/sf_plates/sf_plate1x"..self:GetWide()..".mdl")
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMaterial("phoenix_storms/mat/mat_phx_carbonfiber")
|
||||
self:SetColor(Color(0,0,0))
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self:SetNWInt("LastDamaged", 0)
|
||||
local phys = self:GetPhysicsObject()
|
||||
self.nodupe = true
|
||||
self.ShareGravgun = true
|
||||
|
||||
phys:Wake()
|
||||
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
|
||||
if self:GetModel() != "models/squad/sf_plates/sf_plate1x"..self:GetWide()..".mdl" then
|
||||
self:SetModel("models/squad/sf_plates/sf_plate1x"..self:GetWide()..".mdl")
|
||||
end
|
||||
|
||||
self:NextThink( CurTime() + 1 )
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage(data)
|
||||
|
||||
if data:GetDamagePosition() then
|
||||
self:EmitSound("ambient/energy/spark".. math.random(1,6) ..".wav")
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( data:GetDamagePosition() )
|
||||
util.Effect( "StunstickImpact", effectdata )
|
||||
self:SetNWInt("LastDamaged", math.Round(CurTime()+2))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_gmodentity"
|
||||
ENT.PrintName = "Spawned Sign"
|
||||
ENT.Author = "Mac"
|
||||
ENT.Spawnable = false
|
||||
ENT.AdminSpawnable = true
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("String", 0, "Text")
|
||||
self:NetworkVar("Vector", 0, "TColor")
|
||||
self:NetworkVar("Int", 0, "Type")
|
||||
self:NetworkVar("Int", 1, "Speed")
|
||||
self:NetworkVar("Int", 2, "Wide")
|
||||
self:NetworkVar("Int", 3, "On")
|
||||
self:NetworkVar("Int", 4, "FX")
|
||||
end
|
||||
@@ -0,0 +1,142 @@
|
||||
include("shared.lua")
|
||||
|
||||
local font = "InfoRUS2"
|
||||
|
||||
local sizetable = {
|
||||
[3] = {350, 0.5},
|
||||
[4] = {470, -11.5},
|
||||
[5] = {590, -11.5},
|
||||
[6] = {710, 0.5},
|
||||
[7] = {830, 0.5},
|
||||
[8] = {950, 0.5},
|
||||
}
|
||||
|
||||
function ENT:Initialize()
|
||||
|
||||
self.OldWide = self:GetWide()
|
||||
|
||||
self.frame = vgui.Create( "DPanel" )
|
||||
self.frame:SetSize( sizetable[self:GetWide()][1], 120 )
|
||||
self.frame.Text = self:GetText()
|
||||
self.frame.Type = self:GetType()
|
||||
self.frame.col = self:GetTColor()
|
||||
self.frame.damage = 0
|
||||
self.frame.appr = nil
|
||||
self.frame.FX = self:GetFX()
|
||||
self.frame.On = self:GetOn()
|
||||
self.frame.alfa = 0
|
||||
self.frame.speed = self:GetSpeed()
|
||||
self.frame:SetPaintedManually( true )
|
||||
self.frame.Paint = function(self,w,h)
|
||||
|
||||
if self.On <= 0 then
|
||||
if self.alfa < 1 then return end
|
||||
self.alfa = Lerp(FrameTime() * 5,self.alfa,0)
|
||||
else
|
||||
if self.FX > 0 then
|
||||
self.alfa = math.random(100,220)
|
||||
else
|
||||
self.alfa = 255
|
||||
end
|
||||
end
|
||||
|
||||
surface.DisableClipping( false )
|
||||
surface.SetFont(font)
|
||||
local ww,hh = surface.GetTextSize(self.Text)
|
||||
local multiplier = self.speed * 100
|
||||
|
||||
self.static = false
|
||||
|
||||
if self.damage < CurTime() and self.On then
|
||||
if self.Type == 1 then
|
||||
|
||||
local xs = (math.fmod(SysTime() * multiplier,w+ww)) - ww
|
||||
|
||||
draw.DrawText(self.Text,font,xs,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),0)
|
||||
elseif self.Type == 2 then
|
||||
|
||||
if !self.appr or self.appr > ww then
|
||||
self.appr = -w
|
||||
else
|
||||
self.appr = math.Approach(self.appr, ww+w, FrameTime() * multiplier)
|
||||
end
|
||||
|
||||
draw.DrawText(self.Text,font,self.appr * -1,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),0)
|
||||
else
|
||||
if !self.appr then
|
||||
self.appr = 0
|
||||
end
|
||||
|
||||
if w > ww then
|
||||
if self.Type == 3 then
|
||||
if self.appr < w-ww and !self.refl then
|
||||
self.appr = math.Approach(self.appr, ww+w, FrameTime() * multiplier)
|
||||
else
|
||||
if self.appr <= 0 then
|
||||
self.refl = nil
|
||||
else
|
||||
self.refl = true
|
||||
self.appr = math.Approach(self.appr, 0, FrameTime() * multiplier)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.static = true
|
||||
end
|
||||
else
|
||||
if self.appr > w-ww-50 and !self.refl then
|
||||
self.appr = math.Approach(self.appr, w-ww-50, FrameTime() * multiplier)
|
||||
else
|
||||
if self.appr >= 50 then
|
||||
self.refl = nil
|
||||
else
|
||||
self.refl = true
|
||||
self.appr = math.Approach(self.appr, 50, FrameTime() * multiplier)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.static then
|
||||
draw.DrawText(self.Text,font,w/2,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),1)
|
||||
else
|
||||
draw.DrawText(self.Text,font,self.appr,10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, self.alfa),0)
|
||||
end
|
||||
end
|
||||
else
|
||||
draw.DrawText(self.Text,font,math.random(0,w-ww),10,Color(self.col.x * 100, self.col.y * 100, self.col.z * 100, math.random(0,255)),0)
|
||||
end
|
||||
surface.DisableClipping( true )
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
|
||||
self:DrawModel()
|
||||
|
||||
if self.frame then
|
||||
self.frame.Text = self:GetText()
|
||||
self.frame.Type = self:GetType()
|
||||
self.frame.col = self:GetTColor()
|
||||
self.frame.FX = self:GetFX()
|
||||
self.frame.On = self:GetOn()
|
||||
self.frame.damage = self:GetNWInt("LastDamaged")
|
||||
self.frame.speed = self:GetSpeed()
|
||||
end
|
||||
|
||||
local Pos = self:GetPos()
|
||||
local Ang = self:GetAngles()
|
||||
local hight = 12
|
||||
|
||||
if self.OldWide != self:GetWide() then
|
||||
self.frame:SetSize( sizetable[self:GetWide()][1], 120 )
|
||||
self.OldWide = self:GetWide()
|
||||
end
|
||||
|
||||
if self:GetWide() == 3 then
|
||||
hight = 6
|
||||
end
|
||||
|
||||
cam.Start3D2D(Pos + Ang:Up() * 1.1 - Ang:Right() * hight + Ang:Forward() * sizetable[self:GetWide()][2], Ang, 0.1)
|
||||
self.frame:PaintManual()
|
||||
cam.End3D2D()
|
||||
|
||||
end
|
||||
@@ -0,0 +1,70 @@
|
||||
AddCSLuaFile("cl_init.lua")
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
util.AddNetworkString("LEDDamaged")
|
||||
|
||||
function ENT:Initialize()
|
||||
|
||||
self:SetText("Made by Mac with Wire and <3")
|
||||
self:SetTColor(Vector(2.55,2,0))
|
||||
self:SetType(1)
|
||||
self:SetSpeed(1.5)
|
||||
self:SetWide(6)
|
||||
self:SetFX(1)
|
||||
self:SetOn(1)
|
||||
|
||||
self:SetModel("models/squad/sf_plates/sf_plate1x"..self:GetWide()..".mdl")
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMaterial("phoenix_storms/mat/mat_phx_carbonfiber")
|
||||
self:SetColor(Color(0,0,0))
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self:SetNWInt("LastDamaged", 0)
|
||||
local phys = self:GetPhysicsObject()
|
||||
self.nodupe = true
|
||||
self.ShareGravgun = true
|
||||
|
||||
phys:Wake()
|
||||
|
||||
|
||||
self.Inputs = WireLib.CreateSpecialInputs(self, { "Text", "Type", "Speed", "Color", "Flicker", "On" }, { "STRING", "NORMAL", "NORMAL", "VECTOR", "NORMAL", "NORMAL" })
|
||||
|
||||
end
|
||||
|
||||
function ENT:TriggerInput(iname, value)
|
||||
if iname == "Text" then
|
||||
self:SetText(value)
|
||||
elseif iname == "Type" then
|
||||
self:SetType(math.Clamp(math.Round(value), 1, 4))
|
||||
elseif iname == "Speed" then
|
||||
self:SetSpeed(math.Clamp(value, 1, 10))
|
||||
elseif iname == "Color" then
|
||||
self:SetTColor(Vector(value.x/100, value.y/100, value.z/100))
|
||||
elseif iname == "On" then
|
||||
self:SetOn(value)
|
||||
elseif iname == "Flicker" then
|
||||
self:SetFX(math.Clamp(value, 0, 1))
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
|
||||
if self:GetModel() != "models/squad/sf_plates/sf_plate1x"..self:GetWide()..".mdl" then
|
||||
self:SetModel("models/squad/sf_plates/sf_plate1x"..self:GetWide()..".mdl")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage(data)
|
||||
|
||||
if data:GetDamagePosition() then
|
||||
self:EmitSound("ambient/energy/spark".. math.random(1,6) ..".wav")
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( data:GetDamagePosition() )
|
||||
util.Effect( "StunstickImpact", effectdata )
|
||||
self:SetNWInt("LastDamaged", math.Round(CurTime()+2))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_wire_entity"
|
||||
ENT.PrintName = "Spawned Sign Wire"
|
||||
ENT.WireDebugName = "LED Screen"
|
||||
ENT.Author = "Mac"
|
||||
ENT.Spawnable = false
|
||||
ENT.AdminSpawnable = true
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("String", 0, "Text")
|
||||
self:NetworkVar("Vector", 0, "TColor")
|
||||
self:NetworkVar("Int", 0, "Type")
|
||||
self:NetworkVar("Int", 1, "Speed")
|
||||
self:NetworkVar("Int", 2, "Wide")
|
||||
self:NetworkVar("Int", 3, "On")
|
||||
self:NetworkVar("Int", 4, "FX")
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
include( "shared.lua" )
|
||||
|
||||
function ENT:Draw()
|
||||
self.BaseClass.Draw(self)
|
||||
self.Entity:DrawModel()
|
||||
end
|
||||
@@ -0,0 +1,291 @@
|
||||
--[[
|
||||
Title: Adv. Dupe 2 Contraption Spawner
|
||||
|
||||
Desc: A mobile duplicator
|
||||
|
||||
Author: TB
|
||||
|
||||
Version: 1.0
|
||||
]]
|
||||
|
||||
AddCSLuaFile( "cl_init.lua" )
|
||||
AddCSLuaFile( "shared.lua" )
|
||||
if(WireLib)then
|
||||
include( "entities/base_wire_entity.lua" )
|
||||
end
|
||||
|
||||
include( "shared.lua" )
|
||||
|
||||
function ENT:Initialize()
|
||||
|
||||
self.Entity:SetMoveType( MOVETYPE_NONE )
|
||||
self.Entity:PhysicsInit( SOLID_VPHYSICS )
|
||||
self.Entity:SetCollisionGroup( COLLISION_GROUP_WORLD )
|
||||
self.Entity:DrawShadow( false )
|
||||
|
||||
local phys = self.Entity:GetPhysicsObject()
|
||||
if phys:IsValid() then
|
||||
phys:Wake()
|
||||
end
|
||||
|
||||
self.UndoList = {}
|
||||
self.Ghosts = {}
|
||||
|
||||
self.SpawnLastValue = 0
|
||||
self.UndoLastValue = 0
|
||||
|
||||
self.LastSpawnTime = 0
|
||||
self.DupeName = ""
|
||||
self.CurrentPropCount = 0
|
||||
|
||||
if WireLib then
|
||||
self.Inputs = Wire_CreateInputs(self.Entity, {"Spawn", "Undo"})
|
||||
self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, {"Out"}, { "NORMAL" })
|
||||
end
|
||||
end
|
||||
|
||||
/*-----------------------------------------------------------------------*
|
||||
* Sets options for this spawner
|
||||
*-----------------------------------------------------------------------*/
|
||||
function ENT:SetOptions(ply, delay, undo_delay, key, undo_key, disgrav, disdrag, addvel, hideprops )
|
||||
|
||||
self.delay = delay
|
||||
self.undo_delay = undo_delay
|
||||
|
||||
--Key bindings
|
||||
self.key = key
|
||||
self.undo_key = undo_key
|
||||
numpad.Remove( self.CreateKey )
|
||||
numpad.Remove( self.UndoKey )
|
||||
self.CreateKey = numpad.OnDown( ply, self.key , "ContrSpawnerCreate", self.Entity, true )
|
||||
self.UndoKey = numpad.OnDown( ply, self.undo_key, "ContrSpawnerUndo" , self.Entity, true )
|
||||
|
||||
-- Other parameters
|
||||
self.DisableGravity = disgrav
|
||||
self.DisableDrag = disdrag
|
||||
self.AddVelocity = addvel
|
||||
self.HideProps = hideprops
|
||||
|
||||
-- Store the player's current dupe name
|
||||
self.DupeName = tostring(ply.AdvDupe2.Name)
|
||||
|
||||
self:ShowOutput()
|
||||
end
|
||||
|
||||
function ENT:UpdateOptions( options )
|
||||
self:SetOptions( options["delay"], options["undo_delay"], options["key"], options["undo_key"])
|
||||
end
|
||||
|
||||
function ENT:AddGhosts()
|
||||
if self.HideProps then return end
|
||||
local moveable = self:GetPhysicsObject():IsMoveable()
|
||||
self:GetPhysicsObject():EnableMotion(false)
|
||||
local EntTable, GhostEntity, Phys
|
||||
local Offset = self.DupeAngle - self.EntAngle
|
||||
for EntIndex,v in pairs(self.EntityTable)do
|
||||
if(EntIndex!=self.HeadEnt)then
|
||||
if(self.EntityTable[EntIndex].Class=="gmod_contr_spawner")then self.EntityTable[EntIndex] = nil continue end
|
||||
EntTable = table.Copy(self.EntityTable[EntIndex])
|
||||
if(EntTable.BuildDupeInfo && EntTable.BuildDupeInfo.PhysicsObjects)then
|
||||
Phys = EntTable.BuildDupeInfo.PhysicsObjects[0]
|
||||
else
|
||||
if(!v.BuildDupeInfo)then v.BuildDupeInfo = {} end
|
||||
v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects)
|
||||
Phys = EntTable.PhysicsObjects[0]
|
||||
end
|
||||
|
||||
GhostEntity = nil
|
||||
|
||||
if(EntTable.Model==nil || !util.IsValidModel(EntTable.Model)) then EntTable.Model="models/error.mdl" end
|
||||
|
||||
if ( EntTable.Model:sub( 1, 1 ) == "*" ) then
|
||||
GhostEntity = ents.Create( "func_physbox" )
|
||||
else
|
||||
GhostEntity = ents.Create( "gmod_ghost" )
|
||||
end
|
||||
|
||||
// If there are too many entities we might not spawn..
|
||||
if ( !GhostEntity || GhostEntity == NULL ) then return end
|
||||
|
||||
duplicator.DoGeneric( GhostEntity, EntTable )
|
||||
|
||||
GhostEntity:Spawn()
|
||||
|
||||
GhostEntity:DrawShadow( false )
|
||||
GhostEntity:SetMoveType( MOVETYPE_NONE )
|
||||
GhostEntity:SetSolid( SOLID_VPHYSICS );
|
||||
GhostEntity:SetNotSolid( true )
|
||||
GhostEntity:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
GhostEntity:SetColor( Color(255, 255, 255, 150) )
|
||||
|
||||
GhostEntity:SetAngles(Phys.Angle)
|
||||
GhostEntity:SetPos(self:GetPos() + Phys.Pos - self.Offset)
|
||||
self:SetAngles(self.EntAngle)
|
||||
GhostEntity:SetParent( self )
|
||||
self:SetAngles(self.DupeAngle)
|
||||
self.Ghosts[EntIndex] = GhostEntity
|
||||
end
|
||||
end
|
||||
self:SetAngles(self.DupeAngle)
|
||||
self:GetPhysicsObject():EnableMotion(moveable)
|
||||
end
|
||||
|
||||
function ENT:GetCreationDelay() return self.delay end
|
||||
function ENT:GetDeletionDelay() return self.undo_delay end
|
||||
|
||||
function ENT:OnTakeDamage( dmginfo ) self.Entity:TakePhysicsDamage( dmginfo ) end
|
||||
|
||||
function ENT:SetDupeInfo( HeadEnt, EntityTable, ConstraintTable )
|
||||
self.HeadEnt = HeadEnt
|
||||
self.EntityTable = EntityTable
|
||||
self.ConstraintTable = ConstraintTable
|
||||
if(!self.DupeAngle)then self.DupeAngle = self:GetAngles() end
|
||||
if(!self.EntAngle)then self.EntAngle = EntityTable[HeadEnt].PhysicsObjects[0].Angle end
|
||||
if(!self.Offset)then self.Offset = self.EntityTable[HeadEnt].PhysicsObjects[0].Pos end
|
||||
|
||||
local headpos, headang = EntityTable[HeadEnt].PhysicsObjects[0].Pos, EntityTable[HeadEnt].PhysicsObjects[0].Angle
|
||||
for k, v in pairs(EntityTable) do
|
||||
for o, p in pairs(v.PhysicsObjects) do
|
||||
p.LPos, p.LAngle = WorldToLocal(p.Pos, p.Angle, headpos, headang)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:DoSpawn( ply )
|
||||
-- Explicitly allow spawning if no player is provided, but an invalid player gets denied. This can happen when a player leaves the server.
|
||||
if not (ply and ply:IsValid()) then return end
|
||||
|
||||
for k, v in pairs(self.EntityTable) do
|
||||
for o, p in pairs(v.PhysicsObjects) do
|
||||
p.Pos, p.Angle = self:LocalToWorld(p.LPos), self:LocalToWorldAngles(p.LAngle)
|
||||
end
|
||||
end
|
||||
|
||||
/*local AngleOffset = self.EntAngle
|
||||
AngleOffset = self:GetAngles() - AngleOffset
|
||||
local AngleOffset2 = Angle(0,0,0)
|
||||
//AngleOffset2.y = AngleOffset.y
|
||||
AngleOffset2:RotateAroundAxis(self:GetUp(), AngleOffset.y)
|
||||
AngleOffset2:RotateAroundAxis(self:GetRight(),AngleOffset.p)
|
||||
AngleOffset2:RotateAroundAxis(self:GetForward(),AngleOffset.r)*/
|
||||
|
||||
local Ents, Constrs = AdvDupe2.duplicator.Paste(ply, self.EntityTable, self.ConstraintTable, nil, nil, Vector(0,0,0), true)
|
||||
local i = #self.UndoList+1
|
||||
self.UndoList[i] = Ents
|
||||
|
||||
local undotxt = "AdvDupe2: Contraption ("..tostring(self.DupeName)..")"
|
||||
|
||||
undo.Create(undotxt)
|
||||
local phys
|
||||
for k,ent in pairs(Ents)do
|
||||
phys = ent:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:Wake()
|
||||
if(self.DisableGravity==1)then phys:EnableGravity(false) end
|
||||
if(self.DisableDrag==1)then phys:EnableDrag(false) end
|
||||
phys:EnableMotion(true)
|
||||
if(ent.SetForce)then ent.SetForce(ent, ent.force, ent.mul) end
|
||||
if(self.AddVelocity==1)then
|
||||
phys:SetVelocity( self:GetVelocity() )
|
||||
phys:AddAngleVelocity( self:GetPhysicsObject():GetAngleVelocity() )
|
||||
end
|
||||
end
|
||||
|
||||
undo.AddEntity(ent)
|
||||
end
|
||||
|
||||
undo.SetPlayer(ply)
|
||||
undo.Finish()
|
||||
|
||||
if(self.undo_delay>0)then
|
||||
timer.Simple(self.undo_delay, function()
|
||||
if(self.UndoList && self.UndoList[i])then
|
||||
for k,ent in pairs(self.UndoList[i]) do
|
||||
if(IsValid(ent)) then
|
||||
ent:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:DoUndo( ply )
|
||||
|
||||
if(!self.UndoList || #self.UndoList == 0)then return end
|
||||
|
||||
local entities = self.UndoList[ #self.UndoList ]
|
||||
self.UndoList[ #self.UndoList ] = nil
|
||||
for _,ent in pairs(entities) do
|
||||
if (IsValid(ent)) then
|
||||
ent:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:TriggerInput(iname, value)
|
||||
local ply = self:GetPlayer()
|
||||
|
||||
if(iname == "Spawn")then
|
||||
if ((value > 0) == self.SpawnLastValue) then return end
|
||||
self.SpawnLastValue = (value > 0)
|
||||
|
||||
if(self.SpawnLastValue)then
|
||||
local delay = self:GetCreationDelay()
|
||||
if (delay == 0) then self:DoSpawn( ply ) return end
|
||||
if(CurTime() < self.LastSpawnTime)then return end
|
||||
self:DoSpawn( ply )
|
||||
self.LastSpawnTime=CurTime()+delay
|
||||
end
|
||||
elseif (iname == "Undo") then
|
||||
// Same here
|
||||
if((value > 0) == self.UndoLastValue)then return end
|
||||
self.UndoLastValue = (value > 0)
|
||||
|
||||
if(self.UndoLastValue)then self:DoUndo(ply) end
|
||||
end
|
||||
end
|
||||
|
||||
local flags = {"Enabled", "Disabled"}
|
||||
function ENT:ShowOutput()
|
||||
local text = "\nGravity: "..((self.DisableGravity == 1) and flags[1] or flags[2])
|
||||
text = text.."\nDrag: " ..((self.DisableDrag == 1) and flags[1] or flags[2])
|
||||
text = text.."\nVelocity: "..((self.AddVelocity == 1) and flags[1] or flags[2])
|
||||
|
||||
self.Entity:SetOverlayText(
|
||||
"Spawn Name: " .. tostring(self.DupeName) ..
|
||||
"\nSpawn Delay: " .. tostring(self:GetCreationDelay()) ..
|
||||
"\nUndo Delay: ".. tostring(self:GetDeletionDelay()) ..
|
||||
text
|
||||
)
|
||||
end
|
||||
|
||||
/*-----------------------------------------------------------------------*
|
||||
* Handler for spawn keypad input
|
||||
*-----------------------------------------------------------------------*/
|
||||
function SpawnContrSpawner( ply, ent )
|
||||
|
||||
if (!ent || !ent:IsValid()) then return end
|
||||
|
||||
local delay = ent:GetTable():GetCreationDelay()
|
||||
|
||||
if(delay == 0) then
|
||||
ent:DoSpawn( ply )
|
||||
return
|
||||
end
|
||||
|
||||
if(CurTime() < ent.LastSpawnTime)then return end
|
||||
ent:DoSpawn( ply )
|
||||
ent.LastSpawnTime=CurTime()+delay
|
||||
end
|
||||
|
||||
/*-----------------------------------------------------------------------*
|
||||
* Handler for undo keypad input
|
||||
*-----------------------------------------------------------------------*/
|
||||
function UndoContrSpawner( ply, ent )
|
||||
if (!ent || !ent:IsValid()) then return end
|
||||
ent:DoUndo( ply, true )
|
||||
end
|
||||
|
||||
numpad.Register( "ContrSpawnerCreate", SpawnContrSpawner )
|
||||
numpad.Register( "ContrSpawnerUndo" , UndoContrSpawner )
|
||||
@@ -0,0 +1,10 @@
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = WireLib and "base_wire_entity" or "base_gmodentity"
|
||||
ENT.PrintName = "Contraption Spawner"
|
||||
ENT.Author = "TB"
|
||||
ENT.Contact = ""
|
||||
ENT.Purpose = ""
|
||||
ENT.Instructions = ""
|
||||
|
||||
ENT.Spawnable = false
|
||||
ENT.AdminSpawnable = false
|
||||
@@ -0,0 +1,141 @@
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
|
||||
ENT.PrintName = ""
|
||||
ENT.Author = ""
|
||||
ENT.Contact = ""
|
||||
ENT.Purpose = ""
|
||||
ENT.Instructions = ""
|
||||
ENT.Spawnable = false
|
||||
ENT.AdminOnly = false
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Initialize
|
||||
-----------------------------------------------------------]]
|
||||
function ENT:Initialize()
|
||||
|
||||
local Radius = 6
|
||||
local min = Vector( 1, 1, 1 ) * Radius * -0.5
|
||||
local max = Vector( 1, 1, 1 ) * Radius * 0.5
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
self.AttachedEntity = ents.Create( "prop_dynamic" )
|
||||
self.AttachedEntity:SetModel( self:GetModel() )
|
||||
self.AttachedEntity:SetAngles( self:GetAngles() )
|
||||
self.AttachedEntity:SetPos( self:GetPos() )
|
||||
self.AttachedEntity:SetSkin( self:GetSkin() )
|
||||
self.AttachedEntity:Spawn()
|
||||
self.AttachedEntity:SetParent( self.Entity )
|
||||
self.AttachedEntity:DrawShadow( false )
|
||||
|
||||
self:SetModel( "models/props_junk/watermelon01.mdl" )
|
||||
|
||||
self:DeleteOnRemove( self.AttachedEntity )
|
||||
|
||||
-- Don't use the model's physics - create a box instead
|
||||
self:PhysicsInitBox( min, max )
|
||||
|
||||
-- Set up our physics object here
|
||||
local phys = self:GetPhysicsObject()
|
||||
if ( IsValid( phys ) ) then
|
||||
phys:Wake()
|
||||
phys:EnableGravity( false )
|
||||
phys:EnableDrag( false )
|
||||
end
|
||||
|
||||
self:DrawShadow( false )
|
||||
self:SetCollisionGroup( COLLISION_GROUP_WEAPON )
|
||||
|
||||
else
|
||||
|
||||
self.GripMaterial = Material( "sprites/grip" )
|
||||
|
||||
-- Get the attached entity so that clientside functions like properties can interact with it
|
||||
local tab = ents.FindByClassAndParent( "prop_dynamic", self )
|
||||
if ( tab && IsValid( tab[ 1 ] ) ) then self.AttachedEntity = tab[ 1 ] end
|
||||
|
||||
end
|
||||
|
||||
-- Set collision bounds exactly
|
||||
self:SetCollisionBounds( min, max )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Draw
|
||||
-----------------------------------------------------------]]
|
||||
function ENT:Draw()
|
||||
|
||||
render.SetMaterial( self.GripMaterial )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: PhysicsUpdate
|
||||
-----------------------------------------------------------]]
|
||||
function ENT:PhysicsUpdate( physobj )
|
||||
|
||||
if ( CLIENT ) then return end
|
||||
|
||||
-- Don't do anything if the player isn't holding us
|
||||
if ( !self:IsPlayerHolding() && !self:IsConstrained() ) then
|
||||
|
||||
physobj:SetVelocity( Vector( 0, 0, 0 ) )
|
||||
physobj:Sleep()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Called after entity 'copy'
|
||||
-----------------------------------------------------------]]
|
||||
function ENT:OnEntityCopyTableFinish( tab )
|
||||
|
||||
-- We need to store the model of the attached entity
|
||||
-- Not the one we have here.
|
||||
tab.Model = self.AttachedEntity:GetModel()
|
||||
|
||||
-- Store the attached entity's table so we can restore it after being pasted
|
||||
tab.AttachedEntityInfo = table.Copy( duplicator.CopyEntTable( self.AttachedEntity ) )
|
||||
tab.AttachedEntityInfo.Pos = nil -- Don't even save angles and position, we are a parented entity
|
||||
tab.AttachedEntityInfo.Angle = nil
|
||||
|
||||
-- Do NOT store the attached entity itself in our table!
|
||||
-- Otherwise, if we copy-paste the prop with the duplicator, its AttachedEntity value will point towards the original prop's attached entity instead, and that'll break stuff
|
||||
tab.AttachedEntity = nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: PostEntityPaste
|
||||
-----------------------------------------------------------]]
|
||||
function ENT:PostEntityPaste( ply )
|
||||
|
||||
-- Restore the attached entity using the information we've saved
|
||||
if ( IsValid( self.AttachedEntity ) ) and ( self.AttachedEntityInfo ) then
|
||||
|
||||
-- Apply skin, bodygroups, bone manipulator, etc.
|
||||
duplicator.DoGeneric( self.AttachedEntity, self.AttachedEntityInfo )
|
||||
|
||||
if ( self.AttachedEntityInfo.EntityMods ) then
|
||||
self.AttachedEntity.EntityMods = table.Copy( self.AttachedEntityInfo.EntityMods )
|
||||
duplicator.ApplyEntityModifiers( ply, self.AttachedEntity )
|
||||
end
|
||||
|
||||
if ( self.AttachedEntityInfo.BoneMods ) then
|
||||
self.AttachedEntity.BoneMods = table.Copy( self.AttachedEntityInfo.BoneMods )
|
||||
duplicator.ApplyBoneModifiers( ply, self.AttachedEntity )
|
||||
end
|
||||
|
||||
self.AttachedEntityInfo = nil
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
386
garrysmod/gamemodes/militaryrp/plugins/buildtools/netstream.lua
Normal file
386
garrysmod/gamemodes/militaryrp/plugins/buildtools/netstream.lua
Normal file
@@ -0,0 +1,386 @@
|
||||
--A net extension which allows sending large streams of data without overflowing the reliable channel
|
||||
--Keep it in lua/autorun so it will be shared between addons
|
||||
AddCSLuaFile()
|
||||
|
||||
net.Stream = {}
|
||||
net.Stream.SendSize = 20000 --This is the size of each packet to send
|
||||
net.Stream.Timeout = 30 --How long to wait for client response before cleaning up
|
||||
net.Stream.MaxWriteStreams = 1024 --The maximum number of write data items to store
|
||||
net.Stream.MaxReadStreams = 128 --The maximum number of queued read data items to store
|
||||
net.Stream.MaxChunks = 3200 --Maximum number of pieces the stream can send to the server. 64 MB
|
||||
net.Stream.MaxSize = net.Stream.SendSize*net.Stream.MaxChunks
|
||||
net.Stream.MaxTries = 3 --Maximum times the client may retry downloading the whole data
|
||||
|
||||
local WriteStreamQueue = {
|
||||
__index = {
|
||||
Add = function(self, stream)
|
||||
local identifier = self.curidentifier
|
||||
local startid = identifier
|
||||
while self.queue[identifier] do
|
||||
identifier = identifier % net.Stream.MaxWriteStreams + 1
|
||||
if identifier == startid then
|
||||
ErrorNoHalt("Netstream is full of WriteStreams!")
|
||||
net.WriteUInt(0, 32)
|
||||
return
|
||||
end
|
||||
end
|
||||
self.curidentifier = identifier % net.Stream.MaxWriteStreams + 1
|
||||
|
||||
if next(self.queue)==nil then
|
||||
self.activitytimeout = CurTime()+net.Stream.Timeout
|
||||
timer.Create("netstream_queueclean", 5, 0, function() self:Clean() end)
|
||||
end
|
||||
self.queue[identifier] = stream
|
||||
stream.identifier = identifier
|
||||
return stream
|
||||
end,
|
||||
|
||||
Write = function(self, ply)
|
||||
local identifier = net.ReadUInt(32)
|
||||
local chunkidx = net.ReadUInt(32)
|
||||
local stream = self.queue[identifier]
|
||||
--print("Got request", identifier, chunkidx, stream)
|
||||
if stream then
|
||||
if stream:Write(ply, chunkidx) then
|
||||
self.activitytimeout = CurTime()+net.Stream.Timeout
|
||||
stream.timeout = CurTime()+net.Stream.Timeout
|
||||
end
|
||||
else
|
||||
-- Tell them the stream doesn't exist
|
||||
net.Start("NetStreamRead")
|
||||
net.WriteUInt(identifier, 32)
|
||||
net.WriteUInt(0, 32)
|
||||
if SERVER then net.Send(ply) else net.SendToServer() end
|
||||
end
|
||||
end,
|
||||
|
||||
Clean = function(self)
|
||||
local t = CurTime()
|
||||
for k, stream in pairs(self.queue) do
|
||||
if (next(stream.clients)~=nil and t >= stream.timeout) or t >= self.activitytimeout then
|
||||
stream:Remove()
|
||||
self.queue[k] = nil
|
||||
end
|
||||
end
|
||||
if next(self.queue)==nil then
|
||||
timer.Remove("netstream_queueclean")
|
||||
end
|
||||
end,
|
||||
},
|
||||
__call = function(t)
|
||||
return setmetatable({
|
||||
activitytimeout = CurTime()+net.Stream.Timeout,
|
||||
curidentifier = 1,
|
||||
queue = {}
|
||||
}, t)
|
||||
end
|
||||
}
|
||||
setmetatable(WriteStreamQueue, WriteStreamQueue)
|
||||
net.Stream.WriteStreams = WriteStreamQueue()
|
||||
|
||||
local ReadStreamQueue = {
|
||||
__index = {
|
||||
Add = function(self, stream)
|
||||
local queue = self.queues[stream.player]
|
||||
|
||||
if #queue == net.Stream.MaxReadStreams then
|
||||
ErrorNoHalt("Receiving too many ReadStream requests!")
|
||||
return
|
||||
end
|
||||
|
||||
for _, v in ipairs(queue) do
|
||||
if v.identifier == stream.identifier then
|
||||
ErrorNoHalt("Tried to start a new ReadStream for an already existing stream!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
queue[#queue+1] = stream
|
||||
if #queue == 1 then
|
||||
stream:Request()
|
||||
end
|
||||
return stream
|
||||
end,
|
||||
|
||||
Remove = function(self, stream)
|
||||
local queue = rawget(self.queues, stream.player)
|
||||
if queue then
|
||||
if stream == queue[1] then
|
||||
table.remove(queue, 1)
|
||||
local nextInQueue = queue[1]
|
||||
if nextInQueue then
|
||||
nextInQueue:Request()
|
||||
else
|
||||
self.queues[stream.player] = nil
|
||||
end
|
||||
else
|
||||
for k, v in ipairs(queue) do
|
||||
if v == stream then
|
||||
table.remove(queue, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
Read = function(self, ply)
|
||||
local identifier = net.ReadUInt(32)
|
||||
local queue = rawget(self.queues, ply)
|
||||
if queue and queue[1] then
|
||||
queue[1]:Read(identifier)
|
||||
end
|
||||
end
|
||||
},
|
||||
__call = function(t)
|
||||
return setmetatable({
|
||||
queues = setmetatable({}, {__index = function(t,k) local r={} t[k]=r return r end})
|
||||
}, t)
|
||||
end
|
||||
}
|
||||
setmetatable(ReadStreamQueue, ReadStreamQueue)
|
||||
net.Stream.ReadStreams = ReadStreamQueue()
|
||||
|
||||
|
||||
local WritingDataItem = {
|
||||
__index = {
|
||||
Write = function(self, ply, chunkidx)
|
||||
local client = self.clients[ply]
|
||||
if client.finished then return false end
|
||||
if chunkidx == #self.chunks+1 then self:Finished(ply) return true end
|
||||
|
||||
if client.downloads+#self.chunks-client.progress >= net.Stream.MaxTries * #self.chunks then self:Finished(ply) return false end
|
||||
client.downloads = client.downloads + 1
|
||||
|
||||
local chunk = self.chunks[chunkidx]
|
||||
if not chunk then return false end
|
||||
|
||||
client.progress = chunkidx
|
||||
|
||||
--print("Sending", "NetStreamRead", self.identifier, #chunk.data, chunkidx, chunk.crc)
|
||||
net.Start("NetStreamRead")
|
||||
net.WriteUInt(self.identifier, 32)
|
||||
net.WriteUInt(#chunk.data, 32)
|
||||
net.WriteUInt(chunkidx, 32)
|
||||
net.WriteString(chunk.crc)
|
||||
net.WriteData(chunk.data, #chunk.data)
|
||||
if CLIENT then net.SendToServer() else net.Send(ply) end
|
||||
return true
|
||||
end,
|
||||
|
||||
Finished = function(self, ply)
|
||||
self.clients[ply].finished = true
|
||||
if self.callback then
|
||||
local ok, err = xpcall(self.callback, debug.traceback, ply)
|
||||
if not ok then ErrorNoHalt(err) end
|
||||
end
|
||||
end,
|
||||
|
||||
GetProgress = function(self, ply)
|
||||
return self.clients[ply].progress / #self.chunks
|
||||
end,
|
||||
|
||||
Remove = function(self)
|
||||
local sendTo = {}
|
||||
for ply, client in pairs(self.clients) do
|
||||
if not client.finished then
|
||||
client.finished = true
|
||||
if CLIENT or ply:IsValid() then sendTo[#sendTo+1] = ply end
|
||||
end
|
||||
end
|
||||
|
||||
if next(sendTo)~=nil then
|
||||
--print("Sending", "NetStreamRead", self.identifier, 0)
|
||||
net.Start("NetStreamRead")
|
||||
net.WriteUInt(self.identifier, 32)
|
||||
net.WriteUInt(0, 32)
|
||||
if SERVER then net.Send(sendTo) else net.SendToServer() end
|
||||
end
|
||||
end
|
||||
|
||||
},
|
||||
__call = function(t, data, callback)
|
||||
local chunks = {}
|
||||
for i=1, math.ceil(#data / net.Stream.SendSize) do
|
||||
local datachunk = string.sub(data, (i - 1) * net.Stream.SendSize + 1, i * net.Stream.SendSize)
|
||||
chunks[i] = { data = datachunk, crc = util.CRC(datachunk) }
|
||||
end
|
||||
|
||||
return setmetatable({
|
||||
timeout = CurTime()+net.Stream.Timeout,
|
||||
chunks = chunks,
|
||||
callback = callback,
|
||||
lasttouched = 0,
|
||||
clients = setmetatable({},{__index = function(t,k)
|
||||
local r = {
|
||||
finished = false,
|
||||
downloads = 0,
|
||||
progress = 0,
|
||||
} t[k]=r return r
|
||||
end})
|
||||
}, t)
|
||||
end
|
||||
}
|
||||
setmetatable(WritingDataItem, WritingDataItem)
|
||||
|
||||
local ReadingDataItem = {
|
||||
__index = {
|
||||
Request = function(self)
|
||||
if self.downloads+self.numchunks-#self.chunks >= net.Stream.MaxTries*self.numchunks then self:Remove() return end
|
||||
self.downloads = self.downloads + 1
|
||||
timer.Create("NetStreamReadTimeout" .. self.identifier, net.Stream.Timeout*0.5, 1, function() self:Request() end)
|
||||
self:WriteRequest()
|
||||
end,
|
||||
|
||||
WriteRequest = function(self)
|
||||
--print("Requesting", self.identifier, #self.chunks)
|
||||
net.Start("NetStreamWrite")
|
||||
net.WriteUInt(self.identifier, 32)
|
||||
net.WriteUInt(#self.chunks+1, 32)
|
||||
if CLIENT then net.SendToServer() else net.Send(self.player) end
|
||||
end,
|
||||
|
||||
Read = function(self, identifier)
|
||||
if self.identifier ~= identifier then self:Request() return end
|
||||
|
||||
local size = net.ReadUInt(32)
|
||||
if size == 0 then self:Remove() return end
|
||||
|
||||
local chunkidx = net.ReadUInt(32)
|
||||
if chunkidx ~= #self.chunks+1 then self:Request() return end
|
||||
|
||||
local crc = net.ReadString()
|
||||
local data = net.ReadData(size)
|
||||
|
||||
if crc ~= util.CRC(data) then self:Request() return end
|
||||
|
||||
self.chunks[chunkidx] = data
|
||||
if #self.chunks == self.numchunks then self:Remove(true) return end
|
||||
|
||||
self:Request()
|
||||
end,
|
||||
|
||||
GetProgress = function(self)
|
||||
return #self.chunks/self.numchunks
|
||||
end,
|
||||
|
||||
Remove = function(self, finished)
|
||||
timer.Remove("NetStreamReadTimeout" .. self.identifier)
|
||||
|
||||
local data
|
||||
if finished then
|
||||
data = table.concat(self.chunks)
|
||||
if self.compressed then
|
||||
data = util.Decompress(data, net.Stream.MaxSize)
|
||||
end
|
||||
self:WriteRequest() -- Notify we finished
|
||||
end
|
||||
|
||||
local ok, err = xpcall(self.callback, debug.traceback, data)
|
||||
if not ok then ErrorNoHalt(err) end
|
||||
|
||||
net.Stream.ReadStreams:Remove(self)
|
||||
end
|
||||
},
|
||||
__call = function(t, ply, callback, numchunks, identifier, compressed)
|
||||
return setmetatable({
|
||||
identifier = identifier,
|
||||
chunks = {},
|
||||
compressed = compressed,
|
||||
numchunks = numchunks,
|
||||
callback = callback,
|
||||
player = ply,
|
||||
downloads = 0
|
||||
}, t)
|
||||
end
|
||||
}
|
||||
setmetatable(ReadingDataItem, ReadingDataItem)
|
||||
|
||||
|
||||
function net.WriteStream(data, callback, dontcompress)
|
||||
if not isstring(data) then
|
||||
error("bad argument #1 to 'WriteStream' (string expected, got " .. type(data) .. ")", 2)
|
||||
end
|
||||
if callback ~= nil and not isfunction(callback) then
|
||||
error("bad argument #2 to 'WriteStream' (function expected, got " .. type(callback) .. ")", 2)
|
||||
end
|
||||
|
||||
local compressed = not dontcompress
|
||||
if compressed then
|
||||
data = util.Compress(data) or ""
|
||||
end
|
||||
|
||||
if #data == 0 then
|
||||
net.WriteUInt(0, 32)
|
||||
return
|
||||
end
|
||||
|
||||
if #data > net.Stream.MaxSize then
|
||||
ErrorNoHalt("net.WriteStream request is too large! ", #data/1048576, "MiB")
|
||||
net.WriteUInt(0, 32)
|
||||
return
|
||||
end
|
||||
|
||||
local stream = net.Stream.WriteStreams:Add(WritingDataItem(data, callback, compressed))
|
||||
if not stream then return end
|
||||
|
||||
--print("WriteStream", #stream.chunks, stream.identifier, compressed)
|
||||
net.WriteUInt(#stream.chunks, 32)
|
||||
net.WriteUInt(stream.identifier, 32)
|
||||
net.WriteBool(compressed)
|
||||
|
||||
return stream
|
||||
end
|
||||
|
||||
--If the receiver is a player then add it to a queue.
|
||||
--If the receiver is the server then add it to a queue for each individual player
|
||||
function net.ReadStream(ply, callback)
|
||||
if CLIENT then
|
||||
ply = NULL
|
||||
else
|
||||
if type(ply) ~= "Player" then
|
||||
error("bad argument #1 to 'ReadStream' (Player expected, got " .. type(ply) .. ")", 2)
|
||||
elseif not ply:IsValid() then
|
||||
error("bad argument #1 to 'ReadStream' (Tried to use a NULL entity!)", 2)
|
||||
end
|
||||
end
|
||||
if not isfunction(callback) then
|
||||
error("bad argument #2 to 'ReadStream' (function expected, got " .. type(callback) .. ")", 2)
|
||||
end
|
||||
|
||||
local numchunks = net.ReadUInt(32)
|
||||
if numchunks == nil then
|
||||
return
|
||||
elseif numchunks == 0 then
|
||||
local ok, err = xpcall(callback, debug.traceback, "")
|
||||
if not ok then ErrorNoHalt(err) end
|
||||
return
|
||||
end
|
||||
|
||||
local identifier = net.ReadUInt(32)
|
||||
local compressed = net.ReadBool()
|
||||
|
||||
if numchunks > net.Stream.MaxChunks then
|
||||
ErrorNoHalt("ReadStream requests from ", ply, " is too large! ", numchunks * net.Stream.SendSize / 1048576, "MiB")
|
||||
return
|
||||
end
|
||||
|
||||
--print("ReadStream", numchunks, identifier, compressed)
|
||||
|
||||
return net.Stream.ReadStreams:Add(ReadingDataItem(ply, callback, numchunks, identifier, compressed))
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("NetStreamWrite")
|
||||
util.AddNetworkString("NetStreamRead")
|
||||
end
|
||||
|
||||
--Send requested stream data
|
||||
net.Receive("NetStreamWrite", function(len, ply)
|
||||
net.Stream.WriteStreams:Write(ply or NULL)
|
||||
end)
|
||||
|
||||
--Download the sent stream data
|
||||
net.Receive("NetStreamRead", function(len, ply)
|
||||
net.Stream.ReadStreams:Read(ply or NULL)
|
||||
end)
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
*/
|
||||
|
||||
local function PermaPropsViewer()
|
||||
|
||||
if not LocalPlayer().DrawPPEnt or not istable(LocalPlayer().DrawPPEnt) then return end
|
||||
|
||||
local pos = LocalPlayer():EyePos() + LocalPlayer():EyeAngles():Forward() * 10
|
||||
local ang = LocalPlayer():EyeAngles()
|
||||
|
||||
ang = Angle(ang.p + 90, ang.y, 0)
|
||||
|
||||
for k, v in pairs(LocalPlayer().DrawPPEnt) do
|
||||
|
||||
if not v or not v:IsValid() then LocalPlayer().DrawPPEnt[k] = nil continue end
|
||||
|
||||
render.ClearStencil()
|
||||
render.SetStencilEnable(true)
|
||||
render.SetStencilWriteMask(255)
|
||||
render.SetStencilTestMask(255)
|
||||
render.SetStencilReferenceValue(15)
|
||||
render.SetStencilFailOperation(STENCILOPERATION_REPLACE)
|
||||
render.SetStencilZFailOperation(STENCILOPERATION_REPLACE)
|
||||
render.SetStencilPassOperation(STENCILOPERATION_KEEP)
|
||||
render.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_ALWAYS)
|
||||
render.SetBlend(0)
|
||||
v:DrawModel()
|
||||
render.SetBlend(1)
|
||||
render.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL)
|
||||
cam.Start3D2D(pos, ang, 1)
|
||||
surface.SetDrawColor(255, 0, 0, 255)
|
||||
surface.DrawRect(-ScrW(), -ScrH(), ScrW() * 2, ScrH() * 2)
|
||||
cam.End3D2D()
|
||||
v:DrawModel()
|
||||
render.SetStencilEnable(false)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
hook.Add("PostDrawOpaqueRenderables", "PermaPropsViewer", PermaPropsViewer)
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
*/
|
||||
|
||||
surface.CreateFont( "pp_font", {
|
||||
font = "Arial",
|
||||
size = 20,
|
||||
weight = 700,
|
||||
shadow = false
|
||||
} )
|
||||
|
||||
local function pp_open_menu()
|
||||
|
||||
local Len = net.ReadFloat()
|
||||
local Data = net.ReadData( Len )
|
||||
local UnCompress = util.Decompress( Data )
|
||||
local Content = util.JSONToTable( UnCompress )
|
||||
|
||||
local Main = vgui.Create( "DFrame" )
|
||||
Main:SetSize( 600, 355 )
|
||||
Main:Center()
|
||||
Main:SetTitle("")
|
||||
Main:SetVisible( true )
|
||||
Main:SetDraggable( true )
|
||||
Main:ShowCloseButton( true )
|
||||
Main:MakePopup()
|
||||
Main.Paint = function(self)
|
||||
|
||||
draw.RoundedBox( 0, 0, 0, self:GetWide(), self:GetTall(), Color(155, 155, 155, 220) )
|
||||
surface.SetDrawColor( 17, 148, 240, 255 )
|
||||
surface.DrawOutlinedRect( 0, 0, self:GetWide(), self:GetTall() )
|
||||
|
||||
draw.RoundedBox( 0, 0, 0, self:GetWide(), 25, Color(17, 148, 240, 200) )
|
||||
surface.SetDrawColor( 17, 148, 240, 255 )
|
||||
surface.DrawOutlinedRect( 0, 0, self:GetWide(), 25 )
|
||||
draw.DrawText( "PermaProps Config", "pp_font", 10, 2.2, Color(255, 255, 255, 255), TEXT_ALIGN_LEFT )
|
||||
|
||||
end
|
||||
|
||||
local BSelect
|
||||
local PSelect
|
||||
|
||||
local MainPanel = vgui.Create( "DPanel", Main )
|
||||
MainPanel:SetPos( 190, 51 )
|
||||
MainPanel:SetSize( 390, 275 )
|
||||
MainPanel.Paint = function( self )
|
||||
surface.SetDrawColor( 50, 50, 50, 200 )
|
||||
surface.DrawRect( 0, 0, self:GetWide(), self:GetTall() )
|
||||
surface.DrawOutlinedRect(0, 15, self:GetWide(), 40)
|
||||
end
|
||||
PSelect = MainPanel
|
||||
|
||||
local MainLabel = vgui.Create("DLabel", MainPanel)
|
||||
MainLabel:SetFont("pp_font")
|
||||
MainLabel:SetPos(140, 25)
|
||||
MainLabel:SetColor(Color(50, 50, 50, 255))
|
||||
MainLabel:SetText("Hey ".. LocalPlayer():Nick() .." !")
|
||||
MainLabel:SizeToContents()
|
||||
|
||||
local MainLabel2 = vgui.Create("DLabel", MainPanel)
|
||||
MainLabel2:SetFont("pp_font")
|
||||
MainLabel2:SetPos(80, 80)
|
||||
MainLabel2:SetColor(Color(50, 50, 50, 255))
|
||||
MainLabel2:SetText("There are ".. ( Content.MProps or 0 ) .." props on this map.\n\nThere are ".. ( Content.TProps or 0 ) .." props in the DB.")
|
||||
MainLabel2:SizeToContents()
|
||||
|
||||
local RemoveMapProps = vgui.Create( "DButton", MainPanel )
|
||||
RemoveMapProps:SetText( " Clear map props " )
|
||||
RemoveMapProps:SetFont("pp_font")
|
||||
RemoveMapProps:SetSize( 370, 30)
|
||||
RemoveMapProps:SetPos( 10, 160 )
|
||||
RemoveMapProps:SetTextColor( Color( 50, 50, 50, 255 ) )
|
||||
RemoveMapProps.DoClick = function()
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "CLR_MAP"})
|
||||
net.SendToServer()
|
||||
end
|
||||
RemoveMapProps.Paint = function(self)
|
||||
surface.SetDrawColor(50, 50, 50, 255)
|
||||
surface.DrawOutlinedRect(0, 0, self:GetWide(), self:GetTall())
|
||||
end
|
||||
|
||||
local ClearMapProps = vgui.Create( "DButton", MainPanel )
|
||||
ClearMapProps:SetText( " Clear map props in the DB " )
|
||||
ClearMapProps:SetFont("pp_font")
|
||||
ClearMapProps:SetSize( 370, 30)
|
||||
ClearMapProps:SetPos( 10, 200 )
|
||||
ClearMapProps:SetTextColor( Color( 50, 50, 50, 255 ) )
|
||||
ClearMapProps.DoClick = function()
|
||||
|
||||
Derma_Query("Are you sure you want clear map props in the db ?\nYou can't undo this action !", "PermaProps 4.0", "Yes", function() net.Start("pp_info_send") net.WriteTable({CMD = "DEL_MAP"}) net.SendToServer() end, "Cancel")
|
||||
|
||||
end
|
||||
ClearMapProps.Paint = function(self)
|
||||
surface.SetDrawColor(50, 50, 50, 255)
|
||||
surface.DrawOutlinedRect(0, 0, self:GetWide(), self:GetTall())
|
||||
end
|
||||
|
||||
local ClearAllProps = vgui.Create( "DButton", MainPanel )
|
||||
ClearAllProps:SetText( " Clear all props in the DB " )
|
||||
ClearAllProps:SetFont("pp_font")
|
||||
ClearAllProps:SetSize( 370, 30)
|
||||
ClearAllProps:SetPos( 10, 240 )
|
||||
ClearAllProps:SetTextColor( Color( 50, 50, 50, 255 ) )
|
||||
ClearAllProps.DoClick = function()
|
||||
|
||||
Derma_Query("Are you sure you want clear all props in the db ?\nYou can't undo this action !", "PermaProps 4.0", "Yes", function() net.Start("pp_info_send") net.WriteTable({CMD = "DEL_ALL"}) net.SendToServer() end, "Cancel")
|
||||
|
||||
end
|
||||
ClearAllProps.Paint = function(self)
|
||||
surface.SetDrawColor(50, 50, 50, 255)
|
||||
surface.DrawOutlinedRect(0, 0, self:GetWide(), self:GetTall())
|
||||
end
|
||||
|
||||
local BMain = vgui.Create("DButton", Main)
|
||||
BSelect = BMain
|
||||
BMain:SetText("Main")
|
||||
BMain:SetFont("pp_font")
|
||||
BMain:SetSize(160, 50)
|
||||
BMain:SetPos(15, 27 + 25)
|
||||
BMain:SetTextColor( Color( 255, 255, 255, 255 ) )
|
||||
BMain.PaintColor = Color(17, 148, 240, 100)
|
||||
BMain.Paint = function(self)
|
||||
|
||||
draw.RoundedBox(0, 0, 0, self:GetWide(), self:GetTall(), self.PaintColor)
|
||||
surface.SetDrawColor(17, 148, 240, 255)
|
||||
surface.DrawOutlinedRect(0, 0, self:GetWide(), self:GetTall())
|
||||
|
||||
end
|
||||
BMain.DoClick = function( self )
|
||||
|
||||
if BSelect then BSelect.PaintColor = Color(0, 0, 0, 0) end
|
||||
BSelect = self
|
||||
self.PaintColor = Color(17, 148, 240, 100)
|
||||
|
||||
if PSelect then PSelect:Hide() end
|
||||
MainPanel:Show()
|
||||
PSelect = MainPanel
|
||||
|
||||
end
|
||||
|
||||
local ConfigPanel = vgui.Create( "DPanel", Main )
|
||||
ConfigPanel:SetPos( 190, 51 )
|
||||
ConfigPanel:SetSize( 390, 275 )
|
||||
ConfigPanel.Paint = function( self )
|
||||
surface.SetDrawColor( 50, 50, 50, 200 )
|
||||
surface.DrawRect( 0, 0, self:GetWide(), self:GetTall() )
|
||||
end
|
||||
ConfigPanel:Hide()
|
||||
|
||||
local CheckCustom = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckCustom:SetPos( 5, 30 )
|
||||
CheckCustom:SetText( "Custom permissions" )
|
||||
CheckCustom:SetValue( 0 )
|
||||
CheckCustom:SizeToContents()
|
||||
CheckCustom:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckCustom:SetDisabled( true )
|
||||
|
||||
local GroupsList = vgui.Create( "DComboBox", ConfigPanel )
|
||||
GroupsList:SetPos( 5, 5 )
|
||||
GroupsList:SetSize( 125, 20 )
|
||||
GroupsList:SetValue( "Select a group..." )
|
||||
|
||||
local CheckBox1 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox1:SetPos( 150, 10 )
|
||||
CheckBox1:SetText( "Menu" )
|
||||
CheckBox1:SizeToContents()
|
||||
CheckBox1:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox1:SetDisabled( true )
|
||||
CheckBox1.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Menu", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox2 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox2:SetPos( 150, 30 )
|
||||
CheckBox2:SetText( "Edit permissions" )
|
||||
CheckBox2:SizeToContents()
|
||||
CheckBox2:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox2:SetDisabled( true )
|
||||
CheckBox2.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Permissions", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox3 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox3:SetPos( 150, 50 )
|
||||
CheckBox3:SetText( "Physgun permaprops" )
|
||||
CheckBox3:SizeToContents()
|
||||
CheckBox3:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox3:SetDisabled( true )
|
||||
CheckBox3.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Physgun", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox4 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox4:SetPos( 150, 70 )
|
||||
CheckBox4:SetText( "Tool permaprops" )
|
||||
CheckBox4:SizeToContents()
|
||||
CheckBox4:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox4:SetDisabled( true )
|
||||
CheckBox4.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Tool", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox5 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox5:SetPos( 150, 90 )
|
||||
CheckBox5:SetText( "Property permaprops" )
|
||||
CheckBox5:SizeToContents()
|
||||
CheckBox5:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox5:SetDisabled( true )
|
||||
CheckBox5.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Property", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox6 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox6:SetPos( 150, 110 )
|
||||
CheckBox6:SetText( "Save props" )
|
||||
CheckBox6:SizeToContents()
|
||||
CheckBox6:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox6:SetDisabled( true )
|
||||
CheckBox6.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Save", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox7 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox7:SetPos( 150, 130 )
|
||||
CheckBox7:SetText( "Delete permaprops" )
|
||||
CheckBox7:SizeToContents()
|
||||
CheckBox7:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox7:SetDisabled( true )
|
||||
CheckBox7.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Delete", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local CheckBox8 = vgui.Create( "DCheckBoxLabel", ConfigPanel )
|
||||
CheckBox8:SetPos( 150, 150 )
|
||||
CheckBox8:SetText( "Update permaprops" )
|
||||
CheckBox8:SizeToContents()
|
||||
CheckBox8:SetTextColor( Color( 0, 0, 0, 255) )
|
||||
CheckBox8:SetDisabled( true )
|
||||
CheckBox8.OnChange = function(Self, Value)
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Update", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
GroupsList.OnSelect = function( panel, index, value )
|
||||
|
||||
CheckCustom:SetDisabled( false )
|
||||
CheckCustom:SetChecked( Content.Permissions[value].Custom )
|
||||
|
||||
CheckBox1:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox1:SetChecked( Content.Permissions[value].Menu )
|
||||
CheckBox2:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox2:SetChecked( Content.Permissions[value].Permissions )
|
||||
CheckBox3:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox3:SetChecked( Content.Permissions[value].Physgun )
|
||||
CheckBox4:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox4:SetChecked( Content.Permissions[value].Tool )
|
||||
CheckBox5:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox5:SetChecked( Content.Permissions[value].Property )
|
||||
CheckBox6:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox6:SetChecked( Content.Permissions[value].Save )
|
||||
CheckBox7:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox7:SetChecked( Content.Permissions[value].Delete )
|
||||
CheckBox8:SetDisabled( !Content.Permissions[value].Custom )
|
||||
CheckBox8:SetChecked( Content.Permissions[value].Update )
|
||||
|
||||
end
|
||||
|
||||
for k, v in pairs(Content.Permissions) do
|
||||
|
||||
GroupsList:AddChoice(k)
|
||||
|
||||
end
|
||||
|
||||
CheckCustom.OnChange = function(Self, Value)
|
||||
|
||||
CheckBox1:SetDisabled( !Value )
|
||||
CheckBox2:SetDisabled( !Value )
|
||||
CheckBox3:SetDisabled( !Value )
|
||||
CheckBox4:SetDisabled( !Value )
|
||||
CheckBox5:SetDisabled( !Value )
|
||||
CheckBox6:SetDisabled( !Value )
|
||||
CheckBox7:SetDisabled( !Value )
|
||||
CheckBox8:SetDisabled( !Value )
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "VAR", Val = Value, Data = "Custom", Name = GroupsList:GetValue()})
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
local BConfig = vgui.Create("DButton", Main)
|
||||
BConfig:SetText("Configuration")
|
||||
BConfig:SetFont("pp_font")
|
||||
BConfig:SetSize(160, 50)
|
||||
BConfig:SetPos(15, 71 + 55)
|
||||
BConfig:SetTextColor( Color( 255, 255, 255, 255 ) )
|
||||
BConfig.PaintColor = Color(0, 0, 0, 0)
|
||||
BConfig.Paint = function(self)
|
||||
draw.RoundedBox(0, 0, 0, self:GetWide(), self:GetTall(), self.PaintColor)
|
||||
surface.SetDrawColor(17, 148, 240, 255)
|
||||
surface.DrawOutlinedRect(0, 0, self:GetWide(), self:GetTall())
|
||||
end
|
||||
BConfig.DoClick = function( self )
|
||||
|
||||
if BSelect then BSelect.PaintColor = Color(0, 0, 0, 0) end
|
||||
BSelect = self
|
||||
self.PaintColor = Color(17, 148, 240, 100)
|
||||
|
||||
if PSelect then PSelect:Hide() end
|
||||
ConfigPanel:Show()
|
||||
PSelect = ConfigPanel
|
||||
|
||||
end
|
||||
|
||||
local PropsPanel = vgui.Create( "DPanel", Main )
|
||||
PropsPanel:SetPos( 190, 51 )
|
||||
PropsPanel:SetSize( 390, 275 )
|
||||
PropsPanel.Paint = function( self )
|
||||
surface.SetDrawColor( 50, 50, 50, 200 )
|
||||
surface.DrawRect( 0, 0, self:GetWide(), self:GetTall() )
|
||||
end
|
||||
PropsPanel:Hide()
|
||||
|
||||
local PropsList = vgui.Create( "DListView", PropsPanel )
|
||||
PropsList:SetMultiSelect( false )
|
||||
PropsList:SetSize( 390, 275 )
|
||||
local ColID = PropsList:AddColumn( "ID" )
|
||||
local ColEnt = PropsList:AddColumn( "Entity" )
|
||||
local ColMdl = PropsList:AddColumn( "Model" )
|
||||
ColID:SetMinWidth(50)
|
||||
ColID:SetMaxWidth(50)
|
||||
PropsList.Paint = function( self )
|
||||
surface.SetDrawColor(17, 148, 240, 255)
|
||||
end
|
||||
|
||||
PropsList.OnRowRightClick = function(panel, line)
|
||||
|
||||
local MenuButtonOptions = DermaMenu()
|
||||
MenuButtonOptions:AddOption("Draw entity", function()
|
||||
|
||||
if not LocalPlayer().DrawPPEnt or not istable(LocalPlayer().DrawPPEnt) then LocalPlayer().DrawPPEnt = {} end
|
||||
|
||||
if LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)] and LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)]:IsValid() then return end
|
||||
|
||||
local ent = ents.CreateClientProp( Content.PropsList[PropsList:GetLine(line):GetValue(1)].Model )
|
||||
ent:SetPos( Content.PropsList[PropsList:GetLine(line):GetValue(1)].Pos )
|
||||
ent:SetAngles( Content.PropsList[PropsList:GetLine(line):GetValue(1)].Angle )
|
||||
|
||||
LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)] = ent
|
||||
|
||||
end )
|
||||
|
||||
if LocalPlayer().DrawPPEnt and LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)] then
|
||||
|
||||
MenuButtonOptions:AddOption("Stop Drawing", function()
|
||||
|
||||
LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)]:Remove()
|
||||
LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)] = nil
|
||||
|
||||
end )
|
||||
|
||||
end
|
||||
|
||||
if LocalPlayer().DrawPPEnt != nil and istable(LocalPlayer().DrawPPEnt) and table.Count(LocalPlayer().DrawPPEnt) > 0 then
|
||||
|
||||
MenuButtonOptions:AddOption("Stop Drawing All", function()
|
||||
|
||||
for k, v in pairs(LocalPlayer().DrawPPEnt) do
|
||||
|
||||
LocalPlayer().DrawPPEnt[k]:Remove()
|
||||
LocalPlayer().DrawPPEnt[k] = nil
|
||||
|
||||
end
|
||||
|
||||
end )
|
||||
|
||||
end
|
||||
|
||||
MenuButtonOptions:AddOption("Remove", function()
|
||||
|
||||
net.Start("pp_info_send")
|
||||
net.WriteTable({CMD = "DEL", Val = PropsList:GetLine(line):GetValue(1)})
|
||||
net.SendToServer()
|
||||
|
||||
if LocalPlayer().DrawPPEnt and LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)] != nil then
|
||||
|
||||
LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)]:Remove()
|
||||
LocalPlayer().DrawPPEnt[PropsList:GetLine(line):GetValue(1)] = nil
|
||||
|
||||
end
|
||||
|
||||
PropsList:RemoveLine(line)
|
||||
|
||||
|
||||
end )
|
||||
MenuButtonOptions:Open()
|
||||
|
||||
end
|
||||
|
||||
for k, v in pairs(Content.PropsList) do
|
||||
|
||||
PropsList:AddLine(k, v.Class, v.Model)
|
||||
|
||||
end
|
||||
|
||||
local BProps = vgui.Create("DButton", Main)
|
||||
BProps:SetText("Props List")
|
||||
BProps:SetFont("pp_font")
|
||||
BProps:SetSize(160, 50)
|
||||
BProps:SetPos(15, 115 + 85)
|
||||
BProps:SetTextColor( Color( 255, 255, 255, 255 ) )
|
||||
BProps.PaintColor = Color(0, 0, 0, 0)
|
||||
BProps.Paint = function(self)
|
||||
draw.RoundedBox(0, 0, 0, self:GetWide(), self:GetTall(), self.PaintColor)
|
||||
surface.SetDrawColor(17, 148, 240, 255)
|
||||
surface.DrawOutlinedRect(0, 0, self:GetWide(), self:GetTall())
|
||||
end
|
||||
BProps.DoClick = function( self )
|
||||
|
||||
if BSelect then BSelect.PaintColor = Color(0, 0, 0, 0) end
|
||||
BSelect = self
|
||||
self.PaintColor = Color(17, 148, 240, 100)
|
||||
|
||||
if PSelect then PSelect:Hide() end
|
||||
PropsPanel:Show()
|
||||
PSelect = PropsPanel
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
net.Receive("pp_open_menu", pp_open_menu)
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
*/
|
||||
|
||||
if not PermaProps then PermaProps = {} end
|
||||
|
||||
function PermaProps.PPGetEntTable( ent )
|
||||
|
||||
if !ent or !ent:IsValid() then return false end
|
||||
|
||||
local content = {}
|
||||
content.Class = ent:GetClass()
|
||||
content.Pos = ent:GetPos()
|
||||
content.Angle = ent:GetAngles()
|
||||
content.Model = ent:GetModel()
|
||||
content.Skin = ent:GetSkin()
|
||||
//content.Mins, content.Maxs = ent:GetCollisionBounds()
|
||||
content.ColGroup = ent:GetCollisionGroup()
|
||||
content.Name = ent:GetName()
|
||||
content.ModelScale = ent:GetModelScale()
|
||||
content.Color = ent:GetColor()
|
||||
content.Material = ent:GetMaterial()
|
||||
content.Solid = ent:GetSolid()
|
||||
content.RenderMode = ent:GetRenderMode()
|
||||
|
||||
if PermaProps.SpecialENTSSave[ent:GetClass()] != nil and isfunction(PermaProps.SpecialENTSSave[ent:GetClass()]) then
|
||||
|
||||
local othercontent = PermaProps.SpecialENTSSave[ent:GetClass()](ent)
|
||||
if not othercontent then return false end
|
||||
if othercontent != nil and istable(othercontent) then
|
||||
table.Merge(content, othercontent)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
local othercontent = hook.Run("PermaProps.OnEntitySaved", ent)
|
||||
if othercontent and istable(othercontent) then
|
||||
table.Merge(content, othercontent)
|
||||
end
|
||||
end
|
||||
|
||||
if ( ent.GetNetworkVars ) then
|
||||
content.DT = ent:GetNetworkVars()
|
||||
end
|
||||
|
||||
local sm = ent:GetMaterials()
|
||||
if ( sm and istable(sm) ) then
|
||||
|
||||
for k, v in pairs( sm ) do
|
||||
|
||||
if ( ent:GetSubMaterial( k )) then
|
||||
|
||||
content.SubMat = content.SubMat or {}
|
||||
content.SubMat[ k ] = ent:GetSubMaterial( k-1 )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local bg = ent:GetBodyGroups()
|
||||
if ( bg ) then
|
||||
|
||||
for k, v in pairs( bg ) do
|
||||
|
||||
if ( ent:GetBodygroup( v.id ) > 0 ) then
|
||||
|
||||
content.BodyG = content.BodyG or {}
|
||||
content.BodyG[ v.id ] = ent:GetBodygroup( v.id )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if ent:GetPhysicsObject() and ent:GetPhysicsObject():IsValid() then
|
||||
content.Frozen = !ent:GetPhysicsObject():IsMoveable()
|
||||
end
|
||||
|
||||
if content.Class == "prop_dynamic" then
|
||||
content.Class = "prop_physics"
|
||||
end
|
||||
|
||||
--content.Table = PermaProps.UselessContent( ent:GetTable() )
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
|
||||
function PermaProps.PPEntityFromTable( data, id )
|
||||
|
||||
if not id or not isnumber(id) then return false end
|
||||
if not data or not istable(data) then return false end
|
||||
|
||||
if data.Class == "prop_physics" and data.Frozen then
|
||||
data.Class = "prop_dynamic" -- Can reduce lags
|
||||
end
|
||||
|
||||
local ent = ents.Create(data.Class)
|
||||
if !ent then return false end
|
||||
if !ent:IsVehicle() then if !ent:IsValid() then return false end end
|
||||
ent:SetPos( data.Pos or Vector(0, 0, 0) )
|
||||
ent:SetAngles( data.Angle or Angle(0, 0, 0) )
|
||||
ent:SetModel( data.Model or "models/error.mdl" )
|
||||
ent:SetSkin( data.Skin or 0 )
|
||||
//ent:SetCollisionBounds( ( data.Mins or 0 ), ( data.Maxs or 0 ) )
|
||||
ent:SetCollisionGroup( data.ColGroup or 0 )
|
||||
ent:SetName( data.Name or "" )
|
||||
ent:SetModelScale( data.ModelScale or 1 )
|
||||
ent:SetMaterial( data.Material or "" )
|
||||
ent:SetSolid( data.Solid or 6 )
|
||||
|
||||
if PermaProps.SpecialENTSSpawn[data.Class] != nil and isfunction(PermaProps.SpecialENTSSpawn[data.Class]) then
|
||||
|
||||
PermaProps.SpecialENTSSpawn[data.Class](ent, data.Other)
|
||||
|
||||
else
|
||||
|
||||
ent:Spawn()
|
||||
|
||||
end
|
||||
|
||||
hook.Run("PermaProps.OnEntityCreated", ent, data)
|
||||
|
||||
ent:SetRenderMode( data.RenderMode or RENDERMODE_NORMAL )
|
||||
ent:SetColor( data.Color or Color(255, 255, 255, 255) )
|
||||
|
||||
if data.EntityMods != nil and istable(data.EntityMods) then -- OLD DATA
|
||||
|
||||
if data.EntityMods.material then
|
||||
ent:SetMaterial( data.EntityMods.material["MaterialOverride"] or "")
|
||||
end
|
||||
|
||||
if data.EntityMods.colour then
|
||||
ent:SetColor( data.EntityMods.colour.Color or Color(255, 255, 255, 255))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if data.DT then
|
||||
|
||||
for k, v in pairs( data.DT ) do
|
||||
|
||||
if ( data.DT[ k ] == nil ) then continue end
|
||||
if !isfunction(ent[ "Set" .. k ]) then continue end
|
||||
ent[ "Set" .. k ]( ent, data.DT[ k ] )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if data.BodyG then
|
||||
|
||||
for k, v in pairs( data.BodyG ) do
|
||||
|
||||
ent:SetBodygroup( k, v )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if data.SubMat then
|
||||
|
||||
for k, v in pairs( data.SubMat ) do
|
||||
|
||||
if type(k) != "number" or type(v) != "string" then continue end
|
||||
|
||||
ent:SetSubMaterial( k-1, v )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if data.Frozen != nil then
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if phys and phys:IsValid() then
|
||||
phys:EnableMotion(!data.Frozen)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
/*if data.Table then
|
||||
|
||||
table.Merge(ent:GetTable(), data.Table)
|
||||
|
||||
end*/
|
||||
|
||||
ent.PermaProps_ID = id
|
||||
ent.PermaProps = true
|
||||
|
||||
// For all idiots who don't know how to config FPP, FUCK YOU
|
||||
function ent:CanTool( ply, trace, tool )
|
||||
|
||||
if trace and IsValid(trace.Entity) and trace.Entity.PermaProps then
|
||||
|
||||
if tool == "permaprops" then
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
return PermaProps.HasPermission( ply, "Tool")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return ent
|
||||
|
||||
end
|
||||
|
||||
function PermaProps.ReloadPermaProps()
|
||||
|
||||
for k, v in pairs( ents.GetAll() ) do
|
||||
|
||||
if v.PermaProps == true then
|
||||
|
||||
v:Remove()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local content = PermaProps.SQL.Query( "SELECT * FROM permaprops WHERE map = ".. sql.SQLStr(game.GetMap()) .. ";" )
|
||||
|
||||
if not content or content == nil then return end
|
||||
|
||||
for k, v in pairs( content ) do
|
||||
|
||||
local data = util.JSONToTable(v.content)
|
||||
|
||||
local e = PermaProps.PPEntityFromTable(data, tonumber(v.id))
|
||||
if !e or !e:IsValid() then continue end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
hook.Add("InitPostEntity", "InitializePermaProps", PermaProps.ReloadPermaProps)
|
||||
hook.Add("PostCleanupMap", "WhenCleanUpPermaProps", PermaProps.ReloadPermaProps) -- #MOMO
|
||||
timer.Simple(5, function() PermaProps.ReloadPermaProps() end) -- When the hook isn't call ...
|
||||
|
||||
function PermaProps.SparksEffect( ent )
|
||||
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin(ent:GetPos())
|
||||
effectdata:SetMagnitude(2.5)
|
||||
effectdata:SetScale(2)
|
||||
effectdata:SetRadius(3)
|
||||
util.Effect("Sparks", effectdata, true, true)
|
||||
|
||||
end
|
||||
|
||||
function PermaProps.IsUserGroup( ply, name )
|
||||
|
||||
if not ply:IsValid() then return false end
|
||||
return ply:GetNetworkedString("UserGroup") == name
|
||||
|
||||
end
|
||||
|
||||
function PermaProps.IsAdmin( ply )
|
||||
|
||||
if ( PermaProps.IsUserGroup(ply, "superadmin") or false ) then return true end
|
||||
if ( PermaProps.IsUserGroup(ply, "admin") or false ) then return true end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
function PermaProps.IsSuperAdmin( ply )
|
||||
|
||||
return ( PermaProps.IsUserGroup(ply, "superadmin") or false )
|
||||
|
||||
end
|
||||
|
||||
function PermaProps.UselessContent( tbl )
|
||||
|
||||
local function SortFcn( tbl2 )
|
||||
|
||||
for k, v in pairs( tbl2 ) do
|
||||
|
||||
if isfunction( v ) or isentity( v ) then
|
||||
|
||||
tbl2[k] = nil
|
||||
|
||||
elseif istable( v ) then
|
||||
|
||||
SortFcn( v )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return tbl2
|
||||
|
||||
end
|
||||
|
||||
for k, v in pairs( tbl ) do
|
||||
|
||||
if isfunction( v ) or isentity( v ) then
|
||||
|
||||
tbl[k] = nil
|
||||
|
||||
elseif istable( v ) then
|
||||
|
||||
SortFcn( v )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return tbl
|
||||
|
||||
end
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
*/
|
||||
|
||||
util.AddNetworkString("pp_open_menu")
|
||||
util.AddNetworkString("pp_info_send")
|
||||
|
||||
local function PermissionLoad()
|
||||
|
||||
if not PermaProps then PermaProps = {} end
|
||||
if not PermaProps.Permissions then PermaProps.Permissions = {} end
|
||||
|
||||
PermaProps.Permissions["superadmin"] = { Physgun = true, Tool = true, Property = true, Save = true, Delete = true, Update = true, Menu = true, Permissions = true, Inherits = "admin", Custom = true }
|
||||
PermaProps.Permissions["admin"] = { Physgun = false, Tool = false, Property = false, Save = true, Delete = true, Update = true, Menu = true, Permissions = false, Inherits = "user", Custom = true }
|
||||
PermaProps.Permissions["user"] = { Physgun = false, Tool = false, Property = false, Save = false, Delete = false, Update = false, Menu = false, Permissions = false, Inherits = "user", Custom = true }
|
||||
|
||||
if CAMI then
|
||||
|
||||
for k, v in pairs(CAMI.GetUsergroups()) do
|
||||
|
||||
if k == "superadmin" or k == "admin" or k == "user" then continue end
|
||||
|
||||
PermaProps.Permissions[k] = { Physgun = false, Tool = false, Property = false, Save = false, Delete = false, Update = false, Menu = false, Permissions = false, Inherits = v.Inherits, Custom = false }
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if file.Exists( "permaprops_config.txt", "DATA" ) then
|
||||
file.Delete( "permaprops_config.txt" )
|
||||
end
|
||||
|
||||
if file.Exists( "permaprops_permissions.txt", "DATA" ) then
|
||||
|
||||
local content = file.Read("permaprops_permissions.txt", "DATA")
|
||||
local tablecontent = util.JSONToTable( content )
|
||||
|
||||
for k, v in pairs(tablecontent) do
|
||||
|
||||
if PermaProps.Permissions[k] == nil then
|
||||
|
||||
tablecontent[k] = nil
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
table.Merge(PermaProps.Permissions, ( tablecontent or {} ))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
hook.Add("Initialize", "PermaPropPermLoad", PermissionLoad)
|
||||
hook.Add("CAMI.OnUsergroupRegistered", "PermaPropPermLoadCAMI", PermissionLoad) -- In case something changes
|
||||
|
||||
local function PermissionSave()
|
||||
|
||||
file.Write( "permaprops_permissions.txt", util.TableToJSON(PermaProps.Permissions) )
|
||||
|
||||
end
|
||||
|
||||
local function pp_open_menu( ply )
|
||||
|
||||
if !PermaProps.HasPermission( ply, "Menu") then ply:ChatPrint("Access denied !") return end
|
||||
|
||||
local SendTable = {}
|
||||
local Data_PropsList = sql.Query( "SELECT * FROM permaprops WHERE map = ".. sql.SQLStr(game.GetMap()) .. ";" )
|
||||
|
||||
if Data_PropsList and #Data_PropsList < 200 then
|
||||
|
||||
for k, v in pairs( Data_PropsList ) do
|
||||
|
||||
local data = util.JSONToTable(v.content)
|
||||
|
||||
SendTable[v.id] = {Model = data.Model, Class = data.Class, Pos = data.Pos, Angle = data.Angle}
|
||||
|
||||
end
|
||||
|
||||
elseif Data_PropsList and #Data_PropsList > 200 then -- Too much props dude :'(
|
||||
|
||||
for i = 1, 199 do
|
||||
|
||||
local data = util.JSONToTable(Data_PropsList[i].content)
|
||||
|
||||
SendTable[Data_PropsList[i].id] = {Model = data.Model, Class = data.Class, Pos = data.Pos, Angle = data.Angle}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local Content = {}
|
||||
Content.MProps = tonumber(sql.QueryValue("SELECT COUNT(*) FROM permaprops WHERE map = ".. sql.SQLStr(game.GetMap()) .. ";"))
|
||||
Content.TProps = tonumber(sql.QueryValue("SELECT COUNT(*) FROM permaprops;"))
|
||||
|
||||
Content.PropsList = SendTable
|
||||
Content.Permissions = PermaProps.Permissions
|
||||
|
||||
local Data = util.TableToJSON( Content )
|
||||
local Compressed = util.Compress( Data )
|
||||
|
||||
net.Start( "pp_open_menu" )
|
||||
net.WriteFloat( Compressed:len() )
|
||||
net.WriteData( Compressed, Compressed:len() )
|
||||
net.Send( ply )
|
||||
|
||||
end
|
||||
concommand.Add("pp_cfg_open", pp_open_menu)
|
||||
|
||||
local function pp_info_send( um, ply )
|
||||
|
||||
if !PermaProps.HasPermission( ply, "Menu") then ply:ChatPrint("Access denied !") return end
|
||||
|
||||
local Content = net.ReadTable()
|
||||
|
||||
if Content["CMD"] == "DEL" then
|
||||
|
||||
Content["Val"] = tonumber(Content["Val"])
|
||||
|
||||
if Content["Val"] != nil and Content["Val"] <= 0 then return end
|
||||
|
||||
sql.Query("DELETE FROM permaprops WHERE id = ".. sql.SQLStr(Content["Val"]) .. ";")
|
||||
|
||||
for k, v in pairs(ents.GetAll()) do
|
||||
|
||||
if v.PermaProps_ID == Content["Val"] then
|
||||
|
||||
ply:ChatPrint("You erased " .. v:GetClass() .. " with a model of " .. v:GetModel() .. " from the database.")
|
||||
v:Remove()
|
||||
break
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
elseif Content["CMD"] == "VAR" then
|
||||
|
||||
if PermaProps.Permissions[Content["Name"]] == nil or PermaProps.Permissions[Content["Name"]][Content["Data"]] == nil then return end
|
||||
if !isbool(Content["Val"]) then return end
|
||||
|
||||
if Content["Name"] == "superadmin" and ( Content["Data"] == "Custom" or Content["Data"] == "Permissions" or Content["Data"] == "Menu" ) then return end
|
||||
|
||||
if !PermaProps.HasPermission( ply, "Permissions") then ply:ChatPrint("Access denied !") return end
|
||||
|
||||
PermaProps.Permissions[Content["Name"]][Content["Data"]] = Content["Val"]
|
||||
|
||||
PermissionSave()
|
||||
|
||||
elseif Content["CMD"] == "DEL_MAP" then
|
||||
|
||||
sql.Query( "DELETE FROM permaprops WHERE map = ".. sql.SQLStr(game.GetMap()) .. ";" )
|
||||
PermaProps.ReloadPermaProps()
|
||||
|
||||
ply:ChatPrint("You erased all props from the map !")
|
||||
|
||||
elseif Content["CMD"] == "DEL_ALL" then
|
||||
|
||||
sql.Query( "DELETE FROM permaprops;" )
|
||||
PermaProps.ReloadPermaProps()
|
||||
|
||||
ply:ChatPrint("You erased all props !")
|
||||
|
||||
elseif Content["CMD"] == "CLR_MAP" then
|
||||
|
||||
for k, v in pairs( ents.GetAll() ) do
|
||||
|
||||
if v.PermaProps == true then
|
||||
|
||||
v:Remove()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ply:ChatPrint("You have removed all props !")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
net.Receive("pp_info_send", pp_info_send)
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
Thanks to ARitz Cracker for this part
|
||||
*/
|
||||
|
||||
function PermaProps.HasPermission( ply, name )
|
||||
|
||||
if !PermaProps or !PermaProps.Permissions or !PermaProps.Permissions[ply:GetUserGroup()] then return false end
|
||||
|
||||
if PermaProps.Permissions[ply:GetUserGroup()].Custom == false and PermaProps.Permissions[ply:GetUserGroup()].Inherits and PermaProps.Permissions[PermaProps.Permissions[ply:GetUserGroup()].Inherits] then
|
||||
|
||||
return PermaProps.Permissions[PermaProps.Permissions[ply:GetUserGroup()].Inherits][name]
|
||||
|
||||
end
|
||||
|
||||
return PermaProps.Permissions[ply:GetUserGroup()][name]
|
||||
|
||||
end
|
||||
|
||||
local function PermaPropsPhys( ply, ent, phys )
|
||||
|
||||
if ent.PermaProps then
|
||||
|
||||
return PermaProps.HasPermission( ply, "Physgun")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
hook.Add("PhysgunPickup", "PermaPropsPhys", PermaPropsPhys)
|
||||
hook.Add( "CanPlayerUnfreeze", "PermaPropsUnfreeze", PermaPropsPhys) -- Prevents people from pressing RELOAD on the physgun
|
||||
|
||||
local function PermaPropsTool( ply, tr, tool )
|
||||
|
||||
if IsValid(tr.Entity) then
|
||||
|
||||
if tr.Entity.PermaProps then
|
||||
|
||||
if tool == "permaprops" then
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
return PermaProps.HasPermission( ply, "Tool")
|
||||
|
||||
end
|
||||
|
||||
if tr.Entity:GetClass() == "sammyservers_textscreen" and tool == "permaprops" then -- Let people use PermaProps on textscreen
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
hook.Add( "CanTool", "PermaPropsTool", PermaPropsTool)
|
||||
|
||||
local function PermaPropsProperty( ply, property, ent )
|
||||
|
||||
if IsValid(ent) and ent.PermaProps and tool ~= "permaprops" then
|
||||
|
||||
return PermaProps.HasPermission( ply, "Property")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
hook.Add( "CanProperty", "PermaPropsProperty", PermaPropsProperty)
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
*/
|
||||
|
||||
if not PermaProps then PermaProps = {} end
|
||||
|
||||
PermaProps.SpecialENTSSpawn = {}
|
||||
PermaProps.SpecialENTSSpawn["gmod_lamp"] = function( ent, data)
|
||||
|
||||
ent:SetFlashlightTexture( data["Texture"] )
|
||||
ent:SetLightFOV( data["fov"] )
|
||||
ent:SetColor( Color( data["r"], data["g"], data["b"], 255 ) )
|
||||
ent:SetDistance( data["distance"] )
|
||||
ent:SetBrightness( data["brightness"] )
|
||||
ent:Switch( true )
|
||||
|
||||
ent:Spawn()
|
||||
|
||||
ent.Texture = data["Texture"]
|
||||
ent.KeyDown = data["KeyDown"]
|
||||
ent.fov = data["fov"]
|
||||
ent.distance = data["distance"]
|
||||
ent.r = data["r"]
|
||||
ent.g = data["g"]
|
||||
ent.b = data["b"]
|
||||
ent.brightness = data["brightness"]
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSpawn["prop_vehicle_jeep"] = function( ent, data)
|
||||
|
||||
if ( ent:GetModel() == "models/buggy.mdl" ) then ent:SetKeyValue( "vehiclescript", "scripts/vehicles/jeep_test.txt" ) end
|
||||
if ( ent:GetModel() == "models/vehicle.mdl" ) then ent:SetKeyValue( "vehiclescript", "scripts/vehicles/jalopy.txt" ) end
|
||||
|
||||
if ( data["VehicleTable"] && data["VehicleTable"].KeyValues ) then
|
||||
for k, v in pairs( data["VehicleTable"].KeyValues ) do
|
||||
ent:SetKeyValue( k, v )
|
||||
end
|
||||
end
|
||||
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
ent:SetVehicleClass( data["VehicleName"] )
|
||||
ent.VehicleName = data["VehicleName"]
|
||||
ent.VehicleTable = data["VehicleTable"]
|
||||
ent.ClassOverride = data["Class"]
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
PermaProps.SpecialENTSSpawn["prop_vehicle_jeep_old"] = PermaProps.SpecialENTSSpawn["prop_vehicle_jeep"]
|
||||
PermaProps.SpecialENTSSpawn["prop_vehicle_airboat"] = PermaProps.SpecialENTSSpawn["prop_vehicle_jeep"]
|
||||
PermaProps.SpecialENTSSpawn["prop_vehicle_prisoner_pod"] = PermaProps.SpecialENTSSpawn["prop_vehicle_jeep"]
|
||||
|
||||
PermaProps.SpecialENTSSpawn["prop_ragdoll"] = function( ent, data )
|
||||
|
||||
if !data or !istable( data ) then return end
|
||||
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
if data["Bones"] then
|
||||
|
||||
for objectid, objectdata in pairs( data["Bones"] ) do
|
||||
|
||||
local Phys = ent:GetPhysicsObjectNum( objectid )
|
||||
if !IsValid( Phys ) then continue end
|
||||
|
||||
if ( isvector( objectdata.Pos ) && isangle( objectdata.Angle ) ) then
|
||||
|
||||
local pos, ang = LocalToWorld( objectdata.Pos, objectdata.Angle, Vector(0, 0, 0), Angle(0, 0, 0) )
|
||||
Phys:SetPos( pos )
|
||||
Phys:SetAngles( ang )
|
||||
Phys:Wake()
|
||||
|
||||
if objectdata.Frozen then
|
||||
Phys:EnableMotion( false )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if data["BoneManip"] and ent:IsValid() then
|
||||
|
||||
for k, v in pairs( data["BoneManip"] ) do
|
||||
|
||||
if ( v.s ) then ent:ManipulateBoneScale( k, v.s ) end
|
||||
if ( v.a ) then ent:ManipulateBoneAngles( k, v.a ) end
|
||||
if ( v.p ) then ent:ManipulateBonePosition( k, v.p ) end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if data["Flex"] and ent:IsValid() then
|
||||
|
||||
for k, v in pairs( data["Flex"] ) do
|
||||
ent:SetFlexWeight( k, v )
|
||||
end
|
||||
|
||||
if ( Scale ) then
|
||||
ent:SetFlexScale( Scale )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSpawn["sammyservers_textscreen"] = function( ent, data )
|
||||
|
||||
if !data or !istable( data ) then return end
|
||||
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
if data["Lines"] then
|
||||
|
||||
for k, v in pairs(data["Lines"] or {}) do
|
||||
|
||||
ent:SetLine(k, v.text, Color(v.color.r, v.color.g, v.color.b, v.color.a), v.size, v.font, v.rainbow or 0)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSpawn["NPC"] = function( ent, data )
|
||||
|
||||
if data and istable( data ) then
|
||||
|
||||
if data["Equipment"] then
|
||||
|
||||
local valid = false
|
||||
for _, v in pairs( list.Get( "NPCUsableWeapons" ) ) do
|
||||
if v.class == data["Equipment"] then valid = true break end
|
||||
end
|
||||
|
||||
if ( data["Equipment"] && data["Equipment"] != "none" && valid ) then
|
||||
ent:SetKeyValue( "additionalequipment", data["Equipment"] )
|
||||
ent.Equipment = data["Equipment"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
if list.Get( "NPC" ) and istable(list.Get( "NPC" )) then
|
||||
|
||||
for k, v in pairs(list.Get( "NPC" )) do
|
||||
|
||||
PermaProps.SpecialENTSSpawn[k] = PermaProps.SpecialENTSSpawn["NPC"]
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSpawn["item_ammo_crate"] = function( ent, data )
|
||||
|
||||
if data and istable(data) and data["type"] then
|
||||
|
||||
ent.type = data["type"]
|
||||
ent:SetKeyValue( "AmmoType", math.Clamp( data["type"], 0, 9 ) )
|
||||
|
||||
end
|
||||
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
|
||||
PermaProps.SpecialENTSSave = {}
|
||||
PermaProps.SpecialENTSSave["gmod_lamp"] = function( ent )
|
||||
|
||||
local content = {}
|
||||
content.Other = {}
|
||||
content.Other["Texture"] = ent.Texture
|
||||
content.Other["KeyDown"] = ent.KeyDown
|
||||
content.Other["fov"] = ent.fov
|
||||
content.Other["distance"] = ent.distance
|
||||
content.Other["r"] = ent.r
|
||||
content.Other["g"] = ent.g
|
||||
content.Other["b"] = ent.b
|
||||
content.Other["brightness"] = ent.brightness
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSave["prop_vehicle_jeep"] = function( ent )
|
||||
|
||||
if not ent.VehicleTable then return false end
|
||||
|
||||
local content = {}
|
||||
content.Other = {}
|
||||
content.Other["VehicleName"] = ent.VehicleName
|
||||
content.Other["VehicleTable"] = ent.VehicleTable
|
||||
content.Other["ClassOverride"] = ent.ClassOverride
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
PermaProps.SpecialENTSSave["prop_vehicle_jeep_old"] = PermaProps.SpecialENTSSave["prop_vehicle_jeep"]
|
||||
PermaProps.SpecialENTSSave["prop_vehicle_airboat"] = PermaProps.SpecialENTSSave["prop_vehicle_jeep"]
|
||||
PermaProps.SpecialENTSSave["prop_vehicle_prisoner_pod"] = PermaProps.SpecialENTSSave["prop_vehicle_jeep"]
|
||||
|
||||
PermaProps.SpecialENTSSave["prop_ragdoll"] = function( ent )
|
||||
|
||||
local content = {}
|
||||
content.Other = {}
|
||||
content.Other["Bones"] = {}
|
||||
|
||||
local num = ent:GetPhysicsObjectCount()
|
||||
for objectid = 0, num - 1 do
|
||||
|
||||
local obj = ent:GetPhysicsObjectNum( objectid )
|
||||
if ( !obj:IsValid() ) then continue end
|
||||
|
||||
content.Other["Bones"][ objectid ] = {}
|
||||
|
||||
content.Other["Bones"][ objectid ].Pos = obj:GetPos()
|
||||
content.Other["Bones"][ objectid ].Angle = obj:GetAngles()
|
||||
content.Other["Bones"][ objectid ].Frozen = !obj:IsMoveable()
|
||||
if ( obj:IsAsleep() ) then content.Other["Bones"][ objectid ].Sleep = true end
|
||||
|
||||
content.Other["Bones"][ objectid ].Pos, content.Other["Bones"][ objectid ].Angle = WorldToLocal( content.Other["Bones"][ objectid ].Pos, content.Other["Bones"][ objectid ].Angle, Vector( 0, 0, 0 ), Angle( 0, 0, 0 ) )
|
||||
|
||||
end
|
||||
|
||||
if ( ent:HasBoneManipulations() ) then
|
||||
|
||||
content.Other["BoneManip"] = {}
|
||||
|
||||
for i = 0, ent:GetBoneCount() do
|
||||
|
||||
local t = {}
|
||||
|
||||
local s = ent:GetManipulateBoneScale( i )
|
||||
local a = ent:GetManipulateBoneAngles( i )
|
||||
local p = ent:GetManipulateBonePosition( i )
|
||||
|
||||
if ( s != Vector( 1, 1, 1 ) ) then t[ 's' ] = s end -- scale
|
||||
if ( a != Angle( 0, 0, 0 ) ) then t[ 'a' ] = a end -- angle
|
||||
if ( p != Vector( 0, 0, 0 ) ) then t[ 'p' ] = p end -- position
|
||||
|
||||
if ( table.Count( t ) > 0 ) then
|
||||
content.Other["BoneManip"][ i ] = t
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
content.Other["FlexScale"] = ent:GetFlexScale()
|
||||
for i = 0, ent:GetFlexNum() do
|
||||
|
||||
local w = ent:GetFlexWeight( i )
|
||||
if ( w != 0 ) then
|
||||
content.Other["Flex"] = content.Other["Flex"] or {}
|
||||
content.Other["Flex"][ i ] = w
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSave["sammyservers_textscreen"] = function( ent )
|
||||
|
||||
local content = {}
|
||||
content.Other = {}
|
||||
content.Other["Lines"] = ent.lines or {}
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSave["prop_effect"] = function( ent )
|
||||
|
||||
local content = {}
|
||||
content.Class = "pp_prop_effect"
|
||||
content.Model = ent.AttachedEntity:GetModel()
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
PermaProps.SpecialENTSSave["pp_prop_effect"] = PermaProps.SpecialENTSSave["prop_effect"]
|
||||
|
||||
PermaProps.SpecialENTSSave["NPC"] = function( ent )
|
||||
|
||||
if !ent.Equipment then return {} end
|
||||
|
||||
local content = {}
|
||||
content.Other = {}
|
||||
content.Other["Equipment"] = ent.Equipment
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
|
||||
if list.Get( "NPC" ) and istable(list.Get( "NPC" )) then
|
||||
|
||||
for k, v in pairs(list.Get( "NPC" )) do
|
||||
|
||||
PermaProps.SpecialENTSSave[k] = PermaProps.SpecialENTSSave["NPC"]
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
PermaProps.SpecialENTSSave["item_ammo_crate"] = function( ent )
|
||||
|
||||
local content = {}
|
||||
content.Other = {}
|
||||
content.Other["type"] = ent.type
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
____ _ _ ____ __ __ _ _
|
||||
/ ___|___ __| | ___ __| | | __ ) _ _ | \/ | __ _| | |__ ___ _ __ ___
|
||||
| | / _ \ / _` |/ _ \/ _` | | _ \| | | | | |\/| |/ _` | | '_ \ / _ \| '__/ _ \
|
||||
| |__| (_) | (_| | __/ (_| | | |_) | |_| | | | | | (_| | | |_) | (_) | | | (_) |
|
||||
\____\___/ \__,_|\___|\__,_| |____/ \__, | |_| |_|\__,_|_|_.__/ \___/|_| \___/
|
||||
|___/
|
||||
*/
|
||||
|
||||
sql.Query("CREATE TABLE IF NOT EXISTS permaprops('id' INTEGER NOT NULL, 'map' TEXT NOT NULL, 'content' TEXT NOT NULL, PRIMARY KEY('id'));")
|
||||
|
||||
if not PermaProps then PermaProps = {} end
|
||||
|
||||
PermaProps.SQL = {}
|
||||
|
||||
/* NOT WORKS AT THE MOMENT
|
||||
PermaProps.SQL.MySQL = false
|
||||
PermaProps.SQL.Host = "127.0.0.1"
|
||||
PermaProps.SQL.Username = "username"
|
||||
PermaProps.SQL.Password = "password"
|
||||
PermaProps.SQL.Database_name = "PermaProps"
|
||||
PermaProps.SQL.Database_port = 3306
|
||||
PermaProps.SQL.Preferred_module = "mysqloo"
|
||||
*/
|
||||
|
||||
function PermaProps.SQL.Query( data )
|
||||
|
||||
return sql.Query( data )
|
||||
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
PLUGIN.name = "Build Tools"
|
||||
PLUGIN.author = "Various (Портирован для Helix)"
|
||||
PLUGIN.description = "Набор строительных инструментов: AdvDupe2, Permaprops, Precision Tool"
|
||||
|
||||
-- Устанавливаем высокий приоритет загрузки, чтобы плагин загрузился раньше инструментов
|
||||
PLUGIN.priority = 100
|
||||
|
||||
-- Конфигурация
|
||||
ix.config.Add("buildToolsEnabled", true, "Включить строительные инструменты", nil, {
|
||||
category = "Build Tools"
|
||||
})
|
||||
|
||||
ix.config.Add("buildToolsAdvDupe2", true, "Включить AdvDupe2 (дублирование)", nil, {
|
||||
category = "Build Tools"
|
||||
})
|
||||
|
||||
ix.config.Add("buildToolsPermaprops", true, "Включить Permaprops (постоянные пропы)", nil, {
|
||||
category = "Build Tools"
|
||||
})
|
||||
|
||||
ix.config.Add("buildToolsPrecision", true, "Включить Precision Tool (точное позиционирование)", nil, {
|
||||
category = "Build Tools"
|
||||
})
|
||||
|
||||
ix.config.Add("buildToolsNoCollideWorld", true, "Включить NoCollide World (отключение коллизий с миром)", nil, {
|
||||
category = "Build Tools"
|
||||
})
|
||||
|
||||
-- Загрузка NetStream (библиотека для сетевых сообщений)
|
||||
ix.util.Include("netstream.lua")
|
||||
|
||||
-- Загрузка AdvDupe2
|
||||
if ix.config.Get("buildToolsAdvDupe2", true) then
|
||||
-- Инициализация глобальной таблицы AdvDupe2
|
||||
AdvDupe2 = AdvDupe2 or {}
|
||||
print("[Build Tools] Initializing AdvDupe2...")
|
||||
|
||||
-- Shared
|
||||
ix.util.Include("advdupe2/sh_codec_legacy.lua")
|
||||
ix.util.Include("advdupe2/sh_codec.lua")
|
||||
|
||||
-- Server
|
||||
ix.util.Include("advdupe2/sv_file.lua")
|
||||
ix.util.Include("advdupe2/sv_ghost.lua")
|
||||
ix.util.Include("advdupe2/sv_clipboard.lua")
|
||||
ix.util.Include("advdupe2/sv_misc.lua")
|
||||
|
||||
-- Client
|
||||
ix.util.Include("advdupe2/cl_file.lua")
|
||||
ix.util.Include("advdupe2/cl_ghost.lua")
|
||||
ix.util.Include("advdupe2/file_browser.lua")
|
||||
|
||||
print("[Build Tools] AdvDupe2 loaded successfully")
|
||||
end
|
||||
|
||||
-- Загрузка Permaprops
|
||||
if ix.config.Get("buildToolsPermaprops", true) then
|
||||
-- Server
|
||||
ix.util.Include("permaprops/sv_lib.lua")
|
||||
ix.util.Include("permaprops/sv_menu.lua")
|
||||
ix.util.Include("permaprops/sv_permissions.lua")
|
||||
ix.util.Include("permaprops/sv_specialfcn.lua")
|
||||
ix.util.Include("permaprops/sv_sql.lua")
|
||||
|
||||
-- Client
|
||||
ix.util.Include("permaprops/cl_drawent.lua")
|
||||
ix.util.Include("permaprops/cl_menu.lua")
|
||||
end
|
||||
|
||||
function PLUGIN:Initialize()
|
||||
print("[Build Tools] Загружены строительные инструменты!")
|
||||
|
||||
if ix.config.Get("buildToolsAdvDupe2", true) then
|
||||
print("[Build Tools] AdvDupe2 активирован")
|
||||
end
|
||||
|
||||
if ix.config.Get("buildToolsPermaprops", true) then
|
||||
print("[Build Tools] Permaprops активирован")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user