REAL GAM
This commit is contained in:
parent
83334091ba
commit
453e9fd5e8
4 changed files with 422 additions and 241 deletions
BIN
game/client
BIN
game/client
Binary file not shown.
353
game/client.c
353
game/client.c
|
|
@ -1,3 +1,4 @@
|
||||||
|
// game/client.c - SMOOTH TERRAIN VERSION
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
@ -28,7 +29,6 @@ typedef int socklen_t;
|
||||||
#define WEAPON_PISTOL 0
|
#define WEAPON_PISTOL 0
|
||||||
#define WEAPON_RIFLE 1
|
#define WEAPON_RIFLE 1
|
||||||
|
|
||||||
// Item types
|
|
||||||
#define ITEM_NONE 0
|
#define ITEM_NONE 0
|
||||||
#define ITEM_MEDKIT 1
|
#define ITEM_MEDKIT 1
|
||||||
#define ITEM_AMMO_PISTOL 2
|
#define ITEM_AMMO_PISTOL 2
|
||||||
|
|
@ -36,13 +36,226 @@ typedef int socklen_t;
|
||||||
|
|
||||||
#define MAX_ITEMS 64
|
#define MAX_ITEMS 64
|
||||||
|
|
||||||
// Input buttons
|
|
||||||
#define BTN_RELOAD (1u << 0)
|
#define BTN_RELOAD (1u << 0)
|
||||||
#define BTN_SWITCH_PISTOL (1u << 1)
|
#define BTN_SWITCH_PISTOL (1u << 1)
|
||||||
#define BTN_SWITCH_RIFLE (1u << 2)
|
#define BTN_SWITCH_RIFLE (1u << 2)
|
||||||
#define BTN_PICK (1u << 3)
|
#define BTN_PICK (1u << 3)
|
||||||
#define BTN_USE_MEDKIT (1u << 4)
|
#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) {
|
static void set_nonblocking(int sock) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
u_long mode = 1;
|
u_long mode = 1;
|
||||||
|
|
@ -79,10 +292,7 @@ typedef struct MsgInput {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t playerId;
|
uint8_t playerId;
|
||||||
uint32_t clientTick;
|
uint32_t clientTick;
|
||||||
float moveX;
|
float moveX, moveZ, yaw, pitch;
|
||||||
float moveZ;
|
|
||||||
float yaw;
|
|
||||||
float pitch;
|
|
||||||
uint8_t buttons;
|
uint8_t buttons;
|
||||||
} MsgInput;
|
} MsgInput;
|
||||||
|
|
||||||
|
|
@ -93,19 +303,11 @@ typedef struct MsgShoot {
|
||||||
} MsgShoot;
|
} MsgShoot;
|
||||||
|
|
||||||
typedef struct PlayerStateNet {
|
typedef struct PlayerStateNet {
|
||||||
uint8_t id;
|
uint8_t id, alive;
|
||||||
uint8_t alive;
|
|
||||||
int16_t hp;
|
int16_t hp;
|
||||||
float x, y, z;
|
float x, y, z, yaw, pitch;
|
||||||
float yaw, pitch;
|
|
||||||
|
|
||||||
uint8_t weapon;
|
uint8_t weapon;
|
||||||
int16_t pistolMag;
|
int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits;
|
||||||
int16_t rifleMag;
|
|
||||||
int16_t pistolAmmo;
|
|
||||||
int16_t rifleAmmo;
|
|
||||||
int16_t medkits;
|
|
||||||
|
|
||||||
char username[USERNAME_MAX];
|
char username[USERNAME_MAX];
|
||||||
} PlayerStateNet;
|
} PlayerStateNet;
|
||||||
|
|
||||||
|
|
@ -132,19 +334,11 @@ typedef struct MsgItems {
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef struct RemotePlayer {
|
typedef struct RemotePlayer {
|
||||||
int present;
|
int present, alive, hp;
|
||||||
int alive;
|
|
||||||
int hp;
|
|
||||||
Vector3 pos;
|
Vector3 pos;
|
||||||
float yaw, pitch;
|
float yaw, pitch;
|
||||||
|
|
||||||
uint8_t weapon;
|
uint8_t weapon;
|
||||||
int16_t pistolMag;
|
int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits;
|
||||||
int16_t rifleMag;
|
|
||||||
int16_t pistolAmmo;
|
|
||||||
int16_t rifleAmmo;
|
|
||||||
int16_t medkits;
|
|
||||||
|
|
||||||
char username[USERNAME_MAX];
|
char username[USERNAME_MAX];
|
||||||
} RemotePlayer;
|
} RemotePlayer;
|
||||||
|
|
||||||
|
|
@ -172,14 +366,11 @@ static float v3_dot(Vector3 a, Vector3 b) {
|
||||||
static float v3_len(Vector3 a) {
|
static float v3_len(Vector3 a) {
|
||||||
return sqrtf(a.x * a.x + a.y * a.y + a.z * a.z);
|
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) {
|
static Vector3 v3_norm(Vector3 a) {
|
||||||
float l = v3_len(a);
|
float l = v3_len(a);
|
||||||
if (l <= 1e-6f)
|
if (l <= 1e-6f)
|
||||||
return v3(0, 0, 1);
|
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) {
|
static const char *ItemName(uint8_t t) {
|
||||||
|
|
@ -235,7 +426,7 @@ int main(void) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int sw = 1280, sh = 720;
|
const int sw = 1280, sh = 720;
|
||||||
InitWindow(sw, sh, "Voxel Shooter - Client");
|
InitWindow(sw, sh, "Voxel Shooter - Client (SMOOTH TERRAIN)");
|
||||||
SetTargetFPS(120);
|
SetTargetFPS(120);
|
||||||
|
|
||||||
char myName[USERNAME_MAX];
|
char myName[USERNAME_MAX];
|
||||||
|
|
@ -243,6 +434,12 @@ int main(void) {
|
||||||
|
|
||||||
DisableCursor();
|
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);
|
int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
perror("socket");
|
perror("socket");
|
||||||
|
|
@ -261,33 +458,20 @@ int main(void) {
|
||||||
RemotePlayer rp[MAX_PLAYERS] = {0};
|
RemotePlayer rp[MAX_PLAYERS] = {0};
|
||||||
WorldItem wi[MAX_ITEMS] = {0};
|
WorldItem wi[MAX_ITEMS] = {0};
|
||||||
|
|
||||||
Vector3 camPos = v3(0, 1.7f, 6);
|
Vector3 camPos = v3(0, 5.0f, 6);
|
||||||
float yaw = 0.0f;
|
float yaw = 0.0f, pitch = 0.0f;
|
||||||
float pitch = 0.0f;
|
|
||||||
|
|
||||||
float recoilYaw = 0.0f, recoilPitch = 0.0f;
|
|
||||||
float crossSpread = 0.0f;
|
|
||||||
|
|
||||||
|
float recoilYaw = 0.0f, recoilPitch = 0.0f, crossSpread = 0.0f;
|
||||||
float fireCooldown = 0.0f;
|
float fireCooldown = 0.0f;
|
||||||
int shotsInBurst = 0;
|
int shotsInBurst = 0;
|
||||||
float burstResetTimer = 0.0f;
|
float burstResetTimer = 0.0f;
|
||||||
|
|
||||||
const float pistolFireRate = 4.0f;
|
const float pistolFireRate = 4.0f, rifleFireRate = 12.0f;
|
||||||
const float rifleFireRate = 12.0f;
|
const float recoilReturn = 18.0f, crossReturn = 14.0f;
|
||||||
const float recoilReturn = 18.0f;
|
const float pistolKickPitch = 0.010f, pistolKickYaw = 0.004f;
|
||||||
const float crossReturn = 14.0f;
|
const float rifleKickPitch = 0.018f, rifleKickYaw = 0.010f;
|
||||||
|
const float pistolCrossKick = 2.0f, rifleCrossKick = 4.0f;
|
||||||
const float pistolKickPitch = 0.010f;
|
const float pistolSprayGrow = 0.4f, rifleSprayGrow = 1.2f;
|
||||||
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 burstResetTime = 0.18f;
|
const float burstResetTime = 0.18f;
|
||||||
|
|
||||||
MsgHello hello = {0};
|
MsgHello hello = {0};
|
||||||
|
|
@ -320,7 +504,6 @@ int main(void) {
|
||||||
crossSpread = 0.0f;
|
crossSpread = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive packets
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t buf[1400];
|
uint8_t buf[1400];
|
||||||
struct sockaddr_in from = {0};
|
struct sockaddr_in from = {0};
|
||||||
|
|
@ -365,7 +548,6 @@ int main(void) {
|
||||||
p->pistolAmmo = ps->pistolAmmo;
|
p->pistolAmmo = ps->pistolAmmo;
|
||||||
p->rifleAmmo = ps->rifleAmmo;
|
p->rifleAmmo = ps->rifleAmmo;
|
||||||
p->medkits = ps->medkits;
|
p->medkits = ps->medkits;
|
||||||
|
|
||||||
memset(p->username, 0, USERNAME_MAX);
|
memset(p->username, 0, USERNAME_MAX);
|
||||||
strncpy(p->username, ps->username, USERNAME_MAX - 1);
|
strncpy(p->username, ps->username, USERNAME_MAX - 1);
|
||||||
}
|
}
|
||||||
|
|
@ -390,7 +572,6 @@ int main(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse look
|
|
||||||
Vector2 md = GetMouseDelta();
|
Vector2 md = GetMouseDelta();
|
||||||
const float sens = 0.0025f;
|
const float sens = 0.0025f;
|
||||||
yaw -= md.x * sens;
|
yaw -= md.x * sens;
|
||||||
|
|
@ -407,7 +588,6 @@ int main(void) {
|
||||||
if (viewPitch > 1.5f)
|
if (viewPitch > 1.5f)
|
||||||
viewPitch = 1.5f;
|
viewPitch = 1.5f;
|
||||||
|
|
||||||
// Movement
|
|
||||||
float moveX = 0.0f, moveZ = 0.0f;
|
float moveX = 0.0f, moveZ = 0.0f;
|
||||||
if (IsKeyDown(KEY_A))
|
if (IsKeyDown(KEY_A))
|
||||||
moveX += 1.0f;
|
moveX += 1.0f;
|
||||||
|
|
@ -418,9 +598,7 @@ int main(void) {
|
||||||
if (IsKeyDown(KEY_S))
|
if (IsKeyDown(KEY_S))
|
||||||
moveZ -= 1.0f;
|
moveZ -= 1.0f;
|
||||||
|
|
||||||
// Input buttons
|
|
||||||
uint8_t buttons = 0;
|
uint8_t buttons = 0;
|
||||||
|
|
||||||
if (IsKeyPressed(KEY_ONE))
|
if (IsKeyPressed(KEY_ONE))
|
||||||
buttons |= BTN_SWITCH_PISTOL;
|
buttons |= BTN_SWITCH_PISTOL;
|
||||||
if (IsKeyPressed(KEY_TWO))
|
if (IsKeyPressed(KEY_TWO))
|
||||||
|
|
@ -432,7 +610,6 @@ int main(void) {
|
||||||
if (IsKeyPressed(KEY_H))
|
if (IsKeyPressed(KEY_H))
|
||||||
buttons |= BTN_USE_MEDKIT;
|
buttons |= BTN_USE_MEDKIT;
|
||||||
|
|
||||||
// Send INPUT
|
|
||||||
if (myId != 255) {
|
if (myId != 255) {
|
||||||
MsgInput in = {0};
|
MsgInput in = {0};
|
||||||
in.type = MSG_INPUT;
|
in.type = MSG_INPUT;
|
||||||
|
|
@ -447,13 +624,11 @@ int main(void) {
|
||||||
(const struct sockaddr *)&srv, sizeof(srv));
|
(const struct sockaddr *)&srv, sizeof(srv));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shoot
|
|
||||||
if (myId != 255 && IsMouseButtonDown(MOUSE_BUTTON_LEFT) &&
|
if (myId != 255 && IsMouseButtonDown(MOUSE_BUTTON_LEFT) &&
|
||||||
fireCooldown <= 0.0f && rp[myId].present) {
|
fireCooldown <= 0.0f && rp[myId].present) {
|
||||||
int wpn = rp[myId].weapon;
|
int wpn = rp[myId].weapon;
|
||||||
float rate = (wpn == WEAPON_PISTOL) ? pistolFireRate : rifleFireRate;
|
float rate = (wpn == WEAPON_PISTOL) ? pistolFireRate : rifleFireRate;
|
||||||
float interval = 1.0f / rate;
|
fireCooldown = 1.0f / rate;
|
||||||
fireCooldown = interval;
|
|
||||||
|
|
||||||
shotsInBurst++;
|
shotsInBurst++;
|
||||||
burstResetTimer = burstResetTime;
|
burstResetTimer = burstResetTime;
|
||||||
|
|
@ -478,7 +653,6 @@ int main(void) {
|
||||||
(const struct sockaddr *)&srv, sizeof(srv));
|
(const struct sockaddr *)&srv, sizeof(srv));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Camera
|
|
||||||
Vector3 forward = v3(sinf(viewYaw) * cosf(viewPitch), sinf(viewPitch),
|
Vector3 forward = v3(sinf(viewYaw) * cosf(viewPitch), sinf(viewPitch),
|
||||||
cosf(viewYaw) * cosf(viewPitch));
|
cosf(viewYaw) * cosf(viewPitch));
|
||||||
|
|
||||||
|
|
@ -490,23 +664,12 @@ int main(void) {
|
||||||
cam.projection = CAMERA_PERSPECTIVE;
|
cam.projection = CAMERA_PERSPECTIVE;
|
||||||
|
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
ClearBackground((Color){20, 24, 32, 255});
|
ClearBackground((Color){135, 206, 235, 255});
|
||||||
|
|
||||||
BeginMode3D(cam);
|
BeginMode3D(cam);
|
||||||
|
|
||||||
// Ground
|
// Draw smooth terrain
|
||||||
for (int z = -16; z <= 16; z++) {
|
DrawModel(terrainModel, (Vector3){0, 0, 0}, 1.0f, WHITE);
|
||||||
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});
|
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
for (int i = 0; i < MAX_ITEMS; i++) {
|
for (int i = 0; i < MAX_ITEMS; i++) {
|
||||||
|
|
@ -519,8 +682,7 @@ int main(void) {
|
||||||
ic = (Color){255, 220, 120, 255};
|
ic = (Color){255, 220, 120, 255};
|
||||||
if (wi[i].type == ITEM_AMMO_RIFLE)
|
if (wi[i].type == ITEM_AMMO_RIFLE)
|
||||||
ic = (Color){255, 180, 120, 255};
|
ic = (Color){255, 180, 120, 255};
|
||||||
DrawCubeV(v3(wi[i].pos.x, wi[i].pos.y + 0.15f, wi[i].pos.z),
|
DrawSphere(wi[i].pos, 0.3f, ic);
|
||||||
v3(0.3f, 0.3f, 0.3f), ic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Players
|
// Players
|
||||||
|
|
@ -540,21 +702,16 @@ int main(void) {
|
||||||
|
|
||||||
// Nameplates
|
// Nameplates
|
||||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
if (!rp[i].present)
|
if (!rp[i].present || !rp[i].username[0])
|
||||||
continue;
|
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 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 camForward = v3_norm(v3_sub(cam.target, cam.position));
|
||||||
Vector3 toHead = v3_sub(head, cam.position);
|
Vector3 toHead = v3_sub(head, cam.position);
|
||||||
if (v3_dot(camForward, toHead) <= 0.0f)
|
if (v3_dot(camForward, toHead) <= 0.0f)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Vector2 s = GetWorldToScreen(head, cam);
|
Vector2 s = GetWorldToScreen(head, cam);
|
||||||
if (s.x < -200 || s.x > sw + 200 || s.y < -200 || s.y > sh + 200)
|
if (s.x < -200 || s.x > sw + 200 || s.y < -200 || s.y > sh + 200)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int fontSize = 18;
|
int fontSize = 18;
|
||||||
int w = MeasureText(rp[i].username, fontSize);
|
int w = MeasureText(rp[i].username, fontSize);
|
||||||
Color tc = (i == myId) ? (Color){180, 230, 255, 255} : RAYWHITE;
|
Color tc = (i == myId) ? (Color){180, 230, 255, 255} : RAYWHITE;
|
||||||
|
|
@ -566,9 +723,7 @@ int main(void) {
|
||||||
if (myId == 255 || !rp[myId].present) {
|
if (myId == 255 || !rp[myId].present) {
|
||||||
DrawText("Connecting...", 10, 10, 20, RAYWHITE);
|
DrawText("Connecting...", 10, 10, 20, RAYWHITE);
|
||||||
} else {
|
} else {
|
||||||
// Top left - Health and Medkits
|
|
||||||
DrawRectangle(10, 10, 280, 110, (Color){0, 0, 0, 120});
|
DrawRectangle(10, 10, 280, 110, (Color){0, 0, 0, 120});
|
||||||
|
|
||||||
DrawText("HP", 20, 20, 20, RAYWHITE);
|
DrawText("HP", 20, 20, 20, RAYWHITE);
|
||||||
int healthBarWidth = (rp[myId].hp * 220) / 100;
|
int healthBarWidth = (rp[myId].hp * 220) / 100;
|
||||||
Color healthColor = (rp[myId].hp > 60) ? (Color){80, 255, 80, 120}
|
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, 220, 20, (Color){40, 40, 40, 255});
|
||||||
DrawRectangle(20, 45, healthBarWidth, 20, healthColor);
|
DrawRectangle(20, 45, healthBarWidth, 20, healthColor);
|
||||||
DrawText(TextFormat("%d", rp[myId].hp), 250, 47, 18, RAYWHITE);
|
DrawText(TextFormat("%d", rp[myId].hp), 250, 47, 18, RAYWHITE);
|
||||||
|
|
||||||
DrawText(TextFormat("Medkits: %d (H to use)", rp[myId].medkits), 20, 75,
|
DrawText(TextFormat("Medkits: %d (H to use)", rp[myId].medkits), 20, 75,
|
||||||
18,
|
18,
|
||||||
(rp[myId].medkits > 0) ? (Color){120, 255, 120, 120}
|
(rp[myId].medkits > 0) ? (Color){120, 255, 120, 120}
|
||||||
: (Color){120, 120, 120, 120});
|
: (Color){120, 120, 120, 120});
|
||||||
|
|
||||||
DrawText("1=Pistol 2=Rifle R=Reload F=Pick", 20, 95, 14,
|
DrawText("1=Pistol 2=Rifle R=Reload F=Pick", 20, 95, 14,
|
||||||
(Color){150, 150, 150, 150});
|
(Color){150, 150, 150, 150});
|
||||||
|
|
||||||
// Bottom right - Weapon and Ammo
|
|
||||||
const char *weaponName =
|
const char *weaponName =
|
||||||
(rp[myId].weapon == WEAPON_PISTOL) ? "PISTOL" : "RIFLE";
|
(rp[myId].weapon == WEAPON_PISTOL) ? "PISTOL" : "RIFLE";
|
||||||
Color weaponColor = (rp[myId].weapon == WEAPON_PISTOL)
|
Color weaponColor = (rp[myId].weapon == WEAPON_PISTOL)
|
||||||
? (Color){100, 200, 255, 255}
|
? (Color){100, 200, 255, 255}
|
||||||
: (Color){255, 150, 100, 255};
|
: (Color){255, 150, 100, 255};
|
||||||
|
|
||||||
int currentMag = (rp[myId].weapon == WEAPON_PISTOL) ? rp[myId].pistolMag
|
int currentMag = (rp[myId].weapon == WEAPON_PISTOL) ? rp[myId].pistolMag
|
||||||
: rp[myId].rifleMag;
|
: rp[myId].rifleMag;
|
||||||
int reserveAmmo = (rp[myId].weapon == WEAPON_PISTOL) ? rp[myId].pistolAmmo
|
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});
|
DrawRectangle(sw - 260, sh - 120, 250, 110, (Color){0, 0, 0, 180});
|
||||||
DrawText(weaponName, sw - 250, sh - 110, 28, weaponColor);
|
DrawText(weaponName, sw - 250, sh - 110, 28, weaponColor);
|
||||||
|
DrawText(TextFormat("%d", currentMag), sw - 250, sh - 75, 40, RAYWHITE);
|
||||||
char magText[16];
|
DrawText(TextFormat("/ %d", reserveAmmo), sw - 140, sh - 65, 24,
|
||||||
snprintf(magText, sizeof(magText), "%d", currentMag);
|
(Color){180, 180, 180, 255});
|
||||||
DrawText(magText, sw - 250, sh - 75, 40, RAYWHITE);
|
if (currentMag == 0)
|
||||||
|
|
||||||
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("RELOAD!", sw - 250, sh - 30, 20, (Color){255, 80, 80, 255});
|
DrawText("RELOAD!", sw - 250, sh - 30, 20, (Color){255, 80, 80, 255});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawFPS(sw - 90, 10);
|
DrawFPS(sw - 90, 10);
|
||||||
|
|
@ -632,6 +776,7 @@ int main(void) {
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnloadModel(terrainModel);
|
||||||
CloseWindow();
|
CloseWindow();
|
||||||
CLOSESOCK(sock);
|
CLOSESOCK(sock);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
||||||
BIN
game/server
BIN
game/server
Binary file not shown.
308
game/server.c
308
game/server.c
|
|
@ -1,3 +1,4 @@
|
||||||
|
// game/server.c - SMOOTH TERRAIN VERSION
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -50,6 +51,122 @@ typedef int socklen_t;
|
||||||
#define CAT_PLAYER (1u << 1)
|
#define CAT_PLAYER (1u << 1)
|
||||||
#define CAT_ITEM (1u << 2)
|
#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) {
|
static double now_seconds(void) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static LARGE_INTEGER freq;
|
static LARGE_INTEGER freq;
|
||||||
|
|
@ -104,10 +221,7 @@ typedef struct MsgInput {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t playerId;
|
uint8_t playerId;
|
||||||
uint32_t clientTick;
|
uint32_t clientTick;
|
||||||
float moveX;
|
float moveX, moveZ, yaw, pitch;
|
||||||
float moveZ;
|
|
||||||
float yaw;
|
|
||||||
float pitch;
|
|
||||||
uint8_t buttons;
|
uint8_t buttons;
|
||||||
} MsgInput;
|
} MsgInput;
|
||||||
|
|
||||||
|
|
@ -118,19 +232,11 @@ typedef struct MsgShoot {
|
||||||
} MsgShoot;
|
} MsgShoot;
|
||||||
|
|
||||||
typedef struct PlayerStateNet {
|
typedef struct PlayerStateNet {
|
||||||
uint8_t id;
|
uint8_t id, alive;
|
||||||
uint8_t alive;
|
|
||||||
int16_t hp;
|
int16_t hp;
|
||||||
float x, y, z;
|
float x, y, z, yaw, pitch;
|
||||||
float yaw, pitch;
|
|
||||||
|
|
||||||
uint8_t weapon;
|
uint8_t weapon;
|
||||||
int16_t pistolMag;
|
int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits;
|
||||||
int16_t rifleMag;
|
|
||||||
int16_t pistolAmmo;
|
|
||||||
int16_t rifleAmmo;
|
|
||||||
int16_t medkits;
|
|
||||||
|
|
||||||
char username[USERNAME_MAX];
|
char username[USERNAME_MAX];
|
||||||
} PlayerStateNet;
|
} PlayerStateNet;
|
||||||
|
|
||||||
|
|
@ -169,26 +275,13 @@ typedef struct Player {
|
||||||
int inUse;
|
int inUse;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
double lastHeard;
|
double lastHeard;
|
||||||
|
|
||||||
char username[USERNAME_MAX];
|
char username[USERNAME_MAX];
|
||||||
|
float moveX, moveZ, yaw, pitch;
|
||||||
float moveX, moveZ;
|
int alive, hp, weapon;
|
||||||
float yaw, pitch;
|
int pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits;
|
||||||
|
|
||||||
int alive;
|
|
||||||
int hp;
|
|
||||||
|
|
||||||
int weapon;
|
|
||||||
int pistolMag;
|
|
||||||
int rifleMag;
|
|
||||||
int pistolAmmo;
|
|
||||||
int rifleAmmo;
|
|
||||||
int medkits;
|
|
||||||
|
|
||||||
double lastShotTime;
|
double lastShotTime;
|
||||||
int shotsInBurst;
|
int shotsInBurst;
|
||||||
uint32_t rng;
|
uint32_t rng;
|
||||||
|
|
||||||
dBodyID body;
|
dBodyID body;
|
||||||
dGeomID geom;
|
dGeomID geom;
|
||||||
} Player;
|
} Player;
|
||||||
|
|
@ -200,11 +293,7 @@ static int addr_equal(const struct sockaddr_in *a,
|
||||||
}
|
}
|
||||||
|
|
||||||
static float clampf(float v, float lo, float hi) {
|
static float clampf(float v, float lo, float hi) {
|
||||||
if (v < lo)
|
return (v < lo) ? lo : (v > hi) ? hi : v;
|
||||||
return lo;
|
|
||||||
if (v > hi)
|
|
||||||
return hi;
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendto_player(int sock, const Player *pl, const void *data,
|
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,
|
sendto(sock, (const char *)data, len, 0, (const struct sockaddr *)&pl->addr,
|
||||||
sizeof(pl->addr));
|
sizeof(pl->addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void broadcast(int sock, const Player players[MAX_PLAYERS],
|
static void broadcast(int sock, const Player players[MAX_PLAYERS],
|
||||||
const void *data, int len) {
|
const void *data, int len) {
|
||||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
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 dWorldID gWorld;
|
||||||
static dSpaceID gSpace;
|
static dSpaceID gSpace;
|
||||||
static dJointGroupID gContactGroup;
|
static dJointGroupID gContactGroup;
|
||||||
|
|
||||||
static Item gItems[MAX_ITEMS];
|
static Item gItems[MAX_ITEMS];
|
||||||
static uint16_t gNextItemId = 1;
|
static uint16_t gNextItemId = 1;
|
||||||
|
|
||||||
|
|
@ -234,7 +323,8 @@ static void item_destroy(Item *it) {
|
||||||
memset(it, 0, sizeof(*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++) {
|
for (int i = 0; i < MAX_ITEMS; i++) {
|
||||||
if (!gItems[i].active) {
|
if (!gItems[i].active) {
|
||||||
Item *it = &gItems[i];
|
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->x = x;
|
||||||
it->y = y;
|
it->y = y;
|
||||||
it->z = z;
|
it->z = z;
|
||||||
|
|
||||||
it->geom = dCreateSphere(gSpace, 0.30);
|
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);
|
dGeomSetCategoryBits(it->geom, CAT_ITEM);
|
||||||
dGeomSetCollideBits(it->geom, 0);
|
dGeomSetCollideBits(it->geom, 0);
|
||||||
dGeomSetData(it->geom, it);
|
dGeomSetData(it->geom, it);
|
||||||
|
|
||||||
return i;
|
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) {
|
static void nearCallback(void *user, dGeomID o1, dGeomID o2) {
|
||||||
(void)user;
|
(void)user;
|
||||||
|
|
||||||
if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) {
|
if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) {
|
||||||
dSpaceCollide2(o1, o2, user, &nearCallback);
|
dSpaceCollide2(o1, o2, user, &nearCallback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long c1 = dGeomGetCategoryBits(o1);
|
unsigned long c1 = dGeomGetCategoryBits(o1);
|
||||||
unsigned long c2 = dGeomGetCategoryBits(o2);
|
unsigned long c2 = dGeomGetCategoryBits(o2);
|
||||||
if ((c1 & CAT_ITEM) || (c2 & CAT_ITEM))
|
if ((c1 & CAT_ITEM) || (c2 & CAT_ITEM))
|
||||||
|
|
@ -275,19 +360,12 @@ static void nearCallback(void *user, dGeomID o1, dGeomID o2) {
|
||||||
const int MAXC = 8;
|
const int MAXC = 8;
|
||||||
dContact c[MAXC];
|
dContact c[MAXC];
|
||||||
int n = dCollide(o1, o2, MAXC, &c[0].geom, sizeof(dContact));
|
int n = dCollide(o1, o2, MAXC, &c[0].geom, sizeof(dContact));
|
||||||
if (n <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
c[i].surface.mode = dContactApprox1;
|
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 = 0.0;
|
||||||
c[i].surface.bounce_vel = 0.0;
|
|
||||||
|
|
||||||
dJointID j = dJointCreateContact(gWorld, gContactGroup, &c[i]);
|
dJointID j = dJointCreateContact(gWorld, gContactGroup, &c[i]);
|
||||||
dBodyID b1 = dGeomGetBody(o1);
|
dJointAttach(j, dGeomGetBody(o1), dGeomGetBody(o2));
|
||||||
dBodyID b2 = dGeomGetBody(o2);
|
|
||||||
dJointAttach(j, b1, b2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,38 +374,35 @@ static void ode_init_world(void) {
|
||||||
gWorld = dWorldCreate();
|
gWorld = dWorldCreate();
|
||||||
gSpace = dHashSpaceCreate(0);
|
gSpace = dHashSpaceCreate(0);
|
||||||
gContactGroup = dJointGroupCreate(0);
|
gContactGroup = dJointGroupCreate(0);
|
||||||
|
dWorldSetGravity(gWorld, 0, -20.0, 0);
|
||||||
dWorldSetGravity(gWorld, 0, -9.81, 0);
|
|
||||||
dWorldSetERP(gWorld, 0.2);
|
dWorldSetERP(gWorld, 0.2);
|
||||||
dWorldSetCFM(gWorld, 1e-5);
|
dWorldSetCFM(gWorld, 1e-5);
|
||||||
|
|
||||||
dGeomID ground = dCreatePlane(gSpace, 0, 1, 0, 0);
|
generate_smooth_terrain();
|
||||||
dGeomSetCategoryBits(ground, CAT_WORLD);
|
|
||||||
dGeomSetCollideBits(ground, CAT_PLAYER);
|
|
||||||
|
|
||||||
const struct {
|
dHeightfieldDataID hfData = dGeomHeightfieldDataCreate();
|
||||||
float x, y, z, sx, sy, sz;
|
dGeomHeightfieldDataBuildDouble(hfData, gHeightData, 0,
|
||||||
} blocks[] = {
|
TERRAIN_SCALE * TERRAIN_SIZE,
|
||||||
{2, 0.5f, 2, 1, 1, 1},
|
TERRAIN_SCALE * TERRAIN_SIZE, TERRAIN_SIZE,
|
||||||
{2, 1.5f, 2, 1, 1, 1},
|
TERRAIN_SIZE, 1.0, 0.0, 0.0, 0);
|
||||||
{-3, 0.5f, -1, 1, 1, 1},
|
dGeomHeightfieldDataSetBounds(hfData, -10.0, 20.0);
|
||||||
{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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn test items
|
gTerrainGeom = dCreateHeightfield(gSpace, hfData, 1);
|
||||||
item_spawn(ITEM_MEDKIT, 1, 1.0f, 0.0f, 1.0f);
|
dGeomSetPosition(gTerrainGeom, 0, 0, 0);
|
||||||
item_spawn(ITEM_MEDKIT, 1, -2.0f, 0.0f, 3.0f);
|
dGeomSetCategoryBits(gTerrainGeom, CAT_WORLD);
|
||||||
item_spawn(ITEM_AMMO_RIFLE, 30, 0.0f, 0.0f, 2.0f);
|
dGeomSetCollideBits(gTerrainGeom, CAT_PLAYER);
|
||||||
item_spawn(ITEM_AMMO_PISTOL, 12, -1.0f, 0.0f, -2.0f);
|
|
||||||
|
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) {
|
static void ode_shutdown_world(void) {
|
||||||
|
if (gHeightData) {
|
||||||
|
free(gHeightData);
|
||||||
|
gHeightData = NULL;
|
||||||
|
}
|
||||||
dJointGroupDestroy(gContactGroup);
|
dJointGroupDestroy(gContactGroup);
|
||||||
dSpaceDestroy(gSpace);
|
dSpaceDestroy(gSpace);
|
||||||
dWorldDestroy(gWorld);
|
dWorldDestroy(gWorld);
|
||||||
|
|
@ -335,24 +410,20 @@ static void ode_shutdown_world(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void player_ode_create(Player *p, int spawnIndex) {
|
static void player_ode_create(Player *p, int spawnIndex) {
|
||||||
const dReal radius = 0.35;
|
|
||||||
const dReal length = 1.0;
|
|
||||||
|
|
||||||
p->body = dBodyCreate(gWorld);
|
p->body = dBodyCreate(gWorld);
|
||||||
|
|
||||||
dMass m;
|
dMass m;
|
||||||
dMassSetCapsuleTotal(&m, 80.0, 3, radius, length);
|
dMassSetCapsuleTotal(&m, 80.0, 3, 0.35, 1.0);
|
||||||
dBodySetMass(p->body, &m);
|
dBodySetMass(p->body, &m);
|
||||||
|
|
||||||
dBodySetLinearDamping(p->body, 0.05);
|
dBodySetLinearDamping(p->body, 0.05);
|
||||||
dBodySetAngularDamping(p->body, 0.99);
|
dBodySetAngularDamping(p->body, 0.99);
|
||||||
|
|
||||||
dBodySetPosition(p->body, (dReal)(spawnIndex * 2), 2.0, 0.0);
|
float sx = (float)(spawnIndex * 4 - 8);
|
||||||
dBodySetAngularVel(p->body, 0, 0, 0);
|
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);
|
dGeomSetBody(p->geom, p->body);
|
||||||
|
|
||||||
dGeomSetCategoryBits(p->geom, CAT_PLAYER);
|
dGeomSetCategoryBits(p->geom, CAT_PLAYER);
|
||||||
dGeomSetCollideBits(p->geom, CAT_WORLD | CAT_PLAYER);
|
dGeomSetCollideBits(p->geom, CAT_WORLD | CAT_PLAYER);
|
||||||
}
|
}
|
||||||
|
|
@ -369,8 +440,7 @@ static void player_ode_destroy(Player *p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct RaycastCtx {
|
typedef struct RaycastCtx {
|
||||||
dGeomID ray;
|
dGeomID ray, hitGeom;
|
||||||
dGeomID hitGeom;
|
|
||||||
dContactGeom hit;
|
dContactGeom hit;
|
||||||
int hasHit;
|
int hasHit;
|
||||||
} RaycastCtx;
|
} RaycastCtx;
|
||||||
|
|
@ -381,7 +451,6 @@ static void rayCallback(void *data, dGeomID o1, dGeomID o2) {
|
||||||
dGeomID other = (o1 == ray) ? o2 : o1;
|
dGeomID other = (o1 == ray) ? o2 : o1;
|
||||||
if (other == ray)
|
if (other == ray)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dContact c[4];
|
dContact c[4];
|
||||||
int n = dCollide(ray, other, 4, &c[0].geom, sizeof(dContact));
|
int n = dCollide(ray, other, 4, &c[0].geom, sizeof(dContact));
|
||||||
for (int i = 0; i < n; i++) {
|
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);
|
dGeomRaySet(ray, ox, oy, oz, dx, dy, dz);
|
||||||
dGeomSetCategoryBits(ray, 0xFFFFFFFFu);
|
dGeomSetCategoryBits(ray, 0xFFFFFFFFu);
|
||||||
dGeomSetCollideBits(ray, CAT_WORLD | CAT_PLAYER);
|
dGeomSetCollideBits(ray, CAT_WORLD | CAT_PLAYER);
|
||||||
|
|
||||||
RaycastCtx rc = {0};
|
RaycastCtx rc = {0};
|
||||||
rc.ray = ray;
|
rc.ray = ray;
|
||||||
dSpaceCollide2(ray, (dGeomID)gSpace, &rc, &rayCallback);
|
dSpaceCollide2(ray, (dGeomID)gSpace, &rc, &rayCallback);
|
||||||
dGeomDestroy(ray);
|
dGeomDestroy(ray);
|
||||||
|
|
||||||
if (!rc.hasHit)
|
if (!rc.hasHit)
|
||||||
return 0;
|
return 0;
|
||||||
if (outHit)
|
if (outHit)
|
||||||
|
|
@ -423,21 +490,25 @@ static uint32_t xorshift32(uint32_t *s) {
|
||||||
*s = x;
|
*s = x;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float frand_signed(uint32_t *s) {
|
static float frand_signed(uint32_t *s) {
|
||||||
return ((xorshift32(s) & 0xFFFFFF) / (float)0x7FFFFF) - 1.0f;
|
return ((xorshift32(s) & 0xFFFFFF) / (float)0x7FFFFF) - 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dir_from_yaw_pitch(float yaw, float pitch, float *dx, float *dy,
|
static void dir_from_yaw_pitch(float yaw, float pitch, float *dx, float *dy,
|
||||||
float *dz) {
|
float *dz) {
|
||||||
*dx = sinf(yaw) * cosf(pitch);
|
*dx = sinf(yaw) * cosf(pitch);
|
||||||
*dy = sinf(pitch);
|
*dy = sinf(pitch);
|
||||||
*dz = cosf(yaw) * cosf(pitch);
|
*dz = cosf(yaw) * cosf(pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void v3_cross(float ax, float ay, float az, float bx, float by, float bz,
|
static void v3_cross(float ax, float ay, float az, float bx, float by, float bz,
|
||||||
float *ox, float *oy, float *oz) {
|
float *ox, float *oy, float *oz) {
|
||||||
*ox = ay * bz - az * by;
|
*ox = ay * bz - az * by;
|
||||||
*oy = az * bx - ax * bz;
|
*oy = az * bx - ax * bz;
|
||||||
*oz = ax * by - ay * bx;
|
*oz = ax * by - ay * bx;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void v3_normf(float *x, float *y, float *z) {
|
static void v3_normf(float *x, float *y, float *z) {
|
||||||
float l = sqrtf((*x) * (*x) + (*y) * (*y) + (*z) * (*z));
|
float l = sqrtf((*x) * (*x) + (*y) * (*y) + (*z) * (*z));
|
||||||
if (l < 1e-6f) {
|
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)
|
if (!gItems[i].active)
|
||||||
continue;
|
continue;
|
||||||
float dx = gItems[i].x - x;
|
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 dz = gItems[i].z - z;
|
||||||
float d2 = dx * dx + dy * dy + dz * dz;
|
float d2 = dx * dx + dy * dy + dz * dz;
|
||||||
if (d2 < bestD2) {
|
if (d2 < bestD2) {
|
||||||
|
|
@ -496,19 +567,16 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Player players[MAX_PLAYERS] = {0};
|
Player players[MAX_PLAYERS] = {0};
|
||||||
uint32_t serverTick = 0;
|
uint32_t serverTick = 0, itemsTickDiv = 0;
|
||||||
uint32_t itemsTickDiv = 0;
|
|
||||||
|
|
||||||
printf("Server listening UDP %d\n", SERVER_PORT);
|
printf("Server listening UDP %d\n", SERVER_PORT);
|
||||||
double nextTick = now_seconds();
|
double nextTick = now_seconds();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Receive
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint8_t buf[1400];
|
uint8_t buf[1400];
|
||||||
struct sockaddr_in from = {0};
|
struct sockaddr_in from = {0};
|
||||||
socklen_t fromLen = sizeof(from);
|
socklen_t fromLen = sizeof(from);
|
||||||
|
|
||||||
int n = (int)recvfrom(sock, (char *)buf, (int)sizeof(buf), 0,
|
int n = (int)recvfrom(sock, (char *)buf, (int)sizeof(buf), 0,
|
||||||
(struct sockaddr *)&from, &fromLen);
|
(struct sockaddr *)&from, &fromLen);
|
||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
|
|
@ -523,7 +591,6 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t type = buf[0];
|
uint8_t type = buf[0];
|
||||||
|
|
||||||
int pid = -1;
|
int pid = -1;
|
||||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
if (players[i].inUse && addr_equal(&players[i].addr, &from)) {
|
if (players[i].inUse && addr_equal(&players[i].addr, &from)) {
|
||||||
|
|
@ -547,21 +614,15 @@ int main(void) {
|
||||||
p->addr = from;
|
p->addr = from;
|
||||||
p->lastHeard = now_seconds();
|
p->lastHeard = now_seconds();
|
||||||
strncpy(p->username, m->username, USERNAME_MAX - 1);
|
strncpy(p->username, m->username, USERNAME_MAX - 1);
|
||||||
|
|
||||||
p->alive = 1;
|
p->alive = 1;
|
||||||
p->hp = 100;
|
p->hp = 100;
|
||||||
|
|
||||||
p->weapon = WEAPON_RIFLE;
|
p->weapon = WEAPON_RIFLE;
|
||||||
p->pistolMag = 12;
|
p->pistolMag = 12;
|
||||||
p->rifleMag = 30;
|
p->rifleMag = 30;
|
||||||
p->pistolAmmo = 48;
|
p->pistolAmmo = 48;
|
||||||
p->rifleAmmo = 90;
|
p->rifleAmmo = 90;
|
||||||
p->medkits = 2;
|
p->medkits = 2;
|
||||||
|
|
||||||
p->lastShotTime = 0.0;
|
|
||||||
p->shotsInBurst = 0;
|
|
||||||
p->rng = 0xA341316Cu ^ (uint32_t)(i * 2654435761u);
|
p->rng = 0xA341316Cu ^ (uint32_t)(i * 2654435761u);
|
||||||
|
|
||||||
player_ode_create(p, i);
|
player_ode_create(p, i);
|
||||||
printf("Player %d joined as '%s'\n", pid, p->username);
|
printf("Player %d joined as '%s'\n", pid, p->username);
|
||||||
break;
|
break;
|
||||||
|
|
@ -583,19 +644,16 @@ int main(void) {
|
||||||
if (pid >= 0 && m->playerId == (uint8_t)pid) {
|
if (pid >= 0 && m->playerId == (uint8_t)pid) {
|
||||||
Player *p = &players[pid];
|
Player *p = &players[pid];
|
||||||
p->lastHeard = now_seconds();
|
p->lastHeard = now_seconds();
|
||||||
|
|
||||||
p->moveX = clampf(m->moveX, -1.0f, 1.0f);
|
p->moveX = clampf(m->moveX, -1.0f, 1.0f);
|
||||||
p->moveZ = clampf(m->moveZ, -1.0f, 1.0f);
|
p->moveZ = clampf(m->moveZ, -1.0f, 1.0f);
|
||||||
p->yaw = m->yaw;
|
p->yaw = m->yaw;
|
||||||
p->pitch = clampf(m->pitch, -1.5f, 1.5f);
|
p->pitch = clampf(m->pitch, -1.5f, 1.5f);
|
||||||
|
|
||||||
// Weapon switch
|
|
||||||
if (m->buttons & BTN_SWITCH_PISTOL)
|
if (m->buttons & BTN_SWITCH_PISTOL)
|
||||||
p->weapon = WEAPON_PISTOL;
|
p->weapon = WEAPON_PISTOL;
|
||||||
if (m->buttons & BTN_SWITCH_RIFLE)
|
if (m->buttons & BTN_SWITCH_RIFLE)
|
||||||
p->weapon = WEAPON_RIFLE;
|
p->weapon = WEAPON_RIFLE;
|
||||||
|
|
||||||
// Reload
|
|
||||||
if (m->buttons & BTN_RELOAD) {
|
if (m->buttons & BTN_RELOAD) {
|
||||||
if (p->weapon == WEAPON_PISTOL) {
|
if (p->weapon == WEAPON_PISTOL) {
|
||||||
int need = 12 - p->pistolMag;
|
int need = 12 - p->pistolMag;
|
||||||
|
|
@ -614,15 +672,13 @@ int main(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick item
|
|
||||||
if (m->buttons & BTN_PICK) {
|
if (m->buttons & BTN_PICK) {
|
||||||
const dReal *pos = dBodyGetPosition(p->body);
|
const dReal *pos = dBodyGetPosition(p->body);
|
||||||
int itIdx = find_nearest_item((float)pos[0], (float)pos[1] + 0.7f,
|
int itIdx = find_nearest_item((float)pos[0], (float)pos[1],
|
||||||
(float)pos[2], 1.6f);
|
(float)pos[2], 2.0f);
|
||||||
if (itIdx >= 0) {
|
if (itIdx >= 0) {
|
||||||
Item *it = &gItems[itIdx];
|
Item *it = &gItems[itIdx];
|
||||||
int taken = 0;
|
int taken = 0;
|
||||||
|
|
||||||
if (it->type == ITEM_AMMO_PISTOL) {
|
if (it->type == ITEM_AMMO_PISTOL) {
|
||||||
p->pistolAmmo += it->qty;
|
p->pistolAmmo += it->qty;
|
||||||
if (p->pistolAmmo > 120)
|
if (p->pistolAmmo > 120)
|
||||||
|
|
@ -639,13 +695,11 @@ int main(void) {
|
||||||
p->medkits = 10;
|
p->medkits = 10;
|
||||||
taken = 1;
|
taken = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taken)
|
if (taken)
|
||||||
item_destroy(it);
|
item_destroy(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use medkit
|
|
||||||
if (m->buttons & BTN_USE_MEDKIT) {
|
if (m->buttons & BTN_USE_MEDKIT) {
|
||||||
if (p->medkits > 0 && p->hp < 100) {
|
if (p->medkits > 0 && p->hp < 100) {
|
||||||
p->medkits--;
|
p->medkits--;
|
||||||
|
|
@ -661,7 +715,6 @@ int main(void) {
|
||||||
players[pid].alive) {
|
players[pid].alive) {
|
||||||
Player *sh = &players[pid];
|
Player *sh = &players[pid];
|
||||||
const double now = now_seconds();
|
const double now = now_seconds();
|
||||||
|
|
||||||
double fireInterval =
|
double fireInterval =
|
||||||
(sh->weapon == WEAPON_PISTOL) ? (1.0 / 4.0) : (1.0 / 12.0);
|
(sh->weapon == WEAPON_PISTOL) ? (1.0 / 4.0) : (1.0 / 12.0);
|
||||||
if (now - sh->lastShotTime < fireInterval)
|
if (now - sh->lastShotTime < fireInterval)
|
||||||
|
|
@ -679,10 +732,8 @@ int main(void) {
|
||||||
sh->lastShotTime = now;
|
sh->lastShotTime = now;
|
||||||
|
|
||||||
const dReal *pos = dBodyGetPosition(sh->body);
|
const dReal *pos = dBodyGetPosition(sh->body);
|
||||||
float ox = (float)pos[0];
|
float ox = (float)pos[0], oy = (float)pos[1] + 0.7f,
|
||||||
float oy = (float)pos[1] + 0.7f;
|
oz = (float)pos[2];
|
||||||
float oz = (float)pos[2];
|
|
||||||
|
|
||||||
float fx, fy, fz;
|
float fx, fy, fz;
|
||||||
dir_from_yaw_pitch(sh->yaw, sh->pitch, &fx, &fy, &fz);
|
dir_from_yaw_pitch(sh->yaw, sh->pitch, &fx, &fy, &fz);
|
||||||
v3_normf(&fx, &fy, &fz);
|
v3_normf(&fx, &fy, &fz);
|
||||||
|
|
@ -703,7 +754,6 @@ int main(void) {
|
||||||
|
|
||||||
float ax = frand_signed(&sh->rng) * spread;
|
float ax = frand_signed(&sh->rng) * spread;
|
||||||
float ay = frand_signed(&sh->rng) * spread;
|
float ay = frand_signed(&sh->rng) * spread;
|
||||||
|
|
||||||
float dx = fx + rx * ax + ux * ay;
|
float dx = fx + rx * ax + ux * ay;
|
||||||
float dy = fy + ry * ax + uy * ay;
|
float dy = fy + ry * ax + uy * ay;
|
||||||
float dz = fz + rz * ax + uz * ay;
|
float dz = fz + rz * ax + uz * ay;
|
||||||
|
|
@ -711,7 +761,7 @@ int main(void) {
|
||||||
|
|
||||||
dContactGeom hit;
|
dContactGeom hit;
|
||||||
dGeomID hitGeom = 0;
|
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;
|
int victim = -1;
|
||||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
if (!players[i].inUse)
|
if (!players[i].inUse)
|
||||||
|
|
@ -721,7 +771,6 @@ int main(void) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (victim >= 0 && victim != pid && players[victim].alive) {
|
if (victim >= 0 && victim != pid && players[victim].alive) {
|
||||||
int dmg = 25;
|
int dmg = 25;
|
||||||
players[victim].hp -= dmg;
|
players[victim].hp -= dmg;
|
||||||
|
|
@ -735,7 +784,6 @@ int main(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tick
|
|
||||||
double t = now_seconds();
|
double t = now_seconds();
|
||||||
if (t >= nextTick) {
|
if (t >= nextTick) {
|
||||||
nextTick += DT;
|
nextTick += DT;
|
||||||
|
|
@ -753,23 +801,20 @@ int main(void) {
|
||||||
const dReal *lvel = dBodyGetLinearVel(p->body);
|
const dReal *lvel = dBodyGetLinearVel(p->body);
|
||||||
const dReal *avel = dBodyGetAngularVel(p->body);
|
const dReal *avel = dBodyGetAngularVel(p->body);
|
||||||
|
|
||||||
float dx = p->moveX;
|
float dx = p->moveX, dz = p->moveZ;
|
||||||
float dz = p->moveZ;
|
|
||||||
float len = sqrtf(dx * dx + dz * dz);
|
float len = sqrtf(dx * dx + dz * dz);
|
||||||
if (len > 1e-6f) {
|
if (len > 1e-6f) {
|
||||||
dx /= len;
|
dx /= len;
|
||||||
dz /= len;
|
dz /= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
float speed = 5.0f;
|
float speed = 6.0f;
|
||||||
float fx = sinf(p->yaw) * dz - cosf(p->yaw) * dx;
|
float fx = sinf(p->yaw) * dz - cosf(p->yaw) * dx;
|
||||||
float fz = cosf(p->yaw) * dz + sinf(p->yaw) * dx;
|
float fz = cosf(p->yaw) * dz + sinf(p->yaw) * dx;
|
||||||
|
|
||||||
dBodySetLinearVel(p->body, fx * speed, lvel[1], fz * speed);
|
dBodySetLinearVel(p->body, fx * speed, lvel[1], fz * speed);
|
||||||
dBodySetAngularVel(p->body, avel[0], 0, avel[2]);
|
dBodySetAngularVel(p->body, avel[0], 0, avel[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send snapshot
|
|
||||||
MsgSnapshot snap = {0};
|
MsgSnapshot snap = {0};
|
||||||
snap.type = MSG_SNAPSHOT;
|
snap.type = MSG_SNAPSHOT;
|
||||||
snap.serverTick = serverTick;
|
snap.serverTick = serverTick;
|
||||||
|
|
@ -778,9 +823,7 @@ int main(void) {
|
||||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
if (!players[i].inUse)
|
if (!players[i].inUse)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const dReal *pos = dBodyGetPosition(players[i].body);
|
const dReal *pos = dBodyGetPosition(players[i].body);
|
||||||
|
|
||||||
PlayerStateNet *ps = &snap.p[snap.count++];
|
PlayerStateNet *ps = &snap.p[snap.count++];
|
||||||
ps->id = (uint8_t)i;
|
ps->id = (uint8_t)i;
|
||||||
ps->alive = players[i].alive;
|
ps->alive = players[i].alive;
|
||||||
|
|
@ -798,19 +841,15 @@ int main(void) {
|
||||||
ps->medkits = (int16_t)players[i].medkits;
|
ps->medkits = (int16_t)players[i].medkits;
|
||||||
strncpy(ps->username, players[i].username, USERNAME_MAX - 1);
|
strncpy(ps->username, players[i].username, USERNAME_MAX - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(sock, players, &snap, (int)sizeof(snap));
|
broadcast(sock, players, &snap, (int)sizeof(snap));
|
||||||
|
|
||||||
// Send items periodically
|
|
||||||
itemsTickDiv++;
|
itemsTickDiv++;
|
||||||
if (itemsTickDiv >= 6) {
|
if (itemsTickDiv >= 6) {
|
||||||
itemsTickDiv = 0;
|
itemsTickDiv = 0;
|
||||||
|
|
||||||
MsgItems im = {0};
|
MsgItems im = {0};
|
||||||
im.type = MSG_ITEMS;
|
im.type = MSG_ITEMS;
|
||||||
im.serverTick = serverTick;
|
im.serverTick = serverTick;
|
||||||
im.count = 0;
|
im.count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < MAX_ITEMS; i++) {
|
for (int i = 0; i < MAX_ITEMS; i++) {
|
||||||
if (!gItems[i].active)
|
if (!gItems[i].active)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -822,12 +861,10 @@ int main(void) {
|
||||||
it->y = gItems[i].y;
|
it->y = gItems[i].y;
|
||||||
it->z = gItems[i].z;
|
it->z = gItems[i].z;
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(sock, players, &im, (int)sizeof(im));
|
broadcast(sock, players, &im, (int)sizeof(im));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout check
|
|
||||||
double now = now_seconds();
|
double now = now_seconds();
|
||||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
if (players[i].inUse && (now - players[i].lastHeard) > 10.0) {
|
if (players[i].inUse && (now - players[i].lastHeard) > 10.0) {
|
||||||
|
|
@ -837,7 +874,6 @@ int main(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep
|
|
||||||
double wait = nextTick - now_seconds();
|
double wait = nextTick - now_seconds();
|
||||||
if (wait > 0.0) {
|
if (wait > 0.0) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue