Skip to content

Commit

Permalink
add ability to guard NPCs, fixes #294
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Jul 30, 2019
1 parent 99fcb61 commit 5e57f1d
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Sentinel is integrated into by external plugins as well, including:
- /sentinel kill - Kills the NPC.
- /sentinel respawn - Respawns the NPC.
- /sentinel forgive - Forgives all current targets.
- /sentinel guard [PLAYERNAME] - Makes the NPC guard a specific player. Don't specify a player to stop guarding.
- /sentinel guard [PLAYERNAME]/npc:[ID] - Makes the NPC guard a specific player or NPC. Don't specify a player to stop guarding.
- **NPC targeting commands...**
- /sentinel addtarget TYPE - Adds a target.
- /sentinel removetarget TYPE - Removes a target.
Expand Down
119 changes: 83 additions & 36 deletions src/main/java/org/mcmonkey/sentinel/SentinelTrait.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.citizensnpcs.api.ai.TeleportStuckAction;
import net.citizensnpcs.api.ai.speech.SpeechContext;
import net.citizensnpcs.api.event.DespawnReason;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.persistence.PersistenceLoader;
import net.citizensnpcs.api.persistence.Persister;
Expand Down Expand Up @@ -337,6 +338,12 @@ public void save(SentinelTargetList o, DataKey dataKey) {
@Persist("guardingLower")
public long guardingLower = 0;

/**
* ID of an NPC to guard, if any.
*/
@Persist("guardedNPC")
public int guardedNPC = -1;

/**
* Whether the NPC needs ammo to fire ranged weapons (otherwise, infinite ammo).
*/
Expand Down Expand Up @@ -467,17 +474,34 @@ public void save(SentinelTargetList o, DataKey dataKey) {
* Null indicates not guarding anyone.
*/
public UUID getGuarding() {
if (guardedNPC >= 0) {
NPC npc = CitizensAPI.getNPCRegistry().getById(guardedNPC);
if (npc != null) {
return npc.getUniqueId();
}
}
if (guardingLower == 0 && guardingUpper == 0) {
return null;
}
return new UUID(guardingUpper, guardingLower);
}

/**
* Sets the NPC to be guarding a player.
* Sets the NPC to be guarding an NPC.
* -1 indicates not guarding anyone.
*/
public void setGuarding(int npcID) {
guardingUpper = 0;
guardingLower = 0;
guardedNPC = npcID;
}

/**
* Sets the NPC to be guarding an entity.
* Null indicates not guarding anyone.
*/
public void setGuarding(UUID uuid) {
guardedNPC = -1;
if (uuid == null) {
guardingUpper = 0;
guardingLower = 0;
Expand Down Expand Up @@ -1028,6 +1052,33 @@ public void pauseWaypoints() {
*/
public boolean needsSafeReturn = true;

/**
* Gets the entity this NPC is guarding, or null.
*/
public LivingEntity getGuardingEntity() {
if (guardedNPC >= 0) {
NPC npc = CitizensAPI.getNPCRegistry().getById(guardedNPC);
if (npc != null && npc.isSpawned()) {
return (LivingEntity) npc.getEntity();
}
}
UUID guardId = getGuarding();
if (guardId == null) {
return null;
}
Player player = Bukkit.getPlayer(guardId);
if (player != null) {
return player;
}
if (SentinelTarget.v1_12) {
Entity entity = Bukkit.getEntity(guardId);
if (entity instanceof LivingEntity) {
return (LivingEntity) entity;
}
}
return null;
}

/**
* Runs a full update cycle on the NPC.
*/
Expand All @@ -1037,16 +1088,17 @@ public void runUpdate() {
ticksSinceLastBurn += SentinelPlugin.instance.tickRate;
timeSinceAttack += SentinelPlugin.instance.tickRate;
timeSinceHeal += SentinelPlugin.instance.tickRate;
LivingEntity guarded = getGuardingEntity();
// Protection against falling below the world
if (getLivingEntity().getLocation().getY() <= 0) {
if (SentinelPlugin.debugMe) {
debug("Injuring self, I'm below the map!");
}
getLivingEntity().damage(1);
if (!npc.isSpawned()) {
if (getGuarding() != null && Bukkit.getPlayer(getGuarding()) != null) {
if (guarded != null) {
if (respawnTime > 0 && respawnMe == null) {
npc.spawn(Bukkit.getPlayer(getGuarding()).getLocation());
npc.spawn(guarded.getLocation());
}
}
return;
Expand All @@ -1065,7 +1117,7 @@ public void runUpdate() {
if (!npc.getNavigator().isNavigating()) {
pathingTo = null;
}
if ((getGuarding() != null || chasing != null || pathingTo != null) && npc.hasTrait(Waypoints.class)) {
if ((guarded != null || chasing != null || pathingTo != null) && npc.hasTrait(Waypoints.class)) {
pauseWaypoints();
}
else if (needsToUnpause && npc.hasTrait(Waypoints.class)) {
Expand Down Expand Up @@ -1136,34 +1188,31 @@ else if (chasing == null) {
specialUnmarkVision();
}
// Special guarding handling
if (getGuarding() != null) {
Player player = Bukkit.getPlayer(getGuarding());
if (player != null) {
Location myLoc = getLivingEntity().getLocation();
Location theirLoc = player.getLocation();
double dist = theirLoc.getWorld().equals(myLoc.getWorld()) ? myLoc.distanceSquared(theirLoc) : MAX_DIST;
if (dist > 60 * 60) {
npc.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
}
if (dist > guardDistanceMinimum * guardDistanceMinimum) {
ticksCountGuard += SentinelPlugin.instance.tickRate;
if (ticksCountGuard >= 30) {
ticksCountGuard = 0;
npc.getNavigator().getDefaultParameters().range(100);
npc.getNavigator().getDefaultParameters().stuckAction(TeleportStuckAction.INSTANCE);
Location picked = SentinelUtilities.pickNear(player.getLocation(), guardSelectionRange);
if (SentinelPlugin.debugMe) {
debug("Guard movement chosen to go to " + picked.toVector().toBlockVector().toString());
}
npc.getNavigator().setTarget(picked);
npc.getNavigator().getLocalParameters().speedModifier((float) speed);
npc.getNavigator().getLocalParameters().distanceMargin(guardDistanceMinimum * 0.75);
chased = true;
if (guarded != null) {
Location myLoc = getLivingEntity().getLocation();
Location theirLoc = guarded.getLocation();
double dist = theirLoc.getWorld().equals(myLoc.getWorld()) ? myLoc.distanceSquared(theirLoc) : MAX_DIST;
if (dist > 60 * 60) {
npc.teleport(guarded.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
}
if (dist > guardDistanceMinimum * guardDistanceMinimum) {
ticksCountGuard += SentinelPlugin.instance.tickRate;
if (ticksCountGuard >= 30) {
ticksCountGuard = 0;
npc.getNavigator().getDefaultParameters().range(100);
npc.getNavigator().getDefaultParameters().stuckAction(TeleportStuckAction.INSTANCE);
Location picked = SentinelUtilities.pickNear(guarded.getLocation(), guardSelectionRange);
if (SentinelPlugin.debugMe) {
debug("Guard movement chosen to go to " + picked.toVector().toBlockVector().toString());
}
npc.getNavigator().setTarget(picked);
npc.getNavigator().getLocalParameters().speedModifier((float) speed);
npc.getNavigator().getLocalParameters().distanceMargin(guardDistanceMinimum * 0.75);
chased = true;
}
needsSafeReturn = true;
goHome = false;
}
needsSafeReturn = true;
goHome = false;
}
// Avoidance handling
targetingHelper.processAvoidance();
Expand All @@ -1187,7 +1236,7 @@ else if (chasing == null) {
chased = false;
}
else {
if (pathingTo == null && npc.getNavigator().isNavigating() && getGuarding() == null) {
if (pathingTo == null && npc.getNavigator().isNavigating() && guarded == null) {
npc.getNavigator().cancelNavigation();
needsSafeReturn = false;
}
Expand All @@ -1198,7 +1247,7 @@ else if (chasing == null) {
}
}
}
else if (chasing == null && getGuarding() == null && pathingTo == null && npc.getNavigator().isNavigating() && needsSafeReturn) {
else if (chasing == null && guarded == null && pathingTo == null && npc.getNavigator().isNavigating() && needsSafeReturn) {
npc.getNavigator().cancelNavigation();
needsSafeReturn = false;
}
Expand All @@ -1213,11 +1262,9 @@ else if (chasing == null && getGuarding() == null && pathingTo == null && npc.ge
* Gets the location this NPC is guarding (the NPC's own location if nothing else to guard).
*/
public Location getGuardZone() {
if (getGuarding() != null) {
Player player = Bukkit.getPlayer(getGuarding());
if (player != null) {
return player.getLocation();
}
LivingEntity guarded = getGuardingEntity();
if (guarded != null) {
return guarded.getLocation();
}
if (chaseRange > 0) {
Location goal = nearestPathPoint();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import net.citizensnpcs.api.command.Requirements;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.LivingEntity;
import org.mcmonkey.sentinel.SentinelPlugin;
import org.mcmonkey.sentinel.SentinelTrait;

Expand All @@ -16,10 +18,21 @@ public class SentinelInfoCommands {
modifiers = {"info"}, permission = "sentinel.info", min = 1, max = 1)
@Requirements(livingEntity = true, ownership = true, traits = {SentinelTrait.class})
public void info(CommandContext args, CommandSender sender, SentinelTrait sentinel) {
String guardName = null;
LivingEntity guarded = sentinel.getGuardingEntity();
if (guarded != null) {
guardName = guarded.getName();
}
else if (sentinel.getGuarding() != null) {
OfflinePlayer player = Bukkit.getOfflinePlayer(sentinel.getGuarding());
if (player != null && player.getName() != null) {
guardName = player.getName();
}
}
sender.sendMessage(SentinelCommand.prefixGood + ChatColor.RESET + sentinel.getNPC().getFullName() + SentinelCommand.colorBasic
+ ": owned by " + ChatColor.RESET + SentinelPlugin.instance.getOwner(sentinel.getNPC()) +
(sentinel.getGuarding() == null ? "" : SentinelCommand.colorBasic
+ ", guarding: " + ChatColor.RESET + Bukkit.getOfflinePlayer(sentinel.getGuarding()).getName()));
(guardName == null ? "" : SentinelCommand.colorBasic
+ ", guarding: " + ChatColor.RESET + guardName));
sender.sendMessage(SentinelCommand.prefixGood + "Damage: " + ChatColor.AQUA + sentinel.damage
+ SentinelCommand.colorBasic + " Calculated: " + ChatColor.AQUA + sentinel.getDamage());
sender.sendMessage(SentinelCommand.prefixGood + "Armor: " + ChatColor.AQUA + sentinel.armor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.mcmonkey.sentinel.commands;

import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.command.Command;
import net.citizensnpcs.api.command.CommandContext;
import net.citizensnpcs.api.command.Requirements;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand All @@ -25,14 +27,34 @@ public void squad(CommandContext args, CommandSender sender, SentinelTrait senti
sender.sendMessage(SentinelCommand.prefixGood + "Set!");
}

@Command(aliases = {"sentinel"}, usage = "guard [PLAYERNAME]",
desc = "Makes the NPC guard a specific player - don't specify a player to stop guarding.",
@Command(aliases = {"sentinel"}, usage = "guard [PLAYERNAME]/npc:[ID]",
desc = "Makes the NPC guard a specific player or NPC - don't specify a player to stop guarding.",
modifiers = {"guard"}, permission = "sentinel.guard", min = 1, max = 2)
@Requirements(livingEntity = true, ownership = true, traits = {SentinelTrait.class})
public void guard(CommandContext args, CommandSender sender, SentinelTrait sentinel) {
if (args.argsLength() > 1) {
Player pl = Bukkit.getPlayer(args.getString(1));
sentinel.setGuarding(pl == null ? null : pl.getUniqueId());
String inputArg = args.getString(1);
if (inputArg.toLowerCase().startsWith("npc:")) {
try {
int id = Integer.parseInt(inputArg.substring("npc:".length()));
NPC npc = CitizensAPI.getNPCRegistry().getById(id);
if (npc == null) {
sender.sendMessage(SentinelCommand.prefixBad + "NPC ID input invalid (that number isn't any NPC's ID)!");
return;
}
sentinel.setGuarding(id);
sender.sendMessage(SentinelCommand.prefixGood + "NPC now guarding the specified NPC!");
return;
}
catch (NumberFormatException ex) {
sender.sendMessage(SentinelCommand.prefixBad + "NPC ID input invalid (need a number)!");
return;
}
}
else {
Player pl = Bukkit.getPlayer(inputArg);
sentinel.setGuarding(pl == null ? null : pl.getUniqueId());
}
}
else {
sentinel.setGuarding(null);
Expand Down

0 comments on commit 5e57f1d

Please sign in to comment.