Merge pull request #3192 from open-osrs/upstream-1105
This commit is contained in:
@@ -30,6 +30,7 @@ import java.io.IOException;
|
|||||||
import net.runelite.cache.fs.Store;
|
import net.runelite.cache.fs.Store;
|
||||||
import net.runelite.cache.region.Region;
|
import net.runelite.cache.region.Region;
|
||||||
import net.runelite.cache.region.RegionLoader;
|
import net.runelite.cache.region.RegionLoader;
|
||||||
|
import net.runelite.cache.util.KeyProvider;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -48,9 +49,9 @@ public class HeightMapDumper
|
|||||||
this.store = store;
|
this.store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load() throws IOException
|
public void load(KeyProvider keyProvider) throws IOException
|
||||||
{
|
{
|
||||||
regionLoader = new RegionLoader(store);
|
regionLoader = new RegionLoader(store, keyProvider);
|
||||||
regionLoader.loadRegions();
|
regionLoader.loadRegions();
|
||||||
regionLoader.calculateBounds();
|
regionLoader.calculateBounds();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,23 +27,26 @@ package net.runelite.cache;
|
|||||||
|
|
||||||
public enum IndexType
|
public enum IndexType
|
||||||
{
|
{
|
||||||
FRAMES(0),
|
ANIMATIONS(0),
|
||||||
FRAMEMAPS(1),
|
SKELETONS(1),
|
||||||
CONFIGS(2),
|
CONFIGS(2),
|
||||||
INTERFACES(3),
|
INTERFACES(3),
|
||||||
SOUNDEFFECTS(4),
|
SOUNDEFFECTS(4),
|
||||||
MAPS(5),
|
MAPS(5),
|
||||||
TRACK1(6),
|
MUSIC_TRACKS(6),
|
||||||
MODELS(7),
|
MODELS(7),
|
||||||
SPRITES(8),
|
SPRITES(8),
|
||||||
TEXTURES(9),
|
TEXTURES(9),
|
||||||
BINARY(10),
|
BINARY(10),
|
||||||
TRACK2(11),
|
MUSIC_JINGLES(11),
|
||||||
CLIENTSCRIPT(12),
|
CLIENTSCRIPT(12),
|
||||||
FONTS(13),
|
FONTS(13),
|
||||||
VORBIS(14),
|
MUSIC_SAMPLES(14),
|
||||||
INSTRUMENTS(15),
|
MUSIC_PATCHES(15),
|
||||||
WORLDMAP(16);
|
WORLDMAP_OLD(16), // looks unused
|
||||||
|
WORLDMAP_GEOGRAPHY(18),
|
||||||
|
WORLDMAP(19),
|
||||||
|
WORLDMAP_GROUND(20);
|
||||||
|
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,20 @@ package net.runelite.cache;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Image;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.cache.definitions.AreaDefinition;
|
import net.runelite.cache.definitions.AreaDefinition;
|
||||||
import net.runelite.cache.definitions.ObjectDefinition;
|
import net.runelite.cache.definitions.ObjectDefinition;
|
||||||
import net.runelite.cache.definitions.OverlayDefinition;
|
import net.runelite.cache.definitions.OverlayDefinition;
|
||||||
@@ -47,39 +54,44 @@ import net.runelite.cache.fs.FSFile;
|
|||||||
import net.runelite.cache.fs.Index;
|
import net.runelite.cache.fs.Index;
|
||||||
import net.runelite.cache.fs.Storage;
|
import net.runelite.cache.fs.Storage;
|
||||||
import net.runelite.cache.fs.Store;
|
import net.runelite.cache.fs.Store;
|
||||||
import net.runelite.cache.item.ColorPalette;
|
|
||||||
import net.runelite.cache.item.RSTextureProvider;
|
import net.runelite.cache.item.RSTextureProvider;
|
||||||
|
import net.runelite.cache.models.JagexColor;
|
||||||
import net.runelite.cache.region.Location;
|
import net.runelite.cache.region.Location;
|
||||||
|
import net.runelite.cache.region.Position;
|
||||||
import net.runelite.cache.region.Region;
|
import net.runelite.cache.region.Region;
|
||||||
import net.runelite.cache.region.RegionLoader;
|
import net.runelite.cache.region.RegionLoader;
|
||||||
import net.runelite.cache.util.Djb2;
|
import net.runelite.cache.util.BigBufferedImage;
|
||||||
import org.slf4j.Logger;
|
import net.runelite.cache.util.KeyProvider;
|
||||||
import org.slf4j.LoggerFactory;
|
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;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Accessors(chain = true)
|
||||||
public class MapImageDumper
|
public class MapImageDumper
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MapImageDumper.class);
|
|
||||||
|
|
||||||
private static final int MAP_SCALE = 4; // this squared is the number of pixels per map square
|
private static final int MAP_SCALE = 4; // this squared is the number of pixels per map square
|
||||||
private static final int MAPICON_MAX_WIDTH = 5; // scale minimap icons down to this size so they fit..
|
|
||||||
private static final int MAPICON_MAX_HEIGHT = 6;
|
|
||||||
private static final int BLEND = 5; // number of surrounding tiles for ground blending
|
private static final int BLEND = 5; // number of surrounding tiles for ground blending
|
||||||
|
|
||||||
private static int[] colorPalette = new ColorPalette(0.9d, 0, 512).getColorPalette();
|
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_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 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) (Math.random() * 20.0D) - 10 << 16) + (238 + (int) (Math.random() * 20.0D) - 10 << 8) + (238 + (int) (Math.random() * 20.0D) - 10);
|
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) (Math.random() * 20.0D) - 10 << 16;
|
private final int doorColor = 238 + (int) (random() * 20.0D) - 10 << 16;
|
||||||
|
|
||||||
private final Store store;
|
private final Store store;
|
||||||
|
|
||||||
private final Map<Integer, UnderlayDefinition> underlays = new HashMap<>();
|
private final Map<Integer, UnderlayDefinition> underlays = new HashMap<>();
|
||||||
private final Map<Integer, OverlayDefinition> overlays = new HashMap<>();
|
private final Map<Integer, OverlayDefinition> overlays = new HashMap<>();
|
||||||
private final Map<Integer, Image> scaledMapIcons = new HashMap<>();
|
private SpriteDefinition[] mapDecorations;
|
||||||
|
|
||||||
private RegionLoader regionLoader;
|
private final RegionLoader regionLoader;
|
||||||
private final AreaManager areas;
|
private final AreaManager areas;
|
||||||
private final SpriteManager sprites;
|
private final SpriteManager sprites;
|
||||||
private RSTextureProvider rsTextureProvider;
|
private RSTextureProvider rsTextureProvider;
|
||||||
@@ -93,15 +105,106 @@ public class MapImageDumper
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean outlineRegions;
|
private boolean outlineRegions;
|
||||||
|
|
||||||
public MapImageDumper(Store store)
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean renderMap = true;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean renderObjects = true;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean renderIcons = true;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean transparency = false;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean lowMemory = true;
|
||||||
|
|
||||||
|
public MapImageDumper(Store store, KeyProvider keyProvider)
|
||||||
{
|
{
|
||||||
this.store = store;
|
this(store, new RegionLoader(store, keyProvider));
|
||||||
this.areas = new AreaManager(store);
|
|
||||||
this.sprites = new SpriteManager(store);
|
|
||||||
objectManager = new ObjectManager(store);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load() throws IOException
|
public MapImageDumper(Store store, RegionLoader regionLoader)
|
||||||
|
{
|
||||||
|
this.store = store;
|
||||||
|
this.regionLoader = regionLoader;
|
||||||
|
this.areas = new AreaManager(store);
|
||||||
|
this.sprites = new SpriteManager(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");
|
||||||
|
final String 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);
|
loadUnderlays(store);
|
||||||
loadOverlays(store);
|
loadOverlays(store);
|
||||||
@@ -111,10 +214,12 @@ public class MapImageDumper
|
|||||||
textureManager.load();
|
textureManager.load();
|
||||||
rsTextureProvider = new RSTextureProvider(textureManager, sprites);
|
rsTextureProvider = new RSTextureProvider(textureManager, sprites);
|
||||||
|
|
||||||
loadRegions(store);
|
loadRegions();
|
||||||
areas.load();
|
areas.load();
|
||||||
sprites.load();
|
sprites.load();
|
||||||
loadSprites();
|
loadSprites();
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage drawMap(int z)
|
public BufferedImage drawMap(int z)
|
||||||
@@ -131,11 +236,19 @@ public class MapImageDumper
|
|||||||
int pixelsX = dimX * MAP_SCALE;
|
int pixelsX = dimX * MAP_SCALE;
|
||||||
int pixelsY = dimY * MAP_SCALE;
|
int pixelsY = dimY * MAP_SCALE;
|
||||||
|
|
||||||
logger.info("Map image dimensions: {}px x {}px, {}px per map square ({} MB). Max memory: {}mb", pixelsX, pixelsY,
|
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),
|
MAP_SCALE, (pixelsX * pixelsY * 3 / 1024 / 1024),
|
||||||
Runtime.getRuntime().maxMemory() / 1024L / 1024L);
|
Runtime.getRuntime().maxMemory() / 1024L / 1024L);
|
||||||
|
|
||||||
BufferedImage image = new BufferedImage(pixelsX, pixelsY, BufferedImage.TYPE_INT_RGB);
|
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);
|
drawMap(image, z);
|
||||||
drawObjects(image, z);
|
drawObjects(image, z);
|
||||||
@@ -144,15 +257,35 @@ public class MapImageDumper
|
|||||||
return image;
|
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)
|
public BufferedImage drawRegion(Region region, int z)
|
||||||
{
|
{
|
||||||
int pixelsX = Region.X * MAP_SCALE;
|
int pixelsX = Region.X * MAP_SCALE;
|
||||||
int pixelsY = Region.Y * MAP_SCALE;
|
int pixelsY = Region.Y * MAP_SCALE;
|
||||||
|
|
||||||
BufferedImage image = new BufferedImage(pixelsX, pixelsY, BufferedImage.TYPE_INT_RGB);
|
BufferedImage image = new BufferedImage(pixelsX, pixelsY, transparency ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
|
||||||
|
|
||||||
drawMap(image, 0, 0, z, region);
|
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);
|
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);
|
drawMapIcons(image, 0, 0, region, z);
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
@@ -160,31 +293,41 @@ public class MapImageDumper
|
|||||||
|
|
||||||
private void drawMap(BufferedImage image, int drawBaseX, int drawBaseY, int z, Region region)
|
private void drawMap(BufferedImage image, int drawBaseX, int drawBaseY, int z, Region region)
|
||||||
{
|
{
|
||||||
int[][] map = new int[Region.X * MAP_SCALE][Region.Y * MAP_SCALE];
|
if (!renderMap)
|
||||||
drawMap(map, region, z);
|
|
||||||
|
|
||||||
int[][] above = null;
|
|
||||||
if (z < 3)
|
|
||||||
{
|
{
|
||||||
above = new int[Region.X * MAP_SCALE][Region.Y * MAP_SCALE];
|
return;
|
||||||
drawMap(above, region, z + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int[][][] map = new int[4][][];
|
||||||
|
|
||||||
for (int x = 0; x < Region.X; ++x)
|
for (int x = 0; x < Region.X; ++x)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < Region.Y; ++y)
|
for (int y = 0; y < Region.Y; ++y)
|
||||||
{
|
{
|
||||||
boolean isBridge = (region.getTileSetting(1, x, Region.Y - y - 1) & 2) != 0;
|
boolean isBridge = (region.getTileSetting(1, x, Region.Y - y - 1) & 2) != 0;
|
||||||
|
int tileZ = z + (isBridge ? 1 : 0);
|
||||||
int tileSetting = region.getTileSetting(z, x, Region.Y - y - 1);
|
if (tileZ >= Region.Z)
|
||||||
if (!isBridge && ((tileSetting & 24) == 0))
|
|
||||||
{
|
{
|
||||||
drawTile(image, map, drawBaseX, drawBaseY, x, y);
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (z < 3 && isBridge) // client also has a check for &8 != 0 here
|
int tileSetting = region.getTileSetting(z, x, Region.Y - y - 1);
|
||||||
|
if ((tileSetting & 24) == 0)
|
||||||
{
|
{
|
||||||
drawTile(image, above, drawBaseX, drawBaseY, x, y);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,15 +351,27 @@ public class MapImageDumper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawTile(BufferedImage to, int[][] pixels, int drawBaseX, int drawBaseY, int x, int y)
|
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 i = 0; i < MAP_SCALE; ++i)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < MAP_SCALE; ++j)
|
for (int j = 0; j < MAP_SCALE; ++j)
|
||||||
{
|
{
|
||||||
to.setRGB(drawBaseX * MAP_SCALE + x * MAP_SCALE + i,
|
int argb = pixels[x * MAP_SCALE + i][y * MAP_SCALE + j];
|
||||||
drawBaseY * MAP_SCALE + y * MAP_SCALE + j,
|
if (argb != 0)
|
||||||
pixels[x * MAP_SCALE + i][y * MAP_SCALE + j]);
|
{
|
||||||
|
to.setRGB(drawBaseX * MAP_SCALE + x * MAP_SCALE + i,
|
||||||
|
drawBaseY * MAP_SCALE + y * MAP_SCALE + j,
|
||||||
|
argb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,11 +500,11 @@ public class MapImageDumper
|
|||||||
if (underlayHsl != -1)
|
if (underlayHsl != -1)
|
||||||
{
|
{
|
||||||
int var0 = method1792(underlayHsl, 96);
|
int var0 = method1792(underlayHsl, 96);
|
||||||
underlayRgb = colorPalette[var0];
|
underlayRgb = colorPalette[var0] | 0xFF000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
int shape, rotation;
|
int shape, rotation;
|
||||||
Integer overlayRgb = null;
|
int overlayRgb = 0;
|
||||||
if (overlayId == 0)
|
if (overlayId == 0)
|
||||||
{
|
{
|
||||||
shape = rotation = 0;
|
shape = rotation = 0;
|
||||||
@@ -361,28 +516,27 @@ public class MapImageDumper
|
|||||||
|
|
||||||
OverlayDefinition overlayDefinition = findOverlay(overlayId - 1);
|
OverlayDefinition overlayDefinition = findOverlay(overlayId - 1);
|
||||||
int overlayTexture = overlayDefinition.getTexture();
|
int overlayTexture = overlayDefinition.getTexture();
|
||||||
int rgb;
|
int hsl;
|
||||||
|
|
||||||
if (overlayTexture >= 0)
|
if (overlayTexture >= 0)
|
||||||
{
|
{
|
||||||
rgb = rsTextureProvider.getAverageTextureRGB(overlayTexture);
|
hsl = rsTextureProvider.getAverageTextureRGB(overlayTexture);
|
||||||
}
|
}
|
||||||
else if (overlayDefinition.getRgbColor() == 0xFF_00FF)
|
else if (overlayDefinition.getRgbColor() == 0xFF_00FF)
|
||||||
{
|
{
|
||||||
rgb = -2;
|
hsl = -2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// randomness added here
|
// randomness added here
|
||||||
int overlayHsl = packHsl(overlayDefinition.getHue(), overlayDefinition.getSaturation(), overlayDefinition.getLightness());
|
int overlayHsl = packHsl(overlayDefinition.getHue(), overlayDefinition.getSaturation(), overlayDefinition.getLightness());
|
||||||
rgb = overlayHsl;
|
hsl = overlayHsl;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlayRgb = 0;
|
if (hsl != -2)
|
||||||
if (rgb != -2)
|
|
||||||
{
|
{
|
||||||
int var0 = adjustHSLListness0(rgb, 96);
|
int var0 = adjustHSLListness0(hsl, 96);
|
||||||
overlayRgb = colorPalette[var0];
|
overlayRgb = colorPalette[var0] | 0xFF000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlayDefinition.getSecondaryRgbColor() != -1)
|
if (overlayDefinition.getSecondaryRgbColor() != -1)
|
||||||
@@ -390,9 +544,9 @@ public class MapImageDumper
|
|||||||
int hue = overlayDefinition.getOtherHue();
|
int hue = overlayDefinition.getOtherHue();
|
||||||
int sat = overlayDefinition.getOtherSaturation();
|
int sat = overlayDefinition.getOtherSaturation();
|
||||||
int olight = overlayDefinition.getOtherLightness();
|
int olight = overlayDefinition.getOtherLightness();
|
||||||
rgb = packHsl(hue, sat, olight);
|
hsl = packHsl(hue, sat, olight);
|
||||||
int var0 = adjustHSLListness0(rgb, 96);
|
int var0 = adjustHSLListness0(hsl, 96);
|
||||||
overlayRgb = colorPalette[var0];
|
overlayRgb = colorPalette[var0] | 0xFF000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,208 +640,221 @@ public class MapImageDumper
|
|||||||
|
|
||||||
private void drawObjects(BufferedImage image, int drawBaseX, int drawBaseY, Region region, int z)
|
private void drawObjects(BufferedImage image, int drawBaseX, int drawBaseY, Region region, int z)
|
||||||
{
|
{
|
||||||
Graphics2D graphics = image.createGraphics();
|
if (!renderObjects)
|
||||||
|
|
||||||
for (Location location : region.getLocations())
|
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int rotation = location.getOrientation();
|
List<Location> planeLocs = new ArrayList<>();
|
||||||
int type = location.getType();
|
List<Location> pushDownLocs = new ArrayList<>();
|
||||||
|
List<List<Location>> layers = Arrays.asList(planeLocs, pushDownLocs);
|
||||||
int localX = location.getPosition().getX() - region.getBaseX();
|
for (int localX = 0; localX < Region.X; localX++)
|
||||||
int localY = location.getPosition().getY() - region.getBaseY();
|
{
|
||||||
|
int regionX = localX + region.getBaseX();
|
||||||
boolean isBridge = (region.getTileSetting(1, localX, localY) & 2) != 0;
|
for (int localY = 0; localY < Region.Y; localY++)
|
||||||
|
|
||||||
if (location.getPosition().getZ() == z + 1)
|
|
||||||
{
|
{
|
||||||
if (!isBridge)
|
int regionY = localY + region.getBaseY();
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (location.getPosition().getZ() == z)
|
|
||||||
{
|
|
||||||
if (isBridge)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((region.getTileSetting(z, localX, localY) & 24) != 0)
|
planeLocs.clear();
|
||||||
{
|
pushDownLocs.clear();
|
||||||
continue;
|
boolean isBridge = (region.getTileSetting(1, localX, localY) & 2) != 0;
|
||||||
}
|
int tileZ = z + (isBridge ? 1 : 0);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectDefinition object = findObject(location.getId());
|
for (Location loc : region.getLocations())
|
||||||
|
|
||||||
int drawX = (drawBaseX + localX) * MAP_SCALE;
|
|
||||||
int drawY = (drawBaseY + (Region.Y - 1 - localY)) * MAP_SCALE;
|
|
||||||
|
|
||||||
if (type >= 0 && type <= 3)
|
|
||||||
{
|
|
||||||
// this is a wall
|
|
||||||
int hash = (localY << 7) + localX + (location.getId() << 14) + 0x4000_0000;
|
|
||||||
if (object.getWallOrDoor() == 0)
|
|
||||||
{
|
{
|
||||||
hash -= Integer.MIN_VALUE;
|
Position pos = loc.getPosition();
|
||||||
}
|
if (pos.getX() != regionX || pos.getY() != regionY)
|
||||||
|
|
||||||
int rgb = wallColor;
|
|
||||||
if (hash > 0)
|
|
||||||
{
|
|
||||||
rgb = doorColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object.getMapSceneID() != -1)
|
|
||||||
{
|
|
||||||
Image spriteImage = scaledMapIcons.get(object.getMapSceneID());
|
|
||||||
graphics.drawImage(spriteImage, drawX * MAP_SCALE, drawY * MAP_SCALE, null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (type == 0 || type == 2)
|
|
||||||
{
|
{
|
||||||
if (rotation == 0)
|
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)
|
||||||
{
|
{
|
||||||
image.setRGB(drawX + 0, drawY + 0, rgb);
|
int rotation = location.getOrientation();
|
||||||
image.setRGB(drawX + 0, drawY + 1, rgb);
|
|
||||||
image.setRGB(drawX + 0, drawY + 2, rgb);
|
ObjectDefinition object = findObject(location.getId());
|
||||||
image.setRGB(drawX + 0, drawY + 3, rgb);
|
|
||||||
}
|
int drawX = (drawBaseX + localX) * MAP_SCALE;
|
||||||
else if (rotation == 1)
|
int drawY = (drawBaseY + (Region.Y - object.getSizeY() - localY)) * MAP_SCALE;
|
||||||
{
|
|
||||||
image.setRGB(drawX + 0, drawY + 0, rgb);
|
int rgb = wallColor;
|
||||||
image.setRGB(drawX + 1, drawY + 0, rgb);
|
if (object.getWallOrDoor() != 0)
|
||||||
image.setRGB(drawX + 2, drawY + 0, rgb);
|
{
|
||||||
image.setRGB(drawX + 3, drawY + 0, rgb);
|
rgb = doorColor;
|
||||||
}
|
}
|
||||||
else if (rotation == 2)
|
rgb |= 0xFF000000;
|
||||||
{
|
|
||||||
image.setRGB(drawX + 3, drawY + 0, rgb);
|
if (object.getMapSceneID() != -1)
|
||||||
image.setRGB(drawX + 3, drawY + 1, rgb);
|
{
|
||||||
image.setRGB(drawX + 3, drawY + 2, rgb);
|
blitMapDecoration(image, drawX, drawY, object);
|
||||||
image.setRGB(drawX + 3, drawY + 3, rgb);
|
}
|
||||||
}
|
else if (drawX >= 0 && drawY >= 0 && drawX < image.getWidth() && drawY < image.getHeight())
|
||||||
else if (rotation == 3)
|
{
|
||||||
{
|
if (type == 0 || type == 2)
|
||||||
image.setRGB(drawX + 0, drawY + 3, rgb);
|
{
|
||||||
image.setRGB(drawX + 1, drawY + 3, rgb);
|
if (rotation == 0)
|
||||||
image.setRGB(drawX + 2, drawY + 3, rgb);
|
{
|
||||||
image.setRGB(drawX + 3, drawY + 3, rgb);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == 3)
|
for (Location location : locs)
|
||||||
{
|
{
|
||||||
if (rotation == 0)
|
int type = location.getType();
|
||||||
|
if (type == 9)
|
||||||
{
|
{
|
||||||
image.setRGB(drawX + 0, drawY + 0, rgb);
|
int rotation = location.getOrientation();
|
||||||
}
|
|
||||||
else if (rotation == 1)
|
ObjectDefinition object = findObject(location.getId());
|
||||||
{
|
|
||||||
image.setRGB(drawX + 3, drawY + 0, rgb);
|
int drawX = (drawBaseX + localX) * MAP_SCALE;
|
||||||
}
|
int drawY = (drawBaseY + (Region.Y - object.getSizeY() - localY)) * MAP_SCALE;
|
||||||
else if (rotation == 2)
|
|
||||||
{
|
if (object.getMapSceneID() != -1)
|
||||||
image.setRGB(drawX + 3, drawY + 3, rgb);
|
{
|
||||||
}
|
blitMapDecoration(image, drawX, drawY, object);
|
||||||
else if (rotation == 3)
|
continue;
|
||||||
{
|
}
|
||||||
image.setRGB(drawX + 0, drawY + 3, rgb);
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == 2)
|
for (Location location : locs)
|
||||||
{
|
{
|
||||||
if (rotation == 3)
|
int type = location.getType();
|
||||||
|
if (type == 22 || (type >= 9 && type <= 11))
|
||||||
{
|
{
|
||||||
image.setRGB(drawX + 0, drawY + 0, rgb);
|
ObjectDefinition object = findObject(location.getId());
|
||||||
image.setRGB(drawX + 0, drawY + 1, rgb);
|
|
||||||
image.setRGB(drawX + 0, drawY + 2, rgb);
|
int drawX = (drawBaseX + localX) * MAP_SCALE;
|
||||||
image.setRGB(drawX + 0, drawY + 3, rgb);
|
int drawY = (drawBaseY + (Region.Y - object.getSizeY() - localY)) * MAP_SCALE;
|
||||||
}
|
|
||||||
else if (rotation == 0)
|
if (object.getMapSceneID() != -1)
|
||||||
{
|
{
|
||||||
image.setRGB(drawX + 0, drawY + 0, rgb);
|
blitMapDecoration(image, drawX, drawY, object);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == 9)
|
|
||||||
{
|
|
||||||
if (object.getMapSceneID() != -1)
|
|
||||||
{
|
|
||||||
Image spriteImage = scaledMapIcons.get(object.getMapSceneID());
|
|
||||||
graphics.drawImage(spriteImage, drawX, drawY, null);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hash = (localY << 7) + localX + (location.getId() << 14) + 0x4000_0000;
|
|
||||||
if (object.getWallOrDoor() == 0)
|
|
||||||
{
|
|
||||||
hash -= Integer.MIN_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((hash >> 29 & 3) != 2)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rgb = 0xEE_EEEE;
|
|
||||||
if (hash > 0)
|
|
||||||
{
|
|
||||||
rgb = 0xEE_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == 22 || (type >= 9 && type <= 11))
|
|
||||||
{
|
|
||||||
// ground object
|
|
||||||
if (object.getMapSceneID() != -1)
|
|
||||||
{
|
|
||||||
Image spriteImage = scaledMapIcons.get(object.getMapSceneID());
|
|
||||||
graphics.drawImage(spriteImage, drawX, drawY, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawObjects(BufferedImage image, int z)
|
private void drawObjects(BufferedImage image, int z)
|
||||||
@@ -715,7 +882,7 @@ public class MapImageDumper
|
|||||||
|
|
||||||
Graphics2D graphics = image.createGraphics();
|
Graphics2D graphics = image.createGraphics();
|
||||||
|
|
||||||
drawMapIcons(graphics, region, z, drawBaseX, drawBaseY);
|
drawMapIcons(image, region, z, drawBaseX, drawBaseY);
|
||||||
|
|
||||||
if (labelRegions)
|
if (labelRegions)
|
||||||
{
|
{
|
||||||
@@ -854,12 +1021,22 @@ public class MapImageDumper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawMapIcons(Graphics2D graphics, Region region, int z, int drawBaseX, int drawBaseY)
|
private void drawMapIcons(BufferedImage img, Region region, int z, int drawBaseX, int drawBaseY)
|
||||||
{
|
{
|
||||||
|
if (!renderIcons)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (Location location : region.getLocations())
|
for (Location location : region.getLocations())
|
||||||
{
|
{
|
||||||
|
int localX = location.getPosition().getX() - region.getBaseX();
|
||||||
|
int localY = location.getPosition().getY() - region.getBaseY();
|
||||||
|
boolean isBridge = (region.getTileSetting(1, localX, localY) & 2) != 0;
|
||||||
|
|
||||||
|
int tileZ = z + (isBridge ? 1 : 0);
|
||||||
int localZ = location.getPosition().getZ();
|
int localZ = location.getPosition().getZ();
|
||||||
if (z != 0 && localZ != z)
|
if (z != 0 && localZ != tileZ)
|
||||||
{
|
{
|
||||||
// draw all icons on z=0
|
// draw all icons on z=0
|
||||||
continue;
|
continue;
|
||||||
@@ -869,9 +1046,6 @@ public class MapImageDumper
|
|||||||
|
|
||||||
assert od != null;
|
assert od != null;
|
||||||
|
|
||||||
int localX = location.getPosition().getX() - region.getBaseX();
|
|
||||||
int localY = location.getPosition().getY() - region.getBaseY();
|
|
||||||
|
|
||||||
int drawX = drawBaseX + localX;
|
int drawX = drawBaseX + localX;
|
||||||
int drawY = drawBaseY + (Region.Y - 1 - localY);
|
int drawY = drawBaseY + (Region.Y - 1 - localY);
|
||||||
|
|
||||||
@@ -880,27 +1054,26 @@ public class MapImageDumper
|
|||||||
AreaDefinition area = areas.getArea(od.getMapAreaId());
|
AreaDefinition area = areas.getArea(od.getMapAreaId());
|
||||||
assert area != null;
|
assert area != null;
|
||||||
|
|
||||||
int spriteId = area.spriteId;
|
SpriteDefinition sprite = sprites.findSprite(area.spriteId, 0);
|
||||||
|
|
||||||
SpriteDefinition sprite = sprites.findSprite(spriteId, 0);
|
|
||||||
assert sprite != null;
|
assert sprite != null;
|
||||||
|
|
||||||
BufferedImage iconImage = sprites.getSpriteImage(sprite);
|
blitIcon(img,
|
||||||
graphics.drawImage(iconImage, drawX * MAP_SCALE, drawY * MAP_SCALE, null);
|
2 + (drawX * MAP_SCALE) - (sprite.getMaxWidth() / 2),
|
||||||
|
2 + (drawY * MAP_SCALE) - (sprite.getMaxHeight() / 2),
|
||||||
|
sprite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRegions(Store store) throws IOException
|
private void loadRegions() throws IOException
|
||||||
{
|
{
|
||||||
regionLoader = new RegionLoader(store);
|
|
||||||
regionLoader.loadRegions();
|
regionLoader.loadRegions();
|
||||||
regionLoader.calculateBounds();
|
regionLoader.calculateBounds();
|
||||||
|
|
||||||
logger.info("North most region: {}", regionLoader.getLowestY().getBaseY());
|
log.debug("North most region: {}", regionLoader.getLowestY().getBaseY());
|
||||||
logger.info("South most region: {}", regionLoader.getHighestY().getBaseY());
|
log.debug("South most region: {}", regionLoader.getHighestY().getBaseY());
|
||||||
logger.info("West most region: {}", regionLoader.getLowestX().getBaseX());
|
log.debug("West most region: {}", regionLoader.getLowestX().getBaseX());
|
||||||
logger.info("East most region: {}", regionLoader.getHighestX().getBaseX());
|
log.debug("East most region: {}", regionLoader.getHighestX().getBaseX());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadUnderlays(Store store) throws IOException
|
private void loadUnderlays(Store store) throws IOException
|
||||||
@@ -953,35 +1126,42 @@ public class MapImageDumper
|
|||||||
{
|
{
|
||||||
Storage storage = store.getStorage();
|
Storage storage = store.getStorage();
|
||||||
Index index = store.getIndex(IndexType.SPRITES);
|
Index index = store.getIndex(IndexType.SPRITES);
|
||||||
final int mapsceneHash = Djb2.hash("mapscene");
|
Archive a = index.findArchiveByName("mapscene");
|
||||||
|
byte[] contents = a.decompress(storage.loadArchive(a));
|
||||||
|
|
||||||
for (Archive a : index.getArchives())
|
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++)
|
||||||
{
|
{
|
||||||
byte[] contents = a.decompress(storage.loadArchive(a));
|
for (int xo = xmin; xo < xmax; xo++)
|
||||||
|
|
||||||
SpriteLoader loader = new SpriteLoader();
|
|
||||||
SpriteDefinition[] sprites = loader.load(a.getArchiveId(), contents);
|
|
||||||
|
|
||||||
for (SpriteDefinition sprite : sprites)
|
|
||||||
{
|
{
|
||||||
if (sprite.getHeight() <= 0 || sprite.getWidth() <= 0)
|
int rgb = sprite.getPixels()[xo + (yo * sprite.getWidth())];
|
||||||
|
if (rgb != 0)
|
||||||
{
|
{
|
||||||
continue;
|
dst.setRGB(x + xo, y + yo, rgb | 0xFF000000);
|
||||||
}
|
|
||||||
|
|
||||||
if (a.getNameHash() == mapsceneHash)
|
|
||||||
{
|
|
||||||
BufferedImage spriteImage = new BufferedImage(sprite.getWidth(), sprite.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
|
||||||
spriteImage.setRGB(0, 0, sprite.getWidth(), sprite.getHeight(), sprite.getPixels(), 0, sprite.getWidth());
|
|
||||||
|
|
||||||
// scale image down so it fits
|
|
||||||
Image scaledImage = spriteImage.getScaledInstance(MAPICON_MAX_WIDTH, MAPICON_MAX_HEIGHT, 0);
|
|
||||||
|
|
||||||
assert scaledMapIcons.containsKey(sprite.getFrame()) == false;
|
|
||||||
scaledMapIcons.put(sprite.getFrame(), scaledImage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018, 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.item;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class ColorPalette
|
|
||||||
{
|
|
||||||
@Getter
|
|
||||||
private final int[] colorPalette;
|
|
||||||
|
|
||||||
public ColorPalette(double brightness, int var2, int var3)
|
|
||||||
{
|
|
||||||
colorPalette = buildColorPalettee(brightness, var2, var3);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] buildColorPalettee(double brightness, int var2, int var3)
|
|
||||||
{
|
|
||||||
int[] colorPalette = new int[65536];
|
|
||||||
int var4 = var2 * 128;
|
|
||||||
|
|
||||||
for (int var5 = var2; var5 < var3; ++var5)
|
|
||||||
{
|
|
||||||
double var6 = (double) (var5 >> 3) / 64.0D + 0.0078125D;
|
|
||||||
double var8 = (double) (var5 & 7) / 8.0D + 0.0625D;
|
|
||||||
|
|
||||||
for (int var10 = 0; var10 < 128; ++var10)
|
|
||||||
{
|
|
||||||
double var11 = (double) var10 / 128.0D;
|
|
||||||
double var13 = var11;
|
|
||||||
double var15 = var11;
|
|
||||||
double var17 = var11;
|
|
||||||
if (var8 != 0.0D)
|
|
||||||
{
|
|
||||||
double var19;
|
|
||||||
if (var11 < 0.5D)
|
|
||||||
{
|
|
||||||
var19 = var11 * (1.0D + var8);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var19 = var11 + var8 - var11 * var8;
|
|
||||||
}
|
|
||||||
|
|
||||||
double var21 = 2.0D * var11 - var19;
|
|
||||||
double var23 = var6 + 0.3333333333333333D;
|
|
||||||
if (var23 > 1.0D)
|
|
||||||
{
|
|
||||||
--var23;
|
|
||||||
}
|
|
||||||
|
|
||||||
double var27 = var6 - 0.3333333333333333D;
|
|
||||||
if (var27 < 0.0D)
|
|
||||||
{
|
|
||||||
++var27;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (6.0D * var23 < 1.0D)
|
|
||||||
{
|
|
||||||
var13 = var21 + (var19 - var21) * 6.0D * var23;
|
|
||||||
}
|
|
||||||
else if (2.0D * var23 < 1.0D)
|
|
||||||
{
|
|
||||||
var13 = var19;
|
|
||||||
}
|
|
||||||
else if (3.0D * var23 < 2.0D)
|
|
||||||
{
|
|
||||||
var13 = var21 + (var19 - var21) * (0.6666666666666666D - var23) * 6.0D;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var13 = var21;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (6.0D * var6 < 1.0D)
|
|
||||||
{
|
|
||||||
var15 = var21 + (var19 - var21) * 6.0D * var6;
|
|
||||||
}
|
|
||||||
else if (2.0D * var6 < 1.0D)
|
|
||||||
{
|
|
||||||
var15 = var19;
|
|
||||||
}
|
|
||||||
else if (3.0D * var6 < 2.0D)
|
|
||||||
{
|
|
||||||
var15 = var21 + (var19 - var21) * (0.6666666666666666D - var6) * 6.0D;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var15 = var21;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (6.0D * var27 < 1.0D)
|
|
||||||
{
|
|
||||||
var17 = var21 + (var19 - var21) * 6.0D * var27;
|
|
||||||
}
|
|
||||||
else if (2.0D * var27 < 1.0D)
|
|
||||||
{
|
|
||||||
var17 = var19;
|
|
||||||
}
|
|
||||||
else if (3.0D * var27 < 2.0D)
|
|
||||||
{
|
|
||||||
var17 = var21 + (var19 - var21) * (0.6666666666666666D - var27) * 6.0D;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var17 = var21;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int var29 = (int) (var13 * 256.0D);
|
|
||||||
int var20 = (int) (var15 * 256.0D);
|
|
||||||
int var30 = (int) (var17 * 256.0D);
|
|
||||||
int var22 = var30 + (var20 << 8) + (var29 << 16);
|
|
||||||
var22 = adjustRGB(var22, brightness);
|
|
||||||
if (var22 == 0)
|
|
||||||
{
|
|
||||||
var22 = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
colorPalette[var4++] = var22;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return colorPalette;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int adjustRGB(int var0, double var1)
|
|
||||||
{
|
|
||||||
double var3 = (double) (var0 >> 16) / 256.0D;
|
|
||||||
double var5 = (double) (var0 >> 8 & 255) / 256.0D;
|
|
||||||
double var7 = (double) (var0 & 255) / 256.0D;
|
|
||||||
var3 = Math.pow(var3, var1);
|
|
||||||
var5 = Math.pow(var5, var1);
|
|
||||||
var7 = Math.pow(var7, var1);
|
|
||||||
int var9 = (int) (var3 * 256.0D);
|
|
||||||
int var10 = (int) (var5 * 256.0D);
|
|
||||||
int var11 = (int) (var7 * 256.0D);
|
|
||||||
return var11 + (var10 << 8) + (var9 << 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.cache.item;
|
package net.runelite.cache.item;
|
||||||
|
|
||||||
|
import net.runelite.cache.models.JagexColor;
|
||||||
|
|
||||||
class Graphics3D extends Rasterizer2D
|
class Graphics3D extends Rasterizer2D
|
||||||
{
|
{
|
||||||
private static final double UNIT = Math.PI / 1024d; // How much of the circle each unit of SINE/COSINE is
|
private static final double UNIT = Math.PI / 1024d; // How much of the circle each unit of SINE/COSINE is
|
||||||
@@ -132,9 +134,9 @@ class Graphics3D extends Rasterizer2D
|
|||||||
Rasterizer3D_clipMidY2 = Rasterizer3D_clipHeight - centerY;
|
Rasterizer3D_clipMidY2 = Rasterizer3D_clipHeight - centerY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setBrightness(double var0)
|
public final void setBrightness(double brightness)
|
||||||
{
|
{
|
||||||
colorPalette = new ColorPalette(var0, 0, 512).getColorPalette();
|
colorPalette = JagexColor.createPalette(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
final void rasterGouraud(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8)
|
final void rasterGouraud(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import net.runelite.cache.definitions.providers.ModelProvider;
|
|||||||
import net.runelite.cache.definitions.providers.SpriteProvider;
|
import net.runelite.cache.definitions.providers.SpriteProvider;
|
||||||
import net.runelite.cache.definitions.providers.TextureProvider;
|
import net.runelite.cache.definitions.providers.TextureProvider;
|
||||||
import net.runelite.cache.models.FaceNormal;
|
import net.runelite.cache.models.FaceNormal;
|
||||||
|
import net.runelite.cache.models.JagexColor;
|
||||||
import net.runelite.cache.models.VertexNormal;
|
import net.runelite.cache.models.VertexNormal;
|
||||||
|
|
||||||
public class ItemSpriteFactory
|
public class ItemSpriteFactory
|
||||||
@@ -111,7 +112,7 @@ public class ItemSpriteFactory
|
|||||||
|
|
||||||
Sprite spritePixels = new Sprite(36, 32);
|
Sprite spritePixels = new Sprite(36, 32);
|
||||||
Graphics3D graphics = new Graphics3D(rsTextureProvider);
|
Graphics3D graphics = new Graphics3D(rsTextureProvider);
|
||||||
graphics.setBrightness(0.6d);
|
graphics.setBrightness(JagexColor.BRIGHTNESS_MAX);
|
||||||
graphics.setRasterBuffer(spritePixels.pixels, 36, 32);
|
graphics.setRasterBuffer(spritePixels.pixels, 36, 32);
|
||||||
graphics.reset();
|
graphics.reset();
|
||||||
graphics.setRasterClipping();
|
graphics.setRasterClipping();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public final class JagexColor
|
|||||||
public static final double BRIGHTNESS_MAX = .6;
|
public static final double BRIGHTNESS_MAX = .6;
|
||||||
public static final double BRIGHTNESS_HIGH = .7;
|
public static final double BRIGHTNESS_HIGH = .7;
|
||||||
public static final double BRIGHTNESS_LOW = .8;
|
public static final double BRIGHTNESS_LOW = .8;
|
||||||
public static final double BRIGTHNESS_MIN = .9;
|
public static final double BRIGHTNESS_MIN = .9;
|
||||||
|
|
||||||
private static final double HUE_OFFSET = (.5 / 64.D);
|
private static final double HUE_OFFSET = (.5 / 64.D);
|
||||||
private static final double SATURATION_OFFSET = (.5 / 8.D);
|
private static final double SATURATION_OFFSET = (.5 / 8.D);
|
||||||
@@ -133,4 +133,14 @@ public final class JagexColor
|
|||||||
| ((int) (g * 256.0D) << 8)
|
| ((int) (g * 256.0D) << 8)
|
||||||
| (int) (b * 256.0D);
|
| (int) (b * 256.0D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int[] createPalette(double brightness)
|
||||||
|
{
|
||||||
|
int[] colorPalette = new int[65536];
|
||||||
|
for (int i = 0; i < colorPalette.length; i++)
|
||||||
|
{
|
||||||
|
colorPalette[i] = HSLtoRGB((short) i, brightness);
|
||||||
|
}
|
||||||
|
return colorPalette;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ import net.runelite.cache.definitions.TextureDefinition;
|
|||||||
|
|
||||||
public class ObjExporter
|
public class ObjExporter
|
||||||
{
|
{
|
||||||
private static final double BRIGHTNESS = JagexColor.BRIGTHNESS_MIN;
|
private static final double BRIGHTNESS = JagexColor.BRIGHTNESS_MIN;
|
||||||
|
|
||||||
private final TextureManager textureManager;
|
private final TextureManager textureManager;
|
||||||
private final ModelDefinition model;
|
private final ModelDefinition model;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.io.IOException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.cache.IndexType;
|
import net.runelite.cache.IndexType;
|
||||||
import net.runelite.cache.definitions.LocationsDefinition;
|
import net.runelite.cache.definitions.LocationsDefinition;
|
||||||
import net.runelite.cache.definitions.MapDefinition;
|
import net.runelite.cache.definitions.MapDefinition;
|
||||||
@@ -37,39 +38,44 @@ import net.runelite.cache.fs.Archive;
|
|||||||
import net.runelite.cache.fs.Index;
|
import net.runelite.cache.fs.Index;
|
||||||
import net.runelite.cache.fs.Storage;
|
import net.runelite.cache.fs.Storage;
|
||||||
import net.runelite.cache.fs.Store;
|
import net.runelite.cache.fs.Store;
|
||||||
import net.runelite.cache.util.XteaKeyManager;
|
import net.runelite.cache.util.KeyProvider;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class RegionLoader
|
public class RegionLoader
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RegionLoader.class);
|
|
||||||
|
|
||||||
private static final int MAX_REGION = 32768;
|
private static final int MAX_REGION = 32768;
|
||||||
|
|
||||||
private final Store store;
|
private final Store store;
|
||||||
private final Index index;
|
private final Index index;
|
||||||
private final XteaKeyManager keyManager;
|
private final KeyProvider keyProvider;
|
||||||
|
|
||||||
private final Map<Integer, Region> regions = new HashMap<>();
|
private final Map<Integer, Region> regions = new HashMap<>();
|
||||||
private Region lowestX = null, lowestY = null;
|
private Region lowestX = null, lowestY = null;
|
||||||
private Region highestX = null, highestY = null;
|
private Region highestX = null, highestY = null;
|
||||||
|
|
||||||
public RegionLoader(Store store)
|
public RegionLoader(Store store, KeyProvider keyProvider)
|
||||||
{
|
{
|
||||||
this.store = store;
|
this.store = store;
|
||||||
index = store.getIndex(IndexType.MAPS);
|
index = store.getIndex(IndexType.MAPS);
|
||||||
keyManager = new XteaKeyManager();
|
this.keyProvider = keyProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadRegions() throws IOException
|
public void loadRegions() throws IOException
|
||||||
{
|
{
|
||||||
|
if (!this.regions.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAX_REGION; ++i)
|
for (int i = 0; i < MAX_REGION; ++i)
|
||||||
{
|
{
|
||||||
Region region = this.loadRegionFromArchive(i);
|
try
|
||||||
if (region != null)
|
|
||||||
{
|
{
|
||||||
regions.put(i, region);
|
this.loadRegionFromArchive(i);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
log.debug("Can't decrypt region " + i, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,25 +103,31 @@ public class RegionLoader
|
|||||||
Region region = new Region(i);
|
Region region = new Region(i);
|
||||||
region.loadTerrain(mapDef);
|
region.loadTerrain(mapDef);
|
||||||
|
|
||||||
Integer[] keysTmp = keyManager.getKeys(i);
|
int[] keys = keyProvider.getKey(i);
|
||||||
if (keysTmp != null)
|
if (keys != null)
|
||||||
{
|
{
|
||||||
int[] keys = {keysTmp[0], keysTmp[1], keysTmp[2], keysTmp[3]};
|
data = land.decompress(storage.loadArchive(land), keys);
|
||||||
try
|
LocationsDefinition locDef = new LocationsLoader().load(x, y, data);
|
||||||
{
|
region.loadLocations(locDef);
|
||||||
data = land.decompress(storage.loadArchive(land), keys);
|
|
||||||
LocationsDefinition locDef = new LocationsLoader().load(x, y, data);
|
|
||||||
region.loadLocations(locDef);
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
logger.debug("Can't decrypt region " + i, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regions.put(i, region);
|
||||||
|
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Region loadRegion(int id, MapDefinition map, LocationsDefinition locs)
|
||||||
|
{
|
||||||
|
Region r = new Region(id);
|
||||||
|
r.loadTerrain(map);
|
||||||
|
if (locs != null)
|
||||||
|
{
|
||||||
|
r.loadLocations(locs);
|
||||||
|
}
|
||||||
|
regions.put(id, r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
public void calculateBounds()
|
public void calculateBounds()
|
||||||
{
|
{
|
||||||
for (Region region : regions.values())
|
for (Region region : regions.values())
|
||||||
@@ -154,6 +166,11 @@ public class RegionLoader
|
|||||||
return regions.get((x << 8) | y);
|
return regions.get((x << 8) | y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Region findRegionForRegionCoordinates(int x, int y)
|
||||||
|
{
|
||||||
|
return regions.get((x << 8) | y);
|
||||||
|
}
|
||||||
|
|
||||||
public Region getLowestX()
|
public Region getLowestX()
|
||||||
{
|
{
|
||||||
return lowestX;
|
return lowestX;
|
||||||
@@ -173,4 +190,4 @@ public class RegionLoader
|
|||||||
{
|
{
|
||||||
return highestY;
|
return highestY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
346
cache/src/main/java/net/runelite/cache/util/BigBufferedImage.java
vendored
Normal file
346
cache/src/main/java/net/runelite/cache/util/BigBufferedImage.java
vendored
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
package net.runelite.cache.util;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class is part of MCFS (Mission Control - Flight Software) a development
|
||||||
|
* of Team Puli Space, official Google Lunar XPRIZE contestant.
|
||||||
|
* This class is released under Creative Commons CC0.
|
||||||
|
* @author Zsolt Pocze, Dimitry Polivaev
|
||||||
|
* Please like us on facebook, and/or join our Small Step Club.
|
||||||
|
* http://www.pulispace.com
|
||||||
|
* https://www.facebook.com/pulispace
|
||||||
|
* http://nyomdmegteis.hu/en/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.color.ColorSpace;
|
||||||
|
import java.awt.image.BandedSampleModel;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.ComponentColorModel;
|
||||||
|
import java.awt.image.DataBuffer;
|
||||||
|
import java.awt.image.Raster;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.awt.image.SampleModel;
|
||||||
|
import java.awt.image.WritableRaster;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.MappedByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BigBufferedImage extends BufferedImage
|
||||||
|
{
|
||||||
|
private static final String TMP_DIR = System.getProperty("java.io.tmpdir");
|
||||||
|
private static final int MAX_PIXELS_IN_MEMORY = 1024 * 1024;
|
||||||
|
|
||||||
|
public static BufferedImage create(int width, int height, int imageType)
|
||||||
|
{
|
||||||
|
if (width * height > MAX_PIXELS_IN_MEMORY)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
final File tempDir = new File(TMP_DIR);
|
||||||
|
return createBigBufferedImage(tempDir, width, height, imageType);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new BufferedImage(width, height, imageType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage create(File inputFile, int imageType) throws IOException
|
||||||
|
{
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(inputFile))
|
||||||
|
{
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
|
||||||
|
if (readers.hasNext())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
reader.setInput(stream, true, true);
|
||||||
|
int width = reader.getWidth(reader.getMinIndex());
|
||||||
|
int height = reader.getHeight(reader.getMinIndex());
|
||||||
|
BufferedImage image = create(width, height, imageType);
|
||||||
|
int cores = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||||
|
int block = Math.min(MAX_PIXELS_IN_MEMORY / cores / width, (int) (Math.ceil(height / (double) cores)));
|
||||||
|
ExecutorService generalExecutor = Executors.newFixedThreadPool(cores);
|
||||||
|
List<Callable<ImagePartLoader>> partLoaders = new ArrayList<>();
|
||||||
|
for (int y = 0; y < height; y += block)
|
||||||
|
{
|
||||||
|
partLoaders.add(new ImagePartLoader(
|
||||||
|
y, width, Math.min(block, height - y), inputFile, image));
|
||||||
|
}
|
||||||
|
generalExecutor.invokeAll(partLoaders);
|
||||||
|
generalExecutor.shutdown();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex)
|
||||||
|
{
|
||||||
|
log.error(null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BufferedImage createBigBufferedImage(File tempDir, int width, int height, int imageType)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
FileDataBuffer buffer = new FileDataBuffer(tempDir, width * height, 4);
|
||||||
|
ColorModel colorModel;
|
||||||
|
BandedSampleModel sampleModel;
|
||||||
|
switch (imageType)
|
||||||
|
{
|
||||||
|
case TYPE_INT_RGB:
|
||||||
|
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||||
|
new int[]{8, 8, 8, 0},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
ComponentColorModel.TRANSLUCENT,
|
||||||
|
DataBuffer.TYPE_BYTE);
|
||||||
|
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 3);
|
||||||
|
break;
|
||||||
|
case TYPE_INT_ARGB:
|
||||||
|
colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||||
|
new int[]{8, 8, 8, 8},
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
ComponentColorModel.TRANSLUCENT,
|
||||||
|
DataBuffer.TYPE_BYTE);
|
||||||
|
sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported image type: " + imageType);
|
||||||
|
}
|
||||||
|
SimpleRaster raster = new SimpleRaster(sampleModel, buffer, new Point(0, 0));
|
||||||
|
BigBufferedImage image = new BigBufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ImagePartLoader implements Callable<ImagePartLoader>
|
||||||
|
{
|
||||||
|
private final int y;
|
||||||
|
private final BufferedImage image;
|
||||||
|
private final Rectangle region;
|
||||||
|
private final File file;
|
||||||
|
|
||||||
|
public ImagePartLoader(int y, int width, int height, File file, BufferedImage image)
|
||||||
|
{
|
||||||
|
this.y = y;
|
||||||
|
this.image = image;
|
||||||
|
this.file = file;
|
||||||
|
region = new Rectangle(0, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImagePartLoader call() throws Exception
|
||||||
|
{
|
||||||
|
Thread.currentThread().setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2);
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(file);)
|
||||||
|
{
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
|
||||||
|
if (readers.hasNext())
|
||||||
|
{
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
reader.setInput(stream, true, true);
|
||||||
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
|
param.setSourceRegion(region);
|
||||||
|
BufferedImage part = reader.read(0, param);
|
||||||
|
Raster source = part.getRaster();
|
||||||
|
WritableRaster target = image.getRaster();
|
||||||
|
target.setRect(0, y, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ImagePartLoader.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigBufferedImage(ColorModel cm, SimpleRaster raster, boolean isRasterPremultiplied, Hashtable<?, ?> properties)
|
||||||
|
{
|
||||||
|
super(cm, raster, isRasterPremultiplied, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
((SimpleRaster) getRaster()).dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dispose(RenderedImage image)
|
||||||
|
{
|
||||||
|
if (image instanceof BigBufferedImage)
|
||||||
|
{
|
||||||
|
((BigBufferedImage) image).dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SimpleRaster extends WritableRaster
|
||||||
|
{
|
||||||
|
public SimpleRaster(SampleModel sampleModel, FileDataBuffer dataBuffer, Point origin)
|
||||||
|
{
|
||||||
|
super(sampleModel, dataBuffer, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
((FileDataBuffer) getDataBuffer()).dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FileDataBufferDeleterHook extends Thread
|
||||||
|
{
|
||||||
|
static
|
||||||
|
{
|
||||||
|
Runtime.getRuntime().addShutdownHook(new FileDataBufferDeleterHook());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final HashSet<FileDataBuffer> undisposedBuffers = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
final FileDataBuffer[] buffers = undisposedBuffers.toArray(new FileDataBuffer[0]);
|
||||||
|
for (FileDataBuffer b : buffers)
|
||||||
|
{
|
||||||
|
b.disposeNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FileDataBuffer extends DataBuffer
|
||||||
|
{
|
||||||
|
private final String id = "buffer-" + System.currentTimeMillis() + "-" + ((int) (Math.random() * 1000));
|
||||||
|
private File dir;
|
||||||
|
private String path;
|
||||||
|
private File[] files;
|
||||||
|
private RandomAccessFile[] accessFiles;
|
||||||
|
private MappedByteBuffer[] buffer;
|
||||||
|
|
||||||
|
public FileDataBuffer(File dir, int size) throws IOException
|
||||||
|
{
|
||||||
|
super(TYPE_BYTE, size);
|
||||||
|
this.dir = dir;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileDataBuffer(File dir, int size, int numBanks) throws IOException
|
||||||
|
{
|
||||||
|
super(TYPE_BYTE, size, numBanks);
|
||||||
|
this.dir = dir;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() throws IOException
|
||||||
|
{
|
||||||
|
FileDataBufferDeleterHook.undisposedBuffers.add(this);
|
||||||
|
if (dir == null)
|
||||||
|
{
|
||||||
|
dir = new File(".");
|
||||||
|
}
|
||||||
|
if (!dir.exists())
|
||||||
|
{
|
||||||
|
throw new RuntimeException("FileDataBuffer constructor parameter dir does not exist: " + dir);
|
||||||
|
}
|
||||||
|
if (!dir.isDirectory())
|
||||||
|
{
|
||||||
|
throw new RuntimeException("FileDataBuffer constructor parameter dir is not a directory: " + dir);
|
||||||
|
}
|
||||||
|
path = dir.getPath() + "/" + id;
|
||||||
|
File subDir = new File(path);
|
||||||
|
subDir.mkdir();
|
||||||
|
buffer = new MappedByteBuffer[banks];
|
||||||
|
accessFiles = new RandomAccessFile[banks];
|
||||||
|
files = new File[banks];
|
||||||
|
for (int i = 0; i < banks; i++)
|
||||||
|
{
|
||||||
|
File file = files[i] = new File(path + "/bank" + i + ".dat");
|
||||||
|
final RandomAccessFile randomAccessFile = accessFiles[i] = new RandomAccessFile(file, "rw");
|
||||||
|
buffer[i] = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getElem(int bank, int i)
|
||||||
|
{
|
||||||
|
return buffer[bank].get(i) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setElem(int bank, int i, int val)
|
||||||
|
{
|
||||||
|
buffer[bank].put(i, (byte) val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable
|
||||||
|
{
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
new Thread()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
disposeNow();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disposeNow()
|
||||||
|
{
|
||||||
|
this.buffer = null;
|
||||||
|
if (accessFiles != null)
|
||||||
|
{
|
||||||
|
for (RandomAccessFile file : accessFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accessFiles = null;
|
||||||
|
}
|
||||||
|
if (files != null)
|
||||||
|
{
|
||||||
|
for (File file : files)
|
||||||
|
{
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
files = null;
|
||||||
|
}
|
||||||
|
if (path != null)
|
||||||
|
{
|
||||||
|
new File(path).delete();
|
||||||
|
path = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
30
cache/src/main/java/net/runelite/cache/util/KeyProvider.java
vendored
Normal file
30
cache/src/main/java/net/runelite/cache/util/KeyProvider.java
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Abex
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
public interface KeyProvider
|
||||||
|
{
|
||||||
|
int[] getKey(int region);
|
||||||
|
}
|
||||||
34
cache/src/main/java/net/runelite/cache/util/XteaKey.java
vendored
Normal file
34
cache/src/main/java/net/runelite/cache/util/XteaKey.java
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021, 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.util;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class XteaKey
|
||||||
|
{
|
||||||
|
private int region;
|
||||||
|
private int keys[];
|
||||||
|
}
|
||||||
@@ -24,25 +24,40 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.cache.util;
|
package net.runelite.cache.util;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class XteaKeyManager
|
public class XteaKeyManager implements KeyProvider
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(XteaKeyManager.class);
|
private static final Logger logger = LoggerFactory.getLogger(XteaKeyManager.class);
|
||||||
|
|
||||||
private Map<Integer, Integer[]> keys = new HashMap<>();
|
private final Map<Integer, int[]> keys = new HashMap<>();
|
||||||
|
|
||||||
public void loadKeys()
|
public void loadKeys(InputStream in)
|
||||||
{
|
{
|
||||||
keys = null;
|
// CHECKSTYLE:OFF
|
||||||
|
List<XteaKey> k = new Gson()
|
||||||
|
.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), new TypeToken<List<XteaKey>>() { }.getType());
|
||||||
|
// CHECKSTYLE:ON
|
||||||
|
|
||||||
|
for (XteaKey key : k)
|
||||||
|
{
|
||||||
|
keys.put(key.getRegion(), key.getKeys());
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("Loaded {} keys", keys.size());
|
logger.info("Loaded {} keys", keys.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer[] getKeys(int region)
|
@Override
|
||||||
|
public int[] getKey(int region)
|
||||||
{
|
{
|
||||||
return keys.get(region);
|
return keys.get(region);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ public class FrameDumper
|
|||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
Storage storage = store.getStorage();
|
Storage storage = store.getStorage();
|
||||||
Index frameIndex = store.getIndex(IndexType.FRAMES);
|
Index frameIndex = store.getIndex(IndexType.ANIMATIONS);
|
||||||
Index framemapIndex = store.getIndex(IndexType.FRAMEMAPS);
|
Index framemapIndex = store.getIndex(IndexType.SKELETONS);
|
||||||
|
|
||||||
for (Archive archive : frameIndex.getArchives())
|
for (Archive archive : frameIndex.getArchives())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class FramemapDumper
|
|||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
Storage storage = store.getStorage();
|
Storage storage = store.getStorage();
|
||||||
Index index = store.getIndex(IndexType.FRAMEMAPS);
|
Index index = store.getIndex(IndexType.SKELETONS);
|
||||||
|
|
||||||
for (Archive archive : index.getArchives())
|
for (Archive archive : index.getArchives())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class HeightMapDumperTest
|
|||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
HeightMapDumper dumper = new HeightMapDumper(store);
|
HeightMapDumper dumper = new HeightMapDumper(store);
|
||||||
dumper.load();
|
dumper.load(null);
|
||||||
|
|
||||||
BufferedImage image = dumper.drawHeightMap(0);
|
BufferedImage image = dumper.drawHeightMap(0);
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ public class MapDumperTest
|
|||||||
File base = StoreLocation.LOCATION,
|
File base = StoreLocation.LOCATION,
|
||||||
outDir = folder.newFolder();
|
outDir = folder.newFolder();
|
||||||
XteaKeyManager keyManager = new XteaKeyManager();
|
XteaKeyManager keyManager = new XteaKeyManager();
|
||||||
keyManager.loadKeys();
|
|
||||||
|
|
||||||
try (Store store = new Store(base))
|
try (Store store = new Store(base))
|
||||||
{
|
{
|
||||||
@@ -76,7 +75,7 @@ public class MapDumperTest
|
|||||||
|
|
||||||
for (int i = 0; i < MAX_REGIONS; i++)
|
for (int i = 0; i < MAX_REGIONS; i++)
|
||||||
{
|
{
|
||||||
Integer[] keysTmp = keyManager.getKeys(i);
|
int[] keys = keyManager.getKey(i);
|
||||||
|
|
||||||
int x = i >> 8;
|
int x = i >> 8;
|
||||||
int y = i & 0xFF;
|
int y = i & 0xFF;
|
||||||
@@ -95,9 +94,8 @@ public class MapDumperTest
|
|||||||
|
|
||||||
Files.write(data, new File(outDir, "m" + x + "_" + y + ".dat"));
|
Files.write(data, new File(outDir, "m" + x + "_" + y + ".dat"));
|
||||||
|
|
||||||
if (keysTmp != null)
|
if (keys != null)
|
||||||
{
|
{
|
||||||
int[] keys = {keysTmp[0], keysTmp[1], keysTmp[2], keysTmp[3]};
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data = land.decompress(storage.loadArchive(land), keys);
|
data = land.decompress(storage.loadArchive(land), keys);
|
||||||
@@ -122,7 +120,6 @@ public class MapDumperTest
|
|||||||
Storage storage = store.getStorage();
|
Storage storage = store.getStorage();
|
||||||
Index index = store.getIndex(IndexType.MAPS);
|
Index index = store.getIndex(IndexType.MAPS);
|
||||||
XteaKeyManager keyManager = new XteaKeyManager();
|
XteaKeyManager keyManager = new XteaKeyManager();
|
||||||
keyManager.loadKeys();
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_REGIONS; ++i)
|
for (int i = 0; i < MAX_REGIONS; ++i)
|
||||||
{
|
{
|
||||||
@@ -143,10 +140,9 @@ public class MapDumperTest
|
|||||||
MapDefinition mapDef = new MapLoader().load(x, y, data);
|
MapDefinition mapDef = new MapLoader().load(x, y, data);
|
||||||
LocationsDefinition locDef = null;
|
LocationsDefinition locDef = null;
|
||||||
|
|
||||||
Integer[] keysTmp = keyManager.getKeys(i);
|
int[] keys = keyManager.getKey(i);
|
||||||
if (keysTmp != null)
|
if (keys != null)
|
||||||
{
|
{
|
||||||
int[] keys = {keysTmp[0], keysTmp[1], keysTmp[2], keysTmp[3]};
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data = land.decompress(storage.loadArchive(land), keys);
|
data = land.decompress(storage.loadArchive(land), keys);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import javax.imageio.ImageIO;
|
|||||||
import net.runelite.cache.fs.Store;
|
import net.runelite.cache.fs.Store;
|
||||||
import net.runelite.cache.region.Region;
|
import net.runelite.cache.region.Region;
|
||||||
import net.runelite.cache.region.RegionLoader;
|
import net.runelite.cache.region.RegionLoader;
|
||||||
|
import net.runelite.cache.util.XteaKeyManager;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -56,7 +57,10 @@ public class MapImageDumperTest
|
|||||||
{
|
{
|
||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
MapImageDumper dumper = new MapImageDumper(store);
|
XteaKeyManager keyManager = new XteaKeyManager();
|
||||||
|
keyManager.loadKeys(null);
|
||||||
|
|
||||||
|
MapImageDumper dumper = new MapImageDumper(store, keyManager);
|
||||||
dumper.load();
|
dumper.load();
|
||||||
|
|
||||||
for (int i = 0; i < Region.Z; ++i)
|
for (int i = 0; i < Region.Z; ++i)
|
||||||
@@ -82,10 +86,13 @@ public class MapImageDumperTest
|
|||||||
{
|
{
|
||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
RegionLoader regionLoader = new RegionLoader(store);
|
XteaKeyManager keyManager = new XteaKeyManager();
|
||||||
|
keyManager.loadKeys(null);
|
||||||
|
|
||||||
|
RegionLoader regionLoader = new RegionLoader(store, keyManager);
|
||||||
regionLoader.loadRegions();
|
regionLoader.loadRegions();
|
||||||
|
|
||||||
MapImageDumper dumper = new MapImageDumper(store);
|
MapImageDumper dumper = new MapImageDumper(store, regionLoader);
|
||||||
dumper.load();
|
dumper.load();
|
||||||
|
|
||||||
int z = 0;
|
int z = 0;
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ public class TrackDumperTest
|
|||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
Storage storage = store.getStorage();
|
Storage storage = store.getStorage();
|
||||||
Index index = store.getIndex(IndexType.TRACK1);
|
Index index = store.getIndex(IndexType.MUSIC_TRACKS);
|
||||||
Index index2 = store.getIndex(IndexType.TRACK2);
|
Index index2 = store.getIndex(IndexType.MUSIC_JINGLES);
|
||||||
|
|
||||||
for (Archive archive : index.getArchives())
|
for (Archive archive : index.getArchives())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class WorldMapDumperTest
|
|||||||
store.load();
|
store.load();
|
||||||
|
|
||||||
Storage storage = store.getStorage();
|
Storage storage = store.getStorage();
|
||||||
Index index = store.getIndex(IndexType.WORLDMAP);
|
Index index = store.getIndex(IndexType.WORLDMAP_OLD);
|
||||||
Archive archive = index.getArchive(0); // there is also archive 1/2, but their data format is not this
|
Archive archive = index.getArchive(0); // there is also archive 1/2, but their data format is not this
|
||||||
|
|
||||||
byte[] archiveData = storage.loadArchive(archive);
|
byte[] archiveData = storage.loadArchive(archive);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import org.junit.Test;
|
|||||||
public class JagexColorTest
|
public class JagexColorTest
|
||||||
{
|
{
|
||||||
private static final double[] BRIGHTNESS_LEVELS = {
|
private static final double[] BRIGHTNESS_LEVELS = {
|
||||||
JagexColor.BRIGTHNESS_MIN,
|
JagexColor.BRIGHTNESS_MIN,
|
||||||
JagexColor.BRIGHTNESS_LOW,
|
JagexColor.BRIGHTNESS_LOW,
|
||||||
JagexColor.BRIGHTNESS_HIGH,
|
JagexColor.BRIGHTNESS_HIGH,
|
||||||
JagexColor.BRIGHTNESS_MAX,
|
JagexColor.BRIGHTNESS_MAX,
|
||||||
|
|||||||
@@ -1246,6 +1246,17 @@ public interface Client extends OAuthApi, GameEngine
|
|||||||
*/
|
*/
|
||||||
void setMusicVolume(int volume);
|
void setMusicVolume(int volume);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the current {@link #getMusicCurrentTrackId()} is a Jingle, otherwise its a Track
|
||||||
|
*/
|
||||||
|
boolean isPlayingJingle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Currently playing music/jingle id, or -1 if not playing
|
||||||
|
* @see #isPlayingJingle()
|
||||||
|
*/
|
||||||
|
int getMusicCurrentTrackId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play a sound effect at the player's current location. This is how UI,
|
* Play a sound effect at the player's current location. This is how UI,
|
||||||
* and player-generated (e.g. mining, woodcutting) sound effects are
|
* and player-generated (e.g. mining, woodcutting) sound effects are
|
||||||
@@ -2111,6 +2122,12 @@ public interface Client extends OAuthApi, GameEngine
|
|||||||
*/
|
*/
|
||||||
NodeCache getItemCompositionCache();
|
NodeCache getItemCompositionCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns client object composition cache
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
NodeCache getObjectCompositionCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the array of cross sprites that appear and animate when left-clicking
|
* Returns the array of cross sprites that appear and animate when left-clicking
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -56,12 +56,26 @@ public interface ObjectComposition extends ParamHolder
|
|||||||
*/
|
*/
|
||||||
int getMapSceneId();
|
int getMapSceneId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the map scene index into the {@link Client#getMapScene()}
|
||||||
|
* array, or -1 if it has no map scene icon
|
||||||
|
* @param mapSceneId
|
||||||
|
*/
|
||||||
|
void setMapSceneId(int mapSceneId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the index of this object in the {@link Client#getMapIcons()}
|
* Gets the index of this object in the {@link Client#getMapIcons()}
|
||||||
* array, or -1 if it has no full map icon
|
* array, or -1 if it has no full map icon
|
||||||
*/
|
*/
|
||||||
int getMapIconId();
|
int getMapIconId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the index of the object in the {@link Client#getMapIcons()}
|
||||||
|
* array, or -1 if it has no map icon
|
||||||
|
* @param mapIconId
|
||||||
|
*/
|
||||||
|
void setMapIconId(int mapIconId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link ObjectID}s of objects this can transform into, depending
|
* Get the {@link ObjectID}s of objects this can transform into, depending
|
||||||
* on a {@link Varbits} or {@link VarPlayer}
|
* on a {@link Varbits} or {@link VarPlayer}
|
||||||
|
|||||||
@@ -747,19 +747,13 @@ public class Perspective
|
|||||||
|
|
||||||
private static SimplePolygon calculateAABB(Client client, Model m, int jauOrient, int x, int y, int z)
|
private static SimplePolygon calculateAABB(Client client, Model m, int jauOrient, int x, int y, int z)
|
||||||
{
|
{
|
||||||
int ex = m.getExtremeX();
|
m.calculateExtreme(jauOrient);
|
||||||
if (ex == -1)
|
|
||||||
{
|
|
||||||
// dynamic models don't get stored when they render where this normally happens
|
|
||||||
m.calculateBoundsCylinder();
|
|
||||||
m.calculateExtreme(0);
|
|
||||||
ex = m.getExtremeX();
|
|
||||||
}
|
|
||||||
|
|
||||||
int x1 = m.getCenterX();
|
int x1 = m.getCenterX();
|
||||||
int y1 = m.getCenterZ();
|
int y1 = m.getCenterZ();
|
||||||
int z1 = m.getCenterY();
|
int z1 = m.getCenterY();
|
||||||
|
|
||||||
|
int ex = m.getExtremeX();
|
||||||
int ey = m.getExtremeZ();
|
int ey = m.getExtremeZ();
|
||||||
int ez = m.getExtremeY();
|
int ez = m.getExtremeY();
|
||||||
|
|
||||||
@@ -787,7 +781,7 @@ public class Perspective
|
|||||||
int[] x2d = new int[8];
|
int[] x2d = new int[8];
|
||||||
int[] y2d = new int[8];
|
int[] y2d = new int[8];
|
||||||
|
|
||||||
modelToCanvas(client, 8, x, y, z, jauOrient, xa, ya, za, x2d, y2d);
|
modelToCanvasCpu(client, 8, x, y, z, 0, xa, ya, za, x2d, y2d);
|
||||||
|
|
||||||
return Jarvis.convexHull(x2d, y2d);
|
return Jarvis.convexHull(x2d, y2d);
|
||||||
}
|
}
|
||||||
@@ -798,7 +792,7 @@ public class Perspective
|
|||||||
int[] y2d = new int[m.getVerticesCount()];
|
int[] y2d = new int[m.getVerticesCount()];
|
||||||
final int[] faceColors3 = m.getFaceColors3();
|
final int[] faceColors3 = m.getFaceColors3();
|
||||||
|
|
||||||
Perspective.modelToCanvas(client,
|
Perspective.modelToCanvasCpu(client,
|
||||||
m.getVerticesCount(),
|
m.getVerticesCount(),
|
||||||
x, y, z,
|
x, y, z,
|
||||||
jauOrient,
|
jauOrient,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public final class SpriteID
|
|||||||
public static final int SPELL_ENFEEBLE = 57;
|
public static final int SPELL_ENFEEBLE = 57;
|
||||||
public static final int SPELL_STUN = 58;
|
public static final int SPELL_STUN = 58;
|
||||||
public static final int SPELL_FLAMES_OF_ZAMORAK = 59;
|
public static final int SPELL_FLAMES_OF_ZAMORAK = 59;
|
||||||
public static final int SPELL_CLAWS_OF_GUTHIC = 60;
|
public static final int SPELL_CLAWS_OF_GUTHIX = 60;
|
||||||
public static final int SPELL_SARADOMIN_STRIKE = 61;
|
public static final int SPELL_SARADOMIN_STRIKE = 61;
|
||||||
public static final int UNUSED_SPELL_CALL_ANIMAL = 62;
|
public static final int UNUSED_SPELL_CALL_ANIMAL = 62;
|
||||||
public static final int UNUSED_SPELL_RAISE_SKELETON = 63;
|
public static final int UNUSED_SPELL_RAISE_SKELETON = 63;
|
||||||
@@ -139,7 +139,7 @@ public final class SpriteID
|
|||||||
public static final int SPELL_ENFEEBLE_DISABLED = 107;
|
public static final int SPELL_ENFEEBLE_DISABLED = 107;
|
||||||
public static final int SPELL_STUN_DISABLED = 108;
|
public static final int SPELL_STUN_DISABLED = 108;
|
||||||
public static final int SPELL_FLAMES_OF_ZAMORAK_DISABLED = 109;
|
public static final int SPELL_FLAMES_OF_ZAMORAK_DISABLED = 109;
|
||||||
public static final int SPELL_CLAWS_OF_GUTHIC_DISABLED = 110;
|
public static final int SPELL_CLAWS_OF_GUTHIX_DISABLED = 110;
|
||||||
public static final int SPELL_SARADOMIN_STRIKE_DISABLED = 111;
|
public static final int SPELL_SARADOMIN_STRIKE_DISABLED = 111;
|
||||||
public static final int UNUSED_SPELL_CALL_ANIMAL_DISABLED = 112;
|
public static final int UNUSED_SPELL_CALL_ANIMAL_DISABLED = 112;
|
||||||
public static final int UNUSED_SPELL_RAISE_SKELETON_DISABLED = 113;
|
public static final int UNUSED_SPELL_RAISE_SKELETON_DISABLED = 113;
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api.events;
|
package net.runelite.api.events;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Value;
|
||||||
import net.runelite.api.ItemComposition;
|
import net.runelite.api.ItemComposition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event called after a new {@link ItemComposition} is created and
|
* An event called after a new {@link ItemComposition} is created and
|
||||||
* its data is initialized.
|
* its data is initialized.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Value
|
||||||
public class PostItemComposition
|
public class PostItemComposition
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.api.events;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
import net.runelite.api.ObjectComposition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event called after a new {@link ObjectComposition} is created and
|
||||||
|
* its data is initialized.
|
||||||
|
*/
|
||||||
|
@Value
|
||||||
|
public class PostObjectComposition
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The newly created object
|
||||||
|
*/
|
||||||
|
ObjectComposition objectComposition;
|
||||||
|
}
|
||||||
@@ -856,6 +856,7 @@ public final class WidgetID
|
|||||||
static class LunarSpellBook
|
static class LunarSpellBook
|
||||||
{
|
{
|
||||||
static final int LUNAR_HOME_TELEPORT = 101;
|
static final int LUNAR_HOME_TELEPORT = 101;
|
||||||
|
static final int FERTILE_SOIL = 126;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ArceuusSpellBook
|
static class ArceuusSpellBook
|
||||||
|
|||||||
@@ -507,6 +507,7 @@ public enum WidgetInfo
|
|||||||
SPELL_ARCEUUS_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.ArceuusSpellBook.ARCEUUS_HOME_TELEPORT),
|
SPELL_ARCEUUS_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.ArceuusSpellBook.ARCEUUS_HOME_TELEPORT),
|
||||||
SPELL_KOUREND_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.KOUREND_HOME_TELEPORT),
|
SPELL_KOUREND_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.KOUREND_HOME_TELEPORT),
|
||||||
SPELL_CATHERBY_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.CATHERBY_HOME_TELEPORT),
|
SPELL_CATHERBY_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.CATHERBY_HOME_TELEPORT),
|
||||||
|
SPELL_LUNAR_FERTILE_SOIL(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.LunarSpellBook.FERTILE_SOIL),
|
||||||
|
|
||||||
PVP_WILDERNESS_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.WILDERNESS_SKULL_CONTAINER),
|
PVP_WILDERNESS_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.WILDERNESS_SKULL_CONTAINER),
|
||||||
PVP_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER),
|
PVP_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER),
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ package net.runelite.client.plugins.chatcommands;
|
|||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
@@ -90,6 +91,7 @@ import net.runelite.client.hiscore.Skill;
|
|||||||
import net.runelite.client.input.KeyManager;
|
import net.runelite.client.input.KeyManager;
|
||||||
import net.runelite.client.plugins.Plugin;
|
import net.runelite.client.plugins.Plugin;
|
||||||
import net.runelite.client.plugins.PluginDescriptor;
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.util.AsyncBufferedImage;
|
||||||
import net.runelite.client.util.ImageUtil;
|
import net.runelite.client.util.ImageUtil;
|
||||||
import net.runelite.client.util.QuantityFormatter;
|
import net.runelite.client.util.QuantityFormatter;
|
||||||
import net.runelite.client.util.Text;
|
import net.runelite.client.util.Text;
|
||||||
@@ -106,7 +108,7 @@ import org.apache.commons.text.WordUtils;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ChatCommandsPlugin extends Plugin
|
public class ChatCommandsPlugin extends Plugin
|
||||||
{
|
{
|
||||||
private static final Pattern KILLCOUNT_PATTERN = Pattern.compile("Your (?:completion count for |subdued |completed )?(.+?) (?:(?:kill|harvest|lap|completion) )?(?:count )?is: <col=ff0000>(\\d+)</col>");
|
private static final Pattern KILLCOUNT_PATTERN = Pattern.compile("Your (?<pre>completion count for |subdued |completed )?(?<boss>.+?) (?<post>(?:(?:kill|harvest|lap|completion) )?(?:count )?)is: <col=ff0000>(?<kc>\\d+)</col>");
|
||||||
private static final String TEAM_SIZES = "(?<teamsize>\\d+(?:\\+|-\\d+)? players?|Solo)";
|
private static final String TEAM_SIZES = "(?<teamsize>\\d+(?:\\+|-\\d+)? players?|Solo)";
|
||||||
private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(new personal best\\)</col>");
|
private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(new personal best\\)</col>");
|
||||||
private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>[0-9:.]+</col> Personal best: </col><col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col>");
|
private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>[0-9:.]+</col> Personal best: </col><col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col>");
|
||||||
@@ -220,7 +222,15 @@ public class ChatCommandsPlugin extends Plugin
|
|||||||
chatCommandManager.registerCommandAsync(SOUL_WARS_ZEAL_COMMAND, this::soulWarsZealLookup);
|
chatCommandManager.registerCommandAsync(SOUL_WARS_ZEAL_COMMAND, this::soulWarsZealLookup);
|
||||||
chatCommandManager.registerCommandAsync(PET_LIST_COMMAND, this::petListLookup, this::petListSubmit);
|
chatCommandManager.registerCommandAsync(PET_LIST_COMMAND, this::petListLookup, this::petListSubmit);
|
||||||
|
|
||||||
clientThread.invoke(this::loadPetIcons);
|
clientThread.invoke(() ->
|
||||||
|
{
|
||||||
|
if (client.getModIcons() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
loadPetIcons();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -295,13 +305,14 @@ public class ChatCommandsPlugin extends Plugin
|
|||||||
|
|
||||||
private void loadPetIcons()
|
private void loadPetIcons()
|
||||||
{
|
{
|
||||||
final IndexedSprite[] modIcons = client.getModIcons();
|
if (modIconIdx != -1)
|
||||||
if (modIconIdx != -1 || modIcons == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Pet[] pets = Pet.values();
|
final Pet[] pets = Pet.values();
|
||||||
|
final IndexedSprite[] modIcons = client.getModIcons();
|
||||||
|
assert modIcons != null;
|
||||||
final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + pets.length);
|
final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + pets.length);
|
||||||
modIconIdx = modIcons.length;
|
modIconIdx = modIcons.length;
|
||||||
|
|
||||||
@@ -309,9 +320,16 @@ public class ChatCommandsPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
final Pet pet = pets[i];
|
final Pet pet = pets[i];
|
||||||
|
|
||||||
final BufferedImage image = ImageUtil.resizeImage(itemManager.getImage(pet.getIconID()), 18, 16);
|
final AsyncBufferedImage abi = itemManager.getImage(pet.getIconID());
|
||||||
final IndexedSprite sprite = ImageUtil.getImageIndexedSprite(image, client);
|
final int idx = modIconIdx + i;
|
||||||
newModIcons[modIconIdx + i] = sprite;
|
Runnable r = () ->
|
||||||
|
{
|
||||||
|
final BufferedImage image = ImageUtil.resizeImage(abi, 18, 16);
|
||||||
|
final IndexedSprite sprite = ImageUtil.getImageIndexedSprite(image, client);
|
||||||
|
newModIcons[idx] = sprite;
|
||||||
|
};
|
||||||
|
abi.onLoaded(r);
|
||||||
|
r.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
client.setModIcons(newModIcons);
|
client.setModIcons(newModIcons);
|
||||||
@@ -371,8 +389,16 @@ public class ChatCommandsPlugin extends Plugin
|
|||||||
Matcher matcher = KILLCOUNT_PATTERN.matcher(message);
|
Matcher matcher = KILLCOUNT_PATTERN.matcher(message);
|
||||||
if (matcher.find())
|
if (matcher.find())
|
||||||
{
|
{
|
||||||
String boss = matcher.group(1);
|
final String boss = matcher.group("boss");
|
||||||
int kc = Integer.parseInt(matcher.group(2));
|
final int kc = Integer.parseInt(matcher.group("kc"));
|
||||||
|
final String pre = matcher.group("pre");
|
||||||
|
final String post = matcher.group("post");
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(pre) && Strings.isNullOrEmpty(post))
|
||||||
|
{
|
||||||
|
unsetKc(boss);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String renamedBoss = KILLCOUNT_RENAMES
|
String renamedBoss = KILLCOUNT_RENAMES
|
||||||
.getOrDefault(boss, boss)
|
.getOrDefault(boss, boss)
|
||||||
@@ -784,9 +810,6 @@ public class ChatCommandsPlugin extends Plugin
|
|||||||
case HOPPING:
|
case HOPPING:
|
||||||
pohOwner = null;
|
pohOwner = null;
|
||||||
break;
|
break;
|
||||||
case LOGGED_IN:
|
|
||||||
loadPetIcons();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,4 +164,15 @@ public interface ChatFilterConfig extends Config
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "stripAccents",
|
||||||
|
name = "Strip accents",
|
||||||
|
description = "Remove accents before applying filters",
|
||||||
|
position = 13
|
||||||
|
)
|
||||||
|
default boolean stripAccents()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ public class ChatFilterPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
String strippedMessage = jagexPrintableCharMatcher.retainFrom(message)
|
String strippedMessage = jagexPrintableCharMatcher.retainFrom(message)
|
||||||
.replace('\u00A0', ' ');
|
.replace('\u00A0', ' ');
|
||||||
String strippedAccents = StringUtils.stripAccents(strippedMessage);
|
String strippedAccents = stripAccents(strippedMessage);
|
||||||
assert strippedMessage.length() == strippedAccents.length();
|
assert strippedMessage.length() == strippedAccents.length();
|
||||||
|
|
||||||
if (username != null && shouldFilterByName(username))
|
if (username != null && shouldFilterByName(username))
|
||||||
@@ -377,23 +377,28 @@ public class ChatFilterPlugin extends Plugin
|
|||||||
filteredNamePatterns.clear();
|
filteredNamePatterns.clear();
|
||||||
|
|
||||||
Text.fromCSV(config.filteredWords()).stream()
|
Text.fromCSV(config.filteredWords()).stream()
|
||||||
.map(StringUtils::stripAccents)
|
.map(this::stripAccents)
|
||||||
.map(s -> Pattern.compile(Pattern.quote(s), Pattern.CASE_INSENSITIVE))
|
.map(s -> Pattern.compile(Pattern.quote(s), Pattern.CASE_INSENSITIVE))
|
||||||
.forEach(filteredPatterns::add);
|
.forEach(filteredPatterns::add);
|
||||||
|
|
||||||
NEWLINE_SPLITTER.splitToList(config.filteredRegex()).stream()
|
NEWLINE_SPLITTER.splitToList(config.filteredRegex()).stream()
|
||||||
.map(StringUtils::stripAccents)
|
.map(this::stripAccents)
|
||||||
.map(ChatFilterPlugin::compilePattern)
|
.map(ChatFilterPlugin::compilePattern)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEach(filteredPatterns::add);
|
.forEach(filteredPatterns::add);
|
||||||
|
|
||||||
NEWLINE_SPLITTER.splitToList(config.filteredNames()).stream()
|
NEWLINE_SPLITTER.splitToList(config.filteredNames()).stream()
|
||||||
.map(StringUtils::stripAccents)
|
.map(this::stripAccents)
|
||||||
.map(ChatFilterPlugin::compilePattern)
|
.map(ChatFilterPlugin::compilePattern)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEach(filteredNamePatterns::add);
|
.forEach(filteredNamePatterns::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String stripAccents(String input)
|
||||||
|
{
|
||||||
|
return config.stripAccents() ? StringUtils.stripAccents(input) : input;
|
||||||
|
}
|
||||||
|
|
||||||
private static Pattern compilePattern(String pattern)
|
private static Pattern compilePattern(String pattern)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -70,7 +70,20 @@ public class ClueScrollOverlay extends OverlayPanel
|
|||||||
item(KANDARIN_HEADGEAR_4),
|
item(KANDARIN_HEADGEAR_4),
|
||||||
item(BRUMA_TORCH),
|
item(BRUMA_TORCH),
|
||||||
item(MAX_CAPE),
|
item(MAX_CAPE),
|
||||||
item(MAX_CAPE_13342));
|
item(MAX_CAPE_13342),
|
||||||
|
item(ABYSSAL_LANTERN_NORMAL_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_BLUE_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_RED_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_WHITE_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_PURPLE_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_GREEN_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_OAK_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_WILLOW_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_MAPLE_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_YEW_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_BLISTERWOOD_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_MAGIC_LOGS),
|
||||||
|
item(ABYSSAL_LANTERN_REDWOOD_LOGS));
|
||||||
|
|
||||||
public static final Color TITLED_CONTENT_COLOR = new Color(190, 190, 190);
|
public static final Color TITLED_CONTENT_COLOR = new Color(190, 190, 190);
|
||||||
|
|
||||||
|
|||||||
@@ -230,8 +230,8 @@ public class AnagramClue extends ClueScroll implements TextClueScroll, NpcClueSc
|
|||||||
.build(),
|
.build(),
|
||||||
AnagramClue.builder()
|
AnagramClue.builder()
|
||||||
.text("DEKAGRAM")
|
.text("DEKAGRAM")
|
||||||
.npc("Dark mage")
|
.npc("Dark Mage")
|
||||||
.location(new WorldPoint(3039, 4835, 0))
|
.location(new WorldPoint(3039, 4834, 0))
|
||||||
.area("Centre of the Abyss")
|
.area("Centre of the Abyss")
|
||||||
.question("How many rifts are found here in the abyss?")
|
.question("How many rifts are found here in the abyss?")
|
||||||
.answer("13")
|
.answer("13")
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import net.runelite.api.IndexedSprite;
|
|||||||
import net.runelite.api.MessageNode;
|
import net.runelite.api.MessageNode;
|
||||||
import net.runelite.api.Player;
|
import net.runelite.api.Player;
|
||||||
import net.runelite.api.events.ChatMessage;
|
import net.runelite.api.events.ChatMessage;
|
||||||
import net.runelite.api.events.GameStateChanged;
|
|
||||||
import net.runelite.api.events.OverheadTextChanged;
|
import net.runelite.api.events.OverheadTextChanged;
|
||||||
import net.runelite.client.callback.ClientThread;
|
import net.runelite.client.callback.ClientThread;
|
||||||
import net.runelite.client.eventbus.Subscribe;
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
@@ -67,27 +66,27 @@ public class EmojiPlugin extends Plugin
|
|||||||
@Override
|
@Override
|
||||||
protected void startUp()
|
protected void startUp()
|
||||||
{
|
{
|
||||||
clientThread.invoke(this::loadEmojiIcons);
|
clientThread.invoke(() ->
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onGameStateChanged(GameStateChanged gameStateChanged)
|
|
||||||
{
|
|
||||||
if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
|
|
||||||
{
|
{
|
||||||
|
if (client.getModIcons() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
loadEmojiIcons();
|
loadEmojiIcons();
|
||||||
}
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadEmojiIcons()
|
private void loadEmojiIcons()
|
||||||
{
|
{
|
||||||
final IndexedSprite[] modIcons = client.getModIcons();
|
if (modIconsStart != -1)
|
||||||
if (modIconsStart != -1 || modIcons == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Emoji[] emojis = Emoji.values();
|
final Emoji[] emojis = Emoji.values();
|
||||||
|
final IndexedSprite[] modIcons = client.getModIcons();
|
||||||
|
assert modIcons != null;
|
||||||
final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + emojis.length);
|
final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + emojis.length);
|
||||||
modIconsStart = modIcons.length;
|
modIconsStart = modIcons.length;
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import net.runelite.api.IndexedSprite;
|
|||||||
import net.runelite.api.MenuAction;
|
import net.runelite.api.MenuAction;
|
||||||
import net.runelite.api.Nameable;
|
import net.runelite.api.Nameable;
|
||||||
import net.runelite.api.ScriptID;
|
import net.runelite.api.ScriptID;
|
||||||
import net.runelite.api.events.GameStateChanged;
|
|
||||||
import net.runelite.api.events.MenuEntryAdded;
|
import net.runelite.api.events.MenuEntryAdded;
|
||||||
import net.runelite.api.events.NameableNameChanged;
|
import net.runelite.api.events.NameableNameChanged;
|
||||||
import net.runelite.api.events.RemovedFriend;
|
import net.runelite.api.events.RemovedFriend;
|
||||||
@@ -116,7 +115,15 @@ public class FriendNotesPlugin extends Plugin
|
|||||||
protected void startUp() throws Exception
|
protected void startUp() throws Exception
|
||||||
{
|
{
|
||||||
overlayManager.add(overlay);
|
overlayManager.add(overlay);
|
||||||
clientThread.invoke(this::loadIcon);
|
clientThread.invoke(() ->
|
||||||
|
{
|
||||||
|
if (client.getModIcons() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
loadIcon();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
if (client.getGameState() == GameState.LOGGED_IN)
|
if (client.getGameState() == GameState.LOGGED_IN)
|
||||||
{
|
{
|
||||||
rebuildFriendsList();
|
rebuildFriendsList();
|
||||||
@@ -135,15 +142,6 @@ public class FriendNotesPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onGameStateChanged(GameStateChanged event)
|
|
||||||
{
|
|
||||||
if (event.getGameState() == GameState.LOGGED_IN)
|
|
||||||
{
|
|
||||||
loadIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onConfigChanged(ConfigChanged event)
|
public void onConfigChanged(ConfigChanged event)
|
||||||
{
|
{
|
||||||
@@ -380,8 +378,7 @@ public class FriendNotesPlugin extends Plugin
|
|||||||
|
|
||||||
private void loadIcon()
|
private void loadIcon()
|
||||||
{
|
{
|
||||||
final IndexedSprite[] modIcons = client.getModIcons();
|
if (iconIdx != -1)
|
||||||
if (iconIdx != -1 || modIcons == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -394,6 +391,8 @@ public class FriendNotesPlugin extends Plugin
|
|||||||
|
|
||||||
final BufferedImage resized = ImageUtil.resizeImage(iconImg, ICON_WIDTH, ICON_HEIGHT);
|
final BufferedImage resized = ImageUtil.resizeImage(iconImg, ICON_WIDTH, ICON_HEIGHT);
|
||||||
|
|
||||||
|
final IndexedSprite[] modIcons = client.getModIcons();
|
||||||
|
assert modIcons != null;
|
||||||
final IndexedSprite[] newIcons = Arrays.copyOf(modIcons, modIcons.length + 1);
|
final IndexedSprite[] newIcons = Arrays.copyOf(modIcons, modIcons.length + 1);
|
||||||
newIcons[newIcons.length - 1] = ImageUtil.getImageIndexedSprite(resized, client);
|
newIcons[newIcons.length - 1] = ImageUtil.getImageIndexedSprite(resized, client);
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public enum MagicAction implements SkillAction
|
|||||||
FIRE_BLAST("Fire Blast", 59, 34.5f, SpriteID.SPELL_FIRE_BLAST),
|
FIRE_BLAST("Fire Blast", 59, 34.5f, SpriteID.SPELL_FIRE_BLAST),
|
||||||
MARK_OF_DARKNESS("Mark of Darkness", 59, 70, SpriteID.SPELL_MARK_OF_DARKNESS),
|
MARK_OF_DARKNESS("Mark of Darkness", 59, 70, SpriteID.SPELL_MARK_OF_DARKNESS),
|
||||||
SENNTISTEN_TELEPORT("Senntisten Teleport", 60, 70, SpriteID.SPELL_SENNTISTEN_TELEPORT),
|
SENNTISTEN_TELEPORT("Senntisten Teleport", 60, 70, SpriteID.SPELL_SENNTISTEN_TELEPORT),
|
||||||
CLAWS_OF_GUTHIX("Claws Of Guthix", 60, 35, SpriteID.SPELL_CLAWS_OF_GUTHIC),
|
CLAWS_OF_GUTHIX("Claws Of Guthix", 60, 35, SpriteID.SPELL_CLAWS_OF_GUTHIX),
|
||||||
FLAMES_OF_ZAMORAK("Flames Of Zamorak", 60, 35, SpriteID.SPELL_FLAMES_OF_ZAMORAK),
|
FLAMES_OF_ZAMORAK("Flames Of Zamorak", 60, 35, SpriteID.SPELL_FLAMES_OF_ZAMORAK),
|
||||||
SARADOMIN_STRIKE("Saradomin Strike", 60, 35, SpriteID.SPELL_SARADOMIN_STRIKE),
|
SARADOMIN_STRIKE("Saradomin Strike", 60, 35, SpriteID.SPELL_SARADOMIN_STRIKE),
|
||||||
CHARGE_EARTH_ORB("Charge Earth Orb", 60, 70, SpriteID.SPELL_CHARGE_EARTH_ORB),
|
CHARGE_EARTH_ORB("Charge Earth Orb", 60, 70, SpriteID.SPELL_CHARGE_EARTH_ORB),
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.MenuEntry;
|
import net.runelite.api.MenuEntry;
|
||||||
@@ -39,8 +39,8 @@ import net.runelite.api.Point;
|
|||||||
import net.runelite.api.Prayer;
|
import net.runelite.api.Prayer;
|
||||||
import net.runelite.api.Skill;
|
import net.runelite.api.Skill;
|
||||||
import net.runelite.api.SpriteID;
|
import net.runelite.api.SpriteID;
|
||||||
import net.runelite.api.Varbits;
|
|
||||||
import net.runelite.api.VarPlayer;
|
import net.runelite.api.VarPlayer;
|
||||||
|
import net.runelite.api.Varbits;
|
||||||
import net.runelite.api.widgets.Widget;
|
import net.runelite.api.widgets.Widget;
|
||||||
import net.runelite.api.widgets.WidgetInfo;
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
import net.runelite.client.game.AlternateSprites;
|
import net.runelite.client.game.AlternateSprites;
|
||||||
@@ -86,10 +86,10 @@ class StatusBarsOverlay extends Overlay
|
|||||||
private final SpriteManager spriteManager;
|
private final SpriteManager spriteManager;
|
||||||
|
|
||||||
private final Image prayerIcon;
|
private final Image prayerIcon;
|
||||||
|
private final Image heartDisease;
|
||||||
|
private final Image heartPoison;
|
||||||
|
private final Image heartVenom;
|
||||||
private Image heartIcon;
|
private Image heartIcon;
|
||||||
private Image heartDisease;
|
|
||||||
private Image heartPoison;
|
|
||||||
private Image heartVenom;
|
|
||||||
private Image specialIcon;
|
private Image specialIcon;
|
||||||
private Image energyIcon;
|
private Image energyIcon;
|
||||||
private final Map<BarMode, BarRenderer> barRenderers = new EnumMap<>(BarMode.class);
|
private final Map<BarMode, BarRenderer> barRenderers = new EnumMap<>(BarMode.class);
|
||||||
@@ -106,6 +106,10 @@ class StatusBarsOverlay extends Overlay
|
|||||||
this.spriteManager = spriteManager;
|
this.spriteManager = spriteManager;
|
||||||
|
|
||||||
prayerIcon = ImageUtil.resizeCanvas(ImageUtil.resizeImage(skillIconManager.getSkillImage(Skill.PRAYER, true), IMAGE_SIZE, IMAGE_SIZE), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
prayerIcon = ImageUtil.resizeCanvas(ImageUtil.resizeImage(skillIconManager.getSkillImage(Skill.PRAYER, true), IMAGE_SIZE, IMAGE_SIZE), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
||||||
|
heartDisease = ImageUtil.resizeCanvas(ImageUtil.loadImageResource(AlternateSprites.class, AlternateSprites.DISEASE_HEART), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
||||||
|
heartPoison = ImageUtil.resizeCanvas(ImageUtil.loadImageResource(AlternateSprites.class, AlternateSprites.POISON_HEART), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
||||||
|
heartVenom = ImageUtil.resizeCanvas(ImageUtil.loadImageResource(AlternateSprites.class, AlternateSprites.VENOM_HEART), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
||||||
|
|
||||||
initRenderers();
|
initRenderers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,16 +325,28 @@ class StatusBarsOverlay extends Overlay
|
|||||||
|
|
||||||
private void buildIcons()
|
private void buildIcons()
|
||||||
{
|
{
|
||||||
if (heartIcon != null && heartDisease != null && heartPoison != null && heartVenom != null && energyIcon != null && specialIcon != null)
|
if (heartIcon == null)
|
||||||
{
|
{
|
||||||
return;
|
heartIcon = loadAndResize(SpriteID.MINIMAP_ORB_HITPOINTS_ICON);
|
||||||
|
}
|
||||||
|
if (energyIcon == null)
|
||||||
|
{
|
||||||
|
energyIcon = loadAndResize(SpriteID.MINIMAP_ORB_WALK_ICON);
|
||||||
|
}
|
||||||
|
if (specialIcon == null)
|
||||||
|
{
|
||||||
|
specialIcon = loadAndResize(SpriteID.MINIMAP_ORB_SPECIAL_ICON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferedImage loadAndResize(int spriteId)
|
||||||
|
{
|
||||||
|
BufferedImage image = spriteManager.getSprite(spriteId, 0);
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
heartIcon = ImageUtil.resizeCanvas(Objects.requireNonNull(spriteManager.getSprite(SpriteID.MINIMAP_ORB_HITPOINTS_ICON, 0)), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
return ImageUtil.resizeCanvas(image, ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
||||||
heartDisease = ImageUtil.resizeCanvas(ImageUtil.loadImageResource(AlternateSprites.class, AlternateSprites.DISEASE_HEART), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
|
||||||
heartPoison = ImageUtil.resizeCanvas(ImageUtil.loadImageResource(AlternateSprites.class, AlternateSprites.POISON_HEART), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
|
||||||
heartVenom = ImageUtil.resizeCanvas(ImageUtil.loadImageResource(AlternateSprites.class, AlternateSprites.VENOM_HEART), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
|
||||||
energyIcon = ImageUtil.resizeCanvas(Objects.requireNonNull(spriteManager.getSprite(SpriteID.MINIMAP_ORB_WALK_ICON, 0)), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
|
||||||
specialIcon = ImageUtil.resizeCanvas(Objects.requireNonNull(spriteManager.getSprite(SpriteID.MINIMAP_ORB_SPECIAL_ICON, 0)), ICON_DIMENSIONS.width, ICON_DIMENSIONS.height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,6 @@ public class TimersPlugin extends Plugin
|
|||||||
private static final int NMZ_MAP_REGION_ID = 9033;
|
private static final int NMZ_MAP_REGION_ID = 9033;
|
||||||
private static final Pattern TZHAAR_WAVE_MESSAGE = Pattern.compile("Wave: (\\d+)");
|
private static final Pattern TZHAAR_WAVE_MESSAGE = Pattern.compile("Wave: (\\d+)");
|
||||||
private static final String TZHAAR_DEFEATED_MESSAGE = "You have been defeated!";
|
private static final String TZHAAR_DEFEATED_MESSAGE = "You have been defeated!";
|
||||||
private static final Pattern TZHAAR_COMPLETE_MESSAGE = Pattern.compile("Your (?:TzTok-Jad|TzKal-Zuk) kill count is:");
|
|
||||||
private static final Pattern TZHAAR_PAUSED_MESSAGE = Pattern.compile("The (?:Inferno|Fight Cave) has been paused. You may now log out.");
|
private static final Pattern TZHAAR_PAUSED_MESSAGE = Pattern.compile("The (?:Inferno|Fight Cave) has been paused. You may now log out.");
|
||||||
|
|
||||||
private TimerTimer freezeTimer;
|
private TimerTimer freezeTimer;
|
||||||
@@ -776,7 +775,7 @@ public class TimersPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.equals(TZHAAR_DEFEATED_MESSAGE) || TZHAAR_COMPLETE_MESSAGE.matcher(message).matches())
|
if (message.equals(TZHAAR_DEFEATED_MESSAGE))
|
||||||
{
|
{
|
||||||
log.debug("Stopping tzhaar timer");
|
log.debug("Stopping tzhaar timer");
|
||||||
removeTzhaarTimer();
|
removeTzhaarTimer();
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public interface TimeTrackingConfig extends Config
|
|||||||
String PREFER_SOONEST = "preferSoonest";
|
String PREFER_SOONEST = "preferSoonest";
|
||||||
String NOTIFY = "notify";
|
String NOTIFY = "notify";
|
||||||
String BIRDHOUSE_NOTIFY = "birdHouseNotification";
|
String BIRDHOUSE_NOTIFY = "birdHouseNotification";
|
||||||
|
String COMPOST = "compost";
|
||||||
|
|
||||||
@ConfigItem(
|
@ConfigItem(
|
||||||
keyName = "timeFormatMode",
|
keyName = "timeFormatMode",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import net.runelite.api.widgets.Widget;
|
|||||||
import net.runelite.api.widgets.WidgetInfo;
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
import net.runelite.api.widgets.WidgetModalMode;
|
import net.runelite.api.widgets.WidgetModalMode;
|
||||||
import net.runelite.client.config.ConfigManager;
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.eventbus.EventBus;
|
||||||
import net.runelite.client.eventbus.Subscribe;
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
import net.runelite.client.events.ConfigChanged;
|
import net.runelite.client.events.ConfigChanged;
|
||||||
import net.runelite.client.events.RuneScapeProfileChanged;
|
import net.runelite.client.events.RuneScapeProfileChanged;
|
||||||
@@ -54,6 +55,7 @@ import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.PREFER
|
|||||||
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.STOPWATCHES;
|
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.STOPWATCHES;
|
||||||
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.TIMERS;
|
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.TIMERS;
|
||||||
import net.runelite.client.plugins.timetracking.clocks.ClockManager;
|
import net.runelite.client.plugins.timetracking.clocks.ClockManager;
|
||||||
|
import net.runelite.client.plugins.timetracking.farming.CompostTracker;
|
||||||
import net.runelite.client.plugins.timetracking.farming.FarmingContractManager;
|
import net.runelite.client.plugins.timetracking.farming.FarmingContractManager;
|
||||||
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
|
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
|
||||||
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
|
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
|
||||||
@@ -77,6 +79,12 @@ public class TimeTrackingPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private Client client;
|
private Client client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private EventBus eventBus;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CompostTracker compostTracker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private FarmingTracker farmingTracker;
|
private FarmingTracker farmingTracker;
|
||||||
|
|
||||||
@@ -125,6 +133,8 @@ public class TimeTrackingPlugin extends Plugin
|
|||||||
birdHouseTracker.loadFromConfig();
|
birdHouseTracker.loadFromConfig();
|
||||||
farmingTracker.loadCompletionTimes();
|
farmingTracker.loadCompletionTimes();
|
||||||
|
|
||||||
|
eventBus.register(compostTracker);
|
||||||
|
|
||||||
final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "watch.png");
|
final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "watch.png");
|
||||||
|
|
||||||
panel = injector.getInstance(TimeTrackingPanel.class);
|
panel = injector.getInstance(TimeTrackingPanel.class);
|
||||||
@@ -148,6 +158,8 @@ public class TimeTrackingPlugin extends Plugin
|
|||||||
lastTickLocation = null;
|
lastTickLocation = null;
|
||||||
lastTickPostLogin = false;
|
lastTickPostLogin = false;
|
||||||
|
|
||||||
|
eventBus.unregister(compostTracker);
|
||||||
|
|
||||||
if (panelUpdateFuture != null)
|
if (panelUpdateFuture != null)
|
||||||
{
|
{
|
||||||
panelUpdateFuture.cancel(true);
|
panelUpdateFuture.cancel(true);
|
||||||
|
|||||||
@@ -29,8 +29,11 @@ import java.awt.BorderLayout;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -48,9 +51,20 @@ public class TimeablePanel<T> extends JPanel
|
|||||||
{
|
{
|
||||||
private static final ImageIcon NOTIFY_ICON = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_icon.png"));
|
private static final ImageIcon NOTIFY_ICON = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_icon.png"));
|
||||||
private static final ImageIcon NOTIFY_SELECTED_ICON = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_selected_icon.png"));
|
private static final ImageIcon NOTIFY_SELECTED_ICON = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_selected_icon.png"));
|
||||||
|
private static final Rectangle OVERLAY_ICON_BOUNDS;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
int width = Constants.ITEM_SPRITE_WIDTH * 2 / 3;
|
||||||
|
int height = Constants.ITEM_SPRITE_HEIGHT * 2 / 3;
|
||||||
|
int x = Constants.ITEM_SPRITE_WIDTH - width;
|
||||||
|
int y = Constants.ITEM_SPRITE_HEIGHT - height;
|
||||||
|
OVERLAY_ICON_BOUNDS = new Rectangle(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
private final T timeable;
|
private final T timeable;
|
||||||
private final JLabel icon = new JLabel();
|
private final JLabel icon = new JLabel();
|
||||||
|
private final JLabel overlayIcon = new JLabel();
|
||||||
private final JLabel farmingContractIcon = new JLabel();
|
private final JLabel farmingContractIcon = new JLabel();
|
||||||
private final JToggleButton notifyButton = new JToggleButton();
|
private final JToggleButton notifyButton = new JToggleButton();
|
||||||
private final JLabel estimate = new JLabel();
|
private final JLabel estimate = new JLabel();
|
||||||
@@ -70,6 +84,7 @@ public class TimeablePanel<T> extends JPanel
|
|||||||
topContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
topContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
|
||||||
icon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT));
|
icon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT));
|
||||||
|
overlayIcon.setMinimumSize(OVERLAY_ICON_BOUNDS.getSize());
|
||||||
farmingContractIcon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT));
|
farmingContractIcon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT));
|
||||||
|
|
||||||
JPanel infoPanel = new JPanel();
|
JPanel infoPanel = new JPanel();
|
||||||
@@ -105,8 +120,15 @@ public class TimeablePanel<T> extends JPanel
|
|||||||
iconPanel.add(notifyPanel, BorderLayout.EAST);
|
iconPanel.add(notifyPanel, BorderLayout.EAST);
|
||||||
iconPanel.add(farmingContractIcon, BorderLayout.WEST);
|
iconPanel.add(farmingContractIcon, BorderLayout.WEST);
|
||||||
|
|
||||||
|
JLayeredPane layeredIconPane = new JLayeredPane();
|
||||||
|
layeredIconPane.setPreferredSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT));
|
||||||
|
layeredIconPane.add(icon, Integer.valueOf(0));
|
||||||
|
layeredIconPane.add(overlayIcon, Integer.valueOf(1));
|
||||||
|
icon.setBounds(0, 0, Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT);
|
||||||
|
overlayIcon.setBounds(OVERLAY_ICON_BOUNDS);
|
||||||
|
|
||||||
topContainer.add(iconPanel, BorderLayout.EAST);
|
topContainer.add(iconPanel, BorderLayout.EAST);
|
||||||
topContainer.add(icon, BorderLayout.WEST);
|
topContainer.add(layeredIconPane, BorderLayout.WEST);
|
||||||
topContainer.add(infoPanel, BorderLayout.CENTER);
|
topContainer.add(infoPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
progress.setValue(0);
|
progress.setValue(0);
|
||||||
@@ -115,4 +137,21 @@ public class TimeablePanel<T> extends JPanel
|
|||||||
add(topContainer, BorderLayout.NORTH);
|
add(topContainer, BorderLayout.NORTH);
|
||||||
add(progress, BorderLayout.SOUTH);
|
add(progress, BorderLayout.SOUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOverlayIconImage(BufferedImage overlayImg)
|
||||||
|
{
|
||||||
|
if (overlayImg == null)
|
||||||
|
{
|
||||||
|
overlayIcon.setIcon(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OVERLAY_ICON_BOUNDS.width != overlayImg.getWidth() || OVERLAY_ICON_BOUNDS.height != overlayImg.getHeight())
|
||||||
|
{
|
||||||
|
overlayImg = ImageUtil.resizeImage(overlayImg, OVERLAY_ICON_BOUNDS.width, OVERLAY_ICON_BOUNDS.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
overlayIcon.setIcon(new ImageIcon(overlayImg));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 LlemonDuck
|
||||||
|
* 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.client.plugins.timetracking.farming;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.runelite.api.ItemID;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum CompostState
|
||||||
|
{
|
||||||
|
|
||||||
|
COMPOST(ItemID.COMPOST),
|
||||||
|
SUPERCOMPOST(ItemID.SUPERCOMPOST),
|
||||||
|
ULTRACOMPOST(ItemID.ULTRACOMPOST),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final int itemId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 LlemonDuck
|
||||||
|
* 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.client.plugins.timetracking.farming;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.ChatMessageType;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.GameObject;
|
||||||
|
import net.runelite.api.ItemID;
|
||||||
|
import net.runelite.api.ObjectComposition;
|
||||||
|
import net.runelite.api.Tile;
|
||||||
|
import net.runelite.api.annotations.Varbit;
|
||||||
|
import net.runelite.api.coords.LocalPoint;
|
||||||
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
import net.runelite.api.events.ChatMessage;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
|
import net.runelite.api.events.MenuOptionClicked;
|
||||||
|
import net.runelite.api.widgets.Widget;
|
||||||
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor(onConstructor = @__(@Inject))
|
||||||
|
public class CompostTracker
|
||||||
|
{
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@VisibleForTesting
|
||||||
|
static class PendingCompost
|
||||||
|
{
|
||||||
|
Instant timeout;
|
||||||
|
WorldPoint patchLocation;
|
||||||
|
FarmingPatch farmingPatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Duration COMPOST_ACTION_TIMEOUT = Duration.ofSeconds(30);
|
||||||
|
|
||||||
|
private static final Pattern COMPOST_USED_ON_PATCH = Pattern.compile(
|
||||||
|
"You treat the .+ with (?<compostType>ultra|super|)compost\\.");
|
||||||
|
private static final Pattern FERTILE_SOIL_CAST = Pattern.compile(
|
||||||
|
"The .+ has been treated with (?<compostType>ultra|super|)compost\\.");
|
||||||
|
private static final Pattern ALREADY_TREATED = Pattern.compile(
|
||||||
|
"This .+ has already been (treated|fertilised) with (?<compostType>ultra|super|)compost(?: - the spell can't make it any more fertile)?\\.");
|
||||||
|
private static final Pattern INSPECT_PATCH = Pattern.compile(
|
||||||
|
"This is an? .+\\. The soil has been treated with (?<compostType>ultra|super|)compost\\..*");
|
||||||
|
|
||||||
|
private static final ImmutableSet<Integer> COMPOST_ITEMS = ImmutableSet.of(
|
||||||
|
ItemID.COMPOST,
|
||||||
|
ItemID.SUPERCOMPOST,
|
||||||
|
ItemID.ULTRACOMPOST,
|
||||||
|
ItemID.BOTTOMLESS_COMPOST_BUCKET_22997
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Client client;
|
||||||
|
private final FarmingWorld farmingWorld;
|
||||||
|
private final ConfigManager configManager;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
final Map<FarmingPatch, PendingCompost> pendingCompostActions = new HashMap<>();
|
||||||
|
|
||||||
|
private static String configKey(FarmingPatch fp)
|
||||||
|
{
|
||||||
|
return fp.configKey() + "." + TimeTrackingConfig.COMPOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompostState(FarmingPatch fp, CompostState state)
|
||||||
|
{
|
||||||
|
log.debug("Storing compost state [{}] for patch [{}]", state, fp);
|
||||||
|
if (state == null)
|
||||||
|
{
|
||||||
|
configManager.unsetRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey(fp));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey(fp), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompostState getCompostState(FarmingPatch fp)
|
||||||
|
{
|
||||||
|
return configManager.getRSProfileConfiguration(
|
||||||
|
TimeTrackingConfig.CONFIG_GROUP,
|
||||||
|
configKey(fp),
|
||||||
|
CompostState.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onMenuOptionClicked(MenuOptionClicked e)
|
||||||
|
{
|
||||||
|
if (!isCompostAction(e))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectComposition patchDef = client.getObjectDefinition(e.getId());
|
||||||
|
WorldPoint actionLocation = WorldPoint.fromScene(client, e.getParam0(), e.getParam1(), client.getPlane());
|
||||||
|
FarmingPatch targetPatch = farmingWorld.getRegionsForLocation(actionLocation)
|
||||||
|
.stream()
|
||||||
|
.flatMap(fr -> Arrays.stream(fr.getPatches()))
|
||||||
|
.filter(fp -> fp.getVarbit() == patchDef.getVarbitId())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
if (targetPatch == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Storing pending compost action for patch [{}]", targetPatch);
|
||||||
|
PendingCompost pc = new PendingCompost(
|
||||||
|
Instant.now().plus(COMPOST_ACTION_TIMEOUT),
|
||||||
|
actionLocation,
|
||||||
|
targetPatch
|
||||||
|
);
|
||||||
|
pendingCompostActions.put(targetPatch, pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCompostAction(MenuOptionClicked e)
|
||||||
|
{
|
||||||
|
switch (e.getMenuAction())
|
||||||
|
{
|
||||||
|
case WIDGET_TARGET_ON_GAME_OBJECT:
|
||||||
|
Widget w = client.getSelectedWidget();
|
||||||
|
assert w != null;
|
||||||
|
return COMPOST_ITEMS.contains(w.getItemId()) || w.getId() == WidgetInfo.SPELL_LUNAR_FERTILE_SOIL.getPackedId();
|
||||||
|
|
||||||
|
case GAME_OBJECT_FIRST_OPTION:
|
||||||
|
case GAME_OBJECT_SECOND_OPTION:
|
||||||
|
case GAME_OBJECT_THIRD_OPTION:
|
||||||
|
case GAME_OBJECT_FOURTH_OPTION:
|
||||||
|
case GAME_OBJECT_FIFTH_OPTION:
|
||||||
|
return "Inspect".equals(e.getMenuOption());
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onChatMessage(ChatMessage e)
|
||||||
|
{
|
||||||
|
if (e.getType() != ChatMessageType.GAMEMESSAGE && e.getType() != ChatMessageType.SPAM)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompostState compostUsed = determineCompostUsed(e.getMessage());
|
||||||
|
if (compostUsed == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expirePendingActions();
|
||||||
|
|
||||||
|
pendingCompostActions.values()
|
||||||
|
.stream()
|
||||||
|
.filter(this::playerIsBesidePatch)
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(pc ->
|
||||||
|
{
|
||||||
|
setCompostState(pc.getFarmingPatch(), compostUsed);
|
||||||
|
pendingCompostActions.remove(pc.getFarmingPatch());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameStateChanged(GameStateChanged e)
|
||||||
|
{
|
||||||
|
switch (e.getGameState())
|
||||||
|
{
|
||||||
|
case LOGGED_IN:
|
||||||
|
case LOADING:
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pendingCompostActions.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean playerIsBesidePatch(PendingCompost pendingCompost)
|
||||||
|
{
|
||||||
|
// find gameobject instance in scene
|
||||||
|
// it is possible that the scene has reloaded between use and action occurring so we use worldpoint
|
||||||
|
// instead of storing scene coords in the menuoptionclicked event
|
||||||
|
LocalPoint localPatchLocation = LocalPoint.fromWorld(client, pendingCompost.getPatchLocation());
|
||||||
|
if (localPatchLocation == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Varbit int patchVarb = pendingCompost.getFarmingPatch().getVarbit();
|
||||||
|
Tile patchTile = client.getScene()
|
||||||
|
.getTiles()[client.getPlane()][localPatchLocation.getSceneX()][localPatchLocation.getSceneY()];
|
||||||
|
GameObject patchObject = null;
|
||||||
|
for (GameObject go : patchTile.getGameObjects())
|
||||||
|
{
|
||||||
|
if (go != null && client.getObjectDefinition(go.getId()).getVarbitId() == patchVarb)
|
||||||
|
{
|
||||||
|
patchObject = go;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert patchObject != null;
|
||||||
|
|
||||||
|
// player coords
|
||||||
|
final WorldPoint playerPos = client.getLocalPlayer().getWorldLocation();
|
||||||
|
final int playerX = playerPos.getX();
|
||||||
|
final int playerY = playerPos.getY();
|
||||||
|
|
||||||
|
// patch coords
|
||||||
|
final WorldPoint patchBase = pendingCompost.getPatchLocation();
|
||||||
|
final int minX = patchBase.getX();
|
||||||
|
final int minY = patchBase.getY();
|
||||||
|
final int maxX = minX + patchObject.sizeX() - 1;
|
||||||
|
final int maxY = minY + patchObject.sizeY() - 1;
|
||||||
|
|
||||||
|
// player should be within one tile of these coords
|
||||||
|
return playerX >= (minX - 1) && playerX <= (maxX + 1) && playerY >= (minY - 1) && playerY <= (maxY + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expirePendingActions()
|
||||||
|
{
|
||||||
|
pendingCompostActions.values().removeIf(e -> Instant.now().isAfter(e.getTimeout()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static CompostState determineCompostUsed(String chatMessage)
|
||||||
|
{
|
||||||
|
if (!chatMessage.contains("compost"))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher matcher;
|
||||||
|
if ((matcher = COMPOST_USED_ON_PATCH.matcher(chatMessage)).matches() ||
|
||||||
|
(matcher = FERTILE_SOIL_CAST.matcher(chatMessage)).matches() ||
|
||||||
|
(matcher = ALREADY_TREATED.matcher(chatMessage)).matches() ||
|
||||||
|
(matcher = INSPECT_PATCH.matcher(chatMessage)).matches())
|
||||||
|
{
|
||||||
|
String compostGroup = matcher.group("compostType");
|
||||||
|
switch (compostGroup)
|
||||||
|
{
|
||||||
|
case "ultra":
|
||||||
|
return CompostState.ULTRACOMPOST;
|
||||||
|
case "super":
|
||||||
|
return CompostState.SUPERCOMPOST;
|
||||||
|
default:
|
||||||
|
return CompostState.COMPOST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import lombok.AccessLevel;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
import net.runelite.api.annotations.Varbit;
|
import net.runelite.api.annotations.Varbit;
|
||||||
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
|
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
|
||||||
|
|
||||||
@@ -35,13 +36,17 @@ import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
|
|||||||
access = AccessLevel.PACKAGE
|
access = AccessLevel.PACKAGE
|
||||||
)
|
)
|
||||||
@Getter
|
@Getter
|
||||||
|
@ToString(onlyExplicitlyIncluded = true)
|
||||||
class FarmingPatch
|
class FarmingPatch
|
||||||
{
|
{
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
@ToString.Include
|
||||||
private FarmingRegion region;
|
private FarmingRegion region;
|
||||||
|
@ToString.Include
|
||||||
private final String name;
|
private final String name;
|
||||||
@Getter(onMethod_ = {@Varbit})
|
@Getter(onMethod_ = {@Varbit})
|
||||||
private final int varbit;
|
private final int varbit;
|
||||||
|
@ToString.Include
|
||||||
private final PatchImplementation implementation;
|
private final PatchImplementation implementation;
|
||||||
|
|
||||||
String configKey()
|
String configKey()
|
||||||
|
|||||||
@@ -43,10 +43,12 @@ import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
|
|||||||
import net.runelite.client.plugins.timetracking.TimeablePanel;
|
import net.runelite.client.plugins.timetracking.TimeablePanel;
|
||||||
import net.runelite.client.ui.ColorScheme;
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import net.runelite.client.ui.FontManager;
|
import net.runelite.client.ui.FontManager;
|
||||||
|
import net.runelite.client.util.AsyncBufferedImage;
|
||||||
|
|
||||||
public class FarmingTabPanel extends TabContentPanel
|
public class FarmingTabPanel extends TabContentPanel
|
||||||
{
|
{
|
||||||
private final FarmingTracker farmingTracker;
|
private final FarmingTracker farmingTracker;
|
||||||
|
private final CompostTracker compostTracker;
|
||||||
private final ItemManager itemManager;
|
private final ItemManager itemManager;
|
||||||
private final ConfigManager configManager;
|
private final ConfigManager configManager;
|
||||||
private final TimeTrackingConfig config;
|
private final TimeTrackingConfig config;
|
||||||
@@ -55,6 +57,7 @@ public class FarmingTabPanel extends TabContentPanel
|
|||||||
|
|
||||||
FarmingTabPanel(
|
FarmingTabPanel(
|
||||||
FarmingTracker farmingTracker,
|
FarmingTracker farmingTracker,
|
||||||
|
CompostTracker compostTracker,
|
||||||
ItemManager itemManager,
|
ItemManager itemManager,
|
||||||
ConfigManager configManager,
|
ConfigManager configManager,
|
||||||
TimeTrackingConfig config,
|
TimeTrackingConfig config,
|
||||||
@@ -63,6 +66,7 @@ public class FarmingTabPanel extends TabContentPanel
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.farmingTracker = farmingTracker;
|
this.farmingTracker = farmingTracker;
|
||||||
|
this.compostTracker = compostTracker;
|
||||||
this.itemManager = itemManager;
|
this.itemManager = itemManager;
|
||||||
this.configManager = configManager;
|
this.configManager = configManager;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
@@ -131,7 +135,6 @@ public class FarmingTabPanel extends TabContentPanel
|
|||||||
p.setBorder(null);
|
p.setBorder(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -150,10 +153,26 @@ public class FarmingTabPanel extends TabContentPanel
|
|||||||
FarmingPatch patch = panel.getTimeable();
|
FarmingPatch patch = panel.getTimeable();
|
||||||
PatchPrediction prediction = farmingTracker.predictPatch(patch);
|
PatchPrediction prediction = farmingTracker.predictPatch(patch);
|
||||||
|
|
||||||
|
CompostState compostState = compostTracker.getCompostState(patch);
|
||||||
|
String compostTooltip = "";
|
||||||
|
if (compostState != null)
|
||||||
|
{
|
||||||
|
AsyncBufferedImage compostImg = itemManager.getImage(compostState.getItemId());
|
||||||
|
Runnable compostOverlayRunnable = () -> panel.setOverlayIconImage(compostImg);
|
||||||
|
compostImg.onLoaded(compostOverlayRunnable);
|
||||||
|
compostOverlayRunnable.run();
|
||||||
|
|
||||||
|
compostTooltip = " with " + compostState.name().toLowerCase();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
panel.setOverlayIconImage(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (prediction == null)
|
if (prediction == null)
|
||||||
{
|
{
|
||||||
itemManager.getImage(Produce.WEEDS.getItemID()).addTo(panel.getIcon());
|
itemManager.getImage(Produce.WEEDS.getItemID()).addTo(panel.getIcon());
|
||||||
panel.getIcon().setToolTipText("Unknown state");
|
panel.getIcon().setToolTipText("Unknown state" + compostTooltip);
|
||||||
panel.getProgress().setMaximumValue(0);
|
panel.getProgress().setMaximumValue(0);
|
||||||
panel.getProgress().setValue(0);
|
panel.getProgress().setValue(0);
|
||||||
panel.getProgress().setVisible(false);
|
panel.getProgress().setVisible(false);
|
||||||
@@ -165,12 +184,12 @@ public class FarmingTabPanel extends TabContentPanel
|
|||||||
if (prediction.getProduce().getItemID() < 0)
|
if (prediction.getProduce().getItemID() < 0)
|
||||||
{
|
{
|
||||||
panel.getIcon().setIcon(null);
|
panel.getIcon().setIcon(null);
|
||||||
panel.getIcon().setToolTipText("Unknown state");
|
panel.getIcon().setToolTipText("Unknown state" + compostTooltip);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
itemManager.getImage(prediction.getProduce().getItemID()).addTo(panel.getIcon());
|
itemManager.getImage(prediction.getProduce().getItemID()).addTo(panel.getIcon());
|
||||||
panel.getIcon().setToolTipText(prediction.getProduce().getName());
|
panel.getIcon().setToolTipText(prediction.getProduce().getName() + compostTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (prediction.getCropState())
|
switch (prediction.getCropState())
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public class FarmingTracker
|
|||||||
private final TimeTrackingConfig config;
|
private final TimeTrackingConfig config;
|
||||||
private final FarmingWorld farmingWorld;
|
private final FarmingWorld farmingWorld;
|
||||||
private final Notifier notifier;
|
private final Notifier notifier;
|
||||||
|
private final CompostTracker compostTracker;
|
||||||
|
|
||||||
private final Map<Tab, SummaryState> summaries = new EnumMap<>(Tab.class);
|
private final Map<Tab, SummaryState> summaries = new EnumMap<>(Tab.class);
|
||||||
|
|
||||||
@@ -78,7 +79,7 @@ public class FarmingTracker
|
|||||||
private boolean firstNotifyCheck = true;
|
private boolean firstNotifyCheck = true;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld, Notifier notifier)
|
private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld, Notifier notifier, CompostTracker compostTracker)
|
||||||
{
|
{
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.itemManager = itemManager;
|
this.itemManager = itemManager;
|
||||||
@@ -86,11 +87,12 @@ public class FarmingTracker
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
this.farmingWorld = farmingWorld;
|
this.farmingWorld = farmingWorld;
|
||||||
this.notifier = notifier;
|
this.notifier = notifier;
|
||||||
|
this.compostTracker = compostTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager)
|
public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager)
|
||||||
{
|
{
|
||||||
return new FarmingTabPanel(this, itemManager, configManager, config, farmingWorld.getTabs().get(tab), farmingContractManager);
|
return new FarmingTabPanel(this, compostTracker, itemManager, configManager, config, farmingWorld.getTabs().get(tab), farmingContractManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,6 +150,12 @@ public class FarmingTracker
|
|||||||
String strVarbit = Integer.toString(client.getVarbitValue(varbit));
|
String strVarbit = Integer.toString(client.getVarbitValue(varbit));
|
||||||
String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key);
|
String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key);
|
||||||
|
|
||||||
|
PatchState currentPatchState = patch.getImplementation().forVarbitValue(client.getVarbitValue(varbit));
|
||||||
|
if (currentPatchState == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (storedValue != null)
|
if (storedValue != null)
|
||||||
{
|
{
|
||||||
String[] parts = storedValue.split(":");
|
String[] parts = storedValue.split(":");
|
||||||
@@ -172,9 +180,8 @@ public class FarmingTracker
|
|||||||
else if (!newRegionLoaded && timeSinceModalClose > 1)
|
else if (!newRegionLoaded && timeSinceModalClose > 1)
|
||||||
{
|
{
|
||||||
PatchState previousPatchState = patch.getImplementation().forVarbitValue(Integer.parseInt(parts[0]));
|
PatchState previousPatchState = patch.getImplementation().forVarbitValue(Integer.parseInt(parts[0]));
|
||||||
PatchState currentPatchState = patch.getImplementation().forVarbitValue(client.getVarbitValue(varbit));
|
|
||||||
|
|
||||||
if (previousPatchState == null || currentPatchState == null)
|
if (previousPatchState == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -217,6 +224,11 @@ public class FarmingTracker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentPatchState.getCropState() == CropState.DEAD || currentPatchState.getCropState() == CropState.HARVESTABLE)
|
||||||
|
{
|
||||||
|
compostTracker.setCompostState(patch, null);
|
||||||
|
}
|
||||||
|
|
||||||
String value = strVarbit + ":" + unixNow;
|
String value = strVarbit + ":" + unixNow;
|
||||||
configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key, value);
|
configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key, value);
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public enum Produce
|
|||||||
POTATO_CACTUS("Potato cactus", "Potato cacti", PatchImplementation.CACTUS, ItemID.POTATO_CACTUS, 10, 8, 5, 7),
|
POTATO_CACTUS("Potato cactus", "Potato cacti", PatchImplementation.CACTUS, ItemID.POTATO_CACTUS, 10, 8, 5, 7),
|
||||||
|
|
||||||
// Hardwood
|
// Hardwood
|
||||||
TEAK("Teak", PatchImplementation.HARDWOOD_TREE, ItemID.TEAK_LOGS, 560, 8),
|
TEAK("Teak", PatchImplementation.HARDWOOD_TREE, ItemID.TEAK_LOGS, 640, 8),
|
||||||
MAHOGANY("Mahogany", PatchImplementation.HARDWOOD_TREE, ItemID.MAHOGANY_LOGS, 640, 9),
|
MAHOGANY("Mahogany", PatchImplementation.HARDWOOD_TREE, ItemID.MAHOGANY_LOGS, 640, 9),
|
||||||
|
|
||||||
// Anima
|
// Anima
|
||||||
|
|||||||
@@ -66,12 +66,15 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
@@ -1156,4 +1159,13 @@ public class ChatCommandsPluginTest
|
|||||||
|
|
||||||
verify(configManager).setRSProfileConfiguration("killcount", "guardians of the rift", 167);
|
verify(configManager).setRSProfileConfiguration("killcount", "guardians of the rift", 167);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReward()
|
||||||
|
{
|
||||||
|
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your reward is: <col=ff0000>1</col> x <col=ff0000>Kebab</col>.", null, 0);
|
||||||
|
chatCommandsPlugin.onChatMessage(chatMessage);
|
||||||
|
|
||||||
|
verify(configManager, never()).setRSProfileConfiguration(anyString(), anyString(), anyInt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ public class ChatFilterPluginTest
|
|||||||
{
|
{
|
||||||
when(chatFilterConfig.filterType()).thenReturn(ChatFilterType.CENSOR_WORDS);
|
when(chatFilterConfig.filterType()).thenReturn(ChatFilterType.CENSOR_WORDS);
|
||||||
when(chatFilterConfig.filteredWords()).thenReturn("filterme");
|
when(chatFilterConfig.filteredWords()).thenReturn("filterme");
|
||||||
|
when(chatFilterConfig.stripAccents()).thenReturn(true);
|
||||||
|
|
||||||
chatFilterPlugin.updateFilteredPatterns();
|
chatFilterPlugin.updateFilteredPatterns();
|
||||||
assertEquals("plëäsë ******** plügïn", chatFilterPlugin.censorMessage("Blue", "plëäsë fïltërmë plügïn"));
|
assertEquals("plëäsë ******** plügïn", chatFilterPlugin.censorMessage("Blue", "plëäsë fïltërmë plügïn"));
|
||||||
@@ -211,6 +212,7 @@ public class ChatFilterPluginTest
|
|||||||
{
|
{
|
||||||
when(chatFilterConfig.filterType()).thenReturn(ChatFilterType.CENSOR_WORDS);
|
when(chatFilterConfig.filterType()).thenReturn(ChatFilterType.CENSOR_WORDS);
|
||||||
when(chatFilterConfig.filteredWords()).thenReturn("plëäsë, filterme");
|
when(chatFilterConfig.filteredWords()).thenReturn("plëäsë, filterme");
|
||||||
|
when(chatFilterConfig.stripAccents()).thenReturn(true);
|
||||||
|
|
||||||
chatFilterPlugin.updateFilteredPatterns();
|
chatFilterPlugin.updateFilteredPatterns();
|
||||||
assertEquals("****** ******** plügïn", chatFilterPlugin.censorMessage("Blue", "plëäsë fïltërmë plügïn"));
|
assertEquals("****** ******** plügïn", chatFilterPlugin.censorMessage("Blue", "plëäsë fïltërmë plügïn"));
|
||||||
|
|||||||
@@ -28,24 +28,27 @@ import com.google.inject.Guice;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.testing.fieldbinder.Bind;
|
import com.google.inject.testing.fieldbinder.Bind;
|
||||||
import com.google.inject.testing.fieldbinder.BoundFieldModule;
|
import com.google.inject.testing.fieldbinder.BoundFieldModule;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
import net.runelite.api.ChatMessageType;
|
import net.runelite.api.ChatMessageType;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.GameState;
|
import net.runelite.api.GameState;
|
||||||
import net.runelite.api.IndexedSprite;
|
import net.runelite.api.IndexedSprite;
|
||||||
import net.runelite.api.MessageNode;
|
import net.runelite.api.MessageNode;
|
||||||
import net.runelite.api.events.ChatMessage;
|
import net.runelite.api.events.ChatMessage;
|
||||||
import net.runelite.api.events.GameStateChanged;
|
import net.runelite.client.callback.ClientThread;
|
||||||
import net.runelite.client.chat.ChatMessageManager;
|
import net.runelite.client.chat.ChatMessageManager;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class EmojiPluginTest
|
public class EmojiPluginTest
|
||||||
@@ -58,6 +61,10 @@ public class EmojiPluginTest
|
|||||||
@Bind
|
@Bind
|
||||||
private ChatMessageManager chatMessageManager;
|
private ChatMessageManager chatMessageManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private ClientThread clientThread;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private EmojiPlugin emojiPlugin;
|
private EmojiPlugin emojiPlugin;
|
||||||
|
|
||||||
@@ -65,19 +72,23 @@ public class EmojiPluginTest
|
|||||||
public void before()
|
public void before()
|
||||||
{
|
{
|
||||||
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
|
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
|
||||||
|
|
||||||
|
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
|
||||||
|
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
|
||||||
|
|
||||||
|
doAnswer(a ->
|
||||||
|
{
|
||||||
|
final BooleanSupplier b = a.getArgument(0);
|
||||||
|
return b.getAsBoolean();
|
||||||
|
}).when(clientThread).invoke(any(BooleanSupplier.class));
|
||||||
|
|
||||||
|
emojiPlugin.startUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnChatMessage()
|
public void testOnChatMessage()
|
||||||
{
|
{
|
||||||
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
|
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
|
||||||
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
|
|
||||||
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
|
|
||||||
|
|
||||||
// Trip emoji loading
|
|
||||||
GameStateChanged gameStateChanged = new GameStateChanged();
|
|
||||||
gameStateChanged.setGameState(GameState.LOGGED_IN);
|
|
||||||
emojiPlugin.onGameStateChanged(gameStateChanged);
|
|
||||||
|
|
||||||
MessageNode messageNode = mock(MessageNode.class);
|
MessageNode messageNode = mock(MessageNode.class);
|
||||||
// With chat recolor, message may be wrapped in col tags
|
// With chat recolor, message may be wrapped in col tags
|
||||||
@@ -96,13 +107,6 @@ public class EmojiPluginTest
|
|||||||
public void testGtLt()
|
public void testGtLt()
|
||||||
{
|
{
|
||||||
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
|
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
|
||||||
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
|
|
||||||
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
|
|
||||||
|
|
||||||
// Trip emoji loading
|
|
||||||
GameStateChanged gameStateChanged = new GameStateChanged();
|
|
||||||
gameStateChanged.setGameState(GameState.LOGGED_IN);
|
|
||||||
emojiPlugin.onGameStateChanged(gameStateChanged);
|
|
||||||
|
|
||||||
MessageNode messageNode = mock(MessageNode.class);
|
MessageNode messageNode = mock(MessageNode.class);
|
||||||
when(messageNode.getValue()).thenReturn("<gt>:D<lt>");
|
when(messageNode.getValue()).thenReturn("<gt>:D<lt>");
|
||||||
@@ -119,8 +123,8 @@ public class EmojiPluginTest
|
|||||||
@Test
|
@Test
|
||||||
public void testEmojiUpdateMessage()
|
public void testEmojiUpdateMessage()
|
||||||
{
|
{
|
||||||
String PARTY_POPPER = "<img=" + (-1 + Emoji.getEmoji("@@@").ordinal()) + '>';
|
String PARTY_POPPER = "<img=" + Emoji.getEmoji("@@@").ordinal() + '>';
|
||||||
String OPEN_MOUTH = "<img=" + (-1 + Emoji.getEmoji(":O").ordinal()) + '>';
|
String OPEN_MOUTH = "<img=" + Emoji.getEmoji(":O").ordinal() + '>';
|
||||||
assertNull(emojiPlugin.updateMessage("@@@@@"));
|
assertNull(emojiPlugin.updateMessage("@@@@@"));
|
||||||
assertEquals(PARTY_POPPER, emojiPlugin.updateMessage("@@@"));
|
assertEquals(PARTY_POPPER, emojiPlugin.updateMessage("@@@"));
|
||||||
assertEquals(PARTY_POPPER + ' ' + PARTY_POPPER, emojiPlugin.updateMessage("@@@ @@@"));
|
assertEquals(PARTY_POPPER + ' ' + PARTY_POPPER, emojiPlugin.updateMessage("@@@ @@@"));
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 LlemonDuck
|
||||||
|
* 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.client.plugins.timetracking.farming;
|
||||||
|
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.testing.fieldbinder.Bind;
|
||||||
|
import com.google.inject.testing.fieldbinder.BoundFieldModule;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Collections;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import net.runelite.api.ChatMessageType;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.GameObject;
|
||||||
|
import net.runelite.api.ItemID;
|
||||||
|
import net.runelite.api.MenuAction;
|
||||||
|
import net.runelite.api.ObjectComposition;
|
||||||
|
import net.runelite.api.Player;
|
||||||
|
import net.runelite.api.Scene;
|
||||||
|
import net.runelite.api.Tile;
|
||||||
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
import net.runelite.api.events.ChatMessage;
|
||||||
|
import net.runelite.api.events.MenuOptionClicked;
|
||||||
|
import net.runelite.api.widgets.Widget;
|
||||||
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ErrorCollector;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class CompostTrackerTest
|
||||||
|
{
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CompostTracker compostTracker;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private FarmingWorld farmingWorld;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private ConfigManager configManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private FarmingRegion farmingRegion;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private FarmingPatch farmingPatch;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private GameObject patchObject;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private Player player;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private Scene scene;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private Tile tile;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private ObjectComposition patchDef;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ErrorCollector collector = new ErrorCollector();
|
||||||
|
|
||||||
|
private static final int PATCH_ID = 12345;
|
||||||
|
private static final int PATCH_VARBIT = 54321;
|
||||||
|
private static final WorldPoint worldPoint = new WorldPoint(1, 2, 0);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before()
|
||||||
|
{
|
||||||
|
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
|
||||||
|
compostTracker.pendingCompostActions.clear();
|
||||||
|
|
||||||
|
when(client.getBaseX()).thenReturn(0);
|
||||||
|
when(client.getBaseY()).thenReturn(0);
|
||||||
|
when(client.getPlane()).thenReturn(0);
|
||||||
|
when(client.getLocalPlayer()).thenReturn(player);
|
||||||
|
when(player.getWorldLocation()).thenReturn(worldPoint);
|
||||||
|
when(client.getScene()).thenReturn(scene);
|
||||||
|
when(client.getObjectDefinition(PATCH_ID)).thenReturn(patchDef);
|
||||||
|
|
||||||
|
when(scene.getTiles()).thenReturn(new Tile[][][]{{null, {null, null, tile}}}); // indices match worldPoint
|
||||||
|
when(tile.getGameObjects()).thenReturn(new GameObject[]{patchObject});
|
||||||
|
|
||||||
|
when(farmingWorld.getRegionsForLocation(any())).thenReturn(Collections.singleton(farmingRegion));
|
||||||
|
|
||||||
|
when(farmingRegion.getPatches()).thenReturn(new FarmingPatch[]{farmingPatch});
|
||||||
|
|
||||||
|
when(farmingPatch.getVarbit()).thenReturn(PATCH_VARBIT);
|
||||||
|
when(farmingPatch.configKey()).thenReturn("MOCK");
|
||||||
|
|
||||||
|
when(patchObject.getId()).thenReturn(PATCH_ID);
|
||||||
|
when(patchObject.sizeX()).thenReturn(1);
|
||||||
|
when(patchObject.sizeY()).thenReturn(1);
|
||||||
|
|
||||||
|
when(patchDef.getVarbitId()).thenReturn(PATCH_VARBIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setCompostState_storesNonNullChangesToConfig()
|
||||||
|
{
|
||||||
|
compostTracker.setCompostState(farmingPatch, CompostState.COMPOST);
|
||||||
|
verify(configManager).setRSProfileConfiguration("timetracking", "MOCK.compost", CompostState.COMPOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setCompostState_storesNullChangesByClearingConfig()
|
||||||
|
{
|
||||||
|
compostTracker.setCompostState(farmingPatch, null);
|
||||||
|
verify(configManager).unsetRSProfileConfiguration("timetracking", "MOCK.compost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCompostState_directlyReturnsFromConfig()
|
||||||
|
{
|
||||||
|
when(configManager.getRSProfileConfiguration("timetracking", "MOCK.compost", CompostState.class)).thenReturn(
|
||||||
|
CompostState.SUPERCOMPOST);
|
||||||
|
assertThat(compostTracker.getCompostState(farmingPatch), is(CompostState.SUPERCOMPOST));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void determineCompostUsed_returnsAppropriateCompostValues()
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("This is not a farming chat message."),
|
||||||
|
is((CompostState) null)
|
||||||
|
);
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("Contains word compost but is not examine message."),
|
||||||
|
is((CompostState) null)
|
||||||
|
);
|
||||||
|
|
||||||
|
// inspect
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("This is an allotment. The soil has been treated with supercompost. The patch is empty and weeded."),
|
||||||
|
is(CompostState.SUPERCOMPOST)
|
||||||
|
);
|
||||||
|
|
||||||
|
// fertile soil on existing patch
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("This patch has already been fertilised with ultracompost - the spell can't make it any more fertile."),
|
||||||
|
is(CompostState.ULTRACOMPOST)
|
||||||
|
);
|
||||||
|
// fertile soil on cleared patch
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("The herb patch has been treated with supercompost."),
|
||||||
|
is(CompostState.SUPERCOMPOST)
|
||||||
|
);
|
||||||
|
|
||||||
|
// bucket on cleared patch
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("You treat the herb patch with ultracompost."),
|
||||||
|
is(CompostState.ULTRACOMPOST)
|
||||||
|
);
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("You treat the tree patch with compost."),
|
||||||
|
is(CompostState.COMPOST)
|
||||||
|
);
|
||||||
|
collector.checkThat(
|
||||||
|
CompostTracker.determineCompostUsed("You treat the fruit tree patch with supercompost."),
|
||||||
|
is(CompostState.SUPERCOMPOST)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onMenuOptionClicked_queuesPendingCompostForInspectActions()
|
||||||
|
{
|
||||||
|
MenuOptionClicked inspectPatchAction = mock(MenuOptionClicked.class);
|
||||||
|
when(inspectPatchAction.getMenuAction()).thenReturn(MenuAction.GAME_OBJECT_SECOND_OPTION);
|
||||||
|
when(inspectPatchAction.getMenuOption()).thenReturn("Inspect");
|
||||||
|
when(inspectPatchAction.getId()).thenReturn(PATCH_ID);
|
||||||
|
when(inspectPatchAction.getParam0()).thenReturn(1);
|
||||||
|
when(inspectPatchAction.getParam1()).thenReturn(2);
|
||||||
|
|
||||||
|
compostTracker.onMenuOptionClicked(inspectPatchAction);
|
||||||
|
CompostTracker.PendingCompost actual = compostTracker.pendingCompostActions.get(farmingPatch);
|
||||||
|
|
||||||
|
assertThat(actual.getFarmingPatch(), is(farmingPatch));
|
||||||
|
assertThat(actual.getPatchLocation(), is(new WorldPoint(1, 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onMenuOptionClicked_queuesPendingCompostForCompostActions()
|
||||||
|
{
|
||||||
|
Widget widget = mock(Widget.class);
|
||||||
|
when(client.getSelectedWidget()).thenReturn(widget);
|
||||||
|
when(widget.getItemId()).thenReturn(ItemID.ULTRACOMPOST);
|
||||||
|
|
||||||
|
MenuOptionClicked inspectPatchAction = mock(MenuOptionClicked.class);
|
||||||
|
when(inspectPatchAction.getMenuAction()).thenReturn(MenuAction.WIDGET_TARGET_ON_GAME_OBJECT);
|
||||||
|
when(inspectPatchAction.getId()).thenReturn(PATCH_ID);
|
||||||
|
when(inspectPatchAction.getParam0()).thenReturn(1);
|
||||||
|
when(inspectPatchAction.getParam1()).thenReturn(2);
|
||||||
|
|
||||||
|
compostTracker.onMenuOptionClicked(inspectPatchAction);
|
||||||
|
CompostTracker.PendingCompost actual = compostTracker.pendingCompostActions.get(farmingPatch);
|
||||||
|
|
||||||
|
assertThat(actual.getFarmingPatch(), is(farmingPatch));
|
||||||
|
assertThat(actual.getPatchLocation(), is(new WorldPoint(1, 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onMenuOptionClicked_queuesPendingCompostForFertileSoilSpellActions()
|
||||||
|
{
|
||||||
|
Widget widget = mock(Widget.class);
|
||||||
|
when(client.getSelectedWidget()).thenReturn(widget);
|
||||||
|
when(widget.getId()).thenReturn(WidgetInfo.SPELL_LUNAR_FERTILE_SOIL.getPackedId());
|
||||||
|
|
||||||
|
MenuOptionClicked inspectPatchAction = mock(MenuOptionClicked.class);
|
||||||
|
when(inspectPatchAction.getMenuAction()).thenReturn(MenuAction.WIDGET_TARGET_ON_GAME_OBJECT);
|
||||||
|
when(inspectPatchAction.getId()).thenReturn(PATCH_ID);
|
||||||
|
when(inspectPatchAction.getParam0()).thenReturn(1);
|
||||||
|
when(inspectPatchAction.getParam1()).thenReturn(2);
|
||||||
|
|
||||||
|
compostTracker.onMenuOptionClicked(inspectPatchAction);
|
||||||
|
CompostTracker.PendingCompost actual = compostTracker.pendingCompostActions.get(farmingPatch);
|
||||||
|
|
||||||
|
assertThat(actual.getFarmingPatch(), is(farmingPatch));
|
||||||
|
assertThat(actual.getPatchLocation(), is(new WorldPoint(1, 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onChatMessage_ignoresInvalidTypes()
|
||||||
|
{
|
||||||
|
ChatMessage chatEvent = mock(ChatMessage.class);
|
||||||
|
when(chatEvent.getType()).thenReturn(ChatMessageType.PUBLICCHAT);
|
||||||
|
|
||||||
|
compostTracker.onChatMessage(chatEvent);
|
||||||
|
|
||||||
|
verifyNoInteractions(client);
|
||||||
|
verifyNoInteractions(farmingWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onChatMessage_handlesInspectMessages()
|
||||||
|
{
|
||||||
|
ChatMessage chatEvent = mock(ChatMessage.class);
|
||||||
|
when(chatEvent.getType()).thenReturn(ChatMessageType.SPAM);
|
||||||
|
when(chatEvent.getMessage()).thenReturn("This is a tree patch. The soil has been treated with ultracompost. The patch is empty and weeded.");
|
||||||
|
|
||||||
|
compostTracker.pendingCompostActions.put(farmingPatch, new CompostTracker.PendingCompost(Instant.MAX, worldPoint, farmingPatch));
|
||||||
|
compostTracker.onChatMessage(chatEvent);
|
||||||
|
|
||||||
|
verify(configManager).setRSProfileConfiguration("timetracking", "MOCK.compost", CompostState.ULTRACOMPOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onChatMessage_handlesBucketUseMessages()
|
||||||
|
{
|
||||||
|
ChatMessage chatEvent = mock(ChatMessage.class);
|
||||||
|
when(chatEvent.getType()).thenReturn(ChatMessageType.SPAM);
|
||||||
|
when(chatEvent.getMessage()).thenReturn("You treat the herb patch with compost.");
|
||||||
|
|
||||||
|
compostTracker.pendingCompostActions.put(farmingPatch, new CompostTracker.PendingCompost(Instant.MAX, worldPoint, farmingPatch));
|
||||||
|
compostTracker.onChatMessage(chatEvent);
|
||||||
|
|
||||||
|
verify(configManager).setRSProfileConfiguration("timetracking", "MOCK.compost", CompostState.COMPOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onChatMessage_handlesFertileSoilMessages()
|
||||||
|
{
|
||||||
|
ChatMessage chatEvent = mock(ChatMessage.class);
|
||||||
|
when(chatEvent.getType()).thenReturn(ChatMessageType.SPAM);
|
||||||
|
when(chatEvent.getMessage()).thenReturn("The allotment has been treated with supercompost.");
|
||||||
|
|
||||||
|
compostTracker.pendingCompostActions.put(farmingPatch, new CompostTracker.PendingCompost(Instant.MAX, worldPoint, farmingPatch));
|
||||||
|
compostTracker.onChatMessage(chatEvent);
|
||||||
|
|
||||||
|
verify(configManager).setRSProfileConfiguration("timetracking", "MOCK.compost", CompostState.SUPERCOMPOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -68,8 +68,7 @@ public abstract class RSItemCompositionMixin implements RSItemComposition
|
|||||||
@MethodHook(value = "post", end = true)
|
@MethodHook(value = "post", end = true)
|
||||||
public void post()
|
public void post()
|
||||||
{
|
{
|
||||||
final PostItemComposition event = new PostItemComposition();
|
final PostItemComposition event = new PostItemComposition(this);
|
||||||
event.setItemComposition(this);
|
|
||||||
client.getCallbacks().post(event);
|
client.getCallbacks().post(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.runelite.mixins;
|
|||||||
|
|
||||||
import net.runelite.api.IterableHashTable;
|
import net.runelite.api.IterableHashTable;
|
||||||
import net.runelite.api.Node;
|
import net.runelite.api.Node;
|
||||||
|
import net.runelite.api.events.PostObjectComposition;
|
||||||
import net.runelite.api.mixins.Copy;
|
import net.runelite.api.mixins.Copy;
|
||||||
import net.runelite.api.mixins.Inject;
|
import net.runelite.api.mixins.Inject;
|
||||||
import net.runelite.api.mixins.MethodHook;
|
import net.runelite.api.mixins.MethodHook;
|
||||||
@@ -36,6 +37,14 @@ public abstract class RSObjectCompositionMixin implements RSObjectComposition
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@MethodHook(value = "postDecode", end = true)
|
||||||
|
public void postDecode()
|
||||||
|
{
|
||||||
|
final PostObjectComposition event = new PostObjectComposition(this);
|
||||||
|
client.getCallbacks().post(event);
|
||||||
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public int getAccessBitMask()
|
public int getAccessBitMask()
|
||||||
|
|||||||
@@ -1366,6 +1366,12 @@ public interface RSClient extends RSGameEngine, Client
|
|||||||
@Import("meslayerContinueWidget")
|
@Import("meslayerContinueWidget")
|
||||||
Widget getMessageContinueWidget();
|
Widget getMessageContinueWidget();
|
||||||
|
|
||||||
|
@Import("playingJingle")
|
||||||
|
boolean isPlayingJingle();
|
||||||
|
|
||||||
|
@Import("musicTrackGroupId")
|
||||||
|
int getMusicCurrentTrackId();
|
||||||
|
|
||||||
@Import("musicPlayerStatus")
|
@Import("musicPlayerStatus")
|
||||||
void setMusicPlayerStatus(int var0);
|
void setMusicPlayerStatus(int var0);
|
||||||
|
|
||||||
@@ -1579,6 +1585,9 @@ public interface RSClient extends RSGameEngine, Client
|
|||||||
@Import("ObjectDefinition_cached")
|
@Import("ObjectDefinition_cached")
|
||||||
RSEvictingDualNodeHashTable getObjectDefinitionCache();
|
RSEvictingDualNodeHashTable getObjectDefinitionCache();
|
||||||
|
|
||||||
|
@Import("ObjectDefinition_cached")
|
||||||
|
RSEvictingDualNodeHashTable getObjectCompositionCache();
|
||||||
|
|
||||||
@Import("ObjectDefinition_cachedModelData")
|
@Import("ObjectDefinition_cachedModelData")
|
||||||
RSEvictingDualNodeHashTable getObjectDefinitionModelDataCache();
|
RSEvictingDualNodeHashTable getObjectDefinitionModelDataCache();
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ public final class Client extends GameEngine implements Usernamed, OAuthApi {
|
|||||||
)
|
)
|
||||||
public static int field779;
|
public static int field779;
|
||||||
@ObfuscatedName("ss")
|
@ObfuscatedName("ss")
|
||||||
static boolean field746;
|
@Export("playingJingle")
|
||||||
|
static boolean playingJingle;
|
||||||
@ObfuscatedName("tz")
|
@ObfuscatedName("tz")
|
||||||
@ObfuscatedGetter(
|
@ObfuscatedGetter(
|
||||||
intValue = 599158567
|
intValue = 599158567
|
||||||
@@ -1583,7 +1584,7 @@ public final class Client extends GameEngine implements Usernamed, OAuthApi {
|
|||||||
destinationY = 0; // L: 571
|
destinationY = 0; // L: 571
|
||||||
minimapState = 0; // L: 578
|
minimapState = 0; // L: 578
|
||||||
currentTrackGroupId = -1; // L: 579
|
currentTrackGroupId = -1; // L: 579
|
||||||
field746 = false; // L: 580
|
playingJingle = false; // L: 580
|
||||||
soundEffectCount = 0; // L: 586
|
soundEffectCount = 0; // L: 586
|
||||||
soundEffectIds = new int[50]; // L: 587
|
soundEffectIds = new int[50]; // L: 587
|
||||||
queuedSoundEffectLoops = new int[50]; // L: 588
|
queuedSoundEffectLoops = new int[50]; // L: 588
|
||||||
@@ -1850,7 +1851,7 @@ public final class Client extends GameEngine implements Usernamed, OAuthApi {
|
|||||||
var2 = false; // L: 993
|
var2 = false; // L: 993
|
||||||
}
|
}
|
||||||
|
|
||||||
if (var2 && field746 && KeyHandler.pcmPlayer0 != null) { // L: 996 997
|
if (var2 && playingJingle && KeyHandler.pcmPlayer0 != null) { // L: 996 997
|
||||||
KeyHandler.pcmPlayer0.tryDiscard();
|
KeyHandler.pcmPlayer0.tryDiscard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public final class ItemLayer {
|
|||||||
System.gc(); // L: 2932
|
System.gc(); // L: 2932
|
||||||
Actor.method2183(2); // L: 2933
|
Actor.method2183(2); // L: 2933
|
||||||
Client.currentTrackGroupId = -1; // L: 2934
|
Client.currentTrackGroupId = -1; // L: 2934
|
||||||
Client.field746 = false; // L: 2935
|
Client.playingJingle = false; // L: 2935
|
||||||
ClientPreferences.method2259(); // L: 2936
|
ClientPreferences.method2259(); // L: 2936
|
||||||
Decimator.updateGameState(10); // L: 2937
|
Decimator.updateGameState(10); // L: 2937
|
||||||
} // L: 2938
|
} // L: 2938
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class Players {
|
|||||||
static void method2419(int var0, int var1) {
|
static void method2419(int var0, int var1) {
|
||||||
if (class131.clientPreferences.method2321() != 0 && var0 != -1) { // L: 3622
|
if (class131.clientPreferences.method2321() != 0 && var0 != -1) { // L: 3622
|
||||||
class18.method266(Message.archive11, var0, 0, class131.clientPreferences.method2321(), false); // L: 3623
|
class18.method266(Message.archive11, var0, 0, class131.clientPreferences.method2321(), false); // L: 3623
|
||||||
Client.field746 = true; // L: 3624
|
Client.playingJingle = true; // L: 3624
|
||||||
}
|
}
|
||||||
|
|
||||||
} // L: 3626
|
} // L: 3626
|
||||||
|
|||||||
@@ -214,11 +214,11 @@ public class SoundCache {
|
|||||||
)
|
)
|
||||||
@Export("playSong")
|
@Export("playSong")
|
||||||
static void playSong(int var0) {
|
static void playSong(int var0) {
|
||||||
if (var0 == -1 && !Client.field746) { // L: 3608
|
if (var0 == -1 && !Client.playingJingle) { // L: 3608
|
||||||
class273.midiPcmStream.clear(); // L: 3610
|
class273.midiPcmStream.clear(); // L: 3610
|
||||||
class273.musicPlayerStatus = 1; // L: 3611
|
class273.musicPlayerStatus = 1; // L: 3611
|
||||||
class273.musicTrackArchive = null; // L: 3612
|
class273.musicTrackArchive = null; // L: 3612
|
||||||
} else if (var0 != -1 && var0 != Client.currentTrackGroupId && class131.clientPreferences.method2321() != 0 && !Client.field746) { // L: 3615
|
} else if (var0 != -1 && var0 != Client.currentTrackGroupId && class131.clientPreferences.method2321() != 0 && !Client.playingJingle) { // L: 3615
|
||||||
NPCComposition.method3530(2, class16.archive6, var0, 0, class131.clientPreferences.method2321(), false); // L: 3616
|
NPCComposition.method3530(2, class16.archive6, var0, 0, class131.clientPreferences.method2321(), false); // L: 3616
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,12 +96,12 @@ public class class1 implements Callable {
|
|||||||
if (var0 != class131.clientPreferences.method2321()) { // L: 12009
|
if (var0 != class131.clientPreferences.method2321()) { // L: 12009
|
||||||
if (class131.clientPreferences.method2321() == 0 && Client.currentTrackGroupId != -1) { // L: 12010
|
if (class131.clientPreferences.method2321() == 0 && Client.currentTrackGroupId != -1) { // L: 12010
|
||||||
class18.method266(class16.archive6, Client.currentTrackGroupId, 0, var0, false); // L: 12011
|
class18.method266(class16.archive6, Client.currentTrackGroupId, 0, var0, false); // L: 12011
|
||||||
Client.field746 = false; // L: 12012
|
Client.playingJingle = false; // L: 12012
|
||||||
} else if (var0 == 0) { // L: 12014
|
} else if (var0 == 0) { // L: 12014
|
||||||
class273.midiPcmStream.clear(); // L: 12016
|
class273.midiPcmStream.clear(); // L: 12016
|
||||||
class273.musicPlayerStatus = 1; // L: 12017
|
class273.musicPlayerStatus = 1; // L: 12017
|
||||||
class273.musicTrackArchive = null; // L: 12018
|
class273.musicTrackArchive = null; // L: 12018
|
||||||
Client.field746 = false; // L: 12020
|
Client.playingJingle = false; // L: 12020
|
||||||
} else if (class273.musicPlayerStatus != 0) { // L: 12023
|
} else if (class273.musicPlayerStatus != 0) { // L: 12023
|
||||||
class273.musicTrackVolume = var0;
|
class273.musicTrackVolume = var0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -139,12 +139,12 @@ public class class136 extends class144 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Client.field746 && !class307.method5789()) { // L: 3546
|
if (Client.playingJingle && !class307.method5789()) { // L: 3546
|
||||||
if (class131.clientPreferences.method2321() != 0 && Client.currentTrackGroupId != -1) { // L: 3547
|
if (class131.clientPreferences.method2321() != 0 && Client.currentTrackGroupId != -1) { // L: 3547
|
||||||
class18.method266(class16.archive6, Client.currentTrackGroupId, 0, class131.clientPreferences.method2321(), false);
|
class18.method266(class16.archive6, Client.currentTrackGroupId, 0, class131.clientPreferences.method2321(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.field746 = false; // L: 3548
|
Client.playingJingle = false; // L: 3548
|
||||||
}
|
}
|
||||||
|
|
||||||
} // L: 3550
|
} // L: 3550
|
||||||
|
|||||||
Reference in New Issue
Block a user