diff --git a/TODO.txt b/TODO.txt index fa1b668..9eb8360 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,12 +1,13 @@ =========== GENERAL ============== +- replay validation - add time slow down setting - 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 replays etc. - maybe each map could have a target time embedded: when beaten, the map would 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, showing changes in menu etc. - make the racing module usable by itself, e.g. to allow making tools for @@ -17,7 +18,6 @@ lower memory) - at the end check error handling, make sure the game handles garbage data in resource file etc. -- replay format =========== BUGS ================= @@ -27,6 +27,7 @@ =========== HANDLED ============== - allow stopping car rotation in air like in Trackmania +- replay format - 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 z-buffer (shadow as 3D model would require collision detection and would make diff --git a/racing.h b/racing.h index 4ee468b..549d243 100644 --- a/racing.h +++ b/racing.h @@ -13,13 +13,14 @@ 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 current input state. - - Replay text format: first there is hexadeciaml hash of the map (exactly 8 - characters), then the name of the map follows immediately, then the - character ';', then the replay data, i.e. the sries of 16 bit words in - hexadecimal. The blocks (but nothing else) may be preceeded or followed by - blank characters. 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. + - Replay text format: first there is the name of the map terminated by ';', + then hexadecimal hash of the map follows (exactly 8 characters), then + blank character follows, then achieved time as a series of decimal digits + expressing the number of milliseconds, then a non-decimal character follows, + then the replay data, i.e. the series of 16 bit words in hexadecimal. The + blocks (but nothing else) may be preceeded or followed by blank characters. + 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 @@ -70,11 +71,13 @@ typedef int32_t LCR_GameUnit; ///< abstract game unit #define LCR_CAR_GRASS_FACTOR 5 #define LCR_CAR_DIRT_FACTOR 3 #define LCR_CAR_ICE_FACTOR 1 -#define LCR_CAR_DRIFT_FACTOR 2 ///< only affects steering friction +#define LCR_CAR_DRIFT_FACTOR 2 ///< only affects steering friction #define LCR_CAR_JOINTS 5 #define LCR_CAR_CONNECTIONS 10 +#define LCR_REPLAY_EVENT_END 0xff ///< special event fed to replay at the end + struct { TPE_World physicsWorld; @@ -114,6 +117,7 @@ struct // for playing uint16_t currentEvent; uint16_t currentFrame; + uint32_t achievedTime; } LCR_replay; /** @@ -137,6 +141,7 @@ void LCR_replayInitRecording(void) { LCR_LOG1("initializing replay recording"); LCR_replay.eventCount = 0; + LCR_replay.achievedTime = 0; } void LCR_replayInitPlaying(void) @@ -172,6 +177,15 @@ void LCR_replayOutputStr(void (*printChar)(char)) 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) { uint16_t e = LCR_replay.events[i]; @@ -179,8 +193,8 @@ void LCR_replayOutputStr(void (*printChar)(char)) for (int j = 0; j < 4; ++j) { - printChar(_LCR_hexDigit(e % 16)); - e /= 16; + printChar(_LCR_hexDigit((e >> 12) & 0x0f)); + 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 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) { 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 uint8_t previousInput = 0; uint32_t previousFrame = 0; @@ -1363,7 +1388,12 @@ uint32_t LCR_racingStep(unsigned int input) } if (valid) + { result |= LCR_RACING_EVENT_FINISHED; + + if (!LCR_racing.playingReplay) + LCR_replayRecordEvent(LCR_racing.tick,LCR_REPLAY_EVENT_END); + } } }