247 lines
8.4 KiB
Java
247 lines
8.4 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.exception.NPCLoadException;
|
|
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 (NPCLoadException 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";
|
|
} |