Settings overlay panel for the match/game HUD. Draws the gear button, a tabbed settings card (general options: language, censorship, text size, volume, tutorial replay, credits, plus a HotkeySettings tab) and the in-progress match exit dialog. Reads and writes GamePreferences and calls into MatchController for open/close, tutorial start and match exit actions.
@using Sandbox
@using Sandbox.UI
@using Socer
@using System
@using System.Linq
@inherits PanelComponent
<root class="settings-hud @GamePreferences.TextSizeClass @(Game?.GameSettingsOpen==true?"settings-open":"") @(Game?.TutorialStep>=0?"tour-running":"")">
<button class="settings-gear" title="@(GamePreferences.Russian?"Настройки":"Settings")" onclick=@ToggleSettings><span class="settings-gear-art"></span></button>
@if(Game?.GameSettingsOpen==true)
{
<div class="settings-shade">
<div class="settings-card">
<div class="settings-heading"><div><label class="settings-eyebrow">SOCER / @(GamePreferences.Russian?"ПАРАМЕТРЫ ИГРЫ":"GAME OPTIONS")</label><label class="settings-title">@(GamePreferences.Russian?"Настройки":"Settings")</label></div><button class="settings-close" title="@(GamePreferences.Russian?"Закрыть настройки":"Close settings")" onclick=@ToggleSettings>×</button></div>
<div class="settings-tabs">
<button class="@(!KeysOpen?"chosen":"")" onclick=@(()=>SetTab(false))>@(GamePreferences.Russian?"Общие":"General")</button>
<button class="@(KeysOpen?"chosen":"")" onclick=@(()=>SetTab(true))>@(GamePreferences.Russian?"Клавиши":"Keyboard")</button>
</div>
<div class="settings-body">
@if(KeysOpen)
{
<HotkeySettings />
}
else
{
<div class="settings-section"><div><label class="settings-label">@(GamePreferences.Russian?"Язык":"Language")</label><label class="settings-help">@(GamePreferences.Russian?"Язык интерфейса изменится сразу.":"Interface language changes immediately.")</label></div><div class="settings-languages"><button class="@(GamePreferences.Language=="en"?"chosen":"")" onclick=@(()=>SetLanguage("en"))>English</button><button class="@(GamePreferences.Language=="ru"?"chosen":"")" onclick=@(()=>SetLanguage("ru"))>Русский</button></div></div>
<DialogueCensorshipSettings />
<div class="settings-section text-size-section"><div><label class="settings-label">@(GamePreferences.Russian?"Размер текста":"Text size")</label><label class="settings-help">@(GamePreferences.Russian?"Крупнее подписи, меню и трансляция. Поле сохраняет свой размер.":"Larger labels, menus and commentary. The pitch keeps its size.")</label></div><div class="text-size-choices">@foreach(int size in new[]{100,115,130}){int choice=size;<button class="text-size-choice-@choice @(GamePreferences.TextSizePercent==choice?"chosen":"")" onclick=@(()=>SetTextSize(choice))><label>@($"{choice}%")</label><small>@(GamePreferences.Russian?(choice==100?"Обычный":choice==115?"Крупный":"Очень крупный"):(choice==100?"Default":choice==115?"Large":"Extra large"))</small></button>}</div><div class="text-size-preview"><label>@(GamePreferences.Russian?"Передача в свободную зону":"Pass into space")</label><small>@(GamePreferences.Russian?"Подсказки и события матча":"Hints and match events")</small></div></div>
<div class="settings-section sound-section"><div><label class="settings-label">@(GamePreferences.Russian?"Громкость звука":"Sound volume")</label><label class="settings-help">@(GamePreferences.Russian?"Громкость s&box сейчас:":"s&box master volume:") @GameAudio.EngineMasterPercent%</label></div><div class="volume-control"><button class="volume-step" onclick=@(()=>SetVolume(GamePreferences.SoundVolume-10))>−</button><div class="volume-steps">@for(int i=0;i<=10;i++){int volume=i*10;<button class="@(GamePreferences.SoundVolume>=volume?"filled":"")" title="@($"{volume}%")" onclick=@(()=>SetVolume(volume))></button>}</div><button class="volume-step" onclick=@(()=>SetVolume(GamePreferences.SoundVolume+10))>+</button><label class="volume-percent">@($"{GamePreferences.SoundVolume}%")</label></div><button class="sound-test" disabled="@(GamePreferences.SoundVolume==0)" onclick=@(()=>GameAudio.Tap())>@(GamePreferences.Russian?"♪ Проверить звук":"♪ Test sound")</button></div>
<div class="settings-section tutorial-section"><div><label class="settings-label">@(GamePreferences.Russian?"Обучение":"First steps")</label><label class="settings-help">@(GamePreferences.Russian?"Короткое знакомство или практика команд на поле.":"A quick introduction or hands-on practice on the pitch.")</label></div><div class="tutorial-choices"><button class="tutorial-replay" disabled="@(Game?.ClubOpen!=true || Game.PreparingMatch || Game.MatchModeSelection || Game.QuickBroadcastOpen)" onclick=@ReplayTutorial>@(GamePreferences.Russian?"Знакомство с игрой":"Quick introduction")</button><button class="tutorial-replay tutorial-match-replay" disabled="@(Game?.CanStartMatchTutorial!=true)" onclick=@ReplayMatchTutorial>@(GamePreferences.Russian?"Обучающий матч":"Training match")</button></div><button class="tutorial-hints-reset" onclick=@(()=>{GameAudio.Tap();GamePreferences.ResetClubHelp();})>@(GamePreferences.Russian?"Показывать подсказки разделов снова":"Show section hints again")</button><label class="settings-help">@(GamePreferences.Russian?"Матч не меняет сезон, карточки и валюту. Можно выйти в любой момент.":"The match leaves your season, cards and currency unchanged. Leave whenever you like.")</label></div>
<div class="settings-section credits-section">
<button class="credits-toggle" onclick=@(()=>{CreditsOpen=!CreditsOpen;GameAudio.Tap();})>@(GamePreferences.Russian?"Авторы и лицензии":"Credits and licences") @(CreditsOpen?"−":"+")</button>
@if(CreditsOpen)
{
<label class="settings-help">@(GamePreferences.Russian?"Socer: оригинальная симуляция, интерфейс и процедурный стадион. Иллюстрации созданы для проекта с OpenAI image_gen. Платформа и базовые ресурсы: Facepunch / s&box.":"Socer: original simulation, interface and procedural stadium. Illustrations created for the project with OpenAI image_gen. Platform and base resources: Facepunch / s&box.")</label>
<label class="settings-label">@(GamePreferences.Russian?"Записи стадиона":"Stadium recordings")</label>
<label class="settings-help">Crowd recordings by Gregor Quendel, CC BY 4.0; edited for Socer. Free Crowd Cheering Sounds. @(GamePreferences.Russian?"Обрезка, затухания, зацикливание и конвертация в Ogg Vorbis.":"Trimmed, faded, looped and converted to Ogg Vorbis.")</label>
<label class="credit-url">opengameart.org/content/free-crowd-cheering-sounds</label>
<label class="credit-url">creativecommons.org/licenses/by/4.0/</label>
<label class="settings-label">@(GamePreferences.Russian?"Записи CC0 1.0":"CC0 1.0 recordings")</label>
<label class="settings-help">@("Kenney Vleugels / Kenney — Voiceover Pack; Artmasterrich — Male_Yes_01, Male_Nooo_02; HowardV — crowd booing; Nicolas4677 — Ball Kick; purchasing102 — SoccerBallKick.wav; Rosa-Orenes256 — Referee whistle sound.wav. "+(GamePreferences.Russian?"Отредактированы и конвертированы для игры.":"Edited and converted for the game."))</label>
<label class="credit-url">kenney.nl/assets/voiceover-pack · freesound.org</label>
<label class="credit-url">creativecommons.org/publicdomain/zero/1.0/</label>
}
</div>
}
</div>
<div class="settings-footer"><label>@(GamePreferences.Russian?"Настройки сохраняются автоматически.":"Changes are saved automatically.")</label><button onclick=@ToggleSettings>@(GamePreferences.Russian?"Готово":"Done")</button></div>
</div>
</div>
}
@if(Game?.MatchExitPromptOpen==true)
{
<div class="match-exit-shade localized-text">
<div class="match-exit-dialog">
<label class="match-exit-eyebrow">@(GamePreferences.Russian?"МАТЧ ПРОДОЛЖАЕТСЯ":"MATCH IN PROGRESS")</label>
<label class="match-exit-title">@(GamePreferences.Russian?"Доиграем до финального свистка":"Finish the match first")</label>
<label class="match-exit-copy">@(GamePreferences.Russian?"Остаться на поле или быстро доиграть матч в текстовой трансляции? Текущий счёт и замены сохранятся.":"Stay on the pitch, or finish quickly with text commentary. The current score and substitutions carry over.")</label>
@if(!Game.CanFinishCurrentMatchQuickly){<label class="match-exit-reason">@Game.MatchExitTextUnavailable</label>}
<div class="match-exit-actions">
<button class="match-exit-continue" onclick=@(()=>Game.ContinueAfterMatchExitPrompt())>@(GamePreferences.Russian?"Продолжить матч":"Continue match")</button>
<button class="match-exit-text" disabled="@(!Game.CanFinishCurrentMatchQuickly)" onclick=@(()=>Game.ConfirmMatchExitInText())>@(GamePreferences.Russian?"Доиграть текстом →":"Finish in text →")</button>
</div>
<label class="match-exit-keys">@($"{GameHotkeys.Display("app.back")} · {(GamePreferences.Russian?"Продолжить матч":"Continue match")} / {GameHotkeys.Display("app.confirm")} · {(GamePreferences.Russian?"Доиграть текстом":"Finish in text")}")</label>
</div>
</div>
}
</root>
@code {
protected override void OnTreeBuilt()
{
base.OnTreeBuilt();
Panel.StyleSheet.Load("/GameSettingsHud.razor.scss");
}
bool CreditsOpen,KeysOpen;
MatchController cachedGame;
MatchController Game
{
get
{
if(cachedGame?.GameObject?.IsValid!=true || cachedGame.Scene!=Scene)
cachedGame=Scene.GetAllComponents<MatchController>().FirstOrDefault();
return cachedGame;
}
}
float nextTranslate;
protected override void OnUpdate()
{
UiTextSizing.Apply(Panel);
if(Game?.GameSettingsOpen!=true && GameHotkeys.Capturing)GameHotkeys.CancelCapture();
if(Time.Now<nextTranslate)return;
nextTranslate=Time.Now+.15f;
HotkeyHints.Apply(Panel);
foreach(var component in Scene.GetAllComponents<PanelComponent>())
if(component!=this){UiRussian.Apply(component.Panel);MenuButtonAudio.Attach(component.Panel);HotkeyHints.Apply(component.Panel);}
}
void ToggleSettings(){if(Game==null)return;InputFocus.Clear();GameHotkeys.CancelCapture();GameAudio.Tap();Game.ToggleGameSettings();}
void SetTab(bool keys){InputFocus.Clear();GameHotkeys.CancelCapture();KeysOpen=keys;GameAudio.Tap();}
void SetLanguage(string value){GamePreferences.SetLanguage(value);nextTranslate=0;GameAudio.Tap();}
void SetVolume(int value){GamePreferences.SetSoundVolume(value);GameAudio.Tap();}
void SetTextSize(int value){GamePreferences.SetTextSize(value);UiTextSizing.Apply(Panel);GameAudio.Tap();}
void ReplayTutorial(){Game.ToggleGameSettings();Game.StartTutorial();}
void ReplayMatchTutorial(){if(Game?.CanStartMatchTutorial!=true)return;Game.ToggleGameSettings();Game.StartMatchTutorial();}
protected override int BuildHash()=>HashCode.Combine(HashCode.Combine(Game?.MatchExitPromptOpen,Game?.CanFinishCurrentMatchQuickly),Game?.GameSettingsOpen,Game?.TutorialStep,GamePreferences.Revision,Game?.CanStartMatchTutorial,Game?.MatchTutorialActive,CreditsOpen,
HashCode.Combine(KeysOpen,GameHotkeys.Revision,GameHotkeys.Capturing,GameHotkeys.CapturingId,GameHotkeys.CaptureMessage));
}