mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-07 08:58:20 +01:00
Projectiles shooting
This commit is contained in:
parent
3d14b614ce
commit
3a251934ec
@ -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;
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user