mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-03 23:17:48 +01:00
Merge pull request #155 from RinesThaix/entities
Projectile improvements, custom entity position synchronization cooldown support
This commit is contained in:
commit
3971777e56
@ -101,6 +101,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
||||
|
||||
// Network synchronization, send the absolute position of the entity each X milliseconds
|
||||
private static final UpdateOption SYNCHRONIZATION_COOLDOWN = new UpdateOption(1500, TimeUnit.MILLISECOND);
|
||||
private UpdateOption customSynchronizationCooldown;
|
||||
private long lastAbsoluteSynchronizationTime;
|
||||
|
||||
// Events
|
||||
@ -670,7 +671,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
||||
}
|
||||
|
||||
// Scheduled synchronization
|
||||
if (!CooldownUtils.hasCooldown(time, lastAbsoluteSynchronizationTime, SYNCHRONIZATION_COOLDOWN)) {
|
||||
if (!CooldownUtils.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) {
|
||||
this.lastAbsoluteSynchronizationTime = time;
|
||||
sendSynchronization();
|
||||
}
|
||||
@ -1485,6 +1486,22 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission
|
||||
this.lastAbsoluteSynchronizationTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom cooldown for position synchronization.
|
||||
*
|
||||
* @param cooldown custom cooldown for position synchronization.
|
||||
*/
|
||||
public void setCustomSynchronizationCooldown(@Nullable UpdateOption cooldown) {
|
||||
this.customSynchronizationCooldown = cooldown;
|
||||
}
|
||||
|
||||
private UpdateOption getSynchronizationCooldown() {
|
||||
if (this.customSynchronizationCooldown != null) {
|
||||
return this.customSynchronizationCooldown;
|
||||
}
|
||||
return SYNCHRONIZATION_COOLDOWN;
|
||||
}
|
||||
|
||||
public enum Pose {
|
||||
STANDING,
|
||||
FALL_FLYING,
|
||||
|
@ -0,0 +1,177 @@
|
||||
package net.minestom.server.entity.ai.goal;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.ai.GoalSelector;
|
||||
import net.minestom.server.entity.pathfinding.Navigator;
|
||||
import net.minestom.server.entity.type.projectile.EntityProjectile;
|
||||
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.Function;
|
||||
|
||||
/**
|
||||
* Allows entity to perform both melee and ranged attacks.
|
||||
*/
|
||||
public class CombinedAttackGoal extends GoalSelector {
|
||||
|
||||
private final int meleeRangeSquared;
|
||||
private final int meleeDelay;
|
||||
private final TimeUnit meleeTimeUnit;
|
||||
private final int rangedRangeSquared;
|
||||
private final double rangedPower;
|
||||
private final double rangedSpread;
|
||||
private final int rangedDelay;
|
||||
private final TimeUnit rangedTimeUnit;
|
||||
private final int desirableRangeSquared;
|
||||
private final boolean comeClose;
|
||||
|
||||
private Function<Entity, EntityProjectile> projectileGenerator;
|
||||
|
||||
private long lastAttack;
|
||||
private boolean stop;
|
||||
private Entity cachedTarget;
|
||||
|
||||
/**
|
||||
* @param entityCreature the entity to add the goal to.
|
||||
* @param meleeRange the allowed range the entity can hit others in melee.
|
||||
* @param rangedRange the allowed range the entity can shoot others.
|
||||
* @param rangedPower shot power (1 for normal).
|
||||
* @param rangedSpread shot spread (0 for best accuracy).
|
||||
* @param delay the delay between any attacks.
|
||||
* @param timeUnit the unit of the delay.
|
||||
* @param desirableRange the desirable range: the entity will try to stay no further than this distance.
|
||||
* @param comeClose if entity should go as close as possible to the target whether target is not in line of sight for a ranged attack.
|
||||
*/
|
||||
public CombinedAttackGoal(@NotNull EntityCreature entityCreature,
|
||||
int meleeRange, int rangedRange, double rangedPower, double rangedSpread,
|
||||
int delay, TimeUnit timeUnit,
|
||||
int desirableRange, boolean comeClose) {
|
||||
this(
|
||||
entityCreature,
|
||||
meleeRange, delay, timeUnit,
|
||||
rangedRange, rangedPower, rangedSpread, delay, timeUnit,
|
||||
desirableRange, comeClose
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entityCreature the entity to add the goal to.
|
||||
* @param meleeRange the allowed range the entity can hit others in melee.
|
||||
* @param meleeDelay the delay between melee attacks.
|
||||
* @param meleeTimeUnit the unit of the melee delay.
|
||||
* @param rangedRange the allowed range the entity can shoot others.
|
||||
* @param rangedPower shot power (1 for normal).
|
||||
* @param rangedSpread shot spread (0 for best accuracy).
|
||||
* @param rangedDelay the delay between ranged attacks.
|
||||
* @param rangedTimeUnit the unit of the ranged delay.
|
||||
* @param desirableRange the desirable range: the entity will try to stay no further than this distance.
|
||||
* @param comeClose if entity should go as close as possible to the target whether target is not in line of sight for a ranged attack.
|
||||
*/
|
||||
public CombinedAttackGoal(@NotNull EntityCreature entityCreature,
|
||||
int meleeRange, int meleeDelay, TimeUnit meleeTimeUnit,
|
||||
int rangedRange, double rangedPower, double rangedSpread, int rangedDelay, TimeUnit rangedTimeUnit,
|
||||
int desirableRange, boolean comeClose) {
|
||||
super(entityCreature);
|
||||
this.meleeRangeSquared = meleeRange * meleeRange;
|
||||
this.meleeDelay = meleeDelay;
|
||||
this.meleeTimeUnit = meleeTimeUnit;
|
||||
this.rangedRangeSquared = rangedRange * rangedRange;
|
||||
this.rangedPower = rangedPower;
|
||||
this.rangedSpread = rangedSpread;
|
||||
this.rangedDelay = rangedDelay;
|
||||
this.rangedTimeUnit = rangedTimeUnit;
|
||||
this.desirableRangeSquared = desirableRange * desirableRange;
|
||||
this.comeClose = comeClose;
|
||||
Check.argCondition(desirableRange > rangedRange, "Desirable range can not exceed ranged range!");
|
||||
}
|
||||
|
||||
public void setProjectileGenerator(Function<Entity, EntityProjectile> projectileGenerator) {
|
||||
this.projectileGenerator = projectileGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldStart() {
|
||||
this.cachedTarget = findTarget();
|
||||
return this.cachedTarget != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.entityCreature.getNavigator().setPathTo(this.cachedTarget.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(long time) {
|
||||
Entity target;
|
||||
if (this.cachedTarget != null) {
|
||||
target = this.cachedTarget;
|
||||
this.cachedTarget = null;
|
||||
} else {
|
||||
target = findTarget();
|
||||
}
|
||||
if (target == null) {
|
||||
this.stop = true;
|
||||
return;
|
||||
}
|
||||
double distanceSquared = this.entityCreature.getDistanceSquared(target);
|
||||
boolean comeClose = false;
|
||||
// First of all, checking if to perform melee or ranged attack depending on the distance to target.
|
||||
if (distanceSquared <= this.meleeRangeSquared) {
|
||||
if (!CooldownUtils.hasCooldown(time, this.lastAttack, this.meleeTimeUnit, this.meleeDelay)) {
|
||||
this.entityCreature.attack(target, true);
|
||||
this.lastAttack = time;
|
||||
}
|
||||
} else if (distanceSquared <= this.rangedRangeSquared) {
|
||||
if (!CooldownUtils.hasCooldown(time, this.lastAttack, this.rangedTimeUnit, this.rangedDelay)) {
|
||||
if (this.entityCreature.hasLineOfSight(target)) {
|
||||
// If target is on line of entity sight, ranged attack can be performed
|
||||
Position to = target.getPosition().clone().add(0D, target.getEyeHeight(), 0D);
|
||||
|
||||
Function<Entity, EntityProjectile> projectileGenerator = this.projectileGenerator;
|
||||
if (projectileGenerator == null) {
|
||||
projectileGenerator = shooter -> new EntityProjectile(shooter, EntityType.ARROW);
|
||||
}
|
||||
EntityProjectile projectile = projectileGenerator.apply(this.entityCreature);
|
||||
projectile.setInstance(this.entityCreature.getInstance(), this.entityCreature.getPosition());
|
||||
|
||||
projectile.shoot(to, this.rangedPower, this.rangedSpread);
|
||||
this.lastAttack = time;
|
||||
} else {
|
||||
// Otherwise deciding whether to go to the enemy.
|
||||
comeClose = this.comeClose;
|
||||
}
|
||||
}
|
||||
}
|
||||
Navigator navigator = this.entityCreature.getNavigator();
|
||||
Position pathPosition = navigator.getPathPosition();
|
||||
// If we don't want to come close and we're already within desirable range, no movement is needed.
|
||||
if (!comeClose && distanceSquared <= this.desirableRangeSquared) {
|
||||
if (pathPosition != null) {
|
||||
navigator.setPathTo(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Otherwise going to the target.
|
||||
Position targetPosition = target.getPosition();
|
||||
if (pathPosition == null || !pathPosition.isSimilar(targetPosition)) {
|
||||
navigator.setPathTo(targetPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnd() {
|
||||
return this.stop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
// Stop following the target
|
||||
this.entityCreature.getNavigator().setPathTo(null);
|
||||
}
|
||||
|
||||
}
|
@ -2,17 +2,17 @@ package net.minestom.server.entity.ai.goal;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
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.entity.type.projectile.EntityProjectile;
|
||||
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;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class RangedAttackGoal extends GoalSelector {
|
||||
|
||||
@ -25,7 +25,7 @@ public class RangedAttackGoal extends GoalSelector {
|
||||
private final double power;
|
||||
private final double spread;
|
||||
|
||||
private BiFunction<Entity, Position, Projectile> projectileGenerator;
|
||||
private Function<Entity, EntityProjectile> projectileGenerator;
|
||||
|
||||
private boolean stop;
|
||||
private Entity cachedTarget;
|
||||
@ -52,7 +52,7 @@ public class RangedAttackGoal extends GoalSelector {
|
||||
Check.argCondition(desirableRange > attackRange, "Desirable range can not exceed attack range!");
|
||||
}
|
||||
|
||||
public void setProjectileGenerator(BiFunction<Entity, Position, Projectile> projectileGenerator) {
|
||||
public void setProjectileGenerator(Function<Entity, EntityProjectile> projectileGenerator) {
|
||||
this.projectileGenerator = projectileGenerator;
|
||||
}
|
||||
|
||||
@ -87,13 +87,14 @@ public class RangedAttackGoal extends GoalSelector {
|
||||
if (this.entityCreature.hasLineOfSight(target)) {
|
||||
Position to = target.getPosition().clone().add(0D, target.getEyeHeight(), 0D);
|
||||
|
||||
BiFunction<Entity, Position, Projectile> projectileGenerator = this.projectileGenerator;
|
||||
Function<Entity, EntityProjectile> projectileGenerator = this.projectileGenerator;
|
||||
if (projectileGenerator == null) {
|
||||
projectileGenerator = EntityArrow::new;
|
||||
projectileGenerator = shooter -> new EntityProjectile(shooter, EntityType.ARROW);
|
||||
}
|
||||
Projectile projectile = projectileGenerator.apply(this.entityCreature, new Position(0D, 0D, 0D));
|
||||
EntityProjectile projectile = projectileGenerator.apply(this.entityCreature);
|
||||
projectile.setInstance(this.entityCreature.getInstance(), this.entityCreature.getPosition());
|
||||
|
||||
Projectile.shoot(projectile, this.entityCreature, to, this.power, this.spread);
|
||||
projectile.shoot(to, this.power, this.spread);
|
||||
this.lastShot = time;
|
||||
} else {
|
||||
comeClose = this.comeClose;
|
||||
|
@ -0,0 +1,13 @@
|
||||
package net.minestom.server.entity.metadata;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface ProjectileMeta {
|
||||
|
||||
@Nullable
|
||||
Entity getShooter();
|
||||
|
||||
void setShooter(@Nullable Entity shooter);
|
||||
|
||||
}
|
@ -3,10 +3,11 @@ package net.minestom.server.entity.metadata.arrow;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.ObjectDataProvider;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ArrowMeta extends AbstractArrowMeta implements ObjectDataProvider {
|
||||
public class ArrowMeta extends AbstractArrowMeta implements ObjectDataProvider, ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -22,11 +23,13 @@ public class ArrowMeta extends AbstractArrowMeta implements ObjectDataProvider {
|
||||
super.metadata.setIndex((byte) 9, Metadata.VarInt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return this.shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity shooter) {
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
@ -3,10 +3,11 @@ package net.minestom.server.entity.metadata.arrow;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.ObjectDataProvider;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SpectralArrowMeta extends AbstractArrowMeta implements ObjectDataProvider {
|
||||
public class SpectralArrowMeta extends AbstractArrowMeta implements ObjectDataProvider, ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -14,11 +15,13 @@ public class SpectralArrowMeta extends AbstractArrowMeta implements ObjectDataPr
|
||||
super(entity, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return this.shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity shooter) {
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
@ -3,11 +3,12 @@ package net.minestom.server.entity.metadata.item;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.ObjectDataProvider;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import net.minestom.server.item.Material;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FireballMeta extends ItemContainingMeta implements ObjectDataProvider {
|
||||
public class FireballMeta extends ItemContainingMeta implements ObjectDataProvider, ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -15,11 +16,13 @@ public class FireballMeta extends ItemContainingMeta implements ObjectDataProvid
|
||||
super(entity, metadata, Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity shooter) {
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
@ -3,11 +3,12 @@ package net.minestom.server.entity.metadata.item;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.ObjectDataProvider;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import net.minestom.server.item.Material;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SmallFireballMeta extends ItemContainingMeta implements ObjectDataProvider {
|
||||
public class SmallFireballMeta extends ItemContainingMeta implements ObjectDataProvider, ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -15,11 +16,13 @@ public class SmallFireballMeta extends ItemContainingMeta implements ObjectDataP
|
||||
super(entity, metadata, Material.FIRE_CHARGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity shooter) {
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.EntityMeta;
|
||||
import net.minestom.server.entity.metadata.ObjectDataProvider;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class DragonFireballMeta extends EntityMeta implements ObjectDataProvider {
|
||||
public class DragonFireballMeta extends EntityMeta implements ObjectDataProvider, ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -15,11 +16,13 @@ public class DragonFireballMeta extends EntityMeta implements ObjectDataProvider
|
||||
super(entity, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity shooter) {
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
@ -3,11 +3,12 @@ package net.minestom.server.entity.metadata.other;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.EntityMeta;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FireworkRocketMeta extends EntityMeta {
|
||||
public class FireworkRocketMeta extends EntityMeta implements ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -24,11 +25,13 @@ public class FireworkRocketMeta extends EntityMeta {
|
||||
super.metadata.setIndex((byte) 7, Metadata.Slot(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return this.shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity value) {
|
||||
this.shooter = value;
|
||||
Integer entityID = value == null ? null : value.getEntityId();
|
||||
|
@ -4,10 +4,11 @@ import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.EntityMeta;
|
||||
import net.minestom.server.entity.metadata.ObjectDataProvider;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class WitherSkullMeta extends EntityMeta implements ObjectDataProvider {
|
||||
public class WitherSkullMeta extends EntityMeta implements ObjectDataProvider, ProjectileMeta {
|
||||
|
||||
private Entity shooter;
|
||||
|
||||
@ -23,11 +24,13 @@ public class WitherSkullMeta extends EntityMeta implements ObjectDataProvider {
|
||||
super.metadata.setIndex((byte) 7, Metadata.Boolean(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShooter(@Nullable Entity shooter) {
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
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 org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public interface Projectile {
|
||||
|
||||
static void shoot(@NotNull Projectile projectile, @NotNull Entity shooter, Position to, double power, double spread) {
|
||||
Check.argCondition(!(projectile instanceof Entity), "Projectile must be an instance of Entity!");
|
||||
EntityShootEvent event = new EntityShootEvent(shooter, projectile, to, power, spread);
|
||||
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.getPower(), event.getSpread());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
static void shoot(@NotNull Projectile projectile, @NotNull Position from, @NotNull Position to, double power, 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;
|
||||
Vector velocity = proj.getVelocity();
|
||||
velocity.setX(dx);
|
||||
velocity.setY(dy);
|
||||
velocity.setZ(dz);
|
||||
velocity.multiply(20 * power);
|
||||
proj.setView(
|
||||
(float) Math.toDegrees(Math.atan2(dx, dz)),
|
||||
(float) Math.toDegrees(Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shooter of this projectile.
|
||||
*
|
||||
* @return the shooter of this projectile.
|
||||
*/
|
||||
@Nullable
|
||||
Entity getShooter();
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package net.minestom.server.entity.type.projectile;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.ObjectEntity;
|
||||
import net.minestom.server.entity.type.Projectile;
|
||||
import net.minestom.server.utils.Position;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class AbstractProjectile extends Entity implements Projectile {
|
||||
|
||||
private final Entity shooter;
|
||||
|
||||
public AbstractProjectile(@Nullable Entity shooter, @NotNull EntityType entityType, @NotNull Position spawnPosition) {
|
||||
super(entityType, spawnPosition);
|
||||
setGravity(0.02f, 0.04f, 1.96f);
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getShooter() {
|
||||
return this.shooter;
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package net.minestom.server.entity.type.projectile;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.arrow.ArrowMeta;
|
||||
import net.minestom.server.utils.Position;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EntityArrow extends EntityAbstractArrow {
|
||||
|
||||
public EntityArrow(@Nullable Entity shooter, @NotNull Position spawnPosition) {
|
||||
super(shooter, EntityType.ARROW, spawnPosition);
|
||||
((ArrowMeta) getEntityMeta()).setShooter(shooter);
|
||||
}
|
||||
|
||||
}
|
@ -13,10 +13,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @deprecated Use {@link net.minestom.server.entity.metadata.item.EyeOfEnderMeta} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class EntityEyeOfEnder extends AbstractProjectile {
|
||||
public class EntityEyeOfEnder extends Entity {
|
||||
|
||||
public EntityEyeOfEnder(@Nullable Entity shooter, @NotNull Position spawnPosition) {
|
||||
super(shooter, EntityType.EYE_OF_ENDER, spawnPosition);
|
||||
super(EntityType.EYE_OF_ENDER, spawnPosition);
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,10 +12,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @deprecated Use {@link net.minestom.server.entity.metadata.item.ThrownPotionMeta} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class EntityPotion extends AbstractProjectile {
|
||||
public class EntityPotion extends Entity {
|
||||
|
||||
public EntityPotion(@Nullable Entity shooter, @NotNull Position spawnPosition, @NotNull ItemStack potion) {
|
||||
super(shooter, EntityType.POTION, spawnPosition);
|
||||
super(EntityType.POTION, spawnPosition);
|
||||
setBoundingBox(0.25f, 0.25f, 0.25f);
|
||||
setPotion(potion);
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ package net.minestom.server.entity.type.projectile;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.LivingEntity;
|
||||
import net.minestom.server.entity.Metadata;
|
||||
import net.minestom.server.entity.metadata.ProjectileMeta;
|
||||
import net.minestom.server.event.entity.EntityAttackEvent;
|
||||
import net.minestom.server.event.entity.EntityShootEvent;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
@ -16,17 +17,94 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class EntityAbstractArrow extends AbstractProjectile {
|
||||
/**
|
||||
* Class that allows to instantiate entities with projectile-like physics handling.
|
||||
*/
|
||||
public class EntityProjectile extends Entity {
|
||||
|
||||
EntityAbstractArrow(@Nullable Entity shooter, @NotNull EntityType entityType, @NotNull Position spawnPosition) {
|
||||
super(shooter, entityType, spawnPosition);
|
||||
private final Entity shooter;
|
||||
|
||||
public EntityProjectile(@Nullable Entity shooter, @NotNull EntityType entityType) {
|
||||
super(entityType);
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public EntityProjectile(@Nullable Entity shooter, @NotNull EntityType entityType, @NotNull Position spawnPosition) {
|
||||
super(entityType, spawnPosition);
|
||||
this.shooter = shooter;
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
super.hasPhysics = false;
|
||||
|
||||
if (getEntityMeta() instanceof ProjectileMeta) {
|
||||
((ProjectileMeta) getEntityMeta()).setShooter(this.shooter);
|
||||
}
|
||||
setBoundingBox(.5F, .5F, .5F);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Entity getShooter() {
|
||||
return this.shooter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this projectile is stuck in blocks.
|
||||
* Probably you want to do nothing with arrows in such case and to remove other types of projectiles.
|
||||
*/
|
||||
public void onStuck() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this projectile unstucks.
|
||||
* Probably you want to add some random velocity to arrows in such case.
|
||||
*/
|
||||
public void onUnstuck() {
|
||||
|
||||
}
|
||||
|
||||
public void shoot(Position to, double power, double spread) {
|
||||
EntityShootEvent event = new EntityShootEvent(this.shooter, this, to, power, spread);
|
||||
this.shooter.callEvent(EntityShootEvent.class, event);
|
||||
if (event.isCancelled()) {
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
Position from = this.shooter.getPosition().clone().add(0D, this.shooter.getEyeHeight(), 0D);
|
||||
shoot(from, to, event.getPower(), event.getSpread());
|
||||
}
|
||||
|
||||
private void shoot(@NotNull Position from, @NotNull Position to, double power, double spread) {
|
||||
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;
|
||||
super.velocity.setX(dx);
|
||||
super.velocity.setY(dy);
|
||||
super.velocity.setZ(dz);
|
||||
super.velocity.multiply(20 * power);
|
||||
setView(
|
||||
(float) Math.toDegrees(Math.atan2(dx, dz)),
|
||||
(float) Math.toDegrees(Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(long time) {
|
||||
Position posBefore = getPosition().clone();
|
||||
@ -40,12 +118,14 @@ public class EntityAbstractArrow extends AbstractProjectile {
|
||||
getVelocity().zero();
|
||||
sendPacketToViewersAndSelf(getVelocityPacket());
|
||||
setNoGravity(true);
|
||||
onStuck();
|
||||
} else {
|
||||
if (!super.onGround) {
|
||||
return;
|
||||
}
|
||||
super.onGround = false;
|
||||
setNoGravity(false);
|
||||
onUnstuck();
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
package net.minestom.server.entity.type.projectile;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.metadata.arrow.SpectralArrowMeta;
|
||||
import net.minestom.server.utils.Position;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EntitySpectralArrow extends EntityAbstractArrow {
|
||||
|
||||
public EntitySpectralArrow(@Nullable Entity shooter, @NotNull Position spawnPosition) {
|
||||
super(shooter, EntityType.SPECTRAL_ARROW, spawnPosition);
|
||||
((SpectralArrowMeta) getEntityMeta()).setShooter(shooter);
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +1,24 @@
|
||||
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, double)}.
|
||||
* Called with {@link net.minestom.server.entity.type.projectile.EntityProjectile#shoot(Position, double, double)}
|
||||
*/
|
||||
public class EntityShootEvent extends EntityEvent implements CancellableEvent {
|
||||
|
||||
private final Projectile projectile;
|
||||
private final Entity projectile;
|
||||
private final Position to;
|
||||
private double power;
|
||||
private double spread;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public EntityShootEvent(@NotNull Entity entity, @NotNull Projectile projectile, @NotNull Position to, double power, double spread) {
|
||||
public EntityShootEvent(@NotNull Entity entity, @NotNull Entity projectile, @NotNull Position to, double power, double spread) {
|
||||
super(entity);
|
||||
this.projectile = projectile;
|
||||
this.to = to;
|
||||
@ -32,7 +31,7 @@ public class EntityShootEvent extends EntityEvent implements CancellableEvent {
|
||||
*
|
||||
* @return the projectile.
|
||||
*/
|
||||
public Projectile getProjectile() {
|
||||
public Entity getProjectile() {
|
||||
return this.projectile;
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,10 @@ 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.EntityType;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.metadata.arrow.ArrowMeta;
|
||||
import net.minestom.server.entity.type.Projectile;
|
||||
import net.minestom.server.entity.type.projectile.EntityArrow;
|
||||
import net.minestom.server.entity.type.projectile.EntitySpectralArrow;
|
||||
import net.minestom.server.entity.type.projectile.EntityProjectile;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@ -42,28 +40,28 @@ public class ShootCommand extends Command {
|
||||
}
|
||||
|
||||
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);
|
||||
Player player = (Player) sender;
|
||||
String mode = args.getWord("type");
|
||||
EntityProjectile projectile;
|
||||
switch (mode) {
|
||||
case "default":
|
||||
projectile = new EntityArrow(player, pos);
|
||||
projectile = new EntityProjectile(player, EntityType.ARROW);
|
||||
break;
|
||||
case "spectral":
|
||||
projectile = new EntitySpectralArrow(player, pos);
|
||||
projectile = new EntityProjectile(player, EntityType.SPECTRAL_ARROW);
|
||||
break;
|
||||
case "colored":
|
||||
projectile = new EntityArrow(player, pos);
|
||||
var meta = (ArrowMeta) ((Entity) projectile).getEntityMeta();
|
||||
projectile = new EntityProjectile(player, EntityType.ARROW);
|
||||
var meta = (ArrowMeta) projectile.getEntityMeta();
|
||||
meta.setColor(ThreadLocalRandom.current().nextInt());
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
((Entity) projectile).setInstance(player.getInstance());
|
||||
var pos = player.getPosition().clone().add(0D, player.getEyeHeight(), 0D);
|
||||
projectile.setInstance(player.getInstance(), pos);
|
||||
var dir = pos.getDirection().multiply(30D);
|
||||
pos = pos.clone().add(dir.getX(), dir.getY(), dir.getZ());
|
||||
Projectile.shoot(projectile, player, pos, 1D, 0D);
|
||||
projectile.shoot(pos, 1D, 0D);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user