diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java b/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java index 97758b2..571fc2a 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java +++ b/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java @@ -4,13 +4,12 @@ import nl.andrewl.aos2_client.Client; import nl.andrewl.aos2_client.CommunicationHandler; import nl.andrewl.aos2_client.model.ClientPlayer; import nl.andrewl.aos_core.model.item.BlockItemStack; +import nl.andrewl.aos_core.model.item.GunItemStack; import nl.andrewl.aos_core.model.world.Hit; import nl.andrewl.aos_core.net.client.BlockColorMessage; import nl.andrewl.aos_core.net.client.ChatWrittenMessage; import nl.andrewl.aos_core.net.client.ClientInputState; -import static org.lwjgl.glfw.GLFW.*; - /** * Class which manages the player's input, and sending it to the server. */ @@ -234,6 +233,11 @@ public class InputHandler { } } + public boolean isScopeEnabled() { + return interacting && + client.getMyPlayer().getInventory().getSelectedItemStack() instanceof GunItemStack; + } + public void pickBlock() { var player = client.getMyPlayer(); if (player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) { diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerViewCursorCallback.java b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerViewCursorCallback.java index 17ff234..fc8d9a0 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerViewCursorCallback.java +++ b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerViewCursorCallback.java @@ -48,9 +48,11 @@ public class PlayerViewCursorCallback implements GLFWCursorPosCallbackI { float dy = y - lastMouseCursorY; lastMouseCursorX = x; lastMouseCursorY = y; + float trueSensitivity = config.mouseSensitivity; + if (client.getInputHandler().isScopeEnabled()) trueSensitivity *= 0.1f; client.getMyPlayer().setOrientation( - client.getMyPlayer().getOrientation().x - dx * config.mouseSensitivity, - client.getMyPlayer().getOrientation().y - dy * config.mouseSensitivity + client.getMyPlayer().getOrientation().x - dx * trueSensitivity, + client.getMyPlayer().getOrientation().y - dy * trueSensitivity ); camera.setOrientationToPlayer(client.getMyPlayer()); long now = System.currentTimeMillis(); diff --git a/client/src/main/java/nl/andrewl/aos2_client/model/ClientPlayer.java b/client/src/main/java/nl/andrewl/aos2_client/model/ClientPlayer.java index e3ed59c..3550f8b 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/model/ClientPlayer.java +++ b/client/src/main/java/nl/andrewl/aos2_client/model/ClientPlayer.java @@ -1,6 +1,7 @@ package nl.andrewl.aos2_client.model; import nl.andrewl.aos2_client.Camera; +import nl.andrewl.aos2_client.control.InputHandler; import nl.andrewl.aos_core.model.Player; import nl.andrewl.aos_core.model.item.Inventory; import org.joml.Matrix3f; @@ -42,12 +43,16 @@ public class ClientPlayer extends Player { this.health = health; } - public void updateHeldItemTransform(Camera cam) { + public void updateHeldItemTransform(Camera cam, InputHandler inputHandler) { heldItemTransform.identity() .translate(cam.getPosition()) .rotate((float) (cam.getOrientation().x + Math.PI), Camera.UP) - .rotate(-cam.getOrientation().y + (float) Math.PI / 2, Camera.RIGHT) - .translate(-0.35f, -0.4f, 0.5f); + .rotate(-cam.getOrientation().y + (float) Math.PI / 2, Camera.RIGHT); + if (inputHandler.isScopeEnabled()) { + heldItemTransform.translate(0, -0.12f, 0); + } else { + heldItemTransform.translate(-0.35f, -0.4f, 0.5f); + } heldItemTransform.get(heldItemTransformData); heldItemTransform.normal(heldItemNormalTransform); diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java index 74d3a0a..4579105 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java @@ -56,6 +56,7 @@ public class GameRenderer { private final int screenHeight; private final Matrix4f perspectiveTransform; + private final float[] perspectiveTransformData = new float[16]; public GameRenderer(Client client, InputHandler inputHandler) { this.config = client.getConfig().display; @@ -155,10 +156,9 @@ public class GameRenderer { fovRad = 0.01f; } perspectiveTransform.setPerspective(fovRad, getAspectRatio(), Z_NEAR, Z_FAR); - float[] data = new float[16]; - perspectiveTransform.get(data); - if (chunkRenderer != null) chunkRenderer.setPerspective(data); - if (modelRenderer != null) modelRenderer.setPerspective(data); + perspectiveTransform.get(perspectiveTransformData); + if (chunkRenderer != null) chunkRenderer.setPerspective(perspectiveTransformData); + if (modelRenderer != null) modelRenderer.setPerspective(perspectiveTransformData); } public boolean windowShouldClose() { @@ -175,13 +175,19 @@ public class GameRenderer { public void draw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - chunkRenderer.draw(camera, client.getWorld().getChunkMeshesToDraw()); ClientPlayer myPlayer = client.getMyPlayer(); + if (client.getInputHandler().isScopeEnabled()) { + updatePerspective(15); + } else { + updatePerspective(config.fov); + } + myPlayer.updateHeldItemTransform(camera, client.getInputHandler()); + + chunkRenderer.draw(camera, client.getWorld().getChunkMeshesToDraw()); // Draw models. Use one texture at a time for efficiency. modelRenderer.start(camera.getViewTransformData()); - myPlayer.updateHeldItemTransform(camera); playerModel.bind(); for (var player : client.getPlayers().values()) { diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/TransformData.java b/client/src/main/java/nl/andrewl/aos2_client/render/TransformData.java new file mode 100644 index 0000000..a4e611f --- /dev/null +++ b/client/src/main/java/nl/andrewl/aos2_client/render/TransformData.java @@ -0,0 +1,6 @@ +package nl.andrewl.aos2_client.render; + +public record TransformData( + float[] tx, + float[] norm +) {} diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java index 68d6cdf..f369ba7 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java @@ -222,10 +222,12 @@ public class GuiRenderer { nvgBeginFrame(vgId, width, height, width / height); nvgSave(vgId); - drawCrosshair(width, height); - drawChat(width, height, client); - drawHealthBar(width, height, client.getMyPlayer()); - drawHeldItemStackInfo(width, height, client.getMyPlayer()); + drawCrosshair(width, height, client.getInputHandler().isScopeEnabled()); + if (!client.getInputHandler().isScopeEnabled()) { + drawChat(width, height, client); + drawHealthBar(width, height, client.getMyPlayer()); + drawHeldItemStackInfo(width, height, client.getMyPlayer()); + } if (client.getInputHandler().isDebugEnabled()) { drawDebugInfo(width, height, client); } @@ -253,16 +255,18 @@ public class GuiRenderer { shaderProgram.free(); } - private void drawCrosshair(float w, float h) { + private void drawCrosshair(float w, float h, boolean scopeEnabled) { float cx = w / 2f; float cy = h / 2f; + float size = 20f; + if (scopeEnabled) size = 3f; nvgStrokeColor(vgId, GuiUtils.rgba(1, 1, 1, 0.25f, colorA)); nvgBeginPath(vgId); - nvgMoveTo(vgId, cx - 10, cy); - nvgLineTo(vgId, cx + 10, cy); - nvgMoveTo(vgId, cx, cy - 10); - nvgLineTo(vgId, cx, cy + 10); + nvgMoveTo(vgId, cx - size / 2, cy); + nvgLineTo(vgId, cx + size / 2, cy); + nvgMoveTo(vgId, cx, cy - size / 2); + nvgLineTo(vgId, cx, cy + size / 2); nvgStroke(vgId); } diff --git a/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java b/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java index e1e0242..6f5f2c2 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java +++ b/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java @@ -54,14 +54,22 @@ public class ProjectileManager { if (nextProjectileId == Integer.MAX_VALUE) nextProjectileId = 1; pos.set(0); - bulletTransform.identity() - .translate(player.getEyePosition()) - .rotate(player.getOrientation().x + (float) Math.PI, Directions.UPf) - .translate(-0.35f, -0.4f, 0.35f); - bulletTransform.transformPosition(pos); - direction.set(player.getViewVector()).normalize(); float accuracy = gun.getAccuracy(); + + if (player.getActionManager().isScopeEnabled()) { + bulletTransform.identity() + .translate(player.getEyePosition()) + .rotate(player.getOrientation().x + (float) Math.PI, Directions.UPf); + accuracy += (1f - accuracy) / 2f; + } else { + bulletTransform.identity() + .translate(player.getEyePosition()) + .rotate(player.getOrientation().x + (float) Math.PI, Directions.UPf) + .translate(-0.35f, -0.4f, 0.35f); + } + + bulletTransform.transformPosition(pos); accuracy -= server.getConfig().actions.movementAccuracyDecreaseFactor * player.getVelocity().length(); float perturbationFactor = (1 - accuracy) / 8; direction.x += rand.nextGaussian(0, perturbationFactor); diff --git a/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java b/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java index cd4628b..a1800ff 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java +++ b/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java @@ -56,6 +56,11 @@ public class PlayerActionManager { return input.setLastInputState(lastInputState); } + public boolean isScopeEnabled() { + return input.interacting() && + player.getInventory().getSelectedItemStack() instanceof GunItemStack; + } + public boolean isUpdated() { return updated; } @@ -113,6 +118,7 @@ public class PlayerActionManager { server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory())); // Apply recoil! float recoilFactor = 10f; // Maximum number of degrees to recoil. + if (isScopeEnabled()) recoilFactor *= 0.1f; float recoil = recoilFactor * gun.getRecoil() + (float) ThreadLocalRandom.current().nextGaussian(0, 0.01); server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ClientRecoilMessage(0, Math.toRadians(recoil))); // Play sound! @@ -249,7 +255,7 @@ public class PlayerActionManager { acceleration.rotateAxis(orientation.x, 0, 1, 0); acceleration.mul(config.movementAcceleration); horizontalVelocity.add(acceleration); - final float maxSpeed; + float maxSpeed; if (input.crouching()) { maxSpeed = config.crouchingSpeed; } else if (input.sprinting()) { @@ -257,6 +263,8 @@ public class PlayerActionManager { } else { maxSpeed = config.walkingSpeed; } + // If scoping, then force limit to crouching speed. + if (isScopeEnabled()) maxSpeed = config.crouchingSpeed; if (horizontalVelocity.length() > maxSpeed) { horizontalVelocity.normalize(maxSpeed); }