From ea105ba3f34185acc7d581fe537de4bbafce712e Mon Sep 17 00:00:00 2001 From: Miloslav Ciz Date: Mon, 27 Jan 2025 23:46:44 +0100 Subject: [PATCH] Add map beating --- TODO.txt | 26 +++++++++++++-------- assets.h | 1 + data | 2 ++ game.h | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- general.h | 17 ++++++++++++++ renderer.h | 24 +++++++++++++------ 6 files changed, 119 insertions(+), 18 deletions(-) diff --git a/TODO.txt b/TODO.txt index 868b94e..bf0e1c5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,13 +1,19 @@ =========== GENERAL ============== - make a small txt game menual -- test if the replay stretching works +- test: + - long replay + - replay stretching works + - replay with input not occuring for more that LCR_SETTING_GHOST_STEP - replay validation -- ghosts: if the replay for the ghost is too long (too many samples for the - preallocated array), we can subdivide the sample resolution (i.e. "stretch" - the samples) to cover the whole replay (ofc for the price of worse quality). - add argc/argv to gameInit? could be used to quickly start maps, verify - replays etc. + replays etc. Options: + -sN: sound (0/1) + -mN: music (0/1) + -cN: set camera + -M: load last map in resource files (good for making maps) + -R: load last replay in resource files (good for making TASes) + -P: load and play against last replay in resource files - maybe each map could have a target time embedded: when beaten, the map would be marked as such - player name (modifiable via resource file) @@ -24,13 +30,15 @@ =========== BUGS ================= -- starting test replay two twice in a row breaks it -- drawing dithered transparent objects fills z-buffer in the transparent parts - and then the geometry behind it isn't drawn <- PARTIALLY FIXED NOW - =========== HANDLED ============== - allow stopping car rotation in air like in Trackmania +- drawing dithered transparent objects fills z-buffer in the transparent parts + and then the geometry behind it isn't drawn <- PARTIALLY FIXED, LOOKS GOOD +- starting test replay two twice in a row breaks it +- ghosts: if the replay for the ghost is too long (too many samples for the + preallocated array), we can subdivide the sample resolution (i.e. "stretch" + the samples) to cover the whole replay (ofc for the price of worse quality). - force chunk reload upon map restart so that there's no blinking at start - ghost visible distance -- maybe even car should have this (watching rep with free camera)? maybe generalize setCar/GhostVisibility to diff --git a/assets.h b/assets.h index 849d032..659eb74 100644 --- a/assets.h +++ b/assets.h @@ -172,6 +172,7 @@ uint16_t LCR_getFontChar(char c) case '_': _F( 8,10,10,10,10,10,10,10,10,10) case '|': _F( 5, 7, 7, 7, 7, 7, 7, 7, 7, 7) case '=': _F( 4, 6, 0, 2, 2, 2, 2, 2, 2, 2) + case '#': _F( 5, 7,14,14,14,14,14,14,14,14) // used for "check" default: return 0; break; } diff --git a/data b/data index f3324aa..1fd17d3 100644 --- a/data +++ b/data @@ -1,2 +1,4 @@ #Mmap2;4321 1 :*H1k0J :,s0s0 :fd190 +#Bnomap #Rrep2;testmap;482f70f9 00000843:0173:0081:0029:0111:0039:0071:00a3:0061:0169:0051:00b3:0041:0073:0081:0033:0041:0033:0231:0030:0098:0029:0011:0163:00f1:0053:0081:0033:0051:0023:0031:00f0:0032:0023:0131:00b9:0081:0023:00a1:0119:00e1:00c9:0071:0039:00a1:00d3:0021:01f9:0091:0079:0091:0039:0051:0049:0021:0083:0031:0083:0031:0083:0061:0089:0121:00a0:0058:002c:0048:0061:0013:0150:0052:00c0:00a1:0053:0041:0043:0031:0020:0092:0063:0181:0010:00a2:0013:0071:00e0:0028:00e9:0078:00a9:0043:0032:0123:0042:0080:0038:004c:01a8:0050:0032:0033:0101 +#Btestmap; diff --git a/game.h b/game.h index c3fd6c0..d53fa0b 100644 --- a/game.h +++ b/game.h @@ -838,13 +838,71 @@ void LCR_gameLoadDataFileChunk(unsigned int startIndex, char magicNumber) } } +/** + Assumes maps are loaded in menu items, checks (in the resource file) which + ones have been marked as beaten and marks corresponding menu items as such. +*/ +void LCR_checkBeatenMaps(void) +{ + LCR_LOG2("checking beaten maps"); + + char name[LCR_MAP_NAME_MAX_LEN + 1]; + LCR_gameRewindDataFile(); + + while (1) + { + char c = LCR_gameGetNextDataFileChar(); + + if (c == 'B') + { + uint8_t i = 0; + + while (i < LCR_MAP_NAME_MAX_LEN + 1) + { + c = LCR_gameGetNextDataFileChar(); + + if (c < ' ' || c == LCR_RESOURCE_FILE_SEPARATOR || + c == LCR_RESOURCE_FILE_SEPARATOR2) + break; + + name[i] = c; + + i++; + } + + name[i] = 0; + + for (uint8_t j = 0; j < LCR_game.menu.itemCount; ++j) + if (_LCR_strCmp(name,LCR_game.menu.itemNamePtrs[j])) + { + for (uint8_t k = 0; k < LCR_MENU_STRING_SIZE; ++k) + if (LCR_game.menu.itemNames[j][k] == 0) + { + LCR_game.menu.itemNames[j] + [k - (k == LCR_MENU_STRING_SIZE - 1)] = '#'; + LCR_game.menu.itemNames[j] + [k + (k != LCR_MENU_STRING_SIZE - 1)] = 0; + break; + } + } + } + else + while (c != 0 && c != LCR_RESOURCE_FILE_SEPARATOR) + c = LCR_gameGetNextDataFileChar(); + + if (c == 0) + break; + } +} + + + + void LCR_gameEnd(void) { LCR_LOG0("ending"); } - - void LCR_gameTimeToStr(uint32_t timeMS, char *str) { str[9] = 0; @@ -1168,10 +1226,15 @@ void LCR_gameHandleInput(void) if (tabSwitchedTo == 0) LCR_gameLoadMainMenuItems(); else if (tabSwitchedTo > 0 || scrolled != 0) + { LCR_gameLoadDataFileChunk( (tabSwitchedTo > 0) ? 0 : (LCR_game.dataFile.firstItemIndex + scrolled * LCR_RESOURCE_ITEM_CHUNK), LCR_game.menu.selectedTab == 1 ? 'M' : 'R'); + + if (LCR_game.menu.selectedTab == 1) + LCR_checkBeatenMaps(); + } } uint8_t LCR_gameStep(uint32_t time) diff --git a/general.h b/general.h index 1e70a90..bef9b20 100644 --- a/general.h +++ b/general.h @@ -66,4 +66,21 @@ uint16_t _LCR_simpleStrHash(char (*nextChar)(void), char endChar) return r; } +int _LCR_strCmp(const char *s1, const char *s2) +{ + while (1) + { + if (*s1 != *s2) + return 0; + + if (*s1 == 0) + break; + + s1++; + s2++; + } + + return 1; +} + #endif // guard diff --git a/renderer.h b/renderer.h index 6213959..3074f6f 100644 --- a/renderer.h +++ b/renderer.h @@ -1789,8 +1789,8 @@ void LCR_rendererBlitImage(uint8_t index, unsigned int x, unsigned int y, void LCR_rendererDrawMenu(const char *tabName,const char **items, unsigned char itemCount,char selectedItem) { - int stripHeight = LCR_EFFECTIVE_RESOLUTION_Y / 4; - int stripHeight2 = LCR_EFFECTIVE_RESOLUTION_Y / 10; + int stripHeight = (2 * LCR_EFFECTIVE_RESOLUTION_Y) / 7; + int stripHeight2 = LCR_EFFECTIVE_RESOLUTION_Y / 9; int i = 0; uint16_t effect = LCR_renderer.frame >> 1; @@ -1798,13 +1798,13 @@ void LCR_rendererDrawMenu(const char *tabName,const char **items, while (i < stripHeight * LCR_EFFECTIVE_RESOLUTION_X) { - LCR_drawPixel(i,0xd69b + LCR_drawPixel(i,0x4a8c #if LCR_SETTING_POTATO_GRAPHICS ); #else - ^ (effect & 0x18e3)); + ^ (effect & 0x1827)); - effect += 7; + effect += 3; #endif ++i; @@ -1850,6 +1850,16 @@ void LCR_rendererDrawMenu(const char *tabName,const char **items, for (int j = 0; j < itemCount + 1; ++j) { const char *s = j ? items[j - 1] : tabName; + const char *s2 = s; + uint16_t textColor = 0x5aeb; + + while (*s2) // if the item contains '#' (check), we draw it as green + { + if (*s2 == '#') + textColor = 0x0700; + + s2++; + } if (j == selectedItem + 1) for (int y = i - 10; y < i + LCR_rendererComputeTextHeight(3) + 10; ++y) @@ -1859,7 +1869,7 @@ void LCR_rendererDrawMenu(const char *tabName,const char **items, LCR_rendererDrawText(s,(LCR_EFFECTIVE_RESOLUTION_X - LCR_rendererComputeTextWidth(s,3)) / 2,i, - (j == 0 || j == selectedItem + 1) ? 0xf79c : 0x5aeb,3); + (j == 0 || j == selectedItem + 1) ? 0xf79c : textColor,3); if (j == 0) i = stripHeight + stripHeight2; @@ -1873,7 +1883,7 @@ void LCR_rendererDrawMenu(const char *tabName,const char **items, stripHeight / LCR_IMAGE_SIZE,0xffff); #endif - LCR_rendererDrawText(LCR_texts[LCR_TEXTS_VERSION],5,5,0xffff,2); + LCR_rendererDrawText(LCR_texts[LCR_TEXTS_VERSION],5,5,0xe71c,2); LCR_renderer.frame++; }