diff --git a/README.md b/README.md index c29ed9c..c68d16b 100644 --- a/README.md +++ b/README.md @@ -78,12 +78,12 @@ I do not cover the topic in detail because there are many better sources on that - line clear animation (TGM?) - ports for other platforms - better joystick support -- advanced hiscore system (best score in Marathon/Ultra, best time in Sprint, best lines in Ultra) - another implementation of hold (as an option) with a separate slot for a tetromino - new KiCAD skin with no tetromino stats (I got bored with them) - some kind of system promoting sponsoring the project (sponsor-defined wishes shown during gameplay?) - new sound effects - option to imitate playing multiplayer mode (dynamic debris generation during gameplay) +- skin redesign to make them more consistent with implemented game modes ### sponsors - Saikazu diff --git a/inc/data_persistence.h b/inc/data_persistence.h index 33b7c83..90e4103 100644 --- a/inc/data_persistence.h +++ b/inc/data_persistence.h @@ -1,12 +1,24 @@ #ifndef _H_HISCORE #define _H_HISCORE +enum RecordType +{ + RT_MARATHON_SCORE, + RT_MARATHON_LINES, + RT_SPRINT_TIME, + RT_ULTRA_SCORE, + RT_ULTRA_LINES, + RT_END +}; + extern char dirpath[]; void initPaths(void); -int loadHiscore(void); -void saveHiscore(int hi); +int getRecord(enum RecordType rt); +void setRecord(enum RecordType rt, int record); +void loadRecords(void); +void saveRecords(void); void loadSettings(void); void saveSettings(void); diff --git a/inc/main.h b/inc/main.h index 28e4764..259f1e8 100644 --- a/inc/main.h +++ b/inc/main.h @@ -147,7 +147,6 @@ extern Uint32 last_drop_time; extern int draw_delta_drop; extern int brick_size; -extern int hiscore; extern int score; extern int lines; extern int level; @@ -181,6 +180,7 @@ void markDrop(void); Uint32 getNextDropTime(void); void setDropRate(int level); void softDropTimeCounter(void); +void convertMsToStr(Uint32 ms, char *dest); void getShapeDimensions(const struct Shape *shape, int *minx, int *maxx, int *miny, int *maxy); struct Shape* getShape(enum FigureId id); diff --git a/skins/pen'n'paper/game.txt b/skins/pen'n'paper/game.txt index d39e648..255e61d 100644 --- a/skins/pen'n'paper/game.txt +++ b/skins/pen'n'paper/game.txt @@ -33,7 +33,7 @@ box 246 172 48 24 48 255 255 255 ; text fontid x y alignx aligny r g b string text 0 47 25 0 0 58 56 73 "$hiscore" text 0 47 60 0 0 58 56 73 "$score" -text 0 122 4 0 0 58 56 73 "$timer" +text 0 132 4 0 0 58 56 73 "$timer" text 0 154 20 1 0 58 56 73 "LINE(S): $lines" text 0 154 88 1 0 58 56 73 "$lcttop" text 0 154 104 1 0 58 56 73 "$lctmid" diff --git a/src/data_persistence.c b/src/data_persistence.c index 7406109..0ff7da8 100644 --- a/src/data_persistence.c +++ b/src/data_persistence.c @@ -19,6 +19,8 @@ char dirpath[256]; static char hiscore_path[256]; static char settings_path[256]; +static int records[RT_END] = { 0 }; +static bool records_need_save = false; static void createGameDir(void) { @@ -33,28 +35,49 @@ void initPaths(void) sprintf(settings_path, "%s/%s", dirpath, SETTINGS_FILE); } -int loadHiscore(void) +int getRecord(enum RecordType rt) +{ + return records[rt]; +} + +void setRecord(enum RecordType rt, int record) +{ + records_need_save = records[rt] != record; + records[rt] = record; +} + +void loadRecords(void) { FILE *hifile = fopen(hiscore_path, "r"); if (hifile) { - int hi = 0; - fscanf(hifile, "%d", &hi); + for (int i = 0; i < RT_END; ++i) + { + int value = 0; + if (1 == fscanf(hifile, "%d", &value)) + { + setRecord(i, value); + } + } fclose(hifile); - return hi; + records_need_save = false; } - else - return 0; } -void saveHiscore(int hi) +void saveRecords(void) { + if (!records_need_save) + return; createGameDir(); FILE *hifile = fopen(hiscore_path, "w"); if (hifile) { - fprintf(hifile, "%d", hi); + for (int i = 0; i < RT_END; ++i) + { + fprintf(hifile, "%d\n", getRecord(i)); + } fclose(hifile); + records_need_save = false; } } diff --git a/src/main.c b/src/main.c index 285a914..c80fe1d 100644 --- a/src/main.c +++ b/src/main.c @@ -77,7 +77,6 @@ enum TetrominoColor tetrominocolor = TC_STANDARD; enum GameState gamestate = GS_MAINMENU; static bool hold_ready = true; -int hiscore = 0; int score = 0; int lines = 0; int level = 0; @@ -86,7 +85,6 @@ int ttr = 0; int b2b = 0; // back2back bonus int combo = 0; // combo bonus static int lines_level_up = 0; -static int old_hiscore = 0; char lctext_top[LCT_LEN]; char lctext_mid[LCT_LEN]; @@ -94,6 +92,7 @@ char lctext_bot[LCT_LEN]; Uint32 lct_deadline = 0; Uint32 game_starttime = 0; +Uint32 game_totaltime = 0; char gametimer[GAMETIMER_STRLEN]; // test variables for T-Spin detection @@ -232,10 +231,11 @@ enum TSpinType getTSpinType(void); enum GameOverType checkGameEnd(void); void onDrop(void); void onLineClear(int removed); -void onGameOver(void); +void onGameOver(enum GameOverType reason); void checkForPrelocking(void); void updateLCT(char *top, char *mid, char *bot, Uint32 ms); void updateGTimer(Uint32 ms); +void updateHiscores(enum GameMode gm, enum GameOverType got); void initFigures(void); void spawnFigure(void); @@ -385,8 +385,7 @@ void initialize(void) { SDL_Surface *ts = NULL; - hiscore = loadHiscore(); - old_hiscore = hiscore; + loadRecords(); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { @@ -459,10 +458,9 @@ void initialize(void) void finalize(void) { - if (hiscore > old_hiscore) - saveHiscore(hiscore); if (settings_changed) saveSettings(); + saveRecords(); skin_destroySkin(&gameskin); @@ -700,8 +698,6 @@ void onLineClear(int removed) extra += 50 * (level + 1) * combo; } score += extra; - if (score > hiscore) - hiscore = score; // notify a player about a special event static char clear_text[5][16] = { @@ -786,9 +782,11 @@ void onLineClear(int removed) playEffect(SE_CLEAR); } -void onGameOver(void) +void onGameOver(enum GameOverType reason) { stopMusic(); + game_totaltime = SDL_GetTicks() - game_starttime; + updateHiscores(menu_gamemode, reason); gamestate = GS_GAMEOVER; } @@ -821,8 +819,6 @@ void dropSoft(void) checkForPrelocking(); } } - if (score > hiscore) - hiscore = score; onDrop(); } } @@ -858,8 +854,6 @@ void dropHard(void) } --figures[0]->y; score -= 2; - if (score > hiscore) - hiscore = score; lockFigure(); onDrop(); } @@ -938,8 +932,9 @@ void lockFigure(void) figures[0] = NULL; int removed = removeFullLines(); - if (checkGameEnd() != GOT_PLAYING) - onGameOver(); + enum GameOverType goreason = checkGameEnd(); + if (goreason != GOT_PLAYING) + onGameOver(goreason); softdrop_pressed = false; softdrop_press_time = 0; @@ -1042,8 +1037,6 @@ int removeFullLines(void) default: ; // no action } - if (score > hiscore) - hiscore = score; } return removed_lines; @@ -1201,11 +1194,6 @@ static void pause(void) static void quit(void) { gamestate = GS_MAINMENU; - if (hiscore > old_hiscore) - { - saveHiscore(hiscore); - old_hiscore = hiscore; - } } void ingame_processInputEvents(void) @@ -1691,6 +1679,26 @@ void updateLCT(char *top, char *mid, char *bot, Uint32 ms) lct_deadline = SDL_GetTicks() + ms; } +void convertMsToStr(Uint32 ms, char *dest) +{ + int hh, mm, ss, cs; + hh = ms / (1000 * 60 * 60); + ms = ms % (1000 * 60 * 60); + mm = ms / (1000 * 60); + ms = ms % (1000 * 60); + ss = ms / 1000; + ms = ms % 1000; + cs = ms / 10; + if (hh) + { + sprintf(dest, "%02d:%02d:%02d.%02d", hh, mm, ss, cs); + } + else + { + sprintf(dest, "%02d:%02d.%02d", mm, ss, cs); + } +} + void updateGTimer(Uint32 ms) { if (GM_ULTRA == menu_gamemode) @@ -1704,16 +1712,43 @@ void updateGTimer(Uint32 ms) ms = 0; } } + convertMsToStr(ms, gametimer); +} - int hh, mm, ss, cs; - hh = ms / (1000 * 60 * 60); - ms = ms % (1000 * 60 * 60); - mm = ms / (1000 * 60); - ms = ms % (1000 * 60); - ss = ms / 1000; - ms = ms % 1000; - cs = ms / 10; - sprintf(gametimer, "%02d:%02d:%02d.%02d", hh, mm, ss, cs); +void updateHiscores(enum GameMode gm, enum GameOverType got) +{ + switch (gm) + { + case GM_MARATHON: + if (score > getRecord(RT_MARATHON_SCORE)) + { + setRecord(RT_MARATHON_SCORE, score); + } + if (lines > getRecord(RT_MARATHON_LINES)) + { + setRecord(RT_MARATHON_LINES, lines); + } + break; + case GM_SPRINT: + if (getRecord(RT_SPRINT_TIME) == 0 || + (game_totaltime < getRecord(RT_SPRINT_TIME) && + GOT_LINECLEAR == got)) + { + setRecord(RT_SPRINT_TIME, game_totaltime); + } + break; + case GM_ULTRA: + if (score > getRecord(RT_ULTRA_SCORE)) + { + setRecord(RT_ULTRA_SCORE, score); + } + if (lines > getRecord(RT_ULTRA_LINES)) + { + setRecord(RT_ULTRA_LINES, lines); + } + break; + } + saveRecords(); } #ifdef DEV diff --git a/src/skin.c b/src/skin.c index ab5ffea..546c438 100644 --- a/src/skin.c +++ b/src/skin.c @@ -9,6 +9,7 @@ #include "main.h" #include "video.h" #include "state_mainmenu.h" +#include "data_persistence.h" #define log /*printf*/ @@ -844,17 +845,32 @@ static void int_replace(char *where, const char *what, int with) static void replace_all_vars(char *where) { - int_replace(where, "$hiscore", hiscore); - int_replace(where, "$score", score); - int_replace(where, "$level", level); + enum RecordType rt; int showlines = lines; - if (GM_SPRINT == menu_gamemode) + switch (menu_gamemode) { - if (lines < SPRINT_LINE_COUNT) - showlines = SPRINT_LINE_COUNT - lines; - else - showlines = 0; + case GM_MARATHON: + rt = RT_MARATHON_SCORE; + int_replace(where, "$hiscore", getRecord(rt)); + break; + case GM_SPRINT: + // TODO: format time properly + rt = RT_SPRINT_TIME; + if (lines < SPRINT_LINE_COUNT) + showlines = SPRINT_LINE_COUNT - lines; + else + showlines = 0; + char buff[GAMETIMER_STRLEN]; + convertMsToStr(getRecord(rt), buff); + str_replace(where, "$hiscore", buff); + break; + case GM_ULTRA: + rt = RT_ULTRA_SCORE; + int_replace(where, "$hiscore", getRecord(rt)); + break; } + int_replace(where, "$score", score); + int_replace(where, "$level", level); int_replace(where, "$lines", showlines); int_replace(where, "$tetris", ttr); int_replace(where, "$fps", fps);