--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] AddCSLuaFile() properties.Add( "bone_manipulate", { MenuLabel = "#edit_bones", Order = 500, MenuIcon = "icon16/vector.png", Filter = function( self, ent, ply ) if ( !gamemode.Call( "CanProperty", ply, "bonemanipulate", ent ) ) then return false end if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end -- If our ent has an attached entity, we want to use and modify its bones instead local bonecount = ent:GetBoneCount() if ( bonecount <= 1 ) then return false end return ents.FindByClassAndParent( "widget_bones", ent ) == nil end, Action = function( self, ent ) if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end self:MsgStart() net.WriteEntity( ent ) self:MsgEnd() end, Receive = function( self, length, ply ) local ent = net.ReadEntity() if ( !IsValid( ent ) ) then return end if ( !properties.CanBeTargeted( ent, ply ) ) then return end if ( !self:Filter( ent, ply ) ) then return end ent.widget = ents.Create( "widget_bones" ) ent.widget:Setup( ent ) ent.widget:Spawn() ent.widget.LastBonePress = 0 ent.widget.BonePressCount = 0 -- What happens when we click on a bone? ent.widget.OnBoneClick = function( w, boneid, pl ) -- If we have an old axis, remove it if ( IsValid( w.axis ) ) then w.axis:Remove() end -- We clicked on the same bone if ( w.LastBonePress == boneid ) then w.BonePressCount = w.BonePressCount + 1 if ( w.BonePressCount >= 3 ) then w.BonePressCount = 0 end -- We clicked on a new bone! else w.BonePressCount = 0 w.LastBonePress = boneid end local EntityCycle = { "widget_bonemanip_move", "widget_bonemanip_rotate", "widget_bonemanip_scale" } w.axis = ents.Create( EntityCycle[ w.BonePressCount + 1 ] ) w.axis:Setup( ent, boneid, w.BonePressCount == 1 ) w.axis:Spawn() w.axis:SetPriority( 0.5 ) w:DeleteOnRemove( w.axis ) end end } ) properties.Add( "bone_manipulate_end", { MenuLabel = "#stop_editing_bones", Order = 500, MenuIcon = "icon16/vector_delete.png", Filter = function( self, ent ) if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end -- If our ent has an attached entity, we want to use and modify its bones instead return ents.FindByClassAndParent( "widget_bones", ent ) != nil end, Action = function( self, ent ) if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end self:MsgStart() net.WriteEntity( ent ) self:MsgEnd() end, Receive = function( self, length, ply ) local ent = net.ReadEntity() if ( !IsValid( ent ) ) then return end if ( !IsValid( ent.widget ) ) then return end ent.widget:Remove() end } ) local widget_bonemanip_move = { Base = "widget_axis", OnArrowDragged = function( self, num, dist, pl, mv ) -- Prediction doesn't work properly yet.. because of the confusion with the bone moving, and the parenting, Agh. if ( CLIENT ) then return end local ent = self:GetParent() if ( !IsValid( ent ) ) then return end local bone = self:GetParentAttachment() if ( bone <= 0 ) then return end local v = Vector( 0, 0, 0 ) if ( num == 1 ) then v.x = dist end if ( num == 2 ) then v.y = dist end if ( num == 3 ) then v.z = dist end ent:ManipulateBonePosition( bone, ent:GetManipulateBonePosition( bone ) + v ) end, -- -- Although we use the position from our bone, we want to use the angles from the -- parent bone - because that's the direction our bone goes -- CalcAbsolutePosition = function( self, v, a ) local ent = self:GetParent() if ( !IsValid( ent ) ) then return end local bone = ent:GetBoneParent( self:GetParentAttachment() ) if ( bone <= 0 ) then return end local _, ang = ent:GetBonePosition( bone ) local pos, _ = ent:GetBonePosition( self:GetParentAttachment() ) return pos, ang end } scripted_ents.Register( widget_bonemanip_move, "widget_bonemanip_move" ) local widget_bonemanip_rotate = { Base = "widget_axis", OnArrowDragged = function( self, num, dist, pl, mv ) -- Prediction doesn't work properly yet.. because of the confusion with the bone moving, and the parenting, Agh. if ( CLIENT ) then return end local ent = self:GetParent() if ( !IsValid( ent ) ) then return end local bone = self:GetParentAttachment() if ( bone <= 0 ) then return end local v = Angle( 0, 0, 0 ) if ( num == 2 ) then v.x = dist end if ( num == 3 ) then v.y = dist end if ( num == 1 ) then v.z = dist end ent:ManipulateBoneAngles( bone, ent:GetManipulateBoneAngles( bone ) + v ) end } scripted_ents.Register( widget_bonemanip_rotate, "widget_bonemanip_rotate" ) local widget_bonemanip_scale = { Base = "widget_axis", IsScaleArrow = true, OnArrowDragged = function( self, num, dist, pl, mv ) -- Prediction doesn't work properly yet.. because of the confusion with the bone moving, and the parenting, Agh. if ( CLIENT ) then return end local ent = self:GetParent() if ( !IsValid( ent ) ) then return end local bone = self:GetParentAttachment() if ( bone <= 0 ) then return end local v = Vector( 0, 0, 0 ) if ( num == 1 ) then v.x = dist end if ( num == 2 ) then v.y = dist end if ( num == 3 ) then v.z = dist end ent:ManipulateBoneScale( bone, ent:GetManipulateBoneScale( bone ) + v * 0.1 ) ent:ManipulateBoneScale( ent:GetBoneParent( bone ), ent:GetManipulateBoneScale( ent:GetBoneParent( bone ) ) + v ) end, -- -- Although we use the position from our bone, we want to use the angles from the -- parent bone - because that's the direction our bone goes -- CalcAbsolutePosition = function( self, v, a ) local ent = self:GetParent() if ( !IsValid( ent ) ) then return end local bone = self:GetParentAttachment() if ( bone <= 0 ) then return end local pbone = ent:GetBoneParent( bone ) if ( pbone <= 0 ) then return end local pos, ang = ent:GetBonePosition( bone ) local pos2, _ = ent:GetBonePosition( pbone ) return pos + ( pos2 - pos ) * 0.5, ang end } scripted_ents.Register( widget_bonemanip_scale, "widget_bonemanip_scale" )