using System; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif namespace RaceFields.GameFlow { public sealed class SelectionMenuController : MonoBehaviour { [Serializable] public sealed class VehicleOption { public string displayName = "BUGGY"; public GameObject prefab; public Vector3 previewRotation = new Vector3(0f, 145f, 0f); public float previewScale = 1f; } [Serializable] public sealed class MapOption { public string displayName = "DEMO SCENE"; public string sceneName = "Demo Scene"; public SelectedGameMode mode = SelectedGameMode.Arena; } private enum Step { Vehicle, Mode, Map } [Header("Content")] [SerializeField] private VehicleOption[] vehicles; [SerializeField] private MapOption[] maps; [Header("Preview")] [SerializeField] private Transform previewRoot; [SerializeField] private float previewRotationSpeed = 28f; [Header("Panels")] [SerializeField] private GameObject vehiclePanel; [SerializeField] private GameObject modePanel; [SerializeField] private GameObject mapPanel; [Header("Texts")] [SerializeField] private Text titleText; [SerializeField] private Text subtitleText; [SerializeField] private Text vehicleNameText; [SerializeField] private Text vehicleClassText; [SerializeField] private Text[] modeLabels; [SerializeField] private Text[] mapLabels; private readonly string[] modeNames = { "\u0410\u0420\u0415\u041d\u0410", "\u0413\u041e\u041d\u041a\u0410 \u041d\u0410 \u0412\u042b\u0416\u0418\u0412\u0410\u041d\u0418\u0415" }; private Step currentStep; private GameObject currentPreview; private int vehicleIndex; private int modeIndex; private int mapIndex; private void Awake() { if (previewRoot == null) previewRoot = transform; } private void Start() { currentStep = Step.Vehicle; ShowVehicle(vehicleIndex); RefreshUi(); } private void Update() { RotatePreview(); HandleKeyboard(); } public void SelectPrevious() { MoveSelection(-1); } public void SelectNext() { MoveSelection(1); } public void Confirm() { if (currentStep == Step.Vehicle) { currentStep = Step.Mode; RefreshUi(); return; } if (currentStep == Step.Mode) { mapIndex = 0; currentStep = Step.Map; RefreshUi(); return; } StartSelectedMap(); } public void ChooseArena() { modeIndex = 0; mapIndex = 0; currentStep = Step.Map; RefreshUi(); } public void ChooseSurvivalRace() { modeIndex = 1; mapIndex = 0; currentStep = Step.Map; RefreshUi(); } public void ChooseCurrentMap() { StartSelectedMap(); } public void Back() { if (currentStep == Step.Map) currentStep = Step.Mode; else if (currentStep == Step.Mode) currentStep = Step.Vehicle; RefreshUi(); } private void HandleKeyboard() { #if ENABLE_INPUT_SYSTEM Keyboard keyboard = Keyboard.current; if (keyboard == null) return; if (keyboard.leftArrowKey.wasPressedThisFrame || keyboard.aKey.wasPressedThisFrame) SelectPrevious(); else if (keyboard.rightArrowKey.wasPressedThisFrame || keyboard.dKey.wasPressedThisFrame) SelectNext(); if (keyboard.spaceKey.wasPressedThisFrame || keyboard.enterKey.wasPressedThisFrame || keyboard.numpadEnterKey.wasPressedThisFrame) Confirm(); else if (keyboard.escapeKey.wasPressedThisFrame || keyboard.backspaceKey.wasPressedThisFrame) Back(); #endif } private void MoveSelection(int direction) { if (currentStep == Step.Vehicle) { int length = vehicles != null ? vehicles.Length : 0; vehicleIndex = Wrap(vehicleIndex + direction, length); ShowVehicle(vehicleIndex); } else if (currentStep == Step.Mode) { modeIndex = Wrap(modeIndex + direction, modeNames.Length); } else if (currentStep == Step.Map) { int length = GetMapCountForCurrentMode(); mapIndex = Wrap(mapIndex + direction, length); } RefreshUi(); } private void ShowVehicle(int index) { if (currentPreview != null) Destroy(currentPreview); if (vehicles == null || vehicles.Length == 0 || vehicles[index] == null || vehicles[index].prefab == null) return; VehicleOption option = vehicles[index]; currentPreview = Instantiate(option.prefab, previewRoot.position, Quaternion.Euler(option.previewRotation), previewRoot); currentPreview.name = "Runtime Preview - " + option.displayName; currentPreview.transform.localPosition = Vector3.zero; currentPreview.transform.localRotation = Quaternion.Euler(option.previewRotation); currentPreview.transform.localScale = Vector3.one * Mathf.Max(0.01f, option.previewScale); DisableGameplayComponents(currentPreview); } private void DisableGameplayComponents(GameObject root) { Rigidbody[] bodies = root.GetComponentsInChildren(true); for (int i = 0; i < bodies.Length; i++) { bodies[i].isKinematic = true; bodies[i].useGravity = false; } Collider[] colliders = root.GetComponentsInChildren(true); for (int i = 0; i < colliders.Length; i++) colliders[i].enabled = false; MonoBehaviour[] behaviours = root.GetComponentsInChildren(true); for (int i = 0; i < behaviours.Length; i++) behaviours[i].enabled = false; AudioSource[] audioSources = root.GetComponentsInChildren(true); for (int i = 0; i < audioSources.Length; i++) { audioSources[i].Stop(); audioSources[i].enabled = false; } ParticleSystem[] particles = root.GetComponentsInChildren(true); for (int i = 0; i < particles.Length; i++) particles[i].Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); TrailRenderer[] trails = root.GetComponentsInChildren(true); for (int i = 0; i < trails.Length; i++) trails[i].emitting = false; } private void RotatePreview() { if (currentPreview == null) return; currentPreview.transform.Rotate(Vector3.up, previewRotationSpeed * Time.deltaTime, Space.World); } private void RefreshUi() { if (vehiclePanel != null) vehiclePanel.SetActive(currentStep == Step.Vehicle); if (modePanel != null) modePanel.SetActive(currentStep == Step.Mode); if (mapPanel != null) mapPanel.SetActive(currentStep == Step.Map); if (titleText != null) titleText.text = GetTitle(); if (subtitleText != null) subtitleText.text = GetSubtitle(); if (vehicleNameText != null) vehicleNameText.text = GetVehicleName(); if (vehicleClassText != null) vehicleClassText.text = (vehicleIndex + 1) + " / " + Mathf.Max(vehicles != null ? vehicles.Length : 0, 1); RefreshModeLabels(); RefreshMapLabels(); } private void RefreshModeLabels() { if (modeLabels == null) return; for (int i = 0; i < modeLabels.Length && i < modeNames.Length; i++) { if (modeLabels[i] != null) modeLabels[i].text = i == modeIndex ? "< " + modeNames[i] + " >" : modeNames[i]; } } private void RefreshMapLabels() { if (mapLabels == null) return; MapOption selectedMap = GetMapForCurrentMode(mapIndex); for (int i = 0; i < mapLabels.Length; i++) { if (mapLabels[i] != null) mapLabels[i].text = selectedMap != null ? selectedMap.displayName : "NO MAPS"; } } private string GetTitle() { if (currentStep == Step.Mode) return "\u0420\u0415\u0416\u0418\u041c \u0418\u0413\u0420\u042b"; if (currentStep == Step.Map) return "\u0412\u042b\u0411\u041e\u0420 \u041a\u0410\u0420\u0422\u042b"; return "\u0412\u042b\u0411\u041e\u0420 \u041c\u0410\u0428\u0418\u041d\u042b"; } private string GetSubtitle() { if (currentStep == Step.Vehicle) return "\u0421\u0422\u0420\u0415\u041b\u041a\u0418 / A-D - \u041c\u0410\u0428\u0418\u041d\u0410 SPACE - \u0412\u042b\u0411\u0420\u0410\u0422\u042c"; if (currentStep == Step.Mode) return "\u0421\u0422\u0420\u0415\u041b\u041a\u0418 / A-D - \u0420\u0415\u0416\u0418\u041c SPACE - \u0414\u0410\u041b\u042c\u0428\u0415 ESC - \u041d\u0410\u0417\u0410\u0414"; return "\u0421\u0422\u0420\u0415\u041b\u041a\u0418 / A-D - \u041a\u0410\u0420\u0422\u0410 SPACE - \u0421\u0422\u0410\u0420\u0422 ESC - \u041d\u0410\u0417\u0410\u0414"; } private string GetVehicleName() { if (vehicles == null || vehicles.Length == 0 || vehicles[vehicleIndex] == null) return "NO VEHICLE"; return string.IsNullOrWhiteSpace(vehicles[vehicleIndex].displayName) ? vehicles[vehicleIndex].prefab.name : vehicles[vehicleIndex].displayName; } private void StartSelectedMap() { MapOption selectedMap = GetMapForCurrentMode(mapIndex); if (selectedMap == null || string.IsNullOrWhiteSpace(selectedMap.sceneName)) { Debug.LogWarning("No map is configured for selected game mode."); return; } SelectedGameMode selectedMode = modeIndex == 0 ? SelectedGameMode.Arena : SelectedGameMode.SurvivalRace; GameObject selectedPrefab = vehicles != null && vehicles.Length > 0 ? vehicles[vehicleIndex].prefab : null; GameSelectionState.SetSelection(selectedPrefab, GetVehicleName(), selectedMode, selectedMap.sceneName); SceneManager.LoadScene(selectedMap.sceneName); } private MapOption GetMapForCurrentMode(int filteredIndex) { if (maps == null || maps.Length == 0) return null; SelectedGameMode selectedMode = modeIndex == 0 ? SelectedGameMode.Arena : SelectedGameMode.SurvivalRace; int currentFilteredIndex = 0; for (int i = 0; i < maps.Length; i++) { if (maps[i] == null || maps[i].mode != selectedMode) continue; if (currentFilteredIndex == filteredIndex) return maps[i]; currentFilteredIndex++; } return null; } private int GetMapCountForCurrentMode() { if (maps == null || maps.Length == 0) return 0; SelectedGameMode selectedMode = modeIndex == 0 ? SelectedGameMode.Arena : SelectedGameMode.SurvivalRace; int count = 0; for (int i = 0; i < maps.Length; i++) { if (maps[i] != null && maps[i].mode == selectedMode) count++; } return count; } private static int Wrap(int value, int length) { if (length <= 0) return 0; value %= length; return value < 0 ? value + length : value; } } }