313 lines
12 KiB
Java
313 lines
12 KiB
Java
package net.minecraft.world.entity.animal;
|
|
|
|
import net.minecraft.world.entity.ai.goal.TemptGoal;
|
|
import net.minecraft.world.entity.EntitySelector;
|
|
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.LevelReader;
|
|
import java.util.Random;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.world.entity.MobSpawnType;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.entity.AgableMob;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.world.entity.monster.SharedMonsterAttributes;
|
|
import net.minecraft.world.entity.Pose;
|
|
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
|
import net.minecraft.world.entity.PathfinderMob;
|
|
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
|
import net.minecraft.world.entity.ai.goal.BreedGoal;
|
|
import net.minecraft.world.entity.ai.goal.OcelotAttackGoal;
|
|
import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal;
|
|
import net.minecraft.world.entity.ai.goal.Goal;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.world.item.crafting.Ingredient;
|
|
|
|
public class Ocelot extends Animal {
|
|
private static final Ingredient TEMPT_INGREDIENT;
|
|
private static final EntityDataAccessor<Boolean> DATA_TRUSTING;
|
|
private OcelotAvoidEntityGoal<Player> ocelotAvoidPlayersGoal;
|
|
private OcelotTemptGoal temptGoal;
|
|
|
|
public Ocelot(final EntityType<? extends Ocelot> akr, final Level bjt) {
|
|
super(akr, bjt);
|
|
this.reassessTrustingGoals();
|
|
}
|
|
|
|
private boolean isTrusting() {
|
|
return this.entityData.<Boolean>get(Ocelot.DATA_TRUSTING);
|
|
}
|
|
|
|
private void setTrusting(final boolean boolean1) {
|
|
this.entityData.<Boolean>set(Ocelot.DATA_TRUSTING, boolean1);
|
|
this.reassessTrustingGoals();
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(final CompoundTag jt) {
|
|
super.addAdditionalSaveData(jt);
|
|
jt.putBoolean("Trusting", this.isTrusting());
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(final CompoundTag jt) {
|
|
super.readAdditionalSaveData(jt);
|
|
this.setTrusting(jt.getBoolean("Trusting"));
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
this.entityData.<Boolean>define(Ocelot.DATA_TRUSTING, false);
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.temptGoal = new OcelotTemptGoal(this, 0.6, Ocelot.TEMPT_INGREDIENT, true);
|
|
this.goalSelector.addGoal(1, new FloatGoal(this));
|
|
this.goalSelector.addGoal(3, this.temptGoal);
|
|
this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3f));
|
|
this.goalSelector.addGoal(8, new OcelotAttackGoal(this));
|
|
this.goalSelector.addGoal(9, new BreedGoal(this, 0.8));
|
|
this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8, 1.0000001E-5f));
|
|
this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0f));
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false));
|
|
this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR));
|
|
}
|
|
|
|
public void customServerAiStep() {
|
|
if (this.getMoveControl().hasWanted()) {
|
|
final double double2 = this.getMoveControl().getSpeedModifier();
|
|
if (double2 == 0.6) {
|
|
this.setPose(Pose.CROUCHING);
|
|
this.setSprinting(false);
|
|
}
|
|
else if (double2 == 1.33) {
|
|
this.setPose(Pose.STANDING);
|
|
this.setSprinting(true);
|
|
}
|
|
else {
|
|
this.setPose(Pose.STANDING);
|
|
this.setSprinting(false);
|
|
}
|
|
}
|
|
else {
|
|
this.setPose(Pose.STANDING);
|
|
this.setSprinting(false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean removeWhenFarAway(final double double1) {
|
|
return !this.isTrusting() && this.tickCount > 2400;
|
|
}
|
|
|
|
@Override
|
|
protected void registerAttributes() {
|
|
super.registerAttributes();
|
|
this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(10.0);
|
|
this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.30000001192092896);
|
|
this.getAttributes().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(3.0);
|
|
}
|
|
|
|
@Override
|
|
public boolean causeFallDamage(final float float1, final float float2) {
|
|
return false;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
return SoundEvents.OCELOT_AMBIENT;
|
|
}
|
|
|
|
@Override
|
|
public int getAmbientSoundInterval() {
|
|
return 900;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getHurtSound(final DamageSource ajw) {
|
|
return SoundEvents.OCELOT_HURT;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getDeathSound() {
|
|
return SoundEvents.OCELOT_DEATH;
|
|
}
|
|
|
|
private float getAttackDamage() {
|
|
return (float)this.getAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getValue();
|
|
}
|
|
|
|
@Override
|
|
public boolean doHurtTarget(final Entity akn) {
|
|
return akn.hurt(DamageSource.mobAttack(this), this.getAttackDamage());
|
|
}
|
|
|
|
@Override
|
|
public boolean hurt(final DamageSource ajw, final float float2) {
|
|
return !this.isInvulnerableTo(ajw) && super.hurt(ajw, float2);
|
|
}
|
|
|
|
@Override
|
|
public boolean mobInteract(final Player ayg, final InteractionHand ajh) {
|
|
final ItemStack bek4 = ayg.getItemInHand(ajh);
|
|
if ((this.temptGoal == null || this.temptGoal.isRunning()) && !this.isTrusting() && this.isFood(bek4) && ayg.distanceToSqr(this) < 9.0) {
|
|
this.usePlayerItem(ayg, bek4);
|
|
if (!this.level.isClientSide) {
|
|
if (this.random.nextInt(3) == 0) {
|
|
this.setTrusting(true);
|
|
this.spawnTrustingParticles(true);
|
|
this.level.broadcastEntityEvent(this, (byte)41);
|
|
}
|
|
else {
|
|
this.spawnTrustingParticles(false);
|
|
this.level.broadcastEntityEvent(this, (byte)40);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return super.mobInteract(ayg, ajh);
|
|
}
|
|
|
|
@Override
|
|
public void handleEntityEvent(final byte byte1) {
|
|
if (byte1 == 41) {
|
|
this.spawnTrustingParticles(true);
|
|
}
|
|
else if (byte1 == 40) {
|
|
this.spawnTrustingParticles(false);
|
|
}
|
|
else {
|
|
super.handleEntityEvent(byte1);
|
|
}
|
|
}
|
|
|
|
private void spawnTrustingParticles(final boolean boolean1) {
|
|
ParticleOptions gt3 = ParticleTypes.HEART;
|
|
if (!boolean1) {
|
|
gt3 = ParticleTypes.SMOKE;
|
|
}
|
|
for (int integer4 = 0; integer4 < 7; ++integer4) {
|
|
final double double5 = this.random.nextGaussian() * 0.02;
|
|
final double double6 = this.random.nextGaussian() * 0.02;
|
|
final double double7 = this.random.nextGaussian() * 0.02;
|
|
this.level.addParticle(gt3, this.getRandomX(1.0), this.getRandomY() + 0.5, this.getRandomZ(1.0), double5, double6, double7);
|
|
}
|
|
}
|
|
|
|
protected void reassessTrustingGoals() {
|
|
if (this.ocelotAvoidPlayersGoal == null) {
|
|
this.ocelotAvoidPlayersGoal = new OcelotAvoidEntityGoal<Player>(this, Player.class, 16.0f, 0.8, 1.33);
|
|
}
|
|
this.goalSelector.removeGoal(this.ocelotAvoidPlayersGoal);
|
|
if (!this.isTrusting()) {
|
|
this.goalSelector.addGoal(4, this.ocelotAvoidPlayersGoal);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Ocelot getBreedOffspring(final AgableMob akl) {
|
|
return EntityType.OCELOT.create(this.level);
|
|
}
|
|
|
|
@Override
|
|
public boolean isFood(final ItemStack bek) {
|
|
return Ocelot.TEMPT_INGREDIENT.test(bek);
|
|
}
|
|
|
|
public static boolean checkOcelotSpawnRules(final EntityType<Ocelot> akr, final LevelAccessor bju, final MobSpawnType akz, final BlockPos fk, final Random random) {
|
|
return random.nextInt(3) != 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean checkSpawnObstruction(final LevelReader bjw) {
|
|
if (bjw.isUnobstructed(this) && !bjw.containsAnyLiquid(this.getBoundingBox())) {
|
|
final BlockPos fk3 = new BlockPos(this);
|
|
if (fk3.getY() < bjw.getSeaLevel()) {
|
|
return false;
|
|
}
|
|
final BlockState byg4 = bjw.getBlockState(fk3.below());
|
|
final Block bpe5 = byg4.getBlock();
|
|
if (bpe5 == Blocks.GRASS_BLOCK || byg4.is(BlockTags.LEAVES)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(final LevelAccessor bju, final DifficultyInstance ajg, final MobSpawnType akz, @Nullable SpawnGroupData alj, @Nullable final CompoundTag jt) {
|
|
if (alj == null) {
|
|
alj = new AgableMobGroupData();
|
|
((AgableMobGroupData)alj).setBabySpawnChance(1.0f);
|
|
}
|
|
return super.finalizeSpawn(bju, ajg, akz, alj, jt);
|
|
}
|
|
|
|
static {
|
|
TEMPT_INGREDIENT = Ingredient.of(Items.COD, Items.SALMON);
|
|
DATA_TRUSTING = SynchedEntityData.<Boolean>defineId(Ocelot.class, EntityDataSerializers.BOOLEAN);
|
|
}
|
|
|
|
static class OcelotAvoidEntityGoal<T extends LivingEntity> extends AvoidEntityGoal<T> {
|
|
private final Ocelot ocelot;
|
|
|
|
public OcelotAvoidEntityGoal(final Ocelot atk, final Class<T> class2, final float float3, final double double4, final double double5) {
|
|
super(atk, class2, float3, double4, double5, EntitySelector.NO_CREATIVE_OR_SPECTATOR::test);
|
|
this.ocelot = atk;
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
return !this.ocelot.isTrusting() && super.canUse();
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
return !this.ocelot.isTrusting() && super.canContinueToUse();
|
|
}
|
|
}
|
|
|
|
static class OcelotTemptGoal extends TemptGoal {
|
|
private final Ocelot ocelot;
|
|
|
|
public OcelotTemptGoal(final Ocelot atk, final double double2, final Ingredient bgq, final boolean boolean4) {
|
|
super(atk, double2, bgq, boolean4);
|
|
this.ocelot = atk;
|
|
}
|
|
|
|
@Override
|
|
protected boolean canScare() {
|
|
return super.canScare() && !this.ocelot.isTrusting();
|
|
}
|
|
}
|
|
}
|