diff options
author | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
commit | abef6da56913f1c55528103e60a50451a39628b1 (patch) | |
tree | b3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Block.c |
initial commit
Diffstat (limited to 'src/Block.c')
-rw-r--r-- | src/Block.c | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/src/Block.c b/src/Block.c new file mode 100644 index 0000000..d8eda91 --- /dev/null +++ b/src/Block.c @@ -0,0 +1,805 @@ +#include "Block.h" +#include "Funcs.h" +#include "ExtMath.h" +#include "TexturePack.h" +#include "Game.h" +#include "Entity.h" +#include "Inventory.h" +#include "Event.h" +#include "Picking.h" +#include "Lighting.h" + +struct _BlockLists Blocks; + +const char* const Sound_Names[SOUND_COUNT] = { + "none", "wood", "gravel", "grass", "stone", + "metal", "glass", "cloth", "sand", "snow", +}; + +/*########################################################################################################################* +*---------------------------------------------------Default properties----------------------------------------------------* +*#########################################################################################################################*/ +#define FOG_NONE 0 +#define FOG_WATER PackedCol_Make( 5, 5, 51, 255) +#define FOG_LAVA PackedCol_Make(153, 25, 0, 255) + +/* Brightness */ +#define BRIT_NONE 0 +#define BRIT_FULL FANCY_LIGHTING_MAX_LEVEL +#define BRIT_MAGM 10 + +struct SimpleBlockDef { + const char* name; + cc_uint8 topTexture, sideTexture, bottomTexture, height; + PackedCol fogColor; cc_uint8 fogDensity; + cc_uint8 brightness, blocksLight; cc_uint8 gravity; + cc_uint8 draw, collide, digSound, stepSound; +}; +static const struct SimpleBlockDef invalid_blockDef = { + "Invalid", 0,0,0,16, FOG_NONE,0, false,true, 100, DRAW_OPAQUE,COLLIDE_SOLID,0 +}; + +/* Properties for all built-in blocks (Classic and CPE blocks) */ +static const struct SimpleBlockDef core_blockDefs[] = { +/*NAME TOP SID BOT HEI FOG_COLOR DENS BRIT BLOCKS GRAV DRAW_MODE COLLIDE_MODE DIG_SOUND STEP_SOUND */ +{ "Air", 0, 0, 0, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_GAS, COLLIDE_NONE, SOUND_NONE, SOUND_NONE }, +{ "Stone", 1, 1, 1, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Grass", 0, 3, 2, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_GRASS, SOUND_GRASS }, +{ "Dirt", 2, 2, 2, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_GRAVEL, SOUND_GRAVEL }, +{ "Cobblestone", 16, 16, 16, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Wood", 4, 4, 4, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_WOOD, SOUND_WOOD }, +{ "Sapling", 15, 15, 15, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_SPRITE, COLLIDE_NONE, SOUND_GRASS, SOUND_NONE }, +{ "Bedrock", 17, 17, 17, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, + +{ "Water", 14, 14, 14, 16, FOG_WATER, 10, BRIT_NONE, true, 100, DRAW_TRANSLUCENT, COLLIDE_WATER, SOUND_NONE, SOUND_NONE }, +{ "Still water", 14, 14, 14, 16, FOG_WATER, 10, BRIT_NONE, true, 100, DRAW_TRANSLUCENT, COLLIDE_WATER, SOUND_NONE, SOUND_NONE }, +{ "Lava", 30, 30, 30, 16, FOG_LAVA , 180, BRIT_FULL, true, 100, DRAW_OPAQUE, COLLIDE_LAVA, SOUND_NONE, SOUND_NONE }, +{ "Still lava", 30, 30, 30, 16, FOG_LAVA , 180, BRIT_FULL, true, 100, DRAW_OPAQUE, COLLIDE_LAVA, SOUND_NONE, SOUND_NONE }, +{ "Sand", 18, 18, 18, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_SAND, SOUND_SAND }, +{ "Gravel", 19, 19, 19, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_GRAVEL, SOUND_GRAVEL }, +{ "Gold ore", 32, 32, 32, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Iron ore", 33, 33, 33, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, + +{ "Coal ore", 34, 34, 34, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Log", 21, 20, 21, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_WOOD, SOUND_WOOD }, +{ "Leaves", 22, 22, 22, 16, FOG_NONE , 0, BRIT_NONE, false, 40, DRAW_TRANSPARENT_THICK, COLLIDE_SOLID, SOUND_GRASS, SOUND_GRASS }, +{ "Sponge", 48, 48, 48, 16, FOG_NONE , 0, BRIT_NONE, true, 90, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_GRASS, SOUND_GRASS }, +{ "Glass", 49, 49, 49, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_TRANSPARENT, COLLIDE_SOLID, SOUND_GLASS,SOUND_STONE}, +{ "Red", 64, 64, 64, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Orange", 65, 65, 65, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Yellow", 66, 66, 66, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, + +{ "Lime", 67, 67, 67, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Green", 68, 68, 68, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Teal", 69, 69, 69, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Aqua", 70, 70, 70, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Cyan", 71, 71, 71, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Blue", 72, 72, 72, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Indigo", 73, 73, 73, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Violet", 74, 74, 74, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, + +{ "Magenta", 75, 75, 75, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Pink", 76, 76, 76, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Black", 77, 77, 77, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Gray", 78, 78, 78, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "White", 79, 79, 79, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Dandelion", 13, 13, 13, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_SPRITE, COLLIDE_NONE, SOUND_GRASS, SOUND_NONE }, +{ "Rose", 12, 12, 12, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_SPRITE, COLLIDE_NONE, SOUND_GRASS, SOUND_NONE }, +{ "Brown mushroom", 29, 29, 29, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_SPRITE, COLLIDE_NONE, SOUND_GRASS, SOUND_NONE }, + +{ "Red mushroom", 28, 28, 28, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_SPRITE, COLLIDE_NONE, SOUND_GRASS, SOUND_NONE }, +{ "Gold", 24, 40, 56, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_METAL, SOUND_METAL }, +{ "Iron", 23, 39, 55, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_METAL, SOUND_METAL }, +{ "Double slab", 6, 5, 6, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Slab", 6, 5, 6, 8, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Brick", 7, 7, 7, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "TNT", 9, 8, 10, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_GRASS, SOUND_GRASS }, +{ "Bookshelf", 4, 35, 4, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_WOOD, SOUND_WOOD }, + +{ "Mossy rocks", 36, 36, 36, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Obsidian", 37, 37, 37, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Cobblestone slab", 16, 16, 16, 8, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Rope", 11, 11, 11, 16, FOG_NONE , 0, BRIT_NONE, false, 100, DRAW_SPRITE, COLLIDE_CLIMB, SOUND_CLOTH, SOUND_CLOTH }, +{ "Sandstone", 25, 41, 57, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Snow", 50, 50, 50, 4, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_NONE, SOUND_SNOW, SOUND_SNOW }, +{ "Fire", 38, 38, 38, 16, FOG_NONE , 0, BRIT_FULL, false, 100, DRAW_SPRITE, COLLIDE_NONE, SOUND_WOOD, SOUND_NONE }, +{ "Light pink", 80, 80, 80, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, + +{ "Forest green", 81, 81, 81, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Brown", 82, 82, 82, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Deep blue", 83, 83, 83, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Turquoise", 84, 84, 84, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_CLOTH, SOUND_CLOTH }, +{ "Ice", 51, 51, 51, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_TRANSLUCENT, COLLIDE_ICE, SOUND_STONE, SOUND_STONE }, +{ "Ceramic tile", 54, 54, 54, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Magma", 86, 86, 86, 16, FOG_NONE , 0, BRIT_MAGM, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, +{ "Pillar", 26, 42, 58, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE }, + +{ "Crate", 53, 53, 53, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_WOOD, SOUND_WOOD }, +{ "Stone brick", 52, 52, 52, 16, FOG_NONE , 0, BRIT_NONE, true, 100, DRAW_OPAQUE, COLLIDE_SOLID, SOUND_STONE, SOUND_STONE } +/*NAME TOP SID BOT HEI FOG_COLOR DENS BRIT BLOCKS GRAV DRAW_MODE COLLIDE_MODE DIG_SOUND STEP_SOUND */ +}; + +/* Returns a backwards compatible collide type of a block */ +static cc_uint8 DefaultSet_MapOldCollide(BlockID b, cc_uint8 collide) { + if (b == BLOCK_ROPE && collide == COLLIDE_NONE) return COLLIDE_CLIMB; + if (b == BLOCK_ICE && collide == COLLIDE_SOLID) return COLLIDE_ICE; + + if ((b == BLOCK_WATER || b == BLOCK_STILL_WATER) && collide == COLLIDE_LIQUID) + return COLLIDE_WATER; + if ((b == BLOCK_LAVA || b == BLOCK_STILL_LAVA) && collide == COLLIDE_LIQUID) + return COLLIDE_LAVA; + return collide; +} + + +/*########################################################################################################################* +*---------------------------------------------------Block properties------------------------------------------------------* +*#########################################################################################################################*/ +static void Block_RecalcIsLiquid(BlockID b) { + cc_uint8 collide = Blocks.ExtendedCollide[b]; + Blocks.IsLiquid[b] = + (collide == COLLIDE_WATER && Blocks.Draw[b] == DRAW_TRANSLUCENT) || + (collide == COLLIDE_LAVA && Blocks.Draw[b] == DRAW_TRANSPARENT); +} + +/* Sets the basic and extended collide types of the given block */ +static void Block_SetCollide(BlockID block, cc_uint8 collide) { + Blocks.ExtendedCollide[block] = collide; + Block_RecalcIsLiquid(block); + + /* Reduce extended collision types to their simpler forms */ + if (collide == COLLIDE_ICE) collide = COLLIDE_SOLID; + if (collide == COLLIDE_SLIPPERY_ICE) collide = COLLIDE_SOLID; + + if (collide == COLLIDE_WATER) collide = COLLIDE_LIQUID; + if (collide == COLLIDE_LAVA) collide = COLLIDE_LIQUID; + Blocks.Collide[block] = collide; +} + +/* Sets draw type and updates related state (e.g. FullOpaque) for the given block */ +static void Block_SetDrawType(BlockID block, cc_uint8 draw) { + if (draw == DRAW_OPAQUE && Blocks.Collide[block] != COLLIDE_SOLID) draw = DRAW_TRANSPARENT; + Blocks.Draw[block] = draw; + Block_RecalcIsLiquid(block); + + /* Whether a block is opaque and exactly occupies a cell in the world */ + /* The mesh builder module needs this information for optimisation purposes */ + Blocks.FullOpaque[block] = draw == DRAW_OPAQUE + && Blocks.MinBB[block].x == 0 && Blocks.MinBB[block].y == 0 && Blocks.MinBB[block].z == 0 + && Blocks.MaxBB[block].x == 1 && Blocks.MaxBB[block].y == 1 && Blocks.MaxBB[block].z == 1; +} + +void Block_SetSide(TextureLoc texLoc, BlockID blockId) { + int index = blockId * FACE_COUNT; + Blocks.Textures[index + FACE_XMIN] = texLoc; + Blocks.Textures[index + FACE_XMAX] = texLoc; + Blocks.Textures[index + FACE_ZMIN] = texLoc; + Blocks.Textures[index + FACE_ZMAX] = texLoc; +} + + +/*########################################################################################################################* +*--------------------------------------------------Block bounds/culling---------------------------------------------------* +*#########################################################################################################################*/ +/* Calculates render min/max corners of this block */ +/* Works by slightly offsetting collision min/max corners */ +static void Block_CalcRenderBounds(BlockID block) { + Vec3 min = Blocks.MinBB[block], max = Blocks.MaxBB[block]; + + if (Blocks.IsLiquid[block]) { + min.x += 0.1f/16.0f; max.x += 0.1f/16.0f; + min.z += 0.1f/16.0f; max.z += 0.1f/16.0f; + min.y -= 1.5f/16.0f; max.y -= 1.5f/16.0f; + } else if (Blocks.Draw[block] == DRAW_TRANSLUCENT && Blocks.Collide[block] != COLLIDE_SOLID) { + min.x += 0.1f/16.0f; max.x += 0.1f/16.0f; + min.z += 0.1f/16.0f; max.z += 0.1f/16.0f; + min.y -= 0.1f/16.0f; max.y -= 0.1f/16.0f; + } + + Blocks.RenderMinBB[block] = min; Blocks.RenderMaxBB[block] = max; +} + +/* Calculates light colour offset for each face of the given block */ +static void Block_CalcLightOffset(BlockID block) { + int flags = 0xFF; + Vec3 min = Blocks.MinBB[block], max = Blocks.MaxBB[block]; + + if (min.x != 0) flags &= ~(1 << FACE_XMIN); + if (max.x != 1) flags &= ~(1 << FACE_XMAX); + if (min.z != 0) flags &= ~(1 << FACE_ZMIN); + if (max.z != 1) flags &= ~(1 << FACE_ZMAX); + if (min.y != 0) flags &= ~(1 << FACE_YMIN); + if (max.y != 1) flags &= ~(1 << FACE_YMAX); + + if ((min.y != 0 && max.y == 1) && Blocks.Draw[block] != DRAW_GAS) { + flags &= ~(1 << LIGHT_FLAG_SHADES_FROM_BELOW); + } + Blocks.LightOffset[block] = flags; +} + + +static float GetSpriteBB_MinX(int size, int tileX, int tileY, const struct Bitmap* bmp) { + BitmapCol* row; + int x, y; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + row = Bitmap_GetRow(bmp, tileY * size + y) + (tileX * size); + if (BitmapCol_A(row[x])) { return (float)x / size; } + } + } + return 1.0f; +} + +static float GetSpriteBB_MinY(int size, int tileX, int tileY, const struct Bitmap* bmp) { + BitmapCol* row; + int x, y; + + for (y = size - 1; y >= 0; y--) { + row = Bitmap_GetRow(bmp, tileY * size + y) + (tileX * size); + for (x = 0; x < size; x++) { + if (BitmapCol_A(row[x])) { return 1.0f - (float)(y + 1) / size; } + } + } + return 1.0f; +} + +static float GetSpriteBB_MaxX(int size, int tileX, int tileY, const struct Bitmap* bmp) { + BitmapCol* row; + int x, y; + + for (x = size - 1; x >= 0; x--) { + for (y = 0; y < size; y++) { + row = Bitmap_GetRow(bmp, tileY * size + y) + (tileX * size); + if (BitmapCol_A(row[x])) { return (float)(x + 1) / size; } + } + } + return 0.0f; +} + +static float GetSpriteBB_MaxY(int size, int tileX, int tileY, const struct Bitmap* bmp) { + BitmapCol* row; + int x, y; + + for (y = 0; y < size; y++) { + row = Bitmap_GetRow(bmp, tileY * size + y) + (tileX * size); + for (x = 0; x < size; x++) { + if (BitmapCol_A(row[x])) { return 1.0f - (float)y / size; } + } + } + return 0.0f; +} + +/* Recalculates bounding box of the given sprite block */ +static void Block_RecalculateBB(BlockID block) { + struct Bitmap* bmp = &Atlas2D.Bmp; + int tileSize = Atlas2D.TileSize; + TextureLoc texLoc = Block_Tex(block, FACE_XMAX); + int x = Atlas2D_TileX(texLoc), y = Atlas2D_TileY(texLoc); + + Vec3 centre = { 0.5f, 0.0f, 0.5f }; + float minX = 0, minY = 0, maxX = 1, maxY = 1; + Vec3 minRaw, maxRaw; + + if (y < Atlas2D.RowsCount) { + minX = GetSpriteBB_MinX(tileSize, x, y, bmp); + minY = GetSpriteBB_MinY(tileSize, x, y, bmp); + maxX = GetSpriteBB_MaxX(tileSize, x, y, bmp); + maxY = GetSpriteBB_MaxY(tileSize, x, y, bmp); + } + + minRaw = Vec3_RotateY3(minX - 0.5f, minY, 0.0f, 45.0f * MATH_DEG2RAD); + maxRaw = Vec3_RotateY3(maxX - 0.5f, maxY, 0.0f, 45.0f * MATH_DEG2RAD); + Vec3_Add(&Blocks.MinBB[block], &minRaw, ¢re); + Vec3_Add(&Blocks.MaxBB[block], &maxRaw, ¢re); + Block_CalcRenderBounds(block); +} + +/* Recalculates bounding boxes of all sprite blocks */ +static void Block_RecalculateAllSpriteBB(void) { + int block; + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) { + if (Blocks.Draw[block] != DRAW_SPRITE) continue; + + Block_RecalculateBB((BlockID)block); + } +} + + +static void Block_CalcStretch(BlockID block) { + /* faces which can be stretched on X axis */ + if (Blocks.MinBB[block].x == 0.0f && Blocks.MaxBB[block].x == 1.0f) { + Blocks.CanStretch[block] |= 0x3C; + } else { + Blocks.CanStretch[block] &= 0xC3; /* ~0x3C */ + } + + /* faces which can be stretched on Z axis */ + if (Blocks.MinBB[block].z == 0.0f && Blocks.MaxBB[block].z == 1.0f) { + Blocks.CanStretch[block] |= 0x03; + } else { + Blocks.CanStretch[block] &= 0xFC; /* ~0x03 */ + } +} + +static cc_bool Block_MightCull(BlockID block, BlockID other) { + cc_uint8 bType, oType; + /* Sprite blocks can never cull blocks. */ + if (Blocks.Draw[block] == DRAW_SPRITE) return false; + + /* NOTE: Water is always culled by lava */ + if ((block == BLOCK_WATER || block == BLOCK_STILL_WATER) + && (other == BLOCK_LAVA || other == BLOCK_STILL_LAVA)) + return true; + + /* All blocks (except for say leaves) cull with themselves */ + if (block == other) return Blocks.Draw[block] != DRAW_TRANSPARENT_THICK; + + /* An opaque neighbour (asides from lava) culls this block. */ + if (Blocks.Draw[other] == DRAW_OPAQUE && !Blocks.IsLiquid[other]) return true; + /* Transparent/Gas blocks don't cull other blocks (except themselves) */ + if (Blocks.Draw[block] != DRAW_TRANSLUCENT || Blocks.Draw[other] != DRAW_TRANSLUCENT) return false; + + /* Some translucent blocks may still cull other translucent blocks */ + /* e.g. for water/ice, don't need to draw faces of water */ + bType = Blocks.Collide[block]; oType = Blocks.Collide[other]; + return (bType == COLLIDE_SOLID && oType == COLLIDE_SOLID) || bType != COLLIDE_SOLID; +} + +static void Block_CalcCulling(BlockID block, BlockID other) { + Vec3 bMin, bMax, oMin, oMax; + cc_bool occludedX, occludedY, occludedZ, bothLiquid; + int f; + + /* Fast path: Full opaque neighbouring blocks will always have all shared faces hidden */ + if (Blocks.FullOpaque[block] && Blocks.FullOpaque[other]) { + Blocks.Hidden[(block * BLOCK_COUNT) + other] = 0x3F; + return; + } + + /* Some blocks may not cull 'other' block, in which case just skip detailed check */ + /* e.g. sprite blocks, default leaves, will not cull any other blocks */ + if (!Block_MightCull(block, other)) { + Blocks.Hidden[(block * BLOCK_COUNT) + other] = 0; + return; + } + + bMin = Blocks.MinBB[block]; bMax = Blocks.MaxBB[block]; + oMin = Blocks.MinBB[other]; oMax = Blocks.MaxBB[other]; + + /* Extend offsets of liquid down to match rendered position */ + /* This isn't completely correct, but works well enough */ + if (Blocks.IsLiquid[block]) bMax.y -= 1.50f / 16.0f; + if (Blocks.IsLiquid[other]) oMax.y -= 1.50f / 16.0f; + + bothLiquid = Blocks.IsLiquid[block] && Blocks.IsLiquid[other]; + f = 0; /* mark all faces initially 'not hidden' */ + + /* Whether the 'texture region' of a face on block fits inside corresponding region on other block */ + occludedX = (bMin.z >= oMin.z && bMax.z <= oMax.z) && (bMin.y >= oMin.y && bMax.y <= oMax.y); + occludedY = (bMin.x >= oMin.x && bMax.x <= oMax.x) && (bMin.z >= oMin.z && bMax.z <= oMax.z); + occludedZ = (bMin.x >= oMin.x && bMax.x <= oMax.x) && (bMin.y >= oMin.y && bMax.y <= oMax.y); + + f |= occludedX && oMax.x == 1.0f && bMin.x == 0.0f ? (1 << FACE_XMIN) : 0; + f |= occludedX && oMin.x == 0.0f && bMax.x == 1.0f ? (1 << FACE_XMAX) : 0; + f |= occludedZ && oMax.z == 1.0f && bMin.z == 0.0f ? (1 << FACE_ZMIN) : 0; + f |= occludedZ && oMin.z == 0.0f && bMax.z == 1.0f ? (1 << FACE_ZMAX) : 0; + f |= occludedY && (bothLiquid || (oMax.y == 1.0f && bMin.y == 0.0f)) ? (1 << FACE_YMIN) : 0; + f |= occludedY && (bothLiquid || (oMin.y == 0.0f && bMax.y == 1.0f)) ? (1 << FACE_YMAX) : 0; + Blocks.Hidden[(block * BLOCK_COUNT) + other] = f; +} + +/* Updates culling data of all blocks */ +static void Block_UpdateAllCulling(void) { + int block, neighbour; + + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) { + Block_CalcStretch((BlockID)block); + for (neighbour = BLOCK_AIR; neighbour < BLOCK_COUNT; neighbour++) { + Block_CalcCulling((BlockID)block, (BlockID)neighbour); + } + } +} + +/* Updates culling data just for this block */ +/* (e.g. whether block can be stretched, visibility with other blocks) */ +static void Block_UpdateCulling(BlockID block) { + int neighbour; + Block_CalcStretch(block); + + for (neighbour = BLOCK_AIR; neighbour < BLOCK_COUNT; neighbour++) { + Block_CalcCulling(block, (BlockID)neighbour); + Block_CalcCulling((BlockID)neighbour, block); + } +} + + +/*########################################################################################################################* +*---------------------------------------------------------Block-----------------------------------------------------------* +*#########################################################################################################################*/ +static cc_uint32 definedCustomBlocks[BLOCK_COUNT >> 5]; +static char Block_NamesBuffer[STRING_SIZE * BLOCK_COUNT]; +#define Block_NamePtr(i) &Block_NamesBuffer[STRING_SIZE * i] + +cc_bool Block_IsCustomDefined(BlockID block) { + return (definedCustomBlocks[block >> 5] & (1u << (block & 0x1F))) != 0; +} + +/* Sets whether the given block has been changed from default */ +static void Block_SetCustomDefined(BlockID block, cc_bool defined) { + if (defined) { + definedCustomBlocks[block >> 5] |= (1u << (block & 0x1F)); + } else { + definedCustomBlocks[block >> 5] &= ~(1u << (block & 0x1F)); + } +} + +void Block_DefineCustom(BlockID block, cc_bool checkSprite) { + PackedCol black = PackedCol_Make(0, 0, 0, 255); + cc_string name = Block_UNSAFE_GetName(block); + /* necessary if servers redefined core blocks, before extended collide types were added */ + cc_uint8 collide = DefaultSet_MapOldCollide(block, Blocks.Collide[block]); + Blocks.Tinted[block] = Blocks.FogCol[block] != black && String_IndexOf(&name, '#') >= 0; + + Block_SetCollide(block, collide); + Block_SetDrawType(block, Blocks.Draw[block]); + Block_CalcRenderBounds(block); + Block_UpdateCulling(block); + Block_CalcLightOffset(block); + + Inventory_AddDefault(block); + Block_SetCustomDefined(block, true); + Event_RaiseVoid(&BlockEvents.BlockDefChanged); + + if (!checkSprite) return; /* TODO eliminate this */ + /* Update sprite BoundingBox if necessary */ + if (Blocks.Draw[block] == DRAW_SPRITE) Block_RecalculateBB(block); +} + +void Block_UndefineCustom(BlockID block) { + Block_ResetProps(block); + Block_UpdateCulling(block); + + Inventory_Remove(block); + if (block <= BLOCK_MAX_CPE) { Inventory_AddDefault(block); } + + Block_SetCustomDefined(block, false); + Event_RaiseVoid(&BlockEvents.BlockDefChanged); + + /* Update sprite BoundingBox if necessary */ + if (Blocks.Draw[block] == DRAW_SPRITE) Block_RecalculateBB(block); +} + +void Block_ResetProps(BlockID block) { + const struct SimpleBlockDef* def = block <= Game_Version.MaxCoreBlock ? &core_blockDefs[block] : &invalid_blockDef; + const cc_string name = String_FromReadonly(def->name); + + Blocks.BlocksLight[block] = def->blocksLight; + Blocks.Brightness[block] = def->brightness; + Blocks.FogCol[block] = def->fogColor; + Blocks.FogDensity[block] = def->fogDensity / 100.0f; + Block_SetCollide(block, def->collide); + Blocks.DigSounds[block] = def->digSound; + Blocks.StepSounds[block] = def->stepSound; + Blocks.SpeedMultiplier[block] = 1.0f; + Block_SetName(block, &name); + Blocks.Tinted[block] = false; + Blocks.SpriteOffset[block] = 0; + + Blocks.Draw[block] = def->draw; + if (def->draw == DRAW_SPRITE) { + Vec3_Set(Blocks.MinBB[block], 2.50f/16.0f, 0, 2.50f/16.0f); + Vec3_Set(Blocks.MaxBB[block], 13.5f/16.0f, 1, 13.5f/16.0f); + } else { + Vec3_Set(Blocks.MinBB[block], 0, 0, 0); + Vec3_Set(Blocks.MaxBB[block], 1, def->height / 16.0f, 1); + } + + Block_SetDrawType(block, def->draw); + Block_CalcRenderBounds(block); + Block_CalcLightOffset(block); + + Block_Tex(block, FACE_YMAX) = def->topTexture; + Block_Tex(block, FACE_YMIN) = def->bottomTexture; + Block_SetSide(def->sideTexture, block); + + Blocks.ParticleGravity[block] = 5.4f * (def->gravity / 100.0f); +} + +STRING_REF cc_string Block_UNSAFE_GetName(BlockID block) { + return String_FromRaw(Block_NamePtr(block), STRING_SIZE); +} + +void Block_SetName(BlockID block, const cc_string* name) { + String_CopyToRaw(Block_NamePtr(block), STRING_SIZE, name); +} + +int Block_FindID(const cc_string* name) { + cc_string blockName; + int block; + + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) + { + blockName = Block_UNSAFE_GetName(block); + if (String_CaselessEquals(&blockName, name)) return block; + } + return -1; +} + +int Block_Parse(const cc_string* name) { + int b; + if (Convert_ParseInt(name, &b) && b < BLOCK_COUNT) return b; + return Block_FindID(name); +} + +/* 0b_1000_0000 */ +#define USE_MODERN_BRIGHTNESS_FLAG 1 << 7 +/* 0b_0100_0000 */ +#define USE_LAMP_COLOR 1 << 6 +/* 0b_0000_1111 */ +#define BRIGHTNESS_MASK FANCY_LIGHTING_MAX_LEVEL + +/* Reads network format 0b_US--_LLLL where U = uses fancy brightness, S = uses lamp brightness, and L = brightness */ +/* Into CC's native format 0b_SSSS_BBBB where S = lamp brightness and B = lava brightness */ +cc_uint8 Block_ReadBrightness(cc_uint8 fullBright) { + cc_bool useSun; + /* If the fullBright byte does not use the flag, we should interpret it as either completely dark or casting max block light */ + if ((fullBright & USE_MODERN_BRIGHTNESS_FLAG) == 0) { return fullBright > 0 ? FANCY_LIGHTING_MAX_LEVEL : 0; } + + useSun = fullBright & USE_LAMP_COLOR; + + /* Preserve only the least significant four bits. This gives us our raw brightness level for sun or block light. */ + fullBright &= BRIGHTNESS_MASK; + + /* Sun light is stored in the upper four bits */ + if (useSun) { fullBright <<= FANCY_LIGHTING_LAMP_SHIFT; } + return fullBright; +} + +/* Writes CC's native format 0b_SSSS_BBBB where S = lamp brightness and B = lava brightness */ +/* into network format 0b_US--_LLLL where U = uses fancy brightness, S = uses lamp brightness, and L = brightness */ +cc_uint8 Block_WriteFullBright(cc_uint8 brightness) { + cc_uint8 lavaBrightness, lampBrightness, fullBright; + lavaBrightness = brightness & BRIGHTNESS_MASK; + lampBrightness = brightness >> FANCY_LIGHTING_LAMP_SHIFT; + fullBright = USE_MODERN_BRIGHTNESS_FLAG; + + /* Modern brightness stored in a fullbright value is mutually exclusive between using block and using sun light */ + if (lavaBrightness > 0) { + fullBright |= lavaBrightness; + } else if (lampBrightness > 0) { + fullBright |= USE_LAMP_COLOR; /* Insert flag that tells us this fullbright value should be interpreted as sun brightness */ + fullBright |= lampBrightness; + } else { + return 0; + } + return fullBright; +} + +/*########################################################################################################################* +*-------------------------------------------------------AutoRotate--------------------------------------------------------* +*#########################################################################################################################*/ +cc_bool AutoRotate_Enabled; + +#define AR_GROUP_CORNERS 0 +#define AR_GROUP_VERTICAL 1 +#define AR_GROUP_DIRECTION 2 +#define AR_GROUP_PILLAR 3 + +#define AR_EQ1(x) (dir0 == x && dir1 == '\0') +#define AR_EQ2(x, y) (dir0 == x && dir1 == y) +static int AR_CalcGroup(const cc_string* dir) { + char dir0, dir1; + dir0 = dir->length > 1 ? dir->buffer[1] : '\0'; Char_MakeLower(dir0); + dir1 = dir->length > 2 ? dir->buffer[2] : '\0'; Char_MakeLower(dir1); + + if (AR_EQ2('n','w') || AR_EQ2('n','e') || AR_EQ2('s','w') || AR_EQ2('s','e')) { + return AR_GROUP_CORNERS; + } else if (AR_EQ1('u') || AR_EQ1('d')) { + return AR_GROUP_VERTICAL; + } else if (AR_EQ1('n') || AR_EQ1('w') || AR_EQ1('s') || AR_EQ1('e')) { + return AR_GROUP_DIRECTION; + } else if (AR_EQ2('u','d') || AR_EQ2('w','e') || AR_EQ2('n','s')) { + return AR_GROUP_PILLAR; + } + return -1; +} + +/* replaces a portion of a string, appends otherwise */ +static void AutoRotate_Insert(cc_string* str, int offset, const char* suffix) { + int i = str->length - offset; + + for (; *suffix; suffix++, i++) { + if (i < str->length) { + str->buffer[i] = *suffix; + } else { + String_Append(str, *suffix); + } + } +} +/* finds proper rotated form of a block, based on the given name */ +static int FindRotated(cc_string* name, int offset); + +static int GetRotated(cc_string* name, int offset) { + int rotated = FindRotated(name, offset); + return rotated == -1 ? Block_FindID(name) : rotated; +} + +static int RotateCorner(cc_string* name, int offset) { + float x = Game_SelectedPos.intersect.x - (float)Game_SelectedPos.translatedPos.x; + float z = Game_SelectedPos.intersect.z - (float)Game_SelectedPos.translatedPos.z; + + if (x < 0.5f && z < 0.5f) { + AutoRotate_Insert(name, offset, "-NW"); + } else if (x >= 0.5f && z < 0.5f) { + AutoRotate_Insert(name, offset, "-NE"); + } else if (x < 0.5f && z >= 0.5f) { + AutoRotate_Insert(name, offset, "-SW"); + } else if (x >= 0.5f && z >= 0.5f) { + AutoRotate_Insert(name, offset, "-SE"); + } + return GetRotated(name, offset); +} + +static int RotateVertical(cc_string* name, int offset) { + float y = Game_SelectedPos.intersect.y - (float)Game_SelectedPos.translatedPos.y; + + if (y >= 0.5f) { + AutoRotate_Insert(name, offset, "-U"); + } else { + AutoRotate_Insert(name, offset, "-D"); + } + return GetRotated(name, offset); +} + +static int RotateFence(cc_string* name, int offset) { + /* Fence type blocks */ + float yaw = Math_ClampAngle(Entities.CurPlayer->Base.Yaw); + + if (yaw < 45.0f || (yaw >= 135.0f && yaw < 225.0f) || yaw > 315.0f) { + AutoRotate_Insert(name, offset, "-WE"); + } else { + AutoRotate_Insert(name, offset, "-NS"); + } + return GetRotated(name, offset); +} + +static int RotatePillar(cc_string* name, int offset) { + /* Thin pillar type blocks */ + Face face = Game_SelectedPos.closest; + + if (face == FACE_YMAX || face == FACE_YMIN) { + AutoRotate_Insert(name, offset, "-UD"); + } else if (face == FACE_XMAX || face == FACE_XMIN) { + AutoRotate_Insert(name, offset, "-WE"); + } else if (face == FACE_ZMAX || face == FACE_ZMIN) { + AutoRotate_Insert(name, offset, "-NS"); + } + return GetRotated(name, offset); +} + +static int RotateDirection(cc_string* name, int offset) { + float yaw = Math_ClampAngle(Entities.CurPlayer->Base.Yaw); + + if (yaw >= 45.0f && yaw < 135.0f) { + AutoRotate_Insert(name, offset, "-E"); + } else if (yaw >= 135.0f && yaw < 225.0f) { + AutoRotate_Insert(name, offset, "-S"); + } else if (yaw >= 225.0f && yaw < 315.0f) { + AutoRotate_Insert(name, offset, "-W"); + } else { + AutoRotate_Insert(name, offset, "-N"); + } + return GetRotated(name, offset); +} + +static int FindRotated(cc_string* name, int offset) { + cc_string dir; + int group; + int dirIndex = String_LastIndexOfAt(name, offset, '-'); + if (dirIndex == -1) return -1; /* not a directional block */ + + dir = String_UNSAFE_SubstringAt(name, dirIndex); + dir.length -= offset; + if (dir.length > 3) return -1; + + offset += dir.length; + group = AR_CalcGroup(&dir); + + if (group == AR_GROUP_CORNERS) return RotateCorner(name, offset); + if (group == AR_GROUP_VERTICAL) return RotateVertical(name, offset); + if (group == AR_GROUP_DIRECTION) return RotateDirection(name, offset); + + if (group == AR_GROUP_PILLAR) { + AutoRotate_Insert(name, offset, "-UD"); + if (Block_FindID(name) == -1) { + return RotateFence(name, offset); + } else { + return RotatePillar(name, offset); + } + } + return -1; +} + +BlockID AutoRotate_RotateBlock(BlockID block) { + cc_string str; char strBuffer[STRING_SIZE * 2]; + cc_string name; + int rotated; + + name = Block_UNSAFE_GetName(block); + String_InitArray(str, strBuffer); + String_AppendString(&str, &name); + + /* need to copy since we change characters in name */ + rotated = FindRotated(&str, 0); + return rotated == -1 ? block : (BlockID)rotated; +} + +static void GetAutoRotateTypes(cc_string* name, int* dirTypes) { + int dirIndex, i; + cc_string dir; + dirTypes[0] = -1; + dirTypes[1] = -1; + + for (i = 0; i < 2; i++) { + /* index of rightmost group separated by dashes */ + dirIndex = String_LastIndexOf(name, '-'); + if (dirIndex == -1) return; + + dir = String_UNSAFE_SubstringAt(name, dirIndex); + dirTypes[i] = AR_CalcGroup(&dir); + name->length = dirIndex; + } +} + +cc_bool AutoRotate_BlocksShareGroup(BlockID block, BlockID other) { + cc_string bName, oName; + int bDirTypes[2], oDirTypes[2]; + + bName = Block_UNSAFE_GetName(block); + GetAutoRotateTypes(&bName, bDirTypes); + if (bDirTypes[0] == -1) return false; + + oName = Block_UNSAFE_GetName(other); + GetAutoRotateTypes(&oName, oDirTypes); + if (oDirTypes[0] == -1) return false; + + return bDirTypes[0] == oDirTypes[0] && bDirTypes[1] == oDirTypes[1] + && String_CaselessEquals(&bName, &oName); +} + + +/*########################################################################################################################* +*----------------------------------------------------Blocks component-----------------------------------------------------* +*#########################################################################################################################*/ +static void OnReset(void) { + int i, block; + for (i = 0; i < Array_Elems(definedCustomBlocks); i++) { + definedCustomBlocks[i] = 0; + } + + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) { + Block_ResetProps((BlockID)block); + } + + Block_UpdateAllCulling(); + Block_RecalculateAllSpriteBB(); + + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) { + Blocks.CanPlace[block] = true; + Blocks.CanDelete[block] = true; + } +} + +static void OnAtlasChanged(void* obj) { Block_RecalculateAllSpriteBB(); } +static void OnInit(void) { + AutoRotate_Enabled = true; + Event_Register_(&TextureEvents.AtlasChanged, NULL, OnAtlasChanged); + OnReset(); +} + +struct IGameComponent Blocks_Component = { + OnInit, /* Init */ + NULL, /* Free */ + OnReset, /* Reset */ +}; |