#include "title.h" // Constructor Title::Title(SDL_Renderer *renderer, Screen *screen, Input *input, Asset *asset, options_t *options, Lang *lang, section_t section) { // Copia las direcciones de los punteros this->renderer = renderer; this->screen = screen; this->input = input; this->asset = asset; this->options = options; this->lang = lang; // Reserva memoria para los punteros propios eventHandler = new SDL_Event(); fade = new Fade(renderer); dustTexture = new LTexture(renderer, asset->get("title_dust.png")); coffeeTexture = new LTexture(renderer, asset->get("title_coffee.png")); crisisTexture = new LTexture(renderer, asset->get("title_crisis.png")); gradientTexture = new LTexture(renderer, asset->get("title_gradient.png")); coffeeBitmap = new SmartSprite(coffeeTexture, renderer); crisisBitmap = new SmartSprite(crisisTexture, renderer); dustBitmapL = new AnimatedSprite(dustTexture, renderer, asset->get("title_dust.ani")); dustBitmapR = new AnimatedSprite(dustTexture, renderer, asset->get("title_dust.ani")); gradient = new Sprite({0, 0, 256, 192}, gradientTexture, renderer); text1 = new Text(asset->get("smb2.png"), asset->get("smb2.txt"), renderer); text2 = new Text(asset->get("8bithud.png"), asset->get("8bithud.txt"), renderer); menu.title = new Menu(renderer, asset, input, asset->get("title.men")); menu.options = new Menu(renderer, asset, input, asset->get("options.men")); // Sonidos crashSound = JA_LoadSound(asset->get("title.wav").c_str()); // Musicas titleMusic = JA_LoadMusic(asset->get("title.ogg").c_str()); // Inicializa variables this->section = section; counter = TITLE_COUNTER; backgroundCounter = 0; backgroundMode = rand() % 2; menuVisible = false; menu.active = menu.title; nextSection.name = PROG_SECTION_GAME; postFade = 0; ticks = 0; ticksSpeed = 15; fade->init(0x17, 0x17, 0x26); demo = false; // Pone valores por defecto a las opciones de control options->input.clear(); input_t i; i.id = 0; i.name = "KEYBOARD"; i.deviceType = INPUT_USE_KEYBOARD; options->input.push_back(i); i.id = 0; i.name = "GAME CONTROLLER"; i.deviceType = INPUT_USE_GAMECONTROLLER; options->input.push_back(i); // Comprueba si hay mandos conectados checkInputDevices(); // Pone valores por defecto. El primer jugador el teclado. El segundo jugador el primer mando deviceIndex.clear(); deviceIndex.push_back(availableInputDevices.size() - 1); // El último dispositivo encontrado es el teclado deviceIndex.push_back(0); // El primer mando encontrado. Si no ha encontrado ninguno es el teclado // Si ha encontrado un mando se lo asigna al segundo jugador if (input->gameControllerFound()) { options->input.at(1).id = availableInputDevices.at(deviceIndex.at(1)).id; options->input.at(1).name = availableInputDevices.at(deviceIndex.at(1)).name; options->input.at(1).deviceType = availableInputDevices.at(deviceIndex.at(1)).deviceType; } // Inicializa el bitmap de Coffee coffeeBitmap->setPosX(45); coffeeBitmap->setPosY(11 - 200); coffeeBitmap->setWidth(167); coffeeBitmap->setHeight(46); coffeeBitmap->setVelX(0.0f); coffeeBitmap->setVelY(2.5f); coffeeBitmap->setAccelX(0.0f); coffeeBitmap->setAccelY(0.1f); coffeeBitmap->setSpriteClip(0, 0, 167, 46); coffeeBitmap->setEnabled(true); coffeeBitmap->setEnabledCounter(0); coffeeBitmap->setDestX(45); coffeeBitmap->setDestY(11); // Inicializa el bitmap de Crisis crisisBitmap->setPosX(60); crisisBitmap->setPosY(57 + 200); crisisBitmap->setWidth(137); crisisBitmap->setHeight(46); crisisBitmap->setVelX(0.0f); crisisBitmap->setVelY(-2.5f); crisisBitmap->setAccelX(0.0f); crisisBitmap->setAccelY(-0.1f); crisisBitmap->setSpriteClip(0, 0, 137, 46); crisisBitmap->setEnabled(true); crisisBitmap->setEnabledCounter(0); crisisBitmap->setDestX(60); crisisBitmap->setDestY(57); // Inicializa el bitmap de DustRight dustBitmapR->setPosX(218); dustBitmapR->setPosY(47); dustBitmapR->setWidth(16); dustBitmapR->setHeight(16); dustBitmapR->setFlip(SDL_FLIP_HORIZONTAL); // Inicializa el bitmap de DustLeft dustBitmapL->setPosX(33); dustBitmapL->setPosY(47); dustBitmapL->setWidth(16); dustBitmapL->setHeight(16); // Inicializa el sprite con el degradado gradient->setSpriteClip(0, 96, 256, 192); // Crea el mosaico de fondo del titulo createTiledBackground(); // Coloca la ventana que recorre el mosaico de fondo de manera que coincida con el mosaico que hay pintado en el titulo al iniciar backgroundWindow.x = 128; backgroundWindow.y = 96; backgroundWindow.w = GAME_WIDTH; backgroundWindow.h = GAME_HEIGHT; // Inicializa los valores del vector con los valores del seno for (int i = 0; i < 360; ++i) { sin[i] = SDL_sinf((float)i * 3.14f / 180.0f); } // Actualiza los textos de los menus // updateMenuLabels(); } // Destructor Title::~Title() { delete eventHandler; delete fade; dustTexture->unload(); delete dustTexture; coffeeTexture->unload(); delete coffeeTexture; crisisTexture->unload(); delete crisisTexture; gradientTexture->unload(); delete gradientTexture; delete coffeeBitmap; delete crisisBitmap; delete dustBitmapL; delete dustBitmapR; delete gradient; delete text1; delete text2; delete menu.title; delete menu.options; JA_DeleteSound(crashSound); JA_DeleteMusic(titleMusic); SDL_DestroyTexture(background); } // Actualiza las variables del objeto void Title::update() { // Comprueba los eventos checkEventHandler(); // Calcula la lógica de los objetos if (SDL_GetTicks() - ticks > ticksSpeed) { // Actualiza el contador de ticks ticks = SDL_GetTicks(); switch (section.subsection) { // Sección 1 - Titulo desplazandose case TITLE_SECTION_1: { // Actualiza los objetos coffeeBitmap->update(); crisisBitmap->update(); // Si los objetos han llegado a su destino, cambiamos de Sección if (coffeeBitmap->hasFinished() && crisisBitmap->hasFinished()) { section.subsection = TITLE_SECTION_2; // Pantallazo blanco SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); SDL_RenderPresent(renderer); // Reproduce el efecto sonoro JA_PlaySound(crashSound); } } break; // Sección 2 - Titulo vibrando case TITLE_SECTION_2: { // Agita la pantalla static const int v[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0}; static const int a = coffeeBitmap->getPosX(); static const int b = crisisBitmap->getPosX(); static int step = 0; coffeeBitmap->setPosX(a + v[step / 3]); crisisBitmap->setPosX(b + v[step / 3]); dustBitmapR->update(); dustBitmapL->update(); step++; if (step == 33) { section.subsection = TITLE_SECTION_3; } } break; // Sección 3 - La pantalla de titulo con el menú y la música case TITLE_SECTION_3: { // Reproduce la música if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { JA_PlayMusic(titleMusic); } // Actualiza el contador de ticks ticks = SDL_GetTicks(); dustBitmapR->update(); dustBitmapL->update(); // Actualiza la lógica del titulo menu.active->update(); fade->update(); if (fade->hasEnded()) { switch (postFade) { case 0: // 1 PLAYER section.name = PROG_SECTION_GAME; section.subsection = GAME_SECTION_PLAY_1P; JA_StopMusic(); break; case 1: // 2 PLAYERS section.name = PROG_SECTION_GAME; section.subsection = GAME_SECTION_PLAY_2P; JA_StopMusic(); break; case 2: // QUIT section.name = PROG_SECTION_QUIT; JA_StopMusic(); break; case 3: // TIME OUT counter = TITLE_COUNTER; menu.active->reset(); if (demo) { runDemoGame(); runInstructions(INSTRUCTIONS_MODE_AUTO); } else section.name = PROG_SECTION_LOGO; break; default: break; } } // Actualiza el tileado de fondo updateBG(); // Comprueba las entradas para el menu if (menuVisible == true) { menu.active->checkInput(); } // Comprueba si se ha seleccionado algún item del menú if (menu.active->getName() == "TITLE") { switch (menu.active->getItemSelected()) { case 0: // 1 PLAYER postFade = 0; fade->activateFade(); break; case 1: // 2 PLAYERS postFade = 1; fade->activateFade(); break; case 2: // OPTIONS menu.active = menu.options; optionsPrevious = *options; break; case 3: // QUIT postFade = 2; fade->activateFade(); break; default: break; } } // Comprueba si se ha seleccionado algún item de opciones if (menu.active->getName() == "OPTIONS") { switch (menu.active->getItemSelected()) { case 0: // Difficulty if (options->difficulty == DIFFICULTY_EASY) options->difficulty = DIFFICULTY_NORMAL; else if (options->difficulty == DIFFICULTY_NORMAL) options->difficulty = DIFFICULTY_HARD; else options->difficulty = DIFFICULTY_EASY; updateMenuLabels(); break; case 1: // PLAYER 1 CONTROLS updatePlayerInputs(0); updateMenuLabels(); break; case 3: // PLAYER 2 CONTROLS updatePlayerInputs(1); updateMenuLabels(); break; case 5: // Language options->language++; if (options->language == 3) options->language = 0; updateMenuLabels(); break; case 6: // Display mode switchFullScreenModeVar(); if (options->fullScreenMode != 0) { menu.options->setSelectable(8, false); menu.options->setGreyed(8, true); } else { menu.options->setSelectable(8, true); menu.options->setGreyed(8, false); } updateMenuLabels(); break; case 8: // Windows size options->windowSize++; if (options->windowSize == 5) options->windowSize = 1; updateMenuLabels(); break; case 9: // FILTER if (options->filter == FILTER_LINEAL) options->filter = FILTER_NEAREST; else options->filter = FILTER_LINEAL; updateMenuLabels(); break; case 10: // VSYNC if (options->vSync) options->vSync = false; else options->vSync = true; updateMenuLabels(); break; case 11: // HOW TO PLAY runInstructions(INSTRUCTIONS_MODE_MANUAL); break; case 12: // ACCEPT applyOptions(); menu.active->reset(); menu.active = menu.title; break; case 13: // CANCEL options = &optionsPrevious; updateMenuLabels(); menu.active->reset(); menu.active = menu.title; break; default: break; } } if (menu.active->getName() == "TITLE") { counter--; } } break; default: break; } } } // Dibuja el objeto en pantalla void Title::render() { switch (section.subsection) { // Sección 1 - Titulo desplazandose case TITLE_SECTION_1: { // Prepara para empezar a dibujar en la textura de juego screen->start(); // Limpia la pantalla screen->clean(bgColor); // Dibuja el tileado de fondo SDL_RenderCopy(renderer, background, &backgroundWindow, nullptr); // Dibuja el degradado gradient->render(); // Dibuja los objetos coffeeBitmap->render(); crisisBitmap->render(); // Vuelca el contenido del renderizador en pantalla screen->blit(); } break; // Sección 2 - Titulo vibrando case TITLE_SECTION_2: { // Reproduce el efecto sonoro JA_PlaySound(crashSound); // Agita la pantalla const int v[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0}; const int a = coffeeBitmap->getPosX(); const int b = crisisBitmap->getPosX(); for (int n = 0; n < 11 * 3; ++n) { // Prepara para empezar a dibujar en la textura de juego screen->start(); // Limpia la pantalla screen->clean(bgColor); // Dibuja el tileado de fondo SDL_RenderCopy(renderer, background, &backgroundWindow, nullptr); // Dibuja el degradado gradient->render(); // Dibuja los objetos coffeeBitmap->setPosX(a + v[n / 3]); crisisBitmap->setPosX(b + v[n / 3]); coffeeBitmap->render(); crisisBitmap->render(); dustBitmapR->update(); dustBitmapL->update(); dustBitmapR->render(); dustBitmapL->render(); // Vuelca el contenido del renderizador en pantalla screen->blit(); } section.subsection = TITLE_SECTION_3; } break; // Sección 3 - La pantalla de titulo con el menú y la música case TITLE_SECTION_3: { // Prepara para empezar a dibujar en la textura de juego screen->start(); // Limpia la pantalla screen->clean(bgColor); // Dibuja el tileado de fondo SDL_RenderCopy(renderer, background, &backgroundWindow, nullptr); // Dibuja el degradado gradient->render(); // Dibuja los objetos if (menu.active->getName() != "OPTIONS") { // Bitmaps con el logo/titulo del juego coffeeBitmap->render(); crisisBitmap->render(); // Texto con el copyright y versión text2->writeDX(TXT_CENTER | TXT_SHADOW, SCREEN_CENTER_X, GAME_HEIGHT - (BLOCK * 2), TEXT_COPYRIGHT, 1, noColor, 1, shdwTxtColor); } if (menuVisible == true) { menu.active->render(); } dustBitmapR->render(); dustBitmapL->render(); // PRESS ANY KEY! if ((counter % 50 > 14) && (menuVisible == false)) { text1->writeDX(TXT_CENTER | TXT_SHADOW, SCREEN_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, lang->getText(23), 1, noColor, 1, shdwTxtColor); } // Fade fade->render(); // Vuelca el contenido del renderizador en pantalla screen->blit(); } break; default: break; } } // Comprueba los eventos void Title::checkEventHandler() { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(eventHandler) != 0) { // Evento de salida de la aplicación if (eventHandler->type == SDL_QUIT) { section.name = PROG_SECTION_QUIT; break; } if (section.subsection == TITLE_SECTION_3) { // Si se pulsa alguna tecla durante la tercera sección del titulo if ((eventHandler->type == SDL_KEYUP) || (eventHandler->type == SDL_JOYBUTTONUP)) { // Muestra el menu menuVisible = true; // Reinicia el contador counter = TITLE_COUNTER; } } } } // Actualiza el tileado de fondo void Title::updateBG() { if (backgroundMode == 0) { // El tileado de fondo se desplaza en diagonal ++backgroundWindow.x %= 64; ++backgroundWindow.y %= 64; } else { // El tileado de fondo se desplaza en circulo ++backgroundCounter %= 360; backgroundWindow.x = 128 + (int(sin[(backgroundCounter + 270) % 360] * 128)); backgroundWindow.y = 96 + (int(sin[(360 - backgroundCounter) % 360] * 96)); } } // Cambia el valor de la variable de modo de pantalla completa void Title::switchFullScreenModeVar() { switch (options->fullScreenMode) { case 0: options->fullScreenMode = SDL_WINDOW_FULLSCREEN; break; case SDL_WINDOW_FULLSCREEN: options->fullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP; break; case SDL_WINDOW_FULLSCREEN_DESKTOP: options->fullScreenMode = 0; break; default: options->fullScreenMode = 0; break; } } // Actualiza los elementos de los menus void Title::updateMenuLabels() { int i = 0; // DIFFICULTY switch (options->difficulty) { case DIFFICULTY_EASY: menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(66)); // EASY break; case DIFFICULTY_NORMAL: menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(67)); // NORMAL break; case DIFFICULTY_HARD: menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(68)); // HARD break; default: menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(67)); // NORMAL break; } i++; // PLAYER 1 CONTROLS menu.options->setItemCaption(i, lang->getText(62)); i++; // PLAYER 1 CONTROLS - OPTIONS switch (options->input[0].deviceType) { case INPUT_USE_KEYBOARD: menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD menu.options->setGreyed(i, false); break; case INPUT_USE_GAMECONTROLLER: menu.options->setItemCaption(i, lang->getText(70)); // GAME CONTROLLER if (!input->gameControllerFound()) menu.options->setGreyed(i, true); else { menu.options->setGreyed(i, false); menu.options->setItemCaption(i, options->input[0].name); } break; default: menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD break; } i++; // PLAYER 2 CONTROLS menu.options->setItemCaption(i, lang->getText(63)); i++; // PLAYER 2 CONTROLS - OPTIONS switch (options->input[1].deviceType) { case INPUT_USE_KEYBOARD: menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD menu.options->setGreyed(i, false); break; case INPUT_USE_GAMECONTROLLER: menu.options->setItemCaption(i, lang->getText(70)); // GAME CONTROLLER if (!input->gameControllerFound()) menu.options->setGreyed(i, true); else { menu.options->setGreyed(i, false); menu.options->setItemCaption(i, options->input[1].name); } break; default: menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD break; } i++; // LANGUAGE switch (options->language) { case es_ES: menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(24)); // SPANISH break; case ba_BA: menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(25)); // VALENCIAN break; case en_UK: menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(26)); // ENGLISH break; default: menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(26)); // ENGLISH break; } i++; // DISPLAY MODE menu.options->setItemCaption(i, lang->getText(58)); i++; // DISPLAY MODE - OPTIONS switch (options->fullScreenMode) { case 0: menu.options->setItemCaption(i, lang->getText(4)); // WINDOW break; case SDL_WINDOW_FULLSCREEN: menu.options->setItemCaption(i, lang->getText(5)); // FULLSCREEN break; case SDL_WINDOW_FULLSCREEN_DESKTOP: menu.options->setItemCaption(i, lang->getText(6)); // FAKE FULLSCREEN break; default: menu.options->setItemCaption(i, lang->getText(4)); // WINDOW break; } i++; // WINDOW SIZE menu.options->setItemCaption(i, lang->getText(7) + " x" + std::to_string(options->windowSize)); // WINDOW SIZE i++; // FILTER if (options->filter == FILTER_LINEAL) menu.options->setItemCaption(i, lang->getText(60) + ": " + lang->getText(71)); // BILINEAL else menu.options->setItemCaption(i, lang->getText(60) + ": " + lang->getText(72)); // LINEAL i++; // VSYNC if (options->vSync) menu.options->setItemCaption(i, lang->getText(61) + ": " + lang->getText(73)); // ON else menu.options->setItemCaption(i, lang->getText(61) + ": " + lang->getText(74)); // OFF i++; // HOW TO PLAY menu.options->setItemCaption(i, lang->getText(2)); i++; // ACCEPT menu.options->setItemCaption(i, lang->getText(9)); // ACCEPT i++; // CANCEL menu.options->setItemCaption(i, lang->getText(10)); // CANCEL // Recoloca el menu de opciones menu.options->centerMenuOnX(SCREEN_CENTER_X); menu.options->centerMenuOnY(SCREEN_CENTER_Y); menu.options->centerMenuElementsOnX(); // Establece las etiquetas del menu de titulo menu.title->setItemCaption(0, lang->getText(51)); // 1 PLAYER menu.title->setItemCaption(1, lang->getText(52)); // 2 PLAYERS menu.title->setItemCaption(2, lang->getText(1)); // OPTIONS menu.title->setItemCaption(3, lang->getText(3)); // QUIT // Recoloca el menu de titulo menu.title->centerMenuOnX(SCREEN_CENTER_X); menu.title->centerMenuElementsOnX(); } // Aplica las opciones de menu seleccionadas void Title::applyOptions() { screen->setVideoMode(options->fullScreenMode); lang->setLang(options->language); updateMenuLabels(); createTiledBackground(); } // Bucle para el titulo del juego section_t Title::run() { while (section.name == PROG_SECTION_TITLE) { update(); render(); // Sección 3 - La pantalla de titulo con el menú y la música // Sección Instrucciones // if (section.subsection == TITLE_SECTION_INSTRUCTIONS) //{ // runInstructions(INSTRUCTIONS_MODE_AUTO); //} } return section; } // Ejecuta la parte donde se muestran las instrucciones void Title::runInstructions(Uint8 mode) { instructions = new Instructions(renderer, screen, asset, lang); instructions->run(mode); delete instructions; } // Ejecuta el juego en modo demo void Title::runDemoGame() { demoGame = new Game(1, 0, renderer, screen, asset, lang, input, true, options); demoGame->run(); delete demoGame; } // Modifica las opciones para los controles de los jugadores bool Title::updatePlayerInputs(int numPlayer) { const int numDevices = availableInputDevices.size(); if (!input->gameControllerFound()) { // Si no hay mandos se deja todo de manera prefijada deviceIndex.at(0) = 0; deviceIndex.at(1) = 0; options->input.at(0).id = -1; options->input.at(0).name = "KEYBOARD"; options->input.at(0).deviceType = INPUT_USE_KEYBOARD; options->input.at(1).id = 0; options->input.at(1).name = "GAME CONTROLLER"; options->input.at(1).deviceType = INPUT_USE_GAMECONTROLLER; return true; } else { // Si hay mas de un dispositivo, se recorre el vector printf("numplayer:%i\n", numPlayer); printf("deviceindex:%i\n", deviceIndex.at(numPlayer)); // Incrementa el indice if (deviceIndex.at(numPlayer) < numDevices - 1) { deviceIndex.at(numPlayer)++; } else { deviceIndex.at(numPlayer) = 0; } printf("deviceindex:%i\n", deviceIndex.at(numPlayer)); // Si coincide con el del otro jugador, se lo intercambian if (deviceIndex.at(0) == deviceIndex.at(1)) { const int theOtherPlayer = (numPlayer + 1) % 2; deviceIndex.at(theOtherPlayer)--; if (deviceIndex.at(theOtherPlayer) < 0) { deviceIndex.at(theOtherPlayer) = numDevices - 1; } } // Copia el dispositivo marcado por el indice a la variable de opciones de cada jugador options->input[0] = availableInputDevices.at(deviceIndex.at(0)); options->input[1] = availableInputDevices.at(deviceIndex.at(1)); return true; } } // Crea el mosaico de fondo del titulo void Title::createTiledBackground() { // Crea la textura para el mosaico de fondo background = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAME_WIDTH * 2, GAME_HEIGHT * 2); if (background == nullptr) { printf("TitleSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); } // Crea los objetos para pintar en la textura de fondo LTexture *bgTileTexture = new LTexture(renderer, asset->get("title_bg_tile.png")); Sprite *tile = new Sprite({0, 0, 64, 64}, bgTileTexture, renderer); // Prepara para dibujar sobre la textura SDL_SetRenderTarget(renderer, background); SDL_SetRenderDrawColor(renderer, 0x43, 0x43, 0x4F, 0xFF); SDL_RenderClear(renderer); // Rellena la textura con el tile tile->setSpriteClip(0, 0, 64, 64); for (int i = 0; i < 8; ++i) { for (int j = 0; j < 6; ++j) { tile->setPosX(i * 64); tile->setPosY(j * 64); tile->render(); } } // Vuelve a colocar el renderizador apuntando a la pantalla SDL_SetRenderTarget(renderer, nullptr); // Libera la memoria utilizada por los objetos bgTileTexture->unload(); delete bgTileTexture; delete tile; } // Comprueba cuantos mandos hay conectados para gestionar el menu de opciones void Title::checkInputDevices() { printf("Filling devices for options menu...\n"); const int numControllers = input->getNumControllers(); availableInputDevices.clear(); input_t temp; // Añade todos los mandos if (numControllers > 0) for (int i = 0; i < numControllers; ++i) { temp.id = i; temp.name = input->getControllerName(i); temp.deviceType = INPUT_USE_GAMECONTROLLER; availableInputDevices.push_back(temp); printf("Device %i:\t%s\n", (int)availableInputDevices.size(), temp.name.c_str()); } // Añade el teclado al final temp.id = -1; temp.name = "KEYBOARD"; temp.deviceType = INPUT_USE_KEYBOARD; availableInputDevices.push_back(temp); printf("Device %i:\t%s\n\n", (int)availableInputDevices.size(), temp.name.c_str()); }