Skip to main content

Level Progression Overview

Una Aventura Inesperada features 5 progressively challenging levels (nivel1 through nivel5). Each level introduces new puzzle complexity while testing strategic planning with limited movements.
Levels are stored as tile arrays in teselas.h and loaded via the GenerarNivel() function (source/main.c:792-848).

Movement Limits Per Level

Each level has a strict stamina budget defined by macros:
// From source/main.c:49-53
#define MOVIMIENTOS_NIV1 18
#define MOVIMIENTOS_NIV2 19
#define MOVIMIENTOS_NIV3 23
#define MOVIMIENTOS_NIV4 21
#define MOVIMIENTOS_NIV5 24

Level 1

18 MovementsTutorial-style introduction with basic mechanics

Level 2

19 MovementsSlight increase for added complexity

Level 3

23 MovementsLargest movement budget for complex puzzles

Level 4

21 MovementsReduced stamina requiring efficiency

Level 5

24 MovementsFinal challenge with maximum complexity

Stamina Bar Visualization

The movement limit is visualized as a green stamina bar on the bottom screen:
// From source/main.c:808-814
if(numeroMovimientosMax > 0){
    for(int lin =COMIENZO_LINEA_BARRA_ESTAMINA; lin<ALTO_BARRA_ESTAMINA; lin++){
        for(int col=COMIENZO_COLUMNA_BARRA_ESTAMINA; col<ANCHO_BARRA_ESTAMINA;col++){
            fb[lin*256+col] = RGB15(0,30,0);  // Bright green
        }
    }
}
Bar Properties:
  • Width: 32 pixels
  • Height: 155 pixels
  • Position: (20, 38) on bottom screen
  • Depletion: Top-to-bottom as movements are used

Level-by-Level Breakdown

Level 1: Introduction

Movement Budget: 18 stepsQuiz Questions: 3 questions
  • Pregunta1-1 - Correct answer: Option 1 (index 1)
  • Pregunta1-2 - Correct answer: Option 0 (index 0)
  • Pregunta1-3 - Correct answer: Option 0 (index 0)
Implementation:
// From source/main.c:662-679
case 0:  // Level 1
    if(esJuegoReiniciado == false && 
       CrearDialogo(Pregunta1_1Bitmap,1) && 
       CrearDialogo(Pregunta1_2Bitmap,0) && 
       CrearDialogo(Pregunta1_3Bitmap,0)){
        nivelActual++;
        CrearDialogo(PreguntasAcertadasBitmap,2);
        mapaAcutal = nivel2;
        GenerarNivel(nivel2,HUDBitmap,MOVIMIENTOS_NIV2);
    }else{
        if(esJuegoReiniciado == false){
            CrearDialogo(PreguntaFallidaBitmap,2);
        }
        esJuegoReiniciado = false;
        GenerarNivel(nivel1,HUDBitmap,MOVIMIENTOS_NIV1);
    }
    break;
Progression:
  • All 3 questions must be answered correctly
  • Success → Success screen → Level 2
  • Failure → Failure screen → Restart Level 1
Starting Cinematic: Before Level 1 begins, players see a 3-frame opening cinematic (CinematicaInicioF1/F2/F3) from source/main.c:908-910.

Quiz Question System

Questions are presented as bitmap images displayed on the bottom screen with two clickable answer regions.

Question Assets

All quiz questions are imported as bitmap headers:
// From source/main.c:27-39
#include "Pregunta1-1.h"
#include "Pregunta1-2.h"
#include "Pregunta1-3.h"
#include "Pregunta2-1.h"
#include "Pregunta2-2.h"
#include "Pregunta3-1.h"
#include "Pregunta3-2.h"
#include "Pregunta4.h"
#include "Pregunta5-1.h"
#include "Pregunta5-2.h"
#include "PreguntaFallida.h"
#include "PreguntasAcertadas.h"
#include "PreguntasAcertadasFinal.h"

Answer Validation

The CrearDialogo() function handles all quiz logic:
// From source/main.c:939-975
bool CrearDialogo(unsigned int imagen[], int opcionCorrecta){
    // Display question image
    dmaCopy(imagen, VRAM_A, 256*192*2);
    
    // Wait for touch input on one of two buttons
    // Button 1: (50,138) to (213,161)
    // Button 2: (50,165) to (213,189)
    
    // Return true if chosen option matches opcionCorrecta
    return (opcionElegida == opcionCorrecta);
}
  • Option 0 = First answer (top button)
  • Option 1 = Second answer (bottom button)
  • Option 2 = Any answer acceptable (used for info screens)
Example from Level 1:
CrearDialogo(Pregunta1_1Bitmap, 1)  // Second answer is correct
CrearDialogo(Pregunta1_2Bitmap, 0)  // First answer is correct

Quiz Flow Diagram

Cinematic System

Opening Cinematic

Triggered when starting a new game:
// From source/main.c:907-914
if(/* Start button pressed */){
    CrearDialogo(CinematicaInicioF1Bitmap,2);
    CrearDialogo(CinematicaInicioF2Bitmap,2);
    CrearDialogo(CinematicaInicioF3Bitmap,2);
    mapaAcutal = nivel1;
    GenerarNivel(nivel1,HUDBitmap,MOVIMIENTOS_NIV1);
    esJuegoComenzado = true;
}
Assets:
  • CinematicaInicioF1.h - Frame 1
  • CinematicaInicioF2.h - Frame 2
  • CinematicaInicioF3.h - Frame 3

Ending Cinematic

Triggered after completing Level 5:
// From source/main.c:750-754
CrearDialogo(PreguntasAcertadasFinalBitmap,2);
CrearDialogo(CinematicaFinalF1Bitmap,2);
CrearDialogo(CinematicaFinalF2Bitmap,2);
CrearDialogo(CinematicaFinalF3Bitmap,2);
CrearMenu(menuTitulo,menuPrincipalBitmap,menuCreditosBitmap);
Assets:
  • CinematicaFinalF1.h - Frame 1
  • CinematicaFinalF2.h - Frame 2
  • CinematicaFinalF3.h - Frame 3
Cinematic Navigation: All cinematic frames use opcionCorrecta = 2, meaning players tap anywhere on the touch screen to advance to the next frame.

Level Restart System

Players can restart the current level at any time during gameplay.

Restart Button

The restart button appears on the HUD during active gameplay:
// From source/main.c:143-146
struct PuntoPantalla puntosReinicioNivelBoton [2]={
    {117,100},  // Top-left corner
    {236,125}   // Bottom-right corner
};
Button Properties:
  • Position: (117, 100) on bottom screen
  • Size: 119×25 pixels
  • Activation: Only when esActivoBotonReinicio == true (source/main.c:220)

Restart Implementation

The main loop monitors for restart touches:
// From source/main.c:220-233
if(esActivoBotonReinicio == true){
    scanKeys();
    keys = keysCurrent();
    if(keys & KEY_TOUCH && esActivoBotonesDialogos == true){
        touchRead(&posicionXY);
        if((posicionXY.px >= puntosReinicioNivelBoton[0].x && 
            posicionXY.px <= puntosReinicioNivelBoton[1].x) && 
           (posicionXY.py >= puntosReinicioNivelBoton[0].y && 
            posicionXY.py <= puntosReinicioNivelBoton[1].y)){
            esJuegoReiniciado = true;
            ConsultarSistemaDialogo();  // Triggers level reload
        }
    }
}

Restart Flow

When restart is triggered:
  1. esJuegoReiniciado flag set to true
  2. ConsultarSistemaDialogo() called
  3. Current level’s case statement executed
  4. Quiz questions skipped (due to esJuegoReiniciado check)
  5. Level regenerated with GenerarNivel()
  6. Flag reset to false
// From source/main.c:677-678 (example from Level 1)
if(esJuegoReiniciado == false){
    CrearDialogo(PreguntaFallidaBitmap,2);  // Skipped on manual restart
}
esJuegoReiniciado = false;
GenerarNivel(nivel1,HUDBitmap,MOVIMIENTOS_NIV1);
Restart vs. Failure: Manual restarts skip the failure dialog, while quiz failures show the PreguntaFallidaBitmap before restarting.

Level Generation

The GenerarNivel() function (source/main.c:792-848) handles all level initialization:
void GenerarNivel(u16 mapa[], unsigned int imagen[], int numeroMovimientosMax){
    // Resume animations
    timerUnpause(1);
    
    // Enable gameplay
    REG_KEYCNT = 0x7FFF;
    esActivoBotonReinicio = true;
    esPartidaAcabada = false;
    jugadorVivo = true;
    
    // Display HUD on bottom screen
    dmaCopy(imagen, VRAM_A, 256*192*2);
    REG_DISPCNT = MODE_FB0;
    
    // Set movement budget
    movimientosJugador = numeroMovimientosMax;
    maximoMovimientosJugador = numeroMovimientosMax;
    
    // Draw stamina bar (green)
    if(numeroMovimientosMax > 0){
        for(int lin=COMIENZO_LINEA_BARRA_ESTAMINA; lin<ALTO_BARRA_ESTAMINA; lin++){
            for(int col=COMIENZO_COLUMNA_BARRA_ESTAMINA; col<ANCHO_BARRA_ESTAMINA; col++){
                fb[lin*256+col] = RGB15(0,30,0);
            }
        }
    }
    
    // Generate tile map
    int fila, columna, contEnemigos=0;
    numeroDeEnemigos = 0;
    pos_mapData = 0;
    
    for(fila=0; fila<24; fila++)
        for(columna=0; columna<32; columna++){
            pos_mapMemory = fila*32+columna;
            mapMemory[pos_mapMemory] = mapa[pos_mapData];
            
            // Track player start position
            if(mapMemory[pos_mapMemory] == 0){
                posJugColumna = columna;
                posJugFila = fila;
            }
            // Track NPC position
            else if(mapMemory[pos_mapMemory] == 38){
                posNpcColumna = columna;
                posNpcFila = fila;
            }
            // Track enemy positions
            else if(mapMemory[pos_mapMemory] == 5 || mapMemory[pos_mapMemory] == 42){
                posicionesEnemigo[contEnemigos].x = columna;
                posicionesEnemigo[contEnemigos].y = fila;
                posicionesEnemigo[contEnemigos].vivo = true;
                contEnemigos++;
                numeroDeEnemigos++;
            }
            pos_mapData++;
        }
}

Level Data Structure

Levels are stored as 1D arrays of 768 elements (32×24 tiles):
  • Tile 0: Player start position
  • Tile 38: NPC position
  • Tile 5/42: Enemy positions
  • Other tiles: Walls, floors, grass, boxes, etc.
Maximum Enemies: The game supports up to 10 enemies per level (source/main.c:148). Exceeding this limit will cause array overflow.

Level Progression Table

LevelMovementsQuestionsQuestion IDsCorrect AnswersNext Level
1183Pregunta1-1
Pregunta1-2
Pregunta1-3
1
0
0
Level 2
2192Pregunta2-1
Pregunta2-2
0
0
Level 3
3232Pregunta3-1
Pregunta3-2
0
0
Level 4
4211Pregunta40Level 5
5242Pregunta5-1
Pregunta5-2
0
1
Ending
Total Quiz Questions: 10 questions across all 5 levels must be answered correctly to complete the game.

Build docs developers (and LLMs) love