├── .gradle ├── 6.7 │ ├── gc.properties │ ├── fileChanges │ │ └── last-build.bin │ ├── fileHashes │ │ ├── fileHashes.bin │ │ ├── fileHashes.lock │ │ └── resourceHashesCache.bin │ ├── javaCompile │ │ ├── jarAnalysis.bin │ │ ├── taskHistory.bin │ │ ├── classAnalysis.bin │ │ └── javaCompile.lock │ └── executionHistory │ │ ├── executionHistory.bin │ │ └── executionHistory.lock ├── vcs-1 │ └── gc.properties ├── configuration-cache │ └── gc.properties ├── buildOutputCleanup │ ├── cache.properties │ ├── outputFiles.bin │ └── buildOutputCleanup.lock └── checksums │ ├── checksums.lock │ ├── md5-checksums.bin │ └── sha1-checksums.bin ├── settings.gradle ├── src └── main │ ├── resources │ ├── barrier.png │ ├── checkmark.png │ ├── checkmark_other.png │ ├── Small Minecraft Font.png │ └── plugin.yml │ └── java │ └── net │ └── blockops │ └── server │ └── mapui │ ├── util │ └── Initializers.java │ ├── component │ ├── MapPanel.java │ ├── components │ │ ├── MapBackground.java │ │ ├── MapImage.java │ │ ├── MapText.java │ │ ├── MapButton.java │ │ └── MapCursor.java │ └── MapComponent.java │ ├── MainRenderer.java │ ├── art │ ├── SmallMinecraftFont.java │ └── MapUIColors.java │ ├── MapUIEventListeners.java │ ├── MapUIManager.java │ ├── map │ ├── MapUI.java │ ├── MapPeripheralBlock.java │ ├── ViewController.java │ └── PlayerController.java │ ├── MapUIEventHandlers.java │ └── REMOVE.java ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── gradlew.bat └── gradlew /.gradle/6.7/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/6.7/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/configuration-cache/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mapui' 2 | 3 | -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Wed Feb 03 23:14:45 MST 2021 2 | gradle.version=6.7 3 | -------------------------------------------------------------------------------- /src/main/resources/barrier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/src/main/resources/barrier.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .idea/ 4 | *.iml 5 | 6 | .classpath 7 | .project 8 | .settings 9 | bin/ 10 | -------------------------------------------------------------------------------- /.gradle/checksums/checksums.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/checksums/checksums.lock -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/src/main/resources/checkmark.png -------------------------------------------------------------------------------- /.gradle/checksums/md5-checksums.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/checksums/md5-checksums.bin -------------------------------------------------------------------------------- /.gradle/checksums/sha1-checksums.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/checksums/sha1-checksums.bin -------------------------------------------------------------------------------- /.gradle/6.7/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /.gradle/6.7/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /.gradle/6.7/javaCompile/jarAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/javaCompile/jarAnalysis.bin -------------------------------------------------------------------------------- /.gradle/6.7/javaCompile/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/javaCompile/taskHistory.bin -------------------------------------------------------------------------------- /src/main/resources/checkmark_other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/src/main/resources/checkmark_other.png -------------------------------------------------------------------------------- /.gradle/6.7/javaCompile/classAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/javaCompile/classAnalysis.bin -------------------------------------------------------------------------------- /.gradle/6.7/javaCompile/javaCompile.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/javaCompile/javaCompile.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /src/main/resources/Small Minecraft Font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/src/main/resources/Small Minecraft Font.png -------------------------------------------------------------------------------- /.gradle/6.7/fileHashes/resourceHashesCache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/fileHashes/resourceHashesCache.bin -------------------------------------------------------------------------------- /.gradle/6.7/executionHistory/executionHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/executionHistory/executionHistory.bin -------------------------------------------------------------------------------- /.gradle/6.7/executionHistory/executionHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/6.7/executionHistory/executionHistory.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/MapUI/main/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: MapUI 2 | main: net.blockops.server.mapui.REMOVE 3 | version: 1.0 4 | description: MapUI 5 | api-version: 1.16 6 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/util/Initializers.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.util; 2 | 3 | public interface Initializers { 4 | 5 | void init(); 6 | 7 | void deinit(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MapUI 2 | A library for creating intuitive GUIs in Minecraft using Maps and their rasters. 3 | 4 | ### Future To Do 5 | - Allow a player to move (WASD) to exit the MapUI (include API) 6 | - Client Side Peripheral Block ArmorStand (use ProtocolLib to listen for PlayerInteractEntity packet) 7 | - Scrolling Input (scrolls in hotbar, but MapUI responds and cancels hotbar scrolling) 8 | - Ability for text input (using chat or Anvil GUI) -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/MapPanel.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Convenience class for creating "Panels" for the ViewController 7 | * Something like "MenuPanel" might be a subclass of this class. 8 | * This makes it easier to transition from one view to another. 9 | */ 10 | public abstract class MapPanel { 11 | 12 | private ArrayList mapComponents; 13 | 14 | public void MapPanel() { 15 | this.mapComponents = new ArrayList<>(); 16 | } 17 | 18 | public void addComponent(MapComponent mapComponent) { 19 | mapComponents.add(mapComponent); 20 | } 21 | 22 | public void removeComponent(MapComponent mapComponent) { 23 | mapComponents.remove(mapComponent); 24 | } 25 | 26 | public void clearComponents() { 27 | mapComponents.clear(); 28 | } 29 | 30 | public ArrayList getMapComponents() { 31 | return mapComponents; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/components/MapBackground.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component.components; 2 | 3 | import net.blockops.server.mapui.art.MapUIColors; 4 | import net.blockops.server.mapui.component.MapComponent; 5 | import org.bukkit.map.MapCanvas; 6 | 7 | public class MapBackground extends MapComponent { 8 | 9 | private byte backgroundColor; 10 | 11 | public MapBackground() { 12 | this(MapUIColors.TRANSPARENT); 13 | } 14 | 15 | public MapBackground(byte backgroundColor) { 16 | this.backgroundColor = backgroundColor; 17 | super.setComponentBounds(0, 0, 127, 127); 18 | } 19 | 20 | @Override 21 | public void draw(MapCanvas mapCanvas) { 22 | this.fillRectangle(mapCanvas, getX(), getY(), getWidth(), getHeight(), backgroundColor); 23 | } 24 | 25 | public byte getBackgroundColor() { 26 | return backgroundColor; 27 | } 28 | 29 | public void setBackgroundColor(byte backgroundColor) { 30 | this.backgroundColor = backgroundColor; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/components/MapImage.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component.components; 2 | 3 | import net.blockops.server.mapui.art.MapUIColors; 4 | import net.blockops.server.mapui.component.MapComponent; 5 | import org.bukkit.map.MapCanvas; 6 | 7 | import java.awt.image.BufferedImage; 8 | 9 | public class MapImage extends MapComponent { 10 | 11 | private byte[][] buffer; 12 | 13 | public MapImage(byte[][] imageMapColors, int x, int y) { 14 | this.buffer = imageMapColors; 15 | super.setComponentBounds(x, y, imageMapColors[0].length, imageMapColors.length); 16 | } 17 | 18 | public MapImage(BufferedImage image, int x, int y) { 19 | this.buffer = MapUIColors.imageToMapColors(image); 20 | super.setComponentBounds(x, y, image.getWidth(), image.getHeight()); 21 | } 22 | 23 | @Override 24 | public void draw(MapCanvas mapCanvas) { 25 | if (buffer != null) { 26 | super.drawPixels(mapCanvas, getX(), getY(), buffer, false); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/MainRenderer.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui; 2 | 3 | import net.blockops.server.mapui.map.MapUI; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.map.MapCanvas; 6 | import org.bukkit.map.MapRenderer; 7 | import org.bukkit.map.MapView; 8 | 9 | public class MainRenderer extends MapRenderer { 10 | 11 | private MapUIManager mapUIManager; 12 | 13 | public MainRenderer(MapUIManager mapUIManager) { 14 | super(true); // Contextual (MapCanvas per player) 15 | this.mapUIManager = mapUIManager; 16 | } 17 | 18 | // This renderer is only meant to give MapUI a per-player MapCanvas reference. 19 | @Override 20 | public void render(MapView mapView, MapCanvas mapCanvas, Player player) { 21 | // TODO create a way that only gets a MapUI if there was a new one registered (inefficient to always call .get) 22 | MapUI mapUI = mapUIManager.getPlayerMapUIs().get(player); 23 | if (mapUI != null && mapUI.getViewController().getMapCanvas() == null) { 24 | mapUI.getViewController().setMapCanvas(mapCanvas); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/components/MapText.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component.components; 2 | 3 | import net.blockops.server.mapui.component.MapComponent; 4 | import org.bukkit.map.MapCanvas; 5 | import org.bukkit.map.MapFont; 6 | 7 | public class MapText extends MapComponent { 8 | 9 | private String text; 10 | private byte color; 11 | private MapFont font; 12 | 13 | public MapText(String text, byte color, MapFont font, int x, int y) { 14 | this.text = text; 15 | this.color = color; 16 | this.font = font; 17 | super.setLocation(x, y); 18 | this.updateSize(); 19 | } 20 | 21 | @Override 22 | public void draw(MapCanvas mapCanvas) { 23 | mapCanvas.drawText(getX(), getY(), font, insertColorInText(text, color)); 24 | } 25 | 26 | public void updateSize() { 27 | int width = font.getWidth(text); 28 | int height = font.getHeight(); 29 | 30 | super.setSize(width, height); 31 | } 32 | 33 | public String insertColorInText(String text, byte color) { 34 | return "§" + color + ";" + text; 35 | } 36 | 37 | public void setText(String text, byte color) { 38 | this.text = text; 39 | this.color = color; 40 | } 41 | 42 | public String getText() { 43 | return text; 44 | } 45 | 46 | public byte getColor() { 47 | return color; 48 | } 49 | 50 | public MapFont getFont() { 51 | return font; 52 | } 53 | 54 | public void setFont(MapFont font) { 55 | this.font = font; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/components/MapButton.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component.components; 2 | 3 | import net.blockops.server.mapui.component.MapComponent; 4 | import net.blockops.server.mapui.map.MapUI; 5 | 6 | import java.awt.Rectangle; 7 | 8 | public abstract class MapButton extends MapComponent { 9 | 10 | private Rectangle clickBounds; 11 | protected boolean hovered; 12 | protected boolean clicked; 13 | 14 | public MapButton(Rectangle bounds) { 15 | this(bounds.x, bounds.y, bounds.width, bounds.height); 16 | } 17 | 18 | public MapButton(int x, int y, int width, int height) { 19 | super.setComponentBounds(x, y, width, height); 20 | this.clickBounds = new Rectangle(getComponentBounds()); 21 | } 22 | 23 | // draw() should be implemented by a child class (and it will draw the unaltered MapButton) 24 | 25 | @Override 26 | public void update() { 27 | if (clicked) { 28 | clicked = false; // Clicked should only be true for one tick 29 | } 30 | } 31 | 32 | public void onClick(MapUI mapUI, MapCursor mapCursor) { 33 | 34 | } 35 | 36 | public void onHoverEnter(MapUI mapUI, MapCursor mapCursor) { 37 | 38 | } 39 | 40 | public void onHoverExit(MapUI mapUI, MapCursor mapCursor) { 41 | 42 | } 43 | 44 | public boolean isHovered() { 45 | return hovered; 46 | } 47 | 48 | public void setHovered(boolean hovered) { 49 | this.hovered = hovered; 50 | } 51 | 52 | public boolean isClicked() { 53 | return clicked; 54 | } 55 | 56 | public void setClicked(boolean clicked) { 57 | this.clicked = clicked; 58 | } 59 | 60 | public Rectangle getClickBounds() { 61 | return clickBounds; 62 | } 63 | 64 | public void setClickBounds(Rectangle clickBounds) { 65 | this.clickBounds = clickBounds; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/art/SmallMinecraftFont.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.art; 2 | 3 | import org.bukkit.map.MapFont; 4 | 5 | import javax.imageio.ImageIO; 6 | import java.awt.Color; 7 | import java.awt.image.BufferedImage; 8 | import java.io.IOException; 9 | 10 | public class SmallMinecraftFont extends MapFont { 11 | 12 | private final String fontSpritesheetPath = "/Small Minecraft Font.png"; 13 | private final int spriteHeight = 7; 14 | private final int spriteWidth = 7; 15 | private final int standardCharHeight = 5; 16 | private final String[] characters = new String[]{ 17 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 18 | "abcdefghijklmnopqrstuvwxyz", 19 | "0123456789!?\"\'()*/+-=,.;:", 20 | "<>{}[]" 21 | }; 22 | 23 | public SmallMinecraftFont() { 24 | } 25 | 26 | public void createFont() throws IOException { 27 | BufferedImage fontImage = ImageIO.read(this.getClass().getResourceAsStream(fontSpritesheetPath)); 28 | 29 | if (fontImage.getWidth() * fontImage.getHeight() % (spriteHeight * spriteWidth) != 0) { 30 | throw new IllegalArgumentException("Font SpriteSheet is the wrong size!"); 31 | } 32 | 33 | for (int charRow = 0; charRow < fontImage.getHeight() / spriteHeight; charRow++) { 34 | for (int charCol = 0; charCol < fontImage.getWidth() / spriteWidth; charCol++) { 35 | if (charCol > characters[charRow].length() - 1) { 36 | continue; 37 | } 38 | 39 | int charWidth = 0; 40 | for (int row = 0; row < spriteHeight; row++) { 41 | for (int col = 0; col < spriteWidth; col++) { 42 | int x = charCol * spriteWidth + col; 43 | int y = charRow * spriteHeight + row; 44 | 45 | if (new Color(fontImage.getRGB(x, y), true).getAlpha() != 0) { 46 | charWidth = Math.max(charWidth, col + 1); 47 | } 48 | } 49 | } 50 | 51 | boolean[] fontData = new boolean[spriteHeight * charWidth]; 52 | 53 | for (int row = 0; row < spriteHeight; row++) { 54 | for (int col = 0; col < charWidth; col++) { 55 | int x = charCol * spriteWidth + col; 56 | int y = charRow * spriteHeight + row; 57 | fontData[row * charWidth + col] = new Color(fontImage.getRGB(x, y), true).getAlpha() != 0; 58 | } 59 | } 60 | CharacterSprite characterSprite = new CharacterSprite(charWidth, spriteHeight, fontData); 61 | this.setChar(characters[charRow].charAt(charCol), characterSprite); 62 | } 63 | } 64 | 65 | CharacterSprite spaceCharacterSprite = new CharacterSprite(2, spriteHeight, new boolean[2 * spriteHeight]); 66 | this.setChar(' ', spaceCharacterSprite); 67 | } 68 | 69 | public int getStandardCharHeight() { 70 | return standardCharHeight; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/MapUIEventListeners.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui; 2 | 3 | import net.blockops.server.mapui.util.Initializers; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.Listener; 7 | import org.bukkit.event.entity.EntityDamageByEntityEvent; 8 | import org.bukkit.event.entity.EntityDamageEvent; 9 | import org.bukkit.event.inventory.InventoryClickEvent; 10 | import org.bukkit.event.player.PlayerArmorStandManipulateEvent; 11 | import org.bukkit.event.player.PlayerDropItemEvent; 12 | import org.bukkit.event.player.PlayerInteractEntityEvent; 13 | import org.bukkit.event.player.PlayerInteractEvent; 14 | import org.bukkit.event.player.PlayerItemHeldEvent; 15 | import org.bukkit.event.player.PlayerKickEvent; 16 | import org.bukkit.event.player.PlayerQuitEvent; 17 | import org.bukkit.event.player.PlayerSwapHandItemsEvent; 18 | import org.bukkit.event.player.PlayerToggleSneakEvent; 19 | 20 | public class MapUIEventListeners implements Listener, Initializers { 21 | 22 | private MapUIManager mapUIManager; 23 | private MapUIEventHandlers mapUIEventHandlers; 24 | 25 | protected MapUIEventListeners(MapUIManager mapUIManager) { 26 | this.mapUIManager = mapUIManager; 27 | this.mapUIEventHandlers = mapUIManager.getMapUIEventHandlers(); 28 | } 29 | 30 | @Override 31 | public void init() { 32 | Bukkit.getPluginManager().registerEvents(this, mapUIManager.getPlugin()); 33 | } 34 | 35 | @Override 36 | public void deinit() { 37 | } 38 | 39 | @EventHandler 40 | public void onPlayerInteract(PlayerInteractEvent event) { 41 | this.mapUIEventHandlers.onPlayerInteractEvent(event); 42 | } 43 | 44 | @EventHandler 45 | public void onPlayerInteractEntityEvent(PlayerInteractEntityEvent event) { 46 | this.mapUIEventHandlers.onPlayerInteractEntityEvent(event); 47 | } 48 | 49 | @EventHandler 50 | public void onPlayerArmorStandManipulateEvent(PlayerArmorStandManipulateEvent event) { 51 | this.mapUIEventHandlers.onPlayerArmorStandManipulateEvent(event); 52 | } 53 | 54 | @EventHandler 55 | public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { 56 | this.mapUIEventHandlers.onEntityDamageByEntityEvent(event); 57 | } 58 | 59 | @EventHandler 60 | public void onEntityDamageEvent(EntityDamageEvent event) { 61 | this.mapUIEventHandlers.onEntityDamageEvent(event); 62 | } 63 | 64 | @EventHandler 65 | public void onPlayerToggleSneakEvent(PlayerToggleSneakEvent event) { 66 | this.mapUIEventHandlers.onPlayerToggleSneakEvent(event); 67 | } 68 | 69 | @EventHandler 70 | public void onPlayerItemHeldEvent(PlayerItemHeldEvent event) { 71 | this.mapUIEventHandlers.onPlayerItemHeldEvent(event); 72 | } 73 | 74 | @EventHandler 75 | protected void onInventoryClickEvent(InventoryClickEvent event) { 76 | this.mapUIEventHandlers.onInventoryClickEvent(event); 77 | } 78 | 79 | @EventHandler 80 | public void onPlayerDropItemEvent(PlayerDropItemEvent event) { 81 | this.mapUIEventHandlers.onPlayerDropItemEvent(event); 82 | } 83 | 84 | @EventHandler 85 | public void onPlayerSwapItemEvent(PlayerSwapHandItemsEvent event) { 86 | this.mapUIEventHandlers.onPlayerSwapItemEvent(event); 87 | } 88 | 89 | @EventHandler 90 | public void onPlayerQuitEvent(PlayerQuitEvent event) { 91 | this.mapUIEventHandlers.onPlayerQuitEvent(event); 92 | } 93 | 94 | @EventHandler 95 | public void onPlayerKickEvent(PlayerKickEvent event) { 96 | this.mapUIEventHandlers.onPlayerKickEvent(event); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/MapUIManager.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui; 2 | 3 | import net.blockops.server.mapui.art.SmallMinecraftFont; 4 | import net.blockops.server.mapui.map.MapUI; 5 | import net.blockops.server.mapui.util.Initializers; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.map.MapRenderer; 9 | import org.bukkit.map.MapView; 10 | import org.bukkit.plugin.Plugin; 11 | 12 | import java.util.HashMap; 13 | 14 | public class MapUIManager implements Initializers { 15 | 16 | private Plugin plugin; 17 | private short emptyMapID; 18 | private MapView mapView; 19 | 20 | private static SmallMinecraftFont smallMinecraftFont; 21 | private MapUIEventListeners mapUIEventListeners; 22 | private MapUIEventHandlers mapUIEventHandlers; 23 | private MainRenderer mainRenderer; 24 | private HashMap playerMapUIs; 25 | 26 | public MapUIManager(Plugin plugin, short emptyMapID) { 27 | this.plugin = plugin; 28 | this.emptyMapID = emptyMapID; 29 | 30 | this.mapUIEventHandlers = new MapUIEventHandlers(this); 31 | this.mapUIEventListeners = new MapUIEventListeners(this); 32 | this.mainRenderer = new MainRenderer(this); 33 | this.playerMapUIs = new HashMap<>(); 34 | } 35 | 36 | @Override 37 | @SuppressWarnings("deprecation") 38 | public void init() { 39 | mapUIEventListeners.init(); 40 | 41 | mapView = Bukkit.getMap(emptyMapID); // Deprecated, but it shouldn't be... 42 | if (mapView == null) { 43 | throw new IllegalArgumentException("Map ID: " + emptyMapID + 44 | " does not exist! Create a map first and then pass a valid Map ID please!"); 45 | } 46 | for (MapRenderer mapRenderer : mapView.getRenderers()) { 47 | mapView.removeRenderer(mapRenderer); 48 | } 49 | mapView.addRenderer(mainRenderer); 50 | } 51 | 52 | @Override 53 | public void deinit() { 54 | for (MapUI mapUI : playerMapUIs.values()) { 55 | mapUI.deinit(); 56 | } 57 | playerMapUIs.clear(); 58 | if (mapView != null) { 59 | mapView.removeRenderer(mainRenderer); 60 | } 61 | } 62 | 63 | public SmallMinecraftFont getSmallMinecraftFont() { 64 | if (smallMinecraftFont == null) { 65 | try { 66 | smallMinecraftFont = new SmallMinecraftFont(); 67 | smallMinecraftFont.createFont(); 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | return smallMinecraftFont; 73 | } 74 | 75 | public void registerPlayerMapUI(Player key, MapUI value) { 76 | if (playerMapUIs.containsKey(key)) { 77 | throw new IllegalStateException("Cannot register an already initialized MapUI!"); 78 | } 79 | this.playerMapUIs.put(key, value); 80 | } 81 | 82 | public void deregisterPlayerMapUI(Player key) { 83 | this.playerMapUIs.remove(key); 84 | } 85 | 86 | public MapUI getMapUIFromRegisteredPlayer(Player player) { 87 | return playerMapUIs.get(player); 88 | } 89 | 90 | public boolean isMapUIPlayerRegistered(Player player) { 91 | return playerMapUIs.containsKey(player); 92 | } 93 | 94 | public MapUIEventHandlers getMapUIEventHandlers() { 95 | return mapUIEventHandlers; 96 | } 97 | 98 | public Plugin getPlugin() { 99 | return plugin; 100 | } 101 | 102 | public MapView getMapView() { 103 | return mapView; 104 | } 105 | 106 | protected HashMap getPlayerMapUIs() { 107 | return playerMapUIs; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/components/MapCursor.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component.components; 2 | 3 | import net.blockops.server.mapui.art.MapUIColors; 4 | import net.blockops.server.mapui.component.MapComponent; 5 | import org.bukkit.map.MapCanvas; 6 | 7 | import java.awt.Rectangle; 8 | 9 | public class MapCursor extends MapComponent { 10 | 11 | private byte[][] cursorPixels = new byte[][]{{MapUIColors.WHITE}}; // Default Single white dot cursor 12 | private Rectangle cursorSensitivityBounds = new Rectangle(); 13 | private Rectangle relativeCursorSensitivityBounds = new Rectangle(); 14 | private boolean hidden = false; 15 | private byte[][] previousPixels; 16 | private int previousX; 17 | private int previousY; 18 | 19 | @Override 20 | public void draw(MapCanvas mapCanvas) { 21 | // ViewController will handle all the Cursor updating 22 | } 23 | 24 | // Because MapCursor will likely be drawn so often and redrawing all mapComponents is expensive, MapCursor's 25 | // update will only draw on pixels it needs to. 26 | public void drawPreviousPixels(MapCanvas mapCanvas) { 27 | if (previousPixels == null) { // First time running 28 | this.savePreviousPixels(mapCanvas); 29 | } 30 | super.drawPixels(mapCanvas, previousX, previousY, previousPixels, true); 31 | } 32 | 33 | public void drawCurrentPixels(MapCanvas mapCanvas) { 34 | if (cursorPixels == null) { 35 | return; 36 | } 37 | this.savePreviousPixels(mapCanvas); 38 | 39 | super.drawPixels(mapCanvas, getX(), getY(), cursorPixels, false); 40 | } 41 | 42 | private void savePreviousPixels(MapCanvas mapCanvas) { 43 | int clampedX = getX() > 127 ? 127 : (getX() < 0 ? 0 : getX()); 44 | int clampedY = getY() > 127 ? 127 : (getY() < 0 ? 0 : getY()); 45 | setLocation(clampedX, clampedY); 46 | 47 | previousX = getX(); 48 | previousY = getY(); 49 | 50 | previousPixels = super.getPixels(mapCanvas, getX(), getY(), cursorPixels.length, 51 | cursorPixels[0].length, previousPixels); // Samples pixels and creates array if necessary 52 | } 53 | 54 | public void setColor(byte color) { 55 | for (int row = 0; row < cursorPixels.length; row++) { 56 | for (int col = 0; col < cursorPixels[0].length; col++) { 57 | if (cursorPixels[row][col] != 0) { 58 | cursorPixels[row][col] = color; 59 | } 60 | } 61 | } 62 | } 63 | 64 | public void setCursorPixels(byte[][] cursorPixels, boolean sensitivityBoundsAreSame) { 65 | this.cursorPixels = cursorPixels; 66 | this.setSize(cursorPixels[0].length, cursorPixels.length); 67 | 68 | if (sensitivityBoundsAreSame) { 69 | this.cursorSensitivityBounds.x = getWidth(); 70 | this.cursorSensitivityBounds.y = getHeight(); 71 | } 72 | } 73 | 74 | public void updateCursorSensitivityLocation() { 75 | // Update only X and Y change here because width and height are static 76 | cursorSensitivityBounds.x = relativeCursorSensitivityBounds.x + getX(); 77 | cursorSensitivityBounds.y = relativeCursorSensitivityBounds.y + getY(); 78 | } 79 | 80 | public void setCursorSensitivityBounds(Rectangle rectangle) { 81 | this.setCursorSensitivityBounds(rectangle.x, rectangle.y, rectangle.width, rectangle.height); 82 | } 83 | 84 | public void setCursorSensitivityBounds(int relativeX, int relativeY, int width, int height) { 85 | this.relativeCursorSensitivityBounds.x = relativeX; 86 | this.relativeCursorSensitivityBounds.y = relativeY; 87 | this.relativeCursorSensitivityBounds.width = width; 88 | this.relativeCursorSensitivityBounds.height = height; 89 | 90 | // Set the width and height of absolute cursor sensitivity bounds because they are static 91 | this.cursorSensitivityBounds.width = width; 92 | this.cursorSensitivityBounds.height = height; 93 | } 94 | 95 | public Rectangle getCursorSensitivityBounds() { 96 | return cursorSensitivityBounds; 97 | } 98 | 99 | public Rectangle getRelativeCursorSensitivityBounds() { 100 | return relativeCursorSensitivityBounds; 101 | } 102 | 103 | public byte[][] getCursorPixels() { 104 | return cursorPixels; 105 | } 106 | 107 | public boolean isHidden() { 108 | return hidden; 109 | } 110 | 111 | public void setHidden(boolean hidden) { 112 | this.hidden = hidden; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/map/MapUI.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.map; 2 | 3 | import net.blockops.server.mapui.MapUIManager; 4 | import net.blockops.server.mapui.util.Initializers; 5 | import org.apache.commons.lang.Validate; 6 | import org.bukkit.Material; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.inventory.ItemStack; 9 | import org.bukkit.inventory.meta.MapMeta; 10 | import org.bukkit.scheduler.BukkitRunnable; 11 | import org.bukkit.scheduler.BukkitTask; 12 | 13 | public class MapUI implements Initializers { 14 | 15 | private MapUIManager mapUIManager; 16 | private Player player; 17 | 18 | private BukkitTask updateTask; 19 | private PlayerController playerController; 20 | private ViewController viewController; 21 | private MapPeripheralBlock mapPeripheralBlock; 22 | private ItemStack mapItem; 23 | private boolean isOpen = false; 24 | 25 | public MapUI(MapUIManager mapUIManager, Player player) { 26 | this.mapUIManager = mapUIManager; 27 | this.player = player; 28 | 29 | this.playerController = new PlayerController(this); 30 | this.viewController = new ViewController(this); 31 | this.mapPeripheralBlock = new MapPeripheralBlock(this); 32 | } 33 | 34 | @Override 35 | public void init() { 36 | playerController.init(); 37 | } 38 | 39 | public void deinit() { 40 | this.close(); 41 | playerController.deinit(); 42 | } 43 | 44 | public void open(String mapItemName, boolean createUpdateTask, int motionlessCloseTicks) { 45 | Validate.isTrue(!isOpen, "MapUI must be closed in order to open it!"); 46 | 47 | this.createMapItem(mapItemName); 48 | 49 | playerController.configureLocations(player.getLocation()); 50 | mapPeripheralBlock.createPeripheralBlockArmorStand(); 51 | playerController.onUIOpen(); 52 | 53 | if (createUpdateTask) { 54 | this.createUpdateTask(motionlessCloseTicks); 55 | } 56 | 57 | isOpen = true; 58 | } 59 | 60 | public void update() { 61 | Validate.isTrue(isOpen, "MapUI must be open in order to update it!"); 62 | 63 | playerController.update(); 64 | viewController.update(); 65 | } 66 | 67 | public void close() { 68 | if (!isOpen) { 69 | return; 70 | } 71 | 72 | playerController.onUIClose(); 73 | mapPeripheralBlock.destroyPeripheralBlockArmorStand(); 74 | 75 | if (updateTask != null) { 76 | updateTask.cancel(); 77 | } 78 | 79 | isOpen = false; 80 | } 81 | 82 | public void onClick() { 83 | viewController.onClick(); 84 | } 85 | 86 | @SuppressWarnings("deprecation") 87 | private void createMapItem(String mapItemName) { 88 | mapItem = new ItemStack(Material.FILLED_MAP); 89 | MapMeta mapMeta = (MapMeta) mapItem.getItemMeta(); 90 | 91 | mapMeta.setDisplayName(mapItemName); 92 | mapMeta.setMapId(mapUIManager.getMapView().getId()); 93 | 94 | mapItem.setItemMeta(mapMeta); 95 | } 96 | 97 | private void createUpdateTask(int motionlessCloseTicks) { 98 | updateTask = new BukkitRunnable() { 99 | int motionlessCount = 0; 100 | boolean checkMotionless = motionlessCloseTicks > 0; 101 | 102 | @Override 103 | public void run() { 104 | MapUI.this.update(); 105 | 106 | if (checkMotionless) { 107 | if (!playerController.didPlayerDirectionChange()) { 108 | motionlessCount++; 109 | } else { 110 | motionlessCount = 0; 111 | } 112 | 113 | if (motionlessCount >= motionlessCloseTicks) { 114 | MapUI.this.close(); 115 | } 116 | } 117 | } 118 | }.runTaskTimer(mapUIManager.getPlugin(), 0, 0); 119 | } 120 | 121 | public MapUIManager getMapUIManager() { 122 | return mapUIManager; 123 | } 124 | 125 | public PlayerController getPlayerController() { 126 | return playerController; 127 | } 128 | 129 | public ViewController getViewController() { 130 | return viewController; 131 | } 132 | 133 | public MapPeripheralBlock getMapPeripheralBlock() { 134 | return mapPeripheralBlock; 135 | } 136 | 137 | public void setMapPeripheralBlock(MapPeripheralBlock mapPeripheralBlock) { 138 | this.mapPeripheralBlock = mapPeripheralBlock; 139 | } 140 | 141 | public ItemStack getMapItem() { 142 | return mapItem; 143 | } 144 | 145 | public boolean isOpen() { 146 | return isOpen; 147 | } 148 | 149 | public Player getPlayer() { 150 | return player; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/map/MapPeripheralBlock.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.map; 2 | 3 | import com.mojang.datafixers.util.Pair; 4 | import net.minecraft.server.v1_16_R3.EnumItemSlot; 5 | import net.minecraft.server.v1_16_R3.PacketPlayOutEntityEquipment; 6 | import org.bukkit.Location; 7 | import org.bukkit.Material; 8 | import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; 9 | import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; 10 | import org.bukkit.entity.ArmorStand; 11 | import org.bukkit.entity.EntityType; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.inventory.ItemStack; 14 | import org.bukkit.scheduler.BukkitRunnable; 15 | import org.bukkit.util.EulerAngle; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class MapPeripheralBlock { 21 | 22 | private MapUI mapUI; 23 | private Player player; 24 | 25 | private ArmorStand peripheralBlockArmorStand; 26 | private ItemStack peripheralBlockItem; 27 | private Location peripheralBlockLocOffset; 28 | private EulerAngle peripheralBlockHeadPose; 29 | private boolean disabled; 30 | 31 | public MapPeripheralBlock(MapUI mapUI) { 32 | this.mapUI = mapUI; 33 | this.player = mapUI.getPlayer(); 34 | 35 | // Defaults 36 | this.peripheralBlockItem = new ItemStack(Material.BLACK_CONCRETE); 37 | //this.peripheralBlockLocOffset = new Location(player.getWorld(), 0.15, 0.27, -0.2); 38 | //this.peripheralBlockHeadPose = new EulerAngle(20d, 0, 0); 39 | this.peripheralBlockLocOffset = new Location(player.getWorld(), 0, 0, 0); 40 | this.peripheralBlockHeadPose = new EulerAngle(20d, 0, 0); 41 | this.disabled = false; 42 | } 43 | 44 | // Create client side EntityArmorStand with vision block item on head (to prevent player 45 | // from seeing the outside world movement when moving the cursor on the map) 46 | public void createPeripheralBlockArmorStand() { 47 | if (peripheralBlockItem == null || disabled) { 48 | return; 49 | } 50 | 51 | Location cursorCenterLocation = mapUI.getPlayerController().getCursorCenterLocation().clone(); 52 | if (peripheralBlockLocOffset != null) { 53 | cursorCenterLocation.add(peripheralBlockLocOffset); 54 | } 55 | peripheralBlockArmorStand = 56 | (ArmorStand) player.getWorld().spawnEntity(cursorCenterLocation, EntityType.ARMOR_STAND); 57 | peripheralBlockArmorStand.setVisible(false); 58 | peripheralBlockArmorStand.setInvulnerable(true); 59 | peripheralBlockArmorStand.setGravity(false); 60 | peripheralBlockArmorStand.setArms(true); // So the player can interact with the AS 61 | 62 | if (peripheralBlockHeadPose != null) { 63 | peripheralBlockArmorStand.setHeadPose(peripheralBlockHeadPose); 64 | } 65 | 66 | // Send Equipment packet with peripheralBlockItem later cause this tick will override it on the client... 67 | if (peripheralBlockItem != null) { 68 | new BukkitRunnable() { 69 | @Override 70 | public void run() { 71 | List> equipmentList = new ArrayList<>(); 72 | equipmentList.add(new Pair<>(EnumItemSlot.HEAD, CraftItemStack.asNMSCopy(peripheralBlockItem))); 73 | 74 | PacketPlayOutEntityEquipment equipmentPacket = 75 | new PacketPlayOutEntityEquipment(peripheralBlockArmorStand.getEntityId(), equipmentList); 76 | ((CraftPlayer) player).getHandle().playerConnection.sendPacket(equipmentPacket); 77 | } 78 | }.runTaskLater(mapUI.getMapUIManager().getPlugin(), 1); 79 | } 80 | } 81 | 82 | public void destroyPeripheralBlockArmorStand() { 83 | if (peripheralBlockArmorStand != null) { 84 | peripheralBlockArmorStand.remove(); 85 | } 86 | } 87 | 88 | public ArmorStand getPeripheralBlockArmorStand() { 89 | return peripheralBlockArmorStand; 90 | } 91 | 92 | public ItemStack getPeripheralBlockItem() { 93 | return peripheralBlockItem; 94 | } 95 | 96 | public void setPeripheralBlockItem(ItemStack peripheralBlockItem) { 97 | this.peripheralBlockItem = peripheralBlockItem; 98 | } 99 | 100 | public Location getPeripheralBlockLocOffset() { 101 | return peripheralBlockLocOffset; 102 | } 103 | 104 | public void setPeripheralBlockLocOffset(Location peripheralBlockLocOffset) { 105 | this.peripheralBlockLocOffset = peripheralBlockLocOffset; 106 | } 107 | 108 | public EulerAngle getPeripheralBlockHeadPose() { 109 | return peripheralBlockHeadPose; 110 | } 111 | 112 | public void setPeripheralBlockHeadPose(EulerAngle peripheralBlockHeadPose) { 113 | this.peripheralBlockHeadPose = peripheralBlockHeadPose; 114 | } 115 | 116 | public boolean isDisabled() { 117 | return disabled; 118 | } 119 | 120 | public void setDisabled(boolean disabled) { 121 | this.disabled = disabled; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/map/ViewController.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.map; 2 | 3 | import net.blockops.server.mapui.art.MapUIColors; 4 | import net.blockops.server.mapui.component.MapComponent; 5 | import net.blockops.server.mapui.component.MapPanel; 6 | import net.blockops.server.mapui.component.components.MapBackground; 7 | import net.blockops.server.mapui.component.components.MapButton; 8 | import net.blockops.server.mapui.component.components.MapCursor; 9 | import org.bukkit.map.MapCanvas; 10 | 11 | import java.util.ArrayList; 12 | 13 | public class ViewController { 14 | 15 | private MapUI mapUI; 16 | 17 | private PlayerController playerController; 18 | private ArrayList mapComponents; 19 | private MapCanvas mapCanvas; 20 | private MapCursor mapCursor; 21 | private MapBackground mapBackground; 22 | private boolean dirty; 23 | 24 | public ViewController(MapUI mapUI) { 25 | this.mapUI = mapUI; 26 | 27 | this.playerController = mapUI.getPlayerController(); 28 | this.mapComponents = new ArrayList<>(); 29 | this.mapBackground = new MapBackground(MapUIColors.TRANSPARENT); // Default transparent background 30 | this.dirty = true; 31 | } 32 | 33 | protected void update() { 34 | if (mapCanvas == null) { // MapCanvas will be set by the MainRenderer class 35 | return; 36 | } 37 | 38 | // Update all components first 39 | for (MapComponent mapComponent : mapComponents) { 40 | mapComponent.update(); // Convenience method for some MapComponents that need to update every tick 41 | } 42 | 43 | // Handle MapCursor 44 | if (mapCursor != null && playerController.didPlayerDirectionChange()) { 45 | mapCursor.setLocation(playerController.getCursorX(), playerController.getCursorY()); 46 | mapCursor.updateCursorSensitivityLocation(); 47 | 48 | // Update raster to display cursor if raster is not dirty 49 | if (!dirty && !mapCursor.isHidden()) { 50 | mapCursor.drawPreviousPixels(mapCanvas); 51 | mapCursor.drawCurrentPixels(mapCanvas); 52 | } 53 | 54 | // Handle Button hovering 55 | MapButton topMostHoveredButton = null; 56 | for (MapComponent mapComponent : mapComponents) { 57 | if (mapComponent instanceof MapButton) { 58 | MapButton mapButton = (MapButton) mapComponent; 59 | 60 | if (mapButton.getClickBounds().intersects(mapCursor.getCursorSensitivityBounds())) { 61 | topMostHoveredButton = mapButton; 62 | } else if (mapButton.isHovered()) { 63 | mapButton.setHovered(false); 64 | mapButton.onHoverExit(mapUI, mapCursor); 65 | } 66 | } 67 | } 68 | if (topMostHoveredButton != null && !topMostHoveredButton.isHovered()) { 69 | topMostHoveredButton.setHovered(true); 70 | topMostHoveredButton.onHoverEnter(mapUI, mapCursor); 71 | } 72 | } 73 | 74 | if (dirty) { 75 | dirty = false; 76 | 77 | // - Clear Canvas and redraw everything (Background -> components -> cursor) - 78 | 79 | mapBackground.draw(mapCanvas); 80 | 81 | for (MapComponent mapComponent : mapComponents) { 82 | if (mapComponent instanceof MapCursor) { 83 | throw new IllegalStateException("MapCursor should not be in MapUI components! Use setCursor()"); 84 | } else if (mapComponent instanceof MapBackground) { 85 | throw new IllegalStateException( 86 | "MapBackground should not be in MapUI components! Use setBackground()"); 87 | } 88 | mapComponent.draw(mapCanvas); 89 | } 90 | 91 | if (mapCursor != null && !mapCursor.isHidden()) { 92 | mapCursor.drawCurrentPixels(mapCanvas); 93 | } 94 | } 95 | } 96 | 97 | public void onClick() { 98 | if (mapCursor == null) { 99 | return; 100 | } 101 | for (MapComponent mapComponent : mapComponents) { 102 | if (mapComponent instanceof MapButton) { 103 | MapButton mapButton = (MapButton) mapComponent; 104 | 105 | if (mapButton.isHovered()) { // Only one button is allowed to be hovered over at a time 106 | mapButton.setClicked(true); 107 | mapButton.onClick(mapUI, mapCursor); 108 | break; 109 | } 110 | } 111 | } 112 | } 113 | 114 | public void setMapPanel(MapPanel mapPanel) { 115 | mapComponents.clear(); 116 | mapComponents.addAll(mapPanel.getMapComponents()); 117 | dirty = true; 118 | } 119 | 120 | public void addComponent(MapComponent mapComponent) { 121 | mapComponents.add(mapComponent); 122 | } 123 | 124 | public void removeComponent(MapComponent mapComponent) { 125 | mapComponents.remove(mapComponent); 126 | } 127 | 128 | public void clearComponents() { 129 | mapComponents.clear(); 130 | } 131 | 132 | public ArrayList getMapComponents() { 133 | return mapComponents; 134 | } 135 | 136 | public MapCanvas getMapCanvas() { 137 | return mapCanvas; 138 | } 139 | 140 | public void setMapCanvas(MapCanvas mapCanvas) { 141 | this.mapCanvas = mapCanvas; 142 | } 143 | 144 | public MapCursor getMapCursor() { 145 | return mapCursor; 146 | } 147 | 148 | public void setMapCursor(MapCursor mapCursor) { 149 | this.mapCursor = mapCursor; 150 | } 151 | 152 | public MapBackground getMapBackground() { 153 | return mapBackground; 154 | } 155 | 156 | public void setMapBackground(MapBackground mapBackground) { 157 | this.mapBackground = mapBackground; 158 | } 159 | 160 | public boolean isDirty() { 161 | return dirty; 162 | } 163 | 164 | public void setDirty(boolean dirty) { 165 | this.dirty = dirty; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/MapUIEventHandlers.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui; 2 | 3 | import net.blockops.server.mapui.map.MapUI; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.block.Action; 6 | import org.bukkit.event.entity.EntityDamageByEntityEvent; 7 | import org.bukkit.event.entity.EntityDamageEvent; 8 | import org.bukkit.event.inventory.InventoryClickEvent; 9 | import org.bukkit.event.player.PlayerArmorStandManipulateEvent; 10 | import org.bukkit.event.player.PlayerDropItemEvent; 11 | import org.bukkit.event.player.PlayerEvent; 12 | import org.bukkit.event.player.PlayerInteractEntityEvent; 13 | import org.bukkit.event.player.PlayerInteractEvent; 14 | import org.bukkit.event.player.PlayerItemHeldEvent; 15 | import org.bukkit.event.player.PlayerKickEvent; 16 | import org.bukkit.event.player.PlayerQuitEvent; 17 | import org.bukkit.event.player.PlayerSwapHandItemsEvent; 18 | import org.bukkit.event.player.PlayerToggleSneakEvent; 19 | import org.bukkit.inventory.EquipmentSlot; 20 | import org.bukkit.scheduler.BukkitRunnable; 21 | 22 | public class MapUIEventHandlers { 23 | 24 | private MapUIManager mapUIManager; 25 | 26 | public MapUIEventHandlers(MapUIManager mapUIManager) { 27 | this.mapUIManager = mapUIManager; 28 | } 29 | 30 | protected void onPlayerInteractEvent(PlayerInteractEvent event) { 31 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 32 | 33 | if (mapUI != null && mapUI.isOpen()) { 34 | event.setCancelled(true); 35 | 36 | if (event.getHand() == EquipmentSlot.HAND) { 37 | if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) { 38 | mapUI.onClick(); 39 | } 40 | } 41 | } 42 | } 43 | 44 | protected void onPlayerInteractEntityEvent(PlayerInteractEntityEvent event) { 45 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 46 | 47 | if (mapUI != null && mapUI.isOpen()) { 48 | event.setCancelled(true); 49 | 50 | if (event.getHand() == EquipmentSlot.HAND) { 51 | mapUI.onClick(); 52 | } 53 | } 54 | } 55 | 56 | protected void onPlayerArmorStandManipulateEvent(PlayerArmorStandManipulateEvent event) { 57 | for (MapUI mapUI : mapUIManager.getPlayerMapUIs().values()) { 58 | if (mapUI.getMapPeripheralBlock().getPeripheralBlockArmorStand().equals(event.getRightClicked()) 59 | && mapUI.getPlayer() != event.getPlayer()) { 60 | event.setCancelled(true); 61 | return; 62 | } 63 | } 64 | 65 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 66 | 67 | if (mapUI != null && mapUI.isOpen()) { 68 | event.setCancelled(true); 69 | 70 | if (event.getHand() == EquipmentSlot.HAND) { 71 | mapUI.onClick(); 72 | } 73 | } 74 | } 75 | 76 | protected void onEntityDamageByEntityEvent(EntityDamageByEntityEvent event) { 77 | if (event.getDamager() instanceof Player) { // If Player left-clicked MapPeripheralBlock/Armorstand 78 | 79 | MapUI mapUI = getPlayerMapUI((Player) event.getDamager()); 80 | 81 | if (mapUI != null && mapUI.isOpen()) { 82 | event.setCancelled(true); 83 | } 84 | } 85 | } 86 | 87 | protected void onEntityDamageEvent(EntityDamageEvent event) { // If another player damaged a MapUI Player 88 | if (event.getEntity() instanceof Player) { 89 | MapUI mapUI = getPlayerMapUI((Player) event.getEntity()); 90 | 91 | if (mapUI != null && mapUI.isOpen() && mapUI.getPlayerController().isDamageCanceled()) { 92 | event.setCancelled(true); 93 | } 94 | } 95 | } 96 | 97 | protected void onPlayerToggleSneakEvent(PlayerToggleSneakEvent event) { 98 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 99 | 100 | if (mapUI != null && mapUI.isOpen()) { 101 | mapUI.close(); 102 | } 103 | } 104 | 105 | protected void onPlayerItemHeldEvent(PlayerItemHeldEvent event) { 106 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 107 | 108 | if (mapUI != null && mapUI.isOpen()) { 109 | mapUI.close(); 110 | } 111 | } 112 | 113 | protected void onInventoryClickEvent(InventoryClickEvent event) { 114 | if (event.getWhoClicked() instanceof Player) { 115 | MapUI mapUI = getPlayerMapUI((Player) event.getWhoClicked()); 116 | 117 | if (mapUI != null && mapUI.isOpen()) { 118 | event.setCancelled(true); // Player can't move stuff in their inventory while MapUI open 119 | 120 | // Close their inventory 2 ticks later (to avoid any problems stated in Javadocs for this event) 121 | new BukkitRunnable() { 122 | @Override 123 | public void run() { 124 | event.getWhoClicked().closeInventory(); 125 | } 126 | }.runTaskLater(mapUI.getMapUIManager().getPlugin(), 2); 127 | } 128 | } 129 | } 130 | 131 | protected void onPlayerDropItemEvent(PlayerDropItemEvent event) { 132 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 133 | if (mapUI != null && mapUI.isOpen()) { 134 | // This event is somewhat troublesome with giving the player the item they used 135 | // to have back... so just cancel it and use 'Sneak' to officially close MapUI. 136 | event.setCancelled(true); 137 | } 138 | } 139 | 140 | protected void onPlayerSwapItemEvent(PlayerSwapHandItemsEvent event) { 141 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 142 | 143 | if (mapUI != null && mapUI.isOpen()) { 144 | event.setCancelled(true); // Player should only use Drop Item 'Q' key to exit MapUI 145 | } 146 | } 147 | 148 | protected void onPlayerQuitEvent(PlayerQuitEvent event) { 149 | this.onPlayerLeaveEvent(event); 150 | } 151 | 152 | protected void onPlayerKickEvent(PlayerKickEvent event) { 153 | this.onPlayerLeaveEvent(event); 154 | } 155 | 156 | private void onPlayerLeaveEvent(PlayerEvent event) { 157 | MapUI mapUI = this.getPlayerMapUI(event.getPlayer()); 158 | 159 | if (mapUI != null && mapUI.isOpen()) { 160 | mapUI.close(); 161 | } 162 | mapUIManager.getPlayerMapUIs().remove(event.getPlayer()); 163 | } 164 | 165 | private MapUI getPlayerMapUI(Player player) { 166 | return mapUIManager.getPlayerMapUIs().get(player); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/component/MapComponent.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.component; 2 | 3 | import org.bukkit.map.MapCanvas; 4 | 5 | import java.awt.Rectangle; 6 | import java.awt.geom.Point2D; 7 | 8 | public abstract class MapComponent { 9 | 10 | private Rectangle componentBounds = new Rectangle(0, 0, 0, 0); 11 | 12 | public abstract void draw(MapCanvas mapCanvas); 13 | 14 | // Convenient method if a MapComponent needs to update every tick 15 | // This method is called before draw() 16 | public void update() { 17 | } 18 | 19 | protected byte[][] getPixels(MapCanvas mapCanvas, int x, int y, int width, int height, byte[][] pixels) { 20 | if (pixels == null) { 21 | pixels = new byte[height][width]; 22 | } 23 | for (int row = 0; row < height; row++) { 24 | for (int col = 0; col < width; col++) { 25 | pixels[row][col] = mapCanvas.getPixel(col + x, row + y); 26 | } 27 | } 28 | return pixels; 29 | } 30 | 31 | protected void drawPixels(MapCanvas mapCanvas, int x, int y, byte[][] pixels, boolean drawTransparency) { 32 | for (int row = 0; row < pixels.length; row++) { 33 | for (int col = 0; col < pixels[0].length; col++) { 34 | byte pixel = pixels[row][col]; 35 | if (drawTransparency) { 36 | mapCanvas.setPixel(col + x, row + y, pixel); 37 | } else if (pixel != 0) { 38 | mapCanvas.setPixel(col + x, row + y, pixel); 39 | } 40 | } 41 | } 42 | } 43 | 44 | protected void drawRectangle(MapCanvas mapCanvas, Rectangle rectangle, byte color) { 45 | this.drawRectangle(mapCanvas, rectangle.x, rectangle.y, rectangle.width, rectangle.height, color); 46 | } 47 | 48 | protected void drawRectangle(MapCanvas mapCanvas, int x, int y, int width, int height, byte color) { 49 | for (int xi = x; xi < x + width; xi++) { 50 | mapCanvas.setPixel(xi, y, color); 51 | mapCanvas.setPixel(xi, y + height - 1, color); 52 | } 53 | for (int yi = y; yi < y + height; yi++) { 54 | mapCanvas.setPixel(x, yi, color); 55 | mapCanvas.setPixel(x + width - 1, yi, color); 56 | } 57 | } 58 | 59 | protected void drawThickRectangle(MapCanvas mapCanvas, int x, int y, int width, int height, int thickness, byte color) { 60 | // TODO drawThickRectangle 61 | throw new UnsupportedOperationException("Not implemented yet!"); 62 | } 63 | 64 | protected void fillRectangle(MapCanvas mapCanvas, Rectangle rectangle, byte color) { 65 | this.fillRectangle(mapCanvas, rectangle.x, rectangle.y, rectangle.width, rectangle.height, color); 66 | } 67 | 68 | protected void fillRectangle(MapCanvas mapCanvas, int x, int y, int width, int height, byte color) { 69 | for (int xi = x; xi < x + width; xi++) { 70 | for (int yi = y; yi < y + height; yi++) { 71 | mapCanvas.setPixel(xi, yi, color); 72 | } 73 | } 74 | } 75 | 76 | protected void drawCircle(MapCanvas mapCanvas, int x, int y, int radius, boolean fill, byte color) { 77 | this.drawElipse(mapCanvas, x, y, radius, radius, fill, color); 78 | } 79 | 80 | protected void drawElipse(MapCanvas mapCanvas, int x, int y, int width, int height, boolean fill, byte color) { 81 | // TODO drawElipse 82 | throw new UnsupportedOperationException("Not implemented yet!"); 83 | } 84 | 85 | protected void drawLine(MapCanvas mapCanvas, int x1, int y1, int x2, int y2, byte color) { 86 | this.drawLine(mapCanvas, x1, y1, x2, y2, 1, color); 87 | } 88 | 89 | protected void drawLine(MapCanvas mapCanvas, int x1, int y1, int x2, int y2, int thickness, byte color) { 90 | if (thickness < 1) { 91 | return; 92 | } 93 | Point2D start = new Point2D.Float(x1, y1); 94 | Point2D end = new Point2D.Float(x2, y2); 95 | 96 | double halfThickness = (double) thickness / 2; 97 | double distance = start.distance(end); 98 | if (distance == 0) { 99 | return; 100 | } 101 | double dx = (double) (x2 - x1) / distance; 102 | double dy = (double) (y2 - y1) / distance; 103 | 104 | double currentX = start.getX(); 105 | double currentY = start.getY(); 106 | 107 | for (double i = 0; i < distance; i++) { 108 | if (thickness > 1) { 109 | double accuracy = 2.5d; 110 | double preciseDX = dx / accuracy; 111 | double preciseDY = dy / accuracy; 112 | 113 | double perpX1 = currentX; 114 | double perpY1 = currentY; 115 | for (int perpIndex = 0; perpIndex < halfThickness * accuracy; perpIndex++) { 116 | perpX1 += preciseDY; 117 | perpY1 -= preciseDX; 118 | mapCanvas.setPixel((int) Math.round(perpX1), (int) Math.round(perpY1), color); 119 | } 120 | 121 | double perpX2 = currentX; 122 | double perpY2 = currentY; 123 | for (int perpIndex = 0; perpIndex < halfThickness * accuracy; perpIndex++) { 124 | perpX2 -= preciseDY; 125 | perpY2 += preciseDX; 126 | mapCanvas.setPixel((int) Math.round(perpX2), (int) Math.round(perpY2), color); 127 | } 128 | } 129 | mapCanvas.setPixel((int) Math.round(currentX), (int) Math.round(currentY), color); 130 | currentX += dx; 131 | currentY += dy; 132 | } 133 | } 134 | 135 | public void centerHorizontallyTo(int leftX, int width) { 136 | int halfWidth = Math.round(((float) width) / 2f); 137 | int halfComponentWidth = Math.round(((float) getWidth() / 2f)); 138 | 139 | int offset = halfWidth - halfComponentWidth; 140 | setLocation(leftX + offset, getY()); 141 | } 142 | 143 | public void centerVerticallyTo(int topY, int height) { 144 | int halfHeight = Math.round(((float) height) / 2f); 145 | int halfComponentHeight = Math.round(((float) getHeight() / 2f)); 146 | 147 | int offset = halfHeight - halfComponentHeight; 148 | setLocation(getX(), topY + offset); 149 | } 150 | 151 | public void setLocation(int x, int y) { 152 | componentBounds.x = x; 153 | componentBounds.y = y; 154 | } 155 | 156 | public int getX() { 157 | return componentBounds.x; 158 | } 159 | 160 | public int getY() { 161 | return componentBounds.y; 162 | } 163 | 164 | public void setSize(int width, int height) { 165 | componentBounds.width = width; 166 | componentBounds.height = height; 167 | } 168 | 169 | public int getWidth() { 170 | return componentBounds.width; 171 | } 172 | 173 | public int getHeight() { 174 | return componentBounds.height; 175 | } 176 | 177 | public void setComponentBounds(int x, int y, int width, int height) { 178 | this.componentBounds.setBounds(x, y, width, height); 179 | } 180 | 181 | public void setComponentBounds(Rectangle componentBounds) { 182 | this.componentBounds = componentBounds; 183 | } 184 | 185 | public Rectangle getComponentBounds() { 186 | return componentBounds; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/art/MapUIColors.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.art; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.awt.Image; 6 | import java.awt.RenderingHints; 7 | import java.awt.image.BufferedImage; 8 | 9 | public class MapUIColors { 10 | 11 | private MapUIColors() { 12 | } 13 | 14 | // Colors found on https://minecraft.gamepedia.com/Map_item_format 15 | private static final Color[] basicMapColors = new Color[]{ 16 | c(0, 0, 0), // Transparent Color 17 | c(127, 178, 56), 18 | c(247, 233, 163), 19 | c(199, 199, 199), 20 | c(255, 0, 0), 21 | c(160, 160, 255), 22 | c(167, 167, 167), 23 | c(0, 124, 0), 24 | c(255, 255, 255), 25 | c(164, 168, 184), 26 | c(151, 109, 77), 27 | c(112, 112, 112), 28 | c(64, 64, 255), 29 | c(143, 119, 72), 30 | c(255, 252, 245), 31 | c(216, 127, 51), 32 | c(178, 76, 216), 33 | c(102, 153, 216), 34 | c(229, 229, 51), 35 | c(127, 204, 25), 36 | c(242, 127, 165), 37 | c(76, 76, 76), 38 | c(153, 153, 153), 39 | c(76, 127, 153), 40 | c(127, 63, 178), 41 | c(51, 76, 178), 42 | c(102, 76, 51), 43 | c(102, 127, 51), 44 | c(153, 51, 51), 45 | c(25, 25, 25), 46 | c(250, 238, 77), 47 | c(92, 219, 213), 48 | c(74, 128, 255), 49 | c(0, 217, 58), 50 | c(129, 86, 49), 51 | c(112, 2, 0), 52 | c(209, 177, 161), 53 | c(159, 82, 36), 54 | c(149, 87, 108), 55 | c(112, 108, 138), 56 | c(186, 133, 36), 57 | c(103, 117, 53), 58 | c(160, 77, 78), 59 | c(57, 41, 35), 60 | c(135, 107, 98), 61 | c(87, 92, 92), 62 | c(122, 73, 88), 63 | c(76, 62, 92), 64 | c(76, 50, 35), 65 | c(76, 82, 42), 66 | c(142, 60, 46), 67 | c(37, 22, 16) 68 | }; 69 | 70 | private static Color c(int r, int g, int b) { 71 | return new Color(r, g, b); 72 | } 73 | 74 | private static class MapUIColor { 75 | 76 | private final byte mapID; 77 | private final Color color; 78 | 79 | MapUIColor(byte mapID, Color color) { 80 | this.mapID = mapID; 81 | this.color = color; 82 | } 83 | 84 | public byte getMapID() { 85 | return mapID; 86 | } 87 | 88 | public Color getColor() { 89 | return color; 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return "MapID: " + mapID + " " + color.toString(); 95 | } 96 | } 97 | 98 | // 4 variations of 51 colors. 99 | private static MapUIColor[] allMapColors = new MapUIColor[basicMapColors.length * 4]; 100 | 101 | static { 102 | for (int index = 0; index < allMapColors.length; index += 4) { 103 | 104 | byte baseMapIDIndex = (byte) Math.floor(index / 4f); 105 | 106 | for (byte variant = 0; variant < 4; variant++) { 107 | byte newMapID = (byte) (baseMapIDIndex * (byte) 4 + variant & 0xFF); 108 | 109 | int rgbMultiplier; 110 | if (variant == 0) { 111 | rgbMultiplier = 180; 112 | } else if (variant == 1) { 113 | rgbMultiplier = 220; 114 | } else if (variant == 2) { 115 | rgbMultiplier = 255; 116 | } else { // 3 117 | rgbMultiplier = 135; 118 | } 119 | 120 | Color basicMapColor = basicMapColors[baseMapIDIndex]; 121 | int r = basicMapColor.getRed() * rgbMultiplier / 255; 122 | int g = basicMapColor.getGreen() * rgbMultiplier / 255; 123 | int b = basicMapColor.getBlue() * rgbMultiplier / 255; 124 | 125 | allMapColors[index + variant] = new MapUIColor(newMapID, new Color(r, g, b)); 126 | } 127 | } 128 | } 129 | 130 | public static byte matchColor(int r, int g, int b) { 131 | return matchColor(new Color(r, g, b)); 132 | } 133 | 134 | public static byte matchColor(Color color) { 135 | if (color.getAlpha() < 255) { 136 | return allMapColors[0].getMapID(); 137 | } 138 | 139 | int smallestDistance = Byte.MAX_VALUE; 140 | MapUIColor bestMatchColor = null; 141 | 142 | for (int i = 4; i < allMapColors.length; i++) { 143 | MapUIColor otherMapUIColor = allMapColors[i]; 144 | 145 | Color otherMapColor = otherMapUIColor.getColor(); 146 | 147 | int distance = (int) Math.sqrt( 148 | Math.pow(otherMapColor.getRed() - color.getRed(), 2) + 149 | Math.pow(otherMapColor.getGreen() - color.getGreen(), 2) + 150 | Math.pow(otherMapColor.getBlue() - color.getBlue(), 2)); 151 | if (distance < smallestDistance) { 152 | smallestDistance = distance; 153 | bestMatchColor = otherMapUIColor; 154 | } 155 | } 156 | return bestMatchColor == null ? 0 : bestMatchColor.getMapID(); 157 | } 158 | 159 | /** 160 | * @param image the image to resize 161 | * @param resizeWidth -1 to not resize 162 | * @param resizeHeight -1 to not resize 163 | * @param isPixelArtImage use specific resizing interpolation for pixel art or regular images 164 | * @return the resized image 165 | */ 166 | public static BufferedImage resizeImage(Image image, int resizeWidth, int resizeHeight, boolean isPixelArtImage) { 167 | if (resizeHeight == -1) { 168 | resizeHeight = image.getHeight(null); 169 | } 170 | if (resizeWidth == -1) { 171 | resizeWidth = image.getWidth(null); 172 | } 173 | 174 | BufferedImage bufferedImage = new BufferedImage(resizeWidth, resizeHeight, BufferedImage.TYPE_INT_ARGB); 175 | Graphics2D graphics = bufferedImage.createGraphics(); 176 | if (isPixelArtImage) { 177 | graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); 178 | // Nearest Neighbor works best with pixel art. 179 | graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 180 | RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); 181 | } else { 182 | graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 183 | graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 184 | } 185 | graphics.drawImage(image, 0, 0, resizeWidth, resizeHeight, 0, 0, image.getWidth(null), image.getHeight(null), 186 | null); 187 | graphics.dispose(); 188 | 189 | return bufferedImage; 190 | } 191 | 192 | /** 193 | * @param image the image to convert to map colors 194 | * @return a [row][col] byte array of map colors 195 | */ 196 | public static byte[][] imageToMapColors(BufferedImage image) { 197 | byte[][] mapPixels = new byte[image.getHeight()][image.getWidth()]; 198 | 199 | for (int row = 0; row < image.getHeight(); row++) { 200 | for (int col = 0; col < image.getWidth(); col++) { 201 | mapPixels[row][col] = matchColor(new Color(image.getRGB(col, row), true)); 202 | } 203 | } 204 | 205 | return mapPixels; 206 | } 207 | 208 | // Declaring these constants at the bottom is necessary for matchColor method to work properly. 209 | 210 | public static byte TRANSPARENT = 0; 211 | 212 | public static byte BLACK = matchColor(new Color(10, 10, 10)); 213 | 214 | public static byte WHITE = matchColor(new Color(255, 255, 255)); 215 | 216 | public static byte GRAY = matchColor(new Color(145, 145, 145)); 217 | 218 | public static byte LIGHT_GRAY = matchColor(new Color(190, 190, 190)); 219 | 220 | public static byte DARK_GRAY = matchColor(new Color(40, 40, 40)); 221 | 222 | public static byte RED = matchColor(new Color(255, 0, 0)); 223 | 224 | public static byte DARK_RED = matchColor(new Color(150, 0, 0)); 225 | 226 | public static byte ORANGE = matchColor(new Color(216, 127, 51)); 227 | 228 | public static byte YELLOW = matchColor(new Color(255, 255, 85)); 229 | 230 | public static byte GREEN = matchColor(new Color(10, 200, 10)); 231 | 232 | public static byte AQUA = matchColor(new Color(85, 255, 255)); 233 | 234 | public static byte BLUE = matchColor(new Color(85, 85, 255)); 235 | 236 | public static byte PURPLE = matchColor(new Color(115, 12, 115)); 237 | } 238 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/REMOVE.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui; 2 | 3 | import net.blockops.server.mapui.art.MapUIColors; 4 | import net.blockops.server.mapui.component.components.*; 5 | import net.blockops.server.mapui.map.MapUI; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.ChatColor; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.PlayerCommandPreprocessEvent; 11 | import org.bukkit.map.MapCanvas; 12 | import org.bukkit.plugin.java.JavaPlugin; 13 | 14 | import javax.imageio.ImageIO; 15 | import java.awt.*; 16 | import java.awt.image.BufferedImage; 17 | import java.io.IOException; 18 | 19 | public class REMOVE extends JavaPlugin implements Listener { 20 | 21 | private MapUIManager mapUIManager; 22 | 23 | @Override 24 | public void onEnable() { 25 | mapUIManager = new MapUIManager(this, (short) 0); 26 | mapUIManager.init(); 27 | getServer().getPluginManager().registerEvents(this, this); 28 | } 29 | 30 | @EventHandler 31 | public void onCommand(PlayerCommandPreprocessEvent event) { 32 | if (event.getMessage().equalsIgnoreCase("/testMapUI")) { 33 | MapUI mapUI = mapUIManager.getMapUIFromRegisteredPlayer(event.getPlayer()); 34 | if (mapUI != null) { 35 | mapUI.getViewController().clearComponents(); 36 | mapUI.getViewController().setDirty(true); 37 | } else { 38 | mapUI = new MapUI(mapUIManager, event.getPlayer()); 39 | mapUI.init(); 40 | mapUIManager.registerPlayerMapUI(event.getPlayer(), mapUI); 41 | } 42 | final MapUI mapUIReference = mapUI; // For inner classes 43 | 44 | MapBackground mapBackground = new MapBackground(MapUIColors.TRANSPARENT); 45 | mapUI.getViewController().setMapBackground(mapBackground); 46 | 47 | byte x = MapUIColors.WHITE; 48 | byte[][] cursorPixels = new byte[][]{ 49 | {x, x, x}, 50 | {x, x, 0}, 51 | {x, 0, x} 52 | }; 53 | MapCursor mapCursor = new MapCursor(); 54 | mapCursor.setCursorPixels(cursorPixels, false); 55 | mapCursor.setCursorSensitivityBounds(0, 0, 2, 2); 56 | mapUI.getViewController().setMapCursor(mapCursor); 57 | 58 | MapText mapText = new MapText( 59 | "Attachment", MapUIColors.DARK_RED, mapUIManager.getSmallMinecraftFont(), 0, 0); 60 | mapText.centerHorizontallyTo(0, 128); 61 | mapText.centerVerticallyTo(0, 64); 62 | 63 | Rectangle buttonBounds = new Rectangle(mapText.getComponentBounds()); 64 | buttonBounds.grow(2, 1); 65 | 66 | MapButton mapButton = new MapButton(buttonBounds) { 67 | @Override 68 | public void draw(MapCanvas mapCanvas) { 69 | if (isHovered()) { 70 | drawRectangle(mapCanvas, getComponentBounds(), MapUIColors.RED); 71 | } else { 72 | drawRectangle(mapCanvas, getComponentBounds(), MapUIColors.DARK_GRAY); 73 | } 74 | mapText.draw(mapCanvas); 75 | } 76 | 77 | @Override 78 | public void onHoverEnter(MapUI mapUI, MapCursor mapCursor) { 79 | mapUI.getViewController().setDirty(true); 80 | } 81 | 82 | @Override 83 | public void onHoverExit(MapUI mapUI, MapCursor mapCursor) { 84 | mapUI.getViewController().setDirty(true); 85 | } 86 | 87 | @Override 88 | public void onClick(MapUI mapUI, MapCursor mapCursor) { 89 | Bukkit.broadcastMessage(mapUI.getPlayer().getName() + " clicked MapButton!"); 90 | } 91 | }; 92 | mapUI.getViewController().addComponent(mapButton); 93 | 94 | BufferedImage barrierImage = null; 95 | BufferedImage checkmarkImage = null; 96 | BufferedImage tateImage = null; 97 | try { 98 | barrierImage = ImageIO.read(this.getClass().getResourceAsStream("/barrier.png")); 99 | checkmarkImage = ImageIO.read(this.getClass().getResourceAsStream("/checkmark.png")); 100 | //tateImage = ImageIO.read(this.getClass().getResourceAsStream("/tate.png")); 101 | } catch (IOException e) { 102 | e.printStackTrace(); 103 | } 104 | 105 | if (barrierImage != null) { 106 | MapImage cancelMapImage = new MapImage(barrierImage, 0, 0); 107 | cancelMapImage.centerVerticallyTo(64, 64); 108 | cancelMapImage.centerHorizontallyTo(0, 64); 109 | 110 | Rectangle checkmarkButtonBounds = new Rectangle(cancelMapImage.getComponentBounds()); 111 | checkmarkButtonBounds.grow(2, 2); 112 | 113 | MapButton cancelButton = new MapButton(checkmarkButtonBounds) { 114 | @Override 115 | public void draw(MapCanvas mapCanvas) { 116 | if (isHovered()) { 117 | drawRectangle(mapCanvas, getComponentBounds(), MapUIColors.WHITE); 118 | } else { 119 | drawRectangle(mapCanvas, getComponentBounds(), MapUIColors.DARK_GRAY); 120 | } 121 | cancelMapImage.draw(mapCanvas); 122 | } 123 | 124 | public void onHoverEnter(MapUI mapUI, MapCursor mapCursor) { 125 | mapUI.getViewController().setDirty(true); 126 | } 127 | 128 | @Override 129 | public void onHoverExit(MapUI mapUI, MapCursor mapCursor) { 130 | mapUI.getViewController().setDirty(true); 131 | } 132 | 133 | @Override 134 | public void onClick(MapUI mapUI, MapCursor mapCursor) { 135 | Bukkit.broadcastMessage(mapUI.getPlayer().getName() + " clicked cancel!"); 136 | } 137 | }; 138 | mapUI.getViewController().addComponent(cancelButton); 139 | } 140 | if (checkmarkImage != null) { 141 | MapImage checkmarkMapImage = new MapImage(checkmarkImage, 0, 0); 142 | checkmarkMapImage.centerVerticallyTo(64, 64); 143 | checkmarkMapImage.centerHorizontallyTo(64, 64); 144 | 145 | Rectangle checkmarkButtonBounds = new Rectangle(checkmarkMapImage.getComponentBounds()); 146 | checkmarkButtonBounds.grow(2, 2); 147 | MapButton checkmarkButton = new MapButton(checkmarkButtonBounds) { 148 | @Override 149 | public void draw(MapCanvas mapCanvas) { 150 | if (isHovered()) { 151 | drawRectangle(mapCanvas, getComponentBounds(), MapUIColors.WHITE); 152 | } else { 153 | drawRectangle(mapCanvas, getComponentBounds(), MapUIColors.DARK_GRAY); 154 | } 155 | checkmarkMapImage.draw(mapCanvas); 156 | } 157 | 158 | public void onHoverEnter(MapUI mapUI, MapCursor mapCursor) { 159 | mapUI.getViewController().setDirty(true); 160 | } 161 | 162 | @Override 163 | public void onHoverExit(MapUI mapUI, MapCursor mapCursor) { 164 | mapUI.getViewController().setDirty(true); 165 | } 166 | 167 | @Override 168 | public void onClick(MapUI mapUI, MapCursor mapCursor) { 169 | Bukkit.broadcastMessage(mapUI.getPlayer().getName() + " clicked checkmark!"); 170 | } 171 | }; 172 | mapUI.getViewController().addComponent(checkmarkButton); 173 | } 174 | 175 | if (tateImage != null) { 176 | MapImage tateMapImage = new MapImage(tateImage, 0, 0); 177 | mapUI.getViewController().addComponent(tateMapImage); 178 | } 179 | 180 | //mapUI.getViewController().addComponent(new MapComponent() { 181 | // @Override 182 | // public void update() { 183 | // if (mapUIReference.getPlayerController().didPlayerDirectionChange()) { 184 | // mapUIReference.getViewController().setDirty(true); 185 | // } 186 | // } 187 | // 188 | // @Override 189 | // public void draw(MapCanvas mapCanvas) { 190 | // PlayerController playerController = mapUIReference.getPlayerController(); 191 | // drawLine(mapCanvas, 0, 0, playerController.getCursorX(), playerController.getCursorY(), 2, 192 | // MapUIColors.RED); 193 | // } 194 | //}); 195 | mapUI.getViewController().setDirty(true); 196 | 197 | mapUI.open(ChatColor.BOLD.toString() + ChatColor.BLUE + "Sneak to Exit", true, -1); 198 | } 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/net/blockops/server/mapui/map/PlayerController.java: -------------------------------------------------------------------------------- 1 | package net.blockops.server.mapui.map; 2 | 3 | import net.blockops.server.mapui.util.Initializers; 4 | import net.minecraft.server.v1_16_R3.DataWatcher; 5 | import net.minecraft.server.v1_16_R3.DataWatcherObject; 6 | import net.minecraft.server.v1_16_R3.DataWatcherRegistry; 7 | import net.minecraft.server.v1_16_R3.PacketPlayOutEntityMetadata; 8 | import org.apache.commons.lang.Validate; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.Location; 11 | import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.inventory.ItemStack; 14 | import org.bukkit.scoreboard.Scoreboard; 15 | import org.bukkit.scoreboard.Team; 16 | import org.bukkit.util.Vector; 17 | 18 | public class PlayerController implements Initializers { 19 | 20 | private MapUI mapUI; 21 | private Player player; 22 | 23 | private final float centerPitch = 70f; 24 | private final float centerYaw = 40f; 25 | private final float pitchBound = 20f; 26 | private final float yawBound = 40f; 27 | private Location previousLocation; 28 | private Location cursorCenterLocation; 29 | private float lastPitch; 30 | private float lastYaw; 31 | private ItemStack initialMainHandItemStack; 32 | private ItemStack initialOffHandItemStack; 33 | private boolean wasFlightAllowed; 34 | private boolean wasFlying; 35 | private final float defaultFlySpeed = 0.1f; 36 | private float previousFlySpeed = defaultFlySpeed; 37 | private Vector previousVelocityVector; 38 | private int playerMoveIssueCount; 39 | private boolean damageCanceled = true; 40 | private boolean playerDirectionChanged; 41 | private int cursorX; 42 | private int cursorY; 43 | private static DataWatcherObject baseDataWatcherObject = new DataWatcherObject<>(0, DataWatcherRegistry.a); 44 | private DataWatcher temporaryDataWatcher; 45 | private DataWatcher entityPlayerDataWatcher; 46 | private Scoreboard collisionScoreboard; 47 | private Team collisionRuleTeam; 48 | 49 | public PlayerController(MapUI mapUI) { 50 | this.mapUI = mapUI; 51 | this.player = mapUI.getPlayer(); 52 | 53 | this.temporaryDataWatcher = new DataWatcher(((CraftPlayer) player).getHandle()); 54 | this.entityPlayerDataWatcher = ((CraftPlayer) player).getHandle().getDataWatcher(); 55 | } 56 | 57 | @Override 58 | public void init() { 59 | temporaryDataWatcher.register(baseDataWatcherObject, (byte) 0); 60 | } 61 | 62 | @Override 63 | public void deinit() { 64 | } 65 | 66 | public void onUIOpen() { 67 | initialMainHandItemStack = player.getInventory().getItemInMainHand(); 68 | initialOffHandItemStack = player.getInventory().getItemInOffHand(); 69 | player.getInventory().setItemInMainHand(mapUI.getMapItem()); 70 | player.getInventory().setItemInOffHand(null); 71 | 72 | if (cursorCenterLocation == null || previousLocation == null) { 73 | this.configureLocations(player.getLocation()); 74 | } 75 | this.freezePlayer(cursorCenterLocation); 76 | this.setClientCollidable(false); 77 | this.setClientInvisible(true); 78 | playerDirectionChanged = true; 79 | 80 | this.calculateCursorPosition(cursorCenterLocation.getPitch(), cursorCenterLocation.getYaw()); 81 | } 82 | 83 | protected void update() { 84 | Location location = player.getLocation(); 85 | 86 | // Check if player moved at all (they may have deactivated flying causing gravity to move them!) 87 | if (location.getX() != cursorCenterLocation.getX() || location.getY() != cursorCenterLocation.getY() 88 | || location.getZ() != cursorCenterLocation.getZ() || !player.isFlying()) { 89 | 90 | this.unFreezePlayer(); // reset flying speeds and such 91 | this.freezePlayer(cursorCenterLocation); // reset player location and flying speed/flying 92 | 93 | if (player.isFlying()) { // Player was flying and therefore must have been a world movement issue 94 | playerMoveIssueCount++; 95 | 96 | // This ensures that the player is not involuntarily being moved indefinitely 97 | if (playerMoveIssueCount >= 5) { 98 | mapUI.close(); 99 | return; 100 | } 101 | } 102 | } 103 | 104 | float pitch = location.getPitch(); 105 | float yaw = location.getYaw(); 106 | 107 | boolean outOfBounds = clampToBounds(location); 108 | if (outOfBounds) { 109 | player.teleport(location); 110 | } 111 | 112 | if (!outOfBounds) { 113 | playerDirectionChanged = pitch != lastPitch || yaw != lastYaw; 114 | lastPitch = pitch; 115 | lastYaw = yaw; 116 | 117 | if (playerDirectionChanged) { 118 | this.calculateCursorPosition(pitch, yaw); 119 | } 120 | } 121 | } 122 | 123 | public void onUIClose() { 124 | this.unFreezePlayer(); 125 | player.teleport(previousLocation); 126 | playerMoveIssueCount = 0; 127 | 128 | this.setClientCollidable(true); 129 | 130 | player.getInventory().setItemInMainHand(initialMainHandItemStack); 131 | player.getInventory().setItemInOffHand(initialOffHandItemStack); 132 | 133 | this.setClientInvisible(false); 134 | } 135 | 136 | public void configureLocations(Location playerLocation) { 137 | previousLocation = playerLocation; 138 | 139 | cursorCenterLocation = playerLocation.clone(); 140 | cursorCenterLocation.setYaw(centerYaw); 141 | cursorCenterLocation.setPitch(centerPitch); 142 | } 143 | 144 | private boolean clampToBounds(Location location) { 145 | float pitch = location.getPitch(); 146 | float yaw = location.getYaw(); 147 | 148 | boolean outOfBounds = false; 149 | float lowerPitchBound = centerPitch + pitchBound; 150 | if (pitch > lowerPitchBound) { // Should be impossible, but just check for it anyway... 151 | location.setPitch(lowerPitchBound); 152 | outOfBounds = true; 153 | } 154 | float upperPitchBound = centerPitch - pitchBound; 155 | if (pitch < upperPitchBound) { 156 | location.setPitch(upperPitchBound); 157 | outOfBounds = true; 158 | } 159 | float rightBoundYaw = centerYaw + yawBound; 160 | if (yaw > rightBoundYaw) { 161 | location.setYaw(rightBoundYaw); 162 | outOfBounds = true; 163 | } 164 | float leftBoundYaw = centerYaw - yawBound; 165 | if (yaw < leftBoundYaw) { 166 | location.setYaw(leftBoundYaw); 167 | outOfBounds = true; 168 | } 169 | return outOfBounds; 170 | } 171 | 172 | private void calculateCursorPosition(float pitch, float yaw) { 173 | cursorX = (int) (yaw / (centerYaw + yawBound) * 128); 174 | cursorY = (int) ((pitch - 50) / (centerPitch + pitchBound - 50) * 128); // -50 because min pitch is 50 175 | } 176 | 177 | public void teleportToCursorLocation(int x, int y) { 178 | Validate.isTrue(x >= 0 && y >= 0 && x < 128 && y < 128, "Out of bounds X or Y!"); 179 | 180 | Location location = player.getLocation(); 181 | this.clampToBounds(location); // Just in case 182 | 183 | float pitch = ((float) (y / 128)) * (centerPitch + pitchBound - 50) + 50; // -50 because min pitch is 50 184 | float yaw = ((float) (x / 128)) * (centerYaw + yawBound); 185 | 186 | location.setPitch(pitch); 187 | location.setYaw(yaw); 188 | 189 | player.teleport(location); 190 | } 191 | 192 | private void freezePlayer(Location atLocation) { 193 | if (player.getFlySpeed() == 0) { 194 | previousFlySpeed = defaultFlySpeed; 195 | } else if (player.getFlySpeed() != defaultFlySpeed) { 196 | previousFlySpeed = player.getFlySpeed(); 197 | } 198 | wasFlying = player.isFlying(); 199 | wasFlightAllowed = player.getAllowFlight(); 200 | 201 | previousVelocityVector = player.getVelocity(); 202 | 203 | player.setVelocity(new Vector(0, 0, 0)); 204 | player.setFlySpeed(0f); 205 | player.setAllowFlight(true); 206 | player.setFlying(true); 207 | 208 | if (atLocation != null) { 209 | player.teleport(atLocation); 210 | } 211 | } 212 | 213 | private void unFreezePlayer() { 214 | player.setFlySpeed(previousFlySpeed); 215 | player.setFlying(wasFlying); 216 | player.setAllowFlight(wasFlightAllowed); 217 | player.setVelocity(previousVelocityVector); 218 | } 219 | 220 | private void setClientInvisible(boolean invisible) { 221 | byte previousData = entityPlayerDataWatcher.get(baseDataWatcherObject); 222 | byte newData; 223 | 224 | if (invisible) { 225 | newData = (byte) (previousData | 0b0010_0000); 226 | } else { 227 | newData = (byte) (previousData & ~0b0010_0000); 228 | } 229 | temporaryDataWatcher.set(baseDataWatcherObject, newData); 230 | 231 | PacketPlayOutEntityMetadata entityMetadataPacket = new PacketPlayOutEntityMetadata(player.getEntityId(), 232 | temporaryDataWatcher, true); 233 | ((CraftPlayer) player).getHandle().playerConnection.sendPacket(entityMetadataPacket); 234 | } 235 | 236 | private void setClientCollidable(boolean collidable) { 237 | if (collisionScoreboard == null) { // First time run 238 | collisionScoreboard = player.getScoreboard(); 239 | if (collisionScoreboard == null) { 240 | collisionScoreboard = Bukkit.getScoreboardManager().getNewScoreboard(); 241 | player.setScoreboard(collisionScoreboard); 242 | } else { // Player already has a scoreboard 243 | for (Team team : collisionScoreboard.getTeams()) { 244 | if (team.getOption(Team.Option.COLLISION_RULE) == Team.OptionStatus.NEVER) { 245 | return; // Collision rule never has already been set by something else so don't mess with it 246 | } 247 | } 248 | } 249 | } 250 | if (collisionRuleTeam == null) { 251 | final String collisionTeamName = "no_collision"; 252 | collisionRuleTeam = collisionScoreboard.getTeam(collisionTeamName); 253 | if (collisionRuleTeam == null) { 254 | collisionRuleTeam = collisionScoreboard.registerNewTeam(collisionTeamName); 255 | } 256 | } 257 | 258 | // Set collision rule no matter what because collision rule has not been set by another scoreboard/plugin 259 | if (collidable) { 260 | collisionRuleTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.ALWAYS); 261 | } else { 262 | collisionRuleTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); 263 | } 264 | } 265 | 266 | public boolean didPlayerDirectionChange() { 267 | return playerDirectionChanged; 268 | } 269 | 270 | public Location getCursorCenterLocation() { 271 | return cursorCenterLocation; 272 | } 273 | 274 | public int getCursorX() { 275 | return cursorX; 276 | } 277 | 278 | public int getCursorY() { 279 | return cursorY; 280 | } 281 | 282 | public boolean isDamageCanceled() { 283 | return damageCanceled; 284 | } 285 | 286 | public void setDamageCanceled(boolean damageCanceled) { 287 | this.damageCanceled = damageCanceled; 288 | } 289 | } 290 | --------------------------------------------------------------------------------