forked from Mirrorlandia_minetest/minetest
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.
This commit is contained in:
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
Block a user