Projectiles shooting

This commit is contained in:
Konstantin Shandurenko 2021-02-22 09:45:19 +03:00
parent 3d14b614ce
commit 3a251934ec
3 changed files with 147 additions and 1 deletions

View File

@ -4,12 +4,16 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.ai.GoalSelector;
import net.minestom.server.entity.pathfinding.Navigator;
import net.minestom.server.entity.type.Projectile;
import net.minestom.server.entity.type.projectile.EntityArrow;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.time.CooldownUtils;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiFunction;
/**
* Created by k.shandurenko on 22.02.2021
*/
@ -21,6 +25,9 @@ public class RangedAttackGoal extends GoalSelector {
private final int attackRangeSquared;
private final int desirableRangeSquared;
private final boolean comeClose;
private final double spread;
private BiFunction<Entity, Position, Projectile> projectileGenerator;
private boolean stop;
@ -30,18 +37,24 @@ public class RangedAttackGoal extends GoalSelector {
* @param attackRange the allowed range the entity can shoot others.
* @param desirableRange the desirable range: the entity will try to stay no further than this distance.
* @param comeClose whether entity should go as close as possible to the target whether target is not in line of sight.
* @param spread shot spread (0 for best accuracy).
* @param timeUnit the unit of the delay.
*/
public RangedAttackGoal(@NotNull EntityCreature entityCreature, int delay, int attackRange, int desirableRange, boolean comeClose, @NotNull TimeUnit timeUnit) {
public RangedAttackGoal(@NotNull EntityCreature entityCreature, int delay, int attackRange, int desirableRange, boolean comeClose, double spread, @NotNull TimeUnit timeUnit) {
super(entityCreature);
this.delay = delay;
this.timeUnit = timeUnit;
this.attackRangeSquared = attackRange * attackRange;
this.desirableRangeSquared = desirableRange * desirableRange;
this.comeClose = comeClose;
this.spread = spread;
Check.argCondition(desirableRange <= attackRange, "Desirable range can not exceed attack range!");
}
public void setProjectileGenerator(BiFunction<Entity, Position, Projectile> projectileGenerator) {
this.projectileGenerator = projectileGenerator;
}
@Override
public boolean shouldStart() {
return findAndUpdateTarget() != null;
@ -66,7 +79,15 @@ public class RangedAttackGoal extends GoalSelector {
if (distanceSquared <= this.attackRangeSquared) {
if (!CooldownUtils.hasCooldown(time, this.lastShot, this.timeUnit, this.delay)) {
if (this.entityCreature.hasLineOfSight(target)) {
Position to = target.getPosition().clone().add(0D, target.getEyeHeight(), 0D);
BiFunction<Entity, Position, Projectile> projectileGenerator = this.projectileGenerator;
if (projectileGenerator == null) {
projectileGenerator = EntityArrow::new;
}
Projectile projectile = projectileGenerator.apply(this.entityCreature, new Position(0D, 0D, 0D));
Projectile.shoot(projectile, this.entityCreature, to, this.spread);
this.lastShot = time;
} else {
comeClose = this.comeClose;

View File

@ -1,4 +1,55 @@
package net.minestom.server.entity.type;
import net.minestom.server.entity.Entity;
import net.minestom.server.event.entity.EntityShootEvent;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public interface Projectile {
static void shoot(@NotNull Projectile projectile, @NotNull Entity shooter, Position to, double spread) {
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());
});
}
@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!");
Entity proj = (Entity) projectile;
double dx = to.getX() - from.getX();
double dy = to.getY() - from.getY();
double dz = to.getZ() - from.getZ();
double xzLength = Math.sqrt(dx * dx + dz * dz);
dy += xzLength * 0.20000000298023224D;
double length = Math.sqrt(dx * dx + dy * dy + dz * dz);
dx /= length;
dy /= length;
dz /= length;
Random random = ThreadLocalRandom.current();
spread *= 0.007499999832361937D;
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)));
}
}

View File

@ -0,0 +1,74 @@
package net.minestom.server.event.entity;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.type.Projectile;
import net.minestom.server.event.CancellableEvent;
import net.minestom.server.event.EntityEvent;
import net.minestom.server.utils.Position;
import org.jetbrains.annotations.NotNull;
/**
* Called with {@link Projectile#shoot(Projectile, Entity, Position, double)}.
*/
public class EntityShootEvent extends EntityEvent implements CancellableEvent {
private final Projectile projectile;
private final Position to;
private double spread;
private boolean cancelled;
public EntityShootEvent(@NotNull Entity entity, @NotNull Projectile projectile, @NotNull Position to, double spread) {
super(entity);
this.projectile = projectile;
this.to = to;
this.spread = spread;
}
/**
* Gets the projectile.
*
* @return the projectile.
*/
public Projectile getProjectile() {
return this.projectile;
}
/**
* Gets the position projectile was shot to.
*
* @return the position projectile was shot to.
*/
public Position getTo() {
return this.to;
}
/**
* Gets shot spread.
*
* @return shot spread.
*/
public double getSpread() {
return this.spread;
}
/**
* Sets shot spread.
*
* @param spread shot spread.
*/
public void setSpread(double spread) {
this.spread = spread;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
}