First commit of group-ironmen-master directory.

This commit is contained in:
2025-10-27 08:25:16 +08:00
commit a8467389ef
26390 changed files with 35396 additions and 0 deletions

11
group-ironmen-master/cache/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
runelite
node_modules
item-data
item-images
.#*
items_need_images.csv
item_data.json
cache
map-data
output_files
output.dzi

128
group-ironmen-master/cache/Cache.java vendored Normal file
View File

@@ -0,0 +1,128 @@
package net.runelite.cache;
import net.runelite.cache.definitions.loaders.ModelLoader;
import net.runelite.cache.definitions.providers.ModelProvider;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Store;
import net.runelite.cache.item.ItemSpriteFactory;
import org.apache.commons.cli.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Cache {
public static void main(String[] args) throws IOException {
Options options = new Options();
options.addOption("c", "cache", true, "cache base");
options.addOption(null, "ids", true, "csv file with item ids to create images from");
options.addOption(null, "output", true, "directory to dump item model images to");
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;
}
String cache = cmd.getOptionValue("cache");
Store store = loadStore(cache);
if (cmd.hasOption("output") && cmd.hasOption("ids")) {
String outputDir = cmd.getOptionValue("output");
String imageIdsFile = cmd.getOptionValue("ids");
if (outputDir == null) {
System.err.println("Item image directory must be specified");
return;
}
if (imageIdsFile == null) {
System.err.println("Image ID CSV file must be specified");
return;
}
List<Integer> itemIds = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(imageIdsFile))) {
String line = br.readLine();
if (line != null) {
String[] values = line.split(",");
for (String value : values) {
Integer itemId = Integer.parseInt(value);
itemIds.add(itemId);
}
}
}
System.out.println("Dumping item model images to " + outputDir);
dumpItemModelImages(store, new File(outputDir), itemIds);
} else {
System.err.println("Nothing to do");
}
}
private static Store loadStore(String cache) throws IOException {
Store store = new Store(new File(cache));
store.load();
return store;
}
private static void dumpItemModelImages(Store store, File outputDir, List<Integer> itemIds) throws IOException {
ItemManager dumper = new ItemManager(store);
dumper.load();
ModelProvider modelProvider = modelId -> {
Index models = store.getIndex(IndexType.MODELS);
Archive archive = models.getArchive(modelId);
byte[] data = archive.decompress(store.getStorage().loadArchive(archive));
return new ModelLoader().load(modelId, data);
};
SpriteManager spriteManager = new SpriteManager(store);
spriteManager.load();
TextureManager textureManager = new TextureManager(store);
textureManager.load();
if (!outputDir.exists()) {
outputDir.mkdir();
}
for (Integer itemId : itemIds) {
try {
final int border = 1;
final int shadowColor = 0x111111;
final boolean noted = false;
BufferedImage sprite = ItemSpriteFactory.createSprite(
dumper,
modelProvider,
spriteManager,
textureManager,
itemId,
1,
border,
shadowColor,
noted);
File out = new File(outputDir, itemId + ".png");
assert sprite != null;
ImageIO.write(sprite, "PNG", out);
} catch (Exception ex) {
System.err.println("error dumping item " + itemId);
}
}
}
}

View File

@@ -0,0 +1,226 @@
package net.runelite.cache;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.Data;
import net.runelite.cache.definitions.EnumDefinition;
import net.runelite.cache.definitions.ItemDefinition;
import net.runelite.cache.definitions.ScriptDefinition;
import net.runelite.cache.definitions.StructDefinition;
import net.runelite.cache.definitions.loaders.EnumLoader;
import net.runelite.cache.definitions.loaders.ScriptLoader;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.ArchiveFiles;
import net.runelite.cache.fs.FSFile;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Storage;
import net.runelite.cache.fs.Store;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CollectionLogDumper
{
private static String outputDirectory;
private static final List<Integer> COLLECTION_LOG_TAB_STRUCT_IDS = ImmutableList.of(
471, // Bosses
472, // Raids
473, // Clues
474, // Minigames
475 // Other
);
private static final int COLLECTION_LOG_TAB_ENUM_PARAM_ID = 683;
private static final int COLLECTION_LOG_PAGE_NAME_PARAM_ID = 689;
private static final int COLLECTION_LOG_PAGE_ITEMS_ENUM_PARAM_ID = 690;
private static final int COLLECTION_CATEGORY_COUNT_SCRIPT = 2735;
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Data
static class CollectionLogItem
{
public Integer id;
public String name;
}
@Data
static class CollectionLogPage
{
public String name;
public List<String> completion_labels = new ArrayList<>();
public List<CollectionLogItem> items = new ArrayList<>();
}
@Data
static class CollectionLogTab
{
public Integer tabId;
public List<CollectionLogPage> pages = new ArrayList<>();
}
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("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");
outputDirectory = cmd.getOptionValue("outputdir");
File base = new File(cacheDirectory);
File outDir = new File(outputDirectory);
outDir.mkdirs();
try (Store store = new Store(base))
{
store.load();
Storage storage = store.getStorage();
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.ENUM.getId());
byte[] archiveData = storage.loadArchive(archive);
ArchiveFiles files = archive.getFiles(archiveData);
EnumLoader enumLoader = new EnumLoader();
StructManager structManager = new StructManager(store);
structManager.load();
Index scriptIndex = store.getIndex(IndexType.CLIENTSCRIPT);
Archive collectionCategoryCountScript = scriptIndex.getArchive(COLLECTION_CATEGORY_COUNT_SCRIPT);
byte[] collectionCategoryCountScriptData = storage.loadArchive(collectionCategoryCountScript);
FSFile collectionCategoryCountScriptFile = collectionCategoryCountScript.getFiles(collectionCategoryCountScriptData).findFile(0);
ScriptLoader scriptLoader = new ScriptLoader();
ScriptDefinition collectionCategoryCountScriptDefinition = scriptLoader.load(COLLECTION_CATEGORY_COUNT_SCRIPT, collectionCategoryCountScriptFile.getContents());
Map<Integer, List<String>> completionLabels = new HashMap<>();
String[] labelStrings = collectionCategoryCountScriptDefinition.getStringOperands();
int offset = 1;
for (Integer pageId : collectionCategoryCountScriptDefinition.getSwitches()[0].keySet())
{
List<String> labels = new ArrayList<>();
int count = 0;
// Every completion count return has 3 strings
for (int i = offset; count < 3; ++i, ++offset)
{
String label = labelStrings[i];
String previousLabel = labelStrings[i - 1];
// If the previous value is another valid label then it is part of some argument to a proc
// and we only want the first one. Example is the "High-level Gambles" which can change to not
// include the word "Gamble" on mobile.
if (label != null && !label.trim().startsWith("<") && (previousLabel == null || !previousLabel.trim().endsWith(":")))
{
++count;
// non-empty labels should always have a <col></col> tag that we can advance to avoid
// any other empty string up until then.
if (label.trim().endsWith(":"))
{
for (; labelStrings[i] == null || !labelStrings[i].trim().equals("</col>"); ++i, ++offset);
labels.add(label.trim().substring(0, label.trim().length() - 1));
}
}
}
Collections.reverse(labels);
completionLabels.put(pageId, labels);
}
ItemManager itemManager = new ItemManager(store);
itemManager.load();
List<CollectionLogTab> collectionLog = new ArrayList<>();
int tabIdx = 0;
for (Integer collectionLogTabStructId : COLLECTION_LOG_TAB_STRUCT_IDS)
{
StructDefinition tabStruct = structManager.getStruct(collectionLogTabStructId);
Integer tabEnumId = (Integer) tabStruct.getParams().get(COLLECTION_LOG_TAB_ENUM_PARAM_ID);
EnumDefinition tabEnum = getEnumDefinition(enumLoader, files, tabEnumId);
CollectionLogTab collectionLogTab = new CollectionLogTab();
collectionLogTab.tabId = tabIdx++;
collectionLog.add(collectionLogTab);
for (Integer pageStructId : tabEnum.getIntVals())
{
StructDefinition pageStruct = structManager.getStruct(pageStructId);
String pageName = (String) pageStruct.getParams().get(COLLECTION_LOG_PAGE_NAME_PARAM_ID);
Integer pageItemsEnumId = (Integer) pageStruct.getParams().get(COLLECTION_LOG_PAGE_ITEMS_ENUM_PARAM_ID);
EnumDefinition pageItemsEnum = getEnumDefinition(enumLoader, files, pageItemsEnumId);
CollectionLogPage collectionLogPage = new CollectionLogPage();
collectionLogPage.name = pageName;
collectionLogPage.completion_labels = completionLabels.getOrDefault(pageStructId, new ArrayList<>());
collectionLogTab.pages.add(collectionLogPage);
for (Integer pageItemId : pageItemsEnum.getIntVals())
{
CollectionLogItem collectionLogItem = new CollectionLogItem();
ItemDefinition item = itemManager.getItem(pageItemId);
collectionLogItem.id = item.getId();
collectionLogItem.name = item.getName();
collectionLogPage.items.add(collectionLogItem);
}
}
}
Files.asCharSink(new File(outputDirectory, "collection_log_info.json"), Charset.defaultCharset()).write(gson.toJson(collectionLog));
}
}
private static EnumDefinition getEnumDefinition(EnumLoader enumLoader, ArchiveFiles files, Integer enumId) throws IOException
{
FSFile enumFile = null;
for (FSFile file : files.getFiles())
{
if (file.getFileId() == enumId)
{
enumFile = file;
break;
}
}
if (enumFile == null)
{
throw new IOException("Unable to find enum with id " + enumId);
}
byte[] b = enumFile.getContents();
EnumDefinition enumDefinition = enumLoader.load(enumFile.getFileId(), b);
if (enumDefinition == null)
{
throw new IOException("Unable to load enum definition for enum id " + enumId);
}
return enumDefinition;
}
}

View File

@@ -0,0 +1,442 @@
/*
* 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 java.awt.image.BufferedImage;
import java.io.IOException;
import net.runelite.cache.definitions.ItemDefinition;
import net.runelite.cache.definitions.ModelDefinition;
import net.runelite.cache.definitions.providers.ItemProvider;
import net.runelite.cache.definitions.providers.ModelProvider;
import net.runelite.cache.definitions.providers.SpriteProvider;
import net.runelite.cache.definitions.providers.TextureProvider;
import net.runelite.cache.models.FaceNormal;
import net.runelite.cache.models.JagexColor;
import net.runelite.cache.models.VertexNormal;
public class ItemSpriteFactory
{
public static BufferedImage createSprite(ItemProvider itemProvider, ModelProvider modelProvider,
SpriteProvider spriteProvider, TextureProvider textureProvider,
int itemId, int quantity, int border, int shadowColor,
boolean noted) throws IOException
{
SpritePixels spritePixels = createSpritePixels(itemProvider, modelProvider, spriteProvider, textureProvider,
itemId, quantity, border, shadowColor, noted);
return spritePixels == null ? null : spritePixels.toBufferedImage();
}
private static SpritePixels createSpritePixels(ItemProvider itemProvider, ModelProvider modelProvider,
SpriteProvider spriteProvider, TextureProvider textureProvider,
int itemId, int quantity, int border, int shadowColor,
boolean noted) throws IOException
{
ItemDefinition item = itemProvider.provide(itemId);
if (quantity > 1 && item.countObj != null)
{
int stackItemID = -1;
for (int i = 0; i < 10; ++i)
{
if (quantity >= item.countCo[i] && item.countCo[i] != 0)
{
stackItemID = item.countObj[i];
}
}
if (stackItemID != -1)
{
item = itemProvider.provide(stackItemID);
}
}
Model itemModel = getModel(modelProvider, item);
if (itemModel == null)
{
return null;
}
SpritePixels auxSpritePixels = null;
if (item.notedTemplate != -1)
{
auxSpritePixels = createSpritePixels(itemProvider, modelProvider, spriteProvider, textureProvider,
item.notedID, 10, 1, 0, true);
if (auxSpritePixels == null)
{
return null;
}
}
else if (item.boughtTemplateId != -1)
{
auxSpritePixels = createSpritePixels(itemProvider, modelProvider, spriteProvider, textureProvider,
item.boughtId, quantity, border, 0, false);
if (auxSpritePixels == null)
{
return null;
}
}
else if (item.placeholderTemplateId != -1)
{
auxSpritePixels = createSpritePixels(itemProvider, modelProvider, spriteProvider, textureProvider,
item.placeholderId, quantity, 0, 0, false);
if (auxSpritePixels == null)
{
return null;
}
}
RSTextureProvider rsTextureProvider = new RSTextureProvider(textureProvider, spriteProvider);
int width = 36 * 2;
int height = 32 * 2;
SpritePixels spritePixels = new SpritePixels(width, height);
Graphics3D graphics = new Graphics3D(rsTextureProvider);
graphics.setBrightness(JagexColor.BRIGHTNESS_MAX);
graphics.setRasterBuffer(spritePixels.pixels, width, height);
graphics.reset();
graphics.setRasterClipping();
graphics.setOffset(16 * 2, 16 * 2);
graphics.rasterGouraudLowRes = false;
if (item.placeholderTemplateId != -1)
{
auxSpritePixels.drawAtOn(graphics, 0, 0);
}
int zoom2d;
// The holy symbol item for some reason needs a different zoom value otherwise it gets cut off
if (itemId == 1716 || itemId == 1718) {
zoom2d = (int) (((double) item.zoom2d) / 1.5D);
} else {
zoom2d = (int) (((double) item.zoom2d) / 1.95D);
}
if (noted)
{
zoom2d = (int) ((double) zoom2d * 1.5D);
}
else if (border == 2)
{
zoom2d = (int) ((double) zoom2d * 1.04D);
}
int var17 = zoom2d * Graphics3D.SINE[item.xan2d] >> 16;
int var18 = zoom2d * Graphics3D.COSINE[item.xan2d] >> 16;
itemModel.calculateBoundsCylinder();
itemModel.projectAndDraw(graphics, 0,
item.yan2d,
item.zan2d,
item.xan2d,
item.xOffset2d,
itemModel.modelHeight / 2 + var17 + item.yOffset2d,
var18 + item.yOffset2d);
if (item.boughtTemplateId != -1)
{
auxSpritePixels.drawAtOn(graphics, 0, 0);
}
if (border >= 1)
{
spritePixels.drawBorder(1);
}
if (border >= 2)
{
spritePixels.drawBorder(0xffffff);
}
if (shadowColor != 0)
{
spritePixels.drawShadow(shadowColor);
}
graphics.setRasterBuffer(spritePixels.pixels, width, height);
if (item.notedTemplate != -1)
{
auxSpritePixels.drawAtOn(graphics, 0, 0);
}
graphics.setRasterBuffer(graphics.graphicsPixels,
graphics.graphicsPixelsWidth,
graphics.graphicsPixelsHeight);
graphics.setRasterClipping();
graphics.rasterGouraudLowRes = true;
return spritePixels;
}
private static Model getModel(ModelProvider modelProvider, ItemDefinition item) throws IOException
{
Model itemModel;
ModelDefinition inventoryModel = modelProvider.provide(item.inventoryModel);
if (inventoryModel == null)
{
return null;
}
if (item.resizeX != 128 || item.resizeY != 128 || item.resizeZ != 128)
{
inventoryModel.resize(item.resizeX, item.resizeY, item.resizeZ);
}
if (item.colorFind != null)
{
for (int i = 0; i < item.colorFind.length; ++i)
{
inventoryModel.recolor(item.colorFind[i], item.colorReplace[i]);
}
}
if (item.textureFind != null)
{
for (int i = 0; i < item.textureFind.length; ++i)
{
inventoryModel.retexture(item.textureFind[i], item.textureReplace[i]);
}
}
itemModel = light(inventoryModel, item.ambient + 64, item.contrast + 768, -50, -10, -50);
return itemModel;
}
private static Model light(ModelDefinition def, int ambient, int contrast, int x, int y, int z)
{
def.computeNormals();
int somethingMagnitude = (int) Math.sqrt((double) (z * z + x * x + y * y));
int var7 = somethingMagnitude * contrast >> 8;
Model litModel = new Model();
litModel.faceColors1 = new int[def.faceCount];
litModel.faceColors2 = new int[def.faceCount];
litModel.faceColors3 = new int[def.faceCount];
if (def.numTextureFaces > 0 && def.textureCoords != null)
{
int[] var9 = new int[def.numTextureFaces];
int var10;
for (var10 = 0; var10 < def.faceCount; ++var10)
{
if (def.textureCoords[var10] != -1)
{
++var9[def.textureCoords[var10] & 255];
}
}
litModel.numTextureFaces = 0;
for (var10 = 0; var10 < def.numTextureFaces; ++var10)
{
if (var9[var10] > 0 && def.textureRenderTypes[var10] == 0)
{
++litModel.numTextureFaces;
}
}
litModel.texIndices1 = new int[litModel.numTextureFaces];
litModel.texIndices2 = new int[litModel.numTextureFaces];
litModel.texIndices3 = new int[litModel.numTextureFaces];
var10 = 0;
for (int i = 0; i < def.numTextureFaces; ++i)
{
if (var9[i] > 0 && def.textureRenderTypes[i] == 0)
{
litModel.texIndices1[var10] = def.texIndices1[i] & '\uffff';
litModel.texIndices2[var10] = def.texIndices2[i] & '\uffff';
litModel.texIndices3[var10] = def.texIndices3[i] & '\uffff';
var9[i] = var10++;
}
else
{
var9[i] = -1;
}
}
litModel.textureCoords = new byte[def.faceCount];
for (int i = 0; i < def.faceCount; ++i)
{
if (def.textureCoords[i] != -1)
{
litModel.textureCoords[i] = (byte) var9[def.textureCoords[i] & 255];
}
else
{
litModel.textureCoords[i] = -1;
}
}
}
for (int faceIdx = 0; faceIdx < def.faceCount; ++faceIdx)
{
byte faceType;
if (def.faceRenderTypes == null)
{
faceType = 0;
}
else
{
faceType = def.faceRenderTypes[faceIdx];
}
byte faceAlpha;
if (def.faceTransparencies == null)
{
faceAlpha = 0;
}
else
{
faceAlpha = def.faceTransparencies[faceIdx];
}
short faceTexture;
if (def.faceTextures == null)
{
faceTexture = -1;
}
else
{
faceTexture = def.faceTextures[faceIdx];
}
if (faceAlpha == -2)
{
faceType = 3;
}
if (faceAlpha == -1)
{
faceType = 2;
}
VertexNormal vertexNormal;
int tmp;
FaceNormal faceNormal;
if (faceTexture == -1)
{
if (faceType != 0)
{
if (faceType == 1)
{
faceNormal = def.faceNormals[faceIdx];
tmp = (y * faceNormal.y + z * faceNormal.z + x * faceNormal.x) / (var7 / 2 + var7) + ambient;
litModel.faceColors1[faceIdx] = method2608(def.faceColors[faceIdx] & '\uffff', tmp);
litModel.faceColors3[faceIdx] = -1;
}
else if (faceType == 3)
{
litModel.faceColors1[faceIdx] = 128;
litModel.faceColors3[faceIdx] = -1;
}
else
{
litModel.faceColors3[faceIdx] = -2;
}
}
else
{
int var15 = def.faceColors[faceIdx] & '\uffff';
vertexNormal = def.vertexNormals[def.faceIndices1[faceIdx]];
tmp = (y * vertexNormal.y + z * vertexNormal.z + x * vertexNormal.x) / (var7 * vertexNormal.magnitude) + ambient;
litModel.faceColors1[faceIdx] = method2608(var15, tmp);
vertexNormal = def.vertexNormals[def.faceIndices2[faceIdx]];
tmp = (y * vertexNormal.y + z * vertexNormal.z + x * vertexNormal.x) / (var7 * vertexNormal.magnitude) + ambient;
litModel.faceColors2[faceIdx] = method2608(var15, tmp);
vertexNormal = def.vertexNormals[def.faceIndices3[faceIdx]];
tmp = (y * vertexNormal.y + z * vertexNormal.z + x * vertexNormal.x) / (var7 * vertexNormal.magnitude) + ambient;
litModel.faceColors3[faceIdx] = method2608(var15, tmp);
}
}
else if (faceType != 0)
{
if (faceType == 1)
{
faceNormal = def.faceNormals[faceIdx];
tmp = (y * faceNormal.y + z * faceNormal.z + x * faceNormal.x) / (var7 / 2 + var7) + ambient;
litModel.faceColors1[faceIdx] = bound2to126(tmp);
litModel.faceColors3[faceIdx] = -1;
}
else
{
litModel.faceColors3[faceIdx] = -2;
}
}
else
{
vertexNormal = def.vertexNormals[def.faceIndices1[faceIdx]];
tmp = (y * vertexNormal.y + z * vertexNormal.z + x * vertexNormal.x) / (var7 * vertexNormal.magnitude) + ambient;
litModel.faceColors1[faceIdx] = bound2to126(tmp);
vertexNormal = def.vertexNormals[def.faceIndices2[faceIdx]];
tmp = (y * vertexNormal.y + z * vertexNormal.z + x * vertexNormal.x) / (var7 * vertexNormal.magnitude) + ambient;
litModel.faceColors2[faceIdx] = bound2to126(tmp);
vertexNormal = def.vertexNormals[def.faceIndices3[faceIdx]];
tmp = (y * vertexNormal.y + z * vertexNormal.z + x * vertexNormal.x) / (var7 * vertexNormal.magnitude) + ambient;
litModel.faceColors3[faceIdx] = bound2to126(tmp);
}
}
litModel.verticesCount = def.vertexCount;
litModel.verticesX = def.vertexX;
litModel.verticesY = def.vertexY;
litModel.verticesZ = def.vertexZ;
litModel.indicesCount = def.faceCount;
litModel.indices1 = def.faceIndices1;
litModel.indices2 = def.faceIndices2;
litModel.indices3 = def.faceIndices3;
litModel.facePriorities = def.faceRenderPriorities;
litModel.faceTransparencies = def.faceTransparencies;
litModel.faceTextures = def.faceTextures;
return litModel;
}
static int method2608(int var0, int var1)
{
var1 = ((var0 & 127) * var1) >> 7;
var1 = bound2to126(var1);
return (var0 & 65408) + var1;
}
static int bound2to126(int var0)
{
if (var0 < 2)
{
var0 = 2;
}
else if (var0 > 126)
{
var0 = 126;
}
return var0;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
package net.runelite.cache;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import net.runelite.cache.definitions.AreaDefinition;
import net.runelite.cache.definitions.FontDefinition;
import net.runelite.cache.definitions.SpriteDefinition;
import net.runelite.cache.definitions.WorldMapElementDefinition;
import net.runelite.cache.fs.Store;
import net.runelite.cache.region.Position;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class MapLabelDumper
{
private static String outputDirectory;
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("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");
outputDirectory = cmd.getOptionValue("outputdir");
File base = new File(cacheDirectory);
File outDir = new File(outputDirectory);
outDir.mkdirs();
try (Store store = new Store(base))
{
store.load();
WorldMapManager worldMapManager = new WorldMapManager(store);
worldMapManager.load();
AreaManager areas = new AreaManager(store);
areas.load();
FontManager fonts = new FontManager(store);
fonts.load();
SpriteManager sprites = new SpriteManager(store);
sprites.load();
List<Object[]> result = new ArrayList<>();
FontName[] fontSizes = new FontName[]{FontName.VERDANA_11, FontName.VERDANA_13, FontName.VERDANA_15};
List<WorldMapElementDefinition> elements = worldMapManager.getElements();
int x = 0;
for (WorldMapElementDefinition element : elements)
{
AreaDefinition area = areas.getArea(element.getAreaDefinitionId());
Position worldPosition = element.getWorldPosition();
if (area == null || area.getName() == null)
{
continue;
}
result.add(new Object[]{
worldPosition.getX(),
worldPosition.getY(),
worldPosition.getZ()
});
FontName fontSize = fontSizes[area.getTextScale()];
FontDefinition font = fonts.findFontByName(fontSize.getName());
String areaLabel = area.getName();
String[] lines = areaLabel.split("<br>");
int ascent = 0;
int startImageWidth = 0;
for (String line : lines)
{
startImageWidth = Math.max(startImageWidth, font.stringWidth(line));
}
startImageWidth += 200;
int startImageHeight = 300;
BufferedImage image = new BufferedImage(startImageWidth, startImageHeight, BufferedImage.TYPE_INT_ARGB);
for (String line : lines)
{
int advance = 0;
int stringWidth = font.stringWidth(line);
for (int i = 0; i < line.length(); ++i)
{
char c = line.charAt(i);
SpriteDefinition sprite = sprites.findSpriteByArchiveName(fontSize.getName(), c);
if (sprite.getWidth() != 0 && sprite.getHeight() != 0)
{
blitGlyph(image,
advance + (startImageWidth / 2) - (stringWidth / 2),
ascent + (startImageHeight / 2),
area.getTextColor(),
sprite
);
}
advance += font.getAdvances()[c];
}
ascent += font.getAscent() / 2;
}
int imageTop = 0;
int imageBottom = 0;
for (int y = 0; y < startImageHeight; ++y) {
boolean lineHasPixels = false;
for (int xx = 0; xx < startImageWidth; ++xx)
{
if (image.getRGB(xx, y) != 0)
{
lineHasPixels = true;
}
}
if (lineHasPixels)
{
if (imageTop == 0)
{
imageTop = y;
}
else
{
imageBottom = Math.max(imageBottom, y);
}
}
}
imageBottom += 1;
int imageLeft = 0;
int imageRight = 0;
for (int xx = 0; xx < startImageWidth; ++xx)
{
boolean columnHasPixels = false;
for (int y = 0; y < startImageHeight; ++y)
{
if (image.getRGB(xx, y) != 0)
{
columnHasPixels = true;
}
}
if (columnHasPixels)
{
if (imageLeft == 0)
{
imageLeft = xx;
}
else
{
imageRight = Math.max(imageRight, xx);
}
}
}
imageRight += 1;
int imageHeight = imageBottom - imageTop;
int imageWidth = imageRight - imageLeft;
BufferedImage finalImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = finalImage.createGraphics();
g.drawImage(
image,
0,
0,
imageWidth,
imageHeight,
imageLeft,
imageTop,
imageRight,
imageBottom,
null
);
File imageFile = new File(outDir, "" + (x++) + ".png");
ImageIO.write(finalImage, "png", imageFile);
}
try {
Gson gson = new Gson();
File jsonFile = new File(outDir, "map-labels.json");
FileWriter writer = new FileWriter(jsonFile);
gson.toJson(result, writer);
writer.flush();
writer.close();
} catch (Exception ex) {
log.error("Failed to write map-labels.json", ex);
}
}
}
private static void blitGlyph(BufferedImage dst, int x, int y, int color, SpriteDefinition glyph)
{
int[] pixels = glyph.getPixels();
int[] shadowPixels = new int[pixels.length];
for (int i = 0; i < pixels.length; ++i)
{
if (pixels[i] != 0)
{
pixels[i] = color;
shadowPixels[i] = 0xFF000000;
}
}
SpriteDefinition shadow = new SpriteDefinition();
shadow.setPixels(shadowPixels);
shadow.setOffsetX(glyph.getOffsetX());
shadow.setOffsetY(glyph.getOffsetY());
shadow.setWidth(glyph.getWidth());
shadow.setHeight(glyph.getHeight());
blitIcon(dst, x + 1, y + 1, shadow);
blitIcon(dst, x, y, glyph);
}
private static void blitIcon(BufferedImage dst, int x, int y, SpriteDefinition sprite)
{
x += sprite.getOffsetX();
y += sprite.getOffsetY();
int ymin = Math.max(0, -y);
int ymax = Math.min(sprite.getHeight(), dst.getHeight() - y);
int xmin = Math.max(0, -x);
int xmax = Math.min(sprite.getWidth(), dst.getWidth() - x);
for (int yo = ymin; yo < ymax; yo++)
{
for (int xo = xmin; xo < xmax; xo++)
{
int rgb = sprite.getPixels()[xo + (yo * sprite.getWidth())];
if (rgb != 0)
{
dst.setRGB(x + xo, y + yo, rgb | 0xFF000000);
}
}
}
}
}

1259
group-ironmen-master/cache/package-lock.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

19
group-ironmen-master/cache/package.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "osrs-cache",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"update": "node update.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"async": "^3.2.3",
"axios": "^0.26.1",
"glob": "^8.0.1",
"sharp": "^0.31.1",
"unzipper": "^0.12.3",
"xml2js": "^0.4.23"
}
}

513
group-ironmen-master/cache/update.js vendored Normal file
View File

@@ -0,0 +1,513 @@
const child_process = require('child_process');
const fs = require('fs');
const xml2js = require('xml2js');
const glob = require('glob');
const nAsync = require('async');
const path = require('path');
const axios = require('axios');
const sharp = require('sharp');
const unzipper = require('unzipper');
// NOTE: sharp will keep some files open and prevent them from being deleted
sharp.cache(false);
const xmlParser = new xml2js.Parser();
const xmlBuilder = new xml2js.Builder();
const runelitePath = './runelite';
const cacheProjectPath = `${runelitePath}/cache`;
const cachePomPath = `${cacheProjectPath}/pom.xml`;
const cacheJarOutputDir = `${cacheProjectPath}/target`;
const osrsCacheDirectory = './cache/cache';
const siteItemDataPath = '../site/public/data/item_data.json';
const siteMapIconMetaPath = "../site/public/data/map_icons.json";
const siteMapLabelMetaPath = "../site/public/data/map_labels.json";
const siteItemImagesPath = '../site/public/icons/items';
const siteMapImagesPath = '../site/public/map';
const siteMapLabelsPath = '../site/public/map/labels';
const siteMapIconPath = "../site/public/map/icons/map_icons.webp";
const tileSize = 256;
function exec(command, options) {
console.log(command);
options = options || {};
options.stdio = 'inherit';
try {
child_process.execSync(command, options);
} catch (err) {
console.log(err);
process.exit(1);
}
}
async function retry(fn, skipLast) {
const attempts = 10;
for (let i = 0; i < attempts; ++i) {
try {
await fn();
return;
} catch (ex) {
await new Promise(resolve => setTimeout(resolve, 100));
if (i === (attempts - 1) && skipLast) {
console.error(ex);
}
}
}
if (!skipLast) {
fn();
}
}
async function setMainClassInCachePom(mainClass) {
console.log(`Setting mainClass of ${cachePomPath} to ${mainClass}`);
xmlParser.reset();
const cachePomData = fs.readFileSync(cachePomPath, 'utf8');
const cachePom = await xmlParser.parseStringPromise(cachePomData);
const plugins = cachePom.project.build[0].plugins[0].plugin;
const mavenAssemblyPlugin = plugins.find((plugin) => plugin.artifactId[0] === 'maven-assembly-plugin');
const configuration = mavenAssemblyPlugin.configuration[0];
configuration.archive = [{ manifest: [{ mainClass: [mainClass] }] }];
const cachePomResult = xmlBuilder.buildObject(cachePom);
fs.writeFileSync(cachePomPath, cachePomResult);
}
function execRuneliteCache(params) {
const jars = glob.sync(`${cacheJarOutputDir}/cache-*-jar-with-dependencies.jar`);
let cacheJar = jars[0];
let cacheJarmtime = fs.statSync(cacheJar).mtime;
for (const jar of jars) {
const mtime = fs.statSync(jar).mtime;
if (mtime > cacheJarmtime) {
cacheJarmtime = mtime;
cacheJar = jar;
}
}
const cmd = `java -Xmx8g -jar ${cacheJar} ${params}`;
exec(cmd);
}
async function readAllItemFiles() {
const itemFiles = glob.sync(`./item-data/*.json`);
const result = {};
const q = nAsync.queue((itemFile, callback) => {
fs.promises.readFile(itemFile, 'utf8').then((itemFileData) => {
const item = JSON.parse(itemFileData);
if (isNaN(item.id)) console.log(item);
result[item.id] = item;
callback();
});
}, 50);
for (const itemFile of itemFiles) {
q.push(itemFile);
}
await q.drain();
return result;
}
function buildCacheProject() {
exec(`mvn install -Dmaven.test.skip=true -f pom.xml`, { cwd: cacheProjectPath });
}
async function setupRunelite() {
console.log('Step: Setting up runelite');
if (!fs.existsSync(runelitePath)) {
exec(`git clone "git@github.com:runelite/runelite.git"`);
}
exec(`git fetch origin master`, { cwd: runelitePath });
exec(`git reset --hard origin/master`, { cwd: runelitePath });
}
async function dumpItemData() {
console.log('\nStep: Unpacking item data from cache');
await setMainClassInCachePom('net.runelite.cache.Cache');
buildCacheProject();
execRuneliteCache(`-c ${osrsCacheDirectory} -items ./item-data`);
}
async function getNonAlchableItemNames() {
console.log('\nStep: Fetching unalchable items from wiki');
const nonAlchableItemNames = new Set();
let cmcontinue = '';
do {
const url = `https://oldschool.runescape.wiki/api.php?cmtitle=Category:Items_that_cannot_be_alchemised&action=query&list=categorymembers&format=json&cmlimit=500&cmcontinue=${cmcontinue}`;
const response = await axios.get(url);
const itemNames = response.data.query.categorymembers.map((member) => member.title).filter((title) => !title.startsWith('File:') && !title.startsWith('Category:'));
itemNames.forEach((name) => nonAlchableItemNames.add(name));
cmcontinue = response.data?.continue?.cmcontinue || null;
} while(cmcontinue);
return nonAlchableItemNames;
}
async function buildItemDataJson() {
console.log('\nStep: Build item_data.json');
const items = await readAllItemFiles();
const includedItems = {};
const allIncludedItemIds = new Set();
for (const [itemId, item] of Object.entries(items)) {
if (item.name && item.name.trim().toLowerCase() !== 'null') {
const includedItem = {
name: item.name,
highalch: Math.floor(item.cost * 0.6)
};
const stackedList = [];
if (item.countCo && item.countObj && item.countCo.length > 0 && item.countObj.length > 0) {
for (let i = 0; i < item.countCo.length; ++i) {
const stackBreakPoint = item.countCo[i];
const stackedItemId = item.countObj[i];
if (stackBreakPoint > 0 && stackedItemId === 0) {
console.log(`${itemId}: Item has a stack breakpoint without an associated item id for that stack.`);
} else if (stackBreakPoint > 0 && stackedItemId > 0) {
allIncludedItemIds.add(stackedItemId);
stackedList.push([stackBreakPoint, stackedItemId]);
}
}
if (stackedList.length > 0) {
includedItem.stacks = stackedList;
}
}
allIncludedItemIds.add(item.id);
includedItems[itemId] = includedItem;
}
}
const nonAlchableItemNames = await getNonAlchableItemNames();
let itemsMadeNonAlchable = 0;
for (const item of Object.values(includedItems)) {
const itemName = item.name;
if (nonAlchableItemNames.has(itemName)) {
// NOTE: High alch value = 0 just means unalchable in the context of this program
item.highalch = 0;
itemsMadeNonAlchable++;
}
// NOTE: The wiki data does not list every variant of an item such as 'Abyssal lantern (yew logs)'
// which is also not alchable. So this step is to handle that case by searching for the non variant item.
if (itemName.trim().endsWith(')') && itemName.indexOf('(') !== -1) {
const nonVariantItemName = itemName.substring(0, itemName.indexOf('(')).trim();
if (nonAlchableItemNames.has(nonVariantItemName)) {
item.highalch = 0;
itemsMadeNonAlchable++;
}
}
}
console.log(`${itemsMadeNonAlchable} items were updated to be unalchable`);
fs.writeFileSync('./item_data.json', JSON.stringify(includedItems));
return allIncludedItemIds;
}
async function dumpItemImages(allIncludedItemIds) {
console.log('\nStep: Extract item model images');
console.log(`Generating images for ${allIncludedItemIds.size} items`);
fs.writeFileSync('items_need_images.csv', Array.from(allIncludedItemIds.values()).join(','));
const imageDumperDriver = fs.readFileSync('./Cache.java', 'utf8');
fs.writeFileSync(`${cacheProjectPath}/src/main/java/net/runelite/cache/Cache.java`, imageDumperDriver);
const itemSpriteFactory = fs.readFileSync('./ItemSpriteFactory.java', 'utf8');
fs.writeFileSync(`${cacheProjectPath}/src/main/java/net/runelite/cache/item/ItemSpriteFactory.java`, itemSpriteFactory);
buildCacheProject();
execRuneliteCache(`-c ${osrsCacheDirectory} -ids ./items_need_images.csv -output ./item-images`);
const itemImages = glob.sync(`./item-images/*.png`);
let p = [];
for (const itemImage of itemImages) {
p.push(new Promise(async (resolve) => {
const itemImageData = await sharp(itemImage).webp({ lossless: true }).toBuffer();
fs.unlinkSync(itemImage);
await sharp(itemImageData).webp({ lossless: true, effort: 6 }).toFile(itemImage.replace(".png", ".webp")).then(resolve);
}));
}
await Promise.all(p);
}
async function convertXteasToRuneliteFormat() {
const xteas = JSON.parse(fs.readFileSync(`${osrsCacheDirectory}/../xteas.json`, 'utf8'));
let result = xteas.map((region) => ({
region: region.mapsquare,
keys: region.key
}));
const location = `${osrsCacheDirectory}/../xteas-runelite.json`;
fs.writeFileSync(location, JSON.stringify(result));
return location;
}
async function dumpMapData(xteasLocation) {
console.log('\nStep: Dumping map data');
const mapImageDumper = fs.readFileSync('./MapImageDumper.java', 'utf8');
fs.writeFileSync(`${cacheProjectPath}/src/main/java/net/runelite/cache/MapImageDumper.java`, mapImageDumper);
await setMainClassInCachePom('net.runelite.cache.MapImageDumper');
buildCacheProject();
execRuneliteCache(`--cachedir ${osrsCacheDirectory} --xteapath ${xteasLocation} --outputdir ./map-data`);
}
async function dumpMapLabels() {
console.log('\nStep: Dumping map labels');
const mapLabelDumper = fs.readFileSync('./MapLabelDumper.java', 'utf8');
fs.writeFileSync(`${cacheProjectPath}/src/main/java/net/runelite/cache/MapLabelDumper.java`, mapLabelDumper);
await setMainClassInCachePom('net.runelite.cache.MapLabelDumper');
buildCacheProject();
execRuneliteCache(`--cachedir ${osrsCacheDirectory} --outputdir ./map-data/labels`);
const mapLabels = glob.sync("./map-data/labels/*.png");
let p = [];
for (const mapLabel of mapLabels) {
p.push(new Promise(async (resolve) => {
const mapLabelImageData = await sharp(mapLabel).webp({ lossless: true }).toBuffer();
fs.unlinkSync(mapLabel);
await sharp(mapLabelImageData).webp({ lossless: true, effort: 6 }).toFile(mapLabel.replace(".png", ".webp")).then(resolve);
}));
}
await Promise.all(p);
}
async function dumpCollectionLog() {
console.log('\nStep: Dumping collection log');
const collectionLogDumper = fs.readFileSync('./CollectionLogDumper.java', 'utf8');
fs.writeFileSync(`${cacheProjectPath}/src/main/java/net/runelite/cache/CollectionLogDumper.java`, collectionLogDumper);
await setMainClassInCachePom('net.runelite.cache.CollectionLogDumper');
buildCacheProject();
execRuneliteCache(`--cachedir ${osrsCacheDirectory} --outputdir ../server`);
}
async function tilePlane(plane) {
await retry(() => fs.rmSync('./output_files', { recursive: true, force: true }));
const planeImage = sharp(`./map-data/img-${plane}.png`, { limitInputPixels: false }).flip();
await planeImage.webp({ lossless: true }).tile({
size: tileSize,
depth: "one",
background: { r: 0, g: 0, b: 0, alpha: 0 },
skipBlanks: 0
}).toFile('output.dz');
}
async function outputTileImage(s, plane, x, y) {
return s.flatten({ background: '#000000' })
.webp({ lossless: true, alphaQuality: 0, effort: 6 })
.toFile(`./map-data/tiles/${plane}_${x}_${y}.webp`);
}
async function finalizePlaneTiles(plane, previousTiles) {
const tileImages = glob.sync('./output_files/0/*.webp');
for (const tileImage of tileImages) {
const filename = path.basename(tileImage, '.webp');
const [x, y] = filename.split('_').map((coord) => parseInt(coord, 10));
const finalX = x + (4608 / tileSize);
const finalY = y + (4864 / tileSize);
let s;
if (plane > 0) {
const backgroundPath = `./map-data/tiles/${plane-1}_${finalX}_${finalY}.webp`;
const backgroundExists = fs.existsSync(backgroundPath);
if (backgroundExists) {
const tile = await sharp(tileImage).flip().webp({ lossless: true }).toBuffer();
const background = await sharp(backgroundPath).linear(0.5).webp({ lossless: true }).toBuffer();
s = sharp(background)
.composite([
{ input: tile }
]);
}
}
if (!s) {
s = sharp(tileImage).flip();
}
previousTiles.add(`${plane}_${finalX}_${finalY}`);
await outputTileImage(s, plane, finalX, finalY);
}
// NOTE: This is just so the plane will have a darker version of the tile below it
// even if the plane does not have its own image for a tile.
if (plane > 0) {
const belowTiles = [...previousTiles].filter(x => x.startsWith(plane - 1));
for (const belowTile of belowTiles) {
const [belowPlane, x, y] = belowTile.split('_');
const lookup = `${plane}_${x}_${y}`;
if (!previousTiles.has(lookup)) {
const outputPath = `./map-data/tiles/${plane}_${x}_${y}.webp`;
if (fs.existsSync(outputPath) === true) {
throw new Error(`Filling tile ${outputPath} but it already exists!`);
}
const s = sharp(`./map-data/tiles/${belowTile}.webp`).linear(0.5);
previousTiles.add(lookup);
await outputTileImage(s, plane, x, y);
}
}
}
}
async function generateMapTiles() {
console.log('\nStep: Generate map tiles');
fs.rmSync('./map-data/tiles', { recursive: true, force: true });
fs.mkdirSync('./map-data/tiles');
const previousTiles = new Set();
const planes = 4;
for (let i = 0; i < planes; ++i) {
console.log(`Tiling map plane ${i + 1}/${planes}`);
await tilePlane(i);
console.log(`Finalizing map plane ${i + 1}/${planes}`);
await finalizePlaneTiles(i, previousTiles);
}
}
async function moveFiles(globSource, destination) {
const files = glob.sync(globSource);
for (file of files) {
const base = path.parse(file).base;
if (base) {
await retry(() => fs.renameSync(file, `${destination}/${base}`), true);
}
}
}
async function moveResults() {
console.log('\nStep: Moving results to site');
await retry(() => fs.renameSync('./item_data.json', siteItemDataPath), true);
await moveFiles('./item-images/*.webp', siteItemImagesPath);
await moveFiles("./map-data/tiles/*.webp", siteMapImagesPath);
await moveFiles("./map-data/labels/*.webp", siteMapLabelsPath);
// Create a tile sheet of the map icons
const mapIcons = glob.sync("./map-data/icons/*.png");
let mapIconsCompositeOpts = [];
const iconIdToSpriteMapIndex = {};
for (let i = 0; i < mapIcons.length; ++i) {
mapIconsCompositeOpts.push({
input: mapIcons[i],
left: 15 * i,
top: 0
});
iconIdToSpriteMapIndex[path.basename(mapIcons[i], '.png')] = i;
}
await sharp({
create: {
width: 15 * mapIcons.length,
height: 15,
channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 }
}
}).composite(mapIconsCompositeOpts).webp({ lossless: true, effort: 6 }).toFile(siteMapIconPath);
// Convert the output of the map-icons locations to be keyed by the X an Y of the regions
// that they are in. This is done so that the canvas map component can quickly lookup
// all of the icons in each of the regions that are being shown.
const mapIconsMeta = JSON.parse(fs.readFileSync("./map-data/icons/map-icons.json", 'utf8'));
const locationByRegion = {};
for (const [iconId, coordinates] of Object.entries(mapIconsMeta)) {
for (let i = 0; i < coordinates.length; i += 2) {
const x = coordinates[i] + 128;
const y = coordinates[i + 1] + 1;
const regionX = Math.floor(x / 64);
const regionY = Math.floor(y / 64);
const spriteMapIndex = iconIdToSpriteMapIndex[iconId];
if (spriteMapIndex === undefined) {
throw new Error("Could not find sprite map index for map icon: " + iconId);
}
locationByRegion[regionX] = locationByRegion[regionX] || {};
locationByRegion[regionX][regionY] = locationByRegion[regionX][regionY] || {};
locationByRegion[regionX][regionY][spriteMapIndex] = locationByRegion[regionX][regionY][spriteMapIndex] || [];
locationByRegion[regionX][regionY][spriteMapIndex].push(x, y);
}
}
fs.writeFileSync(siteMapIconMetaPath, JSON.stringify(locationByRegion));
// Do the same for map labels
const mapLabelsMeta = JSON.parse(fs.readFileSync("./map-data/labels/map-labels.json", 'utf8'));
const labelByRegion = {};
for (let i = 0; i < mapLabelsMeta.length; ++i) {
const coordinates = mapLabelsMeta[i];
const x = coordinates[0] + 128;
const y = coordinates[1] + 1;
const z = coordinates[2];
const regionX = Math.floor(x / 64);
const regionY = Math.floor(y / 64);
labelByRegion[regionX] = labelByRegion[regionX] || {};
labelByRegion[regionX][regionY] = labelByRegion[regionX][regionY] || {};
labelByRegion[regionX][regionY][z] = labelByRegion[regionX][regionY][z] || [];
labelByRegion[regionX][regionY][z].push(x, y, i);
}
fs.writeFileSync(siteMapLabelMetaPath, JSON.stringify(labelByRegion));
}
async function getLatestGameCache() {
if (!fs.existsSync('./cache')) {
fs.mkdirSync('./cache');
}
const caches = (await axios.get('https://archive.openrs2.org/caches.json')).data;
const latestOSRSCache = caches.filter((cache) => {
return cache.scope === 'runescape' && cache.game === 'oldschool' && cache.environment === 'live' && !!cache.timestamp;
}).sort((a, b) => (new Date(b.timestamp)) - (new Date(a.timestamp)))[0];
console.log(latestOSRSCache);
const pctValidArchives = latestOSRSCache.valid_indexes / latestOSRSCache.indexes;
if (pctValidArchives < 1) {
throw new Error(`valid_indexes was less than indexes valid_indexes=${latestOSRSCache.valid_indexes} indexes=${latestOSRSCache.indexes} pctValidArchives=${pctValidArchives}`);
}
const pctValidGroups = latestOSRSCache.valid_groups / latestOSRSCache.groups;
if (pctValidGroups < 1) {
throw new Error(`valid_groups was less than groups valid_groups=${latestOSRSCache.valid_groups} groups=${latestOSRSCache.groups} pctValidGroups=${pctValidGroups}`);
}
const pctValidKeys = latestOSRSCache.valid_keys / latestOSRSCache.keys;
if (pctValidKeys < 0.97) {
throw new Error(`pctValidKeys was less that 97% valid_keys=${latestOSRSCache.valid_keys} keys=${latestOSRSCache.keys} pctValidKeys=${pctValidKeys}`);
}
const cacheFilesResponse = await axios.get(`https://archive.openrs2.org/caches/${latestOSRSCache.scope}/${latestOSRSCache.id}/disk.zip`, {
responseType: 'arraybuffer'
});
const cacheFiles = await unzipper.Open.buffer(cacheFilesResponse.data);
await cacheFiles.extract({ path: './cache' });
const xteas = (await axios.get(`https://archive.openrs2.org/caches/${latestOSRSCache.scope}/${latestOSRSCache.id}/keys.json`)).data;
fs.writeFileSync('./cache/xteas.json', JSON.stringify(xteas));
}
(async () => {
await getLatestGameCache();
await setupRunelite();
await dumpItemData();
const allIncludedItemIds = await buildItemDataJson();
await dumpItemImages(allIncludedItemIds);
const xteasLocation = await convertXteasToRuneliteFormat();
await dumpMapData(xteasLocation);
await generateMapTiles();
await dumpMapLabels();
await dumpCollectionLog();
await moveResults();
})();