fix: stop mobs from targeting NPCs when they are no longer targetable (#3171)

This commit is contained in:
ZX夏夜之风 2024-10-31 22:08:39 +08:00 committed by GitHub
parent c1def34749
commit 81d54bba57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 96 additions and 9 deletions

View File

@ -4,6 +4,7 @@ import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import net.citizensnpcs.trait.TrackTargetedByTrait;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -12,6 +13,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.FishHook; import org.bukkit.entity.FishHook;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Minecart; import org.bukkit.entity.Minecart;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Snowman; import org.bukkit.entity.Snowman;
import org.bukkit.entity.Vehicle; import org.bukkit.entity.Vehicle;
@ -445,15 +447,37 @@ public class EventListen implements Listener {
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onEntityTarget(EntityTargetEvent event) { public void onEntityTarget(EntityTargetEvent event) {
NPC npc = plugin.getNPCRegistry().getNPC(event.getTarget()); final Entity targeted = event.getTarget();
if (npc == null) NPC npc = plugin.getNPCRegistry().getNPC(targeted);
return; final Entity cause = event.getEntity();
if (npc != null) {
final EntityTargetNPCEvent targetNPCEvent = new EntityTargetNPCEvent(event, npc); final EntityTargetNPCEvent targetNPCEvent = new EntityTargetNPCEvent(event, npc);
targetNPCEvent.setCancelled(!npc.data().get(NPC.Metadata.TARGETABLE, !npc.isProtected())); targetNPCEvent.setCancelled(!npc.data().get(NPC.Metadata.TARGETABLE, !npc.isProtected()));
Bukkit.getPluginManager().callEvent(targetNPCEvent); Bukkit.getPluginManager().callEvent(targetNPCEvent);
if (targetNPCEvent.isCancelled()) { if (targetNPCEvent.isCancelled()) {
event.setCancelled(true); event.setCancelled(true);
} else {
if (event.isCancelled()) {
return;
}
if (!(cause instanceof Mob)) {
return;
}
final TrackTargetedByTrait beTargetedBy = npc.getOrAddTrait(TrackTargetedByTrait.class);
beTargetedBy.add(cause.getUniqueId());
}
} else {
if (cause instanceof Mob) {
final LivingEntity previousTarget = ((Mob) cause).getTarget();
if (previousTarget == null) { // normally it is impossible
return;
}
final NPC previousAsNPC = plugin.getNPCRegistry().getNPC(previousTarget);
if (previousAsNPC != null) {
final TrackTargetedByTrait beTargetedBy = previousAsNPC.getOrAddTrait(TrackTargetedByTrait.class);
beTargetedBy.remove(cause.getUniqueId());
}
}
} }
} }

View File

@ -18,6 +18,7 @@ import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.citizensnpcs.trait.TrackTargetedByTrait;
import org.bukkit.Art; import org.bukkit.Art;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -3418,6 +3419,12 @@ public class NPCCommands {
NMS.addOrRemoveFromPlayerList(npc.getEntity(), false); NMS.addOrRemoveFromPlayerList(npc.getEntity(), false);
} }
} }
if (!targetable) {
final TrackTargetedByTrait trackTargetedByTrait = npc.getTraitNullable(TrackTargetedByTrait.class);
if (trackTargetedByTrait != null) { // may not be targeted by anything so prevent possible garbages
trackTargetedByTrait.clearTargets();
}
}
Messaging.sendTr(sender, targetable ? Messages.TARGETABLE_SET : Messages.TARGETABLE_UNSET, npc.getName()); Messaging.sendTr(sender, targetable ? Messages.TARGETABLE_SET : Messages.TARGETABLE_UNSET, npc.getName());
} }

View File

@ -25,6 +25,7 @@ import net.citizensnpcs.trait.Anchors;
import net.citizensnpcs.trait.ArmorStandTrait; import net.citizensnpcs.trait.ArmorStandTrait;
import net.citizensnpcs.trait.AttributeTrait; import net.citizensnpcs.trait.AttributeTrait;
import net.citizensnpcs.trait.BatTrait; import net.citizensnpcs.trait.BatTrait;
import net.citizensnpcs.trait.TrackTargetedByTrait;
import net.citizensnpcs.trait.BoundingBoxTrait; import net.citizensnpcs.trait.BoundingBoxTrait;
import net.citizensnpcs.trait.ClickRedirectTrait; import net.citizensnpcs.trait.ClickRedirectTrait;
import net.citizensnpcs.trait.CommandTrait; import net.citizensnpcs.trait.CommandTrait;
@ -103,6 +104,7 @@ public class CitizensTraitFactory implements TraitFactory {
registerTrait(TraitInfo.create(ItemFrameTrait.class)); registerTrait(TraitInfo.create(ItemFrameTrait.class));
registerTrait(TraitInfo.create(LookClose.class)); registerTrait(TraitInfo.create(LookClose.class));
registerTrait(TraitInfo.create(PaintingTrait.class)); registerTrait(TraitInfo.create(PaintingTrait.class));
registerTrait(TraitInfo.create(TrackTargetedByTrait.class));
registerTrait(TraitInfo.create(MirrorTrait.class).optInToStats()); registerTrait(TraitInfo.create(MirrorTrait.class).optInToStats());
registerTrait(TraitInfo.create(MountTrait.class)); registerTrait(TraitInfo.create(MountTrait.class));
registerTrait(TraitInfo.create(MobType.class).asDefaultTrait()); registerTrait(TraitInfo.create(MobType.class).asDefaultTrait());

View File

@ -0,0 +1,54 @@
package net.citizensnpcs.trait;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@TraitName("tracktargetedby")
public class TrackTargetedByTrait extends Trait {
private Set<UUID> beTargetedBy;
public TrackTargetedByTrait() {
super("tracktargetedby");
}
@Override
public void onDespawn() {
clearTargets();
}
// Only for internal use
public void add(UUID uuid) {
if (beTargetedBy == null) {
beTargetedBy = new HashSet<>();
}
beTargetedBy.add(uuid);
}
// Only for internal use
public void remove(UUID uuid) {
if (beTargetedBy != null) {
beTargetedBy.remove(uuid);
}
}
public void clearTargets() {
if (beTargetedBy != null) {
for (UUID entityUUID : beTargetedBy) {
final Entity entity = Bukkit.getEntity(entityUUID);
if (entity instanceof Mob) {
if (entity.isValid()) {
((Mob) entity).setTarget(null);
}
}
}
beTargetedBy = null;
}
}
}