Licar/map.h

478 lines
14 KiB
C
Raw Normal View History

2023-07-21 21:17:49 +02:00
#ifndef _LCR_MAP
#define _LCR_MAP
#include <stdint.h>
2023-08-03 21:12:23 +02:00
#include "constants.h"
#include "settings.h"
2023-07-21 21:17:49 +02:00
/**
The map (track) module for Licar.
2023-07-23 16:51:09 +02:00
Map coordinates/size:
- map size is 64x64x64 blocks
- [0,0,0] is is bottom-left-front-most
- x goes right, y goes up, z goes forward
- coordinate number is a single number obtained as x + 64 * y + 64 * 64 * z
2023-08-03 21:12:23 +02:00
The STORAGE map format is binary and consists of the following values:
2023-07-23 16:51:09 +02:00
- 76, 77 (for "LM", magic number)
2023-08-08 16:17:51 +02:00
- one byte recording the map environment
2023-07-23 16:51:09 +02:00
- ASCII map name
- 10 (separator)
- ASCII comment
- 10 (separator)
- block values, each one in the format:
- 1 byte type: says the type of block. If the highest bit is 0, the block
2023-08-03 21:12:23 +02:00
is normal, otherwise it is a special block (will be preprocessed)
2023-07-23 16:51:09 +02:00
- 3 bytes: A, B, C, such that:
- A, B and lowest 2 bits of C form the block coordinate number (A being
the lowest part etc.)
- bits C2 and C3 say the block material
- highest 4 bits of C (C4, C5, C6, C7) say the block's transform:
- first if C4 is set, the block is flipped in the X direction
- then the block is rotated around vertical axis by 0, 90, 180 or 270
degrees if C5C6 is 00, 01, 10 or 11.
- last if C7 is set, the block is flipped vertically
- 255 (terminator)
In this format order of blocks matters, latter blocks will replace previous
blocks placed on the same coordinate. Internally the map will be preprocessed
to RAM when loaded so that thing like the magic number and special blocks are
removed and the remaining blocks will be sorted for fast block searching.
2023-08-03 21:12:23 +02:00
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.
2023-07-21 21:17:49 +02:00
*/
2023-07-23 16:51:09 +02:00
#define LCR_BLOCK_TRANSFORM_FLIP_H 0x10
#define LCR_BLOCK_TRANSFORM_ROT_90 0x20
#define LCR_BLOCK_TRANSFORM_ROT_180 0x40
#define LCR_BLOCK_TRANSFORM_ROT_270 0x60
#define LCR_BLOCK_TRANSFORM_FLIP_V 0x80
2024-07-24 20:28:57 +02:00
#define LCR_BLOCK_XYZ_TO_COORD(x,y,z) // ???
2023-07-23 16:51:09 +02:00
2023-08-03 21:12:23 +02:00
#define LCR_MAP_MAGIC_NUMBER1 'L'
#define LCR_MAP_MAGIC_NUMBER2 'M'
#define LCR_MAP_MAGIC_NUMBER LCR_MAP_MAGIC_NUMBER1, LCR_MAP_MAGIC_NUMBER2
2023-07-23 16:51:09 +02:00
#define LCR_MAP_TERMINATOR 0xff
2024-07-24 23:16:13 +02:00
#define LCR_MAP_BLOCK(t,x,y,z,m,r) t,(uint8_t) (x | (y << 6)), \
(uint8_t) ((y >> 2) | (z << 4)), \
(uint8_t) ((z >> 4) | (m << 2) | (r))
2023-07-23 16:51:09 +02:00
2024-07-24 20:28:57 +02:00
#define LCR_BLOCK_SIZE 4 ///< size of map block, in bytes
2023-07-23 16:51:09 +02:00
#define LCR_BLOCK_MATERIAL_CONCRETE 0x00
2024-08-01 21:41:21 +02:00
#define LCR_BLOCK_MATERIAL_GRASS 0x01
#define LCR_BLOCK_MATERIAL_DIRT 0x02
#define LCR_BLOCK_MATERIAL_ICE 0x03
2023-07-21 21:17:49 +02:00
2023-07-23 16:51:09 +02:00
#define LCR_MAP_COUNT 1
2023-08-03 21:12:23 +02:00
// normal blocks:
2024-07-24 20:28:57 +02:00
#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
#define LCR_BLOCK_RAMP_HALF 0x06
#define LCR_BLOCK_RAMP_CURVED 0x07
#define LCR_BLOCK_RAMP_CURVED_SHORT 0x08
#define LCR_BLOCK_RAMP_CURVED_WALL 0x09
2023-08-03 21:12:23 +02:00
2024-08-01 21:41:21 +02:00
#define LCR_BLOCK_FULL_ACCEL 0x40
#define LCR_BLOCK_FULL_STICKER 0x60
#define LCR_BLOCK_CHECKPOINT_0 0x10 ///< checkpoint, not taken
#define LCR_BLOCK_CHECKPOINT_1 0x11 ///< checkpoint, taken
#define LCR_BLOCK_FINISH 0x12 ///< finish
2023-08-03 21:12:23 +02:00
// special blocks:
#define LCR_BLOCK_NONE 0x80 /**< no block, can be used 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 coordinates */
#define LCR_BLOCK_CUBOID_HOLLOW 0x82 /**< same as the cuboid special block, but
makes a hollow cuboid */
#define LCR_BLOCK_START 0x83 /**< specifies start block position */
2023-07-21 21:17:49 +02:00
2023-08-03 21:12:23 +02:00
struct
2023-07-21 21:17:49 +02:00
{
2023-08-03 21:12:23 +02:00
uint16_t blockCount;
2024-07-24 20:28:57 +02:00
uint8_t blocks[LCR_SETTING_MAP_MAX_SIZE * LCR_BLOCK_SIZE];
2023-08-03 21:12:23 +02:00
uint32_t startPos;
2023-08-08 16:17:51 +02:00
uint8_t environment;
2023-08-03 21:12:23 +02:00
// TODO: name, desc? possibly as a single '\n' separated string?
} LCR_currentMap;
2023-07-21 21:17:49 +02:00
2023-07-23 16:51:09 +02:00
static const uint8_t LCR_map0[] =
{
LCR_MAP_MAGIC_NUMBER,
2023-08-03 21:12:23 +02:00
77, 48, 10, // map name: M0
2023-07-23 16:51:09 +02:00
10, // map comment:
2023-08-03 21:12:23 +02:00
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),
2023-07-23 16:51:09 +02:00
LCR_MAP_TERMINATOR
};
static const uint8_t *LCR_maps[LCR_MAP_COUNT] =
{
LCR_map0
};
2024-07-24 20:28:57 +02:00
void LCR_mapBlockGetCoords(const uint8_t block[LCR_BLOCK_SIZE],
uint8_t *x, uint8_t *y, uint8_t *z)
2023-08-03 21:12:23 +02:00
{
*x = block[1] & 0x3f;
*y = (block[1] >> 6) | ((block[2] & 0x0f) << 2);
*z = (block[2] >> 4) | ((block[3] & 0x03) << 4);
}
2024-07-24 23:16:13 +02:00
uint8_t LCR_mapBlockGetTransform(const uint8_t block[LCR_BLOCK_SIZE])
{
return block[3] & 0xf0;
}
2024-08-01 21:41:21 +02:00
uint8_t LCR_mapBlockGetMaterial(const uint8_t block[LCR_BLOCK_SIZE])
{
return (block[3] >> 2) & 0x03;
}
2024-07-24 20:28:57 +02:00
uint32_t LCR_mapBlockGetCoordNumber(const uint8_t block[LCR_BLOCK_SIZE])
2023-08-03 21:12:23 +02:00
{
return block[1] | (((uint32_t) block[2]) << 8) |
((((uint32_t) block[3]) & 0x3) << 16);
}
2023-08-08 16:17:51 +02:00
uint8_t *LCR_getMapBlockAtCoordNumber(uint32_t coord)
{
// binary search the block:
uint16_t a = 0, b = LCR_currentMap.blockCount - 1;
while (b >= a)
{
uint16_t mid = (a + b) / 2;
2024-07-24 20:28:57 +02:00
uint8_t *block = LCR_currentMap.blocks + mid * LCR_BLOCK_SIZE;
2023-08-08 16:17:51 +02:00
uint32_t coord2 =
LCR_mapBlockGetCoordNumber(block);
if (coord2 == coord)
return block;
else if (coord2 > coord)
b = mid - 1;
else
a = mid + 1;
}
return 0;
}
2023-08-03 21:12:23 +02:00
/**
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.
*/
2024-07-24 20:28:57 +02:00
uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
2023-08-03 21:12:23 +02:00
{
if (LCR_currentMap.blockCount >= LCR_SETTING_MAP_MAX_SIZE)
return 0;
uint32_t coord = LCR_mapBlockGetCoordNumber(block);
uint16_t insertAt = 0;
while (insertAt < LCR_currentMap.blockCount &&
2024-07-24 20:28:57 +02:00
coord > LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks +
insertAt * LCR_BLOCK_SIZE))
2023-08-03 21:12:23 +02:00
insertAt++;
if (block[0] == LCR_BLOCK_NONE)
{
if (insertAt < LCR_currentMap.blockCount &&
2024-07-24 20:28:57 +02:00
coord == LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks +
insertAt * LCR_BLOCK_SIZE))
2023-08-03 21:12:23 +02:00
{
// shift all left (remove the block):
2024-07-24 20:28:57 +02:00
for (uint16_t i = insertAt * LCR_BLOCK_SIZE;
i < LCR_currentMap.blockCount * LCR_BLOCK_SIZE - 1; ++i)
2023-08-03 21:12:23 +02:00
LCR_currentMap.blocks[i] = LCR_currentMap.blocks[i + 1];
LCR_currentMap.blockCount--;
}
return 1;
}
if (insertAt == LCR_currentMap.blockCount ||
2024-07-24 20:28:57 +02:00
coord != LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks +
insertAt * LCR_BLOCK_SIZE))
2023-08-03 21:12:23 +02:00
{
// shift from here to the right, make room for the new block
LCR_currentMap.blockCount++;
2024-07-24 20:28:57 +02:00
for (int16_t i = ((int16_t) LCR_currentMap.blockCount) - 1;
i > insertAt; i--)
for (uint8_t j = 0; j < LCR_BLOCK_SIZE; ++j)
LCR_currentMap.blocks[i * LCR_BLOCK_SIZE + j] =
LCR_currentMap.blocks[(i - 1) * LCR_BLOCK_SIZE + j];
2023-08-03 21:12:23 +02:00
}
2024-07-24 20:28:57 +02:00
insertAt *= LCR_BLOCK_SIZE;
2023-08-03 21:12:23 +02:00
2024-07-24 20:28:57 +02:00
for (uint8_t j = 0; j < LCR_BLOCK_SIZE; ++j)
2023-08-03 21:12:23 +02:00
LCR_currentMap.blocks[insertAt + j] = block[j];
return 1;
}
/**
Loads and preprocesses given map. Returns 1 on success, otherwise 0.
*/
uint8_t LCR_mapLoad(const uint8_t *map)
{
LCR_currentMap.startPos = 0;
LCR_currentMap.blockCount = 0;
if (map[0] != LCR_MAP_MAGIC_NUMBER1 || map[1] != LCR_MAP_MAGIC_NUMBER2)
return 1;
map += 2;
while (*map != 10) // read map name
{
// TODO
map++;
}
map++;
while (*map != 10) // read map description
{
// TODO
map++;
}
map++;
2023-08-08 16:17:51 +02:00
LCR_currentMap.environment = *map;
map++;
2023-08-03 21:12:23 +02:00
while (*map != LCR_MAP_TERMINATOR)
{
if (!_LCR_mapAddBlock(map))
return 0;
map += 4;
}
// process and remove special blocks:
// TODO
// sort the blocks (for fast searching):
// TODO
return 1;
}
/**
Gets a pointer to a map block of the currently loaded map at given
coordinates. If there is no block at given coordinates, 0 is returned.
*/
const uint8_t *LCR_mapGetBlockAt(uint8_t x, uint8_t y, uint8_t z)
{
return 0;
}
2024-07-23 19:57:29 +02:00
uint8_t _LCR_encodeMapBlockCoords(uint8_t x, uint8_t y, uint8_t z)
{
return (5 * 7) * z + 7 * y + x;
}
void _LCR_decodeMapBlockCoords(uint8_t byte, uint8_t *x, uint8_t *y, uint8_t *z)
{
*x = (byte % 7);
*y = ((byte / 7) % 5);
*z = (byte / 35);
}
2024-08-01 21:41:21 +02:00
#define LCR_BLOCK_SHAPE_COORD_MAX 12
2024-07-23 19:57:29 +02:00
/**
Decodes XYZ coordinates encoded in a byte returned by LCR_mapGetBlockShape.
Each coordinate will be in range 0 to 12 (including both). This unusual range
is intentional as it for example has an exact mid value.
*/
void LCR_decodeMapBlockCoords(uint8_t byte, uint8_t *x, uint8_t *y, uint8_t *z)
{
_LCR_decodeMapBlockCoords(byte,x,y,z);
*x *= 2;
*y *= 3;
*z *= 2;
}
2024-07-22 01:16:16 +02:00
void _LCR_addBlockShapeByte(uint8_t *bytes, uint8_t *byteCount,
int x, int y, int z)
{
if (*byteCount >= LCR_MAP_BLOCK_SHAPE_MAX_BYTES)
return;
2024-07-23 19:57:29 +02:00
bytes[*byteCount] = _LCR_encodeMapBlockCoords(x,y,z);
2024-07-22 01:16:16 +02:00
*byteCount += 1;
}
2023-08-03 21:12:23 +02:00
/**
Gets a shape of given map block type as a 3D model composed of triangles. The
2024-07-22 01:16:16 +02:00
model is returned as an array of byte triplets (triangles), with each byte
representing one coordinate. These coordinates can be decoded with
LCR_decodeMapBlockCoords function.
2023-08-03 21:12:23 +02:00
*/
void LCR_mapGetBlockShape(uint8_t blockType, uint8_t transform,
2024-07-22 01:16:16 +02:00
uint8_t bytes[LCR_MAP_BLOCK_SHAPE_MAX_BYTES], uint8_t *byteCount)
{
/*
The coordinate format is following: byte B specifies coordinates X (0 to 6)
2024-07-23 19:57:29 +02:00
= B % 7, Y (vertical, 0 to 4) = (B / 7) % 5, Z (0 to 6) = B % 35. Helper
side view grid:
4 . . . . . . . ^
3 . . . . . . . | y
2 . . . . . . .
1 . . . . . . .
0 . . . . . . .
0 1 2 3 4 5 6 -> x/z
2024-07-22 01:16:16 +02:00
*/
*byteCount = 0;
2024-07-23 19:57:29 +02:00
#define ADD(a,b,c) _LCR_addBlockShapeByte(bytes,byteCount,a,b,c);
2024-07-22 01:16:16 +02:00
switch (blockType)
{
case LCR_BLOCK_FULL:
2024-07-24 20:28:57 +02:00
case LCR_BLOCK_BOTTOM:
case LCR_BLOCK_LEFT:
case LCR_BLOCK_BOTTOM_LEFT:
case LCR_BLOCK_BOTTOM_LEFT_FRONT:
2024-08-01 21:41:21 +02:00
case LCR_BLOCK_FULL_ACCEL:
case LCR_BLOCK_FULL_STICKER:
2024-07-24 20:28:57 +02:00
{
uint8_t xRight = 6, yTop = 4,
zBack = 6 >> (blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT);
if (blockType == LCR_BLOCK_BOTTOM || blockType == LCR_BLOCK_BOTTOM_LEFT ||
blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT)
yTop /= 2;
if (blockType == LCR_BLOCK_LEFT ||
blockType == LCR_BLOCK_BOTTOM_LEFT ||
blockType == LCR_BLOCK_BOTTOM_LEFT_FRONT)
xRight /= 2;
ADD(0,0,0) ADD(xRight,0,0) ADD(xRight,yTop,0) // front
ADD(0,0,0) ADD(xRight,yTop,0) ADD(0,yTop,0)
ADD(xRight,0,0) ADD(xRight,0,zBack) ADD(xRight,yTop,zBack) // right
ADD(xRight,0,0) ADD(xRight,yTop,zBack) ADD(xRight,yTop,0)
ADD(0,0,0) ADD(0,yTop,0) ADD(0,yTop,zBack) // left
ADD(0,0,0) ADD(0,yTop,zBack) ADD(0,0,zBack)
ADD(0,0,zBack) ADD(0,yTop,zBack) ADD(xRight,yTop,zBack) // back
ADD(0,0,zBack) ADD(xRight,yTop,zBack) ADD(xRight,0,zBack)
ADD(0,yTop,0) ADD(xRight,yTop,0) ADD(xRight,yTop,zBack) // top
ADD(0,yTop,0) ADD(xRight,yTop,zBack) ADD(0,yTop,zBack)
ADD(0,0,0) ADD(xRight,0,zBack) ADD(xRight,0,0) // bottom
ADD(0,0,0) ADD(0,0,zBack) ADD(xRight,0,zBack)
break;
}
case LCR_BLOCK_RAMP_CURVED_WALL:
2024-07-23 19:57:29 +02:00
ADD(0,0,0) ADD(5,0,0) ADD(0,1,3) // ramp
ADD(0,1,3) ADD(5,0,0) ADD(5,1,3)
ADD(0,1,3) ADD(5,1,3) ADD(0,2,4) // ramp
ADD(0,2,4) ADD(5,1,3) ADD(5,2,4)
ADD(0,2,4) ADD(5,2,4) ADD(0,4,5) // ramp
ADD(0,4,5) ADD(5,2,4) ADD(5,4,5)
ADD(0,4,5) ADD(5,4,5) ADD(0,4,6) // top
ADD(0,4,6) ADD(5,4,5) ADD(6,4,6)
ADD(5,4,5) ADD(6,4,0) ADD(6,4,6) // top
ADD(5,4,5) ADD(5,4,0) ADD(6,4,0)
ADD(5,4,0) ADD(5,4,5) ADD(5,2,4) // inner side
ADD(5,4,0) ADD(5,2,4) ADD(5,1,3)
ADD(5,4,0) ADD(5,1,3) ADD(5,0,0)
ADD(5,4,0) ADD(5,0,0) ADD(6,4,0) // front
ADD(6,4,0) ADD(5,0,0) ADD(6,0,0)
ADD(6,4,0) ADD(6,0,0) ADD(6,4,6) // right
ADD(6,4,6) ADD(6,0,0) ADD(6,0,6)
ADD(0,0,6) ADD(0,4,5) ADD(0,4,6) // left
ADD(0,0,6) ADD(0,2,4) ADD(0,4,5)
ADD(0,0,6) ADD(0,1,3) ADD(0,2,4)
ADD(0,0,6) ADD(0,0,0) ADD(0,1,3)
ADD(0,0,6) ADD(0,4,6) ADD(6,0,6) // back
ADD(6,0,6) ADD(0,4,6) ADD(6,4,6)
ADD(0,0,0) ADD(6,0,6) ADD(6,0,0) // bottom
ADD(0,0,0) ADD(0,0,6) ADD(6,0,6)
2024-07-22 01:16:16 +02:00
break;
2024-07-24 20:28:57 +02:00
default: break;
2024-07-22 01:16:16 +02:00
}
2024-07-23 19:57:29 +02:00
if (transform)
{
for (int i = 0; i < *byteCount; ++i)
{
uint8_t x, y, z, tmp;
_LCR_decodeMapBlockCoords(bytes[i],&x,&y,&z);
if (transform & LCR_BLOCK_TRANSFORM_FLIP_H)
x = 6 - x;
if (transform & 0x20) // for both 90 and 270
{
tmp = z;
z = x;
x = 6 - tmp;
}
if (transform & 0x40) // for both 180 and 270
{
x = 6 - x;
z = 6 - z;
}
if (transform & LCR_BLOCK_TRANSFORM_FLIP_V)
y = 4 - y;
bytes[i] = _LCR_encodeMapBlockCoords(x,y,z);
}
if (((transform & LCR_BLOCK_TRANSFORM_FLIP_H) == 0) !=
((transform & LCR_BLOCK_TRANSFORM_FLIP_V) == 0)) // flip triangles
for (int i = 0; i < *byteCount; i += 3)
{
uint8_t tmp = bytes[i];
bytes[i] = bytes[i + 1];
bytes[i + 1] = tmp;
}
}
#undef ADD
2023-08-03 21:12:23 +02:00
}
2023-07-21 21:17:49 +02:00
#endif // guard