Advertisement
wifiboost

Apply, Rotate, and Move.

Aug 5th, 2017
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 50.77 KB | None | 0 0
  1.  
  2. TOOL.Category       = "Constraints"
  3. TOOL.Name           = "#Precision"
  4. TOOL.Command        = nil
  5. TOOL.ConfigName     = ""
  6.  
  7. TOOL.ClientConVar[ "mode" ]             = "1"
  8. TOOL.ClientConVar[ "user" ]             = "1"
  9.  
  10. TOOL.ClientConVar[ "freeze" ]           = "1"
  11. TOOL.ClientConVar[ "nocollide" ]        = "1"
  12. TOOL.ClientConVar[ "nocollideall" ]     = "0"
  13. TOOL.ClientConVar[ "rotation" ]         = "15"
  14. TOOL.ClientConVar[ "rotate" ]           = "1"
  15. TOOL.ClientConVar[ "offset" ]           = "0"
  16. TOOL.ClientConVar[ "forcelimit" ]       = "0"
  17. TOOL.ClientConVar[ "torquelimit" ]      = "0"
  18. TOOL.ClientConVar[ "friction" ]         = "0"
  19. TOOL.ClientConVar[ "width" ]            = "1"
  20. TOOL.ClientConVar[ "offsetpercent" ]    = "1"
  21. TOOL.ClientConVar[ "removal" ]          = "0"
  22. TOOL.ClientConVar[ "move" ]             = "1"
  23. TOOL.ClientConVar[ "physdisable" ]      = "0"
  24. TOOL.ClientConVar[ "ShadowDisable" ]    = "0"
  25. TOOL.ClientConVar[ "allowphysgun" ]     = "0"
  26. TOOL.ClientConVar[ "autorotate" ]       = "0"
  27. TOOL.ClientConVar[ "entirecontrap" ]    = "0"
  28. TOOL.ClientConVar[ "nudge" ]            = "25"
  29. TOOL.ClientConVar[ "nudgepercent" ]     = "1"
  30.  
  31. //adv ballsocket
  32. TOOL.ClientConVar[ "XRotMin" ]      = "-180"
  33. TOOL.ClientConVar[ "XRotMax" ]      = "180"
  34. TOOL.ClientConVar[ "YRotMin" ]      = "-180"
  35. TOOL.ClientConVar[ "YRotMax" ]      = "180"
  36. TOOL.ClientConVar[ "ZRotMin" ]      = "-180"
  37. TOOL.ClientConVar[ "ZRotMax" ]      = "180"
  38. TOOL.ClientConVar[ "XRotFric" ]     = "0"
  39. TOOL.ClientConVar[ "YRotFric" ]     = "0"
  40. TOOL.ClientConVar[ "ZRotFric" ]     = "0"
  41. TOOL.ClientConVar[ "FreeMov" ]      = "0"
  42.  
  43. //Removal
  44. TOOL.ClientConVar[ "removal_nocollide" ]    = "1"
  45. TOOL.ClientConVar[ "removal_weld" ]         = "1"
  46. TOOL.ClientConVar[ "removal_axis" ]         = "1"
  47. TOOL.ClientConVar[ "removal_ballsocket" ]   = "1"
  48. TOOL.ClientConVar[ "removal_advballsocket" ]= "1"
  49. TOOL.ClientConVar[ "removal_parent" ]       = "1"
  50. TOOL.ClientConVar[ "removal_other" ]        = "1"
  51.  
  52.  
  53. TOOL.ClientConVar[ "enablefeedback" ]   = "1"
  54. TOOL.ClientConVar[ "chatfeedback" ]     = "1"
  55. TOOL.ClientConVar[ "nudgeundo" ]        = "0"
  56. TOOL.ClientConVar[ "moveundo" ]         = "1"
  57. TOOL.ClientConVar[ "rotateundo" ]       = "1"
  58.  
  59. function TOOL:DoParent( Ent1, Ent2 )
  60.     local TempEnt = Ent2
  61.     if !(Ent1 && Ent1:IsValid() && Ent1:EntIndex() != 0) then
  62.         self:SendMessage( "Oops, First Target was world or something invalid" )
  63.         return
  64.     end
  65.     if !(Ent2 && Ent2:IsValid() && Ent2:EntIndex() != 0) then
  66.         self:SendMessage( "Oops, Second Target was world or something invalid" )
  67.         return
  68.     end
  69.     if ( Ent1 == Ent2 ) then
  70.         self:SendMessage( "Oops, Can't parent something to itself" )
  71.         return
  72.     end
  73.     Ent1:SetMoveType(MOVETYPE_NONE)
  74.     local disablephysgun = self:GetClientNumber( "allowphysgun" ) == 0
  75.     Ent1.PhysgunDisabled = disablephysgun
  76.     Ent1:SetUnFreezable( disablephysgun )
  77.     local Phys1 = Ent1:GetPhysicsObject()
  78.     if Phys1:IsValid() then
  79.         Phys1:EnableCollisions( false )
  80.     end
  81.     while true do
  82.         if ( !TempEnt:GetParent():IsValid() ) then
  83.             Ent1:SetParent( Ent2 )
  84.             if self:GetClientNumber( "entirecontrap" ) == 0 then self:SendMessage( "Parent Set." ) end
  85.             Phys1:Wake()
  86.             break
  87.         elseif ( TempEnt:GetParent() == Ent1 ) then
  88.             UndoParent( TempEnt )
  89.             timer.Simple( 0.1, function()//delay to stop crash
  90.                 Ent1.SetParent( Ent1, Ent2)
  91.             end)
  92.             self:SendMessage( "Oops, Closed Parent Loop Detected; Broken loop and set parent." )
  93.             break
  94.         else
  95.             TempEnt = TempEnt:GetParent()
  96.         end
  97.     end
  98.     Phys1:Wake()
  99.     //Phys1:UpdateShadow(Ent1:GetAngles(),Ent1:GetAngles())
  100. end
  101.  
  102. function TOOL:UndoParent( Ent1 )
  103.     Ent1:SetParent( nil )
  104.     Ent1:SetMoveType(MOVETYPE_VPHYSICS)
  105.     Ent1.PhysgunDisabled = false
  106.     Ent1:SetUnFreezable( false )
  107.     local Phys1 = Ent1:GetPhysicsObject()
  108.     if Phys1:IsValid() then
  109.         Phys1:EnableCollisions( true )
  110.         Phys1:Wake()
  111.         //Phys1:UpdateShadow(Ent1:GetAngles(),Ent1:GetAngles())
  112.     end
  113. end
  114.  
  115. function TOOL:DoApply(CurrentEnt, FirstEnt, autorotate, nocollideall, ShadowDisable )
  116.     local CurrentPhys = CurrentEnt:GetPhysicsObject()
  117.    
  118.     //local col = CurrentEnt:GetCollisionGroup()
  119.     //col = 19
  120.     //CurrentEnt:SetCollisionGroup(col)
  121.     //self:SendMessage("New group: "..col)
  122.    
  123.     //if ( CurrentPhys:IsDragEnabled() ) then
  124.     //end
  125.     //CurrentPhys:SetAngleDragCoefficient(1.05)
  126.     //CurrentPhys:SetDragCoefficient(1.05)
  127.    
  128.     if ( autorotate ) then
  129.         if ( CurrentEnt == FirstEnt ) then//Snap-rotate original object first.  Rest needs to rotate around it.
  130.             local angle = CurrentPhys:RotateAroundAxis( Vector( 0, 0, 1 ), 0 )
  131.             self.anglechange = Vector( angle.p - (math.Round(angle.p/45))*45, angle.r - (math.Round(angle.r/45))*45, angle.y - (math.Round(angle.y/45))*45 )
  132.             if ( table.Count(self.TaggedEnts) == 1 ) then
  133.                 angle.p = (math.Round(angle.p/45))*45
  134.                 angle.r = (math.Round(angle.r/45))*45//Only rotate on these axies if its singular.
  135.             end
  136.             angle.y = (math.Round(angle.y/45))*45
  137.             CurrentPhys:SetAngles( angle )
  138.         else
  139.             local distance = math.sqrt(math.pow((CurrentEnt:GetPos().X-FirstEnt:GetPos().X),2)+math.pow((CurrentEnt:GetPos().Y-FirstEnt:GetPos().Y),2))
  140.             local theta = math.atan((CurrentEnt:GetPos().Y-FirstEnt:GetPos().Y) / (CurrentEnt:GetPos().X-FirstEnt:GetPos().X)) - math.rad(self.anglechange.Z)
  141.             if (CurrentEnt:GetPos().X-FirstEnt:GetPos().X) < 0 then
  142.                 CurrentEnt:SetPos( Vector( FirstEnt:GetPos().X - (distance*(math.cos(theta))), FirstEnt:GetPos().Y - (distance*(math.sin(theta))), CurrentEnt:GetPos().Z ) )
  143.             else
  144.                 CurrentEnt:SetPos( Vector( FirstEnt:GetPos().X + (distance*(math.cos(theta))), FirstEnt:GetPos().Y + (distance*(math.sin(theta))), CurrentEnt:GetPos().Z ) )
  145.             end
  146.             CurrentPhys:SetAngles( CurrentPhys:RotateAroundAxis( Vector( 0, 0, -1 ), self.anglechange.Z ) )
  147.         end
  148.     end
  149.  
  150.     CurrentPhys:EnableCollisions( !nocollideall )
  151.     CurrentEnt:DrawShadow( !ShadowDisable )
  152.     if physdis then
  153.         CurrentEnt:SetMoveType(MOVETYPE_NONE)
  154.         CurrentEnt.PhysgunDisabled = disablephysgun
  155.         CurrentEnt:SetUnFreezable( disablephysgun )
  156.     else
  157.         CurrentEnt:SetMoveType(MOVETYPE_VPHYSICS)
  158.         CurrentEnt.PhysgunDisabled = false
  159.         CurrentEnt:SetUnFreezable( false )
  160.     end
  161.     CurrentPhys:Wake()
  162. end
  163.  
  164. function TOOL:CreateUndo(constraint,undoname)
  165.     if (constraint) then
  166.         undo.Create(undoname)
  167.         undo.AddEntity( constraint )
  168.         undo.SetPlayer( self:GetOwner() )
  169.         undo.Finish()
  170.         self:GetOwner():AddCleanup( "constraints", constraint )
  171.     end
  172. end
  173.  
  174. function TOOL:UndoRepairToggle()
  175.     for key,CurrentEnt in pairs(self.TaggedEnts) do
  176.         if ( CurrentEnt and CurrentEnt:IsValid() ) then
  177.             if !(CurrentEnt == Ent2 ) then
  178.                 local CurrentPhys = CurrentEnt:GetPhysicsObject()
  179.                 if ( CurrentPhys:IsValid() && !CurrentEnt:GetParent():IsValid() ) then//parent?
  180.                     if ( CurrentEnt:GetPhysicsObjectCount() < 2 ) then //not a ragdoll
  181.                         if ( CurrentEnt:GetCollisionGroup() == COLLISION_GROUP_WORLD ) then
  182.                             CurrentEnt:SetCollisionGroup( COLLISION_GROUP_NONE )
  183.                         elseif ( CurrentEnt:GetCollisionGroup() == COLLISION_GROUP_NONE ) then
  184.                             CurrentEnt:SetCollisionGroup( COLLISION_GROUP_WORLD )
  185.                         end
  186.                         if ( speeddamp == 0 && angledamp == 0 ) then
  187.                             CurrentPhys:SetDamping( 5, 5 )
  188.                         elseif ( speeddamp == 5 && angledamp == 5 ) then
  189.                             CurrentPhys:SetDamping( 0, 0 )
  190.                         end
  191.                         CurrentPhys:Wake()
  192.                     end
  193.                 end
  194.             end
  195.         end
  196.     end
  197.     self.RepairTodo = false
  198. end
  199.  
  200. function TOOL:DoConstraint(mode)
  201.     self:SetStage(0)
  202.     // Get information we're about to use
  203.     local Ent1,  Ent2  = self:GetEnt(1),    self:GetEnt(2)
  204.  
  205.     if ( !Ent1:IsValid() || CLIENT ) then
  206.         self:ClearObjects()
  207.         return false//Something happened to original target, dont continue
  208.     end
  209.     // Get client's CVars
  210.     local forcelimit    = self:GetClientNumber( "forcelimit", 0 )
  211.     local freeze        = util.tobool( self:GetClientNumber( "freeze", 1 ) )
  212.     local nocollide     = self:GetClientNumber( "nocollide", 0 )
  213.     local nocollideall  = util.tobool( self:GetClientNumber( "nocollideall", 0 ) )
  214.     local torquelimit   = self:GetClientNumber( "torquelimit", 0 )
  215.     local width         = self:GetClientNumber( "width", 1 )
  216.     local friction      = self:GetClientNumber( "friction", 0 )
  217.     local physdis       = util.tobool( self:GetClientNumber( "physdisable", 0 ) )
  218.     local ShadowDisable = util.tobool( self:GetClientNumber( "ShadowDisable", 0 ) )
  219.     local autorotate    = util.tobool(self:GetClientNumber( "autorotate",1 ))
  220.     local removal_nocollide     = util.tobool(self:GetClientNumber( "removal_nocollide",1 ))
  221.     local removal_weld  = util.tobool(self:GetClientNumber( "removal_weld",1 ))
  222.     local removal_axis  = util.tobool(self:GetClientNumber( "removal_axis",1 ))
  223.     local removal_ballsocket    = util.tobool(self:GetClientNumber( "removal_ballsocket",1 ))
  224.     local removal_advballsocket     = util.tobool(self:GetClientNumber( "removal_advballsocket",1 ))
  225.     local removal_slider    = util.tobool(self:GetClientNumber( "removal_slider",1 ))
  226.     local removal_parent    = util.tobool(self:GetClientNumber( "removal_parent",1 ))
  227.     local removal_other     = util.tobool(self:GetClientNumber( "removal_other",1 ))
  228.     local Bone1 = self:GetBone(1)
  229.     local LPos1 = self:GetLocalPos(1)
  230.     local Bone2 = nil
  231.     local LPos2 = nil
  232.     if ( Ent2 && (Ent2:IsValid() || Ent2:IsWorld()) ) then
  233.         Bone2 = self:GetBone(2)
  234.         LPos2 = self:GetLocalPos(2)
  235.     end
  236.     local Phys1 = self:GetPhys(1)
  237.    
  238.     local NumApp = 0
  239.    
  240.  
  241.     for key,CurrentEnt in pairs(self.TaggedEnts) do
  242.         if ( CurrentEnt and CurrentEnt:IsValid() ) then
  243.             if !(CurrentEnt == Ent2 ) then
  244.                 local CurrentPhys = CurrentEnt:GetPhysicsObject()
  245.                 if ( CurrentPhys:IsValid() && !CurrentEnt:GetParent():IsValid() ) then//parent?
  246.                     if ( CurrentEnt:GetPhysicsObjectCount() < 2 ) then //not a ragdoll
  247.                         if (  util.tobool( nocollide ) && (mode == 1 || mode == 3)) then // not weld/axis/ballsocket or single application
  248.                             local constraint = constraint.NoCollide(CurrentEnt, Ent2, 0, Bone2)
  249.                         end
  250.                         if ( mode == 1 ) then //Apply
  251.                             self:DoApply( CurrentEnt, Ent1, autorotate, nocollideall, ShadowDisable )
  252.                         elseif ( mode == 2 ) then //Rotate
  253.                             //self:SendMessage("Sorry, No entire contraption rotating... yet")
  254.                             //return false//TODO: Entire contrpation rotaton
  255.                         elseif ( mode == 3 ) then //move
  256.                             //self:SendMessage("Sorry, No entire contraption moving... yet")
  257.                             //return false//todo: entire contraption move/snap
  258.                                 //CurrentPhys:EnableGravity( !CurrentPhys:IsGravityEnabled() )//Cant disable gravity - sliders would go nuts and disappear.
  259.                             local speeddamp,angledamp = CurrentPhys:GetDamping()
  260.                             if ( speeddamp == 0 && angledamp == 0 ) then
  261.                                 CurrentPhys:SetDamping( 5, 5 )
  262.                             elseif ( speeddamp == 5 && angledamp == 5 ) then
  263.                                 CurrentPhys:SetDamping( 0, 0 )
  264.                             end
  265.                             CurrentEnt:SetPos(CurrentEnt:GetPos())
  266.                             CurrentPhys:Wake()
  267.                         end
  268.                         if ( mode <= 8 ) then
  269.                             CurrentPhys:EnableMotion( !freeze )
  270.                             CurrentPhys:Wake()
  271.                         end
  272.                     end
  273.                 end
  274.             end
  275.         end
  276.         NumApp = NumApp + 1
  277.     end//Next
  278.     if ( mode == 1 ) then
  279.         self:SendMessage( NumApp .. " items targeted for apply." )
  280.     elseif ( mode == 2 ) then
  281.         self:SendMessage( NumApp .. " items targeted for rotate." )
  282.     elseif ( mode == 3 ) then
  283.         self:SendMessage( NumApp .. " items targeted for move." )
  284.     end
  285.    
  286.    
  287.     if ( mode == 10 ) then
  288.         self.RepairTodo = true
  289.         timer.Simple( 1.0, function()
  290.         self:ClearSelection()
  291.         end)
  292.     else
  293.         self:ClearSelection()
  294.     end
  295.     // Clear the objects so we're ready to go again
  296.     self:ClearObjects()
  297. end
  298.  
  299. function TOOL:SendMessage( message )
  300.     if ( self:GetClientNumber( "enablefeedback" ) == 0 ) then return end
  301.     if ( self:GetClientNumber( "chatfeedback" ) == 1 ) then
  302.         self:GetOwner():PrintMessage( HUD_PRINTTALK, "Tool: " .. message )
  303.     else
  304.         self:GetOwner():PrintMessage( HUD_PRINTCENTER, message )
  305.     end
  306. end
  307.  
  308. function TOOL:TargetValidity ( trace, Phys ) //TODO: Parented stuff should return 1
  309.     if ( SERVER && (!util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) || !Phys:IsValid()) ) then
  310.         local mode = self:GetClientNumber( "mode" )
  311.         if ( trace.Entity:GetParent():IsValid() ) then
  312.             return 2//Valid parent, but itself isn't
  313.         else
  314.             return 0//No valid phys
  315.         end
  316.     elseif ( trace.Entity:IsPlayer() ) then
  317.         return 0// Don't attach players, or to players
  318.     elseif ( trace.HitWorld ) then
  319.         return 1// Only allow second click to be here...
  320.     else
  321.         return 3//Everything seems good
  322.     end
  323. end
  324.  
  325. function TOOL:StartRotate()
  326.     local Ent = self:GetEnt(1)
  327.     local Phys = self:GetPhys(1)
  328.     local oldposu = Ent:GetPos()
  329.     local oldangles = Ent:GetAngles()
  330.  
  331.     local function MoveUndo( Undo, Entity, oldposu, oldangles )
  332.         if Entity:IsValid() then
  333.             Entity:SetAngles( oldangles )
  334.             Entity:SetPos( oldposu )
  335.         end
  336.     end
  337.    
  338.     if ( self:GetClientNumber( "rotateundo" )) then
  339.         if SERVER then
  340.             undo.Create("Precision_Rotate")
  341.                 undo.SetPlayer(self:GetOwner())
  342.                 undo.AddFunction( MoveUndo, Ent, oldposu, oldangles )
  343.             undo.Finish()
  344.         end
  345.     end
  346.    
  347.     if IsValid( Phys ) then
  348.         Phys:EnableMotion( false ) //else it drifts
  349.     end
  350.    
  351.     local rotation = self:GetClientNumber( "rotation" )
  352.     if ( rotation < 0.02 ) then rotation = 0.02 end
  353.     self.axis = self:GetNormal(1)
  354.     self.axisY = self.axis:Cross(Ent:GetUp())
  355.     if self:WithinABit( self.axisY, Vector(0,0,0) ) then
  356.         self.axisY = self.axis:Cross(Ent:GetForward())
  357.     end
  358.     self.axisZ = self.axisY:Cross(self.axis)
  359.     self.realdegrees = 0
  360.     self.lastdegrees = -((rotation/2) % rotation)
  361.     self.realdegreesY = 0
  362.     self.lastdegreesY = -((rotation/2) % rotation)
  363.     self.realdegreesZ = 0
  364.     self.lastdegreesZ = -((rotation/2) % rotation)
  365.     self.OldPos = self:GetPos(1)//trace.HitPos
  366. end
  367.  
  368. function TOOL:DoMove()
  369.     // Get information we're about to use
  370.     local Norm1, Norm2 = self:GetNormal(1),   self:GetNormal(2)
  371.     local Phys1, Phys2 = self:GetPhys(1),     self:GetPhys(2)
  372.    
  373.     local Ang1, Ang2 = Norm1:Angle(), (Norm2 * -1):Angle()
  374.     if self:GetClientNumber( "autorotate" ) == 1 then
  375.         Ang2.p = (math.Round(Ang2.p/45))*45
  376.         Ang2.r = (math.Round(Ang2.r/45))*45
  377.         Ang2.y = (math.Round(Ang2.y/45))*45
  378.         Norm2 = Ang2:Forward() * -1
  379.     end
  380.  
  381.  
  382.     local oldposu = self:GetEnt(1):GetPos()
  383.     local oldangles = self:GetEnt(1):GetAngles()
  384.  
  385.     local function MoveUndo( Undo, Entity, oldposu, oldangles )
  386.         if Entity:IsValid() then
  387.             Entity:SetAngles( oldangles )
  388.             Entity:SetPos( oldposu )
  389.         end
  390.     end
  391.     if self:GetClientNumber( "moveundo" ) == 1 then
  392.     undo.Create("Precision Move")
  393.         undo.SetPlayer(self:GetOwner())
  394.         undo.AddFunction( MoveUndo, self:GetEnt(1), oldposu, oldangles )
  395.     undo.Finish()
  396.     end
  397.  
  398.     local rotation = self:GetClientNumber( "rotation" )
  399.     if ( rotation < 0.02 ) then rotation = 0.02 end
  400.     if ( (self:GetClientNumber( "rotate" ) == 1 && mode != 1) || mode == 2) then//Set axies for rotation mode directions
  401.         self.axis = Norm2
  402.         self.axisY = self.axis:Cross(Vector(0,1,0))
  403.         if self:WithinABit( self.axisY, Vector(0,0,0) ) then
  404.             self.axisY = self.axis:Cross(Vector(0,0,1))
  405.         end
  406.         self.axisY:Normalize()
  407.         self.axisZ = self.axisY:Cross(self.axis)
  408.         self.axisZ:Normalize()
  409.         self.realdegrees = 0
  410.         self.lastdegrees = -((rotation/2) % rotation)
  411.         self.realdegreesY = 0
  412.         self.lastdegreesY = -((rotation/2) % rotation)
  413.         self.realdegreesZ = 0
  414.         self.lastdegreesZ = -((rotation/2) % rotation)
  415.     else
  416.         self.axis = Norm2
  417.         self.axisY = self.axis:Cross(Vector(0,1,0))
  418.         if self:WithinABit( self.axisY, Vector(0,0,0) ) then
  419.             self.axisY = self.axis:Cross(Vector(0,0,1))
  420.         end
  421.         self.axisY:Normalize()
  422.         self.axisZ = self.axisY:Cross(self.axis)
  423.         self.axisZ:Normalize()
  424.     end
  425.  
  426.  
  427.  
  428.     local TargetAngle = Phys1:AlignAngles( Ang1, Ang2 )//Get angle Phys1 would be at if difference between Ang1 and Ang2 was added
  429.  
  430.  
  431.     if self:GetClientNumber( "autorotate" ) == 1 then
  432.         TargetAngle.p = (math.Round(TargetAngle.p/45))*45
  433.         TargetAngle.r = (math.Round(TargetAngle.r/45))*45
  434.         TargetAngle.y = (math.Round(TargetAngle.y/45))*45
  435.     end
  436.  
  437.     Phys1:SetAngles( TargetAngle )
  438.  
  439.  
  440.     local NewOffset = math.Clamp( self:GetClientNumber( "offset" ), -5000, 5000 )
  441.     local offsetpercent     = self:GetClientNumber( "offsetpercent" ) == 1
  442.     if ( offsetpercent ) then
  443.         local  Ent2  = self:GetEnt(2)
  444.         local glower = Ent2:OBBMins()
  445.         local gupper = Ent2:OBBMaxs()
  446.         local height = math.abs(gupper.z - glower.z) -0.5
  447.         if self:WithinABit(Norm2,Ent2:GetForward()) then
  448.             height = math.abs(gupper.x - glower.x)-0.5
  449.         elseif self:WithinABit(Norm2,Ent2:GetRight()) then
  450.             height = math.abs(gupper.y - glower.y)-0.5
  451.         end
  452.         NewOffset = NewOffset / 100
  453.         NewOffset = NewOffset * height
  454.     end
  455.     Norm2 = Norm2 * (-0.0625 + NewOffset)
  456.     local TargetPos = self:GetPos(2) + (Phys1:GetPos() - self:GetPos(1)) + (Norm2)
  457.     //self:SetPos(2)
  458.  
  459.     // Set the position
  460.  
  461.     Phys1:SetPos( TargetPos )
  462.     Phys1:EnableMotion( false )
  463.  
  464.     // Wake up the physics object so that the entity updates
  465.     Phys1:Wake()
  466. end
  467.  
  468. function TOOL:ToggleColor( CurrentEnt )
  469.     color = CurrentEnt:GetColor()
  470.     color["a"] = color["a"] - 128
  471.     if ( color["a"] < 0 ) then
  472.         color["a"] = color["a"] + 256
  473.     end
  474.     color["r"] = color["r"] - 128
  475.     if ( color["r"] < 0 ) then
  476.         color["r"] = color["r"] + 256
  477.     end
  478.     color["g"] = color["g"] - 128
  479.     if ( color["g"] < 0 ) then
  480.         color["g"] = color["g"] + 256
  481.     end
  482.     color["b"] = color["b"] - 128
  483.     if ( color["b"] < 0 ) then
  484.         color["b"] = color["b"] + 256
  485.     end
  486.     CurrentEnt:SetColor( color )
  487.     if ( color["a"] == 255 ) then
  488.         CurrentEnt:SetRenderMode( 0 )
  489.     else
  490.         CurrentEnt:SetRenderMode( 1 )
  491.     end
  492. end
  493.  
  494. function TOOL:ClearSelection()
  495.     if ( self.RepairTodo ) then
  496.         self:UndoRepairToggle()
  497.     end
  498.     if ( self.TaggedEnts ) then
  499.         local color
  500.         for key,CurrentEnt in pairs(self.TaggedEnts) do
  501.             if ( CurrentEnt and CurrentEnt:IsValid() ) then
  502.                 local CurrentPhys = CurrentEnt:GetPhysicsObject()
  503.                 if ( CurrentPhys:IsValid() ) then
  504.                     self:ToggleColor(CurrentEnt)
  505.                 end
  506.             end
  507.         end
  508.     end
  509.     self.TaggedEnts = {}
  510. end
  511.  
  512. function TOOL:SelectEnts(StartEnt, AllConnected)
  513.     self:ClearSelection()
  514.     if ( CLIENT ) then return end
  515.     local color
  516.     if ( AllConnected == 1 ) then
  517.         local NumApp = 0
  518.         EntsTab = {}
  519.         ConstsTab = {}
  520.         GetAllEnts(StartEnt, self.TaggedEnts, EntsTab, ConstsTab)
  521.         for key,CurrentEnt in pairs(self.TaggedEnts) do
  522.             if ( CurrentEnt and CurrentEnt:IsValid() ) then
  523.                 local CurrentPhys = CurrentEnt:GetPhysicsObject()
  524.                 if ( CurrentPhys:IsValid() ) then
  525.                     self:ToggleColor(CurrentEnt)
  526.                 end
  527.             end
  528.             NumApp = NumApp + 1
  529.         end
  530.         self:SendMessage( NumApp .. " objects selected." )
  531.     else
  532.         if ( StartEnt and StartEnt:IsValid() ) then
  533.             local CurrentPhys = StartEnt:GetPhysicsObject()
  534.             if ( CurrentPhys:IsValid() ) then
  535.                 table.insert(self.TaggedEnts, StartEnt)
  536.                 self:ToggleColor(StartEnt)
  537.             end
  538.         end
  539.     end
  540.    
  541. end
  542.  
  543. function TOOL:LeftClick( trace )
  544.     local stage = self:GetStage()//0 = started, 1 = moving/second target, 2 = rotation?
  545.     local mode = self:GetClientNumber( "mode" )
  546.     local moving = ( mode == 3 || (self:GetClientNumber( "move" ) == 1 && mode >= 3 && mode <= 8 ) )
  547.     local rotating = ( self:GetClientNumber( "rotate" ) == 1 )
  548.     local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone )
  549.  
  550.    
  551.     if ( stage == 0 ) then//first click - choose a target.
  552.         if ( self:TargetValidity(trace, Phys) <= 1 ) then
  553.             return false//No phys or hit world
  554.         end
  555.         self:SetObject( 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal )
  556.        
  557.         if (self:GetClientNumber( "entirecontrap" ) == 1 || mode == 10 ) then
  558.             self:SelectEnts(trace.Entity,1)
  559.         else
  560.             self:SelectEnts(trace.Entity,0)
  561.         end
  562.         if ( mode == 1 || mode == 10 || mode == 11 ) then //Dont care about stage, always apply.
  563.             self:DoConstraint(mode)
  564.         else
  565.             if ( mode == 9 ) then
  566.                 self:SetStage(1)
  567.             else
  568.                 if ( moving ) then//Moving
  569.                     self:StartGhostEntity( trace.Entity )
  570.                     self:SetStage(1)
  571.                 elseif ( mode == 2 ) then//Straight to rotate
  572.                     self:StartRotate()
  573.                     self:SetStage(2)
  574.                 else
  575.                     self:SetStage(1)
  576.                 end
  577.             end
  578.         end
  579.     elseif ( stage == 1 ) then//Second click
  580.         self:SetObject( 2, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal )
  581.        
  582.         if ( self:GetEnt(1) == self:GetEnt(2) ) then
  583.             SavedPos = self:GetPos(2)
  584.         end
  585.         if ( mode == 9 ) then
  586.             self:DoConstraint(mode)
  587.         else
  588.             if ( moving ) then
  589.                 if ( CLIENT ) then
  590.                     self:ReleaseGhostEntity()
  591.                     return true
  592.                 end
  593.                 if ( SERVER && !game.SinglePlayer() ) then
  594.                     self:ReleaseGhostEntity()
  595.                     //return true
  596.                 end
  597.                 self:DoMove()
  598.             end
  599.             if ( rotating ) then
  600.                 self:StartRotate()
  601.                 self:SetStage(2)
  602.             else
  603.                 self:DoConstraint(mode)
  604.             end
  605.         end
  606.     elseif ( stage == 2 ) then//Done rotate
  607.         self:DoConstraint(mode)
  608.     end
  609.     return true
  610. end
  611.  
  612. function TOOL:WithinABit( v1, v2 )
  613.     local tol = 0.1
  614.     local da = v1.x-v2.x
  615.     local db = v1.y-v2.y
  616.     local dc = v1.z-v2.z
  617.     if da < tol && da > -tol && db < tol && db > -tol && dc < tol && dc > -tol then
  618.         return true
  619.     else
  620.         da = v1.x+v2.x
  621.         db = v1.y+v2.y
  622.         dc = v1.z+v2.z
  623.         if da < tol && da > -tol && db < tol && db > -tol && dc < tol && dc > -tol then
  624.             return true
  625.         else
  626.             return false
  627.         end
  628.     end
  629. end
  630.  
  631. if ( SERVER ) then
  632.    
  633.     function GetAllEnts( Ent, OrderedEntList, EntsTab, ConstsTab )
  634.         if ( Ent and Ent:IsValid() ) and ( !EntsTab[ Ent:EntIndex() ] ) then
  635.             EntsTab[ Ent:EntIndex() ] = Ent
  636.             table.insert(OrderedEntList, Ent)
  637.             if ( !constraint.HasConstraints( Ent ) ) then return OrderedEntList end
  638.             for key, ConstraintEntity in pairs( Ent.Constraints ) do
  639.                 if ( !ConstsTab[ ConstraintEntity ] ) then
  640.                     ConstsTab[ ConstraintEntity ] = true
  641.                     local ConstTable = ConstraintEntity:GetTable()
  642.                     for i=1, 6 do
  643.                         local e = ConstTable[ "Ent"..i ]
  644.                         if ( e and e:IsValid() ) and ( !EntsTab[ e:EntIndex() ] ) then
  645.                             GetAllEnts( e, OrderedEntList, EntsTab, ConstsTab )
  646.                         end
  647.                     end
  648.                 end
  649.             end
  650.         end
  651.         return OrderedEntList
  652.     end
  653.    
  654.     function GetAllConstraints( EntsTab )
  655.         local ConstsTab = {}
  656.         for key, Ent in pairs( EntsTab ) do
  657.             if ( Ent and Ent:IsValid() ) then
  658.                 local MyTable = constraint.GetTable( Ent )
  659.                 for key, Constraint in pairs( MyTable ) do
  660.                     if ( !ConstsTab[ Constraint.Constraint ] ) then
  661.                         ConstsTab[ Constraint.Constraint ] = Constraint
  662.                     end
  663.                 end
  664.             end
  665.         end
  666.         return ConstsTab
  667.     end
  668. end
  669.  
  670. function TOOL:UpdateCustomGhost( ghost, player, offset )
  671.    
  672.     // Ghost is identically buggy to that of easyweld...  welding two frozen props and two unfrozen props yields different ghosts even if identical allignment
  673.  
  674.     if (ghost == nil) then return end
  675.     if (!ghost:IsValid()) then ghost = nil return end
  676.  
  677.     local tr = util.GetPlayerTrace( player, player:GetAimVector() )
  678.     local trace = util.TraceLine( tr )
  679.     if (!trace.Hit) then return end
  680.  
  681.     local Ang1, Ang2 = self:GetNormal(1):Angle(), (trace.HitNormal * -1):Angle()
  682.     local TargetAngle = self:GetEnt(1):AlignAngles( Ang1, Ang2 )
  683.  
  684.     self.GhostEntity:SetPos( self:GetEnt(1):GetPos() )
  685.    
  686.     if self:GetClientNumber( "autorotate" ) == 1 then
  687.         TargetAngle.p = (math.Round(TargetAngle.p/45))*45
  688.         TargetAngle.r = (math.Round(TargetAngle.r/45))*45
  689.         TargetAngle.y = (math.Round(TargetAngle.y/45))*45
  690.     end
  691.     self.GhostEntity:SetAngles( TargetAngle )
  692.  
  693.     local TraceNormal = trace.HitNormal
  694.  
  695.     local offsetpercent     = self:GetClientNumber( "offsetpercent" ) == 1
  696.     local NewOffset = offset
  697.     if ( offsetpercent ) then
  698.         local glower = trace.Entity:OBBMins()
  699.         local gupper = trace.Entity:OBBMaxs()
  700.         local height = math.abs(gupper.z - glower.z) -0.5
  701.         if self:WithinABit(TraceNormal,trace.Entity:GetForward()) then
  702.             height = math.abs(gupper.x - glower.x) -0.5
  703.         elseif self:WithinABit(TraceNormal,trace.Entity:GetRight()) then
  704.             height = math.abs(gupper.y - glower.y) -0.5
  705.         end
  706.         NewOffset = NewOffset / 100
  707.         NewOffset = NewOffset * height
  708.     end
  709.  
  710.     local TranslatedPos = ghost:LocalToWorld( self:GetLocalPos(1) )
  711.     local TargetPos = trace.HitPos + (self:GetEnt(1):GetPos() - TranslatedPos) + (TraceNormal*NewOffset)
  712.  
  713.     self.GhostEntity:SetPos( TargetPos )
  714. end
  715.  
  716.  
  717. function TOOL:Think()
  718.     //if CLIENT then return end
  719.     local pl = self:GetOwner()
  720.     local wep = pl:GetActiveWeapon()
  721.     if not wep:IsValid() or wep:GetClass() != "gmod_tool" or pl:GetInfo("gmod_toolmode") != "precision" then return end
  722.        
  723.     if (self:NumObjects() < 1) then return end
  724.     local Ent1 = self:GetEnt(1)
  725.     if ( SERVER ) then
  726.         if ( !Ent1:IsValid() ) then
  727.             self:ClearObjects()
  728.             return
  729.         end
  730.     end
  731.     local mode = self:GetClientNumber( "mode" )
  732.  
  733.     if self:NumObjects() == 1 && mode != 2 then
  734.         if ( (self:GetClientNumber( "move" ) == 1 && mode >= 3) || mode == 3 ) then
  735.             if ( mode <= 8 ) then//no move = no ghost in parent mode
  736.                 local offset = math.Clamp( self:GetClientNumber( "offset" ), -5000, 5000 )
  737.                 self:UpdateCustomGhost( self.GhostEntity, self:GetOwner(), offset )
  738.             end
  739.         end
  740.     else
  741.         local rotate = (self:GetClientNumber( "rotate" ) == 1 && mode != 1) || mode == 2
  742.         if ( SERVER && rotate && mode <= 8 ) then
  743.             local offset = math.Clamp( self:GetClientNumber( "offset" ), -5000, 5000 )
  744.  
  745.             local Phys1 = self:GetPhys(1)
  746.  
  747.             local cmd = self:GetOwner():GetCurrentCommand()
  748.  
  749.             local rotation      = self:GetClientNumber( "rotation" )
  750.             if ( rotation < 0.02 ) then rotation = 0.02 end
  751.             local degrees = cmd:GetMouseX() * 0.02
  752.  
  753.             local newdegrees = 0
  754.             local changedegrees = 0
  755.  
  756.             local angle = 0
  757.             if( self:GetOwner():KeyDown( IN_RELOAD ) ) then
  758.                 self.realdegreesY = self.realdegreesY + degrees
  759.                 newdegrees =  self.realdegreesY - ((self.realdegreesY + (rotation/2)) % rotation)
  760.                 changedegrees = self.lastdegreesY - newdegrees
  761.                 self.lastdegreesY = newdegrees
  762.                 angle = Phys1:RotateAroundAxis( self.axisY , changedegrees )
  763.             elseif( self:GetOwner():KeyDown( IN_ATTACK2 ) ) then
  764.                 self.realdegreesZ = self.realdegreesZ + degrees
  765.                 newdegrees =  self.realdegreesZ - ((self.realdegreesZ + (rotation/2)) % rotation)
  766.                 changedegrees = self.lastdegreesZ - newdegrees
  767.                 self.lastdegreesZ = newdegrees
  768.                 angle = Phys1:RotateAroundAxis( self.axisZ , changedegrees )
  769.             else
  770.                 self.realdegrees = self.realdegrees + degrees
  771.                 newdegrees =  self.realdegrees - ((self.realdegrees + (rotation/2)) % rotation)
  772.                 changedegrees = self.lastdegrees - newdegrees
  773.                 self.lastdegrees = newdegrees
  774.                 angle = Phys1:RotateAroundAxis( self.axis , changedegrees )
  775.             end
  776.             Phys1:SetAngles( angle )
  777.  
  778.             if ( ( self:GetClientNumber( "move" ) == 1 && mode >= 3) || mode == 3 ) then
  779.                 local WPos2 = self:GetPos(2)
  780.                 local Ent2 = self:GetEnt(2)
  781.                 // Move so spots join up
  782.                 local Norm2 = self:GetNormal(2)
  783.  
  784.                 local NewOffset = offset
  785.                 local offsetpercent = self:GetClientNumber( "offsetpercent" ) == 1
  786.                 if ( offsetpercent ) then
  787.                     local glower = Ent2:OBBMins()
  788.                     local gupper = Ent2:OBBMaxs()
  789.                     local height = math.abs(gupper.z - glower.z) -0.5
  790.                     if self:WithinABit(Norm2,Ent2:GetForward()) then
  791.                         height = math.abs(gupper.x - glower.x) -0.5
  792.                     elseif self:WithinABit(Norm2,Ent2:GetRight()) then
  793.                         height = math.abs(gupper.y - glower.y) -0.5
  794.                     end
  795.                     NewOffset = NewOffset / 100
  796.                     NewOffset = NewOffset * height
  797.                 end
  798.  
  799.                 Norm2 = Norm2 * (-0.0625 + NewOffset)
  800.                 local TargetPos = Vector(0,0,0)
  801.                 if ( self:GetEnt(1) == self:GetEnt(2) ) then
  802.     //////////////////////////////////////////
  803.                     TargetPos = SavedPos + (Phys1:GetPos() - self:GetPos(1)) + (Norm2)
  804.                 else
  805.                     TargetPos = WPos2 + (Phys1:GetPos() - self:GetPos(1)) + (Norm2)
  806.                 end
  807.                 Phys1:SetPos( TargetPos )
  808.             else
  809.                 // Move so rotating on axis
  810.  
  811.                 local TargetPos = (Phys1:GetPos() - self:GetPos(1)) + self.OldPos
  812.                 Phys1:SetPos( TargetPos )
  813.             end
  814.             Phys1:Wake()
  815.         end
  816.     end
  817. end
  818.  
  819. function TOOL:Nudge( trace, direction )
  820.     if (!trace.Entity:IsValid() || trace.Entity:IsPlayer() ) then return false end
  821.     local Phys1 = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone )
  822.     local offsetpercent     = self:GetClientNumber( "nudgepercent" ) == 1
  823.     local offset        = self:GetClientNumber( "nudge", 100 )
  824.     local max = 8192
  825.     if ( offsetpercent != 1 ) then
  826.         if ( offset > max ) then
  827.             offset = max
  828.         elseif ( offset < -max ) then
  829.             offset = -max
  830.         end
  831.     end
  832.     //if ( offset == 0 ) then offset = 1 end
  833.     local NewOffset = offset
  834.     if ( offsetpercent ) then
  835.         local glower = trace.Entity:OBBMins()
  836.         local gupper = trace.Entity:OBBMaxs()
  837.         local height = math.abs(gupper.z - glower.z) -0.5
  838.         if self:WithinABit(trace.HitNormal,trace.Entity:GetForward()) then
  839.             height = math.abs(gupper.x - glower.x)-0.5
  840.         elseif self:WithinABit(trace.HitNormal,trace.Entity:GetRight()) then
  841.             height = math.abs(gupper.y - glower.y)-0.5
  842.         end
  843.         NewOffset = NewOffset / 100
  844.         local cap = math.floor(max / height)//No more than max units.
  845.         if ( NewOffset > cap ) then
  846.             NewOffset = cap
  847.         elseif ( NewOffset < -cap ) then
  848.             NewOffset = -cap
  849.         end
  850.         NewOffset = NewOffset * height
  851.     end
  852.  
  853.     if ( self:GetClientNumber( "entirecontrap" ) == 1 ) then
  854.         local NumApp = 0
  855.         local TargetEnts = {}
  856.         local EntsTab = {}
  857.         local ConstsTab = {}
  858.         GetAllEnts(trace.Entity, TargetEnts, EntsTab, ConstsTab)
  859.         for key,CurrentEnt in pairs(TargetEnts) do
  860.             if ( CurrentEnt and CurrentEnt:IsValid() ) then
  861.                 local CurrentPhys = CurrentEnt:GetPhysicsObject()
  862.                 if ( CurrentPhys:IsValid() ) then
  863.  
  864.                     /*if ( self:GetClientNumber( "nudgeundo" ) == 1 ) then
  865.                         local oldpos = CurrentPhys:GetPos()
  866.                         local function NudgeUndo( Undo, Entity, oldpos )
  867.                             if CurrentEnt:IsValid() then
  868.                                 CurrentEnt:SetPos( oldpos )
  869.                             end
  870.                         end
  871.                         undo.Create("Nrecision Nudge")
  872.                             undo.SetPlayer(self:GetOwner())
  873.                             undo.AddFunction( NudgeUndo, CurrentEnt, oldpos )
  874.                         undo.Finish()
  875.                     end*/// todo: all in 1 undo for mass nudging
  876.  
  877.                     local TargetPos = CurrentPhys:GetPos() + trace.HitNormal * NewOffset * direction
  878.                     CurrentPhys:SetPos( TargetPos )
  879.                     CurrentPhys:Wake()
  880.                     if (CurrentEnt:GetMoveType() == 0 ) then //phys disabled, so move manually
  881.                         CurrentEnt:SetPos( TargetPos )
  882.                     end
  883.  
  884.                 end
  885.             end
  886.             NumApp = NumApp + 1
  887.         end
  888.         if ( direction == -1 ) then
  889.             self:SendMessage( NumApp .. " items pushed." )
  890.         elseif ( direction == 1 ) then
  891.             self:SendMessage( NumApp .. " items pulled." )
  892.         else
  893.             self:SendMessage( NumApp .. " items nudged." )
  894.         end
  895.     else
  896.         if ( self:GetClientNumber( "nudgeundo" ) == 1 ) then
  897.             local oldpos = Phys1:GetPos()
  898.             local function NudgeUndo( Undo, Entity, oldpos )
  899.                 if trace.Entity:IsValid() then
  900.                     trace.Entity:SetPos( oldpos )
  901.                 end
  902.             end
  903.             undo.Create("Precision PushPull")
  904.                 undo.SetPlayer(self:GetOwner())
  905.                 undo.AddFunction( NudgeUndo, trace.Entity, oldpos )
  906.             undo.Finish()
  907.         end
  908.         local TargetPos = Phys1:GetPos() + trace.HitNormal * NewOffset * direction
  909.         Phys1:SetPos( TargetPos )
  910.         Phys1:Wake()
  911.         if ( trace.Entity:GetMoveType() == 0 ) then
  912.             trace.Entity:SetPos( TargetPos )
  913.         end
  914.         if ( direction == -1 ) then
  915.             self:SendMessage( "target pushed." )
  916.         elseif ( direction == 1 ) then
  917.             self:SendMessage( "target pulled." )
  918.         else
  919.             self:SendMessage( "target nudged." )
  920.         end
  921.     end
  922.     return true
  923. end
  924.  
  925. function TOOL:RightClick( trace )
  926.     local rotate = self:GetClientNumber( "rotate" ) == 1
  927.     local mode = self:GetClientNumber( "mode" )
  928.     if ( (mode == 2 && self:NumObjects() == 1) || (rotate && self:NumObjects() == 2 ) ) then
  929.         if ( CLIENT ) then return false end
  930.     else
  931.         if ( CLIENT ) then return true end
  932.         return self:Nudge( trace, -1 )
  933.     end
  934. end
  935.  
  936. function TOOL:Reload( trace )
  937.     local rotate = self:GetClientNumber( "rotate" ) == 1
  938.     local mode = self:GetClientNumber( "mode" )
  939.     if ( (mode == 2 && self:NumObjects() == 1) || (rotate && self:NumObjects() == 2 ) ) then
  940.         if ( CLIENT ) then return false end
  941.     else
  942.         if ( CLIENT ) then return true end
  943.         return self:Nudge( trace, 1 )
  944.     end
  945. end
  946.  
  947. if CLIENT then
  948.  
  949.     language.Add( "Tool.precision.name", "Precision Tool 0.98e" )
  950.     language.Add( "Tool.precision.desc", "Accurately moves/constrains objects" )
  951.     language.Add( "Tool.precision.0", "Primary: Move/Apply | Secondary: Push | Reload: Pull" )
  952.     language.Add( "Tool.precision.1", "Target the second item. If enabled, this will move the first item.  (Swap weps to cancel)" )
  953.     language.Add( "Tool.precision.2", "Rotate enabled: Turn left and right to rotate the object (Hold Reload or Secondary for other rotation directions!)" )
  954.  
  955.  
  956.     language.Add("Undone.precision", "Undone Precision Constraint")
  957.     language.Add("Undone.precision.nudge", "Undone Precision PushPull")
  958.     language.Add("Undone.precision.rotate", "Undone Precision Rotate")
  959.     language.Add("Undone.precision.move", "Undone Precision Move")
  960.     language.Add("Undone.precision.weld", "Undone Precision Weld")
  961.     language.Add("Undone.precision.axis", "Undone Precision Axis")
  962.     language.Add("Undone.precision.ballsocket", "Undone Precision Ballsocket")
  963.     language.Add("Undone.precision.advanced.ballsocket", "Undone Precision Advanced Ballsocket")
  964.     language.Add("Undone.precision.slider", "Undone Precision Slider")
  965.  
  966.     local showgenmenu = 0//Seems to hide often, probably for the best
  967.  
  968.     local function AddDefControls( Panel )
  969.         Panel:ClearControls()
  970.  
  971.         Panel:AddControl("ComboBox",
  972.         {
  973.             Label = "#Presets",
  974.             MenuButton = 1,
  975.             Folder = "precision",
  976.             Options = {},
  977.             CVars =
  978.             {
  979.                 [0] = "precision_offset",
  980.                 [1] = "precision_forcelimit",
  981.                 [2] = "precision_freeze",
  982.                 [3] = "precision_nocollide",
  983.                 [4] = "precision_nocollideall",
  984.                 [5] = "precision_rotation",
  985.                 [6] = "precision_rotate",
  986.                 [7] = "precision_torquelimit",
  987.                 [8] = "precision_friction",
  988.                 [9] = "precision_mode",
  989.                 [10] = "precision_width",
  990.                 [11] = "precision_offsetpercent",
  991.                 [12] = "precision_removal",
  992.                 [13] = "precision_move",
  993.                 [14] = "precision_physdisable",
  994.                 [15] = "precision_advballsocket",
  995.                 [16] = "precision_XRotMin",
  996.                 [17] = "precision_XRotMax",
  997.                 [18] = "precision_YRotMin",
  998.                 [19] = "precision_YRotMax",
  999.                 [20] = "precision_ZRotMin",
  1000.                 [21] = "precision_ZRotMax",
  1001.                 [22] = "precision_XRotFric",
  1002.                 [23] = "precision_YRotFric",
  1003.                 [24] = "precision_ZRotFric",
  1004.                 [25] = "precision_FreeMov",
  1005.                 [26] = "precision_ShadowDisable",
  1006.                 [27] = "precision_allowphysgun",
  1007.                 [28] = "precision_autorotate",
  1008.                 [29] = "precision_massmode",
  1009.                 [30] = "precision_nudge",
  1010.                 [31] = "precision_nudgepercent",
  1011.                 [32] = "precision_disablesliderfix"
  1012.             }
  1013.         })
  1014.  
  1015.         //Panel:AddControl( "Label", { Text = "Secondary attack pushes, Reload pulls by this amount:", Description  = "Phx 1x is 47.45, Small tiled cube is 11.8625 and thin is 3 exact units" }  )
  1016.         Panel:AddControl( "Slider",  { Label    = "Push/Pull Amount",
  1017.                     Type    = "Float",
  1018.                     Min     = 1,
  1019.                     Max     = 100,
  1020.                     Command = "precision_nudge",
  1021.                     Description = "Distance to push/pull props with altfire/reload"}     ):SetDecimals( 4 )
  1022.  
  1023.  
  1024.         Panel:AddControl( "Checkbox", { Label = "Push/Pull as Percent (%) of target's depth", Command = "precision_nudgepercent", Description = "Unchecked = Exact units, Checked = takes % of width from target prop when pushing/pulling" } )
  1025.  
  1026.  
  1027.         local user = LocalPlayer():GetInfoNum( "precision_user", 0 )
  1028.         local mode = LocalPlayer():GetInfoNum( "precision_mode", 0 )
  1029.         //Panel:AddControl( "Label", { Text = "Primary attack uses the tool's main mode.", Description  = "Select a mode and configure the options, be sure to try new things out!" }  )
  1030.  
  1031.         local list = vgui.Create("DListView")
  1032.  
  1033.         //17 per item + 16 for title
  1034.         local height = 203 //All 11 shown
  1035.         if ( user < 2 ) then
  1036.             height = 135 //7 shown
  1037.         elseif ( user < 3 ) then
  1038.             height = 170 //9 shown
  1039.         end
  1040.        
  1041.  
  1042.         list:SetSize(30,height)
  1043.         //list:SizeToContents()
  1044.         list:AddColumn("Tool Mode")
  1045.         list:SetMultiSelect(false)
  1046.         function list:OnRowSelected(LineID, line)
  1047.             if not (mode == LineID) then
  1048.                 RunConsoleCommand("precision_setmode", LineID)
  1049.             end
  1050.         end
  1051.  
  1052.         if ( mode == 1 ) then
  1053.             list:AddLine(" 1 ->Apply<- (Directly apply settings to target)")
  1054.         else
  1055.             list:AddLine(" 1   Apply   (Directly apply settings to target)")
  1056.         end
  1057.         if ( mode == 2 ) then
  1058.             list:AddLine(" 2 ->Rotate<- (Turn an object without moving it)")
  1059.         else
  1060.             list:AddLine(" 2   Rotate   (Turn an object without moving it)")
  1061.         end
  1062.         if ( mode == 3 ) then
  1063.             list:AddLine(" 3 ->Move<- (Snap objects together - Great for building!)")
  1064.         else
  1065.             list:AddLine(" 3   Move   (Snap objects together - Great for building!)")
  1066.         end
  1067.  
  1068.         list:SortByColumn(1)
  1069.         Panel:AddItem(list)
  1070.  
  1071.         if ( mode >= 4 && mode <= 8 ) then
  1072.             Panel:AddControl( "Checkbox", { Label = "Move Target? ('Easy' constraint mode)", Command = "precision_move", Description = "Uncheck this to apply the constraint without altering positions." } )
  1073.         end
  1074.         if (  mode >= 3 && mode <= 8 ) then
  1075.             Panel:AddControl( "Checkbox", { Label = "Rotate Target? (Rotation after moving)", Command = "precision_rotate", Description = "Uncheck this to remove the extra click for rotation. Handy for speed building." } )
  1076.             //Panel:AddControl( "Label", { Text = "This is the distance from touching of the targeted props after moving:", Description = "Use 0 mostly, % takes the second prop's width." }  )
  1077.             Panel:AddControl( "Slider",  { Label    = "Snap Distance",
  1078.                     Type    = "Float",
  1079.                     Min     = 0,
  1080.                     Max     = 10,
  1081.                     Command = "precision_offset",
  1082.                     Description = "Distance offset between joined props.  Type in negative to inset when moving."}   )
  1083.             Panel:AddControl( "Checkbox", { Label = "Snap distance as Percent (%) of target's depth", Command = "precision_offsetpercent", Description = "Unchecked = Exact units, Checked = takes % of width from second prop" } )
  1084.         end
  1085.         if ( mode >= 2 && mode <= 8 ) then
  1086.             Panel:AddControl( "Slider",  { Label    = "Rotation Snap (Degrees)",
  1087.                     Type    = "Float",
  1088.                     Min     = 0.02,
  1089.                     Max     = 90,
  1090.                     Command = "precision_rotation",
  1091.                     Description = "Rotation rotates by this amount at a time. No more guesswork. Min: 0.02 degrees "}    ):SetDecimals( 4 )
  1092.         end
  1093.         if ( mode <= 8 ) then
  1094.             Panel:AddControl( "Checkbox", { Label = "Freeze Target", Command = "precision_freeze", Description = "Freeze props when this tool is used" } )
  1095.  
  1096.             if ( mode >= 3 && mode <= 8 ) then
  1097.                 Panel:AddControl( "Checkbox", { Label = "No Collide Targets", Command = "precision_nocollide", Description = "Nocollide pairs of props when this tool is used. Note: No current way to remove this constraint when used alone."  } )
  1098.             end
  1099.         end
  1100.  
  1101.         if ( user >= 2 || mode == 1 ) then
  1102.             if ( (mode >= 3 && mode <= 8) || mode == 1 ) then
  1103.                 Panel:AddControl( "Checkbox", { Label = "Auto-align to world (nearest 45 degrees)", Command = "precision_autorotate", Description = "Rotates to the nearest world axis (similar to holding sprint and use with physgun)"  } )
  1104.             end
  1105.  
  1106.             if ( mode == 1 ) then
  1107.                 Panel:AddControl( "Checkbox", { Label = "Disable target shadow", Command = "precision_ShadowDisable", Description = "Disables shadows cast from the prop"  } )
  1108.             end
  1109.         end
  1110.  
  1111.         if ( user >= 3 ) then
  1112.             if ( mode == 1 ) then //apply
  1113.                 Panel:AddControl( "Checkbox", { Label = "Only Collide with Player", Command = "precision_nocollideall", Description = "Nocollides the first prop to everything and the world (except players collide with it). Warning: don't let it fall away through the world."  } )
  1114.                 Panel:AddControl( "Checkbox", { Label = "Disable Physics on object", Command = "precision_physdisable", Description = "Disables physics on the first prop (gravity, being shot etc won't effect it)"  } )
  1115.                 Panel:AddControl( "Checkbox", { Label = "Adv: Allow Physgun on PhysDisable objects", Command = "precision_allowphysgun", Description = "Disabled to stop accidents, use if you want to be able to manually move props after phyics disabling them (may break clipboxes)."  } )
  1116.                
  1117.                 //Panel:AddControl( "Checkbox", { Label = "Drag", Command = "precision_drag", Description = ""  } )
  1118.             end
  1119.             if ( mode == 9 ) then //parent
  1120.                 Panel:AddControl( "Checkbox", { Label = "Adv: Allow Physgun on Parented objects", Command = "precision_allowphysgun", Description = "Disabled to stop accidents, use this if you want to play with the parenting hierarchy etc."  } )
  1121.             end
  1122.         end
  1123.         if ( user >= 2 ) then
  1124.             if ( mode != 2 && mode != 3 && mode != 10 ) then //todo: entire contrap move/rotate support
  1125.                 Panel:AddControl( "Checkbox", { Label = "Entire Contraption! (Everything connected to target)", Command = "precision_entirecontrap", Description = "For mass constraining or removal or nudging or applying of things. Yay generic."  } )
  1126.             end
  1127.         end
  1128.  
  1129.         if ( mode == 9 ) then //parent
  1130.             Panel:AddControl( "Label", { Text = "Parenting Notes:", Description = "Due to lack of working descriptions at time of coding" }  )
  1131.             Panel:AddControl( "Label", { Text = "Parenting objects is most similar to a very strong weld, but it stops most interaction on the first object when you attach it to the second.  Players can walk on it, but it will fall through players.  It will not collide with objects or the world.  It will also not cause any extra physics lag/spaz.  Try it out on a test object, and decide if it's useful to you!", Description  = "Due to lack of working descriptions at time of coding" }  )
  1132.  
  1133.             Panel:AddControl( "Label", { Text = "Parented objects are most useful for: Adding detail to moving objects without creating extra physics lag.  Things like houses that you want to move (though you can only safely walk on parented objects when they are still.)", Description   = "Due to lack of working descriptions at time of coding" }  )
  1134.  
  1135.             Panel:AddControl( "Label", { Text = "Possible issues:  Remove constraints first to avoid spaz. Duplicating or such may cause the collision model to become separated.  Best to test it if in doubt.", Description   = "Why must labels cause menu flicker? D:" }  )
  1136.         end
  1137.        
  1138.         if ( mode == 10 ) then //repair
  1139.             Panel:AddControl( "Label", { Text = "Repair mode", Description  = "" }  )
  1140.             Panel:AddControl( "Label", { Text = "Usage: When a contraption is going crazy, colliding, making rubbing noises.", Description  = "" }  )
  1141.             Panel:AddControl( "Label", { Text = "What it does: Temporarily toggles collisions, allowing things that are bent out of shape to pop back.", Description    = "" }  )
  1142.             Panel:AddControl( "Label", { Text = "Warning: No guarantees.  This may turn things inside-out or make things worse depending on the situation.", Description    = "" }  )
  1143.         end
  1144.         if ( mode == 11 ) then //removal
  1145.             Panel:AddControl( "Label", { Text = "This mode will remove:", Description   = "" }  )
  1146.             Panel:AddControl( "Checkbox", { Label = "Nocollide", Command = "precision_removal_nocollide", Description = "" } )
  1147.             Panel:AddControl( "Checkbox", { Label = "Weld", Command = "precision_removal_weld", Description = "" } )
  1148.             Panel:AddControl( "Checkbox", { Label = "Axis", Command = "precision_removal_axis", Description = "" } )
  1149.             Panel:AddControl( "Checkbox", { Label = "Ballsocket", Command = "precision_removal_ballsocket", Description = "" } )
  1150.             Panel:AddControl( "Checkbox", { Label = "Adv. Ballsocket", Command = "precision_removal_advballsocket", Description = "" } )
  1151.             Panel:AddControl( "Checkbox", { Label = "Slider", Command = "precision_removal_slider", Description = "" } )
  1152.             Panel:AddControl( "Checkbox", { Label = "Parent", Command = "precision_removal_parent", Description = "" } )
  1153.             Panel:AddControl( "Checkbox", { Label = "Other", Command = "precision_removal_other", Description = "" } )
  1154.             Panel:AddControl( "Label", { Text = "(Other = Rope/slider variants like winch/hydraulic, also motor/keepupright)", Description  = "" }  )
  1155.             Panel:AddControl( "Button", { Label = "Select All", Command = "precision_removal_all", Description = ""  } )
  1156.             Panel:AddControl( "Button", { Label = "Select None", Command = "precision_removal_none", Description = ""  } )
  1157.  
  1158.         end
  1159.  
  1160.  
  1161.  
  1162.  
  1163.         local params = {Label = "User Level",Description = "Shows options appropriate to user experience level", MenuButton = "0", Height = 67, Options = {}}
  1164.         if ( user == 1 ) then
  1165.             params.Options[" 1 ->Normal<-"] = { precision_setuser = "1" }
  1166.         else
  1167.             params.Options[" 1   Normal"] = { precision_setuser = "1" }
  1168.         end
  1169.             Panel:AddControl( "Checkbox", { Label = "Enable tool feedback messages?", Command = "precision_enablefeedback", Description = "Toggle for feedback messages incase they get annoying"  } )
  1170.             Panel:AddControl( "Checkbox", { Label = "On = Feedback in Chat, Off = Centr Scrn", Command = "precision_chatfeedback", Description = "Chat too cluttered? Can have messages centre screen instead"  } )
  1171.             //Panel:AddControl( "Checkbox", { Label = "Hide Menu Tips?", Command = "precision_hidehints", Description = "Streamline the menu once you're happy with using the tool."  } )
  1172.             Panel:AddControl( "Checkbox", { Label = "Add Push/Pull to Undo List", Command = "precision_nudgeundo", Description = "For if you're in danger of nudging somthing to where you can't reach it"  } )
  1173.             Panel:AddControl( "Checkbox", { Label = "Add Movement to Undo List", Command = "precision_moveundo", Description = "So you don't have to secondary fire with nocollide to undo mistakes"  } )
  1174.             Panel:AddControl( "Checkbox", { Label = "Add Rotation to Undo List", Command = "precision_rotateundo", Description = "So you can find the exact rotation value easier"  } )
  1175.             Panel:AddControl( "Button", { Label = "Restore Current Mode Default", Command = "precision_defaultrestore", Description = "Collapse menu"  } )
  1176.     end
  1177.  
  1178.  
  1179.  
  1180.     local function precision_defaults()
  1181.         local mode = LocalPlayer():GetInfoNum( "precision_mode", 3 )
  1182.         if mode  == 1 then
  1183.             RunConsoleCommand("precision_freeze", "1")
  1184.             RunConsoleCommand("precision_autorotate", "1")
  1185.             RunConsoleCommand("precision_ShadowDisable", "0")
  1186.             RunConsoleCommand("precision_nocollideall", "0")
  1187.             RunConsoleCommand("precision_physdisable", "0")
  1188.             RunConsoleCommand("precision_allowphysgun", "0")
  1189.             RunConsoleCommand("precision_entirecontrap", "0")
  1190.         elseif mode == 2 then
  1191.             RunConsoleCommand("precision_rotation", "15")
  1192.             RunConsoleCommand("precision_freeze", "1")
  1193.             RunConsoleCommand("precision_entirecontrap", "0")
  1194.         elseif mode == 3 then
  1195.             RunConsoleCommand("precision_rotate", "1")
  1196.             RunConsoleCommand("precision_offset", "0")
  1197.             RunConsoleCommand("precision_offsetpercent", "1")
  1198.             RunConsoleCommand("precision_rotation", "15")
  1199.             RunConsoleCommand("precision_freeze", "1")
  1200.             RunConsoleCommand("precision_nocollide", "1")
  1201.             RunConsoleCommand("precision_autorotate", "1")
  1202.             RunConsoleCommand("precision_entirecontrap", "0")
  1203.         elseif mode == 5 then
  1204.             RunConsoleCommand("precision_move", "1")
  1205.             RunConsoleCommand("precision_rotate", "1")
  1206.             RunConsoleCommand("precision_offset", "0")
  1207.             RunConsoleCommand("precision_offsetpercent", "1")
  1208.             RunConsoleCommand("precision_rotation", "15")
  1209.             RunConsoleCommand("precision_freeze", "1")
  1210.             RunConsoleCommand("precision_nocollide", "1")
  1211.             RunConsoleCommand("precision_autorotate", "0")
  1212.             RunConsoleCommand("precision_entirecontrap", "0")
  1213.             RunConsoleCommand("precision_forcelimit", "0")
  1214.             RunConsoleCommand("precision_torquelimit", "0")
  1215.             RunConsoleCommand("precision_friction", "0")
  1216.         elseif mode == 6 then
  1217.             RunConsoleCommand("precision_move", "1")
  1218.             RunConsoleCommand("precision_rotate", "1")
  1219.             RunConsoleCommand("precision_offset", "0")
  1220.             RunConsoleCommand("precision_offsetpercent", "1")
  1221.             RunConsoleCommand("precision_rotation", "15")
  1222.             RunConsoleCommand("precision_freeze", "1")
  1223.             RunConsoleCommand("precision_nocollide", "1")
  1224.             RunConsoleCommand("precision_autorotate", "0")
  1225.             RunConsoleCommand("precision_entirecontrap", "0")
  1226.             RunConsoleCommand("precision_forcelimit", "0")
  1227.             RunConsoleCommand("precision_torquelimit", "0")
  1228.         elseif mode == 7 then
  1229.             RunConsoleCommand("precision_move", "0")
  1230.             RunConsoleCommand("precision_rotate", "1")
  1231.             RunConsoleCommand("precision_offset", "0")
  1232.             RunConsoleCommand("precision_offsetpercent", "1")
  1233.             RunConsoleCommand("precision_rotation", "15")
  1234.             RunConsoleCommand("precision_freeze", "1")
  1235.             RunConsoleCommand("precision_nocollide", "1")
  1236.             RunConsoleCommand("precision_autorotate", "0")
  1237.             RunConsoleCommand("precision_entirecontrap", "0")
  1238.             RunConsoleCommand("precision_forcelimit", "0")
  1239.             RunConsoleCommand("precision_torquelimit", "0")
  1240.             RunConsoleCommand("precision_XRotMin", "0")
  1241.             RunConsoleCommand("precision_XRotMax", "0")
  1242.             RunConsoleCommand("precision_YRotMin", "0")
  1243.             RunConsoleCommand("precision_YRotMax", "0")
  1244.             RunConsoleCommand("precision_ZRotMin", "0")
  1245.             RunConsoleCommand("precision_ZRotMax", "0")
  1246.             RunConsoleCommand("precision_XRotFric", "0")
  1247.             RunConsoleCommand("precision_YRotFric", "0")
  1248.             RunConsoleCommand("precision_ZRotFric", "0")
  1249.             RunConsoleCommand("precision_FreeMov", "1")
  1250.         elseif mode == 9 then
  1251.             RunConsoleCommand("precision_allowphysgun", "0")
  1252.             RunConsoleCommand("precision_entirecontrap", "0")
  1253.         end
  1254.         precision_updatecpanel()
  1255.     end
  1256.     concommand.Add( "precision_defaultrestore", precision_defaults )
  1257.  
  1258.     local function precision_genmenu()
  1259.         if ( showgenmenu == 1 ) then
  1260.             showgenmenu = 0
  1261.         else
  1262.             showgenmenu = 1
  1263.         end
  1264.         precision_updatecpanel()
  1265.     end
  1266.     concommand.Add( "precision_generalmenu", precision_genmenu )
  1267.    
  1268.  
  1269.     function precision_setmode( player, tool, args )
  1270.         if LocalPlayer():GetInfoNum( "precision_mode", 3 ) != args[1] then
  1271.             RunConsoleCommand("precision_mode", args[1])
  1272.             timer.Simple(0.05, function() precision_updatecpanel() end )
  1273.         end
  1274.     end
  1275.     concommand.Add( "precision_setmode", precision_setmode )
  1276.  
  1277.  
  1278.     function precision_setuser( player, tool, args )
  1279.         if LocalPlayer():GetInfoNum( "precision_user", 3 ) != args[1] then
  1280.             RunConsoleCommand("precision_user", args[1])
  1281.             timer.Simple(0.05, function() precision_updatecpanel() end )
  1282.         end
  1283.     end
  1284.     concommand.Add( "precision_setuser", precision_setuser )
  1285.  
  1286.  
  1287.     function precision_updatecpanel()
  1288.         local Panel = controlpanel.Get( "precision" )
  1289.         if (!Panel) then return end
  1290.         //custom panel building ( wtf does Panel:AddDefaultControls() get its defaults from? )
  1291.         AddDefControls( Panel )
  1292.     end
  1293.     concommand.Add( "precision_updatecpanel", precision_updatecpanel )
  1294.  
  1295.     function TOOL.BuildCPanel( Panel )
  1296.         AddDefControls( Panel )
  1297.     end
  1298.  
  1299.     local function precision_removalall()
  1300.         RunConsoleCommand("precision_removal_nocollide", "1")
  1301.         RunConsoleCommand("precision_removal_weld", "1")
  1302.         RunConsoleCommand("precision_removal_axis", "1")
  1303.         RunConsoleCommand("precision_removal_ballsocket", "1")
  1304.         RunConsoleCommand("precision_removal_advballsocket", "1")
  1305.         RunConsoleCommand("precision_removal_slider", "1")
  1306.         RunConsoleCommand("precision_removal_parent", "1")
  1307.         RunConsoleCommand("precision_removal_other", "1")
  1308.         precision_updatecpanel()
  1309.     end
  1310.     concommand.Add( "precision_removal_all", precision_removalall )
  1311.     local function precision_removalnone()
  1312.         RunConsoleCommand("precision_removal_nocollide", "0")
  1313.         RunConsoleCommand("precision_removal_weld", "0")
  1314.         RunConsoleCommand("precision_removal_axis", "0")
  1315.         RunConsoleCommand("precision_removal_ballsocket", "0")
  1316.         RunConsoleCommand("precision_removal_advballsocket", "0")
  1317.         RunConsoleCommand("precision_removal_slider", "0")
  1318.         RunConsoleCommand("precision_removal_parent", "0")
  1319.         RunConsoleCommand("precision_removal_other", "0")
  1320.         precision_updatecpanel()
  1321.     end
  1322.     concommand.Add( "precision_removal_none", precision_removalnone )
  1323.  
  1324.     function TOOL:FreezeMovement()
  1325.         local stage = self:GetStage()
  1326.         if ( stage == 2 ) then
  1327.             return true
  1328.         //elseif ( iNum > 0 && self:GetClientNumber("mode") == 2 ) then
  1329.         //  return true
  1330.         end
  1331.         return false
  1332.     end
  1333. end
  1334.  
  1335. function TOOL:Holster()
  1336.     self:ClearObjects()
  1337.     self:SetStage(0)
  1338.     self:ClearSelection()
  1339. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement