feat: targetable trait (#3173)

This commit is contained in:
ZX夏夜之风 2024-11-01 23:27:04 +08:00 committed by GitHub
parent 69acf89ff9
commit 5c5cf51b3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 121 additions and 82 deletions

View File

@ -4,6 +4,7 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import net.citizensnpcs.trait.TargetableTrait;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
@ -116,7 +117,6 @@ import net.citizensnpcs.trait.Controllable;
import net.citizensnpcs.trait.CurrentLocation;
import net.citizensnpcs.trait.HologramTrait.HologramRenderer;
import net.citizensnpcs.trait.ShopTrait;
import net.citizensnpcs.trait.TrackTargetedByTrait;
import net.citizensnpcs.trait.versioned.SnowmanTrait;
import net.citizensnpcs.util.ChunkCoord;
import net.citizensnpcs.util.Messages;
@ -452,7 +452,7 @@ public class EventListen implements Listener {
final Entity targeter = event.getEntity();
if (npc != null) {
final EntityTargetNPCEvent targetNPCEvent = new EntityTargetNPCEvent(event, npc);
targetNPCEvent.setCancelled(!npc.data().get(NPC.Metadata.TARGETABLE, !npc.isProtected()));
targetNPCEvent.setCancelled(!npc.getOrAddTrait(TargetableTrait.class).isTargetable());
Bukkit.getPluginManager().callEvent(targetNPCEvent);
if (targetNPCEvent.isCancelled()) {
event.setCancelled(true);
@ -460,12 +460,12 @@ public class EventListen implements Listener {
}
if (event.isCancelled() || !(targeter instanceof Mob))
return;
npc.getOrAddTrait(TrackTargetedByTrait.class).add(targeter.getUniqueId());
npc.getOrAddTrait(TargetableTrait.class).addTargeter(targeter.getUniqueId());
} else if (targeter instanceof Mob) {
final NPC prev = plugin.getNPCRegistry().getNPC(((Mob) targeter).getTarget());
if (prev == null)
return;
prev.getOrAddTrait(TrackTargetedByTrait.class).remove(targeter.getUniqueId());
prev.getOrAddTrait(TargetableTrait.class).removeTargeter(targeter.getUniqueId());
}
}

View File

@ -165,7 +165,7 @@ import net.citizensnpcs.trait.SkinLayers;
import net.citizensnpcs.trait.SkinLayers.Layer;
import net.citizensnpcs.trait.SkinTrait;
import net.citizensnpcs.trait.SlimeSize;
import net.citizensnpcs.trait.TrackTargetedByTrait;
import net.citizensnpcs.trait.TargetableTrait;
import net.citizensnpcs.trait.WitherTrait;
import net.citizensnpcs.trait.WolfModifiers;
import net.citizensnpcs.trait.shop.StoredShops;
@ -3401,12 +3401,9 @@ public class NPCCommands {
flags = "t",
permission = "citizens.npc.targetable")
public void targetable(CommandContext args, CommandSender sender, NPC npc) {
boolean targetable = !npc.data().get(NPC.Metadata.TARGETABLE, npc.isProtected());
if (args.hasFlag('t')) {
npc.data().set(NPC.Metadata.TARGETABLE, targetable);
} else {
npc.data().setPersistent(NPC.Metadata.TARGETABLE, targetable);
}
boolean targetable = !npc.getOrAddTrait(TargetableTrait.class).isTargetable();
boolean persist = !args.hasFlag('t');
npc.getOrAddTrait(TargetableTrait.class).setTargetable(targetable, persist);
if (targetable && npc.getOrAddTrait(MobType.class).getType() == EntityType.PLAYER
&& npc.shouldRemoveFromPlayerList()) {
Messaging.sendTr(sender, Messages.TARGETABLE_PLAYERLIST_WARNING);
@ -3419,12 +3416,6 @@ public class NPCCommands {
NMS.addOrRemoveFromPlayerList(npc.getEntity(), false);
}
}
if (!targetable) {
final TrackTargetedByTrait trait = npc.getTraitNullable(TrackTargetedByTrait.class);
if (trait != null) {
trait.clearTargets();
}
}
Messaging.sendTr(sender, targetable ? Messages.TARGETABLE_SET : Messages.TARGETABLE_UNSET, npc.getName());
}

View File

@ -63,7 +63,7 @@ import net.citizensnpcs.trait.SkinTrait;
import net.citizensnpcs.trait.SleepTrait;
import net.citizensnpcs.trait.SlimeSize;
import net.citizensnpcs.trait.SneakTrait;
import net.citizensnpcs.trait.TrackTargetedByTrait;
import net.citizensnpcs.trait.TargetableTrait;
import net.citizensnpcs.trait.VillagerProfession;
import net.citizensnpcs.trait.WitherTrait;
import net.citizensnpcs.trait.WolfModifiers;
@ -104,6 +104,7 @@ public class CitizensTraitFactory implements TraitFactory {
registerTrait(TraitInfo.create(ItemFrameTrait.class));
registerTrait(TraitInfo.create(LookClose.class));
registerTrait(TraitInfo.create(PaintingTrait.class));
registerTrait(TraitInfo.create(TargetableTrait.class));
registerTrait(TraitInfo.create(MirrorTrait.class).optInToStats());
registerTrait(TraitInfo.create(MountTrait.class));
registerTrait(TraitInfo.create(MobType.class).asDefaultTrait());

View File

@ -0,0 +1,111 @@
package net.citizensnpcs.trait;
import net.citizensnpcs.api.exception.NPCLoadException;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
import net.citizensnpcs.api.util.DataKey;
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("targetable")
public class TargetableTrait extends Trait {
private Set<UUID> targeters;
@Persist
private Boolean state;
private boolean shouldSave;
public TargetableTrait() {
super("targetable");
}
@Override
public void onDespawn() {
clearTargeters();
}
@Override
public void load(DataKey key) throws NPCLoadException {
final Object rawState = key.getRaw("state");
if (rawState instanceof Boolean) {
state = (Boolean) rawState;
shouldSave = true;
}
}
@Override
public void save(DataKey key) {
if (shouldSave) {
if (state != null) {
key.setBoolean("state", state);
}
}
}
public boolean isTargetable() {
if (state == null) {
return !npc.isProtected();
}
return state;
}
public void setTargetable(boolean state, boolean persistent) {
if (this.state != state) {
this.state = state;
this.shouldSave = persistent;
if (!state) {
clearTargeters();
}
} else {
if (!shouldSave && persistent) {
this.shouldSave = true; // user want to persist it so here we go
}
}
}
// Only for internal use
public void addTargeter(UUID uuid) {
if (targeters == null) {
targeters = new HashSet<>();
}
targeters.add(uuid);
}
// Only for internal use
public void removeTargeter(UUID uuid) {
if (targeters != null) {
targeters.remove(uuid);
}
}
public void clearTargeters() {
if (targeters == null) {
return;
}
if (SUPPORTS_GET_ENTITY) {
for (UUID entityUUID : targeters) {
final Entity entity = Bukkit.getEntity(entityUUID);
if (entity instanceof Mob) {
if (entity.isValid()) {
((Mob) entity).setTarget(null);
}
}
}
}
targeters = null;
}
private static boolean SUPPORTS_GET_ENTITY = true;
static {
try {
Bukkit.class.getMethod("getEntity", UUID.class);
} catch (NoSuchMethodException e) {
SUPPORTS_GET_ENTITY = false;
}
}
}

View File

@ -1,64 +0,0 @@
package net.citizensnpcs.trait;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
@TraitName("tracktargetedby")
public class TrackTargetedByTrait extends Trait {
private Set<UUID> targetedBy;
public TrackTargetedByTrait() {
super("tracktargetedby");
}
public void add(UUID uuid) {
if (targetedBy == null) {
targetedBy = new HashSet<>();
}
targetedBy.add(uuid);
}
public void clearTargets() {
if (targetedBy == null)
return;
targetedBy = null;
if (!SUPPORTS_GET_ENTITY)
return;
for (UUID uuid : targetedBy) {
final Entity entity = Bukkit.getEntity(uuid);
if (entity instanceof Mob) {
if (entity.isValid()) {
((Mob) entity).setTarget(null);
}
}
}
}
@Override
public void onDespawn() {
clearTargets();
}
public void remove(UUID uuid) {
if (targetedBy != null) {
targetedBy.remove(uuid);
}
}
private static boolean SUPPORTS_GET_ENTITY = true;
static {
try {
Bukkit.class.getMethod("getEntity", UUID.class);
} catch (NoSuchMethodException e) {
SUPPORTS_GET_ENTITY = false;
}
}
}