MatchTutorialScenario.cs

Tutorial scenario factory for MatchSimulation. Builds and positions players for penalty, free kick, injury, substitution, marking, stamina and skill-link drills on the practice roster.

Rough Code
using System;
using System.Collections.Generic;
namespace Socer;

public sealed partial class MatchSimulation
{
    // Only the starting arrangement changes; all 22 players and normal physics remain active.
    public static ClubCareer CreateTutorialRoster()=>ClubCareer.Create(738291);

    public static MatchSimulation TutorialScenario(string key,ClubCareer practiceRoster=null)
    {
        if(key is "penalty" or "free-kick")
        {
            var standard=InteractiveRestartPreview(key=="penalty"?RestartKind.Penalty:RestartKind.FreeKick);
            standard.HomeName="COACH ACADEMY";standard.AwayName="PRACTICE XI";
            standard.InjuriesEnabled=false;
            // Prepare the real manual restart before waiting for the learner.
            // No aim, power or successful shot is supplied by the fixture.
            for(int i=0;i<120 && !standard.ManualRestartWaiting;i++)standard.Step(1f/60);
            return standard;
        }
        if(key=="freeplay")
        {
            var practice=Laboratory("match",738291,82,78);
            practice.HomeName="COACH ACADEMY";practice.AwayName="PRACTICE XI";
            return practice;
        }
        var m=Laboratory("match",738291,82,72);
        if(key is "substitution" or "injury")
        {
            practiceRoster??=CreateTutorialRoster();practiceRoster.Apply(m);
            m.HomeName="COACH ACADEMY";m.AwayName="PRACTICE XI";
            m.Seconds=30;m.RestartRemaining=0;m.AutomaticStrategiesEnabled=false;m.InjuriesEnabled=false;
            m.GiveLabBall(m.Players[6]);m.Players[9].Stamina=.16f;
            if(key=="injury")
            {
                m.ManualHomeInjurySubstitution=true;
                m.Players[9].Position=new Point(64,3);
                m.RegisterInjury(m.Players[9],InjurySeverity.Serious,InjuryBodyPart.Ankle);
                // Start at the real carry-off phase, keeping the short visible
                // finish of treatment and the genuine mandatory choice.
                for(int i=0;i<2700 && (m.MedicalStage!=MedicalTreatmentStage.CarryOff || m.MedicalStageProgress<.55f);i++)m.Step(1f/60);
            }
            return m;
        }
        m.HomeName="COACH ACADEMY";m.AwayName="PRACTICE XI";
        m.Seconds=30;m.RestartRemaining=0;m.Passer=-1;m.AutomaticStrategiesEnabled=false;
        foreach(var p in m.Players)
        {
            p.Profile.SlotRole=PlayerPositions.Slot(Formation.All[0],p.Id%11);p.Profile.RoleFamiliarity=1;
            p.Position=m.World(p.Home,p.Team);p.Velocity=new Point();
            p.Decision=0;p.Held=2;p.Facing=p.Team==0?0:MathF.PI;
        }
        m.Players[6].Position=new Point(60,34);m.Players[9].Position=new Point(69,22);
        for(int i=12;i<22;i++)m.Players[i].Position=new Point(85+(i%3)*4,7+(i-12)*5.6f);
        m.Players[11].Position=new Point(103,34);m.GiveLabBall(m.Players[6]);
        if(key is "attack" or "shoot")
        {
            m.Players[9].Position=new Point(89,34);
            foreach(var p in m.Players)if(p.Team==1 && !p.Keeper)p.Position=new Point(68,p.Position.Y);
            m.GiveLabBall(m.Players[9]);
        }
        if(key is "mark" or "press" or "zone" or "clear")
        {
            m.Players[4].Position=new Point(64,40);m.Players[4].Facing=-.7f;
            m.Players[20].Position=new Point(70,34);m.Players[20].Facing=MathF.PI;
            m.GiveLabBall(m.Players[20]);
            if(key=="clear")m.GiveDirectOrder(4,DirectOrderKind.GuardZone,point:new Point(61,38));
        }
        if(key=="stamina")
        {
            m.SetBroadcastPlan(1,1);m.Players[9].Stamina=.78f;
            m.GiveDirectOrder(9,DirectOrderKind.AttackRun,point:new Point(86,22));
        }
        if(key=="links")
        {
            m.Players[6].Profile.SkillLoadout=new List<SkillInstance>{new(){Skill=UniqueSkill.ThreadNeedle,Grade=2}};
            m.Players[9].Profile.SkillLoadout=new List<SkillInstance>{new(){Skill=UniqueSkill.SpaceHunter,Grade=2}};
            m.Players[6].Position=new Point(62,34);m.Players[9].Position=new Point(68,26);
            m.Players[13].Position=new Point(63,38);
            m.Players[2].Profile.SkillLoadout=new List<SkillInstance>{new(){Skill=UniqueSkill.CoverGuard,Grade=2}};
            m.Players[3].Profile.SkillLoadout=new List<SkillInstance>{new(){Skill=UniqueSkill.SteelPress,Grade=2}};
            m.TeamCohesion[0]=.9f;m.GiveLabBall(m.Players[6]);
        }
        return m;
    }
}