Items/SoccerGoal.cs
using Clover.Interactable;
using Clover.Persistence;
using Clover.Player;
using Clover.Ui;
namespace Clover.Items;
public class SoccerGoal : Component, IInteract, Component.ITriggerListener, IPersistent
{
[RequireComponent] public WorldItem WorldItem { get; set; }
[Property] public ModelRenderer GoalModel { get; set; }
[Property] public SoundEvent ScoreSound { get; set; }
public enum Team
{
Red,
Blue,
Yellow,
Green
}
private Team _teamColor;
private bool _isBusy;
[Sync]
public Team TeamColor
{
get => _teamColor;
set
{
_teamColor = value;
UpdateModel();
}
}
private void UpdateModel()
{
if ( !GoalModel.IsValid() )
{
Log.Warning( "GoalModel is not valid" );
return;
}
GoalModel.Tint = TeamColor switch
{
Team.Red => Color.Red,
Team.Blue => Color.Blue,
Team.Yellow => Color.Yellow,
Team.Green => Color.Green,
_ => Color.White
};
}
[Sync] public int Score { get; set; }
protected override void OnStart()
{
base.OnStart();
UpdateModel();
}
private SoccerSpawn GetClosestSpawn()
{
var spawns = Game.ActiveScene.GetAllComponents<SoccerSpawn>()
.Where( x => x.WorldPosition.Distance( WorldPosition ) < 500 ).ToList();
if ( spawns.Count == 0 )
{
Log.Error( "No spawns found" );
return null;
}
var closest = spawns.OrderBy( x => x.WorldPosition.Distance( WorldPosition ) ).FirstOrDefault();
return closest;
}
private SoccerBall _ball;
void ITriggerListener.OnTriggerEnter( Collider other )
{
if ( IsProxy ) return;
Log.Info( $"SoccerGoal triggered by {other.GameObject}" );
if ( other.GameObject.Components.Get<SoccerBall>() == null )
return;
_ball = other.GameObject.Components.Get<SoccerBall>();
BallInGoal();
}
private async void BallInGoal()
{
if ( _isBusy )
return;
var spawn = GetClosestSpawn();
if ( spawn == null )
{
Log.Error( "No spawn found" );
return;
}
// PlayerCharacter.NotifyAll( Notifications.NotificationType.Info, $"{TeamColor} scored!" );
SoundEx.Play( ScoreSound, WorldPosition );
Score++;
GetClosestSpawn()?.OnBallScored( TeamColor );
_isBusy = true;
_ball.Tags.Add( "invisible" );
_ball.Tags.Add( "nocollideplayer" );
_ball.Components.Get<Rigidbody>().MotionEnabled = false;
await GameTask.DelaySeconds( 3f );
if ( !_ball.IsValid() )
return;
_ball.Tags.Remove( "invisible" );
_ball.Tags.Remove( "nocollideplayer" );
_ball.Components.Get<Rigidbody>().MotionEnabled = true;
RespawnBall( _ball, spawn );
_isBusy = false;
}
private static void RespawnBall( SoccerBall ball, SoccerSpawn spawn )
{
var playersNearby = Game.ActiveScene.GetAllComponents<PlayerCharacter>()
.Where( x => x.WorldPosition.Distance( spawn.WorldPosition ) < 32 ).ToList();
foreach ( var player in playersNearby )
{
var playerPos = player.WorldPosition;
var spawnPos = spawn.WorldPosition;
var norm = (playerPos - spawnPos).Normal;
player.TeleportTo( spawnPos + norm * 32, Rotation.Identity );
}
ball.WorldPosition = spawn.WorldPosition + Vector3.Up * 16f;
ball.Components.Get<Rigidbody>().Velocity = Vector3.Zero;
ball.Components.Get<Rigidbody>().AngularVelocity = Vector3.Zero;
}
/*void ITriggerListener.OnTriggerExit( Collider other )
{
}*/
public void StartInteract( PlayerCharacter player )
{
if ( IsProxy )
{
player.Notify( Notifications.NotificationType.Error, "Only the host can change the team color" );
return;
}
TeamColor = TeamColor switch
{
Team.Red => Team.Blue,
Team.Blue => Team.Yellow,
Team.Yellow => Team.Green,
Team.Green => Team.Red,
_ => Team.Red
};
Score = 0;
_isBusy = false;
}
public string GetInteractName()
{
return "Change team";
}
protected override void OnUpdate()
{
base.OnUpdate();
if ( Gizmo.Camera == null ) return;
Gizmo.Draw.Text( Score.ToString(), new Transform( WorldPosition + Vector3.Up * 32f ), "Roboto", 36f );
}
public void OnSave( PersistentItem item )
{
item.SetSaveData( "TeamColor", TeamColor );
}
public void OnLoad( PersistentItem item )
{
TeamColor = item.GetSaveData<Team>( "TeamColor" );
UpdateModel();
}
}