588 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			588 lines
		
	
	
	
		
			17 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 STORAGE map format is binary and consists of the following values:
 | |
|     - 76, 77 (for "LM", magic number)
 | |
|     - one byte recording the map environment
 | |
|     - 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
 | |
|         is normal, otherwise it is a special block (will be preprocessed)
 | |
|       - 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.
 | |
| 
 | |
|   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.
 | |
| */
 | |
| 
 | |
| #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_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
 | |
| 
 | |
| #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         0x09
 | |
| #define LCR_BLOCK_RAMP_CURVED_SHORT   0x0a
 | |
| #define LCR_BLOCK_RAMP_CURVED_WALL    0x0b
 | |
| 
 | |
| #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
 | |
| 
 | |
| struct
 | |
| {
 | |
|   uint16_t blockCount;
 | |
|   uint8_t blocks[LCR_SETTING_MAP_MAX_BLOCKS * LCR_BLOCK_SIZE];
 | |
|   uint32_t startPos;
 | |
| 
 | |
|   uint8_t environment;
 | |
| 
 | |
|   // 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])
 | |
| {
 | |
|   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_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);
 | |
| }
 | |
| 
 | |
| 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 1 on success, else 0.
 | |
| */
 | |
| uint8_t _LCR_mapAddBlock(const uint8_t block[LCR_BLOCK_SIZE])
 | |
| {
 | |
|   if (LCR_currentMap.blockCount >= LCR_SETTING_MAP_MAX_BLOCKS)
 | |
|     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++;
 | |
| 
 | |
|   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 1;
 | |
|   }
 | |
| 
 | |
|   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 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++;
 | |
| 
 | |
|   LCR_currentMap.environment = *map;
 | |
| 
 | |
|   map++;
 | |
| 
 | |
|   while (*map != LCR_MAP_TERMINATOR)
 | |
|   {
 | |
|     switch (*map)
 | |
|     {
 | |
|       case LCR_BLOCK_CUBOID_FILL:
 | |
|       case LCR_BLOCK_CUBOID_HOLLOW:
 | |
|       {
 | |
|         const uint8_t *prevBlock = map - LCR_BLOCK_SIZE;
 | |
|         uint8_t x, y, z, w, h, d, mat, transform;
 | |
|         uint8_t tmpBlock[LCR_BLOCK_SIZE];
 | |
| 
 | |
|         if (LCR_currentMap.blockCount == 0 || (prevBlock[0] & 0x80))
 | |
|           return 0;
 | |
| 
 | |
|         mat = LCR_mapBlockGetMaterial(prevBlock);
 | |
|         transform = LCR_mapBlockGetTransform(prevBlock);
 | |
|         LCR_mapBlockGetCoords(prevBlock,&x,&y,&z);
 | |
|         LCR_mapBlockGetCoords(map,&w,&h,&d);
 | |
|         
 | |
|         for (uint8_t k = 0; k < d; ++k)
 | |
|           for (uint8_t j = 0; j < h; ++j)
 | |
|             for (uint8_t i = 0; i < w; ++i)
 | |
|               if (*map == LCR_BLOCK_CUBOID_FILL ||
 | |
|                   k == 0 || k == d - 1 ||
 | |
|                   j == 0 || j == h - 1 ||
 | |
|                   i == 0 || i == w - 1)
 | |
|               {
 | |
|                 LCR_makeMapBlock(prevBlock[0],x + i,y + j,z + k,mat,transform,
 | |
|                   tmpBlock);
 | |
|         
 | |
|                 if (!_LCR_mapAddBlock(tmpBlock))
 | |
|                   return 0;
 | |
|               }
 | |
| 
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       default:
 | |
|         if (!_LCR_mapAddBlock(map)) // normal block
 | |
|           return 0;
 | |
| 
 | |
|         break;
 | |
|     }
 | |
|     
 | |
|     map += 4;
 | |
|   }
 | |
| 
 | |
|   // process and remove special blocks:
 | |
| 
 | |
|   // TODO
 | |
|  
 | |
|   // sort the blocks (for fast searching): 
 | |
| 
 | |
|   // TODO
 | |
| 
 | |
|   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);
 | |
| 
 | |
|   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
 | |
|       return m;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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_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_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:
 | |
|     {
 | |
|       uint8_t top =
 | |
|         (blockType == LCR_BLOCK_RAMP_14) +
 | |
|         (blockType == LCR_BLOCK_RAMP) * 4 +
 | |
|         (blockType == LCR_BLOCK_RAMP_12 || blockType == LCR_BLOCK_RAMP_34) * 2 +
 | |
|         (blockType == LCR_BLOCK_RAMP_34);
 | |
| 
 | |
|       ADD(0,0,0) ADD(0,top,6) ADD(0,0,6)     // side
 | |
|       ADD(6,0,0) ADD(6,0,6)   ADD(6,top,6)   // side
 | |
|       ADD(0,0,0) ADD(6,0,0)   ADD(0,top,6)   // top
 | |
|       ADD(6,0,0) 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,0) ADD(0,0,6)   ADD(6,0,6)     // bottom
 | |
|       ADD(0,0,0) ADD(6,0,6)   ADD(6,0,0)     // bottom
 | |
| 
 | |
|       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);
 | |
|      
 | |
|       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
 | |
| }
 | |
| 
 | |
| #endif // guard
 |