MorriCraft v2.2.26: Hunger System, Apple item, improved Grass Top texture, and World Spawn offset fix
|
After Width: | Height: | Size: 427 KiB |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 870 KiB |
|
After Width: | Height: | Size: 427 KiB |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 870 KiB |
|
After Width: | Height: | Size: 427 KiB |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 870 KiB |
153
src/main.cpp
|
|
@ -31,7 +31,8 @@ enum BlockType {
|
||||||
STONE_AXE = 20, STONE_PICKAXE = 21, STONE_SWORD = 22, STONE_SHOVEL = 23, STONE_HOE = 24,
|
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,
|
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,
|
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) {
|
std::string GetBlockName(int type) {
|
||||||
|
|
@ -78,6 +79,7 @@ std::string GetBlockName(int type) {
|
||||||
case DIAMOND_SWORD: return "Diamond Sword";
|
case DIAMOND_SWORD: return "Diamond Sword";
|
||||||
case DIAMOND_SHOVEL: return "Diamond Shovel";
|
case DIAMOND_SHOVEL: return "Diamond Shovel";
|
||||||
case DIAMOND_HOE: return "Diamond Hoe";
|
case DIAMOND_HOE: return "Diamond Hoe";
|
||||||
|
case APPLE: return "Apple";
|
||||||
default: return "Unknown";
|
default: return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +172,10 @@ static float masterSoundVolume = 1.0f;
|
||||||
static Color myShirtColor = BLUE;
|
static Color myShirtColor = BLUE;
|
||||||
static Color myPantsColor = DARKBLUE;
|
static Color myPantsColor = DARKBLUE;
|
||||||
static float playerHealth = 16.0f;
|
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 uint32_t localPlayerID = 0;
|
||||||
static Sound hitSound;
|
static Sound hitSound;
|
||||||
|
|
||||||
|
|
@ -1075,7 +1081,7 @@ int main(void)
|
||||||
float updateTimer = 0.0f;
|
float updateTimer = 0.0f;
|
||||||
float downloadProgress = 0.0f;
|
float downloadProgress = 0.0f;
|
||||||
std::string latestVersion = "";
|
std::string latestVersion = "";
|
||||||
std::string localVersion = "v2.2.25"; // Default fallback
|
std::string localVersion = "v2.2.26"; // Default fallback
|
||||||
|
|
||||||
// Read local version
|
// Read local version
|
||||||
std::ifstream vfile("assets/version.txt");
|
std::ifstream vfile("assets/version.txt");
|
||||||
|
|
@ -1163,6 +1169,7 @@ int main(void)
|
||||||
blockTextures[CRAFTING_TABLE] = craftingSideTexture; // Preview for inventory
|
blockTextures[CRAFTING_TABLE] = craftingSideTexture; // Preview for inventory
|
||||||
|
|
||||||
blockTextures[STICK] = LoadTexture("assets/stick.png");
|
blockTextures[STICK] = LoadTexture("assets/stick.png");
|
||||||
|
blockTextures[APPLE] = LoadTexture("assets/apple.png");
|
||||||
blockTextures[WOOD_AXE] = LoadTexture("assets/wooden_axe.png");
|
blockTextures[WOOD_AXE] = LoadTexture("assets/wooden_axe.png");
|
||||||
blockTextures[WOOD_PICKAXE] = LoadTexture("assets/wooden_pickaxe.png");
|
blockTextures[WOOD_PICKAXE] = LoadTexture("assets/wooden_pickaxe.png");
|
||||||
blockTextures[WOOD_SWORD] = LoadTexture("assets/wooden_sword.png");
|
blockTextures[WOOD_SWORD] = LoadTexture("assets/wooden_sword.png");
|
||||||
|
|
@ -1683,7 +1690,37 @@ int main(void)
|
||||||
|
|
||||||
// --- GLOBAL TIME & AUDIO MANAGEMENT ---
|
// --- GLOBAL TIME & AUDIO MANAGEMENT ---
|
||||||
float cycleLength = 600.0f; // Slower day cycle (v2.2.23: cut speed in half)
|
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 timeOfDay = fmodf(gameTime, cycleLength) / cycleLength;
|
||||||
float sunAngle = timeOfDay * 2.0f * 3.14159f - 3.14159f/2.0f;
|
float sunAngle = timeOfDay * 2.0f * 3.14159f - 3.14159f/2.0f;
|
||||||
dayFactor = (sinf(sunAngle) + 1.0f) / 2.0f;
|
dayFactor = (sinf(sunAngle) + 1.0f) / 2.0f;
|
||||||
|
|
@ -2060,41 +2097,48 @@ int main(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
|
if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
|
||||||
int targetBlock = GetBlock(hitX, hitY, hitZ);
|
// NEW: Eating Logic
|
||||||
if (targetBlock == CRAFTING_TABLE) {
|
if (hotbar[activeHotbarSlot].blockType == APPLE && playerHunger < 20.0f) {
|
||||||
currentState = CRAFTING_GUI;
|
playerHunger += 4.0f;
|
||||||
EnableCursor();
|
if (playerHunger > 20.0f) playerHunger = 20.0f;
|
||||||
} else if (targetBlock == CHEST) {
|
hotbar[activeHotbarSlot].count--;
|
||||||
activeChestPos = (Vector3){ (float)hitX, (float)hitY, (float)hitZ };
|
if (hotbar[activeHotbarSlot].count <= 0) hotbar[activeHotbarSlot].blockType = AIR;
|
||||||
uint64_t key = GetPosKey(hitX, hitY, hitZ);
|
PlaySound(digGrass);
|
||||||
if (chestInventories.find(key) == chestInventories.end()) {
|
} else if (hitBlock) {
|
||||||
chestInventories[key] = std::vector<InventorySlot>(27, InventorySlot(AIR, 0));
|
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<InventorySlot>(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) {
|
if (chunksGeneratedCount >= totalChunksToPreGen) {
|
||||||
// FINALIZE: Place player and save world data
|
// FINALIZE: Place player and save world data
|
||||||
if (isNewWorldGeneration) {
|
if (isNewWorldGeneration) {
|
||||||
Vector2 idealSpawn = FindIdealSpawn();
|
float spawnY = FindSpawnY((int)camera3D.position.x, (int)camera3D.position.z);
|
||||||
float spawnY = FindSpawnY((int)idealSpawn.x, (int)idealSpawn.y);
|
camera3D.position.y = spawnY;
|
||||||
camera3D.position = (Vector3){ idealSpawn.x, spawnY, idealSpawn.y };
|
camera3D.target = (Vector3){ camera3D.position.x, spawnY, camera3D.position.z + 1.0f };
|
||||||
camera3D.target = (Vector3){ idealSpawn.x, spawnY, idealSpawn.y + 1.0f };
|
|
||||||
|
|
||||||
std::ofstream worldFile2("saves/" + currentWorldName + "/world.dat");
|
std::ofstream worldFile2("saves/" + currentWorldName + "/world.dat");
|
||||||
if (worldFile2.is_open()) {
|
if (worldFile2.is_open()) {
|
||||||
|
|
@ -2519,8 +2562,7 @@ int main(void)
|
||||||
invf.close();
|
invf.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
playerVelocityY = 0.0f;
|
|
||||||
camYaw = 0.0f; camPitch = 0.0f;
|
|
||||||
currentState = GAMEPLAY;
|
currentState = GAMEPLAY;
|
||||||
DisableCursor();
|
DisableCursor();
|
||||||
}
|
}
|
||||||
|
|
@ -3071,7 +3113,14 @@ int main(void)
|
||||||
isNewWorldGeneration = true;
|
isNewWorldGeneration = true;
|
||||||
chunksGeneratedCount = 0;
|
chunksGeneratedCount = 0;
|
||||||
worldGenProgress = 0.0f;
|
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;
|
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
|
// Hotbar background panel
|
||||||
DrawRectangle(hotbarX - 6, hotbarY - 6, hotbarW + 12, slotSize + 12,
|
DrawRectangle(hotbarX - 6, hotbarY - 6, hotbarW + 12, slotSize + 12,
|
||||||
(Color){ 30, 30, 30, 180 });
|
(Color){ 30, 30, 30, 180 });
|
||||||
|
|
|
||||||