diff --git a/src/main/java/cc/fros/mixin/entity/ColorParticleOptionAccessor.java b/src/main/java/cc/fros/mixin/entity/ColorParticleOptionAccessor.java new file mode 100644 index 0000000..0c3e2f3 --- /dev/null +++ b/src/main/java/cc/fros/mixin/entity/ColorParticleOptionAccessor.java @@ -0,0 +1,12 @@ +package cc.fros.mixin.entity; + +import net.minecraft.core.particles.ColorParticleOption; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ColorParticleOption.class) +public interface ColorParticleOptionAccessor { + + @Accessor("color") + int fros$getColor(); +} diff --git a/src/main/java/cc/fros/mixin/entity/LivingEntityEffectParticlesAccessor.java b/src/main/java/cc/fros/mixin/entity/LivingEntityEffectParticlesAccessor.java new file mode 100644 index 0000000..3119c68 --- /dev/null +++ b/src/main/java/cc/fros/mixin/entity/LivingEntityEffectParticlesAccessor.java @@ -0,0 +1,18 @@ +package cc.fros.mixin.entity; + +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(LivingEntity.class) +public interface LivingEntityEffectParticlesAccessor { + + @Accessor("DATA_EFFECT_PARTICLES") + static EntityDataAccessor> fros$getDataEffectParticles() { + throw new AssertionError(); + } +} diff --git a/src/main/java/cc/fros/util/DamageSyncTracker.java b/src/main/java/cc/fros/util/DamageSyncTracker.java new file mode 100644 index 0000000..f6c9251 --- /dev/null +++ b/src/main/java/cc/fros/util/DamageSyncTracker.java @@ -0,0 +1,54 @@ +package cc.fros.util; + +import cc.fros.mixin.entity.ColorParticleOptionAccessor; +import cc.fros.mixin.entity.LivingEntityEffectParticlesAccessor; +import net.minecraft.core.particles.ColorParticleOption; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.LivingEntity; + +import java.util.List; + +/** + * Detects active effects on other players from the effect particle data the + * server syncs with them, ported from homovore's {@code DamageSyncTracker}. + */ +public final class DamageSyncTracker { + + private DamageSyncTracker() { + throw new AssertionError(); + } + + private static final int RESISTANCE_COLOR = MobEffects.RESISTANCE.value().getColor() & 0xFFFFFF; + private static final int SLOWNESS_COLOR = MobEffects.SLOWNESS.value().getColor() & 0xFFFFFF; + private static final int STRENGTH_COLOR = MobEffects.STRENGTH.value().getColor() & 0xFFFFFF; + + private static boolean hasEffectColor(LivingEntity entity, int rgb) { + List particles = entity.getEntityData() + .get(LivingEntityEffectParticlesAccessor.fros$getDataEffectParticles()); + if (particles.isEmpty()) return false; + for (int i = 0, n = particles.size(); i < n; i++) { + if (particles.get(i) instanceof ColorParticleOption color + && (((ColorParticleOptionAccessor) (Object) color).fros$getColor() & 0xFFFFFF) == rgb) { + return true; + } + } + return false; + } + + public static boolean hasResistance(LivingEntity entity) { + return hasEffectColor(entity, RESISTANCE_COLOR); + } + + public static boolean hasSlowness(LivingEntity entity) { + return hasEffectColor(entity, SLOWNESS_COLOR); + } + + public static boolean hasStrength(LivingEntity entity) { + return hasEffectColor(entity, STRENGTH_COLOR); + } + + public static boolean isTurtleMaster(LivingEntity entity) { + return hasResistance(entity) && hasSlowness(entity); + } +} diff --git a/src/main/resources/fros.mixins.json b/src/main/resources/fros.mixins.json index 8aa3adb..c5a0bb7 100644 --- a/src/main/resources/fros.mixins.json +++ b/src/main/resources/fros.mixins.json @@ -7,6 +7,8 @@ "network.MixinConnection" ], "client": [ + "entity.ColorParticleOptionAccessor", + "entity.LivingEntityEffectParticlesAccessor", "entity.MixinAbstractClientPlayer", "entity.MixinClientPlayerEntity", "input.MixinKeyboardHandler",