Citizens2/src/main/java/net/citizensnpcs/npc/CitizensNPC.java

246 lines
8.3 KiB
Java

package net.citizensnpcs.npc;
import java.util.List;
import javax.annotation.Nullable;
import net.citizensnpcs.EventListen;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.ai.Navigator;
import net.citizensnpcs.api.event.DespawnReason;
import net.citizensnpcs.api.event.NPCDespawnEvent;
import net.citizensnpcs.api.event.NPCSpawnEvent;
import net.citizensnpcs.api.npc.AbstractNPC;
import net.citizensnpcs.api.persistence.PersistenceLoader;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.trait.Spawned;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.npc.ai.CitizensNavigator;
import net.citizensnpcs.trait.CurrentLocation;
import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Messaging;
import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.Util;
import net.minecraft.server.v1_4_5.EntityLiving;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_4_5.entity.CraftLivingEntity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.metadata.FixedMetadataValue;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
public class CitizensNPC extends AbstractNPC {
private EntityController entityController;
private final CitizensNavigator navigator = new CitizensNavigator(this);
private final List<String> removedTraits = Lists.newArrayList();
public CitizensNPC(int id, String name, EntityController entityController) {
super(id, name);
Preconditions.checkNotNull(entityController);
this.entityController = entityController;
}
@Override
public boolean despawn(DespawnReason reason) {
if (!isSpawned())
return false;
NPCDespawnEvent event = new NPCDespawnEvent(this, reason);
if (reason == DespawnReason.CHUNK_UNLOAD)
event.setCancelled(Setting.KEEP_CHUNKS_LOADED.asBoolean());
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
getBukkitEntity().getLocation().getChunk();
// ensure that we are in a loaded chunk.
return false;
}
boolean keepSelected = getTrait(Spawned.class).shouldSpawn();
if (!keepSelected)
data().remove("selectors");
for (Trait trait : traits.values())
trait.onDespawn();
entityController.remove();
return true;
}
@Override
public LivingEntity getBukkitEntity() {
return entityController.getBukkitEntity();
}
@Deprecated
public EntityLiving getHandle() {
return ((CraftLivingEntity) getBukkitEntity()).getHandle();
}
@Override
public Navigator getNavigator() {
return navigator;
}
@Override
public boolean isSpawned() {
return getBukkitEntity() != null;
}
public void load(final DataKey root) {
metadata.loadFrom(root.getRelative("metadata"));
// Load traits
String traitNames = root.getString("traitnames");
Iterable<DataKey> keys = traitNames.isEmpty() ? root.getRelative("traits").getSubKeys() : Iterables
.transform(Splitter.on(',').split(traitNames), new Function<String, DataKey>() {
@Override
public DataKey apply(@Nullable String input) {
return root.getRelative("traits." + input);
}
});
for (DataKey traitKey : keys) {
if (traitKey.keyExists("enabled") && !traitKey.getBoolean("enabled"))
continue;
Class<? extends Trait> clazz = CitizensAPI.getTraitFactory().getTraitClass(traitKey.name());
Trait trait;
if (hasTrait(clazz)) {
trait = getTrait(clazz);
} else {
trait = CitizensAPI.getTraitFactory().getTrait(clazz);
if (trait == null) {
Messaging.severeTr(Messages.SKIPPING_BROKEN_TRAIT, traitKey.name(), getId());
continue;
}
addTrait(trait);
}
loadTrait(trait, traitKey);
}
// Spawn the NPC
CurrentLocation spawnLocation = getTrait(CurrentLocation.class);
if (getTrait(Spawned.class).shouldSpawn() && spawnLocation.getLocation() != null)
spawn(spawnLocation.getLocation());
navigator.load(root.getRelative("navigator"));
}
private void loadTrait(Trait trait, DataKey traitKey) {
try {
trait.load(traitKey);
PersistenceLoader.load(trait, traitKey);
} catch (Throwable ex) {
Messaging.logTr(Messages.TRAIT_LOAD_FAILED, traitKey.name(), getId());
}
}
@Override
public void removeTrait(Class<? extends Trait> clazz) {
Trait present = traits.get(clazz);
if (present != null)
removedTraits.add(present.getName());
super.removeTrait(clazz);
}
private void removeTraitData(DataKey root) {
for (String name : removedTraits) {
root.removeKey("traits." + name);
}
removedTraits.clear();
}
public void save(DataKey root) {
root.setString("name", getFullName());
metadata.saveTo(root.getRelative("metadata"));
navigator.save(root.getRelative("navigator"));
// Save all existing traits
StringBuilder traitNames = new StringBuilder();
for (Trait trait : traits.values()) {
DataKey traitKey = root.getRelative("traits." + trait.getName());
trait.save(traitKey);
PersistenceLoader.save(trait, traitKey);
removedTraits.remove(trait.getName());
traitNames.append(trait.getName() + ",");
}
if (traitNames.length() > 0) {
root.setString("traitnames", traitNames.substring(0, traitNames.length() - 1));
}
removeTraitData(root);
}
public void setEntityController(EntityController newController) {
Preconditions.checkNotNull(newController);
boolean wasSpawned = isSpawned();
Location prev = null;
if (wasSpawned) {
prev = getBukkitEntity().getLocation();
despawn();
}
entityController = newController;
if (wasSpawned)
spawn(prev);
}
@Override
public boolean spawn(Location at) {
Preconditions.checkNotNull(at, "location cannot be null");
if (isSpawned())
return false;
entityController.spawn(at, this);
EntityLiving mcEntity = getHandle();
boolean couldSpawn = !Util.isLoaded(at) ? false : mcEntity.world.addEntity(mcEntity,
SpawnReason.CUSTOM);
if (!couldSpawn) {
// we need to wait for a chunk load before trying to spawn
mcEntity = null;
EventListen.addForRespawn(at, getId());
return true;
}
NPCSpawnEvent spawnEvent = new NPCSpawnEvent(this, at);
Bukkit.getPluginManager().callEvent(spawnEvent);
if (spawnEvent.isCancelled()) {
mcEntity = null;
return false;
}
NMS.setHeadYaw(mcEntity, at.getYaw());
getBukkitEntity().setMetadata(NPC_METADATA_MARKER,
new FixedMetadataValue(CitizensAPI.getPlugin(), true));
// Set the spawned state
getTrait(CurrentLocation.class).setLocation(at);
getTrait(Spawned.class).setSpawned(true);
navigator.onSpawn();
// Modify NPC using traits after the entity has been created
for (Trait trait : traits.values())
trait.onSpawn();
getBukkitEntity().setRemoveWhenFarAway(false);
return true;
}
@Override
public void update() {
try {
super.update();
if (isSpawned()) {
NMS.trySwim(getBukkitEntity());
navigator.run();
}
} catch (Exception ex) {
Messaging.logTr(Messages.EXCEPTION_UPDATING_NPC, getId(), ex.getMessage());
ex.printStackTrace();
}
}
private static final String NPC_METADATA_MARKER = "NPC";
}