Sboku/FindNearestAlivePlayerCondition.cs
using Sandbox;
using Sandbox.Sboku;
using Sandbox.Sboku.Shared;
using Sandbox.Shared;
using SWB.Demo;
using SWB.Player;
using SWB.Shared;
using System.Linq;
using System.Numerics;
using static Sandbox.PhysicsContact;

public sealed class FindNearestAlivePlayerCondition : ISbokuCondition
{
    private readonly SbokuBase bot;

    public FindNearestAlivePlayerCondition(SbokuBase bot)
    {
        this.bot = bot;
    }

    public bool If()
    {
        //Log.Info($"[BOT {((BotAdapter)bot).BotIndex}] Condition.If() called");

        var players = Game.ActiveScene.GetAllComponents<IPlayerBase>()
        .Where(p => p.IsAlive && p.IsValid && p is not BotAdapter)
        .ToList();

        //Log.Info($"[BOT {((BotAdapter)bot).BotIndex}] Alive players found: {players.Count}");

        return players.Count > 0;
    }


    public void Then()
    {
        IPlayerBase best = null;
        float bestDist = float.MaxValue;

        var players = Game.ActiveScene.GetAllComponents<IPlayerBase>()
        .Where(p => p.IsAlive && p.IsValid && p is not BotAdapter)
        .ToList();

        if (players.Count == 0)
        {
            Log.Info($"[BOT {((BotAdapter)bot).BotIndex}] Then(): NO PLAYERS");
            return;
        }


        foreach (var player in players)
        {
            
            float dist = Vector3.DistanceBetween(
                bot.EyePos,
                player.EyePos
            );

            if (dist < bestDist && dist < bot.SearchRange)
            {
                
                bestDist = dist;
                best = player;
            }
        }

        if (best is ISbokuTarget tgt)
            bot.Target = tgt;
        //Log.Info($"[BOT {((BotAdapter)bot).BotIndex}] TARGET SET -> {best.GameObject.Id}");
    }

    public bool IsTerminal() => false; // bot.Target != null;
}