Combat Update: PvP, Health Hearts, and Spawn Fix - v2.1.2
This commit is contained in:
parent
2bdfbd9fbc
commit
f169322dc0
Binary file not shown.
Binary file not shown.
111
src/main.cpp
111
src/main.cpp
|
|
@ -91,6 +91,9 @@ static float masterMusicVolume = 1.0f;
|
|||
static float masterSoundVolume = 1.0f;
|
||||
static Color myShirtColor = BLUE;
|
||||
static Color myPantsColor = DARKBLUE;
|
||||
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 };
|
||||
|
||||
|
|
@ -780,7 +783,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.1.1");
|
||||
InitWindow(screenWidth, screenHeight, "MorriCraft v2.1.2");
|
||||
LoadConfig();
|
||||
SetExitKey(KEY_NULL); // Prevent ESC from closing the window
|
||||
|
||||
|
|
@ -844,6 +847,10 @@ int main(void)
|
|||
int activeNetField = 0; // 0=none, 1=IP, 2=Port
|
||||
|
||||
InitNetworking();
|
||||
InitAudioDevice();
|
||||
hitSound = LoadSound("assets/hit.wav");
|
||||
if (hitSound.frameCount == 0) TraceLog(LOG_WARNING, "Hit sound NOT LOADED! Make sure assets/hit.wav exists.");
|
||||
|
||||
InventorySlot mouseHeldItem(AIR, 0);
|
||||
|
||||
// Chat State
|
||||
|
|
@ -1093,6 +1100,11 @@ int main(void)
|
|||
SendAll(sock, (char*)&pData, sizeof(pData));
|
||||
}
|
||||
}
|
||||
// Tell the new client its own ID (its socket value)
|
||||
PacketHeader idHead = { (uint8_t)PACKET_PLAYER_UPDATE, (uint32_t)sizeof(PacketPlayerUpdate) };
|
||||
PacketPlayerUpdate idData = { camera3D.position.x, camera3D.position.y - 1.6f, camera3D.position.z, camYaw, (uint32_t)sock };
|
||||
SendAll(sock, (char*)&idHead, sizeof(idHead));
|
||||
|
||||
PacketHeader hHead = { (uint8_t)PACKET_PLAYER_UPDATE, (uint32_t)sizeof(PacketPlayerUpdate) };
|
||||
PacketPlayerUpdate hData = { camera3D.position.x, camera3D.position.y - 1.6f, camera3D.position.z, camYaw, 0 };
|
||||
SendAll(sock, (char*)&hHead, sizeof(hHead));
|
||||
|
|
@ -1112,16 +1124,21 @@ int main(void)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!found && !isServer) {
|
||||
if (pu.playerID != 0 && !isServer && pu.playerID != (uint32_t)clientSocket) {
|
||||
// If it's a new player we haven't seen yet
|
||||
if (!found) {
|
||||
RemotePlayer rp;
|
||||
rp.id = pu.playerID;
|
||||
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.shirtColor = BLUE;
|
||||
rp.pantsColor = DARKBLUE;
|
||||
remotePlayers.push_back(rp);
|
||||
}
|
||||
} else if (!isServer && pu.playerID == (uint32_t)clientSocket) {
|
||||
localPlayerID = pu.playerID;
|
||||
}
|
||||
if (isServer) {
|
||||
pu.playerID = (uint32_t)sock;
|
||||
for (auto& other : clientSockets) {
|
||||
|
|
@ -1154,6 +1171,13 @@ int main(void)
|
|||
globalSeedHash = ss.seed;
|
||||
for (auto& pair : worldChunks) delete pair.second;
|
||||
worldChunks.clear();
|
||||
|
||||
// Force spawn generation to fix "falling through ground"
|
||||
int scx = (int)floorf(camera3D.position.x / CHUNK_SIZE);
|
||||
int scz = (int)floorf(camera3D.position.z / CHUNK_SIZE);
|
||||
GenerateChunk(scx, scz);
|
||||
float sy = FindSpawnY((int)camera3D.position.x, (int)camera3D.position.z);
|
||||
camera3D.position.y = sy + 1.6f;
|
||||
}
|
||||
} else if (head.type == PACKET_CHAT) {
|
||||
PacketChat pc;
|
||||
|
|
@ -1167,6 +1191,30 @@ int main(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (head.type == PACKET_PLAYER_HIT) {
|
||||
PacketPlayerHit ph;
|
||||
if (RecvAll(sock, (char*)&ph, sizeof(ph)) <= 0) break;
|
||||
|
||||
if (ph.targetID == localPlayerID) {
|
||||
// I got hit!
|
||||
playerHealth -= ph.damage;
|
||||
playerVelocityY = 6.0f; // Small jump
|
||||
Vector3 pushDir = Vector3Normalize((Vector3){ camera3D.position.x - ph.attackerX, 0, camera3D.position.z - ph.attackerZ });
|
||||
camera3D.position.x += pushDir.x * 0.8f;
|
||||
camera3D.position.z += pushDir.z * 0.8f;
|
||||
PlaySound(hitSound);
|
||||
chatLog.push_back({ "You were hit!", 2.0f });
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
// Broadcast hit to all OTHER clients
|
||||
for (auto& other : clientSockets) {
|
||||
if (other != sock) {
|
||||
SendAll(other, (char*)&head, sizeof(head));
|
||||
SendAll(other, (char*)&ph, sizeof(ph));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (head.size > 0 && head.size < 2048) {
|
||||
std::vector<char> discard(head.size);
|
||||
|
|
@ -1452,6 +1500,34 @@ int main(void)
|
|||
// Block Raycasting (moved outside to update every frame for wireframe)
|
||||
hitBlock = false;
|
||||
if (!inventoryOpen && !isChatting) {
|
||||
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
||||
isSwinging = true;
|
||||
swingTime = 0.0f;
|
||||
|
||||
// PvP Attack Raycast
|
||||
Ray ray = GetMouseRay((Vector2){ (float)currentWidth / 2, (float)currentHeight / 2 }, camera3D);
|
||||
for (auto& rp : remotePlayers) {
|
||||
BoundingBox pBox = { (Vector3){ rp.position.x - 0.4f, rp.position.y, rp.position.z - 0.4f },
|
||||
(Vector3){ rp.position.x + 0.4f, rp.position.y + 1.8f, rp.position.z + 0.4f } };
|
||||
RayCollision rCol = GetRayCollisionBox(ray, pBox);
|
||||
if (rCol.hit && rCol.distance < 4.5f) {
|
||||
PacketHeader hitH = { (uint8_t)PACKET_PLAYER_HIT, (uint32_t)sizeof(PacketPlayerHit) };
|
||||
PacketPlayerHit hitD = { rp.id, 1.0f, camera3D.position.x, camera3D.position.z };
|
||||
if (clientSocket != INVALID_SOCKET_VAL) {
|
||||
SendAll(clientSocket, (char*)&hitH, sizeof(hitH));
|
||||
SendAll(clientSocket, (char*)&hitD, sizeof(hitD));
|
||||
} else if (serverMode) {
|
||||
for (auto& s : clientSockets) {
|
||||
if (s == (Socket)rp.id) {
|
||||
SendAll(s, (char*)&hitH, sizeof(hitH));
|
||||
SendAll(s, (char*)&hitD, sizeof(hitD));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ray ray = GetMouseRay((Vector2){ (float)currentWidth / 2, (float)currentHeight / 2 }, camera3D);
|
||||
float closestDist = 8.0f;
|
||||
|
||||
|
|
@ -1868,8 +1944,8 @@ int main(void)
|
|||
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
|
||||
EndMode2D();
|
||||
|
||||
// Show Version Number (v2.1.1) in Red
|
||||
DrawTextEx(customFont, "v2.1.1", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||
// Show Version Number (v2.1.2) in Red
|
||||
DrawTextEx(customFont, "v2.1.2", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||
|
||||
// --- PLAYER NAME POPUP (IF MISSING) ---
|
||||
if (playerName == "") {
|
||||
|
|
@ -2602,16 +2678,6 @@ int main(void)
|
|||
camera3D.position.x, camera3D.position.y - 1.6f, camera3D.position.z),
|
||||
(Vector2){ 10, 80 }, 16, 1.0f, DARKGRAY);
|
||||
|
||||
// --- In-Game Clock ---
|
||||
int totalMinutes = (int)(timeOfDay * 24 * 60);
|
||||
int hours = (totalMinutes / 60) % 24;
|
||||
int minutes = totalMinutes % 60;
|
||||
const char* ampm = (hours >= 12) ? "PM" : "AM";
|
||||
int displayHours = hours % 12;
|
||||
if (displayHours == 0) displayHours = 12;
|
||||
|
||||
DrawTextEx(customFont, TextFormat("Time: %i:%02i %s", displayHours, minutes, ampm), (Vector2){ 10, 110 }, 18, 1.0f, (dayFactor > 0.5f) ? BLACK : WHITE);
|
||||
DrawTextEx(customFont, (dayFactor > 0.5f) ? "Status: Day" : "Status: Night", (Vector2){ 10, 130 }, 16, 1.0f, (dayFactor > 0.5f) ? (Color){180, 150, 0, 255} : (Color){100, 100, 255, 255});
|
||||
|
||||
// Draw Chat Log (Bottom Left, above inventory)
|
||||
int chatY = currentHeight - 120;
|
||||
|
|
@ -2640,6 +2706,21 @@ int main(void)
|
|||
int hotbarX = currentWidth / 2 - hotbarW / 2;
|
||||
int hotbarY = currentHeight - slotSize - 12;
|
||||
|
||||
// --- Health Hearts ---
|
||||
int heartX = currentWidth / 2 - 225;
|
||||
int heartY = hotbarY - 35;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Rectangle heartRect = { (float)heartX + i * 28, (float)heartY, 24, 24 };
|
||||
float h = playerHealth - (i * 2);
|
||||
if (h >= 2.0f) DrawRectangleRec(heartRect, RED); // Full heart
|
||||
else if (h >= 1.0f) {
|
||||
DrawRectangle((int)heartRect.x, (int)heartRect.y, 12, 24, RED); // Half heart
|
||||
DrawRectangleLinesEx(heartRect, 2.0f, DARKGRAY);
|
||||
} else {
|
||||
DrawRectangleLinesEx(heartRect, 2.0f, GRAY); // Empty heart
|
||||
}
|
||||
}
|
||||
|
||||
// Hotbar background panel
|
||||
DrawRectangle(hotbarX - 6, hotbarY - 6, hotbarW + 12, slotSize + 12,
|
||||
(Color){ 30, 30, 30, 180 });
|
||||
|
|
|
|||
|
|
@ -54,7 +54,14 @@ enum PacketType {
|
|||
PACKET_TIME_SYNC = 3,
|
||||
PACKET_SEED_SYNC = 4,
|
||||
PACKET_CHAT = 5,
|
||||
PACKET_DISCONNECT = 6
|
||||
PACKET_DISCONNECT = 6,
|
||||
PACKET_PLAYER_HIT = 7
|
||||
};
|
||||
|
||||
struct PacketPlayerHit {
|
||||
uint32_t targetID;
|
||||
float damage;
|
||||
float attackerX, attackerZ;
|
||||
};
|
||||
|
||||
struct PacketHeader {
|
||||
|
|
|
|||
Loading…
Reference in New Issue