Start ghost

This commit is contained in:
Miloslav Ciz 2025-01-21 22:46:56 +01:00
parent c0f5e5cf5b
commit 0a91d5f54e
4 changed files with 105 additions and 9 deletions

View file

@ -1,6 +1,9 @@
=========== GENERAL ==============
- replay validation
- ghosts: if the replay for the ghost is too long (too many samples for the
preallocated array), we can subdivide the sample resolution (i.e. "stretch"
the samples) to cover the whole replay (ofc for the price of worse quality).
- 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

72
game.h
View file

@ -211,6 +211,12 @@ static inline void LCR_drawPixelXYSafe(unsigned int x, unsigned int y,
#define LCR_MENU_MAX_ITEMS 9 // don't change
#define LCR_RESOURCE_ITEM_CHUNK (LCR_MENU_MAX_ITEMS - 1)
#define LCR_MENU_TABS 4
#if LCR_SETTING_GHOST_MAX_SAMPLES == 0
#undef LCR_MENU_TABS
#define LCR_MENU_TABS 3
#endif
#define LCR_MENU_STRING_SIZE 16
#define LCR_RESOURCE_FILE_SEPARATOR '#'
@ -266,6 +272,23 @@ struct
unsigned int firstItemIndex;
unsigned int itemsTotal;
} dataFile;
#define LCR_GHOST_SAMPLE_SIZE 5
#if LCR_SETTING_GHOST_MAX_SAMPLES != 0
struct
{
uint8_t samples[LCR_SETTING_GHOST_MAX_SAMPLES * LCR_GHOST_SAMPLE_SIZE];
/**< Samples, each 5 bytes: 9 bits for X and Z, 10 for Z,
4 for each rotation component. */
uint8_t stretch; /**< Stretch of the base sample step, as a bit shift
(i.e. 1 means the step will be 2x as long etc.). This
is to allow ghosts for even long replays. */
const uint8_t *currentSample;
uint16_t nextSampleIn;
} ghost;
#endif
} LCR_game;
uint8_t LCR_gameMusicOn(void)
@ -314,13 +337,6 @@ void LCR_gameSetState(uint8_t state)
LCR_game.stateStartTime = LCR_game.time;
}
LCR_GameUnit LCR_carSpeedKMH(void)
{
return // we use 28/8 as an approximation of 3.6 to convers MPS to KMH
(28 * LCR_SETTING_CMS_PER_BLOCK * LCR_racingGetCarSpeedUnsigned() *
LCR_RACING_FPS) / (800 * LCR_GAME_UNIT);
}
void LCR_gameResetRun(uint8_t replay)
{
LCR_GameUnit carTransform[6];
@ -336,6 +352,42 @@ void LCR_gameResetRun(uint8_t replay)
LCR_game.runTimeMS = 0;
}
void _LCR_gamePrepareGhost(void)
{
LCR_GameUnit carTransform[6];
LCR_LOG1("preparing ghost");
LCR_gameResetRun(1);
LCR_game.ghost.stretch = 0;
while (((int) LCR_replay.achievedTime) >
(LCR_SETTING_GHOST_STEP << LCR_game.ghost.stretch) *
LCR_SETTING_GHOST_MAX_SAMPLES)
{
LCR_LOG2("stretching replay step");
LCR_game.ghost.stretch++;
}
while (!LCR_replayHasFinished())
{
if (LCR_racing.tick % (LCR_SETTING_GHOST_STEP << LCR_game.ghost.stretch)
== 0)
{
LCR_racingGetCarTransform(carTransform,carTransform + 3,0);
}
LCR_racingStep(0);
}
}
LCR_GameUnit LCR_carSpeedKMH(void)
{
return // we use 28/8 as an approximation of 3.6 to convers MPS to KMH
(28 * LCR_SETTING_CMS_PER_BLOCK * LCR_racingGetCarSpeedUnsigned() *
LCR_RACING_FPS) / (800 * LCR_GAME_UNIT);
}
/**
Rewinds the global data file reading head to the beginning.
*/
@ -1014,6 +1066,12 @@ uint8_t LCR_gameStep(uint32_t time)
if (_LCR_gameIsLoading())
{
LCR_rendererLoadMap();
if (LCR_game.state == LCR_GAME_STATE_LOADING_REP2)
{
_LCR_gamePrepareGhost();
}
LCR_gameResetRun(
LCR_game.state == LCR_GAME_STATE_LOADING_REP1);
}

View file

@ -295,7 +295,10 @@ for (int i = 0; i < 8; ++i) // hash
uint8_t LCR_replayGetNextInput(void)
{
if (LCR_replay.currentEvent >= LCR_replay.eventCount)
{
LCR_replay.currentFrame++; // has to be here
return 0;
}
if (LCR_replay.currentFrame ==
(LCR_replay.events[LCR_replay.currentEvent] >> 4))
@ -312,7 +315,19 @@ uint8_t LCR_replayGetNextInput(void)
int LCR_replayHasFinished(void)
{
return LCR_replay.currentEvent >= LCR_replay.eventCount;
if (LCR_replay.currentEvent == LCR_replay.eventCount)
{
uint32_t totalTime = LCR_replay.currentFrame;
for (int i = 0; i < LCR_replay.eventCount; ++i)
totalTime += LCR_replay.events[i] >> 4;
return totalTime >= LCR_replay.achievedTime;
}
return LCR_replay.currentEvent > LCR_replay.eventCount;
// return LCR_replay.currentEvent >= LCR_replay.eventCount;
}
/**
@ -1084,8 +1099,13 @@ uint32_t LCR_racingStep(unsigned int input)
if (LCR_racing.tick == 0)
LCR_replayInitPlaying();
input = LCR_replayGetNextInput();
if (LCR_replayHasFinished())
{
LCR_LOG1("replay finished");
return LCR_RACING_EVENT_FINISHED;
}
input = LCR_replayGetNextInput();
}
else
{

View file

@ -205,4 +205,19 @@
#define LCR_SETTING_TIME_MULTIPLIER 100
#endif
#ifndef LCR_SETTING_GHOST_STEP
/** Step (in physics engine ticks) by which the samples for ghost car will be
spaced (positions inbetween will be interpolated). Lower step along with more
ghost samples should result in more accurate ghost animation, but will eat up
more memory. This should ideally be kept a power of two. */
#define LCR_SETTING_GHOST_STEP 16
#endif
#ifndef LCR_SETTING_GHOST_MAX_SAMPLES
/** Maximum number of samples the ghost car will be able to use. Higher value
should generally result in more accurate ghost animation, but will eat up more
memory. Value 0 disables ghosts. */
#define LCR_SETTING_GHOST_MAX_SAMPLES 128
#endif
#endif // guard