mirror of
https://gitlab.com/drummyfish/anarch.git
synced 2024-12-21 23:08:49 -05:00
283 lines
7.5 KiB
C
Executable File
283 lines
7.5 KiB
C
Executable File
#include <stdint.h>
|
|
#include "constants.h"
|
|
#include "settings.h"
|
|
#include "levels.h"
|
|
#include "assets.h"
|
|
|
|
#define SFG_KEY_UP 0
|
|
#define SFG_KEY_RIGHT 1
|
|
#define SFG_KEY_DOWN 2
|
|
#define SFG_KEY_LEFT 3
|
|
#define SFG_KEY_A 4
|
|
#define SFG_KEY_B 5
|
|
#define SFG_KEY_C 6
|
|
|
|
/* ============================= PORTING =================================== */
|
|
|
|
/* When porting, define the following in your specific platform_*.h. Also you
|
|
have to call SFG_mainLoopBody() in the platform's main loop and SFG_init()
|
|
in the platform initialization. Also set specific settings in settings.h */
|
|
|
|
/** Return 1 (0) if given key is pressed (not pressed). */
|
|
int8_t SFG_keyPressed(uint8_t key);
|
|
|
|
/** Return time in ms sice program start. */
|
|
uint32_t SFG_getTimeMs();
|
|
|
|
/** Sleep (yield CPU) for specified amount of ms. This is used to relieve CPU
|
|
usage. If your platform doesn't need this or handles it in other way, this
|
|
function can do nothing. */
|
|
void SFG_sleepMs(uint16_t timeMs);
|
|
|
|
/** Set specified screen pixel. The function doesn't have to check whether
|
|
the coordinates are within screen. */
|
|
static inline void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex);
|
|
|
|
/* ========================================================================= */
|
|
|
|
/**
|
|
Game main loop body, call this inside the platform's specific main loop.
|
|
*/
|
|
void SFG_mainLoopBody();
|
|
|
|
/**
|
|
Initializes the whole program, call this in the platform initialization.
|
|
*/
|
|
void SFG_init();
|
|
|
|
#include "platform_sdl.h"
|
|
|
|
#define SFG_MS_PER_FRAME (1000 / SFG_FPS) // ms per frame with target FPS
|
|
|
|
#define RCL_PIXEL_FUNCTION SFG_pixelFunc
|
|
#define RCL_TEXTURE_VERTICAL_STRETCH 0
|
|
|
|
#include "raycastlib.h"
|
|
|
|
RCL_Camera SFG_camera;
|
|
RCL_RayConstraints SFG_rayConstraints;
|
|
|
|
uint8_t SFG_backgroundScaleMap[SFG_RESOLUTION_Y];
|
|
uint16_t SFG_backgroundScroll;
|
|
|
|
/**
|
|
Stores the current level and helper precomputed vaues for better performance.
|
|
*/
|
|
struct
|
|
{
|
|
const SFG_Map *mapPointer;
|
|
const SFG_Level *levelPointer;
|
|
const uint8_t* textures[7];
|
|
} SFG_currentLevel;
|
|
|
|
void SFG_setLevel(const SFG_Level *level)
|
|
{
|
|
SFG_currentLevel.levelPointer = level;
|
|
SFG_currentLevel.mapPointer = &(level->map);
|
|
|
|
for (uint8_t i = 0; i < 7; ++i)
|
|
SFG_currentLevel.textures[i] =
|
|
SFG_texturesWall[level->map.textureIndices[i]];
|
|
}
|
|
|
|
void SFG_pixelFunc(RCL_PixelInfo *pixel)
|
|
{
|
|
uint8_t color;
|
|
uint8_t shadow = 0;
|
|
|
|
if (pixel->isWall)
|
|
{
|
|
uint8_t textureIndex =
|
|
pixel->isFloor ?
|
|
(pixel->hit.type & 0x7) :
|
|
((pixel->hit.type & 0x38) >> 3);
|
|
|
|
color =
|
|
textureIndex != SFG_TILE_TEXTURE_TRANSPARENT ?
|
|
(SFG_getTexel(SFG_currentLevel.textures[pixel->hit.type],pixel->texCoords.x / 32,pixel->texCoords.y / 32)) :
|
|
SFG_TRANSPARENT_COLOR;
|
|
|
|
shadow = pixel->hit.direction >> 1;
|
|
}
|
|
else
|
|
{
|
|
color = pixel->isFloor ? 20 : 50;
|
|
}
|
|
|
|
if (color != SFG_TRANSPARENT_COLOR)
|
|
{
|
|
#if SFG_DITHERED_SHADOW
|
|
uint8_t fogShadow = (pixel->depth * 2) / RCL_UNITS_PER_SQUARE;
|
|
|
|
uint8_t fogShadowPart = fogShadow & 0x03;
|
|
uint8_t xMod2 = pixel->position.x & 0x01;
|
|
uint8_t yMod2 = pixel->position.y & 0x01;
|
|
|
|
fogShadow >>= 2;
|
|
|
|
if (((fogShadowPart == 1) && xMod2 && yMod2) ||
|
|
((fogShadowPart == 2) && (xMod2 == yMod2)) ||
|
|
((fogShadowPart == 3) && (xMod2 || yMod2)))
|
|
fogShadow += 1;
|
|
|
|
shadow += fogShadow;
|
|
#else
|
|
shadow += pixel->depth / (RCL_UNITS_PER_SQUARE * 2);
|
|
#endif
|
|
|
|
color = palette_minusValue(color,shadow);
|
|
}
|
|
else
|
|
{
|
|
color = SFG_getTexel(SFG_backgrounds[0],
|
|
SFG_backgroundScaleMap[(pixel->position.x + SFG_backgroundScroll) % SFG_RESOLUTION_Y],
|
|
SFG_backgroundScaleMap[pixel->position.y]);
|
|
}
|
|
|
|
SFG_setPixel(pixel->position.x,pixel->position.y,color);
|
|
}
|
|
|
|
RCL_Unit SFG_texturesAt(int16_t x, int16_t y)
|
|
{
|
|
SFG_TileDefinition tile = SFG_getMapTile(&(SFG_level0.map),x,y);
|
|
return SFG_TILE_FLOOR_TEXTURE(tile) | (SFG_TILE_CEILING_TEXTURE(tile) << 3);
|
|
// ^ store both textures (floor and ceiling) in one number
|
|
}
|
|
|
|
RCL_Unit SFG_floorHeightAt(int16_t x, int16_t y)
|
|
{
|
|
SFG_TileDefinition tile = SFG_getMapTile(&(SFG_level0.map),x,y);
|
|
|
|
return SFG_TILE_FLOOR_HEIGHT(tile) * (RCL_UNITS_PER_SQUARE / 4);
|
|
}
|
|
|
|
RCL_Unit SFG_ceilingHeightAt(int16_t x, int16_t y)
|
|
{
|
|
SFG_TileDefinition tile = SFG_getMapTile(&(SFG_level0.map),x,y);
|
|
|
|
uint8_t height = SFG_TILE_CEILING_HEIGHT(tile);
|
|
|
|
return height != SFG_TILE_CEILING_MAX_HEIGHT ?
|
|
((SFG_TILE_FLOOR_HEIGHT(tile) + height) * (RCL_UNITS_PER_SQUARE / 4)) :
|
|
(RCL_UNITS_PER_SQUARE * 32);
|
|
}
|
|
|
|
uint32_t SFG_frame;
|
|
uint32_t SFG_lastFrameTimeMs;
|
|
|
|
void SFG_init()
|
|
{
|
|
SFG_frame = 0;
|
|
SFG_lastFrameTimeMs = 0;
|
|
|
|
RCL_initCamera(&SFG_camera);
|
|
RCL_initRayConstraints(&SFG_rayConstraints);
|
|
|
|
SFG_camera.resolution.x = SFG_RESOLUTION_X;
|
|
SFG_camera.resolution.y = SFG_RESOLUTION_Y;
|
|
SFG_camera.height = RCL_UNITS_PER_SQUARE;
|
|
SFG_camera.position.x = RCL_UNITS_PER_SQUARE * 5;
|
|
SFG_camera.position.y = RCL_UNITS_PER_SQUARE * 5;
|
|
|
|
SFG_rayConstraints.maxHits = 10;
|
|
SFG_rayConstraints.maxSteps = 32;
|
|
|
|
for (uint16_t i = 0; i < SFG_RESOLUTION_Y; ++i)
|
|
SFG_backgroundScaleMap[i] = (i * SFG_TEXTURE_SIZE) / SFG_RESOLUTION_Y;
|
|
|
|
SFG_backgroundScroll = 0;
|
|
|
|
SFG_setLevel(&SFG_level0);
|
|
}
|
|
|
|
#define SFG_PLAYER_TURN_UNITS_PER_FRAME\
|
|
((SFG_PLAYER_TURN_SPEED * RCL_UNITS_PER_SQUARE) / (360 * SFG_FPS))
|
|
|
|
#define SFG_PLAYER_MOVE_UNITS_PER_FRAME\
|
|
((SFG_PLAYER_MOVE_SPEED * RCL_UNITS_PER_SQUARE) / SFG_FPS)
|
|
|
|
RCL_Vector2D playerDirection;
|
|
|
|
/**
|
|
Performs one game step (logic, physics), happening SFG_MS_PER_FRAME after
|
|
previous frame.
|
|
*/
|
|
void SFG_gameStep()
|
|
{
|
|
int8_t recomputeDirection = 0;
|
|
|
|
if (SFG_keyPressed(SFG_KEY_LEFT))
|
|
{
|
|
SFG_camera.direction -= SFG_PLAYER_TURN_UNITS_PER_FRAME;
|
|
recomputeDirection = 1;
|
|
}
|
|
else if (SFG_keyPressed(SFG_KEY_RIGHT))
|
|
{
|
|
SFG_camera.direction += SFG_PLAYER_TURN_UNITS_PER_FRAME;
|
|
recomputeDirection = 1;
|
|
}
|
|
|
|
if (recomputeDirection)
|
|
{
|
|
SFG_camera.direction = RCL_wrap(SFG_camera.direction,RCL_UNITS_PER_SQUARE);
|
|
|
|
playerDirection = RCL_angleToDirection(SFG_camera.direction);
|
|
|
|
playerDirection.x = (playerDirection.x * SFG_PLAYER_MOVE_UNITS_PER_FRAME) / RCL_UNITS_PER_SQUARE;
|
|
playerDirection.y = (playerDirection.y * SFG_PLAYER_MOVE_UNITS_PER_FRAME) / RCL_UNITS_PER_SQUARE;
|
|
|
|
SFG_backgroundScroll =
|
|
((SFG_camera.direction * 8) * SFG_RESOLUTION_Y) / RCL_UNITS_PER_SQUARE;
|
|
}
|
|
|
|
if (SFG_keyPressed(SFG_KEY_UP))
|
|
{
|
|
SFG_camera.position.x += playerDirection.x;
|
|
SFG_camera.position.y += playerDirection.y;
|
|
}
|
|
else if (SFG_keyPressed(SFG_KEY_DOWN))
|
|
{
|
|
SFG_camera.position.x -= playerDirection.x;
|
|
SFG_camera.position.y -= playerDirection.y;
|
|
}
|
|
|
|
if (SFG_keyPressed(SFG_KEY_A))
|
|
SFG_camera.height += SFG_PLAYER_MOVE_UNITS_PER_FRAME;
|
|
else if (SFG_keyPressed(SFG_KEY_B))
|
|
SFG_camera.height -= SFG_PLAYER_MOVE_UNITS_PER_FRAME;
|
|
}
|
|
|
|
void SFG_mainLoopBody()
|
|
{
|
|
/* standard deterministic game loop, independed on actuall achieved FPS,
|
|
each game logic (physics) frame is performed with the SFG_MS_PER_FRAME
|
|
delta time. */
|
|
|
|
uint32_t timeNow = SFG_getTimeMs();
|
|
uint16_t timeSinceLastFrame = timeNow - SFG_lastFrameTimeMs;
|
|
|
|
if (timeSinceLastFrame >= SFG_MS_PER_FRAME)
|
|
{
|
|
// perform game logic (physics), for each frame
|
|
while (timeSinceLastFrame >= SFG_MS_PER_FRAME)
|
|
{
|
|
SFG_gameStep();
|
|
|
|
timeSinceLastFrame -= SFG_MS_PER_FRAME;
|
|
|
|
SFG_frame++;
|
|
}
|
|
|
|
// render noly once
|
|
RCL_renderComplex(SFG_camera,SFG_floorHeightAt,SFG_ceilingHeightAt,SFG_texturesAt,SFG_rayConstraints);
|
|
|
|
SFG_lastFrameTimeMs = timeNow;
|
|
}
|
|
|
|
uint32_t timeNextFrame = timeNow + SFG_MS_PER_FRAME;
|
|
timeNow = SFG_getTimeMs();
|
|
|
|
if (timeNextFrame > timeNow)
|
|
SFG_sleepMs((timeNextFrame - timeNow) / 2); // wait, relieve CPU
|
|
}
|