OMRBotController.UtilityBrain.cs
using Sandbox;
using System;
using System.Collections.Generic;

/// <summary>
/// BOT 7D - Utility Combat Brain.
///
/// BOT 6.1/7C already supplied a large vocabulary of useful behaviours, but the
/// old coordinator was mostly a priority chain. BOT 7D makes the *intent* a
/// scored decision while deliberately reusing the proven cover/flank/search/
/// suppression/execution systems underneath it.
///
/// Hard rules (High Noon, impossible actions, empty weapons and immediate blind
/// incoming-fire reactions) remain hard rules. Everything else competes in one
/// inspectable decision frame with commitment/hysteresis so the bot does not
/// change its mind every few frames.
/// </summary>
public sealed partial class OMRBotController
{
	[Property, Group( "BOT 7D / Utility Brain" )]
	public bool UtilityCombatBrainEnabled { get; set; } = true;

	[Property, Group( "BOT 7D / Utility Brain" )]
	public float UtilityDecisionInterval { get; set; } = 0.28f;

	[Property, Group( "BOT 7D / Utility Brain" )]
	public float UtilityMinimumCommitTime { get; set; } = 0.65f;

	[Property, Group( "BOT 7D / Utility Brain" ), Range( 0f, 0.35f )]
	public float UtilitySwitchMargin { get; set; } = 0.08f;

	[Property, Group( "BOT 7D / Utility Brain" ), Range( 0f, 0.35f )]
	public float UtilityCurrentIntentBonus { get; set; } = 0.08f;

	[Property, Group( "BOT 7D / Utility Brain" )]
	public float UtilityRepositionDistance { get; set; } = 230f;

	[Property, Sync( SyncFlags.FromHost )]
	public string UtilityBrainStatus { get; set; } = "LEGACY";

	private OMRBotIntentKind _utilitySelectedIntent = OMRBotIntentKind.None;
	private float _utilitySelectedScore;
	private string _utilitySelectedReason = string.Empty;
	private Vector3 _utilitySelectedTarget = Vector3.Zero;
	private bool _utilityHasSelectedTarget;
	private float _utilityTargetChosenAt = -999f;
	private float _utilitySelectedAt = -999f;
	private float _utilityCommitUntil = -999f;
	private float _nextUtilityDecisionAt;
	private float _utilityRangeScale = 1f;
	private float _utilityCoverFailureUntil = -999f;
	private float _utilityFlankFailureUntil = -999f;
	private float _utilityPositionFailureUntil = -999f;

	private bool TryHandleTacticalPriority( OneMoreRoundWeapon weapon )
	{
		if ( UtilityCombatBrainEnabled )
			return TryHandleUtilityCombatBrain( weapon );

		UtilityBrainStatus = "LEGACY";
		return TryHandleLegacyTacticalPriority( weapon );
	}

	private bool TryHandleUtilityCombatBrain( OneMoreRoundWeapon weapon )
	{
		if ( !UsesTacticalBehavior() )
			return false;

		bool hasVisual = _hasConfirmedSight && _perceptionTarget?.IsAlive == true;
		bool recentThreat = HasRecentThreat();

		if ( hasVisual && _searchPoints.Count > 0 )
			ClearSearchPlan( "CONTACT" );

		// Level-2 emergency from the architecture audit: being hit from an unseen
		// direction gets an immediate orient/evasion window before deliberation.
		if ( recentThreat && !hasVisual && Time.Now < _threatOrientUntil )
		{
			bool evadeNow = ShouldEvadeBlindThreatImmediately();
			SetForcedUtilityDecision(
				evadeNow ? OMRBotIntentKind.Disengage : OMRBotIntentKind.Reposition,
				1f,
				evadeNow ? "REPEATED BLIND FIRE" : "LOCATE INCOMING FIRE"
			);

			if ( evadeNow )
				UpdateUnderFireReaction( weapon );
			else
				UpdateBlindThreatLocateReaction( weapon );
			return true;
		}

		// Raw LOS is not usable combat information until BOT 2's reaction delay has
		// elapsed. Let the existing reaction gate own this frame rather than allowing
		// a belief/search action to accidentally bypass human-like reaction timing.
		if ( PerceptionStatus == "REACTING" )
		{
			UtilityBrainStatus = "REACTION GATE";
			return false;
		}

		if ( Time.Now >= _nextUtilityDecisionAt || !IsUtilityIntentStillLegal( _utilitySelectedIntent, weapon ) )
			EvaluateUtilityCombatDecision( weapon );

		return ExecuteUtilityCombatDecision( weapon );
	}

	private void EvaluateUtilityCombatDecision( OneMoreRoundWeapon weapon )
	{
		RefreshBrainArchitecture( weapon );
		_brainDecisionFrame.Clear();
		_utilityRangeScale = 1f;

		float interval = MathF.Max( 0.12f, UtilityDecisionInterval );
		interval *= Difficulty switch
		{
			OMRBotDifficulty.Easy => 1.30f,
			OMRBotDifficulty.Hard => 0.82f,
			_ => 1f
		};
		_nextUtilityDecisionAt = Time.Now + interval;

		OMRBotBlackboard bb = _brainBlackboard;
		OMRBotWeaponDoctrine doctrine = GetWeaponDoctrine( weapon );
		OMRBotMatchupContext matchup = WeaponMatchupReasoningEnabled ? ( bb.Matchup ?? OMRBotMatchupContext.Neutral ) : OMRBotMatchupContext.Neutral;
		bool hasVisual = bb.HasConfirmedSight;
		bool hasMemory = bb.HasVisualMemory;
		bool hasAudio = bb.HasActionableAudio;
		bool recentThreat = bb.HasRecentThreat;
		bool hasBelief = bb.HasOpponentBelief;
		bool hasEvidence = hasVisual || hasMemory || hasAudio || recentThreat || hasBelief;
		bool needsReload = UtilityNeedsReload( weapon );
		float health = MathX.Clamp( bb.OwnHealthFraction, 0f, 1f );
		float aggression = MathX.Clamp( bb.TacticalAggression, 0f, 1f );
		float caution = 1f - aggression;
		float desiredRange = MathF.Max( 80f, doctrine.LegacyHoldDistance * matchup.DesiredRangeScale );
		float distance = MathF.Max( 0f, bb.TargetDistance );
		float rangeFit = hasVisual || hasMemory || hasAudio || hasBelief
			? 1f - MathX.Clamp( MathF.Abs( distance - desiredRange ) / desiredRange, 0f, 1f )
			: 0.5f;
		float tooClose = hasVisual
			? MathX.Clamp( ( desiredRange - distance ) / desiredRange, 0f, 1f )
			: 0f;
		float tooFar = hasVisual
			? MathX.Clamp( ( distance - desiredRange ) / MathF.Max( desiredRange, 1f ), 0f, 1f )
			: 0f;
		float adaptive = MathX.Clamp( bb.OpponentModel.Confidence, 0f, 1f );
		float tempo = MathX.Clamp( bb.Rule.TempoPressure, 0f, 1f );
		float currentRisk = MathX.Clamp( GetSpatialMemoryRiskAt( WorldPosition ) / 8f, 0f, 1f );
		int blindThreatHits = !hasVisual ? GetRecentBlindThreatHits() : 0;
		float blindThreatEscalation = MathX.Clamp( ( blindThreatHits - 1 ) / 2f, 0f, 1f );
		bool automaticWeapon = weapon?.IsAutomaticWeapon == true;

		float engage = hasVisual
			? 0.40f + aggression * 0.22f + rangeFit * 0.20f + tempo * 0.10f
			: -1f;
		engage += _dynamicPressureBias * 0.07f;
		engage += matchup.EngageAdjustment;
		if ( automaticWeapon ) engage += 0.14f;
		engage -= ( 1f - health ) * 0.16f;
		if ( bb.KnowsOpponentHealth )
			engage += ( 1f - bb.OpponentHealthFraction ) * 0.10f;
		if ( weapon?.Clip1 > 0 ) engage += 0.06f;
		if ( needsReload ) engage -= 0.65f;
		AddUtilityCandidate( OMRBotIntentKind.Engage, engage, hasVisual, "VISIBLE / RANGE FIT" );

		float press = hasVisual
			? 0.16f + doctrine.ClosePressureBias * 0.34f + tooFar * 0.32f + aggression * 0.18f + tempo * 0.14f
			: -1f;
		press += _dynamicPressureBias * 0.08f;
		press += matchup.PressureAdjustment;
		if ( automaticWeapon ) press += 0.10f;
		press -= ( 1f - health ) * 0.16f;
		press -= doctrine.PrecisionBias * 0.10f;
		if ( needsReload ) press -= 0.75f;
		AddUtilityCandidate( OMRBotIntentKind.Press, press, hasVisual, "CLOSE RANGE / PRESSURE" );

		float openDistance = hasVisual
			? 0.10f + doctrine.PrecisionBias * 0.28f + doctrine.CoverCycleBias * 0.12f + tooClose * 0.42f
			: -1f;
		if ( doctrine.PreserveSpaceAgainstRush ) openDistance += 0.10f;
		openDistance += matchup.OpenDistanceAdjustment;
		openDistance += MathF.Max( 0f, bb.OpponentModel.Aggression - 0.52f ) * adaptive * 0.16f;
		openDistance += ( 1f - health ) * 0.10f;
		AddUtilityCandidate( OMRBotIntentKind.OpenDistance, openDistance, hasVisual, "CREATE SPACE" );

		float cover = hasEvidence
			? 0.10f + ( 1f - health ) * 0.38f + doctrine.CoverCycleBias * 0.18f + caution * 0.14f
			: 0.04f;
		cover += recentThreat
			? MathX.Lerp( 0.08f, 0.30f, hasVisual ? 1f : blindThreatEscalation )
			: 0f;
		cover += needsReload ? 0.52f : 0f;
		cover += currentRisk * 0.13f;
		cover += _dynamicCoverBias * 0.08f;
		cover += bb.Rule.AmmoDiscipline * 0.08f;
		cover += matchup.CoverAdjustment;
		if ( automaticWeapon && health > 0.55f && !recentThreat && !needsReload )
			cover -= 0.06f;
		if ( Time.Now < _utilityCoverFailureUntil ) cover -= 0.50f;
		AddUtilityCandidate( OMRBotIntentKind.TakeCover, cover, hasEvidence, needsReload ? "SAFE RELOAD" : "SURVIVAL / COVER" );

		float disengage = ( hasVisual || recentThreat ) && health < 0.58f
			? 0.12f + MathX.Clamp( ( 0.58f - health ) / 0.58f, 0f, 1f ) * 0.48f +
				( recentThreat ? MathX.Lerp( 0.04f, 0.18f, hasVisual ? 1f : blindThreatEscalation ) : 0f )
			: -1f;
		disengage += doctrine.PrecisionBias * 0.10f;
		disengage += matchup.OpenDistanceAdjustment * 0.45f;
		disengage -= tempo * 0.16f;
		AddUtilityCandidate( OMRBotIntentKind.Disengage, disengage, disengage >= 0f, "LOW HEALTH / BREAK CONTACT" );

		float reposition = hasVisual
			? 0.16f + ( 1f - rangeFit ) * 0.10f + currentRisk * 0.18f + doctrine.CoverCycleBias * 0.10f
			: -1f;
		if ( _confirmedSightStartedAt > 0f )
			reposition += MathX.Clamp( ( Time.Now - _confirmedSightStartedAt - 1.5f ) / 4f, 0f, 1f ) * 0.12f;
		reposition += MathF.Abs( _dynamicMovementBias ) * 0.06f;
		reposition += matchup.RepositionAdjustment;
		if ( automaticWeapon && rangeFit > 0.45f ) reposition -= 0.04f;
		if ( Time.Now < _utilityPositionFailureUntil ) reposition -= 0.35f;
		AddUtilityCandidate( OMRBotIntentKind.Reposition, reposition, hasVisual, "CHANGE ANGLE / SPACE" );

		bool flankLegal =
			hasVisual &&
			weapon?.IsValid == true &&
			!weapon.IsReloading &&
			weapon.Clip1 > 0 &&
			!recentThreat &&
			Time.Now >= _nextFlankDecisionAt &&
			Time.Now >= _utilityFlankFailureUntil &&
			_confirmedSightStartedAt > 0f &&
			Time.Now - _confirmedSightStartedAt >= MathF.Max( 0.65f, FlankMinimumContactTime * 0.72f );

		if ( flankLegal )
		{
			float contact = MathX.Clamp( ( Time.Now - _confirmedSightStartedAt ) / 4f, 0f, 1f );
			float flankBase =
				0.14f +
				aggression * 0.17f +
				contact * 0.17f +
				( 1f - bb.OpponentModel.Mobility ) * adaptive * 0.15f +
				doctrine.ClosePressureBias * 0.07f +
				_dynamicFlankBias * 0.11f +
				matchup.FlankAdjustment -
				( 1f - health ) * 0.12f;

			Vector3 toThreat = ( _observedTargetPosition - WorldPosition ).WithZ( 0f );
			Vector3 lateral = toThreat.IsNearlyZero( 0.01f )
				? new Vector3( -WorldRotation.Forward.y, WorldRotation.Forward.x, 0f ).Normal
				: new Vector3( -toThreat.Normal.y, toThreat.Normal.x, 0f ).Normal;
			float sideProbeDistance = MathF.Max( 120f, FlankMinimumDistance );
			float leftRisk = GetSpatialMemoryRiskAt( WorldPosition + lateral * sideProbeDistance );
			float rightRisk = GetSpatialMemoryRiskAt( WorldPosition - lateral * sideProbeDistance );
			float leftScore = flankBase - MathX.Clamp( leftRisk / 8f, 0f, 1f ) * 0.16f;
			float rightScore = flankBase - MathX.Clamp( rightRisk / 8f, 0f, 1f ) * 0.16f;
			if ( _flankSide < 0 ) leftScore += 0.025f; else rightScore += 0.025f;
			AddUtilityCandidate( OMRBotIntentKind.FlankLeft, leftScore, true, "ANGLE CHANGE / LEFT" );
			AddUtilityCandidate( OMRBotIntentKind.FlankRight, rightScore, true, "ANGLE CHANGE / RIGHT" );
		}

		bool suppressionLegal =
			!hasVisual &&
			hasMemory &&
			doctrine.SupportsSuppressiveFire &&
			weapon?.IsValid == true &&
			weapon.Clip1 > 1 &&
			!weapon.IsReloading &&
			Time.Now - _lastConfirmedSeenAt <= MathF.Max( 0.35f, SuppressionMemoryWindow * 1.15f );
		float suppression = suppressionLegal
			? 0.24f + aggression * 0.12f + ( 1f - MathX.Clamp( bb.LastSeenAge / MathF.Max( 0.4f, SuppressionMemoryWindow ), 0f, 1f ) ) * 0.18f
			: -1f;
		AddUtilityCandidate( OMRBotIntentKind.Suppress, suppression, suppressionLegal, "DENY RE-PEEK" );

		bool beliefEvidence = hasBelief && bb.OpponentBeliefConfidence >= 0.055f;
		bool searchLegal = !hasVisual && hasEvidence;
		float search = searchLegal
			? 0.34f + ( hasMemory ? 0.13f : 0f ) + bb.LastHeardConfidence * 0.14f + bb.OpponentBeliefConfidence * 0.40f + tempo * 0.08f
			: -1f;
		if ( recentThreat )
			search += !hasVisual && blindThreatHits <= 1 ? 0.15f : 0.06f;
		AddUtilityCandidate( OMRBotIntentKind.Search, search, searchLegal, "FOLLOW EVIDENCE" );

		bool interceptLegal =
			!hasVisual &&
			beliefEvidence &&
			!_lastSeenDirection.WithZ( 0f ).IsNearlyZero( 0.01f ) &&
			bb.OpponentBeliefConfidence >= MathF.Max( 0.06f, BeliefInterceptMinimumConfidence );
		float intercept = interceptLegal
			? 0.24f + bb.OpponentBeliefConfidence * 0.45f + bb.OpponentModel.Mobility * adaptive * 0.16f + aggression * 0.08f + bb.OpponentModel.RoutePredictionConfidence * 0.15f
			: -1f;
		AddUtilityCandidate( OMRBotIntentKind.Intercept, intercept, interceptLegal, "BELIEF / CUT ROUTE", bb.OpponentBeliefPosition, interceptLegal );

		bool ambushLegal =
			!hasVisual &&
			beliefEvidence &&
			bb.TargetDistance <= MathF.Max( 180f, desiredRange * 1.15f );
		float ambush = ambushLegal
			? 0.18f + doctrine.PrecisionBias * 0.18f + doctrine.CoverCycleBias * 0.14f + bb.OpponentModel.Aggression * adaptive * 0.14f + bb.OpponentBeliefConfidence * 0.25f + matchup.AmbushAdjustment + bb.OpponentModel.RepeekTendency * 0.09f + bb.OpponentModel.PushTendency * 0.06f
			: -1f;
		AddUtilityCandidate( OMRBotIntentKind.HoldAmbush, ambush, ambushLegal, "LIKELY APPROACH / HOLD", bb.OpponentBeliefPosition, ambushLegal );

		bool reloadLegal = needsReload;
		float reload = reloadLegal ? ( hasEvidence ? 0.96f : 0.76f ) + matchup.ReloadSafetyAdjustment : -1f;
		AddUtilityCandidate( OMRBotIntentKind.ReloadSafely, reload, reloadLegal, "AMMO CRITICAL" );

		if ( needsReload )
		{
			foreach ( OMRBotDecisionCandidate candidate in _brainDecisionFrame.Candidates )
			{
				if ( candidate.Intent != OMRBotIntentKind.ReloadSafely || !candidate.Legal )
					continue;

				SetSelectedUtilityDecision(
					candidate.Intent,
					1f,
					candidate.Reason,
					candidate.TargetPosition,
					candidate.HasTargetPosition
				);
				return;
			}
		}

		SelectUtilityCandidate();
	}

	private void AddUtilityCandidate(
		OMRBotIntentKind intent,
		float rawScore,
		bool legal,
		string reason,
		Vector3 target = default,
		bool hasTarget = false
	)
	{
		if ( legal )
			rawScore += GetCombatSequenceUtilityAdjustment( intent );

		float score = legal ? MathX.Clamp( rawScore, 0f, 1f ) : 0f;
		if ( legal && intent == _utilitySelectedIntent )
			score = MathX.Clamp( score + MathF.Max( 0f, UtilityCurrentIntentBonus ), 0f, 1f );

		OMRBotDecisionCandidate candidate = _brainDecisionFrame.RentCandidate();
		candidate.Intent = intent;
		candidate.Score = score;
		candidate.Legal = legal;
		candidate.Reason = reason ?? string.Empty;
		candidate.TargetPosition = target;
		candidate.HasTargetPosition = hasTarget;
	}

	private void SelectUtilityCandidate()
	{
		OMRBotDecisionCandidate best = null;
		OMRBotDecisionCandidate current = null;

		foreach ( OMRBotDecisionCandidate candidate in _brainDecisionFrame.Candidates )
		{
			if ( !candidate.Legal )
				continue;

			if ( candidate.Intent == _utilitySelectedIntent )
				current = candidate;

			if ( best is null || candidate.Score > best.Score )
				best = candidate;
		}

		if ( best is null )
		{
			SetSelectedUtilityDecision( OMRBotIntentKind.None, 0f, "NO LEGAL ACTION", Vector3.Zero, false );
			return;
		}

		if (
			current is not null &&
			best.Intent != current.Intent &&
			Time.Now < _utilityCommitUntil &&
			best.Score < current.Score + MathF.Max( 0f, UtilitySwitchMargin )
		)
		{
			best = current;
		}

		SetSelectedUtilityDecision(
			best.Intent,
			best.Score,
			best.Reason,
			best.TargetPosition,
			best.HasTargetPosition
		);
	}

	private void SetSelectedUtilityDecision(
		OMRBotIntentKind intent,
		float score,
		string reason,
		Vector3 target,
		bool hasTarget
	)
	{
		bool changed = intent != _utilitySelectedIntent;
		if ( changed )
		{
			_utilitySelectedAt = Time.Now;
			_utilityCommitUntil = Time.Now + MathF.Max( 0.18f, UtilityMinimumCommitTime );
			ApplyUtilityIntentTransition( _utilitySelectedIntent, intent );
		}

		_utilitySelectedIntent = intent;
		_utilitySelectedScore = score;
		_utilitySelectedReason = reason ?? string.Empty;
		if ( changed || hasTarget )
		{
			_utilitySelectedTarget = target;
			_utilityHasSelectedTarget = hasTarget;
			_utilityTargetChosenAt = hasTarget ? Time.Now : -999f;
		}
		_brainDecisionFrame.SelectedIntent = intent;
		_brainDecisionFrame.SelectedScore = score;
		_brainDecisionFrame.SelectionReason = _utilitySelectedReason;

		UtilityBrainStatus =
			$"{intent.ToString().ToUpperInvariant()} {score * 100f:0}% · {_utilitySelectedReason}";
	}

	private void SetForcedUtilityDecision( OMRBotIntentKind intent, float score, string reason )
	{
		_brainDecisionFrame.Clear();
		AddUtilityCandidate( intent, score, true, reason );
		SetSelectedUtilityDecision( intent, score, reason, Vector3.Zero, false );
	}

	private void ApplyUtilityIntentTransition( OMRBotIntentKind from, OMRBotIntentKind to )
	{
		if ( from == to )
			return;

		bool wantsCover = to == OMRBotIntentKind.TakeCover || to == OMRBotIntentKind.ReloadSafely;
		bool wantsFlank = to == OMRBotIntentKind.FlankLeft || to == OMRBotIntentKind.FlankRight;
		bool wantsSearch = to == OMRBotIntentKind.Search || to == OMRBotIntentKind.Intercept || to == OMRBotIntentKind.HoldAmbush;

		if ( _hasCoverTarget && !wantsCover )
			ClearCoverPlan( $"UTILITY → {to.ToString().ToUpperInvariant()}" );
		if ( _flankActive && !wantsFlank )
			CancelFlank( $"UTILITY → {to.ToString().ToUpperInvariant()}" );
		if ( _searchPoints.Count > 0 && !wantsSearch )
			ClearSearchPlan( $"UTILITY → {to.ToString().ToUpperInvariant()}" );
	}

	private bool ExecuteUtilityCombatDecision( OneMoreRoundWeapon weapon )
	{
		bool hasVisual = _hasConfirmedSight && _perceptionTarget?.IsAlive == true;
		OMRBotMatchupContext matchup = ActiveMatchup;
		_utilityRangeScale = matchup.DesiredRangeScale;

		switch ( _utilitySelectedIntent )
		{
			case OMRBotIntentKind.ReloadSafely:
				return ExecuteUtilitySafeReload( weapon );

			case OMRBotIntentKind.TakeCover:
				return ExecuteUtilityCover( weapon, "UTILITY" );

			case OMRBotIntentKind.FlankLeft:
				return ExecuteUtilityFlank( weapon, -1 );

			case OMRBotIntentKind.FlankRight:
				return ExecuteUtilityFlank( weapon, 1 );

			case OMRBotIntentKind.Suppress:
				if ( TryHandleSuppressiveFire( weapon ) )
					return true;
				_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
				return TryHandleBeliefDrivenSearch( weapon );

			case OMRBotIntentKind.Search:
				return TryHandleBeliefDrivenSearch( weapon );

			case OMRBotIntentKind.Intercept:
				return ExecuteUtilityIntercept( weapon );

			case OMRBotIntentKind.HoldAmbush:
				return ExecuteUtilityAmbush( weapon );

			case OMRBotIntentKind.Disengage:
				if ( hasVisual )
					return ExecuteUtilityTacticalMove( weapon, OMRBotTacticalQueryPurpose.Retreat, "DISENGAGE" );
				return TryHandleBeliefDrivenSearch( weapon );

			case OMRBotIntentKind.Reposition:
				if ( hasVisual )
					return ExecuteUtilityTacticalMove( weapon, OMRBotTacticalQueryPurpose.Reposition, "REPOSITION" );
				return TryHandleBeliefDrivenSearch( weapon );

			case OMRBotIntentKind.OpenDistance:
				_utilityRangeScale = matchup.DesiredRangeScale * 1.28f;
				if ( hasVisual && _brainBlackboard.TargetDistance < GetCombatHoldDistance( weapon ) * 0.92f )
				{
					if ( ExecuteUtilityTacticalMove( weapon, OMRBotTacticalQueryPurpose.OpenDistance, "OPEN DISTANCE" ) )
						return true;
				}
				return false;

			case OMRBotIntentKind.Press:
				_utilityRangeScale = matchup.DesiredRangeScale * 0.76f;
				return false;

			case OMRBotIntentKind.Engage:
			default:
				_utilityRangeScale = matchup.DesiredRangeScale;
				return false;
		}
	}

	private bool ExecuteUtilitySafeReload( OneMoreRoundWeapon weapon )
	{
		if ( weapon?.IsValid != true )
			return false;

		bool hasEvidence =
			_hasConfirmedSight || HasFreshMemory() || HasRecentThreat() || HasFreshHearing() || HasOpponentBelief();

		if ( hasEvidence )
		{
			if ( ExecuteUtilityCover( weapon, "RELOAD" ) )
				return true;
		}

		StopMovementOnly();
		ReleaseBotWeaponIntent( weapon );
		if ( !weapon.IsReloading )
			weapon.BotTryReload();
		CombatStatus = weapon.IsReloading ? "UTILITY / SAFE RELOAD" : "UTILITY / RELOAD";
		TacticalStatus = "RELOAD / NO COVER NEEDED";
		return true;
	}

	private bool ExecuteUtilityCover( OneMoreRoundWeapon weapon, string reason )
	{
		if ( _hasCoverTarget )
			return UpdateActiveCoverPlan( weapon );

		if ( Time.Now < _utilityCoverFailureUntil || Time.Now < _nextCoverDecisionAt )
			return false;

		if (
			!TryResolveThreatReference( out Vector3 threatReference, out Vector3 threatDirection ) ||
			!TryChooseCoverPosition( weapon, threatReference, threatDirection, out OMRCoverChoice cover )
		)
		{
			_utilityCoverFailureUntil = Time.Now + 0.75f;
			_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
			CoverStatus = "UTILITY / NO COVER";
			return false;
		}

		_hasCoverTarget = true;
		_coverTarget = cover.Anchor;
		_coverReason = reason;
		_coverHoldUntil = -1f;
		_coverPosture = cover.Posture;
		_coverLeftPeek = cover.LeftPeek;
		_coverRightPeek = cover.RightPeek;
		_coverHasLeftPeek = cover.HasLeftPeek;
		_coverHasRightPeek = cover.HasRightPeek;
		_coverPeekActive = false;
		_coverPeekReturning = false;
		_coverPeekUntil = -1f;
		_nextCoverPeekAt = Time.Now + 0.12f;
		_nextCoverDecisionAt = Time.Now + MathF.Max( 0.5f, CoverDecisionCooldown );
		ClearSearchPlan( "UTILITY COVER" );
		CancelFlank( "UTILITY COVER" );
		TacticalStatus = $"UTILITY COVER / {reason}";
		CoverStatus = $"SEEKING {reason}";
		return UpdateActiveCoverPlan( weapon );
	}

	private bool ExecuteUtilityFlank( OneMoreRoundWeapon weapon, int side )
	{
		if ( _flankActive )
			return TryHandleFlankPlan( weapon );

		if ( Time.Now < _nextFlankDecisionAt )
		{
			_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
			return false;
		}

		if ( Time.Now < _utilityFlankFailureUntil )
			return false;

		if ( !TryChooseFlankPositionForSide( weapon, side, out Vector3 flank ) )
		{
			// One tactical side can legitimately be sealed by geometry. Try the other
			// before abandoning the intent; position quality still comes from BOT 7C.
			if ( !TryChooseFlankPositionForSide( weapon, -side, out flank ) )
			{
				_utilityFlankFailureUntil = Time.Now + 1.15f;
				_nextFlankDecisionAt = Time.Now + 1.35f;
				_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
				FlankStatus = "UTILITY / NO ROUTE";
				return false;
			}
			side = -side;
		}

		_flankActive = true;
		_flankTarget = flank;
		_flankSide = -side;
		_nextFlankDecisionAt = Time.Now + MathF.Max( 1.6f, FlankDecisionCooldown );
		FlankStatus = side < 0 ? "UTILITY / LEFT" : "UTILITY / RIGHT";
		ClearSearchPlan( "UTILITY FLANK" );
		return TryHandleFlankPlan( weapon );
	}

	private bool TryChooseFlankPositionForSide( OneMoreRoundWeapon weapon, int side, out Vector3 best )
	{
		best = Vector3.Zero;
		if ( Scene?.NavMesh is null || _perceptionTarget?.IsAlive != true )
			return false;

		Vector3 toTarget = ( _observedTargetPosition - WorldPosition ).WithZ( 0f );
		if ( toTarget.IsNearlyZero( 0.01f ) )
			return false;

		Vector3 forward = toTarget.Normal;
		Vector3 lateral = new Vector3( -forward.y, forward.x, 0f ).Normal;
		float hold = GetCombatHoldDistance( weapon );
		float minDistance = MathF.Max( 140f, FlankMinimumDistance );
		float maxDistance = MathF.Max( minDistance + 60f, FlankMaximumDistance );
		float[] distances = { minDistance, ( minDistance + maxDistance ) * 0.5f, maxDistance };
		List<Vector3> candidates = new();

		foreach ( float distance in distances )
		{
			candidates.Add(
				WorldPosition +
				lateral * side * distance +
				forward * MathF.Min( 90f, distance * 0.22f )
			);
		}

		AddAuthoredTacticalCandidates(
			candidates,
			OMRBotTacticalQueryPurpose.Flank,
			WorldPosition,
			maxDistance * 1.55f
		);

		OMRBotWeaponDoctrine doctrine = GetWeaponDoctrine( weapon );
		Vector3 threatReference = _observedTargetAimPoint.IsNearlyZero( 0.01f )
			? _observedTargetPosition + Vector3.Up * 46f
			: _observedTargetAimPoint;
		float bestScore = float.MinValue;
		OMRBotTacticalPositionEvaluation bestEvaluation = default;

		foreach ( Vector3 raw in candidates )
		{
			Vector3 candidateDirection = ( raw - WorldPosition ).WithZ( 0f );
			if ( !candidateDirection.IsNearlyZero( 0.01f ) )
			{
				float sideDot = candidateDirection.Normal.Dot( lateral ) * side;
				if ( sideDot < -0.20f )
					continue;
			}

			if (
				!TryEvaluateTacticalPosition(
					raw,
					OMRBotTacticalQueryPurpose.Flank,
					doctrine,
					threatReference,
					true,
					_observedTargetPosition,
					hold,
					out OMRBotTacticalPositionEvaluation evaluation
				)
			)
				continue;

			float score = evaluation.Score;
			if ( score <= bestScore )
				continue;

			bestScore = score;
			best = evaluation.Position;
			bestEvaluation = evaluation;
		}

		if ( bestScore == float.MinValue )
			return false;

		SetTacticalQueryDiagnostic( OMRBotTacticalQueryPurpose.Flank, bestEvaluation );
		return true;
	}

	private bool ExecuteUtilityTacticalMove(
		OneMoreRoundWeapon weapon,
		OMRBotTacticalQueryPurpose purpose,
		string label
	)
	{
		if ( !_hasConfirmedSight || _perceptionTarget?.IsAlive != true )
			return false;

		if ( Time.Now < _utilityPositionFailureUntil )
			return false;

		OMRBotTacticalPositionEvaluation evaluation = default;
		if (
			_utilityHasSelectedTarget &&
			( _utilitySelectedTarget - WorldPosition ).WithZ( 0f ).Length <= 42f
		)
		{
			_utilityHasSelectedTarget = false;
			_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
			return false;
		}

		bool needTarget =
			!_utilityHasSelectedTarget ||
			Time.Now - _utilityTargetChosenAt > 1.25f;

		if ( needTarget )
		{
			if ( !TryChooseUtilityTacticalPosition( weapon, purpose, out evaluation ) )
			{
				_utilityPositionFailureUntil = Time.Now + 0.65f;
				_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
				_utilityHasSelectedTarget = false;
				return false;
			}

			_utilitySelectedTarget = evaluation.Position;
			_utilityHasSelectedTarget = true;
			_utilityTargetChosenAt = Time.Now;
			SetTacticalQueryDiagnostic( purpose, evaluation );
		}

		float enemyDistance = ( _observedTargetPosition - WorldPosition ).WithZ( 0f ).Length;
		float travelDistance = ( _utilitySelectedTarget - WorldPosition ).WithZ( 0f ).Length;
		UpdateCombatAim( _observedTargetAimPoint );
		bool wantsAds = ShouldUseAds( weapon, enemyDistance );
		CombatWantsAim = wantsAds;
		weapon?.SetBotAimIntent( wantsAds );

		NavigateToWorldPosition(
			_utilitySelectedTarget,
			$"UTILITY {label}",
			48f,
			$"UTILITY / {label}",
			allowSprint: false,
			faceMovement: false,
			advancedMovement: true,
			movementWeapon: weapon,
			visibleCombatTarget: true
		);

		UpdateVisibleWeaponAttack( weapon, enemyDistance, wantsAds );
		TacticalStatus = $"UTILITY / {label}";
		return true;
	}

	private bool TryChooseUtilityTacticalPosition(
		OneMoreRoundWeapon weapon,
		OMRBotTacticalQueryPurpose purpose,
		out OMRBotTacticalPositionEvaluation best
	)
	{
		best = default;
		if ( Scene?.NavMesh is null || !_hasConfirmedSight )
			return false;

		OMRBotWeaponDoctrine doctrine = GetWeaponDoctrine( weapon );
		Vector3 threatReference = _observedTargetAimPoint.IsNearlyZero( 0.01f )
			? _observedTargetPosition + Vector3.Up * 46f
			: _observedTargetAimPoint;
		float desiredRange = MathF.Max( 90f, doctrine.LegacyHoldDistance );
		float baseDistance = MathF.Max( 110f, UtilityRepositionDistance );
		List<Vector3> candidates = new();

		Vector3 fromThreat = ( WorldPosition - _observedTargetPosition ).WithZ( 0f );
		if ( fromThreat.IsNearlyZero( 0.01f ) )
			fromThreat = -WorldRotation.Forward.WithZ( 0f );
		fromThreat = fromThreat.Normal;
		Vector3 lateral = new Vector3( -fromThreat.y, fromThreat.x, 0f ).Normal;

		Vector3[] directions =
		{
			fromThreat,
			-fromThreat,
			lateral,
			-lateral,
			( fromThreat + lateral ).Normal,
			( fromThreat - lateral ).Normal,
			( -fromThreat + lateral ).Normal,
			( -fromThreat - lateral ).Normal
		};

		float[] radii = { baseDistance * 0.72f, baseDistance, baseDistance * 1.35f };
		foreach ( Vector3 direction in directions )
		{
			foreach ( float radius in radii )
				candidates.Add( WorldPosition + direction * radius );
		}

		AddAuthoredTacticalCandidates(
			candidates,
			purpose,
			WorldPosition,
			baseDistance * 2.25f
		);

		float bestScore = float.MinValue;
		foreach ( Vector3 candidate in candidates )
		{
			if (
				!TryEvaluateTacticalPosition(
					candidate,
					purpose,
					doctrine,
					threatReference,
					true,
					_observedTargetPosition,
					desiredRange,
					out OMRBotTacticalPositionEvaluation evaluation
				)
			)
				continue;

			if ( evaluation.Score <= bestScore )
				continue;

			bestScore = evaluation.Score;
			best = evaluation;
		}

		return bestScore != float.MinValue;
	}

	private bool ExecuteUtilityIntercept( OneMoreRoundWeapon weapon )
	{
		if ( _hasConfirmedSight )
			return false;

		Vector3 target = _utilityHasSelectedTarget ? _utilitySelectedTarget : Vector3.Zero;
		if ( !TryGetBeliefInterceptTarget( out Vector3 liveIntercept, out float confidence ) )
		{
			if ( target.IsNearlyZero( 0.01f ) )
				return TryHandleBeliefDrivenSearch( weapon );
		}
		else
		{
			target = liveIntercept;
		}

		float distance = ( target - WorldPosition ).WithZ( 0f ).Length;
		if ( distance <= 58f )
		{
			MarkOpponentBeliefAreaCleared( target );
			_nextUtilityDecisionAt = MathF.Min( _nextUtilityDecisionAt, Time.Now + 0.12f );
			return TryHandleBeliefDrivenSearch( weapon );
		}

		ReleaseBotWeaponIntent( weapon );
		NavigateToWorldPosition(
			target,
			"BELIEF INTERCEPT",
			58f,
			"UTILITY / INTERCEPT",
			allowSprint: distance > 250f,
			faceMovement: true,
			advancedMovement: true,
			movementWeapon: weapon,
			visibleCombatTarget: false
		);
		TacticalStatus = $"INTERCEPT / BELIEF {confidence * 100f:0}%";
		SearchStatus = "INTERCEPTING LIKELY ROUTE";
		return true;
	}

	private bool ExecuteUtilityAmbush( OneMoreRoundWeapon weapon )
	{
		if ( _hasConfirmedSight )
			return false;

		StopMovementOnly();
		ReleaseBotWeaponIntent( weapon );
		if ( TryGetBestOpponentBelief( out Vector3 beliefPosition, out float confidence ) )
		{
			LookTowardWorldPoint( beliefPosition + Vector3.Up * 46f, 6.5f );
			SearchStatus = $"HOLD / BELIEF {confidence * 100f:0}%";
			TacticalStatus = "AMBUSH / HOLD LIKELY APPROACH";
		}
		else
		{
			SweepLook( IdleSweepSpeed * 0.55f );
			SearchStatus = "HOLD / SCAN";
		}
		return true;
	}

	private void LookTowardWorldPoint( Vector3 point, float followSpeed )
	{
		if ( _controller?.IsValid != true )
			return;

		Vector3 direction = ( point - _controller.EyePosition ).Normal;
		if ( direction.IsNearlyZero( 0.001f ) )
			return;

		Rotation desired = Rotation.LookAt( direction, Vector3.Up );
		float follow = 1f - MathF.Exp( -MathF.Max( 0.5f, followSpeed ) * MathF.Max( Time.Delta, 0f ) );
		_controller.EyeAngles = Rotation.Slerp(
			_controller.EyeAngles.ToRotation(),
			desired,
			MathX.Clamp( follow, 0f, 1f )
		).Angles();
	}

	private bool UtilityNeedsReload( OneMoreRoundWeapon weapon )
	{
		if ( weapon?.IsValid != true || !weapon.UsesPrimaryClip )
			return false;

		if ( weapon.IsReloading || weapon.Clip1 <= 0 )
			return true;

		if (
			_brainRuleContext.AmmoDiscipline >= 0.75f &&
			weapon.PrimaryClipSize > 0 &&
			weapon.Ammo1 > 0
		)
		{
			return weapon.Clip1 <= Math.Max( 1, (int)MathF.Floor( weapon.PrimaryClipSize * 0.24f ) );
		}

		return false;
	}

	private bool IsUtilityIntentStillLegal( OMRBotIntentKind intent, OneMoreRoundWeapon weapon )
	{
		bool hasVisual = _hasConfirmedSight && _perceptionTarget?.IsAlive == true;
		return intent switch
		{
			OMRBotIntentKind.None => false,
			OMRBotIntentKind.Engage => hasVisual,
			OMRBotIntentKind.Press => hasVisual,
			OMRBotIntentKind.OpenDistance => hasVisual,
			OMRBotIntentKind.Reposition => hasVisual,
			OMRBotIntentKind.FlankLeft => hasVisual && weapon?.Clip1 > 0,
			OMRBotIntentKind.FlankRight => hasVisual && weapon?.Clip1 > 0,
			OMRBotIntentKind.Suppress => !hasVisual && HasFreshMemory(),
			OMRBotIntentKind.ReloadSafely => UtilityNeedsReload( weapon ),
			OMRBotIntentKind.TakeCover => HasRecentThreat() || hasVisual || HasFreshMemory() || HasFreshHearing(),
			OMRBotIntentKind.Search => !hasVisual,
			OMRBotIntentKind.Intercept => !hasVisual && HasOpponentBelief(),
			OMRBotIntentKind.HoldAmbush => !hasVisual && HasOpponentBelief(),
			OMRBotIntentKind.Disengage => hasVisual || HasRecentThreat(),
			_ => true
		};
	}

	private float GetUtilityRangeScale()
	{
		return UtilityCombatBrainEnabled
			? MathX.Clamp( _utilityRangeScale, 0.60f, 1.55f )
			: 1f;
	}

	private void ResetUtilityBrainState()
	{
		_utilitySelectedIntent = OMRBotIntentKind.None;
		_utilitySelectedScore = 0f;
		_utilitySelectedReason = string.Empty;
		_utilitySelectedTarget = Vector3.Zero;
		_utilityHasSelectedTarget = false;
		_utilityTargetChosenAt = -999f;
		_utilitySelectedAt = -999f;
		_utilityCommitUntil = -999f;
		_nextUtilityDecisionAt = 0f;
		_utilityRangeScale = 1f;
		_utilityCoverFailureUntil = -999f;
		_utilityFlankFailureUntil = -999f;
		_utilityPositionFailureUntil = -999f;
		UtilityBrainStatus = UtilityCombatBrainEnabled ? "READY" : "LEGACY";
		_brainDecisionFrame.Clear();
	}
}