aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAaron <51387595+AzureAaron@users.noreply.github.com>2025-06-26 12:07:10 -0400
committerAaron <51387595+AzureAaron@users.noreply.github.com>2025-06-26 12:07:10 -0400
commit71ca871f2a2441813b26e24467b8e75b166f747a (patch)
tree68df274445975ad1fc1c13fe667c59b2df486a52 /src
parent3bd588279171d1f59b0433d2b94da29f5db4a740 (diff)
downloadSkyblocker-71ca871f2a2441813b26e24467b8e75b166f747a.tar.gz
Skyblocker-71ca871f2a2441813b26e24467b8e75b166f747a.tar.bz2
Skyblocker-71ca871f2a2441813b26e24467b8e75b166f747a.zip
Add attribute list for bazaar price support
The code for generating the list has been left in so that it can easily be regenerated if necessary.
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java16
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java53
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java103
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/ItemUtils.java10
-rw-r--r--src/main/resources/assets/skyblocker/hunting/attribute_upgrades.json72
-rw-r--r--src/main/resources/assets/skyblocker/hunting/attributes.json1046
-rw-r--r--src/test/java/de/hysky/skyblocker/skyblock/hunting/AttributeSerializationTest.java18
-rw-r--r--src/test/java/de/hysky/skyblocker/utils/ItemUtilsTest.java2
8 files changed, 1314 insertions, 6 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java
new file mode 100644
index 00000000..4090a571
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java
@@ -0,0 +1,16 @@
+package de.hysky.skyblocker.skyblock.hunting;
+
+import java.util.List;
+
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+
+public record Attribute(String name, String shardName, String id, String apiId) {
+ private static final Codec<Attribute> CODEC = RecordCodecBuilder.create(instance -> instance.group(
+ Codec.STRING.fieldOf("name").forGetter(Attribute::name),
+ Codec.STRING.fieldOf("shardName").forGetter(Attribute::shardName),
+ Codec.STRING.fieldOf("id").forGetter(Attribute::id),
+ Codec.STRING.fieldOf("apiId").forGetter(Attribute::apiId)
+ ).apply(instance, Attribute::new));
+ public static final Codec<List<Attribute>> LIST_CODEC = CODEC.listOf();
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java
new file mode 100644
index 00000000..e24f11bd
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java
@@ -0,0 +1,53 @@
+package de.hysky.skyblocker.skyblock.hunting;
+
+import java.io.BufferedReader;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+
+import com.google.gson.JsonParser;
+import com.mojang.logging.LogUtils;
+import com.mojang.serialization.JsonOps;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.annotations.Init;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.component.ComponentHolder;
+import net.minecraft.component.DataComponentTypes;
+import net.minecraft.util.Identifier;
+
+public class Attributes {
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final Identifier ATTRIBUTES_FILE = Identifier.of(SkyblockerMod.NAMESPACE, "hunting/attributes.json");
+ private static List<Attribute> attributes = List.of();
+
+ @Init
+ public static void init() {
+ ClientLifecycleEvents.CLIENT_STARTED.register(Attributes::loadShards);
+ }
+
+ private static void loadShards(MinecraftClient client) {
+ CompletableFuture.runAsync(() -> {
+ try (BufferedReader reader = client.getResourceManager().openAsReader(ATTRIBUTES_FILE)) {
+ attributes = Attribute.LIST_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow();
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Attributes] Failed to load attributes.", e);
+ }
+ });
+ }
+
+ @Nullable
+ public static Attribute getAttributeFromItemName(ComponentHolder stack) {
+ if (!stack.contains(DataComponentTypes.CUSTOM_NAME)) return null;
+ String name = stack.get(DataComponentTypes.CUSTOM_NAME).getString();
+
+ for (Attribute attribute : attributes) {
+ if (attribute.shardName().equals(name)) return attribute;
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java b/src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java
new file mode 100644
index 00000000..1ba4bd72
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java
@@ -0,0 +1,103 @@
+package de.hysky.skyblocker.skyblock.hunting;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.lwjgl.glfw.GLFW;
+import org.slf4j.Logger;
+
+import com.mojang.logging.LogUtils;
+import com.mojang.serialization.JsonOps;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.skyblock.item.tooltip.info.TooltipInfoType;
+import de.hysky.skyblocker.utils.ItemUtils;
+import de.hysky.skyblocker.utils.RomanNumerals;
+import de.hysky.skyblocker.utils.container.ContainerSolver;
+import de.hysky.skyblocker.utils.container.ContainerSolverManager;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ingame.HandledScreen;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.GenericContainerScreenHandler;
+
+public class AttributesDebug {
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+ private static final List<Attribute> DUMPED_ATTRIBUTES = new ArrayList<>();
+ private static final Pattern SOURCE_PATTERN = Pattern.compile("Source: (?<shardName>[A-za-z ]+) Shard \\((?<id>[CUREL]\\d+)\\)");
+ private static final Path ATTRIBUTE_EXPORT_DEST = SkyblockerMod.CONFIG_DIR.resolve("attribute_export.json");
+
+ //@Init
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ ScreenKeyboardEvents.afterKeyPress(screen).register((screen1, key, scancode, modifiers) -> {
+ if (key == GLFW.GLFW_KEY_G) {
+ dumpAttributes();
+ } else if (key == GLFW.GLFW_KEY_J) {
+ exportAttributes();
+ }
+ });
+ });
+ }
+
+ private static void dumpAttributes() {
+ if (CLIENT.currentScreen instanceof HandledScreen<?> screen && screen.getTitle().getString().equals("Attribute Menu")) {
+ @SuppressWarnings("unchecked")
+ Int2ObjectMap<ItemStack> slots = ContainerSolverManager.slotMap(screen.getScreenHandler().slots.subList(0, ((HandledScreen<GenericContainerScreenHandler>) screen).getScreenHandler().getRows() * 9));
+ ContainerSolver.trimEdges(slots, 6);
+
+ for (ItemStack stack : slots.values()) {
+ if (stack.isEmpty()) continue;
+
+ String name = stack.getName().getString();
+ Matcher sourceMatcher = ItemUtils.getLoreLineIfMatch(stack, SOURCE_PATTERN);
+
+ //Remove roman numeral from name
+ List<String> words = new ArrayList<>(Arrays.asList(name.split(" ")));
+ if (RomanNumerals.isValidRomanNumeral(words.getLast().strip())) {
+ words.removeLast();
+ name = String.join(" ", words);
+ }
+
+ if (sourceMatcher != null) {
+ String shardName = sourceMatcher.group("shardName");
+ String id = sourceMatcher.group("id");
+ String apiIdGuess = "SHARD_" + shardName.replace(' ', '_').toUpperCase(Locale.ENGLISH);
+ boolean hasDataForId = TooltipInfoType.BAZAAR.getData().containsKey(apiIdGuess);
+
+ //Most attributes follow the format above but some have different ids so this is to catch those ones
+ if (!hasDataForId) LOGGER.warn("[Skyblocker Attributes Debug] No data found for shard. Shard Name: {}", shardName);
+
+ Attribute attribute = new Attribute(name, shardName, id, apiIdGuess);
+ DUMPED_ATTRIBUTES.add(attribute);
+ } else {
+ LOGGER.warn("[Skyblocker Attributes Debug] Failed to match shard! Name: {}", name);
+ }
+ }
+ }
+ }
+
+ private static void exportAttributes() {
+ if (CLIENT.currentScreen instanceof HandledScreen screen && screen.getTitle().getString().equals("Attribute Menu")) {
+ List<Attribute> copy = DUMPED_ATTRIBUTES.stream().distinct().toList();
+
+ CompletableFuture.runAsync(() -> {
+ try {
+ Files.writeString(ATTRIBUTE_EXPORT_DEST, Attribute.LIST_CODEC.encodeStart(JsonOps.INSTANCE, copy).getOrThrow().toString());
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Attributes Debug] Failed to export attributes!", e);
+ }
+ });
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java
index a27ed97a..dc3f3c5a 100644
--- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java
@@ -11,6 +11,8 @@ import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.debug.Debug;
+import de.hysky.skyblocker.skyblock.hunting.Attribute;
+import de.hysky.skyblocker.skyblock.hunting.Attributes;
import de.hysky.skyblocker.skyblock.item.PetInfo;
import de.hysky.skyblocker.skyblock.item.tooltip.adders.ObtainedDateTooltip;
import de.hysky.skyblocker.skyblock.item.tooltip.info.TooltipInfoType;
@@ -185,11 +187,9 @@ public final class ItemUtils {
}
}
case "ATTRIBUTE_SHARD" -> {
- if (customData.contains("attributes")) {
- NbtCompound shards = customData.getCompoundOrEmpty("attributes");
- String shard = shards.getKeys().stream().findFirst().orElse("");
- return id + "-" + shard.toUpperCase(Locale.ENGLISH) + "_" + shards.getInt(shard, 0);
- }
+ Attribute attribute = Attributes.getAttributeFromItemName(itemStack);
+
+ if (attribute != null) return attribute.apiId();
}
case "NEW_YEAR_CAKE" -> {
return id + "_" + customData.getInt("new_years_cake", 0);
diff --git a/src/main/resources/assets/skyblocker/hunting/attribute_upgrades.json b/src/main/resources/assets/skyblocker/hunting/attribute_upgrades.json
new file mode 100644
index 00000000..c26bc82f
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/hunting/attribute_upgrades.json
@@ -0,0 +1,72 @@
+{
+ "COMMON": {
+ "shardsForUpgrade": [
+ 1,
+ 4,
+ 9,
+ 15,
+ 22,
+ 30,
+ 40,
+ 54,
+ 72,
+ 96
+ ]
+ },
+ "UNCOMMON": {
+ "shardsForUpgrade": [
+ 1,
+ 3,
+ 6,
+ 10,
+ 15,
+ 21,
+ 28,
+ 36,
+ 48,
+ 64
+ ]
+ },
+ "RARE": {
+ "shardsForUpgrade": [
+ 1,
+ 3,
+ 6,
+ 9,
+ 13,
+ 17,
+ 22,
+ 28,
+ 36,
+ 48
+ ]
+ },
+ "EPIC": {
+ "shardsForUpgrade": [
+ 1,
+ 2,
+ 4,
+ 6,
+ 9,
+ 12,
+ 16,
+ 20,
+ 25,
+ 32
+ ]
+ },
+ "LEGENDARY": {
+ "shardsForUpgrade": [
+ 1,
+ 2,
+ 3,
+ 5,
+ 7,
+ 9,
+ 12,
+ 15,
+ 19,
+ 24
+ ]
+ }
+} \ No newline at end of file
diff --git a/src/main/resources/assets/skyblocker/hunting/attributes.json b/src/main/resources/assets/skyblocker/hunting/attributes.json
new file mode 100644
index 00000000..97a055af
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/hunting/attributes.json
@@ -0,0 +1,1046 @@
+[
+ {
+ "name": "Nature Elemental",
+ "shardName": "Grove",
+ "id": "C1",
+ "apiId": "SHARD_GROVE"
+ },
+ {
+ "name": "Fog Elemental",
+ "shardName": "Mist",
+ "id": "C2",
+ "apiId": "SHARD_MIST"
+ },
+ {
+ "name": "Light Elemental",
+ "shardName": "Flash",
+ "id": "C3",
+ "apiId": "SHARD_FLASH"
+ },
+ {
+ "name": "Nocturnal Animal",
+ "shardName": "Phanpyre",
+ "id": "C4",
+ "apiId": "SHARD_PHANPYRE"
+ },
+ {
+ "name": "Cheapstake",
+ "shardName": "Cod",
+ "id": "C5",
+ "apiId": "SHARD_COD"
+ },
+ {
+ "name": "Moonglade Mastery",
+ "shardName": "Phanflare",
+ "id": "C7",
+ "apiId": "SHARD_PHANFLARE"
+ },
+ {
+ "name": "Fisherman",
+ "shardName": "Night Squid",
+ "id": "C8",
+ "apiId": "SHARD_NIGHT_SQUID"
+ },
+ {
+ "name": "Experience",
+ "shardName": "Lapis Zombie",
+ "id": "C9",
+ "apiId": "SHARD_LAPIS_ZOMBIE"
+ },
+ {
+ "name": "Mossy Box",
+ "shardName": "Hideonleaf",
+ "id": "C10",
+ "apiId": "SHARD_HIDEONLEAF"
+ },
+ {
+ "name": "Forest Fishing",
+ "shardName": "Verdant",
+ "id": "C11",
+ "apiId": "SHARD_VERDANT"
+ },
+ {
+ "name": "Skeletal Ruler",
+ "shardName": "Chill",
+ "id": "C12",
+ "apiId": "SHARD_CHILL"
+ },
+ {
+ "name": "Monster Bait",
+ "shardName": "Sea Archer",
+ "id": "C14",
+ "apiId": "SHARD_SEA_ARCHER"
+ },
+ {
+ "name": "Arthropod Resistance",
+ "shardName": "Voracious Spider",
+ "id": "C15",
+ "apiId": "SHARD_VORACIOUS_SPIDER"
+ },
+ {
+ "name": "Happy Box",
+ "shardName": "Hideongift",
+ "id": "C16",
+ "apiId": "SHARD_HIDEONGIFT"
+ },
+ {
+ "name": "Yummy",
+ "shardName": "Birries",
+ "id": "C17",
+ "apiId": "SHARD_BIRRIES"
+ },
+ {
+ "name": "Undead Resistance",
+ "shardName": "Tank Zombie",
+ "id": "C18",
+ "apiId": "SHARD_TANK_ZOMBIE"
+ },
+ {
+ "name": "Fig Sharpening",
+ "shardName": "Crow",
+ "id": "C19",
+ "apiId": "SHARD_CROW"
+ },
+ {
+ "name": "Unity is Strength",
+ "shardName": "Tadgang",
+ "id": "C20",
+ "apiId": "SHARD_TADGANG"
+ },
+ {
+ "name": "Ender Resistance",
+ "shardName": "Zealot",
+ "id": "C21",
+ "apiId": "SHARD_ZEALOT"
+ },
+ {
+ "name": "Bucket Lover",
+ "shardName": "Coralot",
+ "id": "C23",
+ "apiId": "SHARD_CORALOT"
+ },
+ {
+ "name": "Tree Lurker",
+ "shardName": "Harpy",
+ "id": "C24",
+ "apiId": "SHARD_HARPY"
+ },
+ {
+ "name": "Visitor Bait",
+ "shardName": "Mudworm",
+ "id": "C25",
+ "apiId": "SHARD_MUDWORM"
+ },
+ {
+ "name": "Midas Touch",
+ "shardName": "Golden Ghoul",
+ "id": "C27",
+ "apiId": "SHARD_GOLDEN_GHOUL"
+ },
+ {
+ "name": "Forest Strength",
+ "shardName": "Azure",
+ "id": "C29",
+ "apiId": "SHARD_AZURE"
+ },
+ {
+ "name": "Blazing Resistance",
+ "shardName": "Bezal",
+ "id": "C30",
+ "apiId": "SHARD_BEZAL"
+ },
+ {
+ "name": "Yog Membrane",
+ "shardName": "Yog",
+ "id": "C33",
+ "apiId": "SHARD_YOG"
+ },
+ {
+ "name": "Owl Friend",
+ "shardName": "Boreal Owl",
+ "id": "C34",
+ "apiId": "SHARD_BOREAL_OWL"
+ },
+ {
+ "name": "Decent Karma",
+ "shardName": "Newt",
+ "id": "C35",
+ "apiId": "SHARD_NEWT"
+ },
+ {
+ "name": "Rotten Pickaxe",
+ "shardName": "Miner Zombie",
+ "id": "C36",
+ "apiId": "SHARD_MINER_ZOMBIE"
+ },
+ {
+ "name": "Wood Elemental",
+ "shardName": "Bramble",
+ "id": "U1",
+ "apiId": "SHARD_BRAMBLE"
+ },
+ {
+ "name": "Water Elemental",
+ "shardName": "Tide",
+ "id": "U2",
+ "apiId": "SHARD_TIDE"
+ },
+ {
+ "name": "Stone Elemental",
+ "shardName": "Quake",
+ "id": "U3",
+ "apiId": "SHARD_QUAKE"
+ },
+ {
+ "name": "Fig Collector",
+ "shardName": "Sparrow",
+ "id": "U4",
+ "apiId": "SHARD_SPARROW"
+ },
+ {
+ "name": "Gold Bait",
+ "shardName": "Goldfin",
+ "id": "U5",
+ "apiId": "SHARD_GOLDFIN"
+ },
+ {
+ "name": "Mountain Climber",
+ "shardName": "Troglobyte",
+ "id": "U6",
+ "apiId": "SHARD_TROGLOBYTE"
+ },
+ {
+ "name": "Bigger Box",
+ "shardName": "Hideoncave",
+ "id": "U7",
+ "apiId": "SHARD_HIDEONCAVE"
+ },
+ {
+ "name": "Good Karma",
+ "shardName": "Salamander",
+ "id": "U8",
+ "apiId": "SHARD_SALAMANDER"
+ },
+ {
+ "name": "Echo of Boxes",
+ "shardName": "Cuboa",
+ "id": "U9",
+ "apiId": "SHARD_CUBOA"
+ },
+ {
+ "name": "Pest Luck",
+ "shardName": "Pest",
+ "id": "U10",
+ "apiId": "SHARD_PEST"
+ },
+ {
+ "name": "Forest Trap",
+ "shardName": "Mossybit",
+ "id": "U11",
+ "apiId": "SHARD_MOSSYBIT"
+ },
+ {
+ "name": "Mana Steal",
+ "shardName": "Rain Slime",
+ "id": "U12",
+ "apiId": "SHARD_RAIN_SLIME"
+ },
+ {
+ "name": "Dragon Shortbow Improvement",
+ "shardName": "Seer",
+ "id": "U15",
+ "apiId": "SHARD_SEER"
+ },
+ {
+ "name": "Mangrove Sharpening",
+ "shardName": "Heron",
+ "id": "U16",
+ "apiId": "SHARD_HERON"
+ },
+ {
+ "name": "Speed",
+ "shardName": "Obsidian Defender",
+ "id": "U18",
+ "apiId": "SHARD_OBSIDIAN_DEFENDER"
+ },
+ {
+ "name": "Lost and Found",
+ "shardName": "Salmon",
+ "id": "U20",
+ "apiId": "SHARD_SALMON"
+ },
+ {
+ "name": "Hunter's Fang",
+ "shardName": "Viper",
+ "id": "U21",
+ "apiId": "SHARD_VIPER"
+ },
+ {
+ "name": "Insect Power",
+ "shardName": "Praying Mantis",
+ "id": "U22",
+ "apiId": "SHARD_PRAYING_MANTIS"
+ },
+ {
+ "name": "Undead Ruler",
+ "shardName": "Zombie Soldier",
+ "id": "U24",
+ "apiId": "SHARD_ZOMBIE_SOLDIER"
+ },
+ {
+ "name": "Strong Arms",
+ "shardName": "Bambuleaf",
+ "id": "U25",
+ "apiId": "SHARD_BAMBULEAF"
+ },
+ {
+ "name": "Life Recovery",
+ "shardName": "Sycophant",
+ "id": "U27",
+ "apiId": "SHARD_SYCOPHANT"
+ },
+ {
+ "name": "Mangrove Collector",
+ "shardName": "Seagull",
+ "id": "U28",
+ "apiId": "SHARD_SEAGULL"
+ },
+ {
+ "name": "Spirit Axe",
+ "shardName": "Ent",
+ "id": "U29",
+ "apiId": "SHARD_ENT"
+ },
+ {
+ "name": "Combo",
+ "shardName": "Soul of the Alpha",
+ "id": "U30",
+ "apiId": "SHARD_SOUL_OF_THE_ALPHA"
+ },
+ {
+ "name": "Strong Legs",
+ "shardName": "Mochibear",
+ "id": "U31",
+ "apiId": "SHARD_MOCHIBEAR"
+ },
+ {
+ "name": "Infection",
+ "shardName": "Magma Slug",
+ "id": "U32",
+ "apiId": "SHARD_MAGMA_SLUG"
+ },
+ {
+ "name": "Arthropod Ruler",
+ "shardName": "Flaming Spider",
+ "id": "U33",
+ "apiId": "SHARD_FLAMING_SPIDER"
+ },
+ {
+ "name": "Kat's Favorite",
+ "shardName": "Kiwi",
+ "id": "U34",
+ "apiId": "SHARD_KIWI"
+ },
+ {
+ "name": "Ender Ruler",
+ "shardName": "Bruiser",
+ "id": "U36",
+ "apiId": "SHARD_BRUISER"
+ },
+ {
+ "name": "Magmatic Ruler",
+ "shardName": "Stridersurfer",
+ "id": "U38",
+ "apiId": "SHARD_STRIDER_SURFER"
+ },
+ {
+ "name": "Battle Frog",
+ "shardName": "Rana",
+ "id": "U39",
+ "apiId": "SHARD_RANA"
+ },
+ {
+ "name": "Infiltration",
+ "shardName": "Termite",
+ "id": "U40",
+ "apiId": "SHARD_TERMITE"
+ },
+ {
+ "name": "Forest Elemental",
+ "shardName": "Sylvan",
+ "id": "R1",
+ "apiId": "SHARD_SYLVAN"
+ },
+ {
+ "name": "Torrent Elemental",
+ "shardName": "Cascade",
+ "id": "R2",
+ "apiId": "SHARD_CASCADE"
+ },
+ {
+ "name": "Lightning Elemental",
+ "shardName": "Bolt",
+ "id": "R3",
+ "apiId": "SHARD_BOLT"
+ },
+ {
+ "name": "Animal Expertise",
+ "shardName": "Bambloom",
+ "id": "R4",
+ "apiId": "SHARD_BAMBLOOM"
+ },
+ {
+ "name": "Frog Legs",
+ "shardName": "Toad",
+ "id": "R5",
+ "apiId": "SHARD_TOAD"
+ },
+ {
+ "name": "Essence of Ice",
+ "shardName": "Glacite Walker",
+ "id": "R6",
+ "apiId": "SHARD_GLACITE_WALKER"
+ },
+ {
+ "name": "Beacon Zealot",
+ "shardName": "Beaconmite",
+ "id": "R7",
+ "apiId": "SHARD_BEACONMITE"
+ },
+ {
+ "name": "Great Karma",
+ "shardName": "Lizard King",
+ "id": "R8",
+ "apiId": "SHARD_LIZARD_KING"
+ },
+ {
+ "name": "Hunter's Pressure",
+ "shardName": "Python",
+ "id": "R9",
+ "apiId": "SHARD_PYTHON"
+ },
+ {
+ "name": "Fancy Visit",
+ "shardName": "Invisibug",
+ "id": "R10",
+ "apiId": "SHARD_INVISIBUG"
+ },
+ {
+ "name": "Moonglade Serendipity",
+ "shardName": "Piranha",
+ "id": "R11",
+ "apiId": "SHARD_PIRANHA"
+ },
+ {
+ "name": "Catacombs Box",
+ "shardName": "Hideongeon",
+ "id": "R13",
+ "apiId": "SHARD_HIDEONGEON"
+ },
+ {
+ "name": "Bone Font",
+ "shardName": "Lapis Skeleton",
+ "id": "R15",
+ "apiId": "SHARD_LAPIS_SKELETON"
+ },
+ {
+ "name": "Crop Bug",
+ "shardName": "Cropeetle",
+ "id": "R16",
+ "apiId": "SHARD_CROPEETLE"
+ },
+ {
+ "name": "Humanoid Ruler",
+ "shardName": "Drowned",
+ "id": "R18",
+ "apiId": "SHARD_DROWNED"
+ },
+ {
+ "name": "Atomized Mithril",
+ "shardName": "Star Sentry",
+ "id": "R21",
+ "apiId": "SHARD_STAR_SENTRY"
+ },
+ {
+ "name": "Kuudra's Box",
+ "shardName": "Hideondra",
+ "id": "R22",
+ "apiId": "SHARD_HIDEONDRA"
+ },
+ {
+ "name": "Dwarven Serendipity",
+ "shardName": "Abyssal Lanternfish",
+ "id": "R23",
+ "apiId": "SHARD_ABYSSAL_LANTERN"
+ },
+ {
+ "name": "Essence of Arthropods",
+ "shardName": "Arachne",
+ "id": "R24",
+ "apiId": "SHARD_ARACHNE"
+ },
+ {
+ "name": "Cookie Eater",
+ "shardName": "Bitbug",
+ "id": "R25",
+ "apiId": "SHARD_BITBUG"
+ },
+ {
+ "name": "Essence of Unliving",
+ "shardName": "Revenant",
+ "id": "R27",
+ "apiId": "SHARD_REVENANT"
+ },
+ {
+ "name": "Crystal Serendipity",
+ "shardName": "Silentdepth",
+ "id": "R29",
+ "apiId": "SHARD_SILENTDEPTH"
+ },
+ {
+ "name": "Deadeye",
+ "shardName": "Skeletor",
+ "id": "R30",
+ "apiId": "SHARD_SKELETOR"
+ },
+ {
+ "name": "Atomized Crystals",
+ "shardName": "Thyst",
+ "id": "R31",
+ "apiId": "SHARD_THYST"
+ },
+ {
+ "name": "Quartz Speed",
+ "shardName": "Quartzfang",
+ "id": "R33",
+ "apiId": "SHARD_QUARTZFANG"
+ },
+ {
+ "name": "Accessory Size",
+ "shardName": "Hideonring",
+ "id": "R34",
+ "apiId": "SHARD_HIDEONRING"
+ },
+ {
+ "name": "Winter's Serendipity",
+ "shardName": "Snowfin",
+ "id": "R35",
+ "apiId": "SHARD_SNOWFIN"
+ },
+ {
+ "name": "Lifeline",
+ "shardName": "Kada Knight",
+ "id": "R36",
+ "apiId": "SHARD_KADA_KNIGHT"
+ },
+ {
+ "name": "Rabbit Crew",
+ "shardName": "Carrot King",
+ "id": "R38",
+ "apiId": "SHARD_CARROT_KING"
+ },
+ {
+ "name": "Breeze",
+ "shardName": "Wither Specter",
+ "id": "R39",
+ "apiId": "SHARD_WITHER_SPECTER"
+ },
+ {
+ "name": "Ignition",
+ "shardName": "Matcho",
+ "id": "R42",
+ "apiId": "SHARD_MATCHO"
+ },
+ {
+ "name": "Pretty Clothes",
+ "shardName": "Ladybug",
+ "id": "R43",
+ "apiId": "SHARD_LADYBUG"
+ },
+ {
+ "name": "Extreme Pressure",
+ "shardName": "Lumisquid",
+ "id": "R44",
+ "apiId": "SHARD_LUMISQUID"
+ },
+ {
+ "name": "Pure Reptile",
+ "shardName": "Crocodile",
+ "id": "R45",
+ "apiId": "SHARD_CROCODILE"
+ },
+ {
+ "name": "Berry Enjoyer",
+ "shardName": "Bullfrog",
+ "id": "R46",
+ "apiId": "SHARD_BULLFROG"
+ },
+ {
+ "name": "Essence of the Forest",
+ "shardName": "Dreadwing",
+ "id": "R49",
+ "apiId": "SHARD_DREADWING"
+ },
+ {
+ "name": "Deep Diving",
+ "shardName": "Joydive",
+ "id": "R50",
+ "apiId": "SHARD_JOYDIVE"
+ },
+ {
+ "name": "Atomized Glacite",
+ "shardName": "Stalagmight",
+ "id": "R51",
+ "apiId": "SHARD_STALAGMIGHT"
+ },
+ {
+ "name": "Fungy Luck",
+ "shardName": "Fungloom",
+ "id": "R52",
+ "apiId": "SHARD_FUNGLOOM"
+ },
+ {
+ "name": "Eelastic",
+ "shardName": "Eel",
+ "id": "R53",
+ "apiId": "SHARD_EEL"
+ },
+ {
+ "name": "Hunter's Grasp",
+ "shardName": "King Cobra",
+ "id": "R54",
+ "apiId": "SHARD_KING_COBRA"
+ },
+ {
+ "name": "Blazing Fortune",
+ "shardName": "Lava Flame",
+ "id": "R56",
+ "apiId": "SHARD_LAVA_FLAME"
+ },
+ {
+ "name": "Essence of Dragons",
+ "shardName": "Draconic",
+ "id": "R57",
+ "apiId": "SHARD_DRACONIC"
+ },
+ {
+ "name": "Battle Experience",
+ "shardName": "Falcon",
+ "id": "R58",
+ "apiId": "SHARD_FALCON"
+ },
+ {
+ "name": "Crimson Serendipity",
+ "shardName": "Inferno Koi",
+ "id": "R59",
+ "apiId": "SHARD_INFERNO_KOI"
+ },
+ {
+ "name": "Essence of Wither",
+ "shardName": "Wither",
+ "id": "R60",
+ "apiId": "SHARD_WITHER"
+ },
+ {
+ "name": "Echo of Sharpening",
+ "shardName": "Gecko",
+ "id": "R61",
+ "apiId": "SHARD_GECKO"
+ },
+ {
+ "name": "Earth Elemental",
+ "shardName": "Terra",
+ "id": "E1",
+ "apiId": "SHARD_TERRA"
+ },
+ {
+ "name": "Frost Elemental",
+ "shardName": "Cryo",
+ "id": "E2",
+ "apiId": "SHARD_CRYO"
+ },
+ {
+ "name": "Wind Elemental",
+ "shardName": "Aero",
+ "id": "E3",
+ "apiId": "SHARD_AERO"
+ },
+ {
+ "name": "Foraging Wisdom",
+ "shardName": "Pandarai",
+ "id": "E4",
+ "apiId": "SHARD_PANDARAI"
+ },
+ {
+ "name": "Excellent Karma",
+ "shardName": "Leviathan",
+ "id": "E5",
+ "apiId": "SHARD_LEVIATHAN"
+ },
+ {
+ "name": "Echo of Resistance",
+ "shardName": "Alligator",
+ "id": "E6",
+ "apiId": "SHARD_ALLIGATOR"
+ },
+ {
+ "name": "Berry Mogul",
+ "shardName": "Fenlord",
+ "id": "E7",
+ "apiId": "SHARD_FENLORD"
+ },
+ {
+ "name": "Hunter's Suppress",
+ "shardName": "Basilisk",
+ "id": "E9",
+ "apiId": "SHARD_BASILISK"
+ },
+ {
+ "name": "Echo of Atomized",
+ "shardName": "Iguana",
+ "id": "E10",
+ "apiId": "SHARD_IGUANA"
+ },
+ {
+ "name": "Lucky Rod",
+ "shardName": "Moray Eel",
+ "id": "E11",
+ "apiId": "SHARD_MORAY_EEL"
+ },
+ {
+ "name": "Dominance",
+ "shardName": "Thorn",
+ "id": "E12",
+ "apiId": "SHARD_THORN"
+ },
+ {
+ "name": "Lunar Power",
+ "shardName": "Lunar Moth",
+ "id": "E13",
+ "apiId": "SHARD_LUNAR_MOTH"
+ },
+ {
+ "name": "Trophy Hunter",
+ "shardName": "Fire Eel",
+ "id": "E14",
+ "apiId": "SHARD_FIRE_EEL"
+ },
+ {
+ "name": "Deep Technique",
+ "shardName": "Bal",
+ "id": "E15",
+ "apiId": "SHARD_BAL"
+ },
+ {
+ "name": "Sack Size",
+ "shardName": "Hideonsack",
+ "id": "E16",
+ "apiId": "SHARD_HIDEONSACK"
+ },
+ {
+ "name": "Fishing Speed",
+ "shardName": "Water Hydra",
+ "id": "E17",
+ "apiId": "SHARD_WATER_HYDRA"
+ },
+ {
+ "name": "Blazing",
+ "shardName": "Flare",
+ "id": "E18",
+ "apiId": "SHARD_FLARE"
+ },
+ {
+ "name": "Sea Wisdom",
+ "shardName": "Sea Emperor",
+ "id": "E20",
+ "apiId": "SHARD_SEA_EMPEROR"
+ },
+ {
+ "name": "Reborn",
+ "shardName": "Prince",
+ "id": "E21",
+ "apiId": "SHARD_PRINCE"
+ },
+ {
+ "name": "Echo of Essence",
+ "shardName": "Komodo Dragon",
+ "id": "E22",
+ "apiId": "SHARD_KOMODO_DRAGON"
+ },
+ {
+ "name": "Faker",
+ "shardName": "Mimic",
+ "id": "E24",
+ "apiId": "SHARD_MIMIC"
+ },
+ {
+ "name": "Shell",
+ "shardName": "Shellwise",
+ "id": "E26",
+ "apiId": "SHARD_SHELLWISE"
+ },
+ {
+ "name": "Warrior",
+ "shardName": "Barbarian Duke X",
+ "id": "E27",
+ "apiId": "SHARD_BARBARIAN_DUKE_X"
+ },
+ {
+ "name": "Why Not More",
+ "shardName": "Toucan",
+ "id": "E28",
+ "apiId": "SHARD_TOUCAN"
+ },
+ {
+ "name": "Matriarch Cubs",
+ "shardName": "Hellwisp",
+ "id": "E29",
+ "apiId": "SHARD_HELLWISP"
+ },
+ {
+ "name": "Echo of Ruler",
+ "shardName": "Caiman",
+ "id": "E30",
+ "apiId": "SHARD_CAIMAN"
+ },
+ {
+ "name": "Solar Power",
+ "shardName": "Firefly",
+ "id": "E31",
+ "apiId": "SHARD_FIREFLY"
+ },
+ {
+ "name": "Echo of Hunter",
+ "shardName": "Sea Serpent",
+ "id": "E32",
+ "apiId": "SHARD_SEA_SERPENT"
+ },
+ {
+ "name": "Veil",
+ "shardName": "Ghost",
+ "id": "E33",
+ "apiId": "SHARD_GHOST"
+ },
+ {
+ "name": "Mana Regeneration",
+ "shardName": "XYZ",
+ "id": "E34",
+ "apiId": "SHARD_XYZ"
+ },
+ {
+ "name": "Hunt Wisdom",
+ "shardName": "Leatherback",
+ "id": "E35",
+ "apiId": "SHARD_LEATHERBACK"
+ },
+ {
+ "name": "Cavern Wisdom",
+ "shardName": "Cavernshade",
+ "id": "E36",
+ "apiId": "SHARD_CAVERNSHADE"
+ },
+ {
+ "name": "Garden Wisdom",
+ "shardName": "Dragonfly",
+ "id": "E37",
+ "apiId": "SHARD_DRAGONFLY"
+ },
+ {
+ "name": "Shadow Elemental",
+ "shardName": "Tenebris",
+ "id": "L1",
+ "apiId": "SHARD_TENEBRIS"
+ },
+ {
+ "name": "Snow Elemental",
+ "shardName": "Blizzard",
+ "id": "L2",
+ "apiId": "SHARD_BLIZZARD"
+ },
+ {
+ "name": "Storm Elemental",
+ "shardName": "Tempest",
+ "id": "L3",
+ "apiId": "SHARD_TEMPEST"
+ },
+ {
+ "name": "Reptiloid",
+ "shardName": "Chameleon",
+ "id": "L4",
+ "apiId": "SHARD_CHAMELEON"
+ },
+ {
+ "name": "Echo of Echoes",
+ "shardName": "Tiamat",
+ "id": "L6",
+ "apiId": "SHARD_TIAMAT"
+ },
+ {
+ "name": "Echo of Wisdom",
+ "shardName": "Wyvern",
+ "id": "L7",
+ "apiId": "SHARD_WYVERN"
+ },
+ {
+ "name": "Cloak Improvement",
+ "shardName": "Tortoise",
+ "id": "L8",
+ "apiId": "SHARD_TORTOISE"
+ },
+ {
+ "name": "Paramount Fortitude",
+ "shardName": "Endstone Protector",
+ "id": "L9",
+ "apiId": "SHARD_ENDSTONE_PROTECTOR"
+ },
+ {