diff --git a/changelog.md b/changelog.md index 302f933e..99b0e467 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,9 @@ * REGENERATE THE CONFIG * Wither and Dragon config options now scale with max difficulty instead of current difficulty * E.g. Dragon bonus health was 25 per difficulty, now is 200 at max difficulty +* Wither Minion + * Shoot every 1.5 seconds instead of 2 (1 in hard) + * No longer strafe when attacking * Changed Ender Dragon enchanted golden apple drop (~~10%/20%/30%/40%~~ -> 10%/20%/30%/40%/50% chance at difficulties ~~5/6/7/8~~ -> 4/5/6/7/8) * Removed "Apply to Vanilla Wither" config option diff --git a/src/main/java/insane96mcp/progressivebosses/module/dragon/feature/AttackFeature.java b/src/main/java/insane96mcp/progressivebosses/module/dragon/feature/AttackFeature.java index de4e75b7..c1730493 100644 --- a/src/main/java/insane96mcp/progressivebosses/module/dragon/feature/AttackFeature.java +++ b/src/main/java/insane96mcp/progressivebosses/module/dragon/feature/AttackFeature.java @@ -296,10 +296,10 @@ private boolean onImpact3DCloud(DragonFireball fireball, HitResult result) { HitResult.Type raytraceresult$type = result.getType(); if (raytraceresult$type == HitResult.Type.ENTITY) { - Reflection.ProjectileEntity_onEntityHit(fireball, (EntityHitResult)result); + Reflection.Projectile_onHitEntity(fireball, (EntityHitResult)result); } else if (raytraceresult$type == HitResult.Type.BLOCK) { - Reflection.ProjectileEntity_onBlockHit(fireball, (BlockHitResult)result); + Reflection.Projectile_onHitBlock(fireball, (BlockHitResult)result); } Entity entity = fireball.getOwner(); if (entity != null && (result.getType() != HitResult.Type.ENTITY || !((EntityHitResult)result).getEntity().is(entity))) { diff --git a/src/main/java/insane96mcp/progressivebosses/module/wither/ai/RangedMinionAttackGoal.java b/src/main/java/insane96mcp/progressivebosses/module/wither/ai/RangedMinionAttackGoal.java new file mode 100644 index 00000000..d6e3d7c6 --- /dev/null +++ b/src/main/java/insane96mcp/progressivebosses/module/wither/ai/RangedMinionAttackGoal.java @@ -0,0 +1,102 @@ +package insane96mcp.progressivebosses.module.wither.ai; + +import insane96mcp.progressivebosses.module.wither.entity.WitherMinion; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.item.BowItem; + +import java.util.EnumSet; + +// Same as RangedBowAttackGoal but without the strafing +public class RangedMinionAttackGoal extends Goal { + private final WitherMinion mob; + private final double speedModifier; + private int attackIntervalMin; + private final float attackRadiusSqr; + private int attackTime = -1; + private int seeTime; + + public RangedMinionAttackGoal(WitherMinion p_25792_, double p_25793_, int p_25794_, float p_25795_) { + this.mob = p_25792_; + this.speedModifier = p_25793_; + this.attackIntervalMin = p_25794_; + this.attackRadiusSqr = p_25795_ * p_25795_; + this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); + } + + public void setMinAttackInterval(int p_25798_) { + this.attackIntervalMin = p_25798_; + } + + public boolean canUse() { + return this.mob.getTarget() != null && this.isHoldingBow(); + } + + protected boolean isHoldingBow() { + return this.mob.isHolding(is -> is.getItem() instanceof BowItem); + } + + public boolean canContinueToUse() { + return (this.canUse() || !this.mob.getNavigation().isDone()) && this.isHoldingBow(); + } + + public void start() { + super.start(); + this.mob.setAggressive(true); + } + + public void stop() { + super.stop(); + this.mob.setAggressive(false); + this.seeTime = 0; + this.attackTime = -1; + this.mob.stopUsingItem(); + } + + public boolean requiresUpdateEveryTick() { + return true; + } + + public void tick() { + LivingEntity livingentity = this.mob.getTarget(); + if (livingentity != null) { + double d0 = this.mob.distanceToSqr(livingentity.getX(), livingentity.getY(), livingentity.getZ()); + boolean flag = this.mob.getSensing().hasLineOfSight(livingentity); + boolean flag1 = this.seeTime > 0; + if (flag != flag1) { + this.seeTime = 0; + } + + if (flag) { + ++this.seeTime; + } else { + --this.seeTime; + } + + if (!(d0 > (double)this.attackRadiusSqr) && this.seeTime >= 1) { + this.mob.getNavigation().stop(); + } else { + this.mob.getNavigation().moveTo(livingentity, this.speedModifier); + } + + this.mob.getLookControl().setLookAt(livingentity, 30.0F, 30.0F); + + if (this.mob.isUsingItem()) { + if (!flag && this.seeTime < -60) { + this.mob.stopUsingItem(); + } else if (flag) { + int i = this.mob.getTicksUsingItem(); + if (i >= 20) { + this.mob.stopUsingItem(); + this.mob.performRangedAttack(livingentity, BowItem.getPowerForTime(i)); + this.attackTime = this.attackIntervalMin; + } + } + } else if (--this.attackTime <= 0 && this.seeTime >= -60) { + this.mob.startUsingItem(ProjectileUtil.getWeaponHoldingHand(this.mob, item -> item instanceof BowItem)); + } + + } + } +} diff --git a/src/main/java/insane96mcp/progressivebosses/module/wither/entity/WitherMinion.java b/src/main/java/insane96mcp/progressivebosses/module/wither/entity/WitherMinion.java index 04eb0b13..b496f2b1 100644 --- a/src/main/java/insane96mcp/progressivebosses/module/wither/entity/WitherMinion.java +++ b/src/main/java/insane96mcp/progressivebosses/module/wither/entity/WitherMinion.java @@ -1,6 +1,7 @@ package insane96mcp.progressivebosses.module.wither.entity; import insane96mcp.insanelib.ai.ILNearestAttackableTargetGoal; +import insane96mcp.progressivebosses.module.wither.ai.RangedMinionAttackGoal; import net.minecraft.nbt.CompoundTag; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; @@ -20,6 +21,7 @@ import net.minecraft.world.entity.boss.wither.WitherBoss; import net.minecraft.world.entity.monster.AbstractSkeleton; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; @@ -30,10 +32,13 @@ public class WitherMinion extends AbstractSkeleton { + protected final RangedMinionAttackGoal minionBowGoal = new RangedMinionAttackGoal(this, 1.0D, 20, 15.0F); + private static final Predicate NOT_UNDEAD = livingEntity -> livingEntity != null && livingEntity.getMobType() != MobType.UNDEAD && livingEntity.attackable(); public WitherMinion(EntityType type, Level worldIn) { super(type, worldIn); + this.reassesMinionWeapon(); } @Override @@ -86,11 +91,33 @@ protected void populateDefaultEquipmentSlots(DifficultyInstance difficulty) { @Nullable public SpawnGroupData finalizeSpawn(ServerLevelAccessor worldIn, DifficultyInstance difficultyIn, MobSpawnType reason, @Nullable SpawnGroupData spawnDataIn, @Nullable CompoundTag dataTag) { SpawnGroupData ilivingentitydata = super.finalizeSpawn(worldIn, difficultyIn, reason, spawnDataIn, dataTag); - this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(4.0D); this.reassessWeaponGoal(); return ilivingentitydata; } + @Override + public void reassessWeaponGoal() { + super.reassessWeaponGoal(); + if (this.minionBowGoal != null) + this.reassesMinionWeapon(); + } + + private void reassesMinionWeapon() { + if (!this.level.isClientSide) { + this.goalSelector.removeGoal(this.meleeGoal); + this.goalSelector.removeGoal(this.bowGoal); + this.goalSelector.removeGoal(this.minionBowGoal); + ItemStack itemstack = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, item -> item instanceof net.minecraft.world.item.BowItem)); + if (itemstack.is(Items.BOW)) { + this.minionBowGoal.setMinAttackInterval(30); + this.goalSelector.addGoal(4, this.minionBowGoal); + } else { + this.goalSelector.addGoal(4, this.meleeGoal); + } + + } + } + public boolean doHurtTarget(Entity entityIn) { if (!super.doHurtTarget(entityIn)) { return false; @@ -120,7 +147,7 @@ protected void createWitherRose(@Nullable LivingEntity entitySource) { public static AttributeSupplier.Builder prepareAttributes() { return LivingEntity.createLivingAttributes() .add(Attributes.ATTACK_DAMAGE, 3.0d) - .add(Attributes.MAX_HEALTH, 20.0d) + .add(Attributes.MAX_HEALTH, 16.0d) .add(Attributes.FOLLOW_RANGE, 64.0d) .add(Attributes.MOVEMENT_SPEED, 0.25d) .add(Attributes.ATTACK_KNOCKBACK, 1.5d); diff --git a/src/main/java/insane96mcp/progressivebosses/module/wither/feature/AttackFeature.java b/src/main/java/insane96mcp/progressivebosses/module/wither/feature/AttackFeature.java index e8b8b253..d8f24193 100644 --- a/src/main/java/insane96mcp/progressivebosses/module/wither/feature/AttackFeature.java +++ b/src/main/java/insane96mcp/progressivebosses/module/wither/feature/AttackFeature.java @@ -49,7 +49,7 @@ public class AttackFeature extends Feature { public double maxChargeAttackChance = 0.06d; public double chargeAttackBaseDamage = 16d; //Barrage Attack - public double maxBarrageChance = 0.09d; + public double maxBarrageChance = 0.075d; public int minBarrageDuration = 20; public int maxBarrageDuration = 150; //Skulls @@ -76,8 +76,8 @@ public AttackFeature(Module module) { //Barrage Config.builder.push("Barrage Attack"); maxBarrageChanceConfig = Config.builder - .comment("Chance (at max difficulty) every time the Wither takes damage to start a barrage attack. Lower health and more damage taken increases the chance.\n" + - "This value is the chance at 0% health and when taking 10 damage.") + .comment("Chance (at max difficulty) every time the Wither takes damage to start a barrage attack. More damage taken increases the chance.\n" + + "This value is the chance when taking 10 damage.") .defineInRange("Barrage Attack Chance", maxBarrageChance, 0d, 1d); minBarrageDurationConfig = Config.builder .comment("Min time (in ticks) for the duration of the barrage attack. Less health = longer barrage.") @@ -218,7 +218,7 @@ private void doBarrage(WitherBoss wither, float damageTaken) { CompoundTag witherTags = wither.getPersistentData(); double missingHealthPerc = 1d - wither.getHealth() / wither.getMaxHealth(); - double chance = (this.maxBarrageChance * DifficultyHelper.getScalingDifficulty(wither)) * missingHealthPerc; + double chance = (this.maxBarrageChance * DifficultyHelper.getScalingDifficulty(wither)); chance *= (damageTaken / 10f); double r = wither.getRandom().nextDouble(); if (r < chance) { diff --git a/src/main/java/insane96mcp/progressivebosses/setup/Reflection.java b/src/main/java/insane96mcp/progressivebosses/setup/Reflection.java index 5f4bd151..2ba275a2 100644 --- a/src/main/java/insane96mcp/progressivebosses/setup/Reflection.java +++ b/src/main/java/insane96mcp/progressivebosses/setup/Reflection.java @@ -13,22 +13,22 @@ public class Reflection { static MethodHandles.Lookup lookup = MethodHandles.lookup(); - static Method onEntityHitMethod; - public static MethodHandle onEntityHitMH; - public static void ProjectileEntity_onEntityHit(Projectile projectileEntity, EntityHitResult p_213868_1_) { + static Method onHitEntityMethod; + static MethodHandle onHitEntityHandler; + public static void Projectile_onHitEntity(Projectile projectileEntity, EntityHitResult p_213868_1_) { try { - onEntityHitMH.invoke(projectileEntity, p_213868_1_); + onHitEntityHandler.invoke(projectileEntity, p_213868_1_); } catch (Throwable throwable) { throwable.printStackTrace(); } } - static Method onBlockHitMethod; - public static MethodHandle onBlockHitMH; - public static void ProjectileEntity_onBlockHit(Projectile projectileEntity, BlockHitResult p_213868_1_) { + static Method onHitBlockMethod; + static MethodHandle onHitBlockHandler; + public static void Projectile_onHitBlock(Projectile projectileEntity, BlockHitResult p_213868_1_) { try { - onBlockHitMH.invoke(projectileEntity, p_213868_1_); + onHitBlockHandler.invoke(projectileEntity, p_213868_1_); } catch (Throwable throwable) { throwable.printStackTrace(); @@ -37,11 +37,11 @@ public static void ProjectileEntity_onBlockHit(Projectile projectileEntity, Bloc public static void init() { try { - onEntityHitMethod = ObfuscationReflectionHelper.findMethod(Projectile.class, "m_5790_", EntityHitResult.class); - onEntityHitMH = lookup.unreflect(onEntityHitMethod); + onHitEntityMethod = ObfuscationReflectionHelper.findMethod(Projectile.class, "m_5790_", EntityHitResult.class); + onHitEntityHandler = lookup.unreflect(onHitEntityMethod); - onBlockHitMethod = ObfuscationReflectionHelper.findMethod(Projectile.class, "m_8060_", BlockHitResult.class); - onBlockHitMH = lookup.unreflect(onBlockHitMethod); + onHitBlockMethod = ObfuscationReflectionHelper.findMethod(Projectile.class, "m_8060_", BlockHitResult.class); + onHitBlockHandler = lookup.unreflect(onHitBlockMethod); } catch (IllegalAccessException e) { ProgressiveBosses.LOGGER.error(e.toString()); } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index c080d26f..01b63f5d 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -58,4 +58,8 @@ public-f net.minecraft.world.level.Explosion f_46010_ # mode #GuardianEntity public net.minecraft.world.entity.monster.Guardian m_32817_(I)V # setActiveAttackTarget -public net.minecraft.world.entity.monster.Guardian$GuardianAttackGoal \ No newline at end of file +public net.minecraft.world.entity.monster.Guardian$GuardianAttackGoal + +#AbstractSkeleton +public net.minecraft.world.entity.monster.AbstractSkeleton f_32130_ # bowGoal +public net.minecraft.world.entity.monster.AbstractSkeleton f_32131_ # meleeGoal \ No newline at end of file