mirror of https://github.com/Minestom/Minestom.git
Block collisions for arrows
This commit is contained in:
parent
3a251934ec
commit
dbfebc50ec
|
@ -91,7 +91,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P
|
|||
protected Entity vehicle;
|
||||
|
||||
// Velocity
|
||||
protected Vector velocity = new Vector(); // Movement in block per second
|
||||
protected Vector velocity = new Vector(); // Movement in block per second
|
||||
protected boolean hasPhysics = true;
|
||||
|
||||
protected double gravityDragPerTick;
|
||||
protected double gravityAcceleration;
|
||||
|
@ -483,7 +484,11 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P
|
|||
getVelocity().getZ() / tps
|
||||
);
|
||||
|
||||
this.onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
|
||||
if (this.hasPhysics) {
|
||||
this.onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
|
||||
} else {
|
||||
newVelocityOut = deltaPos;
|
||||
}
|
||||
|
||||
// World border collision
|
||||
final Position finalVelocityPosition = CollisionUtils.applyWorldBorder(instance, position, newPosition);
|
||||
|
|
|
@ -48,7 +48,7 @@ public class RangedAttackGoal extends GoalSelector {
|
|||
this.desirableRangeSquared = desirableRange * desirableRange;
|
||||
this.comeClose = comeClose;
|
||||
this.spread = spread;
|
||||
Check.argCondition(desirableRange <= attackRange, "Desirable range can not exceed attack range!");
|
||||
Check.argCondition(desirableRange > attackRange, "Desirable range can not exceed attack range!");
|
||||
}
|
||||
|
||||
public void setProjectileGenerator(BiFunction<Entity, Position, Projectile> projectileGenerator) {
|
||||
|
|
|
@ -13,16 +13,21 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||
public interface Projectile {
|
||||
|
||||
static void shoot(@NotNull Projectile projectile, @NotNull Entity shooter, Position to, double spread) {
|
||||
Check.argCondition(!(projectile instanceof Entity), "Projectile must be an instance of Entity!");
|
||||
EntityShootEvent event = new EntityShootEvent(shooter, projectile, to, spread);
|
||||
shooter.callCancellableEvent(EntityShootEvent.class, event, () -> {
|
||||
Position from = shooter.getPosition().clone().add(0D, shooter.getEyeHeight(), 0D);
|
||||
shoot(projectile, from, to, event.getSpread());
|
||||
});
|
||||
shooter.callEvent(EntityShootEvent.class, event);
|
||||
if (event.isCancelled()) {
|
||||
Entity proj = (Entity) projectile;
|
||||
proj.remove();
|
||||
return;
|
||||
}
|
||||
Position from = shooter.getPosition().clone().add(0D, shooter.getEyeHeight(), 0D);
|
||||
shoot(projectile, from, to, event.getSpread());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
static void shoot(@NotNull Projectile projectile, @NotNull Position from, @NotNull Position to, double spread) {
|
||||
Check.argCondition(projectile instanceof Entity, "Projectile must be an instance of Entity!");
|
||||
Check.argCondition(!(projectile instanceof Entity), "Projectile must be an instance of Entity!");
|
||||
Entity proj = (Entity) projectile;
|
||||
double dx = to.getX() - from.getX();
|
||||
double dy = to.getY() - from.getY();
|
||||
|
@ -39,17 +44,15 @@ public interface Projectile {
|
|||
dx += random.nextGaussian() * spread;
|
||||
dy += random.nextGaussian() * spread;
|
||||
dz += random.nextGaussian() * spread;
|
||||
dx *= 2;
|
||||
dy *= 2;
|
||||
dz *= 2;
|
||||
Vector velocity = proj.getVelocity();
|
||||
velocity.setX(dx);
|
||||
velocity.setY(dy);
|
||||
velocity.setZ(dz);
|
||||
xzLength = Math.sqrt(dx * dx + dz * dz);
|
||||
double yaw = Math.max(Math.abs(dx), Math.abs(dz));
|
||||
double pitch = Math.max(Math.abs(dy), Math.abs(xzLength));
|
||||
proj.setView((float) (yaw * Math.toDegrees(1D)), (float) (pitch * Math.toDegrees(1D)));
|
||||
velocity.multiply(20);
|
||||
proj.setView(
|
||||
(float) Math.toDegrees(Math.atan2(dx, dz)),
|
||||
(float) Math.toDegrees(Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ import net.minestom.server.entity.EntityType;
|
|||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.ObjectEntity;
|
||||
import net.minestom.server.entity.type.Projectile;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.network.packet.server.play.EntityTeleportPacket;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -17,11 +21,60 @@ public class EntityAbstractArrow extends ObjectEntity implements Projectile {
|
|||
private final static byte CRITICAL_BIT = 0x01;
|
||||
private final static byte NO_CLIP_BIT = 0x02;
|
||||
|
||||
private final int shooterID;
|
||||
private final int shooterID;
|
||||
|
||||
EntityAbstractArrow(@Nullable Entity shooter, @NotNull EntityType entityType, @NotNull Position spawnPosition) {
|
||||
super(entityType, spawnPosition);
|
||||
this.shooterID = shooter == null ? 0 : shooter.getEntityId();
|
||||
super.hasPhysics = false;
|
||||
|
||||
setBoundingBox(.5F, .5F, .5F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(long time) {
|
||||
Position posBefore = getPosition().clone();
|
||||
super.tick(time);
|
||||
Position posNow = getPosition().clone();
|
||||
if (isStuck(posBefore, posNow)) {
|
||||
if (super.onGround) {
|
||||
return;
|
||||
}
|
||||
super.onGround = true;
|
||||
getVelocity().zero();
|
||||
sendPacketToViewersAndSelf(getVelocityPacket());
|
||||
setNoGravity(true);
|
||||
} else {
|
||||
if (!super.onGround) {
|
||||
return;
|
||||
}
|
||||
super.onGround = false;
|
||||
setNoGravity(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStuck(Position pos, Position posNow) {
|
||||
if (pos.isSimilar(posNow)) {
|
||||
return true;
|
||||
}
|
||||
double part = .25D; // half of the bounding box
|
||||
Vector dir = posNow.toVector().subtract(pos.toVector());
|
||||
int parts = (int) Math.ceil(dir.length() / part);
|
||||
Position direction = dir.normalize().multiply(part).toPosition();
|
||||
for (int i = 0; i < parts; ++i) {
|
||||
if (i == parts - 1) {
|
||||
pos = posNow;
|
||||
} else {
|
||||
pos.add(direction);
|
||||
}
|
||||
BlockPosition bpos = pos.toBlockPosition();
|
||||
Block block = getInstance().getBlock(bpos.getX(), bpos.getY() - 1, bpos.getZ());
|
||||
if (!block.isAir() && !block.isLiquid()) {
|
||||
teleport(pos);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setCritical(boolean value) {
|
||||
|
|
|
@ -14,6 +14,7 @@ public class EntityArrow extends EntityAbstractArrow {
|
|||
|
||||
public EntityArrow(@Nullable Entity shooter, @NotNull Position spawnPosition) {
|
||||
super(shooter, EntityType.ARROW, spawnPosition);
|
||||
|
||||
}
|
||||
|
||||
public void setColor(int value) {
|
||||
|
|
|
@ -42,6 +42,7 @@ public class Main {
|
|||
commandManager.register(new PotionCommand());
|
||||
commandManager.register(new TitleCommand());
|
||||
commandManager.register(new BookCommand());
|
||||
commandManager.register(new ShootCommand());
|
||||
|
||||
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage("unknown command"));
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package demo.commands;
|
||||
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.type.Projectile;
|
||||
import net.minestom.server.entity.type.projectile.EntityArrow;
|
||||
import net.minestom.server.entity.type.projectile.EntitySpectralArrow;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Created by k.shandurenko on 22.02.2021
|
||||
*/
|
||||
public class ShootCommand extends Command {
|
||||
|
||||
public ShootCommand() {
|
||||
super("shoot");
|
||||
setCondition(this::condition);
|
||||
setDefaultExecutor(this::defaultExecutor);
|
||||
var typeArg = ArgumentType.Word("type").from("default", "spectral", "colored");
|
||||
setArgumentCallback(this::onTypeError, typeArg);
|
||||
addSyntax(this::onShootCommand, typeArg);
|
||||
}
|
||||
|
||||
private boolean condition(CommandSender sender, String commandString) {
|
||||
if (!sender.isPlayer()) {
|
||||
sender.sendMessage("The command is only available for player");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void defaultExecutor(CommandSender sender, Arguments args) {
|
||||
sender.sendMessage("Correct usage: shoot [default/spectral/colored]");
|
||||
}
|
||||
|
||||
private void onTypeError(CommandSender sender, ArgumentSyntaxException exception) {
|
||||
sender.sendMessage("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'default', 'spectral' or 'colored'");
|
||||
}
|
||||
|
||||
private void onShootCommand(CommandSender sender, Arguments args) {
|
||||
Player player = (Player) sender;
|
||||
String mode = args.getWord("type");
|
||||
Projectile projectile;
|
||||
var pos = player.getPosition().clone().add(0D, player.getEyeHeight(), 0D);
|
||||
switch (mode) {
|
||||
case "default":
|
||||
projectile = new EntityArrow(player, pos);
|
||||
break;
|
||||
case "spectral":
|
||||
projectile = new EntitySpectralArrow(player, pos);
|
||||
break;
|
||||
case "colored":
|
||||
projectile = new EntityArrow(player, pos);
|
||||
((EntityArrow) projectile).setColor(ThreadLocalRandom.current().nextInt());
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
((Entity) projectile).setInstance(player.getInstance());
|
||||
var dir = pos.getDirection().multiply(30D);
|
||||
pos = pos.clone().add(dir.getX(), dir.getY(), dir.getZ());
|
||||
Projectile.shoot(projectile, player, pos, 0D);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue