Simple mod that will print run stats (such as maximum speed or acceleration) to the console upon finishing (in normal run or replay). diff --git a/frontend_helper.h b/frontend_helper.h index f95b078..f006fe4 100644 --- a/frontend_helper.h +++ b/frontend_helper.h @@ -128,3 +128,63 @@ void closeDataFile(void) fclose(dataFile); #endif } + +void _printStatVal(int val, int divisor) +{ + if (val < 0) + { + putchar('-'); + val *= -1; + } + + printf("%d",val / divisor); + + if (divisor > 1) + printf(".%d",val % divisor); +} + +void LCR_presentStats(const int *stats) +{ + printf("run stats: "); + printf("speed (max/avg): "); + _printStatVal(stats[0],1); + putchar(' '); + _printStatVal(stats[1],1000); + + printf("; accel (min/max/avg/avgAbs): "); + _printStatVal(stats[2],1); + putchar(' '); + _printStatVal(stats[3],1); + putchar(' '); + _printStatVal(stats[4],1000); + putchar(' '); + _printStatVal(stats[5],1000); + + printf("; dist: "); + _printStatVal(stats[6],100); + + printf("; surface %% (concr./grass/dirt/ice/air): "); + + for (int i = 7; i <= 11; ++i) + { + _printStatVal(stats[i],1); + putchar(i == 11 ? ';' : ' '); + } + + printf(" keys (U/D/R/L/avg): "); + + _printStatVal(stats[12],1); + putchar(' '); + _printStatVal(stats[13],1); + putchar(' '); + _printStatVal(stats[14],1); + putchar(' '); + _printStatVal(stats[15],1); + putchar(' '); + _printStatVal(stats[16],100); + + printf("; drift: "); + _printStatVal(stats[17],1); + + putchar('\n'); +} diff --git a/game.h b/game.h index 1ad5131..0ec623d 100644 --- a/game.h +++ b/game.h @@ -167,6 +167,33 @@ uint8_t LCR_gameMusicOn(void); */ uint8_t LCR_gameGetNextAudioSample(void); +#define LCR_STATS_SPEED_KMH_MAX 0 +#define LCR_STATS_SPEED_MH_AVG 1 +#define LCR_STATS_ACCEL_KMH_MIN 2 +#define LCR_STATS_ACCEL_KMH_MAX 3 +#define LCR_STATS_ACCEL_MHS_AVG 4 +#define LCR_STATS_ACCEL_ABS_MHS_AVG 5 +#define LCR_STATS_DIST_CM 6 +#define LCR_STATS_SURF_PERC_CONCR 7 +#define LCR_STATS_SURF_PERC_GRASS 8 +#define LCR_STATS_SURF_PERC_DIRT 9 +#define LCR_STATS_SURF_PERC_ICE 10 +#define LCR_STATS_SURF_PERC_AIR 11 +#define LCR_STATS_KEY_FRAMES_U 12 +#define LCR_STATS_KEY_FRAMES_D 13 +#define LCR_STATS_KEY_FRAMES_R 14 +#define LCR_STATS_KEY_FRAMES_L 15 +#define LCR_STATS_KEYS_AVG_X100 16 +#define LCR_STATS_DRIFT_FRAMES 17 +#define _LCR_STATS_TOTAL 18 + +/** + Will be called to present run stats, implement the presentation however you + want (e.g. by printing to standard output). The stats array holds individual + stats values, their meaning is described by the LCR_STATS_* constants. +*/ +void LCR_presentStats(const int *stats); + /** This macro may be redefined by frontend to a command that will be periodically performed during map loading. This exists to prevent making the program seem @@ -386,6 +413,9 @@ struct uint8_t renderFramesMeasured; uint8_t physicsFramesMeasured; #endif + + LCR_GameUnit prevSpeed; + int stats[_LCR_STATS_TOTAL]; } LCR_game; uint8_t LCR_gameMusicOn(void) @@ -494,6 +524,11 @@ void LCR_gameResetRun(uint8_t replay, uint8_t ghost) LCR_racingGetCarTransform(carTransform,carTransform + 3,0); LCR_rendererSetCarTransform(carTransform,carTransform + 3); + LCR_game.prevSpeed = 0; + + for (int i = 0; i < _LCR_STATS_TOTAL; ++i) + LCR_game.stats[i] = 0; + if (LCR_game.cameraMode != LCR_CAMERA_MODE_FREE) { LCR_rendererCameraReset(); @@ -1776,6 +1811,63 @@ uint8_t LCR_gameStep(uint32_t time) uint32_t events = paused ? 0 : LCR_racingStep(input); + // collect stats: + + int stat = LCR_carSpeedKMH(); + + if (stat > LCR_game.stats[LCR_STATS_SPEED_KMH_MAX]) + LCR_game.stats[LCR_STATS_SPEED_KMH_MAX] = stat; + + LCR_game.stats[LCR_STATS_SPEED_MH_AVG] += stat; + + LCR_game.stats[LCR_STATS_DRIFT_FRAMES] += LCR_racing.carDrifting; + + if ((LCR_racing.wheelCollisions & 0x0f) == 0) + LCR_game.stats[LCR_STATS_SURF_PERC_AIR]++; + else if (LCR_racingCurrentGroundMaterial() == LCR_BLOCK_MATERIAL_GRASS) + LCR_game.stats[LCR_STATS_SURF_PERC_GRASS]++; + else if (LCR_racingCurrentGroundMaterial() == LCR_BLOCK_MATERIAL_DIRT) + LCR_game.stats[LCR_STATS_SURF_PERC_DIRT]++; + else if (LCR_racingCurrentGroundMaterial() == LCR_BLOCK_MATERIAL_ICE) + LCR_game.stats[LCR_STATS_SURF_PERC_ICE]++; + else + LCR_game.stats[LCR_STATS_SURF_PERC_CONCR]++; + + LCR_game.stats[LCR_STATS_KEY_FRAMES_U] += + (LCR_racing.currentInputs & LCR_RACING_INPUT_FORW) != 0; + LCR_game.stats[LCR_STATS_KEY_FRAMES_D] += + (LCR_racing.currentInputs & LCR_RACING_INPUT_BACK) != 0; + LCR_game.stats[LCR_STATS_KEY_FRAMES_R] += + (LCR_racing.currentInputs & LCR_RACING_INPUT_RIGHT) != 0; + LCR_game.stats[LCR_STATS_KEY_FRAMES_L] += + (LCR_racing.currentInputs & LCR_RACING_INPUT_LEFT) != 0; + + LCR_game.stats[LCR_STATS_KEYS_AVG_X100] += + ((LCR_racing.currentInputs & LCR_RACING_INPUT_FORW) != 0) + + ((LCR_racing.currentInputs & LCR_RACING_INPUT_BACK) != 0) + + ((LCR_racing.currentInputs & LCR_RACING_INPUT_RIGHT) != 0) + + ((LCR_racing.currentInputs & LCR_RACING_INPUT_LEFT) != 0); + + stat = LCR_carSpeedKMH() - LCR_game.prevSpeed; + + if (LCR_game.runTime <= 1 || + stat > LCR_game.stats[LCR_STATS_ACCEL_KMH_MAX]) + LCR_game.stats[LCR_STATS_ACCEL_KMH_MAX] = stat; + + if (LCR_game.runTime <= 1 || + stat < LCR_game.stats[LCR_STATS_ACCEL_KMH_MIN]) + LCR_game.stats[LCR_STATS_ACCEL_KMH_MIN] = stat; + + LCR_game.stats[LCR_STATS_ACCEL_MHS_AVG] += stat; + + if (stat < 0) + stat *= -1; + + LCR_game.stats[LCR_STATS_ACCEL_ABS_MHS_AVG] += stat; + LCR_game.stats[LCR_STATS_DIST_CM] += LCR_racingGetCarSpeedUnsigned(); + + LCR_game.prevSpeed = LCR_carSpeedKMH(); + #if LCR_SETTING_PARTICLES LCR_rendererSetParticles(0); @@ -1816,6 +1908,46 @@ uint8_t LCR_gameStep(uint32_t time) LCR_LOG1("finished, time:"); LCR_LOG1_NUM(LCR_game.runTime); + // now convert collected stats to correct units: + + LCR_game.stats[LCR_STATS_SPEED_MH_AVG] = + (LCR_game.stats[LCR_STATS_SPEED_MH_AVG] * 1000) / LCR_game.runTime; + + LCR_game.stats[LCR_STATS_ACCEL_MHS_AVG] = + (LCR_game.stats[LCR_STATS_ACCEL_MHS_AVG] * 1000 * LCR_RACING_FPS) / + LCR_game.runTime; + + LCR_game.stats[LCR_STATS_ACCEL_ABS_MHS_AVG] = + (LCR_game.stats[LCR_STATS_ACCEL_ABS_MHS_AVG] * 1000 * LCR_RACING_FPS) + / LCR_game.runTime; + + int tmp = + LCR_game.stats[LCR_STATS_SURF_PERC_CONCR] + + LCR_game.stats[LCR_STATS_SURF_PERC_GRASS] + + LCR_game.stats[LCR_STATS_SURF_PERC_DIRT] + + LCR_game.stats[LCR_STATS_SURF_PERC_ICE] + + LCR_game.stats[LCR_STATS_SURF_PERC_AIR]; + + LCR_game.stats[LCR_STATS_SURF_PERC_CONCR] = + (100 * LCR_game.stats[LCR_STATS_SURF_PERC_CONCR]) / tmp; + LCR_game.stats[LCR_STATS_SURF_PERC_GRASS] = + (100 * LCR_game.stats[LCR_STATS_SURF_PERC_GRASS]) / tmp; + LCR_game.stats[LCR_STATS_SURF_PERC_DIRT] = + (100 * LCR_game.stats[LCR_STATS_SURF_PERC_DIRT]) / tmp; + LCR_game.stats[LCR_STATS_SURF_PERC_ICE] = + (100 * LCR_game.stats[LCR_STATS_SURF_PERC_ICE]) / tmp; + LCR_game.stats[LCR_STATS_SURF_PERC_AIR] = + (100 * LCR_game.stats[LCR_STATS_SURF_PERC_AIR]) / tmp; + + LCR_game.stats[LCR_STATS_KEYS_AVG_X100] = + (LCR_game.stats[LCR_STATS_KEYS_AVG_X100] * 100)/ LCR_game.runTime; + + LCR_game.stats[LCR_STATS_DIST_CM] = + (LCR_game.stats[LCR_STATS_DIST_CM] * LCR_SETTING_CMS_PER_BLOCK) + / LCR_GAME_UNIT; + + LCR_presentStats(LCR_game.stats); + if (LCR_game.runTime <= LCR_currentMap.targetTime #if LCR_SETTING_REPLAY_MAX_SIZE != 0 && !LCR_racing.replay.on