WorldBuilder/AreaTrigger.cs
using Clover.Components;
using Clover.Player;
namespace Clover;
[Category( "Clover/World" )]
public sealed class AreaTrigger : Component, Component.ITriggerListener
{
[RequireComponent] public WorldLayerObject WorldLayerObject { get; set; }
[RequireComponent] public BoxCollider Collider { get; set; }
[Property] public Data.WorldData DestinationWorldData { get; set; }
[Property] public string DestinationEntranceId { get; set; }
[Property] public bool UnloadPreviousWorld { get; set; } = true;
[Property] public bool NoWalk { get; set; }
private HashSet<GameObject> _triggerQueue = new();
protected override void DrawGizmos()
{
base.DrawGizmos();
Gizmo.Draw.Text( DestinationWorldData?.Title + "\n" + DestinationEntranceId, new Transform() );
Gizmo.Hitbox.BBox( BBox.FromPositionAndSize( Collider.Center, Collider.Scale ) );
Gizmo.Draw.LineBBox( BBox.FromPositionAndSize( Collider.Center, Collider.Scale ) );
Gizmo.Draw.Arrow( Vector3.Zero, Vector3.Forward * 64f );
}
void ITriggerListener.OnTriggerEnter( Collider other )
{
if ( IsProxy || !Networking.IsHost ) return;
var player = other.GetComponent<PlayerCharacter>();
if ( !player.IsValid() )
{
// Log.Warning( $"AreaTrigger: OnTriggerEnter: Not a player: {other}" );
return;
}
// Enter( player );
_triggerQueue.Add( other.GameObject );
}
void ITriggerListener.OnTriggerExit( Collider other )
{
_triggerQueue.Remove( other.GameObject );
}
private async void Enter( PlayerCharacter player )
{
if ( !DestinationWorldData.IsValid() )
{
Log.Error( $"AreaTrigger: OnTriggerEnter: Invalid destination world data" );
return;
}
var w = await WorldManager.Instance.GetWorldOrLoad( DestinationWorldData );
if ( !w.IsValid() )
{
Log.Error( $"AreaTrigger: OnTriggerEnter: Failed to load world {DestinationWorldData.ResourceName}" );
return;
}
if ( player.InCutscene )
{
Log.Warning( "AreaTrigger: OnTriggerEnter: Player is in cutscene" );
return;
}
var entrance = w.GetEntrance( DestinationEntranceId );
if ( entrance.IsValid() )
{
var currentWorld = WorldManager.Instance.GetWorld( WorldLayerObject.Layer );
currentWorld.Save();
player.StartCutscene( WorldPosition + WorldRotation.Forward * 64f );
if ( !player.Network.Owner.IsHost )
{
Log.Info( $"areatrigger waiting for host" );
await Task.DelayRealtimeSeconds( 1f ); // Wait for the host to load the world
}
// await Fader.Instance.FadeToBlack( true );
using ( Rpc.FilterInclude( player.Network.Owner ) )
{
Fader.Instance.FadeToBlackRpc( true );
}
await Task.DelayRealtimeSeconds( Fader.Instance.FadeTime );
player.SetLayer( entrance.WorldLayerObject.Layer );
player.TeleportTo( entrance.EntranceId );
// player.ModelLookAt( entrance.WorldPosition + entrance.WorldRotation.Forward );
player.OnWorldChanged?.Invoke( w );
if ( UnloadPreviousWorld && currentWorld.ShouldUnloadOnExit )
{
WorldManager.Instance.UnloadWorld( currentWorld );
}
await GameTask.DelayRealtimeSeconds( 0.25f );
player.StartCutscene( entrance.WorldPosition + entrance.WorldRotation.Forward * 64f );
// await Fader.Instance.FadeFromBlack( true );
using ( Rpc.FilterInclude( player.Network.Owner ) )
{
Fader.Instance.FadeFromBlackRpc( true );
}
await GameTask.DelayRealtimeSeconds( Fader.Instance.FadeTime );
// await GameTask.DelayRealtimeSeconds( 0.25f );
player.EndCutscene();
Log.Info( $"areatrigger finished" );
}
else
{
Log.Warning( $"AreaTrigger: OnTriggerEnter: No entrance found with id: {DestinationEntranceId}" );
}
}
protected override void OnFixedUpdate()
{
if ( !Networking.IsHost ) return;
// hack to continually check for players in the trigger
foreach ( var go in _triggerQueue )
{
var player = go.GetComponent<PlayerCharacter>();
if ( player.IsValid() && !player.InCutscene )
{
Enter( player );
_triggerQueue.Remove( go );
}
}
}
}