304 lines
11 KiB
Java
304 lines
11 KiB
Java
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> POSE_HANDLER = new TrackedDataHandler<Pose>() {
|
|
@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> POSE = DataTracker.registerData(HerobrineEntity.class, POSE_HANDLER);
|
|
|
|
private final ServerBossBar bossBar;
|
|
|
|
private final List<ServerPlayerEntity> tracking = new ArrayList<>();
|
|
private int noPlayerTeleportCountdown = HardcodedConfig.HEROBRINE_NO_PLAYER_TELEPORT_COUNTDOWN;
|
|
|
|
private final HerobrineAI ai = new HerobrineAI(this);
|
|
|
|
public HerobrineEntity(EntityType<? extends LivingEntity> 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<ItemStack> 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<? extends PlayerEntity> 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
|
|
}
|
|
}
|