MorriCraft v2.2.9: Major performance optimizations, viewmodel fixes, and critical bug fixes

This commit is contained in:
Michael Howard 2026-04-24 20:01:11 -05:00
parent 5530a1a58c
commit 4e8bc59eeb
50 changed files with 295 additions and 280 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

After

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 B

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 226 B

1
assets/version.txt Normal file
View File

@ -0,0 +1 @@
v2.2.8

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

After

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 B

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 226 B

View File

@ -1 +1 @@
v2.2.7
v2.2.8

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Binary file not shown.

View File

@ -20,7 +20,7 @@
#define PI 3.1415926535f
#endif
#define CHUNK_HEIGHT 128
#define RENDER_DISTANCE 4
#define RENDER_DISTANCE 8
enum BlockType {
AIR = 0, GRASS = 1, DIRT = 2, COBBLESTONE = 3, LOG = 4, LEAVES = 5, PLANK = 6,
@ -122,13 +122,18 @@ struct ChunkPosHash {
}
};
struct BlockRenderData {
float x, y, z;
unsigned char faces; // 1:front, 2:back, 4:top, 8:bottom, 16:right, 32:left
};
struct Chunk {
int blocks[CHUNK_SIZE][CHUNK_HEIGHT][CHUNK_SIZE];
int maxY = 0;
bool generated = false;
bool modified = false;
bool dirty = true;
std::vector<Vector3> renderLists[32];
std::vector<BlockRenderData> renderLists[32];
Chunk() : generated(false), modified(false), maxY(0), dirty(true) {}
};
@ -150,7 +155,7 @@ static Sound hitSound;
enum MenuState { MAIN_MENU, OPTIONS_MENU, CREATE_WORLD_MENU, LOAD_WORLD_MENU, GAMEPLAY, PAUSE_MENU, CRAFTING_GUI, CHECKING_UPDATES, UPDATE_NOTES, UPDATE_FOUND, DOWNLOADING_UPDATE, CONNECT_MENU, SKIN_EDITOR, WORLD_CREATION_PROGRESS };
// Forward Declarations
bool IsExposedOptimized(int lx, int ly, int lz, Chunk* chunk, Chunk* nxM, Chunk* nxP, Chunk* nzM, Chunk* nzP);
unsigned char GetExposedFaces(int lx, int ly, int lz, Chunk* chunk, Chunk* nxM, Chunk* nxP, Chunk* nzM, Chunk* nzP);
void RebuildChunkRenderList(Chunk* chunk, int cx, int cz);
void GenerateChunk(int cx, int cz);
void SetBlock(int x, int y, int z, int type);
@ -230,6 +235,11 @@ void AddToInventory(int blockType) {
// Inventory full — item is lost for now
}
// Helper to give multiple items
void GiveItems(int type, int count) {
for (int i = 0; i < count; i++) AddToInventory(type);
}
// Camera look angles — global so world load/create can reset them cleanly
static float camYaw = 0.0f;
static float camPitch = 0.0f;
@ -280,18 +290,35 @@ int GetBlock(int x, int y, int z) {
return 0; // Air if chunk not loaded
}
unsigned char GetExposedFaces(int lx, int ly, int lz, Chunk* chunk, Chunk* nxM, Chunk* nxP, Chunk* nzM, Chunk* nzP) {
auto IsTrans = [](int bt) { return bt == AIR || bt == LEAVES; };
unsigned char mask = 0;
// Front (+Z)
if (lz < CHUNK_SIZE - 1) { if (IsTrans(chunk->blocks[lx][ly][lz+1])) mask |= 1; }
else if (nzP) { if (IsTrans(nzP->blocks[lx][ly][0])) mask |= 1; } else mask |= 1;
// Back (-Z)
if (lz > 0) { if (IsTrans(chunk->blocks[lx][ly][lz-1])) mask |= 2; }
else if (nzM) { if (IsTrans(nzM->blocks[lx][ly][CHUNK_SIZE-1])) mask |= 2; } else mask |= 2;
// Top (+Y)
if (ly < CHUNK_HEIGHT - 1) { if (IsTrans(chunk->blocks[lx][ly+1][lz])) mask |= 4; } else mask |= 4;
// Bottom (-Y)
if (ly > 0) { if (IsTrans(chunk->blocks[lx][ly-1][lz])) mask |= 8; } else mask |= 8;
// Right (+X)
if (lx < CHUNK_SIZE - 1) { if (IsTrans(chunk->blocks[lx+1][ly][lz])) mask |= 16; }
else if (nxP) { if (IsTrans(nxP->blocks[0][ly][lz])) mask |= 16; } else mask |= 16;
// Left (-X)
if (lx > 0) { if (IsTrans(chunk->blocks[lx-1][ly][lz])) mask |= 32; }
else if (nxM) { if (IsTrans(nxM->blocks[CHUNK_SIZE-1][ly][lz])) mask |= 32; } else mask |= 32;
return mask;
}
void RebuildChunkRenderList(Chunk* chunk, int cx, int cz) {
for (int i = 0; i < 32; i++) chunk->renderLists[i].clear();
// Neighbors for exposure check
auto itNM = worldChunks.find({cx-1, cz});
Chunk* nxM = (itNM != worldChunks.end()) ? itNM->second : nullptr;
auto itNP = worldChunks.find({cx+1, cz});
Chunk* nxP = (itNP != worldChunks.end()) ? itNP->second : nullptr;
auto itZM = worldChunks.find({cx, cz-1});
Chunk* nzM = (itZM != worldChunks.end()) ? itZM->second : nullptr;
auto itZP = worldChunks.find({cx, cz+1});
Chunk* nzP = (itZP != worldChunks.end()) ? itZP->second : nullptr;
auto itNM = worldChunks.find({cx-1, cz}); Chunk* nxM = (itNM != worldChunks.end()) ? itNM->second : nullptr;
auto itNP = worldChunks.find({cx+1, cz}); Chunk* nxP = (itNP != worldChunks.end()) ? itNP->second : nullptr;
auto itZM = worldChunks.find({cx, cz-1}); Chunk* nzM = (itZM != worldChunks.end()) ? itZM->second : nullptr;
auto itZP = worldChunks.find({cx, cz+1}); Chunk* nzP = (itZP != worldChunks.end()) ? itZP->second : nullptr;
int worldX = cx * CHUNK_SIZE;
int worldZ = cz * CHUNK_SIZE;
@ -302,8 +329,9 @@ void RebuildChunkRenderList(Chunk* chunk, int cx, int cz) {
int bt = chunk->blocks[lx][ly][lz];
if (bt == AIR) continue;
if (IsExposedOptimized(lx, ly, lz, chunk, nxM, nxP, nzM, nzP)) {
chunk->renderLists[bt].push_back((Vector3){(float)(worldX+lx), (float)ly, (float)(worldZ+lz)});
unsigned char faces = GetExposedFaces(lx, ly, lz, chunk, nxM, nxP, nzM, nzP);
if (faces != 0) {
chunk->renderLists[bt].push_back({ (float)(worldX+lx), (float)ly, (float)(worldZ+lz), faces });
}
}
}
@ -535,6 +563,10 @@ void GenerateChunk(int cx, int cz) {
if (LoadChunk(newChunk, cx, cz)) {
worldChunks[key] = newChunk;
auto n1 = worldChunks.find({cx-1, cz}); if (n1 != worldChunks.end()) n1->second->dirty = true;
auto n2 = worldChunks.find({cx+1, cz}); if (n2 != worldChunks.end()) n2->second->dirty = true;
auto n3 = worldChunks.find({cx, cz-1}); if (n3 != worldChunks.end()) n3->second->dirty = true;
auto n4 = worldChunks.find({cx, cz+1}); if (n4 != worldChunks.end()) n4->second->dirty = true;
return;
}
@ -623,10 +655,10 @@ void GenerateChunk(int cx, int cz) {
}
newChunk->generated = true;
newChunk->modified = true; // Newly generated, so we should save it
newChunk->modified = true;
worldChunks[key] = newChunk;
// Mark neighbors as dirty so they rebuild their render lists with new occlusion data
// Mark neighbors as dirty (Bug #2 fix)
ChunkPos neighbors[] = { {cx-1, cz}, {cx+1, cz}, {cx, cz-1}, {cx, cz+1} };
for (auto& nPos : neighbors) {
auto it = worldChunks.find(nPos);
@ -708,163 +740,102 @@ void DrawTexturedCube(Vector3 position, float width, float height, float length,
// Draws a grass block with correct per-face textures:
// top=green grass, sides=dirt+grass stripe, bottom=pure dirt.
// Caller must NOT have an active rlSetTexture — this function manages its own binds.
void DrawGrassBlock(Vector3 position, unsigned int sideTexId, unsigned int topTexId, unsigned int botTexId, Color tint) {
float x = position.x;
float y = position.y;
float z = position.z;
// ---- SIDES (4 faces) ----
rlSetTexture(sideTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
// Front
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f);
// Back
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f);
// Right
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f);
// Left
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f);
rlEnd();
// ---- TOP (pure green) ----
rlSetTexture(topTexId);
rlBegin(RL_QUADS);
// Combine grass green with ambient light
rlColor4ub((unsigned char)(124 * (tint.r/255.0f)),
(unsigned char)(189 * (tint.g/255.0f)),
(unsigned char)(107 * (tint.b/255.0f)), 255);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f);
// ---- BOTTOM (pure dirt) ----
rlSetTexture(botTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f);
rlEnd();
}
// Draws a crafting table with custom top and sides
void DrawCraftingTable(Vector3 position, unsigned int sideTexId, unsigned int topTexId, unsigned int botTexId, Color tint) {
float x = position.x; float y = position.y; float z = position.z;
// SIDES
rlSetTexture(sideTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
// Front, Back, Right, Left
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f);
rlEnd();
// TOP
rlSetTexture(topTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f);
rlEnd();
// BOTTOM (dirt)
rlSetTexture(botTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f);
rlEnd();
}
void DrawLog(Vector3 position, unsigned int sideTexId, unsigned int topTexId, Color tint) {
float x = position.x; float y = position.y; float z = position.z;
rlSetTexture(sideTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f);
rlEnd();
rlSetTexture(topTexId);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, 255);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f);
rlEnd();
}
// Optimized check to see if a block has any exposed faces.
bool IsExposedOptimized(int lx, int ly, int lz, Chunk* chunk, Chunk* nxM, Chunk* nxP, Chunk* nzM, Chunk* nzP) {
if (ly > 0) { if (chunk->blocks[lx][ly-1][lz] == AIR) return true; } else return true;
if (ly < CHUNK_HEIGHT - 1) { if (chunk->blocks[lx][ly+1][lz] == AIR) return true; } else return true;
if (lx > 0) { if (chunk->blocks[lx-1][ly][lz] == AIR) return true; }
else if (nxM) { if (nxM->blocks[CHUNK_SIZE-1][ly][lz] == AIR) return true; } else return true;
if (lx < CHUNK_SIZE - 1) { if (chunk->blocks[lx+1][ly][lz] == AIR) return true; }
else if (nxP) { if (nxP->blocks[0][ly][lz] == AIR) return true; } else return true;
if (lz > 0) { if (chunk->blocks[lx][ly][lz-1] == AIR) return true; }
else if (nzM) { if (nzM->blocks[lx][ly][CHUNK_SIZE-1] == AIR) return true; } else return true;
if (lz < CHUNK_SIZE - 1) { if (chunk->blocks[lx][ly][lz+1] == AIR) return true; }
else if (nzP) { if (nzP->blocks[lx][ly][0] == AIR) return true; } else return true;
return false;
}
// Optimized helper for batched rendering: Draws only the vertices (no texture bind or rlBegin/End)
void DrawCubeVertices(float x, float y, float z, float w, float h, float l) {
void DrawCubeVertices(float x, float y, float z, float w, float h, float l, unsigned char mask) {
float hw = w/2.0f; float hh = h/2.0f; float hl = l/2.0f;
// Front
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + hw, y + hh, z + hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - hw, y + hh, z + hl);
// Back
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - hw, y - hh, z - hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + hw, y - hh, z - hl);
// Top
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - hw, y + hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + hw, y + hh, z + hl);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + hw, y + hh, z - hl);
// Bottom
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - hw, y - hh, z - hl);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + hw, y - hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - hw, y - hh, z + hl);
// Right
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + hw, y - hh, z - hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + hw, y + hh, z + hl);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + hw, y - hh, z + hl);
// Left
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - hw, y - hh, z - hl);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - hw, y + hh, z + hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - hw, y + hh, z - hl);
if (mask & 1) { // Front (+Z)
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + hw, y + hh, z + hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - hw, y + hh, z + hl);
}
if (mask & 2) { // Back (-Z)
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - hw, y - hh, z - hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + hw, y - hh, z - hl);
}
if (mask & 4) { // Top (+Y)
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - hw, y + hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + hw, y + hh, z + hl);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + hw, y + hh, z - hl);
}
if (mask & 8) { // Bottom (-Y)
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - hw, y - hh, z - hl);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + hw, y - hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - hw, y - hh, z + hl);
}
if (mask & 16) { // Right (+X)
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + hw, y - hh, z - hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + hw, y + hh, z - hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + hw, y + hh, z + hl);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + hw, y - hh, z + hl);
}
if (mask & 32) { // Left (-X)
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - hw, y - hh, z - hl);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - hw, y - hh, z + hl);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - hw, y + hh, z + hl);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - hw, y + hh, z - hl);
}
}
void DrawGrassBlock(Vector3 position, unsigned int sideTexId, unsigned int topTexId, unsigned int botTexId, Color tint, unsigned char mask) {
float x = position.x; float y = position.y; float z = position.z;
if (mask & 51) { // Sides (Front, Back, Right, Left: 1|2|16|32 = 51)
rlSetTexture(sideTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255);
if (mask & 1) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); }
if (mask & 2) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); }
if (mask & 16) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); }
if (mask & 32) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); }
rlEnd();
}
if (mask & 4) { // Top
rlSetTexture(topTexId); rlBegin(RL_QUADS);
rlColor4ub((unsigned char)(124 * (tint.r/255.0f)), (unsigned char)(189 * (tint.g/255.0f)), (unsigned char)(107 * (tint.b/255.0f)), 255);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlEnd();
}
if (mask & 8) { // Bottom
rlSetTexture(botTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlEnd();
}
}
void DrawLog(Vector3 position, unsigned int sideTexId, unsigned int topTexId, Color tint, unsigned char mask) {
float x = position.x; float y = position.y; float z = position.z;
if (mask & 51) {
rlSetTexture(sideTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255);
if (mask & 1) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); }
if (mask & 2) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); }
if (mask & 16) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); }
if (mask & 32) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); }
rlEnd();
}
if (mask & 12) { // Top + Bottom
rlSetTexture(topTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255);
if (mask & 4) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); }
if (mask & 8) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); }
rlEnd();
}
}
void DrawCraftingTable(Vector3 position, unsigned int sideTexId, unsigned int topTexId, unsigned int botTexId, Color tint, unsigned char mask) {
float x = position.x; float y = position.y; float z = position.z;
if (mask & 51) {
rlSetTexture(sideTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255);
if (mask & 1) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); }
if (mask & 2) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); }
if (mask & 16) { rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); }
if (mask & 32) { rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); }
rlEnd();
}
if (mask & 4) { rlSetTexture(topTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - 0.5f, y + 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + 0.5f, y + 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + 0.5f, y + 0.5f, z - 0.5f); rlEnd(); }
if (mask & 8) { rlSetTexture(botTexId); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, 255); rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + 0.5f, y - 0.5f, z - 0.5f); rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + 0.5f, y - 0.5f, z + 0.5f); rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - 0.5f, y - 0.5f, z + 0.5f); rlEnd(); }
}
// Optimized check to see if a block has any exposed faces
bool IsExposed(int x, int ly, int z, Chunk* chunk, int lx, int lz) {
// Local check (inside same chunk)
@ -960,7 +931,7 @@ int main(void)
float updateTimer = 0.0f;
float downloadProgress = 0.0f;
std::string latestVersion = "";
std::string localVersion = "v1.9.0"; // Default fallback
std::string localVersion = "v2.2.9"; // Default fallback
// Read local version
std::ifstream vfile("assets/version.txt");
@ -1595,6 +1566,19 @@ int main(void)
currentZoom += (targetZoom - currentZoom) * 0.05f;
camera.zoom = currentZoom;
// --- GLOBAL INPUTS (Outside gameplay state) ---
if (IsKeyPressed(KEY_E) && !isChatting && (currentState == GAMEPLAY || currentState == CRAFTING_GUI)) {
if (currentState == CRAFTING_GUI) {
currentState = GAMEPLAY;
DisableCursor();
} else {
inventoryOpen = !inventoryOpen;
if (inventoryOpen) EnableCursor();
else DisableCursor();
}
}
// Gameplay Update
if (currentState == GAMEPLAY) {
gameTime += GetFrameTime();
@ -1615,13 +1599,6 @@ int main(void)
if (scroll > 0.0f) activeHotbarSlot = (activeHotbarSlot - 1 + 9) % 9;
if (scroll < 0.0f) activeHotbarSlot = (activeHotbarSlot + 1) % 9;
// Toggle inventory
if (IsKeyPressed(KEY_E) && !isChatting) {
inventoryOpen = !inventoryOpen;
if (inventoryOpen) EnableCursor();
else DisableCursor();
}
// Dynamic Chunk Loading
int playerCX = (int)floorf(camera3D.position.x / CHUNK_SIZE);
int playerCZ = (int)floorf(camera3D.position.z / CHUNK_SIZE);
@ -1654,11 +1631,16 @@ int main(void)
} else if (cmd == "fly off") {
isFlying = false;
chatLog.push_back({ "[Server] Flight disabled", 5.0f });
} else if (cmd == "test") {
GiveItems(CRAFTING_TABLE, 1);
GiveItems(LOG, 64);
chatLog.push_back({ "[Server] Added test items to inventory", 5.0f });
} else if (cmd == "help") {
chatLog.push_back({ "[Server] Commands List:", 8.0f });
chatLog.push_back({ " /help - Shows this list", 8.0f });
chatLog.push_back({ " /seed - Shows the world seed", 8.0f });
chatLog.push_back({ " /fly [on|off] - Toggle flight mode", 8.0f });
chatLog.push_back({ " /test - Give basic crafting materials", 8.0f });
} else {
chatLog.push_back({ "[Server] Unknown command: /" + cmd, 5.0f });
}
@ -2000,71 +1982,69 @@ int main(void)
}
} else if (currentState == UPDATE_FOUND) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 220 });
int pw = 450, ph = 250;
int pw = 450, ph = 180;
Rectangle pBox = { (float)currentWidth/2 - pw/2, (float)currentHeight/2 - ph/2, (float)pw, (float)ph };
DrawRectangleRec(pBox, (Color){ 40, 40, 40, 255 });
DrawRectangleLinesEx(pBox, 4.0f, GREEN);
DrawTextEx(customFont, "NEW VERSION AVAILABLE!", (Vector2){ pBox.x + 40, pBox.y + 30 }, 24, 1.0f, GREEN);
std::string updMsg = latestVersion + " is now ready for download.";
DrawTextEx(customFont, updMsg.c_str(), (Vector2){ pBox.x + 40, pBox.y + 70 }, 18, 1.0f, LIGHTGRAY);
// Update Button
Rectangle updBtn = { pBox.x + 40, pBox.y + 120, 370, 40 };
Rectangle updBtn = { pBox.x + 40, pBox.y + 110, 370, 40 };
bool isUpdHovered = CheckCollisionPointRec(mousePos, updBtn);
DrawRectangleRec(updBtn, isUpdHovered ? GREEN : (Color){ 0, 100, 0, 255 });
DrawTextEx(customFont, "UPDATE NOW", (Vector2){ updBtn.x + 100, updBtn.y + 10 }, 20, 1.0f, WHITE);
if (isUpdHovered && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) currentState = DOWNLOADING_UPDATE;
// Update Notes Button
Rectangle noteBtn = { pBox.x + 40, pBox.y + 175, 370, 40 };
bool isNoteHovered = CheckCollisionPointRec(mousePos, noteBtn);
DrawRectangleRec(noteBtn, isNoteHovered ? DARKGRAY : BLACK);
DrawRectangleLinesEx(noteBtn, 2.0f, GRAY);
DrawTextEx(customFont, "VIEW UPDATE NOTES", (Vector2){ noteBtn.x + 65, noteBtn.y + 10 }, 20, 1.0f, WHITE);
if (isNoteHovered && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) currentState = UPDATE_NOTES;
} else if (currentState == DOWNLOADING_UPDATE) {
static bool startedDownload = false;
static bool downloadFailed = false;
static float fakeProgress = 0.0f;
static bool downloadFinished = false;
static float currentProgress = 0.0f;
if (!startedDownload) {
startedDownload = true;
// REAL DOWNLOAD ATTEMPT - Platform-aware URLs
#ifdef _WIN32
std::string binaryUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/build-windows/MorriCraft.exe";
std::string binaryName = "MorriCraft.exe";
#else
std::string binaryUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/build-linux/MorriCraft";
std::string binaryName = "MorriCraft";
#endif
std::string versionUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/version.txt";
// Download new binary and version file
int r1 = system(("curl -L -s -o " + binaryName + ".new \"" + binaryUrl + "\"").c_str());
int r2 = system(("curl -L -s -o version.txt.new \"" + versionUrl + "\"").c_str());
if (r1 == 0 && r2 == 0) {
currentProgress = 0.1f;
std::thread([]() {
std::string binaryUrl, binaryName;
#ifdef _WIN32
// Windows: rename old, move new into place
system(("rename " + binaryName + " " + binaryName + ".old").c_str());
system(("rename " + binaryName + ".new " + binaryName).c_str());
system("rename version.txt version.txt.old");
system("rename version.txt.new version.txt");
system("copy version.txt assets\\version.txt >nul 2>&1");
binaryUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/build-windows/MorriCraft.exe";
binaryName = "MorriCraft.exe";
#else
// Linux: chmod, move
system(("chmod +x " + binaryName + ".new").c_str());
system(("mv " + binaryName + " " + binaryName + ".old 2>/dev/null").c_str());
system(("mv " + binaryName + ".new " + binaryName).c_str());
system("mv version.txt.new version.txt");
system("cp version.txt assets/version.txt 2>/dev/null");
binaryUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/build-linux/MorriCraft";
binaryName = "MorriCraft";
#endif
fakeProgress = 1.0f;
} else {
downloadFailed = true;
}
std::string versionUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/version.txt";
// Download binary
int r1 = system(("curl -L -s -o " + binaryName + ".new \"" + binaryUrl + "\"").c_str());
currentProgress = 0.6f;
// Download version
int r2 = system(("curl -L -s -o version.txt.new \"" + versionUrl + "\"").c_str());
currentProgress = 0.8f;
if (r1 == 0 && r2 == 0) {
#ifdef _WIN32
system(("rename " + binaryName + " " + binaryName + ".old").c_str());
system(("rename " + binaryName + ".new " + binaryName).c_str());
system("rename version.txt version.txt.old");
system("rename version.txt.new version.txt");
system("copy version.txt assets\\version.txt >nul 2>&1");
#else
system(("chmod +x " + binaryName + ".new").c_str());
system(("mv " + binaryName + " " + binaryName + ".old 2>/dev/null").c_str());
system(("mv " + binaryName + ".new " + binaryName).c_str());
system("mv version.txt.new version.txt");
system("cp version.txt assets/version.txt 2>/dev/null");
#endif
currentProgress = 1.0f;
downloadFinished = true;
} else {
downloadFailed = true;
}
}).detach();
}
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 20, 20, 20, 255 });
@ -2072,35 +2052,33 @@ int main(void)
DrawTextEx(customFont, "DOWNLOAD FAILED! Check internet connection.", (Vector2){ (float)currentWidth/2 - 250, (float)currentHeight/2 - 40 }, 24, 1.0f, RED);
Rectangle backBtn = { (float)currentWidth/2 - 100, (float)currentHeight/2 + 20, 200, 40 };
if (CheckCollisionPointRec(mousePos, backBtn) && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
currentState = MAIN_MENU;
startedDownload = false;
downloadFailed = false;
currentState = MAIN_MENU; startedDownload = false; downloadFailed = false;
}
DrawRectangleRec(backBtn, DARKGRAY);
DrawTextEx(customFont, "BACK", (Vector2){ backBtn.x + 70, backBtn.y + 10 }, 20, 1.0f, WHITE);
} else {
fakeProgress += 0.001f; // Slow visual for user
if (fakeProgress > 1.0f) fakeProgress = 1.0f;
DrawTextEx(customFont, "DOWNLOADING & REPLACING SYSTEM FILES...", (Vector2){ (float)currentWidth/2 - 250, (float)currentHeight/2 - 60 }, 24, 1.0f, YELLOW);
int bw = 500, bh = 35;
Rectangle barBg = { (float)currentWidth/2 - bw/2, (float)currentHeight/2 - bh/2, (float)bw, (float)bh };
DrawRectangleRec(barBg, BLACK);
DrawRectangle(barBg.x, barBg.y, (int)(bw * currentProgress), bh, GREEN);
DrawRectangleLinesEx(barBg, 2.0f, GRAY);
DrawTextEx(customFont, TextFormat("%i%%", (int)(currentProgress * 100)), (Vector2){ barBg.x + bw + 15, barBg.y + 5 }, 20, 1.0f, WHITE);
DrawTextEx(customFont, "DOWNLOADING & REPLACING SYSTEM FILES...", (Vector2){ (float)currentWidth/2 - 250, (float)currentHeight/2 - 40 }, 24, 1.0f, YELLOW);
int bw = 450, bh = 30;
DrawRectangle(currentWidth/2 - bw/2, currentHeight/2, bw, bh, BLACK);
DrawRectangle(currentWidth/2 - bw/2, currentHeight/2, (int)(bw * (fakeProgress > 0.9f ? 0.9f : fakeProgress)), bh, GREEN);
if (fakeProgress >= 1.0f) {
if (downloadFinished) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 230 });
int pw = 450, ph = 200;
int pw = 500, ph = 220;
Rectangle pBox = { (float)currentWidth/2 - pw/2, (float)currentHeight/2 - ph/2, (float)pw, (float)ph };
DrawRectangleRec(pBox, (Color){ 30, 30, 30, 255 });
DrawRectangleLinesEx(pBox, 4.0f, GREEN);
DrawTextEx(customFont, "UPDATE SUCCESSFUL!", (Vector2){ pBox.x + 60, pBox.y + 40 }, 24, 1.0f, GREEN);
DrawTextEx(customFont, "PLEASE RESTART THE CLIENT.", (Vector2){ pBox.x + 60, pBox.y + 80 }, 20, 1.0f, WHITE);
DrawTextEx(customFont, "UPDATE SUCCESSFUL!", (Vector2){ pBox.x + 80, pBox.y + 40 }, 28, 1.0f, GREEN);
DrawTextEx(customFont, "PLEASE RESTART THE CLIENT TO APPLY.", (Vector2){ pBox.x + 60, pBox.y + 90 }, 20, 1.0f, WHITE);
Rectangle exitBtn = { pBox.x + pw/2 - 75, pBox.y + 130, 150, 40 };
Rectangle exitBtn = { pBox.x + pw/2 - 90, pBox.y + 145, 180, 45 };
if (CheckCollisionPointRec(mousePos, exitBtn) && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) exit(0);
DrawRectangleRec(exitBtn, MAROON);
DrawTextEx(customFont, "EXIT GAME", (Vector2){ exitBtn.x + 30, exitBtn.y + 10 }, 20, 1.0f, WHITE);
DrawTextEx(customFont, "EXIT GAME", (Vector2){ exitBtn.x + 40, exitBtn.y + 12 }, 20, 1.0f, WHITE);
}
}
} else if (currentState == SKIN_EDITOR) {
@ -2337,8 +2315,8 @@ int main(void)
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
EndMode2D();
// Show Version Number (v2.2.7) in Red
DrawTextEx(customFont, "v2.2.7", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
// Show Version Number (v2.2.9) in Red
DrawTextEx(customFont, "v2.2.9", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
// --- PLAYER NAME POPUP (IF MISSING) ---
if (playerName == "") {
@ -2566,30 +2544,30 @@ int main(void)
if (showDeleteConfirm) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 200 });
int confWidth = 400;
int confHeight = 200;
int confWidth = 550;
int confHeight = 240;
int confX = (currentWidth / 2) - (confWidth / 2);
int confY = (currentHeight / 2) - (confHeight / 2);
DrawRectangle(confX, confY, confWidth, confHeight, (Color){ 50, 20, 20, 255 });
DrawRectangleLinesEx((Rectangle){ (float)confX, (float)confY, (float)confWidth, (float)confHeight }, 4.0f, RED);
Vector2 warnSize = MeasureTextEx(customFont, "Delete World?", 24, 1.0f);
DrawTextEx(customFont, "Delete World?", (Vector2){ confX + (confWidth/2) - (warnSize.x/2), confY + 30 }, 24, 1.0f, WHITE);
Vector2 warnSize = MeasureTextEx(customFont, "Delete World?", 28, 1.0f);
DrawTextEx(customFont, "Delete World?", (Vector2){ confX + (confWidth/2) - (warnSize.x/2), confY + 30 }, 28, 1.0f, WHITE);
std::string warnText = "Delete '" + deletingWorldName + "'?";
Vector2 nameSize = MeasureTextEx(customFont, warnText.c_str(), 20, 1.0f);
DrawTextEx(customFont, warnText.c_str(), (Vector2){ confX + (confWidth/2) - (nameSize.x/2), confY + 70 }, 20, 1.0f, LIGHTGRAY);
std::string warnText = "Are you sure you want to delete '" + deletingWorldName + "'?";
Vector2 nameSize = MeasureTextEx(customFont, warnText.c_str(), 22, 1.0f);
DrawTextEx(customFont, warnText.c_str(), (Vector2){ confX + (confWidth/2) - (nameSize.x/2), confY + 85 }, 22, 1.0f, LIGHTGRAY);
// Yes Button
Rectangle yesBtn = { (float)confX + 50, (float)confY + 130, 120, 40 };
Rectangle yesBtn = { (float)confX + 80, (float)confY + 150, 140, 40 };
bool isYesHovered = CheckCollisionPointRec(mousePos, yesBtn);
DrawRectangleRec(yesBtn, isYesHovered ? RED : MAROON);
DrawRectangleLinesEx(yesBtn, 2.0f, isYesHovered ? WHITE : GRAY);
DrawTextEx(customFont, "YES", (Vector2){ yesBtn.x + 40, yesBtn.y + 10 }, 20, 1.0f, WHITE);
DrawTextEx(customFont, "YES, DELETE", (Vector2){ yesBtn.x + 10, yesBtn.y + 10 }, 20, 1.0f, WHITE);
// No Button
Rectangle noBtn = { (float)confX + confWidth - 170, (float)confY + 130, 120, 40 };
Rectangle noBtn = { (float)confX + confWidth - 220, (float)confY + 150, 140, 40 };
bool isNoHovered = CheckCollisionPointRec(mousePos, noBtn);
DrawRectangleRec(noBtn, isNoHovered ? (Color){ 100, 100, 100, 255 } : DARKGRAY);
DrawRectangleLinesEx(noBtn, 2.0f, isNoHovered ? WHITE : GRAY);
@ -2892,11 +2870,14 @@ int main(void)
std::vector<Chunk*> visibleChunks;
for (int cx = playerCX - RENDER_DISTANCE; cx <= playerCX + RENDER_DISTANCE; cx++) {
for (int cz = playerCZ - RENDER_DISTANCE; cz <= playerCZ + RENDER_DISTANCE; cz++) {
// FRUSTUM CULLING
Vector3 chunkCenter = { (float)(cx * CHUNK_SIZE + 8), 32.0f, (float)(cz * CHUNK_SIZE + 8) };
Vector3 toChunk = Vector3Normalize(Vector3Subtract(chunkCenter, camera3D.position));
if (Vector3Length(Vector3Subtract(chunkCenter, camera3D.position)) > 24.0f &&
Vector3DotProduct(toChunk, camForward) < -0.3f) continue;
// OPTIMIZED FRUSTUM CULLING (Bug #1 Fix: Don't cull the player's current chunk)
if (abs(cx - playerCX) > 1 || abs(cz - playerCZ) > 1) {
Vector3 chunkCenter = { (float)(cx * CHUNK_SIZE + 16), 64.0f, (float)(cz * CHUNK_SIZE + 16) };
float distSqr = Vector3LengthSqr(Vector3Subtract(chunkCenter, camera3D.position));
if (distSqr > 160.0f * 160.0f) continue;
Vector3 toChunk = Vector3Normalize(Vector3Subtract(chunkCenter, camera3D.position));
if (distSqr > 32.0f * 32.0f && Vector3DotProduct(toChunk, camForward) < -0.4f) continue;
}
auto it = worldChunks.find({cx, cz});
if (it == worldChunks.end()) continue;
@ -2911,20 +2892,20 @@ int main(void)
for (int renderType = 1; renderType <= 13; renderType++) {
if (renderType == GRASS) {
for (Chunk* chunk : visibleChunks) {
for (auto& pos : chunk->renderLists[GRASS]) {
DrawGrassBlock(pos, blockTextures[GRASS].id, grassTopTexture.id, blockTextures[DIRT].id, blockTint);
for (auto& data : chunk->renderLists[GRASS]) {
DrawGrassBlock((Vector3){data.x, data.y, data.z}, blockTextures[GRASS].id, grassTopTexture.id, blockTextures[DIRT].id, blockTint, data.faces);
}
}
} else if (renderType == LOG) {
for (Chunk* chunk : visibleChunks) {
for (auto& pos : chunk->renderLists[LOG]) {
DrawLog(pos, logSideTexture.id, logTopTexture.id, blockTint);
for (auto& data : chunk->renderLists[LOG]) {
DrawLog((Vector3){data.x, data.y, data.z}, logSideTexture.id, logTopTexture.id, blockTint, data.faces);
}
}
} else if (renderType == CRAFTING_TABLE) {
for (Chunk* chunk : visibleChunks) {
for (auto& pos : chunk->renderLists[CRAFTING_TABLE]) {
DrawCraftingTable(pos, craftingSideTexture.id, craftingTopTexture.id, blockTextures[DIRT].id, blockTint);
for (auto& data : chunk->renderLists[CRAFTING_TABLE]) {
DrawCraftingTable((Vector3){data.x, data.y, data.z}, craftingSideTexture.id, craftingTopTexture.id, blockTextures[DIRT].id, blockTint, data.faces);
}
}
} else {
@ -2936,8 +2917,8 @@ int main(void)
}
for (Chunk* chunk : visibleChunks) {
for (auto& pos : chunk->renderLists[renderType]) {
DrawCubeVertices(pos.x, pos.y, pos.z, 1.0f, 1.0f, 1.0f);
for (auto& data : chunk->renderLists[renderType]) {
DrawCubeVertices(data.x, data.y, data.z, 1.0f, 1.0f, 1.0f, data.faces);
}
}
rlEnd();
@ -2990,19 +2971,27 @@ int main(void)
handPos = Vector3Add(handPos, Vector3Scale(upVM, -0.25f - swingVal * 0.15f));
// Draw Arm (rotated cube)
// We use rlgl to rotate it to point roughly forward
rlPushMatrix();
rlTranslatef(handPos.x, handPos.y, handPos.z);
rlRotatef(camYaw * 180.0f/PI, 0, 1, 0); // Yaw with camera
rlRotatef(-20.0f - swingVal * 30.0f, 1, 0, 0); // Slight slant
DrawCube((Vector3){0,0,0}, 0.08f, 0.1f, 0.35f, (Color){220, 180, 150, 255});
rlRotatef(camYaw * 180.0f/PI, 0, 1, 0);
rlRotatef(camPitch * 180.0f/PI - 15.0f - swingVal * 20.0f, 1, 0, 0); // Point forward + swing
DrawCube((Vector3){0,0,0}, 0.1f, 0.12f, 0.4f, (Color){220, 180, 150, 255});
rlPopMatrix();
// Draw Held Item
// Draw Held Item - Fixed pivot and attachment (Bug #3)
int heldBT = hotbar[activeHotbarSlot].blockType;
if (heldBT != AIR) {
Vector3 itemPos = Vector3Add(handPos, Vector3Scale(forwardVM, 0.12f));
itemPos = Vector3Add(itemPos, Vector3Scale(upVM, 0.12f));
// Calculate position at the end of the arm
// The arm is rotated by camPitch - 15.0f - swingVal * 20.0f
float armPitch = (camPitch * 180.0f/PI - 15.0f - swingVal * 20.0f) * PI/180.0f;
Vector3 armForward = {
forwardVM.x * cosf(-armPitch) - forwardVM.y * sinf(-armPitch),
forwardVM.x * sinf(-armPitch) + forwardVM.y * cosf(-armPitch),
forwardVM.z
}; // This is a simplification, but let's use the handPos + offset
Vector3 itemPos = Vector3Add(handPos, Vector3Scale(forwardVM, 0.25f));
itemPos = Vector3Add(itemPos, Vector3Scale(upVM, 0.05f));
itemPos = Vector3Add(itemPos, Vector3Scale(rightVM, -0.05f));
Texture2D itemTex = (heldBT == GRASS) ? grassTopTexture : (heldBT == CRAFTING_TABLE ? craftingSideTexture : blockTextures[heldBT]);
@ -3014,16 +3003,18 @@ int main(void)
rlTranslatef(itemPos.x, itemPos.y, itemPos.z);
rlRotatef(camYaw * 180.0f/PI + 45.0f, 0, 1, 0);
rlRotatef(20.0f, 1, 0, 1);
DrawCubeVertices(0,0,0, 0.12f, 0.12f, 0.12f);
DrawCubeVertices(0,0,0, 0.12f, 0.12f, 0.12f, 63);
rlPopMatrix();
rlSetTexture(0);
} else {
// Item (Stick/Axe) as Billboard - Scaled larger for better visibility
DrawBillboard(camera3D, itemTex, itemPos, 0.35f, WHITE);
// Item (Stick/Axe) as Billboard
Rectangle src = { 0, 0, (float)itemTex.width, (float)itemTex.height };
Vector2 itemSize = { 0.45f, 0.45f };
// Pivot at bottom-left corner (Bug #3 fix)
// In DrawBillboardPro, origin (0,0) is center. Bottom-left is (-w/2, -h/2).
Vector2 pivot = { -itemSize.x/2.0f, -itemSize.y/2.0f };
DrawBillboardPro(camera3D, itemTex, src, itemPos, camera3D.up, itemSize, pivot, -45.0f + swingVal * 40.0f, WHITE);
}
} else {
// Fallback
DrawCube(itemPos, 0.1f, 0.1f, 0.1f, RED);
}
}
rlPopMatrix();
@ -3173,7 +3164,30 @@ int main(void)
}
if (hov && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
if (IsKeyDown(KEY_LEFT_SHIFT) && !isResultSlot && slot.blockType != AIR) {
if (IsKeyDown(KEY_LEFT_SHIFT) && isResultSlot && slot.blockType != AIR) {
// Take everything possible from the result box
while (slot.blockType != AIR) {
GiveItems(slot.blockType, slot.count);
// Decrease ingredients
if (isTableResult) {
for(int i=0; i<9; i++) {
if (tableSlots[i].count > 0) tableSlots[i].count--;
if (tableSlots[i].count == 0) tableSlots[i].blockType = AIR;
}
UpdateTableCrafting();
slot = tableResult; // Refresh from local reference
} else {
for(int i=0; i<4; i++) {
if (craftingSlots[i].count > 0) craftingSlots[i].count--;
if (craftingSlots[i].count == 0) craftingSlots[i].blockType = AIR;
}
UpdateCrafting();
slot = craftingResult;
}
// Safety: if we can't craft anymore, break
if (slot.blockType == AIR) break;
}
} else if (IsKeyDown(KEY_LEFT_SHIFT) && !isResultSlot && slot.blockType != AIR) {
// Pick up entire stack into hand immediately
if (mouseHeldItem.blockType == AIR) {
mouseHeldItem = slot;
@ -3313,7 +3327,7 @@ int main(void)
// Close hint
DrawTextEx(customFont, "Press E or ESC to close", (Vector2){ (float)(invPanelX + 12), (float)(invPanelY + invPanelH - 22) }, 14, 1.0f, (Color){160, 160, 160, 200});
if (IsKeyPressed(KEY_ESCAPE) || IsKeyPressed(KEY_E)) {
if (IsKeyPressed(KEY_ESCAPE)) {
inventoryOpen = false;
DisableCursor();
}
@ -3343,7 +3357,7 @@ int main(void)
drawInvGrid(guiX + 16, tableStartY + 3*(invSlotSize+invGap) + 40);
if (IsKeyPressed(KEY_ESCAPE) || IsKeyPressed(KEY_E)) {
if (IsKeyPressed(KEY_ESCAPE)) {
currentState = GAMEPLAY;
DisableCursor();
}

View File

@ -1 +1 @@
v2.2.7
v2.2.9