Add player physics and item entities

This commit is contained in:
xtreme8000 2023-10-27 23:08:52 +02:00
parent 7336dacd05
commit 40b0ebed19
28 changed files with 1104 additions and 101 deletions

View file

@ -75,6 +75,10 @@ add_executable(cavex
source/block/block_workbench.c
source/block/face_occlusion.c
source/entity/entity.c
source/entity/entity_local_player.c
source/entity/entity_item.c
source/cNBT/buffer.c
source/cNBT/nbt_loading.c
source/cNBT/nbt_parsing.c

View file

@ -23,7 +23,7 @@ include $(DEVKITPPC)/wii_rules
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source source/block source/graphics source/network source/game source/game/gui source/platform source/item source/cNBT source/lodepng source/parson
SOURCES := source source/block source/entity source/graphics source/network source/game source/game/gui source/platform source/item source/cNBT source/lodepng source/parson
DATA :=
TEXTURES := textures
INCLUDES :=

View file

@ -59,6 +59,7 @@ enum block_type {
BLOCK_DOUBLE_SLAB = 43,
BLOCK_MOSSY_COBBLE = 48,
BLOCK_OBSIDIAN = 49,
BLOCK_LADDER = 65,
BLOCK_SNOW = 78,
BLOCK_ICE = 79,
BLOCK_CACTUS = 81,

301
source/entity/entity.c Normal file
View file

@ -0,0 +1,301 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#include "entity.h"
#include "../network/server_world.h"
#include "../platform/gfx.h"
#include "../world.h"
void entity_default_init(struct entity* e, bool server, void* world) {
e->on_server = server;
e->world = world;
e->on_ground = true;
e->delay_destroy = -1;
glm_vec3_zero(e->pos);
glm_vec3_zero(e->pos_old);
glm_vec3_zero(e->network_pos);
glm_vec3_zero(e->vel);
glm_vec2_zero(e->orient);
glm_vec2_zero(e->orient_old);
}
void entity_default_teleport(struct entity* e, vec3 pos) {
e->on_ground = false;
glm_vec3_copy(pos, e->pos);
glm_vec3_copy(pos, e->pos_old);
glm_vec3_copy(pos, e->network_pos);
}
bool entity_default_client_tick(struct entity* e) {
assert(e);
glm_vec3_copy(e->pos, e->pos_old);
glm_vec3_copy(e->network_pos, e->pos);
glm_vec2_copy(e->orient, e->orient_old);
return false;
}
bool entity_get_block(struct entity* e, w_coord_t x, w_coord_t y, w_coord_t z,
struct block_data* blk) {
assert(e && blk);
if(e->on_server) {
return server_world_get_block(e->world, x, y, z, blk);
} else {
*blk = world_get_block(e->world, x, y, z);
return true;
}
}
void entity_shadow(struct entity* e, struct AABB* a, mat4 view) {
assert(e && a && view);
w_coord_t min_x = floorf(a->x1);
w_coord_t min_y = floorf(a->y1);
w_coord_t min_z = floorf(a->z1);
w_coord_t max_x = ceilf(a->x2) + 1;
w_coord_t max_y = ceilf(a->y2) + 1;
w_coord_t max_z = ceilf(a->z2) + 1;
gfx_matrix_modelview(view);
gfx_blending(MODE_BLEND);
gfx_alpha_test(false);
gfx_bind_texture(&texture_shadow);
gfx_lighting(false);
float offset = 0.01F;
float du = 1.0F / (a->x2 - a->x1);
float dv = 1.0F / (a->z2 - a->z1);
for(w_coord_t x = min_x; x < max_x; x++) {
for(w_coord_t z = min_z; z < max_z; z++) {
for(w_coord_t y = min_y; y < max_y; y++) {
struct block_data blk;
if(entity_get_block(e, x, y, z, &blk) && blocks[blk.type]) {
struct AABB b;
if(blocks[blk.type]->getBoundingBox(
&(struct block_info) {.block = &blk,
.neighbours = NULL,
.x = x,
.y = y,
.z = z},
true, &b)) {
aabb_translate(&b, x, y, z);
if(a->y2 > b.y2 && aabb_intersection(a, &b)) {
float u1 = (b.x1 - a->x1) * du;
float u2 = (b.x2 - a->x1) * du;
float v1 = (b.z1 - a->z1) * dv;
float v2 = (b.z2 - a->z1) * dv;
gfx_draw_quads_flt(
4,
(float[]) {b.x1, b.y2 + offset, b.z1, b.x2,
b.y2 + offset, b.z1, b.x2,
b.y2 + offset, b.z2, b.x1,
b.y2 + offset, b.z2},
(uint8_t[]) {0xFF, 0xFF, 0xFF, 0x60, 0xFF, 0xFF,
0xFF, 0x60, 0xFF, 0xFF, 0xFF, 0x60,
0xFF, 0xFF, 0xFF, 0x60},
(float[]) {u1, v1, u2, v1, u2, v2, u1, v2});
}
}
}
}
}
}
gfx_blending(MODE_OFF);
gfx_alpha_test(true);
gfx_lighting(true);
}
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
bool entity_aabb_intersection(struct entity* e, struct AABB* a,
bool (*test)(struct block_data*, w_coord_t,
w_coord_t, w_coord_t)) {
assert(e && a);
w_coord_t min_x = floorf(a->x1);
// need to look one further, otherwise fence block breaks
w_coord_t min_y = max((w_coord_t)(floorf(a->y1) - 1), 0);
w_coord_t min_z = floorf(a->z1);
w_coord_t max_x = ceilf(a->x2) + 1;
w_coord_t max_y = min((w_coord_t)(ceilf(a->y2) + 1), WORLD_HEIGHT - 1);
w_coord_t max_z = ceilf(a->z2) + 1;
for(w_coord_t x = min_x; x < max_x; x++) {
for(w_coord_t z = min_z; z < max_z; z++) {
for(w_coord_t y = min_y; y < max_y; y++) {
struct block_data blk;
if(entity_get_block(e, x, y, z, &blk) && blocks[blk.type]) {
struct AABB b;
if(blocks[blk.type]->getBoundingBox(
&(struct block_info) {.block = &blk,
.neighbours = NULL,
.x = x,
.y = y,
.z = z},
true, &b)
|| (test && test(&blk, x, y, z))) {
aabb_translate(&b, x, y, z);
if(aabb_intersection(a, &b)
&& (!test || test(&blk, x, y, z)))
return true;
}
}
}
}
}
return false;
}
bool entity_intersection_threshold(struct entity* e, struct AABB* aabb,
vec3 old_pos, vec3 new_pos,
float* threshold) {
assert(e && aabb && old_pos && new_pos && threshold);
struct AABB tmp = *aabb;
aabb_translate(&tmp, old_pos[0], old_pos[1], old_pos[2]);
bool a = entity_aabb_intersection(e, &tmp, NULL);
tmp = *aabb;
aabb_translate(&tmp, new_pos[0], new_pos[1], new_pos[2]);
bool b = entity_aabb_intersection(e, &tmp, NULL);
if(!a && b) {
float range_min = 0.0F;
float range_max = 1.0F;
while(1) {
vec3 pos_min, pos_max;
glm_vec3_lerp(old_pos, new_pos, range_min, pos_min);
glm_vec3_lerp(old_pos, new_pos, range_max, pos_max);
if(glm_vec3_distance2(pos_min, pos_max) < glm_pow2(0.01F))
break;
float mid = (range_max + range_min) / 2.0F;
vec3 pos_mid;
glm_vec3_lerp(old_pos, new_pos, mid, pos_mid);
struct AABB dest = *aabb;
aabb_translate(&dest, pos_mid[0], pos_mid[1], pos_mid[2]);
if(entity_aabb_intersection(e, &dest, NULL)) {
range_max = mid;
} else {
range_min = mid;
}
}
*threshold = range_min;
return true;
} else if(a) {
*threshold = 0.0F;
return true;
} else {
*threshold = 1.0F;
return false;
}
}
void entity_try_move(struct entity* e, vec3 pos, vec3 vel, struct AABB* bbox,
size_t coord, bool* collision_xz, bool* on_ground) {
assert(e && pos && vel && bbox && collision_xz && on_ground);
vec3 tmp;
glm_vec3_copy(pos, tmp);
tmp[coord] += vel[coord];
float threshold;
if(entity_intersection_threshold(e, bbox, pos, tmp, &threshold)) {
if(coord == 1 && vel[1] < 0.0F)
*on_ground = true;
if(coord == 0 || coord == 2)
*collision_xz = true;
vel[coord] = 0.0F;
} else if(coord == 1) {
*on_ground = false;
}
pos[coord] = pos[coord] * (1.0F - threshold) + tmp[coord] * threshold;
}
uint32_t entity_gen_id(dict_entity_t dict) {
assert(dict);
dict_entity_it_t it;
dict_entity_it(it, dict);
// id = 0 is reserved for local player
uint32_t id = 0;
while(!dict_entity_end_p(it)) {
uint32_t key = dict_entity_ref(it)->key;
if(key > id)
id = key;
dict_entity_next(it);
}
return id + 1;
}
void entities_client_tick(dict_entity_t dict) {
dict_entity_it_t it;
dict_entity_it(it, dict);
while(!dict_entity_end_p(it)) {
struct entity* e = &dict_entity_ref(it)->value;
if(e->tick_client)
e->tick_client(e);
dict_entity_next(it);
}
}
void entities_client_render(dict_entity_t dict, struct camera* c,
float tick_delta) {
dict_entity_it_t it;
dict_entity_it(it, dict);
while(!dict_entity_end_p(it)) {
struct entity* e = &dict_entity_ref(it)->value;
if(e->render
&& glm_vec3_distance2(e->pos, (vec3) {c->x, c->y, c->z})
< glm_pow2(32.0F))
e->render(e, c->view, tick_delta);
dict_entity_next(it);
}
}

99
source/entity/entity.h Normal file
View file

@ -0,0 +1,99 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ENTITY_H
#define ENTITY_H
#include <m-lib/m-dict.h>
#include <stdbool.h>
#include "../cglm/cglm.h"
#include "../item/items.h"
enum entity_type {
ENTITY_LOCAL_PLAYER,
ENTITY_ITEM,
};
struct server_local;
struct entity {
uint32_t id;
bool on_server;
void* world;
int delay_destroy;
vec3 pos;
vec3 pos_old;
vec3 vel;
vec2 orient;
vec2 orient_old;
bool on_ground;
vec3 network_pos;
bool (*tick_client)(struct entity*);
bool (*tick_server)(struct entity*, struct server_local*);
void (*render)(struct entity*, mat4, float);
void (*teleport)(struct entity*, vec3);
enum entity_type type;
union entity_data {
struct entity_local_player {
int jump_ticks;
bool capture_input;
} local_player;
struct entity_item {
struct item_data item;
int age;
} item;
} data;
};
DICT_DEF2(dict_entity, uint32_t, M_BASIC_OPLIST, struct entity, M_POD_OPLIST)
#include "../world.h"
void entity_local_player(uint32_t id, struct entity* e, struct world* w);
void entity_item(uint32_t id, struct entity* e, bool server, void* world,
struct item_data it);
uint32_t entity_gen_id(dict_entity_t dict);
void entities_client_tick(dict_entity_t dict);
void entities_client_render(dict_entity_t dict, struct camera* c,
float tick_delta);
void entity_default_init(struct entity* e, bool server, void* world);
void entity_default_teleport(struct entity* e, vec3 pos);
bool entity_default_client_tick(struct entity* e);
void entity_shadow(struct entity* e, struct AABB* a, mat4 view);
bool entity_get_block(struct entity* e, w_coord_t x, w_coord_t y, w_coord_t z,
struct block_data* blk);
bool entity_intersection_threshold(struct entity* e, struct AABB* aabb,
vec3 old_pos, vec3 new_pos,
float* threshold);
bool entity_aabb_intersection(struct entity* e, struct AABB* a,
bool (*test)(struct block_data*, w_coord_t,
w_coord_t, w_coord_t));
void entity_try_move(struct entity* e, vec3 pos, vec3 vel, struct AABB* bbox,
size_t coord, bool* collision_xz, bool* on_ground);
#endif

205
source/entity/entity_item.c Normal file
View file

@ -0,0 +1,205 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../block/blocks_data.h"
#include "../game/game_state.h"
#include "../network/client_interface.h"
#include "../network/server_local.h"
#include "../platform/gfx.h"
#include "entity.h"
static bool entity_client_tick(struct entity* e) {
entity_default_client_tick(e);
e->data.item.age++;
return false;
}
static bool entity_server_tick(struct entity* e, struct server_local* s) {
assert(e);
glm_vec3_copy(e->pos, e->pos_old);
glm_vec2_copy(e->orient, e->orient_old);
for(int k = 0; k < 3; k++)
if(fabsf(e->vel[k]) < 0.005F)
e->vel[k] = 0.0F;
struct AABB bbox;
aabb_setsize_centered(&bbox, 0.25F, 0.25F, 0.25F);
struct AABB tmp = bbox;
aabb_translate(&tmp, e->pos[0], e->pos[1], e->pos[2]);
if(entity_aabb_intersection(e, &tmp, NULL)) { // is item stuck in block?
// find possible new position, try top/bottom last
enum side sides[6] = {SIDE_LEFT, SIDE_RIGHT, SIDE_FRONT,
SIDE_BACK, SIDE_TOP, SIDE_BOTTOM};
for(int k = 0; k < 6; k++) {
int x, y, z;
blocks_side_offset(sides[k], &x, &y, &z);
vec3 new_pos;
glm_vec3_add(e->pos, (vec3) {x, y, z}, new_pos);
struct AABB tmp2 = tmp;
aabb_translate(&tmp2, x, y, z);
if(!entity_aabb_intersection(e, &tmp2, NULL)) {
float threshold;
entity_intersection_threshold(e, &bbox, new_pos, e->pos,
&threshold);
glm_vec3_lerp(new_pos, e->pos, threshold, e->pos);
e->vel[0] = x * 0.1F;
e->vel[1] = y * 0.1F;
e->vel[2] = z * 0.1F;
break;
}
}
}
bool collision_xz = false;
for(int k = 0; k < 3; k++)
entity_try_move(e, e->pos, e->vel, &bbox, (size_t[]) {1, 0, 2}[k],
&collision_xz, &e->on_ground);
e->vel[1] -= 0.04F;
e->vel[0] *= (e->on_ground ? 0.6F : 1.0F) * 0.98F;
e->vel[2] *= (e->on_ground ? 0.6F : 1.0F) * 0.98F;
e->vel[1] *= 0.98F;
e->data.item.age++;
if(e->delay_destroy > 0) {
e->delay_destroy--;
} else if(e->data.item.age >= 2 * 20
&& glm_vec3_distance2(
e->pos,
(vec3) {s->player.x, s->player.y - 0.6F, s->player.z})
< glm_pow2(2.0F)) { // allow pickup after 2s
bool slots_changed[INVENTORY_SIZE];
memset(slots_changed, false, sizeof(slots_changed));
// TODO: case where item cannot be picked up completely
inventory_collect_inventory(&s->player.inventory, &e->data.item.item,
slots_changed);
for(size_t k = 0; k < INVENTORY_SIZE; k++) {
if(slots_changed[k])
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_INVENTORY_SLOT,
.payload.inventory_slot.window = WINDOWC_INVENTORY,
.payload.inventory_slot.slot = k,
.payload.inventory_slot.item = s->player.inventory.items[k],
});
}
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_PICKUP_ITEM,
.payload.pickup_item.entity_id = e->id,
.payload.pickup_item.collector_id = 0, // local player
});
e->delay_destroy = 1;
}
return e->data.item.age >= 5 * 60 * 20; // destroy after 5 min
}
static void entity_render(struct entity* e, mat4 view, float tick_delta) {
struct item* it = item_get(&e->data.item.item);
if(it) {
vec3 pos_lerp;
glm_vec3_lerp(e->pos_old, e->pos, tick_delta, pos_lerp);
struct block_data in_block;
entity_get_block(e, floorf(pos_lerp[0]), floorf(pos_lerp[1]),
floorf(pos_lerp[2]), &in_block);
render_item_update_light((in_block.torch_light << 4)
| in_block.sky_light);
float ticks = e->data.item.age + tick_delta;
mat4 model;
glm_translate_make(model, pos_lerp);
glm_translate_y(model, sinf(ticks / 30.0F * GLM_PIf) * 0.1F + 0.1F);
glm_rotate_y(model, glm_rad(ticks * 3.0F), model);
glm_scale_uni(model, 0.25F);
glm_translate(model, (vec3) {-0.5F, -0.5F, -0.5F});
mat4 mv;
glm_mat4_mul(view, model, mv);
int amount = 1;
if(e->data.item.item.count > 1) {
amount = 2;
} else if(e->data.item.item.count > 5) {
amount = 3;
} else if(e->data.item.item.count > 20) {
amount = 4;
}
vec3 displacement[4] = {
{0.0F, 0.0F, 0.0F},
{-0.701F, -0.331F, -0.239F},
{0.139F, -0.276F, 0.211F},
{0.443F, 0.512F, -0.101F},
};
for(int k = 0; k < amount; k++) {
mat4 final;
glm_translate_make(final, displacement[k]);
glm_mat4_mul(mv, final, final);
it->renderItem(it, &e->data.item.item, final, false,
R_ITEM_ENV_ENTITY);
}
struct AABB bbox;
aabb_setsize_centered(&bbox, 0.25F, 0.25F, 0.25F);
aabb_translate(&bbox, pos_lerp[0], pos_lerp[1] - 0.04F, pos_lerp[2]);
entity_shadow(e, &bbox, view);
}
}
void entity_item(uint32_t id, struct entity* e, bool server, void* world,
struct item_data it) {
assert(e && world);
e->id = id;
e->tick_server = entity_server_tick;
e->tick_client = entity_client_tick;
e->render = entity_render;
e->teleport = entity_default_teleport;
e->type = ENTITY_ITEM;
e->data.item.age = 0;
e->data.item.item = it;
entity_default_init(e, server, world);
if(server) {
glm_vec3_copy(
(vec3) {rand_flt() - 0.5F, rand_flt() - 0.5F, rand_flt() - 0.5F},
e->vel);
glm_vec3_normalize(e->vel);
glm_vec3_scale(e->vel, (2.0F * rand_flt() + 0.5F) * 0.1F, e->vel);
}
}

View file

@ -0,0 +1,201 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../block/blocks_data.h"
#include "../platform/input.h"
#include "entity.h"
static bool test_in_lava(struct block_data* blk, w_coord_t x, w_coord_t y,
w_coord_t z) {
return blk->type == BLOCK_LAVA_FLOW || blk->type == BLOCK_LAVA_STILL;
}
static bool test_in_water(struct block_data* blk, w_coord_t x, w_coord_t y,
w_coord_t z) {
return blk->type == BLOCK_WATER_FLOW || blk->type == BLOCK_WATER_STILL;
}
static bool test_in_liquid(struct block_data* blk, w_coord_t x, w_coord_t y,
w_coord_t z) {
return test_in_water(blk, x, y, z) || test_in_lava(blk, x, y, z);
}
static bool entity_tick(struct entity* e) {
assert(e);
glm_vec3_copy(e->pos, e->pos_old);
glm_vec2_copy(e->orient, e->orient_old);
for(int k = 0; k < 3; k++)
if(fabsf(e->vel[k]) < 0.005F)
e->vel[k] = 0.0F;
struct AABB bbox;
aabb_setsize_centered(&bbox, 0.6F, 1.0F, 0.6F);
aabb_translate(&bbox, e->pos[0], e->pos[1] + 1.8F / 2.0F - 1.62F,
e->pos[2]);
bool in_water = entity_aabb_intersection(e, &bbox, test_in_water);
bool in_lava = entity_aabb_intersection(e, &bbox, test_in_lava);
float slipperiness
= (in_lava || in_water) ? 1.0F : (e->on_ground ? 0.6F : 1.0F);
int forward = 0;
int strafe = 0;
bool jumping = false;
if(e->data.local_player.capture_input) {
if(input_held(IB_FORWARD))
forward++;
if(input_held(IB_BACKWARD))
forward--;
if(input_held(IB_RIGHT))
strafe++;
if(input_held(IB_LEFT))
strafe--;
jumping = input_held(IB_JUMP);
}
int dist = forward * forward + strafe * strafe;
if(dist > 0) {
float distf = fmaxf(sqrtf(dist), 1.0F);
float dx = (forward * sinf(e->orient[0]) - strafe * cosf(e->orient[0]))
/ distf;
float dy = (strafe * sinf(e->orient[0]) + forward * cosf(e->orient[0]))
/ distf;
e->vel[0] += 0.1F * powf(0.6F / slipperiness, 3.0F) * dx;
e->vel[2] += 0.1F * powf(0.6F / slipperiness, 3.0F) * dy;
}
if(e->data.local_player.jump_ticks > 0)
e->data.local_player.jump_ticks--;
if(jumping) {
if(in_water || in_lava) {
e->vel[1] += 0.04F;
} else if(e->on_ground && e->data.local_player.jump_ticks == 0) {
e->vel[1] = 0.42F;
e->data.local_player.jump_ticks = 10;
}
} else {
e->data.local_player.jump_ticks = 0;
}
float eye_height = 1.62F;
aabb_setsize_centered(&bbox, 0.6F, 1.8F, 0.6F);
aabb_translate(&bbox, 0.0F, 1.8F / 2.0F - eye_height, 0.0F);
vec3 new_pos, new_vel;
glm_vec3_copy(e->pos, new_pos);
glm_vec3_copy(e->vel, new_vel);
bool collision_xz = false;
for(int k = 0; k < 3; k++)
entity_try_move(e, e->pos, e->vel, &bbox, (size_t[]) {1, 0, 2}[k],
&collision_xz, &e->on_ground);
if(e->on_ground) {
bool collision = false;
bool ground = e->on_ground;
new_vel[1] = 0.6F;
entity_try_move(e, new_pos, new_vel, &bbox, 1, &collision, &ground);
new_vel[1] = 0.0F;
entity_try_move(e, new_pos, new_vel, &bbox, 0, &collision, &ground);
entity_try_move(e, new_pos, new_vel, &bbox, 2, &collision, &ground);
new_vel[1] = -0.6F;
entity_try_move(e, new_pos, new_vel, &bbox, 1, &collision, &ground);
if(glm_vec3_distance2(e->pos_old, e->pos)
< glm_vec3_distance2(e->pos_old, new_pos)) {
collision_xz = collision;
e->on_ground = ground;
glm_vec3_copy(new_pos, e->pos);
glm_vec3_copy(new_vel, e->vel);
}
}
if(in_lava) {
e->vel[0] *= 0.5F;
e->vel[2] *= 0.5F;
e->vel[1] = e->vel[1] * 0.5F - 0.02F;
} else if(in_water) {
e->vel[0] *= 0.8F;
e->vel[2] *= 0.8F;
e->vel[1] = e->vel[1] * 0.8F - 0.02F;
} else {
e->vel[0] *= slipperiness * 0.91F;
e->vel[2] *= slipperiness * 0.91F;
e->vel[1] -= 0.08F;
struct block_data blk;
if(entity_get_block(e, floorf(e->pos[0]),
floorf(e->pos[1] - eye_height), floorf(e->pos[2]),
&blk)
&& blk.type == BLOCK_LADDER) {
if(collision_xz)
e->vel[1] = 0.12F;
e->vel[0] = fmaxf(fminf(e->vel[0], 0.15F), -0.15F);
e->vel[1] = fmaxf(e->vel[1], -0.15F);
e->vel[2] = fmaxf(fminf(e->vel[2], 0.15F), -0.15F);
}
e->vel[1] *= 0.98F;
}
if(collision_xz && (in_lava || in_water)) {
struct AABB tmp;
aabb_setsize_centered(&tmp, 0.6F, 1.8F, 0.6F);
aabb_translate(&tmp, e->pos[0] + e->vel[0],
e->pos[1] + e->vel[1] + 1.8F / 2.0F - 1.62F + 0.6F,
e->pos[2] + e->vel[2]);
if(!entity_aabb_intersection(e, &tmp, test_in_liquid))
e->vel[1] = 0.3F;
}
return false;
}
void entity_local_player(uint32_t id, struct entity* e, struct world* w) {
assert(e && w);
e->id = id;
e->tick_server = NULL;
e->tick_client = entity_tick;
e->render = NULL;
e->teleport = entity_default_teleport;
e->type = ENTITY_LOCAL_PLAYER;
e->data.local_player.capture_input = false;
entity_default_init(e, false, w);
e->data.local_player.jump_ticks = 0;
}

View file

@ -195,3 +195,23 @@ void camera_update(struct camera* c) {
glm_mat4_mul(c->projection, c->view, view_proj);
glm_frustum_planes(view_proj, c->frustum_planes);
}
void camera_attach(struct camera* c, struct entity* e, float tick_delta,
float dt) {
vec3 pos_lerp;
glm_vec3_lerp(e->pos_old, e->pos, tick_delta, pos_lerp);
c->x = pos_lerp[0];
c->y = pos_lerp[1];
c->z = pos_lerp[2];
float jdx, jdy;
if(input_joystick(dt, &jdx, &jdy)) {
c->rx -= jdx * 2.0F;
c->ry -= jdy * 2.0F;
}
c->ry = glm_clamp(c->ry, glm_rad(0.5F), GLM_PI - glm_rad(0.5F));
e->orient[0] = c->rx;
e->orient[1] = c->ry;
}

View file

@ -34,6 +34,7 @@ struct camera {
} controller;
};
#include "../entity/entity.h"
#include "../world.h"
struct camera_ray_result {
@ -47,5 +48,7 @@ void camera_ray_pick(struct world* w, float gx0, float gy0, float gz0,
struct camera_ray_result* res);
void camera_physics(struct camera* c, float dt);
void camera_update(struct camera* c);
void camera_attach(struct camera* c, struct entity* e, float tick_delta,
float dt);
#endif

View file

@ -25,6 +25,7 @@
#include <stddef.h>
#include "../config.h"
#include "../entity/entity.h"
#include "../item/window_container.h"
#include "../platform/time.h"
#include "../world.h"
@ -53,6 +54,8 @@ struct game_state {
struct camera camera;
struct camera_ray_result camera_hit;
struct world world;
struct entity* local_player;
dict_entity_t entities;
uint64_t world_time;
ptime_t world_time_start;
struct window_container* windows[256];

View file

@ -33,6 +33,9 @@
static void screen_ingame_reset(struct screen* s, int width, int height) {
input_pointer_enable(false);
if(gstate.local_player)
gstate.local_player->data.local_player.capture_input = true;
}
void screen_ingame_render3D(struct screen* s, mat4 view) {
@ -106,7 +109,6 @@ void screen_ingame_render3D(struct screen* s, mat4 view) {
uint8_t light = (in_block.torch_light << 4) | in_block.sky_light;
gfx_depth_range(0.0F, 0.1F);
gfx_write_buffers(true, true, true);
struct item_data item;
if(inventory_get_slot(windowc_get_latest(gstate.windows[WINDOWC_INVENTORY]),
@ -132,13 +134,10 @@ void screen_ingame_render3D(struct screen* s, mat4 view) {
gfx_lookup_light(light));
}
gfx_write_buffers(true, false, false);
gfx_depth_range(0.0F, 1.0F);
}
static void screen_ingame_update(struct screen* s, float dt) {
camera_physics(&gstate.camera, dt);
if(gstate.camera_hit.hit && input_pressed(IB_ACTION2)
&& !gstate.digging.active) {
struct item_data item;
@ -393,7 +392,6 @@ static void screen_ingame_render2D(struct screen* s, int width, int height) {
* inventory_get_hotbar(windowc_get_latest(
gstate.windows[WINDOWC_INVENTORY])),
height - 32 * 8 / 5 - 23 * 2, 208, 0, 24, 24, 24 * 2, 24 * 2);
gfx_blending(MODE_OFF);
}
struct screen screen_ingame = {

View file

@ -45,6 +45,10 @@ static size_t selected_slot;
static void screen_inventory_reset(struct screen* s, int width, int height) {
input_pointer_enable(true);
if(gstate.local_player)
gstate.local_player->data.local_player.capture_input = false;
s->render3D = screen_ingame.render3D;
pointer_available = false;

View file

@ -25,6 +25,9 @@
static void screen_lworld_reset(struct screen* s, int width, int height) {
input_pointer_enable(false);
if(gstate.local_player)
gstate.local_player->data.local_player.capture_input = false;
}
static void screen_lworld_update(struct screen* s, float dt) {

View file

@ -55,6 +55,9 @@ struct world_option {
static void screen_sworld_reset(struct screen* s, int width, int height) {
input_pointer_enable(true);
if(gstate.local_player)
gstate.local_player->data.local_player.capture_input = false;
if(worlds) {
while(!stack_empty(worlds)) {
struct world_option opt;

View file

@ -55,9 +55,9 @@ void render_item_update_light(uint8_t light) {
memset(vertex_light, light, sizeof(vertex_light));
}
void render_item_flat(struct item* item, struct item_data* stack, mat4 model,
void render_item_flat(struct item* item, struct item_data* stack, mat4 view,
bool fullbright, enum render_item_env env) {
assert(item && stack && model);
assert(item && stack && view);
uint8_t s, t;
@ -89,7 +89,7 @@ void render_item_flat(struct item* item, struct item_data* stack, mat4 model,
}
if(env == R_ITEM_ENV_INVENTORY) {
gfx_matrix_modelview(model);
gfx_matrix_modelview(view);
gutil_texquad(0, 0, s, t, 16, 16, 16 * 2, 16 * 2);
} else {
displaylist_reset(&dl);
@ -200,21 +200,42 @@ void render_item_flat(struct item* item, struct item_data* stack, mat4 model,
displaylist_texcoord(&dl, s + 16, t + k + 1);
}
if(env == R_ITEM_ENV_THIRDPERSON) {
glm_translate(model, (vec3) {-4.0F, -8.0F, -7.0F});
glm_rotate_x(model, glm_rad(60.0F), model);
glm_scale(model, (vec3) {9.0F, 9.0F, 9.0F});
mat4 model;
switch(env) {
case R_ITEM_ENV_THIRDPERSON:
glm_translate_make(model, (vec3) {-4.0F, -8.0F, -7.0F});
glm_rotate_x(model, glm_rad(60.0F), model);
glm_scale(model, (vec3) {9.0F, 9.0F, 9.0F});
glm_translate(model, (vec3) {0.5F, 0.2F, 0.5F});
glm_scale_uni(model, 1.5F);
glm_rotate_y(model, glm_rad(90.0F), model);
glm_rotate_z(model, glm_rad(335.0F), model);
glm_translate(
model, (vec3) {1.0F / 16.0F - 1.0F, -1.0F / 16.0F, 0.0F});
break;
case R_ITEM_ENV_ENTITY:
glm_mat4_identity(model);
glm_scale_uni(model, 1.5F);
glm_translate_make(model,
(vec3) {0.0F, 0.0F, 0.5F + 1.0F / 32.0F});
break;
case R_ITEM_ENV_FIRSTPERSON:
glm_translate_make(model, (vec3) {0.5F, 0.2F, 0.5F});
glm_scale_uni(model, 1.5F);
glm_rotate_y(model, glm_rad(50.0F), model);
glm_rotate_z(model, glm_rad(335.0F), model);
glm_translate(
model, (vec3) {1.0F / 16.0F - 1.0F, -1.0F / 16.0F, 0.0F});
break;
default: break;
}
glm_translate(model, (vec3) {0.5F, 0.2F, 0.5F});
glm_scale_uni(model, 1.5F);
glm_rotate_y(model,
glm_rad(env == R_ITEM_ENV_FIRSTPERSON ? 50.0F : 90.0F),
model);
glm_rotate_z(model, glm_rad(335.0F), model);
glm_translate(model, (vec3) {1.0F / 16.0F - 1.0F, -1.0F / 16.0F, 0.0F});
mat4 modelview;
glm_mat4_mul(view, model, modelview);
gfx_matrix_modelview(modelview);
gfx_matrix_modelview(model);
gfx_lighting(true);
displaylist_render_immediate(&dl, (2 + 16 * 4) * 4);
gfx_lighting(false);
@ -223,9 +244,9 @@ void render_item_flat(struct item* item, struct item_data* stack, mat4 model,
gfx_matrix_modelview(GLM_MAT4_IDENTITY);
}
void render_item_block(struct item* item, struct item_data* stack, mat4 model,
void render_item_block(struct item* item, struct item_data* stack, mat4 view,
bool fullbright, enum render_item_env env) {
assert(item && stack && model);
assert(item && stack && view);
assert(item_is_block(stack));
struct block* b = blocks[stack->id];
@ -282,33 +303,33 @@ void render_item_block(struct item* item, struct item_data* stack, mat4 model,
fullbright ? vertex_light_inv : vertex_light, false);
}
mat4 view;
mat4 model;
if(env == R_ITEM_ENV_INVENTORY) {
glm_translate_make(view, (vec3) {3 * 2, 3 * 2, -16});
glm_scale(view, (vec3) {20, 20, -20});
glm_translate(view, (vec3) {0.5F, 0.5F, 0.5F});
glm_rotate_z(view, glm_rad(180.0F), view);
glm_rotate_x(view, glm_rad(-30.0F), view);
glm_translate_make(model, (vec3) {3 * 2, 3 * 2, -16});
glm_scale(model, (vec3) {20, 20, -20});
glm_translate(model, (vec3) {0.5F, 0.5F, 0.5F});
glm_rotate_z(model, glm_rad(180.0F), model);
glm_rotate_x(model, glm_rad(-30.0F), model);
glm_rotate_y(
view,
model,
glm_rad((item->render_data.block.has_default ?
item->render_data.block.default_rotation * 90.0F :
0)
- 45.0F),
view);
glm_translate(view, (vec3) {-0.5F, -0.5F, -0.5F});
model);
glm_translate(model, (vec3) {-0.5F, -0.5F, -0.5F});
} else if(env == R_ITEM_ENV_THIRDPERSON) {
glm_translate_make(view, (vec3) {-4.0F, -14.0F, 3.0F});
glm_rotate_x(view, glm_rad(22.5F), view);
glm_rotate_y(view, glm_rad(45.0F), view);
glm_scale(view, (vec3) {6.0F, 6.0F, 6.0F});
glm_translate_make(model, (vec3) {-4.0F, -14.0F, 3.0F});
glm_rotate_x(model, glm_rad(22.5F), model);
glm_rotate_y(model, glm_rad(45.0F), model);
glm_scale(model, (vec3) {6.0F, 6.0F, 6.0F});
} else {
glm_mat4_identity(view);
glm_mat4_identity(model);
}
mat4 modelview;
glm_mat4_mul(model, view, modelview);
glm_mat4_mul(view, model, modelview);
gfx_matrix_modelview(modelview);
gfx_bind_texture(b->transparent ? &texture_anim : &texture_terrain);

View file

@ -26,9 +26,9 @@
void render_item_init(void);
void render_item_update_light(uint8_t light);
void render_item_flat(struct item* item, struct item_data* stack, mat4 model,
void render_item_flat(struct item* item, struct item_data* stack, mat4 view,
bool fullbright, enum render_item_env env);
void render_item_block(struct item* item, struct item_data* stack, mat4 model,
void render_item_block(struct item* item, struct item_data* stack, mat4 view,
bool fullbright, enum render_item_env env);
#endif

View file

@ -85,7 +85,7 @@ void inventory_consume(struct inventory* inv, size_t slot) {
#define min(a, b) ((a) < (b) ? (a) : (b))
bool inventory_collect(struct inventory* inv, struct item_data* item,
size_t slot_start, size_t slot_length, bool* mask) {
uint8_t* slot_priority, size_t slot_length, bool* mask) {
assert(inv && item && item->id != 0 && mask);
struct item* it = item_get(item);
@ -99,18 +99,20 @@ bool inventory_collect(struct inventory* inv, struct item_data* item,
bool has_canidate_empty = false;
size_t candidate_empty = 0;
for(size_t k = slot_start; k < slot_start + slot_length; k++) {
if(inv->items[k].id == item->id
&& inv->items[k].durability == item->durability
&& inv->items[k].count < it->max_stack) {
for(size_t k = 0; k < slot_length; k++) {
uint8_t slot = slot_priority[k];
if(inv->items[slot].id == item->id
&& inv->items[slot].durability == item->durability
&& inv->items[slot].count < it->max_stack) {
has_canidate_equal = true;
candidate_equal = k;
candidate_equal = slot;
break;
}
if(!has_canidate_empty && inv->items[k].id == 0) {
if(!has_canidate_empty && inv->items[slot].id == 0) {
has_canidate_empty = true;
candidate_empty = k;
candidate_empty = slot;
}
}
@ -132,6 +134,20 @@ bool inventory_collect(struct inventory* inv, struct item_data* item,
return true;
}
bool inventory_collect_inventory(struct inventory* inv, struct item_data* item,
bool* mask) {
uint8_t priorities[INVENTORY_SIZE_HOTBAR + INVENTORY_SIZE_MAIN];
for(size_t k = 0; k < INVENTORY_SIZE_HOTBAR; k++)
priorities[k] = k + INVENTORY_SLOT_HOTBAR;
for(size_t k = 0; k < INVENTORY_SIZE_MAIN; k++)
priorities[k + INVENTORY_SIZE_HOTBAR] = k + INVENTORY_SLOT_MAIN;
return inventory_collect(inv, item, priorities,
sizeof(priorities) / sizeof(*priorities), mask);
}
size_t inventory_get_hotbar(struct inventory* inv) {
assert(inv);
return inv->hotbar_slot;

View file

@ -59,7 +59,9 @@ void inventory_destroy(struct inventory* inv);
void inventory_clear(struct inventory* inv);
void inventory_consume(struct inventory* inv, size_t slot);
bool inventory_collect(struct inventory* inv, struct item_data* item,
size_t slot_start, size_t slot_length, bool* mask);
uint8_t* slot_priority, size_t slot_length, bool* mask);
bool inventory_collect_inventory(struct inventory* inv, struct item_data* item,
bool* mask);
size_t inventory_get_hotbar(struct inventory* inv);
void inventory_set_hotbar(struct inventory* inv, size_t slot);
bool inventory_get_hotbar_item(struct inventory* inv, struct item_data* item);

View file

@ -82,6 +82,9 @@ int main(void) {
chunk_mesher_init();
particle_init();
dict_entity_init(gstate.entities);
gstate.local_player = NULL;
struct server_local server;
server_local_create(&server);
@ -109,8 +112,13 @@ int main(void) {
last_tick = time_add_ms(last_tick, 50);
tick_delta -= 1.0F;
particle_update();
entities_client_tick(gstate.entities);
}
if(gstate.local_player)
camera_attach(&gstate.camera, gstate.local_player, tick_delta,
gstate.stats.dt);
bool render_world
= gstate.current_screen->render_world && gstate.world_loaded;
@ -171,11 +179,6 @@ int main(void) {
gstate.stats.chunks_rendered
= world_render(&gstate.world, &gstate.camera, false);
particle_render(
gstate.camera.view,
(vec3) {gstate.camera.x, gstate.camera.y, gstate.camera.z},
tick_delta);
} else {
gstate.stats.chunks_rendered = 0;
}
@ -187,6 +190,14 @@ int main(void) {
}
if(render_world) {
gfx_fog(false);
particle_render(
gstate.camera.view,
(vec3) {gstate.camera.x, gstate.camera.y, gstate.camera.z},
tick_delta);
entities_client_render(gstate.entities, &gstate.camera, tick_delta);
gfx_fog(true);
world_render(&gstate.world, &gstate.camera, true);
if(gstate.world.dimension == WORLD_DIM_OVERWORLD)

View file

@ -94,12 +94,15 @@ void clin_process(struct client_rpc* call) {
call->payload.unload_chunk.z);
break;
case CRPC_PLAYER_POS:
gstate.camera.x = call->payload.player_pos.position[0];
gstate.camera.y = call->payload.player_pos.position[1];
gstate.camera.z = call->payload.player_pos.position[2];
gstate.camera.rx = glm_rad(-call->payload.player_pos.rotation[0]);
gstate.camera.ry = glm_rad(glm_clamp(
call->payload.player_pos.rotation[1] + 90.0F, 0.0F, 180.0F));
if(gstate.local_player)
gstate.local_player->teleport(
gstate.local_player,
(vec3) {call->payload.player_pos.position[0],
call->payload.player_pos.position[1],
call->payload.player_pos.position[2]});
gstate.world_loaded = true;
break;
case CRPC_WORLD_RESET:
@ -113,6 +116,8 @@ void clin_process(struct client_rpc* call) {
}
}
dict_entity_reset(gstate.entities);
gstate.windows[WINDOWC_INVENTORY]
= malloc(sizeof(struct window_container));
assert(gstate.windows[WINDOWC_INVENTORY]);
@ -122,6 +127,11 @@ void clin_process(struct client_rpc* call) {
gstate.world_loaded = false;
gstate.world.dimension = call->payload.world_reset.dimension;
gstate.local_player = dict_entity_safe_get(
gstate.entities, call->payload.world_reset.local_entity);
entity_local_player(call->payload.world_reset.local_entity,
gstate.local_player, &gstate.world);
if(gstate.current_screen == &screen_ingame)
screen_set(&screen_load_world);
break;
@ -178,6 +188,36 @@ void clin_process(struct client_rpc* call) {
call->payload.set_block.block, true);
break;
case CRPC_SPAWN_ITEM: {
struct entity* e = dict_entity_safe_get(
gstate.entities, call->payload.spawn_item.entity_id);
entity_item(call->payload.spawn_item.entity_id, e, false,
&gstate.world, call->payload.spawn_item.item);
e->teleport(e, call->payload.spawn_item.pos);
} break;
case CRPC_PICKUP_ITEM: {
if(gstate.local_player
&& call->payload.pickup_item.collector_id
== gstate.local_player->id) {
struct entity* e = dict_entity_get(
gstate.entities, call->payload.pickup_item.entity_id);
if(e)
glm_vec3_copy((vec3) {gstate.camera.x,
gstate.camera.y - 0.2F,
gstate.camera.z},
e->network_pos);
}
} break;
case CRPC_ENTITY_DESTROY:
dict_entity_erase(gstate.entities,
call->payload.entity_destroy.entity_id);
break;
case CRPC_ENTITY_MOVE: {
struct entity* e = dict_entity_get(
gstate.entities, call->payload.entity_move.entity_id);
if(e)
glm_vec3_copy(call->payload.entity_move.pos, e->network_pos);
} break;
}
}

View file

@ -34,6 +34,10 @@ enum client_rpc_type {
CRPC_WORLD_RESET,
CRPC_SET_BLOCK,
CRPC_WINDOW_TRANSACTION,
CRPC_SPAWN_ITEM,
CRPC_PICKUP_ITEM,
CRPC_ENTITY_DESTROY,
CRPC_ENTITY_MOVE,
};
struct client_rpc {
@ -62,6 +66,7 @@ struct client_rpc {
uint64_t time_set;
struct {
enum world_dim dimension;
uint32_t local_entity;
} world_reset;
struct {
w_coord_t x, y, z;
@ -72,6 +77,22 @@ struct client_rpc {
uint16_t action_id;
bool accepted;
} window_transaction;
struct {
uint32_t entity_id;
struct item_data item;
vec3 pos;
} spawn_item;
struct {
uint32_t entity_id;
uint32_t collector_id;
} pickup_item;
struct {
uint32_t entity_id;
} entity_destroy;
struct {
uint32_t entity_id;
vec3 pos;
} entity_move;
} payload;
};

View file

@ -32,6 +32,31 @@
#define CHUNK_DIST2(x1, x2, z1, z2) \
(((x1) - (x2)) * ((x1) - (x2)) + ((z1) - (z2)) * ((z1) - (z2)))
static struct entity* spawn_item(vec3 pos, struct item_data* it, bool throw,
struct server_local* s) {
uint32_t entity_id = entity_gen_id(s->entities);
struct entity* e = dict_entity_safe_get(s->entities, entity_id);
entity_item(entity_id, e, true, &s->world, *it);
e->teleport(e, pos);
if(throw) {
float rx = glm_rad(-s->player.rx);
float ry = glm_rad(s->player.ry + 90.0F);
e->vel[0] = sinf(rx) * sinf(ry) * 0.25F;
e->vel[1] = cosf(ry) * 0.25F;
e->vel[2] = cosf(rx) * sinf(ry) * 0.25F;
}
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_SPAWN_ITEM,
.payload.spawn_item.entity_id = e->id,
.payload.spawn_item.item = e->data.item.item,
.payload.spawn_item.pos = {e->pos[0], e->pos[1], e->pos[2]},
});
return e;
}
static void server_local_process(struct server_rpc* call, void* user) {
assert(call && user);
@ -92,10 +117,10 @@ static void server_local_process(struct server_rpc* call, void* user) {
if(item.id != 0) {
inventory_clear_slot(&s->player.inventory, k);
inventory_collect(&s->player.inventory, &item,
INVENTORY_SLOT_MAIN,
INVENTORY_SIZE_MAIN, slots_changed);
slots_changed[k] = true;
spawn_item(
(vec3) {s->player.x, s->player.y, s->player.z},
&item, true, s);
}
}
@ -116,46 +141,25 @@ static void server_local_process(struct server_rpc* call, void* user) {
&& call->payload.block_dig.y < WORLD_HEIGHT
&& call->payload.block_dig.finished) {
struct block_data blk;
bool slots_changed[INVENTORY_SIZE];
memset(slots_changed, false, sizeof(slots_changed));
if(server_world_get_block(&s->world, call->payload.block_dig.x,
call->payload.block_dig.y,
call->payload.block_dig.z, &blk)) {
struct item_data drop = (struct item_data) {
.id = blk.type,
.durability = blk.metadata,
.count = 1,
};
server_world_set_block(&s->world, call->payload.block_dig.x,
call->payload.block_dig.y,
call->payload.block_dig.z,
(struct block_data) {
.type = BLOCK_AIR,
.metadata = 0,
});
// TODO: correct priority
inventory_collect(&s->player.inventory, &drop,
INVENTORY_SLOT_HOTBAR,
INVENTORY_SIZE_HOTBAR, slots_changed);
inventory_collect(&s->player.inventory, &drop,
INVENTORY_SLOT_MAIN, INVENTORY_SIZE_MAIN,
slots_changed);
for(size_t k = 0; k < INVENTORY_SIZE; k++) {
if(slots_changed[k])
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_INVENTORY_SLOT,
.payload.inventory_slot.window
= WINDOWC_INVENTORY,
.payload.inventory_slot.slot = k,
.payload.inventory_slot.item
= s->player.inventory.items[k],
});
}
spawn_item((vec3) {call->payload.block_dig.x + 0.5F,
call->payload.block_dig.y + 0.5F,
call->payload.block_dig.z + 0.5F},
&(struct item_data) {.id = blk.type,
.durability = blk.metadata,
.count = 1},
false, s);
}
server_world_set_block(&s->world, call->payload.block_dig.x,
call->payload.block_dig.y,
call->payload.block_dig.z,
(struct block_data) {
.type = BLOCK_AIR,
.metadata = 0,
});
}
break;
case SRPC_BLOCK_PLACE:
@ -222,7 +226,8 @@ static void server_local_process(struct server_rpc* call, void* user) {
// save chunks here, then destroy all
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_WORLD_RESET,
.payload.world_reset.dimension = WORLD_DIM_OVERWORLD,
.payload.world_reset.dimension = s->player.dimension,
.payload.world_reset.local_entity = 0,
});
level_archive_write_player(
@ -231,6 +236,7 @@ static void server_local_process(struct server_rpc* call, void* user) {
level_archive_write_inventory(&s->level, &s->player.inventory);
dict_entity_reset(s->entities);
server_world_destroy(&s->world);
level_archive_destroy(&s->level);
@ -262,9 +268,12 @@ static void server_local_process(struct server_rpc* call, void* user) {
if(level_archive_read(&s->level, LEVEL_TIME, &s->world_time, 0))
s->world_time_start = time_get();
dict_entity_reset(s->entities);
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_WORLD_RESET,
.payload.world_reset.dimension = dim,
.payload.world_reset.local_entity = 0,
});
}
break;
@ -279,6 +288,37 @@ static void server_local_update(struct server_local* s) {
if(!s->player.has_pos)
return;
dict_entity_it_t it;
dict_entity_it(it, s->entities);
while(!dict_entity_end_p(it)) {
uint32_t key = dict_entity_ref(it)->key;
struct entity* e = &dict_entity_ref(it)->value;
if(e->tick_server) {
bool remove = (e->delay_destroy == 0) || e->tick_server(e, s);
dict_entity_next(it);
if(remove) {
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_ENTITY_DESTROY,
.payload.entity_destroy.entity_id = key,
});
dict_entity_erase(s->entities, key);
} else if(e->delay_destroy < 0) {
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_ENTITY_MOVE,
.payload.entity_move.entity_id = key,
.payload.entity_move.pos
= {e->pos[0], e->pos[1], e->pos[2]},
});
}
} else {
dict_entity_next(it);
}
}
w_coord_t px = WCOORD_CHUNK_OFFSET(floor(s->player.x));
w_coord_t pz = WCOORD_CHUNK_OFFSET(floor(s->player.z));
@ -392,6 +432,7 @@ void server_local_create(struct server_local* s) {
s->player.finished_loading = false;
string_init(s->level_name);
inventory_create(&s->player.inventory, INVENTORY_SIZE);
dict_entity_init(s->entities);
struct thread t;
thread_create(&t, server_local_thread, s, 8);

View file

@ -43,6 +43,7 @@ struct server_local {
struct inventory inventory;
} player;
struct server_world world;
dict_entity_t entities;
uint64_t world_time;
ptime_t world_time_start;
string_t level_name;

View file

@ -42,8 +42,8 @@ void tex_gfx_load(struct tex_gfx* tex, void* img, size_t width, size_t height,
linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glBindTexture(GL_TEXTURE_2D, 0);
}

View file

@ -39,6 +39,7 @@ struct tex_gfx texture_pointer;
struct tex_gfx texture_clouds;
struct tex_gfx texture_sun;
struct tex_gfx texture_moon;
struct tex_gfx texture_shadow;
struct tex_gfx texture_mob_char;
@ -94,6 +95,7 @@ void tex_init() {
false);
tex_gfx_load_file(&texture_sun, "terrain/sun.png", TEX_FMT_RGB16, false);
tex_gfx_load_file(&texture_moon, "terrain/moon.png", TEX_FMT_RGB16, false);
tex_gfx_load_file(&texture_shadow, "misc/shadow.png", TEX_FMT_IA4, false);
tex_gfx_load_file(&texture_armor_chain1, "armor/chain_1.png",
TEX_FMT_RGBA16, false);

View file

@ -64,6 +64,7 @@ extern struct tex_gfx texture_pointer;
extern struct tex_gfx texture_clouds;
extern struct tex_gfx texture_sun;
extern struct tex_gfx texture_moon;
extern struct tex_gfx texture_shadow;
extern struct tex_gfx texture_mob_char;

View file

@ -548,9 +548,10 @@ size_t world_render(struct world* w, struct camera* c, bool pass) {
bool world_aabb_intersection(struct world* w, struct AABB* a) {
assert(w && a);
w_coord_t min_x = floorf(a->x1) - 1;
w_coord_t min_x = floorf(a->x1);
// need to look one further, otherwise fence block breaks
w_coord_t min_y = floorf(a->y1) - 1;
w_coord_t min_z = floorf(a->z1) - 1;
w_coord_t min_z = floorf(a->z1);
w_coord_t max_x = ceilf(a->x2) + 1;
w_coord_t max_y = ceilf(a->y2) + 1;

View file

@ -48,7 +48,8 @@ struct world_section {
struct chunk* column[COLUMN_HEIGHT];
};
#define SECTION_TO_ID(x, z) (((int64_t)(z) << 32) | (((int64_t)(x)&0xFFFFFFFF)))
#define SECTION_TO_ID(x, z) \
(((int64_t)(z) << 32) | (((int64_t)(x) & 0xFFFFFFFF)))
DICT_DEF2(dict_wsection, int64_t, M_BASIC_OPLIST, struct world_section,
M_POD_OPLIST)