885 lines
26 KiB
C
885 lines
26 KiB
C
#ifndef _LCR_MAP
|
|
#define _LCR_MAP
|
|
|
|
#include <stdint.h>
|
|
#include "constants.h"
|
|
#include "settings.h"
|
|
|
|
/**
|
|
The map (track) module for Licar.
|
|
|
|
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
|
|
|
|
The TEXT format serves for editing maps in human readable format, it more or
|
|
less corresponds to the binary storage format (below) with some exceptions.
|
|
It 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 follows. Blocks may be separated by
|
|
characters that aren't '#' (comments may be added this way). 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 (for more detail see the binary format)
|
|
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
|
|
|
|
Here the block order matters, latter blocks rewrite previously placed ones.
|
|
|
|
The internal BINARY map format is made from the text string. It can't contain
|
|
special blocks and blocks are ordered by their coordinate number (for fast
|
|
lookup). The format consists of blocks, each of format:
|
|
- 1 byte type: says the type of block, is the same value as B in the text
|
|
format.
|
|
- 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
|
|
*/
|
|
|
|
#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
|
|
|
|
#define LCR_BLOCK_XYZ_TO_COORD(x,y,z) // ???
|
|
|
|
#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)), \
|
|
(uint8_t) ((y >> 2) | (z << 4)), \
|
|
(uint8_t) ((z >> 4) | (m << 2) | (r))
|
|
|
|
#define LCR_BLOCK_SIZE 4 ///< size of map block, in bytes
|
|
|
|
#define LCR_BLOCK_MATERIAL_CONCRETE 0x00
|
|
#define LCR_BLOCK_MATERIAL_GRASS 0x01
|
|
#define LCR_BLOCK_MATERIAL_DIRT 0x02
|
|
#define LCR_BLOCK_MATERIAL_ICE 0x03
|
|
|
|
// normal blocks:
|
|
#define LCR_BLOCK_FULL '=' ///< completely filled block
|
|
#define LCR_BLOCK_BOTTOM '-' ///< filled bottom half
|
|
#define LCR_BLOCK_LEFT ';' ///< filled left half
|
|
#define LCR_BLOCK_BOTTOM_LEFT ',' ///< filled bottom left quarter
|
|
#define LCR_BLOCK_BOTTOM_LEFT_FRONT '.' ///< filled bottom left front eigth
|
|
#define LCR_BLOCK_RAMP '^' ///< plain ramp
|
|
#define LCR_BLOCK_RAMP_34 '/' ///< plain ramp, 3/4 size
|
|
#define LCR_BLOCK_RAMP_12 '<' ///< plain ramp, 1/2 size
|
|
#define LCR_BLOCK_RAMP_14 '_' ///< plain ramp, 1/4 size
|
|
#define LCR_BLOCK_RAMP_CURVED_PLAT ']' ///< curved ramp with top platgform
|
|
#define LCR_BLOCK_RAMP_CURVED ')' ///< curv. ramp without top platf.
|
|
#define LCR_BLOCK_RAMP_CURVED_WALL '}' ///< curved ramp plus small wall
|
|
#define LCR_BLOCK_RAMP_STEEP '|' ///< extremely steep ramp
|
|
#define LCR_BLOCK_CORNER 'A' ///< diagonal corner
|
|
#define LCR_BLOCK_CORNER_12 '\\' ///< diagonal corner (1/2 wide)
|
|
#define LCR_BLOCK_HILL '(' ///< curved "hill"
|
|
#define LCR_BLOCK_FULL_ACCEL '>'
|
|
#define LCR_BLOCK_FULL_FAN 'o'
|
|
#define LCR_BLOCK_BUMP '~' ///< small bump on the road
|
|
|
|
#define LCR_BLOCK_CHECKPOINT_0 '+' ///< checkpoint, not taken
|
|
#define LCR_BLOCK_CHECKPOINT_1 '\'' ///< checkpoint, taken
|
|
|
|
#define LCR_BLOCK_FINISH '!' ///< finish
|
|
|
|
// special blocks:
|
|
#define LCR_BLOCK_NONE 'x' ///< no block, e.g to make holes
|
|
|
|
#define LCR_BLOCK_CUBOID_FILL 'f' /**< makes a cuboid from the
|
|
previously specified block, the
|
|
size is given by block coords */
|
|
#define LCR_BLOCK_CUBOID_HOLLOW 'h' /**< same as cuboid special block,
|
|
but makes a hollow one */
|
|
#define LCR_BLOCK_START '*' ///< specifies start block position
|
|
|
|
/*
|
|
TODO:
|
|
- ramp corner???
|
|
- curved corner?
|
|
- curved out corner?
|
|
- curved "hill"
|
|
- bumpy road
|
|
- bigger structures like a loop, sloped road etc?
|
|
*/
|
|
|
|
#define LCR_MAP_BLOCK_CACHE_SIZE (8 * 2) /// do not change
|
|
|
|
/**
|
|
Cache for accelerating LCR_mapGetBlockAtFast, consists of 8 2-item records,
|
|
the first record item stores block coord number, the second one stores the
|
|
value returned by LCR_mapGetBlockAtFast (-1 is 0xffffffff). The record index
|
|
depends on the block coords: lowest bit is x % 2, middle bit y % 2, highest
|
|
one z % 2.
|
|
*/
|
|
uint32_t _LCR_mapBlockCache[LCR_MAP_BLOCK_CACHE_SIZE];
|
|
|
|
struct
|
|
{
|
|
uint16_t blockCount;
|
|
uint8_t blocks[LCR_SETTING_MAP_MAX_BLOCKS * LCR_BLOCK_SIZE];
|
|
uint8_t startPos[4]; ///< Initial position and rotation.
|
|
|
|
uint8_t environment;
|
|
uint8_t checkpointCount;
|
|
// TODO: name, desc? possibly as a single '\n' separated string?
|
|
} LCR_currentMap;
|
|
|
|
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])
|
|
{
|
|
block[0] = type;
|
|
block[1] = x | (y << 6);
|
|
block[2] = (y >> 2) | (z << 4);
|
|
block[3] = (z >> 4) | (material << 2) | transform;
|
|
}
|
|
|
|
void LCR_mapBlockGetCoords(const uint8_t block[LCR_BLOCK_SIZE],
|
|
uint8_t *x, uint8_t *y, uint8_t *z)
|
|
{
|
|
*x = block[1] & 0x3f;
|
|
*y = (block[1] >> 6) | ((block[2] & 0x0f) << 2);
|
|
*z = (block[2] >> 4) | ((block[3] & 0x03) << 4);
|
|
}
|
|
|
|
uint8_t LCR_mapBlockOppositeTransform(uint8_t transform)
|
|
{
|
|
if (!(transform & LCR_BLOCK_TRANSFORM_FLIP_H))
|
|
{
|
|
if ((transform & 0x60) == LCR_BLOCK_TRANSFORM_ROT_90)
|
|
return ((transform & (~0x60)) | LCR_BLOCK_TRANSFORM_ROT_270);
|
|
|
|
if ((transform & 0x60) == LCR_BLOCK_TRANSFORM_ROT_270)
|
|
return ((transform & (~0x60)) | LCR_BLOCK_TRANSFORM_ROT_90);
|
|
}
|
|
|
|
return transform;
|
|
}
|
|
|
|
uint8_t LCR_mapBlockGetTransform(const uint8_t block[LCR_BLOCK_SIZE])
|
|
{
|
|
return block[3] & 0xf0;
|
|
}
|
|
|
|
uint8_t LCR_mapBlockGetMaterial(const uint8_t block[LCR_BLOCK_SIZE])
|
|
{
|
|
return (block[3] >> 2) & 0x03;
|
|
}
|
|
|
|
uint32_t LCR_mapBlockGetCoordNumber(const uint8_t block[LCR_BLOCK_SIZE])
|
|
{
|
|
return block[1] | (((uint32_t) block[2]) << 8) |
|
|
((((uint32_t) block[3]) & 0x3) << 16);
|
|
}
|
|
|
|
uint32_t LCR_mapBlockCoordsToCoordNumber(uint8_t x, uint8_t y, uint8_t z)
|
|
{
|
|
uint8_t b[LCR_BLOCK_SIZE];
|
|
LCR_makeMapBlock(0,x,y,z,0,0,b);
|
|
return LCR_mapBlockGetCoordNumber(b);
|
|
}
|
|
|
|
/**
|
|
Gets dimensions of a non-curved ramp.
|
|
*/
|
|
void LCR_rampGetDimensions(uint8_t rampType, uint8_t *height4ths,
|
|
uint8_t *length6ths)
|
|
{
|
|
*height4ths =
|
|
(rampType == LCR_BLOCK_RAMP_14) +
|
|
(rampType == LCR_BLOCK_RAMP || rampType == LCR_BLOCK_RAMP_STEEP) * 4 +
|
|
(rampType == LCR_BLOCK_RAMP_12 || rampType == LCR_BLOCK_RAMP_34) * 2 +
|
|
(rampType == LCR_BLOCK_RAMP_34);
|
|
|
|
*length6ths = rampType != LCR_BLOCK_RAMP_STEEP ? 6 : 1;
|
|
}
|
|
|
|
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;
|
|
|
|
uint8_t *block = LCR_currentMap.blocks + mid * LCR_BLOCK_SIZE;
|
|
|
|
uint32_t coord2 =
|
|
LCR_mapBlockGetCoordNumber(block);
|
|
|
|
if (coord2 == coord)
|
|
return block;
|
|
else if (coord2 > coord)
|
|
b = mid - 1;
|
|
else
|
|
a = mid + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
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 pointer to the block on success, else 0.
|
|
*/
|
|
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;
|
|
|
|
while (insertAt < LCR_currentMap.blockCount &&
|
|
coord > LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks +
|
|
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 &&
|
|
coord == LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks +
|
|
insertAt * LCR_BLOCK_SIZE))
|
|
{
|
|
// shift all left (remove the block):
|
|
for (uint16_t i = insertAt * LCR_BLOCK_SIZE;
|
|
i < LCR_currentMap.blockCount * LCR_BLOCK_SIZE - 1; ++i)
|
|
LCR_currentMap.blocks[i] = LCR_currentMap.blocks[i + 1];
|
|
|
|
LCR_currentMap.blockCount--;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
if (insertAt == LCR_currentMap.blockCount ||
|
|
coord != LCR_mapBlockGetCoordNumber(LCR_currentMap.blocks +
|
|
insertAt * LCR_BLOCK_SIZE))
|
|
{
|
|
// shift from here to the right, make room for the new block
|
|
|
|
LCR_currentMap.blockCount++;
|
|
|
|
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];
|
|
}
|
|
|
|
insertAt *= LCR_BLOCK_SIZE;
|
|
|
|
for (uint8_t j = 0; j < LCR_BLOCK_SIZE; ++j)
|
|
LCR_currentMap.blocks[insertAt + j] = block[j];
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
Resets the map to start a run (including e.g. unmarking checkpoints etc.).
|
|
*/
|
|
void LCR_mapReset(void)
|
|
{
|
|
LCR_LOG0("resetting map");
|
|
|
|
for (int i = 0; i < LCR_currentMap.blockCount; ++i)
|
|
if (LCR_currentMap.blocks[i * LCR_BLOCK_SIZE] == LCR_BLOCK_CHECKPOINT_1)
|
|
LCR_currentMap.blocks[i * LCR_BLOCK_SIZE] = LCR_BLOCK_CHECKPOINT_0;
|
|
}
|
|
|
|
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 = *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) &&
|
|
(x + i < LCR_MAP_SIZE_BLOCKS &&
|
|
y + j < LCR_MAP_SIZE_BLOCKS &&
|
|
z + k < LCR_MAP_SIZE_BLOCKS))
|
|
{
|
|
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;
|
|
}
|
|
|
|
// TODO: check for invalid blocks?
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
Same as LCR_mapGetBlockAt, but allows to specify start and end block of the
|
|
of the search to make it faster.
|
|
*/
|
|
int LCR_mapGetBlockAtFast(uint8_t x, uint8_t y, uint8_t z,
|
|
int start, int end)
|
|
{
|
|
// binary search (the blocks are sorted)
|
|
|
|
uint32_t n = LCR_mapBlockCoordsToCoordNumber(x,y,z);
|
|
uint8_t cacheIndex = 2 * ((x % 2) | ((y % 2) << 1) | ((z % 2) << 2));
|
|
|
|
if (_LCR_mapBlockCache[cacheIndex] == n)
|
|
return
|
|
(_LCR_mapBlockCache[cacheIndex + 1] != 0xffffffff) ?
|
|
((int) _LCR_mapBlockCache[cacheIndex + 1]) : -1;
|
|
|
|
_LCR_mapBlockCache[cacheIndex] = n;
|
|
|
|
while (start <= end)
|
|
{
|
|
int m = (start + end) / 2;
|
|
|
|
uint32_t n2 = LCR_mapBlockGetCoordNumber(
|
|
LCR_currentMap.blocks + m * LCR_BLOCK_SIZE);
|
|
|
|
if (n2 < n)
|
|
start = m + 1;
|
|
else if (n2 > n)
|
|
end = m - 1;
|
|
else
|
|
{
|
|
_LCR_mapBlockCache[cacheIndex + 1] = m;
|
|
return m;
|
|
}
|
|
}
|
|
|
|
_LCR_mapBlockCache[cacheIndex + 1] = 0xffffffff;
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
Gets an index to a map block of the currently loaded map at given
|
|
coordinates. If there is no block at given coordinates, -1 is returned.
|
|
*/
|
|
int LCR_mapGetBlockAt(uint8_t x, uint8_t y, uint8_t z)
|
|
{
|
|
if (LCR_currentMap.blockCount == 0)
|
|
return -1;
|
|
|
|
return LCR_mapGetBlockAtFast(x,y,z,0,LCR_currentMap.blockCount - 1);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#define LCR_BLOCK_SHAPE_COORD_MAX 12
|
|
|
|
/**
|
|
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;
|
|
}
|
|
|
|
void _LCR_addBlockShapeByte(uint8_t *bytes, uint8_t *byteCount,
|
|
int x, int y, int z)
|
|
{
|
|
if (*byteCount >= LCR_MAP_BLOCK_SHAPE_MAX_BYTES)
|
|
return;
|
|
|
|
bytes[*byteCount] = _LCR_encodeMapBlockCoords(x,y,z);
|
|
*byteCount += 1;
|
|
}
|
|
|
|
/**
|
|
Macro that transforms coordinates according to block transformation.
|
|
*/
|
|
#define LCR_TRANSFORM_COORDS(trans,cx,cy,cz,maxXZ,maxY)\
|
|
if (trans & LCR_BLOCK_TRANSFORM_FLIP_H) cx = maxXZ - cx;\
|
|
if (trans & 0x20) { /* for both 90 and 270 */ \
|
|
cx ^= cz; cz ^= cx; cx ^= cz; /* swap */ \
|
|
cx = maxXZ - cx; } \
|
|
if (trans & 0x40) { /* for both 180 and 270 */ \
|
|
cx = maxXZ - cx; \
|
|
cz = maxXZ - cz; } \
|
|
if (trans & LCR_BLOCK_TRANSFORM_FLIP_V) \
|
|
cy = maxY - cy;
|
|
|
|
/**
|
|
Gets a shape of given map block type as a 3D model composed of triangles. The
|
|
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.
|
|
*/
|
|
void LCR_mapGetBlockShape(uint8_t blockType, uint8_t transform,
|
|
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)
|
|
= 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
|
|
*/
|
|
|
|
*byteCount = 0;
|
|
|
|
#define ADD(a,b,c) _LCR_addBlockShapeByte(bytes,byteCount,a,b,c);
|
|
|
|
switch (blockType)
|
|
{
|
|
case LCR_BLOCK_CHECKPOINT_0:
|
|
case LCR_BLOCK_CHECKPOINT_1:
|
|
case LCR_BLOCK_FINISH:
|
|
ADD(3,0,3) ADD(0,2,6) ADD(6,2,6)
|
|
ADD(3,0,3) ADD(0,2,0) ADD(0,2,6)
|
|
ADD(3,0,3) ADD(6,2,0) ADD(0,2,0)
|
|
ADD(3,0,3) ADD(6,2,6) ADD(6,2,0)
|
|
ADD(3,4,3) ADD(0,2,6) ADD(0,2,0)
|
|
ADD(3,4,3) ADD(0,2,0) ADD(6,2,0)
|
|
ADD(3,4,3) ADD(6,2,0) ADD(6,2,6)
|
|
ADD(3,4,3) ADD(6,2,6) ADD(0,2,6)
|
|
break;
|
|
|
|
case LCR_BLOCK_FULL:
|
|
case LCR_BLOCK_BOTTOM:
|
|
case LCR_BLOCK_LEFT:
|
|
case LCR_BLOCK_BOTTOM_LEFT:
|
|
case LCR_BLOCK_BOTTOM_LEFT_FRONT:
|
|
case LCR_BLOCK_FULL_ACCEL:
|
|
case LCR_BLOCK_FULL_FAN:
|
|
{
|
|
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_PLAT:
|
|
ADD(0,0,6) ADD(0,4,5) ADD(0,4,6) // left
|
|
ADD(6,0,6) ADD(6,4,6) ADD(6,4,5) // right
|
|
ADD(0,4,5) ADD(6,4,5) ADD(0,4,6) // top
|
|
ADD(0,4,6) ADD(6,4,5) ADD(6,4,6)
|
|
// fall through
|
|
|
|
case LCR_BLOCK_RAMP_CURVED:
|
|
{
|
|
uint8_t plusZ = blockType == LCR_BLOCK_RAMP_CURVED;
|
|
|
|
ADD(0,0,0) ADD(6,0,0) ADD(0,1,3 + plusZ) // ramp
|
|
ADD(0,1,3 + plusZ) ADD(6,0,0) ADD(6,1,3 + plusZ)
|
|
ADD(0,1,3 + plusZ) ADD(6,1,3 + plusZ) ADD(0,2,4 + plusZ) // ramp
|
|
ADD(0,2,4 + plusZ) ADD(6,1,3 + plusZ) ADD(6,2,4 + plusZ)
|
|
ADD(0,2,4 + plusZ) ADD(6,2,4 + plusZ) ADD(0,4,5 + plusZ) // ramp
|
|
ADD(0,4,5 + plusZ) ADD(6,2,4 + plusZ) ADD(6,4,5 + plusZ)
|
|
ADD(0,0,0) ADD(0,1,3 + plusZ) ADD(0,0,6) // left
|
|
ADD(0,0,6) ADD(0,1,3 + plusZ) ADD(0,2,4 + plusZ)
|
|
ADD(0,0,6) ADD(0,2,4 + plusZ) ADD(0,4,5 + plusZ)
|
|
ADD(6,0,0) ADD(6,0,6) ADD(6,1,3 + plusZ) // right
|
|
ADD(6,0,6) ADD(6,2,4 + plusZ) ADD(6,1,3 + plusZ)
|
|
ADD(6,0,6) ADD(6,4,5 + plusZ) ADD(6,2,4 + plusZ)
|
|
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)
|
|
break;
|
|
}
|
|
|
|
case LCR_BLOCK_RAMP_CURVED_WALL:
|
|
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)
|
|
break;
|
|
|
|
case LCR_BLOCK_RAMP:
|
|
case LCR_BLOCK_RAMP_12:
|
|
case LCR_BLOCK_RAMP_14:
|
|
case LCR_BLOCK_RAMP_34:
|
|
case LCR_BLOCK_RAMP_STEEP:
|
|
{
|
|
uint8_t front, top;
|
|
LCR_rampGetDimensions(blockType,&top,&front);
|
|
front = 6 - front;
|
|
|
|
ADD(0,0,front) ADD(0,top,6) ADD(0,0,6) // side
|
|
ADD(6,0,front) ADD(6,0,6) ADD(6,top,6) // side
|
|
ADD(0,0,front) ADD(6,0,front) ADD(0,top,6) // top
|
|
ADD(6,0,front) ADD(6,top,6) ADD(0,top,6) // top
|
|
ADD(0,0,6) ADD(6,top,6) ADD(6,0,6) // back
|
|
ADD(0,0,6) ADD(0,top,6) ADD(6,top,6) // back
|
|
ADD(0,0,front) ADD(0,0,6) ADD(6,0,6) // bottom
|
|
ADD(0,0,front) ADD(6,0,6) ADD(6,0,front) // bottom
|
|
|
|
break;
|
|
}
|
|
|
|
case LCR_BLOCK_CORNER:
|
|
case LCR_BLOCK_CORNER_12:
|
|
{
|
|
uint8_t right = blockType == LCR_BLOCK_CORNER ? 6 : 3;
|
|
|
|
ADD(0,0,0) ADD(right,0,6) ADD(right,4,6) // front/right
|
|
ADD(0,0,0) ADD(right,4,6) ADD(0,4,0) // front/right
|
|
ADD(0,0,0) ADD(0,4,6) ADD(0,0,6) // left
|
|
ADD(0,0,0) ADD(0,4,0) ADD(0,4,6) // left
|
|
ADD(right,0,6) ADD(0,0,6) ADD(0,4,6) // back
|
|
ADD(0,4,6) ADD(right,4,6) ADD(right,0,6) // back
|
|
ADD(0,4,0) ADD(right,4,6) ADD(0,4,6) // top
|
|
ADD(0,0,6) ADD(right,0,6) ADD(0,0,0) // bottom
|
|
|
|
break;
|
|
}
|
|
|
|
case LCR_BLOCK_BUMP:
|
|
ADD(3,0,0) ADD(6,0,3) ADD(3,1,3) // top
|
|
ADD(6,0,3) ADD(3,0,6) ADD(3,1,3)
|
|
ADD(3,0,6) ADD(0,0,3) ADD(3,1,3)
|
|
ADD(0,0,3) ADD(3,0,0) ADD(3,1,3)
|
|
ADD(3,0,0) ADD(3,0,6) ADD(6,0,3) // bottom
|
|
ADD(3,0,6) ADD(3,0,0) ADD(0,0,3) // bottom
|
|
break;
|
|
|
|
case LCR_BLOCK_HILL:
|
|
ADD(0,0,0) ADD(6,0,0) ADD(0,2,1) // front
|
|
ADD(6,0,0) ADD(6,2,1) ADD(0,2,1)
|
|
ADD(0,2,1) ADD(6,2,1) ADD(0,3,2) // front 2
|
|
ADD(6,2,1) ADD(6,3,2) ADD(0,3,2)
|
|
ADD(0,3,2) ADD(6,3,2) ADD(0,4,4) // front 3
|
|
ADD(6,3,2) ADD(6,4,4) ADD(0,4,4)
|
|
ADD(0,4,4) ADD(6,4,4) ADD(0,4,6) // top
|
|
ADD(6,4,4) ADD(6,4,6) ADD(0,4,6)
|
|
ADD(0,0,0) ADD(0,0,6) ADD(6,0,0) // bottom
|
|
ADD(6,0,0) ADD(0,0,6) ADD(6,0,6)
|
|
ADD(0,0,6) ADD(0,4,6) ADD(6,4,6) // back
|
|
ADD(0,0,6) ADD(6,4,6) ADD(6,0,6)
|
|
ADD(0,0,0) ADD(0,2,1) ADD(0,0,6) // left
|
|
ADD(0,2,1) ADD(0,3,2) ADD(0,0,6)
|
|
ADD(0,3,2) ADD(0,4,4) ADD(0,0,6)
|
|
ADD(0,4,4) ADD(0,4,6) ADD(0,0,6)
|
|
ADD(6,0,0) ADD(6,0,6) ADD(6,2,1) // right
|
|
ADD(6,2,1) ADD(6,0,6) ADD(6,3,2)
|
|
ADD(6,3,2) ADD(6,0,6) ADD(6,4,4)
|
|
ADD(6,4,4) ADD(6,0,6) ADD(6,4,6)
|
|
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
if (transform)
|
|
{
|
|
for (int i = 0; i < *byteCount; ++i)
|
|
{
|
|
uint8_t x, y, z, tmp;
|
|
|
|
_LCR_decodeMapBlockCoords(bytes[i],&x,&y,&z);
|
|
|
|
LCR_TRANSFORM_COORDS(transform,x,y,z,6,4)
|
|
|
|
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
|
|
}
|
|
|
|
#endif // guard
|