Wither Config allows you to adjust the Wither's stats and behavior for a tailored boss experience.
1package dev.redstudio.witherconfig.mixin;
2
3import dev.redstudio.witherconfig.config.WitherConfigConfig;
4import net.minecraft.block.state.IBlockState;
5import net.minecraft.entity.Entity;
6import net.minecraft.entity.boss.EntityWither;
7import net.minecraft.entity.monster.EntityMob;
8import net.minecraft.entity.player.EntityPlayer;
9import net.minecraft.util.math.BlockPos;
10import net.minecraft.util.math.MathHelper;
11import net.minecraft.world.World;
12import net.minecraftforge.event.ForgeEventFactory;
13import org.spongepowered.asm.mixin.Mixin;
14import org.spongepowered.asm.mixin.Shadow;
15import org.spongepowered.asm.mixin.Unique;
16import org.spongepowered.asm.mixin.injection.At;
17import org.spongepowered.asm.mixin.injection.Inject;
18import org.spongepowered.asm.mixin.injection.ModifyArg;
19import org.spongepowered.asm.mixin.injection.Redirect;
20import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
21
22/**
23 * @author Luna Lage (Desoroxxx)
24 * @since 1
25 */
26@Mixin(EntityWither.class)
27public abstract class EntityWitherMixin extends EntityMob {
28
29 @Shadow public abstract int getWatchedTargetId(final int head);
30
31 @Shadow public abstract boolean isArmored();
32
33 @Shadow private int blockBreakCounter;
34
35 private EntityWitherMixin(final World world) {
36 super(world);
37 }
38
39 @ModifyArg(method = "applyEntityAttributes", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ai/attributes/IAttributeInstance;setBaseValue(D)V", ordinal = 0))
40 private double changeMaxHealth(final double original) {
41 return WitherConfigConfig.common.maxHealth;
42 }
43
44 @ModifyArg(method = "applyEntityAttributes", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ai/attributes/IAttributeInstance;setBaseValue(D)V", ordinal = 1))
45 private double changeMovementSpeed(final double original) {
46 return WitherConfigConfig.common.movementSpeed;
47 }
48
49 @ModifyArg(method = "applyEntityAttributes", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ai/attributes/IAttributeInstance;setBaseValue(D)V", ordinal = 2))
50 private double changeFollowRange(final double original) {
51 return WitherConfigConfig.common.followRange;
52 }
53
54 @ModifyArg(method = "applyEntityAttributes", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ai/attributes/IAttributeInstance;setBaseValue(D)V", ordinal = 3))
55 private double changeArmor(final double original) {
56 return WitherConfigConfig.common.armor;
57 }
58
59 @ModifyArg(method = "ignite", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/boss/EntityWither;setInvulTime(I)V"))
60 private int changeSummonSequenceLength(final int original) {
61 return WitherConfigConfig.common.summonSequence.length;
62 }
63
64 @ModifyArg(method = "updateAITasks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;newExplosion(Lnet/minecraft/entity/Entity;DDDFZZ)Lnet/minecraft/world/Explosion;"))
65 private float changeEndExplosionStrength(final float original) {
66 return WitherConfigConfig.common.summonSequence.endExplosionStrength;
67 }
68
69 @Redirect(method = "onLivingUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/boss/EntityWither;getWatchedTargetId(I)I", ordinal = 0))
70 private int disableVanillaTargetFollowingLogic(final EntityWither instance, int head) {
71 return 0;
72 }
73
74 @Inject(method = "onLivingUpdate", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isRemote:Z", shift = At.Shift.AFTER))
75 private void newTargetFollowingLogic(final CallbackInfo callbackInfo) {
76 if (world.isRemote || getWatchedTargetId(0) <= 0)
77 return;
78
79 final Entity target = world.getEntityByID(getWatchedTargetId(0));
80
81 if (target == null)
82 return;
83
84 if (target instanceof EntityPlayer && WitherConfigConfig.common.breakBlocksWhenTargetingPlayer)
85 wither_Config$destroyBlocks();
86
87 final double movementSpeed = WitherConfigConfig.common.movementSpeed;
88
89 if (posY < target.posY || !isArmored() && posY < target.posY + WitherConfigConfig.common.unarmoredFlyHeight) {
90 if (motionY < 0)
91 motionY = 0;
92
93 motionY += (0.5 - motionY) * movementSpeed;
94 }
95
96 final double deltaX = target.posX - posX;
97 final double deltaZ = target.posZ - posZ;
98 final double distanceToTarget = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ);
99
100 if (distanceToTarget > WitherConfigConfig.common.followDistance) {
101 motionX += (deltaX / distanceToTarget * 0.5 - motionX) * movementSpeed;
102 motionZ += (deltaZ / distanceToTarget * 0.5 - motionZ) * movementSpeed;
103 }
104 }
105
106 @Unique
107 private void wither_Config$destroyBlocks() {
108 if (net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this)) {
109 final int x = MathHelper.floor(posX);
110 final int y = MathHelper.floor(posY);
111 final int z = MathHelper.floor(posZ);
112
113 boolean flag = false;
114
115 for (int xOffset = -1; xOffset <= 1; ++xOffset) {
116 for (int zOffset = -1; zOffset <= 1; ++zOffset) {
117 for (int yOffset = 0; yOffset <= 4; ++yOffset) {
118 final int currentX = x + xOffset;
119 final int currentY = y + yOffset;
120 final int currentZ = z + zOffset;
121
122 final BlockPos blockPos = new BlockPos(currentX, currentY, currentZ);
123 final IBlockState blockState = this.world.getBlockState(blockPos);
124
125 if (blockState.getBlock().canEntityDestroy(blockState, world, blockPos, this) && ForgeEventFactory.onEntityDestroyBlock(this, blockPos, blockState))
126 flag = this.world.destroyBlock(blockPos, true) || flag;
127 }
128 }
129 }
130
131 if (flag) {
132 this.world.playEvent(null, 1022, new BlockPos(this), 0);
133 }
134 }
135 }
136}