Licar/mods/tas.diff

159 lines
4.7 KiB
Diff
Raw Normal View History

2025-06-26 15:39:44 +02:00
Licar mod for making TAS (tool assisted speedruns). This is a simple mod that
2025-06-29 15:16:52 +02:00
mainly adds one feature: instead of completely restarting a map, the restart key
2025-06-26 15:39:44 +02:00
rather rewinds time a little back. This allows to retry any part of a map as
2025-06-29 15:16:52 +02:00
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.
2025-06-26 15:39:44 +02:00
2025-06-29 15:16:52 +02:00
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",
2025-06-26 15:39:44 +02:00
diff --git a/game.h b/game.h
2025-06-29 15:16:52 +02:00
index b1db32c..1743707 100644
2025-06-26 15:39:44 +02:00
--- 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;
2025-06-29 15:16:52 +02:00
@@ -504,8 +507,36 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost)
2025-06-26 15:39:44 +02:00
LCR_game.ghost.active = ghost;
#endif
- LCR_gameSetState(LCR_GAME_STATE_RUN_STARTING);
LCR_game.runTime = 0;
+
+ if (!LCR_racing.replay.on)
+ {
2025-06-29 15:16:52 +02:00
+ LCR_game.tasInputCount = LCR_game.tasInputCount >= LCR_SETTING_TAS_REWIND ?
+ LCR_game.tasInputCount - LCR_SETTING_TAS_REWIND : 0;
2025-06-26 15:39:44 +02:00
+
+ 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;
2025-06-29 15:16:52 +02:00
+ LCR_game.nextRacingTickTime = LCR_game.time + LCR_RACING_TICK_MS_RT;
2025-06-26 15:39:44 +02:00
+ }
+ else
+ LCR_gameSetState(LCR_GAME_STATE_RUN_STARTING);
}
void LCR_gameGetNthGhostSample(unsigned int n,
2025-06-29 15:16:52 +02:00
@@ -867,6 +898,7 @@ uint8_t LCR_gameLoadMap(unsigned int mapIndex)
2025-06-26 15:39:44 +02:00
result = LCR_mapLoadFromStr(LCR_gameGetNextDataStrChar,name);
LCR_game.mapBeaten = LCR_mapIsBeaten(LCR_currentMap.name);
+ LCR_game.tasInputCount = 0;
return result;
}
2025-06-29 15:16:52 +02:00
@@ -1708,6 +1740,23 @@ void LCR_gameHandleInput(void)
}
}
2025-06-26 15:39:44 +02:00
2025-06-29 15:16:52 +02:00
+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;
2025-06-26 15:39:44 +02:00
+
2025-06-29 15:16:52 +02:00
+ *inputRec = (LCR_game.tasInputCount % 2) ?
+ *inputRec | ((input & 0x0f) << 4) : (input & 0x0f);
2025-06-26 15:39:44 +02:00
+
2025-06-29 15:16:52 +02:00
+ 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);
2025-06-26 15:39:44 +02:00
+
#ifdef LCR_FPS_GET_MS
frameTime = LCR_FPS_GET_MS;
#endif
diff --git a/settings.h b/settings.h
2025-06-29 15:16:52 +02:00
index 332df45..b9b3aba 100644
2025-06-26 15:39:44 +02:00
--- 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. */
2025-06-29 15:16:52 +02:00
+ #define LCR_SETTING_TAS_REWIND 20
2025-06-26 15:39:44 +02:00
+#endif
+
#endif // guard