MorriCraft v2.3.2: Vivid damage feedback (red flash, knockback, classic hit sound) and cheat menu fixes

This commit is contained in:
Michael Howard 2026-04-25 14:56:48 -05:00
parent f0b8d2f3db
commit 7c001ef8cc
14 changed files with 51 additions and 29 deletions

View File

@ -106,7 +106,7 @@ A pre-built `MorriCraft-Windows.zip` is available in the repository root.
## 📜 Version History
### v2.3.1 - Major Update: Hunger System & Accurate Updater (Current)
### v2.3.2 - Major Update: Vivid Damage Feedback & Cheat Fixes (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.

BIN
assets/hit.ogg Normal file

Binary file not shown.

View File

@ -1 +1 @@
v2.3.1
v2.3.2

Binary file not shown.

BIN
build-linux/assets/hit.ogg Normal file

Binary file not shown.

View File

@ -1 +1 @@
v2.3.1
v2.3.2

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
v2.3.1
v2.3.2

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
v2.3.1
v2.3.2

View File

@ -184,6 +184,7 @@ static float playerHunger = 20.0f;
static float hungerTimer = 0.0f;
static float healthRegenTimer = 0.0f;
static float healthStarveTimer = 0.0f;
static float damageFlashTimer = 0.0f;
static uint32_t localPlayerID = 0;
static Sound hitSound;
@ -394,7 +395,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.1)
// Simple Torch Lighting (v2.3.2)
float blockLight = 0.0f;
Vector3 bPos = {(float)(worldX+lx), (float)ly, (float)(worldZ+lz)};
for (const auto& tp : torchPositions) {
@ -704,7 +705,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.1: Spawn higher (feet at y + 1.5, eyes at y + 3.1) to avoid ground-clipping
// v2.3.2: Spawn higher (feet at y + 1.5, eyes at y + 3.1) to avoid ground-clipping
return (float)y + 1.5f + 1.6f;
}
}
@ -745,7 +746,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.1): Smoothly flatten area around (0,0)
// Spawn Plateau (v2.3.2): Smoothly flatten area around (0,0)
float distToSpawn = sqrtf(worldX * worldX + worldZ * worldZ);
if (distToSpawn < 12.0f) {
float plateauStrength = 1.0f - (distToSpawn / 12.0f);
@ -757,7 +758,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.1: 4x larger biomes)
// Biome noise: determines surface type (v2.3.2: 4x larger biomes)
float biomeNoise = fbm(worldX * 0.002f, worldZ * 0.002f, globalSeedHash + 77777);
for (int y = 0; y <= height; y++) {
@ -1042,7 +1043,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.1");
InitWindow(screenWidth, screenHeight, "MorriCraft v2.3.2");
LoadConfig();
SetExitKey(KEY_NULL); // Prevent ESC from closing the window
@ -1081,6 +1082,7 @@ int main(void)
// Digging Sound Effects
Sound digGrass = LoadSound("assets/grass1.ogg");
hitSound = LoadSound("assets/hit.ogg");
Sound digWood = LoadSound("assets/wood1.ogg");
Sound digStone = LoadSound("assets/stone1.ogg");
Sound digSand = LoadSound("assets/sand1.ogg");
@ -1112,7 +1114,7 @@ int main(void)
float updateTimer = 0.0f;
float downloadProgress = 0.0f;
std::string latestVersion = "";
std::string localVersion = "v2.3.1"; // Default fallback
std::string localVersion = "v2.3.2"; // Default fallback
// Read local version
std::ifstream vfile("assets/version.txt");
@ -1724,7 +1726,7 @@ int main(void)
// --- GLOBAL TIME & AUDIO MANAGEMENT ---
float cycleLength = 600.0f; // Slower day cycle (v2.3.1: cut speed in half)
float cycleLength = 600.0f; // Slower day cycle (v2.3.2: cut speed in half)
if (currentState == GAMEPLAY) {
gameTime += GetFrameTime();
@ -1750,12 +1752,21 @@ int main(void)
healthStarveTimer += GetFrameTime();
if (healthStarveTimer >= 3.0f) {
healthStarveTimer = 0;
if (playerHealth > 2.0f) playerHealth -= 1.0f; // Half heart until 1 heart left
if (playerHealth > 2.0f) {
playerHealth -= 1.0f; // Half heart
PlaySound(hitSound);
damageFlashTimer = 0.4f;
// Knockback
Vector3 forward = Vector3Normalize(Vector3Subtract(camera3D.target, camera3D.position));
camera3D.position = Vector3Subtract(camera3D.position, Vector3Scale(forward, 0.5f));
}
}
} else {
healthStarveTimer = 0;
}
if (damageFlashTimer > 0) damageFlashTimer -= GetFrameTime();
// Item Pickup Logic
for (auto& item : droppedItems) {
if (item.active) {
@ -2128,7 +2139,7 @@ int main(void)
}
NetSetBlock(hitX, hitY, hitZ, AIR);
// Tool Durability Logic (v2.3.1)
// Tool Durability Logic (v2.3.2)
InventorySlot* slot = &hotbar[activeHotbarSlot];
if (slot->maxDurability > 0) {
slot->durability--;
@ -2668,8 +2679,8 @@ int main(void)
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
EndMode2D();
// Show Version Number (v2.3.1) in Red
DrawTextEx(customFont, "v2.3.1", (Vector2){ 20, (float)currentHeight - 30 }, 22, 1.0f, RED);
// Show Version Number (v2.3.2) in Red
DrawTextEx(customFont, "v2.3.2", (Vector2){ 20, (float)currentHeight - 30 }, 22, 1.0f, RED);
// --- PLAYER NAME POPUP (IF MISSING) ---
if (playerName == "") {
@ -2847,7 +2858,7 @@ int main(void)
spawnSavedX = 0; spawnSavedY = -1; spawnSavedZ = 0;
}
// Load Chests (v2.3.1)
// Load Chests (v2.3.2)
chestInventories.clear();
std::ifstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary);
if (cf.is_open()) {
@ -2865,7 +2876,7 @@ int main(void)
cf.close();
}
// Load Torches (v2.3.1)
// Load Torches (v2.3.2)
torchPositions.clear();
std::ifstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary);
if (tf.is_open()) {
@ -3149,7 +3160,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.1 adjusted for slower cycle)
gameTime = 150.0f; // Start new world at 7:00 AM (v2.3.2 adjusted for slower cycle)
if (serverMode) {
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
@ -3314,7 +3325,7 @@ int main(void)
} else if (renderType == TORCH) {
for (Chunk* chunk : visibleChunks) {
for (auto& data : chunk->renderLists[TORCH]) {
// 3D Flickering Torch (v2.3.1)
// 3D Flickering Torch (v2.3.2)
float flicker = sinf(GetTime() * 12.0f) * 0.03f;
float h = 0.6f + flicker;
float w = 0.12f;
@ -3472,6 +3483,10 @@ int main(void)
EndMode3D();
if (damageFlashTimer > 0) {
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 255, 0, 0, (unsigned char)(120 * (damageFlashTimer/0.4f)) });
}
// Draw 2D Crosshair overlay (hide when inventory open)
if (!inventoryOpen) {
DrawRectangle(currentWidth/2 - 2, currentHeight/2 - 10, 4, 20, (Color){255, 255, 255, 150});
@ -3583,7 +3598,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.1)
// Draw Hotbar Durability Bar (v2.3.2)
if (hotbar[s].maxDurability > 0 && hotbar[s].durability < hotbar[s].maxDurability) {
float durP = (float)hotbar[s].durability / hotbar[s].maxDurability;
int barW = slotSize - 10;
@ -3614,7 +3629,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.1)
// Draw Durability Bar (v2.3.2)
if (slot.maxDurability > 0 && slot.durability < slot.maxDurability) {
float durPercent = (float)slot.durability / slot.maxDurability;
int barW = slotRect.width - 10;
@ -3891,7 +3906,7 @@ int main(void)
}
}
if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
// Right click: pick up/place one (v2.3.1)
// Right click: pick up/place one (v2.3.2)
if (mouseHeldItem.blockType == AIR) {
if (chestInv[i].blockType != AIR && chestInv[i].count > 0) {
mouseHeldItem = InventorySlot(chestInv[i].blockType, 1);
@ -3938,7 +3953,7 @@ int main(void)
}
}
if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
// Right click logic for player inv in chest GUI (v2.3.1)
// Right click logic for player inv in chest GUI (v2.3.2)
if (mouseHeldItem.blockType == AIR) {
if (inventory[i].blockType != AIR && inventory[i].count > 0) {
mouseHeldItem = InventorySlot(inventory[i].blockType, 1);
@ -3983,7 +3998,7 @@ int main(void)
int cols = 9;
int slotS = 64;
for (int i = 1; i <= 42; i++) { // All blocks and tools (including Apple)
for (int i = 1; i < 64; i++) { // Show all slots to ensure nothing is missed
if (i == 14 || i == 15 || i == 16 || (i >= 17 && i <= 24)) continue; // Skip untextured/meta items if any
int row = (i-1) / cols;
@ -3997,7 +4012,14 @@ int main(void)
Texture2D tex = (i == GRASS) ? grassTopTexture : blockTextures[i];
if (tex.id > 0) {
DrawTexturePro(tex, (Rectangle){0,0,(float)tex.width,(float)tex.height}, (Rectangle){r.x+8, r.y+8, 48, 48}, (Vector2){0,0}, 0, WHITE);
if (hov) hoveredItemName = GetBlockName(i);
} else if (i == APPLE) {
// Emergency fallback for Apple if texture failed
DrawCircle(r.x + 32, r.y + 32, 20, RED);
}
if (hov) {
hoveredItemName = GetBlockName(i);
if (hoveredItemName == "Unknown" && i == APPLE) hoveredItemName = "Apple"; // Double check
}
if (hov && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
@ -4117,7 +4139,7 @@ int main(void)
invf.close();
}
// Save Chests (v2.3.1)
// Save Chests (v2.3.2)
std::ofstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary);
if (cf.is_open()) {
uint32_t count = (uint32_t)chestInventories.size();
@ -4133,7 +4155,7 @@ int main(void)
cf.close();
}
// Save Torches (v2.3.1)
// Save Torches (v2.3.2)
std::ofstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary);
if (tf.is_open()) {
uint32_t count = (uint32_t)torchPositions.size();

View File

@ -1 +1 @@
v2.3.1
v2.3.2