using Sandbox; using Sandbox.Utility; using Sandbox.Diagnostics; using System.Linq; public sealed partial class Dedugan : Component { public Component Pressed { get; set; } public bool EnablePressing { get; set; } = true; public Component Hovered { get; set; } [Sync( SyncFlags.Interpolate )] public Vector3 TracedHitPos { get; set; } [Sync] public bool CameraTraceIsHit { get; set; } public Vector3 TracedHitNormal { get; set; } void InteractionsUpdate() { if ( !EnablePressing ) return; if ( Pressed.IsValid() ) { UpdatePressed(); } else { UpdateHovered(); } if ( Input.Pressed( "Ragdoll" ) ) { Health = 100; } } private void UpdatePressed() { bool flag = Input.Down( "use" ); if ( flag && Pressed.Components.TryGet( out var pressable ) ) { flag = pressable.Pressing( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ); } if ( GetDistanceFromGameObject( Pressed.GameObject, Camera.WorldPosition ) > InteractDistance ) { flag = false; } if ( !flag ) { StopPressing(); } } private void UpdateHovered() { if ( Pressed.IsValid() ) return; SwitchHovered( TryGetLookedAt() ); if ( Hovered is IPressable pressable ) { pressable.Look( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ); } if ( Input.Pressed( "use" ) ) { StartPressing( Hovered ); } } public void StartPressing( Component obj ) { if ( Pressed == obj ) return; StopPressing(); if ( !obj.IsValid() ) { ISceneEvent.PostToGameObject( GameObject, x => x.FailPressing() ); return; } var component = obj.Components.Get( FindMode.EnabledInSelfAndDescendants ); if ( component != null ) { var pressEvent = new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this }; if ( !component.CanPress( pressEvent ) ) { ISceneEvent.PostToGameObject( GameObject, x => x.FailPressing() ); return; } component.Press( pressEvent ); } Pressed = obj; if ( Pressed.IsValid() ) { ISceneEvent.PostToGameObject( GameObject, x => x.StartPressing( Pressed ) ); } } public void StopPressing() { if ( Pressed.IsValid() ) { ISceneEvent.PostToGameObject( GameObject, x => x.StopPressing( Pressed ) ); if ( Pressed is IPressable pressable ) { pressable.Release( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ); } Pressed = null; } } private void SwitchHovered( Component obj ) { var e = new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this }; if ( Hovered == obj ) { if ( Hovered is IPressable pressable ) pressable.Look( e ); return; } if ( Hovered is IPressable oldPressable ) oldPressable.Blur( e ); Hovered = obj; if ( Hovered is IPressable newPressable ) { newPressable.Hover( e ); newPressable.Look( e ); } } private Component TryGetLookedAt() { var from = Scene.Camera.WorldPosition + Scene.Camera.WorldRotation.Forward; var to = from + Scene.Camera.WorldRotation.Forward * InteractDistance; var trace = Scene.Trace .Ray( from, to ) .IgnoreGameObjectHierarchy( GameObject ) .Run(); TracedHitPos = trace.Hit ? trace.HitPosition : trace.EndPosition; CameraTraceIsHit = trace.Hit; TracedHitNormal = trace.Normal; if ( !trace.Hit || !trace.GameObject.IsValid() ) return null; var go = trace.GameObject; var pressable = go.Components.Get( FindMode.EnabledInSelfAndDescendants ); if ( pressable != null && pressable.CanPress( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ) ) { return pressable as Component; } return null; } private float GetDistanceFromGameObject( GameObject obj, Vector3 point ) { Vector3 closest = obj.WorldPosition; float minDist = Vector3.DistanceBetween( closest, point ); foreach ( var col in Pressed.GetComponentsInChildren() ) { Vector3 cp = col.FindClosestPoint( point ); float dist = Vector3.DistanceBetween( cp, point ); if ( dist < minDist ) minDist = dist; } return minDist; } }