diff --git a/README.md b/README.md index eb84627..0c7983d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Some of the **gameplay features** include: - **No codes of censorship, flags, furry mascots or similar political bullshit**. This is just a game. - **No "modern" bullshit such as OOP, meme design patterns, scripting and similar nonsense**. - It is **absolutely and completely public domain free software with ZERO conditions on use** under CC0, it is legally more free that most "FOSS" software, there is no copyleft or credit requirement, you can do ABSOLUTELY anything you want with the project. This is a **selfless project aiming for no benefit of the creator** (include any non-finacial benefit as well), this is made purely to bring more good to the world without gaining any advantage for self. +- This game has **PURE SOUL**. No Unity or Unreal, no "retro" imitation shaders, no assets from a store. This isn't an immitation of a 90s game, this IS a 90s game. ## Manifesto diff --git a/TODO.txt b/TODO.txt index 71971a0..c6a9699 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,5 +1,10 @@ =========== GENERAL ============== +- make the U-ramp map taller due to new physics +- replay format should probably record game version + - also there should probably be some version system that says version of + physics vs version of everything else; replay could only record physics + version (maybe just two or three chars?) - on 1st map the camera is obscured by the wall at the start, fix it somehow (not the best first impression) - culling is very slow now, it showed that distance bailout can accelerate it diff --git a/assets.h b/assets.h index f05f416..dba0fa0 100644 --- a/assets.h +++ b/assets.h @@ -42,7 +42,11 @@ static const char *LCR_texts[] = "exit", #define LCR_TEXTS_LOADING 10 - "loading" + "loading", + +#define LCR_TEXTS_SAVED 11 + + "saved" }; // TODO: define string for CLI arguments for frontends? diff --git a/game.h b/game.h index 309e483..8c856ad 100644 --- a/game.h +++ b/game.h @@ -264,6 +264,7 @@ static inline void LCR_gameDrawPixelXYSafe(unsigned int x, unsigned int y, struct { uint8_t state; + uint32_t statePrev; uint32_t stateStartTime; uint32_t time; ///< Current frame's time. uint32_t frame; ///< Current frame number. @@ -401,6 +402,7 @@ static inline void LCR_gameDrawPixelXYSafe(unsigned int x, unsigned int y, void LCR_gameSetState(uint8_t state) { LCR_LOG1("changing state"); + LCR_game.statePrev = LCR_game.state; LCR_game.state = state; LCR_game.stateStartTime = LCR_game.time; } @@ -1049,7 +1051,7 @@ void LCR_gameTimeToStr(uint32_t timeMS, char *str) str[2] = '\''; } -LCR_gameDrawPopupMessage(void) +void LCR_gameDrawPopupMessage(void) { int textH = LCR_rendererComputeTextHeight(5); int textW = LCR_rendererComputeTextWidth(LCR_game.popupStr,5); @@ -1151,6 +1153,12 @@ void _LCR_gameDataCharWrite(char c) printf("%c",c); } +void LCR_gameSaveReplay(void) +{ + LCR_LOG0("saving replay"); + LCR_replayOutputStr(_LCR_gameDataCharWrite); +} + /** Helper subroutine, handles user input during main loop frame, EXCEPT for the driving input (that is handled in the loop itself). @@ -1160,153 +1168,24 @@ void LCR_gameHandleInput(void) int tabSwitchedTo = -1; int scrolled = 0; - switch (LCR_game.state) + if (LCR_game.state != LCR_GAME_STATE_MENU) { - case LCR_GAME_STATE_MENU: - if (LCR_game.keyStates[LCR_KEY_RIGHT] == 1) - { - LCR_LOG1("menu tab right"); - LCR_game.menu.selectedTab = - (LCR_game.menu.selectedTab + 1) % LCR_MENU_TABS; - tabSwitchedTo = LCR_game.menu.selectedTab; - LCR_game.menu.selectedItem = 0; - LCR_audioPlaySound(LCR_SOUND_CLICK); - } - else if (LCR_game.keyStates[LCR_KEY_LEFT] == 1) - { - LCR_LOG1("menu tab left"); - LCR_game.menu.selectedTab = - (LCR_game.menu.selectedTab + LCR_MENU_TABS - 1) % LCR_MENU_TABS; - tabSwitchedTo = LCR_game.menu.selectedTab; - LCR_game.menu.selectedItem = 0; - LCR_audioPlaySound(LCR_SOUND_CLICK); - } - else if (LCR_game.keyStates[LCR_KEY_UP] == 1) - { - LCR_LOG1("menu item up"); - - if (LCR_game.menu.selectedItem != 0) - { - LCR_game.menu.selectedItem--; - LCR_audioPlaySound(LCR_SOUND_CLICK); - } - else if (LCR_game.menu.selectedTab != 0 && - LCR_game.dataFile.firstItemIndex != 0) - { - LCR_game.menu.selectedItem = LCR_RESOURCE_ITEM_CHUNK - 1; - LCR_audioPlaySound(LCR_SOUND_CLICK); - scrolled = -1; - } - } - else if (LCR_game.keyStates[LCR_KEY_DOWN] == 1) - { - LCR_LOG1("menu item down"); - - if (LCR_game.menu.selectedTab == 0) - { - if (LCR_game.menu.selectedItem < 4) - { - LCR_game.menu.selectedItem++; - LCR_audioPlaySound(LCR_SOUND_CLICK); - } - } - else if (LCR_game.menu.selectedItem < LCR_game.menu.itemCount - 1) - { - LCR_game.menu.selectedItem++; - LCR_audioPlaySound(LCR_SOUND_CLICK); - } - else if (LCR_game.dataFile.firstItemIndex + - LCR_RESOURCE_ITEM_CHUNK < LCR_game.dataFile.itemsTotal) - { - LCR_game.menu.selectedItem = 0; - LCR_audioPlaySound(LCR_SOUND_CLICK); - scrolled = 1; - } - } - else if (LCR_game.keyStates[LCR_KEY_B] == 1 && LCR_currentMap.blockCount) - { - LCR_LOG1("menu quit"); - LCR_gameSetState(LCR_GAME_STATE_RUN); - } - else if (LCR_game.keyStates[LCR_KEY_A] == 1) - { - LCR_LOG1("menu confirm"); - LCR_audioPlaySound(LCR_SOUND_CLICK); - - switch (LCR_game.menu.selectedTab) - { - case 0: - switch (LCR_game.menu.selectedItem) - { - case 0: - LCR_game.cameraMode = (LCR_game.cameraMode + 1) % 4; - LCR_rendererSetCarVisibility( - LCR_game.cameraMode != LCR_CAMERA_MODE_INSIDE); - LCR_rendererCameraReset(); - LCR_gamePopupNumber(LCR_game.cameraMode); - break; - -#if LCR_SETTING_MUSIC - case 1: - LCR_game.musicOn = !LCR_game.musicOn; - LCR_gamePopupNumber(LCR_game.musicOn); - break; -#endif - - case 2: - LCR_audio.on = !LCR_audio.on; - LCR_gamePopupNumber(LCR_audio.on); - break; - - case 4: - LCR_gameSetState(LCR_GAME_STATE_END); - break; - - default: break; - } - - LCR_gameLoadMainMenuItems(); - break; - - case 1: // maps - LCR_gameLoadMap(LCR_game.dataFile.firstItemIndex + - LCR_game.menu.selectedItem); - LCR_gameSetState(LCR_GAME_STATE_LOADING); - break; - - case 2: // view replay - case 3: // play against replay - { - int mapIndex = LCR_gameLoadReplay(LCR_game.dataFile.firstItemIndex + - LCR_game.menu.selectedItem); - - if (mapIndex < -1) - { - LCR_LOG1("couldn't load replay"); - } - else if (mapIndex == -1) - { - LCR_LOG1("couldn't load replay map"); - } - else - LCR_gameSetState(LCR_GAME_STATE_LOADING); - - break; - } - - default: break; - } - } - - break; - - case LCR_GAME_STATE_RUN_FINISHED: + if (LCR_game.state == LCR_GAME_STATE_RUN_FINISHED) + { if (LCR_game.keyStates[LCR_KEY_A] == 1) + { + if (LCR_game.runTimeMS <= LCR_currentMap.targetTime * LCR_RACING_TICK_MS + && !LCR_game.ghost.active) + { + LCR_LOG1("setting new target time"); + LCR_currentMap.targetTime = LCR_game.runTimeMS / LCR_RACING_TICK_MS; + } + LCR_gameResetRun(LCR_racing.playingReplay,LCR_game.ghost.active); - - break; - - case LCR_GAME_STATE_RUN_STARTING: + } + } + else if (LCR_game.state == LCR_GAME_STATE_RUN_STARTING) + { if (LCR_game.time - LCR_game.stateStartTime >= 1000 * LCR_SETTING_COUNTDOWN_SECONDS) { @@ -1316,53 +1195,195 @@ void LCR_gameHandleInput(void) else LCR_gamePopupNumber(LCR_SETTING_COUNTDOWN_SECONDS - (LCR_game.time - LCR_game.stateStartTime) / 1000); + } - // fall through - default: - if (LCR_game.keyStates[LCR_KEY_B] == 1) + if (LCR_game.keyStates[LCR_KEY_B] == 1) + { + LCR_LOG1("menu open"); + LCR_gameSetState(LCR_GAME_STATE_MENU); + LCR_game.menu.selectedItem = 0; + } + else if (LCR_game.cameraMode == LCR_CAMERA_MODE_FREE) + { + LCR_GameUnit offsets[5]; + + for (int i = 0; i < 5; ++i) + offsets[i] = 0; + + if (LCR_game.keyStates[LCR_KEY_A]) + { + if (LCR_game.keyStates[LCR_KEY_UP]) + offsets[4] = LCR_FREE_CAMERA_TURN_STEP; + else if (LCR_game.keyStates[LCR_KEY_DOWN]) + offsets[4] -= LCR_FREE_CAMERA_TURN_STEP; + + if (LCR_game.keyStates[LCR_KEY_RIGHT]) + offsets[3] -= LCR_FREE_CAMERA_TURN_STEP; + else if (LCR_game.keyStates[LCR_KEY_LEFT]) + offsets[3] = LCR_FREE_CAMERA_TURN_STEP; + } + else + { + if (LCR_game.keyStates[LCR_KEY_UP]) + offsets[0] = LCR_FREE_CAMERA_STEP; + else if (LCR_game.keyStates[LCR_KEY_DOWN]) + offsets[0] -= LCR_FREE_CAMERA_STEP; + + if (LCR_game.keyStates[LCR_KEY_RIGHT]) + offsets[1] = LCR_FREE_CAMERA_STEP; + else if (LCR_game.keyStates[LCR_KEY_LEFT]) + offsets[1] -= LCR_FREE_CAMERA_STEP; + } + + LCR_rendererMoveCamera(offsets,offsets + 3); + } + else if (LCR_game.keyStates[LCR_KEY_A] == 1) + LCR_gameResetRun(LCR_racing.playingReplay,LCR_game.ghost.active); + } + else // LCR_GAME_STATE_MENU + { + if (LCR_game.keyStates[LCR_KEY_RIGHT] == 1) + { + LCR_LOG1("menu tab right"); + LCR_game.menu.selectedTab = + (LCR_game.menu.selectedTab + 1) % LCR_MENU_TABS; + tabSwitchedTo = LCR_game.menu.selectedTab; + LCR_game.menu.selectedItem = 0; + LCR_audioPlaySound(LCR_SOUND_CLICK); + } + else if (LCR_game.keyStates[LCR_KEY_LEFT] == 1) + { + LCR_LOG1("menu tab left"); + LCR_game.menu.selectedTab = + (LCR_game.menu.selectedTab + LCR_MENU_TABS - 1) % LCR_MENU_TABS; + tabSwitchedTo = LCR_game.menu.selectedTab; + LCR_game.menu.selectedItem = 0; + LCR_audioPlaySound(LCR_SOUND_CLICK); + } + else if (LCR_game.keyStates[LCR_KEY_UP] == 1) + { + LCR_LOG1("menu item up"); + + if (LCR_game.menu.selectedItem != 0) + { + LCR_game.menu.selectedItem--; + LCR_audioPlaySound(LCR_SOUND_CLICK); + } + else if (LCR_game.menu.selectedTab != 0 && + LCR_game.dataFile.firstItemIndex != 0) + { + LCR_game.menu.selectedItem = LCR_RESOURCE_ITEM_CHUNK - 1; + LCR_audioPlaySound(LCR_SOUND_CLICK); + scrolled = -1; + } + } + else if (LCR_game.keyStates[LCR_KEY_DOWN] == 1) + { + LCR_LOG1("menu item down"); + + if (LCR_game.menu.selectedTab == 0) + { + if (LCR_game.menu.selectedItem < 4) + { + LCR_game.menu.selectedItem++; + LCR_audioPlaySound(LCR_SOUND_CLICK); + } + } + else if (LCR_game.menu.selectedItem < LCR_game.menu.itemCount - 1) + { + LCR_game.menu.selectedItem++; + LCR_audioPlaySound(LCR_SOUND_CLICK); + } + else if (LCR_game.dataFile.firstItemIndex + + LCR_RESOURCE_ITEM_CHUNK < LCR_game.dataFile.itemsTotal) { - LCR_LOG1("menu open"); - LCR_gameSetState(LCR_GAME_STATE_MENU); LCR_game.menu.selectedItem = 0; + LCR_audioPlaySound(LCR_SOUND_CLICK); + scrolled = 1; } - else if (LCR_game.cameraMode == LCR_CAMERA_MODE_FREE) + } + else if (LCR_game.keyStates[LCR_KEY_B] == 1 && LCR_currentMap.blockCount) + { + LCR_LOG1("menu quit"); + LCR_gameSetState(LCR_game.statePrev); + } + else if (LCR_game.keyStates[LCR_KEY_A] == 1) + { + LCR_LOG1("menu confirm"); + LCR_audioPlaySound(LCR_SOUND_CLICK); + + switch (LCR_game.menu.selectedTab) { - LCR_GameUnit offsets[5]; - - for (int i = 0; i < 5; ++i) - offsets[i] = 0; + case 0: + switch (LCR_game.menu.selectedItem) + { + case 0: + LCR_game.cameraMode = (LCR_game.cameraMode + 1) % 4; + LCR_rendererSetCarVisibility( + LCR_game.cameraMode != LCR_CAMERA_MODE_INSIDE); + LCR_rendererCameraReset(); + LCR_gamePopupNumber(LCR_game.cameraMode); + break; - if (LCR_game.keyStates[LCR_KEY_A]) +#if LCR_SETTING_MUSIC + case 1: + LCR_game.musicOn = !LCR_game.musicOn; + LCR_gamePopupNumber(LCR_game.musicOn); + break; +#endif + case 2: + LCR_audio.on = !LCR_audio.on; + LCR_gamePopupNumber(LCR_audio.on); + break; + + case 3: + if (LCR_game.statePrev == LCR_GAME_STATE_RUN_FINISHED) + { + LCR_gameSaveReplay(); + LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_SAVED]); + } + + break; + + case 4: + LCR_gameSetState(LCR_GAME_STATE_END); + break; + + default: break; + } + + LCR_gameLoadMainMenuItems(); + break; + + case 1: // maps + LCR_gameLoadMap(LCR_game.dataFile.firstItemIndex + + LCR_game.menu.selectedItem); + LCR_gameSetState(LCR_GAME_STATE_LOADING); + break; + + case 2: // view replay + case 3: // play against replay { - if (LCR_game.keyStates[LCR_KEY_UP]) - offsets[4] = LCR_FREE_CAMERA_TURN_STEP; - else if (LCR_game.keyStates[LCR_KEY_DOWN]) - offsets[4] -= LCR_FREE_CAMERA_TURN_STEP; + int mapIndex = LCR_gameLoadReplay(LCR_game.dataFile.firstItemIndex + + LCR_game.menu.selectedItem); - if (LCR_game.keyStates[LCR_KEY_RIGHT]) - offsets[3] -= LCR_FREE_CAMERA_TURN_STEP; - else if (LCR_game.keyStates[LCR_KEY_LEFT]) - offsets[3] = LCR_FREE_CAMERA_TURN_STEP; - } - else - { - if (LCR_game.keyStates[LCR_KEY_UP]) - offsets[0] = LCR_FREE_CAMERA_STEP; - else if (LCR_game.keyStates[LCR_KEY_DOWN]) - offsets[0] -= LCR_FREE_CAMERA_STEP; + if (mapIndex < -1) + { + LCR_LOG1("couldn't load replay"); + } + else if (mapIndex == -1) + { + LCR_LOG1("couldn't load replay map"); + } + else + LCR_gameSetState(LCR_GAME_STATE_LOADING); - if (LCR_game.keyStates[LCR_KEY_RIGHT]) - offsets[1] = LCR_FREE_CAMERA_STEP; - else if (LCR_game.keyStates[LCR_KEY_LEFT]) - offsets[1] -= LCR_FREE_CAMERA_STEP; + break; } - LCR_rendererMoveCamera(offsets,offsets + 3); + default: break; } - else if (LCR_game.keyStates[LCR_KEY_A] == 1) - LCR_gameResetRun(LCR_racing.playingReplay,LCR_game.ghost.active); - - break; + } } if (tabSwitchedTo == 0) @@ -1378,7 +1399,7 @@ void LCR_gameHandleInput(void) LCR_checkBeatenMaps(); } } - + uint8_t LCR_gameStep(uint32_t time) { LCR_LOG2("game step (start)"); @@ -1441,7 +1462,9 @@ uint8_t LCR_gameStep(uint32_t time) LCR_game.state != LCR_GAME_STATE_RUN_FINISHED) { LCR_LOG1("finished"); -LCR_replayOutputStr(_LCR_gameDataCharWrite); + + if (LCR_game.runTimeMS <= LCR_currentMap.targetTime * LCR_RACING_TICK_MS) + LCR_gameSaveReplay(); LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_gameSetState(LCR_GAME_STATE_RUN_FINISHED); diff --git a/map.h b/map.h index a759180..768feb1 100644 --- a/map.h +++ b/map.h @@ -164,7 +164,7 @@ struct uint8_t checkpointCount; uint32_t hash; ///< Hash of the processed binary map. - uint32_t targetTime; + uint32_t targetTime; ///< Target time in physics ticks. char name[LCR_MAP_NAME_MAX_LEN + 1]; } LCR_currentMap; diff --git a/racing.h b/racing.h index 2d3231b..79a3d1e 100644 --- a/racing.h +++ b/racing.h @@ -30,7 +30,7 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit -#define LCR_GAME_UNIT 2048 //1024 ///< length of map square in LCR_GameUnits +#define LCR_GAME_UNIT 2048 ///< length of map square in LCR_GameUnits #define LCR_RACING_INPUT_FORW 0x01 #define LCR_RACING_INPUT_RIGHT 0x02