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

331 lines
13 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.entity.animal;
import net.minecraft.core.BlockPos;
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.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Crackiness;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.NeutralMob;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.GolemRandomStrollInVillageGoal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.ai.goal.MoveBackToVillageGoal;
import net.minecraft.world.entity.ai.goal.MoveTowardsTargetGoal;
import net.minecraft.world.entity.ai.goal.OfferFlowerGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.ai.goal.target.DefendVillageTargetGoal;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal;
import net.minecraft.world.entity.animal.AbstractGolem;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.NaturalSpawner;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluids;
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 IronGolem
extends AbstractGolem
implements NeutralMob {
protected static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(IronGolem.class, EntityDataSerializers.BYTE);
private static final int IRON_INGOT_HEAL_AMOUNT = 25;
private static final boolean DEFAULT_PLAYER_CREATED = false;
private int attackAnimationTick;
private int offerFlowerTick;
private static final UniformInt PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39);
private long persistentAngerEndTime;
private @Nullable EntityReference<LivingEntity> persistentAngerTarget;
public IronGolem(EntityType<? extends IronGolem> type, Level level) {
super((EntityType<? extends AbstractGolem>)type, level);
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0, true));
this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9, 32.0f));
this.goalSelector.addGoal(2, new MoveBackToVillageGoal((PathfinderMob)this, 0.6, false));
this.goalSelector.addGoal(4, new GolemRandomStrollInVillageGoal(this, 0.6));
this.goalSelector.addGoal(5, new OfferFlowerGoal(this));
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0f));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<Player>(this, Player.class, 10, true, false, this::isAngryAt));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<Mob>(this, Mob.class, 5, false, false, (target, level) -> target instanceof Enemy && !(target instanceof Creeper)));
this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<IronGolem>(this, false));
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
entityData.define(DATA_FLAGS_ID, (byte)0);
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 100.0).add(Attributes.MOVEMENT_SPEED, 0.25).add(Attributes.KNOCKBACK_RESISTANCE, 1.0).add(Attributes.ATTACK_DAMAGE, 15.0).add(Attributes.STEP_HEIGHT, 1.0);
}
@Override
protected int decreaseAirSupply(int currentSupply) {
return currentSupply;
}
@Override
protected void doPush(Entity entity) {
if (entity instanceof Enemy && !(entity instanceof Creeper) && this.getRandom().nextInt(20) == 0) {
this.setTarget((LivingEntity)entity);
}
super.doPush(entity);
}
@Override
public void aiStep() {
super.aiStep();
if (this.attackAnimationTick > 0) {
--this.attackAnimationTick;
}
if (this.offerFlowerTick > 0) {
--this.offerFlowerTick;
}
if (!this.level().isClientSide()) {
this.updatePersistentAnger((ServerLevel)this.level(), true);
}
}
@Override
public boolean canSpawnSprintParticle() {
return this.getDeltaMovement().horizontalDistanceSqr() > 2.500000277905201E-7 && this.random.nextInt(5) == 0;
}
@Override
public boolean canAttackType(EntityType<?> targetType) {
if (this.isPlayerCreated() && targetType == EntityType.PLAYER) {
return false;
}
if (targetType == EntityType.CREEPER) {
return false;
}
return super.canAttackType(targetType);
}
@Override
protected void addAdditionalSaveData(ValueOutput output) {
super.addAdditionalSaveData(output);
output.putBoolean("PlayerCreated", this.isPlayerCreated());
this.addPersistentAngerSaveData(output);
}
@Override
protected void readAdditionalSaveData(ValueInput input) {
super.readAdditionalSaveData(input);
this.setPlayerCreated(input.getBooleanOr("PlayerCreated", false));
this.readPersistentAngerSaveData(this.level(), input);
}
@Override
public void startPersistentAngerTimer() {
this.setTimeToRemainAngry(PERSISTENT_ANGER_TIME.sample(this.random));
}
@Override
public void setPersistentAngerEndTime(long endTime) {
this.persistentAngerEndTime = endTime;
}
@Override
public long getPersistentAngerEndTime() {
return this.persistentAngerEndTime;
}
@Override
public void setPersistentAngerTarget(@Nullable EntityReference<LivingEntity> persistentAngerTarget) {
this.persistentAngerTarget = persistentAngerTarget;
}
@Override
public @Nullable EntityReference<LivingEntity> getPersistentAngerTarget() {
return this.persistentAngerTarget;
}
private float getAttackDamage() {
return (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE);
}
@Override
public boolean doHurtTarget(ServerLevel level, Entity target) {
this.attackAnimationTick = 10;
level.broadcastEntityEvent(this, (byte)4);
float attackDamage = this.getAttackDamage();
float damage = (int)attackDamage > 0 ? attackDamage / 2.0f + (float)this.random.nextInt((int)attackDamage) : attackDamage;
DamageSource damageSource = this.damageSources().mobAttack(this);
boolean hurt = target.hurtServer(level, damageSource, damage);
if (hurt) {
double d;
if (target instanceof LivingEntity) {
LivingEntity livingEntity = (LivingEntity)target;
d = livingEntity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE);
} else {
d = 0.0;
}
double knockbackResistance = d;
double scale = Math.max(0.0, 1.0 - knockbackResistance);
target.setDeltaMovement(target.getDeltaMovement().add(0.0, (double)0.4f * scale, 0.0));
EnchantmentHelper.doPostAttackEffects(level, target, damageSource);
}
this.playSound(SoundEvents.IRON_GOLEM_ATTACK, 1.0f, 1.0f);
return hurt;
}
@Override
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
Crackiness.Level previousCrackiness = this.getCrackiness();
boolean wasHurt = super.hurtServer(level, source, damage);
if (wasHurt && this.getCrackiness() != previousCrackiness) {
this.playSound(SoundEvents.IRON_GOLEM_DAMAGE, 1.0f, 1.0f);
}
return wasHurt;
}
public Crackiness.Level getCrackiness() {
return Crackiness.GOLEM.byFraction(this.getHealth() / this.getMaxHealth());
}
@Override
public void handleEntityEvent(byte id) {
if (id == 4) {
this.attackAnimationTick = 10;
this.playSound(SoundEvents.IRON_GOLEM_ATTACK, 1.0f, 1.0f);
} else if (id == 11) {
this.offerFlowerTick = 400;
} else if (id == 34) {
this.offerFlowerTick = 0;
} else {
super.handleEntityEvent(id);
}
}
public int getAttackAnimationTick() {
return this.attackAnimationTick;
}
public void offerFlower(boolean offer) {
if (offer) {
this.offerFlowerTick = 400;
this.level().broadcastEntityEvent(this, (byte)11);
} else {
this.offerFlowerTick = 0;
this.level().broadcastEntityEvent(this, (byte)34);
}
}
@Override
protected SoundEvent getHurtSound(DamageSource source) {
return SoundEvents.IRON_GOLEM_HURT;
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.IRON_GOLEM_DEATH;
}
@Override
protected InteractionResult mobInteract(Player player, InteractionHand hand) {
ItemStack itemStack = player.getItemInHand(hand);
if (!itemStack.is(Items.IRON_INGOT)) {
return InteractionResult.PASS;
}
float healthBefore = this.getHealth();
this.heal(25.0f);
if (this.getHealth() == healthBefore) {
return InteractionResult.PASS;
}
float pitch = 1.0f + (this.random.nextFloat() - this.random.nextFloat()) * 0.2f;
this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0f, pitch);
itemStack.consume(1, player);
return InteractionResult.SUCCESS;
}
@Override
protected void playStepSound(BlockPos pos, BlockState blockState) {
this.playSound(SoundEvents.IRON_GOLEM_STEP, 1.0f, 1.0f);
}
public int getOfferFlowerTick() {
return this.offerFlowerTick;
}
public boolean isPlayerCreated() {
return (this.entityData.get(DATA_FLAGS_ID) & 1) != 0;
}
public void setPlayerCreated(boolean value) {
byte current = this.entityData.get(DATA_FLAGS_ID);
if (value) {
this.entityData.set(DATA_FLAGS_ID, (byte)(current | 1));
} else {
this.entityData.set(DATA_FLAGS_ID, (byte)(current & 0xFFFFFFFE));
}
}
@Override
public void die(DamageSource source) {
super.die(source);
}
@Override
public boolean checkSpawnObstruction(LevelReader level) {
BlockPos pos = this.blockPosition();
BlockPos belowPos = pos.below();
BlockState below = level.getBlockState(belowPos);
if (below.entityCanStandOn(level, belowPos, this)) {
for (int i = 1; i < 3; ++i) {
BlockState above;
BlockPos abovePos = pos.above(i);
if (NaturalSpawner.isValidEmptySpawnBlock(level, abovePos, above = level.getBlockState(abovePos), above.getFluidState(), EntityType.IRON_GOLEM)) continue;
return false;
}
return NaturalSpawner.isValidEmptySpawnBlock(level, pos, level.getBlockState(pos), Fluids.EMPTY.defaultFluidState(), EntityType.IRON_GOLEM) && level.isUnobstructed(this);
}
return false;
}
@Override
public Vec3 getLeashOffset() {
return new Vec3(0.0, 0.875f * this.getEyeHeight(), this.getBbWidth() * 0.4f);
}
}