Tutorial HUD panel for the training match. Renders the lesson card, step progress, projected on-pitch markers for the focused player and target, and the substitution/injury choice controls, with English/Russian text.
@using Sandbox
@using Sandbox.UI
@using Socer
@using System
@inherits Panel
<root class="match-tutorial-hud localized-text">
@if(Game?.MatchTutorialActive==true && Game.MatchTutorialLesson is not null)
{
@if(!ChoiceOpen && FocusVisible)
{
<div class="tutorial-actor-anchor @(FocusBelow?"below":"")" style="@FocusStyle">
<div class="tutorial-actor-callout" style="@FocusCardStyle">
<div class="tutorial-actor-action"><div class="tutorial-actor-beacon"></div><label>@FocusAction</label></div>
<label class="tutorial-actor-detail">@FocusDetail</label>
</div>
<div class="tutorial-actor-pointer"></div>
</div>
}
@if(!ChoiceOpen && TargetVisible)
{
<div class="tutorial-target @(Game.MatchTutorialSatisfied?"accepted":"")" style="@TargetStyle">
<div class="tutorial-target-ring"></div>
<label class="tutorial-target-caption">@TargetCaption</label>
</div>
}
@if(ChoiceOpen)
{
<div class="tutorial-modal-tools"><label>@Text("Training match","Учебный матч")</label><button class="tutorial-match-repeat" onclick=@(()=>Game.RetryMatchTutorialStep())>@Text("Repeat","Повторить")</button><button class="tutorial-match-exit" onclick=@(()=>Game.EndMatchTutorial())>@Text("Leave training","Выйти из обучения")</button></div>
}
else
{
<div class="tutorial-match-card @(Game.MatchTutorialLesson.Key is "attack" or "shoot" or "run" or "stamina" or "links" or "penalty" or "free-kick" or "injury"?"goal-lesson":"") @(Game.MatchTutorialSetPieceLesson?"set-piece-lesson":"") @(Game.MatchTutorialObserving?"observing":"") @(Game.CanAdvanceMatchTutorial?"complete":"")">
<div class="tutorial-match-head">
<label class="tutorial-match-kicker">@Text("TRAINING MATCH","ОБУЧАЮЩИЙ МАТЧ")</label>
<label class="tutorial-match-counter">@($"{Game.MatchTutorialStep+1} / {Game.MatchTutorialStepCount}")</label>
</div>
<div class="tutorial-match-progress">
@for(int i=0;i<Game.MatchTutorialStepCount;i++)
{
<div class="@(i==Game.MatchTutorialStep?"current":Game.IsMatchTutorialLessonCompleted(i)?"done":"")"></div>
}
</div>
<label class="tutorial-match-title">@Game.MatchTutorialLesson.Title(GamePreferences.Russian)</label>
<label class="tutorial-match-body">@Game.MatchTutorialLesson.Body(GamePreferences.Russian)</label>
@if(!string.IsNullOrWhiteSpace(Game.MatchTutorialLesson.Hint(GamePreferences.Russian)))
{
<div class="tutorial-match-instruction"><label>@Game.MatchTutorialLesson.Hint(GamePreferences.Russian)</label></div>
}
@if(Game.MatchTutorialSubstitutionLesson && !Game.MatchTutorialSatisfied)
{
<button class="tutorial-open-substitutions" onclick=@(()=>Game.OpenSubstitutions())>@Text("Open Substitutions →","Открыть замены →")</button>
}
<div class="tutorial-match-status">
<div class="tutorial-status-dot"></div>
<div><label class="tutorial-match-phase">@Phase</label><label class="tutorial-match-feedback">@Feedback</label></div>
</div>
<div class="tutorial-match-actions">
<button class="tutorial-match-repeat" onclick=@(()=>Game.RetryMatchTutorialStep())>@Text("Repeat","Повторить")</button>
<button class="tutorial-match-next @(Game.CanAdvanceMatchTutorial?"ready":"disabled")" disabled="@(!Game.CanAdvanceMatchTutorial)" onclick=@(()=>Game.AdvanceMatchTutorial())>@(LastStep?Text("Finish ✓","Завершить ✓"):Text("Next →","Далее →"))</button>
</div>
<button class="tutorial-match-exit" onclick=@(()=>Game.EndMatchTutorial())>@Text("Skip training","Пропустить обучение")</button>
<button class="tutorial-chapters-toggle" onclick=@(()=>ChaptersOpen=!ChaptersOpen)>@Text(ChaptersOpen?"Hide topics ↑":"Choose a topic ↓",ChaptersOpen?"Скрыть темы ↑":"Выбрать тему ↓")</button>
@if(ChaptersOpen)
{
<div class="tutorial-chapters">
@foreach(var chapter in MatchTutorialLesson.Chapters)
{
var choice=chapter;
<button class="tutorial-chapter [email protected]" onclick=@(()=>{ChaptersOpen=false;Game.JumpToMatchTutorialChapter(choice.Key);})>@chapter.Title(GamePreferences.Russian)</button>
}
</div>
}
</div>
}
}
</root>
@code {
public MatchController Game {get;set;}
public FootballWorldPanel World {get;set;}
bool ChaptersOpen;
bool ChoiceOpen=>Game.SubstitutionOpen || Game.InjuryChoiceOpen;
public override void Tick(){base.Tick();if(Game?.MatchTutorialActive==true)Game.TickMatchTutorial();}
bool LastStep=>Game.MatchTutorialStep>=Game.MatchTutorialStepCount-1;
static string Text(string en,string ru)=>GamePreferences.Russian?ru:en;
string Phase=>Game.MatchTutorialInjuryLesson && Game.Match.MedicalActive && !Game.InjuryChoiceOpen?Text("Medical staff on the pitch","Медики на поле"):
Game.CanAdvanceMatchTutorial?Game.MatchTutorialLesson.IsTask?Text("Lesson complete","Шаг выполнен"):Text("Ready to continue","Можно продолжить"):
Game.MatchTutorialObserving?Game.MatchTutorialLesson.IsTask?Text("Order accepted","Команда принята"):Text("Watch the moment","Наблюдение"):
Game.MatchTutorialLesson.IsTask?Text("Your turn","Ваше действие"):Text("Look around","Знакомство");
string Feedback=>!string.IsNullOrWhiteSpace(Game.MatchTutorialFeedback)?Game.MatchTutorialFeedback:
Game.MatchTutorialInjuryLesson && Game.Match.MedicalActive?Text("The compulsory replacement screen opens after the player is carried off.","Окно обязательной замены откроется, когда игрока унесут с поля."):
Game.MatchTutorialObserving?Text("Watch the players respond on the pitch.","Посмотрите, как игроки выполняют команду."):
Game.CanAdvanceMatchTutorial?Text("You can move on or repeat this moment.","Можно продолжить или повторить эпизод."):
Game.MatchTutorialLesson.IsTask?Text("Follow the instruction above.","Выполните действие из подсказки выше."):
Text("Press Next when you are ready.","Нажмите «Далее», когда будете готовы.");
Point? Target
{
get
{
int player=Game.MatchTutorialTargetPlayer;
if(player>=0 && player<Game.Match.Players.Count && !Game.Match.Players[player].SentOff)
return Game.Match.Players[player].Position;
return Game.MatchTutorialTarget;
}
}
int FocusPlayer=>Game.MatchTutorialFocusPlayer;
bool FocusPending=>Game.MatchTutorialNeedsAction && FocusPlayer>=0 && FocusPlayer<Game.Match.Players.Count &&
!Game.Match.Players[FocusPlayer].SentOff && World?.CallInput?.Dragging!=true && !Game.CommandWheelOpen;
Vector2 FocusScreen=>FocusPending && World is not null?World.Project(Game.Match.Players[FocusPlayer].Position,2.65f):new Vector2(-10,-10);
float HudWidth=>Math.Max(1,Box.Rect.Width*ScaleFromScreen);
float HudHeight=>Math.Max(1,Box.Rect.Height*ScaleFromScreen);
bool FocusVisible=>FocusPending && FocusScreen.x>.02f && FocusScreen.x<.98f && FocusScreen.y>.02f && FocusScreen.y<.88f;
bool FocusBelow=>FocusScreen.y*HudHeight<94;
string FocusStyle=>FormattableString.Invariant($"left:{FocusScreen.x*100:0.###}%;top:{FocusScreen.y*100:0.###}%;");
string FocusCardStyle=>FormattableString.Invariant($"left:{Math.Clamp(FocusScreen.x*HudWidth-136,12,Math.Max(12,HudWidth-284))-FocusScreen.x*HudWidth:0.##}px;");
string FocusAction=>Game.MatchTutorialLesson.Key switch
{"run" or "zone"=>Text("HOLD LEFT CLICK","ЗАЖМИТЕ ЛКМ"),
"pass" or "clear"=>Text("RIGHT CLICK","НАЖМИТЕ ПКМ"),
"press" when FocusPlayer>=11=>Text("RIGHT CLICK","НАЖМИТЕ ПКМ"),
_=>Text("LEFT CLICK","НАЖМИТЕ ЛКМ")};
string FocusDetail=>Game.MatchTutorialLesson.Key switch
{"run" or "zone"=>Text($"Drag #{Game.Match.Players[FocusPlayer].Number} to TARGET",$"Потяните №{Game.Match.Players[FocusPlayer].Number} к метке «Цель»"),
"pass"=>Text("Then choose Pass here","Затем выберите «Пас сюда»"),
"clear"=>Text("Then choose Clear order","Затем выберите «Снять приказ»"),
"press" when FocusPlayer>=11=>Text("Then choose Press","Затем выберите «Прессинг»"),
"mark" when FocusPlayer>=11=>Text($"Mark opposing #{Game.Match.Players[FocusPlayer].Number}",$"Опекайте соперника №{Game.Match.Players[FocusPlayer].Number}"),
_=>Text($"Select your #{Game.Match.Players[FocusPlayer].Number}",$"Выберите своего №{Game.Match.Players[FocusPlayer].Number}")};
Vector2 TargetScreen=>Target is Point p && World is not null?World.Project(p,.06f):new Vector2(-10,-10);
bool TargetVisible=>!(FocusPending && Game.MatchTutorialTargetPlayer==FocusPlayer) && TargetScreen.x>.025f && TargetScreen.x<.975f && TargetScreen.y>.05f && TargetScreen.y<.93f;
string TargetStyle=>FormattableString.Invariant($"left:{TargetScreen.x*100:0.###}%;top:{TargetScreen.y*100:0.###}%;");
string TargetCaption=>Game.MatchTutorialTargetPlayer>=0 && Game.MatchTutorialTargetPlayer<Game.Match.Players.Count?
$"#{Game.Match.Players[Game.MatchTutorialTargetPlayer].Number}":Text("TARGET","ЦЕЛЬ");
protected override int BuildHash()=>Game?.MatchTutorialActive!=true?0:
HashCode.Combine(HashCode.Combine(Game.Revision,Game.MatchTutorialStep,Game.MatchTutorialSatisfied,Game.MatchTutorialObserving,Game.CanAdvanceMatchTutorial,Game.MatchTutorialFeedback,GamePreferences.Revision),
FocusPlayer,FocusPending,FocusScreen,HudWidth,HudHeight)^HashCode.Combine(ChaptersOpen,ChoiceOpen);
}