use of net.countercraft.movecraft.MovecraftLocation in project Movecraft by APDevTeam.
the class TranslationTask method execute.
@Override
protected void execute() throws InterruptedException, ExecutionException {
// Check if theres anything to move
if (oldHitBox.isEmpty())
return;
if (getCraft().getDisabled() && !(craft instanceof SinkingCraft)) {
fail(I18nSupport.getInternationalisedString("Translation - Failed Craft Is Disabled"));
return;
}
// call event
final CraftPreTranslateEvent preTranslateEvent = new CraftPreTranslateEvent(craft, dx, dy, dz, world);
Bukkit.getServer().getPluginManager().callEvent(preTranslateEvent);
if (preTranslateEvent.isCancelled()) {
fail(preTranslateEvent.getFailMessage(), preTranslateEvent.isPlayingFailSound());
return;
}
if (dx != preTranslateEvent.getDx())
dx = preTranslateEvent.getDx();
if (dy != preTranslateEvent.getDy())
dy = preTranslateEvent.getDy();
if (dz != preTranslateEvent.getDz())
dz = preTranslateEvent.getDz();
world = preTranslateEvent.getWorld();
final int minY = oldHitBox.getMinY();
final int maxY = oldHitBox.getMaxY();
// proccess nether portals
if (Settings.CraftsUseNetherPortals && craft.getWorld().getEnvironment() != Environment.THE_END && world.equals(craft.getWorld())) {
// ensure chunks are loaded for portal checking only if change in location is
// large
Set<MovecraftChunk> chunksToLoad = ChunkManager.getChunks(oldHitBox, world, dx, dy, dz);
MovecraftChunk.addSurroundingChunks(chunksToLoad, 2);
ChunkManager.checkChunks(chunksToLoad);
if (!chunksToLoad.isEmpty())
ChunkManager.syncLoadChunks(chunksToLoad).get();
for (MovecraftLocation oldLocation : oldHitBox) {
Location location = oldLocation.translate(dx, dy, dz).toBukkit(craft.getWorld());
Block block = craft.getWorld().getBlockAt(location);
if (block.getType() == Material.NETHER_PORTAL) {
if (processNetherPortal(block)) {
sound = Sound.BLOCK_PORTAL_TRAVEL;
volume = 0.25f;
break;
}
}
}
}
// ensure chunks are loaded only if world is different or change in location is
// large
Set<MovecraftChunk> chunksToLoad = ChunkManager.getChunks(oldHitBox, craft.getWorld());
chunksToLoad.addAll(ChunkManager.getChunks(oldHitBox, world, dx, dy, dz));
MovecraftChunk.addSurroundingChunks(chunksToLoad, 1);
ChunkManager.checkChunks(chunksToLoad);
if (!chunksToLoad.isEmpty())
ChunkManager.syncLoadChunks(chunksToLoad).get();
// Check if the craft is too high
if (world.equals(craft.getWorld()) && (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_LIMIT, craft.getWorld()) < craft.getHitBox().getMinY())
dy = Math.min(dy, -1);
else if (world.equals(craft.getWorld()) && (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_ABOVE_GROUND, craft.getWorld()) > 0) {
final MovecraftLocation middle = oldHitBox.getMidPoint();
int testY = minY;
while (testY > 0) {
testY--;
if (!craft.getWorld().getBlockAt(middle.getX(), testY, middle.getZ()).getType().isAir())
break;
}
if (maxY - testY > (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_ABOVE_GROUND, world))
dy = Math.min(dy, -1);
}
// Process gravity
if (world.equals(craft.getWorld()) && craft.getType().getBoolProperty(CraftType.USE_GRAVITY) && !(craft instanceof SinkingCraft)) {
int incline = inclineCraft(oldHitBox);
if (incline > 0) {
boolean tooSteep = craft.getType().getIntProperty(CraftType.GRAVITY_INCLINE_DISTANCE) > -1 && incline > craft.getType().getIntProperty(CraftType.GRAVITY_INCLINE_DISTANCE);
if (tooSteep && craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) <= 0F) {
fail(I18nSupport.getInternationalisedString("Translation - Failed Incline too steep"));
return;
}
dy = tooSteep ? 0 : incline;
} else if (!isOnGround(oldHitBox) && craft.getType().getBoolProperty(CraftType.CAN_HOVER)) {
MovecraftLocation midPoint = oldHitBox.getMidPoint();
int centreMinY = oldHitBox.getMinYAt(midPoint.getX(), midPoint.getZ());
int groundY = centreMinY;
World w = craft.getWorld();
while (groundY - 1 >= WorldUtils.getWorldMinHeightLimit(w) && (w.getBlockAt(midPoint.getX(), groundY - 1, midPoint.getZ()).getType().isAir() || craft.getType().getMaterialSetProperty(CraftType.PASSTHROUGH_BLOCKS).contains(w.getBlockAt(midPoint.getX(), groundY - 1, midPoint.getZ()).getType()))) {
groundY--;
}
if (centreMinY - groundY > craft.getType().getIntProperty(CraftType.HOVER_LIMIT))
dy = -1;
} else if (!isOnGround(oldHitBox))
dy = dropDistance(oldHitBox);
}
// Fail the movement if the craft is too high and if the craft is not explosive
int maxHeightLimit = (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_LIMIT, world);
int minHeightLimit = (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MIN_HEIGHT_LIMIT, world);
if (dy > 0 && maxY + dy > maxHeightLimit && craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) <= 0F) {
fail(I18nSupport.getInternationalisedString("Translation - Failed Craft hit height limit"));
return;
} else if (dy > 0 && maxY + dy > maxHeightLimit) {
// If explosive and too high, set dy to 0
dy = 0;
} else if (minY + dy < minHeightLimit && dy < 0 && !(craft instanceof SinkingCraft) && !craft.getType().getBoolProperty(CraftType.USE_GRAVITY)) {
fail(I18nSupport.getInternationalisedString("Translation - Failed Craft hit minimum height limit"));
return;
} else if (minY + dy < minHeightLimit && dy < 0 && craft.getType().getBoolProperty(CraftType.USE_GRAVITY))
// if a craft using gravity hits the minimum height limit, set dy = 0 instead of failing
dy = 0;
if (!(dy < 0 && dx == 0 && dz == 0) && !checkFuel()) {
fail(I18nSupport.getInternationalisedString("Translation - Failed Craft out of fuel"));
return;
}
final EnumSet<Material> harvestBlocks = craft.getType().getMaterialSetProperty(CraftType.HARVEST_BLOCKS);
final List<MovecraftLocation> harvestedBlocks = new ArrayList<>();
final EnumSet<Material> harvesterBladeBlocks = craft.getType().getMaterialSetProperty(CraftType.HARVESTER_BLADE_BLOCKS);
final SetHitBox collisionBox = new SetHitBox();
for (MovecraftLocation oldLocation : oldHitBox) {
final MovecraftLocation newLocation = oldLocation.translate(dx, dy, dz);
// itself
if (world.equals(craft.getWorld()) && oldHitBox.contains(newLocation)) {
newHitBox.add(newLocation);
continue;
}
final Material testMaterial = newLocation.toBukkit(world).getBlock().getType();
if (Tags.CHESTS.contains(testMaterial) && checkChests(testMaterial, newLocation)) {
// prevent chests collision
fail(String.format(I18nSupport.getInternationalisedString("Translation - Failed Craft is obstructed") + " @ %d,%d,%d,%s", newLocation.getX(), newLocation.getY(), newLocation.getZ(), newLocation.toBukkit(craft.getWorld()).getBlock().getType()));
return;
}
if (!withinWorldBorder(world, newLocation)) {
fail(I18nSupport.getInternationalisedString("Translation - Failed Craft cannot pass world border") + String.format(" @ %d,%d,%d", newLocation.getX(), newLocation.getY(), newLocation.getZ()));
return;
}
boolean blockObstructed;
if (craft instanceof SinkingCraft)
blockObstructed = !Tags.FALL_THROUGH_BLOCKS.contains(testMaterial);
else
blockObstructed = !testMaterial.equals(Material.AIR) && !craft.getType().getMaterialSetProperty(CraftType.PASSTHROUGH_BLOCKS).contains(testMaterial);
boolean ignoreBlock = oldLocation.toBukkit(craft.getWorld()).getBlock().getType().isAir() && blockObstructed;
if (blockObstructed && !harvestBlocks.isEmpty() && harvestBlocks.contains(testMaterial)) {
Material tmpType = oldLocation.toBukkit(craft.getWorld()).getBlock().getType();
if (harvesterBladeBlocks.size() > 0 && harvesterBladeBlocks.contains(tmpType)) {
blockObstructed = false;
harvestedBlocks.add(newLocation);
}
}
if (blockObstructed) {
if (!(craft instanceof SinkingCraft) && craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) <= 0F) {
fail(String.format(I18nSupport.getInternationalisedString("Translation - Failed Craft is obstructed") + " @ %d,%d,%d,%s", newLocation.getX(), newLocation.getY(), newLocation.getZ(), testMaterial));
return;
}
collisionBox.add(newLocation);
} else if (!ignoreBlock)
newHitBox.add(newLocation);
}
if (!oldFluidList.isEmpty()) {
for (MovecraftLocation fluidLoc : oldFluidList) {
newFluidList.add(fluidLoc.translate(dx, dy, dz));
}
}
if (craft.getType().getMaterialSetProperty(CraftType.FORBIDDEN_HOVER_OVER_BLOCKS).size() > 0) {
MovecraftLocation test = new MovecraftLocation(newHitBox.getMidPoint().getX(), newHitBox.getMinY(), newHitBox.getMidPoint().getZ());
test = test.translate(0, -1, 0);
while (test.toBukkit(world).getBlock().getType().isAir()) {
test = test.translate(0, -1, 0);
}
Material testType = test.toBukkit(world).getBlock().getType();
if (craft.getType().getMaterialSetProperty(CraftType.FORBIDDEN_HOVER_OVER_BLOCKS).contains(testType)) {
fail(String.format(I18nSupport.getInternationalisedString("Translation - Failed Craft over block"), testType.name().toLowerCase().replace("_", " ")));
}
}
// call event
CraftTranslateEvent translateEvent = new CraftTranslateEvent(craft, oldHitBox, newHitBox, world);
Bukkit.getServer().getPluginManager().callEvent(translateEvent);
if (translateEvent.isCancelled()) {
this.fail(translateEvent.getFailMessage(), translateEvent.isPlayingFailSound());
return;
}
// do not switch world if sinking
if (craft instanceof SinkingCraft) {
List<MovecraftLocation> air = new ArrayList<>();
for (MovecraftLocation location : oldHitBox) {
if (location.toBukkit(craft.getWorld()).getBlock().getType().isAir()) {
air.add(location.translate(dx, dy, dz));
}
}
newHitBox.removeAll(air);
for (MovecraftLocation location : collisionBox) {
if (craft.getType().getFloatProperty(CraftType.EXPLODE_ON_CRASH) > 0F) {
if (System.currentTimeMillis() - craft.getOrigPilotTime() <= 1000) {
continue;
}
Location loc = location.toBukkit(craft.getWorld());
if (!loc.getBlock().getType().isAir() && ThreadLocalRandom.current().nextDouble(1) < .05) {
updates.add(new ExplosionUpdateCommand(loc, craft.getType().getFloatProperty(CraftType.EXPLODE_ON_CRASH)));
collisionExplosion = true;
}
}
SetHitBox toRemove = new SetHitBox();
MovecraftLocation next = location.translate(-dx, -dy, -dz);
while (oldHitBox.contains(next)) {
toRemove.add(next);
next = next.translate(0, 1, 0);
}
craft.getCollapsedHitBox().addAll(toRemove);
newHitBox.removeAll(toRemove);
}
} else if ((craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) > 0F) && System.currentTimeMillis() - craft.getOrigPilotTime() > Settings.CollisionPrimer) {
for (MovecraftLocation location : collisionBox) {
float explosionForce = craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION);
if (craft.getType().getBoolProperty(CraftType.FOCUSED_EXPLOSION)) {
explosionForce *= Math.min(oldHitBox.size(), craft.getType().getIntProperty(CraftType.MAX_SIZE));
}
// TODO: Account for underwater explosions
/*if (location.getY() < waterLine) { // underwater explosions require more force to do anything
explosionForce += 25;//TODO: find the correct amount
}*/
Location oldLocation = location.translate(-dx, -dy, -dz).toBukkit(craft.getWorld());
Location newLocation = location.toBukkit(world);
if (!oldLocation.getBlock().getType().isAir()) {
CraftCollisionExplosionEvent e = new CraftCollisionExplosionEvent(craft, newLocation, craft.getWorld());
Bukkit.getServer().getPluginManager().callEvent(e);
if (!e.isCancelled()) {
updates.add(new ExplosionUpdateCommand(newLocation, explosionForce));
collisionExplosion = true;
}
}
if (craft.getType().getBoolProperty(CraftType.FOCUSED_EXPLOSION)) {
// don't handle any further collisions if it is set to focusedexplosion
break;
}
}
}
if (!collisionBox.isEmpty() && craft.getType().getBoolProperty(CraftType.CRUISE_ON_PILOT)) {
CraftManager.getInstance().release(craft, CraftReleaseEvent.Reason.EMPTY, false);
for (MovecraftLocation location : oldHitBox) {
BlockData phaseBlock = craft.getPhaseBlocks().getOrDefault(location.toBukkit(craft.getWorld()), Material.AIR.createBlockData());
updates.add(new BlockCreateCommand(craft.getWorld(), location, phaseBlock));
}
newHitBox = new SetHitBox();
}
if (!collisionBox.isEmpty()) {
Bukkit.getServer().getPluginManager().callEvent(new CraftCollisionEvent(craft, collisionBox, world));
}
updates.add(new CraftTranslateCommand(craft, new MovecraftLocation(dx, dy, dz), world));
// prevents torpedo and rocket pilots
if (!(craft instanceof SinkingCraft && craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS)) && craft.getType().getBoolProperty(CraftType.MOVE_ENTITIES)) {
Location midpoint = new Location(craft.getWorld(), (oldHitBox.getMaxX() + oldHitBox.getMinX()) / 2.0, (oldHitBox.getMaxY() + oldHitBox.getMinY()) / 2.0, (oldHitBox.getMaxZ() + oldHitBox.getMinZ()) / 2.0);
for (Entity entity : craft.getWorld().getNearbyEntities(midpoint, oldHitBox.getXLength() / 2.0 + 1, oldHitBox.getYLength() / 2.0 + 2, oldHitBox.getZLength() / 2.0 + 1)) {
if (entity.getType() == EntityType.PLAYER) {
if (craft instanceof SinkingCraft)
continue;
EntityUpdateCommand eUp = new EntityUpdateCommand(entity, dx, dy, dz, 0, 0, world, sound, volume);
updates.add(eUp);
} else if (!craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS) || entity.getType() == EntityType.PRIMED_TNT) {
EntityUpdateCommand eUp = new EntityUpdateCommand(entity, dx, dy, dz, 0, 0, world);
updates.add(eUp);
}
}
} else {
// add releaseTask without playermove to manager
if (!craft.getType().getBoolProperty(CraftType.CRUISE_ON_PILOT) && !(craft instanceof SinkingCraft))
// not necessary to release cruiseonpilot crafts, because they will already be released
CraftManager.getInstance().addReleaseTask(craft);
}
captureYield(harvestedBlocks);
}
use of net.countercraft.movecraft.MovecraftLocation in project Movecraft by APDevTeam.
the class ContactsCommand method onCommand.
@Override
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) {
if (!command.getName().equalsIgnoreCase("contacts")) {
return false;
}
if (!(commandSender instanceof Player)) {
commandSender.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Contacts - Must Be Player"));
return true;
}
Player player = (Player) commandSender;
if (CraftManager.getInstance().getCraftByPlayer(player) == null) {
player.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("You must be piloting a craft"));
return true;
}
int page;
try {
if (args.length == 0)
page = 1;
else
page = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
commandSender.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Paginator - Invalid Page") + " \"" + args[0] + "\"");
return true;
}
TopicPaginator pageinator = new TopicPaginator(I18nSupport.getInternationalisedString("Contacts"));
Craft ccraft = CraftManager.getInstance().getCraftByPlayer(player);
HitBox hitBox = ccraft.getHitBox();
MovecraftLocation center = hitBox.getMidPoint();
for (Craft tcraft : ccraft.getContacts()) {
HitBox tHitBox = tcraft.getHitBox();
if (tHitBox.isEmpty())
continue;
MovecraftLocation tCenter = tHitBox.getMidPoint();
int distsquared = center.distanceSquared(tCenter);
String notification = I18nSupport.getInternationalisedString("Contact");
notification += ": ";
notification += tcraft instanceof SinkingCraft ? ChatColor.RED : tcraft.getDisabled() ? ChatColor.BLUE : "";
notification += tcraft.getName().length() >= 1 ? tcraft.getName() + " (" : "";
notification += tcraft.getType().getStringProperty(CraftType.NAME);
notification += tcraft.getName().length() >= 1 ? ") " : " ";
notification += ChatColor.RESET;
notification += I18nSupport.getInternationalisedString("Contact - Commanded By") + ", ";
notification += tcraft instanceof PilotedCraft ? ((PilotedCraft) tcraft).getPilot().getDisplayName() : "null";
notification += " ";
notification += I18nSupport.getInternationalisedString("Contact - Size") + " ";
notification += tcraft.getOrigBlockCount();
notification += ", " + I18nSupport.getInternationalisedString("Contact - Range") + " ";
notification += (int) Math.sqrt(distsquared);
notification += " " + I18nSupport.getInternationalisedString("Contact - To The");
int diffx = center.getX() - tCenter.getX();
int diffz = center.getZ() - tCenter.getZ();
if (Math.abs(diffx) > Math.abs(diffz))
if (diffx < 0)
notification += " " + I18nSupport.getInternationalisedString("Contact/Subcraft Rotate - East") + ".";
else
notification += " " + I18nSupport.getInternationalisedString("Contact/Subcraft Rotate - West") + ".";
else if (diffz < 0)
notification += " " + I18nSupport.getInternationalisedString("Contact/Subcraft Rotate - South") + ".";
else
notification += " " + I18nSupport.getInternationalisedString("Contact/Subcraft Rotate - North") + ".";
pageinator.addLine(notification);
}
if (pageinator.isEmpty()) {
player.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Contacts - None Found"));
return true;
}
if (!pageinator.isInBounds(page)) {
commandSender.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Paginator - Invalid page") + "\"" + page + "\"");
return true;
}
for (String line : pageinator.getPage(page)) commandSender.sendMessage(line);
return true;
}
use of net.countercraft.movecraft.MovecraftLocation in project Movecraft by APDevTeam.
the class PilotCommand method onCommand.
@Override
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) {
if (!command.getName().equalsIgnoreCase("pilot"))
return false;
if (!(commandSender instanceof Player)) {
commandSender.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Pilot - Must Be Player"));
return true;
}
Player player = (Player) commandSender;
if (!player.hasPermission("movecraft.commands") || !player.hasPermission("movecraft.commands.pilot")) {
player.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Insufficient Permissions"));
return true;
}
if (args.length < 1) {
player.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Pilot - No Craft Type"));
return true;
}
if (!player.hasPermission("movecraft." + args[0] + ".pilot")) {
player.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Insufficient Permissions"));
return true;
}
CraftType craftType = CraftManager.getInstance().getCraftTypeFromString(args[0]);
if (craftType == null) {
player.sendMessage(MOVECRAFT_COMMAND_PREFIX + I18nSupport.getInternationalisedString("Pilot - Invalid Craft Type"));
return true;
}
final World world = player.getWorld();
MovecraftLocation startPoint = MathUtils.bukkit2MovecraftLoc(player.getLocation());
CraftManager.getInstance().detect(startPoint, craftType, (type, w, p, parents) -> {
// Note: This only passes in a non-null player.
assert p != null;
if (parents.size() > 0)
return new Pair<>(Result.failWithMessage(I18nSupport.getInternationalisedString("Detection - Failed - Already commanding a craft")), null);
return new Pair<>(Result.succeed(), new PlayerCraftImpl(type, w, p));
}, world, player, Movecraft.getAdventure().player(player), craft -> () -> {
// Release old craft if it exists
Craft oldCraft = CraftManager.getInstance().getCraftByPlayer(player);
if (oldCraft != null)
CraftManager.getInstance().release(oldCraft, CraftReleaseEvent.Reason.PLAYER, false);
});
return true;
}
use of net.countercraft.movecraft.MovecraftLocation in project Movecraft by APDevTeam.
the class PlayerListener method checkCraftBorders.
private Set<Location> checkCraftBorders(Craft craft) {
Set<Location> mergePoints = new HashSet<>();
final EnumSet<Material> ALLOWED_BLOCKS = craft.getType().getMaterialSetProperty(CraftType.ALLOWED_BLOCKS);
final EnumSet<Material> FORBIDDEN_BLOCKS = craft.getType().getMaterialSetProperty(CraftType.FORBIDDEN_BLOCKS);
final MovecraftLocation[] SHIFTS = { // x
new MovecraftLocation(-1, 0, 0), new MovecraftLocation(-1, -1, 0), new MovecraftLocation(-1, 1, 0), new MovecraftLocation(1, -1, 0), new MovecraftLocation(1, 1, 0), new MovecraftLocation(1, 0, 0), // z
new MovecraftLocation(0, 1, 1), new MovecraftLocation(0, 0, 1), new MovecraftLocation(0, -1, 1), new MovecraftLocation(0, 1, -1), new MovecraftLocation(0, 0, -1), new MovecraftLocation(0, -1, -1), // y
new MovecraftLocation(0, 1, 0), new MovecraftLocation(0, -1, 0) };
// Check each location in the hitbox
for (MovecraftLocation ml : craft.getHitBox()) {
// Check the surroundings of each location
for (MovecraftLocation shift : SHIFTS) {
MovecraftLocation test = ml.add(shift);
// Ignore locations contained in the craft's hitbox
if (craft.getHitBox().contains(test)) {
continue;
}
Block testBlock = test.toBukkit(craft.getWorld()).getBlock();
Material testMaterial = testBlock.getType();
// Break the loop if an allowed block is found adjacent to the craft's hitbox
if (ALLOWED_BLOCKS.contains(testMaterial)) {
mergePoints.add(testBlock.getLocation());
} else // Do the same if a forbidden block is found
if (FORBIDDEN_BLOCKS.contains(testMaterial)) {
mergePoints.add(testBlock.getLocation());
}
}
}
// Return the string representation of the merging point and alert the pilot
return mergePoints;
}
use of net.countercraft.movecraft.MovecraftLocation in project Movecraft by APDevTeam.
the class CraftRotateCommand method doUpdate.
@Override
public void doUpdate() {
final Logger logger = Movecraft.getInstance().getLogger();
if (craft.getHitBox().isEmpty()) {
logger.warning("Attempted to move craft with empty HashHitBox!");
CraftManager.getInstance().release(craft, CraftReleaseEvent.Reason.EMPTY, false);
return;
}
long time = System.nanoTime();
final Set<Material> passthroughBlocks = new HashSet<>(craft.getType().getMaterialSetProperty(CraftType.PASSTHROUGH_BLOCKS));
if (craft instanceof SinkingCraft) {
passthroughBlocks.addAll(Tags.FLUID);
passthroughBlocks.addAll(Tag.LEAVES.getValues());
passthroughBlocks.addAll(Tags.SINKING_PASSTHROUGH);
}
if (!passthroughBlocks.isEmpty()) {
SetHitBox originalLocations = new SetHitBox();
final MovecraftRotation counterRotation = rotation == MovecraftRotation.CLOCKWISE ? MovecraftRotation.ANTICLOCKWISE : MovecraftRotation.CLOCKWISE;
for (MovecraftLocation movecraftLocation : craft.getHitBox()) {
originalLocations.add(MathUtils.rotateVec(counterRotation, movecraftLocation.subtract(originLocation)).add(originLocation));
}
final HitBox to = craft.getHitBox().difference(originalLocations);
for (MovecraftLocation location : to) {
var data = location.toBukkit(craft.getWorld()).getBlock().getBlockData();
if (passthroughBlocks.contains(data.getMaterial())) {
craft.getPhaseBlocks().put(location.toBukkit(craft.getWorld()), data);
}
}
// The subtraction of the set of coordinates in the HitBox cube and the HitBox itself
final HitBox invertedHitBox = new SetHitBox(craft.getHitBox().boundingHitBox()).difference(craft.getHitBox());
// A set of locations that are confirmed to be "exterior" locations
final SetHitBox exterior = new SetHitBox();
final SetHitBox interior = new SetHitBox();
// place phased blocks
final Set<Location> overlap = new HashSet<>(craft.getPhaseBlocks().keySet());
overlap.retainAll(craft.getHitBox().asSet().stream().map(l -> l.toBukkit(craft.getWorld())).collect(Collectors.toSet()));
final int minX = craft.getHitBox().getMinX();
final int maxX = craft.getHitBox().getMaxX();
final int minY = craft.getHitBox().getMinY();
final int maxY = overlap.isEmpty() ? craft.getHitBox().getMaxY() : Collections.max(overlap, Comparator.comparingInt(Location::getBlockY)).getBlockY();
final int minZ = craft.getHitBox().getMinZ();
final int maxZ = craft.getHitBox().getMaxZ();
final HitBox[] surfaces = { new SolidHitBox(new MovecraftLocation(minX, minY, minZ), new MovecraftLocation(minX, maxY, maxZ)), new SolidHitBox(new MovecraftLocation(minX, minY, minZ), new MovecraftLocation(maxX, maxY, minZ)), new SolidHitBox(new MovecraftLocation(maxX, minY, maxZ), new MovecraftLocation(minX, maxY, maxZ)), new SolidHitBox(new MovecraftLocation(maxX, minY, maxZ), new MovecraftLocation(maxX, maxY, minZ)), new SolidHitBox(new MovecraftLocation(minX, minY, minZ), new MovecraftLocation(maxX, minY, maxZ)) };
// Valid exterior starts as the 6 surface planes of the HitBox with the locations that lie in the HitBox removed
final SetHitBox validExterior = new SetHitBox();
for (HitBox hitBox : surfaces) {
validExterior.addAll(hitBox.difference(craft.getHitBox()));
}
// Check to see which locations in the from set are actually outside of the craft
for (MovecraftLocation location : validExterior) {
if (craft.getHitBox().contains(location) || exterior.contains(location)) {
continue;
}
// use a modified BFS for multiple origin elements
SetHitBox visited = new SetHitBox();
Queue<MovecraftLocation> queue = new LinkedList<>();
queue.add(location);
while (!queue.isEmpty()) {
MovecraftLocation node = queue.poll();
// If the node is already a valid member of the exterior of the HitBox, continued search is unitary.
for (MovecraftLocation neighbor : CollectionUtils.neighbors(invertedHitBox, node)) {
if (visited.contains(neighbor)) {
continue;
}
visited.add(neighbor);
queue.add(neighbor);
}
}
exterior.addAll(visited);
}
interior.addAll(invertedHitBox.difference(exterior));
final WorldHandler handler = Movecraft.getInstance().getWorldHandler();
for (MovecraftLocation location : invertedHitBox.difference(exterior)) {
var data = location.toBukkit(craft.getWorld()).getBlock().getBlockData();
if (!passthroughBlocks.contains(data.getMaterial())) {
continue;
}
craft.getPhaseBlocks().put(location.toBukkit(craft.getWorld()), data);
}
// translate the craft
handler.rotateCraft(craft, originLocation, rotation);
// trigger sign events
sendSignEvents();
// place confirmed blocks if they have been un-phased
for (MovecraftLocation location : exterior) {
Location bukkit = location.toBukkit(craft.getWorld());
if (!craft.getPhaseBlocks().containsKey(bukkit)) {
continue;
}
var phaseBlock = craft.getPhaseBlocks().remove(bukkit);
handler.setBlockFast(bukkit, phaseBlock);
craft.getPhaseBlocks().remove(bukkit);
}
for (MovecraftLocation location : originalLocations.boundingHitBox()) {
Location bukkit = location.toBukkit(craft.getWorld());
if (!craft.getHitBox().inBounds(location) && craft.getPhaseBlocks().containsKey(bukkit)) {
var phaseBlock = craft.getPhaseBlocks().remove(bukkit);
handler.setBlockFast(bukkit, phaseBlock);
}
}
for (MovecraftLocation location : interior) {
Location bukkit = location.toBukkit(craft.getWorld());
var data = bukkit.getBlock().getBlockData();
if (passthroughBlocks.contains(data.getMaterial())) {
craft.getPhaseBlocks().put(bukkit, data);
handler.setBlockFast(bukkit, Material.AIR.createBlockData());
}
}
} else {
// translate the craft
Movecraft.getInstance().getWorldHandler().rotateCraft(craft, originLocation, rotation);
// trigger sign events
sendSignEvents();
}
if (!craft.isNotProcessing())
craft.setProcessing(false);
time = System.nanoTime() - time;
if (Settings.Debug)
logger.info("Total time: " + (time / 1e6) + " milliseconds. Moving with cooldown of " + craft.getTickCooldown() + ". Speed of: " + String.format("%.2f", craft.getSpeed()));
}
Aggregations