mirror of
https://github.com/xtreme8000/CavEX.git
synced 2025-01-22 09:11:55 -05:00
Add player physics and item entities
This commit is contained in:
parent
7336dacd05
commit
40b0ebed19
28 changed files with 1104 additions and 101 deletions
|
@ -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
|
||||
|
|
2
Makefile
2
Makefile
|
@ -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 :=
|
||||
|
|
|
@ -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
301
source/entity/entity.c
Normal 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
99
source/entity/entity.h
Normal 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
205
source/entity/entity_item.c
Normal 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);
|
||||
}
|
||||
}
|
201
source/entity/entity_local_player.c
Normal file
201
source/entity/entity_local_player.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue