Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
34 kB
1
Indexable
Never
AddCSLuaFile()

ENT.Type = "anim"
ENT.Base = "base_anim"

ENT.Editable		= true
ENT.PrintName		= "Collar"
ENT.Category 		= "Strongest Bonds"
ENT.Spawnable 		= true
ENT.AdminSpawnable 	= true


--Gmodstore addon support
local PLAYER = FindMetaTable("Player")

function PLAYER:IsCuffed()
	local wep = self:GetActiveWeapon()
	if IsValid(wep) and wep.Cuffs then
		return true,wep
	end
	
	return false
end
----------------------------------

if(SERVER)then
	util.AddNetworkString("collar_network_player")
	util.AddNetworkString("sb_network_master_options")
	util.AddNetworkString("sb_master_options_closed")
	util.AddNetworkString("sb_master_options")
	util.AddNetworkString("sb_pet_customizations")
	util.AddNetworkString("sb_pet_customizations_READ")
	util.AddNetworkString("sb_network_master")
	util.AddNetworkString("sb_network_rope")
	util.AddNetworkString("SB_NoMoreCollar")
	util.AddNetworkString("SB_masterAnchor")
	util.AddNetworkString("SB_forcefulRemove")
	util.AddNetworkString("SB_notify")
	util.AddNetworkString("SB_Concommand")
end



function ENT:Initialize()
	
	if(SERVER)then
		self:SetModel("models/charaplyar/collar/collar.mdl")

		self:SetMoveType( MOVETYPE_VPHYSICS )
		self:SetSolid( SOLID_VPHYSICS )
		self:PhysicsInit( SOLID_VPHYSICS )

	end
	
	if not(self.BoneName)then
		self.BoneName = "ValveBiped.Bip01_Neck1"
	end
	
	self.Roped = false
	self.Master = nil
	self:DrawShadow(false)
	
	if not(self.MasterOptions)then
		self.MasterOptions = {
			Rope_Length = 100,
			Rope_Type = "rope",
			Collar_Col = Color(255,255,255),
			Collar_Name = "Pet~"
		}
	end
	
	if not(self.Size)then
		self.Size = 1
	end
	
	if not(self.WhiteList)then
		self.WhiteList = {}
		if(IsValid(self.Owner))then
			table.insert(self.WhiteList, tostring(self.Owner))
		end
	end
	
	if(SERVER)then
		self:SetUseType( CONTINUOUS_USE )
	end
	
end

function VectorRotate(vector, ang)

	local up = ang:Up()
	local right = ang:Right()
	local forward = ang:Forward()

	return up * vector.z + right * vector.x + forward * vector.y
end

function ENT:GetPositionsFromUser(ply)
	local pos = Vector(0,0,0)
	local ang = Angle(0,0,0)
	
	pos = ply:GetPos()
	ang = ply:GetAngles()
	
	local boneName = self.BoneName
	
	if not(boneName)then
		boneName = "ValveBiped.Bip01_Neck1"
	end
	
	if(boneName)then
	
		local boneId = (ply:LookupBone(boneName) or 0)
	
		pos, ang = ply:GetBonePosition(boneId)
		if(pos == ply:GetPos())then
			pos = ply:GetBoneMatrix(boneId):GetTranslation()
			ang = ply:GetBoneMatrix(boneId):GetAngles()
		end
		ang = Angle(0, ply:EyeAngles().y, 0)
	end
	
	pos = pos + (self.PosOffset or Vector(0,0,0))
	ang = ang + (self.AngOffset or Angle(0,0,0))
	
	local offseta = VectorRotate(self.RelPosOffset or Vector(0,0,0), ang)
	
	pos = pos + (offseta or Vector(0,0,0))
	
	
	return pos, ang
end


function NoMoreCollar(self)

	if(SERVER)then
		if not(self.EquipingTimr)then
			self.EquipingTimr = 0
		end
		if(CurTime() - self.EquipingTimr > 1)then
			if(IsValid(self.User))then
				--if not((self.User:IsCuffed() or false))then
					
					self.User.HasCollar = false
					
					self:SetPos(self.User:GetPos() + Vector(0,0,50))
				--end
			end
			
			
			self.Roped = false
			for i, a in ipairs(player.GetAll())do
				net.Start("sb_network_rope")
					net.WriteEntity(self)
					net.WriteBool(false)
				net.Send(a)
			end
			
			self.User = nil
			for i, a in pairs(player.GetAll())do
				net.Start("collar_network_player")
					net.WriteEntity(nil)
					net.WriteEntity(self)
				net.Send(a)
			end
			
			if(self:GetSolid() != SOLID_VPHYSICS)then
				self:SetMoveType( MOVETYPE_VPHYSICS )
				self:SetSolid( SOLID_VPHYSICS )
				self:PhysicsInit( SOLID_VPHYSICS )
				
				local phys = self:GetPhysicsObject()
				if(IsValid(phys))then
					phys:Wake()
				end
			end
			
			self.EquipingTimr = CurTime()
		end
	end
end
function ENT:EquipCollar(ply)
	if(SERVER)then
		if not(self.EquipingTimr)then
			self.EquipingTimr = 0
		end
		if(CurTime() - self.EquipingTimr > 1)then
			ply.HasCollar = true
			self.User = ply
			
			for i, a in pairs(player.GetAll())do
				net.Start("collar_network_player")
					net.WriteEntity(ply)
					net.WriteEntity(self)
				net.Send(a)
			end
			
			self:EmitSound("collar/collar_attach.wav")
			
			self:SetMoveType( MOVETYPE_VPHYSICS )
			self:SetSolid( SOLID_NONE )
			self:PhysicsInit( SOLID_NONE )
			

			
				
			net.Start("sb_pet_customizations_READ")
				net.WriteEntity(ply)
				net.WriteEntity(self)
			net.Send(ply)
			
			self.EquipingTimr = CurTime()
		end
	end
end

function ENT:Save()
	local data = self:Read(false)
	if not(data)then
		data = {}
	end
	if(self.User)then
		local pm = self.User:GetModel()
		
		data[pm] = {
			PosOffset = self.PosOffset,
			RelPosOffset = self.RelPosOffset,
			AngOffset = self.AngOffset,
			Size = self.Size,
			BoneName = self.BoneName,
			WhiteList = self.WhiteList
		}
		
		local dataS = util.TableToJSON( data )
		file.Write("strongest_bonds_DATA.txt", dataS)
		
	end
end
function ENT:Read(shouldValue)
	if(shouldValue == nil)then
		shouldValue = true
	end
	local dataS = file.Read("strongest_bonds_DATA.txt", "DATA" )
	if(dataS)then
		local data = util.JSONToTable( dataS )
		if(data)then
			local userData = nil
			if(self.User)then
				local pm = self.User:GetModel()
				if(data[pm])then
					if(shouldValue)then
						self.PosOffset = data[pm].PosOffset
						self.RelPosOffset = data[pm].RelPosOffset
						self.AngOffset = data[pm].AngOffset
						self.Size = data[pm].Size
						self.BoneName = data[pm].BoneName
						self.WhiteList = data[pm].WhiteList
					end
					userData = data[pm]
				end
			end
			return data, userData
		end
	else
		return
	end
end

function OptionsMenu(ply)

	if(CLIENT)then
		if not(ply.InMenus)then
			ply.InMenus = true
			local self = ply.Collar
			
			if not(self)then return end
			
			local us = ents.FindInSphere(ply:GetPos(), 200)
			for i, a in pairs(us)do
				if(IsValid(a))then
					if(a:GetClass() == "sb_collar")then
						us = a
						break
					end
				end
			end
		
			local W = ScrW() * 0.5
			local H = ScrH() * 0.5
			
		
			local frame = vgui.Create( "DFrame" )
			frame:SetPos( ScrW()/2 - W/2, ScrH()/2 - H/2 )
			frame:SetSize( W, H )
			frame:SetTitle( "Collar-Customization" )
			frame:SetDraggable(true)
			frame:MakePopup()
			
			frame.OnClose = function()
				ply.InMenus = false
				self:Save()
			end
			
			local function CreateSliderHere(posMult, sizeMult, text, mina, maxa, default)
				local slidah = vgui.Create( "DNumSlider", frame )
				local xSize = W * 0.25 * (sizeMult[1] or 1)
				local ySize = H * 0.09 * (sizeMult[2] or 1)
				
				slidah:SetPos( W * (posMult[1] or 0.5) - xSize/2, H * (posMult[2] or 0.5) - ySize/2)
				slidah:SetSize( xSize, ySize)
				slidah:SetText( text )
				slidah:SetMin( mina )
				slidah:SetMax( maxa )
				slidah:SetDecimals( 1 )
				slidah:SetValue(default or 0) 
				
				return slidah
			end
			
			local maximaRange = 25
			
			
			local _, loadData = self:Read(true)
			
			local posOffset = self.PosOffset or Vector(0,0,0)
			local posOffsetRel = self.RelPosOffset or Vector(0,0,0)
			local angleOffset = self.AngOffset or Angle(0,0,0)
			local size = self.Size or 1
			local ourBone = self.BoneName or "ValveBiped.Bip01_Neck1"
			local whiteList = self.WhiteList or {}
			if(loadData)then
				posOffset = loadData.PosOffset or Vector(0,0,0)
				posOffsetRel = loadData.RelPosOffset or Vector(0,0,0)
				angleOffset = loadData.AngOffset or Angle(0,0,0)
				size = loadData.Size or 1
				ourBone = loadData.BoneName or "ValveBiped.Bip01_Neck1"
				whiteList = loadData.WhiteList or {}
			end
			
			local xPosOffset = CreateSliderHere({0.22, 0.2}, {1.5,2}, "X - Pos Offset", -maximaRange, maximaRange, posOffset.x)
			local yPosOffset = CreateSliderHere({0.22, 0.3}, {1.5,2}, "Y - Pos Offset", -maximaRange, maximaRange, posOffset.y)
			local zPosOffset = CreateSliderHere({0.22, 0.4}, {1.5,2}, "Z - Pos Offset", -maximaRange, maximaRange, posOffset.z)
			
			
			local xPosOffsetRel = CreateSliderHere({0.22, 0.6}, {1.5,2}, "X - Pos Offset (Relative)", -maximaRange, maximaRange, posOffsetRel.x)
			local yPosOffsetRel = CreateSliderHere({0.22, 0.7}, {1.5,2}, "Y - Pos Offset (Relative)", -maximaRange, maximaRange, posOffsetRel.y)
			local zPosOffsetRel = CreateSliderHere({0.22, 0.8}, {1.5,2}, "Z - Pos Offset (Relative)", -maximaRange, maximaRange, posOffsetRel.z)

			local function AngleGetFunc(position, radius)
				local num = nil
				local x, y = input.GetCursorPos()
				
				local ourW = ScrW()/2 - W/2
				local ourH = ScrH()/2 - H/2
				
				local dif = Vector(ourW + position.x, ourH + position.y, 0) - Vector(x, y, 0)
				if(dif:Length() <= radius)then
					local norm = dif:GetNormalized()
					local xEnd = norm.x
					local yEnd = norm.y
					
					num = ((math.atan2(yEnd, xEnd) * 180/math.pi) + 180)
				end
				
				if(num)then
					if(input.IsMouseDown(MOUSE_RIGHT))then
						num = 0
					end
				end
				
				return num
			end
			local function DrawAngle(data)
				local xPos = data.X
				local yPos = data.Y
				local num = data.Num
				local radius = data.Radius
				local col = data.Color
				
				surface.DrawCircle(xPos, yPos, radius, col or Color(255, 255, 255, 255) )
				
				local x = math.cos(num * math.pi/180)
				local y = math.sin(num * math.pi/180)
				
				surface.DrawLine(xPos, yPos, xPos + x*radius, yPos + y*radius )
				
				draw.DrawText(math.Round(num), "DermaDefault", xPos + radius + W*0.025, yPos, col, TEXT_ALIGN_CENTER )
				
			end
			
			
			local anglersXPos = 0.6
			
			
			local sizeSlider = vgui.Create( "DAlphaBar", frame )
			local sizW = W * 0.03
			local sizH = H * 0.4
			
			local remap = 5
			
			sizeSlider:SetPos( W * 0.8 - sizW/2, H * 0.5 - sizH/2 )
			sizeSlider:SetSize( sizW, sizH)
			sizeSlider:SetValue( size / remap )
			sizeSlider.OnChange = function( self, newvalue )
				size = math.Round(newvalue * remap, 2)
			end
			
			
			local boneList = vgui.Create( "DComboBox", frame )
			boneList:SetPos( W * 0.25, H * 0.9 )
			boneList:SetSize( W * 0.25, H * 0.05 )
			boneList:SetValue( "Bone-Parrent" )
			
			local bones = {}
			for i = 1,100 do
				local bon = ply:GetBoneName(i)
				if(bon && bon != "__INVALIDBONE__")then
					if not(string.find(string.lower(bon), "finger"))then
						table.insert(bones, bon)
					end
				end
			end
			
			for i,a in ipairs(bones)do
				boneList:AddChoice(a)
			end
			boneList.OnSelect = function( self, index, value )
				ourBone = value
			end
			
			
			local masterList = vgui.Create( "DListView", frame )
			masterList:SetPos(W * 0.6, H * 0.85)
			masterList:SetSize(W * 0.3, H * 0.15)
			masterList:SetMultiSelect( false )
			masterList:AddColumn( "Masters" )
			masterList:AddColumn( "Unwanted" )
			
			local function PopulateMasterList()
			
				for i, a in ipairs(player.GetAll())do
					if(IsValid(a))then
						if(a != self.User)then
							if(table.HasValue(whiteList, tostring(a)))then
								masterList:AddLine(tostring(a), "click-to-switch")
							else
								masterList:AddLine("click-to-switch", tostring(a))
							end
						end
					end
				end
			end
			
			PopulateMasterList()
			
			masterList.OnRowSelected = function( lst, index, pnl )
			
				local value = pnl:GetColumnText(1)
				if(value == "click-to-switch")then
					value = pnl:GetColumnText(2)
				end
			
				if not(table.HasValue(whiteList, value))then
					table.insert(whiteList, value)
				else
					table.RemoveByValue(whiteList, value)
				end
				masterList:Clear()
				
				PopulateMasterList()
				
			end
			
			frame.Think = function()
				if(input.IsMouseDown(MOUSE_LEFT))then
					local angl = AngleGetFunc({x = W * anglersXPos, y = H * 0.3}, 50)
					local angl2 = AngleGetFunc({x = W * anglersXPos, y = H * 0.5}, 50)
					local angl3 = AngleGetFunc({x = W * anglersXPos, y = H * 0.7}, 50)
					
					if(angl)then
						angleOffset.x = angl
					end
					if(angl2)then
						angleOffset.y = angl2
					end
					if(angl3)then
						angleOffset.z = angl3
					end
				end
				
				posOffset = Vector(xPosOffset:GetValue(),yPosOffset:GetValue(),zPosOffset:GetValue())
				posOffsetRel = Vector(xPosOffsetRel:GetValue(),yPosOffsetRel:GetValue(),zPosOffsetRel:GetValue())
				
				if(IsValid(us))then
					us.PosOffset = posOffset
					us.RelPosOffset = posOffsetRel
					us.AngOffset = angleOffset
					us.Size = size
					us.BoneName = ourBone
					us.WhiteList = whiteList
					
					local customizations = {
					pOfset = posOffset,
					rPOfset = posOffsetRel,
					angOff = angleOffset,
					size = size,
					boneName = ourBone,
					WhiteList = util.TableToJSON(whiteList) or ""}
					
					net.Start("sb_pet_customizations")
						net.WriteEntity(us)
						net.WriteTable(customizations)
						net.WriteEntity(ply)
					net.SendToServer()
				end
				
				
				
				--code from gmod itself:
				local mousex = math.Clamp( gui.MouseX(), 1, ScrW() - 1 )
				local mousey = math.Clamp( gui.MouseY(), 1, ScrH() - 1 )

				if ( frame.Dragging ) then

					local x = mousex - frame.Dragging[1]
					local y = mousey - frame.Dragging[2]

					-- Lock to screen bounds if screenlock is enabled
					if ( frame:GetScreenLock() ) then

						x = math.Clamp( x, 0, ScrW() - frame:GetWide() )
						y = math.Clamp( y, 0, ScrH() - frame:GetTall() )

					end

					frame:SetPos( x, y )

				end
				
				
				local screenX, screenY = frame:LocalToScreen( 0, 0 )
				
				
				if ( frame.Hovered && frame:GetDraggable() && mousey < ( screenY + 24 ) ) then
					frame:SetCursor( "sizeall" )
					return
				end
				frame:SetCursor( "arrow" )

				-- Don't allow the frame to go higher than 0
				if ( frame.y < 0 ) then
					frame:SetPos( frame.x, 0 )
				end
				------------------------
			end
			frame.Paint = function( self, w, h )
				draw.RoundedBox( 0, 0, 0, w, h, Color( 0, 0, 0, 100 ) )
				DrawAngle({
					X = W * anglersXPos,
					Y = H * 0.3,
					Num = angleOffset.x,
					Radius = 50,
					col = Color(0,255,255,255)
				})
				DrawAngle({
					X = W * anglersXPos,
					Y = H * 0.5,
					Num = angleOffset.y,
					Radius = 50,
					col = Color(0,255,255,255)
				})
				DrawAngle({
					X = W * anglersXPos,
					Y = H * 0.7,
					Num = angleOffset.z,
					Radius = 50,
					col = Color(0,255,255,255)
				})
				
				draw.DrawText("Angle-X", "DermaDefault", W * anglersXPos - W * 0.1, H * 0.3, Color(255,255,255,255), TEXT_ALIGN_CENTER )
				draw.DrawText("Angle-Y", "DermaDefault", W * anglersXPos - W * 0.1, H * 0.5, Color(255,255,255,255), TEXT_ALIGN_CENTER )
				draw.DrawText("Angle-Z", "DermaDefault", W * anglersXPos - W * 0.1, H * 0.7, Color(255,255,255,255), TEXT_ALIGN_CENTER )
				
				
				draw.DrawText("Size", "DermaDefault", W * 0.9, H * 0.45, Color(255,255,255,255), TEXT_ALIGN_CENTER )
				draw.DrawText(size, "DermaDefault", W * 0.9, H * 0.5, Color(255,255,255,255), TEXT_ALIGN_CENTER )
				
				
			end
			
		end
	end
end

net.Receive("sb_network_master_options", function()
	local self = net.ReadEntity()
	local tableOpt = net.ReadTable()
	
	if(CLIENT)then
		self.MasterOptions = tableOpt
	end
	if(SERVER)then
		self.MasterOptions = tableOpt
		
		for i,a in ipairs(player.GetAll())do
			net.Start("sb_network_master_options")
				net.WriteEntity(self)
				net.WriteTable(tableOpt)
			net.Send(a)
		end
	end
end)

net.Receive("sb_pet_customizations_READ", function()
	local ply = net.ReadEntity()
	local self = net.ReadEntity()
	
	self:Read(true)
	
	local customizations = {
	pOfset = self.PosOffset or Vector(0,0,0),
	rPOfset = self.PosOffsetRel or Vector(0,0,0),
	angOff = self.AngOffset or Angle(0,0,0),
	size = self.Size or 1,
	boneName = self.BoneName or "ValveBiped.Bip01_Neck1",
	WhiteList = util.TableToJSON(self.WhiteList) or ""}
	
	
	net.Start("sb_pet_customizations")
		net.WriteEntity(self)
		net.WriteTable(customizations)
		net.WriteEntity(ply)
	net.SendToServer()

end)

net.Receive("sb_pet_customizations", function()
	local self = net.ReadEntity()
	local tableVars = net.ReadTable()
	local ply = net.ReadEntity()
	
	local pOfset = tableVars.pOfset
	local rPOfset = tableVars.rPOfset
	local angOff = tableVars.angOff
	local size = tableVars.size
	local boneName = tableVars.boneName
	local whiteList = util.JSONToTable(tableVars.WhiteList or "")
	
	if(IsValid(self))then
		if(CLIENT)then
			self.PosOffset = pOfset
			self.RelPosOffset = rPOfset
			self.AngOffset = angOff
			self.Size = size
			self.BoneName = boneName
			self.WhiteList = whiteList
		end
		if(SERVER)then
			self.PosOffset = pOfset
			self.RelPosOffset = rPOfset
			self.AngOffset = angOff
			self.Size = size
			self.BoneName = boneName
			self.WhiteList = whiteList
			
			for i,a in ipairs(player.GetAll())do
				if(a != ply)then
					net.Start("sb_pet_customizations")
						net.WriteEntity(self)
						net.WriteTable(tableVars)
						net.WriteEntity(ply)
					net.Send(a)
				end
			end
		end
	end
end)

function MasterOptionsMenu(ply)
	
	if(CLIENT)then
			local us = LocalPlayer()
			
			if !IsValid(us.Pet) then return end
			
			if !IsValid(us.Pet.Collar) then return end
			
			us = us.Pet.Collar
			
			local W = ScrW() * 0.3
			local H = ScrH() * 0.4
			
		
			local frame = vgui.Create( "DFrame" )
			frame:SetPos( ScrW()/2 - W/2, ScrH()/2 - H/2 )
			frame:SetSize( W, H )
			frame:SetTitle( "Masters~Menu" )
			frame:SetDraggable( true )
			frame:MakePopup()
			
			frame.OnClose = function()
				net.Start("sb_master_options_closed")
					net.WriteEntity(ply)
				net.SendToServer()
			end
			
			local ropeLength = us.MasterOptions.Rope_Length or 100
			local ropeWidth = us.MasterOptions.RopeWidth or 1
			local ropeType = us.MasterOptions.Rope_Type or "rope"
			local collarColour = us.MasterOptions.Collar_Col or Color(255,255,255)
			local collarName = us.MasterOptions.Collar_Name	or "Pet~"
			local bodygroup = us.MasterOptions.HasBodygroups or 0
			
			local lengthSlid = vgui.Create( "DNumSlider", frame )
			local xSize = W * 0.5
			local ySize = H * 0.09
			
			lengthSlid:SetPos( W * 0.3 - xSize/2, H * 0.16 - ySize/2)
			lengthSlid:SetSize( xSize, ySize)
			lengthSlid:SetText( "Rope Length" )
			lengthSlid:SetMin( 5 )
			lengthSlid:SetMax( 500 )
			lengthSlid:SetDecimals(0)
			lengthSlid:SetValue(ropeLength) 
			
			
			local widthSlid = vgui.Create( "DNumSlider", frame )
			widthSlid:SetPos( W * 0.3 - xSize/2, H * 0.25 - ySize/2)
			widthSlid:SetSize( xSize, ySize)
			widthSlid:SetText( "Rope Width" )
			widthSlid:SetMin( 0.1 )
			widthSlid:SetMax( 5 )
			widthSlid:SetDecimals(2)
			widthSlid:SetValue(ropeWidth) 
			
			local ropeTypeB = vgui.Create( "DComboBox", frame )
			ropeTypeB:SetPos( W * 0.1, H * 0.3 )
			ropeTypeB:SetSize( W * 0.3, H * 0.075 )
			ropeTypeB:SetValue( ropeType )
			
			ropeTypeB:AddChoice("rope")
			ropeTypeB:AddChoice("chain")
			ropeTypeB:AddChoice("elastic")
			
			ropeTypeB.OnSelect = function( self, index, value )
				ropeType = value
			end
			
			local colCol = vgui.Create("DColorMixer", frame)
			colCol:SetPos(W * 0.5, H * 0.3)
			colCol:SetSize(W * 0.4, H * 0.3)
			colCol:SetPalette(false)  			-- Show/hide the palette 				DEF:true
			colCol:SetAlphaBar(false) 			-- Show/hide the alpha bar 				DEF:true
			colCol:SetWangs(true) 				-- Show/hide the R G B A indicators 	DEF:true
			colCol:SetColor(collarColour) 	-- Set the default color
			
			colCol.ValueChanged = function(_, col)
				collarColour = Color(col.r, col.g, col.b)
			end
			
			
			local cNameEntry = vgui.Create( "DTextEntry", frame )
			local sizW = W * 0.5
			local sizH = H * 0.08
			cNameEntry:SetPos(W * 0.5 - sizW/2, H * 0.8 - sizH/2)
			cNameEntry:SetSize(sizW, sizH)
			cNameEntry:SetValue( collarName )
			cNameEntry.OnEnter = function( self )
				local name = self:GetValue()
				
				collarName = name
			end
			
			local bodySlider = vgui.Create( "DNumSlider", frame )
			bodySlider:SetPos( W * 0.3 - xSize/2, H * 0.5 - ySize/2)
			bodySlider:SetSize( xSize, ySize)
			bodySlider:SetText( "BodyGroup" )
			bodySlider:SetMin( 0 )
			bodySlider:SetMax( 3 )
			bodySlider:SetDecimals(0)
			bodySlider:SetValue(bodygroup)
			
			frame.Think = function()
				
				ropeLength = lengthSlid:GetValue()
				ropeWidth = widthSlid:GetValue()
				
				bodygroup = math.Round(bodySlider:GetValue())
				
				if(IsValid(us))then
					local tableOptions = {
						Rope_Length = ropeLength,
						RopeWidth = ropeWidth,
						Rope_Type = ropeType,
						Collar_Col = collarColour,
						Collar_Name = collarName,
						HasBodygroups = bodygroup
					}
				
					net.Start("sb_network_master_options")
						net.WriteEntity(us)
						net.WriteTable(tableOptions)
					net.SendToServer()
				end
			end
			
			frame.Paint = function( self, w, h )
				draw.RoundedBox( 0, 0, 0, w, h, Color( 0, 0, 0, 200 ) )
			end
		end
end

function ENT:MaterialFromType()
	local typee = self.MasterOptions.Rope_Type or "rope"
	if(typee == "rope")then
		return "cable/rope"
	end
	
	if(typee == "chain")then
		return "cable/custom_chains2"
	end
	
	if(typee == "elastic")then
		return "cable/cable2"
	end
	
	
	return "cable/rope"
end

function ENT:RenderRope()
	if(SERVER)then
	
		local function removeOurStuff()
			if(IsValid(self.TheRope))then
				self.TheRope:Remove()
			end
			if(IsValid(self.TheAnchor))then
				self.TheAnchor:Remove()
			end
			self.TheRope = nil
			self.TheAnchor = nil
		end
	
		if(IsValid(self.User) && IsValid(self.Master))then
			if(self.Roped)then
				if not(self.TheRope)then
					if not(self.TheAnchor)then
						self.TheAnchor = constraint.CreateStaticAnchorPoint(self.Master:GetPos())
					end
				
					self.TheRope = constraint.CreateKeyframeRope(self:GetPos(), 4, self:MaterialFromType(), nil, self, Vector(0,self.Size * 5,0), 0, self.TheAnchor, Vector(0,0,0), 0, {Collide = 1})
					
					self:EmitSound("collar/leash_attach.wav")							
				end
				if(IsValid(self.TheRope))then
				
					self.TheRope:SetMaterial(self:MaterialFromType())
					self.TheRope:SetKeyValue("Rope Material", self:MaterialFromType())
					self.TheRope:SetKeyValue("Length", self.MasterOptions.Rope_Length)
					self.TheRope:SetKeyValue("Slack", 25)
					self.TheRope:SetKeyValue("Width", self.MasterOptions.RopeWidth or 1)
					self.TheRope:SetKeyValue("Subdivision", 5)
				end
				if(IsValid(self.TheAnchor))then
					if not(self.TheAnchor.WorldPositioning)then
						local plyBone = (self.Master:LookupBone("ValveBiped.Bip01_R_Hand") or self.Master:LookupBone("ValveBiped.Bip01_R_Wrist") or self.Master:LookupBone("ValveBiped.Anim_Attachment_RH")) or 0
						local pos = self.Master:GetBonePosition(plyBone)
						if(pos == self.Master:GetPos())then
							pos = self.Master:GetBoneMatrix(plyBone):GetTranslation()
						end
						
						self.TheAnchor:SetPos(pos)
					else
						self.TheAnchor:SetPos(self.TheAnchor.WorldPositioning)
					end
				end
			else
				removeOurStuff()
			end
		else
			removeOurStuff()
		end
	end
end


function ColorMath(col, col1, operation)
	if not(operation)then
		return col
	end
	if(operation == "minus")then
		return (Color(col.r - col1.r, col.g - col1.g, col.b - col1.b))
	end
	if(operation == "plus")then
		return (Color(col.r + col1.r, col.g + col1.g, col.b + col1.b))
	end
	if(operation == "multiply")then
		return (Color(col.r * col1.r, col.g * col1.g, col.b * col1.b))
	end
	if(operation == "divide")then
		return (Color(col.r / col1.r, col.g / col1.g, col.b / col1.b))
	end
end


function DoConcommand(conc, ply, args)
	if(CLIENT)then
		if(conc == "SB_Master_GetPets")then
			for i,a in ipairs(player.GetAll())do
				if(IsValid(a))then
					if(a != ply)then
						if(a.Collar)then
							if(IsValid(a.Collar))then
								if(a.Collar.Master == ply)then
									print(a:Nick())
								end
							end
						end
					end
				end
			end
		end
		if(conc == "SB_ForceRemoveMaster")then
			if(IsValid(ply))then
				if(ply.Collar)then
					if(IsValid(ply.Collar))then
						if not(ply.Collar.Roped)then
							net.Start("SB_forcefulRemove")
								net.WriteEntity(ply.Collar)
							net.SendToServer()
						end
					end
				end
			end
		end
		if(conc == "SB_Collar_MasterAnchor")then
			if(IsValid(ply))then
			
				net.Start("SB_masterAnchor")
					net.WriteEntity(ply)
				net.SendToServer()
				
			end
		end
		if(conc == "SB_Collar_Options")then
			OptionsMenu(ply)
		end
		if(conc == "SB_Collar_MasterOptions")then
			MasterOptionsMenu(ply)
		end
		if(conc == "SB_Collar_Remove")then
		
			local us = ply.Collar
			if(IsValid(us))then
				net.Start("SB_NoMoreCollar")
					net.WriteEntity(us)
				net.SendToServer()
			end
		end
	else
		net.Start("SB_Concommand")
			net.WriteString(conc)
			net.WriteEntity(ply)
		net.Send(ply)
	end

end

net.Receive("SB_Concommand", function()
	local conc = net.ReadString()
	local ply = net.ReadEntity()
	print(ply)
	DoConcommand(conc, ply)
end)


concommand.Add("SB_Master_GetPets", function(ply)
	DoConcommand("SB_Master_GetPets", ply)
end)

concommand.Add("SB_ForceRemoveMaster", function(ply)
	DoConcommand("SB_ForceRemoveMaster", ply)
end)

net.Receive("SB_forcefulRemove", function()
	local self = net.ReadEntity()
	
	self.Master = nil
	for i, a in ipairs(player.GetAll())do
		net.Start("sb_network_master")
			net.WriteEntity(self)
			net.WriteEntity(nil)
		net.Send(a)
	end
end)




concommand.Add("SB_Collar_MasterAnchor", function( ply, cmd, nyah, args )
	DoConcommand("SB_Collar_MasterAnchor", ply)
end)


net.Receive("SB_masterAnchor", function()
	
	local ply = net.ReadEntity()

	local pet = ply.Pet
	if(IsValid(pet))then
		local self = pet.Collar
		if(IsValid(self))then
			if(self.TheAnchor)then
				if not(self.TheAnchor.WorldPositioning)then
					if(ply:GetEyeTrace().Entity:IsWorld())then
						local thePos = ply:GetEyeTrace().HitPos
						if((ply:GetPos() - thePos):Length() < 200)then
							self.TheAnchor.WorldPositioning = thePos
						end
					end
				end
			end
		end
	end
end)



concommand.Add("SB_Collar_MasterOptions", function( ply, cmd, nyah, args )
	DoConcommand("SB_Collar_MasterOptions", ply)
end)
concommand.Add("SB_Collar_Options", function( ply, cmd, nyah, args )
	DoConcommand("SB_Collar_Options", ply)
end)

concommand.Add("SB_Collar_Remove", function( ply, cmd, nyah, args )
	DoConcommand("SB_Collar_Remove", ply)
end)

net.Receive("SB_NoMoreCollar", function()
	local us = net.ReadEntity()
	if(IsValid(us))then
		NoMoreCollar(us)
	end
end)

net.Receive("SB_notify", function()
	local stringi = net.ReadString()
	
	notification.AddLegacy(stringi, NOTIFY_HINT, 2)
end)

function ENT:Think()
	if(IsValid(self.User) && self.User:Alive())then
		if(CLIENT)then
			local pos, ang = self:GetPositionsFromUser(self.User)
			self:SetPos(pos)
			self:SetAngles(ang)
		end
		if not(self.RustleSound)then
			self.RustleSound = CurTime()
		end
		if not(self.RustleDelay)then
			self.RustleDelay = math.random(1,3)
		end
		if(CurTime() - self.RustleSound > self.RustleDelay)then
			if(self.User:GetVelocity():Length() > 50)then
				self:EmitSound("collar/chain/chains_move.wav", 70, math.random(90,110))
				self.RustleSound = CurTime()
				self.RustleDelay = math.random(1,3)
			end
		end
		
		self:SetColor(ColorMath(self.MasterOptions.Collar_Col, Color(255/2,255/2,255/2), "plus"))
		
		self.User.Collar = self
		
		local bNum = self.MasterOptions.HasBodygroups or 0
		if(bNum == 0)then
			self:SetBodygroup(1, 0)
			self:SetBodygroup(2, 0)
		elseif(bNum == 1)then
			self:SetBodygroup(1, 0)
			self:SetBodygroup(2, 1)
		elseif(bNum == 2)then
			self:SetBodygroup(1, 1)
			self:SetBodygroup(2, 0)
		else
			self:SetBodygroup(1, 1)
			self:SetBodygroup(2, 1)
		end
		
		if not(self.PetUseTimr)then
			self.PetUseTimr = 0
		end
		
		
		if(IsValid(self.Master))then
			self.Master.Pet = self.User
			if(SERVER)then
				if not(self.Master:Alive())then
					self.Roped = false
					
					for i, a in ipairs(player.GetAll())do
						net.Start("sb_network_rope")
							net.WriteEntity(self)
							net.WriteBool(false)
						net.Send(a)
					end
				end
			
				if(self.Roped)then
					--leash code here
					
					local leashPosition = self.Master:GetPos()
					if(self.TheAnchor)then
						if(self.TheAnchor.WorldPositioning)then
							leashPosition = self.TheAnchor.WorldPositioning
						end
					end
					
					local dif = leashPosition - self.User:GetPos()
					local difNum = dif:Length()
					
					local ropeLength = self.MasterOptions.Rope_Length
					
					if(difNum > ropeLength)then
					
						local dir = (ropeLength - difNum)
						local velo = dif:GetNormalized() * -dir
						self.User:SetVelocity(velo)
						
						
						if not(self.RopeSqueek)then
							self.RopeSqueek = CurTime()
						end
						if(CurTime() - self.RopeSqueek > 1)then
							self:EmitSound("collar/rope_squeek.wav", 60, math.random(80,120))
							self.RopeSqueek = CurTime()
						end
						
						if not(self.TheAnchor && self.TheAnchor.WorldPositioning)then
							self.Master:SetVelocity(velo / -5)
						end
					end
					
				end
			end
		end
		
	else
		NoMoreCollar(self)
	end	
	
	self:SetModelScale(self.Size or 1)
	
	self:RenderRope()
	
	
	if(SERVER)then
		for i, ply in ipairs(player.GetAll())do
			if(IsValid(self.Master))then
				if(ply == self.Master)then
					if not(table.HasValue(self.WhiteList, tostring(ply)))then --no longer allowed
						self.Master = nil
						for i, a in ipairs(player.GetAll())do
							net.Start("sb_network_master")
								net.WriteEntity(self)
								net.WriteEntity(nil)
							net.Send(a)
						end
					end
				end
			end
			
			
			if not(ply.MasterUsageDelay)then
				ply.MasterUsageDelay = CurTime()
			end
			
			if(CurTime() - ply.MasterUsageDelay > 0.5)then
				if(ply:KeyDown(IN_USE))then
					local looking = ply:GetEyeTrace().Entity
				
					if(IsValid(looking))then
						if(looking:IsPlayer())then
							if(looking == self.User)then
								local diff = looking:GetPos() - ply:GetPos()
								if(diff:Length() < 700)then
									if(IsValid(self.Master))then
										if(self.Master == ply)then
											self:DoMasterUsage(ply, looking)
										end
									else
										if(table.HasValue(self.WhiteList, tostring(ply)))then --is even a master~?
											self.Master = ply
											for i, a in ipairs(player.GetAll())do
												net.Start("sb_network_master")
													net.WriteEntity(self)
													net.WriteEntity(ply)
												net.Send(a)
											end
											
											net.Start("SB_notify")
												net.WriteString( "You became Master of " .. looking:Nick() .. "~")
											net.Send(ply)
											
											net.Start("SB_notify")
												net.WriteString(ply:Nick() .. " is now your Master~")
											net.Send(looking)
											sound.Play("garrysmod/content_downloaded.wav", ply:GetPos(), 75, 100, 1)
										end
									end
								end
							end
						end
					end
					
					ply.MasterUsageDelay = CurTime()
				end
			end
		end
	end
	
	self:NextThink( CurTime() ) -- Set the next think to run as soon as possible, i.e. the next frame.

	return true -- Apply NextThink call
	
end




function ENT:DoMasterUsage(ply, otherPly)


	if(ply:KeyDown(IN_WALK))then
		self.Master = nil
		for i, a in ipairs(player.GetAll())do
			net.Start("sb_network_master")
				net.WriteEntity(self)
				net.WriteEntity(nil)
			net.Send(a)
		end
		net.Start("SB_notify")
			net.WriteString( "You are no longer Master of " .. self.User:Nick() .. ".")
		net.Send(ply)
		
		net.Start("SB_notify")
			net.WriteString(ply:Nick() .. " is no longer your Master.")
		net.Send(self.User)
		sound.Play("garrysmod/content_downloaded.wav", ply:GetPos(), 75, 70, 1)
	else
	
		if(self.Roped)then
		
			self:EmitSound("collar/leash_attach.wav", 75, 70)
		
			self.Roped = false
			for i, a in ipairs(player.GetAll())do
				net.Start("sb_network_rope")
					net.WriteEntity(self)
					net.WriteBool(false)
				net.Send(a)
			end
		else
		
			self.Roped = true
			for i, a in ipairs(player.GetAll())do
				net.Start("sb_network_rope")
					net.WriteEntity(self)
					net.WriteBool(true)
				net.Send(a)
			end
		end
		
	end
	
end


net.Receive("collar_network_player", function()
	local ply = net.ReadEntity()
	local self = net.ReadEntity()
	
	self.User = ply
end)

net.Receive("sb_master_options_closed", function()
	local ply = net.ReadEntity()

	ply.InMenus = false
end)

net.Receive("sb_master_options", function()
	local self = net.ReadEntity()
	local master = net.ReadEntity()
	
	MasterOptionsMenu(self)
end)
net.Receive("sb_network_master", function()
	local self = net.ReadEntity()
	local master = net.ReadEntity()
	
	self.Master = master
end)
net.Receive("sb_network_rope", function()
	local self = net.ReadEntity()
	local rope = net.ReadBool()
	
	self.Roped = rope
end)

function ENT:Use(ply)
	if(IsValid(ply))then
		if not(IsValid(self.User))then
			if not(ply.HasCollar)then
				self:EquipCollar(ply)
			end
		end
	end

end

function ENT:OnRemove()
	if(IsValid(self.User))then
		self.User.HasCollar = false
	end
end

function ENT:Draw()

	

	if((not(LocalPlayer() == self.User && LocalPlayer() == LocalPlayer():GetViewEntity())) || LocalPlayer():ShouldDrawLocalPlayer())then
		self:DrawModel()
	end
	
end