Improve TAS mod

This commit is contained in:
Miloslav Ciz 2025-07-03 00:35:59 +02:00
parent 57df4750be
commit 0231cd25c5

View file

@ -3,13 +3,14 @@ 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.
Additionally there is more information shown in HUD and the possibility to set
slow motion levels in main menu.
diff --git a/assets.h b/assets.h
index 4629aaa..c9449f5 100644
index 06b853c..a8edf18 100644
--- a/assets.h
+++ b/assets.h
@@ -36,7 +36,7 @@
@@ -36,23 +36,24 @@
static const char *LCR_texts[] =
{
#define LCR_TEXTS_VERSION 0
@ -18,11 +19,32 @@ index 4629aaa..c9449f5 100644
#define LCR_TEXTS_TABS 1
"main menu",
"play map",
- "view repl",
+ "load repl",
"race repl",
#define LCR_TEXTS_MAIN_MENU 5
"camera",
"music",
"sound",
"save repl",
+ "slowmo",
"exit",
-#define LCR_TEXTS_LOADING 10
+#define LCR_TEXTS_LOADING 11
"loading",
-#define LCR_TEXTS_SAVED 11
+#define LCR_TEXTS_SAVED 12
"saved",
-#define LCR_TEXTS_FAIL 12
+#define LCR_TEXTS_FAIL 13
"failed"
};
diff --git a/game.h b/game.h
index b1db32c..1743707 100644
index 1ad5131..f407ef4 100644
--- a/game.h
+++ b/game.h
@@ -344,6 +344,9 @@ struct
@@ -344,9 +344,16 @@ struct
uint32_t runTime; ///< Current time of the run, in ticks.
uint8_t mapBeaten;
@ -32,7 +54,37 @@ index b1db32c..1743707 100644
char popupStr[LCR_POPUP_STR_SIZE];
uint8_t popupCountdown;
@@ -504,8 +507,36 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost)
+ uint16_t racingTickRT;
+
+ int carSpeeds[2]; ///< Current and previous tick speed.
+
struct
{
uint8_t selectedTab;
@@ -441,8 +448,10 @@ void LCR_gamePopupMessage(const char *str)
void LCR_gamePopupNumber(uint8_t num)
{
- LCR_game.popupStr[0] = '0' + num;
- LCR_game.popupStr[1] = 0;
+ LCR_game.popupStr[3] = 0;
+ LCR_game.popupStr[2] = '0' + num % 10;
+ LCR_game.popupStr[1] = num > 9 ? '0' + (num / 10) % 10 : ' ';
+ LCR_game.popupStr[0] = num > 99 ? '0' + num / 100 : ' ';
LCR_gamePopupMessage(LCR_game.popupStr);
}
@@ -489,6 +498,9 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost)
LCR_LOG0("resetting run");
LCR_mapReset();
+ LCR_game.carSpeeds[0] = 0;
+ LCR_game.carSpeeds[1] = 0;
+
LCR_racingRestart(replay);
LCR_rendererUnmarkCPs();
LCR_racingGetCarTransform(carTransform,carTransform + 3,0);
@@ -504,8 +516,36 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost)
LCR_game.ghost.active = ghost;
#endif
@ -63,14 +115,14 @@ index b1db32c..1743707 100644
+ 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;
+ LCR_game.nextRacingTickTime = LCR_game.time + LCR_game.racingTickRT;
+ }
+ 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)
@@ -872,6 +912,7 @@ uint8_t LCR_gameLoadMap(unsigned int mapIndex)
result = LCR_mapLoadFromStr(LCR_gameGetNextDataStrChar,name);
LCR_game.mapBeaten = LCR_mapIsBeaten(LCR_currentMap.name);
@ -78,7 +130,212 @@ index b1db32c..1743707 100644
return result;
}
@@ -1708,6 +1740,23 @@ void LCR_gameHandleInput(void)
@@ -967,14 +1008,14 @@ void LCR_gameEraseMenuItemNames(void)
void LCR_gameLoadMainMenuItems(void)
{
- for (int j = 0; j < 5; ++j)
+ for (int j = 0; j < 6; ++j)
for (int i = 0; i < LCR_MENU_STRING_SIZE - 1; ++i)
{
LCR_game.menu.itemNames[j][i] = LCR_texts[LCR_TEXTS_MAIN_MENU + j][i];
LCR_game.menu.itemNames[j][i + 1] = 0;
}
- LCR_game.menu.itemCount = 5;
+ LCR_game.menu.itemCount = 6;
}
#define LCR_GAME_DATA_FILE_BUFFER_SIZE 32
@@ -1094,6 +1135,8 @@ void LCR_gameInit(int argc, const char **argv)
LCR_game.dataFile.state = 0;
+ LCR_game.racingTickRT = LCR_RACING_TICK_MS_RT;
+
for (int i = 0; i < LCR_MENU_MAX_ITEMS; ++i)
LCR_game.menu.itemNamePtrs[i] = LCR_game.menu.itemNames[i];
@@ -1231,22 +1274,30 @@ void LCR_gameEnd(void)
void LCR_gameTimeToStr(uint32_t timeMS, char *str)
{
- str[9] = 0;
- str[8] = '0' + timeMS % 10; // milliseconds
+ uint32_t ticks = timeMS / LCR_RACING_TICK_MS;
+
+ str[12] = 0;
+ str[11] = '0' + timeMS % 10; // milliseconds
timeMS /= 10;
- str[7] = '0' + timeMS % 10;
+ str[10] = '0' + timeMS % 10;
timeMS /= 10;
- str[6] = '0' + timeMS % 10;
+ str[9] = '0' + timeMS % 10;
timeMS /= 10;
- str[5] = '\'';
- str[4] = '0' + timeMS % 10; // seconds
+ str[8] = '0' + timeMS % 10; // seconds
timeMS /= 10;
- str[3] = '0' + timeMS % 6;
- str[2] = '\'';
- timeMS /= 6;
- str[1] = '0' + timeMS % 10; // minutes
+ str[7] = '0' + timeMS % 10;
timeMS /= 10;
- str[0] = '0' + timeMS % 10;
+ str[6] = '0' + timeMS % 10;
+ str[5] = '\'';
+ str[4] = '0' + ticks % 10;
+ ticks /= 10;
+ str[3] = '0' + ticks % 10;
+ ticks /= 10;
+ str[2] = '0' + ticks % 10;
+ ticks /= 10;
+ str[1] = '0' + ticks % 10;
+ ticks /= 10;
+ str[0] = '0' + ticks % 10;
}
void LCR_gameDrawPopupMessage(void)
@@ -1280,7 +1331,7 @@ void LCR_gameDraw3DView(void)
#endif
? LCR_GAME_UNIT -
(((int) (LCR_game.nextRacingTickTime - LCR_game.time)) * LCR_GAME_UNIT)
- / LCR_RACING_TICK_MS_RT // 32: magic constant
+ / LCR_game.racingTickRT // 32: magic constant
: _LCR_min(LCR_GAME_UNIT,32 * ((int) (LCR_game.time -
LCR_game.stateStartTime)));
@@ -1331,25 +1382,67 @@ void LCR_gameDraw3DView(void)
#if LCR_SETTING_DISPLAY_HUD
// GUI/HUD:
+
+ char str[16];
- char str[10];
- int val = LCR_carSpeedKMH();
+ for (int i = 0; i < 3; ++i)
+ {
+ int val = i == 2 ? (100 * LCR_racing.driftFriction) /
+ LCR_CAR_DRIFT_THRESHOLD_1 : LCR_game.carSpeeds[0];
- if (val < 5) // don't show tiny oscillations when still
- val = 0;
+ if (i == 1)
+ val -= LCR_game.carSpeeds[1];
+ else if (i == 0 && val < 5) // don't show tiny speed oscillations when still
+ val = 0;
- str[0] = val >= 100 ? '0' + (val / 100) % 10 : ' ';
- str[1] = val >= 10 ? '0' + (val / 10) % 10 : ' ';
- str[2] = '0' + val % 10;
- str[3] = 0;
+ str[0] = ' ';
- #define _FONT_SIZE (1 + (LCR_EFFECTIVE_RESOLUTION_Y > 96))
+ if (val < 0)
+ {
+ str[0] = '-';
+ val *= -1;
+ }
- LCR_rendererDrawText(str,LCR_EFFECTIVE_RESOLUTION_X - // speed (bot., right)
- LCR_rendererComputeTextWidth(str,_FONT_SIZE) - LCR_GUI_GAP,
- LCR_EFFECTIVE_RESOLUTION_Y - LCR_rendererComputeTextHeight(_FONT_SIZE) -
- LCR_GUI_GAP,0,_FONT_SIZE);
+ str[1] = val >= 100 ? '0' + (val / 100) % 10 : ' ';
+ str[2] = val >= 10 ? '0' + (val / 10) % 10 : ' ';
+ str[3] = '0' + val % 10;
+ str[4] = 0;
+
+ #define _FONT_SIZE (1 + (LCR_EFFECTIVE_RESOLUTION_Y > 96))
+
+ LCR_rendererDrawText(str,LCR_EFFECTIVE_RESOLUTION_X -
+ LCR_rendererComputeTextWidth(str,_FONT_SIZE) - LCR_GUI_GAP,
+ LCR_EFFECTIVE_RESOLUTION_Y - (i + 1) *
+ (LCR_rendererComputeTextHeight(_FONT_SIZE) + LCR_GUI_GAP),
+ (i == 2 && LCR_racing.carDrifting) ? 0xf800 : 0,_FONT_SIZE);
+ if (i == 0)
+ {
+ str[0] = (LCR_racing.currentInputs & LCR_RACING_INPUT_LEFT) ? 'L' : '.';
+ str[1] = (LCR_racing.currentInputs & LCR_RACING_INPUT_BACK) ? 'D' : '.';
+ str[2] = (LCR_racing.currentInputs & LCR_RACING_INPUT_FORW) ? 'U' : '.';
+ str[3] = (LCR_racing.currentInputs & LCR_RACING_INPUT_RIGHT) ? 'R' : '.';
+ str[4] = 0;
+ }
+ else if (i == 1)
+ {
+ str[0] = '0' + LCR_racingCurrentGroundMaterial();
+ str[1] = (LCR_racing.wheelCollisions & 0x02) ? 'f' : '.';
+ str[2] = (LCR_racing.wheelCollisions & 0x01) ? 'f' : '.';
+ str[3] = (LCR_racing.wheelCollisions & 0x08) ? 'r' : '.';
+ str[4] = (LCR_racing.wheelCollisions & 0x04) ? 'r' : '.';
+ str[5] = 0;
+ }
+
+ if (i < 2)
+ LCR_rendererDrawText(str,LCR_EFFECTIVE_RESOLUTION_X -
+ LCR_rendererComputeTextWidth(str,_FONT_SIZE) - LCR_GUI_GAP,
+ LCR_EFFECTIVE_RESOLUTION_Y - (4 + i) *
+ (LCR_rendererComputeTextHeight(_FONT_SIZE) + LCR_GUI_GAP),0,_FONT_SIZE);
+ }
+
+
+/*
str[0] = (LCR_racing.currentInputs & LCR_RACING_INPUT_LEFT) ? 'L' : '.';
str[1] = (LCR_racing.currentInputs & LCR_RACING_INPUT_BACK) ? 'D' : '.';
str[2] = (LCR_racing.currentInputs & LCR_RACING_INPUT_FORW) ? 'U' : '.';
@@ -1358,8 +1451,9 @@ void LCR_gameDraw3DView(void)
LCR_rendererDrawText(str,LCR_EFFECTIVE_RESOLUTION_X -
LCR_rendererComputeTextWidth(str,_FONT_SIZE) - LCR_GUI_GAP,
- LCR_EFFECTIVE_RESOLUTION_Y - 2 *
+ LCR_EFFECTIVE_RESOLUTION_Y - 3 *
(LCR_rendererComputeTextHeight(_FONT_SIZE) + LCR_GUI_GAP),0,_FONT_SIZE);
+*/
LCR_gameTimeToStr(LCR_game.runTime * LCR_RACING_TICK_MS,str);
@@ -1384,7 +1478,7 @@ void LCR_gameDraw3DView(void)
void LCR_gameSaveReplay(void)
{
- char str[10];
+ char str[16];
LCR_LOG0("saving replay");
@@ -1581,7 +1675,7 @@ void LCR_gameHandleInput(void)
if (LCR_game.menu.selectedTab == 0)
{
- if (LCR_game.menu.selectedItem < 4)
+ if (LCR_game.menu.selectedItem < 5)
{
LCR_game.menu.selectedItem++;
LCR_audioPlaySound(LCR_SOUND_CLICK);
@@ -1647,6 +1741,17 @@ void LCR_gameHandleInput(void)
break;
case 4:
+ LCR_game.racingTickRT += LCR_RACING_TICK_MS / 2;
+
+ if (LCR_game.racingTickRT > LCR_RACING_TICK_MS * 10)
+ LCR_game.racingTickRT = LCR_RACING_TICK_MS;
+
+ LCR_gamePopupNumber(
+ (100 * LCR_RACING_TICK_MS ) / LCR_game.racingTickRT);
+
+ break;
+
+ case 5:
LCR_gameSetState(LCR_GAME_STATE_END);
break;
@@ -1713,6 +1818,23 @@ void LCR_gameHandleInput(void)
}
}
@ -102,7 +359,7 @@ index b1db32c..1743707 100644
uint8_t LCR_gameStep(uint32_t time)
{
LCR_LOG2("game step (start)");
@@ -1742,9 +1791,21 @@ uint8_t LCR_gameStep(uint32_t time)
@@ -1747,9 +1869,21 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_gameSetState(LCR_GAME_STATE_LOADED);
}
else if (LCR_game.state == LCR_GAME_STATE_LOADED)
@ -127,7 +384,7 @@ index b1db32c..1743707 100644
else
{
LCR_gameHandleInput();
@@ -1765,6 +1826,9 @@ uint8_t LCR_gameStep(uint32_t time)
@@ -1770,12 +1904,19 @@ 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));
@ -137,6 +394,100 @@ index b1db32c..1743707 100644
#ifdef LCR_FPS_GET_MS
frameTime = LCR_FPS_GET_MS;
#endif
+ LCR_game.carSpeeds[1] = LCR_game.carSpeeds[0];
+
uint32_t events = paused ? 0 : LCR_racingStep(input);
+ LCR_game.carSpeeds[0] = LCR_carSpeedKMH();
+
#if LCR_SETTING_PARTICLES
LCR_rendererSetParticles(0);
@@ -1801,7 +1942,7 @@ uint8_t LCR_gameStep(uint32_t time)
if (events & LCR_RACING_EVENT_CP_TAKEN)
{
int carBlock[3];
- char str[10];
+ char str[16];
LCR_LOG1("CP taken");
LCR_racingGetCarBlockCoords(carBlock);
@@ -1890,7 +2031,7 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_game.state == LCR_GAME_STATE_RUN_STARTING)
LCR_game.runTime = LCR_racing.tick;
- LCR_game.nextRacingTickTime += LCR_RACING_TICK_MS_RT;
+ LCR_game.nextRacingTickTime += LCR_game.racingTickRT;
}
// handle rendering:
diff --git a/racing.h b/racing.h
index 0824354..cee1c23 100644
--- a/racing.h
+++ b/racing.h
@@ -174,6 +174,8 @@ struct
uint16_t crashState;
+ TPE_Unit driftFriction;
+
#if LCR_SETTING_REPLAY_MAX_SIZE != 0
struct
{
@@ -1022,6 +1024,7 @@ void LCR_racingRestart(uint8_t replay)
LCR_racing.tick = 0;
LCR_racing.fanForce = 0;
+ LCR_racing.driftFriction = 0;
#if LCR_SETTING_REPLAY_MAX_SIZE > 0
LCR_racing.replay.on = replay;
@@ -1258,7 +1261,7 @@ uint32_t LCR_racingStep(unsigned int input)
TPE_Vec3 carForw, carRight, carUp, carVel;
uint8_t onAccel = 0; // standing on accelerator?
int groundBlockIndex = -1;
- TPE_Unit driftFriction = 0; // average wheel friction (absolute value)
+ LCR_racing.driftFriction = 0; // average wheel friction (absolute value)
LCR_racing.groundMaterial = LCR_BLOCK_MATERIAL_CONCRETE;
@@ -1484,7 +1487,7 @@ uint32_t LCR_racingStep(unsigned int input)
(LCR_CAR_STEER_FRICTION * LCR_CAR_DRIFT_FACTOR) / 8 :
LCR_CAR_STEER_FRICTION,LCR_racing.groundMaterial)) / TPE_F);
- driftFriction += TPE_vec3Len(fric);
+ LCR_racing.driftFriction += TPE_vec3Len(fric);
jv = TPE_vec3Minus(jv,fric); // subtract the friction
@@ -1493,7 +1496,7 @@ uint32_t LCR_racingStep(unsigned int input)
LCR_racing.carBody.joints[i].velocity[2] = jv.z;
}
- driftFriction /= 4; // divide by 4 wheels
+ LCR_racing.driftFriction /= 4; // divide by 4 wheels
if (steering &&
(input & (LCR_RACING_INPUT_FORW | LCR_RACING_INPUT_BACK)) &&
@@ -1510,7 +1513,7 @@ uint32_t LCR_racingStep(unsigned int input)
}
if ((!LCR_racing.carDrifting) &&
- driftFriction >
+ LCR_racing.driftFriction >
(LCR_CAR_DRIFT_THRESHOLD_1 >> (( // back key initiates drift easily
((input & (LCR_RACING_INPUT_FORW | LCR_RACING_INPUT_BACK))
== (LCR_RACING_INPUT_FORW | LCR_RACING_INPUT_BACK))) << 2)))
@@ -1519,7 +1522,7 @@ uint32_t LCR_racingStep(unsigned int input)
LCR_racing.carDrifting = 1;
}
else if (LCR_racing.carDrifting &&
- (driftFriction < LCR_CAR_DRIFT_THRESHOLD_0 ||
+ (LCR_racing.driftFriction < LCR_CAR_DRIFT_THRESHOLD_0 ||
LCR_racingGetCarSpeedUnsigned() < 5))
{
LCR_LOG1("drift end");
diff --git a/settings.h b/settings.h
index 332df45..b9b3aba 100644
--- a/settings.h