Part of the football match simulation. Decides when a non-keeper player on the ball inside their own box should clear it, choosing between a pass to an open teammate, an emergency hoof out for a throw-in, or a clearance upfield, then kicks the ball and logs the event.
using System;
using System.Linq;
namespace Socer;
public sealed partial class MatchSimulation
{
public int[] BoxClearances=new int[2];
bool ClearDanger(Footballer p)
{
if(p.Keeper || !BallAtFoot(p))return false;
var local=World(p.Position,p.Team);
if(local.X>18 || Math.Abs(local.Y-34)>24)return false;
var rivals=ActivePlayers.Where(q=>q.Team!=p.Team).OrderBy(q=>(q.Position-p.Position).Length).ToArray();
float pressure=(rivals[0].Position-p.Position).Length;
float goalThreat=rivals.Min(q=>(q.Position-World(new Point(5.5f,34),p.Team)).Length);
if(pressure>4.5f)return false;
// With time on the ball, use a visible short outlet instead of clearing just
// because another attacker happens to be near our goal.
bool safeOutlet=pressure>1.7f && ActivePlayers.Any(q=>q.Team==p.Team && !q.Keeper && q.Id!=p.Id &&
(q.Position-p.Position).Length is >3 and <22 && SeesPartner(p,q) && IsOnside(q,p.Position) &&
PerceivedSpace(p,q.Position)>3 && PassSafety(p,q,PassMeetingPoint(p,q))>.7f && PerceivedLane(p,q.Position)>1.6f);
if(safeOutlet)return false;
if(pressure>3 && goalThreat>9)return false;
var outsides=ActivePlayers.Where(q=>q.Team==p.Team && !q.Keeper && q.Id!=p.Id &&
World(q.Position,p.Team).X>local.X+15 && World(q.Position,p.Team).X<75)
.Select(q=>new{Player=q,Space=rivals.Min(r=>(r.Position-q.Position).Length)})
.Where(q=>q.Space>3).OrderByDescending(q=>q.Space+World(q.Player.Position,p.Team).X*.06f).ToArray();
Point target;
bool emergencyOut=pressure<1.45f && goalThreat<11 && outsides.Length==0;
if(emergencyOut)
target=World(new Point(Math.Clamp(local.X+14,18,38),local.Y<34?-4:72),p.Team);
else if(outsides.Length>0 && pressure>2.8f)
target=Clamp(outsides[0].Player.Position+outsides[0].Player.Velocity*.35f);
else
target=World(new Point(Math.Clamp(local.X+31,31,48),local.Y<34?7:61),p.Team);
if(!PrepareKickFacing(p,target))return true;
Kick(p,target,Math.Clamp(23+p.Profile.Passing*5,23,29),false);
if(!emergencyOut){ShapeFlight(target,StrikeKind.Cross);PrepareSecondBall(p,target);}
Passes[p.Team]++;BoxClearances[p.Team]++;
Emit("pass",p,emergencyOut?"В АУТ!":"ВЫНОС!",1.1f);
AddEvent($"{TeamName(p.Team)} · {(emergencyOut?"выбил мяч в аут":"вынес мяч из штрафной")} #{p.Number}");
return true;
}
}