All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.entity.projectile.SlenderProjectile Maven / Gradle / Ivy

package cn.nukkit.entity.projectile;

import cn.nukkit.Player;
import cn.nukkit.block.Block;
import cn.nukkit.block.BlockSignPost;
import cn.nukkit.entity.Entity;
import cn.nukkit.event.entity.ProjectileHitEvent;
import cn.nukkit.level.MovingObjectPosition;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.level.generator.populator.impl.structure.utils.block.state.Direction;
import cn.nukkit.math.BVector3;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.CompassRoseDirection;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.tag.CompoundTag;

import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Predicate;

/**
 * 这个抽象类代表较为细长的投射物实体(例如弓箭,三叉戟),它通过重写{@link Entity#move}方法实现这些实体较为准确的碰撞箱计算。
 * 

* This abstract class represents slender projectile entities (e.g.arrow, trident), and it realized a more accurate collision box calculation for these entities by overriding the {@link Entity#move} method. */ public abstract class SlenderProjectile extends EntityProjectile { private static final int SPLIT_NUMBER = 10; private MovingObjectPosition lastHitBlock; public SlenderProjectile(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } public SlenderProjectile(FullChunk chunk, CompoundTag nbt, Entity shootingEntity) { super(chunk, nbt, shootingEntity); } //对于SlenderProjectile你不应该把Width设置太大,如果没必要请使用默认值. @Override public float getWidth() { return 0.1f; } //对于SlenderProjectile你不应该把Height设置太大,如果没必要请使用默认值. @Override public float getHeight() { return 0.1f; } /* * 经过测试这个算法在大多数情况下效果不错。 */ @Override public boolean move(double dx, double dy, double dz) { if (dx == 0 && dz == 0 && dy == 0) { return true; } this.ySize *= 0.4; double movX = dx; double movY = dy; double movZ = dz; final SlenderProjectile projectile = this; final Entity shootEntity = shootingEntity; final int ticks = ticksLived; var currentAABB = this.boundingBox.clone(); var dirVector = new Vector3(dx, dy, dz).multiply(1 / (double) SPLIT_NUMBER); Entity collisionEntity = null; Block collisionBlock = null; for (int i = 0; i < SPLIT_NUMBER; ++i) { var collisionBlocks = this.level.getCollisionBlocks(currentAABB.offset(dirVector.x, dirVector.y, dirVector.z)); var collisionEntities = this.getLevel().fastCollidingEntities(currentAABB, this); if (collisionBlocks.length != 0) { currentAABB.offset(-dirVector.x, -dirVector.y, -dirVector.z); collisionBlock = Arrays.stream(collisionBlocks).min(Comparator.comparingDouble(projectile::distanceSquared)).get(); break; } collisionEntity = collisionEntities.stream() .filter(Predicate.not(entity -> (entity == shootEntity && ticks < 5) || (entity instanceof Player && ((Player) entity).getGamemode() == Player.SPECTATOR))) .min(Comparator.comparingDouble(o -> o.distanceSquared(projectile))) .orElse(null); if (collisionEntity != null) { break; } } Vector3 centerPoint1 = new Vector3((currentAABB.getMinX() + currentAABB.getMaxX()) / 2, (currentAABB.getMinY() + currentAABB.getMaxY()) / 2, (currentAABB.getMinZ() + currentAABB.getMaxZ()) / 2); //collide with entity if (collisionEntity != null) { MovingObjectPosition movingObject = new MovingObjectPosition(); movingObject.typeOfHit = 1; movingObject.entityHit = collisionEntity; movingObject.hitVector = centerPoint1; onCollideWithEntity(movingObject.entityHit); return true; } Vector3 centerPoint2 = new Vector3((this.boundingBox.getMinX() + this.boundingBox.getMaxX()) / 2, (this.boundingBox.getMinY() + this.boundingBox.getMaxY()) / 2, (this.boundingBox.getMinZ() + this.boundingBox.getMaxZ()) / 2); Vector3 diff = centerPoint1.subtract(centerPoint2); if (dy > 0) { if (diff.getY() + 0.001 < dy) { dy = diff.getY(); } } if (dy < 0) { if (diff.getY() - 0.001 > dy) { dy = diff.getY(); } } if (dx > 0) { if (diff.getX() + 0.001 < dx) { dx = diff.getX(); } } if (dx < 0) { if (diff.getX() - 0.001 > dx) { dx = diff.getX(); } } if (dz > 0) { if (diff.getZ() + 0.001 < dz) { dz = diff.getZ(); } } if (dz < 0) { if (diff.getZ() - 0.001 > dz) { dz = diff.getZ(); } } this.boundingBox.offset(0, dy, 0); this.boundingBox.offset(dx, 0, 0); this.boundingBox.offset(0, 0, dz); this.x = (this.boundingBox.getMinX() + this.boundingBox.getMaxX()) / 2; this.y = this.boundingBox.getMinY() - this.ySize; this.z = (this.boundingBox.getMinZ() + this.boundingBox.getMaxZ()) / 2; this.checkChunks(); this.checkGroundState(movX, movY, movZ, dx, dy, dz); this.updateFallState(this.onGround); if (movX != dx) { this.motionX = 0; } if (movY != dy) { this.motionY = 0; } if (movZ != dz) { this.motionZ = 0; } //collide with block if (this.isCollided && !this.hadCollision) { this.hadCollision = true; this.motionX = 0; this.motionY = 0; this.motionZ = 0; BVector3 bVector3 = BVector3.fromPos(new Vector3(dx, dy, dz)); BlockFace blockFace = BlockFace.fromHorizontalAngle(bVector3.getYaw()); Block block = level.getBlock(this.getFloorX(), this.getFloorY(), this.getFloorZ()).getSide(blockFace); if (block.getId() == 0) { blockFace = BlockFace.DOWN; block = level.getBlock(this.getFloorX(), this.getFloorY(), this.getFloorZ()).down(); } if (block.getId() == 0) { blockFace = BlockFace.UP; block = level.getBlock(this.getFloorX(), this.getFloorY(), this.getFloorZ()).up(); } if (block.getId() == 0 && collisionBlock != null) { block = collisionBlock; } this.server.getPluginManager().callEvent(new ProjectileHitEvent(this, lastHitBlock = MovingObjectPosition.fromBlock(block.getFloorX(), block.getFloorY(), block.getFloorZ(), blockFace, this))); onCollideWithBlock(getPosition(), getMotion()); addHitEffect(); } return true; } @Override public boolean onUpdate(int currentTick) { if (this.closed) { return false; } if (this.isCollided && this.hadCollision) { if (lastHitBlock != null && lastHitBlock.typeOfHit == 0 && level.getBlock(lastHitBlock.blockX, lastHitBlock.blockY, lastHitBlock.blockZ).getId() == 0) { this.motionY -= this.getGravity(); updateRotation(); this.move(this.motionX, this.motionY, this.motionZ); this.updateMovement(); } return false; } int tickDiff = currentTick - this.lastUpdate; if (tickDiff <= 0 && !this.justCreated) { return true; } this.lastUpdate = currentTick; boolean hasUpdate = this.entityBaseTick(tickDiff); if (this.isAlive()) { if (!this.isCollided) { updateMotion(); } if (!this.hadCollision || Math.abs(this.motionX) > 0.00001 || Math.abs(this.motionY) > 0.00001 || Math.abs(this.motionZ) > 0.00001) { updateRotation(); hasUpdate = true; } this.move(this.motionX, this.motionY, this.motionZ); this.updateMovement(); } return hasUpdate; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy