superb-warfare/src/main/java/com/atsuishio/superbwarfare/event/LivingEventHandler.java
2025-05-11 23:21:15 +08:00

655 lines
27 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.atsuishio.superbwarfare.event;
import com.atsuishio.superbwarfare.api.event.PreKillEvent;
import com.atsuishio.superbwarfare.component.ModDataComponents;
import com.atsuishio.superbwarfare.config.common.GameplayConfig;
import com.atsuishio.superbwarfare.config.server.MiscConfig;
import com.atsuishio.superbwarfare.config.server.VehicleConfig;
import com.atsuishio.superbwarfare.entity.TargetEntity;
import com.atsuishio.superbwarfare.entity.mixin.ICustomKnockback;
import com.atsuishio.superbwarfare.entity.vehicle.LaserTowerEntity;
import com.atsuishio.superbwarfare.entity.vehicle.base.ArmedVehicleEntity;
import com.atsuishio.superbwarfare.entity.vehicle.base.ContainerMobileVehicleEntity;
import com.atsuishio.superbwarfare.entity.vehicle.base.VehicleEntity;
import com.atsuishio.superbwarfare.init.*;
import com.atsuishio.superbwarfare.item.common.ammo.box.AmmoBoxInfo;
import com.atsuishio.superbwarfare.item.gun.GunItem;
import com.atsuishio.superbwarfare.item.gun.data.GunData;
import com.atsuishio.superbwarfare.item.gun.data.value.ReloadState;
import com.atsuishio.superbwarfare.network.message.receive.ClientIndicatorMessage;
import com.atsuishio.superbwarfare.network.message.receive.DrawClientMessage;
import com.atsuishio.superbwarfare.network.message.receive.PlayerGunKillMessage;
import com.atsuishio.superbwarfare.perk.AmmoPerk;
import com.atsuishio.superbwarfare.perk.Perk;
import com.atsuishio.superbwarfare.tools.*;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundStopSoundPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.damagesource.DamageTypes;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.block.entity.HopperBlockEntity;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.common.util.TriState;
import net.neoforged.neoforge.event.entity.living.*;
import net.neoforged.neoforge.event.entity.player.ItemEntityPickupEvent;
import net.neoforged.neoforge.network.PacketDistributor;
import java.util.ArrayList;
import java.util.Objects;
import java.util.stream.Stream;
@EventBusSubscriber
public class LivingEventHandler {
@SubscribeEvent
public static void onEntityAttacked(LivingIncomingDamageEvent event) {
if (!event.getSource().is(ModDamageTypes.VEHICLE_EXPLOSION) && event.getEntity().getVehicle() instanceof VehicleEntity vehicle) {
if (event.getEntity().getVehicle() instanceof ArmedVehicleEntity iArmedVehicle && iArmedVehicle.hidePassenger(event.getEntity())) {
if (!(event.getSource().is(DamageTypes.EXPLOSION)
|| event.getSource().is(DamageTypes.PLAYER_EXPLOSION)
|| event.getSource().is(ModDamageTypes.CUSTOM_EXPLOSION)
|| event.getSource().is(ModDamageTypes.MINE)
|| event.getSource().is(ModDamageTypes.PROJECTILE_BOOM))) {
vehicle.hurt(event.getSource(), event.getContainer().getOriginalDamage());
}
event.setCanceled(true);
}
}
}
@SubscribeEvent
public static void onEntityHurt(LivingIncomingDamageEvent event) {
if (event == null) return;
handleVehicleHurt(event);
handleGunPerksWhenHurt(event);
renderDamageIndicator(event);
reduceDamage(event);
giveExpToWeapon(event);
handleGunLevels(event);
}
@SubscribeEvent
public static void onEntityDeath(LivingDeathEvent event) {
if (event == null) return;
killIndication(event);
handleGunPerksWhenDeath(event);
handlePlayerKillEntity(event);
giveKillExpToWeapon(event);
if (event.getEntity() instanceof Player player) {
handlePlayerBeamReset(player);
}
}
private static void handleVehicleHurt(LivingIncomingDamageEvent event) {
var vehicle = event.getEntity().getVehicle();
if (vehicle instanceof VehicleEntity) {
if (vehicle instanceof ArmedVehicleEntity iArmedVehicle) {
if (iArmedVehicle.hidePassenger(event.getEntity())) {
if (!event.getSource().is(ModDamageTypes.VEHICLE_EXPLOSION)) {
event.setCanceled(true);
}
} else {
if (!(event.getSource().is(DamageTypes.EXPLOSION)
|| event.getSource().is(DamageTypes.PLAYER_EXPLOSION)
|| event.getSource().is(ModDamageTypes.CUSTOM_EXPLOSION)
|| event.getSource().is(ModDamageTypes.MINE)
|| event.getSource().is(ModDamageTypes.PROJECTILE_BOOM))) {
vehicle.hurt(event.getSource(), 0.7f * event.getAmount());
}
event.setAmount(0.3f * event.getAmount());
}
}
}
}
/**
* 计算伤害减免
*/
private static void reduceDamage(LivingIncomingDamageEvent event) {
DamageSource source = event.getSource();
LivingEntity entity = event.getEntity();
Entity sourceEntity = source.getEntity();
if (sourceEntity == null) return;
if (sourceEntity.level().isClientSide) return;
double amount = event.getAmount();
double damage = amount;
ItemStack stack = sourceEntity instanceof LivingEntity living ? living.getMainHandItem() : ItemStack.EMPTY;
// 距离衰减
if (DamageTypeTool.isGunDamage(source) && stack.getItem() instanceof GunItem) {
var data = GunData.from(stack);
double distance = entity.position().distanceTo(sourceEntity.position());
var ammoType = data.ammoTypeInfo().playerAmmoType();
if (ammoType != null) {
switch (ammoType) {
case SHOTGUN -> {
var perk = data.perk.get(Perk.Type.AMMO);
if (perk instanceof AmmoPerk ammoPerk && ammoPerk.slug) {
damage = reduceDamageByDistance(amount, distance, 0.015, 30);
} else {
damage = reduceDamageByDistance(amount, distance, 0.05, 15);
}
}
case SNIPER -> damage = reduceDamageByDistance(amount, distance, 0.001, 150);
case HEAVY -> damage = reduceDamageByDistance(amount, distance, 0.0007, 250);
case HANDGUN -> damage = reduceDamageByDistance(amount, distance, 0.03, 40);
case RIFLE -> damage = reduceDamageByDistance(amount, distance, 0.007, 100);
}
}
// TODO 正确计算距离衰减
if (stack.is(ModTags.Items.SMG)) {
damage = reduceDamageByDistance(amount, distance, 0.02, 50);
} else if (stack.getItem() == ModItems.BOCEK.get()) {
damage = reduceDamageByDistance(amount, distance, 0.007, 100);
}
}
// 计算防弹插板减伤
ItemStack armor = entity.getItemBySlot(EquipmentSlot.CHEST);
var tag = NBTTool.getTag(armor);
if (armor != ItemStack.EMPTY && tag.contains("ArmorPlate")) {
double armorValue;
armorValue = tag.getDouble("ArmorPlate");
tag.putDouble("ArmorPlate", Math.max(armorValue - damage, 0));
NBTTool.saveTag(armor, tag);
damage = Math.max(damage - armorValue, 0);
}
// 计算防弹护具减伤
if (source.is(ModTags.DamageTypes.PROJECTILE) || source.is(DamageTypes.MOB_PROJECTILE)) {
damage *= 1 - 0.8 * Mth.clamp(entity.getAttributeValue(ModAttributes.BULLET_RESISTANCE), 0, 1);
}
if (source.is(ModTags.DamageTypes.PROJECTILE_ABSOLUTE)) {
damage *= 1 - 0.2 * Mth.clamp(entity.getAttributeValue(ModAttributes.BULLET_RESISTANCE), 0, 1);
}
if (source.is(ModDamageTypes.PROJECTILE_BOOM) || source.is(ModDamageTypes.MINE) || source.is(ModDamageTypes.CANNON_FIRE) || source.is(ModDamageTypes.CUSTOM_EXPLOSION)
|| source.is(DamageTypes.EXPLOSION) || source.is(DamageTypes.PLAYER_EXPLOSION)) {
damage *= 1 - 0.3 * Mth.clamp(entity.getAttributeValue(ModAttributes.BULLET_RESISTANCE), 0, 1);
}
event.setAmount((float) damage);
if (entity instanceof TargetEntity && sourceEntity instanceof Player player) {
player.displayClientMessage(Component.translatable("tips.superbwarfare.target.damage",
FormatTool.format2D(damage),
FormatTool.format1D(entity.position().distanceTo(sourceEntity.position())), "m"), false);
}
}
private static double reduceDamageByDistance(double amount, double distance, double rate, double minDistance) {
return amount / (1 + rate * Math.max(0, distance - minDistance));
}
/**
* 根据造成的伤害,提供武器经验
*/
private static void giveExpToWeapon(LivingIncomingDamageEvent event) {
DamageSource source = event.getSource();
Entity sourceEntity = source.getEntity();
if (!(sourceEntity instanceof Player player)) return;
ItemStack stack = player.getMainHandItem();
if (!(stack.getItem() instanceof GunItem)) return;
if (event.getEntity() instanceof TargetEntity) return;
var data = GunData.from(stack);
double amount = Math.min(0.125 * event.getAmount(), event.getEntity().getMaxHealth());
// 先处理发射器类武器或高爆弹的爆炸伤害
if (source.is(ModDamageTypes.PROJECTILE_BOOM)) {
if (stack.is(ModTags.Items.LAUNCHER) || data.perk.getLevel(ModPerks.HE_BULLET) > 0) {
data.exp.set(data.exp.get() + amount);
}
}
// 再判断是不是枪械能造成的伤害
if (!DamageTypeTool.isGunDamage(source)) return;
data.exp.set(data.exp.get() + amount);
data.save();
}
private static void giveKillExpToWeapon(LivingDeathEvent event) {
DamageSource source = event.getSource();
Entity sourceEntity = source.getEntity();
if (!(sourceEntity instanceof Player player)) return;
ItemStack stack = player.getMainHandItem();
if (!(stack.getItem() instanceof GunItem)) return;
if (event.getEntity() instanceof TargetEntity) return;
var data = GunData.from(stack);
double amount = 20 + 2 * event.getEntity().getMaxHealth();
// 先处理发射器类武器或高爆弹的爆炸伤害
if (source.is(ModDamageTypes.PROJECTILE_BOOM)) {
if (stack.is(ModTags.Items.LAUNCHER) || data.perk.getLevel(ModPerks.HE_BULLET) > 0) {
data.exp.set(data.exp.get() + amount);
}
}
// 再判断是不是枪械能造成的伤害
if (DamageTypeTool.isGunDamage(source)) {
data.exp.set(data.exp.get() + amount);
}
// 提升武器等级
int level = data.level.get();
double exp = data.exp.get();
double upgradeExpNeeded = 20 * Math.pow(level, 2) + 160 * level + 20;
while (exp >= upgradeExpNeeded) {
exp -= upgradeExpNeeded;
level = data.level.get() + 1;
upgradeExpNeeded = 20 * Math.pow(level, 2) + 160 * level + 20;
data.exp.set(exp);
data.level.set(level);
data.upgradePoint.set(data.upgradePoint.get() + 0.5);
}
data.save();
}
private static void handleGunLevels(LivingIncomingDamageEvent event) {
DamageSource source = event.getSource();
Entity sourceEntity = source.getEntity();
if (!(sourceEntity instanceof Player player)) return;
ItemStack stack = player.getMainHandItem();
if (!(stack.getItem() instanceof GunItem)) return;
if (event.getEntity() instanceof TargetEntity) return;
var data = GunData.from(stack);
int level = data.level.get();
double exp = data.exp.get();
double upgradeExpNeeded = 20 * Math.pow(level, 2) + 160 * level + 20;
while (exp >= upgradeExpNeeded) {
exp -= upgradeExpNeeded;
level = data.level.get() + 1;
upgradeExpNeeded = 20 * Math.pow(level, 2) + 160 * level + 20;
data.exp.set(exp);
data.level.set(level);
data.upgradePoint.set(data.upgradePoint.get() + 0.5);
}
data.save();
}
private static void killIndication(LivingDeathEvent event) {
DamageSource source = event.getSource();
var sourceEntity = source.getEntity();
if (sourceEntity == null) {
return;
}
// 如果配置不选择全局伤害提示则只在伤害类型为mod添加的时显示指示器
if (!GameplayConfig.GLOBAL_INDICATION.get() && !DamageTypeTool.isModDamage(source)) {
return;
}
if (!sourceEntity.level().isClientSide() && sourceEntity instanceof ServerPlayer player) {
if (NeoForge.EVENT_BUS.post(new PreKillEvent.Indicator(player, source, event.getEntity())).isCanceled()) {
return;
}
SoundTool.playLocalSound(player, ModSounds.TARGET_DOWN.get(), 3f, 1f);
PacketDistributor.sendToPlayer(player, new ClientIndicatorMessage(2, 8));
}
}
private static void renderDamageIndicator(LivingIncomingDamageEvent event) {
if (event == null) return;
var damagesource = event.getSource();
var sourceEntity = damagesource.getEntity();
if (sourceEntity == null) return;
if (sourceEntity instanceof ServerPlayer player && (damagesource.is(DamageTypes.EXPLOSION) || damagesource.is(DamageTypes.PLAYER_EXPLOSION)
|| damagesource.is(ModDamageTypes.MINE) || damagesource.is(ModDamageTypes.PROJECTILE_BOOM))) {
SoundTool.playLocalSound(player, ModSounds.INDICATION.get(), 1f, 1f);
PacketDistributor.sendToPlayer(player, new ClientIndicatorMessage(0, 5));
}
}
/**
* 换弹时切换枪械,取消换弹音效播放
*/
@SubscribeEvent
public static void handleChangeSlot(LivingEquipmentChangeEvent event) {
if (event.getEntity() instanceof Player player && event.getSlot() == EquipmentSlot.MAINHAND) {
if (player.level().isClientSide) return;
ItemStack oldStack = event.getFrom();
ItemStack newStack = event.getTo();
var laserCap = player.getCapability(ModCapabilities.LASER_CAPABILITY);
if (laserCap != null) laserCap.stop();
var oldTag = NBTTool.getTag(oldStack);
var newTag = NBTTool.getTag(newStack);
if (player instanceof ServerPlayer serverPlayer
&& (newStack.getItem() != oldStack.getItem()
|| (newStack.getItem() instanceof GunItem && !GunData.from(newStack).initialized())
|| (oldStack.getItem() instanceof GunItem && !GunData.from(oldStack).initialized())
|| (newStack.getItem() instanceof GunItem && oldStack.getItem() instanceof GunItem && !Objects.equals(GunsTool.getGunUUID(newTag), GunsTool.getGunUUID(oldTag)))
)) {
if (oldStack.getItem() instanceof GunItem oldGun) {
stopGunReloadSound(serverPlayer, oldGun);
var oldData = GunData.from(oldStack);
if (oldData.defaultActionTime() > 0) {
oldData.bolt.actionTimer.reset();
}
oldData.reload.setTime(0);
oldData.reload.setState(ReloadState.NOT_RELOADING);
if (oldData.defaultIterativeTime() != 0) {
oldData.stopped.set(false);
oldData.forceStop.set(false);
oldData.reload.setStage(0);
oldData.reload.prepareTimer.reset();
oldData.reload.prepareLoadTimer.reset();
oldData.reload.iterativeLoadTimer.reset();
oldData.reload.finishTimer.reset();
}
if (oldStack.is(ModItems.SENTINEL.get())) {
oldData.charge.timer.reset();
}
oldData.save();
}
if (newStack.getItem() instanceof GunItem) {
var newData = GunData.from(newStack);
newData.draw.set(true);
newTag = newData.tag();
if (newData.defaultActionTime() > 0) {
newData.bolt.actionTimer.reset();
}
newData.reload.setState(ReloadState.NOT_RELOADING);
newData.reload.reloadTimer.reset();
if (newData.defaultIterativeTime() != 0) {
newData.forceStop.set(false);
newData.stopped.set(false);
newData.reload.setStage(0);
newData.reload.prepareTimer.reset();
newData.reload.prepareLoadTimer.reset();
newData.reload.iterativeLoadTimer.reset();
newData.reload.finishTimer.reset();
}
if (newStack.is(ModItems.SENTINEL.get())) {
newData.charge.timer.reset();
}
for (Perk.Type type : Perk.Type.values()) {
var instance = newData.perk.getInstance(type);
if (instance != null) {
instance.perk().onChangeSlot(newData, instance, player);
}
}
if (player.level() instanceof ServerLevel) {
PacketDistributor.sendToPlayer(serverPlayer, new DrawClientMessage(true));
}
newData.save();
}
}
}
}
private static void stopGunReloadSound(ServerPlayer player, GunItem gun) {
gun.getReloadSound().forEach(sound -> {
var clientboundstopsoundpacket = new ClientboundStopSoundPacket(sound.getLocation(), SoundSource.PLAYERS);
player.connection.send(clientboundstopsoundpacket);
});
}
/**
* 发送击杀消息
*/
private static void handlePlayerKillEntity(LivingDeathEvent event) {
LivingEntity entity = event.getEntity();
DamageSource source = event.getSource();
ResourceKey<DamageType> damageTypeResourceKey = source.typeHolder().unwrapKey().isPresent() ? source.typeHolder().unwrapKey().get() : DamageTypes.GENERIC;
ServerPlayer attacker = null;
if (source.getEntity() instanceof ServerPlayer player) {
attacker = player;
}
if (source.getDirectEntity() instanceof Projectile projectile && projectile.getOwner() instanceof ServerPlayer player) {
attacker = player;
}
if (NeoForge.EVENT_BUS.post(new PreKillEvent.SendKillMessage(attacker, source, entity)).isCanceled()) {
return;
}
if (attacker != null && MiscConfig.SEND_KILL_FEEDBACK.get()) {
if (DamageTypeTool.isHeadshotDamage(source)) {
PacketDistributor.sendToAllPlayers(new PlayerGunKillMessage(attacker.getId(), entity.getId(), true, damageTypeResourceKey));
} else {
PacketDistributor.sendToAllPlayers(new PlayerGunKillMessage(attacker.getId(), entity.getId(), false, damageTypeResourceKey));
}
}
}
private static void handleGunPerksWhenHurt(LivingIncomingDamageEvent event) {
DamageSource source = event.getSource();
Player attacker = null;
if (source.getEntity() instanceof Player player) {
attacker = player;
}
if (source.getDirectEntity() instanceof Projectile projectile && projectile.getOwner() instanceof Player player) {
attacker = player;
}
if (attacker == null) {
return;
}
ItemStack stack = attacker.getMainHandItem();
if (!(stack.getItem() instanceof GunItem)) return;
var data = GunData.from(stack);
float damage = event.getAmount();
for (Perk.Type type : Perk.Type.values()) {
var instance = data.perk.getInstance(type);
if (instance != null) {
damage = instance.perk().getModifiedDamage(damage, data, instance, event.getEntity(), source);
instance.perk().onHit(damage, data, instance, event.getEntity(), source);
if (instance.perk().shouldCancelHurtEvent(damage, data, instance, event.getEntity(), source)) {
event.setCanceled(true);
return;
}
}
}
event.setAmount(damage);
}
private static void handleGunPerksWhenDeath(LivingDeathEvent event) {
DamageSource source = event.getSource();
Player attacker = null;
if (source.getEntity() instanceof Player player) {
attacker = player;
}
if (source.getDirectEntity() instanceof Projectile projectile && projectile.getOwner() instanceof Player player) {
attacker = player;
}
if (attacker == null) {
return;
}
ItemStack stack = attacker.getMainHandItem();
if (!(stack.getItem() instanceof GunItem)) {
return;
}
GunData data = GunData.from(stack);
for (Perk.Type type : Perk.Type.values()) {
var instance = data.perk.getInstance(type);
if (instance != null) {
instance.perk().onKill(data, instance, event.getEntity(), source);
}
}
}
@SubscribeEvent
public static void onPickup(ItemEntityPickupEvent.Pre event) {
if (!VehicleConfig.VEHICLE_ITEM_PICKUP.get()) return;
if (event.getPlayer().getVehicle() instanceof ContainerMobileVehicleEntity containerMobileVehicleEntity) {
var pickUp = event.getItemEntity();
if (!containerMobileVehicleEntity.level().isClientSide) {
HopperBlockEntity.addItem(containerMobileVehicleEntity, pickUp);
}
event.setCanPickup(TriState.FALSE);
}
}
@SubscribeEvent
public static void onLivingDrops(LivingDropsEvent event) {
// 死亡掉落弹药盒
if (event.getEntity() instanceof Player player && !player.level().getLevelData().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY)) {
var cap = player.getData(ModAttachments.PLAYER_VARIABLE).watch();
boolean drop = Stream.of(Ammo.values())
.mapToInt(type -> type.get(cap))
.sum() > 0;
if (drop) {
var stack = new ItemStack(ModItems.AMMO_BOX.get());
for (var type : Ammo.values()) {
type.set(stack, type.get(cap));
type.set(cap, 0);
}
var info = new AmmoBoxInfo("All", true);
stack.set(ModDataComponents.AMMO_BOX_INFO, info);
player.setData(ModAttachments.PLAYER_VARIABLE, cap);
cap.sync(player);
event.getDrops().add(new ItemEntity(player.level(), player.getX(), player.getY() + 1, player.getZ(), stack));
}
}
DamageSource source = event.getSource();
Entity sourceEntity = source.getEntity();
if (!(sourceEntity instanceof Player player)) return;
// 创生物收集掉落物
if (player.getVehicle() instanceof ContainerMobileVehicleEntity containerMobileVehicleEntity && source.is(ModDamageTypes.VEHICLE_STRIKE)) {
var drops = event.getDrops();
var removed = new ArrayList<ItemEntity>();
drops.forEach(itemEntity -> {
ItemStack stack = itemEntity.getItem();
InventoryTool.insertItem(containerMobileVehicleEntity.getItemStacks(), stack);
if (stack.getCount() <= 0) {
player.drop(stack, false);
removed.add(itemEntity);
}
});
drops.removeAll(removed);
}
}
@SubscribeEvent
public static void onLivingExperienceDrop(LivingExperienceDropEvent event) {
Player player = event.getAttackingPlayer();
if (player == null) return;
if (player.getVehicle() instanceof ArmedVehicleEntity) {
player.giveExperiencePoints(event.getDroppedExperience());
event.setCanceled(true);
}
}
public static void handlePlayerBeamReset(Player player) {
var cap = player.getCapability(ModCapabilities.LASER_CAPABILITY);
if (cap != null) {
cap.end();
}
}
@SubscribeEvent
public static void onKnockback(LivingKnockBackEvent event) {
ICustomKnockback knockback = ICustomKnockback.getInstance(event.getEntity());
if (knockback.superbWarfare$getKnockbackStrength() >= 0) {
event.setStrength((float) knockback.superbWarfare$getKnockbackStrength());
}
}
@SubscribeEvent
public static void onEntityFall(LivingFallEvent event) {
LivingEntity living = event.getEntity();
if (living.getVehicle() instanceof VehicleEntity) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void onPreSendKillMessage(PreKillEvent.SendKillMessage event) {
if (event.getSource().getDirectEntity() instanceof LaserTowerEntity && !(event.getTarget() instanceof Player)) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void onPreIndicator(PreKillEvent.Indicator event) {
if (event.getSource().getDirectEntity() instanceof LaserTowerEntity && !(event.getTarget() instanceof Player)) {
event.setCanceled(true);
}
}
@SubscribeEvent
public static void onEffectApply(MobEffectEvent.Applicable event) {
if (event.getEntity().getVehicle() instanceof ArmedVehicleEntity vehicle && vehicle.hidePassenger(event.getEntity())) {
event.setResult(MobEffectEvent.Applicable.Result.DO_NOT_APPLY);
}
}
}