/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.mojang.datafixers.kinds.App * com.mojang.datafixers.kinds.Applicative * com.mojang.serialization.Codec * com.mojang.serialization.codecs.RecordCodecBuilder * io.netty.buffer.ByteBuf */ package net.minecraft.world.item.component; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.Applicative; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.netty.buffer.ByteBuf; import java.util.Optional; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.Holder; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.PiercingWeapon; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; public record KineticWeapon(float minReach, float maxReach, float hitboxMargin, int contactCooldownTicks, int delayTicks, Optional dismountConditions, Optional knockbackConditions, Optional damageConditions, float forwardMovement, float damageMultiplier, Optional> sound, Optional> hitSound) { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group((App)ExtraCodecs.floatRange(0.0f, 128.0f).optionalFieldOf("min_reach", (Object)Float.valueOf(0.0f)).forGetter(KineticWeapon::minReach), (App)ExtraCodecs.floatRange(0.0f, 128.0f).optionalFieldOf("max_reach", (Object)Float.valueOf(3.0f)).forGetter(KineticWeapon::maxReach), (App)ExtraCodecs.floatRange(0.0f, 1.0f).optionalFieldOf("hitbox_margin", (Object)Float.valueOf(0.3f)).forGetter(KineticWeapon::hitboxMargin), (App)ExtraCodecs.NON_NEGATIVE_INT.optionalFieldOf("contact_cooldown_ticks", (Object)10).forGetter(KineticWeapon::contactCooldownTicks), (App)ExtraCodecs.NON_NEGATIVE_INT.optionalFieldOf("delay_ticks", (Object)0).forGetter(KineticWeapon::delayTicks), (App)Condition.CODEC.optionalFieldOf("dismount_conditions").forGetter(KineticWeapon::dismountConditions), (App)Condition.CODEC.optionalFieldOf("knockback_conditions").forGetter(KineticWeapon::knockbackConditions), (App)Condition.CODEC.optionalFieldOf("damage_conditions").forGetter(KineticWeapon::damageConditions), (App)Codec.FLOAT.optionalFieldOf("forward_movement", (Object)Float.valueOf(0.0f)).forGetter(KineticWeapon::forwardMovement), (App)Codec.FLOAT.optionalFieldOf("damage_multiplier", (Object)Float.valueOf(1.0f)).forGetter(KineticWeapon::damageMultiplier), (App)SoundEvent.CODEC.optionalFieldOf("sound").forGetter(KineticWeapon::sound), (App)SoundEvent.CODEC.optionalFieldOf("hit_sound").forGetter(KineticWeapon::hitSound)).apply((Applicative)i, KineticWeapon::new)); public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.FLOAT, KineticWeapon::minReach, ByteBufCodecs.FLOAT, KineticWeapon::maxReach, ByteBufCodecs.FLOAT, KineticWeapon::hitboxMargin, ByteBufCodecs.VAR_INT, KineticWeapon::contactCooldownTicks, ByteBufCodecs.VAR_INT, KineticWeapon::delayTicks, Condition.STREAM_CODEC.apply(ByteBufCodecs::optional), KineticWeapon::dismountConditions, Condition.STREAM_CODEC.apply(ByteBufCodecs::optional), KineticWeapon::knockbackConditions, Condition.STREAM_CODEC.apply(ByteBufCodecs::optional), KineticWeapon::damageConditions, ByteBufCodecs.FLOAT, KineticWeapon::forwardMovement, ByteBufCodecs.FLOAT, KineticWeapon::damageMultiplier, SoundEvent.STREAM_CODEC.apply(ByteBufCodecs::optional), KineticWeapon::sound, SoundEvent.STREAM_CODEC.apply(ByteBufCodecs::optional), KineticWeapon::hitSound, KineticWeapon::new); public static Vec3 getMotion(Entity livingEntity) { if (!(livingEntity instanceof Player) && livingEntity.isPassenger()) { livingEntity = livingEntity.getRootVehicle(); } return livingEntity.getKnownSpeed().scale(20.0); } public void makeSound(Entity causer) { this.sound.ifPresent(s -> causer.level().playSound(causer, causer.getX(), causer.getY(), causer.getZ(), (Holder)s, causer.getSoundSource(), 1.0f, 1.0f)); } public void makeHitSound(Entity causer) { this.hitSound.ifPresent(s -> causer.level().playSound(null, causer.getX(), causer.getY(), causer.getZ(), (Holder)s, causer.getSoundSource(), 1.0f, 1.0f)); } public int computeDamageUseDuration() { return this.delayTicks + this.damageConditions.map(Condition::maxDurationTicks).orElse(0); } public void damageEntities(ItemStack stack, int ticksRemaining, LivingEntity livingEntity, EquipmentSlot equipmentSlot) { int ticksUsed = stack.getUseDuration(livingEntity) - ticksRemaining; if (ticksUsed < this.delayTicks) { return; } ticksUsed -= this.delayTicks; Vec3 attackerLookVector = livingEntity.getLookAngle(); double attackerSpeedProjection = attackerLookVector.dot(KineticWeapon.getMotion(livingEntity)); float actionFactor = livingEntity instanceof Player ? 1.0f : 0.2f; float rangeFactor = livingEntity instanceof Player ? 1.0f : 0.5f; double baseMobDamage = livingEntity.getAttributeBaseValue(Attributes.ATTACK_DAMAGE); boolean affected = false; for (EntityHitResult hitResult : ProjectileUtil.getHitEntitiesAlong(livingEntity, rangeFactor * this.minReach, rangeFactor * this.maxReach, this.hitboxMargin, e -> PiercingWeapon.canHitEntity(livingEntity, e))) { boolean dealsDamage; Entity otherEntity = hitResult.getEntity(); boolean wasStabbed = livingEntity.wasRecentlyStabbed(otherEntity, this.contactCooldownTicks); livingEntity.rememberStabbedEntity(otherEntity); if (wasStabbed) continue; double targetSpeedProjection = attackerLookVector.dot(KineticWeapon.getMotion(otherEntity)); double relativeSpeed = Math.max(0.0, attackerSpeedProjection - targetSpeedProjection); boolean dealsDismount = this.dismountConditions.isPresent() && this.dismountConditions.get().test(ticksUsed, attackerSpeedProjection, relativeSpeed, actionFactor); boolean dealsKnockback = this.knockbackConditions.isPresent() && this.knockbackConditions.get().test(ticksUsed, attackerSpeedProjection, relativeSpeed, actionFactor); boolean bl = dealsDamage = this.damageConditions.isPresent() && this.damageConditions.get().test(ticksUsed, attackerSpeedProjection, relativeSpeed, actionFactor); if (!dealsDismount && !dealsKnockback && !dealsDamage) continue; float damageDealt = (float)baseMobDamage + (float)Mth.floor(relativeSpeed * (double)this.damageMultiplier); affected |= livingEntity.stabAttack(equipmentSlot, otherEntity, damageDealt, dealsDamage, dealsKnockback, dealsDismount); } if (affected) { this.makeHitSound(livingEntity); livingEntity.level().broadcastEntityEvent(livingEntity, (byte)2); if (livingEntity instanceof ServerPlayer) { ServerPlayer player = (ServerPlayer)livingEntity; CriteriaTriggers.SPEAR_MOBS_TRIGGER.trigger(player, livingEntity.stabbedEntities()); } } } public record Condition(int maxDurationTicks, float minSpeed, float minRelativeSpeed) { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group((App)ExtraCodecs.NON_NEGATIVE_INT.fieldOf("max_duration_ticks").forGetter(Condition::maxDurationTicks), (App)Codec.FLOAT.optionalFieldOf("min_speed", (Object)Float.valueOf(0.0f)).forGetter(Condition::minSpeed), (App)Codec.FLOAT.optionalFieldOf("min_relative_speed", (Object)Float.valueOf(0.0f)).forGetter(Condition::minRelativeSpeed)).apply((Applicative)i, Condition::new)); public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.VAR_INT, Condition::maxDurationTicks, ByteBufCodecs.FLOAT, Condition::minSpeed, ByteBufCodecs.FLOAT, Condition::minRelativeSpeed, Condition::new); public boolean test(int ticksUsed, double attackerSpeed, double relativeSpeed, double entityFactor) { return ticksUsed <= this.maxDurationTicks && attackerSpeed >= (double)this.minSpeed * entityFactor && relativeSpeed >= (double)this.minRelativeSpeed * entityFactor; } public static Optional ofAttackerSpeed(int untilTicks, float minAttackerSpeed) { return Optional.of(new Condition(untilTicks, minAttackerSpeed, 0.0f)); } public static Optional ofRelativeSpeed(int untilTicks, float minRelativeSpeed) { return Optional.of(new Condition(untilTicks, 0.0f, minRelativeSpeed)); } } }