WorldBuilder/Door.cs
using System;
using System.Threading.Tasks;
using Clover.Interactable;
using Clover.Player;
namespace Clover.WorldBuilder;
[Category( "Clover/World" )]
public class Door : Component, IInteract
{
[Property] public GameObject DoorModel { get; set; }
[Property] public GameObject FloorCollider { get; set; }
[Property] public SoundEvent OpenSound { get; set; }
[Property] public SoundEvent CloseSound { get; set; }
[Property] public SoundEvent SqueakSound { get; set; }
[Property] public bool IsLocked { get; set; } = false;
[Property] public bool HasOpenHours { get; set; } = false;
[Property] public int HourOpen { get; set; } = 0;
[Property] public int HourClose { get; set; } = 24;
[Property] public Curve DoorCurve { get; set; }
private bool _isBusy;
private bool _isBeingUsed;
[Sync] private bool _openState { get; set; }
private float _openAngle = -90;
[Sync] private TimeSince _lastUse { get; set; }
private float _doorSpeed = 0.5f;
private SoundHandle _squeakSoundHandle;
[Rpc.Owner]
public void SetBusy( bool state )
{
_isBusy = state;
}
public void StartInteract( PlayerCharacter player )
{
if ( _isBusy ) return;
// _ = PlayerEnter( player );
PlayerEnterRpc();
}
public void FinishInteract( PlayerCharacter player )
{
}
public string GetInteractName()
{
return "Open door";
}
public void SetState( bool state )
{
_openState = state;
DoorModel.LocalRotation = Rotation.FromYaw( _openState ? _openAngle : 0 );
}
public void Open()
{
if ( _squeakSoundHandle.IsValid() ) _squeakSoundHandle.Stop();
_openState = true;
_lastUse = 0;
_isBeingUsed = true;
SetBusy( true );
SetCollision( false );
GameObject.PlaySound( OpenSound );
_squeakSoundHandle = GameObject.PlaySound( SqueakSound );
}
public void Close()
{
if ( _squeakSoundHandle.IsValid() ) _squeakSoundHandle.Stop();
// if ( !_openState ) return;
_openState = false;
_lastUse = 0;
_isBeingUsed = true;
SetBusy( true );
SetCollision( true );
_squeakSoundHandle = GameObject.PlaySound( SqueakSound );
// Log.Info( "Door: Close" );
}
/*void ITriggerListener.OnTriggerEnter( Collider other )
{
var player = other.GetComponent<PlayerCharacter>();
if ( !player.IsValid() )
{
return;
}
OnEnter( player );
}*/
[Rpc.Owner]
private void PlayerEnterRpc()
{
_ = PlayerEnter( PlayerCharacter.Get( Rpc.Caller ) );
}
private async Task PlayerEnter( PlayerCharacter player )
{
if ( _isBeingUsed ) return;
player.CharacterController.Velocity = Vector3.Zero;
player.TeleportTo( WorldPosition + WorldRotation.Forward * 25f + WorldRotation.Left * 16f,
WorldRotation * Rotation.FromYaw( 180 ) );
Open();
SetCollision( false );
await Task.DelayRealtimeSeconds( _doorSpeed );
player.StartCutscene( WorldPosition + WorldRotation.Forward * -32f + WorldRotation.Left * 16f );
await Task.DelayRealtimeSeconds( 0.5f );
Close();
await Task.DelayRealtimeSeconds( _doorSpeed + 0.5f );
SetCollision( true );
player.EndCutscene();
_isBeingUsed = false;
}
private void SetCollision( bool state )
{
var collider = Components.Get<Collider>( FindMode.EverythingInSelfAndDescendants );
if ( collider.IsValid() )
{
collider.Enabled = state;
}
else
{
Log.Error( "Door: SetCollision: No collider found" );
}
}
protected override void OnFixedUpdate()
{
if ( !_isBeingUsed ) return;
if ( _isBeingUsed && _lastUse > _doorSpeed )
{
Log.Info( "Door: OnFixedUpdate: Finish using" );
_isBeingUsed = false;
SetBusy( false );
if ( !_openState )
{
SetCollision( true );
// CloseSound?.Play();
GameObject.PlaySound( CloseSound );
}
// SqueakSound?.Stop();
if ( _squeakSoundHandle.IsValid() ) _squeakSoundHandle.Stop();
}
// animate door opening/closing by rotating it
/*var sourceAngle = _openState ? 0 : _openAngle;
var destinationAngle = _openState ? _openAngle : 0;*/
var frac = Math.Clamp( _lastUse / _doorSpeed, 0, 1 );
if ( !_openState ) frac = 1 - frac;
// var angle = Mathf.CubicInterpolate( sourceAngle, destinationAngle, 0, 1, frac );
var curveFrac = DoorCurve.Evaluate( frac );
var yaw = MathX.LerpTo( 0, _openAngle, curveFrac );
DoorModel.LocalRotation = Rotation.FromYaw( yaw );
}
public async void CloseAfter( float seconds )
{
DoorModel.LocalRotation = Rotation.FromYaw( _openAngle );
SetBusy( true );
await Task.DelayRealtimeSeconds( seconds );
Close();
}
}