434 lines
18 KiB
Java
434 lines
18 KiB
Java
package com.atsuishio.superbwarfare.client.overlay;
|
|
|
|
import com.atsuishio.superbwarfare.Mod;
|
|
import com.atsuishio.superbwarfare.client.RenderHelper;
|
|
import com.atsuishio.superbwarfare.client.screens.DogTagEditorScreen;
|
|
import com.atsuishio.superbwarfare.client.tooltip.ClientDogTagImageTooltip;
|
|
import com.atsuishio.superbwarfare.config.client.DisplayConfig;
|
|
import com.atsuishio.superbwarfare.config.client.KillMessageConfig;
|
|
import com.atsuishio.superbwarfare.entity.vehicle.base.ArmedVehicleEntity;
|
|
import com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity;
|
|
import com.atsuishio.superbwarfare.event.KillMessageHandler;
|
|
import com.atsuishio.superbwarfare.init.ModDamageTypes;
|
|
import com.atsuishio.superbwarfare.init.ModItems;
|
|
import com.atsuishio.superbwarfare.item.DogTag;
|
|
import com.atsuishio.superbwarfare.item.gun.GunItem;
|
|
import com.atsuishio.superbwarfare.tools.DamageTypeTool;
|
|
import com.atsuishio.superbwarfare.tools.PlayerKillRecord;
|
|
import com.mojang.blaze3d.platform.GlStateManager;
|
|
import com.mojang.blaze3d.systems.RenderSystem;
|
|
import net.minecraft.ChatFormatting;
|
|
import net.minecraft.client.DeltaTracker;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.Font;
|
|
import net.minecraft.client.gui.GuiGraphics;
|
|
import net.minecraft.client.gui.LayeredDraw;
|
|
import net.minecraft.client.renderer.RenderType;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.damagesource.DamageTypes;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.neoforged.api.distmarker.Dist;
|
|
import net.neoforged.api.distmarker.OnlyIn;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import top.theillusivec4.curios.api.CuriosApi;
|
|
|
|
import javax.annotation.ParametersAreNonnullByDefault;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import static com.atsuishio.superbwarfare.client.RenderHelper.preciseBlit;
|
|
|
|
@OnlyIn(Dist.CLIENT)
|
|
public class KillMessageOverlay implements LayeredDraw.Layer {
|
|
|
|
public static final ResourceLocation ID = Mod.loc("kill_message");
|
|
|
|
private static final ResourceLocation HEADSHOT = Mod.loc("textures/screens/damage_types/headshot.png");
|
|
|
|
private static final ResourceLocation KNIFE = Mod.loc("textures/screens/damage_types/knife.png");
|
|
private static final ResourceLocation EXPLOSION = Mod.loc("textures/screens/damage_types/explosion.png");
|
|
private static final ResourceLocation CLAYMORE = Mod.loc("textures/screens/damage_types/claymore.png");
|
|
private static final ResourceLocation GENERIC = Mod.loc("textures/screens/damage_types/generic.png");
|
|
private static final ResourceLocation BEAST = Mod.loc("textures/screens/damage_types/beast.png");
|
|
private static final ResourceLocation BLEEDING = Mod.loc("textures/screens/damage_types/bleeding.png");
|
|
private static final ResourceLocation SHOCK = Mod.loc("textures/screens/damage_types/shock.png");
|
|
private static final ResourceLocation BLOOD_CRYSTAL = Mod.loc("textures/screens/damage_types/blood_crystal.png");
|
|
private static final ResourceLocation BURN = Mod.loc("textures/screens/damage_types/burn.png");
|
|
private static final ResourceLocation DRONE = Mod.loc("textures/screens/damage_types/drone.png");
|
|
private static final ResourceLocation LASER = Mod.loc("textures/screens/damage_types/laser.png");
|
|
private static final ResourceLocation VEHICLE = Mod.loc("textures/screens/damage_types/vehicle_strike.png");
|
|
|
|
private static final ResourceLocation WORLD_PEACE_STAFF = Mod.loc("textures/gun_icon/compat/world_peace_staff.png");
|
|
|
|
@Override
|
|
@ParametersAreNonnullByDefault
|
|
public void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker) {
|
|
if (!KillMessageConfig.SHOW_KILL_MESSAGE.get()) {
|
|
return;
|
|
}
|
|
|
|
Player player = Minecraft.getInstance().player;
|
|
|
|
if (player == null) {
|
|
return;
|
|
}
|
|
|
|
if (KillMessageHandler.QUEUE.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
int screenWidth = guiGraphics.guiWidth();
|
|
int screenHeight = guiGraphics.guiHeight();
|
|
|
|
var pos = KillMessageConfig.KILL_MESSAGE_POSITION.get();
|
|
int posX = screenWidth;
|
|
float posY = KillMessageConfig.KILL_MESSAGE_MARGIN_Y.get();
|
|
boolean left = false;
|
|
boolean bottom = false;
|
|
|
|
switch (pos) {
|
|
case LEFT_TOP -> {
|
|
posX = KillMessageConfig.KILL_MESSAGE_MARGIN_X.get();
|
|
posY = KillMessageConfig.KILL_MESSAGE_MARGIN_Y.get();
|
|
left = true;
|
|
}
|
|
case RIGHT_TOP -> {
|
|
posX = screenWidth - KillMessageConfig.KILL_MESSAGE_MARGIN_X.get();
|
|
posY = KillMessageConfig.KILL_MESSAGE_MARGIN_Y.get();
|
|
}
|
|
case LEFT_BOTTOM -> {
|
|
posX = KillMessageConfig.KILL_MESSAGE_MARGIN_X.get();
|
|
posY = screenHeight - KillMessageConfig.KILL_MESSAGE_MARGIN_Y.get() - 10;
|
|
left = true;
|
|
bottom = true;
|
|
}
|
|
case RIGHT_BOTTOM -> {
|
|
posX = screenWidth - KillMessageConfig.KILL_MESSAGE_MARGIN_X.get();
|
|
posY = screenHeight - KillMessageConfig.KILL_MESSAGE_MARGIN_Y.get() - 10;
|
|
bottom = true;
|
|
}
|
|
}
|
|
|
|
var arr = KillMessageHandler.QUEUE.toArray(new PlayerKillRecord[0]);
|
|
var record = arr[0];
|
|
|
|
if (record.freeze) {
|
|
for (var playerKillRecord : arr) {
|
|
playerKillRecord.freeze = false;
|
|
}
|
|
}
|
|
|
|
if (record.tick >= 80) {
|
|
if (arr.length > 1 && record.tick - arr[1].tick < (record.fastRemove ? 2 : 20)) {
|
|
arr[1].fastRemove = true;
|
|
record.fastRemove = true;
|
|
for (int j = 1; j < arr.length; j++) {
|
|
arr[j].freeze = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (PlayerKillRecord r : KillMessageHandler.QUEUE) {
|
|
posY = renderKillMessages(r, guiGraphics, deltaTracker.getGameTimeDeltaPartialTick(true), posX, posY, left, bottom);
|
|
}
|
|
}
|
|
|
|
private static float renderKillMessages(PlayerKillRecord record, GuiGraphics guiGraphics, float partialTick, int width, float baseTop, boolean left, boolean bottom) {
|
|
float top = baseTop;
|
|
|
|
Font font = Minecraft.getInstance().font;
|
|
|
|
String targetName = getEntityName(record.target);
|
|
int targetNameWidth = font.width(targetName);
|
|
|
|
guiGraphics.pose().pushPose();
|
|
|
|
RenderSystem.disableDepthTest();
|
|
RenderSystem.depthMask(false);
|
|
RenderSystem.enableBlend();
|
|
RenderSystem.blendFuncSeparate(
|
|
GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE,
|
|
GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE
|
|
);
|
|
|
|
// 入场效果
|
|
if (record.tick < 3) {
|
|
guiGraphics.pose().translate((3 - record.tick - partialTick) * 33 * (left ? -1 : 1), 0, 0);
|
|
}
|
|
|
|
// 4s后开始消失
|
|
if (record.tick >= 80) {
|
|
int animationTickCount = record.fastRemove ? 2 : 20;
|
|
float rate = (float) Math.pow((record.tick + partialTick - 80) / animationTickCount, 5);
|
|
guiGraphics.pose().translate(rate * 100 * (left ? -1 : 1), 0, 0);
|
|
guiGraphics.setColor(1, 1, 1, 1 - rate);
|
|
baseTop += 10 * (1 - rate) * (bottom ? -1 : 1);
|
|
} else {
|
|
baseTop += 10 * (bottom ? -1 : 1);
|
|
}
|
|
|
|
// 击杀提示默认是右对齐的,这里从右向左渲染
|
|
if (!left) {
|
|
float currentPosX = width - targetNameWidth - 10f;
|
|
|
|
// 渲染被击杀者名称
|
|
guiGraphics.drawString(
|
|
Minecraft.getInstance().font,
|
|
targetName,
|
|
currentPosX,
|
|
top,
|
|
record.target.getTeamColor(),
|
|
false
|
|
);
|
|
|
|
// 渲染狗牌图标
|
|
if (record.target instanceof LivingEntity living && shouldRenderDogTagIcon(living)) {
|
|
currentPosX -= 14;
|
|
renderDogTagIcon(guiGraphics, living, currentPosX, top - 0.5f);
|
|
}
|
|
|
|
// 渲染伤害类型图标
|
|
ResourceLocation damageTypeIcon = getDamageTypeIcon(record);
|
|
if (damageTypeIcon != null) {
|
|
currentPosX -= 18;
|
|
preciseBlit(guiGraphics,
|
|
damageTypeIcon,
|
|
currentPosX,
|
|
top - 2,
|
|
0,
|
|
0,
|
|
12,
|
|
12,
|
|
12,
|
|
12
|
|
);
|
|
}
|
|
|
|
// 渲染武器图标
|
|
ResourceLocation currentWeaponIcon = getWeaponIcon(record);
|
|
if (currentWeaponIcon != null) {
|
|
currentPosX -= 36;
|
|
preciseBlit(guiGraphics,
|
|
currentWeaponIcon,
|
|
currentPosX,
|
|
top,
|
|
0,
|
|
0,
|
|
32,
|
|
8,
|
|
-32,
|
|
8
|
|
);
|
|
}
|
|
|
|
// 渲染击杀者名称
|
|
String attackerName = getEntityName(record.attacker);
|
|
currentPosX -= font.width(attackerName) + 6;
|
|
|
|
guiGraphics.drawString(
|
|
Minecraft.getInstance().font,
|
|
attackerName,
|
|
currentPosX,
|
|
top,
|
|
record.attacker.getTeamColor(),
|
|
false
|
|
);
|
|
|
|
// 渲染狗牌图标
|
|
if (shouldRenderDogTagIcon(record.attacker)) {
|
|
currentPosX -= 14;
|
|
renderDogTagIcon(guiGraphics, record.attacker, currentPosX, top - 0.5f);
|
|
}
|
|
} else {
|
|
float currentPosX = width + 10f;
|
|
|
|
// 渲染狗牌图标
|
|
if (shouldRenderDogTagIcon(record.attacker)) {
|
|
renderDogTagIcon(guiGraphics, record.attacker, currentPosX, top - 0.5f);
|
|
currentPosX += 14;
|
|
}
|
|
|
|
// 渲染击杀者名称
|
|
String attackerName = getEntityName(record.attacker);
|
|
guiGraphics.drawString(
|
|
Minecraft.getInstance().font,
|
|
attackerName,
|
|
currentPosX,
|
|
top,
|
|
record.attacker.getTeamColor(),
|
|
false
|
|
);
|
|
|
|
currentPosX += font.width(attackerName) + 6;
|
|
|
|
// 渲染武器图标
|
|
ResourceLocation currentWeaponIcon = getWeaponIcon(record);
|
|
if (currentWeaponIcon != null) {
|
|
preciseBlit(guiGraphics,
|
|
currentWeaponIcon,
|
|
currentPosX,
|
|
top,
|
|
0,
|
|
0,
|
|
32,
|
|
8,
|
|
-32,
|
|
8
|
|
);
|
|
currentPosX += 36;
|
|
}
|
|
|
|
// 渲染伤害类型图标
|
|
ResourceLocation damageTypeIcon = getDamageTypeIcon(record);
|
|
if (damageTypeIcon != null) {
|
|
preciseBlit(guiGraphics,
|
|
damageTypeIcon,
|
|
currentPosX,
|
|
top - 2,
|
|
0,
|
|
0,
|
|
12,
|
|
12,
|
|
12,
|
|
12
|
|
);
|
|
currentPosX += 18;
|
|
}
|
|
|
|
// 渲染狗牌图标
|
|
if (record.target instanceof LivingEntity living && shouldRenderDogTagIcon(living)) {
|
|
renderDogTagIcon(guiGraphics, living, currentPosX, top - 0.5f);
|
|
currentPosX += 14;
|
|
}
|
|
|
|
// 渲染被击杀者名称
|
|
guiGraphics.drawString(
|
|
Minecraft.getInstance().font,
|
|
targetName,
|
|
currentPosX,
|
|
top,
|
|
record.target.getTeamColor(),
|
|
false
|
|
);
|
|
}
|
|
|
|
RenderSystem.defaultBlendFunc();
|
|
RenderSystem.disableBlend();
|
|
RenderSystem.depthMask(true);
|
|
RenderSystem.enableDepthTest();
|
|
|
|
guiGraphics.setColor(1, 1, 1, 1);
|
|
guiGraphics.pose().popPose();
|
|
|
|
return baseTop;
|
|
}
|
|
|
|
@Nullable
|
|
private static ResourceLocation getDamageTypeIcon(PlayerKillRecord record) {
|
|
ResourceLocation icon;
|
|
// 渲染爆头图标
|
|
if (record.headshot) {
|
|
icon = HEADSHOT;
|
|
} else {
|
|
if (DamageTypeTool.isGunDamage(record.damageType)) {
|
|
icon = null;
|
|
} else {
|
|
// 如果是其他伤害,则渲染对应图标
|
|
if (record.damageType == DamageTypes.EXPLOSION || record.damageType == DamageTypes.PLAYER_EXPLOSION || record.damageType == ModDamageTypes.PROJECTILE_BOOM || record.damageType == DamageTypes.FIREWORKS) {
|
|
icon = EXPLOSION;
|
|
} else if (record.damageType == DamageTypes.PLAYER_ATTACK) {
|
|
icon = KNIFE;
|
|
} else if (record.damageType == ModDamageTypes.BEAST) {
|
|
icon = BEAST;
|
|
} else if (record.damageType == ModDamageTypes.MINE) {
|
|
icon = CLAYMORE;
|
|
} else if (record.damageType == ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath("dreamaticvoyage", "bleeding"))) {
|
|
icon = BLEEDING;
|
|
} else if (record.damageType == ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath("dreamaticvoyage", "blood_crystal"))) {
|
|
icon = BLOOD_CRYSTAL;
|
|
} else if (record.damageType == ModDamageTypes.SHOCK) {
|
|
icon = SHOCK;
|
|
} else if (record.damageType == ModDamageTypes.BURN || record.damageType == DamageTypes.IN_FIRE || record.damageType == DamageTypes.ON_FIRE || record.damageType == DamageTypes.LAVA) {
|
|
icon = BURN;
|
|
} else if (record.damageType == ModDamageTypes.DRONE_HIT) {
|
|
icon = DRONE;
|
|
} else if (record.damageType == ModDamageTypes.LASER || record.damageType == ModDamageTypes.LASER_HEADSHOT || record.damageType == ModDamageTypes.LASER_STATIC) {
|
|
icon = LASER;
|
|
} else if (record.damageType == ModDamageTypes.VEHICLE_STRIKE) {
|
|
icon = VEHICLE;
|
|
} else {
|
|
icon = GENERIC;
|
|
}
|
|
}
|
|
}
|
|
return icon;
|
|
}
|
|
|
|
public static String getEntityName(Entity entity) {
|
|
AtomicReference<String> targetName = new AtomicReference<>(entity.getDisplayName().getString());
|
|
if (entity instanceof Player targetPlayer) {
|
|
CuriosApi.getCuriosInventory(targetPlayer)
|
|
.flatMap(c -> c.findFirstCurio(ModItems.DOG_TAG.get()))
|
|
.ifPresent(s -> targetName.set(s.stack().getHoverName().getString()));
|
|
}
|
|
return targetName.get();
|
|
}
|
|
|
|
@Nullable
|
|
public static ResourceLocation getWeaponIcon(PlayerKillRecord record) {
|
|
Player player = record.attacker;
|
|
if (player != null && player.getVehicle() instanceof VehicleEntity vehicleEntity) {
|
|
// 载具图标
|
|
if ((vehicleEntity instanceof ArmedVehicleEntity iArmedVehicle && iArmedVehicle.banHand(player)) || record.damageType == ModDamageTypes.VEHICLE_STRIKE) {
|
|
return vehicleEntity.getVehicleIcon();
|
|
} else {
|
|
if (record.stack.getItem() instanceof GunItem gunItem) {
|
|
return gunItem.getGunIcon();
|
|
}
|
|
}
|
|
} else {
|
|
// 如果是枪械击杀,则渲染枪械图标
|
|
if (record.stack.getItem() instanceof GunItem gunItem) {
|
|
return gunItem.getGunIcon();
|
|
}
|
|
|
|
// TODO 如果是特殊武器击杀,则渲染对应图标
|
|
if (record.stack.getItem().getDescriptionId().equals("item.dreamaticvoyage.world_peace_staff")) {
|
|
return WORLD_PEACE_STAFF;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static boolean shouldRenderDogTagIcon(LivingEntity living) {
|
|
return CuriosApi.getCuriosInventory(living)
|
|
.flatMap(c -> c.findFirstCurio(ModItems.DOG_TAG.get()))
|
|
.map(s -> ClientDogTagImageTooltip.shouldRenderIcon(s.stack()))
|
|
.orElse(false)
|
|
&& DisplayConfig.DOG_TAG_ICON_VISIBLE.get();
|
|
}
|
|
|
|
public static void renderDogTagIcon(GuiGraphics guiGraphics, LivingEntity living, float x, float y) {
|
|
CuriosApi.getCuriosInventory(living).flatMap(c -> c.findFirstCurio(ModItems.DOG_TAG.get())).ifPresent(s -> {
|
|
short[][] icon = DogTag.getColors(s.stack());
|
|
guiGraphics.pose().pushPose();
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
for (int j = 0; j < 16; j++) {
|
|
if (icon[i][j] == -1) continue;
|
|
|
|
var color = ChatFormatting.getById(icon[i][j]);
|
|
RenderHelper.fill(guiGraphics, RenderType.gui(),
|
|
x + i * 0.6f, y + j * 0.6f, x + (i + 1) * 0.6f, y + (j + 1) * 0.6f,
|
|
0, DogTagEditorScreen.getColorFromFormatting(color)
|
|
);
|
|
}
|
|
}
|
|
|
|
guiGraphics.pose().popPose();
|
|
});
|
|
}
|
|
}
|