282 lines
8.5 KiB
C
282 lines
8.5 KiB
C
/*
|
|
* 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;
|
|
}
|