This commit is contained in:
Miloslav Ciz 2025-01-28 22:27:19 +01:00
parent e695921c5c
commit 6986028e33
4 changed files with 171 additions and 155 deletions

98
game.h
View file

@ -8,6 +8,9 @@
graphics, sound etc., and is meant to be included and used by specific
frontends (which will handle each platform's hardware details and I/O). See
the frontend info below for help with porting the game to a new platform.
This module (with the help of other modules) will perform all the computations
(physics, graphics, audio, ...) and only use the frontend's quite primitive
functions to actually present the results to the user.
The code uses LCR_ (or _LCR) prefix as a kind of namespace preventing
collision with 3rd party identifiers.
@ -59,19 +62,21 @@
/*
FOR FRONTENDS (porting to other platforms):
- Implement the below described functions according to their description.
- Implement the frontend functions given below according to their description.
- Implement the main program and game loop.
- Call the below described functions as described.
- If you want to support music, make your frontend play music from the "music"
file in assets. It is in raw format, storing 8bit unsigned samples at 8000
Hz. Use the LCR_gameMusicOn to check what the music volume is. If you
don't support music, set LCR_SETTING_MUSIC to 0 in your frontend code so
that the game knows music is disabled.
- Call some of the frontend funtions given below in your main program in
places as described in the description of the function.
- If you want to support music, make your code load and play the "music"
file in the asset directory. It is in raw format, storing 8bit unsigned
samples at 8 KHz mono. Use the LCR_gameMusicOn function to check whether the
music is currently enabled (if not, stop playing it). If you don't want to
support music, set LCR_SETTING_MUSIC to 0 in your frontend code (before
including this module) so that the game knows music is disabled.
*/
/**
Implement this in your frontend. Returns 1 if given key is pressed or 0
otherwise.
Implement this in your frontend. Returns 1 if given key (see LCR_KEY_*
constants) is pressed or 0 otherwise.
*/
uint8_t LCR_keyPressed(uint8_t key);
@ -87,35 +92,40 @@ void LCR_sleep(uint16_t timeMs);
to the screen back buffer (i.e. NOT directly to screen, back buffer shall
only be copied to front buffer once the LCR_gameStep function finishes all
rendering). This function should NOT check for out-of-screen coordinates, this
is handled by the game internals and out-of-screen pixels will never be drawn.
The color value depends on game settings but is normally an RGB565 value.
is handled by the game internals and out-of-screen pixels will never be drawn
(your code unnecessarily checking it again would likely slow down rendering a
lot, as drawing pixels is a bottleneck). The color value depends on game
settings but is normally an RGB565 value. The index parameter says the
coordinate at which to write the pixel, assuming the pixels are stored by
rows, from top to bottom.
*/
void LCR_drawPixel(unsigned long index, uint16_t color);
/**
Implement this in your frontend. This function will be called to log what the
program is doing. If you want to ignore logging, simply make the function do
nothing.
program is doing. Typically this function should print the string to console.
If you want to ignore logging, simply make the function do nothing.
*/
void LCR_log(const char *str);
/**
Implement this in your frontend. This function serves for loading optional
data file that allows to add more maps, replays etc. If your frontend
won't support this, just make the function return 0. Otherwise it must return
characters from the data file one by one; after reaching the end of file
0 must be returned and the reading position will be reset to start again.
data file that allows adding more maps, replays etc. If your frontend won't
support this, just make the function return 0. Otherwise it must return
characters from the data file one by one; after reaching the end of file 0
must be returned and the reading position will be reset to start from
beginning again.
*/
char LCR_getNextDataFileChar(void);
/**
Implement this in your frontend. This serves to store data in the optional
data file, e.g. replays. If your frontend doesn't support this (e.g.
because the file is read only), the function may ignore the append, but if
the file is otherwise supported, a rewind of the read position must still be
done. If appending is supported, the function must append the provided string
to the data file AND then reset the data file reading position back to
the start.
data file (e.g. replays and info about beaten maps). If your frontend doesn't
support this (e.g. because the file is read only), the function may ignore the
append, but if the file is otherwise supported, a rewind of the read position
must still be performed. If appending is supported, the function must append
the provided string to the data file AND then reset the data file reading
position back to the start.
*/
void LCR_appendDataStr(const char *str);
@ -134,18 +144,22 @@ void LCR_gameEnd(void);
Call this function in your frontend repeatedly inside the main loop, pass the
current time as the number of milliseconds since program start. This function
will perform the game step AND other things such as checking the input states,
rendering or sleeping (all using the above functions you should implement).
Returns 0 if program should end, otherwise 1.
rendering or sleeping (all using the above described functions you should
implement). Returns 0 if program should end, otherwise 1.
*/
uint8_t LCR_gameStep(uint32_t timeMs);
/**
Gets the current music volume;
Returns 1 if music is currently on, else 0. If your frontend supports music,
use this function at each frame to know whether you should start/stop playing
music. Otherwise ignore this.
*/
uint8_t LCR_gameMusicOn(void);
/**
Gets next audio sample (unsigned 8bit samples, 8 KHz).
Gets the next audio sample (unsigned 8bit samples, 8 KHz mono). If your
frontend supports sound, call this continuously and play the stream of
samples, otherwise ignore this.
*/
uint8_t LCR_gameGetNextAudioSample(void);
@ -187,7 +201,7 @@ uint8_t LCR_gameGetNextAudioSample(void);
// forward decls of pixel drawing functions for the renderer
/**
Internal pixel drawing function that draws pixel at specified screen coords
Internal pixel drawing function that puts a pixel at specified screen coords
without checking for safety (it's faster but can only be done if we know for
sure we're not drawing outside the screen).
*/
@ -213,7 +227,7 @@ static inline void LCR_drawPixelXYSafe(unsigned int x, unsigned int y,
#if LCR_SETTING_GHOST_MAX_SAMPLES == 0
#undef LCR_MENU_TABS
#define LCR_MENU_TABS 3
#define LCR_MENU_TABS 3 // no ghosts => remove the tab for ghosts
#endif
#define LCR_MENU_STRING_SIZE 16
@ -241,18 +255,17 @@ struct
{
uint8_t state;
uint32_t stateStartTime;
uint32_t time;
uint32_t frame;
uint32_t nextRenderFrameTime;
uint32_t nextRacingTickTime;
uint32_t time; ///< Current frame's time.
uint32_t frame; ///< Current frame number.
uint32_t nextRenderFrameTime; ///< At which frame to render next frame.
uint32_t nextRacingTickTime; ///< When to simulate next physics tick.
uint8_t cameraMode;
uint8_t debugDraw;
uint8_t musicOn;
uint8_t keyStates[LCR_KEYS_TOTAL]; /**< Assures unchanging key states
during a single frame, hold number of
frames for which the key has been
continuously held. */
uint32_t runTimeMS; /**< Current time of the run */
continuously held. */
uint32_t runTimeMS; ///< Current time of the run
struct
{
@ -260,14 +273,14 @@ struct
uint8_t selectedItem;
uint8_t itemCount;
char itemNames[LCR_MENU_MAX_ITEMS][LCR_MENU_STRING_SIZE];
const char *itemNamePtrs[LCR_MENU_MAX_ITEMS]; ///< helper array
const char *itemNamePtrs[LCR_MENU_MAX_ITEMS]; ///< Helper array.
} menu;
struct
{
int state; ///< -1 if reading external res. f., else pos.
int state; ///< -1 if reading external data f., else pos.
// indices and counts are among the data of the same type
// Indices and counts are among the data of the same type.
unsigned int firstItemIndex;
unsigned int itemsTotal;
} dataFile;
@ -286,7 +299,6 @@ struct
is to allow ghosts for even long replays. */
} ghost;
#endif
} LCR_game;
uint8_t LCR_gameMusicOn(void)
@ -448,7 +460,7 @@ void _LCR_gamePrepareGhost(void)
LCR_game.ghost.stretch = 0;
while (((int) LCR_replay.achievedTime) >
while (((int) LCR_racing.replay.achievedTime) >
(LCR_SETTING_GHOST_STEP << LCR_game.ghost.stretch) *
LCR_SETTING_GHOST_MAX_SAMPLES)
{
@ -780,7 +792,7 @@ void LCR_gameInit(int argc, const char **argv)
LCR_game.menu.selectedItem = 0;
LCR_game.frame = 0;
LCR_game.musicOn = 1;
LCR_game.musicOn = LCR_SETTING_MUSIC;
LCR_game.nextRenderFrameTime = 0;
LCR_game.nextRacingTickTime = 0;
LCR_game.cameraMode = LCR_CAMERA_MODE_DRIVE;
@ -1176,9 +1188,11 @@ void LCR_gameHandleInput(void)
LCR_rendererCameraReset();
break;
#if LCR_SETTING_MUSIC
case 1:
LCR_game.musicOn = !LCR_game.musicOn;
break;
#endif
case 2:
LCR_audio.on = !LCR_audio.on;