aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/eu/olli
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/olli')
-rw-r--r--src/main/java/eu/olli/cowlection/command/MooCommand.java111
-rw-r--r--src/main/java/eu/olli/cowlection/data/DataHelper.java27
-rw-r--r--src/main/java/eu/olli/cowlection/data/HyPlayerData.java2
-rw-r--r--src/main/java/eu/olli/cowlection/data/HySkyBlockStats.java179
-rw-r--r--src/main/java/eu/olli/cowlection/data/XpTables.java253
-rw-r--r--src/main/java/eu/olli/cowlection/listener/PlayerListener.java3
-rw-r--r--src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java2
-rw-r--r--src/main/java/eu/olli/cowlection/util/ApiUtils.java15
-rw-r--r--src/main/java/eu/olli/cowlection/util/Utils.java3
9 files changed, 449 insertions, 146 deletions
diff --git a/src/main/java/eu/olli/cowlection/command/MooCommand.java b/src/main/java/eu/olli/cowlection/command/MooCommand.java
index e1c8a13..bde7758 100644
--- a/src/main/java/eu/olli/cowlection/command/MooCommand.java
+++ b/src/main/java/eu/olli/cowlection/command/MooCommand.java
@@ -7,10 +7,8 @@ import eu.olli.cowlection.command.exception.InvalidPlayerNameException;
import eu.olli.cowlection.command.exception.MooCommandException;
import eu.olli.cowlection.config.MooConfig;
import eu.olli.cowlection.config.MooGuiConfig;
-import eu.olli.cowlection.data.DataHelper;
-import eu.olli.cowlection.data.Friend;
-import eu.olli.cowlection.data.HySkyBlockStats;
-import eu.olli.cowlection.data.HyStalkingData;
+import eu.olli.cowlection.data.*;
+import eu.olli.cowlection.data.HySkyBlockStats.Profile.Pet;
import eu.olli.cowlection.handler.DungeonCache;
import eu.olli.cowlection.search.GuiSearch;
import eu.olli.cowlection.util.*;
@@ -53,7 +51,9 @@ public class MooCommand extends CommandBase {
// work-around so you can still say 'moo' in chat without triggering the client-side command
String msg = CommandBase.buildString(args, 1);
Minecraft.getMinecraft().thePlayer.sendChatMessage(getCommandName() + (!msg.isEmpty() ? " " + msg : ""));
- } else if (args[0].equalsIgnoreCase("stalk")) {
+ } else if (args[0].equalsIgnoreCase("stalk")
+ || args[0].equalsIgnoreCase("s")
+ || args[0].equalsIgnoreCase("askPolitelyWhereTheyAre")) {
if (args.length != 2) {
throw new WrongUsageException("/" + getCommandName() + " stalk <playerName>");
} else if (!Utils.isValidMcName(args[1])) {
@@ -61,9 +61,12 @@ public class MooCommand extends CommandBase {
} else {
handleStalking(args[1]);
}
- } else if (args[0].equalsIgnoreCase("stalksb") || args[0].equalsIgnoreCase("stalkskyblock") || args[0].equalsIgnoreCase("skyblockstalk")) {
+ } else if (args[0].equalsIgnoreCase("stalkskyblock") || args[0].equalsIgnoreCase("skyblockstalk")
+ || args[0].equalsIgnoreCase("ss")
+ || args[0].equalsIgnoreCase("stalksb") || args[0].equalsIgnoreCase("sbstalk")
+ || args[0].equalsIgnoreCase("askPolitelyAboutTheirSkyBlockProgress")) {
if (args.length != 2) {
- throw new WrongUsageException("/" + getCommandName() + " stalkskyblock <playerName>");
+ throw new WrongUsageException("/" + getCommandName() + " skyblockstalk <playerName>");
} else if (!Utils.isValidMcName(args[1])) {
throw new InvalidPlayerNameException(args[1]);
} else {
@@ -370,21 +373,25 @@ public class MooCommand extends CommandBase {
MooChatComponent skillLevels = new MooChatComponent("Skill levels:").gold();
HySkyBlockStats.Profile.Member member = activeProfile.getMember(stalkedPlayer.getUuid());
- for (Map.Entry<HySkyBlockStats.SkillLevel, Double> entry : member.getSkills().entrySet()) {
+ int skillLevelsSum = 0;
+ for (Map.Entry<XpTables.Skill, Integer> entry : member.getSkills().entrySet()) {
String skill = Utils.fancyCase(entry.getKey().name());
- int level = entry.getKey().getLevel(entry.getValue());
- if (level > 0) {
- String skillLevel = MooConfig.useRomanNumerals() ? Utils.convertArabicToRoman(level) : String.valueOf(level);
- skillLevels.appendFreshSibling(new MooChatComponent.KeyValueTooltipComponent(skill, skillLevel));
- }
+ int level = entry.getValue();
+ String skillLevel = MooConfig.useRomanNumerals() ? Utils.convertArabicToRoman(level) : String.valueOf(level);
+ skillLevels.appendFreshSibling(new MooChatComponent.KeyValueTooltipComponent(skill, skillLevel));
if (level > highestLevel) {
highestSkill = skill;
highestLevel = level;
}
+ if (!skill.equals("Carpentry") && !skill.equals("Runecrafting")) {
+ skillLevelsSum += level;
+ }
}
// output inspired by /profiles hover
+
+ // coins:
String coinsBankAndPurse = (activeProfile.getCoinBank() >= 0) ? Utils.formatNumberWithAbbreviations(activeProfile.getCoinBank() + member.getCoinPurse()) : "API access disabled";
Pair<String, String> fancyFirstJoined = member.getFancyFirstJoined();
@@ -399,6 +406,7 @@ public class MooCommand extends CommandBase {
MooChatComponent sbStats = new MooChatComponent("SkyBlock stats of " + stalkedPlayer.getName() + " (" + activeProfile.getCuteName() + ")").gold().bold().setUrl("https://sky.lea.moe/stats/" + stalkedPlayer.getName() + "/" + activeProfile.getCuteName(), "Click to view SkyBlock stats on sky.lea.moe")
.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Coins", coinsBankAndPurse).setHover(wealthHover));
+ // highest skill + skill average:
if (highestSkill != null) {
if (highestLevel == 0) {
sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Highest Skill", "All skills level 0"));
@@ -406,22 +414,93 @@ public class MooCommand extends CommandBase {
String highestSkillLevel = MooConfig.useRomanNumerals() ? Utils.convertArabicToRoman(highestLevel) : String.valueOf(highestLevel);
sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Highest Skill", highestSkill + " " + highestSkillLevel).setHover(skillLevels));
}
+ double skillAverage = XpTables.Skill.getSkillAverage(skillLevelsSum);
+ sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Skill average", String.format("%.1f", skillAverage))
+ .setHover(new MooChatComponent("Average skill level over all non-cosmetic skills\n(all except Carpentry and Runecrafting)").gray()));
} else {
sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Highest Skill", "API access disabled"));
}
+ // slayer levels:
+ StringBuilder slayerLevels = new StringBuilder();
+ StringBuilder slayerLevelsTooltip = new StringBuilder();
+ MooChatComponent slayerLevelsTooltipComponent = new MooChatComponent("Slayer bosses:").gold();
+ for (Map.Entry<XpTables.Slayer, Integer> entry : member.getSlayerLevels().entrySet()) {
+ String slayerBoss = Utils.fancyCase(entry.getKey().name());
+ if (slayerLevels.length() > 0) {
+ slayerLevels.append(EnumChatFormatting.GRAY).append(" | ").append(EnumChatFormatting.YELLOW);
+ slayerLevelsTooltip.append(EnumChatFormatting.DARK_GRAY).append(" | ").append(EnumChatFormatting.WHITE);
+ }
+ slayerLevelsTooltip.append(slayerBoss);
+ int level = entry.getValue();
+
+ String slayerLevel = (level > 0) ? (MooConfig.useRomanNumerals() ? Utils.convertArabicToRoman(level) : String.valueOf(level)) : "0";
+ slayerLevels.append(slayerLevel);
+ }
+ MooChatComponent slayerLevelsComponent = new MooChatComponent.KeyValueChatComponent("Slayer levels", slayerLevels.toString());
+ slayerLevelsComponent.setHover(slayerLevelsTooltipComponent.appendFreshSibling(new MooChatComponent(slayerLevelsTooltip.toString()).white()));
+ sbStats.appendFreshSibling(slayerLevelsComponent);
+
+ // pets:
+ Pet activePet = null;
+ Pet bestPet = null;
+ StringBuilder pets = new StringBuilder();
+ List<Pet> memberPets = member.getPets();
+ int showPetsLimit = Math.min(16, memberPets.size());
+ for (int i = 0; i < showPetsLimit; i++) {
+ Pet pet = memberPets.get(i);
+ if (pet.isActive()) {
+ activePet = pet;
+ } else {
+ if (activePet == null && bestPet == null && pets.length() == 0) {
+ // no active pet, display highest pet instead
+ bestPet = pet;
+ continue;
+ } else if (pets.length() > 0) {
+ pets.append("\n");
+ }
+ pets.append(pet.toFancyString());
+ }
+ }
+ int remainingPets = memberPets.size() - showPetsLimit;
+ if (remainingPets > 0 && pets.length() > 0) {
+ pets.append("\n").append(EnumChatFormatting.GRAY).append(" + ").append(remainingPets).append(" other pets");
+ }
+ MooChatComponent petsComponent = null;
+ if (activePet != null) {
+ petsComponent = new MooChatComponent.KeyValueChatComponent("Active Pet", activePet.toFancyString());
+ } else if (bestPet != null) {
+ petsComponent = new MooChatComponent.KeyValueChatComponent("Best Pet", bestPet.toFancyString());
+ }
+ if (pets.length() > 0 && petsComponent != null) {
+ petsComponent.setHover(new MooChatComponent("Other pets:").gold().bold().appendFreshSibling(new MooChatComponent(pets.toString())));
+ }
+ if (petsComponent == null) {
+ petsComponent = new MooChatComponent.KeyValueChatComponent("Pet", "none");
+ }
+ sbStats.appendFreshSibling(petsComponent);
+
+ // minions:
Pair<Integer, Integer> uniqueMinionsData = activeProfile.getUniqueMinions();
String uniqueMinions = String.valueOf(uniqueMinionsData.first());
+ String uniqueMinionsHoverText = null;
if (uniqueMinionsData.second() > activeProfile.coopCount()) {
// all players have their unique minions api access disabled
uniqueMinions = "API access disabled";
} else if (uniqueMinionsData.second() > 0) {
// at least one player has their unique minions api access disabled
- uniqueMinions += " or more (" + uniqueMinionsData.second() + "/" + (activeProfile.coopCount() + 1) + " have their API access disabled)";
+ uniqueMinions += EnumChatFormatting.GRAY + " or more";
+ uniqueMinionsHoverText = "" + EnumChatFormatting.WHITE + uniqueMinionsData.second() + " out of " + (activeProfile.coopCount() + 1) + EnumChatFormatting.GRAY + " Co-op members have disabled API access, so some unique minions may be missing";
}
- sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Unique Minions", uniqueMinions));
+ MooChatComponent.KeyValueChatComponent uniqueMinionsComponent = new MooChatComponent.KeyValueChatComponent("Unique Minions", uniqueMinions);
+ if (uniqueMinionsHoverText != null) {
+ uniqueMinionsComponent.setHover(new MooChatComponent(uniqueMinionsHoverText).gray());
+ }
+ sbStats.appendFreshSibling(uniqueMinionsComponent);
+ // fairy souls:
sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Fairy Souls", (member.getFairySoulsCollected() >= 0) ? String.valueOf(member.getFairySoulsCollected()) : "API access disabled"));
+ // profile age:
sbStats.appendFreshSibling(new MooChatComponent.KeyValueChatComponent("Profile age", fancyFirstJoined.first()).setHover(new MooChatComponent.KeyValueTooltipComponent("Join date", fancyFirstJoined.second())));
main.getChatHelper().sendMessage(sbStats);
@@ -548,7 +627,7 @@ public class MooCommand extends CommandBase {
public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) {
if (args.length == 1) {
return getListOfStringsMatchingLastWord(args,
- /* friends & other players */ "stalk", "stalkskyblock", "skyblockstalk", "analyzeIsland", "deaths", "add", "remove", "list", "nameChangeCheck", "toggle",
+ /* friends & other players */ "stalk", "askPolitelyWhereTheyAre", "stalkskyblock", "skyblockstalk", "askPolitelyAboutTheirSkyBlockProgress", "analyzeIsland", "deaths", "add", "remove", "list", "nameChangeCheck", "toggle",
/* miscellaneous */ "config", "search", "guiscale", "rr", "shrug", "apikey",
/* update mod */ "update", "updateHelp", "version", "directory",
/* help */ "help");
diff --git a/src/main/java/eu/olli/cowlection/data/DataHelper.java b/src/main/java/eu/olli/cowlection/data/DataHelper.java
index d6167b0..02911bb 100644
--- a/src/main/java/eu/olli/cowlection/data/DataHelper.java
+++ b/src/main/java/eu/olli/cowlection/data/DataHelper.java
@@ -1,7 +1,9 @@
package eu.olli.cowlection.data;
import eu.olli.cowlection.util.Utils;
+import net.minecraft.util.EnumChatFormatting;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -9,6 +11,31 @@ public final class DataHelper {
private DataHelper() {
}
+ public enum SkyBlockRarity {
+ COMMON(EnumChatFormatting.WHITE),
+ UNCOMMON(EnumChatFormatting.GREEN),
+ RARE(EnumChatFormatting.BLUE),
+ EPIC(EnumChatFormatting.DARK_PURPLE),
+ LEGENDARY(EnumChatFormatting.GOLD),
+ MYTHIC(EnumChatFormatting.LIGHT_PURPLE),
+ SPECIAL(EnumChatFormatting.RED),
+ VERY_SPECIAL(EnumChatFormatting.RED);
+
+ public final EnumChatFormatting rarityColor;
+
+ SkyBlockRarity(EnumChatFormatting color) {
+ this.rarityColor = color;
+ }
+
+ public static SkyBlockRarity[] getPetRarities() {
+ return Arrays.stream(values(), 0, 5).toArray(SkyBlockRarity[]::new);
+ }
+
+ public EnumChatFormatting getColor() {
+ return rarityColor;
+ }
+ }
+
// TODO replace with api request: https://github.com/HypixelDev/PublicAPI/blob/master/Documentation/misc/GameType.md
public enum GameType {
QUAKECRAFT("Quakecraft"),
diff --git a/src/main/java/eu/olli/cowlection/data/HyPlayerData.java b/src/main/java/eu/olli/cowlection/data/HyPlayerData.java
index 06be1e9..a34e9fa 100644
--- a/src/main/java/eu/olli/cowlection/data/HyPlayerData.java
+++ b/src/main/java/eu/olli/cowlection/data/HyPlayerData.java
@@ -73,7 +73,7 @@ public class HyPlayerData {
case "ADMIN":
return EnumChatFormatting.RED + "[ADMIN]";
case "YOUTUBER":
- return EnumChatFormatting.RED + "[" + EnumChatFormatting.WHITE + "YOUTUBER" + EnumChatFormatting.RED + "]";
+ return EnumChatFormatting.RED + "[" + EnumChatFormatting.WHITE + "YOUTUBE" + EnumChatFormatting.RED + "]";
}
}
if (rankPlusColor == null) {
diff --git a/src/main/java/eu/olli/cowlection/data/HySkyBlockStats.java b/src/main/java/eu/olli/cowlection/data/HySkyBlockStats.java
index 45727fc..f5f3d0d 100644
--- a/src/main/java/eu/olli/cowlection/data/HySkyBlockStats.java
+++ b/src/main/java/eu/olli/cowlection/data/HySkyBlockStats.java
@@ -1,13 +1,11 @@
package eu.olli.cowlection.data;
+import com.google.common.collect.ComparisonChain;
import com.mojang.realmsclient.util.Pair;
import com.mojang.util.UUIDTypeAdapter;
import eu.olli.cowlection.util.Utils;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.UUID;
+import java.util.*;
public class HySkyBlockStats {
private boolean success;
@@ -115,6 +113,8 @@ public class HySkyBlockStats {
private double experience_skill_carpentry = -1;
private double experience_skill_runecrafting = -1;
private double experience_skill_taming = -1;
+ private Map<String, SlayerBossDetails> slayer_bosses;
+ private List<Pet> pets;
/**
* No-args constructor for GSON
@@ -134,40 +134,82 @@ public class HySkyBlockStats {
return fairy_souls_collected;
}
- public Map<SkillLevel, Double> getSkills() {
- Map<SkillLevel, Double> skills = new TreeMap<>();
+ public Map<XpTables.Skill, Integer> getSkills() {
+ Map<XpTables.Skill, Integer> skills = new TreeMap<>();
if (experience_skill_farming >= 0) {
- skills.put(SkillLevel.FARMING, experience_skill_farming);
+ skills.put(XpTables.Skill.FARMING, XpTables.Skill.FARMING.getLevel(experience_skill_farming));
}
if (experience_skill_mining >= 0) {
- skills.put(SkillLevel.MINING, experience_skill_mining);
+ skills.put(XpTables.Skill.MINING, XpTables.Skill.MINING.getLevel(experience_skill_mining));
}
if (experience_skill_combat >= 0) {
- skills.put(SkillLevel.COMBAT, experience_skill_combat);
+ skills.put(XpTables.Skill.COMBAT, XpTables.Skill.COMBAT.getLevel(experience_skill_combat));
}
if (experience_skill_foraging >= 0) {
- skills.put(SkillLevel.FORAGING, experience_skill_foraging);
+ skills.put(XpTables.Skill.FORAGING, XpTables.Skill.FORAGING.getLevel(experience_skill_foraging));
}
if (experience_skill_fishing >= 0) {
- skills.put(SkillLevel.FISHING, experience_skill_fishing);
+ skills.put(XpTables.Skill.FISHING, XpTables.Skill.FISHING.getLevel(experience_skill_fishing));
}
if (experience_skill_enchanting >= 0) {
- skills.put(SkillLevel.ENCHANTING, experience_skill_enchanting);
+ skills.put(XpTables.Skill.ENCHANTING, XpTables.Skill.ENCHANTING.getLevel(experience_skill_enchanting));
}
if (experience_skill_alchemy >= 0) {
- skills.put(SkillLevel.ALCHEMY, experience_skill_alchemy);
+ skills.put(XpTables.Skill.ALCHEMY, XpTables.Skill.ALCHEMY.getLevel(experience_skill_alchemy));
}
if (experience_skill_carpentry >= 0) {
- skills.put(SkillLevel.CARPENTRY, experience_skill_carpentry);
+ skills.put(XpTables.Skill.CARPENTRY, XpTables.Skill.CARPENTRY.getLevel(experience_skill_carpentry));
}
if (experience_skill_runecrafting >= 0) {
- skills.put(SkillLevel.RUNECRAFTING, experience_skill_runecrafting);
+ skills.put(XpTables.Skill.RUNECRAFTING, XpTables.Skill.RUNECRAFTING.getLevel(experience_skill_runecrafting));
}
if (experience_skill_taming >= 0) {
- skills.put(SkillLevel.TAMING, experience_skill_taming);
+ skills.put(XpTables.Skill.TAMING, XpTables.Skill.TAMING.getLevel(experience_skill_taming));
}
return skills;
}
+
+ public Map<XpTables.Slayer, Integer> getSlayerLevels() {
+ Map<XpTables.Slayer, Integer> slayerLevels = new EnumMap<>(XpTables.Slayer.class);
+ for (XpTables.Slayer slayerBoss : XpTables.Slayer.values()) {
+ SlayerBossDetails bossDetails = slayer_bosses.get(slayerBoss.name().toLowerCase());
+ int slayerLevel = slayerBoss.getLevel(bossDetails.xp);
+ slayerLevels.put(slayerBoss, slayerLevel);
+ }
+ return slayerLevels;
+ }
+
+ public List<Pet> getPets() {
+ pets.sort((p1, p2) -> ComparisonChain.start().compare(p2.active, p1.active).compare(p2.getRarity(), p1.getRarity()).compare(p2.exp, p1.exp).result());
+ return pets;
+ }
+ }
+
+ private static class SlayerBossDetails {
+ private int xp;
+ }
+
+ public static class Pet {
+ private String type;
+ private double exp;
+ private String tier;
+ private boolean active;
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public DataHelper.SkyBlockRarity getRarity() {
+ return DataHelper.SkyBlockRarity.valueOf(tier);
+ }
+
+ public String toFancyString() {
+ return getRarity().getColor() + Utils.fancyCase(type) + " " + getLevel();
+ }
+
+ private int getLevel() {
+ return XpTables.Pet.getLevel(tier, exp);
+ }
}
public static class Banking {
@@ -194,109 +236,4 @@ public class HySkyBlockStats {
// }
}
}
-
- public enum SkillLevel {
- FARMING, MINING, COMBAT, FORAGING, FISHING, ENCHANTING, ALCHEMY, CARPENTRY, RUNECRAFTING(true), TAMING;
- private final boolean alternativeXpFormula;
- private static final TreeMap<Integer, Integer> XP_TO_LEVEL = new TreeMap<>();
- private static final TreeMap<Integer, Integer> XP_TO_LEVEL_ALTERNATIVE = new TreeMap<>();
-
- static {
- // exp data taken from https://api.hypixel.net/resources/skyblock/skills
- XP_TO_LEVEL.put(0, 0);
- XP_TO_LEVEL.put(50, 1);
- XP_TO_LEVEL.put(175, 2);
- XP_TO_LEVEL.put(375, 3);
- XP_TO_LEVEL.put(675, 4);
- XP_TO_LEVEL.put(1175, 5);
- XP_TO_LEVEL.put(1925, 6);
- XP_TO_LEVEL.put(2925, 7);
- XP_TO_LEVEL.put(4425, 8);
- XP_TO_LEVEL.put(6425, 9);
- XP_TO_LEVEL.put(9925, 10);
- XP_TO_LEVEL.put(14925, 11);
- XP_TO_LEVEL.put(22425, 12);
- XP_TO_LEVEL.put(32425, 13);
- XP_TO_LEVEL.put(47425, 14);
- XP_TO_LEVEL.put(67425, 15);
- XP_TO_LEVEL.put(97425, 16);
- XP_TO_LEVEL.put(147425, 17);
- XP_TO_LEVEL.put(222425, 18);
- XP_TO_LEVEL.put(322425, 19);
- XP_TO_LEVEL.put(522425, 20);
- XP_TO_LEVEL.put(822425, 21);
- XP_TO_LEVEL.put(1222425, 22);
- XP_TO_LEVEL.put(1722425, 23);
- XP_TO_LEVEL.put(2322425, 24);
- XP_TO_LEVEL.put(3022425, 25);
- XP_TO_LEVEL.put(3822425, 26);
- XP_TO_LEVEL.put(4722425, 27);
- XP_TO_LEVEL.put(5722425, 28);
- XP_TO_LEVEL.put(6822425, 29);
- XP_TO_LEVEL.put(8022425, 30);
- XP_TO_LEVEL.put(9322425, 31);
- XP_TO_LEVEL.put(10722425, 32);
- XP_TO_LEVEL.put(12222425, 33);
- XP_TO_LEVEL.put(13822425, 34);
- XP_TO_LEVEL.put(15522425, 35);
- XP_TO_LEVEL.put(17322425, 36);
- XP_TO_LEVEL.put(19222425, 37);
- XP_TO_LEVEL.put(21222425, 38);
- XP_TO_LEVEL.put(23322425, 39);
- XP_TO_LEVEL.put(25522425, 40);
- XP_TO_LEVEL.put(27822425, 41);
- XP_TO_LEVEL.put(30222425, 42);
- XP_TO_LEVEL.put(32722425, 43);
- XP_TO_LEVEL.put(35322425, 44);
- XP_TO_LEVEL.put(38072425, 45);
- XP_TO_LEVEL.put(40972425, 46);
- XP_TO_LEVEL.put(44072425, 47);
- XP_TO_LEVEL.put(47472425, 48);
- XP_TO_LEVEL.put(51172425, 49);
- XP_TO_LEVEL.put(55172425, 50);
-
- XP_TO_LEVEL_ALTERNATIVE.put(0, 0);
- XP_TO_LEVEL_ALTERNATIVE.put(50, 1);
- XP_TO_LEVEL_ALTERNATIVE.put(150, 2);
- XP_TO_LEVEL_ALTERNATIVE.put(275, 3);
- XP_TO_LEVEL_ALTERNATIVE.put(435, 4);
- XP_TO_LEVEL_ALTERNATIVE.put(635, 5);
- XP_TO_LEVEL_ALTERNATIVE.put(885, 6);
- XP_TO_LEVEL_ALTERNATIVE.put(1200, 7);
- XP_TO_LEVEL_ALTERNATIVE.put(1600, 8);
- XP_TO_LEVEL_ALTERNATIVE.put(2100, 9);
- XP_TO_LEVEL_ALTERNATIVE.put(2725, 10);
- XP_TO_LEVEL_ALTERNATIVE.put(3510, 11);
- XP_TO_LEVEL_ALTERNATIVE.put(4510, 12);
- XP_TO_LEVEL_ALTERNATIVE.put(5760, 13);
- XP_TO_LEVEL_ALTERNATIVE.put(7325, 14);
- XP_TO_LEVEL_ALTERNATIVE.put(9325, 15);
- XP_TO_LEVEL_ALTERNATIVE.put(11825, 16);
- XP_TO_LEVEL_ALTERNATIVE.put(14950, 17);
- XP_TO_LEVEL_ALTERNATIVE.put(18950, 18);
- XP_TO_LEVEL_ALTERNATIVE.put(23950, 19);
- XP_TO_LEVEL_ALTERNATIVE.put(30200, 20);
- XP_TO_LEVEL_ALTERNATIVE.put(38050, 21);
- XP_TO_LEVEL_ALTERNATIVE.put(47850, 22);
- XP_TO_LEVEL_ALTERNATIVE.put(60100, 23);
- XP_TO_LEVEL_ALTERNATIVE.put(75400, 24);
-
- }
-
- SkillLevel() {
- this(false);
- }
-
- SkillLevel(boolean alternativeXpFormula) {
- this.alternativeXpFormula = alternativeXpFormula;
- }
-
- public int getLevel(double exp) {
- if (alternativeXpFormula) {
- return XP_TO_LEVEL_ALTERNATIVE.floorEntry((int) exp).getValue();
- } else {
- return XP_TO_LEVEL.floorEntry((int) exp).getValue();
- }
- }
- }
}
diff --git a/src/main/java/eu/olli/cowlection/data/XpTables.java b/src/main/java/eu/olli/cowlection/data/XpTables.java
new file mode 100644
index 0000000..85b77a3
--- /dev/null
+++ b/src/main/java/eu/olli/cowlection/data/XpTables.java
@@ -0,0 +1,253 @@
+package eu.olli.cowlection.data;
+
+import java.util.*;
+
+public class XpTables {
+ public enum Skill {
+ FARMING, MINING, COMBAT, FORAGING, FISHING, ENCHANTING, ALCHEMY, CARPENTRY, RUNECRAFTING(true), TAMING;
+ private final boolean alternativeXpFormula;
+ private static final TreeMap<Integer, Integer> XP_TO_LEVEL = new TreeMap<>();
+ private static final TreeMap<Integer, Integer> XP_TO_LEVEL_ALTERNATIVE = new TreeMap<>();
+
+ static {
+ // exp data taken from https://api.hypixel.net/resources/skyblock/skills
+ XP_TO_LEVEL.put(0, 0);
+ XP_TO_LEVEL.put(50, 1);
+ XP_TO_LEVEL.put(175, 2);
+ XP_TO_LEVEL.put(375, 3);
+ XP_TO_LEVEL.put(675, 4);
+ XP_TO_LEVEL.put(1175, 5);
+ XP_TO_LEVEL.put(1925, 6);
+ XP_TO_LEVEL.put(2925, 7);
+ XP_TO_LEVEL.put(4425, 8);
+ XP_TO_LEVEL.put(6425, 9);
+ XP_TO_LEVEL.put(9925, 10);
+ XP_TO_LEVEL.put(14925, 11);
+ XP_TO_LEVEL.put(22425, 12);
+ XP_TO_LEVEL.put(32425, 13);
+ XP_TO_LEVEL.put(47425, 14);
+ XP_TO_LEVEL.put(67425, 15);
+ XP_TO_LEVEL.put(97425, 16);
+ XP_TO_LEVEL.put(147425, 17);
+ XP_TO_LEVEL.put(222425, 18);
+ XP_TO_LEVEL.put(322425, 19);
+ XP_TO_LEVEL.put(522425, 20);
+ XP_TO_LEVEL.put(822425, 21);
+ XP_TO_LEVEL.put(1222425, 22);
+ XP_TO_LEVEL.put(1722425, 23);
+ XP_TO_LEVEL.put(2322425, 24);
+ XP_TO_LEVEL.put(3022425, 25);
+ XP_TO_LEVEL.put(3822425, 26);
+ XP_TO_LEVEL.put(4722425, 27);
+ XP_TO_LEVEL.put(5722425, 28);
+ XP_TO_LEVEL.put(6822425, 29);
+ XP_TO_LEVEL.put(8022425, 30);
+ XP_TO_LEVEL.put(9322425, 31);
+ XP_TO_LEVEL.put(10722425, 32);
+ XP_TO_LEVEL.put(12222425, 33);
+ XP_TO_LEVEL.put(13822425, 34);
+ XP_TO_LEVEL.put(15522425, 35);
+ XP_TO_LEVEL.put(17322425, 36);
+ XP_TO_LEVEL.put(19222425, 37);
+ XP_TO_LEVEL.put(21222425, 38);
+ XP_TO_LEVEL.put(23322425, 39);
+ XP_TO_LEVEL.put(25522425, 40);
+ XP_TO_LEVEL.put(27822425, 41);
+ XP_TO_LEVEL.put(30222425, 42);
+ XP_TO_LEVEL.put(32722425, 43);
+ XP_TO_LEVEL.put(35322425, 44);
+ XP_TO_LEVEL.put(38072425, 45);
+ XP_TO_LEVEL.put(40972425, 46);
+ XP_TO_LEVEL.put(44072425, 47);
+ XP_TO_LEVEL.put(47472425, 48);
+ XP_TO_LEVEL.put(51172425, 49);
+ XP_TO_LEVEL.put(55172425, 50);
+
+ XP_TO_LEVEL_ALTERNATIVE.put(0, 0);
+ XP_TO_LEVEL_ALTERNATIVE.put(50, 1);
+ XP_TO_LEVEL_ALTERNATIVE.put(150, 2);
+ XP_TO_LEVEL_ALTERNATIVE.put(275, 3);
+ XP_TO_LEVEL_ALTERNATIVE.put(435, 4);
+ XP_TO_LEVEL_ALTERNATIVE.put(635, 5);
+ XP_TO_LEVEL_ALTERNATIVE.put(885, 6);
+ XP_TO_LEVEL_ALTERNATIVE.put(1200, 7);
+ XP_TO_LEVEL_ALTERNATIVE.put(1600, 8);
+ XP_TO_LEVEL_ALTERNATIVE.put(2100, 9);
+ XP_TO_LEVEL_ALTERNATIVE.put(2725, 10);
+ XP_TO_LEVEL_ALTERNATIVE.put(3510, 11);
+ XP_TO_LEVEL_ALTERNATIVE.put(4510, 12);
+ XP_TO_LEVEL_ALTERNATIVE.put(5760, 13);
+ XP_TO_LEVEL_ALTERNATIVE.put(7325, 14);
+ XP_TO_LEVEL_ALTERNATIVE.put(9325, 15);
+ XP_TO_LEVEL_ALTERNATIVE.put(11825, 16);
+ XP_TO_LEVEL_ALTERNATIVE.put(14950, 17);
+ XP_TO_LEVEL_ALTERNATIVE.put(18950, 18);
+ XP_TO_LEVEL_ALTERNATIVE.put(23950, 19);
+ XP_TO_LEVEL_ALTERNATIVE.put(30200, 20);
+ XP_TO_LEVEL_ALTERNATIVE.put(38050, 21);
+ XP_TO_LEVEL_ALTERNATIVE.put(47850, 22);
+ XP_TO_LEVEL_ALTERNATIVE.put(60100, 23);
+ XP_TO_LEVEL_ALTERNATIVE.put(75400, 24);
+
+ }
+
+ Skill() {
+ this(false);
+ }
+
+ Skill(boolean alternativeXpFormula) {
+ this.alternativeXpFormula = alternativeXpFormula;
+ }
+
+ public int getLevel(double exp) {
+ if (alternativeXpFormula) {
+ return XP_TO_LEVEL_ALTERNATIVE.floorEntry((int) exp).getValue();
+ } else {
+ return XP_TO_LEVEL.floorEntry((int) exp).getValue();
+ }
+ }
+
+ public static double getSkillAverage(int skillLevelsSum) {
+ return skillLevelsSum / (getSkillCount() * 1d);
+ }
+
+ /**
+ * Amount of skills without cosmetic skills (Carpentry, Runecrafting)
+ *
+ * @return amount of existing skills
+ */
+ private static int getSkillCount() {
+ return values().length - 2;
+ }
+ }
+
+ public enum Slayer {
+ ZOMBIE, SPIDER, WOLF(true);
+ private final boolean alternativeXpFormula;
+ /**
+ * Valid for Zombie + Spider
+ */
+ private static final TreeMap<Integer, Integer> XP_TO_LEVEL = new TreeMap<>();
+ /**
+ * Valid for Wolf
+ */
+ private static final TreeMap<Integer, Integer> XP_TO_LEVEL_ALTERNATIVE = new TreeMap<>();
+
+ static {
+ XP_TO_LEVEL.put(0, 0);
+ XP_TO_LEVEL.put(5, 1);
+ XP_TO_LEVEL.put(15, 2);
+ XP_TO_LEVEL.put(200, 3);
+ XP_TO_LEVEL.put(1000, 4);
+ XP_TO_LEVEL.put(5000, 5);
+ XP_TO_LEVEL.put(20000, 6);
+ XP_TO_LEVEL.put(100000, 7);
+ XP_TO_LEVEL.put(400000, 8);
+ XP_TO_LEVEL.put(1000000, 9);
+
+ XP_TO_LEVEL_ALTERNATIVE.put(0, 0);
+ XP_TO_LEVEL_ALTERNATIVE.put(5, 1);
+ XP_TO_LEVEL_ALTERNATIVE.put(15, 2);
+ XP_TO_LEVEL_ALTERNATIVE.put(200, 3);
+ XP_TO_LEVEL_ALTERNATIVE.put(1500, 4);
+ XP_TO_LEVEL_ALTERNATIVE.put(5000, 5);
+ XP_TO_LEVEL_ALTERNATIVE.put(20000, 6);
+ XP_TO_LEVEL_ALTERNATIVE.put(100000, 7);
+ XP_TO_LEVEL_ALTERNATIVE.put(400000, 8);
+ XP_TO_LEVEL_ALTERNATIVE.put(1000000, 9);
+ }
+
+ Slayer() {
+ this(false);
+ }
+
+ Slayer(boolean alternativeXpFormula) {
+ this.alternativeXpFormula = alternativeXpFormula;
+ }
+
+ public int getLevel(double exp) {
+ if (alternativeXpFormula) {
+ return XP_TO_LEVEL_ALTERNATIVE.floorEntry((int) exp).getValue();
+ } else {
+ return XP_TO_LEVEL.floorEntry((int) exp).getValue();
+ }
+ }
+ }
+
+ public static final class Pet {
+ private static final Map<DataHelper.SkyBlockRarity, TreeSet<Integer>> PET_XP = new HashMap<>();
+
+ private Pet() {
+ }
+
+ static {
+ for (DataHelper.SkyBlockRarity rarity : DataHelper.SkyBlockRarity.getPetRarities()) {
+ PET_XP.put(rarity, new TreeSet<>());
+ }
+ Collections.addAll(PET_XP.get(DataHelper.SkyBlockRarity.COMMON),
+ 0, 100, 210, 330, 460, 605, 765, 940, 1130, 1340, // 1-10
+ 1570, 1820, 2095, 2395, 2725, 3085, 3485, 3925, 4415, 4955, // 11-20
+ 5555, 6215, 6945, 7745, 8625, 9585, 10635, 11785, 13045, 14425, // 21-30
+ 15935, 17585, 19385, 21345, 23475, 25785, 28285, 30985, 33905, 37065, // 31-40
+ 40485, 44185, 48185, 52535, 57285, 62485, 68185, 74485, 81485, 89285, // 41-50
+ 97985, 107685, 118485, 130485, 143785, 158485, 174685, 192485, 211985, 233285, // 51-60
+ 256485, 281685, 309085, 338885, 371285, 406485, 444685, 486085, 530885, 579285, // 61-70
+ 631485, 687685, 748085, 812885, 882285, 956485, 1035685, 1120385, 1211085, 1308285, // 71-80
+ 1412485, 1524185, 1643885, 1772085, 1909285, 2055985, 2212685, 2380385, 2560085, 2752785, // 81-90
+ 2959485, 3181185, 3418885, 3673585, 3946285, 4237985, 4549685, 4883385, 5241085, 5624785); // 91-100
+ Collections.addAll(PET_XP.get(DataHelper.SkyBlockRarity.UNCOMMON),
+ 0, 175, 365, 575, 805, 1055, 1330, 1630, 1960, 2320, // 1-10
+ 2720, 3160, 3650, 4190, 4790, 5450, 6180, 6980, 7860, 8820, // 11-20
+ 9870, 11020, 12280, 13660, 15170, 16820, 18620, 20580, 22710, 25020, // 21-30
+ 27520, 30220, 33140, 36300, 39720, 43420, 47420, 51770, 56520, 61720, // 31-40
+ 67420, 73720, 80720, 88520, 97220, 106920, 117720, 129720, 143020, 157720, // 41-50
+ 173920, 191720, 211220, 232520, 255720, 280920, 308320, 338120, 370520, 405720, // 51-60
+ 443920, 485320, 530120, 578520, 630720, 686920, 747320, 812120, 881520, 955720, // 61-70
+ 1034920, 1119620, 1210320, 1307520, 1411720, 1523420, 1643120, 1771320, 1908520, 2055220, // 71-80
+ 2211920, 2379620, 2559320, 2752020, 2958720, 3180420, 3418120, 3672820, 3945520, 4237220, // 81-90
+ 4548920, 4882620, 5240320, 5624020, 6035720, 6477420, 6954120, 7470820, 8032520, 8644220); // 91-100
+ Collections.addAll(PET_XP.get(DataHelper.SkyBlockRarity.RARE),
+ 0, 275, 575, 905, 1265, 1665, 2105, 2595, 3135, 3735, // 1-10
+ 4395, 5125, 5925, 6805, 7765, 8815, 9965, 11225, 12605, 14115, // 11-20
+ 15765, 17565, 19525, 21655, 23965, 26465, 29165, 32085, 35245, 38665, // 21-30
+ 42365, 46365, 50715, 55465, 60665, 66365, 72665, 79665, 87465, 96165, // 31-40
+ 105865, 116665, 128665, 141965, 156665, 172865, 190665, 210165, 231465, 254665, // 41-50
+ 279865, 307265, 337065, 369465, 404665, 442865, 484265, 529065, 577465, 629665, // 51-60
+ 685865, 746265, 811065, 880465, 954665, 1033865, 1118565, 1209265, 1306465, 1410665, // 61-70
+ 1522365, 1642065, 1770265, 1907465, 2054165, 2210865, 2378565, 2558265, 2750965, 2957665, // 71-80
+ 3179365, 3417065, 3671765, 3944465, 4236165, 4547865, 4881565, 5239265, 5622965, 6034665, // 81-90
+ 6476365, 6953065, 7469765, 8031465, 8643165, 9309865, 10036565, 10828265, 11689965, 12626665); // 91-100
+ Collections.addAll(PET_XP.get(DataHelper.SkyBlockRarity.EPIC),
+ 0, 440, 930, 1470, 2070, 2730, 3460, 4260, 5140, 6100, // 1-10
+ 7150, 8300, 9560, 10940, 12450, 14100, 15900, 17860, 19990, 22300, // 11-20
+ 24800, 27500, 30420, 33580, 37000, 40700, 44700, 49050, 53800, 59000, // 21-30
+ 64700, 71000, 78000, 85800, 94500, 104200, 115000, 127000, 140300, 155000, // 31-40
+ 171200, 189000, 208500, 229800, 253000, 278200, 305600, 335400, 367800, 403000, // 41-50
+ 441200, 482600, 527400, 575800, 628000, 684200, 744600, 809400, 878800, 953000, // 51-60
+ 1032200, 1116900, 1207600, 1304800, 1409000, 1520700, 1640400, 1768600, 1905800, 2052500, // 61-70
+ 2209200, 2376900, 2556600, 2749300, 2956000, 3177700, 3415400, 3670100, 3942800, 4234500, // 71-80
+ 4546200, 4879900, 5237600, 5621300, 6033000, 6474700, 6951400, 7468100, 8029800, 8641500, // 81-90
+ 9308200, 10034900, 10826600, 11688300, 12625000, 13641700, 14743400, 15935100, 17221800, 18608500); // 91-100
+ Collections.addAll(PET_XP.get(DataHelper.SkyBlockRarity.LEGENDARY),
+ 0, 660, 1390, 2190, 3070, 4030, 5080, 6230, 7490, 8870, // 1-10
+ 10380, 12030, 13830, 15790, 17920, 20230, 22730, 25430, 28350, 31510, // 11-20
+ 34930, 38630, 42630, 46980, 51730, 56930, 62630, 68930, 75930, 83730, // 21-30
+ 92430, 102130, 112930, 124930, 138230, 152930, 169130, 186930, 206430, 227730, // 31-40
+ 250930, 276130, 303530, 333330, 365730, 400930, 439130, 480530, 525330, 573730, // 41-50
+ 625930, 682130, 742530, 807330, 876730, 950930, 1030130, 1114830, 1205530, 1302730, // 51-60
+ 1406930, 1518630, 1638330, 1766530, 1903730, 2050430, 2207130, 2374830, 2554530, 2747230, // 61-70
+ 2953930, 3175630, 3413330, 3668030, 3940730, 4232430, 4544130, 4877830, 5235530, 5619230, // 71-80
+ 6030930, 6472630, 6949330, 7466030, 8027730, 8639430, 9306130, 10032830, 10824530, 11686230, // 81-90
+ 12622930, 13639630, 14741330, 15933030, 17219730, 18606430, 20103130, 21719830, 23466530, 25353230); // 91-100
+ }
+
+ public static int getLevel(String rarity, double exp) {
+ TreeSet<Integer> xpToLevels = PET_XP.get(DataHelper.SkyBlockRarity.valueOf(rarity));
+ if (xpToLevels != null) {
+ return xpToLevels.headSet((int) exp, true).size();
+ } else {
+ return -1;
+ }
+ }
+ }
+}
diff --git a/src/main/java/eu/olli/cowlection/listener/PlayerListener.java b/src/main/java/eu/olli/cowlection/listener/PlayerListener.java
index aa91678..36e13cb 100644
--- a/src/main/java/eu/olli/cowlection/listener/PlayerListener.java
+++ b/src/main/java/eu/olli/cowlection/listener/PlayerListener.java
@@ -73,6 +73,7 @@ public class PlayerListener {
main.getVersionChecker().runUpdateCheck(false);
new TickDelay(() -> main.getChatHelper().sendOfflineMessages(), 6 * 20);
isOnSkyBlock = false;
+ main.getLogger().info("Joined the server");
}
@SubscribeEvent
@@ -113,6 +114,7 @@ public class PlayerListener {
if (skyBlockListener != null) {
MinecraftForge.EVENT_BUS.unregister(skyBlockListener);
skyBlockListener = null;
+ main.getLogger().info("Left SkyBlock");
}
}
@@ -121,5 +123,6 @@ public class PlayerListener {
main.getFriendsHandler().saveBestFriends();
main.getPlayerCache().clearAllCaches();
unregisterSkyBlockListeners();
+ main.getLogger().info("Left the server");
}
}
diff --git a/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java b/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java
index c10360b..a05bda2 100644
--- a/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java
+++ b/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java
@@ -271,7 +271,7 @@ public class DungeonsListener {
int guiTop = (guiChest.height - ySize) / 2;
GlStateManager.pushMatrix();
- GlStateManager.translate(0, 0, 1);
+ GlStateManager.translate(0, 0, 280);
float scaleFactor = 0.8f;
GlStateManager.scale(scaleFactor, scaleFactor, 0);
for (Slot inventorySlot : inventorySlots.inventorySlots) {
diff --git a/src/main/java/eu/olli/cowlection/util/ApiUtils.java b/src/main/java/eu/olli/cowlection/util/ApiUtils.java
index f4f23ff..04a67cd 100644
--- a/src/main/java/eu/olli/cowlection/util/ApiUtils.java
+++ b/src/main/java/eu/olli/cowlection/util/ApiUtils.java
@@ -2,14 +2,15 @@ package eu.olli.cowlection.util;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
import com.mojang.util.UUIDTypeAdapter;
import eu.olli.cowlection.Cowlection;
import eu.olli.cowlection.command.exception.ThrowingConsumer;
import eu.olli.cowlection.config.MooConfig;
import eu.olli.cowlection.data.Friend;
+import eu.olli.cowlection.data.HyPlayerData;
import eu.olli.cowlection.data.HySkyBlockStats;
import eu.olli.cowlection.data.HyStalkingData;
-import eu.olli.cowlection.data.HyPlayerData;
import org.apache.http.HttpStatus;
import java.io.BufferedReader;
@@ -44,7 +45,7 @@ public class ApiUtils {
} else {
return GsonUtils.fromJson(reader, Friend.class);
}
- } catch (IOException e) {
+ } catch (IOException | JsonSyntaxException e) {
e.printStackTrace();
}
return null;
@@ -64,7 +65,7 @@ public class ApiUtils {
return nameHistoryData.get(nameHistoryData.size() - 1).getAsJsonObject().get("name").getAsString();
}
}
- } catch (IOException e) {
+ } catch (IOException | JsonSyntaxException e) {
e.printStackTrace();
}
return null;
@@ -79,7 +80,7 @@ public class ApiUtils {
if (reader != null) {
return GsonUtils.fromJson(reader, HyStalkingData.class);
}
- } catch (IOException e) {
+ } catch (IOException | JsonSyntaxException e) {
e.printStackTrace();
}
return null;
@@ -94,7 +95,7 @@ public class ApiUtils {
if (reader != null) {
return GsonUtils.fromJson(reader, HySkyBlockStats.class);
}
- } catch (IOException e) {
+ } catch (IOException | JsonSyntaxException e) {
e.printStackTrace();
}
return null;
@@ -109,7 +110,7 @@ public class ApiUtils {
if (reader != null) {
return GsonUtils.fromJson(reader, HyPlayerData.class);
}
- } catch (IOException e) {
+ } catch (IOException | JsonSyntaxException e) {
e.printStackTrace();
}
return null;
@@ -118,7 +119,7 @@ public class ApiUtils {
private static BufferedReader makeApiCall(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(5000);
- connection.setReadTimeout(5000);
+ connection.setReadTimeout(10000);
connection.addRequestProperty("User-Agent", "Forge Mod " + Cowlection.MODNAME + "/" + Cowlection.VERSION + " (" + Cowlection.GITURL + ")");
connection.getResponseCode();
diff --git a/src/main/java/eu/olli/cowlection/util/Utils.java b/src/main/java/eu/olli/cowlection/util/Utils.java
index 92f4c9e..629a3dd 100644
--- a/src/main/java/eu/olli/cowlection/util/Utils.java
+++ b/src/main/java/eu/olli/cowlection/util/Utils.java
@@ -174,6 +174,9 @@ public final class Utils {
* @see <a href="https://stackoverflow.com/a/48357180">Source</a>
*/
public static String convertArabicToRoman(int number) {
+ if (number == 0) {
+ return "0";
+ }
String romanOnes = arabicToRomanChars(number % 10, "I", "V", "X");
number /= 10;