10 lives, game over for Valentines

This commit is contained in:
Michal Pikulski
2025-12-19 17:10:48 +01:00
parent e550d5ce92
commit 8e889d5df0
5 changed files with 1070 additions and 358 deletions

View File

@@ -35,6 +35,13 @@ namespace Minigames.Airplane.Core
[SerializeField] private UI.AirplaneSelectionUI selectionUI;
[SerializeField] private UI.AirplaneAbilityButton abilityButton;
[Header("UI")]
[SerializeField] private Minigames.CardSorting.UI.LivesDisplay livesDisplay;
[SerializeField] private UI.GameOverScreen gameOverScreen;
[Header("Game Configuration")]
[SerializeField] private int maxLives = 10;
[Header("Debug")]
[SerializeField] private bool showDebugLogs = true;
@@ -74,12 +81,16 @@ namespace Minigames.Airplane.Core
private int _successCount;
private int _failCount;
private int _totalTurns;
private int _targetsHit;
private int _currentLives;
private IAirplaneSettings _cachedSettings;
public AirplaneGameState CurrentState => _currentState;
public Person CurrentPerson => _currentPerson;
public int SuccessCount => _successCount;
public int FailCount => _failCount;
public int TargetsHit => _targetsHit;
public int CurrentLives => _currentLives;
#endregion
@@ -196,6 +207,17 @@ namespace Minigames.Airplane.Core
Logging.Warning("[AirplaneGameManager] ⚠️ AbilityButton not assigned! Player will not be able to use abilities.");
Logging.Warning("[AirplaneGameManager] → Assign AirplaneAbilityButton GameObject in Inspector under 'Airplane Type Selection'");
}
// Validate UI elements
if (livesDisplay == null)
{
Logging.Warning("[AirplaneGameManager] ⚠️ LivesDisplay not assigned! Lives will not be displayed.");
}
if (gameOverScreen == null)
{
Logging.Warning("[AirplaneGameManager] ⚠️ GameOverScreen not assigned! Game over screen will not be shown.");
}
}
#endregion
@@ -223,6 +245,15 @@ namespace Minigames.Airplane.Core
{
if (showDebugLogs) Logging.Debug("[AirplaneGameManager] ===== GAME STARTING =====");
// Initialize lives
_currentLives = maxLives;
_targetsHit = 0;
if (livesDisplay != null)
{
livesDisplay.Initialize(maxLives);
}
StartCoroutine(IntroSequence());
}
@@ -693,6 +724,9 @@ namespace Minigames.Airplane.Core
_currentAirplane = airplane;
// Lose a life on each shot
LoseLife();
// Disable launch controller
if (launchController != null)
{
@@ -785,6 +819,7 @@ namespace Minigames.Airplane.Core
{
_lastShotHit = true;
_successCount++;
_targetsHit++;
// Hide target UI immediately on successful hit
if (spawnManager != null)
@@ -792,7 +827,7 @@ namespace Minigames.Airplane.Core
spawnManager.HideTargetUI();
}
if (showDebugLogs) Logging.Debug($"[AirplaneGameManager] ✓ SUCCESS! Hit correct target: {targetName}");
if (showDebugLogs) Logging.Debug($"[AirplaneGameManager] ✓ SUCCESS! Hit correct target: {targetName} (Total targets hit: {_targetsHit})");
}
/// <summary>
@@ -859,7 +894,9 @@ namespace Minigames.Airplane.Core
if (showDebugLogs)
{
Logging.Debug($"[AirplaneGameManager] Turn result: {(success ? "SUCCESS" : "FAILURE")}" +
$"\n Score: {_successCount} / {_totalTurns}");
$"\n Score: {_successCount} / {_totalTurns}" +
$"\n Targets Hit: {_targetsHit}" +
$"\n Lives Remaining: {_currentLives}");
}
OnPersonFinishTurn?.Invoke(_currentPerson, success);
@@ -883,21 +920,138 @@ namespace Minigames.Airplane.Core
launchController.ClearActiveAirplane();
}
// Route to appropriate flow based on result
if (success)
// Check for game over conditions
bool allTargetsHit = _targetsHit >= 3;
bool outOfLives = _currentLives <= 0;
if (allTargetsHit || outOfLives)
{
// Success: Go to person shuffle flow
StartCoroutine(ExecutePersonShuffleFlow());
// Game over - trigger end game sequence
if (showDebugLogs)
{
string reason = allTargetsHit ? "All 3 targets hit!" : "Out of lives!";
Logging.Debug($"[AirplaneGameManager] Game ending: {reason}");
}
StartCoroutine(EndGameSequence());
}
else
{
// Failure: Go to repeat shot flow
StartCoroutine(ExecuteRepeatShotFlow());
// Route to appropriate flow based on result
if (success)
{
// Success: Go to person shuffle flow
StartCoroutine(ExecutePersonShuffleFlow());
}
else
{
// Failure: Go to repeat shot flow
StartCoroutine(ExecuteRepeatShotFlow());
}
}
}
/// <summary>
/// Game over - no more people
/// Lose a life and update UI
/// </summary>
private void LoseLife()
{
_currentLives--;
if (livesDisplay != null)
{
livesDisplay.RemoveLife();
}
if (showDebugLogs)
{
Logging.Debug($"[AirplaneGameManager] Life lost! Remaining lives: {_currentLives}");
}
}
/// <summary>
/// End game sequence: award boosters, wait for completion, then show game over screen
/// </summary>
private IEnumerator EndGameSequence()
{
// Calculate booster reward
int boosterReward = CalculateBoosterReward(_targetsHit);
if (showDebugLogs)
{
Logging.Debug($"[AirplaneGameManager] Targets hit: {_targetsHit}, awarding {boosterReward} booster pack(s)");
}
// Show booster pack reward UI and wait for completion
if (boosterReward > 0)
{
if (showDebugLogs)
{
Logging.Debug("[AirplaneGameManager] Starting booster giver sequence via GameManager");
}
var task = GameManager.Instance.GiveBoosterPacksAsync(boosterReward);
// Wait for the task to complete
while (!task.IsCompleted)
{
yield return null;
}
// Check for exceptions
if (task.IsFaulted)
{
Logging.Warning($"[AirplaneGameManager] Booster pack reward failed: {task.Exception?.GetBaseException().Message}");
}
else
{
if (showDebugLogs)
{
Logging.Debug("[AirplaneGameManager] Booster giver sequence finished, proceeding to game over screen");
}
}
}
else
{
if (showDebugLogs)
{
Logging.Debug("[AirplaneGameManager] No boosters to award, proceeding directly to game over screen");
}
}
// Now show game over screen
if (gameOverScreen != null)
{
gameOverScreen.Show();
}
else
{
Logging.Error("[AirplaneGameManager] GameOverScreen reference missing!");
}
// Transition to GameOver state
yield return StartCoroutine(GameOver());
}
/// <summary>
/// Calculate booster pack reward based on targets hit.
/// Rules:
/// - 1 booster for participation (playing the game)
/// - 1 additional booster per target hit
/// </summary>
private int CalculateBoosterReward(int targetsHit)
{
// Base reward: 1 booster for participation
int reward = 1;
// Additional reward: 1 booster per target hit
reward += targetsHit;
return reward;
}
/// <summary>
/// Game over - final state
/// </summary>
private IEnumerator GameOver()
{
@@ -909,15 +1063,15 @@ namespace Minigames.Airplane.Core
$"\n Total Turns: {_totalTurns}" +
$"\n Success: {_successCount}" +
$"\n Failures: {_failCount}" +
$"\n Targets Hit: {_targetsHit}" +
$"\n Success Rate: {(_totalTurns > 0 ? (_successCount * 100f / _totalTurns) : 0):F1}%");
}
OnGameComplete?.Invoke();
// Stub: Show game over UI
yield return new WaitForSeconds(2f);
if (showDebugLogs) Logging.Debug("[AirplaneGameManager] Game complete");
yield return null;
}

View File

@@ -0,0 +1,91 @@
using System;
using UnityEngine;
using UnityEngine.UI;
using Core;
namespace Minigames.Airplane.UI
{
/// <summary>
/// Game over screen for Airplane minigame.
/// Displays when the game ends (out of lives or all targets hit) and allows restarting the level.
/// </summary>
public class GameOverScreen : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private Button dismissButton;
[SerializeField] private CanvasGroup canvasGroup;
private void Awake()
{
// Subscribe to button click
if (dismissButton != null)
{
dismissButton.onClick.AddListener(OnDismissClicked);
}
else
{
Logging.Error("[GameOverScreen] Dismiss button not assigned!");
}
// Get or add CanvasGroup for fade effects
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null)
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
}
// Hide by default
gameObject.SetActive(false);
}
private void OnDestroy()
{
// Unsubscribe from button
if (dismissButton != null)
{
dismissButton.onClick.RemoveListener(OnDismissClicked);
}
}
/// <summary>
/// Show the game over screen.
/// </summary>
public void Show()
{
gameObject.SetActive(true);
// Set canvas group for interaction
if (canvasGroup != null)
{
canvasGroup.alpha = 1f;
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
}
Logging.Debug("[GameOverScreen] Game Over screen shown");
}
/// <summary>
/// Called when dismiss button is clicked. Reloads the level.
/// </summary>
private async void OnDismissClicked()
{
Logging.Debug("[GameOverScreen] Dismiss button clicked - Reloading level");
// Hide this screen first
if (canvasGroup != null)
{
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
}
gameObject.SetActive(false);
var progress = new Progress<float>(p => Logging.Debug($"Loading progress: {p * 100:F0}%"));
await SceneManagerService.Instance.ReloadCurrentScene(progress);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c8bd5b50856a416cb64619d99b26061f
timeCreated: 1766159655