dithering floyd (partial don't work properly)

This commit is contained in:
Vlammar 2020-07-08 20:06:43 +02:00
parent 219869c82b
commit 97af1a8080
8 changed files with 672 additions and 382 deletions

View File

@ -120,6 +120,9 @@ public class NewCommand extends IoMCommand
} }
}); });
} }
catch(final Throwable e){
throw e;
}
//Added to fix bug with rendering displaying after error //Added to fix bug with rendering displaying after error
finally { finally {
ActionBar.removeMessage(player); ActionBar.removeMessage(player);

View File

@ -38,6 +38,7 @@ package fr.moribus.imageonmap.image;
import fr.moribus.imageonmap.ImageOnMap; import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.map.MapManager; import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.ui.SplatterMapManager;
import fr.zcraft.zlib.core.ZLib; import fr.zcraft.zlib.core.ZLib;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
@ -47,9 +48,11 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.ItemFrame; import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -114,7 +117,56 @@ public class MapInitEvent implements Listener
initMap(event.getCursor()); initMap(event.getCursor());
} }
} }
@EventHandler(priority = EventPriority.HIGHEST)
private void onInventoryClick(InventoryClickEvent event) {
//Checks if they have clicked anywhere else on the screen while an inventory is open
if(event.getClickedInventory() == null) return;
//Checks if the clicked slot is empty
if(event.getClickedInventory().getItem(event.getSlot()) == null) return;
//Forbid deposit of map in unwanted block
if(event.getInventory().getType().name().equals("GRINDSTONE") & SplatterMapManager.isSplatterMap(event.getCurrentItem())){
event.setCancelled(true);
return;
}
//To cancel removing of glowing effect
if(event.getClickedInventory().getSize()==3) {
String inventoryClickedTitle=event.getClickedInventory().getType().name();
//Ignore those blocks
if(event.getClickedInventory().getType()== InventoryType.ANVIL||event.getClickedInventory().getType()==InventoryType.FURNACE||inventoryClickedTitle.equals("BLAST_FURNACE")||inventoryClickedTitle.equals("SMOKER")){
return;
}
if(inventoryClickedTitle.equals("CARTOGRAPHY_TABLE")){
if(event.getInventory().contains(Material.PAPER)){
ItemStack[] inventory=event.getClickedInventory().getContents();
for(ItemStack item:inventory) {
if (SplatterMapManager.isSplatterMap(item)) {
event.setCancelled(true);
return;
}
}
}
}
ItemStack[] inventory=event.getClickedInventory().getContents();
for(ItemStack item:inventory){
if (SplatterMapManager.isSplatterMap(item)) {
event.setCancelled(true);
return;
}
}
}
}
static public void initMap(ItemStack item) static public void initMap(ItemStack item)
{ {
if (item != null && item.getType() == Material.FILLED_MAP) if (item != null && item.getType() == Material.FILLED_MAP)

View File

@ -40,88 +40,280 @@ import fr.zcraft.zlib.tools.PluginLogger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.map.MapCanvas; import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapPalette;
import org.bukkit.map.MapRenderer; import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView; import org.bukkit.map.MapView;
import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
public class Renderer extends MapRenderer public class Renderer extends MapRenderer {
{ private BufferedImage image;
static public boolean isHandled(MapView map)
{ protected Renderer() {
if(map == null) return false; this(null);
for(MapRenderer renderer : map.getRenderers()) }
{
if(renderer instanceof Renderer) return true; protected Renderer(BufferedImage image) {
this.image = image;
}
static public boolean isHandled(MapView map) {
if (map == null) return false;
for (MapRenderer renderer : map.getRenderers()) {
if (renderer instanceof Renderer) return true;
} }
return false; return false;
} }
static public void installRenderer(PosterImage image, int[] mapsIds) static public void installRenderer(PosterImage image, int[] mapsIds) {
{ for (int i = 0; i < mapsIds.length; i++) {
for(int i = 0; i < mapsIds.length; i++)
{
installRenderer(image.getImageAt(i), mapsIds[i]); installRenderer(image.getImageAt(i), mapsIds[i]);
} }
} }
static public void installRenderer(BufferedImage image, int mapID) static public void installRenderer(BufferedImage image, int mapID) {
{
MapView map = Bukkit.getMap(mapID); MapView map = Bukkit.getMap(mapID);
if(map == null) if (map == null) {
{
PluginLogger.warning("Could not install renderer for map {0}: the Minecraft map does not exist", mapID); PluginLogger.warning("Could not install renderer for map {0}: the Minecraft map does not exist", mapID);
} } else {
else
{
installRenderer(map).setImage(image); installRenderer(map).setImage(image);
} }
} }
static public Renderer installRenderer(MapView map) static public Renderer installRenderer(MapView map) {
{
Renderer renderer = new Renderer(); Renderer renderer = new Renderer();
removeRenderers(map); removeRenderers(map);
map.addRenderer(renderer); map.addRenderer(renderer);
return renderer; return renderer;
} }
static public void removeRenderers(MapView map) static public void removeRenderers(MapView map) {
{ for (MapRenderer renderer : map.getRenderers()) {
for(MapRenderer renderer : map.getRenderers())
{
map.removeRenderer(renderer); map.removeRenderer(renderer);
} }
} }
private BufferedImage image;
protected Renderer()
{ private static double getDistance(Color c1, Color c2) {
this(null); double rmean = (c1.getRed() + c2.getRed()) / 2.0;
double r = c1.getRed() - c2.getRed();
double g = c1.getGreen() - c2.getGreen();
int b = c1.getBlue() - c2.getBlue();
double weightR = 2 + rmean / 256.0;
double weightG = 4.0;
double weightB = 2 + (255 - rmean) / 256.0;
return weightR * r * r + weightG * g * g + weightB * b * b;
} }
protected Renderer(BufferedImage image) private byte closestColor(int rgb) {
{ int alpha = (rgb >>> 24) & 0xFF;
this.image = image; int r = (rgb >>> 16) & 0xFF;
int g = (rgb >>> 8) & 0xFF;
int b = rgb & 0xFF;
byte col = MapPalette.matchColor(new Color(r, g, b, alpha));
return col;
}
protected class RGB{
private int alpha,r,g,b;
public RGB(int r, int g, int b,int alpha){
this.r=r;
this.g=g;
this.b=b;
this.alpha=alpha;
}
public RGB(int rgb){
int alpha = (rgb >>> 24) & 0xFF;
int r = (rgb >>> 16) & 0xFF;
int g = (rgb >>> 8) & 0xFF;
int b = rgb & 0xFF;
this.r=r;
this.g=g;
this.b=b;
this.alpha=alpha;
}
public RGB(int r, int g, int b){
this.r=r;
this.g=g;
this.b=b;
this.alpha=255;
}
public void applyFactor(double factor){
r*=factor;
g*=factor;
b*=factor;
}
}
private void dithering(MapCanvas canvas) {
for (int x = 0; x < 128; x++) {
for (int y = 0; y < 128; y++) {
int rgb = image.getRGB(x, y);
Color old_pix = new Color(rgb);
Color new_pix = MapPalette.getColor(MapPalette.matchColor(old_pix));//Couleur la plus proche
int alpha = (rgb >>> 24) & 0xFF;
if (alpha < 128) {
canvas.setPixel(x, y, (byte) 0);
continue;
}
canvas.setPixel(x, y, MapPalette.matchColor(new_pix));//On applique la nouvelle couleur
int diff_red=old_pix.getRed()-new_pix.getRed();
int diff_green=old_pix.getGreen()-new_pix.getGreen();
int diff_blue=old_pix.getBlue()-new_pix.getBlue();
//Distribution de l'erreur
int X = 0, Y = 0, r, g, b;
int value = 0;//120+4+4+7*4+24+1+16;//208+10*4+4;
if (x + 1 >= 128 | y + 1 >= 128 | x - 1 < 0) {
//PluginLogger.info("On va trop loin");
canvas.setPixel(x, y, (byte) 20);//MapPalette.matchColor(new_pix));
continue;
}
double factor;
try {
X = x + 1;
Y = y;
factor=7.0/16;
rgb=(MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB());
//rgb = (int) ((MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB()) + (error * 7.0 / 16));
alpha = (rgb >>> 24) & 0xFF;
r = (rgb >>> 16) & 0xFF;
g = (rgb >>> 8) & 0xFF;
b = rgb & 0xFF;
r+=diff_red*factor;
g+=diff_green*factor;
b+=diff_blue*factor;
if (alpha < 128)
canvas.setPixel(x, y, (byte) 0);
else
canvas.setPixel(x, y, MapPalette.matchColor(r, g, b));//Pb possible avec alpha
X = x - 1;
Y = y + 1;
factor=3.0/16;
rgb=(MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB());
//rgb = (int) ((MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB()) + (error * 3.0 / 16));
alpha = (rgb >>> 24) & 0xFF;
r = (rgb >>> 16) & 0xFF;
g = (rgb >>> 8) & 0xFF;
b = rgb & 0xFF;
r+=diff_red*factor;
g+=diff_green*factor;
b+=diff_blue*factor;
if (alpha < 128)
canvas.setPixel(x, y, (byte) 0);
else
canvas.setPixel(x, y, MapPalette.matchColor(r, g, b));//Pb possible avec alpha
X = x;
Y = y + 1;
factor=5.0/16;
rgb=(MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB());
//rgb = (int) ((MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB()) + (error * 5.0 / 16));
alpha = (rgb >>> 24) & 0xFF;
r = (rgb >>> 16) & 0xFF;
g = (rgb >>> 8) & 0xFF;
b = rgb & 0xFF;
r+=diff_red*factor;
g+=diff_green*factor;
b+=diff_blue*factor;
if (alpha < 128)
canvas.setPixel(x, y, (byte) 0);
else
canvas.setPixel(x, y, MapPalette.matchColor(r, g, b));//Pb possible avec alpha
X = x + 1;
Y = y + 1;
factor=1.0/16;
rgb=(MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB());
//rgb = (int) ((MapPalette.getColor(canvas.getPixel(X, Y) < 0 ? (byte) (canvas.getPixel(x, y) + value) : canvas.getPixel(x, y)).getRGB()) + (error * 1.0 / 16));
alpha = (rgb >>> 24) & 0xFF;
r = (rgb >>> 16) & 0xFF;
g = (rgb >>> 8) & 0xFF;
b = rgb & 0xFF;
int rold = r;
int gold = g;
int bold = b;
r+=diff_red*factor;
g+=diff_green*factor;
b+=diff_blue*factor;
if (alpha < 128)
canvas.setPixel(x, y, (byte) 0);
else
canvas.setPixel(x, y, MapPalette.matchColor(r, g, b));//Pb possible avec alpha
// PluginLogger.info("r "+r+" g "+g+" b "+b);
//PluginLogger.info("rold "+rold+" gold "+gold+" bold "+bold);
} catch (final Exception e) {
PluginLogger.info("Exception get color ");
throw e;
}
//canvas.setPixel(x-1,y+1,canvas.getPixel(x-1,y+1)+(error*7.0/16));
//canvas.setPixel(x,y+1,canvas.getPixel(x,y+1)+(error*7.0/16));
//canvas.setPixel(x+1,y+1,canvas.getPixel(x+1,y+1)+(error*7.0/16));
}
}
} }
@Override @Override
public void render(MapView v, final MapCanvas canvas, Player p) public void render(MapView v, MapCanvas canvas, Player p) {
{
//Render only once to avoid overloading the server //Render only once to avoid overloading the server
if (image == null) return; if (image == null) return;
canvas.drawImage(0, 0, image); boolean dither = true;
// canvas.drawImage(0, 0, image);
if (dither) {
dithering(canvas);
return;
}
for (int x = 0; x < 128; x++) {
for (int y = 0; y < 128; y++) {
int rgb = image.getRGB(x, y);
byte col = closestColor(rgb);
canvas.setPixel(x, y, col);
}
}
/* for(int x=0;x<128;x++){
for(int y=0;y<128;y++){
int rgb=image.getRGB(x,y);
int r = (rgb >>> 16) & 0xFF;
int g = (rgb >>> 8) & 0xFF;
int b = rgb & 0xFF;
//PluginLogger.info("canvas pixel "+x+" "+y+" r "+r+" g "+g+" b "+b);
PluginLogger.info(""+canvas.getBasePixel(x,y));
canvas.setPixel(x,y,(byte)208);
}*/
image = null; image = null;
} }
public BufferedImage getImage() public BufferedImage getImage() {
{
return image; return image;
} }
public void setImage(BufferedImage image) public void setImage(BufferedImage image) {
{
this.image = image; this.image = image;
} }
} }

View File

@ -126,11 +126,19 @@ abstract public class MapManager
static public int[] getNewMapsIds(int amount) static public int[] getNewMapsIds(int amount)
{ {
int[] mapsIds = new int[amount]; int[] mapsIds = new int[amount];
for(int i = 0; i < amount; i++) try {
{ for (int i = 0; i < amount; i++) {
mapsIds[i] = Bukkit.createMap(Bukkit.getWorlds().get(0)).getId(); mapsIds[i] = Bukkit.createMap(Bukkit.getWorlds().get(0)).getId();
}
return mapsIds;
} }
return mapsIds; catch (final Throwable e){
PluginLogger.warning("Erreur a la creation de map !!!");
throw e;
}
} }
/** /**
@ -140,11 +148,14 @@ abstract public class MapManager
*/ */
static public int getMapIdFromItemStack(final ItemStack item) static public int getMapIdFromItemStack(final ItemStack item)
{ {
final ItemMeta meta = item.getItemMeta(); final ItemMeta meta = item.getItemMeta();
if (!(meta instanceof MapMeta)) return 0; if (!(meta instanceof MapMeta)) return 0;
return ((MapMeta) meta).hasMapId() ? ((MapMeta) meta).getMapId() : 0; return ((MapMeta) meta).hasMapId() ? ((MapMeta) meta).getMapId() : 0;
} }
static public void addMap(ImageMap map) throws MapManagerException static public void addMap(ImageMap map) throws MapManagerException
{ {

View File

@ -36,12 +36,14 @@
package fr.moribus.imageonmap.ui; package fr.moribus.imageonmap.ui;
import fr.moribus.imageonmap.Permissions;
import fr.moribus.imageonmap.map.ImageMap; import fr.moribus.imageonmap.map.ImageMap;
import fr.moribus.imageonmap.map.MapManager; import fr.moribus.imageonmap.map.MapManager;
import fr.moribus.imageonmap.map.PosterMap; import fr.moribus.imageonmap.map.PosterMap;
import fr.moribus.imageonmap.map.SingleMap; import fr.moribus.imageonmap.map.SingleMap;
import fr.zcraft.zlib.components.i18n.I; import fr.zcraft.zlib.components.i18n.I;
import fr.zcraft.zlib.core.ZLib; import fr.zcraft.zlib.core.ZLib;
import fr.zcraft.zlib.tools.PluginLogger;
import fr.zcraft.zlib.tools.items.ItemStackBuilder; import fr.zcraft.zlib.tools.items.ItemStackBuilder;
import fr.zcraft.zlib.tools.items.ItemUtils; import fr.zcraft.zlib.tools.items.ItemUtils;
import org.bukkit.*; import org.bukkit.*;
@ -271,6 +273,7 @@ public class MapItemManager implements Listener
} }
else{ else{
PluginLogger.info("Coucou je suis une carte simple");
frame.setItem(mapItem); frame.setItem(mapItem);
} }
@ -285,17 +288,20 @@ public class MapItemManager implements Listener
ItemStack item = frame.getItem(); ItemStack item = frame.getItem();
if (frame.getItem().getType() != Material.FILLED_MAP) return; if (frame.getItem().getType() != Material.FILLED_MAP) return;
if (player.isSneaking()) if (Permissions.REMOVE_SPLATTER_MAP.grantedTo(player))
{ {
PosterMap poster = SplatterMapManager.removeSplatterMap(frame,player); if (player.isSneaking())
if (poster != null)
{ {
event.setCancelled(true); PosterMap poster = SplatterMapManager.removeSplatterMap(frame,player);
if (poster != null)
{
event.setCancelled(true);
if (player.getGameMode() != GameMode.CREATIVE || !SplatterMapManager.hasSplatterMap(player, poster)) if (player.getGameMode() != GameMode.CREATIVE || !SplatterMapManager.hasSplatterMap(player, poster))
poster.give(player); poster.give(player);
return; return;
}
} }
} }

View File

@ -107,6 +107,7 @@ public class PosterOnASurface {
return true; return true;
} }
//If in creative expand item frames in order to auto deploy
public void expand() { public void expand() {
} }

View File

@ -43,113 +43,146 @@ import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ItemFrame; import org.bukkit.entity.ItemFrame;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
public class PosterWall public class PosterWall {
{
public FlatLocation loc1; public FlatLocation loc1;
public FlatLocation loc2; public FlatLocation loc2;
public ItemFrame[] frames; public ItemFrame[] frames;
public boolean isValid()
{
ItemFrame curFrame;
FlatLocation bottomLeft = FlatLocation.minMerged(loc1, loc2);
FlatLocation loc = bottomLeft.clone();
int distX = FlatLocation.flatBlockDistanceX(loc1, loc2);
int distY = FlatLocation.flatBlockDistanceY(loc1, loc2);
frames = new ItemFrame[distX * distY];
for(int x = 0; x < distX; x++) private static boolean supportIsValid(FlatLocation location, BlockFace bf, int width, int height) {
{ //Code to add
for(int y = 0; y < distY; y++) FlatLocation loc=location.clone();
{ loc.add(0,-1);
curFrame = getEmptyFrameAt(loc, loc.getFacing()); switch (bf){
if(curFrame == null) return false; case EAST:
frames[y * distX + x] = curFrame; case WEST:
loc.add(0, 1); loc.addH(1,0,bf);
} break;
loc.add(1, 0); case NORTH:
loc.setY(bottomLeft.getY()); case SOUTH:
loc.addH(0,1,bf);
break;
} }
for (int x = 0; x < width; x++) {
return true; for (int y = 0; y < height; y++) {
} // if(loc.getBlock().)
public void expand()
{
}
}
return false;
} }
static public ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location, int mapId) //Used to expand itemframes when possible
{ public static void expand(FlatLocation location,BlockFace bf ,int width, int height) throws Exception{
if (!supportIsValid(location,bf, width, height))
return;
FlatLocation loc=location.clone();
loc.add(0,-1);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
loc.add(0,1);
loc.getWorld().spawnEntity(loc, EntityType.ITEM_FRAME);
}
loc.add(1,-height);
}
}
/*static public ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location, int mapId, Player p) {
if (p.getGameMode() == GameMode.CREATIVE)
expand(location, map.getColumnCount(), map.getRowCount());
return getMatchingMapFrames(map, location, mapId);
}*/
static public ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location, int mapId) {
int mapIndex = map.getIndex(mapId); int mapIndex = map.getIndex(mapId);
int x = map.getColumnAt(mapIndex), y = map.getRowAt(mapIndex); int x = map.getColumnAt(mapIndex), y = map.getRowAt(mapIndex);
return getMatchingMapFrames(map, location.clone().add(-x, y)); return getMatchingMapFrames(map, location.clone().add(-x, y));
} }
static public ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location) static public ItemFrame[] getMatchingMapFrames(PosterMap map, FlatLocation location) {
{
ItemFrame[] frames = new ItemFrame[map.getMapCount()]; ItemFrame[] frames = new ItemFrame[map.getMapCount()];
FlatLocation loc = location.clone(); FlatLocation loc = location.clone();
for(int y = 0; y < map.getRowCount(); ++y) for (int y = 0; y < map.getRowCount(); ++y) {
{ for (int x = 0; x < map.getColumnCount(); ++x) {
for(int x = 0; x < map.getColumnCount(); ++x)
{
int mapIndex = map.getIndexAt(x, y); int mapIndex = map.getIndexAt(x, y);
ItemFrame frame = getMapFrameAt(loc, map); ItemFrame frame = getMapFrameAt(loc, map);
if(frame != null) frames[mapIndex] = frame; if (frame != null) frames[mapIndex] = frame;
loc.add(1, 0); loc.add(1, 0);
} }
loc.setX(location.getX()); loc.setX(location.getX());
loc.setZ(location.getZ()); loc.setZ(location.getZ());
loc.add(0, -1); loc.add(0, -1);
} }
return frames; return frames;
} }
static public ItemFrame getMapFrameAt(FlatLocation location, PosterMap map) static public ItemFrame getMapFrameAt(FlatLocation location, PosterMap map) {
{
Entity entities[] = location.getChunk().getEntities(); Entity entities[] = location.getChunk().getEntities();
for(Entity entity : entities) for (Entity entity : entities) {
{ if (!(entity instanceof ItemFrame)) continue;
if(!(entity instanceof ItemFrame)) continue; if (!WorldUtils.blockEquals(location, entity.getLocation())) continue;
if(!WorldUtils.blockEquals(location, entity.getLocation())) continue;
ItemFrame frame = (ItemFrame) entity; ItemFrame frame = (ItemFrame) entity;
if(frame.getFacing() != location.getFacing()) continue; if (frame.getFacing() != location.getFacing()) continue;
ItemStack item = frame.getItem(); ItemStack item = frame.getItem();
if(item.getType() != Material.FILLED_MAP) continue; if (item.getType() != Material.FILLED_MAP) continue;
if(!map.managesMap(item)) continue; if (!map.managesMap(item)) continue;
return frame; return frame;
} }
return null; return null;
} }
static public ItemFrame getEmptyFrameAt(Location location, BlockFace facing) static public ItemFrame getEmptyFrameAt(Location location, BlockFace facing) {
{
Entity entities[] = location.getChunk().getEntities(); Entity entities[] = location.getChunk().getEntities();
for(Entity entity : entities) for (Entity entity : entities) {
{ if (!(entity instanceof ItemFrame)) continue;
if(!(entity instanceof ItemFrame)) continue; if (!WorldUtils.blockEquals(location, entity.getLocation())) continue;
if(!WorldUtils.blockEquals(location, entity.getLocation())) continue;
ItemFrame frame = (ItemFrame) entity; ItemFrame frame = (ItemFrame) entity;
if(frame.getFacing() != facing) continue; if (frame.getFacing() != facing) continue;
ItemStack item = frame.getItem(); ItemStack item = frame.getItem();
if(item.getType() != Material.AIR) continue; if (item.getType() != Material.AIR) continue;
return frame; return frame;
} }
return null; return null;
} }
public boolean isValid() {
ItemFrame curFrame;
FlatLocation bottomLeft = FlatLocation.minMerged(loc1, loc2);
FlatLocation loc = bottomLeft.clone();
int distX = FlatLocation.flatBlockDistanceX(loc1, loc2);
int distY = FlatLocation.flatBlockDistanceY(loc1, loc2);
frames = new ItemFrame[distX * distY];
for (int x = 0; x < distX; x++) {
for (int y = 0; y < distY; y++) {
curFrame = getEmptyFrameAt(loc, loc.getFacing());
if (curFrame == null) return false;
frames[y * distX + x] = curFrame;
loc.add(0, 1);
}
loc.add(1, 0);
loc.setY(bottomLeft.getY());
}
return true;
}
} }

View File

@ -52,10 +52,7 @@ import fr.zcraft.zlib.tools.reflection.NMSException;
import fr.zcraft.zlib.tools.text.MessageSender; import fr.zcraft.zlib.tools.text.MessageSender;
import fr.zcraft.zlib.tools.world.FlatLocation; import fr.zcraft.zlib.tools.world.FlatLocation;
import fr.zcraft.zlib.tools.world.WorldUtils; import fr.zcraft.zlib.tools.world.WorldUtils;
import org.bukkit.ChatColor; import org.bukkit.*;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.Rotation;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.ItemFrame; import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -64,293 +61,288 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta; import org.bukkit.inventory.meta.MapMeta;
abstract public class SplatterMapManager { abstract public class SplatterMapManager {
private SplatterMapManager() { private SplatterMapManager() {
} }
static public ItemStack makeSplatterMap(PosterMap map) { static public ItemStack makeSplatterMap(PosterMap map) {
final ItemStack splatter = new ItemStackBuilder(Material.FILLED_MAP).title(ChatColor.GOLD, map.getName())
.title(ChatColor.DARK_GRAY, " - ").title(ChatColor.GRAY, I.t("Splatter Map"))
.title(ChatColor.DARK_GRAY, " - ")
.title(ChatColor.GRAY, I.t("{0} × {1}", map.getColumnCount(), map.getRowCount()))
.loreLine(ChatColor.GRAY, map.getId()).loreLine()
/// Title in a splatter map tooltip
.loreLine(ChatColor.BLUE, I.t("Item frames needed"))
/// Size of a map stored in a splatter map
.loreLine(ChatColor.GRAY,
I.t("{0} × {1} (total {2} frames)", map.getColumnCount(), map.getRowCount(),
map.getColumnCount() * map.getRowCount()))
.loreLine()
/// Title in a splatter map tooltip
.loreLine(ChatColor.BLUE, I.t("How to use this?"))
.longLore(
ChatColor.GRAY
+ I.t("Place empty item frames on a wall, enough to host the whole map. Then, right-click on the bottom-left frame with this map."),
40)
.loreLine()
.longLore(ChatColor.GRAY
+ I.t("Shift-click one of the placed maps to remove the whole poster in one shot."), 40)
.hideAttributes().craftItem();
final ItemStack splatter = new ItemStackBuilder(Material.FILLED_MAP).title(ChatColor.GOLD, map.getName()) final MapMeta meta = (MapMeta) splatter.getItemMeta();
.title(ChatColor.DARK_GRAY, " - ").title(ChatColor.GRAY, I.t("Splatter Map")) meta.setMapId(map.getMapIdAt(0));
.title(ChatColor.DARK_GRAY, " - ") meta.setColor(Color.GREEN);
.title(ChatColor.GRAY, I.t("{0} × {1}", map.getColumnCount(), map.getRowCount())) splatter.setItemMeta(meta);
.loreLine(ChatColor.GRAY, map.getId()).loreLine()
/// Title in a splatter map tooltip
.loreLine(ChatColor.BLUE, I.t("Item frames needed"))
/// Size of a map stored in a splatter map
.loreLine(ChatColor.GRAY,
I.t("{0} × {1} (total {2} frames)", map.getColumnCount(), map.getRowCount(),
map.getColumnCount() * map.getRowCount()))
.loreLine()
/// Title in a splatter map tooltip
.loreLine(ChatColor.BLUE, I.t("How to use this?"))
.longLore(
ChatColor.GRAY
+ I.t("Place empty item frames on a wall, enough to host the whole map. Then, right-click on the bottom-left frame with this map."),
40)
.loreLine()
.longLore(ChatColor.GRAY
+ I.t("Shift-click one of the placed maps to remove the whole poster in one shot."), 40)
.hideAttributes().craftItem();
final MapMeta meta = (MapMeta) splatter.getItemMeta(); return addSplatterAttribute(splatter);
meta.setMapId(map.getMapIdAt(0)); }
meta.setColor(Color.GREEN);
splatter.setItemMeta(meta);
return addSplatterAttribute(splatter); /**
} * To identify image on maps for the auto-splattering to work, we mark the
* items using an enchantment maps are not supposed to have (Mending).
* <p>
* Then we check if the map is enchanted at all to know if it's a splatter
* map. This ensure compatibility with old splatter maps from 3.x, where
* zLib's glow effect was used.
* <p>
* An AttributeModifier (using zLib's attributes system) is not used,
* because Minecraft (or Spigot) removes them from maps in 1.14+, so that
* wasn't stable enough (and the glowing effect of enchantments is
* prettier).
*
* @param itemStack The item stack to mark as a splatter map.
* @return The modified item stack. The instance may be different if the
* passed item stack is not a craft item stack; that's why the
* instance is returned.
*/
static public ItemStack addSplatterAttribute(final ItemStack itemStack) {
try {
final NBTCompound nbt = NBT.fromItemStack(itemStack);
final NBTList enchantments = new NBTList();
final NBTCompound protection = new NBTCompound();
/** protection.put("id", "minecraft:mending");
* To identify image on maps for the auto-splattering to work, we mark the protection.put("lvl", 1);
* items using an enchantment maps are not supposed to have (Mending). enchantments.add(protection);
*
* Then we check if the map is enchanted at all to know if it's a splatter
* map. This ensure compatibility with old splatter maps from 3.x, where
* zLib's glow effect was used.
*
* An AttributeModifier (using zLib's attributes system) is not used,
* because Minecraft (or Spigot) removes them from maps in 1.14+, so that
* wasn't stable enough (and the glowing effect of enchantments is
* prettier).
*
* @param itemStack
* The item stack to mark as a splatter map.
* @return The modified item stack. The instance may be different if the
* passed item stack is not a craft item stack; that's why the
* instance is returned.
*/
static public ItemStack addSplatterAttribute(final ItemStack itemStack) {
try {
final NBTCompound nbt = NBT.fromItemStack(itemStack);
final NBTList enchantments = new NBTList();
final NBTCompound protection = new NBTCompound();
protection.put("id", "minecraft:mending"); nbt.put("Enchantments", enchantments);
protection.put("lvl", 1);
enchantments.add(protection);
nbt.put("Enchantments", enchantments); return NBT.addToItemStack(itemStack, nbt, false);
} catch (NBTException | NMSException e) {
PluginLogger.error("Unable to set Splatter Map attribute on item", e);
return itemStack;
}
}
return NBT.addToItemStack(itemStack, nbt, false); /**
} catch (NBTException | NMSException e) { * Checks if an item have the splatter attribute set (i.e. if the item is
PluginLogger.error("Unable to set Splatter Map attribute on item", e); * enchanted in any way).
return itemStack; *
} * @param itemStack The item to check.
} * @return True if the attribute was detected.
*/
static public boolean hasSplatterAttributes(ItemStack itemStack) {
try {
final NBTCompound nbt = NBT.fromItemStack(itemStack);
if (!nbt.containsKey("Enchantments"))
return false;
final Object enchantments = nbt.get("Enchantments");
if (!(enchantments instanceof NBTList))
return false;
return !((NBTList) enchantments).isEmpty();
} catch (NMSException e) {
PluginLogger.error("Unable to get Splatter Map attribute on item", e);
return false;
}
}
/** /**
* Checks if an item have the splatter attribute set (i.e. if the item is * Return true if it is a platter map
* enchanted in any way). *
* * @param itemStack The item to check.
* @param itemStack * @return True if is a splatter map
* The item to check. */
* @return True if the attribute was detected. static public boolean isSplatterMap(ItemStack itemStack) {
*/ if (itemStack == null) return false;
static public boolean hasSplatterAttributes(ItemStack itemStack) { return hasSplatterAttributes(itemStack) && MapManager.managesMap(itemStack);
try { }
final NBTCompound nbt = NBT.fromItemStack(itemStack);
if (!nbt.containsKey("Enchantments"))
return false;
final Object enchantments = nbt.get("Enchantments");
if (!(enchantments instanceof NBTList))
return false;
return !((NBTList) enchantments).isEmpty();
} catch (NMSException e) {
PluginLogger.error("Unable to get Splatter Map attribute on item", e);
return false;
}
}
/**
* Return true if it is a platter map
*
* @param itemStack
* The item to check.
* @return True if is a splatter map
*/
static public boolean isSplatterMap(ItemStack itemStack) {
if(itemStack==null) return false;
return hasSplatterAttributes(itemStack) && MapManager.managesMap(itemStack);
}
//TODO doc a faire //TODO doc a faire
static public boolean hasSplatterMap(Player player, PosterMap map) { static public boolean hasSplatterMap(Player player, PosterMap map) {
Inventory playerInventory = player.getInventory(); Inventory playerInventory = player.getInventory();
for (int i = 0; i < playerInventory.getSize(); ++i) { for (int i = 0; i < playerInventory.getSize(); ++i) {
ItemStack item = playerInventory.getItem(i); ItemStack item = playerInventory.getItem(i);
if (isSplatterMap(item) && map.managesMap(item)) if (isSplatterMap(item) && map.managesMap(item))
return true; return true;
} }
return false; return false;
} }
/** /**
* Place a splatter map * Place a splatter map
* *
* @param startFrame * @param startFrame Frame clicked by the player
* Frame clicked by the player * @param player Player placing map
* @param player * @return true if the map was correctly placed
* Player placing map */
* @return true if the map was correctly placed static public boolean placeSplatterMap(ItemFrame startFrame, Player player, PlayerInteractEntityEvent event) {
*/ ImageMap map = MapManager.getMap(player.getInventory().getItemInMainHand());
static public boolean placeSplatterMap(ItemFrame startFrame, Player player, PlayerInteractEntityEvent event) {
ImageMap map = MapManager.getMap(player.getInventory().getItemInMainHand());
if (!(map instanceof PosterMap)) if (!(map instanceof PosterMap))
return false; return false;
PosterMap poster = (PosterMap) map; PosterMap poster = (PosterMap) map;
PosterWall wall = new PosterWall(); PosterWall wall = new PosterWall();
if (startFrame.getFacing().equals(BlockFace.DOWN) || startFrame.getFacing().equals(BlockFace.UP)) { if (startFrame.getFacing().equals(BlockFace.DOWN) || startFrame.getFacing().equals(BlockFace.UP)) {
// If it is on floor or ceiling // If it is on floor or ceiling
PosterOnASurface surface = new PosterOnASurface(); PosterOnASurface surface = new PosterOnASurface();
FlatLocation startLocation = new FlatLocation(startFrame.getLocation(), startFrame.getFacing()); FlatLocation startLocation = new FlatLocation(startFrame.getLocation(), startFrame.getFacing());
FlatLocation endLocation = startLocation.clone().addH(poster.getColumnCount(), poster.getRowCount(), FlatLocation endLocation = startLocation.clone().addH(poster.getColumnCount(), poster.getRowCount(),
WorldUtils.get4thOrientation(player.getLocation())); WorldUtils.get4thOrientation(player.getLocation()));
surface.loc1 = startLocation; surface.loc1 = startLocation;
surface.loc2 = endLocation; surface.loc2 = endLocation;
if (!surface.isValid(player)) { if (!surface.isValid(player)) {
MessageSender.sendActionBarMessage(player, MessageSender.sendActionBarMessage(player,
I.t("{ce}There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(), I.t("{ce}There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(),
poster.getRowCount())); poster.getRowCount()));
return false; return false;
} }
int i = 0; int i = 0;
for (ItemFrame frame : surface.frames) { for (ItemFrame frame : surface.frames) {
BlockFace bf = WorldUtils.get4thOrientation(player.getLocation()); BlockFace bf = WorldUtils.get4thOrientation(player.getLocation());
int id = poster.getMapIdAtReverseZ(i, bf, startFrame.getFacing()); int id = poster.getMapIdAtReverseZ(i, bf, startFrame.getFacing());
Rotation rot = Rotation.NONE; Rotation rot = Rotation.NONE;
switch(frame.getFacing()){ switch (frame.getFacing()) {
case UP: case UP:
break; break;
case DOWN: case DOWN:
rot = Rotation.FLIPPED; rot = Rotation.FLIPPED;
break; break;
} }
//Rotation management relative to player rotation the default position is North, when on ceiling we flipped the rotation //Rotation management relative to player rotation the default position is North, when on ceiling we flipped the rotation
frame.setItem(new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem()); frame.setItem(new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem());
if(i==0){//First map need to be rotate one time CounterClockwise if (i == 0) {//First map need to be rotate one time CounterClockwise
rot=rot.rotateCounterClockwise(); rot = rot.rotateCounterClockwise();
} }
switch (bf) { switch (bf) {
case NORTH: case NORTH:
if (frame.getFacing() == BlockFace.DOWN) { if (frame.getFacing() == BlockFace.DOWN) {
rot = rot.rotateClockwise(); rot = rot.rotateClockwise();
rot = rot.rotateClockwise(); rot = rot.rotateClockwise();
} }
frame.setRotation(rot); frame.setRotation(rot);
break; break;
case EAST: case EAST:
rot = rot.rotateClockwise(); rot = rot.rotateClockwise();
frame.setRotation(rot); frame.setRotation(rot);
break; break;
case SOUTH: case SOUTH:
if (frame.getFacing() == BlockFace.UP) { if (frame.getFacing() == BlockFace.UP) {
rot = rot.rotateClockwise(); rot = rot.rotateClockwise();
rot = rot.rotateClockwise(); rot = rot.rotateClockwise();
} }
frame.setRotation(rot); frame.setRotation(rot);
break; break;
case WEST: case WEST:
rot = rot.rotateCounterClockwise(); rot = rot.rotateCounterClockwise();
frame.setRotation(rot); frame.setRotation(rot);
break; break;
} }
WorldMap
MapInitEvent.initMap(id);
i++;
}
} else {
// If it is on a wall NSEW
FlatLocation startLocation = new FlatLocation(startFrame.getLocation(), startFrame.getFacing());
FlatLocation endLocation = startLocation.clone().add(poster.getColumnCount(), poster.getRowCount());
if (player.getGameMode() == GameMode.CREATIVE)
PosterWall.expand(startLocation,startFrame.getAttachedFace(), poster.getColumnCount(), poster.getRowCount());
wall.loc1 = startLocation;
wall.loc2 = endLocation;
if (!wall.isValid()) {
MessageSender.sendActionBarMessage(player,
I.t("{ce}There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(),
poster.getRowCount()));
return false;
}
MapInitEvent.initMap(id); int i = 0;
i++; for (ItemFrame frame : wall.frames) {
}
} else {
// If it is on a wall NSEW
FlatLocation startLocation = new FlatLocation(startFrame.getLocation(), startFrame.getFacing());
FlatLocation endLocation = startLocation.clone().add(poster.getColumnCount(), poster.getRowCount());
wall.loc1 = startLocation; int id = poster.getMapIdAtReverseY(i);
wall.loc2 = endLocation; frame.setItem(new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem());
if (!wall.isValid()) { //Force reset of rotation
MessageSender.sendActionBarMessage(player, if (i == 0) {//First map need to be rotate one time Clockwise
I.t("{ce}There is not enough space to place this map ({0} × {1}).", poster.getColumnCount(), frame.setRotation(Rotation.NONE.rotateCounterClockwise());
poster.getRowCount())); } else {
return false; frame.setRotation(Rotation.NONE);
} }
MapInitEvent.initMap(id);
++i;
}
}
return true;
}
int i = 0; /**
for (ItemFrame frame : wall.frames) { * Remove splattermap
*
* @param startFrame Frame clicked by the player
* @param player The player removing the map
* @return
*/
static public PosterMap removeSplatterMap(ItemFrame startFrame, Player player) {
final ImageMap map = MapManager.getMap(startFrame.getItem());
if (!(map instanceof PosterMap))
return null;
PosterMap poster = (PosterMap) map;
if (!poster.hasColumnData())
return null;
FlatLocation loc = new FlatLocation(startFrame.getLocation(), startFrame.getFacing());
ItemFrame[] matchingFrames = null;
int id = poster.getMapIdAtReverseY(i); switch (startFrame.getFacing()) {
frame.setItem(new ItemStackBuilder(Material.FILLED_MAP).nbt(ImmutableMap.of("map", id)).craftItem()); case UP:
case DOWN:
//Force reset of rotation
if(i==0){//First map need to be rotate one time Clockwise
frame.setRotation(Rotation.NONE.rotateCounterClockwise());
}
else{frame.setRotation(Rotation.NONE);}
MapInitEvent.initMap(id);
++i;
}
}
return true;
}
/**
* Remove splattermap
*
* @param startFrame
* Frame clicked by the player
* @param player
* The player removing the map
* @return
*/
static public PosterMap removeSplatterMap(ItemFrame startFrame, Player player) {
final ImageMap map = MapManager.getMap(startFrame.getItem());
if (!(map instanceof PosterMap))
return null;
PosterMap poster = (PosterMap) map;
if (!poster.hasColumnData())
return null;
FlatLocation loc = new FlatLocation(startFrame.getLocation(), startFrame.getFacing());
ItemFrame[] matchingFrames=null;
switch(startFrame.getFacing()){
case UP:
case DOWN:
matchingFrames = PosterOnASurface.getMatchingMapFrames(poster, loc, matchingFrames = PosterOnASurface.getMatchingMapFrames(poster, loc,
MapManager.getMapIdFromItemStack(startFrame.getItem()),WorldUtils.get4thOrientation(player.getLocation()));//startFrame.getFacing()); MapManager.getMapIdFromItemStack(startFrame.getItem()), WorldUtils.get4thOrientation(player.getLocation()));//startFrame.getFacing());
break; break;
case NORTH: case NORTH:
case SOUTH: case SOUTH:
case EAST: case EAST:
case WEST: case WEST:
matchingFrames = PosterWall.getMatchingMapFrames(poster, loc, matchingFrames = PosterWall.getMatchingMapFrames(poster, loc,
MapManager.getMapIdFromItemStack(startFrame.getItem())); MapManager.getMapIdFromItemStack(startFrame.getItem()));
} }
if (matchingFrames == null) if (matchingFrames == null)
return null; return null;
for (ItemFrame frame : matchingFrames) { for (ItemFrame frame : matchingFrames) {
if (frame != null) if (frame != null)
frame.setItem(null); frame.setItem(null);
} }
return poster; return poster;
} }
} }