ChainHook.cs

A component that represents a thrown chain hook. It updates its position and rotation relative to an AnchorUnit, renders a line between the anchor and hook, times out after Lifetime, and attaches to players that enter its trigger to apply a Chain effect.

Networking
using System;
using Sandbox;

public class ChainHook : Component, Component.ITriggerListener
{
	[Property] public LineRenderer LineRenderer { get; set; }

	[Sync] public Unit AnchorUnit { get; set; }

	public Vector2 Direction { get; set; }
	public float Distance { get; set; }

	private TimeSince _timeSinceSpawn;

	public float Lifetime { get; set; }	

	protected override void OnAwake()
	{
		base.OnAwake();

		_timeSinceSpawn = 0f;
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		//Gizmo.Draw.Color = Color.White;
		//Gizmo.Draw.Text( $"AnchorUnit: {AnchorUnit}", new global::Transform( WorldPosition ) );

		if ( !AnchorUnit.IsValid() || AnchorUnit.IsDying )
		{
			GameObject.Destroy();
			return;
		}

		if ( AnchorUnit.IsValid() )
			LineRenderer.VectorPoints[0] = AnchorUnit.WorldPosition + new Vector3( 0f, 0f, AnchorUnit.Height * 0.75f );

		LineRenderer.VectorPoints[1] = WorldPosition;

		if ( IsProxy ) 
			return;

		var anchorPos = AnchorUnit.WorldPosition.WithZ( 50f );

		WorldPosition = anchorPos + (Vector3)Direction * Distance * Utils.MapReturn( _timeSinceSpawn, 0f, Lifetime, 0f, 1f, EasingType.SineOut );

		WorldRotation = Rotation.From( 0f, -Utils.GetAngleDegreesFromVector( (WorldPosition - anchorPos).Normal ), 0f );

		if ( _timeSinceSpawn > Lifetime )
			GameObject.Destroy();
	}

	void ITriggerListener.OnTriggerEnter( Collider collider )
	{
		if ( IsProxy || !collider.IsTrigger ) 
			return;

		var thing = collider.GetComponent<Thing>();
		if ( !thing.IsValid() )
			return;

		if ( !thing.Tags.HasAny( "player") )
			return;

		var player = thing as Player;
		if ( !player.IsValid() )
			return;

		if ( player.IsInvincible )
			return;

		var chainLifetime = Utils.Select( Manager.Instance.Difficulty, 6f, 10f, 12f );
		player.Chain( AnchorUnit, AnchorUnit.WorldPosition, chainLength: 150f, lifetime: chainLifetime );

		GameObject.Destroy();
	}
}