Added gravity and refactored player controls

- Added gravity (still testing)
  - Two modes: realistic (9.8 m/s^2) and Minecraft (32 m/s^2)
  - Switch with G
    - Changing (0; 0; 0) rebound to H
- Added walking controls
  - Minecraft-style
  - WIP
- LayerTestGUI now displays dev options
- Added Units
This commit is contained in:
OLEGSHA 2020-11-14 13:49:35 +03:00
parent 9c894215f8
commit 154cc0a3b8
8 changed files with 516 additions and 267 deletions

View File

@ -3,10 +3,10 @@ package ru.windcorp.progressia.client;
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.flat.LayerTestUI;
import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.LayerTestGUI;
public class ClientState {

View File

@ -1,125 +0,0 @@
/*******************************************************************************
* Progressia
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.progressia.client.graphics.gui;
import com.google.common.eventbus.Subscribe;
import glm.vec._2.i.Vec2i;
import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.client.localization.MutableString;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
public class LayerTestGUI extends GUILayer {
private static class DebugComponent extends Component {
private final int color;
public DebugComponent(String name, Vec2i size, int color) {
super(name);
this.color = color;
setPreferredSize(size);
addListener(new Object() {
@Subscribe
public void onHoverChanged(HoverEvent e) {
requestReassembly();
}
});
addListener(KeyEvent.class, this::onClicked);
}
private boolean onClicked(KeyEvent event) {
if (!event.isMouse()) {
return false;
} else if (event.isPress() && event.isLeftMouseButton()) {
System.out.println("You pressed a Component!");
}
return true;
}
@Override
protected void assembleSelf(RenderTarget target) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK);
target.fill(
getX() + 2, getY() + 2,
getWidth() - 4, getHeight() - 4,
isHovered() ? Colors.DEBUG_YELLOW : color
);
}
}
public LayerTestGUI() {
super("LayerTestGui", new LayoutAlign(1, 0.75, 5));
Panel panel = new Panel("Alex", new LayoutVertical(5));
panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44));
Component charlie = new DebugComponent("Charlie", null, 0x222222);
charlie.setLayout(new LayoutVertical(5));
//Debug
Localizer.getInstance().setLanguage("ru-RU");
MutableString epsilon = new MutableStringLocalized("Epsilon")
.addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four");
// These two are swapped in code due to a bug in layouts, fixing ATM
charlie.addChild(
new Label(
"Delta",
new Font().withColor(0xCCBB44).deriveShadow().deriveBold(),
"Пре-альфа!"
)
);
charlie.addChild(
new Label(
"Epsilon",
new Font().withColor(0x4444BB).deriveItalic(),
() -> epsilon.get().concat("\u269b")
)
);
panel.addChild(charlie);
charlie.addListener(KeyEvent.class, e -> {
if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) {
Localizer localizer = Localizer.getInstance();
if (localizer.getLanguage().equals("ru-RU")) {
localizer.setLanguage("en-US");
} else {
localizer.setLanguage("ru-RU");
}
return true;
} return false;
});
charlie.setFocusable(true);
charlie.takeFocus();
getRoot().addChild(panel);
}
}

View File

@ -272,6 +272,10 @@ public class Camera {
}
}
public int getCurrentModeIndex() {
return currentModeIndex;
}
public float getLastAnchorYaw() {
return lastAnchorYaw;
}

View File

@ -20,41 +20,27 @@ package ru.windcorp.progressia.client.graphics.world;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import glm.Glm;
import glm.mat._3.Mat3;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.comms.controls.InputBasedControls;
import ru.windcorp.progressia.client.graphics.Layer;
import ru.windcorp.progressia.client.graphics.backend.FaceCulling;
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.collision.Collideable;
import ru.windcorp.progressia.common.collision.colliders.Collider;
import ru.windcorp.progressia.common.util.FloatMathUtils;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.test.CollisionModelRenderer;
import ru.windcorp.progressia.test.TestPlayerControls;
public class LayerWorld extends Layer {
private final Mat3 angMat = new Mat3();
private int movementForward = 0;
private int movementRight = 0;
private int movementUp = 0;
private final WorldRenderHelper helper = new WorldRenderHelper();
private final Client client;
private final InputBasedControls inputBasedControls;
private final TestPlayerControls tmp_testControls = TestPlayerControls.getInstance();
public LayerWorld(Client client) {
super("World");
@ -75,40 +61,12 @@ public class LayerWorld extends Layer {
@Override
protected void doRender() {
if (client.getLocalPlayer() != null) {
tmp_handleControls();
}
Camera camera = client.getCamera();
if (camera.hasAnchor()) {
renderWorld();
}
}
private void tmp_handleControls() {
EntityData player = client.getLocalPlayer();
angMat.identity().rotateZ(player.getYaw());
Vec3 movement = Vectors.grab3();
// Horizontal and vertical max control speed
final float movementSpeed = 0.1f * 60.0f;
// (0; 1], 1 is instant change, 0 is no control authority
final float controlAuthority = 0.1f;
movement.set(movementForward, -movementRight, 0);
if (movementForward != 0 && movementRight != 0) movement.normalize();
angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped
movement.z = movementUp;
movement.mul(movementSpeed);
movement.sub(player.getVelocity());
movement.mul(controlAuthority);
player.getVelocity().add(movement);
Vectors.release(movement);
}
private void renderWorld() {
client.getCamera().apply(helper);
FaceCulling.push(true);
@ -127,11 +85,16 @@ public class LayerWorld extends Layer {
private static final boolean RENDER_COLLISION_MODELS = false;
private void tmp_doEveryFrame() {
float tickLength = (float) GraphicsInterface.getFrameLength();
try {
tmp_performCollisions();
tmp_performCollisions(tickLength);
tmp_testControls.applyPlayerControls();
for (EntityData data : this.client.getWorld().getData().getEntities()) {
tmp_applyFriction(data);
tmp_applyGravity(data, tickLength);
tmp_renderCollisionModel(data);
}
} catch (Throwable e) {
@ -147,121 +110,47 @@ public class LayerWorld extends Layer {
}
}
private void tmp_performCollisions() {
private void tmp_performCollisions(float tickLength) {
tmp_collideableList.clear();
tmp_collideableList.addAll(this.client.getWorld().getData().getEntities());
Collider.performCollisions(
tmp_collideableList,
this.client.getWorld().getData(),
(float) GraphicsInterface.getFrameLength(),
tickLength,
tmp_colliderWorkspace
);
}
private void tmp_applyFriction(EntityData entity) {
final float frictionCoeff = 1 - 1e-2f;
final float frictionCoeff = 1 - 1e-5f;
entity.getVelocity().mul(frictionCoeff);
}
private void tmp_applyGravity(EntityData entity, float tickLength) {
if (ClientState.getInstance().getLocalPlayer() == entity && tmp_testControls.isFlying()) {
return;
}
final float gravitationalAcceleration;
if (tmp_testControls.useMinecraftGravity()) {
gravitationalAcceleration = 32f * Units.METERS_PER_SECOND_SQUARED; // plz dont sue me M$
} else {
gravitationalAcceleration = 9.81f * Units.METERS_PER_SECOND_SQUARED;
}
entity.getVelocity().add(0, 0, -gravitationalAcceleration * tickLength);
}
@Override
protected void handleInput(Input input) {
if (input.isConsumed()) return;
InputEvent event = input.getEvent();
if (event instanceof KeyEvent) {
if (onKeyEvent((KeyEvent) event)) {
input.consume();
}
} else if (event instanceof CursorMoveEvent) {
onMouseMoved((CursorMoveEvent) event);
input.consume();
}
tmp_testControls.handleInput(input);
if (!input.isConsumed()) {
inputBasedControls.handleInput(input);
}
}
private boolean flag = true;
private boolean onKeyEvent(KeyEvent event) {
if (event.isRepeat()) return false;
int multiplier = event.isPress() ? 1 : -1;
switch (event.getKey()) {
case GLFW.GLFW_KEY_W:
movementForward += +1 * multiplier;
break;
case GLFW.GLFW_KEY_S:
movementForward += -1 * multiplier;
break;
case GLFW.GLFW_KEY_A:
movementRight += -1 * multiplier;
break;
case GLFW.GLFW_KEY_D:
movementRight += +1 * multiplier;
break;
case GLFW.GLFW_KEY_SPACE:
movementUp += +1 * multiplier;
break;
case GLFW.GLFW_KEY_LEFT_SHIFT:
movementUp += -1 * multiplier;
break;
case GLFW.GLFW_KEY_ESCAPE:
if (!event.isPress()) return false;
if (flag) {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
} else {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
}
flag = !flag;
break;
case GLFW.GLFW_KEY_F5:
if (!event.isPress()) return false;
if (client.getCamera().hasAnchor()) {
client.getCamera().selectNextMode();
}
break;
default:
return false;
}
return true;
}
private void onMouseMoved(CursorMoveEvent event) {
if (!flag) return;
final float yawScale = -0.002f;
final float pitchScale = yawScale;
EntityData player = client.getLocalPlayer();
if (player != null) {
normalizeAngles(player.getDirection().add(
(float) (event.getChangeX() * yawScale),
(float) (event.getChangeY() * pitchScale)
));
}
}
private void normalizeAngles(Vec2 dir) {
// Normalize yaw
dir.x = FloatMathUtils.normalizeAngle(dir.x);
// Clamp pitch
dir.y = Glm.clamp(
dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2
);
}
}

View File

@ -0,0 +1,56 @@
package ru.windcorp.progressia.common;
public class Units {
// Base units
// We're SI.
public static final float METERS = 1;
public static final float KILOGRAMS = 1;
public static final float SECONDS = 1;
// Length
public static final float CENTIMETERS = METERS / 100;
public static final float MILLIMETERS = METERS / 1000;
public static final float KILOMETERS = METERS * 1000;
// Surface
public static final float SQUARE_CENTIMETERS = CENTIMETERS * CENTIMETERS;
public static final float SQUARE_METERS = METERS * METERS;
public static final float SQUARE_MILLIMETERS = MILLIMETERS * MILLIMETERS;
public static final float SQUARE_KILOMETERS = KILOMETERS * KILOMETERS;
// Volume
public static final float CUBIC_CENTIMETERS = CENTIMETERS * CENTIMETERS * CENTIMETERS;
public static final float CUBIC_METERS = METERS * METERS * METERS;
public static final float CUBIC_MILLIMETERS = MILLIMETERS * MILLIMETERS * MILLIMETERS;
public static final float CUBIC_KILOMETERS = KILOMETERS * KILOMETERS * KILOMETERS;
// Mass
public static final float GRAMS = KILOGRAMS / 1000;
public static final float TONNES = KILOGRAMS * 1000;
// Density
public static final float KILOGRAMS_PER_CUBIC_METER = KILOGRAMS / CUBIC_METERS;
public static final float GRAMS_PER_CUBIC_CENTIMETER = GRAMS / CUBIC_CENTIMETERS;
// Time
public static final float MILLISECONDS = SECONDS / 1000;
public static final float MINUTES = SECONDS * 60;
public static final float HOURS = MINUTES * 60;
public static final float DAYS = HOURS * 24;
// Frequency
public static final float HERTZ = 1 / SECONDS;
public static final float KILOHERTZ = HERTZ * 1000;
// Velocity
public static final float METERS_PER_SECOND = METERS / SECONDS;
public static final float KILOMETERS_PER_HOUR = KILOMETERS / HOURS;
// Acceleration
public static final float METERS_PER_SECOND_SQUARED = METERS_PER_SECOND / SECONDS;
// Force
public static final float NEWTONS = METERS_PER_SECOND_SQUARED * KILOGRAMS;
}

View File

@ -0,0 +1,155 @@
/*******************************************************************************
* Progressia
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.progressia.test;
import java.util.ArrayList;
import java.util.Collection;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Panel;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
public class LayerTestGUI extends GUILayer {
public LayerTestGUI() {
super("LayerTestGui", new LayoutAlign(0, 1, 5));
Panel panel = new Panel("ControlDisplays", new LayoutVertical(5));
Collection<Label> labels = new ArrayList<>();
panel.addChild(new Label(
"IsFlyingDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
() -> String.format("Flying: %5s (Space bar x2)", TestPlayerControls.getInstance().isFlying())
));
panel.addChild(new Label(
"IsMouseCapturedDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
() -> String.format("Mouse captured: %5s (esc)", TestPlayerControls.getInstance().isMouseCaptured())
));
panel.addChild(new Label(
"CameraModeDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
() -> String.format("Camera mode: %5d (F5)", ClientState.getInstance().getCamera().getCurrentModeIndex())
));
panel.addChild(new Label(
"GravityModeDisplay", new Font().withColor(0x37A3E6).deriveShadow(),
() -> String.format("Gravity: %9s (G)", TestPlayerControls.getInstance().useMinecraftGravity() ? "Minecraft" : "Realistic")
));
panel.getChildren().forEach(c -> labels.add((Label) c));
TestPlayerControls.getInstance().setUpdateCallback(() -> labels.forEach(Label::update));
getRoot().addChild(panel);
}
// private static class DebugComponent extends Component {
// private final int color;
//
// public DebugComponent(String name, Vec2i size, int color) {
// super(name);
// this.color = color;
//
// setPreferredSize(size);
//
// addListener(new Object() {
// @Subscribe
// public void onHoverChanged(HoverEvent e) {
// requestReassembly();
// }
// });
//
// addListener(KeyEvent.class, this::onClicked);
// }
//
// private boolean onClicked(KeyEvent event) {
// if (!event.isMouse()) {
// return false;
// } else if (event.isPress() && event.isLeftMouseButton()) {
// System.out.println("You pressed a Component!");
// }
// return true;
// }
//
// @Override
// protected void assembleSelf(RenderTarget target) {
// target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK);
//
// target.fill(
// getX() + 2, getY() + 2,
// getWidth() - 4, getHeight() - 4,
// isHovered() ? Colors.DEBUG_YELLOW : color
// );
// }
// }
//
// public LayerTestGUI() {
// super("LayerTestGui", new LayoutAlign(1, 0.75, 5));
//
// Panel panel = new Panel("Alex", new LayoutVertical(5));
//
// panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44));
//
// Component charlie = new DebugComponent("Charlie", null, 0x222222);
// charlie.setLayout(new LayoutVertical(5));
//
// //Debug
// Localizer.getInstance().setLanguage("ru-RU");
// MutableString epsilon = new MutableStringLocalized("Epsilon")
// .addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four");
// // These two are swapped in code due to a bug in layouts, fixing ATM
// charlie.addChild(
// new Label(
// "Delta",
// new Font().withColor(0xCCBB44).deriveShadow().deriveBold(),
// "Пре-альфа!"
// )
// );
// charlie.addChild(
// new Label(
// "Epsilon",
// new Font().withColor(0x4444BB).deriveItalic(),
// () -> epsilon.get().concat("\u269b")
// )
// );
// panel.addChild(charlie);
//
//
// charlie.addListener(KeyEvent.class, e -> {
// if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) {
// Localizer localizer = Localizer.getInstance();
// if (localizer.getLanguage().equals("ru-RU")) {
// localizer.setLanguage("en-US");
// } else {
// localizer.setLanguage("ru-RU");
// }
// return true;
// } return false;
// });
// charlie.setFocusable(true);
// charlie.takeFocus();
//
// getRoot().addChild(panel);
// }
}

View File

@ -98,7 +98,7 @@ public class TestContent {
private static void regsiterControls() {
ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000"));
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_G, new int[0], 0)::matches));
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches));
ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") {
@Override
public void apply(Server server, PacketControl packet, Client client) {

View File

@ -0,0 +1,270 @@
package ru.windcorp.progressia.test;
import org.lwjgl.glfw.GLFW;
import glm.Glm;
import glm.mat._3.Mat3;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.util.FloatMathUtils;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData;
public class TestPlayerControls {
private static final TestPlayerControls INSTANCE = new TestPlayerControls();
public static TestPlayerControls getInstance() {
return INSTANCE;
}
private TestPlayerControls() {}
private static final double MODE_SWITCH_MAX_DELAY = 100 * Units.MILLISECONDS;
private static final double MIN_JUMP_DELAY = 200 * Units.MILLISECONDS;
// Horizontal and vertical max control speed when flying
private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority
private static final float FLYING_CONTROL_AUTHORITY = 0.05f;
// Horizontal and vertical max control speed when walking
private static final float WALKING_SPEED = 5.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority
private static final float WALKING_CONTROL_AUTHORITY = 0.1f;
// Vertical velocity instantly add to player when they jump
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND;
private boolean isFlying = true;
private int movementForward = 0;
private int movementRight = 0;
private int movementUp = 0;
private double lastSpacePress = Double.NEGATIVE_INFINITY;
private boolean captureMouse = true;
private boolean useMinecraftGravity = false;
private Runnable updateCallback = null;
public void applyPlayerControls() {
if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) {
return;
}
EntityData player = getPlayer();
Mat3 angMat = Matrices.grab3();
angMat.identity().rotateZ(player.getYaw());
Vec3 movement = Vectors.grab3();
movement.set(movementForward, -movementRight, 0);
if (movementForward != 0 && movementRight != 0) {
movement.normalize();
}
angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped
if (isFlying) {
movement.z = movementUp;
movement.mul(FLYING_SPEED);
movement.sub(player.getVelocity());
movement.mul(FLYING_CONTROL_AUTHORITY);
} else {
movement.mul(WALKING_SPEED);
movement.sub(player.getVelocity());
movement.mul(WALKING_CONTROL_AUTHORITY);
movement.z = 0;
}
player.getVelocity().add(movement);
Matrices.release(angMat);
Vectors.release(movement);
}
public void handleInput(Input input) {
if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) {
return;
}
InputEvent event = input.getEvent();
if (event instanceof KeyEvent) {
if (onKeyEvent((KeyEvent) event)) {
input.consume();
}
} else if (event instanceof CursorMoveEvent) {
onMouseMoved((CursorMoveEvent) event);
input.consume();
}
}
private boolean onKeyEvent(KeyEvent event) {
if (event.isRepeat()) return false;
int multiplier = event.isPress() ? 1 : -1;
switch (event.getKey()) {
case GLFW.GLFW_KEY_W:
movementForward += +1 * multiplier;
break;
case GLFW.GLFW_KEY_S:
movementForward += -1 * multiplier;
break;
case GLFW.GLFW_KEY_A:
movementRight += -1 * multiplier;
break;
case GLFW.GLFW_KEY_D:
movementRight += +1 * multiplier;
break;
case GLFW.GLFW_KEY_SPACE:
handleSpace(multiplier);
break;
case GLFW.GLFW_KEY_LEFT_SHIFT:
handleShift(multiplier);
break;
case GLFW.GLFW_KEY_ESCAPE:
if (!event.isPress()) return false;
handleEscape();
break;
case GLFW.GLFW_KEY_F5:
if (!event.isPress()) return false;
handleCameraMode();
break;
case GLFW.GLFW_KEY_G:
if (!event.isPress()) return false;
handleGravitySwitch();
break;
default:
return false;
}
return true;
}
private void handleSpace(int multiplier) {
boolean isPressed = multiplier > 0;
double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSpacePress;
if (isPressed && timeSinceLastSpacePress < MODE_SWITCH_MAX_DELAY) {
isFlying = !isFlying;
updateGUI();
movementUp = +1;
} else {
if (isFlying) {
movementUp += +1 * multiplier;
} else {
if (isPressed && timeSinceLastSpacePress > MIN_JUMP_DELAY) {
jump();
}
}
}
lastSpacePress = GraphicsInterface.getTime();
}
private void jump() {
getPlayer().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1));
}
private void handleShift(int multiplier) {
if (isFlying) {
movementUp += -1 * multiplier;
}
}
private void handleEscape() {
if (captureMouse) {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
} else {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
}
captureMouse = !captureMouse;
updateGUI();
}
private void handleCameraMode() {
if (ClientState.getInstance().getCamera().hasAnchor()) {
ClientState.getInstance().getCamera().selectNextMode();
updateGUI();
}
}
private void handleGravitySwitch() {
useMinecraftGravity = !useMinecraftGravity;
updateGUI();
}
private void onMouseMoved(CursorMoveEvent event) {
if (!captureMouse) return;
final float yawScale = -0.002f;
final float pitchScale = yawScale;
EntityData player = getPlayer();
normalizeAngles(player.getDirection().add(
(float) (event.getChangeX() * yawScale),
(float) (event.getChangeY() * pitchScale)
));
}
private void normalizeAngles(Vec2 dir) {
// Normalize yaw
dir.x = FloatMathUtils.normalizeAngle(dir.x);
// Clamp pitch
dir.y = Glm.clamp(
dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2
);
}
private EntityData getPlayer() {
return ClientState.getInstance().getLocalPlayer();
}
public void setUpdateCallback(Runnable updateCallback) {
this.updateCallback = updateCallback;
}
private void updateGUI() {
if (this.updateCallback != null) {
this.updateCallback.run();
}
}
public boolean isFlying() {
return isFlying;
}
public boolean isMouseCaptured() {
return captureMouse;
}
public boolean useMinecraftGravity() {
return useMinecraftGravity;
}
}