WorldBuilder/InteriorManager.cs
using Clover.Items;
using Clover.Player;

namespace Clover.WorldBuilder;

[Category( "Clover/World" )]
public class InteriorManager : Component
{
	[RequireComponent] public WorldLayerObject WorldLayerObject { get; set; }

	[Property, InlineEditor] public Dictionary<string, Room> Rooms { get; set; } = new();

	public string CurrentRoom { get; set; }
	public List<GameObject> CurrentRoomPieces { get; set; } = new();

	[Property] public string DefaultRoom { get; set; }


	public IEnumerable<InteriorPiece> InteriorPieces =>
		WorldLayerObject.World?.Components.GetAll<InteriorPiece>( FindMode.EverythingInSelfAndDescendants );

	public class Room
	{
		[Property] public GameObject Wall { get; set; }
		[Property] public GameObject Floor { get; set; }
		[Property] public RoomTrigger[] Triggers { get; set; }
	}

	public bool IsInCurrentRoom( Vector3 position )
	{
		var room = CollectionExtensions.GetValueOrDefault( Rooms, CurrentRoom );

		if ( room == null )
		{
			Log.Warning( $"Room {CurrentRoom} not found" );
			return false;
		}

		foreach ( var trigger in room.Triggers )
		{
			var collider = trigger.Components.Get<BoxCollider>();
			var bbox = BBox.FromPositionAndSize( collider.WorldPosition + collider.Center, collider.Scale );
			if ( bbox.Contains( position ) )
			{
				return true;
			}
		}

		return false;
	}

	protected override void OnStart()
	{
		base.OnStart();
		UpdateMaterials();

		Log.Info( $"InteriorManager started, found {InteriorPieces.Count()} interior pieces" );
		foreach ( var piece in InteriorPieces )
		{
			if ( piece.BelongsToRoom( DefaultRoom ) )
			{
				piece.Show();
			}
			else
			{
				piece.Hide();
			}
		}

		CurrentRoom = DefaultRoom;

		RebuildVisibility();
	}

	private void RebuildVisibility()
	{
		foreach ( var room in Rooms )
		{
			foreach ( var trigger in room.Value.Triggers )
			{
				var collider = trigger.Components.Get<Collider>();
				if ( !collider.IsValid() )
				{
					Log.Error( $"Room {room.Key} trigger {trigger.GameObject.Name} has no collider" );
					continue;
				}

				foreach ( var touching in collider.Touching )
				{
					if ( touching.Components.Get<WorldItem>() == null )
						continue;

					touching.Tags.Remove( "room_invisible" );

					if ( CurrentRoom != room.Key )
					{
						touching.Tags.Add( "room_invisible" );
					}
				}
			}
		}
	}

	private void UpdateMaterials()
	{
		foreach ( var room in Rooms )
		{
			UpdateRoom( room.Key, room.Value );
		}
	}

	private void UpdateRoom( string roomId, Room roomData )
	{
		if ( roomData.Floor.IsValid() )
		{
			if ( roomData.Floor.Components.TryGet<MeshComponent>( out var mesh ) )
			{
				foreach ( var face in mesh.Mesh.FaceHandles )
				{
					mesh.Mesh.SetFaceMaterial( face, "materials/world/summer/cliff.vmat" );
				}
			}

			if ( roomData.Floor.Components.TryGet<ModelRenderer>( out var modelRenderer ) )
			{
			}
		}

		if ( roomData.Wall.IsValid() )
		{
			if ( roomData.Wall.Components.TryGet<MeshComponent>( out var mesh ) )
			{
				foreach ( var face in mesh.Mesh.FaceHandles )
				{
					mesh.Mesh.SetFaceMaterial( face, "materials/world/summer/cliff.vmat" );
				}
			}
		}
	}

	public void EnterRoom( PlayerCharacter player, RoomTrigger trigger )
	{
		var roomToEnter = trigger.RoomId;

		if ( roomToEnter == CurrentRoom )
			return;

		var piecesToShow = InteriorPieces.Where( x => x.BelongsToRoom( roomToEnter ) );
		var piecesToHide = InteriorPieces.Where( x => !x.BelongsToRoom( roomToEnter ) );

		foreach ( var piece in piecesToShow )
		{
			piece.Show();
		}

		foreach ( var piece in piecesToHide )
		{
			piece.Hide();
		}

		CurrentRoom = roomToEnter;

		RebuildVisibility();
	}

	public void ExitRoom( PlayerCharacter player, RoomTrigger trigger )
	{
	}
}