minecraft-src/net/minecraft/world/item/component/BlocksAttacks.java
2025-07-04 03:45:38 +03:00

182 lines
7.7 KiB
Java

package net.minecraft.world.item.component;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.netty.buffer.ByteBuf;
import java.util.List;
import java.util.Optional;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.RegistryCodecs;
import net.minecraft.core.registries.Registries;
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.sounds.SoundEvent;
import net.minecraft.stats.Stats;
import net.minecraft.tags.TagKey;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
public record BlocksAttacks(
float blockDelaySeconds,
float disableCooldownScale,
List<BlocksAttacks.DamageReduction> damageReductions,
BlocksAttacks.ItemDamageFunction itemDamage,
Optional<TagKey<DamageType>> bypassedBy,
Optional<Holder<SoundEvent>> blockSound,
Optional<Holder<SoundEvent>> disableSound
) {
public static final Codec<BlocksAttacks> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
ExtraCodecs.NON_NEGATIVE_FLOAT.optionalFieldOf("block_delay_seconds", 0.0F).forGetter(BlocksAttacks::blockDelaySeconds),
ExtraCodecs.NON_NEGATIVE_FLOAT.optionalFieldOf("disable_cooldown_scale", 1.0F).forGetter(BlocksAttacks::disableCooldownScale),
BlocksAttacks.DamageReduction.CODEC
.listOf()
.optionalFieldOf("damage_reductions", List.of(new BlocksAttacks.DamageReduction(90.0F, Optional.empty(), 0.0F, 1.0F)))
.forGetter(BlocksAttacks::damageReductions),
BlocksAttacks.ItemDamageFunction.CODEC.optionalFieldOf("item_damage", BlocksAttacks.ItemDamageFunction.DEFAULT).forGetter(BlocksAttacks::itemDamage),
TagKey.hashedCodec(Registries.DAMAGE_TYPE).optionalFieldOf("bypassed_by").forGetter(BlocksAttacks::bypassedBy),
SoundEvent.CODEC.optionalFieldOf("block_sound").forGetter(BlocksAttacks::blockSound),
SoundEvent.CODEC.optionalFieldOf("disabled_sound").forGetter(BlocksAttacks::disableSound)
)
.apply(instance, BlocksAttacks::new)
);
public static final StreamCodec<RegistryFriendlyByteBuf, BlocksAttacks> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.FLOAT,
BlocksAttacks::blockDelaySeconds,
ByteBufCodecs.FLOAT,
BlocksAttacks::disableCooldownScale,
BlocksAttacks.DamageReduction.STREAM_CODEC.apply(ByteBufCodecs.list()),
BlocksAttacks::damageReductions,
BlocksAttacks.ItemDamageFunction.STREAM_CODEC,
BlocksAttacks::itemDamage,
TagKey.streamCodec(Registries.DAMAGE_TYPE).apply(ByteBufCodecs::optional),
BlocksAttacks::bypassedBy,
SoundEvent.STREAM_CODEC.apply(ByteBufCodecs::optional),
BlocksAttacks::blockSound,
SoundEvent.STREAM_CODEC.apply(ByteBufCodecs::optional),
BlocksAttacks::disableSound,
BlocksAttacks::new
);
public void onBlocked(ServerLevel level, LivingEntity entity) {
this.blockSound
.ifPresent(
holder -> level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), holder, entity.getSoundSource(), 1.0F, 0.8F + level.random.nextFloat() * 0.4F)
);
}
public void disable(ServerLevel level, LivingEntity entity, float duration, ItemStack stack) {
int i = this.disableBlockingForTicks(duration);
if (i > 0) {
if (entity instanceof Player player) {
player.getCooldowns().addCooldown(stack, i);
}
entity.stopUsingItem();
this.disableSound
.ifPresent(
holder -> level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), holder, entity.getSoundSource(), 0.8F, 0.8F + level.random.nextFloat() * 0.4F)
);
}
}
public void hurtBlockingItem(Level level, ItemStack stack, LivingEntity entity, InteractionHand hand, float damage) {
if (entity instanceof Player player) {
if (!level.isClientSide) {
player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
}
int i = this.itemDamage.apply(damage);
if (i > 0) {
stack.hurtAndBreak(i, entity, LivingEntity.getSlotForHand(hand));
}
}
}
private int disableBlockingForTicks(float duration) {
float f = duration * this.disableCooldownScale;
return f > 0.0F ? Math.round(f * 20.0F) : 0;
}
public int blockDelayTicks() {
return Math.round(this.blockDelaySeconds * 20.0F);
}
public float resolveBlockedDamage(DamageSource damageSource, float damageAmount, double horizontalAngle) {
float f = 0.0F;
for (BlocksAttacks.DamageReduction damageReduction : this.damageReductions) {
f += damageReduction.resolve(damageSource, damageAmount, horizontalAngle);
}
return Mth.clamp(f, 0.0F, damageAmount);
}
public record DamageReduction(float horizontalBlockingAngle, Optional<HolderSet<DamageType>> type, float base, float factor) {
public static final Codec<BlocksAttacks.DamageReduction> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
ExtraCodecs.POSITIVE_FLOAT.optionalFieldOf("horizontal_blocking_angle", 90.0F).forGetter(BlocksAttacks.DamageReduction::horizontalBlockingAngle),
RegistryCodecs.homogeneousList(Registries.DAMAGE_TYPE).optionalFieldOf("type").forGetter(BlocksAttacks.DamageReduction::type),
Codec.FLOAT.fieldOf("base").forGetter(BlocksAttacks.DamageReduction::base),
Codec.FLOAT.fieldOf("factor").forGetter(BlocksAttacks.DamageReduction::factor)
)
.apply(instance, BlocksAttacks.DamageReduction::new)
);
public static final StreamCodec<RegistryFriendlyByteBuf, BlocksAttacks.DamageReduction> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.FLOAT,
BlocksAttacks.DamageReduction::horizontalBlockingAngle,
ByteBufCodecs.holderSet(Registries.DAMAGE_TYPE).apply(ByteBufCodecs::optional),
BlocksAttacks.DamageReduction::type,
ByteBufCodecs.FLOAT,
BlocksAttacks.DamageReduction::base,
ByteBufCodecs.FLOAT,
BlocksAttacks.DamageReduction::factor,
BlocksAttacks.DamageReduction::new
);
public float resolve(DamageSource damageSource, float damageAmount, double horizontalAngle) {
if (horizontalAngle > (float) (Math.PI / 180.0) * this.horizontalBlockingAngle) {
return 0.0F;
} else {
return this.type.isPresent() && !((HolderSet)this.type.get()).contains(damageSource.typeHolder())
? 0.0F
: Mth.clamp(this.base + this.factor * damageAmount, 0.0F, damageAmount);
}
}
}
public record ItemDamageFunction(float threshold, float base, float factor) {
public static final Codec<BlocksAttacks.ItemDamageFunction> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
ExtraCodecs.NON_NEGATIVE_FLOAT.fieldOf("threshold").forGetter(BlocksAttacks.ItemDamageFunction::threshold),
Codec.FLOAT.fieldOf("base").forGetter(BlocksAttacks.ItemDamageFunction::base),
Codec.FLOAT.fieldOf("factor").forGetter(BlocksAttacks.ItemDamageFunction::factor)
)
.apply(instance, BlocksAttacks.ItemDamageFunction::new)
);
public static final StreamCodec<ByteBuf, BlocksAttacks.ItemDamageFunction> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.FLOAT,
BlocksAttacks.ItemDamageFunction::threshold,
ByteBufCodecs.FLOAT,
BlocksAttacks.ItemDamageFunction::base,
ByteBufCodecs.FLOAT,
BlocksAttacks.ItemDamageFunction::factor,
BlocksAttacks.ItemDamageFunction::new
);
public static final BlocksAttacks.ItemDamageFunction DEFAULT = new BlocksAttacks.ItemDamageFunction(1.0F, 0.0F, 1.0F);
public int apply(float damageAmount) {
return damageAmount < this.threshold ? 0 : Mth.floor(this.base + this.factor * damageAmount);
}
}
}