diff --git a/build-linux/MorriCraft b/build-linux/MorriCraft index 5dff29a..5957487 100755 Binary files a/build-linux/MorriCraft and b/build-linux/MorriCraft differ diff --git a/build-windows/MorriCraft.exe b/build-windows/MorriCraft.exe index 5137445..ca112a2 100755 Binary files a/build-windows/MorriCraft.exe and b/build-windows/MorriCraft.exe differ diff --git a/src/main.cpp b/src/main.cpp index 76dd953..45ae1cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -419,7 +419,7 @@ 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) { +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; @@ -427,7 +427,7 @@ void DrawGrassBlock(Vector3 position, unsigned int sideTexId, unsigned int topTe // ---- SIDES (4 faces) ---- rlSetTexture(sideTexId); rlBegin(RL_QUADS); - rlColor4ub(255, 255, 255, 255); + 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); @@ -453,7 +453,10 @@ void DrawGrassBlock(Vector3 position, unsigned int sideTexId, unsigned int topTe // ---- TOP (pure green) ---- rlSetTexture(topTexId); rlBegin(RL_QUADS); - rlColor4ub(124, 189, 107, 255); // Minecraft grass green tint + // 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); @@ -461,7 +464,7 @@ void DrawGrassBlock(Vector3 position, unsigned int sideTexId, unsigned int topTe // ---- BOTTOM (pure dirt) ---- rlSetTexture(botTexId); rlBegin(RL_QUADS); - rlColor4ub(255, 255, 255, 255); + 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); @@ -470,13 +473,13 @@ void DrawGrassBlock(Vector3 position, unsigned int sideTexId, unsigned int topTe } // Draws a crafting table with custom top and sides -void DrawCraftingTable(Vector3 position, unsigned int sideTexId, unsigned int topTexId, unsigned int botTexId) { +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(255, 255, 255, 255); + 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); @@ -487,6 +490,7 @@ void DrawCraftingTable(Vector3 position, unsigned int sideTexId, unsigned int to // 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); @@ -496,6 +500,7 @@ void DrawCraftingTable(Vector3 position, unsigned int sideTexId, unsigned int to // 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); @@ -617,6 +622,7 @@ int main(void) MenuState optionsReturnState = MAIN_MENU; InventorySlot mouseHeldItem(AIR, 0); + float gameTime = 0.0f; // Total game time for day/night cycle float targetZoom = 1.1f; float currentZoom = 1.1f; @@ -666,7 +672,10 @@ int main(void) Texture2D grassTopTexture = LoadTexture("assets/grass_top.png"); Texture2D craftingSideTexture = LoadTexture("assets/crafting_table_side.png"); Texture2D craftingTopTexture = LoadTexture("assets/crafting_table_top.png"); - blockTextures[CRAFTING_TABLE] = craftingSideTexture; // Default preview + + // Safety Fallback: Use Planks if custom textures failed to load (prevents crash) + if (craftingSideTexture.id == 0) craftingSideTexture = blockTextures[PLANK]; + if (craftingTopTexture.id == 0) craftingTopTexture = blockTextures[PLANK]; // Inventory Crafting State InventorySlot craftingSlots[4]; // Default to AIR/0 @@ -740,6 +749,8 @@ int main(void) // Gameplay Update if (currentState == GAMEPLAY) { + gameTime += GetFrameTime(); + // Hotbar slot selection (keys 1-9) if (IsKeyPressed(KEY_ONE)) activeHotbarSlot = 0; if (IsKeyPressed(KEY_TWO)) activeHotbarSlot = 1; @@ -975,8 +986,8 @@ int main(void) DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE); EndMode2D(); - // Show Version Number (v1.4.2) in Red - DrawTextEx(customFont, "v1.4.2", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED); + // Show Version Number (v1.5.1) in Red + DrawTextEx(customFont, "v1.5.1", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED); } Vector2 mousePos = GetMousePosition(); @@ -1407,10 +1418,33 @@ int main(void) // Draw Gameplay overlay if we entered gameplay if (currentState == GAMEPLAY || currentState == PAUSE_MENU) { - // Clear the background to a sky color - ClearBackground((Color){ 135, 206, 235, 255 }); // Sky Blue + // ---- Day / Night Cycle Calculations ---- + float cycleLength = 300.0f; // 5 minutes + float timeOfDay = fmodf(gameTime, cycleLength) / cycleLength; + float sunAngle = timeOfDay * 2.0f * PI - PI/2.0f; // Offset so it starts at sunrise + + float dayFactor = (sinf(sunAngle) + 1.0f) / 2.0f; // 0 (night) to 1 (day) + float lightLevel = 0.2f + 0.8f * dayFactor; // Ambient light multiplier + Color blockTint = { (unsigned char)(255 * lightLevel), (unsigned char)(255 * lightLevel), (unsigned char)(255 * lightLevel), 255 }; + + Color skyBlue = { 135, 206, 235, 255 }; + Color nightBlue = { 10, 10, 25, 255 }; + Color currentSky = { + (unsigned char)(nightBlue.r + dayFactor * (skyBlue.r - nightBlue.r)), + (unsigned char)(nightBlue.g + dayFactor * (skyBlue.g - nightBlue.g)), + (unsigned char)(nightBlue.b + dayFactor * (skyBlue.b - nightBlue.b)), + 255 + }; + + ClearBackground(currentSky); BeginMode3D(camera3D); + // Draw Sun and Moon (simple billboards or spheres far away) + Vector3 sunPos = { camera3D.position.x + cosf(sunAngle) * 100, camera3D.position.y + sinf(sunAngle) * 100, camera3D.position.z }; + Vector3 moonPos = { camera3D.position.x + cosf(sunAngle + PI) * 100, camera3D.position.y + sinf(sunAngle + PI) * 100, camera3D.position.z }; + + DrawSphere(sunPos, 5.0f, YELLOW); + DrawSphere(moonPos, 4.0f, LIGHTGRAY); int playerCX = (int)floorf(camera3D.position.x / CHUNK_SIZE); int playerCZ = (int)floorf(camera3D.position.z / CHUNK_SIZE); @@ -1439,9 +1473,11 @@ int main(void) for (int ly = 0; ly <= chunk->maxY; ly++) { for (int lz = 0; lz < CHUNK_SIZE; lz++) { if (chunk->blocks[lx][ly][lz] != GRASS) continue; - if (!IsExposedOptimized(lx, ly, lz, chunk, nxM, nxP, nzM, nzP)) continue; - DrawGrassBlock((Vector3){(float)(worldX+lx), (float)ly, (float)(worldZ+lz)}, - blockTextures[GRASS].id, grassTopTexture.id, blockTextures[DIRT].id); + + if (IsExposedOptimized(lx, ly, lz, chunk, nxM, nxP, nzM, nzP)) { + DrawGrassBlock((Vector3){(float)(worldX+lx), (float)ly, (float)(worldZ+lz)}, + blockTextures[GRASS].id, grassTopTexture.id, blockTextures[DIRT].id, blockTint); + } } } } @@ -1471,7 +1507,7 @@ int main(void) if (chunk->blocks[lx][ly][lz] != CRAFTING_TABLE) continue; if (!IsExposedOptimized(lx, ly, lz, chunk, nxM, nxP, nzM, nzP)) continue; DrawCraftingTable((Vector3){(float)(worldX+lx), (float)ly, (float)(worldZ+lz)}, - craftingSideTexture.id, craftingTopTexture.id, blockTextures[DIRT].id); + craftingSideTexture.id, craftingTopTexture.id, blockTextures[DIRT].id, blockTint); } } } @@ -1481,8 +1517,11 @@ int main(void) unsigned int currentTexId = blockTextures[renderType].id; rlSetTexture(currentTexId); rlBegin(RL_QUADS); - rlColor4ub(255, 255, 255, 255); - if (renderType == LEAVES) rlColor4ub(40, 140, 40, 255); + rlColor4ub(blockTint.r, blockTint.g, blockTint.b, 255); + if (renderType == LEAVES) { + // Combined green and day/night tint + rlColor4ub((unsigned char)(blockTint.r * 0.2f), (unsigned char)(blockTint.g * 0.6f), (unsigned char)(blockTint.b * 0.2f), 255); + } for (int cx = playerCX - RENDER_DISTANCE; cx <= playerCX + RENDER_DISTANCE; cx++) { for (int cz = playerCZ - RENDER_DISTANCE; cz <= playerCZ + RENDER_DISTANCE; cz++) { @@ -1535,6 +1574,18 @@ int main(void) DrawTextEx(customFont, TextFormat("XYZ: %.1f / %.1f / %.1f", camera3D.position.x, camera3D.position.y - 1.6f, camera3D.position.z), (Vector2){ 10, 80 }, 16, 1.0f, DARKGRAY); + + // --- In-Game Clock --- + float clockTime = fmodf(timeOfDay + 0.25f, 1.0f); // Offset so 0.5 is noon + int totalMinutes = (int)(clockTime * 24 * 60); + int hours = (totalMinutes / 60) % 24; + int minutes = totalMinutes % 60; + const char* ampm = (hours >= 12) ? "PM" : "AM"; + int displayHours = hours % 12; + if (displayHours == 0) displayHours = 12; + + DrawTextEx(customFont, TextFormat("Time: %i:%02i %s", displayHours, minutes, ampm), (Vector2){ 10, 110 }, 18, 1.0f, (dayFactor > 0.5f) ? BLACK : WHITE); + DrawTextEx(customFont, (dayFactor > 0.5f) ? "Status: Day" : "Status: Night", (Vector2){ 10, 130 }, 16, 1.0f, (dayFactor > 0.5f) ? (Color){180, 150, 0, 255} : (Color){100, 100, 255, 255}); // ---- HOTBAR ---- const int slotSize = 50; @@ -1606,7 +1657,7 @@ int main(void) if (slot.count > 0 && slot.blockType != AIR) { int bt = slot.blockType; Texture2D tex = (bt == GRASS) ? grassTopTexture : (bt == CRAFTING_TABLE ? craftingSideTexture : blockTextures[bt]); - if (tex.id > 0) { + if (tex.id > 0 && tex.width > 0 && tex.height > 0) { Rectangle src = { 0,0,(float)tex.width,(float)tex.height }; Rectangle dst = { (float)(px+5),(float)(py+5), (float)(invSlotSize-10),(float)(invSlotSize-10) }; Color tint = (bt == GRASS) ? (Color){124,189,107,255} : WHITE; @@ -1645,6 +1696,27 @@ int main(void) UpdateTableCrafting(); } } + + if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { + if (!isResultSlot) { + if (mouseHeldItem.blockType == AIR) { + // Pick up half? Not requested, but common. + // For now just following user request: "holding a stack... right click into a box, place one" + } else { + // Holding something, place one + if (slot.blockType == AIR) { + slot = InventorySlot(mouseHeldItem.blockType, 1); + mouseHeldItem.count--; + } else if (slot.blockType == mouseHeldItem.blockType && slot.count < 64) { + slot.count++; + mouseHeldItem.count--; + } + if (mouseHeldItem.count <= 0) mouseHeldItem = InventorySlot(AIR, 0); + UpdateCrafting(); + UpdateTableCrafting(); + } + } + } }; auto drawInvGrid = [&](int startX, int startY) {