HTTPS
https://git.nageyqma.icu/nageyqma/blade-addons-next.git
SSH
git@git.nageyqma.icu:nageyqma/blade-addons-next.git
HTTPS 链接可直接 git clone;推送代码请使用 SSH。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
package blade.addon.features.highlight;
import blade.addon.utils.Location;
import blade.addon.utils.data.EntityUtil;
import blade.addon.utils.events.Events;
import blade.addon.utils.rendering.RenderUtils;
import blade.addon.utils.rendering.RenderingEvents;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import config.practical.manager.ConfigValue;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextColor;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.monster.Enderman;
import net.minecraft.world.entity.monster.zombie.Zombie;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.phys.AABB;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class MobHighlight {
public enum MobType {
STAR, TANK, MINI, FEL, ASSASSIN, MIMIC
}
public record DataHolder(Entity entity, MobHighlight.MobType type) {
}
public enum HighlightType {
FILLED("Filled"), OUTLINE("Outline"), BOTH("Both");
private final String label;
HighlightType(String label) {
this.label = label;
}
@Override
public String toString() {
return label;
}
}
private static final int LEATHER_BOOTS_ID = Item.getId(Items.LEATHER_BOOTS);
private static final ConcurrentHashMap<Integer, Entity> foundEntities = new ConcurrentHashMap<>();
public static final ConcurrentLinkedQueue<ArmorStand> nonStaredTags = new ConcurrentLinkedQueue<>();
public static final ConcurrentLinkedQueue<DataHolder> savedEntities = new ConcurrentLinkedQueue<>();
public static boolean dontRenderHighlight = false;
@ConfigValue
public static boolean mobHighlight = false;
@ConfigValue
public static boolean dontShowInvisibleMobs = true;
@ConfigValue
public static HighlightType currentHighlight = HighlightType.FILLED;
@ConfigValue
public static double witherExtraWidth = 0;
@ConfigValue
public static boolean highlightMimicChests = true;
@ConfigValue
public static boolean highlightSheep = false;
@ConfigValue
public static boolean hideSheep = false;
@ConfigValue
public static int outlineWidth = 4;
@ConfigValue
public static boolean hideNoneStaredNameTags = false;
@ConfigValue
public static int starFilledColor = 0xff00ff00;
@ConfigValue
public static int tankFilledColor = 0xffff0000;
@ConfigValue
public static int miniFilledColor = 0xffffff00;
@ConfigValue
public static int felFilledColor = 0xff00ffff;
@ConfigValue
public static int assassinFilledColor = 0xff800080;
@ConfigValue
public static int batFilledColor = 0xff2244ff;
@ConfigValue
public static int witherFilledColor = 0x88222222;
@ConfigValue
public static int mimicFilledColor = 0xffffffff;
@ConfigValue
public static int sheepFilledColor = 0xffffffff;
@ConfigValue
public static int starOutlineColor = 0xff00ff00;
@ConfigValue
public static int tankOutlineColor = 0xffff0000;
@ConfigValue
public static int miniOutlineColor = 0xffffff00;
@ConfigValue
public static int felOutlineColor = 0xff00ffff;
@ConfigValue
public static int assassinOutlineColor = 0xff800080;
@ConfigValue
public static int batOutlineColor = 0xff2244ff;
@ConfigValue
public static int witherOutlineColor = 0x88222222;
@ConfigValue
public static int mimicOutlineColor = 0xffffffff;
@ConfigValue
public static int sheepOutlineColor = 0xffffffff;
public static void init() {
Events.ON_ENTITY_SPAWNED.register((entity, world) -> {
if (entity instanceof ArmorStand) return false;
foundEntities.put(entity.getId(), entity);
return false;
});
Events.ON_ENTITY_TRACKED.register((entity, world) -> {
if (!Location.inDungeon()) return false;
if (entity instanceof Player player) {
if (MobHighlight.isShadowAssassin(player)) {
savedEntities.add(new DataHolder(player, MobType.ASSASSIN));
}
}
if (entity instanceof ArmorStand armorStand) {
testArmorStand(armorStand);
}
return false;
});
Events.ON_LOCATION_CHANGE.register(newLocation -> {
savedEntities.clear();
foundEntities.clear();
nonStaredTags.clear();
return false;
});
ClientTickEvents.END_CLIENT_TICK.register(minecraftClient -> {
LocalPlayer player = minecraftClient.player;
if (player == null) return;
dontRenderHighlight = player.hasEffect(MobEffects.BLINDNESS);
});
ClientEntityEvents.ENTITY_UNLOAD.register((entity, world) -> {
if (entity == null) return;
int id = entity.getId();
savedEntities.removeIf(dataHolder -> dataHolder.entity == entity);
foundEntities.remove(id);
if (entity instanceof ArmorStand) {
nonStaredTags.remove(entity);
}
});
RenderingEvents.FILLED.register(MobHighlight::renderFilled);
RenderingEvents.LINE.register(MobHighlight::renderOutline);
}
private static void testArmorStand(ArmorStand armorStand) {
MobType type = getType(armorStand);
if (type == null) {
if (isGeneralStaredMob(armorStand)) {
nonStaredTags.add(armorStand);
}
return;
}
int idOffset = getIdOffset(armorStand);
if (idOffset < 0) return;
int id = armorStand.getId() - idOffset;
Entity entity = foundEntities.getOrDefault(id, null);
if (entity == null) return;
foundEntities.remove(id);
savedEntities.add(new DataHolder(entity, type));
}
public static MobType getType(ArmorStand armorStand) {
Component text = armorStand.getCustomName();
if (text == null) return null;
String name = text.getString();
if (name.contains("King Midas")) return MobType.MINI;
if (name.contains("Mimic")) return MobType.MIMIC;
if (!MobHighlight.containsStarText(text)) return null;
if (name.contains("Fel")) return MobType.FEL;
if (isMiniBoss(name)) return MobType.MINI;
if (isTankMob(name)) return MobType.TANK;
return MobType.STAR;
}
public static int getIdOffset(ArmorStand armorStand) {
Component text = armorStand.getCustomName();
if (text == null) return -1;
String name = text.getString();
if (name.toLowerCase().contains("withermancer")) return 3;
return 1;
}
public static boolean containsStarText(Component text) {
for (Component sib : text.getSiblings()) {
TextColor color = sib.getStyle().getColor();
if (color == null) continue;
if (color.getValue() == 0xFFAA00 && sib.getString().equals("✯ ")) {
return true;
}
}
return false;
}
public static boolean isGeneralStaredMob(ArmorStand armorStand) {
Component text = armorStand.getCustomName();
if (text == null) return false;
String name = text.getString();
return name.contains("Lurker") || name.contains("Dreadlord") || name.contains("Souleater") || name.contains("Zombie") || name.contains("Skeleton") || name.contains("Skeletor")
|| name.contains("Sniper") || name.contains("Spider") || name.contains("Fel") || isTankMob(name) || isMiniBoss(name);
}
public static boolean isTankMob(String name) {
return name.contains("Zombie Commander") || name.contains("Zombie Lord") || name.contains("Skeleton Lord") || name.contains("Withermancer") || name.contains("Super Archer");
}
public static boolean isMiniBoss(String name) {
return name.contains("Lost Adventurer") || name.contains("Angry Archaeologist") || name.contains("Frozen Adventurer");
}
public static boolean isShadowAssassin(Player player) {
if (EntityUtil.isARealPlayer(player)) return false;
ItemStack heldItem = player.getMainHandItem();
ItemStack boots = player.getInventory().getItem(36);
Component text = heldItem.getCustomName();
if (text == null) return false;
if (!text.getString().equals("Silent Death")) return false;
return Item.getId(boots.getItem()) == LEATHER_BOOTS_ID;
}
public static int getFilledColor(MobType mob) {
return switch (mob) {
case STAR -> starFilledColor;
case TANK -> tankFilledColor;
case MINI -> miniFilledColor;
case FEL -> felFilledColor;
case ASSASSIN -> assassinFilledColor;
case MIMIC -> mimicFilledColor;
};
}
public static int getOutlineColor(MobType mob) {
return switch (mob) {
case STAR -> starOutlineColor;
case TANK -> tankOutlineColor;
case MINI -> miniOutlineColor;
case FEL -> felOutlineColor;
case ASSASSIN -> assassinOutlineColor;
case MIMIC -> mimicOutlineColor;
};
}
public static boolean renderFilled() {
return currentHighlight == HighlightType.BOTH || currentHighlight == HighlightType.FILLED;
}
public static boolean renderOutline() {
return currentHighlight == HighlightType.BOTH || currentHighlight == HighlightType.OUTLINE;
}
public static AABB getBox(Entity entity) {
AABB box = EntityUtil.getBox(entity);
//only shows the head
if (entity instanceof Enderman && entity.isInvisible() && MobHighlight.dontShowInvisibleMobs) {
box = box.inflate(0, -1.8, 0).move(0, -1.2, 0);
}
//bigger mimic highlight
if (entity instanceof Zombie zombie) {
if (zombie.isBaby()) {
box = box.inflate(0.15, 0.2, 0.15);
}
}
return box;
}
private static void renderFilled(LevelRenderContext context, PoseStack matrixStack, VertexConsumer consumer) {
if (!MobHighlight.mobHighlight || dontRenderHighlight || !MobHighlight.renderFilled()) return;
for (DataHolder savedEntity : savedEntities) {
Entity entity = savedEntity.entity;
MobType type = savedEntity.type;
if (entity.isInvisible() && MobHighlight.dontShowInvisibleMobs && entity instanceof Player) return;
AABB box = getBox(entity);
int filledColor = getFilledColor(type);
float[] rgba = RenderUtils.toFloats(filledColor);
RenderUtils.renderFilledBox(matrixStack, consumer,box, rgba);
}
}
private static void renderOutline(LevelRenderContext context, PoseStack matrixStack, VertexConsumer consumer) {
if (!MobHighlight.mobHighlight || dontRenderHighlight || !MobHighlight.renderOutline()) return;
for (DataHolder savedEntity : savedEntities) {
Entity entity = savedEntity.entity;
MobType type = savedEntity.type;
if (entity.isInvisible() && MobHighlight.dontShowInvisibleMobs && entity instanceof Player) return;
AABB box = getBox(entity);
int outlineColor = getOutlineColor(type);
float[] rgba = RenderUtils.toFloats(outlineColor);
RenderUtils.renderOutlinedBox(matrixStack, consumer,box, rgba);
}
}
}