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.

GameState is the single source of truth that drives every screen in ChefDash. Declared as a class conforming to ObservableObject, it is instantiated exactly once in ChefDashApp as a @StateObject and then injected into the entire view hierarchy via SwiftUI’s @EnvironmentObject. Any view that needs to read or mutate game data — from the main map to the mid-round kitchen — reaches into this shared object, ensuring that a coin earned during gameplay is immediately reflected on the shop screen without any manual synchronisation.

Lifecycle & Injection

GameState is constructed at app launch in ChefDashApp, which calls setupLevels() in init() to build the full 20-level catalogue before the first frame is ever rendered.
// ChefDashApp (conceptual)
@StateObject private var gameState = GameState()

WindowGroup {
    ContentView()
        .environmentObject(gameState)
}
Every child view accesses the same instance:
@EnvironmentObject var gameState: GameState

Screen routing is driven by a single string property rather than an enum, keeping navigation changes simple one-liners from anywhere in the codebase.
@Published var activeTab: String = "house.fill"

house.fill

Home / landing screen shown on launch.

map.fill

The MainMapView level-selection map.

kitchen_round

The active gameplay screen (RecipesView).

level_complete

Post-round results screen (VictoryView).

cart.fill

The in-game shop where power-ups are purchased.
Setting activeTab triggers a SwiftUI animation and swaps the visible view immediately:
withAnimation(.spring(response: 0.4, dampingFraction: 0.75)) {
    gameState.activeTab = "map.fill"
}

Player Data

These four published properties hold player identity and preferences for the lifetime of the current GameState instance:
PropertyTypeDefaultPurpose
coinsInt150Spendable currency for shop purchases
playerNameString"Chef Novato"Display name shown in the HUD
playerAvatarString"👨‍🍳"Emoji avatar displayed on the profile screen
isHapticsEnabledBooltrueToggles UIImpactFeedbackGenerator calls site-wide

Level Management

@Published var levels: [Level] = []
@Published var selectedLevelIndex: Int = 0
The full catalogue of 20 levels lives in levels. When the player taps a level node on the map, MainMapView sets selectedLevelIndex to that node’s array position before presenting the pre-game modal.

currentLevel Computed Property

Rather than passing an index around, every gameplay view accesses the active level through a single computed property that guards against out-of-bounds reads:
var currentLevel: Level {
    guard selectedLevelIndex >= 0 && selectedLevelIndex < levels.count else {
        return levels[0]
    }
    return levels[selectedLevelIndex]
}

Active Gameplay State

These properties are reset at the start of each round by startNewRound() and mutated in real time during play:

currentBurgerStack

[String] — the ingredients the player has tapped so far in the current order, in sequence.

targetRecipeEmojis

[String] — the target emoji sequence the player must match to complete the current order.

currentRecipeName

String — human-readable name displayed above the order target (e.g. “Classic Cheeseburger”).

ordersCompletedInSession

Int — running count of successfully completed orders; compared against targetOrders for star rating.

comboMultiplier

Int — starts at 1, increments by 1 (up to 5×) on each consecutive success, resets to 1 on any error.

Visual Feedback Flags

Two short-lived boolean flags control the animated feedback overlays rendered in RecipesView:
@Published var showPerfectMessage: Bool = false  // ✨ success flash
@Published var showErrorMessage: Bool = false    // 💥 error flash
Both are set to true briefly and then cleared after 0.8 seconds via DispatchQueue.main.asyncAfter.

onRecipeSuccessBonusTime Callback

RecipesView owns the countdown timer, but GameState needs to trigger a time bonus on each successful order. Because a model layer should not reach directly into a view, GameState exposes a closure that the view assigns at onAppear:
// In GameState
var onRecipeSuccessBonusTime: (() -> Void)?

// Called inside triggerSuccess()
onRecipeSuccessBonusTime?()
// In RecipesView.onAppear
gameState.onRecipeSuccessBonusTime = {
    withAnimation {
        timeRemaining += 1.5
        randomizedIngredients.shuffle()
    }
}
This keeps timer mutation inside the view while allowing GameState to signal when a bonus should occur.

Shop State

@Published var shopInventory: [ShopItem] = [ /* three items */ ]
@Published var selectedUpgradesForRound: Set<String> = []
shopInventory is the in-memory list of purchasable consumables. selectedUpgradesForRound is populated in the pre-game modal when the player toggles an owned power-up on, and is consumed — cleared — the moment startNewRound() runs. See the Economy guide for full details on each item.

setupLevels() — Level Construction

setupLevels() is called once from init(). It builds three hand-crafted levels first, then appends seventeen procedurally generated levels (IDs 4–20).

Hand-crafted Levels (source)

private func setupLevels() {
    var allLevels = [
        Level(id: 1, name: "Burger Station", targetOrders: 3,
              availableIngredients: ["🍞", "🥩", "🧀"],
              possibleRecipes: [["🍞", "🥩", "🍞"], ["🍞", "🥩", "🧀", "🍞"]],
              baseTime: 40, isUnlocked: true, starsEarned: 0),

        Level(id: 2, name: "Green Diner", targetOrders: 4,
              availableIngredients: ["🍞", "🥩", "🧀", "🥬", "🍅"],
              possibleRecipes: [["🍞", "🥩", "🥬", "🍞"], ["🍞", "🥩", "🧀", "🍅", "🍞"]],
              baseTime: 45, isUnlocked: false, starsEarned: 0),

        Level(id: 3, name: "Bacon & Grill", targetOrders: 5,
              availableIngredients: ["🍞", "🥩", "🧀", "🥬", "🍅", "🥓", "🧅"],
              possibleRecipes: [["🍞", "🥩", "🧀", "🥓", "🍞"], ["🍞", "🥩", "🥬", "🍅", "🧅", "🍞"]],
              baseTime: 50, isUnlocked: false, starsEarned: 0)
    ]

    // Procedural levels 4–20 appended here...
}

Procedural Generation (Levels 4–20)

For levels 4 through 20, difficulty scales automatically using the level index i:
let poolIngredients = ["🍞", "🥩", "🧀", "🥬", "🍅", "🥓", "🧅", "🍄"]

for i in 4...20 {
    let ingredientCount = min(5 + (i / 5), poolIngredients.count)
    let ingredientsForLevel = Array(poolIngredients.prefix(ingredientCount))

    let target = 4 + (i / 2)

    let recipeOne = ["🍞", ingredientsForLevel.randomElement() ?? "🥩", "🧀", "🍞"]
    let recipeTwo = ["🍞", "🥩", ingredientsForLevel.randomElement() ?? "🥬",
                     ingredientsForLevel.randomElement() ?? "🍅", "🍞"]

    allLevels.append(
        Level(
            id: i,
            name: "Kitchen Arcade Expo \(i)",
            targetOrders: target,
            availableIngredients: ingredientsForLevel,
            possibleRecipes: [recipeOne, recipeTwo],
            baseTime: max(30, 55 - (i * i / 15)),
            isUnlocked: false,
            starsEarned: 0
        )
    )
}
Procedural recipes are generated once at app launch with randomElement(), so the two possible recipes for each of Levels 4–20 are fixed for the lifetime of that GameState instance — they do not re-randomise between rounds.

Build docs developers (and LLMs) love