2201 lines
		
	
	
	
		
			68 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			2201 lines
		
	
	
	
		
			68 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.entity.player;
 | |
| 
 | |
| import com.google.common.base.MoreObjects;
 | |
| import com.google.common.collect.ImmutableList;
 | |
| import com.google.common.collect.ImmutableMap;
 | |
| import com.google.common.collect.Lists;
 | |
| import com.google.common.math.IntMath;
 | |
| import com.mojang.authlib.GameProfile;
 | |
| import com.mojang.datafixers.util.Either;
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.util.Collection;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.Optional;
 | |
| import java.util.OptionalInt;
 | |
| import java.util.function.Predicate;
 | |
| import net.minecraft.Util;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Direction;
 | |
| import net.minecraft.core.GlobalPos;
 | |
| import net.minecraft.core.Holder;
 | |
| import net.minecraft.core.component.DataComponents;
 | |
| import net.minecraft.core.particles.ParticleTypes;
 | |
| import net.minecraft.nbt.CompoundTag;
 | |
| import net.minecraft.nbt.NbtUtils;
 | |
| import net.minecraft.network.chat.ClickEvent;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.network.chat.MutableComponent;
 | |
| import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
 | |
| import net.minecraft.network.syncher.EntityDataAccessor;
 | |
| import net.minecraft.network.syncher.EntityDataSerializers;
 | |
| import net.minecraft.network.syncher.SynchedEntityData;
 | |
| import net.minecraft.resources.ResourceKey;
 | |
| import net.minecraft.resources.ResourceLocation;
 | |
| import net.minecraft.server.dialog.Dialog;
 | |
| import net.minecraft.server.level.ServerLevel;
 | |
| import net.minecraft.server.level.ServerPlayer;
 | |
| import net.minecraft.sounds.SoundEvent;
 | |
| import net.minecraft.sounds.SoundEvents;
 | |
| import net.minecraft.sounds.SoundSource;
 | |
| import net.minecraft.stats.Stat;
 | |
| 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.util.Mth;
 | |
| import net.minecraft.util.ProblemReporter;
 | |
| import net.minecraft.util.Unit;
 | |
| import net.minecraft.world.Container;
 | |
| import net.minecraft.world.Difficulty;
 | |
| import net.minecraft.world.InteractionHand;
 | |
| import net.minecraft.world.InteractionResult;
 | |
| import net.minecraft.world.ItemStackWithSlot;
 | |
| import net.minecraft.world.MenuProvider;
 | |
| import net.minecraft.world.damagesource.DamageSource;
 | |
| import net.minecraft.world.effect.MobEffectInstance;
 | |
| import net.minecraft.world.effect.MobEffectUtil;
 | |
| import net.minecraft.world.effect.MobEffects;
 | |
| import net.minecraft.world.entity.Entity;
 | |
| import net.minecraft.world.entity.EntityAttachment;
 | |
| import net.minecraft.world.entity.EntityAttachments;
 | |
| import net.minecraft.world.entity.EntityDimensions;
 | |
| import net.minecraft.world.entity.EntityEquipment;
 | |
| import net.minecraft.world.entity.EntitySpawnReason;
 | |
| import net.minecraft.world.entity.EntityType;
 | |
| import net.minecraft.world.entity.EquipmentSlot;
 | |
| import net.minecraft.world.entity.HumanoidArm;
 | |
| import net.minecraft.world.entity.LivingEntity;
 | |
| import net.minecraft.world.entity.MoverType;
 | |
| import net.minecraft.world.entity.Pose;
 | |
| import net.minecraft.world.entity.SlotAccess;
 | |
| import net.minecraft.world.entity.TamableAnimal;
 | |
| import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
 | |
| import net.minecraft.world.entity.ai.attributes.Attributes;
 | |
| import net.minecraft.world.entity.animal.Parrot;
 | |
| import net.minecraft.world.entity.animal.horse.AbstractHorse;
 | |
| import net.minecraft.world.entity.boss.EnderDragonPart;
 | |
| import net.minecraft.world.entity.decoration.ArmorStand;
 | |
| import net.minecraft.world.entity.item.ItemEntity;
 | |
| import net.minecraft.world.entity.monster.warden.WardenSpawnTracker;
 | |
| import net.minecraft.world.entity.projectile.FishingHook;
 | |
| import net.minecraft.world.entity.projectile.Projectile;
 | |
| import net.minecraft.world.entity.projectile.ProjectileDeflection;
 | |
| import net.minecraft.world.food.FoodData;
 | |
| import net.minecraft.world.inventory.AbstractContainerMenu;
 | |
| import net.minecraft.world.inventory.ClickAction;
 | |
| import net.minecraft.world.inventory.InventoryMenu;
 | |
| import net.minecraft.world.inventory.PlayerEnderChestContainer;
 | |
| import net.minecraft.world.item.Item;
 | |
| import net.minecraft.world.item.ItemCooldowns;
 | |
| import net.minecraft.world.item.ItemStack;
 | |
| import net.minecraft.world.item.Items;
 | |
| import net.minecraft.world.item.ProjectileWeaponItem;
 | |
| import net.minecraft.world.item.component.BlocksAttacks;
 | |
| import net.minecraft.world.item.crafting.Recipe;
 | |
| import net.minecraft.world.item.crafting.RecipeHolder;
 | |
| import net.minecraft.world.item.enchantment.EnchantmentEffectComponents;
 | |
| import net.minecraft.world.item.enchantment.EnchantmentHelper;
 | |
| import net.minecraft.world.item.equipment.Equippable;
 | |
| import net.minecraft.world.item.trading.MerchantOffers;
 | |
| import net.minecraft.world.level.BaseCommandBlock;
 | |
| import net.minecraft.world.level.GameRules;
 | |
| import net.minecraft.world.level.GameType;
 | |
| import net.minecraft.world.level.Level;
 | |
| import net.minecraft.world.level.block.entity.CommandBlockEntity;
 | |
| import net.minecraft.world.level.block.entity.JigsawBlockEntity;
 | |
| import net.minecraft.world.level.block.entity.SignBlockEntity;
 | |
| import net.minecraft.world.level.block.entity.StructureBlockEntity;
 | |
| import net.minecraft.world.level.block.entity.TestBlockEntity;
 | |
| import net.minecraft.world.level.block.entity.TestInstanceBlockEntity;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| import net.minecraft.world.level.block.state.pattern.BlockInWorld;
 | |
| import net.minecraft.world.level.gameevent.GameEvent;
 | |
| import net.minecraft.world.level.storage.TagValueInput;
 | |
| import net.minecraft.world.level.storage.ValueInput;
 | |
| import net.minecraft.world.level.storage.ValueOutput;
 | |
| import net.minecraft.world.phys.AABB;
 | |
| import net.minecraft.world.phys.Vec3;
 | |
| import net.minecraft.world.scores.PlayerTeam;
 | |
| import net.minecraft.world.scores.Scoreboard;
 | |
| import net.minecraft.world.scores.Team;
 | |
| import org.jetbrains.annotations.NotNull;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public abstract class Player extends LivingEntity {
 | |
| 	private static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	public static final HumanoidArm DEFAULT_MAIN_HAND = HumanoidArm.RIGHT;
 | |
| 	public static final int DEFAULT_MODEL_CUSTOMIZATION = 0;
 | |
| 	public static final int MAX_HEALTH = 20;
 | |
| 	public static final int SLEEP_DURATION = 100;
 | |
| 	public static final int WAKE_UP_DURATION = 10;
 | |
| 	public static final int ENDER_SLOT_OFFSET = 200;
 | |
| 	public static final int HELD_ITEM_SLOT = 499;
 | |
| 	public static final int CRAFTING_SLOT_OFFSET = 500;
 | |
| 	public static final float DEFAULT_BLOCK_INTERACTION_RANGE = 4.5F;
 | |
| 	public static final float DEFAULT_ENTITY_INTERACTION_RANGE = 3.0F;
 | |
| 	public static final float CROUCH_BB_HEIGHT = 1.5F;
 | |
| 	public static final float SWIMMING_BB_WIDTH = 0.6F;
 | |
| 	public static final float SWIMMING_BB_HEIGHT = 0.6F;
 | |
| 	public static final float DEFAULT_EYE_HEIGHT = 1.62F;
 | |
| 	private static final int CURRENT_IMPULSE_CONTEXT_RESET_GRACE_TIME_TICKS = 40;
 | |
| 	public static final Vec3 DEFAULT_VEHICLE_ATTACHMENT = new Vec3(0.0, 0.6, 0.0);
 | |
| 	public static final EntityDimensions STANDING_DIMENSIONS = EntityDimensions.scalable(0.6F, 1.8F)
 | |
| 		.withEyeHeight(1.62F)
 | |
| 		.withAttachments(EntityAttachments.builder().attach(EntityAttachment.VEHICLE, DEFAULT_VEHICLE_ATTACHMENT));
 | |
| 	private static final Map<Pose, EntityDimensions> POSES = ImmutableMap.<Pose, EntityDimensions>builder()
 | |
| 		.put(Pose.STANDING, STANDING_DIMENSIONS)
 | |
| 		.put(Pose.SLEEPING, SLEEPING_DIMENSIONS)
 | |
| 		.put(Pose.FALL_FLYING, EntityDimensions.scalable(0.6F, 0.6F).withEyeHeight(0.4F))
 | |
| 		.put(Pose.SWIMMING, EntityDimensions.scalable(0.6F, 0.6F).withEyeHeight(0.4F))
 | |
| 		.put(Pose.SPIN_ATTACK, EntityDimensions.scalable(0.6F, 0.6F).withEyeHeight(0.4F))
 | |
| 		.put(
 | |
| 			Pose.CROUCHING,
 | |
| 			EntityDimensions.scalable(0.6F, 1.5F)
 | |
| 				.withEyeHeight(1.27F)
 | |
| 				.withAttachments(EntityAttachments.builder().attach(EntityAttachment.VEHICLE, DEFAULT_VEHICLE_ATTACHMENT))
 | |
| 		)
 | |
| 		.put(Pose.DYING, EntityDimensions.fixed(0.2F, 0.2F).withEyeHeight(1.62F))
 | |
| 		.build();
 | |
| 	private static final EntityDataAccessor<Float> DATA_PLAYER_ABSORPTION_ID = SynchedEntityData.defineId(Player.class, EntityDataSerializers.FLOAT);
 | |
| 	private static final EntityDataAccessor<Integer> DATA_SCORE_ID = SynchedEntityData.defineId(Player.class, EntityDataSerializers.INT);
 | |
| 	protected static final EntityDataAccessor<Byte> DATA_PLAYER_MODE_CUSTOMISATION = SynchedEntityData.defineId(Player.class, EntityDataSerializers.BYTE);
 | |
| 	protected static final EntityDataAccessor<Byte> DATA_PLAYER_MAIN_HAND = SynchedEntityData.defineId(Player.class, EntityDataSerializers.BYTE);
 | |
| 	protected static final EntityDataAccessor<CompoundTag> DATA_SHOULDER_LEFT = SynchedEntityData.defineId(Player.class, EntityDataSerializers.COMPOUND_TAG);
 | |
| 	protected static final EntityDataAccessor<CompoundTag> DATA_SHOULDER_RIGHT = SynchedEntityData.defineId(Player.class, EntityDataSerializers.COMPOUND_TAG);
 | |
| 	public static final int CLIENT_LOADED_TIMEOUT_TIME = 60;
 | |
| 	private static final short DEFAULT_SLEEP_TIMER = 0;
 | |
| 	private static final float DEFAULT_EXPERIENCE_PROGRESS = 0.0F;
 | |
| 	private static final int DEFAULT_EXPERIENCE_LEVEL = 0;
 | |
| 	private static final int DEFAULT_TOTAL_EXPERIENCE = 0;
 | |
| 	private static final int NO_ENCHANTMENT_SEED = 0;
 | |
| 	private static final int DEFAULT_SELECTED_SLOT = 0;
 | |
| 	private static final int DEFAULT_SCORE = 0;
 | |
| 	private static final boolean DEFAULT_IGNORE_FALL_DAMAGE_FROM_CURRENT_IMPULSE = false;
 | |
| 	private static final int DEFAULT_CURRENT_IMPULSE_CONTEXT_RESET_GRACE_TIME = 0;
 | |
| 	private long timeEntitySatOnShoulder;
 | |
| 	final Inventory inventory;
 | |
| 	protected PlayerEnderChestContainer enderChestInventory = new PlayerEnderChestContainer();
 | |
| 	public final InventoryMenu inventoryMenu;
 | |
| 	public AbstractContainerMenu containerMenu;
 | |
| 	protected FoodData foodData = new FoodData();
 | |
| 	protected int jumpTriggerTime;
 | |
| 	private boolean clientLoaded = false;
 | |
| 	protected int clientLoadedTimeoutTimer = 60;
 | |
| 	public float oBob;
 | |
| 	public float bob;
 | |
| 	public int takeXpDelay;
 | |
| 	public double xCloakO;
 | |
| 	public double yCloakO;
 | |
| 	public double zCloakO;
 | |
| 	public double xCloak;
 | |
| 	public double yCloak;
 | |
| 	public double zCloak;
 | |
| 	private int sleepCounter = 0;
 | |
| 	protected boolean wasUnderwater;
 | |
| 	private final Abilities abilities = new Abilities();
 | |
| 	public int experienceLevel = 0;
 | |
| 	public int totalExperience = 0;
 | |
| 	public float experienceProgress = 0.0F;
 | |
| 	protected int enchantmentSeed = 0;
 | |
| 	protected final float defaultFlySpeed = 0.02F;
 | |
| 	private int lastLevelUpTime;
 | |
| 	/**
 | |
| 	 * The player's unique game profile
 | |
| 	 */
 | |
| 	private final GameProfile gameProfile;
 | |
| 	private boolean reducedDebugInfo;
 | |
| 	private ItemStack lastItemInMainHand = ItemStack.EMPTY;
 | |
| 	private final ItemCooldowns cooldowns = this.createItemCooldowns();
 | |
| 	private Optional<GlobalPos> lastDeathLocation = Optional.empty();
 | |
| 	@Nullable
 | |
| 	public FishingHook fishing;
 | |
| 	protected float hurtDir;
 | |
| 	@Nullable
 | |
| 	public Vec3 currentImpulseImpactPos;
 | |
| 	@Nullable
 | |
| 	public Entity currentExplosionCause;
 | |
| 	private boolean ignoreFallDamageFromCurrentImpulse = false;
 | |
| 	private int currentImpulseContextResetGraceTime = 0;
 | |
| 
 | |
| 	public Player(Level level, GameProfile gameProfile) {
 | |
| 		super(EntityType.PLAYER, level);
 | |
| 		this.setUUID(gameProfile.getId());
 | |
| 		this.gameProfile = gameProfile;
 | |
| 		this.inventory = new Inventory(this, this.equipment);
 | |
| 		this.inventoryMenu = new InventoryMenu(this.inventory, !level.isClientSide, this);
 | |
| 		this.containerMenu = this.inventoryMenu;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected EntityEquipment createEquipment() {
 | |
| 		return new PlayerEquipment(this);
 | |
| 	}
 | |
| 
 | |
| 	public boolean blockActionRestricted(Level level, BlockPos pos, GameType gameMode) {
 | |
| 		if (!gameMode.isBlockPlacingRestricted()) {
 | |
| 			return false;
 | |
| 		} else if (gameMode == GameType.SPECTATOR) {
 | |
| 			return true;
 | |
| 		} else if (this.mayBuild()) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			ItemStack itemStack = this.getMainHandItem();
 | |
| 			return itemStack.isEmpty() || !itemStack.canBreakBlockInAdventureMode(new BlockInWorld(level, pos, false));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static AttributeSupplier.Builder createAttributes() {
 | |
| 		return LivingEntity.createLivingAttributes()
 | |
| 			.add(Attributes.ATTACK_DAMAGE, 1.0)
 | |
| 			.add(Attributes.MOVEMENT_SPEED, 0.1F)
 | |
| 			.add(Attributes.ATTACK_SPEED)
 | |
| 			.add(Attributes.LUCK)
 | |
| 			.add(Attributes.BLOCK_INTERACTION_RANGE, 4.5)
 | |
| 			.add(Attributes.ENTITY_INTERACTION_RANGE, 3.0)
 | |
| 			.add(Attributes.BLOCK_BREAK_SPEED)
 | |
| 			.add(Attributes.SUBMERGED_MINING_SPEED)
 | |
| 			.add(Attributes.SNEAKING_SPEED)
 | |
| 			.add(Attributes.MINING_EFFICIENCY)
 | |
| 			.add(Attributes.SWEEPING_DAMAGE_RATIO)
 | |
| 			.add(Attributes.WAYPOINT_TRANSMIT_RANGE, 6.0E7)
 | |
| 			.add(Attributes.WAYPOINT_RECEIVE_RANGE, 6.0E7);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void defineSynchedData(SynchedEntityData.Builder builder) {
 | |
| 		super.defineSynchedData(builder);
 | |
| 		builder.define(DATA_PLAYER_ABSORPTION_ID, 0.0F);
 | |
| 		builder.define(DATA_SCORE_ID, 0);
 | |
| 		builder.define(DATA_PLAYER_MODE_CUSTOMISATION, (byte)0);
 | |
| 		builder.define(DATA_PLAYER_MAIN_HAND, (byte)DEFAULT_MAIN_HAND.getId());
 | |
| 		builder.define(DATA_SHOULDER_LEFT, new CompoundTag());
 | |
| 		builder.define(DATA_SHOULDER_RIGHT, new CompoundTag());
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void tick() {
 | |
| 		this.noPhysics = this.isSpectator();
 | |
| 		if (this.isSpectator() || this.isPassenger()) {
 | |
| 			this.setOnGround(false);
 | |
| 		}
 | |
| 
 | |
| 		if (this.takeXpDelay > 0) {
 | |
| 			this.takeXpDelay--;
 | |
| 		}
 | |
| 
 | |
| 		if (this.isSleeping()) {
 | |
| 			this.sleepCounter++;
 | |
| 			if (this.sleepCounter > 100) {
 | |
| 				this.sleepCounter = 100;
 | |
| 			}
 | |
| 
 | |
| 			if (!this.level().isClientSide && this.level().isBrightOutside()) {
 | |
| 				this.stopSleepInBed(false, true);
 | |
| 			}
 | |
| 		} else if (this.sleepCounter > 0) {
 | |
| 			this.sleepCounter++;
 | |
| 			if (this.sleepCounter >= 110) {
 | |
| 				this.sleepCounter = 0;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		this.updateIsUnderwater();
 | |
| 		super.tick();
 | |
| 		if (!this.level().isClientSide && this.containerMenu != null && !this.containerMenu.stillValid(this)) {
 | |
| 			this.closeContainer();
 | |
| 			this.containerMenu = this.inventoryMenu;
 | |
| 		}
 | |
| 
 | |
| 		this.moveCloak();
 | |
| 		if (this instanceof ServerPlayer serverPlayer) {
 | |
| 			this.foodData.tick(serverPlayer);
 | |
| 			this.awardStat(Stats.PLAY_TIME);
 | |
| 			this.awardStat(Stats.TOTAL_WORLD_TIME);
 | |
| 			if (this.isAlive()) {
 | |
| 				this.awardStat(Stats.TIME_SINCE_DEATH);
 | |
| 			}
 | |
| 
 | |
| 			if (this.isDiscrete()) {
 | |
| 				this.awardStat(Stats.CROUCH_TIME);
 | |
| 			}
 | |
| 
 | |
| 			if (!this.isSleeping()) {
 | |
| 				this.awardStat(Stats.TIME_SINCE_REST);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		int i = 29999999;
 | |
| 		double d = Mth.clamp(this.getX(), -2.9999999E7, 2.9999999E7);
 | |
| 		double e = Mth.clamp(this.getZ(), -2.9999999E7, 2.9999999E7);
 | |
| 		if (d != this.getX() || e != this.getZ()) {
 | |
| 			this.setPos(d, this.getY(), e);
 | |
| 		}
 | |
| 
 | |
| 		this.attackStrengthTicker++;
 | |
| 		ItemStack itemStack = this.getMainHandItem();
 | |
| 		if (!ItemStack.matches(this.lastItemInMainHand, itemStack)) {
 | |
| 			if (!ItemStack.isSameItem(this.lastItemInMainHand, itemStack)) {
 | |
| 				this.resetAttackStrengthTicker();
 | |
| 			}
 | |
| 
 | |
| 			this.lastItemInMainHand = itemStack.copy();
 | |
| 		}
 | |
| 
 | |
| 		if (!this.isEyeInFluid(FluidTags.WATER) && this.isEquipped(Items.TURTLE_HELMET)) {
 | |
| 			this.turtleHelmetTick();
 | |
| 		}
 | |
| 
 | |
| 		this.cooldowns.tick();
 | |
| 		this.updatePlayerPose();
 | |
| 		if (this.currentImpulseContextResetGraceTime > 0) {
 | |
| 			this.currentImpulseContextResetGraceTime--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected float getMaxHeadRotationRelativeToBody() {
 | |
| 		return this.isBlocking() ? 15.0F : super.getMaxHeadRotationRelativeToBody();
 | |
| 	}
 | |
| 
 | |
| 	public boolean isSecondaryUseActive() {
 | |
| 		return this.isShiftKeyDown();
 | |
| 	}
 | |
| 
 | |
| 	protected boolean wantsToStopRiding() {
 | |
| 		return this.isShiftKeyDown();
 | |
| 	}
 | |
| 
 | |
| 	protected boolean isStayingOnGroundSurface() {
 | |
| 		return this.isShiftKeyDown();
 | |
| 	}
 | |
| 
 | |
| 	protected boolean updateIsUnderwater() {
 | |
| 		this.wasUnderwater = this.isEyeInFluid(FluidTags.WATER);
 | |
| 		return this.wasUnderwater;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onAboveBubbleColumn(boolean downwards, BlockPos pos) {
 | |
| 		if (!this.getAbilities().flying) {
 | |
| 			super.onAboveBubbleColumn(downwards, pos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onInsideBubbleColumn(boolean downwards) {
 | |
| 		if (!this.getAbilities().flying) {
 | |
| 			super.onInsideBubbleColumn(downwards);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void turtleHelmetTick() {
 | |
| 		this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true));
 | |
| 	}
 | |
| 
 | |
| 	private boolean isEquipped(Item item) {
 | |
| 		for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) {
 | |
| 			ItemStack itemStack = this.getItemBySlot(equipmentSlot);
 | |
| 			Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
 | |
| 			if (itemStack.is(item) && equippable != null && equippable.slot() == equipmentSlot) {
 | |
| 				return true;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	protected ItemCooldowns createItemCooldowns() {
 | |
| 		return new ItemCooldowns();
 | |
| 	}
 | |
| 
 | |
| 	private void moveCloak() {
 | |
| 		this.xCloakO = this.xCloak;
 | |
| 		this.yCloakO = this.yCloak;
 | |
| 		this.zCloakO = this.zCloak;
 | |
| 		double d = this.getX() - this.xCloak;
 | |
| 		double e = this.getY() - this.yCloak;
 | |
| 		double f = this.getZ() - this.zCloak;
 | |
| 		double g = 10.0;
 | |
| 		if (d > 10.0) {
 | |
| 			this.xCloak = this.getX();
 | |
| 			this.xCloakO = this.xCloak;
 | |
| 		}
 | |
| 
 | |
| 		if (f > 10.0) {
 | |
| 			this.zCloak = this.getZ();
 | |
| 			this.zCloakO = this.zCloak;
 | |
| 		}
 | |
| 
 | |
| 		if (e > 10.0) {
 | |
| 			this.yCloak = this.getY();
 | |
| 			this.yCloakO = this.yCloak;
 | |
| 		}
 | |
| 
 | |
| 		if (d < -10.0) {
 | |
| 			this.xCloak = this.getX();
 | |
| 			this.xCloakO = this.xCloak;
 | |
| 		}
 | |
| 
 | |
| 		if (f < -10.0) {
 | |
| 			this.zCloak = this.getZ();
 | |
| 			this.zCloakO = this.zCloak;
 | |
| 		}
 | |
| 
 | |
| 		if (e < -10.0) {
 | |
| 			this.yCloak = this.getY();
 | |
| 			this.yCloakO = this.yCloak;
 | |
| 		}
 | |
| 
 | |
| 		this.xCloak += d * 0.25;
 | |
| 		this.zCloak += f * 0.25;
 | |
| 		this.yCloak += e * 0.25;
 | |
| 	}
 | |
| 
 | |
| 	protected void updatePlayerPose() {
 | |
| 		if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.SWIMMING)) {
 | |
| 			Pose pose = this.getDesiredPose();
 | |
| 			Pose pose2;
 | |
| 			if (this.isSpectator() || this.isPassenger() || this.canPlayerFitWithinBlocksAndEntitiesWhen(pose)) {
 | |
| 				pose2 = pose;
 | |
| 			} else if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING)) {
 | |
| 				pose2 = Pose.CROUCHING;
 | |
| 			} else {
 | |
| 				pose2 = Pose.SWIMMING;
 | |
| 			}
 | |
| 
 | |
| 			this.setPose(pose2);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private Pose getDesiredPose() {
 | |
| 		if (this.isSleeping()) {
 | |
| 			return Pose.SLEEPING;
 | |
| 		} else if (this.isSwimming()) {
 | |
| 			return Pose.SWIMMING;
 | |
| 		} else if (this.isFallFlying()) {
 | |
| 			return Pose.FALL_FLYING;
 | |
| 		} else if (this.isAutoSpinAttack()) {
 | |
| 			return Pose.SPIN_ATTACK;
 | |
| 		} else {
 | |
| 			return this.isShiftKeyDown() && !this.abilities.flying ? Pose.CROUCHING : Pose.STANDING;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected boolean canPlayerFitWithinBlocksAndEntitiesWhen(Pose pose) {
 | |
| 		return this.level().noCollision(this, this.getDimensions(pose).makeBoundingBox(this.position()).deflate(1.0E-7));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getSwimSound() {
 | |
| 		return SoundEvents.PLAYER_SWIM;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getSwimSplashSound() {
 | |
| 		return SoundEvents.PLAYER_SPLASH;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getSwimHighSpeedSplashSound() {
 | |
| 		return SoundEvents.PLAYER_SPLASH_HIGH_SPEED;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public int getDimensionChangingDelay() {
 | |
| 		return 10;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void playSound(SoundEvent sound, float volume, float pitch) {
 | |
| 		this.level().playSound(this, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), volume, pitch);
 | |
| 	}
 | |
| 
 | |
| 	public void playNotifySound(SoundEvent sound, SoundSource source, float volume, float pitch) {
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public SoundSource getSoundSource() {
 | |
| 		return SoundSource.PLAYERS;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected int getFireImmuneTicks() {
 | |
| 		return 20;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void handleEntityEvent(byte id) {
 | |
| 		if (id == 9) {
 | |
| 			this.completeUsingItem();
 | |
| 		} else if (id == 23) {
 | |
| 			this.reducedDebugInfo = false;
 | |
| 		} else if (id == 22) {
 | |
| 			this.reducedDebugInfo = true;
 | |
| 		} else {
 | |
| 			super.handleEntityEvent(id);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sets the current crafting inventory back to the 2x2 square.
 | |
| 	 */
 | |
| 	protected void closeContainer() {
 | |
| 		this.containerMenu = this.inventoryMenu;
 | |
| 	}
 | |
| 
 | |
| 	protected void doCloseContainer() {
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void rideTick() {
 | |
| 		if (!this.level().isClientSide && this.wantsToStopRiding() && this.isPassenger()) {
 | |
| 			this.stopRiding();
 | |
| 			this.setShiftKeyDown(false);
 | |
| 		} else {
 | |
| 			super.rideTick();
 | |
| 			this.oBob = this.bob;
 | |
| 			this.bob = 0.0F;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void aiStep() {
 | |
| 		if (this.jumpTriggerTime > 0) {
 | |
| 			this.jumpTriggerTime--;
 | |
| 		}
 | |
| 
 | |
| 		this.tickRegeneration();
 | |
| 		this.inventory.tick();
 | |
| 		this.oBob = this.bob;
 | |
| 		if (this.abilities.flying && !this.isPassenger()) {
 | |
| 			this.resetFallDistance();
 | |
| 		}
 | |
| 
 | |
| 		super.aiStep();
 | |
| 		this.updateSwingTime();
 | |
| 		this.yHeadRot = this.getYRot();
 | |
| 		this.setSpeed((float)this.getAttributeValue(Attributes.MOVEMENT_SPEED));
 | |
| 		float f;
 | |
| 		if (this.onGround() && !this.isDeadOrDying() && !this.isSwimming()) {
 | |
| 			f = Math.min(0.1F, (float)this.getDeltaMovement().horizontalDistance());
 | |
| 		} else {
 | |
| 			f = 0.0F;
 | |
| 		}
 | |
| 
 | |
| 		this.bob = this.bob + (f - this.bob) * 0.4F;
 | |
| 		if (this.getHealth() > 0.0F && !this.isSpectator()) {
 | |
| 			AABB aABB;
 | |
| 			if (this.isPassenger() && !this.getVehicle().isRemoved()) {
 | |
| 				aABB = this.getBoundingBox().minmax(this.getVehicle().getBoundingBox()).inflate(1.0, 0.0, 1.0);
 | |
| 			} else {
 | |
| 				aABB = this.getBoundingBox().inflate(1.0, 0.5, 1.0);
 | |
| 			}
 | |
| 
 | |
| 			List<Entity> list = this.level().getEntities(this, aABB);
 | |
| 			List<Entity> list2 = Lists.<Entity>newArrayList();
 | |
| 
 | |
| 			for (Entity entity : list) {
 | |
| 				if (entity.getType() == EntityType.EXPERIENCE_ORB) {
 | |
| 					list2.add(entity);
 | |
| 				} else if (!entity.isRemoved()) {
 | |
| 					this.touch(entity);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (!list2.isEmpty()) {
 | |
| 				this.touch(Util.getRandom(list2, this.random));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		this.playShoulderEntityAmbientSound(this.getShoulderEntityLeft());
 | |
| 		this.playShoulderEntityAmbientSound(this.getShoulderEntityRight());
 | |
| 		if (!this.level().isClientSide && (this.fallDistance > 0.5 || this.isInWater()) || this.abilities.flying || this.isSleeping() || this.isInPowderSnow) {
 | |
| 			this.removeEntitiesOnShoulder();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void tickRegeneration() {
 | |
| 	}
 | |
| 
 | |
| 	private void playShoulderEntityAmbientSound(CompoundTag entityCompound) {
 | |
| 		if (!entityCompound.isEmpty() && !entityCompound.getBooleanOr("Silent", false)) {
 | |
| 			if (this.level().random.nextInt(200) == 0) {
 | |
| 				EntityType<?> entityType = (EntityType<?>)entityCompound.read("id", EntityType.CODEC).orElse(null);
 | |
| 				if (entityType == EntityType.PARROT && !Parrot.imitateNearbyMobs(this.level(), this)) {
 | |
| 					this.level()
 | |
| 						.playSound(
 | |
| 							null,
 | |
| 							this.getX(),
 | |
| 							this.getY(),
 | |
| 							this.getZ(),
 | |
| 							Parrot.getAmbient(this.level(), this.level().random),
 | |
| 							this.getSoundSource(),
 | |
| 							1.0F,
 | |
| 							Parrot.getPitch(this.level().random)
 | |
| 						);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void touch(Entity entity) {
 | |
| 		entity.playerTouch(this);
 | |
| 	}
 | |
| 
 | |
| 	public int getScore() {
 | |
| 		return this.entityData.get(DATA_SCORE_ID);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Set player's score
 | |
| 	 */
 | |
| 	public void setScore(int score) {
 | |
| 		this.entityData.set(DATA_SCORE_ID, score);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Add to player's score
 | |
| 	 */
 | |
| 	public void increaseScore(int score) {
 | |
| 		int i = this.getScore();
 | |
| 		this.entityData.set(DATA_SCORE_ID, i + score);
 | |
| 	}
 | |
| 
 | |
| 	public void startAutoSpinAttack(int ticks, float damage, ItemStack itemStack) {
 | |
| 		this.autoSpinAttackTicks = ticks;
 | |
| 		this.autoSpinAttackDmg = damage;
 | |
| 		this.autoSpinAttackItemStack = itemStack;
 | |
| 		if (!this.level().isClientSide) {
 | |
| 			this.removeEntitiesOnShoulder();
 | |
| 			this.setLivingEntityFlag(4, true);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@NotNull
 | |
| 	@Override
 | |
| 	public ItemStack getWeaponItem() {
 | |
| 		return this.isAutoSpinAttack() && this.autoSpinAttackItemStack != null ? this.autoSpinAttackItemStack : super.getWeaponItem();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void die(DamageSource damageSource) {
 | |
| 		super.die(damageSource);
 | |
| 		this.reapplyPosition();
 | |
| 		if (!this.isSpectator() && this.level() instanceof ServerLevel serverLevel) {
 | |
| 			this.dropAllDeathLoot(serverLevel, damageSource);
 | |
| 		}
 | |
| 
 | |
| 		if (damageSource != null) {
 | |
| 			this.setDeltaMovement(
 | |
| 				-Mth.cos((this.getHurtDir() + this.getYRot()) * (float) (Math.PI / 180.0)) * 0.1F,
 | |
| 				0.1F,
 | |
| 				-Mth.sin((this.getHurtDir() + this.getYRot()) * (float) (Math.PI / 180.0)) * 0.1F
 | |
| 			);
 | |
| 		} else {
 | |
| 			this.setDeltaMovement(0.0, 0.1, 0.0);
 | |
| 		}
 | |
| 
 | |
| 		this.awardStat(Stats.DEATHS);
 | |
| 		this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_DEATH));
 | |
| 		this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_REST));
 | |
| 		this.clearFire();
 | |
| 		this.setSharedFlagOnFire(false);
 | |
| 		this.setLastDeathLocation(Optional.of(GlobalPos.of(this.level().dimension(), this.blockPosition())));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void dropEquipment(ServerLevel level) {
 | |
| 		super.dropEquipment(level);
 | |
| 		if (!level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY)) {
 | |
| 			this.destroyVanishingCursedItems();
 | |
| 			this.inventory.dropAll();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void destroyVanishingCursedItems() {
 | |
| 		for (int i = 0; i < this.inventory.getContainerSize(); i++) {
 | |
| 			ItemStack itemStack = this.inventory.getItem(i);
 | |
| 			if (!itemStack.isEmpty() && EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) {
 | |
| 				this.inventory.removeItemNoUpdate(i);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getHurtSound(DamageSource damageSource) {
 | |
| 		return damageSource.type().effects().sound();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getDeathSound() {
 | |
| 		return SoundEvents.PLAYER_DEATH;
 | |
| 	}
 | |
| 
 | |
| 	public void handleCreativeModeItemDrop(ItemStack stack) {
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Drops an item into the world.
 | |
| 	 */
 | |
| 	@Nullable
 | |
| 	public ItemEntity drop(ItemStack itemStack, boolean includeThrowerName) {
 | |
| 		return this.drop(itemStack, false, includeThrowerName);
 | |
| 	}
 | |
| 
 | |
| 	public float getDestroySpeed(BlockState state) {
 | |
| 		float f = this.inventory.getSelectedItem().getDestroySpeed(state);
 | |
| 		if (f > 1.0F) {
 | |
| 			f += (float)this.getAttributeValue(Attributes.MINING_EFFICIENCY);
 | |
| 		}
 | |
| 
 | |
| 		if (MobEffectUtil.hasDigSpeed(this)) {
 | |
| 			f *= 1.0F + (MobEffectUtil.getDigSpeedAmplification(this) + 1) * 0.2F;
 | |
| 		}
 | |
| 
 | |
| 		if (this.hasEffect(MobEffects.MINING_FATIGUE)) {
 | |
| 			float g = switch (this.getEffect(MobEffects.MINING_FATIGUE).getAmplifier()) {
 | |
| 				case 0 -> 0.3F;
 | |
| 				case 1 -> 0.09F;
 | |
| 				case 2 -> 0.0027F;
 | |
| 				default -> 8.1E-4F;
 | |
| 			};
 | |
| 			f *= g;
 | |
| 		}
 | |
| 
 | |
| 		f *= (float)this.getAttributeValue(Attributes.BLOCK_BREAK_SPEED);
 | |
| 		if (this.isEyeInFluid(FluidTags.WATER)) {
 | |
| 			f *= (float)this.getAttribute(Attributes.SUBMERGED_MINING_SPEED).getValue();
 | |
| 		}
 | |
| 
 | |
| 		if (!this.onGround()) {
 | |
| 			f /= 5.0F;
 | |
| 		}
 | |
| 
 | |
| 		return f;
 | |
| 	}
 | |
| 
 | |
| 	public boolean hasCorrectToolForDrops(BlockState state) {
 | |
| 		return !state.requiresCorrectToolForDrops() || this.inventory.getSelectedItem().isCorrectToolForDrops(state);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void readAdditionalSaveData(ValueInput input) {
 | |
| 		super.readAdditionalSaveData(input);
 | |
| 		this.setUUID(this.gameProfile.getId());
 | |
| 		this.inventory.load(input.listOrEmpty("Inventory", ItemStackWithSlot.CODEC));
 | |
| 		this.inventory.setSelectedSlot(input.getIntOr("SelectedItemSlot", 0));
 | |
| 		this.sleepCounter = input.getShortOr("SleepTimer", (short)0);
 | |
| 		this.experienceProgress = input.getFloatOr("XpP", 0.0F);
 | |
| 		this.experienceLevel = input.getIntOr("XpLevel", 0);
 | |
| 		this.totalExperience = input.getIntOr("XpTotal", 0);
 | |
| 		this.enchantmentSeed = input.getIntOr("XpSeed", 0);
 | |
| 		if (this.enchantmentSeed == 0) {
 | |
| 			this.enchantmentSeed = this.random.nextInt();
 | |
| 		}
 | |
| 
 | |
| 		this.setScore(input.getIntOr("Score", 0));
 | |
| 		this.foodData.readAdditionalSaveData(input);
 | |
| 		input.read("abilities", Abilities.Packed.CODEC).ifPresent(this.abilities::apply);
 | |
| 		this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.abilities.getWalkingSpeed());
 | |
| 		this.enderChestInventory.fromSlots(input.listOrEmpty("EnderItems", ItemStackWithSlot.CODEC));
 | |
| 		this.setShoulderEntityLeft((CompoundTag)input.read("ShoulderEntityLeft", CompoundTag.CODEC).orElseGet(CompoundTag::new));
 | |
| 		this.setShoulderEntityRight((CompoundTag)input.read("ShoulderEntityRight", CompoundTag.CODEC).orElseGet(CompoundTag::new));
 | |
| 		this.setLastDeathLocation(input.read("LastDeathLocation", GlobalPos.CODEC));
 | |
| 		this.currentImpulseImpactPos = (Vec3)input.read("current_explosion_impact_pos", Vec3.CODEC).orElse(null);
 | |
| 		this.ignoreFallDamageFromCurrentImpulse = input.getBooleanOr("ignore_fall_damage_from_current_explosion", false);
 | |
| 		this.currentImpulseContextResetGraceTime = input.getIntOr("current_impulse_context_reset_grace_time", 0);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void addAdditionalSaveData(ValueOutput output) {
 | |
| 		super.addAdditionalSaveData(output);
 | |
| 		NbtUtils.addCurrentDataVersion(output);
 | |
| 		this.inventory.save(output.list("Inventory", ItemStackWithSlot.CODEC));
 | |
| 		output.putInt("SelectedItemSlot", this.inventory.getSelectedSlot());
 | |
| 		output.putShort("SleepTimer", (short)this.sleepCounter);
 | |
| 		output.putFloat("XpP", this.experienceProgress);
 | |
| 		output.putInt("XpLevel", this.experienceLevel);
 | |
| 		output.putInt("XpTotal", this.totalExperience);
 | |
| 		output.putInt("XpSeed", this.enchantmentSeed);
 | |
| 		output.putInt("Score", this.getScore());
 | |
| 		this.foodData.addAdditionalSaveData(output);
 | |
| 		output.store("abilities", Abilities.Packed.CODEC, this.abilities.pack());
 | |
| 		this.enderChestInventory.storeAsSlots(output.list("EnderItems", ItemStackWithSlot.CODEC));
 | |
| 		if (!this.getShoulderEntityLeft().isEmpty()) {
 | |
| 			output.store("ShoulderEntityLeft", CompoundTag.CODEC, this.getShoulderEntityLeft());
 | |
| 		}
 | |
| 
 | |
| 		if (!this.getShoulderEntityRight().isEmpty()) {
 | |
| 			output.store("ShoulderEntityRight", CompoundTag.CODEC, this.getShoulderEntityRight());
 | |
| 		}
 | |
| 
 | |
| 		this.lastDeathLocation.ifPresent(globalPos -> output.store("LastDeathLocation", GlobalPos.CODEC, globalPos));
 | |
| 		output.storeNullable("current_explosion_impact_pos", Vec3.CODEC, this.currentImpulseImpactPos);
 | |
| 		output.putBoolean("ignore_fall_damage_from_current_explosion", this.ignoreFallDamageFromCurrentImpulse);
 | |
| 		output.putInt("current_impulse_context_reset_grace_time", this.currentImpulseContextResetGraceTime);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isInvulnerableTo(ServerLevel level, DamageSource damageSource) {
 | |
| 		if (super.isInvulnerableTo(level, damageSource)) {
 | |
| 			return true;
 | |
| 		} else if (damageSource.is(DamageTypeTags.IS_DROWNING)) {
 | |
| 			return !level.getGameRules().getBoolean(GameRules.RULE_DROWNING_DAMAGE);
 | |
| 		} else if (damageSource.is(DamageTypeTags.IS_FALL)) {
 | |
| 			return !level.getGameRules().getBoolean(GameRules.RULE_FALL_DAMAGE);
 | |
| 		} else if (damageSource.is(DamageTypeTags.IS_FIRE)) {
 | |
| 			return !level.getGameRules().getBoolean(GameRules.RULE_FIRE_DAMAGE);
 | |
| 		} else {
 | |
| 			return damageSource.is(DamageTypeTags.IS_FREEZING) ? !level.getGameRules().getBoolean(GameRules.RULE_FREEZE_DAMAGE) : false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) {
 | |
| 		if (this.isInvulnerableTo(level, damageSource)) {
 | |
| 			return false;
 | |
| 		} else if (this.abilities.invulnerable && !damageSource.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			this.noActionTime = 0;
 | |
| 			if (this.isDeadOrDying()) {
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				this.removeEntitiesOnShoulder();
 | |
| 				if (damageSource.scalesWithDifficulty()) {
 | |
| 					if (level.getDifficulty() == Difficulty.PEACEFUL) {
 | |
| 						amount = 0.0F;
 | |
| 					}
 | |
| 
 | |
| 					if (level.getDifficulty() == Difficulty.EASY) {
 | |
| 						amount = Math.min(amount / 2.0F + 1.0F, amount);
 | |
| 					}
 | |
| 
 | |
| 					if (level.getDifficulty() == Difficulty.HARD) {
 | |
| 						amount = amount * 3.0F / 2.0F;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return amount == 0.0F ? false : super.hurtServer(level, damageSource, amount);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void blockUsingItem(ServerLevel level, LivingEntity entity) {
 | |
| 		super.blockUsingItem(level, entity);
 | |
| 		ItemStack itemStack = this.getItemBlockingWith();
 | |
| 		BlocksAttacks blocksAttacks = itemStack != null ? itemStack.get(DataComponents.BLOCKS_ATTACKS) : null;
 | |
| 		float f = entity.getSecondsToDisableBlocking();
 | |
| 		if (f > 0.0F && blocksAttacks != null) {
 | |
| 			blocksAttacks.disable(level, this, f, itemStack);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canBeSeenAsEnemy() {
 | |
| 		return !this.getAbilities().invulnerable && super.canBeSeenAsEnemy();
 | |
| 	}
 | |
| 
 | |
| 	public boolean canHarmPlayer(Player other) {
 | |
| 		Team team = this.getTeam();
 | |
| 		Team team2 = other.getTeam();
 | |
| 		if (team == null) {
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			return !team.isAlliedTo(team2) ? true : team.isAllowFriendlyFire();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void hurtArmor(DamageSource damageSource, float damageAmount) {
 | |
| 		this.doHurtEquipment(damageSource, damageAmount, new EquipmentSlot[]{EquipmentSlot.FEET, EquipmentSlot.LEGS, EquipmentSlot.CHEST, EquipmentSlot.HEAD});
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void hurtHelmet(DamageSource damageSource, float damageAmount) {
 | |
| 		this.doHurtEquipment(damageSource, damageAmount, new EquipmentSlot[]{EquipmentSlot.HEAD});
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) {
 | |
| 		if (!this.isInvulnerableTo(level, damageSource)) {
 | |
| 			amount = this.getDamageAfterArmorAbsorb(damageSource, amount);
 | |
| 			amount = this.getDamageAfterMagicAbsorb(damageSource, amount);
 | |
| 			float var8 = Math.max(amount - this.getAbsorptionAmount(), 0.0F);
 | |
| 			this.setAbsorptionAmount(this.getAbsorptionAmount() - (amount - var8));
 | |
| 			float g = amount - var8;
 | |
| 			if (g > 0.0F && g < 3.4028235E37F) {
 | |
| 				this.awardStat(Stats.DAMAGE_ABSORBED, Math.round(g * 10.0F));
 | |
| 			}
 | |
| 
 | |
| 			if (var8 != 0.0F) {
 | |
| 				this.causeFoodExhaustion(damageSource.getFoodExhaustion());
 | |
| 				this.getCombatTracker().recordDamage(damageSource, var8);
 | |
| 				this.setHealth(this.getHealth() - var8);
 | |
| 				if (var8 < 3.4028235E37F) {
 | |
| 					this.awardStat(Stats.DAMAGE_TAKEN, Math.round(var8 * 10.0F));
 | |
| 				}
 | |
| 
 | |
| 				this.gameEvent(GameEvent.ENTITY_DAMAGE);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean isTextFilteringEnabled() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public void openTextEdit(SignBlockEntity signEntity, boolean isFrontText) {
 | |
| 	}
 | |
| 
 | |
| 	public void openMinecartCommandBlock(BaseCommandBlock commandEntity) {
 | |
| 	}
 | |
| 
 | |
| 	public void openCommandBlock(CommandBlockEntity commandBlockEntity) {
 | |
| 	}
 | |
| 
 | |
| 	public void openStructureBlock(StructureBlockEntity structureEntity) {
 | |
| 	}
 | |
| 
 | |
| 	public void openTestBlock(TestBlockEntity testBlockEntity) {
 | |
| 	}
 | |
| 
 | |
| 	public void openTestInstanceBlock(TestInstanceBlockEntity testInstanceBlockEntity) {
 | |
| 	}
 | |
| 
 | |
| 	public void openJigsawBlock(JigsawBlockEntity jigsawBlockEntity) {
 | |
| 	}
 | |
| 
 | |
| 	public void openHorseInventory(AbstractHorse horse, Container inventory) {
 | |
| 	}
 | |
| 
 | |
| 	public OptionalInt openMenu(@Nullable MenuProvider menu) {
 | |
| 		return OptionalInt.empty();
 | |
| 	}
 | |
| 
 | |
| 	public void openDialog(Holder<Dialog> dialog) {
 | |
| 	}
 | |
| 
 | |
| 	public void sendMerchantOffers(int containerId, MerchantOffers offers, int villagerLevel, int villagerXp, boolean showProgress, boolean canRestock) {
 | |
| 	}
 | |
| 
 | |
| 	public void openItemGui(ItemStack stack, InteractionHand hand) {
 | |
| 	}
 | |
| 
 | |
| 	public InteractionResult interactOn(Entity entityToInteractOn, InteractionHand hand) {
 | |
| 		if (this.isSpectator()) {
 | |
| 			if (entityToInteractOn instanceof MenuProvider) {
 | |
| 				this.openMenu((MenuProvider)entityToInteractOn);
 | |
| 			}
 | |
| 
 | |
| 			return InteractionResult.PASS;
 | |
| 		} else {
 | |
| 			ItemStack itemStack = this.getItemInHand(hand);
 | |
| 			ItemStack itemStack2 = itemStack.copy();
 | |
| 			InteractionResult interactionResult = entityToInteractOn.interact(this, hand);
 | |
| 			if (interactionResult.consumesAction()) {
 | |
| 				if (this.hasInfiniteMaterials() && itemStack == this.getItemInHand(hand) && itemStack.getCount() < itemStack2.getCount()) {
 | |
| 					itemStack.setCount(itemStack2.getCount());
 | |
| 				}
 | |
| 
 | |
| 				return interactionResult;
 | |
| 			} else {
 | |
| 				if (!itemStack.isEmpty() && entityToInteractOn instanceof LivingEntity) {
 | |
| 					if (this.hasInfiniteMaterials()) {
 | |
| 						itemStack = itemStack2;
 | |
| 					}
 | |
| 
 | |
| 					InteractionResult interactionResult2 = itemStack.interactLivingEntity(this, (LivingEntity)entityToInteractOn, hand);
 | |
| 					if (interactionResult2.consumesAction()) {
 | |
| 						this.level().gameEvent(GameEvent.ENTITY_INTERACT, entityToInteractOn.position(), GameEvent.Context.of(this));
 | |
| 						if (itemStack.isEmpty() && !this.hasInfiniteMaterials()) {
 | |
| 							this.setItemInHand(hand, ItemStack.EMPTY);
 | |
| 						}
 | |
| 
 | |
| 						return interactionResult2;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return InteractionResult.PASS;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void removeVehicle() {
 | |
| 		super.removeVehicle();
 | |
| 		this.boardingCooldown = 0;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean isImmobile() {
 | |
| 		return super.isImmobile() || this.isSleeping();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isAffectedByFluids() {
 | |
| 		return !this.abilities.flying;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected Vec3 maybeBackOffFromEdge(Vec3 vec, MoverType mover) {
 | |
| 		float f = this.maxUpStep();
 | |
| 		if (!this.abilities.flying
 | |
| 			&& !(vec.y > 0.0)
 | |
| 			&& (mover == MoverType.SELF || mover == MoverType.PLAYER)
 | |
| 			&& this.isStayingOnGroundSurface()
 | |
| 			&& this.isAboveGround(f)) {
 | |
| 			double d = vec.x;
 | |
| 			double e = vec.z;
 | |
| 			double g = 0.05;
 | |
| 			double h = Math.signum(d) * 0.05;
 | |
| 
 | |
| 			double i;
 | |
| 			for (i = Math.signum(e) * 0.05; d != 0.0 && this.canFallAtLeast(d, 0.0, f); d -= h) {
 | |
| 				if (Math.abs(d) <= 0.05) {
 | |
| 					d = 0.0;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			while (e != 0.0 && this.canFallAtLeast(0.0, e, f)) {
 | |
| 				if (Math.abs(e) <= 0.05) {
 | |
| 					e = 0.0;
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				e -= i;
 | |
| 			}
 | |
| 
 | |
| 			while (d != 0.0 && e != 0.0 && this.canFallAtLeast(d, e, f)) {
 | |
| 				if (Math.abs(d) <= 0.05) {
 | |
| 					d = 0.0;
 | |
| 				} else {
 | |
| 					d -= h;
 | |
| 				}
 | |
| 
 | |
| 				if (Math.abs(e) <= 0.05) {
 | |
| 					e = 0.0;
 | |
| 				} else {
 | |
| 					e -= i;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return new Vec3(d, vec.y, e);
 | |
| 		} else {
 | |
| 			return vec;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private boolean isAboveGround(float maxUpStep) {
 | |
| 		return this.onGround() || this.fallDistance < maxUpStep && !this.canFallAtLeast(0.0, 0.0, maxUpStep - this.fallDistance);
 | |
| 	}
 | |
| 
 | |
| 	private boolean canFallAtLeast(double x, double z, double distance) {
 | |
| 		AABB aABB = this.getBoundingBox();
 | |
| 		return this.level()
 | |
| 			.noCollision(
 | |
| 				this, new AABB(aABB.minX + 1.0E-7 + x, aABB.minY - distance - 1.0E-7, aABB.minZ + 1.0E-7 + z, aABB.maxX - 1.0E-7 + x, aABB.minY, aABB.maxZ - 1.0E-7 + z)
 | |
| 			);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Attacks for the player the targeted entity with the currently equipped item.  The equipped item has hitEntity called on it. Args: targetEntity
 | |
| 	 */
 | |
| 	public void attack(Entity target) {
 | |
| 		if (target.isAttackable()) {
 | |
| 			if (!target.skipAttackInteraction(this)) {
 | |
| 				float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE);
 | |
| 				ItemStack itemStack = this.getWeaponItem();
 | |
| 				DamageSource damageSource = (DamageSource)Optional.ofNullable(itemStack.getItem().getDamageSource(this)).orElse(this.damageSources().playerAttack(this));
 | |
| 				float g = this.getEnchantedDamage(target, f, damageSource) - f;
 | |
| 				float h = this.getAttackStrengthScale(0.5F);
 | |
| 				f *= 0.2F + h * h * 0.8F;
 | |
| 				g *= h;
 | |
| 				this.resetAttackStrengthTicker();
 | |
| 				if (target.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE)
 | |
| 					&& target instanceof Projectile projectile
 | |
| 					&& projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this, this, true)) {
 | |
| 					this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource());
 | |
| 				} else {
 | |
| 					if (f > 0.0F || g > 0.0F) {
 | |
| 						boolean bl = h > 0.9F;
 | |
| 						boolean bl2;
 | |
| 						if (this.isSprinting() && bl) {
 | |
| 							this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_KNOCKBACK, this.getSoundSource(), 1.0F, 1.0F);
 | |
| 							bl2 = true;
 | |
| 						} else {
 | |
| 							bl2 = false;
 | |
| 						}
 | |
| 
 | |
| 						f += itemStack.getItem().getAttackDamageBonus(target, f, damageSource);
 | |
| 						boolean bl3 = bl
 | |
| 							&& this.fallDistance > 0.0
 | |
| 							&& !this.onGround()
 | |
| 							&& !this.onClimbable()
 | |
| 							&& !this.isInWater()
 | |
| 							&& !this.hasEffect(MobEffects.BLINDNESS)
 | |
| 							&& !this.isPassenger()
 | |
| 							&& target instanceof LivingEntity
 | |
| 							&& !this.isSprinting();
 | |
| 						if (bl3) {
 | |
| 							f *= 1.5F;
 | |
| 						}
 | |
| 
 | |
| 						float i = f + g;
 | |
| 						boolean bl4 = false;
 | |
| 						if (bl && !bl3 && !bl2 && this.onGround()) {
 | |
| 							double d = this.getKnownMovement().horizontalDistanceSqr();
 | |
| 							double e = this.getSpeed() * 2.5;
 | |
| 							if (d < Mth.square(e) && this.getItemInHand(InteractionHand.MAIN_HAND).is(ItemTags.SWORDS)) {
 | |
| 								bl4 = true;
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						float j = 0.0F;
 | |
| 						if (target instanceof LivingEntity livingEntity) {
 | |
| 							j = livingEntity.getHealth();
 | |
| 						}
 | |
| 
 | |
| 						Vec3 vec3 = target.getDeltaMovement();
 | |
| 						boolean bl5 = target.hurtOrSimulate(damageSource, i);
 | |
| 						if (bl5) {
 | |
| 							float k = this.getKnockback(target, damageSource) + (bl2 ? 1.0F : 0.0F);
 | |
| 							if (k > 0.0F) {
 | |
| 								if (target instanceof LivingEntity livingEntity2) {
 | |
| 									livingEntity2.knockback(k * 0.5F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)));
 | |
| 								} else {
 | |
| 									target.push(-Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)) * k * 0.5F, 0.1, Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)) * k * 0.5F);
 | |
| 								}
 | |
| 
 | |
| 								this.setDeltaMovement(this.getDeltaMovement().multiply(0.6, 1.0, 0.6));
 | |
| 								this.setSprinting(false);
 | |
| 							}
 | |
| 
 | |
| 							if (bl4) {
 | |
| 								float l = 1.0F + (float)this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * f;
 | |
| 
 | |
| 								for (LivingEntity livingEntity3 : this.level().getEntitiesOfClass(LivingEntity.class, target.getBoundingBox().inflate(1.0, 0.25, 1.0))) {
 | |
| 									if (livingEntity3 != this
 | |
| 										&& livingEntity3 != target
 | |
| 										&& !this.isAlliedTo(livingEntity3)
 | |
| 										&& !(livingEntity3 instanceof ArmorStand armorStand && armorStand.isMarker())
 | |
| 										&& this.distanceToSqr(livingEntity3) < 9.0) {
 | |
| 										float m = this.getEnchantedDamage(livingEntity3, l, damageSource) * h;
 | |
| 										if (this.level() instanceof ServerLevel serverLevel && livingEntity3.hurtServer(serverLevel, damageSource, m)) {
 | |
| 											livingEntity3.knockback(0.4F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)));
 | |
| 											EnchantmentHelper.doPostAttackEffects(serverLevel, livingEntity3, damageSource);
 | |
| 										}
 | |
| 									}
 | |
| 								}
 | |
| 
 | |
| 								this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_SWEEP, this.getSoundSource(), 1.0F, 1.0F);
 | |
| 								this.sweepAttack();
 | |
| 							}
 | |
| 
 | |
| 							if (target instanceof ServerPlayer && target.hurtMarked) {
 | |
| 								((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target));
 | |
| 								target.hurtMarked = false;
 | |
| 								target.setDeltaMovement(vec3);
 | |
| 							}
 | |
| 
 | |
| 							if (bl3) {
 | |
| 								this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_CRIT, this.getSoundSource(), 1.0F, 1.0F);
 | |
| 								this.crit(target);
 | |
| 							}
 | |
| 
 | |
| 							if (!bl3 && !bl4) {
 | |
| 								if (bl) {
 | |
| 									this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_STRONG, this.getSoundSource(), 1.0F, 1.0F);
 | |
| 								} else {
 | |
| 									this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_WEAK, this.getSoundSource(), 1.0F, 1.0F);
 | |
| 								}
 | |
| 							}
 | |
| 
 | |
| 							if (g > 0.0F) {
 | |
| 								this.magicCrit(target);
 | |
| 							}
 | |
| 
 | |
| 							this.setLastHurtMob(target);
 | |
| 							Entity entity = target;
 | |
| 							if (target instanceof EnderDragonPart) {
 | |
| 								entity = ((EnderDragonPart)target).parentMob;
 | |
| 							}
 | |
| 
 | |
| 							boolean bl6 = false;
 | |
| 							if (this.level() instanceof ServerLevel serverLevel2) {
 | |
| 								if (entity instanceof LivingEntity livingEntity3x) {
 | |
| 									bl6 = itemStack.hurtEnemy(livingEntity3x, this);
 | |
| 								}
 | |
| 
 | |
| 								EnchantmentHelper.doPostAttackEffects(serverLevel2, target, damageSource);
 | |
| 							}
 | |
| 
 | |
| 							if (!this.level().isClientSide && !itemStack.isEmpty() && entity instanceof LivingEntity) {
 | |
| 								if (bl6) {
 | |
| 									itemStack.postHurtEnemy((LivingEntity)entity, this);
 | |
| 								}
 | |
| 
 | |
| 								if (itemStack.isEmpty()) {
 | |
| 									if (itemStack == this.getMainHandItem()) {
 | |
| 										this.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY);
 | |
| 									} else {
 | |
| 										this.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY);
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 
 | |
| 							if (target instanceof LivingEntity) {
 | |
| 								float n = j - ((LivingEntity)target).getHealth();
 | |
| 								this.awardStat(Stats.DAMAGE_DEALT, Math.round(n * 10.0F));
 | |
| 								if (this.level() instanceof ServerLevel && n > 2.0F) {
 | |
| 									int o = (int)(n * 0.5);
 | |
| 									((ServerLevel)this.level()).sendParticles(ParticleTypes.DAMAGE_INDICATOR, target.getX(), target.getY(0.5), target.getZ(), o, 0.1, 0.0, 0.1, 0.2);
 | |
| 								}
 | |
| 							}
 | |
| 
 | |
| 							this.causeFoodExhaustion(0.1F);
 | |
| 						} else {
 | |
| 							this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected float getEnchantedDamage(Entity entity, float damage, DamageSource damageSource) {
 | |
| 		return damage;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void doAutoAttackOnTouch(LivingEntity target) {
 | |
| 		this.attack(target);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Called when the entity is dealt a critical hit.
 | |
| 	 */
 | |
| 	public void crit(Entity entityHit) {
 | |
| 	}
 | |
| 
 | |
| 	public void magicCrit(Entity entityHit) {
 | |
| 	}
 | |
| 
 | |
| 	public void sweepAttack() {
 | |
| 		double d = -Mth.sin(this.getYRot() * (float) (Math.PI / 180.0));
 | |
| 		double e = Mth.cos(this.getYRot() * (float) (Math.PI / 180.0));
 | |
| 		if (this.level() instanceof ServerLevel) {
 | |
| 			((ServerLevel)this.level()).sendParticles(ParticleTypes.SWEEP_ATTACK, this.getX() + d, this.getY(0.5), this.getZ() + e, 0, d, 0.0, e, 0.0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void respawn() {
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void remove(Entity.RemovalReason reason) {
 | |
| 		super.remove(reason);
 | |
| 		this.inventoryMenu.removed(this);
 | |
| 		if (this.containerMenu != null && this.hasContainerOpen()) {
 | |
| 			this.doCloseContainer();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isClientAuthoritative() {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean isLocalClientAuthoritative() {
 | |
| 		return this.isLocalPlayer();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns whether this is a {@link net.minecraft.client.player.LocalPlayer}.
 | |
| 	 */
 | |
| 	public boolean isLocalPlayer() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canSimulateMovement() {
 | |
| 		return !this.level().isClientSide || this.isLocalPlayer();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isEffectiveAi() {
 | |
| 		return !this.level().isClientSide || this.isLocalPlayer();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the GameProfile for this player
 | |
| 	 */
 | |
| 	public GameProfile getGameProfile() {
 | |
| 		return this.gameProfile;
 | |
| 	}
 | |
| 
 | |
| 	public Inventory getInventory() {
 | |
| 		return this.inventory;
 | |
| 	}
 | |
| 
 | |
| 	public Abilities getAbilities() {
 | |
| 		return this.abilities;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean hasInfiniteMaterials() {
 | |
| 		return this.abilities.instabuild;
 | |
| 	}
 | |
| 
 | |
| 	public boolean preventsBlockDrops() {
 | |
| 		return this.abilities.instabuild;
 | |
| 	}
 | |
| 
 | |
| 	public void updateTutorialInventoryAction(ItemStack carried, ItemStack clicked, ClickAction action) {
 | |
| 	}
 | |
| 
 | |
| 	public boolean hasContainerOpen() {
 | |
| 		return this.containerMenu != this.inventoryMenu;
 | |
| 	}
 | |
| 
 | |
| 	public boolean canDropItems() {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	public Either<Player.BedSleepingProblem, Unit> startSleepInBed(BlockPos bedPos) {
 | |
| 		this.startSleeping(bedPos);
 | |
| 		this.sleepCounter = 0;
 | |
| 		return Either.right(Unit.INSTANCE);
 | |
| 	}
 | |
| 
 | |
| 	public void stopSleepInBed(boolean wakeImmediately, boolean updateLevelForSleepingPlayers) {
 | |
| 		super.stopSleeping();
 | |
| 		if (this.level() instanceof ServerLevel && updateLevelForSleepingPlayers) {
 | |
| 			((ServerLevel)this.level()).updateSleepingPlayerList();
 | |
| 		}
 | |
| 
 | |
| 		this.sleepCounter = wakeImmediately ? 0 : 100;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void stopSleeping() {
 | |
| 		this.stopSleepInBed(true, true);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns whether the player is asleep and the screen has fully faded.
 | |
| 	 */
 | |
| 	public boolean isSleepingLongEnough() {
 | |
| 		return this.isSleeping() && this.sleepCounter >= 100;
 | |
| 	}
 | |
| 
 | |
| 	public int getSleepTimer() {
 | |
| 		return this.sleepCounter;
 | |
| 	}
 | |
| 
 | |
| 	public void displayClientMessage(Component chatComponent, boolean actionBar) {
 | |
| 	}
 | |
| 
 | |
| 	public void awardStat(ResourceLocation statKey) {
 | |
| 		this.awardStat(Stats.CUSTOM.get(statKey));
 | |
| 	}
 | |
| 
 | |
| 	public void awardStat(ResourceLocation stat, int increment) {
 | |
| 		this.awardStat(Stats.CUSTOM.get(stat), increment);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Add a stat once
 | |
| 	 */
 | |
| 	public void awardStat(Stat<?> stat) {
 | |
| 		this.awardStat(stat, 1);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Adds a value to a statistic field.
 | |
| 	 */
 | |
| 	public void awardStat(Stat<?> stat, int increment) {
 | |
| 	}
 | |
| 
 | |
| 	public void resetStat(Stat<?> stat) {
 | |
| 	}
 | |
| 
 | |
| 	public int awardRecipes(Collection<RecipeHolder<?>> recipes) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	public void triggerRecipeCrafted(RecipeHolder<?> recipe, List<ItemStack> items) {
 | |
| 	}
 | |
| 
 | |
| 	public void awardRecipesByKey(List<ResourceKey<Recipe<?>>> recipes) {
 | |
| 	}
 | |
| 
 | |
| 	public int resetRecipes(Collection<RecipeHolder<?>> recipes) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void travel(Vec3 travelVector) {
 | |
| 		if (this.isPassenger()) {
 | |
| 			super.travel(travelVector);
 | |
| 		} else {
 | |
| 			if (this.isSwimming()) {
 | |
| 				double d = this.getLookAngle().y;
 | |
| 				double e = d < -0.2 ? 0.085 : 0.06;
 | |
| 				if (d <= 0.0 || this.jumping || !this.level().getFluidState(BlockPos.containing(this.getX(), this.getY() + 1.0 - 0.1, this.getZ())).isEmpty()) {
 | |
| 					Vec3 vec3 = this.getDeltaMovement();
 | |
| 					this.setDeltaMovement(vec3.add(0.0, (d - vec3.y) * e, 0.0));
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (this.getAbilities().flying) {
 | |
| 				double d = this.getDeltaMovement().y;
 | |
| 				super.travel(travelVector);
 | |
| 				this.setDeltaMovement(this.getDeltaMovement().with(Direction.Axis.Y, d * 0.6));
 | |
| 			} else {
 | |
| 				super.travel(travelVector);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean canGlide() {
 | |
| 		return !this.abilities.flying && super.canGlide();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void updateSwimming() {
 | |
| 		if (this.abilities.flying) {
 | |
| 			this.setSwimming(false);
 | |
| 		} else {
 | |
| 			super.updateSwimming();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected boolean freeAt(BlockPos pos) {
 | |
| 		return !this.level().getBlockState(pos).isSuffocating(this.level(), pos);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public float getSpeed() {
 | |
| 		return (float)this.getAttributeValue(Attributes.MOVEMENT_SPEED);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) {
 | |
| 		if (this.abilities.mayfly) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			if (fallDistance >= 2.0) {
 | |
| 				this.awardStat(Stats.FALL_ONE_CM, (int)Math.round(fallDistance * 100.0));
 | |
| 			}
 | |
| 
 | |
| 			boolean bl = this.currentImpulseImpactPos != null && this.ignoreFallDamageFromCurrentImpulse;
 | |
| 			double d;
 | |
| 			if (bl) {
 | |
| 				d = Math.min(fallDistance, this.currentImpulseImpactPos.y - this.getY());
 | |
| 				boolean bl2 = d <= 0.0;
 | |
| 				if (bl2) {
 | |
| 					this.resetCurrentImpulseContext();
 | |
| 				} else {
 | |
| 					this.tryResetCurrentImpulseContext();
 | |
| 				}
 | |
| 			} else {
 | |
| 				d = fallDistance;
 | |
| 			}
 | |
| 
 | |
| 			if (d > 0.0 && super.causeFallDamage(d, damageMultiplier, damageSource)) {
 | |
| 				this.resetCurrentImpulseContext();
 | |
| 				return true;
 | |
| 			} else {
 | |
| 				this.propagateFallToPassengers(fallDistance, damageMultiplier, damageSource);
 | |
| 				return false;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean tryToStartFallFlying() {
 | |
| 		if (!this.isFallFlying() && this.canGlide() && !this.isInWater()) {
 | |
| 			this.startFallFlying();
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void startFallFlying() {
 | |
| 		this.setSharedFlag(7, true);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void doWaterSplashEffect() {
 | |
| 		if (!this.isSpectator()) {
 | |
| 			super.doWaterSplashEffect();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void playStepSound(BlockPos pos, BlockState state) {
 | |
| 		if (this.isInWater()) {
 | |
| 			this.waterSwimSound();
 | |
| 			this.playMuffledStepSound(state);
 | |
| 		} else {
 | |
| 			BlockPos blockPos = this.getPrimaryStepSoundBlockPos(pos);
 | |
| 			if (!pos.equals(blockPos)) {
 | |
| 				BlockState blockState = this.level().getBlockState(blockPos);
 | |
| 				if (blockState.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS)) {
 | |
| 					this.playCombinationStepSounds(blockState, state);
 | |
| 				} else {
 | |
| 					super.playStepSound(blockPos, blockState);
 | |
| 				}
 | |
| 			} else {
 | |
| 				super.playStepSound(pos, state);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public LivingEntity.Fallsounds getFallSounds() {
 | |
| 		return new LivingEntity.Fallsounds(SoundEvents.PLAYER_SMALL_FALL, SoundEvents.PLAYER_BIG_FALL);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean killedEntity(ServerLevel level, LivingEntity entity) {
 | |
| 		this.awardStat(Stats.ENTITY_KILLED.get(entity.getType()));
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void makeStuckInBlock(BlockState state, Vec3 motionMultiplier) {
 | |
| 		if (!this.abilities.flying) {
 | |
| 			super.makeStuckInBlock(state, motionMultiplier);
 | |
| 		}
 | |
| 
 | |
| 		this.tryResetCurrentImpulseContext();
 | |
| 	}
 | |
| 
 | |
| 	public void giveExperiencePoints(int xpPoints) {
 | |
| 		this.increaseScore(xpPoints);
 | |
| 		this.experienceProgress = this.experienceProgress + (float)xpPoints / this.getXpNeededForNextLevel();
 | |
| 		this.totalExperience = Mth.clamp(this.totalExperience + xpPoints, 0, Integer.MAX_VALUE);
 | |
| 
 | |
| 		while (this.experienceProgress < 0.0F) {
 | |
| 			float f = this.experienceProgress * this.getXpNeededForNextLevel();
 | |
| 			if (this.experienceLevel > 0) {
 | |
| 				this.giveExperienceLevels(-1);
 | |
| 				this.experienceProgress = 1.0F + f / this.getXpNeededForNextLevel();
 | |
| 			} else {
 | |
| 				this.giveExperienceLevels(-1);
 | |
| 				this.experienceProgress = 0.0F;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		while (this.experienceProgress >= 1.0F) {
 | |
| 			this.experienceProgress = (this.experienceProgress - 1.0F) * this.getXpNeededForNextLevel();
 | |
| 			this.giveExperienceLevels(1);
 | |
| 			this.experienceProgress = this.experienceProgress / this.getXpNeededForNextLevel();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public int getEnchantmentSeed() {
 | |
| 		return this.enchantmentSeed;
 | |
| 	}
 | |
| 
 | |
| 	public void onEnchantmentPerformed(ItemStack enchantedItem, int levelCost) {
 | |
| 		this.experienceLevel -= levelCost;
 | |
| 		if (this.experienceLevel < 0) {
 | |
| 			this.experienceLevel = 0;
 | |
| 			this.experienceProgress = 0.0F;
 | |
| 			this.totalExperience = 0;
 | |
| 		}
 | |
| 
 | |
| 		this.enchantmentSeed = this.random.nextInt();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Add experience levels to this player.
 | |
| 	 */
 | |
| 	public void giveExperienceLevels(int levels) {
 | |
| 		this.experienceLevel = IntMath.saturatedAdd(this.experienceLevel, levels);
 | |
| 		if (this.experienceLevel < 0) {
 | |
| 			this.experienceLevel = 0;
 | |
| 			this.experienceProgress = 0.0F;
 | |
| 			this.totalExperience = 0;
 | |
| 		}
 | |
| 
 | |
| 		if (levels > 0 && this.experienceLevel % 5 == 0 && this.lastLevelUpTime < this.tickCount - 100.0F) {
 | |
| 			float f = this.experienceLevel > 30 ? 1.0F : this.experienceLevel / 30.0F;
 | |
| 			this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), f * 0.75F, 1.0F);
 | |
| 			this.lastLevelUpTime = this.tickCount;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * This method returns the cap amount of experience that the experience bar can hold. With each level, the experience cap on the player's experience bar is raised by 10.
 | |
| 	 */
 | |
| 	public int getXpNeededForNextLevel() {
 | |
| 		if (this.experienceLevel >= 30) {
 | |
| 			return 112 + (this.experienceLevel - 30) * 9;
 | |
| 		} else {
 | |
| 			return this.experienceLevel >= 15 ? 37 + (this.experienceLevel - 15) * 5 : 7 + this.experienceLevel * 2;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Increases exhaustion level by the supplied amount.
 | |
| 	 */
 | |
| 	public void causeFoodExhaustion(float exhaustion) {
 | |
| 		if (!this.abilities.invulnerable) {
 | |
| 			if (!this.level().isClientSide) {
 | |
| 				this.foodData.addExhaustion(exhaustion);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public Optional<WardenSpawnTracker> getWardenSpawnTracker() {
 | |
| 		return Optional.empty();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the player's FoodStats object.
 | |
| 	 */
 | |
| 	public FoodData getFoodData() {
 | |
| 		return this.foodData;
 | |
| 	}
 | |
| 
 | |
| 	public boolean canEat(boolean canAlwaysEat) {
 | |
| 		return this.abilities.invulnerable || canAlwaysEat || this.foodData.needsFood();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Checks if the player's health is not full and not zero.
 | |
| 	 */
 | |
| 	public boolean isHurt() {
 | |
| 		return this.getHealth() > 0.0F && this.getHealth() < this.getMaxHealth();
 | |
| 	}
 | |
| 
 | |
| 	public boolean mayBuild() {
 | |
| 		return this.abilities.mayBuild;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns whether this player can modify the block at a certain location with the given stack.
 | |
| 	 * <p>
 | |
| 	 * The position being queried is {@code pos.offset(facing.getOpposite())}.
 | |
| 	 * 
 | |
| 	 * @return Whether this player may modify the queried location in the current world
 | |
| 	 * @see ItemStack#canPlaceOn(Block)
 | |
| 	 * @see ItemStack#canEditBlocks()
 | |
| 	 * @see PlayerCapabilities#allowEdit
 | |
| 	 */
 | |
| 	public boolean mayUseItemAt(BlockPos pos, Direction facing, ItemStack stack) {
 | |
| 		if (this.abilities.mayBuild) {
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			BlockPos blockPos = pos.relative(facing.getOpposite());
 | |
| 			BlockInWorld blockInWorld = new BlockInWorld(this.level(), blockPos, false);
 | |
| 			return stack.canPlaceOnBlockInAdventureMode(blockInWorld);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected int getBaseExperienceReward(ServerLevel level) {
 | |
| 		return !level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean isAlwaysExperienceDropper() {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean shouldShowName() {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected Entity.MovementEmission getMovementEmission() {
 | |
| 		return this.abilities.flying || this.onGround() && this.isDiscrete() ? Entity.MovementEmission.NONE : Entity.MovementEmission.ALL;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sends the player's abilities to the server (if there is one).
 | |
| 	 */
 | |
| 	public void onUpdateAbilities() {
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Component getName() {
 | |
| 		return Component.literal(this.gameProfile.getName());
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the InventoryEnderChest of this player.
 | |
| 	 */
 | |
| 	public PlayerEnderChestContainer getEnderChestInventory() {
 | |
| 		return this.enderChestInventory;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean doesEmitEquipEvent(EquipmentSlot slot) {
 | |
| 		return slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR;
 | |
| 	}
 | |
| 
 | |
| 	public boolean addItem(ItemStack stack) {
 | |
| 		return this.inventory.add(stack);
 | |
| 	}
 | |
| 
 | |
| 	public boolean setEntityOnShoulder(CompoundTag entityCompound) {
 | |
| 		if (this.isPassenger() || !this.onGround() || this.isInWater() || this.isInPowderSnow) {
 | |
| 			return false;
 | |
| 		} else if (this.getShoulderEntityLeft().isEmpty()) {
 | |
| 			this.setShoulderEntityLeft(entityCompound);
 | |
| 			this.timeEntitySatOnShoulder = this.level().getGameTime();
 | |
| 			return true;
 | |
| 		} else if (this.getShoulderEntityRight().isEmpty()) {
 | |
| 			this.setShoulderEntityRight(entityCompound);
 | |
| 			this.timeEntitySatOnShoulder = this.level().getGameTime();
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void removeEntitiesOnShoulder() {
 | |
| 		if (this.timeEntitySatOnShoulder + 20L < this.level().getGameTime()) {
 | |
| 			this.respawnEntityOnShoulder(this.getShoulderEntityLeft());
 | |
| 			this.setShoulderEntityLeft(new CompoundTag());
 | |
| 			this.respawnEntityOnShoulder(this.getShoulderEntityRight());
 | |
| 			this.setShoulderEntityRight(new CompoundTag());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void respawnEntityOnShoulder(CompoundTag entityCompound) {
 | |
| 		if (this.level() instanceof ServerLevel serverLevel && !entityCompound.isEmpty()) {
 | |
| 			try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) {
 | |
| 				EntityType.create(
 | |
| 						TagValueInput.create(scopedCollector.forChild(() -> ".shoulder"), serverLevel.registryAccess(), entityCompound), serverLevel, EntitySpawnReason.LOAD
 | |
| 					)
 | |
| 					.ifPresent(entity -> {
 | |
| 						if (entity instanceof TamableAnimal tamableAnimal) {
 | |
| 							tamableAnimal.setOwner(this);
 | |
| 						}
 | |
| 
 | |
| 						entity.setPos(this.getX(), this.getY() + 0.7F, this.getZ());
 | |
| 						serverLevel.addWithUUID(entity);
 | |
| 					});
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public abstract GameType gameMode();
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isSpectator() {
 | |
| 		return this.gameMode() == GameType.SPECTATOR;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canBeHitByProjectile() {
 | |
| 		return !this.isSpectator() && super.canBeHitByProjectile();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isSwimming() {
 | |
| 		return !this.abilities.flying && !this.isSpectator() && super.isSwimming();
 | |
| 	}
 | |
| 
 | |
| 	public boolean isCreative() {
 | |
| 		return this.gameMode() == GameType.CREATIVE;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isPushedByFluid() {
 | |
| 		return !this.abilities.flying;
 | |
| 	}
 | |
| 
 | |
| 	public Scoreboard getScoreboard() {
 | |
| 		return this.level().getScoreboard();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Component getDisplayName() {
 | |
| 		MutableComponent mutableComponent = PlayerTeam.formatNameForTeam(this.getTeam(), this.getName());
 | |
| 		return this.decorateDisplayNameComponent(mutableComponent);
 | |
| 	}
 | |
| 
 | |
| 	private MutableComponent decorateDisplayNameComponent(MutableComponent displayName) {
 | |
| 		String string = this.getGameProfile().getName();
 | |
| 		return displayName.withStyle(
 | |
| 			style -> style.withClickEvent(new ClickEvent.SuggestCommand("/tell " + string + " ")).withHoverEvent(this.createHoverEvent()).withInsertion(string)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public String getScoreboardName() {
 | |
| 		return this.getGameProfile().getName();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void internalSetAbsorptionAmount(float absorptionAmount) {
 | |
| 		this.getEntityData().set(DATA_PLAYER_ABSORPTION_ID, absorptionAmount);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public float getAbsorptionAmount() {
 | |
| 		return this.getEntityData().get(DATA_PLAYER_ABSORPTION_ID);
 | |
| 	}
 | |
| 
 | |
| 	public boolean isModelPartShown(PlayerModelPart part) {
 | |
| 		return (this.getEntityData().get(DATA_PLAYER_MODE_CUSTOMISATION) & part.getMask()) == part.getMask();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public SlotAccess getSlot(int slot) {
 | |
| 		if (slot == 499) {
 | |
| 			return new SlotAccess() {
 | |
| 				@Override
 | |
| 				public ItemStack get() {
 | |
| 					return Player.this.containerMenu.getCarried();
 | |
| 				}
 | |
| 
 | |
| 				@Override
 | |
| 				public boolean set(ItemStack carried) {
 | |
| 					Player.this.containerMenu.setCarried(carried);
 | |
| 					return true;
 | |
| 				}
 | |
| 			};
 | |
| 		} else {
 | |
| 			final int i = slot - 500;
 | |
| 			if (i >= 0 && i < 4) {
 | |
| 				return new SlotAccess() {
 | |
| 					@Override
 | |
| 					public ItemStack get() {
 | |
| 						return Player.this.inventoryMenu.getCraftSlots().getItem(i);
 | |
| 					}
 | |
| 
 | |
| 					@Override
 | |
| 					public boolean set(ItemStack carried) {
 | |
| 						Player.this.inventoryMenu.getCraftSlots().setItem(i, carried);
 | |
| 						Player.this.inventoryMenu.slotsChanged(Player.this.inventory);
 | |
| 						return true;
 | |
| 					}
 | |
| 				};
 | |
| 			} else if (slot >= 0 && slot < this.inventory.getNonEquipmentItems().size()) {
 | |
| 				return SlotAccess.forContainer(this.inventory, slot);
 | |
| 			} else {
 | |
| 				int j = slot - 200;
 | |
| 				return j >= 0 && j < this.enderChestInventory.getContainerSize() ? SlotAccess.forContainer(this.enderChestInventory, j) : super.getSlot(slot);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Whether the "reducedDebugInfo" option is active for this player.
 | |
| 	 */
 | |
| 	public boolean isReducedDebugInfo() {
 | |
| 		return this.reducedDebugInfo;
 | |
| 	}
 | |
| 
 | |
| 	public void setReducedDebugInfo(boolean reducedDebugInfo) {
 | |
| 		this.reducedDebugInfo = reducedDebugInfo;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void setRemainingFireTicks(int remainingFireTicks) {
 | |
| 		super.setRemainingFireTicks(this.abilities.invulnerable ? Math.min(remainingFireTicks, 1) : remainingFireTicks);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public HumanoidArm getMainArm() {
 | |
| 		return this.entityData.get(DATA_PLAYER_MAIN_HAND) == 0 ? HumanoidArm.LEFT : HumanoidArm.RIGHT;
 | |
| 	}
 | |
| 
 | |
| 	public void setMainArm(HumanoidArm hand) {
 | |
| 		this.entityData.set(DATA_PLAYER_MAIN_HAND, (byte)(hand == HumanoidArm.LEFT ? 0 : 1));
 | |
| 	}
 | |
| 
 | |
| 	public CompoundTag getShoulderEntityLeft() {
 | |
| 		return this.entityData.get(DATA_SHOULDER_LEFT);
 | |
| 	}
 | |
| 
 | |
| 	protected void setShoulderEntityLeft(CompoundTag entityCompound) {
 | |
| 		this.entityData.set(DATA_SHOULDER_LEFT, entityCompound);
 | |
| 	}
 | |
| 
 | |
| 	public CompoundTag getShoulderEntityRight() {
 | |
| 		return this.entityData.get(DATA_SHOULDER_RIGHT);
 | |
| 	}
 | |
| 
 | |
| 	protected void setShoulderEntityRight(CompoundTag entityCompound) {
 | |
| 		this.entityData.set(DATA_SHOULDER_RIGHT, entityCompound);
 | |
| 	}
 | |
| 
 | |
| 	public float getCurrentItemAttackStrengthDelay() {
 | |
| 		return (float)(1.0 / this.getAttributeValue(Attributes.ATTACK_SPEED) * 20.0);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the percentage of attack power available based on the cooldown (zero to one).
 | |
| 	 */
 | |
| 	public float getAttackStrengthScale(float adjustTicks) {
 | |
| 		return Mth.clamp((this.attackStrengthTicker + adjustTicks) / this.getCurrentItemAttackStrengthDelay(), 0.0F, 1.0F);
 | |
| 	}
 | |
| 
 | |
| 	public void resetAttackStrengthTicker() {
 | |
| 		this.attackStrengthTicker = 0;
 | |
| 	}
 | |
| 
 | |
| 	public ItemCooldowns getCooldowns() {
 | |
| 		return this.cooldowns;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected float getBlockSpeedFactor() {
 | |
| 		return !this.abilities.flying && !this.isFallFlying() ? super.getBlockSpeedFactor() : 1.0F;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public float getLuck() {
 | |
| 		return (float)this.getAttributeValue(Attributes.LUCK);
 | |
| 	}
 | |
| 
 | |
| 	public boolean canUseGameMasterBlocks() {
 | |
| 		return this.abilities.instabuild && this.getPermissionLevel() >= 2;
 | |
| 	}
 | |
| 
 | |
| 	public int getPermissionLevel() {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	public boolean hasPermissions(int permissionLevel) {
 | |
| 		return this.getPermissionLevel() >= permissionLevel;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public EntityDimensions getDefaultDimensions(Pose pose) {
 | |
| 		return (EntityDimensions)POSES.getOrDefault(pose, STANDING_DIMENSIONS);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public ImmutableList<Pose> getDismountPoses() {
 | |
| 		return ImmutableList.of(Pose.STANDING, Pose.CROUCHING, Pose.SWIMMING);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public ItemStack getProjectile(ItemStack weaponStack) {
 | |
| 		if (!(weaponStack.getItem() instanceof ProjectileWeaponItem)) {
 | |
| 			return ItemStack.EMPTY;
 | |
| 		} else {
 | |
| 			Predicate<ItemStack> predicate = ((ProjectileWeaponItem)weaponStack.getItem()).getSupportedHeldProjectiles();
 | |
| 			ItemStack itemStack = ProjectileWeaponItem.getHeldProjectile(this, predicate);
 | |
| 			if (!itemStack.isEmpty()) {
 | |
| 				return itemStack;
 | |
| 			} else {
 | |
| 				predicate = ((ProjectileWeaponItem)weaponStack.getItem()).getAllSupportedProjectiles();
 | |
| 
 | |
| 				for (int i = 0; i < this.inventory.getContainerSize(); i++) {
 | |
| 					ItemStack itemStack2 = this.inventory.getItem(i);
 | |
| 					if (predicate.test(itemStack2)) {
 | |
| 						return itemStack2;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return this.hasInfiniteMaterials() ? new ItemStack(Items.ARROW) : ItemStack.EMPTY;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Vec3 getRopeHoldPosition(float partialTicks) {
 | |
| 		double d = 0.22 * (this.getMainArm() == HumanoidArm.RIGHT ? -1.0 : 1.0);
 | |
| 		float f = Mth.lerp(partialTicks * 0.5F, this.getXRot(), this.xRotO) * (float) (Math.PI / 180.0);
 | |
| 		float g = Mth.lerp(partialTicks, this.yBodyRotO, this.yBodyRot) * (float) (Math.PI / 180.0);
 | |
| 		if (this.isFallFlying() || this.isAutoSpinAttack()) {
 | |
| 			Vec3 vec3 = this.getViewVector(partialTicks);
 | |
| 			Vec3 vec32 = this.getDeltaMovement();
 | |
| 			double e = vec32.horizontalDistanceSqr();
 | |
| 			double h = vec3.horizontalDistanceSqr();
 | |
| 			float k;
 | |
| 			if (e > 0.0 && h > 0.0) {
 | |
| 				double i = (vec32.x * vec3.x + vec32.z * vec3.z) / Math.sqrt(e * h);
 | |
| 				double j = vec32.x * vec3.z - vec32.z * vec3.x;
 | |
| 				k = (float)(Math.signum(j) * Math.acos(i));
 | |
| 			} else {
 | |
| 				k = 0.0F;
 | |
| 			}
 | |
| 
 | |
| 			return this.getPosition(partialTicks).add(new Vec3(d, -0.11, 0.85).zRot(-k).xRot(-f).yRot(-g));
 | |
| 		} else if (this.isVisuallySwimming()) {
 | |
| 			return this.getPosition(partialTicks).add(new Vec3(d, 0.2, -0.15).xRot(-f).yRot(-g));
 | |
| 		} else {
 | |
| 			double l = this.getBoundingBox().getYsize() - 1.0;
 | |
| 			double e = this.isCrouching() ? -0.2 : 0.07;
 | |
| 			return this.getPosition(partialTicks).add(new Vec3(d, l, e).yRot(-g));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isAlwaysTicking() {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	public boolean isScoping() {
 | |
| 		return this.isUsingItem() && this.getUseItem().is(Items.SPYGLASS);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean shouldBeSaved() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public Optional<GlobalPos> getLastDeathLocation() {
 | |
| 		return this.lastDeathLocation;
 | |
| 	}
 | |
| 
 | |
| 	public void setLastDeathLocation(Optional<GlobalPos> lastDeathLocation) {
 | |
| 		this.lastDeathLocation = lastDeathLocation;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public float getHurtDir() {
 | |
| 		return this.hurtDir;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void animateHurt(float yaw) {
 | |
| 		super.animateHurt(yaw);
 | |
| 		this.hurtDir = yaw;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canSprint() {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected float getFlyingSpeed() {
 | |
| 		if (this.abilities.flying && !this.isPassenger()) {
 | |
| 			return this.isSprinting() ? this.abilities.getFlyingSpeed() * 2.0F : this.abilities.getFlyingSpeed();
 | |
| 		} else {
 | |
| 			return this.isSprinting() ? 0.025999999F : 0.02F;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean hasClientLoaded() {
 | |
| 		return this.clientLoaded || this.clientLoadedTimeoutTimer <= 0;
 | |
| 	}
 | |
| 
 | |
| 	public void tickClientLoadTimeout() {
 | |
| 		if (!this.clientLoaded) {
 | |
| 			this.clientLoadedTimeoutTimer--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void setClientLoaded(boolean clientLoaded) {
 | |
| 		this.clientLoaded = clientLoaded;
 | |
| 		if (!this.clientLoaded) {
 | |
| 			this.clientLoadedTimeoutTimer = 60;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public double blockInteractionRange() {
 | |
| 		return this.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE);
 | |
| 	}
 | |
| 
 | |
| 	public double entityInteractionRange() {
 | |
| 		return this.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE);
 | |
| 	}
 | |
| 
 | |
| 	public boolean canInteractWithEntity(Entity entity, double distance) {
 | |
| 		return entity.isRemoved() ? false : this.canInteractWithEntity(entity.getBoundingBox(), distance);
 | |
| 	}
 | |
| 
 | |
| 	public boolean canInteractWithEntity(AABB boundingBox, double distance) {
 | |
| 		double d = this.entityInteractionRange() + distance;
 | |
| 		return boundingBox.distanceToSqr(this.getEyePosition()) < d * d;
 | |
| 	}
 | |
| 
 | |
| 	public boolean canInteractWithBlock(BlockPos pos, double distance) {
 | |
| 		double d = this.blockInteractionRange() + distance;
 | |
| 		return new AABB(pos).distanceToSqr(this.getEyePosition()) < d * d;
 | |
| 	}
 | |
| 
 | |
| 	public void setIgnoreFallDamageFromCurrentImpulse(boolean ignoreFallDamageFromCurrentImpulse) {
 | |
| 		this.ignoreFallDamageFromCurrentImpulse = ignoreFallDamageFromCurrentImpulse;
 | |
| 		if (ignoreFallDamageFromCurrentImpulse) {
 | |
| 			this.currentImpulseContextResetGraceTime = 40;
 | |
| 		} else {
 | |
| 			this.currentImpulseContextResetGraceTime = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean isIgnoringFallDamageFromCurrentImpulse() {
 | |
| 		return this.ignoreFallDamageFromCurrentImpulse;
 | |
| 	}
 | |
| 
 | |
| 	public void tryResetCurrentImpulseContext() {
 | |
| 		if (this.currentImpulseContextResetGraceTime == 0) {
 | |
| 			this.resetCurrentImpulseContext();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void resetCurrentImpulseContext() {
 | |
| 		this.currentImpulseContextResetGraceTime = 0;
 | |
| 		this.currentExplosionCause = null;
 | |
| 		this.currentImpulseImpactPos = null;
 | |
| 		this.ignoreFallDamageFromCurrentImpulse = false;
 | |
| 	}
 | |
| 
 | |
| 	public boolean shouldRotateWithMinecart() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean onClimbable() {
 | |
| 		return this.abilities.flying ? false : super.onClimbable();
 | |
| 	}
 | |
| 
 | |
| 	public String debugInfo() {
 | |
| 		return MoreObjects.toStringHelper(this)
 | |
| 			.add("name", this.getName().getString())
 | |
| 			.add("id", this.getId())
 | |
| 			.add("pos", this.position())
 | |
| 			.add("mode", this.gameMode())
 | |
| 			.add("permission", this.getPermissionLevel())
 | |
| 			.toString();
 | |
| 	}
 | |
| 
 | |
| 	public static enum BedSleepingProblem {
 | |
| 		NOT_POSSIBLE_HERE,
 | |
| 		NOT_POSSIBLE_NOW(Component.translatable("block.minecraft.bed.no_sleep")),
 | |
| 		TOO_FAR_AWAY(Component.translatable("block.minecraft.bed.too_far_away")),
 | |
| 		OBSTRUCTED(Component.translatable("block.minecraft.bed.obstructed")),
 | |
| 		OTHER_PROBLEM,
 | |
| 		NOT_SAFE(Component.translatable("block.minecraft.bed.not_safe"));
 | |
| 
 | |
| 		@Nullable
 | |
| 		private final Component message;
 | |
| 
 | |
| 		private BedSleepingProblem() {
 | |
| 			this.message = null;
 | |
| 		}
 | |
| 
 | |
| 		private BedSleepingProblem(final Component message) {
 | |
| 			this.message = message;
 | |
| 		}
 | |
| 
 | |
| 		@Nullable
 | |
| 		public Component getMessage() {
 | |
| 			return this.message;
 | |
| 		}
 | |
| 	}
 | |
| }
 |