Licar mod for making TAS (tool assisted speedruns). This is a simple mod that mainly adds one feature: instead of completely restarting a map, the restart key rather rewinds time a little back. This allows to retry any part of a map as many times as one desires. Loading a replay for viewing also loads the inputs from the replay so that it's possible to reuse a start portion of another run. It helps to slow down time in the settings and drive in slow motion. diff --git a/assets.h b/assets.h index 4629aaa..c9449f5 100644 --- a/assets.h +++ b/assets.h @@ -36,7 +36,7 @@ static const char *LCR_texts[] = { #define LCR_TEXTS_VERSION 0 - LCR_VERSION, // version string + LCR_VERSION " TAS mod", // version string #define LCR_TEXTS_TABS 1 "main menu", "play map", diff --git a/game.h b/game.h index b1db32c..1743707 100644 --- a/game.h +++ b/game.h @@ -344,6 +344,9 @@ struct uint32_t runTime; ///< Current time of the run, in ticks. uint8_t mapBeaten; + uint8_t tasInputs[LCR_SETTING_TAS_MAX_INPUTS / 2 + 1]; + unsigned int tasInputCount; + char popupStr[LCR_POPUP_STR_SIZE]; uint8_t popupCountdown; @@ -504,8 +507,36 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost) LCR_game.ghost.active = ghost; #endif - LCR_gameSetState(LCR_GAME_STATE_RUN_STARTING); LCR_game.runTime = 0; + + if (!LCR_racing.replay.on) + { + LCR_game.tasInputCount = LCR_game.tasInputCount >= LCR_SETTING_TAS_REWIND ? + LCR_game.tasInputCount - LCR_SETTING_TAS_REWIND : 0; + + for (unsigned int i = 0; i < LCR_game.tasInputCount; ++i) + { + if (LCR_racingStep((LCR_game.tasInputs[i / 2] >> ((i % 2) * 4)) & 0x0f) + == LCR_RACING_EVENT_CP_TAKEN) + { + int carBlock[3]; + LCR_racingGetCarBlockCoords(carBlock); + LCR_rendererMarkTakenCP(carBlock[0],carBlock[1],carBlock[2]); + } + + LCR_game.runTime = LCR_racing.tick; + } + + LCR_gameSetState(LCR_GAME_STATE_RUN); + LCR_racingGetCarTransform(carTransform,carTransform + 3,0); + LCR_rendererSetCarTransform(carTransform,carTransform + 3); + LCR_rendererCameraReset(); + LCR_rendererLoadMapChunks(); + LCR_game.nextRenderFrameTime = LCR_game.time + 1000 / LCR_SETTING_FPS; + LCR_game.nextRacingTickTime = LCR_game.time + LCR_RACING_TICK_MS_RT; + } + else + LCR_gameSetState(LCR_GAME_STATE_RUN_STARTING); } void LCR_gameGetNthGhostSample(unsigned int n, @@ -867,6 +898,7 @@ uint8_t LCR_gameLoadMap(unsigned int mapIndex) result = LCR_mapLoadFromStr(LCR_gameGetNextDataStrChar,name); LCR_game.mapBeaten = LCR_mapIsBeaten(LCR_currentMap.name); + LCR_game.tasInputCount = 0; return result; } @@ -1708,6 +1740,23 @@ void LCR_gameHandleInput(void) } } +void LCR_gameAddTASInput(uint8_t input) +{ + if (LCR_game.tasInputCount >= LCR_SETTING_TAS_MAX_INPUTS) + { + LCR_LOG1("maximum TAS inputs reached!"); + } + else + { + uint8_t *inputRec = LCR_game.tasInputs + LCR_game.tasInputCount / 2; + + *inputRec = (LCR_game.tasInputCount % 2) ? + *inputRec | ((input & 0x0f) << 4) : (input & 0x0f); + + LCR_game.tasInputCount++; + } +} + uint8_t LCR_gameStep(uint32_t time) { LCR_LOG2("game step (start)"); @@ -1742,9 +1791,21 @@ uint8_t LCR_gameStep(uint32_t time) LCR_gameSetState(LCR_GAME_STATE_LOADED); } else if (LCR_game.state == LCR_GAME_STATE_LOADED) - LCR_gameResetRun( // start countdown now, loading the map lost many frames - LCR_game.menu.selectedTab == 2, - LCR_game.menu.selectedTab == 3); + { + if (LCR_game.menu.selectedTab == 2) // view replay? => load TAS inputs + { + LCR_replayInitPlaying(); + LCR_game.tasInputCount = 0; + + while (!LCR_replayHasFinished()) + LCR_gameAddTASInput(LCR_replayGetNextInput()); + + LCR_game.menu.selectedTab = 3; + _LCR_gamePrepareGhost(); + } + + LCR_gameResetRun(0,LCR_game.menu.selectedTab == 3); + } else { LCR_gameHandleInput(); @@ -1765,6 +1826,9 @@ uint8_t LCR_gameStep(uint32_t time) (LCR_game.keyStates[LCR_KEY_DOWN] ? LCR_RACING_INPUT_BACK : 0) | (LCR_game.keyStates[LCR_KEY_LEFT] ? LCR_RACING_INPUT_LEFT : 0)); + if (!paused && !LCR_racing.replay.on) + LCR_gameAddTASInput(input); + #ifdef LCR_FPS_GET_MS frameTime = LCR_FPS_GET_MS; #endif diff --git a/settings.h b/settings.h index 332df45..b9b3aba 100644 --- a/settings.h +++ b/settings.h @@ -296,4 +296,14 @@ #define LCR_SETTING_ONLY_SMALL_MAPS 0 #endif +#ifndef LCR_SETTING_TAS_MAX_INPUTS + /** Maximum possible length of a run in the TAS mod, in physics ticks. */ + #define LCR_SETTING_TAS_MAX_INPUTS 4096 +#endif + +#ifndef LCR_SETTING_TAS_REWIND + /** Length of TAS rewind, in physics ticks. */ + #define LCR_SETTING_TAS_REWIND 20 +#endif + #endif // guard