Initial commit: Tiny Tackle Heroes Unity project
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
using System.Collections.Generic;
|
||||
using BumpCombat.Audio;
|
||||
using BumpCombat.Constants;
|
||||
using BumpCombat.Core;
|
||||
using BumpCombat.Player;
|
||||
using BumpCombat.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BumpCombat.Progression
|
||||
{
|
||||
public sealed class LevelUpController : MonoBehaviour
|
||||
{
|
||||
private const int MaximumSupportedOptions = 3;
|
||||
|
||||
[SerializeField] private GameObject panel;
|
||||
[SerializeField] private Text[] optionTexts;
|
||||
[System.NonSerialized] private ModifierDefinition[] modifierCatalog;
|
||||
|
||||
private readonly ModifierDefinition[] shownModifiers =
|
||||
new ModifierDefinition[MaximumSupportedOptions];
|
||||
private Image[] optionCardImages = System.Array.Empty<Image>();
|
||||
private ExperienceSystem experienceSystem;
|
||||
private PlayerStats playerStats;
|
||||
private PlayerHealth playerHealth;
|
||||
private int selectedIndex;
|
||||
private int shownCount;
|
||||
private int pendingSelections;
|
||||
private int ignoreInputThroughFrame;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
experienceSystem = FindAnyObjectByType<ExperienceSystem>();
|
||||
if (experienceSystem == null || panel == null)
|
||||
{
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
playerStats = experienceSystem.GetComponent<PlayerStats>();
|
||||
playerHealth = experienceSystem.GetComponent<PlayerHealth>();
|
||||
experienceSystem.OnLevelGained += QueueSelection;
|
||||
if (RunManager.Instance != null)
|
||||
{
|
||||
RunManager.Instance.OnSelectionChanged += HandleSelectionChanged;
|
||||
}
|
||||
PresentationUiStyle.ApplyPanel(panel.GetComponent<Image>());
|
||||
ApplySelectionTextStyle();
|
||||
CreateCardBackdrops();
|
||||
panel.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (experienceSystem != null)
|
||||
{
|
||||
experienceSystem.OnLevelGained -= QueueSelection;
|
||||
}
|
||||
if (RunManager.Instance != null)
|
||||
{
|
||||
RunManager.Instance.OnSelectionChanged -= HandleSelectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!panel.activeSelf
|
||||
|| shownCount == 0
|
||||
|| Time.frameCount <= ignoreInputThroughFrame
|
||||
|| Keyboard.current == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Keyboard.current.leftArrowKey.wasPressedThisFrame
|
||||
|| Keyboard.current.upArrowKey.wasPressedThisFrame)
|
||||
{
|
||||
selectedIndex = (selectedIndex + shownCount - 1) % shownCount;
|
||||
BumpCombatAudioService.Instance?.PlayUi("ui_move");
|
||||
UpdateSelectionVisual();
|
||||
}
|
||||
else if (Keyboard.current.rightArrowKey.wasPressedThisFrame
|
||||
|| Keyboard.current.downArrowKey.wasPressedThisFrame)
|
||||
{
|
||||
selectedIndex = (selectedIndex + 1) % shownCount;
|
||||
BumpCombatAudioService.Instance?.PlayUi("ui_move");
|
||||
UpdateSelectionVisual();
|
||||
}
|
||||
else if (Keyboard.current.spaceKey.wasPressedThisFrame)
|
||||
{
|
||||
bool applied = shownModifiers[selectedIndex].TryApply(
|
||||
playerStats,
|
||||
playerHealth);
|
||||
if (applied)
|
||||
{
|
||||
BumpCombatAudioService.Instance?.PlayUi("ui_confirm");
|
||||
}
|
||||
panel.SetActive(false);
|
||||
RunManager.Instance?.CloseSelection(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueSelection(int level)
|
||||
{
|
||||
pendingSelections++;
|
||||
TryOpenPendingSelection();
|
||||
}
|
||||
|
||||
private void TryOpenPendingSelection()
|
||||
{
|
||||
if (pendingSelections == 0
|
||||
|| RunManager.Instance == null
|
||||
|| !RunManager.Instance.TryOpenSelection(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BuildChoices())
|
||||
{
|
||||
pendingSelections--;
|
||||
RunManager.Instance.CloseSelection(this);
|
||||
return;
|
||||
}
|
||||
|
||||
pendingSelections--;
|
||||
selectedIndex = 0;
|
||||
panel.SetActive(true);
|
||||
ignoreInputThroughFrame = Time.frameCount;
|
||||
UpdateSelectionVisual();
|
||||
}
|
||||
|
||||
private bool BuildChoices()
|
||||
{
|
||||
List<ModifierDefinition> pool = new();
|
||||
ModifierDefinition[] definitions =
|
||||
modifierCatalog ?? GameplayConstants.Current.LevelUp.ModifierDefinitions;
|
||||
if (definitions != null)
|
||||
{
|
||||
foreach (ModifierDefinition definition in definitions)
|
||||
{
|
||||
if (definition != null && definition.CanApply(playerStats))
|
||||
{
|
||||
pool.Add(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shownCount = Mathf.Min(
|
||||
Mathf.Clamp(
|
||||
GameplayConstants.Current.LevelUp.OptionCount,
|
||||
1,
|
||||
MaximumSupportedOptions),
|
||||
optionTexts?.Length ?? 0,
|
||||
pool.Count);
|
||||
for (int i = 0; i < shownCount; i++)
|
||||
{
|
||||
int choiceIndex = Random.Range(0, pool.Count);
|
||||
shownModifiers[i] = pool[choiceIndex];
|
||||
pool.RemoveAt(choiceIndex);
|
||||
optionTexts[i].text = shownModifiers[i].DisplayName;
|
||||
optionTexts[i].gameObject.SetActive(true);
|
||||
}
|
||||
for (int i = shownCount; i < (optionTexts?.Length ?? 0); i++)
|
||||
{
|
||||
optionTexts[i].gameObject.SetActive(false);
|
||||
}
|
||||
return shownCount > 0;
|
||||
}
|
||||
|
||||
private void HandleSelectionChanged(bool isOpen)
|
||||
{
|
||||
if (!isOpen && !panel.activeSelf)
|
||||
{
|
||||
TryOpenPendingSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSelectionVisual()
|
||||
{
|
||||
for (int i = 0; i < (optionTexts?.Length ?? 0); i++)
|
||||
{
|
||||
bool selected = i == selectedIndex && i < shownCount;
|
||||
optionTexts[i].color = selected
|
||||
? new Color(1f, 0.85f, 0.2f)
|
||||
: Color.white;
|
||||
if (i < optionCardImages.Length)
|
||||
{
|
||||
PresentationUiStyle.ApplyCard(
|
||||
optionCardImages[i],
|
||||
selected);
|
||||
optionCardImages[i].gameObject.SetActive(
|
||||
optionTexts[i].gameObject.activeSelf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySelectionTextStyle()
|
||||
{
|
||||
Text[] texts = panel.GetComponentsInChildren<Text>(true);
|
||||
foreach (Text text in texts)
|
||||
{
|
||||
PresentationUiStyle.ApplyText(text, Mathf.Max(1, text.fontSize));
|
||||
if (text.name == "Title")
|
||||
{
|
||||
text.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
text.verticalOverflow = VerticalWrapMode.Truncate;
|
||||
text.resizeTextForBestFit = true;
|
||||
text.resizeTextMinSize = 24;
|
||||
text.resizeTextMaxSize = 38;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCardBackdrops()
|
||||
{
|
||||
optionCardImages = new Image[optionTexts?.Length ?? 0];
|
||||
for (int i = 0; i < optionCardImages.Length; i++)
|
||||
{
|
||||
Text optionText = optionTexts[i];
|
||||
if (optionText == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
optionCardImages[i] = PresentationUiStyle.CreateCardBackdrop(
|
||||
optionText,
|
||||
$"Option Card {i + 1}",
|
||||
optionText.rectTransform.sizeDelta);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public bool DebugIsSelectionVisible => panel != null && panel.activeSelf;
|
||||
|
||||
public void Configure(
|
||||
GameObject selectionPanel,
|
||||
Text[] options,
|
||||
ModifierDefinition[] definitions)
|
||||
{
|
||||
panel = selectionPanel;
|
||||
optionTexts = options;
|
||||
modifierCatalog = definitions;
|
||||
}
|
||||
|
||||
public ModifierDefinition GetShownModifier(int index)
|
||||
{
|
||||
return index >= 0 && index < shownCount
|
||||
? shownModifiers[index]
|
||||
: null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user