Component that manages arena shrinking over a round. It tracks round time, triggers a short warning period for outer tiles, highlights them, and then drops them to shrink the playable area.
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class ArenaShrinker : Component
{
[Property] public float RoundTime { get; set; } = 120f;
[Property] public float ShrinkInterval { get; set; } = 15f;
[Property] public float WarningDuration { get; set; } = 3f;
public float TimeRemaining { get; private set; }
private float nextShrinkTime;
private bool isWarningActive = false;
private List<ArenaTile> activeWarningTiles = new();
protected override void OnStart()
{
StartRound();
}
public void StartRound()
{
TimeRemaining = RoundTime;
nextShrinkTime = RoundTime - ShrinkInterval;
isWarningActive = false;
activeWarningTiles.Clear();
var tiles = Scene.GetAllComponents<ArenaTile>();
foreach ( var tile in tiles )
{
tile.ResetTile();
}
Log.Info( "[ArenaShrinker] Round started!" );
}
protected override void OnUpdate()
{
if ( TimeRemaining <= 0 )
{
if ( RoundManager.Instance != null && RoundManager.Instance.IsInRound )
{
_ = RoundManager.Instance.EndRound();
}
return;
}
TimeRemaining -= Time.Delta;
if ( !isWarningActive && TimeRemaining <= (nextShrinkTime + WarningDuration) )
{
TriggerWarning();
}
if ( isWarningActive && TimeRemaining <= nextShrinkTime )
{
DropWarnedTiles();
}
}
private void TriggerWarning()
{
isWarningActive = true;
activeWarningTiles.Clear();
var availableTiles = Scene.GetAllComponents<ArenaTile>()
.Where( t => !t.IsFalling )
.ToList();
if ( availableTiles.Count <= 2 ) return;
Vector3 center = Vector3.Zero;
foreach ( var tile in availableTiles )
{
center += tile.WorldPosition;
}
center /= availableTiles.Count;
var sortedTiles = availableTiles
.OrderByDescending( t => Vector3.DistanceBetween( t.WorldPosition, center ) )
.ToList();
int tilesToDropCount = Math.Max( 1, (int)(sortedTiles.Count * 0.25f) );
if ( sortedTiles.Count - tilesToDropCount < 2 )
{
tilesToDropCount = Math.Max( 0, sortedTiles.Count - 2 );
}
if ( tilesToDropCount <= 0 ) return;
activeWarningTiles = sortedTiles.Take( tilesToDropCount ).ToList();
foreach ( var tile in activeWarningTiles )
{
tile.HighlightWarning( true );
}
Log.Info( $"[ArenaShrinker] Warning started for {activeWarningTiles.Count} outer tiles!" );
}
private void DropWarnedTiles()
{
foreach ( var tile in activeWarningTiles )
{
tile.HighlightWarning( false );
tile.Drop();
}
activeWarningTiles.Clear();
isWarningActive = false;
nextShrinkTime -= ShrinkInterval;
Log.Info( "[ArenaShrinker] Outer ring dropped successfully!" );
}
}