public sealed partial class Dedugan { 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; } private GameObject interactionPanel; private static GameObject interactionPanelPrefab; private TimeSince holdTimer; private bool isHolding; private bool triggered; private const string InteractionPrefabPath = "prefabs/InteractionPanel.prefab"; public void UpdateLookAt() { if ( EnablePressing ) { if ( Pressed.IsValid() ) { UpdatePressed(); } else { UpdateHovered(); } } } private void UpdatePressed() { bool flag = Input.Pressed( "Use" ); if ( flag && Pressed.Components.TryGet( out var pressable ) ) { if ( pressable.RequiresHold ) { if ( !isHolding ) { holdTimer = 0; isHolding = true; triggered = false; } if ( triggered ) return; var progress = holdTimer / pressable.HoldTime; ShowInteractionUI( TracedHitPos, pressable.DisplayText, true, progress ); if ( holdTimer > pressable.HoldTime ) { triggered = true; pressable.Pressing( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ); } } else { 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() { SwitchHovered( TryGetLookedAt() ); if ( Hovered is IPressable pressable ) { pressable.Look( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ); ShowInteractionUI( TracedHitPos, pressable.DisplayText ); } else { ClearInteractionUI(); } if ( Input.Pressed( "use" ) ) { StartPressing( Hovered ); } } public void StartPressing( Component obj ) { StopPressing(); if ( !obj.IsValid() ) { ISceneEvent.PostToGameObject( GameObject, x => x.FailPressing() ); return; } var component = obj.Components.Get( FindMode.EnabledInSelfAndDescendants ); if ( component != null ) { if ( !component.CanPress( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ) ) { ISceneEvent.PostToGameObject( GameObject, x => x.FailPressing() ); return; } component.Press( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ); } Pressed = obj; if ( Pressed.IsValid() ) { ISceneEvent.PostToGameObject( GameObject, x => x.StartPressing( Pressed ) ); } } private Component TryGetLookedAt() { for ( float num = 0f; num <= 4f; num += 2f ) { var from = Scene.Camera.WorldPosition + Scene.Camera.WorldRotation.Forward; var to = from + Scene.Camera.WorldRotation.Forward * (InteractDistance - num); var eyeTrace = Scene.Trace.Ray( from, to ).IgnoreGameObjectHierarchy( GameObject ).Radius( num ).Run(); TracedHitPos = eyeTrace.Hit ? eyeTrace.HitPosition : eyeTrace.EndPosition; CameraTraceIsHit = eyeTrace.Hit; TracedHitNormal = eyeTrace.Normal; if ( !eyeTrace.Hit || !eyeTrace.GameObject.IsValid() ) continue; Component foundComponent = null; ISceneEvent.PostToGameObject( GameObject, x => { foundComponent = x.GetUsableComponent( eyeTrace.GameObject ) ?? foundComponent; } ); if ( foundComponent.IsValid() ) return foundComponent; foreach ( var component in eyeTrace.GameObject.Components.GetAll() ) { if ( component.CanPress( new IPressable.Event { Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ), Source = this } ) ) { return component as Component; } } } return null; } 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; } isHolding = false; triggered = false; ClearInteractionUI(); } 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 pressable2 ) { pressable2.Blur( e ); Hovered = null; } Hovered = obj; if ( Hovered is IPressable pressable3 ) { pressable3.Hover( e ); pressable3.Look( e ); } } private float GetDistanceFromGameObject( GameObject obj, Vector3 point ) { var a = obj.WorldPosition; var b = Camera.WorldPosition; float minDist = Vector3.DistanceBetween( a, b ); foreach ( var collider in Pressed.GetComponentsInChildren() ) { var closest = collider.FindClosestPoint( Camera.WorldPosition ); var dist = Vector3.DistanceBetween( closest, Camera.WorldPosition ); if ( dist < minDist ) { minDist = dist; } } return minDist; } private void ShowInteractionUI( Vector3 position, string text, bool showProgress = false, float progress = 0f ) { if ( interactionPanelPrefab == null ) { interactionPanelPrefab = GameObject.GetPrefab( InteractionPrefabPath ); if ( interactionPanelPrefab == null ) return; } if ( !interactionPanel.IsValid() ) { interactionPanel = interactionPanelPrefab.Clone( GameObject.Scene ); } interactionPanel.Transform.Position = position; interactionPanel.Transform.Rotation = Rotation.LookAt( Camera.WorldPosition - position ); var panel = interactionPanel.GetComponent()?.GetPanel(); if ( panel is not null ) { panel.SetProperty( "InteractionString", text ); panel.SetProperty( "IsHoldInteraction", showProgress ); panel.SetProperty( "ProgressionHold", progress ); } } private void ClearInteractionUI() { if ( interactionPanel.IsValid() ) { interactionPanel.Destroy(); interactionPanel = null; } } }