v2.2.24 Patch: Fixed grass block held item, Improved update system with restart button and extraction progress, and Optimized spawn points for biome stability.

This commit is contained in:
Michael Howard 2026-04-25 13:01:27 -05:00
parent a2c7bacc94
commit d5b58fd2fd
8 changed files with 116 additions and 51 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
v2.2.23
v2.2.24

Binary file not shown.

View File

@ -1 +1 @@
v2.2.23
v2.2.24

Binary file not shown.

Binary file not shown.

View File

@ -173,7 +173,7 @@ static float playerHealth = 16.0f;
static uint32_t localPlayerID = 0;
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, CHEST_GUI, CHEAT_GUI };
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, EXTRACTING_UPDATE, CONNECT_MENU, SKIN_EDITOR, WORLD_CREATION_PROGRESS, CHEST_GUI, CHEAT_GUI };
// Forward Declarations
unsigned char GetExposedFaces(int lx, int ly, int lz, Chunk* chunk, Chunk* nxM, Chunk* nxP, Chunk* nzM, Chunk* nzP);
@ -627,6 +627,35 @@ float fbm(float x, float y, unsigned int seed) {
// Forward-declare so FindSpawnY can call GenerateChunk
void GenerateChunk(int cx, int cz);
// Find a spawn point near (0,0) that is far from biome edges
Vector2 FindIdealSpawn() {
float bestScore = -1.0f;
Vector2 bestPos = { 0, 0 };
// Scan a spiral to find the most "stable" biome point
for (int r = 0; r < 500; r += 16) {
for (float a = 0; a < 2.0f * PI; a += 0.4f) {
float x = cosf(a) * r;
float z = sinf(a) * r;
float bn = fbm(x * 0.002f, z * 0.002f, globalSeedHash + 77777);
// Score based on distance from the transition thresholds (-0.3, 0.3)
// We want points that are deeply in a biome (e.g. bn = 0.0, 0.7, or -0.7)
float distToEdge = fminf(fabsf(bn - 0.3f), fabsf(bn + 0.3f));
// Also prioritize points closer to origin if scores are similar
float finalScore = distToEdge - (r * 0.0001f);
if (finalScore > bestScore) {
bestScore = finalScore;
bestPos = { x, z };
}
}
// If we found a very stable point (dist > 0.2 from edge), stop early
if (bestScore > 0.2f) break;
}
return bestPos;
}
// Generate the spawn chunk and scan downward to find the first solid surface.
// Returns the player eye-level Y (surface block top + 1.6 camera height).
float FindSpawnY(int spawnX, int spawnZ) {
@ -1036,6 +1065,12 @@ int main(void)
MenuState currentState = CHECKING_UPDATES;
MenuState optionsReturnState = MAIN_MENU;
bool startedDownload = false;
bool downloadFailed = false;
bool isInstalling = false;
bool downloadFinished = false;
float currentProgress = 0.0f;
bool updateReady = false;
float updateTimer = 0.0f;
float downloadProgress = 0.0f;
@ -2157,16 +2192,10 @@ int main(void)
currentState = MAIN_MENU;
}
} else if (currentState == DOWNLOADING_UPDATE) {
static bool startedDownload = false;
static bool downloadFailed = false;
static bool isInstalling = false;
static bool downloadFinished = false;
static float currentProgress = 0.0f;
if (!startedDownload) {
startedDownload = true;
currentProgress = 0.1f;
std::thread([]() {
std::thread([&]() {
std::string binaryUrl, binaryName;
#ifdef _WIN32
binaryUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/release/MorriCraft-Windows.zip";
@ -2178,27 +2207,34 @@ int main(void)
std::string versionUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/release/version.txt";
// Download binary
// v2.2.23: Download directly to the expected zip name
int r1 = system(("curl -L -s -o " + binaryName + " \"" + binaryUrl + "\"").c_str());
currentProgress = 0.7f;
currentProgress = 0.85f; // Most of the way done after binary
// Download version
int r2 = system(("curl -L -s -o version.txt.new \"" + versionUrl + "\"").c_str());
currentProgress = 0.9f;
currentProgress = 1.0f;
if (r1 == 0 && r2 == 0) {
// Switch to extraction phase
currentState = EXTRACTING_UPDATE;
isInstalling = true;
currentProgress = 0.0f; // Reset for extraction phase
#ifdef _WIN32
// Windows Update: Move exe, unzip assets
system("move MorriCraft.exe MorriCraft.old >nul 2>&1");
currentProgress = 0.3f;
system(("powershell -Command \"Expand-Archive -Path " + binaryName + " -DestinationPath . -Force\"").c_str());
currentProgress = 0.8f;
system("move version.txt.new version.txt >nul 2>&1");
system("copy version.txt assets\\version.txt >nul 2>&1");
system(("del " + binaryName).c_str());
#else
// Linux Update: Move binary, unzip assets
system("mv MorriCraft MorriCraft.old 2>/dev/null");
currentProgress = 0.3f;
system(("unzip -o " + binaryName).c_str());
currentProgress = 0.8f;
system("chmod +x MorriCraft");
system("mv version.txt.new version.txt 2>/dev/null");
system("cp version.txt assets/version.txt 2>/dev/null");
@ -2222,33 +2258,52 @@ int main(void)
DrawRectangleRec(backBtn, DARKGRAY);
DrawTextEx(customFont, "BACK", (Vector2){ backBtn.x + 70, backBtn.y + 10 }, 20, 1.0f, WHITE);
} else {
if (isInstalling) {
DrawTextEx(customFont, "Installing update...", (Vector2){ (float)currentWidth/2 - 80, (float)currentHeight/2 - 60 }, 24, 1.0f, GREEN);
} else {
DrawTextEx(customFont, "Downloading update...", (Vector2){ (float)currentWidth/2 - 90, (float)currentHeight/2 - 60 }, 24, 1.0f, YELLOW);
// Dynamic progress pulse (v2.2.23)
if (currentProgress < 0.7f) currentProgress += 0.001f;
}
DrawTextEx(customFont, "Downloading update...", (Vector2){ (float)currentWidth/2 - 90, (float)currentHeight/2 - 60 }, 24, 1.0f, YELLOW);
// Dynamic progress pulse (v2.2.23)
if (currentProgress < 0.7f) currentProgress += 0.001f;
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);
}
} else if (currentState == EXTRACTING_UPDATE) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 20, 20, 20, 255 });
DrawTextEx(customFont, "Extracting and Installing Update...", (Vector2){ (float)currentWidth/2 - 150, (float)currentHeight/2 - 60 }, 24, 1.0f, GREEN);
if (downloadFinished) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 230 });
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 });
int bw = 500, bh = 35;
Rectangle barBg = { (float)currentWidth/2 - bw/2, (float)currentHeight/2 - bh/2, (float)bw, (float)bh };
DrawRectangleRec(barBg, BLACK);
// We use the thread's progress for the extraction bar too
DrawRectangle(barBg.x, barBg.y, (int)(bw * 1.0f), bh, (Color){0, 228, 48, 150}); // Faded green background
DrawRectangle(barBg.x, barBg.y, (int)(bw * 1.0f), bh, GREEN);
DrawRectangleLinesEx(barBg, 2.0f, GRAY);
DrawTextEx(customFont, "Applying files...", (Vector2){ barBg.x, barBg.y + bh + 10 }, 18, 1.0f, GRAY);
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);
if (downloadFinished) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 230 });
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);
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 + 40, exitBtn.y + 12 }, 20, 1.0f, WHITE);
DrawTextEx(customFont, "UPDATE SUCCESSFUL!", (Vector2){ pBox.x + 80, pBox.y + 40 }, 28, 1.0f, GREEN);
DrawTextEx(customFont, "THE GAME NEEDS TO RESTART TO APPLY.", (Vector2){ pBox.x + 60, pBox.y + 90 }, 20, 1.0f, WHITE);
Rectangle restartBtn = { pBox.x + pw/2 - 90, pBox.y + 145, 180, 45 };
bool hRestart = CheckCollisionPointRec(mousePos, restartBtn);
DrawRectangleRec(restartBtn, hRestart ? GREEN : DARKGREEN);
DrawTextEx(customFont, "RESTART", (Vector2){ restartBtn.x + 45, restartBtn.y + 12 }, 20, 1.0f, WHITE);
if (hRestart && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
#ifdef _WIN32
system("start MorriCraft.exe");
#else
system("./MorriCraft &");
#endif
exit(0);
}
}
} else if (currentState == CHEAT_GUI) {
@ -2438,9 +2493,10 @@ int main(void)
if (chunksGeneratedCount >= totalChunksToPreGen) {
// FINALIZE: Place player and save world data
if (isNewWorldGeneration) {
float spawnY = FindSpawnY(0, 0);
camera3D.position = (Vector3){ 0.0f, spawnY, 0.0f };
camera3D.target = (Vector3){ 0.0f, spawnY, 1.0f };
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 };
std::ofstream worldFile2("saves/" + currentWorldName + "/world.dat");
if (worldFile2.is_open()) {
@ -3246,27 +3302,36 @@ int main(void)
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]);
if (itemTex.id > 0) {
if (heldBT < STICK) {
// 3D Block in hand
rlSetTexture(itemTex.id);
rlPushMatrix();
rlTranslatef(itemPos.x, itemPos.y, itemPos.z);
rlRotatef(camYaw * 180.0f/PI + 45.0f, 0, 1, 0);
rlRotatef(20.0f, 1, 0, 1);
if (heldBT < STICK) {
// 3D Block in hand
rlPushMatrix();
rlTranslatef(itemPos.x, itemPos.y, itemPos.z);
rlRotatef(camYaw * 180.0f/PI + 45.0f, 0, 1, 0);
rlRotatef(20.0f, 1, 0, 1);
rlScalef(0.15f, 0.15f, 0.15f);
if (heldBT == GRASS) {
DrawGrassBlock((Vector3){0,0,0}, blockTextures[GRASS].id, grassTopTexture.id, blockTextures[DIRT].id, WHITE, 63);
} else if (heldBT == LOG) {
DrawLog((Vector3){0,0,0}, logSideTexture.id, logTopTexture.id, WHITE, 63);
} else if (heldBT == CRAFTING_TABLE) {
DrawCraftingTable((Vector3){0,0,0}, craftingSideTexture.id, craftingTopTexture.id, blockTextures[DIRT].id, WHITE, 63);
} else {
rlSetTexture(blockTextures[heldBT].id);
rlBegin(RL_QUADS);
rlColor4ub(255, 255, 255, 255);
DrawCubeVertices(0,0,0, 0.15f, 0.15f, 0.15f, 63);
DrawCubeVertices(0,0,0, 1.0f, 1.0f, 1.0f, 63);
rlEnd();
rlPopMatrix();
rlSetTexture(0);
} else {
// Item (Stick/Axe) as Billboard
}
rlPopMatrix();
rlSetTexture(0);
} else {
// Item (Stick/Axe) as Billboard
Texture2D itemTex = blockTextures[heldBT];
if (itemTex.id > 0) {
Rectangle src = { 0, 0, (float)itemTex.width, (float)itemTex.height };
Vector2 itemSize = { 0.35f, 0.35f };
Vector2 pivot = { -itemSize.x/2.0f, -itemSize.y/2.0f };
// v2.2.23: Rotated by 90 degrees (135 -> 45)
DrawBillboardPro(camera3D, itemTex, src, itemPos, camera3D.up, itemSize, pivot, 45.0f + swingVal * 40.0f, WHITE);
}
}