summaryrefslogtreecommitdiffstats
path: root/game
diff options
context:
space:
mode:
authoruntodesu <kirill@untode.su>2025-03-21 18:16:40 +0500
committeruntodesu <kirill@untode.su>2025-03-21 18:16:40 +0500
commit9f80a278a6f188c8e9131df0684bad14a07491ee (patch)
tree067c6ad4b892a34c3c03e3ecdbe312453bd46ea4 /game
parentfddd7f761176bb45cfdd41eeccaeadac22d33ddf (diff)
downloadvoxelius-9f80a278a6f188c8e9131df0684bad14a07491ee.tar.bz2
voxelius-9f80a278a6f188c8e9131df0684bad14a07491ee.zip
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
Diffstat (limited to 'game')
-rw-r--r--game/client/camera.cc9
-rw-r--r--game/client/chunk_renderer.cc4
-rw-r--r--game/client/game.cc11
-rw-r--r--game/client/gamepad.cc21
-rw-r--r--game/client/player_move.cc49
-rw-r--r--game/client/toggles.cc140
-rw-r--r--game/client/toggles.hh28
-rw-r--r--game/shared/collision.cc11
-rw-r--r--game/shared/factory.cc2
9 files changed, 195 insertions, 80 deletions
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<float>(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<GravityComponent>(globals::player);
+ else globals::dimension->entities.emplace_or_replace<GravityComponent>(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<std::size_t>(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<ToggleEnabledEvent>().connect<&on_toggle_enable>();
+ globals::dispatcher.sink<ToggleDisabledEvent>().connect<&on_toggle_disable>();
globals::dispatcher.sink<GlfwJoystickEvent>().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<float> 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<float>;
+
+ next_jump_us = 0x0000000000000000U;
speedometer_value = 0.0f;
footsteps_distance = 0.0f;
- movement_direction = ZERO_VEC3<float>;
+ // UNDONE: make this a separate subsystem
pitch_random.seed(std::random_device()());
pitch_distrib = std::uniform_real_distribution<float>(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<GroundedComponent>(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<GlfwKeyEvent>().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
@@ -13,15 +30,10 @@ extern bool is_sequence_await;
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 */
diff --git a/game/shared/collision.cc b/game/shared/collision.cc
index 126d3bb..0593fbb 100644
--- a/game/shared/collision.cc
+++ b/game/shared/collision.cc
@@ -6,6 +6,7 @@
#include "shared/coord.hh"
#include "shared/dimension.hh"
#include "shared/globals.hh"
+#include "shared/gravity.hh"
#include "shared/grounded.hh"
#include "shared/transform.hh"
#include "shared/velocity.hh"
@@ -146,11 +147,15 @@ void CollisionComponent::fixed_update(Dimension *dimension)
auto surface = voxel_surface::UNKNOWN;
auto vertical_move = vgrid_collide(dimension, 1, collision, transform, velocity, surface);
- if(vertical_move == cxpr::sign<int>(dimension->get_gravity())) {
- auto &component = dimension->entities.get_or_emplace<GroundedComponent>(entity);
- component.surface = surface;
+ if(dimension->entities.any_of<GravityComponent>(entity)) {
+ if(vertical_move == cxpr::sign<int>(dimension->get_gravity()))
+ dimension->entities.emplace_or_replace<GroundedComponent>(entity, GroundedComponent{surface});
+ else dimension->entities.remove<GroundedComponent>(entity);
}
else {
+ // The entity cannot be grounded because the component
+ // setup of said entity should not let it comprehend the
+ // concept of resting on the ground (it flies around)
dimension->entities.remove<GroundedComponent>(entity);
}
diff --git a/game/shared/factory.cc b/game/shared/factory.cc
index ee09a24..34cc55c 100644
--- a/game/shared/factory.cc
+++ b/game/shared/factory.cc
@@ -18,8 +18,6 @@ void shared_factory::create_player(Dimension *dimension, entt::entity entity)
collision.aabb.min = glm::fvec3(-0.4f, -1.6f, -0.4f);
collision.aabb.max = glm::fvec3(+0.4f, +0.2f, +0.4f);
- dimension->entities.emplace_or_replace<GravityComponent>(entity);
-
auto &head = dimension->entities.emplace_or_replace<HeadComponent>(entity);
head.angles = glm::fvec3(0.0f, 0.0f, 0.0f);
head.offset = glm::fvec3(0.0f, 0.0f, 0.0f);