Compare commits
18 Commits
v0.2.99-rc
...
feautre/pl
Author | SHA1 | Date | |
---|---|---|---|
669fab22cd | |||
221539001e | |||
61d0af1a05 | |||
75087b23e1 | |||
8af0909110 | |||
9944a7bcdb | |||
e21115cc65 | |||
51fdc80542 | |||
ff70dd255b | |||
b5a28e40cf | |||
4bfce1fd8d | |||
81960d14ce | |||
4bbada298f | |||
e3afbdc3c7 | |||
034527c208 | |||
092ed9c6d0 | |||
ac14a78d7f | |||
eaa8313cd1 |
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 |
21
pom.xml
21
pom.xml
@ -4,17 +4,23 @@
|
||||
<groupId>us.camin.regions</groupId>
|
||||
<artifactId>Regions</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.2.99-rc4</version>
|
||||
<version>0.3.1</version>
|
||||
<name>regions</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<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>
|
||||
@ -45,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>
|
||||
@ -87,6 +87,10 @@
|
||||
<pattern>io.papermc.lib</pattern>
|
||||
<shadedPattern>us.camin.regions.paperlib</shadedPattern> <!-- Replace this -->
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>us.camin.regions.bstats</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
@ -167,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) {
|
||||
@ -91,17 +91,10 @@ public class DynmapEventRelay implements Listener {
|
||||
log.info("Could not generate polygon for region " + region.name());
|
||||
continue;
|
||||
}
|
||||
boolean isFrontier = geom.isFrontier(region);
|
||||
if (!isFrontier) {
|
||||
AreaMarker marker = m_borderSet.createAreaMarker(null, region.name(), false, world.getName(), polygon.x, polygon.z, false);
|
||||
marker.setFillStyle(0.7, region.color().getColor().asRGB());
|
||||
marker.setLineStyle(2, 0.8, region.color().getColor().asRGB());
|
||||
m_borderMarkers.get(world).add(marker);
|
||||
} else {
|
||||
PolyLineMarker marker = m_borderSet.createPolyLineMarker(null, region.name(), false, world.getName(), polygon.x, polygon.y, polygon.z, false);
|
||||
marker.setLineStyle(2, 0.5, region.color().getColor().asRGB());
|
||||
m_borderMarkers.get(world).add(marker);
|
||||
}
|
||||
AreaMarker marker = m_borderSet.createAreaMarker(null, region.name(), false, world.getName(), polygon.x, polygon.z, false);
|
||||
marker.setFillStyle(0.7, region.color().getColor().asRGB());
|
||||
marker.setLineStyle(2, 0.8, region.color().getColor().asRGB());
|
||||
m_borderMarkers.get(world).add(marker);
|
||||
|
||||
// Add a line between each region, for teleportations
|
||||
double thickness = Math.max(1, Math.log(geom.neighbors(region).size()) * 2.75);
|
||||
@ -109,9 +102,13 @@ public class DynmapEventRelay implements Listener {
|
||||
double x[] = { neighbor.location().getBlockX(), region.location().getBlockX() };
|
||||
double y[] = { 64, 64 };
|
||||
double z[] = { neighbor.location().getBlockZ(), region.location().getBlockZ() };
|
||||
PolyLineMarker marker = m_routesSet.createPolyLineMarker(null, null, false, world.getName(), x, y, z, false);
|
||||
marker.setLineStyle((int)Math.ceil(thickness), 0.5, region.color().getColor().asRGB());
|
||||
m_borderMarkers.get(world).add(marker);
|
||||
String label = neighbor.name() + " / " + region.name();
|
||||
String description = "<p>Travel Cost to " + neighbor.name() + ": " + region.getTravelCost(neighbor) + "</p>";
|
||||
description += "<p>Travel Cost to " + region.name() + ": " + neighbor.getTravelCost(region) + "</p>";
|
||||
PolyLineMarker routeMarker = m_routesSet.createPolyLineMarker(null, label, true, world.getName(), x, y, z, false);
|
||||
routeMarker.setDescription(description);
|
||||
routeMarker.setLineStyle((int)Math.ceil(thickness), 0.5, region.color().getColor().asRGB());
|
||||
m_borderMarkers.get(world).add(routeMarker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,11 +116,17 @@ public class DynmapEventRelay implements Listener {
|
||||
private void createMarkerForRegion(Region region) {
|
||||
Location loc = region.location();
|
||||
MarkerIcon icon = m_api.getMarkerIcon("compass");
|
||||
if (region.isHub()) {
|
||||
icon = m_api.getMarkerIcon("world");
|
||||
}
|
||||
CircleMarker circleMarker = m_routesSet.createCircleMarker(null, region.name(), false, loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), 60, 60, false);
|
||||
Marker marker = m_centerSet.createMarker(null, region.name(), loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), icon, false);
|
||||
circleMarker.setFillStyle(0.75, region.color().getColor().asRGB());
|
||||
circleMarker.setLineStyle(0, 0, 0);
|
||||
String desc = "<h2>" + region.name() + "</h2>";
|
||||
if (region.isHub()) {
|
||||
desc += "<p><strong>World Hub</strong></p>";
|
||||
}
|
||||
marker.setDescription(desc);
|
||||
circleMarker.setDescription(desc);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -23,8 +23,13 @@ import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.World;
|
||||
|
||||
import org.dynmap.markers.MarkerAPI;
|
||||
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SingleLineChart;
|
||||
|
||||
import us.camin.regions.commands.RegionCommand;
|
||||
import us.camin.regions.commands.RegionOpCommand;
|
||||
import us.camin.regions.commands.RegionsCommand;
|
||||
@ -73,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.");
|
||||
}
|
||||
@ -86,6 +92,17 @@ public class Plugin extends JavaPlugin {
|
||||
getServer().getPluginManager().registerEvents(new PlayerInventoryTeleporter(this, m_regions), this);
|
||||
getServer().getPluginManager().registerEvents(new RegionPostItemWatcher(this, m_regions), this);
|
||||
getServer().getPluginManager().registerEvents(new RegionPostInteractionWatcher(this, m_regions), this);
|
||||
|
||||
// PluginID is from bstats.org for CaminusRegions
|
||||
Metrics metrics = new Metrics(this, 11705);
|
||||
metrics.addCustomChart(new SingleLineChart("regions", () -> {
|
||||
int allRegions = 0;
|
||||
for(World w : getServer().getWorlds()) {
|
||||
allRegions += m_regions.regionsForWorld(w).size();
|
||||
}
|
||||
return allRegions;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public void loadRegions() {
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,9 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.CompassMeta;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.RecipeChoice;
|
||||
import org.bukkit.inventory.ShapedRecipe;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.event.block.Action;
|
||||
|
||||
import us.camin.regions.ui.RegionPostBuilder;
|
||||
@ -45,6 +48,32 @@ public class RegionPostItemWatcher implements Listener {
|
||||
public RegionPostItemWatcher(Plugin plugin, RegionManager manager) {
|
||||
m_manager = manager;
|
||||
m_plugin = plugin;
|
||||
|
||||
// TODO: Make recipe-based creation of items configurable/disablable
|
||||
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.PURPUR_BLOCK);
|
||||
chargeRecipe.setIngredient('G', Material.QUARTZ);
|
||||
|
||||
NamespacedKey anchorKey = new NamespacedKey(m_plugin, "region_post_anchor");
|
||||
ShapedRecipe anchorRecipe = new ShapedRecipe(anchorKey, m_theAnchor);
|
||||
anchorRecipe.shape("DDD", "DGD", "DDD");
|
||||
anchorRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem));
|
||||
anchorRecipe.setIngredient('G', Material.LANTERN);
|
||||
|
||||
NamespacedKey compassKey = new NamespacedKey(m_plugin, "region_post_compass");
|
||||
ShapedRecipe compassRecipe = new ShapedRecipe(compassKey, m_theCompass);
|
||||
// Uses four fewer charges, slightly cheaper.
|
||||
// TODO: Maybe we just want this to be glowstone instead of effectively 4
|
||||
// ghast tears?
|
||||
compassRecipe.shape(" D ", " G ", " ");
|
||||
compassRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem));
|
||||
compassRecipe.setIngredient('G', Material.COMPASS);
|
||||
|
||||
m_plugin.getServer().addRecipe(chargeRecipe);
|
||||
m_plugin.getServer().addRecipe(anchorRecipe);
|
||||
m_plugin.getServer().addRecipe(compassRecipe);
|
||||
}
|
||||
|
||||
static public ItemStack createCompass(Region r) {
|
||||
@ -53,6 +82,9 @@ public class RegionPostItemWatcher implements Listener {
|
||||
List<String> lore = new ArrayList<String>();
|
||||
lore.add("Right click to locate the nearest Region Post");
|
||||
if (r == null) {
|
||||
meta.setDisplayName(ChatColor.DARK_PURPLE + "Region Compass");
|
||||
lore.add("Tracking: " + ChatColor.MAGIC + "NOWHERE IN PARTICULAR");
|
||||
lore.add("Coordinates: " + ChatColor.MAGIC + "0000" + ChatColor.RESET + ", " + ChatColor.MAGIC + "0000");
|
||||
} else {
|
||||
CompassMeta compassMeta = (CompassMeta)meta;
|
||||
compassMeta.setDisplayName(ChatColor.DARK_PURPLE + "Region Compass (" + r.coloredName() + ChatColor.RESET + ChatColor.DARK_PURPLE + ")");
|
||||
|
@ -25,26 +25,10 @@ public class BorderMesh {
|
||||
Map<Region, Set<Region>> m_neighbors;
|
||||
|
||||
public Collection<Region> neighbors(Region region) {
|
||||
Collection<Region> ret = new ArrayList<Region>();
|
||||
if (m_neighbors.containsKey(region)) {
|
||||
Collection<Region> allNeighbors = m_neighbors.get(region);
|
||||
if (false /*isFrontier(region)*/) {
|
||||
//log.trace("Region " + region.name() + " is a frontier");
|
||||
for(Region neighbor : allNeighbors) {
|
||||
if (!isFrontier(neighbor)) {
|
||||
ret.add(neighbor);
|
||||
} else {
|
||||
//log.trace("Not linking " + region.name() + " to " + neighbor.name() + " as it is also a frontier");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//log.trace("Region " + region.name() + " is not a frontier");
|
||||
for(Region neighbor : allNeighbors) {
|
||||
ret.add(neighbor);
|
||||
}
|
||||
}
|
||||
return m_neighbors.get(region);
|
||||
}
|
||||
return ret;
|
||||
return new ArrayList<Region>();
|
||||
}
|
||||
|
||||
public BorderMesh(Collection<Region> regions) {
|
||||
@ -58,6 +42,24 @@ public class BorderMesh {
|
||||
public double y[];
|
||||
public double z[];
|
||||
|
||||
public class Segment {
|
||||
public Vector2D start;
|
||||
public Vector2D end;
|
||||
public Segment(Vector2D start, Vector2D end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Segment> segments() {
|
||||
List<Segment> ret = new ArrayList<Segment>();
|
||||
for(int i = 0; i < x.length-1; i++) {
|
||||
ret.add(new Segment(new Vector2D(x[i], z[i]), new Vector2D(x[i+1], z[i+1])));
|
||||
}
|
||||
ret.add(new Segment(new Vector2D(x[x.length-1], z[z.length-1]), new Vector2D(x[0], z[0])));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Polygon(List<Vector2D> points) {
|
||||
x = new double[points.size()];
|
||||
y = new double[points.size()];
|
||||
@ -93,6 +95,27 @@ public class BorderMesh {
|
||||
}
|
||||
}
|
||||
|
||||
private int orientation(Vector2D p, Vector2D q, Vector2D r) {
|
||||
int val = (int)((q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y));
|
||||
if (val == 0) return 0; // colinear
|
||||
return (val > 0) ? 1 : 2; // Clockwise or counter-clockwise
|
||||
}
|
||||
|
||||
private boolean doIntersect(Vector2D p1, Vector2D q1, Vector2D p2, Vector2D q2) {
|
||||
int o1 = orientation(p1, q1, p2);
|
||||
int o2 = orientation(p1, q1, q2);
|
||||
int o3 = orientation(p2, q2, p1);
|
||||
int o4 = orientation(p2, q2, q1);
|
||||
|
||||
if (o1 != o2 && o3 != o4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// It probably isn't possible to generate a route that is colinear with a
|
||||
// region border, so assume it isn't intersecting.
|
||||
return false;
|
||||
}
|
||||
|
||||
public Polygon polygonForRegion(Region r) {
|
||||
return m_polygons.get(r);
|
||||
}
|
||||
@ -144,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) {
|
||||
@ -158,46 +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;
|
||||
}
|
||||
}*/
|
||||
|
||||
public boolean isFrontier(Region region) {
|
||||
Polygon poly = polygonForRegion(region);
|
||||
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;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class PlayerTeleporter {
|
||||
Random rand = new Random();
|
||||
|
||||
Vector travelVec = m_dest.toVector().subtract(m_src.toVector()).normalize();
|
||||
double angleDelta = (Math.PI / 3) * (rand.nextGaussian() - 0.5) * (1-accuracy);
|
||||
double angleDelta = (Math.PI / 5) * (rand.nextGaussian() - 0.5) * (1-accuracy);
|
||||
travelVec.rotateAroundY(angleDelta);
|
||||
|
||||
double distanceToDest = m_src.distance(m_dest);
|
||||
|
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