sasalka/Code/Player/Dedugan.Interaction.cs
2025-05-28 17:15:12 +03:00

220 lines
5.9 KiB
C#

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; }
// private TimeSince HoldTime = 0;
// private bool Holding = false;
// private bool HoldingInteractionHappened = false;
void InteractionsUpdate()
{
if (!EnablePressing)
{
// Holding = false;
// HoldingInteractionHappened = false;
return;
}
if (Pressed.IsValid())
{
UpdatePressed();
}
else
{
UpdateHovered();
}
}
private void UpdatePressed()
{
bool flag = Input.Down("use");
if (flag && Pressed.Components.TryGet<IPressable>(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()
{
SwitchHovered(TryGetLookedAt());
if (Hovered is IPressable pressable)
{
pressable.Look(new IPressable.Event
{
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
Source = this
});
}
if (Input.Down("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)
{
var pressEvent = new IPressable.Event
{
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
Source = this
};
if (!component.CanPress(pressEvent))
{
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x => x.FailPressing());
return;
}
component.Press(pressEvent);
}
Pressed = obj;
if (Pressed.IsValid())
{
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x => x.StartPressing(Pressed));
}
}
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;
}
}
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()
{
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 trace = Scene.Trace.Ray(from, to).IgnoreGameObjectHierarchy(GameObject).Radius(num).Run();
TracedHitPos = trace.Hit ? trace.HitPosition : trace.EndPosition;
CameraTraceIsHit = trace.Hit;
TracedHitNormal = trace.Normal;
if (!trace.Hit || !trace.GameObject.IsValid()) continue;
Component foundComponent = null;
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x =>
{
foundComponent = x.GetUsableComponent(trace.GameObject) ?? foundComponent;
});
if (foundComponent.IsValid()) return foundComponent;
foreach (var component in trace.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;
}
private float GetDistanceFromGameObject(GameObject obj, Vector3 point)
{
Vector3 closest = obj.WorldPosition;
float minDist = Vector3.DistanceBetween(closest, point);
foreach (var col in Pressed.GetComponentsInChildren<Collider>())
{
Vector3 cp = col.FindClosestPoint(point);
float dist = Vector3.DistanceBetween(cp, point);
if (dist < minDist)
minDist = dist;
}
return minDist;
}
}