package com.thebrokenrail.herobrine.entity; import com.thebrokenrail.herobrine.HerobrineRewoven; import com.thebrokenrail.herobrine.config.HardcodedConfig; import com.thebrokenrail.herobrine.data.HerobrineData; import com.thebrokenrail.herobrine.entity.ai.HerobrineAI; import com.thebrokenrail.herobrine.entity.ai.stage.TeleportStage; import net.minecraft.block.BlockState; import net.minecraft.entity.EntityType; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.boss.BossBar; import net.minecraft.entity.boss.ServerBossBar; import net.minecraft.entity.boss.WitherEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandler; import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.MessageType; import net.minecraft.network.PacketByteBuf; import net.minecraft.network.packet.s2c.play.GameMessageS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; import net.minecraft.util.Arm; import net.minecraft.util.Formatting; import net.minecraft.util.Util; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.Difficulty; import net.minecraft.world.GameRules; import net.minecraft.world.World; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; @SuppressWarnings("EntityConstructor") public class HerobrineEntity extends LivingEntity { private static final TrackedDataHandler POSE_HANDLER = new TrackedDataHandler() { @Override public void write(PacketByteBuf data, Pose object) { data.writeEnumConstant(object); } @Override public Pose read(PacketByteBuf packetByteBuf) { return packetByteBuf.readEnumConstant(Pose.class); } @Override public Pose copy(Pose object) { return object; } }; static { TrackedDataHandlerRegistry.register(POSE_HANDLER); } public static final TrackedData POSE = DataTracker.registerData(HerobrineEntity.class, POSE_HANDLER); private final ServerBossBar bossBar; private final List tracking = new ArrayList<>(); private int noPlayerTeleportCountdown = HardcodedConfig.HEROBRINE_NO_PLAYER_TELEPORT_COUNTDOWN; private final HerobrineAI ai = new HerobrineAI(this); public HerobrineEntity(EntityType entityType, World world) { super(entityType, world); bossBar = new ServerBossBar(getDisplayName(), BossBar.Color.RED, BossBar.Style.PROGRESS); setHealth(getMaxHealth()); if (world.random.nextDouble() <= 0.001) { setCustomName(new TranslatableText("entity." + HerobrineRewoven.NAMESPACE + ".herobrine/easter_egg")); } noClip = true; } @Override protected void initDataTracker() { super.initDataTracker(); getDataTracker().startTracking(POSE, Pose.NORMAL); } @Override public void setCustomName(Text name) { super.setCustomName(name); bossBar.setName(name); } @Override public Iterable getArmorItems() { return Collections.emptyList(); } @Override public ItemStack getEquippedStack(EquipmentSlot slot) { if (slot == EquipmentSlot.MAINHAND) { return new ItemStack(Items.DIAMOND_SWORD); } else { return ItemStack.EMPTY; } } @Override public void equipStack(EquipmentSlot slot, ItemStack stack) { } @Override public Arm getMainArm() { return Arm.RIGHT; } @Override public boolean isInvulnerableTo(DamageSource damageSource) { return damageSource == DamageSource.IN_WALL || super.isInvulnerableTo(damageSource); } @Override public boolean hasNoGravity() { return true; } public static DefaultAttributeContainer.Builder createMobAttributes() { return LivingEntity.createLivingAttributes().add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 1.0D).add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.10000000149011612D).add(EntityAttributes.GENERIC_ATTACK_SPEED).add(EntityAttributes.GENERIC_LUCK).add(EntityAttributes.GENERIC_MAX_HEALTH, HardcodedConfig.HEROBRINE_HEALTH); } private int destroyTime = 0; @Override public void tick() { super.tick(); if (!getEntityWorld().isClient()) { if (getEntityWorld().getDifficulty() == Difficulty.PEACEFUL || HerobrineData.get((ServerWorld) getEntityWorld()).set(getUuid())) { // Herobrine Already Exists Or Is Peaceful remove(); } else { ai.tick(); if (destroyTime > 0) { destroyTime--; if (destroyTime == 0 && getEntityWorld().getGameRules().getBoolean(GameRules.DO_MOB_GRIEFING)) { int j = MathHelper.floor(this.getY()); int n = MathHelper.floor(this.getX()); int o = MathHelper.floor(this.getZ()); boolean sendEvent = false; for (int p = -1; p <= 1; p++) { for (int q = -1; q <= 1; q++) { for (int r = 0; r <= 3; r++) { int s = n + p; int t = j + r; int u = o + q; BlockPos blockPos = new BlockPos(s, t, u); BlockState blockState = getEntityWorld().getBlockState(blockPos); if (WitherEntity.canDestroy(blockState)) { sendEvent = getEntityWorld().breakBlock(blockPos, true, this) || sendEvent; } } } } if (sendEvent) { getEntityWorld().syncWorldEvent(null, 1022, this.getBlockPos(), 0); } } } else { destroyTime = 20; } } } bossBar.setPercent(getHealth() / getMaxHealth()); } public String getMessageKey(String id) { return "entity." + HerobrineRewoven.NAMESPACE + ".herobrine.msg." + id; } public void dataTick() { boolean reset = false; if (getAI().getStage() instanceof TeleportStage) { reset = true; } else { if (tracking.size() > 0) { reset = true; } else if (noPlayerTeleportCountdown == 0) { reset = true; List players = getEntityWorld().getPlayers(); if (players.size() > 0 || getNearestPlayer().distanceTo(this) >= HardcodedConfig.HEROBRINE_MAX_DISTANCE) { while (true) { PlayerEntity player = players.get(getEntityWorld().random.nextInt(players.size())); if (player.isAlive()) { getAI().setStage(new TeleportStage(this, player.getUuid())); break; } } } } else { noPlayerTeleportCountdown--; } } if (reset) { noPlayerTeleportCountdown = HardcodedConfig.HEROBRINE_NO_PLAYER_TELEPORT_COUNTDOWN; } } @Override public void onStartedTrackingBy(ServerPlayerEntity player) { super.onStartedTrackingBy(player); bossBar.addPlayer(player); tracking.add(player); } @Override public void onStoppedTrackingBy(ServerPlayerEntity player) { super.onStoppedTrackingBy(player); bossBar.removePlayer(player); tracking.remove(player); } public ServerPlayerEntity getNearestPlayer() { tracking.sort((player1, player2) -> Float.compare(distanceTo(player1), distanceTo(player2))); if (tracking.size() > 0) { int i = 0; while (true) { if (tracking.get(i).isAlive()) { return tracking.get(i); } else { i++; } } } else { return null; } } public static void summon(PlayerEntity player) { HerobrineData data = HerobrineData.get((ServerWorld) player.getEntityWorld()); HerobrineEntity entity = data.get(player.getEntityWorld()); if (entity == null) { ServerWorld world = Objects.requireNonNull(player.getEntityWorld().getServer()).getWorld(World.OVERWORLD); entity = new HerobrineEntity(HerobrineRewoven.HEROBRINE_ENTITY_TYPE, world); entity.updatePosition(0d, 51200d, 0d); assert world != null; Objects.requireNonNull(world.getServer().getWorld(World.OVERWORLD)).spawnEntity(entity); data.set(entity.getUuid()); world.getServer().getPlayerManager().sendToAll(new GameMessageS2CPacket(new TranslatableText("multiplayer.player.joined", entity.getDisplayName()).formatted(Formatting.YELLOW), MessageType.CHAT, Util.NIL_UUID)); entity.getAI().setStage(new TeleportStage(entity, world.getSpawnPos())); world.getServer().getPlayerManager().sendToAll(new GameMessageS2CPacket(new TranslatableText(entity.getMessageKey("spawn"), entity.getDisplayName()), MessageType.CHAT, Util.NIL_UUID)); } } public ServerWorld getServerWorld() { return (ServerWorld) getEntityWorld(); } public HerobrineAI getAI() { return ai; } public void handleStageChange() { getDataTracker().set(POSE, Pose.NORMAL); } @Override public void writeCustomDataToTag(CompoundTag tag) { super.writeCustomDataToTag(tag); tag.put("AI", ai.toTag()); tag.putInt("DestroyTime", destroyTime); } @Override public void readCustomDataFromTag(CompoundTag tag) { super.readCustomDataFromTag(tag); ai.fromTag(tag.getCompound("AI")); destroyTime = tag.getInt("DestroyTime"); } @Override public void setRotation(float yaw, float pitch) { super.setRotation(yaw, pitch); } public void playSound(SoundEvent sound) { getEntityWorld().playSound(null, getX(), getY(), getZ(), sound, SoundCategory.HOSTILE, 1.0f, 1.0f); } public enum Pose { NORMAL, FIREBALL, LIGHTNING } }