3977 lines
101 KiB
C
3977 lines
101 KiB
C
#include <GL/glew.h>
|
|
#include <GLFW/glfw3.h>
|
|
#include <curl/curl.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
//#include <gtk.h>
|
|
#include "attrib.h"
|
|
#include "auth.h"
|
|
#include "chunk.h"
|
|
#include "client.h"
|
|
#include "config.h"
|
|
#include "cube.h"
|
|
#include "db.h"
|
|
#include "inventory.h"
|
|
#include "item.h"
|
|
#include "map.h"
|
|
#include "matrix.h"
|
|
#include "noise.h"
|
|
#include "parser.h"
|
|
#include "player.h"
|
|
#include "rendering.h"
|
|
#include "sign.h"
|
|
#include "state.h"
|
|
#include "tinycthread.h"
|
|
#include "util.h"
|
|
#include "worldgen/world.h"
|
|
|
|
#define MAX_CHUNKS 8192
|
|
#define MAX_PLAYERS 128
|
|
#define WORKERS 4
|
|
#define MAX_TEXT_LENGTH 256
|
|
#define MAX_PATH_LENGTH 256
|
|
#define MAX_ADDR_LENGTH 256
|
|
|
|
#define ALIGN_LEFT 0
|
|
#define ALIGN_CENTER 1
|
|
#define ALIGN_RIGHT 2
|
|
|
|
#define MODE_OFFLINE 0
|
|
#define MODE_ONLINE 1
|
|
|
|
#define WORKER_IDLE 0
|
|
#define WORKER_BUSY 1
|
|
#define WORKER_DONE 2
|
|
|
|
// initial values to allow the game to run without config
|
|
int SHOW_LIGHTS = 1;
|
|
int SHOW_PLANTS = 1;
|
|
int SHOW_CLOUDS = 1;
|
|
int SHOW_TREES = 1;
|
|
int SHOW_ITEM = 1;
|
|
int SHOW_CROSSHAIRS = 1;
|
|
int SHOW_INFO_TEXT = 1;
|
|
int SHOW_CHAT_TEXT = 1;
|
|
int SHOW_PLAYER_NAMES = 1;
|
|
int FONT_SIZE = 16; //16
|
|
|
|
|
|
typedef struct {
|
|
int p;
|
|
int q;
|
|
int load;
|
|
Map *block_maps[3][3];
|
|
Map *light_maps[3][3];
|
|
int miny;
|
|
int maxy;
|
|
int faces;
|
|
GLfloat *data;
|
|
} WorkerItem;
|
|
|
|
typedef struct {
|
|
int index;
|
|
int state;
|
|
thrd_t thrd;
|
|
mtx_t mtx;
|
|
cnd_t cnd;
|
|
WorkerItem item;
|
|
} Worker;
|
|
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
int z;
|
|
int w;
|
|
} Block;
|
|
|
|
typedef struct {
|
|
float r;
|
|
float g;
|
|
float b;
|
|
} SkyColor;
|
|
|
|
typedef struct {
|
|
GLFWwindow *window;
|
|
Worker workers[WORKERS];
|
|
Chunk chunks[MAX_CHUNKS];
|
|
int chunk_count;
|
|
int create_radius;
|
|
int render_radius;
|
|
int delete_radius;
|
|
int sign_radius;
|
|
Player players[MAX_PLAYERS];
|
|
int player_count;
|
|
int typing;
|
|
char typing_buffer[MAX_TEXT_LENGTH];
|
|
int message_index;
|
|
char messages[MAX_MESSAGES][MAX_TEXT_LENGTH];
|
|
int width;
|
|
int height;
|
|
int observe1;
|
|
int observe2;
|
|
int flying;
|
|
int item_index;
|
|
int scale;
|
|
int ortho;
|
|
float fov;
|
|
int suppress_char;
|
|
int mode;
|
|
int mode_changed;
|
|
char db_path[MAX_PATH_LENGTH];
|
|
char server_addr[MAX_ADDR_LENGTH];
|
|
int server_port;
|
|
int day_length;
|
|
int time_changed;
|
|
Block block0;
|
|
Block block1;
|
|
Block copy0;
|
|
Block copy1;
|
|
SkyColor sky_color;
|
|
SkyColor last_sky_color;
|
|
//int is_fullscreen;
|
|
Inventory inventory;
|
|
} Model;
|
|
|
|
static Model model;
|
|
static Model *g = &model;
|
|
|
|
float time_of_day() {
|
|
if (g->day_length <= 0) {
|
|
return 0.5;
|
|
}
|
|
float t;
|
|
t = glfwGetTime();
|
|
t = t / g->day_length;
|
|
t = t - (int)t;
|
|
return t;
|
|
}
|
|
|
|
float get_daylight() {
|
|
float timer = time_of_day();
|
|
if (timer < 0.5) {
|
|
float t = (timer - 0.25) * 100;
|
|
return 1 / (1 + powf(2, -t));
|
|
}
|
|
else {
|
|
float t = (timer - 0.85) * 100;
|
|
return 1 - 1 / (1 + powf(2, -t));
|
|
}
|
|
}
|
|
|
|
void get_sky_tint(Biome biome, SkyColor *c) {
|
|
switch(biome) {
|
|
//Not needed
|
|
//case Biome_TEMPERATE:
|
|
case Biome_DESERT:
|
|
c->r = 1.6f;
|
|
c->g = 1.3f,
|
|
c->b = 1.0f;
|
|
break;
|
|
case Biome_MESA:
|
|
c->r = 2.2f;
|
|
c->g = 1.6f,
|
|
c->b = 1.7f;
|
|
break;
|
|
case Biome_RAINFOREST:
|
|
c->r = 1.5f;
|
|
c->g = 2.0f,
|
|
c->b = 2.0f;
|
|
break;
|
|
case Biome_TAIGA:
|
|
c->r = 2.0f;
|
|
c->g = 2.0f,
|
|
c->b = 2.0f;
|
|
break;
|
|
case Biome_SWAMP:
|
|
c->r = 0.4f;
|
|
c->g = 1.0f,
|
|
c->b = 0.6f;
|
|
break;
|
|
default:
|
|
c->r = 1.0f;
|
|
c->g = 1.5f,
|
|
c->b = 2.0f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int get_scale_factor() {
|
|
int window_width, window_height;
|
|
int buffer_width, buffer_height;
|
|
glfwGetWindowSize(g->window, &window_width, &window_height);
|
|
glfwGetFramebufferSize(g->window, &buffer_width, &buffer_height);
|
|
int result = buffer_width / window_width;
|
|
result = MAX(1, result);
|
|
result = MIN(2, result);
|
|
return result;
|
|
}
|
|
|
|
void get_sight_vector(float rx, float ry, float *vx, float *vy, float *vz) {
|
|
float m = cosf(ry);
|
|
*vx = cosf(rx - RADIANS(90)) * m;
|
|
*vy = sinf(ry);
|
|
*vz = sinf(rx - RADIANS(90)) * m;
|
|
}
|
|
|
|
void get_motion_vector(int flying, int sz, int sx, float rx, float ry,
|
|
float *vx, float *vy, float *vz) {
|
|
*vx = 0; *vy = 0; *vz = 0;
|
|
if (!sz && !sx) {
|
|
return;
|
|
}
|
|
float strafe = atan2f(sz, sx);
|
|
if (flying) {
|
|
float m = cosf(ry);
|
|
float y = sinf(ry);
|
|
if (sx) {
|
|
if (!sz) {
|
|
y = 0;
|
|
}
|
|
m = 1;
|
|
}
|
|
if (sz > 0) {
|
|
y = -y;
|
|
}
|
|
*vx = cosf(rx + strafe) * m;
|
|
*vy = y;
|
|
*vz = sinf(rx + strafe) * m;
|
|
}
|
|
else {
|
|
*vx = cosf(rx + strafe);
|
|
*vy = 0;
|
|
*vz = sinf(rx + strafe);
|
|
}
|
|
}
|
|
|
|
GLuint gen_wireframe_buffer(float x, float y, float z, float n) {
|
|
float data[72];
|
|
make_cube_wireframe(data, x, y, z, n);
|
|
return gen_buffer(sizeof(data), data);
|
|
}
|
|
|
|
GLuint gen_sky_buffer() {
|
|
float data[12288];
|
|
make_sphere(data, 1, 3);
|
|
return gen_buffer(sizeof(data), data);
|
|
}
|
|
|
|
GLuint gen_cube_buffer(float x, float y, float z, float n, int w) {
|
|
GLfloat *data = malloc_faces(10, 6);
|
|
float ao[6][4] = {0};
|
|
float light[6][4] = {
|
|
{0.5, 0.5, 0.5, 0.5},
|
|
{0.5, 0.5, 0.5, 0.5},
|
|
{0.5, 0.5, 0.5, 0.5},
|
|
{0.5, 0.5, 0.5, 0.5},
|
|
{0.5, 0.5, 0.5, 0.5},
|
|
{0.5, 0.5, 0.5, 0.5}
|
|
};
|
|
make_cube(data, ao, light, 1, 1, 1, 1, 1, 1, x, y, z, n, w);
|
|
return gen_faces(10, 6, data);
|
|
}
|
|
|
|
Player *find_player(int id) {
|
|
for (int i = 0; i < g->player_count; i++) {
|
|
Player *player = g->players + i;
|
|
if (player->id == id) {
|
|
return player;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void update_player(Player *player,
|
|
float x, float y, float z, float rx, float ry, int interpolate)
|
|
{
|
|
if (interpolate) {
|
|
State *s1 = &player->state1;
|
|
State *s2 = &player->state2;
|
|
memcpy(s1, s2, sizeof(State));
|
|
s2->x = x; s2->y = y; s2->z = z; s2->rx = rx; s2->ry = ry;
|
|
s2->t = glfwGetTime();
|
|
if (s2->rx - s1->rx > PI) {
|
|
s1->rx += 2 * PI;
|
|
}
|
|
if (s1->rx - s2->rx > PI) {
|
|
s1->rx -= 2 * PI;
|
|
}
|
|
}
|
|
else {
|
|
State *s = &player->state;
|
|
s->x = x; s->y = y; s->z = z; s->rx = rx; s->ry = ry;
|
|
del_buffer(player->buffer);
|
|
player->buffer = gen_player_buffer(s->x, s->y, s->z, s->rx, s->ry);
|
|
}
|
|
}
|
|
|
|
void interpolate_player(Player *player) {
|
|
State *s1 = &player->state1;
|
|
State *s2 = &player->state2;
|
|
float t1 = s2->t - s1->t;
|
|
float t2 = glfwGetTime() - s2->t;
|
|
t1 = MIN(t1, 1);
|
|
t1 = MAX(t1, 0.1);
|
|
float p = MIN(t2 / t1, 1);
|
|
update_player(
|
|
player,
|
|
s1->x + (s2->x - s1->x) * p,
|
|
s1->y + (s2->y - s1->y) * p,
|
|
s1->z + (s2->z - s1->z) * p,
|
|
s1->rx + (s2->rx - s1->rx) * p,
|
|
s1->ry + (s2->ry - s1->ry) * p,
|
|
0);
|
|
}
|
|
|
|
void delete_player(int id) {
|
|
Player *player = find_player(id);
|
|
if (!player) {
|
|
return;
|
|
}
|
|
int count = g->player_count;
|
|
del_buffer(player->buffer);
|
|
Player *other = g->players + (--count);
|
|
memcpy(player, other, sizeof(Player));
|
|
g->player_count = count;
|
|
}
|
|
|
|
void delete_all_players() {
|
|
for (int i = 0; i < g->player_count; i++) {
|
|
Player *player = g->players + i;
|
|
del_buffer(player->buffer);
|
|
}
|
|
g->player_count = 0;
|
|
}
|
|
|
|
float player_player_distance(Player *p1, Player *p2) {
|
|
State *s1 = &p1->state;
|
|
State *s2 = &p2->state;
|
|
float x = s2->x - s1->x;
|
|
float y = s2->y - s1->y;
|
|
float z = s2->z - s1->z;
|
|
return sqrtf(x * x + y * y + z * z);
|
|
}
|
|
|
|
float player_crosshair_distance(Player *p1, Player *p2) {
|
|
State *s1 = &p1->state;
|
|
State *s2 = &p2->state;
|
|
float d = player_player_distance(p1, p2);
|
|
float vx, vy, vz;
|
|
get_sight_vector(s1->rx, s1->ry, &vx, &vy, &vz);
|
|
vx *= d; vy *= d; vz *= d;
|
|
float px, py, pz;
|
|
px = s1->x + vx; py = s1->y + vy; pz = s1->z + vz;
|
|
float x = s2->x - px;
|
|
float y = s2->y - py;
|
|
float z = s2->z - pz;
|
|
return sqrtf(x * x + y * y + z * z);
|
|
}
|
|
|
|
Player *player_crosshair(Player *player) {
|
|
Player *result = 0;
|
|
float threshold = RADIANS(5);
|
|
float best = 0;
|
|
for (int i = 0; i < g->player_count; i++) {
|
|
Player *other = g->players + i;
|
|
if (other == player) {
|
|
continue;
|
|
}
|
|
float p = player_crosshair_distance(player, other);
|
|
float d = player_player_distance(player, other);
|
|
if (d < 96 && p / d < threshold) {
|
|
if (best == 0 || d < best) {
|
|
best = d;
|
|
result = other;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Chunk *find_chunk(int p, int q) {
|
|
for (int i = 0; i < g->chunk_count; i++) {
|
|
Chunk *chunk = g->chunks + i;
|
|
if (chunk->p == p && chunk->q == q) {
|
|
return chunk;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int chunk_distance(Chunk *chunk, int p, int q) {
|
|
int dp = ABS(chunk->p - p);
|
|
int dq = ABS(chunk->q - q);
|
|
return MAX(dp, dq);
|
|
}
|
|
|
|
int chunk_visible(float planes[6][4], int p, int q, int miny, int maxy) {
|
|
int x = p * CHUNK_SIZE - 1;
|
|
int z = q * CHUNK_SIZE - 1;
|
|
int d = CHUNK_SIZE + 1;
|
|
float points[8][3] = {
|
|
{x + 0, miny, z + 0},
|
|
{x + d, miny, z + 0},
|
|
{x + 0, miny, z + d},
|
|
{x + d, miny, z + d},
|
|
{x + 0, maxy, z + 0},
|
|
{x + d, maxy, z + 0},
|
|
{x + 0, maxy, z + d},
|
|
{x + d, maxy, z + d}
|
|
};
|
|
int n = g->ortho ? 4 : 6;
|
|
for (int i = 0; i < n; i++) {
|
|
int in = 0;
|
|
int out = 0;
|
|
for (int j = 0; j < 8; j++) {
|
|
float d =
|
|
planes[i][0] * points[j][0] +
|
|
planes[i][1] * points[j][1] +
|
|
planes[i][2] * points[j][2] +
|
|
planes[i][3];
|
|
if (d < 0) {
|
|
out++;
|
|
}
|
|
else {
|
|
in++;
|
|
}
|
|
if (in && out) {
|
|
break;
|
|
}
|
|
}
|
|
if (in == 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int highest_block(float x, float z) {
|
|
int result = -1;
|
|
int nx = roundf(x);
|
|
int nz = roundf(z);
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
Map *map = &chunk->map;
|
|
MAP_FOR_EACH(map, ex, ey, ez, ew) {
|
|
if (is_obstacle(ew) && ex == nx && ez == nz) {
|
|
result = MAX(result, ey);
|
|
}
|
|
} END_MAP_FOR_EACH;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int _hit_test(
|
|
Map *map, float max_distance, int previous,
|
|
float x, float y, float z,
|
|
float vx, float vy, float vz,
|
|
int *hx, int *hy, int *hz)
|
|
{
|
|
int m = 32;
|
|
int px = 0;
|
|
int py = 0;
|
|
int pz = 0;
|
|
for (int i = 0; i < max_distance * m; i++) {
|
|
int nx = roundf(x);
|
|
int ny = roundf(y);
|
|
int nz = roundf(z);
|
|
if (nx != px || ny != py || nz != pz) {
|
|
int hw = map_get(map, nx, ny, nz);
|
|
if (hw > 0) {
|
|
if (previous) {
|
|
*hx = px; *hy = py; *hz = pz;
|
|
}
|
|
else {
|
|
*hx = nx; *hy = ny; *hz = nz;
|
|
}
|
|
return hw;
|
|
}
|
|
px = nx; py = ny; pz = nz;
|
|
}
|
|
x += vx / m; y += vy / m; z += vz / m;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int hit_test(
|
|
int previous, float x, float y, float z, float rx, float ry,
|
|
int *bx, int *by, int *bz)
|
|
{
|
|
int result = 0;
|
|
float best = 0;
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
float vx, vy, vz;
|
|
get_sight_vector(rx, ry, &vx, &vy, &vz);
|
|
for (int i = 0; i < g->chunk_count; i++) {
|
|
Chunk *chunk = g->chunks + i;
|
|
if (chunk_distance(chunk, p, q) > 1) {
|
|
continue;
|
|
}
|
|
int hx, hy, hz;
|
|
int hw = _hit_test(&chunk->map, 8, previous,
|
|
x, y, z, vx, vy, vz, &hx, &hy, &hz);
|
|
if (hw > 0) {
|
|
float d = sqrtf(
|
|
powf(hx - x, 2) + powf(hy - y, 2) + powf(hz - z, 2));
|
|
if (best == 0 || d < best) {
|
|
best = d;
|
|
*bx = hx; *by = hy; *bz = hz;
|
|
result = hw;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int hit_test_face(Player *player, int *x, int *y, int *z, int *face) {
|
|
State *s = &player->state;
|
|
int w = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, x, y, z);
|
|
if (is_obstacle(w)) {
|
|
int hx, hy, hz;
|
|
hit_test(1, s->x, s->y, s->z, s->rx, s->ry, &hx, &hy, &hz);
|
|
int dx = hx - *x;
|
|
int dy = hy - *y;
|
|
int dz = hz - *z;
|
|
if (dx == -1 && dy == 0 && dz == 0) {
|
|
*face = 0; return 1;
|
|
}
|
|
if (dx == 1 && dy == 0 && dz == 0) {
|
|
*face = 1; return 1;
|
|
}
|
|
if (dx == 0 && dy == 0 && dz == -1) {
|
|
*face = 2; return 1;
|
|
}
|
|
if (dx == 0 && dy == 0 && dz == 1) {
|
|
*face = 3; return 1;
|
|
}
|
|
if (dx == 0 && dy == 1 && dz == 0) {
|
|
int degrees = roundf(DEGREES(atan2f(s->x - hx, s->z - hz)));
|
|
if (degrees < 0) {
|
|
degrees += 360;
|
|
}
|
|
int top = ((degrees + 45) / 90) % 4;
|
|
*face = 4 + top; return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int collide(int height, float *x, float *y, float *z) {
|
|
int result = 0;
|
|
int p = chunked(*x);
|
|
int q = chunked(*z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (!chunk) {
|
|
return result;
|
|
}
|
|
Map *map = &chunk->map;
|
|
int nx = round(*x);
|
|
int ny = round(*y);
|
|
int nz = round(*z);
|
|
float px = *x - nx;
|
|
float py = *y - ny;
|
|
float pz = *z - nz;
|
|
float pad = 0.0625;
|
|
for (int dy = 0; dy < height; dy++) {
|
|
if (px < -pad && is_obstacle(map_get(map, nx - 1, ny - dy, nz))) {
|
|
*x = nx - pad;
|
|
}
|
|
if (px > pad && is_obstacle(map_get(map, nx + 1, ny - dy, nz))) {
|
|
*x = nx + pad;
|
|
}
|
|
if (py < -pad && is_obstacle(map_get(map, nx, ny - dy - 1, nz))) {
|
|
*y = ny - pad;
|
|
result = 1;
|
|
}
|
|
if (py > pad && is_obstacle(map_get(map, nx, ny - dy + 1, nz))) {
|
|
*y = ny + pad;
|
|
result = 1;
|
|
}
|
|
if (pz < -pad && is_obstacle(map_get(map, nx, ny - dy, nz - 1))) {
|
|
*z = nz - pad;
|
|
}
|
|
if (pz > pad && is_obstacle(map_get(map, nx, ny - dy, nz + 1))) {
|
|
*z = nz + pad;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int player_intersects_block(
|
|
int height,
|
|
float x, float y, float z,
|
|
int hx, int hy, int hz)
|
|
{
|
|
int nx = round(x);
|
|
int ny = round(y);
|
|
int nz = round(z);
|
|
for (int i = 0; i < height; i++) {
|
|
if (x == hx && y - i == hy && z == hz) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int _gen_sign_buffer(
|
|
GLfloat *data, float x, float y, float z, int face, const char *text)
|
|
{
|
|
static const int glyph_dx[8] = {0, 0, -1, 1, 1, 0, -1, 0};
|
|
static const int glyph_dz[8] = {1, -1, 0, 0, 0, -1, 0, 1};
|
|
static const int line_dx[8] = {0, 0, 0, 0, 0, 1, 0, -1};
|
|
static const int line_dy[8] = {-1, -1, -1, -1, 0, 0, 0, 0};
|
|
static const int line_dz[8] = {0, 0, 0, 0, 1, 0, -1, 0};
|
|
if (face < 0 || face >= 8) {
|
|
return 0;
|
|
}
|
|
int count = 0;
|
|
float max_width = 64;
|
|
float line_height = 1.25;
|
|
char lines[1024];
|
|
int rows = wrap(text, max_width, lines, 1024);
|
|
rows = MIN(rows, 5);
|
|
int dx = glyph_dx[face];
|
|
int dz = glyph_dz[face];
|
|
int ldx = line_dx[face];
|
|
int ldy = line_dy[face];
|
|
int ldz = line_dz[face];
|
|
float n = 1.0 / (max_width / 10);
|
|
float sx = x - n * (rows - 1) * (line_height / 2) * ldx;
|
|
float sy = y - n * (rows - 1) * (line_height / 2) * ldy;
|
|
float sz = z - n * (rows - 1) * (line_height / 2) * ldz;
|
|
char *key;
|
|
char *line = tokenize(lines, "\n", &key);
|
|
while (line) {
|
|
int length = strlen(line);
|
|
int line_width = string_width(line);
|
|
line_width = MIN(line_width, max_width);
|
|
float rx = sx - dx * line_width / max_width / 2;
|
|
float ry = sy;
|
|
float rz = sz - dz * line_width / max_width / 2;
|
|
for (int i = 0; i < length; i++) {
|
|
int width = char_width(line[i]);
|
|
line_width -= width;
|
|
if (line_width < 0) {
|
|
break;
|
|
}
|
|
rx += dx * width / max_width / 2;
|
|
rz += dz * width / max_width / 2;
|
|
if (line[i] != ' ') {
|
|
make_character_3d(
|
|
data + count * 30, rx, ry, rz, n / 2, face, line[i]);
|
|
count++;
|
|
}
|
|
rx += dx * width / max_width / 2;
|
|
rz += dz * width / max_width / 2;
|
|
}
|
|
sx += n * line_height * ldx;
|
|
sy += n * line_height * ldy;
|
|
sz += n * line_height * ldz;
|
|
line = tokenize(NULL, "\n", &key);
|
|
rows--;
|
|
if (rows <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void gen_sign_buffer(Chunk *chunk) {
|
|
SignList *signs = &chunk->signs;
|
|
|
|
// first pass - count characters
|
|
int max_faces = 0;
|
|
for (int i = 0; i < signs->size; i++) {
|
|
Sign *e = signs->data + i;
|
|
max_faces += strlen(e->text);
|
|
}
|
|
|
|
// second pass - generate geometry
|
|
GLfloat *data = malloc_faces(5, max_faces);
|
|
int faces = 0;
|
|
for (int i = 0; i < signs->size; i++) {
|
|
Sign *e = signs->data + i;
|
|
faces += _gen_sign_buffer(
|
|
data + faces * 30, e->x, e->y, e->z, e->face, e->text);
|
|
}
|
|
|
|
del_buffer(chunk->sign_buffer);
|
|
chunk->sign_buffer = gen_faces(5, faces, data);
|
|
chunk->sign_faces = faces;
|
|
}
|
|
|
|
int has_lights(Chunk *chunk) {
|
|
if (!SHOW_LIGHTS) {
|
|
return 0;
|
|
}
|
|
for (int dp = -1; dp <= 1; dp++) {
|
|
for (int dq = -1; dq <= 1; dq++) {
|
|
Chunk *other = chunk;
|
|
if (dp || dq) {
|
|
other = find_chunk(chunk->p + dp, chunk->q + dq);
|
|
}
|
|
if (!other) {
|
|
continue;
|
|
}
|
|
Map *map = &other->lights;
|
|
if (map->size) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void dirty_chunk(Chunk *chunk) {
|
|
chunk->dirty = 1;
|
|
if (has_lights(chunk)) {
|
|
for (int dp = -1; dp <= 1; dp++) {
|
|
for (int dq = -1; dq <= 1; dq++) {
|
|
Chunk *other = find_chunk(chunk->p + dp, chunk->q + dq);
|
|
if (other) {
|
|
other->dirty = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void occlusion(
|
|
char neighbors[27], char lights[27], float shades[27],
|
|
float ao[6][4], float light[6][4])
|
|
{
|
|
static const int lookup3[6][4][3] = {
|
|
{{0, 1, 3}, {2, 1, 5}, {6, 3, 7}, {8, 5, 7}},
|
|
{{18, 19, 21}, {20, 19, 23}, {24, 21, 25}, {26, 23, 25}},
|
|
{{6, 7, 15}, {8, 7, 17}, {24, 15, 25}, {26, 17, 25}},
|
|
{{0, 1, 9}, {2, 1, 11}, {18, 9, 19}, {20, 11, 19}},
|
|
{{0, 3, 9}, {6, 3, 15}, {18, 9, 21}, {24, 15, 21}},
|
|
{{2, 5, 11}, {8, 5, 17}, {20, 11, 23}, {26, 17, 23}}
|
|
};
|
|
static const int lookup4[6][4][4] = {
|
|
{{0, 1, 3, 4}, {1, 2, 4, 5}, {3, 4, 6, 7}, {4, 5, 7, 8}},
|
|
{{18, 19, 21, 22}, {19, 20, 22, 23}, {21, 22, 24, 25}, {22, 23, 25, 26}},
|
|
{{6, 7, 15, 16}, {7, 8, 16, 17}, {15, 16, 24, 25}, {16, 17, 25, 26}},
|
|
{{0, 1, 9, 10}, {1, 2, 10, 11}, {9, 10, 18, 19}, {10, 11, 19, 20}},
|
|
{{0, 3, 9, 12}, {3, 6, 12, 15}, {9, 12, 18, 21}, {12, 15, 21, 24}},
|
|
{{2, 5, 11, 14}, {5, 8, 14, 17}, {11, 14, 20, 23}, {14, 17, 23, 26}}
|
|
};
|
|
static const float curve[4] = {0.0, 0.25, 0.5, 0.75};
|
|
for (int i = 0; i < 6; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
int corner = neighbors[lookup3[i][j][0]];
|
|
int side1 = neighbors[lookup3[i][j][1]];
|
|
int side2 = neighbors[lookup3[i][j][2]];
|
|
int value = side1 && side2 ? 3 : corner + side1 + side2;
|
|
float shade_sum = 0;
|
|
float light_sum = 0;
|
|
int is_light = lights[13] == 15;
|
|
for (int k = 0; k < 4; k++) {
|
|
shade_sum += shades[lookup4[i][j][k]];
|
|
light_sum += lights[lookup4[i][j][k]];
|
|
}
|
|
if (is_light) {
|
|
light_sum = 15 * 4 * 10;
|
|
}
|
|
float total = curve[value] + shade_sum / 4.0;
|
|
ao[i][j] = MIN(total, 1.0);
|
|
light[i][j] = light_sum / 15.0 / 4.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define XZ_SIZE (CHUNK_SIZE * 3 + 2)
|
|
#define XZ_LO (CHUNK_SIZE)
|
|
#define XZ_HI (CHUNK_SIZE * 2 + 1)
|
|
#define Y_SIZE 256 //Higher would be better
|
|
#define XYZ(x, y, z) ((y) * XZ_SIZE * XZ_SIZE + (x) * XZ_SIZE + (z))
|
|
#define XZ(x, z) ((x) * XZ_SIZE + (z))
|
|
|
|
|
|
int map_get_block(int x, int y, int z) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
Map *map = &chunk->map;
|
|
return map_get(map, x, y, z);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void light_fill(
|
|
char *opaque, char *light,
|
|
int x, int y, int z, int w, int force)
|
|
{
|
|
if (x + w < XZ_LO || z + w < XZ_LO) {
|
|
return;
|
|
}
|
|
if (x - w > XZ_HI || z - w > XZ_HI) {
|
|
return;
|
|
}
|
|
if (y < 0 || y >= Y_SIZE) {
|
|
return;
|
|
}
|
|
if (light[XYZ(x, y, z)] >= w) {
|
|
return;
|
|
}
|
|
if (!force && opaque[XYZ(x, y, z)]) {
|
|
return;
|
|
}
|
|
light[XYZ(x, y, z)] = w--;
|
|
light_fill(opaque, light, x - 1, y, z, w, 0);
|
|
light_fill(opaque, light, x + 1, y, z, w, 0);
|
|
light_fill(opaque, light, x, y - 1, z, w, 0);
|
|
light_fill(opaque, light, x, y + 1, z, w, 0);
|
|
light_fill(opaque, light, x, y, z - 1, w, 0);
|
|
light_fill(opaque, light, x, y, z + 1, w, 0);
|
|
}
|
|
|
|
int umap_get_block(Map *map, int x, int y, int z) { //optimized function for use during chunk generation
|
|
return map_get(map, x, y, z);
|
|
}
|
|
|
|
void compute_chunk(WorkerItem *item) {
|
|
char *opaque = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char));
|
|
char *visible = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char));
|
|
char *transparent = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char));
|
|
char *light = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char));
|
|
char *highest = (char *)calloc(XZ_SIZE * XZ_SIZE, sizeof(char));
|
|
|
|
int ox = item->p * CHUNK_SIZE - CHUNK_SIZE - 1;
|
|
int oy = -1;
|
|
int oz = item->q * CHUNK_SIZE - CHUNK_SIZE - 1;
|
|
|
|
// check for lights
|
|
int has_light = 0;
|
|
if (SHOW_LIGHTS) {
|
|
for (int a = 0; a < 3; a++) {
|
|
for (int b = 0; b < 3; b++) {
|
|
Map *map = item->light_maps[a][b];
|
|
if (map && map->size) {
|
|
has_light = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// populate opaque array
|
|
for (int a = 0; a < 3; a++) {
|
|
for (int b = 0; b < 3; b++) {
|
|
Map *map = item->block_maps[a][b];
|
|
if (!map) {
|
|
continue;
|
|
}
|
|
MAP_FOR_EACH(map, ex, ey, ez, ew) {
|
|
int x = ex - ox;
|
|
int y = ey - oy;
|
|
int z = ez - oz;
|
|
int w = ew;
|
|
|
|
// TODO: this should be unnecessary (but as long it works :3)
|
|
if (x < 0 || y < 0 || z < 0) {
|
|
continue;
|
|
}
|
|
if (x >= XZ_SIZE || y >= Y_SIZE || z >= XZ_SIZE) {
|
|
continue;
|
|
}
|
|
// END TODO
|
|
opaque[XYZ(x, y, z)] = !is_transparent(w);
|
|
transparent[XYZ(x, y, z)] = is_transparent(w); // dupe needed sorry
|
|
visible[XYZ(x, y, z)] = !is_invisible(w);
|
|
|
|
if (opaque[XYZ(x, y, z)]) {
|
|
highest[XZ(x, z)] = MAX(highest[XZ(x, z)], y);
|
|
}
|
|
} END_MAP_FOR_EACH;
|
|
}
|
|
}
|
|
|
|
// flood fill light intensities
|
|
if (has_light) {
|
|
for (int a = 0; a < 3; a++) {
|
|
for (int b = 0; b < 3; b++) {
|
|
Map *map = item->light_maps[a][b];
|
|
if (!map) {
|
|
continue;
|
|
}
|
|
MAP_FOR_EACH(map, ex, ey, ez, ew) {
|
|
int x = ex - ox;
|
|
int y = ey - oy;
|
|
int z = ez - oz;
|
|
light_fill(opaque, light, x, y, z, ew, 1);
|
|
} END_MAP_FOR_EACH;
|
|
}
|
|
}
|
|
}
|
|
|
|
Map *map = item->block_maps[1][1];
|
|
|
|
// count exposed faces
|
|
int miny = 256;
|
|
int maxy = 0;
|
|
int faces = 0;
|
|
MAP_FOR_EACH(map, ex, ey, ez, ew) {
|
|
if (ew <= 0) {
|
|
continue;
|
|
}
|
|
|
|
int x = ex - ox;
|
|
int y = ey - oy;
|
|
int z = ez - oz;
|
|
|
|
|
|
int f1 = !opaque[XYZ(x - 1, y, z)];
|
|
int f2 = !opaque[XYZ(x + 1, y, z)];
|
|
int f3 = !opaque[XYZ(x, y + 1, z)];
|
|
int f4 = !opaque[XYZ(x, y - 1, z)] && (ey > 0);
|
|
int f5 = !opaque[XYZ(x, y, z - 1)];
|
|
int f6 = !opaque[XYZ(x, y, z + 1)];
|
|
|
|
int v1 = !transparent[XYZ(x - 1, y, z)];
|
|
int v2 = !transparent[XYZ(x + 1, y, z)];
|
|
int v3 = !transparent[XYZ(x, y + 1, z)];
|
|
int v4 = !transparent[XYZ(x, y - 1, z)] && (ey > 0);
|
|
int v5 = !transparent[XYZ(x, y, z - 1)];
|
|
int v6 = !transparent[XYZ(x, y, z + 1)];
|
|
|
|
int i1 = visible[XYZ(x - 1, y, z)];
|
|
int i2 = visible[XYZ(x + 1, y, z)];
|
|
int i3 = visible[XYZ(x, y + 1, z)];
|
|
int i4 = visible[XYZ(x, y - 1, z)] && (ey > 0);
|
|
int i5 = visible[XYZ(x, y, z - 1)];
|
|
int i6 = visible[XYZ(x, y, z + 1)];
|
|
|
|
if (is_transparent(ew) && !is_invisible(ew)) {
|
|
if (v1 == 0) {
|
|
f1 = 0;
|
|
} else {
|
|
f1 = 1;
|
|
}
|
|
if (v2 == 0) {
|
|
f2 = 0;
|
|
} else {
|
|
f2 = 1;
|
|
}
|
|
|
|
if (v3 == 0) {
|
|
f3 = 0;
|
|
} else {
|
|
f3 = 1;
|
|
}
|
|
|
|
if (v4 == 0) {
|
|
f4 = 0;
|
|
} else {
|
|
f4 = 1;
|
|
}
|
|
|
|
|
|
if (v5 == 0) {
|
|
f5 = 0;
|
|
} else {
|
|
f5 = 1;
|
|
}
|
|
|
|
if (v6 == 0) {
|
|
f6 = 0;
|
|
} else {
|
|
f6 = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
//air check
|
|
if (i1 == 0) {
|
|
f1 = 1;
|
|
}
|
|
if (i2 == 0) {
|
|
f2 = 1;
|
|
}
|
|
|
|
if (i3 == 0) {
|
|
f3 = 1;
|
|
}
|
|
if (i4 == 0) {
|
|
f4 = 1;
|
|
}
|
|
|
|
if (i5 == 0) {
|
|
f5 = 1;
|
|
}
|
|
if (i6 == 0) {
|
|
f6 = 1;
|
|
}
|
|
*/
|
|
}
|
|
|
|
int total = f1 + f2 + f3 + f4 + f5 + f6;
|
|
|
|
if (total == 0) {
|
|
continue;
|
|
}
|
|
if (is_plant(ew)) {
|
|
total = 4;
|
|
}
|
|
miny = MIN(miny, ey);
|
|
maxy = MAX(maxy, ey);
|
|
faces += total;
|
|
} END_MAP_FOR_EACH;
|
|
|
|
// generate geometry
|
|
GLfloat *data = malloc_faces(10, faces);
|
|
int offset = 0;
|
|
MAP_FOR_EACH(map, ex, ey, ez, ew) {
|
|
if (ew <= 0) {
|
|
continue;
|
|
}
|
|
int x = ex - ox;
|
|
int y = ey - oy;
|
|
int z = ez - oz;
|
|
int f1 = !opaque[XYZ(x - 1, y, z)];
|
|
int f2 = !opaque[XYZ(x + 1, y, z)];
|
|
int f3 = !opaque[XYZ(x, y + 1, z)];
|
|
int f4 = !opaque[XYZ(x, y - 1, z)] && (ey > 0);
|
|
int f5 = !opaque[XYZ(x, y, z - 1)];
|
|
int f6 = !opaque[XYZ(x, y, z + 1)];
|
|
|
|
int v1 = !transparent[XYZ(x - 1, y, z)];
|
|
int v2 = !transparent[XYZ(x + 1, y, z)];
|
|
int v3 = !transparent[XYZ(x, y + 1, z)];
|
|
int v4 = !transparent[XYZ(x, y - 1, z)] && (ey > 0);
|
|
int v5 = !transparent[XYZ(x, y, z - 1)];
|
|
int v6 = !transparent[XYZ(x, y, z + 1)];
|
|
|
|
int i1 = visible[XYZ(x - 1, y, z)];
|
|
int i2 = visible[XYZ(x + 1, y, z)];
|
|
int i3 = visible[XYZ(x, y + 1, z)];
|
|
int i4 = visible[XYZ(x, y - 1, z)] && (ey > 0);
|
|
int i5 = visible[XYZ(x, y, z - 1)];
|
|
int i6 = visible[XYZ(x, y, z + 1)];
|
|
|
|
if (is_transparent(ew) && !is_invisible(ew)) {
|
|
if (v1 == 0) {
|
|
f1 = 0;
|
|
} else {
|
|
f1 = 1;
|
|
}
|
|
if (v2 == 0) {
|
|
f2 = 0;
|
|
} else {
|
|
f2 = 1;
|
|
}
|
|
|
|
if (v3 == 0) {
|
|
f3 = 0;
|
|
} else {
|
|
f3 = 1;
|
|
}
|
|
|
|
if (v4 == 0) {
|
|
f4 = 0;
|
|
} else {
|
|
f4 = 1;
|
|
}
|
|
|
|
|
|
if (v5 == 0) {
|
|
f5 = 0;
|
|
} else {
|
|
f5 = 1;
|
|
}
|
|
|
|
if (v6 == 0) {
|
|
f6 = 0;
|
|
} else {
|
|
f6 = 1;
|
|
}
|
|
}
|
|
|
|
int total = f1 + f2 + f3 + f4 + f5 + f6;
|
|
|
|
if (total == 0) {
|
|
continue;
|
|
}
|
|
char neighbors[27] = {0};
|
|
char lights[27] = {0};
|
|
float shades[27] = {0};
|
|
int index = 0;
|
|
for (int dx = -1; dx <= 1; dx++) {
|
|
for (int dy = -1; dy <= 1; dy++) {
|
|
for (int dz = -1; dz <= 1; dz++) {
|
|
neighbors[index] = opaque[XYZ(x + dx, y + dy, z + dz)];
|
|
lights[index] = light[XYZ(x + dx, y + dy, z + dz)];
|
|
shades[index] = 0;
|
|
if (y + dy <= highest[XZ(x + dx, z + dz)]) {
|
|
for (int oy = 0; oy < 8; oy++) {
|
|
if (opaque[XYZ(x + dx, y + dy + oy, z + dz)]) {
|
|
shades[index] = 1.0 - oy * 0.125;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
float ao[6][4];
|
|
float light[6][4];
|
|
|
|
occlusion(neighbors, lights, shades, ao, light);
|
|
|
|
if (is_plant(ew)) {
|
|
total = 4;
|
|
float min_ao = 1;
|
|
float max_light = 0;
|
|
for (int a = 0; a < 6; a++) {
|
|
for (int b = 0; b < 4; b++) {
|
|
min_ao = MIN(min_ao, ao[a][b]);
|
|
max_light = MAX(max_light, light[a][b]);
|
|
}
|
|
}
|
|
float rotation = simplex2(ex, ez, 4, 0.5, 2) * 360;
|
|
make_plant(
|
|
data + offset, min_ao, max_light,
|
|
ex, ey, ez, 0.5, ew, rotation);
|
|
}
|
|
else if (is_noncube(ew) && noncube_type(ew) == NonCubeType_SLAB_LOWER) {
|
|
make_cube(
|
|
data + offset, ao, light,
|
|
f1, f2, f3, f4, f5, f6,
|
|
ex, ey, ez, 0.5, ew);
|
|
}
|
|
else {
|
|
make_cube(
|
|
data + offset, ao, light,
|
|
f1, f2, f3, f4, f5, f6,
|
|
ex, ey, ez, 0.5, ew);
|
|
}
|
|
offset += total * 60;
|
|
} END_MAP_FOR_EACH;
|
|
|
|
free(opaque);
|
|
free(visible);
|
|
free(transparent);
|
|
free(light);
|
|
free(highest);
|
|
|
|
item->miny = miny;
|
|
item->maxy = maxy;
|
|
item->faces = faces;
|
|
item->data = data;
|
|
}
|
|
|
|
void generate_chunk(Chunk *chunk, WorkerItem *item) {
|
|
chunk->miny = item->miny;
|
|
chunk->maxy = item->maxy;
|
|
chunk->faces = item->faces;
|
|
del_buffer(chunk->buffer);
|
|
chunk->buffer = gen_faces(10, item->faces, item->data);
|
|
gen_sign_buffer(chunk);
|
|
}
|
|
|
|
void gen_chunk_buffer(Chunk *chunk) {
|
|
WorkerItem _item;
|
|
WorkerItem *item = &_item;
|
|
item->p = chunk->p;
|
|
item->q = chunk->q;
|
|
for (int dp = -1; dp <= 1; dp++) {
|
|
for (int dq = -1; dq <= 1; dq++) {
|
|
Chunk *other = chunk;
|
|
if (dp || dq) {
|
|
other = find_chunk(chunk->p + dp, chunk->q + dq);
|
|
}
|
|
if (other) {
|
|
item->block_maps[dp + 1][dq + 1] = &other->map;
|
|
item->light_maps[dp + 1][dq + 1] = &other->lights;
|
|
}
|
|
else {
|
|
item->block_maps[dp + 1][dq + 1] = 0;
|
|
item->light_maps[dp + 1][dq + 1] = 0;
|
|
}
|
|
}
|
|
}
|
|
compute_chunk(item);
|
|
generate_chunk(chunk, item);
|
|
chunk->dirty = 0;
|
|
}
|
|
|
|
void map_set_func(int x, int y, int z, int w, void *arg) {
|
|
Map *map = (Map *)arg;
|
|
map_set(map, x, y, z, w);
|
|
}
|
|
|
|
void load_chunk(WorkerItem *item) {
|
|
int p = item->p;
|
|
int q = item->q;
|
|
Map *block_map = item->block_maps[1][1];
|
|
Map *light_map = item->light_maps[1][1];
|
|
create_world(p, q, map_set_func, block_map);
|
|
db_load_blocks(block_map, p, q);
|
|
db_load_lights(light_map, p, q);
|
|
}
|
|
|
|
void request_chunk(int p, int q) {
|
|
int key = db_get_key(p, q);
|
|
client_chunk(p, q, key);
|
|
}
|
|
|
|
void init_chunk(Chunk *chunk, int p, int q) {
|
|
chunk->p = p;
|
|
chunk->q = q;
|
|
chunk->faces = 0;
|
|
chunk->sign_faces = 0;
|
|
chunk->buffer = 0;
|
|
chunk->sign_buffer = 0;
|
|
dirty_chunk(chunk);
|
|
SignList *signs = &chunk->signs;
|
|
sign_list_alloc(signs, 16);
|
|
db_load_signs(signs, p, q);
|
|
Map *block_map = &chunk->map;
|
|
Map *light_map = &chunk->lights;
|
|
int dx = p * CHUNK_SIZE - 1;
|
|
int dy = 0;
|
|
int dz = q * CHUNK_SIZE - 1;
|
|
map_alloc(block_map, dx, dy, dz, 0x7fff);
|
|
map_alloc(light_map, dx, dy, dz, 0xf);
|
|
}
|
|
|
|
void create_chunk(Chunk *chunk, int p, int q) {
|
|
init_chunk(chunk, p, q);
|
|
|
|
WorkerItem _item;
|
|
WorkerItem *item = &_item;
|
|
item->p = chunk->p;
|
|
item->q = chunk->q;
|
|
item->block_maps[1][1] = &chunk->map;
|
|
item->light_maps[1][1] = &chunk->lights;
|
|
load_chunk(item);
|
|
|
|
request_chunk(p, q);
|
|
}
|
|
|
|
void delete_chunks() {
|
|
int count = g->chunk_count;
|
|
State *s1 = &g->players->state;
|
|
State *s2 = &(g->players + g->observe1)->state;
|
|
State *s3 = &(g->players + g->observe2)->state;
|
|
State *states[3] = {s1, s2, s3};
|
|
for (int i = 0; i < count; i++) {
|
|
Chunk *chunk = g->chunks + i;
|
|
int delete = 1;
|
|
for (int j = 0; j < 3; j++) {
|
|
State *s = states[j];
|
|
int p = chunked(s->x);
|
|
int q = chunked(s->z);
|
|
if (chunk_distance(chunk, p, q) < g->delete_radius) {
|
|
delete = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (delete) {
|
|
map_free(&chunk->map);
|
|
map_free(&chunk->lights);
|
|
sign_list_free(&chunk->signs);
|
|
del_buffer(chunk->buffer);
|
|
del_buffer(chunk->sign_buffer);
|
|
Chunk *other = g->chunks + (--count);
|
|
memcpy(chunk, other, sizeof(Chunk));
|
|
}
|
|
}
|
|
g->chunk_count = count;
|
|
}
|
|
|
|
void delete_all_chunks() {
|
|
for (int i = 0; i < g->chunk_count; i++) {
|
|
Chunk *chunk = g->chunks + i;
|
|
map_free(&chunk->map);
|
|
map_free(&chunk->lights);
|
|
sign_list_free(&chunk->signs);
|
|
//printf("deleting: %d, %d\n", (int) chunk->buffer, (int) chunk->sign_buffer);
|
|
del_buffer(chunk->buffer);
|
|
del_buffer(chunk->sign_buffer);
|
|
}
|
|
g->chunk_count = 0;
|
|
}
|
|
|
|
void check_workers() {
|
|
for (int i = 0; i < WORKERS; i++) {
|
|
Worker *worker = g->workers + i;
|
|
mtx_lock(&worker->mtx);
|
|
if (worker->state == WORKER_DONE) {
|
|
WorkerItem *item = &worker->item;
|
|
Chunk *chunk = find_chunk(item->p, item->q);
|
|
if (chunk) {
|
|
if (item->load) {
|
|
Map *block_map = item->block_maps[1][1];
|
|
Map *light_map = item->light_maps[1][1];
|
|
map_free(&chunk->map);
|
|
map_free(&chunk->lights);
|
|
map_copy(&chunk->map, block_map);
|
|
map_copy(&chunk->lights, light_map);
|
|
request_chunk(item->p, item->q);
|
|
}
|
|
generate_chunk(chunk, item);
|
|
}
|
|
for (int a = 0; a < 3; a++) {
|
|
for (int b = 0; b < 3; b++) {
|
|
Map *block_map = item->block_maps[a][b];
|
|
Map *light_map = item->light_maps[a][b];
|
|
if (block_map) {
|
|
map_free(block_map);
|
|
free(block_map);
|
|
}
|
|
if (light_map) {
|
|
map_free(light_map);
|
|
free(light_map);
|
|
}
|
|
}
|
|
}
|
|
worker->state = WORKER_IDLE;
|
|
}
|
|
mtx_unlock(&worker->mtx);
|
|
}
|
|
}
|
|
|
|
void force_chunks(Player *player) {
|
|
State *s = &player->state;
|
|
int p = chunked(s->x);
|
|
int q = chunked(s->z);
|
|
int r = 1;
|
|
for (int dp = -r; dp <= r; dp++) {
|
|
for (int dq = -r; dq <= r; dq++) {
|
|
int a = p + dp;
|
|
int b = q + dq;
|
|
Chunk *chunk = find_chunk(a, b);
|
|
if (chunk) {
|
|
if (chunk->dirty) {
|
|
gen_chunk_buffer(chunk);
|
|
}
|
|
}
|
|
else if (g->chunk_count < MAX_CHUNKS) {
|
|
chunk = g->chunks + g->chunk_count++;
|
|
create_chunk(chunk, a, b);
|
|
gen_chunk_buffer(chunk);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ensure_chunks_worker(Player *player, Worker *worker) {
|
|
State *s = &player->state;
|
|
float matrix[16];
|
|
set_matrix_3d(
|
|
matrix, g->width, g->height,
|
|
s->x, s->y, s->z, s->rx, s->ry, g->fov, g->ortho, g->render_radius);
|
|
float planes[6][4];
|
|
frustum_planes(planes, g->render_radius, matrix);
|
|
int p = chunked(s->x);
|
|
int q = chunked(s->z);
|
|
int r = g->create_radius;
|
|
int start = 0x0fffffff;
|
|
int best_score = start;
|
|
int best_a = 0;
|
|
int best_b = 0;
|
|
for (int dp = -r; dp <= r; dp++) {
|
|
for (int dq = -r; dq <= r; dq++) {
|
|
int a = p + dp;
|
|
int b = q + dq;
|
|
int index = (ABS(a) ^ ABS(b)) % WORKERS;
|
|
if (index != worker->index) {
|
|
continue;
|
|
}
|
|
Chunk *chunk = find_chunk(a, b);
|
|
if (chunk && !chunk->dirty) {
|
|
continue;
|
|
}
|
|
int distance = MAX(ABS(dp), ABS(dq));
|
|
int invisible = !chunk_visible(planes, a, b, 0, 256);
|
|
int priority = 0;
|
|
if (chunk) {
|
|
priority = chunk->buffer && chunk->dirty;
|
|
}
|
|
int score = (invisible << 24) | (priority << 16) | distance;
|
|
if (score < best_score) {
|
|
best_score = score;
|
|
best_a = a;
|
|
best_b = b;
|
|
}
|
|
}
|
|
}
|
|
if (best_score == start) {
|
|
return;
|
|
}
|
|
int a = best_a;
|
|
int b = best_b;
|
|
int load = 0;
|
|
Chunk *chunk = find_chunk(a, b);
|
|
if (!chunk) {
|
|
load = 1;
|
|
if (g->chunk_count < MAX_CHUNKS) {
|
|
chunk = g->chunks + g->chunk_count++;
|
|
init_chunk(chunk, a, b);
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
WorkerItem *item = &worker->item;
|
|
item->p = chunk->p;
|
|
item->q = chunk->q;
|
|
item->load = load;
|
|
for (int dp = -1; dp <= 1; dp++) {
|
|
for (int dq = -1; dq <= 1; dq++) {
|
|
Chunk *other = chunk;
|
|
if (dp || dq) {
|
|
other = find_chunk(chunk->p + dp, chunk->q + dq);
|
|
}
|
|
if (other) {
|
|
Map *block_map = malloc(sizeof(Map));
|
|
map_copy(block_map, &other->map);
|
|
Map *light_map = malloc(sizeof(Map));
|
|
map_copy(light_map, &other->lights);
|
|
item->block_maps[dp + 1][dq + 1] = block_map;
|
|
item->light_maps[dp + 1][dq + 1] = light_map;
|
|
}
|
|
else {
|
|
item->block_maps[dp + 1][dq + 1] = 0;
|
|
item->light_maps[dp + 1][dq + 1] = 0;
|
|
}
|
|
}
|
|
}
|
|
chunk->dirty = 0;
|
|
worker->state = WORKER_BUSY;
|
|
cnd_signal(&worker->cnd);
|
|
}
|
|
|
|
void ensure_chunks(Player *player) {
|
|
check_workers();
|
|
force_chunks(player);
|
|
for (int i = 0; i < WORKERS; i++) {
|
|
Worker *worker = g->workers + i;
|
|
mtx_lock(&worker->mtx);
|
|
if (worker->state == WORKER_IDLE) {
|
|
ensure_chunks_worker(player, worker);
|
|
}
|
|
mtx_unlock(&worker->mtx);
|
|
}
|
|
}
|
|
|
|
int worker_run(void *arg) {
|
|
Worker *worker = (Worker *)arg;
|
|
int running = 1;
|
|
while (running) {
|
|
mtx_lock(&worker->mtx);
|
|
while (worker->state != WORKER_BUSY) {
|
|
cnd_wait(&worker->cnd, &worker->mtx);
|
|
}
|
|
mtx_unlock(&worker->mtx);
|
|
WorkerItem *item = &worker->item;
|
|
if (item->load) {
|
|
load_chunk(item);
|
|
}
|
|
compute_chunk(item);
|
|
mtx_lock(&worker->mtx);
|
|
worker->state = WORKER_DONE;
|
|
mtx_unlock(&worker->mtx);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void unset_sign(int x, int y, int z) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
SignList *signs = &chunk->signs;
|
|
if (sign_list_remove_all(signs, x, y, z)) {
|
|
chunk->dirty = 1;
|
|
db_delete_signs(x, y, z);
|
|
}
|
|
}
|
|
else {
|
|
db_delete_signs(x, y, z);
|
|
}
|
|
}
|
|
|
|
void unset_sign_face(int x, int y, int z, int face) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
SignList *signs = &chunk->signs;
|
|
if (sign_list_remove(signs, x, y, z, face)) {
|
|
chunk->dirty = 1;
|
|
db_delete_sign(x, y, z, face);
|
|
}
|
|
}
|
|
else {
|
|
db_delete_sign(x, y, z, face);
|
|
}
|
|
}
|
|
|
|
void _set_sign(
|
|
int p, int q, int x, int y, int z, int face, const char *text, int dirty)
|
|
{
|
|
if (strlen(text) == 0) {
|
|
unset_sign_face(x, y, z, face);
|
|
return;
|
|
}
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
SignList *signs = &chunk->signs;
|
|
sign_list_add(signs, x, y, z, face, text);
|
|
if (dirty) {
|
|
chunk->dirty = 1;
|
|
}
|
|
}
|
|
db_insert_sign(p, q, x, y, z, face, text);
|
|
}
|
|
|
|
void set_sign(int x, int y, int z, int face, const char *text) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
_set_sign(p, q, x, y, z, face, text, 1);
|
|
client_sign(x, y, z, face, text);
|
|
}
|
|
|
|
void toggle_light(int x, int y, int z) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
Map *map = &chunk->lights;
|
|
int w = map_get(map, x, y, z) ? 0 : 15;
|
|
map_set(map, x, y, z, w);
|
|
db_insert_light(p, q, x, y, z, w);
|
|
client_light(x, y, z, w);
|
|
dirty_chunk(chunk);
|
|
}
|
|
}
|
|
|
|
void set_light(int p, int q, int x, int y, int z, int w) {
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
Map *map = &chunk->lights;
|
|
if (map_set(map, x, y, z, w)) {
|
|
dirty_chunk(chunk);
|
|
db_insert_light(p, q, x, y, z, w);
|
|
}
|
|
}
|
|
else {
|
|
db_insert_light(p, q, x, y, z, w);
|
|
}
|
|
}
|
|
|
|
void _set_block(int p, int q, int x, int y, int z, int w, int dirty) {
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
Map *map = &chunk->map;
|
|
if (map_set(map, x, y, z, w)) {
|
|
if (dirty) {
|
|
dirty_chunk(chunk);
|
|
}
|
|
db_insert_block(p, q, x, y, z, w);
|
|
}
|
|
}
|
|
else {
|
|
db_insert_block(p, q, x, y, z, w);
|
|
}
|
|
if (w == 0 && chunked(x) == p && chunked(z) == q) {
|
|
unset_sign(x, y, z);
|
|
set_light(p, q, x, y, z, 0);
|
|
}
|
|
}
|
|
|
|
void set_block(int x, int y, int z, int w) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
_set_block(p, q, x, y, z, w, 1);
|
|
for (int dx = -1; dx <= 1; dx++) {
|
|
for (int dz = -1; dz <= 1; dz++) {
|
|
if (dx == 0 && dz == 0) {
|
|
continue;
|
|
}
|
|
if (dx && chunked(x + dx) == p) {
|
|
continue;
|
|
}
|
|
if (dz && chunked(z + dz) == q) {
|
|
continue;
|
|
}
|
|
_set_block(p + dx, q + dz, x, y, z, -w, 1);
|
|
}
|
|
}
|
|
client_block(x, y, z, w);
|
|
}
|
|
|
|
void record_block(int x, int y, int z, int w) {
|
|
memcpy(&g->block1, &g->block0, sizeof(Block));
|
|
g->block0.x = x;
|
|
g->block0.y = y;
|
|
g->block0.z = z;
|
|
g->block0.w = w;
|
|
}
|
|
|
|
// Get_block was here
|
|
int get_block(int x, int y, int z) {
|
|
int p = chunked(x);
|
|
int q = chunked(z);
|
|
Chunk *chunk = find_chunk(p, q);
|
|
if (chunk) {
|
|
Map *map = &chunk->map;
|
|
return map_get(map, x, y, z);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void builder_block(int x, int y, int z, int w) {
|
|
if (y <= 0 || y >= BUILD_HEIGHT_LIMIT) {
|
|
return;
|
|
}
|
|
if (is_destructable(get_block(x, y, z))) {
|
|
set_block(x, y, z, 0);
|
|
}
|
|
if (w) {
|
|
set_block(x, y, z, w);
|
|
}
|
|
}
|
|
|
|
int render_chunks(Attrib *attrib, Player *player) {
|
|
int result = 0;
|
|
State *s = &player->state;
|
|
ensure_chunks(player);
|
|
int p = chunked(s->x);
|
|
int q = chunked(s->z);
|
|
float light = get_daylight();
|
|
float matrix[16];
|
|
set_matrix_3d(
|
|
matrix, g->width, g->height,
|
|
s->x, s->y, s->z, s->rx, s->ry, g->fov, g->ortho, g->render_radius);
|
|
float planes[6][4];
|
|
frustum_planes(planes, g->render_radius, matrix);
|
|
glUseProgram(attrib->program);
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
glUniform3f(attrib->camera, s->x, s->y, s->z);
|
|
glUniform1i(attrib->sampler, 0);
|
|
glUniform1i(attrib->extra1, 2);
|
|
glUniform1f(attrib->extra2, light);
|
|
glUniform1f(attrib->extra3, g->render_radius * CHUNK_SIZE);
|
|
glUniform1i(attrib->extra4, g->ortho);
|
|
glUniform1f(attrib->timer, time_of_day());
|
|
|
|
SkyColor *c = &g->sky_color;
|
|
glUniform3f(attrib->extra5, c->r, c->g, c->b);
|
|
|
|
for (int i = 0; i < g->chunk_count; i++) {
|
|
Chunk *chunk = g->chunks + i;
|
|
if (chunk_distance(chunk, p, q) > g->render_radius) {
|
|
continue;
|
|
}
|
|
if (!chunk_visible(
|
|
planes, chunk->p, chunk->q, chunk->miny, chunk->maxy))
|
|
{
|
|
continue;
|
|
}
|
|
draw_chunk(attrib, chunk);
|
|
result += chunk->faces;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void render_signs(Attrib *attrib, Player *player) {
|
|
State *s = &player->state;
|
|
int p = chunked(s->x);
|
|
int q = chunked(s->z);
|
|
float matrix[16];
|
|
set_matrix_3d(
|
|
matrix, g->width, g->height,
|
|
s->x, s->y, s->z, s->rx, s->ry, g->fov, g->ortho, g->render_radius);
|
|
float planes[6][4];
|
|
frustum_planes(planes, g->render_radius, matrix);
|
|
glUseProgram(attrib->program);
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
glUniform1i(attrib->sampler, 3);
|
|
glUniform1i(attrib->extra1, 1);
|
|
for (int i = 0; i < g->chunk_count; i++) {
|
|
Chunk *chunk = g->chunks + i;
|
|
if (chunk_distance(chunk, p, q) > g->sign_radius) {
|
|
continue;
|
|
}
|
|
if (!chunk_visible(
|
|
planes, chunk->p, chunk->q, chunk->miny, chunk->maxy))
|
|
{
|
|
continue;
|
|
}
|
|
draw_signs(attrib, chunk);
|
|
}
|
|
}
|
|
|
|
void render_sign(Attrib *attrib, Player *player) {
|
|
if (!g->typing || g->typing_buffer[0] != CRAFT_KEY_SIGN) {
|
|
return;
|
|
}
|
|
int x, y, z, face;
|
|
if (!hit_test_face(player, &x, &y, &z, &face)) {
|
|
return;
|
|
}
|
|
State *s = &player->state;
|
|
float matrix[16];
|
|
set_matrix_3d(
|
|
matrix, g->width, g->height,
|
|
s->x, s->y, s->z, s->rx, s->ry, g->fov, g->ortho, g->render_radius);
|
|
glUseProgram(attrib->program);
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
glUniform1i(attrib->sampler, 3);
|
|
glUniform1i(attrib->extra1, 1);
|
|
char text[MAX_SIGN_LENGTH];
|
|
strncpy(text, g->typing_buffer + 1, MAX_SIGN_LENGTH);
|
|
text[MAX_SIGN_LENGTH - 1] = '\0';
|
|
GLfloat *data = malloc_faces(5, strlen(text));
|
|
int length = _gen_sign_buffer(data, x, y, z, face, text);
|
|
GLuint buffer = gen_faces(5, length, data);
|
|
draw_sign(attrib, buffer, length);
|
|
del_buffer(buffer);
|
|
}
|
|
|
|
void render_players(Attrib *attrib, Player *player) {
|
|
State *s = &player->state;
|
|
float matrix[16];
|
|
set_matrix_3d(
|
|
matrix, g->width, g->height,
|
|
s->x, s->y, s->z, s->rx, s->ry, g->fov, g->ortho, g->render_radius);
|
|
glUseProgram(attrib->program);
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
glUniform3f(attrib->camera, s->x, s->y, s->z);
|
|
glUniform1i(attrib->sampler, 0);
|
|
glUniform1f(attrib->timer, time_of_day());
|
|
for (int i = 0; i < g->player_count; i++) {
|
|
Player *other = g->players + i;
|
|
if (other != player) {
|
|
draw_player(attrib, other);
|
|
}
|
|
}
|
|
}
|
|
|
|
void render_sky(Attrib *attrib, Player *player, GLuint buffer) {
|
|
State *s = &player->state;
|
|
float matrix[16];
|
|
set_matrix_3d(
|
|
matrix, g->width, g->height,
|
|
0, 0, 0, s->rx, s->ry, g->fov, 0, g->render_radius);
|
|
glUseProgram(attrib->program);
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
glUniform1i(attrib->sampler, 2);
|
|
glUniform1f(attrib->timer, time_of_day());
|
|
glUniform3f(attrib->extra1, 1.0f, 1.0f, 1.0f);
|
|
|
|
SkyColor *c = &g->sky_color;
|
|
glUniform3f(attrib->extra1, c->r, c->g, c->b);
|
|
|
|
draw_triangles_3d(attrib, buffer, 512 * 3);
|
|
}
|
|
|
|
void render_item(Attrib *attrib) {
|
|
float matrix[16];
|
|
glUseProgram(attrib->program);
|
|
set_matrix_item(matrix, g->width, g->height, g->scale + 1);
|
|
glUniform3f(attrib->camera, 0, 0, 5);
|
|
glUniform1i(attrib->sampler, 0);
|
|
glUniform1f(attrib->timer, time_of_day());
|
|
for(int i = 0; i < NUM_INVENTORY_VISIBLE; ++i) {
|
|
if(g->item_index + i >= item_count) {
|
|
break;
|
|
}
|
|
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
|
|
int w = items[g->item_index + i];
|
|
|
|
//if (g->inventory.count[items[g->item_index]] == 0) {
|
|
// w = 0;
|
|
//}
|
|
|
|
// TODO: g->inventory.count[items[g->item_index]] = 16;
|
|
|
|
if (is_plant(w)) {
|
|
GLuint buffer = gen_plant_buffer(0, 0, 0, 0.5, w);
|
|
draw_plant(attrib, buffer);
|
|
del_buffer(buffer);
|
|
}
|
|
else {
|
|
GLuint buffer = gen_cube_buffer(0, 0, 0, 0.5, w);
|
|
draw_cube(attrib, buffer);
|
|
del_buffer(buffer);
|
|
}
|
|
|
|
if(!i) {
|
|
set_matrix_item(matrix, g->width, g->height, g->scale);
|
|
matrix[12] += 0.08f;
|
|
matrix[13] += 0.1f;
|
|
}
|
|
|
|
matrix[13] += 0.25f;
|
|
}
|
|
}
|
|
|
|
void render_item_count(Attrib *attrib, float ts) {
|
|
const int buf_len = 4;
|
|
|
|
float pos = 15.0f;
|
|
for(int i = 0; i < NUM_INVENTORY_VISIBLE; ++i) {
|
|
if(g->item_index + i >= item_count) {
|
|
break;
|
|
}
|
|
|
|
char buf[buf_len];
|
|
snprintf(buf, buf_len, "%d\n", Inventory_getCount(&g->inventory, items[g->item_index + i]));
|
|
//snprintf(buf, buf_len, "%d\n", g->inventory.count[items[g->item_index]]);
|
|
|
|
render_text(attrib, ALIGN_CENTER, g->width - 20.0f, pos, ts, buf, g->width, g->height);
|
|
//printf("%d\n", g->width);
|
|
|
|
float ratio_to_hardcoded = (g->height / 768.0f);
|
|
if(i) {
|
|
pos += 96.0f * ratio_to_hardcoded;
|
|
} else {
|
|
pos += 140.0f * ratio_to_hardcoded;
|
|
}
|
|
}
|
|
}
|
|
|
|
void render_ui_texture(Attrib *attrib) {
|
|
float matrix[16];
|
|
glUseProgram(attrib->program);
|
|
set_matrix_item(matrix, g->width, g->height, g->scale + 1);
|
|
glUniform3f(attrib->camera, 0, 0, 5);
|
|
glUniform1i(attrib->sampler, 0);
|
|
glUniform1f(attrib->timer, time_of_day());
|
|
for(int i = 0; i < NUM_INVENTORY_VISIBLE; ++i) {
|
|
if(g->item_index + i >= item_count) {
|
|
break;
|
|
}
|
|
|
|
glUniformMatrix4fv(attrib->matrix, 1, GL_FALSE, matrix);
|
|
|
|
int w = items[g->item_index + i];
|
|
if (is_plant(w)) {
|
|
GLuint buffer = gen_plant_buffer(0, 0, 0, 0.5, w);
|
|
draw_plant(attrib, buffer);
|
|
del_buffer(buffer);
|
|
}
|
|
else {
|
|
GLuint buffer = gen_cube_buffer(0, 0, 0, 0.5, w);
|
|
draw_cube(attrib, buffer);
|
|
del_buffer(buffer);
|
|
}
|
|
|
|
if(!i) {
|
|
set_matrix_item(matrix, g->width, g->height, g->scale);
|
|
matrix[12] += 0.08f;
|
|
matrix[13] += 0.1f;
|
|
}
|
|
|
|
matrix[13] += 0.25f;
|
|
}
|
|
}
|
|
|
|
void add_message(const char *text) {
|
|
//printf("%s\n", text);
|
|
snprintf(
|
|
g->messages[g->message_index], MAX_TEXT_LENGTH, "%s", text);
|
|
g->message_index = (g->message_index + 1) % MAX_MESSAGES;
|
|
}
|
|
|
|
void login() {
|
|
char username[128] = {0};
|
|
char identity_token[128] = {0};
|
|
char access_token[128] = {0};
|
|
if (db_auth_get_selected(username, 128, identity_token, 128)) {
|
|
printf("Contacting login server for username: %s\n", username);
|
|
if (get_access_token(
|
|
access_token, 128, username, identity_token))
|
|
{
|
|
printf("Successfully authenticated with the login server\n");
|
|
client_login(username, access_token);
|
|
}
|
|
else {
|
|
printf("Failed to authenticate with the login server\n");
|
|
client_login("", "");
|
|
}
|
|
}
|
|
else {
|
|
printf("Logging in anonymously\n");
|
|
client_login("", "");
|
|
}
|
|
}
|
|
|
|
void copy() {
|
|
memcpy(&g->copy0, &g->block0, sizeof(Block));
|
|
memcpy(&g->copy1, &g->block1, sizeof(Block));
|
|
}
|
|
|
|
void paste() {
|
|
Block *c1 = &g->copy1;
|
|
Block *c2 = &g->copy0;
|
|
Block *p1 = &g->block1;
|
|
Block *p2 = &g->block0;
|
|
int scx = SIGN(c2->x - c1->x);
|
|
int scz = SIGN(c2->z - c1->z);
|
|
int spx = SIGN(p2->x - p1->x);
|
|
int spz = SIGN(p2->z - p1->z);
|
|
int oy = p1->y - c1->y;
|
|
int dx = ABS(c2->x - c1->x);
|
|
int dz = ABS(c2->z - c1->z);
|
|
for (int y = 0; y < 256; y++) {
|
|
for (int x = 0; x <= dx; x++) {
|
|
for (int z = 0; z <= dz; z++) {
|
|
int w = get_block(c1->x + x * scx, y, c1->z + z * scz);
|
|
builder_block(p1->x + x * spx, y + oy, p1->z + z * spz, w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void array(Block *b1, Block *b2, int xc, int yc, int zc) {
|
|
if (b1->w != b2->w) {
|
|
return;
|
|
}
|
|
int w = b1->w;
|
|
int dx = b2->x - b1->x;
|
|
int dy = b2->y - b1->y;
|
|
int dz = b2->z - b1->z;
|
|
xc = dx ? xc : 1;
|
|
yc = dy ? yc : 1;
|
|
zc = dz ? zc : 1;
|
|
for (int i = 0; i < xc; i++) {
|
|
int x = b1->x + dx * i;
|
|
for (int j = 0; j < yc; j++) {
|
|
int y = b1->y + dy * j;
|
|
for (int k = 0; k < zc; k++) {
|
|
int z = b1->z + dz * k;
|
|
builder_block(x, y, z, w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cube(Block *b1, Block *b2, int fill) {
|
|
if (b1->w != b2->w) {
|
|
return;
|
|
}
|
|
int w = b1->w;
|
|
int x1 = MIN(b1->x, b2->x);
|
|
int y1 = MIN(b1->y, b2->y);
|
|
int z1 = MIN(b1->z, b2->z);
|
|
int x2 = MAX(b1->x, b2->x);
|
|
int y2 = MAX(b1->y, b2->y);
|
|
int z2 = MAX(b1->z, b2->z);
|
|
int a = (x1 == x2) + (y1 == y2) + (z1 == z2);
|
|
for (int x = x1; x <= x2; x++) {
|
|
for (int y = y1; y <= y2; y++) {
|
|
for (int z = z1; z <= z2; z++) {
|
|
if (!fill) {
|
|
int n = 0;
|
|
n += x == x1 || x == x2;
|
|
n += y == y1 || y == y2;
|
|
n += z == z1 || z == z2;
|
|
if (n <= a) {
|
|
continue;
|
|
}
|
|
}
|
|
builder_block(x, y, z, w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void sphere(Block *center, int radius, int fill, int fx, int fy, int fz) {
|
|
static const float offsets[8][3] = {
|
|
{-0.5, -0.5, -0.5},
|
|
{-0.5, -0.5, 0.5},
|
|
{-0.5, 0.5, -0.5},
|
|
{-0.5, 0.5, 0.5},
|
|
{0.5, -0.5, -0.5},
|
|
{0.5, -0.5, 0.5},
|
|
{0.5, 0.5, -0.5},
|
|
{0.5, 0.5, 0.5}
|
|
};
|
|
int cx = center->x;
|
|
int cy = center->y;
|
|
int cz = center->z;
|
|
int w = center->w;
|
|
for (int x = cx - radius; x <= cx + radius; x++) {
|
|
if (fx && x != cx) {
|
|
continue;
|
|
}
|
|
for (int y = cy - radius; y <= cy + radius; y++) {
|
|
if (fy && y != cy) {
|
|
continue;
|
|
}
|
|
for (int z = cz - radius; z <= cz + radius; z++) {
|
|
if (fz && z != cz) {
|
|
continue;
|
|
}
|
|
int inside = 0;
|
|
int outside = fill;
|
|
for (int i = 0; i < 8; i++) {
|
|
float dx = x + offsets[i][0] - cx;
|
|
float dy = y + offsets[i][1] - cy;
|
|
float dz = z + offsets[i][2] - cz;
|
|
float d = sqrtf(dx * dx + dy * dy + dz * dz);
|
|
if (d < radius) {
|
|
inside = 1;
|
|
}
|
|
else {
|
|
outside = 1;
|
|
}
|
|
}
|
|
if (inside && outside) {
|
|
builder_block(x, y, z, w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cylinder(Block *b1, Block *b2, int radius, int fill) {
|
|
if (b1->w != b2->w) {
|
|
return;
|
|
}
|
|
int w = b1->w;
|
|
int x1 = MIN(b1->x, b2->x);
|
|
int y1 = MIN(b1->y, b2->y);
|
|
int z1 = MIN(b1->z, b2->z);
|
|
int x2 = MAX(b1->x, b2->x);
|
|
int y2 = MAX(b1->y, b2->y);
|
|
int z2 = MAX(b1->z, b2->z);
|
|
int fx = x1 != x2;
|
|
int fy = y1 != y2;
|
|
int fz = z1 != z2;
|
|
if (fx + fy + fz != 1) {
|
|
return;
|
|
}
|
|
Block block = {x1, y1, z1, w};
|
|
if (fx) {
|
|
for (int x = x1; x <= x2; x++) {
|
|
block.x = x;
|
|
sphere(&block, radius, fill, 1, 0, 0);
|
|
}
|
|
}
|
|
if (fy) {
|
|
for (int y = y1; y <= y2; y++) {
|
|
block.y = y;
|
|
sphere(&block, radius, fill, 0, 1, 0);
|
|
}
|
|
}
|
|
if (fz) {
|
|
for (int z = z1; z <= z2; z++) {
|
|
block.z = z;
|
|
sphere(&block, radius, fill, 0, 0, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void tree(Block *block) {
|
|
int bx = block->x;
|
|
int by = block->y;
|
|
int bz = block->z;
|
|
for (int y = by + 3; y < by + 8; y++) {
|
|
for (int dx = -3; dx <= 3; dx++) {
|
|
for (int dz = -3; dz <= 3; dz++) {
|
|
int dy = y - (by + 4);
|
|
int d = (dx * dx) + (dy * dy) + (dz * dz);
|
|
if (d < 11) {
|
|
builder_block(bx + dx, y, bz + dz, 15);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int y = by; y < by + 7; y++) {
|
|
builder_block(bx, y, bz, 5);
|
|
}
|
|
}
|
|
|
|
void time_command(double new_elapsed) {
|
|
glfwSetTime(fmod(new_elapsed, g->day_length));
|
|
g->time_changed = 1;
|
|
}
|
|
|
|
void parse_command(const char *buffer, int forward) {
|
|
char username[128] = {0};
|
|
char token[128] = {0};
|
|
char server_addr[MAX_ADDR_LENGTH];
|
|
int server_port = DEFAULT_PORT;
|
|
char filename[MAX_PATH_LENGTH];
|
|
int radius, page, count, xc, yc, zc;
|
|
double time;
|
|
int is_allowing_time_change = !get_client_enabled();
|
|
if (sscanf(buffer, "/identity %128s %128s", username, token) == 2) {
|
|
db_auth_set(username, token);
|
|
add_message("Successfully imported identity token!");
|
|
login();
|
|
}
|
|
else if (strcmp(buffer, "/logout") == 0) {
|
|
db_auth_select_none();
|
|
login();
|
|
}
|
|
else if (sscanf(buffer, "/login %128s", username) == 1) {
|
|
if (db_auth_select(username)) {
|
|
login();
|
|
}
|
|
else {
|
|
add_message("Unknown username.");
|
|
}
|
|
}
|
|
else if (sscanf(buffer,
|
|
"/online %128s %d", server_addr, &server_port) >= 1)
|
|
{
|
|
g->mode_changed = 1;
|
|
g->mode = MODE_ONLINE;
|
|
strncpy(g->server_addr, server_addr, MAX_ADDR_LENGTH);
|
|
g->server_port = server_port;
|
|
snprintf(g->db_path, MAX_PATH_LENGTH,
|
|
"cache.%s.%d.db", g->server_addr, g->server_port);
|
|
}
|
|
else if (sscanf(buffer, "/offline %128s", filename) == 1) {
|
|
g->mode_changed = 1;
|
|
g->mode = MODE_OFFLINE;
|
|
snprintf(g->db_path, MAX_PATH_LENGTH, "%s.db", filename);
|
|
}
|
|
else if (strcmp(buffer, "/offline") == 0) {
|
|
g->mode_changed = 1;
|
|
g->mode = MODE_OFFLINE;
|
|
snprintf(g->db_path, MAX_PATH_LENGTH, "%s", DB_PATH);
|
|
}
|
|
else if (sscanf(buffer, "/view %d", &radius) == 1) {
|
|
if (radius >= 1 && radius <= 24) {
|
|
g->create_radius = radius;
|
|
g->render_radius = radius;
|
|
g->delete_radius = radius + 4;
|
|
}
|
|
else {
|
|
add_message("Viewing distance must be between 1 and 24.");
|
|
}
|
|
}
|
|
else if (strcmp(buffer, "/help") == 0) {
|
|
add_message("Use /help <pages> to see the list of commands.");
|
|
}
|
|
|
|
else if (sscanf(buffer, "/help %d", &page) == 1) {
|
|
if (!page == 0) {
|
|
page = page - 1;
|
|
}
|
|
float pages_float = 14 / MAX_MESSAGES;
|
|
int pages = ceil(pages_float);
|
|
const char *content[] = { // List of commands
|
|
"/help (<pages>) \n",
|
|
"/identity <username> <token> \n",
|
|
"/logout & /login <username> \n",
|
|
"/online <server adress> <port> \n",
|
|
"/offline (<server adress> <port>) \n",
|
|
"/view <radius>\n",
|
|
"/copy & /paste & /tree\n",
|
|
"/array <x> <y> <z>\n",
|
|
"/array <count>\n",
|
|
"/fcube & /cube\n",
|
|
"/(f)sphere <radius>\n",
|
|
"/(f)circlex <radius> & /(f)circley <~> & /(f)circlez <~>\n",
|
|
"/(f)cylinder <radius>\n",
|
|
"/day & /night & /time <hour>\n"};
|
|
|
|
for(int i = 0+MAX_MESSAGES*page; i < MAX_MESSAGES+MAX_MESSAGES*page; i++) {
|
|
const char *output = content[i];
|
|
if (output) {
|
|
add_message(output);
|
|
} else {
|
|
add_message("");
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(buffer, "/copy") == 0) {
|
|
copy();
|
|
}
|
|
else if (strcmp(buffer, "/paste") == 0) {
|
|
paste();
|
|
}
|
|
else if (strcmp(buffer, "/tree") == 0) {
|
|
tree(&g->block0);
|
|
}
|
|
else if (sscanf(buffer, "/array %d %d %d", &xc, &yc, &zc) == 3) {
|
|
array(&g->block1, &g->block0, xc, yc, zc);
|
|
}
|
|
else if (sscanf(buffer, "/array %d", &count) == 1) {
|
|
array(&g->block1, &g->block0, count, count, count);
|
|
}
|
|
else if (strcmp(buffer, "/fcube") == 0) {
|
|
cube(&g->block0, &g->block1, 1);
|
|
}
|
|
else if (strcmp(buffer, "/cube") == 0) {
|
|
cube(&g->block0, &g->block1, 0);
|
|
}
|
|
else if (sscanf(buffer, "/fsphere %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 1, 0, 0, 0);
|
|
}
|
|
else if (sscanf(buffer, "/sphere %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 0, 0, 0, 0);
|
|
}
|
|
else if (sscanf(buffer, "/fcirclex %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 1, 1, 0, 0);
|
|
}
|
|
else if (sscanf(buffer, "/circlex %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 0, 1, 0, 0);
|
|
}
|
|
else if (sscanf(buffer, "/fcircley %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 1, 0, 1, 0);
|
|
}
|
|
else if (sscanf(buffer, "/circley %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 0, 0, 1, 0);
|
|
}
|
|
else if (sscanf(buffer, "/fcirclez %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 1, 0, 0, 1);
|
|
}
|
|
else if (sscanf(buffer, "/circlez %d", &radius) == 1) {
|
|
sphere(&g->block0, radius, 0, 0, 0, 1);
|
|
}
|
|
else if (sscanf(buffer, "/fcylinder %d", &radius) == 1) {
|
|
cylinder(&g->block0, &g->block1, radius, 1);
|
|
}
|
|
else if (sscanf(buffer, "/cylinder %d", &radius) == 1) {
|
|
cylinder(&g->block0, &g->block1, radius, 0);
|
|
}
|
|
else if (is_allowing_time_change && strcmp(buffer, "/day") == 0) {
|
|
time_command(DAY_LENGTH / 2.0);
|
|
}
|
|
else if (is_allowing_time_change && strcmp(buffer, "/night") == 0) {
|
|
time_command(0.0);
|
|
}
|
|
else if (is_allowing_time_change && sscanf(buffer, "/time %lf", &time) == 1) {
|
|
time_command(time);
|
|
}
|
|
else if (forward) {
|
|
client_talk(buffer);
|
|
}
|
|
}
|
|
|
|
void on_light() {
|
|
State *s = &g->players->state;
|
|
int hx, hy, hz;
|
|
int hw = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, &hx, &hy, &hz);
|
|
if (hy > 0 && hy < BUILD_HEIGHT_LIMIT && is_destructable(hw)) {
|
|
toggle_light(hx, hy, hz);
|
|
compute_chunk;
|
|
}
|
|
}
|
|
|
|
void on_left_click() {
|
|
State *s = &g->players->state;
|
|
int hx, hy, hz;
|
|
int hw = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, &hx, &hy, &hz);
|
|
int mining = 1;
|
|
//while (mining = 1) {
|
|
if (hy > 0 && hy < BUILD_HEIGHT_LIMIT && is_destructable(hw) && Inventory_collect(&g->inventory, hw)) {
|
|
set_block(hx, hy, hz, 0);
|
|
record_block(hx, hy, hz, 0);
|
|
if (is_plant(get_block(hx, hy + 1, hz))) {
|
|
set_block(hx, hy + 1, hz, 0);
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
void on_right_click() {
|
|
State *s = &g->players->state;
|
|
int hx, hy, hz;
|
|
int hw = hit_test(1, s->x, s->y, s->z, s->rx, s->ry, &hx, &hy, &hz);
|
|
|
|
int nx, ny, nz;
|
|
int hwb = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, &nx, &ny, &nz);
|
|
|
|
//if (ny > 0 && ny < BUILD_HEIGHT_LIMIT && !boom_on_click(hwb) && Inventory_use(&g->inventory, items[g->item_index])) {
|
|
// sphere((get_block(hx, hy, hz)), 10, Item_EMPTY, nx, ny, nz);
|
|
//}
|
|
if (hy > 0 && hy < BUILD_HEIGHT_LIMIT && is_obstacle(hw) && Inventory_use(&g->inventory, items[g->item_index])) {
|
|
if (!player_intersects_block(2, s->x, s->y, s->z, hx, hy, hz)) {
|
|
set_block(hx, hy, hz, items[g->item_index]);
|
|
record_block(hx, hy, hz, items[g->item_index]);
|
|
}
|
|
} else if (ny > 0 && ny < BUILD_HEIGHT_LIMIT && !buildable_to(hwb) && Inventory_use(&g->inventory, items[g->item_index])) {
|
|
if (!player_intersects_block(2, s->x, s->y, s->z, hx, hy, hz)) {
|
|
//int bhw = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, &nx, &ny, &nz);
|
|
//if (!is_obstacle(bhw)) {
|
|
set_block(nx, ny, nz, items[g->item_index]);
|
|
record_block(hx, hy, hz, items[g->item_index]);
|
|
//}
|
|
}
|
|
}
|
|
}
|
|
|
|
void on_middle_click() {
|
|
State *s = &g->players->state;
|
|
int hx, hy, hz;
|
|
int hw = hit_test(0, s->x, s->y, s->z, s->rx, s->ry, &hx, &hy, &hz);
|
|
for (int i = 0; i < item_count; i++) {
|
|
if (items[i] == hw) {
|
|
g->item_index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void on_key(GLFWwindow *window, int key, int scancode, int action, int mods) {
|
|
int control = mods & (GLFW_MOD_CONTROL | GLFW_MOD_SUPER);
|
|
int exclusive =
|
|
glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
|
if (action == GLFW_RELEASE) {
|
|
return;
|
|
}
|
|
if (key == GLFW_KEY_BACKSPACE) {
|
|
if (g->typing) {
|
|
int n = strlen(g->typing_buffer);
|
|
if (n > 0) {
|
|
g->typing_buffer[n - 1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
if (action != GLFW_PRESS) {
|
|
return;
|
|
}
|
|
if (key == GLFW_KEY_ESCAPE) {
|
|
if (g->typing) {
|
|
g->typing = 0;
|
|
}
|
|
}
|
|
if (key == GLFW_KEY_LEFT_ALT) {
|
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
|
}
|
|
if (key == GLFW_KEY_ENTER) {
|
|
if (g->typing) {
|
|
if (mods & GLFW_MOD_SHIFT) {
|
|
int n = strlen(g->typing_buffer);
|
|
if (n < MAX_TEXT_LENGTH - 1) {
|
|
g->typing_buffer[n] = '\r';
|
|
g->typing_buffer[n + 1] = '\0';
|
|
}
|
|
}
|
|
else {
|
|
g->typing = 0;
|
|
if (g->typing_buffer[0] == CRAFT_KEY_SIGN) {
|
|
Player *player = g->players;
|
|
int x, y, z, face;
|
|
if (hit_test_face(player, &x, &y, &z, &face)) {
|
|
set_sign(x, y, z, face, g->typing_buffer + 1);
|
|
}
|
|
}
|
|
else if (g->typing_buffer[0] == '/') {
|
|
parse_command(g->typing_buffer, 1);
|
|
}
|
|
else {
|
|
client_talk(g->typing_buffer);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (control) {
|
|
on_right_click();
|
|
}
|
|
else {
|
|
on_left_click();
|
|
}
|
|
}
|
|
}
|
|
if (control && key == 'V') {
|
|
const char *buffer = glfwGetClipboardString(window);
|
|
if (g->typing) {
|
|
g->suppress_char = 1;
|
|
strncat(g->typing_buffer, buffer,
|
|
MAX_TEXT_LENGTH - strlen(g->typing_buffer) - 1);
|
|
}
|
|
else {
|
|
parse_command(buffer, 0);
|
|
}
|
|
}
|
|
if (!g->typing) {
|
|
if (key == CRAFT_KEY_FLY) {
|
|
g->flying = !g->flying;
|
|
}
|
|
if (key >= '1' && key <= '9') {
|
|
g->item_index = key - '1';
|
|
}
|
|
if (key == '0') {
|
|
g->item_index = 9;
|
|
}
|
|
if (key == CRAFT_KEY_ITEM_NEXT) {
|
|
g->item_index = (g->item_index + 1) % item_count;
|
|
}
|
|
if (key == CRAFT_KEY_ITEM_PREV) {
|
|
g->item_index--;
|
|
if (g->item_index < 0) {
|
|
g->item_index = item_count - 1;
|
|
}
|
|
}
|
|
if (key == CRAFT_KEY_OBSERVE) {
|
|
g->observe1 = (g->observe1 + 1) % g->player_count;
|
|
}
|
|
if (key == CRAFT_KEY_OBSERVE_INSET) {
|
|
g->observe2 = (g->observe2 + 1) % g->player_count;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if(key == GLFW_KEY_F11) {
|
|
if(g->is_fullscreen) {
|
|
GLFWmonitor *monitor = NULL;
|
|
int mode_count;
|
|
monitor = glfwGetPrimaryMonitor();
|
|
const GLFWvidmode *modes = glfwGetVideoModes(monitor, &mode_count);
|
|
int width = modes[mode_count - 1].width;
|
|
int height = modes[mode_count - 1].height;
|
|
|
|
glfwSetWindowMonitor(g->window, monitor, 0, 0, width, height, GLFW_DONT_CARE);
|
|
} else {
|
|
glfwSetWindowMonitor(g->window, NULL, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GLFW_DONT_CARE);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
void on_char(GLFWwindow *window, unsigned int u) {
|
|
if (g->suppress_char) {
|
|
g->suppress_char = 0;
|
|
return;
|
|
}
|
|
if (g->typing) {
|
|
if (u >= 32 && u < 128) {
|
|
char c = (char)u;
|
|
int n = strlen(g->typing_buffer);
|
|
if (n < MAX_TEXT_LENGTH - 1) {
|
|
g->typing_buffer[n] = c;
|
|
g->typing_buffer[n + 1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (u == CRAFT_KEY_CHAT) {
|
|
g->typing = 1;
|
|
g->typing_buffer[0] = '\0';
|
|
}
|
|
if (u == CRAFT_KEY_COMMAND) {
|
|
g->typing = 1;
|
|
g->typing_buffer[0] = '/';
|
|
g->typing_buffer[1] = '\0';
|
|
}
|
|
if (u == CRAFT_KEY_SIGN) {
|
|
g->typing = 1;
|
|
g->typing_buffer[0] = CRAFT_KEY_SIGN;
|
|
g->typing_buffer[1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
void on_scroll(GLFWwindow *window, double xdelta, double ydelta) {
|
|
static double ypos = 0;
|
|
ypos += ydelta;
|
|
if (ypos < -SCROLL_THRESHOLD) {
|
|
g->item_index = (g->item_index + 1) % item_count;
|
|
ypos = 0;
|
|
}
|
|
if (ypos > SCROLL_THRESHOLD) {
|
|
g->item_index--;
|
|
if (g->item_index < 0) {
|
|
g->item_index = item_count - 1;
|
|
}
|
|
ypos = 0;
|
|
}
|
|
}
|
|
|
|
void on_mouse_button(GLFWwindow *window, int button, int action, int mods) {
|
|
int control = mods & (GLFW_MOD_CONTROL | GLFW_MOD_SUPER);
|
|
int exclusive =
|
|
glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
|
if (action != GLFW_PRESS) {
|
|
return;
|
|
}
|
|
if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
|
if (exclusive) {
|
|
if (control) {
|
|
on_right_click();
|
|
}
|
|
else {
|
|
on_left_click();
|
|
}
|
|
}
|
|
else {
|
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
}
|
|
}
|
|
if (button == GLFW_MOUSE_BUTTON_RIGHT) {
|
|
if (exclusive) {
|
|
if (control) {
|
|
on_light();
|
|
}
|
|
else {
|
|
on_right_click();
|
|
}
|
|
}
|
|
}
|
|
if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
|
|
if (exclusive) {
|
|
on_middle_click();
|
|
}
|
|
}
|
|
}
|
|
|
|
void create_window() {
|
|
int window_width = WINDOW_WIDTH;
|
|
int window_height = WINDOW_HEIGHT;
|
|
GLFWmonitor *monitor = NULL;
|
|
if (FULLSCREEN) {
|
|
int mode_count;
|
|
monitor = glfwGetPrimaryMonitor();
|
|
const GLFWvidmode *modes = glfwGetVideoModes(monitor, &mode_count);
|
|
window_width = modes[mode_count - 1].width;
|
|
window_height = modes[mode_count - 1].height;
|
|
}
|
|
g->window = glfwCreateWindow(
|
|
window_width, window_height, "Omicron, enjoy.", monitor, NULL);
|
|
|
|
//g->is_fullscreen = FULLSCREEN;
|
|
}
|
|
|
|
void handle_mouse_input() {
|
|
int exclusive =
|
|
glfwGetInputMode(g->window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
|
static double px = 0;
|
|
static double py = 0;
|
|
State *s = &g->players->state;
|
|
if (exclusive && (px || py)) {
|
|
double mx, my;
|
|
glfwGetCursorPos(g->window, &mx, &my);
|
|
float m = 0.0025;
|
|
s->rx += (mx - px) * m;
|
|
if (INVERT_MOUSE) {
|
|
s->ry += (my - py) * m;
|
|
}
|
|
else {
|
|
s->ry -= (my - py) * m;
|
|
}
|
|
if (s->rx < 0) {
|
|
s->rx += RADIANS(360);
|
|
}
|
|
if (s->rx >= RADIANS(360)){
|
|
s->rx -= RADIANS(360);
|
|
}
|
|
s->ry = MAX(s->ry, -RADIANS(90));
|
|
s->ry = MIN(s->ry, RADIANS(90));
|
|
px = mx;
|
|
py = my;
|
|
}
|
|
else {
|
|
glfwGetCursorPos(g->window, &px, &py);
|
|
}
|
|
}
|
|
|
|
void handle_mouse_input_in_menu() {
|
|
static double px = 0;
|
|
static double py = 0;
|
|
glfwSetInputMode(g->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
|
glfwGetCursorPos(g->window, &px, &py);
|
|
}
|
|
|
|
void handle_movement(double dt) {
|
|
static float dy = 0;
|
|
State *s = &g->players->state;
|
|
int sz = 0;
|
|
int sx = 0;
|
|
if (!g->typing) {
|
|
float m = dt * 1.0;
|
|
g->ortho = glfwGetKey(g->window, CRAFT_KEY_ORTHO) ? 64 : 0;
|
|
g->fov = glfwGetKey(g->window, CRAFT_KEY_ZOOM) ? 15 : 65;
|
|
if (glfwGetKey(g->window, CRAFT_KEY_FORWARD)) sz--;
|
|
if (glfwGetKey(g->window, CRAFT_KEY_BACKWARD)) sz++;
|
|
if (glfwGetKey(g->window, CRAFT_KEY_LEFT)) sx--;
|
|
if (glfwGetKey(g->window, CRAFT_KEY_RIGHT)) sx++;
|
|
if (glfwGetKey(g->window, GLFW_KEY_LEFT)) s->rx -= m;
|
|
if (glfwGetKey(g->window, GLFW_KEY_RIGHT)) s->rx += m;
|
|
if (glfwGetKey(g->window, GLFW_KEY_UP)) s->ry += m;
|
|
if (glfwGetKey(g->window, GLFW_KEY_DOWN)) s->ry -= m;
|
|
}
|
|
float vx, vy, vz;
|
|
get_motion_vector(g->flying, sz, sx, s->rx, s->ry, &vx, &vy, &vz);
|
|
int w = get_block(s->x, s->y, s->z);
|
|
int is_jump_pressed = glfwGetKey(g->window, CRAFT_KEY_JUMP);
|
|
int is_descend_pressed = glfwGetKey(g->window, CRAFT_KEY_DECSEND);
|
|
if (!g->typing) {
|
|
if (is_jump_pressed) {
|
|
if (g->flying) {
|
|
vy = 1;
|
|
}
|
|
else if (dy == 0) {
|
|
if (is_climbable(w)) {
|
|
dy = CLIMB_SPEED;
|
|
} else {
|
|
dy = 8;
|
|
}
|
|
}
|
|
}
|
|
else if (is_descend_pressed) {
|
|
if (is_climbable(w)) {
|
|
dy = -CLIMB_SPEED;
|
|
}
|
|
}
|
|
}
|
|
float speed = g->flying ? FLY_SPEED : WALK_SPEED;
|
|
int estimate = roundf(sqrtf(
|
|
powf(vx * speed, 2) +
|
|
powf(vy * speed + ABS(dy) * 2, 2) +
|
|
powf(vz * speed, 2)) * dt * 8);
|
|
int step = MAX(8, estimate);
|
|
float ut = dt / step;
|
|
vx = vx * ut * speed;
|
|
vy = vy * ut * speed;
|
|
vz = vz * ut * speed;
|
|
for (int i = 0; i < step; i++) {
|
|
if (g->flying) {
|
|
dy = 0;
|
|
|
|
if (is_descend_pressed) {
|
|
vy = -0.05f;
|
|
}
|
|
}
|
|
else {
|
|
if (is_climbable(w)) {
|
|
if (!is_jump_pressed) {
|
|
if (dy > 0) {
|
|
dy = 0.0f;
|
|
}
|
|
else if (!is_descend_pressed) {
|
|
dy = 0.0f;
|
|
}
|
|
}
|
|
} else if (is_liquid(w)) {
|
|
|
|
if (is_jump_pressed) {
|
|
dy = 8.0f;
|
|
//dy = 0;
|
|
|
|
} else if (is_descend_pressed) {
|
|
dy = -1.2f;
|
|
//dy = 0;
|
|
|
|
} else {
|
|
dy = -0.5f;
|
|
//dy = 0;
|
|
|
|
}
|
|
|
|
vx = vx*0.65;
|
|
//vy = vy/1.6;
|
|
vz = vz*0.65;
|
|
|
|
}
|
|
else {
|
|
dy -= ut * 25;
|
|
dy = MAX(dy, -250);
|
|
}
|
|
}
|
|
s->x += vx;
|
|
s->y += vy + dy * ut;
|
|
s->z += vz;
|
|
if (collide(2, &s->x, &s->y, &s->z)) {
|
|
dy = 0;
|
|
}
|
|
}
|
|
if (s->y < 0) {
|
|
s->y = highest_block(s->x, s->z) + 2;
|
|
}
|
|
}
|
|
|
|
void menu_movement(double dt) {
|
|
// TODO: Allow the player to fall while in inventory
|
|
static float dy = 0;
|
|
State *s = &g->players->state;
|
|
int sz = 0;
|
|
int sx = 0;
|
|
s->ry += 1;
|
|
}
|
|
|
|
void handle_liquids() {
|
|
State *s = &g->players->state;
|
|
int px = s->x;
|
|
int py = s->y;
|
|
int pz = s->z;
|
|
|
|
//int nx, ny, nz;
|
|
//set_block(px, py+5, pz, 35);
|
|
Block block = {px, py, pz, 1};
|
|
sphere(&block, 2, Item_EMPTY, px, py, pz);
|
|
}
|
|
|
|
void parse_buffer(char *buffer) {
|
|
Player *me = g->players;
|
|
State *s = &g->players->state;
|
|
char *key;
|
|
char *line = tokenize(buffer, "\n", &key);
|
|
while (line) {
|
|
int pid;
|
|
float ux, uy, uz, urx, ury;
|
|
if (sscanf(line, "U,%d,%f,%f,%f,%f,%f",
|
|
&pid, &ux, &uy, &uz, &urx, &ury) == 6)
|
|
{
|
|
me->id = pid;
|
|
s->x = ux; s->y = uy; s->z = uz; s->rx = urx; s->ry = ury;
|
|
force_chunks(me);
|
|
if (uy == 0) {
|
|
s->y = highest_block(s->x, s->z) + 2;
|
|
}
|
|
}
|
|
int bp, bq, bx, by, bz, bw;
|
|
if (sscanf(line, "B,%d,%d,%d,%d,%d,%d",
|
|
&bp, &bq, &bx, &by, &bz, &bw) == 6)
|
|
{
|
|
_set_block(bp, bq, bx, by, bz, bw, 0);
|
|
if (player_intersects_block(2, s->x, s->y, s->z, bx, by, bz)) {
|
|
s->y = highest_block(s->x, s->z) + 2;
|
|
}
|
|
}
|
|
if (sscanf(line, "L,%d,%d,%d,%d,%d,%d",
|
|
&bp, &bq, &bx, &by, &bz, &bw) == 6)
|
|
{
|
|
set_light(bp, bq, bx, by, bz, bw);
|
|
}
|
|
float px, py, pz, prx, pry;
|
|
if (sscanf(line, "P,%d,%f,%f,%f,%f,%f",
|
|
&pid, &px, &py, &pz, &prx, &pry) == 6)
|
|
{
|
|
Player *player = find_player(pid);
|
|
if (!player && g->player_count < MAX_PLAYERS) {
|
|
player = g->players + g->player_count;
|
|
g->player_count++;
|
|
player->id = pid;
|
|
player->buffer = 0;
|
|
snprintf(player->name, MAX_NAME_LENGTH, "player%d", pid);
|
|
update_player(player, px, py, pz, prx, pry, 1); // twice
|
|
}
|
|
if (player) {
|
|
update_player(player, px, py, pz, prx, pry, 1);
|
|
}
|
|
}
|
|
if (sscanf(line, "D,%d", &pid) == 1) {
|
|
delete_player(pid);
|
|
}
|
|
int kp, kq, kk;
|
|
if (sscanf(line, "K,%d,%d,%d", &kp, &kq, &kk) == 3) {
|
|
db_set_key(kp, kq, kk);
|
|
}
|
|
if (sscanf(line, "R,%d,%d", &kp, &kq) == 2) {
|
|
Chunk *chunk = find_chunk(kp, kq);
|
|
if (chunk) {
|
|
dirty_chunk(chunk);
|
|
}
|
|
}
|
|
double elapsed;
|
|
int day_length;
|
|
if (sscanf(line, "E,%lf,%d", &elapsed, &day_length) == 2) {
|
|
glfwSetTime(fmod(elapsed, day_length));
|
|
g->day_length = day_length;
|
|
g->time_changed = 1;
|
|
}
|
|
if (line[0] == 'T' && line[1] == ',') {
|
|
char *text = line + 2;
|
|
add_message(text);
|
|
}
|
|
char format[64];
|
|
snprintf(
|
|
format, sizeof(format), "N,%%d,%%%ds", MAX_NAME_LENGTH - 1);
|
|
char name[MAX_NAME_LENGTH];
|
|
if (sscanf(line, format, &pid, name) == 2) {
|
|
Player *player = find_player(pid);
|
|
if (player) {
|
|
strncpy(player->name, name, MAX_NAME_LENGTH);
|
|
}
|
|
}
|
|
snprintf(
|
|
format, sizeof(format),
|
|
"S,%%d,%%d,%%d,%%d,%%d,%%d,%%%d[^\n]", MAX_SIGN_LENGTH - 1);
|
|
int face;
|
|
char text[MAX_SIGN_LENGTH] = {0};
|
|
if (sscanf(line, format,
|
|
&bp, &bq, &bx, &by, &bz, &face, text) >= 6)
|
|
{
|
|
_set_sign(bp, bq, bx, by, bz, face, text, 0);
|
|
}
|
|
line = tokenize(NULL, "\n", &key);
|
|
}
|
|
}
|
|
|
|
void update_sky_tint() {
|
|
SkyColor *c = &g->sky_color;
|
|
g->last_sky_color = *c;
|
|
|
|
State *s = &g->players->state;
|
|
int p = chunked(s->x);
|
|
int q = chunked(s->z);
|
|
int x = p * CHUNK_SIZE;
|
|
int z = q * CHUNK_SIZE;
|
|
|
|
SkyColor new_color;
|
|
get_sky_tint(biome_at_pos(q, x, z), &new_color);
|
|
|
|
//Interpolate sky color to get close to the actual value gradually.
|
|
//Technically, it won't arrive, but rather achieve an extremely close color.
|
|
c->r += 0.01 * (new_color.r - c->r);
|
|
c->g += 0.01 * (new_color.g - c->g);
|
|
c->b += 0.01 * (new_color.b - c->b);
|
|
|
|
//printf("c: %f last: %f\n", c->r, g->last_sky_color.r);
|
|
}
|
|
|
|
void reset_model() {
|
|
memset(g->chunks, 0, sizeof(Chunk) * MAX_CHUNKS);
|
|
g->chunk_count = 0;
|
|
memset(g->players, 0, sizeof(Player) * MAX_PLAYERS);
|
|
g->player_count = 0;
|
|
g->observe1 = 0;
|
|
g->observe2 = 0;
|
|
g->flying = 0;
|
|
g->item_index = 0;
|
|
memset(g->typing_buffer, 0, sizeof(char) * MAX_TEXT_LENGTH);
|
|
g->typing = 0;
|
|
memset(g->messages, 0, sizeof(char) * MAX_MESSAGES * MAX_TEXT_LENGTH);
|
|
g->message_index = 0;
|
|
g->day_length = DAY_LENGTH;
|
|
glfwSetTime(g->day_length / 3.0);
|
|
g->time_changed = 1;
|
|
|
|
g->sky_color = (SkyColor){0.0f, 0.0f, 0.0f};
|
|
g->last_sky_color = g->sky_color;
|
|
Inventory_reset(&g->inventory);
|
|
}
|
|
|
|
void advance_cursor() {
|
|
static int pos=0;
|
|
char cursor[4]={'/','-','\\','|'};
|
|
printf("> The game is running %c\r", cursor[pos]);
|
|
fflush(stdout);
|
|
//sleep(1);
|
|
pos = (pos+1) % 4;
|
|
}
|
|
|
|
void edit_config(char param, int value) {
|
|
FILE *fconf;
|
|
fconf = fopen("config.txt","r+");
|
|
|
|
//fprintf(fconf,"%c %d \n", param, value);
|
|
fprintf(fconf,"lol \n");
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
// INITIALIZATION //
|
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
srand(time(NULL));
|
|
rand();
|
|
|
|
// TERMINAL UI //
|
|
/*
|
|
* This UI allows the player to know
|
|
* whether or not the game has launched
|
|
* properly. Also used for debug.
|
|
*/
|
|
|
|
|
|
// Header generated using -> https://fsymbols.com/generators/tarty/
|
|
printf("\n▒█▀▀▀█ ▒█▀▄▀█ ▀█▀ ▒█▀▀█ ▒█▀▀█ ▒█▀▀▀█ ▒█▄░▒█ \n▒█░░▒█ ▒█▒█▒█ ▒█░ ▒█░░░ ▒█▄▄▀ ▒█░░▒█ ▒█▒█▒█ \n▒█▄▄▄█ ▒█░░▒█ ▄█▄ ▒█▄▄█ ▒█░▒█ ▒█▄▄▄█ ▒█░░▀█ \n a game not by azekill_DIABLO\n");
|
|
// Sorry for the long line of code
|
|
|
|
// unused world selection stuff
|
|
char world;
|
|
|
|
/*
|
|
printf("> Available World list:");
|
|
struct dirent *de; // Pointer for directory entry
|
|
|
|
// opendir() returns a pointer of DIR type.
|
|
DIR *dr = opendir("./world");
|
|
|
|
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
|
// for readdir()
|
|
while ((de = readdir(dr)) != NULL)
|
|
printf("%s\n", de->d_name);
|
|
|
|
printf("> Enter the name of the world you want to load:");
|
|
scanf("> %d", &world);
|
|
printf("> Loading selected world.");
|
|
*/
|
|
|
|
//printf("> Press RETURN to start");
|
|
//getchar();
|
|
|
|
|
|
|
|
|
|
// WINDOW INITIALIZATION //
|
|
if (!glfwInit()) {
|
|
return -1;
|
|
}
|
|
create_window();
|
|
if (!g->window) {
|
|
glfwTerminate();
|
|
return -1;
|
|
}
|
|
|
|
glfwMakeContextCurrent(g->window);
|
|
glfwSwapInterval(VSYNC);
|
|
glfwSetInputMode(g->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
glfwSetKeyCallback(g->window, on_key);
|
|
glfwSetCharCallback(g->window, on_char);
|
|
glfwSetMouseButtonCallback(g->window, on_mouse_button);
|
|
glfwSetScrollCallback(g->window, on_scroll);
|
|
|
|
if (glewInit() != GLEW_OK) {
|
|
return -1;
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_ALPHA_TEST);
|
|
glEnable(GL_CULL_FACE);
|
|
glLogicOp(GL_INVERT);
|
|
glClearColor(0, 0, 0, 0);
|
|
|
|
//parser_parse_all();
|
|
|
|
GLuint texture;
|
|
glGenTextures(1, &texture);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/texture.png");
|
|
|
|
/*GLuint texture_obj;
|
|
glGenTextures(1, &texture_obj);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture_obj);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/texture_obj.png");*/
|
|
|
|
GLuint font;
|
|
glGenTextures(1, &font);
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, font);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/font.png");
|
|
|
|
GLuint sky;
|
|
glGenTextures(1, &sky);
|
|
glActiveTexture(GL_TEXTURE2);
|
|
glBindTexture(GL_TEXTURE_2D, sky);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
load_png_texture("textures/sky.png");
|
|
|
|
GLuint sign;
|
|
glGenTextures(1, &sign);
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glBindTexture(GL_TEXTURE_2D, sign);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/sign.png");
|
|
|
|
GLuint ui;
|
|
glGenTextures(1, &ui);
|
|
glActiveTexture(GL_TEXTURE4);
|
|
glBindTexture(GL_TEXTURE_2D, ui);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/ui.png");
|
|
|
|
GLuint logo;
|
|
glGenTextures(1, &logo);
|
|
glActiveTexture(GL_TEXTURE5);
|
|
glBindTexture(GL_TEXTURE_2D, logo);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/logo.png"); //logo.png
|
|
|
|
GLuint background;
|
|
glGenTextures(1, &background);
|
|
glActiveTexture(GL_TEXTURE6);
|
|
glBindTexture(GL_TEXTURE_2D, background);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
load_png_texture("textures/background.png"); //background.png
|
|
|
|
// LOAD SHADERS //
|
|
Attrib block_attrib = {0};
|
|
Attrib tblock_attrib = {0};
|
|
Attrib line_attrib = {0};
|
|
Attrib text_attrib = {0};
|
|
Attrib sky_attrib = {0};
|
|
Attrib ui_attrib = {0};
|
|
Attrib logo_attrib = {0};
|
|
GLuint program;
|
|
|
|
program = load_program(
|
|
"shaders/block_vertex.glsl", "shaders/block_fragment.glsl");
|
|
block_attrib.program = program;
|
|
block_attrib.position = glGetAttribLocation(program, "position");
|
|
block_attrib.normal = glGetAttribLocation(program, "normal");
|
|
block_attrib.uv = glGetAttribLocation(program, "uv");
|
|
block_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
block_attrib.sampler = glGetUniformLocation(program, "sampler");
|
|
block_attrib.extra1 = glGetUniformLocation(program, "sky_sampler");
|
|
block_attrib.extra2 = glGetUniformLocation(program, "daylight");
|
|
block_attrib.extra3 = glGetUniformLocation(program, "fog_distance");
|
|
block_attrib.extra4 = glGetUniformLocation(program, "ortho");
|
|
block_attrib.camera = glGetUniformLocation(program, "camera");
|
|
block_attrib.timer = glGetUniformLocation(program, "timer");
|
|
block_attrib.extra5 = glGetUniformLocation(program, "sky_tint");
|
|
|
|
program = load_program(
|
|
"shaders/tblock_vertex.glsl", "shaders/tblock_fragment.glsl");
|
|
tblock_attrib.program = program;
|
|
tblock_attrib.position = glGetAttribLocation(program, "position");
|
|
tblock_attrib.normal = glGetAttribLocation(program, "normal");
|
|
tblock_attrib.uv = glGetAttribLocation(program, "uv");
|
|
tblock_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
tblock_attrib.sampler = glGetUniformLocation(program, "sampler");
|
|
tblock_attrib.extra1 = glGetUniformLocation(program, "sky_sampler");
|
|
tblock_attrib.extra2 = glGetUniformLocation(program, "daylight");
|
|
tblock_attrib.extra3 = glGetUniformLocation(program, "fog_distance");
|
|
tblock_attrib.extra4 = glGetUniformLocation(program, "ortho");
|
|
tblock_attrib.camera = glGetUniformLocation(program, "camera");
|
|
tblock_attrib.timer = glGetUniformLocation(program, "timer");
|
|
tblock_attrib.extra5 = glGetUniformLocation(program, "sky_tint");
|
|
|
|
program = load_program(
|
|
"shaders/line_vertex.glsl", "shaders/line_fragment.glsl");
|
|
line_attrib.program = program;
|
|
line_attrib.position = glGetAttribLocation(program, "position");
|
|
line_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
|
|
program = load_program(
|
|
"shaders/text_vertex.glsl", "shaders/text_fragment.glsl");
|
|
text_attrib.program = program;
|
|
text_attrib.position = glGetAttribLocation(program, "position");
|
|
text_attrib.uv = glGetAttribLocation(program, "uv");
|
|
text_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
text_attrib.sampler = glGetUniformLocation(program, "sampler");
|
|
text_attrib.extra1 = glGetUniformLocation(program, "is_sign");
|
|
|
|
program = load_program(
|
|
"shaders/sky_vertex.glsl", "shaders/sky_fragment.glsl");
|
|
sky_attrib.program = program;
|
|
sky_attrib.position = glGetAttribLocation(program, "position");
|
|
sky_attrib.normal = glGetAttribLocation(program, "normal");
|
|
sky_attrib.uv = glGetAttribLocation(program, "uv");
|
|
sky_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
sky_attrib.sampler = glGetUniformLocation(program, "sampler");
|
|
sky_attrib.timer = glGetUniformLocation(program, "timer");
|
|
sky_attrib.extra1 = glGetUniformLocation(program, "sky_tint");
|
|
|
|
program = load_program(
|
|
"shaders/ui_vertex.glsl", "shaders/ui_fragment.glsl");
|
|
ui_attrib.program = program;
|
|
ui_attrib.position = glGetAttribLocation(program, "position");
|
|
ui_attrib.normal = glGetAttribLocation(program, "normal");
|
|
ui_attrib.uv = glGetAttribLocation(program, "uv");
|
|
ui_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
ui_attrib.sampler = glGetUniformLocation(program, "sampler");
|
|
ui_attrib.timer = glGetUniformLocation(program, "timer");
|
|
ui_attrib.extra1 = glGetUniformLocation(program, "sky_tint");
|
|
|
|
program = load_program(
|
|
"shaders/ui_vertex.glsl", "shaders/ui_fragment.glsl");
|
|
logo_attrib.program = program;
|
|
logo_attrib.position = glGetAttribLocation(program, "position");
|
|
logo_attrib.normal = glGetAttribLocation(program, "normal");
|
|
logo_attrib.uv = glGetAttribLocation(program, "uv");
|
|
logo_attrib.matrix = glGetUniformLocation(program, "matrix");
|
|
logo_attrib.sampler = glGetUniformLocation(program, "sampler");
|
|
logo_attrib.timer = glGetUniformLocation(program, "timer");
|
|
logo_attrib.extra1 = glGetUniformLocation(program, "sky_tint");
|
|
|
|
|
|
// CHECK COMMAND LINE ARGUMENTS //
|
|
if (argc == 2 || argc == 3) {
|
|
g->mode = MODE_ONLINE;
|
|
strncpy(g->server_addr, argv[1], MAX_ADDR_LENGTH);
|
|
g->server_port = argc == 3 ? atoi(argv[2]) : DEFAULT_PORT;
|
|
snprintf(g->db_path, MAX_PATH_LENGTH,
|
|
"cache.%s.%d.db", g->server_addr, g->server_port);
|
|
}
|
|
else {
|
|
g->mode = MODE_OFFLINE;
|
|
snprintf(g->db_path, MAX_PATH_LENGTH, "%s", DB_PATH);
|
|
}
|
|
|
|
g->create_radius = CREATE_CHUNK_RADIUS;
|
|
g->render_radius = RENDER_CHUNK_RADIUS;
|
|
g->delete_radius = DELETE_CHUNK_RADIUS;
|
|
g->sign_radius = RENDER_SIGN_RADIUS;
|
|
|
|
// INITIALIZE WORKER THREADS
|
|
for (int i = 0; i < WORKERS; i++) {
|
|
Worker *worker = g->workers + i;
|
|
worker->index = i;
|
|
worker->state = WORKER_IDLE;
|
|
mtx_init(&worker->mtx, mtx_plain);
|
|
cnd_init(&worker->cnd);
|
|
thrd_create(&worker->thrd, worker_run, worker);
|
|
}
|
|
|
|
// OUTER LOOP //
|
|
int running = 1;
|
|
while (running) {
|
|
// DATABASE INITIALIZATION //
|
|
if (g->mode == MODE_OFFLINE || USE_CACHE) {
|
|
db_enable();
|
|
if (db_init(g->db_path)) {
|
|
return -1;
|
|
}
|
|
if (g->mode == MODE_ONLINE) {
|
|
// TODO: support proper caching of signs (handle deletions)
|
|
db_delete_all_signs();
|
|
}
|
|
}
|
|
|
|
// CLIENT INITIALIZATION //
|
|
if (g->mode == MODE_ONLINE) {
|
|
client_enable();
|
|
client_connect(g->server_addr, g->server_port);
|
|
client_start();
|
|
client_version(1);
|
|
login();
|
|
}
|
|
|
|
// LOCAL VARIABLES //
|
|
reset_model();
|
|
FPS fps = {0, 0, 0};
|
|
double last_commit = glfwGetTime();
|
|
double last_update = glfwGetTime();
|
|
GLuint sky_buffer = gen_sky_buffer();
|
|
|
|
Player *me = g->players;
|
|
State *s = &g->players->state;
|
|
me->id = 0;
|
|
me->name[0] = '\0';
|
|
me->buffer = 0;
|
|
me->hp = 20;
|
|
g->player_count = 1;
|
|
|
|
|
|
//edit_config("FONT_SIZE", 18);
|
|
|
|
|
|
//for (int i = 0; i < 11; i++) {
|
|
/*
|
|
int *show_lights;
|
|
if (fscanf(fconf,"SHOW_LIGHTS %d", &show_lights)) {
|
|
|
|
SHOW_LIGHTS = show_lights;
|
|
} else {
|
|
SHOW_LIGHTS = 1;
|
|
fprintf(fconf,"SHOW_LIGHTS 1 \n");
|
|
}
|
|
|
|
|
|
int *show_plants;
|
|
if (fscanf(fconf,"SHOW_PLANTS %d", &show_plants)) {
|
|
|
|
SHOW_PLANTS = show_plants;
|
|
} else {
|
|
SHOW_PLANTS = 1;
|
|
fprintf(fconf,"SHOW_PLANTS 1 \n");
|
|
}
|
|
|
|
|
|
int *show_clouds;
|
|
if (fscanf(fconf,"SHOW_CLOUDS %d", &show_clouds)) {
|
|
|
|
SHOW_CLOUDS = show_clouds;
|
|
} else {
|
|
SHOW_CLOUDS = 1;
|
|
fprintf(fconf,"SHOW_CLOUDS 1 \n");
|
|
}
|
|
|
|
|
|
int *show_trees;
|
|
if (fscanf(fconf,"SHOW_TREES %d", &show_trees)) {
|
|
|
|
SHOW_TREES = show_trees;
|
|
} else {
|
|
SHOW_TREES = 1;
|
|
fprintf(fconf,"SHOW_TREES 1 \n");
|
|
}
|
|
|
|
|
|
int *show_item;
|
|
if (fscanf(fconf,"SHOW_ITEM %d", &show_item)) {
|
|
|
|
SHOW_ITEM = show_item;
|
|
} else {
|
|
SHOW_ITEM = 1;
|
|
fprintf(fconf,"SHOW_ITEM 1 \n");
|
|
}
|
|
|
|
|
|
int *show_crosshairs;
|
|
if (fscanf(fconf,"SHOW_CROSSHAIRS %d", &show_crosshairs)) {
|
|
|
|
SHOW_CROSSHAIRS = show_crosshairs;
|
|
} else {
|
|
SHOW_CROSSHAIRS = 1;
|
|
fprintf(fconf,"SHOW_CROSSHAIRS 1\n");
|
|
}
|
|
|
|
int *show_info_text;
|
|
if (fscanf(fconf,"SHOW_INFO_TEXT %d", &show_info_text)) {
|
|
|
|
SHOW_INFO_TEXT = show_info_text;
|
|
} else {
|
|
SHOW_INFO_TEXT = 1;
|
|
fprintf(fconf,"SHOW_INFO_TEXT 1 \n");
|
|
}
|
|
|
|
|
|
int *show_chat_text;
|
|
if (fscanf(fconf,"SHOW_CHAT_TEXT %d", &show_chat_text)) {
|
|
|
|
SHOW_CHAT_TEXT = show_chat_text;
|
|
} else {
|
|
SHOW_CHAT_TEXT = 1;
|
|
fprintf(fconf,"SHOW_CHAT_TEXT 1 \n");
|
|
}
|
|
|
|
|
|
int *show_player_names;
|
|
if (fscanf(fconf,"SHOW_PLAYER_NAMES %d", &show_player_names)) {
|
|
|
|
SHOW_PLAYER_NAMES = show_player_names;
|
|
} else {
|
|
SHOW_PLAYER_NAMES = 1;
|
|
fprintf(fconf,"SHOW_PLAYER_NAMES 1 \n");
|
|
}
|
|
|
|
|
|
int *font_size;
|
|
if (fscanf(fconf, "FONT_SIZE %d", &font_size)) {
|
|
|
|
FONT_SIZE = font_size;
|
|
//printf("if choosen: %d", font_size);
|
|
} else {
|
|
|
|
fprintf(fconf,"FONT_SIZE 16 \n");
|
|
//printf("else choosen");
|
|
}
|
|
//}*/
|
|
|
|
|
|
// LOAD STATE FROM DATABASE //
|
|
int loaded = db_load_state(&s->x, &s->y, &s->z, &s->rx, &s->ry);
|
|
force_chunks(me);
|
|
if (!loaded) {
|
|
s->y = highest_block(s->x, s->z) + 2;
|
|
}
|
|
|
|
// --------------- //
|
|
// CHECKING CONFIG //
|
|
// --------------- //
|
|
|
|
// TODO: Make the config editable from ingame config menu
|
|
|
|
FILE *fconf;
|
|
fconf = fopen("config.txt","r");
|
|
|
|
int show_lights;
|
|
if (fscanf(fconf,"SHOW_LIGHTS %d\n", &show_lights)) {
|
|
SHOW_LIGHTS = show_lights;
|
|
|
|
}
|
|
|
|
int show_item;
|
|
if (fscanf(fconf,"SHOW_ITEM %d\n", &show_item)) {
|
|
SHOW_ITEM = show_item;
|
|
|
|
}
|
|
|
|
int show_crosshairs;
|
|
if (fscanf(fconf,"SHOW_CROSSHAIRS %d\n", &show_crosshairs)) {
|
|
SHOW_CROSSHAIRS = show_crosshairs;
|
|
}
|
|
|
|
int show_info_text;
|
|
if (fscanf(fconf,"SHOW_INFO_TEXT %d\n", &show_info_text)) {
|
|
SHOW_INFO_TEXT = show_info_text;
|
|
}
|
|
|
|
int show_chat_text;
|
|
if (fscanf(fconf,"SHOW_CHAT_TEXT %d\n", &show_chat_text)) {
|
|
SHOW_CHAT_TEXT = show_chat_text;
|
|
}
|
|
|
|
int show_player_names;
|
|
if (fscanf(fconf,"SHOW_PLAYER_NAMES %d\n", &show_player_names)) {
|
|
SHOW_PLAYER_NAMES = show_player_names;
|
|
}
|
|
|
|
int font_size;
|
|
if (fscanf(fconf, "FONT_SIZE %d\n", &font_size)) {
|
|
FONT_SIZE = font_size;
|
|
//printf("if choosen: %d", font_size);
|
|
}
|
|
|
|
|
|
|
|
// BEGIN MAIN LOOP //
|
|
|
|
double previous = glfwGetTime();
|
|
|
|
// ---------- //\
|
|
// GAME MODES // \
|
|
// ---------- // \
|
|
|
|
//glfwSetInputMode(g->window, GLFW_STICKY_KEYS, 1);
|
|
//glfwSetKeyCallback(window, key_callback);
|
|
|
|
int game_state = 0; // switch: 0 menu (dflt), 1 normal
|
|
int menu_id = 0; // switch: 0 main menu, 1 config
|
|
int inventory = 0; // switch: 0 closed, 1 opened
|
|
int i_pressed = 0; // to avoid too fast toogling
|
|
|
|
while (1) {
|
|
|
|
// ----------------- //
|
|
// GENERAL FUNCTIONS //
|
|
// ----------------- //
|
|
|
|
double now = glfwGetTime();
|
|
double dt = now - previous;
|
|
dt = MIN(dt, 0.2);
|
|
dt = MAX(dt, 0.0);
|
|
previous = now;
|
|
|
|
// WINDOW SIZE AND SCALE //
|
|
g->scale = get_scale_factor();
|
|
glfwGetFramebufferSize(g->window, &g->width, &g->height);
|
|
glViewport(0, 0, g->width, g->height);
|
|
|
|
// SWAP AND POLL //
|
|
glfwSwapBuffers(g->window);
|
|
glfwPollEvents();
|
|
if (glfwWindowShouldClose(g->window)) {
|
|
running = 0;
|
|
break;
|
|
}
|
|
if (g->mode_changed) {
|
|
g->mode_changed = 0;
|
|
break;
|
|
}
|
|
|
|
// HANDLE DATA FROM SERVER //
|
|
char *buffer = client_recv();
|
|
if (buffer) {
|
|
parse_buffer(buffer);
|
|
free(buffer);
|
|
}
|
|
|
|
// FLUSH DATABASE //
|
|
if (now - last_commit > COMMIT_INTERVAL) {
|
|
last_commit = now;
|
|
db_commit();
|
|
}
|
|
|
|
// SEND POSITION TO SERVER //
|
|
if (now - last_update > 0.1) {
|
|
last_update = now;
|
|
client_position(s->x, s->y, s->z, s->rx, s->ry);
|
|
}
|
|
|
|
|
|
// ----------------- //
|
|
// FIRST MODE (MENU) //
|
|
// ----------------- //
|
|
|
|
|
|
if (game_state == 0) {
|
|
|
|
// HANDLE MOUSE INPUT //
|
|
handle_mouse_input_in_menu();
|
|
|
|
double cx;
|
|
double cy;
|
|
glfwGetCursorPos(g->window, &cx, &cy);
|
|
int state = glfwGetMouseButton(g->window, GLFW_MOUSE_BUTTON_LEFT);
|
|
|
|
// TURN AROUND RANDOMLY //
|
|
//menu_movement(dt);
|
|
|
|
// CLEAR SCREEN
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// PREPARE TO RENDER //
|
|
update_sky_tint();
|
|
g->observe1 = g->observe1 % g->player_count;
|
|
g->observe2 = g->observe2 % g->player_count;
|
|
delete_chunks();
|
|
del_buffer(me->buffer);
|
|
me->buffer = gen_player_buffer(s->x, s->y, s->z, s->rx, s->ry);
|
|
for (int i = 1; i < g->player_count; i++) {
|
|
interpolate_player(g->players + i);
|
|
}
|
|
Player *player = g->players + g->observe1;
|
|
|
|
|
|
|
|
// RENDER HUD //
|
|
|
|
char text_buffer[1024];
|
|
float ts = FONT_SIZE * g->scale;
|
|
float tx = ts / 2;
|
|
float ty = g->height - ts;
|
|
|
|
// RENDER TEXT // *optimisation needed for the render of lines. \n maybe?
|
|
snprintf(text_buffer, 1024, "Omicron version 0.7 `Tweaky Update`");
|
|
render_text(&text_attrib, ALIGN_LEFT, tx, ty, ts, text_buffer, g->width, g->height);
|
|
//ty -= ts * 2;
|
|
|
|
snprintf(text_buffer, 1024, "Alpha");
|
|
render_text(&text_attrib, ALIGN_LEFT, tx, ts+2, ts, text_buffer, g->width, g->height);
|
|
|
|
if (menu_id == 0) {
|
|
// BUTTON TEXT //
|
|
|
|
snprintf(text_buffer, 1024, "Play");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*1.5, g->height/2, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "X");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2+ts*3-1, g->height/2-24*2, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "Conf");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*3-1, g->height/2-24*2, ts, text_buffer, g->width, g->height);
|
|
|
|
|
|
// MATRIX FOR GUI //
|
|
float matrix[16];
|
|
set_matrix_2d(matrix, g->width, g->height);
|
|
glUseProgram(ui_attrib.program);
|
|
glUniformMatrix4fv(ui_attrib.matrix, 1, GL_FALSE, matrix);
|
|
glUniform1i(ui_attrib.sampler, 4);
|
|
glUniform1i(ui_attrib.extra1, 0);
|
|
|
|
// BUTTONS //
|
|
|
|
// PLAY
|
|
GLuint button_left_on = gen_ui_buffer(g->width/2 - (24 * g->scale)*2, g->height/2, 24 * g->scale, 10);
|
|
GLuint button_middle_on = gen_ui_buffer(g->width/2, g->height/2, 24 * g->scale, 11);
|
|
GLuint button_right_on = gen_ui_buffer(g->width/2 + (24 * g->scale)*2, g->height/2, 24 * g->scale, 12);
|
|
|
|
GLuint button_left = gen_ui_buffer(g->width/2 - (24 * g->scale)*2, g->height/2, 24 * g->scale, 6);
|
|
GLuint button_middle = gen_ui_buffer(g->width/2, g->height/2, 24 * g->scale, 7);
|
|
GLuint button_right = gen_ui_buffer(g->width/2 + (24 * g->scale)*2, g->height/2, 24 * g->scale, 8);
|
|
|
|
// EXIT
|
|
GLuint button_exit_on = gen_ui_buffer(g->width/2 + (24 * g->scale)*2, g->height/2 - (24 * g->scale)*2, 24 * g->scale, 9);
|
|
GLuint button_exit = gen_ui_buffer(g->width/2 + (24 * g->scale)*2, g->height/2 - (24 * g->scale)*2, 24 * g->scale, 5);
|
|
|
|
// CONF
|
|
GLuint button_conf_left_on = gen_ui_buffer(g->width/2 - (24 * g->scale)*2, g->height/2 - (24 * g->scale)*2, 24 * g->scale, 10);
|
|
GLuint button_conf_right_on = gen_ui_buffer(g->width/2, g->height/2 - (24 * g->scale)*2, 24 * g->scale, 12);
|
|
|
|
GLuint button_conf_right = gen_ui_buffer(g->width/2 - (24 * g->scale)*2, g->height/2 - (24 * g->scale)*2, 24 * g->scale, 6);
|
|
GLuint button_conf_left = gen_ui_buffer(g->width/2, g->height/2 - (24 * g->scale)*2, 24 * g->scale, 8);
|
|
|
|
// PLAY
|
|
if (cx > (g->width/2-24*3) && cx < (g->width/2+24*3) && cy > (g->height/2-22) && cy < (g->height/2+22)) {
|
|
draw_ui(&ui_attrib, button_left_on);
|
|
draw_ui(&ui_attrib, button_middle_on);
|
|
draw_ui(&ui_attrib, button_right_on);
|
|
|
|
// CLICK ACTION
|
|
if (state == GLFW_PRESS) {
|
|
game_state = 1;
|
|
glfwSetInputMode(g->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
}
|
|
|
|
} else {
|
|
draw_ui(&ui_attrib, button_left);
|
|
draw_ui(&ui_attrib, button_middle);
|
|
draw_ui(&ui_attrib, button_right);
|
|
}
|
|
|
|
// EXIT
|
|
if (cx > (g->width/2+24) && cx < (g->width/2+24*3) && cy > (g->height/2+24) && cy < (g->height/2+24*3)) {
|
|
draw_ui(&ui_attrib, button_exit_on);
|
|
|
|
if (state == GLFW_PRESS) {
|
|
// CLICK ACTION
|
|
printf("\n\n[Omicron] The game is closing ... Have a good day!\n");
|
|
db_save_state(s->x, s->y, s->z, s->rx, s->ry);
|
|
db_close();
|
|
db_disable();
|
|
client_stop();
|
|
client_disable();
|
|
del_buffer(sky_buffer);
|
|
delete_all_chunks();
|
|
delete_all_players();
|
|
|
|
//glfwTerminate();
|
|
curl_global_cleanup();
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
draw_ui(&ui_attrib, button_exit);
|
|
}
|
|
|
|
// CONF
|
|
if (cx > (g->width/2-24*3) && cx < (g->width/2+24) && cy > (g->height/2+24) && cy < (g->height/2+24*3)) {
|
|
draw_ui(&ui_attrib, button_conf_left_on);
|
|
draw_ui(&ui_attrib, button_conf_right_on);
|
|
|
|
if (state == GLFW_PRESS) {
|
|
// CLICK ACTION
|
|
menu_id = 1;
|
|
}
|
|
|
|
} else {
|
|
draw_ui(&ui_attrib, button_conf_left);
|
|
draw_ui(&ui_attrib, button_conf_right);
|
|
}
|
|
|
|
del_buffer(button_left);
|
|
del_buffer(button_middle);
|
|
del_buffer(button_right);
|
|
|
|
// RENDER LOGO //
|
|
//glUniformMatrix4fv(logo_attrib.matrix, 2, GL_TRUE, matrix);
|
|
glUniform1i(ui_attrib.sampler, 5);
|
|
GLuint logo_buffer = gen_logo_buffer(g->width/2, (g->height/5)*4, 256 * g->scale, 8);
|
|
draw_logo(&ui_attrib, logo_buffer);
|
|
del_buffer(logo_buffer);
|
|
|
|
snprintf(text_buffer, 1024, "Credits:");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width - ts*20, (ts+2)*3, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "- Michael Fogleman");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width - ts*20, (ts+2)*2, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "- Twetzel");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width - ts*20, (ts+2)*1, ts, text_buffer, g->width, g->height);
|
|
|
|
//snprintf(text_buffer, 1024, "sample test");
|
|
//render_text(&text_attrib, ALIGN_RIGHT, tx, ty, ts, text_buffer, g->width, g->height);
|
|
|
|
} else if (menu_id == 1) {
|
|
|
|
// BUTTON TEXT //
|
|
|
|
snprintf(text_buffer, 1024, "Configuration");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-6*ts, g->height/2+ts*6, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "* Show lights : %d", SHOW_LIGHTS);
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*10, g->height/2+ts*2, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "* Show crosshair : %d", SHOW_CROSSHAIRS);
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*10, g->height/2+ts, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "* Show infotext : %d", SHOW_INFO_TEXT);
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*10, g->height/2, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "* Show chat : %d", SHOW_CHAT_TEXT);
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*10, g->height/2-ts, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "* Show nametags : %d", SHOW_PLAYER_NAMES);
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*10, g->height/2-ts*2, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "* Font size : %d", FONT_SIZE);
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-ts*10, g->height/2-ts*3, ts, text_buffer, g->width, g->height);
|
|
|
|
snprintf(text_buffer, 1024, "X");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2+ts*11+5, g->height/2+ts*6-6, ts, text_buffer, g->width, g->height);
|
|
|
|
|
|
// MATRIX FOR GUI //
|
|
float matrix[16];
|
|
set_matrix_2d(matrix, g->width, g->height);
|
|
glUseProgram(ui_attrib.program);
|
|
glUniformMatrix4fv(ui_attrib.matrix, 1, GL_FALSE, matrix);
|
|
glUniform1i(ui_attrib.sampler, 4);
|
|
glUniform1i(ui_attrib.extra1, 0);
|
|
|
|
// BUTTONS //
|
|
|
|
GLint offset = (24 * g->scale);
|
|
|
|
// LEAVE BUTTON
|
|
GLuint button_exit_on = gen_ui_buffer(g->width/2 + 8 * offset - 9, g->height/2 + 4 * offset - 6, offset, 9);
|
|
GLuint button_exit = gen_ui_buffer(g->width/2 + 8 * offset - 9, g->height/2 + 4 * offset - 6, offset, 5);
|
|
|
|
// LEAVE FUNCTION *sry long string ahead
|
|
if (cx > (g->width/2 + 8 * offset - 9 - 24) && cx < (g->width/2 + 8 * offset - 9 + 24) && cy > (g->height/2 - 4 * offset + 6 - 24) && cy < (g->height/2 - 4 * offset + 6 + 24)) {
|
|
draw_ui(&ui_attrib, button_exit_on);
|
|
|
|
if (state == GLFW_PRESS) {
|
|
menu_id = 0;
|
|
}
|
|
|
|
} else {
|
|
draw_ui(&ui_attrib, button_exit);
|
|
}
|
|
|
|
// BACKGROUND
|
|
GLuint corner_uleft = gen_ui_buffer(g->width/2 - 8 * offset, g->height/2 + 4 * offset, offset, 17);
|
|
GLuint corner_dleft = gen_ui_buffer(g->width/2 - 8 * offset, g->height/2 - 3 * offset, offset, 19);
|
|
GLuint corner_uright = gen_ui_buffer(g->width/2 + 8 * offset, g->height/2 + 4 * offset, offset, 18);
|
|
GLuint button_dright = gen_ui_buffer(g->width/2 + 8 * offset, g->height/2 - 3 * offset, offset, 20);
|
|
|
|
draw_ui(&ui_attrib, corner_uleft);
|
|
draw_ui(&ui_attrib, corner_dleft);
|
|
draw_ui(&ui_attrib, corner_uright);
|
|
draw_ui(&ui_attrib, button_dright);
|
|
|
|
for (int i = 0; i < 7; ++i) {
|
|
GLuint top_bar = gen_ui_buffer(g->width/2 + i * 2 * offset - 6 * offset, g->height/2 + 4 * offset, offset, 21);
|
|
GLuint bottom_bar = gen_ui_buffer(g->width/2 + i * 2 * offset - 6 * offset, g->height/2 - 3 * offset, offset, 24);
|
|
draw_ui(&ui_attrib, top_bar);
|
|
draw_ui(&ui_attrib, bottom_bar);
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
GLuint filler = gen_ui_buffer(g->width/2 + i * 2 * offset - 6 * offset, g->height/2 + 2 * offset - j * 2 * offset, offset, 25);
|
|
draw_ui(&ui_attrib, filler);
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
GLuint left_bar = gen_ui_buffer(g->width/2 - 8 * offset, g->height/2 + 2 * offset - j * 2 * offset, offset, 23);
|
|
GLuint right_bar = gen_ui_buffer(g->width/2 + 8 * offset, g->height/2 + 2 * offset - j * 2 * offset, offset, 22);
|
|
draw_ui(&ui_attrib, left_bar);
|
|
draw_ui(&ui_attrib, right_bar);
|
|
}
|
|
|
|
|
|
del_buffer(button_exit);
|
|
del_buffer(button_exit_on);
|
|
}
|
|
|
|
// RENDER BACKGROUND //
|
|
glUniform1i(ui_attrib.sampler, 6);
|
|
GLuint bg_buffer = gen_logo_buffer(g->width/2, g->height/2, (g->width * g->scale)*0.75, 1);
|
|
draw_ui(&ui_attrib, bg_buffer);
|
|
del_buffer(bg_buffer);
|
|
|
|
}
|
|
|
|
|
|
// -------------------- //
|
|
// SECOND MODE (NORMAL) //
|
|
// -------------------- //
|
|
|
|
if (game_state == 1) {
|
|
|
|
// FRAME RATE //
|
|
if (g->time_changed) {
|
|
g->time_changed = 0;
|
|
last_commit = glfwGetTime();
|
|
last_update = glfwGetTime();
|
|
memset(&fps, 0, sizeof(fps));
|
|
}
|
|
update_fps(&fps);
|
|
double now = glfwGetTime();
|
|
double dt = now - previous;
|
|
dt = MIN(dt, 0.2);
|
|
dt = MAX(dt, 0.0);
|
|
previous = now;
|
|
|
|
|
|
// PREPARE TO RENDER //
|
|
update_sky_tint();
|
|
g->observe1 = g->observe1 % g->player_count;
|
|
g->observe2 = g->observe2 % g->player_count;
|
|
delete_chunks();
|
|
del_buffer(me->buffer);
|
|
me->buffer = gen_player_buffer(s->x, s->y, s->z, s->rx, s->ry);
|
|
for (int i = 1; i < g->player_count; i++) {
|
|
interpolate_player(g->players + i);
|
|
}
|
|
Player *player = g->players + g->observe1;
|
|
|
|
// RENDER 3-D SCENE //
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
render_sky(&sky_attrib, player, sky_buffer);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
int face_count = render_chunks(&block_attrib, player);
|
|
render_chunks(&tblock_attrib, player);
|
|
render_signs(&text_attrib, player);
|
|
render_sign(&text_attrib, player);
|
|
render_players(&block_attrib, player);
|
|
|
|
// RENDER HUD //
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
if (SHOW_CROSSHAIRS) {
|
|
render_crosshairs(&line_attrib, g->width, g->height, g->scale);
|
|
}
|
|
char text_buffer[1024];
|
|
float ts = FONT_SIZE * g->scale;
|
|
float tx = ts / 2;
|
|
float ty = g->height - ts;
|
|
float tyb = ts;
|
|
if (SHOW_ITEM) {
|
|
render_item(&block_attrib);
|
|
render_item(&tblock_attrib);
|
|
render_item_count(&text_attrib, ts);
|
|
}
|
|
|
|
// RENDER TEXT //
|
|
if (SHOW_INFO_TEXT) {
|
|
int hour = time_of_day() * 24;
|
|
char am_pm = hour < 12 ? 'a' : 'p';
|
|
hour = hour % 12;
|
|
hour = hour ? hour : 12;
|
|
|
|
// First line
|
|
snprintf(text_buffer, 1024, "%d fps | X=%.2f Y=%.2f Z=%.2f | ChunkX=%d ChunkZ=%d",
|
|
fps.fps, s->x, s->y, s->z, chunked(s->x), chunked(s->z));
|
|
render_text(&text_attrib, ALIGN_LEFT, tx, ty, ts, text_buffer, g->width, g->height);
|
|
|
|
// Second line
|
|
snprintf(text_buffer, 1024, "%d %cm | Players=%d | Chunks=%d | Faces=%d",
|
|
hour, am_pm, g->player_count, g->chunk_count, face_count * 2);
|
|
render_text(&text_attrib, ALIGN_LEFT, tx, ty-ts*2, ts, text_buffer, g->width, g->height);
|
|
|
|
|
|
//ty -= ts * 2;
|
|
}
|
|
if (SHOW_CHAT_TEXT==1) {
|
|
for (int i = 0; i < MAX_MESSAGES; i++) {
|
|
int index = (g->message_index + i) % MAX_MESSAGES;
|
|
if (strlen(g->messages[index])) {
|
|
render_text(&text_attrib, ALIGN_LEFT, tx, tyb+ts*2, ts,
|
|
g->messages[index], g->width, g->height);
|
|
tyb += ts * 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// RENDER PICTURE IN PICTURE //
|
|
if (g->observe2) {
|
|
player = g->players + g->observe2;
|
|
|
|
int pw = 64 * g->scale; // 256 seems to slow the game down
|
|
int ph = 64 * g->scale; // what's this for?
|
|
int offset = 32 * g->scale;
|
|
int pad = 3 * g->scale;
|
|
int sw = pw + pad * 2;
|
|
int sh = ph + pad * 2;
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(g->width - sw - offset + pad, offset - pad, sw, sh);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
glViewport(g->width - pw - offset, offset, pw, ph);
|
|
|
|
g->width = pw;
|
|
g->height = ph;
|
|
g->ortho = 0;
|
|
g->fov = 65;
|
|
|
|
render_sky(&sky_attrib, player, sky_buffer);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
render_chunks(&block_attrib, player);
|
|
render_chunks(&tblock_attrib, player);
|
|
render_signs(&text_attrib, player);
|
|
render_players(&block_attrib, player);
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
if (SHOW_PLAYER_NAMES) {
|
|
render_text(&text_attrib, ALIGN_CENTER,
|
|
pw / 2, ts, ts, player->name,
|
|
g->width, g->height);
|
|
}
|
|
}
|
|
|
|
if (inventory==0) {
|
|
if (INFINITE_STUFF == 1) {
|
|
g->inventory.count[items[g->item_index]] = 16;
|
|
//printf("%d %d\n", items[g->item_index], (int) g->inventory.count[items[g->item_index]]);
|
|
}
|
|
|
|
// HANDLE MOUSE INPUT //
|
|
handle_mouse_input();
|
|
|
|
|
|
// HANDLE MOVEMENT //
|
|
handle_movement(dt);
|
|
|
|
|
|
if (g->typing) {
|
|
snprintf(text_buffer, 1024, "> %s_", g->typing_buffer);
|
|
render_text(&text_attrib, ALIGN_LEFT, tx, ts, ts, text_buffer,
|
|
g->width, g->height);
|
|
//tyb += ts * 2;
|
|
}
|
|
if (SHOW_PLAYER_NAMES) {
|
|
if (player != me) {
|
|
render_text(&text_attrib, ALIGN_CENTER,
|
|
g->width / 2, ts, ts, player->name,
|
|
g->width, g->height);
|
|
}
|
|
Player *other = player_crosshair(player);
|
|
if (other) {
|
|
render_text(&text_attrib, ALIGN_CENTER,
|
|
g->width / 2, g->height / 2 - ts - 24, ts,
|
|
other->name, g->width, g->height);
|
|
}
|
|
}
|
|
|
|
// RETURN TO MENU WHEN PRESSING ESCAPE
|
|
int esc = glfwGetKey(g->window, GLFW_KEY_ESCAPE);
|
|
if (esc == GLFW_PRESS) {
|
|
game_state = 0;
|
|
// Clear screen
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
// ENTER INVENTORY WHEN PRESSING I
|
|
int inv = glfwGetKey(g->window, GLFW_KEY_I);
|
|
|
|
// Avoid entering inventory when typing in chat
|
|
if (!g->typing) {
|
|
if (inv == GLFW_PRESS) {
|
|
i_pressed = 1;
|
|
}
|
|
|
|
// Enter inventory on key release to avoid fast toggle
|
|
if (inv == GLFW_RELEASE && i_pressed == 1) {
|
|
//inv = GLFW_RELEASE;
|
|
inventory = 1;
|
|
i_pressed = 0;
|
|
// Clear screen
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
}
|
|
|
|
// OUTPUT IN TERMINAL //
|
|
//advance_cursor();
|
|
}
|
|
|
|
if (inventory==1) {
|
|
// TODO: Allow player to fall when inside inventory
|
|
|
|
// RENDER HUD //
|
|
char text_buffer[1024];
|
|
float ts = FONT_SIZE * g->scale;
|
|
float tx = ts / 2;
|
|
float ty = g->height - ts;
|
|
|
|
// HANDLE MOUSE INPUT //
|
|
handle_mouse_input_in_menu();
|
|
|
|
// RENDER SOME TEXT
|
|
snprintf(text_buffer, 1024, "Inventory");
|
|
render_text(&text_attrib, ALIGN_LEFT, g->width/2-4*ts, g->height/2+6*ts, ts, text_buffer, g->width, g->height);
|
|
|
|
// LEAVE INVENTORY WHEN PRESSING I
|
|
int inv = glfwGetKey(g->window, GLFW_KEY_I);
|
|
|
|
if (inv == GLFW_PRESS) {
|
|
i_pressed = 1;
|
|
}
|
|
if (inv == GLFW_RELEASE && i_pressed == 1) {
|
|
//inv = GLFW_RELEASE;
|
|
inventory = 0;
|
|
i_pressed = 0;
|
|
// Clear screen
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glfwSetInputMode(g->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
}
|
|
|
|
// MATRIX FOR GUI //
|
|
float matrix[16];
|
|
set_matrix_2d(matrix, g->width, g->height);
|
|
glUseProgram(ui_attrib.program);
|
|
glUniformMatrix4fv(ui_attrib.matrix, 1, GL_FALSE, matrix);
|
|
glUniform1i(ui_attrib.sampler, 4);
|
|
glUniform1i(ui_attrib.extra1, 0);
|
|
|
|
// INVENTORY //
|
|
|
|
GLint offset = (24 * g->scale);
|
|
|
|
// BACKGROUND
|
|
GLuint corner_uleft = gen_ui_buffer(g->width/2 - 8 * offset, g->height/2 + 4 * offset, offset, 17);
|
|
GLuint corner_dleft = gen_ui_buffer(g->width/2 - 8 * offset, g->height/2 - 3 * offset, offset, 19);
|
|
GLuint corner_uright = gen_ui_buffer(g->width/2 + 8 * offset, g->height/2 + 4 * offset, offset, 18);
|
|
GLuint button_dright = gen_ui_buffer(g->width/2 + 8 * offset, g->height/2 - 3 * offset, offset, 20);
|
|
|
|
draw_ui(&ui_attrib, corner_uleft);
|
|
draw_ui(&ui_attrib, corner_dleft);
|
|
draw_ui(&ui_attrib, corner_uright);
|
|
draw_ui(&ui_attrib, button_dright);
|
|
|
|
for (int i = 0; i < 7; ++i) {
|
|
GLuint top_bar = gen_ui_buffer(g->width/2 + i * 2 * offset - 6 * offset, g->height/2 + 4 * offset, offset, 21);
|
|
GLuint bottom_bar = gen_ui_buffer(g->width/2 + i * 2 * offset - 6 * offset, g->height/2 - 3 * offset, offset, 24);
|
|
draw_ui(&ui_attrib, top_bar);
|
|
draw_ui(&ui_attrib, bottom_bar);
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
GLuint filler = gen_ui_buffer(g->width/2 + i * 2 * offset - 6 * offset, g->height/2 + 2 * offset - j * 2 * offset, offset, 25);
|
|
draw_ui(&ui_attrib, filler);
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
GLuint left_bar = gen_ui_buffer(g->width/2 - 8 * offset, g->height/2 + 2 * offset - j * 2 * offset, offset, 23);
|
|
GLuint right_bar = gen_ui_buffer(g->width/2 + 8 * offset, g->height/2 + 2 * offset - j * 2 * offset, offset, 22);
|
|
draw_ui(&ui_attrib, left_bar);
|
|
draw_ui(&ui_attrib, right_bar);
|
|
}
|
|
|
|
// ITEMS //
|
|
//for(int i = 0; i < 24; ++i) {
|
|
// GLuint buffer = gen_cube_buffer(20, 20, 20, 5, 25);
|
|
// draw_cube(&block_attrib, buffer);
|
|
// del_buffer(buffer);
|
|
//}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// SHUTDOWN //
|
|
printf("\n\n[Omicron] The game is closing... Have a good day!\n");
|
|
db_save_state(s->x, s->y, s->z, s->rx, s->ry);
|
|
db_close();
|
|
db_disable();
|
|
client_stop();
|
|
client_disable();
|
|
del_buffer(sky_buffer);
|
|
delete_all_chunks();
|
|
delete_all_players();
|
|
}
|
|
|
|
glfwTerminate(); // Causes segfaults???
|
|
curl_global_cleanup();
|
|
return 0;
|
|
}
|