sasalka/Code/Player/Dedugan.Interaction.cs
2025-05-27 00:59:12 +03:00

303 lines
6.7 KiB
C#

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<IPressable>( 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<PlayerController.IEvents>.PostToGameObject( GameObject, x => x.FailPressing() );
return;
}
var component = obj.Components.Get<IPressable>( FindMode.EnabledInSelfAndDescendants );
if ( component != null )
{
if ( !component.CanPress( new IPressable.Event
{
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
Source = this
} ) )
{
ISceneEvent<PlayerController.IEvents>.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<PlayerController.IEvents>.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<PlayerController.IEvents>.PostToGameObject( GameObject, x =>
{
foundComponent = x.GetUsableComponent( eyeTrace.GameObject ) ?? foundComponent;
} );
if ( foundComponent.IsValid() )
return foundComponent;
foreach ( var component in eyeTrace.GameObject.Components.GetAll<IPressable>() )
{
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<PlayerController.IEvents>.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<Collider>() )
{
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<PanelComponent>()?.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;
}
}
}