Ensure physics aren't tied to framerate

isolation_bkp/dynres
Dominik Madarász 2021-10-27 00:22:54 +02:00
parent 16da0ad158
commit 6f57fb178a
10 changed files with 1261 additions and 1258 deletions

View File

@ -1,278 +1,278 @@
#include "game.h" #include "game.h"
#include "zpl.h" #include "zpl.h"
#include "platform.h" #include "platform.h"
#include "world/world.h" #include "world/world.h"
#include "packet.h" #include "packet.h"
#include "signal_handling.h" #include "signal_handling.h"
#include "network.h" #include "network.h"
#include "entity.h" #include "entity.h"
#include "world_view.h" #include "world_view.h"
#include "entity_view.h" #include "entity_view.h"
#include "camera.h" #include "camera.h"
#include "profiler.h" #include "profiler.h"
#include "flecs/flecs.h" #include "flecs/flecs.h"
//#include "flecs/flecs_dash.h" //#include "flecs/flecs_dash.h"
//#include "flecs/flecs_systems_civetweb.h" //#include "flecs/flecs_systems_civetweb.h"
#include "flecs/flecs_os_api_stdcpp.h" #include "flecs/flecs_os_api_stdcpp.h"
#include "modules/components.h" #include "modules/components.h"
#include "modules/systems.h" #include "modules/systems.h"
#include "packets/pkt_00_init.h" #include "packets/pkt_00_init.h"
#include "packets/pkt_01_welcome.h" #include "packets/pkt_01_welcome.h"
#include "packets/pkt_send_keystate.h" #include "packets/pkt_send_keystate.h"
static uint8_t game_mode; static uint8_t game_mode;
static world_view *world_viewers; static world_view *world_viewers;
static world_view *active_viewer; static world_view *active_viewer;
static WORLD_PKT_READER(pkt_reader) { static WORLD_PKT_READER(pkt_reader) {
pkt_header header = {0}; pkt_header header = {0};
uint32_t ok = pkt_header_decode(&header, data, datalen); uint32_t ok = pkt_header_decode(&header, data, datalen);
header.udata = udata; header.udata = udata;
if (ok && header.ok) { if (ok && header.ok) {
return pkt_handlers[header.id].handler(&header) >= 0; return pkt_handlers[header.id].handler(&header) >= 0;
} else { } else {
zpl_printf("[warn] unknown packet id %d (header %d data %d)\n", header.id, ok, header.ok); zpl_printf("[warn] unknown packet id %d (header %d data %d)\n", header.id, ok, header.ok);
} }
return -1; return -1;
} }
static WORLD_PKT_WRITER(sp_pkt_writer) { static WORLD_PKT_WRITER(sp_pkt_writer) {
(void)udata; (void)udata;
return world_read(pkt->data, pkt->datalen, (void*)game_world_view_get_active()->owner_id); return world_read(pkt->data, pkt->datalen, (void*)game_world_view_get_active()->owner_id);
} }
static WORLD_PKT_WRITER(mp_pkt_writer) { static WORLD_PKT_WRITER(mp_pkt_writer) {
if (pkt->is_reliable) { if (pkt->is_reliable) {
return network_msg_send(udata, pkt->data, pkt->datalen, pkt->channel_id); return network_msg_send(udata, pkt->data, pkt->datalen, pkt->channel_id);
} }
else { else {
return network_msg_send_unreliable(udata, pkt->data, pkt->datalen, pkt->channel_id); return network_msg_send_unreliable(udata, pkt->data, pkt->datalen, pkt->channel_id);
} }
} }
static WORLD_PKT_WRITER(mp_cli_pkt_writer) { static WORLD_PKT_WRITER(mp_cli_pkt_writer) {
(void)udata; (void)udata;
if (pkt->is_reliable) { if (pkt->is_reliable) {
return network_msg_send(0, pkt->data, pkt->datalen, pkt->channel_id); return network_msg_send(0, pkt->data, pkt->datalen, pkt->channel_id);
} }
else { else {
return network_msg_send_unreliable(0, pkt->data, pkt->datalen, pkt->channel_id); return network_msg_send_unreliable(0, pkt->data, pkt->datalen, pkt->channel_id);
} }
} }
void world_viewers_init(uint32_t num_viewers) { void world_viewers_init(uint32_t num_viewers) {
zpl_buffer_init(world_viewers, zpl_heap(), num_viewers); zpl_buffer_init(world_viewers, zpl_heap(), num_viewers);
for (uint32_t i = 0; i < num_viewers; i++) { for (uint32_t i = 0; i < num_viewers; i++) {
zpl_buffer_append(world_viewers, world_view_create(i)); zpl_buffer_append(world_viewers, world_view_create(i));
} }
} }
void world_viewers_destroy() { void world_viewers_destroy() {
for (uint32_t i = 0; i < zpl_buffer_count(world_viewers); i++) { for (uint32_t i = 0; i < zpl_buffer_count(world_viewers); i++) {
world_view_destroy(&world_viewers[i]); world_view_destroy(&world_viewers[i]);
} }
zpl_buffer_free(world_viewers); zpl_buffer_free(world_viewers);
} }
world_view *game_world_view_get(uint16_t idx) { world_view *game_world_view_get(uint16_t idx) {
return &world_viewers[idx]; return &world_viewers[idx];
} }
world_view *game_world_view_get_active(void) { world_view *game_world_view_get_active(void) {
return active_viewer; return active_viewer;
} }
void game_world_view_cycle_active(int8_t dir) { void game_world_view_cycle_active(int8_t dir) {
uint16_t idx = (uint16_t)(active_viewer - world_viewers); uint16_t idx = (uint16_t)(active_viewer - world_viewers);
game_world_view_set_active_by_idx(zpl_max(0, (idx+dir)%zpl_buffer_count(world_viewers))); game_world_view_set_active_by_idx(zpl_max(0, (idx+dir)%zpl_buffer_count(world_viewers)));
} }
void game_world_view_set_active_by_idx(uint16_t idx) { void game_world_view_set_active_by_idx(uint16_t idx) {
ZPL_ASSERT(idx >= 0 && idx < zpl_buffer_count(world_viewers)); ZPL_ASSERT(idx >= 0 && idx < zpl_buffer_count(world_viewers));
game_world_view_set_active(&world_viewers[idx]); game_world_view_set_active(&world_viewers[idx]);
} }
void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)) { void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)) {
entity_view_map(&active_viewer->entities, map_proc); entity_view_map(&active_viewer->entities, map_proc);
} }
entity_view *game_world_view_active_get_entity(uint64_t ent_id) { entity_view *game_world_view_active_get_entity(uint64_t ent_id) {
return entity_view_get(&active_viewer->entities, ent_id); return entity_view_get(&active_viewer->entities, ent_id);
} }
void game_world_view_set_active(world_view *view) { void game_world_view_set_active(world_view *view) {
active_viewer = view; active_viewer = view;
camera_set_follow(view->owner_id); camera_set_follow(view->owner_id);
} }
void flecs_dash_init() { void flecs_dash_init() {
#if 0 #if 0
ECS_IMPORT(world_ecs(), FlecsDash); ECS_IMPORT(world_ecs(), FlecsDash);
ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb); ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb);
ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001}); ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001});
#endif #endif
} }
float game_time() { float game_time() {
return zpl_time_rel(); return zpl_time_rel();
} }
void game_init(game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) { void game_init(game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) {
game_mode = play_mode; game_mode = play_mode;
if (game_mode != GAMEKIND_HEADLESS) { if (game_mode != GAMEKIND_HEADLESS) {
platform_init(); platform_init();
world_viewers_init(num_viewers); world_viewers_init(num_viewers);
active_viewer = &world_viewers[0]; active_viewer = &world_viewers[0];
camera_reset(); camera_reset();
} }
if (game_mode != GAMEKIND_SINGLE) { if (game_mode != GAMEKIND_SINGLE) {
network_init(); network_init();
} }
if (game_mode == GAMEKIND_CLIENT) { if (game_mode == GAMEKIND_CLIENT) {
world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer); world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer);
#ifndef _DEBUG #ifndef _DEBUG
network_client_connect("lab.zakto.pw", 27000); network_client_connect("lab.zakto.pw", 27000);
#else #else
network_client_connect("127.0.0.1", 27000); network_client_connect("127.0.0.1", 27000);
#endif #endif
} else { } else {
stdcpp_set_os_api(); stdcpp_set_os_api();
world_setup_pkt_handlers(pkt_reader, game_mode == GAMEKIND_SINGLE ? sp_pkt_writer : mp_pkt_writer); world_setup_pkt_handlers(pkt_reader, game_mode == GAMEKIND_SINGLE ? sp_pkt_writer : mp_pkt_writer);
world_init(seed, chunk_size, chunk_amount); world_init(seed, chunk_size, chunk_amount);
if (is_dash_enabled) flecs_dash_init(); if (is_dash_enabled) flecs_dash_init();
if (game_mode == GAMEKIND_HEADLESS) { if (game_mode == GAMEKIND_HEADLESS) {
network_server_start(0, 27000); network_server_start(0, 27000);
ecs_set_target_fps(world_ecs(), 60); ecs_set_target_fps(world_ecs(), 60);
} }
} }
if (game_mode == GAMEKIND_SINGLE) { if (game_mode == GAMEKIND_SINGLE) {
for (uint32_t i = 0; i < num_viewers; i++) { for (uint32_t i = 0; i < num_viewers; i++) {
pkt_00_init_send(i); pkt_00_init_send(i);
} }
} }
} }
int8_t game_is_networked() { int8_t game_is_networked() {
return game_mode != GAMEKIND_SINGLE; return game_mode != GAMEKIND_SINGLE;
} }
void game_shutdown() { void game_shutdown() {
if (game_mode == GAMEKIND_CLIENT) { if (game_mode == GAMEKIND_CLIENT) {
network_client_disconnect(); network_client_disconnect();
} else { } else {
world_destroy(); world_destroy();
if (game_mode == GAMEKIND_HEADLESS) { if (game_mode == GAMEKIND_HEADLESS) {
network_server_stop(); network_server_stop();
} }
} }
if (game_mode != GAMEKIND_SINGLE) { if (game_mode != GAMEKIND_SINGLE) {
network_destroy(); network_destroy();
} }
if (game_mode != GAMEKIND_HEADLESS) { if (game_mode != GAMEKIND_HEADLESS) {
world_viewers_destroy(); world_viewers_destroy();
// TODO(zaklaus): crashes on exit // TODO(zaklaus): crashes on exit
//platform_shutdown(); //platform_shutdown();
} }
} }
uint8_t game_is_running() { uint8_t game_is_running() {
return game_mode == GAMEKIND_HEADLESS || platform_is_running(); return game_mode == GAMEKIND_HEADLESS || platform_is_running();
} }
game_kind game_get_kind(void) { game_kind game_get_kind(void) {
return game_mode; return game_mode;
} }
void game_input() { void game_input() {
if (game_mode != GAMEKIND_HEADLESS) { if (game_mode != GAMEKIND_HEADLESS) {
platform_input(); platform_input();
} }
} }
void game_update() { void game_update() {
if (game_mode == GAMEKIND_CLIENT) { if (game_mode == GAMEKIND_CLIENT) {
network_client_tick(); network_client_tick();
} }
else { else {
world_update(); world_update();
if (game_mode == GAMEKIND_HEADLESS) { if (game_mode == GAMEKIND_HEADLESS) {
network_server_tick(); network_server_tick();
} }
} }
if (game_mode != GAMEKIND_HEADLESS) { if (game_mode != GAMEKIND_HEADLESS) {
game_world_cleanup_entities(); game_world_cleanup_entities();
} }
} }
void game_render() { void game_render() {
if (game_mode != GAMEKIND_HEADLESS) { if (game_mode != GAMEKIND_HEADLESS) {
platform_render(); platform_render();
} }
} }
void game_action_send_keystate(float x, void game_action_send_keystate(float x,
float y, float y,
float mx, float mx,
float my, float my,
uint8_t use, uint8_t use,
uint8_t sprint, uint8_t sprint,
uint8_t ctrl, uint8_t ctrl,
uint8_t drop, uint8_t drop,
uint8_t selected_item, uint8_t selected_item,
uint8_t swap, uint8_t swap,
uint8_t swap_from, uint8_t swap_from,
uint8_t swap_to) { uint8_t swap_to) {
pkt_send_keystate_send(active_viewer->view_id, x, y, mx, my, use, sprint, ctrl, drop, selected_item, swap, swap_from, swap_to); pkt_send_keystate_send(active_viewer->view_id, x, y, mx, my, use, sprint, ctrl, drop, selected_item, swap, swap_from, swap_to);
} }
#define GAME_ENT_REMOVAL_TIME 10000 #define GAME_ENT_REMOVAL_TIME 10000
#define GAME_ENT_REMOVAL_TRESHOLD 500 #define GAME_ENT_REMOVAL_TRESHOLD 500
void game_world_cleanup_entities(void) { void game_world_cleanup_entities(void) {
// TODO(zaklaus): not the best approach to do a cleanup, let memory stay for a while as it might be reused later on anyway. // TODO(zaklaus): not the best approach to do a cleanup, let memory stay for a while as it might be reused later on anyway.
#if 1 #if 1
profile(PROF_ENTITY_REMOVAL) { profile(PROF_ENTITY_REMOVAL) {
static uint64_t last_removal_time = 0; static uint64_t last_removal_time = 0;
if (last_removal_time > zpl_time_rel_ms()) return; if (last_removal_time > zpl_time_rel_ms()) return;
last_removal_time = zpl_time_rel_ms() + GAME_ENT_REMOVAL_TIME; last_removal_time = zpl_time_rel_ms() + GAME_ENT_REMOVAL_TIME;
for (int i = 0; i < zpl_buffer_count(world_viewers); i += 1){ for (int i = 0; i < zpl_buffer_count(world_viewers); i += 1){
entity_view_tbl *view = &world_viewers[i].entities; entity_view_tbl *view = &world_viewers[i].entities;
uint32_t deletions = 0; uint32_t deletions = 0;
for (int j = 0; j < zpl_array_count(view->entries); j += 1) { for (int j = 0; j < zpl_array_count(view->entries); j += 1) {
if (deletions > GAME_ENT_REMOVAL_TRESHOLD) return; if (deletions > GAME_ENT_REMOVAL_TRESHOLD) return;
entity_view *e = &view->entries[j].value; entity_view *e = &view->entries[j].value;
if (e->tran_effect == ETRAN_REMOVE) { if (e->tran_effect == ETRAN_REMOVE) {
entity_view_tbl_remove(view, e->ent_id); entity_view_tbl_remove(view, e->ent_id);
deletions++; deletions++;
} }
} }
} }
} }
#endif #endif
} }
void game_request_close() { void game_request_close() {
platform_request_close(); platform_request_close();
} }

View File

@ -1,158 +1,158 @@
#include "platform.h" #include "platform.h"
#include "raylib.h" #include "raylib.h"
#include "raymath.h" #include "raymath.h"
#include "network.h" #include "network.h"
#include "game.h" #include "game.h"
#include "entity_view.h" #include "entity_view.h"
#include "prediction.h" #include "prediction.h"
#include "camera.h" #include "camera.h"
#include "math.h" #include "math.h"
#include "world/blocks.h" #include "world/blocks.h"
#include "assets.h" #include "assets.h"
#include "profiler.h" #include "profiler.h"
#include "debug_ui.h" #include "debug_ui.h"
#include "utils/raylib_helpers.h" #include "utils/raylib_helpers.h"
static uint16_t screenWidth = 1600; static uint16_t screenWidth = 1600;
static uint16_t screenHeight = 900; static uint16_t screenHeight = 900;
static float target_zoom = 1.5f; static float target_zoom = 1.5f;
static bool request_shutdown; static bool request_shutdown;
#define GFX_KIND 2 #define GFX_KIND 2
#include "renderer_bridge.c" #include "renderer_bridge.c"
// NOTE(zaklaus): add-ins // NOTE(zaklaus): add-ins
#include "gui/inventory.c" #include "gui/inventory.c"
void platform_init() { void platform_init() {
InitWindow(screenWidth, screenHeight, "eco2d"); InitWindow(screenWidth, screenHeight, "eco2d");
SetWindowState(FLAG_WINDOW_UNDECORATED|FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT); SetWindowState(FLAG_WINDOW_UNDECORATED|FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT);
SetTargetFPS(60); //SetTargetFPS(144);
screenWidth = GetScreenWidth(); screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight(); screenHeight = GetScreenHeight();
renderer_init(); renderer_init();
} }
void display_conn_status() { void display_conn_status() {
if (game_is_networked()) { if (game_is_networked()) {
if (network_client_is_connected()) { if (network_client_is_connected()) {
DrawText("Connection: online", 5, 5, 12, GREEN); DrawText("Connection: online", 5, 5, 12, GREEN);
} else { } else {
DrawText("Connection: offline", 5, 5, 12, RED); DrawText("Connection: offline", 5, 5, 12, RED);
} }
} else { } else {
DrawText("Connection: single-player", 5, 5, 12, BLUE); DrawText("Connection: single-player", 5, 5, 12, BLUE);
} }
} }
void platform_shutdown() { void platform_shutdown() {
renderer_shutdown(); renderer_shutdown();
CloseWindow(); CloseWindow();
} }
uint8_t platform_is_running() { uint8_t platform_is_running() {
return !WindowShouldClose(); return !WindowShouldClose();
} }
void platform_input() { void platform_input() {
float mouse_z = (GetMouseWheelMove()*0.5f); float mouse_z = (GetMouseWheelMove()*0.5f);
if (mouse_z != 0.0f) { if (mouse_z != 0.0f) {
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 10.0f); target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 10.0f);
} }
// NOTE(zaklaus): keystate handling // NOTE(zaklaus): keystate handling
{ {
float x=0.0f, y=0.0f; float x=0.0f, y=0.0f;
uint8_t use, sprint, drop, ctrl; uint8_t use, sprint, drop, ctrl;
if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f; if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f;
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f; if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f;
if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f; if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f;
if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f; if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f;
use = IsKeyPressed(KEY_SPACE); use = IsKeyPressed(KEY_SPACE);
sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL); ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
drop = IsKeyPressed(KEY_G) || inv_drop_item; drop = IsKeyPressed(KEY_G) || inv_drop_item;
// NOTE(zaklaus): NEW! mouse movement // NOTE(zaklaus): NEW! mouse movement
Vector2 mouse_pos = GetMousePosition(); Vector2 mouse_pos = GetMousePosition();
mouse_pos.x /= screenWidth; mouse_pos.x /= screenWidth;
mouse_pos.y /= screenHeight; mouse_pos.y /= screenHeight;
mouse_pos.x -= 0.5f; mouse_pos.x -= 0.5f;
mouse_pos.y -= 0.5f; mouse_pos.y -= 0.5f;
mouse_pos = Vector2Normalize(mouse_pos); mouse_pos = Vector2Normalize(mouse_pos);
if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) { if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) {
x = mouse_pos.x; x = mouse_pos.x;
y = -mouse_pos.y; y = -mouse_pos.y;
} }
game_action_send_keystate(x, y, mouse_pos.x, mouse_pos.y, use, sprint, ctrl, drop, inv_selected_item, inv_swap, inv_swap_from, inv_swap_to); game_action_send_keystate(x, y, mouse_pos.x, mouse_pos.y, use, sprint, ctrl, drop, inv_selected_item, inv_swap, inv_swap_from, inv_swap_to);
} }
// NOTE(zaklaus): cycle through viewers // NOTE(zaklaus): cycle through viewers
{ {
if (IsKeyPressed(KEY_Q)) { if (IsKeyPressed(KEY_Q)) {
game_world_view_cycle_active(-1); game_world_view_cycle_active(-1);
} }
else if (IsKeyPressed(KEY_E)) { else if (IsKeyPressed(KEY_E)) {
game_world_view_cycle_active(1); game_world_view_cycle_active(1);
} }
} }
// NOTE(zaklaus): switch render modes // NOTE(zaklaus): switch render modes
{ {
if (IsKeyPressed(KEY_O)) { if (IsKeyPressed(KEY_O)) {
renderer_switch(1-gfx_kind); renderer_switch(1-gfx_kind);
} }
} }
// NOTE(zaklaus): toggle debug drawing // NOTE(zaklaus): toggle debug drawing
#ifndef ECO2D_PROD #ifndef ECO2D_PROD
{ {
if (IsKeyPressed(KEY_T)) { if (IsKeyPressed(KEY_T)) {
debug_draw_enable(!debug_draw_state()); debug_draw_enable(!debug_draw_state());
} }
} }
#endif #endif
} }
void platform_render() { void platform_render() {
profile(PROF_ENTITY_LERP) { profile(PROF_ENTITY_LERP) {
game_world_view_active_entity_map(lerp_entity_positions); game_world_view_active_entity_map(lerp_entity_positions);
game_world_view_active_entity_map(do_entity_fadeinout); game_world_view_active_entity_map(do_entity_fadeinout);
} }
BeginDrawing(); BeginDrawing();
{ {
profile (PROF_RENDER) { profile (PROF_RENDER) {
renderer_draw(); renderer_draw();
} }
renderer_debug_draw(); renderer_debug_draw();
{ {
// NOTE(zaklaus): add-ins // NOTE(zaklaus): add-ins
inventory_draw(); inventory_draw();
} }
display_conn_status(); display_conn_status();
debug_draw(); debug_draw();
} }
EndDrawing(); EndDrawing();
if (request_shutdown) { if (request_shutdown) {
CloseWindow(); CloseWindow();
} }
} }
float platform_frametime() { float platform_frametime() {
return GetFrameTime(); return GetFrameTime();
} }
float platform_zoom_get(void) { float platform_zoom_get(void) {
return target_zoom; return target_zoom;
} }
void platform_request_close(void) { void platform_request_close(void) {
request_shutdown = true; request_shutdown = true;
} }

View File

@ -1,22 +1,23 @@
#include "vehicle.h" #include "vehicle.h"
#include "entity.h" #include "entity.h"
#include "entity_view.h" #include "entity_view.h"
#include "world/world.h" #include "world/world.h"
#include "modules/components.h" #include "modules/components.h"
uint64_t vehicle_spawn(void) { uint64_t vehicle_spawn(void) {
ecs_entity_t e = entity_spawn(EKIND_VEHICLE); ecs_entity_t e = entity_spawn(EKIND_VEHICLE);
Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL); Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL);
zpl_zero_item(veh); *veh = (Vehicle){
veh->wheel_base = 50.0f; .wheel_base = 50.0f,
veh->speed = 50.0f; .speed = 50.0f,
veh->reverse_speed = -20.0f; .reverse_speed = -20.0f,
veh->force = 0.0f; .force = 0.0f,
return (uint64_t)e; };
} return (uint64_t)e;
}
void vehicle_despawn(uint64_t ent_id) {
entity_despawn(ent_id); void vehicle_despawn(uint64_t ent_id) {
} entity_despawn(ent_id);
}

View File

@ -1,488 +1,488 @@
#include "zpl.h" #include "zpl.h"
#include "librg.h" #include "librg.h"
#include "modules/components.h" #include "modules/components.h"
#include "modules/systems.h" #include "modules/systems.h"
#include "world/world.h" #include "world/world.h"
#include "entity_view.h" #include "entity_view.h"
#include "debug_replay.h" #include "debug_replay.h"
#include "items.h" #include "items.h"
#include "world/worldgen/worldgen.h" #include "world/worldgen/worldgen.h"
#include "platform.h" #include "platform.h"
#include "profiler.h" #include "profiler.h"
#include "game.h" #include "game.h"
#include "packets/pkt_send_librg_update.h" #include "packets/pkt_send_librg_update.h"
ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view); ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view);
static world_data world = {0}; static world_data world = {0};
static world_snapshot streamer_snapshot; static world_snapshot streamer_snapshot;
entity_view world_build_entity_view(int64_t e) { entity_view world_build_entity_view(int64_t e) {
entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e); entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e);
if (cached_ev) return *cached_ev; if (cached_ev) return *cached_ev;
entity_view view = {0}; entity_view view = {0};
const Classify *classify = ecs_get(world_ecs(), e, Classify); const Classify *classify = ecs_get(world_ecs(), e, Classify);
ZPL_ASSERT(classify); ZPL_ASSERT(classify);
view.kind = classify->id; view.kind = classify->id;
const Position *pos = ecs_get(world_ecs(), e, Position); const Position *pos = ecs_get(world_ecs(), e, Position);
if (pos) { if (pos) {
view.x = pos->x; view.x = pos->x;
view.y = pos->y; view.y = pos->y;
} }
const Velocity *vel = ecs_get(world_ecs(), e, Velocity); const Velocity *vel = ecs_get(world_ecs(), e, Velocity);
if (vel) { if (vel) {
view.flag |= EFLAG_INTERP; view.flag |= EFLAG_INTERP;
view.vx = vel->x; view.vx = vel->x;
view.vy = vel->y; view.vy = vel->y;
} }
const Health *health = ecs_get(world_ecs(), e, Health); const Health *health = ecs_get(world_ecs(), e, Health);
if (health) { if (health) {
view.hp = health->hp; view.hp = health->hp;
view.max_hp = health->max_hp; view.max_hp = health->max_hp;
} }
if (ecs_get(world_ecs(), e, Vehicle)) { if (ecs_get(world_ecs(), e, Vehicle)) {
Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle); Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle);
view.heading = veh->heading; view.heading = veh->heading;
} }
if (ecs_get(world_ecs(), e, ItemDrop)) { if (ecs_get(world_ecs(), e, ItemDrop)) {
ItemDrop const* dr = ecs_get(world_ecs(), e, ItemDrop); ItemDrop const* dr = ecs_get(world_ecs(), e, ItemDrop);
view.asset = item_get_asset(dr->kind); view.asset = item_get_asset(dr->kind);
view.quantity = dr->quantity; view.quantity = dr->quantity;
} }
view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false; view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false;
Inventory *inv = 0; Inventory *inv = 0;
if ((inv = ecs_get_mut_if(world_ecs(), e, Inventory))) { if ((inv = ecs_get_mut_if(world_ecs(), e, Inventory))) {
view.has_items = true; view.has_items = true;
for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) { for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) {
view.items[i] = inv->items[i]; view.items[i] = inv->items[i];
} }
const Input *in = ecs_get(world_ecs(), e, Input); const Input *in = ecs_get(world_ecs(), e, Input);
if (in) if (in)
view.selected_item = in->selected_item; view.selected_item = in->selected_item;
} }
Chunk *chpos = 0; Chunk *chpos = 0;
if ((chpos = ecs_get_mut_if(world_ecs(), e, Chunk))) { if ((chpos = ecs_get_mut_if(world_ecs(), e, Chunk))) {
view.x = chpos->x; view.x = chpos->x;
view.y = chpos->y; view.y = chpos->y;
view.blocks_used = 1; view.blocks_used = 1;
view.is_dirty = chpos->is_dirty; view.is_dirty = chpos->is_dirty;
chpos->is_dirty = false; chpos->is_dirty = false;
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.blocks[i] = world.block_mapping[chpos->id][i]; view.blocks[i] = world.block_mapping[chpos->id][i];
} }
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.outer_blocks[i] = world.outer_block_mapping[chpos->id][i]; view.outer_blocks[i] = world.outer_block_mapping[chpos->id][i];
} }
} }
world_snapshot_set(&streamer_snapshot, e, view); world_snapshot_set(&streamer_snapshot, e, view);
return view; return view;
} }
int32_t tracker_write_create(librg_world *w, librg_event *e) { int32_t tracker_write_create(librg_world *w, librg_event *e) {
int64_t entity_id = librg_event_entity_get(w, e); int64_t entity_id = librg_event_entity_get(w, e);
#ifdef WORLD_LAYERING #ifdef WORLD_LAYERING
if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) { if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) {
// NOTE(zaklaus): reject updates from smaller layers // NOTE(zaklaus): reject updates from smaller layers
return LIBRG_WRITE_REJECT; return LIBRG_WRITE_REJECT;
} }
#endif #endif
size_t actual_length = librg_event_size_get(w, e); size_t actual_length = librg_event_size_get(w, e);
char *buffer = librg_event_buffer_get(w, e); char *buffer = librg_event_buffer_get(w, e);
return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id)); return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id));
} }
int32_t tracker_write_remove(librg_world *w, librg_event *e) { int32_t tracker_write_remove(librg_world *w, librg_event *e) {
(void)e; (void)e;
(void)w; (void)w;
#ifdef WORLD_LAYERING #ifdef WORLD_LAYERING
if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) { if (world.active_layer_id != WORLD_TRACKER_LAYERS-1) {
// NOTE(zaklaus): reject updates from smaller layers // NOTE(zaklaus): reject updates from smaller layers
return LIBRG_WRITE_REJECT; return LIBRG_WRITE_REJECT;
} }
#endif #endif
return 0; return 0;
} }
int32_t tracker_write_update(librg_world *w, librg_event *e) { int32_t tracker_write_update(librg_world *w, librg_event *e) {
int64_t entity_id = librg_event_entity_get(w, e); int64_t entity_id = librg_event_entity_get(w, e);
size_t actual_length = librg_event_size_get(w, e); size_t actual_length = librg_event_size_get(w, e);
char *buffer = librg_event_buffer_get(w, e); char *buffer = librg_event_buffer_get(w, e);
entity_view view = world_build_entity_view(entity_id); entity_view view = world_build_entity_view(entity_id);
// NOTE(zaklaus): exclude chunks from updates as they never move // NOTE(zaklaus): exclude chunks from updates as they never move
{ {
if (view.kind == EKIND_CHUNK && !view.is_dirty) { if (view.kind == EKIND_CHUNK && !view.is_dirty) {
return LIBRG_WRITE_REJECT; return LIBRG_WRITE_REJECT;
} }
} }
return (int32_t)entity_view_pack_struct(buffer, actual_length, view); return (int32_t)entity_view_pack_struct(buffer, actual_length, view);
} }
void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc) { void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc) {
world.reader_proc = reader_proc; world.reader_proc = reader_proc;
world.writer_proc = writer_proc; world.writer_proc = writer_proc;
} }
int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
if (world.data) { if (world.data) {
return 0; return 0;
} }
world.seed = seed; world.seed = seed;
world.chunk_size = chunk_size; world.chunk_size = chunk_size;
world.chunk_amount = chunk_amount; world.chunk_amount = chunk_amount;
world.dim = (world.chunk_size * world.chunk_amount); world.dim = (world.chunk_size * world.chunk_amount);
world.size = world.dim * world.dim; world.size = world.dim * world.dim;
if (world.tracker == NULL) { if (world.tracker == NULL) {
world.tracker = librg_world_create(); world.tracker = librg_world_create();
} }
if (world.tracker == NULL) { if (world.tracker == NULL) {
zpl_printf("[ERROR] An error occurred while trying to create a server world.\n"); zpl_printf("[ERROR] An error occurred while trying to create a server world.\n");
return WORLD_ERROR_TRACKER_FAILED; return WORLD_ERROR_TRACKER_FAILED;
} }
/* config our world grid */ /* config our world grid */
librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 0); librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 0);
librg_config_chunkamount_set(world.tracker, world.chunk_amount, world.chunk_amount, 0); librg_config_chunkamount_set(world.tracker, world.chunk_amount, world.chunk_amount, 0);
librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG); librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG);
librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create); librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create);
librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove); librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove);
librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update); librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update);
world.data = zpl_malloc(sizeof(uint8_t)*world.size); world.data = zpl_malloc(sizeof(uint8_t)*world.size);
if (!world.data) { if (!world.data) {
return WORLD_ERROR_OUTOFMEM; return WORLD_ERROR_OUTOFMEM;
} }
world.ecs = ecs_init(); world.ecs = ecs_init();
ECS_IMPORT(world.ecs, Components); ECS_IMPORT(world.ecs, Components);
ECS_IMPORT(world.ecs, Systems); ECS_IMPORT(world.ecs, Systems);
world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position"); world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position");
world.chunk_mapping = zpl_malloc(sizeof(ecs_entity_t)*zpl_square(chunk_amount)); world.chunk_mapping = zpl_malloc(sizeof(ecs_entity_t)*zpl_square(chunk_amount));
world.block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); world.block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount));
world.outer_block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); world.outer_block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount));
world_snapshot_init(&streamer_snapshot, zpl_heap()); world_snapshot_init(&streamer_snapshot, zpl_heap());
int32_t world_build_status = worldgen_test(&world); int32_t world_build_status = worldgen_test(&world);
ZPL_ASSERT(world_build_status >= 0); ZPL_ASSERT(world_build_status >= 0);
for (int i = 0; i < world.chunk_amount * world.chunk_amount; ++i) { for (int i = 0; i < world.chunk_amount * world.chunk_amount; ++i) {
ecs_entity_t e = ecs_new(world.ecs, 0); ecs_entity_t e = ecs_new(world.ecs, 0);
ecs_set(world.ecs, e, Classify, {.id = EKIND_CHUNK }); ecs_set(world.ecs, e, Classify, {.id = EKIND_CHUNK });
Chunk *chunk = ecs_get_mut(world.ecs, e, Chunk, NULL); Chunk *chunk = ecs_get_mut(world.ecs, e, Chunk, NULL);
librg_entity_track(world.tracker, e); librg_entity_track(world.tracker, e);
librg_entity_chunk_set(world.tracker, e, i); librg_entity_chunk_set(world.tracker, e, i);
librg_chunk_to_chunkpos(world.tracker, i, &chunk->x, &chunk->y, NULL); librg_chunk_to_chunkpos(world.tracker, i, &chunk->x, &chunk->y, NULL);
world.chunk_mapping[i] = e; world.chunk_mapping[i] = e;
world.block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); world.block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size));
world.outer_block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); world.outer_block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size));
chunk->id = i; chunk->id = i;
chunk->is_dirty = false; chunk->is_dirty = false;
for (int y = 0; y < chunk_size; y += 1) { for (int y = 0; y < chunk_size; y += 1) {
for (int x = 0; x < chunk_size; x += 1) { for (int x = 0; x < chunk_size; x += 1) {
int chk_x = chunk->x * chunk_size; int chk_x = chunk->x * chunk_size;
int chk_y = chunk->y * chunk_size; int chk_y = chunk->y * chunk_size;
uint8_t *c = &world.block_mapping[i][(y*chunk_size)+x]; uint8_t *c = &world.block_mapping[i][(y*chunk_size)+x];
*c = world.data[(chk_y+y)*world.dim + (chk_x+x)]; *c = world.data[(chk_y+y)*world.dim + (chk_x+x)];
c = &world.outer_block_mapping[i][(y*chunk_size)+x]; c = &world.outer_block_mapping[i][(y*chunk_size)+x];
*c = 0; *c = 0;
} }
} }
} }
zpl_mfree(world.data); zpl_mfree(world.data);
world.data = NULL; world.data = NULL;
zpl_printf("[INFO] Created a new server world\n"); zpl_printf("[INFO] Created a new server world\n");
return world_build_status; return world_build_status;
} }
int32_t world_destroy(void) { int32_t world_destroy(void) {
librg_world_destroy(world.tracker); librg_world_destroy(world.tracker);
ecs_fini(world.ecs); ecs_fini(world.ecs);
zpl_mfree(world.chunk_mapping); zpl_mfree(world.chunk_mapping);
for (int i = 0; i < zpl_square(world.chunk_amount); i+=1) { for (int i = 0; i < zpl_square(world.chunk_amount); i+=1) {
zpl_mfree(world.block_mapping[i]); zpl_mfree(world.block_mapping[i]);
zpl_mfree(world.outer_block_mapping[i]); zpl_mfree(world.outer_block_mapping[i]);
} }
zpl_mfree(world.block_mapping); zpl_mfree(world.block_mapping);
zpl_mfree(world.outer_block_mapping); zpl_mfree(world.outer_block_mapping);
world_snapshot_destroy(&streamer_snapshot); world_snapshot_destroy(&streamer_snapshot);
zpl_memset(&world, 0, sizeof(world)); zpl_memset(&world, 0, sizeof(world));
zpl_printf("[INFO] World was destroyed.\n"); zpl_printf("[INFO] World was destroyed.\n");
return WORLD_ERROR_NONE; return WORLD_ERROR_NONE;
} }
#define WORLD_LIBRG_BUFSIZ 2000000 #define WORLD_LIBRG_BUFSIZ 2000000
static void world_tracker_update(uint8_t ticker, uint32_t freq, uint8_t radius) { static void world_tracker_update(uint8_t ticker, uint32_t freq, uint8_t radius) {
if (world.tracker_update[ticker] > zpl_time_rel_ms()) return; if (world.tracker_update[ticker] > zpl_time_rel_ms()) return;
world.tracker_update[ticker] = zpl_time_rel_ms() + freq; world.tracker_update[ticker] = zpl_time_rel_ms() + freq;
profile(PROF_WORLD_WRITE) { profile(PROF_WORLD_WRITE) {
ecs_iter_t it = ecs_query_iter(world.ecs_update); ecs_iter_t it = ecs_query_iter(world.ecs_update);
static char buffer[WORLD_LIBRG_BUFSIZ] = {0}; static char buffer[WORLD_LIBRG_BUFSIZ] = {0};
world.active_layer_id = ticker; world.active_layer_id = ticker;
while (ecs_query_next(&it)) { while (ecs_query_next(&it)) {
ClientInfo *p = ecs_column(&it, ClientInfo, 1); ClientInfo *p = ecs_column(&it, ClientInfo, 1);
for (int i = 0; i < it.count; i++) { for (int i = 0; i < it.count; i++) {
size_t datalen = WORLD_LIBRG_BUFSIZ; size_t datalen = WORLD_LIBRG_BUFSIZ;
// TODO(zaklaus): SUPER TEMPORARY HOT !!! simulate variable radius queries // TODO(zaklaus): SUPER TEMPORARY HOT !!! simulate variable radius queries
{ {
librg_entity_radius_set(world_tracker(), it.entities[i], radius); librg_entity_radius_set(world_tracker(), it.entities[i], radius);
} }
// TODO(zaklaus): push radius once librg patch comes in // TODO(zaklaus): push radius once librg patch comes in
int32_t result = librg_world_write(world_tracker(), it.entities[i], buffer, &datalen, NULL); int32_t result = librg_world_write(world_tracker(), it.entities[i], buffer, &datalen, NULL);
if (result > 0) { if (result > 0) {
zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result); zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result);
} else if (result < 0) { } else if (result < 0) {
zpl_printf("[error] an error happened writing the world %d\n", result); zpl_printf("[error] an error happened writing the world %d\n", result);
} }
pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen); pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen);
} }
} }
// NOTE(zaklaus): clear out our streaming snapshot // NOTE(zaklaus): clear out our streaming snapshot
// TODO(zaklaus): move this to zpl // TODO(zaklaus): move this to zpl
{ {
zpl_array_clear(streamer_snapshot.hashes); zpl_array_clear(streamer_snapshot.hashes);
zpl_array_clear(streamer_snapshot.entries); zpl_array_clear(streamer_snapshot.entries);
} }
} }
} }
int32_t world_update() { int32_t world_update() {
profile (PROF_UPDATE_SYSTEMS) { profile (PROF_UPDATE_SYSTEMS) {
ecs_progress(world.ecs, 0.0f); ecs_progress(world.ecs, 0.0f);
} }
uint32_t fast_ms = WORLD_TRACKER_UPDATE_FAST_MS; uint32_t fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
uint32_t normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS; uint32_t normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS;
uint32_t slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS; uint32_t slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS;
if (game_get_kind() != GAMEKIND_SINGLE) { if (game_get_kind() != GAMEKIND_SINGLE) {
fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS; fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS;
normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS; normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS;
slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS; slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
} }
world_tracker_update(0, fast_ms, 2); world_tracker_update(0, fast_ms, 2);
world_tracker_update(1, normal_ms, 4); world_tracker_update(1, normal_ms, 4);
world_tracker_update(2, slow_ms, 6); world_tracker_update(2, slow_ms, 6);
debug_replay_update(); debug_replay_update();
return 0; return 0;
} }
int32_t world_read(void* data, uint32_t datalen, void *udata) { int32_t world_read(void* data, uint32_t datalen, void *udata) {
if (world.reader_proc) { if (world.reader_proc) {
return world.reader_proc(data, datalen, udata); return world.reader_proc(data, datalen, udata);
} }
return -1; return -1;
} }
int32_t world_write(pkt_header *pkt, void *udata) { int32_t world_write(pkt_header *pkt, void *udata) {
if (world.writer_proc) { if (world.writer_proc) {
return world.writer_proc(pkt, udata); return world.writer_proc(pkt, udata);
} }
return -1; return -1;
} }
uint32_t world_buf(uint8_t const **ptr, uint32_t *width) { uint32_t world_buf(uint8_t const **ptr, uint32_t *width) {
ZPL_ASSERT_NOT_NULL(world.data); ZPL_ASSERT_NOT_NULL(world.data);
ZPL_ASSERT_NOT_NULL(ptr); ZPL_ASSERT_NOT_NULL(ptr);
*ptr = world.data; *ptr = world.data;
if (width) *width = world.dim; if (width) *width = world.dim;
return world.size; return world.size;
} }
uint32_t world_seed(void) { uint32_t world_seed(void) {
return world.seed; return world.seed;
} }
ecs_world_t * world_ecs() { ecs_world_t * world_ecs() {
if (world.ecs_stage != NULL) { if (world.ecs_stage != NULL) {
return world.ecs_stage; return world.ecs_stage;
} }
return world.ecs; return world.ecs;
} }
void world_set_stage(ecs_world_t *ecs) { void world_set_stage(ecs_world_t *ecs) {
world.ecs_stage = ecs; world.ecs_stage = ecs;
} }
librg_world *world_tracker() { librg_world *world_tracker() {
return world.tracker; return world.tracker;
} }
uint16_t world_chunk_size(void) { uint16_t world_chunk_size(void) {
return world.chunk_size; return world.chunk_size;
} }
uint16_t world_chunk_amount(void) { uint16_t world_chunk_amount(void) {
return world.chunk_amount; return world.chunk_amount;
} }
uint16_t world_dim(void) { uint16_t world_dim(void) {
return WORLD_BLOCK_SIZE * world.chunk_size * world.chunk_amount; return WORLD_BLOCK_SIZE * world.chunk_size * world.chunk_amount;
} }
ecs_entity_t world_chunk_mapping(librg_chunk id) { ecs_entity_t world_chunk_mapping(librg_chunk id) {
ZPL_ASSERT(id >= 0 && id < zpl_square(world.chunk_amount)); ZPL_ASSERT(id >= 0 && id < zpl_square(world.chunk_amount));
return world.chunk_mapping[id]; return world.chunk_mapping[id];
} }
world_block_lookup world_block_from_realpos(float x, float y) { world_block_lookup world_block_from_realpos(float x, float y) {
x = zpl_clamp(x, 0, world_dim()-1); x = zpl_clamp(x, 0, world_dim()-1);
y = zpl_clamp(y, 0, world_dim()-1); y = zpl_clamp(y, 0, world_dim()-1);
librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0);
ecs_entity_t e = world.chunk_mapping[chunk_id]; ecs_entity_t e = world.chunk_mapping[chunk_id];
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE; int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
int16_t chunk_x, chunk_y; int16_t chunk_x, chunk_y;
librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL); librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL);
// NOTE(zaklaus): pos relative to chunk // NOTE(zaklaus): pos relative to chunk
float chx = x - chunk_x * size; float chx = x - chunk_x * size;
float chy = y - chunk_y * size; float chy = y - chunk_y * size;
uint32_t bx = (uint32_t)chx / WORLD_BLOCK_SIZE; uint32_t bx = (uint32_t)chx / WORLD_BLOCK_SIZE;
uint32_t by = (uint32_t)chy / WORLD_BLOCK_SIZE; uint32_t by = (uint32_t)chy / WORLD_BLOCK_SIZE;
uint32_t block_idx = (by*world.chunk_size)+bx; uint32_t block_idx = (by*world.chunk_size)+bx;
uint8_t block_id = world.outer_block_mapping[chunk_id][block_idx]; uint8_t block_id = world.outer_block_mapping[chunk_id][block_idx];
if (block_id == 0) { if (block_id == 0) {
block_id = world.block_mapping[chunk_id][block_idx]; block_id = world.block_mapping[chunk_id][block_idx];
} }
// NOTE(zaklaus): pos relative to block's center // NOTE(zaklaus): pos relative to block's center
float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
world_block_lookup lookup = { world_block_lookup lookup = {
.id = block_idx, .id = block_idx,
.block_id = block_id, .block_id = block_id,
.chunk_id = chunk_id, .chunk_id = chunk_id,
.chunk_e = e, .chunk_e = e,
.ox = box, .ox = box,
.oy = boy, .oy = boy,
}; };
return lookup; return lookup;
} }
world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) { world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
uint8_t block_id = world.outer_block_mapping[id][block_idx]; uint8_t block_id = world.outer_block_mapping[id][block_idx];
if (block_id == 0) { if (block_id == 0) {
block_id = world.block_mapping[id][block_idx]; block_id = world.block_mapping[id][block_idx];
} }
world_block_lookup lookup = { world_block_lookup lookup = {
.id = block_idx, .id = block_idx,
.block_id = block_id, .block_id = block_id,
.chunk_id = id, .chunk_id = id,
.chunk_e = world.chunk_mapping[id], .chunk_e = world.chunk_mapping[id],
}; };
return lookup; return lookup;
} }
int64_t world_chunk_from_realpos(float x, float y) { int64_t world_chunk_from_realpos(float x, float y) {
librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0);
return world.chunk_mapping[chunk_id]; return world.chunk_mapping[chunk_id];
} }
int64_t world_chunk_from_entity(ecs_entity_t id) { int64_t world_chunk_from_entity(ecs_entity_t id) {
return librg_entity_chunk_get(world.tracker, id); return librg_entity_chunk_get(world.tracker, id);
} }
void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id) { void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id) {
ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size));
world.block_mapping[id][block_idx] = block_id; world.block_mapping[id][block_idx] = block_id;
world_chunk_mark_dirty(world.chunk_mapping[id]); world_chunk_mark_dirty(world.chunk_mapping[id]);
} }
bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id) { bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id) {
ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size));
if (world.outer_block_mapping[id][block_idx] != 0 && block_id != 0) return false; if (world.outer_block_mapping[id][block_idx] != 0 && block_id != 0) return false;
world.outer_block_mapping[id][block_idx] = block_id; world.outer_block_mapping[id][block_idx] = block_id;
world_chunk_mark_dirty(world.chunk_mapping[id]); world_chunk_mark_dirty(world.chunk_mapping[id]);
return true; return true;
} }
uint8_t *world_chunk_get_blocks(int64_t id) { uint8_t *world_chunk_get_blocks(int64_t id) {
return world.block_mapping[id]; return world.block_mapping[id];
} }
void world_chunk_mark_dirty(ecs_entity_t e) { void world_chunk_mark_dirty(ecs_entity_t e) {
bool was_added=false; bool was_added=false;
Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added);
ZPL_ASSERT(!was_added); ZPL_ASSERT(!was_added);
if (chunk) chunk->is_dirty = true; if (chunk) chunk->is_dirty = true;
} }
uint8_t world_chunk_is_dirty(ecs_entity_t e) { uint8_t world_chunk_is_dirty(ecs_entity_t e) {
bool was_added=false; bool was_added=false;
Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added); Chunk *chunk = ecs_get_mut(world_ecs(), e, Chunk, &was_added);
ZPL_ASSERT(!was_added); ZPL_ASSERT(!was_added);
if (chunk) return chunk->is_dirty; if (chunk) return chunk->is_dirty;
return false; return false;
} }
int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len) { int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len) {
ZPL_ASSERT_NOT_NULL(ents_len); ZPL_ASSERT_NOT_NULL(ents_len);
static int64_t ents[UINT16_MAX]; static int64_t ents[UINT16_MAX];
*ents_len = UINT16_MAX; *ents_len = UINT16_MAX;
librg_world_fetch_chunk(world.tracker, chunk_id, ents, ents_len); librg_world_fetch_chunk(world.tracker, chunk_id, ents, ents_len);
return ents; return ents;
} }
int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len) { int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len) {
return world_chunk_fetch_entities(librg_chunk_from_realpos(world.tracker, x, y, 0), ents_len); return world_chunk_fetch_entities(librg_chunk_from_realpos(world.tracker, x, y, 0), ents_len);
} }
int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius) { int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius) {
ZPL_ASSERT_NOT_NULL(ents_len); ZPL_ASSERT_NOT_NULL(ents_len);
static int64_t ents[UINT16_MAX]; static int64_t ents[UINT16_MAX];
*ents_len = UINT16_MAX; *ents_len = UINT16_MAX;
librg_entity_radius_set(world.tracker, e, radius); librg_entity_radius_set(world.tracker, e, radius);
librg_world_query(world.tracker, e, ents, ents_len); librg_world_query(world.tracker, e, ents, ents_len);
return ents; return ents;
} }
uint8_t world_entity_valid(ecs_entity_t e) { uint8_t world_entity_valid(ecs_entity_t e) {
if (!e) return false; if (!e) return false;
return ecs_is_alive(world_ecs(), e); return ecs_is_alive(world_ecs(), e);
} }

View File

@ -1,94 +1,94 @@
#pragma once #pragma once
#include "system.h" #include "system.h"
#include "librg.h" #include "librg.h"
#include "packet.h" #include "packet.h"
#include "flecs/flecs.h" #include "flecs/flecs.h"
#include "flecs/flecs_meta.h" #include "flecs/flecs_meta.h"
#include "modules/components.h" #include "modules/components.h"
#define WORLD_ERROR_NONE +0x0000 #define WORLD_ERROR_NONE +0x0000
#define WORLD_ERROR_OUTOFMEM -0x0001 #define WORLD_ERROR_OUTOFMEM -0x0001
#define WORLD_ERROR_INVALID_BLOCKS -0x0002 #define WORLD_ERROR_INVALID_BLOCKS -0x0002
#define WORLD_ERROR_INVALID_DIMENSIONS -0x0003 #define WORLD_ERROR_INVALID_DIMENSIONS -0x0003
#define WORLD_ERROR_INVALID_BUFFER -0x0004 #define WORLD_ERROR_INVALID_BUFFER -0x0004
#define WORLD_ERROR_TRACKER_FAILED -0x0005 #define WORLD_ERROR_TRACKER_FAILED -0x0005
#define WORLD_LAYERING 0 #define WORLD_LAYERING 0
#define WORLD_TRACKER_LAYERS 3 #define WORLD_TRACKER_LAYERS 3
#define WORLD_TRACKER_UPDATE_FAST_MS 10 #define WORLD_TRACKER_UPDATE_FAST_MS 10
#define WORLD_TRACKER_UPDATE_NORMAL_MS 50 #define WORLD_TRACKER_UPDATE_NORMAL_MS 50
#define WORLD_TRACKER_UPDATE_SLOW_MS 100 #define WORLD_TRACKER_UPDATE_SLOW_MS 100
#define WORLD_TRACKER_UPDATE_MP_FAST_MS 50 #define WORLD_TRACKER_UPDATE_MP_FAST_MS 50
#define WORLD_TRACKER_UPDATE_MP_NORMAL_MS 150 #define WORLD_TRACKER_UPDATE_MP_NORMAL_MS 150
#define WORLD_TRACKER_UPDATE_MP_SLOW_MS 300 #define WORLD_TRACKER_UPDATE_MP_SLOW_MS 300
#define WORLD_BLOCK_SIZE 64 #define WORLD_BLOCK_SIZE 64
#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata) #define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata)
typedef WORLD_PKT_READER(world_pkt_reader_proc); typedef WORLD_PKT_READER(world_pkt_reader_proc);
#define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt, void *udata) #define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt, void *udata)
typedef WORLD_PKT_WRITER(world_pkt_writer_proc); typedef WORLD_PKT_WRITER(world_pkt_writer_proc);
typedef struct { typedef struct {
uint8_t *data; uint8_t *data;
uint32_t seed; uint32_t seed;
uint32_t size; uint32_t size;
uint16_t chunk_size; uint16_t chunk_size;
uint16_t chunk_amount; uint16_t chunk_amount;
uint8_t **block_mapping; uint8_t **block_mapping;
uint8_t **outer_block_mapping; uint8_t **outer_block_mapping;
uint16_t dim; uint16_t dim;
uint64_t tracker_update[3]; uint64_t tracker_update[3];
uint8_t active_layer_id; uint8_t active_layer_id;
ecs_world_t *ecs; ecs_world_t *ecs;
ecs_world_t *ecs_stage; ecs_world_t *ecs_stage;
ecs_query_t *ecs_update; ecs_query_t *ecs_update;
ecs_entity_t *chunk_mapping; ecs_entity_t *chunk_mapping;
librg_world *tracker; librg_world *tracker;
world_pkt_reader_proc *reader_proc; world_pkt_reader_proc *reader_proc;
world_pkt_writer_proc *writer_proc; world_pkt_writer_proc *writer_proc;
} world_data; } world_data;
void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc); void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc);
int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount); int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount);
int32_t world_destroy(void); int32_t world_destroy(void);
int32_t world_update(void); int32_t world_update(void);
int32_t world_read(void* data, uint32_t datalen, void *udata); int32_t world_read(void* data, uint32_t datalen, void *udata);
int32_t world_write(pkt_header *pkt, void *udata); int32_t world_write(pkt_header *pkt, void *udata);
uint32_t world_buf(uint8_t const **ptr, uint32_t *width); uint32_t world_buf(uint8_t const **ptr, uint32_t *width);
uint32_t world_seed(void); uint32_t world_seed(void);
ecs_world_t *world_ecs(void); ecs_world_t *world_ecs(void);
void world_set_stage(ecs_world_t *ecs); void world_set_stage(ecs_world_t *ecs);
librg_world *world_tracker(void); librg_world *world_tracker(void);
uint16_t world_chunk_size(void); uint16_t world_chunk_size(void);
uint16_t world_chunk_amount(void); uint16_t world_chunk_amount(void);
uint16_t world_dim(void); uint16_t world_dim(void);
ecs_entity_t world_chunk_mapping(librg_chunk id); ecs_entity_t world_chunk_mapping(librg_chunk id);
typedef struct { typedef struct {
uint32_t id; uint32_t id;
uint8_t block_id; uint8_t block_id;
ecs_entity_t chunk_e; ecs_entity_t chunk_e;
int64_t chunk_id; int64_t chunk_id;
float ox, oy; float ox, oy;
} world_block_lookup; } world_block_lookup;
world_block_lookup world_block_from_realpos(float x, float y); world_block_lookup world_block_from_realpos(float x, float y);
world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx); world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx);
int64_t world_chunk_from_realpos(float x, float y); int64_t world_chunk_from_realpos(float x, float y);
int64_t world_chunk_from_entity(ecs_entity_t id); int64_t world_chunk_from_entity(ecs_entity_t id);
void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id); void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id);
bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id); bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id);
uint8_t *world_chunk_get_blocks(int64_t id); uint8_t *world_chunk_get_blocks(int64_t id);
void world_chunk_mark_dirty(ecs_entity_t e); void world_chunk_mark_dirty(ecs_entity_t e);
uint8_t world_chunk_is_dirty(ecs_entity_t e); uint8_t world_chunk_is_dirty(ecs_entity_t e);
// NOTE(zaklaus): Uses locally persistent buffer !! // NOTE(zaklaus): Uses locally persistent buffer !!
int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len); int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len);
int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len); int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len);
int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius); int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius);
uint8_t world_entity_valid(ecs_entity_t e); uint8_t world_entity_valid(ecs_entity_t e);

View File

@ -8,7 +8,7 @@
#include "game.h" #include "game.h"
#define PHY_BLOCK_COLLISION 1 #define PHY_BLOCK_COLLISION 1
#define PHY_WALK_DRAG 0.12 #define PHY_WALK_DRAG 4.23f
#define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f) #define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f)
#include "source/system_onfoot.c" #include "source/system_onfoot.c"
@ -29,7 +29,7 @@ void IntegratePositions(ecs_iter_t *it) {
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
// NOTE(zaklaus): world bounds // NOTE(zaklaus): world bounds
{ {
double w = (double)world_dim(); float w = (float)world_dim();
p[i].x = zpl_clamp(p[i].x, 0, w-1); p[i].x = zpl_clamp(p[i].x, 0, w-1);
p[i].y = zpl_clamp(p[i].y, 0, w-1); p[i].y = zpl_clamp(p[i].y, 0, w-1);
} }
@ -127,8 +127,8 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y); world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f);
float friction = blocks_get_friction(lookup.block_id); float friction = blocks_get_friction(lookup.block_id);
v[i].x = zpl_lerp(v[i].x, 0.0f, PHY_WALK_DRAG*drag*friction); v[i].x = zpl_lerp(v[i].x, 0.0f, PHY_WALK_DRAG*drag*friction*it->delta_time);
v[i].y = zpl_lerp(v[i].y, 0.0f, PHY_WALK_DRAG*drag*friction); v[i].y = zpl_lerp(v[i].y, 0.0f, PHY_WALK_DRAG*drag*friction*it->delta_time);
} }
} }

View File

@ -1,10 +1,11 @@
#pragma once #pragma once
#include "flecs/flecs.h" #include "flecs/flecs.h"
typedef struct { typedef struct {
// NOTE(zaklaus): Public systems are exposed here // NOTE(zaklaus): Public systems are exposed here
} Systems; int32_t _unused;
} Systems;
#define SystemsImportHandles(handles) (void)(handles)
#define SystemsImportHandles(handles) (void)(handles)
void SystemsImport(ecs_world_t *ecs);
void SystemsImport(ecs_world_t *ecs);

View File

@ -1,12 +1,12 @@
#define DEMO_NPC_MOVE_SPEED 50 #define DEMO_NPC_MOVE_SPEED 500
#define DEMO_NPC_STEER_SPEED 30 #define DEMO_NPC_STEER_SPEED 300
void DemoNPCMoveAround(ecs_iter_t *it) { void DemoNPCMoveAround(ecs_iter_t *it) {
Velocity *v = ecs_column(it, Velocity, 1); Velocity *v = ecs_column(it, Velocity, 1);
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y); float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y);
v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED + zpl_cos(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED); v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED*it->delta_time + zpl_cos(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*it->delta_time);
v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED + zpl_sin(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED); v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED*it->delta_time + zpl_sin(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*it->delta_time);
} }
} }

View File

@ -1,4 +1,4 @@
#define PLR_MOVE_SPEED 30.0 #define PLR_MOVE_SPEED 800.0
#define PLR_MOVE_SPEED_MULT 1.5 #define PLR_MOVE_SPEED_MULT 1.5
void MovementImpulse(ecs_iter_t *it) { void MovementImpulse(ecs_iter_t *it) {
@ -10,7 +10,7 @@ void MovementImpulse(ecs_iter_t *it) {
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y); world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f);
double speed = PLR_MOVE_SPEED * (in[i].sprint ? PLR_MOVE_SPEED_MULT : 1.0); double speed = PLR_MOVE_SPEED * (in[i].sprint ? PLR_MOVE_SPEED_MULT : 1.0);
v[i].x += in[i].x*speed*drag; v[i].x += in[i].x*speed*drag*it->delta_time;
v[i].y -= in[i].y*speed*drag; v[i].y -= in[i].y*speed*drag*it->delta_time;
} }
} }

View File

@ -1,191 +1,192 @@
#include "debug_draw.h" #include "debug_draw.h"
#define VEH_ENTER_RADIUS 45.0f #define VEH_ENTER_RADIUS 45.0f
void LeaveVehicle(ecs_iter_t *it) { void LeaveVehicle(ecs_iter_t *it) {
Input *in = ecs_column(it, Input, 1); Input *in = ecs_column(it, Input, 1);
IsInVehicle *vehp = ecs_column(it, IsInVehicle, 2); IsInVehicle *vehp = ecs_column(it, IsInVehicle, 2);
Velocity *v = ecs_column(it, Velocity, 3); Velocity *v = ecs_column(it, Velocity, 3);
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
if (!in[i].use) continue; if (!in[i].use) continue;
Vehicle *veh = 0; Vehicle *veh = 0;
if ((veh = ecs_get_mut_if(it->world, vehp->veh, Vehicle))) { if ((veh = ecs_get_mut_if(it->world, vehp->veh, Vehicle))) {
for (int k = 0; k < 4; k++) { for (int k = 0; k < 4; k++) {
if (veh->seats[k] == it->entities[i]) { if (veh->seats[k] == it->entities[i]) {
veh->seats[k] = 0; veh->seats[k] = 0;
break; break;
} }
} }
in[i].use = false; in[i].use = false;
ecs_remove(it->world, it->entities[i], IsInVehicle); ecs_remove(it->world, it->entities[i], IsInVehicle);
// NOTE(zaklaus): push passenger out // NOTE(zaklaus): push passenger out
{ {
float px = zpl_cos(veh->heading)*400.0f; float px = zpl_cos(veh->heading)*400.0f;
float py = zpl_sin(veh->heading)*400.0f; float py = zpl_sin(veh->heading)*400.0f;
v->x += py; v->x += py;
v->y -= px; v->y -= px;
} }
} else { } else {
ZPL_PANIC("unreachable code"); ZPL_PANIC("unreachable code");
} }
} }
} }
void EnterVehicle(ecs_iter_t *it) { void EnterVehicle(ecs_iter_t *it) {
Input *in = ecs_column(it, Input, 1); Input *in = ecs_column(it, Input, 1);
Position *p = ecs_column(it, Position, 2); Position *p = ecs_column(it, Position, 2);
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
if (!in[i].use) continue; if (!in[i].use) continue;
size_t ents_count; size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2); int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
bool has_entered_veh = false; bool has_entered_veh = false;
for (size_t j = 0; j < ents_count; j++) { for (size_t j = 0; j < ents_count; j++) {
Vehicle *veh = 0; Vehicle *veh = 0;
if (has_entered_veh) break; if (has_entered_veh) break;
if ((veh = ecs_get_mut_if(it->world, ents[j], Vehicle))) { if ((veh = ecs_get_mut_if(it->world, ents[j], Vehicle))) {
Position const* p2 = ecs_get(it->world, ents[j], Position); Position const* p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - p[i].x; float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y; float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy); float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= VEH_ENTER_RADIUS) { if (range <= VEH_ENTER_RADIUS) {
for (int k = 0; k < 4; k++) { for (int k = 0; k < 4; k++) {
if (veh->seats[k] != 0) continue; if (veh->seats[k] != 0) continue;
// NOTE(zaklaus): We can enter the vehicle, yay! // NOTE(zaklaus): We can enter the vehicle, yay!
veh->seats[k] = it->entities[i]; veh->seats[k] = it->entities[i];
ecs_set(it->world, it->entities[i], IsInVehicle, { ecs_set(it->world, it->entities[i], IsInVehicle, {
.veh = ents[j] .veh = ents[j]
}); });
p[i] = *p2; p[i] = *p2;
in[i].use = false; in[i].use = false;
has_entered_veh = true; has_entered_veh = true;
break; break;
} }
} }
} }
} }
} }
} }
#define VEHICLE_FORCE 34.8f #define VEHICLE_FORCE 340.8f
#define VEHICLE_ACCEL 0.42f #define VEHICLE_ACCEL 0.12f
#define VEHICLE_DECEL 0.28f #define VEHICLE_DECEL 0.28f
#define VEHICLE_STEER 3.89f #define VEHICLE_STEER 3.89f
#define VEHICLE_BRAKE_FORCE 0.84f #define VEHICLE_POWER 34.89f
#define VEHICLE_BRAKE_FORCE 0.84f
void VehicleHandling(ecs_iter_t *it) {
Vehicle *veh = ecs_column(it, Vehicle, 1); void VehicleHandling(ecs_iter_t *it) {
Position *p = ecs_column(it, Position, 2); Vehicle *veh = ecs_column(it, Vehicle, 1);
Velocity *v = ecs_column(it, Velocity, 3); Position *p = ecs_column(it, Position, 2);
Velocity *v = ecs_column(it, Velocity, 3);
for (int i = 0; i < it->count; i++) {
Vehicle *car = &veh[i]; for (int i = 0; i < it->count; i++) {
Vehicle *car = &veh[i];
for (int j = 0; j < 4; j++) {
// NOTE(zaklaus): Perform seat cleanup for (int j = 0; j < 4; j++) {
if (!world_entity_valid(veh[i].seats[j])) { // NOTE(zaklaus): Perform seat cleanup
veh[i].seats[j] = 0; if (!world_entity_valid(veh[i].seats[j])) {
continue; veh[i].seats[j] = 0;
} continue;
}
ecs_entity_t pe = veh[i].seats[j];
ecs_entity_t pe = veh[i].seats[j];
// NOTE(zaklaus): Handle driver input
if (j == 0) { // NOTE(zaklaus): Handle driver input
Input const* in = ecs_get(it->world, pe, Input); if (j == 0) {
Input const* in = ecs_get(it->world, pe, Input);
car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, VEHICLE_ACCEL*it->delta_time);
if (in->sprint) { car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, VEHICLE_ACCEL*it->delta_time);
car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*it->delta_time); if (in->sprint) {
car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*it->delta_time);
if (zpl_abs(car->force) < 5.5f)
car->force = 0.0f; if (zpl_abs(car->force) < 5.5f)
} car->force = 0.0f;
car->steer *= 0.97f; }
car->steer += (in->x * VEHICLE_STEER)*it->delta_time; car->steer *= 0.97f;
car->steer = zpl_clamp(car->steer, -40.0f, 40.0f); car->steer += (in->x * VEHICLE_STEER)*it->delta_time;
} car->steer = zpl_clamp(car->steer, -40.0f, 40.0f);
} }
}
car->force = zpl_clamp(car->force, car->reverse_speed, car->speed);
car->force = zpl_clamp(car->force, car->reverse_speed, car->speed);
// NOTE(zaklaus): Vehicle physics
float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading); // NOTE(zaklaus): Vehicle physics
float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading); float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading);
float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading);
float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading);
float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading); float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading);
float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading);
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f); world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.block_id), 0.0f, 1.0f);
bk_x += car->force * drag * zpl_cos(car->heading);
bk_y += car->force * drag * zpl_sin(car->heading); bk_x += car->force * drag * zpl_cos(car->heading);
fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)); bk_y += car->force * drag * zpl_sin(car->heading);
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)); fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer));
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer));
v[i].x += (fr_x + bk_x) / 2.0f - p[i].x;
v[i].y += (fr_y + bk_y) / 2.0f - p[i].y; v[i].x += ((fr_x + bk_x) / 2.0f - p[i].x)*it->delta_time*VEHICLE_POWER;
car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x); v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y)*it->delta_time*VEHICLE_POWER;
car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x);
world_block_lookup lookahead = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y+PHY_LOOKAHEAD(v[i].y));
uint32_t flags = blocks_get_flags(lookahead.block_id); world_block_lookup lookahead = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y+PHY_LOOKAHEAD(v[i].y));
if (flags & BLOCK_FLAG_COLLISION) { uint32_t flags = blocks_get_flags(lookahead.block_id);
car->force = 0.0f; if (flags & BLOCK_FLAG_COLLISION) {
} car->force = 0.0f;
}
for (int j = 0; j < 4; j++) {
if (!world_entity_valid(veh[i].seats[j])) continue; for (int j = 0; j < 4; j++) {
ecs_entity_t pe = veh[i].seats[j]; if (!world_entity_valid(veh[i].seats[j])) continue;
ecs_entity_t pe = veh[i].seats[j];
// NOTE(zaklaus): Update passenger position
{ // NOTE(zaklaus): Update passenger position
Position *p2 = ecs_get_mut(it->world, pe, Position, NULL); {
Velocity *v2 = ecs_get_mut(it->world, pe, Velocity, NULL); Position *p2 = ecs_get_mut(it->world, pe, Position, NULL);
*p2 = p[i]; Velocity *v2 = ecs_get_mut(it->world, pe, Velocity, NULL);
*v2 = v[i]; *p2 = p[i];
} *v2 = v[i];
} }
}
{
debug_v2 b2 = {p[i].x + zpl_cos(car->heading)*(car->wheel_base), p[i].y + zpl_sin(car->heading)*(car->wheel_base)}; {
debug_push_line((debug_v2){p[i].x, p[i].y}, b2, 0x0000FFFF); debug_v2 b2 = {p[i].x + zpl_cos(car->heading)*(car->wheel_base), p[i].y + zpl_sin(car->heading)*(car->wheel_base)};
debug_push_line((debug_v2){p[i].x, p[i].y}, b2, 0x0000FFFF);
// NOTE(zaklaus): force
{ // NOTE(zaklaus): force
float dx = zpl_cos(car->heading); {
float dy = zpl_sin(car->heading); float dx = zpl_cos(car->heading);
debug_push_circle((debug_v2){p[i].x+dx*car->force, p[i].y+dy*car->force}, 5.0f, 0x00FF00FF); float dy = zpl_sin(car->heading);
} debug_push_circle((debug_v2){p[i].x+dx*car->force, p[i].y+dy*car->force}, 5.0f, 0x00FF00FF);
}
// NOTE(zaklaus): steer
{ // NOTE(zaklaus): steer
float dx = zpl_sin(car->heading); {
float dy = -zpl_cos(car->heading); float dx = zpl_sin(car->heading);
debug_push_circle((debug_v2){p[i].x+dx*car->steer*-20, p[i].y+dy*car->steer*-20}, 5.0f, 0x00FFAAFF); float dy = -zpl_cos(car->heading);
} debug_push_circle((debug_v2){p[i].x+dx*car->steer*-20, p[i].y+dy*car->steer*-20}, 5.0f, 0x00FFAAFF);
} }
} }
} }
}
void ClearVehicle(ecs_iter_t *it) {
Vehicle *veh = ecs_column(it, Vehicle, 1); void ClearVehicle(ecs_iter_t *it) {
Vehicle *veh = ecs_column(it, Vehicle, 1);
for (int i = 0; i < it->count; i++) {
for (int k = 0; k < 4; k++) { for (int i = 0; i < it->count; i++) {
if (world_entity_valid(veh[i].seats[k])) { for (int k = 0; k < 4; k++) {
ecs_remove(it->world, veh[i].seats[k], IsInVehicle); if (world_entity_valid(veh[i].seats[k])) {
} ecs_remove(it->world, veh[i].seats[k], IsInVehicle);
} }
} }
} }
}