diff --git a/src/main/java/de/teamlapen/vampirism/core/ModEntities.java b/src/main/java/de/teamlapen/vampirism/core/ModEntities.java index 224c25d79..5ddc5aeef 100755 --- a/src/main/java/de/teamlapen/vampirism/core/ModEntities.java +++ b/src/main/java/de/teamlapen/vampirism/core/ModEntities.java @@ -65,7 +65,7 @@ public class ModEntities { public static final DeferredHolder, EntityType> CONVERTED_SHEEP = prepareEntityType("converted_sheep", () -> EntityType.Builder.of(ConvertedSheepEntity::new, MobCategory.CREATURE).sized(0.9F, 1.3F), false); public static final DeferredHolder, EntityType> CONVERTED_COW = prepareEntityType("converted_cow", () -> EntityType.Builder.of(ConvertedCowEntity::new, MobCategory.CREATURE).sized(0.9F, 1.4F), false); public static final DeferredHolder, EntityType> CROSSBOW_ARROW = prepareEntityType("crossbow_arrow", () -> EntityType.Builder.of(CrossbowArrowEntity::new, MobCategory.MISC).sized(0.5F, 0.5F), false); - public static final DeferredHolder, EntityType> DARK_BLOOD_PROJECTILE = prepareEntityType("dark_blood_projectile", () -> EntityType.Builder.of(DarkBloodProjectileEntity::new, MobCategory.MISC).sized(0.6F, 0.6F).fireImmune(), false); + public static final DeferredHolder, EntityType> DARK_BLOOD_PROJECTILE = prepareEntityType("dark_blood_projectile", () -> EntityType.Builder.of(DarkBloodProjectileEntity::new, MobCategory.MISC).sized(0.6F, 0.6F).fireImmune().clientTrackingRange(20), false); public static final DeferredHolder, EntityType> HUNTER_TRAINER_DUMMY = prepareEntityType("hunter_trainer_dummy", () -> EntityType.Builder.of(DummyHunterTrainerEntity::new, MobCategory.MISC).sized(0.6F, 1.95F), true); public static final DeferredHolder, EntityType> PARTICLE_CLOUD = prepareEntityType("particle_cloud", () -> EntityType.Builder.of(AreaParticleCloudEntity::new, MobCategory.MISC).sized(6.0F, 0.5F).fireImmune(), false); public static final DeferredHolder, EntityType> SOUL_ORB = prepareEntityType("soul_orb", () -> EntityType.Builder.of(SoulOrbEntity::new, MobCategory.MISC).sized(0.25F, 0.25F).fireImmune(), false); diff --git a/src/main/java/de/teamlapen/vampirism/core/ModParticles.java b/src/main/java/de/teamlapen/vampirism/core/ModParticles.java index 94abb3c31..e1939e795 100644 --- a/src/main/java/de/teamlapen/vampirism/core/ModParticles.java +++ b/src/main/java/de/teamlapen/vampirism/core/ModParticles.java @@ -91,7 +91,9 @@ public static void spawnParticleClient(@NotNull Level worldIn, @NotNull Particle * @param zOffset Used for random offset * @param speed Direction is randomized but multiplied by x/y/zOffset * @return Number of players this has been sent to. + * @deprecated Use {@link ServerLevel#sendParticles(ParticleOptions, double, double, double, int, double, double, double, double)} directly */ + @Deprecated public static int spawnParticlesServer(Level worldIn, @NotNull ParticleOptions particle, double posX, double posY, double posZ, int particleCount, double xOffset, double yOffset, double zOffset, double speed) { assert worldIn instanceof ServerLevel : "Calling spawnParticlesServer on client side is pointless"; if (worldIn instanceof ServerLevel) { diff --git a/src/main/java/de/teamlapen/vampirism/entity/DarkBloodProjectileEntity.java b/src/main/java/de/teamlapen/vampirism/entity/DarkBloodProjectileEntity.java index dbcda629e..cd01d663b 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/DarkBloodProjectileEntity.java +++ b/src/main/java/de/teamlapen/vampirism/entity/DarkBloodProjectileEntity.java @@ -2,13 +2,13 @@ import de.teamlapen.vampirism.api.util.VResourceLocation; import de.teamlapen.vampirism.core.ModEntities; -import de.teamlapen.vampirism.core.ModParticles; import de.teamlapen.vampirism.core.ModSounds; import de.teamlapen.vampirism.particle.GenericParticleOptions; import de.teamlapen.vampirism.util.DamageHandler; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffectInstance; @@ -17,7 +17,6 @@ import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; import net.minecraft.world.level.Level; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; @@ -32,7 +31,7 @@ *

* Damages directly hit entities but also has a small area of effect damage */ -public class DarkBloodProjectileEntity extends AbstractHurtingProjectile { +public class DarkBloodProjectileEntity extends HomingProjectile { protected float directDamage = 4; protected float indirectDamage = 2; @@ -46,17 +45,6 @@ public DarkBloodProjectileEntity(@NotNull EntityType pEntityType, Level pLevel) { + super(pEntityType, pLevel); + } + + public HomingProjectile(EntityType pEntityType, double pX, double pY, double pZ, Vec3 direction, Level pLevel) { + super(pEntityType, pX, pY, pZ, direction, pLevel); + this.direction = direction; + } + + public HomingProjectile(EntityType pEntityType, LivingEntity pShooter, Vec3 offset, Level pLevel) { + super(pEntityType, pShooter, offset, pLevel); + } + + public void setTarget(@Nullable LivingEntity cachedTarget) { + if (cachedTarget != null) { + this.targetUUID = cachedTarget.getUUID(); + this.cachedTarget = cachedTarget; + } + } + + @Override + public void addAdditionalSaveData(@NotNull CompoundTag pCompound) { + super.addAdditionalSaveData(pCompound); + if (this.targetUUID != null) { + pCompound.putUUID("target", this.targetUUID); + } + } + + @Override + public void readAdditionalSaveData(@NotNull CompoundTag pCompound) { + super.readAdditionalSaveData(pCompound); + if (pCompound.hasUUID("target")) { + this.targetUUID = pCompound.getUUID("target"); + } + } + + @Nullable + protected LivingEntity getTarget() { + if (targetUUID != null) { + if (this.cachedTarget != null) { + if (this.cachedTarget.isRemoved()) { + this.cachedTarget = null; + } else { + return this.cachedTarget; + } + } else if (this.level() instanceof ServerLevel serverLevel) { + this.cachedTarget = (LivingEntity) serverLevel.getEntity(this.targetUUID); + return this.cachedTarget; + } + } + return null; + } + + @Override + public void tick() { + super.tick(); + LivingEntity target = getTarget(); + if (target != null) { + Vec3 updatedTarget = target.getEyePosition(); + Vec3 position = position(); + Vec3 idealDirection = updatedTarget.subtract(position); + Vec3 currentDirection = this.direction; + + double angleBetween = Math.acos(idealDirection.dot(currentDirection)); + double maxAngle = Math.toRadians(20); + + if (angleBetween > maxAngle) { + Vec3 rotationAxis = currentDirection.cross(idealDirection).normalize(); + + // Create a quaternion for the rotation + Quaterniond rotation = new Quaterniond().rotationAxis(maxAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); + + // Apply the rotation to the current direction + Vector3d newDir = rotation.transform(new Vector3d(currentDirection.x, currentDirection.y, currentDirection.z)); + + this.direction = new Vec3(newDir.x, newDir.y, newDir.z); + } else { + this.direction = idealDirection; + } + + // use the acceleration from the superclass + double length = getDeltaMovement().length(); + + this.setDeltaMovement(this.direction.normalize().scale(length)); + } + } +} diff --git a/src/main/java/de/teamlapen/vampirism/entity/player/vampire/actions/DarkBloodProjectileAction.java b/src/main/java/de/teamlapen/vampirism/entity/player/vampire/actions/DarkBloodProjectileAction.java index 06aba0387..6b839c17e 100644 --- a/src/main/java/de/teamlapen/vampirism/entity/player/vampire/actions/DarkBloodProjectileAction.java +++ b/src/main/java/de/teamlapen/vampirism/entity/player/vampire/actions/DarkBloodProjectileAction.java @@ -8,7 +8,11 @@ import de.teamlapen.vampirism.core.ModRefinements; import de.teamlapen.vampirism.entity.DarkBloodProjectileEntity; import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; @@ -54,12 +58,17 @@ protected IActionResult activate(@NotNull IVampirePlayer player, ActivationConte if (skillHandler.isRefinementEquipped(ModRefinements.DARK_BLOOD_PROJECTILE_SPEED)) { speed = 1.4f; } + Vec3 startPosition = player.asEntity().getEyePosition(); + Vec3 direction = player.asEntity().getViewVector(1.0F); + Vec3 endPosition = startPosition.add(direction.x * 200.0, direction.y * 200.0, direction.z * 200.0); + EntityHitResult entityHitResult = ProjectileUtil.getEntityHitResult(player.asEntity().level(), player.asEntity(), startPosition, endPosition, new AABB(startPosition, endPosition), (e) -> e instanceof LivingEntity && !e.isSpectator(), 0); + LivingEntity target = entityHitResult != null && entityHitResult.getEntity() instanceof LivingEntity livingEntity ? livingEntity : null; if (skillHandler.isRefinementEquipped(ModRefinements.DARK_BLOOD_PROJECTILE_AOE)) { for (int i = 0; i < 32; i++) { Vec3 vec3d = getRotationVector(shooter.getViewXRot(1.0f), shooter.getViewYRot(1.0f) + i * 11.25f); - DarkBloodProjectileEntity entity = createProjectile(shooter, shooter.position(), 0, vec3d, false, 0, 0, 0.95f); - entity.setMaxTicks(7); + DarkBloodProjectileEntity entity = createProjectile(shooter, shooter.position(), vec3d, false, 0, 0, speed, null); + entity.setMaxTicks(15); entity.excludeShooter(); if (i == 0) { entity.setDamage(0, directDamage); @@ -68,20 +77,21 @@ protected IActionResult activate(@NotNull IVampirePlayer player, ActivationConte } } else { boolean goThrough = skillHandler.isRefinementEquipped(ModRefinements.DARK_BLOOD_PROJECTILE_PENETRATION); - createProjectile(shooter, shooter.position(), shooter.getEyeHeight() * 0.9f, shooter.getViewVector(1.0F), goThrough, directDamage, indirectDamage, speed); + createProjectile(shooter, shooter.getEyePosition(), shooter.getViewVector(1.0F), goThrough, directDamage, indirectDamage, speed, target); if (skillHandler.isRefinementEquipped(ModRefinements.DARK_BLOOD_PROJECTILE_MULTI_SHOT)) { - createProjectile(shooter, shooter.position(), shooter.getEyeHeight() * 0.9f, getVectorForRotation(shooter.getViewXRot(1.0f), shooter.getViewYRot(1.0f) + 30f), goThrough, directDamage, indirectDamage, speed); - createProjectile(shooter, shooter.position(), shooter.getEyeHeight() * 0.9f, getVectorForRotation(shooter.getViewXRot(1.0f), shooter.getViewYRot(1.0f) - 30f), goThrough, directDamage, indirectDamage, speed); + createProjectile(shooter, shooter.getEyePosition(), getVectorForRotation(shooter.getViewXRot(1.0f), shooter.getViewYRot(1.0f) + 30f), goThrough, directDamage, indirectDamage, speed, target); + createProjectile(shooter, shooter.getEyePosition(), getVectorForRotation(shooter.getViewXRot(1.0f), shooter.getViewYRot(1.0f) - 30f), goThrough, directDamage, indirectDamage, speed, target); } } return IActionResult.SUCCESS; } - private @NotNull DarkBloodProjectileEntity createProjectile(@NotNull Player shooter, @NotNull Vec3 position, double height, @NotNull Vec3 direction, boolean goThrough, float directDamage, float indirectDamage, float speed) { - DarkBloodProjectileEntity entity = new DarkBloodProjectileEntity(shooter.getCommandSenderWorld(), position.x + direction.x, position.y + height, position.z + direction.z, direction); + private @NotNull DarkBloodProjectileEntity createProjectile(@NotNull Player shooter, @NotNull Vec3 position, @NotNull Vec3 direction, boolean goThrough, float directDamage, float indirectDamage, float speed, LivingEntity target) { + DarkBloodProjectileEntity entity = new DarkBloodProjectileEntity(shooter.getCommandSenderWorld(), position.x + direction.x, position.y + direction.y, position.z + direction.z, direction); entity.setMotionFactor(speed); entity.setOwner(shooter); entity.setDamage(directDamage, indirectDamage); + entity.setTarget(target); if (goThrough) { entity.setGothrough(true); } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 7da90dd77..c91c4180f 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -67,6 +67,7 @@ protected net.minecraft.world.entity.ai.goal.RangedCrossbowAttackGoal crossbowSt protected net.minecraft.world.entity.ai.goal.RangedCrossbowAttackGoal canRun()Z protected net.minecraft.world.inventory.AbstractContainerMenu remoteSlots protected net.minecraft.world.inventory.AbstractFurnaceMenu data +protected net.minecraft.world.entity.projectile.AbstractHurtingProjectile assignPower(DDD)V public net.minecraft.world.item.enchantment.EnchantmentHelper runIterationOnItem(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/enchantment/EnchantmentHelper$EnchantmentVisitor;)V public net.minecraft.world.item.enchantment.Enchantment modifyItemFilteredCount(Lnet/minecraft/core/component/DataComponentType;Lnet/minecraft/server/level/ServerLevel;ILnet/minecraft/world/item/ItemStack;Lorg/apache/commons/lang3/mutable/MutableFloat;)V public net.minecraft.data.models.BlockModelGenerators createBooleanModelDispatch(Lnet/minecraft/world/level/block/state/properties/BooleanProperty;Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/data/models/blockstates/PropertyDispatch;