From 9f80a278a6f188c8e9131df0684bad14a07491ee Mon Sep 17 00:00:00 2001 From: untodesu Date: Fri, 21 Mar 2025 18:16:40 +0500 Subject: Toggles system rework, added flight pmove mode - Reworked toggles to use a constant-styled enumerations - Added TOGGLE_PM_FLIGHT and an according movement mode. Now server-side just doesn't simulate gravity altogether for players, instead relying on whatever the client provides which works fine for now. Closes #12 --- game/client/camera.cc | 9 ++- game/client/chunk_renderer.cc | 4 +- game/client/game.cc | 11 +++- game/client/gamepad.cc | 21 +++++++ game/client/player_move.cc | 49 ++++++++++----- game/client/toggles.cc | 140 +++++++++++++++++++++++++++++------------- game/client/toggles.hh | 28 ++++++--- 7 files changed, 187 insertions(+), 75 deletions(-) (limited to 'game/client') diff --git a/game/client/camera.cc b/game/client/camera.cc index 5eb150a..30ec581 100644 --- a/game/client/camera.cc +++ b/game/client/camera.cc @@ -14,6 +14,7 @@ #include "client/player_move.hh" #include "client/session.hh" #include "client/settings.hh" +#include "client/toggles.hh" ConfigFloat camera::roll_angle(2.0f, 0.0f, 4.0f); ConfigFloat camera::vertical_fov(90.0f, 45.0f, 110.0f); @@ -88,10 +89,12 @@ void camera::update(void) glm::fvec3 right_vector, up_vector; cxangles::vectors(camera::angles, &camera::direction, &right_vector, &up_vector); - // Apply view roll angle - // FIXME: check if grounded auto client_angles = camera::angles; - client_angles[2] = cxpr::radians(-camera::roll_angle.get_value() * glm::dot(velocity.value / PMOVE_MAX_SPEED_GROUND, right_vector)); + + if(!toggles::get(TOGGLE_PM_FLIGHT)) { + // Apply the quake-like view rolling + client_angles[2] = cxpr::radians(-camera::roll_angle.get_value() * glm::dot(velocity.value / PMOVE_MAX_SPEED_GROUND, right_vector)); + } const auto z_near = 0.01f; const auto z_far = 1.25f * static_cast(CHUNK_SIZE * camera::view_distance.get_value()); diff --git a/game/client/chunk_renderer.cc b/game/client/chunk_renderer.cc index 1b038b1..7282f4a 100644 --- a/game/client/chunk_renderer.cc +++ b/game/client/chunk_renderer.cc @@ -87,7 +87,7 @@ void chunk_renderer::render(void) glDepthFunc(GL_LEQUAL); glLineWidth(1.0f); - if(toggles::render_wireframe) + if(toggles::get(TOGGLE_WIREFRAME)) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -192,7 +192,7 @@ void chunk_renderer::render(void) } } - if(toggles::draw_chunk_borders) { + if(toggles::get(TOGGLE_CHUNK_AABB)) { outline::prepare(); for(const auto [entity, chunk, mesh] : group.each()) { diff --git a/game/client/game.cc b/game/client/game.cc index 72e1dc5..af6fba6 100644 --- a/game/client/game.cc +++ b/game/client/game.cc @@ -354,6 +354,8 @@ void client_game::init(void) void client_game::init_late(void) { + toggles::init_late(); + sound::init_late(); language::init_late(); @@ -498,7 +500,14 @@ void client_game::fixed_update_late(void) void client_game::update(void) { + if(session::is_ingame()) { + if(toggles::get(TOGGLE_PM_FLIGHT)) + globals::dimension->entities.remove(globals::player); + else globals::dimension->entities.emplace_or_replace(globals::player); + } + sound::update(); + listener::update(); interpolation::update(); @@ -616,7 +625,7 @@ void client_game::layout(void) } if(!globals::gui_screen || (globals::gui_screen == GUI_CHAT)) { - if(toggles::draw_metrics && !client_game::hide_hud) { + if(toggles::get(TOGGLE_METRICS_UI) && !client_game::hide_hud) { // This contains Minecraft-esque debug information // about the hardware, world state and other // things that might be uesful diff --git a/game/client/gamepad.cc b/game/client/gamepad.cc index 73c60b6..b26dbb8 100644 --- a/game/client/gamepad.cc +++ b/game/client/gamepad.cc @@ -8,6 +8,7 @@ #include "client/glfw.hh" #include "client/globals.hh" #include "client/settings.hh" +#include "client/toggles.hh" constexpr static int INVALID_GAMEPAD_ID = INT_MAX; constexpr static std::size_t NUM_AXES = static_cast(GLFW_GAMEPAD_AXIS_LAST + 1); @@ -22,6 +23,24 @@ ConfigBoolean gamepad::active(false); GLFWgamepadstate gamepad::state; GLFWgamepadstate gamepad::last_state; +static void on_toggle_enable(const ToggleEnabledEvent &event) +{ + if(event.type == TOGGLE_USE_GAMEPAD) { + spdlog::info("KHUETA ENABLED"); + gamepad::active.set_value(true); + return; + } +} + +static void on_toggle_disable(const ToggleDisabledEvent &event) +{ + if(event.type == TOGGLE_USE_GAMEPAD) { + spdlog::info("KHUETA DISABLED"); + gamepad::active.set_value(false); + return; + } +} + static void on_glfw_joystick_event(const GlfwJoystickEvent &event) { if((event.event_type == GLFW_CONNECTED) && glfwJoystickIsGamepad(event.joystick_id) && (active_gamepad_id == INVALID_GAMEPAD_ID)) { @@ -92,6 +111,8 @@ void gamepad::init(void) for(int i = 0; i < NUM_AXES; gamepad::state.axes[i++] = 0.0f); for(int i = 0; i < NUM_BUTTONS; gamepad::state.buttons[i++] = GLFW_RELEASE); + globals::dispatcher.sink().connect<&on_toggle_enable>(); + globals::dispatcher.sink().connect<&on_toggle_disable>(); globals::dispatcher.sink().connect<&on_glfw_joystick_event>(); } diff --git a/game/client/player_move.cc b/game/client/player_move.cc index 2c196f9..f17603a 100644 --- a/game/client/player_move.cc +++ b/game/client/player_move.cc @@ -22,6 +22,7 @@ #include "client/settings.hh" #include "client/sound.hh" #include "client/status_lines.hh" +#include "client/toggles.hh" #include "client/voxel_sounds.hh" constexpr static std::uint64_t PMOVE_JUMP_COOLDOWN = 500000; // 0.5 seconds @@ -47,17 +48,17 @@ static ConfigGamepadButton button_move_up(GLFW_GAMEPAD_BUTTON_DPAD_UP); // General movement options static ConfigBoolean enable_speedometer(true); -static std::uint64_t next_jump; +static glm::fvec3 movement_direction; + +static std::uint64_t next_jump_us; static float speedometer_value; static float footsteps_distance; -static glm::fvec3 movement_direction; -// Pitch randomizer static std::mt19937_64 pitch_random; static std::uniform_real_distribution pitch_distrib; -// Quake III's PM_Accelerate clean-roomed -// into my physics and mathematics universe +// Quake III's PM_Accelerate-ish function used for +// conventional (gravity-affected non-flight) movement static glm::fvec3 pm_accelerate(const glm::fvec3 &wishdir, const glm::fvec3 &velocity, float wishspeed, float accel) { auto current_speed = glm::dot(velocity, wishdir); @@ -76,15 +77,14 @@ static glm::fvec3 pm_accelerate(const glm::fvec3 &wishdir, const glm::fvec3 &vel return result; } -// Returns new player velocity when moving in air -// FIXME: this feels like we have too much air control -static glm::fvec3 pm_airmove(const glm::fvec3 &wishdir, const glm::fvec3 &velocity) +// Conventional movement - velocity update when not on the ground +static glm::fvec3 pm_air_move(const glm::fvec3 &wishdir, const glm::fvec3 &velocity) { return pm_accelerate(wishdir, velocity, PMOVE_ACCELERATION_AIR, PMOVE_MAX_SPEED_AIR); } -// Returns new player velocity when moving on the ground -static glm::fvec3 pm_groundmove(const glm::fvec3 &wishdir, const glm::fvec3 &velocity) +// Conventional movement - velocity uodate when on the ground +static glm::fvec3 pm_ground_move(const glm::fvec3 &wishdir, const glm::fvec3 &velocity) { if(auto speed = glm::length(velocity)) { auto speed_drop = speed * PMOVE_FRICTION_GROUND * globals::fixed_frametime; @@ -95,13 +95,23 @@ static glm::fvec3 pm_groundmove(const glm::fvec3 &wishdir, const glm::fvec3 &vel return pm_accelerate(wishdir, velocity, PMOVE_ACCELERATION_GROUND, PMOVE_MAX_SPEED_GROUND); } +// A simpler minecraft-like acceleration model +// used whenever the TOGGLE_PM_FLIGHT is enabled +static glm::fvec3 pm_flight_move(const glm::fvec3 &wishdir) +{ + // FIXME: make it smoother + return wishdir * PMOVE_MAX_SPEED_AIR; +} + void player_move::init(void) { - next_jump = UINT64_C(0); + movement_direction = ZERO_VEC3; + + next_jump_us = 0x0000000000000000U; speedometer_value = 0.0f; footsteps_distance = 0.0f; - movement_direction = ZERO_VEC3; + // UNDONE: make this a separate subsystem pitch_random.seed(std::random_device()()); pitch_distrib = std::uniform_real_distribution(0.9f, 1.1f); @@ -149,11 +159,16 @@ void player_move::fixed_update(void) wishdir.x = glm::dot(movevars, right); wishdir.z = glm::dot(movevars, forward); + if(toggles::get(TOGGLE_PM_FLIGHT)) { + velocity.value = pm_flight_move(glm::fvec3(wishdir.x, movement_direction.y, wishdir.z)); + return; + } + auto grounded = globals::dimension->entities.try_get(globals::player); auto velocity_horizontal = glm::fvec3(velocity.value.x, 0.0f, velocity.value.z); if(grounded) { - auto new_velocity = pm_groundmove(wishdir, velocity_horizontal); + auto new_velocity = pm_ground_move(wishdir, velocity_horizontal); velocity.value.x = new_velocity.x; velocity.value.z = new_velocity.z; @@ -170,7 +185,7 @@ void player_move::fixed_update(void) } } else { - auto new_velocity = pm_airmove(wishdir, velocity_horizontal); + auto new_velocity = pm_air_move(wishdir, velocity_horizontal); velocity.value.x = new_velocity.x; velocity.value.z = new_velocity.z; } @@ -178,11 +193,11 @@ void player_move::fixed_update(void) if(movement_direction.y == 0.0f) { // Allow players to queue bunny-jumps by quickly // releasing and pressing the jump key again without a cooldown - next_jump = UINT64_C(0); + next_jump_us = 0x0000000000000000U; return; } - if(grounded && (movement_direction.y > 0.0f) && (globals::curtime >= next_jump)) { + if(grounded && (movement_direction.y > 0.0f) && (globals::curtime >= next_jump_us)) { velocity.value.y = -PMOVE_JUMP_FORCE * globals::dimension->get_gravity(); auto new_speed = glm::length(glm::fvec2(velocity.value.x, velocity.value.z)); @@ -191,7 +206,7 @@ void player_move::fixed_update(void) speedometer_value = new_speed; - next_jump = globals::curtime + PMOVE_JUMP_COOLDOWN; + next_jump_us = globals::curtime + PMOVE_JUMP_COOLDOWN; if(enable_speedometer.get_value()) { if(cxpr::abs(speed_change) < 0.01f) { diff --git a/game/client/toggles.cc b/game/client/toggles.cc index 7e20875..105b0d2 100644 --- a/game/client/toggles.cc +++ b/game/client/toggles.cc @@ -10,38 +10,37 @@ #include "client/globals.hh" #include "client/language.hh" -bool toggles::is_sequence_await = false; +struct ToggleInfo final { + const char *description; + int glfw_keycode; + bool is_enabled; +}; -bool toggles::draw_chunk_borders = false; -bool toggles::render_fullbright = false; -bool toggles::render_wireframe = false; +bool toggles::is_sequence_await = false; -#if defined(NDEBUG) -bool toggles::draw_metrics = false; -#else -bool toggles::draw_metrics = true; -#endif +static ToggleInfo toggle_infos[TOGGLE_COUNT]; -static void toggle_value(bool *value, const char *description) +static void print_toggle_state(const ToggleInfo &info) { - value[0] = !value[0]; - - if(description) { - if(value[0]) - client_chat::print(fmt::format("[debug] {} ON", description)); - else client_chat::print(fmt::format("[debug] {} OFF", description)); + if(info.description) { + if(info.is_enabled) + client_chat::print(fmt::format("[toggles] {} ON", info.description)); + else client_chat::print(fmt::format("[toggles] {} OFF", info.description)); } } -static void toggle_value_config(ConfigBoolean &value, const char *description) +static void toggle_value(ToggleInfo &info, toggle_type type) { - value.set_value(!value.get_value()); - - if(description) { - if(value.get_value()) - client_chat::print(fmt::format("[debug] {} ON", description)); - else client_chat::print(fmt::format("[debug] {} OFF", description)); + if(info.is_enabled) { + info.is_enabled = false; + globals::dispatcher.trigger(ToggleDisabledEvent {type}); } + else { + info.is_enabled = true; + globals::dispatcher.trigger(ToggleEnabledEvent {type}); + } + + print_toggle_state(info); } static void on_glfw_key(const GlfwKeyEvent &event) @@ -55,43 +54,96 @@ static void on_glfw_key(const GlfwKeyEvent &event) if(event.key == DEBUG_KEY) { if(event.action == GLFW_PRESS) { toggles::is_sequence_await = true; + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard; return; } if(event.action == GLFW_RELEASE) { toggles::is_sequence_await = false; + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; return; } } if((event.action == GLFW_PRESS) && toggles::is_sequence_await) { - switch(event.key) { - case GLFW_KEY_G: - toggle_value(&toggles::draw_chunk_borders, "chunk borders"); - return; - case GLFW_KEY_V: - toggle_value(&toggles::draw_metrics, nullptr); - return; - case GLFW_KEY_J: - toggle_value(&toggles::render_fullbright, nullptr); - return; - case GLFW_KEY_Z: - toggle_value(&toggles::render_wireframe, "wireframe"); - return; - case GLFW_KEY_P: - toggle_value_config(gamepad::active, "gamepad input"); - return; - case GLFW_KEY_L: - // This causes the language subsystem - // to re-parse the JSON file essentially - // causing the game to soft-reload language - language::set(language::get_current()); + if(event.key == GLFW_KEY_L) { + // This causes the language subsystem + // to re-parse the JSON file essentially + // causing the game to soft-reload language + language::set(language::get_current()); + return; + } + + for(toggle_type i = 0; i < TOGGLE_COUNT; ++i) { + if(event.key == toggle_infos[i].glfw_keycode) { + toggle_value(toggle_infos[i], i); return; + } } } } void toggles::init(void) { + toggle_infos[TOGGLE_WIREFRAME].description = "wireframe"; + toggle_infos[TOGGLE_WIREFRAME].glfw_keycode = GLFW_KEY_Z; + toggle_infos[TOGGLE_WIREFRAME].is_enabled = false; + + toggle_infos[TOGGLE_FULLBRIGHT].description = "fullbright"; + toggle_infos[TOGGLE_FULLBRIGHT].glfw_keycode = GLFW_KEY_J; + toggle_infos[TOGGLE_FULLBRIGHT].is_enabled = false; + + toggle_infos[TOGGLE_CHUNK_AABB].description = "chunk Borders"; + toggle_infos[TOGGLE_CHUNK_AABB].glfw_keycode = GLFW_KEY_G; + toggle_infos[TOGGLE_CHUNK_AABB].is_enabled = false; + + toggle_infos[TOGGLE_METRICS_UI].description = nullptr; + toggle_infos[TOGGLE_METRICS_UI].glfw_keycode = GLFW_KEY_V; + toggle_infos[TOGGLE_METRICS_UI].is_enabled = false; + + toggle_infos[TOGGLE_USE_GAMEPAD].description = "gamepad input"; + toggle_infos[TOGGLE_USE_GAMEPAD].glfw_keycode = GLFW_KEY_P; + toggle_infos[TOGGLE_USE_GAMEPAD].is_enabled = false; + + toggle_infos[TOGGLE_PM_FLIGHT].description = "flight mode"; + toggle_infos[TOGGLE_PM_FLIGHT].glfw_keycode = GLFW_KEY_F; + toggle_infos[TOGGLE_PM_FLIGHT].is_enabled = false; + +#ifndef NDEBUG + toggle_infos[TOGGLE_WIREFRAME].is_enabled = true; +#endif + globals::dispatcher.sink().connect<&on_glfw_key>(); } + +void toggles::init_late(void) +{ + for(toggle_type i = 0; i < TOGGLE_COUNT; ++i) { + if(toggle_infos[i].is_enabled) + globals::dispatcher.trigger(ToggleEnabledEvent {i}); + else globals::dispatcher.trigger(ToggleDisabledEvent {i}); + } +} + +bool toggles::get(toggle_type type) +{ + if(type < TOGGLE_COUNT) + return toggle_infos[type].is_enabled; + return false; +} + +void toggles::set(toggle_type type, bool value) +{ + if(type < TOGGLE_COUNT) { + if(value) { + toggle_infos[type].is_enabled = true; + globals::dispatcher.trigger(ToggleEnabledEvent {type}); + } + else { + toggle_infos[type].is_enabled = false; + globals::dispatcher.trigger(ToggleDisabledEvent {type}); + } + + print_toggle_state(toggle_infos[type]); + } +} diff --git a/game/client/toggles.hh b/game/client/toggles.hh index 98b9fe5..d5b18c7 100644 --- a/game/client/toggles.hh +++ b/game/client/toggles.hh @@ -2,6 +2,23 @@ #define CLIENT_TOGGLES_HH 1 #pragma once +using toggle_type = unsigned int; +constexpr static toggle_type TOGGLE_WIREFRAME = 0x0000U; // Render things in wireframe mode +constexpr static toggle_type TOGGLE_FULLBRIGHT = 0x0001U; // Render things without lighting +constexpr static toggle_type TOGGLE_CHUNK_AABB = 0x0002U; // Render chunk bounding boxes +constexpr static toggle_type TOGGLE_METRICS_UI = 0x0003U; // Render debug metrics overlay +constexpr static toggle_type TOGGLE_USE_GAMEPAD = 0x0004U; // Use gamepad for player movement +constexpr static toggle_type TOGGLE_PM_FLIGHT = 0x0005U; // Enable flight for player movement +constexpr static std::size_t TOGGLE_COUNT = 0x0006U; + +struct ToggleEnabledEvent final { + toggle_type type; +}; + +struct ToggleDisabledEvent final { + toggle_type type; +}; + namespace toggles { // The value is true whenever the debug @@ -11,17 +28,12 @@ namespace toggles extern bool is_sequence_await; } // namespace toggles -namespace toggles -{ -extern bool draw_chunk_borders; -extern bool draw_metrics; -extern bool render_fullbright; -extern bool render_wireframe; -} // namespace toggles - namespace toggles { void init(void); +void init_late(void); +bool get(toggle_type type); +void set(toggle_type type, bool value); } // namespace toggles #endif /* CLIENT_TOGGLES_HH */ -- cgit