diff --git a/MorriCraft-Windows.zip b/MorriCraft-Windows.zip index 65a2758..562ea05 100644 Binary files a/MorriCraft-Windows.zip and b/MorriCraft-Windows.zip differ diff --git a/assets/apple.png b/assets/apple.png new file mode 100644 index 0000000..65959a0 Binary files /dev/null and b/assets/apple.png differ diff --git a/assets/grass_top.png b/assets/grass_top.png index c6a1fa0..f819d51 100644 Binary files a/assets/grass_top.png and b/assets/grass_top.png differ diff --git a/build-linux/MorriCraft b/build-linux/MorriCraft index 66c2150..894bb8a 100755 Binary files a/build-linux/MorriCraft and b/build-linux/MorriCraft differ diff --git a/build-linux/assets/apple.png b/build-linux/assets/apple.png new file mode 100644 index 0000000..65959a0 Binary files /dev/null and b/build-linux/assets/apple.png differ diff --git a/build-linux/assets/grass_top.png b/build-linux/assets/grass_top.png index c6a1fa0..f819d51 100644 Binary files a/build-linux/assets/grass_top.png and b/build-linux/assets/grass_top.png differ diff --git a/build-windows/MorriCraft.exe b/build-windows/MorriCraft.exe index 20664e2..9a899bf 100755 Binary files a/build-windows/MorriCraft.exe and b/build-windows/MorriCraft.exe differ diff --git a/build-windows/assets/apple.png b/build-windows/assets/apple.png new file mode 100644 index 0000000..65959a0 Binary files /dev/null and b/build-windows/assets/apple.png differ diff --git a/build-windows/assets/grass_top.png b/build-windows/assets/grass_top.png index c6a1fa0..f819d51 100644 Binary files a/build-windows/assets/grass_top.png and b/build-windows/assets/grass_top.png differ diff --git a/release/MorriCraft-Linux.zip b/release/MorriCraft-Linux.zip index d47dc72..d3ec65c 100644 Binary files a/release/MorriCraft-Linux.zip and b/release/MorriCraft-Linux.zip differ diff --git a/release/MorriCraft-Windows.zip b/release/MorriCraft-Windows.zip index 5fb497d..e8e5d14 100644 Binary files a/release/MorriCraft-Windows.zip and b/release/MorriCraft-Windows.zip differ diff --git a/src/main.cpp b/src/main.cpp index b7dc7be..adc9a75 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,7 +31,8 @@ enum BlockType { STONE_AXE = 20, STONE_PICKAXE = 21, STONE_SWORD = 22, STONE_SHOVEL = 23, STONE_HOE = 24, FURNACE = 25, CHEST = 26, LADDER = 27, FENCE = 28, TORCH = 29, DOOR = 30, STONE_SLAB = 31, IRON_AXE = 32, IRON_PICKAXE = 33, IRON_SWORD = 34, IRON_SHOVEL = 35, IRON_HOE = 36, - DIAMOND_AXE = 37, DIAMOND_PICKAXE = 38, DIAMOND_SWORD = 39, DIAMOND_SHOVEL = 40, DIAMOND_HOE = 41 + DIAMOND_AXE = 37, DIAMOND_PICKAXE = 38, DIAMOND_SWORD = 39, DIAMOND_SHOVEL = 40, DIAMOND_HOE = 41, + APPLE = 42 }; std::string GetBlockName(int type) { @@ -78,6 +79,7 @@ std::string GetBlockName(int type) { case DIAMOND_SWORD: return "Diamond Sword"; case DIAMOND_SHOVEL: return "Diamond Shovel"; case DIAMOND_HOE: return "Diamond Hoe"; + case APPLE: return "Apple"; default: return "Unknown"; } } @@ -170,6 +172,10 @@ static float masterSoundVolume = 1.0f; static Color myShirtColor = BLUE; static Color myPantsColor = DARKBLUE; static float playerHealth = 16.0f; +static float playerHunger = 20.0f; +static float hungerTimer = 0.0f; +static float healthRegenTimer = 0.0f; +static float healthStarveTimer = 0.0f; static uint32_t localPlayerID = 0; static Sound hitSound; @@ -1075,7 +1081,7 @@ int main(void) float updateTimer = 0.0f; float downloadProgress = 0.0f; std::string latestVersion = ""; - std::string localVersion = "v2.2.25"; // Default fallback + std::string localVersion = "v2.2.26"; // Default fallback // Read local version std::ifstream vfile("assets/version.txt"); @@ -1163,6 +1169,7 @@ int main(void) blockTextures[CRAFTING_TABLE] = craftingSideTexture; // Preview for inventory blockTextures[STICK] = LoadTexture("assets/stick.png"); + blockTextures[APPLE] = LoadTexture("assets/apple.png"); blockTextures[WOOD_AXE] = LoadTexture("assets/wooden_axe.png"); blockTextures[WOOD_PICKAXE] = LoadTexture("assets/wooden_pickaxe.png"); blockTextures[WOOD_SWORD] = LoadTexture("assets/wooden_sword.png"); @@ -1683,7 +1690,37 @@ int main(void) // --- GLOBAL TIME & AUDIO MANAGEMENT --- float cycleLength = 600.0f; // Slower day cycle (v2.2.23: cut speed in half) - if (currentState == GAMEPLAY) gameTime += GetFrameTime(); + if (currentState == GAMEPLAY) { + gameTime += GetFrameTime(); + + // Hunger Depletion + hungerTimer += GetFrameTime(); + if (hungerTimer >= 10.0f) { // Deplete every 10 seconds + hungerTimer = 0; + if (playerHunger > 0) playerHunger -= 0.5f; + } + + // Hunger Effects (Health Regen/Starve) + if (playerHunger >= 20.0f) { + healthRegenTimer += GetFrameTime(); + if (healthRegenTimer >= 3.0f) { + healthRegenTimer = 0; + if (playerHealth < 16.0f) playerHealth += 1.0f; // Half heart + } + } else { + healthRegenTimer = 0; + } + + if (playerHunger <= 0) { + healthStarveTimer += GetFrameTime(); + if (healthStarveTimer >= 3.0f) { + healthStarveTimer = 0; + if (playerHealth > 2.0f) playerHealth -= 1.0f; // Half heart until 1 heart left + } + } else { + healthStarveTimer = 0; + } + } float timeOfDay = fmodf(gameTime, cycleLength) / cycleLength; float sunAngle = timeOfDay * 2.0f * 3.14159f - 3.14159f/2.0f; dayFactor = (sinf(sunAngle) + 1.0f) / 2.0f; @@ -2060,41 +2097,48 @@ int main(void) } if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { - int targetBlock = GetBlock(hitX, hitY, hitZ); - if (targetBlock == CRAFTING_TABLE) { - currentState = CRAFTING_GUI; - EnableCursor(); - } else if (targetBlock == CHEST) { - activeChestPos = (Vector3){ (float)hitX, (float)hitY, (float)hitZ }; - uint64_t key = GetPosKey(hitX, hitY, hitZ); - if (chestInventories.find(key) == chestInventories.end()) { - chestInventories[key] = std::vector(27, InventorySlot(AIR, 0)); + // NEW: Eating Logic + if (hotbar[activeHotbarSlot].blockType == APPLE && playerHunger < 20.0f) { + playerHunger += 4.0f; + if (playerHunger > 20.0f) playerHunger = 20.0f; + hotbar[activeHotbarSlot].count--; + if (hotbar[activeHotbarSlot].count <= 0) hotbar[activeHotbarSlot].blockType = AIR; + PlaySound(digGrass); + } else if (hitBlock) { + int targetBlock = GetBlock(hitX, hitY, hitZ); + if (targetBlock == CRAFTING_TABLE) { + currentState = CRAFTING_GUI; + EnableCursor(); + } else if (targetBlock == CHEST) { + activeChestPos = (Vector3){ (float)hitX, (float)hitY, (float)hitZ }; + uint64_t key = GetPosKey(hitX, hitY, hitZ); + if (chestInventories.find(key) == chestInventories.end()) { + chestInventories[key] = std::vector(27, InventorySlot(AIR, 0)); + } + currentState = CHEST_GUI; + PlaySound(chestOpenSound); + EnableCursor(); + } else if (hotbar[activeHotbarSlot].count > 0) { + int placeX = hitX + (int)closestNormal.x; + int placeY = hitY + (int)closestNormal.y; + int placeZ = hitZ + (int)closestNormal.z; + + BoundingBox playerBB = { + (Vector3){camera3D.position.x - 0.3f, camera3D.position.y - 1.5f, camera3D.position.z - 0.3f}, + (Vector3){camera3D.position.x + 0.3f, camera3D.position.y + 0.3f, camera3D.position.z + 0.3f} + }; + BoundingBox blockBB = { + (Vector3){(float)placeX - 0.5f, (float)placeY - 0.5f, (float)placeZ - 0.5f}, + (Vector3){(float)placeX + 0.5f, (float)placeY + 0.5f, (float)placeZ + 0.5f} + }; + + if (!CheckCollisionBoxes(playerBB, blockBB) || hotbar[activeHotbarSlot].blockType == TORCH) { + NetSetBlock(placeX, placeY, placeZ, hotbar[activeHotbarSlot].blockType); + hotbar[activeHotbarSlot].count--; + if (hotbar[activeHotbarSlot].count <= 0) hotbar[activeHotbarSlot].blockType = AIR; + } + isSwinging = true; } - currentState = CHEST_GUI; - PlaySound(chestOpenSound); - EnableCursor(); - } else if (hotbar[activeHotbarSlot].count > 0) { - int placeX = hitX + (int)roundf(closestNormal.x); - int placeY = hitY + (int)roundf(closestNormal.y); - int placeZ = hitZ + (int)roundf(closestNormal.z); - - // Check if space is occupied by player - BoundingBox playerBB = { - (Vector3){camera3D.position.x - 0.3f, camera3D.position.y - 1.5f, camera3D.position.z - 0.3f}, - (Vector3){camera3D.position.x + 0.3f, camera3D.position.y + 0.3f, camera3D.position.z + 0.3f} - }; - BoundingBox blockBB = { - (Vector3){(float)placeX - 0.5f, (float)placeY - 0.5f, (float)placeZ - 0.5f}, - (Vector3){(float)placeX + 0.5f, (float)placeY + 0.5f, (float)placeZ + 0.5f} - }; - - // Torches and some other blocks don't collide, but for now we just check occupancy - if (!CheckCollisionBoxes(playerBB, blockBB) || hotbar[activeHotbarSlot].blockType == TORCH) { - NetSetBlock(placeX, placeY, placeZ, hotbar[activeHotbarSlot].blockType); - hotbar[activeHotbarSlot].count--; - if (hotbar[activeHotbarSlot].count <= 0) hotbar[activeHotbarSlot].blockType = AIR; - } - isSwinging = true; // Swing when placing } } } @@ -2493,10 +2537,9 @@ int main(void) if (chunksGeneratedCount >= totalChunksToPreGen) { // FINALIZE: Place player and save world data if (isNewWorldGeneration) { - Vector2 idealSpawn = FindIdealSpawn(); - float spawnY = FindSpawnY((int)idealSpawn.x, (int)idealSpawn.y); - camera3D.position = (Vector3){ idealSpawn.x, spawnY, idealSpawn.y }; - camera3D.target = (Vector3){ idealSpawn.x, spawnY, idealSpawn.y + 1.0f }; + float spawnY = FindSpawnY((int)camera3D.position.x, (int)camera3D.position.z); + camera3D.position.y = spawnY; + camera3D.target = (Vector3){ camera3D.position.x, spawnY, camera3D.position.z + 1.0f }; std::ofstream worldFile2("saves/" + currentWorldName + "/world.dat"); if (worldFile2.is_open()) { @@ -2519,8 +2562,7 @@ int main(void) invf.close(); } } - playerVelocityY = 0.0f; - camYaw = 0.0f; camPitch = 0.0f; + currentState = GAMEPLAY; DisableCursor(); } @@ -3071,7 +3113,14 @@ int main(void) isNewWorldGeneration = true; chunksGeneratedCount = 0; worldGenProgress = 0.0f; - spawnSavedX = 0; spawnSavedY = -1; spawnSavedZ = 0; // Fresh world start at origin + + // NEW: Find the ideal spawn BEFORE generating chunks to fix "missing terrain" bug + Vector2 idealSpawn = FindIdealSpawn(); + spawnSavedX = idealSpawn.x; + spawnSavedZ = idealSpawn.y; + spawnSavedY = -1; + camera3D.position = (Vector3){ idealSpawn.x, 60.0f, idealSpawn.y }; + currentState = WORLD_CREATION_PROGRESS; } } @@ -3399,6 +3448,22 @@ int main(void) } } + // --- Hunger Bar --- + int hungerX = currentWidth / 2 + 5; + int hungerY = hotbarY - 35; + for (int i = 0; i < 10; i++) { + Rectangle hungerRect = { (float)hungerX + i * 22, (float)hungerY, 18, 18 }; + float h = playerHunger - (i * 2); + Color hungerColor = { 180, 100, 40, 255 }; // Brownish orange + if (h >= 2.0f) DrawRectangleRec(hungerRect, hungerColor); + else if (h >= 1.0f) { + DrawRectangle((int)hungerRect.x, (int)hungerRect.y, 9, 18, hungerColor); + DrawRectangleLinesEx(hungerRect, 2.0f, DARKGRAY); + } else { + DrawRectangleLinesEx(hungerRect, 2.0f, (Color){ 60, 60, 60, 255 }); + } + } + // Hotbar background panel DrawRectangle(hotbarX - 6, hotbarY - 6, hotbarW + 12, slotSize + 12, (Color){ 30, 30, 30, 180 });