Client-side autojump. Remove Android-only stepheight autojump (#7228)
Works by detecting a collision while moving forward and then simulating a jump. If the simulated jump is more successful, an artificial jump key press is injected in the client. Includes setting and key change GUI element for enabling and disabling this feature.wsc-master-rebase
parent
1c91cb8f8f
commit
93bccb3490
|
@ -111,6 +111,10 @@ always_fly_fast (Always fly and fast) bool true
|
||||||
# mouse button.
|
# mouse button.
|
||||||
repeat_rightclick_time (Rightclick repetition interval) float 0.25
|
repeat_rightclick_time (Rightclick repetition interval) float 0.25
|
||||||
|
|
||||||
|
# Automatically jump up single-node obstacles.
|
||||||
|
# type: bool
|
||||||
|
autojump (Automatic jumping) bool false
|
||||||
|
|
||||||
# Prevent digging and placing from repeating when holding the mouse buttons.
|
# Prevent digging and placing from repeating when holding the mouse buttons.
|
||||||
# Enable this when you dig or place too often by accident.
|
# Enable this when you dig or place too often by accident.
|
||||||
safe_dig_and_place (Safe digging and placing) bool false
|
safe_dig_and_place (Safe digging and placing) bool false
|
||||||
|
|
|
@ -69,6 +69,10 @@
|
||||||
# type: bool
|
# type: bool
|
||||||
# always_fly_fast = true
|
# always_fly_fast = true
|
||||||
|
|
||||||
|
# Automatically jump up single-node obstacles.
|
||||||
|
# type: bool
|
||||||
|
# autojump = false
|
||||||
|
|
||||||
# The time in seconds it takes between repeated right clicks when holding the right mouse button.
|
# The time in seconds it takes between repeated right clicks when holding the right mouse button.
|
||||||
# type: float
|
# type: float
|
||||||
# repeat_rightclick_time = 0.25
|
# repeat_rightclick_time = 0.25
|
||||||
|
|
|
@ -515,6 +515,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
||||||
|
|
||||||
info.node_p = nearest_info.position;
|
info.node_p = nearest_info.position;
|
||||||
info.old_speed = *speed_f;
|
info.old_speed = *speed_f;
|
||||||
|
info.plane = nearest_collided;
|
||||||
|
|
||||||
// Set the speed component that caused the collision to zero
|
// Set the speed component that caused the collision to zero
|
||||||
if (step_up) {
|
if (step_up) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ struct CollisionInfo
|
||||||
v3s16 node_p = v3s16(-32768,-32768,-32768); // COLLISION_NODE
|
v3s16 node_p = v3s16(-32768,-32768,-32768); // COLLISION_NODE
|
||||||
v3f old_speed;
|
v3f old_speed;
|
||||||
v3f new_speed;
|
v3f new_speed;
|
||||||
|
int plane = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct collisionMoveResult
|
struct collisionMoveResult
|
||||||
|
|
|
@ -247,6 +247,11 @@ void set_default_settings(Settings *settings)
|
||||||
settings->setDefault("aux1_descends", "false");
|
settings->setDefault("aux1_descends", "false");
|
||||||
settings->setDefault("doubletap_jump", "false");
|
settings->setDefault("doubletap_jump", "false");
|
||||||
settings->setDefault("always_fly_fast", "true");
|
settings->setDefault("always_fly_fast", "true");
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
settings->setDefault("autojump", "true");
|
||||||
|
#else
|
||||||
|
settings->setDefault("autojump", "false");
|
||||||
|
#endif
|
||||||
settings->setDefault("continuous_forward", "false");
|
settings->setDefault("continuous_forward", "false");
|
||||||
settings->setDefault("enable_joysticks", "false");
|
settings->setDefault("enable_joysticks", "false");
|
||||||
settings->setDefault("joystick_id", "0");
|
settings->setDefault("joystick_id", "0");
|
||||||
|
|
|
@ -2449,8 +2449,15 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
client->setPlayerControl(control);
|
|
||||||
LocalPlayer *player = client->getEnv().getLocalPlayer();
|
LocalPlayer *player = client->getEnv().getLocalPlayer();
|
||||||
|
|
||||||
|
// autojump if set: simulate "jump" key
|
||||||
|
if (player->getAutojump()) {
|
||||||
|
control.jump = true;
|
||||||
|
keypress_bits |= 1U << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->setPlayerControl(control);
|
||||||
player->keyPressed = keypress_bits;
|
player->keyPressed = keypress_bits;
|
||||||
|
|
||||||
//tt.stop();
|
//tt.stop();
|
||||||
|
|
|
@ -77,6 +77,7 @@ enum
|
||||||
// other
|
// other
|
||||||
GUI_ID_CB_AUX1_DESCENDS,
|
GUI_ID_CB_AUX1_DESCENDS,
|
||||||
GUI_ID_CB_DOUBLETAP_JUMP,
|
GUI_ID_CB_DOUBLETAP_JUMP,
|
||||||
|
GUI_ID_CB_AUTOJUMP,
|
||||||
};
|
};
|
||||||
|
|
||||||
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
|
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
|
||||||
|
@ -195,6 +196,21 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
|
||||||
offset += v2s32(0, 25);
|
offset += v2s32(0, 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
s32 option_x = offset.X;
|
||||||
|
s32 option_y = offset.Y + 5;
|
||||||
|
u32 option_w = 280;
|
||||||
|
{
|
||||||
|
core::rect<s32> rect(0, 0, option_w, 30);
|
||||||
|
rect += topleft + v2s32(option_x, option_y);
|
||||||
|
const wchar_t *text = wgettext("Automatic jumping");
|
||||||
|
Environment->addCheckBox(g_settings->getBool("autojump"), rect, this,
|
||||||
|
GUI_ID_CB_AUTOJUMP, text);
|
||||||
|
delete[] text;
|
||||||
|
}
|
||||||
|
offset += v2s32(0, 25);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
core::rect < s32 > rect(0, 0, 100, 30);
|
core::rect < s32 > rect(0, 0, 100, 30);
|
||||||
rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
|
rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
|
||||||
|
@ -239,14 +255,19 @@ bool GUIKeyChangeMenu::acceptInput()
|
||||||
|
|
||||||
{
|
{
|
||||||
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
|
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
|
||||||
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
|
if(e && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||||
g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
|
g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
|
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
|
||||||
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
|
if(e && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||||
g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
|
g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUTOJUMP);
|
||||||
|
if(e && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||||
|
g_settings->setBool("autojump", ((gui::IGUICheckBox*)e)->isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
clearKeyCache();
|
clearKeyCache();
|
||||||
|
|
||||||
|
|
|
@ -287,16 +287,9 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|
||||||
float player_stepheight = (m_cao == nullptr) ? 0.0f :
|
float player_stepheight = (m_cao == nullptr) ? 0.0f :
|
||||||
(touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
|
(touching_ground ? m_cao->getStepHeight() : (0.2f * BS));
|
||||||
|
|
||||||
// TODO this is a problematic hack.
|
|
||||||
// Use a better implementation for autojump, or apply a custom stepheight
|
|
||||||
// to all players, as this currently creates unintended special movement
|
|
||||||
// abilities and advantages for Android players on a server.
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (touching_ground)
|
|
||||||
player_stepheight += (0.6f * BS);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
v3f accel_f = v3f(0,0,0);
|
v3f accel_f = v3f(0,0,0);
|
||||||
|
const v3f initial_position = position;
|
||||||
|
const v3f initial_speed = m_speed;
|
||||||
|
|
||||||
collisionMoveResult result = collisionMoveSimple(env, m_client,
|
collisionMoveResult result = collisionMoveSimple(env, m_client,
|
||||||
pos_max_d, m_collisionbox, player_stepheight, dtime,
|
pos_max_d, m_collisionbox, player_stepheight, dtime,
|
||||||
|
@ -461,6 +454,9 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|
||||||
setSpeed(m_speed);
|
setSpeed(m_speed);
|
||||||
m_can_jump = false;
|
m_can_jump = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Autojump
|
||||||
|
handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
|
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
|
||||||
|
@ -891,11 +887,9 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
|
||||||
// this shouldn't be hardcoded but transmitted from server
|
// this shouldn't be hardcoded but transmitted from server
|
||||||
float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
|
float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
player_stepheight += (0.6 * BS);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
v3f accel_f = v3f(0, 0, 0);
|
v3f accel_f = v3f(0, 0, 0);
|
||||||
|
const v3f initial_position = position;
|
||||||
|
const v3f initial_speed = m_speed;
|
||||||
|
|
||||||
collisionMoveResult result = collisionMoveSimple(env, m_client,
|
collisionMoveResult result = collisionMoveSimple(env, m_client,
|
||||||
pos_max_d, m_collisionbox, player_stepheight, dtime,
|
pos_max_d, m_collisionbox, player_stepheight, dtime,
|
||||||
|
@ -1059,6 +1053,9 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
|
||||||
setSpeed(m_speed);
|
setSpeed(m_speed);
|
||||||
m_can_jump = false;
|
m_can_jump = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Autojump
|
||||||
|
handleAutojump(dtime, env, result, initial_position, initial_speed, pos_max_d);
|
||||||
}
|
}
|
||||||
|
|
||||||
float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
|
float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
|
||||||
|
@ -1080,3 +1077,82 @@ float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH)
|
||||||
}
|
}
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
|
||||||
|
const collisionMoveResult &result, const v3f &initial_position,
|
||||||
|
const v3f &initial_speed, f32 pos_max_d)
|
||||||
|
{
|
||||||
|
PlayerSettings &player_settings = getPlayerSettings();
|
||||||
|
if (!player_settings.autojump)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_autojump) {
|
||||||
|
// release autojump after a given time
|
||||||
|
m_autojump_time -= dtime;
|
||||||
|
if (m_autojump_time <= 0.0f)
|
||||||
|
m_autojump = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool control_forward = control.up ||
|
||||||
|
(!control.up && !control.down &&
|
||||||
|
control.forw_move_joystick_axis < -0.05);
|
||||||
|
bool could_autojump =
|
||||||
|
m_can_jump && !control.jump && !control.sneak && control_forward;
|
||||||
|
if (!could_autojump)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool horizontal_collision = false;
|
||||||
|
for (const auto &colinfo : result.collisions) {
|
||||||
|
if (colinfo.type == COLLISION_NODE && colinfo.plane != 1) {
|
||||||
|
horizontal_collision = true;
|
||||||
|
break; // one is enough
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be running against something to trigger autojumping
|
||||||
|
if (!horizontal_collision)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check for nodes above
|
||||||
|
v3f headpos_min = m_position + m_collisionbox.MinEdge * 0.99f;
|
||||||
|
v3f headpos_max = m_position + m_collisionbox.MaxEdge * 0.99f;
|
||||||
|
headpos_min.Y = headpos_max.Y; // top face of collision box
|
||||||
|
v3s16 ceilpos_min = floatToInt(headpos_min, BS) + v3s16(0, 1, 0);
|
||||||
|
v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0);
|
||||||
|
const NodeDefManager *ndef = env->getGameDef()->ndef();
|
||||||
|
bool is_position_valid;
|
||||||
|
for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) {
|
||||||
|
for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) {
|
||||||
|
MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid);
|
||||||
|
|
||||||
|
if (!is_position_valid)
|
||||||
|
break; // won't collide with the void outside
|
||||||
|
if (n.getContent() == CONTENT_IGNORE)
|
||||||
|
return; // players collide with ignore blocks -> same as walkable
|
||||||
|
const ContentFeatures &f = ndef->get(n);
|
||||||
|
if (f.walkable)
|
||||||
|
return; // would bump head, don't jump
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float jump_height = 1.1f; // TODO: better than a magic number
|
||||||
|
v3f jump_pos = initial_position + v3f(0.0f, jump_height * BS, 0.0f);
|
||||||
|
v3f jump_speed = initial_speed;
|
||||||
|
|
||||||
|
// try at peak of jump, zero step height
|
||||||
|
collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d,
|
||||||
|
m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed,
|
||||||
|
v3f(0, 0, 0));
|
||||||
|
|
||||||
|
// see if we can get a little bit farther horizontally if we had
|
||||||
|
// jumped
|
||||||
|
v3f run_delta = m_position - initial_position;
|
||||||
|
run_delta.Y = 0.0f;
|
||||||
|
v3f jump_delta = jump_pos - initial_position;
|
||||||
|
jump_delta.Y = 0.0f;
|
||||||
|
if (jump_delta.getLengthSQ() > run_delta.getLengthSQ() * 1.01f) {
|
||||||
|
m_autojump = true;
|
||||||
|
m_autojump_time = 0.1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ class GenericCAO;
|
||||||
class ClientActiveObject;
|
class ClientActiveObject;
|
||||||
class ClientEnvironment;
|
class ClientEnvironment;
|
||||||
class IGameDef;
|
class IGameDef;
|
||||||
|
struct collisionMoveResult;
|
||||||
|
|
||||||
enum LocalPlayerAnimations
|
enum LocalPlayerAnimations
|
||||||
{
|
{
|
||||||
|
@ -145,11 +146,17 @@ public:
|
||||||
float getZoomFOV() const { return m_zoom_fov; }
|
float getZoomFOV() const { return m_zoom_fov; }
|
||||||
void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; }
|
void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; }
|
||||||
|
|
||||||
|
bool getAutojump() const { return m_autojump; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
|
void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
|
||||||
void accelerateVertical(const v3f &target_speed, const f32 max_increase);
|
void accelerateVertical(const v3f &target_speed, const f32 max_increase);
|
||||||
bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max);
|
bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max);
|
||||||
float getSlipFactor(Environment *env, const v3f &speedH);
|
float getSlipFactor(Environment *env, const v3f &speedH);
|
||||||
|
void handleAutojump(f32 dtime, Environment *env,
|
||||||
|
const collisionMoveResult &result,
|
||||||
|
const v3f &position_before_move, const v3f &speed_before_move,
|
||||||
|
f32 pos_max_d);
|
||||||
|
|
||||||
v3f m_position;
|
v3f m_position;
|
||||||
v3s16 m_standing_node;
|
v3s16 m_standing_node;
|
||||||
|
@ -183,6 +190,8 @@ private:
|
||||||
BS * 1.75f, BS * 0.30f);
|
BS * 1.75f, BS * 0.30f);
|
||||||
float m_eye_height = 1.625f;
|
float m_eye_height = 1.625f;
|
||||||
float m_zoom_fov = 0.0f;
|
float m_zoom_fov = 0.0f;
|
||||||
|
bool m_autojump = false;
|
||||||
|
float m_autojump_time = 0.0f;
|
||||||
|
|
||||||
GenericCAO *m_cao = nullptr;
|
GenericCAO *m_cao = nullptr;
|
||||||
Client *m_client;
|
Client *m_client;
|
||||||
|
|
|
@ -144,6 +144,7 @@ void PlayerSettings::readGlobalSettings()
|
||||||
always_fly_fast = g_settings->getBool("always_fly_fast");
|
always_fly_fast = g_settings->getBool("always_fly_fast");
|
||||||
aux1_descends = g_settings->getBool("aux1_descends");
|
aux1_descends = g_settings->getBool("aux1_descends");
|
||||||
noclip = g_settings->getBool("noclip");
|
noclip = g_settings->getBool("noclip");
|
||||||
|
autojump = g_settings->getBool("autojump");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::settingsChangedCallback(const std::string &name, void *data)
|
void Player::settingsChangedCallback(const std::string &name, void *data)
|
||||||
|
|
|
@ -92,10 +92,11 @@ struct PlayerSettings
|
||||||
bool always_fly_fast = false;
|
bool always_fly_fast = false;
|
||||||
bool aux1_descends = false;
|
bool aux1_descends = false;
|
||||||
bool noclip = false;
|
bool noclip = false;
|
||||||
|
bool autojump = false;
|
||||||
|
|
||||||
const std::string setting_names[6] = {
|
const std::string setting_names[7] = {
|
||||||
"free_move", "fast_move", "continuous_forward", "always_fly_fast",
|
"free_move", "fast_move", "continuous_forward", "always_fly_fast",
|
||||||
"aux1_descends", "noclip"
|
"aux1_descends", "noclip", "autojump"
|
||||||
};
|
};
|
||||||
void readGlobalSettings();
|
void readGlobalSettings();
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@ fake_function() {
|
||||||
gettext("Double tap jump for fly");
|
gettext("Double tap jump for fly");
|
||||||
gettext("Double-tapping the jump key toggles fly mode.");
|
gettext("Double-tapping the jump key toggles fly mode.");
|
||||||
gettext("Always fly and fast");
|
gettext("Always fly and fast");
|
||||||
|
gettext("Automatic jumping");
|
||||||
gettext("If disabled, \"special\" key is used to fly fast if both fly and fast mode are enabled.");
|
gettext("If disabled, \"special\" key is used to fly fast if both fly and fast mode are enabled.");
|
||||||
gettext("Rightclick repetition interval");
|
gettext("Rightclick repetition interval");
|
||||||
gettext("The time in seconds it takes between repeated right clicks when holding the right mouse button.");
|
gettext("The time in seconds it takes between repeated right clicks when holding the right mouse button.");
|
||||||
|
|
Loading…
Reference in New Issue