Refactor a bit

This commit is contained in:
Miloslav Číž 2020-10-10 21:08:52 +02:00
parent dd7f764bea
commit a7c96d630c
4 changed files with 74 additions and 78 deletions

View File

@ -11,6 +11,8 @@ These are some ideas about what you can do with this game:
- Add OpenGL (or other accelerated) rendering, with true 3D, true color,
antialiasing, texture filtering etc.
- Improve SW rendering on PC, e.g. use full z-buffer, particle effects etc.
- Try to add screen space filters, like screen space antialiasing or pixel art
upscaling algorithms.
- Add procedurally generated levels, maybe also procedural textures etc.
By taking this to the extreme you could fit this game into just a few kb.
- Add demo recording support.

View File

@ -57,15 +57,17 @@ SDL:
## Stats and comparisons (for version 1.0)
lines of code (80 column wrapping, a lot of empty lines):
source code stats (80 column wrapping, a lot of empty lines):
| file | LOC |
| --------------------------------------| -------|
| game.h (main game logic) | TODO |
| raycastlib.h (ray casting library) | TODO |
| images.h, levels.h, sounds.h (assets) | TODO |
| settings.h, constants.h | TODO |
| **total** | TODO |
| file | LOC | file size |
| --------------------------------------| ------- | --------- |
| game.h (main game logic) | TODO | TODO |
| raycastlib.h (ray casting library) | TODO | TODO |
| images.h, levels.h, sounds.h (assets) | TODO | TODO |
| settings.h, constants.h | TODO | TODO |
| **total** | TODO | TODO |
| Doom | TODO | TODO |
| Wolf 3D | TODO | TODO |
compiled:

128
game.h
View File

@ -28,6 +28,8 @@
/*
The following keys are mandatory to be implemented on any platform in order
for the game to be playable.
Enums are bloat :)
*/
#define SFG_KEY_UP 0
#define SFG_KEY_RIGHT 1
@ -55,15 +57,14 @@
/* ============================= PORTING =================================== */
/* When porting, do the following:
- Include this file (and possibly other optionaly files) in your main_*
frontend source.
- Include this file (and possibly other optionaly files, like sounds.h) in
your main_*.c frontend source.
- Implement the following functions in your frontend source.
- Call SFG_init() from your frontend initialization code.
- Call SFG_mainLoopBody() from within your frontend main loop.
*/
- Call SFG_mainLoopBody() from within your frontend main loop. */
#ifndef SFG_LOG
#define SFG_LOG(str) ; ///< Can be redefined to log messages for better debug.
#define SFG_LOG(str) {} ///< Can be redefined to log game messages.
#endif
#ifndef SFG_CPU_LOAD
@ -73,16 +74,16 @@
/**
Returns 1 (0) if given key is pressed (not pressed). At least the mandatory
keys have to be implemented, the optional keys don't have to ever return 1.
See the key contant definitions to see which ones are mandatory.
See the key constant definitions to see which ones are mandatory.
*/
int8_t SFG_keyPressed(uint8_t key);
/**
Optinal function for mouse/analog controls, gets mouse x and y offset in
pixels from the game screen center (to achieve classic FPS mouse controls the
platform should center the mouse at the end). If the platform isn't using a
mouse, this function can simply return [0,0] offets at each call, or even
do nothing (leave the variables as are).
Optinal function for mouse/joystick/analog controls, gets mouse x and y offset
in pixels from the game screen center (to achieve classic FPS mouse controls
the platform should center the mouse after the call). If the platform isn't
using a mouse, this function can simply return [0,0] offets at each call, or
even do nothing at all (leave the variables as are).
*/
void SFG_getMouseOffset(int16_t *x, int16_t *y);
@ -99,8 +100,9 @@ uint32_t SFG_getTimeMs();
void SFG_sleepMs(uint16_t timeMs);
/**
Set specified screen pixel. The function doesn't have to check whether
the coordinates are within screen.
Set specified screen pixel. ColorIndex is the index of the game's palette.
The function doesn't have to (and shouldn't, for the sake of performance)
check whether the coordinates are within screen.
*/
static inline void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex);
@ -117,8 +119,8 @@ void SFG_playSound(uint8_t soundIndex, uint8_t volume);
/**
Informs the frontend whether music should get enabled/disabled. Playing music
is optional and the frontend can ignore it. If a frontend wants to implement
music, it can use the one provided in sounds.h or use its own.
is optional and the frontend can ignore this. If a frontend wants to implement
music, it can use the bytebeat provided in sounds.h or use its own.
*/
void SFG_enableMusic(uint8_t enable);
@ -135,18 +137,19 @@ void SFG_enableMusic(uint8_t enable);
/**
This is an optional function that informs the frontend about special events
which may trigger something special, such as a controller vibration, logging
something etc. This function can do nothing.
which may trigger something special on the platform, such as a controller
vibration, logging etc. This implementation of this function can be left
empty.
*/
void SFG_processEvent(uint8_t event, uint8_t data);
#define SFG_SAVE_SIZE 12
/**
Optional function for permanently saving game state. Platform that don't have
permanent storage may let this function do nothing. If implemented, the
function should save the passed data into its permanent storage, e.g. a file,
a cookie etc.
Optional function for permanently saving the game state. Platforms that don't
have permanent storage (HDD, EEPROM etc.) may let this function simply do
nothing. If implemented, the function should save the passed data into its
permanent storage, e.g. a file, a cookie etc.
*/
void SFG_save(uint8_t data[SFG_SAVE_SIZE]);
@ -161,20 +164,22 @@ void SFG_save(uint8_t data[SFG_SAVE_SIZE]);
array as is).
This function should return 1 if saving/loading is possible and 0 if not (this
will be used by the game to detect this capability).
will be used by the game to detect saving/loading capability).
*/
uint8_t SFG_load(uint8_t data[SFG_SAVE_SIZE]);
/* ========================================================================= */
/**
Game main loop body, call this inside the platform's specific main loop.
Returns 1 if the game continues, 0 if the game was exited.
Game main loop body, call this inside your platform's specific main loop.
Returns 1 if the game continues, 0 if the game was exited and program should
halt. This functions handles reaching the target FPS and sleeping for
relieving CPU, so don't do this.
*/
uint8_t SFG_mainLoopBody();
/**
Initializes the whole program, call this in the platform initialization.
Initializes the game, call this in the platform's initialization code.
*/
void SFG_init();
@ -190,7 +195,7 @@ void SFG_init();
#define SFG_PROGRAM_MEMORY_U8(addr) ((uint8_t) (*(addr)))
#endif
#include "images.h"
#include "images.h" // don't change the order of these includes
#include "levels.h"
#include "texts.h"
#include "palette.h"
@ -212,15 +217,14 @@ void SFG_init();
typedef struct
{
uint8_t coords[2];
uint8_t state; /**< door state in format:
uint8_t state; /**< door state in format:
MSB ccbaaaaa LSB
MSB ccbaaaaa LSB
aaaaa: current door height (how much they're open)
b: whether currently going up (0) or down (1)
cc: by which card (key) the door is unlocked, 00
means no card (unlocked), 1 means card 0 etc.
*/
aaaaa: current door height (how much they're open)
b: whether currently going up (0) or down (1)
cc: by which card (key) the door is unlocked, 00
means no card (unlocked), 1 means card 0 etc. */
} SFG_DoorRecord;
#define SFG_SPRITE_SIZE(size0to3) \
@ -252,9 +256,9 @@ typedef uint8_t SFG_ItemRecord;
typedef struct
{
uint8_t stateType; /**< Holds state (lower 4 bits) and type of monster
(upper 4 bits). */
uint8_t coords[2]; /**< monster position, in 1/4s of a square */
uint8_t stateType; /**< Holds state (lower 4 bits) and type of monster (upper
4 bits). */
uint8_t coords[2]; /**< monster position, in 1/4s of a square */
uint8_t health;
} SFG_MonsterRecord;
@ -289,7 +293,7 @@ typedef struct
typedef struct
{
uint8_t type;
uint8_t doubleFramesToLive; /**< This number times two (because 256 could be
uint8_t doubleFramesToLive; /**< This number times two (because 255 could be
too little at high FPS) says after how many
frames the projectile is destroyed. */
uint16_t position[3]; /**< Current position, stored as u16 to save space, as
@ -328,37 +332,32 @@ typedef struct
*/
struct
{
uint8_t state;
uint8_t state; ///< Current game state.
uint32_t stateChangeTime; ///< Time in ms at which the state was changed.
uint8_t currentRandom; ///< for RNG
uint8_t spriteAnimationFrame;
uint8_t soundsPlayedThisFrame; /**< Each bit says whether given sound was
played this frame, prevents playing too many
sounds at once. */
RCL_RayConstraints rayConstraints;
uint8_t keyStates[SFG_KEY_COUNT]; /**< Pressed states of keys, each value
stores the number of frames for which the
key has been held. */
uint8_t zBuffer[SFG_Z_BUFFER_SIZE];
uint8_t textureAverageColors[SFG_WALL_TEXTURE_COUNT]; /**< Contains average
color for each wall texture. */
int8_t backgroundScaleMap[SFG_GAME_RESOLUTION_Y];
uint16_t backgroundScroll;
uint8_t spriteSamplingPoints[SFG_MAX_SPRITE_SIZE]; /**< Helper for
precomputing sprite
sampling positions for
drawing. */
uint32_t frameTime; ///< Keeps a constant time (in ms) during a frame
uint32_t frame;
uint32_t frameTime; ///< time (in ms) of the current frame start
uint32_t frame; ///< frame number
uint8_t selectedMenuItem;
uint8_t selectedLevel; ///< Level to play selected in the main menu.
uint8_t antiSpam; ///< Prevents log message spamming.
uint8_t settings; /**< Dynamic game settings (can be changed at runtime),
uint8_t selectedLevel; ///< level to play selected in the main menu
uint8_t antiSpam; ///< Prevents log message spamming.
uint8_t settings; /**< dynamic game settings (can be changed at runtime),
bit meaning:
MSB -------- LSB
@ -367,7 +366,6 @@ struct
||\__ music
|\___ shearing
\____ freelook (shearing not sliding back) */
uint8_t blink; ///< Says whether blinkg is currently on or off.
uint8_t saved; /**< Helper variable to know if game was saved. Can be
0 (not saved), 1 (just saved) or 255 (can't save).*/
@ -407,19 +405,15 @@ struct
air. */
uint16_t headBobFrame;
uint8_t weapon; ///< currently selected weapon
uint8_t health;
uint32_t weaponCooldownFrames; ///< frames left for weapon cooldow
uint32_t weaponCooldownFrames; ///< frames left for weapon cooldow
uint32_t lastHurtFrame;
uint32_t lastItemTakenFrame;
uint8_t ammo[SFG_AMMO_TOTAL];
uint8_t cards; /**< Lowest 3 bits say which access cards have
been taken., the next 3 bits say which cards
should be blinking in the HUD, the last
2 bits are a blink reset counter. */
uint8_t cards; /**< Lowest 3 bits say which access cards
have been taken., the next 3 bits say
which cards should be blinking in the HUD,
the last 2 bits are a blink reset counter. */
uint8_t justTeleported;
} SFG_player;
@ -430,12 +424,10 @@ struct
{
const SFG_Level *levelPointer;
uint8_t levelNumber;
const uint8_t* textures[7];
const uint8_t* textures[7]; ///< textures the level is using
uint32_t timeStart;
uint32_t frameStart;
uint32_t completionTime10sOfS; ///< Completion time in 10th of second.
uint32_t completionTime10sOfS; ///< completion time in 10th of second
uint8_t floorColor;
uint8_t ceilingColor;
@ -459,7 +451,6 @@ struct
uint8_t teleportCount;
uint16_t mapRevealMask; /**< Bits say which parts of the map have been
revealed. */
uint8_t itemCollisionMap[(SFG_MAP_SIZE * SFG_MAP_SIZE) / 8];
/**< Bit array, for each map square says whether there
is a colliding item or not. */
@ -468,7 +459,8 @@ struct
#if SFG_ARDUINO
/**
Copy of the current level that is stored in RAM. This is only done on Arduino
because accessing it in program memory directly would be difficult.
because accessing it in program memory (PROGMEM) directly would be a pain.
Because of this Arduino needs more RAM.
*/
SFG_Level SFG_ramLevel;
#endif
@ -703,7 +695,7 @@ const uint8_t *SFG_getMonsterSprite(
{
uint8_t index =
state == SFG_MONSTER_STATE_DEAD ? 18 : 17;
// ^ makes the code smaller compared to returning pointers
// ^ makes the compiled binary smaller compared to returning pointers directly
if ((state != SFG_MONSTER_STATE_DYING) && (state != SFG_MONSTER_STATE_DEAD))
switch (monsterType)
@ -862,9 +854,7 @@ static inline uint8_t
textureIndex != 255 ?
SFG_currentLevel.textures[textureIndex] :
(SFG_wallTextures + SFG_currentLevel.levelPointer->doorTextureIndex
* SFG_TEXTURE_STORE_SIZE),
u / 32,
v / 32);
* SFG_TEXTURE_STORE_SIZE), u / 32, v / 32);
}
static inline uint8_t SFG_getTexelAverage(uint8_t textureIndex)
@ -4499,7 +4489,7 @@ void SFG_draw()
return;
}
if (SFG_keyPressed(SFG_KEY_MAP) || (SFG_game.state == SFG_GAME_STATE_MAP))
if (SFG_keyIsDown(SFG_KEY_MAP) || (SFG_game.state == SFG_GAME_STATE_MAP))
{
SFG_drawMap();
}

View File

@ -26,7 +26,9 @@
the game may actually become slowed down if FPS is set too high. Too high or
too low FPS can also negatively affect game speeds which are computed as
integers and rounding errors can occur soon, so don't set this to extreme
values (try to keep from 20 to 100).
values (try to keep between 20 to 100). FPS also determines the game
simulation step length, so different FPS values may result in very slight
differences in game behavior (not noticeable but affecting demos etc.).
*/
#ifndef SFG_FPS
#define SFG_FPS 60