diff --git a/Xplat/src/main/java/vazkii/botania/common/HurtByTargetGoalAccess.java b/Xplat/src/main/java/vazkii/botania/common/HurtByTargetGoalAccess.java new file mode 100644 index 0000000000..8d3c004209 --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/common/HurtByTargetGoalAccess.java @@ -0,0 +1,5 @@ +package vazkii.botania.common; + +public interface HurtByTargetGoalAccess { + void botania$setBrainwashed(); +} diff --git a/Xplat/src/main/java/vazkii/botania/common/TargetingConditionsAccess.java b/Xplat/src/main/java/vazkii/botania/common/TargetingConditionsAccess.java new file mode 100644 index 0000000000..f470aaa3b0 --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/common/TargetingConditionsAccess.java @@ -0,0 +1,5 @@ +package vazkii.botania.common; + +public interface TargetingConditionsAccess { + void botania$setBrainwashed(); +} diff --git a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/HeiseiDreamBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/HeiseiDreamBlockEntity.java index 3468504456..42dddf17b6 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/HeiseiDreamBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/HeiseiDreamBlockEntity.java @@ -17,12 +17,14 @@ import net.minecraft.world.entity.ai.goal.GoalSelector; import net.minecraft.world.entity.ai.goal.WrappedGoal; import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import vazkii.botania.api.block_entity.FunctionalFlowerBlockEntity; import vazkii.botania.api.block_entity.RadiusDescriptor; +import vazkii.botania.common.HurtByTargetGoalAccess; import vazkii.botania.common.block.BotaniaFlowerBlocks; import vazkii.botania.mixin.GoalSelectorAccessor; import vazkii.botania.mixin.HurtByTargetGoalAccessor; @@ -78,6 +80,7 @@ public static boolean brainwashEntity(Mob entity, List mobs) { // Move any HurtByTargetGoal to highest priority GoalSelector targetSelector = ((MobAccessor) entity).getTargetSelector(); + boolean modifiedGoals = false; for (WrappedGoal entry : ((GoalSelectorAccessor) targetSelector).getAvailableGoals()) { if (entry.getGoal() instanceof HurtByTargetGoal goal) { // Remove all ignorals. We can't actually resize or overwrite @@ -89,10 +92,18 @@ public static boolean brainwashEntity(Mob entity, List mobs) { // Concurrent modification OK since we break out of the loop targetSelector.removeGoal(goal); targetSelector.addGoal(-1, goal); + + ((HurtByTargetGoalAccess) goal).botania$setBrainwashed(); + modifiedGoals = true; break; } } + if (!modifiedGoals) { + // For entities using the newer Brain system + entity.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, mob); + } + // Now set last hurt by, which HurtByTargetGoal will pick up entity.setLastHurtByMob(mob); did = true; diff --git a/Xplat/src/main/java/vazkii/botania/mixin/HurtByTargetGoalMixin.java b/Xplat/src/main/java/vazkii/botania/mixin/HurtByTargetGoalMixin.java new file mode 100644 index 0000000000..b2a57dba8f --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/mixin/HurtByTargetGoalMixin.java @@ -0,0 +1,52 @@ +package vazkii.botania.mixin; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; +import net.minecraft.world.entity.ai.goal.target.TargetGoal; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import vazkii.botania.common.HurtByTargetGoalAccess; +import vazkii.botania.common.TargetingConditionsAccess; + +@Mixin(HurtByTargetGoal.class) +abstract class HurtByTargetGoalMixin extends TargetGoal implements HurtByTargetGoalAccess { + @Shadow + @Final + private static TargetingConditions HURT_BY_TARGETING; + @Unique + private boolean brainwashed; + + private static final TargetingConditions BRAINWASHED_CONDITIONS = HURT_BY_TARGETING.copy(); + + static { + ((TargetingConditionsAccess) BRAINWASHED_CONDITIONS).botania$setBrainwashed(); + } + + public HurtByTargetGoalMixin(Mob $$0, boolean $$1) { + super($$0, $$1); + } + + @Override + public void botania$setBrainwashed() { + this.brainwashed = true; + } + + @Redirect( + method = "canUse()Z", + at = @At( + target = "Lnet/minecraft/world/entity/ai/goal/target/HurtByTargetGoal;canAttack(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/entity/ai/targeting/TargetingConditions;)Z", + value = "INVOKE" + ) + ) + boolean replaceAttackPredicate(HurtByTargetGoal instance, LivingEntity livingEntity, TargetingConditions targetingConditions) { + return this.canAttack(livingEntity, this.brainwashed ? BRAINWASHED_CONDITIONS : HURT_BY_TARGETING); + } +} diff --git a/Xplat/src/main/java/vazkii/botania/mixin/TargetingConditionsMixin.java b/Xplat/src/main/java/vazkii/botania/mixin/TargetingConditionsMixin.java new file mode 100644 index 0000000000..a6ef207c3e --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/mixin/TargetingConditionsMixin.java @@ -0,0 +1,55 @@ +package vazkii.botania.mixin; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import vazkii.botania.common.TargetingConditionsAccess; + +@Mixin(TargetingConditions.class) +abstract class TargetingConditionsMixin implements TargetingConditionsAccess { + private boolean brainwashed; + + @Redirect( + method = "test", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;canAttack(Lnet/minecraft/world/entity/LivingEntity;)Z" + ) + ) + boolean redirectCanAttack(LivingEntity instance, LivingEntity entity) { + return this.brainwashed || instance.canAttack(entity); + } + + @Redirect( + method = "test", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;canAttackType(Lnet/minecraft/world/entity/EntityType;)Z" + ) + ) + boolean redirectCanAttackType(LivingEntity instance, EntityType entityType) { + return this.brainwashed || instance.canAttackType(entityType); + } + + @Redirect( + method = "test", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/LivingEntity;isAlliedTo(Lnet/minecraft/world/entity/Entity;)Z" + ) + ) + boolean redirectIsAlliedTo(LivingEntity instance, Entity entity) { + return !this.brainwashed && instance.isAlliedTo(entity); + } + + @Override + public void botania$setBrainwashed() { + this.brainwashed = true; + } +} diff --git a/Xplat/src/main/resources/botania_xplat.mixins.json b/Xplat/src/main/resources/botania_xplat.mixins.json index 4988ab8052..404153d12b 100644 --- a/Xplat/src/main/resources/botania_xplat.mixins.json +++ b/Xplat/src/main/resources/botania_xplat.mixins.json @@ -26,6 +26,7 @@ "GoalSelectorAccessor", "HopperBlockEntityAccessor", "HurtByTargetGoalAccessor", + "HurtByTargetGoalMixin", "IngredientAccessor", "ItemEntityAccessor", "ItemEntityMixin", @@ -50,6 +51,7 @@ "SoundTypeAccessor", "SpawnPlacementsMixin", "StatsAccessor", + "TargetingConditionsMixin", "TextureSlotAccessor", "ThrowableProjectileMixin", "WitherEntityAccessor"