Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package com.nisovin.magicspells.spells.targeted;

import java.util.Map;
import java.util.UUID;
import java.util.HashMap;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Mob;
import org.bukkit.util.Vector;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.entity.LivingEntity;
import org.bukkit.util.NumberConversions;

import com.nisovin.magicspells.Subspell;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.util.SpellData;
import com.nisovin.magicspells.util.CastResult;
import com.nisovin.magicspells.util.TargetInfo;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.spells.TargetedSpell;
import com.nisovin.magicspells.util.config.ConfigData;
import com.nisovin.magicspells.spells.TargetedEntitySpell;

import com.destroystokyo.paper.entity.Pathfinder;
import com.destroystokyo.paper.event.entity.EntityPathfindEvent;

public class PathfindToSpell extends TargetedSpell implements TargetedEntitySpell {

private static Monitor monitor;

private final ConfigData<Vector> position;

private final ConfigData<Double> speed;
private final ConfigData<Double> distanceAllowed;

private final ConfigData<Boolean> allowInterrupt;

private Subspell arriveSpell;

public PathfindToSpell(MagicConfig config, String spellName) {
super(config, spellName);

position = getConfigDataVector("position", null);

speed = getConfigDataDouble("speed", 1);
distanceAllowed = getConfigDataDouble("distance-allowed", 1);

allowInterrupt = getConfigDataBoolean("allow-interrupt", true);
}

@Override
protected void initialize() {
super.initialize();

if (monitor == null) monitor = new Monitor();

arriveSpell = initSubspell(
getConfigString("spell-on-arrive", null),
"PathfindToSpell '" + internalName + "' has an invalid 'spell-on-arrive' defined.",
true
);
}

@Override
protected void turnOff() {
super.turnOff();

if (monitor == null) return;
monitor.stop();
monitor = null;
}

@Override
public CastResult cast(SpellData data) {
TargetInfo<LivingEntity> info = getTargetedEntity(data);
if (info.noTarget()) return noTarget(info);

return setPath(info.spellData());
}

@Override
public CastResult castAtEntity(SpellData data) {
return setPath(data);
}

private CastResult setPath(SpellData data) {
if (!(data.target() instanceof Mob mob)) return noTarget(data);

Location destination = position.get(data).toLocation(mob.getWorld());

Pathfinder.PathResult path = mob.getPathfinder().findPath(destination);
if (path == null || !path.canReachFinalPoint()) return noTarget(data);

mob.getPathfinder().moveTo(path, speed.get(data));

monitor.track(new MonitorData(
data,
mob.getUniqueId(),
destination,
NumberConversions.square(distanceAllowed.get(data)),
allowInterrupt.get(data),
arriveSpell
));

playSpellEffects(data);
return new CastResult(PostCastAction.HANDLE_NORMALLY, data);
}

private record MonitorData(
SpellData spellData,
UUID target,
Location destination,
double distanceAllowedSquared,
boolean allowInterrupt,
Subspell arriveSpell
) {}

private static class Monitor implements Runnable, Listener {

private final Map<UUID, MonitorData> mobs = new HashMap<>();

private int taskId = -1;

public void track(MonitorData data) {
mobs.put(data.target(), data);
start();
}

public void start() {
if (taskId != -1) return;

MagicSpells.registerEvents(this);
taskId = MagicSpells.scheduleRepeatingTask(this, 0, 1);
}

public void stop() {
if (taskId == -1) return;

EntityPathfindEvent.getHandlerList().unregister(this);
MagicSpells.cancelTask(taskId);
taskId = -1;
}

@Override
public void run() {
mobs.values().removeIf(Monitor::removeIf);
if (mobs.isEmpty()) stop();
}

private static boolean removeIf(MonitorData data) {
if (!(Bukkit.getEntity(data.target()) instanceof Mob mob) || !mob.isValid()) return true;

if (mob.getLocation().distanceSquared(data.destination()) > data.distanceAllowedSquared()) {
Pathfinder.PathResult path = mob.getPathfinder().getCurrentPath();
return path == null || !path.canReachFinalPoint();
}

mob.getPathfinder().stopPathfinding();
if (data.arriveSpell() != null) data.arriveSpell().subcast(data.spellData());

return true;
}

@EventHandler(priority = EventPriority.LOWEST)
private void onPath(EntityPathfindEvent event) {
if (!(event.getEntity() instanceof Mob mob)) return;

MonitorData data = mobs.get(mob.getUniqueId());
if (data == null) return;

if (data.allowInterrupt()) {
mobs.remove(data.target());
return;
}

event.setCancelled(true);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.nisovin.magicspells.spells.targeted;

import org.bukkit.Location;
import org.bukkit.entity.Player;

import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.util.SpellData;
import com.nisovin.magicspells.util.CastResult;
import com.nisovin.magicspells.util.TargetInfo;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.spells.TargetedSpell;
import com.nisovin.magicspells.util.config.ConfigData;
import com.nisovin.magicspells.spells.TargetedLocationSpell;
import com.nisovin.magicspells.util.managers.VariableManager;

public class SaveLocationSpell extends TargetedSpell implements TargetedLocationSpell {

private final ConfigData<String> variableWorld;

private final ConfigData<String> variableX;
private final ConfigData<String> variableY;
private final ConfigData<String> variableZ;

private final ConfigData<String> variableYaw;
private final ConfigData<String> variablePitch;

public SaveLocationSpell(MagicConfig config, String spellName) {
super(config, spellName);

variableWorld = getConfigDataString("variable-world", null);

variableX = getConfigDataString("variable-x", null);
variableY = getConfigDataString("variable-y", null);
variableZ = getConfigDataString("variable-z", null);

variableYaw = getConfigDataString("variable-yaw", null);
variablePitch = getConfigDataString("variable-pitch", null);
}

@Override
public CastResult cast(SpellData data) {
TargetInfo<Location> info = getTargetedBlockLocation(data);
if (info.noTarget()) return noTarget(info);

return castAtLocation(info.spellData());
}

@Override
public CastResult castAtLocation(SpellData data) {
if (!(data.caster() instanceof Player caster)) return new CastResult(PostCastAction.ALREADY_HANDLED, data);

VariableManager manager = MagicSpells.getVariableManager();
Location loc = data.location();

manager.set(variableWorld.get(data), caster, loc.getWorld().getName());

manager.set(variableX.get(data), caster, loc.x());
manager.set(variableZ.get(data), caster, loc.z());
manager.set(variableY.get(data), caster, loc.y());

manager.set(variableYaw.get(data), caster, loc.getYaw());
manager.set(variablePitch.get(data), caster, loc.getPitch());

playSpellEffects(data);
return new CastResult(PostCastAction.HANDLE_NORMALLY, data);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.bukkit.Location;
import org.bukkit.entity.Mob;
import org.bukkit.util.Vector;
import org.bukkit.util.NumberConversions;
import org.bukkit.configuration.ConfigurationSection;

import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -36,16 +37,15 @@ public PathToGoal(Mob mob, SpellData data) {
public boolean initialize(@Nullable ConfigurationSection config) {
if (config == null) return false;
speed = ConfigDataUtil.getDouble(config, "speed", 1);
position = ConfigDataUtil.getVector(config, "position", new Vector());
position = ConfigDataUtil.getVector(config, "position", null);
distanceAllowed = ConfigDataUtil.getDouble(config, "distance-allowed", 1);
return true;
}

private void setLocation() {
Vector position = PathToGoal.this.position.get(data);
if (position == null) return;

location = new Location(mob.getWorld(), position.getX(), position.getY(), position.getZ());
location = position.toLocation(mob.getWorld());
}

@Override
Expand All @@ -54,7 +54,7 @@ public boolean shouldActivate() {
return location != null &&
location.isChunkLoaded() &&
location.getWorld().equals(mob.getWorld()) &&
mob.getLocation().distanceSquared(location) > distanceAllowed.get(data);
mob.getLocation().distanceSquared(location) > NumberConversions.square(distanceAllowed.get(data));
}

@Override
Expand Down
Loading