Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ImLukzy/ChefDash/llms.txt

Use this file to discover all available pages before exploring further.

VictoryView is the screen that greets the player the moment a round’s timer hits zero. It provides an honest, un-animated summary of how the session went: a win/lose headline, a row of up to three stars, a performance card showing orders served and total coins, and a single action button that returns the player to the level map. The screen deliberately uses a light cream palette — a visual exhale after the dark arcade intensity of RecipesView.

Activation

VictoryView becomes visible when gameState.activeTab is set to "level_complete". This transition is triggered inside RecipesView’s timer handler, immediately after gameState.finishRound(starsEarned:) is called:
// RecipesView — timer expiry block
gameState.finishRound(starsEarned: estrellasFinales)

withAnimation(.spring()) {
    gameState.activeTab = "level_complete"
}
Like RecipesView, this screen hides the CustomTabBar. ContentView suppresses the tab bar whenever activeTab is "kitchen_round" or "level_complete", ensuring neither gameplay screen is cluttered by navigation chrome.

Win vs. Lose State

The view reads two values from GameState and derives everything else locally:
let level = gameState.currentLevel
let completadas = gameState.ordersCompletedInSession
let ganoPartida = completadas >= level.targetOrders

🎉 Round Complete

Condition: ordersCompletedInSession >= currentLevel.targetOrdersHeadline: ”🎉 ¡RONDA COMPLETADA!” rendered in green.Action button: “CONTINUAR AL MAPA” with a green background.

⏰ Time's Up

Condition: ordersCompletedInSession < currentLevel.targetOrdersHeadline: ”⏰ ¡TIEMPO AGOTADO!” rendered in orange (Color(red: 0.96, green: 0.44, blue: 0.13)).Action button: “REINTENTAR NIVEL” with the same orange background.

Star Calculation

Stars are calculated independently in both RecipesView (passed to finishRound) and VictoryView (for display). Both use the same formula:
let estrellasSesion = completadas >= level.targetOrders ? 3
    : (completadas >= max(1, level.targetOrders / 2) ? 2
    : (completadas > 0 ? 1 : 0))
Orders CompletedStars Awarded
>= targetOrders⭐⭐⭐
>= targetOrders / 2⭐⭐
> 0
0(none)
The max(1, level.targetOrders / 2) guard ensures that on a level with targetOrders == 1, a player cannot earn 2 stars with zero completed orders — the threshold for 2 stars is always at least 1. The three star slots are rendered with Image(systemName: index < estrellasSesion ? "star.fill" : "star"). Filled stars use the orange accent color at scaleEffect(1.1); empty stars are gray and un-scaled.

Performance Card

Below the star row, a white rounded card (cornerRadius: 22) shows two metrics from the current session:
// "Comandas Servidas" row
Text("\(completadas) / \(level.targetOrders)")
    .foregroundColor(ganoPartida ? .green : .black)

// "Monedero Total" row
Text("\(gameState.coins) 🪙")
  • Comandas Servidas — orders completed this session out of the level target. The count turns green on a win.
  • Monedero Total — the player’s total accumulated coin balance (not just coins earned this round), pulled directly from gameState.coins.
Both outcomes share the same button action body:
Button(action: {
    if gameState.isHapticsEnabled {
        UIImpactFeedbackGenerator(style: .medium).impactOccurred()
    }

    gameState.currentBurgerStack.removeAll()

    withAnimation(.spring(response: 0.4, dampingFraction: 0.75)) {
        gameState.activeTab = "map.fill"
    }
}) {
    Text(ganoPartida ? "CONTINUAR AL MAPA" : "REINTENTAR NIVEL")
        ...
}
Both paths clear currentBurgerStack and navigate to "map.fill". The only difference is the button label and color.
Tapping “REINTENTAR NIVEL” does not restart the level directly. It returns the player to the map screen, where they must tap the same level node again to open the pre-game modal and then press “JUGAR” to begin a new attempt. There is no in-place level restart shortcut.

finishRound: Persisting Stars and Unlocking the Next Level

finishRound(starsEarned:) is called on GameState before the transition to VictoryView. It runs asynchronously on the main queue to avoid UI freezes:
func finishRound(starsEarned: Int) {
    DispatchQueue.main.async {
        if starsEarned > 0 {
            if self.selectedLevelIndex < self.levels.count {
                // 1. Persist stars (only improves — never decreases a prior score)
                self.levels[self.selectedLevelIndex].starsEarned =
                    max(self.levels[self.selectedLevelIndex].starsEarned, starsEarned)

                // 2. Find the next level by ID
                let currentLevelId = self.levels[self.selectedLevelIndex].id
                let nextLevelId = currentLevelId + 1

                // 3. Unlock it
                if let nextLevelIndex = self.levels.firstIndex(where: { $0.id == nextLevelId }) {
                    self.levels[nextLevelIndex].isUnlocked = true
                }
            }
        }
    }
}
Key behaviors:
  • Stars are only persisted if starsEarned > 0. A zero-star run does not touch starsEarned or unlock anything.
  • The best score is preserved: max(existing, new) ensures a previous 3-star run is never overwritten by a worse attempt.
  • Unlocking uses ID-based lookup (firstIndex(where: { $0.id == nextLevelId })), not index arithmetic, making it safe even if the levels array is reordered.
  • If the player is already on the last level, firstIndex simply returns nil and no unlock occurs.

Visual Design

VictoryView uses a distinct light palette, separate from the dark arcade theme of RecipesView:
private let creamBackground = Color(red: 0.98, green: 0.97, blue: 0.95)
private let orangeAccent    = Color(red: 0.96, green: 0.44, blue: 0.13)
The cream background fills the entire screen via .background(creamBackground.ignoresSafeArea()). The performance card sits on Color.white with a very subtle shadow (opacity: 0.02), giving it a soft lift against the warm cream field.

Build docs developers (and LLMs) love