diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java new file mode 100644 index 00000000..561af19c --- /dev/null +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java @@ -0,0 +1,166 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.simulator; + +import org.bukkit.Axis; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Vector; +import org.bukkit.util.VoxelShape; + +import java.util.ArrayList; +import java.util.List; + +public class AxisMovementLimiter { + + private Axis axis; + private double movement; + + private double minX; + private double maxX; + private double minY; + private double maxY; + private double minZ; + private double maxZ; + + public AxisMovementLimiter(double x, double y, double z, Axis axis, double movement) { + this.axis = axis; + this.movement = movement; + + // Calculate the min and max values for the movement + minX = x; + maxX = x + 0.98; + + minY = y; + maxY = y + 0.98; + + minZ = z; + maxZ = z + 0.98; + + switch (axis) { + case X: + if (movement < 0) { + minX += movement; + } else if (movement > 0) { + maxX += movement; + } + break; + case Y: + if (movement < 0) { + minY += movement; + } else if (movement > 0) { + maxY += movement; + } + break; + case Z: + if (movement < 0) { + minZ += movement; + } else if (movement > 0) { + maxZ += movement; + } + break; + } + } + + private List possibleCollisions() { + int minX = TNT.floor(this.minX); + int maxX = TNT.floor(this.maxX); + int minY = TNT.floor(this.minY) - 1; + int maxY = TNT.floor(this.maxY); + int minZ = TNT.floor(this.minZ); + int maxZ = TNT.floor(this.maxZ); + + List vectors = new ArrayList<>(); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + vectors.add(new Vector(x, y, z)); + } + } + } + return vectors; + } + + public double run() { + BoundingBox movementBoundingBox = new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ); + List vectors = possibleCollisions(); + + Double collision = null; + for (Vector vector : vectors) { + VoxelShape voxelShape = Simulator19.getVoxelShape(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(vector); + boolean collides = boundingBox.overlaps(movementBoundingBox); + if (!collides) continue; + + double value; + switch (axis) { + case X: + if (movement < 0) { + value = boundingBox.getMaxX(); + } else { + value = boundingBox.getMinX() - 0.98; + } + break; + case Y: + if (movement < 0) { + value = boundingBox.getMaxY(); + } else { + value = boundingBox.getMinY() - 0.98; + } + break; + case Z: + if (movement < 0) { + value = boundingBox.getMaxZ(); + } else { + value = boundingBox.getMinZ() - 0.98; + } + break; + default: + throw new IllegalStateException("Unexpected value: " + axis); + } + + if (collision == null) { + collision = value; + } else { + if (movement < 0) { + collision = Math.max(collision, value); + } else { + collision = Math.min(collision, value); + } + } + } + } + + if (collision == null) { + return movement; + } else { + switch (axis) { + case X: + return movement + (collision - minX); + case Y: + return movement + (collision - minY); + case Z: + return movement + (collision - minZ); + default: + throw new IllegalStateException("Unexpected value: " + axis); + } + } + } +} diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java index 6bad4658..96a8d382 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java @@ -23,8 +23,12 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.data.BlockData; +import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; +import org.bukkit.util.VoxelShape; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -34,6 +38,7 @@ public class Simulator19 implements Simulator { private static final Map BLOCK_TYPES_MAP = new HashMap<>(); private static final Map BLOCK_DATA_MAP = new HashMap<>(); + private static final Map COLLISION_DATA_MAP = new HashMap<>(); @Override public synchronized void run() { @@ -45,6 +50,7 @@ public class Simulator19 implements Simulator { BLOCK_TYPES_MAP.clear(); BLOCK_DATA_MAP.clear(); + COLLISION_DATA_MAP.clear(); } public static Material getBlockType(int x, int y, int z) { @@ -57,9 +63,25 @@ public class Simulator19 implements Simulator { return BLOCK_DATA_MAP.computeIfAbsent(vector, v -> WORLD.getBlockAt(v.getBlockX(), v.getBlockY(), v.getBlockZ()).getBlockData()); } - public static void setBlock(int x, int y, int z, Material material) { + public static VoxelShape getVoxelShape(int x, int y, int z) { Vector vector = new Vector(x, y, z); - BLOCK_TYPES_MAP.put(vector, material); - BLOCK_DATA_MAP.put(vector, material.createBlockData()); + return COLLISION_DATA_MAP.computeIfAbsent(vector, v -> WORLD.getBlockAt(v.getBlockX(), v.getBlockY(), v.getBlockZ()).getCollisionShape()); + } + + public static void clearBlock(int x, int y, int z) { + Vector vector = new Vector(x, y, z); + BLOCK_TYPES_MAP.put(vector, Material.AIR); + BLOCK_DATA_MAP.put(vector, Material.AIR.createBlockData()); + COLLISION_DATA_MAP.put(vector, new VoxelShape() { + @Override + public Collection getBoundingBoxes() { + return Collections.emptyList(); + } + + @Override + public boolean overlaps(BoundingBox other) { + return false; + } + }); } } diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java index 9c67507f..bbc9c137 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java @@ -21,23 +21,16 @@ package de.steamwar.bausystem.features.simulator; import lombok.Getter; import lombok.Setter; -import net.minecraft.core.EnumDirection; import net.minecraft.world.level.RayTrace; -import net.minecraft.world.phys.AxisAlignedBB; import net.minecraft.world.phys.MovingObjectPosition; import net.minecraft.world.phys.Vec3D; -import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraft.world.phys.shapes.VoxelShapes; +import org.bukkit.Axis; import org.bukkit.Material; -import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.*; import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity; -import org.bukkit.entity.Entity; import org.bukkit.util.Vector; -import java.util.List; import java.util.Random; @Getter @@ -95,7 +88,7 @@ public class TNT { } private void move(Vector movement) { - if (movement.lengthSquared() > 1.0E-7) { + if (movementMultiplier.lengthSquared() > 1.0E-7) { movement.multiply(movementMultiplier); movementMultiplier = new Vector(0, 0, 0); vx = 0; @@ -104,19 +97,20 @@ public class TNT { } Vector vec3d = adjustMovementForCollisions(movement); + // System.out.println(movement + " " + vec3d); double lengthSquared = vec3d.lengthSquared(); if (lengthSquared > 1.0E-7) { if (fallDistance != 0.0F) { // TODO: This could be wrong - MovingObjectPosition movingObjectPosition = ((CraftWorld) Simulator19.WORLD).getHandle().a(new RayTrace(new Vec3D(x, y, z), new Vec3D(x + vx, y + vy, z + vz), RayTrace.BlockCollisionOption.d, RayTrace.FluidCollisionOption.d, null)); + MovingObjectPosition movingObjectPosition = ((CraftWorld) Simulator19.WORLD).getHandle().a(new RayTrace(new Vec3D(x, y, z), new Vec3D(x + vec3d.getX(), y + vec3d.getY(), z + vec3d.getZ()), RayTrace.BlockCollisionOption.d, RayTrace.FluidCollisionOption.d, null)); if (movingObjectPosition.c() != MovingObjectPosition.EnumMovingObjectType.a) { onLanding(); } } - this.x += movement.getX(); - this.y += movement.getY(); - this.z += movement.getZ(); + this.x += vec3d.getX(); + this.y += vec3d.getY(); + this.z += vec3d.getZ(); } boolean bl = !approximatelyEquals(movement.getX(), vec3d.getX()); @@ -128,7 +122,7 @@ public class TNT { Vector blockPos = getLandingPos(); Material material = Simulator19.getBlockType(blockPos.getBlockX(), blockPos.getBlockY(), blockPos.getBlockZ()); BlockData blockData = Simulator19.getBlockData(blockPos.getBlockX(), blockPos.getBlockY(), blockPos.getBlockZ()); - fall(vec3d.getX(), onGround); + fall(vec3d.getY(), onGround); if (horizontalCollision) { this.vx = bl ? 0.0 : this.vx; @@ -140,11 +134,12 @@ public class TNT { if (this.vy < 0.0) { this.vy = -this.vy * 0.8; } - } - if (blockData instanceof Bed) { + } else if (blockData instanceof Bed) { if (this.vy < 0.0) { this.vy = -this.vy * 0.6600000262260437 * 0.8; } + } else { + this.vy = 0.0; } } @@ -153,7 +148,7 @@ public class TNT { double cy = Math.abs(this.vy); if (cy < 0.1) { double cy2 = 0.4 + cy * 0.2; - this.vx = vy * cy2; + this.vx = vx * cy2; this.vz = vz * cy2; } } @@ -161,57 +156,26 @@ public class TNT { checkBlockCollision(); float j = this.getVelocityMultiplier(); - this.vy *= j; this.vx *= j; + this.vz *= j; } private Vector adjustMovementForCollisions(Vector movement) { - AxisAlignedBB boundingBox = new AxisAlignedBB(x, y, z, x + 0.98, y + 0.98, z + 0.98); - return movement.lengthSquared() == 0.0 ? movement : adjustMovementForCollisions(null, movement, boundingBox, Simulator19.WORLD); - } - - public static Vector adjustMovementForCollisions(Entity entity, Vector movement, AxisAlignedBB entityBoundingBox, World world) { - Iterable voxelShapes = ((CraftWorld) world).getHandle().d(((CraftEntity) entity).getHandle(), entityBoundingBox); - List collisions = new java.util.ArrayList<>(); - voxelShapes.forEach(collisions::add); - - return adjustMovementForCollisions(movement, entityBoundingBox, collisions); - } - - private static Vector adjustMovementForCollisions(Vector movement, AxisAlignedBB entityBoundingBox, List collisions) { - if (collisions.isEmpty()) { + if (movement.lengthSquared() == 0.0) { return movement; + } + + double mY = new AxisMovementLimiter(x, y, z, Axis.Y, movement.getY()).run(); + + boolean bl = Math.abs(movement.getX()) < Math.abs(movement.getZ()); + if (bl) { + double mZ = new AxisMovementLimiter(x, y + mY, z, Axis.Z, movement.getZ()).run(); + double mX = new AxisMovementLimiter(x, y + mY, z + mZ, Axis.X, movement.getX()).run(); + return new Vector(mX, mY, mZ); } else { - double d = movement.getX(); - double e = movement.getY(); - double f = movement.getZ(); - if (e != 0.0) { - e = VoxelShapes.a(EnumDirection.EnumAxis.b, entityBoundingBox, collisions, e); - if (e != 0.0) { - entityBoundingBox = entityBoundingBox.c(0.0, e, 0.0); - } - } - - boolean bl = Math.abs(d) < Math.abs(f); - if (bl && f != 0.0) { - f = VoxelShapes.a(EnumDirection.EnumAxis.c, entityBoundingBox, collisions, f); - if (f != 0.0) { - entityBoundingBox = entityBoundingBox.c(0.0, 0.0, f); - } - } - - if (d != 0.0) { - d = VoxelShapes.a(EnumDirection.EnumAxis.a, entityBoundingBox, collisions, d); - if (!bl && d != 0.0) { - entityBoundingBox = entityBoundingBox.c(d, 0.0, 0.0); - } - } - - if (!bl && f != 0.0) { - f = VoxelShapes.a(EnumDirection.EnumAxis.c, entityBoundingBox, collisions, f); - } - - return new Vector(d, e, f); + double mX = new AxisMovementLimiter(x, y + mY, z, Axis.X, movement.getX()).run(); + double mZ = new AxisMovementLimiter(x + mX, y + mY, z, Axis.Z, movement.getZ()).run(); + return new Vector(mX, mY, mZ); } } @@ -219,7 +183,7 @@ public class TNT { return Math.abs(b - a) < 9.999999747378752E-6; } - private int floor(double value) { + public static int floor(double value) { int i = (int)value; return value < (double)i ? i - 1 : i; } @@ -240,12 +204,6 @@ public class TNT { private void fall(double heightDifference, boolean onGround) { if (onGround) { - /* - if (this.fallDistance > 0.0F) { - // TODO: Is this needed?: state.getBlock().onLandedUpon(this.world, state, landedPosition, this, this.fallDistance); - } - */ - this.onLanding(); } else if (heightDifference < 0.0) { this.fallDistance -= (float)heightDifference;