minecraft-src/net/minecraft/world/effect/MobEffect.java
2025-07-04 03:45:38 +03:00

220 lines
7.8 KiB
Java

package net.minecraft.world.effect;
import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.Function;
import net.minecraft.Util;
import net.minecraft.core.Holder;
import net.minecraft.core.particles.ColorParticleOption;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeMap;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.flag.FeatureElement;
import net.minecraft.world.flag.FeatureFlag;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import org.jetbrains.annotations.Nullable;
public class MobEffect implements FeatureElement {
public static final Codec<Holder<MobEffect>> CODEC = BuiltInRegistries.MOB_EFFECT.holderByNameCodec();
public static final StreamCodec<RegistryFriendlyByteBuf, Holder<MobEffect>> STREAM_CODEC = ByteBufCodecs.holderRegistry(Registries.MOB_EFFECT);
private static final int AMBIENT_ALPHA = Mth.floor(38.25F);
/**
* Contains a Map of the AttributeModifiers registered by potions
*/
private final Map<Holder<Attribute>, MobEffect.AttributeTemplate> attributeModifiers = new Object2ObjectOpenHashMap<>();
private final MobEffectCategory category;
private final int color;
private final Function<MobEffectInstance, ParticleOptions> particleFactory;
@Nullable
private String descriptionId;
private int blendInDurationTicks;
private int blendOutDurationTicks;
private int blendOutAdvanceTicks;
private Optional<SoundEvent> soundOnAdded = Optional.empty();
private FeatureFlagSet requiredFeatures = FeatureFlags.VANILLA_SET;
protected MobEffect(MobEffectCategory category, int color) {
this.category = category;
this.color = color;
this.particleFactory = mobEffectInstance -> {
int j = mobEffectInstance.isAmbient() ? AMBIENT_ALPHA : 255;
return ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, ARGB.color(j, color));
};
}
protected MobEffect(MobEffectCategory category, int color, ParticleOptions particle) {
this.category = category;
this.color = color;
this.particleFactory = mobEffectInstance -> particle;
}
public int getBlendInDurationTicks() {
return this.blendInDurationTicks;
}
public int getBlendOutDurationTicks() {
return this.blendOutDurationTicks;
}
public int getBlendOutAdvanceTicks() {
return this.blendOutAdvanceTicks;
}
public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) {
return true;
}
public void applyInstantenousEffect(
ServerLevel level, @Nullable Entity source, @Nullable Entity indirectSource, LivingEntity entity, int amplifier, double health
) {
this.applyEffectTick(level, entity, amplifier);
}
public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
return false;
}
public void onEffectStarted(LivingEntity entity, int amplifier) {
}
public void onEffectAdded(LivingEntity entity, int amplifier) {
this.soundOnAdded
.ifPresent(soundEvent -> entity.level().playSound(null, entity.getX(), entity.getY(), entity.getZ(), soundEvent, entity.getSoundSource(), 1.0F, 1.0F));
}
public void onMobRemoved(ServerLevel level, LivingEntity entity, int amplifier, Entity.RemovalReason reason) {
}
public void onMobHurt(ServerLevel level, LivingEntity entity, int amplifier, DamageSource damageSource, float amount) {
}
/**
* Returns {@code true} if the potion has an instant effect instead of a continuous one (e.g. Harming)
*/
public boolean isInstantenous() {
return false;
}
protected String getOrCreateDescriptionId() {
if (this.descriptionId == null) {
this.descriptionId = Util.makeDescriptionId("effect", BuiltInRegistries.MOB_EFFECT.getKey(this));
}
return this.descriptionId;
}
/**
* Returns the name of the effect.
*/
public String getDescriptionId() {
return this.getOrCreateDescriptionId();
}
public Component getDisplayName() {
return Component.translatable(this.getDescriptionId());
}
public MobEffectCategory getCategory() {
return this.category;
}
/**
* Returns the color of the potion liquid.
*/
public int getColor() {
return this.color;
}
public MobEffect addAttributeModifier(Holder<Attribute> attribute, ResourceLocation id, double amount, AttributeModifier.Operation operation) {
this.attributeModifiers.put(attribute, new MobEffect.AttributeTemplate(id, amount, operation));
return this;
}
public MobEffect setBlendDuration(int blendDuration) {
return this.setBlendDuration(blendDuration, blendDuration, blendDuration);
}
public MobEffect setBlendDuration(int blendInDurationTicks, int blendOutDurationTicks, int blendOutAdvanceTicks) {
this.blendInDurationTicks = blendInDurationTicks;
this.blendOutDurationTicks = blendOutDurationTicks;
this.blendOutAdvanceTicks = blendOutAdvanceTicks;
return this;
}
public void createModifiers(int amplifier, BiConsumer<Holder<Attribute>, AttributeModifier> output) {
this.attributeModifiers.forEach((holder, attributeTemplate) -> output.accept(holder, attributeTemplate.create(amplifier)));
}
public void removeAttributeModifiers(AttributeMap attributeMap) {
for (Entry<Holder<Attribute>, MobEffect.AttributeTemplate> entry : this.attributeModifiers.entrySet()) {
AttributeInstance attributeInstance = attributeMap.getInstance((Holder<Attribute>)entry.getKey());
if (attributeInstance != null) {
attributeInstance.removeModifier(((MobEffect.AttributeTemplate)entry.getValue()).id());
}
}
}
public void addAttributeModifiers(AttributeMap attributeMap, int amplifier) {
for (Entry<Holder<Attribute>, MobEffect.AttributeTemplate> entry : this.attributeModifiers.entrySet()) {
AttributeInstance attributeInstance = attributeMap.getInstance((Holder<Attribute>)entry.getKey());
if (attributeInstance != null) {
attributeInstance.removeModifier(((MobEffect.AttributeTemplate)entry.getValue()).id());
attributeInstance.addPermanentModifier(((MobEffect.AttributeTemplate)entry.getValue()).create(amplifier));
}
}
}
/**
* Get if the potion is beneficial to the player. Beneficial potions are shown on the first row of the HUD
*/
public boolean isBeneficial() {
return this.category == MobEffectCategory.BENEFICIAL;
}
public ParticleOptions createParticleOptions(MobEffectInstance effect) {
return (ParticleOptions)this.particleFactory.apply(effect);
}
public MobEffect withSoundOnAdded(SoundEvent sound) {
this.soundOnAdded = Optional.of(sound);
return this;
}
public MobEffect requiredFeatures(FeatureFlag... requiredFeatures) {
this.requiredFeatures = FeatureFlags.REGISTRY.subset(requiredFeatures);
return this;
}
@Override
public FeatureFlagSet requiredFeatures() {
return this.requiredFeatures;
}
record AttributeTemplate(ResourceLocation id, double amount, AttributeModifier.Operation operation) {
public AttributeModifier create(int level) {
return new AttributeModifier(this.id, this.amount * (level + 1), this.operation);
}
}
}