diff --git a/core/src/main/java/nl/andrewl/aos_core/model/PlayerMode.java b/core/src/main/java/nl/andrewl/aos_core/model/PlayerMode.java new file mode 100644 index 0000000..46bc71c --- /dev/null +++ b/core/src/main/java/nl/andrewl/aos_core/model/PlayerMode.java @@ -0,0 +1,27 @@ +package nl.andrewl.aos_core.model; + +/** + * Represents the different modes that a player can be in. + * + */ +public enum PlayerMode { + NORMAL, + CREATIVE, + SPECTATOR +} 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 7b95d6f..6b59bf5 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java +++ b/server/src/main/java/nl/andrewl/aos2_server/ProjectileManager.java @@ -13,6 +13,7 @@ import nl.andrewl.aos_core.net.client.SoundMessage; import nl.andrewl.aos_core.net.world.ChunkUpdateMessage; import org.joml.Matrix4f; import org.joml.Vector3f; +import org.joml.Vector3fc; import org.joml.Vector3i; import java.util.*; @@ -103,10 +104,9 @@ public class ProjectileManager { // Check for if the bullet will move close enough to a player to hit them. Vector3f movement = new Vector3f(projectile.getVelocity()).mul(dt); + Vector3f direction = new Vector3f(projectile.getVelocity()).normalize(); // Check first to see if we'll hit a player this tick. - Vector3f testPos = new Vector3f(); - Vector3f testMovement = new Vector3f(movement).normalize(0.1f); Vector3f playerHit = null; ServerPlayer hitPlayer = null; int playerHitType = -1; @@ -121,23 +121,25 @@ public class ProjectileManager { ) continue; Vector3f headPos = player.getEyePosition(); - Vector3f bodyPos = new Vector3f(player.getPosition()); - bodyPos.y += 1.0f; + float headRadius = Player.RADIUS; + Vector3f bodyPos = new Vector3f(player.getPosition().x, player.getPosition().y + 1, player.getPosition().z); + float bodyRadius = Player.RADIUS; - // Do a really shitty collision detection... check in 10cm increments if we're close to the player. - // TODO: Come up with a better collision system. - testPos.set(projectile.getPosition()); - while (testPos.distanceSquared(projectile.getPosition()) < movement.lengthSquared() && playerHit == null) { - if (testPos.distanceSquared(headPos) < Player.RADIUS * Player.RADIUS) { - playerHitType = 1; - playerHit = new Vector3f(testPos); - hitPlayer = player; - } else if (testPos.distanceSquared(bodyPos) < Player.RADIUS * Player.RADIUS) { - playerHitType = 2; - playerHit = new Vector3f(testPos); - hitPlayer = player; - } - testPos.add(testMovement); + Vector3f headIntersect = checkSphereIntersection(projectile.getPosition(), direction, headPos, headRadius); + if (headIntersect != null) { + playerHitType = 1; + playerHit = headIntersect; + hitPlayer = player; + break; + } + + // Check body intersection. + Vector3f bodyIntersect = checkSphereIntersection(projectile.getPosition(), direction, bodyPos, bodyRadius); + if (bodyIntersect != null) { + playerHitType = 2; + playerHit = bodyIntersect; + hitPlayer = player; + break; } } @@ -168,6 +170,37 @@ public class ProjectileManager { } } + /** + * Checks for and returns the point at which a ray with origin p and + * normalized direction d intersects with a sphere whose center is c, and + * whose radius is r. + * @param p The ray origin. + * @param d The ray's normalized direction. + * @param c The sphere's center. + * @param r The radius of the sphere. + * @return The point of intersection, if one exists. + */ + private Vector3f checkSphereIntersection(Vector3fc p, Vector3fc d, Vector3fc c, float r) { + Vector3f vpc = new Vector3f(c).sub(p); + if (vpc.dot(d) >= 0) {// Only check for intersections ahead of us. + Vector3f puv = new Vector3f(d).mul(d.dot(vpc) / d.length()); + Vector3f pc = new Vector3f(p).add(puv); + Vector3f diff = new Vector3f(c).sub(pc); + if (diff.length() <= r) { + Vector3f pcToC = new Vector3f(pc).sub(c); + float dist = (float) Math.sqrt(r * r - pcToC.lengthSquared()); + float distanceToFirstIntersection; + if (vpc.length() > r) { + distanceToFirstIntersection = pcToC.length() - dist; + } else { + distanceToFirstIntersection = pcToC.length() + dist; + } + return new Vector3f(d).mul(distanceToFirstIntersection).add(p); + } + } + return null; + } + private void handleProjectileBlockHit(Hit hit, ServerProjectile projectile, long now) { if (!server.getTeamManager().isProtected(hit.pos())) { Gun gun = (Gun) projectile.getSourceItem(); 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 18010e6..e683f3a 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 @@ -102,7 +102,7 @@ public class PlayerActionManager { } tickMovement(dt, server, world, server.getConfig().physics); - input.reset(); + input.reset(); // Reset our input state after processing this tick's player input. } private void tickGunAction(long now, Server server, GunItemStack g) { @@ -115,58 +115,17 @@ public class PlayerActionManager { (gun.isAutomatic() || !gunNeedsReCock) && !server.getTeamManager().isProtected(player) // Don't allow players to shoot from within their own team's protected zones. ) { - server.getProjectileManager().spawnBullets(player, gun); - g.setBulletCount(g.getBulletCount() - 1); - gunLastShotAt = now; - if (!gun.isAutomatic()) { - gunNeedsReCock = true; - } - 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! - String shotSound = null; - if (gun instanceof Rifle) { - shotSound = "shot_m1-garand_1"; - } else if (gun instanceof Ak47) { - shotSound = "shot_ak-47_1"; - } else if (gun instanceof Winchester) { - shotSound = "shot_winchester_1"; - } - Vector3f soundLocation = new Vector3f(player.getPosition()); - soundLocation.y += 1.4f; - soundLocation.add(player.getViewVector()); - server.getPlayerManager().broadcastUdpMessage(new SoundMessage(shotSound, 1, soundLocation, player.getVelocity())); + shootGun(now, server, gun, g); } - if (// Check to see if the player is reloading. - input.reloading() && - !gunReloading && - g.getClipCount() > 0 - ) { - g.setClipCount(g.getClipCount() - 1); - gunReloadingStartedAt = now; - gunReloading = true; - reloadingItemStack = g; - server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory())); - server.getPlayerManager().broadcastUdpMessage(new SoundMessage("reload", 1, player.getPosition(), player.getVelocity())); + // Check to see if the player is reloading. + if (input.reloading() && !gunReloading && g.getClipCount() > 0) { + reloadGun(now, server, g); } - if (// Check to see if reloading is done. - gunReloading && - reloadingItemStack != null && - now - gunReloadingStartedAt > gun.getReloadTime() * 1000 - ) { - reloadingItemStack.setBulletCount(gun.getMaxBulletCount()); - int idx = player.getInventory().getIndex(reloadingItemStack); - if (idx != -1) { - server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(idx, reloadingItemStack)); - } - gunReloading = false; - reloadingItemStack = null; + // Check to see if reloading is done. + if (gunReloading && reloadingItemStack != null && now - gunReloadingStartedAt > gun.getReloadTime() * 1000) { + reloadingComplete(server, gun); } // Check to see if the player released the trigger, for non-automatic weapons. @@ -334,26 +293,6 @@ public class PlayerActionManager { return points; } - private boolean isSpaceOccupied(Vector3i pos) { - var playerPos = player.getPosition(); - float playerBodyMinZ = playerPos.z - RADIUS; - float playerBodyMaxZ = playerPos.z + RADIUS; - float playerBodyMinX = playerPos.x - RADIUS; - float playerBodyMaxX = playerPos.x + RADIUS; - float playerBodyMinY = playerPos.y; - float playerBodyMaxY = playerPos.y + player.getCurrentHeight(); - - // Compute the bounds of all blocks the player is intersecting with. - int minX = (int) Math.floor(playerBodyMinX); - int minZ = (int) Math.floor(playerBodyMinZ); - int minY = (int) Math.floor(playerBodyMinY); - int maxX = (int) Math.floor(playerBodyMaxX); - int maxZ = (int) Math.floor(playerBodyMaxZ); - int maxY = (int) Math.floor(playerBodyMaxY); - - return pos.x >= minX && pos.x <= maxX && pos.y >= minY && pos.y <= maxY && pos.z >= minZ && pos.z <= maxZ; - } - private void checkBlockCollisions(Vector3f movement, Server server, World world) { var position = player.getPosition(); var velocity = player.getVelocity(); @@ -501,4 +440,51 @@ public class PlayerActionManager { } } } + + private void shootGun(long now, Server server, Gun gun, GunItemStack g) { + server.getProjectileManager().spawnBullets(player, gun); + g.setBulletCount(g.getBulletCount() - 1); + gunLastShotAt = now; + if (!gun.isAutomatic()) { + gunNeedsReCock = true; + } + 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! + String shotSound = null; + if (gun instanceof Rifle) { + shotSound = "shot_m1-garand_1"; + } else if (gun instanceof Ak47) { + shotSound = "shot_ak-47_1"; + } else if (gun instanceof Winchester) { + shotSound = "shot_winchester_1"; + } + Vector3f soundLocation = new Vector3f(player.getPosition()); + soundLocation.y += 1.4f; + soundLocation.add(player.getViewVector()); + server.getPlayerManager().broadcastUdpMessage(new SoundMessage(shotSound, 1, soundLocation, player.getVelocity())); + } + + private void reloadGun(long now, Server server, GunItemStack g) { + g.setClipCount(g.getClipCount() - 1); + gunReloadingStartedAt = now; + gunReloading = true; + reloadingItemStack = g; + server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory())); + server.getPlayerManager().broadcastUdpMessage(new SoundMessage("reload", 1, player.getPosition(), player.getVelocity())); + } + + private void reloadingComplete(Server server, Gun gun) { + reloadingItemStack.setBulletCount(gun.getMaxBulletCount()); + int idx = player.getInventory().getIndex(reloadingItemStack); + if (idx != -1) { + server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(idx, reloadingItemStack)); + } + gunReloading = false; + reloadingItemStack = null; + } }