diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/TextDisplayEntity.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/TextDisplayEntity.java index b0abe6a25..d3a87821b 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/TextDisplayEntity.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/TextDisplayEntity.java @@ -10,7 +10,6 @@ public class TextDisplayEntity extends LivingEntity { - public TextDisplayEntity(Component text, Consumer metaConsumer) { super(EntityType.TEXT_DISPLAY); @@ -22,4 +21,5 @@ public TextDisplayEntity(Component text, Consumer metaConsumer) metaConsumer.accept(meta); }); } + } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/SkyBlockMob.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/SkyBlockMob.java index 75d042aec..dd57f40de 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/SkyBlockMob.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/SkyBlockMob.java @@ -14,7 +14,7 @@ import net.minestom.server.entity.ai.TargetSelector; import net.minestom.server.entity.attribute.Attribute; import net.minestom.server.entity.damage.Damage; -import net.minestom.server.entity.metadata.avatar.PlayerMeta; +import net.minestom.server.entity.metadata.display.TextDisplayMeta; import net.minestom.server.instance.Instance; import net.minestom.server.network.packet.server.play.EntityHeadLookPacket; import net.minestom.server.network.packet.server.play.EntityMetaDataPacket; @@ -29,6 +29,7 @@ import net.swofty.type.generic.utility.MathUtility; import net.swofty.type.skyblockgeneric.SkyBlockGenericLoader; import net.swofty.type.skyblockgeneric.entity.DroppedItemEntityImpl; +import net.swofty.type.skyblockgeneric.entity.TextDisplayEntity; import net.swofty.type.skyblockgeneric.entity.mob.impl.MobPlayerSkin; import net.swofty.type.skyblockgeneric.entity.mob.impl.RegionPopulator; import net.swofty.type.skyblockgeneric.event.custom.PlayerKilledSkyBlockMobEvent; @@ -59,6 +60,7 @@ public abstract class SkyBlockMob extends EntityCreature { private boolean hasBeenDamaged = false; private Component customName; + private TextDisplayEntity nameDisplayEntity; public SkyBlockMob(EntityType entityType) { super(entityType); @@ -82,7 +84,8 @@ private void init() { + "§f/§a" + Math.round(getBaseStatistics().getOverall(ItemStatistic.HEALTH).floatValue()) ); - this.set(DataComponents.CUSTOM_NAME, customName); + this.set(DataComponents.CUSTOM_NAME, customName); // ARI - could be removed + nameDisplayEntity = new TextDisplayEntity(customName, meta -> meta.setTranslation(new Pos(0, getNameDisplayHeightOffset(), 0))); setAutoViewable(true); setAutoViewEntities(true); @@ -107,7 +110,7 @@ public void updateNewViewer(@NotNull Player player) { false, 0, GameMode.SURVIVAL, - customName, + customName, // ARI - this is only used in tab for some reason null, 1, true)), new SpawnEntityPacket(this.getEntityId(), this.getUuid(), EntityType.PLAYER, @@ -128,6 +131,8 @@ public void updateNewViewer(@NotNull Player player) { public void spawn() { super.spawn(); mobs.add(this); + addPassenger(nameDisplayEntity); + updateCustomName(customName); onSpawn(); } @@ -139,6 +144,10 @@ public void onSpawn() { // override this } + public float getNameDisplayHeightOffset() { + return 0.3f; + } + public abstract String getDisplayName(); public abstract Integer getLevel(); public abstract List getGoalSelectors(); @@ -180,7 +189,7 @@ public boolean damage(@NotNull Damage damage) { -Math.cos(sourcePoint.getPosition().yaw() * Math.PI / 180)); } - this.set(DataComponents.CUSTOM_NAME, Component.text( + updateCustomName(Component.text( "§8[§7Lv" + getLevel() + "§8] §c" + getDisplayName() + " §a" + Math.round(getHealth()) + "§f/§a" @@ -251,6 +260,14 @@ public void kill() { } } + public void updateCustomName(Component newName) { + this.customName = newName; + this.set(DataComponents.CUSTOM_NAME, customName); + if (nameDisplayEntity != null) { + nameDisplayEntity.editEntityMeta(TextDisplayMeta.class, meta -> meta.setTranslation(new Pos(0, getNameDisplayHeightOffset(), 0))); + } + } + public static void runRegionPopulators(Scheduler scheduler) { scheduler.submitTask(() -> { if (SkyBlockGenericLoader.getLoadedPlayers().isEmpty()) return TaskSchedule.seconds(10); diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/GroundNodeLockedPitchFollower.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/GroundNodeLockedPitchFollower.java new file mode 100644 index 000000000..4132fcf87 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/GroundNodeLockedPitchFollower.java @@ -0,0 +1,79 @@ +package net.swofty.type.skyblockgeneric.entity.mob.ai; + +import net.minestom.server.collision.CollisionUtils; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.pathfinding.followers.NodeFollower; +import net.minestom.server.utils.position.PositionUtils; +import org.jetbrains.annotations.Nullable; + +public class GroundNodeLockedPitchFollower implements NodeFollower { + private final Entity entity; + private final int pitch; + + public GroundNodeLockedPitchFollower(Entity entity, int pitch) { + this.entity = entity; + this.pitch = pitch; + } + + /** + * Used to move the entity toward {@code direction} in the X and Z axis + * Gravity is still applied but the entity will not attempt to jump + * Also update the yaw/pitch of the entity to look along 'direction' + * + * @param direction the targeted position + * @param speed define how far the entity will move + */ + public void moveTowards(Point direction, double speed, Point lookAt) { + final Pos position = entity.getPosition(); + final double dx = direction.x() - position.x(); + final double dy = direction.y() - position.y(); + final double dz = direction.z() - position.z(); + + final double dxLook = lookAt.x() - position.x(); + final double dzLook = lookAt.z() - position.z(); + + // the purpose of these few lines is to slow down entities when they reach their destination + final double distSquared = dx * dx + dy * dy + dz * dz; + if (speed > distSquared) { + speed = distSquared; + } + + final double radians = Math.atan2(dz, dx); + final double speedX = Math.cos(radians) * speed; + final double speedZ = Math.sin(radians) * speed; + final float yaw = PositionUtils.getLookYaw(dxLook, dzLook); + + final var physicsResult = CollisionUtils.handlePhysics(entity, new Vec(speedX, 0, speedZ)); + this.entity.refreshPosition(physicsResult.newPosition().asPos().withView(yaw, this.pitch)); + } + + @Override + public void jump(@Nullable Point point, @Nullable Point target) { + if (entity.isOnGround()) { + jump(4f); + } + } + + @Override + public boolean isAtPoint(Point point) { + return entity.getPosition().sameBlock(point); + } + + public void jump(float height) { + this.entity.setVelocity(new Vec(0, height * 2.5f, 0)); + } + + @Override + public double movementSpeed() { + if (entity instanceof LivingEntity living) { + return living.getAttribute(Attribute.MOVEMENT_SPEED).getValue(); + } + + return 0.1f; + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/MeleeAttackWithinRegionGoal.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/MeleeAttackWithinRegionGoal.java index 5f495c107..b5b3d583f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/MeleeAttackWithinRegionGoal.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/ai/MeleeAttackWithinRegionGoal.java @@ -26,10 +26,17 @@ public class MeleeAttackWithinRegionGoal extends GoalSelector { private boolean stop; private Entity cachedTarget; + private boolean lookAt = true; + public MeleeAttackWithinRegionGoal(@NotNull EntityCreature entityCreature, double range, int delay, @NotNull TemporalUnit timeUnit, RegionType type) { this(entityCreature, range, Duration.of(delay, timeUnit), type); } + public MeleeAttackWithinRegionGoal(@NotNull EntityCreature entityCreature, double range, int delay, @NotNull TemporalUnit timeUnit, RegionType type, boolean lookAt) { + this(entityCreature, range, Duration.of(delay, timeUnit), type); + this.lookAt = lookAt; + } + public MeleeAttackWithinRegionGoal(@NotNull EntityCreature entityCreature, double range, Duration delay, RegionType type) { super(entityCreature); this.range = range; @@ -64,7 +71,9 @@ public void tick(long time) { if (!stop) { // Attack the target entity if (entityCreature.getDistanceSquared(target) <= range * range) { - entityCreature.lookAt(target); + if (lookAt) { + entityCreature.lookAt(target); + } if (!Cooldown.hasCooldown(time, lastHit, delay) && !entityCreature.isDead()) { entityCreature.attack(target, true); this.lastHit = time; diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/mobs/dwarvenmines/goblin/MobGoblin.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/mobs/dwarvenmines/goblin/MobGoblin.java index e413e833a..99d6b16f5 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/mobs/dwarvenmines/goblin/MobGoblin.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/entity/mob/mobs/dwarvenmines/goblin/MobGoblin.java @@ -5,7 +5,6 @@ import net.minestom.server.entity.ai.GoalSelector; import net.minestom.server.entity.ai.TargetSelector; import net.minestom.server.entity.ai.target.LastEntityDamagerTarget; -import net.minestom.server.item.Material; import net.minestom.server.utils.time.TimeUnit; import net.swofty.commons.skyblock.item.ItemType; import net.swofty.commons.skyblock.statistics.ItemStatistic; @@ -14,11 +13,11 @@ import net.swofty.type.skyblockgeneric.entity.mob.BestiaryMob; import net.swofty.type.skyblockgeneric.entity.mob.MobType; import net.swofty.type.skyblockgeneric.entity.mob.ai.ClosestEntityRegionTarget; +import net.swofty.type.skyblockgeneric.entity.mob.ai.GroundNodeLockedPitchFollower; import net.swofty.type.skyblockgeneric.entity.mob.ai.MeleeAttackWithinRegionGoal; import net.swofty.type.skyblockgeneric.entity.mob.ai.RandomRegionStrollGoal; import net.swofty.type.skyblockgeneric.entity.mob.impl.MobPlayerSkin; import net.swofty.type.skyblockgeneric.entity.mob.impl.RegionPopulator; -import net.swofty.type.skyblockgeneric.item.SkyBlockItem; import net.swofty.type.skyblockgeneric.loottable.OtherLoot; import net.swofty.type.skyblockgeneric.loottable.SkyBlockLootTable; import net.swofty.type.skyblockgeneric.region.RegionType; @@ -26,7 +25,6 @@ import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.tinylog.Logger; import java.util.List; @@ -66,6 +64,21 @@ public String getDisplayName() { return "Goblin"; } + @Override + public void onInit() { + getNavigator().setNodeFollower(() -> new GroundNodeLockedPitchFollower(this, 90)); + } + + @Override + public void onSpawn() { + setView(getPosition().yaw(), 90, getPosition().yaw()); + } + + @Override + public float getNameDisplayHeightOffset() { + return 0.1f; + } + @Override public List getGoalSelectors() { return List.of( @@ -73,7 +86,7 @@ public List getGoalSelectors() { 1.6, 20, TimeUnit.SERVER_TICK, - RegionType.GOBLIN_BURROWS), + RegionType.GOBLIN_BURROWS, false), new RandomRegionStrollGoal(this, 5, RegionType.GOBLIN_BURROWS) ); }