diff --git a/README.md b/README.md index 8a79620..bdd33f1 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ A pre-built `MorriCraft-Windows.zip` is available in the repository root. ## 📜 Version History -### v2.3.0 - Major Update: Hunger System & Accurate Updater (Current) +### v2.3.1 - Major Update: Hunger System & Accurate Updater (Current) - **Hunger System**: Added player hunger bar, starvation mechanics, and health regeneration when full. - **Apple Item**: Added edible Apple item to restore hunger. - **Grass Texture**: Improved `grass_top` texture with better pixel art. diff --git a/assets/version.txt b/assets/version.txt index b1d18bc..aaf7425 100644 --- a/assets/version.txt +++ b/assets/version.txt @@ -1 +1 @@ -v2.3.0 +v2.3.1 diff --git a/build-linux/MorriCraft b/build-linux/MorriCraft index 78c1d8e..4bc7f7e 100755 Binary files a/build-linux/MorriCraft and b/build-linux/MorriCraft differ diff --git a/build-linux/assets/version.txt b/build-linux/assets/version.txt index b1d18bc..aaf7425 100644 --- a/build-linux/assets/version.txt +++ b/build-linux/assets/version.txt @@ -1 +1 @@ -v2.3.0 +v2.3.1 diff --git a/build-windows/MorriCraft.exe b/build-windows/MorriCraft.exe index 380c8c3..7b380b5 100755 Binary files a/build-windows/MorriCraft.exe and b/build-windows/MorriCraft.exe differ diff --git a/build-windows/assets/version.txt b/build-windows/assets/version.txt index b1d18bc..aaf7425 100644 --- a/build-windows/assets/version.txt +++ b/build-windows/assets/version.txt @@ -1 +1 @@ -v2.3.0 +v2.3.1 diff --git a/release/MorriCraft-Linux.zip b/release/MorriCraft-Linux.zip index a917974..8181ada 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 6be9b00..b92987e 100644 Binary files a/release/MorriCraft-Windows.zip and b/release/MorriCraft-Windows.zip differ diff --git a/release/version.txt b/release/version.txt index b1d18bc..aaf7425 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -v2.3.0 +v2.3.1 diff --git a/src/main.cpp b/src/main.cpp index 1767487..2d5a38a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -150,6 +150,14 @@ struct BlockRenderData { unsigned char faces; // 1:front, 2:back, 4:top, 8:bottom, 16:right, 32:left }; +struct DroppedItem { + Vector3 pos; + int type; + bool active; +}; + +static std::vector droppedItems; + struct Chunk { int blocks[CHUNK_SIZE][CHUNK_HEIGHT][CHUNK_SIZE]; int maxY = 0; @@ -386,7 +394,7 @@ void RebuildChunkRenderList(Chunk* chunk, int cx, int cz) { unsigned char faces = GetExposedFaces(lx, ly, lz, chunk, nxM, nxP, nzM, nzP); if (faces != 0) { - // Simple Torch Lighting (v2.3.0) + // Simple Torch Lighting (v2.3.1) float blockLight = 0.0f; Vector3 bPos = {(float)(worldX+lx), (float)ly, (float)(worldZ+lz)}; for (const auto& tp : torchPositions) { @@ -696,7 +704,7 @@ float FindSpawnY(int spawnX, int spawnZ) { // Feet land at top of block (y + 0.5). // We use +0.6f + 1.6f to ensure we start slightly ABOVE the block // to avoid getting stuck in collision on frame 1. - // v2.3.0: Spawn higher (feet at y + 1.5, eyes at y + 3.1) to avoid ground-clipping + // v2.3.1: Spawn higher (feet at y + 1.5, eyes at y + 3.1) to avoid ground-clipping return (float)y + 1.5f + 1.6f; } } @@ -737,7 +745,7 @@ void GenerateChunk(int cx, int cz) { // Combine: broad hills (±12) + local detail (±4) = up to ±16 around Y=32 int height = 32 + (int)(continentNoise * 12.0f) + (int)(detailNoise * 4.0f); - // Spawn Plateau (v2.3.0): Smoothly flatten area around (0,0) + // Spawn Plateau (v2.3.1): Smoothly flatten area around (0,0) float distToSpawn = sqrtf(worldX * worldX + worldZ * worldZ); if (distToSpawn < 12.0f) { float plateauStrength = 1.0f - (distToSpawn / 12.0f); @@ -749,7 +757,7 @@ void GenerateChunk(int cx, int cz) { if (height < 10) height = 10; if (height >= CHUNK_HEIGHT - 2) height = CHUNK_HEIGHT - 2; - // Biome noise: determines surface type (v2.3.0: 4x larger biomes) + // Biome noise: determines surface type (v2.3.1: 4x larger biomes) float biomeNoise = fbm(worldX * 0.002f, worldZ * 0.002f, globalSeedHash + 77777); for (int y = 0; y <= height; y++) { @@ -1034,7 +1042,7 @@ int main(void) // By default, windows have minimize, maximize, and close buttons on the top bar. SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT); - InitWindow(screenWidth, screenHeight, "MorriCraft v2.3.0"); + InitWindow(screenWidth, screenHeight, "MorriCraft v2.3.1"); LoadConfig(); SetExitKey(KEY_NULL); // Prevent ESC from closing the window @@ -1104,7 +1112,7 @@ int main(void) float updateTimer = 0.0f; float downloadProgress = 0.0f; std::string latestVersion = ""; - std::string localVersion = "v2.3.0"; // Default fallback + std::string localVersion = "v2.3.1"; // Default fallback // Read local version std::ifstream vfile("assets/version.txt"); @@ -1716,7 +1724,7 @@ int main(void) // --- GLOBAL TIME & AUDIO MANAGEMENT --- - float cycleLength = 600.0f; // Slower day cycle (v2.3.0: cut speed in half) + float cycleLength = 600.0f; // Slower day cycle (v2.3.1: cut speed in half) if (currentState == GAMEPLAY) { gameTime += GetFrameTime(); @@ -1747,6 +1755,17 @@ int main(void) } else { healthStarveTimer = 0; } + + // Item Pickup Logic + for (auto& item : droppedItems) { + if (item.active) { + float dist = Vector3Distance(camera3D.position, item.pos); + if (dist < 1.5f) { + item.active = false; + AddToInventory(item.type); + } + } + } } float timeOfDay = fmodf(gameTime, cycleLength) / cycleLength; float sunAngle = timeOfDay * 2.0f * 3.14159f - 3.14159f/2.0f; @@ -2076,13 +2095,13 @@ int main(void) if (targetBlock != AIR && targetBlock != BEDROCK) { float breakSpeed = 2.0f; // Seconds to break - // Axe logic: Wood/Plank/Logs are faster - if (targetBlock == LOG || targetBlock == PLANK || targetBlock == CRAFTING_TABLE) { - if (hotbar[activeHotbarSlot].blockType == WOOD_AXE) breakSpeed = 0.5f; // Fast - else breakSpeed = 1.5f; // Normal - } else if (targetBlock == LEAVES) { - breakSpeed = 0.2f; // Very fast - } + if (targetBlock == LOG || targetBlock == PLANK || targetBlock == CRAFTING_TABLE) { + if (hotbar[activeHotbarSlot].blockType == WOOD_AXE) breakSpeed = 0.5f; // Fast + else breakSpeed = 1.5f; // Normal + } else if (targetBlock == LEAVES) { + if (hotbar[activeHotbarSlot].blockType == WOOD_SWORD) breakSpeed = 1.4f; // 4 hits (0.35s per swing) + else breakSpeed = 2.1f; // 6 hits + } breakProgress += GetFrameTime(); @@ -2099,10 +2118,17 @@ int main(void) } if (breakProgress >= breakSpeed) { - AddToInventory(targetBlock); + if (targetBlock == LEAVES) { + // 1 in 20 chance for an apple + if (GetRandomValue(0, 19) == 0) { + droppedItems.push_back({(Vector3){(float)hitX, (float)hitY, (float)hitZ}, APPLE, true}); + } + } else { + AddToInventory(targetBlock); + } NetSetBlock(hitX, hitY, hitZ, AIR); - // Tool Durability Logic (v2.3.0) + // Tool Durability Logic (v2.3.1) InventorySlot* slot = &hotbar[activeHotbarSlot]; if (slot->maxDurability > 0) { slot->durability--; @@ -2591,6 +2617,7 @@ int main(void) } for (int i = 0; i < 9; i++) hotbar[i] = InventorySlot(AIR, 0); for (int i = 0; i < 27; i++) inventory[i] = InventorySlot(AIR, 0); + playerHunger = 20.0f; // Start with full hunger } else { // Restoration logic for existing worlds float spawnY = (spawnSavedY > 0) ? spawnSavedY : FindSpawnY((int)spawnSavedX, (int)spawnSavedZ); @@ -2641,8 +2668,8 @@ int main(void) DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE); EndMode2D(); - // Show Version Number (v2.3.0) in Red - DrawTextEx(customFont, "v2.3.0", (Vector2){ 20, (float)currentHeight - 30 }, 22, 1.0f, RED); + // Show Version Number (v2.3.1) in Red + DrawTextEx(customFont, "v2.3.1", (Vector2){ 20, (float)currentHeight - 30 }, 22, 1.0f, RED); // --- PLAYER NAME POPUP (IF MISSING) --- if (playerName == "") { @@ -2820,7 +2847,7 @@ int main(void) spawnSavedX = 0; spawnSavedY = -1; spawnSavedZ = 0; } - // Load Chests (v2.3.0) + // Load Chests (v2.3.1) chestInventories.clear(); std::ifstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary); if (cf.is_open()) { @@ -2838,7 +2865,7 @@ int main(void) cf.close(); } - // Load Torches (v2.3.0) + // Load Torches (v2.3.1) torchPositions.clear(); std::ifstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary); if (tf.is_open()) { @@ -3122,7 +3149,7 @@ int main(void) snprintf(worldName, sizeof(worldName), "%s", currentWorldName.c_str()); worldNameLen = strlen(worldName); - gameTime = 150.0f; // Start new world at 7:00 AM (v2.3.0 adjusted for slower cycle) + gameTime = 150.0f; // Start new world at 7:00 AM (v2.3.1 adjusted for slower cycle) if (serverMode) { serverSocket = socket(AF_INET, SOCK_STREAM, 0); @@ -3248,6 +3275,17 @@ int main(void) } } + // Render Dropped Items + for (const auto& item : droppedItems) { + if (item.active) { + Texture2D tex = (item.type == GRASS) ? grassTopTexture : blockTextures[item.type]; + // Floating animation + float bounce = sinf(GetTime() * 3.0f) * 0.1f; + Vector3 renderPos = { item.pos.x, item.pos.y + bounce, item.pos.z }; + DrawBillboard(camera3D, tex, renderPos, 0.4f, WHITE); + } + } + for (int renderType = 1; renderType < 64; renderType++) { if (renderType == GRASS) { for (Chunk* chunk : visibleChunks) { @@ -3276,7 +3314,7 @@ int main(void) } else if (renderType == TORCH) { for (Chunk* chunk : visibleChunks) { for (auto& data : chunk->renderLists[TORCH]) { - // 3D Flickering Torch (v2.3.0) + // 3D Flickering Torch (v2.3.1) float flicker = sinf(GetTime() * 12.0f) * 0.03f; float h = 0.6f + flicker; float w = 0.12f; @@ -3545,7 +3583,7 @@ int main(void) (Vector2){ (float)(sx + (slotSize/2) - (countSize.x/2)), (float)(hotbarY + slotSize - 14) }, 12, 1.0f, WHITE); - // Draw Hotbar Durability Bar (v2.3.0) + // Draw Hotbar Durability Bar (v2.3.1) if (hotbar[s].maxDurability > 0 && hotbar[s].durability < hotbar[s].maxDurability) { float durP = (float)hotbar[s].durability / hotbar[s].maxDurability; int barW = slotSize - 10; @@ -3576,7 +3614,7 @@ int main(void) DrawRectangleRec(slotRect, hov ? (Color){90,90,90,255} : (Color){60,60,60,255}); DrawRectangleLinesEx(slotRect, 2.0f, hov ? WHITE : (Color){100,100,100,200}); - // Draw Durability Bar (v2.3.0) + // Draw Durability Bar (v2.3.1) if (slot.maxDurability > 0 && slot.durability < slot.maxDurability) { float durPercent = (float)slot.durability / slot.maxDurability; int barW = slotRect.width - 10; @@ -3853,7 +3891,7 @@ int main(void) } } if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { - // Right click: pick up/place one (v2.3.0) + // Right click: pick up/place one (v2.3.1) if (mouseHeldItem.blockType == AIR) { if (chestInv[i].blockType != AIR && chestInv[i].count > 0) { mouseHeldItem = InventorySlot(chestInv[i].blockType, 1); @@ -3900,7 +3938,7 @@ int main(void) } } if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { - // Right click logic for player inv in chest GUI (v2.3.0) + // Right click logic for player inv in chest GUI (v2.3.1) if (mouseHeldItem.blockType == AIR) { if (inventory[i].blockType != AIR && inventory[i].count > 0) { mouseHeldItem = InventorySlot(inventory[i].blockType, 1); @@ -3945,7 +3983,7 @@ int main(void) int cols = 9; int slotS = 64; - for (int i = 1; i <= 41; i++) { // All blocks and tools + for (int i = 1; i <= 42; i++) { // All blocks and tools (including Apple) if (i == 14 || i == 15 || i == 16 || (i >= 17 && i <= 24)) continue; // Skip untextured/meta items if any int row = (i-1) / cols; @@ -4079,7 +4117,7 @@ int main(void) invf.close(); } - // Save Chests (v2.3.0) + // Save Chests (v2.3.1) std::ofstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary); if (cf.is_open()) { uint32_t count = (uint32_t)chestInventories.size(); @@ -4095,7 +4133,7 @@ int main(void) cf.close(); } - // Save Torches (v2.3.0) + // Save Torches (v2.3.1) std::ofstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary); if (tf.is_open()) { uint32_t count = (uint32_t)torchPositions.size(); diff --git a/version.txt b/version.txt index b1d18bc..aaf7425 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.3.0 +v2.3.1