Part of the soccer match simulation. Handles the landing recovery after an acrobatic shot: captures the footballer's residual velocity at take-off, integrates it over a short decay curve to slide the position forward, and steps that recovery both in normal play and while match time is frozen for whistles or treatment.
using System;
namespace Socer;
public sealed partial class MatchSimulation
{
const float AcrobaticFlightSeconds=.28f;
const float AcrobaticMaximumInertia=2.2f;
void BeginAcrobaticRecovery(Footballer p)
{
float duration=LegendaryFootballAnimation.Get(LegendaryAbility.RoyalReception).Duration;
p.AcrobaticAt=Seconds;p.AcrobaticUntil=Seconds+duration;
p.AcrobaticVelocity=p.Velocity.Length>AcrobaticMaximumInertia?
p.Velocity.Unit*AcrobaticMaximumInertia:p.Velocity;
p.Velocity=new Point();
p.RunUntil=p.FootballRunUntil=p.LegendaryRunUntil=0;p.BurstRemaining=0;
p.PlanUntil=p.KickSetupUntil=p.ShotSetupUntil=0;p.ChasingTouch=false;
p.ReceiveReadyUntil=p.ReceiveUntil=p.ChestUntil=Seconds;
p.FeintUntil=p.InterceptUntil=p.BlockUntil=Seconds;
p.PassCallUntil=0;p.Action="Приземляется после акробатического удара";
}
void AdvanceAcrobaticRecovery(Footballer p,float dt)
{
if(p.AcrobaticUntil<=p.AcrobaticAt || Seconds<p.AcrobaticAt)return;
float age=Seconds-p.AcrobaticAt;
// Exact integration of a short, linearly decaying launch velocity.
// The total displacement is <=2.2*.28/2=.308 m, then both roots plant.
float Integral(float t)
{t=Math.Clamp(t,0,AcrobaticFlightSeconds);return t-t*t/(2*AcrobaticFlightSeconds);}
float distance=Integral(age)-Integral(age-dt);
if(distance>0)p.Position=Clamp(p.Position+p.AcrobaticVelocity*distance);
if(p.IsAcrobaticCommitted(Seconds))
{
p.Velocity=new Point();p.BurstRemaining=0;
p.Action=age<.48f?"Приземляется после акробатического удара":"Поднимается после акробатического удара";
}
}
void AdvanceAcrobaticRecoveries(float dt)
{
foreach(var p in Players)AdvanceAcrobaticRecovery(p,dt);
}
void AdvanceFrozenAcrobaticRecoveries(float dt)
{
foreach(var p in Players)
{
if(!p.IsAcrobaticCommitted(Seconds))continue;
// Whistles and treatment stop match time, but an already airborne
// body must finish landing before any presentation walk can start.
p.AcrobaticAt-=dt;p.AcrobaticUntil-=dt;
AdvanceAcrobaticRecovery(p,dt);
}
}
}