diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java index bf329b3b..5b137cb7 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java @@ -23,6 +23,7 @@ import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.SWUtils; import de.steamwar.bausystem.features.simulator.data.Simulator; import de.steamwar.bausystem.features.simulator.execute.SimulatorExecutor; +import de.steamwar.bausystem.features.simulator.preview.SimulatorPreviewData; import de.steamwar.command.PreviousArguments; import de.steamwar.command.SWCommand; import de.steamwar.command.TypeMapper; @@ -90,6 +91,15 @@ public class SimulatorCommand extends SWCommand { } } + @Register(value = "test") + public void test(Player player) { + SimulatorPreviewData data = new SimulatorPreviewData(null); + data.spawnTNT(0, 120, 0); + data.tick(); + data.tick(); + data.tick(); + } + @ClassMapper(value = Simulator.class, local = true) public TypeMapper allSimulators() { return new TypeMapper<>() { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewAABB.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewAABB.java new file mode 100644 index 00000000..5b8ff937 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewAABB.java @@ -0,0 +1,108 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.preview; + +import lombok.AllArgsConstructor; +import lombok.ToString; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.VoxelShape; + +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +@ToString +public class SimulatorPreviewAABB { + public double minX; + public double minY; + public double minZ; + public double maxX; + public double maxY; + public double maxZ; + + public SimulatorPreviewAABB copy() { + return new SimulatorPreviewAABB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public void expandTowards(double vx, double vy, double vz) { + if (vx < 0) { + minX += vx; + } else { + maxX += vx; + } + if (vy < 0) { + minY += vy; + } else { + maxY += vy; + } + if (vz < 0) { + minZ += vz; + } else { + maxZ += vz; + } + } + + public void add(double vx, double vy, double vz) { + minX += vx; + minY += vy; + minZ += vz; + } + + public double sizeX() { + return maxX - minX; + } + + public double sizeY() { + return maxY - minY; + } + + public double sizeZ() { + return maxZ - minZ; + } + + public List intersects(int x, int y, int z, VoxelShape voxelShape) { + return voxelShape.getBoundingBoxes() + .stream() + .filter(boundingBox -> { + return minX - (x + boundingBox.getMaxX()) < -1.0E-7 && + maxX - (x + boundingBox.getMinX()) > 1.0E-7 && + minY - (y + boundingBox.getMaxY()) < -1.0E-7 && + maxY - (y + boundingBox.getMinY()) > 1.0E-7 && + minZ - (z + boundingBox.getMaxZ()) < -1.0E-7 && + maxZ - (z + boundingBox.getMinZ()) > 1.0E-7; + }) + .collect(Collectors.toList()); + } + + public boolean intersectsX(double x, double y, double z, BoundingBox boundingBox) { + return minX - (x + boundingBox.getMaxX()) < -1.0E-7 && + maxX - (x + boundingBox.getMinX()) > 1.0E-7; + } + + public boolean intersectsY(int x, int y, int z, BoundingBox boundingBox) { + return minY - (y + boundingBox.getMaxY()) < -1.0E-7 && + maxY - (y + boundingBox.getMinY()) > 1.0E-7; + } + + public boolean intersectsZ(double x, double y, double z, BoundingBox boundingBox) { + return minZ - (z + boundingBox.getMaxZ()) < -1.0E-7 && + maxZ - (z + boundingBox.getMinZ()) > 1.0E-7; + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewData.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewData.java new file mode 100644 index 00000000..9e5ae5d0 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewData.java @@ -0,0 +1,92 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.preview; + +import de.steamwar.bausystem.features.simulator.data.Simulator; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.util.VoxelShape; + +import java.util.*; + +@RequiredArgsConstructor +public class SimulatorPreviewData { + private static final World WORLD = Bukkit.getWorlds().get(0); + + public final Simulator simulator; + public final List tnts = new ArrayList<>(); + + private final Set AIR = new HashSet<>(); + private final Map datas = new HashMap<>(); + private final Map shapes = new HashMap<>(); + + public BlockData getBlockData(int x, int y, int z) { + Pos pos = new Pos(x, y, z); + if (AIR.contains(pos)) return null; + return datas.computeIfAbsent(pos, __ -> { + BlockData blockData = WORLD.getBlockData(x, y, z); + if (blockData.getMaterial().isAir()) { + AIR.add(pos); + return null; + } + return blockData; + }); + } + + public VoxelShape getBlockShape(int x, int y, int z) { + Pos pos = new Pos(x, y, z); + if (AIR.contains(pos)) return null; + return shapes.computeIfAbsent(pos, __ -> { + Block block = WORLD.getBlockAt(x, y, z); + if (block.getType().isAir()) { + AIR.add(pos); + return null; + } + return block.getCollisionShape(); + }); + } + + public void setAir(int x, int y, int z) { + Pos pos = new Pos(x, y, z); + AIR.add(pos); + datas.remove(pos); + shapes.remove(pos); + } + + public void spawnTNT(double x, double y, double z) { + tnts.add(new SimulatorPreviewTNT(this, x, y, z)); + } + + public void tick() { + tnts.forEach(SimulatorPreviewTNT::tick); + } + + @RequiredArgsConstructor + @EqualsAndHashCode + private final class Pos { + private final int x; + private final int y; + private final int z; + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewTNT.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewTNT.java index 6df7a066..72d2df61 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewTNT.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewTNT.java @@ -19,15 +19,21 @@ package de.steamwar.bausystem.features.simulator.preview; +import lombok.RequiredArgsConstructor; +import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; +import org.bukkit.util.VoxelShape; +import java.util.ArrayList; +import java.util.List; import java.util.Random; public class SimulatorPreviewTNT { private static final Random random = new Random(); - private boolean gravity = false; + private final SimulatorPreviewData data; + private boolean gravity = true; private int fuse = 80; private double x; private double y; @@ -36,9 +42,12 @@ public class SimulatorPreviewTNT { private double vy; private double vz; + private SimulatorPreviewVec collide = new SimulatorPreviewVec(); private boolean onGround; - public SimulatorPreviewTNT(double x, double y, double z) { + public SimulatorPreviewTNT(SimulatorPreviewData data, double x, double y, double z) { + this.data = data; + this.x = x; this.y = y; this.z = z; @@ -51,7 +60,7 @@ public class SimulatorPreviewTNT { public void tick() { // Gravity - if (!gravity) { + if (gravity) { vy -= 0.04; } @@ -83,19 +92,19 @@ public class SimulatorPreviewTNT { } public void move() { - Vector vec = collide(); - double d = vec.lengthSquared(); - if (d > 1.0E-7 || new Vector(vx, vy, vz).lengthSquared() - d < 1.0E-7) { - x += vx; - y += vy; - z += vz; + collide(); + double collisionLengthSquared = collide.lengthSquared(); + if (collisionLengthSquared > 1.0E-7 || new Vector(vx, vy, vz).lengthSquared() - collisionLengthSquared < 1.0E-7) { + x += collide.x; + y += collide.y; + z += collide.z; } - boolean xCollision = !equal(vx, vec.getX()); - boolean zCollision = !equal(vz, vec.getZ()); + boolean xCollision = !equal(vx, collide.x); + boolean zCollision = !equal(vz, collide.z); boolean horizontalCollision = xCollision || zCollision; if (Math.abs(vy) > 0.0F) { - boolean verticalCollision = vy != vec.getY(); + boolean verticalCollision = vy != collide.y; onGround = verticalCollision && vy < (double) 0.0F; } @@ -103,6 +112,9 @@ public class SimulatorPreviewTNT { if (xCollision) vx = 0; if (zCollision) vz = 0; } + // vx = collide.x; + vy = collide.y; + // vz = collide.z; // TODO: Get Block -> updateEntityMovementAfterFallOn! // TODO: Get BlockSpeedFactor multiply } @@ -111,13 +123,155 @@ public class SimulatorPreviewTNT { return Math.abs(y - x) < (double) 1.0E-5F; } - public Vector collide() { - if (x == 0 && y == 0 && z == 0) return new Vector(0, 0, 0); - // TODO: Implement rest! - return null; + public void collide() { + boolean xZero = vx == 0; + boolean zZero = vz == 0; + if (xZero && vy == 0 && zZero) { + collide.x = 0; + collide.y = 0; + collide.z = 0; + return; + } + SimulatorPreviewAABB box = new SimulatorPreviewAABB(x, y, z, x + 0.98, y + 0.98, z + 0.98); + SimulatorPreviewAABB initialCollisionBox = box.copy(); + initialCollisionBox.expandTowards(vx, vy, vz); + System.out.println(vy); + + List collisionDataList = new ArrayList<>(); + for (int x = floor(initialCollisionBox.minX - 1.0E-7) - 1; x < floor(initialCollisionBox.maxX + 1.0E-7) + 1; x++) { + for (int y = floor(initialCollisionBox.minY - 1.0E-7) - 1; y < floor(initialCollisionBox.maxY + 1.0E-7) + 1; y++) { + for (int z = floor(initialCollisionBox.minZ - 1.0E-7) - 1; z < floor(initialCollisionBox.maxZ + 1.0E-7) + 1; z++) { + System.out.println(x + " " + y + " " + z); + VoxelShape shape = data.getBlockShape(x, y, z); + if (shape == null) continue; + List collisionBoxes = initialCollisionBox.intersects(x, y, z, shape); + if (collisionBoxes.isEmpty()) continue; + collisionDataList.add(new CollisionData(x, y, z, collisionBoxes)); + } + } + } + if (collisionDataList.isEmpty()) { + System.out.println("No collision found"); + collide.x = vx; + collide.y = vy; + collide.z = vz; + return; + } + + double vx = this.vx; + double vy = this.vy; + double vz = this.vz; + + System.out.println(vx + " " + vy + " " + vz); + + if (vy != 0) { + for (CollisionData collisionData : collisionDataList) { + vy = collideY(box, collisionData, vy); + } + if (vy != 0) box.add(0, vy, 0); + } + + boolean xSmaller = Math.abs(vx) < Math.abs(vz); + if (xSmaller && vz != 0) { + for (CollisionData collisionData : collisionDataList) { + vz = collideZ(box, collisionData, vz); + } + if (vz != 0) box.add(0, 0, vz); + } + + if (vx != 0) { + for (CollisionData collisionData : collisionDataList) { + vx = collideX(box, collisionData, vx); + } + if (vx != 0) box.add(vx, 0, 0); + } + + if (!xSmaller && vz != 0) { + for (CollisionData collisionData : collisionDataList) { + vz = collideZ(box, collisionData, vz); + } + if (vz != 0) box.add(0, 0, vz); + } + + System.out.println(vx + " " + vy + " " + vz); + + collide.x = vx; + collide.y = vy; + collide.z = vz; + } + + public static int floor(double value) { + int i = (int) value; + return value < (double) i ? i - 1 : i; + } + + public static double collideX(SimulatorPreviewAABB box, CollisionData collisionData, double vx) { + if (vx < 0) { + for (BoundingBox boundingBox : collisionData.collisions) { + if (box.intersectsX(collisionData.x + vx, collisionData.y, collisionData.z, boundingBox)) { + vx = Math.max(vx, boundingBox.getMaxX()); + } + } + } else { + for (BoundingBox boundingBox : collisionData.collisions) { + if (box.intersectsX(collisionData.x + vx, collisionData.y, collisionData.z, boundingBox)) { + vx = Math.min(vx, boundingBox.getMinX()); + } + } + vx -= box.sizeY(); + } + return vx; + } + + public static double collideY(SimulatorPreviewAABB box, CollisionData collisionData, double vy) { + double size = box.sizeY(); + box = box.copy(); + box.expandTowards(0, vy, 0); + if (vy < 0) { + for (BoundingBox boundingBox : collisionData.collisions) { + if (box.intersectsY(collisionData.x, collisionData.y, collisionData.z, boundingBox)) { + vy = Math.max(vy, boundingBox.getMaxY() + collisionData.y - box.minY - size); + } + } + System.out.println("N: " + vy); + } else { + for (BoundingBox boundingBox : collisionData.collisions) { + if (box.intersectsY(collisionData.x, collisionData.y, collisionData.z, boundingBox)) { + vy = Math.min(vy, boundingBox.getMinY() + collisionData.y - box.minY - size); + } + } + System.out.println("P: " + vy); + } + return vy; + } + + public static double collideZ(SimulatorPreviewAABB box, CollisionData collisionData, double vz) { + if (vz < 0) { + for (BoundingBox boundingBox : collisionData.collisions) { + if (box.intersectsZ(collisionData.x, collisionData.y, collisionData.z + vz, boundingBox)) { + vz = Math.max(vz, boundingBox.getMaxZ()); + } + } + } else { + for (BoundingBox boundingBox : collisionData.collisions) { + if (box.intersectsZ(collisionData.x, collisionData.y, collisionData.z + vz, boundingBox)) { + vz = Math.min(vz, boundingBox.getMinZ()); + } + } + vz -= box.sizeZ(); + } + return vz; } public void explode() { // TODO: Implement } + + @RequiredArgsConstructor + private class CollisionData { + private final int x; + private final int y; + private final int z; + private final List collisions; + } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewVec.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewVec.java new file mode 100644 index 00000000..9ed56ae2 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/preview/SimulatorPreviewVec.java @@ -0,0 +1,30 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.preview; + +public class SimulatorPreviewVec { + public double x; + public double y; + public double z; + + public double lengthSquared() { + return x * x + y * y + z * z; + } +}