diff --git a/client/src/main/java/nl/andrewl/aos2_client/Client.java b/client/src/main/java/nl/andrewl/aos2_client/Client.java index 1cf99f5..8c09b00 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/Client.java +++ b/client/src/main/java/nl/andrewl/aos2_client/Client.java @@ -1,10 +1,7 @@ package nl.andrewl.aos2_client; import nl.andrewl.aos2_client.config.ClientConfig; -import nl.andrewl.aos2_client.control.InputHandler; -import nl.andrewl.aos2_client.control.PlayerInputKeyCallback; -import nl.andrewl.aos2_client.control.PlayerInputMouseClickCallback; -import nl.andrewl.aos2_client.control.PlayerViewCursorCallback; +import nl.andrewl.aos2_client.control.*; import nl.andrewl.aos2_client.model.ClientPlayer; import nl.andrewl.aos2_client.model.OtherPlayer; import nl.andrewl.aos2_client.render.GameRenderer; @@ -84,7 +81,8 @@ public class Client implements Runnable { gameRenderer.setupWindow( new PlayerViewCursorCallback(config.input, this, gameRenderer.getCamera(), communicationHandler), new PlayerInputKeyCallback(inputHandler), - new PlayerInputMouseClickCallback(inputHandler) + new PlayerInputMouseClickCallback(inputHandler), + new PlayerInputMouseScrollCallback(this, communicationHandler) ); soundManager = new SoundManager(); log.debug("Sound system initialized."); diff --git a/client/src/main/java/nl/andrewl/aos2_client/config/ClientConfig.java b/client/src/main/java/nl/andrewl/aos2_client/config/ClientConfig.java index a8e62ff..b4021ee 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/config/ClientConfig.java +++ b/client/src/main/java/nl/andrewl/aos2_client/config/ClientConfig.java @@ -13,7 +13,7 @@ public class ClientConfig { public static class DisplayConfig { public boolean fullscreen = false; - public boolean captureCursor = true; + public boolean captureCursor = false; public float fov = 70; } } 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 b3d2fa8..b4a22d2 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 @@ -1,8 +1,12 @@ package nl.andrewl.aos2_client.control; -import nl.andrewl.aos2_client.CommunicationHandler; -import nl.andrewl.aos_core.net.client.ClientInputState; 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.world.Hit; +import nl.andrewl.aos_core.net.client.BlockColorMessage; +import nl.andrewl.aos_core.net.client.ClientInputState; import static org.lwjgl.glfw.GLFW.*; @@ -46,5 +50,19 @@ public class InputHandler { comm.sendDatagramPacket(currentInputState); lastInputState = currentInputState; } + + ClientPlayer player = client.getMyPlayer(); + + // Check for "pick block" functionality. + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS && player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) { + Hit hit = client.getWorld().getLookingAtPos(player.getEyePosition(), player.getViewVector(), 50); + if (hit != null) { + byte selectedBlock = client.getWorld().getBlockAt(hit.pos().x, hit.pos().y, hit.pos().z); + if (selectedBlock > 0) { + stack.setSelectedValue(selectedBlock); + comm.sendDatagramPacket(new BlockColorMessage(player.getId(), selectedBlock)); + } + } + } } } diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseScrollCallback.java b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseScrollCallback.java new file mode 100644 index 0000000..6890915 --- /dev/null +++ b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseScrollCallback.java @@ -0,0 +1,30 @@ +package nl.andrewl.aos2_client.control; + +import nl.andrewl.aos2_client.Client; +import nl.andrewl.aos2_client.CommunicationHandler; +import nl.andrewl.aos_core.model.item.BlockItemStack; +import nl.andrewl.aos_core.net.client.BlockColorMessage; +import org.lwjgl.glfw.GLFWScrollCallbackI; + +public class PlayerInputMouseScrollCallback implements GLFWScrollCallbackI { + private final Client client; + private final CommunicationHandler comm; + + public PlayerInputMouseScrollCallback(Client client, CommunicationHandler comm) { + this.client = client; + this.comm = comm; + } + + @Override + public void invoke(long window, double xoffset, double yoffset) { + System.out.println(yoffset); + if (client.getMyPlayer().getInventory().getSelectedItemStack() instanceof BlockItemStack stack) { + if (yoffset < 0) { + stack.setSelectedValue((byte) (stack.getSelectedValue() - 1)); + } else if (yoffset > 0) { + stack.setSelectedValue((byte) (stack.getSelectedValue() + 1)); + } + comm.sendDatagramPacket(new BlockColorMessage(client.getMyPlayer().getId(), stack.getSelectedValue())); + } + } +} 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 46cee6a..31b4683 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 @@ -77,7 +77,8 @@ public class GameRenderer { public void setupWindow( GLFWCursorPosCallbackI viewCursorCallback, GLFWKeyCallbackI inputKeyCallback, - GLFWMouseButtonCallbackI mouseButtonCallback + GLFWMouseButtonCallbackI mouseButtonCallback, + GLFWScrollCallbackI scrollCallback ) { GLFWErrorCallback.createPrint(System.err).set(); if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW."); @@ -105,6 +106,7 @@ public class GameRenderer { glfwSetKeyCallback(windowHandle, inputKeyCallback); glfwSetCursorPosCallback(windowHandle, viewCursorCallback); glfwSetMouseButtonCallback(windowHandle, mouseButtonCallback); + glfwSetScrollCallback(windowHandle, scrollCallback); if (config.captureCursor) { glfwSetInputMode(windowHandle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } diff --git a/core/src/main/java/nl/andrewl/aos_core/Net.java b/core/src/main/java/nl/andrewl/aos_core/Net.java index d2c2414..2c5c0b4 100644 --- a/core/src/main/java/nl/andrewl/aos_core/Net.java +++ b/core/src/main/java/nl/andrewl/aos_core/Net.java @@ -45,6 +45,7 @@ public final class Net { serializer.registerType(16, SoundMessage.class); serializer.registerType(17, ProjectileMessage.class); serializer.registerType(18, ClientHealthMessage.class); + serializer.registerType(19, BlockColorMessage.class); } public static ExtendedDataInputStream getInputStream(InputStream in) { diff --git a/core/src/main/java/nl/andrewl/aos_core/net/client/BlockColorMessage.java b/core/src/main/java/nl/andrewl/aos_core/net/client/BlockColorMessage.java new file mode 100644 index 0000000..01f9d15 --- /dev/null +++ b/core/src/main/java/nl/andrewl/aos_core/net/client/BlockColorMessage.java @@ -0,0 +1,13 @@ +package nl.andrewl.aos_core.net.client; + +import nl.andrewl.record_net.Message; + +/** + * A message that's sent when a client is holding a block item stack, and + * selects a different color. It's also sent to other clients to tell them that + * the player with the given id has selected a different block color. + */ +public record BlockColorMessage( + int clientId, + byte block +) implements Message {} diff --git a/design/models/flag.blend b/design/models/flag.blend new file mode 100644 index 0000000..b700cce Binary files /dev/null and b/design/models/flag.blend differ diff --git a/design/models/flag.blend1 b/design/models/flag.blend1 new file mode 100644 index 0000000..7899cf1 Binary files /dev/null and b/design/models/flag.blend1 differ diff --git a/design/models/smg.blend b/design/models/smg.blend new file mode 100644 index 0000000..bd21c10 Binary files /dev/null and b/design/models/smg.blend differ 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 6c19262..5033bec 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java +++ b/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java @@ -5,6 +5,7 @@ import nl.andrewl.aos2_server.model.ServerProjectile; import nl.andrewl.aos_core.Directions; import nl.andrewl.aos_core.model.Player; import nl.andrewl.aos_core.model.Projectile; +import nl.andrewl.aos_core.model.item.Gun; import nl.andrewl.aos_core.model.world.Hit; import nl.andrewl.aos_core.net.client.ClientHealthMessage; import nl.andrewl.aos_core.net.client.SoundMessage; @@ -12,10 +13,7 @@ import nl.andrewl.aos_core.net.world.ChunkUpdateMessage; import org.joml.Matrix4f; import org.joml.Vector3f; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; /** @@ -36,18 +34,30 @@ public class ProjectileManager { this.removalQueue = new LinkedList<>(); } - public void spawnBullet(ServerPlayer player) { + public void spawnBullet(ServerPlayer player, Gun gun) { int id = nextProjectileId++; if (nextProjectileId == Integer.MAX_VALUE) nextProjectileId = 1; + Random rand = ThreadLocalRandom.current(); + Vector3f pos = new Vector3f(); Matrix4f bulletTransform = new Matrix4f() .translate(player.getEyePosition()) .rotate(player.getOrientation().x + (float) Math.PI, Directions.UPf) .translate(-0.35f, -0.4f, 0.35f); bulletTransform.transformPosition(pos); - Vector3f vel = new Vector3f(player.getViewVector()).normalize() + + Vector3f direction = new Vector3f(player.getViewVector()).normalize(); + float accuracy = gun.getAccuracy(); + accuracy -= server.getConfig().actions.movementAccuracyDecreaseFactor * player.getVelocity().length(); + float perturbationFactor = (1 - accuracy) / 8; + direction.x += rand.nextGaussian(0, perturbationFactor); + direction.y += rand.nextGaussian(0, perturbationFactor); + direction.z += rand.nextGaussian(0, perturbationFactor); + + Vector3f vel = new Vector3f(direction).normalize() .mul(200 * MOVEMENT_FACTOR) .add(player.getVelocity()); + ServerProjectile bullet = new ServerProjectile(id, pos, vel, Projectile.Type.BULLET, player); projectiles.put(bullet.getId(), bullet); server.getPlayerManager().broadcastUdpMessage(bullet.toMessage(false)); diff --git a/server/src/main/java/nl/andrewl/aos2_server/Server.java b/server/src/main/java/nl/andrewl/aos2_server/Server.java index 69b75b7..a692990 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/Server.java +++ b/server/src/main/java/nl/andrewl/aos2_server/Server.java @@ -5,9 +5,11 @@ import nl.andrewl.aos2_server.config.ServerConfig; import nl.andrewl.aos2_server.logic.WorldUpdater; import nl.andrewl.aos2_server.model.ServerPlayer; import nl.andrewl.aos_core.config.Config; +import nl.andrewl.aos_core.model.item.BlockItemStack; import nl.andrewl.aos_core.model.world.World; import nl.andrewl.aos_core.model.world.Worlds; import nl.andrewl.aos_core.net.UdpReceiver; +import nl.andrewl.aos_core.net.client.BlockColorMessage; import nl.andrewl.aos_core.net.client.ClientInputState; import nl.andrewl.aos_core.net.client.ClientOrientationState; import nl.andrewl.aos_core.net.connect.DatagramInit; @@ -103,6 +105,12 @@ public class Server implements Runnable { player.setOrientation(orientationState.x(), orientationState.y()); playerManager.broadcastUdpMessageToAllBut(player.getUpdateMessage(now), player); } + } else if (msg instanceof BlockColorMessage blockColorMessage) { + ServerPlayer player = playerManager.getPlayer(blockColorMessage.clientId()); + if (player != null && player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) { + stack.setSelectedValue(blockColorMessage.block()); + playerManager.broadcastUdpMessageToAllBut(blockColorMessage, player); + } } } diff --git a/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java b/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java index 9d873f1..afe89aa 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java +++ b/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java @@ -18,9 +18,12 @@ public class ServerConfig { } public static class ActionsConfig { - public float blockRemoveCooldown = 0.25f; + public float blockBreakCooldown = 0.25f; public float blockPlaceCooldown = 0.1f; + public float blockBreakReach = 5; + public float blockPlaceReach = 5; public float resupplyCooldown = 30; public float resupplyRadius = 3; + public float movementAccuracyDecreaseFactor = 0.01f; } } 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 a166509..6849c83 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 @@ -111,7 +111,7 @@ public class PlayerActionManager { now - gunLastShotAt > gun.getShotCooldownTime() * 1000 && (gun.isAutomatic() || !gunNeedsReCock) ) { - server.getProjectileManager().spawnBullet(player); + server.getProjectileManager().spawnBullet(player, gun); g.setBulletCount(g.getBulletCount() - 1); gunLastShotAt = now; if (!gun.isAutomatic()) { @@ -159,9 +159,9 @@ public class PlayerActionManager { if ( lastInputState.hitting() && stack.getAmount() < stack.getType().getMaxAmount() && - now - lastBlockRemovedAt > server.getConfig().actions.blockRemoveCooldown * 1000 + now - lastBlockRemovedAt > server.getConfig().actions.blockBreakCooldown * 1000 ) { - var hit = world.getLookingAtPos(player.getEyePosition(), player.getViewVector(), 10); + var hit = world.getLookingAtPos(player.getEyePosition(), player.getViewVector(), server.getConfig().actions.blockBreakReach); if (hit != null) { world.setBlockAt(hit.pos().x, hit.pos().y, hit.pos().z, (byte) 0); lastBlockRemovedAt = now; @@ -177,7 +177,7 @@ public class PlayerActionManager { stack.getAmount() > 0 && now - lastBlockPlacedAt > server.getConfig().actions.blockPlaceCooldown * 1000 ) { - var hit = world.getLookingAtPos(player.getEyePosition(), player.getViewVector(), 10); + var hit = world.getLookingAtPos(player.getEyePosition(), player.getViewVector(), server.getConfig().actions.blockPlaceReach); if (hit != null) { Vector3i placePos = new Vector3i(hit.pos()); placePos.add(hit.norm());