diff --git a/README.md b/README.md index 756576c..35f6d26 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,16 @@ MorriCraft is a high-performance voxel engine built with C++ and Raylib. It feat ## Version History -### v2.0.9 - Identity Update (Latest) -* **Humanoid Player Models**: Replaced placeholder "blue blobs" with multi-cube humanoid shapes (Head, Body, Arms, Legs). -* **Chat Input Isolation**: All movement (WASD, Space) and action keys are now fully inhibited while the chat box is active. -* **Visual Polish**: Adjusted nameplate positioning for better clarity with the new player models. +### v2.1.0 - Personalization & Stability (Latest) +* **Integrated Skin Editor**: Personalize your shirt and pants colors directly from the Options menu. +* **Network Skin Sync**: Custom skin colors are now synchronized between all players via an updated handshake protocol. +* **Enhanced World Sync**: Increased chunk rebuild priority and optimized block update broadcasting for real-time consistency. +* **Refined Nameplates**: Elevated nameplates to 2.1f for better visibility above the new humanoid models. +* **Diagnostic Logging**: Added developer-focused logging for world state changes to ensure parity. + +### v2.0.9 - Identity Update +* **Humanoid Player Models**: Replaced placeholder "blue blobs" with multi-cube humanoid shapes. +* **Chat Input Isolation**: Inhibited movement and action keys while the chat interface is active. ### v2.0.8 - Real Update & LAN * **Real Update System**: Implemented live file download via `curl` for seamless client updates. diff --git a/build-linux/MorriCraft b/build-linux/MorriCraft index ceb189a..d37dfc2 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 c03bb3d..1defe53 100644 --- a/build-linux/assets/version.txt +++ b/build-linux/assets/version.txt @@ -1 +1 @@ -v2.0.9 +v2.1.0 diff --git a/build-windows/MorriCraft.exe b/build-windows/MorriCraft.exe index 8f46291..5996fe4 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 c03bb3d..1defe53 100644 --- a/build-windows/assets/version.txt +++ b/build-windows/assets/version.txt @@ -1 +1 @@ -v2.0.9 +v2.1.0 diff --git a/src/main.cpp b/src/main.cpp index d256ac2..f0d71b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,8 +89,10 @@ static std::string playerName = "Player"; static bool serverMode = false; static float masterMusicVolume = 1.0f; static float masterSoundVolume = 1.0f; +static Color myShirtColor = BLUE; +static Color myPantsColor = DARKBLUE; -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 }; +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 }; // Forward Declarations bool IsExposedOptimized(int lx, int ly, int lz, Chunk* chunk, Chunk* nxM, Chunk* nxP, Chunk* nzM, Chunk* nzP); @@ -107,6 +109,8 @@ struct RemotePlayer { std::string name; Vector3 position; float yaw; + Color shirtColor; + Color pantsColor; }; static std::vector remotePlayers; @@ -313,11 +317,10 @@ void SetBlock(int x, int y, int z, int type) { // Keep maxY up to date if (type != AIR && y > worldChunks[key]->maxY) worldChunks[key]->maxY = y; - // Mark neighbors dirty as well since exposure changes - if (lx == 0) { auto n = worldChunks.find({cx-1, cz}); if (n != worldChunks.end()) n->second->dirty = true; } - if (lx == CHUNK_SIZE-1) { auto n = worldChunks.find({cx+1, cz}); if (n != worldChunks.end()) n->second->dirty = true; } - if (lz == 0) { auto n = worldChunks.find({cx, cz-1}); if (n != worldChunks.end()) n->second->dirty = true; } if (lz == CHUNK_SIZE-1) { auto n = worldChunks.find({cx, cz+1}); if (n != worldChunks.end()) n->second->dirty = true; } + + // Diagnostic log for block changes (helps debug sync) + TraceLog(LOG_INFO, "Block set at %d, %d, %d to %d", x, y, z, type); } } @@ -344,6 +347,8 @@ void SaveConfig() { file << "playerName=" << playerName << "\n"; file << "music=" << masterMusicVolume << "\n"; file << "sound=" << masterSoundVolume << "\n"; + file << "shirt=" << (int)myShirtColor.r << "," << (int)myShirtColor.g << "," << (int)myShirtColor.b << "\n"; + file << "pants=" << (int)myPantsColor.r << "," << (int)myPantsColor.g << "," << (int)myPantsColor.b << "\n"; file.close(); } } @@ -775,7 +780,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.0.9"); + InitWindow(screenWidth, screenHeight, "MorriCraft v2.1.0"); LoadConfig(); SetExitKey(KEY_NULL); // Prevent ESC from closing the window @@ -998,6 +1003,8 @@ int main(void) PacketHeader head = { (uint8_t)PACKET_HANDSHAKE, (uint32_t)sizeof(PacketHandshake) }; PacketHandshake hand; strncpy(hand.name, playerName.c_str(), 31); + hand.shirtR = myShirtColor.r; hand.shirtG = myShirtColor.g; hand.shirtB = myShirtColor.b; + hand.pantsR = myPantsColor.r; hand.pantsG = myPantsColor.g; hand.pantsB = myPantsColor.b; SendAll(clientSocket, (char*)&head, sizeof(head)); SendAll(clientSocket, (char*)&hand, sizeof(hand)); } @@ -1054,6 +1061,8 @@ int main(void) rp.id = newID; rp.name = hand.name; rp.position = (Vector3){0,0,0}; + rp.shirtColor = (Color){ hand.shirtR, hand.shirtG, hand.shirtB, 255 }; + rp.pantsColor = (Color){ hand.pantsR, hand.pantsG, hand.pantsB, 255 }; remotePlayers.push_back(rp); chatLog.push_back({ std::string(hand.name) + " joined the game", 5.0f }); @@ -1109,6 +1118,8 @@ int main(void) rp.name = "Remote Player"; rp.position = (Vector3){ pu.x, pu.y, pu.z }; rp.yaw = pu.yaw; + rp.shirtColor = BLUE; // Default until handshake syncs + rp.pantsColor = DARKBLUE; remotePlayers.push_back(rp); } if (isServer) { @@ -1693,7 +1704,68 @@ int main(void) DrawTextEx(customFont, "EXIT GAME", (Vector2){ exitBtn.x + 30, exitBtn.y + 10 }, 20, 1.0f, WHITE); } } - } else if (currentState == CONNECT_MENU) { + } else if (currentState == SKIN_EDITOR) { + DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 20, 20, 20, 255 }); + int panelWidth = 700; + int panelHeight = 500; + int panelX = (currentWidth / 2) - (panelWidth / 2); + int panelY = (currentHeight / 2) - (panelHeight / 2); + DrawRectangle(panelX, panelY, panelWidth, panelHeight, (Color){ 40, 40, 40, 240 }); + DrawRectangleLinesEx((Rectangle){ (float)panelX, (float)panelY, (float)panelWidth, (float)panelHeight }, 4.0f, BLUE); + + DrawTextEx(customFont, "Skin Editor", (Vector2){ (float)panelX + 20, (float)panelY + 20 }, 32, 1.0f, WHITE); + + // Preview Model (3D) + static float previewRot = 0.0f; + previewRot += GetFrameTime() * 30.0f; + + Camera3D previewCam = { 0 }; + previewCam.position = (Vector3){ 3.0f, 2.0f, 3.0f }; + previewCam.target = (Vector3){ 0.0f, 1.0f, 0.0f }; + previewCam.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + previewCam.fovy = 45.0f; + previewCam.projection = CAMERA_PERSPECTIVE; + + BeginMode3D(previewCam); + rlPushMatrix(); + rlRotatef(previewRot, 0, 1, 0); + // Render Humanoid + DrawCube((Vector3){0, 0.7f, 0}, 0.6f, 0.9f, 0.3f, myShirtColor); + DrawCube((Vector3){0, 1.4f, 0}, 0.45f, 0.45f, 0.45f, (Color){220, 180, 150, 255}); + DrawCube((Vector3){-0.45f, 0.7f, 0}, 0.2f, 0.8f, 0.2f, (Color){200, 160, 130, 255}); + DrawCube((Vector3){0.45f, 0.7f, 0}, 0.2f, 0.8f, 0.2f, (Color){200, 160, 130, 255}); + DrawCube((Vector3){-0.15f, 0.15f, 0}, 0.25f, 0.4f, 0.25f, myPantsColor); + DrawCube((Vector3){0.15f, 0.15f, 0}, 0.25f, 0.4f, 0.25f, myPantsColor); + rlPopMatrix(); + EndMode3D(); + + // Color Pickers + Color shirtPresets[] = { RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, WHITE, BLACK }; + Color pantsPresets[] = { DARKBLUE, BROWN, DARKGRAY, BLACK, DARKGREEN, MAROON }; + + DrawTextEx(customFont, "Shirt Color", (Vector2){ (float)panelX + 400, (float)panelY + 100 }, 20, 1.0f, LIGHTGRAY); + for (int i = 0; i < 8; i++) { + Rectangle r = { (float)panelX + 400 + (i % 4) * 50, (float)panelY + 130 + (i / 4) * 50, 40, 40 }; + DrawRectangleRec(r, shirtPresets[i]); + if (CheckCollisionPointRec(mousePos, r) && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) myShirtColor = shirtPresets[i]; + if (myShirtColor.r == shirtPresets[i].r && myShirtColor.g == shirtPresets[i].g && myShirtColor.b == shirtPresets[i].b) DrawRectangleLinesEx(r, 2.0f, WHITE); + } + + DrawTextEx(customFont, "Pants Color", (Vector2){ (float)panelX + 400, (float)panelY + 250 }, 20, 1.0f, LIGHTGRAY); + for (int i = 0; i < 6; i++) { + Rectangle r = { (float)panelX + 400 + (i % 3) * 50, (float)panelY + 280 + (i / 3) * 50, 40, 40 }; + DrawRectangleRec(r, pantsPresets[i]); + if (CheckCollisionPointRec(mousePos, r) && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) myPantsColor = pantsPresets[i]; + if (myPantsColor.r == pantsPresets[i].r && myPantsColor.g == pantsPresets[i].g && myPantsColor.b == pantsPresets[i].b) DrawRectangleLinesEx(r, 2.0f, WHITE); + } + + Rectangle doneBtn = { (float)panelX + panelWidth - 150, (float)panelY + panelHeight - 60, 120, 40 }; + if (CheckCollisionPointRec(mousePos, doneBtn) && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + SaveConfig(); + currentState = OPTIONS_MENU; + } + DrawRectangleRec(doneBtn, GREEN); + DrawTextEx(customFont, "SAVE", (Vector2){ doneBtn.x + 35, doneBtn.y + 10 }, 20, 1.0f, WHITE); DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 220 }); int cw = 500, ch = 350; Rectangle cBox = { (float)currentWidth/2 - cw/2, (float)currentHeight/2 - ch/2, (float)cw, (float)ch }; @@ -1795,8 +1867,8 @@ int main(void) DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE); EndMode2D(); - // Show Version Number (v2.0.9) in Red - DrawTextEx(customFont, "v2.0.9", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED); + // Show Version Number (v2.1.0) in Red + DrawTextEx(customFont, "v2.1.0", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED); // --- PLAYER NAME POPUP (IF MISSING) --- if (playerName == "") { @@ -2120,6 +2192,17 @@ int main(void) DrawRectangleRec(soundHandle, LIGHTGRAY); DrawRectangleLinesEx(soundHandle, 2.0f, WHITE); + // Edit Skin Button + Rectangle skinBtn = { (float)panelX + panelWidth/2 - 100, (float)panelY + 310, 200, 40 }; + bool isSkinHovered = CheckCollisionPointRec(mousePos, skinBtn); + DrawRectangleRec(skinBtn, isSkinHovered ? BLUE : DARKBLUE); + DrawRectangleLinesEx(skinBtn, 2.0f, isSkinHovered ? WHITE : GRAY); + DrawTextEx(customFont, "EDIT SKIN", (Vector2){ skinBtn.x + 55, skinBtn.y + 10 }, 20, 1.0f, WHITE); + + if (isSkinHovered && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + currentState = SKIN_EDITOR; + } + // No Multiplayer options here anymore, moved to Connect Menu // Done Button @@ -2344,19 +2427,19 @@ int main(void) // Simple Humanoid Shape (Cube-based "Skin") // Body - DrawCube((Vector3){0, 0.7f, 0}, 0.6f, 0.9f, 0.3f, BLUE); + DrawCube((Vector3){0, 0.7f, 0}, 0.6f, 0.9f, 0.3f, rp.shirtColor); // Head DrawCube((Vector3){0, 1.4f, 0}, 0.45f, 0.45f, 0.45f, (Color){220, 180, 150, 255}); // Arms DrawCube((Vector3){-0.45f, 0.7f, 0}, 0.2f, 0.8f, 0.2f, (Color){200, 160, 130, 255}); DrawCube((Vector3){0.45f, 0.7f, 0}, 0.2f, 0.8f, 0.2f, (Color){200, 160, 130, 255}); // Legs - DrawCube((Vector3){-0.15f, 0.15f, 0}, 0.25f, 0.4f, 0.25f, DARKBLUE); - DrawCube((Vector3){0.15f, 0.15f, 0}, 0.25f, 0.4f, 0.25f, DARKBLUE); + DrawCube((Vector3){-0.15f, 0.15f, 0}, 0.25f, 0.4f, 0.25f, rp.pantsColor); + DrawCube((Vector3){0.15f, 0.15f, 0}, 0.25f, 0.4f, 0.25f, rp.pantsColor); rlPopMatrix(); // Draw Nameplate - Vector2 namePos = GetWorldToScreen((Vector3){ rp.position.x, rp.position.y + 1.8f, rp.position.z }, camera3D); + Vector2 namePos = GetWorldToScreen((Vector3){ rp.position.x, rp.position.y + 2.1f, rp.position.z }, camera3D); DrawTextEx(customFont, rp.name.c_str(), (Vector2){ namePos.x - MeasureTextEx(customFont, rp.name.c_str(), 20, 1.0f).x/2, namePos.y }, 20, 1.0f, WHITE); } diff --git a/src/network.h b/src/network.h index 6a5e812..44f0ebb 100644 --- a/src/network.h +++ b/src/network.h @@ -64,6 +64,8 @@ struct PacketHeader { struct PacketHandshake { char name[32]; + uint8_t shirtR, shirtG, shirtB; + uint8_t pantsR, pantsG, pantsB; }; struct PacketPlayerUpdate { diff --git a/version.txt b/version.txt index c03bb3d..1defe53 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.0.9 +v2.1.0