mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 19:38:24 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
306 lines
8.4 KiB
C#
306 lines
8.4 KiB
C#
using Sandbox.Movement;
|
|
|
|
namespace Sandbox;
|
|
|
|
[Expose]
|
|
[Icon( "event_seat" ), Group( "Game" ), Title( "Chair" )]
|
|
public partial class BaseChair : Component, Component.IPressable, ISitTarget
|
|
{
|
|
[Expose]
|
|
public enum AnimatorSitPose
|
|
{
|
|
Standing = 0,
|
|
Chair = 1,
|
|
ChairForward = 2,
|
|
ChairCrossed = 3,
|
|
KneelingOpen = 4,
|
|
Kneeling = 5,
|
|
Ground = 6,
|
|
GroundCrossed = 7,
|
|
}
|
|
|
|
/// <summary>
|
|
/// A GameObject representing the seat position
|
|
/// </summary>
|
|
[Header( "Seat" )]
|
|
[Property] public GameObject SeatPosition { get; set; }
|
|
|
|
/// <summary>
|
|
/// The sitting pose to use when a player is seated
|
|
/// </summary>
|
|
[Header( "Animation" )]
|
|
[Property] public AnimatorSitPose SitPose { get; set; } = AnimatorSitPose.Chair;
|
|
|
|
/// <summary>
|
|
/// Height offset for sitting position, from -1 (lowest) to 1 (highest)
|
|
/// </summary>
|
|
[Range( -1, 1 )]
|
|
[Property] public float SitHeight { get; set; } = 0;
|
|
|
|
/// <summary>
|
|
/// A GameObject representing the eye position
|
|
/// </summary>
|
|
[Header( "Eyes" )]
|
|
[Property] public GameObject EyePosition { get; set; }
|
|
|
|
/// <summary>
|
|
/// Pitch range for seated players
|
|
/// </summary>
|
|
[Property] public Vector2 PitchRange { get; set; } = new Vector2( -90, 70 );
|
|
|
|
/// <summary>
|
|
/// Yaw range for seated players
|
|
/// </summary>
|
|
[Property] public Vector2 YawRange { get; set; } = new Vector2( -120, 120 );
|
|
|
|
[Header( "Exit" )]
|
|
[Property] public GameObject[] ExitPoints { get; set; }
|
|
|
|
/// <summary>
|
|
/// Chair is usable if the player can enter
|
|
/// </summary>
|
|
public bool CanPress( IPressable.Event e )
|
|
{
|
|
var player = e.Source as PlayerController;
|
|
if ( player is null ) return false;
|
|
return CanEnter( player );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the player has pressed to use the chair.
|
|
/// Only called if CanPress returned true.
|
|
/// </summary>
|
|
public bool Press( IPressable.Event e )
|
|
{
|
|
var player = e.Source as PlayerController;
|
|
if ( player is null ) return false;
|
|
|
|
EnterChair( player );
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called on the host to enter the chair.
|
|
/// </summary>
|
|
[Rpc.Host]
|
|
private void EnterChair( PlayerController player )
|
|
{
|
|
if ( player.Network.Owner != Rpc.Caller )
|
|
return;
|
|
|
|
if ( !CanEnter( player ) ) return;
|
|
|
|
using ( Rpc.FilterInclude( player.Network.Owner ) )
|
|
{
|
|
// TODO - when https://github.com/Facepunch/sbox/issues/3270 is done
|
|
// we should be able to set the parent here on the host and have it replicate
|
|
// rather than telling the client to parent themselves!
|
|
|
|
Sit( player );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called on the client to place the player in the chair.
|
|
/// </summary>
|
|
[Rpc.Broadcast( NetFlags.HostOnly )]
|
|
public void Sit( PlayerController player )
|
|
{
|
|
var seatPos = SeatPosition ?? GameObject;
|
|
|
|
player.Body.Enabled = false;
|
|
player.ColliderObject.Enabled = false;
|
|
|
|
player.GameObject.SetParent( seatPos, false );
|
|
player.GameObject.LocalTransform = global::Transform.Zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called on the host to request leaving the chair.
|
|
/// </summary>
|
|
[Rpc.Host]
|
|
public void AskToLeave( PlayerController player )
|
|
{
|
|
if ( player.Network.Owner != Rpc.Caller )
|
|
return;
|
|
|
|
if ( GetOccupant() != player )
|
|
return;
|
|
|
|
if ( !CanLeave( player ) ) return;
|
|
|
|
using ( Rpc.FilterInclude( player.Network.Owner ) )
|
|
{
|
|
// TODO - when https://github.com/Facepunch/sbox/issues/3270 is done
|
|
// we should be able to set the parent here on the host and have it replicate
|
|
// rather than telling the client to parent themselves!
|
|
|
|
Eject( player );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if this player can leave the chair
|
|
/// </summary>
|
|
public virtual bool CanLeave( PlayerController player )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called on the client to eject the player from the chair.
|
|
/// </summary>
|
|
[Rpc.Broadcast( NetFlags.HostOnly )]
|
|
public void Eject( PlayerController player )
|
|
{
|
|
if ( GetOccupant() != player )
|
|
return;
|
|
|
|
var exitPoint = FindBestExitPoint();
|
|
|
|
var seatPos = SeatPosition ?? GameObject;
|
|
player.GameObject.SetParent( null, true );
|
|
|
|
player.WorldPosition = exitPoint;
|
|
player.EyeAngles = WorldRotation.Inverse * player.EyeAngles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a position to place the player when they exit the chair. This searches
|
|
/// through ExitPoints to find the best one, which is usually the one the player is most
|
|
/// facing towards.
|
|
/// </summary>
|
|
public Vector3 FindBestExitPoint()
|
|
{
|
|
if ( ExitPoints == null || ExitPoints.Length == 0 ) return (SeatPosition ?? GameObject).WorldPosition;
|
|
|
|
return ExitPoints.OrderByDescending( ScoreExitPoint ).First().WorldPosition;
|
|
}
|
|
|
|
private float ScoreExitPoint( GameObject exitPoint )
|
|
{
|
|
var eyeForward = GetOccupant()?.EyeTransform.Forward ?? WorldTransform.Forward;
|
|
|
|
var seatPos = (SeatPosition ?? GameObject).WorldPosition;
|
|
var toExit = exitPoint.WorldPosition - seatPos;
|
|
var forwardScore = Vector3.Dot( toExit.Normal, eyeForward );
|
|
|
|
// todo: Trace test? Make sure we're not going through the world?
|
|
|
|
return forwardScore * 1000.0f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if this player can enter the chair
|
|
/// </summary>
|
|
public virtual bool CanEnter( PlayerController player )
|
|
{
|
|
if ( player is null ) return false;
|
|
if ( IsOccupied ) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the transform representing the eye position when seated
|
|
/// </summary>
|
|
public virtual Transform GetEyeTransform()
|
|
{
|
|
var seatPos = EyePosition ?? SeatPosition ?? GameObject;
|
|
return seatPos.WorldTransform;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the chair is currently occupied
|
|
/// </summary>
|
|
public bool IsOccupied => GetOccupant().IsValid();
|
|
|
|
/// <summary>
|
|
/// Gets the player that is currently occupying the chair
|
|
/// </summary>
|
|
public PlayerController GetOccupant() => GetComponentInChildren<PlayerController>();
|
|
|
|
/// <summary>
|
|
/// Called to update the player's animator when seated
|
|
/// </summary>
|
|
public virtual void UpdatePlayerAnimator( PlayerController controller, SkinnedModelRenderer renderer )
|
|
{
|
|
// Make sure the controller is aligned with the chair
|
|
controller.LocalTransform = global::Transform.Zero;
|
|
|
|
// Make sure the body rotation is zero
|
|
renderer.LocalRotation = Rotation.Identity;
|
|
|
|
// Update animation parameters
|
|
renderer.Set( "sit", (int)SitPose );
|
|
renderer.Set( "sit_offset_height", SitHeight * 12.0f );
|
|
renderer.Set( "b_grounded", true );
|
|
renderer.Set( "b_climbing", false );
|
|
renderer.Set( "b_swim", false );
|
|
renderer.Set( "duck", false );
|
|
|
|
// Look in the direction the player is aiming
|
|
var eyesForward = controller.EyeTransform.Forward;
|
|
renderer.SetLookDirection( "aim_eyes", eyesForward, controller.AimStrengthEyes );
|
|
renderer.SetLookDirection( "aim_head", eyesForward, controller.AimStrengthHead );
|
|
renderer.SetLookDirection( "aim_body", eyesForward, controller.AimStrengthBody );
|
|
|
|
// Clamp the eye angles
|
|
ClampEyes( controller );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clamps the eye angles of a seated player between the PitchRange and YawRange
|
|
/// </summary>
|
|
protected void ClampEyes( PlayerController controller )
|
|
{
|
|
var ea = controller.EyeAngles;
|
|
ea.pitch = MathX.Clamp( ea.pitch, PitchRange.x, PitchRange.y );
|
|
ea.yaw = MathX.Clamp( ea.yaw, YawRange.x, YawRange.y );
|
|
controller.EyeAngles = ea;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the eye transform for a seated player
|
|
/// </summary>
|
|
public virtual Transform CalculateEyeTransform( PlayerController controller )
|
|
{
|
|
// clamp the player's eye angles
|
|
ClampEyes( controller );
|
|
|
|
var seatEyeTx = GetEyeTransform();
|
|
|
|
var transform = new Transform();
|
|
transform.Position = seatEyeTx.Position;
|
|
transform.Rotation = WorldRotation * controller.EyeAngles.ToRotation();
|
|
|
|
return transform;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the player model sitting in the chair if it's selected
|
|
/// </summary>
|
|
protected override void DrawGizmos()
|
|
{
|
|
var selectedObject = Scene.Editor?.SelectedGameObject;
|
|
if ( selectedObject == null ) return;
|
|
|
|
if ( selectedObject != GameObject && selectedObject != SeatPosition && selectedObject != EyePosition )
|
|
return;
|
|
|
|
var seatPos = (SeatPosition ?? GameObject).WorldTransform;
|
|
var localSeatPos = GameObject.WorldTransform.ToLocal( seatPos );
|
|
|
|
var so = Gizmo.Draw.Model( "models/citizen/citizen.vmdl", localSeatPos.WithScale( 1 ) );
|
|
so.ColorTint = Color.White.WithAlpha( 0.6f );
|
|
so.SetAnimParameter( "sit", (int)SitPose );
|
|
so.SetAnimParameter( "sit_offset_height", SitHeight * 12.0f );
|
|
so.SetAnimParameter( "b_grounded", true );
|
|
so.SetAnimParameter( "b_climbing", false );
|
|
so.SetAnimParameter( "b_swim", false );
|
|
so.SetAnimParameter( "duck", false );
|
|
so.Update( RealTime.Delta * 10.0f );
|
|
}
|
|
}
|
|
|