Game/ControlSystem/ControlSystem.cs
public class ControlSystem : GameObjectSystem<ControlSystem>
{
// When each chair first became occupied. Used to sort seats so the earliest occupant is in charge
private readonly Dictionary<BaseChair, RealTimeSince> _occupiedSince = new();
public ControlSystem( Scene scene ) : base( scene )
{
Listen( Stage.StartFixedUpdate, 10, OnTick, "ControlSystem" );
}
void OnTick()
{
// TODO this should be more generic, some kind of interface?
var driven = new HashSet<GameObject>();
foreach ( var chair in GetSortedSeats() )
{
var builder = new LinkedGameObjectBuilder();
builder.AddConnected( chair.GameObject );
// Skip if a seat occupied earlier already claimed this
if ( builder.Objects.Any( driven.Contains ) ) continue;
driven.UnionWith( builder.Objects );
RunControl( chair, builder );
}
}
IEnumerable<BaseChair> GetSortedSeats()
{
var chairs = Scene.GetAll<BaseChair>();
foreach ( var chair in chairs )
{
if ( !chair.IsValid() || !chair.IsOccupied )
_occupiedSince.Remove( chair );
else
_occupiedSince.TryAdd( chair, 0 );
}
return chairs
.Where( c => c.IsValid() && c.IsOccupied )
.OrderBy( c => (float)_occupiedSince.GetValueOrDefault( c, default ) );
}
void RunControl( BaseChair chair, LinkedGameObjectBuilder builder )
{
var controller = chair.GetOccupant();
if ( !controller.IsValid() ) return;
var player = controller.GetComponent<Player>();
if ( !player.IsValid() ) return;
using var scope = ClientInput.PushScope( player );
foreach ( var o in builder.Objects )
{
foreach ( var controllable in o.GetComponentsInChildren<IPlayerControllable>() )
{
if ( controllable is null ) continue;
if ( !controllable.CanControl( player ) ) continue;
controllable.OnControl();
}
}
}
}