2025-11-24 22:52:51 +03:00

3691 lines
149 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.annotations.VisibleForTesting
* com.google.common.base.Objects
* com.google.common.collect.ImmutableList
* com.google.common.collect.Lists
* com.google.common.collect.Maps
* com.mojang.datafixers.util.Pair
* com.mojang.logging.LogUtils
* com.mojang.serialization.Codec
* com.mojang.serialization.DataResult
* com.mojang.serialization.Dynamic
* com.mojang.serialization.DynamicOps
* com.mojang.serialization.JavaOps
* it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair
* it.unimi.dsi.fastutil.objects.Object2LongMap
* it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap
* it.unimi.dsi.fastutil.objects.ObjectArrayList
* it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
* it.unimi.dsi.fastutil.objects.Reference2ObjectMap
* org.jetbrains.annotations.Contract
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
*/
package net.minecraft.world.entity;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.Dynamic;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JavaOps;
import it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundAnimatePacket;
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
import net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.network.protocol.game.ClientboundTakeItemEntityPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.waypoints.ServerWaypointManager;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.EntityTypeTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.BlockUtil;
import net.minecraft.util.Mth;
import net.minecraft.util.Util;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.Difficulty;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.damagesource.CombatRules;
import net.minecraft.world.damagesource.CombatTracker;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageTypes;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffectUtil;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Attackable;
import net.minecraft.world.entity.ElytraAnimationState;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.EntityEquipment;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.EquipmentSlotGroup;
import net.minecraft.world.entity.ExperienceOrb;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.InterpolationHandler;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.SlotAccess;
import net.minecraft.world.entity.WalkAnimationState;
import net.minecraft.world.entity.ai.Brain;
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.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.attributes.DefaultAttributes;
import net.minecraft.world.entity.animal.FlyingAnimal;
import net.minecraft.world.entity.animal.wolf.Wolf;
import net.minecraft.world.entity.boss.wither.WitherBoss;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.BlocksAttacks;
import net.minecraft.world.item.component.DeathProtection;
import net.minecraft.world.item.component.Weapon;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.effects.EnchantmentLocationBasedEffect;
import net.minecraft.world.item.equipment.Equippable;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BedBlock;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.HoneyBlock;
import net.minecraft.world.level.block.LadderBlock;
import net.minecraft.world.level.block.PowderSnowBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.TrapDoorBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gamerules.GameRules;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.waypoints.Waypoint;
import net.minecraft.world.waypoints.WaypointTransmitter;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public abstract class LivingEntity
extends Entity
implements Attackable,
WaypointTransmitter {
private static final Logger LOGGER = LogUtils.getLogger();
private static final String TAG_ACTIVE_EFFECTS = "active_effects";
public static final String TAG_ATTRIBUTES = "attributes";
public static final String TAG_SLEEPING_POS = "sleeping_pos";
public static final String TAG_EQUIPMENT = "equipment";
public static final String TAG_BRAIN = "Brain";
public static final String TAG_FALL_FLYING = "FallFlying";
public static final String TAG_HURT_TIME = "HurtTime";
public static final String TAG_DEATH_TIME = "DeathTime";
public static final String TAG_HURT_BY_TIMESTAMP = "HurtByTimestamp";
public static final String TAG_HEALTH = "Health";
private static final Identifier SPEED_MODIFIER_POWDER_SNOW_ID = Identifier.withDefaultNamespace("powder_snow");
private static final Identifier SPRINTING_MODIFIER_ID = Identifier.withDefaultNamespace("sprinting");
private static final AttributeModifier SPEED_MODIFIER_SPRINTING = new AttributeModifier(SPRINTING_MODIFIER_ID, 0.3f, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL);
public static final int EQUIPMENT_SLOT_OFFSET = 98;
public static final int ARMOR_SLOT_OFFSET = 100;
public static final int BODY_ARMOR_OFFSET = 105;
public static final int SADDLE_OFFSET = 106;
public static final int PLAYER_HURT_EXPERIENCE_TIME = 100;
private static final int DAMAGE_SOURCE_TIMEOUT = 40;
public static final double MIN_MOVEMENT_DISTANCE = 0.003;
public static final double DEFAULT_BASE_GRAVITY = 0.08;
public static final int DEATH_DURATION = 20;
protected static final float INPUT_FRICTION = 0.98f;
private static final int TICKS_PER_ELYTRA_FREE_FALL_EVENT = 10;
private static final int FREE_FALL_EVENTS_PER_ELYTRA_BREAK = 2;
public static final float BASE_JUMP_POWER = 0.42f;
protected static final float DEFAULT_KNOCKBACK = 0.4f;
protected static final int INVULNERABLE_DURATION = 20;
private static final double MAX_LINE_OF_SIGHT_TEST_RANGE = 128.0;
protected static final int LIVING_ENTITY_FLAG_IS_USING = 1;
protected static final int LIVING_ENTITY_FLAG_OFF_HAND = 2;
protected static final int LIVING_ENTITY_FLAG_SPIN_ATTACK = 4;
protected static final EntityDataAccessor<Byte> DATA_LIVING_ENTITY_FLAGS = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.BYTE);
private static final EntityDataAccessor<Float> DATA_HEALTH_ID = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<List<ParticleOptions>> DATA_EFFECT_PARTICLES = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.PARTICLES);
private static final EntityDataAccessor<Boolean> DATA_EFFECT_AMBIENCE_ID = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Integer> DATA_ARROW_COUNT_ID = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> DATA_STINGER_COUNT_ID = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Optional<BlockPos>> SLEEPING_POS_ID = SynchedEntityData.defineId(LivingEntity.class, EntityDataSerializers.OPTIONAL_BLOCK_POS);
private static final int PARTICLE_FREQUENCY_WHEN_INVISIBLE = 15;
protected static final EntityDimensions SLEEPING_DIMENSIONS = EntityDimensions.fixed(0.2f, 0.2f).withEyeHeight(0.2f);
public static final float EXTRA_RENDER_CULLING_SIZE_WITH_BIG_HAT = 0.5f;
public static final float DEFAULT_BABY_SCALE = 0.5f;
private static final float WATER_FLOAT_IMPULSE = 0.04f;
public static final Predicate<LivingEntity> PLAYER_NOT_WEARING_DISGUISE_ITEM = livingEntity -> {
if (!(livingEntity instanceof Player)) {
return true;
}
Player player = (Player)livingEntity;
ItemStack helmet = player.getItemBySlot(EquipmentSlot.HEAD);
return !helmet.is(ItemTags.GAZE_DISGUISE_EQUIPMENT);
};
private static final Dynamic<?> EMPTY_BRAIN = new Dynamic((DynamicOps)JavaOps.INSTANCE, Map.of("memories", Map.of()));
private final AttributeMap attributes;
private final CombatTracker combatTracker = new CombatTracker(this);
private final Map<Holder<MobEffect>, MobEffectInstance> activeEffects = Maps.newHashMap();
private final Map<EquipmentSlot, ItemStack> lastEquipmentItems = Util.makeEnumMap(EquipmentSlot.class, slot -> ItemStack.EMPTY);
public boolean swinging;
private boolean discardFriction = false;
public InteractionHand swingingArm;
public int swingTime;
public int removeArrowTime;
public int removeStingerTime;
public int hurtTime;
public int hurtDuration;
public int deathTime;
public float oAttackAnim;
public float attackAnim;
protected int attackStrengthTicker;
protected int itemSwapTicker;
public final WalkAnimationState walkAnimation = new WalkAnimationState();
public float yBodyRot;
public float yBodyRotO;
public float yHeadRot;
public float yHeadRotO;
public final ElytraAnimationState elytraAnimationState = new ElytraAnimationState(this);
protected @Nullable EntityReference<Player> lastHurtByPlayer;
protected int lastHurtByPlayerMemoryTime;
protected boolean dead;
protected int noActionTime;
protected float lastHurt;
protected boolean jumping;
public float xxa;
public float yya;
public float zza;
protected InterpolationHandler interpolation = new InterpolationHandler(this);
protected double lerpYHeadRot;
protected int lerpHeadSteps;
private boolean effectsDirty = true;
private @Nullable EntityReference<LivingEntity> lastHurtByMob;
private int lastHurtByMobTimestamp;
private @Nullable LivingEntity lastHurtMob;
private int lastHurtMobTimestamp;
private float speed;
private int noJumpDelay;
private float absorptionAmount;
protected ItemStack useItem = ItemStack.EMPTY;
protected int useItemRemaining;
protected int fallFlyTicks;
private long lastEnemyHitTime = Integer.MIN_VALUE;
private BlockPos lastPos;
private Optional<BlockPos> lastClimbablePos = Optional.empty();
private @Nullable DamageSource lastDamageSource;
private long lastDamageStamp;
protected int autoSpinAttackTicks;
protected float autoSpinAttackDmg;
protected @Nullable ItemStack autoSpinAttackItemStack;
protected @Nullable Object2LongMap<Entity> recentKineticEnemies;
private float swimAmount;
private float swimAmountO;
protected Brain<?> brain;
private boolean skipDropExperience;
private final EnumMap<EquipmentSlot, Reference2ObjectMap<Enchantment, Set<EnchantmentLocationBasedEffect>>> activeLocationDependentEnchantments = new EnumMap(EquipmentSlot.class);
protected final EntityEquipment equipment;
private Waypoint.Icon locatorBarIcon = new Waypoint.Icon();
protected LivingEntity(EntityType<? extends LivingEntity> type, Level level) {
super(type, level);
this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
this.setHealth(this.getMaxHealth());
this.equipment = this.createEquipment();
this.blocksBuilding = true;
this.reapplyPosition();
this.setYRot((float)(Math.random() * 6.2831854820251465));
this.yHeadRot = this.getYRot();
this.brain = this.makeBrain(EMPTY_BRAIN);
}
@Override
public @Nullable LivingEntity asLivingEntity() {
return this;
}
@Contract(pure=true)
protected EntityEquipment createEquipment() {
return new EntityEquipment();
}
public Brain<?> getBrain() {
return this.brain;
}
protected Brain.Provider<?> brainProvider() {
return Brain.provider(ImmutableList.of(), ImmutableList.of());
}
protected Brain<?> makeBrain(Dynamic<?> input) {
return this.brainProvider().makeBrain(input);
}
@Override
public void kill(ServerLevel level) {
this.hurtServer(level, this.damageSources().genericKill(), Float.MAX_VALUE);
}
public boolean canAttackType(EntityType<?> targetType) {
return true;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
entityData.define(DATA_LIVING_ENTITY_FLAGS, (byte)0);
entityData.define(DATA_EFFECT_PARTICLES, List.of());
entityData.define(DATA_EFFECT_AMBIENCE_ID, false);
entityData.define(DATA_ARROW_COUNT_ID, 0);
entityData.define(DATA_STINGER_COUNT_ID, 0);
entityData.define(DATA_HEALTH_ID, Float.valueOf(1.0f));
entityData.define(SLEEPING_POS_ID, Optional.empty());
}
public static AttributeSupplier.Builder createLivingAttributes() {
return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS).add(Attributes.MAX_ABSORPTION).add(Attributes.STEP_HEIGHT).add(Attributes.SCALE).add(Attributes.GRAVITY).add(Attributes.SAFE_FALL_DISTANCE).add(Attributes.FALL_DAMAGE_MULTIPLIER).add(Attributes.JUMP_STRENGTH).add(Attributes.OXYGEN_BONUS).add(Attributes.BURNING_TIME).add(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE).add(Attributes.WATER_MOVEMENT_EFFICIENCY).add(Attributes.MOVEMENT_EFFICIENCY).add(Attributes.ATTACK_KNOCKBACK).add(Attributes.CAMERA_DISTANCE).add(Attributes.WAYPOINT_TRANSMIT_RANGE);
}
@Override
protected void checkFallDamage(double ya, boolean onGround, BlockState onState, BlockPos pos) {
Level level;
if (!this.isInWater()) {
this.updateInWaterStateAndDoWaterCurrentPushing();
}
if ((level = this.level()) instanceof ServerLevel) {
ServerLevel level2 = (ServerLevel)level;
if (onGround && this.fallDistance > 0.0) {
this.onChangedBlock(level2, pos);
double power = Math.max(0, Mth.floor(this.calculateFallPower(this.fallDistance)));
if (power > 0.0 && !onState.isAir()) {
double x = this.getX();
double y = this.getY();
double z = this.getZ();
BlockPos entityPos = this.blockPosition();
if (pos.getX() != entityPos.getX() || pos.getZ() != entityPos.getZ()) {
double xDiff = x - (double)pos.getX() - 0.5;
double zDiff = z - (double)pos.getZ() - 0.5;
double maxDiff = Math.max(Math.abs(xDiff), Math.abs(zDiff));
x = (double)pos.getX() + 0.5 + xDiff / maxDiff * 0.5;
z = (double)pos.getZ() + 0.5 + zDiff / maxDiff * 0.5;
}
double scale = Math.min((double)0.2f + power / 15.0, 2.5);
int particles = (int)(150.0 * scale);
level2.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, onState), x, y, z, particles, 0.0, 0.0, 0.0, 0.15f);
}
}
}
super.checkFallDamage(ya, onGround, onState, pos);
if (onGround) {
this.lastClimbablePos = Optional.empty();
}
}
public boolean canBreatheUnderwater() {
return this.getType().is(EntityTypeTags.CAN_BREATHE_UNDER_WATER);
}
public float getSwimAmount(float a) {
return Mth.lerp(a, this.swimAmountO, this.swimAmount);
}
public boolean hasLandedInLiquid() {
return this.getDeltaMovement().y() < (double)1.0E-5f && this.isInLiquid();
}
@Override
public void baseTick() {
LivingEntity hurtByMob;
Level level;
Level level2;
this.oAttackAnim = this.attackAnim;
if (this.firstTick) {
this.getSleepingPos().ifPresent(this::setPosToBed);
}
if ((level2 = this.level()) instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level2;
EnchantmentHelper.tickEffects(serverLevel, this);
}
super.baseTick();
ProfilerFiller profiler = Profiler.get();
profiler.push("livingEntityBaseTick");
if (this.isAlive() && (level = this.level()) instanceof ServerLevel) {
double damagePerBlock;
double dist;
ServerLevel level3 = (ServerLevel)level;
boolean isPlayer = this instanceof Player;
if (this.isInWall()) {
this.hurtServer(level3, this.damageSources().inWall(), 1.0f);
} else if (isPlayer && !level3.getWorldBorder().isWithinBounds(this.getBoundingBox()) && (dist = level3.getWorldBorder().getDistanceToBorder(this) + level3.getWorldBorder().getSafeZone()) < 0.0 && (damagePerBlock = level3.getWorldBorder().getDamagePerBlock()) > 0.0) {
this.hurtServer(level3, this.damageSources().outOfBorder(), Math.max(1, Mth.floor(-dist * damagePerBlock)));
}
if (this.isEyeInFluid(FluidTags.WATER) && !level3.getBlockState(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())).is(Blocks.BUBBLE_COLUMN)) {
boolean canDrownInWater;
boolean bl = canDrownInWater = !this.canBreatheUnderwater() && !MobEffectUtil.hasWaterBreathing(this) && (!isPlayer || !((Player)this).getAbilities().invulnerable);
if (canDrownInWater) {
this.setAirSupply(this.decreaseAirSupply(this.getAirSupply()));
if (this.shouldTakeDrowningDamage()) {
this.setAirSupply(0);
level3.broadcastEntityEvent(this, (byte)67);
this.hurtServer(level3, this.damageSources().drown(), 2.0f);
}
} else if (this.getAirSupply() < this.getMaxAirSupply() && !this.hasEffect(MobEffects.BREATH_OF_THE_NAUTILUS)) {
this.setAirSupply(this.increaseAirSupply(this.getAirSupply()));
}
if (this.isPassenger() && this.getVehicle() != null && this.getVehicle().dismountsUnderwater()) {
this.stopRiding();
}
} else if (this.getAirSupply() < this.getMaxAirSupply()) {
this.setAirSupply(this.increaseAirSupply(this.getAirSupply()));
}
BlockPos pos = this.blockPosition();
if (!Objects.equal((Object)this.lastPos, (Object)pos)) {
this.lastPos = pos;
this.onChangedBlock(level3, pos);
}
}
if (this.hurtTime > 0) {
--this.hurtTime;
}
if (this.invulnerableTime > 0 && !(this instanceof ServerPlayer)) {
--this.invulnerableTime;
}
if (this.isDeadOrDying() && this.level().shouldTickDeath(this)) {
this.tickDeath();
}
if (this.lastHurtByPlayerMemoryTime > 0) {
--this.lastHurtByPlayerMemoryTime;
} else {
this.lastHurtByPlayer = null;
}
if (this.lastHurtMob != null && !this.lastHurtMob.isAlive()) {
this.lastHurtMob = null;
}
if ((hurtByMob = this.getLastHurtByMob()) != null) {
if (!hurtByMob.isAlive()) {
this.setLastHurtByMob(null);
} else if (this.tickCount - this.lastHurtByMobTimestamp > 100) {
this.setLastHurtByMob(null);
}
}
this.tickEffects();
this.yHeadRotO = this.yHeadRot;
this.yBodyRotO = this.yBodyRot;
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
profiler.pop();
}
protected boolean shouldTakeDrowningDamage() {
return this.getAirSupply() <= -20;
}
@Override
protected float getBlockSpeedFactor() {
return Mth.lerp((float)this.getAttributeValue(Attributes.MOVEMENT_EFFICIENCY), super.getBlockSpeedFactor(), 1.0f);
}
public float getLuck() {
return 0.0f;
}
protected void removeFrost() {
AttributeInstance speed = this.getAttribute(Attributes.MOVEMENT_SPEED);
if (speed == null) {
return;
}
if (speed.getModifier(SPEED_MODIFIER_POWDER_SNOW_ID) != null) {
speed.removeModifier(SPEED_MODIFIER_POWDER_SNOW_ID);
}
}
protected void tryAddFrost() {
int ticksFrozen;
if (!this.getBlockStateOnLegacy().isAir() && (ticksFrozen = this.getTicksFrozen()) > 0) {
AttributeInstance speed = this.getAttribute(Attributes.MOVEMENT_SPEED);
if (speed == null) {
return;
}
float slowAmount = -0.05f * this.getPercentFrozen();
speed.addTransientModifier(new AttributeModifier(SPEED_MODIFIER_POWDER_SNOW_ID, slowAmount, AttributeModifier.Operation.ADD_VALUE));
}
}
protected void onChangedBlock(ServerLevel level, BlockPos pos) {
EnchantmentHelper.runLocationChangedEffects(level, this);
}
public boolean isBaby() {
return false;
}
public float getAgeScale() {
return this.isBaby() ? 0.5f : 1.0f;
}
public final float getScale() {
AttributeMap attributes = this.getAttributes();
if (attributes == null) {
return 1.0f;
}
return this.sanitizeScale((float)attributes.getValue(Attributes.SCALE));
}
protected float sanitizeScale(float scale) {
return scale;
}
public boolean isAffectedByFluids() {
return true;
}
protected void tickDeath() {
++this.deathTime;
if (this.deathTime >= 20 && !this.level().isClientSide() && !this.isRemoved()) {
this.level().broadcastEntityEvent(this, (byte)60);
this.remove(Entity.RemovalReason.KILLED);
}
}
public boolean shouldDropExperience() {
return !this.isBaby();
}
protected boolean shouldDropLoot(ServerLevel level) {
return !this.isBaby() && level.getGameRules().get(GameRules.MOB_DROPS) != false;
}
protected int decreaseAirSupply(int currentSupply) {
AttributeInstance respiration = this.getAttribute(Attributes.OXYGEN_BONUS);
double oxygenBonus = respiration != null ? respiration.getValue() : 0.0;
if (oxygenBonus > 0.0 && this.random.nextDouble() >= 1.0 / (oxygenBonus + 1.0)) {
return currentSupply;
}
return currentSupply - 1;
}
protected int increaseAirSupply(int currentSupply) {
return Math.min(currentSupply + 4, this.getMaxAirSupply());
}
public final int getExperienceReward(ServerLevel level, @Nullable Entity killer) {
return EnchantmentHelper.processMobExperience(level, killer, this, this.getBaseExperienceReward(level));
}
protected int getBaseExperienceReward(ServerLevel level) {
return 0;
}
protected boolean isAlwaysExperienceDropper() {
return false;
}
public @Nullable LivingEntity getLastHurtByMob() {
return EntityReference.getLivingEntity(this.lastHurtByMob, this.level());
}
public @Nullable Player getLastHurtByPlayer() {
return EntityReference.getPlayer(this.lastHurtByPlayer, this.level());
}
@Override
public LivingEntity getLastAttacker() {
return this.getLastHurtByMob();
}
public int getLastHurtByMobTimestamp() {
return this.lastHurtByMobTimestamp;
}
public void setLastHurtByPlayer(Player player, int timeToRemember) {
this.setLastHurtByPlayer(EntityReference.of(player), timeToRemember);
}
public void setLastHurtByPlayer(UUID player, int timeToRemember) {
this.setLastHurtByPlayer(EntityReference.of(player), timeToRemember);
}
private void setLastHurtByPlayer(EntityReference<Player> player, int timeToRemember) {
this.lastHurtByPlayer = player;
this.lastHurtByPlayerMemoryTime = timeToRemember;
}
public void setLastHurtByMob(@Nullable LivingEntity hurtBy) {
this.lastHurtByMob = EntityReference.of(hurtBy);
this.lastHurtByMobTimestamp = this.tickCount;
}
public @Nullable LivingEntity getLastHurtMob() {
return this.lastHurtMob;
}
public int getLastHurtMobTimestamp() {
return this.lastHurtMobTimestamp;
}
public void setLastHurtMob(Entity target) {
this.lastHurtMob = target instanceof LivingEntity ? (LivingEntity)target : null;
this.lastHurtMobTimestamp = this.tickCount;
}
public int getNoActionTime() {
return this.noActionTime;
}
public void setNoActionTime(int noActionTime) {
this.noActionTime = noActionTime;
}
public boolean shouldDiscardFriction() {
return this.discardFriction;
}
public void setDiscardFriction(boolean discardFriction) {
this.discardFriction = discardFriction;
}
protected boolean doesEmitEquipEvent(EquipmentSlot slot) {
return true;
}
public void onEquipItem(EquipmentSlot slot, ItemStack oldStack, ItemStack stack) {
if (this.level().isClientSide() || this.isSpectator()) {
return;
}
if (ItemStack.isSameItemSameComponents(oldStack, stack) || this.firstTick) {
return;
}
Equippable equippable = stack.get(DataComponents.EQUIPPABLE);
if (!this.isSilent() && equippable != null && slot == equippable.slot()) {
this.level().playSeededSound(null, this.getX(), this.getY(), this.getZ(), this.getEquipSound(slot, stack, equippable), this.getSoundSource(), 1.0f, 1.0f, this.random.nextLong());
}
if (this.doesEmitEquipEvent(slot)) {
this.gameEvent(equippable != null ? GameEvent.EQUIP : GameEvent.UNEQUIP);
}
}
protected Holder<SoundEvent> getEquipSound(EquipmentSlot slot, ItemStack stack, Equippable equippable) {
return equippable.equipSound();
}
@Override
public void remove(Entity.RemovalReason reason) {
Level level;
if ((reason == Entity.RemovalReason.KILLED || reason == Entity.RemovalReason.DISCARDED) && (level = this.level()) instanceof ServerLevel) {
ServerLevel level2 = (ServerLevel)level;
this.triggerOnDeathMobEffects(level2, reason);
}
super.remove(reason);
this.brain.clearMemories();
}
@Override
public void onRemoval(Entity.RemovalReason reason) {
super.onRemoval(reason);
Level level = this.level();
if (level instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level;
serverLevel.getWaypointManager().untrackWaypoint(this);
}
}
protected void triggerOnDeathMobEffects(ServerLevel level, Entity.RemovalReason reason) {
for (MobEffectInstance effect : this.getActiveEffects()) {
effect.onMobRemoved(level, this, reason);
}
this.activeEffects.clear();
}
@Override
protected void addAdditionalSaveData(ValueOutput output) {
output.putFloat(TAG_HEALTH, this.getHealth());
output.putShort(TAG_HURT_TIME, (short)this.hurtTime);
output.putInt(TAG_HURT_BY_TIMESTAMP, this.lastHurtByMobTimestamp);
output.putShort(TAG_DEATH_TIME, (short)this.deathTime);
output.putFloat("AbsorptionAmount", this.getAbsorptionAmount());
output.store(TAG_ATTRIBUTES, AttributeInstance.Packed.LIST_CODEC, this.getAttributes().pack());
if (!this.activeEffects.isEmpty()) {
output.store(TAG_ACTIVE_EFFECTS, MobEffectInstance.CODEC.listOf(), List.copyOf(this.activeEffects.values()));
}
output.putBoolean(TAG_FALL_FLYING, this.isFallFlying());
this.getSleepingPos().ifPresent(sleepingPos -> output.store(TAG_SLEEPING_POS, BlockPos.CODEC, sleepingPos));
DataResult writtenBrain = this.brain.serializeStart(NbtOps.INSTANCE).map(t -> new Dynamic((DynamicOps)NbtOps.INSTANCE, t));
writtenBrain.resultOrPartial(arg_0 -> ((Logger)LOGGER).error(arg_0)).ifPresent(b -> output.store(TAG_BRAIN, Codec.PASSTHROUGH, b));
if (this.lastHurtByPlayer != null) {
this.lastHurtByPlayer.store(output, "last_hurt_by_player");
output.putInt("last_hurt_by_player_memory_time", this.lastHurtByPlayerMemoryTime);
}
if (this.lastHurtByMob != null) {
this.lastHurtByMob.store(output, "last_hurt_by_mob");
output.putInt("ticks_since_last_hurt_by_mob", this.tickCount - this.lastHurtByMobTimestamp);
}
if (!this.equipment.isEmpty()) {
output.store(TAG_EQUIPMENT, EntityEquipment.CODEC, this.equipment);
}
if (this.locatorBarIcon.hasData()) {
output.store("locator_bar_icon", Waypoint.Icon.CODEC, this.locatorBarIcon);
}
}
public @Nullable ItemEntity drop(ItemStack itemStack, boolean randomly, boolean thrownFromHand) {
if (itemStack.isEmpty()) {
return null;
}
if (this.level().isClientSide()) {
this.swing(InteractionHand.MAIN_HAND);
return null;
}
ItemEntity entity = this.createItemStackToDrop(itemStack, randomly, thrownFromHand);
if (entity != null) {
this.level().addFreshEntity(entity);
}
return entity;
}
@Override
protected void readAdditionalSaveData(ValueInput input) {
this.internalSetAbsorptionAmount(input.getFloatOr("AbsorptionAmount", 0.0f));
if (this.level() != null && !this.level().isClientSide()) {
input.read(TAG_ATTRIBUTES, AttributeInstance.Packed.LIST_CODEC).ifPresent(this.getAttributes()::apply);
}
List effects = input.read(TAG_ACTIVE_EFFECTS, MobEffectInstance.CODEC.listOf()).orElse(List.of());
this.activeEffects.clear();
for (MobEffectInstance effect : effects) {
this.activeEffects.put(effect.getEffect(), effect);
this.effectsDirty = true;
}
this.setHealth(input.getFloatOr(TAG_HEALTH, this.getMaxHealth()));
this.hurtTime = input.getShortOr(TAG_HURT_TIME, (short)0);
this.deathTime = input.getShortOr(TAG_DEATH_TIME, (short)0);
this.lastHurtByMobTimestamp = input.getIntOr(TAG_HURT_BY_TIMESTAMP, 0);
input.getString("Team").ifPresent(teamName -> {
boolean success;
Scoreboard scoreboard = this.level().getScoreboard();
PlayerTeam team = scoreboard.getPlayerTeam((String)teamName);
boolean bl = success = team != null && scoreboard.addPlayerToTeam(this.getStringUUID(), team);
if (!success) {
LOGGER.warn("Unable to add mob to team \"{}\" (that team probably doesn't exist)", teamName);
}
});
this.setSharedFlag(7, input.getBooleanOr(TAG_FALL_FLYING, false));
input.read(TAG_SLEEPING_POS, BlockPos.CODEC).ifPresentOrElse(sleepingPos -> {
this.setSleepingPos((BlockPos)sleepingPos);
this.entityData.set(DATA_POSE, Pose.SLEEPING);
if (!this.firstTick) {
this.setPosToBed((BlockPos)sleepingPos);
}
}, this::clearSleepingPos);
input.read(TAG_BRAIN, Codec.PASSTHROUGH).ifPresent(brainTag -> {
this.brain = this.makeBrain((Dynamic<?>)brainTag);
});
this.lastHurtByPlayer = EntityReference.read(input, "last_hurt_by_player");
this.lastHurtByPlayerMemoryTime = input.getIntOr("last_hurt_by_player_memory_time", 0);
this.lastHurtByMob = EntityReference.read(input, "last_hurt_by_mob");
this.lastHurtByMobTimestamp = input.getIntOr("ticks_since_last_hurt_by_mob", 0) + this.tickCount;
this.equipment.setAll(input.read(TAG_EQUIPMENT, EntityEquipment.CODEC).orElseGet(EntityEquipment::new));
this.locatorBarIcon = input.read("locator_bar_icon", Waypoint.Icon.CODEC).orElseGet(Waypoint.Icon::new);
}
@Override
public void updateDataBeforeSync() {
super.updateDataBeforeSync();
this.updateDirtyEffects();
}
protected void tickEffects() {
Level level = this.level();
if (level instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level;
Iterator<Object> iterator = this.activeEffects.keySet().iterator();
try {
while (iterator.hasNext()) {
Holder mobEffect = (Holder)iterator.next();
MobEffectInstance effect = this.activeEffects.get(mobEffect);
if (!effect.tickServer(serverLevel, this, () -> this.onEffectUpdated(effect, true, null))) {
iterator.remove();
this.onEffectsRemoved(List.of(effect));
continue;
}
if (effect.getDuration() % 600 != 0) continue;
this.onEffectUpdated(effect, false, null);
}
}
catch (ConcurrentModificationException mobEffect) {}
} else {
for (MobEffectInstance effect : this.activeEffects.values()) {
effect.tickClient();
}
List<ParticleOptions> particles = this.entityData.get(DATA_EFFECT_PARTICLES);
if (!particles.isEmpty()) {
int ambientFactor;
boolean isAmbient = this.entityData.get(DATA_EFFECT_AMBIENCE_ID);
int bound = this.isInvisible() ? 15 : 4;
int n = ambientFactor = isAmbient ? 5 : 1;
if (this.random.nextInt(bound * ambientFactor) == 0) {
this.level().addParticle(Util.getRandom(particles, this.random), this.getRandomX(0.5), this.getRandomY(), this.getRandomZ(0.5), 1.0, 1.0, 1.0);
}
}
}
}
private void updateDirtyEffects() {
if (this.effectsDirty) {
this.updateInvisibilityStatus();
this.updateGlowingStatus();
this.effectsDirty = false;
}
}
protected void updateInvisibilityStatus() {
if (this.activeEffects.isEmpty()) {
this.removeEffectParticles();
this.setInvisible(false);
return;
}
this.setInvisible(this.hasEffect(MobEffects.INVISIBILITY));
this.updateSynchronizedMobEffectParticles();
}
private void updateSynchronizedMobEffectParticles() {
List<ParticleOptions> visibleEffectParticles = this.activeEffects.values().stream().filter(MobEffectInstance::isVisible).map(MobEffectInstance::getParticleOptions).toList();
this.entityData.set(DATA_EFFECT_PARTICLES, visibleEffectParticles);
this.entityData.set(DATA_EFFECT_AMBIENCE_ID, LivingEntity.areAllEffectsAmbient(this.activeEffects.values()));
}
private void updateGlowingStatus() {
boolean glowingState = this.isCurrentlyGlowing();
if (this.getSharedFlag(6) != glowingState) {
this.setSharedFlag(6, glowingState);
}
}
public double getVisibilityPercent(@Nullable Entity targetingEntity) {
double visibilityPercent = 1.0;
if (this.isDiscrete()) {
visibilityPercent *= 0.8;
}
if (this.isInvisible()) {
float coverPercentage = this.getArmorCoverPercentage();
if (coverPercentage < 0.1f) {
coverPercentage = 0.1f;
}
visibilityPercent *= 0.7 * (double)coverPercentage;
}
if (targetingEntity != null) {
ItemStack itemStack = this.getItemBySlot(EquipmentSlot.HEAD);
EntityType<?> type = targetingEntity.getType();
if (type == EntityType.SKELETON && itemStack.is(Items.SKELETON_SKULL) || type == EntityType.ZOMBIE && itemStack.is(Items.ZOMBIE_HEAD) || type == EntityType.PIGLIN && itemStack.is(Items.PIGLIN_HEAD) || type == EntityType.PIGLIN_BRUTE && itemStack.is(Items.PIGLIN_HEAD) || type == EntityType.CREEPER && itemStack.is(Items.CREEPER_HEAD)) {
visibilityPercent *= 0.5;
}
}
return visibilityPercent;
}
public boolean canAttack(LivingEntity target) {
if (target instanceof Player && this.level().getDifficulty() == Difficulty.PEACEFUL) {
return false;
}
return target.canBeSeenAsEnemy();
}
public boolean canBeSeenAsEnemy() {
return !this.isInvulnerable() && this.canBeSeenByAnyone();
}
public boolean canBeSeenByAnyone() {
return !this.isSpectator() && this.isAlive();
}
public static boolean areAllEffectsAmbient(Collection<MobEffectInstance> effects) {
for (MobEffectInstance effect : effects) {
if (!effect.isVisible() || effect.isAmbient()) continue;
return false;
}
return true;
}
protected void removeEffectParticles() {
this.entityData.set(DATA_EFFECT_PARTICLES, List.of());
}
public boolean removeAllEffects() {
if (this.level().isClientSide()) {
return false;
}
if (this.activeEffects.isEmpty()) {
return false;
}
HashMap copy = Maps.newHashMap(this.activeEffects);
this.activeEffects.clear();
this.onEffectsRemoved(copy.values());
return true;
}
public Collection<MobEffectInstance> getActiveEffects() {
return this.activeEffects.values();
}
public Map<Holder<MobEffect>, MobEffectInstance> getActiveEffectsMap() {
return this.activeEffects;
}
public boolean hasEffect(Holder<MobEffect> effect) {
return this.activeEffects.containsKey(effect);
}
public @Nullable MobEffectInstance getEffect(Holder<MobEffect> effect) {
return this.activeEffects.get(effect);
}
public float getEffectBlendFactor(Holder<MobEffect> effect, float partialTicks) {
MobEffectInstance instance = this.getEffect(effect);
if (instance != null) {
return instance.getBlendFactor(this, partialTicks);
}
return 0.0f;
}
public final boolean addEffect(MobEffectInstance newEffect) {
return this.addEffect(newEffect, null);
}
public boolean addEffect(MobEffectInstance newEffect, @Nullable Entity source) {
if (!this.canBeAffected(newEffect)) {
return false;
}
MobEffectInstance effect = this.activeEffects.get(newEffect.getEffect());
boolean changed = false;
if (effect == null) {
this.activeEffects.put(newEffect.getEffect(), newEffect);
this.onEffectAdded(newEffect, source);
changed = true;
newEffect.onEffectAdded(this);
} else if (effect.update(newEffect)) {
this.onEffectUpdated(effect, true, source);
changed = true;
}
newEffect.onEffectStarted(this);
return changed;
}
public boolean canBeAffected(MobEffectInstance newEffect) {
if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) {
return !newEffect.is(MobEffects.INFESTED);
}
if (this.getType().is(EntityTypeTags.IMMUNE_TO_OOZING)) {
return !newEffect.is(MobEffects.OOZING);
}
if (this.getType().is(EntityTypeTags.IGNORES_POISON_AND_REGEN)) {
return !newEffect.is(MobEffects.REGENERATION) && !newEffect.is(MobEffects.POISON);
}
return true;
}
public void forceAddEffect(MobEffectInstance newEffect, @Nullable Entity source) {
if (!this.canBeAffected(newEffect)) {
return;
}
MobEffectInstance previousEffect = this.activeEffects.put(newEffect.getEffect(), newEffect);
if (previousEffect == null) {
this.onEffectAdded(newEffect, source);
} else {
newEffect.copyBlendState(previousEffect);
this.onEffectUpdated(newEffect, true, source);
}
}
public boolean isInvertedHealAndHarm() {
return this.getType().is(EntityTypeTags.INVERTED_HEALING_AND_HARM);
}
public final @Nullable MobEffectInstance removeEffectNoUpdate(Holder<MobEffect> effect) {
return this.activeEffects.remove(effect);
}
public boolean removeEffect(Holder<MobEffect> effect) {
MobEffectInstance effectInstance = this.removeEffectNoUpdate(effect);
if (effectInstance != null) {
this.onEffectsRemoved(List.of(effectInstance));
return true;
}
return false;
}
protected void onEffectAdded(MobEffectInstance effect, @Nullable Entity source) {
if (!this.level().isClientSide()) {
this.effectsDirty = true;
effect.getEffect().value().addAttributeModifiers(this.getAttributes(), effect.getAmplifier());
this.sendEffectToPassengers(effect);
}
}
public void sendEffectToPassengers(MobEffectInstance effect) {
for (Entity passenger : this.getPassengers()) {
if (!(passenger instanceof ServerPlayer)) continue;
ServerPlayer serverPlayer = (ServerPlayer)passenger;
serverPlayer.connection.send(new ClientboundUpdateMobEffectPacket(this.getId(), effect, false));
}
}
protected void onEffectUpdated(MobEffectInstance effect, boolean doRefreshAttributes, @Nullable Entity source) {
if (this.level().isClientSide()) {
return;
}
this.effectsDirty = true;
if (doRefreshAttributes) {
MobEffect mobEffect = effect.getEffect().value();
mobEffect.removeAttributeModifiers(this.getAttributes());
mobEffect.addAttributeModifiers(this.getAttributes(), effect.getAmplifier());
this.refreshDirtyAttributes();
}
this.sendEffectToPassengers(effect);
}
protected void onEffectsRemoved(Collection<MobEffectInstance> effects) {
if (this.level().isClientSide()) {
return;
}
this.effectsDirty = true;
for (MobEffectInstance effect : effects) {
effect.getEffect().value().removeAttributeModifiers(this.getAttributes());
for (Entity passenger : this.getPassengers()) {
if (!(passenger instanceof ServerPlayer)) continue;
ServerPlayer serverPlayer = (ServerPlayer)passenger;
serverPlayer.connection.send(new ClientboundRemoveMobEffectPacket(this.getId(), effect.getEffect()));
}
}
this.refreshDirtyAttributes();
}
private void refreshDirtyAttributes() {
Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
for (AttributeInstance changedAttributeInstance : attributesToUpdate) {
this.onAttributeUpdated(changedAttributeInstance.getAttribute());
}
attributesToUpdate.clear();
}
protected void onAttributeUpdated(Holder<Attribute> attribute) {
Level level;
if (attribute.is(Attributes.MAX_HEALTH)) {
float currentMaxHealth = this.getMaxHealth();
if (this.getHealth() > currentMaxHealth) {
this.setHealth(currentMaxHealth);
}
} else if (attribute.is(Attributes.MAX_ABSORPTION)) {
float currentMaxAbsorption = this.getMaxAbsorption();
if (this.getAbsorptionAmount() > currentMaxAbsorption) {
this.setAbsorptionAmount(currentMaxAbsorption);
}
} else if (attribute.is(Attributes.SCALE)) {
this.refreshDimensions();
} else if (attribute.is(Attributes.WAYPOINT_TRANSMIT_RANGE) && (level = this.level()) instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level;
ServerWaypointManager waypointManager = serverLevel.getWaypointManager();
if (this.attributes.getValue(attribute) > 0.0) {
waypointManager.trackWaypoint(this);
} else {
waypointManager.untrackWaypoint(this);
}
}
}
public void heal(float heal) {
float health = this.getHealth();
if (health > 0.0f) {
this.setHealth(health + heal);
}
}
public float getHealth() {
return this.entityData.get(DATA_HEALTH_ID).floatValue();
}
public void setHealth(float health) {
this.entityData.set(DATA_HEALTH_ID, Float.valueOf(Mth.clamp(health, 0.0f, this.getMaxHealth())));
}
public boolean isDeadOrDying() {
return this.getHealth() <= 0.0f;
}
@Override
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
Entity entity;
boolean success;
boolean blocked;
if (this.isInvulnerableTo(level, source)) {
return false;
}
if (this.isDeadOrDying()) {
return false;
}
if (source.is(DamageTypeTags.IS_FIRE) && this.hasEffect(MobEffects.FIRE_RESISTANCE)) {
return false;
}
if (this.isSleeping()) {
this.stopSleeping();
}
this.noActionTime = 0;
if (damage < 0.0f) {
damage = 0.0f;
}
float originalDamage = damage;
ItemStack itemInUse = this.getUseItem();
float damageBlocked = this.applyItemBlocking(level, source, damage);
damage -= damageBlocked;
boolean bl = blocked = damageBlocked > 0.0f;
if (source.is(DamageTypeTags.IS_FREEZING) && this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) {
damage *= 5.0f;
}
if (source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
this.hurtHelmet(source, damage);
damage *= 0.75f;
}
if (Float.isNaN(damage) || Float.isInfinite(damage)) {
damage = Float.MAX_VALUE;
}
boolean tookFullDamage = true;
if ((float)this.invulnerableTime > 10.0f && !source.is(DamageTypeTags.BYPASSES_COOLDOWN)) {
if (damage <= this.lastHurt) {
return false;
}
this.actuallyHurt(level, source, damage - this.lastHurt);
this.lastHurt = damage;
tookFullDamage = false;
} else {
this.lastHurt = damage;
this.invulnerableTime = 20;
this.actuallyHurt(level, source, damage);
this.hurtTime = this.hurtDuration = 10;
}
this.resolveMobResponsibleForDamage(source);
this.resolvePlayerResponsibleForDamage(source);
if (tookFullDamage) {
BlocksAttacks blocksAttacks = itemInUse.get(DataComponents.BLOCKS_ATTACKS);
if (blocked && blocksAttacks != null) {
blocksAttacks.onBlocked(level, this);
} else {
level.broadcastDamageEvent(this, source);
}
if (!(source.is(DamageTypeTags.NO_IMPACT) || blocked && !(damage > 0.0f))) {
this.markHurt();
}
if (!source.is(DamageTypeTags.NO_KNOCKBACK)) {
double xd = 0.0;
double zd = 0.0;
Entity entity2 = source.getDirectEntity();
if (entity2 instanceof Projectile) {
Projectile projectile = (Projectile)entity2;
DoubleDoubleImmutablePair knockbackDirection = projectile.calculateHorizontalHurtKnockbackDirection(this, source);
xd = -knockbackDirection.leftDouble();
zd = -knockbackDirection.rightDouble();
} else if (source.getSourcePosition() != null) {
xd = source.getSourcePosition().x() - this.getX();
zd = source.getSourcePosition().z() - this.getZ();
}
this.knockback(0.4f, xd, zd);
if (!blocked) {
this.indicateDamage(xd, zd);
}
}
}
if (this.isDeadOrDying()) {
if (!this.checkTotemDeathProtection(source)) {
if (tookFullDamage) {
this.makeSound(this.getDeathSound());
this.playSecondaryHurtSound(source);
}
this.die(source);
}
} else if (tookFullDamage) {
this.playHurtSound(source);
this.playSecondaryHurtSound(source);
}
boolean bl2 = success = !blocked || damage > 0.0f;
if (success) {
this.lastDamageSource = source;
this.lastDamageStamp = this.level().getGameTime();
for (MobEffectInstance effect : this.getActiveEffects()) {
effect.onMobHurt(level, this, source, damage);
}
}
if ((entity = this) instanceof ServerPlayer) {
ServerPlayer serverPlayer = (ServerPlayer)entity;
CriteriaTriggers.ENTITY_HURT_PLAYER.trigger(serverPlayer, source, originalDamage, damage, blocked);
if (damageBlocked > 0.0f && damageBlocked < 3.4028235E37f) {
serverPlayer.awardStat(Stats.DAMAGE_BLOCKED_BY_SHIELD, Math.round(damageBlocked * 10.0f));
}
}
if ((entity = source.getEntity()) instanceof ServerPlayer) {
ServerPlayer sourcePlayer = (ServerPlayer)entity;
CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(sourcePlayer, this, source, originalDamage, damage, blocked);
}
return success;
}
public float applyItemBlocking(ServerLevel level, DamageSource source, float damage) {
Entity directEntity;
double angle;
AbstractArrow abstractArrow;
BlocksAttacks blocksAttacks;
ItemStack blockingWith;
block10: {
block9: {
if (damage <= 0.0f) {
return 0.0f;
}
blockingWith = this.getItemBlockingWith();
if (blockingWith == null) {
return 0.0f;
}
blocksAttacks = blockingWith.get(DataComponents.BLOCKS_ATTACKS);
if (blocksAttacks == null) break block9;
if (!blocksAttacks.bypassedBy().map(source::is).orElse(false).booleanValue()) break block10;
}
return 0.0f;
}
Entity entity = source.getDirectEntity();
if (entity instanceof AbstractArrow && (abstractArrow = (AbstractArrow)entity).getPierceLevel() > 0) {
return 0.0f;
}
Vec3 sourcePosition = source.getSourcePosition();
if (sourcePosition != null) {
Vec3 viewVector = this.calculateViewVector(0.0f, this.getYHeadRot());
Vec3 vectorTo = sourcePosition.subtract(this.position());
vectorTo = new Vec3(vectorTo.x, 0.0, vectorTo.z).normalize();
angle = Math.acos(vectorTo.dot(viewVector));
} else {
angle = 3.1415927410125732;
}
float damageBlocked = blocksAttacks.resolveBlockedDamage(source, damage, angle);
blocksAttacks.hurtBlockingItem(this.level(), blockingWith, this, this.getUsedItemHand(), damageBlocked);
if (damageBlocked > 0.0f && !source.is(DamageTypeTags.IS_PROJECTILE) && (directEntity = source.getDirectEntity()) instanceof LivingEntity) {
LivingEntity livingEntity = (LivingEntity)directEntity;
this.blockUsingItem(level, livingEntity);
}
return damageBlocked;
}
private void playSecondaryHurtSound(DamageSource source) {
if (source.is(DamageTypes.THORNS)) {
SoundSource soundSource = this instanceof Player ? SoundSource.PLAYERS : SoundSource.HOSTILE;
this.level().playSound(null, this.position().x, this.position().y, this.position().z, SoundEvents.THORNS_HIT, soundSource);
}
}
protected void resolveMobResponsibleForDamage(DamageSource source) {
Entity entity = source.getEntity();
if (entity instanceof LivingEntity) {
LivingEntity livingSource = (LivingEntity)entity;
if (!(source.is(DamageTypeTags.NO_ANGER) || source.is(DamageTypes.WIND_CHARGE) && this.getType().is(EntityTypeTags.NO_ANGER_FROM_WIND_CHARGE))) {
this.setLastHurtByMob(livingSource);
}
}
}
protected @Nullable Player resolvePlayerResponsibleForDamage(DamageSource source) {
Wolf wolf;
Entity sourceEntity = source.getEntity();
if (sourceEntity instanceof Player) {
Player playerSource = (Player)sourceEntity;
this.setLastHurtByPlayer(playerSource, 100);
} else if (sourceEntity instanceof Wolf && (wolf = (Wolf)sourceEntity).isTame()) {
if (wolf.getOwnerReference() != null) {
this.setLastHurtByPlayer(wolf.getOwnerReference().getUUID(), 100);
} else {
this.lastHurtByPlayer = null;
this.lastHurtByPlayerMemoryTime = 0;
}
}
return EntityReference.getPlayer(this.lastHurtByPlayer, this.level());
}
protected void blockUsingItem(ServerLevel level, LivingEntity attacker) {
attacker.blockedByItem(this);
}
protected void blockedByItem(LivingEntity defender) {
defender.knockback(0.5, defender.getX() - this.getX(), defender.getZ() - this.getZ());
}
private boolean checkTotemDeathProtection(DamageSource killingDamage) {
if (killingDamage.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) {
return false;
}
ItemStack protectionItem = null;
DeathProtection protection = null;
for (InteractionHand hand : InteractionHand.values()) {
ItemStack itemStack = this.getItemInHand(hand);
protection = itemStack.get(DataComponents.DEATH_PROTECTION);
if (protection == null) continue;
protectionItem = itemStack.copy();
itemStack.shrink(1);
break;
}
if (protectionItem != null) {
LivingEntity livingEntity = this;
if (livingEntity instanceof ServerPlayer) {
ServerPlayer player = (ServerPlayer)livingEntity;
player.awardStat(Stats.ITEM_USED.get(protectionItem.getItem()));
CriteriaTriggers.USED_TOTEM.trigger(player, protectionItem);
this.gameEvent(GameEvent.ITEM_INTERACT_FINISH);
}
this.setHealth(1.0f);
protection.applyEffects(protectionItem, this);
this.level().broadcastEntityEvent(this, (byte)35);
}
return protection != null;
}
public @Nullable DamageSource getLastDamageSource() {
if (this.level().getGameTime() - this.lastDamageStamp > 40L) {
this.lastDamageSource = null;
}
return this.lastDamageSource;
}
protected void playHurtSound(DamageSource source) {
this.makeSound(this.getHurtSound(source));
}
public void makeSound(@Nullable SoundEvent sound) {
if (sound != null) {
this.playSound(sound, this.getSoundVolume(), this.getVoicePitch());
}
}
private void breakItem(ItemStack itemStack) {
if (!itemStack.isEmpty()) {
Holder<SoundEvent> breakSound = itemStack.get(DataComponents.BREAK_SOUND);
if (breakSound != null && !this.isSilent()) {
this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), breakSound.value(), this.getSoundSource(), 0.8f, 0.8f + this.level().random.nextFloat() * 0.4f, false);
}
this.spawnItemParticles(itemStack, 5);
}
}
public void die(DamageSource source) {
if (this.isRemoved() || this.dead) {
return;
}
Entity sourceEntity = source.getEntity();
LivingEntity killer = this.getKillCredit();
if (killer != null) {
killer.awardKillScore(this, source);
}
if (this.isSleeping()) {
this.stopSleeping();
}
this.stopUsingItem();
if (!this.level().isClientSide() && this.hasCustomName()) {
LOGGER.info("Named entity {} died: {}", (Object)this, (Object)this.getCombatTracker().getDeathMessage().getString());
}
this.dead = true;
this.getCombatTracker().recheckStatus();
Level level = this.level();
if (level instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level;
if (sourceEntity == null || sourceEntity.killedEntity(serverLevel, this, source)) {
this.gameEvent(GameEvent.ENTITY_DIE);
this.dropAllDeathLoot(serverLevel, source);
this.createWitherRose(killer);
}
this.level().broadcastEntityEvent(this, (byte)3);
}
this.setPose(Pose.DYING);
}
protected void createWitherRose(@Nullable LivingEntity killer) {
Level level = this.level();
if (!(level instanceof ServerLevel)) {
return;
}
ServerLevel serverLevel = (ServerLevel)level;
boolean plantedWitherRose = false;
if (killer instanceof WitherBoss) {
if (serverLevel.getGameRules().get(GameRules.MOB_GRIEFING).booleanValue()) {
BlockPos pos = this.blockPosition();
BlockState state = Blocks.WITHER_ROSE.defaultBlockState();
if (this.level().getBlockState(pos).isAir() && state.canSurvive(this.level(), pos)) {
this.level().setBlock(pos, state, 3);
plantedWitherRose = true;
}
}
if (!plantedWitherRose) {
ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), new ItemStack(Items.WITHER_ROSE));
this.level().addFreshEntity(itemEntity);
}
}
}
protected void dropAllDeathLoot(ServerLevel level, DamageSource source) {
boolean playerKilled;
boolean bl = playerKilled = this.lastHurtByPlayerMemoryTime > 0;
if (this.shouldDropLoot(level)) {
this.dropFromLootTable(level, source, playerKilled);
this.dropCustomDeathLoot(level, source, playerKilled);
}
this.dropEquipment(level);
this.dropExperience(level, source.getEntity());
}
protected void dropEquipment(ServerLevel level) {
}
protected void dropExperience(ServerLevel level, @Nullable Entity killer) {
if (!this.wasExperienceConsumed() && (this.isAlwaysExperienceDropper() || this.lastHurtByPlayerMemoryTime > 0 && this.shouldDropExperience() && level.getGameRules().get(GameRules.MOB_DROPS).booleanValue())) {
ExperienceOrb.award(level, this.position(), this.getExperienceReward(level, killer));
}
}
protected void dropCustomDeathLoot(ServerLevel level, DamageSource source, boolean killedByPlayer) {
}
public long getLootTableSeed() {
return 0L;
}
protected float getKnockback(Entity target, DamageSource damageSource) {
float knockback = (float)this.getAttributeValue(Attributes.ATTACK_KNOCKBACK);
Level level = this.level();
if (level instanceof ServerLevel) {
ServerLevel level2 = (ServerLevel)level;
return EnchantmentHelper.modifyKnockback(level2, this.getWeaponItem(), target, damageSource, knockback) / 2.0f;
}
return knockback / 2.0f;
}
protected void dropFromLootTable(ServerLevel level, DamageSource source, boolean playerKilled) {
Optional<ResourceKey<LootTable>> lootTable = this.getLootTable();
if (lootTable.isEmpty()) {
return;
}
this.dropFromLootTable(level, source, playerKilled, lootTable.get());
}
public void dropFromLootTable(ServerLevel level, DamageSource source, boolean playerKilled, ResourceKey<LootTable> lootTable) {
this.dropFromLootTable(level, source, playerKilled, lootTable, itemStack -> this.spawnAtLocation(level, (ItemStack)itemStack));
}
public void dropFromLootTable(ServerLevel level, DamageSource source, boolean playerKilled, ResourceKey<LootTable> lootTable, Consumer<ItemStack> itemStackConsumer) {
LootTable table = level.getServer().reloadableRegistries().getLootTable(lootTable);
LootParams.Builder builder = new LootParams.Builder(level).withParameter(LootContextParams.THIS_ENTITY, this).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.DAMAGE_SOURCE, source).withOptionalParameter(LootContextParams.ATTACKING_ENTITY, source.getEntity()).withOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY, source.getDirectEntity());
Player killerPlayer = this.getLastHurtByPlayer();
if (playerKilled && killerPlayer != null) {
builder = builder.withParameter(LootContextParams.LAST_DAMAGE_PLAYER, killerPlayer).withLuck(killerPlayer.getLuck());
}
LootParams params = builder.create(LootContextParamSets.ENTITY);
table.getRandomItems(params, this.getLootTableSeed(), itemStackConsumer);
}
public boolean dropFromEntityInteractLootTable(ServerLevel level, ResourceKey<LootTable> key, @Nullable Entity interactingEntity, ItemStack tool, BiConsumer<ServerLevel, ItemStack> consumer) {
return this.dropFromLootTable(level, key, params -> params.withParameter(LootContextParams.TARGET_ENTITY, this).withOptionalParameter(LootContextParams.INTERACTING_ENTITY, interactingEntity).withParameter(LootContextParams.TOOL, tool).create(LootContextParamSets.ENTITY_INTERACT), consumer);
}
public boolean dropFromGiftLootTable(ServerLevel level, ResourceKey<LootTable> key, BiConsumer<ServerLevel, ItemStack> consumer) {
return this.dropFromLootTable(level, key, params -> params.withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.THIS_ENTITY, this).create(LootContextParamSets.GIFT), consumer);
}
protected void dropFromShearingLootTable(ServerLevel level, ResourceKey<LootTable> key, ItemStack tool, BiConsumer<ServerLevel, ItemStack> consumer) {
this.dropFromLootTable(level, key, params -> params.withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.THIS_ENTITY, this).withParameter(LootContextParams.TOOL, tool).create(LootContextParamSets.SHEARING), consumer);
}
protected boolean dropFromLootTable(ServerLevel level, ResourceKey<LootTable> key, Function<LootParams.Builder, LootParams> paramsBuilder, BiConsumer<ServerLevel, ItemStack> consumer) {
LootParams params;
LootTable lootTable = level.getServer().reloadableRegistries().getLootTable(key);
ObjectArrayList<ItemStack> drops = lootTable.getRandomItems(params = paramsBuilder.apply(new LootParams.Builder(level)));
if (!drops.isEmpty()) {
drops.forEach(stack -> consumer.accept(level, (ItemStack)stack));
return true;
}
return false;
}
public void knockback(double power, double xd, double zd) {
if ((power *= 1.0 - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)) <= 0.0) {
return;
}
this.needsSync = true;
Vec3 deltaMovement = this.getDeltaMovement();
while (xd * xd + zd * zd < (double)1.0E-5f) {
xd = (Math.random() - Math.random()) * 0.01;
zd = (Math.random() - Math.random()) * 0.01;
}
Vec3 deltaVector = new Vec3(xd, 0.0, zd).normalize().scale(power);
this.setDeltaMovement(deltaMovement.x / 2.0 - deltaVector.x, this.onGround() ? Math.min(0.4, deltaMovement.y / 2.0 + power) : deltaMovement.y, deltaMovement.z / 2.0 - deltaVector.z);
}
public void indicateDamage(double xd, double zd) {
}
protected @Nullable SoundEvent getHurtSound(DamageSource source) {
return SoundEvents.GENERIC_HURT;
}
protected @Nullable SoundEvent getDeathSound() {
return SoundEvents.GENERIC_DEATH;
}
private SoundEvent getFallDamageSound(int dmg) {
return dmg > 4 ? this.getFallSounds().big() : this.getFallSounds().small();
}
public void skipDropExperience() {
this.skipDropExperience = true;
}
public boolean wasExperienceConsumed() {
return this.skipDropExperience;
}
public float getHurtDir() {
return 0.0f;
}
protected AABB getHitbox() {
AABB aabb = this.getBoundingBox();
Entity vehicle = this.getVehicle();
if (vehicle != null) {
Vec3 pos = vehicle.getPassengerRidingPosition(this);
return aabb.setMinY(Math.max(pos.y, aabb.minY));
}
return aabb;
}
public Map<Enchantment, Set<EnchantmentLocationBasedEffect>> activeLocationDependentEnchantments(EquipmentSlot slot) {
return (Map)this.activeLocationDependentEnchantments.computeIfAbsent(slot, s -> new Reference2ObjectArrayMap());
}
public void lungeForwardMaybe() {
Level level = this.level();
if (level instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level;
EnchantmentHelper.doLungeEffects(serverLevel, this);
}
}
public Fallsounds getFallSounds() {
return new Fallsounds(SoundEvents.GENERIC_SMALL_FALL, SoundEvents.GENERIC_BIG_FALL);
}
public Optional<BlockPos> getLastClimbablePos() {
return this.lastClimbablePos;
}
public boolean onClimbable() {
if (this.isSpectator()) {
return false;
}
BlockPos ladderCheckPos = this.blockPosition();
BlockState state = this.getInBlockState();
if (this.isFallFlying() && state.is(BlockTags.CAN_GLIDE_THROUGH)) {
return false;
}
if (state.is(BlockTags.CLIMBABLE)) {
this.lastClimbablePos = Optional.of(ladderCheckPos);
return true;
}
if (state.getBlock() instanceof TrapDoorBlock && this.trapdoorUsableAsLadder(ladderCheckPos, state)) {
this.lastClimbablePos = Optional.of(ladderCheckPos);
return true;
}
return false;
}
private boolean trapdoorUsableAsLadder(BlockPos pos, BlockState state) {
if (state.getValue(TrapDoorBlock.OPEN).booleanValue()) {
BlockState belowState = this.level().getBlockState(pos.below());
return belowState.is(Blocks.LADDER) && belowState.getValue(LadderBlock.FACING) == state.getValue(TrapDoorBlock.FACING);
}
return false;
}
@Override
public boolean isAlive() {
return !this.isRemoved() && this.getHealth() > 0.0f;
}
public boolean isLookingAtMe(LivingEntity target, double coneSize, boolean adjustForDistance, boolean seeThroughTransparentBlocks, double ... gazeHeights) {
Vec3 look = target.getViewVector(1.0f).normalize();
for (double gazeHeight : gazeHeights) {
Vec3 dir = new Vec3(this.getX() - target.getX(), gazeHeight - target.getEyeY(), this.getZ() - target.getZ());
double dist = dir.length();
dir = dir.normalize();
double dot = look.dot(dir);
double d = adjustForDistance ? dist : 1.0;
if (!(dot > 1.0 - coneSize / d) || !target.hasLineOfSight(this, seeThroughTransparentBlocks ? ClipContext.Block.VISUAL : ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, gazeHeight)) continue;
return true;
}
return false;
}
@Override
public int getMaxFallDistance() {
return this.getComfortableFallDistance(0.0f);
}
protected final int getComfortableFallDistance(float allowedDamage) {
return Mth.floor(allowedDamage + 3.0f);
}
@Override
public boolean causeFallDamage(double fallDistance, float damageModifier, DamageSource damageSource) {
boolean damaged = super.causeFallDamage(fallDistance, damageModifier, damageSource);
int dmg = this.calculateFallDamage(fallDistance, damageModifier);
if (dmg > 0) {
this.playSound(this.getFallDamageSound(dmg), 1.0f, 1.0f);
this.playBlockFallSound();
this.hurt(damageSource, dmg);
return true;
}
return damaged;
}
protected int calculateFallDamage(double fallDistance, float damageModifier) {
if (this.getType().is(EntityTypeTags.FALL_DAMAGE_IMMUNE)) {
return 0;
}
double baseDamage = this.calculateFallPower(fallDistance);
return Mth.floor(baseDamage * (double)damageModifier * this.getAttributeValue(Attributes.FALL_DAMAGE_MULTIPLIER));
}
private double calculateFallPower(double fallDistance) {
return fallDistance + 1.0E-6 - this.getAttributeValue(Attributes.SAFE_FALL_DISTANCE);
}
protected void playBlockFallSound() {
if (this.isSilent()) {
return;
}
int xx = Mth.floor(this.getX());
int yy = Mth.floor(this.getY() - (double)0.2f);
int zz = Mth.floor(this.getZ());
BlockState state = this.level().getBlockState(new BlockPos(xx, yy, zz));
if (!state.isAir()) {
SoundType soundType = state.getSoundType();
this.playSound(soundType.getFallSound(), soundType.getVolume() * 0.5f, soundType.getPitch() * 0.75f);
}
}
@Override
public void animateHurt(float yaw) {
this.hurtTime = this.hurtDuration = 10;
}
public int getArmorValue() {
return Mth.floor(this.getAttributeValue(Attributes.ARMOR));
}
protected void hurtArmor(DamageSource damageSource, float damage) {
}
protected void hurtHelmet(DamageSource damageSource, float damage) {
}
protected void doHurtEquipment(DamageSource damageSource, float damage, EquipmentSlot ... slots) {
if (damage <= 0.0f) {
return;
}
int durabilityDamage = (int)Math.max(1.0f, damage / 4.0f);
for (EquipmentSlot slot : slots) {
ItemStack itemStack = this.getItemBySlot(slot);
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
if (equippable == null || !equippable.damageOnHurt() || !itemStack.isDamageableItem() || !itemStack.canBeHurtBy(damageSource)) continue;
itemStack.hurtAndBreak(durabilityDamage, this, slot);
}
}
protected float getDamageAfterArmorAbsorb(DamageSource damageSource, float damage) {
if (!damageSource.is(DamageTypeTags.BYPASSES_ARMOR)) {
this.hurtArmor(damageSource, damage);
damage = CombatRules.getDamageAfterAbsorb(this, damage, damageSource, this.getArmorValue(), (float)this.getAttributeValue(Attributes.ARMOR_TOUGHNESS));
}
return damage;
}
protected float getDamageAfterMagicAbsorb(DamageSource damageSource, float damage) {
float enchantmentArmor;
int absorbValue;
int absorb;
float v;
float oldDamage;
float damageResisted;
if (damageSource.is(DamageTypeTags.BYPASSES_EFFECTS)) {
return damage;
}
if (this.hasEffect(MobEffects.RESISTANCE) && !damageSource.is(DamageTypeTags.BYPASSES_RESISTANCE) && (damageResisted = (oldDamage = damage) - (damage = Math.max((v = damage * (float)(absorb = 25 - (absorbValue = (this.getEffect(MobEffects.RESISTANCE).getAmplifier() + 1) * 5))) / 25.0f, 0.0f))) > 0.0f && damageResisted < 3.4028235E37f) {
if (this instanceof ServerPlayer) {
((ServerPlayer)this).awardStat(Stats.DAMAGE_RESISTED, Math.round(damageResisted * 10.0f));
} else if (damageSource.getEntity() instanceof ServerPlayer) {
((ServerPlayer)damageSource.getEntity()).awardStat(Stats.DAMAGE_DEALT_RESISTED, Math.round(damageResisted * 10.0f));
}
}
if (damage <= 0.0f) {
return 0.0f;
}
if (damageSource.is(DamageTypeTags.BYPASSES_ENCHANTMENTS)) {
return damage;
}
Level level = this.level();
if (level instanceof ServerLevel) {
ServerLevel serverLevel = (ServerLevel)level;
enchantmentArmor = EnchantmentHelper.getDamageProtection(serverLevel, this, damageSource);
} else {
enchantmentArmor = 0.0f;
}
if (enchantmentArmor > 0.0f) {
damage = CombatRules.getDamageAfterMagicAbsorb(damage, enchantmentArmor);
}
return damage;
}
protected void actuallyHurt(ServerLevel level, DamageSource source, float dmg) {
Entity entity;
if (this.isInvulnerableTo(level, source)) {
return;
}
dmg = this.getDamageAfterArmorAbsorb(source, dmg);
float originalDamage = dmg = this.getDamageAfterMagicAbsorb(source, dmg);
dmg = Math.max(dmg - this.getAbsorptionAmount(), 0.0f);
this.setAbsorptionAmount(this.getAbsorptionAmount() - (originalDamage - dmg));
float absorbedDamage = originalDamage - dmg;
if (absorbedDamage > 0.0f && absorbedDamage < 3.4028235E37f && (entity = source.getEntity()) instanceof ServerPlayer) {
ServerPlayer serverPlayer = (ServerPlayer)entity;
serverPlayer.awardStat(Stats.DAMAGE_DEALT_ABSORBED, Math.round(absorbedDamage * 10.0f));
}
if (dmg == 0.0f) {
return;
}
this.getCombatTracker().recordDamage(source, dmg);
this.setHealth(this.getHealth() - dmg);
this.setAbsorptionAmount(this.getAbsorptionAmount() - dmg);
this.gameEvent(GameEvent.ENTITY_DAMAGE);
}
public CombatTracker getCombatTracker() {
return this.combatTracker;
}
public @Nullable LivingEntity getKillCredit() {
if (this.lastHurtByPlayer != null) {
return this.lastHurtByPlayer.getEntity(this.level(), Player.class);
}
if (this.lastHurtByMob != null) {
return this.lastHurtByMob.getEntity(this.level(), LivingEntity.class);
}
return null;
}
public final float getMaxHealth() {
return (float)this.getAttributeValue(Attributes.MAX_HEALTH);
}
public final float getMaxAbsorption() {
return (float)this.getAttributeValue(Attributes.MAX_ABSORPTION);
}
public final int getArrowCount() {
return this.entityData.get(DATA_ARROW_COUNT_ID);
}
public final void setArrowCount(int count) {
this.entityData.set(DATA_ARROW_COUNT_ID, count);
}
public final int getStingerCount() {
return this.entityData.get(DATA_STINGER_COUNT_ID);
}
public final void setStingerCount(int count) {
this.entityData.set(DATA_STINGER_COUNT_ID, count);
}
private int getCurrentSwingDuration() {
ItemStack handStack = this.getItemInHand(InteractionHand.MAIN_HAND);
int swingDuration = handStack.getSwingAnimation().duration();
if (MobEffectUtil.hasDigSpeed(this)) {
return swingDuration - (1 + MobEffectUtil.getDigSpeedAmplification(this));
}
if (this.hasEffect(MobEffects.MINING_FATIGUE)) {
return swingDuration + (1 + this.getEffect(MobEffects.MINING_FATIGUE).getAmplifier()) * 2;
}
return swingDuration;
}
public void swing(InteractionHand hand) {
this.swing(hand, false);
}
public void swing(InteractionHand hand, boolean sendToSwingingEntity) {
if (!this.swinging || this.swingTime >= this.getCurrentSwingDuration() / 2 || this.swingTime < 0) {
this.swingTime = -1;
this.swinging = true;
this.swingingArm = hand;
if (this.level() instanceof ServerLevel) {
ClientboundAnimatePacket packet = new ClientboundAnimatePacket(this, hand == InteractionHand.MAIN_HAND ? 0 : 3);
ServerChunkCache chunkSource = ((ServerLevel)this.level()).getChunkSource();
if (sendToSwingingEntity) {
chunkSource.sendToTrackingPlayersAndSelf(this, packet);
} else {
chunkSource.sendToTrackingPlayers(this, packet);
}
}
}
}
@Override
public void handleDamageEvent(DamageSource source) {
this.walkAnimation.setSpeed(1.5f);
this.invulnerableTime = 20;
this.hurtTime = this.hurtDuration = 10;
SoundEvent hurtSound = this.getHurtSound(source);
if (hurtSound != null) {
this.playSound(hurtSound, this.getSoundVolume(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2f + 1.0f);
}
this.lastDamageSource = source;
this.lastDamageStamp = this.level().getGameTime();
}
@Override
public void handleEntityEvent(byte id) {
switch (id) {
case 3: {
SoundEvent deathSound = this.getDeathSound();
if (deathSound != null) {
this.playSound(deathSound, this.getSoundVolume(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2f + 1.0f);
}
if (this instanceof Player) break;
this.setHealth(0.0f);
this.die(this.damageSources().generic());
break;
}
case 46: {
int count = 128;
for (int i = 0; i < 128; ++i) {
double d = (double)i / 127.0;
float xa = (this.random.nextFloat() - 0.5f) * 0.2f;
float ya = (this.random.nextFloat() - 0.5f) * 0.2f;
float za = (this.random.nextFloat() - 0.5f) * 0.2f;
double x = Mth.lerp(d, this.xo, this.getX()) + (this.random.nextDouble() - 0.5) * (double)this.getBbWidth() * 2.0;
double y = Mth.lerp(d, this.yo, this.getY()) + this.random.nextDouble() * (double)this.getBbHeight();
double z = Mth.lerp(d, this.zo, this.getZ()) + (this.random.nextDouble() - 0.5) * (double)this.getBbWidth() * 2.0;
this.level().addParticle(ParticleTypes.PORTAL, x, y, z, xa, ya, za);
}
break;
}
case 47: {
this.breakItem(this.getItemBySlot(EquipmentSlot.MAINHAND));
break;
}
case 48: {
this.breakItem(this.getItemBySlot(EquipmentSlot.OFFHAND));
break;
}
case 49: {
this.breakItem(this.getItemBySlot(EquipmentSlot.HEAD));
break;
}
case 50: {
this.breakItem(this.getItemBySlot(EquipmentSlot.CHEST));
break;
}
case 51: {
this.breakItem(this.getItemBySlot(EquipmentSlot.LEGS));
break;
}
case 52: {
this.breakItem(this.getItemBySlot(EquipmentSlot.FEET));
break;
}
case 65: {
this.breakItem(this.getItemBySlot(EquipmentSlot.BODY));
break;
}
case 68: {
this.breakItem(this.getItemBySlot(EquipmentSlot.SADDLE));
break;
}
case 54: {
HoneyBlock.showJumpParticles(this);
break;
}
case 55: {
this.swapHandItems();
break;
}
case 60: {
this.makePoofParticles();
break;
}
case 67: {
this.makeDrownParticles();
break;
}
case 2: {
this.lastEnemyHitTime = this.level().getGameTime();
break;
}
default: {
super.handleEntityEvent(id);
}
}
}
public float getTicksSinceEnemyHit(float partial) {
if (this.lastEnemyHitTime < 0L) {
return 0.0f;
}
return (float)(this.level().getGameTime() - this.lastEnemyHitTime) + partial;
}
public void makePoofParticles() {
for (int i = 0; i < 20; ++i) {
double xa = this.random.nextGaussian() * 0.02;
double ya = this.random.nextGaussian() * 0.02;
double za = this.random.nextGaussian() * 0.02;
double dd = 10.0;
this.level().addParticle(ParticleTypes.POOF, this.getRandomX(1.0) - xa * 10.0, this.getRandomY() - ya * 10.0, this.getRandomZ(1.0) - za * 10.0, xa, ya, za);
}
}
private void makeDrownParticles() {
Vec3 movement = this.getDeltaMovement();
for (int i = 0; i < 8; ++i) {
double offsetX = this.random.triangle(0.0, 1.0);
double offsetY = this.random.triangle(0.0, 1.0);
double offsetZ = this.random.triangle(0.0, 1.0);
this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + offsetX, this.getY() + offsetY, this.getZ() + offsetZ, movement.x, movement.y, movement.z);
}
}
private void swapHandItems() {
ItemStack tmp = this.getItemBySlot(EquipmentSlot.OFFHAND);
this.setItemSlot(EquipmentSlot.OFFHAND, this.getItemBySlot(EquipmentSlot.MAINHAND));
this.setItemSlot(EquipmentSlot.MAINHAND, tmp);
}
@Override
protected void onBelowWorld() {
this.hurt(this.damageSources().fellOutOfWorld(), 4.0f);
}
protected void updateSwingTime() {
int currentSwingDuration = this.getCurrentSwingDuration();
if (this.swinging) {
++this.swingTime;
if (this.swingTime >= currentSwingDuration) {
this.swingTime = 0;
this.swinging = false;
}
} else {
this.swingTime = 0;
}
this.attackAnim = (float)this.swingTime / (float)currentSwingDuration;
}
public @Nullable AttributeInstance getAttribute(Holder<Attribute> attribute) {
return this.getAttributes().getInstance(attribute);
}
public double getAttributeValue(Holder<Attribute> attribute) {
return this.getAttributes().getValue(attribute);
}
public double getAttributeBaseValue(Holder<Attribute> attribute) {
return this.getAttributes().getBaseValue(attribute);
}
public AttributeMap getAttributes() {
return this.attributes;
}
public ItemStack getMainHandItem() {
return this.getItemBySlot(EquipmentSlot.MAINHAND);
}
public ItemStack getOffhandItem() {
return this.getItemBySlot(EquipmentSlot.OFFHAND);
}
public ItemStack getItemHeldByArm(HumanoidArm arm) {
return this.getMainArm() == arm ? this.getMainHandItem() : this.getOffhandItem();
}
@Override
public ItemStack getWeaponItem() {
return this.getMainHandItem();
}
public boolean isHolding(Item item) {
return this.isHolding((ItemStack heldItem) -> heldItem.is(item));
}
public boolean isHolding(Predicate<ItemStack> itemPredicate) {
return itemPredicate.test(this.getMainHandItem()) || itemPredicate.test(this.getOffhandItem());
}
public ItemStack getItemInHand(InteractionHand hand) {
if (hand == InteractionHand.MAIN_HAND) {
return this.getItemBySlot(EquipmentSlot.MAINHAND);
}
if (hand == InteractionHand.OFF_HAND) {
return this.getItemBySlot(EquipmentSlot.OFFHAND);
}
throw new IllegalArgumentException("Invalid hand " + String.valueOf((Object)hand));
}
public void setItemInHand(InteractionHand hand, ItemStack itemStack) {
if (hand == InteractionHand.MAIN_HAND) {
this.setItemSlot(EquipmentSlot.MAINHAND, itemStack);
} else if (hand == InteractionHand.OFF_HAND) {
this.setItemSlot(EquipmentSlot.OFFHAND, itemStack);
} else {
throw new IllegalArgumentException("Invalid hand " + String.valueOf((Object)hand));
}
}
public boolean hasItemInSlot(EquipmentSlot slot) {
return !this.getItemBySlot(slot).isEmpty();
}
public boolean canUseSlot(EquipmentSlot slot) {
return true;
}
public ItemStack getItemBySlot(EquipmentSlot slot) {
return this.equipment.get(slot);
}
public void setItemSlot(EquipmentSlot slot, ItemStack itemStack) {
this.onEquipItem(slot, this.equipment.set(slot, itemStack), itemStack);
}
public float getArmorCoverPercentage() {
int total = 0;
int count = 0;
for (EquipmentSlot slot : EquipmentSlotGroup.ARMOR) {
if (slot.getType() != EquipmentSlot.Type.HUMANOID_ARMOR) continue;
ItemStack itemStack = this.getItemBySlot(slot);
if (!itemStack.isEmpty()) {
++count;
}
++total;
}
return total > 0 ? (float)count / (float)total : 0.0f;
}
@Override
public void setSprinting(boolean isSprinting) {
super.setSprinting(isSprinting);
AttributeInstance speed = this.getAttribute(Attributes.MOVEMENT_SPEED);
speed.removeModifier(SPEED_MODIFIER_SPRINTING.id());
if (isSprinting) {
speed.addTransientModifier(SPEED_MODIFIER_SPRINTING);
}
}
protected float getSoundVolume() {
return 1.0f;
}
public float getVoicePitch() {
if (this.isBaby()) {
return (this.random.nextFloat() - this.random.nextFloat()) * 0.2f + 1.5f;
}
return (this.random.nextFloat() - this.random.nextFloat()) * 0.2f + 1.0f;
}
protected boolean isImmobile() {
return this.isDeadOrDying();
}
@Override
public void push(Entity entity) {
if (!this.isSleeping()) {
super.push(entity);
}
}
private void dismountVehicle(Entity vehicle) {
Vec3 teleportTarget;
if (this.isRemoved()) {
teleportTarget = this.position();
} else if (vehicle.isRemoved() || this.level().getBlockState(vehicle.blockPosition()).is(BlockTags.PORTALS)) {
boolean isSmall;
double maxY = Math.max(this.getY(), vehicle.getY());
teleportTarget = new Vec3(this.getX(), maxY, this.getZ());
boolean bl = isSmall = this.getBbWidth() <= 4.0f && this.getBbHeight() <= 4.0f;
if (isSmall) {
double halfHeight = (double)this.getBbHeight() / 2.0;
Vec3 center = teleportTarget.add(0.0, halfHeight, 0.0);
VoxelShape allowedCenters = Shapes.create(AABB.ofSize(center, this.getBbWidth(), this.getBbHeight(), this.getBbWidth()));
teleportTarget = this.level().findFreePosition(this, allowedCenters, center, this.getBbWidth(), this.getBbHeight(), this.getBbWidth()).map(pos -> pos.add(0.0, -halfHeight, 0.0)).orElse(teleportTarget);
}
} else {
teleportTarget = vehicle.getDismountLocationForPassenger(this);
}
this.dismountTo(teleportTarget.x, teleportTarget.y, teleportTarget.z);
}
@Override
public boolean shouldShowName() {
return this.isCustomNameVisible();
}
protected float getJumpPower() {
return this.getJumpPower(1.0f);
}
protected float getJumpPower(float multiplier) {
return (float)this.getAttributeValue(Attributes.JUMP_STRENGTH) * multiplier * this.getBlockJumpFactor() + this.getJumpBoostPower();
}
public float getJumpBoostPower() {
return this.hasEffect(MobEffects.JUMP_BOOST) ? 0.1f * ((float)this.getEffect(MobEffects.JUMP_BOOST).getAmplifier() + 1.0f) : 0.0f;
}
@VisibleForTesting
public void jumpFromGround() {
float jumpPower = this.getJumpPower();
if (jumpPower <= 1.0E-5f) {
return;
}
Vec3 movement = this.getDeltaMovement();
this.setDeltaMovement(movement.x, Math.max((double)jumpPower, movement.y), movement.z);
if (this.isSprinting()) {
float angle = this.getYRot() * ((float)Math.PI / 180);
this.addDeltaMovement(new Vec3((double)(-Mth.sin(angle)) * 0.2, 0.0, (double)Mth.cos(angle) * 0.2));
}
this.needsSync = true;
}
protected void goDownInWater() {
this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.04f, 0.0));
}
protected void jumpInLiquid(TagKey<Fluid> type) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0, 0.04f, 0.0));
}
protected float getWaterSlowDown() {
return 0.8f;
}
public boolean canStandOnFluid(FluidState fluid) {
return false;
}
@Override
protected double getDefaultGravity() {
return this.getAttributeValue(Attributes.GRAVITY);
}
protected double getEffectiveGravity() {
boolean isFalling;
boolean bl = isFalling = this.getDeltaMovement().y <= 0.0;
if (isFalling && this.hasEffect(MobEffects.SLOW_FALLING)) {
return Math.min(this.getGravity(), 0.01);
}
return this.getGravity();
}
public void travel(Vec3 input) {
if (this.shouldTravelInFluid(this.level().getFluidState(this.blockPosition()))) {
this.travelInFluid(input);
} else if (this.isFallFlying()) {
this.travelFallFlying(input);
} else {
this.travelInAir(input);
}
}
protected boolean shouldTravelInFluid(FluidState fluidState) {
return (this.isInWater() || this.isInLava()) && this.isAffectedByFluids() && !this.canStandOnFluid(fluidState);
}
protected void travelFlying(Vec3 input, float speed) {
this.travelFlying(input, 0.02f, 0.02f, speed);
}
protected void travelFlying(Vec3 input, float waterSpeed, float lavaSpeed, float airSpeed) {
if (this.isInWater()) {
this.moveRelative(waterSpeed, input);
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.8f));
} else if (this.isInLava()) {
this.moveRelative(lavaSpeed, input);
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.5));
} else {
this.moveRelative(airSpeed, input);
this.move(MoverType.SELF, this.getDeltaMovement());
this.setDeltaMovement(this.getDeltaMovement().scale(0.91f));
}
}
private void travelInAir(Vec3 input) {
BlockPos posBelow = this.getBlockPosBelowThatAffectsMyMovement();
float blockFriction = this.onGround() ? this.level().getBlockState(posBelow).getBlock().getFriction() : 1.0f;
float friction = blockFriction * 0.91f;
Vec3 movement = this.handleRelativeFrictionAndCalculateMovement(input, blockFriction);
double movementY = movement.y;
MobEffectInstance levitationEffect = this.getEffect(MobEffects.LEVITATION);
movementY = levitationEffect != null ? (movementY += (0.05 * (double)(levitationEffect.getAmplifier() + 1) - movement.y) * 0.2) : (!this.level().isClientSide() || this.level().hasChunkAt(posBelow) ? (movementY -= this.getEffectiveGravity()) : (this.getY() > (double)this.level().getMinY() ? -0.1 : 0.0));
if (this.shouldDiscardFriction()) {
this.setDeltaMovement(movement.x, movementY, movement.z);
} else {
float verticalFriction = this instanceof FlyingAnimal ? friction : 0.98f;
this.setDeltaMovement(movement.x * (double)friction, movementY * (double)verticalFriction, movement.z * (double)friction);
}
}
private void travelInFluid(Vec3 input) {
boolean isFalling = this.getDeltaMovement().y <= 0.0;
double oldY = this.getY();
double baseGravity = this.getEffectiveGravity();
if (this.isInWater()) {
this.travelInWater(input, baseGravity, isFalling, oldY);
this.floatInWaterWhileRidden();
} else {
this.travelInLava(input, baseGravity, isFalling, oldY);
}
}
protected void travelInWater(Vec3 input, double baseGravity, boolean isFalling, double oldY) {
float slowDown = this.isSprinting() ? 0.9f : this.getWaterSlowDown();
float speed = 0.02f;
float waterWalker = (float)this.getAttributeValue(Attributes.WATER_MOVEMENT_EFFICIENCY);
if (!this.onGround()) {
waterWalker *= 0.5f;
}
if (waterWalker > 0.0f) {
slowDown += (0.54600006f - slowDown) * waterWalker;
speed += (this.getSpeed() - speed) * waterWalker;
}
if (this.hasEffect(MobEffects.DOLPHINS_GRACE)) {
slowDown = 0.96f;
}
this.moveRelative(speed, input);
this.move(MoverType.SELF, this.getDeltaMovement());
Vec3 ladderMovement = this.getDeltaMovement();
if (this.horizontalCollision && this.onClimbable()) {
ladderMovement = new Vec3(ladderMovement.x, 0.2, ladderMovement.z);
}
ladderMovement = ladderMovement.multiply(slowDown, 0.8f, slowDown);
this.setDeltaMovement(this.getFluidFallingAdjustedMovement(baseGravity, isFalling, ladderMovement));
this.jumpOutOfFluid(oldY);
}
private void travelInLava(Vec3 input, double baseGravity, boolean isFalling, double oldY) {
this.moveRelative(0.02f, input);
this.move(MoverType.SELF, this.getDeltaMovement());
if (this.getFluidHeight(FluidTags.LAVA) <= this.getFluidJumpThreshold()) {
this.setDeltaMovement(this.getDeltaMovement().multiply(0.5, 0.8f, 0.5));
Vec3 movement = this.getFluidFallingAdjustedMovement(baseGravity, isFalling, this.getDeltaMovement());
this.setDeltaMovement(movement);
} else {
this.setDeltaMovement(this.getDeltaMovement().scale(0.5));
}
if (baseGravity != 0.0) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0, -baseGravity / 4.0, 0.0));
}
this.jumpOutOfFluid(oldY);
}
private void jumpOutOfFluid(double oldY) {
Vec3 movement = this.getDeltaMovement();
if (this.horizontalCollision && this.isFree(movement.x, movement.y + (double)0.6f - this.getY() + oldY, movement.z)) {
this.setDeltaMovement(movement.x, 0.3f, movement.z);
}
}
private void floatInWaterWhileRidden() {
boolean canEntityFloatInWater = this.getType().is(EntityTypeTags.CAN_FLOAT_WHILE_RIDDEN);
if (canEntityFloatInWater && this.isVehicle() && this.getFluidHeight(FluidTags.WATER) > this.getFluidJumpThreshold()) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0, 0.04f, 0.0));
}
}
private void travelFallFlying(Vec3 input) {
if (this.onClimbable()) {
this.travelInAir(input);
this.stopFallFlying();
return;
}
Vec3 lastMovement = this.getDeltaMovement();
double lastSpeed = lastMovement.horizontalDistance();
this.setDeltaMovement(this.updateFallFlyingMovement(lastMovement));
this.move(MoverType.SELF, this.getDeltaMovement());
if (!this.level().isClientSide()) {
double newSpeed = this.getDeltaMovement().horizontalDistance();
this.handleFallFlyingCollisions(lastSpeed, newSpeed);
}
}
public void stopFallFlying() {
this.setSharedFlag(7, true);
this.setSharedFlag(7, false);
}
private Vec3 updateFallFlyingMovement(Vec3 movement) {
double convert;
Vec3 lookAngle = this.getLookAngle();
float leanAngle = this.getXRot() * ((float)Math.PI / 180);
double lookHorLength = Math.sqrt(lookAngle.x * lookAngle.x + lookAngle.z * lookAngle.z);
double moveHorLength = movement.horizontalDistance();
double gravity = this.getEffectiveGravity();
double liftForce = Mth.square(Math.cos(leanAngle));
movement = movement.add(0.0, gravity * (-1.0 + liftForce * 0.75), 0.0);
if (movement.y < 0.0 && lookHorLength > 0.0) {
convert = movement.y * -0.1 * liftForce;
movement = movement.add(lookAngle.x * convert / lookHorLength, convert, lookAngle.z * convert / lookHorLength);
}
if (leanAngle < 0.0f && lookHorLength > 0.0) {
convert = moveHorLength * (double)(-Mth.sin(leanAngle)) * 0.04;
movement = movement.add(-lookAngle.x * convert / lookHorLength, convert * 3.2, -lookAngle.z * convert / lookHorLength);
}
if (lookHorLength > 0.0) {
movement = movement.add((lookAngle.x / lookHorLength * moveHorLength - movement.x) * 0.1, 0.0, (lookAngle.z / lookHorLength * moveHorLength - movement.z) * 0.1);
}
return movement.multiply(0.99f, 0.98f, 0.99f);
}
private void handleFallFlyingCollisions(double moveHorLength, double newMoveHorLength) {
double diff;
float dmg;
if (this.horizontalCollision && (dmg = (float)((diff = moveHorLength - newMoveHorLength) * 10.0 - 3.0)) > 0.0f) {
this.playSound(this.getFallDamageSound((int)dmg), 1.0f, 1.0f);
this.hurt(this.damageSources().flyIntoWall(), dmg);
}
}
private void travelRidden(Player controller, Vec3 selfInput) {
Vec3 riddenInput = this.getRiddenInput(controller, selfInput);
this.tickRidden(controller, riddenInput);
if (this.canSimulateMovement()) {
this.setSpeed(this.getRiddenSpeed(controller));
this.travel(riddenInput);
} else {
this.setDeltaMovement(Vec3.ZERO);
}
}
protected void tickRidden(Player controller, Vec3 riddenInput) {
}
protected Vec3 getRiddenInput(Player controller, Vec3 selfInput) {
return selfInput;
}
protected float getRiddenSpeed(Player controller) {
return this.getSpeed();
}
public void calculateEntityAnimation(boolean useY) {
float distance = (float)Mth.length(this.getX() - this.xo, useY ? this.getY() - this.yo : 0.0, this.getZ() - this.zo);
if (this.isPassenger() || !this.isAlive()) {
this.walkAnimation.stop();
} else {
this.updateWalkAnimation(distance);
}
}
protected void updateWalkAnimation(float distance) {
float targetSpeed = Math.min(distance * 4.0f, 1.0f);
this.walkAnimation.update(targetSpeed, 0.4f, this.isBaby() ? 3.0f : 1.0f);
}
private Vec3 handleRelativeFrictionAndCalculateMovement(Vec3 input, float friction) {
this.moveRelative(this.getFrictionInfluencedSpeed(friction), input);
this.setDeltaMovement(this.handleOnClimbable(this.getDeltaMovement()));
this.move(MoverType.SELF, this.getDeltaMovement());
Vec3 movement = this.getDeltaMovement();
if ((this.horizontalCollision || this.jumping) && (this.onClimbable() || this.wasInPowderSnow && PowderSnowBlock.canEntityWalkOnPowderSnow(this))) {
movement = new Vec3(movement.x, 0.2, movement.z);
}
return movement;
}
public Vec3 getFluidFallingAdjustedMovement(double baseGravity, boolean isFalling, Vec3 movement) {
if (baseGravity != 0.0 && !this.isSprinting()) {
double yd = isFalling && Math.abs(movement.y - 0.005) >= 0.003 && Math.abs(movement.y - baseGravity / 16.0) < 0.003 ? -0.003 : movement.y - baseGravity / 16.0;
return new Vec3(movement.x, yd, movement.z);
}
return movement;
}
private Vec3 handleOnClimbable(Vec3 delta) {
if (this.onClimbable()) {
this.resetFallDistance();
float max = 0.15f;
double xd = Mth.clamp(delta.x, (double)-0.15f, (double)0.15f);
double zd = Mth.clamp(delta.z, (double)-0.15f, (double)0.15f);
double yd = Math.max(delta.y, (double)-0.15f);
if (yd < 0.0 && !this.getInBlockState().is(Blocks.SCAFFOLDING) && this.isSuppressingSlidingDownLadder() && this instanceof Player) {
yd = 0.0;
}
delta = new Vec3(xd, yd, zd);
}
return delta;
}
private float getFrictionInfluencedSpeed(float blockFriction) {
if (this.onGround()) {
return this.getSpeed() * (0.21600002f / (blockFriction * blockFriction * blockFriction));
}
return this.getFlyingSpeed();
}
protected float getFlyingSpeed() {
return this.getControllingPassenger() instanceof Player ? this.getSpeed() * 0.1f : 0.02f;
}
public float getSpeed() {
return this.speed;
}
public void setSpeed(float speed) {
this.speed = speed;
}
public boolean doHurtTarget(ServerLevel level, Entity target) {
this.setLastHurtMob(target);
return false;
}
public void causeExtraKnockback(Entity target, float knockback, Vec3 oldMovement) {
if (knockback > 0.0f && target instanceof LivingEntity) {
LivingEntity livingTarget = (LivingEntity)target;
livingTarget.knockback(knockback, Mth.sin(this.getYRot() * ((float)Math.PI / 180)), -Mth.cos(this.getYRot() * ((float)Math.PI / 180)));
this.setDeltaMovement(this.getDeltaMovement().multiply(0.6, 1.0, 0.6));
}
}
protected void playAttackSound() {
}
@Override
public void tick() {
super.tick();
this.updatingUsingItem();
this.updateSwimAmount();
if (!this.level().isClientSide()) {
int stingerCount;
int arrowCount = this.getArrowCount();
if (arrowCount > 0) {
if (this.removeArrowTime <= 0) {
this.removeArrowTime = 20 * (30 - arrowCount);
}
--this.removeArrowTime;
if (this.removeArrowTime <= 0) {
this.setArrowCount(arrowCount - 1);
}
}
if ((stingerCount = this.getStingerCount()) > 0) {
if (this.removeStingerTime <= 0) {
this.removeStingerTime = 20 * (30 - stingerCount);
}
--this.removeStingerTime;
if (this.removeStingerTime <= 0) {
this.setStingerCount(stingerCount - 1);
}
}
this.detectEquipmentUpdates();
if (this.tickCount % 20 == 0) {
this.getCombatTracker().recheckStatus();
}
if (!(!this.isSleeping() || this.canInteractWithLevel() && this.checkBedExists())) {
this.stopSleeping();
}
}
if (!this.isRemoved()) {
this.aiStep();
}
double xd = this.getX() - this.xo;
double zd = this.getZ() - this.zo;
float sideDist = (float)(xd * xd + zd * zd);
float yBodyRotT = this.yBodyRot;
if (sideDist > 0.0025000002f) {
float walkDirection = (float)Mth.atan2(zd, xd) * 57.295776f - 90.0f;
float diffBetweenDirectionAndFacing = Mth.abs(Mth.wrapDegrees(this.getYRot()) - walkDirection);
yBodyRotT = 95.0f < diffBetweenDirectionAndFacing && diffBetweenDirectionAndFacing < 265.0f ? walkDirection - 180.0f : walkDirection;
}
if (this.attackAnim > 0.0f) {
yBodyRotT = this.getYRot();
}
ProfilerFiller profiler = Profiler.get();
profiler.push("headTurn");
this.tickHeadTurn(yBodyRotT);
profiler.pop();
profiler.push("rangeChecks");
while (this.getYRot() - this.yRotO < -180.0f) {
this.yRotO -= 360.0f;
}
while (this.getYRot() - this.yRotO >= 180.0f) {
this.yRotO += 360.0f;
}
while (this.yBodyRot - this.yBodyRotO < -180.0f) {
this.yBodyRotO -= 360.0f;
}
while (this.yBodyRot - this.yBodyRotO >= 180.0f) {
this.yBodyRotO += 360.0f;
}
while (this.getXRot() - this.xRotO < -180.0f) {
this.xRotO -= 360.0f;
}
while (this.getXRot() - this.xRotO >= 180.0f) {
this.xRotO += 360.0f;
}
while (this.yHeadRot - this.yHeadRotO < -180.0f) {
this.yHeadRotO -= 360.0f;
}
while (this.yHeadRot - this.yHeadRotO >= 180.0f) {
this.yHeadRotO += 360.0f;
}
profiler.pop();
this.fallFlyTicks = this.isFallFlying() ? ++this.fallFlyTicks : 0;
if (this.isSleeping()) {
this.setXRot(0.0f);
}
this.refreshDirtyAttributes();
this.elytraAnimationState.tick();
}
public boolean wasRecentlyStabbed(Entity target, int allowedTime) {
if (this.recentKineticEnemies == null) {
return false;
}
if (this.recentKineticEnemies.containsKey((Object)target)) {
return this.level().getGameTime() - this.recentKineticEnemies.getLong((Object)target) < (long)allowedTime;
}
return false;
}
public void rememberStabbedEntity(Entity target) {
if (this.recentKineticEnemies != null) {
this.recentKineticEnemies.put((Object)target, this.level().getGameTime());
}
}
public int stabbedEntities() {
if (this.recentKineticEnemies == null) {
return 0;
}
return this.recentKineticEnemies.size();
}
public boolean stabAttack(EquipmentSlot weaponSlot, Entity target, float baseDamage, boolean dealsDamage, boolean dealsKnockback, boolean dismounts) {
Level level = this.level();
if (!(level instanceof ServerLevel)) {
return false;
}
ServerLevel serverLevel = (ServerLevel)level;
ItemStack weaponItem = this.getItemBySlot(weaponSlot);
DamageSource damageSource = weaponItem.getDamageSource(this, () -> this.damageSources().mobAttack(this));
float postEnchantmentDamage = EnchantmentHelper.modifyDamage(serverLevel, weaponItem, target, damageSource, baseDamage);
Vec3 oldMovement = target.getDeltaMovement();
boolean affected = dealsKnockback;
boolean dealtDamage = dealsDamage && target.hurtServer(serverLevel, damageSource, postEnchantmentDamage);
affected |= dealtDamage;
if (dealsKnockback) {
this.causeExtraKnockback(target, 0.4f + this.getKnockback(target, damageSource), oldMovement);
}
if (dismounts && target.isPassenger()) {
affected = true;
target.stopRiding();
}
if (target instanceof LivingEntity) {
LivingEntity livingTarget = (LivingEntity)target;
weaponItem.hurtEnemy(livingTarget, this);
}
if (dealtDamage) {
EnchantmentHelper.doPostAttackEffects(serverLevel, target, damageSource);
}
if (!affected) {
return false;
}
this.setLastHurtMob(target);
this.playAttackSound();
return true;
}
public void onAttack() {
}
private void detectEquipmentUpdates() {
Map<EquipmentSlot, ItemStack> changedItems = this.collectEquipmentChanges();
if (changedItems != null) {
this.handleHandSwap(changedItems);
if (!changedItems.isEmpty()) {
this.handleEquipmentChanges(changedItems);
}
}
}
private @Nullable Map<EquipmentSlot, ItemStack> collectEquipmentChanges() {
ItemStack current;
Map changedItems = null;
for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) {
ItemStack previous = this.lastEquipmentItems.get(equipmentSlot);
if (!this.equipmentHasChanged(previous, current = this.getItemBySlot(equipmentSlot))) continue;
if (changedItems == null) {
changedItems = Maps.newEnumMap(EquipmentSlot.class);
}
changedItems.put(equipmentSlot, current);
AttributeMap attributes = this.getAttributes();
if (previous.isEmpty()) continue;
this.stopLocationBasedEffects(previous, equipmentSlot, attributes);
}
if (changedItems != null) {
for (Map.Entry entry : changedItems.entrySet()) {
EquipmentSlot slot = (EquipmentSlot)entry.getKey();
current = (ItemStack)entry.getValue();
if (current.isEmpty() || current.isBroken()) continue;
current.forEachModifier(slot, (attribute, modifier) -> {
AttributeInstance instance = this.attributes.getInstance((Holder<Attribute>)attribute);
if (instance != null) {
instance.removeModifier(modifier.id());
instance.addTransientModifier((AttributeModifier)modifier);
}
});
Level level = this.level();
if (!(level instanceof ServerLevel)) continue;
ServerLevel serverLevel = (ServerLevel)level;
EnchantmentHelper.runLocationChangedEffects(serverLevel, current, this, slot);
}
}
return changedItems;
}
public boolean equipmentHasChanged(ItemStack previous, ItemStack current) {
return !ItemStack.matches(current, previous);
}
private void handleHandSwap(Map<EquipmentSlot, ItemStack> changedItems) {
ItemStack currentMainHand = changedItems.get(EquipmentSlot.MAINHAND);
ItemStack currentOffHand = changedItems.get(EquipmentSlot.OFFHAND);
if (currentMainHand != null && currentOffHand != null && ItemStack.matches(currentMainHand, this.lastEquipmentItems.get(EquipmentSlot.OFFHAND)) && ItemStack.matches(currentOffHand, this.lastEquipmentItems.get(EquipmentSlot.MAINHAND))) {
((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(this, new ClientboundEntityEventPacket(this, 55));
changedItems.remove(EquipmentSlot.MAINHAND);
changedItems.remove(EquipmentSlot.OFFHAND);
this.lastEquipmentItems.put(EquipmentSlot.MAINHAND, currentMainHand.copy());
this.lastEquipmentItems.put(EquipmentSlot.OFFHAND, currentOffHand.copy());
}
}
private void handleEquipmentChanges(Map<EquipmentSlot, ItemStack> changedItems) {
ArrayList itemsToSend = Lists.newArrayListWithCapacity((int)changedItems.size());
changedItems.forEach((slot, newItem) -> {
ItemStack newItemToStore = newItem.copy();
itemsToSend.add(Pair.of((Object)slot, (Object)newItemToStore));
this.lastEquipmentItems.put((EquipmentSlot)slot, newItemToStore);
});
((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(this, new ClientboundSetEquipmentPacket(this.getId(), itemsToSend));
}
protected void tickHeadTurn(float yBodyRotT) {
float yBodyRotD = Mth.wrapDegrees(yBodyRotT - this.yBodyRot);
this.yBodyRot += yBodyRotD * 0.3f;
float headDiff = Mth.wrapDegrees(this.getYRot() - this.yBodyRot);
float maxHeadRotation = this.getMaxHeadRotationRelativeToBody();
if (Math.abs(headDiff) > maxHeadRotation) {
this.yBodyRot += headDiff - (float)Mth.sign(headDiff) * maxHeadRotation;
}
}
protected float getMaxHeadRotationRelativeToBody() {
return 50.0f;
}
/*
* Unable to fully structure code
*/
public void aiStep() {
if (this.noJumpDelay > 0) {
--this.noJumpDelay;
}
if (this.isInterpolating()) {
this.getInterpolation().interpolate();
} else if (!this.canSimulateMovement()) {
this.setDeltaMovement(this.getDeltaMovement().scale(0.98));
}
if (this.lerpHeadSteps > 0) {
this.lerpHeadRotationStep(this.lerpHeadSteps, this.lerpYHeadRot);
--this.lerpHeadSteps;
}
this.equipment.tick(this);
movement = this.getDeltaMovement();
dx = movement.x;
dy = movement.y;
dz = movement.z;
if (this.getType().equals(EntityType.PLAYER)) {
if (movement.horizontalDistanceSqr() < 9.0E-6) {
dx = 0.0;
dz = 0.0;
}
} else {
if (Math.abs(movement.x) < 0.003) {
dx = 0.0;
}
if (Math.abs(movement.z) < 0.003) {
dz = 0.0;
}
}
if (Math.abs(movement.y) < 0.003) {
dy = 0.0;
}
this.setDeltaMovement(dx, dy, dz);
profiler = Profiler.get();
profiler.push("ai");
this.applyInput();
if (this.isImmobile()) {
this.jumping = false;
this.xxa = 0.0f;
this.zza = 0.0f;
} else if (this.isEffectiveAi() && !this.level().isClientSide()) {
profiler.push("newAi");
this.serverAiStep();
profiler.pop();
}
profiler.pop();
profiler.push("jump");
if (this.jumping && this.isAffectedByFluids()) {
fluidHeight = this.isInLava() != false ? this.getFluidHeight(FluidTags.LAVA) : this.getFluidHeight(FluidTags.WATER);
inWaterAndHasFluidHeight = this.isInWater() != false && fluidHeight > 0.0;
fluidJumpThreshold = this.getFluidJumpThreshold();
if (inWaterAndHasFluidHeight && (!this.onGround() || fluidHeight > fluidJumpThreshold)) {
this.jumpInLiquid(FluidTags.WATER);
} else if (this.isInLava() && (!this.onGround() || fluidHeight > fluidJumpThreshold)) {
this.jumpInLiquid(FluidTags.LAVA);
} else if ((this.onGround() || inWaterAndHasFluidHeight && fluidHeight <= fluidJumpThreshold) && this.noJumpDelay == 0) {
this.jumpFromGround();
this.noJumpDelay = 10;
}
} else {
this.noJumpDelay = 0;
}
profiler.pop();
profiler.push("travel");
if (this.isFallFlying()) {
this.updateFallFlying();
}
beforeTravelBox = this.getBoundingBox();
input = new Vec3(this.xxa, this.yya, this.zza);
if (this.hasEffect(MobEffects.SLOW_FALLING) || this.hasEffect(MobEffects.LEVITATION)) {
this.resetFallDistance();
}
if (!((var12_13 = this.getControllingPassenger()) instanceof Player)) ** GOTO lbl-1000
controller = (Player)var12_13;
if (this.isAlive()) {
this.travelRidden(controller, input);
} else if (this.canSimulateMovement() && this.isEffectiveAi()) {
this.travel(input);
}
if (!this.level().isClientSide() || this.isLocalInstanceAuthoritative()) {
this.applyEffectsFromBlocks();
}
if (this.level().isClientSide()) {
this.calculateEntityAnimation(this instanceof FlyingAnimal);
}
profiler.pop();
var12_13 = this.level();
if (var12_13 instanceof ServerLevel) {
serverLevel = (ServerLevel)var12_13;
profiler.push("freezing");
if (!this.isInPowderSnow || !this.canFreeze()) {
this.setTicksFrozen(Math.max(0, this.getTicksFrozen() - 2));
}
this.removeFrost();
this.tryAddFrost();
if (this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
this.hurtServer(serverLevel, this.damageSources().freeze(), 1.0f);
}
profiler.pop();
}
profiler.push("push");
if (this.autoSpinAttackTicks > 0) {
--this.autoSpinAttackTicks;
this.checkAutoSpinAttack(beforeTravelBox, this.getBoundingBox());
}
this.pushEntities();
profiler.pop();
var12_13 = this.level();
if (var12_13 instanceof ServerLevel) {
serverLevel = (ServerLevel)var12_13;
if (this.isSensitiveToWater() && this.isInWaterOrRain()) {
this.hurtServer(serverLevel, this.damageSources().drown(), 1.0f);
}
}
}
protected void applyInput() {
this.xxa *= 0.98f;
this.zza *= 0.98f;
}
public boolean isSensitiveToWater() {
return false;
}
public boolean isJumping() {
return this.jumping;
}
protected void updateFallFlying() {
this.checkFallDistanceAccumulation();
if (!this.level().isClientSide()) {
if (!this.canGlide()) {
this.setSharedFlag(7, false);
return;
}
int checkFallFlyTicks = this.fallFlyTicks + 1;
if (checkFallFlyTicks % 10 == 0) {
int freeFallInterval = checkFallFlyTicks / 10;
if (freeFallInterval % 2 == 0) {
List<EquipmentSlot> slotsWithGliders = EquipmentSlot.VALUES.stream().filter(slot -> LivingEntity.canGlideUsing(this.getItemBySlot((EquipmentSlot)slot), slot)).toList();
EquipmentSlot slotToDamage = Util.getRandom(slotsWithGliders, this.random);
this.getItemBySlot(slotToDamage).hurtAndBreak(1, this, slotToDamage);
}
this.gameEvent(GameEvent.ELYTRA_GLIDE);
}
}
}
protected boolean canGlide() {
if (this.onGround() || this.isPassenger() || this.hasEffect(MobEffects.LEVITATION)) {
return false;
}
for (EquipmentSlot slot : EquipmentSlot.VALUES) {
if (!LivingEntity.canGlideUsing(this.getItemBySlot(slot), slot)) continue;
return true;
}
return false;
}
protected void serverAiStep() {
}
protected void pushEntities() {
ServerLevel serverLevel;
int maxCramming;
List<Entity> pushableEntities = this.level().getPushableEntities(this, this.getBoundingBox());
if (pushableEntities.isEmpty()) {
return;
}
Level level = this.level();
if (level instanceof ServerLevel && (maxCramming = (serverLevel = (ServerLevel)level).getGameRules().get(GameRules.MAX_ENTITY_CRAMMING).intValue()) > 0 && pushableEntities.size() > maxCramming - 1 && this.random.nextInt(4) == 0) {
int count = 0;
for (Entity entity : pushableEntities) {
if (entity.isPassenger()) continue;
++count;
}
if (count > maxCramming - 1) {
this.hurtServer(serverLevel, this.damageSources().cramming(), 6.0f);
}
}
for (Entity entity : pushableEntities) {
this.doPush(entity);
}
}
protected void checkAutoSpinAttack(AABB old, AABB current) {
AABB minmax = old.minmax(current);
List<Entity> entities = this.level().getEntities(this, minmax);
if (!entities.isEmpty()) {
for (Entity entity : entities) {
if (!(entity instanceof LivingEntity)) continue;
this.doAutoAttackOnTouch((LivingEntity)entity);
this.autoSpinAttackTicks = 0;
this.setDeltaMovement(this.getDeltaMovement().scale(-0.2));
break;
}
} else if (this.horizontalCollision) {
this.autoSpinAttackTicks = 0;
}
if (!this.level().isClientSide() && this.autoSpinAttackTicks <= 0) {
this.setLivingEntityFlag(4, false);
this.autoSpinAttackDmg = 0.0f;
this.autoSpinAttackItemStack = null;
}
}
protected void doPush(Entity entity) {
entity.push(this);
}
protected void doAutoAttackOnTouch(LivingEntity entity) {
}
public boolean isAutoSpinAttack() {
return (this.entityData.get(DATA_LIVING_ENTITY_FLAGS) & 4) != 0;
}
@Override
public void stopRiding() {
Entity oldVehicle = this.getVehicle();
super.stopRiding();
if (oldVehicle != null && oldVehicle != this.getVehicle() && !this.level().isClientSide()) {
this.dismountVehicle(oldVehicle);
}
}
@Override
public void rideTick() {
super.rideTick();
this.resetFallDistance();
}
@Override
public InterpolationHandler getInterpolation() {
return this.interpolation;
}
@Override
public void lerpHeadTo(float yRot, int steps) {
this.lerpYHeadRot = yRot;
this.lerpHeadSteps = steps;
}
public void setJumping(boolean jump) {
this.jumping = jump;
}
public void onItemPickup(ItemEntity entity) {
Entity thrower = entity.getOwner();
if (thrower instanceof ServerPlayer) {
CriteriaTriggers.THROWN_ITEM_PICKED_UP_BY_ENTITY.trigger((ServerPlayer)thrower, entity.getItem(), this);
}
}
public void take(Entity entity, int orgCount) {
if (!entity.isRemoved() && !this.level().isClientSide() && (entity instanceof ItemEntity || entity instanceof AbstractArrow || entity instanceof ExperienceOrb)) {
((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(entity, new ClientboundTakeItemEntityPacket(entity.getId(), this.getId(), orgCount));
}
}
public boolean hasLineOfSight(Entity target) {
return this.hasLineOfSight(target, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, target.getEyeY());
}
public boolean hasLineOfSight(Entity target, ClipContext.Block blockCollidingContext, ClipContext.Fluid fluidCollidingContext, double eyeHeight) {
if (target.level() != this.level()) {
return false;
}
Vec3 from = new Vec3(this.getX(), this.getEyeY(), this.getZ());
Vec3 to = new Vec3(target.getX(), eyeHeight, target.getZ());
if (to.distanceTo(from) > 128.0) {
return false;
}
return this.level().clip(new ClipContext(from, to, blockCollidingContext, fluidCollidingContext, this)).getType() == HitResult.Type.MISS;
}
@Override
public float getViewYRot(float a) {
if (a == 1.0f) {
return this.yHeadRot;
}
return Mth.rotLerp(a, this.yHeadRotO, this.yHeadRot);
}
public float getAttackAnim(float a) {
float diff = this.attackAnim - this.oAttackAnim;
if (diff < 0.0f) {
diff += 1.0f;
}
return this.oAttackAnim + diff * a;
}
@Override
public boolean isPickable() {
return !this.isRemoved();
}
@Override
public boolean isPushable() {
return this.isAlive() && !this.isSpectator() && !this.onClimbable();
}
@Override
public float getYHeadRot() {
return this.yHeadRot;
}
@Override
public void setYHeadRot(float yHeadRot) {
this.yHeadRot = yHeadRot;
}
@Override
public void setYBodyRot(float yBodyRot) {
this.yBodyRot = yBodyRot;
}
@Override
public Vec3 getRelativePortalPosition(Direction.Axis axis, BlockUtil.FoundRectangle portalArea) {
return LivingEntity.resetForwardDirectionOfRelativePortalPosition(super.getRelativePortalPosition(axis, portalArea));
}
public static Vec3 resetForwardDirectionOfRelativePortalPosition(Vec3 offsets) {
return new Vec3(offsets.x, offsets.y, 0.0);
}
public float getAbsorptionAmount() {
return this.absorptionAmount;
}
public final void setAbsorptionAmount(float absorptionAmount) {
this.internalSetAbsorptionAmount(Mth.clamp(absorptionAmount, 0.0f, this.getMaxAbsorption()));
}
protected void internalSetAbsorptionAmount(float absorptionAmount) {
this.absorptionAmount = absorptionAmount;
}
public void onEnterCombat() {
}
public void onLeaveCombat() {
}
protected void updateEffectVisibility() {
this.effectsDirty = true;
}
public abstract HumanoidArm getMainArm();
public boolean isUsingItem() {
return (this.entityData.get(DATA_LIVING_ENTITY_FLAGS) & 1) > 0;
}
public InteractionHand getUsedItemHand() {
return (this.entityData.get(DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
}
private void updatingUsingItem() {
if (this.isUsingItem()) {
if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
this.useItem = this.getItemInHand(this.getUsedItemHand());
this.updateUsingItem(this.useItem);
} else {
this.stopUsingItem();
}
}
}
private @Nullable ItemEntity createItemStackToDrop(ItemStack itemStack, boolean randomly, boolean thrownFromHand) {
if (itemStack.isEmpty()) {
return null;
}
double yHandPos = this.getEyeY() - (double)0.3f;
ItemEntity entity = new ItemEntity(this.level(), this.getX(), yHandPos, this.getZ(), itemStack);
entity.setPickUpDelay(40);
if (thrownFromHand) {
entity.setThrower(this);
}
if (randomly) {
float pow = this.random.nextFloat() * 0.5f;
float dir = this.random.nextFloat() * ((float)Math.PI * 2);
entity.setDeltaMovement(-Mth.sin(dir) * pow, 0.2f, Mth.cos(dir) * pow);
} else {
float pow = 0.3f;
float sinX = Mth.sin(this.getXRot() * ((float)Math.PI / 180));
float cosX = Mth.cos(this.getXRot() * ((float)Math.PI / 180));
float sinY = Mth.sin(this.getYRot() * ((float)Math.PI / 180));
float cosY = Mth.cos(this.getYRot() * ((float)Math.PI / 180));
float dir = this.random.nextFloat() * ((float)Math.PI * 2);
float pow2 = 0.02f * this.random.nextFloat();
entity.setDeltaMovement((double)(-sinY * cosX * 0.3f) + Math.cos(dir) * (double)pow2, -sinX * 0.3f + 0.1f + (this.random.nextFloat() - this.random.nextFloat()) * 0.1f, (double)(cosY * cosX * 0.3f) + Math.sin(dir) * (double)pow2);
}
return entity;
}
protected void updateUsingItem(ItemStack useItem) {
useItem.onUseTick(this.level(), this, this.getUseItemRemainingTicks());
if (--this.useItemRemaining == 0 && !this.level().isClientSide() && !useItem.useOnRelease()) {
this.completeUsingItem();
}
}
private void updateSwimAmount() {
this.swimAmountO = this.swimAmount;
this.swimAmount = this.isVisuallySwimming() ? Math.min(1.0f, this.swimAmount + 0.09f) : Math.max(0.0f, this.swimAmount - 0.09f);
}
protected void setLivingEntityFlag(int flag, boolean value) {
int currentFlags = this.entityData.get(DATA_LIVING_ENTITY_FLAGS).byteValue();
currentFlags = value ? (currentFlags |= flag) : (currentFlags &= ~flag);
this.entityData.set(DATA_LIVING_ENTITY_FLAGS, (byte)currentFlags);
}
public void startUsingItem(InteractionHand hand) {
ItemStack itemStack = this.getItemInHand(hand);
if (itemStack.isEmpty() || this.isUsingItem()) {
return;
}
this.useItem = itemStack;
this.useItemRemaining = itemStack.getUseDuration(this);
if (!this.level().isClientSide()) {
this.setLivingEntityFlag(1, true);
this.setLivingEntityFlag(2, hand == InteractionHand.OFF_HAND);
this.gameEvent(GameEvent.ITEM_INTERACT_START);
if (this.useItem.has(DataComponents.KINETIC_WEAPON)) {
this.recentKineticEnemies = new Object2LongOpenHashMap();
}
}
}
@Override
public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
super.onSyncedDataUpdated(accessor);
if (SLEEPING_POS_ID.equals(accessor)) {
if (this.level().isClientSide()) {
this.getSleepingPos().ifPresent(this::setPosToBed);
}
} else if (DATA_LIVING_ENTITY_FLAGS.equals(accessor) && this.level().isClientSide()) {
if (this.isUsingItem() && this.useItem.isEmpty()) {
this.useItem = this.getItemInHand(this.getUsedItemHand());
if (!this.useItem.isEmpty()) {
this.useItemRemaining = this.useItem.getUseDuration(this);
}
} else if (!this.isUsingItem() && !this.useItem.isEmpty()) {
this.useItem = ItemStack.EMPTY;
this.useItemRemaining = 0;
}
}
}
@Override
public void lookAt(EntityAnchorArgument.Anchor anchor, Vec3 pos) {
super.lookAt(anchor, pos);
this.yHeadRotO = this.yHeadRot;
this.yBodyRotO = this.yBodyRot = this.yHeadRot;
}
@Override
public float getPreciseBodyRotation(float partial) {
return Mth.lerp(partial, this.yBodyRotO, this.yBodyRot);
}
public void spawnItemParticles(ItemStack itemStack, int count) {
for (int i = 0; i < count; ++i) {
Vec3 d = new Vec3(((double)this.random.nextFloat() - 0.5) * 0.1, Math.random() * 0.1 + 0.1, 0.0);
d = d.xRot(-this.getXRot() * ((float)Math.PI / 180));
d = d.yRot(-this.getYRot() * ((float)Math.PI / 180));
double y1 = (double)(-this.random.nextFloat()) * 0.6 - 0.3;
Vec3 p = new Vec3(((double)this.random.nextFloat() - 0.5) * 0.3, y1, 0.6);
p = p.xRot(-this.getXRot() * ((float)Math.PI / 180));
p = p.yRot(-this.getYRot() * ((float)Math.PI / 180));
p = p.add(this.getX(), this.getEyeY(), this.getZ());
this.level().addParticle(new ItemParticleOption(ParticleTypes.ITEM, itemStack), p.x, p.y, p.z, d.x, d.y + 0.05, d.z);
}
}
protected void completeUsingItem() {
if (this.level().isClientSide() && !this.isUsingItem()) {
return;
}
InteractionHand hand = this.getUsedItemHand();
if (!this.useItem.equals(this.getItemInHand(hand))) {
this.releaseUsingItem();
return;
}
if (!this.useItem.isEmpty() && this.isUsingItem()) {
ItemStack result = this.useItem.finishUsingItem(this.level(), this);
if (result != this.useItem) {
this.setItemInHand(hand, result);
}
this.stopUsingItem();
}
}
public void handleExtraItemsCreatedOnUse(ItemStack extraCreatedRemainder) {
}
public ItemStack getUseItem() {
return this.useItem;
}
public int getUseItemRemainingTicks() {
return this.useItemRemaining;
}
public int getTicksUsingItem() {
if (this.isUsingItem()) {
return this.useItem.getUseDuration(this) - this.getUseItemRemainingTicks();
}
return 0;
}
public float getTicksUsingItem(float partialTicks) {
if (!this.isUsingItem()) {
return 0.0f;
}
return (float)this.getTicksUsingItem() + partialTicks;
}
public void releaseUsingItem() {
ItemStack itemInUsedHand = this.getItemInHand(this.getUsedItemHand());
if (!this.useItem.isEmpty() && ItemStack.isSameItem(itemInUsedHand, this.useItem)) {
this.useItem = itemInUsedHand;
this.useItem.releaseUsing(this.level(), this, this.getUseItemRemainingTicks());
if (this.useItem.useOnRelease()) {
this.updatingUsingItem();
}
}
this.stopUsingItem();
}
public void stopUsingItem() {
if (!this.level().isClientSide()) {
boolean wasUsingItem = this.isUsingItem();
this.recentKineticEnemies = null;
this.setLivingEntityFlag(1, false);
if (wasUsingItem) {
this.gameEvent(GameEvent.ITEM_INTERACT_FINISH);
}
}
this.useItem = ItemStack.EMPTY;
this.useItemRemaining = 0;
}
public boolean isBlocking() {
return this.getItemBlockingWith() != null;
}
public @Nullable ItemStack getItemBlockingWith() {
int elapsedTicks;
if (!this.isUsingItem()) {
return null;
}
BlocksAttacks blocksAttacks = this.useItem.get(DataComponents.BLOCKS_ATTACKS);
if (blocksAttacks != null && (elapsedTicks = this.useItem.getItem().getUseDuration(this.useItem, this) - this.useItemRemaining) >= blocksAttacks.blockDelayTicks()) {
return this.useItem;
}
return null;
}
public boolean isSuppressingSlidingDownLadder() {
return this.isShiftKeyDown();
}
public boolean isFallFlying() {
return this.getSharedFlag(7);
}
@Override
public boolean isVisuallySwimming() {
return super.isVisuallySwimming() || !this.isFallFlying() && this.hasPose(Pose.FALL_FLYING);
}
public int getFallFlyingTicks() {
return this.fallFlyTicks;
}
public boolean randomTeleport(double xx, double yy, double zz, boolean showParticles) {
LivingEntity livingEntity;
double xo = this.getX();
double yo = this.getY();
double zo = this.getZ();
double y = yy;
boolean ok = false;
BlockPos pos = BlockPos.containing(xx, y, zz);
Level level = this.level();
if (level.hasChunkAt(pos)) {
boolean landed = false;
while (!landed && pos.getY() > level.getMinY()) {
BlockPos below = pos.below();
BlockState state = level.getBlockState(below);
if (state.blocksMotion()) {
landed = true;
continue;
}
y -= 1.0;
pos = below;
}
if (landed) {
this.teleportTo(xx, y, zz);
if (level.noCollision(this) && !level.containsAnyLiquid(this.getBoundingBox())) {
ok = true;
}
}
}
if (!ok) {
this.teleportTo(xo, yo, zo);
return false;
}
if (showParticles) {
level.broadcastEntityEvent(this, (byte)46);
}
if ((livingEntity = this) instanceof PathfinderMob) {
PathfinderMob pathfinderMob = (PathfinderMob)livingEntity;
pathfinderMob.getNavigation().stop();
}
return true;
}
public boolean isAffectedByPotions() {
return !this.isDeadOrDying();
}
public boolean attackable() {
return true;
}
public void setRecordPlayingNearby(BlockPos jukebox, boolean isPlaying) {
}
public boolean canPickUpLoot() {
return false;
}
@Override
public final EntityDimensions getDimensions(Pose pose) {
return pose == Pose.SLEEPING ? SLEEPING_DIMENSIONS : this.getDefaultDimensions(pose).scale(this.getScale());
}
protected EntityDimensions getDefaultDimensions(Pose pose) {
return this.getType().getDimensions().scale(this.getAgeScale());
}
public ImmutableList<Pose> getDismountPoses() {
return ImmutableList.of((Object)Pose.STANDING);
}
public AABB getLocalBoundsForPose(Pose pose) {
EntityDimensions dimensions = this.getDimensions(pose);
return new AABB(-dimensions.width() / 2.0f, 0.0, -dimensions.width() / 2.0f, dimensions.width() / 2.0f, dimensions.height(), dimensions.width() / 2.0f);
}
protected boolean wouldNotSuffocateAtTargetPose(Pose pose) {
AABB targetBB = this.getDimensions(pose).makeBoundingBox(this.position());
return this.level().noBlockCollision(this, targetBB);
}
@Override
public boolean canUsePortal(boolean ignorePassenger) {
return super.canUsePortal(ignorePassenger) && !this.isSleeping();
}
public Optional<BlockPos> getSleepingPos() {
return this.entityData.get(SLEEPING_POS_ID);
}
public void setSleepingPos(BlockPos bedPosition) {
this.entityData.set(SLEEPING_POS_ID, Optional.of(bedPosition));
}
public void clearSleepingPos() {
this.entityData.set(SLEEPING_POS_ID, Optional.empty());
}
public boolean isSleeping() {
return this.getSleepingPos().isPresent();
}
public void startSleeping(BlockPos bedPosition) {
BlockState blockState;
if (this.isPassenger()) {
this.stopRiding();
}
if ((blockState = this.level().getBlockState(bedPosition)).getBlock() instanceof BedBlock) {
this.level().setBlock(bedPosition, (BlockState)blockState.setValue(BedBlock.OCCUPIED, true), 3);
}
this.setPose(Pose.SLEEPING);
this.setPosToBed(bedPosition);
this.setSleepingPos(bedPosition);
this.setDeltaMovement(Vec3.ZERO);
this.needsSync = true;
}
private void setPosToBed(BlockPos bedPosition) {
this.setPos((double)bedPosition.getX() + 0.5, (double)bedPosition.getY() + 0.6875, (double)bedPosition.getZ() + 0.5);
}
private boolean checkBedExists() {
return this.getSleepingPos().map(bedPosition -> this.level().getBlockState((BlockPos)bedPosition).getBlock() instanceof BedBlock).orElse(false);
}
public void stopSleeping() {
this.getSleepingPos().filter(this.level()::hasChunkAt).ifPresent(bedPosition -> {
BlockState state = this.level().getBlockState((BlockPos)bedPosition);
if (state.getBlock() instanceof BedBlock) {
Direction facing = (Direction)state.getValue(BedBlock.FACING);
this.level().setBlock((BlockPos)bedPosition, (BlockState)state.setValue(BedBlock.OCCUPIED, false), 3);
Vec3 standUp = BedBlock.findStandUpPosition(this.getType(), this.level(), bedPosition, facing, this.getYRot()).orElseGet(() -> {
BlockPos above = bedPosition.above();
return new Vec3((double)above.getX() + 0.5, (double)above.getY() + 0.1, (double)above.getZ() + 0.5);
});
Vec3 lookDirection = Vec3.atBottomCenterOf(bedPosition).subtract(standUp).normalize();
float yaw = (float)Mth.wrapDegrees(Mth.atan2(lookDirection.z, lookDirection.x) * 57.2957763671875 - 90.0);
this.setPos(standUp.x, standUp.y, standUp.z);
this.setYRot(yaw);
this.setXRot(0.0f);
}
});
Vec3 pos = this.position();
this.setPose(Pose.STANDING);
this.setPos(pos.x, pos.y, pos.z);
this.clearSleepingPos();
}
public @Nullable Direction getBedOrientation() {
BlockPos bedPos = this.getSleepingPos().orElse(null);
return bedPos != null ? BedBlock.getBedOrientation(this.level(), bedPos) : null;
}
@Override
public boolean isInWall() {
return !this.isSleeping() && super.isInWall();
}
public ItemStack getProjectile(ItemStack heldWeapon) {
return ItemStack.EMPTY;
}
private static byte entityEventForEquipmentBreak(EquipmentSlot equipmentSlot) {
return switch (equipmentSlot) {
default -> throw new MatchException(null, null);
case EquipmentSlot.MAINHAND -> 47;
case EquipmentSlot.OFFHAND -> 48;
case EquipmentSlot.HEAD -> 49;
case EquipmentSlot.CHEST -> 50;
case EquipmentSlot.FEET -> 52;
case EquipmentSlot.LEGS -> 51;
case EquipmentSlot.BODY -> 65;
case EquipmentSlot.SADDLE -> 68;
};
}
public void onEquippedItemBroken(Item brokenItem, EquipmentSlot inSlot) {
this.level().broadcastEntityEvent(this, LivingEntity.entityEventForEquipmentBreak(inSlot));
this.stopLocationBasedEffects(this.getItemBySlot(inSlot), inSlot, this.attributes);
}
private void stopLocationBasedEffects(ItemStack previous, EquipmentSlot inSlot, AttributeMap attributes) {
previous.forEachModifier(inSlot, (attribute, modifier) -> {
AttributeInstance instance = attributes.getInstance((Holder<Attribute>)attribute);
if (instance != null) {
instance.removeModifier((AttributeModifier)modifier);
}
});
EnchantmentHelper.stopLocationBasedEffects(previous, this, inSlot);
}
public final boolean canEquipWithDispenser(ItemStack itemStack) {
if (!this.isAlive() || this.isSpectator()) {
return false;
}
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
if (equippable == null || !equippable.dispensable()) {
return false;
}
EquipmentSlot slot = equippable.slot();
if (!this.canUseSlot(slot) || !equippable.canBeEquippedBy(this.getType())) {
return false;
}
return this.getItemBySlot(slot).isEmpty() && this.canDispenserEquipIntoSlot(slot);
}
protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) {
return true;
}
public final EquipmentSlot getEquipmentSlotForItem(ItemStack itemStack) {
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
if (equippable != null && this.canUseSlot(equippable.slot())) {
return equippable.slot();
}
return EquipmentSlot.MAINHAND;
}
public final boolean isEquippableInSlot(ItemStack itemStack, EquipmentSlot slot) {
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
if (equippable == null) {
return slot == EquipmentSlot.MAINHAND && this.canUseSlot(EquipmentSlot.MAINHAND);
}
return slot == equippable.slot() && this.canUseSlot(equippable.slot()) && equippable.canBeEquippedBy(this.getType());
}
private static SlotAccess createEquipmentSlotAccess(LivingEntity entity, EquipmentSlot equipmentSlot) {
if (equipmentSlot == EquipmentSlot.HEAD || equipmentSlot == EquipmentSlot.MAINHAND || equipmentSlot == EquipmentSlot.OFFHAND) {
return SlotAccess.forEquipmentSlot(entity, equipmentSlot);
}
return SlotAccess.forEquipmentSlot(entity, equipmentSlot, stack -> stack.isEmpty() || entity.getEquipmentSlotForItem((ItemStack)stack) == equipmentSlot);
}
private static @Nullable EquipmentSlot getEquipmentSlot(int slot) {
if (slot == 100 + EquipmentSlot.HEAD.getIndex()) {
return EquipmentSlot.HEAD;
}
if (slot == 100 + EquipmentSlot.CHEST.getIndex()) {
return EquipmentSlot.CHEST;
}
if (slot == 100 + EquipmentSlot.LEGS.getIndex()) {
return EquipmentSlot.LEGS;
}
if (slot == 100 + EquipmentSlot.FEET.getIndex()) {
return EquipmentSlot.FEET;
}
if (slot == 98) {
return EquipmentSlot.MAINHAND;
}
if (slot == 99) {
return EquipmentSlot.OFFHAND;
}
if (slot == 105) {
return EquipmentSlot.BODY;
}
if (slot == 106) {
return EquipmentSlot.SADDLE;
}
return null;
}
@Override
public @Nullable SlotAccess getSlot(int slot) {
EquipmentSlot equipmentSlot = LivingEntity.getEquipmentSlot(slot);
if (equipmentSlot != null) {
return LivingEntity.createEquipmentSlotAccess(this, equipmentSlot);
}
return super.getSlot(slot);
}
@Override
public boolean canFreeze() {
if (this.isSpectator()) {
return false;
}
for (EquipmentSlot slot : EquipmentSlotGroup.ARMOR) {
if (!this.getItemBySlot(slot).is(ItemTags.FREEZE_IMMUNE_WEARABLES)) continue;
return false;
}
return super.canFreeze();
}
@Override
public boolean isCurrentlyGlowing() {
return !this.level().isClientSide() && this.hasEffect(MobEffects.GLOWING) || super.isCurrentlyGlowing();
}
@Override
public float getVisualRotationYInDegrees() {
return this.yBodyRot;
}
@Override
public void recreateFromPacket(ClientboundAddEntityPacket packet) {
double x = packet.getX();
double y = packet.getY();
double z = packet.getZ();
float yRot = packet.getYRot();
float xRot = packet.getXRot();
this.syncPacketPositionCodec(x, y, z);
this.yBodyRot = packet.getYHeadRot();
this.yHeadRot = packet.getYHeadRot();
this.yBodyRotO = this.yBodyRot;
this.yHeadRotO = this.yHeadRot;
this.setId(packet.getId());
this.setUUID(packet.getUUID());
this.absSnapTo(x, y, z, yRot, xRot);
this.setDeltaMovement(packet.getMovement());
}
public float getSecondsToDisableBlocking() {
Weapon weapon = this.getWeaponItem().get(DataComponents.WEAPON);
return weapon != null ? weapon.disableBlockingForSeconds() : 0.0f;
}
@Override
public float maxUpStep() {
float maxUpStep = (float)this.getAttributeValue(Attributes.STEP_HEIGHT);
return this.getControllingPassenger() instanceof Player ? Math.max(maxUpStep, 1.0f) : maxUpStep;
}
@Override
public Vec3 getPassengerRidingPosition(Entity passenger) {
return this.position().add(this.getPassengerAttachmentPoint(passenger, this.getDimensions(this.getPose()), this.getScale() * this.getAgeScale()));
}
protected void lerpHeadRotationStep(int lerpHeadSteps, double targetYHeadRot) {
this.yHeadRot = (float)Mth.rotLerp(1.0 / (double)lerpHeadSteps, (double)this.yHeadRot, targetYHeadRot);
}
@Override
public void igniteForTicks(int numberOfTicks) {
super.igniteForTicks(Mth.ceil((double)numberOfTicks * this.getAttributeValue(Attributes.BURNING_TIME)));
}
public boolean hasInfiniteMaterials() {
return false;
}
public boolean isInvulnerableTo(ServerLevel level, DamageSource source) {
return this.isInvulnerableToBase(source) || EnchantmentHelper.isImmuneToDamage(level, this, source);
}
public static boolean canGlideUsing(ItemStack itemStack, EquipmentSlot slot) {
if (!itemStack.has(DataComponents.GLIDER)) {
return false;
}
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
return equippable != null && slot == equippable.slot() && !itemStack.nextDamageWillBreak();
}
@VisibleForTesting
public int getLastHurtByPlayerMemoryTime() {
return this.lastHurtByPlayerMemoryTime;
}
@Override
public boolean isTransmittingWaypoint() {
return this.getAttributeValue(Attributes.WAYPOINT_TRANSMIT_RANGE) > 0.0;
}
@Override
public Optional<WaypointTransmitter.Connection> makeWaypointConnectionWith(ServerPlayer player) {
if (this.firstTick || player == this) {
return Optional.empty();
}
if (WaypointTransmitter.doesSourceIgnoreReceiver(this, player)) {
return Optional.empty();
}
Waypoint.Icon icon = this.locatorBarIcon.cloneAndAssignStyle(this);
if (WaypointTransmitter.isReallyFar(this, player)) {
return Optional.of(new WaypointTransmitter.EntityAzimuthConnection(this, icon, player));
}
if (!WaypointTransmitter.isChunkVisible(this.chunkPosition(), player)) {
return Optional.of(new WaypointTransmitter.EntityChunkConnection(this, icon, player));
}
return Optional.of(new WaypointTransmitter.EntityBlockConnection(this, icon, player));
}
@Override
public Waypoint.Icon waypointIcon() {
return this.locatorBarIcon;
}
public record Fallsounds(SoundEvent small, SoundEvent big) {
}
}