package net.minecraft.world.effect; import com.google.common.collect.ComparisonChain; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.Int2IntFunction; import java.util.Optional; import javax.annotation.Nullable; import net.minecraft.core.Holder; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import org.slf4j.Logger; public class MobEffectInstance implements Comparable { private static final Logger LOGGER = LogUtils.getLogger(); public static final int INFINITE_DURATION = -1; public static final int MIN_AMPLIFIER = 0; public static final int MAX_AMPLIFIER = 255; public static final Codec CODEC = RecordCodecBuilder.create( p_341259_ -> p_341259_.group( MobEffect.CODEC.fieldOf("id").forGetter(MobEffectInstance::getEffect), MobEffectInstance.Details.MAP_CODEC.forGetter(MobEffectInstance::asDetails) ) .apply(p_341259_, MobEffectInstance::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( MobEffect.STREAM_CODEC, MobEffectInstance::getEffect, MobEffectInstance.Details.STREAM_CODEC, MobEffectInstance::asDetails, MobEffectInstance::new ); private final Holder effect; private int duration; private int amplifier; private boolean ambient; private boolean visible; private boolean showIcon; @Nullable private MobEffectInstance hiddenEffect; private final MobEffectInstance.BlendState blendState = new MobEffectInstance.BlendState(); public MobEffectInstance(Holder p_333937_) { this(p_333937_, 0, 0); } public MobEffectInstance(Holder p_332556_, int p_19523_) { this(p_332556_, p_19523_, 0); } public MobEffectInstance(Holder p_334453_, int p_328066_, int p_330997_) { this(p_334453_, p_328066_, p_330997_, false, true); } public MobEffectInstance(Holder p_327781_, int p_19529_, int p_19530_, boolean p_19531_, boolean p_19532_) { this(p_327781_, p_19529_, p_19530_, p_19531_, p_19532_, p_19532_); } public MobEffectInstance(Holder p_333122_, int p_216888_, int p_216889_, boolean p_216890_, boolean p_216891_, boolean p_216892_) { this(p_333122_, p_216888_, p_216889_, p_216890_, p_216891_, p_216892_, null); } public MobEffectInstance( Holder p_334558_, int p_19519_, int p_19520_, boolean p_332448_, boolean p_327855_, boolean p_334281_, @Nullable MobEffectInstance p_332569_ ) { this.effect = p_334558_; this.duration = p_19519_; this.amplifier = Mth.clamp(p_19520_, 0, 255); this.ambient = p_332448_; this.visible = p_327855_; this.showIcon = p_334281_; this.hiddenEffect = p_332569_; } public MobEffectInstance(MobEffectInstance p_19543_) { this.effect = p_19543_.effect; this.setDetailsFrom(p_19543_); } private MobEffectInstance(Holder p_330051_, MobEffectInstance.Details p_332322_) { this( p_330051_, p_332322_.duration(), p_332322_.amplifier(), p_332322_.ambient(), p_332322_.showParticles(), p_332322_.showIcon(), p_332322_.hiddenEffect().map(p_326756_ -> new MobEffectInstance(p_330051_, p_326756_)).orElse(null) ); } private MobEffectInstance.Details asDetails() { return new MobEffectInstance.Details( this.getAmplifier(), this.getDuration(), this.isAmbient(), this.isVisible(), this.showIcon(), Optional.ofNullable(this.hiddenEffect).map(MobEffectInstance::asDetails) ); } public float getBlendFactor(LivingEntity p_333473_, float p_327866_) { return this.blendState.getFactor(p_333473_, p_327866_); } public ParticleOptions getParticleOptions() { return this.effect.value().createParticleOptions(this); } void setDetailsFrom(MobEffectInstance p_19549_) { this.duration = p_19549_.duration; this.amplifier = p_19549_.amplifier; this.ambient = p_19549_.ambient; this.visible = p_19549_.visible; this.showIcon = p_19549_.showIcon; } public boolean update(MobEffectInstance p_19559_) { if (!this.effect.equals(p_19559_.effect)) { LOGGER.warn("This method should only be called for matching effects!"); } boolean flag = false; if (p_19559_.amplifier > this.amplifier) { if (p_19559_.isShorterDurationThan(this)) { MobEffectInstance mobeffectinstance = this.hiddenEffect; this.hiddenEffect = new MobEffectInstance(this); this.hiddenEffect.hiddenEffect = mobeffectinstance; } this.amplifier = p_19559_.amplifier; this.duration = p_19559_.duration; flag = true; } else if (this.isShorterDurationThan(p_19559_)) { if (p_19559_.amplifier == this.amplifier) { this.duration = p_19559_.duration; flag = true; } else if (this.hiddenEffect == null) { this.hiddenEffect = new MobEffectInstance(p_19559_); } else { this.hiddenEffect.update(p_19559_); } } if (!p_19559_.ambient && this.ambient || flag) { this.ambient = p_19559_.ambient; flag = true; } if (p_19559_.visible != this.visible) { this.visible = p_19559_.visible; flag = true; } if (p_19559_.showIcon != this.showIcon) { this.showIcon = p_19559_.showIcon; flag = true; } return flag; } private boolean isShorterDurationThan(MobEffectInstance p_268133_) { return !this.isInfiniteDuration() && (this.duration < p_268133_.duration || p_268133_.isInfiniteDuration()); } public boolean isInfiniteDuration() { return this.duration == -1; } public boolean endsWithin(int p_268088_) { return !this.isInfiniteDuration() && this.duration <= p_268088_; } public int mapDuration(Int2IntFunction p_268089_) { return !this.isInfiniteDuration() && this.duration != 0 ? p_268089_.applyAsInt(this.duration) : this.duration; } public Holder getEffect() { return this.effect; } public int getDuration() { return this.duration; } public int getAmplifier() { return this.amplifier; } public boolean isAmbient() { return this.ambient; } public boolean isVisible() { return this.visible; } public boolean showIcon() { return this.showIcon; } public boolean tick(LivingEntity p_19553_, Runnable p_19554_) { if (this.hasRemainingDuration()) { int i = this.isInfiniteDuration() ? p_19553_.tickCount : this.duration; if (p_19553_.level() instanceof ServerLevel serverlevel && this.effect.value().shouldApplyEffectTickThisTick(i, this.amplifier) && !this.effect.value().applyEffectTick(serverlevel, p_19553_, this.amplifier)) { p_19553_.removeEffect(this.effect); } this.tickDownDuration(); if (this.duration == 0 && this.hiddenEffect != null) { this.setDetailsFrom(this.hiddenEffect); this.hiddenEffect = this.hiddenEffect.hiddenEffect; p_19554_.run(); } } this.blendState.tick(this); return this.hasRemainingDuration(); } private boolean hasRemainingDuration() { return this.isInfiniteDuration() || this.duration > 0; } private int tickDownDuration() { if (this.hiddenEffect != null) { this.hiddenEffect.tickDownDuration(); } return this.duration = this.mapDuration(p_267916_ -> p_267916_ - 1); } public void onEffectStarted(LivingEntity p_297679_) { this.effect.value().onEffectStarted(p_297679_, this.amplifier); } public void onMobRemoved(ServerLevel p_364127_, LivingEntity p_329318_, Entity.RemovalReason p_333232_) { this.effect.value().onMobRemoved(p_364127_, p_329318_, this.amplifier, p_333232_); } public void onMobHurt(ServerLevel p_361358_, LivingEntity p_327684_, DamageSource p_328403_, float p_331463_) { this.effect.value().onMobHurt(p_361358_, p_327684_, this.amplifier, p_328403_, p_331463_); } public String getDescriptionId() { return this.effect.value().getDescriptionId(); } @Override public String toString() { String s; if (this.amplifier > 0) { s = this.getDescriptionId() + " x " + (this.amplifier + 1) + ", Duration: " + this.describeDuration(); } else { s = this.getDescriptionId() + ", Duration: " + this.describeDuration(); } if (!this.visible) { s = s + ", Particles: false"; } if (!this.showIcon) { s = s + ", Show Icon: false"; } return s; } private String describeDuration() { return this.isInfiniteDuration() ? "infinite" : Integer.toString(this.duration); } @Override public boolean equals(Object p_19574_) { if (this == p_19574_) { return true; } else { return !(p_19574_ instanceof MobEffectInstance mobeffectinstance) ? false : this.duration == mobeffectinstance.duration && this.amplifier == mobeffectinstance.amplifier && this.ambient == mobeffectinstance.ambient && this.visible == mobeffectinstance.visible && this.showIcon == mobeffectinstance.showIcon && this.effect.equals(mobeffectinstance.effect); } } @Override public int hashCode() { int i = this.effect.hashCode(); i = 31 * i + this.duration; i = 31 * i + this.amplifier; i = 31 * i + (this.ambient ? 1 : 0); i = 31 * i + (this.visible ? 1 : 0); return 31 * i + (this.showIcon ? 1 : 0); } public Tag save() { return CODEC.encodeStart(NbtOps.INSTANCE, this).getOrThrow(); } @Nullable public static MobEffectInstance load(CompoundTag p_19561_) { return CODEC.parse(NbtOps.INSTANCE, p_19561_).resultOrPartial(LOGGER::error).orElse(null); } public int compareTo(MobEffectInstance p_19566_) { int i = 32147; return (this.getDuration() <= 32147 || p_19566_.getDuration() <= 32147) && (!this.isAmbient() || !p_19566_.isAmbient()) ? ComparisonChain.start() .compareFalseFirst(this.isAmbient(), p_19566_.isAmbient()) .compareFalseFirst(this.isInfiniteDuration(), p_19566_.isInfiniteDuration()) .compare(this.getDuration(), p_19566_.getDuration()) .compare(this.getEffect().value().getColor(), p_19566_.getEffect().value().getColor()) .result() : ComparisonChain.start() .compare(this.isAmbient(), p_19566_.isAmbient()) .compare(this.getEffect().value().getColor(), p_19566_.getEffect().value().getColor()) .result(); } public void onEffectAdded(LivingEntity p_334348_) { this.effect.value().onEffectAdded(p_334348_, this.amplifier); } public boolean is(Holder p_329529_) { return this.effect.equals(p_329529_); } public void copyBlendState(MobEffectInstance p_335404_) { this.blendState.copyFrom(p_335404_.blendState); } public void skipBlending() { this.blendState.setImmediate(this); } static class BlendState { private float factor; private float factorPreviousFrame; public void setImmediate(MobEffectInstance p_333918_) { this.factor = computeTarget(p_333918_); this.factorPreviousFrame = this.factor; } public void copyFrom(MobEffectInstance.BlendState p_327821_) { this.factor = p_327821_.factor; this.factorPreviousFrame = p_327821_.factorPreviousFrame; } public void tick(MobEffectInstance p_330345_) { this.factorPreviousFrame = this.factor; int i = getBlendDuration(p_330345_); if (i == 0) { this.factor = 1.0F; } else { float f = computeTarget(p_330345_); if (this.factor != f) { float f1 = 1.0F / (float)i; this.factor = this.factor + Mth.clamp(f - this.factor, -f1, f1); } } } private static float computeTarget(MobEffectInstance p_334116_) { boolean flag = !p_334116_.endsWithin(getBlendDuration(p_334116_)); return flag ? 1.0F : 0.0F; } private static int getBlendDuration(MobEffectInstance p_335826_) { return p_335826_.getEffect().value().getBlendDurationTicks(); } public float getFactor(LivingEntity p_333208_, float p_330792_) { if (p_333208_.isRemoved()) { this.factorPreviousFrame = this.factor; } return Mth.lerp(p_330792_, this.factorPreviousFrame, this.factor); } } static record Details(int amplifier, int duration, boolean ambient, boolean showParticles, boolean showIcon, Optional hiddenEffect) { public static final MapCodec MAP_CODEC = MapCodec.recursive( "MobEffectInstance.Details", p_332855_ -> RecordCodecBuilder.mapCodec( p_327980_ -> p_327980_.group( ExtraCodecs.UNSIGNED_BYTE.optionalFieldOf("amplifier", 0).forGetter(MobEffectInstance.Details::amplifier), Codec.INT.optionalFieldOf("duration", Integer.valueOf(0)).forGetter(MobEffectInstance.Details::duration), Codec.BOOL.optionalFieldOf("ambient", Boolean.valueOf(false)).forGetter(MobEffectInstance.Details::ambient), Codec.BOOL.optionalFieldOf("show_particles", Boolean.valueOf(true)).forGetter(MobEffectInstance.Details::showParticles), Codec.BOOL.optionalFieldOf("show_icon").forGetter(p_330483_ -> Optional.of(p_330483_.showIcon())), p_332855_.optionalFieldOf("hidden_effect").forGetter(MobEffectInstance.Details::hiddenEffect) ) .apply(p_327980_, MobEffectInstance.Details::create) ) ); public static final StreamCodec STREAM_CODEC = StreamCodec.recursive( p_333279_ -> StreamCodec.composite( ByteBufCodecs.VAR_INT, MobEffectInstance.Details::amplifier, ByteBufCodecs.VAR_INT, MobEffectInstance.Details::duration, ByteBufCodecs.BOOL, MobEffectInstance.Details::ambient, ByteBufCodecs.BOOL, MobEffectInstance.Details::showParticles, ByteBufCodecs.BOOL, MobEffectInstance.Details::showIcon, p_333279_.apply(ByteBufCodecs::optional), MobEffectInstance.Details::hiddenEffect, MobEffectInstance.Details::new ) ); private static MobEffectInstance.Details create( int p_334251_, int p_332882_, boolean p_330487_, boolean p_334607_, Optional p_329280_, Optional p_330477_ ) { return new MobEffectInstance.Details(p_334251_, p_332882_, p_330487_, p_334607_, p_329280_.orElse(p_334607_), p_330477_); } } }