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

456 lines
16 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.entity.item;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.UUIDUtil;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.SlotAccess;
import net.minecraft.world.entity.TraceableEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gamerules.GameRules;
import net.minecraft.world.level.portal.TeleportTransition;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
public class ItemEntity
extends Entity
implements TraceableEntity {
private static final EntityDataAccessor<ItemStack> DATA_ITEM = SynchedEntityData.defineId(ItemEntity.class, EntityDataSerializers.ITEM_STACK);
private static final float FLOAT_HEIGHT = 0.1f;
public static final float EYE_HEIGHT = 0.2125f;
private static final int LIFETIME = 6000;
private static final int INFINITE_PICKUP_DELAY = Short.MAX_VALUE;
private static final int INFINITE_LIFETIME = Short.MIN_VALUE;
private static final int DEFAULT_HEALTH = 5;
private static final short DEFAULT_AGE = 0;
private static final short DEFAULT_PICKUP_DELAY = 0;
private int age = 0;
private int pickupDelay = 0;
private int health = 5;
private @Nullable EntityReference<Entity> thrower;
private @Nullable UUID target;
public final float bobOffs = this.random.nextFloat() * (float)Math.PI * 2.0f;
public ItemEntity(EntityType<? extends ItemEntity> type, Level level) {
super(type, level);
this.setYRot(this.random.nextFloat() * 360.0f);
}
public ItemEntity(Level level, double x, double y, double z, ItemStack itemStack) {
this(level, x, y, z, itemStack, level.random.nextDouble() * 0.2 - 0.1, 0.2, level.random.nextDouble() * 0.2 - 0.1);
}
public ItemEntity(Level level, double x, double y, double z, ItemStack itemStack, double deltaX, double deltaY, double deltaZ) {
this((EntityType<? extends ItemEntity>)EntityType.ITEM, level);
this.setPos(x, y, z);
this.setDeltaMovement(deltaX, deltaY, deltaZ);
this.setItem(itemStack);
}
@Override
public boolean dampensVibrations() {
return this.getItem().is(ItemTags.DAMPENS_VIBRATIONS);
}
@Override
public @Nullable Entity getOwner() {
return EntityReference.getEntity(this.thrower, this.level());
}
@Override
public void restoreFrom(Entity oldEntity) {
super.restoreFrom(oldEntity);
if (oldEntity instanceof ItemEntity) {
ItemEntity item = (ItemEntity)oldEntity;
this.thrower = item.thrower;
}
}
@Override
protected Entity.MovementEmission getMovementEmission() {
return Entity.MovementEmission.NONE;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
entityData.define(DATA_ITEM, ItemStack.EMPTY);
}
@Override
protected double getDefaultGravity() {
return 0.04;
}
@Override
public void tick() {
double value;
int rate;
if (this.getItem().isEmpty()) {
this.discard();
return;
}
super.tick();
if (this.pickupDelay > 0 && this.pickupDelay != Short.MAX_VALUE) {
--this.pickupDelay;
}
this.xo = this.getX();
this.yo = this.getY();
this.zo = this.getZ();
Vec3 oldMovement = this.getDeltaMovement();
if (this.isInWater() && this.getFluidHeight(FluidTags.WATER) > (double)0.1f) {
this.setUnderwaterMovement();
} else if (this.isInLava() && this.getFluidHeight(FluidTags.LAVA) > (double)0.1f) {
this.setUnderLavaMovement();
} else {
this.applyGravity();
}
if (this.level().isClientSide()) {
this.noPhysics = false;
} else {
boolean bl = this.noPhysics = !this.level().noCollision(this, this.getBoundingBox().deflate(1.0E-7));
if (this.noPhysics) {
this.moveTowardsClosestSpace(this.getX(), (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0, this.getZ());
}
}
if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > (double)1.0E-5f || (this.tickCount + this.getId()) % 4 == 0) {
this.move(MoverType.SELF, this.getDeltaMovement());
this.applyEffectsFromBlocks();
float friction = 0.98f;
if (this.onGround()) {
friction = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98f;
}
this.setDeltaMovement(this.getDeltaMovement().multiply(friction, 0.98, friction));
if (this.onGround()) {
Vec3 movement = this.getDeltaMovement();
if (movement.y < 0.0) {
this.setDeltaMovement(movement.multiply(1.0, -0.5, 1.0));
}
}
}
boolean moved = Mth.floor(this.xo) != Mth.floor(this.getX()) || Mth.floor(this.yo) != Mth.floor(this.getY()) || Mth.floor(this.zo) != Mth.floor(this.getZ());
int n = rate = moved ? 2 : 40;
if (this.tickCount % rate == 0 && !this.level().isClientSide() && this.isMergable()) {
this.mergeWithNeighbours();
}
if (this.age != Short.MIN_VALUE) {
++this.age;
}
this.needsSync |= this.updateInWaterStateAndDoFluidPushing();
if (!this.level().isClientSide() && (value = this.getDeltaMovement().subtract(oldMovement).lengthSqr()) > 0.01) {
this.needsSync = true;
}
if (!this.level().isClientSide() && this.age >= 6000) {
this.discard();
}
}
@Override
public BlockPos getBlockPosBelowThatAffectsMyMovement() {
return this.getOnPos(0.999999f);
}
private void setUnderwaterMovement() {
this.setFluidMovement(0.99f);
}
private void setUnderLavaMovement() {
this.setFluidMovement(0.95f);
}
private void setFluidMovement(double multiplier) {
Vec3 movement = this.getDeltaMovement();
this.setDeltaMovement(movement.x * multiplier, movement.y + (double)(movement.y < (double)0.06f ? 5.0E-4f : 0.0f), movement.z * multiplier);
}
private void mergeWithNeighbours() {
if (!this.isMergable()) {
return;
}
List<ItemEntity> items = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), other -> other != this && other.isMergable());
for (ItemEntity entity : items) {
if (!entity.isMergable()) continue;
this.tryToMerge(entity);
if (!this.isRemoved()) continue;
break;
}
}
private boolean isMergable() {
ItemStack item = this.getItem();
return this.isAlive() && this.pickupDelay != Short.MAX_VALUE && this.age != Short.MIN_VALUE && this.age < 6000 && item.getCount() < item.getMaxStackSize();
}
private void tryToMerge(ItemEntity other) {
ItemStack thisItemStack = this.getItem();
ItemStack otherItemStack = other.getItem();
if (!Objects.equals(this.target, other.target) || !ItemEntity.areMergable(thisItemStack, otherItemStack)) {
return;
}
if (otherItemStack.getCount() < thisItemStack.getCount()) {
ItemEntity.merge(this, thisItemStack, other, otherItemStack);
} else {
ItemEntity.merge(other, otherItemStack, this, thisItemStack);
}
}
public static boolean areMergable(ItemStack thisItemStack, ItemStack otherItemStack) {
if (otherItemStack.getCount() + thisItemStack.getCount() > otherItemStack.getMaxStackSize()) {
return false;
}
return ItemStack.isSameItemSameComponents(thisItemStack, otherItemStack);
}
public static ItemStack merge(ItemStack toStack, ItemStack fromStack, int maxCount) {
int delta = Math.min(Math.min(toStack.getMaxStackSize(), maxCount) - toStack.getCount(), fromStack.getCount());
ItemStack newToStack = toStack.copyWithCount(toStack.getCount() + delta);
fromStack.shrink(delta);
return newToStack;
}
private static void merge(ItemEntity toItem, ItemStack toStack, ItemStack fromStack) {
ItemStack newToStack = ItemEntity.merge(toStack, fromStack, 64);
toItem.setItem(newToStack);
}
private static void merge(ItemEntity toItem, ItemStack toStack, ItemEntity fromItem, ItemStack fromStack) {
ItemEntity.merge(toItem, toStack, fromStack);
toItem.pickupDelay = Math.max(toItem.pickupDelay, fromItem.pickupDelay);
toItem.age = Math.min(toItem.age, fromItem.age);
if (fromStack.isEmpty()) {
fromItem.discard();
}
}
@Override
public boolean fireImmune() {
return !this.getItem().canBeHurtBy(this.damageSources().inFire()) || super.fireImmune();
}
@Override
protected boolean shouldPlayLavaHurtSound() {
if (this.health <= 0) {
return true;
}
return this.tickCount % 10 == 0;
}
@Override
public final boolean hurtClient(DamageSource source) {
if (this.isInvulnerableToBase(source)) {
return false;
}
return this.getItem().canBeHurtBy(source);
}
@Override
public final boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
if (this.isInvulnerableToBase(source)) {
return false;
}
if (!level.getGameRules().get(GameRules.MOB_GRIEFING).booleanValue() && source.getEntity() instanceof Mob) {
return false;
}
if (!this.getItem().canBeHurtBy(source)) {
return false;
}
this.markHurt();
this.health = (int)((float)this.health - damage);
this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity());
if (this.health <= 0) {
this.getItem().onDestroyed(this);
this.discard();
}
return true;
}
@Override
public boolean ignoreExplosion(Explosion explosion) {
if (explosion.shouldAffectBlocklikeEntities()) {
return super.ignoreExplosion(explosion);
}
return true;
}
@Override
protected void addAdditionalSaveData(ValueOutput output) {
output.putShort("Health", (short)this.health);
output.putShort("Age", (short)this.age);
output.putShort("PickupDelay", (short)this.pickupDelay);
EntityReference.store(this.thrower, output, "Thrower");
output.storeNullable("Owner", UUIDUtil.CODEC, this.target);
if (!this.getItem().isEmpty()) {
output.store("Item", ItemStack.CODEC, this.getItem());
}
}
@Override
protected void readAdditionalSaveData(ValueInput input) {
this.health = input.getShortOr("Health", (short)5);
this.age = input.getShortOr("Age", (short)0);
this.pickupDelay = input.getShortOr("PickupDelay", (short)0);
this.target = input.read("Owner", UUIDUtil.CODEC).orElse(null);
this.thrower = EntityReference.read(input, "Thrower");
this.setItem(input.read("Item", ItemStack.CODEC).orElse(ItemStack.EMPTY));
if (this.getItem().isEmpty()) {
this.discard();
}
}
@Override
public void playerTouch(Player player) {
if (this.level().isClientSide()) {
return;
}
ItemStack itemStack = this.getItem();
Item item = itemStack.getItem();
int orgCount = itemStack.getCount();
if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID())) && player.getInventory().add(itemStack)) {
player.take(this, orgCount);
if (itemStack.isEmpty()) {
this.discard();
itemStack.setCount(orgCount);
}
player.awardStat(Stats.ITEM_PICKED_UP.get(item), orgCount);
player.onItemPickup(this);
}
}
@Override
public Component getName() {
Component name = this.getCustomName();
if (name != null) {
return name;
}
return this.getItem().getItemName();
}
@Override
public boolean isAttackable() {
return false;
}
@Override
public @Nullable Entity teleport(TeleportTransition transition) {
Entity entity = super.teleport(transition);
if (!this.level().isClientSide() && entity instanceof ItemEntity) {
ItemEntity item = (ItemEntity)entity;
item.mergeWithNeighbours();
}
return entity;
}
public ItemStack getItem() {
return this.getEntityData().get(DATA_ITEM);
}
public void setItem(ItemStack itemStack) {
this.getEntityData().set(DATA_ITEM, itemStack);
}
@Override
public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
super.onSyncedDataUpdated(accessor);
if (DATA_ITEM.equals(accessor)) {
this.getItem().setEntityRepresentation(this);
}
}
public void setTarget(@Nullable UUID target) {
this.target = target;
}
public void setThrower(Entity thrower) {
this.thrower = EntityReference.of(thrower);
}
public int getAge() {
return this.age;
}
public void setDefaultPickUpDelay() {
this.pickupDelay = 10;
}
public void setNoPickUpDelay() {
this.pickupDelay = 0;
}
public void setNeverPickUp() {
this.pickupDelay = Short.MAX_VALUE;
}
public void setPickUpDelay(int ticks) {
this.pickupDelay = ticks;
}
public boolean hasPickUpDelay() {
return this.pickupDelay > 0;
}
public void setUnlimitedLifetime() {
this.age = Short.MIN_VALUE;
}
public void setExtendedLifetime() {
this.age = -6000;
}
public void makeFakeItem() {
this.setNeverPickUp();
this.age = 5999;
}
public static float getSpin(float ageInTicks, float bobOffset) {
return ageInTicks / 20.0f + bobOffset;
}
@Override
public SoundSource getSoundSource() {
return SoundSource.AMBIENT;
}
@Override
public float getVisualRotationYInDegrees() {
return 180.0f - ItemEntity.getSpin((float)this.getAge() + 0.5f, this.bobOffs) / ((float)Math.PI * 2) * 360.0f;
}
@Override
public @Nullable SlotAccess getSlot(int slot) {
if (slot == 0) {
return SlotAccess.of(this::getItem, this::setItem);
}
return super.getSlot(slot);
}
}