Compare commits
13 Commits
v0.2.99-rc
...
feautre/pl
Author | SHA1 | Date | |
---|---|---|---|
669fab22cd | |||
221539001e | |||
61d0af1a05 | |||
75087b23e1 | |||
8af0909110 | |||
9944a7bcdb | |||
e21115cc65 | |||
51fdc80542 | |||
ff70dd255b | |||
b5a28e40cf | |||
4bfce1fd8d | |||
81960d14ce | |||
4bbada298f |
70
README.md
70
README.md
@ -1 +1,71 @@
|
||||
# Regions
|
||||
A plugin to carve up your minecraft world into named regions.
|
||||
|
||||
For most of minecraftian history, players, server owners, and content builders
|
||||
have sought to find a way around one of the least exciting problems in
|
||||
minecraft: How to travel long distances on a big world.
|
||||
|
||||
In 2010, Mojang gave us minecarts. Using some clever physics glitches,
|
||||
minecrafters devised minecart boosters to send them through distant lands at a
|
||||
modest speed.
|
||||
|
||||
Later that year, we all set sail for the infinite seas upon our new boats.
|
||||
Travel was swift, provided water.
|
||||
|
||||
Soon after, we were blessed with the bright magicks of redstone and powered
|
||||
rails. No longer did minecrafters need to rely on janky collision physics to
|
||||
move ourselves through the non-aquatic world.
|
||||
|
||||
With the Beta 1.9 release, Ender pearls and speed potions were introduced. We
|
||||
catapulted ourselves to terrifying new heights and found the world that much
|
||||
smaller.
|
||||
|
||||
After the Beta era, Mojang bestowed upon us a terrifying and awesome power:
|
||||
Nether portals. Soon long distance travel was a reasonable idea, if you didn't
|
||||
mind losing your entire inventory to an errant ghast or lava pool.
|
||||
|
||||
The 1.9 update allowed us to take to the sky with Elytra, and 1.11 sent us into
|
||||
the distant horizon with firework rockets. With enough determination, a compass,
|
||||
and some gunpowder, the world was all that much smaller to us.
|
||||
|
||||
And yet, we remain unsatiated. Dissatisfied with the high cost of elytra and the
|
||||
regular need to move great distances quickly, a great number of server plugins
|
||||
that included teleportation proliferated the pages of spigotmc.org.
|
||||
|
||||
And yet still, we remain dissatisfied. Typing out a /warp or /home command is
|
||||
trivial. Instantaneous teleportation at your fingertips might sound great, but
|
||||
there remains a distinct un-minecraftian feel about it.
|
||||
|
||||
What if there was a more immersive way to add fast travel to your server?
|
||||
|
||||
What if your players didn't need to do impossible feats like scrying some runes
|
||||
into a "chat box", something out of place from the minecraft world?
|
||||
|
||||
For your consideration: **Regions**
|
||||
|
||||
## Features
|
||||
- Point-to-point teleportation with a GUI
|
||||

|
||||
- Create a point of interest in your world, give it a name, apply a banner.
|
||||

|
||||
- Fast travel routes between POIs are automatically established.
|
||||
- Create World Hubs, accessable from any other POI on the world
|
||||
- Players can only travel to POI's they've already explored
|
||||

|
||||
- An incredibly cool and flashy teleportation effect
|
||||
- Pay for your fast travel with XP levels
|
||||
- Attempting to jump without enough XP might lead to a dangerous misfire,
|
||||
dropping you an unexpected distance from your destination.
|
||||
- Level up your region posts with craftable Region Post Charges
|
||||
- Not enough XP? No worries, you can pay for the ticket with a post's stored
|
||||
charges
|
||||
- Craft a Region Compass to locate the nearest region post
|
||||
- Wrap a latern in charges to create an anchor with which any player can create
|
||||
their own local region post
|
||||

|
||||
- Restrict any of the above features using permissions
|
||||
- Get a spiffy notification whenever you cross a region's border and enter a new
|
||||
land
|
||||
- Dynmap integration
|
||||
- Asynchronous chunk loading and teleportation on Paper servers that all but
|
||||
eliminates teleportation-induced lag
|
||||
|
BIN
docs/craft-region-items.gif
Executable file
BIN
docs/craft-region-items.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
BIN
docs/create-region-post.gif
Executable file
BIN
docs/create-region-post.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 10 MiB |
BIN
docs/discover-region-post.gif
Executable file
BIN
docs/discover-region-post.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 26 MiB |
BIN
docs/use-region-post.gif
Executable file
BIN
docs/use-region-post.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 34 MiB |
11
pom.xml
11
pom.xml
@ -4,7 +4,7 @@
|
||||
<groupId>us.camin.regions</groupId>
|
||||
<artifactId>Regions</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.2.99-rc5</version>
|
||||
<version>0.3.1</version>
|
||||
<name>regions</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<properties>
|
||||
@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>4.6.0</version>
|
||||
<version>4.7.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -51,12 +51,6 @@
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<extensions>
|
||||
@ -177,6 +171,7 @@
|
||||
<id>papermc</id>
|
||||
<url>http://papermc.io/repo/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository><id>pl3x-repo</id><url>http://repo.pl3x.net/</url></repository>
|
||||
<repository><id>dynmap-repo</id><url>http://repo.mikeprimm.com/</url></repository>
|
||||
<repository><id>imagej</id><url>http://maven.imagej.net/content/repositories/public/</url></repository>
|
||||
</repositories>
|
||||
|
@ -71,7 +71,7 @@ public class DynmapEventRelay implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePolygons(World world) {
|
||||
private void updatePolygons(World world) {
|
||||
List<GenericMarker> oldMarkers = m_borderMarkers.get(world);
|
||||
if (oldMarkers != null) {
|
||||
for(GenericMarker marker : oldMarkers) {
|
||||
|
@ -34,8 +34,8 @@ import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
|
||||
import com.destroystokyo.paper.Title;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import us.camin.regions.events.PlayerMoveInEvent;
|
||||
@ -50,9 +50,64 @@ public class PlayerNotifier implements Listener {
|
||||
Logger log = Logger.getLogger("Regions.PlayerNotifier");
|
||||
RegionManager m_manager;
|
||||
Plugin m_plugin;
|
||||
ProtocolManager m_protocolManager;
|
||||
int m_protoVersion = 0;
|
||||
|
||||
public PlayerNotifier(Plugin plugin, RegionManager manager) {
|
||||
m_manager = manager;
|
||||
m_plugin = plugin;
|
||||
m_protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
if (m_protocolManager != null) {
|
||||
m_protoVersion = MinecraftProtocolVersion.getCurrentVersion();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendTitle(Player player, WrappedChatComponent title, WrappedChatComponent subtitle) {
|
||||
// Title packet format changed in 1.17
|
||||
if (m_protoVersion < 755) {
|
||||
PacketContainer setTitle = m_protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
setTitle.getChatComponents().write(0, title);
|
||||
PacketContainer setSubtitle = m_protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
setSubtitle.getChatComponents().write(0, subtitle);
|
||||
setSubtitle.getTitleActions().write(0, TitleAction.SUBTITLE);
|
||||
try {
|
||||
m_protocolManager.sendServerPacket(player, setTitle);
|
||||
m_protocolManager.sendServerPacket(player, setSubtitle);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
} else {
|
||||
PacketContainer setTitle = m_protocolManager.createPacket(PacketType.Play.Server.SET_TITLE_TEXT);
|
||||
setTitle.getChatComponents().write(0, title);
|
||||
PacketContainer setSubtitle = m_protocolManager.createPacket(PacketType.Play.Server.SET_SUBTITLE_TEXT);
|
||||
setSubtitle.getChatComponents().write(0, subtitle);
|
||||
|
||||
try {
|
||||
m_protocolManager.sendServerPacket(player, setTitle);
|
||||
m_protocolManager.sendServerPacket(player, setSubtitle);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendActionBar(Player player, WrappedChatComponent text) {
|
||||
// Title packet format changed in 1.17
|
||||
if (m_protoVersion < 755) {
|
||||
PacketContainer setActionBar = m_protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
setActionBar.getChatComponents().write(0, text);
|
||||
setActionBar.getTitleActions().write(0, TitleAction.ACTIONBAR);
|
||||
try {
|
||||
m_protocolManager.sendServerPacket(player, setActionBar);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
} else {
|
||||
PacketContainer setActionBar = m_protocolManager.createPacket(PacketType.Play.Server.SET_ACTION_BAR_TEXT);
|
||||
setActionBar.getChatComponents().write(0, text);
|
||||
try {
|
||||
m_protocolManager.sendServerPacket(player, setActionBar);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@ -72,30 +127,16 @@ public class PlayerNotifier implements Listener {
|
||||
int pop = m_plugin.playerWatcher().playersInRegion(event.newRegion).size();
|
||||
Location center = event.newRegion.location();
|
||||
int altitude = center.getBlockY();
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
if (protocolManager != null) {
|
||||
PacketContainer chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
if (m_protocolManager != null) {
|
||||
String colorHex = "#" + String.format("%06X", event.newRegion.color().getColor().asRGB());
|
||||
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"" + event.newRegion.name() + "\", color: \""+colorHex+"\"}"));
|
||||
try {
|
||||
protocolManager.sendServerPacket(event.player, chatMessage);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"Population: " + pop + " Altitude: "+ altitude + "\", color: \"#ffffff\"}"));
|
||||
chatMessage.getTitleActions().write(0, TitleAction.SUBTITLE);
|
||||
try {
|
||||
protocolManager.sendServerPacket(event.player, chatMessage);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"Now entering " + event.newRegion.name() + "\", color: \""+colorHex+"\"}"));
|
||||
chatMessage.getTitleActions().write(0, TitleAction.ACTIONBAR);
|
||||
try {
|
||||
protocolManager.sendServerPacket(event.player, chatMessage);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
WrappedChatComponent titleComponent = WrappedChatComponent.fromJson("{text:\"" + event.newRegion.name() + "\", color: \""+colorHex+"\"}");
|
||||
WrappedChatComponent subtitleComponent = WrappedChatComponent.fromJson("{text:\"Population: " + pop + " Altitude: "+ altitude + "\", color: \"#ffffff\"}");
|
||||
WrappedChatComponent actionBarComponent = WrappedChatComponent.fromJson("{text:\"Now entering " + event.newRegion.name() + "\", color: \""+colorHex+"\"}");
|
||||
sendTitle(event.player, titleComponent, subtitleComponent);
|
||||
sendActionBar(event.player, actionBarComponent);
|
||||
|
||||
} else {
|
||||
// Fallback to approximated colors
|
||||
event.player.sendTitle(event.newRegion.coloredName(), "Population: " + pop + " Altitude: " + altitude);
|
||||
}
|
||||
}
|
||||
@ -107,28 +148,17 @@ public class PlayerNotifier implements Listener {
|
||||
if (event.region.markSeenByPlayer(event.player)) {
|
||||
event.player.playSound(event.region.location(), Sound.UI_TOAST_CHALLENGE_COMPLETE, (float)1, (float)1);
|
||||
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
if (protocolManager != null) {
|
||||
PacketContainer chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
if (m_protocolManager != null) {
|
||||
String colorHex = "#" + String.format("%06X", event.region.color().getColor().asRGB());
|
||||
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"Region discovered\", color: \""+colorHex+"\"}"));
|
||||
try {
|
||||
protocolManager.sendServerPacket(event.player, chatMessage);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
|
||||
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"You discovered the region " + event.region.name() + "\", color: \""+colorHex+"\"}"));
|
||||
chatMessage.getTitleActions().write(0, TitleAction.SUBTITLE);
|
||||
try {
|
||||
protocolManager.sendServerPacket(event.player, chatMessage);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
WrappedChatComponent discoverTitle = WrappedChatComponent.fromJson("{text:\"Region discovered\", color: \""+colorHex+"\"}");
|
||||
WrappedChatComponent discoverSubtitle = WrappedChatComponent.fromJson("{text:\"You discovered the region " + event.region.name() + "\", color: \""+colorHex+"\"}");
|
||||
sendTitle(event.player, discoverTitle, discoverSubtitle);
|
||||
|
||||
} else {
|
||||
//FIXME: also show pop/alt subtitle
|
||||
Title title = new Title.Builder().title("Region Discovered").subtitle(event.region.name()).build();
|
||||
event.player.sendMessage("You discovered the region " + event.region.name());
|
||||
event.player.sendTitle(title);
|
||||
// Fallback to approximated colors
|
||||
// TODO: Apply region color to the rest of the title and subtitle
|
||||
// text
|
||||
event.player.sendTitle("Region Discovered", "You discovered the region " + event.region.coloredName());
|
||||
}
|
||||
|
||||
// TODO: Make this configurable and disablable
|
||||
|
@ -78,6 +78,7 @@ public class Plugin extends JavaPlugin {
|
||||
if (markerAPI != null) {
|
||||
DynmapEventRelay regionHandler = new DynmapEventRelay (this, markerAPI);
|
||||
getServer().getPluginManager().registerEvents(regionHandler, this);
|
||||
log.info("Dynmap support enabled.");
|
||||
} else {
|
||||
log.info("Dynmap marker API not found. Disabling map support.");
|
||||
}
|
||||
|
@ -151,8 +151,8 @@ public class Region {
|
||||
public int getTravelCost(Region destination) {
|
||||
int baseCost = getBaseTravelCost(destination);
|
||||
if (destination.isHub()) {
|
||||
// Travel *to* a hub is 50% cheaper, before charges applied
|
||||
baseCost /= 2;
|
||||
// Travel *to* a hub is always 1
|
||||
baseCost = 1;
|
||||
}
|
||||
return Math.max(1, (int)(baseCost / (Math.min(4, m_charges + 1))));
|
||||
}
|
||||
@ -166,7 +166,7 @@ public class Region {
|
||||
return 1;
|
||||
}
|
||||
double distance = teleportLocation().distance(destination.teleportLocation());
|
||||
double blocksPerXP = 500;
|
||||
double blocksPerXP = 768;
|
||||
return Math.max(1, (int)(distance / blocksPerXP));
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,8 @@ public class RegionPostItemWatcher implements Listener {
|
||||
NamespacedKey chargeKey = new NamespacedKey(m_plugin, "region_post_charge");
|
||||
ShapedRecipe chargeRecipe = new ShapedRecipe(chargeKey, m_theChargeItem);
|
||||
chargeRecipe.shape("DDD", "DGD", "DDD");
|
||||
chargeRecipe.setIngredient('D', Material.GLOWSTONE_DUST);
|
||||
chargeRecipe.setIngredient('G', Material.GHAST_TEAR);
|
||||
chargeRecipe.setIngredient('D', Material.PURPUR_BLOCK);
|
||||
chargeRecipe.setIngredient('G', Material.QUARTZ);
|
||||
|
||||
NamespacedKey anchorKey = new NamespacedKey(m_plugin, "region_post_anchor");
|
||||
ShapedRecipe anchorRecipe = new ShapedRecipe(anchorKey, m_theAnchor);
|
||||
@ -67,7 +67,7 @@ public class RegionPostItemWatcher implements Listener {
|
||||
// Uses four fewer charges, slightly cheaper.
|
||||
// TODO: Maybe we just want this to be glowstone instead of effectively 4
|
||||
// ghast tears?
|
||||
compassRecipe.shape(" D ", "DGD", " D ");
|
||||
compassRecipe.shape(" D ", " G ", " ");
|
||||
compassRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem));
|
||||
compassRecipe.setIngredient('G', Material.COMPASS);
|
||||
|
||||
|
@ -24,35 +24,11 @@ public class BorderMesh {
|
||||
Map<Region, Polygon> m_polygons;
|
||||
Map<Region, Set<Region>> m_neighbors;
|
||||
|
||||
// TODO: Probably need to cache the neighbors after doing all this
|
||||
// intersection work! Should be generated during triangulate()
|
||||
public Collection<Region> neighbors(Region region) {
|
||||
Collection<Region> ret = new ArrayList<Region>();
|
||||
if (m_neighbors.containsKey(region)) {
|
||||
Collection<Region> allNeighbors = m_neighbors.get(region);
|
||||
for(Region neighbor : allNeighbors) {
|
||||
int crossings = 0;
|
||||
Vector2D start = new Vector2D(region.location().getBlockX(), region.location().getBlockZ());
|
||||
Vector2D neighborEnd = new Vector2D(neighbor.location().getBlockX(), neighbor.location().getBlockZ());
|
||||
for(Region distantNeighbor : m_regions) {
|
||||
if (distantNeighbor.equals(neighbor)) {
|
||||
continue;
|
||||
}
|
||||
Polygon poly = m_polygons.get(distantNeighbor);
|
||||
for(Polygon.Segment edge : poly.segments()) {
|
||||
// Check if the line from region->neighbor intersects with
|
||||
// any polygon
|
||||
if (doIntersect(start, neighborEnd, edge.start, edge.end)) {
|
||||
crossings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (crossings == 1 || crossings == 0) {
|
||||
ret.add(neighbor);
|
||||
}
|
||||
}
|
||||
return m_neighbors.get(region);
|
||||
}
|
||||
return ret;
|
||||
return new ArrayList<Region>();
|
||||
}
|
||||
|
||||
public BorderMesh(Collection<Region> regions) {
|
||||
@ -191,10 +167,12 @@ public class BorderMesh {
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<Region, Set<Region>> allNeighborSet = new HashMap<Region, Set<Region>>();
|
||||
|
||||
// Now we go back through our region mesh to generate vornoi points
|
||||
for(Region region : m_regions) {
|
||||
ArrayList<Vector2D> points = new ArrayList<Vector2D>();
|
||||
HashSet<Region> neighbors = new HashSet<Region>();
|
||||
HashSet<Region> allNeighbors = new HashSet<Region>();
|
||||
log.info("Executing voronoi transform...");
|
||||
for(Triangle tri : triangleSoup) {
|
||||
if (tri.region == region) {
|
||||
@ -205,36 +183,58 @@ public class BorderMesh {
|
||||
Location neighborLoc = neighbor.location();
|
||||
Vector2D neighborCenter = new Vector2D(neighborLoc.getBlockX(), neighborLoc.getBlockZ());
|
||||
if (vecEquals(tri.a, neighborCenter) || vecEquals(tri.b, neighborCenter) || vecEquals(tri.c, neighborCenter)) {
|
||||
neighbors.add(neighbor);
|
||||
allNeighbors.add(neighbor);
|
||||
}
|
||||
}
|
||||
points.add(tri.circumcenter());
|
||||
}
|
||||
}
|
||||
|
||||
allNeighborSet.put(region, allNeighbors);
|
||||
|
||||
// Sort points into a renderable polygon based on direction to center
|
||||
Location loc = region.location();
|
||||
Vector2D regionCenter = new Vector2D(loc.getBlockX(), loc.getBlockZ());
|
||||
points.sort((Vector2D a, Vector2D b) -> Double.compare(direction(a, regionCenter), direction(b, regionCenter)));
|
||||
log.info("Border for " + region.name() + " is defined by " + points.size() + " points");
|
||||
log.fine("Border for " + region.name() + " is defined by " + points.size() + " points");
|
||||
Polygon polygon = new Polygon(points);
|
||||
m_polygons.put(region, polygon);
|
||||
m_neighbors.put(region, neighbors);
|
||||
|
||||
}
|
||||
|
||||
// Now that we have the borders, we generate valid routes that cross
|
||||
// zero or one border lines at most.
|
||||
for(Region region : m_regions) {
|
||||
Set<Region> routedNeighbors = new HashSet<Region>();
|
||||
for(Region neighbor : allNeighborSet.get(region)) {
|
||||
int crossings = 0;
|
||||
Vector2D start = new Vector2D(region.location().getBlockX(), region.location().getBlockZ());
|
||||
Vector2D neighborEnd = new Vector2D(neighbor.location().getBlockX(), neighbor.location().getBlockZ());
|
||||
for(Region distantNeighbor : m_regions) {
|
||||
if (distantNeighbor.equals(neighbor) || distantNeighbor.equals(region)) {
|
||||
continue;
|
||||
}
|
||||
Polygon poly = m_polygons.get(distantNeighbor);
|
||||
for(Polygon.Segment edge : poly.segments()) {
|
||||
// Check if the line from region->neighbor intersects with
|
||||
// any polygon
|
||||
if (doIntersect(start, neighborEnd, edge.start, edge.end)) {
|
||||
log.fine("Intersect " + distantNeighbor.name());
|
||||
crossings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.fine("Route: " + region.name() + " -> " + neighbor.name() + "\t" + crossings);
|
||||
if (crossings == 1 || crossings == 0) {
|
||||
routedNeighbors.add(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
m_neighbors.put(region, routedNeighbors);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*public boolean validNeighbors(Region regionA, Region regionB) {
|
||||
Polygon polyA = polygonForRegion(regionA);
|
||||
Polygon polyB = polygonForRegion(regionA);
|
||||
Location center = region.location();
|
||||
if (poly.contains(center.getBlockX(), center.getBlockZ())) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
private boolean vecEquals(Vector2D a, Vector2D b) {
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
BIN
src/main/resources/lantern.png
Normal file
BIN
src/main/resources/lantern.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
@ -4,7 +4,7 @@ author: Torrie Fischer <tdfischer@hackerbots.net>
|
||||
website: http://hackerbots.net/
|
||||
version: ${version}
|
||||
api-version: 1.16
|
||||
softdepend: [dynmap, ProtocolLib]
|
||||
softdepend: [dynmap, ProtocolLib, Pl3xMap]
|
||||
commands:
|
||||
regions:
|
||||
description: "List available regions."
|
||||
|
Reference in New Issue
Block a user