227 lines
5.3 KiB
C#
227 lines
5.3 KiB
C#
using Sandbox;
|
|
using Sandbox.Utility;
|
|
using Sandbox.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace SimpleInteractions {
|
|
|
|
/// <summary>
|
|
/// Simple interaction component
|
|
/// </summary>
|
|
[Icon( "touch_app" )]
|
|
[Title( "Simple Interaction" )]
|
|
public class SimpleInteraction : Component
|
|
{
|
|
[Property]
|
|
public bool InteractionEnabled {get; set;} = true;
|
|
|
|
[Property, Title("Interaction Name")]
|
|
public string InteractionString {get; set;} = "Interact";
|
|
|
|
[Property]
|
|
public float InteractionDistance {get; set;} = 120f;
|
|
|
|
[Property, ToggleGroup("InteractionHold")]
|
|
public bool InteractionHold {get; set;} = false;
|
|
|
|
[Property, Group("InteractionHold")]
|
|
public float InteractionHoldDuration {get; set;} = 0.5f;
|
|
|
|
|
|
/// <summary>
|
|
/// If not set, will try to find a collider on the same GameObject.
|
|
/// </summary>
|
|
[Property, Title("Override collider")]
|
|
public Collider Collider { get; set; }
|
|
|
|
private GameObject CurrentPanel = null;
|
|
|
|
private TimeSince HoldTime = 0;
|
|
private bool Holding = false;
|
|
private bool HoldingInteractionHappened = false;
|
|
|
|
static protected GameObject InteractionPanelPrefab ;
|
|
|
|
protected override void OnStart()
|
|
{
|
|
InteractionPanelPrefab = GameObject.GetPrefab("InteractionsPanel.prefab");
|
|
|
|
Assert.True(InteractionPanelPrefab.IsValid(), $"No InteractionPanel prefab found for {this.GameObject.Name}!");
|
|
|
|
if (!Collider.IsValid()) {
|
|
|
|
Collider = this.GameObject.GetComponent<Collider>();
|
|
|
|
Assert.True(Collider.IsValid(), $"No collider found for {this.GameObject.Name}!");
|
|
}
|
|
this.GameObject.Tags.Add("Interact");
|
|
}
|
|
|
|
protected override void OnUpdate()
|
|
{
|
|
if (!InteractionEnabled)
|
|
{
|
|
// Reset everything just in case
|
|
Holding = false;
|
|
HoldingInteractionHappened = false;
|
|
|
|
// Delete the Interaction panel otherwise it would just float there...
|
|
if (CurrentPanel.IsValid())
|
|
{
|
|
InteractionPanel panel = CurrentPanel.GetComponent<InteractionPanel>();
|
|
if (panel.IsValid()) {
|
|
_ = DeletePanel();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
Ray ray = Scene.Camera.GameObject.Transform.World.ForwardRay;
|
|
|
|
var traces = Scene.Trace.Ray(ray, InteractionDistance)
|
|
.WithoutTags("IgnoreInteract")
|
|
.HitTriggers()
|
|
.RunAll();
|
|
|
|
// Gizmo.Draw.Line(tr.StartPosition, tr.EndPosition);
|
|
|
|
if (traces.Count() <= 0)
|
|
{
|
|
_ = DeletePanel();
|
|
|
|
// Force repressing use in case you looked away while holding down.
|
|
HoldingInteractionHappened = true;
|
|
return;
|
|
}
|
|
|
|
foreach (var tr in traces)
|
|
{
|
|
|
|
Collider HitCollider = tr.Shape.Collider as Collider;
|
|
|
|
// If it's a trigger and it doesn't have the interact tag, skip it.
|
|
// We can see through it.
|
|
if (HitCollider.IsTrigger && !HitCollider.GameObject.Tags.Has("Interact"))
|
|
{
|
|
continue;
|
|
} else if (!HitCollider.IsTrigger && !HitCollider.GameObject.Tags.Has("Interact"))
|
|
{
|
|
// Something is blocking the interaction.
|
|
_ = DeletePanel();
|
|
|
|
// Force repressing use in case you looked away while holding down.
|
|
HoldingInteractionHappened = true;
|
|
break;
|
|
}
|
|
|
|
Vector3 offset = Vector3.Zero;
|
|
|
|
if (HitCollider is BoxCollider)
|
|
{
|
|
offset = (HitCollider as BoxCollider).Center;
|
|
} else if (HitCollider is SphereCollider)
|
|
{
|
|
offset = new Vector3((HitCollider as SphereCollider).Center);
|
|
}
|
|
|
|
|
|
if (HitCollider == Collider)
|
|
{
|
|
Vector3 pos = new Vector3(offset.x, offset.y, - offset.z);
|
|
OnHover(HitCollider.GameObject.WorldPosition - pos);
|
|
break;
|
|
} else
|
|
{
|
|
_ = DeletePanel();
|
|
|
|
// Force repressing use in case you looked away while holding down.
|
|
HoldingInteractionHappened = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnHover(Vector3 pos)
|
|
{
|
|
if (!CurrentPanel.IsValid())
|
|
{
|
|
CurrentPanel = InteractionPanelPrefab.Clone();
|
|
}
|
|
|
|
CurrentPanel.WorldPosition = pos;
|
|
|
|
// Flip the panel to face the camera
|
|
Rotation camRotation = Scene.Camera.WorldRotation;
|
|
|
|
Angles ang = camRotation.Angles();
|
|
ang.roll += 180;
|
|
ang.pitch += 180;
|
|
Rotation rot = ang.ToRotation();
|
|
CurrentPanel.WorldRotation = rot;
|
|
|
|
InteractionPanel panel = CurrentPanel.GetComponent<InteractionPanel>();
|
|
panel.InteractionString = InteractionString;
|
|
panel.IsHoldInteraction = InteractionHold;
|
|
panel.ProgressionHold = 0;
|
|
|
|
|
|
|
|
if (!InteractionHold)
|
|
{
|
|
if (Input.Pressed("use"))
|
|
{
|
|
_ = panel.TriggerInteractAnimation();
|
|
OnInteract();
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if (!Input.Down("use"))
|
|
{
|
|
Holding = false;
|
|
HoldingInteractionHappened = false;
|
|
return;
|
|
}
|
|
|
|
// Interaction already happened. Player needs to release and press again.
|
|
if (HoldingInteractionHappened)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Holding)
|
|
{
|
|
panel.ProgressionHold = Easing.QuadraticInOut(HoldTime / InteractionHoldDuration);
|
|
if (HoldTime >= InteractionHoldDuration)
|
|
{
|
|
HoldingInteractionHappened = true;
|
|
OnInteract();
|
|
}
|
|
} else
|
|
{
|
|
// Started holding.
|
|
Holding = true;
|
|
HoldTime = 0;
|
|
_ = panel.TriggerInteractAnimation();
|
|
}
|
|
}
|
|
|
|
async private Task DeletePanel()
|
|
{
|
|
if(!CurrentPanel.IsValid()) return;
|
|
|
|
CurrentPanel.GetComponent<PanelComponent>().Panel.Delete();
|
|
await Task.DelaySeconds( 0.1f );
|
|
CurrentPanel.Destroy();
|
|
}
|
|
|
|
[Rpc.Broadcast]
|
|
protected virtual void OnInteract()
|
|
{
|
|
Log.Error($"Interaction not implemented for {this.GameObject.Name}!");
|
|
}
|
|
|
|
}
|
|
|
|
} |