PaintBot.cs
using Sandbox;
using System;
using System.Linq;

/// <summary>
/// Автоматичний тестовий бот/турель, який знаходить гравця ворожої команди
/// та стріляє в нього слизом для перевірки механік влучання та смерті.
/// </summary>
public sealed class PaintBot : Component
{
	/// <summary>
	/// Команда бота: 0 (Синя) або 1 (Червона). За замовчуванням ворожа (1 - Червона).
	/// </summary>
	[Property, Category( "Settings" )] 
	public int TeamId { get; set; } = 1;

	/// <summary>
	/// Частота стрільби (кожні X секунд)
	/// </summary>
	[Property, Category( "Settings" )] 
	public float FireRate { get; set; } = 1.5f;

	/// <summary>
	/// Швидкість польоту снаряда
	/// </summary>
	[Property, Category( "Settings" )] 
	public float ProjectileSpeed { get; set; } = 1200f;

	/// <summary>
	/// Префаб декалі слизу, який буде спавнитися при влучанні
	/// </summary>
	[Property, Category( "Settings" )] 
	public GameObject DecalPrefab { get; set; }

	/// <summary>
	/// Колір слизу бота. Автоматично оновлюється під команду при старті.
	/// </summary>
	[Property, Category( "Colors" )]
	public Color PaintColor { get; set; } = new Color( 1f, 0f, 0f );

	private TimeSince timeSinceLastShot;

	protected override void OnStart()
	{
		timeSinceLastShot = 0f;
		
		// Автоматично підбираємо колір під обрану команду
		if ( TeamId == 0 )
			PaintColor = new Color( 0f, 0.3f, 1f ); // Яскравий синій
		else
			PaintColor = new Color( 1f, 0f, 0f ); // Яскравий червоний
	}

	protected override void OnUpdate()
	{
		// Бот стріляє тільки якщо раунд на карті активний
		var gameManager = Scene.GetAllComponents<GameManager>().FirstOrDefault();
		if ( gameManager.IsValid() && !gameManager.IsRoundActive ) return;

		if ( timeSinceLastShot >= FireRate )
		{
			ShootAtEnemyPlayer();
			timeSinceLastShot = 0f;
		}
	}

	private void ShootAtEnemyPlayer()
	{
		// Знаходимо локального гравця на сцені
		var targetPlayer = Scene.GetAllComponents<PlayerController>().FirstOrDefault( x => !x.IsProxy );
		if ( !targetPlayer.IsValid() ) return;

		// Переконуємось, що гравець належить до ворожої команди (інакше не стріляємо)
		var playerTeam = targetPlayer.Components.Get<PlayerTeam>();
		if ( playerTeam.IsValid() )
		{
			if ( playerTeam.TeamId == TeamId )
			{
				return; // Це наш союзник, не стріляємо
			}
			
			if ( playerTeam.IsDead )
			{
				return; // Не стріляємо в уже мертвих гравців
			}
		}

		// Розраховуємо напрямок стрільби в голову гравця
		Vector3 targetPos = targetPlayer.EyePosition;
		Vector3 startPos = WorldPosition;
		Vector3 direction = ( targetPos - startPos ).Normal;

		// Створюємо об'єкт снаряда
		var go = new GameObject();
		go.Name = "BotPaintProjectile";
		go.WorldPosition = startPos + direction * 50f;
		go.LocalScale = Vector3.One * 0.12f;

		var renderer = go.Components.Create<ModelRenderer>();
		renderer.Model = Model.Load( "models/dev/sphere.vmdl" );
		renderer.Tint = PaintColor;

		// Додаємо і налаштовуємо скрипт снаряда
		var paint = go.Components.Create<PaintProjectile>();
		paint.DecalPrefab = DecalPrefab;
		paint.PaintColor = PaintColor;
		paint.TeamId = TeamId;
		paint.Shooter = GameObject; // Бот є стрільцем, кулі ігноруватимуть його тіло
		paint.Velocity = direction * ProjectileSpeed;
	}
}