diff --git a/plugin.yml b/plugin.yml index 25d183b3..fde4117d 100644 --- a/plugin.yml +++ b/plugin.yml @@ -81,6 +81,7 @@ permissions: factions.unclaim.circle: {description: unclaim by circle and radius, default: false} factions.unclaim.all: {description: unclaim all faction land, default: false} factions.unsethome: {description: unset faction home, default: false} + factions.unstuck: {description: teleport to nearest wilderness, default: false} factions.version: {description: see plugin version, default: false} # -------------------------------------------- # # STAR NOTATION @@ -161,6 +162,7 @@ permissions: factions.unclaim.circle: true factions.unclaim.all: true factions.unsethome: true + factions.unstuck: true factions.version: true # -------------------------------------------- # # KITS @@ -262,6 +264,7 @@ permissions: factions.unclaim.circle: true factions.unclaim.all: true factions.unsethome: true + factions.unstuck: true factions.version: true factions.kit.default: default: true diff --git a/src/com/massivecraft/factions/Perm.java b/src/com/massivecraft/factions/Perm.java index 84ed997d..3f8c12e0 100644 --- a/src/com/massivecraft/factions/Perm.java +++ b/src/com/massivecraft/factions/Perm.java @@ -81,6 +81,7 @@ public enum Perm UNCLAIM_CIRCLE, UNCLAIM_ALL, UNSETHOME, + UNSTUCK, VERSION, // END OF LIST diff --git a/src/com/massivecraft/factions/cmd/CmdFactions.java b/src/com/massivecraft/factions/cmd/CmdFactions.java index 16e2d706..661bda1e 100644 --- a/src/com/massivecraft/factions/cmd/CmdFactions.java +++ b/src/com/massivecraft/factions/cmd/CmdFactions.java @@ -50,6 +50,7 @@ public class CmdFactions extends FactionsCommand public CmdFactionsRelationEnemy cmdFactionsRelationEnemy = new CmdFactionsRelationEnemy(); public CmdFactionsPerm cmdFactionsPerm = new CmdFactionsPerm(); public CmdFactionsFlag cmdFactionsFlag = new CmdFactionsFlag(); + public CmdFactionsUnstuck cmdFactionsUnstuck = new CmdFactionsUnstuck(); public CmdFactionsExpansions cmdFactionsExpansions = new CmdFactionsExpansions(); public CmdFactionsXPlaceholder cmdFactionsTax = new CmdFactionsXPlaceholder("FactionsTax", "tax"); public CmdFactionsXPlaceholder cmdFactionsDynmap = new CmdFactionsXPlaceholder("FactionsDynmap", "dynmap"); @@ -102,6 +103,7 @@ public class CmdFactions extends FactionsCommand this.addSubCommand(this.cmdFactionsRelationEnemy); this.addSubCommand(this.cmdFactionsPerm); this.addSubCommand(this.cmdFactionsFlag); + this.addSubCommand(this.cmdFactionsUnstuck); this.addSubCommand(this.cmdFactionsExpansions); this.addSubCommand(this.cmdFactionsTax); this.addSubCommand(this.cmdFactionsDynmap); diff --git a/src/com/massivecraft/factions/cmd/CmdFactionsUnstuck.java b/src/com/massivecraft/factions/cmd/CmdFactionsUnstuck.java new file mode 100644 index 00000000..53ab619b --- /dev/null +++ b/src/com/massivecraft/factions/cmd/CmdFactionsUnstuck.java @@ -0,0 +1,162 @@ +package com.massivecraft.factions.cmd; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.World; + +import com.massivecraft.factions.Perm; +import com.massivecraft.factions.entity.BoardColl; +import com.massivecraft.factions.entity.Faction; +import com.massivecraft.factions.entity.FactionColl; +import com.massivecraft.factions.entity.MConf; +import com.massivecraft.factions.entity.MPerm; +import com.massivecraft.factions.entity.MPlayer; +import com.massivecraft.massivecore.MassiveException; +import com.massivecraft.massivecore.cmd.req.ReqHasPerm; +import com.massivecraft.massivecore.cmd.req.ReqIsPlayer; +import com.massivecraft.massivecore.mixin.Mixin; +import com.massivecraft.massivecore.mixin.TeleporterException; +import com.massivecraft.massivecore.ps.PS; +import com.massivecraft.massivecore.teleport.Destination; +import com.massivecraft.massivecore.teleport.DestinationSimple; + +public class CmdFactionsUnstuck extends FactionsCommand +{ + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public CmdFactionsUnstuck() + { + // Aliases + this.addAliases("unstuck"); + + // Requirements + this.addRequirements(ReqHasPerm.get(Perm.UNSTUCK.node)); + this.addRequirements(ReqIsPlayer.get()); + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public void perform() throws MassiveException + { + // If the player is in a chunk ... + final PS center = PS.valueOf(me.getLocation().getChunk()); + + // ... that isn't free ... + if (isFree(msender, center)) + { + msg("You don't seem to be stuck."); + } + + // ... get the nearest free top location ... + Location location = getNearestFreeTopLocation(msender, center); + if (location == null) + { + msg("No nearby chunk with %s or build rights found.", FactionColl.get().getNone().describeTo(msender)); + return; + } + + // ... and schedule a teleport. + Destination destination = new DestinationSimple(PS.valueOf(location)); + try + { + Mixin.teleport(me, destination, MConf.get().unstuckSeconds); + } + catch (TeleporterException e) + { + msg("%s", e.getMessage()); + } + } + + // Get the first free top location in the spiral. + public static Location getNearestFreeTopLocation(MPlayer mplayer, PS ps) + { + List chunks = getChunkSpiral(ps); + for (PS chunk : chunks) + { + if (!isFree(mplayer, chunk)) continue; + Location ret = getTopLocation(chunk); + if (ret == null) continue; + return ret; + } + return null; + } + + // Return the ground level top location for this ps. + public static Location getTopLocation(PS ps) + { + Location ret = null; + try + { + World world = ps.asBukkitWorld(); + + int blockX = ps.getBlockX(true); + int blockZ = ps.getBlockZ(true); + int blockY = world.getHighestBlockYAt(blockX, blockZ); + + // We must have something to stand on. + if (blockY <= 1) return null; + + double locationX = blockX + 0.5D; + double locationY = blockY + 0.5D; + double locationZ = blockZ + 0.5D; + + ret = new Location(world, locationX, locationY, locationZ); + } + catch (Exception e) + { + e.printStackTrace(); + } + return ret; + } + + // With a "free" chunk we mean wilderness or that the player has build rights. + public static boolean isFree(MPlayer mplayer, PS ps) + { + Faction faction = BoardColl.get().getFactionAt(ps); + if (faction.isNone()) return true; + return MPerm.getPermBuild().has(mplayer, ps, false); + } + + // Not exactly a spiral. But it will do. + public static List getChunkSpiral(PS center) + { + // Create Ret + List ret = new ArrayList(); + + // Fill Ret + center = center.getChunk(true); + final int centerX = center.getChunkX(); + final int centerZ = center.getChunkZ(); + + for (int delta = 1; delta <= MConf.get().unstuckChunkRadius; delta++) + { + int minX = centerX - delta; + int maxX = centerX + delta; + int minZ = centerZ - delta; + int maxZ = centerZ + delta; + + for (int x = minX; x <= maxX; x++) + { + ret.add(center.withChunkX(x).withChunkZ(minZ)); + ret.add(center.withChunkX(x).withChunkZ(maxZ)); + } + + for (int z = minZ + 1; z <= maxZ - 1; z++) + { + ret.add(center.withChunkX(minX).withChunkZ(z)); + ret.add(center.withChunkX(maxX).withChunkZ(z)); + } + } + + // Return Ret + return ret; + } + +} diff --git a/src/com/massivecraft/factions/entity/MConf.java b/src/com/massivecraft/factions/entity/MConf.java index 4c8bfd16..5fc4bece 100644 --- a/src/com/massivecraft/factions/entity/MConf.java +++ b/src/com/massivecraft/factions/entity/MConf.java @@ -461,6 +461,13 @@ public class MConf extends Entity public float seeChunkParticleOffsetY = 2; public float seeChunkParticleDeltaY = 2; + // -------------------------------------------- // + // UNSTUCK + // -------------------------------------------- // + + public int unstuckSeconds = 30; + public int unstuckChunkRadius = 10; + // -------------------------------------------- // // LOGGING // -------------------------------------------- //