diff --git a/Assets/PrizesConfigurator.prefab b/Assets/PrizesConfigurator.prefab index 27ba91a..9dd2fdf 100644 --- a/Assets/PrizesConfigurator.prefab +++ b/Assets/PrizesConfigurator.prefab @@ -423,6 +423,7 @@ GameObject: - component: {fileID: 6087319970201647579} - component: {fileID: 5163440344643624749} - component: {fileID: 7572701421691474762} + - component: {fileID: 1117177583243168359} m_Layer: 5 m_Name: Added Prizes Table m_TagString: Untagged @@ -487,6 +488,32 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!114 &1117177583243168359 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1230475039650522997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!1 &1590808388917995624 GameObject: m_ObjectHideFlags: 0 @@ -719,12 +746,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 852fdedcfe3484442b0d7e16ba459a3b, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::CharacterSelectionPanel - CharacterNameLabel: {fileID: 8964039495605377843} - CharacterIcon: {fileID: 5624866514520992697} - ClickSound: {fileID: 8300000, guid: 1c48900c9ab0aa8418920a8193050870, type: 3} + _characterNameLabel: {fileID: 8964039495605377843} + _characterIcon: {fileID: 5624866514520992697} _prizeTable: {fileID: 3273224345942325796} _prizeViewPrefab: {fileID: 1203032466002125781, guid: f8172e74629723c4e993e8ac2f322bdf, type: 3} - buttons: + _buttons: - {fileID: 1081245877292184043} - {fileID: 6492870122095265573} _selectButton: {fileID: 1081245877292184043} @@ -1369,10 +1395,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::PrizesSelector _addedPrizesTable: {fileID: 6087319970201647579} - _prizeViewPrefab: {fileID: 1203032466002125781, guid: f8172e74629723c4e993e8ac2f322bdf, type: 3} _availablePrizesTable: {fileID: 978156554344200501} - skipFrame: 0 - pointer: {fileID: 2717940143288701108} + _selectButton: {fileID: 6745117456236530743} + _pointer: {fileID: 2717940143288701108} --- !u!1 &8288873238761209220 GameObject: m_ObjectHideFlags: 0 @@ -1464,12 +1489,10 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::AvailablePrizesTable _prizeButtonPrefab: {fileID: 942699711437869465, guid: f4d8e373cd4ebc948abb9692f14709e6, type: 3} _buttonsRoot: {fileID: 4388752671798834034} - prizeButtons: [] - countablePrizeConfigurator: {fileID: 5680088572919083885, guid: 8b123b0e9e795b842948d07b6e135d84, type: 3} - qrPrizeConfigurator: {fileID: 8201104754896611310, guid: 419ede247f441324681aeb7eeacf4ea0, type: 3} - vitrinaPrizeConfigurator: {fileID: 2013129107579668837, guid: fa4e8b6466206ac4cb113d8b98d3a8e5, type: 3} - root: {fileID: 4098477273951441249} - selector: {fileID: 2866604199148495954} + _countablePrizeConfigurator: {fileID: 5680088572919083885, guid: 8b123b0e9e795b842948d07b6e135d84, type: 3} + _qrPrizeConfigurator: {fileID: 8201104754896611310, guid: 419ede247f441324681aeb7eeacf4ea0, type: 3} + _vitrinaPrizeConfigurator: {fileID: 2013129107579668837, guid: fa4e8b6466206ac4cb113d8b98d3a8e5, type: 3} + _configuratorsRoot: {fileID: 4098477273951441249} --- !u!114 &2520044121805980792 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1676,6 +1699,17 @@ PrefabInstance: m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: c244207dd10914141af206f200c91b0a, type: 3} +--- !u!114 &6745117456236530743 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 6123304204807351912, guid: c244207dd10914141af206f200c91b0a, type: 3} + m_PrefabInstance: {fileID: 675856799151252063} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6422f146b37ef714db72d2550697c362, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MSignalButton --- !u!224 &8162745928107063912 stripped RectTransform: m_CorrespondingSourceObject: {fileID: 8657825045506627639, guid: c244207dd10914141af206f200c91b0a, type: 3} diff --git a/Assets/Scenes/GameScene3D.unity b/Assets/Scenes/GameScene3D.unity index f718f02..9538e73 100644 --- a/Assets/Scenes/GameScene3D.unity +++ b/Assets/Scenes/GameScene3D.unity @@ -18836,7 +18836,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &1967587903 RectTransform: m_ObjectHideFlags: 0 @@ -22504,7 +22504,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5423109734219761443, guid: dcef42dd0beb95f4e9b230192966f091, type: 3} propertyPath: m_IsActive - value: 0 + value: 1 objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] diff --git a/Assets/Scripts/PrizeSystem/CharacterPrizeConfigurator.cs b/Assets/Scripts/PrizeSystem/CharacterPrizeConfigurator.cs index f6ed04c..4d53d28 100644 --- a/Assets/Scripts/PrizeSystem/CharacterPrizeConfigurator.cs +++ b/Assets/Scripts/PrizeSystem/CharacterPrizeConfigurator.cs @@ -1,22 +1,65 @@ -using CharacterSystem; +using CharacterSystem; +using PrizeSystem; +using System; using UnityEngine; public class CharacterPrizeConfigurator : MonoBehaviour { + public event Action OnClosed; + [SerializeField] private CharacterSelectionPanel _characterSelection; [SerializeField] private PrizesSelector _prizesSelector; - private void OnEnable() + private void Awake() { - _characterSelection.gameObject.SetActive(true); - _prizesSelector.gameObject.SetActive(false); - - _characterSelection.OnCharacterSelected += OnCharacterSelected; + _characterSelection.OnCharacterSelected += OpenPrizeSelector; + _characterSelection.OnExitRequested += SaveAndClose; + _prizesSelector.OnConfirmed += OpenCharacterSelection; } - private void OnCharacterSelected(Character character) + private void OnEnable() + { + OpenCharacterSelection(); + } + + private void Start() + { + OpenCharacterSelection(); + } + + private void OnDestroy() + { + _characterSelection.OnCharacterSelected -= OpenPrizeSelector; + _characterSelection.OnExitRequested -= SaveAndClose; + _prizesSelector.OnConfirmed -= OpenCharacterSelection; + } + + private void OpenPrizeSelector(Character character) { _characterSelection.gameObject.SetActive(false); _prizesSelector.Open(character); + ControlsPerformer.Instance.SetControlable(_prizesSelector); + } + + private void OpenCharacterSelection() + { + _prizesSelector.gameObject.SetActive(false); + _characterSelection.gameObject.SetActive(true); + _characterSelection.Open(); + + if (ControlsPerformer.Instance != null) + ControlsPerformer.Instance.SetControlable(_characterSelection); + } + + private void SaveAndClose() + { + PrizeSerializer.SaveToFile(); + PrizeSystemManager.SavePrizes(); + + if (ControlsPerformer.Instance != null) + ControlsPerformer.Instance.SetControlable(null); + + gameObject.SetActive(false); + OnClosed?.Invoke(); } } diff --git a/Assets/Scripts/PrizeSystem/Configuration/AvailablePrizesTable.cs b/Assets/Scripts/PrizeSystem/Configuration/AvailablePrizesTable.cs index 741ec5a..ef6b01b 100644 --- a/Assets/Scripts/PrizeSystem/Configuration/AvailablePrizesTable.cs +++ b/Assets/Scripts/PrizeSystem/Configuration/AvailablePrizesTable.cs @@ -1,81 +1,41 @@ -using PrizeSystem; +using PrizeSystem; +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; -using UnityEngine.UI; +using UnityEngine.Serialization; public class AvailablePrizesTable : MonoBehaviour { - public PrizeButton _prizeButtonPrefab; + public event Action OnPrizeConfigured; - [SerializeField] RectTransform _buttonsRoot; + [SerializeField] private PrizeButton _prizeButtonPrefab; + [SerializeField] private RectTransform _buttonsRoot; + [SerializeField, FormerlySerializedAs("countablePrizeConfigurator")] private CountablePrizeConfigurator _countablePrizeConfigurator; + [SerializeField, FormerlySerializedAs("qrPrizeConfigurator")] private QrPrizeConfigurator _qrPrizeConfigurator; + [SerializeField, FormerlySerializedAs("vitrinaPrizeConfigurator")] private VitrinaPrizeConfigurator _vitrinaPrizeConfigurator; + [SerializeField, FormerlySerializedAs("root")] private RectTransform _configuratorsRoot; - public List prizeButtons; - - public CountablePrizeConfigurator countablePrizeConfigurator; - public QrPrizeConfigurator qrPrizeConfigurator; - public VitrinaPrizeConfigurator vitrinaPrizeConfigurator; + private readonly List _prizeButtons = new(); - public PrizeSet cachedSet; - public RectTransform root; - public PrizesSelector selector; + public IReadOnlyList PrizeButtons => _prizeButtons; + public PrizeButton PrizeButtonPrefab => _prizeButtonPrefab; - public void Refresh(PrizeSet set) + public void Refresh(PrizeSet prizeSet) { - cachedSet = set; ClearButtons(); - prizeButtons = new(); - foreach (var type in GetMissingPrizeTypes(set.Prizes)) + foreach (PrizeType type in GetMissingPrizeTypes(prizeSet.Prizes)) { - var button = Instantiate(_prizeButtonPrefab, _buttonsRoot); + PrizeButton button = Instantiate(_prizeButtonPrefab, _buttonsRoot); button.Configure(type); - button.OnPrize += OnPrizeTypeSelected; - prizeButtons.Add(button); + button.OnPrize += OpenConfigurator; + _prizeButtons.Add(button); } } - private void OnPrizeTypeSelected(PrizeType type) + public static List GetMissingPrizeTypes(IReadOnlyCollection prizes) { - print($"Выбран приз {type}"); - - - BasePrizeConfigurator window = type switch - { - PrizeType.Hopper => countablePrizeConfigurator, - PrizeType.Marbles => countablePrizeConfigurator, - PrizeType.Ticket => countablePrizeConfigurator, - PrizeType.VitrinaBox => vitrinaPrizeConfigurator, - PrizeType.Qr => qrPrizeConfigurator - }; - - var configurator = Instantiate(window, root); - configurator.Configure(type); - - configurator.OnPrizeConfigured += Configurator_OnPrizeConfigured; - ControlsPerformer.Instance.SetControlable(configurator); - - } - - private void Configurator_OnPrizeConfigured(Prize prize) - { - cachedSet.Prizes.Add(prize); - Refresh(cachedSet); - selector.skipFrame = true; - } - - private void ClearButtons() - { - for (int i = 0; i < _buttonsRoot.childCount; i++) - { - var go = _buttonsRoot.GetChild(i).gameObject; - Destroy(go); - } - } - - public static List GetMissingPrizeTypes(List prizes) - { - // Все типы призов (кроме None и Empty) - var allTypes = new List + PrizeType[] allTypes = { PrizeType.Hopper, PrizeType.Marbles, @@ -84,62 +44,63 @@ public class AvailablePrizesTable : MonoBehaviour PrizeType.VitrinaBox }; - // Определяем, есть ли в списке хоть один CountablePrize - bool hasCountablePrize = prizes.Any(p => p is CountablePrize); + bool hasCountablePrize = prizes.Any(prize => prize is CountablePrize); + HashSet existingTypes = prizes + .Where(prize => prize is not EmptyPrize) + .Select(prize => prize.PrizeType) + .ToHashSet(); - // Если есть CountablePrize, все счетные типы считаются добавленными - var countableTypes = new HashSet - { - PrizeType.Hopper, - PrizeType.Marbles, - PrizeType.Ticket - }; - - // Собираем существующие типы - var existingTypes = new HashSet(); - - foreach (var prize in prizes) - { - // Если это EmptyPrize - пропускаем (не влияет на существующие типы) - if (prize is EmptyPrize) continue; - - // Если это CountablePrize и у нас уже есть флаг, что счетные есть - if (prize is CountablePrize) - { - // Добавляем все счетные типы, если их еще нет - if (hasCountablePrize) - { - foreach (var type in countableTypes) - { - existingTypes.Add(type); - } - } - else - { - // На случай, если hasCountablePrize false (но такого быть не может) - existingTypes.Add(prize.PrizeType); - } - } - else - { - // Для не-счетных призов добавляем их конкретный тип - existingTypes.Add(prize.PrizeType); - } - } - - // Если есть CountablePrize, но не все типы добавлены, добавляем все счетные - if (hasCountablePrize) - { - foreach (var type in countableTypes) - { - existingTypes.Add(type); - } - } - - // Возвращаем типы, которых нет в существующих return allTypes - .Where(type => !existingTypes.Contains(type)) + .Where(type => IsCountable(type) ? !hasCountablePrize : !existingTypes.Contains(type)) .ToList(); } -} \ No newline at end of file + private void OpenConfigurator(PrizeType type) + { + BasePrizeConfigurator prefab = GetConfiguratorPrefab(type); + if (prefab == null) + { + Debug.LogError($"Не найден конфигуратор для приза {type}"); + return; + } + + BasePrizeConfigurator configurator = Instantiate(prefab, _configuratorsRoot); + configurator.OnPrizeConfigured += CompleteConfiguration; + configurator.Configure(type); + ControlsPerformer.Instance.SetControlable(configurator); + } + + private BasePrizeConfigurator GetConfiguratorPrefab(PrizeType type) + { + return type switch + { + PrizeType.Hopper => _countablePrizeConfigurator, + PrizeType.Marbles => _countablePrizeConfigurator, + PrizeType.Ticket => _countablePrizeConfigurator, + PrizeType.VitrinaBox => _vitrinaPrizeConfigurator, + PrizeType.Qr => _qrPrizeConfigurator, + _ => null + }; + } + + private void CompleteConfiguration(Prize prize) + { + OnPrizeConfigured?.Invoke(prize); + } + + private void ClearButtons() + { + _prizeButtons.Clear(); + for (int i = _buttonsRoot.childCount - 1; i >= 0; i--) + { + Transform child = _buttonsRoot.GetChild(i); + child.SetParent(null, false); + Destroy(child.gameObject); + } + } + + private static bool IsCountable(PrizeType type) + { + return type is PrizeType.Hopper or PrizeType.Marbles or PrizeType.Ticket; + } +} diff --git a/Assets/Scripts/PrizeSystem/Configuration/CharacterSelectionPanel.cs b/Assets/Scripts/PrizeSystem/Configuration/CharacterSelectionPanel.cs index 9fc544f..0dccd9d 100644 --- a/Assets/Scripts/PrizeSystem/Configuration/CharacterSelectionPanel.cs +++ b/Assets/Scripts/PrizeSystem/Configuration/CharacterSelectionPanel.cs @@ -1,9 +1,10 @@ -using CharacterSystem; +using CharacterSystem; using PrizeSystem; using System; using System.Collections.Generic; using TMPro; using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.UI; public class CharacterSelectionPanel : MonoBehaviour, IControlable @@ -11,114 +12,127 @@ public class CharacterSelectionPanel : MonoBehaviour, IControlable public event Action OnCharacterSelected; public event Action OnExitRequested; - [SerializeField] TextMeshProUGUI CharacterNameLabel; - [SerializeField] Image CharacterIcon; - [SerializeField] AudioClip ClickSound; - - [SerializeField] RectTransform _prizeTable; - [SerializeField] PrizeView _prizeViewPrefab; - - - private Character[] characters; - private int characterIndex = 0; - - [SerializeField] private List buttons; + [SerializeField, FormerlySerializedAs("CharacterNameLabel")] private TextMeshProUGUI _characterNameLabel; + [SerializeField, FormerlySerializedAs("CharacterIcon")] private Image _characterIcon; + [SerializeField] private RectTransform _prizeTable; + [SerializeField] private PrizeView _prizeViewPrefab; + [SerializeField, FormerlySerializedAs("buttons")] private List _buttons; [SerializeField] private MSignalButton _selectButton; [SerializeField] private MSignalButton _exitButton; - private int buttonIndex = 0; + private Character[] _characters; + private int _characterIndex; + private int _buttonIndex; - public int CharactersCount => characters.Length; - public Character SelectedCharacter => characters[characterIndex]; + public int CharactersCount => _characters?.Length ?? 0; + public Character SelectedCharacter => _characters[_characterIndex]; + public bool ControlsEnabled { get; set; } = true; + private MSignalButton SelectedButton => _buttons[_buttonIndex]; - public bool ControlsEnabled { get; set; } - private MSignalButton SelectedButton => buttons[buttonIndex]; - - private void Update() + private void Awake() { - UpdateControls(); + _selectButton.OnClick += SelectCharacter; + _exitButton.OnClick += RequestExit; } - + private void Start() { - _selectButton.OnClick += _selectButton_OnClick; - _exitButton.OnClick += _exitButton_OnClick; + _characters = CharactersController.GetCharactersArray(); + Open(); + } - characters = CharactersController.GetCharactersArray(); - characterIndex = 0; - DisplaySelectedCharacter(); + private void OnDestroy() + { + _selectButton.OnClick -= SelectCharacter; + _exitButton.OnClick -= RequestExit; + } - buttonIndex = 0; + public void Open() + { + if (_buttons == null || _buttons.Count == 0) + return; + + SelectedButton.DeselectAnimation(); + _buttonIndex = 0; SelectedButton.SelectAnimation(); + + if (_characters == null || _characters.Length == 0) + return; + + _characterIndex = Mathf.Clamp(_characterIndex, 0, _characters.Length - 1); DisplaySelectedCharacter(); } - - private void _selectButton_OnClick() - { - OnCharacterSelected?.Invoke(SelectedCharacter); - } - - private void _exitButton_OnClick() - { - OnExitRequested?.Invoke(); - } - public void SwitchCharacter(int direction) { - if (direction == 0) return; + if (direction == 0 || CharactersCount == 0) + return; - characterIndex = (characterIndex + direction + characters.Length) % characters.Length; + _characterIndex = (_characterIndex + direction + _characters.Length) % _characters.Length; DisplaySelectedCharacter(); } public void SwitchButton(int direction) { - if (direction == 0) return; + if (direction == 0) + return; - int newIndex = Mathf.Clamp(buttonIndex + direction, 0, buttons.Count - 1); - if (newIndex == buttonIndex) return; + int newIndex = Mathf.Clamp(_buttonIndex + direction, 0, _buttons.Count - 1); + if (newIndex == _buttonIndex) + return; SelectedButton.DeselectAnimation(); - buttonIndex = newIndex; + _buttonIndex = newIndex; SelectedButton.SelectAnimation(); } - private void DisplaySelectedCharacter() - { - CharacterNameLabel.text = SelectedCharacter.characterName; - CharacterIcon.sprite = SelectedCharacter.visualModule.icon; - - ClearTable(); - - var set = PrizeSystemManager.PrizeContainer.Sets[SelectedCharacter.CharacterIndex]; - DisplayPrizeSet(set); - } - public void UpdateControls() { + if (CharactersCount == 0) + return; + Vector2Int axes = In.GetAxis(JoystickSide.Both, InteractMode.Discrete); SwitchCharacter(axes.x); SwitchButton(-axes.y); - SelectedButton.TakeInput(In.GetAction(JoystickSide.Both, InteractMode.Continious)); } + private void SelectCharacter() + { + OnCharacterSelected?.Invoke(SelectedCharacter); + } + + private void RequestExit() + { + OnExitRequested?.Invoke(); + } + + private void DisplaySelectedCharacter() + { + _characterNameLabel.text = SelectedCharacter.characterName; + _characterIcon.sprite = SelectedCharacter.visualModule.icon; + + ClearTable(); + PrizeSet prizeSet = PrizeSystemManager.PrizeContainer.Sets[SelectedCharacter.CharacterIndex]; + DisplayPrizeSet(prizeSet); + } + private void ClearTable() { for (int i = _prizeTable.childCount - 1; i >= 0; i--) { - var go = _prizeTable.GetChild(i).gameObject; - Destroy(go); + Transform child = _prizeTable.GetChild(i); + child.SetParent(null, false); + Destroy(child.gameObject); } } private void DisplayPrizeSet(PrizeSet prizeSet) { - foreach (var item in prizeSet.Prizes) + foreach (Prize prize in prizeSet.Prizes) { - var view = Instantiate(_prizeViewPrefab, _prizeTable); - view.SetPrize(item); + PrizeView view = Instantiate(_prizeViewPrefab, _prizeTable); + view.SetPrize(prize); } } } diff --git a/Assets/Scripts/PrizeSystem/Configuration/CountablePrizeConfigurator.cs b/Assets/Scripts/PrizeSystem/Configuration/CountablePrizeConfigurator.cs index 8fb6377..c00c291 100644 --- a/Assets/Scripts/PrizeSystem/Configuration/CountablePrizeConfigurator.cs +++ b/Assets/Scripts/PrizeSystem/Configuration/CountablePrizeConfigurator.cs @@ -1,33 +1,45 @@ -using PrizeSystem; +using PrizeSystem; +using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.UI; public class CountablePrizeConfigurator : BasePrizeConfigurator, IControlable { - public Image icon; - public MNumberStepper numberInput; + [SerializeField, FormerlySerializedAs("icon")] private Image _icon; + [SerializeField, FormerlySerializedAs("numberInput")] private MNumberStepper _numberInput; + + private PrizeType _prizeType; public override void Configure(PrizeType prizeType) { - icon.sprite = PrizeSystemManager.GetPrizeIcon(prizeType); + _prizeType = prizeType; + _icon.sprite = PrizeSystemManager.GetPrizeIcon(prizeType); - int maxValue = prizeType switch - { + int maxValue = prizeType switch + { PrizeType.Hopper => 10, PrizeType.Ticket => 100, PrizeType.Marbles => 10, - _ => 1 + _ => 1 }; - numberInput.Initialize(value: 1, min: 1, maxValue); + _numberInput.Initialize(value: 1, min: 1, maxValue); } public override void UpdateControls() { - numberInput.SetDirection(In.GetAxis(JoystickSide.Both, InteractMode.Continious).x); + _numberInput.SetDirection(In.GetAxis(JoystickSide.Both, InteractMode.Continious).x); - if (In.GetAction(JoystickSide.Both, InteractMode.Continious)) + if (In.GetAction(JoystickSide.Both, InteractMode.Discrete)) { - Prize prize = PrizeUtils.CreatePrize(CountablePrizeType.Hopper, numberInput.Value); + CountablePrizeType countableType = _prizeType switch + { + PrizeType.Hopper => CountablePrizeType.Hopper, + PrizeType.Ticket => CountablePrizeType.Ticket, + PrizeType.Marbles => CountablePrizeType.Marbles, + _ => CountablePrizeType.Hopper + }; + Prize prize = PrizeUtils.CreatePrize(countableType, _numberInput.Value); Complete(prize); } } diff --git a/Assets/Scripts/PrizeSystem/Models/PrizeConverter.cs b/Assets/Scripts/PrizeSystem/Models/PrizeConverter.cs index 767da74..3736083 100644 --- a/Assets/Scripts/PrizeSystem/Models/PrizeConverter.cs +++ b/Assets/Scripts/PrizeSystem/Models/PrizeConverter.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -6,40 +6,74 @@ namespace PrizeSystem { public class PrizeConverter : JsonConverter { - public override Prize ReadJson(JsonReader reader, Type objectType, Prize existingValue, bool hasExistingValue, JsonSerializer serializer) + public override Prize ReadJson( + JsonReader reader, + Type objectType, + Prize existingValue, + bool hasExistingValue, + JsonSerializer serializer) { - var jsonObject = JObject.Load(reader); - - var prizeTypeToken = jsonObject["PrizeType"]; + JObject jsonObject = JObject.Load(reader); + JToken prizeTypeToken = jsonObject["PrizeType"]; if (prizeTypeToken == null) throw new JsonSerializationException("Missing PrizeType field"); - - var prizeType = prizeTypeToken.ToObject(); - // Создаем объект нужного типа - Prize prize = prizeType switch + PrizeType prizeType = prizeTypeToken.ToObject(serializer); + return prizeType switch { PrizeType.None => new EmptyPrize(), - PrizeType.Qr => new QrPrize(""), // временный, заполним ниже - PrizeType.VitrinaBox => new VitrinaPrize(Array.Empty()), - PrizeType.Ticket or PrizeType.Marbles or PrizeType.Hopper => - new CountablePrize(CountablePrizeType.Ticket, 0), // временный + PrizeType.Qr => new QrPrize(jsonObject.Value("PrizeText") ?? string.Empty), + PrizeType.VitrinaBox => new VitrinaPrize( + jsonObject["Boxes"]?.ToObject(serializer) ?? Array.Empty()), + PrizeType.Hopper or PrizeType.Ticket or PrizeType.Marbles => new CountablePrize( + jsonObject["Type"]?.ToObject(serializer) + ?? GetCountableType(prizeType), + jsonObject.Value("Amount") ?? 0), _ => throw new NotSupportedException($"Unknown prize type: {prizeType}") }; - - // Заполняем поля через стандартную десериализацию - using (var subReader = jsonObject.CreateReader()) - { - serializer.Populate(subReader, prize); - } - - return prize; } public override void WriteJson(JsonWriter writer, Prize value, JsonSerializer serializer) { - var jsonObject = JObject.FromObject(value, serializer); - jsonObject.WriteTo(writer); + writer.WriteStartObject(); + WriteProperty(writer, serializer, "PrizeType", value.PrizeType); + + switch (value) + { + case CountablePrize countablePrize: + WriteProperty(writer, serializer, "Type", countablePrize.Type); + WriteProperty(writer, serializer, "Amount", countablePrize.Amount); + break; + case QrPrize qrPrize: + WriteProperty(writer, serializer, "PrizeText", qrPrize.PrizeText); + break; + case VitrinaPrize vitrinaPrize: + WriteProperty(writer, serializer, "Boxes", vitrinaPrize.Boxes); + break; + } + + writer.WriteEndObject(); + } + + private static void WriteProperty( + JsonWriter writer, + JsonSerializer serializer, + string propertyName, + object value) + { + writer.WritePropertyName(propertyName); + serializer.Serialize(writer, value); + } + + private static CountablePrizeType GetCountableType(PrizeType prizeType) + { + return prizeType switch + { + PrizeType.Hopper => CountablePrizeType.Hopper, + PrizeType.Ticket => CountablePrizeType.Ticket, + PrizeType.Marbles => CountablePrizeType.Marbles, + _ => CountablePrizeType.Hopper + }; } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/PrizeSystem/UI/PrizeButton.cs b/Assets/Scripts/PrizeSystem/UI/PrizeButton.cs index ff5a610..8635e5d 100644 --- a/Assets/Scripts/PrizeSystem/UI/PrizeButton.cs +++ b/Assets/Scripts/PrizeSystem/UI/PrizeButton.cs @@ -32,6 +32,13 @@ public class PrizeButton : MSignalButton _textMeshPro.text = GetText(prizeType); } + public void Configure(Prize prize) + { + _prizeType = prize.PrizeType; + _image.sprite = PrizeSystemManager.GetPrizeIcon(prize.PrizeType); + _textMeshPro.text = PrizeView.GetPrizeText(prize); + } + private string GetText(PrizeType prizeType) => prizeType switch { @@ -43,4 +50,4 @@ public class PrizeButton : MSignalButton _ => "ошибка" }; -} \ No newline at end of file +} diff --git a/Assets/Scripts/PrizeSystem/UI/PrizesSelector.cs b/Assets/Scripts/PrizeSystem/UI/PrizesSelector.cs index 4d7e0c7..b593e64 100644 --- a/Assets/Scripts/PrizeSystem/UI/PrizesSelector.cs +++ b/Assets/Scripts/PrizeSystem/UI/PrizesSelector.cs @@ -1,78 +1,250 @@ -using CharacterSystem; +using CharacterSystem; using PrizeSystem; +using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; +using UnityEngine.Serialization; +using UnityEngine.UI; -public class PrizesSelector : MonoBehaviour +public class PrizesSelector : MonoBehaviour, IControlable { - [SerializeField] RectTransform _addedPrizesTable; - [SerializeField] PrizeView _prizeViewPrefab; + public event Action OnConfirmed; - [SerializeField] AvailablePrizesTable _availablePrizesTable; - int index = 0; + [SerializeField] private RectTransform _addedPrizesTable; + [SerializeField] private AvailablePrizesTable _availablePrizesTable; + [SerializeField] private MSignalButton _selectButton; + [SerializeField, FormerlySerializedAs("pointer")] private RectTransform _pointer; + private readonly List _addedPrizeButtons = new(); + private PrizeSet _prizeSet; + private SelectionArea _selectionArea; + private int _rowIndex; + private bool _waitForActionRelease; - private PrizeSet set; + public bool ControlsEnabled { get; set; } = true; - public bool skipFrame = false; + private enum SelectionArea + { + Available, + Added, + Confirm + } - [SerializeField] Transform pointer; + private void Awake() + { + _availablePrizesTable.OnPrizeConfigured += OnPrizeConfigured; + _selectButton.OnClick += Confirm; + } + + private void OnDestroy() + { + _availablePrizesTable.OnPrizeConfigured -= OnPrizeConfigured; + _selectButton.OnClick -= Confirm; + } public void Open(Character character) { - print($"открыто с персонажем {character.CharacterIndex}"); gameObject.SetActive(true); + _prizeSet = PrizeSystemManager.PrizeContainer.Sets[character.CharacterIndex]; + RefreshView(); - set = PrizeSystemManager.PrizeContainer.Sets[character.CharacterIndex]; - UpdateView(); + _selectionArea = _availablePrizesTable.PrizeButtons.Count > 0 + ? SelectionArea.Available + : GetInitialFallbackArea(); + _rowIndex = 0; + _waitForActionRelease = In.GetAction(JoystickSide.Both, InteractMode.Continious); + RebuildLayouts(); + UpdatePointer(); } - private void UpdateView() + public void UpdateControls() { - ClearTable(); - DisplayPrizeSet(set); - - _availablePrizesTable.Refresh(set); - } - - - private void Update() - { - if (skipFrame) + bool actionPressed = In.GetAction(JoystickSide.Both, InteractMode.Continious); + if (_waitForActionRelease) { - if(In.AnyButton() == false) - skipFrame = false; + _waitForActionRelease = actionPressed; return; } - if (_availablePrizesTable.prizeButtons == null) return; - if (_availablePrizesTable.prizeButtons.Count == 0) return; + Vector2Int axes = In.GetAxis(JoystickSide.Both, InteractMode.Discrete); + NavigateHorizontal(axes.x); + NavigateVertical(axes.y); + GetSelectedButton()?.TakeInput(actionPressed); + } - index -= In.GetAxis(JoystickSide.Both, InteractMode.Discrete).y; - index = Mathf.Clamp(index, 0, _availablePrizesTable.prizeButtons.Count); - pointer.position = _availablePrizesTable.prizeButtons[index].transform.position; + private void RefreshView() + { + ClearAddedPrizes(); + DisplayAddedPrizes(); + _availablePrizesTable.Refresh(_prizeSet); + } - - if (In.GetAction(JoystickSide.Both, InteractMode.Continious)) + private void DisplayAddedPrizes() + { + foreach (Prize prize in _prizeSet.Prizes.Where(prize => prize is not EmptyPrize)) { - _availablePrizesTable.prizeButtons[index].ClickAnimation(); + PrizeButton button = Instantiate(_availablePrizesTable.PrizeButtonPrefab, _addedPrizesTable); + button.Configure(prize); + button.OnPrize += _ => RemovePrize(prize); + _addedPrizeButtons.Add(button); } } - private void ClearTable() + private void ClearAddedPrizes() { + _addedPrizeButtons.Clear(); for (int i = _addedPrizesTable.childCount - 1; i >= 0; i--) { - var go = _addedPrizesTable.GetChild(i).gameObject; - Destroy(go); + Transform child = _addedPrizesTable.GetChild(i); + child.SetParent(null, false); + Destroy(child.gameObject); } } - private void DisplayPrizeSet(PrizeSet prizeSet) + private void OnPrizeConfigured(Prize prize) { - foreach (var item in prizeSet.Prizes) + _prizeSet.Prizes.RemoveAll(item => item is EmptyPrize); + _prizeSet.Prizes.Add(prize); + RefreshView(); + + _selectionArea = SelectionArea.Added; + _rowIndex = _addedPrizeButtons.Count - 1; + _waitForActionRelease = true; + RebuildLayouts(); + UpdatePointer(); + ControlsPerformer.Instance.SetControlable(this); + } + + private void RemovePrize(Prize prize) + { + _prizeSet.Prizes.Remove(prize); + EnsureEmptyPrizeWhenNeeded(); + RefreshView(); + + _selectionArea = _availablePrizesTable.PrizeButtons.Count > 0 + ? SelectionArea.Available + : GetInitialFallbackArea(); + _rowIndex = 0; + _waitForActionRelease = true; + RebuildLayouts(); + UpdatePointer(); + } + + private void EnsureEmptyPrizeWhenNeeded() + { + if (_prizeSet.Prizes.Count == 0) + _prizeSet.Prizes.Add(new EmptyPrize()); + } + + private void NavigateHorizontal(int direction) + { + if (direction < 0 && _availablePrizesTable.PrizeButtons.Count > 0) { - var view = Instantiate(_prizeViewPrefab, _addedPrizesTable); - view.SetPrize(item); + _selectionArea = SelectionArea.Available; + ClampRowIndex(); + UpdatePointer(); + } + else if (direction > 0 && _addedPrizeButtons.Count > 0) + { + _selectionArea = SelectionArea.Added; + ClampRowIndex(); + UpdatePointer(); } } + + private void NavigateVertical(int direction) + { + if (direction == 0) + return; + + if (_selectionArea == SelectionArea.Confirm) + { + if (direction > 0) + SelectBottomAvailableRow(); + return; + } + + int buttonsCount = GetCurrentButtons().Count; + if (buttonsCount == 0) + { + SelectConfirm(); + return; + } + + if (direction < 0 && _rowIndex == buttonsCount - 1) + SelectConfirm(); + else + _rowIndex = Mathf.Clamp(_rowIndex - direction, 0, buttonsCount - 1); + + UpdatePointer(); + } + + private void SelectBottomAvailableRow() + { + if (_availablePrizesTable.PrizeButtons.Count > 0) + { + _selectionArea = SelectionArea.Available; + _rowIndex = _availablePrizesTable.PrizeButtons.Count - 1; + } + else if (_addedPrizeButtons.Count > 0) + { + _selectionArea = SelectionArea.Added; + _rowIndex = _addedPrizeButtons.Count - 1; + } + + UpdatePointer(); + } + + private void SelectConfirm() + { + _selectionArea = SelectionArea.Confirm; + _rowIndex = 0; + } + + private void ClampRowIndex() + { + _rowIndex = Mathf.Clamp(_rowIndex, 0, GetCurrentButtons().Count - 1); + } + + private void UpdatePointer() + { + MSignalButton selectedButton = GetSelectedButton(); + if (selectedButton != null) + _pointer.position = selectedButton.transform.position; + } + + private void RebuildLayouts() + { + Canvas.ForceUpdateCanvases(); + LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)_availablePrizesTable.transform); + LayoutRebuilder.ForceRebuildLayoutImmediate(_addedPrizesTable); + Canvas.ForceUpdateCanvases(); + } + + private MSignalButton GetSelectedButton() + { + if (_selectionArea == SelectionArea.Confirm) + return _selectButton; + + IReadOnlyList buttons = GetCurrentButtons(); + return buttons.Count > 0 ? buttons[_rowIndex] : null; + } + + private IReadOnlyList GetCurrentButtons() + { + return _selectionArea == SelectionArea.Added + ? _addedPrizeButtons + : _availablePrizesTable.PrizeButtons; + } + + private SelectionArea GetInitialFallbackArea() + { + return _addedPrizeButtons.Count > 0 ? SelectionArea.Added : SelectionArea.Confirm; + } + + private void Confirm() + { + OnConfirmed?.Invoke(); + } }