Files
leagues-tools/group-ironmen-master/cache/MapImageDumper.java

1334 lines
63 KiB
Java

/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.cache;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import net.runelite.cache.definitions.AreaDefinition;
import net.runelite.cache.definitions.FontDefinition;
import net.runelite.cache.definitions.ObjectDefinition;
import net.runelite.cache.definitions.OverlayDefinition;
import net.runelite.cache.definitions.SpriteDefinition;
import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.definitions.WorldMapElementDefinition;
import net.runelite.cache.definitions.loaders.OverlayLoader;
import net.runelite.cache.definitions.loaders.SpriteLoader;
import net.runelite.cache.definitions.loaders.UnderlayLoader;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.ArchiveFiles;
import net.runelite.cache.fs.FSFile;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Storage;
import net.runelite.cache.fs.Store;
import net.runelite.cache.item.RSTextureProvider;
import net.runelite.cache.models.JagexColor;
import net.runelite.cache.region.Location;
import net.runelite.cache.region.Position;
import net.runelite.cache.region.Region;
import net.runelite.cache.region.RegionLoader;
import net.runelite.cache.util.BigBufferedImage;
import net.runelite.cache.util.KeyProvider;
import net.runelite.cache.util.XteaKeyManager;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import com.google.gson.Gson;
@Slf4j
@Accessors(chain = true)
public class MapImageDumper
{
private static String outputDirectory;
private static final int MAP_SCALE = 4; // this squared is the number of pixels per map square
private static final int BLEND = 5; // number of surrounding tiles for ground blending
private static int[] colorPalette = JagexColor.createPalette(JagexColor.BRIGHTNESS_MIN);
private static int[][] TILE_SHAPE_2D = new int[][]{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1}, {1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1}, {1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}};
private static int[][] TILE_ROTATION_2D = new int[][]{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {12, 8, 4, 0, 13, 9, 5, 1, 14, 10, 6, 2, 15, 11, 7, 3}, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {3, 7, 11, 15, 2, 6, 10, 14, 1, 5, 9, 13, 0, 4, 8, 12}};
private final int wallColor = (238 + (int) (random() * 20.0D) - 10 << 16) + (238 + (int) (random() * 20.0D) - 10 << 8) + (238 + (int) (random() * 20.0D) - 10);
private final int doorColor = 238 + (int) (random() * 20.0D) - 10 << 16;
private final Store store;
private final Map<Integer, UnderlayDefinition> underlays = new HashMap<>();
private final Map<Integer, OverlayDefinition> overlays = new HashMap<>();
private SpriteDefinition[] mapDecorations;
private final RegionLoader regionLoader;
private final AreaManager areas;
private final SpriteManager sprites;
private final FontManager fonts;
private final WorldMapManager worldMapManager;
private RSTextureProvider rsTextureProvider;
private final ObjectManager objectManager;
@Getter
@Setter
private boolean labelRegions;
@Getter
@Setter
private boolean outlineRegions;
@Getter
@Setter
private boolean renderMap = true;
@Getter
@Setter
private boolean renderObjects = true;
@Getter
@Setter
private boolean renderIcons = true;
@Getter
@Setter
private boolean renderLabels = true;
@Getter
@Setter
private boolean transparency = true;
@Getter
@Setter
private boolean lowMemory = false;
public MapImageDumper(Store store, KeyProvider keyProvider)
{
this(store, new RegionLoader(store, keyProvider));
}
public MapImageDumper(Store store, RegionLoader regionLoader)
{
this.store = store;
this.regionLoader = regionLoader;
this.areas = new AreaManager(store);
this.sprites = new SpriteManager(store);
this.fonts = new FontManager(store);
this.worldMapManager = new WorldMapManager(store);
this.objectManager = new ObjectManager(store);
}
public static void main(String[] args) throws IOException
{
Options options = new Options();
options.addOption(Option.builder().longOpt("cachedir").hasArg().required().build());
options.addOption(Option.builder().longOpt("xteapath").hasArg().required().build());
options.addOption(Option.builder().longOpt("outputdir").hasArg().required().build());
CommandLineParser parser = new DefaultParser();
CommandLine cmd;
try
{
cmd = parser.parse(options, args);
}
catch (ParseException ex)
{
System.err.println("Error parsing command line options: " + ex.getMessage());
System.exit(-1);
return;
}
final String cacheDirectory = cmd.getOptionValue("cachedir");
final String xteaJSONPath = cmd.getOptionValue("xteapath");
outputDirectory = cmd.getOptionValue("outputdir");
XteaKeyManager xteaKeyManager = new XteaKeyManager();
try (FileInputStream fin = new FileInputStream(xteaJSONPath))
{
xteaKeyManager.loadKeys(fin);
}
File base = new File(cacheDirectory);
File outDir = new File(outputDirectory);
outDir.mkdirs();
try (Store store = new Store(base))
{
store.load();
MapImageDumper dumper = new MapImageDumper(store, xteaKeyManager);
dumper.load();
for (int i = 0; i < Region.Z; ++i)
{
BufferedImage image = dumper.drawMap(i);
File imageFile = new File(outDir, "img-" + i + ".png");
ImageIO.write(image, "png", imageFile);
log.info("Wrote image {}", imageFile);
}
}
}
protected double random()
{
// the client would use a random value here, but we prefer determinism
return 0.5;
}
public MapImageDumper setBrightness(double brightness)
{
colorPalette = JagexColor.createPalette(brightness);
return this;
}
public MapImageDumper load() throws IOException
{
loadUnderlays(store);
loadOverlays(store);
objectManager.load();
TextureManager textureManager = new TextureManager(store);
textureManager.load();
rsTextureProvider = new RSTextureProvider(textureManager, sprites);
loadRegions();
areas.load();
sprites.load();
loadSprites();
fonts.load();
worldMapManager.load();
return this;
}
public BufferedImage drawMap(int z)
{
int minX = regionLoader.getLowestX().getBaseX();
int minY = regionLoader.getLowestY().getBaseY();
int maxX = regionLoader.getHighestX().getBaseX() + Region.X;
int maxY = regionLoader.getHighestY().getBaseY() + Region.Y;
int dimX = maxX - minX;
int dimY = maxY - minY;
int pixelsX = dimX * MAP_SCALE;
int pixelsY = dimY * MAP_SCALE;
log.info("Map image dimensions: {}px x {}px, {}px per map square ({} MB). Max memory: {}mb", pixelsX, pixelsY,
MAP_SCALE, (pixelsX * pixelsY * 3 / 1024 / 1024),
Runtime.getRuntime().maxMemory() / 1024L / 1024L);
BufferedImage image;
if (lowMemory)
{
image = BigBufferedImage.create(pixelsX, pixelsY, transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
}
else
{
image = new BufferedImage(pixelsX, pixelsY, transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
}
drawMap(image, z);
drawObjects(image, z);
dumpMapIcons(z);
// drawMapIcons(image, z);
// drawMapLabels(image, z);
return image;
}
private void drawNeighborObjects(BufferedImage image, int rx, int ry, int dx, int dy, int z)
{
Region neighbor = regionLoader.findRegionForRegionCoordinates(rx + dx, ry + dy);
if (neighbor == null)
{
return;
}
drawObjects(image, Region.X * dx, Region.Y * -dy, neighbor, z);
}
public BufferedImage drawRegion(Region region, int z)
{
int pixelsX = Region.X * MAP_SCALE;
int pixelsY = Region.Y * MAP_SCALE;
BufferedImage image = new BufferedImage(pixelsX, pixelsY, transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
drawMap(image, 0, 0, z, region);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), -1, -1, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), -1, 0, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), -1, 1, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), 0, -1, z);
drawObjects(image, 0, 0, region, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), 0, 1, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), 1, -1, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), 1, 0, z);
drawNeighborObjects(image, region.getRegionX(), region.getRegionY(), 1, 1, z);
drawMapIcons(image, 0, 0, region, z);
return image;
}
private void drawMap(BufferedImage image, int drawBaseX, int drawBaseY, int z, Region region)
{
if (!renderMap)
{
return;
}
int[][][] map = new int[4][][];
for (int x = 0; x < Region.X; ++x)
{
for (int y = 0; y < Region.Y; ++y)
{
boolean isBridge = (region.getTileSetting(1, x, Region.Y - y - 1) & 2) != 0;
int tileZ = z + (isBridge ? 1 : 0);
if (tileZ >= Region.Z)
{
continue;
}
int tileSetting = region.getTileSetting(z, x, Region.Y - y - 1);
if ((tileSetting & 24) == 0)
{
if (z == 0 && isBridge)
{
drawTile(image, map, region, drawBaseX, drawBaseY, 0, x, y);
}
drawTile(image, map, region, drawBaseX, drawBaseY, tileZ, x, y);
}
if (tileZ < 3)
{
int upTileSetting = region.getTileSetting(z + 1, x, Region.Y - y - 1);
if ((upTileSetting & 8) != 0)
{
drawTile(image, map, region, drawBaseX, drawBaseY, tileZ + 1, x, y);
}
}
}
}
}
private void drawMap(BufferedImage image, int z)
{
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
drawMap(image, drawBaseX, drawBaseY, z, region);
}
}
private void drawTile(BufferedImage to, int[][][] planes, Region region, int drawBaseX, int drawBaseY, int z, int x, int y)
{
int[][] pixels = planes[z];
if (pixels == null)
{
pixels = planes[z] = new int[Region.X * MAP_SCALE][Region.Y * MAP_SCALE];
drawMap(pixels, region, z);
}
for (int i = 0; i < MAP_SCALE; ++i)
{
for (int j = 0; j < MAP_SCALE; ++j)
{
int argb = pixels[x * MAP_SCALE + i][y * MAP_SCALE + j];
if (argb != 0)
{
to.setRGB(drawBaseX * MAP_SCALE + x * MAP_SCALE + i,
drawBaseY * MAP_SCALE + y * MAP_SCALE + j,
argb);
}
}
}
}
private void drawMap(int[][] pixels, Region region, int z)
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
int len = Region.X + BLEND * 2;
int[] hues = new int[len];
int[] sats = new int[len];
int[] light = new int[len];
int[] mul = new int[len];
int[] num = new int[len];
boolean hasLeftRegion = regionLoader.findRegionForWorldCoordinates(baseX - 1, baseY) != null;
boolean hasRightRegion = regionLoader.findRegionForWorldCoordinates(baseX + Region.X, baseY) != null;
boolean hasUpRegion = regionLoader.findRegionForWorldCoordinates(baseX, baseY + Region.Y) != null;
boolean hasDownRegion = regionLoader.findRegionForWorldCoordinates(baseX, baseY - 1) != null;
for (int xi = (hasLeftRegion ? -BLEND * 2 : -BLEND); xi < Region.X + (hasRightRegion ? BLEND * 2 : BLEND); ++xi)
{
for (int yi = (hasDownRegion ? -BLEND : 0); yi < Region.Y + (hasUpRegion ? BLEND : 0); ++yi)
{
int xr = xi + BLEND;
if (xr >= (hasLeftRegion ? -BLEND : 0) && xr < Region.X + (hasRightRegion ? BLEND : 0))
{
Region r = regionLoader.findRegionForWorldCoordinates(baseX + xr, baseY + yi);
if (r != null)
{
int underlayId = r.getUnderlayId(z, convert(xr), convert(yi));
if (underlayId > 0)
{
UnderlayDefinition underlay = findUnderlay(underlayId - 1);
hues[yi + BLEND] += underlay.getHue();
sats[yi + BLEND] += underlay.getSaturation();
light[yi + BLEND] += underlay.getLightness();
mul[yi + BLEND] += underlay.getHueMultiplier();
num[yi + BLEND]++;
}
}
}
int xl = xi - BLEND;
if (xl >= (hasLeftRegion ? -BLEND : 0) && xl < Region.X + (hasRightRegion ? BLEND : 0))
{
Region r = regionLoader.findRegionForWorldCoordinates(baseX + xl, baseY + yi);
if (r != null)
{
int underlayId = r.getUnderlayId(z, convert(xl), convert(yi));
if (underlayId > 0)
{
UnderlayDefinition underlay = findUnderlay(underlayId - 1);
hues[yi + BLEND] -= underlay.getHue();
sats[yi + BLEND] -= underlay.getSaturation();
light[yi + BLEND] -= underlay.getLightness();
mul[yi + BLEND] -= underlay.getHueMultiplier();
num[yi + BLEND]--;
}
}
}
}
if (xi >= 0 && xi < Region.X)
{
int runningHues = 0;
int runningSat = 0;
int runningLight = 0;
int runningMultiplier = 0;
int runningNumber = 0;
for (int yi = (hasDownRegion ? -BLEND * 2 : -BLEND); yi < Region.Y + (hasUpRegion ? BLEND * 2 : BLEND); ++yi)
{
int yu = yi + BLEND;
if (yu >= (hasDownRegion ? -BLEND : 0) && yu < Region.Y + (hasUpRegion ? BLEND : 0))
{
runningHues += hues[yu + BLEND];
runningSat += sats[yu + BLEND];
runningLight += light[yu + BLEND];
runningMultiplier += mul[yu + BLEND];
runningNumber += num[yu + BLEND];
}
int yd = yi - BLEND;
if (yd >= (hasDownRegion ? -BLEND : 0) && yd < Region.Y + (hasUpRegion ? BLEND : 0))
{
runningHues -= hues[yd + BLEND];
runningSat -= sats[yd + BLEND];
runningLight -= light[yd + BLEND];
runningMultiplier -= mul[yd + BLEND];
runningNumber -= num[yd + BLEND];
}
if (yi >= 0 && yi < Region.Y)
{
Region r = regionLoader.findRegionForWorldCoordinates(baseX + xi, baseY + yi);
if (r != null)
{
int underlayId = r.getUnderlayId(z, convert(xi), convert(yi));
int overlayId = r.getOverlayId(z, convert(xi), convert(yi));
if (underlayId > 0 || overlayId > 0)
{
int underlayHsl = -1;
if (underlayId > 0)
{
int avgHue = runningHues * 256 / runningMultiplier;
int avgSat = runningSat / runningNumber;
int avgLight = runningLight / runningNumber;
// randomness is added to avgHue here
if (avgLight < 0)
{
avgLight = 0;
}
else if (avgLight > 255)
{
avgLight = 255;
}
underlayHsl = packHsl(avgHue, avgSat, avgLight);
}
int underlayRgb = 0;
if (underlayHsl != -1)
{
int var0 = method1792(underlayHsl, 96);
underlayRgb = colorPalette[var0] | 0xFF000000;
}
int shape, rotation;
int overlayRgb = 0;
if (overlayId == 0)
{
shape = rotation = 0;
}
else
{
shape = r.getOverlayPath(z, convert(xi), convert(yi)) + 1;
rotation = r.getOverlayRotation(z, convert(xi), convert(yi));
OverlayDefinition overlayDefinition = findOverlay(overlayId - 1);
int overlayTexture = overlayDefinition.getTexture();
int hsl;
if (overlayTexture >= 0)
{
hsl = rsTextureProvider.getAverageTextureRGB(overlayTexture);
}
else if (overlayDefinition.getRgbColor() == 0xFF_00FF)
{
hsl = -2;
}
else
{
// randomness added here
int overlayHsl = packHsl(overlayDefinition.getHue(), overlayDefinition.getSaturation(), overlayDefinition.getLightness());
hsl = overlayHsl;
}
if (hsl != -2)
{
int var0 = adjustHSLListness0(hsl, 96);
overlayRgb = colorPalette[var0] | 0xFF000000;
}
if (overlayDefinition.getSecondaryRgbColor() != -1)
{
int hue = overlayDefinition.getOtherHue();
int sat = overlayDefinition.getOtherSaturation();
int olight = overlayDefinition.getOtherLightness();
hsl = packHsl(hue, sat, olight);
int var0 = adjustHSLListness0(hsl, 96);
overlayRgb = colorPalette[var0] | 0xFF000000;
}
}
if (shape == 0)
{
int drawX = xi;
int drawY = Region.Y - 1 - yi;
if (underlayRgb != 0)
{
drawMapSquare(pixels, drawX, drawY, underlayRgb);
}
}
else if (shape == 1)
{
int drawX = xi;
int drawY = Region.Y - 1 - yi;
drawMapSquare(pixels, drawX, drawY, overlayRgb);
}
else
{
int drawX = xi * MAP_SCALE;
int drawY = (Region.Y - 1 - yi) * MAP_SCALE;
int[] tileShapes = TILE_SHAPE_2D[shape];
int[] tileRotations = TILE_ROTATION_2D[rotation];
if (underlayRgb != 0)
{
int rotIdx = 0;
for (int i = 0; i < Region.Z; ++i)
{
int p1 = tileShapes[tileRotations[rotIdx++]] == 0 ? underlayRgb : overlayRgb;
int p2 = tileShapes[tileRotations[rotIdx++]] == 0 ? underlayRgb : overlayRgb;
int p3 = tileShapes[tileRotations[rotIdx++]] == 0 ? underlayRgb : overlayRgb;
int p4 = tileShapes[tileRotations[rotIdx++]] == 0 ? underlayRgb : overlayRgb;
pixels[drawX + 0][drawY + i] = p1;
pixels[drawX + 1][drawY + i] = p2;
pixels[drawX + 2][drawY + i] = p3;
pixels[drawX + 3][drawY + i] = p4;
}
}
else
{
int rotIdx = 0;
for (int i = 0; i < Region.Z; ++i)
{
int p1 = tileShapes[tileRotations[rotIdx++]];
int p2 = tileShapes[tileRotations[rotIdx++]];
int p3 = tileShapes[tileRotations[rotIdx++]];
int p4 = tileShapes[tileRotations[rotIdx++]];
if (p1 != 0)
{
pixels[drawX + 0][drawY + i] = overlayRgb;
}
if (p2 != 0)
{
pixels[drawX + 1][drawY + i] = overlayRgb;
}
if (p3 != 0)
{
pixels[drawX + 2][drawY + i] = overlayRgb;
}
if (p4 != 0)
{
pixels[drawX + 3][drawY + i] = overlayRgb;
}
}
}
}
}
}
}
}
}
}
}
private static int convert(int d)
{
if (d >= 0)
{
return d % 64;
}
else
{
return 64 - -(d % 64) - 1;
}
}
private void drawObjects(BufferedImage image, int drawBaseX, int drawBaseY, Region region, int z)
{
if (!renderObjects)
{
return;
}
List<Location> planeLocs = new ArrayList<>();
List<Location> pushDownLocs = new ArrayList<>();
List<List<Location>> layers = Arrays.asList(planeLocs, pushDownLocs);
for (int localX = 0; localX < Region.X; localX++)
{
int regionX = localX + region.getBaseX();
for (int localY = 0; localY < Region.Y; localY++)
{
int regionY = localY + region.getBaseY();
planeLocs.clear();
pushDownLocs.clear();
boolean isBridge = (region.getTileSetting(1, localX, localY) & 2) != 0;
int tileZ = z + (isBridge ? 1 : 0);
for (Location loc : region.getLocations())
{
Position pos = loc.getPosition();
if (pos.getX() != regionX || pos.getY() != regionY)
{
continue;
}
if (pos.getZ() == tileZ && (region.getTileSetting(z, localX, localY) & 24) == 0)
{
planeLocs.add(loc);
}
else if (z < 3 && pos.getZ() == tileZ + 1 && (region.getTileSetting(z + 1, localX, localY) & 8) != 0)
{
pushDownLocs.add(loc);
}
}
for (List<Location> locs : layers)
{
for (Location location : locs)
{
int type = location.getType();
if (type >= 0 && type <= 3)
{
int rotation = location.getOrientation();
ObjectDefinition object = findObject(location.getId());
int drawX = (drawBaseX + localX) * MAP_SCALE;
int drawY = (drawBaseY + (Region.Y - object.getSizeY() - localY)) * MAP_SCALE;
int rgb = wallColor;
if (object.getWallOrDoor() != 0)
{
rgb = doorColor;
}
rgb |= 0xFF000000;
if (object.getMapSceneID() != -1)
{
blitMapDecoration(image, drawX, drawY, object);
}
else if (drawX >= 0 && drawY >= 0 && drawX < image.getWidth() && drawY < image.getHeight())
{
if (type == 0 || type == 2)
{
if (rotation == 0)
{
image.setRGB(drawX + 0, drawY + 0, rgb);
image.setRGB(drawX + 0, drawY + 1, rgb);
image.setRGB(drawX + 0, drawY + 2, rgb);
image.setRGB(drawX + 0, drawY + 3, rgb);
}
else if (rotation == 1)
{
image.setRGB(drawX + 0, drawY + 0, rgb);
image.setRGB(drawX + 1, drawY + 0, rgb);
image.setRGB(drawX + 2, drawY + 0, rgb);
image.setRGB(drawX + 3, drawY + 0, rgb);
}
else if (rotation == 2)
{
image.setRGB(drawX + 3, drawY + 0, rgb);
image.setRGB(drawX + 3, drawY + 1, rgb);
image.setRGB(drawX + 3, drawY + 2, rgb);
image.setRGB(drawX + 3, drawY + 3, rgb);
}
else if (rotation == 3)
{
image.setRGB(drawX + 0, drawY + 3, rgb);
image.setRGB(drawX + 1, drawY + 3, rgb);
image.setRGB(drawX + 2, drawY + 3, rgb);
image.setRGB(drawX + 3, drawY + 3, rgb);
}
}
if (type == 3)
{
if (rotation == 0)
{
image.setRGB(drawX + 0, drawY + 0, rgb);
}
else if (rotation == 1)
{
image.setRGB(drawX + 3, drawY + 0, rgb);
}
else if (rotation == 2)
{
image.setRGB(drawX + 3, drawY + 3, rgb);
}
else if (rotation == 3)
{
image.setRGB(drawX + 0, drawY + 3, rgb);
}
}
if (type == 2)
{
if (rotation == 3)
{
image.setRGB(drawX + 0, drawY + 0, rgb);
image.setRGB(drawX + 0, drawY + 1, rgb);
image.setRGB(drawX + 0, drawY + 2, rgb);
image.setRGB(drawX + 0, drawY + 3, rgb);
}
else if (rotation == 0)
{
image.setRGB(drawX + 0, drawY + 0, rgb);
image.setRGB(drawX + 1, drawY + 0, rgb);
image.setRGB(drawX + 2, drawY + 0, rgb);
image.setRGB(drawX + 3, drawY + 0, rgb);
}
else if (rotation == 1)
{
image.setRGB(drawX + 3, drawY + 0, rgb);
image.setRGB(drawX + 3, drawY + 1, rgb);
image.setRGB(drawX + 3, drawY + 2, rgb);
image.setRGB(drawX + 3, drawY + 3, rgb);
}
else if (rotation == 2)
{
image.setRGB(drawX + 0, drawY + 3, rgb);
image.setRGB(drawX + 1, drawY + 3, rgb);
image.setRGB(drawX + 2, drawY + 3, rgb);
image.setRGB(drawX + 3, drawY + 3, rgb);
}
}
}
}
}
for (Location location : locs)
{
int type = location.getType();
if (type == 9)
{
int rotation = location.getOrientation();
ObjectDefinition object = findObject(location.getId());
int drawX = (drawBaseX + localX) * MAP_SCALE;
int drawY = (drawBaseY + (Region.Y - object.getSizeY() - localY)) * MAP_SCALE;
if (object.getMapSceneID() != -1)
{
blitMapDecoration(image, drawX, drawY, object);
continue;
}
if (drawX >= 0 && drawY >= 0 && drawX < image.getWidth() && drawY < image.getHeight())
{
int rgb = 0xFFEE_EEEE;
if (object.getWallOrDoor() != 0)
{
rgb = 0xFFEE_0000;
}
if (rotation != 0 && rotation != 2)
{
image.setRGB(drawX + 0, drawY + 0, rgb);
image.setRGB(drawX + 1, drawY + 1, rgb);
image.setRGB(drawX + 2, drawY + 2, rgb);
image.setRGB(drawX + 3, drawY + 3, rgb);
}
else
{
image.setRGB(drawX + 0, drawY + 3, rgb);
image.setRGB(drawX + 1, drawY + 2, rgb);
image.setRGB(drawX + 2, drawY + 1, rgb);
image.setRGB(drawX + 3, drawY + 0, rgb);
}
}
}
}
for (Location location : locs)
{
int type = location.getType();
if (type == 22 || (type >= 9 && type <= 11))
{
ObjectDefinition object = findObject(location.getId());
int drawX = (drawBaseX + localX) * MAP_SCALE;
int drawY = (drawBaseY + (Region.Y - object.getSizeY() - localY)) * MAP_SCALE;
if (object.getMapSceneID() != -1)
{
blitMapDecoration(image, drawX, drawY, object);
}
}
}
}
}
}
}
private void drawObjects(BufferedImage image, int z)
{
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
drawObjects(image, drawBaseX, drawBaseY, region, z);
}
}
private void drawMapIcons(BufferedImage image, int drawBaseX, int drawBaseY, Region region, int z)
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
Graphics2D graphics = image.createGraphics();
drawMapIcons(image, region, z, drawBaseX, drawBaseY);
if (labelRegions)
{
graphics.setColor(Color.WHITE);
String str = baseX + "," + baseY + " (" + region.getRegionX() + "," + region.getRegionY() + ")";
graphics.drawString(str, drawBaseX * MAP_SCALE, drawBaseY * MAP_SCALE + graphics.getFontMetrics().getHeight());
}
if (outlineRegions)
{
graphics.setColor(Color.WHITE);
graphics.drawRect(drawBaseX * MAP_SCALE, drawBaseY * MAP_SCALE, Region.X * MAP_SCALE, Region.Y * MAP_SCALE);
}
graphics.dispose();
}
private void drawMapIcons(BufferedImage image, int z)
{
// map icons
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
drawMapIcons(image, drawBaseX, drawBaseY, region, z);
}
}
private void drawMapLabels(BufferedImage image, int z)
{
if (!renderLabels)
{
return;
}
FontName[] fontSizes = new FontName[] { FontName.VERDANA_11, FontName.VERDANA_13, FontName.VERDANA_15 };
List<WorldMapElementDefinition> elements = worldMapManager.getElements();
for (WorldMapElementDefinition element : elements)
{
AreaDefinition area = areas.getArea(element.getAreaDefinitionId());
Position worldPosition = element.getWorldPosition();
if (area == null || area.getName() == null || worldPosition.getZ() != z)
{
continue;
}
FontName fontSize = fontSizes[area.getTextScale()];
FontDefinition font = fonts.findFontByName(fontSize.getName());
String areaLabel = area.getName();
String[] lines = areaLabel.split("<br>");
int ascent = 0;
for (String line : lines)
{
int advance = 0;
int stringWidth = font.stringWidth(line);
for (int i = 0; i < line.length(); ++i)
{
char c = line.charAt(i);
SpriteDefinition sprite = sprites.findSpriteByArchiveName(fontSize.getName(), c);
if (sprite.getWidth() != 0 && sprite.getHeight() != 0)
{
int drawX = worldPosition.getX() - regionLoader.getLowestX().getBaseX();
int drawY = regionLoader.getHighestY().getBaseY() - worldPosition.getY() + Region.Y - 2;
blitGlyph(image,
(drawX * MAP_SCALE) + advance - (stringWidth / 2),
(drawY * MAP_SCALE) + ascent - (font.getAscent() / 2),
area.getTextColor(),
sprite
);
}
advance += font.getAdvances()[c];
}
ascent += font.getAscent() / 2;
}
}
}
private void dumpMapIcons(int z) {
Map<Integer, List<Integer>> icons = new HashMap<>();
if (z == 0) return;
for (Region region : regionLoader.getRegions()) {
for (Location location : region.getLocations()) {
ObjectDefinition od = findObject(location.getId());
if (od.getMapAreaId() != -1) {
AreaDefinition area = areas.getArea(od.getMapAreaId());
List<Integer> x = icons.computeIfAbsent(area.spriteId, k -> new ArrayList<>());
x.add(location.getPosition().getX());
x.add(location.getPosition().getY());
}
}
}
File outDir = new File(outputDirectory + "/icons");
outDir.mkdirs();
for (Integer spriteId : icons.keySet()) {
SpriteDefinition sprite = sprites.findSprite(spriteId, 0);
BufferedImage iconImage = new BufferedImage(sprite.getWidth(), sprite.getHeight(), BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < sprite.getWidth(); ++x) {
for (int y = 0; y < sprite.getHeight(); ++y) {
int rgb = sprite.getPixels()[x + (y * sprite.getWidth())];
if (rgb != 0) {
iconImage.setRGB(x, y, rgb | 0xFF000000);
}
}
}
try {
File iconFile = new File(outDir, spriteId + ".png");
ImageIO.write(iconImage, "png", iconFile);
} catch (Exception ex) {
log.error("Failed to write sprite file", ex);
}
}
try {
Gson gson = new Gson();
File jsonFile = new File(outDir, "map-icons.json");
FileWriter writer = new FileWriter(jsonFile);
gson.toJson(icons, writer);
writer.flush();
writer.close();
} catch (Exception ex) {
log.error("Failed to write map-icons.json", ex);
}
}
private ObjectDefinition findObject(int id)
{
return objectManager.getObject(id);
}
private int packHsl(int var0, int var1, int var2)
{
if (var2 > 179)
{
var1 /= 2;
}
if (var2 > 192)
{
var1 /= 2;
}
if (var2 > 217)
{
var1 /= 2;
}
if (var2 > 243)
{
var1 /= 2;
}
int var3 = (var1 / 32 << 7) + (var0 / 4 << 10) + var2 / 2;
return var3;
}
static int method1792(int var0, int var1)
{
if (var0 == -1)
{
return 12345678;
}
else
{
var1 = (var0 & 127) * var1 / 128;
if (var1 < 2)
{
var1 = 2;
}
else if (var1 > 126)
{
var1 = 126;
}
return (var0 & 65408) + var1;
}
}
static final int adjustHSLListness0(int var0, int var1)
{
if (var0 == -2)
{
return 12345678;
}
else if (var0 == -1)
{
if (var1 < 2)
{
var1 = 2;
}
else if (var1 > 126)
{
var1 = 126;
}
return var1;
}
else
{
var1 = (var0 & 127) * var1 / 128;
if (var1 < 2)
{
var1 = 2;
}
else if (var1 > 126)
{
var1 = 126;
}
return (var0 & 65408) + var1;
}
}
private void drawMapSquare(int[][] pixels, int x, int y, int rgb)
{
x *= MAP_SCALE;
y *= MAP_SCALE;
for (int i = 0; i < MAP_SCALE; ++i)
{
for (int j = 0; j < MAP_SCALE; ++j)
{
pixels[x + i][y + j] = rgb;
}
}
}
private void drawMapIcons(BufferedImage img, Region region, int z, int drawBaseX, int drawBaseY)
{
if (!renderIcons)
{
return;
}
for (Location location : region.getLocations())
{
int localX = location.getPosition().getX() - region.getBaseX();
int localY = location.getPosition().getY() - region.getBaseY();
if (z != location.getPosition().getZ())
{
continue;
}
ObjectDefinition od = findObject(location.getId());
assert od != null;
int drawX = drawBaseX + localX;
int drawY = drawBaseY + (Region.Y - 1 - localY);
if (od.getMapAreaId() != -1)
{
AreaDefinition area = areas.getArea(od.getMapAreaId());
assert area != null;
SpriteDefinition sprite = sprites.findSprite(area.spriteId, 0);
assert sprite != null;
blitIcon(img,
(drawX * MAP_SCALE) - (sprite.getMaxWidth() / 2),
(drawY * MAP_SCALE) - (sprite.getMaxHeight() / 2),
sprite);
}
}
// Draw the intermap link icons which are not stored with the map locations
List<WorldMapElementDefinition> elements = worldMapManager.getElements();
for (WorldMapElementDefinition element : elements)
{
AreaDefinition area = areas.getArea(element.getAreaDefinitionId());
Position worldPosition = element.getWorldPosition();
int regionX = worldPosition.getX() / Region.X;
int regionY = worldPosition.getY() / Region.Y;
if (area == null || area.getName() != null || worldPosition.getZ() != z || regionX != region.getRegionX() || regionY != region.getRegionY())
{
continue;
}
int localX = worldPosition.getX() - region.getBaseX();
int localY = worldPosition.getY() - region.getBaseY();
int drawX = drawBaseX + localX;
int drawY = drawBaseY + (Region.Y - 1 - localY);
SpriteDefinition sprite = sprites.findSprite(area.spriteId, 0);
blitIcon(img,
(drawX * MAP_SCALE) - (sprite.getMaxWidth() / 2),
(drawY * MAP_SCALE) - (sprite.getMaxHeight() / 2),
sprite);
}
}
private void loadRegions() throws IOException
{
regionLoader.loadRegions();
regionLoader.calculateBounds();
log.debug("North most region: {}", regionLoader.getLowestY().getBaseY());
log.debug("South most region: {}", regionLoader.getHighestY().getBaseY());
log.debug("West most region: {}", regionLoader.getLowestX().getBaseX());
log.debug("East most region: {}", regionLoader.getHighestX().getBaseX());
}
private void loadUnderlays(Store store) throws IOException
{
Storage storage = store.getStorage();
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.UNDERLAY.getId());
byte[] archiveData = storage.loadArchive(archive);
ArchiveFiles files = archive.getFiles(archiveData);
for (FSFile file : files.getFiles())
{
UnderlayLoader loader = new UnderlayLoader();
UnderlayDefinition underlay = loader.load(file.getFileId(), file.getContents());
underlays.put(underlay.getId(), underlay);
}
}
private UnderlayDefinition findUnderlay(int id)
{
return underlays.get(id);
}
private void loadOverlays(Store store) throws IOException
{
Storage storage = store.getStorage();
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.OVERLAY.getId());
byte[] archiveData = storage.loadArchive(archive);
ArchiveFiles files = archive.getFiles(archiveData);
for (FSFile file : files.getFiles())
{
OverlayLoader loader = new OverlayLoader();
OverlayDefinition overlay = loader.load(file.getFileId(), file.getContents());
overlays.put(overlay.getId(), overlay);
}
}
private OverlayDefinition findOverlay(int id)
{
return overlays.get(id);
}
private void loadSprites() throws IOException
{
Storage storage = store.getStorage();
Index index = store.getIndex(IndexType.SPRITES);
Archive a = index.findArchiveByName("mapscene");
byte[] contents = a.decompress(storage.loadArchive(a));
SpriteLoader loader = new SpriteLoader();
mapDecorations = loader.load(a.getArchiveId(), contents);
}
private void blitMapDecoration(BufferedImage dst, int x, int y, ObjectDefinition object)
{
SpriteDefinition sprite = mapDecorations[object.getMapSceneID()];
int ox = (object.getSizeX() * MAP_SCALE - sprite.getWidth()) / 2;
int oy = (object.getSizeY() * MAP_SCALE - sprite.getHeight()) / 2;
blitIcon(dst, x + ox, y + oy, sprite);
}
private void blitIcon(BufferedImage dst, int x, int y, SpriteDefinition sprite)
{
x += sprite.getOffsetX();
y += sprite.getOffsetY();
int ymin = Math.max(0, -y);
int ymax = Math.min(sprite.getHeight(), dst.getHeight() - y);
int xmin = Math.max(0, -x);
int xmax = Math.min(sprite.getWidth(), dst.getWidth() - x);
for (int yo = ymin; yo < ymax; yo++)
{
for (int xo = xmin; xo < xmax; xo++)
{
int rgb = sprite.getPixels()[xo + (yo * sprite.getWidth())];
if (rgb != 0)
{
dst.setRGB(x + xo, y + yo, rgb | 0xFF000000);
}
}
}
}
private void blitGlyph(BufferedImage dst, int x, int y, int color, SpriteDefinition glyph)
{
int[] pixels = glyph.getPixels();
int[] shadowPixels = new int[pixels.length];
for (int i = 0; i < pixels.length; ++i)
{
if (pixels[i] != 0)
{
pixels[i] = color;
shadowPixels[i] = 0xFF000000;
}
}
SpriteDefinition shadow = new SpriteDefinition();
shadow.setPixels(shadowPixels);
shadow.setOffsetX(glyph.getOffsetX());
shadow.setOffsetY(glyph.getOffsetY());
shadow.setWidth(glyph.getWidth());
shadow.setHeight(glyph.getHeight());
blitIcon(dst, x + 1, y + 1, shadow);
blitIcon(dst, x, y, glyph);
}
}