2017-12-30 08:36:36 +01:00
|
|
|
package net.citizensnpcs;
|
|
|
|
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map.Entry;
|
|
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.Location;
|
|
|
|
import org.bukkit.Material;
|
2020-04-13 15:56:48 +02:00
|
|
|
import org.bukkit.entity.Entity;
|
2017-12-30 08:36:36 +01:00
|
|
|
import org.bukkit.entity.EntityType;
|
|
|
|
import org.bukkit.entity.FishHook;
|
2018-03-09 13:54:20 +01:00
|
|
|
import org.bukkit.entity.LivingEntity;
|
2017-12-30 08:36:36 +01:00
|
|
|
import org.bukkit.entity.Minecart;
|
|
|
|
import org.bukkit.entity.Player;
|
2019-04-26 17:39:15 +02:00
|
|
|
import org.bukkit.event.Cancellable;
|
2020-06-28 11:36:24 +02:00
|
|
|
import org.bukkit.event.Event;
|
2017-12-30 08:36:36 +01:00
|
|
|
import org.bukkit.event.EventHandler;
|
|
|
|
import org.bukkit.event.EventPriority;
|
|
|
|
import org.bukkit.event.Listener;
|
|
|
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
|
|
import org.bukkit.event.entity.EntityCombustByBlockEvent;
|
|
|
|
import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
|
|
|
import org.bukkit.event.entity.EntityCombustEvent;
|
|
|
|
import org.bukkit.event.entity.EntityDamageByBlockEvent;
|
|
|
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
|
|
import org.bukkit.event.entity.EntityDamageEvent;
|
|
|
|
import org.bukkit.event.entity.EntityDeathEvent;
|
2018-10-01 12:36:12 +02:00
|
|
|
import org.bukkit.event.entity.EntityPortalEvent;
|
2017-12-30 08:36:36 +01:00
|
|
|
import org.bukkit.event.entity.EntityTargetEvent;
|
|
|
|
import org.bukkit.event.entity.PlayerLeashEntityEvent;
|
2018-03-09 13:54:20 +01:00
|
|
|
import org.bukkit.event.entity.PotionSplashEvent;
|
2017-12-30 08:36:36 +01:00
|
|
|
import org.bukkit.event.entity.ProjectileHitEvent;
|
|
|
|
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
|
|
|
import org.bukkit.event.player.PlayerFishEvent;
|
|
|
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|
|
|
import org.bukkit.event.player.PlayerJoinEvent;
|
|
|
|
import org.bukkit.event.player.PlayerMoveEvent;
|
|
|
|
import org.bukkit.event.player.PlayerQuitEvent;
|
|
|
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
|
|
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
|
|
|
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
2020-05-13 07:23:59 +02:00
|
|
|
import org.bukkit.event.vehicle.VehicleDamageEvent;
|
2017-12-30 08:36:36 +01:00
|
|
|
import org.bukkit.event.vehicle.VehicleDestroyEvent;
|
|
|
|
import org.bukkit.event.vehicle.VehicleEnterEvent;
|
|
|
|
import org.bukkit.event.world.ChunkLoadEvent;
|
|
|
|
import org.bukkit.event.world.ChunkUnloadEvent;
|
|
|
|
import org.bukkit.event.world.WorldLoadEvent;
|
|
|
|
import org.bukkit.event.world.WorldUnloadEvent;
|
|
|
|
import org.bukkit.inventory.meta.SkullMeta;
|
|
|
|
import org.bukkit.metadata.FixedMetadataValue;
|
|
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
2020-05-05 12:20:56 +02:00
|
|
|
import org.bukkit.scoreboard.Team;
|
2017-12-30 08:36:36 +01:00
|
|
|
|
|
|
|
import com.google.common.base.Predicates;
|
|
|
|
import com.google.common.collect.ArrayListMultimap;
|
|
|
|
import com.google.common.collect.Iterables;
|
|
|
|
import com.google.common.collect.ListMultimap;
|
2020-04-22 14:25:17 +02:00
|
|
|
import com.google.common.collect.Lists;
|
2017-12-30 08:36:36 +01:00
|
|
|
import com.mojang.authlib.GameProfile;
|
|
|
|
import com.mojang.authlib.properties.Property;
|
|
|
|
|
|
|
|
import net.citizensnpcs.Settings.Setting;
|
|
|
|
import net.citizensnpcs.api.CitizensAPI;
|
|
|
|
import net.citizensnpcs.api.ai.event.NavigationBeginEvent;
|
|
|
|
import net.citizensnpcs.api.ai.event.NavigationCompleteEvent;
|
|
|
|
import net.citizensnpcs.api.event.CitizensDeserialiseMetaEvent;
|
|
|
|
import net.citizensnpcs.api.event.CitizensPreReloadEvent;
|
|
|
|
import net.citizensnpcs.api.event.CitizensSerialiseMetaEvent;
|
|
|
|
import net.citizensnpcs.api.event.CommandSenderCreateNPCEvent;
|
|
|
|
import net.citizensnpcs.api.event.DespawnReason;
|
|
|
|
import net.citizensnpcs.api.event.EntityTargetNPCEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCCombustByBlockEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCCombustByEntityEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCCombustEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCDamageByBlockEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCDamageByEntityEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCDamageEntityEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCDamageEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCDeathEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCDespawnEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCLeftClickEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCRightClickEvent;
|
|
|
|
import net.citizensnpcs.api.event.NPCSpawnEvent;
|
2020-05-13 07:23:59 +02:00
|
|
|
import net.citizensnpcs.api.event.NPCVehicleDamageEvent;
|
2017-12-30 08:36:36 +01:00
|
|
|
import net.citizensnpcs.api.event.PlayerCreateNPCEvent;
|
2018-08-08 10:08:38 +02:00
|
|
|
import net.citizensnpcs.api.event.SpawnReason;
|
2017-12-30 08:36:36 +01:00
|
|
|
import net.citizensnpcs.api.npc.NPC;
|
|
|
|
import net.citizensnpcs.api.npc.NPCRegistry;
|
|
|
|
import net.citizensnpcs.api.trait.trait.Owner;
|
|
|
|
import net.citizensnpcs.api.util.DataKey;
|
|
|
|
import net.citizensnpcs.api.util.Messaging;
|
2018-07-19 14:18:47 +02:00
|
|
|
import net.citizensnpcs.api.util.SpigotUtil;
|
2017-12-30 08:36:36 +01:00
|
|
|
import net.citizensnpcs.editor.Editor;
|
|
|
|
import net.citizensnpcs.npc.skin.SkinUpdateTracker;
|
2020-07-17 03:52:00 +02:00
|
|
|
import net.citizensnpcs.trait.ClickRedirectTrait;
|
2019-09-23 14:29:31 +02:00
|
|
|
import net.citizensnpcs.trait.CommandTrait;
|
2017-12-30 08:36:36 +01:00
|
|
|
import net.citizensnpcs.trait.Controllable;
|
|
|
|
import net.citizensnpcs.trait.CurrentLocation;
|
2019-05-29 10:45:18 +02:00
|
|
|
import net.citizensnpcs.util.ChunkCoord;
|
2017-12-30 08:36:36 +01:00
|
|
|
import net.citizensnpcs.util.Messages;
|
|
|
|
import net.citizensnpcs.util.NMS;
|
2018-04-07 10:02:35 +02:00
|
|
|
import net.citizensnpcs.util.Util;
|
2017-12-30 08:36:36 +01:00
|
|
|
|
|
|
|
public class EventListen implements Listener {
|
|
|
|
private final Map<String, NPCRegistry> registries;
|
|
|
|
private final SkinUpdateTracker skinUpdateTracker;
|
|
|
|
private final ListMultimap<ChunkCoord, NPC> toRespawn = ArrayListMultimap.create();
|
|
|
|
|
|
|
|
EventListen(Map<String, NPCRegistry> registries) {
|
|
|
|
this.registries = registries;
|
2019-01-18 17:18:43 +01:00
|
|
|
this.skinUpdateTracker = new SkinUpdateTracker(registries);
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void checkCreationEvent(CommandSenderCreateNPCEvent event) {
|
|
|
|
if (event.getCreator().hasPermission("citizens.admin.avoid-limits"))
|
|
|
|
return;
|
|
|
|
int limit = Setting.DEFAULT_NPC_LIMIT.asInt();
|
|
|
|
int maxChecks = Setting.MAX_NPC_LIMIT_CHECKS.asInt();
|
|
|
|
for (int i = maxChecks; i >= 0; i--) {
|
|
|
|
if (!event.getCreator().hasPermission("citizens.npc.limit." + i))
|
|
|
|
continue;
|
|
|
|
limit = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (limit < 0)
|
2020-09-14 11:57:58 +02:00
|
|
|
return;
|
2017-12-30 08:36:36 +01:00
|
|
|
int owned = 0;
|
2019-01-18 17:18:43 +01:00
|
|
|
for (NPC npc : CitizensAPI.getNPCRegistry()) {
|
2017-12-30 08:36:36 +01:00
|
|
|
if (!event.getNPC().equals(npc) && npc.hasTrait(Owner.class)
|
2020-09-14 11:57:58 +02:00
|
|
|
&& npc.getTraitNullable(Owner.class).isOwnedBy(event.getCreator())) {
|
2017-12-30 08:36:36 +01:00
|
|
|
owned++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int wouldOwn = owned + 1;
|
|
|
|
if (wouldOwn > limit) {
|
|
|
|
event.setCancelled(true);
|
|
|
|
event.setCancelReason(Messaging.tr(Messages.OVER_NPC_LIMIT, limit));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Iterable<NPC> getAllNPCs() {
|
2019-01-18 17:33:05 +01:00
|
|
|
return Iterables.filter(
|
|
|
|
Iterables.<NPC> concat(CitizensAPI.getNPCRegistry(), Iterables.concat(registries.values())),
|
2017-12-30 08:36:36 +01:00
|
|
|
Predicates.notNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
|
|
public void onChunkLoad(ChunkLoadEvent event) {
|
2019-07-07 08:44:08 +02:00
|
|
|
Runnable runnable = new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2020-06-28 11:36:24 +02:00
|
|
|
respawnAllFromCoord(new ChunkCoord(event.getChunk()), event);
|
2019-07-07 08:44:08 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
if (event instanceof Cancellable) {
|
|
|
|
runnable.run();
|
|
|
|
} else {
|
|
|
|
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), runnable);
|
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
2019-04-28 14:45:38 +02:00
|
|
|
public void onChunkUnload(final ChunkUnloadEvent event) {
|
2020-04-22 14:25:17 +02:00
|
|
|
final List<NPC> toDespawn = Lists.newArrayList();
|
|
|
|
for (Entity entity : event.getChunk().getEntities()) {
|
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity);
|
|
|
|
if (npc == null || !npc.isSpawned())
|
|
|
|
continue;
|
|
|
|
toDespawn.add(npc);
|
|
|
|
}
|
|
|
|
if (toDespawn.isEmpty())
|
|
|
|
return;
|
2020-07-10 09:13:53 +02:00
|
|
|
ChunkCoord coord = new ChunkCoord(event.getChunk());
|
|
|
|
boolean loadChunk = false;
|
|
|
|
for (NPC npc : toDespawn) {
|
|
|
|
if (!npc.despawn(DespawnReason.CHUNK_UNLOAD)) {
|
|
|
|
if (!(event instanceof Cancellable)) {
|
2020-04-21 16:24:13 +02:00
|
|
|
if (Messaging.isDebugging()) {
|
2020-07-10 09:13:53 +02:00
|
|
|
Messaging.debug("Reloading chunk because", npc.getId(), "couldn't despawn");
|
2020-04-21 16:24:13 +02:00
|
|
|
}
|
2020-07-10 09:13:53 +02:00
|
|
|
loadChunk = true;
|
|
|
|
toRespawn.put(coord, npc);
|
|
|
|
continue;
|
2019-04-29 09:58:07 +02:00
|
|
|
}
|
2020-07-10 09:13:53 +02:00
|
|
|
((Cancellable) event).setCancelled(true);
|
|
|
|
if (Messaging.isDebugging()) {
|
|
|
|
Messaging.debug("Cancelled chunk unload at [" + coord.x + "," + coord.z + "]");
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
2020-07-10 09:13:53 +02:00
|
|
|
respawnAllFromCoord(coord, event);
|
|
|
|
return;
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
2020-07-10 09:13:53 +02:00
|
|
|
toRespawn.put(coord, npc);
|
|
|
|
if (Messaging.isDebugging()) {
|
|
|
|
Messaging.debug("Despawned id", npc.getId(),
|
|
|
|
"due to chunk unload at [" + coord.x + "," + coord.z + "]");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (loadChunk) {
|
|
|
|
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (!event.getChunk().isLoaded()) {
|
|
|
|
event.getChunk().load();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 10);
|
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
|
|
public void onCitizensReload(CitizensPreReloadEvent event) {
|
|
|
|
skinUpdateTracker.reset();
|
|
|
|
toRespawn.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onCommandSenderCreateNPC(CommandSenderCreateNPCEvent event) {
|
|
|
|
checkCreationEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Entity events
|
|
|
|
*/
|
|
|
|
@EventHandler
|
|
|
|
public void onEntityCombust(EntityCombustEvent event) {
|
2019-01-18 17:33:05 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null)
|
|
|
|
return;
|
|
|
|
event.setCancelled(npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true));
|
|
|
|
if (event instanceof EntityCombustByEntityEvent) {
|
|
|
|
Bukkit.getPluginManager().callEvent(new NPCCombustByEntityEvent((EntityCombustByEntityEvent) event, npc));
|
|
|
|
} else if (event instanceof EntityCombustByBlockEvent) {
|
|
|
|
Bukkit.getPluginManager().callEvent(new NPCCombustByBlockEvent((EntityCombustByBlockEvent) event, npc));
|
|
|
|
} else {
|
|
|
|
Bukkit.getPluginManager().callEvent(new NPCCombustEvent(event, npc));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onEntityDamage(EntityDamageEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null) {
|
|
|
|
if (event instanceof EntityDamageByEntityEvent) {
|
2019-01-18 17:18:43 +01:00
|
|
|
npc = CitizensAPI.getNPCRegistry().getNPC(((EntityDamageByEntityEvent) event).getDamager());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null)
|
|
|
|
return;
|
|
|
|
event.setCancelled(!npc.data().get(NPC.DAMAGE_OTHERS_METADATA, true));
|
|
|
|
NPCDamageEntityEvent damageEvent = new NPCDamageEntityEvent(npc, (EntityDamageByEntityEvent) event);
|
|
|
|
Bukkit.getPluginManager().callEvent(damageEvent);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
event.setCancelled(npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true));
|
|
|
|
if (event instanceof EntityDamageByEntityEvent) {
|
|
|
|
NPCDamageByEntityEvent damageEvent = new NPCDamageByEntityEvent(npc, (EntityDamageByEntityEvent) event);
|
|
|
|
Bukkit.getPluginManager().callEvent(damageEvent);
|
|
|
|
|
|
|
|
if (!damageEvent.isCancelled() || !(damageEvent.getDamager() instanceof Player))
|
|
|
|
return;
|
|
|
|
Player damager = (Player) damageEvent.getDamager();
|
|
|
|
|
2020-07-17 03:52:00 +02:00
|
|
|
if (npc.hasTrait(ClickRedirectTrait.class)) {
|
2020-09-14 11:57:58 +02:00
|
|
|
npc = npc.getTraitNullable(ClickRedirectTrait.class).getRedirectNPC();
|
2020-07-17 03:52:00 +02:00
|
|
|
if (npc == null)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-30 08:36:36 +01:00
|
|
|
NPCLeftClickEvent leftClickEvent = new NPCLeftClickEvent(npc, damager);
|
|
|
|
Bukkit.getPluginManager().callEvent(leftClickEvent);
|
2019-09-23 14:29:31 +02:00
|
|
|
if (npc.hasTrait(CommandTrait.class)) {
|
2020-09-14 11:57:58 +02:00
|
|
|
npc.getTraitNullable(CommandTrait.class).dispatch(damager, CommandTrait.Hand.LEFT);
|
2019-09-23 14:29:31 +02:00
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
} else if (event instanceof EntityDamageByBlockEvent) {
|
|
|
|
Bukkit.getPluginManager().callEvent(new NPCDamageByBlockEvent(npc, (EntityDamageByBlockEvent) event));
|
|
|
|
} else {
|
|
|
|
Bukkit.getPluginManager().callEvent(new NPCDamageEvent(npc, event));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onEntityDeath(EntityDeathEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
final NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!npc.data().get(NPC.DROPS_ITEMS_METADATA, false)) {
|
|
|
|
event.getDrops().clear();
|
|
|
|
}
|
|
|
|
|
2018-02-18 11:41:22 +01:00
|
|
|
final Location location = npc.getStoredLocation();
|
2017-12-30 08:36:36 +01:00
|
|
|
Bukkit.getPluginManager().callEvent(new NPCDeathEvent(npc, event));
|
|
|
|
npc.despawn(DespawnReason.DEATH);
|
|
|
|
|
2020-05-09 16:15:22 +02:00
|
|
|
int delay = npc.data().get(NPC.RESPAWN_DELAY_METADATA, -1);
|
|
|
|
if (delay < 0)
|
|
|
|
return;
|
|
|
|
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (!npc.isSpawned() && npc.getOwningRegistry().getByUniqueId(npc.getUniqueId()) == npc) {
|
|
|
|
npc.spawn(location, SpawnReason.TIMED_RESPAWN);
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
2020-05-09 16:15:22 +02:00
|
|
|
}
|
|
|
|
}, delay + 2);
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
2018-10-01 12:36:12 +02:00
|
|
|
@EventHandler
|
|
|
|
public void onEntityPortal(EntityPortalEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity());
|
2020-08-30 11:00:33 +02:00
|
|
|
if (npc == null || event.getEntityType() != EntityType.PLAYER)
|
2018-10-01 12:36:12 +02:00
|
|
|
return;
|
|
|
|
event.setCancelled(true);
|
|
|
|
npc.despawn(DespawnReason.PENDING_RESPAWN);
|
|
|
|
event.getTo().getChunk();
|
|
|
|
npc.spawn(event.getTo(), SpawnReason.RESPAWN);
|
|
|
|
}
|
|
|
|
|
2017-12-30 08:36:36 +01:00
|
|
|
@EventHandler(priority = EventPriority.HIGHEST)
|
|
|
|
public void onEntitySpawn(CreatureSpawnEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
if (event.isCancelled() && CitizensAPI.getNPCRegistry().isNPC(event.getEntity())) {
|
2017-12-30 08:36:36 +01:00
|
|
|
event.setCancelled(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onEntityTarget(EntityTargetEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getTarget());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null)
|
|
|
|
return;
|
|
|
|
event.setCancelled(
|
|
|
|
!npc.data().get(NPC.TARGETABLE_METADATA, !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)));
|
|
|
|
Bukkit.getPluginManager().callEvent(new EntityTargetNPCEvent(event, npc));
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onMetaDeserialise(CitizensDeserialiseMetaEvent event) {
|
|
|
|
if (event.getKey().keyExists("skull")) {
|
|
|
|
String owner = event.getKey().getString("skull.owner", "");
|
|
|
|
UUID uuid = event.getKey().keyExists("skull.uuid") ? UUID.fromString(event.getKey().getString("skull.uuid"))
|
|
|
|
: null;
|
|
|
|
if (owner.isEmpty() && uuid == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GameProfile profile = new GameProfile(uuid, owner);
|
|
|
|
for (DataKey sub : event.getKey().getRelative("skull.properties").getSubKeys()) {
|
|
|
|
String propertyName = sub.name();
|
|
|
|
for (DataKey property : sub.getIntegerSubKeys()) {
|
|
|
|
profile.getProperties().put(propertyName,
|
|
|
|
new Property(property.getString("name"), property.getString("value"),
|
|
|
|
property.keyExists("signature") ? property.getString("signature") : null));
|
|
|
|
}
|
|
|
|
}
|
2018-07-19 14:18:47 +02:00
|
|
|
Material mat = SpigotUtil.isUsing1_13API() ? Material.SKELETON_SKULL : Material.valueOf("SKULL_ITEM");
|
|
|
|
SkullMeta meta = (SkullMeta) Bukkit.getItemFactory().getItemMeta(mat);
|
2017-12-30 08:36:36 +01:00
|
|
|
NMS.setProfile(meta, profile);
|
|
|
|
event.getItemStack().setItemMeta(meta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onMetaSerialise(CitizensSerialiseMetaEvent event) {
|
|
|
|
if (!(event.getMeta() instanceof SkullMeta))
|
|
|
|
return;
|
|
|
|
SkullMeta meta = (SkullMeta) event.getMeta();
|
|
|
|
GameProfile profile = NMS.getProfile(meta);
|
|
|
|
if (profile == null)
|
|
|
|
return;
|
|
|
|
if (profile.getName() != null) {
|
|
|
|
event.getKey().setString("skull.owner", profile.getName());
|
|
|
|
}
|
|
|
|
if (profile.getId() != null) {
|
|
|
|
event.getKey().setString("skull.uuid", profile.getId().toString());
|
|
|
|
}
|
|
|
|
if (profile.getProperties() != null) {
|
|
|
|
for (Entry<String, Collection<Property>> entry : profile.getProperties().asMap().entrySet()) {
|
|
|
|
DataKey relative = event.getKey().getRelative("skull.properties." + entry.getKey());
|
|
|
|
int i = 0;
|
|
|
|
for (Property value : entry.getValue()) {
|
|
|
|
relative.getRelative(i).setString("name", value.getName());
|
|
|
|
if (value.getSignature() != null) {
|
|
|
|
relative.getRelative(i).setString("signature", value.getSignature());
|
|
|
|
}
|
|
|
|
relative.getRelative(i).setString("value", value.getValue());
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onNavigationBegin(NavigationBeginEvent event) {
|
|
|
|
skinUpdateTracker.onNPCNavigationBegin(event.getNPC());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onNavigationComplete(NavigationCompleteEvent event) {
|
|
|
|
skinUpdateTracker.onNPCNavigationComplete(event.getNPC());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onNeedsRespawn(NPCNeedsRespawnEvent event) {
|
2019-05-29 10:45:18 +02:00
|
|
|
ChunkCoord coord = new ChunkCoord(event.getSpawnLocation());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (toRespawn.containsEntry(coord, event.getNPC()))
|
|
|
|
return;
|
|
|
|
Messaging.debug("Stored", event.getNPC().getId(), "for respawn from NPCNeedsRespawnEvent");
|
|
|
|
toRespawn.put(coord, event.getNPC());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
|
|
public void onNPCDespawn(NPCDespawnEvent event) {
|
|
|
|
if (event.getReason() == DespawnReason.PLUGIN || event.getReason() == DespawnReason.REMOVAL
|
|
|
|
|| event.getReason() == DespawnReason.RELOAD) {
|
|
|
|
Messaging.debug("Preventing further respawns of " + event.getNPC().getId() + " due to DespawnReason."
|
|
|
|
+ event.getReason().name());
|
|
|
|
if (event.getNPC().getStoredLocation() != null) {
|
2019-05-29 10:45:18 +02:00
|
|
|
toRespawn.remove(new ChunkCoord(event.getNPC().getStoredLocation()), event.getNPC());
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Messaging.debug("Removing " + event.getNPC().getId() + " from skin tracker due to DespawnReason."
|
|
|
|
+ event.getReason().name());
|
|
|
|
}
|
|
|
|
skinUpdateTracker.onNPCDespawn(event.getNPC());
|
2020-07-17 03:52:00 +02:00
|
|
|
if (!Setting.USE_SCOREBOARD_TEAMS.asBoolean())
|
|
|
|
return;
|
|
|
|
String teamName = event.getNPC().data().get(NPC.SCOREBOARD_FAKE_TEAM_NAME_METADATA, "");
|
2020-08-09 07:04:46 +02:00
|
|
|
if (teamName.isEmpty())
|
2020-07-17 03:52:00 +02:00
|
|
|
return;
|
|
|
|
Team team = Util.getDummyScoreboard().getTeam(teamName);
|
2020-08-09 07:04:46 +02:00
|
|
|
event.getNPC().data().remove(NPC.SCOREBOARD_FAKE_TEAM_NAME_METADATA);
|
|
|
|
if (team == null || !(event.getNPC().getEntity() instanceof Player))
|
|
|
|
return;
|
|
|
|
Player player = (Player) event.getNPC().getEntity();
|
|
|
|
if (team.hasPlayer(player)) {
|
2020-07-17 03:52:00 +02:00
|
|
|
if (team.getSize() == 1) {
|
|
|
|
Util.sendTeamPacketToOnlinePlayers(team, 1);
|
|
|
|
team.unregister();
|
|
|
|
} else {
|
|
|
|
team.removePlayer(player);
|
2020-05-05 12:20:56 +02:00
|
|
|
}
|
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onNPCSpawn(NPCSpawnEvent event) {
|
|
|
|
skinUpdateTracker.onNPCSpawn(event.getNPC());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
if (CitizensAPI.getNPCRegistry().getNPC(event.getPlayer()) == null)
|
2017-12-30 08:36:36 +01:00
|
|
|
return;
|
|
|
|
NMS.removeFromServerPlayerList(event.getPlayer());
|
|
|
|
// on teleport, player NPCs are added to the server player list. this is
|
|
|
|
// undesirable as player NPCs are not real players and confuse plugins.
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
|
|
public void onPlayerChangeWorld(PlayerChangedWorldEvent event) {
|
|
|
|
skinUpdateTracker.updatePlayer(event.getPlayer(), 20, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onPlayerCreateNPC(PlayerCreateNPCEvent event) {
|
|
|
|
checkCreationEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onPlayerFish(PlayerFishEvent event) {
|
2019-01-18 17:33:05 +01:00
|
|
|
if (CitizensAPI.getNPCRegistry().isNPC(event.getCaught())
|
|
|
|
&& CitizensAPI.getNPCRegistry().getNPC(event.getCaught()).isProtected()) {
|
2017-12-30 08:36:36 +01:00
|
|
|
event.setCancelled(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
|
|
|
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getRightClicked());
|
2018-04-07 10:02:35 +02:00
|
|
|
if (npc == null || Util.isOffHand(event)) {
|
2017-12-30 08:36:36 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-07-17 03:52:00 +02:00
|
|
|
if (npc.hasTrait(ClickRedirectTrait.class)) {
|
2020-09-14 11:57:58 +02:00
|
|
|
npc = npc.getTraitNullable(ClickRedirectTrait.class).getRedirectNPC();
|
2020-07-17 03:52:00 +02:00
|
|
|
if (npc == null)
|
|
|
|
return;
|
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
Player player = event.getPlayer();
|
|
|
|
NPCRightClickEvent rightClickEvent = new NPCRightClickEvent(npc, player);
|
|
|
|
Bukkit.getPluginManager().callEvent(rightClickEvent);
|
2020-04-20 15:44:50 +02:00
|
|
|
if (rightClickEvent.isCancelled()) {
|
|
|
|
event.setCancelled(true);
|
|
|
|
}
|
2019-09-23 14:29:31 +02:00
|
|
|
if (npc.hasTrait(CommandTrait.class)) {
|
2020-09-14 11:57:58 +02:00
|
|
|
npc.getTraitNullable(CommandTrait.class).dispatch(player, CommandTrait.Hand.RIGHT);
|
2019-09-23 14:29:31 +02:00
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
|
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
|
|
|
skinUpdateTracker.updatePlayer(event.getPlayer(), 6 * 20, true);
|
2020-04-30 14:51:10 +02:00
|
|
|
|
2020-05-01 11:49:25 +02:00
|
|
|
if (Setting.USE_SCOREBOARD_TEAMS.asBoolean()) {
|
2020-04-30 19:47:07 +02:00
|
|
|
Util.updateNPCTeams(event.getPlayer(), 0);
|
2020-04-30 14:51:10 +02:00
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onPlayerLeashEntity(PlayerLeashEntityEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getEntity());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true)) {
|
|
|
|
event.setCancelled(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// recalculate player NPCs the first time a player moves and every time
|
|
|
|
// a player moves a certain distance from their last position.
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
|
|
public void onPlayerMove(final PlayerMoveEvent event) {
|
|
|
|
skinUpdateTracker.onPlayerMove(event.getPlayer());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
|
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
|
|
|
Editor.leave(event.getPlayer());
|
|
|
|
if (event.getPlayer().isInsideVehicle()) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getPlayer().getVehicle());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc != null) {
|
|
|
|
event.getPlayer().leaveVehicle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
skinUpdateTracker.removePlayer(event.getPlayer().getUniqueId());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR)
|
|
|
|
public void onPlayerRespawn(PlayerRespawnEvent event) {
|
|
|
|
skinUpdateTracker.updatePlayer(event.getPlayer(), 15, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onPlayerTeleport(final PlayerTeleportEvent event) {
|
|
|
|
if (event.getCause() == TeleportCause.PLUGIN && !event.getPlayer().hasMetadata("citizens-force-teleporting")
|
2019-01-18 17:33:05 +01:00
|
|
|
&& CitizensAPI.getNPCRegistry().getNPC(event.getPlayer()) != null
|
|
|
|
&& Setting.TELEPORT_DELAY.asInt() > 0) {
|
2017-12-30 08:36:36 +01:00
|
|
|
event.setCancelled(true);
|
|
|
|
Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
event.getPlayer().setMetadata("citizens-force-teleporting",
|
|
|
|
new FixedMetadataValue(CitizensAPI.getPlugin(), true));
|
|
|
|
event.getPlayer().teleport(event.getTo());
|
|
|
|
event.getPlayer().removeMetadata("citizens-force-teleporting", CitizensAPI.getPlugin());
|
|
|
|
}
|
|
|
|
}, Setting.TELEPORT_DELAY.asInt());
|
|
|
|
}
|
|
|
|
skinUpdateTracker.updatePlayer(event.getPlayer(), 15, true);
|
|
|
|
}
|
|
|
|
|
2018-03-09 13:54:20 +01:00
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onPotionSplashEvent(PotionSplashEvent event) {
|
|
|
|
for (LivingEntity entity : event.getAffectedEntities()) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(entity);
|
2018-03-09 13:54:20 +01:00
|
|
|
if (npc == null)
|
|
|
|
continue;
|
|
|
|
if (npc.isProtected()) {
|
|
|
|
event.setIntensity(entity, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-30 08:36:36 +01:00
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onProjectileHit(final ProjectileHitEvent event) {
|
|
|
|
if (!(event.getEntity() instanceof FishHook))
|
|
|
|
return;
|
2019-01-18 17:18:43 +01:00
|
|
|
NMS.removeHookIfNecessary(CitizensAPI.getNPCRegistry(), (FishHook) event.getEntity());
|
2017-12-30 08:36:36 +01:00
|
|
|
new BukkitRunnable() {
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (n++ > 5) {
|
|
|
|
cancel();
|
|
|
|
}
|
2019-01-18 17:18:43 +01:00
|
|
|
NMS.removeHookIfNecessary(CitizensAPI.getNPCRegistry(), (FishHook) event.getEntity());
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
}.runTaskTimer(CitizensAPI.getPlugin(), 0, 1);
|
|
|
|
}
|
|
|
|
|
2020-05-13 07:23:59 +02:00
|
|
|
@EventHandler
|
|
|
|
public void onVehicleDamage(VehicleDamageEvent event) {
|
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getVehicle());
|
|
|
|
if (npc == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
event.setCancelled(npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true));
|
|
|
|
|
|
|
|
NPCVehicleDamageEvent damageEvent = new NPCVehicleDamageEvent(npc, event);
|
|
|
|
Bukkit.getPluginManager().callEvent(damageEvent);
|
|
|
|
|
|
|
|
if (!damageEvent.isCancelled() || !(damageEvent.getDamager() instanceof Player))
|
|
|
|
return;
|
|
|
|
Player damager = (Player) damageEvent.getDamager();
|
|
|
|
|
|
|
|
NPCLeftClickEvent leftClickEvent = new NPCLeftClickEvent(npc, damager);
|
|
|
|
Bukkit.getPluginManager().callEvent(leftClickEvent);
|
|
|
|
if (npc.hasTrait(CommandTrait.class)) {
|
2020-09-14 11:57:58 +02:00
|
|
|
npc.getTraitNullable(CommandTrait.class).dispatch(damager, CommandTrait.Hand.LEFT);
|
2020-05-13 07:23:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-30 08:36:36 +01:00
|
|
|
@EventHandler
|
|
|
|
public void onVehicleDestroy(VehicleDestroyEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getVehicle());
|
2017-12-30 08:36:36 +01:00
|
|
|
if (npc == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
event.setCancelled(npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true));
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
2018-03-09 12:29:59 +01:00
|
|
|
public void onVehicleEnter(final VehicleEnterEvent event) {
|
2019-01-18 17:18:43 +01:00
|
|
|
NPC npc = CitizensAPI.getNPCRegistry().getNPC(event.getVehicle());
|
2019-05-26 09:40:54 +02:00
|
|
|
if (npc == null)
|
|
|
|
return;
|
2020-02-22 05:57:03 +01:00
|
|
|
if ((Util.isHorse(npc.getEntity().getType()) || npc.getEntity().getType() == EntityType.BOAT
|
2018-03-09 12:29:59 +01:00
|
|
|
|| npc.getEntity().getType() == EntityType.PIG || npc.getEntity() instanceof Minecart)
|
2020-09-14 11:57:58 +02:00
|
|
|
&& (!npc.hasTrait(Controllable.class) || !npc.getTraitNullable(Controllable.class).isEnabled())) {
|
2017-12-30 08:36:36 +01:00
|
|
|
event.setCancelled(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(ignoreCancelled = true)
|
|
|
|
public void onWorldLoad(WorldLoadEvent event) {
|
|
|
|
for (ChunkCoord chunk : toRespawn.keySet()) {
|
2018-08-08 10:08:38 +02:00
|
|
|
if (!chunk.worldUUID.equals(event.getWorld().getUID()) || !event.getWorld().isChunkLoaded(chunk.x, chunk.z))
|
2017-12-30 08:36:36 +01:00
|
|
|
continue;
|
2020-06-28 11:36:24 +02:00
|
|
|
respawnAllFromCoord(chunk, event);
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
|
|
public void onWorldUnload(WorldUnloadEvent event) {
|
|
|
|
for (NPC npc : getAllNPCs()) {
|
|
|
|
if (npc == null || !npc.isSpawned() || !npc.getEntity().getWorld().equals(event.getWorld()))
|
|
|
|
continue;
|
|
|
|
boolean despawned = npc.despawn(DespawnReason.WORLD_UNLOAD);
|
|
|
|
if (event.isCancelled() || !despawned) {
|
|
|
|
for (ChunkCoord coord : toRespawn.keySet()) {
|
2018-08-08 01:50:08 +02:00
|
|
|
if (event.getWorld().getUID().equals(coord.worldUUID)) {
|
2020-06-28 11:36:24 +02:00
|
|
|
respawnAllFromCoord(coord, event);
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
event.setCancelled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (npc.isSpawned()) {
|
2020-10-12 14:44:11 +02:00
|
|
|
toRespawn.put(new ChunkCoord(npc.getEntity().getLocation()), npc);
|
2017-12-30 08:36:36 +01:00
|
|
|
Messaging.debug("Despawned", npc.getId() + "due to world unload at", event.getWorld().getName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 11:36:24 +02:00
|
|
|
private void respawnAllFromCoord(ChunkCoord coord, Event event) {
|
2017-12-30 08:36:36 +01:00
|
|
|
List<NPC> ids = toRespawn.get(coord);
|
|
|
|
for (int i = 0; i < ids.size(); i++) {
|
|
|
|
NPC npc = ids.get(i);
|
2019-10-03 07:14:20 +02:00
|
|
|
if (npc.getOwningRegistry().getById(npc.getId()) != npc) {
|
|
|
|
ids.remove(i--);
|
|
|
|
if (Messaging.isDebugging()) {
|
|
|
|
Messaging.debug("Prevented deregistered NPC from respawning", npc.getId());
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2020-05-12 16:13:22 +02:00
|
|
|
if (npc.isSpawned()) {
|
|
|
|
ids.remove(i--);
|
|
|
|
if (Messaging.isDebugging()) {
|
|
|
|
Messaging.debug("NPC", npc.getId(), "already spawned");
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2017-12-30 08:36:36 +01:00
|
|
|
boolean success = spawn(npc);
|
|
|
|
if (!success) {
|
|
|
|
if (Messaging.isDebugging()) {
|
2020-06-28 11:36:24 +02:00
|
|
|
Messaging.debug("Couldn't respawn id", npc.getId(), "during", event,
|
|
|
|
"at [" + coord.x + "," + coord.z + "]");
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
ids.remove(i--);
|
|
|
|
} catch (IndexOutOfBoundsException ex) {
|
|
|
|
// something caused toRespawn to get modified?
|
|
|
|
Messaging.debug("Some strange chunk loading happened while spawning", npc.getId(),
|
|
|
|
" - check all your NPCs in chunk [" + coord.x + "," + coord.z + "] are spawned");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (Messaging.isDebugging()) {
|
2020-06-28 11:36:24 +02:00
|
|
|
Messaging.debug("Spawned id", npc.getId(), "during", event, "at [" + coord.x + "," + coord.z + "]");
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean spawn(NPC npc) {
|
2020-09-14 11:57:58 +02:00
|
|
|
Location spawn = npc.getOrAddTrait(CurrentLocation.class).getLocation();
|
2017-12-30 08:36:36 +01:00
|
|
|
if (spawn == null) {
|
|
|
|
if (Messaging.isDebugging()) {
|
|
|
|
Messaging.debug("Couldn't find a spawn location for despawned NPC id", npc.getId());
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-12 15:06:06 +02:00
|
|
|
return npc.spawn(spawn, SpawnReason.CHUNK_LOAD);
|
2017-12-30 08:36:36 +01:00
|
|
|
}
|
|
|
|
}
|