package net.aufdemrand.denizen.scripts.commands.entity;
import com.google.common.base.Function;
import net.aufdemrand.denizen.BukkitScriptEntryData;
import net.aufdemrand.denizen.nms.NMSHandler;
import net.aufdemrand.denizen.objects.dEntity;
import net.aufdemrand.denizen.objects.dLocation;
import net.aufdemrand.denizen.objects.dNPC;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizen.utilities.depends.Depends;
import net.aufdemrand.denizencore.exceptions.CommandExecutionException;
import net.aufdemrand.denizencore.exceptions.InvalidArgumentsException;
import net.aufdemrand.denizencore.objects.Element;
import net.aufdemrand.denizencore.objects.aH;
import net.aufdemrand.denizencore.objects.dList;
import net.aufdemrand.denizencore.scripts.ScriptEntry;
import net.aufdemrand.denizencore.scripts.commands.AbstractCommand;
import net.aufdemrand.denizencore.scripts.commands.Holdable;
import net.citizensnpcs.api.ai.Navigator;
import org.bukkit.Location;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class WalkCommand extends AbstractCommand implements Holdable {
// percentage
// walk [location] (speed:#.#) (auto_range)
//
@Override
public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException {
// Interpret arguments
for (aH.Argument arg : aH.interpret(scriptEntry.getArguments())) {
if (!scriptEntry.hasObject("lookat")
&& arg.matchesPrefix("lookat")
&& arg.matchesArgumentType(dLocation.class)) {
scriptEntry.addObject("lookat", arg.asType(dLocation.class));
}
else if (!scriptEntry.hasObject("speed")
&& arg.matchesPrimitive(aH.PrimitiveType.Percentage)
&& arg.matchesPrefix("s, speed")) {
scriptEntry.addObject("speed", arg.asElement());
}
else if (!scriptEntry.hasObject("auto_range")
&& arg.matches("auto_range")) {
scriptEntry.addObject("auto_range", Element.TRUE);
}
else if (!scriptEntry.hasObject("radius")
&& arg.matchesPrimitive(aH.PrimitiveType.Double)
&& arg.matchesPrefix("radius")) {
scriptEntry.addObject("radius", arg.asElement());
}
else if (!scriptEntry.hasObject("stop")
&& arg.matches("stop")) {
scriptEntry.addObject("stop", new Element(true));
}
else if (!scriptEntry.hasObject("location")
&& arg.matchesArgumentType(dLocation.class)) {
scriptEntry.addObject("location", arg.asType(dLocation.class));
}
else if (!scriptEntry.hasObject("entities")
&& arg.matchesArgumentList(dEntity.class)) {
scriptEntry.addObject("entities", arg.asType(dList.class).filter(dEntity.class));
}
else {
arg.reportUnhandled();
}
}
// Check for required information
if (!scriptEntry.hasObject("location") && !scriptEntry.hasObject("stop")) {
throw new InvalidArgumentsException("Must specify a location!");
}
if (!scriptEntry.hasObject("entities")) {
if (((BukkitScriptEntryData) scriptEntry.entryData).getNPC() == null
|| !((BukkitScriptEntryData) scriptEntry.entryData).getNPC().isValid()
|| !((BukkitScriptEntryData) scriptEntry.entryData).getNPC().isSpawned()) {
throw new InvalidArgumentsException("Must have a valid spawned NPC attached.");
}
else {
scriptEntry.addObject("entities",
Arrays.asList(((BukkitScriptEntryData) scriptEntry.entryData).getNPC().getDenizenEntity()));
}
}
scriptEntry.defaultObject("stop", new Element(false));
}
@Override
public void execute(ScriptEntry scriptEntry) throws CommandExecutionException {
// Fetch required objects
dLocation loc = (dLocation) scriptEntry.getObject("location");
Element speed = scriptEntry.getElement("speed");
Element auto_range = scriptEntry.getElement("auto_range");
Element radius = scriptEntry.getElement("radius");
Element stop = scriptEntry.getElement("stop");
List<dEntity> entities = (List<dEntity>) scriptEntry.getObject("entities");
final dLocation lookat = scriptEntry.getdObject("lookat");
// Debug the execution
dB.report(scriptEntry, getName(), (loc != null ? loc.debug() : "")
+ (speed != null ? speed.debug() : "")
+ (auto_range != null ? auto_range.debug() : "")
+ (radius != null ? radius.debug() : "")
+ (lookat != null ? lookat.debug() : "")
+ stop.debug()
+ (aH.debugObj("entities", entities)));
// Do the execution
boolean shouldStop = stop.asBoolean();
List<dNPC> npcs = new ArrayList<dNPC>();
final List<dEntity> waitForEntities = new ArrayList<dEntity>();
for (final dEntity entity : entities) {
if (entity.isCitizensNPC()) {
dNPC npc = entity.getDenizenNPC();
npcs.add(npc);
if (!npc.isSpawned()) {
dB.echoError(scriptEntry.getResidingQueue(), "NPC " + npc.identify() + " is not spawned!");
continue;
}
if (shouldStop) {
npc.getNavigator().cancelNavigation();
continue;
}
if (auto_range != null
&& auto_range == Element.TRUE) {
double distance = npc.getLocation().distance(loc);
if (npc.getNavigator().getLocalParameters().range() < distance + 10) {
npc.getNavigator().getLocalParameters().range((float) distance + 10);
}
}
npc.getNavigator().setTarget(loc);
if (lookat != null) {
npc.getNavigator().getLocalParameters().lookAtFunction(new Function<Navigator, Location>() {
@Override
public Location apply(Navigator nav) {
return lookat;
}
});
}
if (speed != null) {
npc.getNavigator().getLocalParameters().speedModifier(speed.asFloat());
}
if (radius != null) {
npc.getNavigator().getLocalParameters().addRunCallback(WalkCommandCitizensEvents
.generateNewFlocker(npc.getCitizen(), radius.asDouble()));
}
}
else if (shouldStop) {
NMSHandler.getInstance().getEntityHelper().stopWalking(entity.getBukkitEntity());
}
else {
waitForEntities.add(entity);
NMSHandler.getInstance().getEntityHelper().walkTo(entity.getBukkitEntity(), loc, speed != null ? speed.asDouble() : 0.2,
new Runnable() {
@Override
public void run() {
checkHeld(entity);
}
});
}
}
if (scriptEntry.shouldWaitFor()) {
held.add(scriptEntry);
if (!npcs.isEmpty()) {
scriptEntry.addObject("tally", npcs);
}
if (!waitForEntities.isEmpty()) {
scriptEntry.addObject("entities", waitForEntities);
}
}
}
// Held script entries
public static List<ScriptEntry> held = new ArrayList<ScriptEntry>();
public void checkHeld(dEntity entity) {
for (int i = 0; i < held.size(); i++) {
ScriptEntry entry = held.get(i);
List<dEntity> waitForEntities = (List<dEntity>) entry.getObject("entities");
if (waitForEntities == null) {
continue;
}
waitForEntities.remove(entity);
if (waitForEntities.isEmpty()) {
if (!entry.hasObject("tally") || ((List<dNPC>) entry.getObject("tally")).isEmpty()) {
entry.setFinished(true);
held.remove(i);
i--;
}
}
}
}
@Override
public void onEnable() {
if (Depends.citizens != null) {
DenizenAPI.getCurrentInstance().getServer().getPluginManager()
.registerEvents(new WalkCommandCitizensEvents(), DenizenAPI.getCurrentInstance());
}
}
}