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

584 lines
25 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.entity.monster;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.particles.ParticleTypes;
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.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.valueproviders.UniformInt;
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.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.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
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.ai.targeting.TargetingConditions;
import net.minecraft.world.entity.monster.Endermite;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractThrownPotion;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionContents;
import net.minecraft.world.item.alchemy.Potions;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.providers.VanillaEnchantmentProviders;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gamerules.GameRules;
import net.minecraft.world.level.pathfinder.PathType;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
public class EnderMan
extends Monster
implements NeutralMob {
private static final Identifier SPEED_MODIFIER_ATTACKING_ID = Identifier.withDefaultNamespace("attacking");
private static final AttributeModifier SPEED_MODIFIER_ATTACKING = new AttributeModifier(SPEED_MODIFIER_ATTACKING_ID, 0.15f, AttributeModifier.Operation.ADD_VALUE);
private static final int DELAY_BETWEEN_CREEPY_STARE_SOUND = 400;
private static final int MIN_DEAGGRESSION_TIME = 600;
private static final EntityDataAccessor<Optional<BlockState>> DATA_CARRY_STATE = SynchedEntityData.defineId(EnderMan.class, EntityDataSerializers.OPTIONAL_BLOCK_STATE);
private static final EntityDataAccessor<Boolean> DATA_CREEPY = SynchedEntityData.defineId(EnderMan.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> DATA_STARED_AT = SynchedEntityData.defineId(EnderMan.class, EntityDataSerializers.BOOLEAN);
private int lastStareSound = Integer.MIN_VALUE;
private int targetChangeTime;
private static final UniformInt PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39);
private long persistentAngerEndTime;
private @Nullable EntityReference<LivingEntity> persistentAngerTarget;
public EnderMan(EntityType<? extends EnderMan> type, Level level) {
super((EntityType<? extends Monster>)type, level);
this.setPathfindingMalus(PathType.WATER, -1.0f);
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new EndermanFreezeWhenLookedAt(this));
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0, false));
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal((PathfinderMob)this, 1.0, 0.0f));
this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0f));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
this.goalSelector.addGoal(10, new EndermanLeaveBlockGoal(this));
this.goalSelector.addGoal(11, new EndermanTakeBlockGoal(this));
this.targetSelector.addGoal(1, new EndermanLookForPlayerGoal(this, this::isAngryAt));
this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<Endermite>((Mob)this, Endermite.class, true, false));
this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<EnderMan>(this, false));
}
@Override
public float getWalkTargetValue(BlockPos pos, LevelReader level) {
return 0.0f;
}
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 40.0).add(Attributes.MOVEMENT_SPEED, 0.3f).add(Attributes.ATTACK_DAMAGE, 7.0).add(Attributes.FOLLOW_RANGE, 64.0).add(Attributes.STEP_HEIGHT, 1.0);
}
@Override
public void setTarget(@Nullable LivingEntity target) {
super.setTarget(target);
AttributeInstance movementSpeed = this.getAttribute(Attributes.MOVEMENT_SPEED);
if (target == null) {
this.targetChangeTime = 0;
this.entityData.set(DATA_CREEPY, false);
this.entityData.set(DATA_STARED_AT, false);
movementSpeed.removeModifier(SPEED_MODIFIER_ATTACKING_ID);
} else {
this.targetChangeTime = this.tickCount;
this.entityData.set(DATA_CREEPY, true);
if (!movementSpeed.hasModifier(SPEED_MODIFIER_ATTACKING_ID)) {
movementSpeed.addTransientModifier(SPEED_MODIFIER_ATTACKING);
}
}
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
super.defineSynchedData(entityData);
entityData.define(DATA_CARRY_STATE, Optional.empty());
entityData.define(DATA_CREEPY, false);
entityData.define(DATA_STARED_AT, false);
}
@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;
}
public void playStareSound() {
if (this.tickCount >= this.lastStareSound + 400) {
this.lastStareSound = this.tickCount;
if (!this.isSilent()) {
this.level().playLocalSound(this.getX(), this.getEyeY(), this.getZ(), SoundEvents.ENDERMAN_STARE, this.getSoundSource(), 2.5f, 1.0f, false);
}
}
}
@Override
public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
if (DATA_CREEPY.equals(accessor) && this.hasBeenStaredAt() && this.level().isClientSide()) {
this.playStareSound();
}
super.onSyncedDataUpdated(accessor);
}
@Override
protected void addAdditionalSaveData(ValueOutput output) {
super.addAdditionalSaveData(output);
BlockState blockState = this.getCarriedBlock();
if (blockState != null) {
output.store("carriedBlockState", BlockState.CODEC, blockState);
}
this.addPersistentAngerSaveData(output);
}
@Override
protected void readAdditionalSaveData(ValueInput input) {
super.readAdditionalSaveData(input);
this.setCarriedBlock(input.read("carriedBlockState", BlockState.CODEC).filter(blockState -> !blockState.isAir()).orElse(null));
this.readPersistentAngerSaveData(this.level(), input);
}
private boolean isBeingStaredBy(Player player) {
if (!LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM.test(player)) {
return false;
}
return this.isLookingAtMe(player, 0.025, true, false, this.getEyeY());
}
@Override
public void aiStep() {
if (this.level().isClientSide()) {
for (int i = 0; i < 2; ++i) {
this.level().addParticle(ParticleTypes.PORTAL, this.getRandomX(0.5), this.getRandomY() - 0.25, this.getRandomZ(0.5), (this.random.nextDouble() - 0.5) * 2.0, -this.random.nextDouble(), (this.random.nextDouble() - 0.5) * 2.0);
}
}
this.jumping = false;
if (!this.level().isClientSide()) {
this.updatePersistentAnger((ServerLevel)this.level(), true);
}
super.aiStep();
}
@Override
public boolean isSensitiveToWater() {
return true;
}
@Override
protected void customServerAiStep(ServerLevel level) {
float br;
if (level.isBrightOutside() && this.tickCount >= this.targetChangeTime + 600 && (br = this.getLightLevelDependentMagicValue()) > 0.5f && level.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0f < (br - 0.4f) * 2.0f) {
this.setTarget(null);
this.teleport();
}
super.customServerAiStep(level);
}
protected boolean teleport() {
if (this.level().isClientSide() || !this.isAlive()) {
return false;
}
double xx = this.getX() + (this.random.nextDouble() - 0.5) * 64.0;
double yy = this.getY() + (double)(this.random.nextInt(64) - 32);
double zz = this.getZ() + (this.random.nextDouble() - 0.5) * 64.0;
return this.teleport(xx, yy, zz);
}
private boolean teleportTowards(Entity entity) {
Vec3 dir = new Vec3(this.getX() - entity.getX(), this.getY(0.5) - entity.getEyeY(), this.getZ() - entity.getZ());
dir = dir.normalize();
double d = 16.0;
double xx = this.getX() + (this.random.nextDouble() - 0.5) * 8.0 - dir.x * 16.0;
double yy = this.getY() + (double)(this.random.nextInt(16) - 8) - dir.y * 16.0;
double zz = this.getZ() + (this.random.nextDouble() - 0.5) * 8.0 - dir.z * 16.0;
return this.teleport(xx, yy, zz);
}
private boolean teleport(double x, double y, double z) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(x, y, z);
while (pos.getY() > this.level().getMinY() && !this.level().getBlockState(pos).blocksMotion()) {
pos.move(Direction.DOWN);
}
BlockState blockState = this.level().getBlockState(pos);
boolean couldStandOn = blockState.blocksMotion();
boolean isWet = blockState.getFluidState().is(FluidTags.WATER);
if (!couldStandOn || isWet) {
return false;
}
Vec3 oldPos = this.position();
boolean result = this.randomTeleport(x, y, z, true);
if (result) {
this.level().gameEvent(GameEvent.TELEPORT, oldPos, GameEvent.Context.of(this));
if (!this.isSilent()) {
this.level().playSound(null, this.xo, this.yo, this.zo, SoundEvents.ENDERMAN_TELEPORT, this.getSoundSource(), 1.0f, 1.0f);
this.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0f, 1.0f);
}
}
return result;
}
@Override
protected SoundEvent getAmbientSound() {
return this.isCreepy() ? SoundEvents.ENDERMAN_SCREAM : SoundEvents.ENDERMAN_AMBIENT;
}
@Override
protected SoundEvent getHurtSound(DamageSource source) {
return SoundEvents.ENDERMAN_HURT;
}
@Override
protected SoundEvent getDeathSound() {
return SoundEvents.ENDERMAN_DEATH;
}
@Override
protected void dropCustomDeathLoot(ServerLevel level, DamageSource source, boolean killedByPlayer) {
super.dropCustomDeathLoot(level, source, killedByPlayer);
BlockState carryingBlock = this.getCarriedBlock();
if (carryingBlock != null) {
ItemStack fakeTool = new ItemStack(Items.DIAMOND_AXE);
EnchantmentHelper.enchantItemFromProvider(fakeTool, level.registryAccess(), VanillaEnchantmentProviders.ENDERMAN_LOOT_DROP, level.getCurrentDifficultyAt(this.blockPosition()), this.getRandom());
LootParams.Builder params = new LootParams.Builder((ServerLevel)this.level()).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.TOOL, fakeTool).withOptionalParameter(LootContextParams.THIS_ENTITY, this);
List<ItemStack> blockDrops = carryingBlock.getDrops(params);
for (ItemStack itemStack : blockDrops) {
this.spawnAtLocation(level, itemStack);
}
}
}
public void setCarriedBlock(@Nullable BlockState carryingBlock) {
this.entityData.set(DATA_CARRY_STATE, Optional.ofNullable(carryingBlock));
}
public @Nullable BlockState getCarriedBlock() {
return this.entityData.get(DATA_CARRY_STATE).orElse(null);
}
@Override
public boolean hurtServer(ServerLevel level, DamageSource source, float damage) {
AbstractThrownPotion potion;
AbstractThrownPotion thrownPotion;
if (this.isInvulnerableTo(level, source)) {
return false;
}
Entity entity = source.getDirectEntity();
AbstractThrownPotion abstractThrownPotion = thrownPotion = entity instanceof AbstractThrownPotion ? (potion = (AbstractThrownPotion)entity) : null;
if (source.is(DamageTypeTags.IS_PROJECTILE) || thrownPotion != null) {
boolean hurtWithCleanWater = thrownPotion != null && this.hurtWithCleanWater(level, source, thrownPotion, damage);
for (int i = 0; i < 64; ++i) {
if (!this.teleport()) continue;
return true;
}
return hurtWithCleanWater;
}
boolean result = super.hurtServer(level, source, damage);
if (!(source.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0) {
this.teleport();
}
return result;
}
private boolean hurtWithCleanWater(ServerLevel level, DamageSource source, AbstractThrownPotion thrownPotion, float damage) {
ItemStack potionItemStack = thrownPotion.getItem();
PotionContents potionContents = potionItemStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
if (potionContents.is(Potions.WATER)) {
return super.hurtServer(level, source, damage);
}
return false;
}
public boolean isCreepy() {
return this.entityData.get(DATA_CREEPY);
}
public boolean hasBeenStaredAt() {
return this.entityData.get(DATA_STARED_AT);
}
public void setBeingStaredAt() {
this.entityData.set(DATA_STARED_AT, true);
}
@Override
public boolean requiresCustomPersistence() {
return super.requiresCustomPersistence() || this.getCarriedBlock() != null;
}
private static class EndermanFreezeWhenLookedAt
extends Goal {
private final EnderMan enderman;
private @Nullable LivingEntity target;
public EndermanFreezeWhenLookedAt(EnderMan enderman) {
this.enderman = enderman;
this.setFlags(EnumSet.of(Goal.Flag.JUMP, Goal.Flag.MOVE));
}
@Override
public boolean canUse() {
this.target = this.enderman.getTarget();
LivingEntity livingEntity = this.target;
if (!(livingEntity instanceof Player)) {
return false;
}
Player playerTarget = (Player)livingEntity;
double dist = this.target.distanceToSqr(this.enderman);
if (dist > 256.0) {
return false;
}
return this.enderman.isBeingStaredBy(playerTarget);
}
@Override
public void start() {
this.enderman.getNavigation().stop();
}
@Override
public void tick() {
this.enderman.getLookControl().setLookAt(this.target.getX(), this.target.getEyeY(), this.target.getZ());
}
}
private static class EndermanLeaveBlockGoal
extends Goal {
private final EnderMan enderman;
public EndermanLeaveBlockGoal(EnderMan enderman) {
this.enderman = enderman;
}
@Override
public boolean canUse() {
if (this.enderman.getCarriedBlock() == null) {
return false;
}
if (!EndermanLeaveBlockGoal.getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING).booleanValue()) {
return false;
}
return this.enderman.getRandom().nextInt(EndermanLeaveBlockGoal.reducedTickDelay(2000)) == 0;
}
@Override
public void tick() {
RandomSource random = this.enderman.getRandom();
Level level = this.enderman.level();
int xt = Mth.floor(this.enderman.getX() - 1.0 + random.nextDouble() * 2.0);
int yt = Mth.floor(this.enderman.getY() + random.nextDouble() * 2.0);
int zt = Mth.floor(this.enderman.getZ() - 1.0 + random.nextDouble() * 2.0);
BlockPos pos = new BlockPos(xt, yt, zt);
BlockState targetState = level.getBlockState(pos);
BlockPos below = pos.below();
BlockState belowState = level.getBlockState(below);
BlockState carried = this.enderman.getCarriedBlock();
if (carried == null) {
return;
}
if (this.canPlaceBlock(level, pos, carried = Block.updateFromNeighbourShapes(carried, this.enderman.level(), pos), targetState, belowState, below)) {
level.setBlock(pos, carried, 3);
level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(this.enderman, carried));
this.enderman.setCarriedBlock(null);
}
}
private boolean canPlaceBlock(Level level, BlockPos pos, BlockState carried, BlockState targetState, BlockState belowState, BlockPos below) {
return targetState.isAir() && !belowState.isAir() && !belowState.is(Blocks.BEDROCK) && belowState.isCollisionShapeFullBlock(level, below) && carried.canSurvive(level, pos) && level.getEntities(this.enderman, AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos))).isEmpty();
}
}
private static class EndermanTakeBlockGoal
extends Goal {
private final EnderMan enderman;
public EndermanTakeBlockGoal(EnderMan enderman) {
this.enderman = enderman;
}
@Override
public boolean canUse() {
if (this.enderman.getCarriedBlock() != null) {
return false;
}
if (!EndermanTakeBlockGoal.getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING).booleanValue()) {
return false;
}
return this.enderman.getRandom().nextInt(EndermanTakeBlockGoal.reducedTickDelay(20)) == 0;
}
@Override
public void tick() {
RandomSource random = this.enderman.getRandom();
Level level = this.enderman.level();
int xt = Mth.floor(this.enderman.getX() - 2.0 + random.nextDouble() * 4.0);
int yt = Mth.floor(this.enderman.getY() + random.nextDouble() * 3.0);
int zt = Mth.floor(this.enderman.getZ() - 2.0 + random.nextDouble() * 4.0);
BlockPos pos = new BlockPos(xt, yt, zt);
BlockState blockState = level.getBlockState(pos);
Vec3 from = new Vec3((double)this.enderman.getBlockX() + 0.5, (double)yt + 0.5, (double)this.enderman.getBlockZ() + 0.5);
Vec3 to = new Vec3((double)xt + 0.5, (double)yt + 0.5, (double)zt + 0.5);
BlockHitResult result = level.clip(new ClipContext(from, to, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman));
boolean reachable = result.getBlockPos().equals(pos);
if (blockState.is(BlockTags.ENDERMAN_HOLDABLE) && reachable) {
level.removeBlock(pos, false);
level.gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(this.enderman, blockState));
this.enderman.setCarriedBlock(blockState.getBlock().defaultBlockState());
}
}
}
private static class EndermanLookForPlayerGoal
extends NearestAttackableTargetGoal<Player> {
private final EnderMan enderman;
private @Nullable Player pendingTarget;
private int aggroTime;
private int teleportTime;
private final TargetingConditions startAggroTargetConditions;
private final TargetingConditions continueAggroTargetConditions = TargetingConditions.forCombat().ignoreLineOfSight();
private final TargetingConditions.Selector isAngerInducing;
public EndermanLookForPlayerGoal(EnderMan enderman, @Nullable TargetingConditions.Selector isAngryAt) {
super(enderman, Player.class, 10, false, false, isAngryAt);
this.enderman = enderman;
this.isAngerInducing = (target, level) -> (enderman.isBeingStaredBy((Player)target) || enderman.isAngryAt(target, level)) && !enderman.hasIndirectPassenger(target);
this.startAggroTargetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(this.isAngerInducing);
}
@Override
public boolean canUse() {
this.pendingTarget = EndermanLookForPlayerGoal.getServerLevel(this.enderman).getNearestPlayer(this.startAggroTargetConditions.range(this.getFollowDistance()), this.enderman);
return this.pendingTarget != null;
}
@Override
public void start() {
this.aggroTime = this.adjustedTickDelay(5);
this.teleportTime = 0;
this.enderman.setBeingStaredAt();
}
@Override
public void stop() {
this.pendingTarget = null;
super.stop();
}
@Override
public boolean canContinueToUse() {
if (this.pendingTarget != null) {
if (!this.isAngerInducing.test(this.pendingTarget, EndermanLookForPlayerGoal.getServerLevel(this.enderman))) {
return false;
}
this.enderman.lookAt(this.pendingTarget, 10.0f, 10.0f);
return true;
}
if (this.target != null) {
if (this.enderman.hasIndirectPassenger(this.target)) {
return false;
}
if (this.continueAggroTargetConditions.test(EndermanLookForPlayerGoal.getServerLevel(this.enderman), this.enderman, this.target)) {
return true;
}
}
return super.canContinueToUse();
}
@Override
public void tick() {
if (this.enderman.getTarget() == null) {
super.setTarget(null);
}
if (this.pendingTarget != null) {
if (--this.aggroTime <= 0) {
this.target = this.pendingTarget;
this.pendingTarget = null;
super.start();
}
} else {
if (this.target != null && !this.enderman.isPassenger()) {
if (this.enderman.isBeingStaredBy((Player)this.target)) {
if (this.target.distanceToSqr(this.enderman) < 16.0) {
this.enderman.teleport();
}
this.teleportTime = 0;
} else if (this.target.distanceToSqr(this.enderman) > 256.0 && this.teleportTime++ >= this.adjustedTickDelay(30) && this.enderman.teleportTowards(this.target)) {
this.teleportTime = 0;
}
}
super.tick();
}
}
}
}