Rework main loop

This commit is contained in:
Miloslav Ciz 2024-12-30 22:19:41 +01:00
parent c3dcd1ff0f
commit c7711b6cf4

278
game.h
View file

@ -23,6 +23,23 @@
independent of rendering and physics libraries, but out of convenient adopts independent of rendering and physics libraries, but out of convenient adopts
their coordinate system (X right, Y up, Z forward) and rotations (Euler their coordinate system (X right, Y up, Z forward) and rotations (Euler
angles, by Z, then by X, then Y). angles, by Z, then by X, then Y).
RESOURCE FILE: The game uses so called resource file to store various
resources, mainly maps and replays. There is considered to be one abstract
global file which is just a long text string. Internally the global file is
composed of a hardcoded internal resource file string (stored in assets) with
very basic maps, and an optional user file (appended to the internal file)
that the frontend may provide, allowing adding more resources without
recompiling the game. The user file may be disabled on platforms that e.g.
don't have file systems, but the internal file will be always present. The
format of the resource file is following: it consists of resource strings
separated by '#' character (resource strings cannot contain this character).
Each resource string starts with a magic number: a single character
identifying the type of the resource (map, replay, ...), then the name of the
resource follows, then character ';' and then the resource string itself (up
until next '#' or end of file). The format of the string depends on the type
of resource, i.e. the format of map string has a special format (described in
the map module) etc.
*/ */
#ifndef _LCR_GAME_H #ifndef _LCR_GAME_H
@ -157,6 +174,10 @@ uint8_t LCR_gameGetNextAudioSample(void);
#define LCR_GAME_STATE_RUN 0x02 #define LCR_GAME_STATE_RUN 0x02
#define LCR_GAME_STATE_RUN_FINISHED 0x03 #define LCR_GAME_STATE_RUN_FINISHED 0x03
#define LCR_RESOURCE_ITEM_CHUNK 8
#define LCR_MENU_STRING_SIZE 16
struct struct
{ {
uint8_t state; uint8_t state;
@ -168,7 +189,16 @@ struct
uint8_t controlMode; uint8_t controlMode;
uint8_t debugDraw; uint8_t debugDraw;
uint8_t musicVolume; uint8_t musicVolume;
int resourceFileState; ///< -1 if reading external res. f., else pos.
struct
{
int state; ///< -1 if reading external res. f., else pos.
unsigned char loadedItemCount;
char loadedItemNames[LCR_RESOURCE_ITEM_CHUNK * LCR_MENU_STRING_SIZE];
unsigned int loadedItemsIndices[LCR_RESOURCE_ITEM_CHUNK]; // absolute
unsigned int itemsTotal; // total of items of this type
} resourceFile;
} LCR_game; } LCR_game;
uint8_t LCR_gameGetMusicVolume(void) uint8_t LCR_gameGetMusicVolume(void)
@ -249,7 +279,7 @@ void LCR_gameResetRun(void)
void LCR_gameRewindResourceFile(void) void LCR_gameRewindResourceFile(void)
{ {
LCR_appendResourceStr(""); LCR_appendResourceStr("");
LCR_game.resourceFileState = 0; LCR_game.resourceFile.state = 0;
} }
/** /**
@ -263,34 +293,34 @@ char LCR_gameGetNextResourceFileChar(void)
#if LCR_SETTING_ENABLE_RESOURCE_FILE #if LCR_SETTING_ENABLE_RESOURCE_FILE
char c; char c;
if (LCR_game.resourceFileState < 0) // external file? if (LCR_game.resourceFile.state < 0) // external file?
{ {
c = LCR_getNextResourceFileChar(); c = LCR_getNextResourceFileChar();
if (c == 0) if (c == 0)
LCR_game.resourceFileState = 0; // move to internal file next LCR_game.resourceFile.state = 0; // move to internal file next
} }
else // internal file else // internal file
{ {
c = LCR_internalResourceFile[LCR_game.resourceFileState]; c = LCR_internalResourceFile[LCR_game.resourceFile.state];
LCR_game.resourceFileState++; LCR_game.resourceFile.state++;
if (c == 0) if (c == 0)
{ {
c = LCR_getNextResourceFileChar(); c = LCR_getNextResourceFileChar();
LCR_game.resourceFileState = c ? -1 : 0; // trust this LCR_game.resourceFile.state = c ? -1 : 0; // trust this
} }
} }
return c; return c;
#else #else
if (LCR_internalResourceFile[LCR_game.resourceFileState] == 0) if (LCR_internalResourceFile[LCR_game.resourceFile.state] == 0)
{ {
LCR_game.resourceFileState = 0; LCR_game.resourceFile.state = 0;
return 0; return 0;
} }
return LCR_internalResourceFile[LCR_game.resourceFileState++]; return LCR_internalResourceFile[LCR_game.resourceFile.state++];
#endif #endif
} }
@ -327,7 +357,7 @@ void LCR_seekResourceByIndex(unsigned int index)
index--; index--;
} }
do // skip the name do // skip magic number and name
c = LCR_gameGetNextResourceFileChar(); c = LCR_gameGetNextResourceFileChar();
while (c != LCR_RESOURCE_FILE_SEPARATOR2 && while (c != LCR_RESOURCE_FILE_SEPARATOR2 &&
c != LCR_RESOURCE_FILE_SEPARATOR && c != 0); c != LCR_RESOURCE_FILE_SEPARATOR && c != 0);
@ -354,7 +384,19 @@ void LCR_gameInit(void)
LCR_racingInit(); LCR_racingInit();
LCR_audioInit(); LCR_audioInit();
LCR_game.resourceFileState = 0; LCR_game.resourceFile.state = 0;
LCR_game.resourceFile.loadedItemCount = 0;
LCR_game.resourceFile.itemsTotal = 0;
for (int i = 0; i < LCR_RESOURCE_ITEM_CHUNK; ++i)
{
for (int j = 0; j < LCR_MENU_STRING_SIZE; ++j)
LCR_game.resourceFile.loadedItemNames[i * LCR_MENU_STRING_SIZE + j] = 0;
LCR_game.resourceFile.loadedItemsIndices[i] = 0;
}
LCR_game.frame = 0; LCR_game.frame = 0;
LCR_game.musicVolume = 255; LCR_game.musicVolume = 255;
@ -362,6 +404,19 @@ void LCR_gameInit(void)
LCR_game.nextRacingTickTime = 0; LCR_game.nextRacingTickTime = 0;
LCR_game.controlMode = LCR_CONTROL_MODE_FREECAM; LCR_game.controlMode = LCR_CONTROL_MODE_FREECAM;
LCR_gameStartRun(); LCR_gameStartRun();
LCR_game.state = LCR_GAME_STATE_RUN;
}
/**
Loads up to LCR_RESOURCE_ITEM_CHUNK items of given type (0 = map, 1 = replay)
into RAM, starting at given index (among items of the same type).
*/
void LCR_gameLoadResourceFileChunk(uint8_t type, unsigned int startIndex)
{
// TODO
} }
void LCR_gameEnd(void) void LCR_gameEnd(void)
@ -369,10 +424,73 @@ void LCR_gameEnd(void)
LCR_LOG0("ending"); LCR_LOG0("ending");
} }
uint8_t LCR_gameStep(uint32_t time) void LCR_gameDraw3DView(void)
{ {
LCR_GameUnit carTransform[6]; LCR_GameUnit carTransform[6];
LCR_GameUnit physicsInterpolationParam = LCR_GAME_UNIT -
((LCR_game.nextRacingTickTime - LCR_game.time) * LCR_GAME_UNIT)
/ LCR_RACING_TICK_MS;
LCR_racingGetCarTransform(carTransform,carTransform + 3,
physicsInterpolationParam);
LCR_rendererSetCarTransform(carTransform,carTransform + 3);
if (LCR_game.controlMode != LCR_CONTROL_MODE_FREECAM)
LCR_rendererCameraFollow();
#if LCR_ANIMATE_CAR
LCR_rendererSetWheelState(LCR_racingGetWheelRotation(),
LCR_racingGetWheelSteer() * 2);
#endif
LCR_rendererDraw();
int val = LCR_carSpeedKMH();
if (val < 5) // don't show tiny oscillations when still
val = 0;
char str[6];
str[0] = val >= 100 ? '0' + (val / 100) % 10 : ' ';
str[1] = val >= 10 ? '0' + (val / 10) % 10 : ' ';
str[2] = '0' + val % 10;
str[3] = 0;
LCR_rendererDrawText(str,
LCR_EFFECTIVE_RESOLUTION_X -
LCR_rendererComputeTextWidth(str,2) - 20,
LCR_EFFECTIVE_RESOLUTION_Y -
LCR_rendererComputeTextHeight(2) - 20,0,2);
val = LCR_racingGetRunTimeMS() / 1000; // seconds
str[3] = '0' + (val % 60) / 10;
str[4] = '0' + val % 10;
val = (val / 60) % 100; // minutes
str[0] = '0' + val / 10;
str[1] = '0' + val % 10;
str[2] = '\'';
str[5] = 0;
LCR_rendererDrawText(str,20,LCR_EFFECTIVE_RESOLUTION_Y -
LCR_rendererComputeTextHeight(2) - 20,0,2);
#if LCR_SETTING_DEBUG_PHYSICS_DRAW
LCR_GameUnit camTr[7];
LCR_rendererGetCameraTransform(camTr,camTr + 3,camTr + 6);
LCR_physicsDebugDraw(camTr,camTr + 3,camTr[6]);
#endif
}
uint8_t LCR_gameStep(uint32_t time)
{
uint32_t sleep = 0;
LCR_LOG2("game step start"); LCR_LOG2("game step start");
LCR_game.time = time; LCR_game.time = time;
@ -381,14 +499,47 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_keyStates[i] = LCR_keyPressed(i) ? LCR_keyStates[i] = LCR_keyPressed(i) ?
(LCR_keyStates[i] < 255 ? LCR_keyStates[i] + 1 : 255) : 0; (LCR_keyStates[i] < 255 ? LCR_keyStates[i] + 1 : 255) : 0;
uint32_t sleep = 0;
if (LCR_keyStates[LCR_KEY_B] == 1) if (LCR_keyStates[LCR_KEY_B] == 1)
LCR_game.controlMode = LCR_game.controlMode == LCR_CONTROL_MODE_FREECAM ? LCR_game.controlMode = LCR_game.controlMode == LCR_CONTROL_MODE_FREECAM ?
LCR_CONTROL_MODE_DRIVE : LCR_CONTROL_MODE_FREECAM; LCR_CONTROL_MODE_DRIVE : LCR_CONTROL_MODE_FREECAM;
else if (LCR_keyStates[LCR_KEY_B] == 30) else if (LCR_keyStates[LCR_KEY_B] == 30)
LCR_gameResetRun(); LCR_gameResetRun();
LCR_GameUnit offsets[5];
for (int i = 0; i < 5; ++i)
offsets[i] = 0;
if (LCR_game.controlMode == LCR_CONTROL_MODE_FREECAM)
{
if (LCR_keyStates[LCR_KEY_A])
{
if (LCR_keyStates[LCR_KEY_UP])
offsets[4] = LCR_FREE_CAMERA_TURN_STEP;
else if (LCR_keyStates[LCR_KEY_DOWN])
offsets[4] -= LCR_FREE_CAMERA_TURN_STEP;
if (LCR_keyStates[LCR_KEY_RIGHT])
offsets[3] -= LCR_FREE_CAMERA_TURN_STEP;
else if (LCR_keyStates[LCR_KEY_LEFT])
offsets[3] = LCR_FREE_CAMERA_TURN_STEP;
}
else
{
if (LCR_keyStates[LCR_KEY_UP])
offsets[0] = LCR_FREE_CAMERA_STEP;
else if (LCR_keyStates[LCR_KEY_DOWN])
offsets[0] -= LCR_FREE_CAMERA_STEP;
if (LCR_keyStates[LCR_KEY_RIGHT])
offsets[1] = LCR_FREE_CAMERA_STEP;
else if (LCR_keyStates[LCR_KEY_LEFT])
offsets[1] -= LCR_FREE_CAMERA_STEP;
}
LCR_rendererMoveCamera(offsets,offsets + 3);
}
while (time >= LCR_game.nextRacingTickTime) while (time >= LCR_game.nextRacingTickTime)
{ {
LCR_LOG2("gonna step racing engine"); LCR_LOG2("gonna step racing engine");
@ -427,6 +578,9 @@ uint8_t LCR_gameStep(uint32_t time)
LCR_LOG1("crash (big)"); LCR_LOG1("crash (big)");
} }
int engineIntensity = LCR_carSpeedKMH() * 2;
LCR_audioSetEngineIntensity(engineIntensity < 256 ? engineIntensity : 255);
LCR_game.nextRacingTickTime += LCR_RACING_TICK_MS; LCR_game.nextRacingTickTime += LCR_RACING_TICK_MS;
} }
@ -436,102 +590,10 @@ uint8_t LCR_gameStep(uint32_t time)
{ {
LCR_LOG2("gonna render next frame"); LCR_LOG2("gonna render next frame");
LCR_GameUnit physicsInterpolationParam = LCR_GAME_UNIT -
((LCR_game.nextRacingTickTime - time) * LCR_GAME_UNIT)
/ LCR_RACING_TICK_MS;
LCR_racingGetCarTransform(carTransform,carTransform + 3,
physicsInterpolationParam);
LCR_rendererSetCarTransform(carTransform,carTransform + 3);
while (time >= LCR_game.nextRenderFrameTime) while (time >= LCR_game.nextRenderFrameTime)
LCR_game.nextRenderFrameTime += 1000 / LCR_SETTING_FPS; LCR_game.nextRenderFrameTime += 1000 / LCR_SETTING_FPS;
LCR_GameUnit offsets[5]; LCR_gameDraw3DView();
for (int i = 0; i < 5; ++i)
offsets[i] = 0;
if (LCR_game.controlMode == LCR_CONTROL_MODE_FREECAM)
{
if (LCR_keyStates[LCR_KEY_A])
{
if (LCR_keyStates[LCR_KEY_UP])
offsets[4] = LCR_FREE_CAMERA_TURN_STEP;
else if (LCR_keyStates[LCR_KEY_DOWN])
offsets[4] -= LCR_FREE_CAMERA_TURN_STEP;
if (LCR_keyStates[LCR_KEY_RIGHT])
offsets[3] -= LCR_FREE_CAMERA_TURN_STEP;
else if (LCR_keyStates[LCR_KEY_LEFT])
offsets[3] = LCR_FREE_CAMERA_TURN_STEP;
}
else
{
if (LCR_keyStates[LCR_KEY_UP])
offsets[0] = LCR_FREE_CAMERA_STEP;
else if (LCR_keyStates[LCR_KEY_DOWN])
offsets[0] -= LCR_FREE_CAMERA_STEP;
if (LCR_keyStates[LCR_KEY_RIGHT])
offsets[1] = LCR_FREE_CAMERA_STEP;
else if (LCR_keyStates[LCR_KEY_LEFT])
offsets[1] -= LCR_FREE_CAMERA_STEP;
}
LCR_rendererMoveCamera(offsets,offsets + 3);
}
else
LCR_rendererCameraFollow();
#if LCR_ANIMATE_CAR
LCR_rendererSetWheelState(LCR_racingGetWheelRotation(),
LCR_racingGetWheelSteer() * 2);
#endif
LCR_rendererDraw();
int val = LCR_carSpeedKMH();
LCR_audioSetEngineIntensity((2 * val) < 256 ? (2 * val) : 255);
if (val < 5) // don't show tiny oscillations when still
val = 0;
char str[6];
str[0] = val >= 100 ? '0' + (val / 100) % 10 : ' ';
str[1] = val >= 10 ? '0' + (val / 10) % 10 : ' ';
str[2] = '0' + val % 10;
str[3] = 0;
LCR_rendererDrawText(str,
LCR_EFFECTIVE_RESOLUTION_X -
LCR_rendererComputeTextWidth(str,2) - 20,
LCR_EFFECTIVE_RESOLUTION_Y -
LCR_rendererComputeTextHeight(2) - 20,0,2);
val = LCR_racingGetRunTimeMS() / 1000; // seconds
str[3] = '0' + (val % 60) / 10;
str[4] = '0' + val % 10;
val = (val / 60) % 100; // minutes
str[0] = '0' + val / 10;
str[1] = '0' + val % 10;
str[2] = '\'';
str[5] = 0;
LCR_rendererDrawText(str,20,LCR_EFFECTIVE_RESOLUTION_Y -
LCR_rendererComputeTextHeight(2) - 20,0,2);
#if LCR_SETTING_DEBUG_PHYSICS_DRAW
LCR_GameUnit camTr[7];
LCR_rendererGetCameraTransform(camTr,camTr + 3,camTr + 6);
LCR_physicsDebugDraw(camTr,camTr + 3,camTr[6]);
#endif
} }
else else
{ {