Add time to replays

This commit is contained in:
Miloslav Ciz 2025-01-15 21:56:06 +01:00
parent 9d2c6108b1
commit 8d0adf66eb
2 changed files with 44 additions and 13 deletions

View file

@ -1,12 +1,13 @@
=========== GENERAL ============== =========== GENERAL ==============
- replay validation
- add time slow down setting - add time slow down setting
- the horizon on background seems too low? maybe add setting to shift it a bit? - the horizon on background seems too low? maybe add setting to shift it a bit?
- add argc/argv to gameInit? could be used to quickly start maps, verify - add argc/argv to gameInit? could be used to quickly start maps, verify
replays etc. replays etc.
- maybe each map could have a target time embedded: when beaten, the map would - maybe each map could have a target time embedded: when beaten, the map would
be marked as such be marked as such
- player name (modifyable via resource file) - player name (modifiable via resource file)
- popup messages? would be useful for several things: showing checkpoint times, - popup messages? would be useful for several things: showing checkpoint times,
showing changes in menu etc. showing changes in menu etc.
- make the racing module usable by itself, e.g. to allow making tools for - make the racing module usable by itself, e.g. to allow making tools for
@ -17,7 +18,6 @@
lower memory) lower memory)
- at the end check error handling, make sure the game handles garbage data in - at the end check error handling, make sure the game handles garbage data in
resource file etc. resource file etc.
- replay format
=========== BUGS ================= =========== BUGS =================
@ -27,6 +27,7 @@
=========== HANDLED ============== =========== HANDLED ==============
- allow stopping car rotation in air like in Trackmania - allow stopping car rotation in air like in Trackmania
- replay format
- prevent time overflow! stop incrementing level frame once it's at maximum - prevent time overflow! stop incrementing level frame once it's at maximum
- car shadow? probably would have to be done as screen space effect with - car shadow? probably would have to be done as screen space effect with
z-buffer (shadow as 3D model would require collision detection and would make z-buffer (shadow as 3D model would require collision detection and would make

View file

@ -13,13 +13,14 @@
because the offset is too big (input didn't change for more than 2^12 because the offset is too big (input didn't change for more than 2^12
frames), there must simply be inserted an extra word that just copies the frames), there must simply be inserted an extra word that just copies the
current input state. current input state.
- Replay text format: first there is hexadeciaml hash of the map (exactly 8 - Replay text format: first there is the name of the map terminated by ';',
characters), then the name of the map follows immediately, then the then hexadecimal hash of the map follows (exactly 8 characters), then
character ';', then the replay data, i.e. the sries of 16 bit words in blank character follows, then achieved time as a series of decimal digits
hexadecimal. The blocks (but nothing else) may be preceeded or followed by expressing the number of milliseconds, then a non-decimal character follows,
blank characters. All hexadecimal letters must be lowercase. The word then the replay data, i.e. the series of 16 bit words in hexadecimal. The
00000000 may optinally be used to terminate the replay, the rest of the blocks (but nothing else) may be preceeded or followed by blank characters.
string will be ignored. All hexadecimal letters must be lowercase. The word 00000000 may optinally
be used to terminate the replay, the rest of the string will be ignored.
*/ */
typedef int32_t LCR_GameUnit; ///< abstract game unit typedef int32_t LCR_GameUnit; ///< abstract game unit
@ -75,6 +76,8 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit
#define LCR_CAR_JOINTS 5 #define LCR_CAR_JOINTS 5
#define LCR_CAR_CONNECTIONS 10 #define LCR_CAR_CONNECTIONS 10
#define LCR_REPLAY_EVENT_END 0xff ///< special event fed to replay at the end
struct struct
{ {
TPE_World physicsWorld; TPE_World physicsWorld;
@ -114,6 +117,7 @@ struct
// for playing // for playing
uint16_t currentEvent; uint16_t currentEvent;
uint16_t currentFrame; uint16_t currentFrame;
uint32_t achievedTime;
} LCR_replay; } LCR_replay;
/** /**
@ -137,6 +141,7 @@ void LCR_replayInitRecording(void)
{ {
LCR_LOG1("initializing replay recording"); LCR_LOG1("initializing replay recording");
LCR_replay.eventCount = 0; LCR_replay.eventCount = 0;
LCR_replay.achievedTime = 0;
} }
void LCR_replayInitPlaying(void) void LCR_replayInitPlaying(void)
@ -172,6 +177,15 @@ void LCR_replayOutputStr(void (*printChar)(char))
hash /= 16; hash /= 16;
} }
printChar(' ');
// 8 decimal digits are enough to record 24 hours
#define PUTD(order) printChar('0' + (LCR_replay.achievedTime / order) % 10);
PUTD(10000000) PUTD(1000000) PUTD(100000) PUTD(10000)
PUTD(1000) PUTD(100) PUTD(10) PUTD(1)
#undef PUTD
for (int i = 0; i < LCR_replay.eventCount; ++i) for (int i = 0; i < LCR_replay.eventCount; ++i)
{ {
uint16_t e = LCR_replay.events[i]; uint16_t e = LCR_replay.events[i];
@ -179,8 +193,8 @@ void LCR_replayOutputStr(void (*printChar)(char))
for (int j = 0; j < 4; ++j) for (int j = 0; j < 4; ++j)
{ {
printChar(_LCR_hexDigit(e % 16)); printChar(_LCR_hexDigit((e >> 12) & 0x0f));
e /= 16; e <<= 4;
} }
} }
@ -216,12 +230,23 @@ int LCR_replayHasFinished(void)
/** /**
Records another input event. Returns 1 on success, or 0 if the event couldn't Records another input event. Returns 1 on success, or 0 if the event couldn't
be recorded. The event is only added if necessary, i.e. this function can be recorded. The event is only added if necessary, i.e. this function can
safely be called every frame without worrying about inflating its size. (and must) be called every frame without worrying about inflating its size.
When the run ends, the LCR_REPLAY_EVENT_END has to be fed!
*/ */
int LCR_replayRecordEvent(uint32_t frame, uint8_t input) int LCR_replayRecordEvent(uint32_t frame, uint8_t input)
{ {
LCR_LOG2("recording replay event"); LCR_LOG2("recording replay event");
if (LCR_replay.achievedTime)
return 1; // already finished
if (input == LCR_REPLAY_EVENT_END)
{
LCR_replay.achievedTime = frame;
LCR_LOG1("replay recording finished");
return 1;
}
#if LCR_SETTING_REPLAY_MAX_SIZE > 0 #if LCR_SETTING_REPLAY_MAX_SIZE > 0
uint8_t previousInput = 0; uint8_t previousInput = 0;
uint32_t previousFrame = 0; uint32_t previousFrame = 0;
@ -1363,7 +1388,12 @@ uint32_t LCR_racingStep(unsigned int input)
} }
if (valid) if (valid)
{
result |= LCR_RACING_EVENT_FINISHED; result |= LCR_RACING_EVENT_FINISHED;
if (!LCR_racing.playingReplay)
LCR_replayRecordEvent(LCR_racing.tick,LCR_REPLAY_EVENT_END);
}
} }
} }