1974 lines
75 KiB
Java
1974 lines
75 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.base.MoreObjects
|
|
* com.google.common.collect.ImmutableList
|
|
* com.google.common.collect.Lists
|
|
* com.google.common.math.IntMath
|
|
* com.mojang.authlib.GameProfile
|
|
* com.mojang.datafixers.util.Either
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.world.entity.player;
|
|
|
|
import com.google.common.base.MoreObjects;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.math.IntMath;
|
|
import com.mojang.authlib.GameProfile;
|
|
import com.mojang.datafixers.util.Either;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.OptionalInt;
|
|
import java.util.function.Predicate;
|
|
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.Identifier;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.dialog.Dialog;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.server.permissions.PermissionSet;
|
|
import net.minecraft.server.permissions.Permissions;
|
|
import net.minecraft.server.players.NameAndId;
|
|
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.Unit;
|
|
import net.minecraft.util.Util;
|
|
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.attribute.EnvironmentAttributes;
|
|
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.Avatar;
|
|
import net.minecraft.world.entity.ContainerUser;
|
|
import net.minecraft.world.entity.Entity;
|
|
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.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.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.player.Abilities;
|
|
import net.minecraft.world.entity.player.Inventory;
|
|
import net.minecraft.world.entity.player.PlayerEquipment;
|
|
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.entity.vehicle.MinecartCommandBlock;
|
|
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.GameType;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.entity.CommandBlockEntity;
|
|
import net.minecraft.world.level.block.entity.ContainerOpenersCounter;
|
|
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.gamerules.GameRules;
|
|
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.Team;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public abstract class Player
|
|
extends Avatar
|
|
implements ContainerUser {
|
|
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;
|
|
private static final int CURRENT_IMPULSE_CONTEXT_RESET_GRACE_TIME_TICKS = 40;
|
|
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);
|
|
private static final EntityDataAccessor<OptionalInt> DATA_SHOULDER_PARROT_LEFT = SynchedEntityData.defineId(Player.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT);
|
|
private static final EntityDataAccessor<OptionalInt> DATA_SHOULDER_PARROT_RIGHT = SynchedEntityData.defineId(Player.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT);
|
|
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 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 int takeXpDelay;
|
|
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;
|
|
private final GameProfile gameProfile;
|
|
private boolean reducedDebugInfo;
|
|
private ItemStack lastItemInMainHand = ItemStack.EMPTY;
|
|
private final ItemCooldowns cooldowns = this.createItemCooldowns();
|
|
private Optional<GlobalPos> lastDeathLocation = Optional.empty();
|
|
public @Nullable FishingHook fishing;
|
|
protected float hurtDir;
|
|
public @Nullable Vec3 currentImpulseImpactPos;
|
|
public @Nullable Entity currentExplosionCause;
|
|
private boolean ignoreFallDamageFromCurrentImpulse = false;
|
|
private int currentImpulseContextResetGraceTime = 0;
|
|
|
|
public Player(Level level, GameProfile gameProfile) {
|
|
super((EntityType<? extends LivingEntity>)EntityType.PLAYER, level);
|
|
this.setUUID(gameProfile.id());
|
|
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 gameType) {
|
|
if (!gameType.isBlockPlacingRestricted()) {
|
|
return false;
|
|
}
|
|
if (gameType == GameType.SPECTATOR) {
|
|
return true;
|
|
}
|
|
if (this.mayBuild()) {
|
|
return false;
|
|
}
|
|
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 entityData) {
|
|
super.defineSynchedData(entityData);
|
|
entityData.define(DATA_PLAYER_ABSORPTION_ID, Float.valueOf(0.0f));
|
|
entityData.define(DATA_SCORE_ID, 0);
|
|
entityData.define(DATA_SHOULDER_PARROT_LEFT, OptionalInt.empty());
|
|
entityData.define(DATA_SHOULDER_PARROT_RIGHT, OptionalInt.empty());
|
|
}
|
|
|
|
@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().environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, this.position()).canSleep(this.level())) {
|
|
this.stopSleepInBed(false, true);
|
|
}
|
|
} else if (this.sleepCounter > 0) {
|
|
++this.sleepCounter;
|
|
if (this.sleepCounter >= 110) {
|
|
this.sleepCounter = 0;
|
|
}
|
|
}
|
|
this.updateIsUnderwater();
|
|
super.tick();
|
|
int maxPositionOffset = 29999999;
|
|
double nx = Mth.clamp(this.getX(), -2.9999999E7, 2.9999999E7);
|
|
double nz = Mth.clamp(this.getZ(), -2.9999999E7, 2.9999999E7);
|
|
if (nx != this.getX() || nz != this.getZ()) {
|
|
this.setPos(nx, this.getY(), nz);
|
|
}
|
|
++this.attackStrengthTicker;
|
|
++this.itemSwapTicker;
|
|
ItemStack mainHandItemStack = this.getMainHandItem();
|
|
if (!ItemStack.matches(this.lastItemInMainHand, mainHandItemStack)) {
|
|
if (!ItemStack.isSameItem(this.lastItemInMainHand, mainHandItemStack)) {
|
|
this.resetAttackStrengthTicker();
|
|
}
|
|
this.lastItemInMainHand = mainHandItemStack.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() {
|
|
if (this.isBlocking()) {
|
|
return 15.0f;
|
|
}
|
|
return 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 dragDown, BlockPos pos) {
|
|
if (!this.getAbilities().flying) {
|
|
super.onAboveBubbleColumn(dragDown, pos);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onInsideBubbleColumn(boolean dragDown) {
|
|
if (!this.getAbilities().flying) {
|
|
super.onInsideBubbleColumn(dragDown);
|
|
}
|
|
}
|
|
|
|
private void turtleHelmetTick() {
|
|
this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true));
|
|
}
|
|
|
|
private boolean isEquipped(Item item) {
|
|
for (EquipmentSlot slot : EquipmentSlot.VALUES) {
|
|
ItemStack itemStack = this.getItemBySlot(slot);
|
|
Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE);
|
|
if (!itemStack.is(item) || equippable == null || equippable.slot() != slot) continue;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected ItemCooldowns createItemCooldowns() {
|
|
return new ItemCooldowns();
|
|
}
|
|
|
|
protected void updatePlayerPose() {
|
|
if (!this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.SWIMMING)) {
|
|
return;
|
|
}
|
|
Pose desiredPose = this.getDesiredPose();
|
|
Pose actualPose = this.isSpectator() || this.isPassenger() || this.canPlayerFitWithinBlocksAndEntitiesWhen(desiredPose) ? desiredPose : (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING) ? Pose.CROUCHING : Pose.SWIMMING);
|
|
this.setPose(actualPose);
|
|
}
|
|
|
|
private Pose getDesiredPose() {
|
|
if (this.isSleeping()) {
|
|
return Pose.SLEEPING;
|
|
}
|
|
if (this.isSwimming()) {
|
|
return Pose.SWIMMING;
|
|
}
|
|
if (this.isFallFlying()) {
|
|
return Pose.FALL_FLYING;
|
|
}
|
|
if (this.isAutoSpinAttack()) {
|
|
return Pose.SPIN_ATTACK;
|
|
}
|
|
if (this.isShiftKeyDown() && !this.abilities.flying) {
|
|
return Pose.CROUCHING;
|
|
}
|
|
return Pose.STANDING;
|
|
}
|
|
|
|
protected boolean canPlayerFitWithinBlocksAndEntitiesWhen(Pose newPose) {
|
|
return this.level().noCollision(this, this.getDimensions(newPose).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((Entity)this, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), volume, pitch);
|
|
}
|
|
|
|
public void playNotifySound(SoundEvent sound, SoundSource soundSource, 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.setReducedDebugInfo(false);
|
|
} else if (id == 22) {
|
|
this.setReducedDebugInfo(true);
|
|
} else {
|
|
super.handleEntityEvent(id);
|
|
}
|
|
}
|
|
|
|
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);
|
|
return;
|
|
}
|
|
super.rideTick();
|
|
}
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
if (this.jumpTriggerTime > 0) {
|
|
--this.jumpTriggerTime;
|
|
}
|
|
this.tickRegeneration();
|
|
this.inventory.tick();
|
|
if (this.abilities.flying && !this.isPassenger()) {
|
|
this.resetFallDistance();
|
|
}
|
|
super.aiStep();
|
|
this.updateSwingTime();
|
|
this.yHeadRot = this.getYRot();
|
|
this.setSpeed((float)this.getAttributeValue(Attributes.MOVEMENT_SPEED));
|
|
if (this.getHealth() > 0.0f && !this.isSpectator()) {
|
|
AABB pickupArea = this.isPassenger() && !this.getVehicle().isRemoved() ? this.getBoundingBox().minmax(this.getVehicle().getBoundingBox()).inflate(1.0, 0.0, 1.0) : this.getBoundingBox().inflate(1.0, 0.5, 1.0);
|
|
List<Entity> entities = this.level().getEntities(this, pickupArea);
|
|
ArrayList orbs = Lists.newArrayList();
|
|
for (Entity entity : entities) {
|
|
if (entity.getType() == EntityType.EXPERIENCE_ORB) {
|
|
orbs.add(entity);
|
|
continue;
|
|
}
|
|
if (entity.isRemoved()) continue;
|
|
this.touch(entity);
|
|
}
|
|
if (!orbs.isEmpty()) {
|
|
this.touch((Entity)Util.getRandom(orbs, this.random));
|
|
}
|
|
}
|
|
this.handleShoulderEntities();
|
|
}
|
|
|
|
protected void tickRegeneration() {
|
|
}
|
|
|
|
public void handleShoulderEntities() {
|
|
}
|
|
|
|
protected void removeEntitiesOnShoulder() {
|
|
}
|
|
|
|
private void touch(Entity entity) {
|
|
entity.playerTouch(this);
|
|
}
|
|
|
|
public int getScore() {
|
|
return this.entityData.get(DATA_SCORE_ID);
|
|
}
|
|
|
|
public void setScore(int value) {
|
|
this.entityData.set(DATA_SCORE_ID, value);
|
|
}
|
|
|
|
public void increaseScore(int amount) {
|
|
int score = this.getScore();
|
|
this.entityData.set(DATA_SCORE_ID, score + amount);
|
|
}
|
|
|
|
public void startAutoSpinAttack(int activationTicks, float dmg, ItemStack itemStackUsed) {
|
|
this.autoSpinAttackTicks = activationTicks;
|
|
this.autoSpinAttackDmg = dmg;
|
|
this.autoSpinAttackItemStack = itemStackUsed;
|
|
if (!this.level().isClientSide()) {
|
|
this.removeEntitiesOnShoulder();
|
|
this.setLivingEntityFlag(4, true);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getWeaponItem() {
|
|
if (this.isAutoSpinAttack() && this.autoSpinAttackItemStack != null) {
|
|
return this.autoSpinAttackItemStack;
|
|
}
|
|
return super.getWeaponItem();
|
|
}
|
|
|
|
@Override
|
|
public void die(DamageSource source) {
|
|
Level level;
|
|
super.die(source);
|
|
this.reapplyPosition();
|
|
if (!this.isSpectator() && (level = this.level()) instanceof ServerLevel) {
|
|
ServerLevel level2 = (ServerLevel)level;
|
|
this.dropAllDeathLoot(level2, source);
|
|
}
|
|
if (source != null) {
|
|
this.setDeltaMovement(-Mth.cos((this.getHurtDir() + this.getYRot()) * ((float)Math.PI / 180)) * 0.1f, 0.1f, -Mth.sin((this.getHurtDir() + this.getYRot()) * ((float)Math.PI / 180)) * 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().get(GameRules.KEEP_INVENTORY).booleanValue()) {
|
|
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)) continue;
|
|
this.inventory.removeItemNoUpdate(i);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getHurtSound(DamageSource source) {
|
|
return source.type().effects().sound();
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getDeathSound() {
|
|
return SoundEvents.PLAYER_DEATH;
|
|
}
|
|
|
|
public void handleCreativeModeItemDrop(ItemStack stack) {
|
|
}
|
|
|
|
public @Nullable ItemEntity drop(ItemStack itemStack, boolean thrownFromHand) {
|
|
return this.drop(itemStack, false, thrownFromHand);
|
|
}
|
|
|
|
public float getDestroySpeed(BlockState state) {
|
|
float speed = this.inventory.getSelectedItem().getDestroySpeed(state);
|
|
if (speed > 1.0f) {
|
|
speed += (float)this.getAttributeValue(Attributes.MINING_EFFICIENCY);
|
|
}
|
|
if (MobEffectUtil.hasDigSpeed(this)) {
|
|
speed *= 1.0f + (float)(MobEffectUtil.getDigSpeedAmplification(this) + 1) * 0.2f;
|
|
}
|
|
if (this.hasEffect(MobEffects.MINING_FATIGUE)) {
|
|
float scale = switch (this.getEffect(MobEffects.MINING_FATIGUE).getAmplifier()) {
|
|
case 0 -> 0.3f;
|
|
case 1 -> 0.09f;
|
|
case 2 -> 0.0027f;
|
|
default -> 8.1E-4f;
|
|
};
|
|
speed *= scale;
|
|
}
|
|
speed *= (float)this.getAttributeValue(Attributes.BLOCK_BREAK_SPEED);
|
|
if (this.isEyeInFluid(FluidTags.WATER)) {
|
|
speed *= (float)this.getAttribute(Attributes.SUBMERGED_MINING_SPEED).getValue();
|
|
}
|
|
if (!this.onGround()) {
|
|
speed /= 5.0f;
|
|
}
|
|
return speed;
|
|
}
|
|
|
|
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.id());
|
|
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.setLastDeathLocation(input.read("LastDeathLocation", GlobalPos.CODEC));
|
|
this.currentImpulseImpactPos = 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));
|
|
this.lastDeathLocation.ifPresent(pos -> output.store("LastDeathLocation", GlobalPos.CODEC, pos));
|
|
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 source) {
|
|
if (super.isInvulnerableTo(level, source)) {
|
|
return true;
|
|
}
|
|
if (source.is(DamageTypeTags.IS_DROWNING)) {
|
|
return level.getGameRules().get(GameRules.DROWNING_DAMAGE) == false;
|
|
}
|
|
if (source.is(DamageTypeTags.IS_FALL)) {
|
|
return level.getGameRules().get(GameRules.FALL_DAMAGE) == false;
|
|
}
|
|
if (source.is(DamageTypeTags.IS_FIRE)) {
|
|
return level.getGameRules().get(GameRules.FIRE_DAMAGE) == false;
|
|
}
|
|
if (source.is(DamageTypeTags.IS_FREEZING)) {
|
|
return level.getGameRules().get(GameRules.FREEZE_DAMAGE) == false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
|
|
if (this.isInvulnerableTo(level, source)) {
|
|
return false;
|
|
}
|
|
if (this.abilities.invulnerable && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) {
|
|
return false;
|
|
}
|
|
this.noActionTime = 0;
|
|
if (this.isDeadOrDying()) {
|
|
return false;
|
|
}
|
|
this.removeEntitiesOnShoulder();
|
|
if (source.scalesWithDifficulty()) {
|
|
if (level.getDifficulty() == Difficulty.PEACEFUL) {
|
|
damage = 0.0f;
|
|
}
|
|
if (level.getDifficulty() == Difficulty.EASY) {
|
|
damage = Math.min(damage / 2.0f + 1.0f, damage);
|
|
}
|
|
if (level.getDifficulty() == Difficulty.HARD) {
|
|
damage = damage * 3.0f / 2.0f;
|
|
}
|
|
}
|
|
if (damage == 0.0f) {
|
|
return false;
|
|
}
|
|
return super.hurtServer(level, source, damage);
|
|
}
|
|
|
|
@Override
|
|
protected void blockUsingItem(ServerLevel level, LivingEntity attacker) {
|
|
super.blockUsingItem(level, attacker);
|
|
ItemStack itemBlockingWith = this.getItemBlockingWith();
|
|
BlocksAttacks blocksAttacks = itemBlockingWith != null ? itemBlockingWith.get(DataComponents.BLOCKS_ATTACKS) : null;
|
|
float secondsToDisableBlocking = attacker.getSecondsToDisableBlocking();
|
|
if (secondsToDisableBlocking > 0.0f && blocksAttacks != null) {
|
|
blocksAttacks.disable(level, this, secondsToDisableBlocking, itemBlockingWith);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean canBeSeenAsEnemy() {
|
|
return !this.getAbilities().invulnerable && super.canBeSeenAsEnemy();
|
|
}
|
|
|
|
public boolean canHarmPlayer(Player target) {
|
|
PlayerTeam team = this.getTeam();
|
|
PlayerTeam otherTeam = target.getTeam();
|
|
if (team == null) {
|
|
return true;
|
|
}
|
|
if (!team.isAlliedTo(otherTeam)) {
|
|
return true;
|
|
}
|
|
return ((Team)team).isAllowFriendlyFire();
|
|
}
|
|
|
|
@Override
|
|
protected void hurtArmor(DamageSource damageSource, float damage) {
|
|
this.doHurtEquipment(damageSource, damage, EquipmentSlot.FEET, EquipmentSlot.LEGS, EquipmentSlot.CHEST, EquipmentSlot.HEAD);
|
|
}
|
|
|
|
@Override
|
|
protected void hurtHelmet(DamageSource damageSource, float damage) {
|
|
this.doHurtEquipment(damageSource, damage, EquipmentSlot.HEAD);
|
|
}
|
|
|
|
@Override
|
|
protected void actuallyHurt(ServerLevel level, DamageSource source, float dmg) {
|
|
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) {
|
|
this.awardStat(Stats.DAMAGE_ABSORBED, Math.round(absorbedDamage * 10.0f));
|
|
}
|
|
if (dmg == 0.0f) {
|
|
return;
|
|
}
|
|
this.causeFoodExhaustion(source.getFoodExhaustion());
|
|
this.getCombatTracker().recordDamage(source, dmg);
|
|
this.setHealth(this.getHealth() - dmg);
|
|
if (dmg < 3.4028235E37f) {
|
|
this.awardStat(Stats.DAMAGE_TAKEN, Math.round(dmg * 10.0f));
|
|
}
|
|
this.gameEvent(GameEvent.ENTITY_DAMAGE);
|
|
}
|
|
|
|
public boolean isTextFilteringEnabled() {
|
|
return false;
|
|
}
|
|
|
|
public void openTextEdit(SignBlockEntity sign, boolean isFrontText) {
|
|
}
|
|
|
|
public void openMinecartCommandBlock(MinecartCommandBlock commandBlock) {
|
|
}
|
|
|
|
public void openCommandBlock(CommandBlockEntity commandBlock) {
|
|
}
|
|
|
|
public void openStructureBlock(StructureBlockEntity structureBlock) {
|
|
}
|
|
|
|
public void openTestBlock(TestBlockEntity testBlock) {
|
|
}
|
|
|
|
public void openTestInstanceBlock(TestInstanceBlockEntity testInstanceBlock) {
|
|
}
|
|
|
|
public void openJigsawBlock(JigsawBlockEntity jigsawBlock) {
|
|
}
|
|
|
|
public void openHorseInventory(AbstractHorse horse, Container container) {
|
|
}
|
|
|
|
public OptionalInt openMenu(@Nullable MenuProvider provider) {
|
|
return OptionalInt.empty();
|
|
}
|
|
|
|
public void openDialog(Holder<Dialog> dialog) {
|
|
}
|
|
|
|
public void sendMerchantOffers(int containerId, MerchantOffers offers, int merchantLevel, int merchantXp, boolean showProgressBar, boolean canRestock) {
|
|
}
|
|
|
|
public void openItemGui(ItemStack itemStack, InteractionHand hand) {
|
|
}
|
|
|
|
public InteractionResult interactOn(Entity entity, InteractionHand hand) {
|
|
if (this.isSpectator()) {
|
|
if (entity instanceof MenuProvider) {
|
|
this.openMenu((MenuProvider)((Object)entity));
|
|
}
|
|
return InteractionResult.PASS;
|
|
}
|
|
ItemStack itemStack = this.getItemInHand(hand);
|
|
ItemStack itemStackClone = itemStack.copy();
|
|
InteractionResult interact = entity.interact(this, hand);
|
|
if (interact.consumesAction()) {
|
|
if (this.hasInfiniteMaterials() && itemStack == this.getItemInHand(hand) && itemStack.getCount() < itemStackClone.getCount()) {
|
|
itemStack.setCount(itemStackClone.getCount());
|
|
}
|
|
return interact;
|
|
}
|
|
if (!itemStack.isEmpty() && entity instanceof LivingEntity) {
|
|
InteractionResult interactionResult;
|
|
if (this.hasInfiniteMaterials()) {
|
|
itemStack = itemStackClone;
|
|
}
|
|
if ((interactionResult = itemStack.interactLivingEntity(this, (LivingEntity)entity, hand)).consumesAction()) {
|
|
this.level().gameEvent(GameEvent.ENTITY_INTERACT, entity.position(), GameEvent.Context.of(this));
|
|
if (itemStack.isEmpty() && !this.hasInfiniteMaterials()) {
|
|
this.setItemInHand(hand, ItemStack.EMPTY);
|
|
}
|
|
return interactionResult;
|
|
}
|
|
}
|
|
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 delta, MoverType moverType) {
|
|
double deltaX;
|
|
float maxDownStep = this.maxUpStep();
|
|
if (this.abilities.flying || delta.y > 0.0 || moverType != MoverType.SELF && moverType != MoverType.PLAYER || !this.isStayingOnGroundSurface() || !this.isAboveGround(maxDownStep)) {
|
|
return delta;
|
|
}
|
|
double deltaZ = delta.z;
|
|
double step = 0.05;
|
|
double stepX = Math.signum(deltaX) * 0.05;
|
|
double stepZ = Math.signum(deltaZ) * 0.05;
|
|
for (deltaX = delta.x; deltaX != 0.0 && this.canFallAtLeast(deltaX, 0.0, maxDownStep); deltaX -= stepX) {
|
|
if (!(Math.abs(deltaX) <= 0.05)) continue;
|
|
deltaX = 0.0;
|
|
break;
|
|
}
|
|
while (deltaZ != 0.0 && this.canFallAtLeast(0.0, deltaZ, maxDownStep)) {
|
|
if (Math.abs(deltaZ) <= 0.05) {
|
|
deltaZ = 0.0;
|
|
break;
|
|
}
|
|
deltaZ -= stepZ;
|
|
}
|
|
while (deltaX != 0.0 && deltaZ != 0.0 && this.canFallAtLeast(deltaX, deltaZ, maxDownStep)) {
|
|
deltaX = Math.abs(deltaX) <= 0.05 ? 0.0 : (deltaX -= stepX);
|
|
if (Math.abs(deltaZ) <= 0.05) {
|
|
deltaZ = 0.0;
|
|
continue;
|
|
}
|
|
deltaZ -= stepZ;
|
|
}
|
|
return new Vec3(deltaX, delta.y, deltaZ);
|
|
}
|
|
|
|
private boolean isAboveGround(float maxDownStep) {
|
|
return this.onGround() || this.fallDistance < (double)maxDownStep && !this.canFallAtLeast(0.0, 0.0, (double)maxDownStep - this.fallDistance);
|
|
}
|
|
|
|
private boolean canFallAtLeast(double deltaX, double deltaZ, double minHeight) {
|
|
AABB boundingBox = this.getBoundingBox();
|
|
return this.level().noCollision(this, new AABB(boundingBox.minX + 1.0E-7 + deltaX, boundingBox.minY - minHeight - 1.0E-7, boundingBox.minZ + 1.0E-7 + deltaZ, boundingBox.maxX - 1.0E-7 + deltaX, boundingBox.minY, boundingBox.maxZ - 1.0E-7 + deltaZ));
|
|
}
|
|
|
|
public void attack(Entity entity) {
|
|
if (this.cannotAttack(entity)) {
|
|
return;
|
|
}
|
|
float baseDamage = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE);
|
|
ItemStack attackingItemStack = this.getWeaponItem();
|
|
DamageSource damageSource = this.createAttackSource(attackingItemStack);
|
|
float attackStrengthScale = this.getAttackStrengthScale(0.5f);
|
|
float magicBoost = attackStrengthScale * (this.getEnchantedDamage(entity, baseDamage, damageSource) - baseDamage);
|
|
baseDamage *= this.baseDamageScaleFactor();
|
|
this.onAttack();
|
|
if (this.deflectProjectile(entity)) {
|
|
return;
|
|
}
|
|
if (baseDamage > 0.0f || magicBoost > 0.0f) {
|
|
boolean criticalAttack;
|
|
boolean knockbackAttack;
|
|
boolean fullStrengthAttack;
|
|
boolean bl = fullStrengthAttack = attackStrengthScale > 0.9f;
|
|
if (this.isSprinting() && fullStrengthAttack) {
|
|
this.makeSound(SoundEvents.PLAYER_ATTACK_KNOCKBACK);
|
|
knockbackAttack = true;
|
|
} else {
|
|
knockbackAttack = false;
|
|
}
|
|
baseDamage += attackingItemStack.getItem().getAttackDamageBonus(entity, baseDamage, damageSource);
|
|
boolean bl2 = criticalAttack = fullStrengthAttack && this.canCriticalAttack(entity);
|
|
if (criticalAttack) {
|
|
baseDamage *= 1.5f;
|
|
}
|
|
float totalDamage = baseDamage + magicBoost;
|
|
boolean sweepAttack = this.isSweepAttack(fullStrengthAttack, criticalAttack, knockbackAttack);
|
|
float oldLivingEntityHealth = 0.0f;
|
|
if (entity instanceof LivingEntity) {
|
|
LivingEntity livingTarget = (LivingEntity)entity;
|
|
oldLivingEntityHealth = livingTarget.getHealth();
|
|
}
|
|
Vec3 oldMovement = entity.getDeltaMovement();
|
|
boolean wasHurt = entity.hurtOrSimulate(damageSource, totalDamage);
|
|
if (wasHurt) {
|
|
this.causeExtraKnockback(entity, this.getKnockback(entity, damageSource) + (knockbackAttack ? 0.5f : 0.0f), oldMovement);
|
|
if (sweepAttack) {
|
|
this.doSweepAttack(entity, baseDamage, damageSource, attackStrengthScale);
|
|
}
|
|
this.attackVisualEffects(entity, criticalAttack, sweepAttack, fullStrengthAttack, magicBoost);
|
|
this.setLastHurtMob(entity);
|
|
this.itemAttackInteraction(entity, attackingItemStack, damageSource, true);
|
|
this.damageStatsAndHearts(entity, oldLivingEntityHealth);
|
|
this.causeFoodExhaustion(0.1f);
|
|
} else {
|
|
this.playServerSideSound(SoundEvents.PLAYER_ATTACK_NODAMAGE);
|
|
}
|
|
}
|
|
this.lungeForwardMaybe();
|
|
}
|
|
|
|
private void playServerSideSound(SoundEvent sound) {
|
|
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), 1.0f, 1.0f);
|
|
}
|
|
|
|
private DamageSource createAttackSource(ItemStack attackingItemStack) {
|
|
return attackingItemStack.getDamageSource(this, () -> this.damageSources().playerAttack(this));
|
|
}
|
|
|
|
private boolean cannotAttack(Entity entity) {
|
|
if (!entity.isAttackable()) {
|
|
return true;
|
|
}
|
|
return entity.skipAttackInteraction(this);
|
|
}
|
|
|
|
private boolean deflectProjectile(Entity entity) {
|
|
Projectile projectile;
|
|
if (entity.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entity instanceof Projectile && (projectile = (Projectile)entity).deflect(ProjectileDeflection.AIM_DEFLECT, this, EntityReference.of(this), true)) {
|
|
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean canCriticalAttack(Entity entity) {
|
|
return this.fallDistance > 0.0 && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.isMobilityRestricted() && !this.isPassenger() && entity instanceof LivingEntity && !this.isSprinting();
|
|
}
|
|
|
|
private boolean isSweepAttack(boolean fullStrengthAttack, boolean criticalAttack, boolean knockbackAttack) {
|
|
double maxSpeedForSweepAttack;
|
|
double approximateSpeedSq;
|
|
if (fullStrengthAttack && !criticalAttack && !knockbackAttack && this.onGround() && (approximateSpeedSq = this.getKnownMovement().horizontalDistanceSqr()) < Mth.square(maxSpeedForSweepAttack = (double)this.getSpeed() * 2.5)) {
|
|
return this.getItemInHand(InteractionHand.MAIN_HAND).is(ItemTags.SWORDS);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void attackVisualEffects(Entity entity, boolean criticalAttack, boolean sweepAttack, boolean fullStrengthAttack, float magicBoost) {
|
|
if (criticalAttack) {
|
|
this.playServerSideSound(SoundEvents.PLAYER_ATTACK_CRIT);
|
|
this.crit(entity);
|
|
}
|
|
if (!criticalAttack && !sweepAttack) {
|
|
this.playServerSideSound(fullStrengthAttack ? SoundEvents.PLAYER_ATTACK_STRONG : SoundEvents.PLAYER_ATTACK_WEAK);
|
|
}
|
|
if (magicBoost > 0.0f) {
|
|
this.magicCrit(entity);
|
|
}
|
|
}
|
|
|
|
private void damageStatsAndHearts(Entity entity, float oldLivingEntityHealth) {
|
|
if (entity instanceof LivingEntity) {
|
|
float actualDamage = oldLivingEntityHealth - ((LivingEntity)entity).getHealth();
|
|
this.awardStat(Stats.DAMAGE_DEALT, Math.round(actualDamage * 10.0f));
|
|
if (this.level() instanceof ServerLevel && actualDamage > 2.0f) {
|
|
int count = (int)((double)actualDamage * 0.5);
|
|
((ServerLevel)this.level()).sendParticles(ParticleTypes.DAMAGE_INDICATOR, entity.getX(), entity.getY(0.5), entity.getZ(), count, 0.1, 0.0, 0.1, 0.2);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void itemAttackInteraction(Entity entity, ItemStack attackingItemStack, DamageSource damageSource, boolean applyToTarget) {
|
|
Entity hurtTarget = entity;
|
|
if (entity instanceof EnderDragonPart) {
|
|
hurtTarget = ((EnderDragonPart)entity).parentMob;
|
|
}
|
|
boolean itemHurtEnemy = false;
|
|
Level level = this.level();
|
|
if (level instanceof ServerLevel) {
|
|
ServerLevel serverLevel = (ServerLevel)level;
|
|
if (hurtTarget instanceof LivingEntity) {
|
|
LivingEntity livingTarget = (LivingEntity)hurtTarget;
|
|
itemHurtEnemy = attackingItemStack.hurtEnemy(livingTarget, this);
|
|
}
|
|
if (applyToTarget) {
|
|
EnchantmentHelper.doPostAttackEffectsWithItemSource(serverLevel, entity, damageSource, attackingItemStack);
|
|
}
|
|
}
|
|
if (!this.level().isClientSide() && !attackingItemStack.isEmpty() && hurtTarget instanceof LivingEntity) {
|
|
if (itemHurtEnemy) {
|
|
attackingItemStack.postHurtEnemy((LivingEntity)hurtTarget, this);
|
|
}
|
|
if (attackingItemStack.isEmpty()) {
|
|
if (attackingItemStack == this.getMainHandItem()) {
|
|
this.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY);
|
|
} else {
|
|
this.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void causeExtraKnockback(Entity entity, float knockbackAmount, Vec3 oldMovement) {
|
|
if (knockbackAmount > 0.0f) {
|
|
if (entity instanceof LivingEntity) {
|
|
LivingEntity livingTarget = (LivingEntity)entity;
|
|
livingTarget.knockback(knockbackAmount, Mth.sin(this.getYRot() * ((float)Math.PI / 180)), -Mth.cos(this.getYRot() * ((float)Math.PI / 180)));
|
|
} else {
|
|
entity.push(-Mth.sin(this.getYRot() * ((float)Math.PI / 180)) * knockbackAmount, 0.1, Mth.cos(this.getYRot() * ((float)Math.PI / 180)) * knockbackAmount);
|
|
}
|
|
this.setDeltaMovement(this.getDeltaMovement().multiply(0.6, 1.0, 0.6));
|
|
this.setSprinting(false);
|
|
}
|
|
if (entity instanceof ServerPlayer && entity.hurtMarked) {
|
|
((ServerPlayer)entity).connection.send(new ClientboundSetEntityMotionPacket(entity));
|
|
entity.hurtMarked = false;
|
|
entity.setDeltaMovement(oldMovement);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public float getVoicePitch() {
|
|
return 1.0f;
|
|
}
|
|
|
|
private void doSweepAttack(Entity entity, float baseDamage, DamageSource damageSource, float attackStrengthScale) {
|
|
this.playServerSideSound(SoundEvents.PLAYER_ATTACK_SWEEP);
|
|
Level level = this.level();
|
|
if (!(level instanceof ServerLevel)) {
|
|
return;
|
|
}
|
|
ServerLevel serverLevel = (ServerLevel)level;
|
|
float sweepDamage = 1.0f + (float)this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * baseDamage;
|
|
List<LivingEntity> nearbyEntities = this.level().getEntitiesOfClass(LivingEntity.class, entity.getBoundingBox().inflate(1.0, 0.25, 1.0));
|
|
for (LivingEntity nearby : nearbyEntities) {
|
|
float enchantedDamage;
|
|
ArmorStand armorStand;
|
|
if (nearby == this || nearby == entity || this.isAlliedTo(nearby) || nearby instanceof ArmorStand && (armorStand = (ArmorStand)nearby).isMarker() || !(this.distanceToSqr(nearby) < 9.0) || !nearby.hurtServer(serverLevel, damageSource, enchantedDamage = this.getEnchantedDamage(nearby, sweepDamage, damageSource) * attackStrengthScale)) continue;
|
|
nearby.knockback(0.4f, Mth.sin(this.getYRot() * ((float)Math.PI / 180)), -Mth.cos(this.getYRot() * ((float)Math.PI / 180)));
|
|
EnchantmentHelper.doPostAttackEffects(serverLevel, nearby, damageSource);
|
|
}
|
|
double dx = -Mth.sin(this.getYRot() * ((float)Math.PI / 180));
|
|
double dz = Mth.cos(this.getYRot() * ((float)Math.PI / 180));
|
|
serverLevel.sendParticles(ParticleTypes.SWEEP_ATTACK, this.getX() + dx, this.getY(0.5), this.getZ() + dz, 0, dx, 0.0, dz, 0.0);
|
|
}
|
|
|
|
protected float getEnchantedDamage(Entity entity, float dmg, DamageSource damageSource) {
|
|
return dmg;
|
|
}
|
|
|
|
@Override
|
|
protected void doAutoAttackOnTouch(LivingEntity entity) {
|
|
this.attack(entity);
|
|
}
|
|
|
|
public void crit(Entity entity) {
|
|
}
|
|
|
|
private float baseDamageScaleFactor() {
|
|
float attackStrengthScale = this.getAttackStrengthScale(0.5f);
|
|
return 0.2f + attackStrengthScale * attackStrengthScale * 0.8f;
|
|
}
|
|
|
|
@Override
|
|
public boolean stabAttack(EquipmentSlot slot, Entity target, float baseDamage, boolean dealsDamage, boolean dealsKnockback, boolean dismounts) {
|
|
boolean wasHurt;
|
|
if (this.cannotAttack(target)) {
|
|
return false;
|
|
}
|
|
ItemStack weaponItem = this.getItemBySlot(slot);
|
|
DamageSource damageSource = this.createAttackSource(weaponItem);
|
|
float magicBoost = this.getAttackStrengthScale(0.5f) * (this.getEnchantedDamage(target, baseDamage, damageSource) - baseDamage);
|
|
baseDamage *= this.baseDamageScaleFactor();
|
|
if (dealsKnockback && this.deflectProjectile(target)) {
|
|
return true;
|
|
}
|
|
float totalDamage = dealsDamage ? baseDamage + magicBoost : 0.0f;
|
|
float oldLivingEntityHealth = 0.0f;
|
|
if (target instanceof LivingEntity) {
|
|
LivingEntity livingTarget = (LivingEntity)target;
|
|
oldLivingEntityHealth = livingTarget.getHealth();
|
|
}
|
|
Vec3 oldMovement = target.getDeltaMovement();
|
|
boolean bl = wasHurt = dealsDamage && target.hurtOrSimulate(damageSource, totalDamage);
|
|
if (dealsKnockback) {
|
|
this.causeExtraKnockback(target, 0.4f + this.getKnockback(target, damageSource), oldMovement);
|
|
}
|
|
boolean dismounted = false;
|
|
if (dismounts && target.isPassenger()) {
|
|
dismounted = true;
|
|
target.stopRiding();
|
|
}
|
|
if (!(wasHurt || dealsKnockback || dismounted)) {
|
|
return false;
|
|
}
|
|
this.attackVisualEffects(target, false, false, dealsDamage, magicBoost);
|
|
this.setLastHurtMob(target);
|
|
this.itemAttackInteraction(target, weaponItem, damageSource, wasHurt);
|
|
this.damageStatsAndHearts(target, oldLivingEntityHealth);
|
|
this.causeFoodExhaustion(0.1f);
|
|
return true;
|
|
}
|
|
|
|
public void magicCrit(Entity entity) {
|
|
}
|
|
|
|
@Override
|
|
public void remove(Entity.RemovalReason reason) {
|
|
super.remove(reason);
|
|
this.inventoryMenu.removed(this);
|
|
if (this.hasContainerOpen()) {
|
|
this.doCloseContainer();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isClientAuthoritative() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected boolean isLocalClientAuthoritative() {
|
|
return this.isLocalPlayer();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
public GameProfile getGameProfile() {
|
|
return this.gameProfile;
|
|
}
|
|
|
|
public NameAndId nameAndId() {
|
|
return new NameAndId(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 itemCarried, ItemStack itemInSlot, ClickAction clickAction) {
|
|
}
|
|
|
|
public boolean hasContainerOpen() {
|
|
return this.containerMenu != this.inventoryMenu;
|
|
}
|
|
|
|
public boolean canDropItems() {
|
|
return true;
|
|
}
|
|
|
|
public Either<BedSleepingProblem, Unit> startSleepInBed(BlockPos pos) {
|
|
this.startSleeping(pos);
|
|
this.sleepCounter = 0;
|
|
return Either.right((Object)((Object)Unit.INSTANCE));
|
|
}
|
|
|
|
public void stopSleepInBed(boolean forcefulWakeUp, boolean updateLevelList) {
|
|
super.stopSleeping();
|
|
if (this.level() instanceof ServerLevel && updateLevelList) {
|
|
((ServerLevel)this.level()).updateSleepingPlayerList();
|
|
}
|
|
this.sleepCounter = forcefulWakeUp ? 0 : 100;
|
|
}
|
|
|
|
@Override
|
|
public void stopSleeping() {
|
|
this.stopSleepInBed(true, true);
|
|
}
|
|
|
|
public boolean isSleepingLongEnough() {
|
|
return this.isSleeping() && this.sleepCounter >= 100;
|
|
}
|
|
|
|
public int getSleepTimer() {
|
|
return this.sleepCounter;
|
|
}
|
|
|
|
public void displayClientMessage(Component component, boolean overlayMessage) {
|
|
}
|
|
|
|
public void awardStat(Identifier location) {
|
|
this.awardStat(Stats.CUSTOM.get(location));
|
|
}
|
|
|
|
public void awardStat(Identifier location, int count) {
|
|
this.awardStat(Stats.CUSTOM.get(location), count);
|
|
}
|
|
|
|
public void awardStat(Stat<?> stat) {
|
|
this.awardStat(stat, 1);
|
|
}
|
|
|
|
public void awardStat(Stat<?> stat, int count) {
|
|
}
|
|
|
|
public void resetStat(Stat<?> stat) {
|
|
}
|
|
|
|
public int awardRecipes(Collection<RecipeHolder<?>> recipes) {
|
|
return 0;
|
|
}
|
|
|
|
public void triggerRecipeCrafted(RecipeHolder<?> recipe, List<ItemStack> itemStacks) {
|
|
}
|
|
|
|
public void awardRecipesByKey(List<ResourceKey<Recipe<?>>> recipeIds) {
|
|
}
|
|
|
|
public int resetRecipes(Collection<RecipeHolder<?>> recipe) {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void travel(Vec3 input) {
|
|
if (this.isPassenger()) {
|
|
super.travel(input);
|
|
return;
|
|
}
|
|
if (this.isSwimming()) {
|
|
double multiplier;
|
|
double lookAngleY = this.getLookAngle().y;
|
|
double d = multiplier = lookAngleY < -0.2 ? 0.085 : 0.06;
|
|
if (lookAngleY <= 0.0 || this.jumping || !this.level().getFluidState(BlockPos.containing(this.getX(), this.getY() + 1.0 - 0.1, this.getZ())).isEmpty()) {
|
|
Vec3 movement = this.getDeltaMovement();
|
|
this.setDeltaMovement(movement.add(0.0, (lookAngleY - movement.y) * multiplier, 0.0));
|
|
}
|
|
}
|
|
if (this.getAbilities().flying) {
|
|
double originalMovementY = this.getDeltaMovement().y;
|
|
super.travel(input);
|
|
this.setDeltaMovement(this.getDeltaMovement().with(Direction.Axis.Y, originalMovementY * 0.6));
|
|
} else {
|
|
super.travel(input);
|
|
}
|
|
}
|
|
|
|
@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 damageModifier, DamageSource damageSource) {
|
|
double effectiveFallDistance;
|
|
boolean hasRelativeFallDamageResistance;
|
|
if (this.abilities.mayfly) {
|
|
return false;
|
|
}
|
|
if (fallDistance >= 2.0) {
|
|
this.awardStat(Stats.FALL_ONE_CM, (int)Math.round(fallDistance * 100.0));
|
|
}
|
|
boolean bl = hasRelativeFallDamageResistance = this.currentImpulseImpactPos != null && this.ignoreFallDamageFromCurrentImpulse;
|
|
if (hasRelativeFallDamageResistance) {
|
|
boolean hasLandedAboveCurrentImpulseImpactPosY;
|
|
effectiveFallDistance = Math.min(fallDistance, this.currentImpulseImpactPos.y - this.getY());
|
|
boolean bl2 = hasLandedAboveCurrentImpulseImpactPosY = effectiveFallDistance <= 0.0;
|
|
if (hasLandedAboveCurrentImpulseImpactPosY) {
|
|
this.resetCurrentImpulseContext();
|
|
} else {
|
|
this.tryResetCurrentImpulseContext();
|
|
}
|
|
} else {
|
|
effectiveFallDistance = fallDistance;
|
|
}
|
|
if (effectiveFallDistance > 0.0 && super.causeFallDamage(effectiveFallDistance, damageModifier, damageSource)) {
|
|
this.resetCurrentImpulseContext();
|
|
return true;
|
|
}
|
|
this.propagateFallToPassengers(fallDistance, damageModifier, damageSource);
|
|
return false;
|
|
}
|
|
|
|
public boolean tryToStartFallFlying() {
|
|
if (!this.isFallFlying() && this.canGlide() && !this.isInWater()) {
|
|
this.startFallFlying();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void startFallFlying() {
|
|
this.setSharedFlag(7, true);
|
|
}
|
|
|
|
@Override
|
|
protected void doWaterSplashEffect() {
|
|
if (!this.isSpectator()) {
|
|
super.doWaterSplashEffect();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void playStepSound(BlockPos onPos, BlockState onState) {
|
|
if (this.isInWater()) {
|
|
this.waterSwimSound();
|
|
this.playMuffledStepSound(onState);
|
|
} else {
|
|
BlockPos primaryStepSoundPos = this.getPrimaryStepSoundBlockPos(onPos);
|
|
if (!onPos.equals(primaryStepSoundPos)) {
|
|
BlockState primaryStepState = this.level().getBlockState(primaryStepSoundPos);
|
|
if (primaryStepState.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS)) {
|
|
this.playCombinationStepSounds(primaryStepState, onState);
|
|
} else {
|
|
super.playStepSound(primaryStepSoundPos, primaryStepState);
|
|
}
|
|
} else {
|
|
super.playStepSound(onPos, onState);
|
|
}
|
|
}
|
|
}
|
|
|
|
@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, DamageSource source) {
|
|
this.awardStat(Stats.ENTITY_KILLED.get(entity.getType()));
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void makeStuckInBlock(BlockState blockState, Vec3 speedMultiplier) {
|
|
if (!this.abilities.flying) {
|
|
super.makeStuckInBlock(blockState, speedMultiplier);
|
|
}
|
|
this.tryResetCurrentImpulseContext();
|
|
}
|
|
|
|
public void giveExperiencePoints(int i) {
|
|
this.increaseScore(i);
|
|
this.experienceProgress += (float)i / (float)this.getXpNeededForNextLevel();
|
|
this.totalExperience = Mth.clamp(this.totalExperience + i, 0, Integer.MAX_VALUE);
|
|
while (this.experienceProgress < 0.0f) {
|
|
float remaining = this.experienceProgress * (float)this.getXpNeededForNextLevel();
|
|
if (this.experienceLevel > 0) {
|
|
this.giveExperienceLevels(-1);
|
|
this.experienceProgress = 1.0f + remaining / (float)this.getXpNeededForNextLevel();
|
|
continue;
|
|
}
|
|
this.giveExperienceLevels(-1);
|
|
this.experienceProgress = 0.0f;
|
|
}
|
|
while (this.experienceProgress >= 1.0f) {
|
|
this.experienceProgress = (this.experienceProgress - 1.0f) * (float)this.getXpNeededForNextLevel();
|
|
this.giveExperienceLevels(1);
|
|
this.experienceProgress /= (float)this.getXpNeededForNextLevel();
|
|
}
|
|
}
|
|
|
|
public int getEnchantmentSeed() {
|
|
return this.enchantmentSeed;
|
|
}
|
|
|
|
public void onEnchantmentPerformed(ItemStack itemStack, int enchantmentCost) {
|
|
this.experienceLevel -= enchantmentCost;
|
|
if (this.experienceLevel < 0) {
|
|
this.experienceLevel = 0;
|
|
this.experienceProgress = 0.0f;
|
|
this.totalExperience = 0;
|
|
}
|
|
this.enchantmentSeed = this.random.nextInt();
|
|
}
|
|
|
|
public void giveExperienceLevels(int amount) {
|
|
this.experienceLevel = IntMath.saturatedAdd((int)this.experienceLevel, (int)amount);
|
|
if (this.experienceLevel < 0) {
|
|
this.experienceLevel = 0;
|
|
this.experienceProgress = 0.0f;
|
|
this.totalExperience = 0;
|
|
}
|
|
if (amount > 0 && this.experienceLevel % 5 == 0 && (float)this.lastLevelUpTime < (float)this.tickCount - 100.0f) {
|
|
float vol = this.experienceLevel > 30 ? 1.0f : (float)this.experienceLevel / 30.0f;
|
|
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), vol * 0.75f, 1.0f);
|
|
this.lastLevelUpTime = this.tickCount;
|
|
}
|
|
}
|
|
|
|
public int getXpNeededForNextLevel() {
|
|
if (this.experienceLevel >= 30) {
|
|
return 112 + (this.experienceLevel - 30) * 9;
|
|
}
|
|
if (this.experienceLevel >= 15) {
|
|
return 37 + (this.experienceLevel - 15) * 5;
|
|
}
|
|
return 7 + this.experienceLevel * 2;
|
|
}
|
|
|
|
public void causeFoodExhaustion(float amount) {
|
|
if (this.abilities.invulnerable) {
|
|
return;
|
|
}
|
|
if (!this.level().isClientSide()) {
|
|
this.foodData.addExhaustion(amount);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void lungeForwardMaybe() {
|
|
if (this.hasEnoughFoodToDoExhaustiveManoeuvres()) {
|
|
super.lungeForwardMaybe();
|
|
}
|
|
}
|
|
|
|
protected boolean hasEnoughFoodToDoExhaustiveManoeuvres() {
|
|
return this.getFoodData().hasEnoughFood() || this.getAbilities().mayfly;
|
|
}
|
|
|
|
public Optional<WardenSpawnTracker> getWardenSpawnTracker() {
|
|
return Optional.empty();
|
|
}
|
|
|
|
public FoodData getFoodData() {
|
|
return this.foodData;
|
|
}
|
|
|
|
public boolean canEat(boolean canAlwaysEat) {
|
|
return this.abilities.invulnerable || canAlwaysEat || this.foodData.needsFood();
|
|
}
|
|
|
|
public boolean isHurt() {
|
|
return this.getHealth() > 0.0f && this.getHealth() < this.getMaxHealth();
|
|
}
|
|
|
|
public boolean mayBuild() {
|
|
return this.abilities.mayBuild;
|
|
}
|
|
|
|
public boolean mayUseItemAt(BlockPos pos, Direction direction, ItemStack itemStack) {
|
|
if (this.abilities.mayBuild) {
|
|
return true;
|
|
}
|
|
BlockPos target = pos.relative(direction.getOpposite());
|
|
BlockInWorld block = new BlockInWorld(this.level(), target, false);
|
|
return itemStack.canPlaceOnBlockInAdventureMode(block);
|
|
}
|
|
|
|
@Override
|
|
protected int getBaseExperienceReward(ServerLevel level) {
|
|
if (level.getGameRules().get(GameRules.KEEP_INVENTORY).booleanValue() || this.isSpectator()) {
|
|
return 0;
|
|
}
|
|
return Math.min(this.experienceLevel * 7, 100);
|
|
}
|
|
|
|
@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.ALL : Entity.MovementEmission.NONE;
|
|
}
|
|
|
|
public void onUpdateAbilities() {
|
|
}
|
|
|
|
@Override
|
|
public Component getName() {
|
|
return Component.literal(this.gameProfile.name());
|
|
}
|
|
|
|
@Override
|
|
public String getPlainTextName() {
|
|
return this.gameProfile.name();
|
|
}
|
|
|
|
public PlayerEnderChestContainer getEnderChestInventory() {
|
|
return this.enderChestInventory;
|
|
}
|
|
|
|
@Override
|
|
protected boolean doesEmitEquipEvent(EquipmentSlot slot) {
|
|
return slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR;
|
|
}
|
|
|
|
public boolean addItem(ItemStack itemStack) {
|
|
return this.inventory.add(itemStack);
|
|
}
|
|
|
|
public abstract @Nullable 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;
|
|
}
|
|
|
|
@Override
|
|
public Component getDisplayName() {
|
|
MutableComponent result = PlayerTeam.formatNameForTeam(this.getTeam(), this.getName());
|
|
return this.decorateDisplayNameComponent(result);
|
|
}
|
|
|
|
private MutableComponent decorateDisplayNameComponent(MutableComponent nameComponent) {
|
|
String name = this.getGameProfile().name();
|
|
return nameComponent.withStyle(s -> s.withClickEvent(new ClickEvent.SuggestCommand("/tell " + name + " ")).withHoverEvent(this.createHoverEvent()).withInsertion(name));
|
|
}
|
|
|
|
@Override
|
|
public String getScoreboardName() {
|
|
return this.getGameProfile().name();
|
|
}
|
|
|
|
@Override
|
|
protected void internalSetAbsorptionAmount(float absorptionAmount) {
|
|
this.getEntityData().set(DATA_PLAYER_ABSORPTION_ID, Float.valueOf(absorptionAmount));
|
|
}
|
|
|
|
@Override
|
|
public float getAbsorptionAmount() {
|
|
return this.getEntityData().get(DATA_PLAYER_ABSORPTION_ID).floatValue();
|
|
}
|
|
|
|
@Override
|
|
public @Nullable SlotAccess getSlot(int slot) {
|
|
if (slot == 499) {
|
|
return new SlotAccess(){
|
|
|
|
@Override
|
|
public ItemStack get() {
|
|
return Player.this.containerMenu.getCarried();
|
|
}
|
|
|
|
@Override
|
|
public boolean set(ItemStack itemStack) {
|
|
Player.this.containerMenu.setCarried(itemStack);
|
|
return true;
|
|
}
|
|
};
|
|
}
|
|
final int craftSlot = slot - 500;
|
|
if (craftSlot >= 0 && craftSlot < 4) {
|
|
return new SlotAccess(){
|
|
|
|
@Override
|
|
public ItemStack get() {
|
|
return Player.this.inventoryMenu.getCraftSlots().getItem(craftSlot);
|
|
}
|
|
|
|
@Override
|
|
public boolean set(ItemStack itemStack) {
|
|
Player.this.inventoryMenu.getCraftSlots().setItem(craftSlot, itemStack);
|
|
Player.this.inventoryMenu.slotsChanged(Player.this.inventory);
|
|
return true;
|
|
}
|
|
};
|
|
}
|
|
if (slot >= 0 && slot < this.inventory.getNonEquipmentItems().size()) {
|
|
return this.inventory.getSlot(slot);
|
|
}
|
|
int enderSlot = slot - 200;
|
|
if (enderSlot >= 0 && enderSlot < this.enderChestInventory.getContainerSize()) {
|
|
return this.enderChestInventory.getSlot(enderSlot);
|
|
}
|
|
return super.getSlot(slot);
|
|
}
|
|
|
|
public boolean isReducedDebugInfo() {
|
|
return this.reducedDebugInfo;
|
|
}
|
|
|
|
public void setReducedDebugInfo(boolean reducedDebugInfo) {
|
|
this.reducedDebugInfo = reducedDebugInfo;
|
|
}
|
|
|
|
@Override
|
|
public void setRemainingFireTicks(int remainingTicks) {
|
|
super.setRemainingFireTicks(this.abilities.invulnerable ? Math.min(remainingTicks, 1) : remainingTicks);
|
|
}
|
|
|
|
protected static Optional<Parrot.Variant> extractParrotVariant(CompoundTag tag) {
|
|
EntityType entityType;
|
|
if (!tag.isEmpty() && (entityType = (EntityType)tag.read("id", EntityType.CODEC).orElse(null)) == EntityType.PARROT) {
|
|
return tag.read("Variant", Parrot.Variant.LEGACY_CODEC);
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
|
|
protected static OptionalInt convertParrotVariant(Optional<Parrot.Variant> variant) {
|
|
return variant.map(v -> OptionalInt.of(v.getId())).orElse(OptionalInt.empty());
|
|
}
|
|
|
|
private static Optional<Parrot.Variant> convertParrotVariant(OptionalInt variant) {
|
|
if (variant.isPresent()) {
|
|
return Optional.of(Parrot.Variant.byId(variant.getAsInt()));
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
|
|
public void setShoulderParrotLeft(Optional<Parrot.Variant> variant) {
|
|
this.entityData.set(DATA_SHOULDER_PARROT_LEFT, Player.convertParrotVariant(variant));
|
|
}
|
|
|
|
public Optional<Parrot.Variant> getShoulderParrotLeft() {
|
|
return Player.convertParrotVariant(this.entityData.get(DATA_SHOULDER_PARROT_LEFT));
|
|
}
|
|
|
|
public void setShoulderParrotRight(Optional<Parrot.Variant> variant) {
|
|
this.entityData.set(DATA_SHOULDER_PARROT_RIGHT, Player.convertParrotVariant(variant));
|
|
}
|
|
|
|
public Optional<Parrot.Variant> getShoulderParrotRight() {
|
|
return Player.convertParrotVariant(this.entityData.get(DATA_SHOULDER_PARROT_RIGHT));
|
|
}
|
|
|
|
public float getCurrentItemAttackStrengthDelay() {
|
|
return (float)(1.0 / this.getAttributeValue(Attributes.ATTACK_SPEED) * 20.0);
|
|
}
|
|
|
|
public boolean cannotAttackWithItem(ItemStack itemStack, int tolerance) {
|
|
float requiredStrength = itemStack.getOrDefault(DataComponents.MINIMUM_ATTACK_CHARGE, Float.valueOf(0.0f)).floatValue();
|
|
float optimisticStrength = (float)(this.attackStrengthTicker + tolerance) / this.getCurrentItemAttackStrengthDelay();
|
|
return requiredStrength > 0.0f && optimisticStrength < requiredStrength;
|
|
}
|
|
|
|
public float getAttackStrengthScale(float a) {
|
|
return Mth.clamp(((float)this.attackStrengthTicker + a) / this.getCurrentItemAttackStrengthDelay(), 0.0f, 1.0f);
|
|
}
|
|
|
|
public float getItemSwapScale(float a) {
|
|
return Mth.clamp(((float)this.itemSwapTicker + a) / this.getCurrentItemAttackStrengthDelay(), 0.0f, 1.0f);
|
|
}
|
|
|
|
public void resetAttackStrengthTicker() {
|
|
this.attackStrengthTicker = 0;
|
|
this.itemSwapTicker = 0;
|
|
}
|
|
|
|
@Override
|
|
public void onAttack() {
|
|
this.resetOnlyAttackStrengthTicker();
|
|
super.onAttack();
|
|
}
|
|
|
|
public void resetOnlyAttackStrengthTicker() {
|
|
this.attackStrengthTicker = 0;
|
|
}
|
|
|
|
public ItemCooldowns getCooldowns() {
|
|
return this.cooldowns;
|
|
}
|
|
|
|
@Override
|
|
protected float getBlockSpeedFactor() {
|
|
return this.abilities.flying || this.isFallFlying() ? 1.0f : super.getBlockSpeedFactor();
|
|
}
|
|
|
|
@Override
|
|
public float getLuck() {
|
|
return (float)this.getAttributeValue(Attributes.LUCK);
|
|
}
|
|
|
|
public boolean canUseGameMasterBlocks() {
|
|
return this.abilities.instabuild && this.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER);
|
|
}
|
|
|
|
public PermissionSet permissions() {
|
|
return PermissionSet.NO_PERMISSIONS;
|
|
}
|
|
|
|
@Override
|
|
public ImmutableList<Pose> getDismountPoses() {
|
|
return ImmutableList.of((Object)Pose.STANDING, (Object)Pose.CROUCHING, (Object)Pose.SWIMMING);
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getProjectile(ItemStack heldWeapon) {
|
|
if (!(heldWeapon.getItem() instanceof ProjectileWeaponItem)) {
|
|
return ItemStack.EMPTY;
|
|
}
|
|
Predicate<ItemStack> supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getSupportedHeldProjectiles();
|
|
ItemStack heldProjectile = ProjectileWeaponItem.getHeldProjectile(this, supportedProjectiles);
|
|
if (!heldProjectile.isEmpty()) {
|
|
return heldProjectile;
|
|
}
|
|
supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getAllSupportedProjectiles();
|
|
for (int i = 0; i < this.inventory.getContainerSize(); ++i) {
|
|
ItemStack itemStack = this.inventory.getItem(i);
|
|
if (!supportedProjectiles.test(itemStack)) continue;
|
|
return itemStack;
|
|
}
|
|
return this.hasInfiniteMaterials() ? new ItemStack(Items.ARROW) : ItemStack.EMPTY;
|
|
}
|
|
|
|
@Override
|
|
public Vec3 getRopeHoldPosition(float partialTickTime) {
|
|
double xOff = 0.22 * (this.getMainArm() == HumanoidArm.RIGHT ? -1.0 : 1.0);
|
|
float xRot = Mth.lerp(partialTickTime * 0.5f, this.getXRot(), this.xRotO) * ((float)Math.PI / 180);
|
|
float yRot = Mth.lerp(partialTickTime, this.yBodyRotO, this.yBodyRot) * ((float)Math.PI / 180);
|
|
if (this.isFallFlying() || this.isAutoSpinAttack()) {
|
|
float zRot;
|
|
Vec3 lookAngle = this.getViewVector(partialTickTime);
|
|
Vec3 movement = this.getDeltaMovement();
|
|
double speedLen = movement.horizontalDistanceSqr();
|
|
double lookLen = lookAngle.horizontalDistanceSqr();
|
|
if (speedLen > 0.0 && lookLen > 0.0) {
|
|
double dot = (movement.x * lookAngle.x + movement.z * lookAngle.z) / Math.sqrt(speedLen * lookLen);
|
|
double sign = movement.x * lookAngle.z - movement.z * lookAngle.x;
|
|
zRot = (float)(Math.signum(sign) * Math.acos(dot));
|
|
} else {
|
|
zRot = 0.0f;
|
|
}
|
|
return this.getPosition(partialTickTime).add(new Vec3(xOff, -0.11, 0.85).zRot(-zRot).xRot(-xRot).yRot(-yRot));
|
|
}
|
|
if (this.isVisuallySwimming()) {
|
|
return this.getPosition(partialTickTime).add(new Vec3(xOff, 0.2, -0.15).xRot(-xRot).yRot(-yRot));
|
|
}
|
|
double yOff = this.getBoundingBox().getYsize() - 1.0;
|
|
double zOff = this.isCrouching() ? -0.2 : 0.07;
|
|
return this.getPosition(partialTickTime).add(new Vec3(xOff, yOff, zOff).yRot(-yRot));
|
|
}
|
|
|
|
@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> pos) {
|
|
this.lastDeathLocation = pos;
|
|
}
|
|
|
|
@Override
|
|
public float getHurtDir() {
|
|
return this.hurtDir;
|
|
}
|
|
|
|
@Override
|
|
public void animateHurt(float yaw) {
|
|
super.animateHurt(yaw);
|
|
this.hurtDir = yaw;
|
|
}
|
|
|
|
public boolean isMobilityRestricted() {
|
|
return this.hasEffect(MobEffects.BLINDNESS);
|
|
}
|
|
|
|
@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();
|
|
}
|
|
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 loaded) {
|
|
this.clientLoaded = loaded;
|
|
if (!this.clientLoaded) {
|
|
this.clientLoadedTimeoutTimer = 60;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasContainerOpen(ContainerOpenersCounter container, BlockPos blockPos) {
|
|
return container.isOwnContainer(this);
|
|
}
|
|
|
|
@Override
|
|
public double getContainerInteractionRange() {
|
|
return this.blockInteractionRange();
|
|
}
|
|
|
|
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 buffer) {
|
|
if (entity.isRemoved()) {
|
|
return false;
|
|
}
|
|
return this.canInteractWithEntity(entity.getBoundingBox(), buffer);
|
|
}
|
|
|
|
public boolean canInteractWithEntity(AABB aabb, double buffer) {
|
|
double maxRange = this.entityInteractionRange() + buffer;
|
|
return aabb.distanceToSqr(this.getEyePosition()) < maxRange * maxRange;
|
|
}
|
|
|
|
public boolean canInteractWithBlock(BlockPos pos, double buffer) {
|
|
double maxRange = this.blockInteractionRange() + buffer;
|
|
return new AABB(pos).distanceToSqr(this.getEyePosition()) < maxRange * maxRange;
|
|
}
|
|
|
|
public void setIgnoreFallDamageFromCurrentImpulse(boolean ignoreFallDamage) {
|
|
this.ignoreFallDamageFromCurrentImpulse = ignoreFallDamage;
|
|
this.currentImpulseContextResetGraceTime = ignoreFallDamage ? 40 : 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() {
|
|
if (this.abilities.flying) {
|
|
return false;
|
|
}
|
|
return super.onClimbable();
|
|
}
|
|
|
|
public String debugInfo() {
|
|
return MoreObjects.toStringHelper((Object)this).add("name", (Object)this.getPlainTextName()).add("id", this.getId()).add("pos", (Object)this.position()).add("mode", (Object)this.gameMode()).add("permission", (Object)this.permissions()).toString();
|
|
}
|
|
|
|
public record BedSleepingProblem(@Nullable Component message) {
|
|
public static final BedSleepingProblem TOO_FAR_AWAY = new BedSleepingProblem(Component.translatable("block.minecraft.bed.too_far_away"));
|
|
public static final BedSleepingProblem OBSTRUCTED = new BedSleepingProblem(Component.translatable("block.minecraft.bed.obstructed"));
|
|
public static final BedSleepingProblem OTHER_PROBLEM = new BedSleepingProblem(null);
|
|
public static final BedSleepingProblem NOT_SAFE = new BedSleepingProblem(Component.translatable("block.minecraft.bed.not_safe"));
|
|
}
|
|
}
|
|
|