Start map text format

This commit is contained in:
Miloslav Ciz 2024-11-27 20:30:54 +01:00
parent a388a915f9
commit 2cbbd8fb1a
5 changed files with 392 additions and 80 deletions

View file

@ -16,6 +16,13 @@
#include <stdint.h>
#include "map.h"
static const char *LCR_maps[] =
{
"LM;aaa; bbb; 2; #=s0s0#=s1s0#f4120"
};
/*
static const uint8_t map1[] =
{
LCR_MAP_MAGIC_NUMBER,
@ -64,6 +71,7 @@ LCR_MAP_BLOCK(LCR_BLOCK_CHECKPOINT_0,3,1,4,LCR_BLOCK_MATERIAL_CONCRETE,0),
LCR_MAP_BLOCK(LCR_BLOCK_CHECKPOINT_0,3,1,8,LCR_BLOCK_MATERIAL_CONCRETE,0),
LCR_MAP_BLOCK(LCR_BLOCK_FINISH,2,1,10,LCR_BLOCK_MATERIAL_CONCRETE,0),
*/
/*
LCR_MAP_BLOCK(LCR_BLOCK_FULL,2,1,9,LCR_BLOCK_MATERIAL_CONCRETE,0),
@ -85,10 +93,10 @@ LCR_MAP_BLOCK(LCR_BLOCK_CUBOID_FILL,10,1,15,0,0),
LCR_MAP_BLOCK(LCR_BLOCK_FULL,35,0,0,LCR_BLOCK_MATERIAL_DIRT,0),
LCR_MAP_BLOCK(LCR_BLOCK_CUBOID_FILL,10,1,15,0,0),
*/
LCR_MAP_TERMINATOR
};
*/
#define LCR_IMAGE_SIZE 64 ///< one-dimension resolution of bitmap image
#define LCR_IMAGE_STORE_SIZE (LCR_IMAGE_SIZE * LCR_IMAGE_SIZE + 256 * 2)

31
game.h
View file

@ -117,8 +117,17 @@ uint8_t LCR_gameStep(uint32_t timeMs);
#define LCR_CONTROL_MODE_FREECAM 0x00
#define LCR_CONTROL_MODE_DRIVE 0x01
#define LCR_GAME_STATE_MENU 0x00
#define LCR_GAME_STATE_RUN_STARTING 0x01
#define LCR_GAME_STATE_RUN 0x02
#define LCR_GAME_STATE_RUN_FINISHED 0x03
struct
{
uint8_t state;
uint32_t stateStartTime;
uint32_t time;
uint32_t nextRenderFrameTime;
uint32_t nextRacingTickTime;
uint8_t controlMode;
@ -163,12 +172,29 @@ static inline void LCR_drawPixelXYSafe(unsigned int x, unsigned int y,
LCR_drawPixelXYUnsafe(x,y,color);
}
void LCR_gameSetState(uint8_t state)
{
LCR_LOG1("changing state");
LCR_game.state = state;
LCR_game.stateStartTime = LCR_game.time;
}
void LCR_gameResetRun(void)
{
LCR_LOG0("resetting run");
LCR_game.checkpointsTaken = 0;
LCR_mapReset();
LCR_rendererUnmarkCPs();
LCR_gameSetState(LCR_GAME_STATE_RUN_STARTING);
}
//void LCR_gameStartRun(const uint8_t *map)
void LCR_gameStartRun(const char *mapStr)
{
LCR_mapLoadFromStr(mapStr);
LCR_rendererLoadMap();
LCR_gameResetRun();
LCR_racingRestart();
}
void LCR_gameInit(void)
@ -178,7 +204,6 @@ void LCR_gameInit(void)
for (int i = 0; i < LCR_KEYS_TOTAL; ++i)
LCR_keyStates[i] = 0;
LCR_mapLoad(map1);
LCR_rendererInit();
LCR_racingInit();
@ -188,7 +213,7 @@ void LCR_gameInit(void)
LCR_game.controlMode = LCR_CONTROL_MODE_FREECAM;
LCR_game.debugDraw = 0;
LCR_gameResetRun();
LCR_gameStartRun(LCR_maps[0]);
}
void LCR_gameEnd(void)
@ -200,6 +225,8 @@ uint8_t LCR_gameStep(uint32_t time)
{
LCR_LOG2("game step start");
LCR_game.time = time;
LCR_GameUnit carTransform[6];
for (int i = 0; i < LCR_KEYS_TOTAL; ++i)

394
map.h
View file

@ -43,8 +43,42 @@
The PREPROCESSED map format is similar, but only consists of block values but
there are only normal blocks (no special blocks) and they are sorted by their
coordinate number.
The TEXT FORMAT serves for editing maps in human readable format, it more or
less corresponds to the binary storage format and has the following structure:
- Magic number string: "LM;".
- Until next ';': name.
- Until next ';': description.
- Until next ';': tags. Currently this may contain the following:
- digit N: set the map environment to N.
- Then a series of block strings follow. Blocks may be separated by
characters that aren't '#'. Block format is following:
#BXYZMT
where:
- B: is block type
- X, Y and Z are block coordinates, each one a single character. The
following are characters signifying numbers 0 to 63:
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$@
- M is block material ('0' to '3')
- T is an optinal transform string consisting from 0 to 3 characters,
which may be:
- '|': flip horizontally
- '-': flip vertically
- 'L': rotate 90 degrees
- 'I': rotate 180 degrees
- 'J': rotate 270 degrees
*/
#define LCR_BLOCK_TRANSFORM_FLIP_H 0x10
#define LCR_BLOCK_TRANSFORM_ROT_90 0x20
#define LCR_BLOCK_TRANSFORM_ROT_180 0x40
@ -55,6 +89,8 @@
#define LCR_MAP_MAGIC_NUMBER1 'L'
#define LCR_MAP_MAGIC_NUMBER2 'M'
#define LCR_MAP_SEPARATOR ';'
#define LCR_MAP_MAGIC_NUMBER LCR_MAP_MAGIC_NUMBER1, LCR_MAP_MAGIC_NUMBER2
#define LCR_MAP_TERMINATOR 0xff
#define LCR_MAP_BLOCK(t,x,y,z,m,r) t,(uint8_t) (x | (y << 6)), \
@ -68,24 +104,38 @@
#define LCR_BLOCK_MATERIAL_DIRT 0x02
#define LCR_BLOCK_MATERIAL_ICE 0x03
#define LCR_MAP_COUNT 1
// normal blocks:
#define LCR_BLOCK_FULL 0x00 ///< completely filled block
#define LCR_BLOCK_BOTTOM 0x01 ///< filled bottom half
#define LCR_BLOCK_LEFT 0x02 ///< filled left half
#define LCR_BLOCK_BOTTOM_LEFT 0x03 ///< filled bottom left quarter
#define LCR_BLOCK_BOTTOM_LEFT_FRONT 0x04 ///< filled bottom left front eigth
#define LCR_BLOCK_RAMP 0x05 ///< plain ramp
#define LCR_BLOCK_RAMP_34 0x06 ///< plain ramp, 3/4 size
#define LCR_BLOCK_RAMP_12 0x07 ///< plain ramp, 1/2 size
#define LCR_BLOCK_RAMP_14 0x08 ///< plain ramp, 1/4 size
#define LCR_BLOCK_RAMP_CURVED_PLAT 0x09 ///< curved ramp with top platgform
#define LCR_BLOCK_RAMP_CURVED 0x0a ///< curv. ramp without top platf.
#define LCR_BLOCK_RAMP_CURVED_WALL 0x0b ///< curved ramp plus small wall
#define LCR_BLOCK_RAMP_STEEP 0x0c ///< extremely steep ramp
#define LCR_BLOCK_CORNER 0x0d ///< diagonal corner
#define LCR_BLOCK_CORNER_12 0x0e ///< diagonal corner (1/2 wide)
#define LCR_BLOCK_FULL 0x00 ///< completely filled block
#define LCR_BLOCK_BOTTOM 0x01 ///< filled bottom half
#define LCR_BLOCK_LEFT 0x02 ///< filled left half
#define LCR_BLOCK_BOTTOM_LEFT 0x03 ///< filled bottom left quarter
#define LCR_BLOCK_BOTTOM_LEFT_FRONT 0x04 ///< filled bottom left front eigth
#define LCR_BLOCK_RAMP 0x05 ///< plain ramp
#define LCR_BLOCK_RAMP_34 0x06 ///< plain ramp, 3/4 size
#define LCR_BLOCK_RAMP_12 0x07 ///< plain ramp, 1/2 size
#define LCR_BLOCK_RAMP_14 0x08 ///< plain ramp, 1/4 size
#define LCR_BLOCK_RAMP_CURVED_PLAT 0x09 ///< curved ramp with top platgform
#define LCR_BLOCK_RAMP_CURVED 0x0a ///< curv. ramp without top platf.
#define LCR_BLOCK_RAMP_CURVED_WALL 0x0b ///< curved ramp plus small wall
#define LCR_BLOCK_RAMP_STEEP 0x0c ///< extremely steep ramp
#define LCR_BLOCK_CORNER 0x0d ///< diagonal corner
#define LCR_BLOCK_CORNER_12 0x0e ///< diagonal corner (1/2 wide)
#define LCR_BLOCK_FULL_ACCEL 0x20
#define LCR_BLOCK_FULL_FAN 0x30
#define LCR_BLOCK_CHECKPOINT_0 0x40 ///< checkpoint, not taken
#define LCR_BLOCK_CHECKPOINT_1 0x41 ///< checkpoint, taken
#define LCR_BLOCK_FINISH 0x42 ///< finish
// special blocks:
#define LCR_BLOCK_NONE 0x80 ///< no block, e.g to make holes
#define LCR_BLOCK_CUBOID_FILL 0x81 /**< makes a cuboid from the
previously specified block, the
size is given by block coords */
#define LCR_BLOCK_CUBOID_HOLLOW 0x82 /**< same as cuboid special block,
but makes a hollow one */
#define LCR_BLOCK_START 0x83 ///< specifies start block position
/*
TODO:
@ -96,21 +146,7 @@
- bumpy road
*/
#define LCR_BLOCK_FULL_ACCEL 0x20
#define LCR_BLOCK_FULL_FAN 0x30
#define LCR_BLOCK_CHECKPOINT_0 0x40 ///< checkpoint, not taken
#define LCR_BLOCK_CHECKPOINT_1 0x41 ///< checkpoint, taken
#define LCR_BLOCK_FINISH 0x42 ///< finish
// special blocks:
#define LCR_BLOCK_NONE 0x80 ///< no block, e.g to make holes
#define LCR_BLOCK_CUBOID_FILL 0x81 /**< makes a cuboid from the
previously specified block, the
size is given by block coords */
#define LCR_BLOCK_CUBOID_HOLLOW 0x82 /**< same as cuboid special block,
but makes a hollow one */
#define LCR_BLOCK_START 0x83 ///< specifies start block position
#define LCR_MAP_BLOCK_CACHE_SIZE (8 * 2) /// do not change
@ -134,26 +170,6 @@ struct
// TODO: name, desc? possibly as a single '\n' separated string?
} LCR_currentMap;
static const uint8_t LCR_map0[] =
{
LCR_MAP_MAGIC_NUMBER,
77, 48, 10, // map name: M0
10, // map comment:
LCR_MAP_BLOCK( LCR_BLOCK_NONE, 3, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0),
LCR_MAP_BLOCK( LCR_BLOCK_FULL, 3, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0),
LCR_MAP_BLOCK( LCR_BLOCK_FULL, 0, 1, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0),
LCR_MAP_BLOCK( LCR_BLOCK_FULL, 2, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0),
LCR_MAP_BLOCK( LCR_BLOCK_FULL, 0, 1, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0),
LCR_MAP_BLOCK( LCR_BLOCK_FULL, 3, 0, 0, LCR_BLOCK_MATERIAL_CONCRETE, 0),
LCR_MAP_TERMINATOR
};
static const uint8_t *LCR_maps[LCR_MAP_COUNT] =
{
LCR_map0
};
void LCR_makeMapBlock(uint8_t type, uint8_t x, uint8_t y, uint8_t z,
uint8_t material, uint8_t transform, uint8_t block[LCR_BLOCK_SIZE])
{
@ -252,14 +268,17 @@ uint8_t *LCR_getMapBlockAtCoordNumber(uint32_t coord)
/**
Adds given block to current map, including possibly deleting a block by
adding LCR_BLOCK_NONE. The function handles sorting the block to the right
position. Returns 1 on success, else 0.
position. Returns pointer to the block on success, else 0.
*/
uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
uint8_t *_LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
{
LCR_LOG2("adding map block");
if (LCR_currentMap.blockCount >= LCR_SETTING_MAP_MAX_BLOCKS)
{
LCR_LOG0("couldn't add block");
return 0;
}
uint32_t coord = LCR_mapBlockGetCoordNumber(block);
uint16_t insertAt = 0;
@ -269,6 +288,8 @@ uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
insertAt * LCR_BLOCK_SIZE))
insertAt++;
uint8_t *result = LCR_currentMap.blocks + insertAt * LCR_BLOCK_SIZE;
if (block[0] == LCR_BLOCK_NONE)
{
if (insertAt < LCR_currentMap.blockCount &&
@ -283,7 +304,7 @@ uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
LCR_currentMap.blockCount--;
}
return 1;
return result;
}
if (insertAt == LCR_currentMap.blockCount ||
@ -306,7 +327,7 @@ uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
for (uint8_t j = 0; j < LCR_BLOCK_SIZE; ++j)
LCR_currentMap.blocks[insertAt + j] = block[j];
return 1;
return result;
}
/**
@ -321,9 +342,265 @@ void LCR_mapReset(void)
LCR_currentMap.blocks[i * LCR_BLOCK_SIZE] = LCR_BLOCK_CHECKPOINT_0;
}
uint8_t _LCR_mapCharToBlockType(char c)
{
switch (c)
{
case '=': return LCR_BLOCK_FULL; break;
case '-': return LCR_BLOCK_BOTTOM; break;
case ';': return LCR_BLOCK_LEFT; break;
case ',': return LCR_BLOCK_BOTTOM_LEFT; break;
case '.': return LCR_BLOCK_BOTTOM_LEFT_FRONT; break;
case '^': return LCR_BLOCK_RAMP; break;
case '/': return LCR_BLOCK_RAMP_34; break;
case '<': return LCR_BLOCK_RAMP_12; break;
case '_': return LCR_BLOCK_RAMP_14; break;
case ']': return LCR_BLOCK_RAMP_CURVED_PLAT; break;
case ')': return LCR_BLOCK_RAMP_CURVED; break;
case '}': return LCR_BLOCK_RAMP_CURVED_WALL; break;
case '|': return LCR_BLOCK_RAMP_STEEP; break;
case 'A': return LCR_BLOCK_CORNER; break;
case '\\': return LCR_BLOCK_CORNER_12; break;
case '>': return LCR_BLOCK_FULL_ACCEL; break;
case 'o': return LCR_BLOCK_FULL_FAN; break;
case '+': return LCR_BLOCK_CHECKPOINT_0; break;
case '!': return LCR_BLOCK_FINISH; break;
case 'x': return LCR_BLOCK_NONE; break;
case 'f': return LCR_BLOCK_CUBOID_FILL; break;
case 'h': return LCR_BLOCK_CUBOID_HOLLOW; break;
case '*': return LCR_BLOCK_START; break;
default:
LCR_LOG1("unknown block char");
return LCR_BLOCK_NONE; break;
}
}
int _LCR_mapCharToCoord(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'z')
return c - 'a' + 10;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 36;
if (c == '@')
return 62;
if (c == '&')
return 63;
return -1;
}
uint8_t LCR_mapLoadFromStr(const char *mapStr)
{
LCR_LOG0("loading map string");
const uint8_t *prevBlock = 0;
for (int i = 0; i < 4; ++i)
LCR_currentMap.startPos[i] = 0;
LCR_currentMap.checkpointCount = 0;
LCR_currentMap.blockCount = 0;
LCR_currentMap.environment = 0;
if (mapStr[0] != LCR_MAP_MAGIC_NUMBER1 || mapStr[1] != LCR_MAP_MAGIC_NUMBER2
|| mapStr[2] != LCR_MAP_SEPARATOR)
{
LCR_LOG0("bad magic number");
return 0;
}
mapStr += 3;
while (*mapStr != LCR_MAP_SEPARATOR) // read map name
{
if (mapStr[0] == 0)
return 0;
// TODO
mapStr++;
}
mapStr++;
while (*mapStr != LCR_MAP_SEPARATOR) // read map description
{
if (mapStr[0] == 0)
return 0;
// TODO
mapStr++;
}
mapStr++;
while (*mapStr != LCR_MAP_SEPARATOR) // read map tags
{
if (mapStr[0] >= '0' && mapStr[0] <= '9')
LCR_currentMap.environment = mapStr[0] - '0';
else if (mapStr[0] == 0)
return 0;
// TODO
mapStr++;
}
mapStr++;
while (*mapStr)
{
if (*mapStr == '#')
{
mapStr++;
uint8_t block = _LCR_mapCharToBlockType(*mapStr);
uint8_t trans = 0;
uint8_t mat = 0;
int coords[3];
for (int i = 0; i < 3; ++i)
{
mapStr++;
coords[i] = _LCR_mapCharToCoord(*mapStr);
if (coords[i] < 0)
{
LCR_LOG0("bad coord");
return 0;
}
}
mapStr++;
if (*mapStr >= '0' && *mapStr <= '3')
mat = *mapStr - '0';
else
{
LCR_LOG0("bad material");
return 0;
}
mapStr++;
while (1)
{
if (*mapStr == '|')
trans |= LCR_BLOCK_TRANSFORM_FLIP_H;
else if (*mapStr == '-')
trans |= LCR_BLOCK_TRANSFORM_FLIP_V;
else if (*mapStr == 'L')
trans |= LCR_BLOCK_TRANSFORM_ROT_90;
else if (*mapStr == 'I')
trans |= LCR_BLOCK_TRANSFORM_ROT_180;
else if (*mapStr == 'J')
trans |= LCR_BLOCK_TRANSFORM_ROT_270;
else
break;
mapStr++;
}
switch (block)
{
case LCR_BLOCK_CUBOID_FILL:
case LCR_BLOCK_CUBOID_HOLLOW:
{
uint8_t x, y, z, mat, transform;
uint8_t tmpBlock[LCR_BLOCK_SIZE];
if (prevBlock == 0 || LCR_currentMap.blockCount == 0)
{
LCR_LOG0("no previous block");
return 0;
}
mat = LCR_mapBlockGetMaterial(prevBlock);
transform = LCR_mapBlockGetTransform(prevBlock);
LCR_mapBlockGetCoords(prevBlock,&x,&y,&z);
for (uint8_t k = 0; k < coords[2]; ++k)
for (uint8_t j = 0; j < coords[1]; ++j)
for (uint8_t i = 0; i < coords[0]; ++i)
if (block == LCR_BLOCK_CUBOID_FILL ||
k == 0 || k == coords[2] - 1 ||
j == 0 || j == coords[1] - 1 ||
i == 0 || i == coords[0] - 1)
{
LCR_makeMapBlock(prevBlock[0],x + i,y + j,z + k,mat,transform,
tmpBlock);
prevBlock = _LCR_mapAddBlock(tmpBlock);
if (!prevBlock)
return 0;
}
break;
}
case LCR_BLOCK_START:
LCR_currentMap.startPos[0] = coords[0];
LCR_currentMap.startPos[1] = coords[1];
LCR_currentMap.startPos[2] = coords[2];
LCR_currentMap.startPos[3] = trans & 0x60;
break;
case LCR_BLOCK_CHECKPOINT_0:
LCR_currentMap.checkpointCount++;
// fall through
default: // normal block
{
uint8_t tmpBlock[LCR_BLOCK_SIZE];
LCR_makeMapBlock(block,coords[0],coords[1],coords[2],mat,trans,
tmpBlock);
prevBlock = _LCR_mapAddBlock(tmpBlock);
if (prevBlock == 0)
return 0;
break;
}
}
}
if (*mapStr != '#')
mapStr++;
}
LCR_LOG2("clearing map block cache")
for (int i = 0; i < LCR_MAP_BLOCK_CACHE_SIZE; ++i)
_LCR_mapBlockCache[i] = 0xffffffff;
LCR_LOG2("map loaded")
LCR_mapReset();
return 1;
}
/**
Loads and preprocesses given map. Returns 1 on success, otherwise 0.
*/
/*
uint8_t LCR_mapLoad(const uint8_t *map)
{
LCR_LOG0("loading map")
@ -430,6 +707,13 @@ uint8_t LCR_mapLoad(const uint8_t *map)
return 1;
}
*/
/**
Same as LCR_mapGetBlockAt, but allows to specify start and end block of the
of the search to make it faster.

View file

@ -430,10 +430,6 @@ LCR_racing.carNotOKCount = 0;
// TODO
}
/**
Initializes the racing module, only call once.
*/
@ -468,8 +464,6 @@ void LCR_racingInit(void)
function with large min. distance which would lead to slow iteration over
all map blocks. */
LCR_racing.carBody.flags |= TPE_BODY_FLAG_NO_BSPHERE;
LCR_racingRestart();
}
/**

View file

@ -848,22 +848,9 @@ void LCR_rendererMarkTakenCP(int x, int y, int z)
}
}
/**
Call to reset currently loaded map, i.e. mark all checkpoints as untaken etc.
*/
void LCR_rendererRestart(void)
uint8_t LCR_rendererLoadMap(void)
{
for (int i = 0; i < LCR_renderer.mapModel.triangleCount; ++i)
if ((LCR_renderer.mapTriangleData[i] & 0x0f) == LCR_RENDERER_MAT_CP1)
LCR_renderer.mapTriangleData[i] = (LCR_renderer.mapTriangleData[i] & 0xf0)
| LCR_RENDERER_MAT_CP0;
}
uint8_t LCR_rendererInit(void)
{
LCR_LOG0("initializing renderer");
LCR_renderer.frame = 0;
LCR_LOG0("loading map");
if (!_LCR_buildMapModel())
return 0;
@ -871,6 +858,18 @@ uint8_t LCR_rendererInit(void)
_LCR_makeMapChunks();
_LCR_rendererComputeLOD();
return 1;
}
/**
Initializes renderer, only call once.
*/
uint8_t LCR_rendererInit(void)
{
LCR_LOG0("initializing renderer");
LCR_renderer.frame = 0;
LCR_renderer.carModel = LCR_renderer.models + 8;
LCR_renderer.ghostModel = LCR_renderer.models + 9;
@ -1557,7 +1556,7 @@ void LCR_rendererDraw(void)
_LCR_rendererLoadMapChunks(); // TODO: call only once in a while?
LCR_rendererDrawSky(1,
LCR_rendererDrawSky(LCR_currentMap.environment,
LCR_renderer.scene.camera.transform.rotation.y,
-4 * LCR_renderer.scene.camera.transform.rotation.x);