Compare commits
14 Commits
v0.2.99-rc
...
pre-rewrit
Author | SHA1 | Date | |
---|---|---|---|
803c8ced51 | |||
342be770a1 | |||
0856b398eb | |||
bb1d043fb2 | |||
f2253e6801 | |||
7d2a00c996 | |||
b856227e4a | |||
e8f86289aa | |||
72611f7574 | |||
43a499997d | |||
d1045685e5 | |||
fbfe174ed8 | |||
89830a1904 | |||
5ad959a02f |
13
pom.xml
13
pom.xml
@ -4,17 +4,24 @@
|
||||
<groupId>us.camin.regions</groupId>
|
||||
<artifactId>Regions</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.2.99-rc2</version>
|
||||
<version>0.2.99-rc5</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>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.papermc</groupId>
|
||||
@ -86,6 +93,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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -96,8 +96,7 @@ public class PlayerNotifier implements Listener {
|
||||
} catch (Exception e) {
|
||||
}
|
||||
} else {
|
||||
Title title = new Title.Builder().title(event.newRegion.name()).subtitle("Population: "+pop+" Altitude: " + altitude).build();
|
||||
event.player.sendTitle(title);
|
||||
event.player.sendTitle(event.newRegion.coloredName(), "Population: " + pop + " Altitude: " + altitude);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
@ -86,6 +91,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() {
|
||||
|
@ -28,6 +28,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.meta.BannerMeta;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
|
||||
import us.camin.regions.config.RegionConfiguration;
|
||||
import us.camin.regions.ui.Colors;
|
||||
@ -47,12 +48,12 @@ public class Region {
|
||||
private boolean m_isHub = false;
|
||||
|
||||
public Region(String name, Location location) {
|
||||
m_location = location.toBlockLocation();
|
||||
m_location = location.getBlock().getLocation();
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
public Region(String name, Location location, int visits, int charges, DyeColor color) {
|
||||
m_location = location.toBlockLocation();
|
||||
m_location = location.getBlock().getLocation();
|
||||
m_name = name;
|
||||
m_visits = visits;
|
||||
m_charges = charges;
|
||||
@ -147,6 +148,28 @@ public class Region {
|
||||
m_charges += charges;
|
||||
}
|
||||
|
||||
public int getTravelCost(Region destination) {
|
||||
int baseCost = getBaseTravelCost(destination);
|
||||
if (destination.isHub()) {
|
||||
// Travel *to* a hub is 50% cheaper, before charges applied
|
||||
baseCost /= 2;
|
||||
}
|
||||
return Math.max(1, (int)(baseCost / (Math.min(4, m_charges + 1))));
|
||||
}
|
||||
|
||||
public int getBaseTravelCost(Region destination) {
|
||||
if (m_isHub && destination.isHub()) {
|
||||
// Free travel between hubs
|
||||
return 0;
|
||||
} else if (m_isHub) {
|
||||
// Max cost 1 level for travel *from* a hub
|
||||
return 1;
|
||||
}
|
||||
double distance = teleportLocation().distance(destination.teleportLocation());
|
||||
double blocksPerXP = 500;
|
||||
return Math.max(1, (int)(distance / blocksPerXP));
|
||||
}
|
||||
|
||||
int m_visits = 0;
|
||||
int m_charges = 0;
|
||||
|
||||
@ -317,6 +340,8 @@ public class Region {
|
||||
ItemStack item = new ItemStack(bannerIconMaterial());
|
||||
BannerMeta bannerMeta = (BannerMeta)item.getItemMeta();
|
||||
bannerMeta.setPatterns(bannerPatterns());
|
||||
bannerMeta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS);
|
||||
bannerMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
|
||||
item.setItemMeta(bannerMeta);
|
||||
return item;
|
||||
}
|
||||
|
@ -89,12 +89,13 @@ public class RegionPostInteractionWatcher implements Listener {
|
||||
Location lanternRegion = nearest.interactLocation().add(0, 1, 0);
|
||||
boolean isInteracted = false;
|
||||
if (clickedBlock != null) {
|
||||
isInteracted |= clickedBlock.getBlockKey() == interactRegion.toBlockKey();
|
||||
isInteracted |= clickedBlock.getBlockKey() == lanternRegion.toBlockKey();
|
||||
isInteracted |= nearest.interactLocation().add(1, 0, 0).toBlockKey() == clickedBlock.getBlockKey();
|
||||
isInteracted |= nearest.interactLocation().add(-1, 0, 0).toBlockKey() == clickedBlock.getBlockKey();
|
||||
isInteracted |= nearest.interactLocation().add(0, 0, 1).toBlockKey() == clickedBlock.getBlockKey();
|
||||
isInteracted |= nearest.interactLocation().add(0, 0, -1).toBlockKey() == clickedBlock.getBlockKey();
|
||||
|
||||
isInteracted |= clickedBlock.getLocation().equals(interactRegion.getBlock().getLocation());
|
||||
isInteracted |= clickedBlock.getLocation().equals(lanternRegion.getBlock().getLocation());
|
||||
isInteracted |= nearest.interactLocation().add(1, 0, 0).getBlock().getLocation().equals(clickedBlock.getLocation());
|
||||
isInteracted |= nearest.interactLocation().add(-1, 0, 0).getBlock().getLocation().equals(clickedBlock.getLocation());
|
||||
isInteracted |= nearest.interactLocation().add(0, 0, 1).getBlock().getLocation().equals(clickedBlock.getLocation());
|
||||
isInteracted |= nearest.interactLocation().add(0, 0, -1).getBlock().getLocation().equals(clickedBlock.getLocation());
|
||||
}
|
||||
if (isInteracted) {
|
||||
event.setCancelled(true);
|
||||
@ -110,7 +111,8 @@ public class RegionPostInteractionWatcher implements Listener {
|
||||
builder.updateLantern();
|
||||
});
|
||||
m_plugin.saveRegions();
|
||||
player.setItemInHand(handStack.subtract());
|
||||
handStack.setAmount(handStack.getAmount()-1);
|
||||
player.setItemInHand(handStack);
|
||||
m_plugin.getServer().getPluginManager().callEvent(new PlayerAddRegionChargeEvent(player, nearest));
|
||||
// TODO: Make this configurable and disablable
|
||||
player.giveExp(1);
|
||||
@ -126,10 +128,8 @@ public class RegionPostInteractionWatcher implements Listener {
|
||||
RegionPostBuilder builder = new RegionPostBuilder(nearest, m_plugin);
|
||||
builder.build();
|
||||
});
|
||||
} else if (nearest.charges() > 0 || player.hasPermission("regions.bypass.charges")) {
|
||||
m_plugin.getServer().getPluginManager().callEvent(new PlayerPostInteractEvent(player, nearest));
|
||||
} else {
|
||||
player.sendMessage("This region post is not charged. Right click on it while sneaking and holding a Region Post Charge.");
|
||||
m_plugin.getServer().getPluginManager().callEvent(new PlayerPostInteractEvent(player, nearest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -30,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;
|
||||
@ -44,21 +48,49 @@ 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.GLOWSTONE_DUST);
|
||||
chargeRecipe.setIngredient('G', Material.GHAST_TEAR);
|
||||
|
||||
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 ", "DGD", " D ");
|
||||
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) {
|
||||
ItemStack stack = new ItemStack(Material.COMPASS);
|
||||
ItemMeta meta = stack.getItemMeta();
|
||||
List<String> lore = new ArrayList<String>();
|
||||
lore.add("Right click to locate the nearest Region Post");
|
||||
if (r == null) {
|
||||
lore.add("Right click to locate the nearest Region Post");
|
||||
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(r.name() + " Region Compass");
|
||||
compassMeta.setDisplayName(ChatColor.DARK_PURPLE + "Region Compass (" + r.coloredName() + ChatColor.RESET + ChatColor.DARK_PURPLE + ")");
|
||||
compassMeta.setLodestone(r.location());
|
||||
compassMeta.setLodestoneTracked(false);
|
||||
lore.add("Right click to locate the nearest Region Post");
|
||||
lore.add("Tracking: " + r.name());
|
||||
lore.add("Tracking: " + r.coloredName());
|
||||
lore.add("Coordinates: " + r.location().getX() + ", " + r.location().getY());
|
||||
}
|
||||
meta.setLore(lore);
|
||||
@ -110,7 +142,7 @@ public class RegionPostItemWatcher implements Listener {
|
||||
if (stack.getType() == m_theCompass.getType()) {
|
||||
ItemMeta meta = stack.getItemMeta();
|
||||
ItemMeta theItemMeta = m_theCompass.getItemMeta();
|
||||
if (meta.getItemFlags() == theItemMeta.getItemFlags()) {
|
||||
if (meta.getLore() != null && meta.getLore().get(0).equals(theItemMeta.getLore().get(0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -169,7 +201,8 @@ public class RegionPostItemWatcher implements Listener {
|
||||
RegionPostBuilder builder = new RegionPostBuilder(r, m_plugin);
|
||||
builder.build();
|
||||
});
|
||||
player.setItemInHand(handStack.subtract());
|
||||
handStack.setAmount(handStack.getAmount()-1);
|
||||
player.setItemInHand(handStack);
|
||||
m_plugin.regionManager().addRegion(r);
|
||||
m_plugin.saveRegions();
|
||||
player.sendMessage("You established the region "+r.coloredName());
|
||||
|
@ -24,23 +24,31 @@ 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);
|
||||
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");
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//log.trace("Region " + region.name() + " is not a frontier");
|
||||
for(Region neighbor : allNeighbors) {
|
||||
ret.add(neighbor);
|
||||
if (crossings == 1 || crossings == 0) {
|
||||
ret.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,6 +66,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 +119,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);
|
||||
}
|
||||
@ -113,7 +160,16 @@ public class BorderMesh {
|
||||
log.warning("Got a null pointer when triangulating, skipping world.");
|
||||
return false;
|
||||
} catch (NotEnoughPointsException e) {
|
||||
log.info("Not enough points to triangulate mesh. Add more regions!");
|
||||
log.info("Not enough points to triangulate mesh, all regions will be connected to each other. Add more regions!");
|
||||
for(Region region : m_regions) {
|
||||
HashSet<Region> neighbors = new HashSet<Region>();
|
||||
for(Region neighbor : m_regions) {
|
||||
if (neighbor != region) {
|
||||
neighbors.add(neighbor);
|
||||
}
|
||||
}
|
||||
m_neighbors.put(region, neighbors);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -179,16 +235,6 @@ public class BorderMesh {
|
||||
}
|
||||
}*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -29,7 +30,10 @@ import org.bukkit.event.Event.Result;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -40,6 +44,7 @@ import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import us.camin.regions.Plugin;
|
||||
import us.camin.regions.Region;
|
||||
@ -99,22 +104,25 @@ public class PlayerInventoryTeleporter implements Listener {
|
||||
.filter((r) -> r.name().equals(selectedName))
|
||||
.findFirst();
|
||||
if (destination.isPresent()) {
|
||||
player.sendMessage("Teleporting you there now...");
|
||||
nearest.addCharges(-1);
|
||||
destination.get().addCharges(1);
|
||||
Location targetLocation = destination.get().teleportLocation();
|
||||
int cost = nearest.getTravelCost(destination.get());
|
||||
int chargesConsumed = Math.min(nearest.charges(), (int)cost - player.getLevel());
|
||||
double payment = player.getLevel() + chargesConsumed;
|
||||
double accuracy = 0.8 + (payment / cost) * 0.2;
|
||||
if (chargesConsumed > 0) {
|
||||
nearest.addCharges(-chargesConsumed);
|
||||
player.sendMessage("You don't have enough XP. "+ chargesConsumed + " charges were consumed.");
|
||||
}
|
||||
player.giveExpLevels(-(int)cost);
|
||||
m_plugin.getServer().getScheduler().runTask(m_plugin, () -> {
|
||||
RegionPostBuilder builder = new RegionPostBuilder(nearest, m_plugin);
|
||||
builder.updateLantern();
|
||||
});
|
||||
m_plugin.saveRegions();
|
||||
new PlayerTeleporter(player, nearest.teleportLocation(), destination.get().teleportLocation(), m_plugin).teleport().thenRun(() -> {
|
||||
new PlayerTeleporter(player, nearest.teleportLocation(), targetLocation, m_plugin, accuracy).teleport().thenRun(() -> {
|
||||
RegionPostBuilder builder = new RegionPostBuilder(destination.get(), m_plugin);
|
||||
builder.updateLantern();
|
||||
});
|
||||
if (nearest.charges() == 0) {
|
||||
nearest.location().getWorld().playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, (float)1.0, (float)1.0);
|
||||
//player.sendMessage("You've consumed this region post's last charge! You won't be able to return without recharging it.");
|
||||
}
|
||||
} else {
|
||||
player.sendMessage("There is no region with that name. This is a bug.");
|
||||
}
|
||||
@ -169,6 +177,15 @@ public class PlayerInventoryTeleporter implements Listener {
|
||||
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
||||
meta.addEnchant(Enchantment.SOUL_SPEED, 1, true);
|
||||
}
|
||||
int cost = event.region.getTravelCost(region);
|
||||
int baseCost = event.region.getBaseTravelCost(region);
|
||||
if (cost != baseCost) {
|
||||
lore.add(ChatColor.WHITE + "Travel cost: " + ChatColor.YELLOW + ChatColor.STRIKETHROUGH + Math.round(baseCost) + ChatColor.RESET + ChatColor.YELLOW + ChatColor.BOLD + " " + Math.round(cost) + " levels");
|
||||
} else if (cost > 0) {
|
||||
lore.add(ChatColor.WHITE + "Travel cost: " + ChatColor.YELLOW + Math.round(cost) + " levels");
|
||||
} else {
|
||||
lore.add(ChatColor.WHITE + "Travel cost: " + ChatColor.GREEN + "Free!");
|
||||
}
|
||||
lore.add(ChatColor.WHITE + "Distance: " + ChatColor.YELLOW + Math.round(region.location().distance(event.region.location())));
|
||||
lore.add(ChatColor.WHITE + "Population: " + ChatColor.YELLOW + m_plugin.playerWatcher().playersInRegion(region).size());
|
||||
lore.add(ChatColor.WHITE + "Altitude: " + ChatColor.YELLOW + altitude);
|
||||
@ -180,6 +197,9 @@ public class PlayerInventoryTeleporter implements Listener {
|
||||
}
|
||||
}
|
||||
lore.add("Nearby connections: " + String.join(", ", neighborNames));
|
||||
if (event.player.getLevel() < cost) {
|
||||
lore.add("" + ChatColor.RED + ChatColor.BOLD + "You don't have enough XP! Travel may be dangerous...");
|
||||
}
|
||||
} else {
|
||||
lore.add("" + ChatColor.BOLD + ChatColor.GOLD + "You haven't discovered this location yet!");
|
||||
lore.add(ChatColor.WHITE + "Distance: " + ChatColor.YELLOW + ChatColor.MAGIC + Math.round(region.location().distance(event.region.location())));
|
||||
|
@ -6,8 +6,12 @@ import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Block;
|
||||
import java.util.Random;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import us.camin.regions.Plugin;
|
||||
|
||||
@ -15,6 +19,7 @@ import java.util.logging.Logger;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class PlayerTeleporter {
|
||||
Logger log = Logger.getLogger("Regions.PlayerTeleporter");
|
||||
@ -23,12 +28,30 @@ public class PlayerTeleporter {
|
||||
private Location m_src;
|
||||
private Location m_dest;
|
||||
private Plugin m_plugin;
|
||||
private double m_accuracy;
|
||||
|
||||
public PlayerTeleporter(Player p, Location src, Location dest, Plugin plugin) {
|
||||
public PlayerTeleporter(Player p, Location src, Location dest, Plugin plugin, double accuracy) {
|
||||
this.m_player = p;
|
||||
this.m_src = src;
|
||||
this.m_dest = dest;
|
||||
this.m_plugin = plugin;
|
||||
this.m_accuracy = accuracy;
|
||||
|
||||
if (m_accuracy < 1) {
|
||||
Random rand = new Random();
|
||||
|
||||
Vector travelVec = m_dest.toVector().subtract(m_src.toVector()).normalize();
|
||||
double angleDelta = (Math.PI / 5) * (rand.nextGaussian() - 0.5) * (1-accuracy);
|
||||
travelVec.rotateAroundY(angleDelta);
|
||||
|
||||
double distanceToDest = m_src.distance(m_dest);
|
||||
double distanceDelta = (distanceToDest/3) * (rand.nextGaussian() - 0.5) * (1 - accuracy);
|
||||
double targetDistance = distanceToDest - distanceDelta;
|
||||
|
||||
travelVec.multiply(targetDistance);
|
||||
|
||||
m_dest = m_src.clone().add(travelVec);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> teleport() {
|
||||
@ -37,6 +60,22 @@ public class PlayerTeleporter {
|
||||
|
||||
BukkitTask hoverGenerator = scheduler.runTaskTimer(m_plugin, () -> applyHoverEffect(), 0, 10);
|
||||
BukkitTask particleGenerator = scheduler.runTaskTimer(m_plugin, () -> spawnHoverParticles(), 0, 1);
|
||||
BukkitTask noiseGenerator = scheduler.runTaskTimer(m_plugin, () -> {
|
||||
if (m_accuracy < 1) {
|
||||
m_player.getLocation().getWorld().playSound(m_player.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, (float)0.5, (float)1.0);
|
||||
m_player.getLocation().getWorld().spawnParticle(Particle.EXPLOSION_LARGE, m_player.getLocation(), 3, 1, 1, 1);
|
||||
}
|
||||
}, 20, 15);
|
||||
|
||||
if (m_accuracy < 1) {
|
||||
m_player.sendMessage("Teleporting you there-ish now...");
|
||||
m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
||||
m_player.sendMessage("" + ChatColor.RED + ChatColor.BOLD + "Oh no!" + ChatColor.RESET + ChatColor.YELLOW + " The region post is malfunctioning!");
|
||||
}, 40);
|
||||
} else {
|
||||
m_player.sendMessage("Teleporting you there now...");
|
||||
}
|
||||
|
||||
playTeleportSound();
|
||||
|
||||
m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
||||
@ -46,11 +85,23 @@ public class PlayerTeleporter {
|
||||
m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
||||
spawnTeleportStartParticles();
|
||||
PaperLib.teleportAsync(m_player, m_dest, TeleportCause.COMMAND).thenAccept(res -> {
|
||||
Block targetBlock = m_dest.getBlock();
|
||||
if (!targetBlock.isPassable() && !targetBlock.getRelative(BlockFace.DOWN).isPassable()) {
|
||||
while (!(targetBlock.isPassable() && targetBlock.getRelative(BlockFace.UP).isEmpty() && !targetBlock.getRelative(BlockFace.DOWN).isPassable())) {
|
||||
targetBlock = targetBlock.getRelative(BlockFace.UP);
|
||||
}
|
||||
m_dest = targetBlock.getLocation().add(0.5, 0, 0.5);
|
||||
m_player.teleport(m_dest, TeleportCause.COMMAND);
|
||||
}
|
||||
m_player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 60, 1, false, false, false));
|
||||
m_player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 40, 1, false, false, false));
|
||||
if (m_accuracy < 1) {
|
||||
m_player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, 20 * 5, 1, false, true, false));
|
||||
}
|
||||
spawnEndParticles();
|
||||
particleGenerator.cancel();
|
||||
hoverGenerator.cancel();
|
||||
noiseGenerator.cancel();
|
||||
ret.complete(null);
|
||||
});
|
||||
}, 20 * 6);
|
||||
@ -63,11 +114,22 @@ public class PlayerTeleporter {
|
||||
}
|
||||
|
||||
void spawnEndParticles() {
|
||||
World world = m_player.getLocation().getWorld();
|
||||
final World world = m_player.getLocation().getWorld();
|
||||
world.playSound(m_dest, Sound.BLOCK_PORTAL_TRAVEL, (float)0.5, (float)1.0);
|
||||
world.spawnParticle(Particle.CLOUD, m_dest, 70, 1, 1, 1);
|
||||
world.spawnParticle(Particle.SPELL_MOB, m_dest, 5, 2, 2, 2);
|
||||
world.spawnParticle(Particle.PORTAL, m_player.getLocation(), 70, 1, 1, 1);
|
||||
if (m_accuracy < 1) {
|
||||
BukkitTask fireGenerator = m_plugin.getServer().getScheduler().runTaskTimer(m_plugin, () -> {
|
||||
world.spawnParticle(Particle.FLAME, m_player.getLocation(), 80, 1, 1, 1, 0);
|
||||
}, 0, 8);
|
||||
m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
||||
fireGenerator.cancel();
|
||||
}, 20 * 8);
|
||||
m_player.sendMessage("" + ChatColor.RED + ChatColor.BOLD + "Ouch!" + ChatColor.RESET + ChatColor.YELLOW + " You didn't have enough XP and the region post malfunctioned.");
|
||||
m_player.damage(5 * (1.0-m_accuracy));
|
||||
world.playSound(m_player.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, (float)1.0, (float)1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void applyHoverEffect() {
|
||||
@ -84,5 +146,8 @@ public class PlayerTeleporter {
|
||||
m_src.getWorld().playSound(m_src, Sound.BLOCK_PORTAL_TRIGGER, (float)0.5, (float)0.6);
|
||||
m_src.getWorld().playSound(m_src, Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, (float)0.5, (float)1.0);
|
||||
m_src.getWorld().playSound(m_dest, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, (float)0.5, (float)1.0);
|
||||
if (m_accuracy < 1) {
|
||||
m_src.getWorld().playSound(m_src, Sound.ENTITY_ITEM_BREAK, (float)1.0, (float)1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ author: Torrie Fischer <tdfischer@hackerbots.net>
|
||||
website: http://hackerbots.net/
|
||||
version: ${version}
|
||||
api-version: 1.16
|
||||
softdepend: [dynmap, HolographicDisplays, ProtocolLib]
|
||||
softdepend: [dynmap, ProtocolLib]
|
||||
commands:
|
||||
regions:
|
||||
description: "List available regions."
|
||||
|
Reference in New Issue
Block a user