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

205 lines
7.4 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 blendDurationTicks;
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 getBlendDurationTicks() {
return this.blendDurationTicks;
}
public boolean applyEffectTick(ServerLevel serverLevel, LivingEntity livingEntity, int i) {
return true;
}
public void applyInstantenousEffect(ServerLevel serverLevel, @Nullable Entity entity, @Nullable Entity entity2, LivingEntity livingEntity, int i, double d) {
this.applyEffectTick(serverLevel, livingEntity, i);
}
public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
return false;
}
public void onEffectStarted(LivingEntity livingEntity, int amplifier) {
}
public void onEffectAdded(LivingEntity livingEntity, int amplifier) {
this.soundOnAdded
.ifPresent(
soundEvent -> livingEntity.level()
.playSound(null, livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(), soundEvent, livingEntity.getSoundSource(), 1.0F, 1.0F)
);
}
public void onMobRemoved(ServerLevel serverLevel, LivingEntity livingEntity, int i, Entity.RemovalReason removalReason) {
}
public void onMobHurt(ServerLevel serverLevel, LivingEntity livingEntity, int i, DamageSource damageSource, float f) {
}
/**
* 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) {
this.blendDurationTicks = blendDuration;
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);
}
}
}