Compare commits

...

4 commits

Author SHA1 Message Date
74013219e5 Fix time multiplier 2025-06-12 16:54:56 +02:00
60cf16c8de Add key repeat 2025-06-11 22:40:00 +02:00
57097c57bb Add replay validation 2025-06-11 22:25:29 +02:00
f0d8521e90 Remove spaces from internal file 2025-06-11 18:32:45 +02:00
6 changed files with 676 additions and 602 deletions

View file

@ -2,10 +2,8 @@ fuck issue trackers :D
=========== GENERAL ============== =========== GENERAL ==============
- check again the target times for maps - controller supports? analog input could be "tapping" the keys with varying
- remake TM maps in the third party repo frequency
- make reverse maps
- ghost color
- frontends: - frontends:
- auto test frontend, with no I/O, that will just internally run a series of - auto test frontend, with no I/O, that will just internally run a series of
inputs and check if the output is as expected inputs and check if the output is as expected
@ -46,6 +44,12 @@ fuck issue trackers :D
=========== HANDLED ============== =========== HANDLED ==============
- should drifting make a sound? NO NEED - should drifting make a sound? NO NEED
- menu: key repeat?
- replay validation? maybe yes?
- ghost color
- make reverse maps
- remake TM maps in the third party repo
- check again the target times for maps
- redo the menu effect somehow - redo the menu effect somehow
- press forward map??? :-D only when physics is frozen - press forward map??? :-D only when physics is frozen
- devtest map, internal camera: reversing at start makes the car face completely - devtest map, internal camera: reversing at start makes the car face completely
@ -69,8 +73,6 @@ fuck issue trackers :D
again (reshape iterations, tension, ...); seem acceptable now maybe? again (reshape iterations, tension, ...); seem acceptable now maybe?
- in tiny resolution the sky jumps when rotating - in tiny resolution the sky jumps when rotating
- car particles seem too big in low res - 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 - at high resolution (like 1920) buggy triangles sometimes appeard, tried to
fix this in S3L now but needs to be tested 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 - shift the car texture a bit to align the wheels? KINDA DOESN'T GETT BETTER NOW

1109
assets.h

File diff suppressed because it is too large Load diff

View file

@ -29,7 +29,7 @@ const char *part1 = // big maps
":=C0j:f347" ":=C0j:f347"
":=H0q2:f42c" ":=H0q2:f42c"
":xB2k:f625" // start hole ":xB2k:f625" // start hole
":uB2o:uF2oJ :uB2kL :uF2kI" // curved corners in start ":uB2o:uF2oJ:uB2kL:uF2kI" // curved corners in start
":]G2kL:]G2oL" // decorative ramps near start ":]G2kL:]G2oL" // decorative ramps near start
":-w29:f41e" ":-w29:f41e"
":xH0r:f35b" // hole ":xH0r:f35b" // hole
@ -92,7 +92,7 @@ const char *part1 = // big maps
":xA1I:f912:xf0n:f715" ":xA1I:f912:xf0n:f715"
":xf0s:fc1a:xC0B:f419" ":xf0s:fc1a:xC0B:f419"
":xL0n:f32c:xf0N:fg23" ":xL0n:f32c:xf0N:fg23"
":-r1s:f418:=i0h2 :f63a" ":-r1s:f418:=i0h2:f63a"
":^n2m2L:f114:^l2q2I:f211:vn2q2J:An0q2L:f121:=l0m:m335" ":^n2m2L:f114:^l2q2I:f211:vn2q2J:An0q2L:f121:=l0m:m335"
":=n0q2:'n0C2L:f116:'o0C2J:f116" ":=n0q2:'n0C2L:f116:'o0C2J:f116"
":'l1I2L:f115:-m1I2:f215:'o1I2J:f115" ":'l1I2L:f115:-m1I2:f215:'o1I2J:f115"
@ -122,7 +122,7 @@ const char *part1 = // big maps
":<ubrJ:]uarL-:|u9rL-:|tarJ:]t9rJ" ":<ubrJ:]uarL-:|u9rL-:|tarJ:]t9rJ"
":=t8r:<u5rJ-:]u6rL:|u7rL:|t6rJ-:]t7rJ-" ":=t8r:<u5rJ-:]u6rL:|u7rL:|t6rJ-:]t7rJ-"
":<g1G2:f511:'g1H2:f511" // ramp left ":<g1G2:f511:'g1H2:f511" // ramp left
":<J1G2 :f511 :'J1H2 :f511" // ramp right ":<J1G2:f511:'J1H2:f511" // ramp right
// details: // details:
":vw1F1:f131:vx1F1|:f131:vw1E1L:f131:vx1E1|J:f131" // tree ":vw1F1:f131:vx1F1|:f131:vw1E1L:f131:vx1E1|J:f131" // tree
":~m1D2:~q1E2:~z2I2:~v2M2:~E2K2:~q1o2:~u1p2:~G1z:~H1E" // bumps ":~m1D2:~q1E2:~z2I2:~v2M2:~E2K2:~q1o2:~u1p2:~G1z:~H1E" // bumps
@ -142,7 +142,7 @@ const char *part1 = // big maps
":*qrt:!o1y:!o1z:+Wsz" ":*qrt:!o1y:!o1z:+Wsz"
// start box: // start box:
":=mqr:f96d:xnrs:f74b:^mvrJ:f11d:^uvrL:f11d" ":=mqr:f96d:xnrs:f74b:^mvrJ:f11d:^uvrL:f11d"
":~ouz3- :~suz3- :~ouw3- :~suw3-" // lights ":~ouz3-:~suz3-:~ouw3-:~suw3-" // lights
":^utuI-:^utv-:^utzI-:^utA-" ":^utuI-:^utv-:^utzI-:^utA-"
":'ustI:-usu:-usv:'usw" ":'ustI:-usu:-usv:'usw"
":'usyI:-usz:-usA:'usB" ":'usyI:-usz:-usA:'usB"
@ -390,7 +390,7 @@ const char *part2 = // small maps
- 1536 (0x600) triangles */ - 1536 (0x600) triangles */
// TINY MAP 1: // TINY MAP 1:
"#MLCtiny1;330 0 :*G1b:+n9H:!I1H" "#MLCtiny1;330 0:*G1b:+n9H:!I1H"
// start // start
":=E0b:f61i" ":=E0b:f61i"
":^D1bJ:f11i:^J1bL:f11i" ":^D1bJ:f11i:^J1bL:f11i"

3
data
View file

@ -53,7 +53,6 @@ details
:=h0h2 :fl12 :f11o :=B0i2 :f11m :=m0n2 :fb12 :=h0h2 :fl12 :f11o :=B0i2 :f11m :=m0n2 :fb12
:*n1EI :!n1F :+p1r :+y1l :+y3I :+k1l :*n1EI :!n1F :+p1r :+y1l :+y3I :+k1l
#MLCbonus2;1309 1 #MLCbonus2;1309 1
:*B1lJ :*B1lJ
:^y0oI :f611 :^y0oI :f611
:=w0k :f814 :=w0o :f2a1 :=y6c :f21d :=w0k :f814 :=w0o :f2a1 :=y6c :f21d
@ -130,7 +129,6 @@ details
:]h0gI :}g0gI :|g1gI :f211 :|g3gI- :f211 :]g4gI- :f211 :;g0fJ :f251 :]h0gI :}g0gI :|g1gI :f211 :|g3gI- :f211 :]g4gI- :f211 :;g0fJ :f251
:-e5g :f413 :|g0hL :-e5g :f413 :|g0hL
:+018 :f213 :!v05 :f311 :+018 :f213 :!v05 :f311
#Mempty;594 0
#MLCpressforw1;594 0 #MLCpressforw1;594 0
:*cac :*cac
press forward map, blocks go in the direction that car travels press forward map, blocks go in the direction that car travels
@ -208,4 +206,3 @@ air path
#Rdrummy LCbonus1 13'16;00LCbonus1;999b1acd 0000399:0001:0089:0051:0079:0021:0093:0031:0089:0031:0119:0091:0053:00f1:01a3:00d1:0099:0201:0073:0051:0063:0051:0059:0051:00d9:00b1:0089:0041:00a3:0031:02b9:0048:0020:0081:0149:00c1:0059:0031:0113:0051:0019:0078:0099:0041 #Rdrummy LCbonus1 13'16;00LCbonus1;999b1acd 0000399:0001:0089:0051:0079:0021:0093:0031:0089:0031:0119:0091:0053:00f1:01a3:00d1:0099:0201:0073:0051:0063:0051:0059:0051:00d9:00b1:0089:0041:00a3:0031:02b9:0048:0020:0081:0149:00c1:0059:0031:0113:0051:0019:0078:0099:0041
#Rdrummy LCbonus2 24'18;00LCbonus2;288d28fe 0000733:0001:0153:00d1:0023:0081:00d3:0041:0023:00a9:0128:0034:0036:00e4:005c:0054:0096:0044:007c:0024:0146:0112:0018:0029:00f1:0063:0061:00c3:0031:00c3:0031:0025:0031:0039:0081:0033:01d1:0083:0081:0073:0031:00e3:0071:00f9:0051:01b9:00c4:0040:0028:0069:0021:0043:00e1:0090:0046:00b0:0031:0043:0070:0024:0026:00e0:0018:0049:0018:0039:01a1:0039:0051:0049:0048:001c:0098:0050:0011:0039:0061:00d9:0041:00a9:0018:00c9:00e1:0083:0042:0010:0042:0093:00b1:0170:0022:0016:0042:0083:0141:0013:0021:0059:0041:0053 #Rdrummy LCbonus2 24'18;00LCbonus2;288d28fe 0000733:0001:0153:00d1:0023:0081:00d3:0041:0023:00a9:0128:0034:0036:00e4:005c:0054:0096:0044:007c:0024:0146:0112:0018:0029:00f1:0063:0061:00c3:0031:00c3:0031:0025:0031:0039:0081:0033:01d1:0083:0081:0073:0031:00e3:0071:00f9:0051:01b9:00c4:0040:0028:0069:0021:0043:00e1:0090:0046:00b0:0031:0043:0070:0024:0026:00e0:0018:0049:0018:0039:01a1:0039:0051:0049:0048:001c:0098:0050:0011:0039:0061:00d9:0041:00a9:0018:00c9:00e1:0083:0042:0010:0042:0093:00b1:0170:0022:0016:0042:0083:0141:0013:0021:0059:0041:0053
#Rdrummy LCpressforw1 21'31;00LCpressforw1;6363b43d 0000599:0001 #Rdrummy LCpressforw1 21'31;00LCpressforw1;6363b43d 0000599:0001

56
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 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 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 if the replay couldn't be loaded or -3 if the replay is invalid. This function
map! potentially reloads current map!
*/ */
unsigned int LCR_gameLoadReplay(unsigned int replayIndex) unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
{ {
@ -900,7 +900,7 @@ unsigned int LCR_gameLoadReplay(unsigned int replayIndex)
LCR_LOG2("map name hash matches"); LCR_LOG2("map name hash matches");
if (LCR_gameLoadMap(mapIndex) && mapHash == LCR_currentMap.hash) if (LCR_gameLoadMap(mapIndex) && mapHash == LCR_currentMap.hash)
return mapIndex; return LCR_replayValidate() ? mapIndex : -3;
else else
{ {
LCR_LOG2("bad map"); LCR_LOG2("bad map");
@ -1243,10 +1243,10 @@ void LCR_gameDraw3DView(void)
LCR_GameUnit carTransform[6]; LCR_GameUnit carTransform[6];
LCR_GameUnit physicsInterpolationParam = LCR_GameUnit physicsInterpolationParam =
!(LCR_racing.playingReplay && LCR_replayHasFinished()) ? !(LCR_racing.replay.on && LCR_replayHasFinished()) ?
LCR_GAME_UNIT - LCR_GAME_UNIT -
((LCR_game.nextRacingTickTime - LCR_game.time) * LCR_GAME_UNIT) ((LCR_game.nextRacingTickTime - LCR_game.time) * LCR_GAME_UNIT)
/ LCR_RACING_TICK_MS / LCR_RACING_TICK_MS_RT
: LCR_GAME_UNIT / 2; : LCR_GAME_UNIT / 2;
LCR_racingGetCarTransform(carTransform,carTransform + 3, LCR_racingGetCarTransform(carTransform,carTransform + 3,
@ -1353,7 +1353,8 @@ void LCR_gameSaveReplay(void)
LCR_gameTimeToStr(LCR_timeTicksToMS(LCR_game.runTime),str); LCR_gameTimeToStr(LCR_timeTicksToMS(LCR_game.runTime),str);
for (int i = 0; i < LCR_MAP_NAME_MAX_LEN; ++i) for (int i = (str[0] == '0' && str[1] == '0' ? 3 : 0);
i < LCR_MAP_NAME_MAX_LEN; ++i)
if (str[i]) if (str[i])
_LCR_gameDataCharWrite(str[i]); _LCR_gameDataCharWrite(str[i]);
else else
@ -1365,6 +1366,17 @@ void LCR_gameSaveReplay(void)
LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_SAVED]); LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_SAVED]);
} }
/**
Checks if given key is either immediately pressed or repeated after being
held for some time.
*/
uint8_t LCR_gameKeyActive(uint8_t key)
{
return LCR_game.keyStates[key] == 1 ||
(LCR_game.keyStates[key] >= (1200 / LCR_SETTING_FPS)
&& ((LCR_game.frame & 0x03) == 0));
}
/** /**
Helper subroutine, handles user input during main loop frame, EXCEPT for the Helper subroutine, handles user input during main loop frame, EXCEPT for the
driving input (that is handled in the loop itself). driving input (that is handled in the loop itself).
@ -1387,7 +1399,7 @@ void LCR_gameHandleInput(void)
LCR_currentMap.targetTime = LCR_game.runTime; 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) else if (LCR_game.state == LCR_GAME_STATE_RUN_STARTING)
@ -1447,7 +1459,7 @@ void LCR_gameHandleInput(void)
LCR_rendererMoveCamera(offsets,offsets + 3); LCR_rendererMoveCamera(offsets,offsets + 3);
} }
else if (LCR_game.keyStates[LCR_KEY_A] == 1) 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 else // LCR_GAME_STATE_MENU
{ {
@ -1469,7 +1481,7 @@ void LCR_gameHandleInput(void)
LCR_game.menu.selectedItem = 0; LCR_game.menu.selectedItem = 0;
LCR_audioPlaySound(LCR_SOUND_CLICK); LCR_audioPlaySound(LCR_SOUND_CLICK);
} }
else if (LCR_game.keyStates[LCR_KEY_UP] == 1) else if (LCR_gameKeyActive(LCR_KEY_UP))
{ {
LCR_LOG1("menu item up"); LCR_LOG1("menu item up");
@ -1486,7 +1498,7 @@ void LCR_gameHandleInput(void)
scrolled = -1; scrolled = -1;
} }
} }
else if (LCR_game.keyStates[LCR_KEY_DOWN] == 1) else if (LCR_gameKeyActive(LCR_KEY_DOWN))
{ {
LCR_LOG1("menu item down"); LCR_LOG1("menu item down");
@ -1548,7 +1560,7 @@ void LCR_gameHandleInput(void)
case 3: case 3:
if (LCR_game.statePrev == LCR_GAME_STATE_RUN_FINISHED && if (LCR_game.statePrev == LCR_GAME_STATE_RUN_FINISHED &&
!LCR_racing.playingReplay) !LCR_racing.replay.on)
LCR_gameSaveReplay(); LCR_gameSaveReplay();
else else
LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_FAIL]); LCR_gamePopupMessage(LCR_texts[LCR_TEXTS_FAIL]);
@ -1718,12 +1730,12 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_LOG1_NUM(LCR_game.runTime); LCR_LOG1_NUM(LCR_game.runTime);
if (LCR_game.runTime <= LCR_currentMap.targetTime && if (LCR_game.runTime <= LCR_currentMap.targetTime &&
!LCR_racing.playingReplay) !LCR_racing.replay.on)
{ {
LCR_gameSaveReplay(); LCR_gameSaveReplay();
if (!LCR_game.mapBeaten && !LCR_game.ghost.active && if (!LCR_game.mapBeaten && !LCR_game.ghost.active &&
!LCR_racing.playingReplay) !LCR_racing.replay.on)
{ {
LCR_LOG1("map beaten"); LCR_LOG1("map beaten");
LCR_game.mapBeaten = 1; LCR_game.mapBeaten = 1;
@ -1770,11 +1782,9 @@ uint8_t LCR_gameStep(uint32_t time)
if (LCR_game.state != LCR_GAME_STATE_RUN_FINISHED) if (LCR_game.state != LCR_GAME_STATE_RUN_FINISHED)
LCR_game.runTime = LCR_racing.tick; LCR_game.runTime = LCR_racing.tick;
LCR_game.nextRacingTickTime += LCR_RACING_TICK_MS; LCR_game.nextRacingTickTime += LCR_RACING_TICK_MS_RT;
} }
sleep = (3 * (LCR_game.nextRacingTickTime - time)) / 4;
// handle rendering: // handle rendering:
if (time >= LCR_game.nextRenderFrameTime || if (time >= LCR_game.nextRenderFrameTime ||
LCR_game.state == LCR_GAME_STATE_LOADING) LCR_game.state == LCR_GAME_STATE_LOADING)
@ -1810,11 +1820,6 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_game.renderFramesMeasured++; LCR_game.renderFramesMeasured++;
#endif #endif
} }
else
{
uint32_t tmp = (3 * (LCR_game.nextRenderFrameTime - time)) / 4;
sleep = tmp < sleep ? tmp : sleep;
}
if (LCR_game.state == LCR_GAME_STATE_LOADING) if (LCR_game.state == LCR_GAME_STATE_LOADING)
{ {
@ -1825,6 +1830,15 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_gameDrawPopupMessage(); LCR_gameDrawPopupMessage();
} }
if (LCR_game.nextRacingTickTime > time)
sleep = LCR_game.nextRenderFrameTime - time;
if (LCR_game.nextRenderFrameTime > time &&
LCR_game.nextRenderFrameTime - time < sleep)
sleep = LCR_game.nextRenderFrameTime - time;
sleep = (sleep * 3) / 4;
if (sleep) if (sleep)
LCR_sleep(sleep); LCR_sleep(sleep);
else else

View file

@ -56,7 +56,10 @@ typedef int32_t LCR_GameUnit; ///< Abstract game unit.
#define LCR_RACING_FPS 30 /**< Physics FPS, i.e. the number of #define LCR_RACING_FPS 30 /**< Physics FPS, i.e. the number of
physics ticks per second. */ physics ticks per second. */
#define LCR_RACING_TICK_MS \
#define LCR_RACING_TICK_MS (1000 / LCR_RACING_FPS)
#define LCR_RACING_TICK_MS_RT \
(100000 / (LCR_RACING_FPS * LCR_SETTING_TIME_MULTIPLIER)) (100000 / (LCR_RACING_FPS * LCR_SETTING_TIME_MULTIPLIER))
#define LCR_RACING_VERSION1 '0' ///< First part of physics eng. version. #define LCR_RACING_VERSION1 '0' ///< First part of physics eng. version.
@ -168,10 +171,9 @@ struct
uint16_t crashState; uint16_t crashState;
uint8_t playingReplay;
struct struct
{ {
uint8_t on; ///< Currently playing replay?
uint16_t eventCount; uint16_t eventCount;
uint16_t events[LCR_SETTING_REPLAY_MAX_SIZE]; uint16_t events[LCR_SETTING_REPLAY_MAX_SIZE];
@ -376,7 +378,7 @@ int LCR_replayHasFinished(void)
for (int i = 0; i < LCR_racing.replay.eventCount; ++i) for (int i = 0; i < LCR_racing.replay.eventCount; ++i)
totalTime += LCR_racing.replay.events[i] >> 4; 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; return LCR_racing.replay.currentEvent > LCR_racing.replay.eventCount;
@ -445,7 +447,8 @@ int LCR_replayRecordEvent(uint32_t frame, uint8_t input)
frame -= previousFrame; // convert to offset 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 // add intermediate events
frame -= 4095; frame -= 4095;
@ -982,7 +985,7 @@ void LCR_racingRestart(uint8_t replay)
LCR_racing.tick = 0; LCR_racing.tick = 0;
LCR_racing.fanForce = 0; LCR_racing.fanForce = 0;
LCR_racing.playingReplay = replay; LCR_racing.replay.on = replay;
TPE_bodyActivate(&(LCR_racing.carBody)); TPE_bodyActivate(&(LCR_racing.carBody));
LCR_racing.wheelCollisions = 0; LCR_racing.wheelCollisions = 0;
@ -1066,7 +1069,7 @@ void LCR_racingInit(void)
TPE_worldInit(&(LCR_racing.physicsWorld), TPE_worldInit(&(LCR_racing.physicsWorld),
&(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction); &(LCR_racing.carBody),1,_LCR_racingEnvironmentFunction);
LCR_racing.playingReplay = 0; LCR_racing.replay.on = 0;
LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler; LCR_racing.physicsWorld.collisionCallback = _LCR_racingCollisionHandler;
} }
@ -1202,8 +1205,9 @@ int _LCR_racingCarShapeOK(void)
} }
/** /**
Updates the racing physics world, call every LCR_RACING_TICK_MS milliseconds. Updates the racing physics world, call every LCR_RACING_TICK_MS_RT
Returns a set of events (logically ORed) that occured during this step. milliseconds. Returns a set of events (logically ORed) that occured during
this step.
*/ */
uint32_t LCR_racingStep(unsigned int input) uint32_t LCR_racingStep(unsigned int input)
{ {
@ -1217,7 +1221,7 @@ uint32_t LCR_racingStep(unsigned int input)
LCR_racing.groundMaterial = LCR_BLOCK_MATERIAL_CONCRETE; LCR_racing.groundMaterial = LCR_BLOCK_MATERIAL_CONCRETE;
if (LCR_racing.playingReplay) if (LCR_racing.replay.on)
{ {
if (LCR_racing.tick == 0) if (LCR_racing.tick == 0)
LCR_replayInitPlaying(); LCR_replayInitPlaying();
@ -1637,7 +1641,7 @@ uint32_t LCR_racingStep(unsigned int input)
{ {
result |= LCR_RACING_EVENT_FINISHED; result |= LCR_RACING_EVENT_FINISHED;
if (!LCR_racing.playingReplay) if (!LCR_racing.replay.on)
LCR_replayRecordEvent(LCR_racing.tick,LCR_REPLAY_EVENT_END); LCR_replayRecordEvent(LCR_racing.tick,LCR_REPLAY_EVENT_END);
} }
} }
@ -1678,4 +1682,62 @@ void LCR_physicsDebugDraw(LCR_GameUnit camPos[3], LCR_GameUnit camRot[2],
#endif #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 #endif // guard