diff --git a/build.gradle.kts b/build.gradle.kts index 9ce10cc8..d375a3d5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,8 @@ dependencies { implementation(libs.com.googlecode.json.simple) implementation(libs.bstats.bukkit) implementation(platform(libs.fawe.bom)) + implementation("net.buildtheearth:projection:1.0.3") + compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false } compileOnly(libs.io.papermc.paper.paper.api) diff --git a/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java b/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java index a1314bc8..f223d0e4 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java +++ b/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java @@ -10,7 +10,10 @@ import net.buildtheearth.buildteamtools.modules.network.NetworkModule; import net.buildtheearth.buildteamtools.modules.plotsystem.PlotSystemModule; import net.buildtheearth.buildteamtools.modules.stats.StatsModule; +import net.buildtheearth.buildteamtools.utils.io.ConfigPaths; import net.buildtheearth.buildteamtools.utils.io.ConfigUtil; +import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -32,6 +35,7 @@ public class BuildTeamTools extends JavaPlugin { @Getter private static BuildTeamTools instance = null; + private World earthWorld; @Override public void onEnable() { @@ -88,4 +92,16 @@ public void setDebug(boolean debug) { this.debug = debug; ChatHelper.DEBUG = debug; } + + public World getEarthWorld() { + if (earthWorld != null) + return earthWorld; + + String worldName = BuildTeamTools.getInstance().getConfig().getString(ConfigPaths.EARTH_WORLD); + if (worldName == null || worldName.isEmpty()) + return null; + + earthWorld = Bukkit.getWorld(worldName); + return earthWorld; + } } \ No newline at end of file diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/house/menu/AdvancedSettingsMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/house/menu/AdvancedSettingsMenu.java index 69dd70a7..658d5167 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/house/menu/AdvancedSettingsMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/house/menu/AdvancedSettingsMenu.java @@ -5,8 +5,10 @@ import net.buildtheearth.buildteamtools.modules.generator.components.house.HouseFlag; import net.buildtheearth.buildteamtools.modules.generator.components.house.HouseSettings; import net.buildtheearth.buildteamtools.modules.generator.model.Settings; -import net.buildtheearth.buildteamtools.utils.CustomHeads; import net.buildtheearth.buildteamtools.utils.MenuItems; +import net.buildtheearth.buildteamtools.utils.heads.HeadColor; +import net.buildtheearth.buildteamtools.utils.heads.HeadFactory; +import net.buildtheearth.buildteamtools.utils.heads.HeadTexture; import net.buildtheearth.buildteamtools.utils.menus.AbstractMenu; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -47,15 +49,15 @@ protected void setPreviewItems() { this.windowHeight = (int) house.getPlayerSettings().get(uuid).getValues().get(HouseFlag.WINDOW_HEIGHT); this.windowDistance = (int) house.getPlayerSettings().get(uuid).getValues().get(HouseFlag.WINDOW_DISTANCE); - createCounter(CustomHeads.SliderColor.WHITE, FLOOR_COUNT_SLOT, "Number of Floors", floorCount, 1, 10, "Floors"); - createCounter(CustomHeads.SliderColor.LIGHT_GRAY, FLOOR_HEIGHT_SLOT, "Floors Height", floorHeight, 1, 10, "Blocks"); - createCounter(CustomHeads.SliderColor.WHITE, BASE_HEIGHT_SLOT, "Basement Height", baseHeight, 0, 10, "Blocks"); - createCounter(CustomHeads.SliderColor.WHITE, WINDOW_WIDTH_SLOT, "Window Width", windowWidth, 1, 5, "Blocks"); - createCounter(CustomHeads.SliderColor.LIGHT_GRAY, WINDOW_HEIGHT_SLOT, "Window Height", windowHeight, 1, 5, "Blocks"); - createCounter(CustomHeads.SliderColor.WHITE, WINDOW_DISTANCE_SLOT, "Window Distance", windowDistance, 1, 6, "Blocks"); + createCounter(HeadColor.WHITE, FLOOR_COUNT_SLOT, "Number of Floors", floorCount, 1, 10, "Floors"); + createCounter(HeadColor.LIGHT_GRAY, FLOOR_HEIGHT_SLOT, "Floors Height", floorHeight, 1, 10, "Blocks"); + createCounter(HeadColor.WHITE, BASE_HEIGHT_SLOT, "Basement Height", baseHeight, 0, 10, "Blocks"); + createCounter(HeadColor.WHITE, WINDOW_WIDTH_SLOT, "Window Width", windowWidth, 1, 5, "Blocks"); + createCounter(HeadColor.LIGHT_GRAY, WINDOW_HEIGHT_SLOT, "Window Height", windowHeight, 1, 5, "Blocks"); + createCounter(HeadColor.WHITE, WINDOW_DISTANCE_SLOT, "Window Distance", windowDistance, 1, 6, "Blocks"); - getMenu().getSlot(NEXT_ITEM_SLOT).setItem(CustomHeads.getCheckmarkItem("§eNext")); + getMenu().getSlot(NEXT_ITEM_SLOT).setItem(HeadFactory.head(HeadTexture.CHECKMARK, "§eNext")); setBackItem(BACK_ITEM_SLOT, new BaseColorMenu(getMenuPlayer(), false)); super.setPreviewItems(); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/kml/KmlCommand.java b/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/kml/KmlCommand.java index 78520890..13de3b33 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/kml/KmlCommand.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/kml/KmlCommand.java @@ -4,10 +4,11 @@ import com.cryptomorin.xseries.XMaterial; import de.micromata.opengis.kml.v_2_2_0.Coordinate; import net.buildtheearth.buildteamtools.BuildTeamTools; +import net.buildtheearth.buildteamtools.modules.navigation.NavUtils; import net.buildtheearth.buildteamtools.utils.BlockLocation; -import net.buildtheearth.buildteamtools.utils.GeometricUtils; import net.buildtheearth.buildteamtools.utils.LineRasterization; import net.buildtheearth.buildteamtools.utils.PolygonTools; +import net.buildtheearth.model.GeographicalCoordinate; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -516,7 +517,7 @@ public boolean undoCommand(Player player) { *
* This method: *
@@ -527,7 +528,7 @@ public boolean undoCommand(Player player) { * @return A Bukkit Location with X/Z from projection and Y adjusted for altitude */ private @NonNull Location getLocationFromCoordinates(double[] coordinates, double altitudeFromKML) { - Location mcLocation = GeometricUtils.getLocationFromCoordinates(coordinates); + Location mcLocation = NavUtils.getLocationFromCoordinates(new GeographicalCoordinate(coordinates[0], coordinates[1])); //add altitude from kml (altitude from Google Earth is always relative to ground) //note: the "-2" is only neccesary because // getLocationFromCoordinates returns terrain altitude + 2 diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/road/menu/AdvancedSettingsMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/road/menu/AdvancedSettingsMenu.java index 40334242..4c61812d 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/road/menu/AdvancedSettingsMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/generator/components/road/menu/AdvancedSettingsMenu.java @@ -7,9 +7,11 @@ import net.buildtheearth.buildteamtools.modules.generator.components.road.RoadFlag; import net.buildtheearth.buildteamtools.modules.generator.components.road.RoadSettings; import net.buildtheearth.buildteamtools.modules.generator.model.Settings; -import net.buildtheearth.buildteamtools.utils.CustomHeads; import net.buildtheearth.buildteamtools.utils.ListUtil; import net.buildtheearth.buildteamtools.utils.MenuItems; +import net.buildtheearth.buildteamtools.utils.heads.HeadColor; +import net.buildtheearth.buildteamtools.utils.heads.HeadFactory; +import net.buildtheearth.buildteamtools.utils.heads.HeadTexture; import net.buildtheearth.buildteamtools.utils.menus.AbstractMenu; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -63,17 +65,17 @@ protected void setPreviewItems() { if (roadSlab.length == 0) roadSlab = new XMaterial[]{XMaterial.BARRIER}; - createCounter(CustomHeads.SliderColor.WHITE, LANE_COUNT_SLOT, "Number of Lanes", laneCount, 1, 10, "Lanes"); - createCounter(CustomHeads.SliderColor.LIGHT_GRAY, LANE_WIDTH_SLOT, "Lane Width", laneWidth, 1, 30, "Blocks"); - createCounter(CustomHeads.SliderColor.WHITE, SIDEWALK_WIDTH_SLOT, "Sidewalk Width", sidewalkWidth, 1, 30, "Blocks"); - createCounter(CustomHeads.SliderColor.LIGHT_GRAY, STREET_LAMP_DISTANCE_SLOT, "Street Lamp Distance", streetLampDistance, 5, 500, "Blocks"); + createCounter(HeadColor.WHITE, LANE_COUNT_SLOT, "Number of Lanes", laneCount, 1, 10, "Lanes"); + createCounter(HeadColor.LIGHT_GRAY, LANE_WIDTH_SLOT, "Lane Width", laneWidth, 1, 30, "Blocks"); + createCounter(HeadColor.WHITE, SIDEWALK_WIDTH_SLOT, "Sidewalk Width", sidewalkWidth, 1, 30, "Blocks"); + createCounter(HeadColor.LIGHT_GRAY, STREET_LAMP_DISTANCE_SLOT, "Street Lamp Distance", streetLampDistance, 5, 500, "Blocks"); - setChoiceItems(CustomHeads.SliderColor.WHITE, MARKINGS_MATERIAL_SLOT, "Line Markings Color", markingsMaterial.parseItem()); - setChoiceItems(CustomHeads.SliderColor.LIGHT_GRAY, ROAD_SLAB_SLOT, "Road Elevation Slab", roadSlab[0].parseItem()); - setChoiceItems(CustomHeads.SliderColor.WHITE, SIDEWALK_SLAB_SLOT, "Sidewalk Elevation Slab", sidewalkSlab[0].parseItem()); - setChoiceItems(CustomHeads.SliderColor.LIGHT_GRAY, STREET_LAMP_TYPE_SLOT, "Street Lamp Type", streetLampType); + setChoiceItems(HeadColor.WHITE, MARKINGS_MATERIAL_SLOT, "Line Markings Color", markingsMaterial.parseItem()); + setChoiceItems(HeadColor.LIGHT_GRAY, ROAD_SLAB_SLOT, "Road Elevation Slab", roadSlab[0].parseItem()); + setChoiceItems(HeadColor.WHITE, SIDEWALK_SLAB_SLOT, "Sidewalk Elevation Slab", sidewalkSlab[0].parseItem()); + setChoiceItems(HeadColor.LIGHT_GRAY, STREET_LAMP_TYPE_SLOT, "Street Lamp Type", streetLampType); - getMenu().getSlot(NEXT_ITEM_SLOT).setItem(CustomHeads.getCheckmarkItem("§eNext")); + getMenu().getSlot(NEXT_ITEM_SLOT).setItem(HeadFactory.head(HeadTexture.CHECKMARK, "§eNext")); setBackItem(BACK_ITEM_SLOT, new SidewalkColorMenu(getMenuPlayer(), false)); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavUtils.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavUtils.java index 83c575c8..04030a1d 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavUtils.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavUtils.java @@ -4,16 +4,22 @@ import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import lombok.experimental.UtilityClass; +import net.buildtheearth.OutOfProjectionBoundsException; +import net.buildtheearth.Projection; import net.buildtheearth.buildteamtools.BuildTeamTools; import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.WarpGroup; import net.buildtheearth.buildteamtools.modules.network.NetworkModule; import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam; +import net.buildtheearth.model.GeographicalCoordinate; +import net.buildtheearth.model.MinecraftCoordinate; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.UnsafeValues; +import org.bukkit.World; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -117,4 +123,54 @@ public static void switchToTeam(BuildTeam team, Player clickPlayer) { // Create an "other" Warp Group for warps that don't belong to a warp group return new WarpGroup(team, "Other", "Other warps", -1, null); } + + /** + * Creates a minecraft location object for the specified coordinates, yaw and pitch from the BTE projection. + * Height is extracted from the world. + *
+ * Note: Height returned is actually terrain elevation +2. This is because this method internally uses + * Bukkits @see World::getHighestBlockYAt() already returns elevation+1, and this method deliberately + * adds one to the location elevation on top. + *
+ * The world is extracted from the server config's "earth world". If no earth world is specified then the height defaults to 64 + * and the world is nullified. + * + * @param coordinate Latitude and longitude of the location + * @param yaw Player's yaw + * @param pitch Player's pitch + * @return A bukkit location matching the coordinates, yaw and pitch specified. Height is terrain elevation +2. + */ + public static Location getLocationFromCoordinatesYawPitch(GeographicalCoordinate coordinate, float yaw, float pitch) { + try { + MinecraftCoordinate mcCoord = Projection.toMinecraft(coordinate); + + World earthWorld = BuildTeamTools.getInstance().getEarthWorld(); + int y = 64; + + if (earthWorld != null) + y = earthWorld.getHighestBlockYAt(mcCoord.blockX(), mcCoord.blockZ()) + 1; + + return new Location(earthWorld, mcCoord.x(), y, mcCoord.z(), yaw, pitch); + } catch (OutOfProjectionBoundsException e) { + throw new RuntimeException(e); + } + } + + /** + * Creates a minecraft location object for the specified coordinates from the BTE projection. + * Height is extracted from the world. + *+ * Note: Height returned is actually terrain elevation +2. This is because this method internally uses + * Bukkits @see World::getHighestBlockYAt() already returns elevation+1, and this method deliberately + * adds one to the location elevation on top. + *
+ * The world is extracted from the server config's "earth world". If no earth world is specified then the height defaults to 64 + * and the world is nullified. + * + * @param coordinate Latitude and longitude of the location + * @return A bukkit location matching the coordinates. Height is terrain elevation +2. + */ + public static Location getLocationFromCoordinates(GeographicalCoordinate coordinate) { + return getLocationFromCoordinatesYawPitch(coordinate, 0, 0); + } } diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java index bd488fc0..21ea994a 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java @@ -5,14 +5,17 @@ import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.markers.MarkerSet; import de.bluecolored.bluemap.api.markers.POIMarker; +import net.buildtheearth.OutOfProjectionBoundsException; +import net.buildtheearth.Projection; import net.buildtheearth.buildteamtools.BuildTeamTools; import net.buildtheearth.buildteamtools.modules.ModuleComponent; import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.Warp; import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.WarpGroup; import net.buildtheearth.buildteamtools.modules.network.NetworkModule; -import net.buildtheearth.buildteamtools.utils.geo.CoordinateConversion; import net.buildtheearth.buildteamtools.utils.io.ConfigPaths; import net.buildtheearth.buildteamtools.utils.io.ConfigUtil; +import net.buildtheearth.model.GeographicalCoordinate; +import net.buildtheearth.model.MinecraftCoordinate; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -145,16 +148,23 @@ private void registerWarpsForWorld(BlueMapAPI api, @NotNull WarpGroup warpGroup, */ private void addWarpMarker(@NotNull MarkerSet markerSet, @NotNull Warp warp) { // Convert geographic coordinates to Minecraft world coordinates - double[] xz = CoordinateConversion.convertFromGeo(warp.getLat(), warp.getLon()); - - // Create a POI marker for the warp - POIMarker marker = POIMarker.builder() - .label(warp.getName()) - .position(new Vector3d(xz[0], warp.getY(), xz[1])) - .build(); - - // Add marker to the marker set - markerSet.getMarkers().put(warp.getId().toString(), marker); + try { + MinecraftCoordinate coordinate = Projection.toMinecraft(new GeographicalCoordinate(warp.getLat(), warp.getLon())); + + // Create a POI marker for the warp + POIMarker marker = POIMarker.builder() + .label(warp.getName()) + .position(new Vector3d(coordinate.x(), warp.getY(), coordinate.z())) + .build(); + + // Add marker to the marker set + markerSet.getMarkers().put(warp.getId().toString(), marker); + } catch (OutOfProjectionBoundsException e) { + BuildTeamTools.getInstance().getComponentLogger().warn(Component.text( + "Warp '" + warp.getName() + "' (" + warp.getId() + ") is out of projection bounds. Skipping BlueMap marker registration for this warp.", + NamedTextColor.YELLOW + )); + } } /** diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java index ddd1bac9..3391656b 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java @@ -8,7 +8,7 @@ import net.buildtheearth.buildteamtools.modules.ModuleComponent; import net.buildtheearth.buildteamtools.modules.navigation.NavUtils; import net.buildtheearth.buildteamtools.modules.network.NetworkModule; -import net.buildtheearth.buildteamtools.utils.GeometricUtils; +import net.buildtheearth.model.GeographicalCoordinate; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; @@ -47,11 +47,11 @@ public void addTpllToQueue(ByteArrayDataInput in, Player player) { //Extracts the coordinates from the plugin message double targetLatitude = Double.parseDouble(in.readUTF()); double targetLongitude = Double.parseDouble(in.readUTF()); - double[] coordinates = new double[]{targetLatitude, targetLongitude}; + ChatHelper.logDebug("The coordinates of the tpll event are: %s %s", targetLatitude, targetLongitude); // Creates a bukkit location for this tpll target - Location targetTpllLocation = GeometricUtils.getLocationFromCoordinates(coordinates); + Location targetTpllLocation = NavUtils.getLocationFromCoordinates(new GeographicalCoordinate(targetLatitude, targetLongitude)); ChatHelper.logDebug("Created a bukkit location for this event"); // Location may contain a null world, this is checked for when the tpll event needs to be run // so that the player can be informed that the earth world was not specified diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java index b00a579d..fb883591 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java @@ -4,6 +4,8 @@ import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; +import net.buildtheearth.OutOfProjectionBoundsException; +import net.buildtheearth.Projection; import net.buildtheearth.buildteamtools.BuildTeamTools; import net.buildtheearth.buildteamtools.modules.ModuleComponent; import net.buildtheearth.buildteamtools.modules.navigation.NavUtils; @@ -16,9 +18,9 @@ import net.buildtheearth.buildteamtools.modules.network.NetworkModule; import net.buildtheearth.buildteamtools.modules.network.api.OpenStreetMapAPI; import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam; -import net.buildtheearth.buildteamtools.utils.GeometricUtils; -import net.buildtheearth.buildteamtools.utils.geo.CoordinateConversion; import net.buildtheearth.buildteamtools.utils.menus.AbstractMenu; +import net.buildtheearth.model.GeographicalCoordinate; +import net.buildtheearth.model.MinecraftCoordinate; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; @@ -62,7 +64,7 @@ public void addWarpToQueue(@NotNull ByteArrayDataInput in, Player player) { return; } - Location targetWarpLocation = GeometricUtils.getLocationFromCoordinatesYawPitch(new double[]{warp.getLat(), warp.getLon()}, warp.getYaw(), warp.getPitch()); + Location targetWarpLocation = NavUtils.getLocationFromCoordinatesYawPitch(new GeographicalCoordinate(warp.getLat(), warp.getLon()), warp.getYaw(), warp.getPitch()); targetWarpLocation.setY(warp.getY()); targetWarpLocation.setWorld(Bukkit.getWorld(warp.getWorldName())); @@ -101,7 +103,7 @@ public void warpPlayer(Player player, @NotNull Warp warp) { // If the warp is in the same team, just teleport the player if(warp.getWarpGroup().getBuildTeam().getID().equals(NetworkModule.getInstance().getBuildTeam().getID())) { ChatHelper.logDebug("Warping player %s to warp %s", player.getName(), warp.getName()); - Location loc = GeometricUtils.getLocationFromCoordinatesYawPitch(new double[]{warp.getLat(), warp.getLon()}, warp.getYaw(), warp.getPitch()); + Location loc = NavUtils.getLocationFromCoordinatesYawPitch(new GeographicalCoordinate(warp.getLat(), warp.getLon()), warp.getYaw(), warp.getPitch()); if(loc.getWorld() == null) { World world = Bukkit.getWorld(warp.getWorldName()) == null ? player.getWorld() : Bukkit.getWorld(warp.getWorldName()); @@ -156,37 +158,41 @@ public static void createWarp(Player creator) { public static void createWarp(@NonNull Player creator, WarpGroup group) { // Get the geographic coordinates of the player's location. Location location = creator.getLocation(); - double[] coordinates = CoordinateConversion.convertToGeo(location.getX(), location.getZ()); + try { + GeographicalCoordinate coordinate = Projection.toGeo(new MinecraftCoordinate(location.getX(), location.getZ())); - //Get the country belonging to the coordinates - CompletableFuture- * Note: Height returned is actually terrain elevation +2. This is because this method internally uses - * Bukkits @see World::getHighestBlockYAt() already returns elevation+1, and this method deliberately - * adds one to the location elevation on top. - *
- * Note: Height returned is actually terrain elevation +2. This is because this method internally uses - * Bukkits @see World::getHighestBlockYAt() already returns elevation+1, and this method deliberately - * adds one to the location elevation on top. - * keySet(JsonObject object) { - SetThis class provides a centralized API for generating {@link ItemStack} instances + * based on predefined {@link HeadTexture} values. It supports: + *
All methods delegate to {@link Item#createCustomHeadBase64(String, String, java.util.List)}
+ * using base64 texture data stored in {@link HeadTexture}.
+ */
+public class HeadFactory {
+
+ /**
+ * Creates a custom head using a raw {@link HeadTexture}.
+ *
+ * @param headTexture the texture to use
+ * @param name display name of the item
+ * @param lore optional lore (can be null)
+ * @return the created {@link ItemStack}
+ */
+ public static ItemStack head(HeadTexture headTexture, String name, ArrayList This method attempts to resolve a texture by combining the provided
+ * {@link HeadColor} and the base texture name. If the resolved texture
+ * does not exist, it falls back to the corresponding BLANK texture.
+ *
+ * @param color color scheme to apply
+ * @param headTexture base texture to colorize
+ * @param name display name
+ * @param lore optional lore (can be null)
+ * @return the created {@link ItemStack}
+ */
+ public static ItemStack colorizedHead(HeadColor color, HeadTexture headTexture, String name, ArrayList Resolves a texture using the pattern:
+ * {@code COLOR_VALUE} (e.g. WHITE_5).
+ * Falls back to a blank texture if the value is invalid.
+ *
+ * @param color color scheme
+ * @param value numeric value (e.g. 0–20)
+ * @param name display name
+ * @param lore optional lore (can be null)
+ * @return the created {@link ItemStack}
+ */
+ public static ItemStack number(HeadColor color, int value, String name, ArrayList Resolves textures using the pattern:
+ * {@code TYPE_LETTER_X}. If the letter is invalid,
+ * a QUESTION_MARK fallback texture is used.
+ *
+ * @param type letter style/type (e.g. WOODEN, STONE)
+ * @param letter character input (A–Z)
+ * @param name display name
+ * @param lore optional lore (can be null)
+ * @return the created {@link ItemStack}
+ */
+ public static ItemStack letter(LetterType type, char letter, String name, ArrayList If the value is already at or above the maximum,
+ * a blank head is returned instead.
+ *
+ * @param sliderColor color scheme
+ * @param name display name base
+ * @param value current value
+ * @param maxValue maximum allowed value
+ * @return plus button or blank item
+ */
+ public static ItemStack getCounterPlusItem(HeadColor sliderColor, String name, int value, int maxValue) {
+ if (value >= maxValue)
+ return colorizedHead(sliderColor, HeadTexture.WHITE_BLANK, " ");
+
+ return colorizedHead(sliderColor, HeadTexture.WHITE_PLUS, "§a§l+ §e" + name);
+ }
+
+ /**
+ * Creates a "-" counter button item.
+ *
+ * If the value is already at or below the minimum,
+ * a blank head is returned instead.
+ *
+ * @param sliderColor color scheme
+ * @param name display name base
+ * @param value current value
+ * @param minValue minimum allowed value
+ * @return minus button or blank item
+ */
+ public static ItemStack getCounterMinusItem(HeadColor sliderColor, String name, int value, int minValue) {
+ if (value <= minValue)
+ return colorizedHead(sliderColor, HeadTexture.WHITE_BLANK, " ");
+
+ return colorizedHead(sliderColor, HeadTexture.WHITE_MINUS, "§c§l- §e" + name);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/heads/HeadTexture.java b/src/main/java/net/buildtheearth/buildteamtools/utils/heads/HeadTexture.java
new file mode 100644
index 00000000..5a613aed
--- /dev/null
+++ b/src/main/java/net/buildtheearth/buildteamtools/utils/heads/HeadTexture.java
@@ -0,0 +1,137 @@
+package net.buildtheearth.buildteamtools.utils.heads;
+
+import lombok.Getter;
+
+@Getter
+public enum HeadTexture {
+ EARTH("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjFkZDRmZTRhNDI5YWJkNjY1ZGZkYjNlMjEzMjFkNmVmYTZhNmI1ZTdiOTU2ZGI5YzVkNTljOWVmYWIyNSJ9fX0="),
+ GOLDEN_CUP("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTAyN2Q0YTg2NDVlYzc4YWZhNjIzZmU0MjkwN2YyZGI2NjQxYmZlZjFiNDk5ZmU3N2IzNjBhMWQwYjlhNjMzYyJ9fX0="),
+
+ CHECKMARK("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTkyZTMxZmZiNTljOTBhYjA4ZmM5ZGMxZmUyNjgwMjAzNWEzYTQ3YzQyZmVlNjM0MjNiY2RiNDI2MmVjYjliNiJ9fX0="),
+ GREEN_PLUS("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjA1NmJjMTI0NGZjZmY5OTM0NGYxMmFiYTQyYWMyM2ZlZTZlZjZlMzM1MWQyN2QyNzNjMTU3MjUzMWYifX19"),
+
+ WHITE_BACKWARD("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWQ3M2NmNjZkMzFiODNjZDhiODY0NGMxNTk1OGMxYjczYzhkOTczMjNiODAxMTcwYzFkODg2NGJiNmE4NDZkIn19fQ=="),
+ WHITE_ARROW_LEFT("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2RjOWU0ZGNmYTQyMjFhMWZhZGMxYjViMmIxMWQ4YmVlYjU3ODc5YWYxYzQyMzYyMTQyYmFlMWVkZDUifX19"),
+ WHITE_ARROW_RIGHT("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTU2YTM2MTg0NTllNDNiMjg3YjIyYjdlMjM1ZWM2OTk1OTQ1NDZjNmZjZDZkYzg0YmZjYTRjZjMwYWI5MzExIn19fQ=="),
+
+ WHITE_PLUS("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjBiNTVmNzQ2ODFjNjgyODNhMWMxY2U1MWYxYzgzYjUyZTI5NzFjOTFlZTM0ZWZjYjU5OGRmMzk5MGE3ZTcifX19"),
+ WHITE_MINUS("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzNlNGI1MzNlNGJhMmRmZjdjMGZhOTBmNjdlOGJlZjM2NDI4YjZjYjA2YzQ1MjYyNjMxYjBiMjVkYjg1YiJ9fX0="),
+
+ WHITE_X("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWQxYTNjOTY1NjIzNDg1MjdkNTc5OGYyOTE2MDkyODFmNzJlMTZkNjExZjFhNzZjMGZhN2FiZTA0MzY2NSJ9fX0="),
+ WHITE_BLANK("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTdjMjE0NGZkY2I1NWMzZmMxYmYxZGU1MWNhYmRmNTJjMzg4M2JjYjU3ODkyMzIyNmJlYjBkODVjYjJkOTgwIn19fQ=="),
+
+ WHITE_0("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2YwOTAxOGY0NmYzNDllNTUzNDQ2OTQ2YTM4NjQ5ZmNmY2Y5ZmRmZDYyOTE2YWVjMzNlYmNhOTZiYjIxYjUifX19"),
+ WHITE_1("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2E1MTZmYmFlMTYwNThmMjUxYWVmOWE2OGQzMDc4NTQ5ZjQ4ZjZkNWI2ODNmMTljZjVhMTc0NTIxN2Q3MmNjIn19fQ=="),
+ WHITE_2("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDY5OGFkZDM5Y2Y5ZTRlYTkyZDQyZmFkZWZkZWMzYmU4YTdkYWZhMTFmYjM1OWRlNzUyZTlmNTRhZWNlZGM5YSJ9fX0="),
+ WHITE_3("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmQ5ZTRjZDVlMWI5ZjNjOGQ2Y2E1YTFiZjQ1ZDg2ZWRkMWQ1MWU1MzVkYmY4NTVmZTlkMmY1ZDRjZmZjZDIifX19"),
+ WHITE_4("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjJhM2Q1Mzg5ODE0MWM1OGQ1YWNiY2ZjODc0NjlhODdkNDhjNWMxZmM4MmZiNGU3MmY3MDE1YTM2NDgwNTgifX19"),
+ WHITE_5("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDFmZTM2YzQxMDQyNDdjODdlYmZkMzU4YWU2Y2E3ODA5YjYxYWZmZDYyNDVmYTk4NDA2OTI3NWQxY2JhNzYzIn19fQ=="),
+ WHITE_6("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2FiNGRhMjM1OGI3YjBlODk4MGQwM2JkYjY0Mzk5ZWZiNDQxODc2M2FhZjg5YWZiMDQzNDUzNTYzN2YwYTEifX19"),
+ WHITE_7("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjk3NzEyYmEzMjQ5NmM5ZTgyYjIwY2M3ZDE2ZTE2OGIwMzViNmY4OWYzZGYwMTQzMjRlNGQ3YzM2NWRiM2ZiIn19fQ=="),
+ WHITE_8("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWJjMGZkYTlmYTFkOTg0N2EzYjE0NjQ1NGFkNjczN2FkMWJlNDhiZGFhOTQzMjQ0MjZlY2EwOTE4NTEyZCJ9fX0="),
+ WHITE_9("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDZhYmM2MWRjYWVmYmQ1MmQ5Njg5YzA2OTdjMjRjN2VjNGJjMWFmYjU2YjhiMzc1NWU2MTU0YjI0YTVkOGJhIn19fQ=="),
+ WHITE_10("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2FmM2ZkNDczYTY0OGI4NDdjY2RhMWQyMDc0NDc5YmI3NjcyNzcxZGM0MzUyMjM0NjhlZDlmZjdiNzZjYjMifX19"),
+ WHITE_11("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDhjYWI1M2IwMjA5OGU2ODFhNDZkMWQ3ZjVmZjY5MTc0NmFkZjRlMWZiM2FmZTM1MTZkZDJhZjk0NDU2OSJ9fX0="),
+ WHITE_12("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmZkODNiNWJhYWU0Y2I4NTY5NGExNGQ2ZDEzMzQxZWY3MWFhM2Q5MmQzN2RlMDdiZWE3N2IyYzlkYzUzZSJ9fX0="),
+ WHITE_13("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjFlNTk4NWJlNDg4NmY5ZjE2ZTI0NDdjM2Y0NjEwNTNiNDUxMzQyZDRmYjAxNjZmYjJmODhkZjc0MjIxMzZiNCJ9fX0="),
+ WHITE_14("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTY4MTQ1NjQzOGFlOWIyZDRkMmJmYWI5Y2YzZmZhOTM1NGVlYmRiM2YwMmNlMjk1NzkyOTM0OGU1Yjg1ZmY5NSJ9fX0="),
+ WHITE_15("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzE5YzRkYjczNjViMWI4OGIxMjllNzA0MTg0MjEzZmUwNzhkODhiYzNkNGFlM2Q1MjI5MGY2MWQ5NTVkNTEifX19"),
+ WHITE_16("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWY1ZGQwNzliOThmZGFjNDNhMTlhNzk1YmE0NmZkOTdmMjNlYTc3NTdkOTJhZDBhNjlhZGM5NzMyODllNWEifX19"),
+ WHITE_17("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmU1MWJmZThlYmZhYTU4NWE3ODdlMWNiNzcyYzdmZDdkOWE5Mjg2ZDk1ZWZhNTRkNjZmYTgyNzRmMTg4ZiJ9fX0="),
+ WHITE_18("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjZkZTlhNWEyZDhhMjM3MDcwMTliOWVmNjFkMTY2Mjg2MGUwYjE2NTNkZjZjMjc2MTZiZTJjNzZmY2QxODc1In19fQ=="),
+ WHITE_19("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGJkNDU5MDRkMzRiNjM2YjJmNjQyNjFiM2Q4YmNlZDI1ODI4YzJiOGM0ODIzYjdlMTgzZWU4YTZmMWEyODRkIn19fQ=="),
+ WHITE_20("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRiOTNjNzIxNTE5ZTE0OTY0OWI3ZTRhZmI2ZDc2Y2ZjODE0NjA4YWU5Yzk1ZTdjM2RiNGJmNGJkYWFjZjMxZSJ9fX0="),
+
+ LIGHT_GRAY_PLUS("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjMyZmZmMTYzZTIzNTYzMmY0MDQ3ZjQ4NDE1OTJkNDZmODVjYmJmZGU4OWZjM2RmNjg3NzFiZmY2OWE2NjIifX19"),
+ LIGHT_GRAY_MINUS("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGE1NmRhYjUzZDRlYTFhNzlhOGU1ZWQ2MzIyYzJkNTZjYjcxNGRkMzVlZGY0Nzg3NjNhZDFhODRhODMxMCJ9fX0="),
+
+ LIGHT_GRAY_X("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjVmM2VhN2M3YjI2YTA1NGE5ZmJiYjI4Yjk3YTYwODk5OWMyYzczZGY3NWJmNmIyMzQ4ZDdmYjFlNTllODU1In19fQ=="),
+ LIGHT_GRAY_BLANK("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODFmYjhjZTY0MDhhNTg1MTM4NGUxYzJlZjc1Mzg1MWVhYzE4YmE0MDE4MjY2Y2RkNjY5ZGM5NDQ4NzNkNDIifX19"),
+
+ LIGHT_GRAY_0("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmZhNDU5MTFiMTYyOThjZmNhNGIyMjkxZWVkYTY2NjExM2JjNmYyYTM3ZGNiMmVjZDhjMjc1NGQyNGVmNiJ9fX0="),
+ LIGHT_GRAY_1("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2FmMWIyODBjYWI1OWY0NDY5ZGFiOWYxYTJhZjc5MjdlZDk2YTgxZGYxZTI0ZDUwYThlMzk4NGFiZmU0MDQ0In19fQ=="),
+ LIGHT_GRAY_2("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTRiMWUxZDQyNjEyM2NlNDBjZDZhNTRiMGY4NzZhZDMwYzA4NTM5Y2Y1YTZlYTYzZTg0N2RjNTA3OTUwZmYifX19"),
+ LIGHT_GRAY_3("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTA0Y2NmOGI1MzMyYzE5NmM5ZWEwMmIyMmIzOWI5OWZhY2QxY2M4MmJmZTNmN2Q3YWVlZGMzYzMzMjkwMzkifX19"),
+ LIGHT_GRAY_4("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNmI0ZmMxOGU5NzVmNGYyMjJkODg1MjE2ZTM2M2FkYzllNmQ0NTZhYTI5MDgwZTQ4ZWI0NzE0NGRkYTQzNmY3In19fQ=="),
+ LIGHT_GRAY_5("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWQ4YjIyMjM5NzEyZTBhZDU3OWE2MmFlNGMxMTUxMDNlNzcyODgyNWUxNzUwOGFjZDZjYzg5MTc0ZWU4MzgifX19"),
+ LIGHT_GRAY_6("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWVlZmJhZDE2NzEyYTA1Zjk4ZTRmMGRlNWI0NDg2YWYzOTg3YjQ2ZWE2YWI0ZTNiZTkzZDE0YTgzMmM1NmUifX19"),
+ LIGHT_GRAY_7("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTNlNjlmYTk0MmRmM2Q1ZWE1M2EzYTk3NDkxNjE3NTEwOTI0YzZiOGQ3YzQzNzExOTczNzhhMWNmMmRlZjI3In19fQ=="),
+ LIGHT_GRAY_8("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2QxODRmZDRhYjUxZDQ2MjJmNDliNTRjZTdhMTM5NWMyOWYwMmFkMzVjZTVhYmQ1ZDNjMjU2MzhmM2E4MiJ9fX0="),
+ LIGHT_GRAY_9("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWIyNDU0YTVmYWEyNWY3YzRmNTc3MWQ1MmJiNGY1NWRlYjE5MzlmNzVlZmQ4ZTBhYzQyMTgxMmJhM2RjNyJ9fX0="),
+ LIGHT_GRAY_10("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzZiYWY3ODZjYmI2NmZkOTQzY2I0NWIxZmE1MmYzNjI4OWEzOWYyZDk4NThkOWJlYmUxZTFhMzcwZDdkZmNjIn19fQ=="),
+ LIGHT_GRAY_11("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmRlNDI5YmM3MmIyY2M3ZmY3MGMxZDZjOWYxMTE2ZWMwNzExMmYxZjY0YzU4YmQ4YjljNDgwODNmZTIwMSJ9fX0="),
+ LIGHT_GRAY_12("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWNlMDg0MWYwNjExZDg1NWQ1MGUxNmRkYzNkODM0MDZhN2MwNjRhZDYyNzFkNWM2MzE3M2MwNWQzZDNjYjAifX19"),
+ LIGHT_GRAY_13("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2ViYTZlOTBkZDg4MTYyNjE2MWRlYjllMWE4NzY1YTFhMzRiZjc3MTE3OTMxYWJlYjU3NzhiNTQ5ZmQyYiJ9fX0="),
+ LIGHT_GRAY_14("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYjU4MjAxNGM2YTFhOTYwYjE3MDQ5NDliMTYyYTk4ZDdmNjU1ZmI2NmM2ZjE4MTU0ZWNjZGE2YTVmYTY1In19fQ=="),
+ LIGHT_GRAY_15("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGM0OTYwMWVmNjlkNjNkY2YxZDlmOGVkMThhMzhiNGI3ZjM0NGY3Njc4YjM2NTU1OWE0NzM5ZmNmYmZkOTYyIn19fQ=="),
+ LIGHT_GRAY_16("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjkwZmVmY2YyZGNhMTlhNjcyYzYxNTllMjg3YzRlZGVmNGU5MWY0NTllODNmZmRhZjYxNmI4ZTg4Y2QyYTgifX19"),
+ LIGHT_GRAY_17("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTRlZTU4MTg5ODMyOGMzMTZkMjY2ZWQ2NjRiY2M0ZDI5MzNkOTNiZTc5Mjk4Yzc5OGI3ODlkODNkMzRiM2FlIn19fQ=="),
+ LIGHT_GRAY_18("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjhkOTc3ODhiOTRjMzU2YzJmZjQ0NWY0NGY5NTM0ZmU0OGNiNWQyMTU0N2Q2NGZkYmI5OGFjYzFjNWRlZmUifX19"),
+ LIGHT_GRAY_19("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmM1YjU5ZDk4YzkyNzRmOWI4NDMzNmNmMWFjYWUxNWIxYmU2OWY0MzQ4OGQ2NDI2YzhlMzQzZWMzN2FiMTI2In19fQ=="),
+ LIGHT_GRAY_20("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTRhOTJlZGVkY2FmMTlmYmRjYjUwNWIyMGQ2NzQ3MmJkYTc4MWYyZGQzY2Y3MzcyZmFmOTcyOWQ5NzMxYiJ9fX0="),
+
+ WOODEN_LETTER_A("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTY3ZDgxM2FlN2ZmZTViZTk1MWE0ZjQxZjJhYTYxOWE1ZTM4OTRlODVlYTVkNDk4NmY4NDk0OWM2M2Q3NjcyZSJ9fX0="),
+ WOODEN_LETTER_B("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTBjMWI1ODRmMTM5ODdiNDY2MTM5Mjg1YjJmM2YyOGRmNjc4NzEyM2QwYjMyMjgzZDg3OTRlMzM3NGUyMyJ9fX0="),
+ WOODEN_LETTER_C("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWJlOTgzZWM0NzgwMjRlYzZmZDA0NmZjZGZhNDg0MjY3NjkzOTU1MWI0NzM1MDQ0N2M3N2MxM2FmMThlNmYifX19"),
+ WOODEN_LETTER_D("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzE5M2RjMGQ0YzVlODBmZjlhOGEwNWQyZmNmZTI2OTUzOWNiMzkyNzE5MGJhYzE5ZGEyZmNlNjFkNzEifX19"),
+ WOODEN_LETTER_E("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGJiMjczN2VjYmY5MTBlZmUzYjI2N2RiN2Q0YjMyN2YzNjBhYmM3MzJjNzdiZDBlNGVmZjFkNTEwY2RlZiJ9fX0="),
+ WOODEN_LETTER_F("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjE4M2JhYjUwYTMyMjQwMjQ4ODZmMjUyNTFkMjRiNmRiOTNkNzNjMjQzMjU1OWZmNDllNDU5YjRjZDZhIn19fQ=="),
+ WOODEN_LETTER_G("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWNhM2YzMjRiZWVlZmI2YTBlMmM1YjNjNDZhYmM5MWNhOTFjMTRlYmE0MTlmYTQ3NjhhYzMwMjNkYmI0YjIifX19"),
+ WOODEN_LETTER_H("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzFmMzQ2MmE0NzM1NDlmMTQ2OWY4OTdmODRhOGQ0MTE5YmM3MWQ0YTVkODUyZTg1YzI2YjU4OGE1YzBjNzJmIn19fQ=="),
+ WOODEN_LETTER_I("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDYxNzhhZDUxZmQ1MmIxOWQwYTM4ODg3MTBiZDkyMDY4ZTkzMzI1MmFhYzZiMTNjNzZlN2U2ZWE1ZDMyMjYifX19"),
+ WOODEN_LETTER_J("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2E3OWRiOTkyMzg2N2U2OWMxZGJmMTcxNTFlNmY0YWQ5MmNlNjgxYmNlZGQzOTc3ZWViYmM0NGMyMDZmNDkifX19"),
+ WOODEN_LETTER_K("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTQ2MWIzOGM4ZTQ1NzgyYWRhNTlkMTYxMzJhNDIyMmMxOTM3NzhlN2Q3MGM0NTQyYzk1MzYzNzZmMzdiZTQyIn19fQ=="),
+ WOODEN_LETTER_L("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzE5ZjUwYjQzMmQ4NjhhZTM1OGUxNmY2MmVjMjZmMzU0MzdhZWI5NDkyYmNlMTM1NmM5YWE2YmIxOWEzODYifX19"),
+ WOODEN_LETTER_M("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDljNDVhMjRhYWFiZjQ5ZTIxN2MxNTQ4MzIwNDg0OGE3MzU4MmFiYTdmYWUxMGVlMmM1N2JkYjc2NDgyZiJ9fX0="),
+ WOODEN_LETTER_N("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzViOGIzZDhjNzdkZmI4ZmJkMjQ5NWM4NDJlYWM5NGZmZmE2ZjU5M2JmMTVhMjU3NGQ4NTRkZmYzOTI4In19fQ=="),
+ WOODEN_LETTER_O("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDExZGUxY2FkYjJhZGU2MTE0OWU1ZGVkMWJkODg1ZWRmMGRmNjI1OTI1NWIzM2I1ODdhOTZmOTgzYjJhMSJ9fX0="),
+ WOODEN_LETTER_P("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBhNzk4OWI1ZDZlNjIxYTEyMWVlZGFlNmY0NzZkMzUxOTNjOTdjMWE3Y2I4ZWNkNDM2MjJhNDg1ZGMyZTkxMiJ9fX0="),
+ WOODEN_LETTER_Q("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDM2MDlmMWZhZjgxZWQ0OWM1ODk0YWMxNGM5NGJhNTI5ODlmZGE0ZTFkMmE1MmZkOTQ1YTU1ZWQ3MTllZDQifX19"),
+ WOODEN_LETTER_R("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTVjZWQ5OTMxYWNlMjNhZmMzNTEzNzEzNzliZjA1YzYzNWFkMTg2OTQzYmMxMzY0NzRlNGU1MTU2YzRjMzcifX19"),
+ WOODEN_LETTER_S("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2U0MWM2MDU3MmM1MzNlOTNjYTQyMTIyODkyOWU1NGQ2Yzg1NjUyOTQ1OTI0OWMyNWMzMmJhMzNhMWIxNTE3In19fQ=="),
+ WOODEN_LETTER_T("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTU2MmU4YzFkNjZiMjFlNDU5YmU5YTI0ZTVjMDI3YTM0ZDI2OWJkY2U0ZmJlZTJmNzY3OGQyZDNlZTQ3MTgifX19"),
+ WOODEN_LETTER_U("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjA3ZmJjMzM5ZmYyNDFhYzNkNjYxOWJjYjY4MjUzZGZjM2M5ODc4MmJhZjNmMWY0ZWZkYjk1NGY5YzI2In19fQ=="),
+ WOODEN_LETTER_V("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2M5YTEzODYzOGZlZGI1MzRkNzk5Mjg4NzZiYWJhMjYxYzdhNjRiYTc5YzQyNGRjYmFmY2M5YmFjNzAxMGI4In19fQ=="),
+ WOODEN_LETTER_W("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjY5YWQxYTg4ZWQyYjA3NGUxMzAzYTEyOWY5NGU0YjcxMGNmM2U1YjRkOTk1MTYzNTY3ZjY4NzE5YzNkOTc5MiJ9fX0="),
+ WOODEN_LETTER_X("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWE2Nzg3YmEzMjU2NGU3YzJmM2EwY2U2NDQ5OGVjYmIyM2I4OTg0NWU1YTY2YjVjZWM3NzM2ZjcyOWVkMzcifX19"),
+ WOODEN_LETTER_Y("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzUyZmIzODhlMzMyMTJhMjQ3OGI1ZTE1YTk2ZjI3YWNhNmM2MmFjNzE5ZTFlNWY4N2ExY2YwZGU3YjE1ZTkxOCJ9fX0="),
+ WOODEN_LETTER_Z("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTA1ODJiOWI1ZDk3OTc0YjExNDYxZDYzZWNlZDg1ZjQzOGEzZWVmNWRjMzI3OWY5YzQ3ZTFlMzhlYTU0YWU4ZCJ9fX0="),
+
+ WOODEN_LETTER_QUESTION_MARK("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmFkYzA0OGE3Y2U3OGY3ZGFkNzJhMDdkYTI3ZDg1YzA5MTY4ODFlNTUyMmVlZWQxZTNkYWYyMTdhMzhjMWEifX19"),
+
+ STONE_LETTER_A("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmFjNThiMWEzYjUzYjk0ODFlMzE3YTFlYTRmYzVlZWQ2YmFmY2E3YTI1ZTc0MWEzMmU0ZTNjMjg0MTI3OGMifX19"),
+ STONE_LETTER_B("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDRjNzExNTcxZTdlMjE0ZWU3OGRmZTRlZTBlMTI2M2I5MjUxNmU0MThkZThmYzhmMzI1N2FlMDkwMTQzMSJ9fX0="),
+ STONE_LETTER_C("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmZmNWFhYmVhZDZmZWFmYWFlY2Y0NDIyY2RkNzgzN2NiYjM2YjAzYzk4NDFkZDFiMWQyZDNlZGI3ODI1ZTg1MSJ9fX0="),
+ STONE_LETTER_D("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODkzZTYyMmI1ODE5NzU3OTJmN2MxMTllYzZmNDBhNGYxNmU1NTJiYjk4Nzc2YjBjN2FlMmJkZmQ0MTU0ZmU3In19fQ=="),
+ STONE_LETTER_E("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTE1N2Q2NWIxOTkyMWM3NjBmZjQ5MTBiMzQwNDQ1NWI5YzJlZTM2YWZjMjAyZDg1MzhiYWVmZWM2NzY5NTMifX19"),
+ STONE_LETTER_F("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzU0Y2YyNjFiMmNkNmFiNTRiMGM2MjRmOGY2ZmY1NjVhN2I2M2UyOGUzYjUwYzZkYmZiNTJiNWYwZDdjZjlmIn19fQ=="),
+ STONE_LETTER_G("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDNjOWY4YTc0Y2EwMWJhOGM1NGRlMWVkYzgyZTFmYzA3YTgzOTIzZTY2NTc0YjZmZmU2MDY5MTkyNDBjNiJ9fX0="),
+ STONE_LETTER_H("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjhjNThjNTA5MDM0NjE3YmY4MWVlMGRiOWJlMGJhM2U4NWNhMTU1NjgxNjM5MTRjODc2NjllZGIyZmQ3In19fQ=="),
+ STONE_LETTER_I("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDI0NjMyM2M5ZmIzMTkzMjZlZTJiZjNmNWI2M2VjM2Q5OWRmNzZhMTI0MzliZjBiNGMzYWIzMmQxM2ZkOSJ9fX0="),
+ STONE_LETTER_J("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzU4NDU2Y2Q5YmI4YTdlOTc4NTkxYWUwY2IyNmFmMWFhZGFkNGZhN2ExNjcyNWIyOTUxNDVlMDliZWQ4MDY0In19fQ=="),
+ STONE_LETTER_K("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWY0OWZiNzA4MzY5ZTdiYzI5NDRhZDcwNjk2M2ZiNmFjNmNlNmQ0YzY3MDgxZGRhZGVjZmU1ZGE1MSJ9fX0="),
+ STONE_LETTER_L("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGM4NGY3NTQxNmU4NTNhNzRmNmM3MGZjN2UxMDkzZDUzOTYxODc5OTU1YjQzM2JkOGM3YzZkNWE2ZGYifX19"),
+ STONE_LETTER_M("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzFmZGU5MWIxOWI5MzA5OTEzNzI0ZmVhOWU4NTMxMTI3MWM2N2JjYjc4NTc4ZDQ2MWJmNjVkOTYxMzA3NCJ9fX0="),
+ STONE_LETTER_N("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWM3Yzk3MmU2Nzg1ZDZiMGFjZWI3NzlhYmRkNzcwMmQ5ODM0MWMyNGMyYTcxZTcwMjkzMGVjYTU4MDU1In19fQ=="),
+ STONE_LETTER_O("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODA3M2JiNDRmOTM0NWY5YmIzMWE2NzkwMjdlNzkzOWU0NjE4NDJhOGMyNzQ4NmQ3YTZiODQyYzM5ZWIzOGM0ZSJ9fX0="),
+ STONE_LETTER_P("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjRiMjMxYThkNTU4NzBjZmI1YTlmNGU2NWRiMDZkZDdmOGUzNDI4MmYxNDE2Zjk1ODc4YjE5YWNjMzRhYzk1In19fQ=="),
+ STONE_LETTER_Q("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmZlZGQ2ZjllZmRiMTU2Yjg2OTM1Njk5YjJiNDgzNGRmMGY1ZDIxNDUxM2MwMWQzOGFmM2JkMDMxY2JjYzkyIn19fQ=="),
+ STONE_LETTER_R("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzAzYTFjZDU4M2NiYmZmZGUwOGY5NDNlNTZhYzNlM2FmYWZlY2FlZGU4MzQyMjFhODFlNmRiNmM2NDY2N2Y3ZCJ9fX0="),
+ STONE_LETTER_S("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjY1NzJlNjU1NzI1ZDc4Mzc1YTk4MTdlYjllZThiMzc4MjljYTFmZWE5M2I2MDk1Y2M3YWExOWU1ZWFjIn19fQ=="),
+ STONE_LETTER_T("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzA4YzllZjNhMzc1MWUyNTRlMmFmMWFkOGI1ZDY2OGNjZjVjNmVjM2VhMjY0MTg3N2NiYTU3NTgwN2QzOSJ9fX0="),
+ STONE_LETTER_U("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTVhNmUzYWU1YWU2MjU5MjM1MjQ4MzhmYWM5ZmVmNWI0MjUyN2Y1MDI3YzljYTE0OWU2YzIwNzc5MmViIn19fQ=="),
+ STONE_LETTER_V("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTc1MTIxZjdkOWM2OGRhMGU1YjZhOTZhYzYxNTI5OGIxMmIyZWU1YmQxOTk4OTQzNmVlNjQ3ODc5ZGE1YiJ9fX0="),
+ STONE_LETTER_W("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjdlMTY1YzNlZGM1NTQxZDQ2NTRjNDcyODg3MWU2OTA4ZjYxM2ZjMGVjNDZlODIzYzk2ZWFjODJhYzYyZTYyIn19fQ=="),
+ STONE_LETTER_X("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTkxOWQxNTk0YmY4MDlkYjdiNDRiMzc4MmJmOTBhNjlmNDQ5YTg3Y2U1ZDE4Y2I0MGViNjUzZmRlYzI3MjIifX19"),
+ STONE_LETTER_Y("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTM1NDI0YmI4NjMwNWQ3NzQ3NjA0YjEzZTkyNGQ3NGYxZWZlMzg5MDZlNGU0NThkZDE4ZGNjNjdiNmNhNDgifX19"),
+ STONE_LETTER_Z("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGU5MTIwMGRmMWNhZTUxYWNjMDcxZjg1YzdmN2Y1Yjg0NDlkMzliYjMyZjM2M2IwYWE1MWRiYzg1ZDEzM2UifX19"),
+
+ STONE_LETTER_QUESTION_MARK("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDIzZWFlZmJkNTgxMTU5Mzg0Mjc0Y2RiYmQ1NzZjZWQ4MmViNzI0MjNmMmVhODg3MTI0ZjllZDMzYTY4NzJjIn19fQ==");
+
+ private final String base64;
+
+ HeadTexture(String base64) {
+ this.base64 = base64;
+ }
+
+}
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/heads/LetterType.java b/src/main/java/net/buildtheearth/buildteamtools/utils/heads/LetterType.java
new file mode 100644
index 00000000..1354105d
--- /dev/null
+++ b/src/main/java/net/buildtheearth/buildteamtools/utils/heads/LetterType.java
@@ -0,0 +1,6 @@
+package net.buildtheearth.buildteamtools.utils.heads;
+
+public enum LetterType {
+ WOODEN,
+ STONE
+}
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/io/Constants.java b/src/main/java/net/buildtheearth/buildteamtools/utils/io/Constants.java
deleted file mode 100644
index a1e85fa2..00000000
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/io/Constants.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package net.buildtheearth.buildteamtools.utils.io;
-
-public class Constants {
-
- public static final String API_URL = "https://nwapi.buildtheearth.net";
- public static final int API_PORT = 8080;
-
- public static final String CONFIG_FILE = "config.yml";
- public static final String MODULES_FOLDER = "/modules";
-
- public static final String PLOTSYSTEM_FOLDER = MODULES_FOLDER + "/plotsystem";
-
- public static final String DEFAULT_API_KEY = "00000000-0000-0000-0000-000000000000";
-}
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/io/Errors.java b/src/main/java/net/buildtheearth/buildteamtools/utils/io/Errors.java
deleted file mode 100644
index 823f0e9d..00000000
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/io/Errors.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package net.buildtheearth.buildteamtools.utils.io;
-
-public class Errors {
-
- public static final String WORLD_EDIT_NOT_INSTALLED = "WorldEdit is not installed.";
- public static final String HEAD_DATABASE_NOT_INSTALLED = "HeadDatabase is not installed.";
- public static final String API_KEY_NOT_CONFIGURED = "The API Key was not configured in the config.yml file.";
- public static final String PLOT_SYSTEM_TERRA_ALREADY_ENABLED = "The PlotSystem-Terra plugin is already loaded.";
-
- public static final String BUILD_TEAM_NOT_LOADED = "Failed to load the Build Team!";
- public static final String WORLD_NOT_LOADED = "Failed to load the world from the config.yml file!";
-}
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractMenu.java b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractMenu.java
index 94d30121..6572cbb0 100644
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractMenu.java
+++ b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractMenu.java
@@ -3,7 +3,9 @@
import com.alpsbte.alpslib.utils.item.Item;
import com.cryptomorin.xseries.XMaterial;
import net.buildtheearth.buildteamtools.BuildTeamTools;
-import net.buildtheearth.buildteamtools.utils.CustomHeads;
+import net.buildtheearth.buildteamtools.utils.heads.HeadColor;
+import net.buildtheearth.buildteamtools.utils.heads.HeadFactory;
+import net.buildtheearth.buildteamtools.utils.heads.HeadTexture;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
@@ -105,15 +107,17 @@ protected Player getMenuPlayer() {
* @param maxValue Maximum value of the slider
* @param valueType Type of the value (e.g. "m", "°C", "°F", "blocks", "chunks", "seconds", "minutes", "hours", "days", "weeks", "months", "years", ...)
*/
- protected void createCounter(CustomHeads.SliderColor sliderColor, int sliderItemSlot, String sliderName, int value, int minValue, int maxValue, String valueType){
+ protected void createCounter(HeadColor sliderColor, int sliderItemSlot, String sliderName, int value, int minValue, int maxValue, String valueType){
// Set previous page item
- getMenu().getSlot(sliderItemSlot - 1).setItem(CustomHeads.getCounterMinusItem(sliderColor, sliderName, value, minValue));
+ getMenu().getSlot(sliderItemSlot - 1).setItem(HeadFactory.getCounterMinusItem(sliderColor, sliderName, value, minValue));
// Set current page item
- getMenu().getSlot(sliderItemSlot).setItem(CustomHeads.getCounterCurrentValueItem(sliderColor, sliderName, value, valueType));
-
+ String currentCounterName = "§e" + sliderName + ": §7§l" + value;
+ if (valueType != null)
+ currentCounterName += " " + valueType;
+ getMenu().getSlot(sliderItemSlot).setItem(HeadFactory.number(sliderColor, value, currentCounterName));
// Set next page item
- getMenu().getSlot(sliderItemSlot + 1).setItem(CustomHeads.getCounterPlusItem(sliderColor, sliderName, value, maxValue));
+ getMenu().getSlot(sliderItemSlot + 1).setItem(HeadFactory.getCounterPlusItem(sliderColor, sliderName, value, maxValue));
}
/**
@@ -126,31 +130,31 @@ protected void createCounter(CustomHeads.SliderColor sliderColor, int sliderItem
* @param sliderName Name of the slider
* @param current Current block item
*/
- protected void setChoiceItems(CustomHeads.SliderColor sliderColor, int sliderItemSlot, String sliderName, ItemStack current){
+ protected void setChoiceItems(HeadColor sliderColor, int sliderItemSlot, String sliderName, ItemStack current){
sliderName = "§e" + sliderName;
if(current == null) {
// Set previous page item
- getMenu().getSlot(sliderItemSlot - 1).setItem(CustomHeads.getBlankItem(sliderColor, sliderName + ": §c§lOFF"));
+ getMenu().getSlot(sliderItemSlot - 1).setItem(HeadFactory.colorizedHead(sliderColor, HeadTexture.WHITE_BLANK, sliderName + ": §c§lOFF"));
// Set current page item
getMenu().getSlot(sliderItemSlot).setItem(Item.create(XMaterial.BARRIER.parseMaterial(), sliderName + ": §c§lOFF"));
// Set next page item
- getMenu().getSlot(sliderItemSlot + 1).setItem(CustomHeads.getBlankItem(sliderColor, sliderName + ": §c§lOFF"));
+ getMenu().getSlot(sliderItemSlot + 1).setItem(HeadFactory.colorizedHead(sliderColor, HeadTexture.WHITE_BLANK, sliderName + ": §c§lOFF"));
}else{
ItemMeta meta = current.getItemMeta();
meta.setDisplayName(sliderName);
current.setItemMeta(meta);
// Set previous page item
- getMenu().getSlot(sliderItemSlot - 1).setItem(CustomHeads.getXItem(sliderColor, "§cDisable " + sliderName));
+ getMenu().getSlot(sliderItemSlot - 1).setItem(HeadFactory.colorizedHead(sliderColor, HeadTexture.WHITE_X, "§cDisable " + sliderName));
// Set current page item
getMenu().getSlot(sliderItemSlot).setItem(current);
// Set next page item
- getMenu().getSlot(sliderItemSlot + 1).setItem(CustomHeads.getXItem(sliderColor, "§cDisable " + sliderName));
+ getMenu().getSlot(sliderItemSlot + 1).setItem(HeadFactory.colorizedHead(sliderColor, HeadTexture.WHITE_X, "§cDisable " + sliderName));
}
}
@@ -159,7 +163,7 @@ protected void setChoiceItems(CustomHeads.SliderColor sliderColor, int sliderIte
* @param slot Slot of the back item
*/
protected void setBackItem(int slot, AbstractMenu backMenu){
- getMenu().getSlot(slot).setItem(CustomHeads.getBackItem());
+ getMenu().getSlot(slot).setItem(HeadFactory.head(HeadTexture.WHITE_BACKWARD, "§eBack"));
getMenu().getSlot(slot).setClickHandler((clickPlayer, clickInformation) -> Bukkit.getScheduler().scheduleSyncDelayedTask(BuildTeamTools.getInstance(), () -> {
clickPlayer.closeInventory();
backMenu.reloadMenuAsync();
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractPaginatedMenu.java b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractPaginatedMenu.java
index b6eb6ae1..f493cfb6 100644
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractPaginatedMenu.java
+++ b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/AbstractPaginatedMenu.java
@@ -1,10 +1,13 @@
package net.buildtheearth.buildteamtools.utils.menus;
import net.buildtheearth.buildteamtools.BuildTeamTools;
-import net.buildtheearth.buildteamtools.utils.CustomHeads;
+import net.buildtheearth.buildteamtools.utils.heads.HeadColor;
+import net.buildtheearth.buildteamtools.utils.heads.HeadFactory;
+import net.buildtheearth.buildteamtools.utils.heads.HeadTexture;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
import java.util.List;
@@ -122,6 +125,34 @@ private int getMaxIndex() {
return (currentPage + 1) * maxItemsPerPage;
}
+ /**
+ * @return The current page item.
+ */
+ private ItemStack getCurrentPageItem() {
+ return HeadFactory.number(HeadColor.WHITE, getPage(), "§eCurrent Page §7- §f" + getPage());
+ }
+
+ /**
+ * @return The previous page item.
+ */
+ private ItemStack getPreviousPageItem() {
+ if (currentPage <= 1)
+ return HeadFactory.head(HeadTexture.WHITE_BLANK, " ");
+
+ return HeadFactory.head(HeadTexture.WHITE_ARROW_LEFT, "§ePrevious Page §7- §f" + (getPage() - 1));
+ }
+
+ /**
+ * @return The next page item.
+ */
+ private ItemStack getNextPageItem() {
+ if (!hasNextPage())
+ return HeadFactory.head(HeadTexture.WHITE_BLANK, " ");
+
+ return HeadFactory.head(HeadTexture.WHITE_ARROW_RIGHT, "§eNext Page §7- §f" + (getPage() + 1));
+ }
+
+
/**
* @param reloadSources if true, reload the source collection for the inventory items
@@ -152,13 +183,13 @@ protected void setSwitchPageItems(int switchPageItemSlot){
int currentPage = getPage();
// Set previous page item
- getMenu().getSlot(switchPageItemSlot - 1).setItem(CustomHeads.getPreviousPageItem(currentPage));
+ getMenu().getSlot(switchPageItemSlot - 1).setItem(getPreviousPageItem());
// Set current page item
- getMenu().getSlot(switchPageItemSlot).setItem(CustomHeads.getCurrentPageItem(currentPage));
+ getMenu().getSlot(switchPageItemSlot).setItem(getCurrentPageItem());
// Set next page item
- getMenu().getSlot(switchPageItemSlot + 1).setItem(CustomHeads.getNextPageItem(currentPage, hasNextPage()));
+ getMenu().getSlot(switchPageItemSlot + 1).setItem(getNextPageItem());
}
protected void setSwitchPageItemClickEvents(int switchPageItemSlot){
@@ -178,4 +209,5 @@ protected void setSwitchPageItemClickEvents(int switchPageItemSlot){
}
});
}
+
}
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/BlockListMenu.java b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/BlockListMenu.java
index ab11dd81..92143629 100644
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/BlockListMenu.java
+++ b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/BlockListMenu.java
@@ -1,8 +1,9 @@
package net.buildtheearth.buildteamtools.utils.menus;
import com.alpsbte.alpslib.utils.item.Item;
-import net.buildtheearth.buildteamtools.utils.CustomHeads;
import net.buildtheearth.buildteamtools.utils.MenuItems;
+import net.buildtheearth.buildteamtools.utils.heads.HeadFactory;
+import net.buildtheearth.buildteamtools.utils.heads.HeadTexture;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -45,7 +46,7 @@ protected void setPreviewItems() {
setSwitchPageItems(SWITCH_PAGE_ITEM_SLOT);
if(canProceed())
- getMenu().getSlot(NEXT_ITEM_SLOT).setItem(CustomHeads.getCheckmarkItem("§eNext"));
+ getMenu().getSlot(NEXT_ITEM_SLOT).setItem(HeadFactory.head(HeadTexture.CHECKMARK, "§eNext"));
else
getMenu().getSlot(NEXT_ITEM_SLOT).setItem(MenuItems.ITEM_BACKGROUND);
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/NameListMenu.java b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/NameListMenu.java
index 195b74bd..fcc2f846 100644
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/menus/NameListMenu.java
+++ b/src/main/java/net/buildtheearth/buildteamtools/utils/menus/NameListMenu.java
@@ -1,8 +1,9 @@
package net.buildtheearth.buildteamtools.utils.menus;
import com.alpsbte.alpslib.utils.item.Item;
-import net.buildtheearth.buildteamtools.utils.CustomHeads;
import net.buildtheearth.buildteamtools.utils.MenuItems;
+import net.buildtheearth.buildteamtools.utils.heads.HeadFactory;
+import net.buildtheearth.buildteamtools.utils.heads.HeadTexture;
import org.apache.commons.lang3.tuple.MutablePair;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
@@ -47,7 +48,7 @@ protected void setPreviewItems() {
setSwitchPageItems(SWITCH_PAGE_ITEM_SLOT);
if (canProceed())
- getMenu().getSlot(NEXT_ITEM_SLOT).setItem(CustomHeads.getCheckmarkItem("§eNext"));
+ getMenu().getSlot(NEXT_ITEM_SLOT).setItem(HeadFactory.head(HeadTexture.CHECKMARK, "§eNext"));
else
getMenu().getSlot(NEXT_ITEM_SLOT).setItem(MenuItems.ITEM_BACKGROUND);
diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/raycast/Raycast.java b/src/main/java/net/buildtheearth/buildteamtools/utils/raycast/Raycast.java
deleted file mode 100644
index bd68a22c..00000000
--- a/src/main/java/net/buildtheearth/buildteamtools/utils/raycast/Raycast.java
+++ /dev/null
@@ -1,262 +0,0 @@
-package net.buildtheearth.buildteamtools.utils.raycast;
-
-import com.cryptomorin.xseries.XMaterial;
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.Particle;
-import org.bukkit.World;
-import org.bukkit.block.Block;
-import org.bukkit.entity.Entity;
-import org.bukkit.util.Vector;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-public class Raycast {
- private final double divider = 100.0D;
-
- private ArrayList