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.

These are the methods that drive ChefDash’s gameplay loop. Together they manage the full lifecycle of a round: initialising state before play begins, validating each ingredient tap in real time, generating fresh orders, rewarding or penalising the player, and writing the final star score back to the level map when the round ends. Public methods are callable by any view that has access to GameState via @EnvironmentObject; private helpers are documented here for completeness so you can understand the internal flow without reading the source.

startNewRound()

Prepares GameState for a brand-new round. This method is the entry point called by RecipesView (or whichever view hosts the countdown timer) immediately before the timer starts. What it does, in order:
  1. Resets ordersCompletedInSession to 0 and clears currentBurgerStack.
  2. Sets comboMultiplier to 1. If "knife" (Cuchillo Afilado) is present in selectedUpgradesForRound, it overrides the multiplier to 2 instead.
  3. Iterates over selectedUpgradesForRound and decrements quantityOwned by 1 for each matching item in shopInventory, but only if the current quantity is greater than 0 — preventing underflow on stale IDs.
  4. Clears selectedUpgradesForRound so the same upgrades cannot be spent twice.
  5. Calls generateNextOrder() to prepare the first recipe.
func startNewRound() {
    ordersCompletedInSession = 0
    currentBurgerStack.removeAll()

    comboMultiplier = 1
    if selectedUpgradesForRound.contains("knife") {
        comboMultiplier = 2
    }

    for upgradeId in selectedUpgradesForRound {
        if let index = shopInventory.firstIndex(where: { $0.id == upgradeId }) {
            if shopInventory[index].quantityOwned > 0 {
                shopInventory[index].quantityOwned -= 1
            }
        }
    }

    selectedUpgradesForRound.removeAll()
    generateNextOrder()
}

generateNextOrder()

Picks the next recipe the player must assemble and updates the relevant published properties. This method is called both by startNewRound() at the beginning of a round and by triggerSuccess() after each completed order. What it does:
  1. Clears currentBurgerStack to give the player a fresh slate.
  2. Reads currentLevel.possibleRecipes and selects one at random using randomElement().
  3. Assigns the chosen recipe array to targetRecipeEmojis.
  4. Derives currentRecipeName from the recipe contents:
    • Contains 🥓"Mega Bacon Burger"
    • Contains 🥬 (and no bacon) → "Fresh Veggie Burger"
    • Anything else → "Classic Cheeseburger"
func generateNextOrder() {
    currentBurgerStack.removeAll()
    let level = currentLevel

    if let randomRecipe = level.possibleRecipes.randomElement() {
        targetRecipeEmojis = randomRecipe

        if randomRecipe.contains("🥓") { currentRecipeName = "Mega Bacon Burger" }
        else if randomRecipe.contains("🥬") { currentRecipeName = "Fresh Veggie Burger" }
        else { currentRecipeName = "Classic Cheeseburger" }
    }
}

addIngredient(_ emoji: String)

The main real-time validation entry point. Every ingredient button in RecipesView calls this method when tapped.
emoji
String
required
The emoji string representing the tapped ingredient (e.g. "🥩", "🧀"). This must match one of the emojis in currentLevel.availableIngredients for the tap to make logical sense, though the method itself does not enforce that — it validates against targetRecipeEmojis directly.
Validation logic:
  1. Appends emoji to currentBurgerStack.
  2. If the stack length now exceeds targetRecipeEmojis.count, calls triggerError() and returns immediately — the player added too many ingredients.
  3. Loops over every index in the current stack and checks currentBurgerStack[i] != targetRecipeEmojis[i]. The first mismatch calls triggerError() and returns.
  4. If the stack exactly equals targetRecipeEmojis (same length, all elements match), calls triggerSuccess().
func addIngredient(_ emoji: String) {
    currentBurgerStack.append(emoji)

    if currentBurgerStack.count > targetRecipeEmojis.count {
        triggerError()
        return
    }

    for i in 0..<currentBurgerStack.count {
        if currentBurgerStack[i] != targetRecipeEmojis[i] {
            triggerError()
            return
        }
    }

    if currentBurgerStack == targetRecipeEmojis {
        triggerSuccess()
    }
}
Because validation is positional and runs after every tap, the player receives immediate feedback on the very first wrong ingredient rather than only after submitting a complete stack.

currentLevel (computed property)

Returns the Level object that corresponds to selectedLevelIndex.
var currentLevel: Level {
    guard selectedLevelIndex >= 0 && selectedLevelIndex < levels.count else {
        return levels[0]
    }
    return levels[selectedLevelIndex]
}
If selectedLevelIndex is somehow out of range (e.g. during initialisation before setupLevels() has finished), the guard clause safely falls back to levels[0] — Level 1, Burger Station — rather than crashing with an index out-of-bounds error.

finishRound(starsEarned: Int)

Records the result of a completed round: persists the earned stars to the current level and unlocks the next level if the player earned at least one star.
starsEarned
Int
required
The number of stars awarded for this round (0–3). A value of 0 means the round ended without earning any stars; the level’s existing starsEarned is not touched and no unlock happens.
What it does:
  1. If starsEarned > 0 and selectedLevelIndex is within bounds:
    • Updates levels[selectedLevelIndex].starsEarned using max(existing, starsEarned) so a replay can never reduce a previously earned star count.
    • Computes nextLevelId = currentLevelId + 1.
    • Searches the levels array for the entry whose id equals nextLevelId using firstIndex(where:) and sets its isUnlocked = true.
finishRound(starsEarned:) runs its entire body inside DispatchQueue.main.async. This means all state mutations — including starsEarned and isUnlocked updates — are dispatched asynchronously. Do not read back levels synchronously immediately after calling this method; the changes will not yet be committed on the current run-loop tick. Observe the levels array reactively through SwiftUI’s @Published binding instead.
The unlock logic deliberately uses id-based lookup (firstIndex(where: { $0.id == nextLevelId })) rather than selectedLevelIndex + 1 arithmetic. This makes the unlock safe against any future reordering of the levels array — as long as each Level retains its correct id, the right entry will be found.

Private Helpers

These methods are marked private and cannot be called directly from views, but they form the core of the real-time feedback loop and are documented here so the full game loop is transparent.

triggerSuccess()

Called by addIngredient(_:) when currentBurgerStack perfectly matches targetRecipeEmojis.
  • Increments ordersCompletedInSession by 1.
  • Sets showPerfectMessage = true to trigger the success overlay in RecipesView.
  • Calculates the coin reward: checks whether shopInventory contains a "sauce" item with quantityOwned > 0; if so, adds a 10-coin bonus to the base 25. The total is multiplied by comboMultiplier. Adds the result to coins.
  • Increments comboMultiplier by 1, capped at a maximum of 5.
  • Calls onRecipeSuccessBonusTime?() unconditionally. If RecipesView has assigned this closure (which it does when the Horno Industrial upgrade is active), the closure fires and adds bonus time; if it is nil, the call is a no-op.
  • After a 0.8-second DispatchQueue.main.asyncAfter delay, resets showPerfectMessage to false and calls generateNextOrder().

triggerError()

Called by addIngredient(_:) when a tap results in an oversized stack or a positional mismatch.
  • Sets showErrorMessage = true to trigger the error overlay in RecipesView.
  • Resets comboMultiplier to 1, ending any active combo streak.
  • After a 0.8-second delay, resets showErrorMessage to false and clears currentBurgerStack — the same order remains active so the player tries again from scratch.

setupLevels()

Called once inside init() to populate the levels array. It is never called again at runtime.
  • Builds 3 hand-crafted levels with fixed ingredient sets and curated recipe lists (Burger Station, Green Diner, Bacon & Grill).
  • Generates levels 4–20 procedurally: ingredient count scales with level number (capped at the full pool of 8 ingredients), targetOrders grows with 4 + (i / 2), and baseTime decreases using max(30, 55 - (i * i / 15)) to ensure even the hardest levels always have at least 30 seconds.
  • Assigns the completed array to self.levels.

Build docs developers (and LLMs) love