/* * game_engine.c * * Created on: 29 nov. 2019 * Author: JackCarterSmith */ #include "game_engine.h" #include "string.h" #include "stm32412g_discovery.h" #include "stm32412g_discovery_lcd.h" RNG_HandleTypeDef RngHandle; PP_BallTypeDef ballDatas; PP_BallTypeDef displayedBall; MenuTypdef currMenu = MENU_MAIN; Game_ModeTypdef currGameMode = SOLO_MODE; uint16_t currScore; uint32_t oldTicks = 0; /** * @brief Main control byte for game engine */ struct gameControlByte { unsigned start_disp:1; unsigned scoreUpdFlag:1; unsigned playerFail:1; unsigned reserved3:1; unsigned reserved4:1; unsigned reserved5:1; unsigned reserved6:1; unsigned reserved7:1; } gCtrlByte; /** * @brief Generate new ball's pos and acc from random seed */ void generateNewBall(Game_ModeTypdef type, uint32_t seed) { switch (type) { case MULTI_MODE: break; case SOLO_MODE: default: ballDatas.posX = (seed & 0xFF) % 240; ballDatas.posY = ((seed & 0xFF00 >> 7) % 180) + 60; if (ballDatas.posX < 0) ballDatas.posX = -ballDatas.posX; if (ballDatas.posY < 0) ballDatas.posY = -ballDatas.posY; ballDatas.accX = ((seed & 0xFF0000 >> 15) % 8) + 4; ballDatas.accY = ((seed & 0xFF000000 >> 31) % 8) + 4; if (seed & 0x1) ballDatas.accX = -ballDatas.accX; if (seed & 0x2 >> 1) ballDatas.accY = -ballDatas.accY; break; } } /** * @brief Collision and animation engine for the ball/bar */ void ballRayTracingEngine(PP_BarTypeDef *barDatas) { if (ballDatas.posX + ballDatas.accX > 236) { ballDatas.posX = 236; ballDatas.accX = -ballDatas.accX; } else if (ballDatas.posX + ballDatas.accX < 0) { ballDatas.posX = 0; ballDatas.accX = -ballDatas.accX; } else { ballDatas.posX += ballDatas.accX; } if ((ballDatas.posY + ballDatas.accY > BSP_LCD_GetYSize() - 16) && ballDatas.posX > barDatas->pos - 19 && ballDatas.posX < barDatas->pos + 19) { currScore++; gCtrlByte.scoreUpdFlag = 1; ballDatas.accY = -ballDatas.accY; } else { if (ballDatas.posY + ballDatas.accY > 236) { ballDatas.posY = 236; ballDatas.accY = -ballDatas.accY; } else if (ballDatas.posY + ballDatas.accY < 26) { ballDatas.posY = 26; ballDatas.accY = -ballDatas.accY; } else { ballDatas.posY += ballDatas.accY; } } } /** * @brief Refresh ball displayed on screen */ void updateBallDisplay() { BSP_LCD_SetTextColor(LCD_COLOR_BLACK); BSP_LCD_FillRect(displayedBall.posX, displayedBall.posY, 4, 4); //Clean previous ball BSP_LCD_SetTextColor(LCD_COLOR_WHITE); BSP_LCD_FillRect(ballDatas.posX, ballDatas.posY, 4, 4); //Draw the ball at new pos displayedBall.posX = ballDatas.posX; displayedBall.posY = ballDatas.posY; if (ballDatas.posY >= 236) displayEndMenu(); } /** * @brief Refresh bar displayed on screen */ void updatePongBar(PP_BarTypeDef *barDatas) { BSP_LCD_SetTextColor(LCD_COLOR_BLACK); BSP_LCD_FillRect(0, BSP_LCD_GetYSize() - 12, BSP_LCD_GetXSize(), 7); //Clean previous bar BSP_LCD_SetTextColor(LCD_COLOR_WHITE); if (barDatas->pos + barDatas->acc > BSP_LCD_GetXSize() - 20) { barDatas->pos = BSP_LCD_GetXSize() - 20; } else if (barDatas->pos + barDatas->acc < 20) { barDatas->pos = 20; } else { barDatas->pos += barDatas->acc; } BSP_LCD_FillRect(barDatas->pos - 20, BSP_LCD_GetYSize() - 12, 40, 7); //Draw the bar at new pos } /** * @brief Refresh displayed score on game screen */ void updateScoreArea(Game_ModeTypdef type, uint16_t newScore) { char sTextBuffer[32]; BSP_LCD_SetFont(&Font24); BSP_LCD_SetTextColor(LCD_COLOR_BLACK); BSP_LCD_SetTextColor(LCD_COLOR_WHITE); sprintf(sTextBuffer, "%d", newScore); BSP_LCD_DisplayStringAt(6, 2, (uint8_t *)sTextBuffer, RIGHT_MODE); BSP_LCD_FillRect(10, 24, BSP_LCD_GetXSize() - 20, 1); } /** * @brief Get a new random number */ uint32_t genSeed() { uint32_t tmp = 0; if (HAL_RNG_Init(&RngHandle) == HAL_OK) { tmp = HAL_RNG_GetRandomNumber(&RngHandle); } else BSP_LED_On(LED_RED); if (HAL_RNG_DeInit(&RngHandle) != HAL_OK) BSP_LED_On(LED_RED); return tmp; } /** * @brief Main function called when need to update screen */ void refreshCurrScreen(uint32_t _t, PP_BarTypeDef *_barDatas) { char sTextBuffer[32]; if (currMenu == MENU_GAME) { if (currGameMode == SOLO_MODE) { if (_t - oldTicks >= 10 || _t - oldTicks < 0) { ballRayTracingEngine(_barDatas); if (gCtrlByte.scoreUpdFlag) { gCtrlByte.scoreUpdFlag = 0; updateScoreArea(SOLO_MODE, currScore); } updateBallDisplay(); updatePongBar(_barDatas); oldTicks = _t; } } else if (currGameMode == MULTI_MODE) { //Nothing } } else if (currMenu == MENU_MAIN || currMenu == MENU_END) { if (_t - oldTicks >= 30 || _t - oldTicks < 0) { if (gCtrlByte.start_disp) { strcpy(sTextBuffer, " \0"); gCtrlByte.start_disp = 0; } else if (!gCtrlByte.start_disp) { strcpy(sTextBuffer, "Press START\0"); gCtrlByte.start_disp = 1; } BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - 5 * 12, BSP_LCD_GetYSize() / 2 + 40, (uint8_t *)sTextBuffer, LEFT_MODE); oldTicks = _t; } } } /** * @brief Just a cool screen to display when the board start */ void displayLoadingScreen() { char sTextBuffer[32]; BSP_LCD_SetBackColor(LCD_COLOR_BLACK); BSP_LCD_SetTextColor(LCD_COLOR_WHITE); BSP_LCD_SetFont(&Font16); strcpy(sTextBuffer, "2P - ProjectPong\0"); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 - 48, (uint8_t *)sTextBuffer, LEFT_MODE); /* Print loading screen */ strcpy(sTextBuffer, "Loading...\0"); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 + 40, (uint8_t *)sTextBuffer, LEFT_MODE); HAL_Delay(2000); } void displayStartMenu(PP_HighScoresTypeDef hsDatas) { char sTextBuffer[32]; currMenu = MENU_MAIN; BSP_LCD_Clear(LCD_COLOR_BLACK); BSP_LCD_SetBackColor(LCD_COLOR_BLACK); BSP_LCD_SetTextColor(LCD_COLOR_WHITE); BSP_LCD_SetFont(&Font16); strcpy(sTextBuffer, "High Scores:\0"); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 - 70, (uint8_t *)sTextBuffer, LEFT_MODE); sprintf(sTextBuffer, "Solo - %d", hsDatas.Solo_HS); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 - 54, (uint8_t *)sTextBuffer, LEFT_MODE); sprintf(sTextBuffer, "Multi - %d", hsDatas.Multi_HS); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 - 40, (uint8_t *)sTextBuffer, LEFT_MODE); BSP_LCD_FillRect(BSP_LCD_GetXSize() / 2 - 20, BSP_LCD_GetYSize() - 12, 40, 7); /* READY TO START GAME! */ strcpy(sTextBuffer, "Press START\0"); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - 5 * 12, BSP_LCD_GetYSize() / 2 + 40, (uint8_t *)sTextBuffer, LEFT_MODE); } /** * @brief Generate and display new game */ void displayGameArea(Game_ModeTypdef type, PP_BarTypeDef *_barDatas) { uint32_t seed = 0; currScore = 0; _barDatas->pos = BSP_LCD_GetXSize() / 2; gCtrlByte.scoreUpdFlag = 1; gCtrlByte.playerFail = 0; currMenu = MENU_GAME; BSP_LCD_Clear(LCD_COLOR_BLACK); BSP_LCD_FillRect(BSP_LCD_GetXSize() / 2 - 20, BSP_LCD_GetYSize() - 12, 40, 7); seed = genSeed(); switch (type) { case MULTI_MODE: break; case SOLO_MODE: default: BSP_LCD_SetBackColor(LCD_COLOR_BLACK); BSP_LCD_SetTextColor(LCD_COLOR_WHITE); updateScoreArea(type, 0); break; } generateNewBall(type,seed); updateBallDisplay(); } void displayEndMenu() { char sTextBuffer[32]; currMenu = MENU_END; BSP_LCD_SetFont(&Font16); strcpy(sTextBuffer, "YOU HAVE FAILED!\0"); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 - 54, (uint8_t *)sTextBuffer, LEFT_MODE); sprintf(sTextBuffer, "SCORE: %d", currScore); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - (strlen(sTextBuffer)/2 * BSP_LCD_GetFont()->Width), BSP_LCD_GetYSize() / 2 - 40, (uint8_t *)sTextBuffer, LEFT_MODE); //TODO: Save new HighScore in EEPROM if greather than the old one /* READY TO START NEW GAME! */ strcpy(sTextBuffer, "Press START\0"); BSP_LCD_DisplayStringAt(BSP_LCD_GetXSize() / 2 - 5 * 12, BSP_LCD_GetYSize() / 2 + 40, (uint8_t *)sTextBuffer, LEFT_MODE); } MenuTypdef getCurrMenu() { return currMenu; }