From 453e9fd5e8e0fe99ada16090709f1a8c1b7dc14c Mon Sep 17 00:00:00 2001 From: Masashi Date: Thu, 18 Dec 2025 20:39:46 +0530 Subject: [PATCH] REAL GAM --- game/client | Bin 25488 -> 29920 bytes game/client.c | 353 +++++++++++++++++++++++++++++++++++--------------- game/server | Bin 30640 -> 35160 bytes game/server.c | 308 ++++++++++++++++++++++++------------------- 4 files changed, 421 insertions(+), 240 deletions(-) diff --git a/game/client b/game/client index 739731c1938e18f0956cfe6479f7ee32af701aa0..2164e961c07d9c3ec33b1bdec0bbc0c8aa77690a 100755 GIT binary patch literal 29920 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 literal 25488 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 diff --git a/game/client.c b/game/client.c index 4db9112..33a9dc0 100644 --- a/game/client.c +++ b/game/client.c @@ -1,3 +1,4 @@ +// game/client.c - SMOOTH TERRAIN VERSION #include "raylib.h" #include #include @@ -28,7 +29,6 @@ typedef int socklen_t; #define WEAPON_PISTOL 0 #define WEAPON_RIFLE 1 -// Item types #define ITEM_NONE 0 #define ITEM_MEDKIT 1 #define ITEM_AMMO_PISTOL 2 @@ -36,13 +36,226 @@ typedef int socklen_t; #define MAX_ITEMS 64 -// Input buttons #define BTN_RELOAD (1u << 0) #define BTN_SWITCH_PISTOL (1u << 1) #define BTN_SWITCH_RIFLE (1u << 2) #define BTN_PICK (1u << 3) #define BTN_USE_MEDKIT (1u << 4) +// ============== SIMPLEX NOISE ============== +static const uint8_t perm[512] = { + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, + 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, + 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, + 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, + 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, + 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, + 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, + 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, + 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, + 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, + 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, + 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, + 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, + 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, + 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, + 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, + 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, + 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, + 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, + 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, + 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, + 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, + 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, + 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, + 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, + 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, + 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, + 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, + 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, + 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, + 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, + 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, + 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, + 156, 180}; + +static float grad2(int hash, float x, float y) { + int h = hash & 7; + float u = h < 4 ? x : y; + float v = h < 4 ? y : x; + return ((h & 1) ? -u : u) + ((h & 2) ? -2.0f * v : 2.0f * v); +} + +static float simplex_noise_2d(float x, float y) { + const float F2 = 0.366025403f; + const float G2 = 0.211324865f; + float s = (x + y) * F2; + int i = (int)floorf(x + s); + int j = (int)floorf(y + s); + float t = (i + j) * G2; + float X0 = i - t, Y0 = j - t; + float x0 = x - X0, y0 = y - Y0; + int i1 = (x0 > y0) ? 1 : 0; + int j1 = (x0 > y0) ? 0 : 1; + float x1 = x0 - i1 + G2, y1 = y0 - j1 + G2; + float x2 = x0 - 1.0f + 2.0f * G2, y2 = y0 - 1.0f + 2.0f * G2; + int ii = i & 255, jj = j & 255; + float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f; + float t0 = 0.5f - x0 * x0 - y0 * y0; + if (t0 >= 0.0f) { + t0 *= t0; + n0 = t0 * t0 * grad2(perm[ii + perm[jj]], x0, y0); + } + float t1 = 0.5f - x1 * x1 - y1 * y1; + if (t1 >= 0.0f) { + t1 *= t1; + n1 = t1 * t1 * grad2(perm[ii + i1 + perm[jj + j1]], x1, y1); + } + float t2 = 0.5f - x2 * x2 - y2 * y2; + if (t2 >= 0.0f) { + t2 *= t2; + n2 = t2 * t2 * grad2(perm[ii + 1 + perm[jj + 1]], x2, y2); + } + return 45.0f * (n0 + n1 + n2); +} + +static float fbm_noise(float x, float y, int octaves) { + float value = 0.0f, amplitude = 1.0f, frequency = 1.0f, max_value = 0.0f; + for (int i = 0; i < octaves; i++) { + value += simplex_noise_2d(x * frequency, y * frequency) * amplitude; + max_value += amplitude; + amplitude *= 0.5f; + frequency *= 2.0f; + } + return value / max_value; +} + +float get_terrain_height(float x, float z) { + float height = fbm_noise(x * 0.03f, z * 0.03f, 4); + height += fbm_noise(x * 0.01f, z * 0.01f, 2) * 1.5f; + return height * 6.0f + 2.0f; +} + +#define TERRAIN_SIZE 64 +#define TERRAIN_SCALE 2.0f + +static Mesh generate_terrain_mesh(void) { + int size = TERRAIN_SIZE; + Mesh mesh = {0}; + + int vertexCount = size * size; + int triangleCount = (size - 1) * (size - 1) * 2; + + mesh.vertexCount = vertexCount; + mesh.triangleCount = triangleCount; + + mesh.vertices = (float *)MemAlloc(vertexCount * 3 * sizeof(float)); + mesh.texcoords = (float *)MemAlloc(vertexCount * 2 * sizeof(float)); + mesh.normals = (float *)MemAlloc(vertexCount * 3 * sizeof(float)); + mesh.indices = + (unsigned short *)MemAlloc(triangleCount * 3 * sizeof(unsigned short)); + + // Generate vertices + for (int z = 0; z < size; z++) { + for (int x = 0; x < size; x++) { + int idx = z * size + x; + float wx = ((float)x - size / 2.0f) * TERRAIN_SCALE; + float wz = ((float)z - size / 2.0f) * TERRAIN_SCALE; + float wy = get_terrain_height(wx, wz); + + mesh.vertices[idx * 3 + 0] = wx; + mesh.vertices[idx * 3 + 1] = wy; + mesh.vertices[idx * 3 + 2] = wz; + + mesh.texcoords[idx * 2 + 0] = (float)x / (float)(size - 1); + mesh.texcoords[idx * 2 + 1] = (float)z / (float)(size - 1); + } + } + + // Generate indices + int triIdx = 0; + for (int z = 0; z < size - 1; z++) { + for (int x = 0; x < size - 1; x++) { + int i0 = z * size + x; + int i1 = z * size + (x + 1); + int i2 = (z + 1) * size + x; + int i3 = (z + 1) * size + (x + 1); + + mesh.indices[triIdx * 3 + 0] = i0; + mesh.indices[triIdx * 3 + 1] = i2; + mesh.indices[triIdx * 3 + 2] = i1; + triIdx++; + + mesh.indices[triIdx * 3 + 0] = i1; + mesh.indices[triIdx * 3 + 1] = i2; + mesh.indices[triIdx * 3 + 2] = i3; + triIdx++; + } + } + + // Calculate normals + for (int i = 0; i < vertexCount; i++) { + mesh.normals[i * 3 + 0] = 0; + mesh.normals[i * 3 + 1] = 1; + mesh.normals[i * 3 + 2] = 0; + } + + for (int t = 0; t < triangleCount; t++) { + int i0 = mesh.indices[t * 3 + 0]; + int i1 = mesh.indices[t * 3 + 1]; + int i2 = mesh.indices[t * 3 + 2]; + + float v0x = mesh.vertices[i0 * 3 + 0]; + float v0y = mesh.vertices[i0 * 3 + 1]; + float v0z = mesh.vertices[i0 * 3 + 2]; + + float v1x = mesh.vertices[i1 * 3 + 0]; + float v1y = mesh.vertices[i1 * 3 + 1]; + float v1z = mesh.vertices[i1 * 3 + 2]; + + float v2x = mesh.vertices[i2 * 3 + 0]; + float v2y = mesh.vertices[i2 * 3 + 1]; + float v2z = mesh.vertices[i2 * 3 + 2]; + + float e1x = v1x - v0x, e1y = v1y - v0y, e1z = v1z - v0z; + float e2x = v2x - v0x, e2y = v2y - v0y, e2z = v2z - v0z; + + float nx = e1y * e2z - e1z * e2y; + float ny = e1z * e2x - e1x * e2z; + float nz = e1x * e2y - e1y * e2x; + + mesh.normals[i0 * 3 + 0] += nx; + mesh.normals[i0 * 3 + 1] += ny; + mesh.normals[i0 * 3 + 2] += nz; + + mesh.normals[i1 * 3 + 0] += nx; + mesh.normals[i1 * 3 + 1] += ny; + mesh.normals[i1 * 3 + 2] += nz; + + mesh.normals[i2 * 3 + 0] += nx; + mesh.normals[i2 * 3 + 1] += ny; + mesh.normals[i2 * 3 + 2] += nz; + } + + // Normalize normals + for (int i = 0; i < vertexCount; i++) { + float nx = mesh.normals[i * 3 + 0]; + float ny = mesh.normals[i * 3 + 1]; + float nz = mesh.normals[i * 3 + 2]; + float len = sqrtf(nx * nx + ny * ny + nz * nz); + if (len > 0.0001f) { + mesh.normals[i * 3 + 0] = nx / len; + mesh.normals[i * 3 + 1] = ny / len; + mesh.normals[i * 3 + 2] = nz / len; + } + } + + UploadMesh(&mesh, false); + return mesh; +} +// ============== END SIMPLEX NOISE ============== + static void set_nonblocking(int sock) { #ifdef _WIN32 u_long mode = 1; @@ -79,10 +292,7 @@ typedef struct MsgInput { uint8_t type; uint8_t playerId; uint32_t clientTick; - float moveX; - float moveZ; - float yaw; - float pitch; + float moveX, moveZ, yaw, pitch; uint8_t buttons; } MsgInput; @@ -93,19 +303,11 @@ typedef struct MsgShoot { } MsgShoot; typedef struct PlayerStateNet { - uint8_t id; - uint8_t alive; + uint8_t id, alive; int16_t hp; - float x, y, z; - float yaw, pitch; - + float x, y, z, yaw, pitch; uint8_t weapon; - int16_t pistolMag; - int16_t rifleMag; - int16_t pistolAmmo; - int16_t rifleAmmo; - int16_t medkits; - + int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits; char username[USERNAME_MAX]; } PlayerStateNet; @@ -132,19 +334,11 @@ typedef struct MsgItems { #pragma pack(pop) typedef struct RemotePlayer { - int present; - int alive; - int hp; + int present, alive, hp; Vector3 pos; float yaw, pitch; - uint8_t weapon; - int16_t pistolMag; - int16_t rifleMag; - int16_t pistolAmmo; - int16_t rifleAmmo; - int16_t medkits; - + int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits; char username[USERNAME_MAX]; } RemotePlayer; @@ -172,14 +366,11 @@ static float v3_dot(Vector3 a, Vector3 b) { static float v3_len(Vector3 a) { return sqrtf(a.x * a.x + a.y * a.y + a.z * a.z); } -static Vector3 v3_scale(Vector3 a, float s) { - return v3(a.x * s, a.y * s, a.z * s); -} static Vector3 v3_norm(Vector3 a) { float l = v3_len(a); if (l <= 1e-6f) return v3(0, 0, 1); - return v3_scale(a, 1.0f / l); + return v3(a.x / l, a.y / l, a.z / l); } static const char *ItemName(uint8_t t) { @@ -235,7 +426,7 @@ int main(void) { #endif const int sw = 1280, sh = 720; - InitWindow(sw, sh, "Voxel Shooter - Client"); + InitWindow(sw, sh, "Voxel Shooter - Client (SMOOTH TERRAIN)"); SetTargetFPS(120); char myName[USERNAME_MAX]; @@ -243,6 +434,12 @@ int main(void) { DisableCursor(); + // Generate terrain mesh + Mesh terrainMesh = generate_terrain_mesh(); + Model terrainModel = LoadModelFromMesh(terrainMesh); + terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = + (Color){80, 140, 70, 255}; + int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { perror("socket"); @@ -261,33 +458,20 @@ int main(void) { RemotePlayer rp[MAX_PLAYERS] = {0}; WorldItem wi[MAX_ITEMS] = {0}; - Vector3 camPos = v3(0, 1.7f, 6); - float yaw = 0.0f; - float pitch = 0.0f; - - float recoilYaw = 0.0f, recoilPitch = 0.0f; - float crossSpread = 0.0f; + Vector3 camPos = v3(0, 5.0f, 6); + float yaw = 0.0f, pitch = 0.0f; + float recoilYaw = 0.0f, recoilPitch = 0.0f, crossSpread = 0.0f; float fireCooldown = 0.0f; int shotsInBurst = 0; float burstResetTimer = 0.0f; - const float pistolFireRate = 4.0f; - const float rifleFireRate = 12.0f; - const float recoilReturn = 18.0f; - const float crossReturn = 14.0f; - - const float pistolKickPitch = 0.010f; - const float pistolKickYaw = 0.004f; - const float rifleKickPitch = 0.018f; - const float rifleKickYaw = 0.010f; - - const float pistolCrossKick = 2.0f; - const float rifleCrossKick = 4.0f; - - const float pistolSprayGrow = 0.4f; - const float rifleSprayGrow = 1.2f; - + const float pistolFireRate = 4.0f, rifleFireRate = 12.0f; + const float recoilReturn = 18.0f, crossReturn = 14.0f; + const float pistolKickPitch = 0.010f, pistolKickYaw = 0.004f; + const float rifleKickPitch = 0.018f, rifleKickYaw = 0.010f; + const float pistolCrossKick = 2.0f, rifleCrossKick = 4.0f; + const float pistolSprayGrow = 0.4f, rifleSprayGrow = 1.2f; const float burstResetTime = 0.18f; MsgHello hello = {0}; @@ -320,7 +504,6 @@ int main(void) { crossSpread = 0.0f; } - // Receive packets for (;;) { uint8_t buf[1400]; struct sockaddr_in from = {0}; @@ -365,7 +548,6 @@ int main(void) { p->pistolAmmo = ps->pistolAmmo; p->rifleAmmo = ps->rifleAmmo; p->medkits = ps->medkits; - memset(p->username, 0, USERNAME_MAX); strncpy(p->username, ps->username, USERNAME_MAX - 1); } @@ -390,7 +572,6 @@ int main(void) { } } - // Mouse look Vector2 md = GetMouseDelta(); const float sens = 0.0025f; yaw -= md.x * sens; @@ -407,7 +588,6 @@ int main(void) { if (viewPitch > 1.5f) viewPitch = 1.5f; - // Movement float moveX = 0.0f, moveZ = 0.0f; if (IsKeyDown(KEY_A)) moveX += 1.0f; @@ -418,9 +598,7 @@ int main(void) { if (IsKeyDown(KEY_S)) moveZ -= 1.0f; - // Input buttons uint8_t buttons = 0; - if (IsKeyPressed(KEY_ONE)) buttons |= BTN_SWITCH_PISTOL; if (IsKeyPressed(KEY_TWO)) @@ -432,7 +610,6 @@ int main(void) { if (IsKeyPressed(KEY_H)) buttons |= BTN_USE_MEDKIT; - // Send INPUT if (myId != 255) { MsgInput in = {0}; in.type = MSG_INPUT; @@ -447,13 +624,11 @@ int main(void) { (const struct sockaddr *)&srv, sizeof(srv)); } - // Shoot if (myId != 255 && IsMouseButtonDown(MOUSE_BUTTON_LEFT) && fireCooldown <= 0.0f && rp[myId].present) { int wpn = rp[myId].weapon; float rate = (wpn == WEAPON_PISTOL) ? pistolFireRate : rifleFireRate; - float interval = 1.0f / rate; - fireCooldown = interval; + fireCooldown = 1.0f / rate; shotsInBurst++; burstResetTimer = burstResetTime; @@ -478,7 +653,6 @@ int main(void) { (const struct sockaddr *)&srv, sizeof(srv)); } - // Camera Vector3 forward = v3(sinf(viewYaw) * cosf(viewPitch), sinf(viewPitch), cosf(viewYaw) * cosf(viewPitch)); @@ -490,23 +664,12 @@ int main(void) { cam.projection = CAMERA_PERSPECTIVE; BeginDrawing(); - ClearBackground((Color){20, 24, 32, 255}); + ClearBackground((Color){135, 206, 235, 255}); BeginMode3D(cam); - // Ground - for (int z = -16; z <= 16; z++) { - for (int x = -16; x <= 16; x++) { - DrawCubeV(v3((float)x, 0.0f, (float)z), v3(1, 1, 1), - (Color){50, 60, 75, 255}); - } - } - - // Blocks - DrawCubeV(v3(2, 1, 2), v3(1, 1, 1), (Color){120, 90, 60, 255}); - DrawCubeV(v3(2, 2, 2), v3(1, 1, 1), (Color){120, 90, 60, 255}); - DrawCubeV(v3(-3, 1, -1), v3(1, 1, 1), (Color){90, 120, 60, 255}); - DrawCubeV(v3(0, 1, 4), v3(2, 1, 2), (Color){90, 90, 130, 255}); + // Draw smooth terrain + DrawModel(terrainModel, (Vector3){0, 0, 0}, 1.0f, WHITE); // Items for (int i = 0; i < MAX_ITEMS; i++) { @@ -519,8 +682,7 @@ int main(void) { ic = (Color){255, 220, 120, 255}; if (wi[i].type == ITEM_AMMO_RIFLE) ic = (Color){255, 180, 120, 255}; - DrawCubeV(v3(wi[i].pos.x, wi[i].pos.y + 0.15f, wi[i].pos.z), - v3(0.3f, 0.3f, 0.3f), ic); + DrawSphere(wi[i].pos, 0.3f, ic); } // Players @@ -540,21 +702,16 @@ int main(void) { // Nameplates for (int i = 0; i < MAX_PLAYERS; i++) { - if (!rp[i].present) + if (!rp[i].present || !rp[i].username[0]) continue; - if (!rp[i].username[0]) - continue; - Vector3 head = v3(rp[i].pos.x, rp[i].pos.y + 1.2f, rp[i].pos.z); Vector3 camForward = v3_norm(v3_sub(cam.target, cam.position)); Vector3 toHead = v3_sub(head, cam.position); if (v3_dot(camForward, toHead) <= 0.0f) continue; - Vector2 s = GetWorldToScreen(head, cam); if (s.x < -200 || s.x > sw + 200 || s.y < -200 || s.y > sh + 200) continue; - int fontSize = 18; int w = MeasureText(rp[i].username, fontSize); Color tc = (i == myId) ? (Color){180, 230, 255, 255} : RAYWHITE; @@ -566,9 +723,7 @@ int main(void) { if (myId == 255 || !rp[myId].present) { DrawText("Connecting...", 10, 10, 20, RAYWHITE); } else { - // Top left - Health and Medkits DrawRectangle(10, 10, 280, 110, (Color){0, 0, 0, 120}); - DrawText("HP", 20, 20, 20, RAYWHITE); int healthBarWidth = (rp[myId].hp * 220) / 100; Color healthColor = (rp[myId].hp > 60) ? (Color){80, 255, 80, 120} @@ -577,22 +732,18 @@ int main(void) { DrawRectangle(20, 45, 220, 20, (Color){40, 40, 40, 255}); DrawRectangle(20, 45, healthBarWidth, 20, healthColor); DrawText(TextFormat("%d", rp[myId].hp), 250, 47, 18, RAYWHITE); - DrawText(TextFormat("Medkits: %d (H to use)", rp[myId].medkits), 20, 75, 18, (rp[myId].medkits > 0) ? (Color){120, 255, 120, 120} : (Color){120, 120, 120, 120}); - DrawText("1=Pistol 2=Rifle R=Reload F=Pick", 20, 95, 14, (Color){150, 150, 150, 150}); - // Bottom right - Weapon and Ammo const char *weaponName = (rp[myId].weapon == WEAPON_PISTOL) ? "PISTOL" : "RIFLE"; Color weaponColor = (rp[myId].weapon == WEAPON_PISTOL) ? (Color){100, 200, 255, 255} : (Color){255, 150, 100, 255}; - int currentMag = (rp[myId].weapon == WEAPON_PISTOL) ? rp[myId].pistolMag : rp[myId].rifleMag; int reserveAmmo = (rp[myId].weapon == WEAPON_PISTOL) ? rp[myId].pistolAmmo @@ -600,18 +751,11 @@ int main(void) { DrawRectangle(sw - 260, sh - 120, 250, 110, (Color){0, 0, 0, 180}); DrawText(weaponName, sw - 250, sh - 110, 28, weaponColor); - - char magText[16]; - snprintf(magText, sizeof(magText), "%d", currentMag); - DrawText(magText, sw - 250, sh - 75, 40, RAYWHITE); - - char reserveText[32]; - snprintf(reserveText, sizeof(reserveText), "/ %d", reserveAmmo); - DrawText(reserveText, sw - 140, sh - 65, 24, (Color){180, 180, 180, 255}); - - if (currentMag == 0) { + DrawText(TextFormat("%d", currentMag), sw - 250, sh - 75, 40, RAYWHITE); + DrawText(TextFormat("/ %d", reserveAmmo), sw - 140, sh - 65, 24, + (Color){180, 180, 180, 255}); + if (currentMag == 0) DrawText("RELOAD!", sw - 250, sh - 30, 20, (Color){255, 80, 80, 255}); - } } DrawFPS(sw - 90, 10); @@ -632,6 +776,7 @@ int main(void) { EndDrawing(); } + UnloadModel(terrainModel); CloseWindow(); CLOSESOCK(sock); #ifdef _WIN32 diff --git a/game/server b/game/server index 4bb23844d50566ad20ba94aa98d1ca430c1adc1d..80cf392ba1f08227f5e82b9cf734f1647e34db6a 100755 GIT binary patch literal 35160 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 literal 30640 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 diff --git a/game/server.c b/game/server.c index 4869de1..cbc6aeb 100644 --- a/game/server.c +++ b/game/server.c @@ -1,3 +1,4 @@ +// game/server.c - SMOOTH TERRAIN VERSION #include #include #include @@ -50,6 +51,122 @@ typedef int socklen_t; #define CAT_PLAYER (1u << 1) #define CAT_ITEM (1u << 2) +// ============== SIMPLEX NOISE ============== +static const uint8_t perm[512] = { + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, + 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, + 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, + 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, + 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, + 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, + 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, + 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, + 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, + 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, + 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, + 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, + 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, + 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, + 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, + 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, + 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, + 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, + 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, + 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, + 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, + 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, + 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, + 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, + 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, + 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, + 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, + 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, + 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, + 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, + 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, + 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, + 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, + 156, 180}; + +static float grad2(int hash, float x, float y) { + int h = hash & 7; + float u = h < 4 ? x : y; + float v = h < 4 ? y : x; + return ((h & 1) ? -u : u) + ((h & 2) ? -2.0f * v : 2.0f * v); +} + +static float simplex_noise_2d(float x, float y) { + const float F2 = 0.366025403f; + const float G2 = 0.211324865f; + float s = (x + y) * F2; + int i = (int)floorf(x + s); + int j = (int)floorf(y + s); + float t = (i + j) * G2; + float X0 = i - t, Y0 = j - t; + float x0 = x - X0, y0 = y - Y0; + int i1 = (x0 > y0) ? 1 : 0; + int j1 = (x0 > y0) ? 0 : 1; + float x1 = x0 - i1 + G2, y1 = y0 - j1 + G2; + float x2 = x0 - 1.0f + 2.0f * G2, y2 = y0 - 1.0f + 2.0f * G2; + int ii = i & 255, jj = j & 255; + float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f; + float t0 = 0.5f - x0 * x0 - y0 * y0; + if (t0 >= 0.0f) { + t0 *= t0; + n0 = t0 * t0 * grad2(perm[ii + perm[jj]], x0, y0); + } + float t1 = 0.5f - x1 * x1 - y1 * y1; + if (t1 >= 0.0f) { + t1 *= t1; + n1 = t1 * t1 * grad2(perm[ii + i1 + perm[jj + j1]], x1, y1); + } + float t2 = 0.5f - x2 * x2 - y2 * y2; + if (t2 >= 0.0f) { + t2 *= t2; + n2 = t2 * t2 * grad2(perm[ii + 1 + perm[jj + 1]], x2, y2); + } + return 45.0f * (n0 + n1 + n2); +} + +static float fbm_noise(float x, float y, int octaves) { + float value = 0.0f, amplitude = 1.0f, frequency = 1.0f, max_value = 0.0f; + for (int i = 0; i < octaves; i++) { + value += simplex_noise_2d(x * frequency, y * frequency) * amplitude; + max_value += amplitude; + amplitude *= 0.5f; + frequency *= 2.0f; + } + return value / max_value; +} + +float get_terrain_height(float x, float z) { + float height = fbm_noise(x * 0.03f, z * 0.03f, 4); + height += fbm_noise(x * 0.01f, z * 0.01f, 2) * 1.5f; + return height * 6.0f + 2.0f; +} + +#define TERRAIN_SIZE 64 +#define TERRAIN_SCALE 2.0f +static dReal *gHeightData = NULL; +static dGeomID gTerrainGeom = NULL; + +static void generate_smooth_terrain(void) { + int size = TERRAIN_SIZE; + gHeightData = (dReal *)malloc(size * size * sizeof(dReal)); + + for (int z = 0; z < size; z++) { + for (int x = 0; x < size; x++) { + float wx = ((float)x - size / 2.0f) * TERRAIN_SCALE; + float wz = ((float)z - size / 2.0f) * TERRAIN_SCALE; + gHeightData[z * size + x] = (dReal)get_terrain_height(wx, wz); + } + } + + printf("Generated smooth heightmap terrain %dx%d\n", size, size); +} +// ============== END SIMPLEX NOISE ============== + static double now_seconds(void) { #ifdef _WIN32 static LARGE_INTEGER freq; @@ -104,10 +221,7 @@ typedef struct MsgInput { uint8_t type; uint8_t playerId; uint32_t clientTick; - float moveX; - float moveZ; - float yaw; - float pitch; + float moveX, moveZ, yaw, pitch; uint8_t buttons; } MsgInput; @@ -118,19 +232,11 @@ typedef struct MsgShoot { } MsgShoot; typedef struct PlayerStateNet { - uint8_t id; - uint8_t alive; + uint8_t id, alive; int16_t hp; - float x, y, z; - float yaw, pitch; - + float x, y, z, yaw, pitch; uint8_t weapon; - int16_t pistolMag; - int16_t rifleMag; - int16_t pistolAmmo; - int16_t rifleAmmo; - int16_t medkits; - + int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits; char username[USERNAME_MAX]; } PlayerStateNet; @@ -169,26 +275,13 @@ typedef struct Player { int inUse; struct sockaddr_in addr; double lastHeard; - char username[USERNAME_MAX]; - - float moveX, moveZ; - float yaw, pitch; - - int alive; - int hp; - - int weapon; - int pistolMag; - int rifleMag; - int pistolAmmo; - int rifleAmmo; - int medkits; - + float moveX, moveZ, yaw, pitch; + int alive, hp, weapon; + int pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits; double lastShotTime; int shotsInBurst; uint32_t rng; - dBodyID body; dGeomID geom; } Player; @@ -200,11 +293,7 @@ static int addr_equal(const struct sockaddr_in *a, } static float clampf(float v, float lo, float hi) { - if (v < lo) - return lo; - if (v > hi) - return hi; - return v; + return (v < lo) ? lo : (v > hi) ? hi : v; } static void sendto_player(int sock, const Player *pl, const void *data, @@ -212,6 +301,7 @@ static void sendto_player(int sock, const Player *pl, const void *data, sendto(sock, (const char *)data, len, 0, (const struct sockaddr *)&pl->addr, sizeof(pl->addr)); } + static void broadcast(int sock, const Player players[MAX_PLAYERS], const void *data, int len) { for (int i = 0; i < MAX_PLAYERS; i++) @@ -222,7 +312,6 @@ static void broadcast(int sock, const Player players[MAX_PLAYERS], static dWorldID gWorld; static dSpaceID gSpace; static dJointGroupID gContactGroup; - static Item gItems[MAX_ITEMS]; static uint16_t gNextItemId = 1; @@ -234,7 +323,8 @@ static void item_destroy(Item *it) { memset(it, 0, sizeof(*it)); } -static int item_spawn(uint8_t type, int qty, float x, float y, float z) { +static int item_spawn(uint8_t type, int qty, float x, float z) { + float y = get_terrain_height(x, z) + 0.5f; for (int i = 0; i < MAX_ITEMS; i++) { if (!gItems[i].active) { Item *it = &gItems[i]; @@ -245,14 +335,11 @@ static int item_spawn(uint8_t type, int qty, float x, float y, float z) { it->x = x; it->y = y; it->z = z; - it->geom = dCreateSphere(gSpace, 0.30); - dGeomSetPosition(it->geom, x, y + 0.3f, z); - + dGeomSetPosition(it->geom, x, y, z); dGeomSetCategoryBits(it->geom, CAT_ITEM); dGeomSetCollideBits(it->geom, 0); dGeomSetData(it->geom, it); - return i; } } @@ -261,12 +348,10 @@ static int item_spawn(uint8_t type, int qty, float x, float y, float z) { static void nearCallback(void *user, dGeomID o1, dGeomID o2) { (void)user; - if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) { dSpaceCollide2(o1, o2, user, &nearCallback); return; } - unsigned long c1 = dGeomGetCategoryBits(o1); unsigned long c2 = dGeomGetCategoryBits(o2); if ((c1 & CAT_ITEM) || (c2 & CAT_ITEM)) @@ -275,19 +360,12 @@ static void nearCallback(void *user, dGeomID o1, dGeomID o2) { const int MAXC = 8; dContact c[MAXC]; int n = dCollide(o1, o2, MAXC, &c[0].geom, sizeof(dContact)); - if (n <= 0) - return; - for (int i = 0; i < n; i++) { c[i].surface.mode = dContactApprox1; - c[i].surface.mu = 1.0; + c[i].surface.mu = 1.5; c[i].surface.bounce = 0.0; - c[i].surface.bounce_vel = 0.0; - dJointID j = dJointCreateContact(gWorld, gContactGroup, &c[i]); - dBodyID b1 = dGeomGetBody(o1); - dBodyID b2 = dGeomGetBody(o2); - dJointAttach(j, b1, b2); + dJointAttach(j, dGeomGetBody(o1), dGeomGetBody(o2)); } } @@ -296,38 +374,35 @@ static void ode_init_world(void) { gWorld = dWorldCreate(); gSpace = dHashSpaceCreate(0); gContactGroup = dJointGroupCreate(0); - - dWorldSetGravity(gWorld, 0, -9.81, 0); + dWorldSetGravity(gWorld, 0, -20.0, 0); dWorldSetERP(gWorld, 0.2); dWorldSetCFM(gWorld, 1e-5); - dGeomID ground = dCreatePlane(gSpace, 0, 1, 0, 0); - dGeomSetCategoryBits(ground, CAT_WORLD); - dGeomSetCollideBits(ground, CAT_PLAYER); + generate_smooth_terrain(); - const struct { - float x, y, z, sx, sy, sz; - } blocks[] = { - {2, 0.5f, 2, 1, 1, 1}, - {2, 1.5f, 2, 1, 1, 1}, - {-3, 0.5f, -1, 1, 1, 1}, - {0, 0.5f, 4, 2, 1, 2}, - }; - for (int i = 0; i < (int)(sizeof(blocks) / sizeof(blocks[0])); i++) { - dGeomID b = dCreateBox(gSpace, blocks[i].sx, blocks[i].sy, blocks[i].sz); - dGeomSetPosition(b, blocks[i].x, blocks[i].y, blocks[i].z); - dGeomSetCategoryBits(b, CAT_WORLD); - dGeomSetCollideBits(b, CAT_PLAYER); - } + dHeightfieldDataID hfData = dGeomHeightfieldDataCreate(); + dGeomHeightfieldDataBuildDouble(hfData, gHeightData, 0, + TERRAIN_SCALE * TERRAIN_SIZE, + TERRAIN_SCALE * TERRAIN_SIZE, TERRAIN_SIZE, + TERRAIN_SIZE, 1.0, 0.0, 0.0, 0); + dGeomHeightfieldDataSetBounds(hfData, -10.0, 20.0); - // Spawn test items - item_spawn(ITEM_MEDKIT, 1, 1.0f, 0.0f, 1.0f); - item_spawn(ITEM_MEDKIT, 1, -2.0f, 0.0f, 3.0f); - item_spawn(ITEM_AMMO_RIFLE, 30, 0.0f, 0.0f, 2.0f); - item_spawn(ITEM_AMMO_PISTOL, 12, -1.0f, 0.0f, -2.0f); + gTerrainGeom = dCreateHeightfield(gSpace, hfData, 1); + dGeomSetPosition(gTerrainGeom, 0, 0, 0); + dGeomSetCategoryBits(gTerrainGeom, CAT_WORLD); + dGeomSetCollideBits(gTerrainGeom, CAT_PLAYER); + + item_spawn(ITEM_MEDKIT, 1, 10.0f, 10.0f); + item_spawn(ITEM_MEDKIT, 1, -15.0f, 20.0f); + item_spawn(ITEM_AMMO_RIFLE, 30, 5.0f, -10.0f); + item_spawn(ITEM_AMMO_PISTOL, 12, -10.0f, -15.0f); } static void ode_shutdown_world(void) { + if (gHeightData) { + free(gHeightData); + gHeightData = NULL; + } dJointGroupDestroy(gContactGroup); dSpaceDestroy(gSpace); dWorldDestroy(gWorld); @@ -335,24 +410,20 @@ static void ode_shutdown_world(void) { } static void player_ode_create(Player *p, int spawnIndex) { - const dReal radius = 0.35; - const dReal length = 1.0; - p->body = dBodyCreate(gWorld); - dMass m; - dMassSetCapsuleTotal(&m, 80.0, 3, radius, length); + dMassSetCapsuleTotal(&m, 80.0, 3, 0.35, 1.0); dBodySetMass(p->body, &m); - dBodySetLinearDamping(p->body, 0.05); dBodySetAngularDamping(p->body, 0.99); - dBodySetPosition(p->body, (dReal)(spawnIndex * 2), 2.0, 0.0); - dBodySetAngularVel(p->body, 0, 0, 0); + float sx = (float)(spawnIndex * 4 - 8); + float sz = (float)(spawnIndex * 3 - 6); + float sy = get_terrain_height(sx, sz) + 3.0f; + dBodySetPosition(p->body, sx, sy, sz); - p->geom = dCreateCapsule(gSpace, radius, length); + p->geom = dCreateCapsule(gSpace, 0.35, 1.0); dGeomSetBody(p->geom, p->body); - dGeomSetCategoryBits(p->geom, CAT_PLAYER); dGeomSetCollideBits(p->geom, CAT_WORLD | CAT_PLAYER); } @@ -369,8 +440,7 @@ static void player_ode_destroy(Player *p) { } typedef struct RaycastCtx { - dGeomID ray; - dGeomID hitGeom; + dGeomID ray, hitGeom; dContactGeom hit; int hasHit; } RaycastCtx; @@ -381,7 +451,6 @@ static void rayCallback(void *data, dGeomID o1, dGeomID o2) { dGeomID other = (o1 == ray) ? o2 : o1; if (other == ray) return; - dContact c[4]; int n = dCollide(ray, other, 4, &c[0].geom, sizeof(dContact)); for (int i = 0; i < n; i++) { @@ -400,12 +469,10 @@ static int raycast_space(float ox, float oy, float oz, float dx, float dy, dGeomRaySet(ray, ox, oy, oz, dx, dy, dz); dGeomSetCategoryBits(ray, 0xFFFFFFFFu); dGeomSetCollideBits(ray, CAT_WORLD | CAT_PLAYER); - RaycastCtx rc = {0}; rc.ray = ray; dSpaceCollide2(ray, (dGeomID)gSpace, &rc, &rayCallback); dGeomDestroy(ray); - if (!rc.hasHit) return 0; if (outHit) @@ -423,21 +490,25 @@ static uint32_t xorshift32(uint32_t *s) { *s = x; return x; } + static float frand_signed(uint32_t *s) { return ((xorshift32(s) & 0xFFFFFF) / (float)0x7FFFFF) - 1.0f; } + static void dir_from_yaw_pitch(float yaw, float pitch, float *dx, float *dy, float *dz) { *dx = sinf(yaw) * cosf(pitch); *dy = sinf(pitch); *dz = cosf(yaw) * cosf(pitch); } + static void v3_cross(float ax, float ay, float az, float bx, float by, float bz, float *ox, float *oy, float *oz) { *ox = ay * bz - az * by; *oy = az * bx - ax * bz; *oz = ax * by - ay * bx; } + static void v3_normf(float *x, float *y, float *z) { float l = sqrtf((*x) * (*x) + (*y) * (*y) + (*z) * (*z)); if (l < 1e-6f) { @@ -458,7 +529,7 @@ static int find_nearest_item(float x, float y, float z, float maxDist) { if (!gItems[i].active) continue; float dx = gItems[i].x - x; - float dy = (gItems[i].y + 0.3f) - y; + float dy = (gItems[i].y) - y; float dz = gItems[i].z - z; float d2 = dx * dx + dy * dy + dz * dz; if (d2 < bestD2) { @@ -496,19 +567,16 @@ int main(void) { } Player players[MAX_PLAYERS] = {0}; - uint32_t serverTick = 0; - uint32_t itemsTickDiv = 0; + uint32_t serverTick = 0, itemsTickDiv = 0; printf("Server listening UDP %d\n", SERVER_PORT); double nextTick = now_seconds(); for (;;) { - // Receive for (;;) { uint8_t buf[1400]; struct sockaddr_in from = {0}; socklen_t fromLen = sizeof(from); - int n = (int)recvfrom(sock, (char *)buf, (int)sizeof(buf), 0, (struct sockaddr *)&from, &fromLen); if (n <= 0) { @@ -523,7 +591,6 @@ int main(void) { } uint8_t type = buf[0]; - int pid = -1; for (int i = 0; i < MAX_PLAYERS; i++) { if (players[i].inUse && addr_equal(&players[i].addr, &from)) { @@ -547,21 +614,15 @@ int main(void) { p->addr = from; p->lastHeard = now_seconds(); strncpy(p->username, m->username, USERNAME_MAX - 1); - p->alive = 1; p->hp = 100; - p->weapon = WEAPON_RIFLE; p->pistolMag = 12; p->rifleMag = 30; p->pistolAmmo = 48; p->rifleAmmo = 90; p->medkits = 2; - - p->lastShotTime = 0.0; - p->shotsInBurst = 0; p->rng = 0xA341316Cu ^ (uint32_t)(i * 2654435761u); - player_ode_create(p, i); printf("Player %d joined as '%s'\n", pid, p->username); break; @@ -583,19 +644,16 @@ int main(void) { if (pid >= 0 && m->playerId == (uint8_t)pid) { Player *p = &players[pid]; p->lastHeard = now_seconds(); - p->moveX = clampf(m->moveX, -1.0f, 1.0f); p->moveZ = clampf(m->moveZ, -1.0f, 1.0f); p->yaw = m->yaw; p->pitch = clampf(m->pitch, -1.5f, 1.5f); - // Weapon switch if (m->buttons & BTN_SWITCH_PISTOL) p->weapon = WEAPON_PISTOL; if (m->buttons & BTN_SWITCH_RIFLE) p->weapon = WEAPON_RIFLE; - // Reload if (m->buttons & BTN_RELOAD) { if (p->weapon == WEAPON_PISTOL) { int need = 12 - p->pistolMag; @@ -614,15 +672,13 @@ int main(void) { } } - // Pick item if (m->buttons & BTN_PICK) { const dReal *pos = dBodyGetPosition(p->body); - int itIdx = find_nearest_item((float)pos[0], (float)pos[1] + 0.7f, - (float)pos[2], 1.6f); + int itIdx = find_nearest_item((float)pos[0], (float)pos[1], + (float)pos[2], 2.0f); if (itIdx >= 0) { Item *it = &gItems[itIdx]; int taken = 0; - if (it->type == ITEM_AMMO_PISTOL) { p->pistolAmmo += it->qty; if (p->pistolAmmo > 120) @@ -639,13 +695,11 @@ int main(void) { p->medkits = 10; taken = 1; } - if (taken) item_destroy(it); } } - // Use medkit if (m->buttons & BTN_USE_MEDKIT) { if (p->medkits > 0 && p->hp < 100) { p->medkits--; @@ -661,7 +715,6 @@ int main(void) { players[pid].alive) { Player *sh = &players[pid]; const double now = now_seconds(); - double fireInterval = (sh->weapon == WEAPON_PISTOL) ? (1.0 / 4.0) : (1.0 / 12.0); if (now - sh->lastShotTime < fireInterval) @@ -679,10 +732,8 @@ int main(void) { sh->lastShotTime = now; const dReal *pos = dBodyGetPosition(sh->body); - float ox = (float)pos[0]; - float oy = (float)pos[1] + 0.7f; - float oz = (float)pos[2]; - + float ox = (float)pos[0], oy = (float)pos[1] + 0.7f, + oz = (float)pos[2]; float fx, fy, fz; dir_from_yaw_pitch(sh->yaw, sh->pitch, &fx, &fy, &fz); v3_normf(&fx, &fy, &fz); @@ -703,7 +754,6 @@ int main(void) { float ax = frand_signed(&sh->rng) * spread; float ay = frand_signed(&sh->rng) * spread; - float dx = fx + rx * ax + ux * ay; float dy = fy + ry * ax + uy * ay; float dz = fz + rz * ax + uz * ay; @@ -711,7 +761,7 @@ int main(void) { dContactGeom hit; dGeomID hitGeom = 0; - if (raycast_space(ox, oy, oz, dx, dy, dz, 80.0f, &hit, &hitGeom)) { + if (raycast_space(ox, oy, oz, dx, dy, dz, 100.0f, &hit, &hitGeom)) { int victim = -1; for (int i = 0; i < MAX_PLAYERS; i++) { if (!players[i].inUse) @@ -721,7 +771,6 @@ int main(void) { break; } } - if (victim >= 0 && victim != pid && players[victim].alive) { int dmg = 25; players[victim].hp -= dmg; @@ -735,7 +784,6 @@ int main(void) { } } - // Tick double t = now_seconds(); if (t >= nextTick) { nextTick += DT; @@ -753,23 +801,20 @@ int main(void) { const dReal *lvel = dBodyGetLinearVel(p->body); const dReal *avel = dBodyGetAngularVel(p->body); - float dx = p->moveX; - float dz = p->moveZ; + float dx = p->moveX, dz = p->moveZ; float len = sqrtf(dx * dx + dz * dz); if (len > 1e-6f) { dx /= len; dz /= len; } - float speed = 5.0f; + float speed = 6.0f; float fx = sinf(p->yaw) * dz - cosf(p->yaw) * dx; float fz = cosf(p->yaw) * dz + sinf(p->yaw) * dx; - dBodySetLinearVel(p->body, fx * speed, lvel[1], fz * speed); dBodySetAngularVel(p->body, avel[0], 0, avel[2]); } - // Send snapshot MsgSnapshot snap = {0}; snap.type = MSG_SNAPSHOT; snap.serverTick = serverTick; @@ -778,9 +823,7 @@ int main(void) { for (int i = 0; i < MAX_PLAYERS; i++) { if (!players[i].inUse) continue; - const dReal *pos = dBodyGetPosition(players[i].body); - PlayerStateNet *ps = &snap.p[snap.count++]; ps->id = (uint8_t)i; ps->alive = players[i].alive; @@ -798,19 +841,15 @@ int main(void) { ps->medkits = (int16_t)players[i].medkits; strncpy(ps->username, players[i].username, USERNAME_MAX - 1); } - broadcast(sock, players, &snap, (int)sizeof(snap)); - // Send items periodically itemsTickDiv++; if (itemsTickDiv >= 6) { itemsTickDiv = 0; - MsgItems im = {0}; im.type = MSG_ITEMS; im.serverTick = serverTick; im.count = 0; - for (int i = 0; i < MAX_ITEMS; i++) { if (!gItems[i].active) continue; @@ -822,12 +861,10 @@ int main(void) { it->y = gItems[i].y; it->z = gItems[i].z; } - broadcast(sock, players, &im, (int)sizeof(im)); } } - // Timeout check double now = now_seconds(); for (int i = 0; i < MAX_PLAYERS; i++) { if (players[i].inUse && (now - players[i].lastHeard) > 10.0) { @@ -837,7 +874,6 @@ int main(void) { } } - // Sleep double wait = nextTick - now_seconds(); if (wait > 0.0) { #ifdef _WIN32