GMADytuUPrecision Alignment { "description": "Description", "type": "tool", "tags": [ "build", "realism" ] }Author Namelua/autorun/server/sv_precision_align.luaÕ¡g0Elua/weapons/gmod_tool/stools/precision_align.luaÉ)U%£Ælua/weapons/gmod_tool/stools/precision_align/manipulation_panel.luaŽ)ÊV £lua/weapons/gmod_tool/stools/precision_align/prop_functions.lua=€I.·´lua/weapons/gmod_tool/stools/precision_align/tool_screen.luaÜ<}3zlua/weapons/gmod_tool/stools/precision_align/ui.lua#&&-…// Precision Alignment serverside code - By Wenli if CLIENT then return end local PA = "precision_align" local PA_ = PA .. "_" util.AddNetworkString( PA_ .. "constraint" ) util.AddNetworkString( PA_ .. "click" ) util.AddNetworkString( PA_ .. "ent" ) // Used to record each player's last PA action local action_table = {} CreateConVar( PA_ .. "stack_delay", 0.01 ) // Undo local function UndoMove( Undo, ent, pos, ang ) if IsValid(ent) then ent:SetPos( pos ) ent:SetAngles( ang ) end end // Sounds local function playsound( ply, bool ) if bool then ply:EmitSound("buttons/button14.wav", 100, 100) end end //********************************************************************************************************************// // Mirror lists //********************************************************************************************************************// // Exceptions lists - all lower case! // General list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate1x2_tri.mdl", Angle(180,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate1_tri.mdl", Angle(180,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate2x2_tri.mdl", Angle(180,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate2x4_tri.mdl", Angle(180,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate4x4_tri.mdl", Angle(180,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/plastic/plastic_angle_90.mdl", Angle(180,0,180) ) // Misc list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/stair1x1inside.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/stair1x1outside.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/gibs/wooden_wheel1_gib2.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/gibs/wooden_wheel1_gib3.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/gibs/wooden_wheel2_gib1.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/gibs/wooden_wheel2_gib2.mdl", Angle(0,180,0) ) // Robotics // Most of these are (180,0,0) since it's easier to set the whole of robotics as (0,180,0) in exceptions 2 list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/foot.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/j1.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/j2.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/j3.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/j4.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/stand.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/xfoot.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/xfoot.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/j1.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/j2.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/j3.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/j4.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/claw2l.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/clawl.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/claw.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/robotics/claw2.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/claw_hub_8.mdl", Angle(180,0,0) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/roboticslarge/claw_hub_8l.mdl", Angle(180,0,0) ) // Solid Steel list.Set( "PA_mirror_exceptions_specific", "models/mechanics/solid_steel/sheetmetal_90_4.mdl", Angle(0,-90,180) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/solid_steel/sheetmetal_box90_4.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/solid_steel/sheetmetal_h90_4.mdl", Angle(180,0,90) ) list.Set( "PA_mirror_exceptions_specific", "models/mechanics/solid_steel/sheetmetal_t_4.mdl", Angle(0,0,180) ) // Specialized list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_angle90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_dome90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate_curve.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_plate_curve2x2.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_wire_angle90x1.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/metal_wire_angle90x2.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/glass/glass_angle90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/glass/glass_curve90x1.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/glass/glass_curve90x2.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/glass/glass_dome90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/windows/window_angle90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/windows/window_curve90x1.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/windows/window_curve90x2.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/windows/window_dome90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/wood/wood_angle90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/wood/wood_curve90x1.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/wood/wood_curve90x2.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/wood/wood_dome90.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/wood/wood_wire_angle90x1.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/construct/wood/wood_wire_angle90x2.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/platehole1x1b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/platehole1x1d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x1b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x1d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x2b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x2d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x3b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x3d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x4b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x4d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x5b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x5d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x6b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x6d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x8b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube1x1x8d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/circle2x2b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/circle2x2d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/plates/platehole1x1.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/plates/platehole3.mdl", Angle(0,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/shell2x2b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/shell2x2d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/shell2x2e.mdl", Angle(180,135,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/shell2x2x45.mdl", Angle(180,135,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x025b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x025d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x05b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x05d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x1b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x1d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x2b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x2d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x4b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x4d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x8b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x8d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2x16d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube2x2xtb.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tubebend1x2x90b.mdl", Angle(90,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tubebendinsidesquare.mdl", Angle(-90,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/circle4x4b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/circle4x4d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/platehole4x4b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/misc/platehole4x4d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x025b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x025d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x05b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x05d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x1b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x1d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x2b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x2d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x3b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x3d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x4b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x4d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x5b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x5d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x6b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x6d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x8b.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x8d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tube4x4x16d.mdl", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/tubes/tubebend4x4x90.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/025x025.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/05x05.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/075x075.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x2.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/3x3.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/4x4.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/5x5.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/6x6.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/7x7.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/8x8.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/plates/tri2x1.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/plates/tri3x1.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/05x05x05.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x05x05.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x05x1.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x1.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x2.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x3.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x4.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x5.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x1x1.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x2x1.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x2x2.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/3x2x2.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/3x3x2.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x1carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x1x1carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x2x1carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x2carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x1x2carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x2x2carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x4carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/2x2x4carved.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x1carved025.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x2carved025.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/triangles/1x1x4carved025.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/panel45.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/panel90.mdl", Angle(180,0,90) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/box2s.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/box3s.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/box4s.mdl", Angle(0,90,180) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/boxtri.mdl", Angle(180,-90,0) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/deg45.mdl", Angle(0,180,-45) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/deg45single.mdl", Angle(0,180,-45) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/deg90.mdl", Angle(0,180,-90) ) list.Set( "PA_mirror_exceptions_specific", "models/xqm/deg90single.mdl", Angle(0,180,-90) ) // Transportation list.Set( "PA_mirror_exceptions_specific", "models/props_phx/misc/propeller3x_small.mdl", Angle(0,120,180) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/huge/road_curve.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/trains/tracks/track_turn45.mdl", Angle(180,135,0) ) list.Set( "PA_mirror_exceptions_specific", "models/props_phx/trains/tracks/track_turn90.mdl", Angle(180,90,0) ) // Geometric list.Set( "PA_mirror_exceptions_specific", "models/hunter/geometric/hex025x1.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/geometric/hex1x05.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/geometric/para1x1.mdl", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions_specific", "models/hunter/geometric/pent1x1.mdl", Angle(0,0,180) ) // Vehicles list.Set( "PA_mirror_exceptions_specific", "models/nova/airboat_seat.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/nova/chair_office01.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/nova/chair_office02.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions_specific", "models/nova/jeep_seat.mdl", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions", "models/hunter/tubes/tubebend", Angle(180,0,-90) ) list.Set( "PA_mirror_exceptions", "models/hunter/plates/tri", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions", "models/xqm/quad", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions", "models/xqm/rhombus", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions", "models/xqm/triangle", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions", "models/phxtended/tri", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions", "models/mechanics/robotics", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions", "models/mechanics/solid_steel/steel_beam45", Angle(0,90,180) ) list.Set( "PA_mirror_exceptions", "models/mechanics/solid_steel/type_c_", Angle(0,90,180) ) list.Set( "PA_mirror_exceptions", "models/mechanics/solid_steel/type_d_", Angle(0,135,180) ) list.Set( "PA_mirror_exceptions", "models/mechanics/solid_steel/type_e_", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions", "models/squad/sf_tris/sf_tri", Angle(180,90,0) ) list.Set( "PA_mirror_exceptions", "models/xqm/jettailpiece1", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions", "models/xqm/jetwing2", Angle(0,180,0) ) list.Set( "PA_mirror_exceptions", "models/xqm/wing", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions", "models/xeon133/racewheel/", Angle(0,0,180) ) list.Set( "PA_mirror_exceptions", "models/xeon133/racewheelskinny/", Angle(0,0,180) ) //********************************************************************************************************************// // Duplicate ents //********************************************************************************************************************// local last_ent -- Used for nocollide local stack_queue = {} local undo_table = {} local processing = false // stack_ID is used to keep track within this file, stackID is used locally within functions local stack_ID = 0 // Duplicate an entity in the stack queue local function Queue_Process() local ent_table = table.remove( stack_queue, 1 ) // Stop processing the stack queue if !ent_table then last_ent = nil processing = false return false end local ply = ent_table.ply if !IsValid( ply ) then return false end // Stack entity local ent = duplicator.CreateEntityFromTable( ply, ent_table.data ) if !IsValid( ent ) then return false end local stackID = ent_table.stackID // Apply dupe info ent.EntityMods = table.Copy( ent_table.data.EntityMods ) duplicator.ApplyEntityModifiers( ply, ent ) duplicator.DoGenericPhysics( ent, ply, ent_table.data ) ent:SetCollisionGroup( ent_table.collision_group ) // Apply nocollide if ent_table.nocollide then // Nocollide each stacked ent with previous if IsValid( last_ent ) then constraint.NoCollide( ent, last_ent ) end // Nocollide selected ent with final stacked ent if !stack_queue[1] or stackID ~= stack_queue[1].stackID then constraint.NoCollide( ent, ent_table.data.Entity ) last_ent = nil else last_ent = ent end else last_ent = nil end ent:GetPhysicsObject():EnableMotion( false ) return { ply = ply, ent = ent, stackID = stackID } end cleanup.Register( "stacks" ) // Create the undo function at the end of each stack_ID queue local function Stack_Undo( undo_table ) -- if table.Count( undo_table ) == 0 then return end local ply = undo_table[1].ply local model = "" if IsValid( undo_table[1].ent ) then model = undo_table[1].ent:GetModel() or "" end undo.Create( "PA Stack " .. tostring(#undo_table) .. "x (" .. model .. ")" ) for i = 1, #undo_table do local ent = undo_table[i].ent undo.SetPlayer( ply ) undo.AddEntity( ent ) ply:AddCleanup( "stacks", ent ) end undo.Finish() table.Empty( undo_table ) end // Handle stack queueing (called by timer) local stackID_old local function Stack_Loop() if #stack_queue > 0 and processing then local r, e = pcall( Queue_Process ) if !r then ErrorNoHalt( e, "\n" ) else // Create undo if stack_IDs are different if !stackID_old then stackID_old = e.stackID elseif stackID_old ~= e.stackID then Stack_Undo( undo_table ) stackID_old = e.stackID end table.insert( undo_table, e ) return true end end // Cleanup after stacking timer.Destroy( "PA_StackTimer" ) last_ent = nil processing = false Stack_Undo( undo_table ) stackID_old = nil return false end local function Queue_Add( ply, ent, stackID ) local ent_table = { ply = ply, nocollide = ply:GetInfoNum( PA_ .. "stack_nocollide", 0 ) ~= 0, data = duplicator.CopyEntTable( ent ), collision_group = ent:GetCollisionGroup(), stackID = stackID } table.insert( stack_queue, ent_table ) // Start processing the stack queue if !processing then processing = true local Delay = GetConVarNumber( PA_ .. "stack_delay", 0.1 ) timer.Create( "PA_StackTimer", Delay, 0, Stack_Loop ) end end //********************************************************************************************************************// // Move ents //********************************************************************************************************************// // Args list: // 1,2,3 = Movement vector // 4 = Stack number // 5 = Repeat flag function precision_align_move_func( ply, cmd, args ) if !ply.PA_activeent then return false end local ent = ply.PA_activeent if !IsValid(ent) then return false end if !util.IsValidPhysicsObject(ent, 0) or IsValid(ent:GetParent()) then return false end local v = Vector(args[1], args[2], args[3]) if v == Vector(0,0,0) then return false end local stack = tonumber(args[4]) or 0 stack = math.Round(math.Clamp( stack, 0, 20 )) local startpos = ent:GetPos() local startang = ent:GetAngles() // Begin stacking if stack > 0 then stack_ID = stack_ID + 1 end for stacks = 1, math.max(stack, 1) do // Add to stack queue if stack > 0 then Queue_Add( ply, ent, stack_ID ) end // Move entity local pos = ent:GetPos() local ang = ent:GetAngles() ent:SetPos(pos + v) end ent:GetPhysicsObject():EnableMotion( false ) local model = ent:GetModel() or "" undo.Create("PA Move (" .. model .. ")") undo.SetPlayer( ply ) undo.AddFunction( UndoMove, ent, startpos, startang ) undo.Finish() // Record action if !args[5] then action_table[ply] = args action_table[ply][5] = true action_table[ply].cmd = precision_align_move_func end playsound( ply, true ) return true end concommand.Add( PA_.. "move", precision_align_move_func ) //********************************************************************************************************************// // Rotate ents //********************************************************************************************************************// // Rotate by world axes local function rotate_world( ang, rotang ) if rotang.p != 0 then ang:RotateAroundAxis( Vector(0,1,0), rotang.p ) end if rotang.y != 0 then ang:RotateAroundAxis( Vector(0,0,1), rotang.y ) end if rotang.r != 0 then ang:RotateAroundAxis( Vector(1,0,0), rotang.r ) end return ang end // Convert from euler to axis-angle by quaternion method local function euler_to_axisangle( ang ) if !ang then return false end ang = Angle( math.rad( ang.p ), math.rad( ang.y ), math.rad( ang.r ) ) * 0.5 local c1 = math.cos( ang.y ) local c2 = math.cos( ang.p ) local c3 = math.cos( ang.r ) local s1 = math.sin( ang.y ) local s2 = math.sin( ang.p ) local s3 = math.sin( ang.r ) local c1c2 = c1 * c2 local s1s2 = s1 * s2 local w = c1 * c2 * c3 + s1 * s2 * s3 local x = c1c2 * s3 - s1s2 * c3 local y = c1 * s2 * c3 + s1 * c2 * s3 local z = s1 * c2 * c3 - c1 * s2 * s3 local angle = 2 * math.deg( math.acos( w ) ) local axis = Vector( x, y, z ) if axis:Length() < 0.001 then axis = Vector( 0, 0, 1 ) else axis = axis:GetNormal() end return axis, angle end // Three methods of rotation - absolute(0), relative(1), world(2) and axis/angle(3) // Args list: // 1,2,3 = Rotation Angle // 4,5,6 = Pivot Vector // 7 = Rotation type // 8 = Stack number // 9 = Repeat flag function precision_align_rotate_func( ply, cmd, args ) if !ply.PA_activeent then return false end local ent = ply.PA_activeent if !IsValid(ent) then return false end if !util.IsValidPhysicsObject(ent, 0) or IsValid(ent:GetParent()) then return false end if !tonumber(args[1]) or !tonumber(args[2]) or !tonumber(args[3]) then return false end local startpos = ent:GetPos() local startang = ent:GetAngles() // Change angle method depending on the "relative" variable local a local relative = tonumber(args[7]) or 0 local test if relative == 1 then local localang = Angle( tonumber(args[1]), tonumber(args[2]), tonumber(args[3]) ) if localang == Angle(0, 0, 0) then return false end a = ent:LocalToWorldAngles( localang ) elseif relative == 2 then a = Angle(startang.p, startang.y, startang.r) local rotang = Angle( tonumber(args[1]), tonumber(args[2]), tonumber(args[3]) ) a = rotate_world( a, rotang ) elseif relative == 3 then local axis = Vector( tonumber(args[1]), tonumber(args[2]), tonumber(args[3]) ) a = Angle(startang.p, startang.y, startang.r) a:RotateAroundAxis( axis:GetNormal(), axis:Length() ) else a = Angle( tonumber(args[1]), tonumber(args[2]), tonumber(args[3]) ) if a == startang then return false end end // Get pivot point local v if !tonumber(args[4]) or !tonumber(args[5]) or !tonumber(args[6]) then v = startpos else v = Vector( tonumber(args[4]), tonumber(args[5]), tonumber(args[6]) ) end local stack = tonumber(args[8]) or 0 stack = math.Round(math.Clamp( stack, 0, 20 )) // Figure out parameters for relative angle stacking if stack >= 2 then a = ent:WorldToLocalAngles( a ) end // Begin stacking for stacks = 1, math.max(stack, 1) do // Add to stack queue if stack > 0 then Queue_Add( ply, ent ) end // Rotate entity local pos = ent:GetPos() local ang = ent:GetAngles() local localv // Stack by absolute angle if stack < 2 then if v == pos then ent:SetAngles(a) else localv = ent:WorldToLocal(v) ent:SetAngles(a) pos = pos + ( v - ent:LocalToWorld(localv) ) ent:SetPos(pos) end // Stack by relative angle else if v == pos then ent:SetAngles( ent:LocalToWorldAngles( a ) ) else localv = ent:WorldToLocal(v) ent:SetAngles( ent:LocalToWorldAngles( a ) ) pos = pos + ( v - ent:LocalToWorld(localv) ) ent:SetPos(pos) end end end ent:GetPhysicsObject():EnableMotion( false ) local model = ent:GetModel() or "" undo.Create("PA Rotate (" .. model .. ")") undo.SetPlayer( ply ) undo.AddFunction( UndoMove, ent, startpos, startang ) undo.Finish() // Record action if !args[9] then action_table[ply] = args action_table[ply][9] = true action_table[ply].cmd = precision_align_rotate_func end playsound( ply, true ) return true end concommand.Add( PA_.. "rotate", precision_align_rotate_func ) //********************************************************************************************************************// // Mirror ents //********************************************************************************************************************// // This was converted to a server-side command since it involves both rotation + translation in one step, and massCenter isn't available on client // The mirror exception table can be changed on the server if needed for debugging/updating // Args list: // 1,2,3 = Plane Origin Vector // 4,5,6 = Plane Normal Vector // 7 = Stack number // 8 = Repeat flag function precision_align_mirror_func( ply, cmd, args ) if !ply.PA_activeent then return false end local ent = ply.PA_activeent if !IsValid(ent) then return false end if !util.IsValidPhysicsObject(ent, 0) or IsValid(ent:GetParent()) then return false end local origin = Vector(args[1], args[2], args[3]) local normal = Vector(args[4], args[5], args[6]) local stack = tonumber(args[7]) or 0 stack = math.Round(math.Clamp( stack, 0, 1 )) local pos = ent:GetPos() local ang = ent:GetAngles() // Mass centre seems to be most reliable way of finding a point on the plane of symmetry local v = ent:LocalToWorld(ent:GetPhysicsObject():GetMassCenter()) // Stack before mirroring local ent2 if stack > 0 then Queue_Add( ply, ent ) end local model = ent:GetModel() or "" undo.Create("PA Mirror (" .. model .. ")") undo.SetPlayer( ply ) undo.AddFunction( UndoMove, ent, pos, ang ) undo.Finish() // Mirror angle // Filter through exceptions for ents that need to be rotated differently local exceptionang local model = string.lower( ent:GetModel() ) local PA_mirror_exceptions_specific = list.Get( "PA_mirror_exceptions_specific" ) // Match entire string exceptionang = PA_mirror_exceptions_specific[model] // Match left part of string if !exceptionang then local PA_mirror_exceptions = list.Get( "PA_mirror_exceptions" ) for k, v in pairs( PA_mirror_exceptions ) do if string.match( model, "^" .. k ) then exceptionang = v break end end end if exceptionang then ang = ent:LocalToWorldAngles( exceptionang ) else ang = ent:LocalToWorldAngles( Angle(180,0,0) ) end ang:RotateAroundAxis( normal, 180 ) // Rotate around v, same method as rotation function local localv if v == pos then ent:SetAngles(ang) else localv = ent:WorldToLocal(v) ent:SetAngles(ang) pos = pos + ( v - ent:LocalToWorld(localv) ) end // Mirror position local length = normal:Dot(origin - v) local vec = normal * length * 2 ent:SetPos(pos + vec) ent:GetPhysicsObject():EnableMotion( false ) // Record action if !args[8] then action_table[ply] = args action_table[ply][8] = true action_table[ply].cmd = precision_align_mirror_func end playsound( ply, true ) return true end concommand.Add( PA_.. "mirror", precision_align_mirror_func ) //********************************************************************************************************************// // Constrain ents //********************************************************************************************************************// function precision_align_constraint_func( len, ply ) local data = net.ReadTable() local constraint_type = data.Type local Ent1 = Entity( data.Ent1 ) local Ent2 = Entity( data.Ent2 ) // Entity(0) returns Null rather than World on server if Ent1 == NULL then Ent1 = game.GetWorld() end if Ent2 == NULL then Ent2 = game.GetWorld() end if CPPI then if Ent1:IsWorld() then if !util.IsValidPhysicsObject(Ent2, 0) or !Ent2:CPPICanTool( ply, PA ) then return false end elseif Ent2:IsWorld() then if !util.IsValidPhysicsObject(Ent1, 0) or !Ent1:CPPICanTool( ply, PA ) then return false end elseif !util.IsValidPhysicsObject(Ent1, 0) or !util.IsValidPhysicsObject(Ent2, 0) then return false elseif !Ent1:CPPICanTool( ply, PA ) or !Ent2:CPPICanTool( ply, PA ) then return false end else if Ent1:IsWorld() and !util.IsValidPhysicsObject(Ent2, 0) then return false elseif Ent2:IsWorld() and !util.IsValidPhysicsObject(Ent1, 0) then return false end end local LPos1 = Vector( data.LPos1.x, data.LPos1.y, data.LPos1.z ) local LPos2 = Vector( data.LPos2.x, data.LPos2.y, data.LPos2.z ) local vars = data.vars local const if constraint_type == "Axis" then local forcelimit = ply:GetInfoNum( PA_.. "axis_forcelimit", 0 ) local torquelimit = ply:GetInfoNum( PA_.. "axis_torquelimit", 0 ) local friction = ply:GetInfoNum( PA_.. "axis_friction", 0 ) local nocollide = ply:GetInfoNum( PA_.. "axis_nocollide", 0 ) local axis if vars and vars ~= 0 then axis = Ent1:WorldToLocal( Ent1:LocalToWorld(LPos1) + Vector( vars.x, vars.y, vars.z ) ) end const = constraint.Axis( Ent1, Ent2, 0, 0, LPos1, LPos2, forcelimit, torquelimit, friction, nocollide, axis ) elseif constraint_type == "Ballsocket" then local forcelimit = ply:GetInfoNum( PA_.. "ballsocket_forcelimit", 0 ) local torquelimit = ply:GetInfoNum( PA_.. "ballsocket_torquelimit", 0 ) local nocollide = ply:GetInfoNum( PA_.. "ballsocket_nocollide", 0 ) const = constraint.Ballsocket( Ent2, Ent1, 0, 0, LPos1, forcelimit, torquelimit, nocollide ) elseif constraint_type == "Ballsocket Advanced" then local forcelimit = ply:GetInfoNum( PA_.. "ballsocket_adv_forcelimit", 0 ) local torquelimit = ply:GetInfoNum( PA_.. "ballsocket_adv_torquelimit", 0 ) local xmin = ply:GetInfoNum( PA_.. "ballsocket_adv_xmin", -180 ) local ymin = ply:GetInfoNum( PA_.. "ballsocket_adv_ymin", -180 ) local zmin = ply:GetInfoNum( PA_.. "ballsocket_adv_zmin", -180 ) local xmax = ply:GetInfoNum( PA_.. "ballsocket_adv_xmax", 180 ) local ymax = ply:GetInfoNum( PA_.. "ballsocket_adv_ymax", 180 ) local zmax = ply:GetInfoNum( PA_.. "ballsocket_adv_zmax", 180 ) local xfric = ply:GetInfoNum( PA_.. "ballsocket_adv_xfric", 0 ) local yfric = ply:GetInfoNum( PA_.. "ballsocket_adv_yfric", 0 ) local zfric = ply:GetInfoNum( PA_.. "ballsocket_adv_zfric", 0 ) local onlyrotation = ply:GetInfoNum( PA_.. "ballsocket_adv_onlyrotation", 0 ) local nocollide = ply:GetInfoNum( PA_.. "ballsocket_adv_nocollide", 0 ) const = constraint.AdvBallsocket( Ent1, Ent2, 0, 0, LPos1, LPos2, forcelimit, torquelimit, xmin, ymin, zmin, xmax, ymax, zmax, xfric, yfric, zfric, onlyrotation, nocollide ) elseif constraint_type == "Elastic" then local constant = ply:GetInfoNum( PA_.. "elastic_constant", 0 ) local damping = ply:GetInfoNum( PA_.. "elastic_damping", 0 ) local rdamping = ply:GetInfoNum( PA_.. "elastic_rdamping", 0 ) local material = ply:GetInfo( PA_.. "elastic_material", "cable/rope" ) local width = ply:GetInfoNum( PA_.. "elastic_width", 1 ) local stretchonly = ply:GetInfoNum( PA_.. "elastic_stretchonly", 0 ) const = constraint.Elastic( Ent1, Ent2, 0, 0, LPos1, LPos2, constant, damping, rdamping, material, width, stretchonly ) elseif constraint_type == "Rope" then local forcelimit = ply:GetInfoNum( PA_.. "rope_forcelimit", 0 ) local width = ply:GetInfoNum( PA_.. "rope_width", 1 ) local material = ply:GetInfo( PA_.. "rope_material", "cable/rope" ) local rigid = ply:GetInfoNum( PA_.. "rope_rigid", 0 ) != 0 local length = ply:GetInfoNum( PA_.. "rope_setlength", 0 ) local addlength = 0 if length <= 0 then addlength = ply:GetInfoNum( PA_.. "rope_addlength", 0 ) length = ( Ent1:LocalToWorld(LPos1) - Ent2:LocalToWorld(LPos2) ):Length() end const = constraint.Rope( Ent1, Ent2, 0, 0, LPos1, LPos2, length, addlength, forcelimit, width, material, rigid ) elseif constraint_type == "Slider" then local width = ply:GetInfoNum( PA_.. "slider_width", 0 ) const = constraint.Slider( Ent1, Ent2, 0, 0, LPos1, LPos2, width ) elseif constraint_type == "Wire Hydraulic" then local controller = Entity( vars ) if !IsValid( controller ) then return false end if controller:GetClass() ~= "gmod_wire_hydraulic" then print( "PA Error: Tried to create wire hydraulic with invalid controller ent" ) return false end local width = ply:GetInfoNum( PA_.. "wire_hydraulic_width", 1 ) local material = ply:GetInfo( PA_.. "wire_hydraulic_material", "cable/rope" ) // Create new constraint before removing the old one local oldconstraint = controller.constraint local oldrope = controller.rope local rope const, rope = MakeWireHydraulic( ply, Ent1, Ent2, 0, 0, LPos1, LPos2, width, material, 0, nil ) if const then controller.MyId = controller:EntIndex() const.MyCrtl = controller:EntIndex() controller:SetConstraint( const ) controller:DeleteOnRemove( const ) end if rope then controller:SetRope( rope ) controller:DeleteOnRemove( rope ) end // Remove the existing hydraulic constraint if oldconstraint then controller:DontDeleteOnRemove( oldconstraint ) oldconstraint:DontDeleteOnRemove( controller ) oldconstraint:Remove() end if oldrope then controller:DontDeleteOnRemove( oldrope ) oldrope:DontDeleteOnRemove( controller ) oldrope:Remove() end end if const then undo.Create( "PA_" .. constraint_type ) undo.AddEntity( const ) undo.SetPlayer( ply ) undo.Finish() if Ent1 then Ent1:GetPhysicsObject():EnableMotion( false ) end if Ent2 then Ent2:GetPhysicsObject():EnableMotion( false ) end playsound( ply, true ) end return const end net.Receive( PA_.. "constraint", precision_align_constraint_func ) //********************************************************************************************************************// // Perform last action //********************************************************************************************************************// // Keep a record of each player's last PA action function precision_align_lastaction_func( ply, cmd, args ) if !ply.PA_activeent then return false end local ent = ply.PA_activeent if !IsValid(ent) then return false end if !util.IsValidPhysicsObject(ent, 0) or IsValid(ent:GetParent()) then return false end local lastaction = action_table[ply] if !lastaction then return false end return lastaction.cmd( ply, nil, lastaction ) end concommand.Add( PA_.. "lastaction", precision_align_lastaction_func )// Precision Alignment STool code - By Wenli TOOL.Category = "Constraints" TOOL.Name = "#Tool.precision_align.name" TOOL.Command = nil TOOL.ConfigName = "" local PA = "precision_align" local PA_ = PA .. "_" AddCSLuaFile( PA.."/ui.lua" ) AddCSLuaFile( PA.."/manipulation_panel.lua" ) AddCSLuaFile( PA.."/prop_functions.lua" ) AddCSLuaFile( PA.."/tool_screen.lua" ) include( "weapons/gmod_tool/stools/"..PA.."/tool_screen.lua" ) TOOL.ClientConVar = { // Tool menu options ["edge_snap"] = "1", ["centre_snap"] = "1", ["snap_distance"] = "100", ["display_messages"] = "0", ["display_warnings"] = "1", ["tooltype"] = "1", ["draw_attachments"] = "0", ["default_linelength"] = "200", // Construct draw sizes ["size_point"] = "5", ["size_line_start"] = "4", ["size_line_end"] = "4", ["size_plane"] = "10", ["size_plane_normal"] = "20", // Ent Selection colour ["selectcolour_h"] = "230", ["selectcolour_s"] = "0.6", ["selectcolour_v"] = "1", ["selectcolour_a"] = "255", // Attachment line colour ["attachcolour_h"] = "120", ["attachcolour_s"] = "1", ["attachcolour_v"] = "1", ["attachcolour_a"] = "150", // Constraints ["axis_forcelimit"] = "0", ["axis_torquelimit"] = "0", ["axis_friction"] = "0", ["axis_nocollide"] = "0", ["ballsocket_forcelimit"] = "0", ["ballsocket_torquelimit"] = "0", ["ballsocket_nocollide"] = "0", ["ballsocket_adv_forcelimit"] = "0", ["ballsocket_adv_torquelimit"] = "0", ["ballsocket_adv_xmin"] = "-180", ["ballsocket_adv_ymin"] = "-180", ["ballsocket_adv_zmin"] = "-180", ["ballsocket_adv_xmax"] = "180", ["ballsocket_adv_ymax"] = "180", ["ballsocket_adv_zmax"] = "180", ["ballsocket_adv_xfric"] = "0", ["ballsocket_adv_yfric"] = "0", ["ballsocket_adv_zfric"] = "0", ["ballsocket_adv_onlyrotation"] = "0", ["ballsocket_adv_nocollide"] = "0", ["elastic_constant"] = "100", ["elastic_damping"] = "0", ["elastic_rdamping"] = "0", ["elastic_material"] = "cable/rope", ["elastic_width"] = "1", ["elastic_stretchonly"] = "0", ["rope_forcelimit"] = "0", ["rope_addlength"] = "0", ["rope_width"] = "1", ["rope_material"] = "cable/rope", ["rope_rigid"] = "0", ["rope_setlength"] = "0", ["slider_width"] = "1", ["wire_hydraulic_width"] = "1", ["wire_hydraulic_material"] = "cable/rope", // Stack number ["stack_num"] = "1", ["stack_nocollide"] = "0" } // Client if CLIENT then precision_align_points = {} precision_align_lines = {} precision_align_planes = {} pointcolour = { r = 255, g = 0, b = 0, a = 255 } linecolour = { r = 0, g = 0, b = 255, a = 255 } planecolour = { r = 0, g = 230, b = 0, a = 255 } // Initialize tables, set defaults for i = 1, 9 do precision_align_points[i] = {visible = true} precision_align_lines[i] = {visible = true} precision_align_planes[i] = {visible = true} end language.Add("Tool.precision_align.name", "Precision Alignment") language.Add("Tool.precision_align.desc", "Precision prop alignment tool") language.Add("Tool.precision_align.0", "Primary: Place constructs, Secondary: Select entity, Reload: Open/close manipulation window") language.Add("Tool.precision_align.1", "Click again to place line end point, right click to cancel") language.Add("Undone_precision_align", "Undone Precision Align") end // Tool functions if SERVER then function TOOL:SendClickData( point, normal, ent ) net.Start( PA_ .. "click" ) // Send vectors using floats - was losing precision using just vectors net.WriteFloat( point.x ) net.WriteFloat( point.y ) net.WriteFloat( point.z ) net.WriteFloat( normal.x ) net.WriteFloat( normal.y ) net.WriteFloat( normal.z ) net.WriteEntity( ent ) net.Send( self:GetOwner() ) end function TOOL:SendEntityData( ent ) net.Start( PA_ .. "ent" ) net.WriteEntity( ent ) net.Send( self:GetOwner() ) end function TOOL:SetActive( ent ) local ply = self:GetOwner() local activeent = ply.PA_activeent local function Deselect( ent ) if IsValid( ent ) and ent.PA then local colour = ent.PA.TrueColour if colour then ent:SetColor( colour ) end ent.PA.Ply.PA_activeent = nil ent.PA = nil end end // Deselect last ent if activeent then Deselect( activeent ) end // Select new ent if IsValid( ent ) then // Check for existing player selection if ent.PA then Deselect( ent ) end ply.PA_activeent = ent ent.PA = {} ent.PA.Ply = ply local TrueColour = ent:GetColor() ent.PA.TrueColour = TrueColour local H = ply:GetInfoNum( PA_ .. "selectcolour_h", 230 ) local S = ply:GetInfoNum( PA_ .. "selectcolour_s", 0.6 ) local V = ply:GetInfoNum( PA_ .. "selectcolour_v", 1 ) local A = ply:GetInfoNum( PA_ .. "selectcolour_a", 255 ) local highlight = HSVToColor( H, S, V ) highlight.a = A ent:SetColor(highlight) if A < 255 then ent:SetRenderMode( RENDERMODE_TRANSALPHA ) end duplicator.StoreEntityModifier( ent, "colour", { Color = TrueColour } ) self:SendEntityData( ent ) return true else ply.PA_activeent = nil self:SendEntityData() return false end end end // Build CPanel if CLIENT then function TOOL.BuildCPanel( pnl ) include( "weapons/gmod_tool/stools/"..PA.."/ui.lua" ) end local BuildCPanel = TOOL.BuildCPanel local function reloadui_func() local CPanel = controlpanel.Get( PA ) CPanel:Clear() BuildCPanel( CPanel ) include( "weapons/gmod_tool/stools/"..PA.."/tool_screen.lua" ) MsgAll("Reloading UI\n") end concommand.Add( PA_.."reloadui", reloadui_func ) end // Calculate local position of nearest edge local function Nearest_Edge( HitPosL, BoxMin, BoxMax, BoxCentre, Snap_Dist ) local EdgePosL = Vector( HitPosL.x, HitPosL.y, HitPosL.z ) // This is used to kee local Snapped_Edges = {} local function Find_Edge( k ) if ( HitPosL[k] > BoxCentre[k] and ( BoxMax[k] - Snap_Dist ) <= HitPosL[k] ) then EdgePosL[k] = BoxMax[k] Snapped_Edges[k] = true elseif ( HitPosL[k] < BoxCentre[k] and ( BoxMin[k] + Snap_Dist ) >= HitPosL[k] ) then EdgePosL[k] = BoxMin[k] Snapped_Edges[k] = true end end Find_Edge( "x" ) Find_Edge( "y" ) Find_Edge( "z" ) return EdgePosL, Snapped_Edges end function TOOL:GetClickPosition( trace ) local pos local Ent = trace.Entity local Phys = Ent:GetPhysicsObjectNum( trace.PhysicsBone ) local Edge_Snap = self:GetClientNumber( "edge_snap" ) ~= 0 local Centre_Snap = self:GetClientNumber( "centre_snap" ) ~= 0 local Snap_Dist = math.max(0, self:GetClientNumber( "snap_distance" )) local tooltype = self:GetClientNumber( "tooltype" ) if ( !Phys or !Ent or Ent:IsWorld() ) then Pos = trace.HitPos // Coordinate Centre elseif tooltype == 2 then Pos = Ent:GetPos() // Mass Centre elseif tooltype == 3 then Pos = Ent:LocalToWorld(Phys:GetMassCenter()) // BB Centre elseif tooltype == 4 then Pos = Ent:LocalToWorld(Ent:OBBCenter()) elseif Edge_Snap or Centre_Snap then local HitPosL = Ent:WorldToLocal( trace.HitPos ) local BoxMin, BoxMax = Phys:GetAABB() local BoxCentre = Ent:OBBCenter() local NewPosL = Vector( HitPosL.x, HitPosL.y, HitPosL.z ) local EdgePosL, Edge_Dist local CentrePosL, Centre_Dist // These keep track of whether the point is being snapped to an edge or centre line local Snapped_Edges = {} local Snapped_Centres = {} // Calculate local position of nearest edge if Edge_Snap then EdgePosL, Snapped_Edges = Nearest_Edge( HitPosL, BoxMin, BoxMax, BoxCentre, Snap_Dist ) NewPosL = EdgePosL // We need at least some edge snap if using Centre_Snap, else NewPosL will snap away from the surface being clicked on elseif Centre_Snap then EdgePosL, Snapped_Edges = Nearest_Edge( HitPosL, BoxMin, BoxMax, BoxCentre, 0.1 ) NewPosL = EdgePosL end // Calculate local position of nearest centre line if Centre_Snap then CentrePosL = Vector( HitPosL.x, HitPosL.y, HitPosL.z ) if math.abs( CentrePosL.x - BoxCentre.x ) < Snap_Dist then CentrePosL.x = BoxCentre.x Snapped_Centres.x = true end if math.abs( CentrePosL.y - BoxCentre.y ) < Snap_Dist then CentrePosL.y = BoxCentre.y Snapped_Centres.y = true end if math.abs( CentrePosL.z - BoxCentre.z ) < Snap_Dist then CentrePosL.z = BoxCentre.z Snapped_Centres.z = true end NewPosL = CentrePosL Edge_Dist = EdgePosL - HitPosL Centre_Dist = CentrePosL - HitPosL // NewPosL is already equal to CentrePosL, so only need to set cases where EdgePosL is smaller if ( math.abs( Edge_Dist.x ) < math.abs( Centre_Dist.x ) and Snapped_Edges.x ) or !Snapped_Centres.x then NewPosL.x = EdgePosL.x end if ( math.abs( Edge_Dist.y ) < math.abs( Centre_Dist.y ) and Snapped_Edges.y ) or !Snapped_Centres.y then NewPosL.y = EdgePosL.y end if ( math.abs( Edge_Dist.z ) < math.abs( Centre_Dist.z ) and Snapped_Edges.z ) or !Snapped_Centres.z then NewPosL.z = EdgePosL.z end end Pos = Ent:LocalToWorld(NewPosL) else Pos = trace.HitPos end return Pos end // Place Constructs function TOOL:LeftClick( trace ) if !trace.HitPos then return false end if CLIENT then return true end local point = self:GetClickPosition( trace ) local normal = trace.HitNormal local ent = trace.Entity if !IsValid(ent) then ent = nil end self:SendClickData( point, normal, ent ) return true end // Select Entities function TOOL:RightClick( trace ) if CLIENT then return true end if trace.Entity:IsWorld() then self:SetActive() return true end if !IsValid(trace.Entity) then return false elseif !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) then return false elseif trace.Entity:IsPlayer() then return false end self:SetActive( trace.Entity ) // Repeat last PA move local ply = self:GetOwner() local alt = ply:KeyDown( IN_SPEED ) if alt then precision_align_lastaction_func( ply ) end return true end // Open Manipulation Panel function TOOL:Reload( trace ) if CLIENT then return false end local ply = self:GetOwner() ply:ConCommand( PA_.."open_panel" ) return false end function TOOL:DrawToolScreen( w, h ) PA_DrawToolScreen() end// Derma UI for Precision Alignment stool (client only) - window opens on tool Reload - By Wenli if SERVER then return end local PA_VERSION = 9 // Standard functions local PA = "precision_align" local PA_ = PA .. "_" // Relative to data directory local help_file = "Precision Alignment Help/index.html" // Table cloning local function clone( T ) T2 = {} for k, v in pairs (T) do T2[k] = v end return T2 end local function Message(text) if GetConVarNumber(PA_.."display_messages") == 1 then LocalPlayer():ChatPrint("(PA) " .. text) end end local function Warning( text ) if GetConVarNumber(PA_.."display_warnings") == 1 then LocalPlayer():ChatPrint("(PA) ERROR: " .. text) end end local function NormAng( ang ) local ang_temp = Angle() ang_temp.p = math.NormalizeAngle(ang.p) ang_temp.y = math.NormalizeAngle(ang.y) ang_temp.r = math.NormalizeAngle(ang.r) return ang_temp end local function AddMenuText( text, x, y, parent ) local Text = vgui.Create( "DLabel", parent ) //Text:SetFont("TabLarge") Text:SetFontInternal("Default") Text:SetText( text ) Text:SizeToContents() Text:SetPos( x, y ) return Text end //********************************************************************************************************************// // Manipulation Window //********************************************************************************************************************// local BGColor = Color(50, 50, 50, 50) local BGColor_Background = Color(103, 100, 110, 255) local BGColor_Disabled = Color(160, 160, 160, 255) local BGColor_Display = Color(170, 170, 170, 255) local BGColor_Point = Color(170, 140, 140, 255) local BGColor_Line = Color(140, 140, 170, 255) local BGColor_Plane = Color(140, 170, 140, 255) local BGColor_Rotation = Color(140, 170, 170, 255) local function play_sound_true() LocalPlayer():EmitSound("buttons/button15.wav", 100, 100) end local function play_sound_false() LocalPlayer():EmitSound("buttons/lightswitch2.wav", 100, 100) end local MANIPULATION_FRAME = {} function MANIPULATION_FRAME:Init() self:SetSize(900, 500) self:Center() self:SetTitle( "Precision Alignment Manipulation Panel" ) self:SetVisible( true ) self:SetDraggable( true ) self:SetSizable( false ) self:ShowCloseButton( true ) self:SetDeleteOnClose( false ) self:MakePopup() self:SetKeyBoardInputEnabled( false ) -- self.version_text = AddMenuText( "v1.5", 885, 5, self ) self.version_text = AddMenuText( "(unofficial v" .. PA_VERSION .. ")", 735, 5, self ) self.body = vgui.Create( "PA_Manipulation_Body", self ) self.panel = vgui.Create( "PA_Manipulation_Panel", self.body ) self.displays_tab = vgui.Create( "PA_Displays_Tab", self.body ) self.points_tab = vgui.Create( "PA_Points_Tab", self.body ) self.lines_tab = vgui.Create( "PA_Lines_Tab", self.body ) self.planes_tab = vgui.Create( "PA_Planes_Tab", self.body ) self.move_tab = vgui.Create( "PA_Move_Tab", self.body ) self.functions_tab = vgui.Create( "PA_Functions_Tab", self.body ) self.rotation_tab = vgui.Create( "PA_Rotation_Tab", self.body ) self.rotation_functions_tab = vgui.Create( "PA_Rotation_Functions_Tab", self.body ) self.constraints_tab = vgui.Create( "PA_Constraints_Tab", self.body ) self.panel:AddSheet( "Display Options", self.displays_tab, "icon16/picture_edit.png", false, false ) self.panel:AddSheet( "Points", self.points_tab, "icon16/add.png", false, false ) self.points_tab.tab = self.panel.Items[2].Tab self.panel:AddSheet( "Lines", self.lines_tab, "icon16/tick.png", false, false ) self.lines_tab.tab = self.panel.Items[3].Tab self.panel:AddSheet( "Planes", self.planes_tab, "icon16/world.png", false, false ) self.planes_tab.tab = self.panel.Items[4].Tab self.panel:AddSheet( "Move Constructs", self.move_tab, "icon16/folder_go.png", false, false ) self.panel:AddSheet( "Functions", self.functions_tab, "icon16/plugin.png", false, false ) self.panel:AddSheet( "Rotation", self.rotation_tab, "icon16/arrow_refresh.png", false, false ) self.panel:AddSheet( "Rotation Functions", self.rotation_functions_tab, "icon16/arrow_refresh.png", false, false ) self.panel:AddSheet( "Constraints", self.constraints_tab, "icon16/anchor.png", false, false ) // Help button self.button_help = vgui.Create( "PA_Function_Button", self ) -- self.button_help:SetPos(875, 30) self.button_help:SetPos(830, 30) self.button_help:SetSize(60, 20) self.button_help:SetText( "Help" ) self.button_help:SetToolTip( "Open online help using the Steam in-game browser" ) self.button_help:SetFunction( function() return gui.OpenURL( "https://sourceforge.net/userapps/mediawiki/wenli/index.php?title=Precision_Alignment" ) end ) // Alpha slider --self.slider_alpha = vgui.Create( "DAlphaBar", self ) -- self.slider_alpha:SetPos(910, 55) -- self.slider_alpha:SetSize(25, 435) -- self.slider_alpha.imgBackground:Remove() -- self.slider_alpha.PerformLayout = function() DSlider.PerformLayout( self.slider_alpha ) end -- self.slider_alpha.OnChange = function( panel, alpha ) -- self.body:SetAlpha(alpha) -- panel:SetAlpha(255) -- end -- self.slider_alpha:SetValue(0) // Initialize colour settings local function SetBarColour() local H = GetConVarNumber( PA_.."selectcolour_h" ) local S = GetConVarNumber( PA_.."selectcolour_s" ) local V = GetConVarNumber( PA_.."selectcolour_v" ) -- self.slider_alpha:SetFGColor( HSVToColor( H, S, V ) ) end cvars.AddChangeCallback( PA_.."selectcolour_h", SetBarColour ) cvars.AddChangeCallback( PA_.."selectcolour_s", SetBarColour ) cvars.AddChangeCallback( PA_.."selectcolour_v", SetBarColour ) SetBarColour() end function MANIPULATION_FRAME:Paint() local width, height = self:GetSize() local offset = 60 draw.RoundedBox(6, 0, 0, width, 25, BGColor_Display) draw.RoundedBox(6, 2, 2, width - 4, 21, BGColor_Background) surface.SetDrawColor(0, 0, 0, 255) surface.DrawLine(offset, height - 1, width - offset, height - 1) surface.DrawLine(1, offset, 1, height - offset) surface.DrawLine(width - 1, offset, width - 1, height - offset) end vgui.Register("PA_Manipulation_Frame", MANIPULATION_FRAME, "DFrame") local MANIPULATION_BODY = {} function MANIPULATION_BODY:Init() self:CopyBounds( self:GetParent() ) self:SetTall(475) self:SetPos(0, 25) end function MANIPULATION_BODY:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), Color(0,0,0,255)) draw.RoundedBox(6, 1, 1, self:GetWide() - 2, self:GetTall() - 2, BGColor_Background) end vgui.Register("PA_Manipulation_Body", MANIPULATION_BODY, "DPanel") local MANIPULATION_PANEL = {} function MANIPULATION_PANEL:Init() self:SetPos(0, 5) self:SetSize(900, 470) end function MANIPULATION_PANEL:Paint() end vgui.Register("PA_Manipulation_Panel", MANIPULATION_PANEL, "DPropertySheet") //********************************************************************************************************************// // Displays Tab //********************************************************************************************************************// local DISPLAYS_TAB = {} function DISPLAYS_TAB:Init() self:SetPos(5, 30) self:CopyBounds( self:GetParent() ) local function update_visibility( panel, construct_table ) local selection = panel:GetSelected() local keytable = {} for k, v in pairs( selection ) do keytable[ selection[k]:GetID() ] = true end for I = 1, 9 do if keytable[I] then construct_table[I].visible = true else construct_table[I].visible = false end end end self.panel_multiselect = vgui.Create( "PA_Construct_Multiselect", self ) self.panel_multiselect.button_attach:Remove() self.panel_multiselect.button_delete:Remove() // This flag will make the selection buttons affect visibility self.panel_multiselect.visibility = true self.panel_multiselect:SelectAll( true ) AddMenuText( "Construct Visibility", 10, 5, self.panel_multiselect.colour_panel_1 ) self.panel_multiselect.list_points.OnRowSelected = function( Panel, Line ) update_visibility( Panel, precision_align_points ) end self.panel_multiselect.list_lines.OnRowSelected = function( Panel, Line ) update_visibility( Panel, precision_align_lines ) end self.panel_multiselect.list_planes.OnRowSelected = function( Panel, Line ) update_visibility( Panel, precision_align_planes ) end AddMenuText( "Construct Draw Sizes", 10, self:GetTall()/2 - 10, self ) AddMenuText( "Tool Messages", 290, self:GetTall()/2 - 10, self ) self.checkbox_messages = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_messages:SetPos(300, 252) self.checkbox_messages:SetText( "Display Debug Messages" ) self.checkbox_messages:SizeToContents() self.checkbox_messages:SetToolTip( "Enable detailed tool debug messages" ) self.checkbox_messages:SetConVar( PA_.."display_messages" ) self.checkbox_warnings = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_warnings:SetPos(300, 272) self.checkbox_warnings:SetText( "Display Error Messages" ) self.checkbox_warnings:SizeToContents() // self.checkbox_warnings:SetToolTip( "Enable error messages" ) self.checkbox_warnings:SetConVar( PA_.."display_warnings" ) self.slider_pointsize = vgui.Create( "PA_XYZ_Slider", self ) self.slider_pointsize:SetPos(25, 220) self.slider_pointsize:SetSize(230, 100) self.slider_pointsize:SetText( "Point" ) self.slider_pointsize:SetMinMax( 0, 30 ) self.slider_pointsize:SetDecimals( 1 ) self.slider_pointsize:SetConVar( PA_.."size_point" ) self.slider_line_startsize = vgui.Create( "PA_XYZ_Slider", self ) self.slider_line_startsize:SetPos(25, 285) self.slider_line_startsize:SetSize(230, 100) self.slider_line_startsize:SetText( "Line Start (Cross)" ) self.slider_line_startsize:SetMinMax( 0, 30 ) self.slider_line_startsize:SetDecimals( 1 ) self.slider_line_startsize:SetConVar( PA_.."size_line_start" ) self.slider_line_endsize = vgui.Create( "PA_XYZ_Slider", self ) self.slider_line_endsize:SetPos(300, 285) self.slider_line_endsize:SetSize(230, 100) self.slider_line_endsize:SetText( "Line End (Double Bar)" ) self.slider_line_endsize:SetMinMax( 0, 30 ) self.slider_line_endsize:SetDecimals( 1 ) self.slider_line_endsize:SetConVar( PA_.."size_line_end" ) self.slider_planesize = vgui.Create( "PA_XYZ_Slider", self ) self.slider_planesize:SetPos(25, 350) self.slider_planesize:SetSize(230, 100) self.slider_planesize:SetText( "Plane (Square)" ) self.slider_planesize:SetMinMax( 0, 30 ) self.slider_planesize:SetDecimals( 1 ) self.slider_planesize:SetConVar( PA_.."size_plane" ) self.slider_planesize_normal = vgui.Create( "PA_XYZ_Slider", self ) self.slider_planesize_normal:SetPos(300, 350) self.slider_planesize_normal:SetSize(230, 100) self.slider_planesize_normal:SetText( "Plane Normal (Length)" ) self.slider_planesize_normal:SetMinMax( 0, 30 ) self.slider_planesize_normal:SetDecimals( 1 ) self.slider_planesize_normal:SetConVar( PA_.."size_plane_normal" ) self.button_size_defaults = vgui.Create( "PA_Function_Button", self ) self.button_size_defaults:SetPos(465, 235) self.button_size_defaults:SetSize(90, 25) self.button_size_defaults:SetText( "Reset Defaults" ) self.button_size_defaults:SetFunction( function() RunConsoleCommand( PA_.."display_messages", 0 ) RunConsoleCommand( PA_.."display_warnings", 1 ) self.checkbox_messages:SetValue( false ) self.checkbox_warnings:SetValue( true ) RunConsoleCommand( PA_.."size_point", 5 ) RunConsoleCommand( PA_.."size_line_start", 4 ) RunConsoleCommand( PA_.."size_line_end", 4 ) RunConsoleCommand( PA_.."size_plane", 10 ) RunConsoleCommand( PA_.."size_plane_normal", 20 ) return true end ) AddMenuText( "Entity Selection Colour", 575, 5, self ) self.colourcircle_enthighlight = vgui.Create("PA_ColourControl", self) self.colourcircle_enthighlight:SetPos(590, 15) self.colourcircle_enthighlight:SetConVar( PA_ .. "selectcolour" ) self.colourcircle_enthighlight:SetDefaults( 230, 0.6, 1, 255 ) AddMenuText( "Attachment Line Colour", 575, 225, self ) self.colourcircle_attachment = vgui.Create("PA_ColourControl", self) self.colourcircle_attachment:SetPos(590, 235) self.colourcircle_attachment:SetConVar( PA_ .. "attachcolour" ) self.colourcircle_attachment:SetDefaults( 180, 1, 1, 150 ) // Initialize colour settings self.colourcircle_enthighlight:SetColor( GetConVarNumber(PA_.."selectcolour_h"), GetConVarNumber(PA_.."selectcolour_s"), GetConVarNumber(PA_.."selectcolour_v"), GetConVarNumber(PA_.."selectcolour_a") ) self.colourcircle_attachment:SetColor( GetConVarNumber(PA_.."attachcolour_h"), GetConVarNumber(PA_.."attachcolour_s"), GetConVarNumber(PA_.."attachcolour_v"), GetConVarNumber(PA_.."attachcolour_a") ) end function DISPLAYS_TAB:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Display) draw.RoundedBox(6, 0, 15, 555, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 565, 15, 320, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 0, self:GetTall()/2 + 15, 555, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 565, self:GetTall()/2 + 15, 320, self:GetTall()/2 - 20, BGColor) // Construct Draw Size coloured background draw.RoundedBox(6, 0, 235, 280, 66, BGColor_Point) draw.RoundedBox(6, 0, 301, 555, 67, BGColor_Line) draw.RoundedBox(6, 0, 368, 555, 67, BGColor_Plane) draw.RoundedBox(6, 15, 246, 250, 46, BGColor) draw.RoundedBox(6, 15, 312, 250, 46, BGColor) draw.RoundedBox(6, 15, 379, 250, 46, BGColor) draw.RoundedBox(6, 290, 312, 250, 46, BGColor) draw.RoundedBox(6, 290, 379, 250, 46, BGColor) // Display messages background box draw.RoundedBox(6, 290, 246, 161, 46, BGColor) // Selection colour preview -- local H, S, V, A = self.colourcircle_enthighlight:GetColor() //local colour = HSVToColor( H, S, V -- local colour = Color(H, S, V ,A) //colour.a = A -- surface.SetDrawColor( colour.r, colour.g, colour.b, colour.a ) -- surface.DrawRect( 840, 100, 35, 55 ) // Attachment Line Preview -- H, S, V, A = self.colourcircle_attachment:GetColor() -- colour = HSVToColor( H, S, V ) -- colour.a = A -- surface.SetDrawColor( colour.r, colour.g, colour.b, colour.a ) -- surface.DrawLine( 860, 280, 860, 410 ) end vgui.Register("PA_Displays_Tab", DISPLAYS_TAB, "DPanel") //********************************************************************************************************************// // Points Tab //********************************************************************************************************************// local POINTS_TAB = {} function POINTS_TAB:Init() local ent_relative_1 = false local ent_relative_2 = false self:CopyBounds( self:GetParent() ) AddMenuText( "Point Adjustment", 10, 5, self ) local function update_primary_listview() local selection = self.list_primarypoint:GetSelectedLine() if !selection then return false end local point_temp = PA_funcs.point_global( selection ) local vec if !point_temp then vec = Vector(0,0,0) else vec = point_temp.origin if self.checkbox_relative1:GetChecked() then if IsValid(PA_activeent) then vec = PA_activeent:WorldToLocal(vec) else self.checkbox_relative1:SetValue(false) end end end self.sliders_origin1:SetValues( vec ) return true end self.list_primarypoint = vgui.Create( "PA_Construct_ListView", self ) self.list_primarypoint:Text( "Primary", "Point", self ) self.list_primarypoint:SetToolTip( "Double click to update sliders" ) self.list_primarypoint:SetPos(15, 30) self.list_primarypoint:SetMultiSelect(false) self.list_primarypoint.DoDoubleClick = function( Line, LineID ) update_primary_listview() play_sound_true() end self.checkbox_relative1 = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_relative1:SetPos(140, 30) self.checkbox_relative1:SetText( "Relative to\nEntity" ) self.checkbox_relative1:SetSize(100,30) self.checkbox_relative1.OnChange = function() if !update_primary_listview() and self.checkbox_relative1:GetChecked() then self.checkbox_relative1:SetValue(false) end end self.button_set = vgui.Create( "PA_Function_Button", self ) self.button_set:SetPos(140, 69) self.button_set:SetSize(80, 70) self.button_set:SetText( "Set" ) self.button_set:SetToolTip( "Set the selected point according to these x/y/z values" ) self.button_set:SetFunction( function() local selection = self.list_primarypoint:GetSelectedLine() if selection then local vec = self.sliders_origin1:GetValues() if self.checkbox_relative1:GetChecked() and IsValid(PA_activeent) then vec = PA_activeent:LocalToWorld(vec) end return PA_funcs.set_point( selection, vec ) end Warning("No selection") return false end ) self.button_delete = vgui.Create( "PA_Function_Button", self ) self.button_delete:SetPos(140, 144) self.button_delete:SetSize(80, 25) self.button_delete:SetText( "Delete" ) self.button_delete:SetFunction( function() local selection = self.list_primarypoint:GetSelectedLine() if selection then return PA_funcs.delete_point(selection) end Warning("No selection") return false end ) self.button_deleteall = vgui.Create( "PA_Function_Button", self ) self.button_deleteall:SetPos(140, 174) self.button_deleteall:SetSize(80, 25) self.button_deleteall:SetText( "Delete All" ) self.button_deleteall:SetFunction( function() return PA_funcs.delete_points() end ) self.sliders_origin1 = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_origin1:SetPos(230, 64) self.sliders_origin1:SetSize(230, 150) self.button_zero1 = vgui.Create( "PA_Zero_Button", self ) self.button_zero1:SetPos(413, 30) self.button_zero1:SetSliders( self.sliders_origin1 ) self.button_copy_clipboard1 = vgui.Create( "PA_Copy_Clipboard_Button", self ) self.button_copy_clipboard1:SetPos(313, 30) self.button_copy_clipboard1:SetSliders( self.sliders_origin1 ) local function update_secondary_listview() local selection = self.list_secondarypoint:GetSelectedLine() if !selection then return false end local point_temp = PA_funcs.point_global( selection ) local vec if !point_temp then vec = Vector(0,0,0) else vec = point_temp.origin if self.checkbox_relative2:GetChecked() then if IsValid(PA_activeent) then vec = PA_activeent:WorldToLocal(vec) else self.checkbox_relative2:SetValue(false) end end end self.sliders_origin2:SetValues( vec ) return true end self.list_secondarypoint = vgui.Create( "PA_Construct_ListView", self ) self.list_secondarypoint:Text( "Secondary", "Point", self ) self.list_secondarypoint:SetPos(765, 30) self.list_secondarypoint:SetMultiSelect(false) self.list_secondarypoint.DoDoubleClick = function( Line, LineID ) update_secondary_listview() play_sound_true() end self.checkbox_relative2 = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_relative2:SetPos(463, 30) self.checkbox_relative2:SetSize(100,30) self.checkbox_relative2:SetText( "Relative to\nEntity" ) self.checkbox_relative2.OnChange = function() if !update_secondary_listview() and self.checkbox_relative2:GetChecked() then self.checkbox_relative2:SetValue(false) end end self.sliders_origin2 = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_origin2:SetPos(550, 64) self.sliders_origin2:SetSize(230, 150) self.button_zero2 = vgui.Create( "PA_Zero_Button", self ) self.button_zero2:SetPos(730, 30) self.button_zero2:SetSliders( self.sliders_origin2 ) self.button_copy_clipboard2 = vgui.Create( "PA_Copy_Clipboard_Button", self ) self.button_copy_clipboard2:SetPos(630, 30) self.button_copy_clipboard2:SetSliders( self.sliders_origin2 ) self.button_copy_x = vgui.Create( "PA_Copy_Left_Button", self ) self.button_copy_x:SetPos(460, 70) self.button_copy_x:SetFunction( function() self.sliders_origin1.slider_x:SetValue( self.sliders_origin2.slider_x:GetValue() ) return true end ) self.button_copy_y = vgui.Create( "PA_Copy_Left_Button", self ) self.button_copy_y:SetPos(460, 120) self.button_copy_y:SetFunction( function() self.sliders_origin1.slider_y:SetValue( self.sliders_origin2.slider_y:GetValue() ) return true end ) self.button_copy_z = vgui.Create( "PA_Copy_Left_Button", self ) self.button_copy_z:SetPos(460, 170) self.button_copy_z:SetFunction( function() self.sliders_origin1.slider_z:SetValue( self.sliders_origin2.slider_z:GetValue() ) return true end ) self.button_subtract_x = vgui.Create( "PA_Subtract_Button", self ) self.button_subtract_x:SetPos(486, 70) self.button_subtract_x:SetFunction( function() self.sliders_origin1.slider_x:SetValue( self.sliders_origin1.slider_x:GetValue() - self.sliders_origin2.slider_x:GetValue() ) return true end ) self.button_subtract_y = vgui.Create( "PA_Subtract_Button", self ) self.button_subtract_y:SetPos(486, 120) self.button_subtract_y:SetFunction( function() self.sliders_origin1.slider_y:SetValue( self.sliders_origin1.slider_y:GetValue() - self.sliders_origin2.slider_y:GetValue() ) return true end ) self.button_subtract_z = vgui.Create( "PA_Subtract_Button", self ) self.button_subtract_z:SetPos(486, 170) self.button_subtract_z:SetFunction( function() self.sliders_origin1.slider_z:SetValue( self.sliders_origin1.slider_z:GetValue() - self.sliders_origin2.slider_z:GetValue() ) return true end ) self.button_add_x = vgui.Create( "PA_Add_Button", self ) self.button_add_x:SetPos(512, 70) self.button_add_x:SetFunction( function() self.sliders_origin1.slider_x:SetValue( self.sliders_origin1.slider_x:GetValue() + self.sliders_origin2.slider_x:GetValue() ) return true end ) self.button_add_y = vgui.Create( "PA_Add_Button", self ) self.button_add_y:SetPos(512, 120) self.button_add_y:SetFunction( function() self.sliders_origin1.slider_y:SetValue( self.sliders_origin1.slider_y:GetValue() + self.sliders_origin2.slider_y:GetValue() ) return true end ) self.button_add_z = vgui.Create( "PA_Add_Button", self ) self.button_add_z:SetPos(512, 170) self.button_add_z:SetFunction( function() self.sliders_origin1.slider_z:SetValue( self.sliders_origin1.slider_z:GetValue() + self.sliders_origin2.slider_z:GetValue() ) return true end ) AddMenuText( "Point Functions", 10, self:GetTall()/2 - 10, self ) self.functions_list_functionpoints = vgui.Create( "PA_Construct_ListView", self ) self.functions_list_functionpoints:SetPos(15, 250) self.functions_list_functionpoints:Text( "Function Selection", "Point", self ) self.functions_list_functionpoints.OnRowSelected = function( LineID, Line ) end self.functions_button_negate = vgui.Create( "PA_Function_Button", self ) self.functions_button_negate:SetPos(160, 250) self.functions_button_negate:SetText( "Negate Values" ) self.functions_button_negate:SetToolTip( "Negate values" ) self.functions_button_negate:SetFunction( function() self.sliders_origin1:SetValues(-self.sliders_origin1:GetValues()) return true end ) self.functions_button_copy_origin = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_origin:SetPos(405, 250) self.functions_button_copy_origin:SetText( "Copy Point" ) self.functions_button_copy_origin:SetToolTip( "Copy the selected point's values into the primary sliders" ) self.functions_button_copy_origin:SetFunction( function() local selection = self.functions_list_functionpoints:GetSelected() if #selection == 1 then local pointID = selection[1]:GetID() if PA_funcs.construct_exists( "Point", pointID ) then local point = PA_funcs.point_global(pointID) self.sliders_origin1:SetValues(point.origin) return true end else Warning("Select 1 point") end return false end ) self.functions_button_pointsaverage = vgui.Create( "PA_Function_Button", self ) self.functions_button_pointsaverage:SetPos(650, 250) self.functions_button_pointsaverage:SetText( "Centre of points" ) self.functions_button_pointsaverage:SetToolTip( "Set primary values to the centre (average) of the selected points" ) self.functions_button_pointsaverage:SetFunction( function() local selection_table = {} selection_table = self.functions_list_functionpoints:GetSelected() if #selection_table > 1 then local vec = PA_funcs.point_function_average(selection_table) if vec then self.sliders_origin1:SetValues( vec ) return true end else Warning("Select 2 or more points") end return false end ) self.button_moveentity = vgui.Create( "PA_Move_Button", self ) self.button_moveentity:SetPos(self:GetWide() - 160, self:GetTall() - 90) self.button_moveentity:SetSize(100, 50) self.button_moveentity:SetText( "Move Entity" ) self.button_moveentity:SetToolTip( "Move entity by Primary -> Secondary" ) self.button_moveentity:SetFunction( function() local selected_point1 = self.list_primarypoint:GetSelectedLine() local selected_point2 = self.list_secondarypoint:GetSelectedLine() if !selected_point1 or !selected_point2 then Warning("Select both a primary and secondary point") return false end if selected_point1 == selected_point2 then Warning("Cannot move between the same point!") return false end local point1 = PA_funcs.point_global(selected_point1) local point2 = PA_funcs.point_global(selected_point2) if !point1 or !point2 then Warning("Points not correctly defined") return false end if !PA_funcs.move_entity(point1.origin, point2.origin, PA_activeent) then return false end end ) end function POINTS_TAB:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Point) draw.RoundedBox(6, 5, 15, 440, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 455, 15, 430, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 5, self:GetTall()/2 + 15, self:GetWide() - 10, self:GetTall()/2 - 20, BGColor) end vgui.Register("PA_Points_Tab", POINTS_TAB, "DPanel") //********************************************************************************************************************// // Lines Tab //********************************************************************************************************************// local LINES_TAB = {} function LINES_TAB:Init() self:CopyBounds( self:GetParent() ) AddMenuText( "Line Adjustment", 10, 5, self ) local function update_sliders(startpoint, endpoint, direction, length, angle) self.sliders_startpoint:SetValues(startpoint) self.sliders_endpoint:SetValues(endpoint) self.sliders_direction:SetValues(direction) self.slider_length:SetValue(length) if direction == Vector(0,0,0) then angle = Angle(0,0,0) end if math.abs(angle.p) == 90 then angle.y = 0 end self.slider1_ang_p:SetValue(angle.p) self.slider1_ang_y:SetValue(angle.y) return true end local function update_primary_listview() local selection = self.list_primary:GetSelectedLine() if !selection then return false end local line_temp = PA_funcs.line_global( selection ) local startpoint, endpoint, direction, length, angle if !line_temp then startpoint = Vector(0,0,0) endpoint = Vector(0,0,0) direction = Vector(0,0,0) length = 0 angle = Angle(0,0,0) else startpoint = line_temp.startpoint endpoint = line_temp.endpoint if self.checkbox_relative:GetChecked() then if IsValid(PA_activeent) then startpoint = PA_activeent:WorldToLocal(startpoint) endpoint = PA_activeent:WorldToLocal(endpoint) else self.checkbox_relative:SetValue(false) end end local vec = endpoint - startpoint direction = vec:GetNormal() length = vec:Length() if direction:Length() == 0 then angle = Angle(0,0,0) else angle = NormAng(vec:Angle()) end end return update_sliders(startpoint, endpoint, direction, length, angle) end self.list_primary = vgui.Create( "PA_Construct_ListView", self ) self.list_primary:Text( "Primary", "Line", self ) self.list_primary:SetToolTip( "Double click to update sliders" ) self.list_primary:SetPos(15, 30) self.list_primary:SetMultiSelect(false) self.list_primary.DoDoubleClick = function( Line, LineID ) update_primary_listview() play_sound_true() end self.button_set = vgui.Create( "PA_Function_Button", self ) self.button_set:SetPos(140, 69) self.button_set:SetSize(80, 70) self.button_set:SetText( "Set" ) self.button_set:SetToolTip( "Set the selected line according to these start/end point values" ) self.button_set:SetFunction( function() local selection = self.list_primary:GetSelectedLine() if selection then local startpoint = self.sliders_startpoint:GetValues() local endpoint = self.sliders_endpoint:GetValues() if startpoint == endpoint then Warning("Cannot set line of zero length") return false end if self.checkbox_relative:GetChecked() and IsValid(PA_activeent) then startpoint = PA_activeent:LocalToWorld(startpoint) endpoint = PA_activeent:LocalToWorld(endpoint) end return PA_funcs.set_line( selection, startpoint, endpoint ) end Warning("No selection") return false end ) self.button_delete = vgui.Create( "PA_Function_Button", self ) self.button_delete:SetPos(140, 144) self.button_delete:SetSize(80, 25) self.button_delete:SetText( "Delete" ) self.button_delete:SetFunction( function() local selection = self.list_primary:GetSelectedLine() if selection then return PA_funcs.delete_line(selection) end Warning("No selection") return false end ) self.button_deleteall = vgui.Create( "PA_Function_Button", self ) self.button_deleteall:SetPos(140, 174) self.button_deleteall:SetSize(80, 25) self.button_deleteall:SetText( "Delete All" ) self.button_deleteall:SetFunction( function() return PA_funcs.delete_lines() end ) self.checkbox_relative = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_relative:SetPos(140, 30) self.checkbox_relative:SetText( "Relative to\nEntity" ) self.checkbox_relative:SetSize(100,30) self.checkbox_relative.OnChange = function() if !update_primary_listview() and self.checkbox_relative:GetChecked() then self.checkbox_relative:SetValue(false) end end AddMenuText( "Start Point", 295, 20, self ) local function update_sliders_point() local startpoint, endpoint, direction, length, angle startpoint = self.sliders_startpoint:GetValues() endpoint = self.sliders_endpoint:GetValues() local vec = endpoint - startpoint direction = vec:GetNormal() length = vec:Length() angle = NormAng(vec:Angle()) return update_sliders(startpoint, endpoint, direction, length, angle) end self.sliders_startpoint = vgui.Create( "PA_XYZ_Sliders", self ) -- self.sliders_startpoint:SetPos(200, 44) self.sliders_startpoint:SetPos(255, 44) -- self.sliders_startpoint:SetSize(220, 150) self.sliders_startpoint:SetSize(160, 150) self.button_zero_startpoint = vgui.Create( "PA_Zero_Button", self ) self.button_zero_startpoint:SetPos(380, 15) self.button_zero_startpoint:SetSliders( self.sliders_startpoint ) self.button_update_startpoint = vgui.Create( "PA_Update_Button", self ) self.button_update_startpoint:SetPos(280, 183) self.button_update_startpoint:SetFunction( function() return update_sliders_point() end ) AddMenuText( "End Point", 459, 20, self ) self.sliders_endpoint = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_endpoint:SetPos(415, 44) -- (360, 44) self.sliders_endpoint:SetSize(160, 150) self.button_zero_endpoint = vgui.Create( "PA_Zero_Button", self ) self.button_zero_endpoint:SetPos(540, 15) self.button_zero_endpoint:SetSliders( self.sliders_endpoint ) self.button_update_endpoint = vgui.Create( "PA_Update_Button", self ) self.button_update_endpoint:SetPos(440, 183) self.button_update_endpoint:SetFunction( function() return update_sliders_point() end ) AddMenuText( "Direction", 619, 20, self ) local function update_sliders_direction() local startpoint, endpoint, direction, length, angle startpoint = self.sliders_startpoint:GetValues() direction = self.sliders_direction:GetValues():GetNormal() if direction == Vector(0,0,0) then direction = Vector(1,0,0) end length = self.slider_length:GetValue() endpoint = startpoint + direction * length angle = NormAng(direction:Angle()) return update_sliders(startpoint, endpoint, direction, length, angle) end self.sliders_direction = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_direction:SetPos(575, 44) -- (520, 44) self.sliders_direction:SetSize(160, 150) self.sliders_direction:SetRange(1) self.button_zero_direction = vgui.Create( "PA_Zero_Button", self ) self.button_zero_direction:SetPos(700, 15) self.button_zero_direction:SetSliders( self.sliders_direction ) self.button_update_direction = vgui.Create( "PA_Update_Button", self ) self.button_update_direction:SetPos(600, 183) self.button_update_direction:SetFunction( function() return update_sliders_direction() end ) AddMenuText( "Length / Angle", 764, 20, self ) local function update_sliders_length() local startpoint, endpoint, direction, length, angle startpoint = self.sliders_startpoint:GetValues() angle = Angle(self.slider1_ang_p:GetValue(), self.slider1_ang_y:GetValue(), 0) direction = angle:Forward() length = self.slider_length:GetValue() endpoint = startpoint + direction * length return update_sliders(startpoint, endpoint, direction, length, angle) end self.slider_length = vgui.Create( "PA_XYZ_Slider", self ) self.slider_length:SetPos(735, 49) self.slider_length:SetSize(160, 20) self.slider_length:SetText( "Length" ) self.slider_length.Label:SetWide( 35 ) self.slider_length:SetMinMax( 0.001, 1000 ) self.slider1_ang_p = vgui.Create( "PA_XYZ_Slider", self ) self.slider1_ang_p:SetPos(735, 99) self.slider1_ang_p:SetSize(160, 20) self.slider1_ang_p:SetText( "Pitch" ) self.slider1_ang_p.Label:SetWide( 35 ) self.slider1_ang_p:SetMinMax( -90, 90 ) self.slider1_ang_y = vgui.Create( "PA_XYZ_Slider", self ) self.slider1_ang_y:SetPos(735, 149) self.slider1_ang_y:SetSize(160, 20) self.slider1_ang_y:SetText( "Yaw" ) self.slider1_ang_y.Label:SetWide( 35 ) self.slider1_ang_y:SetMinMax( -180, 180 ) self.button_update_length = vgui.Create( "PA_Update_Button", self ) self.button_update_length:SetPos(760, 183) self.button_update_length:SetFunction( function() return update_sliders_length() end ) AddMenuText( "Line Functions", 10, self:GetTall()/2 - 10, self ) self.functions_list_functionlines = vgui.Create( "PA_Construct_ListView", self ) self.functions_list_functionlines:Text( "Function Selection", "Line" ) self.functions_list_functionlines:SetPos(15, 250) self.functions_list_functionlines.OnRowSelected = function( panel, line ) end self.functions_button_reversedirection = vgui.Create( "PA_Function_Button", self ) self.functions_button_reversedirection:SetPos(160, 250) self.functions_button_reversedirection:SetText( "Reverse Direction" ) self.functions_button_reversedirection:SetToolTip( "Reverse direction values" ) self.functions_button_reversedirection:SetFunction( function() self.sliders_direction:SetValues(Vector(0,0,0) - self.sliders_direction:GetValues()) return update_sliders_direction() end ) self.functions_button_reverseline = vgui.Create( "PA_Function_Button", self ) self.functions_button_reverseline:SetPos(160, 280) self.functions_button_reverseline:SetText( "Switch Start/End" ) self.functions_button_reverseline:SetToolTip( "Switch start point and end point" ) self.functions_button_reverseline:SetFunction( function() local startpoint = self.sliders_startpoint:GetValues() local endpoint = self.sliders_endpoint:GetValues() self.sliders_startpoint:SetValues(endpoint) self.sliders_endpoint:SetValues(startpoint) return update_sliders_point() end ) local function single_line_selection() local selection = self.functions_list_functionlines:GetSelected() if #selection == 1 then local lineID = selection[1]:GetID() if PA_funcs.construct_exists( "Line", lineID ) then local line = PA_funcs.line_global(lineID) return line end end Warning("Select 1 line") return false end self.functions_button_default_length = vgui.Create( "PA_Function_Button", self ) self.functions_button_default_length:SetPos(160, 310) self.functions_button_default_length:SetText( "Set Default Length" ) self.functions_button_default_length:SetToolTip( "Sets the default length to the current length slider value" ) self.functions_button_default_length:SetFunction( function() local length = self.slider_length:GetValue() if !(length > 0) then Warning("Length must be greater than 0") return false end RunConsoleCommand( PA_.."default_linelength", length ) Message("Default line length set to " .. tostring(length) .. " units") return true end ) self.functions_button_copy_start = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_start:SetPos(405, 250) self.functions_button_copy_start:SetText( "Copy Start Point" ) self.functions_button_copy_start:SetToolTip( "Copy start point from the selected line" ) self.functions_button_copy_start:SetFunction( function() local line = single_line_selection() if !line then return false end self.sliders_startpoint:SetValues(line.startpoint) return true end ) self.functions_button_copy_end = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_end:SetPos(405, 280) self.functions_button_copy_end:SetText( "Copy End Point" ) self.functions_button_copy_end:SetToolTip( "Copy end point from the selected line" ) self.functions_button_copy_end:SetFunction( function() local line = single_line_selection() if !line then return false end self.sliders_endpoint:SetValues(line.endpoint) return true end ) self.functions_button_copy_dir = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_dir:SetPos(405, 310) self.functions_button_copy_dir:SetText( "Copy Direction" ) self.functions_button_copy_dir:SetToolTip( "Copy direction from the selected line" ) self.functions_button_copy_dir:SetFunction( function() local line = single_line_selection() if !line then return false end local dir = (line.endpoint - line.startpoint):GetNormal() self.sliders_direction:SetValues(dir) return true end ) self.functions_button_copy_length = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_length:SetPos(405, 340) self.functions_button_copy_length:SetText( "Copy Length" ) self.functions_button_copy_length:SetToolTip( "Copy length from the selected line" ) self.functions_button_copy_length:SetFunction( function() local line = single_line_selection() if !line then return false end local length = (line.endpoint - line.startpoint):Length() self.slider_length:SetValue(length) return true end ) self.functions_button_perpendicular = vgui.Create( "PA_Function_Button", self ) self.functions_button_perpendicular:SetPos(650, 250) self.functions_button_perpendicular:SetText( "Find Perpendicular Direction" ) self.functions_button_perpendicular:SetToolTip( "Set direction perpendicular to two lines" ) self.functions_button_perpendicular:SetFunction( function() local selection = self.functions_list_functionlines:GetSelected() if #selection == 2 then local lineID1, lineID2 = selection[1]:GetID(), selection[2]:GetID() local dir = PA_funcs.line_function_perpendicular( lineID1, lineID2 ) if dir then self.sliders_direction:SetValues(dir) return update_sliders_direction() end else Warning("Select 2 lines") end return false end ) self.functions_button_addlines = vgui.Create( "PA_Function_Button", self ) self.functions_button_addlines:SetPos(650, 280) self.functions_button_addlines:SetText( "Add Line Vectors" ) self.functions_button_addlines:SetToolTip( "Add the selected lines as vectors to the current endpoint" ) self.functions_button_addlines:SetFunction( function() local selection = self.functions_list_functionlines:GetSelected() if !selection then Warning("Select at least 1 line") return false end local lineID, line local totalvec = self.sliders_endpoint:GetValues() for k, v in pairs (selection) do lineID = v:GetID() if PA_funcs.construct_exists( "Line", lineID ) then line = PA_funcs.line_global( lineID ) totalvec = totalvec + line.endpoint - line.startpoint end end self.sliders_endpoint:SetValues( totalvec ) return update_sliders_point() end ) self.button_moveentity = vgui.Create( "PA_Move_Button", self ) self.button_moveentity:SetPos(self:GetWide() - 160, self:GetTall() - 90) self.button_moveentity:SetSize(100, 50) self.button_moveentity:SetText( "Move Entity" ) self.button_moveentity:SetToolTip( "Move entity by line" ) self.button_moveentity:SetFunction( function() local selection = self.list_primary:GetSelectedLine() if !selection then Warning("Select a line") return false end if !PA_funcs.construct_exists( "Line", selection ) then Warning("Line not correctly defined") return false end local line = PA_funcs.line_global(selection) if !PA_funcs.move_entity(line.startpoint, line.endpoint, PA_activeent) then return false end end ) end function LINES_TAB:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Line) draw.RoundedBox(6, 5, 15, 230, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 250, 15, 150, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 410, 15, 150, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 570, 15, 150, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 730, 15, 150, 65, BGColor) draw.RoundedBox(6, 730, 85, 150, 130, BGColor) draw.RoundedBox(6, 5, self:GetTall()/2 + 15, self:GetWide() - 10, self:GetTall()/2 - 20, BGColor) end vgui.Register("PA_Lines_Tab", LINES_TAB, "DPanel") //********************************************************************************************************************// // Planes Tab //********************************************************************************************************************// local PLANES_TAB = {} function PLANES_TAB:Init() self:CopyBounds( self:GetParent() ) AddMenuText( "Plane Adjustment", 10, 5, self ) local function update_sliders(origin, normal, angle) if origin then self.sliders_origin:SetValues(origin) end if normal then self.sliders_normal:SetValues(normal) end if angle then if !normal or normal == Vector(0,0,0) then angle = Angle(0,0,0) end if math.abs(angle.p) > 89.999 then angle.y = 0 end self.slider_ang_p:SetValue(angle.p) self.slider_ang_y:SetValue(angle.y) end return true end local function update_primary_listview() local selection = self.list_primary:GetSelectedLine() if !selection then return false end local plane_temp = PA_funcs.plane_global( selection ) local origin, normal, angle if !plane_temp then origin = Vector(0,0,0) normal = Vector(0,0,0) angle = Angle(0,0,0) else if self.checkbox_relative:GetChecked() then if IsValid(PA_activeent) then plane_temp.origin = PA_activeent:WorldToLocal(plane_temp.origin) plane_temp.normal = ( PA_activeent:WorldToLocal(PA_activeent:GetPos() + plane_temp.normal) ):GetNormal() else self.checkbox_relative:SetValue(false) end end origin = plane_temp.origin normal = plane_temp.normal if normal:Length() == 0 then angle = Angle(0,0,0) else angle = NormAng(normal:Angle()) end end return update_sliders(origin, normal, angle) end self.list_primary = vgui.Create( "PA_Construct_ListView", self ) self.list_primary:Text( "Primary", "Plane", self ) self.list_primary:SetToolTip( "Double click to update sliders" ) self.list_primary:SetPos(15, 30) self.list_primary:SetMultiSelect(false) self.list_primary.DoDoubleClick = function( Line, LineID ) update_primary_listview() play_sound_true() end self.button_set = vgui.Create( "PA_Function_Button", self ) self.button_set:SetPos(140, 69) self.button_set:SetSize(80, 70) self.button_set:SetText( "Set" ) self.button_set:SetToolTip( "Set the selected plane according to these values" ) self.button_set:SetFunction( function() local selection = self.list_primary:GetSelectedLine() if selection then local origin = self.sliders_origin:GetValues() local normal = self.sliders_normal:GetValues() if self.checkbox_relative:GetChecked() and IsValid(PA_activeent) then origin = PA_activeent:LocalToWorld(origin) normal = ( PA_activeent:LocalToWorld(normal) - PA_activeent:GetPos()):GetNormal() end return PA_funcs.set_plane( selection, origin, normal ) end Warning("No selection") return false end ) self.button_delete = vgui.Create( "PA_Function_Button", self ) self.button_delete:SetPos(140, 144) self.button_delete:SetSize(80, 25) self.button_delete:SetText( "Delete" ) self.button_delete:SetFunction( function() local selection = self.list_primary:GetSelectedLine() if selection then return PA_funcs.delete_plane(selection) end Warning("No selection") return false end ) self.button_deleteall = vgui.Create( "PA_Function_Button", self ) self.button_deleteall:SetPos(140, 174) self.button_deleteall:SetSize(80, 25) self.button_deleteall:SetText( "Delete All" ) self.button_deleteall:SetFunction( function() return PA_funcs.delete_planes() end ) self.checkbox_relative = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_relative:SetPos(140, 30) self.checkbox_relative:SetText( "Relative to\nEntity" ) self.checkbox_relative:SetSize(100,30) self.checkbox_relative.OnChange = function() if !update_primary_listview() and self.checkbox_relative:GetChecked() then self.checkbox_relative:SetValue(false) end end AddMenuText( "Origin", 328, 20, self ) self.sliders_origin = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_origin:SetPos(255, 44) self.sliders_origin:SetSize(210, 138) self.button_zero_origin = vgui.Create( "PA_Zero_Button", self ) self.button_zero_origin:SetPos(430, 15) self.button_zero_origin:SetSliders( self.sliders_origin ) AddMenuText( "Normal", 536, 20, self ) self.sliders_normal = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_normal:SetPos(470, 44) self.sliders_normal:SetSize(210, 138) local function update_sliders_normal() local normal = self.sliders_normal:GetValues():GetNormal() local angle = NormAng(normal:Angle()) return update_sliders(origin, normal, angle) end self.button_zero_normal = vgui.Create( "PA_Zero_Button", self ) self.button_zero_normal:SetPos(645, 15) self.button_zero_normal:SetSliders( self.sliders_normal ) self.button_update_normal = vgui.Create( "PA_Update_Button", self ) self.button_update_normal:SetPos(517, 183) self.button_update_normal:SetFunction( function() return update_sliders_normal() end ) AddMenuText( "Angle", 757, 20, self ) self.slider_ang_p = vgui.Create( "PA_XYZ_Slider", self ) self.slider_ang_p:SetPos( 685, 50 ) self.slider_ang_p:SetSize( 210, 40 ) self.slider_ang_p:SetText( "Pitch" ) self.slider_ang_p.Label:SetWide( 25 ) self.slider_ang_p:SetMinMax( -90, 90 ) self.slider_ang_y = vgui.Create( "PA_XYZ_Slider", self ) self.slider_ang_y:SetPos( 685, 120 ) self.slider_ang_y:SetSize( 210, 40 ) self.slider_ang_y:SetText( "Yaw" ) self.slider_ang_y.Label:SetWide( 25 ) self.slider_ang_y:SetMinMax( -180, 180 ) self.button_update_angle = vgui.Create( "PA_Update_Button", self ) self.button_update_angle:SetPos(732, 183) self.button_update_angle:SetFunction( function() local angle = Angle( self.slider_ang_p:GetValue(), self.slider_ang_y:GetValue(), 0 ) local vec = angle:Forward() self.sliders_normal:SetValues(vec) return true end ) local function single_plane_selection() local selection = self.functions_list_functionplanes:GetSelected() if #selection == 1 then local planeID = selection[1]:GetID() if PA_funcs.construct_exists( "Plane", planeID ) then local plane = PA_funcs.plane_global(planeID) return plane end end Warning("Select 1 plane") return false end AddMenuText( "Plane Functions", 10, self:GetTall()/2 - 10, self ) self.functions_list_functionplanes = vgui.Create( "PA_Construct_ListView", self ) self.functions_list_functionplanes:Text( "Function Selection", "Plane" ) self.functions_list_functionplanes:SetPos(15, 250) self.functions_list_functionplanes.OnRowSelected = function( panel, line ) end self.functions_button_reversenormal = vgui.Create( "PA_Function_Button", self ) self.functions_button_reversenormal:SetPos(160, 250) self.functions_button_reversenormal:SetText( "Reverse Normal" ) self.functions_button_reversenormal:SetToolTip( "Reverse normal values" ) self.functions_button_reversenormal:SetFunction( function() self.sliders_normal:SetValues(-self.sliders_normal:GetValues()) return update_sliders_normal() end ) self.functions_button_copy_origin = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_origin:SetPos(405, 250) self.functions_button_copy_origin:SetText( "Copy Origin" ) self.functions_button_copy_origin:SetToolTip( "Copy origin from the selected plane" ) self.functions_button_copy_origin:SetFunction( function() local plane = single_plane_selection() if !plane then return false end self.sliders_origin:SetValues(plane.origin) return true end ) self.functions_button_copy_normal = vgui.Create( "PA_Function_Button", self ) self.functions_button_copy_normal:SetPos(405, 280) self.functions_button_copy_normal:SetText( "Copy Normal" ) self.functions_button_copy_normal:SetToolTip( "Copy normal from the selected plane" ) self.functions_button_copy_normal:SetFunction( function() local plane = single_plane_selection() if !plane then return false end self.sliders_normal:SetValues(plane.normal) return update_sliders_normal() end ) self.functions_button_perpendicular = vgui.Create( "PA_Function_Button", self ) self.functions_button_perpendicular:SetPos(650, 250) self.functions_button_perpendicular:SetText( "Find Perpendicular Normal" ) self.functions_button_perpendicular:SetToolTip( "Set normal perpendicular to two planes" ) self.functions_button_perpendicular:SetFunction( function() local selection = self.functions_list_functionplanes:GetSelected() if #selection == 2 then local planeID1, planeID2 = selection[1]:GetID(), selection[2]:GetID() local normal = PA_funcs.plane_function_perpendicular( planeID1, planeID2 ) if normal then self.sliders_normal:SetValues(normal) return update_sliders_normal() end else Warning("Select 2 planes") end return false end ) end function PLANES_TAB:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Plane) draw.RoundedBox(6, 5, 15, 230, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 245, 15, 205, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 460, 15, 205, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 675, 15, 205, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 5, self:GetTall()/2 + 15, self:GetWide() - 10, self:GetTall()/2 - 20, BGColor) end vgui.Register("PA_Planes_Tab", PLANES_TAB, "DPanel") //********************************************************************************************************************// // Move Constructs Tab //********************************************************************************************************************// local MOVE_TAB = {} function MOVE_TAB:Init() self:CopyBounds( self:GetParent() ) self.panel_multiselect = vgui.Create( "PA_Construct_Multiselect", self ) AddMenuText( "Primary Selection", 10, 5, self.panel_multiselect.colour_panel_1 ) // Rotate Around Axis ************************************************************************************************ AddMenuText( "Rotate Around Axis", 575, 5, self ) self.list_line_axis = vgui.Create( "PA_Construct_ListView", self ) self.list_line_axis:Text( "Axis Selection", "Line", self ) self.list_line_axis:SetPos(585, 30) self.list_line_axis:SetMultiSelect(false) self.slider_rotate_axis = vgui.Create( "PA_XYZ_Slider", self ) self.slider_rotate_axis:SetPos( 700, 55 ) self.slider_rotate_axis:SetSize( 200, 50 ) self.slider_rotate_axis:SetText( "Angle" ) self.slider_rotate_axis.Label:SetWide( 30 ) self.slider_rotate_axis:SetMinMax( -180, 180 ) // If no pivot, then the constructs won't move; only their directions will change local function rotate_constructs( selection_constructs, pivot, axis, degrees ) if pivot then for k, v in pairs ( selection_constructs.points ) do local point = PA_funcs.point_global(v) local vec = point.origin - pivot local ang = vec:Angle() ang:RotateAroundAxis( axis, degrees ) vec = ang:Forward() * vec:Length() PA_funcs.set_point( v, pivot + vec ) end end for k, v in pairs ( selection_constructs.lines ) do local line = PA_funcs.line_global(v) local vec1 = Vector(0,0,0) local pivot_temp = pivot if pivot_temp then vec1 = line.startpoint - pivot_temp local ang1 = vec1:Angle() ang1:RotateAroundAxis( axis, degrees ) vec1 = ang1:Forward() * vec1:Length() else pivot_temp = line.startpoint end local vec2 = line.endpoint - pivot_temp local ang2 = vec2:Angle() ang2:RotateAroundAxis( axis, degrees ) vec2 = ang2:Forward() * vec2:Length() PA_funcs.set_line( v, pivot_temp + vec1, pivot_temp + vec2 ) end for k, v in pairs ( selection_constructs.planes ) do local plane = PA_funcs.plane_global(v) local vec1 = Vector(0,0,0) local pivot_temp = pivot if pivot_temp then vec1 = plane.origin - pivot_temp local ang1 = vec1:Angle() ang1:RotateAroundAxis( axis, degrees ) vec1 = ang1:Forward() * vec1:Length() else pivot_temp = plane.origin end local vec2 = plane.normal local ang2 = vec2:Angle() ang2:RotateAroundAxis( axis, degrees ) vec2 = ang2:Forward() PA_funcs.set_plane( v, pivot_temp + vec1, vec2 ) end return true end self.button_axisrotate_angle = vgui.Create( "PA_Function_Button", self ) self.button_axisrotate_angle:SetPos(750, 150) self.button_axisrotate_angle:SetSize(80, 25) self.button_axisrotate_angle:SetText( "Rotate" ) self.button_axisrotate_angle:SetToolTip( "Rotate constructs around the selected axis" ) self.button_axisrotate_angle:SetFunction( function() local selected_line = self.list_line_axis:GetSelectedLine() if !selected_line then Warning("Select an axis line") return false end if !PA_funcs.construct_exists( "Line", selected_line ) then Warning("Line not correctly defined") return false end // Check construct selections local selection_constructs = self.panel_multiselect:GetSelection() if !selection_constructs then Warning( "No valid constructs selected for move" ) return false end local degrees = self.slider_rotate_axis:GetValue() if degrees == 0 then return false end local line = PA_funcs.line_global(selected_line) local axis = ( line.endpoint - line.startpoint ):GetNormal() // Check pivot selection local pivot_selection = self.list_pivotpoint:GetSelectedLine() local pivot = line.startpoint if pivot_selection then if PA_funcs.construct_exists( "Point", pivot_selection ) then pivot = PA_funcs.point_global( pivot_selection ).origin end end return rotate_constructs( selection_constructs, pivot, axis, degrees ) end ) self.button_negate_axisangle = vgui.Create( "PA_Negate_Button", self ) self.button_negate_axisangle:SetPos(779, 50) self.button_negate_axisangle:SetFunction( function() self.slider_rotate_axis:SetValue( -self.slider_rotate_axis:GetValue() ) return true end ) // Move Constructs ************************************************************************************************ self.colour_panel_1 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_1:SetPos(0, 220) AddMenuText( "Move Constructs", 10, 5, self.colour_panel_1 ) self.list_point_1 = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_point_1:Text( "Point 1 Selection", "Point", self.colour_panel_1 ) self.list_point_1:SetPos(20, 30) self.list_point_1:SetMultiSelect(false) self.list_point_1:SetVisible(false) self.colour_panel_2 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_2:SetPos(150, 220) self.list_point_2 = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_point_2:Text( "Point 2 Selection", "Point", self.colour_panel_2 ) self.list_point_2:SetPos(20, 30) self.list_point_2:SetMultiSelect(false) self.list_point_2:SetVisible(false) self.list_line_1 = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_line_1:Text( "Line Selection", "Line", self.colour_panel_2 ) self.list_line_1:SetPos(20, 30) self.list_line_1:SetMultiSelect(false) self.list_line_1:SetVisible(false) self.list_plane_1 = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_plane_1:Text( "Plane Selection", "Plane", self.colour_panel_2 ) self.list_plane_1:SetPos(20, 30) self.list_plane_1:SetMultiSelect(false) self.list_plane_1:SetVisible(false) self.button_move_constructs = vgui.Create( "PA_Function_Button", self ) self.button_move_constructs:SetPos(320, 360) self.button_move_constructs:SetSize(90, 60) self.button_move_constructs:SetText( "Set" ) self.button_move_constructs:SetToolTip( "Move the selected constructs" ) self.button_move_constructs:SetFunction( function() if !self.activebutton then Warning("No function selected") return false end local selection_type = self.activebutton.selections local selection_primary, selection_1, selection_2, selection_3 // Check primary selection if selection_type == "Point" then local point1, point2 = self.list_point_1:GetSelectedLine(), self.list_point_2:GetSelectedLine() if !point1 then Warning( "Select Point 1" ) return false elseif !point2 then Warning( "Select Point 2" ) return false elseif !PA_funcs.construct_exists( "Point", point1 ) then Warning( "Point " .. tostring(point1) .. " not defined" ) return false elseif !PA_funcs.construct_exists( "Point", point2 ) then Warning( "Point " .. tostring(point2) .. " not defined" ) return false end selection_primary = { point1, point2 } elseif selection_type == "Line" then local line = self.list_line_1:GetSelectedLine() if !line then Warning( "Select Line 1" ) return false elseif !PA_funcs.construct_exists( "Line", line ) then Warning( "Line " .. tostring(line) .. " not defined" ) return false end selection_primary = { line } elseif selection_type == "Plane" then local plane = self.list_plane_1:GetSelectedLine() if !plane then Warning( "Select Plane 1" ) return false elseif !PA_funcs.construct_exists( "Plane", plane ) then Warning( "Plane " .. tostring(plane) .. " not defined" ) return false end selection_primary = { plane } else Warning( "Function selection type not valid" ) return false end // Check construct selections local selection_constructs = self.panel_multiselect:GetSelection() if !selection_constructs then Warning( "No valid constructs selected for move" ) return false end return self.activebutton.func( selection_primary, selection_constructs ) end ) local function move_constructs( selection_constructs, vec ) for k, v in pairs ( selection_constructs.points ) do local point = PA_funcs.point_global(v) PA_funcs.set_point( v, point.origin + vec ) end for k, v in pairs ( selection_constructs.lines ) do local line = PA_funcs.line_global(v) PA_funcs.set_line( v, line.startpoint + vec, line.endpoint + vec ) end for k, v in pairs ( selection_constructs.planes ) do local plane = PA_funcs.plane_global(v) PA_funcs.set_plane( v, plane.origin + vec ) end return true end self.button_move_points = vgui.Create( "PA_Function_Button_3", self ) self.button_move_points:SetPos(310, 250) self.button_move_points:SetText( "Move by 2 Points" ) self.button_move_points.selections = "Point" self.button_move_points.func = function( selection_primary, selection_constructs ) local point1 = PA_funcs.point_global( selection_primary[1] ) local point2 = PA_funcs.point_global( selection_primary[2] ) local vec = point2.origin - point1.origin return move_constructs( selection_constructs, vec ) end self.button_move_line = vgui.Create( "PA_Function_Button_3", self ) self.button_move_line:SetPos(310, 280) self.button_move_line:SetText( "Move by Line" ) self.button_move_line.selections = "Line" self.button_move_line.func = function( selection_primary, selection_constructs ) local line = PA_funcs.line_global( selection_primary[1] ) local vec = line.endpoint - line.startpoint return move_constructs( selection_constructs, vec ) end self.button_move_mirror = vgui.Create( "PA_Function_Button_3", self ) self.button_move_mirror:SetPos(310, 310) self.button_move_mirror:SetText( "Mirror Across Plane" ) self.button_move_mirror.selections = "Plane" self.button_move_mirror.func = function( selection_primary, selection_constructs ) local plane = PA_funcs.plane_global( selection_primary[1] ) local origin, normal = plane.origin, plane.normal for k, v in pairs ( selection_constructs.points ) do local point = PA_funcs.point_global(v) PA_funcs.set_point( v, PA_funcs.point_mirror( point.origin, origin, normal ) ) end for k, v in pairs ( selection_constructs.lines ) do local line = PA_funcs.line_global(v) PA_funcs.set_line( v, PA_funcs.point_mirror( line.startpoint, origin, normal ), PA_funcs.point_mirror( line.endpoint, origin, normal ) ) end for k, v in pairs ( selection_constructs.planes ) do local plane = PA_funcs.plane_global(v) PA_funcs.set_plane( v, PA_funcs.point_mirror( plane.origin, origin, normal ), PA_funcs.direction_mirror( plane.normal, normal ) ) end return true end // Rotate Around World Axes ************************************************************************************************ AddMenuText( "Rotate Around World Axes", 453, self:GetTall()/2 - 10, self ) self.list_pivotpoint = vgui.Create( "PA_Construct_ListView", self ) self.list_pivotpoint:Text( "Pivot Point", "Point", self ) self.list_pivotpoint:SetPos(463, 250) self.list_pivotpoint:SetToolTip( "Double click to deselect" ) self.list_pivotpoint:SetMultiSelect(false) self.list_pivotpoint.DoDoubleClick = function( Line, LineID ) self.list_pivotpoint:ClearSelection() end self.sliders_angle_world = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_angle_world:SetSize(280, 150) self.sliders_angle_world:SetPos(605, 284) self.sliders_angle_world:SetRange( 180 ) self.sliders_angle_world.slider_x:SetText("Pitch") self.sliders_angle_world.slider_y:SetText("Yaw") self.sliders_angle_world.slider_z:SetText("Roll") self.sliders_angle_world.slider_x.Label:SetWide( 35 ) self.sliders_angle_world.slider_y.Label:SetWide( 35 ) self.sliders_angle_world.slider_z.Label:SetWide( 35 ) self.button_zero_world = vgui.Create( "PA_Zero_Button", self ) self.button_zero_world:SetPos(845, 250) self.button_zero_world:SetSliders( self.sliders_angle_world ) self.button_negate_world = vgui.Create( "PA_Negate_Button", self ) self.button_negate_world:SetPos(724, 250) self.button_negate_world:SetSliders( self.sliders_angle_world ) self.button_worldrotate = vgui.Create( "PA_Function_Button", self ) self.button_worldrotate:SetPos(605, 250) self.button_worldrotate:SetSize(80, 25) self.button_worldrotate:SetText( "Rotate" ) self.button_worldrotate:SetToolTip( "Rotate constructs by these values" ) self.button_worldrotate:SetFunction( function() local rotang = self.sliders_angle_world:GetValues() if rotang == Vector(0,0,0) then return false end // Check pivot selection local pivot_selection = self.list_pivotpoint:GetSelectedLine() local pivot if pivot_selection then if PA_funcs.construct_exists( "Point", pivot_selection ) then pivot = PA_funcs.point_global( pivot_selection ).origin end end // Check construct selections local selection_constructs = self.panel_multiselect:GetSelection() if !selection_constructs then Warning( "No valid constructs selected for move" ) return false end // Pitch, Yaw, Roll rotate_constructs( selection_constructs, pivot, Vector(0,1,0), rotang.x ) rotate_constructs( selection_constructs, pivot, Vector(0,0,1), rotang.y ) rotate_constructs( selection_constructs, pivot, Vector(1,0,0), rotang.z ) return true end ) end function MOVE_TAB:Paint() draw.RoundedBox(6, 0, 15, 555, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 563, 0, 327, 235, BGColor_Rotation) draw.RoundedBox(6, 570, 15, 315, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 0, self:GetTall()/2 + 15, 433, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 441, 220, 449, 220, BGColor_Rotation) draw.RoundedBox(6, 448, self:GetTall()/2 + 15, 437, self:GetTall()/2 - 20, BGColor) end vgui.Register("PA_Move_Tab", MOVE_TAB, "DPanel") //********************************************************************************************************************// // Combined Functions Tab //********************************************************************************************************************// local FUNCTIONS_TAB = {} function FUNCTIONS_TAB:Init() self:CopyBounds( self:GetParent() ) local width = 148.3 local string_table = {"Point", "Line", "Plane"} local selection_lists = {} self.colour_panel_1 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_1:SetPos(0, 0) AddMenuText( "Selection", 10, 5, self.colour_panel_1 ) self.list_point_primary = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_point_primary:Text( "Primary", "Point", self.colour_panel_1 ) self.list_point_primary:SetPos(20, 30) self.list_point_primary:SetMultiSelect(false) self.list_point_primary:SetVisible(false) self.list_line_primary = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_line_primary:Text( "Primary", "Line", self.colour_panel_1 ) self.list_line_primary:SetPos(20, 30) self.list_line_primary:SetMultiSelect(false) self.list_line_primary:SetVisible(false) self.list_plane_primary = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_plane_primary:Text( "Primary", "Plane", self.colour_panel_1 ) self.list_plane_primary:SetPos(20, 30) self.list_plane_primary:SetMultiSelect(false) self.list_plane_primary:SetVisible(false) self.colour_panel_2 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_2:SetPos(width, 0) self.point_text = AddMenuText( "", 0, 10, self.colour_panel_2 ) self.point_text:SetSize(width, 20) self.point_text:SetContentAlignment(2) self.list_point_secondary = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_point_secondary:Text( "Point Selection", "Point", self.colour_panel_2 ) self.list_point_secondary:SetPos(20, 30) self.list_point_secondary:SetVisible(false) table.insert(selection_lists, self.list_point_secondary) self.colour_panel_3 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_3:SetPos(width * 2, 0) self.line_text = AddMenuText( "", 0, 10, self.colour_panel_3 ) self.line_text:SetSize(width, 20) self.line_text:SetContentAlignment(2) self.list_line_secondary = vgui.Create( "PA_Construct_ListView", self.colour_panel_3 ) self.list_line_secondary:Text( "Line Selection", "Line", self.colour_panel_3 ) self.list_line_secondary:SetPos(20, 30) self.list_line_secondary:SetVisible(false) table.insert(selection_lists, self.list_line_secondary) self.colour_panel_4 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_4:SetPos(width * 3, 0) self.plane_text = AddMenuText( "", 0, 10, self.colour_panel_4 ) self.plane_text:SetSize(width, 20) self.plane_text:SetContentAlignment(2) self.list_plane_secondary = vgui.Create( "PA_Construct_ListView", self.colour_panel_4 ) self.list_plane_secondary:Text( "Plane Selection", "Plane", self.colour_panel_4 ) self.list_plane_secondary:SetPos(20, 30) self.list_plane_secondary:SetVisible(false) table.insert(selection_lists, self.list_plane_secondary) AddMenuText( "Function description", width * 4 + 10, 5, self ) self.text_background = vgui.Create( "DPanel", self ) self.text_background:SetPos( width * 4 + 10, 20 ) self.text_background:SetSize( width * 2 - 19, self:GetTall() / 2 - 45 ) self.text_title = vgui.Create( "DLabel", self ) self.text_title:SetPos( width * 4 + 15, 25 ) self.text_title:SetWide( width * 2 - 29 ) self.text_title:SetContentAlignment(7) self.text_title:SetTextColor( Color(20,20,20,255) ) self.text_title:SetText("- Select a function -") self.text_description = vgui.Create( "DLabel", self ) self.text_description:SetPos( width * 4 + 15, 50 ) self.text_description:SetSize( width * 2 - 29, self:GetTall() / 2 - 78 ) self.text_description:SetWrap( true ) self.text_description:SetContentAlignment(7) self.text_description:SetTextColor( Color(20,20,20,255) ) self.text_description:SetText("") self.button_set = vgui.Create( "PA_Function_Button", self ) self.button_set:SetPos(775, 165) self.button_set:SetSize(100, 40) self.button_set:SetText( "Set" ) self.button_set:SetToolTip( "Execute the selected function" ) self.button_set:SetFunction( function() if !self.activebutton then Warning("No function selected") return false end local selection_type = self.activebutton.selections local selection_primary, selection_1, selection_2, selection_3 local selection_secondary = {} // Check primary selection if selection_type[1] == "Point" then selection_primary = self.list_point_primary:GetSelectedLine() elseif selection_type[1] == "Line" then selection_primary = self.list_line_primary:GetSelectedLine() else selection_primary = self.list_plane_primary:GetSelectedLine() end if !selection_primary then Warning( "Select a primary " .. string.lower(selection_type[1]) ) return false end // Check secondary selections for k, v in pairs (selection_lists) do if v:IsVisible() then local selection = v:GetSelected() local numselections = selection_type[k + 1] // Check the right number of selections have been made if #selection ~= numselections then local str = "Selections not correct - " .. string_table[k] .. " selection requires " .. tostring(selection_type[k + 1]) .. " item" if selection_type[k + 1] > 1 then str = str .. "s" end Warning( str ) return false end // Check the selections point to valid constructs for l, w in pairs (selection) do local ID = w:GetID() if !PA_funcs.construct_exists( string_table[k], ID ) then Warning( string_table[k] .. " " .. tostring(ID) .. " has not been defined" ) return false end table.insert( selection_secondary, ID ) -- the way this is done, it will return points, then lines, then planes, in the required amounts end end end return self.activebutton.func( selection_primary, selection_secondary ) end ) AddMenuText( "Functions", 10, self:GetTall()/2 - 10, self ) // POINTS ************************************************************************************************ self.button_point_linestart = vgui.Create( "PA_Function_Button_2", self ) self.button_point_linestart:SetPos(15, 250) self.button_point_linestart:SetText( "Line Start Point" ) self.button_point_linestart.description = "Sets a point at the start of a line" self.button_point_linestart.selections = { "Point", 0, 1, 0 } self.button_point_linestart.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.line_global( selection_secondary[1] ).startpoint return PA_funcs.set_point( selection_primary, origin ) end self.button_point_lineend = vgui.Create( "PA_Function_Button_2", self ) self.button_point_lineend:SetPos(152, 250) self.button_point_lineend:SetText( "Line End Point" ) self.button_point_lineend.description = "Sets a point at the end of a line" self.button_point_lineend.selections = { "Point", 0, 1, 0 } self.button_point_lineend.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.line_global( selection_secondary[1] ).endpoint return PA_funcs.set_point( selection_primary, origin ) end self.button_point_planeorigin = vgui.Create( "PA_Function_Button_2", self ) self.button_point_planeorigin:SetPos(15, 280) self.button_point_planeorigin:SetText( "Plane Origin" ) self.button_point_planeorigin.description = "Sets a point at the origin of a plane" self.button_point_planeorigin.selections = { "Point", 0, 0, 1 } self.button_point_planeorigin.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.plane_global( selection_secondary[1] ).origin return PA_funcs.set_point( selection_primary, origin ) end self.button_point_threeplanes = vgui.Create( "PA_Function_Button_2", self ) self.button_point_threeplanes:SetPos(152, 280) self.button_point_threeplanes:SetText( "Intersection of 3 Planes" ) self.button_point_threeplanes.description = "Finds the point where three planes intersect" self.button_point_threeplanes.selections = { "Point", 0, 0, 3 } self.button_point_threeplanes.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_3plane_intersection( selection_secondary[1], selection_secondary[2], selection_secondary[3] ) if origin then return PA_funcs.set_point( selection_primary, origin ) end return false end self.button_point_twolines = vgui.Create( "PA_Function_Button_2", self ) self.button_point_twolines:SetPos(15, 310) self.button_point_twolines:SetText( "Line/Line Intersection" ) self.button_point_twolines.description = "Sets a point where two lines intersect\n\nIf the two lines do not meet, the point will be set halfway between where the two lines are closest" self.button_point_twolines.selections = { "Point", 0, 2, 0 } self.button_point_twolines.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_2line_intersection( selection_secondary[1], selection_secondary[2] ) if origin then return PA_funcs.set_point( selection_primary, origin ) end return false end self.button_point_lineplane = vgui.Create( "PA_Function_Button_2", self ) self.button_point_lineplane:SetPos(152, 310) self.button_point_lineplane:SetText( "Line/Plane Intersection" ) self.button_point_lineplane.description = "Sets a point where a line intersects a plane" self.button_point_lineplane.selections = { "Point", 0, 1, 1 } self.button_point_lineplane.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_lineplane_intersection( selection_secondary[1], selection_secondary[2] ) if origin then return PA_funcs.set_point( selection_primary, origin ) end return false end self.button_point_lineprojection = vgui.Create( "PA_Function_Button_2", self ) self.button_point_lineprojection:SetPos(15, 340) self.button_point_lineprojection:SetText( "Point - Line Projection" ) self.button_point_lineprojection.description = "Finds the point on a line closest to the selected point" self.button_point_lineprojection.selections = { "Point", 1, 1, 0 } self.button_point_lineprojection.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_line_projection( selection_secondary[1], selection_secondary[2] ) if origin then return PA_funcs.set_point( selection_primary, origin ) end return false end self.button_point_planeprojection = vgui.Create( "PA_Function_Button_2", self ) self.button_point_planeprojection:SetPos(152, 340) self.button_point_planeprojection:SetText( "Point - Plane Projection" ) self.button_point_planeprojection.description = "Finds the point on a plane closest to the selected point" self.button_point_planeprojection.selections = { "Point", 1, 0, 1 } self.button_point_planeprojection.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_plane_projection( selection_secondary[1], selection_secondary[2] ) if origin then return PA_funcs.set_point( selection_primary, origin ) end return false end // LINES ************************************************************************************************ self.button_line_point_startpoint = vgui.Create( "PA_Function_Button_2", self ) self.button_line_point_startpoint:SetPos(width * 2 + 15, 250) self.button_line_point_startpoint:SetText( "Startpoint from Point" ) self.button_line_point_startpoint.description = "Sets the startpoint of a line at selected point" self.button_line_point_startpoint.selections = { "Line", 1, 0, 0 } self.button_line_point_startpoint.func = function( selection_primary, selection_secondary ) local startpoint = PA_funcs.point_global( selection_secondary[1] ).origin return PA_funcs.set_line( selection_primary, startpoint ) end self.button_line_point_endpoint = vgui.Create( "PA_Function_Button_2", self ) self.button_line_point_endpoint:SetPos(width * 2 + 152, 250) self.button_line_point_endpoint:SetText( "Endpoint from Point" ) self.button_line_point_endpoint.description = "Sets the endpoint of a line at the selected point" self.button_line_point_endpoint.selections = { "Line", 1, 0, 0 } self.button_line_point_endpoint.func = function( selection_primary, selection_secondary ) local endpoint = PA_funcs.point_global( selection_secondary[1] ).origin return PA_funcs.set_line( selection_primary, nil, endpoint ) end self.button_line_move_startpoint = vgui.Create( "PA_Function_Button_2", self ) self.button_line_move_startpoint:SetPos(width * 2 + 15, 280) self.button_line_move_startpoint:SetText( "Move Line to Point" ) self.button_line_move_startpoint.description = "Position an existing line so that its start point moved to the selected point. This will maintain the direction and length of the line" self.button_line_move_startpoint.selections = { "Line", 1, 0, 0 } self.button_line_move_startpoint.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_global( selection_secondary[1] ).origin if PA_funcs.construct_exists( "Line", selection_primary ) then local startpoint = PA_funcs.line_global( selection_primary ).startpoint local endpoint = PA_funcs.line_global( selection_primary ).endpoint return PA_funcs.set_line( selection_primary, origin, origin + endpoint - startpoint ) else Warning( "Line not defined" ) end return false end self.button_line_2points = vgui.Create( "PA_Function_Button_2", self ) self.button_line_2points:SetPos(width * 2 + 152, 280) self.button_line_2points:SetText( "Line from 2 Points" ) self.button_line_2points.description = "Sets a line with start/end determined by the two selected points" self.button_line_2points.selections = { "Line", 2, 0, 0 } self.button_line_2points.func = function( selection_primary, selection_secondary ) local startpoint = PA_funcs.point_global( selection_secondary[1] ).origin local endpoint = PA_funcs.point_global( selection_secondary[2] ).origin return PA_funcs.set_line( selection_primary, startpoint, endpoint ) end self.button_line_2planes = vgui.Create( "PA_Function_Button_2", self ) self.button_line_2planes:SetPos(width * 2 + 15, 310) self.button_line_2planes:SetText( "Intersection of 2 Planes" ) self.button_line_2planes.description = "Sets a line at the intersection of two planes\n\nThe start point will be set halfway between the plane origins" self.button_line_2planes.selections = { "Line", 0, 0, 2 } self.button_line_2planes.func = function( selection_primary, selection_secondary ) local line = PA_funcs.line_2plane_intersection( selection_secondary[1], selection_secondary[2] ) if line then return PA_funcs.set_line( selection_primary, line.startpoint, nil, line.direction ) end return false end self.button_line_plane = vgui.Create( "PA_Function_Button_2", self ) self.button_line_plane:SetPos(width * 2 + 152, 310) self.button_line_plane:SetText( "Line from Plane" ) self.button_line_plane.description = "Sets a line with startpoint/direction determined by the selected plane" self.button_line_plane.selections = { "Line", 0, 0, 1 } self.button_line_plane.func = function( selection_primary, selection_secondary ) local plane = PA_funcs.plane_global( selection_secondary[1] ) return PA_funcs.set_line( selection_primary, plane.origin, nil, plane.normal ) end self.button_line_plane_direction = vgui.Create( "PA_Function_Button_2", self ) self.button_line_plane_direction:SetPos(width * 2 + 15, 340) self.button_line_plane_direction:SetText( "Direction from Plane" ) self.button_line_plane_direction.description = "Sets a line's direction determined by the direction of the selected plane" self.button_line_plane_direction.selections = { "Line", 0, 0, 1 } self.button_line_plane_direction.func = function( selection_primary, selection_secondary ) local plane = PA_funcs.plane_global( selection_secondary[1] ) return PA_funcs.set_line( selection_primary, nil, nil, plane.normal ) end self.button_line_plane_projection = vgui.Create( "PA_Function_Button_2", self ) self.button_line_plane_projection:SetPos(width * 2 + 152, 340) self.button_line_plane_projection:SetText( "Line - Plane Projection" ) self.button_line_plane_projection.description = "Finds the projection of a line on a plane" self.button_line_plane_projection.selections = { "Line", 0, 1, 1 } self.button_line_plane_projection.func = function( selection_primary, selection_secondary ) local line = PA_funcs.line_plane_projection( selection_secondary[1], selection_secondary[2] ) if line then return PA_funcs.set_line( selection_primary, line.startpoint, line.endpoint ) end return false end // PLANES ************************************************************************************************ self.button_plane_point_origin = vgui.Create( "PA_Function_Button_2", self ) self.button_plane_point_origin:SetPos(width * 4 + 15, 250) self.button_plane_point_origin:SetText( "Origin from Point" ) self.button_plane_point_origin.description = "Sets a plane's origin at the selected point" self.button_plane_point_origin.selections = { "Plane", 1, 0, 0 } self.button_plane_point_origin.func = function( selection_primary, selection_secondary ) local origin = PA_funcs.point_global( selection_secondary[1] ).origin return PA_funcs.set_plane( selection_primary, origin ) end self.button_plane_3points = vgui.Create( "PA_Function_Button_2", self ) self.button_plane_3points:SetPos(width * 4 + 152, 250) self.button_plane_3points:SetText( "Plane from 3 Points" ) self.button_plane_3points.description = "Sets a plane determined by the 3 selected points\n\nThe plane's origin will be set at the centre of the 3 points" self.button_plane_3points.selections = { "Plane", 3, 0, 0 } self.button_plane_3points.func = function( selection_primary, selection_secondary ) local plane_temp = PA_funcs.plane_3points( selection_secondary[1], selection_secondary[2], selection_secondary[3] ) if plane_temp then return PA_funcs.set_plane( selection_primary, plane_temp.origin, plane_temp.direction ) end return false end self.button_plane_line = vgui.Create( "PA_Function_Button_2", self ) self.button_plane_line:SetPos(width * 4 + 15, 280) self.button_plane_line:SetText( "Plane from Line" ) self.button_plane_line.description = "Sets a plane with origin/direction determined by the selected line" self.button_plane_line.selections = { "Plane", 0, 1, 0 } self.button_plane_line.func = function( selection_primary, selection_secondary ) local line = PA_funcs.line_global( selection_secondary[1] ) local direction = (line.endpoint - line.startpoint):GetNormal() return PA_funcs.set_plane( selection_primary, line.startpoint, direction ) end self.button_plane_line_direction = vgui.Create( "PA_Function_Button_2", self ) self.button_plane_line_direction:SetPos(width * 4 + 152, 280) self.button_plane_line_direction:SetText( "Direction from Line" ) self.button_plane_line_direction.description = "Sets a plane's direction determined by the direction of the selected line" self.button_plane_line_direction.selections = { "Plane", 0, 1, 0 } self.button_plane_line_direction.func = function( selection_primary, selection_secondary ) local line = PA_funcs.line_global( selection_secondary[1] ) local direction = (line.endpoint - line.startpoint):GetNormal() return PA_funcs.set_plane( selection_primary, nil, direction ) end self.button_plane_2lines = vgui.Create( "PA_Function_Button_2", self ) self.button_plane_2lines:SetPos(width * 4 + 15, 310) self.button_plane_2lines:SetText( "Plane from 2 Lines" ) self.button_plane_2lines.description = "Sets a plane determined by the 2 selected lines\n\nThe plane's origin will be set halfway between the line startpoints" self.button_plane_2lines.selections = { "Plane", 0, 2, 0 } self.button_plane_2lines.func = function( selection_primary, selection_secondary ) local direction = PA_funcs.line_function_perpendicular( selection_secondary[1], selection_secondary[2] ) if !direction then return false end local origin = ( PA_funcs.line_global(selection_secondary[1]).startpoint + PA_funcs.line_global(selection_secondary[2]).startpoint ) * 0.5 return PA_funcs.set_plane( selection_primary, origin, direction ) end end function FUNCTIONS_TAB:Paint() local width = self:GetWide()/3 local height = self:GetTall()/2 draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Display) draw.RoundedBox(6, 0, height, width, height, BGColor_Point) draw.RoundedBox(6, 5, height + 15, width - 11, height - 20, BGColor) draw.RoundedBox(6, width, height, width, height, BGColor_Line) draw.RoundedBox(6, width + 5, height + 15, width - 12, height - 20, BGColor) draw.RoundedBox(6, width * 2, height, width, height, BGColor_Plane) draw.RoundedBox(6, width * 2 + 5, height + 15, width - 10, height - 20, BGColor) draw.RoundedBox(6, width * 2 + 5, 15, width - 10, height - 20, BGColor_Disabled) end function FUNCTIONS_TAB:UpdateDescription() if self.activebutton then self.text_title:SetText( "- " .. self.activebutton:GetValue() .. " -") self.text_description:SetText(self.activebutton.description) end end vgui.Register("PA_Functions_Tab", FUNCTIONS_TAB, "DPanel") //********************************************************************************************************************// // Rotation Tab //********************************************************************************************************************// local ROTATION_TAB = {} function ROTATION_TAB:Init() local function ToVector( ang ) return Vector( ang.p, ang.y, ang.r ) end local function ToAngle( vec ) return Angle( vec.x, vec.y, vec.z ) end self:CopyBounds( self:GetParent() ) AddMenuText( "Entity Angles", 10, 5, self ) self.list_pivotpoint = vgui.Create( "PA_Construct_ListView", self ) self.list_pivotpoint:Text( "Pivot Point", "Point", self ) self.list_pivotpoint:SetPos(15, 30) self.list_pivotpoint:SetToolTip( "Double click to deselect" ) self.list_pivotpoint:SetMultiSelect(false) self.list_pivotpoint.DoDoubleClick = function( Line, LineID ) self.list_pivotpoint:ClearSelection() end self.checkbox_relative1 = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_relative1:SetPos(140, 30) self.checkbox_relative1:SetText( "Relative to\nEntity" ) self.checkbox_relative1:SetSize(100,30) self.checkbox_relative1.OnChange = function() local ent, ang if !PA_activeent and self.checkbox_relative1:GetChecked() then self.checkbox_relative1:SetValue(false) elseif self.checkbox_relative1:GetChecked() then self.checkbox_relative1.lastentity = PA_activeent ang = self.sliders_angle1:GetValues() ang = PA_activeent:WorldToLocalAngles( ToAngle(ang) ) self.sliders_angle1:SetValues( ToVector(ang) ) else if !PA_activeent then if self.checkbox_relative1.lastentity then ent = self.checkbox_relative1.lastentity self.checkbox_relative1.lastentity = nil else return false end else ent = PA_activeent end ang = self.sliders_angle1:GetValues() ang = ent:LocalToWorldAngles( ToAngle(ang) ) self.sliders_angle1:SetValues( ToVector(ang) ) end end self.button_rotate = vgui.Create( "PA_Move_Button", self ) self.button_rotate:SetPos(140, 95) self.button_rotate:SetSize(80, 70) self.button_rotate:SetText( "Rotate Entity" ) self.button_rotate:SetToolTip( "Set the entity's angles to the current values" ) self.button_rotate:SetFunction( function() local ang = ToAngle( self.sliders_angle1:GetValues() ) local pivot = self.list_pivotpoint:GetSelectedLine() local vec if pivot then if PA_funcs.construct_exists( "Point", pivot ) then vec = PA_funcs.point_global( pivot ).origin end end local relative = 0 if self.checkbox_relative1:GetChecked() then relative = 1 //ang = PA_activeent:LocalToWorldAngles( ang ) end if !PA_funcs.rotate_entity(ang, vec, relative, PA_activeent) then return false end end ) self.button_get1 = vgui.Create( "PA_Move_Button", self ) self.button_get1:SetPos(232, 30) self.button_get1:SetSize(80, 25) self.button_get1:SetText( "Get Angles" ) self.button_get1:SetToolTip( "Get angle values from the selected entity" ) self.button_get1:SetFunction( function() local ang if self.checkbox_relative1:GetChecked() and PA_activeent then ang = Angle(0,0,0) else ang = PA_activeent:GetAngles() end self.sliders_angle1:SetValues( ToVector(ang) ) return true end ) self.sliders_angle1 = vgui.Create( "PA_XYZ_Sliders", self ) --self.sliders_angle1:SetWide(240) self.sliders_angle1:SetSize(240, 150) self.sliders_angle1:SetPos(233, 64) self.sliders_angle1:SetRange( 180 ) self.sliders_angle1.slider_x.Label:SetWide( 35 ) self.sliders_angle1.slider_y.Label:SetWide( 35 ) self.sliders_angle1.slider_z.Label:SetWide( 35 ) self.sliders_angle1.slider_x:SetText("Pitch") self.sliders_angle1.slider_y:SetText("Yaw") self.sliders_angle1.slider_z:SetText("Roll") self.button_zero_angle1 = vgui.Create( "PA_Zero_Button", self ) self.button_zero_angle1:SetPos(453, 30) self.button_zero_angle1:SetSliders( self.sliders_angle1 ) self.button_negate_angle1 = vgui.Create( "PA_Negate_Button", self ) self.button_negate_angle1:SetPos(342, 30) self.button_negate_angle1:SetSliders( self.sliders_angle1 ) // Arithmetic buttons self.button_copy_right_p = vgui.Create( "PA_Copy_Right_Button", self ) self.button_copy_right_p:SetPos(487, 70) self.button_copy_right_p:SetFunction( function() self.sliders_angle2.slider_x:SetValue( self.sliders_angle1.slider_x:GetValue() ) return true end ) self.button_copy_right_y = vgui.Create( "PA_Copy_Right_Button", self ) self.button_copy_right_y:SetPos(487, 120) self.button_copy_right_y:SetFunction( function() self.sliders_angle2.slider_y:SetValue( self.sliders_angle1.slider_y:GetValue() ) return true end ) self.button_copy_right_r = vgui.Create( "PA_Copy_Right_Button", self ) self.button_copy_right_r:SetPos(487, 170) self.button_copy_right_r:SetFunction( function() self.sliders_angle2.slider_z:SetValue( self.sliders_angle1.slider_z:GetValue() ) return true end ) self.button_copy_left_p = vgui.Create( "PA_Copy_Left_Button", self ) self.button_copy_left_p:SetPos(537, 70) self.button_copy_left_p:SetFunction( function() self.sliders_angle1.slider_x:SetValue( self.sliders_angle2.slider_x:GetValue() ) return true end ) self.button_copy_left_y = vgui.Create( "PA_Copy_Left_Button", self ) self.button_copy_left_y:SetPos(537, 120) self.button_copy_left_y:SetFunction( function() self.sliders_angle1.slider_y:SetValue( self.sliders_angle2.slider_y:GetValue() ) return true end ) self.button_copy_left_r = vgui.Create( "PA_Copy_Left_Button", self ) self.button_copy_left_r:SetPos(537, 170) self.button_copy_left_r:SetFunction( function() self.sliders_angle1.slider_z:SetValue( self.sliders_angle2.slider_z:GetValue() ) return true end ) self.button_subtract_p = vgui.Create( "PA_Subtract_Button", self ) self.button_subtract_p:SetPos(563, 70) self.button_subtract_p:SetFunction( function() self.sliders_angle1.slider_x:SetValue( self.sliders_angle1.slider_x:GetValue() - self.sliders_angle2.slider_x:GetValue() ) return true end ) self.button_subtract_y = vgui.Create( "PA_Subtract_Button", self ) self.button_subtract_y:SetPos(563, 120) self.button_subtract_y:SetFunction( function() self.sliders_angle1.slider_y:SetValue( self.sliders_angle1.slider_y:GetValue() - self.sliders_angle2.slider_y:GetValue() ) return true end ) self.button_subtract_r = vgui.Create( "PA_Subtract_Button", self ) self.button_subtract_r:SetPos(563, 170) self.button_subtract_r:SetFunction( function() self.sliders_angle1.slider_z:SetValue( self.sliders_angle1.slider_z:GetValue() - self.sliders_angle2.slider_z:GetValue() ) return true end ) self.button_add_p = vgui.Create( "PA_Add_Button", self ) self.button_add_p:SetPos(589, 70) self.button_add_p:SetFunction( function() self.sliders_angle1.slider_x:SetValue( self.sliders_angle1.slider_x:GetValue() + self.sliders_angle2.slider_x:GetValue() ) return true end ) self.button_add_y = vgui.Create( "PA_Add_Button", self ) self.button_add_y:SetPos(589, 120) self.button_add_y:SetFunction( function() self.sliders_angle1.slider_y:SetValue( self.sliders_angle1.slider_y:GetValue() + self.sliders_angle2.slider_y:GetValue() ) return true end ) self.button_add_r = vgui.Create( "PA_Add_Button", self ) self.button_add_r:SetPos(589, 170) self.button_add_r:SetFunction( function() self.sliders_angle1.slider_z:SetValue( self.sliders_angle1.slider_z:GetValue() + self.sliders_angle2.slider_z:GetValue() ) return true end ) self.checkbox_relative2 = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_relative2:SetPos(540, 30) self.checkbox_relative2:SetText( "Relative to\nEntity" ) self.checkbox_relative2:SetSize(100,30) self.checkbox_relative2.OnChange = function() local ent, ang if !PA_activeent and self.checkbox_relative2:GetChecked() then self.checkbox_relative2:SetValue(false) elseif self.checkbox_relative2:GetChecked() then self.checkbox_relative2.lastentity = PA_activeent ang = self.sliders_angle2:GetValues() ang = PA_activeent:WorldToLocalAngles( ToAngle(ang) ) self.sliders_angle2:SetValues( ToVector(ang) ) else if !PA_activeent then if self.checkbox_relative2.lastentity then ent = self.checkbox_relative2.lastentity self.checkbox_relative2.lastentity = nil else return false end else ent = PA_activeent end ang = self.sliders_angle2:GetValues() ang = ent:LocalToWorldAngles( ToAngle(ang) ) self.sliders_angle2:SetValues( ToVector(ang) ) end end self.sliders_angle2 = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_angle2:SetSize(240, 150) self.sliders_angle2:SetPos(632, 64) self.sliders_angle2:SetRange( 180 ) self.sliders_angle2.slider_x.Label:SetWide( 35 ) self.sliders_angle2.slider_y.Label:SetWide( 35 ) self.sliders_angle2.slider_z.Label:SetWide( 35 ) self.sliders_angle2.slider_x:SetText("Pitch") self.sliders_angle2.slider_y:SetText("Yaw") self.sliders_angle2.slider_z:SetText("Roll") self.button_zero_angle2 = vgui.Create( "PA_Zero_Button", self ) self.button_zero_angle2:SetPos(852, 30) self.button_zero_angle2:SetSliders( self.sliders_angle2 ) self.button_negate_angle2 = vgui.Create( "PA_Negate_Button", self ) self.button_negate_angle2:SetPos(741, 30) self.button_negate_angle2:SetSliders( self.sliders_angle2 ) self.button_get2 = vgui.Create( "PA_Move_Button", self ) self.button_get2:SetPos(631, 30) self.button_get2:SetSize(80, 25) self.button_get2:SetText( "Get Angles" ) self.button_get2:SetToolTip( "Get angle values from the selected entity" ) self.button_get2:SetFunction( function() local ang if self.checkbox_relative2:GetChecked() and PA_activeent then ang = Angle(0,0,0) else ang = PA_activeent:GetAngles() end self.sliders_angle2:SetValues( ToVector(ang) ) return true end ) AddMenuText( "Rotate Around Axis", 10, self:GetTall()/2 - 10, self ) self.list_line_axis = vgui.Create( "PA_Construct_ListView", self ) self.list_line_axis:Text( "Axis", "Line", self ) self.list_line_axis:SetPos(15, 250) self.list_line_axis:SetMultiSelect(false) self.slider_axisangle = vgui.Create( "PA_XYZ_Slider", self ) self.slider_axisangle:SetPos(140, 280) self.slider_axisangle:SetWide(335) self.slider_axisangle.Label:SetWide( 35 ) self.slider_axisangle:SetText( "Angle" ) self.slider_axisangle:SetMinMax( -180, 180 ) self.button_negate_axisangle = vgui.Create( "PA_Negate_Button", self ) self.button_negate_axisangle:SetPos(296, 250) self.button_negate_axisangle:SetFunction( function() self.slider_axisangle:SetValue( -self.slider_axisangle:GetValue() ) return true end ) self.button_axisrotate_angle = vgui.Create( "PA_Function_Button", self ) self.button_axisrotate_angle:SetPos(205, 350) self.button_axisrotate_angle:SetSize(80, 40) self.button_axisrotate_angle:SetText( "Rotate Angle" ) self.button_axisrotate_angle:SetToolTip( "Rotate the primary slider values around the selected axis" ) self.button_axisrotate_angle:SetFunction( function() local selected_line = self.list_line_axis:GetSelectedLine() if !selected_line then Warning("Select an axis line") return false end if !PA_funcs.construct_exists( "Line", selected_line ) then Warning("Line not correctly defined") return false end local line = PA_funcs.line_global(selected_line) local degrees = self.slider_axisangle:GetValue() if degrees == 0 then return false end local direction = ( line.endpoint - line.startpoint ):GetNormal() local ang = ToAngle( self.sliders_angle1:GetValues() ) ang:RotateAroundAxis( direction, degrees ) self.sliders_angle1:SetValues( ToVector(ang) ) return true end ) self.button_axisrotate_entity = vgui.Create( "PA_Move_Button", self ) self.button_axisrotate_entity:SetPos(325, 350) self.button_axisrotate_entity:SetSize(80, 70) self.button_axisrotate_entity:SetText( "Rotate Entity" ) self.button_axisrotate_entity:SetToolTip( "Rotate the active entity around the selected axis, pivot is line startpoint" ) self.button_axisrotate_entity:SetFunction( function() local selected_line = self.list_line_axis:GetSelectedLine() if !selected_line then Warning("Select a line") return false end local line = PA_funcs.line_global(selected_line) if !line then Warning("Line not correctly defined") return false end local degrees = self.slider_axisangle:GetValue() if degrees == 0 then return false end local direction = (line.endpoint - line.startpoint):GetNormal() local ang = ToAngle( direction * degrees ) -- local ang = PA_activeent:GetAngles() -- ang:RotateAroundAxis( direction, degrees ) local pivot = self.list_pivotpoint:GetSelectedLine() local vec = line.startpoint if pivot then if PA_funcs.construct_exists( "Point", pivot ) then vec = PA_funcs.point_global( pivot ).origin end end local relative = 3 if !PA_funcs.rotate_entity(ang, vec, relative, PA_activeent) then return false end end ) AddMenuText( "Rotate Around World Axes", 505, self:GetTall()/2 - 10, self ) self.sliders_angle_world = vgui.Create( "PA_XYZ_Sliders", self ) self.sliders_angle_world:SetSize(240, 150) self.sliders_angle_world:SetPos(632, 284) self.sliders_angle_world:SetRange( 180 ) -- self.sliders_angle_world.slider_tx:SetText("Pitch") -- self.sliders_angle_world.slider_ty:SetText("Yaw") -- self.sliders_angle_world.slider_tz:SetText("Roll") self.button_zero_world = vgui.Create( "PA_Zero_Button", self ) self.button_zero_world:SetPos(852, 250) self.button_zero_world:SetSliders( self.sliders_angle_world ) self.button_negate_world = vgui.Create( "PA_Negate_Button", self ) self.button_negate_world:SetPos(741, 250) self.button_negate_world:SetSliders( self.sliders_angle_world ) self.button_worldrotate = vgui.Create( "PA_Function_Button", self ) self.button_worldrotate:SetPos(525, 300) self.button_worldrotate:SetSize(80, 40) self.button_worldrotate:SetText( "Rotate Angle" ) self.button_worldrotate:SetToolTip( "Rotate the primary slider values by these values" ) self.button_worldrotate:SetFunction( function() local rotang = self.sliders_angle_world:GetValues() if rotang == Vector(0,0,0) then return false end local ang = ToAngle( self.sliders_angle1:GetValues() ) ang = PA_funcs.rotate_world( ang, Angle(rotang.x, rotang.y, rotang.z) ) self.sliders_angle1:SetValues( ToVector(ang) ) return true end ) self.button_worldrotate_entity = vgui.Create( "PA_Move_Button", self ) self.button_worldrotate_entity:SetPos(525, 350) self.button_worldrotate_entity:SetSize(80, 70) self.button_worldrotate_entity:SetText( "Rotate Entity" ) self.button_worldrotate_entity:SetToolTip( "Rotate the active entity relative to the world axes" ) self.button_worldrotate_entity:SetFunction( function() local ang = ToAngle( self.sliders_angle_world:GetValues() ) if ang == Angle(0,0,0) then return false end local pivot = self.list_pivotpoint:GetSelectedLine() local vec if pivot then if PA_funcs.construct_exists( "Point", pivot ) then vec = PA_funcs.point_global( pivot ).origin end end local relative = 2 if !PA_funcs.rotate_entity(ang, vec, relative, PA_activeent) then return false end end ) end function ROTATION_TAB:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Rotation) draw.RoundedBox(6, 5, 15, 515, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 530, 15, 355, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 5, self:GetTall()/2 + 15, 485, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 500, self:GetTall()/2 + 15, 385, self:GetTall()/2 - 20, BGColor) end vgui.Register("PA_Rotation_Tab", ROTATION_TAB, "DPanel") //********************************************************************************************************************// // Rotation Functions Tab //********************************************************************************************************************// local ROTATION_FUNCTIONS_TAB = {} function ROTATION_FUNCTIONS_TAB:Init() self:CopyBounds( self:GetParent() ) local width = 148.3 local string_table = {"Line 1", "Line 2", "Plane 1", "Plane 2"} local string_table2 = {"Line", "Line", "Plane", "Plane"} local selection_lists = {} AddMenuText( "Selection", 10, 5, self ) self.colour_panel_1 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_1:SetPos(0, 0) self.list_line_1 = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_line_1:Text( "Line 1 Selection", "Line", self.colour_panel_1 ) self.list_line_1:SetPos(20, 30) self.list_line_1:SetMultiSelect(false) self.list_line_1:SetVisible(false) table.insert(selection_lists, self.list_line_1) self.colour_panel_2 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_2:SetPos(width, 0) self.list_line_2 = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_line_2:Text( "Line 2 Selection", "Line", self.colour_panel_2 ) self.list_line_2:SetPos(20, 30) self.list_line_2:SetMultiSelect(false) self.list_line_2:SetVisible(false) table.insert(selection_lists, self.list_line_2) self.colour_panel_3 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_3:SetPos(width * 2, 0) self.list_plane_1 = vgui.Create( "PA_Construct_ListView", self.colour_panel_3 ) self.list_plane_1:Text( "Plane 1 Selection", "Plane", self.colour_panel_3 ) self.list_plane_1:SetPos(20, 30) self.list_plane_1:SetMultiSelect(false) self.list_plane_1:SetVisible(false) table.insert(selection_lists, self.list_plane_1) self.colour_panel_4 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_4:SetPos(width * 3, 0) self.list_plane_2 = vgui.Create( "PA_Construct_ListView", self.colour_panel_4 ) self.list_plane_2:Text( "Plane 2 Selection", "Plane", self.colour_panel_4 ) self.list_plane_2:SetPos(20, 30) self.list_plane_2:SetMultiSelect(false) self.list_plane_2:SetVisible(false) table.insert(selection_lists, self.list_plane_2) AddMenuText( "Function description", width * 4 + 10, 5, self ) self.text_background = vgui.Create( "DPanel", self ) self.text_background:SetPos( width * 4 + 10, 20 ) self.text_background:SetSize( width * 2 - 19, self:GetTall() / 2 - 45 ) self.text_title = vgui.Create( "DLabel", self ) self.text_title:SetPos( width * 4 + 15, 25 ) self.text_title:SetWide( width * 2 - 29 ) self.text_title:SetContentAlignment(7) self.text_title:SetTextColor( Color(20,20,20,255) ) self.text_title:SetText("- Select a function -") self.text_description = vgui.Create( "DLabel", self ) self.text_description:SetPos( width * 4 + 15, 50 ) self.text_description:SetSize( width * 2 - 29, self:GetTall() / 2 - 78 ) self.text_description:SetWrap( true ) self.text_description:SetContentAlignment(7) self.text_description:SetTextColor( Color(20,20,20,255) ) self.text_description:SetText("") self.button_set = vgui.Create( "PA_Move_Button", self ) self.button_set:SetPos(775, 165) self.button_set:SetSize(100, 40) self.button_set:SetText( "Rotate Entity" ) self.button_set:SetToolTip( "Execute the selected function" ) self.button_set:SetFunction( function() if !self.activebutton then Warning("No function selected") return false end local selection_type = self.activebutton.selections local selection_pivot, selection_axis local selections = {} // Check selections for k, v in pairs (selection_lists) do if v:IsVisible() then local selection = v:GetSelectedLine() if !selection then local str = "Selections not correct - " .. string_table[k] .. " must be selected" Warning( str ) return false end if !PA_funcs.construct_exists( string_table2[k], selection ) then Warning( string_table2[k] .. " " .. tostring(selection) .. " has not been defined" ) return false end table.insert( selections, selection ) -- the way this is done, it will return points, then lines, then planes, in the required amounts end end // Filter out non-existant pivot / axis selections before passing to functions local pivot = self.list_pivotpoint:GetSelectedLine() if pivot then if !PA_funcs.construct_exists( "Point", pivot ) then pivot = nil else pivot = PA_funcs.point_global( pivot ).origin end end local axis = self.list_line_axis:GetSelectedLine() if axis then if !PA_funcs.construct_exists( "Line", axis ) then axis = nil else axis = PA_funcs.line_global( axis ) end end if !self.activebutton.func( pivot, axis, selections ) then return false end end ) // OPTIONAL SELECTIONS ************************************************************************************************ AddMenuText( "Optional selections", 10, self:GetTall()/2 - 10, self ) self.list_pivotpoint = vgui.Create( "PA_Construct_ListView", self ) self.list_pivotpoint:Text( "Pivot Point", "Point", self ) self.list_pivotpoint:SetPos(20, 250) self.list_pivotpoint:SetToolTip( "Double click to deselect" ) self.list_pivotpoint:SetMultiSelect(false) self.list_pivotpoint.DoDoubleClick = function( Line, LineID ) self.list_pivotpoint:ClearSelection() end self.list_line_axis = vgui.Create( "PA_Construct_ListView", self ) self.list_line_axis:Text( "Axis", "Line", self ) self.list_line_axis:SetPos(width + 20, 250) self.list_line_axis:SetToolTip( "Double click to deselect" ) self.list_line_axis:SetMultiSelect(false) self.list_line_axis.DoDoubleClick = function( Line, LineID ) self.list_line_axis:ClearSelection() end // FUNCTION BUTTONS ************************************************************************************************ AddMenuText( "Functions", width * 2 + 10, self:GetTall()/2 - 10, self ) self.button_angle_2lines = vgui.Create( "PA_Function_Button_Rotation", self ) self.button_angle_2lines:SetPos(323, 250) self.button_angle_2lines:SetText( "Angle Between 2 Lines" ) self.button_angle_2lines.description = "Rotates the selected entity according to the angle between two lines\n\n" .. "This is the angle line 1 must be rotated by until it lies in the same direction as line 2\n\n" .. "If no pivot point is selected, by default this will pivot about the start point of line 1" self.button_angle_2lines.selections = { 1, 1, 0, 0 } self.button_angle_2lines.options = { 1, 0 } self.button_angle_2lines.func = function( pivot, axis, selections ) return PA_funcs.rotate_2lines_parallel( pivot, selections[1], selections[2], PA_activeent ) end self.button_angle_2planes = vgui.Create( "PA_Function_Button_Rotation", self ) self.button_angle_2planes:SetPos(323, 280) self.button_angle_2planes:SetText( "Angle Between 2 Plane Normals" ) self.button_angle_2planes.description = "Rotates the selected entity according to the angle between two plane normals\n\n" .. "This is the angle plane 1 must be rotated by until it lies in the same direction as plane 2\n\n" .. "If no pivot point is selected, by default this will pivot about the origin of plane 1" self.button_angle_2planes.selections = { 0, 0, 1, 1 } self.button_angle_2planes.options = { 1, 0 } self.button_angle_2planes.func = function( pivot, axis, selections ) return PA_funcs.rotate_2planes_parallel( pivot, selections[1], selections[2], PA_activeent ) end self.button_lineplane_normal = vgui.Create( "PA_Function_Button_Rotation", self ) self.button_lineplane_normal:SetPos(323, 310) self.button_lineplane_normal:SetText( "Angle from Line to Plane Normal" ) self.button_lineplane_normal.description = "Rotates the selected entity according to the angle between a line and a plane normal\n\n" .. "This is the angle line 1 must be rotated by until it lies in the same direction as the plane normal" self.button_lineplane_normal.selections = { 1, 0, 1, 0 } self.button_lineplane_normal.options = { 1, 0 } self.button_lineplane_normal.func = function( pivot, axis, selections ) return PA_funcs.rotate_lines_planenormal_parallel( pivot, selections[1], selections[2], PA_activeent ) end self.button_lineplane = vgui.Create( "PA_Function_Button_Rotation", self ) self.button_lineplane:SetPos(509, 250) self.button_lineplane:SetText( "Angle from Line to Plane" ) self.button_lineplane.description = "Rotates the selected entity according to the angle between a line and a plane\n\n" .. "This is the angle line 1 must be rotated by until it lies parallel to the plane" self.button_lineplane.selections = { 1, 0, 1, 0 } self.button_lineplane.options = { 1, 1 } self.button_lineplane.func = function( pivot, axis, selections ) return PA_funcs.rotate_line_plane_parallel( pivot, axis, selections[1], selections[2], PA_activeent ) end -- self.button_2line_intersect = vgui.Create( "PA_Function_Button_Rotation", self ) -- self.button_2line_intersect:SetPos(509, 280) -- self.button_2line_intersect:SetText( "Line/Line Intersection" ) -- self.button_2line_intersect.description = "The rotation angle will be determined by how much line 1 must be rotated until it intersects line 2\n\nBy default, the entity's coordinate centre will be used as the pivot point, and the direction of line 2 will be used as the axis of rotation" -- self.button_2line_intersect.selections = { 1, 1, 0, 0 } -- self.button_2line_intersect.options = { 1, 1 } -- self.button_2line_intersect.func = function( pivot, axis, selections ) -- end -- self.button_2line_intersect:SetDisabled(true) self.button_align_2lines = vgui.Create( "PA_Function_Button_Rotation", self ) self.button_align_2lines:SetPos(695, 250) self.button_align_2lines:SetText( "Align 2 Lines" ) self.button_align_2lines.description = "Moves and rotates the selected entity according to the difference in direction and position of the two selected lines, so that line 1 will end up parallel to line 2\n\n" .. "This is identical to the 'Angle Between 2 Lines' rotation function, except that it will also move the entity along the vector from line 1's start point to line 2's start point." self.button_align_2lines.selections = { 1, 1, 0, 0 } self.button_align_2lines.options = { 0, 0 } self.button_align_2lines.func = function( pivot, axis, selections ) if PA_funcs.rotate_2lines_parallel( pivot, selections[1], selections[2], PA_activeent ) then local vec1 = PA_funcs.line_global( selections[1] ).startpoint local vec2 = PA_funcs.line_global( selections[2] ).startpoint if !vec1 or !vec2 then return false end return PA_funcs.move_entity( vec1, vec2, PA_activeent ) end return false end self.button_mirror = vgui.Create( "PA_Function_Button_Rotation", self ) self.button_mirror:SetPos(695, 280) self.button_mirror:SetText( "Mirror Across Plane" ) self.button_mirror.description = "This will move and rotate the selected entity so that it will be mirrored about the selected plane, rotating about the prop's centre of mass\n\n" .. "Note that some props have different planes of symmetry - this can usually be remedied by rotating the prop in some direction by 90 or 180 degrees relative to itself about its centre of mass" self.button_mirror.selections = { 0, 0, 1, 0 } self.button_mirror.options = { 0, 0 } self.button_mirror.func = function( pivot, axis, selections ) return PA_funcs.plane_mirror_entity( selections[1], PA_activeent ) end end function ROTATION_FUNCTIONS_TAB:Paint() draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), BGColor_Rotation) draw.RoundedBox(6, 598, 15, 287, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 5, self:GetTall()/2 + 15, 140, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 153, self:GetTall()/2 + 15, 140, self:GetTall()/2 - 20, BGColor) draw.RoundedBox(6, 301, self:GetTall()/2 + 15, 584, self:GetTall()/2 - 20, BGColor) end function ROTATION_FUNCTIONS_TAB:UpdateDescription() if self.activebutton then self.text_title:SetText( "- " .. self.activebutton:GetValue() .. " -" ) self.text_description:SetText(self.activebutton.description) end end vgui.Register("PA_Rotation_Functions_Tab", ROTATION_FUNCTIONS_TAB, "DPanel") //********************************************************************************************************************// // Constraints Tab //********************************************************************************************************************// local CONSTRAINTS_TAB = {} function CONSTRAINTS_TAB:Init() self:CopyBounds( self:GetParent() ) AddMenuText( "Anchor Points", 32, 9, self ) self.colour_panel_1 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_1:SetPos(5, 31) self.colour_panel_1:SetSize(136, 202) self.colour_panel_1:SetColour( BGColor_Point ) self.text_LPos1 = vgui.Create( "PA_Constraint_Title_Text", self.colour_panel_1 ) self.text_LPos1:SetText("Entity 1") self.list_point_LPos1 = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_point_LPos1:Text( "Pos 1", "Point", self ) self.list_point_LPos1:SetPos(13, 22) self.list_point_LPos1:SetMultiSelect(false) self.colour_panel_2 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_2:SetPos(5, 233) self.colour_panel_2:SetSize(136, 202) self.colour_panel_2:SetColour( BGColor_Point ) self.text_LPos2 = vgui.Create( "PA_Constraint_Title_Text", self.colour_panel_2 ) self.text_LPos2:SetText("Entity 2") self.list_point_LPos2 = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_point_LPos2:Text( "Pos 2", "Point", self ) self.list_point_LPos2:SetPos(13, 22) self.list_point_LPos2:SetToolTip( "Double click to deselect" ) self.list_point_LPos2:SetMultiSelect(false) self.list_point_LPos2.DoDoubleClick = function( Line, LineID ) self.list_point_LPos2:ClearSelection() end self.panel = vgui.Create("PA_Constraints_Panel", self) self.axis_tab = vgui.Create("PA_Constraints_Axis_Tab", self.panel) self.ballsocket_tab = vgui.Create("PA_Constraints_Ballsocket_Tab", self.panel) self.ballsocket_adv_tab = vgui.Create("PA_Constraints_Ballsocket_Adv_Tab", self.panel) self.elastic_tab = vgui.Create("PA_Constraints_Elastic_Tab", self.panel) self.rope_tab = vgui.Create("PA_Constraints_Rope_Tab", self.panel) self.slider_tab = vgui.Create("PA_Constraints_Slider_Tab", self.panel) self.wire_hydraulic_tab = vgui.Create("PA_Constraints_Wire_Hydraulic_Tab", self.panel) self.panel:AddSheet("Axis", self.axis_tab, false, false, false ) self.panel:AddSheet("Ball Socket", self.ballsocket_tab, false, false, false ) self.panel:AddSheet("Ball Socket Adv", self.ballsocket_adv_tab, false, false, false ) self.panel:AddSheet("Elastic", self.elastic_tab, false, false, false ) self.panel:AddSheet("Rope", self.rope_tab, false, false, false ) self.panel:AddSheet("Slider", self.slider_tab, false, false, false ) self.panel:AddSheet("Wire Hydraulic", self.wire_hydraulic_tab, false, false, false ) AddMenuText( "Constraint Description", 721, 9, self ) self.text_description = vgui.Create( "DLabel", self ) self.text_description:SetPos( 690, 36 ) self.text_description:SetSize( 195, 284 ) self.text_description:SetWrap( true ) self.text_description:SetContentAlignment(7) self.text_description:SetTextColor( Color(20,20,20,255) ) self:UpdateText() self.button_constraint = vgui.Create( "PA_Function_Button", self ) self.button_constraint:SetPos(687, 378) self.button_constraint:SetSize(191, 50) self.button_constraint:SetText("Create Constraint") self.button_constraint:SetFunction( function() local selection1 = self.list_point_LPos1:GetSelectedLine() local selection2 = self.list_point_LPos2:GetSelectedLine() if !selection1 then Warning("Select a point for Pos 1") return false end if !PA_funcs.construct_exists( "Point", selection1 ) then Warning("Point 1 has not been defined") return false end local activepanel = self.panel:GetActiveTab():GetPanel() local constraint_type = activepanel.Constraint if constraint_type ~= "Axis" and constraint_type ~= "Ballsocket" and constraint_type ~= "Ballsocket Advanced" then if !selection2 then Warning("Select a point for Pos 2") return false end if !PA_funcs.construct_exists( "Point", selection2 ) then Warning("Point 2 has not been defined") return false end end // Send local info if possible, no point in converting it to/from global if the constraint requires local coords local point1 = precision_align_points[ selection1 ] local point2 if !PA_funcs.construct_exists( "Point", selection2 ) then point2 = { ["origin"] = PA_funcs.point_global(selection1).origin + Vector(0,0,1) } -- Set so default axis dir is (0,0,1) else point2 = precision_align_points[ selection2 ] end local Ent1 = point1.entity local Ent2 = point2.entity if !Ent1 and !Ent2 then Warning("At least one point must be attached to an entity") return false end if Ent1 == Ent2 and constraint_type ~= "Rope" then Warning("Cannot constrain entity to itself") return false end Ent1 = Ent1 or Entity(0) Ent2 = Ent2 or Entity(0) local constraint_vars = activepanel.Constraint_Func() if constraint_vars == nil then return false end local LPos1 = point1.origin local LPos2 = point2.origin local data = {} data.Type = activepanel.Constraint data.Ent1 = Ent1:EntIndex() data.Ent2 = Ent2:EntIndex() data.LPos1 = { x = LPos1.x, y = LPos1.y, z = LPos1.z } data.LPos2 = { x = LPos2.x, y = LPos2.y, z = LPos2.z } data.vars = constraint_vars net.Start( PA_.. "constraint" ) net.WriteTable( data ) net.SendToServer() end ) // Create function in each sheet tab to update description text for _, v in pairs (self.panel.Items) do local Tab = v.Tab if Tab then Tab.OnMousePressed = function( mcode ) if self.panel:GetActiveTab() ~= Tab then self.panel:SetActiveTab( Tab ) self:UpdateText() end end end end end function CONSTRAINTS_TAB:Paint() //draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), Color(100,100,100,255)) // "Anchor Points" Background draw.RoundedBox(6, 5, 5, 136, 22, BGColor_Point) // Text background draw.RoundedBox(6, 680, 5, 205, 22, BGColor_Display) draw.RoundedBox(6, 682, 7, 201, 18, BGColor_Background) draw.RoundedBox(6, 680, 31, 205, 404, BGColor_Display) end function CONSTRAINTS_TAB:UpdateText() local sheet = self.panel:GetActiveTab():GetPanel() local constraint_type = sheet.Constraint local text = sheet.Description if constraint_type and text then self.text_description:SetText( "- " .. constraint_type .. " -\n\n" .. text ) else self.text_description:SetText( "" ) end end vgui.Register("PA_Constraints_Tab", CONSTRAINTS_TAB, "DPanel") local CONSTRAINTS_PANEL = {} function CONSTRAINTS_PANEL:Init() self:SetPos(140, 5) self:SetSize( 540, 435 ) end function CONSTRAINTS_PANEL:Paint() //draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), Color(100,100,100,255)) end vgui.Register("PA_Constraints_Panel", CONSTRAINTS_PANEL, "DPropertySheet") local CONSTRAINTS_AXIS_TAB = {} function CONSTRAINTS_AXIS_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Axis" self.Description = "Create an axis constraint with the rotation axis between Pos 1 and Pos 2\n\n" .. "If Axis is selected, the rotation axis will be in the direction of the selected line\n\n" .. "In this case Pos 2 is not used, but is still required to define the entity being constrained" self.colour_panel_1 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_1:SetPos(15, 15) self.colour_panel_1:SetSize(136, 202) self.colour_panel_1:SetColour( BGColor_Line ) self.text_axis = vgui.Create( "PA_Constraint_Title_Text", self.colour_panel_1 ) self.text_axis:SetText("Optional") self.list_point_axis = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_point_axis:Text( "Axis", "Line", self ) self.list_point_axis:SetPos(13, 22) self.list_point_axis:SetToolTip( "Double click to deselect" ) self.list_point_axis:SetMultiSelect(false) self.list_point_axis.DoDoubleClick = function( Line, LineID ) self.list_point_axis:ClearSelection() end self.combobox = self:AddComboBox( { Label = "#Presets", MenuButton = 1, Folder = PA, Options = {}, CVars = { [0] = PA_ .. "axis_forcelimit", [1] = PA_ .. "axis_torquelimit", [2] = PA_ .. "axis_friction", [3] = PA_ .. "axis_nocollide" } } ) self.combobox:SetPos(180, 20) self.combobox:SetWide(300) self.slider_forcelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_forcelimit:SetPos(180, 60) self.slider_forcelimit:SetWide(300) self.slider_forcelimit:SetText( "Force Limit" ) self.slider_forcelimit:SetConVar( PA_ .. "axis_forcelimit" ) self.slider_torquelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_torquelimit:SetPos(180, 110) self.slider_torquelimit:SetWide(300) self.slider_torquelimit:SetText( "Torque Limit" ) self.slider_torquelimit:SetConVar( PA_ .. "axis_torquelimit" ) self.slider_friction = vgui.Create( "PA_Constraint_Slider", self ) self.slider_friction:SetWide(300) self.slider_friction:SetPos(180, 160) self.slider_friction:SetText( "Friction" ) self.slider_friction:SetConVar( PA_ .. "axis_friction" ) self.checkbox_nocollide = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_nocollide:SetPos(180, 220) self.checkbox_nocollide:SetText( "Nocollide" ) self.checkbox_nocollide:SetSize(100,30) self.checkbox_nocollide:SetConVar( PA_ .. "axis_nocollide" ) // Send extra constraint info - if this returns false then no data is sent to the server self.Constraint_Func = function() local axis_selection = self.list_point_axis:GetSelectedLine() local axis = 0 local line if axis_selection and PA_funcs.construct_exists( "Line", axis_selection ) then line = PA_funcs.line_global( axis_selection ) axis = ( line.endpoint - line.startpoint ):GetNormal() axis = { x = axis.x, y = axis.y, z = axis.z } end return axis end end vgui.Register("PA_Constraints_Axis_Tab", CONSTRAINTS_AXIS_TAB, "PA_Constraints_Sheet") local CONSTRAINTS_BALLSOCKET_TAB = {} function CONSTRAINTS_BALLSOCKET_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Ballsocket" self.Description = "Create a ball socket constraint about Pos 1\n\n" .. "Pos 2 is not used, but is still required to define the entity being constrained" self.combobox = self:AddComboBox( { Label = "#Presets", MenuButton = 1, Folder = PA, Options = {}, CVars = { [0] = PA_ .. "ballsocket_forcelimit", [1] = PA_ .. "ballsocket_torquelimit", [2] = PA_ .. "ballsocket_nocollide" } } ) self.combobox:SetPos(50, 20) self.combobox:SetWide(430) self.slider_forcelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_forcelimit:SetWide(300) self.slider_forcelimit:SetPos(50, 60) self.slider_forcelimit:SetText( "Force Limit" ) self.slider_forcelimit:SetConVar( PA_ .. "ballsocket_forcelimit" ) self.slider_torquelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_torquelimit:SetWide(300) self.slider_torquelimit:SetPos(50, 110) self.slider_torquelimit:SetText( "Torque Limit" ) self.slider_torquelimit:SetConVar( PA_ .. "ballsocket_torquelimit" ) self.checkbox_nocollide = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_nocollide:SetPos(50, 170) self.checkbox_nocollide:SetText( "Nocollide" ) self.checkbox_nocollide:SetSize(100,30) self.checkbox_nocollide:SetConVar( PA_ .. "ballsocket_nocollide" ) self.Constraint_Func = function() return 0 end end vgui.Register("PA_Constraints_Ballsocket_Tab", CONSTRAINTS_BALLSOCKET_TAB, "PA_Constraints_Sheet") local CONSTRAINTS_BALLSOCKET_ADV_TAB = {} function CONSTRAINTS_BALLSOCKET_ADV_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Ballsocket Advanced" self.Description = "Create an advanced ball socket constraint about Pos 1\n\n" .. "Pos 2 is not used, but is still required to define the entity being constrained\n\n" .. "X/Y/Z Min/Max/Friction values are all in the direction of the world axes\n\n" .. "Free Movement will allow the two constrained entities to move independently of each other, rather than maintaining a set distance about Pos 1" self.combobox = self:AddComboBox( { Label = "#Presets", MenuButton = 1, Folder = PA, Options = {}, CVars = { [0] = PA_ .. "ballsocket_adv_forcelimit", [1] = PA_ .. "ballsocket_adv_torquelimit", [2] = PA_ .. "ballsocket_adv_xmin", [3] = PA_ .. "ballsocket_adv_ymin", [4] = PA_ .. "ballsocket_adv_zmin", [5] = PA_ .. "ballsocket_adv_xmax", [6] = PA_ .. "ballsocket_adv_ymax", [7] = PA_ .. "ballsocket_adv_zmax", [8] = PA_ .. "ballsocket_adv_xfric", [9] = PA_ .. "ballsocket_adv_yfric", [10] = PA_ .. "ballsocket_adv_zfric", [11] = PA_ .. "ballsocket_adv_onlyrotation", [12] = PA_ .. "ballsocket_adv_nocollide" } } ) self.combobox:SetPos(50, 20) self.combobox:SetWide(430) self.slider_forcelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_forcelimit:SetWide(300) self.slider_forcelimit:SetPos(50, 60) self.slider_forcelimit:SetText( "Force Limit" ) self.slider_forcelimit:SetConVar( PA_ .. "ballsocket_adv_forcelimit" ) self.slider_torquelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_torquelimit:SetWide(300) self.slider_torquelimit:SetPos(50, 110) self.slider_torquelimit:SetText( "Torque Limit" ) self.slider_torquelimit:SetConVar( PA_ .. "ballsocket_adv_torquelimit" ) self.slider_xmin = vgui.Create( "PA_Constraint_Slider", self ) self.slider_xmin.Label:SetSize( 50 ) self.slider_xmin:SetPos(20, 170) self.slider_xmin:SetWide(150) self.slider_xmin:SetMinMax(-180, 180) self.slider_xmin:SetText( "X Min" ) self.slider_xmin:SetConVar( PA_ .. "ballsocket_adv_xmin" ) self.slider_ymin = vgui.Create( "PA_Constraint_Slider", self ) self.slider_ymin.Label:SetSize( 50 ) self.slider_ymin:SetPos(20, 220) self.slider_ymin:SetWide(150) self.slider_ymin:SetMinMax(-180, 180) self.slider_ymin:SetText( "Y Min" ) self.slider_ymin:SetConVar( PA_ .. "ballsocket_adv_ymin" ) self.slider_zmin = vgui.Create( "PA_Constraint_Slider", self ) self.slider_zmin.Label:SetSize( 50 ) self.slider_zmin:SetPos(20, 270) self.slider_zmin:SetWide(150) self.slider_zmin:SetMinMax(-180, 180) self.slider_zmin:SetText( "Z Min" ) self.slider_zmin:SetConVar( PA_ .. "ballsocket_adv_zmin" ) self.slider_xmax = vgui.Create( "PA_Constraint_Slider", self ) self.slider_xmax.Label:SetSize( 50 ) self.slider_xmax:SetPos(190, 170) self.slider_xmax:SetWide(150) self.slider_xmax:SetMinMax(-180, 180) self.slider_xmax:SetText( "X Max" ) self.slider_xmax:SetConVar( PA_ .. "ballsocket_adv_xmax" ) self.slider_ymax = vgui.Create( "PA_Constraint_Slider", self ) self.slider_ymax.Label:SetSize( 50 ) self.slider_ymax:SetPos(190, 220) self.slider_ymax:SetWide(150) self.slider_ymax:SetMinMax(-180, 180) self.slider_ymax:SetText( "Y Max" ) self.slider_ymax:SetConVar( PA_ .. "ballsocket_adv_ymax" ) self.slider_zmax = vgui.Create( "PA_Constraint_Slider", self ) self.slider_zmax.Label:SetSize( 50 ) self.slider_zmax:SetPos(190, 270) self.slider_zmax:SetWide(150) self.slider_zmax:SetMinMax(-180, 180) self.slider_zmax:SetText( "Z Max" ) self.slider_zmax:SetConVar( PA_ .. "ballsocket_adv_zmax" ) self.slider_xfric = vgui.Create( "PA_Constraint_Slider", self ) self.slider_xfric.Label:SetSize( 50 ) self.slider_xfric:SetPos(360, 170) self.slider_xfric:SetWide(150) self.slider_xfric:SetMinMax(-180, 180) self.slider_xfric:SetText( "X Friction" ) self.slider_xfric:SetConVar( PA_ .. "ballsocket_adv_xfric" ) self.slider_yfric = vgui.Create( "PA_Constraint_Slider", self ) self.slider_yfric.Label:SetSize( 50 ) self.slider_yfric:SetPos(360, 220) self.slider_yfric:SetWide(150) self.slider_yfric:SetMinMax(-180, 180) self.slider_yfric:SetText( "Y Friction" ) self.slider_yfric:SetConVar( PA_ .. "ballsocket_adv_yfric" ) self.slider_zfric = vgui.Create( "PA_Constraint_Slider", self ) self.slider_zfric.Label:SetSize( 50 ) self.slider_zfric:SetPos(360, 270) self.slider_zfric:SetWide(150) self.slider_zfric:SetMinMax(-180, 180) self.slider_zfric:SetText( "Z Friction" ) self.slider_zfric:SetConVar( PA_ .. "ballsocket_adv_zfric" ) self.checkbox_free = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_free:SetPos(50, 335) self.checkbox_free:SetText( "Free Movement" ) self.checkbox_free:SetSize(100,30) self.checkbox_free:SetConVar( PA_ .. "ballsocket_adv_onlyrotation" ) self.checkbox_nocollide = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_nocollide:SetPos(50, 363) self.checkbox_nocollide:SetText( "Nocollide" ) self.checkbox_nocollide:SetSize(100,30) self.checkbox_nocollide:SetConVar( PA_ .. "ballsocket_adv_nocollide" ) self.Constraint_Func = function() return 0 end end vgui.Register("PA_Constraints_Ballsocket_Adv_Tab", CONSTRAINTS_BALLSOCKET_ADV_TAB, "PA_Constraints_Sheet") local CONSTRAINTS_ELASTIC_TAB = {} function CONSTRAINTS_ELASTIC_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Elastic" self.Description = "Create an elastic constraint between Pos 1 and Pos 2" self.combobox = self:AddComboBox( { Label = "#Presets", MenuButton = 1, Folder = PA, Options = {}, CVars = { [0] = PA_ .. "elastic_constant", [1] = PA_ .. "elastic_damping", [2] = PA_ .. "elastic_rdamping", [3] = PA_ .. "elastic_material", [4] = PA_ .. "elastic_width", [5] = PA_ .. "elastic_stretchonly" } } ) self.combobox:SetPos(25, 20) self.combobox:SetWide(225) self.slider_constant = vgui.Create( "PA_Constraint_Slider", self ) self.slider_constant.Label:SetSize( 75 ) self.slider_constant:SetWide( 225 ) self.slider_constant:SetPos(25, 60) self.slider_constant:SetText( "Spring Constant" ) self.slider_constant:SetConVar( PA_ .. "elastic_constant" ) self.slider_damping = vgui.Create( "PA_Constraint_Slider", self ) self.slider_damping.Label:SetSize( 75 ) self.slider_damping:SetWide( 225 ) self.slider_damping:SetPos(25, 110) self.slider_damping:SetMax(10000) self.slider_damping:SetText( "Damping Constant" ) self.slider_damping:SetConVar( PA_ .. "elastic_damping" ) self.slider_rdamping = vgui.Create( "PA_Constraint_Slider", self ) self.slider_rdamping.Label:SetSize( 75 ) self.slider_rdamping:SetWide( 225 ) self.slider_rdamping:SetPos(25, 160) self.slider_rdamping:SetMax(100) self.slider_rdamping:SetText( "Relative Damping Constant" ) self.slider_rdamping:SetConVar( PA_ .. "elastic_rdamping" ) self.slider_width = vgui.Create( "PA_Constraint_Slider", self ) self.slider_width.Label:SetSize( 75 ) self.slider_width:SetWide( 225 ) self.slider_width:SetPos(25, 210) self.slider_width:SetMax(20) self.slider_width:SetText( "Width" ) self.slider_width:SetConVar( PA_ .. "elastic_width" ) self.checkbox_stretch = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_stretch:SetPos(50, 260) self.checkbox_stretch:SetText( "Stretch Only" ) self.checkbox_stretch:SetSize(100,30) self.checkbox_stretch:SetConVar( PA_ .. "elastic_stretchonly" ) self.matselect = vgui.Create( "RopeMaterial", self ) self.matselect:SetPos(250, 20) self.matselect:SetWide(250) self.matselect:SetConVar( PA_ .. "elastic_material" ) self.Constraint_Func = function() return 0 end end vgui.Register("PA_Constraints_Elastic_Tab", CONSTRAINTS_ELASTIC_TAB, "PA_Constraints_Sheet") local CONSTRAINTS_ROPE_TAB = {} function CONSTRAINTS_ROPE_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Rope" self.Description = "Create a rope constraint between Pos 1 and Pos 2\n\n" .. "By setting Set Length, the length of the rope can be controlled directly\n\n" .. "If this is 0, then the length of the rope will be determined by the distance between Pos 1 and Pos 2, plus Add Length\n\n" self.combobox = self:AddComboBox( { Label = "#Presets", MenuButton = 1, Folder = PA, Options = {}, CVars = { [0] = PA_ .. "rope_forcelimit", [1] = PA_ .. "rope_addlength", [2] = PA_ .. "rope_width", [3] = PA_ .. "rope_material", [4] = PA_ .. "rope_rigid", [5] = PA_ .. "rope_setlength" } } ) self.combobox:SetPos(25, 20) self.combobox:SetWide(225) self.slider_forcelimit = vgui.Create( "PA_Constraint_Slider", self ) self.slider_forcelimit.Label:SetSize( 75 ) self.slider_forcelimit:SetWide(225) self.slider_forcelimit:SetPos(25, 60) self.slider_forcelimit:SetText( "Force Limit" ) self.slider_forcelimit:SetConVar( PA_ .. "rope_forcelimit" ) self.slider_addlength = vgui.Create( "PA_Constraint_Slider", self ) self.slider_addlength.Label:SetSize( 75 ) self.slider_addlength:SetWide(225) self.slider_addlength:SetPos(25, 110) self.slider_addlength:SetMinMax(-1000, 1000) self.slider_addlength:SetText( "Add Length" ) self.slider_addlength:SetConVar( PA_ .. "rope_addlength" ) self.slider_width = vgui.Create( "PA_Constraint_Slider", self ) self.slider_width.Label:SetSize( 75 ) self.slider_width:SetWide(225) self.slider_width:SetPos(25, 160) self.slider_width:SetMax(20) self.slider_width:SetText( "Width" ) self.slider_width:SetConVar( PA_ .. "rope_width" ) self.slider_setlength = vgui.Create( "PA_Constraint_Slider", self ) self.slider_setlength.Label:SetSize( 75 ) self.slider_setlength:SetWide(225) self.slider_setlength:SetPos(25, 210) self.slider_setlength:SetMax(2000) self.slider_setlength:SetText( "Set Length" ) self.slider_setlength:SetConVar( PA_ .. "rope_setlength" ) self.checkbox_rigid = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_rigid:SetPos(50, 260) self.checkbox_rigid:SetText( "Rigid" ) self.checkbox_rigid:SetSize(100,30) self.checkbox_rigid:SetConVar( PA_ .. "rope_rigid" ) self.matselect = vgui.Create( "RopeMaterial", self ) -- self.matselect:SetPos(50, 220) self.matselect:SetPos(250, 20) self.matselect:SetWide(250) self.matselect:SetConVar( PA_ .. "rope_material" ) self.Constraint_Func = function() return 0 end end vgui.Register("PA_Constraints_Rope_Tab", CONSTRAINTS_ROPE_TAB, "PA_Constraints_Sheet") local CONSTRAINTS_SLIDER_TAB = {} function CONSTRAINTS_SLIDER_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Slider" self.Description = "Create a slider constraint between Pos 1 and Pos 2\n\n" .. "The direction of the slider will always be between Pos 1 and Pos 2" self.slider_width = vgui.Create( "PA_Constraint_Slider", self ) self.slider_width:SetWide(300) self.slider_width:SetPos(50, 20) self.slider_width:SetMax(20) self.slider_width:SetText( "Width" ) self.slider_width:SetConVar( PA_ .. "slider_width" ) self.Constraint_Func = function() return 0 end end vgui.Register("PA_Constraints_Slider_Tab", CONSTRAINTS_SLIDER_TAB, "PA_Constraints_Sheet") local CONSTRAINTS_WIRE_HYDRAULIC_TAB = {} function CONSTRAINTS_WIRE_HYDRAULIC_TAB:Init() self:CopyBounds( self:GetParent() ) self.Constraint = "Wire Hydraulic" self.Description = "This is for editing wire hydraulic constraints that have already been created\n\n" .. "Select the hydraulic controller with right click, then set the constraint according to Pos 1 and Pos 2 to overwrite the existing hydraulic\n\n" .. "For technical reasons there is no 'fixed' option, but this can be done easily by creating a slider constraint manually\n\n" .. "As a result, it is adviseable not to use this on fixed hydraulics without first removing the existing hydraulic slider" self.slider_width = vgui.Create( "PA_Constraint_Slider", self ) self.slider_width.Label:SetSize( 75 ) self.slider_width:SetWide(225) self.slider_width:SetPos(25, 20) self.slider_width:SetMax(20) self.slider_width:SetText( "Width" ) self.slider_width:SetConVar( PA_ .. "wire_hydraulic_width" ) self.matselect = vgui.Create( "RopeMaterial", self ) self.matselect:SetPos(250, 20) self.matselect:SetWide(250) self.matselect:SetConVar( PA_ .. "wire_hydraulic_material" ) self.Constraint_Func = function() if !IsValid( PA_activeent ) then Warning( "Select a wire hydraulic controller" ) return false end if PA_activeent:GetClass() ~= "gmod_wire_hydraulic" then Warning( "Select a wire hydraulic controller" ) return false end return PA_activeent:EntIndex() end end vgui.Register("PA_Constraints_Wire_Hydraulic_Tab", CONSTRAINTS_WIRE_HYDRAULIC_TAB, "PA_Constraints_Sheet")// Preicsion Alignment maths functions library - By Wenli if SERVER then return end local PA = "precision_align" local PA_ = PA .. "_" PA_funcs = {} //********************************************************************************************************************// // Global Functions //********************************************************************************************************************// local function Message( text ) if GetConVarNumber(PA_.."display_messages") == 1 then LocalPlayer():ChatPrint("(PA) " .. text) end end local function Warning( text ) if GetConVarNumber(PA_.."display_warnings") == 1 then LocalPlayer():ChatPrint("(PA) ERROR: " .. text) end end // Set view in direction of world position vector v PA_funcs.set_playerview = function( v ) if !v then return false end local ply = LocalPlayer() local pos = ply:GetShootPos() local Ang = (v - pos):Angle() ply:SetEyeAngles(Ang) return Ang end // Sends data to server to move entity - ent1/ent2 are the entities the points are attached to PA_funcs.move_entity = function( vec1, vec2, activeent ) if !IsValid(activeent) then Warning("No valid entity selected") return false end if !vec1 or !vec2 then Warning("Incomplete move data") return false end local v = vec2 - vec1 // Shift determines whether to stack local shift = LocalPlayer():KeyDown( IN_SPEED ) local stack = 0 if shift then stack = GetConVarNumber( PA_.."stack_num" ) if stack == 1 then Message("Stacked entity by " .. tostring(v)) else Message("Stacked entity " .. tostring(stack) .. " times, by " .. tostring(v)) end else Message("Moved entity by " .. tostring(v)) end RunConsoleCommand( PA_.. "move", tostring(v.x), tostring(v.y), tostring(v.z), tostring(stack) ) return true end // Sends data to server to rotate entity - vector is pivot point PA_funcs.rotate_entity = function( ang, vec, relative, activeent ) if !IsValid(activeent) then Warning("No valid entity selected") return false end if !ang then Warning("Incomplete rotation data") return false end // Rotate by prop origin by default vec = vec or {} // Shift determines whether to stack local msgstring local shift = LocalPlayer():KeyDown( IN_SPEED ) local stack = 0 if shift then stack = GetConVarNumber( PA_.."stack_num" ) if stack == 1 then msgstring = "Stacked Entity " else msgstring = "Stacked Entity " .. tostring(stack) .. " times, " end else msgstring = "Rotated Entity " end // Set angles relative to prop if relative == 1 then msgstring = msgstring .. "at local angles " // Rotate by world axes elseif relative == 2 then msgstring = msgstring .. "by world axes " // Rotate by axis/angle, where ang is a vector with magnitude in degrees elseif relative == 3 then msgstring = msgstring .. "by axis/angle " // Set absolute angles else relative = 0 msgstring = msgstring .. "at angles " end msgstring = msgstring .. tostring(ang) Message( msgstring ) RunConsoleCommand( PA_.. "rotate", tostring(ang.p), tostring(ang.y), tostring(ang.r), tostring(vec.x), tostring(vec.y), tostring(vec.z), tostring(relative), tostring(stack) ) return true end PA_funcs.construct_exists = function( construct_type, ID ) if !construct_type or !ID then return false end if construct_type == "Point" then if precision_align_points[ID].origin then return true end elseif construct_type == "Line" then if precision_align_lines[ID].startpoint and precision_align_lines[ID].endpoint then return true end elseif construct_type == "Plane" then if precision_align_planes[ID].origin and precision_align_planes[ID].normal then return true end end return false end //********************************************************************************************************************// // Point Functions //********************************************************************************************************************// PA_funcs.point_global = function( point ) local point_temp = table.Copy( precision_align_points[point] ) if !point_temp.origin then return false end local ent = point_temp.entity if ent then if IsValid(ent) then point_temp.origin = ent:LocalToWorld( point_temp.origin ) else PA_funcs.delete_point(point) return false end end return point_temp end PA_funcs.point_local = function( point ) local point_temp = table.Copy( precision_align_points[point] ) if !point_temp.origin then return false end local ent = point_temp.entity if ent then if IsValid(ent) then point_temp.origin = ent:WorldToLocal( point_temp.origin ) else PA_funcs.delete_point(point) return false end end return point_temp end PA_funcs.set_point = function( point, origin ) if !origin then Warning("Incomplete point data") return false end local vec if IsValid( precision_align_points[point].entity ) then vec = precision_align_points[point].entity:WorldToLocal(origin) else vec = origin end precision_align_points[point].origin = vec Message("Point [" .. tostring(point) .. "] set at " .. tostring(origin)) return true end PA_funcs.delete_point = function( point ) precision_align_points[point] = {visible = true} Message("Point " .. tostring(point) .. " deleted") return true end PA_funcs.delete_points = function() for k, v in ipairs ( precision_align_points ) do precision_align_points[k] = {visible = true} end Message("All points cleared") return true end PA_funcs.attach_point = function( point, ent ) if !precision_align_points[point].origin then Warning("Point must be defined before attaching") return false end local attached_ent = precision_align_points[point].entity if !IsValid(ent) then if attached_ent then precision_align_points[point].origin = attached_ent:LocalToWorld(precision_align_points[point].origin) precision_align_points[point].entity = nil Message("Point " .. tostring(point) .. " detached") return true else Warning("No valid entity selected") return false end end if attached_ent then if attached_ent == ent then Message("Point is already attached to this entity") return false else precision_align_points[point].origin = attached_ent:LocalToWorld(precision_align_points[point].origin) end end precision_align_points[point].entity = ent precision_align_points[point].origin = ent:WorldToLocal(precision_align_points[point].origin) Message("Point " .. tostring(point) .. " attached to " .. tostring(ent)) return true end PA_funcs.point_function_average = function( points_table ) local i = 0 local vec = Vector(0,0,0) for k, v in pairs (points_table) do local point_temp = PA_funcs.point_global(v:GetID()) if point_temp then i = i + 1 vec = vec + point_temp.origin end end if i < 2 then Warning("Point average requires 2 or more points") return false end vec = vec * 1/i return vec end //********************************************************************************************************************// // Line Functions //********************************************************************************************************************// PA_funcs.line_global = function( line ) local line_temp = table.Copy( precision_align_lines[line] ) if !line_temp.startpoint or !line_temp.endpoint then return false end local ent = line_temp.entity if ent then if IsValid(ent) then line_temp.startpoint = ent:LocalToWorld( line_temp.startpoint ) line_temp.endpoint = ent:LocalToWorld( line_temp.endpoint ) else PA_funcs.delete_line(line) return false end end return line_temp end PA_funcs.line_local = function( line ) local line_temp = table.Copy( precision_align_lines[line] ) if !line_temp.startpoint or !line_temp.endpoint then return false end local ent = line_temp.entity if ent then if IsValid(ent) then line_temp.startpoint = ent:WorldToLocal( line_temp.startpoint ) line_temp.endpoint = ent:WorldToLocal( line_temp.endpoint ) else PA_funcs.delete_line(line) return false end end return line_temp end PA_funcs.set_line = function( line, startpoint, endpoint, direction, length ) local ent = precision_align_lines[line].entity if !IsValid(ent) then ent = nil end local startpoint_old = precision_align_lines[line].startpoint if startpoint then if ent then precision_align_lines[line].startpoint = ent:WorldToLocal(startpoint) else precision_align_lines[line].startpoint = startpoint end if !endpoint and !direction then Message("Line [" .. tostring(line) .. "] startpoint set at " .. tostring(startpoint)) return true end end if endpoint then if ent then precision_align_lines[line].endpoint = ent:WorldToLocal(endpoint) else precision_align_lines[line].endpoint = endpoint end if startpoint then Message("Line [" .. tostring(line) .. "] set from " .. tostring(startpoint) .. " to " .. tostring(endpoint)) else Message("Line [" .. tostring(line) .. "] endpoint set at " .. tostring(endpoint)) end return true end if !precision_align_lines[line].startpoint then Warning("Line not defined") return false end if direction then local len if !length then //Check to see whether line already exists, if so use that length if PA_funcs.construct_exists( "Line", line ) then len = startpoint_old:Distance( precision_align_lines[line].endpoint ) else len = GetConVarNumber( PA_.."default_linelength" ) end else len = length end if ent then direction = ( ent:WorldToLocal(ent:GetPos() + direction) ):GetNormal() end precision_align_lines[line].endpoint = precision_align_lines[line].startpoint + direction * len Message("Line [" .. tostring(line) .. "] set at " .. tostring(startpoint) .. " with length " .. tostring(len)) return true end if length then // true regardless of ent local dir = ( precision_align_lines[line].endpoint - precision_align_lines[line].startpoint ):GetNormal() new_endpoint = precision_align_lines[line].startpoint + dir * length Message("Line [" .. tostring(line) .. "] length set to " .. tostring(len)) return true end return true end PA_funcs.delete_line = function( line ) precision_align_lines[line] = {visible = true} Message("Line " .. tostring(line) .. " deleted") return true end PA_funcs.delete_lines = function() for k, v in ipairs (precision_align_lines) do precision_align_lines[k] = {visible = true} end Message("All lines cleared") return true end PA_funcs.attach_line = function( line, ent ) if !precision_align_lines[line].startpoint or !precision_align_lines[line].endpoint then Warning("Line must be defined before attaching") return false end local attached_ent = precision_align_lines[line].entity if !IsValid(ent) then if attached_ent then precision_align_lines[line].startpoint = attached_ent:LocalToWorld(precision_align_lines[line].startpoint) precision_align_lines[line].endpoint = attached_ent:LocalToWorld(precision_align_lines[line].endpoint) precision_align_lines[line].entity = nil Message("Line " .. tostring(line) .. " detached") return true else Warning("No valid entity selected") return false end end if attached_ent then if attached_ent == ent then Message("Line is already attached to this entity") return false else precision_align_lines[line].startpoint = attached_ent:LocalToWorld(precision_align_lines[line].startpoint) precision_align_lines[line].endpoint = attached_ent:LocalToWorld(precision_align_lines[line].endpoint) end end precision_align_lines[line].entity = ent precision_align_lines[line].startpoint = ent:WorldToLocal(precision_align_lines[line].startpoint) precision_align_lines[line].endpoint = ent:WorldToLocal(precision_align_lines[line].endpoint) Message("Line " .. tostring(line) .. " attached to " .. tostring(ent)) return true end PA_funcs.line_function_perpendicular = function( lineID1, lineID2 ) if PA_funcs.construct_exists( "Line", lineID1 ) and PA_funcs.construct_exists( "Line", lineID2 ) then local line1 = PA_funcs.line_global(lineID1) local line2 = PA_funcs.line_global(lineID2) local dir1 = line1.endpoint - line1.startpoint local dir2 = line2.endpoint - line2.startpoint local vec = ( dir1:Cross(dir2) ):GetNormal() if vec:Length() == 0 then Warning("Tried to find cross product of two parallel lines") return false end return vec end Warning("2 lines required to find perpendicular direction") return false end //********************************************************************************************************************// // Plane Functions //********************************************************************************************************************// PA_funcs.plane_global = function( plane ) local plane_temp = table.Copy(precision_align_planes[plane]) if !plane_temp.origin or !plane_temp.normal then return false end local ent = plane_temp.entity if ent then if IsValid(ent) then plane_temp.origin = ent:LocalToWorld(plane_temp.origin) plane_temp.normal = ( ent:LocalToWorld(plane_temp.normal) - ent:GetPos()):GetNormal() else PA_funcs.delete_plane(plane) return false end end return plane_temp end PA_funcs.plane_local = function( plane ) local plane_temp = table.Copy(precision_align_planes[plane]) if !plane_temp.origin or !plane_temp.normal then return false end local ent = plane_temp.entity if ent then if IsValid(ent) then plane_temp.origin = ent:WorldToLocal(plane_temp.origin) plane_temp.normal = ( ent:WorldToLocal(ent:GetPos() + plane_temp.normal) ):GetNormal() else PA_funcs.delete_plane(plane) return false end end return plane_temp end PA_funcs.set_plane = function( plane, origin, normal ) local ent = precision_align_planes[plane].entity if origin then if ent then precision_align_planes[plane].origin = ent:WorldToLocal(origin) else precision_align_planes[plane].origin = origin end end if !precision_align_planes[plane].origin then Warning("Plane not defined") return false end if normal then if ent then precision_align_planes[plane].normal = ( ent:WorldToLocal(ent:GetPos() + normal) ):GetNormal() else precision_align_planes[plane].normal = normal end end if origin and normal then Message("Plane [" .. tostring(plane) .. "] set at " .. tostring(origin) .. " with normal " .. tostring(normal)) elseif origin then Message("Plane [" .. tostring(plane) .. "] origin set to " .. tostring(origin)) else Message("Plane [" .. tostring(plane) .. "] normal set to " .. tostring(normal)) end return true end PA_funcs.delete_plane = function( plane ) precision_align_planes[plane] = {visible = true} Message("Plane " .. tostring(plane) .. " deleted") return true end PA_funcs.delete_planes = function() for k, v in ipairs (precision_align_planes) do precision_align_planes[k] = {visible = true} end Message("All planes cleared") return true end PA_funcs.attach_plane = function( plane, ent ) if !precision_align_planes[plane].origin or !precision_align_planes[plane].normal then Warning("Plane must be defined before attaching") return false end local attached_ent = precision_align_planes[plane].entity if !IsValid(ent) then if attached_ent then precision_align_planes[plane].origin = attached_ent:LocalToWorld(precision_align_planes[plane].origin) precision_align_planes[plane].normal = ( attached_ent:LocalToWorld(precision_align_planes[plane].normal) - attached_ent:GetPos() ):GetNormal() precision_align_planes[plane].entity = nil Message("Plane " .. tostring(plane) .. " detached") return true else Warning("No valid entity selected") return false end end if attached_ent then if attached_ent == ent then Message("Plane is already attached to this entity") return false else precision_align_planes[plane].origin = attached_ent:LocalToWorld(precision_align_planes[plane].origin) precision_align_planes[plane].normal = ( attached_ent:LocalToWorld(precision_align_planes[plane].normal) - attached_ent:GetPos() ):GetNormal() end end precision_align_planes[plane].entity = ent precision_align_planes[plane].origin = ent:WorldToLocal(precision_align_planes[plane].origin) precision_align_planes[plane].normal = ( ent:WorldToLocal(ent:GetPos() + precision_align_planes[plane].normal) ):GetNormal() Message("Plane " .. tostring(plane) .. " attached to " .. tostring(ent)) return true end PA_funcs.plane_function_perpendicular = function( planeID1, planeID2 ) if PA_funcs.construct_exists( "Plane", planeID1 ) and PA_funcs.construct_exists( "Plane", planeID2 ) then local plane1 = PA_funcs.plane_global(planeID1) local plane2 = PA_funcs.plane_global(planeID2) local vec = ( plane1.normal:Cross(plane2.normal) ):GetNormal() if vec:Length() == 0 then Warning("Tried to find cross product of two parallel planes") end return vec end Warning("2 planes required to find perpendicular normal") return false end //********************************************************************************************************************// // Combined Functions //********************************************************************************************************************// // Solve simultaneous equations in form: // a1 + b1 = c1, a2 + b2 = c2 local function solve_simultaneous_2( a1, b1, c1, a2, b2, c2 ) local d = a1 * b2 - b1 * a2 if d == 0 then Warning("No real solution found for given coefficients") return false end local x = ( b2 * c1 - b1 * c2 )/d local y = ( a1 * c2 - a2 * c1 )/d return x, y end local function solve_point_2line_intersection( line1, line2 ) local A = line1.endpoint - line1.startpoint local B = line2.endpoint - line2.startpoint local normal = (A:Cross(B)):GetNormal() // Check lines are not parallel if normal:Length() == 0 then Warning("Cannot find intercept of two parallel lines") return false end local C = line2.startpoint - line1.startpoint local a1, a2, b1, b2, c1, c2 a1 = A:Dot(A) b1 = -A:Dot(B) c1 = A:Dot(C) a2 = -b1 b2 = -B:Dot(B) c2 = B:Dot(C) // solve simultaneous local length1, length2 = solve_simultaneous_2( a1, b1, c1, a2, b2, c2 ) if !length1 or !length2 then return false end local dir1, dir2 = A:GetNormal(), B:GetNormal() local point1 = line1.startpoint + A * length1 local point2 = line2.startpoint + B * length2 local point3 = ( point1 + point2 ) * 0.5 return point3 end PA_funcs.point_2line_intersection = function( lineID1, lineID2 ) local line1 = PA_funcs.line_global( lineID1 ) local line2 = PA_funcs.line_global( lineID2 ) return solve_point_2line_intersection( line1, line2 ) end PA_funcs.point_lineplane_intersection = function( lineID, planeID ) local line = PA_funcs.line_global( lineID ) local plane = PA_funcs.plane_global( planeID ) local A = line.endpoint - line.startpoint local normal = plane.normal // Check line and plane are not parallel if normal:Dot(A) == 0 then Warning("Cannot find intercept of parallel line and plane") return false end local length = normal:Dot(plane.origin - line.startpoint) / normal:Dot(A) local point = line.startpoint + A * length return point end PA_funcs.line_2plane_intersection = function( planeID1, planeID2 ) local plane1 = PA_funcs.plane_global( planeID1 ) local plane2 = PA_funcs.plane_global( planeID2 ) local dir = plane1.normal:Cross(plane2.normal) // Check planes are not parallel if dir:Length() == 0 then Warning("Cannot find intersection of two parallel planes") return false end local line1, line2 = {}, {} line1.startpoint = plane1.origin line1.endpoint = plane1.origin + plane1.normal:Cross(dir) line2.startpoint = plane2.origin line2.endpoint = plane2.origin + plane2.normal:Cross(dir) // Construct solved line local line = {} line.startpoint = solve_point_2line_intersection( line1, line2 ) line.direction = dir:GetNormal() -- returns point/direction format as other functions will calculate endpoint anyway return line end PA_funcs.point_3plane_intersection = function( planeID1, planeID2, planeID3 ) local plane1 = PA_funcs.plane_global( planeID1 ) local plane2 = PA_funcs.plane_global( planeID2 ) local plane3 = PA_funcs.plane_global( planeID3 ) // Check plane normals are not parallel local product = plane1.normal:Dot(plane2.normal:Cross(plane3.normal)) if product == 0 then Warning("One or more planes are parallel, no unique point found") return false elseif math.abs(product) < 0.01 then Warning("Planes are almost parallel, computation may be inaccurate") end local line = PA_funcs.line_2plane_intersection( planeID1, planeID2 ) if !line then return false end // Calculate line/plane intercept local length = plane3.normal:Dot(plane3.origin - line.startpoint) / plane3.normal:Dot(line.direction) local point = line.startpoint + line.direction * length return point end PA_funcs.point_line_projection = function( pointID, lineID ) local point = PA_funcs.point_global( pointID ) local line = PA_funcs.line_global( lineID ) local point1 = line.startpoint local point2 = line.endpoint local point3 = point.origin local dir = point2 - point1 local vec = point1 + ((point3 - point1):Dot(dir)) / (dir:Dot(dir)) * dir return vec end PA_funcs.point_plane_projection = function( pointID, planeID ) local point = PA_funcs.point_global( pointID ) local plane = PA_funcs.plane_global( planeID ) //Same method as for line/plane intersection local normal = plane.normal local length = normal:Dot(plane.origin - point.origin) local vec = point.origin + normal * length return vec end // These are general functions for mirroring vectors PA_funcs.point_mirror = function( vec, origin, normal ) local length = normal:Dot( origin - vec ) local vec1 = vec + normal * length * 2 return vec1 end PA_funcs.direction_mirror = function( dir, normal ) local dir1 = dir - 2 * normal * ( dir:Dot(normal) ) return dir1 end PA_funcs.line_plane_projection = function( lineID, planeID ) local line = PA_funcs.line_global( lineID ) local plane = PA_funcs.plane_global( planeID ) local normal = plane.normal local length = normal:Dot(plane.origin - line.startpoint) line.startpoint = line.startpoint + normal * length local length = normal:Dot(plane.origin - line.endpoint) line.endpoint = line.endpoint + normal * length if line.startpoint == line.endpoint then Warning("Line - plane projection results in a single point! Check line is not perpendicular to plane") return false end return line end PA_funcs.plane_3points = function( pointID1, pointID2, pointID3 ) local point1 = PA_funcs.point_global( pointID1 ).origin local point2 = PA_funcs.point_global( pointID2 ).origin local point3 = PA_funcs.point_global( pointID3 ).origin local dir1 = point2 - point1 local dir2 = point3 - point1 local dir3 = point3 - point2 if dir1:Length() == 0 or dir2:Length() == 0 or dir3:Length() == 0 then Warning("Require at least 3 unique points to define plane") return false end local plane_temp = {} plane_temp.direction = ( dir2:Cross(dir1) ):GetNormal() plane_temp.origin = ( point1 + point2 + point3 ) * 1/3 return plane_temp end //********************************************************************************************************************// // Rotation Functions //********************************************************************************************************************// local function matrix_x_vector( matrix, v ) local v1 = matrix[1] local v2 = matrix[2] local v3 = matrix[3] return Vector( v1.x * v.x + v1.y * v.y + v1.z * v.z, v2.x * v.x + v2.y * v.y + v2.z * v.z, v3.x * v.x + v3.y * v.y + v3.z * v.z ) end local function matrix_transpose( matrix ) local v1 = matrix[1] local v2 = matrix[2] local v3 = matrix[3] return { Vector( v1.x, v2.x, v3.x ), Vector( v1.y, v2.y, v3.y ), Vector( v1.z, v2.z, v3.z ) } end // Rotate angle by world angles PA_funcs.rotate_world = function( ang, rotang ) if rotang.p != 0 then ang:RotateAroundAxis( Vector(0,1,0), rotang.p ) end if rotang.y != 0 then ang:RotateAroundAxis( Vector(0,0,1), rotang.y ) end if rotang.r != 0 then ang:RotateAroundAxis( Vector(1,0,0), rotang.r ) end return ang end // Rotate line 1 so it ends up in same direction as line 2 PA_funcs.rotate_2lines_parallel = function( pivot, lineID1, lineID2, activeent ) local line1 = PA_funcs.line_global( lineID1 ) local line2 = PA_funcs.line_global( lineID2 ) if !pivot then pivot = line1.startpoint end local dir1 = ( line1.endpoint - line1.startpoint ):GetNormal() local dir2 = ( line2.endpoint - line2.startpoint ):GetNormal() local vec = dir1:Cross(dir2):GetNormal() local ang = activeent:GetAngles() if dir1 == Vector(-dir2.x, -dir2.y, -dir2.z) then ang = Angle(-ang.p, ang.y + 180, -ang.r) end local angle = math.deg( math.acos( dir1:Dot(dir2) ) ) ang:RotateAroundAxis( vec, angle ) return PA_funcs.rotate_entity(ang, pivot, 0, activeent) end // Rotate plane 1 normal to match plane 2 normal PA_funcs.rotate_2planes_parallel = function( pivot, planeID1, planeID2, activeent ) local plane1 = PA_funcs.plane_global( planeID1 ) local plane2 = PA_funcs.plane_global( planeID2 ) if !pivot then pivot = plane1.origin end local dir1 = plane1.normal local dir2 = plane2.normal local vec = dir1:Cross(dir2):GetNormal() local ang = activeent:GetAngles() if dir1 == Vector(-dir2.x, -dir2.y, -dir2.z) then ang = Angle(-ang.p, ang.y + 180, -ang.r) end local angle = math.deg( math.acos( dir1:Dot(dir2) ) ) ang:RotateAroundAxis( vec, angle ) return PA_funcs.rotate_entity(ang, pivot, 0, activeent) end // NOT USED CURRENTLY *********************** // Rotate line so it ends up in same direction as plane normal -- function rotate_lines_planenormal_parallel( pivot, lineID, planeID, activeent ) -- local line = PA_funcs.line_global( lineID ) -- local plane = PA_funcs.plane_global( planeID ) -- local dir1 = ( line.endpoint - line.startpoint ):GetNormal() -- local dir2 = plane.normal -- local vec = dir1:Cross(dir2):GetNormal() -- local ang = activeent:GetAngles() -- if dir1 == -dir2 then -- ang = Angle(-ang.p, ang.y + 180, -ang.r) -- end -- local angle = math.deg( math.acos( dir1:Dot(dir2) ) ) -- ang:RotateAroundAxis( vec, angle ) -- return PA_funcs.rotate_entity(ang, pivot, activeent) -- end // Rotate line until parallel to plane PA_funcs.rotate_line_plane_parallel = function( pivot, axis, lineID, planeID, activeent ) local line = PA_funcs.line_global( lineID ) local plane = PA_funcs.plane_global( planeID ) local linedir1 = ( line.endpoint - line.startpoint ):GetNormal() local normal = plane.normal local axisdir if axis then axisdir = ( axis.endpoint - axis.startpoint ):GetNormal() if axisdir == normal or axisdir == -normal then Warning("Rotation axisdir is normal to plane") return false elseif axisdir == linedir1 or axisdir == -linedir1 then Warning("Rotation axisdir is normal to line") return false // Calculate whether axis/plane angle is not more than axis/line angle elseif math.asin(axisdir:Dot(normal)) > math.acos(axisdir:Dot(linedir1)) then Warning("Line cannot be rotated parallel to plane along this axis!") return false end else axisdir = normal:Cross(linedir1):GetNormal() end // Transform everything relative to plane/axis directions local M = { -axisdir:Cross(normal):Cross(normal):GetNormal(), axisdir:Cross(normal):GetNormal(), normal } local MT = matrix_transpose(M) // Now we just have to set Z = 0 after transform local normal2 = Vector(0,0,1) local axisdir2 = matrix_x_vector( M, axisdir ) local linedir2 = matrix_x_vector( M, linedir1 ) // Did the maths, ends up with a quadratic, solving for x component of line // Quadratic formula local a = axisdir2.x^2 - axisdir2.y^2 local c = axisdir2.y^2 - (linedir2:Dot(axisdir2))^2 // 2 solutions local x1 = math.sqrt(-4 * a * c) / (2 * a) local y1 = math.sqrt(1 - x1^2) local y2 = -y1 local x2 = math.sqrt(1 - y2^2) // Find rotation angle given the start/final line directions local function solver(x, y) // Vec is the rotated direction of the line local Vec = Vector(x, y, 0) Vec = matrix_x_vector( MT, Vec ):GetNormal() // Radial line directions around axis local v1 = ( linedir1 - axisdir * (linedir1:Dot(axisdir)) ):GetNormal() local v2 = ( Vec - axisdir * (Vec:Dot(axisdir)) ):GetNormal() local Ang = math.deg(math.acos(math.Clamp( v1:Dot(v2), -1, 1))) -- Warning("x/y: " .. tostring(x) .. " : " .. tostring(y) ) -- Warning("V1/2: " .. tostring(v1) .. " : " .. tostring(v2) ) -- Warning("Vec: " .. tostring(Vec)) -- Warning("Ang: " .. tostring(Ang)) // Handedness is important - determines which way to rotate the vector local Handedness = v1:Dot(v2:Cross(axisdir2)) if Handedness < 0 then Ang = -Ang end return Ang end local Ang = solver(x1, y1) // Determine which way around to rotate if math.abs(Ang) < 0.5 then // Find second solution Ang = solver(x2, y2) end local ang = activeent:GetAngles() ang:RotateAroundAxis( axisdir, Ang ) // Use axis as pivot by default if !pivot and axis then pivot = axis.startpoint end return PA_funcs.rotate_entity( ang, pivot, 0, activeent ) end // Numerical solution -- function PA_funcs.rotate_line_plane_parallel( pivot, axis, lineID, planeID, activeent ) -- local line = PA_funcs.line_global( lineID ) -- local plane = PA_funcs.plane_global( planeID ) -- local linedir1 = ( line.endpoint - line.startpoint ):GetNormal() -- local normal = plane.normal -- local axisdir -- if axis then -- axisdir = ( axis.endpoint - axis.startpoint ):GetNormal() -- if axisdir == normal or axisdir == -normal then -- Warning("Rotation axisdir is normal to plane") -- return false -- elseif axisdir == linedir1 or axisdir == -linedir1 then -- Warning("Rotation axisdir is normal to line") -- return false -- // Calculate whether axis/plane angle is not more than axis/line angle -- elseif math.asin(axisdir:Dot(normal)) > math.acos(axisdir:Dot(linedir1)) then -- Warning("Line cannot be rotated parallel to plane along this axis!") -- return false -- end -- else -- axisdir = normal:Cross(axisdir):GetNormal() -- end -- local dir -- // Max and min angles of rotation -- local ang1 = 0 -- local ang2 = axisdir:Cross(linedir1):Cross(axisdir) -- local count = 0 -- while count < 200 do -- dir_temp = Vector( linedir1.x, linedir1.y, linedir1.z ) -- end -- // Use axis as pivot by default -- if !pivot and axis then -- pivot = axis.startpoint -- end -- return PA_funcs.rotate_entity( ang, pivot, activeent ) -- end // Mirror about selected plane PA_funcs.plane_mirror_entity = function( planeID, activeent ) local plane = PA_funcs.plane_global( planeID ) local origin = plane.origin local normal = plane.normal local shift = LocalPlayer():KeyDown( IN_SPEED ) local stack = 0 if shift then stack = GetConVarNumber( PA_.."stack_num" ) end // Mirror by concommand directly RunConsoleCommand( PA_.. "mirror", tostring(origin.x), tostring(origin.y), tostring(origin.z), tostring(normal.x), tostring(normal.y), tostring(normal.z), tostring(stack) ) Message("Entity mirrored about plane " .. tostring(planeID)) return true end// Tool screen for precision alignment stool (client only) - By Wenli if SERVER then return false end local BGColor = Color(50, 50, 50, 50) local BGColor_Background = Color(103, 100, 110, 255) local BGColor_Display = Color(170, 170, 170, 255) local BGColor_Point = Color(170, 140, 140, 255) local BGColor_Line = Color(140, 140, 170, 255) local BGColor_Plane = Color(140, 170, 140, 255) //surface.CreateFont( "HUDNumber", {60, 400, true, false, "PAToolScreen_Title"} ) surface.CreateFont("PAToolScreen_Title",{font = "HUDNumber",size = 60 , weight = 400 , antialias = true ,additive = false}) //surface.CreateFont( "TabLarge",{ 70, 400, true, false, "PAToolScreen_ToolType"} ) surface.CreateFont("PAToolScreen_ToolType",{font = "TabLarge",size = 70 , weight = 400 , antialias = true ,additive = false}) //surface.CreateFont( "TabLarge",{ 29, 400, true, false, "PAToolScreen_ToolDesc"} ) surface.CreateFont("PAToolScreen_ToolDesc",{font = "TabLarge",size = 29 , weight = 400 , antialias = true ,additive = false}) local ToolType_Current = GetConVarNumber("precision_align_tooltype") local ToolTypeLookup = { [1] = {"Point", "Hitpos"}, [2] = {"Point", "Coordinate Centre"}, [3] = {"Point", "Mass Centre"}, [4] = {"Point", "Bounding Box Centre"}, [5] = {"Line", "Start / End (Alt)"}, [6] = {"Line", "Hitpos + Hitnormal"}, [7] = {"Line", "Hitnormal"}, [8] = {"Plane", "Hitpos + Hitnormal"}, [9] = {"Plane", "Hitnormal"} } local Colour_Current = BGColor_Display local ColourLookup = { [1] = BGColor_Point, [2] = BGColor_Point, [3] = BGColor_Point, [4] = BGColor_Point, [5] = BGColor_Line, [6] = BGColor_Line, [7] = BGColor_Line, [8] = BGColor_Plane, [9] = BGColor_Plane } local function construct_exists( construct_type, ID ) if !construct_type or !ID then return false end if construct_type == "Point" then if precision_align_points[ID].origin then return true end elseif construct_type == "Line" then if precision_align_lines[ID].startpoint and precision_align_lines[ID].endpoint then return true end elseif construct_type == "Plane" then if precision_align_planes[ID].origin and precision_align_planes[ID].normal then return true end end return false end local function GetConstructNum() local ToolNum if ToolType_Current >= 1 and ToolType_Current <= 4 then ToolNum = PA_selected_point elseif ToolType_Current >= 5 and ToolType_Current <= 7 then ToolNum = PA_selected_line elseif ToolType_Current >= 8 and ToolType_Current <= 9 then ToolNum = PA_selected_plane end return ToolNum end // Taken from Garry's tool code local function DrawScrollingText( text, y, texwide ) local w, h = surface.GetTextSize( text ) w = w + 64 local x = math.fmod( CurTime() * 150, w ) * -1 while ( x < texwide ) do surface.SetTextColor( 0, 0, 0, 255 ) surface.SetTextPos( x + 5, y + 5 ) surface.DrawText( text ) surface.SetTextColor( 255, 255, 255, 255 ) surface.SetTextPos( x, y ) surface.DrawText( text ) x = x + w end end local function DrawText_ToolType( y ) surface.SetFont( "PAToolScreen_ToolType" ) surface.SetTextColor( 255, 255, 255, 255 ) local text = ToolTypeLookup[ ToolType_Current ][1] or "-" text = text .. " " .. tostring( GetConstructNum() ) local w, h = surface.GetTextSize( text ) surface.SetTextPos( 125 - w/2, y ) surface.DrawText( text ) end local function DrawText_ToolDesc( y ) surface.SetFont( "PAToolScreen_ToolDesc" ) surface.SetTextColor( 255, 255, 255, 255 ) local text = ToolTypeLookup[ ToolType_Current ][2] or "No tool option selected" local w, h = surface.GetTextSize( text ) surface.SetTextPos( 125 - w/2, y ) surface.DrawText( text ) end local function DrawIndicators( x, y, w ) local radius = 8 local diameter = radius * 2 + 1 local separation = (w - 4) / 9 // Background draw.RoundedBox( 10, x, y, w, diameter + 15, Color(50, 50, 50, 100) ) local xpos = x + radius local ypos = y + radius // Indicators local IndicatorColour local ConstructNum = GetConstructNum() for i = 1, 9 do // Draw construct selection ring if i == ConstructNum then draw.RoundedBox( radius + 4, xpos - 4, ypos - 4, diameter + 8, diameter + 8, Color(255, 255, 255, 255) ) end // Draw indicator status if construct_exists( ToolTypeLookup[ ToolType_Current ][1], i) then IndicatorColour = Color(0, 230, 0, 255) else IndicatorColour = Color(50, 50, 50, 255) end draw.RoundedBox( radius, xpos, ypos, diameter, diameter, IndicatorColour ) xpos = xpos + separation end end // Main Draw Function function PA_DrawToolScreen( w, h ) local r, e = pcall( function() local w = tonumber(w) or 256 local h = tonumber(h) or 256 ToolType_Current = GetConVarNumber("precision_align_tooltype") // Background colour local Colour_Selected = ColourLookup[ ToolType_Current ] or BGColor_Display for k, v in pairs (Colour_Current) do Colour_Current[k] = v + (Colour_Selected[k] - v) / 10 end surface.SetDrawColor( Colour_Current ) surface.DrawRect( 0, 0, w, h ) // Title text / background surface.SetFont( "PAToolScreen_Title" ) local titletext = "Precision Alignment (unofficial)" local textw, texth = surface.GetTextSize( titletext ) surface.SetDrawColor( BGColor_Background ) surface.DrawRect( 0, 10, w, texth + 4 ) DrawScrollingText( titletext, 6, w ) // Lower background box draw.RoundedBox( 10, 10, texth + 22, w - 20, h - texth - 30, BGColor ) // Tool type text DrawText_ToolType( 85 ) DrawText_ToolDesc( 160 ) // Construct indicators DrawIndicators( 10, 212, w - 20 ) end ) if !r then ErrorNoHalt( e, "\n" ) end end// Derma UI for precision alignment stool (client only) - By Wenli if SERVER then return end local PA = "precision_align" local PA_ = PA .. "_" local ent_active = ents.GetByIndex( GetConVarNumber(PA_.."ent_active") ) PA_selected_point = 1 PA_selected_line = 1 PA_selected_plane = 1 PA_activeent = nil include( "weapons/gmod_tool/stools/"..PA.."/manipulation_panel.lua" ) include( "weapons/gmod_tool/stools/"..PA.."/prop_functions.lua" ) local CPanel = controlpanel.Get( PA ) local BGColor = Color(50, 50, 50, 50) local BGColor_Background = Color(103, 100, 110, 255) local BGColor_Disabled = Color(160, 160, 160, 255) local BGColor_Display = Color(170, 170, 170, 255) local BGColor_Point = Color(170, 140, 140, 255) local BGColor_Line = Color(140, 140, 170, 255) local BGColor_Plane = Color(140, 170, 140, 255) local function Warning( text ) if GetConVarNumber( PA_.."display_warnings" ) == 1 then LocalPlayer():ChatPrint( "(PA) ERROR: " .. text ) end end local function AddMenuText( text, x, y, parent ) local Text = vgui.Create( "DLabel", parent ) Text:SetFontInternal("Default") Text:SetText( text ) Text:SizeToContents() Text:SetPos( x, y ) return Text end local function play_sound_true() LocalPlayer():EmitSound("buttons/button15.wav", 100, 100) end local function play_sound_false() LocalPlayer():EmitSound("buttons/lightswitch2.wav", 100, 100) end //********************************************************************************************************************// // Custom Derma Controls //********************************************************************************************************************// /*--------------------------------------------------------- Name: Stack_Num_Request Popup window to request stack_num ---------------------------------------------------------*/ local STACK_POPUP = {} function STACK_POPUP:Init() self:SetSize( 300, 150 ) self:Center() self:SetTitle( "Precision Alignment Multi-Stack Settings" ) self:ShowCloseButton( true ) self:SetDraggable( false ) self:SetBackgroundBlur( true ) self:SetDrawOnTop( true ) self.text_stackamount = vgui.Create( "DLabel", self ) self.text_stackamount:SetText( "Stack Amount:" ) self.text_stackamount:SizeToContents() self.text_stackamount:SetContentAlignment( 8 ) self.text_stackamount:SetTextColor( color_white ) self.text_stackamount:StretchToParent( 5, 40, 5, 5 ) self.slider_stackamount = vgui.Create( "DNumSlider", self ) self.slider_stackamount:StretchToParent( 10, nil, 10, nil ) self.slider_stackamount:AlignTop( 45 ) self.slider_stackamount:SetText( "" ) self.slider_stackamount:SetMinMax( 1, 20 ) self.slider_stackamount:SetDecimals( 0 ) self.slider_stackamount:SetValue( GetConVarNumber( PA_.."stack_num" ) ) self.slider_stackamount.Text = self.slider_stackamount:GetTextArea() self.slider_stackamount.Text.OnEnter = function() self.button_ok:DoClick() end self.slider_stackamount.Text:RequestFocus() self.checkbox_nocollide = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_nocollide:SetText( "Nocollide" ) self.checkbox_nocollide:SetToolTip( "Nocollide each stacked entity with the next" ) self.checkbox_nocollide:SizeToContents() self.checkbox_nocollide:AlignBottom( 45 ) self.checkbox_nocollide:AlignLeft( 10 ) self.checkbox_nocollide:SetValue( GetConVarNumber( PA_.."stack_nocollide" ) ) self.button_ok = vgui.Create( "DButton", self ) self.button_ok:SetText( "OK" ) self.button_ok:SizeToContents() self.button_ok:SetSize( 80, 25 ) self.button_ok:AlignLeft( 5 ) self.button_ok:AlignBottom( 5 ) self.button_ok.DoClick = function() RunConsoleCommand( PA_.."stack_num", tostring( math.Clamp(self.slider_stackamount:GetValue(), 1, 20) ) ) local nocollide = 0 if self.checkbox_nocollide:GetChecked() then nocollide = 1 end RunConsoleCommand( PA_.."stack_nocollide", tostring( nocollide ) ) self:Close() end self.button_cancel = vgui.Create( "DButton", self ) self.button_cancel:SetText( "Cancel" ) self.button_cancel:SizeToContents() self.button_cancel:SetSize( 80, 25 ) self.button_cancel:SetPos( 5, 5 ) self.button_cancel.DoClick = function() self:Close() end self.button_cancel:AlignRight( 5 ) self.button_cancel:AlignBottom( 5 ) self:MakePopup() self:DoModal() end function STACK_POPUP:Paint( w, h) if ( self.m_bBackgroundBlur ) then Derma_DrawBackgroundBlur( self, self.m_fCreateTime ) end local width, height = self:GetSize() draw.RoundedBox(6, 0, 0, width, 25, BGColor_Display) draw.RoundedBox(6, 2, 2, width - 4, 21, BGColor_Background) draw.RoundedBox(6, 0, 25, width, height - 25, Color(0, 0, 0, 255)) draw.RoundedBox(6, 1, 26, width - 2, height - 27, BGColor_Background ) end vgui.Register("PA_Stack_Popup", STACK_POPUP, "DFrame") /*--------------------------------------------------------- Name: PA_Construct_ListView Standard construct list ---------------------------------------------------------*/ local CONSTRUCT_LISTVIEW = {} function CONSTRUCT_LISTVIEW:Init() self:SetSize(110, 169) --self:SetSortable(false) end function CONSTRUCT_LISTVIEW:Text( title, text ) self.construct_type = text self:AddColumn( "" .. title) for i = 1, 9 do local line = self:AddLine(text .. " " .. tostring(i)) line.indicator = vgui.Create( "PA_Indicator", line ) end // Format header local Header = self.Columns[1].Header Header:SetFont("DefaultBold") Header:SetContentAlignment( 5 ) end function CONSTRUCT_LISTVIEW:SetIndicators() for i = 1, 9 do local line = self:GetLine(i) line.indicator = vgui.Create( "PA_Indicator", line ) end end function CONSTRUCT_LISTVIEW:SetIndicatorOffset( offset ) for i = 1, 9 do local indicator = self:GetLine(i).indicator indicator.offset = offset end end vgui.Register("PA_Construct_ListView", CONSTRUCT_LISTVIEW, "DListView") // Indicator to tell whether construct is defined local INDICATOR = {} function INDICATOR:Init() self.offset = 0 end function INDICATOR:PerformLayout() local width, height = self:GetParent():GetWide() - 5 - self.offset, self:GetParent():GetTall() - 4 self:SetSize( height, height ) self:SetPos( width - height, 2 ) end function INDICATOR:Paint(w,h) local textbox = self:GetParent() if PA_funcs.construct_exists(textbox:GetListView().construct_type, textbox:GetID()) then draw.RoundedBox( 6, 0, 0, self:GetWide(), self:GetTall(), Color(0,230,0,255) ) end end vgui.Register("PA_Indicator", INDICATOR, "DPanel") /*--------------------------------------------------------- Name: PA_XYZ_Sliders XYZ slider control for vector input/output ---------------------------------------------------------*/ local XYZ_SLIDER = {} function XYZ_SLIDER:Init() --self:SetSize(130, 100) -- Keep the second number at 100 --self:SetWide(200) self:SetMinMax( -50000, 50000 ) self:SetDecimals( 3 ) self:SetValue( 0 ) // This is so we can identify the slider belongs to PA, so we can hook keyboard focus below self:GetTextArea().Type = "PA" end vgui.Register("PA_XYZ_Slider", XYZ_SLIDER, "DNumSlider") local text = {} function text:Init() self:SetSize(130,100) end vgui.Register("PA_Temp_textbox", text, "DLabel") // These hooks allow the slider text boxes to steal keyboard focus local function TextFocusOn( pnl ) if pnl:GetClassName() == "TextEntry" and pnl.Type == "PA" then PA_manipulation_panel:SetKeyBoardInputEnabled( true ) end end hook.Add( "OnTextEntryGetFocus", "PAKeyboardFocusOn", TextFocusOn ) local function TextFocusOff( pnl ) if pnl:GetClassName() == "TextEntry" and pnl.Type == "PA" then PA_manipulation_panel:SetKeyBoardInputEnabled( false ) end end hook.Add( "OnTextEntryLoseFocus", "PAKeyboardFocusOff", TextFocusOff ) local XYZ_SLIDERS = {} function XYZ_SLIDERS:Init() self.slider_x = vgui.Create( "PA_XYZ_Slider", self ) self.slider_y = vgui.Create( "PA_XYZ_Slider", self ) self.slider_z = vgui.Create( "PA_XYZ_Slider", self ) self.slider_x.Label:SetWide( 5 ) self.slider_y.Label:SetWide( 5 ) self.slider_z.Label:SetWide( 5 ) self.slider_x:SetText("X") self.slider_y:SetText("Y") self.slider_z:SetText("Z") end function XYZ_SLIDERS:PerformLayout() local w, h = self:GetWide(), self:GetTall() - 10 local x = 0 local y1 = 0 local y2 = h / 2 - 20 local y3 = h - 40 local width, height = w, 100 self.slider_x:SetWide( width ) --:SetSize(width, height) self.slider_y:SetWide( width ) --:SetSize(width, height) self.slider_z:SetWide( width ) --:SetSize(width, height) self.slider_x:SetPos(x, y1) self.slider_y:SetPos(x, y2) self.slider_z:SetPos(x, y3) end function XYZ_SLIDERS:GetValues() local x, y, z x = self.slider_x:GetValue() y = self.slider_y:GetValue() z = self.slider_z:GetValue() return Vector( x, y, z ) end function XYZ_SLIDERS:SetValues( vec ) self.slider_x:SetValue(vec.x) self.slider_y:SetValue(vec.y) self.slider_z:SetValue(vec.z) end function XYZ_SLIDERS:SetRange( x ) self.slider_x:SetMinMax( -x, x ) self.slider_y:SetMinMax( -x, x ) self.slider_z:SetMinMax( -x, x ) end function XYZ_SLIDERS:Paint(w,h) end vgui.Register("PA_XYZ_Sliders", XYZ_SLIDERS, "DPanel") /*--------------------------------------------------------- Name: PA_Function_Button Standard button that can be assigned a function ---------------------------------------------------------*/ local FUNCTION_BUTTON = {} function FUNCTION_BUTTON:Init() self:SetSize(200, 25) end function FUNCTION_BUTTON:SetFunction( func ) self.DoClick = function() local ret = func() if ret == true then play_sound_true() elseif ret == false then play_sound_false() end end end vgui.Register("PA_Function_Button", FUNCTION_BUTTON, "DButton") /*--------------------------------------------------------- Name: PA_Function_Button_2 Construct functions selection button ---------------------------------------------------------*/ local function_buttons_2_list = {} local FUNCTION_BUTTON_2 = {} function FUNCTION_BUTTON_2:Init() self:SetSize(130, 25) table.insert(function_buttons_2_list, self) self.DoClick = function() local T = self.selections local tab = self:GetParent() tab.activebutton = self tab:UpdateDescription() tab.list_point_primary:SetVisible(false) tab.list_line_primary:SetVisible(false) tab.list_plane_primary:SetVisible(false) if T[1] == "Point" then tab.colour_panel_1:SetColour(BGColor_Point) tab.list_point_primary:SetVisible(true) elseif T[1] == "Line" then tab.colour_panel_1:SetColour(BGColor_Line) tab.list_line_primary:SetVisible(true) elseif T[1] == "Plane" then tab.colour_panel_1:SetColour(BGColor_Plane) tab.list_plane_primary:SetVisible(true) else tab.colour_panel_1:SetColour(BGColor_Disabled) end if not (T[2] == 0) then //tab.point_text:SetText( "Select " .. tostring(T[2]) ) tab.colour_panel_2:SetColour(BGColor_Point) tab.list_point_secondary:SetVisible(true) else //tab.point_text:SetText( "" ) tab.colour_panel_2:SetColour(BGColor_Disabled) tab.list_point_secondary:SetVisible(false) end if T[3] ~= 0 then //tab.line_text:SetText( "Select " .. tostring(T[3]) ) tab.colour_panel_3:SetColour(BGColor_Line) tab.list_line_secondary:SetVisible(true) else //tab.line_text:SetText( "" ) tab.colour_panel_3:SetColour(BGColor_Disabled) tab.list_line_secondary:SetVisible(false) end if T[4] ~= 0 then //tab.plane_text:SetText( "Select " .. tostring(T[4]) ) tab.colour_panel_4:SetColour(BGColor_Plane) tab.list_plane_secondary:SetVisible(true) else //tab.plane_text:SetText( "" ) tab.colour_panel_4:SetColour(BGColor_Disabled) tab.list_plane_secondary:SetVisible(false) end end end // Override mouse functions (make it into a toggle button) function FUNCTION_BUTTON_2:OnMousePressed( mousecode ) if !self.Depressed then // pop up any previously depressed buttons for k, v in pairs (function_buttons_2_list) do if v.Depressed then v.Depressed = false end end self.Depressed = true return self.DoClick() end end function FUNCTION_BUTTON_2:OnMouseReleased( mousecode ) end vgui.Register("PA_Function_Button_2", FUNCTION_BUTTON_2, "DButton") /*--------------------------------------------------------- Name: PA_Function_Button_3 Move Constructs selection button ---------------------------------------------------------*/ local function_buttons_3_list = {} local FUNCTION_BUTTON_3 = {} function FUNCTION_BUTTON_3:Init() self:SetSize(110, 25) table.insert(function_buttons_3_list, self) self.DoClick = function() local T = self.selections local tab = self:GetParent() tab.activebutton = self tab.list_point_1:SetVisible(false) tab.list_point_2:SetVisible(false) tab.list_line_1:SetVisible(false) tab.list_plane_1:SetVisible(false) if T == "Point" then tab.colour_panel_1:SetColour(BGColor_Point) tab.colour_panel_2:SetColour(BGColor_Point) tab.list_point_1:SetVisible(true) tab.list_point_2:SetVisible(true) elseif T == "Line" then tab.colour_panel_1:SetColour(BGColor_Disabled) tab.colour_panel_2:SetColour(BGColor_Line) tab.list_line_1:SetVisible(true) elseif T == "Plane" then tab.colour_panel_1:SetColour(BGColor_Disabled) tab.colour_panel_2:SetColour(BGColor_Plane) tab.list_plane_1:SetVisible(true) else tab.colour_panel_1:SetColour(BGColor_Disabled) tab.colour_panel_2:SetColour(BGColor_Disabled) end end end // Override mouse functions (make it into a toggle button) function FUNCTION_BUTTON_3:OnMousePressed( mousecode ) if !self.Depressed then // pop up any previously depressed buttons for k, v in pairs (function_buttons_3_list) do if v.Depressed then v.Depressed = false end end self.Depressed = true return self.DoClick() end end function FUNCTION_BUTTON_3:OnMouseReleased( mousecode ) end vgui.Register("PA_Function_Button_3", FUNCTION_BUTTON_3, "DButton") /*--------------------------------------------------------- Name: PA_Function_Button_Rotation Rotation Functions selection button ---------------------------------------------------------*/ local rotation_function_buttons_list = {} local FUNCTION_BUTTON_ROTATION = {} function FUNCTION_BUTTON_ROTATION:Init() self:SetSize(168, 25) table.insert(rotation_function_buttons_list, self) self.DoClick = function() local tab = self:GetParent() tab.activebutton = self tab:UpdateDescription() // Optional pivot point / axis selections if self.options[1] ~= 0 then tab.list_pivotpoint:SetVisible(true) else tab.list_pivotpoint:SetVisible(false) end if self.options[2] ~= 0 then tab.list_line_axis:SetVisible(true) else tab.list_line_axis:SetVisible(false) end // Main function selections if self.selections[1] ~= 0 then tab.colour_panel_1:SetColour(BGColor_Line) tab.list_line_1:SetVisible(true) else tab.colour_panel_1:SetColour(BGColor_Disabled) tab.list_line_1:SetVisible(false) end if self.selections[2] ~= 0 then tab.colour_panel_2:SetColour(BGColor_Line) tab.list_line_2:SetVisible(true) else tab.colour_panel_2:SetColour(BGColor_Disabled) tab.list_line_2:SetVisible(false) end if self.selections[3] ~= 0 then tab.colour_panel_3:SetColour(BGColor_Plane) tab.list_plane_1:SetVisible(true) else tab.colour_panel_3:SetColour(BGColor_Disabled) tab.list_plane_1:SetVisible(false) end if self.selections[4] ~= 0 then tab.colour_panel_4:SetColour(BGColor_Plane) tab.list_plane_2:SetVisible(true) else tab.colour_panel_4:SetColour(BGColor_Disabled) tab.list_plane_2:SetVisible(false) end end end // Override mouse functions (make it into a toggle button) function FUNCTION_BUTTON_ROTATION:OnMousePressed( mousecode ) if !self.Depressed then // pop up any previously depressed buttons for k, v in pairs (rotation_function_buttons_list) do if v.Depressed then v.Depressed = false end end self.Depressed = true return self.DoClick() end end function FUNCTION_BUTTON_ROTATION:OnMouseReleased( mousecode ) end vgui.Register("PA_Function_Button_Rotation", FUNCTION_BUTTON_ROTATION, "DButton") /*--------------------------------------------------------- Name: Standard manipulation window buttons ---------------------------------------------------------*/ local ZERO_BUTTON = {} function ZERO_BUTTON:Init() self:SetSize(20, 20) self:SetText( "0" ) self:SetToolTip( "Set slider values to 0" ) end function ZERO_BUTTON:SetSliders( panel ) self:SetFunction( function() panel:SetValues( Vector(0,0,0) ) return true end ) end vgui.Register("PA_Zero_Button", ZERO_BUTTON, "PA_Function_Button") local NEGATE_BUTTON = {} function NEGATE_BUTTON:Init() self:SetSize(20, 20) self:SetText( "-" ) self:SetToolTip( "Negate slider values" ) end function NEGATE_BUTTON:SetSliders( panel ) self:SetFunction( function() local v = panel:GetValues() panel:SetValues( Vector(-v.x, -v.y, -v.z) ) return true end ) end vgui.Register("PA_Negate_Button", NEGATE_BUTTON, "PA_Function_Button") local COPY_CLIPBOARD_BUTTON = {} function COPY_CLIPBOARD_BUTTON:Init() self:SetSize(40, 20) self:SetText( "Copy" ) self:SetToolTip( "Copy values to clipboard" ) end function COPY_CLIPBOARD_BUTTON:SetSliders( panel ) self:SetFunction( function() local v = panel:GetValues() // Format string for better use with E2 local x = tostring( math.Round(v.x * 1000) / 1000 ) local y = tostring( math.Round(v.y * 1000) / 1000 ) local z = tostring( math.Round(v.z * 1000) / 1000 ) local str = x .. ", " .. y .. ", " .. z SetClipboardText( str ) return true end ) end vgui.Register("PA_Copy_Clipboard_Button", COPY_CLIPBOARD_BUTTON, "PA_Function_Button") local UPDATE_BUTTON = {} function UPDATE_BUTTON:Init() self:SetSize(90, 25) self:SetText( "Update" ) self:SetToolTip( "Update other sliders according to these values" ) end vgui.Register("PA_Update_Button", UPDATE_BUTTON, "PA_Function_Button") local COPY_BUTTON = {} function COPY_BUTTON:Init() self:SetSize(26, 25) self:SetText( "=" ) self:SetToolTip( "Set primary value equal to secondary" ) end vgui.Register("PA_Copy_Button", COPY_BUTTON, "PA_Function_Button") local ADD_BUTTON = {} function ADD_BUTTON:Init() self:SetSize(26, 25) self:SetText( "+" ) self:SetToolTip( "Add this value to primary value" ) end vgui.Register("PA_Add_Button", ADD_BUTTON, "PA_Function_Button") local SUBTRACT_BUTTON = {} function SUBTRACT_BUTTON:Init() self:SetSize(26, 25) self:SetText( "-" ) self:SetToolTip( "Subtract this value from primary value" ) end vgui.Register("PA_Subtract_Button", SUBTRACT_BUTTON, "PA_Function_Button") local COPY_RIGHT_BUTTON = {} function COPY_RIGHT_BUTTON:Init() self:SetSize(26, 25) self:SetText( ">" ) self:SetToolTip( "Copy value across to secondary slider" ) end vgui.Register("PA_Copy_Right_Button", COPY_RIGHT_BUTTON, "PA_Function_Button") local COPY_LEFT_BUTTON = {} function COPY_LEFT_BUTTON:Init() self:SetSize(26, 25) self:SetText( "<" ) self:SetToolTip( "Copy value across to primary slider" ) end vgui.Register("PA_Copy_Left_Button", COPY_LEFT_BUTTON, "PA_Function_Button") local MOVE_BUTTON = {} function MOVE_BUTTON:SetFunction( func ) self.DoClick = function() // Alt to bring up stack number query local alt = LocalPlayer():KeyDown( IN_WALK ) if alt then vgui.Create( "PA_Stack_Popup" ) return end local ret = func() if ret == true then play_sound_true() elseif ret == false then play_sound_false() end end end function MOVE_BUTTON:Think() if IsValid(PA_activeent) and self:GetDisabled() then self:SetDisabled(false) elseif !IsValid(PA_activeent) and !self:GetDisabled() then self:SetDisabled(true) end end vgui.Register("PA_Move_Button", MOVE_BUTTON, "DButton") /*--------------------------------------------------------- Name: PA_Colour_Panel Common background panel for listviews ---------------------------------------------------------*/ local COLOUR_PANEL = {} function COLOUR_PANEL:Init() self.colour = table.Copy( BGColor_Disabled ) self.setcolour = BGColor_Disabled local parent = self:GetParent() self:SetSize( 150, parent:GetTall()/2 - 15 ) end function COLOUR_PANEL:SetColour( colour ) self.setcolour = colour end function COLOUR_PANEL:Paint(w,h) for k, v in pairs (self.colour) do self.colour[k] = v + (self.setcolour[k] - v)/10 end draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), self.colour) draw.RoundedBox(6, 5, 15, self:GetWide() - 10, self:GetTall() - 20, BGColor) end vgui.Register("PA_Colour_Panel", COLOUR_PANEL, "DPanel") /*--------------------------------------------------------- Name: Constraints Panels Standard panels for constraints tab ---------------------------------------------------------*/ local CONSTRAINT_TITLE_TEXT = {} function CONSTRAINT_TITLE_TEXT:Init() self:SetSize( self:GetParent():GetWide(), 15 ) self:SetFontInternal("Default") self:SetContentAlignment(2) end vgui.Register("PA_Constraint_Title_Text", CONSTRAINT_TITLE_TEXT, "DLabel") local CONSTRAINTS_SHEET = {} function CONSTRAINTS_SHEET:Paint(w,h) draw.RoundedBox(6, 0, 0, self:GetWide(), self:GetTall(), Color(140,140,140,255)) end // Taken from gamemodes/sandbox/gamemode/spawnmenu/controls/control_presets.lua function CONSTRAINTS_SHEET:AddComboBox( data ) local data = table.LowerKeyNames( data ) local ctrl = vgui.Create( "ControlPresets", self ) ctrl:SetPreset( data.folder ) if ( data.options ) then for k, v in pairs( data.options ) do if ( k != "id" ) then // Some txt file configs still have an `ID'. But these are redundant now. ctrl:AddOption( k, v ) end end end if ( data.cvars ) then for k, v in pairs( data.cvars ) do ctrl:AddConVar( v ) end end ctrl:SetWide(300) return ctrl end vgui.Register("PA_Constraints_Sheet", CONSTRAINTS_SHEET, "DPanel") local CONSTRAINT_SLIDER = {} function CONSTRAINT_SLIDER:Init() -- self:SetSize(130, 100) -- Keep the second number at 100 self.Label:SetSize( 100 ) self:SetMinMax( 0, 50000 ) end // Base this off PA_XYZ_Slider so the keyboard hook functions apply vgui.Register("PA_Constraint_Slider", CONSTRAINT_SLIDER, "PA_XYZ_Slider") /*--------------------------------------------------------- Name: PA_ColourCircle HSV Colour selection wheel for displays tab ---------------------------------------------------------*/ local COLOUR_CIRCLE = {} function COLOUR_CIRCLE:Init() local H = GetConVarNumber( PA_.."attachcolour_h" ) local S = GetConVarNumber( PA_.."attachcolour_s" ) self:SetColor( H,S ) end function COLOUR_CIRCLE:TranslateValues( x, y ) // Modified version of default TranslateValues function - so it won't print to console all the damn time x = x - 0.5 y = y - 0.5 local angle = math.atan2( x, y ) local length = math.sqrt( x*x + y*y ) length = math.Clamp( length, 0, 0.5 ) x = 0.5 + math.sin( angle ) * length y = 0.5 + math.cos( angle ) * length self.H = math.deg( angle ) + 270 self.S = length * 2 self:OnChange( self.H, self.S ) return x, y end function COLOUR_CIRCLE:GetColor() return self.H, self.S end function COLOUR_CIRCLE:SetColor( H, S ) self.H, self.S = H, S local x, y local length = S / 2 local angle = math.rad( H - 270 ) local x = 0.5 + math.sin( angle ) * length local y = 0.5 + math.cos( angle ) * length //self:SetSlideX( x ) //self:SetValue( y ) self:OnChange( self.H, self.S ) return x, y end function COLOUR_CIRCLE:OnChange( H, S ) // Overwrite in main body end vgui.Register("PA_ColourCircle", COLOUR_CIRCLE, "DColorCube") /*--------------------------------------------------------- Name: PA_ColourControl Full HSV Colour selection display for displays tab ---------------------------------------------------------*/ local HSV_COLOUR_CONTROL = {} function HSV_COLOUR_CONTROL:Init() self:SetSize(300, 200) self.ColorMixer = vgui.Create( "DColorMixer", self ) self.ColorMixer:SetPos( 0, 35 ) self.ColorMixer:SetSize( 250, 150 ) self.ColorMixer:SetPalette( false ) self.ColorMixer.ValueChanged = function( panel, color ) local H, S, V = ColorToHSV( color ) local A = color.a or 255 -- PrintTable( color ) -- print( tostring( color ) ) -- PrintTable( self.ColorMixer:GetColor() ) -- print( "changed: " .. H .. ", " .. S .. ", " .. V .. ", " .. A ) RunConsoleCommand( self.convar .. "_h", H ) RunConsoleCommand( self.convar .. "_s", S ) RunConsoleCommand( self.convar .. "_v", V ) RunConsoleCommand( self.convar .. "_a", A ) end /* self.ColourCircle = vgui.Create("PA_ColourCircle", self) self.ColourCircle:SetPos(0, 35) self.ColourCircle:SetSize(150, 150) self.ColourCircle.OnChange = function( panel, H, S ) local colour_brightness = HSVToColor( H, S, 1 ) self.Bar_Brightness:SetFGColor( colour_brightness ) local V = self.Bar_Brightness:GetColor() local colour_alpha = HSVToColor( H, S, V ) self.Bar_Alpha:SetFGColor( colour_alpha ) RunConsoleCommand( self.convar .. "_h", H ) RunConsoleCommand( self.convar .. "_s", S ) end self.Bar_Brightness = vgui.Create( "DAlphaBar", self ) -- self.Bar_Brightness:SetBGColor( "vgui/hsv-brightness" ) self.Bar_Brightness:SetPaintBackground( "vgui/hsv-brightness" ) self.Bar_Brightness:SetPos(160, 45) self.Bar_Brightness:SetSize(25, 130) self.Bar_Brightness.GetColor = function() return 1 - self.Bar_Brightness:GetValue() end self.Bar_Brightness.SetColor = function( panel, V ) self.Bar_Brightness:SetValue( 1 - V ) self.Bar_Brightness:OnChange( V * 255 ) end self.Bar_Brightness.OnChange = function( panel, brightness ) local V = brightness / 255 local H, S = self.ColourCircle:GetColor() local colour_alpha = HSVToColor( H, S, V ) self.Bar_Alpha:SetFGColor( colour_alpha ) RunConsoleCommand( self.convar .. "_v", V ) end // Remove default alpha bar background image --[[ self.Bar_Brightness.PerformLayout = function() DSlider.PerformLayout( self.Bar_Brightness ) end self.Bar_Brightness.imgBackground:Remove() ]] AddMenuText( "HSV", 130, 182, self ) self.Bar_Alpha = vgui.Create( "DAlphaBar", self ) self.Bar_Alpha:SetPos(210, 45) self.Bar_Alpha:SetSize(25, 130) self.Bar_Alpha.GetColor = function() return ( 1 - self.Bar_Alpha:GetValue() ) * 255 end self.Bar_Alpha.SetColor = function( panel, alpha ) self.Bar_Alpha:SetValue( 1 - alpha / 255 ) self.Bar_Alpha:OnChange( alpha ) end self.Bar_Alpha.OnChange = function( panel, alpha ) RunConsoleCommand( self.convar .. "_a", alpha ) end AddMenuText( "Alpha", 207, 182, self ) */ self.button_defaults = vgui.Create( "PA_Function_Button", self ) self.button_defaults:SetPos(200, 5) self.button_defaults:SetSize(90, 25) self.button_defaults:SetText( "Reset Defaults" ) self.button_defaults:SetFunction( function() self:SetColor( self.default_H, self.default_S, self.default_V, self.default_A ) return true end ) end function HSV_COLOUR_CONTROL:SetConVar( convar ) self.convar = convar end function HSV_COLOUR_CONTROL:SetDefaults( H, S, V, A ) self.default_H = H self.default_S = S self.default_V = V self.default_A = A end function HSV_COLOUR_CONTROL:SetColor( H, S, V, A ) local col = HSVToColor( H, S, V ) col.a = A self.ColorMixer:SetColor( col ) -- self.ColourCircle:SetColor( H, S ) -- self.Bar_Brightness:SetColor( V ) -- self.Bar_Alpha:SetColor( A ) end function HSV_COLOUR_CONTROL:GetColor() local col = self.ColorMixer:GetColor() local H, S, V = ColorToHSV( col ) local A = col.a -- return self.ColorMixer:GetColor() -- local H, S = self.ColourCircle:GetColor() -- local V = self.Bar_Brightness:GetColor() -- local A = self.Bar_Alpha:GetColor() return H, S, V, A end function HSV_COLOUR_CONTROL:Paint( w, h ) end vgui.Register("PA_ColourControl", HSV_COLOUR_CONTROL, "DPanel") /*--------------------------------------------------------- Name: PA_Construct_Multiselect Multi construct selection for displays/move constructs tabs ---------------------------------------------------------*/ local CONSTRUCT_MULTISELECT = {} function CONSTRUCT_MULTISELECT:Init() self:SetSize(555, 215) self.colour_panel_1 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_1:SetPos(0, 0) self.colour_panel_1:SetSize(150, 215) self.colour_panel_1:SetColour( BGColor_Point ) self.list_points = vgui.Create( "PA_Construct_ListView", self.colour_panel_1 ) self.list_points:Text( "Points", "Point", self.colour_panel_1 ) self.list_points:SetToolTip( "Double click to deselect" ) self.list_points:SetPos(20, 30) self.list_points:SetMultiSelect(true) self.list_points.DoDoubleClick = function( Line, LineID ) self.list_points:ClearSelection() end self.colour_panel_2 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_2:SetPos(150, 0) self.colour_panel_2:SetSize(150, 215) self.colour_panel_2:SetColour( BGColor_Line ) self.list_lines = vgui.Create( "PA_Construct_ListView", self.colour_panel_2 ) self.list_lines:Text( "Lines", "Line", self.colour_panel_2 ) self.list_lines:SetToolTip( "Double click to deselect" ) self.list_lines:SetPos(20, 30) self.list_lines:SetMultiSelect(true) self.list_lines.DoDoubleClick = function( Line, LineID ) self.list_lines:ClearSelection() end self.colour_panel_3 = vgui.Create( "PA_Colour_Panel", self ) self.colour_panel_3:SetPos(300, 0) self.colour_panel_3:SetSize(150, 215) self.colour_panel_3:SetColour( BGColor_Plane ) self.list_planes = vgui.Create( "PA_Construct_ListView", self.colour_panel_3 ) self.list_planes:Text( "Planes", "Plane", self.colour_panel_3 ) self.list_planes:SetToolTip( "Double click to deselect" ) self.list_planes:SetPos(20, 30) self.list_planes:SetMultiSelect(true) self.list_planes.DoDoubleClick = function( Line, LineID ) self.list_planes:ClearSelection() end self.button_selectall = vgui.Create( "PA_Function_Button", self ) self.button_selectall:SetPos(462, 30) self.button_selectall:SetSize(80, 30) self.button_selectall:SetText( "Select All" ) self.button_selectall:SetToolTip( "Select all constructs" ) self.button_selectall:SetFunction( function() self:SelectAll( true ) return true end ) self.button_deselectall = vgui.Create( "PA_Function_Button", self ) self.button_deselectall:SetPos(462, 65) self.button_deselectall:SetSize(80, 30) self.button_deselectall:SetText( "Deselect All" ) self.button_deselectall:SetToolTip( "Deselect all constructs" ) self.button_deselectall:SetFunction( function() self:SelectAll( false ) return true end ) self.button_attach = vgui.Create( "PA_Function_Button", self ) self.button_attach:SetPos(462, 100) self.button_attach:SetSize(80, 30) self.button_attach:SetText( "Attach" ) self.button_attach:SetToolTip( "Attach constructs to the selected entity (detach if no ent selected)" ) self.button_attach:SetFunction( function() for k, v in pairs( self.list_points:GetSelected() ) do ID = v:GetID() if PA_funcs.construct_exists( "Point", ID ) then PA_funcs.attach_point( ID, PA_activeent ) end end for k, v in pairs( self.list_lines:GetSelected() ) do ID = v:GetID() if PA_funcs.construct_exists( "Line", ID ) then PA_funcs.attach_line( ID, PA_activeent ) end end for k, v in pairs( self.list_planes:GetSelected() ) do ID = v:GetID() if PA_funcs.construct_exists( "Plane", ID ) then PA_funcs.attach_plane( ID, PA_activeent ) end end return true end ) self.button_delete = vgui.Create( "PA_Function_Button", self ) self.button_delete:SetPos(462, 135) self.button_delete:SetSize(80, 30) self.button_delete:SetText( "Delete" ) self.button_delete:SetToolTip( "Delete the selected constructs" ) self.button_delete:SetFunction( function() local ID for k, v in pairs( self.list_points:GetSelected() ) do ID = v:GetID() PA_funcs.delete_point( ID ) end for k, v in pairs( self.list_lines:GetSelected() ) do ID = v:GetID() PA_funcs.delete_line( ID ) end for k, v in pairs( self.list_planes:GetSelected() ) do ID = v:GetID() PA_funcs.delete_plane( ID ) end return true end ) self.button_deleteall = vgui.Create( "PA_Function_Button", self ) self.button_deleteall:SetPos(462, 170) self.button_deleteall:SetSize(80, 30) self.button_deleteall:SetText( "Delete All" ) self.button_deleteall:SetToolTip( "Delete all existing constructs" ) self.button_deleteall:SetFunction( function() PA_funcs.delete_points() PA_funcs.delete_lines() PA_funcs.delete_planes() return true end ) end function CONSTRUCT_MULTISELECT:SelectAll( value ) local function SelectLines( value, panel, construct_table ) for id = 1, 9 do local line = panel.Sorted[ id ] line:SetSelected( value ) if self.visibility then construct_table[id].visible = value end end end SelectLines( value, self.list_points, precision_align_points ) SelectLines( value, self.list_lines, precision_align_lines ) SelectLines( value, self.list_planes, precision_align_planes ) end function CONSTRUCT_MULTISELECT:GetSelection() local selection = {} selection.points = {} selection.lines = {} selection.planes = {} for k, v in pairs( self.list_points:GetSelected() ) do local ID = v:GetID() if PA_funcs.construct_exists( "Point", ID ) then table.insert( selection.points, ID ) end end for k, v in pairs( self.list_lines:GetSelected() ) do local ID = v:GetID() if PA_funcs.construct_exists( "Line", ID ) then table.insert( selection.lines, ID ) end end for k, v in pairs( self.list_planes:GetSelected() ) do local ID = v:GetID() if PA_funcs.construct_exists( "Plane", ID ) then table.insert( selection.planes, ID ) end end if ( #selection.points + #selection.lines + #selection.planes ) == 0 then selection = nil end return selection end function CONSTRUCT_MULTISELECT:Paint(w,h) end vgui.Register("PA_Construct_Multiselect", CONSTRUCT_MULTISELECT, "DPanel") //********************************************************************************************************************// // Custom CPanel Functions //********************************************************************************************************************// // Reduce width for lower resolutions to compensate for side scrollbar local CPanel_Width if ScrH() < 1050 then CPanel_Width = 281 --265 else CPanel_Width = 297 --281 end local function create_buttons_standard( panel, text ) panel.button_view = vgui.Create( "PA_Function_Button", panel ) panel.button_view:SetPos(0, 120) panel.button_view:SetSize(CPanel_Width/2, 20) panel.button_view:SetText( "View" ) panel.button_view:SetToolTip( "View the selected " .. text ) panel.button_delete = vgui.Create( "PA_Function_Button", panel ) panel.button_delete:SetPos(CPanel_Width/2, 120) panel.button_delete:SetSize(CPanel_Width/2, 20) panel.button_delete:SetText( "Delete" ) panel.button_delete:SetToolTip( "Delete the selected " .. text ) panel.button_attach = vgui.Create( "PA_Function_Button", panel ) panel.button_attach:SetPos(0, 140) panel.button_attach:SetSize(CPanel_Width/2, 20) panel.button_attach:SetText( "Attach" ) panel.button_attach:SetToolTip( "Attach " .. text .. " to selected entity (detach if no ent selected)" ) panel.button_deleteall = vgui.Create( "PA_Function_Button", panel ) panel.button_deleteall:SetPos(CPanel_Width/2, 140) panel.button_deleteall:SetSize(CPanel_Width/2, 20) panel.button_deleteall:SetText( "Delete All" ) panel.button_deleteall:SetToolTip( "Delete all " .. text .. "s" ) end //********************************************************************************************************************// // CPanel Controls //********************************************************************************************************************// // Open a particular tab in the manipulation panel local function Open_Manipulation_Tab( Tab ) PA_manipulation_panel.panel:SetActiveTab( Tab ) if !PA_manipulation_panel:IsVisible() then PA_manipulation_panel:SetVisible(true) end end // Perform double click function on a listview within the manipulation panel local function Listview_DoDoubleClick( panel, LineID ) panel:ClearSelection() local Line = panel:GetLine( LineID ) panel:SelectItem( Line ) panel:DoDoubleClick( Line, LineID ) end local TOOL_POINT_PANEL = {} function TOOL_POINT_PANEL:Init() AddMenuText("POINT", CPanel_Width/2 - 18, 0, self) self.list_primarypoint = vgui.Create("PA_Construct_ListView", self) self.list_primarypoint:Text( "", "Point" ) self.list_primarypoint:SetToolTip( "Primary selection (functions will only affect this point)" ) self.list_primarypoint:SetHeaderHeight( 1 ) self.list_primarypoint:SetPos(0, 15) self.list_primarypoint:SetSize(CPanel_Width/2 - 5, 100) self.list_primarypoint:SetMultiSelect(false) self.list_primarypoint:SetIndicatorOffset( 15 ) self.list_primarypoint.OnRowSelected = function( panel, line ) PA_selected_point = line end self.list_primarypoint.DoDoubleClick = function( Line, LineID ) local panel = PA_manipulation_panel.points_tab Open_Manipulation_Tab( panel.tab ) Listview_DoDoubleClick( panel.list_primarypoint, LineID ) end self.list_primarypoint:SelectFirstItem() self.list_secondarypoint = vgui.Create("PA_Construct_ListView", self) self.list_secondarypoint:Text( "", "Point" ) self.list_secondarypoint:SetToolTip( "Secondary selection" ) self.list_secondarypoint:SetHeaderHeight( 1 ) self.list_secondarypoint:SetPos(CPanel_Width/2 + 5, 15) self.list_secondarypoint:SetSize(CPanel_Width/2 - 5, 100) self.list_secondarypoint:SetMultiSelect(false) self.list_secondarypoint:SetIndicatorOffset( 15 ) self.list_secondarypoint.DoDoubleClick = function( Line, LineID ) local panel = PA_manipulation_panel.points_tab Open_Manipulation_Tab( panel.tab ) Listview_DoDoubleClick( panel.list_primarypoint, LineID ) end self.list_secondarypoint:SelectFirstItem() create_buttons_standard( self, "point" ) self.button_view:SetFunction( function() if !PA_funcs.construct_exists( "Point", PA_selected_point ) then return false end local point = PA_funcs.point_global( PA_selected_point ) return PA_funcs.set_playerview( point.origin ) end ) self.button_delete:SetFunction( function() return PA_funcs.delete_point( PA_selected_point ) end ) self.button_attach:SetFunction( function() return PA_funcs.attach_point( PA_selected_point, PA_activeent ) end ) self.button_deleteall:SetFunction( function() self.list_primarypoint:SelectFirstItem() self.list_secondarypoint:SelectFirstItem() return PA_funcs.delete_points() end ) self.button_moveentity = vgui.Create( "PA_Move_Button", self ) self.button_moveentity:SetPos(0, 160) self.button_moveentity:SetSize(CPanel_Width, 20) self.button_moveentity:SetText( "Move Entity" ) self.button_moveentity:SetToolTip( "Move entity by Primary -> Secondary" ) self.button_moveentity:SetFunction( function() local PA_selected_point2 = self.list_secondarypoint:GetSelectedLine() if PA_selected_point == PA_selected_point2 then Warning("Cannot move between the same point!") return false end local point1 = PA_funcs.point_global( PA_selected_point ) local point2 = PA_funcs.point_global( PA_selected_point2 ) if !point1 or !point2 then Warning("Points not correctly defined") return false end if !PA_funcs.move_entity(point1.origin, point2.origin, PA_activeent) then return false end end ) end function TOOL_POINT_PANEL:PerformLayout() self:SetSize(CPanel_Width, 180) end function TOOL_POINT_PANEL:Paint(w,h) draw.RoundedBox(6, 50, 0, CPanel_Width - 100, 14, BGColor_Point) end vgui.Register("PA_Tool_Point_Panel", TOOL_POINT_PANEL, "DPanel") local TOOL_LINE_PANEL = {} function TOOL_LINE_PANEL:Init() AddMenuText("LINE", CPanel_Width/2 - 12, 0, self) self.list_line = vgui.Create( "PA_Construct_ListView", self ) self.list_line:Text( "", "Line" ) self.list_line:SetHeaderHeight( 1 ) self.list_line:SetPos(0, 15) self.list_line:SetSize(CPanel_Width, 100) self.list_line:SetMultiSelect(false) self.list_line:SetIndicatorOffset( 15 ) self.list_line.OnRowSelected = function( panel, line ) PA_selected_line = line end self.list_line.DoDoubleClick = function( Line, LineID ) local panel = PA_manipulation_panel.lines_tab Open_Manipulation_Tab( panel.tab ) Listview_DoDoubleClick( panel.list_primary, LineID ) end self.list_line:SelectFirstItem() create_buttons_standard( self, "line" ) self.button_view:SetFunction( function() if !PA_funcs.construct_exists( "Line", PA_selected_line ) then return false end local line = PA_funcs.line_global( PA_selected_line ) return PA_funcs.set_playerview( line.startpoint ) end ) self.button_delete:SetFunction( function() return PA_funcs.delete_line(PA_selected_line) end ) self.button_attach:SetFunction( function() return PA_funcs.attach_line(PA_selected_line, PA_activeent) end ) self.button_deleteall:SetFunction( function() self.list_line:SelectFirstItem() return PA_funcs.delete_lines() end ) self.button_moveentity = vgui.Create( "PA_Move_Button", self ) self.button_moveentity:SetPos(0, 160) self.button_moveentity:SetSize(CPanel_Width, 20) self.button_moveentity:SetText( "Move Entity" ) self.button_moveentity:SetToolTip( "Move entity by line" ) self.button_moveentity:SetFunction( function() local line = PA_funcs.line_global(PA_selected_line) if !line then Warning("Line not correctly defined") return false end local point1 = line.startpoint local point2 = line.endpoint if !PA_funcs.move_entity(point1, point2, PA_activeent) then return false end end ) end function TOOL_LINE_PANEL:PerformLayout() self:SetSize(CPanel_Width, 180) end function TOOL_LINE_PANEL:Paint(w,h) draw.RoundedBox(6, 50, 0, CPanel_Width - 100, 14, BGColor_Line) end vgui.Register("PA_Tool_Line_Panel", TOOL_LINE_PANEL, "DPanel") local TOOL_PLANE_PANEL = {} function TOOL_PLANE_PANEL:Init() AddMenuText("PLANE", CPanel_Width/2 - 17, 0, self) self.list_plane = vgui.Create( "PA_Construct_ListView", self ) self.list_plane:Text( "", "Plane" ) self.list_plane:SetHeaderHeight( 1 ) self.list_plane:SetPos(0, 15) self.list_plane:SetSize(CPanel_Width, 100) self.list_plane:SetMultiSelect(false) self.list_plane:SetIndicatorOffset( 15 ) self.list_plane.OnRowSelected = function( panel, line ) PA_selected_plane = line end self.list_plane.DoDoubleClick = function( Line, LineID ) local panel = PA_manipulation_panel.planes_tab Open_Manipulation_Tab( panel.tab ) Listview_DoDoubleClick( panel.list_primary, LineID ) end self.list_plane:SelectFirstItem() create_buttons_standard( self, "plane" ) self.button_view:SetFunction( function() if !PA_funcs.construct_exists( "Plane", PA_selected_plane ) then return false end local plane = PA_funcs.plane_global( PA_selected_plane ) return PA_funcs.set_playerview( plane.origin ) end ) self.button_delete:SetFunction( function() return PA_funcs.delete_plane(PA_selected_plane) end ) self.button_attach:SetFunction( function() return PA_funcs.attach_plane(PA_selected_plane, PA_activeent) end ) self.button_deleteall:SetFunction( function() self.list_plane:SelectFirstItem() return PA_funcs.delete_planes() end ) end function TOOL_PLANE_PANEL:PerformLayout() self:SetSize(CPanel_Width, 160) end function TOOL_PLANE_PANEL:Paint(w,h) draw.RoundedBox(6, 50, 0, CPanel_Width - 100, 14, BGColor_Plane) end vgui.Register("PA_Tool_Plane_Panel", TOOL_PLANE_PANEL, "DPanel") local TOOL_LIST = {} function TOOL_LIST:Init() self.list_tooltype = vgui.Create("DListView", self) self.list_tooltype:SetPos(0, 0) self.list_tooltype:SetSize(CPanel_Width, 153) self.list_tooltype:SetToolTip( "Select left-click function" ) self.list_tooltype:SetHeaderHeight( 0 ) self.list_tooltype:SetSortable(false) self.list_tooltype:SetMultiSelect(false) self.list_tooltype:AddColumn("") //self.list_tooltype:AddColumn(" Left Click Assignment") self.list_tooltype:AddLine("Point - Hitpos") self.list_tooltype:AddLine("Point - Coordinate Centre") self.list_tooltype:AddLine("Point - Mass Centre") self.list_tooltype:AddLine("Point - Bounding Box Centre") self.list_tooltype:AddLine("Line - Start / End (Alt)") self.list_tooltype:AddLine("Line - Hitpos + Hitnormal") self.list_tooltype:AddLine("Line - Hitnormal") self.list_tooltype:AddLine("Plane - Hitpos + Hitnormal") self.list_tooltype:AddLine("Plane - Hitnormal") self.list_tooltype:SelectItem( self.list_tooltype:GetLine(GetConVarNumber(PA_.."tooltype")) ) self.list_tooltype.OnRowSelected = function(parent, line, isselected) RunConsoleCommand( PA_.."tooltype", tostring(line) ) end // Draw coloured tool option backgrounds for i = 1, 9 do local line = self.list_tooltype:GetLine(i) local height = line:GetTall() local DrawColourOutline if i < 5 then DrawColourOutline = table.Copy(BGColor_Point) elseif i < 8 then DrawColourOutline = table.Copy(BGColor_Line) else DrawColourOutline = table.Copy(BGColor_Plane) end local DrawColour = DrawColourOutline line.Paint = function( w, h ) if line:IsSelected() then DrawColour.a = 150 else DrawColour.a = 200 end surface.SetDrawColor( DrawColour ) surface.DrawRect( 0, 0, CPanel_Width, height - 7 ) surface.SetDrawColor( DrawColourOutline ) surface.DrawOutlinedRect(0, 0, CPanel_Width - 2, height-7) end line.Columns[1]:SetTextColor( color_black ) line.Columns[1].UpdateColours = function() end end end function TOOL_LIST:PerformLayout() self:SetSize(CPanel_Width, 153) end function TOOL_LIST:Paint(w,h) end vgui.Register("PA_CPanel_tool_list", TOOL_LIST, "DPanel") local TOOL_OPTIONS = {} function TOOL_OPTIONS:Init() self.checkbox_display = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_display:SetPos(0, 2) self.checkbox_display:SetText( "Enable construct displays" ) self.checkbox_display:SetDark( true ) self.checkbox_display:SetValue( 1 ) self.checkbox_display:SizeToContents() self.checkbox_display:SetToolTip( "Show/Hide all constructs" ) self.checkbox_display:SetConVar( PA_.."displayhud" ) self.checkbox_snap_edge = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_snap_edge:SetPos(0, 21) self.checkbox_snap_edge:SetText( "Snap to Edges" ) self.checkbox_snap_edge:SetDark( true ) self.checkbox_snap_edge:SizeToContents() self.checkbox_snap_edge:SetToolTip( "Snap to the edges of props when placing constructs" ) self.checkbox_snap_edge:SetConVar( PA_.."edge_snap" ) self.checkbox_snap_centre = vgui.Create( "DCheckBoxLabel", self ) self.checkbox_snap_centre:SetPos(0, 40) self.checkbox_snap_centre:SetText( "Snap to Centre Lines" ) self.checkbox_snap_centre:SetDark( true ) self.checkbox_snap_centre:SizeToContents() self.checkbox_snap_centre:SetToolTip( "Snap to the centre-lines of props when placing constructs" ) self.checkbox_snap_centre:SetConVar( PA_.."centre_snap" ) self.slider_snap_dist = vgui.Create( "DNumSlider", self ) self.slider_snap_dist:SetPos(0, 59) -- self.slider_snap_dist:SetSize(CPanel_Width, 30) self.slider_snap_dist.Label:SetSize( 80 ) self.slider_snap_dist:SetWide( CPanel_Width ) self.slider_snap_dist:SetText( "Snap Sensitivity" ) self.slider_snap_dist.Label:SetDark( true ) self.slider_snap_dist:SetMinMax( 0.1, 100 ) self.slider_snap_dist:SetDecimals( 1 ) self.slider_snap_dist.Slider:SetNotches( self.slider_snap_dist.Slider:GetWide() / 4 ) self.slider_snap_dist:SetToolTip( "Sets the maximum distance for edge/centre snap detection (in units)" ) self.slider_snap_dist:SetConVar( PA_.."snap_distance" ) // Help button self.button_help = vgui.Create( "PA_Function_Button", self ) self.button_help:SetSize(60, 20) self.button_help:SetPos(CPanel_Width - self.button_help:GetWide(), 2) self.button_help:SetText( "Help" ) self.button_help:SetToolTip( "Open online help using the Steam in-game browser" ) self.button_help:SetFunction( function() return gui.OpenURL( "https://sourceforge.net/userapps/mediawiki/wenli/index.php?title=Precision_Alignment" ) end ) end function TOOL_OPTIONS:PerformLayout() self:SetSize(CPanel_Width, 100) end function TOOL_OPTIONS:Paint(w,h) end vgui.Register("PA_CPanel_tool_options", TOOL_OPTIONS, "DPanel") //********************************************************************************************************************// // CPanel Layout //********************************************************************************************************************// CPanel:Clear() local tool_list = vgui.Create( "PA_CPanel_tool_list" ) CPanel:AddItem( tool_list ) CPanel.tool_list = tool_list local tool_options = vgui.Create( "PA_CPanel_tool_options" ) CPanel:AddItem( tool_options ) CPanel.tool_options = tool_options local point_window = vgui.Create( "PA_Tool_Point_Panel" ) CPanel:AddItem( point_window ) CPanel.point_window = point_window local line_window = vgui.Create( "PA_Tool_Line_Panel" ) CPanel:AddItem( line_window ) CPanel.line_window = line_window local plane_window = vgui.Create( "PA_Tool_Plane_Panel" ) CPanel:AddItem( plane_window ) CPanel.plane_window = plane_window //********************************************************************************************************************// // Usermessages //********************************************************************************************************************// local function select_next_point() if PA_selected_point < 9 then if PA_funcs.construct_exists( "Point", PA_selected_point ) then PA_selected_point = PA_selected_point + 1 local dlist_points = controlpanel.Get( PA ).point_window.list_primarypoint dlist_points:ClearSelection() dlist_points:SelectItem( dlist_points:GetLine(PA_selected_point) ) return true end end return false end local function select_next_line() if PA_selected_line < 9 then if PA_funcs.construct_exists( "Line", PA_selected_line ) then PA_selected_line = PA_selected_line + 1 local dlist_lines = controlpanel.Get( PA ).line_window.list_line dlist_lines:ClearSelection() dlist_lines:SelectItem( dlist_lines:GetLine(PA_selected_line) ) return true end end return false end local function select_next_plane() if PA_selected_plane < 9 then if PA_funcs.construct_exists( "Plane", PA_selected_plane ) then PA_selected_plane = PA_selected_plane + 1 local dlist_planes = controlpanel.Get( PA ).plane_window.list_plane dlist_planes:ClearSelection() dlist_planes:SelectItem( dlist_planes:GetLine(PA_selected_plane) ) return true end end return false end // Called when the server sends click data - used to add a new point/line local function umsg_click_hook() local point = Vector( net.ReadFloat(), net.ReadFloat(), net.ReadFloat() ) local normal = Vector( net.ReadFloat(), net.ReadFloat(), net.ReadFloat() ) local ent = net.ReadEntity() local shift = LocalPlayer():KeyDown( IN_SPEED ) local alt = LocalPlayer():KeyDown( IN_WALK ) local tooltype = GetConVarNumber( PA_.. "tooltype" ) // Points if tooltype <= 4 then if shift then select_next_point() end PA_funcs.set_point(PA_selected_point, point) // Auto-attach to selected ent if alt then if PA_activeent then PA_funcs.attach_point( PA_selected_point, PA_activeent ) elseif precision_align_points[PA_selected_point].entity then PA_funcs.attach_point( PA_selected_point, nil ) end elseif precision_align_points[PA_selected_point].entity ~= ent then PA_funcs.attach_point( PA_selected_point, ent ) end // Lines elseif tooltype == 5 then if shift then select_next_line() end // Alt-click will place end point if alt then PA_funcs.set_line( PA_selected_line, nil, point, nil, nil ) else PA_funcs.set_line( PA_selected_line, point, nil, nil, nil ) // Only auto-attach by start point, not end point if PA_funcs.construct_exists( "Line", PA_selected_line ) and precision_align_lines[PA_selected_line].entity ~= ent then PA_funcs.attach_line( PA_selected_line, ent ) end end elseif tooltype == 6 then if shift then select_next_line() end PA_funcs.set_line( PA_selected_line, point, nil, normal, nil ) if alt then if PA_activeent then PA_funcs.attach_line( PA_selected_line, PA_activeent ) elseif precision_align_lines[PA_selected_line].entity then PA_funcs.attach_line( PA_selected_line, nil ) end elseif precision_align_lines[PA_selected_line].entity ~= ent then PA_funcs.attach_line( PA_selected_line, ent ) end elseif tooltype == 7 then PA_funcs.set_line( PA_selected_line, nil, nil, normal, nil ) // Planes elseif tooltype == 8 then if shift then select_next_plane() end PA_funcs.set_plane( PA_selected_plane, point, normal ) if alt then if PA_activeent then PA_funcs.attach_plane( PA_selected_plane, PA_activeent ) elseif precision_align_planes[PA_selected_plane].entity then PA_funcs.attach_plane( PA_selected_plane, nil ) end elseif precision_align_planes[PA_selected_plane].entity ~= ent then PA_funcs.attach_plane( PA_selected_plane, ent ) end elseif tooltype == 9 then PA_funcs.set_plane( PA_selected_plane, nil, normal ) end end net.Receive( PA_ .. "click", umsg_click_hook ) // Called when the server sends entity data - so the client knows which entity is selected local function umsg_entity_hook() PA_activeent = net.ReadEntity() end net.Receive( PA_ .. "ent", umsg_entity_hook ) //********************************************************************************************************************// // HUD Display //********************************************************************************************************************// // Construct draw sizes local point_size_min = math.max( GetConVarNumber( PA_.."size_point" ), 1 ) local point_size_max = GetConVarNumber( PA_.."size_point" ) * 1000 local line_size_start = GetConVarNumber( PA_.."size_line_start" ) local line_size_min = GetConVarNumber( PA_.."size_line_end" ) -- End (double bar) local line_size_max = line_size_min * 1000 local plane_size = GetConVarNumber( PA_.."size_plane" ) local plane_size_normal = GetConVarNumber( PA_.."size_plane_normal" ) local text_min, text_max = 1, 4500 local draw_attachments = LocalPlayer():GetInfo( PA_.."draw_attachments" ) cvars.AddChangeCallback( PA_.."size_point", function( CVar, Prev, New ) point_size_min = tonumber(math.max(New, 1)); point_size_max = tonumber(New) * 1000 end ) cvars.AddChangeCallback( PA_.."size_line_start", function( CVar, Prev, New ) line_size_start = tonumber(New) end ) cvars.AddChangeCallback( PA_.."size_line_end", function( CVar, Prev, New ) line_size_min = tonumber(New); line_size_max = line_size_min * 1000 end ) cvars.AddChangeCallback( PA_.."size_plane", function( CVar, Prev, New ) plane_size = tonumber(New) end ) cvars.AddChangeCallback( PA_.."size_plane_normal", function( CVar, Prev, New ) plane_size_normal = tonumber(New) end ) // Manage attachment line colour changes local H = GetConVarNumber( PA_.."attachcolour_h" ) local S = GetConVarNumber( PA_.."attachcolour_s" ) local V = GetConVarNumber( PA_.."attachcolour_v" ) local A = GetConVarNumber( PA_.."attachcolour_a" ) local attachcolourHSV = { h = H, s = S, v = V, a = A } local attachcolourRGB = HSVToColor( H, S, V ) attachcolourRGB.a = A local function SetAttachColour(CVar, Prev, New) if CVar == PA_.."attachcolour_h" then attachcolourHSV.h = New elseif CVar == PA_.."attachcolour_s" then attachcolourHSV.s = New elseif CVar == PA_.."attachcolour_v" then attachcolourHSV.v = New elseif CVar == PA_.."attachcolour_a" then attachcolourHSV.a = New end attachcolourRGB = HSVToColor( attachcolourHSV.h, attachcolourHSV.s, attachcolourHSV.v ) attachcolourRGB.a = attachcolourHSV.a end cvars.AddChangeCallback( PA_.."attachcolour_h", SetAttachColour ) cvars.AddChangeCallback( PA_.."attachcolour_s", SetAttachColour ) cvars.AddChangeCallback( PA_.."attachcolour_v", SetAttachColour ) cvars.AddChangeCallback( PA_.."attachcolour_a", SetAttachColour ) local function inview( pos2D ) if pos2D.x > -ScrW() and pos2D.y > -ScrH() and pos2D.x < ScrW() * 2 and pos2D.y < ScrH() * 2 then return true end return false end // HUD draw function local function precision_align_draw( w, h) local playerpos = LocalPlayer():GetShootPos() // Points for k, v in ipairs (precision_align_points) do if v.visible and v.origin then //Check if point exists local point_temp = PA_funcs.point_global(k) if point_temp then local origin = point_temp.origin local point = origin:ToScreen() if inview( point ) then local distance = playerpos:Distance( origin ) local size = math.Clamp( point_size_max / distance, point_size_min, point_size_max ) local text_dist = math.Clamp(text_max / distance, text_min, text_max) surface.SetDrawColor( pointcolour.r, pointcolour.g, pointcolour.b, pointcolour.a ) surface.DrawLine( point.x - size, point.y, point.x + size, point.y ) surface.DrawLine( point.x, point.y + size, point.x, point.y - size ) draw.DrawText( tostring(k), "Default", point.x + text_dist, point.y + text_dist/1.5, Color(pointcolour.r, pointcolour.g, pointcolour.b, pointcolour.a), 0 ) // Draw attachment line if draw_attachments then if IsValid(v.entity) then local entpos = v.entity:GetPos():ToScreen() surface.SetDrawColor( attachcolourRGB.r, attachcolourRGB.g, attachcolourRGB.b, attachcolourRGB.a ) surface.DrawLine( point.x, point.y, entpos.x, entpos.y ) end end end end end end // Lines for k, v in ipairs (precision_align_lines) do if v.visible and v.startpoint and v.endpoint then //Check if line exists local line_temp = PA_funcs.line_global(k) if line_temp then local startpoint = line_temp.startpoint local endpoint = line_temp.endpoint local line_start = startpoint:ToScreen() local line_end = endpoint:ToScreen() local distance1 = playerpos:Distance( startpoint ) local distance2 = playerpos:Distance( endpoint ) local size2 = math.Clamp(line_size_max / distance2, line_size_min, line_size_max) local text_dist = math.Clamp(text_max / distance1, text_min, text_max) surface.SetDrawColor( linecolour.r, linecolour.g, linecolour.b, linecolour.a ) // Start X local normal = (endpoint - startpoint):GetNormal() local dir1, dir2 if IsValid(v.entity) then local up = v.entity:GetUp() if normal:Dot(up) < 0.9 then dir1 = (normal:Cross(up)):GetNormal() else dir1 = (normal:Cross(v.entity:GetForward())):GetNormal() end else if math.abs(normal.z) < 0.9 then dir1 = (normal:Cross(Vector(0,0,1))):GetNormal() else dir1 = (normal:Cross(Vector(1,0,0))):GetNormal() end end dir2 = (dir1:Cross(normal)):GetNormal() * line_size_start dir1 = dir1 * line_size_start local v1 = (startpoint + dir1 + dir2):ToScreen() local v2 = (startpoint - dir1 + dir2):ToScreen() local v3 = (startpoint - dir1 - dir2):ToScreen() local v4 = (startpoint + dir1 - dir2):ToScreen() // Start X if inview( line_start ) then surface.DrawLine(v1.x, v1.y, v3.x, v3.y) surface.DrawLine(v2.x, v2.y, v4.x, v4.y) end // Line surface.DrawLine( line_start.x, line_start.y, line_end.x, line_end.y ) // End = if inview( line_end ) then local line_dir_2D = Vector(line_end.x - line_start.x, line_end.y - line_start.y, 0):GetNormalized() local norm_dir_2D = {x = -line_dir_2D.y, y = line_dir_2D.x} surface.DrawLine( line_end.x - norm_dir_2D.x * size2, line_end.y - norm_dir_2D.y * size2, line_end.x + norm_dir_2D.x * size2, line_end.y + norm_dir_2D.y * size2 ) surface.DrawLine( line_end.x + (line_dir_2D.x/3 - norm_dir_2D.x) * size2, line_end.y + (line_dir_2D.y/3 - norm_dir_2D.y) * size2, line_end.x + (line_dir_2D.x/3 + norm_dir_2D.x) * size2, line_end.y + (line_dir_2D.y/3 + norm_dir_2D.y) * size2 ) end draw.DrawText( tostring(k), "Default", line_start.x + text_dist, line_start.y - text_dist/1.5 - 15, Color(linecolour.r, linecolour.g, linecolour.b, linecolour.a), 3 ) // Draw attachment line if draw_attachments then if IsValid(v.entity) then local entpos = v.entity:GetPos():ToScreen() surface.SetDrawColor( attachcolourRGB.r, attachcolourRGB.g, attachcolourRGB.b, attachcolourRGB.a ) surface.DrawLine( line_start.x, line_start.y, entpos.x, entpos.y ) end end end end end // Planes for k, v in ipairs (precision_align_planes) do if v.visible and v.origin and v.normal then //Check if plane exists local plane_temp = PA_funcs.plane_global(k) if plane_temp then local origin = plane_temp.origin local normal = plane_temp.normal // Draw normal line local line_start = origin:ToScreen() if inview( line_start ) then local line_end = ( origin + normal * plane_size_normal ):ToScreen() local distance = playerpos:Distance( origin ) local text_dist = math.Clamp(text_max / distance, text_min, text_max) surface.SetDrawColor( planecolour.r, planecolour.g, planecolour.b, planecolour.a ) surface.DrawLine( line_start.x, line_start.y, line_end.x, line_end.y ) // Draw plane surface local dir1, dir2 if IsValid(v.entity) then local up = v.entity:GetUp() if math.abs(normal:Dot(up)) < 0.9 then dir1 = (normal:Cross(up)):GetNormal() else dir1 = (normal:Cross(v.entity:GetForward())):GetNormal() end else if math.abs(normal.z) < 0.9 then dir1 = (normal:Cross(Vector(0,0,1))):GetNormal() else dir1 = (normal:Cross(Vector(1,0,0))):GetNormal() end end dir2 = (dir1:Cross(normal)):GetNormal() * plane_size dir1 = dir1 * plane_size local v1 = (origin + dir1 + dir2):ToScreen() local v2 = (origin - dir1 + dir2):ToScreen() local v3 = (origin - dir1 - dir2):ToScreen() local v4 = (origin + dir1 - dir2):ToScreen() surface.DrawLine( v1.x, v1.y, v2.x, v2.y ) surface.DrawLine( v2.x, v2.y, v3.x, v3.y ) surface.DrawLine( v3.x, v3.y, v4.x, v4.y ) surface.DrawLine( v4.x, v4.y, v1.x, v1.y ) draw.DrawText( tostring(k), "Default", line_start.x - text_dist, line_start.y + text_dist/1.5, Color(planecolour.r, planecolour.g, planecolour.b, planecolour.a), 1 ) //Default // Draw attachment line if draw_attachments then if IsValid(v.entity) then local entpos = v.entity:GetPos():ToScreen() surface.SetDrawColor( attachcolourRGB.r, attachcolourRGB.g, attachcolourRGB.b, attachcolourRGB.a ) surface.DrawLine( line_start.x, line_start.y, entpos.x, entpos.y ) end end end end end end end hook.Add("HUDPaint", "draw_precision_align", precision_align_draw) local function precision_align_displayhud_func( ply, cmd, args ) local enabled = tobool( args[1] ) if !enabled then hook.Remove( "HUDPaint", "draw_precision_align" ) else hook.Add("HUDPaint", "draw_precision_align", precision_align_draw) end return true end concommand.Add( PA_.."displayhud", precision_align_displayhud_func ) PA_manipulation_panel = vgui.Create( "PA_Manipulation_Frame" ) PA_manipulation_panel:SetVisible(false) local function precision_align_open_panel_func( ply, cmd, args ) if !PA_manipulation_panel then local PA_manipulation_panel = vgui.Create( "PA_Manipulation_Frame" ) else if PA_manipulation_panel:IsVisible() then RememberCursorPosition() PA_manipulation_panel:SetVisible(false) else PA_manipulation_panel:SetVisible(true) RestoreCursorPosition() end end end concommand.Add( PA_.."open_panel", precision_align_open_panel_func ) /*$”˜