2024-05-04 11:31:27 +02:00
|
|
|
// #define STATE_DEBUGGING
|
|
|
|
|
2024-03-13 17:35:46 +01:00
|
|
|
#include "stddef.h"
|
|
|
|
#include "stdint.h"
|
|
|
|
|
|
|
|
#include "game.h"
|
2024-03-18 12:51:57 +01:00
|
|
|
#include "scheduler.h"
|
2024-03-13 17:35:46 +01:00
|
|
|
#include "shapes.h"
|
|
|
|
#include "canvas.h"
|
2024-03-22 11:59:28 +01:00
|
|
|
#include "../rgb-driver/colors.h"
|
2024-03-22 18:38:30 +01:00
|
|
|
#include "display.h"
|
2024-04-15 16:42:33 +02:00
|
|
|
#include "sound.h"
|
2024-04-19 11:38:32 +02:00
|
|
|
#include "eeprom.h"
|
|
|
|
#include "buttons.h"
|
2024-03-13 17:35:46 +01:00
|
|
|
|
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
#define GAME_CYCLE_TIME 10
|
2024-03-22 11:59:28 +01:00
|
|
|
#define GAMEOVER_DELAY 10
|
2024-05-04 11:31:27 +02:00
|
|
|
#define MAX_LEVEL 100
|
2024-03-22 11:59:28 +01:00
|
|
|
|
|
|
|
|
2024-05-04 11:35:29 +02:00
|
|
|
static uint16_t delayFactor(uint16_t level) {
|
2024-04-23 12:45:39 +02:00
|
|
|
return MAX_LEVEL + 1 - level;
|
2024-03-22 11:59:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef enum {
|
2024-05-21 12:31:55 +02:00
|
|
|
e_BootWait,
|
2024-05-04 11:31:27 +02:00
|
|
|
e_Start, e_NewStone, e_Down, e_DownDelay,
|
2024-05-04 12:58:02 +02:00
|
|
|
e_ClearRowInit, e_ClearRowNext, e_ClearRowCheck, e_ClearRowFlash, e_ClearRowFlashDelay, e_ClearRowWipe,
|
2024-03-22 11:59:28 +01:00
|
|
|
e_GameOver, e_GameOverFill, e_GameOverWipe, e_GameOverDelay
|
|
|
|
} state_t;
|
2024-03-13 17:35:46 +01:00
|
|
|
|
|
|
|
void gameExec(void *handle) {
|
2024-05-21 12:31:55 +02:00
|
|
|
static state_t state = e_BootWait;
|
|
|
|
static uint16_t bootWaitTime = 2500 / GAME_CYCLE_TIME;
|
2024-03-22 11:59:28 +01:00
|
|
|
static uint8_t gameOverDelay;
|
|
|
|
static uint8_t rowIndex;
|
2024-05-04 11:31:27 +02:00
|
|
|
static uint16_t proceedDelay;
|
|
|
|
static uint16_t level;
|
2024-04-23 12:45:39 +02:00
|
|
|
static uint16_t filledLines;
|
2024-03-22 22:49:43 +01:00
|
|
|
static uint16_t score;
|
2024-04-19 11:38:32 +02:00
|
|
|
static bool newHighScoreAchieved;
|
2024-03-13 17:35:46 +01:00
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
static uint8_t clearCheckCnt;
|
2024-05-03 23:10:29 +02:00
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
#ifdef STATE_DEBUGGING
|
|
|
|
displaySetValue(state);
|
|
|
|
#endif
|
2024-03-22 11:59:28 +01:00
|
|
|
// --- engine begin -------------------------------------------------------
|
2024-03-13 17:35:46 +01:00
|
|
|
switch (state) {
|
2024-05-21 12:31:55 +02:00
|
|
|
case e_BootWait:
|
|
|
|
bootWaitTime -= 1;
|
|
|
|
if (bootWaitTime == 0) {
|
|
|
|
state = e_Start;
|
|
|
|
}
|
|
|
|
break;
|
2024-03-22 12:00:23 +01:00
|
|
|
// --- phase: game --------------------------------------------------------
|
2024-03-22 11:59:28 +01:00
|
|
|
case e_Start:
|
2024-03-13 17:35:46 +01:00
|
|
|
canvasClear();
|
2024-04-17 15:30:45 +02:00
|
|
|
soundCtrl(SOUND_START);
|
2024-03-22 11:59:28 +01:00
|
|
|
level = 1;
|
2024-04-23 12:45:39 +02:00
|
|
|
filledLines = 0;
|
2024-03-22 22:49:43 +01:00
|
|
|
score = 0;
|
2024-04-19 11:38:32 +02:00
|
|
|
newHighScoreAchieved = false;
|
2024-03-22 11:59:28 +01:00
|
|
|
state = e_NewStone;
|
2024-03-13 17:35:46 +01:00
|
|
|
break;
|
|
|
|
|
2024-03-22 11:59:28 +01:00
|
|
|
case e_NewStone:
|
2024-03-13 17:35:46 +01:00
|
|
|
stoneCreate();
|
|
|
|
if (stoneDraw()) {
|
2024-03-22 11:59:28 +01:00
|
|
|
proceedDelay = delayFactor(level);
|
|
|
|
state = e_DownDelay;
|
2024-03-13 17:35:46 +01:00
|
|
|
} else {
|
2024-03-22 11:59:28 +01:00
|
|
|
state = e_GameOver;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case e_DownDelay:
|
|
|
|
proceedDelay--;
|
|
|
|
if (proceedDelay == 0) {
|
2024-05-03 16:21:20 +02:00
|
|
|
state = e_Down;
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
|
|
|
break;
|
2024-03-22 11:59:28 +01:00
|
|
|
|
|
|
|
case e_Down:
|
2024-03-13 17:35:46 +01:00
|
|
|
if (! stoneMoveDown()) {
|
2024-04-17 15:30:45 +02:00
|
|
|
soundCtrl(SOUND_LOCK);
|
2024-05-03 16:21:20 +02:00
|
|
|
stoneLock();
|
2024-05-04 11:31:27 +02:00
|
|
|
state = e_ClearRowInit;
|
2024-03-22 11:59:28 +01:00
|
|
|
} else {
|
|
|
|
proceedDelay = delayFactor(level);
|
|
|
|
state = e_DownDelay;
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
// --- phase: clear rows --------------------------------------------------
|
|
|
|
case e_ClearRowInit:
|
|
|
|
clearCheckCnt = 0;
|
|
|
|
state = e_ClearRowCheck;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case e_ClearRowNext:
|
|
|
|
if (clearCheckCnt >= CANVAS_HEIGHT) {
|
|
|
|
state = e_NewStone;
|
|
|
|
} else {
|
|
|
|
clearCheckCnt += 1;
|
|
|
|
state = e_ClearRowCheck;
|
2024-05-03 23:10:29 +02:00
|
|
|
}
|
2024-05-04 11:31:27 +02:00
|
|
|
break;
|
2024-05-03 23:10:29 +02:00
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
case e_ClearRowCheck:
|
|
|
|
if (canvasIsRowFilled(clearCheckCnt)) {
|
|
|
|
score += level;
|
|
|
|
if (score > eepromReadHighScore()) {
|
|
|
|
newHighScoreAchieved = true;
|
2024-05-18 20:09:21 +02:00
|
|
|
eepromSetHighScore(score);
|
|
|
|
eepromCommit();
|
2024-05-04 11:31:27 +02:00
|
|
|
}
|
|
|
|
state = e_ClearRowFlash;
|
|
|
|
} else {
|
|
|
|
state = e_ClearRowNext;
|
2024-05-03 23:10:29 +02:00
|
|
|
}
|
2024-05-04 11:31:27 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case e_ClearRowFlash:
|
2024-05-18 20:09:21 +02:00
|
|
|
canvasFillRow(clearCheckCnt, eepromReadFlashColor());
|
2024-05-04 12:58:02 +02:00
|
|
|
state = e_ClearRowFlashDelay;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case e_ClearRowFlashDelay:
|
2024-05-04 11:31:27 +02:00
|
|
|
state = e_ClearRowWipe;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case e_ClearRowWipe:
|
|
|
|
canvasWipeRow(clearCheckCnt);
|
|
|
|
filledLines += 1;
|
2024-05-03 23:10:29 +02:00
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
if ((filledLines > 0) && ((filledLines % 10) == 0)) {
|
2024-05-03 23:10:29 +02:00
|
|
|
if (level < MAX_LEVEL) {
|
|
|
|
level += 1;
|
|
|
|
}
|
|
|
|
soundCtrl(SOUND_FANFARE);
|
2024-05-04 11:31:27 +02:00
|
|
|
} else {
|
|
|
|
soundCtrl(SOUND_PLING);
|
2024-05-03 23:10:29 +02:00
|
|
|
}
|
2024-05-04 11:31:27 +02:00
|
|
|
state = e_ClearRowNext;
|
2024-05-03 16:21:20 +02:00
|
|
|
break;
|
|
|
|
|
2024-03-22 12:00:23 +01:00
|
|
|
// --- phase: game over ---------------------------------------------------
|
2024-03-22 11:59:28 +01:00
|
|
|
case e_GameOver:
|
2024-04-17 15:30:45 +02:00
|
|
|
soundCtrl(SOUND_GAMEOVER);
|
2024-03-22 11:59:28 +01:00
|
|
|
rowIndex = CANVAS_HEIGHT;
|
|
|
|
state = e_GameOverFill;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case e_GameOverFill:
|
|
|
|
rowIndex--;
|
2024-04-19 11:38:32 +02:00
|
|
|
canvasFillRow(rowIndex, newHighScoreAchieved ? _green : _red);
|
2024-03-22 11:59:28 +01:00
|
|
|
if (rowIndex == 0) {
|
|
|
|
state = e_GameOverWipe;
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
2024-03-22 11:59:28 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case e_GameOverWipe:
|
|
|
|
canvasWipeRow(rowIndex);
|
|
|
|
rowIndex++;
|
|
|
|
if (rowIndex == CANVAS_HEIGHT) {
|
|
|
|
gameOverDelay = GAMEOVER_DELAY;
|
|
|
|
state = e_GameOverDelay;
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2024-03-22 11:59:28 +01:00
|
|
|
case e_GameOverDelay:
|
|
|
|
gameOverDelay--;
|
|
|
|
if (gameOverDelay == 0) {
|
|
|
|
state = e_Start;
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2024-03-22 11:59:28 +01:00
|
|
|
// --- engine end ---------------------------------------------------------
|
2024-03-13 17:35:46 +01:00
|
|
|
|
|
|
|
canvasShow();
|
2024-04-23 12:45:39 +02:00
|
|
|
|
2024-05-04 11:31:27 +02:00
|
|
|
#ifndef STATE_DEBUGGING
|
2024-04-19 11:38:32 +02:00
|
|
|
if (isGameActive()) {
|
|
|
|
displaySetValue(score);
|
|
|
|
} else {
|
|
|
|
displaySetValue(eepromReadHighScore());
|
|
|
|
}
|
2024-05-04 11:31:27 +02:00
|
|
|
#endif
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void gameInit() {
|
2024-03-22 11:59:28 +01:00
|
|
|
schAdd(gameExec, NULL, 0, GAME_CYCLE_TIME);
|
2024-03-13 17:35:46 +01:00
|
|
|
}
|
2024-04-17 15:30:45 +02:00
|
|
|
|