Add replay validation

This commit is contained in:
Miloslav Ciz 2025-06-11 22:25:29 +02:00
parent f0d8521e90
commit 57097c57bb
3 changed files with 78 additions and 20 deletions

View file

@ -2,6 +2,7 @@ fuck issue trackers :D
=========== GENERAL ==============
- menu: key repeat?
- controller supports? analog input could be "tapping" the keys with varying
frequency
- frontends:
@ -44,6 +45,7 @@ fuck issue trackers :D
=========== HANDLED ==============
- should drifting make a sound? NO NEED
- replay validation? maybe yes?
- ghost color
- make reverse maps
- remake TM maps in the third party repo
@ -71,8 +73,6 @@ fuck issue trackers :D
again (reshape iterations, tension, ...); seem acceptable now maybe?
- in tiny resolution the sky jumps when rotating
- car particles seem too big in low res
- replay validation? MAYBE NOT, make a separate tool if needed, shouldn't likely
be part of the game
- at high resolution (like 1920) buggy triangles sometimes appeard, tried to
fix this in S3L now but needs to be tested
- shift the car texture a bit to align the wheels? KINDA DOESN'T GETT BETTER NOW

18
game.h
View file

@ -853,8 +853,8 @@ uint8_t LCR_gameLoadMap(unsigned int mapIndex)
/**
Loads replay by its index, returns index of a map for the replay (and the map
will be loaded as with LCR_mapLoadFromStr) or -1 if the map wasn't found or -2
if the replay couldn't be loaded. This function potentially reloads current
map!
if the replay couldn't be loaded or -3 if the replay is invalid. This function
potentially reloads current map!
*/
unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
{
@ -900,7 +900,7 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
LCR_LOG2("map name hash matches");
if (LCR_gameLoadMap(mapIndex) && mapHash == LCR_currentMap.hash)
return mapIndex;
return LCR_replayValidate() ? mapIndex : -3;
else
{
LCR_LOG2("bad map");
@ -1243,7 +1243,7 @@ void LCR_gameDraw3DView(void)
LCR_GameUnit carTransform[6];
LCR_GameUnit physicsInterpolationParam =
!(LCR_racing.playingReplay && LCR_replayHasFinished()) ?
!(LCR_racing.replay.on && LCR_replayHasFinished()) ?
LCR_GAME_UNIT -
((LCR_game.nextRacingTickTime - LCR_game.time) * LCR_GAME_UNIT)
/ LCR_RACING_TICK_MS
@ -1388,7 +1388,7 @@ void LCR_gameHandleInput(void)
LCR_currentMap.targetTime = LCR_game.runTime;
}
LCR_gameResetRun(LCR_racing.playingReplay,LCR_game.ghost.active);
LCR_gameResetRun(LCR_racing.replay.on,LCR_game.ghost.active);
}
}
else if (LCR_game.state == LCR_GAME_STATE_RUN_STARTING)
@ -1448,7 +1448,7 @@ void LCR_gameHandleInput(void)
LCR_rendererMoveCamera(offsets,offsets + 3);
}
else if (LCR_game.keyStates[LCR_KEY_A] == 1)
LCR_gameResetRun(LCR_racing.playingReplay,LCR_game.ghost.active);
LCR_gameResetRun(LCR_racing.replay.on,LCR_game.ghost.active);
}
else // LCR_GAME_STATE_MENU
{
@ -1549,7 +1549,7 @@ void LCR_gameHandleInput(void)
case 3:
if (LCR_game.statePrev == LCR_GAME_STATE_RUN_FINISHED &&
!LCR_racing.playingReplay)
!LCR_racing.replay.on)
LCR_gameSaveReplay();
else
LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_FAIL]);
@ -1719,12 +1719,12 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_LOG1_NUM(LCR_game.runTime);
if (LCR_game.runTime <= LCR_currentMap.targetTime &&
!LCR_racing.playingReplay)
!LCR_racing.replay.on)
{
LCR_gameSaveReplay();
if (!LCR_game.mapBeaten && !LCR_game.ghost.active &&
!LCR_racing.playingReplay)
!LCR_racing.replay.on)
{
LCR_LOG1("map beaten");
LCR_game.mapBeaten = 1;

View file

@ -168,10 +168,9 @@ struct
uint16_t crashState;
uint8_t playingReplay;
struct
{
uint8_t on; ///< Currently playing replay?
uint16_t eventCount;
uint16_t events[LCR_SETTING_REPLAY_MAX_SIZE];
@ -376,7 +375,7 @@ int LCR_replayHasFinished(void)
for (int i = 0; i < LCR_racing.replay.eventCount; ++i)
totalTime += LCR_racing.replay.events[i] >> 4;
return totalTime >= LCR_racing.replay.achievedTime;
return totalTime > LCR_racing.replay.achievedTime;
}
return LCR_racing.replay.currentEvent > LCR_racing.replay.eventCount;
@ -445,7 +444,8 @@ int LCR_replayRecordEvent(uint32_t frame, uint8_t input)
frame -= previousFrame; // convert to offset
while (frame > 4095 && LCR_racing.replay.eventCount < LCR_SETTING_REPLAY_MAX_SIZE)
while (frame > 4095 && LCR_racing.replay.eventCount <
LCR_SETTING_REPLAY_MAX_SIZE)
{
// add intermediate events
frame -= 4095;
@ -982,7 +982,7 @@ void LCR_racingRestart(uint8_t replay)
LCR_racing.tick = 0;
LCR_racing.fanForce = 0;
LCR_racing.playingReplay = replay;
LCR_racing.replay.on = replay;
TPE_bodyActivate(&(LCR_racing.carBody));
LCR_racing.wheelCollisions = 0;
@ -1066,7 +1066,7 @@ void LCR_racingInit(void)
TPE_worldInit(&(LCR_racing.physicsWorld),
&(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction);
LCR_racing.playingReplay = 0;
LCR_racing.replay.on = 0;
LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler;
}
@ -1217,7 +1217,7 @@ uint32_t LCR_racingStep(unsigned int input)
LCR_racing.groundMaterial = LCR_BLOCK_MATERIAL_CONCRETE;
if (LCR_racing.playingReplay)
if (LCR_racing.replay.on)
{
if (LCR_racing.tick == 0)
LCR_replayInitPlaying();
@ -1637,7 +1637,7 @@ uint32_t LCR_racingStep(unsigned int input)
{
result |= LCR_RACING_EVENT_FINISHED;
if (!LCR_racing.playingReplay)
if (!LCR_racing.replay.on)
LCR_replayRecordEvent(LCR_racing.tick,LCR_REPLAY_EVENT_END);
}
}
@ -1677,5 +1677,63 @@ void LCR_physicsDebugDraw(LCR_GameUnit camPos[3], LCR_GameUnit camRot[2],
cPos,cRot,cView,16,LCR_PHYSICS_UNIT / 4,LCR_racing.tick * 4);
#endif
}
/**
Validates replay, i.e. checks if it finishes and whether its achieved time
agrees with the stored time. The function can only be called when both the
replay and the correct map are loaded. The loaded replay's playing position
will be reset after this function returns.
*/
int LCR_replayValidate(void)
{
LCR_LOG1("validating replay");
int result = 0;
LCR_racingRestart(1);
while (LCR_racing.tick < 0x00f00000 // no infinite loops
&& LCR_racing.tick <= LCR_racing.replay.achievedTime)
{
int coords[3];
LCR_racingStep(0);
LCR_racingGetCarBlockCoords(coords);
coords[0] = LCR_mapGetBlockAt(coords[0],coords[1],coords[2]);
if (coords[0] && LCR_currentMap.blocks[coords[0] * LCR_BLOCK_SIZE] ==
LCR_BLOCK_FINISH)
{
uint8_t allCPsTaken = 1; // reuse as variable
for (int i = 0; i < LCR_currentMap.blockCount; ++i)
if (LCR_currentMap.blocks[i * LCR_BLOCK_SIZE] == LCR_BLOCK_CHECKPOINT_0)
{
allCPsTaken = 0;
break;
}
if (allCPsTaken)
{
result = LCR_racing.tick == (LCR_racing.replay.achievedTime + 1);
break;
}
}
}
if (result)
{
LCR_LOG1("replay valid");
}
else
{
LCR_LOG1("replay invalid!");
}
LCR_racingRestart(1);
return result;
}
#endif // guard