aboutsummaryrefslogtreecommitdiff
path: root/src/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/features')
-rw-r--r--src/features/bestiary/index.js250
-rw-r--r--src/features/bestiary/metadata.json8
-rw-r--r--src/features/betterGuis/dungeonReadyGui.js284
-rw-r--r--src/features/betterGuis/index.js386
-rw-r--r--src/features/betterGuis/metadata.json8
-rw-r--r--src/features/betterGuis/museumGui.js1024
-rw-r--r--src/features/changeLogGUI/index.js255
-rw-r--r--src/features/changeLogGUI/metadata.json8
-rw-r--r--src/features/cosmetics/cosmetic.js29
-rw-r--r--src/features/cosmetics/cosmetic/dragon/dragonWings.js482
-rw-r--r--src/features/cosmetics/hiddenRequirement.js4
-rw-r--r--src/features/cosmetics/index.js271
-rw-r--r--src/features/cosmetics/metadata.json8
-rw-r--r--src/features/dataLoader/index.js280
-rw-r--r--src/features/dataLoader/metadata.json8
-rw-r--r--src/features/dungeonMap/dungeonBossImages/f1.pngbin0 -> 4681 bytes
-rw-r--r--src/features/dungeonMap/index.js1186
-rw-r--r--src/features/dungeonMap/map.pngbin0 -> 5648 bytes
-rw-r--r--src/features/dungeonMap/metadata.json8
-rw-r--r--src/features/dungeonMap2/DungeonMapData.js513
-rw-r--r--src/features/dungeonMap2/DungeonMapRoom.js256
-rw-r--r--src/features/dungeonMap2/DungeonRoomStaticData.js19
-rw-r--r--src/features/dungeonMap2/index.js312
-rw-r--r--src/features/dungeonMap2/metadata.json8
-rw-r--r--src/features/dungeonRoutes/index.js481
-rw-r--r--src/features/dungeonRoutes/metadata.json8
-rw-r--r--src/features/dungeonRoutes/routesData.json1296
-rw-r--r--src/features/dungeonSolvers/f7data.json263
-rw-r--r--src/features/dungeonSolvers/index.js1270
-rw-r--r--src/features/dungeonSolvers/metadata.json8
-rw-r--r--src/features/events/index.js816
-rw-r--r--src/features/events/metadata.json8
-rw-r--r--src/features/eventsGUI/index.js287
-rw-r--r--src/features/eventsGUI/metadata.json8
-rw-r--r--src/features/featureBase/index.js24
-rw-r--r--src/features/featureBase/metadata.json8
-rw-r--r--src/features/fpsImproveGui/index.js49
-rw-r--r--src/features/fpsImproveGui/metadata.json8
-rw-r--r--src/features/fragBot/index.js84
-rw-r--r--src/features/fragBot/metadata.json8
-rw-r--r--src/features/friendsGUI/index.js363
-rw-r--r--src/features/friendsGUI/metadata.json8
-rw-r--r--src/features/globalSettings/firstLoadPage.js42
-rw-r--r--src/features/globalSettings/firstLoadPages.js135
-rw-r--r--src/features/globalSettings/index.js1121
-rw-r--r--src/features/globalSettings/metadata.json8
-rw-r--r--src/features/globalSettings/warps.json41
-rw-r--r--src/features/guild/index.js70
-rw-r--r--src/features/guild/metadata.json8
-rw-r--r--src/features/hud/HudTextElement.js143
-rw-r--r--src/features/hud/index.js812
-rw-r--r--src/features/hud/metadata.json8
-rw-r--r--src/features/lockedFeatures/index.js121
-rw-r--r--src/features/lockedFeatures/metadata.json8
-rw-r--r--src/features/mining/coords.json202
-rw-r--r--src/features/mining/index.js469
-rw-r--r--src/features/mining/metadata.json8
-rw-r--r--src/features/nether/index.js406
-rw-r--r--src/features/nether/metadata.json8
-rw-r--r--src/features/networthGUI/index.js239
-rw-r--r--src/features/networthGUI/metadata.json8
-rw-r--r--src/features/performance/hiddenRequirement.js7
-rw-r--r--src/features/performance/index.js128
-rw-r--r--src/features/performance/metadata.json8
-rw-r--r--src/features/senitherGui/index.js453
-rw-r--r--src/features/senitherGui/metadata.json8
-rw-r--r--src/features/settings/helpDataLoader.js50
-rw-r--r--src/features/settings/index.js332
-rw-r--r--src/features/settings/locationSettingHolder.js27
-rw-r--r--src/features/settings/metadata.json8
-rw-r--r--src/features/settings/settingThings/FakeRequireToggle.js27
-rw-r--r--src/features/settings/settingThings/button.js18
-rw-r--r--src/features/settings/settingThings/dropdownSetting.js41
-rw-r--r--src/features/settings/settingThings/imageLocation.js321
-rw-r--r--src/features/settings/settingThings/location.js340
-rw-r--r--src/features/settings/settingThings/settingBase.js162
-rw-r--r--src/features/settings/settingThings/textSetting.js41
-rw-r--r--src/features/settings/settingThings/toggle.js62
-rw-r--r--src/features/settings/settingsCommunicator.js35
-rw-r--r--src/features/slayers/index.js1022
-rw-r--r--src/features/slayers/metadata.json8
-rw-r--r--src/features/soopyGui/GuiPage.js68
-rw-r--r--src/features/soopyGui/categoryManager.js37
-rw-r--r--src/features/soopyGui/index.js215
-rw-r--r--src/features/soopyGui/metadata.json8
-rw-r--r--src/features/spamHider/index.js351
-rw-r--r--src/features/spamHider/metadata.json8
-rw-r--r--src/features/specialMining/index.js461
-rw-r--r--src/features/specialMining/metadata.json8
-rw-r--r--src/features/statHistoryGui/index.js189
-rw-r--r--src/features/statHistoryGui/metadata.json8
-rw-r--r--src/features/stat_next_to_name/index.js193
-rw-r--r--src/features/stat_next_to_name/metadata.json8
-rw-r--r--src/features/streamsGUI/index.js181
-rw-r--r--src/features/streamsGUI/metadata.json8
-rw-r--r--src/features/suggestionsGui/index.js217
-rw-r--r--src/features/suggestionsGui/metadata.json8
-rw-r--r--src/features/waypoints/index.js323
-rw-r--r--src/features/waypoints/metadata.json8
-rw-r--r--src/features/waypoints/minewaypoints_socket.js49
-rw-r--r--src/features/waypoints/pathfind.js96
101 files changed, 20003 insertions, 0 deletions
diff --git a/src/features/bestiary/index.js b/src/features/bestiary/index.js
new file mode 100644
index 0000000..da09463
--- /dev/null
+++ b/src/features/bestiary/index.js
@@ -0,0 +1,250 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import { numberWithCommas } from "../../utils/numberUtils";
+import HudTextElement from "../hud/HudTextElement";
+import DropdownSetting from "../settings/settingThings/dropdownSetting";
+import LocationSetting from "../settings/settingThings/location";
+import SettingBase from "../settings/settingThings/settingBase";
+import ToggleSetting from "../settings/settingThings/toggle";
+
+let dontUseApi = new Set()
+dontUseApi.add("arachne")
+dontUseApi.add("barbarian_duke_x")
+dontUseApi.add("bladesoul")
+dontUseApi.add("mage_outlaw")
+dontUseApi.add("ashfang")
+dontUseApi.add("magma_cube_boss")
+dontUseApi.add("headless_horseman")
+dontUseApi.add("dragon")
+
+
+class Bestiary extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.bestiaryData = JSON.parse(FileLib.read("soopyAddonsData", "bestiaryData.json") || "{}")
+ this.bestiaryChanged = false
+
+ this.bestiaryApiTracking = {}
+
+ this.registerStep(true, 5, this.scanInv)
+ this.registerStep(false, 5, this.saveData)
+
+
+ this.bestiaryStatTypes = {
+ "barbarian_duke_x": "Barbarian Duke X"
+ }
+ Object.keys(this.bestiaryData).forEach(k => {
+ if (this.bestiaryData[k].guiName) this.bestiaryStatTypes[k] = this.bestiaryData[k].guiName
+ })
+
+ new SettingBase("NOTE: u need to open ur bestiary menu", "before this will work", true, "info_bestiary", this)
+ this.hudStat = []
+ for (let i = 0; i < 10; i++) {
+ this.hudStat[i] = {}
+ this.hudStat[i].enabled = new ToggleSetting("Bestiary Slot #" + (i + 1), "Allows you to render a custom besiary kills on your hud", false, "bestiary_stat_" + i, this)
+ this.hudStat[i].type = new DropdownSetting("Bestiary Slot #" + (i + 1) + " Type", "The bestiary type", "barbarian_duke_x", "bestiary_stat_" + i + "_type", this, this.bestiaryStatTypes)
+ this.hudStat[i].location = new LocationSetting("Bestiary Slot #" + (i + 1) + " Location", "Allows you to edit the location of the bestiary stat", "bestiary_stat_" + i + "_location", this, [10, 50 + i * 10, 1, 1]).editTempText("&6Bestiary Stat Stat&7> &f12,345")
+ this.hudStat[i].textElement = new HudTextElement().setToggleSetting(this.hudStat[i].enabled).setLocationSetting(this.hudStat[i].location).setText("&6Bestiary Stat&7> &fLoading...")
+ this.hudStat[i].onlySb = new ToggleSetting("Bestiary Slot #" + (i + 1) + " Only SB", "Only render this stat when you are in skyblock", true, "bestiary_stat_" + i + "_only_sb", this).requires(this.hudStat[i].enabled)
+
+ this.hudStat[i].location.requires(this.hudStat[i].enabled)
+ this.hudStat[i].type.requires(this.hudStat[i].enabled)
+ if (this.hudStat[i - 1]) {
+ this.hudStat[i].enabled.requires(this.hudStat[i - 1].enabled)
+ }
+ }
+
+ this.registerStep(false, 5, this.updateHudElements)
+ let lastThing = undefined
+ let lastThingTime = 0
+ this.registerChat("${chat}", (chat) => {
+ let thing
+ switch (chat.trim()) {
+ case "MAGE OUTLAW DOWN!":
+ thing = "mage_outlaw"
+ break;
+ case "BARBARIAN DUKE X DOWN!":
+ thing = "barbarian_duke_x"
+ break;
+ case "BLADESOUL DOWN!":
+ thing = "bladesoul"
+ break;
+ case "ASHFANG DOWN!":
+ thing = "ashfang"
+ break;
+ // case "MAGMA BOSS DOWN!":
+ // thing = "magma_cube_boss"
+ // break;
+ case "ARACHNE DOWN!":
+ lastThing = "arachne"
+ lastThingTime = Date.now()
+ break;
+ case "HORSEMAN DOWN!":
+ lastThing = "arachne"
+ lastThingTime = Date.now()
+ break;
+ default:
+ break;
+ }
+ if (chat.includes("DRAGON")) {
+ lastThing = "dragon"
+ lastThingTime = Date.now()
+ }
+
+ if (chat.trim().startsWith("Your Damage: ")) {
+ let dmg = parseInt(chat.trim().split("Your Damage: ")[1].split(" ")[0])
+ if (dmg > 0 && Date.now() - lastThingTime < 1000) {
+ thing = lastThing
+ lastThing = ""
+ lastThingTime = 0
+ }
+ }
+
+ if (thing) {
+ this.bestiaryData[thing].guessCount++
+ this.bestiaryChanged = true
+ }
+ })
+ this.registerSoopy("apiLoad", this.apiLoad);
+
+ if (this.FeatureManager.features["dataLoader"] && this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock_raw) {
+ this.apiLoad(this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock_raw, "skyblock", false, true);
+ }
+ }
+
+ getBestiaryCount(id) {
+ if (!this.bestiaryData[id]) return "???"
+ let count = this.bestiaryData[id].count
+
+ if (!dontUseApi.has(id)) {
+ let currApiData = this.bestiaryApiTracking[id]
+
+ count += currApiData - this.bestiaryData[id].apiCount
+ }
+
+ count += this.bestiaryData[id].guessCount
+
+ return count
+ }
+
+ updateHudElements() {
+ if (!this.FeatureManager.features["dataLoader"]) return
+
+ let insb = this.FeatureManager.features["dataLoader"].class.isInSkyblock
+
+ this.hudStat.forEach(stat => {
+ if (stat.enabled.getValue()) {
+ if (!insb && stat.onlySb.getValue()) {
+ stat.textElement.setText("")
+ return
+ }
+
+ let type = stat.type.getValue()
+
+ stat.textElement.setText(`&6${this.bestiaryData[type]?.guiName}&7> &f${numberWithCommas(this.getBestiaryCount(type))}`)
+ } else {
+ stat.textElement.setText("")
+ }
+ })
+ }
+
+ apiLoad(data, dataType, isSoopyServer, isLatest) {
+ if (isSoopyServer || !isLatest) return;
+ if (dataType !== "skyblock") return;
+
+ let currentProfile = {}
+ let currentProfileTime = 0
+
+ data.profiles.forEach(p => {
+ if (p.members[Player.getUUID().toString().replace(/-/g, "")].last_save > currentProfileTime) {
+ currentProfileTime = p.members[Player.getUUID().toString().replace(/-/g, "")].last_save
+ currentProfile = p.members[Player.getUUID().toString().replace(/-/g, "")]
+ }
+ })
+
+ Object.keys(currentProfile.stats).forEach(key => {
+ if (key.startsWith("kills_")) {
+ this.bestiaryApiTracking[key.replace("kills_", "")] = currentProfile.stats[key]
+ }
+ })
+ }
+
+ scanInv() {
+ if (!Player.getContainer()) return
+ if (!Player.getContainer().getName().startsWith("Bestiary ➜ ")) return
+ let tempChanged = false
+ let seen = new Set()
+
+ for (let item of Player.getContainer().getItems()) {
+ if (!item) continue
+ let name = ChatLib.removeFormatting(item.getName()).split(" ")
+ name.pop()
+ let apiName = name.join("_").toLowerCase()
+ if (seen.has(apiName)) continue
+ seen.add(apiName)
+
+ if (apiName === "skeletor_prime") continue
+
+ if (this.bestiaryApiTracking[apiName] || dontUseApi.has(apiName)) {
+
+ let count = 0
+
+ for (let l of item.getLore()) {
+ l = ChatLib.removeFormatting(l)
+
+ if (l.startsWith("Kills: ")) {
+ count = parseInt(l.split("Kills: ")[1].replace(/,/g, ""))
+ break;
+ }
+ }
+
+ let needsChange = !this.bestiaryData[apiName] || this.bestiaryData[apiName].guiName !== name.join(" ") || this.bestiaryData[apiName].count !== count || this.bestiaryData[apiName].apiCount !== (this.bestiaryApiTracking[apiName] || 0) || this.bestiaryData[apiName].guessCount !== 0
+ if (needsChange) {
+ this.bestiaryData[apiName] = {
+ guiName: name.join(" "),
+ count,
+ apiCount: this.bestiaryApiTracking[apiName] || 0,
+ guessCount: 0
+ }
+ this.bestiaryChanged = true
+
+ tempChanged = true
+
+ }
+ }
+ }
+
+ if (tempChanged) {
+ this.bestiaryStatTypes = {}
+ Object.keys(this.bestiaryData).forEach(k => {
+ if (this.bestiaryData[k]?.guiName) this.bestiaryStatTypes[k] = this.bestiaryData[k].guiName
+ })
+
+ this.hudStat.forEach(s => {
+ s.type.dropdownObject.setOptions(this.bestiaryStatTypes)
+ })
+
+ this.updateHudElements()
+ }
+ start = Date.now()
+ }
+
+ saveData() {
+ if (this.bestiaryChanged) {
+ FileLib.write("soopyAddonsData", "bestiaryData.json", JSON.stringify(this.bestiaryData))
+ }
+ }
+
+ onDisable() {
+ this.hudStat.forEach(h => h.textElement.delete())
+ this.saveData()
+ }
+
+}
+module.exports = {
+ class: new Bestiary()
+}
diff --git a/src/features/bestiary/metadata.json b/src/features/bestiary/metadata.json
new file mode 100644
index 0000000..3ff9383
--- /dev/null
+++ b/src/features/bestiary/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Bestiary",
+ "description": "Bestiary trackers",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/betterGuis/dungeonReadyGui.js b/src/features/betterGuis/dungeonReadyGui.js
new file mode 100644
index 0000000..ad9a5dd
--- /dev/null
+++ b/src/features/betterGuis/dungeonReadyGui.js
@@ -0,0 +1,284 @@
+import { SoopyGui, SoopyRenderEvent } from "../../../guimanager"
+import SoopyKeyPressEvent from "../../../guimanager/EventListener/SoopyKeyPressEvent"
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent"
+import BoxWithTextAndDescription from "../../../guimanager/GuiElement/BoxWithTextAndDescription"
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow"
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement"
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement"
+
+
+const ContainerChest = Java.type("net.minecraft.inventory.ContainerChest");
+
+class DungeonReadyGui {
+ constructor() {
+ this.checkMenu = false;
+
+ this.soopyGui = new SoopyGui();
+
+ this.soopyGui.optimisedLocations = true;
+
+ this.mainPage = new SoopyGuiElement().setLocation(0, 0, 1, 1);
+ this.soopyGui.element.addChild(this.mainPage);
+ this.soopyGui.element.addEvent(new SoopyKeyPressEvent().setHandler((...args) =>
+ this.keyPress(...args)
+ ));
+
+
+ this.startButton = new ButtonWithArrow()
+ .setText("§0Start Dungeon")
+ .setLocation(0.25, 0.25, 0.5, 0.5)
+ .addEvent(new SoopyMouseClickEvent().setHandler(() =>
+ this.startDungeon()
+ ));
+ this.soopyGui.element.addChild(this.startButton);
+
+ this.startButton.desc = new SoopyTextElement()
+ .setMaxTextScale(2)
+ .setLocation(0.05, 0.6, 0.7, 0.4)
+ .setText("");
+ this.startButton.addChild(this.startButton.desc);
+
+ this.closeMenu = 0;
+ this.nameToId = {};
+ this.nextId = 0;
+ this.currPlayers = 1;
+ this.readyBoxes = [];
+ this.confirmationCooldown = 0;
+ for (let i = 0; i < 4; i++) {
+ let readyBox = new BoxWithTextAndDescription()
+ .setText("§0")
+ .setDesc("§0")
+ .setLocation(0.1 + 0.2 * i, 0.75, 0.15, 0.15)
+ .setColor(255, 150, 150);
+ this.readyBoxes.push(readyBox);
+
+
+ readyBox.text.setLocation(0, 0, 1, 0.6);
+ readyBox.description.setLocation(0.05, 0.6, 0.9, 0.4);
+ readyBox.visable = false;
+
+
+ this.mainPage.addChild(readyBox);
+ }
+
+ this.classBoxes = [];
+ this.currentPlayerClass = -1;
+ this.classes = {
+ "Healer": new Item("minecraft:potion"),
+ "Mage": new Item("minecraft:blaze_rod"),
+ "Berserker": new Item("minecraft:iron_sword"),
+ "Archer": new Item("minecraft:bow"),
+ "Tank": new Item("minecraft:leather_chestplate")
+ };
+ Object.keys(this.classes).forEach((clas, i) => {
+ let classBox = new BoxWithTextAndDescription()
+ .setText("§0" + clas + "&7 - 0")
+ .setDesc("§0")
+ .setLocation(0.1 + 0.1625 * i, 0.1, 0.15, 0.1);
+ this.classBoxes.push(classBox);
+
+ let classIndex = i;
+
+ classBox.text.setLocation(0, 0, 1, 0.6);
+ classBox.description.setLocation(0.05, 0.6, 0.9, 0.4);
+ classBox.addEvent(new SoopyRenderEvent()
+ .setHandler(() => {
+ let scale = 16 / Math.min(classBox.location.getWidthExact(), classBox.location.getHeightExact() / 2) * 3;
+ this.classes[clas].draw(classBox.location.getXExact() + classBox.location.getWidthExact() / 2 - 16 * scale / 2, classBox.location.getYExact() + classBox.location.getHeightExact() - 16 * scale - 4, scale);
+ })).addEvent(new SoopyMouseClickEvent().setHandler(() =>
+ this.clickedClass(classIndex)
+ ));
+
+ this.mainPage.addChild(classBox)
+ })
+
+ this.playerReadyButton = new ButtonWithArrow()
+ .setText("§0Ready")
+ .setLocation(0.33, 0.33, 0.33, 0.33)
+ .setColor(255, 150, 150)
+ .addEvent(new SoopyMouseClickEvent()
+ .setHandler(() => this.ready()));
+ this.mainPage.addChild(this.playerReadyButton);
+ }
+
+ joinedDungeon(players) {
+ this.currPlayers = players;
+ }
+
+ startDungeon() {
+ if (Player.getContainer().getName() !== "Start Dungeon?") return;
+
+ if (!this.confirmationCooldown && World.getAllPlayers().filter(p => p.getPing() === 1).length !== this.currPlayers && !(World.getAllPlayers().filter(p => p.getPing() === 1).length === 1 && this.currPlayers === 2)) {
+ this.startButton.setText("§0Confirm starting Dungeon? (3s)");
+ this.startButton.desc.setText("§0(" + World.getAllPlayers().filter(p => p.getPing() === 1).length + "/" + this.currPlayers + " in dungeon)");
+ this.confirmationCooldown = Date.now() + 3000;
+ return;
+ }
+ if (Date.now() < this.confirmationCooldown) return;
+
+ this.startButton.visable = false;
+ Player.getContainer().click(13, false, "MIDDLE");
+ }
+
+ ready() {
+ if (!Player.getContainer().getName().startsWith("Catacombs - Floor ")) return;
+ this.playerReadyButton.setColor(150, 150, 150);
+ for (let i = 0; i < 5; i++) {
+ if (ChatLib.removeFormatting(Player.getContainer().getStackInSlot(3 + i).getName().split(" ").pop()) === Player.getName())
+ Player.getContainer().drop(12 + i, false);
+ }
+ }
+
+ clickedClass(classIndex) {
+ if (!Player.getContainer().getName().startsWith("Catacombs - Floor ")) return;
+
+ Player.getContainer().drop(2 + 4 * 9 + classIndex, false);
+
+ this.classBoxes[classIndex].setColor(150, 150, 150);
+
+ if (this.currentPlayerClass !== -1) this.classBoxes[this.currentPlayerClass].setColor(253, 255, 227);
+ }
+
+ reset() {
+ this.startButton.visable = true;
+ this.playerReadyButton.setColor(255, 150, 150);
+
+ this.nameToId = {};
+ this.nextId = 0;
+ this.closeMenu = 0;
+
+ this.startButton.setText("§0Start Dungeon");
+ this.startButton.desc.setText("");
+ this.confirmationCooldown = 0;
+
+ this.readyBoxes.forEach(b => b.visable = false);
+ }
+
+ readyInOneSecond() {
+ this.closeMenu = Date.now() + 1000;
+ }
+
+ tick() {
+ if (!this.soopyGui.ctGui.isOpen()) return;
+
+ if (this.closeMenu > 0 && Date.now() > this.closeMenu) {
+ this.soopyGui.close();
+ Client.currentGui.close();
+ this.closeMenu = 0;
+ return;
+ }
+
+ if (Player.getContainer().getName().startsWith("Catacombs - Floor ")) {
+ this.startButton.visable = false;
+
+ let clickingClassButton = -1;
+
+ for (let i = 0; i < 5; i++) {
+ //ready up buttons
+ if (Player.getContainer().getStackInSlot(3 + i)) {
+ if (ChatLib.removeFormatting(Player.getContainer().getStackInSlot(3 + i).getName().split(" ").pop()) === Player.getName()) {
+ if (Player.getContainer().getStackInSlot(12 + i)) {
+ if (ChatLib.removeFormatting(Player.getContainer().getStackInSlot(12 + i).getName()) === "Ready") {
+ this.playerReadyButton.setColor(150, 255, 150);
+ } else {
+ this.playerReadyButton.setColor(255, 150, 150);
+ }
+ } else {
+ this.playerReadyButton.setColor(150, 150, 150);
+ }
+ } else {
+ let boxId = this.nameToId[ChatLib.removeFormatting(Player.getContainer().getStackInSlot(3 + i).getName().split(" ").pop())];
+
+ if (boxId) {
+ this.readyBoxes[boxId].setColor(255, 150, 150);
+ if (ChatLib.removeFormatting(Player.getContainer().getStackInSlot(12 + i).getName()) === "Ready")
+ this.readyBoxes[boxId].setColor(150, 255, 150);
+
+ this.readyBoxes[boxId].setLore(Player.getContainer().getStackInSlot(3 + i).getLore());
+ this.readyBoxes[boxId].setDesc("§0" + ChatLib.removeFormatting(Player.getContainer().getStackInSlot(3 + i).getLore()[2]));
+ }
+ }
+ }
+
+
+ //select class buttons
+ if (Player.getContainer().getStackInSlot(2 + 4 * 9 + i)) {
+ this.classBoxes[i].setColor(253, 255, 227);
+ if (Player.getContainer().getStackInSlot(2 + 4 * 9 + i).getDamage() === 10)
+ this.classBoxes[i].setColor(250, 255, 150);
+
+ this.classBoxes[i]
+ .setText("§0" + Object.keys(this.classes)[i] + "§7 - " + ChatLib.removeFormatting(Player.getContainer().getStackInSlot(2 + 4 * 9 + i).getName().split(" ")[0]))
+ .setLore(Player.getContainer().getStackInSlot(2 + 4 * 9 + i).getLore());
+
+ let isPlayerClass = false;
+ Player.getContainer().getStackInSlot(2 + 4 * 9 + i).getLore().forEach(line => {
+ if (!ChatLib.removeFormatting(line).startsWith(" - ")) return;
+ if (ChatLib.removeFormatting(line.split(" ").pop()) === Player.getName())
+ isPlayerClass = true;
+ })
+
+ if (isPlayerClass) {
+ this.currentPlayerClass = i;
+ this.classBoxes[i].setColor(150, 255, 150);
+ }
+ } else clickingClassButton = i;
+ }
+
+ if (clickingClassButton !== -1) {
+ this.classBoxes[clickingClassButton].setColor(150, 150, 150);
+ if (this.currentPlayerClass !== -1) this.classBoxes[this.currentPlayerClass].setColor(253, 255, 227);
+ }
+ };
+
+ if (this.confirmationCooldown) {
+ this.startButton.setText("§0Confirm starting Dungeon? (" + Math.ceil(Math.max(0, this.confirmationCooldown - Date.now()) / 1000) + "s)");
+ };
+
+ World.getAllPlayers().filter(p => p.getPing() === 1).forEach(p => {
+ if (p.getUUID().toString() === Player.getUUID().toString()) return;
+
+ if (p.getName() in this.nameToId) return;
+
+ this.nameToId[p.getName()] = this.nextId++;
+
+ this.readyBoxes[this.nameToId[p.getName()]].setText("§0" + p.getName()).visable = true;
+ });
+ }
+
+ guiOpened(event) {
+ let name = "";
+ if (event.gui && event.gui.field_147002_h instanceof ContainerChest)
+ name = event.gui.field_147002_h.func_85151_d().func_145748_c_().func_150260_c();
+
+ if (this.soopyGui.ctGui.isOpen()) {
+ if (!event.gui || !event.gui.field_147002_h) return;
+ Player.getPlayer().field_71070_bA = event.gui.field_147002_h
+
+ if (!Player.getContainer().getName().startsWith("Catacombs - Floor ")) return;
+
+ event.gui = this.soopyGui.ctGui
+ this.soopyGui.ctGui.open();
+ return
+ }
+
+ if (name === "Start Dungeon?" || name.startsWith("Catacombs - Floor ")) {
+ if (event.gui && event.gui.field_147002_h) Player.getPlayer().field_71070_bA = event.gui.field_147002_h;
+
+ this.soopyGui.open();
+ event.gui = this.soopyGui.ctGui;
+ }
+ }
+
+ keyPress(key, keyId) {
+ if (keyId === 1) //escape key
+ // this.dontOpen = 1
+ Client.currentGui.close();
+
+ if (keyId === 18) //'e' key
+ Client.currentGui.close();
+
+ }
+}
+
+export default DungeonReadyGui; \ No newline at end of file
diff --git a/src/features/betterGuis/index.js b/src/features/betterGuis/index.js
new file mode 100644
index 0000000..dcd8fb9
--- /dev/null
+++ b/src/features/betterGuis/index.js
@@ -0,0 +1,386 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import logger from "../../logger";
+import { f, m } from "../../../mappings/mappings";
+import ToggleSetting from "../settings/settingThings/toggle";
+import MuseumGui from "./museumGui.js";
+import DungeonReadyGui from "./dungeonReadyGui";
+import { SoopyGui } from "../../../guimanager";
+import TextBox from "../../../guimanager/GuiElement/TextBox";
+import SoopyNumber from "../../../guimanager/Classes/SoopyNumber";
+
+class BetterGuis extends Feature {
+ constructor() {
+ super();
+ }
+
+ inSkyblock() {
+ return this.FeatureManager.features["dataLoader"] && this.FeatureManager.features["dataLoader"].class.isInSkyblock;
+ }
+
+ onEnable() {
+ this.initVariables();
+
+ this.museumGui = new MuseumGui();
+ this.dungeonReady = new DungeonReadyGui();
+
+ this.replaceSbMenuClicks = new ToggleSetting("Improve Clicks on SBMENU", "This will change clicks to middle clicks, AND use commands where possible (eg /pets)", true, "sbmenu_clicks", this);
+ this.reliableSbMenuClicks = { getValue: () => false }//removed because hypixel fixed, code kept incase hypixel adds back bug later //new ToggleSetting("Make SBMENU clicks reliable", "This will delay clicks on sbmenu to time them so they dont get canceled", true, "sbmenu_time", this)
+
+ this.museumGuiEnabled = new ToggleSetting("Custom Museum GUI", "Custom gui for the Museum", true, "custom_museum_enabled", this);
+ this.dungeonReadyGuiEnabled = new ToggleSetting("Custom Dungeon Ready GUI", "Custom gui for the dungeon ready up menu", false, "custom_dungeon_ready_enabled", this);
+
+ this.chestSearchBar = new ToggleSetting("Inventory Search Bar", "use '&' to make it filter by stuff that contains multiple things", false, "inv_search", this);
+ this.customBars = new ToggleSetting("Custom hp and mana bar", "Also hides stuff like armor bar", false, "custom_bars", this);
+
+ this.lastWindowId = 0;
+ this.shouldHold = 10;
+ this.clickSlot = -1;
+ this.clickSlotTime = 0;
+
+ this.middleClickGuis = [
+ "Your SkyBlock Profile",
+ "Your Skills",
+ "Farming Skill",
+ "Mining Skill",
+ "Heart of the Mountain",
+ "Combat Skill",
+ "Foraging Skill",
+ "Fishing Skill",
+ "Enchanting Skill",
+ "Alchemy Skill",
+ "Carpentry Skill",
+ "Runecrafting Skill",
+ "Social Skill",
+ "Taming Skill",
+ "Dungeoneering",
+ "Your Essence",
+ "Healer Class Perks",
+ "Mage Class Perks",
+ "Beserk Class Perks",
+ "Archer Class Perks",
+ "Tank Class Perks",
+ "Recipe Book",
+ "Trades",
+ "Quest Log",
+ "Quest Log (Completed)",
+ "Fairt Souls Guide",
+ "Dungeon Journals",
+ "Calendar and Events",
+ "Booster Cookie",
+ "Island Management",
+ "Toggle Potion Effects",
+ "Bank",
+ "Bank Account Upgrades",
+ "Co-op Bank Account",
+ "Bank Deposit",
+ "Bank Withdrawal",
+ "Personal Bank Account",
+ "Bazaar Orders",
+ "Co-op Bazaar Orders",
+ "Pets"
+ ]
+ this.middleClickStartsWith = [
+ "Bestiary",
+ "Private Island",
+ "Hub",
+ "Spiders Den",
+ "Blazing Fortress",
+ "The End",
+ "Deep Caverns",
+ "The Park",
+ "Spooky Festival",
+ "Catacombs",
+ "The Catacombs",
+ "Settings",
+ "Bazaar",
+ "Farming",
+ "Mining",
+ "Woods & Fishes",
+ "Oddities"
+ ];
+ this.middleClickEndsWith = [
+ "Recipe",
+ "Recipes",
+ ") Pets",
+ "Collection",
+ "Active Effects"
+ ];
+
+ this.registerChat("&r&aDungeon starts in 1 second.&r", () =>
+ this.dungeonReady.readyInOneSecond.call(this.dungeonReady));
+ this.registerChat("&r&aDungeon starts in 1 second. Get ready!&r", () =>
+ this.dungeonReady.readyInOneSecond.call(this.dungeonReady));
+ this.registerEvent("guiMouseClick", this.guiClicked);
+ this.registerEvent("guiOpened", (event) => {
+ if (this.museumGuiEnabled.getValue()) this.museumGui.guiOpened.call(this.museumGui, event);
+ if (this.dungeonReadyGuiEnabled.getValue()) this.dungeonReady.guiOpened.call(this.dungeonReady, event);
+ });
+ this.registerEvent("worldLoad", () => this.dungeonReady.reset());
+ this.registerChat("&e${*} &r&cThe Catacombs &r&ewith &r&9${players}/5 players &r&eon &r${*}&r", (players) => {
+ if (this.dungeonReadyGuiEnabled.getValue()) this.dungeonReady.joinedDungeon.call(this.dungeonReady, ~~players);
+ })
+ this.registerChat("&eSkyBlock Dungeon Warp &r&7(${players} players)&r", (players) => {
+ if (this.dungeonReadyGuiEnabled.getValue()) this.dungeonReady.joinedDungeon.call(this.dungeonReady, ~~players)
+ })
+ this.registerStep(true, 10, this.step)
+ this.registerEvent("worldUnload", () => this.museumGui.saveMuseumCache.call(this.museumGui));
+ this.registerStep(false, 30, () => this.museumGui.saveMuseumCache.call(this.museumGui));
+
+
+ this.invSearchSoopyGui = new SoopyGui();
+ this.invSearchSoopyGui._renderBackground = () => { };
+
+ this.invSearchTextBox = new TextBox().setPlaceholder("Click to search").setLocation(0.4, 0.05, 0.2, 0.05);
+ this.invSearchSoopyGui.element.addChild(this.invSearchTextBox);
+
+ this.mana = new SoopyNumber(0);
+ this.overflowMana = new SoopyNumber(0);
+ this.maxMana = new SoopyNumber(0);
+ this.lastOverFlow = Date.now();
+
+ this.slotMatches = new Map();
+ this.registerEvent("renderHealth", this.renderHealth).registeredWhen(() => this.inSkyblock() && this.customBars.getValue());
+ this.registerEvent("renderFood", cancel).registeredWhen(() => this.inSkyblock() && this.customBars.getValue());
+ this.registerEvent("renderArmor", this.renderMana).registeredWhen(() => this.inSkyblock() && this.customBars.getValue());
+ let registerActionBar = this.registerCustom("actionbar", this.actionbarMana);
+ registerActionBar.trigger.setCriteria('&b${curr}/${max}✎').setParameter('contains');
+ let registerActionBar2 = this.registerCustom("actionbar", this.actionbarOverflowMana);
+ registerActionBar2.trigger.setCriteria('&3${curr}ʬ').setParameter('contains');
+ //&c2532/2532❤ &a798&a❈ Defense &b2525/2525✎ &31ʬ&r (100)
+ //&c2532/2532❤ &f20&f❂ True Defense &b2414/2414✎ &3600ʬ&r (13)
+ //&c2665/2665❤ &a972&a❈ Defense &b2145/2145✎ &3600ʬ&r
+ //&c2,806/2,806❤ &a1,050&a❈ Defense &b1,945/1,945✎ &3600ʬ&r
+ this.registerEvent("guiRender", this.postGuiRender).registeredWhen(() => this.chestSearchBar.getValue());
+ this.registerEvent("guiMouseClick", this.guiMouseClick).registeredWhen(() => this.chestSearchBar.getValue());
+ this.registerEvent("guiKey", this.guiKey).registeredWhen(() => this.chestSearchBar.getValue());
+ this.registerEvent("renderSlot", this.renderSlot).registeredWhen(() => this.chestSearchBar.getValue());
+ this.registerEvent("guiOpened", this.guiOpened).registeredWhen(() => this.chestSearchBar.getValue());
+ }
+
+ actionbarMana(curr, max) {
+ if (curr.includes("Mana"))
+ curr = curr.split("&b").pop();
+
+ this.mana.set(parseInt(curr.replace(/,/g, "")), 500);
+ this.maxMana.set(parseInt(max.replace(/,/g, "")), 500);
+ if (Date.now() - this.lastOverFlow > 1000) this.overflowMana.set(0, 500);
+ }
+
+ actionbarOverflowMana(curr) {
+ this.overflowMana.set(parseInt(curr.replace(/,/g, "")), 500);
+ this.lastOverFlow = Date.now();
+ }
+
+ renderMana(event) {
+ cancel(event);
+
+ let left = Renderer.screen.getWidth() / 2 + 91 - 80;
+ let top = Renderer.screen.getHeight() - 40;
+
+ Renderer.retainTransforms(true);
+ Renderer.translate(left, top);
+
+ let totalAmt = Math.max(this.maxMana.get(), this.mana.get() + this.overflowMana.get());
+
+ let manaPercent = this.mana.get() / totalAmt;
+ let ofPercent = this.overflowMana.get() / totalAmt;
+
+ Renderer.drawRect(Renderer.color(0, 0, 0), 0, 0, 80, 10);
+ Renderer.drawRect(Renderer.color(50, 50, 50), 2, 2, 76, 6);
+ Renderer.drawRect(Renderer.color(0, 0, 255), 2, 2, manaPercent * 76, 6);
+ Renderer.drawRect(Renderer.color(0, 255, 255), 2 + manaPercent * 76, 2, ofPercent * 76, 6);
+ Renderer.retainTransforms(false);
+ }
+
+ renderHealth(event) {
+ cancel(event);
+
+ let left = Renderer.screen.getWidth() / 2 - 91;
+ let top = Renderer.screen.getHeight() - 40;
+
+ Renderer.retainTransforms(true);
+ Renderer.translate(left, top);
+
+ let totalAmt = Math.max(Player.asPlayerMP().getMaxHP(), Player.getHP() + Player.asPlayerMP().getAbsorption());
+
+ let hpPercent = Player.getHP() / totalAmt;
+ let abPercent = Player.asPlayerMP().getAbsorption() / totalAmt;
+
+
+ Renderer.drawRect(Renderer.color(0, 0, 0), 0, 0, 80, 10);
+ Renderer.drawRect(Renderer.color(50, 50, 50), 2, 2, 76, 6);
+ Renderer.drawRect(Renderer.color(255, 0, 0), 2, 2, hpPercent * 76, 6);
+ Renderer.drawRect(Renderer.color(255, 255, 0), 2 + hpPercent * 76, 2, abPercent * 76, 6);
+
+ Renderer.retainTransforms(false);
+ }
+
+ postGuiRender(x, y, gui) {
+ if (gui instanceof net.minecraft.client.gui.inventory.GuiChest)
+ this.invSearchSoopyGui._render(x, y, 0);
+ }
+ guiMouseClick(x, y, button, gui) {
+ if (gui instanceof net.minecraft.client.gui.inventory.GuiChest)
+ this.invSearchSoopyGui._onClick(x, y, button);
+ }
+ guiKey(char, code, gui, event) {
+ if (!(gui instanceof net.minecraft.client.gui.inventory.GuiChest)) return;
+
+ this.invSearchSoopyGui._onKeyPress(char, code);
+
+ if (this.invSearchTextBox.text.selected) {
+ cancel(event);
+ this.slotMatches.clear();
+ }
+ }
+ guiOpened() {
+ this.slotMatches.clear();
+ }
+
+ renderSlot(slot, gui, event) {
+ if (!(gui instanceof net.minecraft.client.gui.inventory.GuiChest)) return;
+ if (!this.invSearchTextBox.getText()) return;
+
+ let searchText = this.invSearchTextBox.getText().toLowerCase();
+
+ let isMatching = false;
+ let slotMatches = this.slotMatches.get(slot.getIndex());
+ if (slotMatches && Date.now() - slotMatches.timestamp < 500) {
+ if (!slotMatches.isMatching) {
+ Renderer.translate(0, 0, 100);
+ Renderer.drawRect(Renderer.color(0, 0, 0, 200), slot.getDisplayX(), slot.getDisplayY(), 8 * Renderer.screen.getScale(), 8 * Renderer.screen.getScale());
+ }
+ return;
+ }
+ let item = slot.getItem();
+ if (item) {
+ isMatching = !searchText.split("&").map(a => {
+ a = a.trim();
+ let isMatching2 = false;
+ if (ChatLib.removeFormatting(item.getName()).toLowerCase().includes(a)) isMatching2 = true;
+ if (!isMatching2 && item.getLore().some(b => ChatLib.removeFormatting(b).toLowerCase().includes(a))) isMatching2 = true;
+ return isMatching2;
+ }).includes(false);
+ }
+
+ this.slotMatches.set(slot.getIndex(), { isMatching, timestamp: Date.now() });
+
+ if (!isMatching) {
+ Renderer.translate(0, 0, 100);
+ Renderer.drawRect(Renderer.color(0, 0, 0, 200), slot.getDisplayX(), slot.getDisplayY(), 8 * Renderer.screen.getScale(), 8 * Renderer.screen.getScale());
+ }
+ }
+
+ guiClicked(mouseX, mouseY, button, gui, event) {
+ if (!(gui instanceof net.minecraft.client.gui.inventory.GuiChest) || button !== 0 || !this.replaceSbMenuClicks.getValue()) return;
+
+ let hoveredSlot = gui.getSlotUnderMouse();
+ if (!hoveredSlot) return;
+
+ let hoveredSlotId = hoveredSlot[f.slotNumber];
+
+ // logger.logMessage(hoveredSlotId, 4)
+
+ if (this.guiSlotClicked(ChatLib.removeFormatting(Player.getContainer().getName()), hoveredSlotId))
+ cancel(event);
+ }
+
+ step() {
+ if (this.museumGuiEnabled.getValue()) this.museumGui.tick.call(this.museumGui);
+ if (this.dungeonReadyGuiEnabled.getValue()) this.dungeonReady.tick.call(this.dungeonReady);
+
+ if (!this.replaceSbMenuClicks.getValue()) return
+ if (Player.getContainer() && Player.getContainer().getName() !== "SkyBlock Menu") {
+ this.lastWindowId = 0;
+ return;
+ }
+ if (this.lastWindowId === 0) {
+ this.lastWindowId = Player.getContainer().getWindowId();
+ return;
+ }
+ if (Player.getContainer().getWindowId() === this.lastWindowId) {
+ this.shouldHold--
+ return;
+ }
+
+ this.lastWindowId = Player.getContainer().getWindowId();
+ this.shouldHold += 10;
+ if (Date.now() - this.clickSlotTime > 1000) {
+ this.clickSlot = -1;
+ }
+ if (this.clickSlot && this.clickSlot != -1) {
+ Player.getContainer().click(this.clickSlot, false, "MIDDLE");
+ this.clickSlot = -1;
+ }
+ }
+
+ guiSlotClicked(inventoryName, slotId) {
+ if (inventoryName.endsWith(" Sack")) return false
+ switch (inventoryName) {
+ case "SkyBlock Menu":
+ switch (slotId) {
+ case 30:
+ ChatLib.command("pets")
+ break
+ case 25:
+ ChatLib.command("storage")
+ break
+ default:
+ if (this.shouldHold > 0 && this.reliableSbMenuClicks.getValue()) {
+ this.clickSlot = slotId
+ this.clickSlotTime = Date.now()
+ } else {
+ Player.getContainer().click(slotId, false, "MIDDLE")
+ }
+ break;
+ }
+ return true
+ break
+ default:
+ if (this.middleClickGuis.includes(inventoryName)) {
+ Player.getContainer().click(slotId, false, "MIDDLE")
+ return true
+ }
+ for (let thing of this.middleClickStartsWith) {
+ if (inventoryName.startsWith(thing)) {
+ Player.getContainer().click(slotId, false, "MIDDLE")
+ return true
+ }
+ }
+ for (let thing of this.middleClickEndsWith) {
+ if (inventoryName.endsWith(thing)) {
+ Player.getContainer().click(slotId, false, "MIDDLE")
+ return true
+ }
+ }
+ return false
+ break
+ }
+ }
+
+ initVariables() {
+ this.replaceSbMenuClicks = undefined;
+ this.lastWindowId = undefined;
+ this.shouldHold = undefined;
+ this.clickSlot = undefined;
+ this.clickSlotTime = undefined;
+ this.reliableSbMenuClicks = undefined;
+ this.middleClickGuis = undefined;
+ this.middleClickStartsWith = undefined;
+ this.middleClickEndsWith = undefined;
+
+ this.museumGui = undefined;
+ }
+
+ onDisable() {
+ this.initVariables();
+
+ this.invSearchSoopyGui.delete();
+ }
+}
+
+module.exports = {
+ class: new BetterGuis()
+} \ No newline at end of file
diff --git a/src/features/betterGuis/metadata.json b/src/features/betterGuis/metadata.json
new file mode 100644
index 0000000..6aeed27
--- /dev/null
+++ b/src/features/betterGuis/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Better guis",
+ "description": "Edits to a bunch of guis to make them nicer to use",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/betterGuis/museumGui.js b/src/features/betterGuis/museumGui.js
new file mode 100644
index 0000000..06497af
--- /dev/null
+++ b/src/features/betterGuis/museumGui.js
@@ -0,0 +1,1024 @@
+import { SoopyGui, SoopyRenderEvent } from "../../../guimanager"
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent"
+import SoopyHoverChangeEvent from "../../../guimanager/EventListener/SoopyHoverChangeEvent"
+import SoopyKeyPressEvent from "../../../guimanager/EventListener/SoopyKeyPressEvent"
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent"
+import SoopyOpenGuiEvent from "../../../guimanager/EventListener/SoopyOpenGuiEvent"
+import BoxWithText from "../../../guimanager/GuiElement/BoxWithText"
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow"
+import ProgressBar from "../../../guimanager/GuiElement/ProgressBar"
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement"
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement"
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement"
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement"
+import TextBox from "../../../guimanager/GuiElement/TextBox"
+import Notification from "../../../guimanager/Notification"
+import renderLibs from "../../../guimanager/renderLibs"
+import { m } from "../../../mappings/mappings"
+import * as utils from "../../utils/utils"
+
+
+const ContainerChest = Java.type("net.minecraft.inventory.ContainerChest")
+
+class MuseumGui {
+ constructor() {
+ this.checkMenu = false
+
+ this.isInMuseum = false
+ this.guiOpenTickThing = false
+ this.dontOpen = 0
+ this.lastClosed = 0
+ this.itemsInPages = JSON.parse(FileLib.read("soopyAddonsData", "museumItemsCache.json") || "{}") || {}
+ this.itemsInPagesSaved = true
+
+ this.soopyGui = new SoopyGui()
+
+ this.soopyGui.optimisedLocations = true
+
+ this.soopyGui.element.addEvent(new SoopyKeyPressEvent().setHandler((...args) => {
+ this.keyPress(...args)
+ }))
+
+ this.mainPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+ this.soopyGui.element.addChild(this.mainPage)
+
+ let widthPer = 0.2
+ let leftOffset = (1 - widthPer * 3 - widthPer * 4 / 5) / 2
+
+ this.weaponsIndicator = new SoopyBoxElement().setLocation(leftOffset, 0.05, widthPer * 4 / 5, 0.15)
+ this.weaponsIndicator.addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (this.weaponsIndicator.hovered && ChatLib.removeFormatting(Player.getContainer().getStackInSlot(4).getName()) !== "Weapons") {
+ this.weaponText.location.location.x.set(0.05, 500)
+ this.weaponText.location.size.x.set(0.9, 500)
+ this.weaponText.location.location.y.set(0.025, 500)
+ this.weaponText.location.size.y.set(0.35, 500)
+
+ this.weaponsIndicator.setColorOffset(-20, -20, -20, 100)
+
+ Renderer.translate(0, 0, 100)
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), this.weaponsIndicator.location.getXExact(), this.weaponsIndicator.location.getYExact(), this.weaponsIndicator.location.getWidthExact(), this.weaponsIndicator.location.getHeightExact())
+ let clicks = Player.getContainer().getName() === "Your Museum" ? "1" : "2"
+ Renderer.translate(0, 0, 100)
+ renderLibs.drawStringCenteredFull(clicks, this.weaponsIndicator.location.getXExact() + this.weaponsIndicator.location.getWidthExact() / 2, this.weaponsIndicator.location.getYExact() + this.weaponsIndicator.location.getHeightExact() / 2, Math.min(this.weaponsIndicator.location.getWidthExact() / Renderer.getStringWidth(clicks) / 4, this.weaponsIndicator.location.getHeightExact() / 4 / 2))
+ } else {
+ this.weaponText.location.location.x.set(0.1, 500)
+ this.weaponText.location.size.x.set(0.8, 500)
+ this.weaponText.location.location.y.set(0.05, 500)
+ this.weaponText.location.size.y.set(0.3, 500)
+
+ this.weaponsIndicator.setColorOffset(0, 0, 0, 100)
+ }
+ })).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedTopButton("Weapons")
+ }))
+
+ this.weaponText = new SoopyTextElement().setText("§5Weapons").setMaxTextScale(10).setLocation(0.1, 0.05, 0.8, 0.3)
+ this.weaponsIndicator.addChild(this.weaponText)
+ this.weaponsPercentageText = new SoopyTextElement().setLocation(0.1, 0.4, 0.8, 0.2).setText("§0Items Donated: §7Loading...").setMaxTextScale(10)
+ this.weaponsIndicator.addChild(this.weaponsPercentageText)
+ this.weaponsProgressBar = new ProgressBar().setLocation(0.1, 0.6, 0.8, 0.35).showPercentage(true)
+ this.weaponsIndicator.addChild(this.weaponsProgressBar)
+ this.mainPage.addChild(this.weaponsIndicator)
+
+ this.armourIndicator = new SoopyBoxElement().setLocation(leftOffset + widthPer, 0.05, widthPer * 4 / 5, 0.15)
+ this.armourIndicator.addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (this.armourIndicator.hovered && ChatLib.removeFormatting(Player.getContainer().getStackInSlot(4).getName()) !== "Armor Sets") {
+ this.armourText.location.location.x.set(0.05, 500)
+ this.armourText.location.size.x.set(0.9, 500)
+ this.armourText.location.location.y.set(0.025, 500)
+ this.armourText.location.size.y.set(0.35, 500)
+
+ this.armourIndicator.setColorOffset(-20, -20, -20, 100)
+
+ Renderer.translate(0, 0, 100)
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), this.armourIndicator.location.getXExact(), this.armourIndicator.location.getYExact(), this.armourIndicator.location.getWidthExact(), this.armourIndicator.location.getHeightExact())
+ let clicks = Player.getContainer().getName() === "Your Museum" ? "1" : "2"
+ Renderer.translate(0, 0, 100)
+ renderLibs.drawStringCenteredFull(clicks, this.armourIndicator.location.getXExact() + this.armourIndicator.location.getWidthExact() / 2, this.armourIndicator.location.getYExact() + this.armourIndicator.location.getHeightExact() / 2, Math.min(this.armourIndicator.location.getWidthExact() / Renderer.getStringWidth(clicks) / 4, this.armourIndicator.location.getHeightExact() / 4 / 2))
+ } else {
+ this.armourText.location.location.x.set(0.1, 500)
+ this.armourText.location.size.x.set(0.8, 500)
+ this.armourText.location.location.y.set(0.05, 500)
+ this.armourText.location.size.y.set(0.3, 500)
+
+ this.armourIndicator.setColorOffset(0, 0, 0, 100)
+ }
+ })).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedTopButton("Armor Sets")
+ }))
+
+ this.armourText = new SoopyTextElement().setText("§5Armor Sets").setMaxTextScale(10).setLocation(0.1, 0.05, 0.8, 0.3)
+ this.armourIndicator.addChild(this.armourText)
+ this.armourPercentageText = new SoopyTextElement().setLocation(0.1, 0.4, 0.8, 0.2).setText("§0Items Donated: §7Loading...").setMaxTextScale(10)
+ this.armourIndicator.addChild(this.armourPercentageText)
+ this.armourProgressBar = new ProgressBar().setLocation(0.1, 0.6, 0.8, 0.35).showPercentage(true)
+ this.armourIndicator.addChild(this.armourProgressBar)
+ this.mainPage.addChild(this.armourIndicator)
+
+ this.raritiesIndicator = new SoopyBoxElement().setLocation(leftOffset + widthPer * 2, 0.05, widthPer * 4 / 5, 0.15)
+ this.raritiesIndicator.addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (this.raritiesIndicator.hovered && ChatLib.removeFormatting(Player.getContainer().getStackInSlot(4).getName()) !== "Rarities") {
+ this.raritiesText.location.location.x.set(0.05, 500)
+ this.raritiesText.location.size.x.set(0.9, 500)
+ this.raritiesText.location.location.y.set(0.025, 500)
+ this.raritiesText.location.size.y.set(0.35, 500)
+
+ this.raritiesIndicator.setColorOffset(-20, -20, -20, 100)
+
+ Renderer.translate(0, 0, 100)
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), this.raritiesIndicator.location.getXExact(), this.raritiesIndicator.location.getYExact(), this.raritiesIndicator.location.getWidthExact(), this.raritiesIndicator.location.getHeightExact())
+ let clicks = Player.getContainer().getName() === "Your Museum" ? "1" : "2"
+ Renderer.translate(0, 0, 100)
+ renderLibs.drawStringCenteredFull(clicks, this.raritiesIndicator.location.getXExact() + this.raritiesIndicator.location.getWidthExact() / 2, this.raritiesIndicator.location.getYExact() + this.raritiesIndicator.location.getHeightExact() / 2, Math.min(this.raritiesIndicator.location.getWidthExact() / Renderer.getStringWidth(clicks) / 4, this.raritiesIndicator.location.getHeightExact() / 4 / 2))
+ } else {
+ this.raritiesText.location.location.x.set(0.1, 500)
+ this.raritiesText.location.size.x.set(0.8, 500)
+ this.raritiesText.location.location.y.set(0.05, 500)
+ this.raritiesText.location.size.y.set(0.3, 500)
+
+ this.raritiesIndicator.setColorOffset(0, 0, 0, 100)
+ }
+ })).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedTopButton("Rarities")
+ }))
+
+ this.raritiesText = new SoopyTextElement().setText("§5Rarities").setMaxTextScale(10).setLocation(0.1, 0.05, 0.8, 0.3)
+ this.raritiesIndicator.addChild(this.raritiesText)
+ this.raritiesPercentageText = new SoopyTextElement().setLocation(0.1, 0.4, 0.8, 0.2).setText("§0Items Donated: §7Loading...").setMaxTextScale(10)
+ this.raritiesIndicator.addChild(this.raritiesPercentageText)
+ this.raritiesProgressBar = new ProgressBar().setLocation(0.1, 0.6, 0.8, 0.35).showPercentage(true)
+ this.raritiesIndicator.addChild(this.raritiesProgressBar)
+ this.mainPage.addChild(this.raritiesIndicator)
+
+ this.specialIndicator = new SoopyBoxElement().setLocation(leftOffset + widthPer * 3, 0.05, widthPer * 4 / 5, 0.15)
+ this.specialIndicator.addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (this.specialIndicator.hovered && ChatLib.removeFormatting(Player.getContainer().getStackInSlot(4).getName()) !== "Special Items") {
+ this.specialText.location.location.x.set(0.05, 500)
+ this.specialText.location.size.x.set(0.9, 500)
+ this.specialText.location.location.y.set(0.025, 500)
+ this.specialText.location.size.y.set(0.35, 500)
+
+ this.specialIndicator.setColorOffset(-20, -20, -20, 100)
+
+ Renderer.translate(0, 0, 100)
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), this.specialIndicator.location.getXExact(), this.specialIndicator.location.getYExact(), this.specialIndicator.location.getWidthExact(), this.specialIndicator.location.getHeightExact())
+ let clicks = Player.getContainer().getName() === "Your Museum" ? "1" : "2"
+ Renderer.translate(0, 0, 100)
+ renderLibs.drawStringCenteredFull(clicks, this.specialIndicator.location.getXExact() + this.specialIndicator.location.getWidthExact() / 2, this.specialIndicator.location.getYExact() + this.specialIndicator.location.getHeightExact() / 2, Math.min(this.specialIndicator.location.getWidthExact() / Renderer.getStringWidth(clicks) / 4, this.specialIndicator.location.getHeightExact() / 4 / 2))
+
+ } else {
+ this.specialText.location.location.x.set(0.1, 500)
+ this.specialText.location.size.x.set(0.8, 500)
+ this.specialText.location.location.y.set(0.05, 500)
+ this.specialText.location.size.y.set(0.3, 500)
+
+ this.specialIndicator.setColorOffset(0, 0, 0, 100)
+ }
+ })).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedTopButton("Special Items")
+ }))
+
+ this.specialText = new SoopyTextElement().setText("§5Special Items").setMaxTextScale(10).setLocation(0.1, 0.05, 0.8, 0.3)
+ this.specialIndicator.addChild(this.specialText)
+ this.specialPercentageText = new SoopyTextElement().setLocation(0.1, 0.4, 0.8, 0.6).setText("§0Items Donated: §7Loading...").setMaxTextScale(10)
+ this.specialIndicator.addChild(this.specialPercentageText)
+ this.mainPage.addChild(this.specialIndicator)
+
+ let box = new SoopyBoxElement().setLocation(0.5 - widthPer * 0.75, 0.25, widthPer * 2 * 0.75, 0.075).setLore(["Click to search"])
+ this.pageTitle = new SoopyTextElement().setText("§5Your Museum").setMaxTextScale(10).setLocation(0, 0, 1, 1)
+ box.addChild(this.pageTitle)
+ this.mainPage.addChild(box)
+
+ this.searchText = ""
+ let search = new TextBox().setLocation(0.5 - widthPer * 0.75, 0.25, widthPer * 2 * 0.75, 0.075)
+ box.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ box.visable = false
+ search.visable = true
+ search.select()
+ }))
+ search.visable = false
+ search.text.mouseClickG = (mouseX, mouseY) => {
+ if (search.text.selected && !this.searchText) {
+ box.visable = true
+ search.visable = false
+ }
+ search.text.selected = false
+ }
+ search.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFunction) => {
+ this.searchText = newVal
+ this.showSearchItems()
+ }))
+ this.mainPage.addChild(search)
+ this.mainPage.addEvent(new SoopyOpenGuiEvent().setHandler(() => {
+ box.visable = true
+ search.visable = false
+ search.text.selected = false
+ search.setText("")
+ this.searchText = ""
+ }))
+
+ this.nextButton = new ButtonWithArrow().setLocation(0.5 + widthPer * 3 / 2 - widthPer / 2, 0.25, widthPer / 2, 0.075).setText("§0Next Page")
+ this.nextButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ if (this.nextButton.visable) this.nextPage()
+ }))
+ this.mainPage.addChild(this.nextButton)
+
+ this.previousButton = new ButtonWithArrow().setLocation(0.5 - widthPer * 3 / 2, 0.25, widthPer / 2, 0.075).setText("§0Previous Page").setDirectionRight(false)
+ this.previousButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ if (this.previousButton.visable) this.previousPage()
+ }))
+ this.mainPage.addChild(this.previousButton)
+
+ this.nextButton.visable = false
+ this.previousButton.visable = false
+
+ this.donateTitleBox = new SoopyBoxElement().setLocation(0.5 + widthPer * 3 / 2 + 0.025, 0.25, 0.5 - widthPer * 1.5 - 0.05, 0.075)
+ let donateTitle = new SoopyTextElement().setText("§5Donate").setMaxTextScale(10).setLocation(0, 0, 1, 1)
+
+ this.donateTitleBox.addChild(donateTitle)
+ this.mainPage.addChild(this.donateTitleBox)
+
+ this.donateBox = new SoopyBoxElement().setLocation(0.5 + widthPer * 3 / 2 + 0.025, 0.35, 0.5 - widthPer * 1.5 - 0.05, 0.6).setScrollable(true).enableFrameBuffer()
+ this.mainPage.addChild(this.donateBox)
+
+ this.favoriteTitleBox = new SoopyBoxElement().setLocation(0.025, 0.25, 0.5 - widthPer * 1.5 - 0.05, 0.075)
+ let favoriteTitle = new SoopyTextElement().setText("§5Favourite Items").setMaxTextScale(10).setLocation(0, 0, 1, 1)
+
+ this.favoriteTitleBox.addChild(favoriteTitle)
+ this.mainPage.addChild(this.favoriteTitleBox)
+
+ this.favoriteBox = new SoopyBoxElement().setLocation(0.025, 0.35, 0.5 - widthPer * 1.5 - 0.05, 0.6).setScrollable(true).enableFrameBuffer()
+ this.mainPage.addChild(this.favoriteBox)
+
+ this.itemsBox = new SoopyBoxElement().setLocation(0.5 - widthPer * 3 / 2, 0.35, widthPer * 3, 0.6).enableFrameBuffer()
+ this.mainPage.addChild(this.itemsBox)
+
+ new Array(this.donateBox, this.favoriteBox, this.itemsBox).forEach((box, i) => {
+ box.addEvent(new SoopyHoverChangeEvent().setHandler((hovered) => {
+ if (i === 2) {
+ if (this.searchText) {
+
+ } else {
+ box.enableFrameBuffer()
+ return
+ }
+ }
+ if (hovered) {
+ box.disableFrameBuffer()
+ } else {
+ box.enableFrameBuffer()
+ box.dirtyFrameBuffer(1000)
+ }
+ }))
+ })
+
+ this.donateItems = []
+
+ this.confirm_temp = ""
+
+ this.replacePage = {
+ "Your Museum": "Museum",
+ " Weapons": "Weapons",
+ " Armor Sets": "Armor Sets",
+ " Rarities": "Rarities",
+ " Special Items": "Special Items"
+ }
+
+ this.tickI = 0
+
+ this.lastGuiTitle = ""
+
+ this.favoriteItems = JSON.parse(FileLib.read("soopyAddonsData", "museumFavoriteData.json") || "[]") || []
+ this.favoriteIds = this.favoriteItems.map(a => a.sb_id)
+ this.updatedFavorites(false)
+ }
+
+ saveMuseumCache() {
+ //Called on worldUnload, and ever 30 seconds
+ if (this.itemsInPagesSaved) return
+ this.itemsInPagesSaved = true
+
+ new Thread(() => {
+ FileLib.write("soopyAddonsData", "museumItemsCache.json", JSON.stringify(this.itemsInPages))
+ }).start()
+ }
+
+ clickedTopButton(type) {
+ if (ChatLib.removeFormatting(Player.getContainer().getStackInSlot(4).getName()) === type) return
+
+ if (Player.getContainer().getName() === "Your Museum") {
+ //if on main page can just directly click on it
+ switch (type) {
+ case "Weapons":
+ Player.getContainer().click(19, false, "MIDDLE")
+ break
+ case "Armor Sets":
+ Player.getContainer().click(21, false, "MIDDLE")
+ break
+ case "Rarities":
+ Player.getContainer().click(23, false, "MIDDLE")
+ break
+ case "Special Items":
+ Player.getContainer().click(25, false, "MIDDLE")
+ break
+ }
+ } else {
+ Player.getContainer().click(48, false, "MIDDLE")
+ }
+ }
+
+ nextPage() {
+ let itempages = ["Weapons", "Armor Sets", "Rarities", "Special Items"]
+ if (itempages.includes(this.replacePage[Player.getContainer().getName().split("➜").pop()])) {
+ Player.getContainer().click(53, false, "MIDDLE")
+
+ let [currPage, pageNum] = Player.getContainer().getName().split(")")[0].split("(")[1].split("/").map(a => parseInt(a))
+ this.regenItems(currPage + 1)
+ }
+ }
+
+ previousPage() {
+ let itempages = ["Weapons", "Armor Sets", "Rarities", "Special Items"]
+ if (itempages.includes(this.replacePage[Player.getContainer().getName().split("➜").pop()])) {
+ Player.getContainer().click(45, false, "MIDDLE")
+
+ let [currPage, pageNum] = Player.getContainer().getName().split(")")[0].split("(")[1].split("/").map(a => parseInt(a))
+ this.regenItems(currPage - 1)
+ }
+ }
+
+ tickMenu(first = false) {
+ if (!first && (this.tickI++) % 5 !== 0) {
+ if (this.lastGuiTitle === Player.getContainer().getName()) {
+ return
+ }
+ }
+ this.lastGuiTitle = Player.getContainer().getName()
+
+ if (Player.getContainer().getName() === "Your Museum") {//main page
+ if (!Player.getContainer().getStackInSlot(19)) return
+
+ let lore = Player.getContainer().getStackInSlot(19).getLore()
+ lore.forEach((line, i) => {
+ if (i === 0) return
+
+ if (line.split(" ")?.[1]?.includes("/")) {
+ let data = ChatLib.removeFormatting(line).split(" ")[1].split("/")
+
+ this.weaponsProgressBar.setProgress(parseInt(data[0]) / parseInt(data[1]))
+ this.weaponsPercentageText.setText("§0Items Donated: §7" + data[0] + "/" + data[1])
+ }
+ })
+ this.weaponsIndicator.setLore(lore)
+
+ lore = Player.getContainer().getStackInSlot(21).getLore()
+ lore.forEach((line, i) => {
+ if (i === 0) return
+
+ if (line.split(" ")?.[1]?.includes("/")) {
+ let data = ChatLib.removeFormatting(line).split(" ")[1].split("/")
+
+ this.armourProgressBar.setProgress(parseInt(data[0]) / parseInt(data[1]))
+ this.armourPercentageText.setText("§0Items Donated: §7" + data[0] + "/" + data[1])
+ }
+ })
+ this.armourIndicator.setLore(lore)
+
+ lore = Player.getContainer().getStackInSlot(23).getLore()
+ lore.forEach((line, i) => {
+ if (i === 0) return
+
+ if (line.split(" ")?.[1]?.includes("/")) {
+ let data = ChatLib.removeFormatting(line).split(" ")[1].split("/")
+
+ this.raritiesProgressBar.setProgress(parseInt(data[0]) / parseInt(data[1]))
+ this.raritiesPercentageText.setText("§0Items Donated: §7" + data[0] + "/" + data[1])
+ }
+ })
+ this.raritiesIndicator.setLore(lore)
+
+ lore = Player.getContainer().getStackInSlot(25).getLore()
+ lore.forEach((line, i) => {
+ if (i === 0) return
+
+ if (ChatLib.removeFormatting(line).startsWith("Items Donated: ")) {
+ this.specialPercentageText.setText("§0Items Donated: §7" + ChatLib.removeFormatting(line).split(": ")[1])
+ }
+ })
+ this.specialIndicator.setLore(lore)
+
+ if ((this.pageTitle.text !== ("§5" + Player.getContainer().getName()) || first) && !this.searchText) {
+ this.itemsBox.clearChildren()
+ let rewardsButton = new ButtonWithArrow().setText("§5Rewards").setLocation(0.1, 0.05, 0.8, 0.2)
+ rewardsButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(40, false, "MIDDLE")
+ }))
+ this.itemsBox.addChild(rewardsButton)
+ let browserButton = new ButtonWithArrow().setText("§5Museum Browser").setLocation(0.1, 0.3, 0.8, 0.2)
+ browserButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(50, false, "MIDDLE")
+ }))
+ this.itemsBox.addChild(browserButton)
+
+ this.itemsBox.dirtyFrameBuffer()
+ }
+ }
+
+ this.nextButton.visable = false
+ this.previousButton.visable = false
+
+ this.donateTitleBox.visable = false
+ this.donateBox.visable = false
+
+ let itempages = ["Weapons", "Armor Sets", "Rarities", "Special Items"]
+ if (itempages.includes(this.replacePage[Player.getContainer().getName().split("➜").pop()])) {
+ let page = this.replacePage[Player.getContainer().getName().split("➜").pop()]
+ let currPage = 0
+ let pageNum = Player.getContainer().getStackInSlot(4) ? Math.ceil(ChatLib.removeFormatting(Player.getContainer().getStackInSlot(4).getLore().pop().split("/").pop()) / 28) : 0
+
+ // {
+ // let item = Player.getContainer().getStackInSlot(45)
+ // if (item) {
+ // let lore = item.getLore()
+ // let lastLine = ChatLib.removeFormatting(lore[lore.length - 1]).trim()
+ // let num = lastLine.match(/(\d+)$/g)
+ // currPage = parseInt(num) + 1
+ // }
+ // }
+ // {
+ // let item = Player.getContainer().getStackInSlot(53)
+ // if (item) {
+ // let lore = item.getLore()
+ // let lastLine = ChatLib.removeFormatting(lore[lore.length - 1]).trim()
+ // let num = lastLine.match(/(\d+)$/g)
+ // currPage = parseInt(num) - 1
+ // }
+ // }
+ console.log(currPage, pageNum)
+
+ if (!this.searchText) {
+ if (currPage > 1) {
+ this.previousButton.visable = true
+ if (Player.getContainer().getStackInSlot(45)) this.previousButton.setLore(Player.getContainer().getStackInSlot(45).getLore())
+ }
+ if (currPage < pageNum) {
+ this.nextButton.visable = true
+ if (Player.getContainer().getStackInSlot(53)) this.nextButton.setLore(Player.getContainer().getStackInSlot(53).getLore())
+ }
+ }
+
+ this.donateTitleBox.visable = true
+ this.donateBox.visable = true
+ let oldDonateItems = JSON.stringify(this.donateItems)
+ this.donateItems = []
+ let donateArmorSets = {}
+ let slot = 0
+ for (let item of Player.getContainer().getItems()) {
+ if (!item) continue
+ if (item.getID() === -1) continue
+ let lore = item.itemStack[m.getTooltip](Player.getPlayer(), false)
+ for (let line of lore) {
+ if (ChatLib.removeFormatting(line) === "Click to donate item!") {
+ let sb_id = utils.getSBID(item)
+
+ this.donateItems.push({
+ sb_id: sb_id || "NA",
+ name: item.getName() || "",
+ lore: item.getLore() || [],
+ slot: slot
+ })
+ }
+ if (ChatLib.removeFormatting(line) === "Click to donate armor set!") {
+ let sb_id = utils.getSBID(item)
+
+ let setId = sb_id.split("_")
+ setId.pop()
+ setId = setId.join("_")
+
+ donateArmorSets[setId] = (donateArmorSets[setId] || 0) + 1
+
+ if (donateArmorSets[setId] === 3) {
+ this.donateItems.push({
+ sb_id: sb_id || "NA",
+ name: item.getName() || "",
+ lore: item.getLore() || [],
+ slot: slot
+ })
+ }
+
+ }
+ }
+ slot++
+ }
+ if (oldDonateItems !== JSON.stringify(this.donateItems)) {
+ this.regenDonateItems()
+ }
+
+ if (!this.itemsInPages[page]) this.itemsInPages[page] = []
+
+ //10-16 43
+ let changed = false
+ for (let i = 0; i < 4; i++) {
+ for (let j = 10; j < 17; j++) {
+ let slot = i * 9 + j
+ let item = Player.getContainer().getStackInSlot(slot)
+ let sb_id = utils.getSBID(item)
+ if (!this.itemsInPages[page][currPage]) this.itemsInPages[page][currPage] = []
+
+ if (item && item.getID() !== -1) {
+
+ let itemData = {
+ sb_id: "NA",
+ name: item.getName() || "",
+ lore: item.getLore() || []
+ }
+
+ if (sb_id) {
+ itemData.sb_id = sb_id
+ }
+ if (!this.itemsInPages[page][currPage][slot] || this.itemsInPages[page][currPage][slot].sb_id !== itemData.sb_id) {
+ this.itemsInPages[page][currPage][slot] = itemData
+
+ changed = true
+ this.itemsInPagesSaved = false
+ }
+ } else {
+ if (this.itemsInPages[page][currPage][slot]) {
+ delete this.itemsInPages[page][currPage][slot]
+
+ changed = true
+ this.itemsInPagesSaved = false
+ }
+ }
+ }
+ }
+ if (changed || this.guiUpdated || first) this.regenItems(currPage)
+ }
+
+ if (Player.getContainer().getName() === "Confirm Donation") {
+ let this_confirm_temp_str = (Player.getContainer().getStackInSlot(4)?.getName() || "") + (Player.getContainer().getStackInSlot(2)?.getName() || "") + (Player.getContainer().getStackInSlot(20)?.getName() || "") + (Player.getContainer().getStackInSlot(24)?.getName() || "")//4, 24, 20
+ if (this.confirm_temp !== this_confirm_temp_str || first) {
+ this.confirm_temp = this_confirm_temp_str
+
+ this.itemsBox.clearChildren()
+
+ let isArmour = utils.getSBID(Player.getContainer().getStackInSlot(4)) === null
+ if (isArmour) {
+ let name = Player.getContainer().getStackInSlot(2).getName()
+ let itemBox = new BoxWithText().setText(name.startsWith("§f") ? "&7" + name.substr(2) : name).setLocation(0.1, 0.05, 0.8, 0.2).setLore(Player.getContainer().getStackInSlot(2).getLore())
+
+ this.itemsBox.addChild(itemBox)
+ } else {
+ let name = Player.getContainer().getStackInSlot(4).getName()
+ let itemBox = new BoxWithText().setText(name.startsWith("§f") ? "&7" + name.substr(2) : name).setLocation(0.1, 0.05, 0.8, 0.2).setLore(Player.getContainer().getStackInSlot(4).getLore())
+
+ this.itemsBox.addChild(itemBox)
+ }
+
+ if (Player.getContainer().getStackInSlot(24) && Player.getContainer().getStackInSlot(20)) {
+
+ let cancelButton = new ButtonWithArrow().setText("§cCancel").setLocation(0.1, 0.4, 0.35, 0.2).setDirectionRight(false).setLore(Player.getContainer().getStackInSlot(24).getLore())
+ cancelButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(24, false, "LEFT")
+ }))
+ this.itemsBox.addChild(cancelButton)
+
+ let confirmButton = new ButtonWithArrow().setText("§aConfirm Donation").setLocation(0.55, 0.4, 0.35, 0.2).setLore(Player.getContainer().getStackInSlot(20).getLore())
+ confirmButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(20, false, "LEFT")
+ }))
+ this.itemsBox.addChild(confirmButton)
+ }
+
+ this.itemsBox.dirtyFrameBuffer()
+ }
+
+ this.favoriteBox.visable = false
+ this.favoriteTitleBox.visable = false
+ } else {
+ if (this.confirm_temp && this.searchText) {
+ this.showSearchItems()
+ }
+ this.confirm_temp = ""
+
+ this.favoriteBox.visable = true
+ this.favoriteTitleBox.visable = true
+ }
+
+ this.pageTitle.setText("§5" + Player.getContainer().getName())
+ this.guiUpdated = false
+ }
+
+ regenDonateItems() {
+ this.donateBox.clearChildren()
+ this.donateItems.forEach((item, i) => {
+ let itemButton = new ButtonWithArrow().setText(item.name.startsWith("§f") ? "&7" + item.name.substr(2) : item.name).setLocation(0.05, 0.025 + 0.125 * i, 0.9, 0.1).setLore(item.lore)
+ itemButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(item.slot, false, "LEFT")
+ }))
+ this.donateBox.addChild(itemButton)
+ })
+
+ this.donateBox.dirtyFrameBuffer()
+ }
+
+ showSearchItems() {
+ if (Player.getContainer().getName() === "Confirm Donation") return
+
+ if (!this.searchText) {
+ this.tickMenu(true)
+ return
+ }
+
+
+ this.itemsBox.clearChildren()
+
+ let items = []
+
+ // this.itemsInPages[page][page2]
+ Object.keys(this.itemsInPages).forEach((pageKey) => {
+ let page = this.itemsInPages[pageKey]
+ page.forEach((page2, page2I) => {
+ if (!page2) return
+ if (items.length >= 4 * 7) return
+ page2.forEach((item, slotNum) => {
+ if (!item) return
+ if (items.length >= 4 * 7) return
+ if (item.name.toLowerCase().includes(this.searchText.toLowerCase())) {
+ let loreNew = []
+ Object.values(item.lore).forEach(a => {
+ loreNew.push(a)
+ })
+ item.lore = loreNew
+ item.page = pageKey //category eg: Weapons, armour sets ect
+ item.page2 = page2I //pagenum of category
+ item.slotNum = slotNum //slotnum
+
+ items.push(item)
+ }
+ })
+ })
+ })
+
+ let y = 0.0325
+ let itemNum = 0
+ let width = 3
+ let widthPer = 1 / (width + 1)
+ let offset = 0.0125
+
+ items.forEach((slot, slotNum) => {
+ if (!slot) return
+
+ let child
+
+ if (slot.sb_id === "NA") {
+ child = new BoxWithText().setText(slot.name.startsWith("§f") ? "&7" + slot.name.substr(2) : slot.name).setLore(slot.lore)
+ if (slot.name.startsWith("§c")) {
+ child.setColor(255, 100, 100)
+ child.setText("&0" + slot.name.substr(2))
+ }
+ if (slot.name.startsWith("§e")) {
+ child.setColor(255, 255, 100)
+ child.setText("&0" + slot.name.substr(2))
+ }
+ } else {
+ let fItem = slot
+ child = new ButtonWithArrow().setText(slot.name.startsWith("§f") ? "&7" + slot.name.substr(2) : slot.name).setLore(slot.lore)
+ child.addEvent(new SoopyMouseClickEvent().setHandler((mouseX, mouseY, button) => {
+ if (button === 2) { //middle click -> add item to favorites
+ this.addItemToFavorites(fItem, fItem.page, fItem.page2, fItem.slotNum)
+ return
+ }
+ // Player.getContainer().click(item.slotNum, false,button===1?"RIGHT":"LEFT")
+
+ let currPage, pageNum
+ if (Player.getContainer().getName().includes("/")) {
+ [currPage, pageNum] = Player.getContainer().getName().split(")")[0].split("(")[1].split("/").map(a => parseInt(a))
+ }
+
+ if (this.replacePage[Player.getContainer().getName().split("➜").pop()] === fItem.page) {
+ if (currPage === fItem.page2) {
+ Player.getContainer().click(fItem.slotNum, false, "LEFT")
+ } else {
+ if (currPage < fItem.page2) {
+ Player.getContainer().click(53, false, "MIDDLE")
+ } else {
+ Player.getContainer().click(45, false, "MIDDLE")
+ }
+ }
+ } else {
+ this.clickedTopButton(fItem.page)
+ }
+ })).addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (child.hovered) {
+
+ child.setColorOffset(-20, -20, -20, 100)
+
+ Renderer.translate(0, 0, 100)
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), child.location.getXExact(), child.location.getYExact(), child.location.getWidthExact(), child.location.getHeightExact())
+
+ let clicks = "?"
+ let currPage, pageNum
+ if (Player.getContainer().getName().includes("/")) {
+ [currPage, pageNum] = Player.getContainer().getName().split(")")[0].split("(")[1].split("/").map(a => parseInt(a))
+ }
+ let pageClicks = Math.abs(currPage - fItem.page2)
+ if (this.replacePage[Player.getContainer().getName().split("➜").pop()] === fItem.page) {
+ clicks = (pageClicks + 1) + ""
+ } else {
+ if (Player.getContainer().getName() === "Your Museum") {
+ clicks = (1 + fItem.page2) + ""
+ } else {
+ clicks = (2 + fItem.page2) + ""
+ }
+ }
+
+ Renderer.translate(0, 0, 100)
+ renderLibs.drawStringCenteredFull(clicks, child.location.getXExact() + child.location.getWidthExact() / 2, child.location.getYExact() + child.location.getHeightExact() / 2, Math.min(child.location.getWidthExact() / Renderer.getStringWidth(clicks) / 4, child.location.getHeightExact() / 4 / 2))
+
+ }
+ }))
+ if (this.favoriteIds.includes(slot.sb_id)) {
+ child.setColor(200, 255, 200)
+ }
+ }
+ child.setLocation(offset + widthPer * itemNum, y, widthPer * 9 / 10, 0.125)
+ this.itemsBox.addChild(child)
+
+ itemNum++
+ if (itemNum > width) {
+ itemNum = 0
+ y += 0.135
+ }
+ })
+
+ this.itemsBox.dirtyFrameBuffer()
+ }
+
+ regenItems(page2) {
+ if (this.searchText) return
+
+ this.itemsBox.clearChildren()
+
+ let page = this.replacePage[Player.getContainer().getName().split("➜").pop()]
+
+ let y = 0.0325
+ let itemNum = 0
+ let width = 3
+ let widthPer = 1 / (width + 1)
+ let offset = 0.0125
+
+ if (!this.itemsInPages[page][page2]) this.itemsInPages[page][page2] = []
+
+ this.itemsInPages[page][page2].forEach((slot, slotNum) => {
+ if (!slot) return
+
+ let child
+
+ if (slot.sb_id === "NA") {
+ child = new BoxWithText().setText(slot.name.startsWith("§f") ? "&7" + slot.name.substr(2) : slot.name).setLore(slot.lore)
+ if (slot.name.startsWith("§c")) {
+ child.setColor(255, 100, 100)
+ child.setText("&0" + slot.name.substr(2))
+ }
+ if (slot.name.startsWith("§e")) {
+ child.setColor(255, 255, 100)
+ child.setText("&0" + slot.name.substr(2))
+ }
+ } else {
+ child = new ButtonWithArrow().setText(slot.name.startsWith("§f") ? "&7" + slot.name.substr(2) : slot.name).setLore(slot.lore)
+ child.addEvent(new SoopyMouseClickEvent().setHandler((mouseX, mouseY, button) => {
+ if (button === 2) { //middle click -> add item to favorites
+ this.addItemToFavorites(slot, page, page2, slotNum)
+ return
+ }
+ // Player.getContainer().click(slotNum, false,button===1?"RIGHT":"LEFT")
+ Player.getContainer().click(slotNum, false, "LEFT") //TODO: add right click support for viewing armour sets
+ }))
+ if (this.favoriteIds.includes(slot.sb_id)) {
+ child.setColor(200, 255, 200)
+ }
+ }
+ child.setLocation(offset + widthPer * itemNum, y, widthPer * 9 / 10, 0.125)
+ this.itemsBox.addChild(child)
+
+ itemNum++
+ if (itemNum > width) {
+ itemNum = 0
+ y += 0.135
+ }
+ })
+
+ this.itemsBox.dirtyFrameBuffer()
+ }
+
+ addItemToFavorites(slot, page, page2, slotNum) {
+ if (page === "Special Items") {
+ new Notification("§cError!", ["You cant add Special Items", ["to favorites"]])
+ return
+ }
+ if (this.favoriteItems.map(a => a.sb_id).includes(slot.sb_id)) {
+ //remove from favorites
+ this.favoriteItems = this.favoriteItems.filter(a => a.sb_id !== slot.sb_id)
+ this.favoriteIds = this.favoriteIds.filter(a => a !== slot.sb_id)
+ this.regenItems(page2)
+ this.updatedFavorites()
+ return
+ }
+ let loreNew = []
+ slot.lore.forEach(a => {
+ loreNew.push(a)
+ })
+ slot.lore = loreNew
+ slot.page = page //category eg: Weapons, armour sets ect
+ slot.page2 = page2 //pagenum of category
+ slot.slotNum = slotNum //slotnum
+ this.favoriteItems.push(slot)
+ this.favoriteIds.push(slot.sb_id)
+ this.regenItems(page2)
+ this.updatedFavorites()
+ }
+
+ updatedFavorites(saveToFile = true) {
+ this.favoriteBox.clearChildren()
+
+ if (this.favoriteItems.length === 0) {
+ let item = new SoopyMarkdownElement().setText("Middle click to add an item to your favorites list").setLocation(0.05, 0.025, 0.9, 0.9)
+
+ this.favoriteBox.addChild(item)
+ }
+
+ this.favoriteItems.forEach((fItem, i) => {
+ let item = new ButtonWithArrow().setText(fItem.name.startsWith("§f") ? "&7" + fItem.name.substr(2) : fItem.name).setLocation(0.05, 0.025 + 0.125 * i, 0.9, 0.1).setLore(fItem.lore)
+
+ item.addEvent(new SoopyMouseClickEvent().setHandler((mouseX, mouseY, button) => {
+ if (button === 2) { //middle click -> remove item from favorites (calling add will remove because it alr exists)
+ this.addItemToFavorites(fItem, fItem.page, fItem.page2, fItem.slotNum)
+ return
+ }
+ // Player.getContainer().click(item.slotNum, false,button===1?"RIGHT":"LEFT")
+
+ let currPage, pageNum
+ if (Player.getContainer().getName().includes("/")) {
+ [currPage, pageNum] = Player.getContainer().getName().split(")")[0].split("(")[1].split("/").map(a => parseInt(a))
+ }
+
+ if (this.replacePage[Player.getContainer().getName().split("➜").pop()] === fItem.page) {
+ if (currPage === fItem.page2) {
+ Player.getContainer().click(fItem.slotNum, false, "LEFT")
+ } else {
+ if (currPage < fItem.page2) {
+ Player.getContainer().click(53, false, "MIDDLE")
+ } else {
+ Player.getContainer().click(45, false, "MIDDLE")
+ }
+ }
+ } else {
+ this.clickedTopButton(fItem.page)
+ }
+ })).addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (item.hovered) {
+
+ item.setColorOffset(-20, -20, -20, 100)
+
+ Renderer.translate(0, 0, 100)
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), item.location.getXExact(), item.location.getYExact(), item.location.getWidthExact(), item.location.getHeightExact())
+
+ let clicks = "?"
+ let currPage, pageNum
+ if (Player.getContainer().getName().includes("/")) {
+ [currPage, pageNum] = Player.getContainer().getName().split(")")[0].split("(")[1].split("/").map(a => parseInt(a))
+ }
+ let pageClicks = Math.abs(currPage - fItem.page2)
+ if (this.replacePage[Player.getContainer().getName().split("➜").pop()] === fItem.page) {
+ clicks = (pageClicks + 1) + ""
+ } else {
+ if (Player.getContainer().getName() === "Your Museum") {
+ clicks = (1 + fItem.page2) + ""
+ } else {
+ clicks = (2 + fItem.page2) + ""
+ }
+ }
+
+ Renderer.translate(0, 0, 100)
+ renderLibs.drawStringCenteredFull(clicks, item.location.getXExact() + item.location.getWidthExact() / 2, item.location.getYExact() + item.location.getHeightExact() / 2, Math.min(item.location.getWidthExact() / Renderer.getStringWidth(clicks) / 4, item.location.getHeightExact() / 4 / 2))
+
+ }
+ }))
+
+ this.favoriteBox.addChild(item)
+ })
+
+ if (saveToFile) {
+ new Thread(() => {
+ FileLib.write("soopyAddonsData", "museumFavoriteData.json", JSON.stringify(this.favoriteItems))
+ }).start()
+ }
+
+ this.favoriteBox.dirtyFrameBuffer()
+ }
+
+ guiOpened(event) {
+ let name = ""
+ if (event.gui && event.gui.field_147002_h instanceof ContainerChest) {
+ name = event.gui.field_147002_h.func_85151_d().func_145748_c_().func_150260_c()
+ }
+ if (this.dontOpen > 0) {
+ this.dontOpen--
+ }
+ if (this.soopyGui.ctGui.isOpen()) {
+ if (event.gui && event.gui.field_147002_h) {
+ Player.getPlayer().field_71070_bA = event.gui.field_147002_h
+
+ if (Player.getContainer().getName() === "Museum Rewards") {
+ return
+ }
+ if (Player.getContainer().getName().startsWith("Museum Browser")) {
+ return
+ }
+
+ event.gui = this.soopyGui.ctGui
+ this.guiUpdated = true
+ this.soopyGui.ctGui.open()
+
+ this.itemsBox.dirtyFrameBuffer()
+ }
+ return
+ }
+ if (this.isInMuseum) {
+ this.soopyGui.ctGui.open()
+
+ this.itemsBox.dirtyFrameBuffer()
+ } else {
+ if (name === "Your Museum" && !this.isInMuseum) {
+
+ if (event.gui && event.gui.field_147002_h) Player.getPlayer().field_71070_bA = event.gui.field_147002_h
+
+ this.isInMuseum = true
+
+ this.soopyGui.open()
+ event.gui = this.soopyGui.ctGui
+ this.guiOpenTickThing = true
+
+ this.itemsBox.clearChildren()
+ let rewardsButton = new ButtonWithArrow().setText("§5Rewards").setLocation(0.1, 0.05, 0.8, 0.2)
+ rewardsButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(40, false, "MIDDLE")
+ }))
+ this.itemsBox.addChild(rewardsButton)
+ let browserButton = new ButtonWithArrow().setText("§5Museum Browser").setLocation(0.1, 0.3, 0.8, 0.2)
+ browserButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(50, false, "MIDDLE")
+ }))
+ this.itemsBox.addChild(browserButton)
+
+ this.nextButton.visable = false
+ this.previousButton.visable = false
+
+ this.donateTitleBox.visable = false
+ this.donateBox.visable = false
+
+ this.pageTitle.setText("§5" + name)
+
+ this.tickMenu(true)
+
+ this.itemsBox.dirtyFrameBuffer()
+ }
+ }
+ }
+
+ keyPress(key, keyId) {
+ if (keyId === 1) { //escape key
+ this.isInMuseum = false
+ // this.dontOpen = 1
+ Client.currentGui.close()
+ }
+ }
+
+ tick() {
+ if (this.isInMuseum) {
+ if (this.soopyGui.ctGui.isOpen() || this.guiOpenTickThing) {
+ this.tickMenu()
+
+ this.guiOpenTickThing = false
+ } else {
+ // Client.currentGui.close()
+ this.isInMuseum = false
+
+ this.lastClosed = Date.now()
+ }
+ }
+
+ if (!(this.soopyGui.ctGui.isOpen() || this.guiOpenTickThing) && Date.now() - this.lastClosed > 1000) {
+ this.weaponsProgressBar.setProgress(0)
+ this.armourProgressBar.setProgress(0)
+ this.raritiesProgressBar.setProgress(0)
+ }
+
+ if (this.dontOpen > 0) {
+ Client.currentGui.close()
+ }
+ }
+}
+
+export default MuseumGui; \ No newline at end of file
diff --git a/src/features/changeLogGUI/index.js b/src/features/changeLogGUI/index.js
new file mode 100644
index 0000000..c9c25fe
--- /dev/null
+++ b/src/features/changeLogGUI/index.js
@@ -0,0 +1,255 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import SoopyMarkdownElement from '../../../guimanager/GuiElement/SoopyMarkdownElement.js';
+import metadata from "../../metadata";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ProgressBar from "../../../guimanager/GuiElement/ProgressBar"
+import SoopyRenderEvent from "../../../guimanager/EventListener/SoopyRenderEvent"
+import { fetch } from "../../utils/networkUtils";
+import { numberWithCommas } from "../../utils/numberUtils";
+const File = Java.type("java.io.File")
+const URL = Java.type("java.net.URL");
+const PrintStream = Java.type("java.io.PrintStream");
+const Byte = Java.type("java.lang.Byte");
+//global.soopyv2featuremanagerthing.features.changeLogGUI.class.ChangelogPage.downloadUpdate()
+class ChangeLogGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.ChangelogPage = new ChangelogPage()
+
+ this.latestAnnouncedVersion = this.ChangelogPage.currVersionId
+
+ this.registerEvent("worldLoad", this.worldLoad)
+
+ this.registerStep(false, 60 * 5, () => {
+ this.ChangelogPage.loadChangeLog()
+ })
+ }
+
+ worldLoad() {
+ if (this.ChangelogPage.downloadableVersion === -1) return
+
+
+ if (this.FeatureManager.features["globalSettings"] === undefined || this.FeatureManager.features["globalSettings"].class.alertAllUpdates === undefined) {
+ return
+ }
+ let alertBeta = this.FeatureManager.features["globalSettings"].class.alertAllUpdates.getValue()
+
+ if (this.latestAnnouncedVersion < (alertBeta ? this.ChangelogPage.downloadableVersion : this.ChangelogPage.importantVersion)) {
+ let version = ""
+ this.ChangelogPage.changelogData.forEach(data => {
+
+ if (this.ChangelogPage.downloadableVersion === data.versionId && this.ChangelogPage.downloadableVersion > this.ChangelogPage.currVersionId) {
+ //add button to download this version
+ version = data.version
+ }
+ })
+
+ ChatLib.chat("§r")
+ ChatLib.chat("&1" + ChatLib.getChatBreak("-").trim())
+ ChatLib.chat(" &6New SoopyV2 Version is avalible (" + version + ")")
+ ChatLib.chat("§r§r")
+ new TextComponent(" &e[CLICK] &7- View changelog and download update").setHover("show_text", "&2Open changelog").setClick("run_command", "/soopyv2 changelog").chat()
+ ChatLib.chat("&1" + ChatLib.getChatBreak("-").trim())
+ ChatLib.chat("§r§r§r")
+
+ this.latestAnnouncedVersion = this.ChangelogPage.downloadableVersion
+ }
+ }
+
+ initVariables() {
+ this.ChangelogPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class ChangelogPage extends GuiPage {
+ constructor() {
+ super(9)
+
+ this.name = "Changelog"
+
+ this.pages = [this.newPage()]
+
+ this.changelogData = []
+ this.downloadableVersion = -1
+
+ this.importantVersion = -1
+
+ let changelogTitle = new SoopyTextElement().setText("§0Changelog").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.pages[0].addChild(changelogTitle)
+
+ this.changelogArea = new SoopyGuiElement().setLocation(0.1, 0.2, 0.8, 0.8).setScrollable(true)
+ this.pages[0].addChild(this.changelogArea)
+
+ //Update confirmation page
+ this.updatingSidebar = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+ this.updatingSidebarConfirmPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+ this.updatingSidebar.addChild(this.updatingSidebarConfirmPage)
+
+ this.updateTitle = new SoopyTextElement().setText("§0Update to SoopyV2 ").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.updatingSidebarConfirmPage.addChild(this.updateTitle)
+
+ // this.warningMessage = new SoopyMarkdownElement().setLocation(0.1, 0.2, 0.8, 0.8)
+ // this.warningMessage.setText(``)
+ // this.updatingSidebarConfirmPage.addChild(this.warningMessage)
+
+ this.updateButton = new ButtonWithArrow().setText("§0Update").setLocation(0.3, 0.3, 0.4, 0.2)
+ this.updatingSidebarConfirmPage.addChild(this.updateButton)
+
+ this.updatingSidebarConfirmPage.setScrollable(true)
+
+ this.updateButton.addEvent(new SoopyRenderEvent().setHandler(() => {
+ this.updateButton.location.location.y.set(0.3, 0)
+ }))
+ this.updateButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.downloadUpdate()
+ }))
+
+ this.updatingSidebarLoadingPage = new SoopyGuiElement().setLocation(1, 0, 1, 1)
+ this.updatingSidebar.addChild(this.updatingSidebarLoadingPage)
+
+ let updatingTitle = new SoopyTextElement().setText("§0Updating...").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.updatingSidebarLoadingPage.addChild(updatingTitle)
+
+ this.progressBar = new ProgressBar().setLocation(0.1, 0.2, 0.8, 0.1)
+ this.updatingSidebarLoadingPage.addChild(this.progressBar)
+
+ this.currVersionId = metadata.versionId
+
+ this.finaliseLoading()
+
+ this.loadChangeLog()
+ }
+
+ async loadChangeLog() {
+ let data = await fetch("http://soopy.dev/api/soopyv2/changelog.json").json()
+
+ this.changelogData = data.changelog.reverse()
+
+ this.downloadableVersion = data.downloadableVersion
+ this.importantVersion = data.importantVersion
+
+ this.updateText()
+ }
+
+ onOpen() {
+ this.loadChangeLog()
+ }
+
+ showConfirmUpdatePage() {
+ let version = ""
+ this.changelogData.forEach(data => {
+
+ if (this.downloadableVersion === data.versionId && this.downloadableVersion > this.currVersionId) {
+ //add button to download this version
+ version = data.version
+ }
+ })
+ this.updateTitle.setText("§0Update to SoopyV2 " + version)
+
+ this.updateButton.location.location.y.set(0.3, 0)
+
+ this.openSidebarPage(this.updatingSidebar)
+ }
+
+ downloadUpdate() {
+ new Thread(() => {
+ this.updatingSidebarConfirmPage.location.location.x.set(-1, 500)
+ this.updatingSidebarLoadingPage.location.location.x.set(0, 500)
+
+ new File("./config/ChatTriggers/modules/SoopyAddonsTempDownload").mkdir()
+
+ this.progressBar.setProgress(0.1)
+
+ this.urlToFile("http://soopy.dev/api/soopyv2/downloadLatest.zip", "./config/ChatTriggers/modules/SoopyAddonsTempDownload/SoopyAddons.zip", 10000, 20000)
+
+ this.progressBar.setProgress(0.5)
+
+ FileLib.unzip("./config/ChatTriggers/modules/SoopyAddonsTempDownload/SoopyAddons.zip", "./config/ChatTriggers/modules/SoopyAddonsTempDownload/SoopyAddons/")
+
+ this.progressBar.setProgress(0.75)
+
+ FileLib.deleteDirectory(new File("./config/ChatTriggers/modules/SoopyV2"))
+
+ this.progressBar.setProgress(0.9)
+
+ new File("./config/ChatTriggers/modules/SoopyAddonsTempDownload/SoopyAddons/SoopyV2").renameTo(new File("./config/ChatTriggers/modules/SoopyV2"))
+
+ FileLib.deleteDirectory(new File("./config/ChatTriggers/modules/SoopyAddonsTempDownload"))
+
+ this.progressBar.setProgress(1)
+
+ Client.currentGui.close()
+
+ ChatLib.command("ct load", true)
+ }).start()
+ }
+
+ urlToFile(url, destination, connecttimeout, readtimeout) {
+ const d = new File(destination);
+ d.getParentFile().mkdirs();
+ const connection = new URL(url).openConnection();
+ connection.setDoOutput(true);
+ connection.setConnectTimeout(connecttimeout);
+ connection.setReadTimeout(readtimeout);
+ const IS = connection.getInputStream();
+ const FilePS = new PrintStream(destination);
+ let buf = new Packages.java.lang.reflect.Array.newInstance(Byte.TYPE, 65536);
+ let len;
+ while ((len = IS.read(buf)) > 0) {
+ FilePS.write(buf, 0, len);
+ }
+ IS.close();
+ FilePS.close();
+ }
+
+ updateText() {
+ this.changelogArea.children = []
+
+ let height = 0
+
+ this.changelogData.forEach(data => {
+
+ if (this.downloadableVersion === data.versionId && this.downloadableVersion > this.currVersionId) {
+ //add button to download this version
+ let button = new ButtonWithArrow().setText("§0Download this version").setLocation(0.7, height, 0.3, 0.1)
+ this.changelogArea.addChild(button)
+
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.showConfirmUpdatePage()
+ }))
+
+ height += 0.05
+ }
+
+ let changes = new SoopyMarkdownElement().setLocation(0, height, 1, 0)
+
+ this.changelogArea.addChild(changes)
+
+ changes.setText("# __" + data.version + "__" + (data.versionId === this.currVersionId ? " §7Current (" : " §7(") + numberWithCommas(data.users || 0) + " using)" + "\n" + data.description)
+
+ height += changes.getHeight()
+
+ height += 0.05
+ })
+ }
+}
+
+module.exports = {
+ class: new ChangeLogGui()
+} \ No newline at end of file
diff --git a/src/features/changeLogGUI/metadata.json b/src/features/changeLogGUI/metadata.json
new file mode 100644
index 0000000..8dfbe4b
--- /dev/null
+++ b/src/features/changeLogGUI/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "ChangeLog gui",
+ "description": "Gui for changelog",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/cosmetics/cosmetic.js b/src/features/cosmetics/cosmetic.js
new file mode 100644
index 0000000..878e425
--- /dev/null
+++ b/src/features/cosmetics/cosmetic.js
@@ -0,0 +1,29 @@
+
+class Cosmetic{
+ constructor(player, parent, id){
+
+ if(player.getUUID().toString() === Player.getUUID().toString()) player = Player
+ /**
+ * @type {PlayerMP | Player}
+ */
+ this.player = player
+
+ this.parent = parent
+
+ this.id = id
+
+ this.settings = this.parent.getPlayerCosmeticSettings(this.player, id)
+
+ this.onTick()
+ }
+
+ onRenderEntity(ticks, isInGui){
+ //override
+ }
+
+ onTick(){
+ //override
+ }
+}
+
+export default Cosmetic; \ No newline at end of file
diff --git a/src/features/cosmetics/cosmetic/dragon/dragonWings.js b/src/features/cosmetics/cosmetic/dragon/dragonWings.js
new file mode 100644
index 0000000..944ba78
--- /dev/null
+++ b/src/features/cosmetics/cosmetic/dragon/dragonWings.js
@@ -0,0 +1,482 @@
+import renderLibs from "../../../../../guimanager/renderLibs.js";
+import { f, m } from "../../../../../mappings/mappings.js";
+import ToggleSetting from "../../../settings/settingThings/toggle.js";
+import Cosmetic from "../../cosmetic.js";
+
+const ModelDragon = Java.type("net.minecraft.client.model.ModelDragon")
+
+if (!GlStateManager) {
+ // var GL11 = Java.type("org.lwjgl.opengl.GL11"); //using var so it goes to global scope
+ var GlStateManager = Java.type("net.minecraft.client.renderer.GlStateManager");
+}
+const Essential = Java.type("gg.essential.Essential")
+const EssentialCosmeticSlot = Java.type("gg.essential.mod.cosmetics.CosmeticSlot")
+const EssentialBone = Java.type("gg.essential.model.Bone")
+
+const FACING = Java.type("net.minecraft.block.BlockDirectional").field_176387_N
+let dragon = new ModelDragon(0) //too lazy to make my own model so i just yoink it from modelDragon lmfao
+let textures = new Map()
+let loadingTextures = new Set()
+function loadTexture(id) {
+ new Thread(() => {
+ loadingTextures.add(id)
+ textures.set(id, renderLibs.getImage("http://soopy.dev/api/soopyv2/textures/cosmetic/dragon/" + id + "/img.png", true))
+ }).start()
+}
+loadTexture("classic")
+let wing = getField(dragon, f.wing)
+let wingTip = getField(dragon, f.wingTip)
+
+class DragonWings extends Cosmetic {
+ constructor(player, parent) {
+ super(player, parent, "dragon_wings");
+
+ this.animOffset = Math.random() * 20 * Math.PI
+ this.lastRender = Date.now()
+
+ this.lastFlapSound = this.animOffset
+ this.i = 0
+
+ this.flying = false
+
+ if (!textures.has(this.settings.texture) && !loadingTextures.has(this.settings.texture)) {
+ loadTexture(this.settings.texture)
+ }
+ }
+
+ onRenderEntity(ticks, isInGui) {
+
+ if (this.player.getPlayer()[m.isInvisibleToPlayer](Player.getPlayer()) || this.player.getPlayer()[m.isInvisible]()) {
+ return
+ }
+ if (!textures.has("classic")) return
+
+ let isSelfPlayer = this.player.getUUID().toString() === Player.getUUID().toString()
+ let isInInv = isSelfPlayer && ticks === 1
+ let thirdPersonView = Client.getMinecraft()[f.gameSettings.Minecraft][f.thirdPersonView]
+
+ if (!this.parent.firstPersonVisable.getValue() && thirdPersonView === 0 && isSelfPlayer && !isInInv) return
+
+ // return;
+ // wing.func_78785_a(1)
+
+ let timeSince = (Date.now() - this.lastRender) / 1000
+ this.lastRender = Date.now()
+
+ let rotation = isInInv ? 0 : this.player.getPlayer()[f.prevRenderYawOffset] + (this.player.getPlayer()[f.renderYawOffset] - this.player.getPlayer()[f.prevRenderYawOffset]) * ticks
+ // rotation += entity.field_70761_aq+(entity.field_70761_aq-entity.field_70760_ar)*ticks
+ // console.log(rotation, entity.getEntity().field_70761_aq+(entity.getEntity().field_70761_aq-entity.getEntity().field_70760_ar)*ticks)
+ let horisontalSpeed = Math.hypot((this.player.getPlayer()[f.posX.Entity] - this.player.getPlayer()[f.lastTickPosX]), (this.player.getPlayer()[f.posZ.Entity] - this.player.getPlayer()[f.lastTickPosZ]))
+
+ let verticleSpeed = this.player.getPlayer()[f.posY.Entity] - this.player.getPlayer()[f.lastTickPosY]
+
+ this.animOffset += Math.min(1, horisontalSpeed) * 10 * timeSince + 1 * timeSince
+
+ let flapAmountMultiplyerNoEnd = 1
+ let flapAmountMultiplyer = 1
+
+ let wingEndOffsetThing = 0
+
+ flapAmountMultiplyerNoEnd += Math.min(5, (horisontalSpeed * 5))
+ let flapMainOffsetThing = 0
+
+ let wingBackAmount = 0
+
+ let shouldStandStillWingCurve = true
+
+ if (this.player.getPlayer()[f.hurtResistantTime] > 17) { //damage tick
+ this.animOffset += 25 * timeSince
+ }
+
+
+ // if((this.player === Player &&this.player.getPlayer().field_71075_bZ.field_75100_b) || (this.player !== Player && Math.abs(verticleSpeed)<0.2 && !this.player.getPlayer().field_70122_E)){//playerCapabilities.isFlying
+ if (this.flying) { //flying
+ shouldStandStillWingCurve = false
+ this.animOffset += 5 * timeSince //flap in mid air
+
+ flapAmountMultiplyer *= 1.75 //flap harder
+
+ if (isSelfPlayer && thirdPersonView === 0) {
+ if (!this.parent.lessFirstPersonVisable.getValue()) {
+ flapAmountMultiplyerNoEnd += 0.4
+ flapMainOffsetThing = 0.3
+ }
+ } else {
+ flapAmountMultiplyer *= 1.25
+ flapAmountMultiplyer *= 0.9
+ flapMainOffsetThing = 0.1
+ wingEndOffsetThing += -0.1
+ }
+
+ wingEndOffsetThing += -0.75
+
+ if (verticleSpeed > 0) {
+ this.animOffset += verticleSpeed * 25 * timeSince //flap when flying upwards
+ }
+ } else {
+ if (this.lastFlapSound < this.animOffset - this.animOffset % (Math.PI * 2)) {
+ this.lastFlapSound = this.animOffset - this.animOffset % (Math.PI * 2)
+ }
+ }
+ if (verticleSpeed < -0.5) {
+ wingBackAmount = Math.min(1, (verticleSpeed + 0.5) * -1.5) //lift wings back further ur falling
+
+ this.animOffset += (verticleSpeed + 0.5) * -3 * timeSince
+ }
+
+ GlStateManager[m.pushMatrix](); // pushMatrix
+ Tessellator.colorize(this.settings.color.r, this.settings.color.g, this.settings.color.b);
+
+ if (!isSelfPlayer) {
+ Tessellator.translate(
+ (this.player.getPlayer()[f.lastTickPosX] + (this.player.getPlayer()[f.posX.Entity] - this.player.getPlayer()[f.lastTickPosX]) * ticks) - (Player.getPlayer()[f.lastTickPosX] + (Player.getPlayer()[f.posX.Entity] - Player.getPlayer()[f.lastTickPosX]) * ticks),
+ (this.player.getPlayer()[f.lastTickPosY] + (this.player.getPlayer()[f.posY.Entity] - this.player.getPlayer()[f.lastTickPosY]) * ticks) - (Player.getPlayer()[f.lastTickPosY] + (Player.getPlayer()[f.posY.Entity] - Player.getPlayer()[f.lastTickPosY]) * ticks),
+ (this.player.getPlayer()[f.lastTickPosZ] + (this.player.getPlayer()[f.posZ.Entity] - this.player.getPlayer()[f.lastTickPosZ]) * ticks) - (Player.getPlayer()[f.lastTickPosZ] + (Player.getPlayer()[f.posZ.Entity] - Player.getPlayer()[f.lastTickPosZ]) * ticks))
+ }
+
+ if (textures.get(this.settings.texture || "classic")) {
+ Tessellator.bindTexture(textures.get(this.settings.texture || "classic")) //bind texture
+ } else {
+ Tessellator.bindTexture(textures.get("classic")) //bind default texture (classic)
+ }
+
+ if (this.player.getPlayer()[f.ridingEntity.Entity]) {
+ rotation = this.player.getPlayer()[f.rotationYawHead] + (this.player.getPlayer()[f.rotationYawHead] - this.player.getPlayer()[f.prevRotationYawHead]) * ticks
+ }
+ if (!this.player.getPlayer()[m.isPlayerSleeping]()) { //dont rotate when in bed
+ Tessellator.rotate((180 - rotation), 0, 1, 0)
+
+ Tessellator.translate(0, 1.2, 0.13)
+
+ if (this.player.getPlayer()[m.isSneaking.Entity]()) { //isSneaking
+ Tessellator.translate(0, -0.125, 0)
+ Tessellator.rotate(-20, 1, 0, 0)
+
+ Tessellator.translate(0, 0, 0.1)
+ if (isSelfPlayer && thirdPersonView === 0) { } else {
+ Tessellator.translate(0, -0.125, 0)
+ }
+ }
+
+ if (isSelfPlayer && !isInInv && thirdPersonView === 0) {
+ //Make wings less scuffed when in first person looking down/up
+ Tessellator.translate(0, 0.25, 0.003 * (this.player.getPitch()))
+ }
+ }
+
+
+ //Higher = more elytra like
+ wing[f.rotateAngleY] = 0.25; //rotateAngleY
+
+ let shouldStandingStillWingThing = false
+
+ let changeStandingStillWingThing = 0
+
+ if (horisontalSpeed < 0.01) {
+ if (!(this.flying)) { //not flying
+ let amt = (this.animOffset + Math.PI / 2) % (20 * Math.PI)
+ if (amt < 1 * Math.PI) {
+ this.animOffset += 2 * timeSince * Math.min(1, (amt / (1 * Math.PI)) * 2)
+
+ flapAmountMultiplyer += (amt / (1 * Math.PI)) / 2
+ } else if (amt < 2 * Math.PI) {
+ this.animOffset += 2 * timeSince * Math.min(1, (1 - (amt / (1 * Math.PI) - 1)) * 2)
+
+ flapAmountMultiplyer += (1 - (amt / (1 * Math.PI) - 1)) / 2
+ }
+ }
+ if (this.player.getPlayer()[m.isSneaking.Entity]()) { //isSneaking
+ if (this.player.getPlayer()[f.rotationPitch] > 20) {
+ shouldStandingStillWingThing = true
+ shouldStandStillWingCurve = false
+ changeStandingStillWingThing = Math.max(0, this.player.getPlayer()[f.rotationPitch] / 600)
+ }
+ }
+ }
+
+ if (shouldStandingStillWingThing) {
+ wing[f.rotateAngleY] = 0.25 + (changeStandingStillWingThing) * 3
+ }
+ if (this.player.getPlayer()[m.isPlayerSleeping]()) { //player in bed
+
+ try { //try catch incase no bed at that location
+ let facing = World.getWorld().func_180495_p(this.player.getPlayer()[f.playerLocation])[m.getValue.BlockState$StateImplementation](FACING)[m.getHorizontalIndex]() //0-3 is S-W-N-E
+
+ let rotation = 0
+ switch (facing) {
+ case 0:
+ rotation = 180
+ Tessellator.translate(0, 0, -0.5)
+ break
+ case 1:
+ rotation = 90
+ Tessellator.translate(0.5, 0, 0)
+ break
+ case 2:
+ rotation = 0
+ Tessellator.translate(0, 0, 0.5)
+ break
+ case 3:
+ rotation = 270
+ Tessellator.translate(-0.5, 0, 0)
+ break
+ }
+ // console.log(rotation)
+ // console.log(World.getBlockAt(this.player.getX(), this.player.getY(), this.player.getZ()).getState().func_177229_b(FACING))
+ Tessellator.rotate(rotation, 0, 1, 0)
+ } catch (e) { }
+ Tessellator.translate(0, - this.settings.scale * 25, 0)
+
+ wing[f.rotateAngleX] = 0; //rotateAngleX
+
+ wing[f.rotateAngleZ] = (-0.45 + Math.sin(this.animOffset / 5) * 0.03); //rotateAngleZ
+
+
+ wingTip[f.rotateAngleZ] = -2.5 + Math.sin(this.animOffset / 5) * 0.03
+ } else if (wingBackAmount === 0) {
+ //tilt
+
+
+ let wing_goback_amount = 0.15 / (Math.min(1, horisontalSpeed) * 3 + 0.25)
+ let temp_wing_thing = 1
+
+ if (shouldStandingStillWingThing) {
+ wing_goback_amount /= 1 + (changeStandingStillWingThing) / 50
+ flapAmountMultiplyer /= 1 + (changeStandingStillWingThing) / 50
+
+ temp_wing_thing += changeStandingStillWingThing * 50
+ }
+
+ let wing_tilt_offset = -Math.min(0.8, horisontalSpeed * 3) + 0.3 //When go faster tilt wing back so its in direction of wind
+
+
+ if (shouldStandingStillWingThing) {
+ wing_tilt_offset += (changeStandingStillWingThing) * 4
+ }
+
+ wing[f.rotateAngleX] = 0.85 - Math.cos(this.animOffset) * 0.2 + wing_tilt_offset - (flapAmountMultiplyer - 1) / 3; //rotateAngleX
+
+ let temp_horis_wingthing = 0
+ if (shouldStandingStillWingThing) {
+ temp_horis_wingthing = -(changeStandingStillWingThing) * 0.75
+ }
+
+ wing[f.rotateAngleZ] = (Math.sin(this.animOffset) / temp_wing_thing + 0.125) * wing_goback_amount * (1 + (flapAmountMultiplyer - 1) * 1) * flapAmountMultiplyerNoEnd - 0.4 - wing_tilt_offset / 3 + temp_horis_wingthing + flapMainOffsetThing; //rotateAngleZ
+
+ let standStillCurveThing = shouldStandStillWingCurve ? (2 - flapAmountMultiplyer) * 0.5 : 0
+
+ wingTip[f.rotateAngleZ] = standStillCurveThing - ((Math.sin((this.animOffset + 1.5 + (1 - temp_wing_thing) / 8.5)) / (1 + (temp_wing_thing - 1) / 3) + 0.5)) * 0.75 * (1 + (flapAmountMultiplyer - 1) * 1) / (1 + temp_horis_wingthing) - (1 - flapAmountMultiplyer) * 2 - (1 - temp_wing_thing) / 10 + wingEndOffsetThing; //rotateAngleZ
+ } else {
+ //tilt
+ let wing_tilt_offset = -Math.min(0.8, horisontalSpeed * 3) //When go faster tilt wing back so its in direction of wind
+ wing[f.rotateAngleX] = 0.75 - Math.cos(this.animOffset) * 0.2 + wing_tilt_offset - wingBackAmount / 2; //rotateAngleX
+
+
+ wing[f.rotateAngleZ] = -wingBackAmount; //rotateAngleZ
+
+
+ wingTip[f.rotateAngleZ] = -((Math.sin((this.animOffset)) * 0.5 + 0.3))
+ }
+
+ GlStateManager[m.disableCull]() //disable culling
+
+ let wing_center_dist = ((0 - Math.log(1000 * this.settings.scale + 0.01) - 2) - 100000 * this.settings.scale * this.settings.scale) / 1000
+
+ // GL11.glDepthMask(GL11.GL_FALSE);
+ Tessellator.translate(-wing_center_dist, 0, 0)
+ Tessellator.scale(this.settings.scale, this.settings.scale, this.settings.scale)
+ wing[m.renderWithRotation](1) //render left wing
+
+ Tessellator.translate(2 * wing_center_dist / this.settings.scale, 0, 0)
+ Tessellator.scale(-1, 1, 1)
+ wing[m.renderWithRotation](1) //render right wing
+
+
+ if (this.player.getPlayer()[f.hurtTime] > 0) { //damage tick
+ GlStateManager[m.pushMatrix](); // pushMatrix
+ GlStateManager[m.depthFunc](514);
+ GlStateManager[m.disableTexture2D]();
+ GlStateManager[m.enableBlend]();
+ GlStateManager[m.blendFunc](770, 771);
+ GlStateManager.func_179131_c(1, 0, 0, 0.25); //m.color.glstatemanager.ffff
+
+ Tessellator.scale(-1, 1, 1)
+ Tessellator.translate(-2 * wing_center_dist / this.settings.scale, 0, 0)
+ wing[m.renderWithRotation](1) //render left wing
+
+ Tessellator.translate(2 * wing_center_dist / this.settings.scale, 0, 0)
+ Tessellator.scale(-1, 1, 1)
+ wing[m.renderWithRotation](1) //render right wing
+
+ GlStateManager[m.enableTexture2D]();
+ GlStateManager[m.disableBlend]();
+ GlStateManager[m.depthFunc](515);
+ GlStateManager[m.popMatrix](); // popMatrix
+ }
+ Tessellator.colorize(1, 1, 1)
+ GlStateManager[m.enableCull]() //enable culling
+ GlStateManager[m.popMatrix](); // popMatrix
+ }
+
+ testPlaySound() {
+ if (this.player.getPlayer()[m.isInvisibleToPlayer](Player.getPlayer())) {
+ return
+ }
+ if (!this.parent.ownCosmeticAudio.getValue()) {
+ return
+ }
+
+ if (this.player.getPlayer()[m.isPlayerSleeping]()) return
+
+ let horisontalSpeed = Math.hypot((this.player.getPlayer()[f.posX.Entity] - this.player.getPlayer()[f.lastTickPosX]), (this.player.getPlayer()[f.posZ.Entity] - this.player.getPlayer()[f.lastTickPosZ]))
+
+
+ // if((this.player === Player &&this.player.getPlayer().field_71075_bZ.field_75100_b) || (this.player !== Player && Math.abs(verticleSpeed)<0.2 && !this.player.getPlayer().field_70122_E)){//playerCapabilities.isFlying
+ if (this.flying) { //flying
+
+ if (this.animOffset - this.lastFlapSound > 2 * Math.PI) {
+
+ let dist = Math.hypot((Player.getX() - this.player.getX()), (Player.getY() - this.player.getY()), (Player.getZ() - this.player.getZ())) + 1
+
+ World.playSound("mob.enderdragon.wings", (this.settings.scale * 15) * Math.min(1, 50 / (dist * dist)), 1)
+ this.lastFlapSound = this.animOffset - this.animOffset % (Math.PI * 2)
+ }
+ }
+
+ if (horisontalSpeed < 0.01) {
+ if (!(this.flying)) { //not flying
+ let amt = (this.animOffset + Math.PI / 2) % (20 * Math.PI)
+ if (amt < 1 * Math.PI) {
+ if (amt > 0.65 * Math.PI && (2 * Math.PI + this.animOffset) - this.lastFlapSound > 2 * Math.PI) {
+
+ let dist = Math.hypot((Player.getX() - this.player.getX()), (Player.getY() - this.player.getY()), (Player.getZ() - this.player.getZ())) + 1
+
+ World.playSound("mob.enderdragon.wings", (Math.max(0.005, this.settings.scale - 0.005) * 25) * Math.min(1, 50 / Math.min(1, dist * dist)) / 50, 1 - (Math.max(0.005, this.settings.scale - 0.005) * 25))
+ this.lastFlapSound = 2 * Math.PI + (this.animOffset) - this.animOffset % (Math.PI * 2)
+ }
+ }
+ }
+ }
+ }
+
+ onTick() {
+
+ this.updateIfNotRendering()
+
+ this.testPlaySound()
+
+ }
+
+ removeEssentialCosmetics() {
+ if (!this.player.getPlayer() || !this.player.getPlayer().getCosmeticsState || !this.player.getPlayer().getCosmeticsState() || !this.player.getPlayer().getCosmeticsState().getCosmetics || !this.player.getPlayer().getCosmeticsState().getCosmetics()) return
+ //player.()
+ let wingCosmetic = this.player.getPlayer().getCosmeticsState().getCosmetics().get(EssentialCosmeticSlot.WINGS)
+ if (wingCosmetic !== null) {
+ let cosmetic = this.player.getPlayer().getCosmeticsState().getModels().get(Essential.instance.getConnectionManager().getCosmeticsManager().getCosmetic(wingCosmetic))
+ if (cosmetic) {
+ let model = cosmetic.getModel().getModel()
+
+ let bones = model.getBones(model.getRootBone())
+
+ bones.forEach(b => {
+ setField(b, "showModel", false)
+
+ this.parent.hiddenEssentialCosmetics.push(b)
+ })
+ }
+ } else {
+ let fullBodyCosmetic = this.player.getPlayer().getCosmeticsState().getCosmetics().get(EssentialCosmeticSlot.FULL_BODY)
+ if (fullBodyCosmetic === "DRAGON_ONESIE_2") {
+ let cosmetic = this.player.getPlayer().getCosmeticsState().getModels().get(Essential.instance.getConnectionManager().getCosmeticsManager().getCosmetic(fullBodyCosmetic))
+ if (cosmetic) {
+ let model = cosmetic.getModel().getModel()
+
+ let bones = model.getBones(model.getRootBone())
+
+ bones.forEach(b => {
+ if (b.boxName === "wing_left_1" || b.boxName === "wing_right_1") {
+ setField(b, "showModel", false)
+
+ this.parent.hiddenEssentialCosmetics.push(b)
+ }
+ })
+ }
+ }
+ }
+ }
+
+ updateIfNotRendering() {
+ let verticleSpeed = this.player.getPlayer()[f.posY.Entity] - this.player.getPlayer()[f.lastTickPosY]
+
+ this.flying = (verticleSpeed > -0.2) && !this.player.getPlayer()[f.onGround.Entity]
+
+ let timeSince = (Date.now() - this.lastRender) / 1000
+
+ if (timeSince < 0.020) {
+ return
+ }
+
+ this.lastRender = Date.now()
+
+ let horisontalSpeed = Math.hypot((this.player.getPlayer()[f.posX.Entity] - this.player.getPlayer()[f.lastTickPosX]), (this.player.getPlayer()[f.posZ.Entity] - this.player.getPlayer()[f.lastTickPosZ]))
+
+
+ this.animOffset += Math.min(1, horisontalSpeed) * 10 * timeSince + 1 * timeSince
+
+ if (this.player.getPlayer()[f.hurtResistantTime] > 0) { //damage tick
+ this.animOffset += 5 * timeSince
+ }
+
+
+ // if((this.player === Player &&this.player.getPlayer().field_71075_bZ.field_75100_b) || (this.player !== Player && Math.abs(verticleSpeed)<0.2 && !this.player.getPlayer().field_70122_E)){//playerCapabilities.isFlying
+ if (this.flying) { //flying
+ this.animOffset += 5 * timeSince //flap in mid air
+
+ if (verticleSpeed > 0) {
+ this.animOffset += verticleSpeed * 25 * timeSince //flap when flying upwards
+ }
+ }
+ if (verticleSpeed < -0.5) {
+ this.animOffset += (verticleSpeed + 0.5) * -3 * timeSince
+ }
+
+ if (horisontalSpeed < 0.01) {
+ if (!(this.flying)) { //not flying
+ let amt = (this.animOffset + Math.PI / 2) % (20 * Math.PI)
+ if (amt < 1 * Math.PI) {
+ this.animOffset += 2 * timeSince * Math.min(1, (amt / (1 * Math.PI)) * 2)
+ } else if (amt < 2 * Math.PI) {
+ this.animOffset += 2 * timeSince * Math.min(1, (1 - (amt / (1 * Math.PI) - 1)) * 2)
+ }
+ }
+ }
+ }
+}
+
+export default DragonWings;
+
+
+function getField(e, field) {
+
+ let field2 = e.class.getDeclaredField(field);
+
+ field2.setAccessible(true)
+
+ return field2.get(e)
+}
+
+function setField(e, field, value) {
+
+ let field2 = e.class.getDeclaredField(field);
+
+ field2.setAccessible(true)
+
+ return field2.set(e, value)
+}
+
+let a = 0
+
+register("command", (val) => {
+ a = parseFloat(val)
+ ChatLib.chat("Set a to " + a)
+}).setName("seta", true) \ No newline at end of file
diff --git a/src/features/cosmetics/hiddenRequirement.js b/src/features/cosmetics/hiddenRequirement.js
new file mode 100644
index 0000000..dbc3e06
--- /dev/null
+++ b/src/features/cosmetics/hiddenRequirement.js
@@ -0,0 +1,4 @@
+
+module.exports = {hidden: function(featureManager){
+ return !(featureManager.features.cosmetics?.["class"]?.playerHasACosmeticA || false)
+}} \ No newline at end of file
diff --git a/src/features/cosmetics/index.js b/src/features/cosmetics/index.js
new file mode 100644
index 0000000..f3deece
--- /dev/null
+++ b/src/features/cosmetics/index.js
@@ -0,0 +1,271 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import DragonWings from "./cosmetic/dragon/dragonWings"
+import Toggle from "../settings/settingThings/toggle"
+import { f } from "../../../mappings/mappings";
+import FakeRequireToggle from "../settings/settingThings/FakeRequireToggle";
+import { fetch } from "../../utils/networkUtils";
+
+class Cosmetics extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+ this.loadedCosmetics = []
+ this.uuidToCosmetic = {}
+ this.uuidToCosmeticDirect = {}
+
+ this.cosmeticsData = {}
+
+ this.hiddenEssentialCosmetics = []
+
+ this.cosmeticsList = {
+ "dragon_wings": DragonWings
+ }
+
+ this.playerHasACosmeticA = false
+
+ this.firstPersonVisable = new Toggle("Cosmetics visable in first person", "", false, "cosmetics_first_person_visable", this)
+ this.lessFirstPersonVisable = new Toggle("Make cosmetics less visable in first person mode", "", true, "cosmetics_first_person_less_visable", this).requires(this.firstPersonVisable)
+ this.ownCosmeticAudio = new Toggle("Audio for own cosmetics", "", false, "cosmetics_own_audio", this)
+
+ this.dragon_wings_enabled = new Toggle("Dragon Wings Toggle", "", true, "cosmetic_dragon_wings_toggle", this).requires(new FakeRequireToggle(false)).onchange(this, () => {
+ global.soopyV2Server.updateCosmeticsData({
+ cosmetic: "dragon_wings",
+ type: this.dragon_wings_enabled.getValue() ? "enable" : "disable"
+ })
+ })
+
+ this.postRenderEntityTrigger = undefined
+
+ this.loadCosmeticsData()
+
+ this.worldLoad()
+
+ this.registerEvent("tick", this.tick)
+ this.registerEvent("renderWorld", this.renderWorld)
+ this.registerEvent("playerJoined", this.playerJoined)
+ this.registerEvent("playerLeft", this.playerLeft)
+ this.registerEvent("worldLoad", this.worldLoad)
+ this.registerStep(false, 2, this.step)
+ this.registerEvent('gameUnload', () => {
+ if (this.postRenderEntityTrigger) {
+ this.postRenderEntityTrigger.unregister()
+ this.postRenderEntityTrigger = undefined
+ }
+ })
+ // this.registerStep(false, 60*10, ()=>{
+ // new Thread(()=>{this.loadCosmeticsData.call(this)}).start()
+ // })
+ // this.registerEvent("renderEntity", this.renderEntity)
+
+ if (global.soopyV2Server && global.soopyV2Server.userCosmeticPermissions) {
+ this.updateUserCosmeticPermissionSettings()
+ }
+ }
+
+ updateUserCosmeticPermissionSettings() {
+ if (!this.enabled) return
+
+ if (global.soopyV2Server.userCosmeticPermissions === "*" || global.soopyV2Server.userCosmeticPermissions.dragon_wings) {
+ this.dragon_wings_enabled.requiresO.set(true)
+ } else {
+ this.dragon_wings_enabled.requiresO.set(false)
+ }
+ }
+
+ renderWorld(ticks) {
+ for (let i = 0; i < this.loadedCosmetics.length; i++) {
+ this.loadedCosmetics[i].onRenderEntity(ticks, false)
+ }
+ }
+
+ async loadCosmeticsData() {
+ let data = await fetch("http://soopy.dev/api/soopyv2/cosmetics.json").json()
+
+ this.cosmeticsData = data
+ this.playerHasACosmeticA = !!data[Player.getUUID().toString().replace(/-/g, "")]
+ if (this.playerHasACosmeticA && !this.postRenderEntityTrigger) {
+ // this.registerEvent("postRenderEntity", this.renderEntity)
+ this.postRenderEntityTrigger = register("postRenderEntity", (entity, pos, ticks, event) => {
+ if (ticks !== 1) return
+ if (this.uuidToCosmeticDirect[entity.getUUID().toString().replace(/-/g, "")]) {
+ let cosmetics = Object.values(this.uuidToCosmeticDirect[entity.getUUID().toString().replace(/-/g, "")])
+ for (let cosmetic of cosmetics) {
+ cosmetic.onRenderEntity(ticks, true)
+ }
+ }
+ })
+ }
+
+ this.scanForNewCosmetics()
+ }
+
+ setUserCosmeticsInformation(uuid, cosmetics) {
+ if (!this.enabled) return
+ uuid = uuid.replace(/-/g, "")
+
+ this.loadedCosmetics = this.loadedCosmetics.filter(cosmetic => {
+ if (cosmetic.player.getUUID().toString().replace(/-/g, "") === uuid) {
+ return false
+ }
+ return true
+ })
+ Object.keys(this.uuidToCosmetic).forEach(cosmeticName => {
+ delete this.uuidToCosmetic[cosmeticName][uuid]
+ })
+
+ delete this.uuidToCosmeticDirect[uuid]
+
+ if (!cosmetics) {
+ delete this.cosmeticsData[uuid]
+ return
+ }
+ this.cosmeticsData[uuid] = cosmetics
+
+ this.scanForNewCosmetics()
+ }
+
+ step() {
+ this.scanForNewCosmetics()
+
+ this.filterUnloadedCosmetics(false)
+
+ this.restoreEssentialCosmetics()
+
+ this.loadedCosmetics.forEach(c => {
+ c.removeEssentialCosmetics()
+ })
+ }
+ scanForNewCosmetics() {
+ this.loadCosmeticsForPlayer(Player)
+ World.getAllPlayers().forEach(p => {
+ if (p.getUUID().toString().replace(/-/g, "") === Player.getUUID().toString().replace(/-/g, "")) return
+ this.loadCosmeticsForPlayer(p)
+ })
+ }
+
+ loadCosmeticsForPlayer(player) {
+ Object.keys(this.cosmeticsList).forEach(cosmeticName => {
+ if (!this.uuidToCosmetic[cosmeticName]) this.uuidToCosmetic[cosmeticName] = {}
+
+ if (this.uuidToCosmetic[cosmeticName][player.getUUID().toString().replace(/-/g, "")]) return
+
+ if (this.shouldPlayerHaveCosmetic(player, cosmeticName)) {
+ let cosmetic = new (this.cosmeticsList[cosmeticName])(player, this)
+ this.loadedCosmetics.push(cosmetic)
+ this.uuidToCosmetic[cosmeticName][player.getUUID().toString().replace(/-/g, "")] = cosmetic
+
+ if (!this.uuidToCosmeticDirect[player.getUUID.toString()]) this.uuidToCosmeticDirect[player.getUUID().toString().replace(/-/g, "")] = {}
+ this.uuidToCosmeticDirect[player.getUUID().toString().replace(/-/g, "")][cosmeticName] = cosmetic
+ }
+ })
+ }
+
+ worldLoad() {
+ this.loadedCosmetics = []
+ this.uuidToCosmetic = {}
+ this.uuidToCosmeticDirect = {}
+
+ this.loadCosmeticsForPlayer(Player)
+ this.scanForNewCosmetics()
+ }
+
+ playerJoined(player) {
+ if (player.getUUID().toString().replace(/-/g, "") === Player.getUUID().toString().replace(/-/g, "")) return
+
+ this.loadCosmeticsForPlayer(player)
+ }
+
+ playerLeft(playerName) {
+ this.loadedCosmetics = this.loadedCosmetics.filter(cosmetic => {
+ if (cosmetic.player.getUUID().toString().replace(/-/g, "") === Player.getUUID().toString().replace(/-/g, "")) return true
+ if (cosmetic.player.getName() === playerName) {
+ this.uuidToCosmetic[cosmetic.id][cosmetic.player.getUUID().toString().replace(/-/g, "")] = undefined
+
+ this.uuidToCosmeticDirect[cosmetic.player.getUUID().toString().replace(/-/g, "")] = undefined
+ return false
+ }
+ return true
+ })
+ }
+
+ shouldPlayerHaveCosmetic(player, cosmetic) {
+ if (this.getPlayerCosmeticSettings(player, cosmetic)) {
+ if (!this.getPlayerCosmeticSettings(player, cosmetic).enabled) return false
+ return true
+ }
+ return false
+ }
+ getPlayerCosmeticSettings(player, cosmetic) {
+ return this.cosmeticsData[player.getUUID().toString().replace(/-/g, "")]?.[cosmetic]
+ }
+
+ filterUnloadedCosmetics(tick = false) {
+ this.loadedCosmetics = this.loadedCosmetics.filter(cosmetic => {
+ if (tick) cosmetic.onTick()
+ if (cosmetic.player.getUUID().toString().replace(/-/g, "") === Player.getUUID().toString().replace(/-/g, "")) return true
+ if (cosmetic.player.getPlayer()[f.isDead]) { //filter out players that are no longer loaded
+ this.uuidToCosmetic[cosmetic.id][cosmetic.player.getUUID().toString().replace(/-/g, "")] = undefined
+
+ this.uuidToCosmeticDirect[cosmetic.player.getUUID().toString().replace(/-/g, "")] = undefined
+ return false
+ }
+ return true
+ })
+ }
+
+ tick() {
+ for (let cosmetic of this.loadedCosmetics) {
+ cosmetic.onTick()
+ }
+ }
+
+ restoreEssentialCosmetics() {
+ this.hiddenEssentialCosmetics.forEach(cosmetic => {
+ setField(cosmetic, "showModel", true)
+ })
+ this.hiddenEssentialCosmetics = []
+ }
+
+ initVariables() {
+ this.loadedCosmetics = undefined
+ this.uuidToCosmetic = undefined
+ this.uuidToCosmeticDirect = {}
+ this.playerHasACosmeticA = undefined
+ this.cosmeticsData = undefined
+ this.hiddenEssentialCosmetics = undefined
+ this.hiddenEssentialCosmetics = undefined
+ this.cosmeticsList = undefined
+ }
+
+ onDisable() {
+
+ if (this.postRenderEntityTrigger) {
+ this.postRenderEntityTrigger.unregister()
+ this.postRenderEntityTrigger = undefined
+ }
+
+ this.restoreEssentialCosmetics()
+
+ this.initVariables()
+ }
+}
+
+let instance = new Cosmetics()
+
+module.exports = {
+ class: instance
+}
+
+function setField(e, field, value) {
+
+ let field2 = e.class.getDeclaredField(field);
+
+ field2.setAccessible(true)
+
+ return field2.set(e, value)
+} \ No newline at end of file
diff --git a/src/features/cosmetics/metadata.json b/src/features/cosmetics/metadata.json
new file mode 100644
index 0000000..f198cff
--- /dev/null
+++ b/src/features/cosmetics/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Cosmetics",
+ "description": "Cosmetics settings",
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "isHidden": "hiddenRequirement.js",
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/dataLoader/index.js b/src/features/dataLoader/index.js
new file mode 100644
index 0000000..eccafb4
--- /dev/null
+++ b/src/features/dataLoader/index.js
@@ -0,0 +1,280 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import socketConnection from "../../socketConnection";
+import { fetch } from "../../utils/networkUtils";
+
+class DataLoader extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.stats = {}
+
+ this.area = undefined
+ this.areaFine = undefined
+
+ this.isInSkyblock = false
+
+ this.dungeonPercentCleared = 0
+
+ this.registerStep(true, 2, this.step)
+
+ this.registerStep(false, 170, this.loadApiStepThing)
+ this.registerStep(false, 60 * 5, this.step_5min)
+
+ this.registerEvent("worldLoad", this.worldLoad)
+
+ this.api_loaded_event = this.createCustomEvent("apiLoad")
+
+ this.checkingPing = false;
+ this.lastPingCheck = 0;
+ this.lastPings = [undefined, undefined, undefined];
+ this.ping = 0;
+ this.pingI = 0;
+
+ this.registerChat("&b&bYou are currently connected to server &6${*}&r", (e) => {
+ if (this.checkingPing) {
+ this.lastPings[this.pingI % 3] = Date.now() - this.lastPingCheck;
+ cancel(e);
+ this.checkingPing = false;
+
+ if (this.lastPings.includes(undefined)) {
+ this.ping = this.lastPings[this.pingI % 3];
+ } else {
+ this.ping = [...this.lastPings].sort((a, b) => a - b)[1];
+ }
+ this.pingI++;
+ }
+ });
+
+ this.lastServer = undefined
+ this.lastSentServer = 0
+
+ this.currentMayorPerks = new Set()
+
+ this.loadedApiDatas = {}
+
+ this.partyMembers = new Set()
+ this.partyMembers.add(Player.getName())
+
+ this.lastApiData = {
+ "skyblock": undefined,
+ "player": undefined,
+ "skyblock_raw": undefined, //the _raw is loaded from hypixel api instead of soopy api
+ "player_raw": undefined
+ }
+
+ this.worldLoaded = true
+
+ this.loadApi()
+
+ this.step_5min()
+
+ this.firstLoaded = false;
+
+ ["You are not currently in a party.", "You have been kicked from the party by ${*}", "You left the party.", "The party was disbanded because all invites expired and the party was empty", "${*} &r&ehas disbanded the party!&r"].forEach(m => this.registerChat(m, () => {
+ this.partyMembers.clear()
+ this.partyMembers.add(Player.getName())
+ }));
+
+ ["${mem} &r&ejoined the party.&r", "${mem} &r&einvited &r${*} &r&eto the party! They have &r&c60 &r&eseconds to accept.&r", "&dDungeon Finder &r&f> &r${mem} &r&ejoined the dungeon group! (&r&b${*}&r&e)&r"].forEach(m => this.registerChat(m, (mem) => {
+ this.partyMembers.add(ChatLib.removeFormatting(mem.trim().split(" ").pop().trim()))
+ }));
+ ["${mem} &r&ehas been removed from the party.&r", "${mem} &r&ehas left the party.&r", "${mem} &r&ewas removed from your party because they disconnected&r", "Kicked ${mem} because they were offline."].forEach(m => this.registerChat(m, (mem) => {
+ this.partyMembers.delete(ChatLib.removeFormatting(mem.trim().split(" ").pop().trim()))
+ }))
+ this.registerChat("&eYou have joined &r${mem}'s &r&eparty!&r", (mem) => {
+ this.partyMembers.clear()
+ this.partyMembers.add(Player.getName())
+ this.partyMembers.add(ChatLib.removeFormatting(p = mem.trim().split(" ").pop().trim()))
+ })
+ this.registerChat("&eYou have joined &r${mem}' &r&eparty!&r", (mem) => {
+ this.partyMembers.clear()
+ this.partyMembers.add(Player.getName())
+ this.partyMembers.add(ChatLib.removeFormatting(mem).trim())
+ })
+ this.registerChat("&eYou'll be partying with: ${mem}", (mem) => {
+ mem.split(",").forEach(p => {
+ this.partyMembers.add(ChatLib.removeFormatting(p.trim().split(" ").pop().trim()))
+ })
+ })
+ this.registerChat("&eParty ${type}: ${mem}", (type, mem) => {
+ if (type.toLowerCase().includes("leader")) this.partyMembers.clear()
+ ChatLib.removeFormatting(mem).split("●").forEach(p => {
+ if (!p.trim()) return
+ this.partyMembers.add(p.trim().split(" ").pop().trim())
+ })
+ })
+ this.registerCommand("pmembdebug", () => {
+ ChatLib.chat([...this.partyMembers].join(" | "))
+ })
+ }
+
+ getPing() {
+ if (Date.now() - this.lastPingCheck > 60000 * 30 || (Date.now() - this.lastPingCheck > 60000 && this.lastPings.includes(undefined) && this.bloodX !== -1)) {
+ this.lastPingCheck = Date.now();
+ ChatLib.command("whereami");
+ this.checkingPing = true;
+ }
+ return this.ping || 0
+ }
+
+ async step_5min() {
+ let data = await fetch("http://soopy.dev/api/v2/mayor").json()
+
+ if (!data.success) return
+ this.mayorData = data.data
+ this.currentMayorPerks = new Set(data.data.mayor.perks.map(a => a.name))
+ }
+
+ worldLoad() {
+ this.area = undefined
+ this.areaFine = undefined
+ this.dungeonFloor = undefined
+ this.loadApiData("skyblock", false)
+ }
+
+ loadApiStepThing() {
+ this.loadApiData("skyblock", false)
+ }
+ async loadApi() {
+ let data = await fetch("http://soopy.dev/api/v2/player_skyblock/" + Player.getUUID().replace(/-/g, "")).json()
+
+ if (!data.success) return
+
+ this.api_loaded_event.trigger(data, "skyblock", true, true)
+ this.lastApiData.skyblock = data
+ }
+
+ loadApiData(type, soopyServer) {
+ if (this.FeatureManager.features["globalSettings"] === undefined || this.FeatureManager.features["globalSettings"].class.apiKeySetting === undefined) {
+ return
+ }
+ let key = this.FeatureManager.features["globalSettings"].class.apiKeySetting.getValue()
+ if (!key) return
+
+ if (this.loadedApiDatas[type] !== undefined) {
+ if (Date.now() - this.loadedApiDatas[type] < 5000) return
+ }
+
+ this.loadedApiDatas[type] = Date.now()
+
+ if (soopyServer) {
+
+ } else {
+ if (type === "skyblock") {
+ fetch("https://api.hypixel.net/skyblock/profiles?key=" + key + "&uuid=" + Player.getUUID().replace(/-/g, "")).json().then(data => {
+ if (!data.success) return
+
+ this.api_loaded_event.trigger(data, "skyblock", false, true)
+ this.lastApiData.skyblock_raw = data
+ })
+ }
+ }
+ }
+
+ step() { //2fps
+ if (!this.firstLoaded) {
+ if (!(this.FeatureManager.features["globalSettings"] === undefined || this.FeatureManager.features["globalSettings"].class.apiKeySetting === undefined)) {
+ this.loadApiData("skyblock", false)
+ this.firstLoaded = true
+ }
+ }
+ this.isInSkyblock = Scoreboard.getTitle()?.removeFormatting().includes("SKYBLOCK")
+
+ if (!this.isInSkyblock) {
+ this.stats = {}
+ this.isInDungeon = false
+ this.dungeonFloor = undefined
+ return
+ }
+
+ this.stats["Area"] = undefined
+ this.stats["Dungeon"] = undefined
+
+ if (World.isLoaded() && TabList.getNames()) {
+ TabList.getNames().forEach(n => {
+ n = ChatLib.removeFormatting(n)
+ if (n.includes(": ")) {
+ if (n.includes('Secrets Found')) {
+ if (n.includes('%')) {
+ this.stats["Secrets Found%"] = n.split(": ")[1]
+ } else {
+ this.stats["Secrets Found"] = n.split(": ")[1]
+ }
+ } else {
+ this.stats[n.split(": ")[0].trim()] = n.split(": ")[1].trim()
+ }
+ }
+ })
+ }
+
+ if (this.stats["Dungeon"]) {
+ this.stats["Area"] = this.stats["Dungeon"]
+ this.isInDungeon = true
+ } else {
+ this.isInDungeon = false
+ }
+
+ this.slayerXpToSpawn = undefined
+ Scoreboard.getLines().forEach(line => {
+ let name = ChatLib.removeFormatting(line.getName()).replace(/[^A-z0-9 \:\(\)\.]/g, "")
+ if (this.isInDungeon) {
+ if (name.includes("The Catacombs (")) {
+ this.dungeonFloor = name.split("(")[1].split(")")[0].toUpperCase()
+ }
+ }
+ if (ChatLib.removeFormatting(line).startsWith(" ⏣ ")) {
+ this.areaFine = ChatLib.removeFormatting(line).split(" ⏣ ")[1].replace(/[^A-z0-9 \:\(\)\.\-]/g, "")
+ }
+ if (name.startsWith("Purse: ")) {
+ this.purse = parseInt(name.split("Purse: ")[1].split(" ")[0])
+ }
+ if (name.startsWith("Bits: ")) {
+ this.bits = parseInt(name.split("Bits: ")[1].split(" ")[0])
+ }
+ if (name.startsWith("Cleared: ")) {
+ this.dungeonPercentCleared = parseInt(name.split(" ")[1]) / 100
+ }
+
+ if (name.endsWith("Combat XP")) {
+ this.slayerXpToSpawn = ChatLib.removeFormatting(name).split("(")[1].split(")")[0].split("/").map(parseInt)
+ }
+ })
+
+ this.area = this.stats["Area"]
+
+
+ if (this.lastServer !== this.stats.Server || Date.now() - this.lastSentServer > 60000 * 5) {
+ this.lastServer = this.stats.Server;
+ this.lastSentServer = Date.now()
+
+ socketConnection.setServer(this.stats.Server, this.area, this.areaFine);
+ }
+ }
+
+ initVariables() {
+ this.stats = undefined
+ this.isInDungeon = false
+
+ this.dungeonFloor = undefined
+ this.area = undefined
+ this.areaFine = undefined
+ this.bits = undefined
+ this.purse = undefined
+ this.lastApiData = undefined
+ this.isInSkyblock = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+module.exports = {
+ class: new DataLoader()
+} \ No newline at end of file
diff --git a/src/features/dataLoader/metadata.json b/src/features/dataLoader/metadata.json
new file mode 100644
index 0000000..4953f99
--- /dev/null
+++ b/src/features/dataLoader/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Data loader",
+ "description": "Loads data",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/dungeonMap/dungeonBossImages/f1.png b/src/features/dungeonMap/dungeonBossImages/f1.png
new file mode 100644
index 0000000..5bc2d08
--- /dev/null
+++ b/src/features/dungeonMap/dungeonBossImages/f1.png
Binary files differ
diff --git a/src/features/dungeonMap/index.js b/src/features/dungeonMap/index.js
new file mode 100644
index 0000000..456dff3
--- /dev/null
+++ b/src/features/dungeonMap/index.js
@@ -0,0 +1,1186 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+
+const Color = Java.type("java.awt.Color")
+
+import Feature from "../../featureClass/class";
+import { f, m } from "../../../mappings/mappings";
+import renderLibs from "../../../guimanager/renderLibs";
+import ToggleSetting from "../settings/settingThings/toggle";
+import { drawBoxAtBlock } from "../../utils/renderUtils";
+import { SoopyGui, SoopyRenderEvent } from "../../../guimanager";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import ImageLocationSetting from "../settings/settingThings/imageLocation";
+import socketConnection from "../../socketConnection";
+import SoopyKeyPressEvent from "../../../guimanager/EventListener/SoopyKeyPressEvent";
+import SettingBase from "../settings/settingThings/settingBase";
+import { Box } from "../../utils/renderJavaUtils";
+const BufferedImage = Java.type("java.awt.image.BufferedImage")
+const AlphaComposite = Java.type("java.awt.AlphaComposite")
+
+class DungeonMap extends Feature {
+ constructor() {
+ super()
+ }
+
+ isInDungeon() {
+ if (!this.FeatureManager || !this.FeatureManager.features["dataLoader"]) return false
+ return this.FeatureManager.features["dataLoader"].class.isInDungeon
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.mapInfo = new SettingBase("NOTE: The more players in the party with this", "category enabled the more accurate the map will be.", undefined, "map_info", this)
+ this.renderMap = new ToggleSetting("Render Map", "Toggles Rendering the map on the hud", false, "dmap_render", this)
+ this.mapIcons = new ToggleSetting("Icons for puzzles on map", "Toggles Rendering the puzzle icons on map", true, "dmap_icons", this).requires(this.renderMap)
+ this.mapLocation = new ImageLocationSetting("Map Location", "Sets the location of the map on the hud", "dmap_location", this, [10, 10, 1], new Image(javax.imageio.ImageIO.read(new java.io.File("./config/ChatTriggers/modules/SoopyV2/features/dungeonMap/map.png"))), 100, 100).requires(this.renderMap)
+ this.mapBackground = new ToggleSetting("Map Background And Border", "Puts a grey background behind the map + Black border", true, "dmap_background", this)
+ this.showMapInBoss = new ToggleSetting("Keep showing the map in the dungeon boss room", "This will center the map when in boss to still be usefull", true, "dmap_enable_boss", this)
+ this.borderedHeads = new ToggleSetting("Add a black border around heads on map", "", false, "dmap_border_head", this)
+ this.roomsecrets = new ToggleSetting("Shows secretscompleted/total secrets in room", "(works best when all of party is using soopy map)", false, "roomsecrets", this)
+ this.brBox = new ToggleSetting("Box around doors in br", "In map category because it uses map to find location (no esp)", true, "dmap_door", this)
+ this.spiritLeapOverlay = new ToggleSetting("Spirit leap overlay", "Cool overlay for the spirit leap menu", true, "spirit_leap_overlay", this)
+ // this.spiritLeapOverlay = new ToggleSetting("Spirit leap overlay", "Cool overlay for the spirit leap menu", true, "spirit_leap_overlay", this).requires(this.spiritLeapOverlay)
+
+ this.MAP_QUALITY_SCALE = 1
+ this.IMAGE_SIZE = 128 * this.MAP_QUALITY_SCALE
+
+ this.defaultPlayerImage = renderLibs.getImage("https://crafatar.com/avatars/dc8c39647b294e03ae9ed13ebd65dd29?size=8", true)
+ this.mapDataPlayers = {}
+ this.offset = []
+ this.people = []
+ this.mapScale = 1
+ this.puzzles = {}
+ this.puzzlesTab = []
+ this.roomWidth = 1
+ this.nameToUUID = {}
+ this.deadPlayers = new Set()
+ this.newPuzzlesTab = []
+ this.mortLocationOnMap = undefined
+ this.brBoxLoc = undefined
+ this.keys = 0
+
+ this.roomDataStuff = new Map()
+ this.roomDataStuffRender = new Map()
+ // this.invMapImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB)
+ // this.renderImage = new BufferedImage(this.IMAGE_SIZE, this.IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB)
+ this.mapData = undefined
+ this.idToPlayer = {}
+
+ this.barrier_block_item = new Item("minecraft:barrier")
+ this.puzzleItems = {
+ "Water Board": new Item("minecraft:water_bucket"),
+ "Higher Or Lower": new Item("minecraft:blaze_powder"),
+ "Quiz": new Item("minecraft:book"),
+ "Three Weirdos": new Item("minecraft:chest"),
+ "Tic Tac Toe": new Item("minecraft:shears"),
+ "Teleport Maze": new Item("minecraft:end_portal_frame"),
+ "Ice Fill": new Item("minecraft:ice"),
+ "Creeper Beams": new Item("minecraft:sea_lantern"),
+ "Bomb Defuse": new Item("minecraft:tnt"),
+ "Boulder": new Item("minecraft:planks"),
+ "Ice Path": new Item("minecraft:mob_spawner")
+ }
+
+ this.dungeonBossImages = {
+ // "F1": [
+ // {
+ // image: new Image(javax.imageio.ImageIO.read(new java.io.File("./config/ChatTriggers/modules/SoopyV2/features/dungeonMap/dungeonBossImages/f1.png"))),
+ // bounds: [[-65, 70, -3], [-19, 90, 45]],
+ // widthInWorld: 46,
+ // heightInWorld: 48,
+ // topLeftLocation: [-64, -2]
+ // }
+ // ]
+ }
+
+ this.currDungeonBossImage = undefined
+ this.dungeonBrBoxElm = new Box([0, 0, 0], [3, 4, 3], 1, 0, 0, 1, 3, false)
+
+ this.bloodOpened = false
+ this.registerChat("&r&cThe &r&c&lBLOOD DOOR&r&c has been opened!&r", () => {
+ this.bloodOpened = true
+ this.keys--
+ this.dungeonBrBoxElm.setRGBA(1, 0, 0, 1)
+ this.dungeonBrBoxElm.stopRender()
+ })
+
+ this.registerChat("${*} &r&ehas obtained &r&a&r&${*} Key&r&e!&r", () => {
+ this.keys++
+ this.dungeonBrBoxElm.setRGBA(0, 1, 0, 1)
+ })
+ this.registerChat("&r&eA &r&a&r&${*} Key&r&e was picked up!&r", () => {
+ this.keys++
+ this.dungeonBrBoxElm.setRGBA(0, 1, 0, 1)
+ })
+
+ this.lastDoorOpener = undefined
+ this.registerChat("&r&a${player}&r&a opened a &r&8&lWITHER &r&adoor!&r", (player) => {
+ this.lastDoorOpener = ChatLib.removeFormatting(player)
+ this.keys--
+ this.dungeonBrBoxElm.setRGBA(1, 0, 0, 1)
+ })
+
+ this.spiritLeapOverlayGui = new SpiritLeapOverlay(this)
+
+ // this.registerEvent("tick", this.tick)
+ this.registerStep(true, 3, this.step)
+ this.registerStep(true, 10, () => {
+ this.spiritLeapOverlayGui.tick()
+ }).registeredWhen(() => this.isInDungeon())
+ this.registerStep(false, 5, this.step5s).registeredWhen(() => this.isInDungeon())
+ this.registerEvent("renderOverlay", this.renderOverlay).registeredWhen(() => this.isInDungeon())
+ this.registerEvent("worldLoad", this.worldLoad)
+ this.renderingPlayerList = false
+ this.registerEvent("renderPlayerList", () => {
+ this.renderingPlayerList = true
+ })
+ this.registerEvent("renderDebug", () => {
+ this.renderingPlayerList = true
+ })
+
+ this.registerEvent("guiOpened", (event) => {
+ if (this.spiritLeapOverlay.getValue()) this.spiritLeapOverlayGui.guiOpened.call(this.spiritLeapOverlayGui, event)
+ })
+
+ this.boringMap = false
+ this.registerChat("&r&r&r ${*}&r&cThe Catacombs &r&8- &r&eFloor ${*}&r", () => {
+ this.boringMap = true
+ })
+ this.registerChat("&r&r&r ${*}&r&cMaster Mode Catacombs &r&8- &r&eFloor ${*}&r", () => {
+ this.boringMap = true
+ })
+ this.registerChat("&r&aDungeon starts in 1 second.&r", () => {
+ this.boringMap = false
+ })
+ this.registerChat("&r&aDungeon starts in 1 second. Get ready!&r", () => {
+ this.boringMap = false
+ })
+
+ this.running = true
+ this.registerEvent("gameUnload", () => {
+ this.running = false
+ })
+ this.registerChat("&r&c ☠ ${info} and became a ghost&r&7.&r", (info, e) => {
+ let player = ChatLib.removeFormatting(info.split(" ")[0])
+
+ this.deadPlayers.add(this.nameToUUID[player.toLowerCase()])
+ });
+ this.registerChat("&r&a ❣ &r${info} was revived${*}!&r", (info, e) => {
+ let player = ChatLib.removeFormatting(info.split(" ")[0])
+
+ this.deadPlayers.delete(this.nameToUUID[player.toLowerCase()])
+ });
+
+ this.registerStep(true, 3, () => {
+ if (!this.isInDungeon()) return
+
+ this.updateMapImage()
+ }).registeredWhen(() => this.isInDungeon())
+
+ this.registerChat("&r&r&r &r&cThe Catacombs &r&8- &r&eFloor ${*} Stats&r", () => {
+ this.puzzles = {}
+ })
+
+ let lastXY = ""
+ let registerActionBar = this.registerCustom("actionbar", (curr, max) => {
+ let loc = this.getRoomXYWorld()
+
+ if (lastXY !== loc.join(",")) {
+ lastXY = loc.join(",")
+ return
+ }
+ // if (curr === max) curr = "&a" + curr
+ if (!this.roomDataStuff.get(loc.join(",")) || this.roomDataStuff.get(loc.join(","))[0] !== curr + " " + max) {
+ this.roomDataStuff.set(loc.join(","), [curr + " " + max, this.getCurrentRoomId()])
+
+ socketConnection.sendDungeonData2(this.people, [loc.join(","), curr + " " + max, this.getCurrentRoomId()])
+ }
+ })
+ registerActionBar.trigger.setCriteria('&7${curr}/${max} Secrets').setParameter('contains');
+ }
+
+ getCurrentRoomId() {
+ let id = Scoreboard.getLineByIndex(Scoreboard.getLines().length - 1).getName().trim().split(" ").pop()
+
+ return id
+ }
+
+ updateDungeonMapData2(data) {
+ // console.log("Recieved: " + JSON.stringify(data, undefined, 2))
+ this.roomDataStuff.set(data[0], [data[1], data[2]])
+ }
+ worldLoad() {
+ this.dungeonBrBoxElm.stopRender()
+ this.mortLocation = undefined
+ this.mapDataPlayers = {}
+ this.offset = []
+ this.mapScale = 1
+ this.puzzles = {}
+ this.puzzlesTab = []
+ this.brBoxLoc = undefined
+ this.mortLocationOnMap = undefined
+ this.bloodOpened = false
+ this.keys = 0
+ this.idToPlayer = {}
+ this.roomDataStuff.clear()
+ }
+
+ renderOverlay() {
+ if (!this.renderingPlayerList && this.isInDungeon() && this.renderMap.getValue() && !this.spiritLeapOverlayGui.soopyGui.ctGui.isOpen()) {
+ this.drawMap(this.mapLocation.getValue()[0], this.mapLocation.getValue()[1], 100 * this.mapLocation.getValue()[2], 0.5 * this.mapLocation.getValue()[2])
+ }
+ this.renderingPlayerList = false
+ }
+
+ drawMap(x, y, size, scale) {
+ if (this.mapData) {
+ if (this.FeatureManager.features["dataLoader"].class.stats.Time === "Soon!" && Player.getInventory().getStackInSlot(8).getID() !== 358) return
+ if (this.boringMap) {
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0, 100), x, y, size, size)
+
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x, y, size, 2)
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x, y, 2, size)
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x + size - 2, y, 2, size)
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x, y + size - 2, size, 2)
+ if (this.currDungeonBossImage) {
+ this.currDungeonBossImage.image.draw(x, y, size, size)
+ } else {
+ GlStateManager.func_179094_E(); //GlStateManager.push()
+ Renderer.translate(x, y, 1)
+ GlStateManager.func_179152_a(size / 128, size / 128, 1); //GlStateManager.scale()
+ GlStateManager.func_179131_c(1.0, 1.0, 1.0, 1.0); // GlStateManager.color()
+ Client.getMinecraft().field_71460_t.func_147701_i().func_148250_a(this.mapData, true);
+ GlStateManager.func_179121_F(); //GlStateManager.pop()
+ }
+
+ return
+ }
+
+ World.getAllPlayers().forEach(player => {
+ if (player.getPing() === -1) return
+ if (!this.people.includes(player.getName())) return
+ this.mapDataPlayers[player.getUUID().toString()] = {
+ x: player.getX(),
+ y: player.getZ(),
+ rot: player.getYaw() + 180,
+ username: player.getName(),
+ uuid: player.getUUID().toString()
+ }
+ })
+ this.mapDataPlayers[Player.getUUID().toString()] = {
+ x: Player.getX(),
+ y: Player.getZ(),
+ rot: Player.getYaw() + 180,
+ username: Player.getName(),
+ uuid: Player.getUUID().toString()
+ }
+
+ let uuid = Player.getUUID().toString()
+ let renderX
+ let renderY
+ let xOff = 0
+ let yOff = 0
+ let disableMap = false
+ if (this.mapDataPlayers[uuid]) {
+
+ if (this.currDungeonBossImage) {
+ renderX = (this.mapDataPlayers[uuid].x - this.currDungeonBossImage.topLeftLocation[0]) / this.currDungeonBossImage.widthInWorld * size
+ renderY = (this.mapDataPlayers[uuid].y - this.currDungeonBossImage.topLeftLocation[1]) / this.currDungeonBossImage.heightInWorld * size
+ } else {
+ renderX = this.mapDataPlayers[uuid].x / this.mapScale / 128 * size + this.offset[0] / 128 * size//*16/this.roomWidth
+ renderY = this.mapDataPlayers[uuid].y / this.mapScale / 128 * size + this.offset[1] / 128 * size//*16/this.roomWidth
+ }
+
+ if (renderX < 0 || renderX > size
+ || renderY < 0 || renderY > size) {
+ xOff = size / 2 - renderX
+ yOff = size / 2 - renderY
+
+ if (!this.showMapInBoss.getValue()) {
+ disableMap = true
+ }
+ }
+ }
+ if (disableMap) return
+
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0, 100), x, y, size, size)
+ renderLibs.scizzor(x + 2, y + 2, size - 4, size - 4)
+
+ if (this.currDungeonBossImage) {
+ this.currDungeonBossImage.image.draw(x + xOff, y + yOff, size, size)
+ } else {
+ GlStateManager.func_179094_E(); //GlStateManager.push()
+ Renderer.translate(x + xOff, y + yOff, 1)
+ GlStateManager.func_179152_a(size / 128, size / 128, 1); //GlStateManager.scale()
+ GlStateManager.func_179131_c(1.0, 1.0, 1.0, 1.0); // GlStateManager.color()
+ Client.getMinecraft().field_71460_t.func_147701_i().func_148250_a(this.mapData, true);
+ GlStateManager.func_179121_F(); //GlStateManager.pop()
+ }
+ try {
+ this.drawOtherMisc(x + xOff, y + yOff, size, scale)
+
+ this.drawPlayersLocations(x + xOff, y + yOff, size, scale)
+
+ this.drawOtherMisc2(x + xOff, y + yOff, size, scale)
+ } catch (e) {
+ console.error(e)
+ console.error(e.stack)
+ }
+ renderLibs.stopScizzor()
+
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x, y, size, 2)
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x, y, 2, size)
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x + size - 2, y, 2, size)
+ if (this.mapBackground.getValue()) Renderer.drawRect(Renderer.color(0, 0, 0), x, y + size - 2, size, 2)
+
+ }
+ }
+
+ drawOtherMisc(x2, y2, size, scale) {
+ if (this.currDungeonBossImage) return
+ if (this.mapIcons.getValue()) {
+ Object.keys(this.puzzles).forEach(loc => {
+ if (!this.puzzles[loc]) return
+ if (this.puzzles[loc][1]) return
+ let y = (loc % 128) / 128 * 100
+ let x = (Math.floor(loc / 128)) / 128 * 100
+
+ let item = this.puzzleItems[this.puzzles[loc][0]] || this.barrier_block_item
+
+ // lines.forEach((l, i)=>{
+ // renderLibs.drawStringCentered("&0&l" + l, x*scale*2+x2-l.length/2*scale*2, y*scale*2+y2-this.roomWidth/3*scale*2+this.roomWidth/3*scale*2+i*6*scale*2-((lines.length-1)*3+4)*scale*2, scale*2)
+ // })
+
+ item.draw(x * scale * 2 + x2 - this.roomWidth / 4 * scale * 2, y * scale * 2 + y2 - this.roomWidth / 4 * scale * 2, 1.5 * scale)
+ })
+ }
+ }
+
+ drawOtherMisc2(x2, y2, size, scale) {
+ if (this.currDungeonBossImage) return
+ if (this.roomsecrets.getValue()) {
+ for (let ent of this.roomDataStuffRender.entries()) {
+ let [loc, [secrets, roomid, color]] = ent
+ let [x, y] = loc.split(",")
+
+ let renderText = color + secrets
+
+ let val2 = ChatLib.removeFormatting(renderText)
+
+ let renderX = (parseInt(x) + 16) / this.mapScale / 128 * size + this.offset[0] / 128 * size
+ let renderY = (parseInt(y) + 16) / this.mapScale / 128 * size + this.offset[1] / 128 * size
+
+ Renderer.translate(0, 0, 1000)
+ renderLibs.drawStringCentered("§0" + val2, x2 + renderX + scale * 1.25, y2 + renderY - 2 * scale * 1.25, scale * 1.25)
+ Renderer.translate(0, 0, 1000)
+ renderLibs.drawStringCentered("§0" + val2, x2 + renderX - scale * 1.25, y2 + renderY - 2 * scale * 1.25, scale * 1.25)
+ Renderer.translate(0, 0, 1000)
+ renderLibs.drawStringCentered("§0" + val2, x2 + renderX, y2 + renderY + scale * 1.25 - 2 * scale * 1.25, scale * 1.25)
+ Renderer.translate(0, 0, 1000)
+ renderLibs.drawStringCentered("§0" + val2, x2 + renderX, y2 + renderY - scale * 1.25 - 2 * scale * 1.25, scale * 1.25)
+ Renderer.translate(0, 0, 1000)
+ renderLibs.drawStringCentered("§f" + renderText, x2 + renderX, y2 + renderY - 2 * scale * 1.25, scale * 1.25)
+ }
+ }
+ }
+
+ drawPlayersLocations(x, y, size, scale) {
+
+ let uuidToPlayer = {}
+ World.getAllPlayers().forEach(player => {
+ if (player.getPing() === -1) return
+ if (!this.people.includes(player.getName())) return
+ uuidToPlayer[player.getUUID().toString()] = player
+ })
+
+ Object.keys(this.mapDataPlayers).forEach((uuid) => {
+ if (this.deadPlayers.has(uuid)) return
+ if (uuid === Player.getUUID().toString()) return
+ let renderX
+ let renderY
+
+ if (this.currDungeonBossImage) {
+ renderX = (this.mapDataPlayers[uuid].x - this.currDungeonBossImage.topLeftLocation[0]) / this.currDungeonBossImage.widthInWorld * size
+ renderY = (this.mapDataPlayers[uuid].y - this.currDungeonBossImage.topLeftLocation[1]) / this.currDungeonBossImage.heightInWorld * size
+ } else {
+ renderX = this.mapDataPlayers[uuid].x / this.mapScale / 128 * size + this.offset[0] / 128 * size//*16/this.roomWidth
+ renderY = this.mapDataPlayers[uuid].y / this.mapScale / 128 * size + this.offset[1] / 128 * size//*16/this.roomWidth
+ }
+
+ if (this.borderedHeads.getValue()) {
+ Renderer.translate(renderX + x, renderY + y, 1000)
+ Renderer.scale(scale * 1.5, scale * 1.5)
+ Renderer.rotate(this.mapDataPlayers[uuid].rot)
+ Renderer.drawRect(Renderer.color(0, 0, 0), -6, -6, 12, 12)
+ }
+ Renderer.translate(renderX + x, renderY + y, 1000)
+ Renderer.scale(scale * 1.5, scale * 1.5)
+ Renderer.rotate(this.mapDataPlayers[uuid].rot)
+ this.getImageForPlayer(uuid).draw(-5, -5, 10, 10)
+ })
+
+ let uuid = Player.getUUID().toString()
+ if (!this.mapDataPlayers[uuid]) return
+ let renderX
+ let renderY
+
+ if (this.currDungeonBossImage) {
+ renderX = (this.mapDataPlayers[uuid].x - this.currDungeonBossImage.topLeftLocation[0]) / this.currDungeonBossImage.widthInWorld * size
+ renderY = (this.mapDataPlayers[uuid].y - this.currDungeonBossImage.topLeftLocation[1]) / this.currDungeonBossImage.heightInWorld * size
+ } else {
+ renderX = this.mapDataPlayers[uuid].x / this.mapScale / 128 * size + this.offset[0] / 128 * size//*16/this.roomWidth
+ renderY = this.mapDataPlayers[uuid].y / this.mapScale / 128 * size + this.offset[1] / 128 * size//*16/this.roomWidth
+ }
+
+
+ if (this.borderedHeads.getValue()) {
+ Renderer.translate(renderX + x, renderY + y, 1000)
+ Renderer.scale(scale * 1.5, scale * 1.5)
+ Renderer.rotate(this.mapDataPlayers[uuid].rot)
+ Renderer.drawRect(Renderer.color(0, 0, 0), -6, -6, 12, 12)
+ }
+ Renderer.translate(renderX + x, renderY + y, 1000)
+ Renderer.scale(scale * 1.5, scale * 1.5)
+ Renderer.rotate(this.mapDataPlayers[uuid].rot)
+ this.getImageForPlayer(uuid).draw(-5, -5, 10, 10)
+ }
+
+ getRoomXYWorld() {
+ let roomData = this.getRoomWorldData()
+ if (roomData.rotation === 4) {
+ return [roomData.x, roomData.y + 32]
+ }
+
+ return [roomData.x, roomData.y]
+ }
+
+ getRotation(x, y, width, height, roofY) {
+ let one = this.getTopBlockAt(x + width / 2 + 1, y + height / 2, roofY)
+ let two = this.getTopBlockAt(x + width / 2 - 1, y + height / 2, roofY)
+ let three = this.getTopBlockAt(x + width / 2, y + height / 2 + 1, roofY)
+ let four = this.getTopBlockAt(x + width / 2, y + height / 2 - 1, roofY)
+
+ if (one === 0 && three === 0) return 1
+ if (two === 0 && three === 0) return 2
+ if (one === 0 && four === 0) return 3
+ if (two === 0 && four === 0) return 4 //4 IS SO TOXIK HGOLY HEL I HATE L SHAPE ROOMS WHY DO THIS TO ME
+
+ return -1
+ }
+
+ getRoomWorldData() {
+ let x = Math.floor((Player.getX() + 8) / 32) * 32 - 8
+ let y = Math.floor((Player.getZ() + 8) / 32) * 32 - 8
+ let width = 30
+ let height = 30
+
+ let roofY = this.getRoofAt(x, y)
+
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y)).getBlockId() !== 0) {
+ x -= 32
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x, roofY, y - 1)).getBlockId() !== 0) {
+ y -= 32
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y)).getBlockId() !== 0) { //second iteration incase of L shape
+ x -= 32
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width + 1, roofY, y)).getBlockId() !== 0) {
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x, roofY, y + height + 1)).getBlockId() !== 0) {
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width, roofY, y + height + 1)).getBlockId() !== 0) { //second iteration incase of L shape
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width + 1, roofY, y + height)).getBlockId() !== 0) { //second iteration incase of L shape
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width, roofY, y - 1)).getBlockId() !== 0) {//second iteration incase of L shape
+ y -= 32
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y + height)).getBlockId() !== 0) { //third iteration incase of L shape
+ x -= 32
+ width += 32
+ }
+
+
+ return {
+ x,
+ y,
+ width,
+ height,
+ cx: x + width / 2,
+ cy: y + height / 2,
+ rotation: this.getRotation(x, y, width, height, roofY)
+ }
+ }
+
+ getRoofAt(x, z) {
+ let y = 255
+ while (y > 0 && World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId() === 0) y--
+
+ return y
+ }
+
+ getTopBlockAt(x, z, y) {
+ if (!y) y = this.getHeightAt(x, z)
+
+ return World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId()
+ }
+
+ step() {
+ if (!World.getWorld()) return
+ if (!this.isInDungeon()) return
+ if (Player.getContainer().getName().startsWith("Catacombs - Floor ")) {
+ this.nameToUUID = {}
+ World.getAllPlayers().forEach(p => {
+ this.nameToUUID[p.getName().toLowerCase()] = p.getUUID().toString()
+ })
+ let playerI = 0
+ for (let i = 0; i < 5; i++) {
+ let name = ChatLib.removeFormatting(Player.getContainer().getStackInSlot(3 + i)?.getName()?.split(" ")?.pop() || "")
+ if (this.nameToUUID[name.toLowerCase()] && this.nameToUUID[name.toLowerCase()] !== Player.getUUID().toString()) {
+ this.idToPlayer[playerI] = this.nameToUUID[name.toLowerCase()]
+ if (name) playerI++
+ }
+ }
+ this.idToPlayer[playerI] = Player.getUUID().toString()
+ playerI++
+ if (playerI !== World.getAllPlayers().filter(p => p.getPing() === 1).length) this.idToPlayer = {}
+ }
+ // console.log("asjbfoasbgp")
+ this.people = []
+ this.puzzlesTab = []
+ TabList.getNames().forEach(nameo => {
+
+
+ // Party (2) | Soopyboo32 (Mage XXXVI) | Ultimate: Ready | Revive Stones: 1 | | zZzJAKE ♲ (DEAD) | Ultimate: 00m 45s | Revive Stones: 0 | | | | | | | | | | | | | Player Stats | Downed: zZzJAKE | Time: 00m 47s | Revive: 01m 40s | | Deaths: (2) | Damage Dealt: 4.7M❤ | Healing Done: 718❤ | Milestone: ☠❸ | | Discoveries: (0) | Secrets Found: 0 | Crypts: 0 | | | | | | | | Dungeon Stats | Dungeon: Catacombs | Opened Rooms: 13 | Completed Rooms: 12 | Secrets Found: 0% | Time: 01m 51s | | Puzzles: (3) | ???: [✦] | ???: [✦] | ???: [✦] | | | | | | | | | | Account Info | Profile: Pomegranate | Pet Sitter: N/A | Bank: 57M/11M | Interest: 04h 19m 10s | | Skills: Combat 60: MAX | Speed: ✦457 | Strength: ❁859 | Crit Chance: ☣62 | Crit Damage: ☠1479 | Attack Speed: ⚔92 | | Event: Election Over! | Starts In: 2h 39m 10s | | Election: 0d 2h 39m 10s | Aatrox: |||||||||| (79%) | Marina: |||||||||| (7%) | Cole: |||||||||| (6%) | Soopyboo32
+ let line = ChatLib.removeFormatting(nameo).trim().replace("♲ ", "") //TODO: Remove bingo symbol
+ if (line.endsWith(")") && line.includes(" (") && line.split(" (").length === 2 && line.split(" (")[0].split(" ").length === 1 && line.split(" (")[1].length > 5) {
+ this.people.push(line.split(" ")[0])
+ }
+
+ name = ChatLib.removeFormatting(nameo).trim().split(" ")
+ let end = name.pop()
+ // console.log(end) Water Board: [✔]
+ if (end !== "[✦]" && end !== "[✔]") return
+ name = name.join(" ").trim().replace(":", "")
+ if (name.length > 1 && !name.includes("?")) {
+ this.puzzlesTab.push([name, end === "[✔]"])
+ }
+ // console.log(name)
+ })
+ let puzzlesTab2 = this.puzzlesTab.map(a => a)
+ // console.log(this.puzzlesTab.length)
+ // Object.keys(this.puzzles).forEach(key=>{
+ // let y = (key%128)
+ // let x = (Math.floor(key/128))
+
+ // if(x>100&&y>100){
+ // this.puzzles[key] = puzzlesTab2.shift()
+ // }
+ // })
+ Object.keys(this.puzzles).forEach(key => {
+ // let y = (key%128)
+ // let x = (Math.floor(key/128))
+
+ // if(x>100&&y>100){
+ // return
+ // }
+ this.puzzles[key] = puzzlesTab2.shift()
+ // console.log(key, this.puzzles[key], this.puzzlesTab.length)
+ })
+ }
+
+ updateMapImage() {
+ if (!this.mortLocation) {
+ try {
+ World.getAllEntities().forEach(entity => {
+ if (ChatLib.removeFormatting(entity.getName()) === ("Mort")) {
+ this.mortLocation = [
+ entity.getX(),
+ entity.getZ()
+ ]
+ }
+ })
+ } catch (e) { }
+ }
+
+ // let graphics = this.renderImage.getGraphics()
+
+ // graphics.setComposite(AlphaComposite.Clear);
+ // graphics.fillRect(0, 0, this.IMAGE_SIZE, this.IMAGE_SIZE)
+ // graphics.setComposite(AlphaComposite.SrcOver);
+
+ let mapData
+ try {
+ let item = Player.getInventory().getStackInSlot(8)
+ mapData = item.getItem()[m.getMapData](item.getItemStack(), World.getWorld()); // ItemStack.getItem().getMapData()
+ } catch (error) {
+ }
+ if (mapData && !this.boringMap) {
+ this.mapData = mapData
+
+
+ // console.log("has map data poggies")
+ let bytes = mapData[f.colors.MapData]
+
+ // let newArr = java.lang.reflect.Array.newInstance(java.lang.Integer.TYPE, bytes.length)
+ // for (let i = 0; i < bytes.length; i++) {
+ // // console.log(bytes[i]/4)
+
+ // if (bytes[i] !== 0) {
+ // let j = bytes[i] & 255
+ // newArr[i] = net.minecraft.block.material.MapColor[f.mapColorArray][j >> 2][m.getMapColor.MapColor](j & 3)
+ // // graphics.setColor(color)
+ // // graphics.fillRect(x * this.MAP_QUALITY_SCALE, y * this.MAP_QUALITY_SCALE, this.MAP_QUALITY_SCALE, this.MAP_QUALITY_SCALE)
+ // }
+ // // x++
+ // // if (x >= 128) {
+ // // x = 0
+ // // y++
+
+ // // if (y > 128) break
+ // // }
+
+ // // mapImage.getRGB()
+ // }
+ // this.renderImage.setRGB(0, 0, 128, 128, newArr, 0, 128)
+
+ // newImage.setRGB(0,0,128,128, ints, 0, 1)
+
+ // graphics. ()
+ //room size is 18
+ //4 inbetween
+
+ //finding room offsets
+ let brBoxTemp = undefined
+ let roomOffsets
+ let roomWidth1 = 0
+ let roomWidth2 = 0
+ let rx = 0
+ let ry = 0
+ for (let x = 0; x < 128; x += 5) {
+ for (let y = 0; y < 128; y += 5) {
+ if (bytes[x + y * 128] === 30
+ && bytes[x + 1 + y * 128] === 30
+ && bytes[x + 2 + y * 128] === 30
+ && bytes[x + 3 + y * 128] === 30
+ && bytes[x + 5 + y * 128] === 30
+ && bytes[x + 10 + y * 128] === 30) {
+ rx = x
+ ry = y
+ while (bytes[(rx - 1) + ry * 128] === 30) {
+ rx--
+ }
+ while (bytes[(rx) + (ry - 1) * 128] === 30) {
+ ry--
+ }
+ break;
+ }
+ }
+ if (rx) break;
+ }
+ let x = rx
+ while (bytes[x + ry * 128] === 30) {
+ x++
+ roomWidth2++
+ }
+ let y = ry
+ while (bytes[rx + y * 128] === 30) {
+ y++
+ roomWidth1++
+ }
+
+ let roomWidth = Math.floor(Math.max(roomWidth1, roomWidth2) * 5 / 4)
+
+ this.mapScale = 32 / roomWidth
+ let mortLocationOnMap
+ roomOffsets = [rx % roomWidth - 3, ry % roomWidth - 3]
+
+ let dir = roomWidth / 2 - 5 / this.mapScale
+
+ //top
+ for (let i = 0; i < roomWidth; i++) {
+ if (bytes[(i + rx - 3) + (ry - 3) * 128] !== 0) {
+ mortLocationOnMap = [rx - 2 + roomWidth / 2, ry - 2 + roomWidth / 2 - dir]
+ break
+ }
+ }
+ // if(mortLocationOnMap) break
+ //bottom
+ for (let i = 0; i < roomWidth; i++) {
+ if (bytes[(i + rx - 3) + (ry + roomWidth - 3) * 128] !== 0) {
+ mortLocationOnMap = [rx - 2 + roomWidth / 2, ry - 2 + roomWidth / 2 + dir]
+ break
+ }
+ }
+ //left
+ for (let i = 0; i < roomWidth; i++) {
+ if (bytes[(rx - 3) + (i + ry - 3) * 128] !== 0) {
+ mortLocationOnMap = [rx - 2 + roomWidth / 2 - dir, ry - 2 + roomWidth / 2]
+ break
+ }
+ }
+ //right
+ for (let i = 0; i < roomWidth; i++) {
+ if (bytes[(rx + roomWidth - 3) + (i + ry - 3) * 128] !== 0) {
+ mortLocationOnMap = [rx - 2 + roomWidth / 2 + dir, ry - 2 + roomWidth / 2]
+ }
+ }
+
+ if (mortLocationOnMap && this.mortLocation) {
+
+ for (let x = roomOffsets[0]; x < 128; x += roomWidth) {
+ for (let y = roomOffsets[1]; y < 128; y += roomWidth) {
+ let testLocs = [[x, y + roomWidth / 2, false], [x + roomWidth / 2, y, true]]
+ testLocs.forEach(([ux, uy, isX]) => {
+
+ // console.log(bytes[~~ux+~~uy*128])
+ if (bytes[~~ux + ~~uy * 128] === 119 || bytes[~~ux + ~~uy * 128] === 18) {
+
+ brBoxTemp = [
+ (ux - mortLocationOnMap[0]) / roomWidth * 32 + this.mortLocation[0],
+ (uy - mortLocationOnMap[1]) / roomWidth * 32 + this.mortLocation[1]
+ ]
+
+ if (isX) {
+ brBoxTemp[0] = Math.floor(brBoxTemp[0] / 32 + 0.5) * 32 + 16
+ brBoxTemp[1] = Math.floor(brBoxTemp[1] / 32 + 0.5) * 32
+ } else {
+ brBoxTemp[0] = Math.floor(brBoxTemp[0] / 32 + 0.5) * 32
+ brBoxTemp[1] = Math.floor(brBoxTemp[1] / 32 + 0.5) * 32 + 16
+ }
+
+ brBoxTemp = [
+ (~~brBoxTemp[0]) - 8.5,
+ (~~brBoxTemp[1]) - 8.5
+ ]
+ }
+ })
+
+ let [tx, ty] = [~~(x + roomWidth / 2), ~~(y + roomWidth / 2)]
+
+ if (bytes[tx + ty * 128] === 66) {
+
+ if (!this.puzzles[(tx) * 128 + ty] && Object.keys(this.puzzles).length < 10) {
+ this.puzzles[(tx) * 128 + ty] = ["Loading", false]
+ }
+ }
+ }
+ }
+ }
+
+ this.brBoxLoc = brBoxTemp
+
+
+ if (this.brBox.getValue() && !this.bloodOpened) this.dungeonBrBoxElm.startRender()
+
+ if (this.brBoxLoc) this.dungeonBrBoxElm.setLocationSize([this.brBoxLoc[0] - 1.5, 69, this.brBoxLoc[1] - 1.5], [3, 4, 3])
+
+ if (roomOffsets) {
+ // for(let x = 0;x<128;x++){
+ // for(let y = 0;y<128;y++){
+ // if((x-roomOffsets[0])%roomWidth===0 || (y-roomOffsets[1])%roomWidth===0){
+ // this.renderImage.setRGB(x*this.MAP_QUALITY_SCALE, y*this.MAP_QUALITY_SCALE, Renderer.color(0,0,0))
+ // }
+ // }
+ // }
+
+ // for(let x = roomOffsets[0];x<128;x+=roomWidth){
+ // for(let y = roomOffsets[1];y<128;y+=roomWidth){
+ // let testLocs = [[x, y+roomWidth/2],[x+roomWidth/2, y]]
+ // testLocs.forEach(([ux, uy])=>{
+ // ux = ~~ux
+ // uy = ~~uy
+
+ // try{
+ // this.renderImage.setRGB(ux*this.MAP_QUALITY_SCALE, uy*this.MAP_QUALITY_SCALE, Renderer.color(255,0,0))
+ // }catch(e){}
+ // })
+ // }
+
+ // }
+
+ if (mortLocationOnMap && this.mortLocation) {
+ this.offset = [mortLocationOnMap[0] - this.mortLocation[0] / this.mapScale, mortLocationOnMap[1] - this.mortLocation[1] / this.mapScale]
+ // this.renderImage.setRGB(mortLocationOnMap[0], mortLocationOnMap[1], Renderer.color(255, 0, 0))
+ // ChatLib.chat(roomWidth + " " + this.offset.join(", "))
+ }
+ }
+
+ // console.log(bytes[Math.floor(Player.getX()/this.mapScale+this.offset[0])+Math.floor(Player.getZ()/this.mapScale + this.offset[1])*128])
+ this.roomWidth = roomWidth
+
+ this.mortLocationOnMap = mortLocationOnMap
+
+ if (this.mortLocation && mortLocationOnMap && roomWidth) {
+ let deco = mapData[f.mapDecorations]
+ this.extraDeco = []
+ try {
+ let i = 0
+ deco.forEach((icon, vec4b) => {
+ let x = vec4b.func_176112_b()
+ let y = vec4b.func_176113_c()
+ let rot = vec4b.func_176111_d()
+ x = x / 2 + 64
+ y = y / 2 + 64
+ rot = rot * 360 / 16 + 180
+
+
+ x = (x - mortLocationOnMap[0]) / roomWidth * 32 + this.mortLocation[0]
+ y = (y - mortLocationOnMap[1]) / roomWidth * 32 + this.mortLocation[1]
+
+
+ //wtf is this
+
+ //vec4b.func_176110_a()
+
+
+ let closestP = undefined
+ let closestDistance = Infinity
+ Object.keys(this.mapDataPlayers).forEach((uuid) => {
+ if ((x - this.mapDataPlayers[uuid].x) ** 2 + (y - this.mapDataPlayers[uuid].y) ** 2 < closestDistance) {
+ closestDistance = (x - this.mapDataPlayers[uuid].x) ** 2 + (y - this.mapDataPlayers[uuid].y) ** 2
+ closestP = uuid
+ }
+ })
+ while (this.idToPlayer[i] && this.deadPlayers.has(this.idToPlayer[i])) i++
+ if (this.idToPlayer[i]) closestP = this.idToPlayer[i]
+ if (closestP && closestP !== Player.getUUID().toString()) {
+ // console.log(closestP, x, y)
+ this.mapDataPlayers[closestP].x = x
+ this.mapDataPlayers[closestP].y = y
+ this.mapDataPlayers[closestP].rot = rot
+ }
+ i++
+ });
+ } catch (e) { }
+ }
+
+
+ if (this.offset && this.offset.length === 2) {
+ this.roomDataStuffRender.clear()
+ for (let ent of this.roomDataStuff.entries()) {
+ let [loc] = ent
+ let [x, y] = loc.split(",")
+
+ let renderX = Math.round((parseInt(x) + 16) / this.mapScale + this.offset[0]) // / 128 * size
+ let renderY = Math.round((parseInt(y) + 16) / this.mapScale + this.offset[1])// / 128 * size
+ // console.log(renderX, renderY)
+ let isGreen = false
+ let color = "&8"
+ for (let i = 0; i < 10; i++) {
+ if (bytes[renderX + i + (renderY + i) * 128] === 30
+ || bytes[renderX + i + 1 + (renderY + i) * 128] === 30) {
+ isGreen = true
+ color = "&a"
+ }
+ if (bytes[renderX + i + (renderY + i) * 128] === 34
+ || bytes[renderX + i + 1 + (renderY + i) * 128] === 34) {
+ color = "&f"
+ }
+ }
+ if (isGreen) {
+ let total = ent[1][0].split(" ")[1]
+ let data = this.roomDataStuff.get(loc)
+ data[0] = total + " " + total
+ this.roomDataStuff.set(loc, data)
+ }
+
+ let setData = [...this.roomDataStuff.get(loc)]
+ setData.push(color)
+ this.roomDataStuffRender.set(loc, setData)
+ }
+ }
+ // if (!this.renderImage) return
+
+ // let newMapImageThing = new Image(this.renderImage)
+ this.currDungeonBossImage = undefined
+ } else {
+ //no map data, check to see if should render boss image
+
+ if (this.dungeonBossImages[this.FeatureManager.features["dataLoader"].class.dungeonFloor]) this.dungeonBossImages[this.FeatureManager.features["dataLoader"].class.dungeonFloor].forEach(data => {
+ if (data.bounds[0][0] <= Player.getX() && data.bounds[0][1] <= Player.getY() && data.bounds[0][2] <= Player.getZ() && data.bounds[1][0] >= Player.getX() && data.bounds[1][1] >= Player.getY() && data.bounds[1][2] >= Player.getZ()) {
+ this.currDungeonBossImage = data
+ }
+ })
+ }
+
+
+ // this.mapImage.setImage(this.renderImage)
+ }
+
+ step5s() {
+ if (!this.FeatureManager.features["dataLoader"].class.isInDungeon) return
+ if (this.people.length < 1) return
+
+ let data = []
+
+ World.getAllPlayers().forEach(player => {
+ if (player.getPing() === -1) return
+ if (!this.people.includes(player.getName())) return
+ data.push({
+ x: player.getX(),
+ y: player.getZ(),
+ rot: player.getYaw() + 180,
+ username: player.getName(),
+ uuid: player.getUUID().toString()
+ })
+ this.nameToUUID[player.getName().toLowerCase()] = player.getUUID().toString()
+ })
+ // console.log("Sending: " + JSON.stringify(this.people, undefined, 2)+JSON.stringify(data, undefined, 2))
+ socketConnection.sendDungeonData(this.people, data)
+ }
+ updateDungeonMapData(data) {
+ // console.log("Recieved: " + JSON.stringify(data, undefined, 2))
+ data.forEach(p => {
+ this.mapDataPlayers[p.uuid] = p
+ })
+ }
+
+ getImageForPlayer(uuid) {
+ let img = renderLibs.getImage("https://crafatar.com/avatars/" + uuid.replace(/-/g, "") + "?size=8&overlay")
+ if (!img) return this.defaultPlayerImage
+
+ return img
+ }
+
+ initVariables() {
+ this.mapImage = undefined
+ this.defaultPlayerImage = undefined
+ this.mortLocation = undefined
+ this.offset = undefined
+ this.puzzles = undefined
+ this.puzzlesTab = undefined
+ this.mapScale = undefined
+ this.newPuzzlesTab = undefined
+ this.renderImage = undefined
+ }
+
+ onDisable() {
+ this.dungeonBrBoxElm.stopRender()
+ this.initVariables()
+ this.running = false
+ this.roomDataStuff.clear()
+ }
+}
+
+module.exports = {
+ class: new DungeonMap()
+}
+
+const ContainerChest = Java.type("net.minecraft.inventory.ContainerChest")
+class SpiritLeapOverlay {
+ constructor(parent) {
+ this.parent = parent
+
+ this.soopyGui = new SoopyGui()
+
+ let renderThing = new soopyGuiMapRendererThing(this).setLocation(0, 0, 1, 1)
+ this.soopyGui.element.addChild(renderThing)
+
+ this.soopyGui.element.addEvent(new SoopyKeyPressEvent().setHandler((key, keyId) => {
+ if (keyId === 18) {
+ this.soopyGui.close()
+ }
+ }))
+
+ this.buttonsContainer = new SoopyGuiElement().setLocation(0.2, 0.2, 0.6, 0.3)
+ this.soopyGui.element.addChild(this.buttonsContainer)
+
+ this.items = {}
+
+ this.players = {}
+ }
+
+ guiOpened(event) {
+ if (event.gui && event.gui.field_147002_h instanceof ContainerChest) {
+ name = event.gui.field_147002_h.func_85151_d().func_145748_c_().func_150260_c()
+ if (name === "Spirit Leap") {
+ this.soopyGui.open()
+ }
+ }
+ }
+
+ tick() {
+ let itemsNew = {}
+
+ if (Player.getContainer()?.getName() === "Spirit Leap") {
+
+ this.players = {}
+ Scoreboard.getLines().forEach(line => {
+ let name = ChatLib.removeFormatting(line.getName()).replace(/[^A-z0-9 \:\(\)\.\[\]]/g, "")
+ if (name.startsWith("[M] ")) this.players[name.split(" ")[1]] = "M"
+ if (name.startsWith("[A] ")) this.players[name.split(" ")[1]] = "A"
+ if (name.startsWith("[B] ")) this.players[name.split(" ")[1]] = "B"
+ if (name.startsWith("[H] ")) this.players[name.split(" ")[1]] = "H"
+ if (name.startsWith("[T] ")) this.players[name.split(" ")[1]] = "T"
+ })
+
+ for (let i = 1; i < 9 * 3; i++) {
+ let item = Player.getContainer().getStackInSlot(i)
+ if (item && item.getID() !== 160) {
+ itemsNew[item.getName()] = i
+ }
+ }
+
+ if (JSON.stringify(this.items) !== JSON.stringify(itemsNew)) {
+ this.items = itemsNew
+ this.buttonsContainer.clearChildren()
+
+ getClass = (name) => {
+ let pClass = (this.players[name] || "?")
+ if (pClass === "?") {
+ Object.keys(this.players).forEach(n => {
+ if (name.startsWith(n)) {
+ pClass = this.players[n]
+ }
+ })
+ }
+ return pClass
+ }
+
+ Object.keys(this.items).sort((a, b) => {
+ return getClass(ChatLib.removeFormatting(a)).codePointAt(0) - getClass(ChatLib.removeFormatting(b)).codePointAt(0)
+ }).forEach((name, i) => {
+
+ let name2 = ChatLib.removeFormatting(name)
+ let pClass = getClass(name2)
+
+ let button = new ButtonWithArrow().setText((name2 === this.parent.lastDoorOpener ? "&4" : "&2") + "[" + pClass + "] " + name2).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Player.getContainer().click(itemsNew[name])
+ ChatLib.chat("Leaping to " + name)
+ })).setLocation((i % 2) * 0.5, Math.floor(i / 2) * 0.5, 0.5, 0.5)
+ button.text.setLocation(0.5, 0, 0.4, 1)
+ button.addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (!this.parent.nameToUUID[name2.toLowerCase()]) return
+ let img = this.parent.getImageForPlayer(this.parent.nameToUUID[name2.toLowerCase()])
+
+ if (!img) return
+
+ let x = button.location.getXExact()
+ let y = button.location.getYExact()
+ let h = button.location.getHeightExact()
+
+ img.draw(x + h / 5, y + h / 10, 8 * h / 10, 8 * h / 10)
+
+ }))
+ this.buttonsContainer.addChild(button)
+ })
+ }
+ }
+ }
+}
+
+class soopyGuiMapRendererThing extends SoopyGuiElement {
+ constructor(parent) {
+ super()
+
+ this.parentE = parent
+
+ this.addEvent(new SoopyRenderEvent().setHandler((mouseX, mouseY) => {
+ let size2 = Math.min(Renderer.screen.getWidth() / 2, Renderer.screen.getHeight() / 2)
+
+ let [x, y, size, scale] = [Renderer.screen.getWidth() / 2 - size2 / 2, 2 * Renderer.screen.getHeight() / 3 - size2 / 3, size2, size2 / this.parentE.parent.IMAGE_SIZE]
+
+ this.parentE.parent.drawMap(x, y, size, scale)
+
+ let closestPlayer
+ if (mouseY > y) {
+ closestPlayer = this.getClosestPlayerTo(x, y, size, scale, mouseX, mouseY)
+
+ if (closestPlayer) {
+ let renderX = closestPlayer.x / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+ let renderY = closestPlayer.y / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+
+ Renderer.translate(renderX + x + this.parentE.parent.offset[0] / 128 * size, renderY + y + this.parentE.parent.offset[1] / 128 * size, 1000)
+ Renderer.scale(scale * 3, scale * 3)
+ Renderer.rotate(closestPlayer.rot)
+ this.parentE.parent.getImageForPlayer(closestPlayer.uuid).draw(-5, -5, 10, 10)
+ }
+ }
+ Object.keys(this.parentE.parent.mapDataPlayers).forEach(uuid => {
+ let player = this.parentE.parent.mapDataPlayers[uuid]
+
+ let renderX = player.x / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+ let renderY = player.y / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+
+ Renderer.translate(renderX + x + this.parentE.parent.offset[0] / 128 * size, renderY + y + this.parentE.parent.offset[1] / 128 * size, 1000)
+ renderLibs.drawStringCenteredShadow((player.uuid === closestPlayer?.uuid ? "&c" : "&a") + "[" + (this.parentE.players[player.username] || "?") + "] " + player.username, 0, -7 * scale * 3, 2)
+ /*
+ {
+ x: player.getX(),
+ y: player.getZ(),
+ rot: player.getYaw()+180,
+ username: player.getName(),
+ uuid: player.getUUID().toString()
+ }
+ */
+ })
+
+ if (closestPlayer) {
+ let renderX = closestPlayer.x / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+ let renderY = closestPlayer.y / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+
+ Renderer.translate(renderX + x + this.parentE.parent.offset[0] / 128 * size, renderY + y + this.parentE.parent.offset[1] / 128 * size)
+ renderLibs.drawStringCenteredShadow("&c" + "[" + (this.parentE.players[closestPlayer.username] || "?") + "] " + closestPlayer.username, 0, -7 * scale * 3, 2)
+ }
+ }))
+ this.addEvent(new SoopyMouseClickEvent().setHandler((mouseX, mouseY) => {
+ let size2 = Math.min(Renderer.screen.getWidth() / 2, Renderer.screen.getHeight() / 2)
+
+ let [x, y, size, scale] = [Renderer.screen.getWidth() / 2 - size2 / 2, 2 * Renderer.screen.getHeight() / 3 - size2 / 3, size2, size2 / this.parentE.parent.IMAGE_SIZE]
+
+ if (mouseY < y) return
+
+ let closestPlayer = this.getClosestPlayerTo(x, y, size, scale, mouseX, mouseY)
+
+ if (closestPlayer) {
+ if (Player.getContainer()?.getName() === "Spirit Leap") {
+ for (let i = 1; i < 9 * 3; i++) {
+ let item = Player.getContainer().getStackInSlot(i)
+ if (item && item.getID() !== 160) {
+ if (ChatLib.removeFormatting(item.getName()) === closestPlayer.username) {
+ Player.getContainer().click(i)
+ ChatLib.chat("Leaping to " + closestPlayer.username)
+ }
+ }
+ }
+ }
+ }
+ }))
+ }
+
+ getClosestPlayerTo(x, y, size, scale, scanX, scanY) {
+
+ let closest = null
+ let closestD = Infinity
+ Object.keys(this.parentE.parent.mapDataPlayers).forEach((uuid) => {
+
+ if (uuid === Player.getUUID()) return
+
+ let renderX = this.parentE.parent.mapDataPlayers[uuid].x / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+ let renderY = this.parentE.parent.mapDataPlayers[uuid].y / this.parentE.parent.mapScale / 128 * size//*16/this.roomWidth
+
+ let distance = (renderX + x + this.parentE.parent.offset[0] / 128 * size - scanX) ** 2 + (renderY + y + this.parentE.parent.offset[1] / 128 * size - scanY) ** 2
+
+ if (distance < closestD) {
+ closestD = distance
+ closest = this.parentE.parent.mapDataPlayers[uuid]
+ }
+ })
+
+ return closest
+ }
+} \ No newline at end of file
diff --git a/src/features/dungeonMap/map.png b/src/features/dungeonMap/map.png
new file mode 100644
index 0000000..ff16aca
--- /dev/null
+++ b/src/features/dungeonMap/map.png
Binary files differ
diff --git a/src/features/dungeonMap/metadata.json b/src/features/dungeonMap/metadata.json
new file mode 100644
index 0000000..beef50e
--- /dev/null
+++ b/src/features/dungeonMap/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Dungeon Map",
+ "description": "Cool dungeon map and related features",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": false,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/dungeonMap2/DungeonMapData.js b/src/features/dungeonMap2/DungeonMapData.js
new file mode 100644
index 0000000..1b50c67
--- /dev/null
+++ b/src/features/dungeonMap2/DungeonMapData.js
@@ -0,0 +1,513 @@
+import { f, m } from "../../../mappings/mappings"
+import DungeonMapRoom from "./DungeonMapRoom"
+import DungeonRoomStaticData from "./DungeonRoomStaticData"
+
+const Color = Java.type("java.awt.Color")
+const BufferedImage = Java.type("java.awt.image.BufferedImage")
+
+let DEFAULT_DOOR_COLOR = new Color(Renderer.color(114, 67, 27, 255))
+let PUZZLE_DOOR_COLOR = new Color(Renderer.color(178, 76, 216, 255))
+let MINIBOSS_DOOR_COLOR = new Color(Renderer.color(229, 229, 51, 255))
+let BLOOD_DOOR_COLOR = new Color(Renderer.color(255, 0, 0, 255))
+let TRAP_DOOR_COLOR = new Color(Renderer.color(216, 127, 51, 255))
+let WITHER_DOOR_COLOR = new Color(Renderer.color(0, 0, 0, 255))
+
+let mapDataScale = {
+ "E": 22,
+ "1": 22,
+ "2": 22,
+ "3": 22,
+ "4": 20,
+ "5": 20,
+ "6": 20,
+ "7": 20
+}
+
+// let TabOverlayListField = Java.type("net.minecraft.client.gui.GuiPlayerTabOverlay").class.getDeclaredField("field_175252_a")
+// TabOverlayListField.setAccessible(true)
+// let TabOverlayList = TabOverlayListField.get(null)
+let PlayerComparator = Java.type("net.minecraft.client.gui.GuiPlayerTabOverlay").PlayerComparator
+let c = PlayerComparator.class.getDeclaredConstructor()
+c.setAccessible(true);
+let sorter = c.newInstance()
+let nethandlerplayclient = Player.getPlayer()[f.sendQueue.EntityPlayerSP]
+
+class DungeonMapData {
+ constructor(floor) {
+ this.floor = floor
+
+ this.roomScaleMap = mapDataScale[floor[floor.length - 1]] //how many pixels on the map is 32 blocks
+ this.roomOffsetMap = [0, 0] //How offset is the map
+
+ /**
+ * @type {Map<String,DungeonMapRoom>}
+ */
+ this.rooms = new Map()
+ /**
+ * @type {Map<String,Number>}
+ */
+ this.doors = new Map()
+
+ this.greenRoomCoords = undefined
+
+ this.image = undefined
+ this.oldImage = undefined
+
+ this.renderTicks = false
+
+ this.players = []
+ this.playersNameToId = {}
+ }
+
+ updatePlayers() {
+ let pl = nethandlerplayclient[m.getPlayerInfoMap]().sort((a, b) => sorter.compare(a, b))
+ let i = 0
+ for (let p of pl) {
+ if (!p[m.getDisplayName.NetworkPlayerInfo]()) continue
+ let line = p[m.getDisplayName.NetworkPlayerInfo]()[m.getUnformattedText]().trim().replace("♲ ", "") //TODO: Remove bingo symbol
+ if (line.endsWith(")") && line.includes(" (") && line.split(" (").length === 2 && line.split(" (")[0].split(" ").length === 1 && line.split(" (")[1].length > 5) {
+ let name = line.split(" ")[0]
+
+ if (!this.players[i]) {
+ this.players[i] = {
+ name: "",
+ x: 0,
+ y: 0,
+ rotate: 0,
+ skin: undefined
+ }
+ }
+ this.players[i].name = name
+ this.players[i].skin = p[m.getLocationSkin.NetworkPlayerInfo]()
+ this.playersNameToId[name] = i
+
+ i++
+ }
+ }
+ }
+
+ updatePlayersFast() {
+ World.getAllPlayers().forEach(player => {
+ let p = this.players[this.playersNameToId[ChatLib.removeFormatting(player.getName()).trim()]]
+ if (!p) return
+
+ p.x = player.getX()
+ p.y = player.getZ()
+ p.rotate = player.getYaw() + 180
+ })
+ }
+
+ loadPlayersFromDecoration(deco) {
+
+ let i = 0
+ deco.forEach((icon, vec4b) => {
+
+ if (!this.players[i]) return
+
+ let x = vec4b.func_176112_b()
+ let y = vec4b.func_176113_c()
+ let rot = vec4b.func_176111_d()
+ x = x / 2 + this.roomScaleMap
+ y = y / 2 + this.roomScaleMap
+ rot = rot * 360 / 16 + 180
+
+ this.players[i].rotate = rot
+ this.players[i].x = (x) / this.roomScaleMap * 32 - this.roomOffsetMap[0]
+ this.players[i].y = (y) / this.roomScaleMap * 32 - this.roomOffsetMap[1]
+
+ i++
+ });
+ }
+
+ updateHotbarData() {
+ let mapData
+ try {
+ let item = Player.getInventory().getStackInSlot(8)
+ mapData = item.getItem()[m.getMapData](item.getItemStack(), World.getWorld()); // ItemStack.getItem().getMapData()
+ } catch (error) {
+ }
+ if (mapData) {
+ this.loadPlayersFromDecoration(mapData[f.mapDecorations])
+
+ let bytes = mapData[f.colors.MapData]
+ if (!this.greenRoomCoords) return
+ //30 = green
+ //0 = transparent
+ //66 = puzzle
+ //34 = white
+
+ let rx = 0
+ let ry = 0
+ for (let x = 0; x < 128; x += 5) {
+ for (let y = 0; y < 128; y += 5) {
+ if (bytes[x + y * 128] === 30
+ && bytes[x + 1 + y * 128] === 30
+ && bytes[x + 2 + y * 128] === 30
+ && bytes[x + 3 + y * 128] === 30) {
+ rx = x
+ ry = y
+ while (bytes[(rx - 1) + ry * 128] === 30) {
+ rx--
+ }
+ while (bytes[(rx) + (ry - 1) * 128] === 30) {
+ ry--
+ }
+ break;
+ }
+ }
+ if (rx) break;
+ }
+ rx += (this.roomScaleMap / 4 * 5) / 2 - this.roomScaleMap
+ ry += (this.roomScaleMap / 4 * 5) / 2 - this.roomScaleMap
+ this.roomOffsetMap[0] = -((this.greenRoomCoords[0]) / 32 * this.roomScaleMap + 2 * this.roomScaleMap - rx) / this.roomScaleMap * 32
+ this.roomOffsetMap[1] = -((this.greenRoomCoords[1]) / 32 * this.roomScaleMap + 2 * this.roomScaleMap - ry) / this.roomScaleMap * 32
+
+ console.log(this.roomOffsetMap.join(","))
+
+
+ let toMap = (x2, y2) => {
+ return Math.round(((x2 + this.roomOffsetMap[0]) / 32 * this.roomScaleMap + 2 * this.roomScaleMap)) + Math.round(((y2 + this.roomOffsetMap[1]) / 32 * this.roomScaleMap + 2 * this.roomScaleMap)) * 128
+ }
+
+ console.log(bytes[toMap(Player.getX(), Player.getZ())])
+
+ let loadRoomAt = (x, y) => {
+ x = Math.floor((x + 8) / 32) * 32 - 8
+ y = Math.floor((y + 8) / 32) * 32 - 8
+
+ if (bytes[toMap(x + 16, y + 16)] === 30) {
+ this.setRoomFull(x, y, 0, DungeonMapRoom.TYPE_SPAWN, DungeonMapRoom.SHAPE_1X1, 0)
+ }
+ if (bytes[toMap(x + 16, y + 16)] === 66) {
+ this.setRoomFull(x, y, 0, DungeonMapRoom.TYPE_PUZZLE, DungeonMapRoom.SHAPE_1X1, 0)
+ }
+ if (bytes[toMap(x + 16, y + 16)] === 82) {
+ this.setRoomFull(x, y, 0, DungeonMapRoom.TYPE_FAIRY, DungeonMapRoom.SHAPE_1X1, 0)
+ }
+ if (bytes[toMap(x + 16, y + 16)] === 18) {
+ this.setRoomFull(x, y, 0, DungeonMapRoom.TYPE_BLOOD, DungeonMapRoom.SHAPE_1X1, 0)
+ }
+ if (bytes[toMap(x + 16, y + 16)] === 64) {
+ this.setRoomFull(x, y, 0, DungeonMapRoom.TYPE_TRAP, DungeonMapRoom.SHAPE_1X1, 0)
+ }
+ if (bytes[toMap(x + 16, y + 16)] === 63) {
+ let width = 32
+ let height = 32
+ while (bytes[toMap(x, y + 5)] === 63) {
+ x -= 32
+ width += 32
+ }
+ while (bytes[toMap(x + 5, y)] === 63) {
+ y -= 32
+ height += 32
+ }
+ while (bytes[toMap(x + width, y + 5)] === 63) {
+ width += 32
+ }
+ while (bytes[toMap(x + 5, y + height)] === 63) {
+ height += 32
+ }
+
+ let shape = DungeonMapRoom.SHAPE_1X1
+ let rotation = 0
+
+ if (width === height) {
+ if (width === 64) {
+ shape = DungeonMapRoom.SHAPE_2X2
+ }
+ }
+ if (width > height) {
+ if (width === 64) {
+ shape = DungeonMapRoom.SHAPE_1X2
+ }
+ if (width === 96) {
+ shape = DungeonMapRoom.SHAPE_1X3
+ }
+ if (width === 128) {
+ shape = DungeonMapRoom.SHAPE_1X4
+ }
+ }
+ if (width < height) {
+ rotation = 1
+ if (height === 64) {
+ shape = DungeonMapRoom.SHAPE_1X2
+ }
+ if (height === 96) {
+ shape = DungeonMapRoom.SHAPE_1X3
+ }
+ if (height === 128) {
+ shape = DungeonMapRoom.SHAPE_1X4
+ }
+ }
+
+ this.setRoomFull(x, y, rotation, DungeonMapRoom.TYPE_NORMAL, shape, 0)
+ }
+ }
+ for (let x = -200; x < 100; x += 32) {
+ for (let y = -200; y < 100; y += 32) {
+ loadRoomAt(x, y)
+ }
+ }
+ }
+ }
+
+ setRenderTicks(val) {
+ if (this.renderTicks !== val) {
+ this.mapChanged()
+ }
+ this.renderTicks = val
+ }
+
+ setDoor(x, y, doorType, rotation) {//doorType 0=normal, 1=wither, 2=blood
+ if (doorType === -1) {
+ let id = World.getBlockStateAt(new BlockPos(x, 69, y)).getBlockId()
+ if (id === 0) doorType = 0
+ else if (id === 97) doorType = 0
+ else if (id === 173) doorType = 1
+ else if (id === 159) doorType = 2
+ else return
+ }
+ if (this.doors.get(x + "," + y)?.join(",") !== doorType + "," + rotation) {
+ this.doors.set(x + "," + y, [doorType, rotation])
+ this.mapChanged()
+ }
+ }
+
+ setRoomFull(x, y, rotation, roomType, shape, checkedState) {
+ let locstr = x + "," + y
+
+ if (shape === DungeonMapRoom.SHAPE_L && rotation === 2) {
+ locstr = x + "," + (y + 32)
+ }
+
+ if (this.rooms.get(locstr) && this.rooms.get(locstr).type !== roomType) {
+ this.rooms.get(locstr).type = roomType
+ this.mapChanged()
+ return
+ }
+ if (this.rooms.get(locstr) && this.rooms.get(locstr).checkedState !== checkedState) {
+ this.rooms.get(locstr).setCheckedState(checkedState)
+ this.mapChanged()
+ return
+ }
+ if (this.rooms.get(locstr) && this.rooms.get(locstr).shape !== shape) {
+ this.rooms.get(locstr).shape = shape
+ this.mapChanged()
+ return
+ }
+ if (this.rooms.get(locstr) && this.rooms.get(locstr).rotation !== rotation) {
+ this.rooms.get(locstr).rotation = rotation
+ this.mapChanged()
+ return
+ }
+ if (this.rooms.get(locstr)) {
+ return
+ }
+
+ let room = new DungeonMapRoom(roomType, shape, rotation, x, y, undefined)
+
+ this.rooms.set(locstr, room)
+
+ this.mapChanged()
+ }
+
+ setRoom(x, y, rotation, id) {
+ let locstr = x + "," + y
+
+ if (DungeonRoomStaticData.getDataFromId(id).shape === 'L' && rotation === 2) {
+ locstr = x + "," + (y + 32)
+ }
+
+ if (this.rooms.get(locstr) && this.rooms.get(locstr).roomId !== id) {
+ this.rooms.get(locstr).setId(id)
+ this.mapChanged()
+ return
+ }
+ if (this.rooms.get(locstr)) {
+ return
+ }
+
+ let room = DungeonMapRoom.fromId(id, x, y, rotation)
+
+ if (room.type === DungeonMapRoom.TYPE_SPAWN) {
+ this.greenRoomCoords = [x, y]
+ }
+
+ this.rooms.set(locstr, room)
+
+ this.mapChanged()
+ }
+
+ getPlayers() {
+ return this.players
+ }
+
+ roomSecrets(x, y, rotation, id, curr, max) {
+ let locstr = x + "," + y
+
+ if (DungeonRoomStaticData.getDataFromId(id).shape === 'L' && rotation === 2) {
+ locstr = x + "," + (y - 32)
+ }
+
+ if (this.rooms.get(locstr)) {
+ this.rooms.get(locstr).setSecrets(curr, max)
+ }
+ }
+
+ destroy() {
+ this.oldImage.getTexture()[m.deleteGlTexture]()
+ this.image.getTexture()[m.deleteGlTexture]()
+ this.oldImage = undefined
+ this.image = undefined
+ }
+
+ mapChanged() {
+ if (this.image) {
+ if (this.oldImage) this.oldImage.getTexture()[m.deleteGlTexture]()
+ this.oldImage = this.image
+ this.image = undefined
+ }
+ }
+
+ /**
+ * @returns {Image}
+ */
+ getImage() {
+ if (!this.image) {
+ this.image = new Image(this.render())
+ this.image.draw(0, 0, 0, 0)
+ if (this.oldImage) return this.oldImage
+ }
+
+ return this.image
+ }
+
+ renderSecrets(size) {
+ for (let data of this.rooms.entries()) {
+ let room = data[1]
+
+ if (room.maxSecrets === 0) continue
+
+ let text = room.getSecrets()
+ let text2 = "&0" + ChatLib.removeFormatting(text)
+ let width = Renderer.getStringWidth(text) - 6
+
+ let location = room.getIconLocation()
+
+ Renderer.drawString(text2, this.toImageX(location[0]) * size - width / 2 - 1, this.toImageY(location[1]) * size)
+ Renderer.drawString(text2, this.toImageX(location[0]) * size - width / 2 + 1, this.toImageY(location[1]) * size)
+ Renderer.drawString(text2, this.toImageX(location[0]) * size - width / 2, this.toImageY(location[1]) * size - 1)
+ Renderer.drawString(text2, this.toImageX(location[0]) * size - width / 2, this.toImageY(location[1]) * size + 1)
+ Renderer.drawString(text, this.toImageX(location[0]) * size - width / 2, this.toImageY(location[1]) * size)
+ }
+ }
+
+ /**
+ * @returns {BufferedImage}
+ */
+ render() {
+ //create 256x256 image
+ let image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB)
+
+ //create graphics rendering context
+ let graphics = image.createGraphics()
+
+ graphics.translate(256 - 32, 256 - 32)
+
+ //render doors
+ for (let data of this.doors.entries()) {
+ let [location, [type, rotation]] = data
+ location = location.split(",")
+ let x = parseInt(location[0])
+ let y = parseInt(location[1])
+
+ let doorColor = type === 0 ? DEFAULT_DOOR_COLOR : (type === 1 ? WITHER_DOOR_COLOR : BLOOD_DOOR_COLOR)
+
+ if (rotation === 0) {
+ if (this.rooms.get((x - 15) + "," + (y + 1))?.type === DungeonMapRoom.TYPE_BLOOD) {
+ doorColor = BLOOD_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y + 1))?.type === DungeonMapRoom.TYPE_PUZZLE) {
+ doorColor = PUZZLE_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y + 1))?.type === DungeonMapRoom.TYPE_TRAP) {
+ doorColor = TRAP_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y + 1))?.type === DungeonMapRoom.TYPE_MINIBOSS) {
+ doorColor = MINIBOSS_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y - 31))?.type === DungeonMapRoom.TYPE_BLOOD) {
+ doorColor = BLOOD_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y - 31))?.type === DungeonMapRoom.TYPE_PUZZLE) {
+ doorColor = PUZZLE_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y - 31))?.type === DungeonMapRoom.TYPE_TRAP) {
+ doorColor = TRAP_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 15) + "," + (y - 31))?.type === DungeonMapRoom.TYPE_MINIBOSS) {
+ doorColor = MINIBOSS_DOOR_COLOR
+ }
+ }
+ if (rotation === 1) {
+ if (this.rooms.get((x - 31) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_BLOOD) {
+ doorColor = BLOOD_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 31) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_PUZZLE) {
+ doorColor = PUZZLE_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 31) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_TRAP) {
+ doorColor = TRAP_DOOR_COLOR
+ }
+ if (this.rooms.get((x - 31) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_MINIBOSS) {
+ doorColor = MINIBOSS_DOOR_COLOR
+ }
+ if (this.rooms.get((x + 1) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_BLOOD) {
+ doorColor = BLOOD_DOOR_COLOR
+ }
+ if (this.rooms.get((x + 1) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_PUZZLE) {
+ doorColor = PUZZLE_DOOR_COLOR
+ }
+ if (this.rooms.get((x + 1) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_TRAP) {
+ doorColor = TRAP_DOOR_COLOR
+ }
+ if (this.rooms.get((x + 1) + "," + (y - 15))?.type === DungeonMapRoom.TYPE_MINIBOSS) {
+ doorColor = MINIBOSS_DOOR_COLOR
+ }
+ }
+
+ graphics.setColor(doorColor)
+
+ graphics.fillRect(x + (rotation === 0 ? 0 : 1), y + (rotation === 1 ? 0 : 1), rotation === 0 ? 8 : 6, rotation === 1 ? 8 : 6)
+
+ if (rotation === 0) {
+ DungeonMapRoom.drawUnknownRoom(graphics, x - 15, y + 1)
+ DungeonMapRoom.drawUnknownRoom(graphics, x - 15, y - 31)
+ }
+ if (rotation === 1) {
+ DungeonMapRoom.drawUnknownRoom(graphics, x - 31, y - 15)
+ DungeonMapRoom.drawUnknownRoom(graphics, x + 1, y - 15)
+ }
+ }
+
+ //render rooms
+ for (let data of this.rooms.entries()) {
+ let room = data[1]
+ room.draw(graphics, this.renderTicks)
+ }
+
+ graphics.translate(-256 + 32, -256 + 32)
+
+ return image
+ }
+
+ toImageX(x) {
+ return (x + 255 - 32) / 256
+ }
+
+ toImageY(y) {
+ return (y + 255 - 32) / 256
+ }
+}
+
+export default DungeonMapData \ No newline at end of file
diff --git a/src/features/dungeonMap2/DungeonMapRoom.js b/src/features/dungeonMap2/DungeonMapRoom.js
new file mode 100644
index 0000000..d6ee014
--- /dev/null
+++ b/src/features/dungeonMap2/DungeonMapRoom.js
@@ -0,0 +1,256 @@
+import DungeonRoomStaticData from "./DungeonRoomStaticData"
+
+const Color = Java.type("java.awt.Color")
+
+class DungeonMapRoom {
+
+ static TYPE_SPAWN = 0
+ static TYPE_NORMAL = 1
+ static TYPE_PUZZLE = 2
+ static TYPE_MINIBOSS = 3
+ static TYPE_FAIRY = 4
+ static TYPE_BLOOD = 5
+ static TYPE_TRAP = 7
+ static TYPE_UNKNOWN = 6
+
+ static SHAPE_1X1 = 0
+ static SHAPE_1X2 = 1
+ static SHAPE_1X3 = 2
+ static SHAPE_1X4 = 3
+ static SHAPE_2X2 = 4
+ static SHAPE_L = 5
+
+ static fromId(roomId, x, y, rotation) {
+ let data = DungeonRoomStaticData.getDataFromId(roomId)
+ let type = DungeonMapRoom.TYPE_NORMAL
+ switch (data.type) {
+ case "mobs":
+ type = DungeonMapRoom.TYPE_NORMAL
+ break
+ case "miniboss":
+ type = DungeonMapRoom.TYPE_NORMAL
+ break
+ case "spawn":
+ type = DungeonMapRoom.TYPE_SPAWN
+ break
+ case "puzzle":
+ type = DungeonMapRoom.TYPE_PUZZLE
+ break
+ case "gold":
+ type = DungeonMapRoom.TYPE_MINIBOSS
+ break
+ case "fairy":
+ type = DungeonMapRoom.TYPE_FAIRY
+ break
+ case "blood":
+ type = DungeonMapRoom.TYPE_BLOOD
+ break
+ case "trap":
+ type = DungeonMapRoom.TYPE_TRAP
+ break
+ }
+
+ let shape = DungeonMapRoom.SHAPE_1X1
+ switch (data.shape) {
+ case "1x1":
+ shape = DungeonMapRoom.SHAPE_1X1
+ break
+ case "1x2":
+ shape = DungeonMapRoom.SHAPE_1X2
+ break
+ case "1x3":
+ shape = DungeonMapRoom.SHAPE_1X3
+ break
+ case "1x4":
+ shape = DungeonMapRoom.SHAPE_1X4
+ break
+ case "2x2":
+ shape = DungeonMapRoom.SHAPE_2X2
+ break
+ case "L":
+ shape = DungeonMapRoom.SHAPE_L
+ break
+ }
+ return new DungeonMapRoom(type, shape, rotation, x, y, roomId)
+ }
+
+ /**
+ *
+ * @param {Number} type
+ * @param {Number} shape
+ * @param {Number} rotation 0-3
+ * @param {Number} x
+ * @param {Number} y
+ * @param {String} roomId
+ */
+ constructor(type, shape, rotation, x, y, roomId) {
+ this.type = type
+ this.shape = shape
+ this.rotation = rotation
+ this.roomId = roomId
+ this.maxSecrets = roomId ? DungeonRoomStaticData.getDataFromId(roomId).secrets : 0
+ this.secrets = 0
+
+ this.checkedState = 0
+
+ this.location = [x, y]
+ }
+
+ setSecrets(curr, max) {
+ if (this.maxSecrets === 0) return
+ this.maxSecrets = max
+ this.secrets = curr
+ }
+
+ getSecrets() {
+ let checkColor = "&8"
+ if (this.checkedState === 1) checkColor = "&f"
+ if (this.checkedState === 2) checkColor = "&a"
+ return checkColor + this.secrets + "/" + this.maxSecrets
+ }
+
+ setId(id) {
+ if (this.roomId !== id) {
+ this.roomId = id
+
+ let newRoomData = DungeonMapRoom.fromId(id)
+ this.type = newRoomData.type
+ this.shape = newRoomData.shape
+ this.maxSecrets = newRoomData.maxSecrets
+ }
+ }
+
+ setCheckedState(state) { }
+
+ /**
+ * Renders this room onto the given Graphics2D
+ * @param {Graphics2D} graphics
+ */
+ draw(graphics, renderTicks) {
+
+ let [roomWidth, roomHeight] = this.getRoomWidthHeight()
+
+ let translateX = this.location[0] + 3 + roomWidth / 2
+ let translateY = this.location[1] + 3 + roomHeight / 2
+
+ graphics.translate(translateX, translateY)
+
+ graphics.setColor(this.getRoomRenderColor())
+
+ switch (this.shape) {
+ case DungeonMapRoom.SHAPE_1X1:
+ case DungeonMapRoom.SHAPE_1X2:
+ case DungeonMapRoom.SHAPE_1X3:
+ case DungeonMapRoom.SHAPE_1X4:
+ case DungeonMapRoom.SHAPE_2X2:
+ graphics.fillRect(-roomWidth / 2 + 3, -roomHeight / 2 + 3, roomWidth - 6, roomHeight - 6)
+ break;
+ case DungeonMapRoom.SHAPE_L:
+ graphics.rotate(this.rotation * Math.PI / 2)
+ graphics.fillRect(-(26 + 32) / 2, -(26 + 32) / 2, 26, 26 + 32)
+ graphics.fillRect(-(26 + 32) / 2, -(26 + 32) / 2, 26 + 32, 26)
+ graphics.rotate(-this.rotation * Math.PI / 2)
+ break;
+ }
+
+ graphics.translate(-translateX, -translateY)
+
+ if (renderTicks || this.maxSecrets === 0) {
+ if (this.checkedState === 1) {
+ let loc = this.getIconLocation()
+ graphics.drawImage(whiteCheck, loc[0] - 2, loc[1] - 2, 10, 10, null)
+ }
+ if (this.checkedState === 2) {
+ let loc = this.getIconLocation()
+ graphics.drawImage(greenCheck, loc[0] - 2, loc[1] - 2, 10, 10, null)
+ }
+ }
+ }
+
+ getRoomWidthHeight() {
+ let roomWidth = 32
+ let roomHeight = 32
+
+ switch (this.shape) {
+ case DungeonMapRoom.SHAPE_1X2:
+ roomWidth = 64
+ break;
+ case DungeonMapRoom.SHAPE_1X3:
+ roomWidth = 96
+ break;
+ case DungeonMapRoom.SHAPE_1X4:
+ roomWidth = 128
+ break;
+ case DungeonMapRoom.SHAPE_2X2:
+ case DungeonMapRoom.SHAPE_L:
+ roomWidth = 64
+ roomHeight = 64
+ break;
+ }
+
+ if (this.rotation === 1 || this.rotation === 3) {
+ let tmp = roomWidth
+ roomWidth = roomHeight
+ roomHeight = tmp
+ }
+
+ return [roomWidth, roomHeight]
+ }
+
+ getRoomRenderColor() {
+ return roomColorMap.get(this.type)
+ }
+
+ getIconLocation() {
+ let [width, height] = this.getRoomWidthHeight()
+ if (this.shape === DungeonMapRoom.SHAPE_L && this.rotation === 0) {
+ return [this.location[0] + 16, this.location[1] + 16]
+ }
+ if (this.shape === DungeonMapRoom.SHAPE_L && this.rotation === 1) {
+ return [this.location[0] + 16 + 32, this.location[1] + 16]
+ }
+ if (this.shape === DungeonMapRoom.SHAPE_L && this.rotation === 2) {
+ return [this.location[0] + 16 + 31, this.location[1] + 16 + 31]
+ }
+ if (this.shape === DungeonMapRoom.SHAPE_L && this.rotation === 2) {
+ return [this.location[0] + 16, this.location[1] + 16 + 32]
+ }
+
+ return [this.location[0] + width / 2, this.location[1] + height / 2]
+ }
+
+ static drawUnknownRoom(graphics, x, y) {
+ let roomWidth = 32
+ let roomHeight = 32
+
+ let translateX = x + 3 + roomWidth / 2
+ let translateY = y + 3 + roomHeight / 2
+
+ graphics.translate(translateX, translateY)
+
+ graphics.setColor(roomColorMap.get(DungeonMapRoom.TYPE_UNKNOWN))
+
+ graphics.fillRect(-13, -13, 26, 26)
+
+ graphics.drawImage(questionMark, -4, -7, 8, 14, null)
+
+ graphics.translate(-translateX, -translateY)
+ }
+}
+
+let roomColorMap = new Map()
+roomColorMap.set(DungeonMapRoom.TYPE_SPAWN, new Color(Renderer.color(0, 124, 0, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_NORMAL, new Color(Renderer.color(114, 67, 27, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_PUZZLE, new Color(Renderer.color(178, 76, 216, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_MINIBOSS, new Color(Renderer.color(229, 229, 51, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_FAIRY, new Color(Renderer.color(242, 127, 165, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_BLOOD, new Color(Renderer.color(255, 0, 0, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_TRAP, new Color(Renderer.color(216, 127, 51, 255)))
+roomColorMap.set(DungeonMapRoom.TYPE_UNKNOWN, new Color(Renderer.color(65, 65, 65, 255)))
+
+const greenCheck = new Image("greenCheckVanilla.png", "https://i.imgur.com/h2WM1LO.png").image
+const whiteCheck = new Image("whiteCheckVanilla.png", "https://i.imgur.com/hwEAcnI.png").image
+const failedRoom = new Image("failedRoomVanilla.png", "https://i.imgur.com/WqW69z3.png").image
+const questionMark = new Image("questionMarkVanilla.png", "https://i.imgur.com/1jyxH9I.png").image
+
+export default DungeonMapRoom \ No newline at end of file
diff --git a/src/features/dungeonMap2/DungeonRoomStaticData.js b/src/features/dungeonMap2/DungeonRoomStaticData.js
new file mode 100644
index 0000000..75d5b1e
--- /dev/null
+++ b/src/features/dungeonMap2/DungeonRoomStaticData.js
@@ -0,0 +1,19 @@
+class DungeonRoomStaticData {
+ constructor() {
+ this.fullRoomData = JSON.parse(FileLib.read("SoopyV2", "data/roomdata.json"))
+
+ this.idMap = new Map()
+ this.fullRoomData.forEach((d, i) => {
+ d.id.forEach(id => {
+ this.idMap.set(id, i)
+ })
+ this.idMap.set(d.index, i)
+ })
+ }
+
+ getDataFromId(id) {
+ return this.fullRoomData[this.idMap.get(id)]
+ }
+}
+
+export default new DungeonRoomStaticData() \ No newline at end of file
diff --git a/src/features/dungeonMap2/index.js b/src/features/dungeonMap2/index.js
new file mode 100644
index 0000000..199bc05
--- /dev/null
+++ b/src/features/dungeonMap2/index.js
@@ -0,0 +1,312 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+
+import Feature from "../../featureClass/class";
+import renderLibs from "../../../guimanager/renderLibs";
+import DungeonMapData from "./DungeonMapData";
+import DungeonRoomStaticData from "./DungeonRoomStaticData";
+import ImageLocationSetting from "../settings/settingThings/imageLocation";
+import ToggleSetting from "../settings/settingThings/toggle";
+import { f, m } from "../../../mappings/mappings";
+
+const DefaultVertexFormats = Java.type("net.minecraft.client.renderer.vertex.DefaultVertexFormats")
+const MCTessellator = Java.type("net.minecraft.client.renderer.Tessellator")
+
+class DungeonMap extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ if (Player.getUUID().toString() !== "dc8c3964-7b29-4e03-ae9e-d13ebd65dd29") {
+ new SettingBase("not Coming soontm", "maby", undefined, "coming_soontm", this)
+ return
+ }
+ this.mapLocation = new ImageLocationSetting("Map Location", "Sets the location of the map on the hud", "dmap_location", this, [10, 10, 1], new Image(javax.imageio.ImageIO.read(new java.io.File("./config/ChatTriggers/modules/SoopyV2/features/dungeonMap/map.png"))), 150, 150)
+ this.mapSecrets = new ToggleSetting("Show secret count instead of tick", "Syncs between soopyv2 users", true, "dmap_secrets", this)
+
+ this.lastRoomId = undefined
+
+ /**@type {DungeonMapData} */
+ this.currentDungeon = undefined
+ this.lastChange = 0
+
+ this.roomXY = this.getRoomXYWorld().join(",")
+ this.lastXY = undefined
+
+ let registerActionBar = this.registerCustom("actionbar", (curr, max) => {
+
+ let roomid = this.getCurrentRoomId()
+ let roomWorldData = this.getRoomWorldData()
+
+ let rotation = roomWorldData.width > roomWorldData.height ? 0 : 1
+
+ if (this.getCurrentRoomData().shape === "L") rotation = roomWorldData.rotation
+ if (this.getCurrentRoomData().type === "spawn") {
+ roomWorldData.x = x + 1
+ roomWorldData.y = y + 1
+ }
+
+ this.currentDungeon.roomSecrets(roomWorldData.x, roomWorldData.y, rotation, roomid, curr, max)
+ })
+ registerActionBar.trigger.setCriteria('&7${curr}/${max} Secrets').setParameter('contains');
+
+ this.registerStep(true, 5, this.update)
+
+ this.registerEvent("renderOverlay", this.renderOverlay)
+ this.registerEvent("worldLoad", this.worldLoad)
+
+ }
+
+ update() {
+ if (!this.isInDungeon()) {
+ if (this.currentDungeon) {
+ this.currentDungeon.destroy()
+ this.lastRoomId = undefined
+ this.currentDungeon = undefined
+ }
+ return
+ }
+
+ if (!this.currentDungeon) {
+ this.currentDungeon = new DungeonMapData(this.FeatureManager.features["dataLoader"].class.dungeonFloor)
+ }
+ this.currentDungeon.setRenderTicks(!this.mapSecrets.getValue())
+
+ this.currentDungeon.updatePlayers()
+ this.currentDungeon.updateHotbarData()
+
+ let roomid = this.getCurrentRoomId()
+ if (!roomid.includes(",")) return
+ if (this.roomXY !== this.getRoomXYWorld().join(",")) {
+ this.roomXY = this.getRoomXYWorld().join(",")
+ this.lastChange = Date.now()
+ }
+
+ let x = Math.floor((Player.getX() + 8) / 32) * 32 - 9
+ let y = Math.floor((Player.getZ() + 8) / 32) * 32 - 9
+
+ if (roomid !== this.lastRoomId && Date.now() - this.lastChange > 500) {
+ this.lastRoomId = roomid
+
+ let roomWorldData = this.getRoomWorldData()
+
+ let rotation = roomWorldData.width > roomWorldData.height ? 0 : 1
+
+ if (this.getCurrentRoomData().shape === "L") rotation = roomWorldData.rotation
+ if (this.getCurrentRoomData().type === "spawn") {
+ roomWorldData.x = x + 1
+ roomWorldData.y = y + 1
+ }
+
+ this.currentDungeon.setRoom(roomWorldData.x, roomWorldData.y, rotation, roomid)
+ }
+
+
+ if (this.lastXY !== x + "," + y) {
+ this.lastXY = x + "," + y
+ if (this.getBlockAt(x + 16, 73, y) !== 0) {
+ this.currentDungeon.setDoor(x + 16, y, -1, 0)
+ }
+ if (this.getBlockAt(x, 73, y + 16) !== 0) {
+ this.currentDungeon.setDoor(x, y + 16, -1, 1)
+ }
+ if (this.getBlockAt(x + 16, 73, y + 32) !== 0) {
+ this.currentDungeon.setDoor(x + 16, y + 32, -1, 0)
+ }
+ if (this.getBlockAt(x + 32, 73, y + 16) !== 0) {
+ this.currentDungeon.setDoor(x + 32, y + 16, -1, 1)
+ }
+ }
+ }
+
+ renderOverlay() {
+ if (this.currentDungeon) {
+ this.currentDungeon.updatePlayersFast()
+
+ let x = this.mapLocation.getValue()[0]
+ let y = this.mapLocation.getValue()[1]
+ let size = 150 * this.mapLocation.getValue()[2]
+ Renderer.drawRect(Renderer.color(0, 0, 0, 100), x, y, size, size)
+
+ this.currentDungeon.getImage().draw(x, y, size, size)
+
+ Renderer.drawRect(Renderer.color(0, 0, 0), x, y, size, 2)
+ Renderer.drawRect(Renderer.color(0, 0, 0), x, y, 2, size)
+ Renderer.drawRect(Renderer.color(0, 0, 0), x + size - 2, y, 2, size)
+ Renderer.drawRect(Renderer.color(0, 0, 0), x, y + size - 2, size, 2)
+
+ for (let player of this.currentDungeon.getPlayers()) {
+ let x2 = this.currentDungeon.toImageX(player.x) * size + x
+ let y2 = this.currentDungeon.toImageY(player.y) * size + y
+ let rx = -6
+ let ry = -6
+ let rw = 12
+ let rh = 12
+
+ Renderer.translate(x2, y2)
+ Renderer.rotate(player.rotate)
+ GlStateManager[m.enableBlend]()
+ GlStateManager[m.scale](1, 1, 50)
+ Client.getMinecraft()[m.getTextureManager]()[m.bindTexture.TextureManager](player.skin)
+ GlStateManager[m.enableTexture2D]()
+
+ let tessellator = MCTessellator[m.getInstance.Tessellator]()
+ let worldRenderer = tessellator[m.getWorldRenderer]()
+ worldRenderer[m.begin](7, DefaultVertexFormats[f.POSITION_TEX])
+
+ worldRenderer[m.pos](rx, ry + rh, 0.0)[m.tex](8 / 64, 16 / 64)[m.endVertex]()
+ worldRenderer[m.pos](rx + rw, ry + rh, 0.0)[m.tex](16 / 64, 16 / 64)[m.endVertex]()
+ worldRenderer[m.pos](rx + rw, ry, 0.0)[m.tex](16 / 64, 8 / 64)[m.endVertex]()
+ worldRenderer[m.pos](rx, ry, 0.0)[m.tex](8 / 64, 8 / 64)[m.endVertex]()
+ tessellator[m.draw.Tessellator]()
+
+ Tessellator.popMatrix()
+ Tessellator.pushMatrix()
+ }
+
+ if (this.mapSecrets.getValue()) {
+ Renderer.retainTransforms(true)
+ Renderer.translate(x, y)
+ this.currentDungeon.renderSecrets(size)
+ Renderer.translate(-x, -y)
+ Renderer.retainTransforms(false)
+ }
+ }
+ }
+
+ worldLoad() {
+ if (this.currentDungeon) this.currentDungeon.destroy()
+ this.lastRoomId = undefined
+ this.currentDungeon = undefined
+ }
+
+ getCurrentRoomId() {
+ if (Scoreboard.getLines().length === 0) return undefined
+ let id = Scoreboard.getLineByIndex(Scoreboard.getLines().length - 1).getName().trim().split(" ").pop()
+
+ return id
+ }
+
+ isInDungeon() {
+ if (!this.FeatureManager || !this.FeatureManager.features["dataLoader"]) return false
+ return this.FeatureManager.features["dataLoader"].class.isInDungeon
+ }
+
+ getRoomXYWorld() {
+ let roomData = this.getRoomWorldData()
+ if (roomData.rotation === 4) {
+ return [roomData.x, roomData.y + 32]
+ }
+
+ return [roomData.x, roomData.y]
+ }
+
+ getCurrentRoomData() {
+ return DungeonRoomStaticData.getDataFromId(this.getCurrentRoomId())
+ }
+
+ getRotation(x, y, width, height, roofY) {
+ let currRoomData = this.getCurrentRoomData()
+ if (!currRoomData) return -1
+
+ if (currRoomData.shape !== "L") {
+ if (this.getTopBlockAt(x, y, roofY) === 11) return 0
+ if (this.getTopBlockAt(x + width, y, roofY) === 11) return 1
+ if (this.getTopBlockAt(x + width, y + height, roofY) === 11) return 2
+ if (this.getTopBlockAt(x, y + height, roofY) === 11) return 3
+ } else {
+ let one = this.getTopBlockAt2(x + width / 2 + 1, y + height / 2, roofY)
+ let two = this.getTopBlockAt2(x + width / 2 - 1, y + height / 2, roofY)
+ let three = this.getTopBlockAt2(x + width / 2, y + height / 2 + 1, roofY)
+ let four = this.getTopBlockAt2(x + width / 2, y + height / 2 - 1, roofY)
+
+ if (one === 0 && three === 0) return 0
+ if (two === 0 && three === 0) return 1
+ if (one === 0 && four === 0) return 3
+ if (two === 0 && four === 0) return 2//3 IS SO TOXIK HGOLY HEL I HATE L SHAPE ROOMS WHY DO THIS TO ME
+ }
+
+ return -1
+ }
+
+ getRoomWorldData() {
+ let x = Math.floor((Player.getX() + 8) / 32) * 32 - 8
+ let y = Math.floor((Player.getZ() + 8) / 32) * 32 - 8
+ let width = 30
+ let height = 30
+
+ let roofY = this.getRoofAt(x, y)
+
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y)).getBlockId() !== 0) {
+ x -= 32
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x, roofY, y - 1)).getBlockId() !== 0) {
+ y -= 32
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y)).getBlockId() !== 0) { //second iteration incase of L shape
+ x -= 32
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width + 1, roofY, y)).getBlockId() !== 0) {
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x, roofY, y + height + 1)).getBlockId() !== 0) {
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width, roofY, y + height + 1)).getBlockId() !== 0) { //second iteration incase of L shape
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width + 1, roofY, y + height)).getBlockId() !== 0) { //second iteration incase of L shape
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width, roofY, y - 1)).getBlockId() !== 0) {//second iteration incase of L shape
+ y -= 32
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y + height)).getBlockId() !== 0) { //third iteration incase of L shape
+ x -= 32
+ width += 32
+ }
+
+
+ return {
+ x,
+ y,
+ width,
+ height,
+ cx: x + width / 2,
+ cy: y + height / 2,
+ rotation: this.getRotation(x, y, width, height, roofY)
+ }
+ }
+
+ getRoofAt(x, z) {
+ let y = 255
+ while (y > 0 && World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId() === 0) y--
+
+ return y
+ }
+
+ getTopBlockAt(x, z, y) {
+ if (!y) y = this.getHeightAt(x, z)
+
+ return World.getBlockStateAt(new BlockPos(x, y, z)).getMetadata()
+ }
+ getBlockAt(x, y, z) {
+ return World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId()
+ }
+ getTopBlockAt2(x, z, y) {
+ if (!y) y = this.getHeightAt(x, z)
+
+ return World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId()
+ }
+ onDisable() {
+ }
+}
+
+module.exports = {
+ class: new DungeonMap()
+} \ No newline at end of file
diff --git a/src/features/dungeonMap2/metadata.json b/src/features/dungeonMap2/metadata.json
new file mode 100644
index 0000000..53e7e03
--- /dev/null
+++ b/src/features/dungeonMap2/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Dungeon Map 2",
+ "description": "WIP",
+ "isHidden": true,
+ "isTogglable": true,
+ "defaultEnabled": false,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/dungeonRoutes/index.js b/src/features/dungeonRoutes/index.js
new file mode 100644
index 0000000..e59e908
--- /dev/null
+++ b/src/features/dungeonRoutes/index.js
@@ -0,0 +1,481 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { m } from "../../../mappings/mappings";
+import Feature from "../../featureClass/class";
+import { drawBoxAtBlock, drawBoxAtBlock2, drawFilledBox, drawLine, drawLinePoints } from "../../utils/renderUtils";
+import { calculateDistanceQuick } from "../../utils/utils";
+import SettingBase from "../settings/settingThings/settingBase";
+
+const EntityItem = Java.type("net.minecraft.entity.item.EntityItem")
+
+const EnumParticleTypes = Java.type('net.minecraft.util.EnumParticleTypes');
+
+class DungeonRoutes extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ if (Player.getUUID().toString() !== "dc8c3964-7b29-4e03-ae9e-d13ebd65dd29") {
+ new SettingBase("Coming soontm", "maby", undefined, "coming_soontm", this)
+ return
+ }
+
+ this.recordingData = []
+ this.currentActionIndex = 0
+ this.lastLocationUpdatedTime = Date.now()
+
+ this.registerEvent("soundPlay", this.playSound)
+ this.registerEvent("worldLoad", this.worldLoad)
+ this.registerEvent("playerInteract", this.playerInteract)
+ this.registerForge(net.minecraftforge.event.entity.EntityJoinWorldEvent, this.entityJoinWorldEvent)
+ let packetRecieved = this.registerCustom("packetReceived", this.pickupItem)
+
+ try {
+ packetRecieved.trigger.setPacketClasses([net.minecraft.network.play.server.S0DPacketCollectItem])
+ } catch (e) { }//older ct version
+
+ this.registerStep(true, 5, () => {
+ let roomId = this.getCurrentRoomId()
+ if (this.lastRoomId !== roomId) {
+ this.lastRoomId = roomId
+ this.recordingData = []
+
+ this.currRoomData = this.getRoomWorldData()
+
+ this.currentRouteDisplay = this.routesIndexMap.get(this.fullRoomData[this.idMap.get(roomId)].index)?.data
+ ChatLib.chat(JSON.stringify(this.currentRouteDisplay, undefined, 2))
+ }
+
+ if (!this.recordRoute) return
+ if (!this.recordingData[this.recordingData.length - 1]) return
+ let locs = this.recordingData[this.recordingData.length - 1].locations
+ if (locs.length === 0
+ || Math.ceil(Player.getX()) !== locs[locs.length - 1][0]
+ || Math.ceil(Player.getY()) !== locs[locs.length - 1][1]
+ || Math.ceil(Player.getZ()) !== locs[locs.length - 1][2]) {
+
+ this.addRecordingPoint("locations", [Math.ceil(Player.getX()), Math.ceil(Player.getY()), Math.ceil(Player.getZ())])
+ }
+ })
+
+ this.registerEvent("renderWorld", () => {
+ if (this.recordingData && this.recordingData[this.recordingData.length - 1]) {
+ this.recordingData[this.recordingData.length - 1].etherwarps.forEach((loc) => {
+ drawFilledBox(loc[0], loc[1] - 1, loc[2], 1, 1, 1, 0, 0, 50 / 255, true)
+ drawBoxAtBlock(loc[0] - 0.5, loc[1] - 1, loc[2] - 0.5, 1, 0, 0, 1, 1, 1)
+ })
+ this.recordingData[this.recordingData.length - 1].mines.forEach((loc) => {
+ drawFilledBox(loc[0], loc[1] - 0.5, loc[2], 1, 1, 0, 1, 0, 50 / 255, true)
+ })
+ this.recordingData[this.recordingData.length - 1].tnts.forEach((loc) => {
+ drawFilledBox(loc[0], loc[1] - 0.5, loc[2], 1, 1, 1, 0, 0, 50 / 255, true)
+ })
+ this.recordingData[this.recordingData.length - 1].interacts.forEach((data) => {
+ let loc = data.loc
+ drawFilledBox(loc[0], loc[1], loc[2], 1, 1, 0, 0, 1, 50 / 255, true)
+ drawBoxAtBlock(loc[0] - 0.5, loc[1], loc[2] - 0.5, 0, 0, 1, 1, 1, 1)
+ })
+ let locs = this.recordingData[this.recordingData.length - 1].locations
+ if (locs.length >= 2) drawLinePoints(locs.map(a => [a[0] - 0.5, a[1] + 0.1, a[2] - 0.5]), 0, 0, 255, 2, false)
+ }
+
+ if (this.currRoomData) {
+ if (this.currentRouteDisplay) {
+ this.currentRouteDisplay[this.currentActionIndex].etherwarps.forEach(loc => {
+ let coords = this.toRoomCoordinates(loc[0], loc[1] - 1, loc[2])
+ drawFilledBox(coords[0], coords[1], coords[2], 1, 1, 1, 0, 0, 50 / 255, true)
+ drawBoxAtBlock(coords[0] - 0.5, coords[1], coords[2] - 0.5, 1, 0, 0, 1, 1, 1)
+ })
+ this.currentRouteDisplay[this.currentActionIndex].mines.forEach(loc => {
+ let coords = this.toRoomCoordinates(loc[0], loc[1] - 0.5, loc[2])
+ drawBoxAtBlock(coords[0] - 0.5, coords[1], coords[2] - 0.5, 1, 1, 0, 1, 1, 50 / 255)
+ })
+ this.currentRouteDisplay[this.currentActionIndex].tnts.forEach(loc => {
+ let coords = this.toRoomCoordinates(loc[0], loc[1] - 1, loc[2])
+ drawBoxAtBlock(coords[0] - 0.5, coords[1] + 0.5, coords[2] - 0.5, 1, 1, 255, 1, 1, 50 / 255)
+ })
+ this.currentRouteDisplay[this.currentActionIndex].interacts.forEach((data) => {
+ let coords = this.toRoomCoordinates(data.loc[0], data.loc[1], data.loc[2])
+ drawFilledBox(coords[0], coords[1], coords[2], 1, 1, 0, 0, 1, 50 / 255, true)
+ drawBoxAtBlock(coords[0] - 0.5, coords[1], coords[2] - 0.5, 0, 0, 1, 1, 1, 1)
+ })
+
+ // if (this.currentRouteDisplay.locations.length >= 2) drawLinePoints(this.currentRouteDisplay.locations.map(a => this.toRoomCoordinates(...a)).map(a => [a[0] - 0.5, a[1] + 0.1, a[2] - 0.5]), 0, 0, 255, 2, false)
+
+ }
+ }
+
+ })
+
+ this.tempItemIdLocs = new Map()
+
+ this.idMap = new Map()
+ this.routesIndexMap = new Map()
+ this.fullRoomData = JSON.parse(FileLib.read("SoopyV2", "data/roomdata.json"))
+ this.fullRoutesData = JSON.parse(FileLib.read("SoopyV2", "features/dungeonRoutes/routesData.json"))
+ this.fullRoomData.forEach((d, i) => {
+ d.id.forEach(id => {
+ this.idMap.set(id, i)
+ })
+ this.idMap.set(d.index, i)
+ })
+ this.fullRoutesData.forEach((d, i) => {
+ this.routesIndexMap.set(d.index, d)
+ })
+ this.lastRoomId = undefined
+
+ this.currRoomData = undefined
+ this.currentRouteDisplay = undefined
+
+ this.registerCommand("roomid", (...name) => {
+ ChatLib.chat(JSON.stringify(this.getCurrentRoomData(), undefined, 2))
+ ChatLib.chat(JSON.stringify(this.getRoomWorldData(), undefined, 2))
+ })
+ this.recordRoute = false
+
+ this.registerCommand("startroute", (...name) => {
+ this.recordRoute = true
+ this.recordingData = []
+ this.addRecordingPoint()
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Started recording route!")
+ })
+
+ this.registerCommand("saveroute", () => {
+ let data = {
+ index: this.fullRoomData[this.idMap.get(this.lastRoomId)].index,
+ data: this.recordingData.map(a => {
+ a.etherwarps = a.etherwarps.map(a => this.fromRoomCoordinates(a[0], a[1], a[2]))
+ a.mines = a.mines.map(a => this.fromRoomCoordinates(a[0], a[1], a[2]))
+ a.locations = a.locations.map(a => this.fromRoomCoordinates(...a))
+ a.interacts = a.interacts.map(b => {
+ b.loc = this.fromRoomCoordinates(...b.loc)
+ return b
+ })
+ a.tnts = a.tnts.map(a => this.fromRoomCoordinates(a[0], a[1], a[2]))
+
+ return a
+ })
+ }
+ // this.recordingData.push({
+ // etherwarps: [],
+ // mines: [],
+ // locations: [],
+ // interacts: [],
+ // tnts: []
+ // })
+
+ ChatLib.chat(JSON.stringify(data, undefined, 4))
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Saved route!")
+ this.recordRoute = false
+ })
+
+ this.registerStep(true, 5, () => {
+ if (this.currRoomData) {
+ if (this.currentRouteDisplay) {
+
+ if (this.currentRouteDisplay[this.currentActionIndex].locations.length >= 2) this.drawLineMultipleParticles(this.currentRouteDisplay[this.currentActionIndex].locations.map(a => this.toRoomCoordinates(a[0], a[1], a[2])))
+
+ }
+ }
+ })
+ }
+
+ addRecordingPoint(type, point) {
+ if (type) {
+ this.recordingData[this.recordingData.length - 1][type].push(point)
+ }
+ if (!type || type === "interacts") {
+ this.recordingData.push({
+ etherwarps: [],
+ mines: [],
+ locations: [],
+ interacts: [],
+ tnts: []
+ })
+ }
+ }
+ drawLineMultipleParticles(locations) {
+ let lastLoc = undefined
+ locations.forEach(loc => {
+ if (!lastLoc) {
+ lastLoc = loc
+ return
+ }
+
+ this.drawLineParticles(lastLoc, loc)
+ lastLoc = loc
+ })
+ }
+
+ drawLineParticles(loc1, loc2) {
+ let distance = Math.hypot(...loc1.map((a, i) => a - loc2[i]))
+ let maxPoints = Math.ceil(distance * 1)
+ for (let i = 0; i < maxPoints; i++) {
+ let actualI = i + Math.random()
+ let a = actualI / maxPoints
+ let loc = [loc1[0] * a + loc2[0] * (1 - a) - 0.5, loc1[1] * a + loc2[1] * (1 - a) + 0.1, loc1[2] * a + loc2[2] * (1 - a) - 0.5]
+
+ let a2 = (actualI + 0.02) / maxPoints
+ let loc3 = [loc1[0] * a2 + loc2[0] * (1 - a2) - 0.5, loc1[1] * a2 + loc2[1] * (1 - a2) + 0.1, loc1[2] * a2 + loc2[2] * (1 - a2) - 0.5]
+ loc3 = loc3.map((a, i) => loc[i] - a)
+
+ this.spawnParticleAtLocation(loc, loc3, "FLAME")
+ }
+ }
+
+ spawnParticleAtLocation(loc, velo, particle) {
+ let particleType = EnumParticleTypes.valueOf(particle);
+ let idField = particleType.getClass().getDeclaredField('field_179372_R');
+ idField.setAccessible(true);
+ let id = idField.get(particleType);
+
+ Client.getMinecraft().field_71438_f.func_174974_b(
+ id, // particleID
+ true, // shouldIgnoreRange
+ loc[0], // x
+ loc[1], // y
+ loc[2], // z
+ velo[0], // speedX
+ velo[1], // speedY
+ velo[2], // speedZ
+ );
+ }
+
+ toRoomCoordinates(x, y, z) { //From relative coords to world coords
+ if (!this.currRoomData) return null
+
+ if (this.currRoomData.rotation === 2) {
+ z *= -1;
+ [x, z] = [z, x]
+ } else if (this.currRoomData.rotation === 3) {
+ x *= -1
+ z *= -1
+ } else if (this.currRoomData.rotation === 4) {
+ x *= -1;
+ [x, z] = [z, x]
+ }
+
+ return [this.currRoomData.cx + x, y, this.currRoomData.cy + z]
+ }
+ fromRoomCoordinates(x, y, z) { //from world coords to relative coords
+ if (!this.currRoomData) return null
+
+ x -= this.currRoomData.cx
+ z -= this.currRoomData.cy
+
+ if (this.currRoomData.rotation === 2) {
+ [x, z] = [z, x]
+ z *= -1
+ } else if (this.currRoomData.rotation === 3) {
+ x *= -1
+ z *= -1
+ } else if (this.currRoomData.rotation === 4) {
+ [x, z] = [z, x]
+ x *= -1
+ }
+
+ return [x, y, z]
+ }
+
+ getRotation(x, y, width, height, roofY) {
+ let currRoomData = this.getCurrentRoomData()
+ if (!currRoomData) return -1
+
+ if (currRoomData.shape !== "L") {
+ if (this.getTopBlockAt(x, y, roofY) === 11) return 1
+ if (this.getTopBlockAt(x + width, y, roofY) === 11) return 2
+ if (this.getTopBlockAt(x + width, y + height, roofY) === 11) return 3
+ if (this.getTopBlockAt(x, y + height, roofY) === 11) return 4
+ } else {
+ let one = this.getTopBlockAt2(x + width / 2 + 1, y + height / 2, roofY)
+ let two = this.getTopBlockAt2(x + width / 2 - 1, y + height / 2, roofY)
+ let three = this.getTopBlockAt2(x + width / 2, y + height / 2 + 1, roofY)
+ let four = this.getTopBlockAt2(x + width / 2, y + height / 2 - 1, roofY)
+
+ if (one === 0 && three === 0) return 1
+ if (two === 0 && three === 0) return 2
+ if (one === 0 && four === 0) return 3
+ if (two === 0 && four === 0) return 4
+ }
+
+ return -1
+ }
+
+ getRoomWorldData() {
+ let x = Math.floor((Player.getX() + 8) / 32) * 32 - 8
+ let y = Math.floor((Player.getZ() + 8) / 32) * 32 - 8
+ let width = 30
+ let height = 30
+
+ let roofY = this.getRoofAt(x, y)
+
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y)).getBlockId() !== 0) {
+ x -= 32
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x, roofY, y - 1)).getBlockId() !== 0) {
+ y -= 32
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y)).getBlockId() !== 0) { //second iteration incase of L shape
+ x -= 32
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width + 1, roofY, y)).getBlockId() !== 0) {
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x, roofY, y + height + 1)).getBlockId() !== 0) {
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width, roofY, y + height + 1)).getBlockId() !== 0) { //second iteration incase of L shape
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width + 1, roofY, y + height)).getBlockId() !== 0) { //second iteration incase of L shape
+ width += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x + width, roofY, y - 1)).getBlockId() !== 0) {//second iteration incase of L shape
+ y -= 32
+ height += 32
+ }
+ while (World.getBlockStateAt(new BlockPos(x - 1, roofY, y + height)).getBlockId() !== 0) { //third iteration incase of L shape
+ x -= 32
+ width += 32
+ }
+
+
+ return {
+ x,
+ y,
+ width,
+ height,
+ cx: x + width / 2,
+ cy: y + height / 2,
+ rotation: this.getRotation(x, y, width, height, roofY)
+ }
+ }
+
+ getRoofAt(x, z) {
+ let y = 255
+ while (y > 0 && World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId() === 0) y--
+
+ return y
+ }
+
+ getTopBlockAt(x, z, y) {
+ if (!y) y = this.getHeightAt(x, z)
+
+ return World.getBlockStateAt(new BlockPos(x, y, z)).getMetadata()
+ }
+ getTopBlockAt2(x, z, y) {
+ if (!y) y = this.getHeightAt(x, z)
+
+ return World.getBlockStateAt(new BlockPos(x, y, z)).getBlockId()
+ }
+
+ getCurrentRoomData() {
+ let roomId = this.idMap.get(this.getCurrentRoomId())
+ if (roomId === undefined) return null
+
+ return this.fullRoomData[roomId]
+ }
+
+ getCurrentRoomId() {
+ let id = Scoreboard.getLineByIndex(Scoreboard.getLines().length - 1).getName().trim().split(" ").pop()
+
+ return id
+ }
+
+ worldLoad() {
+
+ }
+
+ entityJoinWorldEvent(event) {
+ if (event.entity instanceof EntityItem) {
+ // console.log("Blaze joined world")
+ let e = new Entity(event.entity)
+ let pos = [e.getX(), e.getY(), e.getZ()]
+
+ this.tempItemIdLocs.set(event.entity[m.getEntityId.Entity](), pos)
+ // console.log(event.entity[m.getEntityId.Entity]())
+ }
+ }
+
+ pickupItem(packet) {
+ let packetType = new String(packet.class.getSimpleName()).valueOf()
+ if (packetType === "S0DPacketCollectItem") {
+ let pos = this.tempItemIdLocs.get(packet[m.getCollectedItemEntityID]())
+
+ let data = this.currentRouteDisplay[this.currentActionIndex].interacts[0]
+ if (data && data.type === "item") {
+ let coords = this.toRoomCoordinates(data.loc[0], data.loc[1], data.loc[2])
+ if (calculateDistanceQuick(pos, coords) < 2) {
+ if (this.currentRouteDisplay.length >= this.currentActionIndex) {
+ this.currentActionIndex++
+ }
+ }
+ }
+
+ if (!this.recordRoute) return
+ if (pos) this.addRecordingPoint("interacts", { loc: pos, type: "item" })
+ }
+ }
+
+ playerInteract(action, position, event) {
+ if (action.toString() !== "RIGHT_CLICK_BLOCK") return
+
+ let pos = [Player.lookingAt().getX() + 0.5, Player.lookingAt().getY(), Player.lookingAt().getZ() + 0.5]
+
+ let id = Player.lookingAt().getType().getID()
+ if (id !== 54 && id !== 144 && id !== 69) return
+
+
+ let data = this.currentRouteDisplay[this.currentActionIndex].interacts[0]
+ if (data && data.type === "interact") {
+ let coords = this.toRoomCoordinates(data.loc[0], data.loc[1], data.loc[2])
+ console.log(coords.join(","), pos.join(","))
+ if (coords.join(",") === pos.join(",")) {
+ if (this.currentRouteDisplay.length > this.currentActionIndex) {
+ this.currentActionIndex++
+ }
+ }
+ }
+
+ if (!this.recordRoute) return
+ this.addRecordingPoint("interacts", { loc: pos, type: "interact" })
+ }
+
+ playSound(pos, name, volume, pitch, categoryName, event) {
+ if (!this.recordRoute) return
+
+ let loc = [pos.x, pos.y, pos.z]
+ let nameSplitted = name.split(".")
+ if (name === "mob.enderdragon.hit") { //etherwarp
+ this.addRecordingPoint("etherwarps", loc)
+ }
+ if (name === "random.explode" && pitch !== 1) { //tnt OR MIGHT BE spirit scepter
+ this.addRecordingPoint("tnts", loc)
+ }
+ if (name === "mob.bat.death") {
+ this.addRecordingPoint("interacts", { loc: loc, type: "bat" })
+ }
+ if (nameSplitted[0] === "dig") { //mining block
+ if (!this.recordingData[this.recordingData.length - 1].mines.some(a =>
+ a[0] === pos[0]
+ && a[1] === pos[1]
+ && a[2] === pos[2]
+ )) {
+ this.addRecordingPoint("mines", loc)
+ }
+ }
+ }
+
+ onDisable() {
+
+ }
+
+}
+
+module.exports = {
+ class: new DungeonRoutes()
+}
diff --git a/src/features/dungeonRoutes/metadata.json b/src/features/dungeonRoutes/metadata.json
new file mode 100644
index 0000000..c2f2a63
--- /dev/null
+++ b/src/features/dungeonRoutes/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Dungeon Routes",
+ "description": "Routes for dungeons (Coming soontm maby)",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/dungeonRoutes/routesData.json b/src/features/dungeonRoutes/routesData.json
new file mode 100644
index 0000000..d701fc7
--- /dev/null
+++ b/src/features/dungeonRoutes/routesData.json
@@ -0,0 +1,1296 @@
+[
+ {
+ "index": 84,
+ "data": [
+ {
+ "etherwarps": [
+ [
+ 23.5,
+ 83,
+ 12.5
+ ]
+ ],
+ "mines": [],
+ "locations": [
+ [
+ -4,
+ 68,
+ 16
+ ],
+ [
+ 23,
+ 83,
+ 13
+ ],
+ [
+ 24,
+ 84,
+ 13
+ ],
+ [
+ 24,
+ 85,
+ 13
+ ],
+ [
+ 25,
+ 85,
+ 13
+ ],
+ [
+ 25,
+ 84,
+ 12
+ ],
+ [
+ 25,
+ 84,
+ 11
+ ],
+ [
+ 26,
+ 84,
+ 11
+ ],
+ [
+ 27,
+ 84,
+ 9
+ ],
+ [
+ 26,
+ 84,
+ 6
+ ],
+ [
+ 23,
+ 84,
+ 5
+ ],
+ [
+ 19,
+ 84,
+ 5
+ ],
+ [
+ 19,
+ 85,
+ 5
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ 17.5,
+ 84,
+ 5.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [
+ [
+ -1.5,
+ 85,
+ 23.5
+ ]
+ ],
+ "mines": [
+ [
+ -2.5,
+ 84.5,
+ 23.5
+ ]
+ ],
+ "locations": [
+ [
+ 19,
+ 85,
+ 5
+ ],
+ [
+ 21,
+ 85,
+ 5
+ ],
+ [
+ 22,
+ 84,
+ 5
+ ],
+ [
+ 25,
+ 84,
+ 7
+ ],
+ [
+ 26,
+ 84,
+ 10
+ ],
+ [
+ 25,
+ 84,
+ 13
+ ],
+ [
+ 24,
+ 85,
+ 13
+ ],
+ [
+ 24,
+ 84,
+ 14
+ ],
+ [
+ 23,
+ 83,
+ 14
+ ],
+ [
+ -2,
+ 85,
+ 24
+ ],
+ [
+ -2,
+ 84,
+ 24
+ ],
+ [
+ -3,
+ 84,
+ 24
+ ],
+ [
+ -6,
+ 84,
+ 24
+ ],
+ [
+ -8,
+ 83,
+ 24
+ ],
+ [
+ -8,
+ 81,
+ 24
+ ],
+ [
+ -8,
+ 84,
+ 24
+ ],
+ [
+ -11,
+ 83,
+ 24
+ ],
+ [
+ -15,
+ 83,
+ 24
+ ],
+ [
+ -18,
+ 83,
+ 24
+ ],
+ [
+ -21,
+ 83,
+ 25
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ -21.5,
+ 83,
+ 25.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [
+ [
+ -18.5,
+ 91,
+ 24.5
+ ],
+ [
+ -9.5,
+ 83,
+ 2.5
+ ]
+ ],
+ "mines": [],
+ "locations": [
+ [
+ -21,
+ 83,
+ 25
+ ],
+ [
+ -19,
+ 92,
+ 25
+ ],
+ [
+ -19,
+ 91,
+ 25
+ ],
+ [
+ -17,
+ 91,
+ 22
+ ],
+ [
+ -17,
+ 91,
+ 21
+ ],
+ [
+ -15,
+ 91,
+ 18
+ ],
+ [
+ -16,
+ 91,
+ 21
+ ],
+ [
+ -16,
+ 91,
+ 19
+ ],
+ [
+ -15,
+ 90,
+ 17
+ ],
+ [
+ -15,
+ 88,
+ 16
+ ],
+ [
+ -14,
+ 86,
+ 13
+ ],
+ [
+ -15,
+ 86,
+ 10
+ ],
+ [
+ -15,
+ 84,
+ 8
+ ],
+ [
+ -15,
+ 83,
+ 6
+ ],
+ [
+ -16,
+ 83,
+ 4
+ ],
+ [
+ -10,
+ 84,
+ 3
+ ],
+ [
+ -10,
+ 83,
+ 3
+ ],
+ [
+ -8,
+ 83,
+ 3
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ -4.5,
+ 84,
+ 1.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [
+ [
+ -4.5,
+ 88,
+ 1.5
+ ],
+ [
+ -4.5,
+ 92,
+ 1.5
+ ]
+ ],
+ "mines": [],
+ "locations": [
+ [
+ -8,
+ 83,
+ 3
+ ],
+ [
+ -5,
+ 89,
+ 2
+ ],
+ [
+ -5,
+ 88,
+ 2
+ ],
+ [
+ -5,
+ 92,
+ 2
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ -4.5,
+ 93,
+ 1.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [],
+ "mines": [],
+ "locations": [
+ [
+ -5,
+ 92,
+ 2
+ ],
+ [
+ -8,
+ 92,
+ 2
+ ],
+ [
+ -11,
+ 92,
+ 1
+ ],
+ [
+ -11,
+ 96,
+ 1
+ ],
+ [
+ -11,
+ 97,
+ 1
+ ],
+ [
+ -13,
+ 96,
+ 1
+ ],
+ [
+ -13,
+ 97,
+ 1
+ ],
+ [
+ -13,
+ 96,
+ 1
+ ],
+ [
+ -13,
+ 92,
+ 1
+ ],
+ [
+ -13,
+ 92,
+ 2
+ ],
+ [
+ -13,
+ 92,
+ 1
+ ],
+ [
+ -13,
+ 93,
+ 1
+ ],
+ [
+ -13,
+ 92,
+ 1
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ -16.5,
+ 92,
+ 0.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [
+ [
+ -6.5,
+ 91,
+ 8.5
+ ]
+ ],
+ "mines": [],
+ "locations": [
+ [
+ -13,
+ 92,
+ 1
+ ],
+ [
+ -13,
+ 93,
+ 1
+ ],
+ [
+ -13,
+ 93,
+ 2
+ ],
+ [
+ -14,
+ 92,
+ 2
+ ],
+ [
+ -15,
+ 92,
+ 2
+ ],
+ [
+ -16,
+ 92,
+ 2
+ ],
+ [
+ -18,
+ 92,
+ 3
+ ],
+ [
+ -18,
+ 92,
+ 4
+ ],
+ [
+ -18,
+ 92,
+ 5
+ ],
+ [
+ -18,
+ 93,
+ 5
+ ],
+ [
+ -18,
+ 94,
+ 5
+ ],
+ [
+ -18,
+ 93,
+ 8
+ ],
+ [
+ -18,
+ 92,
+ 9
+ ],
+ [
+ -18,
+ 90,
+ 10
+ ],
+ [
+ -18,
+ 87,
+ 11
+ ],
+ [
+ -18,
+ 84,
+ 11
+ ],
+ [
+ -18,
+ 83,
+ 11
+ ],
+ [
+ -7,
+ 91,
+ 9
+ ],
+ [
+ -8,
+ 91,
+ 8
+ ],
+ [
+ -8,
+ 93,
+ 8
+ ],
+ [
+ -8,
+ 92,
+ 8
+ ],
+ [
+ -10,
+ 92,
+ 10
+ ],
+ [
+ -10,
+ 90,
+ 10
+ ],
+ [
+ -10,
+ 87,
+ 11
+ ],
+ [
+ -8,
+ 86,
+ 11
+ ],
+ [
+ -7,
+ 85,
+ 12
+ ],
+ [
+ -7,
+ 83,
+ 12
+ ],
+ [
+ -5,
+ 83,
+ 10
+ ],
+ [
+ -1,
+ 83,
+ 11
+ ],
+ [
+ 3,
+ 83,
+ 12
+ ],
+ [
+ 7,
+ 83,
+ 12
+ ],
+ [
+ 9,
+ 82,
+ 12
+ ],
+ [
+ 11,
+ 80,
+ 13
+ ],
+ [
+ 12,
+ 77,
+ 13
+ ],
+ [
+ 13,
+ 73,
+ 14
+ ],
+ [
+ 14,
+ 68,
+ 14
+ ],
+ [
+ 15,
+ 62,
+ 15
+ ],
+ [
+ 15,
+ 55,
+ 15
+ ],
+ [
+ 15,
+ 48,
+ 16
+ ],
+ [
+ 13,
+ 46,
+ 16
+ ],
+ [
+ 10,
+ 46,
+ 17
+ ],
+ [
+ 7,
+ 46,
+ 17
+ ],
+ [
+ 3,
+ 46,
+ 17
+ ],
+ [
+ -1,
+ 46,
+ 16
+ ],
+ [
+ -6,
+ 46,
+ 16
+ ],
+ [
+ -9,
+ 45,
+ 15
+ ],
+ [
+ -12,
+ 45,
+ 14
+ ],
+ [
+ -17,
+ 47,
+ 13
+ ],
+ [
+ -20,
+ 48,
+ 11
+ ],
+ [
+ -23,
+ 48,
+ 10
+ ],
+ [
+ -24,
+ 49,
+ 10
+ ],
+ [
+ -24,
+ 48,
+ 10
+ ],
+ [
+ -24,
+ 47,
+ 10
+ ],
+ [
+ -25,
+ 48,
+ 10
+ ],
+ [
+ -25,
+ 47,
+ 10
+ ],
+ [
+ -25,
+ 36,
+ 10
+ ],
+ [
+ -25,
+ 35,
+ 10
+ ],
+ [
+ -25,
+ 34,
+ 10
+ ],
+ [
+ -25,
+ 33,
+ 10
+ ],
+ [
+ -14,
+ 29,
+ 10
+ ],
+ [
+ -14,
+ 28,
+ 10
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ -9.5,
+ 28,
+ 9.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [],
+ "mines": [],
+ "locations": [
+ [
+ -14,
+ 28,
+ 10
+ ],
+ [
+ -25,
+ 34,
+ 10
+ ],
+ [
+ -25,
+ 35,
+ 10
+ ],
+ [
+ -25,
+ 36,
+ 10
+ ],
+ [
+ -25,
+ 47,
+ 10
+ ],
+ [
+ -25,
+ 48,
+ 10
+ ],
+ [
+ -24,
+ 48,
+ 10
+ ],
+ [
+ -24,
+ 49,
+ 10
+ ],
+ [
+ -23,
+ 48,
+ 10
+ ],
+ [
+ -21,
+ 48,
+ 10
+ ],
+ [
+ -20,
+ 47,
+ 10
+ ],
+ [
+ -19,
+ 47,
+ 11
+ ],
+ [
+ -20,
+ 47,
+ 10
+ ],
+ [
+ -20,
+ 47,
+ 9
+ ],
+ [
+ -14,
+ 48,
+ 1
+ ],
+ [
+ -13,
+ 48,
+ 0
+ ],
+ [
+ -14,
+ 49,
+ -3
+ ],
+ [
+ -15,
+ 49,
+ -7
+ ],
+ [
+ -15,
+ 49,
+ -11
+ ],
+ [
+ -14,
+ 49,
+ -15
+ ],
+ [
+ -13,
+ 49,
+ -19
+ ],
+ [
+ -13,
+ 49,
+ -23
+ ],
+ [
+ -13,
+ 50,
+ -24
+ ],
+ [
+ -13,
+ 50,
+ -26
+ ],
+ [
+ -13,
+ 50,
+ -28
+ ],
+ [
+ -11,
+ 50,
+ -27
+ ],
+ [
+ -9,
+ 50,
+ -27
+ ]
+ ],
+ "interacts": [
+ {
+ "loc": [
+ -3.5,
+ 50,
+ -26.5
+ ],
+ "type": "interact"
+ }
+ ],
+ "tnts": []
+ },
+ {
+ "etherwarps": [],
+ "mines": [],
+ "locations": [
+ [
+ -9,
+ 50,
+ -27
+ ],
+ [
+ -10,
+ 50,
+ -28
+ ],
+ [
+ -11,
+ 50,
+ -29
+ ],
+ [
+ -13,
+ 50,
+ -26
+ ],
+ [
+ -13,
+ 50,
+ -24
+ ],
+ [
+ -14,
+ 50,
+ -22
+ ],
+ [
+ -16,
+ 50,
+ -20
+ ],
+ [
+ -18,
+ 49,
+ -19
+ ],
+ [
+ -21,
+ 50,
+ -16
+ ],
+ [
+ -23,
+ 52,
+ -14
+ ],
+ [
+ -25,
+ 53,
+ -10
+ ],
+ [
+ -26,
+ 55,
+ -8
+ ],
+ [
+ -26,
+ 55,
+ -5
+ ],
+ [
+ -26,
+ 55,
+ -3
+ ],
+ [
+ -24,
+ 55,
+ -3
+ ],
+ [
+ -20,
+ 55,
+ -3
+ ],
+ [
+ -17,
+ 55,
+ -2
+ ],
+ [
+ -13,
+ 55,
+ -2
+ ],
+ [
+ -10,
+ 55,
+ -3
+ ],
+ [
+ -9,
+ 55,
+ -2
+ ],
+ [
+ -9,
+ 67,
+ -7
+ ],
+ [
+ -9,
+ 66,
+ -7
+ ],
+ [
+ -9,
+ 68,
+ -9
+ ],
+ [
+ -9,
+ 69,
+ -12
+ ],
+ [
+ -10,
+ 69,
+ -16
+ ],
+ [
+ -14,
+ 69,
+ -17
+ ],
+ [
+ -16,
+ 69,
+ -17
+ ],
+ [
+ -17,
+ 69,
+ -17
+ ],
+ [
+ -18,
+ 69,
+ -16
+ ]
+ ],
+ "interacts": [],
+ "tnts": []
+ }
+ ]
+ },
+ {
+ "index": 12,
+ "data": [
+ {
+ "etherwarps": [
+ [
+ 2.5,
+ 80,
+ -1.5
+ ]
+ ],
+ "mines": [
+ [
+ 1.5,
+ 83.5,
+ -0.5
+ ]
+ ],
+ "locations": [
+ [
+ -7,
+ 69,
+ 3
+ ],
+ [
+ -6,
+ 69,
+ 3
+ ],
+ [
+ -3,
+ 69,
+ 0
+ ],
+ [
+ -1,
+ 69,
+ -2
+ ],
+ [
+ 0,
+ 69,
+ -4
+ ],
+ [
+ 1,
+ 69,
+ -5
+ ],
+ [
+ 3,
+ 69,
+ -7
+ ],
+ [
+ 3,
+ 69,
+ -10
+ ],
+ [
+ 5,
+ 69,
+ -12
+ ],
+ [
+ 8,
+ 69,
+ -12
+ ],
+ [
+ 11,
+ 69,
+ -10
+ ],
+ [
+ 11,
+ 69,
+ -8
+ ],
+ [
+ 11,
+ 69,
+ -9
+ ],
+ [
+ 10,
+ 69,
+ -12
+ ],
+ [
+ 7,
+ 69,
+ -12
+ ],
+ [
+ 3,
+ 69,
+ -11
+ ],
+ [
+ 2,
+ 69,
+ -8
+ ],
+ [
+ 1,
+ 69,
+ -4
+ ],
+ [
+ 1,
+ 69,
+ -3
+ ],
+ [
+ 2,
+ 69,
+ -3
+ ],
+ [
+ 2,
+ 80,
+ -1
+ ],
+ [
+ 1,
+ 80,
+ 0
+ ],
+ [
+ 1,
+ 80,
+ 1
+ ],
+ [
+ 1,
+ 80,
+ 0
+ ],
+ [
+ 1,
+ 80,
+ 1
+ ],
+ [
+ 0,
+ 80,
+ 3
+ ],
+ [
+ -1,
+ 78,
+ 5
+ ],
+ [
+ -2,
+ 76,
+ 5
+ ],
+ [
+ -3,
+ 75,
+ 5
+ ],
+ [
+ -4,
+ 73,
+ 5
+ ],
+ [
+ -4,
+ 70,
+ 5
+ ],
+ [
+ -5,
+ 69,
+ 5
+ ]
+ ],
+ "interacts": [],
+ "tnts": [
+ [
+ 3.5,
+ 70.5,
+ -7.5
+ ]
+ ]
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/src/features/dungeonSolvers/f7data.json b/src/features/dungeonSolvers/f7data.json
new file mode 100644
index 0000000..72e3ec6
--- /dev/null
+++ b/src/features/dungeonSolvers/f7data.json
@@ -0,0 +1,263 @@
+[
+ {
+ "type": "device",
+ "location": [
+ 110,
+ 118,
+ 91
+ ],
+ "phase": 0
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 110,
+ 112,
+ 73
+ ],
+ "phase": 0
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 110,
+ 118,
+ 79
+ ],
+ "phase": 0
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 90,
+ 111,
+ 92
+ ],
+ "phase": 0
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 90,
+ 121,
+ 101
+ ],
+ "phase": 0
+ },
+ {
+ "type": "lever",
+ "location": [
+ 106,
+ 124,
+ 113
+ ],
+ "phase": 0
+ },
+ {
+ "type": "lever",
+ "location": [
+ 94,
+ 124,
+ 113
+ ],
+ "phase": 0
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 68,
+ 108,
+ 122
+ ],
+ "phase": 1
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 59,
+ 119,
+ 123
+ ],
+ "phase": 1
+ },
+ {
+ "type": "device",
+ "location": [
+ 60,
+ 131,
+ 142
+ ],
+ "phase": 1
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 47,
+ 108,
+ 122
+ ],
+ "phase": 1
+ },
+ {
+ "type": "lever",
+ "location": [
+ 27,
+ 124,
+ 127
+ ],
+ "phase": 1
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 40,
+ 123,
+ 123
+ ],
+ "phase": 1
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 39,
+ 107,
+ 142
+ ],
+ "phase": 1
+ },
+ {
+ "type": "lever",
+ "location": [
+ 23,
+ 132,
+ 138
+ ],
+ "phase": 1
+ },
+ {
+ "type": "terminal",
+ "location": [
+ -1,
+ 108,
+ 112
+ ],
+ "phase": 2
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 18,
+ 122,
+ 93
+ ],
+ "phase": 2
+ },
+ {
+ "type": "terminal",
+ "location": [
+ -1,
+ 118,
+ 93
+ ],
+ "phase": 2
+ },
+ {
+ "type": "terminal",
+ "location": [
+ -1,
+ 108,
+ 77
+ ],
+ "phase": 2
+ },
+ {
+ "type": "device",
+ "location": [
+ -3,
+ 120,
+ 77
+ ],
+ "phase": 2
+ },
+ {
+ "type": "lever",
+ "location": [
+ 14,
+ 122,
+ 55
+ ],
+ "phase": 2
+ },
+ {
+ "type": "lever",
+ "location": [
+ 2,
+ 122,
+ 55
+ ],
+ "phase": 2
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 44,
+ 120,
+ 30
+ ],
+ "phase": 3
+ },
+ {
+ "type": "device",
+ "location": [
+ 63,
+ 126,
+ 34
+ ],
+ "phase": 3
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 72,
+ 114,
+ 47
+ ],
+ "phase": 3
+ },
+ {
+ "type": "lever",
+ "location": [
+ 86,
+ 128,
+ 46
+ ],
+ "phase": 3
+ },
+ {
+ "type": "lever",
+ "location": [
+ 84,
+ 121,
+ 34
+ ],
+ "phase": 3
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 41,
+ 108,
+ 30
+ ],
+ "phase": 3
+ },
+ {
+ "type": "terminal",
+ "location": [
+ 67,
+ 108,
+ 30
+ ],
+ "phase": 3
+ }
+] \ No newline at end of file
diff --git a/src/features/dungeonSolvers/index.js b/src/features/dungeonSolvers/index.js
new file mode 100644
index 0000000..a959a66
--- /dev/null
+++ b/src/features/dungeonSolvers/index.js
@@ -0,0 +1,1270 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { f, m } from "../../../mappings/mappings";
+import Feature from "../../featureClass/class";
+import { addNotation, numberWithCommas } from "../../utils/numberUtils";
+import * as renderUtils from "../../utils/renderUtils";
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import ToggleSetting from "../settings/settingThings/toggle";
+import TextSetting from "../settings/settingThings/textSetting";
+import { fetch } from "../../utils/networkUtils";
+import { delay } from "../../utils/delayUtils";
+import { Waypoint } from "../../utils/renderJavaUtils";
+import { calculateDistanceQuick } from "../../utils/utils";
+import { drawLinePoints } from "../../utils/renderUtils";
+
+const entityGuardian = Java.type("net.minecraft.entity.monster.EntityGuardian")
+
+const MCBlock = Java.type("net.minecraft.block.Block");
+const EntityBlaze = Java.type("net.minecraft.entity.monster.EntityBlaze");
+let translate;
+try {
+ translate = net.minecraft.util.StringTranslate.func_74808_a();
+} catch (e) {
+ //player doesent have translate (using english default)
+}
+let wrongColorList = {
+ "light grey": "silver",
+ "wool": "white wool",
+ "ink": "black ink",
+ "lapis": "blue lapis",
+ "cocoa": "brown cocoa"
+}
+
+class DungeonSolvers extends Feature {
+ constructor() {
+ super();
+ }
+
+ isInDungeon() {
+ if (!this.FeatureManager || !this.FeatureManager.features["dataLoader"]) return false
+ return this.FeatureManager.features["dataLoader"].class.isInDungeon
+ }
+
+ onEnable() {
+ this.initVariables();
+
+ this.lividData = {};
+ this.lividData.lividColor = {
+ Vendetta: "&f",
+ Crossed: "&d",
+ Hockey: "&c",
+ Doctor: "&7",
+ Frog: "&2",
+ Smile: "&a",
+ Scream: "&9",
+ Purple: "&5",
+ Arcade: "&e",
+ };
+
+ this.bonzoMaskTimer = 0;
+ this.fraggedBonzoMaskTimer = 0;
+ this.spiritMaskTimer = 0;
+ this.eraseBonzoTimer = true;
+ this.bonzoMaskCooldown = 0;
+
+ this.lastWorldload = Date.now()
+ this.lividFindEnabled = new ToggleSetting("Correct livid finder", "Finds the real livid to kill in the f5 boss fight", true, "livid_find_enabled", this);
+ this.lividFindHud = new ToggleSetting("Show Livid Hp", "Shows the nametag of the correct livid", true, "livid_hud_enabled", this).requires(this.lividFindEnabled);
+ this.lividHpElement = new HudTextElement().setToggleSetting(this.lividFindHud).setLocationSetting(new LocationSetting("Correct Livid Hp Location", "Allows you to edit the location of the correct livid hp text", "livid_hp_location", this, [10, 50, 1, 1]).requires(this.lividFindHud).editTempText("§r§e﴾ §c§lLivid§r §a7M§c❤ §e﴿§r"));
+
+ this.hudElements.push(this.lividHpElement);
+
+ this.lividFindBox = new ToggleSetting("Put a box around the correct livid", "This helps to locate it in the group", true, "livid_box_enabled", this).requires(this.lividFindEnabled);
+ this.lividFindNametags = new ToggleSetting("Hide the nametags of incorrect livids", "This helps to locate it in the group", true, "livid_nametags_enabled", this).requires(this.lividFindEnabled);
+
+ this.spiritBowDestroyTimer = new ToggleSetting("Timer for when the spirit bow will self destruct", "", true, "spirit_bow_destroy_timer", this);
+ this.spiritBowDestroyElement = new HudTextElement().setToggleSetting(this.spiritBowDestroyTimer).setLocationSetting(new LocationSetting("Spirit bow destroy timer location", "Allows you to edit the location of the timer", "spirit_destroy_location", this, [10, 70, 3, 1]).requires(this.spiritBowDestroyTimer).editTempText("&dBow Destroyed in: &c15s"));
+
+ this.spiritBearSpawnTimer = new ToggleSetting("Timer for when the spirit bear will spawn", "", true, "spirit_bear_spawn_timer", this);
+ this.spiritBearSpawnElement = new HudTextElement().setToggleSetting(this.spiritBearSpawnTimer).setLocationSetting(new LocationSetting("Spirit bear spawn timer location", "Allows you to edit the location of the timer", "spirit_bear_spawn_location", this, [10, 70, 3, 1]).requires(this.spiritBearSpawnTimer).editTempText("&dBear spawned in: &c1.57s"));
+
+ this.fireFreezeTimer = new ToggleSetting("Timer for when to fire freeze in m3/f3", "", true, "ff_timer", this);
+ this.fireFreezeTimerElement = new HudTextElement().setToggleSetting(this.fireFreezeTimer).setLocationSetting(new LocationSetting("Fire freeze timer location", "Allows you to edit the location of the timer", "fire_freeze_location", this, [10, 80, 3, 1]).requires(this.fireFreezeTimer).editTempText("&dFire freeze in: &c1.57s"));
+
+ this.hudElements.push(this.spiritBearSpawnElement);
+ this.hudElements.push(this.fireFreezeTimerElement);
+ this.hudElements.push(this.spiritBowDestroyElement);
+
+ this.bloodCampAssist = new ToggleSetting("Assist blood camp", "Helps guess where and when blood mobs will spawn", true, "blood_camp_assist", this);
+
+ this.runSpeedRates = new ToggleSetting("Show run speed and exp rates", "(Run speed includes downtime inbetween runs, only shows while doing dungeon runs)", true, "run_speed_rates", this);
+ this.runSpeedRatesElement = new HudTextElement()
+ .setText("&6Run speed&7> &fLoading...\n&6Exp/hour&7> &fLoading...\n&6Runs/hour&7> &fLoading...")
+ .setToggleSetting(this.runSpeedRates)
+ .setLocationSetting(new LocationSetting("Run speed and exp rates location", "Allows you to edit the location of the information", "run_speed_rates_location", this, [10, 100, 1, 1]).requires(this.runSpeedRates).editTempText("&6Run speed&7> &f4:30\n&6Exp/hour&7> &f1,234,567\n&6Runs/hour&7> &f17"));
+
+ this.scoreCalculation = new ToggleSetting("Show score calculation", "", true, "run_score_calc", this);
+ this.scoreElement = new HudTextElement().setToggleSetting(this.scoreCalculation).setLocationSetting(new LocationSetting("Score calculation location", "Allows you to edit the location of the score calc", "score_calc_location", this, [10, 130, 1, 1]).requires(this.scoreCalculation).editTempText("&dScore: 120\n&aS+ ??\n&aS ??"));
+
+ this.hudElements.push(this.runSpeedRatesElement);
+ this.hudElements.push(this.scoreElement);
+
+ this.blazeSolver = new ToggleSetting("Blaze Puzzle Solver", "Shows what order to kill the blazes in", true, "blaze_solver", this);
+ this.respawnTimerTerra = new ToggleSetting("Terracotter respawn timer", "", true, "f6_timer_thing", this);
+
+ this.lastDungFinishes = [];
+ this.lastDungExps = [];
+ this.registerChat("${start}+&r&3${exp} Catacombs Experience&r", (start, exp) => {
+ if (ChatLib.removeFormatting(start).replace(/ /gi, "").length > 0) return;
+ this.lastDungExps.push(parseFloat(exp.replace(/,/gi, "")));
+ if (this.lastDungExps.length > 5) {
+ this.lastDungExps.shift();
+ }
+
+ this.lastDungFinishes.push(Date.now());
+ if (this.lastDungFinishes.length > 5) {
+ this.lastDungFinishes.shift();
+ }
+
+ this.bonzoMaskTimer = 0;
+ this.fraggedBonzoMaskTimer = 0;
+ this.spiritMaskTimer = 0;
+ this.eraseBonzoTimer = true;
+ });
+ this.registerStep(false, 30, this.getBonzoMaskCooldown);
+ this.bonzoSpiritMaskTimer = new ToggleSetting("Timer for when bonzo/spirit mask will be ready", "works for both bonzo masks, hides bonzo masks' timers after you leave the run", false, "bonzo_mask_timer", this);
+ this.bonzoSpiritMaskTimerElement = new HudTextElement().setToggleSetting(this.bonzoSpiritMaskTimer).setLocationSetting(new LocationSetting("Bonzo/Spirit Mask timer location", "Allows you to edit the location of the timer", "bonzo_mask_timer_location", this, [10, 100, 1, 1]).requires(this.bonzoSpiritMaskTimer).editTempText("&9Bonzo's Mask: &c157s"));
+ this.hudElements.push(this.bonzoSpiritMaskTimerElement);
+ this.spiritMaskOutsideDungeon = new ToggleSetting("Spirit Mask Timer Outside Dungeons", "should spirit mask timer be shown outside dungeon?", false, "spirit_mask_timer", this).requires(this.bonzoSpiritMaskTimer);
+
+ this.normalBonzoMask = new ToggleSetting("Bonzo Mask Proc'ed Alert", "Enable this to change the message", true, "bonzo_mask_alert", this).requires(this.bonzoSpiritMaskTimer);
+ this.normalBonzoMaskMessage = new TextSetting("Bonzo Mask Proc'ed Message", "change bonzo mask proc message here", "&cBonzo Mask Used", "bonzo_mask_alert_message", this, "&cBonzo Mask Used", false).requires(this.normalBonzoMask);
+ this.fraggedBonzoMask = new ToggleSetting("⚚ Bonzo Mask Proc'ed Alert", "Enable this to change the message", true, "fragged_bonzo_mask_alert", this).requires(this.bonzoSpiritMaskTimer);
+ this.fraggedBonzoMaskMessage = new TextSetting("⚚ Bonzo Mask Proc'ed Message", "change bonzo mask proc message here", "&c⚚ Bonzo Mask Used", "fragged_bonzo_mask_alert_message", this, "&c⚚ Bonzo Mask Used", false).requires(this.fraggedBonzoMask);
+ this.spiritMask = new ToggleSetting("Spirit Mask Proc'ed Alert", "Enable this to change the message", true, "spirit_mask_alert", this).requires(this.bonzoSpiritMaskTimer);
+ this.spiritMaskMessage = new TextSetting("Spirit Mask Proc'ed Message", "change spirit mask proc message here", "&cSpirit Mask Used", "spirit_mask_alert_message", this, "&cSpirit Mask Used", false).requires(this.spiritMask);
+
+ this.registerChat("&r&aYour &r&9Bonzo's Mask &r&asaved your life!&r", () => {
+ this.eraseBonzoTimer = false;
+ if (this.bonzoSpiritMaskTimer.getValue()) {
+ this.bonzoMaskTimer = Date.now() + this.bonzoMaskCooldown * 1000;
+ }
+ if (this.normalBonzoMask.getValue()) {
+ let m = this.normalBonzoMaskMessage.getValue()
+ Client.showTitle(m ? m : "&cBonzo Mask Used", "", 0, 60, 10)
+ }
+ })
+
+ this.registerChat("&r&aYour &r&9⚚ Bonzo's Mask &r&asaved your life!&r", () => {
+ this.eraseBonzoTimer = false;
+ if (this.bonzoSpiritMaskTimer.getValue()) {
+ this.fraggedBonzoMaskTimer = Date.now() + this.bonzoMaskCooldown * 1000;
+ }
+ if (this.fraggedBonzoMask.getValue()) {
+ let m = this.fraggedBonzoMaskMessage.getValue()
+ Client.showTitle(m ? m : "&c⚚ Bonzo Mask Used", "", 0, 60, 10)
+ }
+ })
+
+ this.registerChat("&r&6Second Wind Activated&r&a! &r&aYour Spirit Mask saved your life!&r", () => {
+ if (this.bonzoSpiritMaskTimer.getValue()) {
+ if (this.spiritMaskOutsideDungeon.getValue() ? true : !this.eraseBonzoTimer) {
+ this.spiritMaskTimer = Date.now() + 30 * 1000;
+ }
+ }
+ if (this.fraggedBonzoMask.getValue()) {
+ let m = this.spiritMaskMessage.getValue()
+ Client.showTitle((m ? m : "&cSpirit Mask Used"), "", 0, 60, 10)
+ }
+ })
+
+ this.registerStep(true, 10, () => {
+ if (!this.bonzoSpiritMaskTimer.getValue()) return
+ let timerText = ""
+ if (this.spiritMaskOutsideDungeon.getValue() ? true : !this.eraseBonzoTimer) {
+ if (this.spiritMask.getValue()) {
+ let timer1 = Math.round((this.spiritMaskTimer - Date.now()) / 100) / 10
+ timerText += "&5Spirit Mask: " + (timer1 > 0 ? "&c" + timer1 + "&6s" : "&6READY") + "\n"
+ }
+ }
+ if (!this.eraseBonzoTimer) {
+ if (this.normalBonzoMask.getValue()) {
+ let timer2 = Math.round((this.bonzoMaskTimer - Date.now()) / 100) / 10
+ timerText += "&9Bonzo's Mask: " + (timer2 > 0 ? "&c" + timer2 + "&6s" : "&6READY") + "\n"
+ }
+ if (this.fraggedBonzoMask.getValue()) {
+ let timer3 = Math.round((this.fraggedBonzoMaskTimer - Date.now()) / 100) / 10
+ timerText += "&9⚚ Bonzo's Mask: " + (timer3 > 0 ? "&c" + timer3 + "&6s" : "&6READY")
+ }
+ }
+ this.bonzoSpiritMaskTimerElement.setText(timerText)
+ })
+ this.forgorEnabled = new ToggleSetting("Change withermancer death message to forgor ", "", true, "withermancer_forgor", this);
+ this.f7waypoints = new ToggleSetting("Waypoints for P3 F7/M7", "(Only shows unfinished ones)", true, "f7_waypoints", this);
+
+ this.IceSprayWarn = new ToggleSetting("Ice Spray Drop Ping", "Renders a big title so you don't miss ice spray wands", false, "ice_spray_ping", this).contributor("EmeraldMerchant");
+
+ this.guardianHp = new ToggleSetting("m3/f3 guardian hp", "", true, "guardian_hp", this);
+
+ this.registerChat("&r&c ☠ &r${player} were killed by Withermancer&r&7 and became a ghost&r&7.&r", (player, e) => {
+ if (this.forgorEnabled.getValue()) {
+ cancel(e);
+ ChatLib.chat(player + " forgor ☠");
+ }
+ });
+ this.registerChat("&r&c ☠ &r${player} was killed by Withermancer&r&7 and became a ghost&r&7.&r", (player, e) => {
+ if (this.forgorEnabled.getValue()) {
+ cancel(e);
+ ChatLib.chat(player + " forgor ☠");
+ }
+ });
+
+ this.registerChat("&r&c ☠ ${info} and became a ghost&r&7.&r", (info, e) => {
+ let player = ChatLib.removeFormatting(info.split(" ")[0])
+
+ this.scanFirstDeathForSpiritPet(player)
+ });
+
+ this.spiritBowPickUps = [];
+ this.registerChat("&r&aYou picked up the &r&5Spirit Bow&r&a! Use it to attack &r&cThorn&r&a!&r", () => {
+ this.spiritBowPickUps.push(Date.now());
+ });
+
+ this.bearSpawning = 0;
+ this.registerChat("&r&a&lThe &r&5&lSpirit Bow &r&a&lhas dropped!&r", () => {
+ this.bearSpawning = -Date.now();
+ });
+
+ this.todoE = [];
+ this.eMovingThing = {};
+ this.nameToUuid = {
+ "you": Player.getUUID().toString()
+ }
+ this.bloodX = -1;
+ this.bloodY = -1;
+ this.startSpawningTime = 0;
+ this.spawnIdThing = 0;
+
+ this.failedPuzzleCount = 0;
+ this.totalPuzzleCount = 0;
+ this.completedPuzzleCount = 0;
+
+ this.ezpz = false
+
+ this.arrows = [];
+ this.blazes = [];
+ this.blazeX = -1;
+ this.blazeY = -1;
+ this.timersData = []
+
+ this.dungeonSecretRquired = {
+ 1: 0.3,
+ 2: 0.4,
+ 3: 0.5,
+ 4: 0.6,
+ 5: 0.7,
+ 6: 0.85,
+ 7: 1,
+ };
+ this.floorSecondMod = {
+ 1: 120,
+ 2: 120,
+ 3: 120,
+ 4: 120,
+ 5: 240,
+ 6: 120,
+ 7: 360,
+ };
+ this.renderEntityEvent = this.registerEvent("renderEntity", this.renderEntity);
+
+ this.registerStep(true, 2, this.stepNotDung).registeredWhen(() => !this.isInDungeon());
+ this.registerStep(true, 2, this.step).registeredWhen(() => this.isInDungeon());
+ this.registerStep(false, 60, this.step)
+ this.registerStep(true, 10, this.step2).registeredWhen(() => this.isInDungeon());
+ this.registerStep(true, 1, this.step_1fps).registeredWhen(() => this.isInDungeon());
+ this.registerStep(false, 60 * 5, this.step_5min)
+ this.registerEvent("worldLoad", this.onWorldLoad);
+
+ this.registerEvent("renderWorld", this.renderWorld).registeredWhen(() => this.isInDungeon());
+
+ this.bloodOpenedBonus = false;
+ this.goneInBonus = false;
+ this.mimicDead = false
+ this.inBoss = false
+ this.registerChat("&r&cThe &r&c&lBLOOD DOOR&r&c has been opened!&r", () => {
+ this.bloodOpenedBonus = true;
+ this.goneInBonus = true;
+ });
+ this.registerChat("[BOSS] The Watcher: You have proven yourself. You may pass.", () => {
+ delay(5000, () => {
+ this.bloodOpenedBonus = false;
+ this.goneInBonus = true;
+ })
+ });
+ let enteredBossMessages = ["[BOSS] Maxor: WELL WELL WELL LOOK WHO’S HERE!", "[BOSS] Livid: Welcome, you arrive right on time. I am Livid, the Master of Shadows.", "[BOSS] Thorn: Welcome Adventurers! I am Thorn, the Spirit! And host of the Vegan Trials!", "[BOSS] The Professor: I was burdened with terrible news recently...", "[BOSS] Scarf: This is where the journey ends for you, Adventurers.", "[BOSS] Bonzo: Gratz for making it this far, but I’m basically unbeatable.", "[BOSS] Sadan: So you made it all the way here...and you wish to defy me? Sadan?!"]
+ enteredBossMessages.forEach(msg => {
+ this.registerChat(msg, () => {
+ this.goneInBonus = false;
+ this.bloodOpenedBonus = false;
+ this.inBoss = true
+ });
+ })
+ this.registerEvent("entityDeath", (entity) => {
+ if (entity.getClassName() === "EntityZombie") {
+ if (entity.getEntity().func_70631_g_()) {
+ if (entity.getEntity().func_82169_q(0) === null && entity.getEntity().func_82169_q(1) === null && entity.getEntity().func_82169_q(2) === null && entity.getEntity().func_82169_q(3) === null) {
+ this.mimicDead = true
+ }
+ }
+ }
+ })
+ let mimicDeadMessages = ["$SKYTILS-DUNGEON-SCORE-MIMIC$", "Mimic Killed!", "Mimic Dead!", "Mimic dead!"]
+ this.registerChat("&r&9Party &8> ${msg}", (msg) => {
+ mimicDeadMessages.forEach(dmsg => {
+ if (msg.includes(dmsg)) this.mimicDead = true
+ })
+ })
+
+ this.registerChat("&r&aDungeon starts in 1 second.&r", () => {
+ this.goneInBonus = false;
+ this.bloodOpenedBonus = false;
+
+ this.firstDeath = false
+ this.firstDeathHadSpirit = false
+ })
+
+ this.registerChat("&r&aDungeon starts in 1 second. Get ready!&r", () => {
+ this.goneInBonus = false;
+ this.bloodOpenedBonus = false;
+
+ this.firstDeath = false
+ this.firstDeathHadSpirit = false
+ })
+
+ this.firstDeath = false
+ this.firstDeathHadSpirit = false
+
+
+ this.registerForge(net.minecraftforge.event.entity.EntityJoinWorldEvent, this.entityJoinWorldEvent).registeredWhen(() => this.isInDungeon() && !this.inBoss);
+
+
+ this.onWorldLoad();
+
+ this.registerEvent("tick", () => {
+ this.terminals.forEach(w => w.update())
+ this.levers.forEach(w => w.update())
+ this.devices.forEach(w => w.update())
+ }).registeredWhen(() => this.f7waypoints.getValue())
+ this.terminals = []
+ this.levers = []
+ this.devices = []
+ this.data = []
+ this.area = -1
+ this.registerChat("[BOSS] Goldor: You have done it, you destroyed the factory…", () => {
+ this.area = -1
+ this.areaUpdated()
+ })
+ this.registerEvent("worldLoad", () => {
+ if (this.area !== -1) {
+ this.area = -1
+ this.areaUpdated()
+ }
+ })
+ this.registerChat("[BOSS] Goldor: Who dares trespass into my domain?", () => {
+ this.area = 0
+ this.areaUpdated()
+ })
+
+ this.loadf7data()
+
+ this.ffCountdownTo = 0
+ this.registerChat("[BOSS] The Professor: Oh? You found my Guardians one weakness?", () => {
+ this.ffCountdownTo = Date.now() + 5000
+
+ delay(5000, () => {
+ this.ffCountdownTo = 0
+ })
+ })
+
+ this.registerChat("${name} activated a lever! (${start}/${end})", (name, start, end) => {
+ let player = World.getPlayerByName(ChatLib.removeFormatting(name))
+
+ if (!player) return
+
+ let closestDist = calculateDistanceQuick([this.levers[0].params.x, this.levers[0].params.y, this.levers[0].params.z], [player.getX(), player.getY(), player.getZ()])
+ let closest = this.levers[0]
+ this.levers.forEach(l => {
+ if (!l.rendering) return
+ let dist = calculateDistanceQuick([l.params.x, l.params.y, l.params.z], [player.getX(), player.getY(), player.getZ()])
+ if (dist < closestDist) {
+ closestDist = dist
+ closest = l
+ }
+ })
+ closest.stopRender()
+
+
+ if (start == "0" || start == end) {
+ this.area++
+ this.areaUpdated()
+ }
+ }).registeredWhen(() => this.f7waypoints.getValue())
+
+ this.registerChat("${name} completed a device! (${start}/${end})", (name, start, end) => {
+
+ let closest = this.devices[0]
+
+ closest.stopRender()
+
+ if (start == "0" || start == end) {
+ this.area++
+ this.areaUpdated()
+ }
+ }).registeredWhen(() => this.f7waypoints.getValue())
+
+ this.registerChat("${name} activated a terminal! (${start}/${end})", (name, start, end) => {
+ let player = World.getPlayerByName(ChatLib.removeFormatting(name))
+
+ if (!player) return
+
+ let closestDist = calculateDistanceQuick([this.terminals[0].params.x, this.terminals[0].params.y, this.terminals[0].params.z], [player.getX(), player.getY(), player.getZ()])
+ let closest = this.terminals[0]
+ this.terminals.forEach(l => {
+ if (!l.rendering) return
+ let dist = calculateDistanceQuick([l.params.x, l.params.y, l.params.z], [player.getX(), player.getY(), player.getZ()])
+ if (dist < closestDist) {
+ closestDist = dist
+ closest = l
+ }
+ })
+ closest.stopRender()
+
+
+ if (start == "0" || start == end) {
+ this.area++
+ this.areaUpdated()
+ }
+ }).registeredWhen(() => this.f7waypoints.getValue())
+ //§r§6Soopyboo32§r§a activated a lever! (§r§c8§r§a/8)§r
+ //§r§6Soopyboo32§r§a completed a device! (§r§c3§r§a/8)§r
+ //§r§bBossmanLeo§r§a activated a terminal! (§r§c2§r§a/8)§r
+
+ this.inf6boss = false
+ this.registerStep(true, 1, () => {
+ this.inf6boss = this.getCurrentRoomId() === "sadan"
+ })
+
+ let packetRecieved = this.registerCustom("packetReceived", this.packetReceived).registeredWhen(() => this.inf6boss && this.respawnTimerTerra.getValue())
+
+ try {
+ packetRecieved.trigger.setPacketClasses([net.minecraft.network.play.server.S23PacketBlockChange, net.minecraft.network.play.server.S22PacketMultiBlockChange])
+ } catch (e) { }//older ct version
+
+ }
+ getBonzoMaskCooldown() {
+ [...Player.getInventory().getItems()].forEach(i => {
+ if (!i) return
+ let itemName = i.getName()
+ if (itemName.removeFormatting().includes("Bonzo's Mask")) {
+ i.getLore().forEach(line => {
+ if (line.includes("Cooldown:")) {
+ this.bonzoMaskCooldown = Number(line.removeFormatting().split("n: ")[1].replace("s", ""))
+ }
+ })
+ }
+ })
+ }
+ getCurrentRoomId() {
+ if (Scoreboard.getLines().length === 0) return
+ let id = ChatLib.removeFormatting(Scoreboard.getLineByIndex(Scoreboard.getLines().length - 1).getName()).trim().split(" ").pop()
+
+ return id
+ }
+ getBlockIdFromState(state) {
+ return MCBlock[m.getStateId](state)
+ }
+
+ packetReceived(packet, event) {
+ if (!this.inf6boss || !this.respawnTimerTerra.getValue()) return
+
+ let packetType = new String(packet.class.getSimpleName()).valueOf()
+ if (packetType === "S23PacketBlockChange") {
+ let position = new BlockPos(packet[m.getBlockPosition.S23PacketBlockChange]())
+ let blockState = this.getBlockIdFromState(packet[m.getBlockState.S23PacketBlockChange]())
+ let oldBlockState = this.getBlockIdFromState(World.getBlockStateAt(position))
+
+ if (oldBlockState === 0 && blockState === 4240 && this.inf6boss) {
+ this.timerThing(position)
+ }
+ }
+ if (packetType === "S22PacketMultiBlockChange") {
+ packet[m.getChangedBlocks]().forEach(b => {
+ let position = new BlockPos(b[m.getPos.S22PacketMultiBlockChange$BlockUpdateData]())
+ let blockState = this.getBlockIdFromState(b[m.getBlockState.S22PacketMultiBlockChange$BlockUpdateData]())
+ let oldBlockState = this.getBlockIdFromState(World.getBlockStateAt(position))
+
+ if (oldBlockState === 0 && blockState === 4240 && this.inf6boss) {
+ this.timerThing(position)
+ }
+ })
+ }
+ }
+
+ timerThing(position) {
+ this.timersData.push([position, Date.now() + (this.FeatureManager.features["dataLoader"].class.dungeonFloor[0] === "M" ? 3500 : 5000)])
+ }
+
+ areaUpdated() {
+ this.terminals.forEach(w => w.stopRender())
+ this.levers.forEach(w => w.stopRender())
+ this.devices.forEach(w => w.stopRender())
+
+ this.terminals = []
+ this.levers = []
+ this.devices = []
+
+ if (!this.f7waypoints.getValue()) return
+
+ this.data.forEach(term => {
+ if (term.phase !== this.area) return
+
+ if (term.type === "lever") {
+ this.levers.push(new Waypoint(term.location[0], term.location[1], term.location[2], 1, 0, 0, { name: "Lever" }).startRender())
+ }
+ if (term.type === "terminal") {
+ this.terminals.push(new Waypoint(term.location[0], term.location[1], term.location[2], 1, 0, 0, { name: "Terminal" }).startRender())
+ }
+ if (term.type === "device") {
+ this.devices.push(new Waypoint(term.location[0], term.location[1], term.location[2], 1, 0, 0, { name: "Device" }).startRender())
+ }
+ })
+ }
+
+ loadf7data() {
+ this.unloadf7data()
+
+ let data = FileLib.read("SoopyV2", "features/dungeonSolvers/f7data.json")
+ data = JSON.parse(data)
+
+ this.data = data
+ }
+
+ unloadf7data() {
+ this.terminals.forEach(w => w.stopRender())
+ this.levers.forEach(w => w.stopRender())
+ this.devices.forEach(w => w.stopRender())
+
+ this.terminals = []
+ this.levers = []
+ this.devices = []
+ this.data = []
+ this.area = 0
+ }
+
+ step_5min() {
+ this.ezpz = false
+ if (!this.FeatureManager.features["dataLoader"].class.mayorData) return
+ if (this.FeatureManager.features["dataLoader"].class.mayorData.mayor.name === "Paul") {
+ if (this.FeatureManager.features["dataLoader"].class.currentMayorPerks.has("EZPZ")) {
+ this.ezpz = true
+ }
+ }
+ }
+
+ calculateDungeonScore() {
+ if (!this.FeatureManager.features["dataLoader"].class.dungeonFloor) {
+ this.scoreElement.setText("");
+ return;
+ }
+
+ if (!this.FeatureManager.features["dataLoader"].class.stats.Deaths) return
+
+ // Information about the dungeon
+ let deaths = parseInt(this.FeatureManager.features["dataLoader"].class.stats.Deaths.replace("(", "").replace(")", ""));
+
+ let seconds = 0;
+ if (this.FeatureManager.features["dataLoader"].class.stats.Time !== "Soon!") {
+ let data = this.FeatureManager.features["dataLoader"].class.stats.Time.split(" ");
+ seconds += parseInt(data.pop() || 0);
+ seconds += parseInt(data.pop() || 0) * 60;
+ seconds += parseInt(data.pop() || 0) * 60 * 60;
+ }
+ let timeLimit = this.FeatureManager.features["dataLoader"].class.dungeonFloor[0] === "M" ? 480 : this.floorSecondMod[this.FeatureManager.features["dataLoader"].class.dungeonFloor[1]];
+ let inDungeonSeconds = seconds - timeLimit;
+
+ let clearedPercent = this.FeatureManager.features["dataLoader"].class.dungeonPercentCleared;
+
+ let dungeonFloor = this.FeatureManager.features["dataLoader"].class.dungeonFloor[1];
+
+ let secretPercentRequired = this.FeatureManager.features["dataLoader"].class.dungeonFloor[0] === "M" ? 1 : this.dungeonSecretRquired[dungeonFloor];
+
+ let clearedRooms = parseInt(this.FeatureManager.features["dataLoader"].class.stats["Completed Rooms"]);
+
+ let totalRooms = clearedRooms / clearedPercent || 25;
+
+ let currentSecretsFound = parseInt(this.FeatureManager.features["dataLoader"].class.stats["Secrets Found"]);
+ let currentSecretPercent = parseFloat(this.FeatureManager.features["dataLoader"].class.stats["Secrets Found%"].replace("%", "")) / 100;
+
+ let crypts = parseInt(this.FeatureManager.features["dataLoader"].class.stats["Crypts"]);
+
+ let maxSecrets = currentSecretsFound / currentSecretPercent || 50;
+
+ //Actual Score Calculation
+ let skillScore = Math.floor(Math.max(20, (20 - (this.totalPuzzleCount - this.completedPuzzleCount) * 10 + (((80 * (clearedRooms + this.bloodOpenedBonus + this.goneInBonus)) / totalRooms) - deaths * 2 + this.firstDeathHadSpirit))));
+ let exploreScore = (Math.floor((60 * (clearedRooms + this.bloodOpenedBonus + this.goneInBonus)) / totalRooms) + Math.floor(Math.min(40, (40 * currentSecretsFound) / secretPercentRequired / maxSecrets)));
+ let speedScore;
+ if (inDungeonSeconds < 480) {
+ speedScore = 100;
+ } else if (inDungeonSeconds < 600) {
+ speedScore = Math.ceil(140 - inDungeonSeconds / 12);
+ } else if (inDungeonSeconds < 840) {
+ speedScore = Math.ceil(115 - inDungeonSeconds / 24);
+ } else if (inDungeonSeconds < 1140) {
+ speedScore = Math.ceil(108 - inDungeonSeconds / 30);
+ } else if (inDungeonSeconds < 3940) {
+ speedScore = Math.ceil(98.5 - inDungeonSeconds / 40);
+ } else {
+ speedScore = 0;
+ }
+ let bonus = Math.min(5, crypts) + this.mimicDead * 2 + this.ezpz * 10;
+
+ //Calculating secrets for s/s+
+
+ let hypotheticalSkillScore = Math.floor(Math.max(20, (20 - (this.failedPuzzleCount) * 10 + 80 - deaths * 2 + this.firstDeathHadSpirit)));
+ let hypotheticalSpeedScore = speedScore
+
+ //Calculating for S
+ let hypotheticalBonusScoreS = Math.min(5, crypts) + this.mimicDead * 2 + this.ezpz * 10;
+
+ let sNeededSecrets = Math.min(maxSecrets * secretPercentRequired, Math.ceil((270 - hypotheticalSkillScore - hypotheticalBonusScoreS - hypotheticalSpeedScore - 60) * maxSecrets * secretPercentRequired / 40));
+
+ sNeededSecrets = Math.max(currentSecretsFound, sNeededSecrets)
+
+ let hypotheticalScoreGottenS = hypotheticalSkillScore + hypotheticalSpeedScore + hypotheticalBonusScoreS + 60 + Math.floor(Math.min(40, (40 * sNeededSecrets) / secretPercentRequired / maxSecrets));
+ let sCryptsNeeded = Math.max(crypts, Math.min(5, (270 - hypotheticalScoreGottenS)))
+ hypotheticalScoreGottenS -= hypotheticalBonusScoreS
+ hypotheticalBonusScoreS = sCryptsNeeded + this.mimicDead * 2 + this.ezpz * 10
+ hypotheticalScoreGottenS += hypotheticalBonusScoreS
+
+ let sPossible = hypotheticalScoreGottenS >= 270
+
+ //Calculating for S+
+ let hypotheticalBonusScoreSplus = 5 + this.mimicDead * 2 + this.ezpz * 10;
+
+ let splusNeededSecrets = Math.ceil((300 - hypotheticalSkillScore - hypotheticalBonusScoreSplus - hypotheticalSpeedScore - 60) * maxSecrets * secretPercentRequired / 40);
+
+ let splusPossible = splusNeededSecrets <= maxSecrets * secretPercentRequired
+
+ splusNeededSecrets = Math.max(currentSecretsFound, splusNeededSecrets)
+
+ let hypotheticalScoreGottenSPlus = hypotheticalSkillScore + hypotheticalSpeedScore + hypotheticalBonusScoreSplus + 60 + Math.floor(Math.min(40, (40 * splusNeededSecrets) / secretPercentRequired / maxSecrets));
+ let splusCryptsNeeded = Math.max(crypts, 5 - (hypotheticalScoreGottenSPlus - 300))
+ hypotheticalScoreGottenSPlus -= hypotheticalBonusScoreSplus
+ hypotheticalBonusScoreSplus = splusCryptsNeeded + this.mimicDead * 2 + this.ezpz * 10
+ hypotheticalScoreGottenSPlus += hypotheticalBonusScoreSplus
+
+ //Setting hud element
+ let sPlusText = currentSecretsFound === 0 ? "??" : (skillScore + exploreScore + speedScore + bonus) >= 300 ? "✔" : splusPossible ? `(${hypotheticalScoreGottenSPlus}): ${currentSecretsFound}/${splusNeededSecrets} +${crypts}c/${splusCryptsNeeded}` : "✖"
+ let sText = currentSecretsFound === 0 ? " ??" : (skillScore + exploreScore + speedScore + bonus) >= 270 ? " ✔" : sPossible ? ` (${hypotheticalScoreGottenS}): ${currentSecretsFound}/${sNeededSecrets} +${crypts}c/${sCryptsNeeded}` : "✖"
+
+ this.scoreElement.setText(`&dScore: ${skillScore + exploreScore + speedScore + bonus}\n&aS+ ${sPlusText}\n&aS ${sText}`);
+ }
+
+ async scanFirstDeathForSpiritPet(username) {
+ if (this.firstDeath) return
+ this.firstDeath = true
+ if (!this.nameToUuid[username.toLowerCase()]) return
+ let uuid = this.nameToUuid[username.toLowerCase()]?.replace(/-/g, "")
+
+ if (this.FeatureManager.features["globalSettings"] && this.FeatureManager.features["globalSettings"].class.apiKeySetting.getValue()) {
+ let data = await fetch(`https://api.hypixel.net/skyblock/profiles?key=${this.FeatureManager.features["globalSettings"].class.apiKeySetting.getValue()}&uuid=${uuid}`).json()
+ if (!data.success) return
+
+ let latestProfile = [0, undefined]
+
+ data.profiles.forEach(p => {
+ if (p.members[uuid].last_save > latestProfile[0]) {
+ latestProfile = [p.members[uuid].last_save, p.members[uuid].pets.some(pet => pet.type === "SPIRIT" && pet.tier === "LEGENDARY")]
+ }
+ })
+
+ if (latestProfile[1]) {
+ this.firstDeathHadSpirit = true
+ if (this.scoreCalculation.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + username + " has spirit pet!")
+ } else {
+ if (this.scoreCalculation.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + username + " does not have spirit pet!")
+ }
+ } else {
+ let data = await fetch(`http://soopy.dev/api/v2/player_skyblock/${uuid}`).json()
+ if (!data.success) return
+
+ if (data.data.profiles[data2.data.stats.currentProfileId].members[uuid].pets.some(pet => pet.type === "SPIRIT" && pet.tier === "LEGENDARY")) {
+ this.firstDeathHadSpirit = true
+ if (this.scoreCalculation.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + username + " has spirit pet!")
+ } else {
+ if (this.scoreCalculation.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + username + " does not have spirit pet!")
+ }
+ }
+ }
+
+ entityJoinWorldEvent(event) {
+ if (this.bloodCampAssist.getValue()) this.todoE.push(event.entity);
+
+ // if(event.entity instanceof EntityArrow){
+ // this.arrows.push(new Entity(event.entity))
+ // }
+ if (event.entity instanceof EntityBlaze) {
+ // console.log("Blaze joined world")
+ this.addBlaze(new Entity(event.entity));
+ }
+ }
+
+ renderWorld(ticks) {
+ if (this.lividFindBox.getValue()) {
+ if (this.lividData.correctLividEntity) {
+ renderUtils.drawBoxAtEntity(this.lividData.correctLividEntity, 255, 0, 0, 0.75, -2, ticks);
+ }
+ }
+
+ if (this.bloodCampAssist.getValue() && this.skulls) {
+ for (let skull of this.skulls) {
+ if (this.eMovingThing[skull.getUUID().toString()] && this.eMovingThing[skull.getUUID().toString()].timeTook) {
+ let skullE = skull.getEntity();
+ let startPoint = [skullE[f.posX.Entity], skullE[f.posY.Entity], skullE[f.posZ.Entity]];
+
+ let xSpeed2 = (startPoint[0] - this.eMovingThing[skull.getUUID().toString()].startX) / this.eMovingThing[skull.getUUID().toString()].timeTook;
+ let ySpeed2 = (startPoint[1] - this.eMovingThing[skull.getUUID().toString()].startY) / this.eMovingThing[skull.getUUID().toString()].timeTook;
+ let zSpeed2 = (startPoint[2] - this.eMovingThing[skull.getUUID().toString()].startZ) / this.eMovingThing[skull.getUUID().toString()].timeTook;
+
+ let time = (this.spawnIdThing >= 4 ? 2900 : 4850) - this.eMovingThing[skull.getUUID().toString()].timeTook;
+ let endPoint1 = this.eMovingThing[skull.getUUID().toString()].endPoint;
+ let endPoint2 = this.eMovingThing[skull.getUUID().toString()].endPointLast;
+ let endPointUpdated = Math.min(Date.now() - this.eMovingThing[skull.getUUID().toString()].endPointUpdated, 100);
+ if (!endPoint2) return;
+ let ping = this.FeatureManager.features["dataLoader"].class.getPing()
+ let endPoint = [endPoint2[0] + ((endPoint1[0] - endPoint2[0]) * endPointUpdated) / 100, endPoint2[1] + ((endPoint1[1] - endPoint2[1]) * endPointUpdated) / 100, endPoint2[2] + ((endPoint1[2] - endPoint2[2]) * endPointUpdated) / 100];
+ let pingPoint = [startPoint[0] + xSpeed2 * ping, startPoint[1] + ySpeed2 * ping, startPoint[2] + zSpeed2 * ping];
+
+ renderUtils.drawLineWithDepth(startPoint[0], startPoint[1] + 2, startPoint[2], endPoint[0], endPoint[1] + 2, endPoint[2], 255, 0, 0, 2);
+
+ if (ping < time) {
+ renderUtils.drawBoxAtBlockNotVisThruWalls(pingPoint[0] - 0.5, pingPoint[1] + 1.5, pingPoint[2] - 0.5, 0, 255, 0);
+ renderUtils.drawBoxAtBlockNotVisThruWalls(endPoint[0] - 0.5, endPoint[1] + 1.5, endPoint[2] - 0.5, 255, 0, 0);
+ } else {
+ renderUtils.drawBoxAtBlockNotVisThruWalls(endPoint[0] - 0.5, endPoint[1] + 1.5, endPoint[2] - 0.5, 0, 0, 255);
+ }
+
+ // if(this.eMovingThing[skull.getUUID().toString()] && this.eMovingThing[skull.getUUID().toString()].timeTook){
+ // Tessellator.drawString((time/1000).toFixed(3)+"s", endPoint[0], endPoint[1]+2, endPoint[2])
+ // }
+ }
+ }
+ }
+
+ if (this.blazeX !== -1 && this.blazes.length > 0 && this.blazeSolver.getValue()) {
+ renderUtils.drawBoxAtEntity(this.blazes[0], 255, 0, 0, 1, 2, ticks, 2);
+
+ let lastLoc = [this.blazes[0].getX(), this.blazes[0].getY() + 1.5, this.blazes[0].getZ()];
+ // this.blazes.forEach((blaze, i) => {
+ for (let i = 0, blaze = this.blazes[0]; i < this.blazes.length; i++, blaze = this.blazes[i]) {
+ if (i < 3 && i !== 0) {
+ renderUtils.drawLineWithDepth(lastLoc[0], lastLoc[1], lastLoc[2], blaze.getX(), blaze.getY() + 1.5, blaze.getZ(), i === 1 ? 0 : 255, i === 1 ? 255 : 0, 0, 3 / i);
+
+ lastLoc[0] = blaze.getX();
+ lastLoc[1] = blaze.getY() + 1.5;
+ lastLoc[2] = blaze.getZ();
+ }
+ }
+ }
+
+ let shifts = 0
+ for (let data of this.timersData) {
+ let [position, time] = data
+
+ if (Date.now() > time) {
+ shifts++
+ }
+
+ Tessellator.drawString(((time - Date.now()) / 1000).toFixed(1) + "s", position.getX(), position.getY() + 0.5, position.getZ(), Renderer.color(0, 255, 50), false, 0.025, false)
+ }
+ for (let i = 0; i < shifts; i++) this.timersData.shift()
+
+ if (this.guardianHp.getValue() && this.FeatureManager.features["dataLoader"].class.dungeonFloor?.[1] === "3") {
+ let es = World.getAllEntitiesOfType(entityGuardian)
+
+ for (let e of es) {
+ if (e.getEntity().func_110143_aJ() < 1000) continue //getHealth
+
+ Tessellator.drawString(addNotation("oneLetters", Math.round(e.getEntity().func_110143_aJ())), e.getX(), e.getY() + 0.25, e.getZ(), Renderer.color(0, 255, 0), false, 0.1, false)
+ }
+ }
+ }
+
+ renderEntity(entity, position, ticks, event) {
+ if (this.lividFindNametags.getValue()) {
+ if (this.lividData.correctLividEntity) {
+ if (entity.getName().includes("Livid") && entity.getName().includes("❤") && entity.getUUID() !== this.lividData.correctLividEntity.getUUID()) {
+ cancel(event);
+ }
+ }
+ }
+ }
+
+ /**
+ * TODO: This is code from old soopyaddons, needs almost complete recode.
+ * TODO: Also needs support for the new terminals, namely click at time
+ * TODO: Make click on time scan even when terminal is throttled (update on chat msg)
+ * TODO: Make all terminal solvers track when the server will accept the click and update on that
+ */
+ findNeededClicks() {
+ let itemsHighlight = []
+ if (ChatLib.removeFormatting(Player.getContainer().getName()).startsWith("What starts with: '") && ChatLib.removeFormatting(Player.getContainer().getName()).endsWith("'?")) {
+ let letter = ChatLib.removeFormatting(Player.getContainer().getName()).substr(19, 1).toLowerCase()
+ let i = 0;
+ Player.getContainer().getItems().forEach((item) => {
+ if (ChatLib.removeFormatting(item.getName()).toLowerCase().startsWith(letter) && !item.isEnchanted()) {
+ itemsHighlight.push(i)
+ }
+ i++
+ })
+ }
+ if (ChatLib.removeFormatting(Player.getContainer().getName()).startsWith("Select all the ") && ChatLib.removeFormatting(Player.getContainer().getName()).endsWith(" items!")) {
+ let color = ChatLib.removeFormatting(Player.getContainer().getName()).substr(15).replace(" items!", "").toLowerCase()
+ let i = 0;//silver -> light grey die, white -> wool--
+ Player.getContainer().getItems().forEach((item) => {
+ let name = ChatLib.removeFormatting(item.getName()).toLowerCase()
+ Object.entries(wrongColorList).forEach(([from, to]) => {
+ name.replace(from, to)
+ })
+ if (name.replace("wool", "white wool").includes(color) && !item.isEnchanted()) {
+ itemsHighlight.push(i)
+ }
+ i++
+ })
+ }
+ if (ChatLib.removeFormatting(Player.getContainer().getName()) === "Click in order!") {
+ let i = 0;//silver -> light grey die, white -> wool--
+ let number = 100;
+ Player.getContainer().getItems().forEach((item, i) => {
+ if (parseInt(ChatLib.removeFormatting(item.getName())) < number && item.getMetadata() === 14) {
+ number = parseInt(ChatLib.removeFormatting(item.getName()))
+ }
+ i++
+ })
+ i = 0
+ Player.getContainer().getItems().forEach((item) => {
+ if (parseInt(ChatLib.removeFormatting(item.getName())) === number) {
+ itemsHighlight.push(i)
+ }
+ i++
+ })
+ i = 0
+ Player.getContainer().getItems().forEach((item) => {
+ if (parseInt(ChatLib.removeFormatting(item.getName())) === number + 1) {
+ itemsHighlight2.push(i)
+ }
+ i++
+ })
+ }
+ }
+
+ onWorldLoad() {
+ this.lastWorldload = Date.now()
+ this.goneInBonus = false;
+ this.bloodOpenedBonus = false;
+ this.mimicDead = false
+ this.lividData.correctLividColor = undefined;
+ this.lividData.correctLividColorHP = undefined;
+ this.lividData.sayLividColors = [];
+ this.lividData.sayLividColors2 = [];
+ this.lividData.correctLividEntity = undefined;
+ this.lividHpElement && this.lividHpElement.setText("");
+ this.nameToUuid = {
+ "you": Player.getUUID().toString()
+ }
+ this.spiritBowPickUps = []
+ this.bearSpawning = 0
+ this.ffCountdownTo = 0
+ this.startSpawningTime = 0;
+ this.spawnIdThing = 0;
+ this.eMovingThing = {};
+ this.bloodX = -1;
+ this.bloodY = -1;
+ this.blazeX = -1;
+ this.blazeY = -1;
+ this.skulls = [];
+ this.arrows = [];
+ this.blazes = [];
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((e) => {
+ if (e.getEntity()[m.getEquipmentInSlot](4) && e.getEntity()[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]().endsWith(getSkullName())) {
+ this.addSkull(e);
+ }
+ });
+ World.getAllEntitiesOfType(EntityBlaze).forEach((e) => {
+ this.addBlaze(e);
+ });
+ // World.getAllEntitiesOfType(EntityArrow).forEach((e) => {
+ // this.arrows.push(e)
+ // });
+ }
+
+ step2() {
+
+ if (this.ffCountdownTo && this.ffCountdownTo > 0) {
+ this.fireFreezeTimerElement.setText("&dFire freeze in: &c" + (Math.max(0, this.ffCountdownTo - Date.now()) / 1000).toFixed(2) + "s");
+ } else {
+ this.fireFreezeTimerElement.setText("");
+ }
+ if (this.bearSpawning && this.bearSpawning > 0 && this.isInDungeon()) {
+ this.spiritBearSpawnElement.setText("&dBear spawned in: &c" + (Math.max(0, this.bearSpawning - Date.now()) / 1000).toFixed(2) + "s");
+ } else {
+ this.spiritBearSpawnElement.setText("");
+ }
+ if (this.scoreCalculation.getValue()) this.calculateDungeonScore();
+ if (this.bloodCampAssist.getValue() && this.skulls) {
+ this.skulls.forEach((skull) => {
+ let skullE = skull.getEntity();
+ // renderUtils.drawBoxAtEntity(skull, 255, 0, 0, 0.5, 0.5, ticks)
+
+ let xSpeed = skullE[f.posX.Entity] - skullE[f.lastTickPosX];
+ let ySpeed = skullE[f.posY.Entity] - skullE[f.lastTickPosY];
+ let zSpeed = skullE[f.posZ.Entity] - skullE[f.lastTickPosZ];
+
+ if (this.eMovingThing[skull.getUUID().toString()] && Date.now() - this.eMovingThing[skull.getUUID().toString()].startMovingTime > 5000) {
+ this.eMovingThing[skull.getUUID().toString()].logged = true;
+ this.spawnIdThing++;
+
+ delete this.eMovingThing[skull.getUUID().toString()];
+ this.skulls = this.skulls.filter((e) => {
+ if (e.getUUID().toString() === skull.getUUID().toString()) {
+ return false;
+ }
+ return true;
+ });
+ return;
+ }
+
+ if (xSpeed !== 0 || ySpeed !== 0) {
+ if (!this.eMovingThing[skull.getUUID().toString()]) this.eMovingThing[skull.getUUID().toString()] = { startMovingTime: Date.now(), startX: skullE[f.posX.Entity], startY: skullE[f.posY.Entity], startZ: skullE[f.posZ.Entity] };
+
+ if (this.eMovingThing[skull.getUUID().toString()].lastX !== skullE[f.posX.Entity] || this.eMovingThing[skull.getUUID().toString()].lastY !== skullE[f.posY.Entity]) {
+ this.eMovingThing[skull.getUUID().toString()].timeTook = Date.now() - this.eMovingThing[skull.getUUID().toString()].startMovingTime;
+ } else if (!this.eMovingThing[skull.getUUID().toString()].logged && (skullE[f.isDead] || !skullE[m.getEquipmentInSlot](4) || !skullE[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]().endsWith(getSkullName()))) {
+ this.eMovingThing[skull.getUUID().toString()].logged = true;
+ this.spawnIdThing++;
+
+ delete this.eMovingThing[skull.getUUID().toString()];
+ this.skulls = this.skulls.filter((e) => {
+ if (e.getUUID().toString() === skull.getUUID().toString()) {
+ return false;
+ }
+ return true;
+ });
+ return;
+ }
+
+ this.eMovingThing[skull.getUUID().toString()].lastX = skullE[f.posX.Entity];
+ this.eMovingThing[skull.getUUID().toString()].lastY = skullE[f.posY.Entity];
+
+ if (!this.startSpawningTime) this.startSpawningTime = Date.now();
+ }
+
+ if (this.eMovingThing[skull.getUUID().toString()] && this.eMovingThing[skull.getUUID().toString()].timeTook) {
+ let startPoint = [skullE[f.posX.Entity], skullE[f.posY.Entity], skullE[f.posZ.Entity]];
+
+ let xSpeed2 = (startPoint[0] - this.eMovingThing[skull.getUUID().toString()].startX) / this.eMovingThing[skull.getUUID().toString()].timeTook;
+ let ySpeed2 = (startPoint[1] - this.eMovingThing[skull.getUUID().toString()].startY) / this.eMovingThing[skull.getUUID().toString()].timeTook;
+ let zSpeed2 = (startPoint[2] - this.eMovingThing[skull.getUUID().toString()].startZ) / this.eMovingThing[skull.getUUID().toString()].timeTook;
+
+ let time = (this.spawnIdThing >= 4 ? 2900 : 4875) - this.eMovingThing[skull.getUUID().toString()].timeTook;
+ let endPoint = [startPoint[0] + xSpeed2 * time, startPoint[1] + ySpeed2 * time, startPoint[2] + zSpeed2 * time];
+
+ this.eMovingThing[skull.getUUID().toString()].endPointLast = this.eMovingThing[skull.getUUID().toString()].endPoint;
+ this.eMovingThing[skull.getUUID().toString()].endPoint = endPoint;
+ this.eMovingThing[skull.getUUID().toString()].endPointUpdated = Date.now();
+
+ // if(this.eMovingThing[skull.getUUID().toString()] && this.eMovingThing[skull.getUUID().toString()].timeTook){
+ // Tessellator.drawString((time/1000).toFixed(3)+"s", endPoint[0], endPoint[1]+2, endPoint[2])
+ // }
+ }
+ });
+ }
+
+ if (this.blazeX !== -1) {
+ this.blazes = this.blazes.filter((e) => !e.getEntity()[f.isDead]);
+
+ this.blazes.sort((a, b) => a.getEntity().func_110143_aJ() - b.getEntity().func_110143_aJ());
+ if (
+ World.getBlockAt(this.blazeX + 17 - 43, 18, this.blazeY + 16 - 43)
+ .getType()
+ .getID() === 9
+ ) {
+ this.blazes = this.blazes.reverse();
+ }
+ }
+
+ // this.arrows = this.arrows.filter(e=>{
+ // let x = e.getX()
+ // let y = e.getY()
+ // let z = e.getZ()
+
+ // let mX = e.getMotionX()
+ // let mY = e.getMotionY()
+ // let mZ = e.getMotionZ()
+
+ // for(let i = 0;i<100;i++){
+ // x+=mX
+ // y+=mY
+ // z+=mZ
+
+ // mX*=0.99
+ // mY*=0.99
+ // mZ*=0.99
+
+ // mY-=0.05
+
+ // this.blazes = this.blazes.filter(e1=>{
+ // let boundingBox = e1.getEntity().func_174813_aQ()
+
+ // if(boundingBox.intersectsWith(new AxisAlignedBB(x-e.getEntity().field_70130_N/2,y,z-e.getEntity().field_70130_N/2,x+e.getEntity().field_70130_N/2,y+e.getEntity().field_70131_O,z+e.getEntity().field_70130_N/2))){
+ // return false
+ // }
+ // return true
+ // })
+
+ // if(World.getBlockAt(x, y, z).getType().getID() !== 0){
+ // break;
+ // }
+ // }
+
+ // return !e.getEntity()[f.isDead]
+ // })
+
+ if (this.spiritBearSpawnTimer.getValue() && (this.FeatureManager.features["dataLoader"].class.dungeonFloor === "F4" || this.FeatureManager.features["dataLoader"].class.dungeonFloor === "M4")) {
+ let id = World.getBlockAt(7, 77, 34).type.getID();
+
+ if ((!this.bearSpawning || (this.bearSpawning < 0 && this.bearSpawning > -Date.now() + 500)) && id === 169) {
+ this.bearSpawning = Date.now() + 3500;
+ }
+ }
+ }
+
+ addSkull(skull) {
+ if (this.bloodX !== -1) {
+ let xA = skull.getX() + 8 - ((skull.getX() + 8) % 32);
+ let yA = skull.getZ() + 8 - ((skull.getZ() + 8) % 32);
+
+ if (xA !== this.bloodX || yA !== this.bloodY) return;
+ } else {
+ if (skull.getEntity()[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]().trim() === getPlayerHeadName().replace("%s", Player.getName())) {
+ this.bloodX = skull.getX() + 8 - ((skull.getX() + 8) % 32);
+ this.bloodY = skull.getZ() + 8 - ((skull.getZ() + 8) % 32);
+ this.skulls = [];
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((e) => {
+ if (e.getEntity()[m.getEquipmentInSlot](4) && e.getEntity()[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]().endsWith(getSkullName())) {
+ this.addSkull(e);
+ }
+ });
+ }
+ return;
+ }
+ this.skulls.push(skull);
+ }
+
+ addBlaze(blaze) {
+ if (!this.FeatureManager.features["dataLoader"].class.dungeonFloor) return;
+ if (this.blazeX === -1) {
+ this.blazes.push(blaze);
+ let locs = {};
+
+ this.blazes.forEach((b) => {
+ if (!locs[b.getX() + 8 - ((b.getX() + 8) % 32) + "_" + (b.getZ() + 8 - ((b.getZ() + 8) % 32))]) locs[b.getX() + 8 - ((b.getX() + 8) % 32) + "_" + (b.getZ() + 8 - ((b.getZ() + 8) % 32))] = 0;
+ locs[b.getX() + 8 - ((b.getX() + 8) % 32) + "_" + (b.getZ() + 8 - ((b.getZ() + 8) % 32))]++;
+ });
+
+ Object.keys(locs).forEach((k) => {
+ if (locs[k] === 4) {
+ [this.blazeX, this.blazeY] = k.split("_").map((a) => ~~a);
+ }
+ });
+
+ if (this.blazeX !== -1) {
+ this.blazes = [];
+ World.getAllEntitiesOfType(EntityBlaze).forEach((e) => {
+ if (e.getX() + 8 - ((e.getX() + 8) % 32) === this.blazeX && e.getZ() + 8 - ((e.getZ() + 8) % 32) === this.blazeY) {
+ this.blazes.push(e);
+ }
+ });
+ }
+ } else {
+ if (blaze.getX() + 8 - ((blaze.getX() + 8) % 32) === this.blazeX && blaze.getZ() + 8 - ((blaze.getZ() + 8) % 32) === this.blazeY) {
+ this.blazes.push(blaze);
+ this.blazes.sort((a, b) => a.getEntity().func_110143_aJ() - b.getEntity().func_110143_aJ());
+ if (
+ World.getBlockAt(this.blazeX + 17 - 43, 18, this.blazeY + 16 - 43)
+ .getType()
+ .getID() === 9
+ ) {
+ this.blazes = this.blazes.reverse();
+ }
+ }
+ }
+ }
+
+ stepNotDung() {
+ if (Date.now() - this.lastWorldload < 5000) return
+ this.inBoss = false
+ }
+
+ step() {
+
+ if (this.ffCountdownTo && this.ffCountdownTo > 0) {
+ this.fireFreezeTimerElement.setText("&dFire freeze in: &c" + (Math.max(0, this.ffCountdownTo - Date.now()) / 1000).toFixed(2) + "s");
+ } else {
+ this.fireFreezeTimerElement.setText("");
+ }
+
+ if (this.bearSpawning && this.bearSpawning > 0) {
+ this.spiritBearSpawnElement.setText("&dBear spawned in: &c" + (Math.max(0, this.bearSpawning - Date.now()) / 1000).toFixed(2) + "s");
+ } else {
+ this.spiritBearSpawnElement.setText("");
+ }
+ if (!Player.getX()) return
+ World.getAllPlayers().forEach((p) => {
+ this.nameToUuid[p.getName().toLowerCase()] = p.getUUID().toString()
+ })
+ this.failedPuzzleCount = 0;
+ this.totalPuzzleCount = 0;
+ this.completedPuzzleCount = 0;
+ TabList.getNames().forEach((n) => {
+ let name = ChatLib.removeFormatting(n).trim().split(" ");
+ let end = name.pop();
+ // console.log(end) Water Board: [✔]
+ if (end !== "[✦]" && end !== "[✔]" && end !== "[✖]") {
+ end = name.pop();
+ // console.log(end) Water Board: [✔]
+ if (end !== "[✦]" && end !== "[✔]" && end !== "[✖]") return;
+ }
+ this.totalPuzzleCount++;
+ if (end === "[✖]") this.failedPuzzleCount++;
+ if (end === "[✔]") this.completedPuzzleCount++;
+ });
+
+ if (this.lividFindEnabled.getValue() && (this.FeatureManager.features["dataLoader"].class.dungeonFloor === "F5" || this.FeatureManager.features["dataLoader"].class.dungeonFloor === "M5")) {
+ let type = World.getBlockAt(3, 108, 30).getMetadata();
+
+ let typeReplace = {
+ 0: "Vendetta",
+ 2: "Crossed",
+ 4: "Arcade",
+ 5: "Smile",
+ 6: "Crossed",
+ 7: "Doctor",
+ 8: "Doctor",
+ 10: "Purple",
+ 11: "Scream",
+ 13: "Frog",
+ 14: "Hockey",
+ };
+
+ World.getAllEntities().forEach((entity) => {
+ let entityName = entity.getName();
+
+ if (entityName.includes("Livid") && entityName.includes("❤")) {
+ // ChatLib.chat("D: " + entityName.substr(1, 1) + " asd " + this.lividData.lividColor[typeReplace[type]].split("").pop())
+ if (entityName.substr(1, 1) === this.lividData.lividColor[typeReplace[type]].split("").pop()) {
+ this.lividHpElement.setText(entityName);
+ this.lividData.correctLividEntity = entity;
+ }
+ }
+ });
+ }
+
+ if (this.lividData.correctLividEntity) {
+ if (!this.renderEntityEvent.enabled) {
+ this.renderEntityEvent.register()
+ }
+ } else {
+ if (this.renderEntityEvent.enabled) {
+ this.renderEntityEvent.unregister()
+ }
+ }
+
+ this.spiritBowPickUps = this.spiritBowPickUps.filter((pickUp) => Date.now() - pickUp < 20000);
+ if (this.spiritBowPickUps[0] && this.isInDungeon()) {
+ this.spiritBowDestroyElement.setText("&dBow Destroyed in: &c" + Math.round((this.spiritBowPickUps[0] + 20000 - Date.now()) / 1000) + "s");
+ } else {
+ this.spiritBowDestroyElement.setText("");
+ }
+ // this.spiritBowPickUps
+ if (this.bloodCampAssist.getValue()) {
+ this.todoE.forEach((e) => {
+ let en = new Entity(e);
+ // console.log(en.getName())
+ if (en.getName().trim() === getArmorStandName() && e[m.getEquipmentInSlot](4) && e[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]().endsWith(getSkullName())) {
+ this.addSkull(en);
+ }
+ });
+
+ this.todoE = [];
+ }
+
+ let averageExp = this.lastDungExps.reduce((a, b) => a + b, 0) / this.lastDungExps.length;
+ let averageLength = (this.lastDungFinishes[this.lastDungFinishes.length - 1] - this.lastDungFinishes[0]) / (this.lastDungFinishes.length - 1);
+ let runsperHour = (60000 * 60) / averageLength;
+ let expPerHour = averageExp * runsperHour;
+
+ if (Date.now() - this.lastDungFinishes[this.lastDungFinishes.length - 1] < 60000 * 5 || this.FeatureManager.features["dataLoader"].class.dungeonFloor) {
+ if (this.lastDungFinishes.length > 1) {
+ this.runSpeedRatesElement.setText("&6Run speed&7> &f" + Math.floor(averageLength / 60000) + ":" + ((Math.floor(averageLength / 1000) % 60 < 10 ? "0" : "") + (Math.floor(averageLength / 1000) % 60)) + "\n&6Exp/hour&7> &f" + numberWithCommas(Math.round(expPerHour)) + "\n&6Runs/hour&7> &f" + Math.floor(runsperHour));
+ } else {
+ this.runSpeedRatesElement.setText("&6Run speed&7> &fLoading...\n&6Exp/hour&7> &fLoading...\n&6Runs/hour&7> &fLoading...");
+ }
+ } else {
+ this.runSpeedRatesElement.setText("");
+ }
+ }
+
+ step_1fps() {
+ if (this.IceSprayWarn.getValue()) {
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((name) => {
+ let MobName = ChatLib.removeFormatting(name.getName())
+ if (MobName.includes("Ice Spray Wand") && name.getTicksExisted() <= 199) {
+ Client.showTitle(`&r&6&l[&b&l&kO&6&l] ${MobName.toUpperCase()} &6&l[&b&l&kO&6&l]`, "", 0, 40, 10);
+ ChatLib.chat(`&6&lRARE DROP! &r${name.getName()}`)
+ }
+ })
+ }
+ }
+
+ initVariables() {
+ this.lividFindEnabled = undefined;
+ this.lividData = undefined;
+ this.hudElements = [];
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.unloadf7data()
+ this.initVariables();
+ }
+}
+
+module.exports = {
+ class: new DungeonSolvers(),
+};
+
+function getSkullName() {
+ if (translate) {
+ return translate.func_74805_b("item.skull.char.name");
+ }
+
+ return "Head";
+}
+
+function getPlayerHeadName() {
+ if (translate) {
+ return translate.func_74805_b("item.skull.player.name");
+ }
+
+ return "%s's Head";
+}
+function getArmorStandName() {
+ if (translate) {
+ return translate.func_74805_b("item.armorStand.name");
+ }
+
+ return "Armor Stand";
+}
diff --git a/src/features/dungeonSolvers/metadata.json b/src/features/dungeonSolvers/metadata.json
new file mode 100644
index 0000000..6467a3a
--- /dev/null
+++ b/src/features/dungeonSolvers/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Dungeon Solvers",
+ "description": "Solvers for dungeon things",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/events/index.js b/src/features/events/index.js
new file mode 100644
index 0000000..ea83312
--- /dev/null
+++ b/src/features/events/index.js
@@ -0,0 +1,816 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { f, m } from "../../../mappings/mappings";
+import Feature from "../../featureClass/class";
+import socketConnection from "../../socketConnection";
+import { drawBoxAtBlock, drawBoxAtBlockNotVisThruWalls, drawCoolWaypoint, drawLine } from "../../utils/renderUtils";
+import { calculateDistance, calculateDistanceQuick } from "../../utils/utils";
+import SettingBase from "../settings/settingThings/settingBase";
+import ToggleSetting from "../settings/settingThings/toggle";
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import ButtonSetting from "../settings/settingThings/button";
+import TextSetting from "../settings/settingThings/textSetting";
+
+let warpData = {
+ "castle": [-250, 130, 45],
+ "da": [91, 75, 176],
+ "museum": [-75, 76, 80],
+ "hub": [-2, 70, -69]
+}
+function getKeyBindFromKey(key, description) {
+ var mcKeyBind = undefined //MinecraftVars.getKeyBindFromKey(key);
+
+ if (mcKeyBind == null || mcKeyBind == undefined) {
+ mcKeyBind = new KeyBind(description, key);
+ }
+
+ return mcKeyBind;
+}
+
+class Events extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.burrialData = {
+ points: [],
+ locations: [],
+ historicalLocations: []
+ }
+ this.lastWorldChange = 0
+ this.lastRequest = 0
+ this.potentialParticleLocs = {}
+ this.showingWaypoints = false
+ this.lastPath = []
+ this.updatingPath = false
+ this.lastPathCords = undefined
+ this.openedWarpsMenu = false
+
+
+ this.loadFromParticles = new ToggleSetting("Load burrials from particles", "Will load particles from burrows in the world", true, "burrial_from_partles", this)
+ this.showBurrialGuess = new ToggleSetting("Estimate burrial location from ability", "Will show a line + box where it thinks the burrial is", true, "burrial_guess", this)
+ new ButtonSetting("NOTE: You must have music disabled", "for burrial guessess to work (/togglemusic)", "togglemusis_button", this, "toggle", () => {
+ ChatLib.command("togglemusic")
+ }, false).requires(this.showBurrialGuess)
+
+ this.otherInquisWaypoints = new ToggleSetting("Show other users inquis locations", "If disabled others wont be able to see urs", true, "inquis_location_other", this).requires(this.loadFromParticles)
+ this.otherInquisPing = new ToggleSetting("Show cool title when someone's inquis spawned", "May be usefull for loot share", true, "inquis_ping_other", this).requires(this.loadFromParticles)
+ this.limitPMemb = new ToggleSetting("Only send inquis ping to party members", "If not in a party it works as default", true, "inquis_ping_party", this).requires(this.otherInquisPing)
+ this.limitPMembRecieve = new ToggleSetting("Only RECIEVE inquis ping from party members", "To prevent trolling for streamers", false, "recieve_inquis_ping_party", this).requires(this.otherInquisPing)
+ this.shinyBlocks = []
+ this.glowingMushrooms = []
+
+ this.MythMobsHPGuiElement = new ToggleSetting("Render Mythological Mobs hp on your screen", "This will help you to know their HP.", true, "myth_mobs_hp", this).contributor("EmeraldMerchant");
+ this.MythMobsHP = new HudTextElement().setToggleSetting(this.MythMobsHPGuiElement).setLocationSetting(new LocationSetting("Mythological Mobs Hp Location", "Allows you to edit the location of Mythological Mobs hp", "myth_mobs_location", this, [10, 50, 1, 1]).requires(this.MythMobsHPGuiElement).editTempText("&8[&7Lv750&8] &2Exalted Minos Inquisitor &a40M&f/&a40M&c❤&r"));
+ this.hudElements.push(this.MythMobsHP);
+
+ this.Mobs = []
+ this.lastDing = 0
+ this.lastDingPitch = 0
+ this.firstPitch = 0
+ this.lastParticlePoint = undefined
+ this.firstParticlePoint = undefined
+ this.particlePoint = undefined
+ this.guessPoint = undefined
+ this.distance = undefined
+ this.dingIndex = 0
+ this.dingSlope = []
+
+ this.ignorePlayers = new Set()
+
+ new SettingBase("There is also a hotkey to warp near", "see minecraft controls menu", true, "warp_info_hotkey", this)
+ this.warpBindDefault = new TextSetting("Default warp keybind", "Eg KEY_F", "CHAR_NONE", "inquis_keybind_default", this, "", false)
+
+ try {
+ this.warpBind = getKeyBindFromKey(Keyboard[this.warpBindDefault.getValue()], "Warp to nearest location to burrial guess");
+ } catch (e) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + this.warpBindDefault.getValue() + " is an invalid keyboard key, see https://legacy.lwjgl.org/javadoc/org/lwjgl/input/Keyboard.html")
+ this.warpBind = getKeyBindFromKey(Keyboard.CHAR_NONE, "Warp to nearest location to burrial guess");
+ }
+
+ this.slayerLocationDataH = {}
+ this.todoE = []
+
+ this.hasWarps = new Set()
+
+ this.shinyBlockOverlayEnabled = new ToggleSetting("Shiny blocks highlight", "Will highlight shiny blocks in the end", false, "shiny_blocks_overlay", this)
+ this.showGlowingMushrooms = new ToggleSetting("Glowing mushrooms highlight", "Will highlight glowing mushrooms", false, "glowing_mushrooms_overlay", this)
+
+ this.registerEvent("worldLoad", this.worldLoad)
+ this.registerEvent("spawnParticle", this.spawnParticle).registeredWhen(() => this.showingWaypoints || this.shinyBlockOverlayEnabled.getValue() || this.showGlowingMushrooms.getValue())
+ this.registerEvent("renderWorld", this.renderWorld).registeredWhen(() => this.showingWaypoints || this.shinyBlockOverlayEnabled.getValue() || this.showGlowingMushrooms.getValue())
+ this.registerStep(true, 2, this.step)
+ this.registerStep(false, 5, this.step_5s)
+
+ this.registerEvent("soundPlay", this.playSound).registeredWhen(() => this.showingWaypoints)
+ this.registerForge(net.minecraftforge.event.entity.EntityJoinWorldEvent, this.entityJoinWorldEvent).registeredWhen(() => this.showingWaypoints);
+
+ this.registerChat("&r&eYou dug out a Griffin Burrow! &r&7(${*}/4)&r", this.burrialClicked)
+ this.registerChat("&r&eYou finished the Griffin burrow chain! &r&7(4/4)&r", this.burrialClicked)
+ this.inquisWaypointSpawned = false
+
+ this.registerEvent("tick", () => {
+ if (this.warpBind.isPressed()) {
+
+ if (!this.openedWarpsMenu) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Please open the warps menu first (/warp)")
+ ChatLib.chat(this.FeatureManager.messagePrefix + "(So the mod knows what warps u have access to)")
+ }
+ let loc = this.getClosestWarp()
+
+ if (loc) ChatLib.command(loc)
+ }
+ })
+
+ this.registerStep(true, 1, this.step_1fps)
+ this.registerStep(true, 10, this.step_10fps)
+
+
+ this.registerCommand("inqwaypointignoreadd", (player) => {
+ this.ignorePlayers.add(player)
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Added " + player + " to inquis waypoint ignore list, this will be cleared next game start!")
+
+ delete this.slayerLocationDataH[player]
+ })
+ this.registerCommand("cleardianawaypoints", () => {
+ this.burrialData.points = []
+ this.burrialData.locations = []
+ this.burrialData.historicalLocations = []
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Cleared all diana waypoints!")
+ })
+
+ this.locs = []
+ this.predictions = []
+ // this.predictionsOld = []
+ // this.registerEvent("renderWorld", () => {
+ // for (let loc of this.locs) {
+ // drawBoxAtBlock(loc[0], loc[1], loc[2], 255, 0, 0, 0.05, 0.05)
+ // }
+ // for (let loc of this.predictions) {
+ // drawBoxAtBlock(loc[0], loc[1], loc[2], 0, 255, 0, 0.05, 0.05)
+ // }
+ // })
+
+ // this.registerCommand("clearlocs", () => {
+ // this.locs = []
+ // this.predictions = []
+ // // this.predictionsOld = []
+ // ChatLib.chat(this.FeatureManager.messagePrefix + "Cleared all locs!")
+ // })
+ }
+
+ step_1fps() {
+ if (!this.MythMobsHPGuiElement.getValue() || !this.showingWaypoints) return
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((mob) => {
+ let name = mob.getName()
+ if (!this.Mobs?.map(a => a.getUUID().toString()).includes(mob.getUUID().toString())) {
+ if ((name.includes("Exalted") || name.includes("Stalwart")) && !name.split(" ")[2].startsWith("0")) {
+ this.Mobs.push(mob)
+ }
+ }
+ })
+ this.Mobs = this.Mobs.filter((e) => !e.getEntity()[f.isDead]);
+ }
+
+ step_10fps() {
+ if (!this.MythMobsHPGuiElement.getValue()) return
+ let names = []
+ this.Mobs.forEach(nameTag => {
+ names.push(nameTag.getName())
+ })
+ this.MythMobsHP.setText(names.join("\n"))
+ }
+
+ entityJoinWorldEvent(e) {
+ if (this.otherInquisWaypoints.getValue()) this.todoE.push(e.entity);
+ }
+
+ inquisData(loc, user) {
+ if (this.ignorePlayers.has(user) && user !== Player.getName()) return
+ if (!loc) {
+ delete this.slayerLocationDataH[user]
+ return
+ }
+
+ if (this.limitPMembRecieve.getValue()) {
+ if (!this.FeatureManager.features["dataLoader"].class.partyMembers.has(user)) return
+ }
+ this.slayerLocationDataH[user] = [loc, Date.now()]
+ if (this.otherInquisPing.getValue()) {
+ Client.showTitle("&r&6&l[&b&l&kO&6&l] MINOS INQUISITOR [&b&l&kO&6&l]", `${user}'s Inquisitor`, 0, 50, 10);
+ new TextComponent(this.FeatureManager.messagePrefix + `${user} spawned an inquis &7(waypoint added), &cCLICK HERE &7to ignore waypoints from them.`).setClick("run_command", "/inqwaypointignoreadd " + user).chat()
+ }
+ }
+
+ renderWorld(ticks) {
+ this.shinyBlocks.forEach(([loc]) => {
+ drawBoxAtBlockNotVisThruWalls(loc[0], loc[1], loc[2], 0, 255, 0, 0.1, 0.1)
+ })
+ this.glowingMushrooms.forEach(([loc]) => {
+ drawBoxAtBlockNotVisThruWalls(loc[0] - 0.2, loc[1], loc[2] - 0.2, 0, 255, 0, 0.4, 0.4)
+ })
+ if (this.showingWaypoints) {
+ if (this.guessPoint && this.showBurrialGuess.getValue()) {
+ let warpLoc = this.getClosestWarp()
+ if (this.guessPoint2) {
+ let gY = 131
+ while (World.getBlockAt(this.guessPoint2[0], gY, this.guessPoint2[2]).getType().getID() !== 2 && gY > 70) {
+ gY--
+ }
+ drawCoolWaypoint(this.guessPoint2[0], gY + 3, this.guessPoint2[2], 255, 255, 0, { name: "§eGuess" + (warpLoc ? " §7(" + warpLoc + ")" : "") })
+ }
+ // drawCoolWaypoint(this.guessPoint[0], this.guessPoint[1], this.guessPoint[2], 255, 255, 0, { name: "§7OLD Guess" + (warpLoc ? " §7(" + warpLoc + ")" : "") })
+ }
+ this.burrialData.locations.forEach((loc, i) => {
+
+ let typeReplace = [
+ "Start",
+ "Mob",
+ "Treasure",
+ "Finish",
+ "Unknown"
+ ]
+ if (!loc.clicked) {
+ blue = false
+ if (loc.lastPing && Date.now() - loc.lastPing < 500) {
+ blue = true
+ }
+
+ let name = ""
+
+ if (loc.fromApi) {
+ name = (loc.nearest ? "§c" : "§a") + "(" + (loc.chain + 1) + "/4) " + typeReplace[loc.type] + " burrial"
+ } else {
+ name = (loc.nearest ? "§c" : "§a") + typeReplace[loc.type] + " burrial"
+ }
+
+ drawCoolWaypoint(loc.x, loc.y, loc.z, 0, blue ? 100 : 255, blue ? 255 : 0, { name: name })
+ }
+ })
+ }
+
+ if (this.otherInquisWaypoints.getValue()) {
+ Object.keys(this.slayerLocationDataH).forEach(key => {
+ drawCoolWaypoint(this.slayerLocationDataH[key][0][0] || 0, this.slayerLocationDataH[key][0][1] || 0, this.slayerLocationDataH[key][0][2] || 0, 255, 0, 0, { name: "§c" + (key || "ERROR") + "'s inquis" })
+ })
+ }
+ }
+
+ sortBurrialLocations() {
+ let sorted = [...this.burrialData.locations]
+ sorted.sort((a, b) => {
+ let aDist = calculateDistanceQuick([Player.getX(), Player.getY(), Player.getZ()], [a.x + 0.5, a.y + 2.5, a.z + 0.5])
+ let bDist = calculateDistanceQuick([Player.getX(), Player.getY(), Player.getZ()], [b.x + 0.5, b.y + 2.5, b.z + 0.5])
+
+ return bDist - aDist
+ })
+ this.burrialData.locations = sorted
+ }
+
+ step() {
+ if (!Player.getInventory()) return
+
+ hasDianaShovle = false
+ let slots = [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ slots.forEach(a => {
+ item = Player.getInventory().getStackInSlot(a)
+ if (!item) return
+ if (ChatLib.removeFormatting(item.getName()) === "Ancestral Spade") {
+ hasDianaShovle = true
+ }
+ })
+
+ let showingWaypointsNew = (this.lastWorldChange + 5000 < Date.now() ? hasDianaShovle && this.FeatureManager.features["dataLoader"].class.area === "Hub" && (this.loadFromParticles.getValue() || this.showBurrialGuess.getValue()) : this.showingWaypoints || (hasDianaShovle && this.FeatureManager.features["dataLoader"].class.area === "Hub" && (this.loadFromParticles.getValue() || this.showBurrialGuess.getValue())))
+
+ this.showingWaypoints = showingWaypointsNew
+
+ this.shinyBlocks = this.shinyBlocks.filter(([loc, time]) => {
+ return time > Date.now() - 5000
+ })
+ this.glowingMushrooms = this.glowingMushrooms.filter(([loc, time]) => {
+ return time > Date.now() - 1000 && World.getBlockAt(...loc.map(a => Math.floor(a))).type.getID() !== 0
+ })
+
+
+ Object.keys(this.slayerLocationDataH).forEach(n => {
+ if (this.slayerLocationDataH[n][1] + 60000 * 3 < Date.now()) {
+ delete this.slayerLocationDataH[n]
+ }
+ })
+
+ this.todoE.forEach(e => {
+ e = new Entity(e)
+
+ if (e.getName().toLowerCase().includes("inquis") && !e.getName().includes("'") && Math.abs(e.getY() - Player.getY()) < 10 && Math.abs(e.getX() - Player.getX()) < 10 && Math.abs(e.getZ() - Player.getZ()) < 10) {
+ let loc = [e.getX(), e.getY() - 1, e.getZ()]
+ let self = false
+ this.burrialData.locations.forEach(a => {
+ if (calculateDistanceQuick([a.x, a.y, a.z], loc) < 25) {
+ self = true
+ }
+ })
+ if (self) {
+ let pmemb = []
+ this.FeatureManager.features["dataLoader"].class.partyMembers.forEach(a => pmemb.push(a))
+ socketConnection.sendInquisData({ loc: [Math.round(Player.getX()), Math.round(Player.getY()), Math.round(Player.getZ())], pmemb, limitPMemb: pmemb.length > 1 && this.limitPMemb.getValue() });
+ this.inquisWaypointSpawned = true
+ }
+ }
+ })
+ this.todoE = []
+
+ if (Player.getContainer().getName() === "Fast Travel") {
+ this.openedWarpsMenu = true
+ for (let item of Player.getContainer().getItems()) {
+ if (!item) continue
+ if (ChatLib.removeFormatting(item.getLore()[1]).startsWith("/warp") && warpData[ChatLib.removeFormatting(item.getLore()[1]).replace("/warp ", "")]) {
+
+ if (item.getLore().some(a => a.includes("Click to warp!"))) {
+ this.hasWarps.add(ChatLib.removeFormatting(item.getLore()[1]).replace("/warp ", ""))
+ }
+ }
+ }
+ }
+ }
+
+ getClosestWarp() {
+ if (!this.guessPoint) return undefined
+ let warp = undefined
+ let minDist = calculateDistance([Player.getX(), Player.getY(), Player.getZ()], this.guessPoint) - 50
+
+ this.hasWarps.forEach(w => {
+ if (!warpData[w]) return
+ let d = calculateDistance(warpData[w], this.guessPoint2)
+ if (d < minDist) {
+ warp = "warp " + w
+ minDist = d
+ }
+ })
+
+ return warp
+ }
+
+ step_5s() {
+ this.sortBurrialLocations()
+ }
+
+ worldLoad() {
+
+ this.burrialData.points = []
+ this.burrialData.locations = []
+ this.burrialData.historicalLocations = []
+ this.lastDing = 0
+ this.lastDingPitch = 0
+ this.firstPitch = 0
+ this.lastParticlePoint = undefined
+ this.lastParticlePoint2 = undefined
+ this.lastSoundPoint = undefined
+ this.firstParticlePoint = undefined
+ this.particlePoint = undefined
+ this.guessPoint = undefined
+ this.distance = undefined
+ this.dingIndex = 0
+ this.dingSlope = []
+
+ this.lastPath = undefined
+ this.lastPathCords = undefined
+
+ this.lastWorldChange = Date.now()
+ this.Mobs = []
+ }
+
+ playSound(pos, name, volume, pitch, categoryName, event) {
+ if (!this.showBurrialGuess.getValue()) return
+ // if (this.dingIndex > 13) return
+ // if (pos.getX() === Math.floor(Player.getX() * 8) / 8 && pos.getZ() === Math.floor(Player.getZ() * 8) / 8) return
+ if (name !== "note.harp") return
+ if (this.lastDing === 0) {
+ this.firstPitch = pitch
+ }
+ this.lastDing = Date.now()
+ if (pitch < this.lastDingPitch) {
+ this.firstPitch = pitch
+ this.dingIndex = 0
+ this.dingSlope = []
+ this.lastDingPitch = pitch
+ this.lastParticlePoint = undefined
+ this.lastParticlePoint2 = undefined
+ this.lastSoundPoint = undefined
+ this.firstParticlePoint = undefined
+ this.distance = undefined
+ this.locs = []
+ // this.predictionsOld = this.predictions
+ }
+ if (this.lastDingPitch === 0) {
+ this.lastDingPitch = pitch
+ this.distance = undefined
+ this.lastParticlePoint = undefined
+ this.lastParticlePoint2 = undefined
+ this.lastSoundPoint = undefined
+ this.firstParticlePoint = undefined
+ this.locs = []
+ // this.predictionsOld = this.predictions
+ return
+ }
+ this.dingIndex++
+ if (this.dingIndex > 1) this.dingSlope.push(pitch - this.lastDingPitch)
+ if (this.dingSlope.length > 20) this.dingSlope.shift()
+ let slope = this.dingSlope.reduce((a, b) => a + b, 0) / this.dingSlope.length
+ // console.log(this.dingSlope.join(","))
+ this.lastSoundPoint = [pos.getX(), pos.getY(), pos.getZ()]
+ this.lastDingPitch = pitch
+
+ if (!this.lastParticlePoint2 || !this.particlePoint || !this.firstParticlePoint) return
+ this.distance2 = Math.E / slope - Math.hypot(this.firstParticlePoint[0] - pos.getX(), this.firstParticlePoint[1] - pos.getY(), this.firstParticlePoint[2] - pos.getZ())
+ // console.log(this.dingIndex + " " + this.dingSlope / this.dingIndex + " " + pitch + " " + (pitch - this.lastDingPitch))
+
+ if (this.distance2 > 1000) {
+ this.distance2 = undefined
+ this.guessPoint = undefined
+ return
+ }
+
+ let lineDist = Math.hypot(this.lastParticlePoint2[0] - this.particlePoint[0], this.lastParticlePoint2[1] - this.particlePoint[1], this.lastParticlePoint2[2] - this.particlePoint[2])
+ let distance = this.distance2
+ let changes = [this.particlePoint[0] - this.lastParticlePoint2[0], this.particlePoint[1] - this.lastParticlePoint2[1], this.particlePoint[2] - this.lastParticlePoint2[2]]
+ changes = changes.map(a => a / lineDist)
+ this.guessPoint = [this.lastSoundPoint[0] + changes[0] * distance, this.lastSoundPoint[1] + changes[1] * distance, this.lastSoundPoint[2] + changes[2] * distance]
+ // let minD = Infinity
+ // for (let i = 0; i < this.predictions.length; i++) {
+ // let p = this.predictions[i]
+ // let d = (p[0] - this.guessPoint[0]) ** 2 + (p[2] - this.guessPoint[2]) ** 2
+ // if (d < minD) {
+ // minD = d
+ // this.guessPoint2 = [Math.floor(p[0]), 255, Math.floor(p[2])]
+ // }
+ // }
+ }
+
+ /**
+ * Solves the equasion y=b/(x+a)+c
+ * returns [a, b, c]
+ */
+ solveEquasionThing(x, y) {
+ let a = (-y[0] * x[1] * x[0] - y[1] * x[1] * x[2] + y[1] * x[1] * x[0] + x[1] * x[2] * y[2] + x[0] * x[2] * y[0] - x[0] * x[2] * y[2]) / (x[1] * y[0] - x[1] * y[2] + x[0] * y[2] - y[0] * x[2] + y[1] * x[2] - y[1] * x[0])
+ let b = (y[0] - y[1]) * (x[0] + a) * (x[1] + a) / (x[1] - x[0])
+ let c = y[0] - b / (x[0] + a)
+ return [a, b, c]
+ }
+
+ spawnParticle(particle, type, event) {
+ if (this.showingWaypoints && this.showBurrialGuess.getValue() && particle.toString().startsWith("EntityDropParticleFX,")) {
+
+ let run = false
+ if (this.lastSoundPoint && !run && Math.abs(particle.getX() - this.lastSoundPoint[0]) < 2 && Math.abs(particle.getY() - this.lastSoundPoint[1]) < 0.5 && Math.abs(particle.getZ() - this.lastSoundPoint[2]) < 2) {
+ run = true
+ }
+ if (run) {
+
+ if (this.locs.length < 100 && this.locs.length === 0 || particle.getX() + particle.getY() + particle.getZ() !== this.locs[this.locs.length - 1][0] + this.locs[this.locs.length - 1][1] + this.locs[this.locs.length - 1][2]) {
+
+ let currLoc = [particle.getX(), particle.getY(), particle.getZ()]
+ let distMultiplier = 1
+ {
+ if (this.locs.length > 2) {
+
+ let predictedDist = 0.06507 * this.locs.length + 0.259
+
+ let lastPos = this.locs[this.locs.length - 1]
+ let actualDist = Math.hypot(...currLoc.map((l, i) => {
+ return (l - lastPos[i])
+ }))
+
+ distMultiplier = actualDist / predictedDist
+ }
+ // if (this.locs.length > 2 && !this.distance) {
+ // let lastPos = this.locs[this.locs.length - 1]
+ // let dist = Math.hypot(...currLoc.map((l, i) => {
+ // return (l - lastPos[i])
+ // }))
+ // let lastPos2 = this.locs[this.locs.length - 2]
+ // let dist2 = Math.hypot(...currLoc.map((l, i) => {
+ // return (lastPos[i] - lastPos2[i])
+ // }))
+ // // console.log("------")
+ // // console.log(dist - dist2)
+ // this.distance = 20 / (dist - dist2) - (dist - dist2) * 80
+ // console.log(this.distance)
+ // // this.distance -= Math.hypot(this.firstParticlePoint[0] - particle.getX(), this.firstParticlePoint[1] - particle.getY(), this.firstParticlePoint[2] - particle.getZ())
+
+ // // console.log(Math.sqrt((Player.getX() - this.firstParticlePoint[0]) ** 2 + (Player.getZ() - this.firstParticlePoint[2]) ** 2))
+ // }
+ }
+ this.locs.push(currLoc)
+ if (this.locs.length > 5 && this.guessPoint) {
+ let slopeThing = this.locs.map((a, i) => {
+ if (i === 0) return
+ let lastLoc = this.locs[i - 1]
+ let currLoc = a
+ return Math.atan((currLoc[0] - lastLoc[0]) / (currLoc[2] - lastLoc[2]))
+ })
+
+ let [a, b, c] = this.solveEquasionThing([slopeThing.length - 5, slopeThing.length - 3, slopeThing.length - 1], [slopeThing[slopeThing.length - 5], slopeThing[slopeThing.length - 3], slopeThing[slopeThing.length - 1]])
+ // console.log(a, b, c)
+
+ let pr1 = []
+ let pr2 = []
+
+ let start = slopeThing.length - 1
+ let lastPos = [this.locs[start][0], this.locs[start][1], this.locs[start][2]]
+ let lastPos2 = [this.locs[start][0], this.locs[start][1], this.locs[start][2]]
+
+ let distCovered = 0
+ // this.locs.forEach((l, i) => {
+ // if (i === 0) return
+
+ // distCovered += Math.hypot(this.locs[i][0] - this.locs[i - 1][0], this.locs[i][1] - this.locs[i - 1][1], this.locs[i][2] - this.locs[i - 1][2])
+ // })
+
+ let ySpeed = (this.locs[this.locs.length - 1][1] - this.locs[this.locs.length - 2][1]) / Math.hypot(this.locs[this.locs.length - 1][0] - this.locs[this.locs.length - 2][0], this.locs[this.locs.length - 1][2] - this.locs[this.locs.length - 2][2])
+
+ let i = start + 1
+ while (distCovered < this.distance2 && i < 10000) {
+ let y = b / (i + a) + c
+
+ let dist = distMultiplier * (0.06507 * i + 0.259) //This is where the inaccuracy's come from
+ //dist = distance between particles for guessed line
+
+ let xOff = dist * Math.sin(y)
+ let zOff = dist * Math.cos(y)
+
+
+
+ let dencity = 5
+ for (let o = 0; o < dencity; o++) {
+ lastPos[0] += xOff / dencity
+ lastPos[2] += zOff / dencity
+
+ lastPos[1] += ySpeed * dist / dencity
+ lastPos2[1] += ySpeed * dist / dencity
+
+ lastPos2[0] -= xOff / dencity
+ lastPos2[2] -= zOff / dencity
+
+ pr1.push([...lastPos])
+ pr2.push([...lastPos2])
+
+
+ // distCovered += Math.hypot(xOff, zOff) / dencity
+ distCovered = Math.hypot(lastPos[0] - this.lastSoundPoint[0], lastPos[2] - this.lastSoundPoint[2])
+ if (distCovered > this.distance2) break;
+ }
+ i++
+ }
+ this.predictions = [...pr1, ...pr2]
+ // let minD = Infinity
+
+ let p1 = pr1[pr1.length - 1]
+ let p2 = pr2[pr2.length - 1]
+
+ if (!this.guessPoint) return
+
+ let d1 = (p1[0] - this.guessPoint[0]) ** 2 + (p1[2] - this.guessPoint[2]) ** 2
+ let d2 = (p2[0] - this.guessPoint[0]) ** 2 + (p2[2] - this.guessPoint[2]) ** 2
+
+ if (d1 < d2) {
+ this.guessPoint2 = [Math.floor(p1[0]), 255, Math.floor(p1[2])]
+ } else {
+ this.guessPoint2 = [Math.floor(p2[0]), 255, Math.floor(p2[2])]
+ }
+ // for (let i = 0; i < this.predictions.length; i++) {
+ // let p = this.predictions[i]
+ // let d2 = (p[0] - this.guessPoint[0]) ** 2 + (p[2] - this.guessPoint[2]) ** 2
+ // let d = Math.abs(this.distance ** 2 - (p[0] - this.firstParticlePoint[0]) ** 2 + (p[2] - this.firstParticlePoint[2]) ** 2)
+ // if (d < minD && d2 < 50 ** 2) {
+ // minD = d
+ // this.guessPoint2 = [Math.floor(p[0]), 255, Math.floor(p[2])]
+ // }
+ // }
+ // console.log(this.predictions[1].join(" "))
+ }
+
+ }
+ if (this.lastParticlePoint === undefined) {
+ this.firstParticlePoint = [particle.getX(), particle.getY(), particle.getZ()]
+ }
+ this.lastParticlePoint2 = this.lastParticlePoint
+ this.lastParticlePoint = this.particlePoint
+ this.particlePoint = [particle.getX(), particle.getY(), particle.getZ()]
+
+ if (!this.lastParticlePoint2 || !this.particlePoint || !this.firstParticlePoint || !this.distance || !this.lastSoundPoint) return
+
+ let lineDist = Math.hypot(this.lastParticlePoint2[0] - this.particlePoint[0], this.lastParticlePoint2[1] - this.particlePoint[1], this.lastParticlePoint2[2] - this.particlePoint[2])
+ let distance = this.distance2
+ let changes = [this.particlePoint[0] - this.lastParticlePoint2[0], this.particlePoint[1] - this.lastParticlePoint2[1], this.particlePoint[2] - this.lastParticlePoint2[2]]
+ changes = changes.map(a => a / lineDist)
+ this.guessPoint = [this.lastSoundPoint[0] + changes[0] * distance, this.lastSoundPoint[1] + changes[1] * distance, this.lastSoundPoint[2] + changes[2] * distance]
+ }
+ }
+ if (this.shinyBlockOverlayEnabled.getValue() && this.FeatureManager.features["dataLoader"].class.areaFine === "The End") {
+ if (particle.toString().startsWith("EntitySpellParticleFX,")) {
+ if (particle.getUnderlyingEntity().func_70534_d() === particle.getUnderlyingEntity().func_70535_g()) {
+ let arr = [particle.getX(), particle.getY(), particle.getZ()]
+ if (arr.map(a => Math.abs(a % 1)).includes(0.25) || arr.map(a => Math.abs(a % 1)).includes(0.75)) {
+ this.shinyBlocks.push([[particle.getX(), particle.getY(), particle.getZ()], Date.now()])
+ }
+ }
+ }
+ }
+ if (this.showGlowingMushrooms.getValue() && this.FeatureManager.features["dataLoader"].class.areaFine === "Glowing Mushroom Cave") {
+ if (particle.toString().startsWith("EntitySpellParticleFX,")) {
+ // console.log([particle.getX(), particle.getY(), particle.getZ()].map(a => a % 1))
+ if (Math.abs(particle.getX() % 1) === 0.5 && Math.abs(particle.getZ() % 1) === 0.5) {
+ this.glowingMushrooms.push([[particle.getX(), particle.getY(), particle.getZ()], Date.now()])
+ }
+ }
+ }
+ if (this.showingWaypoints && this.loadFromParticles.getValue()) {
+ let foundEnchant = false
+ let foundCrit = false
+ let foundStep = false
+ let isMob = undefined
+
+ if (particle.toString().startsWith('EntityEnchantmentTableParticleFX, ')) {
+ foundEnchant = true
+ }
+ else if (particle.toString().startsWith('EntityCrit2FX, ')) {
+ foundCrit = true
+
+ isMob = particle.getUnderlyingEntity().func_70534_d() > 0.5 //mob)
+ }
+ else if (particle.toString().startsWith('EntityFootStepFX, ')) {
+ foundStep = true
+ }
+ else if (particle.toString().startsWith('EntityCritFX, ')) {
+
+ let locstr = Math.floor(particle.getX()) + "," + Math.floor(particle.getY() - 1) + "," + Math.floor(particle.getZ())
+
+ let removed = false
+ this.burrialData.locations.filter((loc, i) => {
+ if (!loc.clicked && loc.x + "," + loc.y + "," + loc.z === locstr) {
+ loc.clicked = true
+ removed = true
+ }
+ })
+ if (!removed) return;
+ this.burrialData.locations = this.burrialData.locations.filter(a => {
+ if (!a.clicked) return true
+ if (calculateDistanceQuick([a.x, a.y, a.z], [Player.getX(), Player.getY(), Player.getZ()]) < 15 * 15) return true;
+
+ this.burrialData.historicalLocations.unshift(a)
+
+ return false
+ })
+ if (this.burrialData.historicalLocations.length > 10) this.burrialData.historicalLocations.pop()
+
+ return;
+ }
+
+ if (!foundEnchant && !foundCrit && !foundStep) return;
+
+ if (Math.abs(particle.getY() % 1) > 0.1) return
+ if (Math.abs(particle.getX() % 1) < 0.1) return
+ if (Math.abs(particle.getX() % 1) > 0.9) return
+ if (Math.abs(particle.getZ() % 1) < 0.1) return
+ if (Math.abs(particle.getZ() % 1) > 0.9) return
+
+ let locstr = Math.floor(particle.getX()) + "," + Math.floor(particle.getY() - 1) + "," + Math.floor(particle.getZ())
+ let locarr = [Math.floor(particle.getX()), Math.floor(particle.getY() - 1), Math.floor(particle.getZ())]
+
+ let found = false
+
+ this.burrialData.locations.forEach((loc) => {
+ if (loc.x + "," + loc.y + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if ((loc.x + 1) + "," + loc.y + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if ((loc.x + 1) + "," + (loc.y + 1) + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if ((loc.x + 1) + "," + (loc.y - 1) + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if ((loc.x - 1) + "," + (loc.y + 1) + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if ((loc.x - 1) + "," + (loc.y - 1) + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if ((loc.x - 1) + "," + loc.y + "," + loc.z === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if (loc.x + "," + loc.y + "," + (loc.z + 1) === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ if (loc.x + "," + loc.y + "," + (loc.z - 1) === locstr) {
+ found = loc
+ loc.lastPing = Date.now()
+ }
+ })
+ if (this.burrialData.historicalLocations) {
+ this.burrialData.historicalLocations.forEach((loc) => {
+ if (loc.x + "," + loc.y + "," + loc.z === locstr) {
+ found = loc
+ }
+ })
+ }
+
+ if (!this.potentialParticleLocs[locstr] || Date.now() - this.potentialParticleLocs[locstr].timestamp > 30000) this.potentialParticleLocs[locstr] = { enchant: 0, crit: 0, step: 0, isMob: 0, timestamp: Date.now() }
+
+ if (foundEnchant) this.potentialParticleLocs[locstr].enchant++
+ if (foundCrit) this.potentialParticleLocs[locstr].crit++
+ if (foundStep) this.potentialParticleLocs[locstr].step++
+ if (foundCrit && isMob) this.potentialParticleLocs[locstr].isMob++
+ if (foundCrit && !isMob) this.potentialParticleLocs[locstr].isMob--
+
+ this.potentialParticleLocs[locstr].timestamp = Date.now()
+
+ if (this.potentialParticleLocs[locstr].enchant >= 1 && this.potentialParticleLocs[locstr].step >= 2) {
+ if (found) {
+ found.type = this.potentialParticleLocs[locstr].isMob >= 1 ? 1 : (this.potentialParticleLocs[locstr].crit > this.potentialParticleLocs[locstr].enchant / 20 ? 0 : 2)
+ return
+ }
+ this.burrialData.locations.push({
+ "x": locarr[0],
+ "y": locarr[1],
+ "z": locarr[2],
+ "type": this.potentialParticleLocs[locstr].isMob >= 1 ? 1 : (this.potentialParticleLocs[locstr].crit > this.potentialParticleLocs[locstr].enchant / 20 ? 0 : 2),
+ "tier": -1,
+ "chain": -1,
+ "fromApi": false
+ })
+ World.playSound("note.pling", 100, 2)
+ }
+ }
+ }
+
+ burrialClicked() {
+ this.locs = []
+ this.predictions = []
+ // this.predictionsOld = []
+ if (this.inquisWaypointSpawned) {
+ socketConnection.sendInquisData({ loc: null });
+ this.inquisWaypointSpawned = false
+ }
+ if (!this.showingWaypoints) return
+
+ let nearestBurriali = undefined
+ let nearestBurrialDist = Infinity
+
+ this.burrialData.locations.forEach((loc, i) => {
+ let dist = calculateDistanceQuick([loc.x, loc.y, loc.z], [Player.getX(), Player.getY(), Player.getZ()])
+ if (dist < nearestBurrialDist) {
+ nearestBurrialDist = dist
+ nearestBurriali = i
+ }
+ })
+
+ if (nearestBurriali === undefined) return;
+ this.burrialData.locations[nearestBurriali].clicked = true
+
+ this.burrialData.locations = this.burrialData.locations.filter(a => {
+ if (!a.clicked) return true
+ if (calculateDistanceQuick([a.x, a.y, a.z], [Player.getX(), Player.getY(), Player.getZ()]) < 15 * 15) return true;
+
+ this.burrialData.historicalLocations.unshift(a)
+
+ return false
+ })
+ if (this.burrialData.historicalLocations.length > 10) this.burrialData.historicalLocations.pop()
+ if (this.lastPathCords) this.lastPathCords.shift()
+ }
+
+ initVariables() {
+ this.burrialData = undefined
+ this.potentialParticleLocs = undefined
+ this.showingWaypoints = undefined
+ this.lastPath = undefined
+ this.updatingPath = undefined
+ this.lastPathCords = undefined
+ this.hudElements = [];
+ this.Mobs = []
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.initVariables();
+ }
+}
+
+module.exports = {
+ class: new Events()
+}
diff --git a/src/features/events/metadata.json b/src/features/events/metadata.json
new file mode 100644
index 0000000..363625f
--- /dev/null
+++ b/src/features/events/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Events",
+ "description": "Features for various events (eg diana)",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/eventsGUI/index.js b/src/features/eventsGUI/index.js
new file mode 100644
index 0000000..90e6993
--- /dev/null
+++ b/src/features/eventsGUI/index.js
@@ -0,0 +1,287 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Enum from "../../../guimanager/Enum";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import BoxWithLoading from "../../../guimanager/GuiElement/BoxWithLoading";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import TextBox from "../../../guimanager/GuiElement/TextBox";
+import Feature from "../../featureClass/class";
+import socketConnection from "../../socketConnection";
+import { numberWithCommas, timeSince } from "../../utils/numberUtils";
+import { firstLetterCapital, firstLetterWordCapital } from "../../utils/stringUtils";
+import GuiPage from "../soopyGui/GuiPage";
+
+class EventsGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new EventsPage()
+
+ // this.registerChat("&9&m-----------------------------------------------------&r&9${*}&r&9 ${*} &6Friends (Page ${pagenum} of ${maxpages})${friendslist}&r&9&m-----------------------------------------------------&r", (...args) => { this.GuiPage.friendListMessageEvent.call(this.GuiPage, ...args) })
+ this.registerStep(true, 5, () => { this.GuiPage.regenGuiElements.call(this.GuiPage) })
+ this.registerStep(false, 10, () => { this.GuiPage.pollData.call(this.GuiPage) })
+ }
+
+ eventsDataUpdated(data) {
+ this.GuiPage.eventsDataUpdated(data)
+ }
+
+ joinEventResult(data) {
+ this.GuiPage.joinEventResult(data)
+ }
+
+ pollEventData(admin) {
+ this.GuiPage.pollEventData(admin)
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class EventsPage extends GuiPage {
+ constructor() {
+ super(8)
+
+ this.name = "Events"
+
+ this.pages = [this.newPage()]
+
+ this.leaderboardElm = undefined
+ this.memberData = undefined
+
+ this.lastScroll = 1
+
+ this.leaderboardChildren = []
+
+ this.code = undefined
+
+ this.finaliseLoading()
+ }
+
+ updateNotInEvent() {
+ this.pages[0].clearChildren()
+
+ this.pages[0].addChild(new SoopyTextElement().setText("§0You are not currently in any events").setMaxTextScale(3).setLocation(0.2, 0.1, 0.6, 0.2))
+ this.pages[0].addChild(new SoopyTextElement().setText("§0If you have a join code enter it here").setMaxTextScale(1).setLocation(0.3, 0.4, 0.4, 0.1))
+ let joinBox = new TextBox().setPlaceholder("Code here").setLocation(0.3, 0.5, 0.4, 0.1)
+ this.pages[0].addChild(joinBox)
+
+ this.pages[0].addChild(new ButtonWithArrow().setLocation(0.35, 0.6, 0.3, 0.1).setText("§0Join Event").addEvent(new SoopyMouseClickEvent().setHandler(() => {
+
+ let code = joinBox.getText()
+
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new BoxWithLoading().setLocation(0.3, 0.3, 0.4, 0.4))
+
+ socketConnection.pollEventCode(code)
+
+ this.code = code
+ })))
+ }
+
+ updateInEvent(data) {
+ //MAIN PAGE
+
+ this.pages[0].clearChildren()
+
+ this.pages[0].addChild(new SoopyTextElement().setText("§0You are curently in an event managed by §6" + data.admin).setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.2))
+
+ if (!data.members[Player.getUUID().toString().replace(/-/g, "")]) {
+ this.pages[0].addChild(new ButtonWithArrow().setText("§0Join").setLocation(0.2, 0.2, 0.6, 0.1).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new BoxWithLoading().setLocation(0.3, 0.3, 0.4, 0.4))
+
+ socketConnection.pollEventCode(data.code)
+
+ this.code = data.code
+ })))
+ }
+
+ let leaderboard = new SoopyGuiElement().setLocation(0.1, 0.3, 0.8, 0.7).setScrollable(true)
+
+ this.pages[0].addChild(leaderboard)
+
+ let playerPosition = -1
+
+ if (this.leaderboardElm) {
+ let scroll = this.leaderboardElm._scrollAmount
+
+ leaderboard._scrollAmount = scroll
+ leaderboard.location.scroll.y.set(scroll, 0)
+ }
+
+ this.leaderboardElm = leaderboard
+
+ Object.values(data.members).sort((a, b) => b.progress - a.progress).forEach((m, i) => {
+ let isPlayer = m.uuid === Player.getUUID().toString().replace(/-/g, "")
+
+ if (isPlayer) playerPosition = i + 1
+
+ let nameLine = new SoopyTextElement().setText(`${isPlayer ? "§d" : "§0"}#${i + 1} ${m.username}`).setLocation(0, i * 0.05, 0.5, 0.05).setLore(["Last updated " + timeSince(m.timestamp) + " ago", "Currently: " + (m.online ? "§aOnline" : "§cOffline")])
+ nameLine.timestamp = m.timestamp
+ nameLine.online = m.online
+ leaderboard.addChild(nameLine)
+
+ let lore = [
+ `TOTAL: §e${numberWithCommas(Math.floor(m.progress))}`,
+ ` `
+ ]
+ Object.keys(m.current.weights).map(w => {
+ let progress = m.current.weights[w] - m.starting.weights[w]
+
+ return [w, progress]
+ }).sort((a, b) => b[1] - a[1]).forEach((l, i) => {
+ if (i > 10) return
+
+ lore.push(firstLetterWordCapital(l[0].replace(/_/g, " ").replace(/^.+? /, "")) + ": §e" + numberWithCommas(Math.floor(l[1])))
+ })
+
+
+ leaderboard.addChild(new SoopyTextElement().setText(`§0+${numberWithCommas(Math.floor(m.progress))}`).setLocation(0.5, i * 0.05, 0.5, 0.05).setLore(lore))
+ })
+
+ this.leaderboardChildren = [...leaderboard.children]
+
+ this.lastScroll = 1
+
+ if (playerPosition >= 0) {
+ this.pages[0].addChild(new SoopyTextElement().setText("§0You are #" + playerPosition + " with +" + Math.floor(data.members[Player.getUUID().toString().replace(/-/g, "")].progress)).setMaxTextScale(2).setLocation(0.2, 0.2, 0.6, 0.1))
+ }
+
+ // SIDEBAR
+
+ let sideBarElm = new SoopyGuiElement().setLocation(0, 0, 1, 1).setScrollable(true)
+
+ sideBarElm.addChild(new SoopyTextElement().setText("§0Event Settings").setMaxTextScale(3).setLocation(0.1, 0, 0.8, 0.2))
+
+ if (Player.getUUID().toString().replace(/-/g, "") === data.admin) {
+ sideBarElm.addChild(new ButtonWithArrow().setText("§0Change").setMaxTextScale(3).setLocation(0.1, 0.2, 0.8, 0.2).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+
+ })))
+ }
+
+ sideBarElm.addChild(new SoopyMarkdownElement().setLocation(0.05, 0.2, 0.9, 1).setText("# Tracking: \n" + data.settings.tracking.map(a => firstLetterCapital(a.replace(/\w+?_/, "").replace(/_/g, " "))).join("\n")))
+
+ this.openSidebarPage(sideBarElm)
+ }
+
+ regenGuiElements() {
+ if (!this.isOpen()) return
+
+ if (this.leaderboardElm) {
+ let scroll = this.leaderboardElm.location.scroll.y.get()
+ if (this.lastScroll !== scroll) {
+ this.lastScroll = scroll
+
+ this.leaderboardElm.children = []
+
+ let min = this.leaderboardElm.location.getYExact() - 100
+ let max = min + 200 + this.leaderboardElm.location.getHeightExact()
+ let lastChildNotAdded = undefined
+ this.leaderboardChildren.forEach(c => {
+ c.setParent(this.leaderboardElm)
+ c.triggerEvent(Enum.EVENT.RESET_FRAME_CACHES)
+
+ let y = c.location.getYExact()
+
+ if (y > min && y < max) {
+ this.leaderboardElm.children.push(c)
+ } else {
+ lastChildNotAdded = c
+ }
+ })
+
+ if (lastChildNotAdded) {
+ this.leaderboardElm.children.push(lastChildNotAdded)
+ }
+ }
+
+
+ this.leaderboardChildren.forEach(c => {
+ if (c.timestamp) {
+ c.setLore(["Last updated " + timeSince(c.timestamp) + " ago", "Currently: " + (c.online ? "§aOnline" : "§cOffline")])
+ }
+ })
+ }
+ }
+
+ eventsDataUpdated(data) {
+ if (!data.inEvent) {
+ this.updateNotInEvent()
+ return
+ }
+
+ this.updateInEvent(data)
+ }
+
+ pollEventData(admin) {
+ if (!admin) {
+ this.updateNotInEvent()
+ this.pages[0].addChild(new SoopyTextElement().setText("§cInvalid code").setMaxTextScale(3).setLocation(0.2, 0.7, 0.6, 0.2))
+ return
+ }
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Join §6" + admin + "§0's event?").setMaxTextScale(3).setLocation(0.2, 0.2, 0.6, 0.2))
+ this.pages[0].addChild(new ButtonWithArrow().setText("§0Join").setLocation(0.4, 0.4, 0.4, 0.3).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ socketConnection.joinEvent(this.code)
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new BoxWithLoading().setLocation(0.3, 0.3, 0.4, 0.4))
+ })))
+ this.pages[0].addChild(new ButtonWithArrow().setText("§0Cancel").setLocation(0.2, 0.4, 0.2, 0.3).setDirectionRight(false).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.updateNotInEvent()
+ })))
+ }
+
+ joinEventResult(data) {
+ this.pages[0].clearChildren()
+ if (data.success) {
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Joined event!").setMaxTextScale(3).setLocation(0.2, 0.2, 0.6, 0.2))
+ this.pages[0].addChild(new ButtonWithArrow().setText("§0Ok").setLocation(0.3, 0.4, 0.4, 0.4).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new BoxWithLoading().setLocation(0.3, 0.3, 0.4, 0.4))
+
+ socketConnection.pollEventData()
+ })))
+ } else {
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Unable to join event!").setMaxTextScale(3).setLocation(0.2, 0.2, 0.6, 0.1))
+ this.pages[0].addChild(new SoopyTextElement().setText("§0" + data.reason).setMaxTextScale(3).setLocation(0.2, 0.3, 0.6, 0.1))
+ this.pages[0].addChild(new ButtonWithArrow().setText("§0Ok").setLocation(0.3, 0.4, 0.4, 0.4).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new BoxWithLoading().setLocation(0.3, 0.3, 0.4, 0.4))
+
+ socketConnection.pollEventData()
+ })))
+ }
+ }
+
+ pollData() {
+ if (!this.isOpen()) return
+
+ socketConnection.pollEventData()
+ }
+
+ onOpen() {
+ this.pages[0].clearChildren()
+ this.pages[0].addChild(new BoxWithLoading().setLocation(0.3, 0.3, 0.4, 0.4))
+
+ this.leaderboardElm = undefined
+ socketConnection.pollEventData()
+ }
+}
+
+module.exports = {
+ class: new EventsGui()
+} \ No newline at end of file
diff --git a/src/features/eventsGUI/metadata.json b/src/features/eventsGUI/metadata.json
new file mode 100644
index 0000000..b7b1f6f
--- /dev/null
+++ b/src/features/eventsGUI/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Friends gui",
+ "description": "Gui for friends",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/featureBase/index.js b/src/features/featureBase/index.js
new file mode 100644
index 0000000..2a96a7f
--- /dev/null
+++ b/src/features/featureBase/index.js
@@ -0,0 +1,24 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+
+class FeatureBase extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+ }
+
+ initVariables() {
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+module.exports = {
+ class: new FeatureBase()
+} \ No newline at end of file
diff --git a/src/features/featureBase/metadata.json b/src/features/featureBase/metadata.json
new file mode 100644
index 0000000..a6b411d
--- /dev/null
+++ b/src/features/featureBase/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Feature Base",
+ "description": "Copy paste and edit this to create a new feature",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": false,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/fpsImproveGui/index.js b/src/features/fpsImproveGui/index.js
new file mode 100644
index 0000000..416d754
--- /dev/null
+++ b/src/features/fpsImproveGui/index.js
@@ -0,0 +1,49 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+
+class FpsImproveGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ // this.GuiPage = new FpsPage()
+
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class FpsPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Fps Improve Tips"
+
+ this.pages = [this.newPage()]
+
+ // this.pages[0].addChild(this.streamsBox)
+
+ this.finaliseLoading()
+ }
+
+ onOpen() {
+ this.updateStreams()
+ }
+}
+
+
+module.exports = {
+ class: new FpsImproveGui()
+} \ No newline at end of file
diff --git a/src/features/fpsImproveGui/metadata.json b/src/features/fpsImproveGui/metadata.json
new file mode 100644
index 0000000..a292c1d
--- /dev/null
+++ b/src/features/fpsImproveGui/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Fps improvement tips",
+ "description": "May help",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/fragBot/index.js b/src/features/fragBot/index.js
new file mode 100644
index 0000000..2b89203
--- /dev/null
+++ b/src/features/fragBot/index.js
@@ -0,0 +1,84 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import SettingBase from "../settings/settingThings/settingBase";
+import ToggleSetting from "../settings/settingThings/toggle";
+
+class FragBot extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.hostingFragBot = false
+ this.fragBotQueue = []
+ this.commandQueue = []
+
+ new SettingBase("To host a fragbot use /fragbot", "", undefined, "host_fragbot_info", this)
+ new SettingBase("NOTE: All this does atm is accept party invites", "More features for this coming soon", true, "stat_next_to_name_description", this)
+
+ // this.uploadToWebsite = new ToggleSetting("Advertise fragbot status", "Will show up as a fragbot in other peoples fragbot lists", true, "advertise_fragbot", this)
+
+ this.registerCommand("fragbot", this.fragbotCommand)
+
+ this.registerStep(false, 5, this.step).registeredWhen(() => this.hostingFragBot)
+ this.registerStep(true, 2, this.step2).registeredWhen(() => this.hostingFragBot)
+
+ this.registerChat("&9&m---------------------------${*}&r&9\n&r${player} &r&ehas invited you to join their party!\n&r&eYou have &r&c60 &r&eseconds to accept. &r&6Click here to join!&r&9\n&r&9&m----------------------------${*}&r", this.recievedPartyInvite)
+ }
+
+ step() {
+ if (!this.hostingFragBot) return
+
+ if (this.fragBotQueue.length > 0) {
+ let player = this.fragBotQueue.shift()
+ if (player) {
+ this.commandQueue.push("/party leave")
+
+ this.commandQueue.push("/party accept " + player)
+ }
+ }
+ }
+ step2() {
+ if (!this.hostingFragBot) return
+
+ if (this.commandQueue.length > 0) {
+ let command = this.commandQueue.shift()
+ if (command) {
+ ChatLib.say(command)
+ }
+ }
+ }
+ recievedPartyInvite(player) {
+ if (!this.hostingFragBot) return
+ player = ChatLib.removeFormatting(player).split(" ").pop()
+
+ this.fragBotQueue.push(player)
+ }
+
+ fragbotCommand(...args) {
+ if (this.hostingFragBot) {
+ this.hostingFragBot = false
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Fragbot has been disabled")
+ } else {
+ this.hostingFragBot = true
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Now acting as a fragbot, run /fragbot again to disable")
+ }
+ }
+
+ initVariables() {
+ this.hostingFragBot = undefined
+ this.fragBotQueue = undefined
+ this.commandQueue = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+module.exports = {
+ class: new FragBot()
+} \ No newline at end of file
diff --git a/src/features/fragBot/metadata.json b/src/features/fragBot/metadata.json
new file mode 100644
index 0000000..5aaa6a9
--- /dev/null
+++ b/src/features/fragBot/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Frag Bot",
+ "description": "Features to assist using fragbots, or to host one",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/friendsGUI/index.js b/src/features/friendsGUI/index.js
new file mode 100644
index 0000000..d4ac2f4
--- /dev/null
+++ b/src/features/friendsGUI/index.js
@@ -0,0 +1,363 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { SoopyRenderEvent } from "../../../guimanager";
+import ButtonWithArrowAndDescription from "../../../guimanager/GuiElement/ButtonWithArrowAndDescription";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import BoxWithLoading from "../../../guimanager/GuiElement/BoxWithLoading";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import ProgressBar from "../../../guimanager/GuiElement/ProgressBar";
+import logger from "../../logger";
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement";
+
+class FriendsGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new SettingPage()
+
+ this.registerChat("&9&m-----------------------------------------------------&r&9${*}&r&9 ${*} &6Friends (Page ${pagenum} of ${maxpages})${friendslist}&r&9&m-----------------------------------------------------&r", (...args) => { this.GuiPage.friendListMessageEvent.call(this.GuiPage, ...args) })
+ this.registerStep(true, 5, () => { this.GuiPage.regenGuiElements.call(this.GuiPage) })
+
+ this.registerStep(false, 1, () => { this.GuiPage.runComm.call(this.GuiPage) })
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class SettingPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Friends"
+
+ this.pages = [this.newPage()]
+
+ this.lastRender = 0
+
+ this.pages[0].addEvent(new SoopyRenderEvent().setHandler(() => { this.lastRender = Date.now() }))
+
+
+ let friendsTitle = new SoopyTextElement().setText("§0Friends").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.pages[0].addChild(friendsTitle)
+
+ this.friendsArea = new SoopyGuiElement().setLocation(0.1, 0.2, 0.8, 0.8).setScrollable(true)
+ this.pages[0].addChild(this.friendsArea)
+
+ this.loadedFriends = {}
+
+ this.loadedPages = new Set()
+
+ this.selectedFriends = new Set()
+
+ this.allFriendsLoaded = false
+
+ this.pageCount = undefined
+
+ this.sidebar = new SoopyGuiElement()
+
+ this.sidebarCustomPage = undefined
+
+ this.sidebarMainPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+
+ this.heightPerFriend = 0.15
+ this.loadingElement = new BoxWithLoading().setLocation(0, 0, 1, 0.175)
+ this.lastLoadedPage = 0
+ this.maxLoadedPage = 0
+
+ this.unfriendMode = false
+
+ this.loadingElement.addEvent(new SoopyRenderEvent().setHandler(() => {
+ if (Date.now() - this.lastLoadedPage > 1000) {
+ this.loadFriendPage(this.maxLoadedPage + 1)
+ }
+ }))
+
+ this.commandQueue = []
+
+ this.updateSidebar()
+
+ this.finaliseLoading()
+ }
+
+ loadAllFriends() {
+ let commands = []
+ for (let i = this.maxLoadedPage + 1; i < this.pageCount + 1; i++) {
+ commands.push("/friend list " + i)
+ }
+
+ this.runCommandsArray(commands)
+ }
+
+ runCommandsArray(commands, callback) {
+ this.sidebarCustomPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+
+
+ this.sidebarCustomPage.addChild(
+ new ButtonWithArrow().setText("§0CANCEL!").setLocation(0.1, 0.20, 0.8, 0.1)
+ .addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.commandQueue = []
+ this.sidebarCustomPage = undefined
+
+ this.updateSidebar()
+
+ if (callback) callback()
+ })))
+
+ this.sidebarCustomPage.addChild(new SoopyTextElement().setText("§0Running commands...").setLocation(0.2, 0.1, 0.6, 0.1).setMaxTextScale(10))
+
+
+ let progressBar = new ProgressBar().setProgress(0).setLocation(0.2, 0.3, 0.6, 0.1)
+
+ let elements = []
+
+ this.sidebarCustomPage.addChild(progressBar)
+ for (let i = 0; i < commands.length; i++) {
+ let i2 = i
+
+ elements.push(new SoopyTextElement().setText("§0" + commands[i]).setLocation(0.2, 0.4 + i * 0.025, 0.6, 0.025))
+
+ this.runCommand(commands[i], () => {
+ progressBar.setProgress((i2 + 1) / (commands.length), 1000)
+
+ let e = elements.shift()
+ this.sidebarCustomPage.removeChild(e)
+
+ elements.forEach((e, i) => {
+ e.location.location.y.set(0.4 + i * 0.025, 500)
+ })
+
+ if (i2 === commands.length - 1) {
+ this.sidebarCustomPage = undefined
+
+ this.updateSidebar()
+
+ if (callback) callback()
+ }
+ })
+ }
+
+ for (let e of elements) {
+ this.sidebarCustomPage.addChild(e)
+ }
+ }
+
+ updateSidebar() {
+ this.sidebar.clearChildren()
+ if (this.sidebarCustomPage) {
+ this.sidebar.addChild(this.sidebarCustomPage)
+ return
+ }
+
+ this.sidebarMainPage.clearChildren()
+ if (this.selectedFriends.size > 0) {
+ this.sidebarMainPage.addChild(
+ new SoopyTextElement().setText("§0" + this.selectedFriends.size + " friends selected").setMaxTextScale(2).setLocation(0.1, 0.05, 0.8, 0.1))
+
+ this.sidebarMainPage.addChild(
+ new ButtonWithArrow().setText("§0Deselect all").setLocation(0.1, 0.15, 0.8, 0.1)
+ .addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.selectedFriends.clear()
+
+ this.regenGuiElements()
+ this.updateSidebar()
+ })))
+ this.sidebarMainPage.addChild(
+ new ButtonWithArrow().setText("§0Unfriend all").setLocation(0.1, 0.25, 0.8, 0.1)
+ .addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ let commands = []
+
+ for (let f of this.selectedFriends) {
+ this.loadedFriends[f].exists = false
+ commands.push("/friend remove " + f)
+ }
+ this.selectedFriends.clear()
+
+ this.runCommandsArray(commands)
+ this.updateSidebar()
+ })))
+
+
+ } else {
+ if (!this.allFriendsLoaded) {
+ this.sidebarMainPage.addChild(
+ new ButtonWithArrow().setText("§0Load all friends").setLocation(0.1, 0.1, 0.8, 0.15)
+ .addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.loadAllFriends()
+
+ this.updateSidebar()
+ })))
+ } else {
+ this.sidebarMainPage.addChild(
+ new ButtonWithArrow().setText("§0Select all").setLocation(0.1, 0.1, 0.8, 0.15)
+ .addEvent(new SoopyMouseClickEvent().setHandler(() => {
+
+ Object.keys(this.loadedFriends).forEach((ign, i) => {
+ if (this.loadedFriends[ign].exists) {
+ this.selectedFriends.add(ign)
+ }
+ })
+
+ this.regenGuiElements()
+ this.updateSidebar()
+ })))
+ }
+ }
+
+ this.sidebar.addChild(this.sidebarMainPage)
+ }
+
+ runCommand(command, callback) {
+ this.commandQueue.push([command, callback])
+ }
+
+ runComm() {
+ if (this.commandQueue.length > 0 && this.isOpen()) {
+ let [c, callback] = this.commandQueue.shift()
+ ChatLib.say(c)
+ logger.logMessage("running command " + c, 3)
+ callback()
+ }
+ }
+
+ friendListMessageEvent(pagenum, maxpages, friendslist, event) {
+ if (Date.now() - this.lastLoadedPage < 3000 || this.isOpen()) {
+ cancel(event)
+ // console.log("Canceling")
+ } else {
+ return
+ }
+
+ this.maxLoadedPage = parseInt(pagenum)
+
+ this.pageCount = parseInt(maxpages)
+
+ this.allFriendsLoaded = this.maxLoadedPage === this.pageCount
+
+ friendslist = friendslist.replace(/\-\>newLine\<\-/g, "\n").split("\n")
+ friendslist.shift()
+
+ friendslist.pop()
+
+ friendslist.forEach((line, i) => {
+ let [name, location] = line.split(" is ")
+
+ if (location) {
+ if (this.loadedFriends[ChatLib.removeFormatting(name)]) {
+ this.loadedFriends[ChatLib.removeFormatting(name)].nameWithFormatting = name
+ this.loadedFriends[ChatLib.removeFormatting(name)].exists = true
+ this.loadedFriends[ChatLib.removeFormatting(name)].currentGame = location.replace("in ", "").replace("currently offline", "&cCurrently offline")
+ this.loadedFriends[ChatLib.removeFormatting(name)].element.setText(name).setDesc("&7" + location.replace("in ", "").replace("currently offline", "&cCurrently offline"))
+ } else {
+ this.loadedFriends[ChatLib.removeFormatting(name)] = {
+ nameWithFormatting: name,
+ exists: true,
+ currentGame: location.replace("in ", "").replace("currently offline", "&cCurrently offline"),
+ element: new ButtonWithArrowAndDescription().setLocation(0, this.heightPerFriend * Object.keys(this.loadedFriends).length + 1, 1, this.heightPerFriend * 0.875).setText(name).setDesc("&7" + location.replace("in ", "").replace("currently offline", "&cCurrently offline")).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedOn(ChatLib.removeFormatting(name))
+ }))
+ }
+ this.loadedFriends[ChatLib.removeFormatting(name)].element.location.location.y.set(this.heightPerFriend * Object.keys(this.loadedFriends).length - this.heightPerFriend, 500 + 100 * i)
+ }
+ }
+ });
+
+ this.regenGuiElements()
+ }
+
+ clickedOn(name) {
+ if (this.selectedFriends.has(name)) {
+ this.selectedFriends.delete(name)
+ } else {
+ this.selectedFriends.add(name)
+ }
+ // this.commandQueue.push("/friend remove " + ChatLib.removeFormatting(name))
+
+ // this.loadedFriends[ChatLib.removeFormatting(name)].exists = false
+
+ this.regenGuiElements()
+ this.updateSidebar()
+ }
+
+ regenGuiElements() {
+ if (Date.now() - this.lastRender < 1000) {
+ this.friendsArea.children = []
+
+ let scroll = this.friendsArea.location.scroll.y.get() / this.friendsArea.location.getHeightExact()
+
+ let minY = -scroll - this.heightPerFriend * 0.875 - 0.5
+
+ let maxY = -scroll + 1.5
+
+ Object.keys(this.loadedFriends).forEach((ign, i) => {
+ /**@type {ButtonWithArrowAndDescription} */
+ let e = this.loadedFriends[ign].element
+
+ if (this.loadedFriends[ign].exists && e.location.location.y.get() > minY && e.location.location.y.get() < maxY) {
+ this.friendsArea.addChild(e)
+
+ if (this.selectedFriends.has(ign)) {
+ e.setColor(100, 255, 100)
+ } else {
+ e.setColor(253, 255, 227)
+ }
+ }
+ })
+
+ this.loadingElement.location.location.y.set(this.heightPerFriend * Object.keys(this.loadedFriends).length, 500)
+ this.friendsArea.addChild(this.loadingElement)
+
+ if (this.maxLoadedPage !== this.pageCount) {
+ this.loadingElement.visable = true
+ } else {
+ this.loadingElement.visable = false
+ }
+ }
+ }
+
+ loadFriendPage(page, callback) {
+ if (this.loadedPages.has(page)) return
+
+ this.loadedPages.add(page)
+ this.runCommand("/friends list " + page, () => {
+ this.lastLoadedPage = Date.now()
+ if (callback) callback()
+ })
+ }
+
+ onOpen() {
+ this.loadedFriends = {}
+ this.pageCount = undefined
+ this.lastLoadedPage = 0
+ this.maxLoadedPage = 0
+
+ this.sidebarCustomPage = undefined
+ this.commandQueue = []
+
+ this.loadedPages.clear()
+
+ this.regenGuiElements()
+
+ this.loadFriendPage(1)
+ this.openSidebarPage(this.sidebar)
+ this.updateSidebar()
+ }
+}
+
+module.exports = {
+ class: new FriendsGui()
+} \ No newline at end of file
diff --git a/src/features/friendsGUI/metadata.json b/src/features/friendsGUI/metadata.json
new file mode 100644
index 0000000..b7b1f6f
--- /dev/null
+++ b/src/features/friendsGUI/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Friends gui",
+ "description": "Gui for friends",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/globalSettings/firstLoadPage.js b/src/features/globalSettings/firstLoadPage.js
new file mode 100644
index 0000000..62805f5
--- /dev/null
+++ b/src/features/globalSettings/firstLoadPage.js
@@ -0,0 +1,42 @@
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent"
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow"
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement"
+
+class FirstLoadPage extends SoopyGuiElement {
+ constructor() {
+ super()
+
+ this.setLocation(0, 0, 1, 1)
+
+ this.guiPage = undefined
+ }
+
+ setLoc(addBack, addNext) {
+ if (addBack) {
+ let backButton = new ButtonWithArrow().setLocation(0.05, 0.85, 0.2, 0.1).setDirectionRight(false).setText("§0Back")
+
+ backButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.guiPage.prevPage()
+ this.guiPage.closeSidebarPage()
+ }))
+
+ this.addChild(backButton)
+ }
+ if (addNext) {
+ let nextButton = new ButtonWithArrow().setLocation(0.75, 0.85, 0.2, 0.1).setText("§0Next")
+
+ nextButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.guiPage.nextPage()
+ this.guiPage.closeSidebarPage()
+ }))
+
+ this.addChild(nextButton)
+ }
+ }
+
+ load() {
+
+ }
+}
+
+export default FirstLoadPage \ No newline at end of file
diff --git a/src/features/globalSettings/firstLoadPages.js b/src/features/globalSettings/firstLoadPages.js
new file mode 100644
index 0000000..2975a69
--- /dev/null
+++ b/src/features/globalSettings/firstLoadPages.js
@@ -0,0 +1,135 @@
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import Notification from "../../../guimanager/Notification";
+import FeatureManager from "../../featureClass/featureManager";
+import FirstLoadPage from "./firstLoadPage";
+
+class WelcomePage extends FirstLoadPage {
+ constructor() {
+ super()
+
+ this.addChild(new SoopyTextElement().setText("§0Welcome to SoopyV2!").setLocation(0.1, 0.05, 0.8, 0.1).setMaxTextScale(10));
+
+ this.addChild(new SoopyTextElement().setText("§7This menu will guide you through important settings").setLocation(0.1, 0.15, 0.8, 0.075).setMaxTextScale(10));
+ // this.addChild(new SoopyTextElement().setText("§7First lets get privacy settings out of the way.").setLocation(0.1, 0.15,0.8,0.075).setMaxTextScale(10));
+
+ this.settingsArea = new SoopyGuiElement().setLocation(0.1, 0.25, 0.8, 0.75);
+ this.settingsArea.setScrollable(true)
+
+ this.addChild(this.settingsArea);
+ }
+
+ load() {
+ let y = 0
+
+ this.guiPage.mainThing.firstPageSettings.forEach(setting => {
+ setting = setting.getGuiObject()
+
+ setting.location.location.y.set(y, 0)
+
+ this.settingsArea.addChild(setting);
+
+ y += 0.045 + setting.location.size.y.get()
+ })
+ }
+}
+class ApiKeyPage extends FirstLoadPage {
+ constructor() {
+ super()
+
+ this.addChild(new SoopyTextElement().setText("§0First lets setup your api key!").setLocation(0.1, 0.05, 0.8, 0.1).setMaxTextScale(10));
+
+ this.addChild(new SoopyTextElement().setText("§7You can skip this but some features may not work").setLocation(0.1, 0.15, 0.8, 0.075).setMaxTextScale(10));
+
+ this.settingsArea = new SoopyGuiElement().setLocation(0.1, 0.25, 0.8, 0.75);
+ this.settingsArea.setScrollable(true)
+
+ this.addChild(this.settingsArea);
+ }
+
+ load() {
+ let y = 0
+
+ let settings = [this.guiPage.mainThing.apiKeySetting, this.guiPage.mainThing.verifyApiKey, this.guiPage.mainThing.findApiKey, this.guiPage.mainThing.newApiKey]
+ settings.forEach(setting => {
+ setting = setting.getGuiObject()
+
+ setting.location.location.y.set(y, 0)
+
+ this.settingsArea.addChild(setting);
+
+ y += 0.045 + setting.location.size.y.get()
+ })
+ }
+}
+
+class HowToOpenMenuPage extends FirstLoadPage {
+ constructor() {
+ super()
+
+ this.addChild(new SoopyTextElement().setText("§0Your all set!").setLocation(0.1, 0.1, 0.8, 0.3).setMaxTextScale(10));
+
+ this.addChild(new SoopyTextElement().setText("§7To change any settings, or to access this menu again run §2/soopy§7.").setLocation(0.1, 0.3, 0.8, 0.1).setMaxTextScale(10));
+
+ let openSettingsButton = new ButtonWithArrow().setText("§0Open settings").setLocation(0.1, 0.5, 0.3, 0.2).setDirectionRight(false)
+
+ openSettingsButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ ChatLib.command("soopyv2", true)
+ }))
+
+ this.addChild(openSettingsButton);
+
+ let closeButton = new ButtonWithArrow().setText("§0Close").setLocation(0.6, 0.5, 0.3, 0.2)
+
+ closeButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ Client.currentGui.close()
+ }))
+
+ this.addChild(closeButton);
+
+ }
+}
+
+class DisableFeatures extends FirstLoadPage {
+ constructor() {
+ super()
+
+ this.addChild(new SoopyTextElement().setText("§0Lastly do you want to disable all features?").setLocation(0.1, 0.1, 0.8, 0.3).setMaxTextScale(10));
+
+ this.addChild(new SoopyTextElement().setText("§7(So you can only enable the ones you want)").setLocation(0.1, 0.3, 0.8, 0.1).setMaxTextScale(10));
+
+ let openSettingsButton = new ButtonWithArrow().setText("§0Disable all features").setLocation(0.35, 0.5, 0.3, 0.2)
+
+ openSettingsButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+
+ new Thread(() => {
+ new Notification("Disabling features...", [])
+ Object.keys(FeatureManager.featureMetas).forEach((f) => {
+ let meta = FeatureManager.featureMetas[f]
+
+ let isHidden = meta.isHidden
+ if (typeof isHidden === "string") {
+ return
+ }
+ if (isHidden) return
+ if (!meta.isTogglable) return
+
+ FeatureManager.featureSettingsData[f].enabled = false
+ FeatureManager.featureSettingsDataLastUpdated = true
+
+ if (FeatureManager.isFeatureLoaded(f)) {
+ FeatureManager.unloadFeature(f)
+ }
+ })
+ new Notification("Disabled all features!", [])
+ }).start()
+ }))
+
+ this.addChild(openSettingsButton);
+
+ }
+}
+
+export default [new WelcomePage(), new ApiKeyPage(), new DisableFeatures(), new HowToOpenMenuPage()] \ No newline at end of file
diff --git a/src/features/globalSettings/index.js b/src/features/globalSettings/index.js
new file mode 100644
index 0000000..b135337
--- /dev/null
+++ b/src/features/globalSettings/index.js
@@ -0,0 +1,1121 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import ButtonSetting from "../settings/settingThings/button";
+import TextSetting from "../settings/settingThings/textSetting";
+import ToggleSetting from "../settings/settingThings/toggle";
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import firstLoadPages from "./firstLoadPages";
+import GuiPage from "../soopyGui/GuiPage"
+import Notification from "../../../guimanager/Notification";
+import logger from "../../logger";
+import { numberWithCommas } from "../../utils/numberUtils";
+import { firstLetterCapital } from "../../utils/stringUtils";
+import { fetch } from "../../utils/networkUtils";
+import socketConnection from "../../socketConnection";
+import renderLibs from "../../../guimanager/renderLibs";
+import { f, m } from "../../../mappings/mappings";
+import { addLore, getSBUUID, toMessageWithLinks } from "../../utils/utils";
+import { delay } from "../../utils/delayUtils";
+import { SoopyGui } from "../../../guimanager";
+import SoopyImageElement from "../../../guimanager/GuiElement/SoopyImageElement";
+const Files = Java.type("java.nio.file.Files")
+const Paths = Java.type("java.nio.file.Paths")
+const JavaString = Java.type("java.lang.String")
+const JavaLong = Java.type("java.lang.Long")
+
+let hecatombLevels = [2, 5, 10, 20, 30, 40, 60, 80, 100]
+let championLevels = [50000, 100000, 250000, 500000, 1000000, 1500000, 2000000, 2500000, 3000000]
+
+class GlobalSettings extends Feature {
+ constructor() {
+ super()
+
+ this.initVariables()
+ }
+
+ initVariables() {
+ this.apiKeySetting = undefined
+ this.GuiPage = undefined
+ }
+
+ onEnable() {
+ this.apiKeySetting = new TextSetting("Api Key", "Your hypixel api key", "", "api_key", this, "Run /api new to load", true)
+ this.verifyApiKey = new ButtonSetting("Verify api key", "Click this to make sure the api key is working", "verify_key", this, "Click!", this.verifyKey2, undefined)
+ this.newApiKey = new ButtonSetting("Run /api new", "This is here so u dont need to exit and re-enter", "api_new_command", this, "Click!", this.apiNewCommand, undefined)
+ this.findApiKey = new ButtonSetting("Attempt to load api key from other mods", "This will scan other mods configs to attempt to find your key", "find_key", this, "Click!", () => { this.findKey() }, undefined)
+
+ this.alertAllUpdates = new ToggleSetting("Send chat update avalible for all updates", "If disabled itll notify for new updates less", false, "alert_all_updates", this)
+
+ this.fixNeuNetworth = new ToggleSetting("Change networth in NEU pv to soopynw", "This should make it a lot more accurate", true, "neu_nw_override", this)
+ this.darkTheme = new ToggleSetting("Dark theme", "This might be scuffed because guis are still made in light theme", false, "dark_theme", this)
+ // this.linkPreview = new ToggleSetting("Link preview", "Shows a preview of where a link will take you", true, "link_preview", this)
+
+ // this.reportErrorsSetting = new ToggleSetting("Send module errors to soopy server", "This will allow me to more effectivly fix them", false, "privacy_send_errors", this)
+
+ this.hideFallingBlocks = new ToggleSetting("Hide falling blocks", "NOTE: This setting is a bit laggy", false, "hide_falling_sand", this)
+ this.twitchCommands = new ToggleSetting("Ingame twitch bot commands", "Allows u to use twitch bot commands ingame (eg -sa)", true, "twitch_commands_ingame", this)
+ this.handChat = new ToggleSetting("Replace [hand] with ur currently held item", "Idea from Synthesis im only adding cus i dont have D:", true, "hand_chat", this)
+ this.itemWorth = new ToggleSetting("(Approximate) Item worth in lore", "Accounts for stuff like enchants/recombs ect", false, "item_worth", this)
+ this.showHecatomb = new ToggleSetting("Show hecatomb enchant info in lore", "", true, "show_hecatomb", this)
+ this.showChampion = new ToggleSetting("Show champion enchant info in lore", "", true, "show_champion", this)
+
+ this.thunderBottle = new ToggleSetting("Thunder Bottle Progress Display", "shows you the progress of thunder bottle in your inventory", false, "thunder_bottle", this);
+ this.thunderBottleElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.thunderBottle)
+ .setLocationSetting(new LocationSetting("Thunder Bottle Progress location", "Allows you to change location of this display", "thunder_bottle_location", this, [20, 100, 1, 1]).requires(this.thunderBottle).editTempText(`&6Thunder Charge&7> &f49,999&7/&750,000`));
+ this.hudElements.push(this.thunderBottleElement);
+ this.thunderBottleFull = new ToggleSetting("Thunder Bottle Full Alert", "Alert when your thunder bottle is fully charged", false, "thunder_bottle_full", this);
+
+ this.oldMasterStars = new ToggleSetting("Use Old Master Stars", "replaces the ugly new master star on item name with the old fashion one", false, "old_master_star", this)
+ this.sbaItemPickUpLog = new ToggleSetting("Sba Item Pick Up Log", "Same as sba item pick up log, but fixes old master stars making it go brrr", false, "sba_item_log", this);
+ this.sbaItemPickUpLogElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.sbaItemPickUpLog)
+ .setLocationSetting(new LocationSetting("Sba Item Pick Up Log location", "Allows you to change location of this display", "sba_item_log_location", this, [10, 100, 1, 1]).requires(this.sbaItemPickUpLog).editTempText(`&a+ 1x &dHeroic Hyperion &c✪✪✪✪✪\n&c- 1x &dHeroic Hyperion &6✪✪✪✪✪`));
+ this.hudElements.push(this.sbaItemPickUpLogElement);
+ this.maxAmount = new TextSetting("Pickup Log Line Limit", "Basically after this length it doesn't log anymore", "20", "item_log_max_amount", this, "20", false).requires(this.sbaItemPickUpLog);
+
+ this.fancyVanquisherAlert = new ToggleSetting("Fancy Vanquisher Alert", "Alert when a Vanquisher spawned", false, "fancy_vanquisher_alert", this);
+ this.fancySeaCreaturesAlert = new ToggleSetting("Fancy Sea Creatures Alert", "Alert when you caught x creature (this is the main toggle)", false, "fancy_sc_alert", this);
+ this.fancySeaCreaturesAlertThunder = new ToggleSetting("Fancy Thunder Alert", "Alert when you caught Thunder creature", false, "fancy_thunder_alert", this).requires(this.fancySeaCreaturesAlert);
+ this.fancySeaCreaturesAlertJawbus = new ToggleSetting("Fancy Jawbus Alert", "Alert when you caught Lord Jawbus creature", false, "fancy_jawbus_alert", this).requires(this.fancySeaCreaturesAlert);
+
+ this.registerEvent('itemTooltip', (lore, i, e) => {
+ if (!this.oldMasterStars.getValue()) return
+ if (!i) return
+ let itemName = i.getName()
+ let itemNameReformat = itemName.removeFormatting()
+ if (itemNameReformat.endsWith("➊")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➊", "§c✪§6✪✪✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➋")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➋", "§c✪✪§6✪✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➌")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➌", "§c✪✪✪§6✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➍")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➍", "§c✪✪✪✪§6✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➎")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➎", "§c✪✪✪✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ })
+
+ this.registerEvent('worldLoad', this.worldLoad)
+
+ this.itemData = {};
+ this.oldItemData = {};
+ this.initOldItemData();
+ this.todoPickUpLog = {};
+ this.clearLog = false;
+
+ this.registerStep(true, 5, this.step5Fps)
+ //4 chat registeries below prevents pickup log to go brrr when warping
+ this.registerChat("&r&c ☠ ${info} and became a ghost&r&7.&r", (info, e) => {
+ if (info.includes("You")) this.preventGoingBrrr();
+ });
+ this.registerChat("${info}You were revived by ${info2}", this.preventGoingBrrr);
+
+ this.registerChat("&r&e> Your bottle of thunder has fully charged!&r", () => {
+ if (this.thunderBottleFull.getValue()) {
+ Client.showTitle("&6Bottle of Thunder Fully Charged", "", 0, 100, 10);
+ }
+ })
+
+ this.warps = JSON.parse(FileLib.read("SoopyV2", "features/globalSettings/warps.json"))
+
+ this.registerCommand("warp", (...name) => {
+ //send command to server
+ ChatLib.command("warp " + (name[0] || ""));
+ }, (args) => {
+ return this.warps.filter(v => v.toLowerCase().startsWith(args[0]))
+ })
+
+ this.registerStep(true, 4, this.mobThings)
+
+ this.firstPageSettings = [this.darkTheme]
+
+ this.firstLoadPageData = JSON.parse(FileLib.read("soopyAddonsData", "soopyv2firstloaddata.json") || "{}") || {}
+
+ this.GuiPage = new FirstLoadingPage(this)
+
+ // soopyV2Server.reportErrorsSetting = this.reportErrorsSetting
+ this.registerEvent("itemTooltip", this.itemTooltipEvent).registeredWhen(() => this.itemWorth.getValue() || this.showChampion.getValue() || this.showHecatomb.getValue())
+
+
+ this.registerChat("&aYour new API key is &r&b${key}&r", this.newKey)
+ const EntityFallingBlock = Java.type("net.minecraft.entity.item.EntityFallingBlock");
+
+ this.registerEvent('renderEntity', (entity, posVec, partialTicks, event) => {
+ if (entity.getEntity() instanceof EntityFallingBlock) {
+ cancel(event);
+ }
+ }).registeredWhen(() => this.hideFallingBlocks.getValue())
+
+ this.ranFirstLoadThing = false
+
+ if (!this.firstLoadPageData.shown) {
+ new Thread(() => {
+ while (!World.isLoaded() || !this.FeatureManager.finishedLoading) {
+ Thread.sleep(100)
+ }
+ Thread.sleep(500)
+ this.showFirstLoadPage.call(this)
+ }).start()
+ }
+
+ if (net.minecraftforge.fml.common.Loader.isModLoaded("notenoughupdates")) {
+ this.GuiProfileViewer = Java.type("io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer")
+ this.currentPlayerOpen = undefined
+ this.currentPlayerNetworth = {}
+ this.registerEvent("tick", this.fixNEU)
+ }
+
+ this.requestingPrices = new Set()
+ this.registerStep(false, 1, this.updateItemLores)
+
+ try { //This enables links from soopy.dev to be shown in patcher image preview
+ let hasHost = false
+
+ for (let host of Java.type("gg.essential.util.TrustedHostsUtil").INSTANCE.getTrustedHosts()) {
+ if (host.getName() === "soopy.dev") {
+ hasHost = true
+ }
+ }
+
+ if (!hasHost) {
+ let TrustedHost = Java.type("gg.essential.api.utils.TrustedHostsUtil").TrustedHost
+ let TreeSet = Java.type("java.util.TreeSet")
+ let hosts = new TreeSet()
+ hosts.add("soopy.dev")
+
+ let host = new TrustedHost(124123, "soopy.dev", hosts)
+
+ Java.type("gg.essential.util.TrustedHostsUtil").INSTANCE.addTrustedHost(host)
+ }
+ } catch (e) { }
+
+ this.registerCommand("soopyweight", (user = Player.getName()) => {
+ this.soopyWeight(user)
+ })
+ this.registerCommand("sweight", (user = Player.getName()) => {
+ this.soopyWeight(user)
+ })
+ this.registerCommand("lobbyday", () => {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Current lobby is day " + (World.getTime() / 20 / 60 / 20).toFixed(2))
+ })
+ this.registerCommand("tps", (time = "3") => {
+ time = parseInt(time)
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Loading tps... this will take " + time + "s")
+
+ let packetMoves = 0
+ let ticks = 0
+ let packetReceived = register("packetReceived", () => {
+ packetMoves++
+ })
+
+ let tick = register("tick", () => {
+ if (packetMoves > 0) ticks++
+ packetMoves = 0
+ })
+
+ delay(time * 1000, () => {
+ packetReceived.unregister()
+ tick.unregister()
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Tps: " + Math.min(20, ticks / time).toFixed(1))
+ })
+ })
+
+ this.lastCookies = 0
+
+ this.registerEvent("postGuiRender", () => {
+ if (Player.getContainer() && Player.getContainer().getName() === "Cookie Clicker v0.01" && Player.getContainer().getStackInSlot(13)) this.renderCookie()
+ // if (this.linkPreview.getValue() && Client.currentGui && Client.currentGui.get() && Client.currentGui.get().toString().startsWith("net.minecraft.client.gui.GuiConfirmOpenLink")) this.renderWebpage()
+ })
+ this.registerStep(false, 1, () => {
+ if (Player.getContainer() && Player.getContainer().getName() === "Cookie Clicker v0.01" && Player.getContainer().getStackInSlot(13)) this.tickCookie()
+
+ global.guiManagerSoopyGuisSetDarkThemeEnabled(this.darkTheme.getValue())
+ })
+
+ this.registerEvent("guiMouseClick", this.guiClicked)
+
+ this.partyChatEnabled = true
+
+ this.registerChat("&r&9Party &8> ${*}", (e) => {
+ if (!this.partyChatEnabled) {
+ cancel(e)
+ }
+ })
+
+ this.registerEvent("messageSent", (message, event) => {
+ if (this.twitchCommands.getValue() && message.startsWith("-") && message[1].toLowerCase().match(/[a-z]/)) {
+ cancel(event)
+ ChatLib.addToSentMessageHistory(message)
+ fetch("http://soopy.dev/api/soopyv2/botcommand?m=" + encodeURIComponent(message.replace("-", "")) + "&u=" + Player.getName()).text().then(text => {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "&7" + message)
+ toMessageWithLinks(this.FeatureManager.messagePrefix + text, "7").chat()
+ })
+ return;
+ }
+
+ if (this.handChat.getValue() && message.toLowerCase().includes("[hand]")) {
+ cancel(event)
+ ChatLib.addToSentMessageHistory(message)
+
+ fetch("http://soopy.dev/api/soopyv2/itemup", {
+ postData: {
+ name: Player.getHeldItem().getName(),
+ lore: Player.getHeldItem().getLore().join("\n")
+ }
+ }).text().then(text => {
+ if (text.length > 20) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "There was an error uploading the item data!")
+ return
+ }
+
+ ChatLib.say(message.replace(/\[hand\]/gi, "[ITEM:" + text + "]"))
+ })
+ return;
+ }
+ })
+
+ let ev = this.registerChat("${*}[ITEM:${*}", (event) => {
+ cancel(event)
+ let message = new Message(event)
+
+ let [_] = message.getUnformattedText().match(/\[ITEM:([0-9]+)\]/g)
+ let id = _.replace("[ITEM:", "").replace(/\]$/g, "")
+
+ fetch("http://soopy.dev/api/soopyv2/itemdown/" + id).json().then(([name, lore]) => {
+ for (let i = 0; i < message.getMessageParts().length; i++) {
+ let component = message.getMessageParts()[i]
+
+ if (component.getText().match(/\[ITEM:([0-9]+)\]/g)) {
+ let [_] = component.getText().match(/\[ITEM:([0-9]+)\]/g)
+ let id = _.replace("[ITEM:", "").replace(/\]$/g, "")
+
+ message.setTextComponent(i, new TextComponent(component.getText().replace("[ITEM:" + id + "]", name + "&r")).setHover("show_text", lore))
+ }
+ }
+ message.setRecursive(true)
+ message.chat()
+ }).catch(() => {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "There was an error downloading the item data!")
+ message.chat()
+ })
+
+ })
+ ev.trigger.setPriority(Priority.HIGHEST)
+
+ this.ahAlerts = [
+ // { //TODO: add a command/gui to add these
+ // id: "ATTRIBUTE_SHARD",
+ // maxPrice: 1300000,
+ // nbt: [
+ // "tag.ExtraAttributes.attributes.mana_pool"
+ // ]
+ // }
+ ]
+
+ this.registerStep(false, 60, async () => {
+ if (this.ahAlerts.length === 0) return
+ let data = await fetch("https://moulberry.codes/auction.json").json()//TODO: use https://moulberry.codes/auction.json.gz
+ if (!data.success) return
+
+ data.new_auctions.forEach(a => {
+ let itemData = decompress(a.item_bytes)
+ let itemJSON = itemData.toObject().i[0]
+ let itemId = itemJSON.tag.ExtraAttributes.id
+
+ if (a.bin && this.ahAlerts.some(al => {
+ let ret = al.id === itemId && a.starting_bid <= al.maxPrice
+
+ if (ret && al.nbt) {
+ if (al.nbt.some(nbtr => {
+ let steps = nbtr.split(".")
+ let o = itemJSON
+ steps.forEach(s => {
+ o = o?.[s]
+ })
+
+ return !o
+ })) ret = false
+ }
+
+ return ret
+ })) {
+ Client.showTitle("SNIPE THING", "CHECK CHAT", 20, 60, 20)
+
+ new TextComponent(this.FeatureManager.messagePrefix + "Bin found " + numberWithCommas(a.starting_bid) + " " + a.item_name).setClick("run_command", "/viewauction " + a.uuid).chat()
+ }
+ })
+ })
+
+ this.registerCommand("price", async () => {
+ let json = await fetch("http://soopy.dev/api/soopyv2/itemPriceDetailed", {
+ postData: {
+ item: Player.getHeldItem().getNBT().toObject()
+ }
+ }).json()
+
+ ChatLib.chat(this.FeatureManager.messagePrefix + "PRICE ANALYSIS (Total: $" + numberWithCommas(Math.round(json.price)) + ")")
+ // json.details.sort((a, b) => {
+ // if (typeof (a) === "string") return 1
+ // if (typeof (b) === "string") return -1
+ // return a[1] - b[1]
+ // }).forEach(d => {
+ // if (typeof (d) === "string") {
+ // ChatLib.chat(d)
+ // } else {
+ // ChatLib.chat("&f" + d[0] + "&7: &6" + numberWithCommas(Math.round(d[1])))
+ // }
+ // })
+ // ChatLib.chat(this.FeatureManager.messagePrefix + "Final price: " + numberWithCommas(Math.round(json.price)))
+ json.details.sort((b, a) => {
+ if (typeof (a) === "string") return 1
+ if (typeof (b) === "string") return -1
+ return a.price - b.price
+ }).forEach(d => {
+ if (typeof (d) === "string") {
+ ChatLib.chat(d)
+ } else {
+ if (!d.price) return
+ let lore = []
+ d.items.sort((b, a) => {
+ if (typeof (a) === "string") return 1
+ if (typeof (b) === "string") return -1
+ return a[1] - b[1]
+ }).forEach(d2 => {
+ if (typeof (d2) === "string") {
+ lore.push(d2)
+ } else {
+ lore.push("&f" + d2[0] + "&7: $&6" + numberWithCommas(Math.round(d2[1])))
+ }
+ })
+ new TextComponent("&d" + d.name + "&7: $&6" + numberWithCommas(Math.round(d.price))).setHover("show_text", lore.join("\n")).chat()
+ }
+ })
+ })
+
+ let zoogui = new SoopyGui().setOpenCommand("zoo")
+
+ zoogui.element.addChild(new SoopyImageElement().setImage("https://img.freepik.com/premium-photo/portrait-monkey-wild_397170-44.jpg?w=1380").setLocation(0, 0, 1, 1))
+ }
+
+ worldLoad() {
+ this.requestingPrices.clear();
+ this.initOldItemData();
+ this.preventGoingBrrr();
+ if (!this.oldMasterStars.getValue()) return
+ let j = 0;
+ [...Player.getInventory().getItems()].forEach(i => {
+ j++;
+ if (j > 8) return //only do the 1-8 hot bar slots
+ if (!i) return
+ let itemName = i.getName()
+ let itemNameReformat = itemName.removeFormatting()
+ if (itemNameReformat.endsWith("➊")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➊", "§c✪§6✪✪✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➋")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➋", "§c✪✪§6✪✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➌")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➌", "§c✪✪✪§6✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➍")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➍", "§c✪✪✪✪§6✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ if (itemNameReformat.endsWith("➎")) {
+ let newItemName = itemName.replace("§6✪§6✪§6✪§6✪§6✪§c➎", "§c✪✪✪✪✪")
+ i.setName(newItemName)
+ this.saveItemData(getSBUUID(i), newItemName)
+ return
+ }
+ })
+ }
+
+ step5Fps() {
+ let old = this.oldMasterStars.getValue();
+ let pick = this.sbaItemPickUpLog.getValue();
+ let thunder = this.thunderBottle.getValue();
+ let max = this.maxAmount.getValue();
+ if (max) {
+ max = Number(max)
+ if (max < 0 || isNaN(max)) this.maxAmount.setValue("0");
+ }
+ let inGui = Client.isInGui()
+ if (!old && !pick && !thunder) return
+ if (inGui) {
+ this.todoPickUpLog = {};
+ }
+ let j = 0;
+ let now = Date.now();
+ let thunderText = [];
+ [...Player.getInventory().getItems()].forEach(i => {
+ j++;
+ if (i) {
+ let uuid = getSBUUID(i)
+ let ItemName = i.getName()
+ if (old) {
+ if (uuid && this.itemData.hasOwnProperty(uuid)) {
+ let newName = this.itemData[uuid]
+ if (ItemName != newName) {
+ i.setName(newName)
+ }
+ }
+ }
+ if (thunder) {
+ if (ItemName.removeFormatting().includes("Empty Thunder Bottle")) {
+ let charges = i?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getDouble("thunder_charge")
+ thunderText.push(`&6Thunder Charge&7> &f${numberWithCommas(charges)}&7/&750,000`)
+ }
+ }
+ }
+ if (pick && !inGui) {
+ let oldItem = this.oldItemData[j]
+ let newItem = i
+ if (!oldItem && !newItem) return //they both are air
+ if (j > 36 || j == 9) return //sbmenu and armors (when switching wardrobe it goes brrr w/o this)
+ let oldItemAmount = oldItem ? oldItem.getNBT().getDouble("Count") : undefined
+ let oldItemName = oldItem ? oldItem.getName().replace(/ §8x\d+$/, "") : ""
+ let newItemAmount = newItem ? newItem.getNBT().getDouble("Count") : undefined
+ let newItemName = newItem ? newItem.getName().replace(/ §8x\d+$/, "") : ""
+ this.oldItemData[j] = newItem
+ if (oldItemName === newItemName) { //only amount is changed
+ if (oldItemAmount === newItemAmount || !newItemAmount || !oldItemAmount) return
+ this.addToTodoPickUpLog(now, oldItemName, newItemAmount - oldItemAmount)
+ return //so it doesn't provide duplicate message
+ }
+ let olduuid = getSBUUID(oldItem)
+ let newuuid = getSBUUID(newItem)
+ if (oldItemAmount == 1 && olduuid == newuuid) return // fixes using old master star making sba go brrr
+ if (oldItemName) { //thing removed from that inventory slot
+ if (!oldItemAmount) return
+ this.addToTodoPickUpLog(now, oldItemName, (-1) * oldItemAmount)
+ }
+ if (newItemName) { //thing being placed into that inventory slot
+ if (!newItemAmount) return
+ this.addToTodoPickUpLog(now, newItemName, newItemAmount)
+ }
+ }
+ })
+ if (thunder) {
+ if (thunderText.length > 0) {
+ this.thunderBottleElement.setText(thunderText.join("\n"))
+ } else {
+ this.thunderBottleElement.setText("")
+ }
+ }
+ let todoText = [];
+ if (pick) {
+ Object.keys(this.todoPickUpLog).forEach((i) => {
+ if (Math.abs(this.todoPickUpLog[i].timeStamp - now) > 5000 || !this.todoPickUpLog[i].Amount || this.todoPickUpLog[i].Amount == 0) {
+ delete this.todoPickUpLog[i]
+ return
+ }
+ //positive and negative prefix colors
+ if (todoText.length < max) todoText.push((this.todoPickUpLog[i].Amount > 0 ? "&r&a+ " : "&r&c- ") + Math.abs(this.todoPickUpLog[i].Amount) + "x &f" + i)
+ })
+ } else {
+ this.todoPickUpLog = {};
+ }
+ // doesn't need to put setText() in if (pick) cuz if (!pick) it clears the todo log list
+ this.sbaItemPickUpLogElement.setText((inGui || this.clearLog) ? "" : (todoText.join("\n")))
+ }
+
+ mobThings() {
+ if (!this.fancyVanquisherAlert.getValue()) return
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach(entity => {
+ let name = entity.getName()
+ if (name.includes("'s")) return
+ let LowerName = name.removeFormatting().toLowerCase()
+ let existedTicks = entity.getTicksExisted()
+ if (this.fancyVanquisherAlert.getValue()) {
+ if (LowerName.includes("vanq")) {
+ if (existedTicks <= 20) {
+ Client.showTitle("&r&5&l[&b&l&kO&5&l] VANQUISHER &5[&b&l&kO&5&l]", "", 0, 50, 10);
+ }
+ }
+ }
+ if (this.fancySeaCreaturesAlertThunder.getValue()) {
+ if (LowerName.includes("thunder")) {
+ if (existedTicks <= 20) {
+ Client.showTitle("&r&6&l[&b&l&kO&6&l] THUNDER [&b&l&kO&6&l]", "", 0, 50, 10)
+ }
+ }
+ }
+ if (this.fancySeaCreaturesAlertJawbus.getValue()) {
+ if (LowerName.includes("jawbus")) {
+ if (existedTicks <= 20) {
+ Client.showTitle("&r&6&l[&b&l&kO&6&l] LORD JAWBUS [&b&l&kO&6&l]", "", 0, 50, 10)
+ }
+ }
+ }
+ })
+ }
+
+ preventGoingBrrr() {
+ this.clearLog = true
+ delay(8000, () => {
+ if (this.clearLog) {
+ this.clearLog = false
+ }
+ })
+ }
+
+ itemTooltipEvent(lore, i, event) {
+ let uuid = getSBUUID(i)
+ if (!uuid) return
+
+ if (this.itemWorth.getValue()) {
+ let a = socketConnection.itemPricesCache.get(uuid)
+
+ if (!a && socketConnection.itemPricesCache2.get(uuid)) {
+ a = socketConnection.itemPricesCache2.get(uuid)
+ socketConnection.itemPricesCache.set(uuid, a)
+ }
+
+ if (a) {
+ addLore(i, "§eWorth: ", "§6$" + numberWithCommas(Math.round(a)))
+ }
+ }
+
+ if (this.showChampion.getValue() && i?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getDouble("champion_combat_xp")) {
+ let xp = i?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getDouble("champion_combat_xp")
+
+ let level = 1
+ championLevels.forEach(l => {
+ if (xp >= l) level++
+ })
+ let xpNext = championLevels[level - 1]
+
+ addLore(i, "§eChampion: ", "§d" + level + " §6" + numberWithCommas(Math.round(xp)) + (xpNext ? " §7/ §6" + numberWithCommas(Math.round(xpNext)) : ""))
+ }
+ if (this.showHecatomb.getValue() && i?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getInteger("hecatomb_s_runs")) {
+ let runs = i?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getInteger("hecatomb_s_runs")
+
+ let level = 1
+ hecatombLevels.forEach(l => {
+ if (runs >= l) level++
+ })
+ let xpNext = hecatombLevels[level - 1]
+
+ addLore(i, "§eHecatomb: ", "§d" + level + " §6" + numberWithCommas(Math.round(runs)) + (xpNext ? " §7/ §6" + numberWithCommas(Math.round(xpNext)) : ""))
+ }
+ }
+
+ updateItemLores() {
+ if (!this.itemWorth.getValue()) return;
+
+ if (!Player.getInventory()) return
+
+ let items = [...Player.getInventory().getItems(), ...Player.getContainer().getItems()]
+
+ items.forEach(i => {
+ let uuid = getSBUUID(i)
+ if (!uuid) return
+
+ if (this.itemWorth.getValue()) {
+ let a = socketConnection.itemPricesCache.get(uuid)
+
+ if (!a && socketConnection.itemPricesCache2.get(uuid)) {
+ a = socketConnection.itemPricesCache2.get(uuid)
+ socketConnection.itemPricesCache.set(uuid, a)
+ }
+
+ if (!a) {
+ if (!this.requestingPrices.has(uuid)) {
+ this.requestingPrices.add(uuid)
+
+ let json = i.getNBT().toObject()
+ socketConnection.requestItemPrice(json, uuid)
+ }
+ }
+
+ }
+
+ })
+ }
+
+ addToTodoPickUpLog(timestamp, itemname, amount) {
+ let basicValue = 0
+ if (this.todoPickUpLog[itemname]) {
+ if (this.todoPickUpLog[itemname].Amount != 0) {
+ basicValue = this.todoPickUpLog[itemname].Amount
+ }
+ }
+ this.todoPickUpLog[itemname] = {
+ timeStamp: timestamp,
+ Amount: basicValue + amount
+ }
+ }
+
+ initOldItemData() {
+ let j = 0;
+ [...Player.getInventory().getItems()].forEach(i => {
+ j++;
+ this.oldItemData[j] = i
+ })
+ }
+
+ saveItemData(uuid, newName) {
+ if (!this.itemData[uuid]) {
+ this.itemData[uuid] = newName
+ }
+ }
+
+ // renderWebpage() {
+ // let url = this.getField(Client.currentGui.get(), f.linkText)
+
+ // let image = renderLibs.getImage("https://soopy.dev/api/soopyv2/webpage?webpage=" + url)
+
+ // if (image) {
+ // let scale = Renderer.screen.getHeight() * 0.5 / image.getTextureHeight()
+ // image.draw(Renderer.screen.getWidth() / 2 - image.getTextureWidth() * scale / 2, Renderer.screen.getHeight() / 2, scale * image.getTextureWidth(), scale * image.getTextureHeight())
+ // } else {
+ // Renderer.drawString("Loading website preview...", Renderer.screen.getWidth() / 2 - Renderer.getStringWidth("Loading website preview...") / 2, Renderer.screen.getHeight() * 3 / 4)
+ // }
+ // }
+
+ guiClicked(mouseX, mouseY, button, gui, event) {
+ if (Player.getContainer() && Player.getContainer().getName() === "Cookie Clicker v0.01") {
+
+ let hoveredSlot = gui.getSlotUnderMouse()
+ if (!hoveredSlot) return
+
+ let hoveredSlotId = hoveredSlot[f.slotNumber]
+
+ // logger.logMessage(hoveredSlotId, 4)
+
+ Player.getContainer().click(hoveredSlotId, false, "MIDDLE")
+ cancel(event)
+ }
+ }
+
+ tickCookie() {
+ let cookies = parseInt(ChatLib.removeFormatting(Player.getContainer().getStackInSlot(13).getName().split(" ")[0]))
+
+ let cookiesGained = cookies - this.lastCookies
+ this.lastCookies = cookies
+ if (cookiesGained > 50) cookiesGained = 0
+ if (cookiesGained < 1) cookiesGained = 0
+ if (!cookiesGained) cookiesGained = 0
+
+ socketConnection.cookiesGained(cookiesGained)
+ }
+
+ renderCookie() {
+ let cookies = parseInt(ChatLib.removeFormatting(Player.getContainer().getStackInSlot(13).getName().split(" ")[0]))
+
+ let cookiesGained = cookies - this.lastCookies
+ let cookieCount = socketConnection.cookieCount + ((cookiesGained < 50 && cookiesGained > 0) ? cookiesGained : 0)
+
+ renderLibs.drawString("Lifetime Cookies: " + numberWithCommas(cookieCount), 10, 10, 1)
+
+ if (socketConnection.cookieData) {
+ // this.cookieData = {
+ // cookieLeaderboard: data.cookieLeaderboard,
+ // clickingNow: data.clickingNow
+ // }
+
+ let linesOfText = []
+
+ linesOfText.push("---- CURRENTLY CLICKING ----")
+ for (let data of socketConnection.cookieData.clickingNow) {
+ linesOfText.push(data[0] + ": " + numberWithCommas(data[1]))
+
+ if (linesOfText.length * 10 > Renderer.screen.getHeight() / 2) break
+ }
+ linesOfText.push("------- LEADERBOARD -------")
+ let i = 0
+ for (let data of socketConnection.cookieData.cookieLeaderboard) {
+ i++
+
+ linesOfText.push("#" + i + " " + data[0] + ": " + numberWithCommas(data[1]))
+
+ if (linesOfText.length * 10 > Renderer.screen.getHeight() - 50) break
+ }
+
+ for (let i = 0; i < linesOfText.length; i++) {
+ renderLibs.drawString(linesOfText[i], 10, 20 + 10 * i, 1)
+ }
+ }
+ }
+
+ fixNEU() {
+ if (Client.currentGui && Client.currentGui.get() instanceof (this.GuiProfileViewer) && this.fixNeuNetworth.getValue()) {
+ let guiProfileViewer = Client.currentGui.get()
+ if (!guiProfileViewer.profile || !guiProfileViewer.profile.getHypixelProfile()) return
+ let uuid = guiProfileViewer.profile.getHypixelProfile().get("uuid").getAsString().replace(/-/g, "")
+
+ if (this.currentPlayerOpen != uuid) {
+ this.currentPlayerOpen = uuid
+ this.currentPlayerNetworth = {}
+
+ fetch("http://soopy.dev/api/v2/player_skyblock/" + uuid).json().then(data => {
+ if (!data.success) return
+
+ if (this.currentPlayerOpen === data.data.uuid) {
+ Object.keys(data.data.profiles).forEach(profileId => {
+ if (!data.data.profiles[profileId].members[uuid].soopyNetworth.networth) return
+ this.currentPlayerNetworth[data.data.profiles[profileId].stats.cute_name] = JavaLong.valueOf(data.data.profiles[profileId].members[uuid].soopyNetworth.networth)
+ })
+ }
+ })
+ }
+
+ let map = this.getField(guiProfileViewer.profile, "networth")
+ Object.keys(this.currentPlayerNetworth).forEach(key => {
+ map.put(new JavaString(key), new JavaLong(this.currentPlayerNetworth[key]))
+ })
+ }
+ }
+
+ getField(e, field) {
+ let field2 = e.class.getDeclaredField(field);
+
+ field2.setAccessible(true)
+
+ return field2.get(e)
+ }
+
+ async soopyWeight(user) {
+
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Finding senither weight for " + user)
+
+ let userData = await fetch("http://soopy.dev/api/v2/player/" + user).json()
+ if (!userData.success) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "&cError loading data: " + userData.error.description)
+ return
+ }
+
+ let sbData = await fetch("http://soopy.dev/api/v2/player_skyblock/" + userData.data.uuid).json()
+
+ if (!sbData.success) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "&cError loading data: " + sbData.error.description)
+ return
+ }
+
+ ChatLib.chat("&c" + ChatLib.getChatBreak("-"))
+ ChatLib.chat(userData.data.stats.nameWithPrefix + "'s senither weight (best profile):")
+ ChatLib.chat("&aTotal: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.total)))
+ new Message(new TextComponent("&aSkills: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.skill.total)))
+ .setHover("show_text", Object.keys(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.skill).map(skill => {
+ if (skill === "total") {
+ return null
+ }
+ return "&a" + firstLetterCapital(skill) + ": &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.skill[skill].total)) + " &7(" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.skill[skill].weight)) + " | " + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.skill[skill].overflow)) + ")"
+ }).filter(a => a).join("\n"))).chat()
+ new Message(new TextComponent("&aSlayer: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.slayer.total)))
+ .setHover("show_text", Object.keys(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.slayer).map(slayer => {
+ if (slayer === "total") {
+ return null
+ }
+ return "&a" + firstLetterCapital(slayer) + ": &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.slayer[slayer].total)) + " &7(" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.slayer[slayer].weight)) + " | " + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.slayer[slayer].overflow)) + ")"
+ }).filter(a => a).join("\n"))).chat()
+ new Message(new TextComponent("&aDungeon: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.dungeons.total)))
+ .setHover("show_text", Object.keys(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.dungeons).map(dungeons => {
+ if (dungeons === "total") {
+ return null
+ }
+ return "&a" + firstLetterCapital(dungeons) + ": &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.dungeons[dungeons].total)) + " &7(" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.dungeons[dungeons].weight)) + " | " + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.bestProfileId].members[userData.data.uuid].weight.dungeons[dungeons].overflow)) + ")"
+ }).filter(a => a).join("\n"))).chat()
+ if (sbData.data.stats.bestProfileId !== sbData.data.stats.currentProfileId) {
+ ChatLib.chat(userData.data.stats.nameWithPrefix + "'s senither weight (current profile):")
+ ChatLib.chat("&aTotal: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.total)))
+ new Message(new TextComponent("&aSkills: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.skill.total)))
+ .setHover("show_text", Object.keys(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.skill).map(skill => {
+ if (skill === "total") {
+ return null
+ }
+ return "&a" + firstLetterCapital(skill) + ": &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.skill[skill].total)) + " &7(" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.skill[skill].weight)) + " | " + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.skill[skill].overflow)) + ")"
+ }).filter(a => a).join("\n"))).chat()
+ new Message(new TextComponent("&aSlayer: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.slayer.total)))
+ .setHover("show_text", Object.keys(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.slayer).map(slayer => {
+ if (slayer === "total") {
+ return null
+ }
+ return "&a" + firstLetterCapital(slayer) + ": &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.slayer[slayer].total)) + " &7(" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.slayer[slayer].weight)) + " | " + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.slayer[slayer].overflow)) + ")"
+ }).filter(a => a).join("\n"))).chat()
+ new Message(new TextComponent("&aDungeon: &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.dungeons.total)))
+ .setHover("show_text", Object.keys(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.dungeons).map(dungeons => {
+ if (dungeons === "total") {
+ return null
+ }
+ return "&a" + firstLetterCapital(dungeons) + ": &b" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.dungeons[dungeons].total)) + " &7(" + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.dungeons[dungeons].weight)) + " | " + numberWithCommas(Math.round(sbData.data.profiles[sbData.data.stats.currentProfileId].members[userData.data.uuid].weight.dungeons[dungeons].overflow)) + ")"
+ }).filter(a => a).join("\n"))).chat()
+ }
+ ChatLib.chat("&c" + ChatLib.getChatBreak("-"))
+ }
+
+ showFirstLoadPage() {
+ if (!this.ranFirstLoadThing && World.isLoaded() && !this.firstLoadPageData.shown) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Opening first load page, if you accidentally close it run /soopyv2 and click the button")
+ ChatLib.command("soopyv2 first_load_thing", true)
+ this.ranFirstLoadThing = true
+ this.firstLoadPageData.shown = true
+ this.firstLoadPageData.version = 1
+ FileLib.write("soopyAddonsData", "soopyv2firstloaddata.json", JSON.stringify(this.firstLoadPageData))
+ }
+ }
+ verifyKey(key) {
+ // console.log(key)
+ if (key) {
+ try {
+ var url = "https://api.hypixel.net/key?key=" + key
+ let data = fetch(url).jsonSync()
+
+ // console.log(data)
+
+ if (data.success) {
+ return true
+ } else {
+ return false
+ }
+ } catch (e) {
+ return false
+ }
+ } else {
+ return false
+ }
+ }
+ findKey() {
+ new Notification("Finding key...", [])
+ new Thread(() => {
+
+ // NEU
+ try {
+ let testKey = JSON.parse(new JavaString(Files.readAllBytes(Paths.get("./config/notenoughupdates/configNew.json")))).apiKey.apiKey
+ if (testKey) {
+ if (this.verifyKey(testKey)) {
+ this.apiKeySetting.setValue(testKey)
+ new Notification("§aSuccess!", ["Found api key in NotEnoughUpdates!"])
+ return;
+ } else {
+ logger.logMessage("Found invalid key in NotEnoughUpdates", 3)
+ }
+ }
+ } catch (_) { }
+
+ // SBE
+ try {
+ let testKey = JSON.parse(new JavaString(Files.readAllBytes(Paths.get("./config/SkyblockExtras.cfg")))).values.apiKey
+ if (testKey) {
+ if (this.verifyKey(testKey)) {
+ this.apiKeySetting.setValue(testKey)
+ new Notification("§aSuccess!", ["Found api key in SkyblockExtras!"])
+ return;
+ } else {
+ logger.logMessage("Found invalid key in SkyblockExtras", 3)
+ }
+ }
+ } catch (_) { }
+ // SKYTILS
+ try {
+ let testKey2 = new JavaString(Files.readAllBytes(Paths.get("./config/skytils/config.toml")))
+ let testKey = undefined
+ testKey2.split("\n").forEach(line => {
+ if (line.startsWith(" hypixel_api_key = \"")) {
+ testKey = line.split("\"")[1]
+ }
+ })
+ if (testKey) {
+ if (this.verifyKey(testKey)) {
+ this.apiKeySetting.setValue(testKey)
+ new Notification("§aSuccess!", ["Found api key in Skytils!"])
+ return;
+ } else {
+ logger.logMessage("Found invalid key in Skytils", 3)
+ }
+ }
+ } catch (_) { }
+
+ // SOOPYADDONS DATA
+ try {
+ let testKey = FileLib.read("soopyAddonsData", "apikey.txt")
+ if (testKey) {
+ if (this.verifyKey(testKey)) {
+ this.apiKeySetting.setValue(testKey)
+ new Notification("§aSuccess!", ["Found api key in old soopyaddons version!"])
+ return;
+ } else {
+ logger.logMessage("Found invalid key in soopyaddonsData", 3)
+ }
+ }
+ } catch (_) { }
+
+ // HypixelApiKeyManager
+ try {
+ let testKey = JSON.parse(FileLib.read("HypixelApiKeyManager", "localdata.json")).key
+ if (testKey) {
+ if (this.verifyKey(testKey)) {
+ this.apiKeySetting.setValue(testKey)
+ new Notification("§aSuccess!", ["Found api key in HypixelApiKeyManager!"])
+ return;
+ } else {
+ logger.logMessage("Found invalid key in HypixelApiKeyManager", 3)
+ }
+ }
+ } catch (_) { }
+
+
+ new Notification("§cUnable to find api key", [])
+ }).start()
+ }
+
+ apiNewCommand() {
+ ChatLib.command("api new")
+ }
+
+ verifyKey2(key) {
+ if (key) {
+ try {
+ var url = "https://api.hypixel.net/key?key=" + key
+ let data = fetch(url).jsonSync()
+
+ if (data.success) {
+ return true
+ } else {
+ return false
+ }
+ } catch (e) {
+ return false
+ }
+ }
+ if (this.module.apiKeySetting.getValue() == "") {
+ new Notification("§cError!", ["You need to set an api key first!"])
+ return
+ }
+
+
+ new Thread(() => {
+ try {
+ var url = "https://api.hypixel.net/key?key=" + this.module.apiKeySetting.getValue()
+ let data = fetch(url).jsonSync()
+
+ if (data.success) {
+ new Notification("§aSuccess!", ["Your api key is valid!"])
+ return
+ } else {
+ new Notification("§cError!", ["Your api key is invalid!"])
+ return
+ }
+ } catch (e) {
+ new Notification("§cError!", ["Your api key is invalid!"])
+ return
+ }
+ }).start()
+ }
+
+ newKey(key, event) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Copied api key!")
+ this.apiKeySetting.setValue(key)
+ }
+
+ initVariables() {
+ this.hudElements = [];
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.initVariables()
+ }
+}
+
+class FirstLoadingPage extends GuiPage {
+ constructor(mainThing) {
+ super(-10)
+
+ this.showBackButton = false
+
+ this.name = "First load thing"
+
+ this.mainThing = mainThing
+
+ this.pageThings = []
+
+ firstLoadPages.forEach((page, i) => {
+ let newPage = this.newPage()
+
+ newPage.addChild(page)
+
+ page.setLoc(i !== 0, i !== firstLoadPages.length - 1)
+ page.guiPage = this
+
+ this.pageThings.push(newPage)
+ })
+
+ this.pageNum = 0
+
+ this.finaliseLoading()
+ }
+
+ nextPage() {
+ this.pageNum++
+
+ this.goToPage(this.pageNum)
+ }
+
+ prevPage() {
+ this.pageNum--
+
+ this.goToPage(this.pageNum)
+ }
+
+ onOpen() {
+ this.pageNum = 0
+
+ firstLoadPages.forEach((page, i) => {
+ page.load()
+ })
+ }
+}
+
+module.exports = {
+ class: new GlobalSettings()
+}
+
+
+const ByteArrayInputStream = Java.type("java.io.ByteArrayInputStream")
+const Base64 = Java.type("java.util.Base64")
+const CompressedStreamTools = Java.type("net.minecraft.nbt.CompressedStreamTools")
+function decompress(compressed) {
+ if (compressed === null || compressed.length == 0) {
+ return null
+ }
+
+ return new NBTTagCompound(CompressedStreamTools.func_74796_a(new ByteArrayInputStream(Base64.getDecoder().decode(compressed))))
+} \ No newline at end of file
diff --git a/src/features/globalSettings/metadata.json b/src/features/globalSettings/metadata.json
new file mode 100644
index 0000000..801716f
--- /dev/null
+++ b/src/features/globalSettings/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Global Settings",
+ "description": "Settings that are applied globally",
+ "isHidden": false,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 5
+} \ No newline at end of file
diff --git a/src/features/globalSettings/warps.json b/src/features/globalSettings/warps.json
new file mode 100644
index 0000000..e84c78c
--- /dev/null
+++ b/src/features/globalSettings/warps.json
@@ -0,0 +1,41 @@
+[
+ "hub",
+ "village",
+ "home",
+ "island",
+ "spider",
+ "spiders",
+ "end",
+ "park",
+ "gold",
+ "deep",
+ "mines",
+ "forge",
+ "crystals",
+ "hollows",
+ "ch",
+ "nucleus",
+ "barn",
+ "desert",
+ "castle",
+ "museum",
+ "da",
+ "crypt",
+ "crypts",
+ "nest",
+ "crimson",
+ "isle",
+ "nether",
+ "dragontail",
+ "scarleton",
+ "skull",
+ "tomb",
+ "smoldering",
+ "void",
+ "drag",
+ "jungle",
+ "howl",
+ "dungeon_hub",
+ "dungeons",
+ "dhub"
+] \ No newline at end of file
diff --git a/src/features/guild/index.js b/src/features/guild/index.js
new file mode 100644
index 0000000..27b143b
--- /dev/null
+++ b/src/features/guild/index.js
@@ -0,0 +1,70 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import { toMessageWithLinks } from "../../utils/utils";
+import { fetch } from "../../utils/networkUtils";
+import ToggleSetting from "../settings/settingThings/toggle";
+import TextSetting from "../settings/settingThings/textSetting";
+
+class Guild extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+
+ this.bridgeBots = new Set()
+ fetch("http://soopy.dev/api/soopyv2/gbots.json").json().then(bots => {
+ bots.forEach(b => this.bridgeBots.add(b))
+ })
+
+ this.shortenPrefix = new ToggleSetting("Shorten guild message prefix", "from Guild > to G > ", false, "shorten_prefix", this)
+ this.guildBot = new TextSetting("Bridge bot ign", "", "", "guild_bot_ign", this, "", false)
+
+ //&r&2Guild > &6[MVP&0++&6] zZzSNOW &e[STAFF]&f: &r@niftynathan7, niftynathan7's weight: 20 087 (#1 198) (Skill: 8 771, Slayer: 1 263, Dungeons: 10 053) ,.,,,..,.,,.,,,....,,,,,,,,,..,,,,,..,,,..,,,.,,.,,.,..,,....,,....,,..,.&r
+ //&r&2Guild > &6[MVP&4++&6] Soopyboo32 &e[STAFF]&f: &rasd&r
+ //&r&2Guild > &6[MVP&1++&6] niftynathan7 &e[E]&f: &r@Soopyboo32, Soopyboo32's networth: $8 424 131 866 (#2 592) ,..,...,....,.,,,....,,,...,,,,,,,..,,.,,..,,.,.,,,.........,.....,,,,.....,..,,...,.,.,...,.,&r
+ let ev = this.registerChat('&r&2Guild > ${player}&f: &r${msg}&r', (player, msg, event) => {
+ if (msg.includes("[ITEM:")) return
+ if (player.includes(":")) return; //stop people sending weard messages to troll using this
+
+ //player = &6[MVP&0++&6] zZzSNOW &e[STAFF]
+ let [_, rank, ign, grank] = player.match(/(&7|&[0-9a-fmnl]\[\w+(?:&[0-9a-fmnl]\+*&[0-9a-fmnl])?\] )(\w+)( &[0-9a-fmnl]\[\w+\])?/)
+
+ cancel(event)
+
+ let message = ""
+ if (this.bridgeBots.has(ign) || ign.toLowerCase() === this.guildBot.getValue().toLowerCase()) {
+ let [name, other] = msg.split(/ ?[\>\:\»] /g)
+
+ if (other) {
+ message = `&2B${this.shortenPrefix.getValue() ? "" : "ridge"} > &b${name.split(" replying to ").reverse().join(" &7⤷&b ").trim()}&f: ${msg.replace(name, "").replace(/^ ?[\>\:\»] /, "").trim()}`
+ } else {
+ if (msg.includes("---------------------------------------------") || msg.includes("You have 60 seconds to accept. Click here to join!")) {
+ return //bridge bot bug
+ }
+ message = `&2B${this.shortenPrefix.getValue() ? "" : "ridge"} > &7⤷&f ${msg.trim()}`
+ }
+ } else {
+ if (msg.match(/^@\w+, [\w\W]+[,.]+$/)) {
+ let [_, name2, reply] = msg.match(/^@(\w+?), ([\w\W]+?)[,.]+$/)
+ message = `&2B${this.shortenPrefix.getValue() ? "" : "ridge"} > &b${name2} &7⤷&f ${reply.trim()}`
+ } else {
+ message = `&2G${this.shortenPrefix.getValue() ? "" : "uild"} > ${rank}${ign}${grank || ""}&f: ${msg}`
+ }
+ }
+
+
+ toMessageWithLinks(message).chat()
+ })
+ ev.trigger.triggerIfCanceled(false)
+ }
+
+ onDisable() {
+
+ }
+}
+
+module.exports = {
+ class: new Guild()
+} \ No newline at end of file
diff --git a/src/features/guild/metadata.json b/src/features/guild/metadata.json
new file mode 100644
index 0000000..babd19d
--- /dev/null
+++ b/src/features/guild/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Guild features",
+ "description": "Features nice for guilds, eg bridge formatting",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": false,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/hud/HudTextElement.js b/src/features/hud/HudTextElement.js
new file mode 100644
index 0000000..cdde6f3
--- /dev/null
+++ b/src/features/hud/HudTextElement.js
@@ -0,0 +1,143 @@
+import { HudText } from "../../utils/renderJavaUtils"
+
+class HudTextElement {
+ constructor() {
+ this.text = ""
+
+ this.toggleSetting = undefined
+ this.locationSetting = undefined
+
+ this.editTempTimeV = 0
+ this.editTempTextV = undefined
+
+ this.editBaseWidth = undefined
+ this.editBaseHeight = undefined
+
+ this.tempDisableTime = 0
+
+ this.renderTextCache = [""]
+ this.textChanged = false
+
+ this.renderingDisabled = false
+
+ this.renderElm = new HudText([""], 0, 0, true).startRender()
+ }
+
+ disableRendering() {
+ this.renderingDisabled = true
+ this.renderElm.stopRender()
+ }
+
+ enableRendering() {
+ this.renderingDisabled = false
+ if (this.toggleSetting.getValue()) {
+ this.renderElm.startRender()
+ }
+ }
+
+ delete() {
+ this.renderElm.stopRender()
+ if (this.locationSetting) this.locationSetting.delete()
+ }
+
+ setBaseEditWidth(width) {
+ this.editBaseWidth = width
+ return this
+ }
+
+ setBaseEditHeight(height) {
+ this.editBaseHeight = height
+ return this
+ }
+
+ setText(text = "") {
+ if (text === this.text) return this
+ this.text = text
+
+ this.renderTextCache = ChatLib.addColor(this.text).split("\n")
+
+ this.renderElm.setText(this.renderTextCache)
+ return this
+ }
+ setToggleSetting(setting) {
+ this.toggleSetting = setting
+ setting.onChange = () => {
+ if (this.toggleSetting.getValue() && !this.renderingDisabled) {
+ this.renderElm.startRender()
+ } else {
+ this.renderElm.stopRender()
+ }
+ }
+ if (this.toggleSetting.getValue() && !this.renderingDisabled) {
+ this.renderElm.startRender()
+ } else {
+ this.renderElm.stopRender()
+ }
+ return this
+ }
+ setLocationSetting(setting) {
+ this.locationSetting = setting
+ setting.setParent(this)
+
+ setting.onChange = () => {
+ this.renderElm.setX(this.locationSetting.x).setY(this.locationSetting.y).setScale(this.locationSetting.scale)
+ }
+ this.renderElm.setX(this.locationSetting.x).setY(this.locationSetting.y).setScale(this.locationSetting.scale)
+ return this
+ }
+
+ isEnabled() {
+ if (!this.toggleSetting) return true
+ return this.locationSetting && this.toggleSetting.getValue()
+ }
+
+ render() {
+ if (this.toggleSetting && !this.toggleSetting.getValue() || !this.locationSetting) return
+ if (Date.now() - this.tempDisableTime < 100) return
+
+ this.renderRaw()
+ }
+
+ getWidth(locationBox = false) {
+ if (locationBox && this.editBaseWidth) return this.editBaseWidth
+ return Math.max(...(this.getText().map(a => Renderer.getStringWidth(ChatLib.removeFormatting(a)))))
+ }
+ getHeight(locationBox = false) {
+ if (locationBox && this.editBaseHeight) return this.editBaseHeight
+ return 9 * this.getText().length
+ }
+
+ getText() {
+ if (Date.now() - this.editTempTimeV < 100) {
+ let text = this.text
+ if (this.editTempTextV) {
+ text = this.editTempTextV
+ }
+
+ if (ChatLib.removeFormatting(text) === "") {
+ text = "&0Empty string"
+ }
+
+ return text.split("\n")
+ }
+ return this.renderTextCache
+ }
+
+ renderRaw() {
+ let text = this.getText()
+
+ for (let i = 0, line = text[0]; i < text.length; i++, line = text[i]) {
+ Renderer.scale(this.locationSetting.scale, this.locationSetting.scale)
+ switch (this.locationSetting.shadowType) {
+ case 0:
+ Renderer.drawString(line, this.locationSetting.x / this.locationSetting.scale, this.locationSetting.y / this.locationSetting.scale + 9 * i)
+ break;
+ case 1:
+ Renderer.drawStringWithShadow(line, this.locationSetting.x / this.locationSetting.scale, this.locationSetting.y / this.locationSetting.scale + 9 * i)
+ break;
+ }
+ }
+ }
+}
+
+export default HudTextElement \ No newline at end of file
diff --git a/src/features/hud/index.js b/src/features/hud/index.js
new file mode 100644
index 0000000..8805dac
--- /dev/null
+++ b/src/features/hud/index.js
@@ -0,0 +1,812 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyNumber from "../../../guimanager/Classes/SoopyNumber";
+import Feature from "../../featureClass/class";
+import { m } from "../../../mappings/mappings";
+import LocationSetting from "../settings/settingThings/location";
+import ToggleSetting from "../settings/settingThings/toggle";
+import HudTextElement from "./HudTextElement";
+import DropdownSetting from "../settings/settingThings/dropdownSetting";
+import { getLevelByXp } from "../../utils/statUtils";
+import { firstLetterCapital } from "../../utils/stringUtils";
+import renderLibs from "../../../guimanager/renderLibs";
+import { addNotation, numberWithCommas } from "../../utils/numberUtils.js";
+
+const ProcessBuilder = Java.type("java.lang.ProcessBuilder")
+const Scanner = Java.type("java.util.Scanner")
+
+//TODO: fix messages like [[Frozen Steve]] fell into the pond long ago, never to resurface... until now!
+//TODO: add this feature
+let fishingExp = { "A Squid appeared.": 41, "You caught a Sea Walker.": 68, "Pitch Darkness reveals you've caught a Night Squid.": 270, "You stumbled upon a Sea Guardian.": 101, "It looks like you've disrupted the Sea Witch's brewing session. Watch out, she's furious!": 338, "You reeled in a Sea Archer.": 169, "The Monster of The Deep emerges from the dark depths...": 270, "Huh? A Catfish!": 405, "Gross! A Sea Leech!": 675, "You've discovered a Guardian Defender of the sea.": 1013, "You have awoken the Deep Sea Protector, prepare for a battle!": 1350, "The Water Hydra has come to test your Strength.": 4050, "The Sea Emperor arises from the depths...": 3375, "A Water Worm surfaces!": 240, "A Poisoned Water Worm surfaces!": 270, "A flaming worm surfaces from the depths!": 240, "A Lava Blaze has surfaced from the depths!": 548, "A Lava Pigman arose from the depths!": 568, "A Zombie Miner surfaces!": 770, "From Beneath the lava": 730, "You hear a faint Moo": 950, "A small but fearsome": 1400, "You feel the heat": 1100, "A Lava Flame flies out from beneath the lava.": 2100, "A Fire Eel slithers out": 2200, "Taurus and his steed emerge.": 4300, "You hear a massive rumble as Thunder emerges.": 12000, "You have angered a legendary creature... ": 40000, "WOAH! A Plhlegblast appeared.": 5000, "Phew! It's only a scarecrow.": 420, "You hear trotting from beneath the waves, you caught a Nightmare": 820, "It must be a full moon, it's a Werewolf!": 1235, "The spirit of a long lost Phantom Fisher has come to haunt you.": 2525, "[[Frozen Steve]] fell into the pond long ago, never to resurface... until now!": 101, "Its a Snowman! It looks harmless.": 203, "The Grinch stole [[Jerry]]'s [[Gifts]]...get them back!": 405, "What is this creature!?": 4050, "A tiny fin emerges from the water, you've caught a Nurse Shark.": 405, "You spot a fin as blue as the water it came from, it's a Blue Shark.": 810, "A striped beast bounds from the depths, the wild Tiger Shark!": 1013, "Hide no longer, a Great White Shark has tracked your scent and thirsts for your blood!": 2025 }
+
+class Hud extends Feature {
+ constructor() {
+ super()
+ }
+
+ initVariables() {
+
+ this.hudElements = []
+
+ this.fpsElement = undefined
+ this.cpsElement = undefined
+ this.soulflowElement = undefined
+ this.petElement = undefined
+ this.fpsEnabledSetting = undefined
+ this.cpsEnabledSetting = undefined
+ this.soulflowEnabledSetting = undefined
+ this.soulflowShowWarningSetting = undefined
+ this.soulflowShowWhen0Setting = undefined
+ this.petEnabledSetting = undefined
+ this.fpsFastSetting = undefined
+ this.fpsLowSetting = undefined
+ this.cpsSeperate = undefined
+ this.cpsIncludeRight = undefined
+
+ this.petLevels = undefined
+
+ this.lastTickTime = undefined
+ this.framesSince = undefined
+ this.lastframe = undefined
+ this.Instant = undefined
+
+ this.lastFrameRates = undefined
+
+ this.fps = undefined
+ this.lowFps = undefined
+
+ this.slowestFrameTime = undefined
+ this.lastFrameRatesS = undefined
+ this.numberUtils = undefined
+
+ this.petText = undefined
+
+ this.lastWitherImpact = undefined
+ this.aup = undefined
+ this.lastTickEventEpochTimestamp = undefined
+ this.lastAbsorbtion = undefined
+ this.impactTest = undefined
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.numberUtils = require("../../utils/numberUtils.js")
+
+ this.fpsEnabledSetting = new ToggleSetting("Fps enabled", "Whether the fps is rendered onto the screen", true, "fps_enabled", this)
+ this.fpsFastSetting = new ToggleSetting("Fast fps update", "Whether the fps is updated fast instead of once per second", true, "fps_fast", this).requires(this.fpsEnabledSetting)
+ this.fpsLowSetting = new ToggleSetting("Low fps display", "Display the minumum frame time next to fps (usefull for finding framedrops)", true, "fps_low", this).requires(this.fpsFastSetting)
+
+ this.fpsElement = new HudTextElement()
+ .setToggleSetting(this.fpsEnabledSetting)
+ .setLocationSetting(new LocationSetting("Show FPS", "Allows you to edit the location of the fps text", "fps_location", this, [10, 10, 1, 1])
+ .requires(this.fpsEnabledSetting))
+ this.hudElements.push(this.fpsElement)
+
+ this.cpsEnabledSetting = new ToggleSetting("Show CPS", "Whether the cps is rendered onto the screen", true, "cps_enabled", this)
+ this.cpsIncludeRight = new ToggleSetting("CPS include right click", "Whether right clicks are shown in the CPS", true, "cps_right", this).requires(this.cpsEnabledSetting)
+ this.cpsSeperate = new ToggleSetting("CPS seperate right", "Seperates right clicks from left clicks", true, "cps_seperate", this).requires(this.cpsIncludeRight)
+
+ this.cpsElement = new HudTextElement()
+ .setToggleSetting(this.cpsEnabledSetting)
+ .setLocationSetting(new LocationSetting("Cps Location", "Allows you to edit the location of the cps text", "cps_location", this, [10, 20, 1, 1])
+ .requires(this.cpsEnabledSetting))
+ this.hudElements.push(this.cpsElement)
+
+ this.petEnabledSetting = new ToggleSetting("Show Current Pet", "Whether the current pet is rendered onto the screen", true, "pet_enabled", this)
+ this.petElement = new HudTextElement()
+ .setToggleSetting(this.petEnabledSetting)
+ .setLocationSetting(new LocationSetting("Pet Location", "Allows you to edit the location of the pet text", "pet_location", this, [10, 30, 1, 1])
+ .requires(this.petEnabledSetting)
+ .editTempText("&6Pet&7> &7[Lvl 100] &aEnderman"))
+ this.hudElements.push(this.petElement)
+ this.scanGuiForPet = new ToggleSetting("Scan pets menu gui for selected pet", "Only disable if you get a lot of lag in the pets menu", true, "scan_pets_menu", this).requires(this.petEnabledSetting)
+
+ this.soulflowEnabledSetting = new ToggleSetting("Show Soulflow", "Whether the soulflow count is rendered onto the screen", true, "soulflow_enabled", this)
+ this.soulflowShowWhen0Setting = new ToggleSetting("Show When 0 Soulflow", "If this is off it wont render when you have 0 soulflow", true, "soulflow_showwhen_0", this).requires(this.soulflowEnabledSetting)
+ this.soulflowElement = new HudTextElement()
+ .setToggleSetting(this.soulflowEnabledSetting)
+ .setLocationSetting(new LocationSetting("Soulflow Location", "Allows you to edit the location of the soulflow text", "soulflow_location", this, [10, 40, 1, 1])
+ .requires(this.soulflowEnabledSetting)
+ .editTempText("&6Soulflow&7> &f12,345"))
+ this.hudElements.push(this.soulflowElement)
+
+ this.lagEnabled = new ToggleSetting("Show Lobby TPS", "Calculates the TPS of your current lobby (20=no lag)", true, "lobby_lag", this)
+ this.lagElement = new HudTextElement()
+ .setToggleSetting(this.lagEnabled)
+ .setLocationSetting(new LocationSetting("Lobby TPS Location", "Allows you to edit the location of the TPS", "lobby_lag_location", this, [10, 60, 1, 1])
+ .requires(this.lagEnabled)
+ .editTempText("&6Tps&7> &f20.0"))
+ this.hudElements.push(this.lagElement)
+
+ this.witherImpactCooldownSetting = new ToggleSetting("Show Wither Impact Cooldown", "This will render a small cooldown above your crosshair", true, "wither_impact_cooldown_enabled", this)
+
+ // this.guidedSheepCooldownSetting = new ToggleSetting("Show Guided Sheep / Explosive Shot Cooldown", "This will render a small cooldown below your crosshair", true, "guided_sheep_cooldown_enabled", this)
+
+ this.showSpotifyPlaying = new ToggleSetting("Show Current Playing Spotify Song", "(WINDOWS + Spotify Desktop only)", false, "spotify_now_playing", this)
+ this.spotifyElement = new HudTextElement()
+ .setText("&6Spotify&7> ")
+ .setBaseEditWidth(Renderer.getStringWidth("Spotify> ") + 150)
+ .setToggleSetting(this.showSpotifyPlaying)
+ .setLocationSetting(new LocationSetting("Spotify Location", "Allows you to edit the location of the spotify text", "spotify_now_playing_location", this, [10, 80, 1, 1])
+ .requires(this.showSpotifyPlaying)
+ .editTempText("&6Spotify&7> &cNot open"))
+ this.hudElements.push(this.spotifyElement)
+ this.spotifyElement2 = new HudTextElement().setLocationSetting({
+ setParent: () => { },
+ x: this.spotifyElement.locationSetting.x + this.spotifyElement.getWidth(),
+ y: this.spotifyElement.locationSetting.y,
+ scale: this.spotifyElement.locationSetting.scale,
+ shadowType: this.spotifyElement.locationSetting.shadowType
+ })
+ this.spotifyElement2.disableRendering()
+
+ this.showLobbyDay = new ToggleSetting("Show Current Lobby Day", "", true, "lobby_day", this)
+ this.lobbyDayElement = new HudTextElement()
+ .setText("&6Day&7> &fLoading...")
+ .setToggleSetting(this.showLobbyDay)
+ .setLocationSetting(new LocationSetting("Lobby Day Location", "Allows you to edit the location of the lobby day", "lobby_day_location", this, [10, 90, 1, 1])
+ .requires(this.showLobbyDay)
+ .editTempText("&6Day&7> &f5.15"))
+ this.showLobbyDayOnlyUnder30 = new ToggleSetting("Show Current Lobby Day ONLY WHEN under day 30", "", true, "lobby_day_30", this)
+ this.hudElements.push(this.lobbyDayElement)
+
+ let hudStatTypes = {
+ "cata": "Catacombs level + Exp",
+ "totaldeaths": "Total deaths"
+ }
+
+ this.skillLevelCaps = {
+ "experience_skill_combat": 60,
+ "experience_skill_foraging": 50,
+ "experience_skill_farming": 60,
+ "experience_skill_fishing": 50,
+ "experience_skill_alchemy": 50,
+ "experience_skill_enchanting": 60,
+ "experience_skill_mining": 60,
+ "experience_skill_taming": 50,
+ "experience_skill_carpentry": 50,
+ };
+
+ this.lastSkillLevel = {
+ "experience_skill_combat": undefined,
+ "experience_skill_foraging": undefined,
+ "experience_skill_farming": undefined,
+ "experience_skill_fishing": undefined,
+ "experience_skill_alchemy": undefined,
+ "experience_skill_enchanting": undefined,
+ "experience_skill_mining": undefined,
+ "experience_skill_taming": undefined,
+ "experience_skill_carpentry": undefined,
+ }
+
+ this.spotifyProcessId = -1
+
+ Object.keys(this.skillLevelCaps).forEach(skill => {
+ hudStatTypes[skill] = firstLetterCapital(skill.split("_").pop()) + " level + Exp"
+ })
+
+ hudStatTypes.completions_enterance = "Enterance completions"
+ for (let i = 1; i < 8; i++) {
+ hudStatTypes["completions_floor_" + i] = "Floor " + i + " completions"
+ }
+ for (let i = 1; i < 8; i++) {
+ hudStatTypes["completions_master_" + i] = "Master " + i + " completions"
+ }
+ for (let i = 1; i < 8; i++) {
+ hudStatTypes["completions_dungeon_" + i] = "Dungeon " + i + " completions"
+ }
+
+
+ hudStatTypes["mythril_powder"] = "Mithril Powder"
+ hudStatTypes["gemstone_powder"] = "Gemstone Powder"
+
+ this.extendLevelCap = new ToggleSetting("Hud Stat Ignore Skill Level Cap", "level cap goes over 60 requiring 50m xp per level", false, "hud_ignore_level_cap", this).contributor("EmeraldMerchant")
+ this.showLevelUpMessage = new ToggleSetting("Show level-up message", "Shows skyblock skills level-up message over level 60 in chat", true, "skill_o60_level_message", this).requires(this.extendLevelCap).contributor("EmeraldMerchant")
+
+ this.hudStat = []
+ for (let i = 0; i < 5; i++) {
+ this.hudStat[i] = {}
+ this.hudStat[i].enabled = new ToggleSetting("Hud Stat Slot #" + (i + 1), "Allows you to render a custom stat on your hud", false, "hud_stat_" + i, this)
+ this.hudStat[i].type = new DropdownSetting("Hud Stat Slot #" + (i + 1) + " Type", "The type of stat to render", "weight", "hud_stat_" + i + "_type", this, hudStatTypes)
+ this.hudStat[i].location = new LocationSetting("Hud Stat Slot #" + (i + 1) + " Location", "Allows you to edit the location of the hud stat", "hud_stat_" + i + "_location", this, [10, 50 + i * 10, 1, 1]).editTempText("&6Hud Stat&7> &f12,345")
+ this.hudStat[i].textElement = new HudTextElement().setToggleSetting(this.hudStat[i].enabled).setLocationSetting(this.hudStat[i].location).setText("&6Hud Stat&7> &fLoading...")
+ this.hudStat[i].onlySb = new ToggleSetting("Hud Stat Slot #" + (i + 1) + " Only SB", "Only render this stat when you are in skyblock", true, "hud_stat_" + i + "_only_sb", this).requires(this.hudStat[i].enabled)
+
+ this.hudStat[i].location.requires(this.hudStat[i].enabled)
+ this.hudStat[i].type.requires(this.hudStat[i].enabled)
+ if (this.hudStat[i - 1]) {
+ this.hudStat[i].enabled.requires(this.hudStat[i - 1].enabled)
+ }
+ }
+
+
+ this.showDragonDamages = new ToggleSetting("Show dragon damages", "This will render the top 3 damages + your damage during a dragon fight", true, "dragon_dmg_enable", this)
+ this.dragonDamageElement = new HudTextElement()
+ .setToggleSetting(this.showDragonDamages)
+ .setLocationSetting(new LocationSetting("Damage Location", "Allows you to edit the location of the damage leaderboard", "dragon_dmg_location", this, [50, 40, 1, 1])
+ .requires(this.showDragonDamages)
+ .editTempText("&6Old Dragon &7(&f13.4&7M HP)\n&7- &fSoopyboo32&7:&f 13.4&7M\n&7- &fCamCamSatNav&7:&f 12.3&7M\n&7- &fMuffixy&7:&f 3.4&7M"))
+ this.hudElements.push(this.dragonDamageElement)
+
+
+ this.step_5second()
+
+ this.lastTickTime = 0
+ this.framesSince = 0
+ this.lastframe = 0
+ this.slowestFrameTime = 0
+
+ this.lastSwappedPet = 0
+
+ this.lastWitherImpact = 0
+ this.aup = 0
+ this.lastTickEventEpochTimestamp = 0
+ this.lastAbsorbtion = 0
+ this.impactTest = false
+ this.apiSoulflow = false
+
+ this.lastUpdatedStatData = 0
+
+ this.lastStatData = undefined
+
+ this.lastFrameRates = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+ this.lastFrameRatesS = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+ this.Instant = Java.type("java.time.Instant");
+
+ this.fps = new SoopyNumber(0)
+ this.lowFps = new SoopyNumber(0)
+
+ this.registerEvent("renderOverlay", this.renderHud).registeredWhen(() => this.showSpotifyPlaying.getValue() || this.witherImpactCooldownSetting.getValue())
+ this.registerStep(true, 5, this.step)
+ this.registerStep(false, 5, this.step_5second)
+ this.registerEvent("renderWorld", this.renderWorld).registeredWhen(() => this.fpsEnabledSetting.getValue() && this.fpsFastSetting.getValue())
+ this.registerEvent("worldLoad", this.worldLoad)
+
+
+ this.petLevels = {}
+ this.petText = "&6Pet&7> &fLoading..."
+ this.petElement.setText(this.petText)
+ this.registerChat("&cAutopet &eequipped your ${pet}&e! &a&lVIEW RULE&r", (pet) => {
+ this.petElement.setText("&6Pet&7> " + pet)
+ this.petText = "&6Pet&7> " + pet
+
+ this.lastSwappedPet = Date.now()
+ })
+ this.registerChat("&r&aYou summoned your &r${pet}&r&a!&r", (pet) => {
+ this.petElement.setText("&6Pet&7> &7[Lvl " + (this.petLevels[pet.replace("&", "§")] || "??") + "] " + pet)
+ this.petText = "&6Pet&7> &7[Lvl " + (this.petLevels[pet.replace("&", "§")] || "??") + "] " + pet
+
+ this.lastSwappedPet = Date.now()
+ })
+ this.registerChat("&r&aYou despawned your &r${*}&r&a!&r", () => {
+ this.petElement.setText("&6Pet&7> &cNone")
+ this.petText = "&6Pet&7> &cNone"
+
+ this.lastSwappedPet = Date.now()
+ })
+ this.registerChat("&r&aYour &r${pet} &r&alevelled up to level &r&9${level}&r&a!&r", (pet, level) => {
+ if (ChatLib.removeFormatting(this.petText.split("] ")[1].trim()) === ChatLib.removeFormatting(pet.trim())) {
+ this.petElement.setText("&6Pet&7> &7[Lvl " + (level || "??") + "] " + pet)
+ this.petText = "&6Pet&7> &7[Lvl " + (level || "??") + "] " + pet
+ this.lastSwappedPet = Date.now()
+ }
+ })
+
+ this.registerSoopy("apiLoad", this.apiLoad)
+ if (this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock) {
+ this.apiLoad(this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock, "skyblock", true, true)
+
+ this.lastSwappedPet = Date.now()
+ }
+ if (this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock_raw) {
+ this.apiLoad(this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock_raw, "skyblock", false, true)
+ }
+
+ this.registerStep(false, 5, () => {
+ if (!this.showSpotifyPlaying.getValue()) return
+
+ new Thread(() => {
+ this.updateSpotify()
+ }).start()
+ })
+
+ this.registerActionBar("${m}", this.actionbarMessage)
+
+ this.packetMoves = 0
+ this.secondPackets = 0
+ this.tps = -2
+ this.lastTps = []
+ this.registerEvent("tick", this.tick)
+ this.registerStep(false, 1, this.step_1second)
+ }
+
+ step_1second() {
+ if (World.getTime() / 20 / 60 / 20 > 30 && this.showLobbyDayOnlyUnder30.getValue()) {
+ this.lobbyDayElement.setText("")
+ } else {
+ if (World.getTime() !== 0) this.lobbyDayElement.setText("&6Day&7> &f" + (World.getTime() / 20 / 60 / 20).toFixed(2))
+ }
+
+ if (!this.lagEnabled.getValue()) {
+ if (this.packetReceived) this.packetReceived.unregister()
+ return
+ }
+ if (!this.packetReceived) this.packetReceived = register("packetReceived", () => {
+ this.packetMoves++
+ })
+ this.lastTps.push(this.secondPackets)
+ if (this.lastTps.length > 10) this.lastTps.shift()
+ if (this.tps === -2) {
+ this.tps = -1
+ this.lastTps = []
+ }
+ this.tps = this.lastTps.reduce((a, b) => a + b, 0) / this.lastTps.length
+ this.secondPackets = 0
+
+ if (this.lastTps.length > 1) {
+ this.lagElement.setText("&6Tps&7> &f" + Math.min(20, this.tps).toFixed(1))
+ } else {
+ this.lagElement.setText("&6Tps&7> &fLOADING")
+ }
+ }
+
+ tick() {
+ if (this.fpsFastSetting.getValue()) {
+ if (this.fpsLowSetting.getValue()) {
+ this.fpsElement.setText("&6Fps&7> &f" + Math.round(this.fps.get()) + "&7/" + Math.round(this.lowFps.get()))
+ } else {
+ this.fpsElement.setText("&6Fps&7> &f" + Math.round(this.fps.get()))
+ }
+ }
+
+ if (!this.lagEnabled.getValue()) return
+ if (this.packetMoves > 0) {
+ this.secondPackets++
+ this.packetMoves = 0
+ }
+ }
+
+ onDisable() {
+ this.fpsEnabledSetting.delete()
+ this.fpsFastSetting.delete()
+ this.cpsEnabledSetting.delete()
+
+ this.hudStat.forEach(h => h.textElement.delete())
+ this.hudElements.forEach(h => h.delete())
+
+ this.initVariables()
+
+ if (this.packetReceived) this.packetReceived.unregister()
+ }
+
+ renderHud() {
+ if (this.showSpotifyPlaying.getValue() && Date.now() - this.spotifyElement.tempDisableTime > 100) {
+ let scale = this.spotifyElement.locationSetting.scale
+ let spotifyWidth1 = this.spotifyElement.getWidth() * scale
+ this.spotifyElement2.locationSetting.x = this.spotifyElement.locationSetting.x + spotifyWidth1
+ this.spotifyElement2.locationSetting.y = this.spotifyElement.locationSetting.y
+ this.spotifyElement2.locationSetting.scale = scale
+ this.spotifyElement2.locationSetting.shadowType = this.spotifyElement.locationSetting.shadowType
+
+ let spotifyWidth2 = this.spotifyElement2.getWidth() * scale
+ if (spotifyWidth2 > 150 * scale) {
+ let w2 = spotifyWidth2 / scale - 150
+ let offX = (Date.now() / 50) % (w2 * 2 + 100)
+ offX = Math.max(0, offX - 50)
+ if (offX > w2 + 50) {
+ offX = w2 - (offX - w2 - 50)
+ } else if (offX > w2) {
+ offX = w2
+ }
+ this.spotifyElement2.locationSetting.x = this.spotifyElement.locationSetting.x + spotifyWidth1 - offX * scale
+
+ renderLibs.scizzorFast(this.spotifyElement.locationSetting.x + spotifyWidth1, this.spotifyElement2.locationSetting.y, 150 * scale, this.spotifyElement2.getHeight() * scale)
+ this.spotifyElement2.render()
+ renderLibs.stopScizzor()
+ } else {
+ this.spotifyElement2.render()
+ }
+ }
+
+ if (this.witherImpactCooldownSetting.getValue() && Date.now() - this.lastWitherImpact < 10000) {
+ Renderer.drawString(Math.max(0, Math.ceil((5000 - (Date.now() - this.lastWitherImpact)) / 1000)) + "s", Renderer.screen.getWidth() / 2 - Renderer.getStringWidth(Math.max(0, Math.ceil((5000 - (Date.now() - this.lastWitherImpact)) / 1000)) + "s") / 2, Renderer.screen.getHeight() / 2 - 15)
+ }
+ }
+
+ renderWorld() {
+ this.framesSince++
+
+ let instant = this.Instant.now()
+ let time = instant.getEpochSecond() + (instant.getNano() / 1000000000);
+
+ let thisframeTime = time - this.lastFrame
+ // console.log(thisframeTime * 1000)
+
+ if (thisframeTime > this.slowestFrameTime) {
+ this.slowestFrameTime = thisframeTime
+ }
+
+ this.lastFrame = time
+ }
+
+ actionbarMessage(m) {
+ if (ChatLib.removeFormatting(m).includes("(Wither Impact)")) {
+ if (Date.now() - this.aup < 750) {
+ this.lastWitherImpact = Date.now()
+ this.aup = 0
+ } else {
+ this.impactTest = Date.now()
+ }
+ }
+ }
+
+ step() {
+ if (!Player.getPlayer()) return
+ let fps = 0
+
+ if (this.fpsEnabledSetting.getValue() && this.fpsFastSetting.getValue()) {
+ //set fps to fast fps
+ // console.log(`${this.framesSince} ${this.lastFrame-this.lastTickTime}`)
+ fps = this.framesSince / (this.lastFrame - this.lastTickTime)
+ if (this.lastFrame === this.lastTickTime) fps = 0
+ this.lastTickTime = this.lastFrame
+ this.framesSince = 0
+
+ this.lastFrameRates.push(fps)
+ this.lastFrameRates.shift()
+
+ if (this.slowestFrameTime > 0) {
+ this.lastFrameRatesS.push(1 / this.slowestFrameTime)
+ } else {
+ this.lastFrameRatesS.push(0)
+ }
+ this.lastFrameRatesS.shift()
+ this.slowestFrameTime = 0
+
+ fps = this.lastFrameRates.reduce((a, b) => a + b, 0) / this.lastFrameRates.length
+ this.fps.set(fps, 200)
+ if (this.fpsLowSetting.getValue()) this.lowFps.set(this.lastFrameRatesS.reduce((a, b) => a + b, 0) / this.lastFrameRatesS.length, 200)
+ } else {
+ fps = Client.getFPS()
+ this.fpsElement.setText("&6Fps&7> &f" + fps)
+ }
+
+ let cpsText = CPS.getLeftClicksAverage()
+
+ if (this.cpsIncludeRight.getValue()) {
+ if (this.cpsSeperate.getValue()) {
+ cpsText += "&7 | &f" + CPS.getRightClicksAverage()
+ } else {
+ cpsText += CPS.getRightClicksAverage()
+ }
+ }
+ this.cpsElement.setText("&6Cps&7> &f" + cpsText)
+
+ //Scan opened inventory for all pet levels
+ if (this.scanGuiForPet.getValue() && Player && Player.getContainer() && Player.getContainer().getName().includes(") Pets")) {
+ let inv = Player.getContainer().getItems()
+ for (let i = 0; i < inv.length; i++) {
+ if (inv[i] != null && inv[i].getName().includes("[Lvl ")) {
+ let level = inv[i].getName().split(" ")[1].replace("]", "")
+ if (!this.petLevels[inv[i].getName().split("] ")[1]] || this.petLevels[inv[i].getName().split("] ")[1]] < level) this.petLevels[inv[i].getName().split("] ")[1]] = level
+
+ if (Date.now() - this.lastSwappedPet > 1000) {
+ inv[i].getLore().forEach(line => {
+ if (line.includes("Click to despawn!")) {
+ this.petElement.setText("&6Pet&7> &7" + inv[i].getName().split("(")[0])
+ this.petText = "&6Pet&7> &7" + inv[i].getName().split("(")[0]
+ }
+ })
+ }
+ }
+ }
+ }
+
+ this.dragonDamageElement.setText("")
+
+ if (this.showDragonDamages.getValue()) {
+
+ let playerDamage = -1
+ let dragonHealth = -1
+
+ Scoreboard.getLines().forEach(line => {
+ if (ChatLib.removeFormatting(line).startsWith("Your Damage: ")) {
+ playerDamage = parseInt(ChatLib.removeFormatting(line).replace("Your Damage: ", "").replace(/[^0-9\.]/g, ""))
+ }
+ if (ChatLib.removeFormatting(line).startsWith("Dragon HP: ")) {
+ dragonHealth = parseInt(ChatLib.removeFormatting(line).replace("Dragon HP: ", "").replace(/[^0-9\.]/g, ""))
+ }
+ })
+
+ if (playerDamage > -1) {
+
+ let damages = [[Player.getName(), playerDamage]]
+ let dragonType = ""
+
+ let lbNum = 0
+
+ TabList.getNames().forEach(n => {
+ l = ChatLib.removeFormatting(n)
+
+ if (lbNum > 0) {
+ lbNum--
+
+ // Soopyboo32: 3.2M
+
+ let name = l.split(":")[0].trim()
+
+ if (name === Player.getName()) return
+
+ let damage = l.split(" ").pop()
+
+ let actualDamage = parseFloat(damage)
+
+ if (damage.includes("k")) actualDamage *= 1000
+ if (damage.includes("M")) actualDamage *= 1000000
+
+ damages.push([name, actualDamage])
+ }
+ if (l.startsWith("Dragon Fight: (")) {
+ dragonType = l.split("(")[1].split(")")[0]
+
+ lbNum = 3
+ }
+ })
+
+ let text = `&6${dragonType} Dragon &7(&f${addNotation("oneLetters", dragonHealth, "&7")}&7 HP)`
+
+ damages.sort((a, b) => b[1] - a[1]).forEach(d => {
+ text += `\n&7- &f${d[0]}&7: &f${addNotation("oneLetters", d[1], "&7")}`
+ })
+
+ this.dragonDamageElement.setText(text)
+ }
+
+ }
+
+ if (Player.getPlayer()[m.getAbsorptionAmount]() > this.lastAbsorbtion) {
+ if (Date.now() - this.impactTest < 750) {
+ this.lastWitherImpact = Date.now()
+ this.impactTest = 0
+ } else {
+ this.aup = Date.now()
+ }
+ }
+ this.lastAbsorbtion = Player.getPlayer()[m.getAbsorptionAmount]()
+ }
+
+ step_5second() {
+ this.updateHudThingos()
+
+ if (!this.soulflowEnabledSetting.getValue()) return
+ if (!Player.getPlayer()) return
+ if (!Player.getInventory()) return
+
+ if (this.FeatureManager.features["dataLoader"] && !this.FeatureManager.features["dataLoader"].class.isInSkyblock) {
+ this.soulflowElement.setText("")
+ this.petElement.setText("")
+ return
+ } else {
+ this.petElement.setText(this.petText)
+ }
+
+ if (!this.apiSoulflow) {
+ this.soulflowElement.setText("")
+ return;
+ }
+
+ this.soulflowElement.setText("&6Soulflow&7> &f" + this.numberUtils.numberWithCommas(this.lastStatData.soulflow))
+ }
+
+ statApiLoadThingo(data) {
+ data.profiles.forEach(p => {
+ if (!this.lastStatData || (p.members[Player.getUUID().toString().replace(/-/g, "")] && p.members[Player.getUUID().toString().replace(/-/g, "")].last_save > this.lastStatData.last_save)) {
+ this.lastStatData = p.members[Player.getUUID().toString().replace(/-/g, "")]
+ }
+ })
+
+ if (this.lastStatData) {
+ if (this.lastStatData.soulflow) this.apiSoulflow = true
+
+ if (this.apiSoulflow) this.soulflowElement.setText("&6Soulflow&7> &f" + this.numberUtils.numberWithCommas(this.lastStatData.soulflow))
+ }
+
+ if (this.showLevelUpMessage.getValue()) {
+ Object.keys(this.skillLevelCaps).forEach(skill => {
+ let skillData = getLevelByXp(this.lastStatData[skill], 0, this.extendLevelCap.getValue() ? Infinity : this.skillLevelCaps[skill])
+ if (this.lastSkillLevel[skill] === skillData.level - 1 && (skillData.level > (this.skillLevelCaps[skill] === 50 ? 50 : 60))) {
+ ChatLib.chat(`&r&3&l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬&r`)
+ ChatLib.chat(` &r&b&lSKILL LEVEL UP &3${firstLetterCapital(skill.split("_").pop())} &8${skillData.level - 1}➜&3${skillData.level}&r`)
+ ChatLib.chat(`&r &r&a&lREWARDS&r\n&r &r&6&lSoopy's Respect\n&r&3&l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬&r\n`)
+ }
+ this.lastSkillLevel[skill] = skillData.level;
+ })
+ }
+
+ this.updateHudThingos()
+ }
+
+ updateSpotify() {
+ if (!this.showSpotifyPlaying.getValue()) return
+
+ let currentSong = "&cNot open"
+
+ if (this.spotifyProcessId !== -1) {
+ let pid = this.spotifyProcessId
+
+ let process = new ProcessBuilder("tasklist.exe", "/FO", "csv", "/V", "/FI", "\"PID eq " + pid + "\"").start();
+ let sc = new Scanner(process.getInputStream());
+ if (sc.hasNextLine()) sc.nextLine();
+ while (sc.hasNextLine()) {
+ let line = sc.nextLine();
+ let parts = line.replace("\"", "").split("\",\"");
+ let song = parts[parts.length - 1].substr(0, parts[parts.length - 1].length - 1)
+ if (song === "N/A") continue
+
+ if (song === "Spotify Free" || song === "Spotify Premium" || song === "AngleHiddenWindow") {
+ currentSong = "&cPaused"
+ } else {
+ if (song === "Spotify") song = "Advertisement"
+ currentSong = "&a" + song.replace(/&/g, "&⭍").replace(" - ", " &7-&b ")
+ }
+
+ }
+ process.waitFor();
+
+ this.spotifyElement2.setText(currentSong.normalize("NFD").replace(/[\u0300-\u036f]/g, ""))
+ }
+
+ if (currentSong !== "&cNot open") return
+
+ this.spotifyProcessId = -1
+ let spotifyProcesses = []
+ let process = new ProcessBuilder("tasklist.exe", "/fo", "csv", "/nh").start();
+ let sc = new Scanner(process.getInputStream());
+ if (sc.hasNextLine()) sc.nextLine();
+ while (sc.hasNextLine()) {
+ let line = sc.nextLine();
+ let parts = line.replace("\"", "").split("\",\"");
+ let unq = parts[0]
+ let pid = parts[1]
+ if (unq === "Spotify.exe") {
+ spotifyProcesses.push(pid)
+ // console.log(parts.join(" "));
+ }
+ }
+ process.waitFor();
+
+ while (spotifyProcesses.length > 0) {
+ let pid = spotifyProcesses.pop()
+ // console.log("Loading pid " + pid)
+ let process = new ProcessBuilder("tasklist.exe", "/FO", "csv", "/V", "/FI", "\"PID eq " + pid + "\"").start();
+ let sc = new Scanner(process.getInputStream());
+ if (sc.hasNextLine()) sc.nextLine();
+ while (sc.hasNextLine()) {
+ let line = sc.nextLine();
+ let parts = line.replace("\"", "").split("\",\"");
+ let song = parts[parts.length - 1].substr(0, parts[parts.length - 1].length - 1)
+ if (song === "N/A") continue
+
+ this.spotifyProcessId = pid
+
+ if (song === "Spotify Free" || song === "Spotify Premium" || song === "AngleHiddenWindow") {
+ currentSong = "&cPaused"
+ } else {
+ if (song === "Spotify") song = "Advertisement"
+ currentSong = "&a" + song.replace(/&/g, "&⭍").replace(" - ", " &7-&b ")
+ }
+
+ }
+ process.waitFor();
+ }
+
+ this.spotifyElement2.setText(currentSong.normalize("NFD").replace(/[\u0300-\u036f]/g, ""))
+ }
+
+ updateHudThingos() {
+ let insb = !!(this.FeatureManager.features["dataLoader"] && this.FeatureManager.features["dataLoader"].class?.isInSkyblock)
+
+ this.hudStat.forEach(stat => {
+ if (stat.enabled.getValue()) {
+ this.updateHudThing(stat, insb)
+ } else {
+ stat.textElement.setText("")
+ }
+ })
+ }
+
+ updateHudThing(thing, insb) {
+ if (!this.lastStatData) return
+
+ if (!insb && thing.onlySb.getValue()) {
+ thing.textElement.setText("")
+ return
+ }
+
+ let type = thing.type.getValue()
+
+ let string = "Unknown stat"
+ if (type === "totaldeaths") {
+ string = "&6Deaths&7> &f" + this.numberUtils.numberWithCommas(this.lastStatData.death_count)
+ }
+ if (type === "cata") {
+ let cataData = getLevelByXp(this.lastStatData.dungeons.dungeon_types.catacombs.experience, 2, Infinity)
+ string = "&6Cata&7> &f" + (~~((cataData.level + cataData.progress) * 100) / 100).toFixed(2) + " &7(" + this.numberUtils.numberWithCommas(cataData.xpCurrent) + (cataData.level === 50 ? "" : "/" + this.numberUtils.numberWithCommas(cataData.xpForNext)) + ")"
+ }
+ if (type === "mythril_powder") {
+ string = "&6Mithril Powder&7> &f" + numberWithCommas(this.lastStatData.mining_core.powder_mithril_total)
+ }
+ if (type === "gemstone_powder") {
+ string = "&6Gemstone Powder&7> &f" + numberWithCommas(this.lastStatData.mining_core.powder_gemstone_total)
+ }
+
+ Object.keys(this.skillLevelCaps).forEach(skill => {
+ if (type === skill) {
+ let skillData = getLevelByXp(this.lastStatData[skill], 0, this.extendLevelCap.getValue() ? Infinity : this.skillLevelCaps[skill])
+ this.lastSkillLevel[skill] = skillData.level;
+ string = "&6" + firstLetterCapital(skill.split("_").pop()) + "&7> &f" + (skillData.level + skillData.progress).toFixed(2) + " &7(" + this.numberUtils.numberWithCommas(skillData.xpCurrent) + (skillData.level === this.skillLevelCaps[skill] ? "" : "/" + this.numberUtils.numberWithCommas(skillData.xpForNext)) + ")"
+ }
+ })
+
+ if (type === "completions_enterance") {
+ string = "&6E Comps&7> &f" + this.numberUtils.numberWithCommas((this.lastStatData.dungeons?.dungeon_types?.catacombs?.tier_completions?.[0] || 0))
+ }
+ if (type.startsWith("completions_floor_")) {
+ let floor = parseInt(type.split("_").pop())
+ string = "&6F" + floor + " Comps&7> &f" + this.numberUtils.numberWithCommas((this.lastStatData.dungeons?.dungeon_types?.catacombs?.tier_completions?.[floor] || 0))
+ }
+ if (type.startsWith("completions_master_")) {
+ let floor = parseInt(type.split("_").pop())
+ string = "&6M" + floor + " Comps&7> &f" + this.numberUtils.numberWithCommas((this.lastStatData.dungeons?.dungeon_types?.master_catacombs?.tier_completions?.[floor] || 0))
+ }
+ if (type.startsWith("completions_dungeon_")) {
+ let floor = parseInt(type.split("_").pop())
+ string = "&6Dungeon " + floor + " Comps&7> &f" + this.numberUtils.numberWithCommas((this.lastStatData.dungeons?.dungeon_types?.catacombs?.tier_completions?.[floor] || 0) + (this.lastStatData.dungeons?.dungeon_types?.master_catacombs?.tier_completions?.[floor] || 0))
+ }
+
+ thing.textElement.setText(string)
+ }
+
+ apiLoad(data, dataType, isSoopyServer, isLatest) {
+ if (dataType === "skyblock" && !isSoopyServer) {
+ this.statApiLoadThingo(data)
+ }
+ if (!isSoopyServer || !isLatest) return
+ if (dataType !== "skyblock") return
+
+ let pet = data.data.profiles[data.data.stats.currentProfileId].members[Player.getUUID().replace(/-/g, "")].selectedPet
+
+ if (!pet) {
+ this.petElement.setText("&6Pet&7> &cNone")
+ this.petText = "&6Pet&7> &cNone"
+ return;
+ }
+
+ let petTierColor = {
+ "COMMON": "&f",
+ "UNCOMMON": "&a",
+ "RARE": "&9",
+ "EPIC": "&5",
+ "LEGENDARY": "&6",
+ "MYTHIC": "&d"
+ }
+
+ this.petElement.setText("&6Pet&7> &7[Lvl " + (pet.level.level || "??") + "] " + petTierColor[pet.tier] + pet.name)
+ this.petText = "&6Pet&7> &7[Lvl " + (pet.level.level || "??") + "] " + petTierColor[pet.tier] + pet.name
+ }
+
+ worldLoad() {
+ this.lastUpdatedStatData = 0
+ this.packetMoves = 0
+ this.secondPackets = 0
+ this.tps = -2
+ this.lastTps = []
+ this.lagElement.setText("&6Tps&7> &fLOADING")
+ }
+}
+
+module.exports = {
+ class: new Hud()
+}
diff --git a/src/features/hud/metadata.json b/src/features/hud/metadata.json
new file mode 100644
index 0000000..67f5671
--- /dev/null
+++ b/src/features/hud/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Hud",
+ "description": "Adds a HUD with custom gui elements like fps, cps, equipped pet ect",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 2
+} \ No newline at end of file
diff --git a/src/features/lockedFeatures/index.js b/src/features/lockedFeatures/index.js
new file mode 100644
index 0000000..7557619
--- /dev/null
+++ b/src/features/lockedFeatures/index.js
@@ -0,0 +1,121 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import ToggleSetting from "../settings/settingThings/toggle";
+import SoopyV2Server from "../../socketConnection"
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import { numberWithCommas, timeNumber2, timeSince } from "../../utils/numberUtils";
+import FakeRequireToggle from "../settings/settingThings/FakeRequireToggle";
+
+class LockedFeatures extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.guildEventLbPossible = new FakeRequireToggle(false)
+ this.guildEventLb = new ToggleSetting("Guild event leaderboard", "A gui element for guild leaderboard progress", true, "guild_event_lb", this).requires(this.guildEventLbPossible)
+
+ this.hudElements = []
+ this.guildLbElement = new HudTextElement()
+ .setToggleSetting(this.guildEventLb)
+ .setLocationSetting(new LocationSetting("Guild Lb Location", "Allows you to edit the location of the guild leaderboard", "guild_lb_location", this, [50, 40, 1, 1])
+ .requires(this.guildEventLb))
+ this.hudElements.push(this.guildLbElement)
+
+ this.eventCommand = undefined
+
+ this.registerStep(true, 1, this.step)
+ }
+
+ step() {
+ if (!SoopyV2Server.lbdatathing) {
+ this.guildEventLbPossible.set(false)
+ if (this.eventCommand) {
+ this.eventCommand.unregister()
+ this.eventCommand = undefined
+ }
+ return;
+ }
+
+ this.guildEventLbPossible.set(true)
+
+ if (!this.eventCommand) {
+ this.eventCommand = this.registerCommand("eventlb", () => {
+ SoopyV2Server.lbdatathing.forEach((u, i) => {
+ let text = ""
+ text += "§6#" + (i + 1)
+ text += "§7 - "
+ text += "§e" + u.username
+ text += "&7: §r" + numberWithCommas(Math.round(parseFloat(u.startingAmount)))
+ if (u.progress) text += " §7(" + (u.progress > 0 ? "+" : "-") + Math.abs(Math.round(u.progress)) + "/h)"
+ ChatLib.chat(text)
+ })
+ })
+ }
+
+ if (!this.guildEventLb.getValue()) return
+
+ let text = ""
+
+ let playerPos = 0
+
+ SoopyV2Server.lbdatathing.forEach((u, i) => {
+ if (u.uuid === Player.getUUID().toString().replace(/-/g, "")) playerPos = i
+ })
+
+ let prevProgress
+ let playerProgress
+ let nextProgress
+
+ SoopyV2Server.lbdatathing.forEach((u, i) => {
+ if (i === playerPos - 1) nextProgress = [parseFloat(u.startingAmount), u.progress]
+ if (i === playerPos) playerProgress = [parseFloat(u.startingAmount), u.progress]
+ if (i === playerPos + 1) prevProgress = [parseFloat(u.startingAmount), u.progress]
+ if (i === playerPos - 1 || i === playerPos || i === playerPos + 1 || (playerPos === 0 && i === playerPos + 2)) {
+ text += "§6#" + (i + 1)
+ text += "§7 - "
+ text += "§e" + u.username
+ text += "&7: §f" + numberWithCommas(Math.round(parseFloat(u.startingAmount)))
+ if (u.progress) text += " §7(" + (u.progress > 0 ? "+" : "-") + Math.abs(Math.round(u.progress)) + "/h)"
+ text += "\n"
+ }
+ })
+
+ text += "&dLast updated " + timeSince(SoopyV2Server.lbdatathingupdated) + " ago"
+
+ let timeTillIncrease = Infinity
+ let timeTillDecrease = Infinity
+ if (nextProgress && nextProgress[1] - playerProgress[1] < 0) {
+ timeTillIncrease = ((nextProgress[0] - playerProgress[0]) / (playerProgress[1] - nextProgress[1]) * 60 * 60 * 1000)
+ }
+ if (prevProgress && prevProgress[1] - playerProgress[1] < 0) {
+ timeTillDecrease = ((playerProgress[0] - prevProgress[0]) / (prevProgress[1] - playerProgress[1]) * 60 * 60 * 1000)
+ }
+
+ if ((timeTillIncrease < timeTillDecrease || (timeTillIncrease > 0)) && timeTillDecrease < 0 && timeTillIncrease < 10000000000) {
+ text = "&d ^ in " + timeNumber2(timeTillIncrease) + "\n" + text
+ }
+ if ((timeTillIncrease > timeTillDecrease || (timeTillDecrease > 0)) && timeTillIncrease < 0 && timeTillDecrease < 10000000000) {
+ text = "&d v in " + timeNumber2(timeTillDecrease) + "\n" + text
+ }
+
+ this.guildLbElement.setText(text)
+ }
+
+ initVariables() {
+
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.initVariables()
+ }
+}
+
+module.exports = {
+ class: new LockedFeatures()
+}
diff --git a/src/features/lockedFeatures/metadata.json b/src/features/lockedFeatures/metadata.json
new file mode 100644
index 0000000..7506cc2
--- /dev/null
+++ b/src/features/lockedFeatures/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Locked Features",
+ "description": "A bunch of random features that are only accessable to some people\n§0(eg only people in specific guild)",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/mining/coords.json b/src/features/mining/coords.json
new file mode 100644
index 0000000..246731b
--- /dev/null
+++ b/src/features/mining/coords.json
@@ -0,0 +1,202 @@
+[
+ [
+ -7,
+ 26,
+ -2
+ ],
+ [
+ -15,
+ 26,
+ 31
+ ],
+ [
+ -17,
+ 26,
+ 19
+ ],
+ [
+ 47,
+ 25,
+ 33
+ ],
+ [
+ 36,
+ 26,
+ 45
+ ],
+ [
+ 48,
+ 27,
+ 45
+ ],
+ [
+ 45,
+ 27,
+ -13
+ ],
+ [
+ -38,
+ 26,
+ 21
+ ],
+ [
+ 42,
+ 26,
+ 27
+ ],
+ [
+ 29,
+ 27,
+ -7
+ ],
+ [
+ 22,
+ 26,
+ -15
+ ],
+ [
+ -7,
+ 27,
+ -26
+ ],
+ [
+ -2,
+ 26,
+ -6
+ ],
+ [
+ 43,
+ 27,
+ -21
+ ],
+ [
+ 10,
+ 26,
+ -11
+ ],
+ [
+ 17,
+ 26,
+ 49
+ ],
+ [
+ 19,
+ 26,
+ -17
+ ],
+ [
+ -35,
+ 27,
+ 35
+ ],
+ [
+ 25,
+ 27,
+ 5
+ ],
+ [
+ -37,
+ 24,
+ 46
+ ],
+ [
+ -24,
+ 26,
+ 49
+ ],
+ [
+ -7,
+ 26,
+ 48
+ ],
+ [
+ -14,
+ 27,
+ -24
+ ],
+ [
+ -18,
+ 27,
+ 44
+ ],
+ [
+ -1,
+ 26,
+ -23
+ ],
+ [
+ 41,
+ 25,
+ -37
+ ],
+ [
+ 19,
+ 26,
+ -38
+ ],
+ [
+ -7,
+ 27,
+ 27
+ ],
+ [
+ 42,
+ 26,
+ 19
+ ],
+ [
+ 42,
+ 26,
+ 19
+ ],
+ [
+ -33,
+ 27,
+ 31
+ ],
+ [
+ 6,
+ 27,
+ 25
+ ],
+ [
+ -2,
+ 26,
+ -17
+ ],
+ [
+ -15,
+ 27,
+ 5
+ ],
+ [
+ -20,
+ 27,
+ -12
+ ],
+ [
+ -25,
+ 26,
+ 30
+ ],
+ [
+ 28,
+ 27,
+ -35
+ ],
+ [
+ -19,
+ 27,
+ -22
+ ],
+ [
+ 4,
+ 26,
+ -15
+ ],
+ [
+ 36,
+ 26,
+ 17
+ ]
+] \ No newline at end of file
diff --git a/src/features/mining/index.js b/src/features/mining/index.js
new file mode 100644
index 0000000..0e9c7c0
--- /dev/null
+++ b/src/features/mining/index.js
@@ -0,0 +1,469 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import { m } from "../../../mappings/mappings";
+import * as stringUtils from "../../utils/stringUtils";
+import * as utils from "../../utils/utils"
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import ToggleSetting from "../settings/settingThings/toggle";
+import { numberWithCommas, timeSince2 } from "../../utils/numberUtils";
+import { fetch } from "../../utils/networkUtils";
+import socketConnection from "../../socketConnection";
+import { drawCoolWaypoint } from "../../utils/renderUtils";
+
+class Mining extends Feature {
+ constructor() {
+ super()
+ }
+
+ isInCH() {
+ if (!this.FeatureManager || !this.FeatureManager.features["dataLoader"]) return false
+ return this.FeatureManager.features["dataLoader"].class.area === "Crystal Hollows"
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.hudElements = []
+
+ this.guessBalHp = new ToggleSetting("Show bal hp", "This will attempt to show remaining hp on bal (guessed)", true, "bal_hp", this)
+ this.balRespawnHud = new ToggleSetting("Show bal hp and respawn timer HUD", "This will add a HUD element with bal's hp and respawn timer", true, "bal_hud", this)
+ this.balHudElement = new HudTextElement()
+ .setToggleSetting(this.balRespawnHud)
+ .setLocationSetting(new LocationSetting("HUD Location", "Allows you to edit the location of the bal info", "bal_hud_location", this, [10, 50, 1, 1])
+ .requires(this.balRespawnHud)
+ .editTempText("&6Bal&7> &f172/250"))
+ this.hudElements.push(this.balHudElement)
+ this.balPetAlert = new ToggleSetting("Bal pet alert", "Give a notification on screen + sound when you drop a bal pet", true, "bal_alert", this)
+
+ this.showUnlockedGemstoneSlots = new ToggleSetting("Show unlocked gemstone slots", "This will show the unlocked gemstone slots of an item.", true, "unlocked_gemstones", this)
+ this.showContainedGemstoneSlots = new ToggleSetting("Show contained gemstones", "This will show the gemstones currently on an item.", true, "contained_gemstones", this)
+
+
+ this.compactProgressHud = new ToggleSetting("Show compact blocks in the current session", "This will add a HUD element with the compact progress", true, "compact_progress_hud", this)
+ this.compactHudElement = new HudTextElement()
+ .setToggleSetting(this.compactProgressHud)
+ .setLocationSetting(new LocationSetting("HUD Location", "Allows you to edit the location of the compact progress", "compact_progress_location", this, [10, 50, 1, 1])
+ .requires(this.compactProgressHud)
+ .editTempText("&6Compact Session&7> &f12,345"))
+ this.hudElements.push(this.compactHudElement)
+ this.compactProgressHudOnlyWhenMoreThan0 = new ToggleSetting("Only show compact progress when it is above 0", "So that you dont need to disable it when you start doing something else", true, "compact_progress_disable_0", this).requires(this.compactProgressHud)
+
+ this.gemstoneMoneyHud = new ToggleSetting("Show $/h made from gemstone mining", "This will add a HUD element with the gemstone $/h", true, "gemstone_money_hud", this)
+ this.gemstoneMoneyHudElement = new HudTextElement()
+ .setToggleSetting(this.gemstoneMoneyHud)
+ .setLocationSetting(new LocationSetting("HUD Location", "Allows you to edit the location of the gemstone $/h", "gemstone_money_location", this, [10, 60, 1, 1])
+ .requires(this.gemstoneMoneyHud)
+ .editTempText("&6$/h&7> &f$12,345,678\n&6$ made&7> &f$123,456,789\n&6Time tracked&7> &f123m"))
+ this.gemstoneMoneyHudMoneyOnly = new ToggleSetting("Force npc price", "(Eg if u are ironman)", false, "gemstone_money_hud_npc", this).requires(this.gemstoneMoneyHud)
+ this.hudElements.push(this.gemstoneMoneyHudElement)
+
+ this.nextChEvent = new ToggleSetting("Show the current and next crystal hollows event", "(syncs the data between all users in ch)", true, "chevent_hud", this)
+ this.nextChEventElement = new HudTextElement()
+ .setToggleSetting(this.nextChEvent)
+ .setLocationSetting(new LocationSetting("HUD Location", "Allows you to edit the location of the hud element", "chevent_hud_location", this, [10, 70, 1, 1])
+ .requires(this.nextChEvent)
+ .editTempText("&6Event&7> &fGONE WITH THE WIND &7->&f 2X POWDER"))
+ this.hudElements.push(this.nextChEventElement)
+
+
+ this.metalDetectorSolver = new ToggleSetting("Metal detector solver", "", true, "metal_detector_solver", this).contributor("tenios", ["- idea that chests are in same locations every time", "- locations of chests", "- finding offset of divan mines"])
+ this.alertTools = new ToggleSetting("Alert when all 4 tools in inventory", "", true, "alert_tools", this)
+
+ this.seenBalDamages = []
+ this.balHP = 250
+ this.lastBalAlive = 0
+ this.balDespawnDebounce = 0
+
+ this.predictedChestLocations = []
+
+ this.totalCompact = 0
+ this.compactProgress = 0
+ this.compactItems = 0
+
+ this.armourstandClass = Java.type("net.minecraft.entity.item.EntityArmorStand").class
+
+ this.registerEvent("tick", this.tick)
+ this.registerEvent("itemTooltip", this.itemTooltipEvent).registeredWhen(() => this.showContainedGemstoneSlots.getValue() || this.showUnlockedGemstoneSlots.getValue())
+ this.registerEvent("renderWorld", this.renderWorld).registeredWhen(() => this.guessBalHp.getValue() || this.metalDetectorSolver.getValue())
+
+ this.registerChat("&r&c&o&r&6&lRARE DROP! &r&eA Bal Pet dropped!&r", () => {
+ if (this.balPetAlert.getValue()) {
+ World.playSound("random.orb", 1, 1)
+ Client.showTitle("§r§c§o§r§6§lRARE DROP! §r§eA Bal Pet dropped!§r", "", 20, 50, 20)
+ }
+ })
+ this.registerChat("&r&c&oThe bosses outer shell looks to be weakening!&r", () => {
+ this.balHP = 200
+ })
+ this.registerChat("&r&c&oHalf way there! The boss is starting to become weaker!&r", () => {
+ this.balHP = 125
+ })
+ this.registerChat("&r&c&oNearly there! The boss is shaking it can't last much longer!&r", () => {
+ this.balHP = 75
+ })
+ this.registerChat("&r&c&oThe boss looks weak and tired and retreats into the lava...&r", () => {
+ this.balHP = 0
+ })
+
+ let startingTime = -1
+ let money = 0
+ let gemstoneCosts = {}
+ let lastMined = 0
+ this.registerChat("&r&d&lPRISTINE! &r&fYou found &r${*} &r&aFlawed ${type} Gemstone &r&8x${num}&r&f!&r", (type, num, event) => {
+
+ let id = "FLAWED_" + type.toUpperCase() + "_GEM"
+ let number = parseInt(num)
+
+ lastMined = Date.now()
+
+ if (!this.gemstoneMoneyHud.getValue()) return
+
+ if (startingTime === 0) return
+ if (startingTime === -1) {
+ startingTime = 0
+ fetch("https://api.hypixel.net/skyblock/bazaar").json().then(data => {
+ startingTime = Date.now()
+
+ Object.keys(data.products).forEach(id => {
+ if (id.startsWith("FLAWED_")) {
+ gemstoneCosts[id] = Math.max(240, data.products[id].quick_status.sellPrice)
+ if (this.gemstoneMoneyHudMoneyOnly.getValue()) {
+ gemstoneCosts[id] = 240
+ }
+ // console.log(id + ": " + gemstoneCosts[id])
+ }
+ })
+ })
+ return
+ }
+
+ money += gemstoneCosts[id] * number
+
+ let moneyPerHour = Math.floor(money / ((Date.now() - startingTime) / (1000 * 60 * 60)))
+ let moneyMade = Math.floor(money)
+ let timeTracked = timeSince2(startingTime)
+
+ this.gemstoneMoneyHudElement.setText("&6$/h&7> &f$" + numberWithCommas(moneyPerHour) + "\n&6$ made&7> &f$" + numberWithCommas(moneyMade) + "\n&6Time tracked&7> &f" + timeTracked)
+ })
+ this.registerStep(false, 10, () => {
+ if (lastMined && Date.now() - lastMined > 2 * 60000) {
+ money = 0
+ startingTime = -1
+ lastMined = 0
+ this.gemstoneMoneyHudElement.setText("")
+ }
+
+ this.nextChEventElement.setText("&6Event&7> &f" + socketConnection.chEvent.join(" &7->&f "))
+ })
+
+ let lastWorldChange = 0
+
+ this.registerEvent("worldLoad", () => {
+ lastWorldChange = Date.now()
+ })
+
+ this.registerChat("&r&r&r ${spaces}&r&${color}&l${event} ENDED!&r", (spaces, color, event) => {
+ if (Date.now() - lastWorldChange < 5000) return
+ if (!this.isInCH()) return
+
+ socketConnection.sendCHEventData(event.trim(), false)
+ })
+ this.registerChat("&r&r&r ${spaces}&r&${color}&l${event} STARTED!&r", (spaces, color, event) => {
+ if (Date.now() - lastWorldChange < 5000) return
+ if (!this.isInCH()) return
+
+ socketConnection.sendCHEventData(event.trim(), true)
+ })
+
+ this.chestCoords = JSON.parse(FileLib.read("SoopyV2", "features/mining/coords.json"))
+
+ let lastLoc = [0, 0, 0]
+
+ this.baseCoordinates = undefined
+
+ this.lastSearchedForBase = 0
+
+ let ignoreLocation = undefined
+
+ this.registerEvent("worldLoad", () => {
+ lastLoc = [0, 0, 0]
+
+ this.baseCoordinates = undefined
+
+ this.lastSearchedForBase = 0
+
+ this.predictedChestLocations = []
+
+ ignoreLocation = undefined
+ })
+
+ let registerActionBar = this.registerCustom("actionbar", (dist) => {
+ let lapis = false
+ let diamond = false
+ let emerald = false
+ let gold = false
+ Player.getInventory().getItems().forEach(i => {
+ if (i && i.getName().includes("Scavenged Lapis")) {
+ lapis = true
+ }
+ if (i && i.getName().includes("Scavenged Diamond")) {
+ diamond = true
+ }
+ if (i && i.getName().includes("Scavenged Emerald")) {
+ emerald = true
+ }
+ if (i && i.getName().includes("Scavenged Golden")) {
+ gold = true
+ }
+ })
+
+ if (this.alertTools.getValue() && lapis && diamond && gold && emerald) Client.showTitle("§cALL TOOLS", "", 10, 40, 20)
+
+ if (!this.metalDetectorSolver.getValue()) return
+ let distance = parseFloat(dist)
+ if (!this.baseCoordinates) this.findBaseCoordinates();
+
+ if (lastLoc[0] !== Player.getX() || lastLoc[1] !== Player.getY() || lastLoc[2] !== Player.getZ()) {
+ lastLoc = [Player.getX(), Player.getY(), Player.getZ()]
+ return
+ }
+
+ this.predictedChestLocations = []
+
+ this.chestCoords.forEach((coordinates) => {
+ let currentDistance = Math.hypot(Player.getX() - (this.baseCoordinates[0] - coordinates[0]), Player.getY() - (this.baseCoordinates[1] - coordinates[1] + 1), Player.getZ() - (this.baseCoordinates[2] - coordinates[2]))
+
+ if (Math.round(currentDistance * 10) / 10 === distance) {
+
+ if ([this.baseCoordinates[0] - coordinates[0], this.baseCoordinates[1] - coordinates[1], this.baseCoordinates[2] - coordinates[2]].join(",") === ignoreLocation) {
+ ignoreLocation = undefined
+ return
+ }
+
+ if (this.predictedChestLocations.length === 0) {
+ World.playSound("note.pling", 100, 2)
+ }
+
+ this.predictedChestLocations.push([this.baseCoordinates[0] - coordinates[0], this.baseCoordinates[1] - coordinates[1], this.baseCoordinates[2] - coordinates[2]])
+ }
+ });
+ })
+ registerActionBar.trigger.setCriteria('TREASURE: ${rest}').setParameter('contains');
+
+ this.registerChat("&r&aYou found${*}with your &r&cMetal Detector&r&a!&r", () => {
+ if (this.predictedChestLocations[0]) ignoreLocation = this.predictedChestLocations[0].join(",")
+ this.predictedChestLocations = []
+ })
+ }
+
+ findBaseCoordinates() {
+ if (Date.now() - this.lastSearchedForBase < 15000) return;
+ let x = ~~Player.getX();
+ let y = ~~Player.getY();
+ let z = ~~Player.getZ();
+ for (let i = x - 50; i < x + 50; i++) {
+ for (let j = y + 30; j >= y - 30; j--) {
+ for (let k = z - 50; k < z + 50; k++) {
+ if (World.getBlockAt(i, j, k).getType().getID() === 156 && World.getBlockAt(i, j + 13, k).getType().getID() === 166) {
+ this.baseCoordinates = this.getBaseCoordinates(i, j + 13, k);
+ return;
+ }
+ }
+ }
+ }
+ this.lastSearchedForBase = Date.now();
+ }
+
+ getBaseCoordinates(x, y, z) {
+ let loop = true;
+ let posX = x;
+ let posY = y;
+ let posZ = z;
+ if (World.getBlockAt(x, y, z).getType().getID() !== 166) return [x, y, z];
+ while (loop) {
+ loop = false;
+ if (World.getBlockAt(posX + 1, posY, posZ).getType().getID() == 166) {
+ posX++;
+ loop = true;
+ }
+ if (World.getBlockAt(posX, posY - 1, posZ).getType().getID() == 166) {
+ posY--;
+ loop = true;
+ }
+ if (World.getBlockAt(posX, posY, posZ + 1).getType().getID() == 166) {
+ posZ++;
+ loop = true;
+ }
+ }
+ return [posX, posY, posZ];
+ }
+
+ itemTooltipEvent(lore, item, event) {
+ this.addLore(item)
+ }
+
+ /**
+ * @param {Item} item
+ */
+ addLore(item) {
+ if (!item) return
+ if (this.showUnlockedGemstoneSlots.getValue()) {
+ let gems = item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getCompoundTag("gems")
+ if (gems) {
+ let unlockedGems = gems.getTagMap().get("unlocked_slots")
+
+ if (unlockedGems) {
+
+ if (unlockedGems[m.tagCount]() === 0) {
+ utils.addLore(item, ChatLib.addColor("&d&lGemstones Unlocked: &f"), ChatLib.addColor("&cNone!"))
+ } else {
+ let gemstoneString = ""
+
+ for (let i = 0; i < unlockedGems[m.tagCount](); i++) {
+ let gem = String(unlockedGems[m.getStringTagAt](i)).split("_")
+
+ let name = stringUtils.firstLetterCapital(gem[0].toLowerCase())
+
+ gemstoneString += (gemstoneString === "" ? "" : "&7, &a") + name
+ }
+ utils.addLore(item, ChatLib.addColor("&d&lGemstones Unlocked: &f"), ChatLib.addColor("&a" + gemstoneString))
+ }
+ }
+ }
+ }
+ if (this.showContainedGemstoneSlots.getValue()) {
+ let gems = item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getCompoundTag("gems")
+ if (gems) {
+ let unlockedGems = gems.getTagMap()
+
+ let gemStr = ""
+
+ unlockedGems.keySet().forEach(gem => {
+ if (gem !== "unlocked_slots" && !gem.endsWith("_gem")) {
+ gem = gem.split("_")
+
+ let gemName = stringUtils.firstLetterCapital(gems.getString(gem.join("_") + "_gem").toLowerCase()) || stringUtils.firstLetterCapital(gem[0].toLowerCase())
+
+ let name = stringUtils.firstLetterCapital(gems.getString(gem.join("_")).toLowerCase()) + " " + gemName
+
+
+ gemStr += (gemStr === "" ? "" : "&7, &a") + name
+ }
+ });
+
+ if (gemStr !== "") {
+ utils.addLore(item, ChatLib.addColor("&d&lGemstones: &f"), ChatLib.addColor("&a" + gemStr))
+ }
+ }
+ }
+ }
+
+ renderWorld() {
+ if (this.guessBalHp.getValue()) {
+ if (this.balEntity) Tessellator.drawString(this.balHP + "/250", this.balEntity.getX(), this.balEntity.getY() + 12, this.balEntity.getZ())
+ }
+ if (!this.metalDetectorSolver.getValue()) return
+ this.predictedChestLocations.forEach(loc => {
+ drawCoolWaypoint(loc[0], loc[1], loc[2], 0, 255, 0, { name: "TREASURE", phase: true })
+ })
+ }
+
+ tick() {
+ let oldCompactItems = this.compactItems
+ let oldTotalCompact = this.totalCompact
+ this.totalCompact = 0
+ this.compactItems = 0
+ let slots = [0, 1, 2, 3, 4, 5, 6, 7, 8]
+
+ slots.forEach(a => {
+ item = Player.getInventory().getStackInSlot(a)
+ if (!item) return
+ if (item.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getInteger("compact_blocks")) {
+ this.compactItems++
+ this.totalCompact += item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getInteger("compact_blocks")
+ }
+ })
+
+ if (oldCompactItems === this.compactItems) {
+ this.compactProgress += this.totalCompact - oldTotalCompact
+ }
+ if (this.compactItems === 0) {
+ this.compactProgress = 0
+ }
+
+ if (this.compactProgress === 0 && this.compactProgressHudOnlyWhenMoreThan0.getValue()) {
+ this.compactHudElement.setText("")
+ } else {
+ this.compactHudElement.setText("&6Compact Session&7> &f" + numberWithCommas(this.compactProgress))
+ }
+
+
+ if (!this.FeatureManager.features["dataLoader"]) return
+ if (this.guessBalHp.getValue() || this.balRespawnHud.getValue()) {
+ if (this.FeatureManager.features["dataLoader"].class.area === "Crystal Hollows" && this.FeatureManager.features["dataLoader"].class.areaFine === "Khazad-dm") {
+
+ this.balEntity = undefined
+ World.getAllEntities().filter(a => a.getName() === "Magma Cube").filter(a => a.getEntity()[m.getSlimeSize]() > 10).forEach((bal) => {
+ //Bal found
+ this.balEntity = bal
+ })
+ if (this.balEntity) {
+ this.balDespawnDebounce = 0
+ if (this.lastBalAlive !== 0) {
+ this.lastBalAlive = 0
+ }
+ World.getAllEntitiesOfType(this.armourstandClass).forEach(e => {
+ if (Math.abs(e.getX() - this.balEntity.getX()) <= 5 && Math.abs(e.getZ() - this.balEntity.getZ()) <= 5 && Math.abs(e.getY() - (this.balEntity.getY() + 12)) <= 5) {
+ if (!this.seenBalDamages.includes(e.getUUID())) {
+ this.balHP--
+ this.seenBalDamages.push(e.getUUID())
+ }
+ }
+ })
+ } else {
+ this.balDespawnDebounce++
+ if (this.balDespawnDebounce > 10) {
+ this.seenBalDamages = []
+ this.balHP = 250
+ if (this.lastBalAlive === 0) this.lastBalAlive = Date.now()
+ }
+ }
+ }
+ }
+
+ if (this.balRespawnHud.getValue() && this.FeatureManager.features["dataLoader"].class.area === "Crystal Hollows" && this.FeatureManager.features["dataLoader"].class.areaFine === "Khazad-dm") {
+ if (this.balEntity) {
+ this.balHudElement.setText("&6Bal&7> &f" + this.balHP + "/250")
+ } else {
+ this.balHudElement.setText("&6Bal&7> &f" + Math.max(0, Math.floor((290000 - (Date.now() - this.lastBalAlive)) / 1000)) + "s")
+ }
+ } else {
+ this.balHudElement.setText("")
+ }
+ }
+
+ initVariables() {
+ this.hudElements = undefined
+ this.guessBalHp = undefined
+ this.balRespawnHud = undefined
+ this.balHudElement = undefined
+ this.balEntity = undefined
+ this.balDespawnDebounce = undefined
+ this.lastBalAlive = undefined
+ this.balHP = undefined
+ this.seenBalDamages = undefined
+ this.armourstandClass = undefined
+ this.balPetAlert = undefined
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.initVariables()
+ }
+}
+
+module.exports = {
+ class: new Mining()
+} \ No newline at end of file
diff --git a/src/features/mining/metadata.json b/src/features/mining/metadata.json
new file mode 100644
index 0000000..ce9ed40
--- /dev/null
+++ b/src/features/mining/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Mining",
+ "description": "A bunch of features to assist with mining",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/nether/index.js b/src/features/nether/index.js
new file mode 100644
index 0000000..4b14c48
--- /dev/null
+++ b/src/features/nether/index.js
@@ -0,0 +1,406 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { f, m } from "../../../mappings/mappings";
+import Feature from "../../featureClass/class";
+import socketConnection from "../../socketConnection";
+import { drawBoxAtBlock, drawBoxAtBlockNotVisThruWalls, drawBoxAtEntity, drawCoolWaypoint, drawLine, drawLineWithDepth, renderBeaconBeam } from "../../utils/renderUtils";
+import ToggleSetting from "../settings/settingThings/toggle";
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+const MCBlock = Java.type("net.minecraft.block.Block");
+const ArmorStand = Java.type("net.minecraft.entity.item.EntityArmorStand")
+const MCItem = Java.type("net.minecraft.item.Item");
+const EntitySkeleton = Java.type("net.minecraft.entity.monster.EntitySkeleton")
+
+let locationData = {
+ barbarian: {
+ "Ⓐ": [16, 148, -929],
+ "Ⓑ": [-37, 122, -1020],
+ "Ⓒ": [-30, 137, -888],
+ "Ⓓ": [-6, 156, -881],
+ },
+ mage: {
+ "Ⓐ": [-664, 124, -981],
+ "Ⓑ": [-635, 160, -1056],
+ "Ⓒ": [-726, 123, -997],
+ "Ⓓ": [-685, 124, -1049],
+ }
+}
+
+let disciplineColors = {
+ "Wood": [196, 100, 0],
+ "Iron": [194, 194, 194],
+ "Gold": [235, 182, 0],
+ "Diamond": [0, 198, 229]
+}
+
+class Nether extends Feature {
+ constructor() {
+ super();
+ }
+
+ isInDojo() {
+ if (!this.FeatureManager || !this.FeatureManager.features["dataLoader"]) return false
+ return this.FeatureManager.features["dataLoader"].class.areaFine === "Dojo" || this.FeatureManager.features["dataLoader"].class.areaFine === "Dojo Arena"
+ }
+
+ isInNether() {
+ if (!this.FeatureManager || !this.FeatureManager.features["dataLoader"]) return false
+ return this.FeatureManager.features["dataLoader"].class.area === "Crimson Isle"
+ }
+
+ onEnable() {
+ this.initVariables();
+
+ this.masteryTimer = new ToggleSetting("Mastery Timer", "Countdown untill a block will turn red", true, "nether_mastery_timer", this)
+ this.speedNextBlock = new ToggleSetting("Show next block to stand on for dojo swiftness", "", true, "dojo_swiftness", this)
+ this.tenacityLine = new ToggleSetting("Show line for fireball in dojo tenacity", "This may help you to dodge the fireballs", false, "dojo_tanacity", this)
+ this.disciplineOverlay = new ToggleSetting("Show overlay for zombies in dojo discipline", "", true, "dojo_discipline", this).contributor("Empa")
+ this.controlHelper = new ToggleSetting("Shows where you actually have to look for", "control dojo task (accounts for ping)", true, "control_helper", this)
+ this.hostageWaypoints = new ToggleSetting("Show hostage waypoints", "Waypoint for location of hostage in rescue missions", true, "hostage_waypoint", this)
+ this.slugfishTimer = new ToggleSetting("Show timer over rod", "This may help with fishing slugfish", false, "slugfish_timer", this)
+
+ this.minibossNametag = new ToggleSetting("Nether Miniboss Nametag Hud", "renders the HP of minibosses on screen (exclude Magma Boss)", false, "nether_mini_nametag_hud", this);
+ this.minibossNametagElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.minibossNametag)
+ .setLocationSetting(new LocationSetting("Nether Miniboss Nametag Hud location", "allows you to change the location of the hud", "nether_mini_nametag_hud_location", this, [10, 100, 1, 1]).requires(this.minibossNametag).editTempText("&5&lMage Outlaw &r&a70M&c❤"));
+ this.hudElements.push(this.minibossNametagElement);
+
+ this.registerStep(true, 5, this.minibossHPHud)
+
+ this.todoE = []
+ this.todoE2 = []
+ this.blocks = []
+
+ this.todoF = []
+ this.todoF2 = []
+ this.todoM = []
+ this.todoM2 = []
+ this.disciplineZombies = {
+ "Wood": [],
+ "Iron": [],
+ "Gold": [],
+ "Diamond": []
+ }
+ this.inDiscipline = false
+
+ this.dojoFireBalls = []
+ this.inSwiftness = false
+ this.rescueMissionDifficulty = undefined
+ this.rescueMissionType = undefined
+ this.lastBlock = undefined
+ this.hookThrown = 0
+ this.controlSkeleton = undefined
+ this.controlLocLast = undefined
+ this.controlLoc = undefined
+ this.registerChat(" Test of Control OBJECTIVES", () => {
+ this.controlSkeleton = undefined
+ this.controlLocLast = undefined
+ this.controlLoc = undefined
+ })
+
+ let packetRecieved = this.registerCustom("packetReceived", this.packetReceived).registeredWhen(() => this.isInDojo())
+
+ try {
+ packetRecieved.trigger.setPacketClasses([net.minecraft.network.play.server.S23PacketBlockChange, net.minecraft.network.play.server.S22PacketMultiBlockChange])
+ } catch (e) { }//older ct version
+
+ this.registerStep(true, 1, this.step1S).registeredWhen(() => this.isInNether())
+ this.registerEvent("renderWorld", this.renderWorld).registeredWhen(() => this.isInNether())
+
+ this.registerForge(net.minecraftforge.event.entity.EntityJoinWorldEvent, this.entityJoinWorldEvent).registeredWhen(() => this.isInDojo() || (this.isInNether() && this.minibossNametag.getValue()));
+ this.registerEvent("tick", this.tick).registeredWhen(() => this.isInNether())
+ this.registerChat("&r&r&r &r&aTest of Swiftness &r&e&lOBJECTIVES&r", () => {
+ if (this.speedNextBlock.getValue()) {
+ this.inSwiftness = true
+ this.lastBlock = [Math.floor(Player.getX()), Math.floor(Player.getY()) - 1, Math.floor(Player.getZ())]
+ }
+ })
+
+ this.registerChat("&r&r&r &r&cTest of Discipline &r&e&lOBJECTIVES&r", () => {
+ if (this.disciplineOverlay.getValue()) {
+ this.inDiscipline = true
+ this.todoF = []
+ this.todoF2 = []
+ }
+ })
+
+ this.registerChat("&r&r&r ${*}&r&6Your Rank: &r${*}&r", () => {
+ this.inSwiftness = false
+ this.lastBlock = undefined
+ this.inDiscipline = false
+
+ this.controlLocLast = undefined
+ this.controlLoc = undefined
+ this.controlSkeleton = undefined
+ })
+ this.registerEvent("worldLoad", () => {
+ this.inSwiftness = false
+ this.lastBlock = undefined
+ this.inDiscipline = false
+
+ this.controlLocLast = undefined
+ this.controlLoc = undefined
+ this.controlSkeleton = undefined
+ this.miniboss = undefined
+ })
+
+ this.registerChat("You completed your rescue quest! Visit the Town Board to claim the rewards,", () => {
+ this.rescueMissionDifficulty = this.rescueMissionType = undefined
+ })
+ this.registerEvent("worldLoad", () => {
+ this.rescueMissionDifficulty = this.rescueMissionType = undefined
+ })
+ }
+
+ tick() {
+
+ let fishHook = Player.getPlayer()[f.fishEntity]
+
+ if (fishHook) {
+ if (!this.hookThrown) this.hookThrown = Date.now()
+ } else {
+ this.hookThrown = 0
+ }
+
+ if (Player.getContainer().getName() === "Rescue" && Player.getContainer().getStackInSlot(22)) {
+ let difficulty = ChatLib.removeFormatting(Player.getContainer().getStackInSlot(22).getName()).trim()[0]
+
+ this.rescueMissionDifficulty = difficulty
+ TabList.getNames().forEach(n => {
+ if (n.toLowerCase().includes("barbarian rep")) this.rescueMissionType = "barbarian"// : "mage"
+ if (n.toLowerCase().includes("mage rep")) this.rescueMissionType = "mage"// : "mage"
+ })
+ }
+
+
+ this.todoF2.forEach(e => {
+ let name = ChatLib.removeFormatting(e.getName())
+ if (!name) return
+ if (!disciplineColors[name]) return
+
+ this.disciplineZombies[name].push(e)
+ })
+
+ this.todoF2 = this.todoF
+ this.todoF = []
+
+ this.todoE2.forEach(e => {
+ let item = e[m.getHeldItem]()
+ if (!item) return
+ if (MCItem[m.getIdFromItem](item[m.getItem.ItemStack]()) !== 173) return
+
+ this.dojoFireBalls.push(e)
+ })
+
+ this.todoE2 = this.todoE
+ this.todoE = []
+
+
+ if (this.controlHelper.getValue() && this.controlSkeleton) {
+ let ping = this.FeatureManager.features["dataLoader"].class.getPing() / 1000
+
+ let e = this.controlSkeleton
+ let x = e.getX() + (e.getX() - e.getLastX()) * (20 * ping)
+ let y = e.getY() + (e.getY() - e.getLastY()) * (20 * ping)
+ let z = e.getZ() + (e.getZ() - e.getLastZ()) * (20 * ping)
+ if (x === e.getX() && y === e.getY() && z === e.getZ()) return
+
+ while (World.getBlockAt(x, y, z).getType().getID() !== 0) { y += 0.2 }
+
+ if (this.controlLoc) this.controlLocLast = [...this.controlLoc]
+ this.controlLoc = [x - 0.5, y, z - 0.5]
+ } else {
+ this.controlLocLast = undefined
+ this.controlLoc = undefined
+ }
+
+ this.todoM2.forEach(e => {
+ let name = e[m.getCustomNameTag]()
+ if (name) {
+ if (name.includes("Ashfang") || name.includes("Barbarian Duke X") || name.includes("Bladesoul") || name.includes("Mage Outlaw")) {
+ this.miniboss = new Entity(e)
+ }
+ }
+ })
+
+ this.todoM2 = this.todoM
+ this.todoM = []
+ }
+
+ entityJoinWorldEvent(event) {
+ if (this.tenacityLine.getValue() && event.entity instanceof ArmorStand) this.todoE.push(event.entity)
+ if (this.disciplineOverlay.getValue() && this.inDiscipline && event.entity instanceof ArmorStand) this.todoF.push(new Entity(event.entity))
+
+ if (event.entity instanceof EntitySkeleton && !this.controlSkeleton) this.controlSkeleton = new Entity(event.entity)
+ if (this.minibossNametag.getValue() && event.entity instanceof ArmorStand) {
+ this.todoM.push(event.entity)
+ }
+ }
+
+ packetReceived(packet, event) {
+ if (!this.masteryTimer.getValue()) return
+ let packetType = new String(packet.class.getSimpleName()).valueOf()
+ if (packetType === "S23PacketBlockChange") {
+ let position = new BlockPos(packet[m.getBlockPosition.S23PacketBlockChange]())
+ let blockState = this.getBlockIdFromState(packet[m.getBlockState.S23PacketBlockChange]())
+ let oldBlockState = this.getBlockIdFromState(World.getBlockStateAt(position))
+ if (oldBlockState === 20515 && blockState === 16419) {
+ if (Math.abs(Player.getX() - position.getX()) <= 20 && Math.abs(Player.getY() - position.getY()) <= 20 && Math.abs(Player.getZ() - position.getZ()) <= 20) {
+ this.blocks.push({ loc: position, time: Date.now() + 3000 })
+ }
+ }
+ if (blockState === 57379) {
+ this.blocks = this.blocks.filter(b => {
+ if (b.loc.x === position.x && b.loc.y === position.y && b.loc.z === position.z) {
+ return false
+ }
+ return true
+ })
+ //air=0
+ //green=20515
+ //yellow=16419
+ //red=57379
+ }
+ if (oldBlockState === 0 && blockState === 20515 && this.inSwiftness) {
+ this.lastBlock = [position.getX(), position.getY(), position.getZ()]
+ }
+ }
+ if (packetType === "S22PacketMultiBlockChange") {
+ packet[m.getChangedBlocks]().forEach(b => {
+ let position = new BlockPos(b[m.getPos.S22PacketMultiBlockChange$BlockUpdateData]())
+ let blockState = this.getBlockIdFromState(b[m.getBlockState.S22PacketMultiBlockChange$BlockUpdateData]())
+ let oldBlockState = this.getBlockIdFromState(World.getBlockStateAt(position))
+ if (oldBlockState === 0 && blockState === 20515 && this.inSwiftness) {
+ this.lastBlock = [position.getX(), position.getY(), position.getZ()]
+ }
+ if (oldBlockState === 20515 && blockState === 16419) {
+ if (Math.abs(Player.getX() - position.getX()) <= 20 && Math.abs(Player.getY() - position.getY()) <= 20 && Math.abs(Player.getZ() - position.getZ()) <= 20) {
+ this.blocks.push({ loc: position, time: Date.now() + 3000 })
+ }
+ }
+ if (blockState === 57379) {
+ this.blocks = this.blocks.filter(b => {
+ if (b.loc.x === position.x && b.loc.y === position.y && b.loc.z === position.z) {
+ return false
+ }
+ return true
+ })
+ //air=0
+ //green=20515
+ //yellow=16419
+ //red=57379
+ }
+ })
+ }
+ }
+
+ renderWorld(ticks) {
+ if (this.masteryTimer.getValue() && this.blocks) {
+ this.blocks.forEach((data, i) => {
+ Tessellator.drawString((i === 0 ? "§1" : "§0") + Math.max(0, (data.time - Date.now()) / 1000).toFixed(1) + "s", data.loc.getX() + 0.5, data.loc.getY() + 0.5, data.loc.getZ() + 0.5, 0, false, 0.05, false)
+ })
+ if (this.blocks.length >= 2) drawLine(this.blocks[0].loc.getX() + 0.5, this.blocks[0].loc.getY(), this.blocks[0].loc.getZ() + 0.5, this.blocks[1].loc.getX() + 0.5, this.blocks[1].loc.getY(), this.blocks[1].loc.getZ() + 0.5, 255, 0, 0)
+ }
+
+ if (this.lastBlock && this.inSwiftness) drawBoxAtBlock(this.lastBlock[0], this.lastBlock[1], this.lastBlock[2], 0, 255, 0, 1, 1)
+
+ if (this.tenacityLine.getValue()) this.dojoFireBalls.forEach(e => {
+ let offset = [e[f.width.Entity] / 2, e[f.height.Entity] / 2, e[f.width.Entity] / 2]
+ let entitylocation = [e[f.posX.Entity], e[f.posY.Entity], e[f.posZ.Entity]]
+ let lastLocation = [e[f.prevPosX], e[f.prevPosY], e[f.prevPosZ]]
+ let change = [entitylocation[0] - lastLocation[0], entitylocation[1] - lastLocation[1], entitylocation[2] - lastLocation[2]]
+ drawLineWithDepth(entitylocation[0] + change[0] * 100 + offset[0], entitylocation[1] + change[1] * 100 + offset[1], entitylocation[2] + change[2] * 100 + offset[2], entitylocation[0] + offset[0], entitylocation[1] + offset[1], entitylocation[2] + offset[2], 255, 0, 0, 2)
+ })
+
+ if (this.disciplineOverlay.getValue() && Player.getHeldItem() && this.disciplineZombies[ChatLib.removeFormatting(Player.getHeldItem().getName().split(" ")[0].replace("en", ""))]) this.disciplineZombies[ChatLib.removeFormatting(Player.getHeldItem().getName().split(" ")[0].replace("en", ""))].forEach(e => {
+ let color = disciplineColors[ChatLib.removeFormatting(e.getName())]
+
+ drawBoxAtEntity(e, color[0] / 255, color[1] / 255, color[2] / 255, 0.7, -2, ticks, 4, false);
+ })
+
+ if (this.rescueMissionDifficulty && this.rescueMissionType && this.hostageWaypoints.getValue()) {
+ let location = locationData[this.rescueMissionType][this.rescueMissionDifficulty]
+ drawCoolWaypoint(location[0], location[1], location[2], 255, 0, 0, { name: "Hostage" })
+ }
+
+ if (this.slugfishTimer.getValue()) {
+ let hook = Player.getPlayer()[f.fishEntity]
+ if (hook && this.hookThrown) {
+ let x = hook[f.posX.Entity]
+ let y = hook[f.posY.Entity]
+ let z = hook[f.posZ.Entity]
+
+ Tessellator.drawString(((Date.now() - this.hookThrown) / 1000).toFixed(1) + "s", x, y + 0.5, z, Renderer.color(0, 255, 50), false, 0.025, false)
+ }
+ }
+
+ if (this.controlLoc && this.controlLocLast) {
+ drawBoxAtBlockNotVisThruWalls(this.controlLoc[0] * ticks + this.controlLocLast[0] * (1 - ticks), this.controlLoc[1] * ticks + this.controlLocLast[1] * (1 - ticks), this.controlLoc[2] * ticks + this.controlLocLast[2] * (1 - ticks), 255, 0, 0, 1, 2)
+ }
+ }
+
+ step1S() {
+ if (this.blocks) this.blocks = this.blocks.filter(state => Date.now() < state.time)
+ if (this.disciplineZombies) Object.keys(this.disciplineZombies).forEach(k => {
+ this.disciplineZombies[k] = this.disciplineZombies[k].filter(e => !e.getEntity()[f.isDead])
+ })
+ if (this.dojoFireBalls) this.dojoFireBalls = this.dojoFireBalls.filter(e => !e[f.isDead])
+ }
+
+ getBlockIdFromState(state) {
+ return MCBlock[m.getStateId](state)
+ }
+
+ minibossHPHud() {
+ if (this.miniboss && this.miniboss.getEntity()[f.isDead]) this.miniboss = undefined
+ if (!this.minibossNametag.getValue() || !this.miniboss) {
+ this.minibossNametagElement.setText("")
+ return
+ }
+ let name = this.miniboss.getName()
+ let nameRemoveFormat = name.removeFormatting()
+ let mobName = ""
+ if (nameRemoveFormat.includes("Ashfang")) mobName = "&dAshfang"
+ if (nameRemoveFormat.includes("Bladesoul")) mobName = "&dBladesoul"
+ if (nameRemoveFormat.includes("Barbarian Duke X")) mobName = "&dBarbarian Duke X"
+ if (nameRemoveFormat.includes("Mage Outlaw")) mobName = "&dMage Outlaw"
+ if (!mobName) {
+ this.minibossNametagElement.setText("")
+ return
+ }
+ let indexOfHP = {
+ "&dAshfang": 3,
+ "&dBladesoul": 3,
+ "&dBarbarian Duke X": 5,
+ "&dMage Outlaw": 4
+ }
+ let HP = `&l${mobName} &r${name.split(" ")[indexOfHP[mobName]].split("/")[0]}&c❤`
+ this.minibossNametagElement.setText(HP)
+ }
+
+ initVariables() {
+ this.hudElements = [];
+ this.miniboss = undefined
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.initVariables();
+ }
+}
+
+let nether = new Nether()
+module.exports = {
+ class: nether,
+};
+
+function getField(e, field) {
+
+ let field2 = e.class.getDeclaredField(field);
+
+ field2.setAccessible(true)
+
+ return field2.get(e)
+}
diff --git a/src/features/nether/metadata.json b/src/features/nether/metadata.json
new file mode 100644
index 0000000..2a7c145
--- /dev/null
+++ b/src/features/nether/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Nether",
+ "description": "uga booga nether",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/networthGUI/index.js b/src/features/networthGUI/index.js
new file mode 100644
index 0000000..b1ff5c2
--- /dev/null
+++ b/src/features/networthGUI/index.js
@@ -0,0 +1,239 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import BoxWithLoading from "../../../guimanager/GuiElement/BoxWithLoading";
+import BoxWithTextAndDescription from "../../../guimanager/GuiElement/BoxWithTextAndDescription";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import TextBox from "../../../guimanager/GuiElement/TextBox";
+import SoopyKeyPressEvent from "../../../guimanager/EventListener/SoopyKeyPressEvent";
+import { numberWithCommas } from "../../utils/numberUtils";
+import { firstLetterWordCapital } from "../../utils/stringUtils";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import { fetch } from "../../utils/networkUtils";
+import Dropdown from "../../../guimanager/GuiElement/Dropdown";
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent";
+
+class NetworthGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new NetworthPage()
+
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class NetworthPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Networth"
+
+ this.pages = [this.newPage()]
+
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Networth").setMaxTextScale(3).setLocation(0.1, 0.05, 0.6, 0.1))
+ this.pages[0].addChild(new SoopyTextElement().setText("§0(This is in beta and may be inaccurate)").setMaxTextScale(3).setLocation(0.1, 0.15, 0.8, 0.075))
+ let button = new ButtonWithArrow().setText("§0Open in browser").setLocation(0.7, 0.05, 0.2, 0.1)
+ this.pages[0].addChild(button)
+
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(
+ new java.net.URI("https://soopy.dev/networth")
+ );
+ }))
+
+ this.nameInput = new TextBox().setPlaceholder("Click to search").setLocation(0.1, 0.225, 0.8, 0.1)
+ this.pages[0].addChild(this.nameInput)
+
+ this.nameInput.addEvent(new SoopyKeyPressEvent().setHandler((key, keyId) => {
+ if (this.nameInput.text.selected && keyId === 28) {
+ this.playerLoad = this.nameInput.text.text
+ this.nameInput.setText("")
+ this.updateData(this.playerLoad)
+ }
+ }))
+
+ this.statArea = new SoopyGuiElement().setLocation(0.05, 0.325, 0.9, 0.675).setScrollable(true)
+ this.pages[0].addChild(this.statArea)
+
+ this.loadingElm = new BoxWithLoading().setLocation(0.35, 0.4, 0.3, 0.2)
+ this.errorElm = new BoxWithTextAndDescription().setLocation(0.2, 0.3, 0.6, 0.4).setText("Error!").setDesc("An error occured while loading the data!")
+ this.statArea.addChild(this.loadingElm)
+
+ this.playerLoad = undefined
+
+ this.sidebarElement = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+
+ this.sidebarUsernameSearch = new TextBox().setLocation(0.15, 0.05, 0.7, 0.1).setPlaceholder("Click to search")
+ this.sidebarElement.addChild(this.sidebarUsernameSearch)
+
+ this.sidebarUsernameSearch.addEvent(new SoopyKeyPressEvent().setHandler((key, keyId) => {
+ if (this.sidebarUsernameSearch.text.selected && keyId === 28) {
+ let search = this.sidebarUsernameSearch.text.text
+ this.sidebarUsernameSearch.setText("")
+ this.sidebarSearch(search)
+ }
+ }))
+
+ this.lbBackButton = new ButtonWithArrow().setLocation(0.05, 0.05, 0.1, 0.1).setText("§0Back").setDirectionRight(false)
+ this.lbNextButton = new ButtonWithArrow().setLocation(0.85, 0.05, 0.1, 0.1).setText("§0Next")
+ this.sidebarElement.addChild(this.lbBackButton)
+ this.sidebarElement.addChild(this.lbNextButton)
+ this.lbBackButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ if (this.currentLbPage > 0) this.goToLeaderboardPage(this.currentLbPage - 1)
+ }))
+ this.lbNextButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.goToLeaderboardPage(this.currentLbPage + 1)
+ }))
+
+ this.leaderboardArea = new SoopyGuiElement().setLocation(0.05, 0.15, 0.9, 0.85).setScrollable(true)
+ this.sidebarElement.addChild(this.leaderboardArea)
+
+ this.currentLbPage = 0
+
+ this.finaliseLoading()
+ }
+
+ async updateData(player, profIn) {
+ this.playerLoad = player
+
+ this.statArea._scrollAmount = 0
+ this.statArea.location.scroll.y.set(0, 100)
+
+ this.statArea.clearChildren()
+ this.statArea.addChild(this.loadingElm)
+
+ let playerData = await fetch("http://soopy.dev/api/v2/player/" + player).json()
+
+ if (player !== this.playerLoad) return
+
+ if (!playerData.success) {
+ this.statArea.clearChildren()
+ this.statArea.addChild(this.errorElm)
+ this.errorElm.setText("§0" + playerData.error.name)
+ this.errorElm.setDesc("§0" + playerData.error.description)
+ return
+ }
+
+ let skyblockData = await fetch("http://soopy.dev/api/v2/player_skyblock/" + playerData.data.uuid).json()
+
+ if (player !== this.playerLoad) return
+
+ this.statArea.clearChildren()
+
+ if (!skyblockData.success) {
+ this.statArea.addChild(this.errorElm)
+ this.errorElm.setText("§0" + skyblockData.error.name)
+ this.errorElm.setDesc("§0" + skyblockData.error.description)
+ return
+ }
+
+ let selectedProf = profIn || skyblockData.data.stats.bestProfileId
+
+ let nwData = skyblockData.data.profiles[selectedProf].members[playerData.data.uuid].soopyNetworth
+ let nameElm = new SoopyTextElement().setText(playerData.data.stats.nameWithPrefix.replace(/§f/g, "§7")).setMaxTextScale(2).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.statArea.addChild(nameElm)
+
+ let profOptions = {}
+ Object.keys(skyblockData.data.profiles).forEach(p => {
+ profOptions[p] = skyblockData.data.profiles[p].stats.cute_name
+ })
+
+ let profileSelect = new Dropdown().setOptions(profOptions).setSelectedOption(selectedProf).setLocation(0.1, 0.15, 0.3, 0.1).addEvent(new SoopyContentChangeEvent().setHandler(newval => {
+ this.updateData(player, newval)
+ }))
+ this.statArea.addChild(profileSelect)
+ this.statArea.addChild(new SoopyTextElement().setText("§0Networth: §2$" + numberWithCommas(Math.round(nwData.networth)).replace(/,/g, "§7,§2")).setMaxTextScale(1.5).setLocation(0.45, 0.15, 0.4, 0.1))
+ this.statArea.addChild(new SoopyTextElement().setText("§0Purse: §2$" + numberWithCommas(Math.round(nwData.purse)).replace(/,/g, "§7,§2") + "§0 | Bank: §2$" + numberWithCommas(Math.round(nwData.bank)).replace(/,/g, "§7,§2") + "§0 | Sack: §2$" + numberWithCommas(Math.round(nwData.sack)).replace(/,/g, "§7,§2")).setMaxTextScale(1.5).setLocation(0.1, 0.25, 0.8, 0.1))
+
+ Object.keys(nwData.categories).sort((a, b) => nwData.categories[b].total - nwData.categories[a].total).forEach((name, i) => {
+ let renderName = firstLetterWordCapital(name.replace(/_/g, " "))
+
+ let data = nwData.categories[name]
+
+ let box = new SoopyBoxElement().setLocation(i % 2 === 0 ? 0 : 0.525, 0.45 + Math.floor(i / 2) * 0.35, 0.475, 0.25)
+
+ box.addChild(new SoopyMarkdownElement().setLocation(0, 0, 1, 1).setText(data.items.filter(i => i.name).splice(0, 5).map(a => {
+ let name = (a.name.startsWith("§f") || a.name.startsWith("§7[Lvl ")) ? a.name.replace("§f", "§7") : a.name
+ return "§0" + name + "§0: §2$" + numberWithCommas(Math.round(a.p)).replace(/,/g, "§7,§2")
+ }).join("\n")))
+
+ let boxName = new SoopyTextElement().setLocation(i % 2 === 0 ? 0 : 0.525, 0.4 + Math.floor(i / 2) * 0.35, 0.475, 0.05).setText("§0" + renderName + "§0: §2$" + numberWithCommas(Math.round(data.total)).replace(/,/g, "§7,§2"))
+
+ this.statArea.addChild(box)
+ this.statArea.addChild(boxName)
+ })
+
+ if (selectedProf === skyblockData.data.stats.bestProfileId) {
+ let leaderboardData = await fetch("http://soopy.dev/api/v2/leaderboard/networth/user/" + playerData.data.uuid).json()
+ if (player !== this.playerLoad) return
+
+ if (leaderboardData.success) nameElm.setText("§0#" + numberWithCommas(leaderboardData.data.data.position + 1) + " " + playerData.data.stats.nameWithPrefix.replace(/§f/g, "§7"))
+
+ }
+ }
+
+ onOpen() {
+ this.playerLoad = Player.getName()
+ this.updateData(Player.getName())
+
+ this.goToLeaderboardPage(0)
+
+ this.openSidebarPage(this.sidebarElement)
+ }
+
+ async sidebarSearch(user) {
+ let data = await fetch("http://soopy.dev/api/v2/leaderboard/networth/user/" + user).json()
+ if (!data.success) {
+ return
+ }
+
+ let position = data.data.data.position
+
+ this.goToLeaderboardPage(Math.floor(position / 100), false)
+
+ this.leaderboardArea._scrollAmount = -((position % 100) * 0.1 - 0.45) * this.leaderboardArea.location.getHeightExact()
+ this.leaderboardArea.location.scroll.y.set(-((position % 100) * 0.1 - 0.45) * this.leaderboardArea.location.getHeightExact(), 100)
+ }
+
+ async goToLeaderboardPage(page, scroll = true) {
+ this.currentLbPage = page
+
+ if (scroll) this.leaderboardArea._scrollAmount = 0
+ if (scroll) this.leaderboardArea.location.scroll.y.set(0, 100)
+
+ let data = await fetch("http://soopy.dev/api/v2/leaderboard/networth/" + page).json()
+ this.leaderboardArea.clearChildren()
+ data.data.data.forEach((user, i) => {
+ this.leaderboardArea.addChild(
+ new SoopyTextElement().setText("§0#" + numberWithCommas(i + 1 + page * 100) + ": " + user.username).setMaxTextScale(1.5).setLocation(0.05, i * 0.05, 0.5, 0.05).setLore(["Click to show detailed stats"]).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.updateData(user.uuid)
+ }))
+ )
+ this.leaderboardArea.addChild(
+ new SoopyTextElement().setText("§2$" + numberWithCommas(Math.round(user.networth)).replace(/,/g, "§7,§2")).setMaxTextScale(1.5).setLocation(0.6, i * 0.05, 0.35, 0.05)
+ )
+ })
+ }
+}
+
+module.exports = {
+ class: new NetworthGui()
+} \ No newline at end of file
diff --git a/src/features/networthGUI/metadata.json b/src/features/networthGUI/metadata.json
new file mode 100644
index 0000000..26e6f60
--- /dev/null
+++ b/src/features/networthGUI/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Networth gui",
+ "description": "Gui for Networth and Networth leaderboard",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/performance/hiddenRequirement.js b/src/features/performance/hiddenRequirement.js
new file mode 100644
index 0000000..bc74bfc
--- /dev/null
+++ b/src/features/performance/hiddenRequirement.js
@@ -0,0 +1,7 @@
+let allowedUUIDS = [
+ "dc8c39647b294e03ae9ed13ebd65dd29"
+]
+
+module.exports = {hidden: function(featureManager){
+ return !allowedUUIDS.includes(Player.getUUID().toString().replace(/-/g, ""))
+}} \ No newline at end of file
diff --git a/src/features/performance/index.js b/src/features/performance/index.js
new file mode 100644
index 0000000..634b994
--- /dev/null
+++ b/src/features/performance/index.js
@@ -0,0 +1,128 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import SettingBase from "../settings/settingThings/settingBase";
+import ToggleSetting from "../settings/settingThings/toggle";
+
+class Performance extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ return;
+ new SettingBase("NOTE: If you dont use any of the features, disable this", "Having performance enabled will decrease performance if no features are used\n(this is due to it using the render entity event)", true, "hide_performance_description", this)
+
+ this.armourStandCapSetting = new ToggleSetting("Armorstand render cap", "Limits the max number of armor stands rendered to 50\n(50 closest to player)", true, "armorstand_render_cap", this)
+
+ this.entitysRenderingTemp = {}
+ this.entitysRendering = {}
+
+ this.dontRender = {}
+
+ // this.removeHiddenEnts = []
+
+ this.nextUpdateDontRender = 0
+ this.maxEntsRender = 50
+ this.armourstandClass = Java.type("net.minecraft.entity.item.EntityArmorStand").class
+ this.armourstandClassString = this.armourstandClass.toString()
+
+ this.registerStep(true, 5, this.updateDontRender)
+
+ this.registerEvent("renderEntity", this.renderEntity)
+ this.registerEvent("renderWorld", this.renderWorld)
+
+ // this.registerForge(net.minecraftforge.event.entity.living.LivingAttackEvent, this.entityAttackEvent);
+ }
+
+ // entityAttackEvent(event) { //TODO: maby make an actual feature for this (0ping 1tap)
+ // if (event.source.func_76346_g() === Player.getPlayer()) {
+ // if(event.entity.func_110143_aJ()<800000){
+
+ // let uuid = new Entity(event.entity).getUUID().toString()
+ // this.dontRender[uuid] = "hit"
+ // this.removeHiddenEnts.push([uuid, Date.now()+1000])
+ // }
+ // }
+ // }
+
+ renderWorld() {
+ if (!this.armourStandCapSetting.getValue()) return
+ this.entitysRendering = {}
+ Object.keys(this.entitysRenderingTemp).forEach(a => {
+ this.entitysRendering[a] = true
+ })
+ this.entitysRenderingTemp = {}
+ }
+
+ updateDontRender() {
+ if (!this.armourStandCapSetting.getValue()) return
+
+ // this.removeHiddenEnts = this.removeHiddenEnts.filter(([uuid, time])=>{
+ // if(Date.now()<time) return true
+ // delete this.dontRender[uuid]
+ // return false
+ // })
+ // try{
+ let start = Date.now()
+ if (start < this.nextUpdateDontRender) return
+ let entities = World.getAllEntitiesOfType(this.armourstandClass)
+ let Ents = new Array(100)
+ for (let i = 0; i < entities.length; i++) {
+ // if(this.dontRender[entities[i].getUUID().toString()] === "hit") continue
+ if (!this.entitysRendering[entities[i].getUUID().toString()]) {
+ delete this.dontRender[entities[i].getUUID().toString()]
+ continue //not rendering
+ }
+
+ let dist = (Player.getX() - entities[i].getX()) ** 2 + (Player.getY() - entities[i].getY()) ** 2 + (Player.getZ() - entities[i].getZ()) ** 2
+ if (dist > 100 ** 2) {
+ this.dontRender[entities[i].getUUID().toString()] = true
+ continue
+ }
+
+ dist = ~~Math.sqrt(dist)
+ Ents[dist] = [entities[i], Ents[dist]]
+ }
+
+ let entsNumber = 0
+ for (let i = 0; i < Ents.length; i++) {
+ let entsNumberTemp = entsNumber
+ while (Ents[i]) {
+ if (entsNumber > this.maxEntsRender) {
+ this.dontRender[Ents[i][0].getUUID().toString()] = true
+ } else {
+ delete this.dontRender[Ents[i][0].getUUID().toString()]
+ }
+
+ Ents[i] = Ents[i][1]
+ entsNumberTemp++
+ }
+ entsNumber = entsNumberTemp
+ }
+ let timeTook = Date.now() - start
+ // console.log(`Update took ${timeTook}ms | ${entsNumber} ents`)
+ this.nextUpdateDontRender = Date.now() + 100 + 10 * timeTook
+ // }catch(e){
+ // //cucurrent modification
+ // }
+ }
+
+ renderEntity(e, pos, ticks, event) {
+ if (!this.armourStandCapSetting.getValue()) return
+ if (!e.getEntity().class.toString() === this.armourstandClassString) return
+ this.entitysRenderingTemp[e.getUUID().toString()] = true
+ if (this.dontRender[e.getUUID().toString()]) {
+ cancel(event)
+ return
+ }
+ }
+
+ onDisable() {
+ this.running = false
+ }
+}
+
+module.exports = {
+ class: new Performance()
+} \ No newline at end of file
diff --git a/src/features/performance/metadata.json b/src/features/performance/metadata.json
new file mode 100644
index 0000000..673854b
--- /dev/null
+++ b/src/features/performance/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Performance Settings",
+ "description": "Settings that may improve / decrease performance",
+ "isHidden": "hiddenRequirement.js",
+ "isTogglable": true,
+ "defaultEnabled": false,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/senitherGui/index.js b/src/features/senitherGui/index.js
new file mode 100644
index 0000000..a3e005c
--- /dev/null
+++ b/src/features/senitherGui/index.js
@@ -0,0 +1,453 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { SoopyRenderEvent } from "../../../guimanager";
+import ButtonWithArrowAndDescription from "../../../guimanager/GuiElement/ButtonWithArrowAndDescription";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import BoxWithLoading from "../../../guimanager/GuiElement/BoxWithLoading";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import { numberWithCommas, timeSince } from "../../utils/numberUtils";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import TextBox from "../../../guimanager/GuiElement/TextBox";
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent";
+import SoopyHoverChangeEvent from "../../../guimanager/EventListener/SoopyHoverChangeEvent";
+import Dropdown from "../../../guimanager/GuiElement/Dropdown";
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement"
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import SoopyRenderUpdateEvent from "../../../guimanager/EventListener/SoopyRenderUpdateEvent";
+import { fetch } from "../../utils/networkUtils";
+
+class SenitherGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new SettingPage()
+
+ this.registerStep(true, 5, () => {
+ this.GuiPage.step.call(this.GuiPage)
+ })
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.GuiPage.delete()
+
+ this.initVariables()
+ }
+}
+
+
+class SettingPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Senither Leaderboard"
+
+ this.pages = [this.newPage(), this.newPage()]
+
+ this.pages[0].addEvent(new SoopyRenderUpdateEvent().setHandler(() => { this.lastRender = Date.now() }))
+
+ let senitherTitle = new SoopyTextElement().setText("§0Senither Leaderboard").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.pages[0].addChild(senitherTitle)
+
+ this.guildButton = new ButtonWithArrowAndDescription().setText("§0Guild Leaderboard").setLocation(0.1, 0.2, 0.35, 0.2)
+ this.guildButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedGuildButton()
+ }))
+ this.pages[0].addChild(this.guildButton)
+ this.playerButton = new ButtonWithArrowAndDescription().setText("§0Player Leaderboard").setLocation(0.55, 0.2, 0.35, 0.2)
+ this.playerButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.clickedPlayerButton()
+ }))
+ this.pages[0].addChild(this.playerButton)
+
+ this.guildButton.setDesc("§0Guilds tracked: &7Loading...")
+ this.playerButton.setDesc("§0Players tracked: &7Loading...")
+
+ let leaveAndJoins = new SoopyTextElement().setText("§0Leave and joins").setMaxTextScale(2).setLocation(0.1, 0.45, 0.8, 0.1)
+ this.pages[0].addChild(leaveAndJoins)
+ this.leaveAndJoinsBox = new SoopyBoxElement().setLocation(0.1, 0.55, 0.8, 0.4)
+ this.pages[0].addChild(this.leaveAndJoinsBox)
+
+ this.leaveAndJoinsBox.addChild(new BoxWithLoading().setLocation(0.2, 0.2, 0.6, 0.6))
+
+
+ this.playerPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+ let playerTitle = new SoopyTextElement().setText("§0Player Leaderboard").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.playerPage.addChild(playerTitle)
+
+ this.playerSearchBox = new TextBox().setPlaceholder("Click to search").setLocation(0.2, 0.15, 0.6, 0.1)
+ this.playerPage.addChild(this.playerSearchBox)
+
+ this.playerPage.addChild(new SoopyTextElement().setText("§7Pos").setLocation(0.1, 0.3, 0.075 * 0.8, 0.1))
+ this.playerPage.addChild(new SoopyTextElement().setText("§7Name").setLocation(0.1 + 0.1 * 0.8, 0.3, 0.2 * 0.8, 0.1))
+ this.playerPage.addChild(new SoopyTextElement().setText("§7Guild").setLocation(0.1 + 0.325 * 0.8, 0.3, 0.2 * 0.8, 0.1))
+ let weight = new SoopyTextElement().setText("§0Weight").setLocation(0.1 + 0.55 * 0.8, 0.3, 0.2 * 0.8, 0.1).setLore(["Click to set sorting to weight"])
+ this.playerPage.addChild(weight)
+ weight.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.playerSortThing = "weight"
+ this.playerInformationUpdated = false
+ this.playersBox.location.scroll.y.set(0, 100)
+ this.playersBox._scrollAmount = 0
+
+ dropdown.setColorPrefix("&7")
+ weight.setText("§0Weight")
+ }))
+
+ let dropdown = new Dropdown().setOptions({
+ "average_skill": "Average Skill",
+ "average_skill_progress": "Average Skill Progress",
+ "catacomb": "Catacomb",
+ "catacomb_xp": "Catacomb Xp",
+ "secrets_found": "Secrets Found",
+ "healer": "Healer Level",
+ "healer_xp": "Healer Xp",
+ "mage": "Mage Level",
+ "mage_xp": "Mage Xp",
+ "berserk": "Berserk Level",
+ "berserk_xp": "Berserk Xp",
+ "archer": "Archer Level",
+ "archer_xp": "Archer Xp",
+ "tank": "Tank Level",
+ "tank_xp": "Tank Xp",
+ "total_slayer": "Total Slayer",
+ "revenant_xp": "Revenant Xp",
+ "tarantula_xp": "Tarantula Xp",
+ "sven_xp": "Sven Xp",
+ "enderman_xp": "Enderman Xp",
+ "mining": "Mining Level",
+ "mining_xp": "Mining Xp",
+ "foraging": "Foraging Level",
+ "foraging_xp": "Foraging Xp",
+ "enchanting": "Enchanting Level",
+ "enchanting_xp": "Enchanting Xp",
+ "farming": "Farming Level",
+ "farming_xp": "Farming Xp",
+ "combat": "Combat Level",
+ "combat_xp": "Combat Xp",
+ "fishing": "Fishing Level",
+ "fishing_xp": "Fishing Xp",
+ "alchemy": "Alchemy Level",
+ "alchemy_xp": "Alchemy Xp",
+ "taming": "Taming Level",
+ "taming_xp": "Taming Xp",
+ "carpentry": "Carpentry Level",
+ "carpentry_xp": "Carpentry Xp",
+ "runecrafting": "Runecrafting Level",
+ "runecrafting_xp": "Runecrafting Xp",
+ }).setSelectedOption("average_skill_progress").setLocation(0.1 + 0.75 * 0.8, 0.3, 0.25 * 0.8, 0.1).renderBox(false).setColorPrefix("&7")
+
+ this.playerPage.addChild(dropdown)
+ dropdown.text.setMaxTextScale(1)
+
+ this.playerSortThing = "weight"
+
+ dropdown.addEvent(new SoopyContentChangeEvent().setHandler(newVal => {
+ this.playerSortThing = newVal
+ this.selectedPlayerVal = newVal
+ this.playerInformationUpdated = false
+ this.playersBox.location.scroll.y.set(0, 100)
+ this.playersBox._scrollAmount = 0
+
+ dropdown.setColorPrefix("&0")
+ weight.setText("§7Weight")
+ }))
+
+ // element.addChild(new SoopyTextElement().setText("§0#"+(i+1)).setLocation(0,0,0.075,1))
+ // element.addChild(new SoopyTextElement().setText("§0"+p.username).setLocation(0.1,0,0.2,1))
+ // element.addChild(new SoopyTextElement().setText("§0"+p.guild_name).setLocation(0.325,0,0.2,1))
+ // element.addChild(new SoopyTextElement().setText("§0"+numberWithCommas(p.weight)).setLocation(0.55,0,0.2,1))
+ // element.addChild(new SoopyTextElement().setText("§0"+numberWithCommas(p[this.selectedPlayerVal])).setLocation(0.75,0,0.2,1))
+
+ this.playersBox = new SoopyGuiElement().setLocation(0.1, 0.4, 0.8, 0.6).setScrollable(true)
+ this.playerPage.addChild(this.playersBox)
+
+ this.playerSearch = ""
+ this.playerInformationUpdated = true
+ this.playerSearchBox.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ if (newVal.includes("&")) {
+ resetFun()
+ return;
+ }
+ this.playerSearch = newVal
+
+ this.playersBox.location.scroll.y.set(0, 100)
+ this.playersBox._scrollAmount = 0
+
+ this.playerInformationUpdated = false
+ }))
+ this.updatingPlayerInfo = false
+
+ this.selectedPlayerVal = "average_skill_progress"
+
+ this.guildData = undefined
+
+ this.guildPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+ let guildTitle = new SoopyTextElement().setText("§0Guild Leaderboard").setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.guildPage.addChild(guildTitle)
+
+ this.guildSearchBox = new TextBox().setPlaceholder("Click to search").setLocation(0.2, 0.15, 0.6, 0.1)
+ this.guildPage.addChild(this.guildSearchBox)
+ this.guildSearch = ""
+ this.guildSearchBox.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ this.guildSearch = newVal
+
+ this.guildsBox.location.scroll.y.set(0, 100)
+ this.guildsBox._scrollAmount = 0
+
+ this.regenGuildElements()
+ }))
+
+
+ this.guildSortThing = "weight.total"
+
+ this.oldSortElm = undefined
+
+ sortify = (sort, name, elm) => {
+ elm.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ if (this.oldSortElm) {
+ this.oldSortElm[0].setText("&7" + this.oldSortElm[1])
+ }
+
+ this.oldSortElm = [elm, name]
+ this.guildSortThing = sort
+
+ elm.setText("&0" + name)
+ this.regenGuildElements()
+ }))
+
+ elm.setLore(["Click to sort by " + name.toLowerCase()])
+
+ if (sort === this.guildSortThing) {
+ this.oldSortElm = [elm, name]
+ elm.setText("&0" + name)
+ } else {
+ elm.setText("&7" + name)
+ }
+
+ return elm
+ }
+
+ this.guildPage.addChild(new SoopyTextElement().setText("§7Pos").setLocation(0.1, 0.3, 0.075 * 0.8, 0.1))
+ this.guildPage.addChild(new SoopyTextElement().setText("§7Name").setLocation(0.1 + 0.1 * 0.8, 0.3, 0.2 * 0.8, 0.1))
+ this.guildPage.addChild(sortify("weight.total", "Weight", new SoopyTextElement().setLocation(0.1 + 0.325 * 0.8, 0.3, 0.15 * 0.8, 0.1)))
+ this.guildPage.addChild(sortify("members", "Members", new SoopyTextElement().setLocation(0.1 + 0.475 * 0.8, 0.3, (0.1 + 0.05 / 4) * 0.8, 0.1)))
+ this.guildPage.addChild(sortify("average_skill_progress", "Skill", new SoopyTextElement().setLocation(0.1 + 0.5875 * 0.8, 0.3, (0.1 + 0.05 / 4) * 0.8, 0.1)))
+ this.guildPage.addChild(sortify("average_slayer", "Slayer", new SoopyTextElement().setLocation(0.1 + 0.7 * 0.8, 0.3, (0.15 + 0.05 / 4) * 0.8, 0.1)))
+ this.guildPage.addChild(sortify("average_catacomb", "Catacombs", new SoopyTextElement().setLocation(0.1 + 0.8625 * 0.8, 0.3, (0.1 + 0.05 / 4) * 0.8, 0.1)))
+
+ this.guildsBox = new SoopyGuiElement().setLocation(0.1, 0.4, 0.8, 0.6).setScrollable(true)
+ this.guildPage.addChild(this.guildsBox)
+
+
+
+ this.finaliseLoading()
+ }
+
+ step() {
+ if (!this.playerInformationUpdated && !this.updatingPlayerInfo) {
+ this.loadPlayerInformation()
+ }
+
+ let pbHeight = this.playersBox.location.getHeightExact()
+ let pbY = this.playersBox.location.getYExact()
+ this.playersBox.children.forEach(b => {
+ b.visable = true
+ // console.log(this.playersBox._scrollAmount)
+ if (b.location.getYExact() + 0.3 * pbHeight < pbY) {
+ // console.log(2)
+ b.visable = false
+ }
+ if (b.location.getYExact() - 0.1 * pbHeight > pbHeight + pbY) {
+ // console.log(3)
+ b.visable = false
+ }
+ })
+
+ let gbHeight = this.guildsBox.location.getHeightExact()
+ let gbY = this.guildsBox.location.getYExact()
+ this.guildsBox.children.forEach(b => {
+ b.visable = true
+ // console.log(this.playersBox._scrollAmount)
+ if (b.location.getYExact() + 0.3 * gbHeight < gbY) {
+ // console.log(2)
+ b.visable = false
+ }
+ if (b.location.getYExact() - 0.1 * gbHeight > gbHeight + gbY) {
+ // console.log(3)
+ b.visable = false
+ }
+ })
+ }
+
+ clickedGuildButton() {
+ this.pages[1].clearChildren()
+ this.pages[1].addChild(this.guildPage)
+
+ this.guildSearchBox.setText("")
+ this.guildSearch = ""
+
+ // this.playersBox.location.scroll.y.set(0, 0)
+ // this.playersBox._scrollAmount = 0
+
+ this.goToPage(2)
+
+ this.loadGuildInformation()
+ }
+
+ async loadGuildInformation() {
+ let data = awaitfetch("https://hypixel-app-api.senither.com/leaderboard/").json()
+ this.guildData = data.data
+
+ this.regenGuildElements()
+ }
+
+ regenGuildElements() {
+ this.guildsBox.clearChildren()
+
+ let yPosThing = 0
+
+ this.guildData.sort((a, b) => {
+ return getThing(b, this.guildSortThing.split(".")) - getThing(a, this.guildSortThing.split("."))
+ }).forEach((g, i) => {
+
+ if (g.name.toLowerCase().includes(this.guildSearch.toLowerCase())) {
+ let element = new SoopyBoxElement().setLocation(0, yPosThing * 0.175, 1, 0.15)
+
+ element.addEvent(new SoopyHoverChangeEvent().setHandler(() => {
+ if (element.hovered) {
+ if (element.color[0] + element.color[1] + element.color[2] < 0.5 * (255 + 255 + 255)) {
+ element.setColorOffset(10, 10, 10, 100)
+ } else {
+ element.setColorOffset(-10, -10, -10, 100)
+ }
+ } else {
+ element.setColorOffset(0, 0, 0, 100)
+ }
+ }))
+
+ element.addChild(new SoopyTextElement().setText("§0#" + (i + 1)).setLocation(0, 0, 0.075, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + g.name).setLocation(0.1, 0, 0.2, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(Math.floor(g.weight.total))).setLocation(0.325, 0, 0.15, 1).setLore(["§6" + numberWithCommas(g.weight.skill) + " §7skill weight", "§6" + numberWithCommas(g.weight.slayer) + " §7slayer weight", "§6" + numberWithCommas(g.weight.catacomb) + " §7dungeons weight", "§6" + g.weight.multiplier + " §7multiplier"]))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(g.members)).setLocation(0.475, 0, 0.1, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(g.average_skill_progress)).setLocation(0.5875, 0, 0.1, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(Math.floor(g.average_slayer))).setLocation(0.7, 0, 0.15, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(g.average_catacomb)).setLocation(0.8625, 0, 0.1, 1))
+ // element.addChild(new SoopyTextElement().setText("§0"+numberWithCommas(g.members)).setLocation(0.75,0,0.2,1))
+
+ this.guildsBox.addChild(element)
+
+ yPosThing++
+ }
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Pos").setLocation(0.1,0.3,0.075*0.8,0.1))
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Name").setLocation(0.1+0.1*0.8,0.3,0.2*0.8,0.1))
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Weight").setLocation(0.1+0.325*0.8,0.3,0.15*0.8,0.1))
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Members").setLocation(0.1+0.475*0.8,0.3,(0.1+0.05/4)*0.8,0.1))
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Skill").setLocation(0.1+0.5875*0.8,0.3,(0.1+0.05/4)*0.8,0.1))
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Slayer").setLocation(0.1+0.7*0.8,0.3,(0.15+0.05/4)*0.8,0.1))
+ // this.guildPage.addChild(new SoopyTextElement().setText("§0Catacombs").setLocation(0.1+0.8625*0.8,0.3,(0.1+0.05/4)*0.8,0.1))
+ })
+ }
+
+ clickedPlayerButton() {
+ this.pages[1].clearChildren()
+ this.pages[1].addChild(this.playerPage)
+
+ this.playerSearchBox.setText("")
+ this.playerSearch = ""
+
+ this.playersBox.location.scroll.y.set(0, 0)
+ this.playersBox._scrollAmount = 0
+
+ this.goToPage(2)
+
+ this.playerInformationUpdated = false
+ }
+
+ async loadPlayerInformation() {
+ if (this.updatingPlayerInfo) return
+ this.updatingPlayerInfo = true
+ this.playerInformationUpdated = true
+ let players = await fetch("https://hypixel-app-api.senither.com/leaderboard/players?perPage=100&page=1&sort=" + this.playerSortThing + (this.playerSearch ? "&username=" + this.playerSearch : "")).json()
+ this.playersBox.clearChildren()
+ players.data.forEach((p, i) => {
+ let element = new SoopyBoxElement().setLocation(0, i * 0.175, 1, 0.15)
+
+ element.addEvent(new SoopyHoverChangeEvent().setHandler(() => {
+ if (element.hovered) {
+ if (element.color[0] + element.color[1] + element.color[2] < 0.5 * (255 + 255 + 255)) {
+ element.setColorOffset(10, 10, 10, 100)
+ } else {
+ element.setColorOffset(-10, -10, -10, 100)
+ }
+ } else {
+ element.setColorOffset(0, 0, 0, 100)
+ }
+ }))
+
+ element.addChild(new SoopyTextElement().setText("§0#" + (i + 1)).setLocation(0, 0, 0.075, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + p.username).setLocation(0.1, 0, 0.2, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + p.guild_name).setLocation(0.325, 0, 0.2, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(p.weight)).setLocation(0.55, 0, 0.2, 1))
+ element.addChild(new SoopyTextElement().setText("§0" + numberWithCommas(p[this.selectedPlayerVal])).setLocation(0.75, 0, 0.2, 1))
+
+ this.playersBox.addChild(element)
+ })
+ this.updatingPlayerInfo = false
+ }
+
+ async loadFirstPageInformation() {
+ let stats = await fetch("https://hypixel-app-api.senither.com/leaderboard/stats").json()
+ this.guildButton.setDesc("§0Guilds tracked: " + numberWithCommas(stats.data.guilds))
+ this.playerButton.setDesc("§0Players tracked: " + numberWithCommas(stats.data.players))
+
+ let leaveAndJoins = await fetch("https://hypixel-app-api.senither.com/leaderboard/history?perPage=10&page=1").json()
+ this.leaveAndJoinsBox.clearChildren()
+ let y = 0
+ leaveAndJoins.data.forEach(elm => {
+ this.leaveAndJoinsBox.addChild(new SoopyTextElement().setText(`§1${elm.username} §7${elm.type === 0 ? "joined" : "left"} §1${elm.guild_name}`).setLocation(0.05, y, 0.8, 0.1))
+ this.leaveAndJoinsBox.addChild(new SoopyTextElement().setText(`§7${timeSince(elm.created_at)} ago`).setLocation(0.8125, y, 0.175, 0.1))
+ y += 0.1
+ })
+ }
+
+
+ onOpen() {
+ this.loadFirstPageInformation()
+
+ let sidebar = new SoopyGuiElement().setLocation(0.1, 0.1, 0.8, 0.8).setScrollable(true).enableFrameBuffer()
+ this.openSidebarPage(sidebar)
+ let markdown = new SoopyMarkdownElement().setText("# NOTE: \nAll credit for the idea, design of this gui, and loading of data goes to Senither who made the original leaderboard (https://hypixel-leaderboard.senither.com/)\n\nThis is just a recode of that to allow for checking the leaderboard from in-game")
+ sidebar.addChild(markdown)
+
+ sidebar.addChild(new ButtonWithArrow().setText("§0Open Leaderboard Website").setLocation(0.1, markdown.getHeight() + 0.05, 0.8, 0.2).addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(new java.net.URI("https://hypixel-leaderboard.senither.com/"));
+ })))
+
+ sidebar.setScrollable(true)
+
+
+ sidebar.dirtyFrameBuffer(1000)
+ }
+}
+
+module.exports = {
+ class: new SenitherGui()
+}
+
+function getThing(obj, stack) {
+ while (stack.length > 0) {
+ obj = obj[stack.shift()]
+ }
+
+ return obj
+} \ No newline at end of file
diff --git a/src/features/senitherGui/metadata.json b/src/features/senitherGui/metadata.json
new file mode 100644
index 0000000..b7b1f6f
--- /dev/null
+++ b/src/features/senitherGui/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Friends gui",
+ "description": "Gui for friends",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/settings/helpDataLoader.js b/src/features/settings/helpDataLoader.js
new file mode 100644
index 0000000..21ca241
--- /dev/null
+++ b/src/features/settings/helpDataLoader.js
@@ -0,0 +1,50 @@
+import { fetch } from "../../utils/networkUtils";
+
+class HelpDataLoader {
+ constructor() {
+ this.availableHelpData = {}
+ this.dataCach = {}
+
+ fetch("http://soopy.dev/api/soopyv2/settingshelpoptions.json").json().then(data => {
+ Object.keys(data).forEach(category => {
+ this.availableHelpData[category] = new Set(data[category])
+ });
+ })
+ }
+
+ hasData(category, id) {
+ return this.availableHelpData[category] && this.availableHelpData[category].has(id)
+ }
+
+ getData(category, id, callback) {
+ if (!this.hasData(category, id)) {
+ callback("")
+ return
+ }
+
+ if (this.dataCach[category] && this.dataCach[category][id]) {
+ callback(this.dataCach[category][id])
+ return
+ }
+
+ fetch("http://soopy.dev/api/soopyv2/settingshelp/" + category + "/" + id).text().then(data => {
+ if (!this.dataCach[category]) {
+ this.dataCach[category] = {}
+ }
+
+ this.dataCach[category][id] = data
+
+ callback(data)
+ })
+ }
+}
+
+if (!global.helpDataLoader) {
+ global.helpDataLoader = new HelpDataLoader();
+
+ register("gameUnload", () => {
+ global.helpDataLoader = undefined
+ })
+}
+
+export default global.helpDataLoader; \ No newline at end of file
diff --git a/src/features/settings/index.js b/src/features/settings/index.js
new file mode 100644
index 0000000..38ccb9d
--- /dev/null
+++ b/src/features/settings/index.js
@@ -0,0 +1,332 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import BoxWithToggleAndDescription from "../../../guimanager/GuiElement/BoxWithToggleAndDescription";
+import ButtonWithArrowAndDescription from "../../../guimanager/GuiElement/ButtonWithArrowAndDescription";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent";
+import settingsCommunicator from "./settingsCommunicator";
+import GuiPage from "../soopyGui/GuiPage"
+import { SoopyGui, SoopyRenderEvent } from "../../../guimanager";
+import TextBox from "../../../guimanager/GuiElement/TextBox";
+import locationSettingHolder from "./locationSettingHolder";
+
+
+class SettingsRenderer extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.SettingPage = new SettingPage()
+ this.EditLocationsPage = new EditLocationsPage()
+ this.SettingPage.FeatureManager = this.FeatureManager
+
+ this.registerStep(true, 1, () => {
+ if (!this.EditLocationsPage) return
+
+ if (this.EditLocationsPage.needsExitPage) {
+ this.EditLocationsPage.goToPage(0, 500)
+ this.EditLocationsPage.needsExitPage = false
+ }
+ })
+
+ this.registerCommand("soopytogglesetting", (category, setting) => {
+ settingsCommunicator.settings?.[category]?.[setting]?.setValue(!settingsCommunicator.settings?.[category]?.[setting]?.getValue())
+ })
+
+ }
+
+ onDisable() {
+ this.EditLocationsPage = undefined
+ this.SettingPage = undefined
+ }
+}
+
+class EditLocationsPage extends GuiPage {
+
+ constructor() {
+ super(9)
+
+ this.name = "Edit GUI Locations"
+
+ this.needsExitPage = false
+
+ this.soopyGui = new SoopyGui()
+ this.soopyGui._renderBackground = () => { } //remove background darkening
+
+
+
+ this.soopyGui.ctGui.registerDraw((mouseX, mouseY, partialTicks) => {
+ this.renderGui(mouseX, mouseY)
+ this.soopyGui._render(mouseX, mouseY, partialTicks)
+ })
+ this.soopyGui.ctGui.registerClicked((mouseX, mouseY, button) => {
+ this.clicked(mouseX, mouseY)
+ this.soopyGui._onClick(mouseX, mouseY, button)
+ })
+ this.soopyGui.ctGui.registerMouseReleased((mouseX, mouseY) => {
+ this.released(mouseX, mouseY)
+ })
+
+ this.finaliseLoading()
+ }
+
+ renderGui(mouseX, mouseY) {
+ for (let setting of locationSettingHolder.getData()) {
+ if (setting.parent) {
+ if (setting.parent.isEnabled()) {
+ setting.renderGui(mouseX, mouseY)
+ }
+ } else {
+ setting.renderGui(mouseX, mouseY)
+ }
+ }
+ }
+
+ clicked(mouseX, mouseY) {
+ for (let setting of locationSettingHolder.getData()) {
+ if (setting.clicked(mouseX, mouseY)) return //dont allow the user to drag 2 locations at once
+ }
+ }
+
+ released(mouseX, mouseY) {
+ for (let setting of locationSettingHolder.getData()) {
+ setting.released(mouseX, mouseY)
+ }
+ }
+
+ onOpen() {
+ this.needsExitPage = true
+
+ this.soopyGui.open()
+ }
+}
+
+class SettingPage extends GuiPage {
+ constructor() {
+ super(10)
+
+ this.name = "Settings"
+
+ this.pages = [this.newPage(), this.newPage()]
+
+ this.settingsCategoryArea = undefined
+ this.settingsTitle = undefined
+ this.settingsArea = undefined
+ this.modifyingFeature = false
+ this.featureLoadedTextBox = undefined
+
+ this.SettingPage = undefined
+
+ this.pages[0].addEvent(new SoopyRenderEvent().setHandler(() => { this.updateLocs() }))
+
+ //###############################################################################################
+ // Settings Category Page
+ //###############################################################################################
+
+
+ let settingsCategoryTitle = new SoopyTextElement().setText("§0Settings").setMaxTextScale(3).setLocation(0.1, 0.05, 0.5, 0.1)
+ this.pages[0].addChild(settingsCategoryTitle)
+
+ this.settingsCategorySearch = new TextBox().setPlaceholder("Search...").setLocation(0.6, 0.05 + 0.0125, 0.3, 0.075)
+ this.pages[0].addChild(this.settingsCategorySearch)
+
+ this.settingsCategorySearch.text.addEvent(new SoopyContentChangeEvent().setHandler(() => {
+ this.updateSettingCategories()
+ }))
+
+ this.settingsCategoryArea = new SoopyGuiElement().setLocation(0.1, 0.2, 0.8, 0.8).setScrollable(true)
+ this.pages[0].addChild(this.settingsCategoryArea)
+
+
+ //###############################################################################################
+ // Settings Page
+ //###############################################################################################
+
+ this.settingsTitle = new SoopyTextElement().setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1)
+ this.pages[1].addChild(this.settingsTitle)
+
+ this.settingsArea = new SoopyGuiElement().setLocation(0.1, 0.2, 0.8, 0.8).setScrollable(true)
+ this.pages[1].addChild(this.settingsArea)
+
+
+ this.finaliseLoading()
+ }
+
+ onOpen() {
+ this.updateSettingCategories()
+ this.settingsCategoryArea.location.scroll.x.set(0, 0)
+ this.settingsCategoryArea.location.scroll.y.set(0, 0)
+ this.settingsCategoryArea._scrollAmount = 0
+
+ this.settingsCategorySearch.setText("")
+ this.updateSettingCategories()
+ }
+
+ onOpenPage(p) {
+ if (p === 1) this.updateSettingCategories()
+
+ this.closeSidebarPage()
+ }
+
+ updateSettingCategories() {
+
+ let search = this.settingsCategorySearch.text.text
+
+ this.settingsCategoryArea.children = []
+
+ let height = 0
+
+ Object.keys(this.FeatureManager.featureMetas).sort((a, b) => { return a.sortA - b.sortA }).forEach((f) => {
+
+ let meta = this.FeatureManager.featureMetas[f]
+
+ let showing = search.length === 0
+
+ if (!showing) {
+ showing = meta.name.toLowerCase().includes(search.toLowerCase()) || meta.description.toLowerCase().includes(search.toLowerCase())
+ }
+
+ if (!showing) {
+ settingsCommunicator.getModuleSettings(f).forEach(setting => {
+
+ if (setting && (setting.name.toLowerCase().includes(search.toLowerCase()) || setting.description.toLowerCase().includes(search.toLowerCase()))) {
+ showing = true
+ }
+ })
+ }
+
+ if (!showing) return
+
+ let isHidden = meta.isHidden
+ if (typeof isHidden === "string") {
+ isHidden = require("../" + f + "/" + isHidden).hidden(this.FeatureManager)
+ }
+ if (isHidden) return
+
+ let category = new ButtonWithArrowAndDescription().setText("&0" + meta.name).setDesc("&0" + meta.description).setLocation(0, height, 1, 0.15)
+ category.addEvent(new SoopyMouseClickEvent().setHandler(() => { this.clickedOpenCategory(f) }))
+
+ this.settingsCategoryArea.addChild(category)
+
+ height += 0.175
+
+
+ if (search.length > 0) {
+ settingsCommunicator.getModuleSettings(f).forEach(setting => {
+
+ if (setting && (setting.name.toLowerCase().includes(search.toLowerCase()) || setting.description.toLowerCase().includes(search.toLowerCase()))) {
+
+ setting.getGuiObject().location.location.y.set(height, 0)
+ this.settingsCategoryArea.addChild(setting.getGuiObject())
+
+ height += 0.025 + setting.getGuiObject().location.size.y.get()
+
+ setting.update()
+ }
+ })
+ }
+ })
+
+
+ // this.FeatureManager.features = {}; enabled features
+ }
+
+ updateSettings(category) {
+ let meta = this.FeatureManager.featureMetas[category]
+
+ this.settingsArea.children = []
+
+ this.settingsTitle.setText("&0" + this.FeatureManager.featureMetas[category].name)
+
+ let height = 0
+
+ if (meta.isTogglable) {
+ let toggle = new BoxWithToggleAndDescription().setLocation(0, height, 1, 0.15).setText("&0Main toggle").setDesc("&0This is the main toggle for the whole category")
+ this.settingsArea.addChild(toggle)
+
+ toggle.toggle.setValue(this.FeatureManager.isFeatureLoaded(category), 0)
+
+ toggle.toggle.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ //dont allow editing if currenately loading/unloading it
+ if (this.modifyingFeature) {
+ resetFun()
+ return
+ }
+
+ //toggle the feature
+ this.modifyingFeature = true
+ this.FeatureManager.featureSettingsData[category].enabled = newVal
+ this.FeatureManager.featureSettingsDataLastUpdated = true
+
+ if (!newVal && this.FeatureManager.isFeatureLoaded(category)) {
+ this.FeatureManager.unloadFeature(category)
+ }
+ if (newVal && !this.FeatureManager.isFeatureLoaded(category)) {
+ this.FeatureManager.loadFeature(category)
+ }
+
+
+ this.modifyingFeature = false
+
+ this.updateSettings(category)
+ }))
+ height += toggle.location.size.y.get() + 0.045
+ }
+
+ if (!this.FeatureManager.isFeatureLoaded(category)) {
+
+ //only show if feature issnt loaded
+
+ let textBox = new SoopyBoxElement().setLocation(0, height, 1, 0.15)
+ .addChild(new SoopyTextElement().setText("&0Feature not loaded").setLocation(0, 0, 1, 0.5))
+ .addChild(new SoopyTextElement().setText("&0Load feature to access settings").setLocation(0, 0.5, 1, 0.5))
+ this.settingsArea.addChild(textBox)
+ return;
+ }
+
+ settingsCommunicator.getModuleSettings(category).forEach(setting => {
+ setting.getGuiObject().location.location.y.set(height, 0)
+ this.settingsArea.addChild(setting.getGuiObject())
+
+ height += 0.045 + setting.getGuiObject().location.size.y.get()
+
+ setting.update()
+ })
+ }
+
+ updateLocs() {
+ let totalHeight = 0
+
+ this.settingsArea.children.forEach(e => {
+ e.location.location.y.set(totalHeight, 0)
+
+ totalHeight += e.location.size.y.get() + Math.min(0.045, e.location.size.y.get())
+ })
+
+ totalHeight = 0
+
+ this.settingsCategoryArea.children.forEach(e => {
+ e.location.location.y.set(totalHeight, 0)
+
+ totalHeight += e.location.size.y.get() + Math.min(0.025, e.location.size.y.get())
+ })
+ }
+
+ clickedOpenCategory(category) {
+ this.updateSettings(category)
+ this.goToPage(2)
+
+ this.settingsArea.location.scroll.x.set(0, 0)
+ this.settingsArea.location.scroll.y.set(0, 0)
+ this.settingsArea._scrollAmount = 0
+ }
+}
+
+module.exports = {
+ class: new SettingsRenderer()
+} \ No newline at end of file
diff --git a/src/features/settings/locationSettingHolder.js b/src/features/settings/locationSettingHolder.js
new file mode 100644
index 0000000..af6f507
--- /dev/null
+++ b/src/features/settings/locationSettingHolder.js
@@ -0,0 +1,27 @@
+class LocationSettingHolder {
+ constructor() {
+ this.data = new Set();
+ }
+
+ addLocationSetting(setting) {
+ this.data.add(setting)
+ }
+
+ removeLocationSetting(setting) {
+ this.data.delete(setting)
+ }
+
+ getData() {
+ return [...this.data]
+ }
+}
+
+if (!global.LocationSettingHolder) {
+ global.LocationSettingHolder = new LocationSettingHolder();
+
+ register("gameUnload", () => {
+ global.LocationSettingHolder = undefined
+ })
+}
+
+export default global.LocationSettingHolder; \ No newline at end of file
diff --git a/src/features/settings/metadata.json b/src/features/settings/metadata.json
new file mode 100644
index 0000000..8f2ace4
--- /dev/null
+++ b/src/features/settings/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Settings",
+ "description": "Adds a settings gui with toggles for all the modules, also renders each individual module's settings",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/settings/settingThings/FakeRequireToggle.js b/src/features/settings/settingThings/FakeRequireToggle.js
new file mode 100644
index 0000000..41932f3
--- /dev/null
+++ b/src/features/settings/settingThings/FakeRequireToggle.js
@@ -0,0 +1,27 @@
+
+class FakeRequireToggle{
+ constructor(val){
+ this.val = val
+
+ this.thisToggleEvents = []
+
+ this.toggleObject = {
+ addEvent: (event)=>{
+ this.thisToggleEvents.push(event)
+ }
+ }
+ }
+
+ set(newVal){
+ if(this.val === newVal) return
+ this.val = newVal
+
+ this.thisToggleEvents.forEach(e=>e._trigger(this, [this.val]))
+ }
+
+ getValue(){
+ return this.val
+ }
+}
+
+export default FakeRequireToggle \ No newline at end of file
diff --git a/src/features/settings/settingThings/button.js b/src/features/settings/settingThings/button.js
new file mode 100644
index 0000000..77ba4b8
--- /dev/null
+++ b/src/features/settings/settingThings/button.js
@@ -0,0 +1,18 @@
+import SoopyMouseClickEvent from "../../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../../guimanager/GuiElement/ButtonWithArrow";
+import SettingBase from "./settingBase";
+
+class ButtonSetting extends SettingBase {
+ constructor(name, description, settingId, module, buttonText, buttonFunction, defaultValue=null){
+ super(name, description, defaultValue, settingId, module)
+
+ this.buttonObject = new ButtonWithArrow().setLocation(0, 0.3, 0.8, 0.4).setText("§0"+buttonText)
+ this.settingObject.addChild(this.buttonObject)
+
+ this.buttonObject.addEvent(new SoopyMouseClickEvent().setHandler(()=>{
+ buttonFunction.call(this)
+ }))
+ }
+}
+
+export default ButtonSetting \ No newline at end of file
diff --git a/src/features/settings/settingThings/dropdownSetting.js b/src/features/settings/settingThings/dropdownSetting.js
new file mode 100644
index 0000000..0cf7b27
--- /dev/null
+++ b/src/features/settings/settingThings/dropdownSetting.js
@@ -0,0 +1,41 @@
+
+import SoopyContentChangeEvent from "../../../../guimanager/EventListener/SoopyContentChangeEvent";
+import SettingBase from "./settingBase";
+import Dropdown from "../../../../guimanager/GuiElement/Dropdown";
+
+class DropdownSetting extends SettingBase {
+ constructor(name, description, defaultVal, settingId, module, optionsData){
+ super(name, description, defaultVal, settingId, module)
+
+ this.dropdownObject = new Dropdown().setLocation(0, 0.2, 0.9, 0.6).setOptions(optionsData).setSelectedOption(this.getValue())
+ this.settingObject.addChild(this.dropdownObject)
+
+ this.settingObject.setLocation(0.6, 0, 0.4, 1)
+ this.guiObject.text.setLocation(0, 0, 0.6, 0.6)
+ this.guiObject.description.setLocation(0, 0.6, 0.55, 0.4)
+
+ this.dropdownObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun)=>{
+ this.setValue(newVal)
+ }))
+
+ }
+ update(){
+ if(this.hasHelp()){
+ this.guiObject.addChild(this.helpButton)
+
+ this.guiObject.text.setLocation(0.075, 0, 0.6-0.075, 0.6)
+ }else{
+ this.guiObject.text.setLocation(0, 0, 0.6, 0.6)
+ }
+ }
+
+ setValue(newVal){
+ super.setValue(newVal)
+
+ this.dropdownObject.setSelectedOption(newVal)
+
+ return this
+ }
+}
+
+export default DropdownSetting \ No newline at end of file
diff --git a/src/features/settings/settingThings/imageLocation.js b/src/features/settings/settingThings/imageLocation.js
new file mode 100644
index 0000000..c459c82
--- /dev/null
+++ b/src/features/settings/settingThings/imageLocation.js
@@ -0,0 +1,321 @@
+import { SoopyGui } from "../../../../guimanager"
+import SoopyBoxElement from "../../../../guimanager/GuiElement/SoopyBoxElement"
+import SoopyTextElement from "../../../../guimanager/GuiElement/SoopyTextElement"
+import ButtonSetting from "./button"
+import BoxWithText from "../../../../guimanager/GuiElement/BoxWithText"
+import ButtonWithArrow from "../../../../guimanager/GuiElement/ButtonWithArrow"
+import SoopyMouseClickEvent from "../../../../guimanager/EventListener/SoopyMouseClickEvent"
+import NumberTextBox from "../../../../guimanager/GuiElement/NumberTextBox"
+import SoopyContentChangeEvent from "../../../../guimanager/EventListener/SoopyContentChangeEvent"
+import locationSettingHolder from "../locationSettingHolder"
+
+class ImageLocationSetting extends ButtonSetting {
+ constructor(name, description, settingId, module, defaultLocation, image, imageWBase, imageHBase) {
+ super(name, description, settingId, module, "Edit Position", this.editPosition, defaultLocation)
+
+ this.x = this.getValue()[0]
+ this.y = this.getValue()[1]
+ this.scale = this.getValue()[2]
+
+ this.image = image
+ this.imageWBase = imageWBase
+ this.imageHBase = imageHBase
+
+ this.dragging = false
+ this.dragOffset = [0, 0]
+ this.resisizing = false
+ this.resizePoint = 0
+ this.resizeInitialPos = [0, 0, 0, 0, 0, 0]
+
+ this.soopyGui = new SoopyGui()
+ this.soopyGui._renderBackground = () => { } //remove background darkening
+
+ this.elmSettings = new SoopyBoxElement().setLocation(0, 0, 0.25, 0.25)
+
+ let scaleText = new SoopyTextElement().setText("&0Scale: ").setLocation(3, 0.025, 1, 0.15)
+ scaleText.location.location.setRelative(false, true)
+ scaleText.centeredX = false
+ this.elmSettings.addChild(scaleText)
+
+ this.scaleInput = new NumberTextBox().setText(this.scale.toFixed(2)).setLocation(Renderer.getStringWidth("Scale: ") + 3, 0.025, -(Renderer.getStringWidth("Scale: ") + 6), 0.15)
+ this.scaleInput.location.location.setRelative(false, true)
+ this.scaleInput.location.size.setRelative(false, true)
+ this.elmSettings.addChild(this.scaleInput)
+ this.scaleInput.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ try {
+ newVal = parseFloat(newVal)
+ if (!isNaN(newVal)) {
+ this.scale = newVal
+ this._updateValue()
+ }
+ } catch (e) { }
+ }))
+ this.lastScaleRender = this.scale.toFixed(2)
+
+ let xText = new SoopyTextElement().setText("&0X: ").setLocation(3, 0.225, 1, 0.15)
+ xText.location.location.setRelative(false, true)
+ xText.centeredX = false
+ this.elmSettings.addChild(xText)
+ this.xInput = new NumberTextBox().setText(this.x.toFixed(0)).setLocation(Renderer.getStringWidth("X: ") + 3, 0.225, -(Renderer.getStringWidth("X: ") + 6), 0.15)
+ this.xInput.location.location.setRelative(false, true)
+ this.xInput.location.size.setRelative(false, true)
+ this.elmSettings.addChild(this.xInput)
+ this.xInput.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ try {
+ newVal = parseFloat(newVal)
+ if (!isNaN(newVal)) {
+ this.x = newVal
+ this._updateValue()
+ }
+ } catch (e) { }
+ }))
+ this.lastXRender = this.x.toFixed(0)
+
+ let yText = new SoopyTextElement().setText("&0Y: ").setLocation(3, 0.425, 1, 0.15)
+ yText.location.location.setRelative(false, true)
+ yText.centeredX = false
+ this.elmSettings.addChild(yText)
+ this.yInput = new NumberTextBox().setText(this.y.toFixed(0)).setLocation(Renderer.getStringWidth("Y: ") + 3, 0.425, -(Renderer.getStringWidth("Y: ") + 6), 0.15)
+ this.yInput.location.location.setRelative(false, true)
+ this.yInput.location.size.setRelative(false, true)
+ this.elmSettings.addChild(this.yInput)
+ this.yInput.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ try {
+ newVal = parseFloat(newVal)
+ if (!isNaN(newVal)) {
+ this.y = newVal
+ this._updateValue()
+ }
+ } catch (e) { }
+ }))
+ this.lastYRender = this.y.toFixed(0)
+
+ this.soopyGui.element.addChild(this.elmSettings)
+
+ let button = new ButtonWithArrow().setText("&0Reset").setLocation(0.125, 0.625, 0.75, 0.15)
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.x = defaultLocation[0]
+ this.y = defaultLocation[1]
+ this.scale = defaultLocation[2]
+ this.shadowType = defaultLocation[3]
+ }))
+ this.elmSettings.addChild(button)
+
+ let button2 = new ButtonWithArrow().setText("&0Back").setLocation(0.125, 0.825, 0.75, 0.15)
+ button2.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.soopyGui.close()
+ this.guiObject.main.ctGui.open()
+ }))
+ this.elmSettings.addChild(button2)
+
+ this.editGui = this.soopyGui.ctGui
+
+ this.editGui.registerDraw((mouseX, mouseY, partialTicks) => {
+ this.renderGui(mouseX, mouseY)
+ this.soopyGui._render(mouseX, mouseY, partialTicks)
+ })
+ this.editGui.registerClicked((mouseX, mouseY, button) => {
+ this.clicked(mouseX, mouseY)
+ this.soopyGui._onClick(mouseX, mouseY, button)
+ })
+ this.editGui.registerMouseReleased((mouseX, mouseY) => {
+ this.released(mouseX, mouseY)
+ })
+
+ locationSettingHolder.addLocationSetting(this)
+ }
+
+ requires(toggleSetting) {
+ this.requiresO = toggleSetting
+
+ toggleSetting.toggleObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ if (newVal) {
+ this.guiObject.location.size.y.set(0.15, 500)
+ } else {
+ this.guiObject.location.size.y.set(0, 500)
+ }
+ }))
+ let newVal = this.requiresO.getValue()
+ if (!newVal) {
+ this.guiObject.location.size.y.set(0, 0)
+ }
+
+ return this
+ }
+
+ _updateValue() {
+ this.setValue([this.x, this.y, this.scale, this.shadowType])
+ }
+
+ editPosition() {
+ this.guiObject.main.ctGui.close()
+
+ this.soopyGui.open()
+ }
+
+ clicked(mouseX, mouseY) {
+ let width = this.getWidth()
+ let height = this.getHeight()
+
+ let locations = [[this.x, this.y], [this.x + width * this.scale, this.y], [this.x, this.y + height * this.scale], [this.x + width * this.scale, this.y + height * this.scale]]
+
+ locations.forEach((loc, i) => {
+ if (mouseX >= loc[0] - 1 * Math.max(1, this.scale) && mouseX <= loc[0] + 1 * Math.max(1, this.scale)
+ && mouseY >= loc[1] - 1 * Math.max(1, this.scale) && mouseY <= loc[1] + 1 * Math.max(1, this.scale)) {
+ this.resisizing = true
+ this.resizePoint = i
+ this.resizeInitialPos = [mouseX, mouseY, this.x, this.y, width * this.scale, height * this.scale]
+ }
+ })
+ if (this.resisizing) return;
+
+ if (mouseX > this.x && mouseX < this.x + width * this.scale
+ && mouseY > this.y && mouseY < this.y + height * this.scale) {
+ this.dragging = true;
+ this.dragOffset = [this.x - mouseX, this.y - mouseY]
+ return true
+ }
+ return false
+ }
+ released(mouseX, mouseY) {
+ this.updateLocation(mouseX, mouseY)
+ this.dragging = false
+ this.resisizing = false
+
+ this._updateValue()
+ }
+
+ getWidth() {
+ return this.imageWBase
+ }
+
+ getHeight() {
+ return this.imageHBase
+ }
+
+ updateLocation(mouseX, mouseY, drawCollidingBox) {
+ let width = this.getWidth()
+ let height = this.getHeight()
+
+ if (this.dragging) {
+ this.x = mouseX + this.dragOffset[0]
+ this.y = mouseY + this.dragOffset[1]
+
+ let snapPoints = []
+ locationSettingHolder.getData().forEach(loc => {
+ if (loc === this) return;
+ snapPoints.push([loc.x, loc.y])
+ snapPoints.push([loc.x + loc.getWidth() * loc.scale, loc.y])
+ snapPoints.push([loc.x + loc.getWidth() * loc.scale, loc.y + height * loc.scale])
+ snapPoints.push([loc.x, loc.y + height * loc.scale])
+ })
+
+ snapPoints.forEach(point => {
+ //top left
+ if (Math.abs(this.x - point[0]) < 5 && Math.abs(this.y - point[1]) < 5) {
+ this.x = point[0]
+ this.y = point[1]
+ }
+
+ //top right
+ if (Math.abs(this.x + width * this.scale - point[0]) < 5 && Math.abs(this.y - point[1]) < 5) {
+ this.x = point[0] - width * this.scale
+ this.y = point[1]
+ }
+
+ //bottom left
+ if (Math.abs(this.x - point[0]) < 5 && Math.abs(this.y + height * this.scale - point[1]) < 5) {
+ this.x = point[0]
+ this.y = point[1] - height * this.scale
+ }
+
+ //bottom right
+ if (Math.abs(this.x + width * this.scale - point[0]) < 5 && Math.abs(this.y + height * this.scale - point[1]) < 5) {
+ this.x = point[0] - width * this.scale
+ this.y = point[1] - height * this.scale
+ }
+ })
+ }
+ if (this.resisizing) {
+ if (this.resizePoint === 3) {
+ this.scale = (mouseX - this.x) / width
+ this.scale = Math.max(0.25, this.scale)
+ }
+ if (this.resizePoint === 1) {
+
+ let [oMouseX, oMouseY, oX, oY, ow, oh] = this.resizeInitialPos
+
+ this.scale = (mouseX - this.x) / width
+ this.scale = Math.max(0.25, this.scale)
+ this.y = oY + oh - height * this.scale
+ }
+ if (this.resizePoint === 0) {
+ let [oMouseX, oMouseY, oX, oY, ow, oh] = this.resizeInitialPos
+
+ this.scale = (oX + ow - mouseX) / width
+ this.scale = Math.max(0.25, this.scale)
+ this.x = oX + ow - width * this.scale
+ this.y = oY + oh - height * this.scale
+ }
+ if (this.resizePoint === 2) {
+ let [oMouseX, oMouseY, oX, oY, ow, oh] = this.resizeInitialPos
+
+ this.scale = (oX + ow - mouseX) / width
+ this.scale = Math.max(0.25, this.scale)
+ this.x = oX + ow - width * this.scale
+ }
+ }
+ }
+
+ renderGui(mouseX, mouseY) {
+
+ let width = this.getWidth()
+ let height = this.getHeight()
+
+ this.image.draw(this.x, this.y, width * this.scale, height * this.scale)
+
+ this.updateLocation(mouseX, mouseY, true)
+
+ this.renderBox(true)
+
+ if (this.x + width * this.scale / 2 > Renderer.screen.getWidth() / 2) {
+ this.elmSettings.location.location.x.set(Math.min(Math.max(this.x / Renderer.screen.getWidth() - 0.25 - 0.03, 0.02), 0.73))
+ } else {
+ this.elmSettings.location.location.x.set(Math.min(Math.max((this.x + width * this.scale) / Renderer.screen.getWidth() + 0.03, 0.02), 0.73))
+ }
+ this.elmSettings.location.location.y.set(Math.min(Math.max((this.y + height * this.scale / 2) / Renderer.screen.getHeight() - this.elmSettings.location.size.y.get() / 2, 0.02), 0.73))
+
+
+ if (this.lastScaleRender !== this.scale.toFixed(2) && !this.scaleInput.text.selected) {
+ this.lastScaleRender = this.scale.toFixed(2)
+ this.scaleInput.setText(this.scale.toFixed(2))
+ }
+ if (this.lastXRender !== this.x.toFixed(0) && !this.xInput.text.selected) {
+ this.lastXRender = this.x.toFixed(0)
+ this.xInput.setText(this.x.toFixed(0))
+ }
+ if (this.lastYRender !== this.y.toFixed(0) && !this.yInput.text.selected) {
+ this.lastYRender = this.y.toFixed(0)
+ this.yInput.setText(this.y.toFixed(0))
+ }
+ }
+
+ renderBox(drawHandles) {
+ let width = this.getWidth()
+ let height = this.getHeight()
+
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x, this.y, width * this.scale, 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x, this.y, 1, height * this.scale)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x, this.y + height * this.scale, width * this.scale, 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x + width * this.scale, this.y, 1, height * this.scale)
+
+ if (drawHandles) {
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale) + width * this.scale, this.y - 1 * Math.max(1, this.scale), 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale), this.y - 1 * Math.max(1, this.scale) + height * this.scale, 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale) + width * this.scale, this.y - 1 * Math.max(1, this.scale) + height * this.scale, 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale), this.y - 1 * Math.max(1, this.scale), 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ }
+ }
+}
+
+export default ImageLocationSetting \ No newline at end of file
diff --git a/src/features/settings/settingThings/location.js b/src/features/settings/settingThings/location.js
new file mode 100644
index 0000000..4e18446
--- /dev/null
+++ b/src/features/settings/settingThings/location.js
@@ -0,0 +1,340 @@
+import { SoopyGui } from "../../../../guimanager"
+import SoopyBoxElement from "../../../../guimanager/GuiElement/SoopyBoxElement"
+import SoopyTextElement from "../../../../guimanager/GuiElement/SoopyTextElement"
+import ButtonSetting from "./button"
+import BoxWithText from "../../../../guimanager/GuiElement/BoxWithText"
+import ButtonWithArrow from "../../../../guimanager/GuiElement/ButtonWithArrow"
+import SoopyMouseClickEvent from "../../../../guimanager/EventListener/SoopyMouseClickEvent"
+import NumberTextBox from "../../../../guimanager/GuiElement/NumberTextBox"
+import SoopyContentChangeEvent from "../../../../guimanager/EventListener/SoopyContentChangeEvent"
+import locationSettingHolder from "../locationSettingHolder"
+
+class LocationSetting extends ButtonSetting {
+ constructor(name, description, settingId, module, defaultLocation) {
+ super(name, description, settingId, module, "Edit Position", this.editPosition, defaultLocation)
+
+ this.x = this.getValue()[0] ?? 10
+ this.y = this.getValue()[1] ?? 10
+ this.scale = this.getValue()[2] || 1
+ this.shadowType = this.getValue()[3] //0-none, 1-vanilla, 2-border
+
+ this.editTempTextV = undefined
+
+ this.dragging = false
+ this.dragOffset = [0, 0]
+ this.resisizing = false
+ this.resizePoint = 0
+ this.resizeInitialPos = [0, 0, 0, 0, 0, 0]
+
+ this.onChange = undefined
+
+ this.parent = undefined
+
+ this.soopyGui = new SoopyGui()
+ this.soopyGui._renderBackground = () => { } //remove background darkening
+
+ this.elmSettings = new SoopyBoxElement().setLocation(0, 0, 0.25, 0.25)
+
+ let scaleText = new SoopyTextElement().setText("&0Scale: ").setLocation(3, 0.025, 1, 0.15)
+ scaleText.location.location.setRelative(false, true)
+ scaleText.centeredX = false
+ this.elmSettings.addChild(scaleText)
+
+ this.scaleInput = new NumberTextBox().setText(this.scale?.toFixed(2) || "?.??").setLocation(Renderer.getStringWidth("Scale: ") + 3, 0.025, -(Renderer.getStringWidth("Scale: ") + 6), 0.15)
+ this.scaleInput.location.location.setRelative(false, true)
+ this.scaleInput.location.size.setRelative(false, true)
+ this.elmSettings.addChild(this.scaleInput)
+ this.scaleInput.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ try {
+ newVal = parseFloat(newVal)
+ if (!isNaN(newVal)) {
+ this.scale = newVal
+ this._updateValue()
+ }
+ } catch (e) { }
+ }))
+ this.lastScaleRender = this.scale?.toFixed(2) || "?.??"
+
+ let xText = new SoopyTextElement().setText("&0X: ").setLocation(3, 0.225, 1, 0.15)
+ xText.location.location.setRelative(false, true)
+ xText.centeredX = false
+ this.elmSettings.addChild(xText)
+ this.xInput = new NumberTextBox().setText(this.x.toFixed(0)).setLocation(Renderer.getStringWidth("X: ") + 3, 0.225, -(Renderer.getStringWidth("X: ") + 6), 0.15)
+ this.xInput.location.location.setRelative(false, true)
+ this.xInput.location.size.setRelative(false, true)
+ this.elmSettings.addChild(this.xInput)
+ this.xInput.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ try {
+ newVal = parseFloat(newVal)
+ if (!isNaN(newVal)) {
+ this.x = newVal
+ this._updateValue()
+ }
+ } catch (e) { }
+ }))
+ this.lastXRender = this.x.toFixed(0)
+
+ let yText = new SoopyTextElement().setText("&0Y: ").setLocation(3, 0.425, 1, 0.15)
+ yText.location.location.setRelative(false, true)
+ yText.centeredX = false
+ this.elmSettings.addChild(yText)
+ this.yInput = new NumberTextBox().setText(this.y.toFixed(0)).setLocation(Renderer.getStringWidth("Y: ") + 3, 0.425, -(Renderer.getStringWidth("Y: ") + 6), 0.15)
+ this.yInput.location.location.setRelative(false, true)
+ this.yInput.location.size.setRelative(false, true)
+ this.elmSettings.addChild(this.yInput)
+ this.yInput.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ try {
+ newVal = parseFloat(newVal)
+ if (!isNaN(newVal)) {
+ this.y = newVal
+ this._updateValue()
+ }
+ } catch (e) { }
+ }))
+ this.lastYRender = this.y.toFixed(0)
+
+ this.soopyGui.element.addChild(this.elmSettings)
+
+ let button = new ButtonWithArrow().setText("&0Reset").setLocation(0.125, 0.625, 0.75, 0.15)
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.x = defaultLocation[0]
+ this.y = defaultLocation[1]
+ this.scale = defaultLocation[2]
+ this.shadowType = defaultLocation[3]
+ }))
+ this.elmSettings.addChild(button)
+
+ let button2 = new ButtonWithArrow().setText("&0Back").setLocation(0.125, 0.825, 0.75, 0.15)
+ button2.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.soopyGui.close()
+ this.guiObject.main.ctGui.open()
+ }))
+ this.elmSettings.addChild(button2)
+
+ this.editGui = this.soopyGui.ctGui
+
+ this.editGui.registerDraw((mouseX, mouseY, partialTicks) => {
+ this.renderGui(mouseX, mouseY)
+ this.soopyGui._render(mouseX, mouseY, partialTicks)
+ })
+ this.editGui.registerClicked((mouseX, mouseY, button) => {
+ this.clicked(mouseX, mouseY)
+ this.soopyGui._onClick(mouseX, mouseY, button)
+ })
+ this.editGui.registerMouseReleased((mouseX, mouseY) => {
+ this.released(mouseX, mouseY)
+ })
+
+ locationSettingHolder.addLocationSetting(this)
+ }
+
+ requires(toggleSetting) {
+ this.requiresO = toggleSetting
+
+ toggleSetting.toggleObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ if (newVal) {
+ this.guiObject.location.size.y.set(0.15, 500)
+ } else {
+ this.guiObject.location.size.y.set(0, 500)
+ }
+ }))
+ let newVal = this.requiresO.getValue()
+ if (!newVal) {
+ this.guiObject.location.size.y.set(0, 0)
+ }
+
+ return this
+ }
+
+ delete() {
+ locationSettingHolder.removeLocationSetting(this)
+ }
+
+ _updateValue() {
+ this.setValue([this.x, this.y, this.scale, this.shadowType])
+ if (this.onChange) this.onChange()
+ }
+
+ editTempText(text) {
+ this.editTempTextV = text
+ return this;
+ }
+
+ setParent(parent) {
+ this.parent = parent
+ }
+
+ editPosition() {
+ this.guiObject.main.ctGui.close()
+
+ this.soopyGui.open()
+ }
+
+ clicked(mouseX, mouseY) {
+ let width = this.getWidth()
+ let height = this.parent.getHeight(true)
+
+ let locations = [[this.x, this.y], [this.x + width * this.scale, this.y], [this.x, this.y + height * this.scale], [this.x + width * this.scale, this.y + height * this.scale]]
+
+ locations.forEach((loc, i) => {
+ if (mouseX >= loc[0] - 1 * Math.max(1, this.scale) && mouseX <= loc[0] + 1 * Math.max(1, this.scale)
+ && mouseY >= loc[1] - 1 * Math.max(1, this.scale) && mouseY <= loc[1] + 1 * Math.max(1, this.scale)) {
+ this.resisizing = true
+ this.resizePoint = i
+ this.resizeInitialPos = [mouseX, mouseY, this.x, this.y, width * this.scale, height * this.scale]
+ }
+ })
+ if (this.resisizing) return;
+
+ if (mouseX > this.x && mouseX < this.x + width * this.scale
+ && mouseY > this.y && mouseY < this.y + height * this.scale) {
+ this.dragging = true;
+ this.dragOffset = [this.x - mouseX, this.y - mouseY]
+ return true
+ }
+ return false
+ }
+ released(mouseX, mouseY) {
+ this.updateLocation(mouseX, mouseY)
+ this.dragging = false
+ this.resisizing = false
+
+ this._updateValue()
+ }
+
+ getWidth() {
+ return this.parent.getWidth(true)
+ }
+
+ updateLocation(mouseX, mouseY, drawCollidingBox) {
+ let width = this.getWidth()
+ let height = this.parent.getHeight(true)
+
+ if (this.dragging) {
+ this.x = mouseX + this.dragOffset[0]
+ this.y = mouseY + this.dragOffset[1]
+
+ let snapPoints = []
+ locationSettingHolder.getData().forEach(loc => {
+ if (loc === this) return;
+ snapPoints.push([loc.x, loc.y])
+ snapPoints.push([loc.x + loc.getWidth() * loc.scale, loc.y])
+ snapPoints.push([loc.x + loc.getWidth() * loc.scale, loc.y + height * loc.scale])
+ snapPoints.push([loc.x, loc.y + height * loc.scale])
+ })
+
+ snapPoints.forEach(point => {
+ //top left
+ if (Math.abs(this.x - point[0]) < 5 && Math.abs(this.y - point[1]) < 5) {
+ this.x = point[0]
+ this.y = point[1]
+ }
+
+ //top right
+ if (Math.abs(this.x + width * this.scale - point[0]) < 5 && Math.abs(this.y - point[1]) < 5) {
+ this.x = point[0] - width * this.scale
+ this.y = point[1]
+ }
+
+ //bottom left
+ if (Math.abs(this.x - point[0]) < 5 && Math.abs(this.y + height * this.scale - point[1]) < 5) {
+ this.x = point[0]
+ this.y = point[1] - height * this.scale
+ }
+
+ //bottom right
+ if (Math.abs(this.x + width * this.scale - point[0]) < 5 && Math.abs(this.y + height * this.scale - point[1]) < 5) {
+ this.x = point[0] - width * this.scale
+ this.y = point[1] - height * this.scale
+ }
+ })
+ }
+ if (this.resisizing) {
+ if (this.resizePoint === 3) {
+ this.scale = (mouseX - this.x) / width
+ this.scale = Math.max(0.25, this.scale)
+ }
+ if (this.resizePoint === 1) {
+
+ let [oMouseX, oMouseY, oX, oY, ow, oh] = this.resizeInitialPos
+
+ this.scale = (mouseX - this.x) / width
+ this.scale = Math.max(0.25, this.scale)
+ this.y = oY + oh - height * this.scale
+ }
+ if (this.resizePoint === 0) {
+ let [oMouseX, oMouseY, oX, oY, ow, oh] = this.resizeInitialPos
+
+ this.scale = (oX + ow - mouseX) / width
+ this.scale = Math.max(0.25, this.scale)
+ this.x = oX + ow - width * this.scale
+ this.y = oY + oh - height * this.scale
+ }
+ if (this.resizePoint === 2) {
+ let [oMouseX, oMouseY, oX, oY, ow, oh] = this.resizeInitialPos
+
+ this.scale = (oX + ow - mouseX) / width
+ this.scale = Math.max(0.25, this.scale)
+ this.x = oX + ow - width * this.scale
+ }
+ }
+ }
+
+ renderGui(mouseX, mouseY) {
+
+ if (this.parent) {
+ this.parent.editTempTextV = this.editTempTextV
+ this.parent.editTempTimeV = Date.now()
+
+ this.parent.tempDisableTime = Date.now()
+ this.parent.renderRaw()
+ }
+
+ let width = this.getWidth()
+ let height = this.parent.getHeight(true)
+
+ this.updateLocation(mouseX, mouseY, true)
+
+ this.renderBox(true)
+
+ if (this.x + width * this.scale / 2 > Renderer.screen.getWidth() / 2) {
+ this.elmSettings.location.location.x.set(Math.min(Math.max(this.x / Renderer.screen.getWidth() - 0.25 - 0.03, 0.02), 0.73))
+ } else {
+ this.elmSettings.location.location.x.set(Math.min(Math.max((this.x + width * this.scale) / Renderer.screen.getWidth() + 0.03, 0.02), 0.73))
+ }
+ this.elmSettings.location.location.y.set(Math.min(Math.max((this.y + height * this.scale / 2) / Renderer.screen.getHeight() - this.elmSettings.location.size.y.get() / 2, 0.02), 0.73))
+
+
+ if (this.lastScaleRender !== this.scale.toFixed(2) && !this.scaleInput.text.selected) {
+ this.lastScaleRender = this.scale.toFixed(2)
+ this.scaleInput.setText(this.scale.toFixed(2))
+ }
+ if (this.lastXRender !== this.x.toFixed(0) && !this.xInput.text.selected) {
+ this.lastXRender = this.x.toFixed(0)
+ this.xInput.setText(this.x.toFixed(0))
+ }
+ if (this.lastYRender !== this.y.toFixed(0) && !this.yInput.text.selected) {
+ this.lastYRender = this.y.toFixed(0)
+ this.yInput.setText(this.y.toFixed(0))
+ }
+ }
+
+ renderBox(drawHandles) {
+ let width = this.getWidth()
+ let height = this.parent.getHeight(true)
+
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x, this.y, width * this.scale, 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x, this.y, 1, height * this.scale)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x, this.y + height * this.scale, width * this.scale, 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x + width * this.scale, this.y, 1, height * this.scale)
+
+ if (drawHandles) {
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale) + width * this.scale, this.y - 1 * Math.max(1, this.scale), 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale), this.y - 1 * Math.max(1, this.scale) + height * this.scale, 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale) + width * this.scale, this.y - 1 * Math.max(1, this.scale) + height * this.scale, 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ Renderer.drawRect(Renderer.color(255, 255, 255), this.x - 1 * Math.max(1, this.scale), this.y - 1 * Math.max(1, this.scale), 2 * Math.max(1, this.scale) + 1, 2 * Math.max(1, this.scale) + 1)
+ }
+ }
+}
+
+export default LocationSetting \ No newline at end of file
diff --git a/src/features/settings/settingThings/settingBase.js b/src/features/settings/settingThings/settingBase.js
new file mode 100644
index 0000000..5320f40
--- /dev/null
+++ b/src/features/settings/settingThings/settingBase.js
@@ -0,0 +1,162 @@
+import SoopyContentChangeEvent from "../../../../guimanager/EventListener/SoopyContentChangeEvent";
+import SoopyMouseClickEvent from "../../../../guimanager/EventListener/SoopyMouseClickEvent";
+import BoxWithText from "../../../../guimanager/GuiElement/BoxWithText";
+import BoxWithTextAndDescription from "../../../../guimanager/GuiElement/BoxWithTextAndDescription"
+import SoopyGuiElement from "../../../../guimanager/GuiElement/SoopyGuiElement";
+import renderLibs from "../../../../guimanager/renderLibs";
+import helpDataLoader from "../helpDataLoader";
+import settingsCommunicator from "../settingsCommunicator";
+import SoopyMarkdownElement from "../../../../guimanager/GuiElement/SoopyMarkdownElement";
+
+class SettingBase {
+ constructor(name, description, defaultVal, settingId, module) {
+ this.name = name;
+ this.description = description;
+ this.defaultVal = defaultVal;
+ this.settingId = settingId
+ this.module = module
+ this.moduleId = module.getId()
+
+ this.contributorVal = undefined
+
+ this.val = defaultVal;
+
+ this.guiObject = new BoxWithTextAndDescription().setDesc("§0" + this.description.replace(/\n/g, "\n§0")).setText("§0" + this.name).setLocation(0, 0, 1, 0.15)
+
+ this.settingObject = new SoopyGuiElement().setLocation(0.8, 0, 0.15, 1)
+
+ this.guiObject.addChild(this.settingObject)
+
+ this.helpButton = new BoxWithText().setText("§0?").setLocation(3, 3, 0.05, 0.5)
+ this.helpButton.location.location.setRelative(false, false)
+
+ this.helpButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ module.FeatureManager.features.soopyGui.class.openSidebarPage(new SoopyGuiElement().setLocation(0.05, 0.05, 0.9, 0.9).setScrollable(true).addChild(new SoopyMarkdownElement().setLocation(0, 0, 1, 1).setText("Loading...")))
+
+ this.getHelp(helpText => {
+ module.FeatureManager.features.soopyGui.class.openSidebarPage(new SoopyGuiElement().setLocation(0.05, 0.05, 0.9, 0.9).setScrollable(true).addChild(new SoopyMarkdownElement().setLocation(0, 0, 1, 1).setText(helpText)))
+ })
+ }))
+
+ this.helpButton.setLore(["Click for more information about this setting"])
+
+ settingsCommunicator.addSetting(this.moduleId, settingId, this)
+
+ if (!module.FeatureManager.featureSettingsData[this.moduleId]) {
+ module.FeatureManager.featureSettingsData[this.moduleId] = {}
+ }
+ if (!module.FeatureManager.featureSettingsData[this.moduleId].subSettings) module.FeatureManager.featureSettingsData[this.moduleId].subSettings = {}
+ if (!module.FeatureManager.featureSettingsData[this.moduleId].subSettings[settingId]) {
+ module.FeatureManager.featureSettingsData[this.moduleId].subSettings[settingId] = {
+ value: this.getDefaultValue(),
+ temp_val: this.getDefaultValue()
+ }
+
+ module.FeatureManager.featureSettingsDataLastUpdated = true
+ }
+ let temp_val_temp = module.FeatureManager.featureSettingsData[this.moduleId].subSettings[settingId].temp_val
+ this.setValue(module.FeatureManager.featureSettingsData[this.moduleId].subSettings[settingId].value)
+ this.temp_val = temp_val_temp
+
+ this.requiresO = undefined
+
+ this.onchangethings = []
+
+ this.initTime = Date.now()
+ }
+
+ update() {
+ if (this.hasHelp()) {
+ this.guiObject.addChild(this.helpButton)
+
+ this.guiObject.text.setLocation(0.075, 0, 0.8 - 0.075, 0.6)
+ } else {
+ this.guiObject.text.setLocation(0, 0, 0.8, 0.6)
+ }
+ }
+
+ hasHelp() {
+ return helpDataLoader.hasData(this.moduleId, this.settingId)
+ }
+
+ getHelp(callback) {
+ helpDataLoader.getData(this.moduleId, this.settingId, callback)
+ }
+
+ getValue() {
+ return this.val;
+ }
+
+ setValue(val) {
+ if (this.val === val) return
+ this.val = val;
+
+ if (!this.requiresO || this.requiresO.getValue()) {
+ this.temp_val = val
+ }
+
+ if (this.module.FeatureManager.featureSettingsData[this.moduleId].subSettings[this.settingId].value !== val) {
+ this.module.FeatureManager.featureSettingsData[this.moduleId].subSettings[this.settingId].value = val
+
+ this.module.FeatureManager.featureSettingsDataLastUpdated = true
+ }
+ if (this.module.FeatureManager.featureSettingsData[this.moduleId].subSettings[this.settingId].temp_val !== this.temp_val) {
+ this.module.FeatureManager.featureSettingsData[this.moduleId].subSettings[this.settingId].temp_val = this.temp_val
+
+ this.module.FeatureManager.featureSettingsDataLastUpdated = true
+ }
+
+ if (this.onchangethings && Date.now() - this.initTime > 1000) this.onchangethings.forEach(([fun, context]) => { fun.call(context) })
+ }
+
+ getName() {
+ return this.name;
+ }
+
+ getDescription() {
+ return this.description;
+ }
+
+ getDefaultValue() {
+ return this.defaultVal;
+ }
+
+ getGuiObject() {
+ return this.guiObject;
+ }
+
+ requires(toggleSetting) {
+ this.requiresO = toggleSetting
+
+ toggleSetting.toggleObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ if (newVal) {
+ this.guiObject.location.size.y.set(0.15, 500)
+ } else {
+ this.guiObject.location.size.y.set(0, 500)
+ }
+ }))
+ let newVal = this.requiresO.getValue()
+ if (!newVal) {
+ this.guiObject.location.size.y.set(0, 0)
+ }
+
+ return this
+ }
+
+ contributor(name, things = []) {
+ this.contributorVal = { name, things }
+ //TODO: this entire function
+ return this
+ }
+
+ delete() {
+ settingsCommunicator.removeSetting(this.module, this.settingId)
+ }
+
+ onchange(context, fun) {
+ this.onchangethings.push([fun, context])
+ return this
+ }
+}
+
+export default SettingBase \ No newline at end of file
diff --git a/src/features/settings/settingThings/textSetting.js b/src/features/settings/settingThings/textSetting.js
new file mode 100644
index 0000000..edc0989
--- /dev/null
+++ b/src/features/settings/settingThings/textSetting.js
@@ -0,0 +1,41 @@
+
+import SoopyContentChangeEvent from "../../../../guimanager/EventListener/SoopyContentChangeEvent";
+import TextBox from "../../../../guimanager/GuiElement/TextBox";
+import PasswordInput from "../../../../guimanager/GuiElement/PasswordInput"
+import SettingBase from "./settingBase";
+
+class TextSetting extends SettingBase {
+ constructor(name, description, defaultVal, settingId, module, placeholder, isSecret) {
+ super(name, description, defaultVal, settingId, module)
+
+ this.textObject = (isSecret ? new PasswordInput() : new TextBox()).setLocation(0, 0.2, 0.9, 0.6).setText(this.getValue() || "").setPlaceholder(placeholder)
+ this.settingObject.addChild(this.textObject)
+
+ this.settingObject.setLocation(0.6, 0, 0.4, 1)
+ this.guiObject.text.setLocation(0, 0, 0.6, 0.6)
+ this.guiObject.description.setLocation(0, 0.6, 0.55, 0.4)
+
+ this.textObject.text.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ this.setValue(newVal)
+ }))
+
+ }
+ update() {
+ if (this.hasHelp()) {
+ this.guiObject.addChild(this.helpButton)
+
+ this.guiObject.text.setLocation(0.075, 0, 0.6 - 0.075, 0.6)
+ } else {
+ this.guiObject.text.setLocation(0, 0, 0.6, 0.6)
+ }
+ }
+ setValue(newVal) {
+ super.setValue(newVal)
+
+ this.textObject.setText(newVal)
+
+ return this
+ }
+}
+
+export default TextSetting \ No newline at end of file
diff --git a/src/features/settings/settingThings/toggle.js b/src/features/settings/settingThings/toggle.js
new file mode 100644
index 0000000..24310d2
--- /dev/null
+++ b/src/features/settings/settingThings/toggle.js
@@ -0,0 +1,62 @@
+import Enum from "../../../../guimanager/Enum";
+import SoopyContentChangeEvent from "../../../../guimanager/EventListener/SoopyContentChangeEvent";
+import Toggle from "../../../../guimanager/GuiElement/Toggle";
+import SettingBase from "./settingBase";
+
+class ToggleSetting extends SettingBase {
+ constructor(name, description, defaultVal, settingId, module) {
+ super(name, description, defaultVal, settingId, module)
+
+ this.onChange = undefined
+
+ this.toggleObject = new Toggle().setLocation(0, 0.3, 0.8, 0.4).setValue(this.getValue())
+ this.settingObject.addChild(this.toggleObject)
+
+ this.toggleObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ this.setValue(newVal)
+ }))
+
+ }
+
+ setValue(newVal) {
+ super.setValue(newVal)
+
+ this.toggleObject.setValue(newVal)
+
+ if (this.onChange) this.onChange()
+
+ return this
+ }
+
+ requires(toggleSetting) {
+ this.requiresO = toggleSetting
+
+ toggleSetting.toggleObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ if (newVal) {
+ this.setValue(this.temp_val)
+
+ this.toggleObject.triggerEvent(Enum.EVENT.CONTENT_CHANGE, [this.temp_val, false, () => { }])
+
+ this.guiObject.location.size.y.set(0.15, 500)
+ } else {
+ this.temp_val = this.getValue()
+ this.setValue(false)
+
+ this.toggleObject.triggerEvent(Enum.EVENT.CONTENT_CHANGE, [false, this.temp_val, () => { }])
+
+ this.guiObject.location.size.y.set(0, 500)
+ }
+ }))
+ let newVal = this.requiresO.getValue()
+ if (!newVal) {
+ let temp_val = this.temp_val
+ this.setValue(false)
+ this.temp_val = temp_val
+ this.guiObject.location.size.y.set(0, 0)
+ }
+
+ return this
+ }
+}
+
+export default ToggleSetting \ No newline at end of file
diff --git a/src/features/settings/settingsCommunicator.js b/src/features/settings/settingsCommunicator.js
new file mode 100644
index 0000000..053a7c1
--- /dev/null
+++ b/src/features/settings/settingsCommunicator.js
@@ -0,0 +1,35 @@
+//So features can add settings by adding to this class, then the gui will load data from this class
+//this makes it so i can add settings before the settings gui is loaded
+//and so that settings gui can still be dynamicly reloaded and not break things
+
+class SettingsCommunicator {
+ constructor(){
+ this.settings = {}
+ }
+
+ addSetting(module, settingID, settingObject){
+ if(!this.settings[module]) this.settings[module] = {}
+
+ this.settings[module][settingID] = settingObject
+ }
+ removeSetting(module, settingID){
+ if(!this.settings[module]) return;
+ delete this.settings[module][settingID]
+ }
+ getSetting(module, settingID){
+ return this.settings[module][settingID]
+ }
+ getModuleSettings(module){
+ return Object.values(this.settings[module] || [])
+ }
+}
+
+if(!global.soopyv2SettingsCommunicator){
+ global.soopyv2SettingsCommunicator = new SettingsCommunicator()
+
+ register("gameUnload", ()=>{
+ global.soopyv2SettingsCommunicator = undefined
+ })
+}
+
+export default global.soopyv2SettingsCommunicator \ No newline at end of file
diff --git a/src/features/slayers/index.js b/src/features/slayers/index.js
new file mode 100644
index 0000000..ab7c9dc
--- /dev/null
+++ b/src/features/slayers/index.js
@@ -0,0 +1,1022 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import { f, m } from "../../../mappings/mappings";
+import { numberWithCommas, timeNumber } from "../../utils/numberUtils";
+import { drawBoxAtBlock, drawBoxAtEntity, drawCoolWaypoint, drawFilledBox, drawLine } from "../../utils/renderUtils";
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import ToggleSetting from "../settings/settingThings/toggle";
+import DropdownSetting from "../settings/settingThings/dropdownSetting";
+import socketConnection from "../../socketConnection";
+import TextSetting from "../settings/settingThings/textSetting";
+import { firstLetterCapital } from "../../utils/stringUtils";
+import { delay } from "../../utils/delayUtils";
+import SettingBase from "../settings/settingThings/settingBase";
+
+function getKeyBindFromKey(key, description) {
+ var mcKeyBind = undefined //MinecraftVars.getKeyBindFromKey(key);
+
+ if (mcKeyBind == null || mcKeyBind == undefined) {
+ mcKeyBind = new KeyBind(description, key);
+ }
+
+ return mcKeyBind;
+}
+
+function distanceTo(entity) {
+ return Math.sqrt((Player.getX() - entity.getX()) ** 2 + (Player.getY() - entity.getY()) ** 2 + (Player.getZ() - entity.getZ()) ** 2)
+}
+
+class Slayers extends Feature {
+ constructor() {
+ super();
+ }
+
+ inSkyblock() {
+ return this.FeatureManager.features["dataLoader"] && this.FeatureManager.features["dataLoader"].class.isInSkyblock
+ }
+ onEnable() {
+ this.initVariables();
+
+ this.expOnKill = new ToggleSetting("Show slayer exp on boss kill", "Says your slayer exp in chat when you kill a boss, also says time taken to spawn+kill", true, "slayer_xp", this);
+ this.slainAlert = new ToggleSetting("Show boss slain alert", "This helps you to not kill mobs for ages with an inactive quest", true, "boss_slain_alert", this);
+ this.spawnAlert = new ToggleSetting("Show boss spawned alert", "This helps you to not miss your boss when you spawn it", true, "boss_spawn_alert", this);
+ this.spawnKillSetting = {
+ "0": "0",
+ "1": "1",
+ "2": "2",
+ "3": "3",
+ "4": "4"
+ }
+ this.bossSpawnKillTime = new ToggleSetting("Show boss spawn and kill time", "tells you your slayer boss speed", true, "Slayer_spawn_kill_time", this).contributor("EmeraldMerchant");
+ this.bossSpawnKillTimeDetalied = new DropdownSetting("Boss spawn & kill time using Decimal Point", "0 = 5s, 1 = 5.1s, 2 = 5.15s etc.", "1", "slayer_spawn_kill_time_decimal_point", this, this.spawnKillSetting).requires(this.bossSpawnKillTime).contributor("EmeraldMerchant");
+ this.killSetting = {
+ "0": "0",
+ "1": "1",
+ "2": "2",
+ "3": "3",
+ "4": "4"
+ }
+ this.bossKillTime = new ToggleSetting("Shows you bosses kill time", "tells you your slayer boss kill time", true, "slayer_kill_time", this).requires(this.bossSpawnKillTime).contributor("EmeraldMerchant");
+ this.bossKillTimeDetalied = new DropdownSetting("Boss kill time using Decimal Point", "0 = 5s, 1 = 5.1s, 2 = 5.15s etc.", "1", "slayer_kill_time_decimal_point", this, this.killSetting).requires(this.bossKillTime).contributor("EmeraldMerchant");
+ this.slayerXpGuiElement = new ToggleSetting("Render the xp of your current slayer on your screen", "This will help you to know how much xp u have now w/o looking in chat", true, "slayer_xp_hud", this).contributor("EmeraldMerchant");
+ this.slayerXpElement = new HudTextElement()
+ .setText("&6Slayer&7> &fLoading...")
+ .setToggleSetting(this.slayerXpGuiElement)
+ .setLocationSetting(new LocationSetting("Slayer Xp Location", "Allows you to edit the location of your current slayer xp", "slayer_xp_location", this, [10, 50, 1, 1]).requires(this.slayerXpGuiElement).editTempText("&6Enderman&7> &d&l2,147,483,647 XP").contributor("EmeraldMerchant"));
+ this.hudElements.push(this.slayerXpElement);
+
+ this.MinibossAlert = new ToggleSetting("Alert when miniboss spawned nearby", "Pops up notification when a miniboss spawned", false, "miniboss_title_ping", this).contributor("EmeraldMerchant");
+ this.MinibossPing = new ToggleSetting("Also make a sound when miniboss spawned", "Sound ping when a miniboss spawned", false, "miniboss_sound_ping", this).contributor("EmeraldMerchant");
+ this.BoxAroundMiniboss = new ToggleSetting("Draws boxes around minibosses.", "If they are too far away it doesnt draw.", false, "box_around_miniboss", this).contributor("EmeraldMerchant");
+ this.BoxAroundAreaMiniboss = new ToggleSetting("Draws boxes around area minibosses", "eg. Voidling Extremist in void sepulture", false, "box_around_area_mini", this).contributor("EmeraldMerchant");
+ this.MinibossOffWhenBoss = new ToggleSetting("Disable miniboss features when your boss spawned", "this will boost your fps a little bit during boss", true, "miniboss_off_when_boss", this).contributor("EmeraldMerchant");
+
+ this.MinibossGuiElement = new ToggleSetting("Lists Nearby Miniboss HP on Screen", "This will help you to know if theres miniboss nearby/ know their hp", true, "miniboss_hud", this).contributor("EmeraldMerchant");
+ this.MinibossElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.MinibossGuiElement)
+ .setLocationSetting(new LocationSetting("Nearby Miniboss HP Location", "Allows you to edit the location of Nearby Miniboss HP hud", "miniboss_hud_location", this, [10, 50, 1, 1]).requires(this.MinibossGuiElement).editTempText("&5Voidling Radical &a25M&c❤").contributor("EmeraldMerchant"));
+ this.hudElements.push(this.MinibossElement);
+
+ this.betterHideDeadEntity = new ToggleSetting("Also hides mob nametag when it's dead.", "An improvement for Skytils's hide dead entity", false, "hide_dead_mob_nametag", this);
+
+ this.beaconSoundType = {
+ "note.pling": "pling",
+ "random.orb": "orb"
+ }
+ this.boxAroundEmanBoss = new ToggleSetting("Box around enderman slayer boss", "This helps to know which boss is yours", true, "eman_box", this);
+ this.boxToEmanBeacon = new ToggleSetting("Box and line to the enderman beacon", "This will help to find the beacon when the boss throws it", true, "eman_beacon", this);
+ this.emanBeaconDinkDonk = new ToggleSetting("DinkDonk when beacon is spawned", "This will help to notice when the beacon is spawned", true, "eman_beacon_dinkdink", this);
+ this.beaconOnlyDingOnce = new ToggleSetting("Beacon DinkDonk but only Dink once", "Might make some people feel better", false, "beacon_dinkdink_once", this).requires(this.emanBeaconDinkDonk).contributor("EmeraldMerchant");
+ this.beaconDingSoundType = new DropdownSetting("Sound it plays for beacon ping", "1st one is louder 2nd one is higher", "note.pling", "beacon_sound", this, this.beaconSoundType).requires(this.emanBeaconDinkDonk).contributor("EmeraldMerchant");
+
+ this.emanEyeThings = new ToggleSetting("Put box around the enderman eye things", "This will help to find them", true, "eman_eye_thing", this);
+ this.emanHpGuiElement = new ToggleSetting("Render the enderman hp on your screen", "This will help you to know what stage u are in etc.", true, "eman_hp", this).contributor("EmeraldMerchant");
+
+ this.emanHpElement = new HudTextElement().setToggleSetting(this.emanHpGuiElement).setLocationSetting(new LocationSetting("Eman Hp Location", "Allows you to edit the location of the enderman hp", "eman_location", this, [10, 50, 1, 1]).requires(this.emanHpGuiElement).editTempText("&6Enderman&7> &f&l30 Hits"));
+ this.hudElements.push(this.emanHpElement);
+
+ this.rcmDaeAxeSupport = new ToggleSetting("Eman Hyp hits before Dae axe swapping", "This will tell u how many clicks with hyp is needed before swapping to dae axe", true, "eman_rcm_support", this).requires(this.emanHpGuiElement).contributor("EmeraldMerchant");
+ this.rcmDamagePerHit = new TextSetting("Hyperion damage", "Your hyp's single hit damage w/o thunderlord/thunderbolt", "", "hyp_dmg", this, "Your hyp dmg (Unit: M)", false).requires(this.rcmDaeAxeSupport).contributor("EmeraldMerchant");
+ this.whenToShowHitsLeft = new TextSetting("Show hits left timing", "At how much hp should the hits left thing be visible", "", "eman_hp_left", this, "How much hp (Unit: M, enter a valid value 0-300)", false).requires(this.rcmDaeAxeSupport).contributor("EmeraldMerchant");
+ this.thunderLevel = new TextSetting("Thunderlord Level", "What thunderlord level you have on your hyperion", "", "thunderlord_level", this, "Thunderlord level (only supports 5/6/7)", false).requires(this.rcmDaeAxeSupport).contributor("EmeraldMerchant");
+
+ this.summonFeatureMaster = new ToggleSetting("Summon Features Main Toggle", "enable this to use summon features", false, "summons_master", this).contributor("EmeraldMerchant");
+ this.summonsHideNametag = new ToggleSetting("Hide your summons' nametags", "so u can see your boss more clearly", false, "hide_summons_nametags", this).requires(this.summonFeatureMaster).contributor("EmeraldMerchant");
+ this.summonsLowWarning = new ToggleSetting("Warns you when a summon is low", "this warns you after a delay after each bosses, until you respawn them", false, "warn_when_summon_low", this).requires(this.summonFeatureMaster).contributor("EmeraldMerchant");
+ this.summonPercentage = new TextSetting("When will it start warning you", "Below how many % hp (your summons) should it start warning you", "30", "summon_warn_percentage", this, "(%)", false).requires(this.summonsLowWarning).contributor("EmeraldMerchant");
+ this.summonHPGuiElement = new ToggleSetting("Render the HP of your summons on your screen", "This will help you to know how much HP your summons have left while hide summons nametags is on", false, "summon_hp_hud", this).requires(this.summonFeatureMaster).contributor("EmeraldMerchant");
+ this.summonHPElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.summonHPGuiElement)
+ .setLocationSetting(new LocationSetting("Summon HP Location", "Allows you to edit the location of your summons' HP info", "summon_hp_location", this, [10, 50, 1, 1]).requires(this.summonHPGuiElement).editTempText("&a160k&c❤ &a160k&c❤ &a160k&c❤ &a160k&c❤").contributor("EmeraldMerchant"));
+ this.hudElements.push(this.summonHPElement);
+
+ this.lazerTimerLocation = {
+ "inBoss": "inside the boss's body",
+ "onScreen": "below Eman boss hp hud"
+ }
+ this.emanLazerTimer = new ToggleSetting("Adds a timer for the boss lazer phase", "The timer will be inside the boss's body during the phase", true, "eman_lazer_timer", this);
+ this.emanLazerTimerLocation = new DropdownSetting("eman lazer phase timer location", "You can change where the timer would be here", "inBoss", "eman_lazer_timer_location", this, this.lazerTimerLocation).requires(this.emanLazerTimer);
+
+ this.slayerSpeedRates = new ToggleSetting("Show slayer speed and exp rates", "(Slayer speed includes downtime inbetween slayers, only shows while doing slayers)", true, "slayer_speed_rates", this);
+ this.slayerSpeedRatesElement = new HudTextElement()
+ .setText("&6Slayer speed&7> &fLoading...\n&6Exp/hour&7> &fLoading...\n&6Kills/hour&7> &fLoading...")
+ .setToggleSetting(this.slayerSpeedRates)
+ .setLocationSetting(new LocationSetting("Slayer speed and exp rates location", "Allows you to edit the location of the information", "slayer_speed_rates_location", this, [10, 100, 1, 1]).requires(this.slayerSpeedRates).editTempText("&6Slayer speed&7> &f4:30\n&6Exp/hour&7> &f1,234,567\n&6Kills/hour&7> &f17"));
+
+ this.hudElements.push(this.slayerSpeedRatesElement);
+
+ this.blazeTowerDink = new ToggleSetting("DinkDonk & Box for blaze tower", "(the tower might not nessesarily belong to your boss though)", true, "blaze_tower_dinkdink", this);
+ this.slayerProgressAlert = new ToggleSetting("Shows slayer progress in middle of screen when close", "(blame dulkir)", false, "slayer_progress_alert", this);
+ this.dulkirThingElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.slayerProgressAlert)
+ .setLocationSetting(new LocationSetting("Slayer progress location", "Allows you to edit the location of the dulkir thing", "dulkir_thing_location", this, [10, 150, 1, 1]).requires(this.slayerProgressAlert).editTempText("&e98&7/&c100&7 Kills"));
+
+ this.hudElements.push(this.dulkirThingElement);
+
+ this.otherSlayerWaypoints = new ToggleSetting("Show other users slayer boss locations", "May be usefull for loot share", true, "slayer_location_other", this)
+ this.disableEmanTp = new ToggleSetting("Disable enderman Teleportation", "Exact same as feature in SBA", false, "emantp_disable", this)
+
+ this.bossBindBase = new SettingBase("Underneath is hotkey for choosing Eman Boss", "see minecraft controls menu", true, "boss_info_hotkey", this)
+ this.bossBindDefault = new TextSetting("Default keybind", "Eg KEY_F", "CHAR_NONE", "boss_keybind_default", this, "", false)
+ this.disableWhenNotYourBoss = new ToggleSetting("Disable KeyBind", "when the boss is not yours", false, "boss_bind_disable", this)
+ this.isCorrectBind = true
+ this.candidateBoss = []
+ this.arachneKeeperMain = new ToggleSetting("Main Toggle for Arachne Keepers", "this is the main toggle of the arachne keeper category", false, "arachne_keeper_main", this)
+ this.boxAroundArachneKeeper = new ToggleSetting("Box Around Arachne Keeper", "red box", false, "arachne_keeper_box", this).requires(this.arachneKeeperMain)
+ this.arachneKeeperSpawnAlert = new ToggleSetting("Arachne Keeper Spawned Alert", "tell you if one of them spawned", false, "arachne_keeper_alert", this).requires(this.arachneKeeperMain)
+ try {
+ this.bossBind = getKeyBindFromKey(Keyboard[this.bossBindDefault.getValue()], "Choose the nearest eman boss as your boss.");
+ } catch (e) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + this.bossBindDefault.getValue() + " is an invalid keyboard key, see https://legacy.lwjgl.org/javadoc/org/lwjgl/input/Keyboard.html")
+ this.isCorrectBind = false
+ }
+ if (this.isCorrectBind) {
+ this.registerEvent("tick", () => {
+ if (this.bossBind.isPressed()) {
+ if (this.disableWhenNotYourBoss.getValue() ? (this.bossSpawnedMessage && this.emanBoss) : true) {
+ let candidatesDist = []
+ this.candidateBoss?.forEach(candidate => {
+ candidatesDist.push(Math.round(parseFloat(distanceTo(candidate)) * 10))
+ })
+ this.emanBoss = this.candidateBoss[candidatesDist.indexOf(Math.min(...candidatesDist))]
+ assignActualEmanBoss(this.emanBoss)
+ }
+ }
+ })
+ }
+
+ this.lastSlayerFinishes = [];
+ this.lastSlayerExps = [];
+ this.slayerExp = {};
+ this.slayerExpLoaded = false;
+
+ this.lastSlayerType = "";
+ this.lastSlayerExp = 0;
+ this.lastBossSlain = 0;
+ this.registerChat("&r &r&a&lSLAYER QUEST COMPLETE!&r", (e) => {
+ socketConnection.sendSlayerSpawnData({ loc: null });
+ this.lastSlayerExps.push(this.lastSlayerExp);
+ if (this.lastSlayerExps.length > 5) {
+ this.lastSlayerExps.shift();
+ }
+
+ this.lastSlayerFinishes.push(Date.now());
+ if (this.lastSlayerFinishes.length > 5) {
+ this.lastSlayerFinishes.shift();
+ }
+
+ let multiplier = 1
+ if (this.FeatureManager.features["dataLoader"].class.mayorData.mayor?.name === "Aatrox") {
+ if (this.FeatureManager.features["dataLoader"].class.currentMayorPerks.has("Slayer XP Buff")) {
+ multiplier += 0.25
+ }
+ }
+
+ this.slayerExp[this.lastSlayerType] = Math.round(this.lastSlayerExp * multiplier) + (this.slayerExp[this.lastSlayerType] || 0);
+
+ if (this.expOnKill.getValue()) {
+ cancel(e);
+ ChatLib.chat("&r &r&a&lSLAYER QUEST COMPLETE!&a&r");
+ ChatLib.chat("&r &r&aYou have &d" + numberWithCommas(this.slayerExp[this.lastSlayerType]) + " " + this.lastSlayerType + " XP&r&7!&r");
+ ChatLib.chat("&r &r&aYou have &d" + numberWithCommas(Object.values(this.slayerExp).reduce((a, t) => t + a, 0)) + " total XP&r&7!&r");
+ if (this.bossSpawnKillTime.getValue() && Date.now() - this.lastBossSlain < 60000 * 10) {
+ let decimals = parseInt(this.bossSpawnKillTimeDetalied.getValue()) || 0
+
+ let time = timeNumber(Date.now() - this.lastBossSlain, decimals);
+
+ ChatLib.chat(`&r &r&aBoss took &d${time} &ato spawn and kill&r&7!`);
+ }
+ if (this.bossKillTime.getValue() && Date.now() - this.lastBossSpawned < 60000 * 4.6) {
+ let decimals = parseInt(this.bossKillTimeDetalied.getValue()) || 0
+
+ let time = timeNumber(Date.now() - this.lastBossSpawned, decimals);
+
+ ChatLib.chat(`&r &r&aBoss took &d${time} &ato kill&r&7!`);
+ }
+ }
+ this.lastBossSlain = Date.now();
+ });
+
+ this.registerChat("&r &r&c&lSLAYER QUEST FAILED!&r", () => {
+ socketConnection.sendSlayerSpawnData({ loc: null });
+ })
+ this.bossSlainMessage = false;
+ this.bossSpawnedMessage = false;
+ this.lastBossNotSpawnedTime = 0;
+ this.lastBossSpawned = 0;
+ this.cannotFindEmanBoss = false;
+
+ this.registerEvent("renderOverlay", this.renderOverlay).registeredWhen(() => this.spawnAlert.getValue() || this.slainAlert.getValue());
+
+ this.registerSoopy("apiLoad", this.apiLoad);
+ if (this.FeatureManager.features["dataLoader"] && this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock) {
+ this.apiLoad(this.FeatureManager.features["dataLoader"].class.lastApiData.skyblock, "skyblock", true, true);
+ }
+
+ this.registerChat("&r&aYou have spawned your ${soul} &r&asoul! &r&d(${mana} Mana)&r", (soul, mana) => {
+ if (!this.summonFeatureMaster.getValue()) {
+ return
+ } else if (!this.summonsHideNametag.getValue() && !this.summonHPGuiElement.getValue() && !this.summonsLowWarning.getValue()) {
+ return
+ }
+ if (this.summonAtHPShouldWarn != 0 && !this.canCaptureSummonHPInfo) {
+ this.canCaptureSummonHPInfo = true
+ }
+ })
+
+ this.registerChat("&r&cYou have despawned your monsters!&r", () => {
+ this.summonAtHPShouldWarn = 0
+ this.canCaptureSummonHPInfo = false
+ this.shouldWarn = false
+ })
+
+ this.registerStep(true, 4, () => {
+ if (this.summonFeatureMaster.getValue() && this.summonsLowWarning.getValue()) {
+ if (this.shouldWarn) {
+ Client.showTitle("&c!ONE OF THE SUMMON IS LOW!", "", 0, 3, 1);
+ World.playSound("random.orb", 1, 1);
+ }
+ }
+ })
+
+ this.todoE = [];
+ this.beaconPoints = {};
+ this.beaconE = [];
+ this.deadE = [];
+ this.beaconLocations = {};
+ this.eyeE = [];
+ this.minibossEntity = [];
+ this.arachneKeeperEntity = [];
+ this.areaMiniEntity = [];
+ this.todoE2 = [];
+ this.emanBoss = undefined;
+ this.actualEmanBoss = undefined
+ this.nextIsBoss = 0;
+ this.counter = 0;
+ this.emanStartedSittingTime = -1
+ this.pillerE = undefined
+ this.lastPillerDink = 0
+ this.slayerLocationDataH = {}
+ this.hasQuest = false
+ this.summonEntity = []
+ this.summonAtHPShouldWarn = 0
+ this.canCaptureSummonHPInfo = false
+ this.wrongSummons = false
+
+ this.Miniboss = {
+ zombie: new Set(["Revenant Sycophant", "Revenant Champion", "Deformed Revenant", "Atoned Champion", "Atoned Revenant"]),
+ spider: new Set(["Tarantula Vermin", "Tarantula Beast", "Mutant Tarantula"]),
+ wolf: new Set(["Pack Enforcer", "Sven Follower", "Sven Alpha"]),
+ enderman: new Set(["Voidling Devotee", "Voidling Radical", "Voidcrazed Maniac"]),
+ blaze: new Set(["Flare Demon", "Kindleheart Demon", "Burningsoul Demon"])
+ }
+ //dont think spider has an area mini
+ this.areaMini = {
+ zombie: new Set(["Golden Ghoul"]),
+ wolf: new Set(["Old Wolf", "Soul of the Alpha"]),
+ enderman: new Set(["Voidling Extremist"]),
+ blaze: new Set(["Millenia-Aged Blaze"])
+ }
+ this.arachneKeeper = {
+ width: 1.5,
+ height: -1,
+ r: 0.67,
+ g: 0,
+ b: 0
+ }
+
+ this.SlayerWidth = {
+ zombie: 1,
+ spider: 2,
+ wolf: 1,
+ enderman: 1,
+ blaze: 1
+ }
+ this.SlayerHeight = {
+ zombie: -2,
+ spider: -1,
+ wolf: -1,
+ enderman: -3,
+ blaze: -2
+ }
+ this.areaColor = {
+ zombie: {
+ r: 1,
+ g: 0.67,
+ b: 0
+ },
+ wolf: {
+ r: 0,
+ g: 0.67,
+ b: 0.67
+ },
+ enderman: {
+ r: 1,
+ g: 0.33,
+ b: 1
+ },
+ blaze: {
+ r: 0.67,
+ g: 0,
+ b: 0
+ }
+ }
+ //the volume of miniboss spawning is 0.6000000238418579
+ this.registerSoundPlay("random.explode", (pos, name, vol, pitch, categoryName, event) => {
+ if (Math.round(10 * vol) !== 6 || Math.abs(pos.getY() - Player.getY()) > 5 || pos.getX() - Player.getX() > 20 || pos.getZ() - Player.getZ() > 20) return
+ if (!this.bossSpawnedMessage) {
+ if (this.MinibossAlert.getValue()) Client.showTitle("&c&lMiniBoss", "", 0, 20, 10);
+ if (this.MinibossPing.getValue()) World.playSound('random.orb', 1, 1);
+ }
+ })
+
+ this.registerForge(net.minecraftforge.event.entity.living.LivingAttackEvent, this.entityAttackEvent).registeredWhen(() => this.hasQuest && this.lastSlayerType === "enderman")
+ this.renderEntityEvent = this.registerEvent("renderEntity", this.renderEntity);
+ this.renderEntityEvent.unregister();
+
+ this.registerForge(net.minecraftforge.event.entity.EntityJoinWorldEvent, this.entityJoinWorldEvent).registeredWhen(() => this.hasQuest);
+ this.registerEvent("tick", this.tick);
+ this.registerEvent("renderWorld", this.renderWorld);
+ this.registerEvent("worldLoad", this.worldLoad);
+ this.registerStep(true, 2, this.step);
+ this.registerStep(true, 4, this.step_4fps);
+ this.registerForge(Java.type("net.minecraftforge.event.entity.living.EnderTeleportEvent"), this.emanTp).registeredWhen(() => this.inSkyblock() && this.disableEmanTp.getValue())
+
+ this.formatNumber = (HPString) => {
+ HPString = HPString.removeFormatting().replace("❤", "");
+ if (HPString.endsWith("k")) {
+ return parseInt(HPString.replace("k", "")) * 1000;
+ } else if (HPString.endsWith("M")) {
+ return parseInt(HPString.replace("M", "")) * 1000000;
+ } else if (!HPString.endsWith("B")) {
+ return parseInt(HPString);
+ }
+ }
+ }
+
+ emanTp(event) {
+ cancel(event)
+ }
+
+ slayerLocationData(loc, user) {
+ if (!loc) {
+ delete this.slayerLocationDataH[user]
+ return
+ }
+ this.slayerLocationDataH[user] = [loc, Date.now()]
+ }
+
+ worldLoad() {
+ this.todoE = [];
+ this.beaconPoints = {};
+ this.beaconE = [];
+ this.deadE = [];
+ this.todoE2 = [];
+ this.beaconLocations = {};
+ this.eyeE = [];
+ this.minibossEntity = [];
+ this.areaMiniEntity = [];
+ this.arachneKeeperEntity = [];
+ this.emanBoss = undefined;
+ this.actualEmanBoss = undefined;
+
+ this.slayerLocationDataH = {}
+ this.summonEntity = []
+ this.canCaptureSummonHPInfo = false
+ this.cannotFindEmanBoss = false
+ this.candidateBoss = []
+ }
+
+ entityAttackEvent(event) {
+ // ChatLib.chat("ENTITY ATTACKING " + event.source + " -> " + event.entity)
+ if (event.source.func_76346_g() === Player.getPlayer()) {
+ if (event.entity instanceof net.minecraft.entity.monster.EntityEnderman) {
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((e) => {
+ if (e.getName().includes("Voidgloom Seraph")) {
+ //if distance from e to event.entity < 5
+ if ((e.getX() - event.entity[f.posX.Entity]) ** 2 + (e.getY() - event.entity[f.posY.Entity]) ** 2 + (e.getZ() - event.entity[f.posZ.Entity]) ** 2 < 25) {
+ this.emanBoss = e;
+ this.actualEmanBoss = event.entity;
+ }
+ }
+ });
+ }
+ }
+ }
+
+ assignActualEmanBoss(entity) {
+ if (this.bossSpawnedMessage) {
+ World.getAllEntitiesOfType(net.minecraft.entity.monster.EntityEnderman).forEach((e) => {
+ if (e.getName().includes("Voidgloom Seraph")) {
+ //if distance from e to entity < 5
+ if ((e.getX() - entity.getX()) ** 2 + (e.getY() - entity.getY()) ** 2 + (e.getZ() - entity.getZ()) ** 2 < 25) {
+ this.actualEmanBoss = e;
+ }
+ }
+ })
+ }
+ }
+
+ renderWorld(ticks) {
+ this.minibossEntity.forEach((x) => {
+ drawBoxAtEntity(x[0], 0, 1, 0, this.SlayerWidth[x[1]], this.SlayerHeight[x[1]], ticks, 4, false);
+ })
+
+ this.arachneKeeperEntity.forEach((x) => {
+ drawBoxAtEntity(x, this.arachneKeeper.r, this.arachneKeeper.g, this.arachneKeeper.b, this.arachneKeeper.width, this.arachneKeeper.height, ticks, 4, false);
+ })
+
+ this.areaMiniEntity.forEach((x) => {
+ drawBoxAtEntity(x[0], this.areaColor[x[1]].r, this.areaColor[x[1]].g, this.areaColor[x[1]].b, this.SlayerWidth[x[1]], this.SlayerHeight[x[1]], ticks, 4, false);
+ })
+
+ if (this.emanBoss && this.boxAroundEmanBoss.getValue()) drawBoxAtEntity(this.emanBoss, 0, 255, 0, 1, -3, ticks, 4, false);
+
+ if (this.emanBoss && this.emanStartedSittingTime > 0 && this.emanLazerTimer.getValue() && this.emanLazerTimerLocation.getValue === "inBoss") {
+ Tessellator.drawString(ChatLib.addColor("&a&lLazer: &c&l" + Math.max(0, 8.2 - (Date.now() - this.emanStartedSittingTime) / 1000).toFixed(1)), this.emanBoss.getX(), this.emanBoss.getY() - 1.2, this.emanBoss.getZ(), 0, true, 0.04, false);
+ }
+
+ if (this.pillerE && this.bossSpawnedMessage) {
+ drawBoxAtBlock(~~this.pillerE.getX() - 1, ~~this.pillerE.getY() + 2, ~~this.pillerE.getZ() - 1, 255, 0, 0, 1, -4);
+ }
+
+ if (this.boxToEmanBeacon.getValue()) {
+ Object.values(this.beaconPoints).forEach((line) => {
+ let lastPoint = undefined;
+ line.forEach((p) => {
+ if (lastPoint) {
+ drawLine(lastPoint[0], lastPoint[1], lastPoint[2], p[0], p[1], p[2], 0, 0, 255, 3);
+ }
+ lastPoint = p;
+ });
+ });
+ Object.values(this.beaconLocations).forEach((loc) => {
+ drawFilledBox(loc[0] + 0.5, loc[1], loc[2] + 0.5, 1.01, 1.01, 0, 0, 1, 1, true);
+ });
+ }
+
+ this.eyeE.forEach((e) => {
+ let x = e.getX() + (e.getX() - e.getLastX()) * ticks;
+ let y = e.getY() + (e.getY() - e.getLastY()) * ticks;
+ let z = e.getZ() + (e.getZ() - e.getLastZ()) * ticks;
+
+ drawBoxAtBlock(x - 0.5, y + 0.7, z - 0.5, 255, 0, 0);
+ });
+ if (this.otherSlayerWaypoints.getValue()) {
+ Object.keys(this.slayerLocationDataH).forEach(key => {
+ drawCoolWaypoint(this.slayerLocationDataH[key][0][0], this.slayerLocationDataH[key][0][1], this.slayerLocationDataH[key][0][2], 255, 0, 0, { name: key + "'s boss" })
+ })
+ }
+ }
+
+ entityJoinWorldEvent(event) {
+ this.todoE2.push(event.entity);
+ }
+
+ step_4fps() {
+ if (this.BoxAroundMiniboss.getValue() || this.BoxAroundAreaMiniboss.getValue() || this.betterHideDeadEntity.getValue() || this.summonsHideNametag.getValue() || this.summonHPGuiElement.getValue() || this.summonsLowWarning.getValue() || (this.isCorrectBind && this.bossBindDefault.getValue() != "CHAR_NONE") || this.arachneKeeperMain.getValue()) {
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((name) => {
+ let Name = name.getName()
+ let nameRemoveFormat = Name.removeFormatting()
+ if (this.arachneKeeperMain.getValue() && Name.startsWith("§8[§7Lv100§8] §4§lArachne's Keeper§r")) {
+ let shouldPing = this.arachneKeeperSpawnAlert.getValue() && (name.getY() - Player.getY()) <= 15
+ if (!this.arachneKeeperEntity?.map(a => a.getUUID().toString()).includes(name.getUUID().toString())) {
+ this.arachneKeeperEntity.push(name)
+ if (shouldPing) {
+ if (this.MinibossPing.getValue()) World.playSound('note.pling', 1, 1);
+ }
+ }
+ if (shouldPing) {
+ if (this.MinibossAlert.getValue()) Client.showTitle("&c&lArachne's Keeper!", "", 0, 20, 10);
+ }
+ return
+ }
+ if (this.cannotFindEmanBoss) {
+ if (!this.bossSpawnedMessage) {
+ this.emanBoss = undefined
+ this.cannotFindEmanBoss = false
+ } else if (nameRemoveFormat.includes("Voidgloom Seraph") && ((name.getX() - Player.getX()) ** 2 + (name.getY() - Player.getY()) ** 2 + (name.getZ() - Player.getZ()) ** 2 < 25)) {
+ this.emanBoss = name
+ this.assignActualEmanBoss(this.emanBoss)
+ this.cannotFindEmanBoss = false
+ }
+ }
+ if (nameRemoveFormat.includes("Voidgloom Seraph")) {
+ if (!this.candidateBoss?.map(c => c.getUUID().toString()).includes(name.getUUID().toString())) {
+ this.candidateBoss.push(name)
+ }
+ }
+ let nameSplit = nameRemoveFormat.split(" ")
+ let MobName = `${nameSplit[0]} ${nameSplit[1]}`
+ let MobName12 = `${nameSplit[1]} ${nameSplit[2]}`
+ let MobName1234 = `${nameSplit[1]} ${nameSplit[2]} ${nameSplit[3]} ${nameSplit[4]}` //so cringe that soul of the alpha is 4 words
+ if (this.summonsHideNametag.getValue() || this.summonsLowWarning.getValue() || this.summonHPGuiElement.getValue()) {
+ if (nameRemoveFormat.startsWith(this.summonNamePrefixs)) {
+ if (!this.summonEntity?.map(a => a.getUUID().toString()).includes(name.getUUID().toString())) {
+ this.summonEntity.push(name)
+ }
+ }
+ }
+ if ((this.MinibossOffWhenBoss.getValue() && !this.bossSpawnedMessage) || !this.MinibossOffWhenBoss.getValue()) {
+ if (this.BoxAroundMiniboss.getValue() && !this.bossSpawnedMessage && this.Miniboss[this.lastSlayerType]?.has(MobName) && !this.minibossEntity.map(a => a[0].getUUID().toString()).includes(name.getUUID().toString())) {
+ this.minibossEntity.push([name, this.lastSlayerType]);
+ }
+ if (this.BoxAroundAreaMiniboss.getValue() && !this.bossSpawnedMessage && (this.areaMini[this.lastSlayerType]?.has(MobName12) || this.areaMini[this.lastSlayerType]?.has(MobName1234)) && !this.areaMiniEntity.map(a => a[0].getUUID().toString()).includes(name.getUUID().toString())) {
+ this.areaMiniEntity.push([name, this.lastSlayerType]);
+ }
+ if (this.betterHideDeadEntity.getValue()) {
+ let lastArgs = nameSplit[nameSplit.length - 1]
+ if (lastArgs.startsWith("0") && lastArgs.endsWith("❤")) {
+ name.getEntity()[m.setAlwaysRenderNameTag](false)
+ }
+ }
+ }
+ });
+ }
+ if (this.MinibossOffWhenBoss.getValue() && this.bossSpawnedMessage && (this.minibossEntity.length > 0 || this.areaMiniEntity.length > 0)) {
+ this.minibossEntity.forEach(m => m.delete())
+ this.areaMiniEntity.forEach(m => m.delete())
+ }
+ }
+
+ tick() {
+ // if (this.FeatureManager.features["dataLoader"].class.isInSkyblock) {
+ // if (!this.entityAttackEventLoaded) {
+ // this.entityAttackEventLoaded = true;
+ // this.entityAttackEventE = this.registerForge(net.minecraftforge.event.entity.living.LivingAttackEvent, this.entityAttackEvent);
+ // }
+ // } else {
+ // if (this.entityAttackEventLoaded) {
+ // this.entityAttackEventLoaded = false;
+ // this.entityAttackEventE.unregister()
+ // }
+ // }
+
+ this.todoE.forEach((e) => {
+ try {
+ if (e instanceof net.minecraft.entity.item.EntityArmorStand && e[m.getEquipmentInSlot](4)) {
+ if (e[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]() === "Beacon") {
+ let closestEIsGaming = false;
+ let closestDist = Infinity;
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((e2) => {
+ if (e2.getName().includes("Voidgloom Seraph")) {
+ if ((e2.getX() - e[f.posX.Entity]) ** 2 + (e2.getY() - e[f.posY.Entity]) ** 2 + (e2.getZ() - e[f.posZ.Entity]) ** 2 < closestDist) {
+ closestDist = (e2.getX() - e[f.posX.Entity]) ** 2 + (e2.getY() - e[f.posY.Entity]) ** 2 + (e2.getZ() - e[f.posZ.Entity]) ** 2;
+ closestEIsGaming = this.emanBoss ? e2.getUUID().toString() === this.emanBoss.getUUID().toString() : false;
+ }
+ }
+ });
+ if (closestEIsGaming && closestDist < 100) {
+ this.beaconE.push(e);
+ }
+ }
+ if (e[m.getEquipmentInSlot](4)[m.getDisplayName.ItemStack]().startsWith("§a")) {
+ let closestEIsGaming = false;
+ let closestDist = Infinity;
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach((e2) => {
+ if (e2.getName().includes("Voidgloom Seraph")) {
+ if ((e2.getX() - e[f.posX.Entity]) ** 2 + (e2.getY() - e[f.posY.Entity]) ** 2 + (e2.getZ() - e[f.posZ.Entity]) ** 2 < closestDist) {
+ closestDist = (e2.getX() - e[f.posX.Entity]) ** 2 + (e2.getY() - e[f.posY.Entity]) ** 2 + (e2.getZ() - e[f.posZ.Entity]) ** 2;
+ closestEIsGaming = this.emanBoss ? e2.getUUID().toString() === this.emanBoss.getUUID().toString() : false;
+ }
+ }
+ });
+
+ if (closestEIsGaming && closestDist < 100 && new Item(e[m.getEquipmentInSlot](4)).getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner").getCompoundTag("Properties").getRawNBT()[m.getTagList]("textures", 10)[m.getCompoundTagAt](0)[m.getString.NBTTagCompound]("Value") === "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWIwNzU5NGUyZGYyNzM5MjFhNzdjMTAxZDBiZmRmYTExMTVhYmVkNWI5YjIwMjllYjQ5NmNlYmE5YmRiYjRiMyJ9fX0=") {
+ this.eyeE.push(new Entity(e));
+ }
+ // console.log(":" + new Item(e[m.getEquipmentInSlot](4)).getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner").getCompoundTag("Properties").getRawNBT().func_150295_c("textures", 10).func_150305_b(0).func_74779_i("Value"))
+ }
+ }
+
+ if (e[m.getCustomNameTag]() && e[m.getCustomNameTag]().includes("Voidgloom Seraph")) {
+ if (Date.now() - this.nextIsBoss < 3000) {
+ this.emanBoss = new Entity(e);
+ this.assignActualEmanBoss(this.emanBoss)
+ this.nextIsBoss = false;
+ }
+ if (this.cannotFindEmanBoss && ((e[f.posX.Entity] - Player.getX()) ** 2 + (e[f.posY.Entity] - Player.getY()) ** 2 + (e[f.posZ.Entity] - Player.getZ()) ** 2 < 5)) {
+ this.emanBoss = new Entity(e);
+ this.assignActualEmanBoss(this.emanBoss)
+ this.cannotFindEmanBoss = false
+ }
+
+ }
+
+ if (e instanceof net.minecraft.entity.item.EntityArmorStand && e[m.getCustomNameTag]() && this.blazeTowerDink.getValue()) {
+ let name = ChatLib.removeFormatting(e[m.getCustomNameTag]())
+ let isPiller = true
+ if (isPiller && name.trim().split(" ").length !== 3) isPiller = false
+ if (isPiller && name.trim().split(" ")[0].split("").pop() !== "s") isPiller = false
+ if (isPiller && name.trim().split(" ")[2] !== "hits") isPiller = false
+ if (isPiller) {
+ this.pillerE = new Entity(e)
+ }
+ }
+ } catch (_) {
+ console.log(JSON.stringify(_, undefined, 2));
+ }
+ });
+ this.todoE = this.todoE2;
+ this.todoE2 = [];
+
+ if (this.slayerXpGuiElement.getValue() && this.lastSlayerType) {
+ this.slayerXpElement.setText(`&6${firstLetterCapital(this.lastSlayerType)}&7> &d&l${numberWithCommas(this.slayerExp[this.lastSlayerType])} XP`);
+ } else {
+ this.slayerXpElement.setText(``);
+ }
+
+ if (this.emanBoss && this.emanBoss.getEntity()[f.isDead]) {
+ this.emanBoss = undefined
+ this.actualEmanBoss = undefined
+ }
+
+ summonHpFloatText = ""
+ shouldWarnNow = false
+ this.summonEntity?.forEach((eArray) => {
+ let splitted = eArray.getName().split(" ")
+ let summonHP = splitted[splitted.length - 1]
+ if (this.summonsHideNametag.getValue()) {
+ eArray.getEntity()[m.setAlwaysRenderNameTag](false)
+ }
+ if (this.summonHPGuiElement.getValue()) {
+ if (this.formatNumber(summonHP) <= this.summonAtHPShouldWarn) {
+ summonHpFloatText += `&c&l${summonHP.removeFormatting().replace("❤", "")}&r&c❤ `
+ } else { summonHpFloatText += `${summonHP} ` }
+
+ }
+ if (this.summonsLowWarning.getValue()) {
+ if (this.formatNumber(summonHP) <= this.summonAtHPShouldWarn) {
+ shouldWarnNow = true
+ }
+ if (this.canCaptureSummonHPInfo) {
+ this.canCaptureSummonHPInfo = false
+ this.summonAtHPShouldWarn = this.formatNumber(summonHP) * (parseInt(this.summonPercentage.getValue()) / 100)
+ }
+ }
+ })
+ this.shouldWarn = shouldWarnNow
+ this.summonEntity = this.summonEntity?.filter((e) => !e.getEntity()[f.isDead]);
+ if (this.summonHPGuiElement.getValue()) {
+ this.summonHPElement.setText(summonHpFloatText)
+ }
+ this.minibossEntity.forEach((eArray) => {
+ if (eArray[0].getEntity()[f.isDead]) {
+ this.minibossEntity.splice(this.minibossEntity.indexOf(eArray))
+ }
+ })
+ this.areaMiniEntity.forEach((eArray) => {
+ if (eArray[0].getEntity()[f.isDead]) {
+ this.areaMiniEntity.splice(this.areaMiniEntity.indexOf(eArray))
+ }
+ })
+ this.arachneKeeperEntity = this.arachneKeeperEntity.filter((e) => !e.getEntity()[f.isDead]);
+ this.eyeE = this.eyeE.filter((e) => !e.getEntity()[f.isDead]);
+ this.candidateBoss = this.candidateBoss.filter((e) => !e[f.isDead]);
+ this.beaconE = this.beaconE.filter((e) => {
+ if (e[f.isDead]) {
+ this.deadE.push([Date.now(), e[m.getUniqueID.Entity]().toString()]);
+
+ let pos = [e[f.posX.Entity] + 0.5, e[f.posY.Entity] + 0.7, e[f.posZ.Entity] + 0.5];
+ //check for a beacon block within 5 blocks of pos
+ if (World.getBlockAt(0, 0, 0).getID) {
+ for (let x = pos[0] - 5; x <= pos[0] + 5; x++) {
+ for (let y = pos[1] - 5; y <= pos[1] + 5; y++) {
+ for (let z = pos[2] - 5; z <= pos[2] + 5; z++) {
+ if (World.getBlockAt(Math.floor(x), Math.floor(y), Math.floor(z)).getID() === 138) {
+ this.beaconLocations[e[m.getUniqueID.Entity]().toString()] = [Math.floor(x), Math.floor(y), Math.floor(z)];
+ }
+ }
+ }
+ }
+ } else {
+ //CT 2.0 support
+ for (let x = pos[0] - 5; x <= pos[0] + 5; x++) {
+ for (let y = pos[1] - 5; y <= pos[1] + 5; y++) {
+ for (let z = pos[2] - 5; z <= pos[2] + 5; z++) {
+ if (World.getBlockAt(Math.floor(x), Math.floor(y), Math.floor(z)).getType().getID() === 138) {
+ this.beaconLocations[e[m.getUniqueID.Entity]().toString()] = [Math.floor(x), Math.floor(y), Math.floor(z)];
+ }
+ }
+ }
+ }
+ }
+
+ // if(!this.beaconLocations[e[m.getUniqueID.Entity]().toString()]){
+ // console.log("Diddnt find beacon wtf?????")
+ // }
+
+ return false;
+ }
+ return true;
+ });
+
+ this.beaconE.forEach((e) => {
+ if (!this.beaconPoints[e[m.getUniqueID.Entity]().toString()]) this.beaconPoints[e[m.getUniqueID.Entity]().toString()] = [];
+
+ this.beaconPoints[e[m.getUniqueID.Entity]().toString()].push([e[f.posX.Entity] + 0.5, e[f.posY.Entity] + 0.7, e[f.posZ.Entity] + 0.5]); //x, y, z
+ });
+
+ this.deadE = this.deadE.filter((e) => {
+ if (Date.now() - e[0] > 5000) {
+ delete this.beaconPoints[e[1]];
+ delete this.beaconLocations[e[1]];
+ return false;
+ }
+
+ let location = this.beaconLocations[e[1]];
+ if (!location) {
+ delete this.beaconPoints[e[1]];
+ delete this.beaconLocations[e[1]];
+ return false;
+ }
+
+ if (World.getBlockAt(0, 0, 0).getID) {
+ if (World.getBlockAt(location[0], location[1], location[2]).getID() === 138) {
+ if (this.emanBeaconDinkDonk.getValue()) {
+ Client.showTitle("&cGO TO BEACON!", "&c" + (Math.max(0, 5000 - (Date.now() - e[0])) / 1000).toFixed(1) + "s", 0, 20, 10);
+ if (this.beaconOnlyDingOnce.getValue() && (Math.max(0, 5000 - (Date.now() - e[0])) / 1000).toFixed(1) > 4.9) {
+ World.playSound(this.beaconDingSoundType?.getValue(), 1, 1);
+ } else if ((this.beaconOnlyDingOnce.getValue() && (Math.max(0, 5000 - (Date.now() - e[0])) / 1000).toFixed(1) >= 4.9) || !this.beaconOnlyDingOnce.getValue()) {
+ World.playSound(this.beaconDingSoundType?.getValue(), 1, 1);
+ }
+ }
+ } else {
+ delete this.beaconPoints[e[1]];
+ delete this.beaconLocations[e[1]];
+ return false;
+ }
+ } else {
+ //CT 2.0 support
+ if (World.getBlockAt(location[0], location[1], location[2]).getType().getID() === 138) {
+ if (this.emanBeaconDinkDonk.getValue()) {
+ Client.showTitle("&cGO TO BEACON!", "&c" + (Math.max(0, 5000 - (Date.now() - e[0])) / 1000).toFixed(1) + "s", 0, 20, 10);
+ if (this.beaconOnlyDingOnce.getValue() && (Math.max(0, 5000 - (Date.now() - e[0])) / 1000).toFixed(1) > 4.9) {
+ World.playSound(this.beaconDingSoundType?.getValue(), 1, 1);
+ } else if ((this.beaconOnlyDingOnce.getValue() && (Math.max(0, 5000 - (Date.now() - e[0])) / 1000).toFixed(1) >= 4.9) || !this.beaconOnlyDingOnce.getValue()) {
+ World.playSound(this.beaconDingSoundType?.getValue(), 1, 1);
+ }
+ }
+ } else {
+ delete this.beaconPoints[e[1]];
+ delete this.beaconLocations[e[1]];
+ return false;
+ }
+ }
+ return true;
+ });
+
+ if (this.emanBoss) {
+ let lazerTimer = (this.emanLazerTimerLocation.getValue() === "onScreen" && this.emanStartedSittingTime > 0 && this.emanLazerTimer.getValue()) ? ("&a&lLazer: &c&l" + Math.max(0, 8.2 - (Date.now() - this.emanStartedSittingTime) / 1000).toFixed(1)) : ""
+ let emanText = "&6Enderman&7> " + (this.emanBoss.getName().split("Voidgloom Seraph")[1] || "").trim()
+ let emanHealth = ChatLib.removeFormatting(this.emanBoss.getName().split("Voidgloom Seraph")[1])
+ if (this.rcmDaeAxeSupport.getValue()) {
+ if (emanHealth.includes("k")) {
+ emanText += " &c0 Hits"
+ } else if (emanHealth.includes("M") && parseInt(emanHealth) <= parseFloat(this.whenToShowHitsLeft.getValue())) {
+ let thunderLevel = MathLib.clamp(parseInt(this.thunderLevel.getValue()), 5, 7)
+
+ let thunderMultiplier = 1 + ((thunderLevel - 1) / 10);
+
+ let hits = parseInt(emanHealth) / (parseFloat(this.rcmDamagePerHit.getValue()) * thunderMultiplier);
+
+ emanText += ` &c${Math.max(0, Math.floor(hits - 0.75))} Hits`
+ }
+ }
+ emanText += `\n${lazerTimer}`
+
+ this.emanHpElement.setText(emanText);
+ } else {
+ this.emanHpElement.setText("");
+ }
+
+ if (this.MinibossGuiElement.getValue() && !this.bossSpawnedMessage && this.minibossEntity.length > 0) {
+ let PY = Player.getY()
+ let minis = this.minibossEntity
+ let tempArray = []
+ let tempEntity = []
+ minis.forEach((x) => {//this.SlayerHeight[slayerType] values are negative
+ if (tempEntity.includes(x[0])) return
+ if (Math.abs((x[0].getY() + this.SlayerHeight[x[1]] - PY)) > 6) return
+ let name = x[0].getName()
+ if (name.split(" ")[2] === "§e0§c❤") return
+ tempEntity.push(x[0])
+ tempArray.push(name)
+ })
+ this.MinibossElement.setText(tempArray.join("\n"))
+ } else this.MinibossElement.setText("")
+
+ if (this.pillerE) {
+ if (this.pillerE.getEntity()[f.isDead]) this.pillerE = undefined
+ }
+ if (this.pillerE && ChatLib.removeFormatting(this.pillerE.getName())[1] === "s" && this.bossSpawnedMessage) {
+ let time = parseInt(ChatLib.removeFormatting(this.pillerE.getName())[0]);
+ if (Date.now() - this.lastPillerDink > time * 40) {
+ World.playSound("note.pling", 1, 1);
+ this.lastPillerDink = Date.now()
+ }
+ Client.showTitle(this.pillerE.getName(), "", 0, 20, 10);
+ }
+
+ if (this.emanLazerTimer.getValue() && this.actualEmanBoss && this.actualEmanBoss[m.isRiding]()) {
+ if (this.emanStartedSittingTime === -1) {
+ this.emanStartedSittingTime = Date.now()
+ }
+ } else {
+ this.emanStartedSittingTime = -1
+ }
+ }
+
+ apiLoad(data, dataType, isSoopyServer, isLatest) {
+ if (!isSoopyServer || !isLatest) return;
+ if (dataType !== "skyblock") return;
+
+ this.slayerExp.zombie = data.data.profiles[data.data.stats.currentProfileId].members[Player.getUUID().replace(/-/g, "")].slayer.zombie?.xp;
+ this.slayerExp.spider = data.data.profiles[data.data.stats.currentProfileId].members[Player.getUUID().replace(/-/g, "")].slayer.spider?.xp;
+ this.slayerExp.wolf = data.data.profiles[data.data.stats.currentProfileId].members[Player.getUUID().replace(/-/g, "")].slayer.wolf?.xp;
+ this.slayerExp.enderman = data.data.profiles[data.data.stats.currentProfileId].members[Player.getUUID().replace(/-/g, "")].slayer.enderman?.xp;
+ this.slayerExp.blaze = data.data.profiles[data.data.stats.currentProfileId].members[Player.getUUID().replace(/-/g, "")].slayer.blaze?.xp;
+ }
+
+ renderOverlay() {
+ if (this.slainAlert.getValue() && this.bossSlainMessage) {
+ let scale = Renderer.getStringWidth(ChatLib.removeFormatting("BOSS SLAIN")) / (Renderer.screen.getWidth() * 0.75);
+
+ Renderer.scale(1 / scale, 1 / scale);
+ Renderer.drawString("&4BOSS SLAIN", Renderer.screen.getWidth() * 0.125 * scale, (Renderer.screen.getHeight() / 2 - 9 / scale) * scale);
+ Renderer.scale(1, 1);
+ }
+ if (this.spawnAlert.getValue() && this.bossSpawnedMessage && Date.now() - this.lastBossNotSpawnedTime < 3000) {
+ let scale = Renderer.getStringWidth(ChatLib.removeFormatting("BOSS SPAWNED")) / (Renderer.screen.getWidth() * 0.75);
+
+ Renderer.scale(1 / scale, 1 / scale);
+ Renderer.drawString("&4BOSS SPAWNED", Renderer.screen.getWidth() * 0.125 * scale, (Renderer.screen.getHeight() / 2 - 9 / scale) * scale);
+ Renderer.scale(1, 1);
+ }
+ }
+
+ step() {
+ let averageExp = this.lastSlayerExps.reduce((a, b) => a + b, 0) / this.lastSlayerExps.length;
+ let averageLength = (this.lastSlayerFinishes[this.lastSlayerFinishes.length - 1] - this.lastSlayerFinishes[0]) / (this.lastSlayerFinishes.length - 1);
+ let runsperHour = (60000 * 60) / averageLength;
+ let expPerHour = averageExp * runsperHour;
+
+ if (this.FeatureManager.features["dataLoader"] && Date.now() - this.lastSlayerFinishes[this.lastSlayerFinishes.length - 1] < 60000 * 5 || (this.FeatureManager.features["dataLoader"].class?.slayerXpToSpawn && this.FeatureManager.features["dataLoader"].class.slayerXpToSpawn[0] !== 0)) {
+ if (this.lastSlayerFinishes.length > 1) {
+ this.slayerSpeedRatesElement.setText("&6Slayer speed&7> &f" + Math.floor(averageLength / 60000) + ":" + ((Math.floor(averageLength / 1000) % 60 < 10 ? "0" : "") + (Math.floor(averageLength / 1000) % 60)) + "\n&6Exp/hour&7> &f" + numberWithCommas(Math.round(expPerHour)) + "\n&6Kills/hour&7> &f" + Math.floor(runsperHour));
+ } else {
+ this.slayerSpeedRatesElement.setText("&6Slayer speed&7> &fLoading...\n&6Exp/hour&7> &fLoading...\n&6Kills/hour&7> &fLoading...");
+ }
+ } else {
+ this.slayerSpeedRatesElement.setText("");
+ }
+
+ Object.keys(this.slayerLocationDataH).forEach(n => {
+ if (this.slayerLocationDataH[n][1] + 60000 * 3 < Date.now()) {
+ delete this.slayerLocationDataH[n]
+ }
+ })
+
+
+ let lastBossSlainMessage = this.bossSlainMessage
+ this.bossSlainMessage = false;
+ this.hasQuest = false
+ let dis1 = false;
+ this.dulkirThingElement.setText("")
+ Scoreboard.getLines().forEach((line, i) => {
+ if (ChatLib.removeFormatting(line.getName()).includes("Slayer Quest")) {
+ this.hasQuest = true
+ let slayerInfo = ChatLib.removeFormatting(Scoreboard.getLines()[i - 1].getName().replace(/§/g, "&"));
+ let levelString = slayerInfo.split(" ").pop().trim();
+ let slayerLevelToExp = {
+ I: 5,
+ II: 25,
+ III: 100,
+ IV: 500,
+ V: 1500,
+ };
+ this.lastSlayerExp = slayerLevelToExp[levelString];
+ let slayerStrToType = {
+ revenant: "zombie",
+ tarantula: "spider",
+ sven: "wolf",
+ voidgloom: "enderman",
+ inferno: "blaze"
+ }
+ this.lastSlayerType = slayerStrToType[slayerInfo.split(" ")[0].toLowerCase()];
+ //slayerExp[lastSlayerType] += lastSlayerExp
+ }
+ if (line.getName().includes("Boss slain!")) {
+ if (!lastBossSlainMessage) {
+ socketConnection.sendSlayerSpawnData({ loc: null });
+ }
+ this.bossSlainMessage = true;
+ this.cannotFindEmanBoss = false
+ }
+
+ if (line.getName().includes("Slay the boss!")) {
+ if (!this.bossSpawnedMessage) {
+ socketConnection.sendSlayerSpawnData({ loc: [Math.round(Player.getX()), Math.round(Player.getY()), Math.round(Player.getZ())] });
+ this.lastBossSpawned = Date.now();
+ if (this.emanBoss) {
+ this.emanBoss = undefined
+
+ } else {
+ this.nextIsBoss = Date.now();
+ }
+ }
+ if (this.bossSpawnedMessage && !this.emanBoss) {
+ this.cannotFindEmanBoss = true
+ }
+
+ dis1 = true;
+ this.bossSpawnedMessage = true;
+ }
+ let lineSplitThing = ChatLib.removeFormatting(line.getName()).replace(/[^a-z/0-9 ]/gi, "").trim().split(" ")
+ // ChatLib.chat(ChatLib.removeFormatting(line.getName()).replace(/[^a-z/0-9 ]+/gi, "").trim())
+
+ if (this.slayerProgressAlert.getValue() && lineSplitThing[0]
+ && lineSplitThing[0].split("/").length === 2
+ && lineSplitThing[1] === "Kills") {
+ let kills = lineSplitThing[0].split("/").map(a => parseInt(a))
+ if (kills[0] / kills[1] >= 0.9) {
+ this.dulkirThingElement.setText(line.getName())
+ }
+ }
+ });
+ if (!dis1) {
+ this.lastBossNotSpawnedTime = Date.now();
+ this.bossSpawnedMessage = false;
+ }
+ }
+
+ initVariables() {
+ this.expOnKill = undefined;
+ this.slainAlert = undefined;
+ this.spawnAlert = undefined;
+ this.slayerExp = undefined;
+ this.slayerExpLoaded = undefined;
+ this.lastSlayerType = undefined;
+ this.lastSlayerExp = undefined;
+ this.bossSpawnedMessage = undefined;
+ this.lastBossNotSpawnedTime = undefined;
+ this.bossSlainMessage = undefined;
+ this.todoE = undefined;
+ this.beaconPoints = undefined;
+ this.beaconE = undefined;
+ this.deadE = undefined;
+ this.beaconLocations = undefined;
+ this.emanBoss = undefined;
+ this.actualEmanBoss = undefined
+ this.emanStartedSittingTime = undefined
+ this.eyeE = undefined;
+ this.minibossEntity = undefined;
+ this.areaMiniEntity = undefined;
+ this.arachneKeeperEntity = undefined;
+ this.nextIsBoss = undefined;
+ this.hudElements = [];
+ this.entityAttackEventLoaded = undefined;
+ this.todoE2 = undefined;
+ this.entityAttackEventE = undefined;
+ this.summonAtHPShouldWarn = undefined;
+ this.canCaptureSummonHPInfo = false
+ this.cannotFindEmanBoss = false
+ this.candidateBoss = []
+ this.summonNamePrefixs = `${Player.getName()}'s`;
+ this.shouldWarn = false;
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.initVariables();
+ }
+}
+
+module.exports = {
+ class: new Slayers(),
+};
diff --git a/src/features/slayers/metadata.json b/src/features/slayers/metadata.json
new file mode 100644
index 0000000..b186b29
--- /dev/null
+++ b/src/features/slayers/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Slayer",
+ "description": "Slayer features",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/soopyGui/GuiPage.js b/src/features/soopyGui/GuiPage.js
new file mode 100644
index 0000000..7a21d26
--- /dev/null
+++ b/src/features/soopyGui/GuiPage.js
@@ -0,0 +1,68 @@
+import SoopyGuiElement from '../../../guimanager/GuiElement/SoopyGuiElement.js';
+import categoryManager from './categoryManager.js';
+
+class GuiPage {
+ constructor(priority) {
+ this.currentPageId = 0;
+ this.priority = priority
+
+ this.name = ""
+
+ this.pages = {}
+
+ this.showBackButton = true
+ this.finalisedLoading = false
+ }
+
+ finaliseLoading() {
+ categoryManager.addCategory(this);
+ }
+
+ getSoopyGui() {
+ if (global.soopyv2featuremanagerthing.features["soopyGui"]) return global.soopyv2featuremanagerthing.features["soopyGui"].class;
+ }
+
+ isOpen() {
+ if (!this.getSoopyGui()?.gui?.ctGui?.isOpen()) return false
+
+ return this.getSoopyGui()?.currCategory === this
+ }
+
+ newPage() {
+ this.currentPageId++
+ let page = new SoopyGuiElement().setLocation(1 * this.currentPageId, 0, 1, 1)
+
+ page._soopyAddonsPageId = this.currentPageId
+
+ this.pages[this.currentPageId] = page
+
+ return page
+ }
+
+ goToPage(page, anim) {
+ this.getSoopyGui().goToPageNum(page, anim)
+ }
+
+ openSidebarPage(child) {
+ this.getSoopyGui().openSidebarPage(child)
+ }
+ closeSidebarPage() {
+ this.getSoopyGui().closeSidebarPage()
+ }
+
+ delete() {
+ categoryManager.deleteCategory(this)
+ }
+
+ //Override me :D
+ onOpen() {
+
+ }
+
+ //Override me :D
+ onOpenPage(page) {
+
+ }
+}
+
+export default GuiPage; \ No newline at end of file
diff --git a/src/features/soopyGui/categoryManager.js b/src/features/soopyGui/categoryManager.js
new file mode 100644
index 0000000..6709ff4
--- /dev/null
+++ b/src/features/soopyGui/categoryManager.js
@@ -0,0 +1,37 @@
+class CategoryManager {
+ constructor(){
+ this.categorys = {}
+
+ this.arr = []
+ }
+
+ addCategory(category){
+ // this.pages = this.pages.filter(a=>a.name!==category.name)
+
+ this.categorys[category.name] = (category)
+ this.update()
+ }
+
+ deleteCategory(category){
+ delete this.categorys[category.name]
+ this.update()
+ }
+
+ update(){
+
+ this.arr = Object.values(this.categorys).sort((a, b)=>{
+ return b.priority - a.priority
+ })
+ }
+}
+
+
+if(!global.soopyv2CategoryManager){
+ global.soopyv2CategoryManager = new CategoryManager()
+
+ register("gameUnload", ()=>{
+ global.soopyv2CategoryManager = undefined
+ })
+}
+
+export default global.soopyv2CategoryManager \ No newline at end of file
diff --git a/src/features/soopyGui/index.js b/src/features/soopyGui/index.js
new file mode 100644
index 0000000..b19c159
--- /dev/null
+++ b/src/features/soopyGui/index.js
@@ -0,0 +1,215 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import TextWithArrow from "../../../guimanager/GuiElement/TextWithArrow";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import SoopyOpenGuiEvent from "../../../guimanager/EventListener/SoopyOpenGuiEvent";
+import SoopyGui2 from "../../../guimanager/SoopyGui";
+import categoryManager from "./categoryManager";
+import SoopyImageElement from "../../../guimanager/GuiElement/SoopyImageElement";
+
+
+class SoopyGui extends Feature {
+ constructor() {
+ super()
+
+ this.gui = undefined
+
+ this.currentPage = 0
+ this.backButton = undefined
+
+ this.categoryPage = undefined
+ this.currCategory = undefined
+ this.activePages = []
+ this.lastClickedOpen = undefined
+
+ this.activeCategory = undefined
+ }
+
+ onEnable() {
+ this.gui = new SoopyGui2()
+
+ // this.gui.isDebugEnabled = true
+
+ this.registerCommand("soopyv2", this.openCommand)
+ this.registerCommand("soopy", this.openCommand)
+ this.registerCommand("snoopyv2", this.openCommand)
+ this.registerCommand("snoopy", this.openCommand)
+
+ this.mainWindowElement = new SoopyBoxElement().setLocation(0.25, 0.1, 0.5, 0.8)
+
+ this.mainWindowElement.addEvent(new SoopyOpenGuiEvent().setHandler(() => { this.goToPageNum(0, false) }))
+
+ //###############################################################################################
+ // Category Page
+ //###############################################################################################
+
+ this.categoryPage = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+
+ let title = new SoopyTextElement().setText("§0SoopyV2!").setMaxTextScale(3).setLocation(0.1, 0.05, 0.5, 0.1)
+ this.categoryPage.addChild(title)
+
+ let discordButton = new ButtonWithArrow().setText("§0Discord").setLocation(0.7, 0.05 + 0.0125, 0.25, 0.075)
+ discordButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(
+ new java.net.URI("https://discord.gg/dfSMq96RSN")
+ );
+ }))
+ this.categoryPage.addChild(discordButton)
+
+ this.buttonListElm = new SoopyGuiElement().setLocation(0.1, 0.2, 0.8, 0.8).setScrollable(true)
+ this.categoryPage.addChild(this.buttonListElm)
+
+ //###############################################################################################
+ // Back button for all second pages
+ //###############################################################################################
+
+ this.backButton = new TextWithArrow().setText("§0Back").setLocation(0.01, -0.2, 0.1, 0.1).setDirectionRight(false)
+ let backButtonEvent = new SoopyMouseClickEvent().setHandler(() => { this.clickedBackButton() })
+ this.backButton.addEvent(backButtonEvent)
+
+ this.mainWindowElement.addChild(this.categoryPage)
+
+ this.sidebarPage = new SoopyBoxElement().setLocation(0.3, 0.1, 0.3, 0.8)
+ // this.sidebarPage.visable = false
+
+ this.gui.element.addChild(this.sidebarPage)
+ this.gui.element.addChild(this.mainWindowElement)
+
+ this.mainWindowElement.addChild(this.backButton)
+
+ this.updateButtons()
+ }
+
+ openCommand(page) {
+ this.gui.open()
+
+ if (page) {
+ this.getPages().forEach(p => {
+ if (p.name.replace(/ /g, "_").toLowerCase() === page.toLowerCase()) {
+ this.clickedOpen(p, false)
+ }
+ })
+ }
+ }
+
+ getPages() {
+ return categoryManager.arr
+ }
+
+ updateButtons() {
+ if (!this.buttonListElm) return;
+
+ this.buttonListElm.children = []
+
+ if (this.getPages()) this.getPages().forEach((p, i) => {
+ let settingsButton = new ButtonWithArrow().setText("§0" + p.name).setLocation(0, 0.175 * i, 1, 0.15)
+ settingsButton.addEvent(new SoopyMouseClickEvent().setHandler(() => { this.clickedOpen(p) }))
+ this.buttonListElm.addChild(settingsButton)
+ })
+ }
+
+ clickedOpen(category, anim = true) {
+ if (!this.lastClickedOpen) this.lastClickedOpen = 0
+ if (Date.now() - this.lastClickedOpen < 100) return //Stopping infinite loop where button getting reset causes click event to get fired again
+ this.lastClickedOpen = Date.now()
+
+ let theParent = this.mainWindowElement.innerObjectPaddingThing || this.mainWindowElement
+ theParent.children = []
+
+ this.mainWindowElement.addChild(this.categoryPage)
+
+ this.activePages = category.pages
+ this.currCategory = category
+
+ Object.values(this.activePages).forEach(p => {
+ this.mainWindowElement.addChild(p)
+ })
+
+ this.mainWindowElement.addChild(this.backButton)
+
+ category.onOpen()
+
+ this.goToPageNum(1, anim)
+ }
+
+ onDisable() {
+ this.gui.delete()
+
+ this.gui = undefined
+
+ this.currentPage = 0
+ this.backButton = undefined
+ this.activePages = []
+ this.currCategory = undefined
+ this.lastClickedOpen = undefined
+ }
+
+ clickedBackButton() {
+ this.goToPageNum(this.currentPage - 1)
+ }
+
+ goToPage(page, animate = true) {
+ let pageNum = page._soopyAddonsPageId
+
+ if (pageNum == this.currentPage) {
+ return
+ }
+
+ this.currentPage = pageNum
+
+ this.getPages().forEach((p) => {
+ Object.values(p.pages).forEach((e, i) => {
+ e.location.location.x.set(i - pageNum + 1, animate ? 500 : 0)
+ })
+ })
+ this.categoryPage.location.location.x.set(-pageNum, animate ? 500 : 0)
+
+ this.backButton.location.location.y.set((pageNum === 0 || !this.currCategory.showBackButton) ? -0.2 : 0, animate ? 500 : 0)
+ }
+ goToPageNum(pageNum, animate = true) {
+ if (pageNum < 0) return;
+
+ this.currentPage = pageNum
+ if (pageNum === 0) {
+ this.currCategory = undefined
+ this.closeSidebarPage()
+ this.updateButtons()
+ }
+
+ this.getPages().forEach((p) => {
+ Object.values(p.pages).forEach((e, i) => {
+ e.location.location.x.set(i - pageNum + 1, animate ? 500 : 0)
+ })
+ })
+ this.categoryPage.location.location.x.set(-pageNum, animate ? 500 : 0)
+
+ this.backButton.location.location.y.set((pageNum === 0 || !this.currCategory.showBackButton) ? -0.2 : 0, animate ? 500 : 0)
+
+ if (this.currCategory) this.currCategory.onOpenPage(pageNum)
+ }
+ openSidebarPage(child) {
+ this.sidebarPage.location.location.x.set(0.625, 500)
+ this.mainWindowElement.location.location.x.set(0.075, 500)
+
+ // this.sidebarPage.visable = true
+ this.sidebarPage.clearChildren()
+ this.sidebarPage.addChild(child)
+ }
+
+ closeSidebarPage() {
+ this.sidebarPage.location.location.x.set(0.3, 500)
+ this.mainWindowElement.location.location.x.set(0.25, 500)
+
+ this.sidebarPage.clearChildren()
+ // this.sidebarPage.visable = false
+ }
+}
+
+module.exports = {
+ class: new SoopyGui()
+} \ No newline at end of file
diff --git a/src/features/soopyGui/metadata.json b/src/features/soopyGui/metadata.json
new file mode 100644
index 0000000..8f2ace4
--- /dev/null
+++ b/src/features/soopyGui/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Settings",
+ "description": "Adds a settings gui with toggles for all the modules, also renders each individual module's settings",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/spamHider/index.js b/src/features/spamHider/index.js
new file mode 100644
index 0000000..697e571
--- /dev/null
+++ b/src/features/spamHider/index.js
@@ -0,0 +1,351 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent";
+import Feature from "../../featureClass/class";
+import soopyV2Server from "../../socketConnection";
+import { fetch } from "../../utils/networkUtils";
+import ToggleSetting from "../settings/settingThings/toggle";
+
+class SpamHider extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.hideMessages = []
+ this.hideMessagesRexex = []
+ this.moveMessages = []
+ this.moveMessagesRexex = []
+
+ this.moveMessagesDict = {
+ all: []
+ }
+ this.hideMessagesDict = {
+ all: []
+ }
+
+ this.hideMessagesSetting = new ToggleSetting("Hide some messages", "This will completely remove some spammy messages from chat", true, "completely_hide_spam", this)
+ this.moveMessagesSetting = new ToggleSetting("Move some messages to spam hider", "This will move some (potentially) usefull messages into a 'second chat'", true, "move_spam", this)
+ this.moveChatMessages = new ToggleSetting("Move spammed chat messages to spam hider", "This will move messages spammed in hubs to spam hider\n(eg the website advertisment bots)", true, "move_spam_chat", this)
+ this.textShadowSetting = new ToggleSetting("Spam Hider Text Shadow", "Whether to give the spam hider text shadow", true, "spam_text_shadow", this)
+ this.showFriendMessages = new ToggleSetting("Show friend message", "should it show friend join/leave message", false, "spam_text_friend", this)
+ this.showGuildMessages = new ToggleSetting("Show guild message", "should it show guild mate join/leave message", false, "spam_text_guild", this)
+ this.showPetLevelUpMessage = new ToggleSetting("Show pet level message", "should it show pet level up message", false, "spam_text_pet_level", this)
+ this.removeBlocksInTheWay = new ToggleSetting("Remove limited tp msg", "completely erases 'There are blocks in the way!' message from gui", false, "limited_tp_msg", this)
+ this.showAutoPetRule = new ToggleSetting("Show autopet rule", "Should it show autopet rule messages", false, "autopet_msg", this)
+
+ this.SpamHiderMessagesRenderer = new SpamHiderMessagesRenderer()
+ this.textShadowSetting.toggleObject.addEvent(new SoopyContentChangeEvent().setHandler((newVal, oldVal, resetFun) => {
+ this.SpamHiderMessagesRenderer.textShadow = this.textShadowSetting.getValue()
+ }))
+
+ this.loadSpamMessages()
+
+ this.registerChat("${*}", this.onChat)
+
+ this.registerEvent("renderOverlay", this.renderOverlay).registeredWhen(() => this.moveMessagesSetting.getValue())
+
+ // this.registerChat("&r${userandrank}&r&f: ${message}&r", this.chatPlayerMessage)
+ }
+
+ // chatPlayerMessage(userandrank, message, e){
+ // if(!this.FeatureManager.features["generalSettings"]) return
+ // if(userandrank.includes(">")) return
+ // if(message.length < 10) return //Short messages like 'LOL' are bound to get repeated
+
+ // let msg = sha256(message + "This is a salt PogU")
+
+ // if(soopyV2Server.spammedMessages.includes(msg)){
+ // if(this.moveChatMessages.getValue()){
+ // this.SpamHiderMessagesRenderer.addMessage(ChatLib.getChatMessage(e, true))
+ // cancel(e)
+ // }
+ // return
+ // }
+
+ // if(this.FeatureManager.features["generalSettings"].class.sendChatSetting && this.FeatureManager.features["generalSettings"].class.sendChatSetting.getValue()){
+ // soopyV2Server.sendMessageToServer(msg, sha256(this.FeatureManager.features["dataLoader"].class.stats["Server"] + "This is a salt PogU"))
+ // }
+ // }
+
+ onChat(e) {
+ let msg = ChatLib.getChatMessage(e, true).replace(/§/g, "&").replace(/(?:^&r)|(?:&r$)/g, "")
+ if (msg.length > 1000) return //performance
+
+ //&r&aFriend > &r&6Soopyboo32 &r&ejoined.&r
+ if (this.showFriendMessages.getValue() && msg.includes("&aFriend")) return
+
+ //&r&2Guild > &r&6Soopyboo32 &r&ejoined.&r
+ if (this.showGuildMessages.getValue() && msg.includes("&2Guild")) return
+
+ //&r&aYour &r&6Golden Dragon &r&alevelled up to level &r&9200&r&a!&r
+ if (this.showPetLevelUpMessage.getValue() && msg.includes("&alevelled up")) return
+
+ //&r&cThere are blocks in the way!&r
+ //completely erases this
+ if (this.removeBlocksInTheWay.getValue() && msg.includes("There are blocks in the way!")) {
+ cancel(e)
+ return
+ }
+
+ //&cAutopet &eequipped your &7[Lvl 200] &6Golden Dragon&e! &a&lVIEW RULE&r
+ if (this.showAutoPetRule.getValue() && msg.includes("&cAutopet")) return
+
+ if (this.hideMessagesSetting.getValue()) {
+ // console.log("testing " + (this.hideMessagesDict[msg.substring(0,5)]?.length || 0) + this.hideMessagesDict.all.length + " hide messages")
+ this.hideMessagesDict[msg.substring(0, 5)]?.forEach(regex => {
+ if (regex.test(msg)) {
+ cancel(e)
+ return
+ }
+ })
+ this.hideMessagesDict.all.forEach(regex => {
+ if (regex.test(msg)) {
+ cancel(e)
+ return
+ }
+ })
+ }
+
+ if (this.moveMessagesSetting.getValue()) {
+ // console.log("testing " + (this.moveMessagesDict[msg.substring(0,5)]?.length || 0) + this.moveMessagesDict.all.length + " spam messages")
+ this.moveMessagesDict[msg.substring(0, 5)]?.forEach(regex => {
+ if (regex.test(msg)) {
+ this.SpamHiderMessagesRenderer.addMessage(msg)
+ cancel(e)
+ return
+ }
+ })
+ this.moveMessagesDict.all.forEach(regex => {
+ if (regex.test(msg)) {
+ this.SpamHiderMessagesRenderer.addMessage(msg)
+ cancel(e)
+ return
+ }
+ })
+ }
+ }
+ renderOverlay() { //TODO: move this to java
+ this.SpamHiderMessagesRenderer.render(100, 100, 1, 1)
+ }
+
+ async loadSpamMessages() {
+ let messages = await fetch("http://soopy.dev/api/soopyv2/spamHiderMessages.json").json()
+ this.hideMessages = messages.hideMessages
+ this.moveMessages = messages.moveMessages
+
+ this.hideMessagesDict = {
+ all: []
+ }
+
+ this.hideMessagesRexex = []
+ this.hideMessages.forEach(message => {
+ let regex = new RegExp(message.replace(/[\\^$*+?.()|[\]{}]/g, '$&')
+ .replace(/\$\{\*\}/g, "(?:.+)"))
+ if (!message.substring(0, 5).includes("$")) {
+ if (!this.hideMessagesDict[message.substring(0, 5)]) this.hideMessagesDict[message.substring(0, 5)] = []
+ this.hideMessagesDict[message.substring(0, 5)].push(regex)
+ } else {
+ this.hideMessagesDict.all.push(regex)
+ }
+ this.hideMessagesRexex.push(regex)
+ })
+
+ this.moveMessagesDict = {
+ all: []
+ }
+
+ this.moveMessagesRexex = []
+ this.moveMessages.forEach(message => {
+ let regex = new RegExp(message.replace(/[\\^$*+?.()|[\]{}]/g, '$&')
+ .replace(/\$\{\*\}/g, "(?:.+)"))
+
+ if (!message.substring(0, 5).includes("$")) {
+ if (!this.moveMessagesDict[message.substring(0, 5)]) this.moveMessagesDict[message.substring(0, 5)] = []
+ this.moveMessagesDict[message.substring(0, 5)].push(regex)
+ } else {
+ this.moveMessagesDict.all.push(regex)
+ }
+ this.moveMessagesRexex.push(regex)
+ })
+ }
+
+ initVariables() {
+ this.hideMessages = undefined
+ this.hideMessagesRexex = undefined
+ this.moveMessages = undefined
+ this.moveMessagesRexex = undefined
+ this.SpamHiderMessagesRenderer = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+class SpamHiderMessagesRenderer {
+ constructor() {
+ this.messages = []
+ this.x = 0 //offset from corner, not absolute location
+ this.y = 0 //offset from corner, not absolute location
+ this.scale = 1
+ this.corner = 2
+
+ this.lastRender = 0
+
+ this.textShadow = true
+ }
+
+ addMessage(str) {
+ this.messages.push([str, Date.now(), this.y])
+ }
+
+ render() {
+ Renderer.drawString("", -100, -100)//Fixes skytils issue //idk if this is still needed, it was in old code and imma just leave it ig
+
+ let now = Date.now()
+ let animDiv = (now - this.lastRender) / 1000
+ this.lastRender = now
+ let swidth = Renderer.screen.getWidth()
+ let sheight = Renderer.screen.getHeight()
+
+ //loop over all messages backwards
+ for (let i = this.messages.length - 1; i >= 0; i--) {
+ let message = this.messages[i]
+
+ let [str, time, height] = message
+
+ time = now - time
+
+ let messageWidth = Renderer.getStringWidth(ChatLib.removeFormatting(str))
+
+ let x = 0;
+ let y = 0;
+ if (this.corner === 0) { //top left
+ x = 20
+ this.messages[i][2] = height + (((this.messages.length - i) * -10) - height) * (animDiv * 5)
+ }
+ if (this.corner === 1) { //top right
+ x = swidth - 20 - messageWidth
+ this.messages[i][2] = height + (((this.messages.length - i) * -10) - height) * (animDiv * 5)
+ }
+ if (this.corner === 2) { //bottom right
+ x = swidth - 20 - messageWidth
+ this.messages[i][2] = height + (((this.messages.length - i) * 10) - height) * (animDiv * 5)
+ }
+
+ let animOnOff = 0
+ if (time < 500) {
+ animOnOff = 1 - (time / 500)
+ }
+ if (time > 3500) {
+ animOnOff = ((time - 3500) / 500)
+ }
+
+ animOnOff *= 90
+ animOnOff += 90
+
+ animOnOff = animOnOff * Math.PI / 180;
+
+ animOnOff = Math.sin(animOnOff)
+
+ animOnOff *= -1
+ animOnOff += 1
+
+ if (this.corner === 0) { //top left
+ x += ((animOnOff * -1) * (messageWidth + 30))
+ y = 30 - (height)
+ }
+ if (this.corner === 1) { //top right
+ x += (animOnOff * (messageWidth + 30))
+ y = 30 - (height)
+ }
+ if (this.corner === 2) { //bottom right
+ x += (animOnOff * (messageWidth + 30))
+ y = sheight - 30 - (height)
+ }
+
+ if (this.textShadow) {
+ Renderer.drawStringWithShadow(str, x + this.x, y + this.y);
+ } else {
+ Renderer.drawString(str, x + this.x, y + this.y);
+ }
+
+ if (time > 4000) {
+ this.messages.shift()
+ }
+ }
+ }
+}
+
+module.exports = {
+ class: new SpamHider()
+}
+
+var sha256 = function a(b) {
+ function c(a, b) {
+ return (a >>> b) | (a << (32 - b));
+ }
+ for (
+ var d,
+ e,
+ f = Math.pow,
+ g = f(2, 32),
+ h = "length",
+ i = "",
+ j = [],
+ k = 8 * b[h],
+ l = (a.h = a.h || []),
+ m = (a.k = a.k || []),
+ n = m[h],
+ o = {},
+ p = 2;
+ 64 > n;
+ p++
+ )
+ if (!o[p]) {
+ for (d = 0; 313 > d; d += p) o[d] = p;
+ (l[n] = (f(p, 0.5) * g) | 0), (m[n++] = (f(p, 1 / 3) * g) | 0);
+ }
+ for (b += "\x80"; (b[h] % 64) - 56;) b += "\x00";
+ for (d = 0; d < b[h]; d++) {
+ if (((e = b.charCodeAt(d)), e >> 8)) return;
+ j[d >> 2] |= e << (((3 - d) % 4) * 8);
+ }
+ for (j[j[h]] = (k / g) | 0, j[j[h]] = k, e = 0; e < j[h];) {
+ var q = j.slice(e, (e += 16)),
+ r = l;
+ for (l = l.slice(0, 8), d = 0; 64 > d; d++) {
+ var s = q[d - 15],
+ t = q[d - 2],
+ u = l[0],
+ v = l[4],
+ w =
+ l[7] +
+ (c(v, 6) ^ c(v, 11) ^ c(v, 25)) +
+ ((v & l[5]) ^ (~v & l[6])) +
+ m[d] +
+ (q[d] =
+ 16 > d
+ ? q[d]
+ : (q[d - 16] +
+ (c(s, 7) ^ c(s, 18) ^ (s >>> 3)) +
+ q[d - 7] +
+ (c(t, 17) ^ c(t, 19) ^ (t >>> 10))) |
+ 0),
+ x =
+ (c(u, 2) ^ c(u, 13) ^ c(u, 22)) +
+ ((u & l[1]) ^ (u & l[2]) ^ (l[1] & l[2]));
+ (l = [(w + x) | 0].concat(l)), (l[4] = (l[4] + w) | 0);
+ }
+ for (d = 0; 8 > d; d++) l[d] = (l[d] + r[d]) | 0;
+ }
+ for (d = 0; 8 > d; d++)
+ for (e = 3; e + 1; e--) {
+ var y = (l[d] >> (8 * e)) & 255;
+ i += (16 > y ? 0 : "") + y.toString(16);
+ }
+ return i;
+};
diff --git a/src/features/spamHider/metadata.json b/src/features/spamHider/metadata.json
new file mode 100644
index 0000000..78031d8
--- /dev/null
+++ b/src/features/spamHider/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Spam Hider",
+ "description": "A couple of features to help clean up your chat",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": false,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/specialMining/index.js b/src/features/specialMining/index.js
new file mode 100644
index 0000000..147193a
--- /dev/null
+++ b/src/features/specialMining/index.js
@@ -0,0 +1,461 @@
+import Feature from "../../featureClass/class";
+import SettingBase from "../settings/settingThings/settingBase";
+import { numberWithCommas, timeNumber } from "../../utils/numberUtils";
+import HudTextElement from "../hud/HudTextElement";
+import LocationSetting from "../settings/settingThings/location";
+import ToggleSetting from "../settings/settingThings/toggle";
+import { delay } from "../../utils/delayUtils";
+import TextSetting from "../settings/settingThings/textSetting";
+import { drawBoxAtBlock, drawFilledBox } from "../../utils/renderUtils";
+import RenderLib2D from "../../utils/renderLib2d";
+import { f, m } from "../../../mappings/mappings";
+
+class PowderAndScatha extends Feature {
+ constructor() {
+ super();
+ }
+
+ onEnable() {
+ this.initVariables();
+ new SettingBase("Chest Miner", "Powder mining feature here are made mainly for powder chest grinding", undefined, "chest_mining_info", this);
+ this.compactedChat = new ToggleSetting("Compact Powder Messages", "same as the one in skytils but support following setting", false, "compact_powder_chat", this)
+ this.fixChatForDoublePowder = new ToggleSetting("Fix Chat Messages During Double Powder", "so it's the correct amount of powder you received during the event", false, "fix_chat_dpowder", this)
+ this.fixChatForDoublePowderSuffix = new TextSetting("Suffix of previous message", "(so you can tell )change it yourself!", "&a(&b2X Powder&a)", "chat_dpowder_suffix", this, "(none)", false).requires(this.fixChatForDoublePowder);
+ this.PowderElement = new ToggleSetting("Powder Mining Info Hud (MAIN TOGGLE)", "This will show your current powder mining section (only in CH)", true, "powder_mining_hud", this).contributor("EmeraldMerchant");
+ this.PowderOverlayElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.PowderElement)
+ .setLocationSetting(new LocationSetting("Powder Mining Info Hud Location", "Allows you to edit the location of Powder Mining Info Hud", "powder_mining_hud_location", this, [10, 50, 1, 1]).requires(this.PowderElement).editTempText(`&b2x Powder: &cINACTIVE\n&aChests: &b32\n&bMithril: &d12,768\n&bGems: &d21,325`).contributor("EmeraldMerchant"));
+ this.hudElements.push(this.PowderOverlayElement);
+ this.PowderOverlayElement.disableRendering()
+
+ new SettingBase("/resetpowderdata", "to reset powder mining data", undefined, "reset_powder_data_command_info", this).requires(this.PowderElement);
+ this.resetPowderWhenLeaveCH = new ToggleSetting("Reset Powder When Left CH", "Should it reset powder hud whenever you left ch", false, "reset_powder_when_left_ch", this).requires(this.PowderElement);
+ this.resetPowderWhenLeaveGame = new ToggleSetting("Reset Powder When Left Game", "Should it reset powder hud whenever you left game", false, "reset_powder_when_left_game", this).requires(this.PowderElement);
+ this.chestUncoverAlert = new ToggleSetting("Alert When You Dug a Chest Out", "so you don't miss it", false, "chest_uncover_alert", this).requires(this.PowderElement);
+ this.chestUnlockHelper = new ToggleSetting("Chest unlock helper", "so you don't miss it", true, "chest_unlock_help", this).requires(this.PowderElement);
+ this.chestUncoverAlertSound = new ToggleSetting("Alert Sound for Chest Alert", "should the alert also play a sound? (sound: levelup)", false, "chest_uncover_alert_sound", this).requires(this.chestUncoverAlert);
+ this.hideGemstoneMessage = new ToggleSetting("Gemstone Messages Hider", "like: &r&aYou received &r&f16 &r&f❈ &r&fRough Amethyst Gemstone&r&a.&r", false, "gemstone_message_hider", this).requires(this.PowderElement)
+ this.showFlawlessGemstone = new ToggleSetting("Gemstone Messages Hider Show Flawless", "should the hider ^ ignore flawless gemstones?", false, "gemstone_show_flawless", this).requires(this.hideGemstoneMessage)
+ this.hideWishingCompassMessage = new ToggleSetting("Wishing Compass Message Hider", "like: &r&aYou received &r&f1 &r&aWishing Compass&r&a.&r", false, "compass_message_hider", this).requires(this.PowderElement)
+ this.hideAscensionRope = new ToggleSetting("Ascension Rope Hider", "like: &r&aYou received &r&f1 &r&9Ascension Rope&r&a.&r", false, "ascension_rope_hider", this).requires(this.PowderElement)
+
+ this.registerChat("&r&aYou received ${thing}&r&a.&r", (thing, e) => {
+ if (this.hideGemstoneMessage.getValue() && thing.endsWith("Gemstone") && (this.showFlawlessGemstone.getValue() ? !thing.includes("Flawless") : true)) cancel(e)
+ if (this.hideWishingCompassMessage.getValue() && thing.endsWith("Wishing Compass")) cancel(e)
+ if (this.hideAscensionRope.getValue() && thing.endsWith("Ascension Rope")) cancel(e)
+ })
+
+ this.registerStep(true, 1, () => {
+ if (this.FeatureManager.features["dataLoader"].class.area === "Crystal Hollows") {
+ if (!this.leftCH && !this.inCrystalHollows) {
+ this.foundWither = false
+ this.inCrystalHollows = true
+ }
+ } else if (this.inCHfromChest) {
+ this.inCrystalHollows = true
+ this.dPowder = 0
+ this.inCHfromChest = false
+ }
+ })
+ this.dPowder = 0;
+ this.sL = Renderer.getStringWidth(" ")
+
+ this.overlayLeft = []
+ this.overlayRight = []
+
+ this.leftCH = false;
+ this.registerEvent("worldLoad", () => {
+ if (this.inCrystalHollows) {
+ this.leftCH = true;
+ this.inCrystalHollows = false;
+ if (this.resetPowderWhenLeaveCH.getValue()) {
+ this.resetMiningData("powder")
+ }
+ this.chests.clear()
+ } else this.leftCH = false
+ })
+
+ this.miningData = {}
+ this.saveMiningData = () => {
+ new Thread(() => {
+ FileLib.write("soopyAddonsData", "miningData.json", JSON.stringify(this.miningData));
+ }).start();
+ }
+ this.miningData = JSON.parse(FileLib.read("soopyAddonsData", "miningData.json") || "{}") || {}
+ if (!this.miningData.powder) this.miningData.powder = { chests: 0, mithril: 0, gemstone: 0 }
+ this.expRateInfo = []
+ this.mythrilRate = 0
+ this.gemstoneRate = 0
+ this.chestRate = 0
+ this.saveMiningData();
+
+ this.registerCommand("resetpowderdata", () => {
+ this.resetMiningData("powder");
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Successfully reset powder data.")
+ }, this)
+
+ this.registerChat("&r&aYou uncovered a treasure chest!&r", (e) => {
+ if (!this.inCrystalHollows) {
+ this.inCrystalHollows = true
+ this.inCHfromChest = true
+ }
+ if (this.chestUncoverAlert.getValue()) Client.showTitle("&aTreasure Chest!", "", 0, 60, 20);
+ if (this.chestUncoverAlertSound.getValue()) World.playSound("random.levelup", 1, 1);
+ })
+
+ this.registerChat("&r&r&r${space}&r&b&l2X POWDER ${status}!&r", (space, status, e) => {
+ if (status.removeFormatting() === "STARTED") {
+ this.dPowder = Date.now() + 15 * 1000 * 60
+ } else this.dPowder = 0
+ })
+ this.inCHfromChest = false;
+ this.registerChat("&r&6You have successfully picked the lock on this chest!&r", (e) => {
+ this.miningData.powder.chests++
+ delay(100, () => {
+ this.expRateInfo.push([Date.now(), this.miningData.powder.mithril, this.miningData.powder.gemstone])
+ if (this.expRateInfo.length > 20) this.expRateInfo.shift()
+
+ let [time, mythril, gemstone] = this.expRateInfo[0]
+
+ this.mythrilRate = (this.miningData.powder.mithril - mythril) / (Date.now() - time)
+ this.gemstoneRate = (this.miningData.powder.gemstone - gemstone) / (Date.now() - time)
+ this.chestRate = (this.miningData.powder.chestRate - chestRate) / (Date.now() - time)
+ })
+ })
+
+ this.lastPowderReceived = { mithril: 0, gemstone: 0 }
+ this.lastPowderReceivedExecuted = false;
+ this.registerChat("&r&aYou received &r&b+${amount} &r&aMithril Powder&r", (amount, e) => {
+ let p = (this.dPowder ? 2 : 1) * parseInt(amount)
+ this.miningData.powder.mithril += p
+ if (this.compactedChat.getValue()) {
+ cancel(e)
+ this.lastPowderReceived.mithril += p
+ return
+ }
+ if (this.fixChatForDoublePowder.getValue() && this.dPowder) {
+ cancel(e)
+ let suffix = "";
+ if (this.fixChatForDoublePowderSuffix.getValue() !== "") suffix = this.fixChatForDoublePowderSuffix.getValue()
+ ChatLib.chat(`&r&aYou received &r&b+${2 * amount} &r&aMithril Powder&r ${suffix}`)
+ }
+ })
+ this.registerChat("&r&aYou received &r&b+${amount} &r&aGemstone Powder&r", (amount, e) => {
+ let p = (this.dPowder ? 2 : 1) * parseInt(amount)
+ this.miningData.powder.gemstone += p
+ if (this.compactedChat.getValue()) {
+ cancel(e)
+ this.lastPowderReceived.gemstone += p
+ return
+ }
+ if (this.fixChatForDoublePowder.getValue() && this.dPowder) {
+ cancel(e)
+ let suffix = "";
+ if (this.fixChatForDoublePowderSuffix.getValue() !== "") suffix = this.fixChatForDoublePowderSuffix.getValue()
+ ChatLib.chat(`&r&aYou received &r&b+${2 * amount} &r&aGemstone Powder&r ${suffix}`)
+ }
+ })
+
+ this.registerStep(true, 5, this.compactPowderChat)
+
+ this.chests = new Map()
+
+
+ this.registerEvent("renderOverlay", this.renderOverlay)
+ this.registerEvent("renderWorld", () => {
+ if (!this.inCrystalHollows || !this.chestUnlockHelper.getValue()) return
+
+ let del = []
+ for (let key of this.chests.keys()) {
+ let pos = this.chests.get(key)
+ let size = 0.2
+ let { x1, y1, x2, y2 } = RenderLib2D.calculateBoundingBox(new net.minecraft.util.AxisAlignedBB(pos[1] - size / 2, pos[2] - size / 2, pos[3] - size / 2, pos[1] + size / 2, pos[2] + size / 2, pos[3] + size / 2))
+
+ let hovered = (x1 < Renderer.screen.getWidth() / 2 && x2 > Renderer.screen.getWidth() / 2 && y1 < Renderer.screen.getHeight() / 2 && y2 > Renderer.screen.getHeight() / 2)
+
+ drawFilledBox(pos[1], pos[2] - size / 2, pos[3], size, size, hovered ? 0 : 255, hovered ? 255 : 0, 0, 1, false)
+
+ if (Date.now() - pos[0] > 5000) {
+ del.push(key)
+ }
+ }
+
+ del.forEach(k => this.chests.delete(k))
+ })
+ this.registerEvent("spawnParticle", this.spawnParticle)
+
+ //==============================================Scatha=Feature=Below================================================================
+
+ if (!this.miningData.scatha) this.miningData.scatha = { total_worms: 0, worms: 0, scathas: 0, rare: 0, epic: 0, legandary: 0, since_scatha: 0, since_pet: 0 }
+ this.saveMiningData();
+
+ this.scathaMain = new ToggleSetting("SCATHA!", "This is the main toggle of Scatha Feature", false, "scatha_main", this);
+
+ this.scathaCounter = new ToggleSetting("Scatha Counter Hud", "This will show your scatha mining progress (only in CH)", false, "scatha_mining_hud", this).requires(this.scathaMain).contributor("EmeraldMerchant");
+ this.scathaCounterElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.scathaCounter)
+ .setLocationSetting(new LocationSetting("Scatha Counter Hud Location", "Allows you to edit the location of Scatha Counter Hud", "scatha_mining_hud_location", this, [10, 50, 1, 1]).requires(this.scathaCounter).editTempText(`&6Scatha Counter\n&bKills: 1,000\n&bWorms: 800\n&bScathas: 200\n&bSince Scatha: 10\n&9Rare Scatha Pets: 5\n&5Epic Scatha Pets: 3\n&6Leg Scatha Pets: 1\n&bSince Pet: 20`));
+ this.hudElements.push(this.scathaCounterElement);
+
+ this.wormEntity = undefined;
+ this.scathaHealth = new ToggleSetting("Scatha Health Hud", "This will show worm/scatha mob HP on screen", false, "scatha_hp_hud", this).requires(this.scathaMain).contributor("EmeraldMerchant");
+ this.scathaHealthElement = new HudTextElement()
+ .setText("")
+ .setToggleSetting(this.scathaHealth)
+ .setLocationSetting(new LocationSetting("Scatha Health Hud Location", "Allows you to edit the location of Scatha Health Hud", "scatha_hp_hud_location", this, [10, 50, 1, 1]).requires(this.scathaHealth).editTempText(`&8[&7Lv5&8] &cWorm &e5&c❤`));
+ this.hudElements.push(this.scathaHealthElement);
+
+ new SettingBase("/scathaset <thing> <value>", "This command will change values in the counter", undefined, "scatha_cmd", this).requires(this.scathaMain);
+ new SettingBase("/ss <thing> <value> works too", "you can press TAB for <thing> auto-complete", undefined, "scatha_cmd2", this).requires(this.scathaMain);
+ this.scathaCmdComp = ["worms", "scathas", "rare", "epic", "legandary", "since_scatha", "since_pet"]
+ this.registerCommand("scathaset", (thing, value) => this.scathaCmd(thing, value), this.scathaCmdComp);
+ this.registerCommand("ss", (thing, value) => this.scathaCmd(thing, value), this.scathaCmdComp);
+
+ this.wormSpawnedWarn = new ToggleSetting("Worm/Scatha Spawned Alert", "make a title and a sound when a worm/scatha spawned", false, "worm_spawned_alert", this).requires(this.scathaMain);
+ this.registerChat("&r&7&oYou hear the sound of something approaching...&r", this.wormSpawning);
+ this.wormSpawnedChatMessage = new ToggleSetting("Worm/Scatha Spawned Chat Message", "if a chat info should be sent when a worm/scatha spawned", false, "worm_spawned_chat_message", this).requires(this.scathaMain);
+ this.petDroppedAlert = new ToggleSetting("Pet Dropped Alert", "Big title when a scatha pet dropped", false, "scatha_pet_dropped_alert", this).requires(this.scathaMain);
+ this.colorToRarity = { 9: "rare", 5: "epic", 6: "legandary" }
+ //&r&6&lPET DROP! &r&9Scatha &r&b(+&r&b291% &r&b✯ Magic Find&r&b)&r
+ this.registerChat("&r&6&lPET DROP! &r&${rarity}Scatha &r&b(+&r&b${mf}% &r&b✯ Magic Find${end}", (rarity, mf, end, e) => {
+ let r = this.colorToRarity[rarity]
+ this.miningData.scatha[r]++
+ this.miningData.scatha.since_pet = 0
+ this.saveMiningData()
+ if (this.petDroppedAlert.getValue()) {
+ World.playSound('note.pling', 1, 1);
+ Client.showTitle(`${rarity}${r.toUpperCase()} SCATHA PET!`, ChatLib.getChatMessage(e), 0, 100, 10);
+ }
+ })
+
+ this.registerStep(true, 2, this.step2fps);
+ this.registerStep(true, 3, this.wormStep);
+ this.registerStep(true, 5, this.scathaHP);
+ }
+
+ spawnParticle(particle, type, event) {
+ if (this.inCrystalHollows && this.chestUnlockHelper.getValue() && particle.toString().startsWith("EntityCrit2FX,")) {
+ if (World.getBlockAt(particle.getX() + 1, particle.getY(), particle.getZ()).type.getID() === 54
+ || World.getBlockAt(particle.getX() - 1, particle.getY(), particle.getZ()).type.getID() === 54
+ || World.getBlockAt(particle.getX(), particle.getY(), particle.getZ() + 1).type.getID() === 54
+ || World.getBlockAt(particle.getX(), particle.getY(), particle.getZ() - 1).type.getID() === 54) {
+ this.chests.set(Math.floor(particle.getX()) + "," + Math.floor(particle.getY()) + "," + Math.floor(particle.getZ()), [Date.now(), particle.getX(), particle.getY(), particle.getZ()])
+ cancel(event)
+ }
+ }
+ }
+
+ resetMiningData(type) {
+ if (type === "powder") {
+ Object.keys(this.miningData.powder).forEach(thing => this.miningData.powder[thing] = 0)
+ this.expRateInfo = []
+ } else if (type === "scatha") {
+ Object.keys(this.miningData.scatha).forEach(thing => this.miningData.scatha[thing] = 0)
+ }
+ }
+
+ renderOverlay() {
+ if (this.PowderOverlayElement.isEnabled()) {
+ let width = Renderer.getStringWidth("&b2x Powder: &cINACTIVE")
+
+ let x = this.PowderOverlayElement.locationSetting.x
+ let y = this.PowderOverlayElement.locationSetting.y
+ let scale = this.PowderOverlayElement.locationSetting.scale
+
+ Renderer.retainTransforms(true)
+ Renderer.scale(scale)
+ Renderer.translate(x / scale, y / scale)
+
+ this.overlayLeft.forEach((l, i) => {
+ Renderer.drawStringWithShadow(l, 0, 10 * i)
+ })
+
+ this.overlayRight.forEach((l, i) => {
+ Renderer.drawStringWithShadow(l, width - Renderer.getStringWidth(l), 10 * i)
+ })
+
+ Renderer.retainTransforms(false)
+ }
+ }
+
+ step2fps() {
+ if (!this.foundWither) {
+ World.getAllEntitiesOfType(net.minecraft.entity.boss.EntityWither)?.forEach(e => {
+ if (e.getName().startsWith("§e§lPASSIVE EVENT §b§l2X POWDER §e§lRUNNING FOR §a§l")) {
+ this.dPowder = Date.now();
+ let time = ChatLib.removeFormatting(e.getName()).split("RUNNING FOR ").pop()
+
+ let [m, s] = time.split(":")
+ this.dPowder += 1000 * parseInt(s)
+ this.dPowder += 60 * 1000 * parseInt(m)
+ };
+ this.foundWither = true;
+ });
+ }
+
+ this.overlayLeft = []
+ this.overlayRight = []
+
+ if (this.PowderElement.getValue() && this.inCrystalHollows) {
+ this.overlayLeft.push(`&b2x Powder:`)
+ this.overlayRight.push(this.dPowder ? "&a" + timeNumber(Math.max(0, this.dPowder - Date.now())) : "&cINACTIVE")
+
+ if (this.miningData.powder.chests) {
+ let c = this.miningData.powder.chests
+
+ this.overlayLeft.push(`&aChests:`)
+ this.overlayRight.push(`&b${numberWithCommas(c)}`)
+ }
+ if (this.miningData.powder.mithril) {
+ let m = this.miningData.powder.mithril
+
+ this.overlayLeft.push(`&bMithril:`)
+ this.overlayRight.push(`&d${numberWithCommas(m)}`)
+ }
+ if (this.miningData.powder.gemstone) {
+ let g = this.miningData.powder.gemstone
+
+ this.overlayLeft.push(`&bGems:`)
+ this.overlayRight.push(`&d${numberWithCommas(g)}`)
+ }
+ if (this.mythrilRate) {
+ this.overlayLeft.push(`&bMithril/h:`)
+ this.overlayRight.push(`&d${numberWithCommas(Math.round(this.mythrilRate * 1000 * 60 * 60))}`)
+ }
+ if (this.gemstoneRate) {
+ this.overlayLeft.push(`&bGems/h:`)
+ this.overlayRight.push(`&d${numberWithCommas(Math.round(this.gemstoneRate * 1000 * 60 * 60))}`)
+ }
+ if (this.chestRate) {
+ this.overlayLeft.push(`&bChests/h:`)
+ this.overlayRight.push(`&d${numberWithCommas(Math.round(this.chestRate * 1000 * 60 * 60))}`)
+ }
+ }
+ if (this.scathaCounter.getValue() && this.inCrystalHollows) {
+ let tempText = `&6Scatha Counter\n&bKills: ${this.miningData.scatha.total_worms}\n&bWorms: ${this.miningData.scatha.worms}\n&bScathas: ${this.miningData.scatha.scathas}\n&bSince Scatha: ${this.miningData.scatha.since_scatha}\n`
+ if (this.miningData.scatha.rare > 0) tempText += `&9Rare Scatha Pets: ${this.miningData.scatha.rare}\n`
+ if (this.miningData.scatha.epic > 0) tempText += `&5Epic Scatha Pets: ${this.miningData.scatha.epic}\n`
+ if (this.miningData.scatha.legandary > 0) tempText += `&6Leg Scatha Pets: ${this.miningData.scatha.legandary}`
+ if (this.miningData.scatha.rare + this.miningData.scatha.epic + this.miningData.scatha.legandary > 0) tempText += `&bSince Pet: ${this.miningData.scatha.since_pet}`
+ this.scathaCounterElement.setText(tempText)
+ }
+ }
+
+ compactPowderChat() {
+ if (this.lastPowderReceived.mithril > 0 && this.lastPowderReceived.gemstone > 0 && !this.lastPowderReceivedExecuted) {
+ this.lastPowderReceivedExecuted = true
+ delay(300, () => {
+ let m = this.lastPowderReceived.mithril
+ let g = this.lastPowderReceived.gemstone
+ let msg = ""
+ if (g > 0) msg += `&r&aYou received &r&b+${g} &r&aGemstone `
+ if (m > 0) {
+ if (!msg) msg += `&r&aYou received &r&b+${m} &r&aMithril `
+ else msg += `and &r&b+${m} &r&aMithril `
+ }
+ msg += `Powder&r`
+ if (this.dPowder) {
+ let suffix = "";
+ if (this.fixChatForDoublePowderSuffix.getValue() !== "") suffix = this.fixChatForDoublePowderSuffix.getValue()
+ ChatLib.chat(`${msg} ${suffix}`)
+ } else ChatLib.chat(msg)
+ this.lastPowderReceived = { mithril: 0, gemstone: 0 }
+ this.lastPowderReceivedExecuted = false
+ })
+ }
+ }
+
+ scathaCmd(thing, value) {
+ if (!this.scathaCmdComp.includes(thing) || parseInt(value) === NaN) {
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Invalid inputs! Usage: /scathaset <thing> <value>")
+ return
+ }
+ let v = parseInt(value);
+ this.miningData.scatha[thing] = v;
+ this.miningData.scatha.total_worms = this.miningData.scatha.worms + this.miningData.scatha.scathas
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Successfully set " + thing + " to " + v + "!")
+ this.saveMiningData();
+ }
+
+ wormSpawning() {
+ if (this.wormSpawnedWarn.getValue()) {
+ World.playSound('note.pling', 1, 1);
+ Client.showTitle("&cWorm Spawning", "", 0, 20, 10);
+ this.wormSpawned = true
+ }
+ }
+
+ wormStep() {
+ if (!this.inCrystalHollows || !this.scathaMain.getValue()) {
+ this.scathaCounterElement.setText("")
+ return
+ }
+ if (!this.wormSpawned) return
+ World.getAllEntitiesOfType(net.minecraft.entity.item.EntityArmorStand).forEach(entity => {
+ let name = entity.getName()
+ //§8[§7Lv5§8] §cWorm§r §e5§c❤
+ if (name.startsWith("§8[§7Lv5§8] §cWorm")) {
+ if (this.wormSpawnedChatMessage.getValue()) ChatLib.chat("&c&lWorm Spawned. (Since Scatha: " + (this.miningData.scatha.since_scatha + 1) + ")");
+ if (this.wormSpawnedWarn.getValue()) Client.showTitle("&c&lWorm Spawned!", "", 0, 20, 10);
+ this.miningData.scatha.total_worms++;
+ this.miningData.scatha.worms++;
+ this.miningData.scatha.since_scatha++;
+ this.saveMiningData()
+ this.wormSpawned = false;
+ if (this.scathaHealth.getValue()) this.wormEntity = entity
+ }
+ if (name.startsWith("§8[§7Lv10§8] §cScatha")) {
+ if (this.wormSpawnedChatMessage.getValue()) ChatLib.chat("&c&lScatha Spawned.");
+ if (this.wormSpawnedWarn.getValue()) Client.showTitle("&c&lScatha Spawned!", "", 0, 20, 10);
+ this.miningData.scatha.total_worms++;
+ this.miningData.scatha.scathas++;
+ this.miningData.scatha.since_pet++;
+ this.miningData.scatha.since_scatha = 0
+ this.saveMiningData()
+ this.wormSpawned = false;
+ if (this.scathaHealth.getValue()) this.wormEntity = entity
+ }
+ });
+ }
+
+ scathaHP() {
+ let tempText = ""
+ if (this.scathaHealth.getValue()) {
+ if (this.wormEntity && this.wormEntity.getEntity()[f.isDead]) {
+ this.wormEntity = undefined;
+ }
+ } else if (this.wormEntity) {
+ this.wormEntity = undefined;
+ }
+ if (this.wormEntity) {
+ tempText = this.wormEntity.getName()
+ this.scathaHealthElement.setText(tempText)
+ } else {
+ this.scathaHealthElement.setText("")
+ }
+ }
+
+ initVariables() {
+ this.hudElements = [];
+ this.inCrystalHollows = false;
+ this.foundWither = true;
+ this.dPowder = 0;
+ this.wormSpawned = false;
+ this.wormEntity = undefined;
+ }
+
+ onDisable() {
+ this.hudElements.forEach(h => h.delete())
+ this.saveMiningData();
+ if (this.resetPowderWhenLeaveGame.getValue()) {
+ this.resetMiningData("powder");
+ }
+ this.initVariables();
+ }
+}
+
+module.exports = {
+ class: new PowderAndScatha(),
+};
diff --git a/src/features/specialMining/metadata.json b/src/features/specialMining/metadata.json
new file mode 100644
index 0000000..0e311c0
--- /dev/null
+++ b/src/features/specialMining/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "SpecialMining",
+ "description": "Powder & Scatha Mining",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 1
+} \ No newline at end of file
diff --git a/src/features/statHistoryGui/index.js b/src/features/statHistoryGui/index.js
new file mode 100644
index 0000000..0390772
--- /dev/null
+++ b/src/features/statHistoryGui/index.js
@@ -0,0 +1,189 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import BoxWithLoading from "../../../guimanager/GuiElement/BoxWithLoading";
+import BoxWithTextAndDescription from "../../../guimanager/GuiElement/BoxWithTextAndDescription";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import TextBox from "../../../guimanager/GuiElement/TextBox";
+import SoopyKeyPressEvent from "../../../guimanager/EventListener/SoopyKeyPressEvent";
+import { firstLetterWordCapital } from "../../utils/stringUtils";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import { fetch } from "../../utils/networkUtils";
+import SoopyImageElement from "../../../guimanager/GuiElement/SoopyImageElement";
+import renderLibs from "../../../guimanager/renderLibs";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import Dropdown from "../../../guimanager/GuiElement/Dropdown";
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent";
+
+class StatHistoryGUI extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new StatGraphPage()
+
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class StatGraphPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Stat Graphs"
+
+ this.pages = [this.newPage()]
+
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Stat Graphs").setMaxTextScale(3).setLocation(0.1, 0.05, 0.6, 0.1))
+ let button = new ButtonWithArrow().setText("§0Open in browser").setLocation(0.7, 0.05, 0.2, 0.1)
+ this.pages[0].addChild(button)
+
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(
+ new java.net.URI("https://soopy.dev/stathistory")
+ );
+ }))
+
+ this.nameInput = new TextBox().setPlaceholder("Click to search").setLocation(0.1, 0.225, 0.8, 0.1)
+ this.pages[0].addChild(this.nameInput)
+
+ this.nameInput.addEvent(new SoopyKeyPressEvent().setHandler((key, keyId) => {
+ if (this.nameInput.text.selected && keyId === 28) {
+ this.playerLoad = this.nameInput.text.text
+ this.nameInput.setText("")
+ this.updateData(this.playerLoad)
+ }
+ }))
+
+ this.statArea = new SoopyGuiElement().setLocation(0.05, 0.325, 0.9, 0.675).setScrollable(true)
+ this.pages[0].addChild(this.statArea)
+
+ this.loadingElm = new BoxWithLoading().setLocation(0.35, 0.4, 0.3, 0.2)
+ this.errorElm = new BoxWithTextAndDescription().setLocation(0.2, 0.3, 0.6, 0.4).setText("Error!").setDesc("An error occured while loading the data!")
+ this.statArea.addChild(this.loadingElm)
+
+ this.playerLoad = undefined
+
+ this.finaliseLoading()
+ }
+
+ async updateData(player, profIn) {
+ this.closeSidebarPage()
+
+ this.playerLoad = player
+
+ this.statArea._scrollAmount = 0
+ this.statArea.location.scroll.y.set(0, 100)
+
+ this.statArea.clearChildren()
+ this.statArea.addChild(this.loadingElm)
+
+ let playerData = await fetch("http://soopy.dev/api/v2/player/" + player).json()
+
+ if (player !== this.playerLoad) return
+
+ if (!playerData.success) {
+ this.statArea.clearChildren()
+ this.statArea.addChild(this.errorElm)
+ this.errorElm.setText("§0" + playerData.error.name)
+ this.errorElm.setDesc("§0" + playerData.error.description)
+ return
+ }
+ this.statArea.clearChildren()
+ let nameElm = new SoopyTextElement().setText(playerData.data.stats.nameWithPrefix.replace(/§f/g, "§7")).setMaxTextScale(2).setLocation(0.1, 0.05, 0.5, 0.1)
+ this.statArea.addChild(nameElm)
+ this.statArea.addChild(this.loadingElm)
+
+ let skyblockData = await fetch("http://soopy.dev/api/v2/player_skyblock/" + playerData.data.uuid).json()
+ if (player !== this.playerLoad) return
+
+ if (!skyblockData.success) {
+ this.statArea.clearChildren()
+ this.statArea.addChild(this.errorElm)
+ this.errorElm.setText("§0" + skyblockData.error.name)
+ this.errorElm.setDesc("§0" + skyblockData.error.description)
+ return
+ }
+
+ let selectedProf = profIn || skyblockData.data.stats.bestProfileId
+ let profOptions = {}
+ Object.keys(skyblockData.data.profiles).forEach(p => {
+ profOptions[p] = skyblockData.data.profiles[p].stats.cute_name
+ })
+
+ let profileSelect = new Dropdown().setOptions(profOptions).setSelectedOption(selectedProf).setLocation(0.6, 0.05, 0.3, 0.1).addEvent(new SoopyContentChangeEvent().setHandler(newval => {
+ this.updateData(player, newval)
+ }))
+ this.statArea.addChild(profileSelect)
+
+ fetch("http://soopy.dev/statgraphgenerations/" + playerData.data.uuid + "/" + selectedProf).json().then(graphData => {
+ if (player !== this.playerLoad) return
+
+ new Thread(() => {
+ let y = 0.2
+ Object.keys(graphData).forEach(type => {
+ let typeElm = new SoopyTextElement().setText("§0" + firstLetterWordCapital(type.replace("total", ""))).setMaxTextScale(2).setLocation(0.1, y, 0.8, 0.1)
+ this.statArea.addChild(typeElm)
+ y += 0.15
+ let graph = Object.keys(graphData[type])[0]
+ renderLibs.getImage(graphData[type][graph], true) //load image synchronously into cache so it knows the height
+ let graphElm = new SoopyImageElement()
+ this.statArea.addChild(graphElm)
+ graphElm.setImage(graphData[type][graph])
+
+ graphElm.setLocation(0.1, y, 0.8, 0.25)
+ graphElm.loadHeightFromImage()
+ y += graphElm.location.size.y.get() + 0.05
+
+ if (Object.keys(graphData[type]).length > 1) {
+ graphElm.setLore(["Click to show more graphs"])
+ graphElm.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ let sideBarElm = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+ sideBarElm.scrollable = true
+ this.openSidebarPage(sideBarElm)
+ let y2 = 0.05
+ new Thread(() => {
+ Object.keys(graphData[type]).forEach((graph, i) => {
+ if (i === 0) return
+ renderLibs.getImage(graphData[type][graph], true) //load image synchronously into cache so it knows the height
+ let graphElm = new SoopyImageElement()
+ sideBarElm.addChild(graphElm)
+ graphElm.setImage(graphData[type][graph])
+
+ graphElm.setLocation(0.1, y2, 0.8, 0.25)
+ graphElm.loadHeightFromImage()
+ y2 += graphElm.location.size.y.get() + 0.05
+ })
+ }).start()
+
+ }))
+ }
+
+ })
+ }).start()
+ })
+ }
+
+ onOpen() {
+ this.playerLoad = Player.getName()
+ this.updateData(Player.getName())
+ }
+}
+
+module.exports = {
+ class: new StatHistoryGUI()
+} \ No newline at end of file
diff --git a/src/features/statHistoryGui/metadata.json b/src/features/statHistoryGui/metadata.json
new file mode 100644
index 0000000..9345e0e
--- /dev/null
+++ b/src/features/statHistoryGui/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Stat History GUI",
+ "description": "Gui for Stat History",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/stat_next_to_name/index.js b/src/features/stat_next_to_name/index.js
new file mode 100644
index 0000000..063a67b
--- /dev/null
+++ b/src/features/stat_next_to_name/index.js
@@ -0,0 +1,193 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import Feature from "../../featureClass/class";
+import soopyV2Server from "../../socketConnection";
+import SettingBase from "../settings/settingThings/settingBase";
+import * as numberUtils from "../../utils/numberUtils";
+import DropdownSetting from "../settings/settingThings/dropdownSetting";
+import { fetch } from "../../utils/networkUtils"
+import ToggleSetting from "../settings/settingThings/toggle";
+
+class StatNextToName extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ new SettingBase("NOTE: A pink star thing (&d⚝§0)", "Means that player is also using SoopyV2", true, "stat_next_to_name_description", this)
+ this.statToShow = new DropdownSetting("Stat to show", "", "weight", "stat_selected_nexttoname", this, {
+ "weight": "Weight",
+ "catacombsLevel": "Catacombs Level",
+ "skillAvg": "Skill Average",
+ "skillAvgOver60": "Skill Average (Over 60)",
+ "totalSlayer": "Total Slayer Exp",
+ "networth": "Networth",
+ "classAverage": "Class Average",
+ "bestiary": "Bestiary"
+ })
+
+ this.decimals = {
+ "weight": 0,
+ "catacombsLevel": 2,
+ "skillAvg": 2,
+ "skillAvgOver60": 2,
+ "totalSlayer": 0,
+ "classAverage": 2,
+ "bestiary": 1,
+ "networth": "small"
+ }
+
+ this.userStats = {}
+
+ this.loadingStats = []
+ this.lastWorldLoad = undefined
+ this.apiKeyThing = new ToggleSetting("Use ur api key for data loading", "Max of 12 requests/min", true, "api_key_stat_load", this)
+
+ soopyV2Server.onPlayerStatsLoaded = (stats) => { this.playerStatsLoaded.call(this, stats) }
+
+ soopyV2Server.apithingo = async (uuid, packetId) => {
+ let key = this.FeatureManager.features["globalSettings"].class.apiKeySetting.getValue()
+ if (!key) return
+
+ let t = await fetch(`https://api.hypixel.net/skyblock/profiles?key=${key}&uuid=${uuid}`).text()
+ soopyV2Server.respondQueue(packetId, t)
+ }
+
+ this.registerStep(false, 1, this.loadPlayerStatsTick)
+ this.registerEvent("worldLoad", this.worldLoad)
+
+ this.registerEvent("playerJoined", this.playerJoined)
+
+ this.worldLoad()
+
+
+ // respondQueue(id, data) {
+ // this.sendData({
+ // type: "api",
+ // id,
+ // data
+ // })
+ // }
+
+ this.registerStep(false, 5, () => {
+ if (keyValid && this.apiKeyThing.getValue()) soopyV2Server.joinApiQ()
+ })
+
+ let keyValid = false
+ let key = this.FeatureManager.features["globalSettings"].class.apiKeySetting.getValue()
+ fetch("https://api.hypixel.net/key?key=" + key).json().then(d => {
+ if (d.success) {
+ keyValid = true
+ }
+ })
+ }
+
+ loadPlayerStatsTick() {
+
+ if (this.lastWorldLoad && Date.now() - this.lastWorldLoad > 1000) {
+ World.getAllPlayers().forEach(player => {
+ if (this.userStats[player.getUUID().toString().replace(/-/g, "")]) return
+ if (Player.getUUID().replace(/-/g, "").toString().substr(12, 1) !== "4") return
+ this.loadPlayerStatsCache(player.getUUID().toString().replace(/-/g, ""), player.getName())
+ })
+ this.lastWorldLoad = undefined
+ }
+
+ let nearestPlayer = undefined
+ let nearestPlayerName = undefined
+ let nearestDistance = Infinity
+
+ World.getAllPlayers().forEach(player => {
+ if (this.userStats[player.getUUID().toString().replace(/-/g, "")]) {
+ this.updatePlayerNametag(player)
+ return
+ }
+ if (this.loadingStats.includes(player.getUUID().toString().replace(/-/g, ""))) return
+ if (Player.getUUID().replace(/-/g, "").toString().substr(12, 1) !== "4") return
+
+ let dist = Math.pow(player.getX() - Player.getX(), 2) + Math.pow(player.getY() - Player.getY(), 2) + Math.pow(player.getZ() - Player.getZ(), 2)
+ if (dist < nearestDistance) {
+ nearestDistance = dist
+ nearestPlayer = player.getUUID().toString().replace(/-/g, "")
+ nearestPlayerName = player.getName()
+ }
+ })
+
+ if (nearestPlayer) {
+ this.loadPlayerStats(nearestPlayer, nearestPlayerName)
+ }
+ }
+
+ worldLoad() {
+ let playerStats = this.userStats[Player.getUUID().toString().replace(/-/g, "")]
+ this.userStats = {}
+ this.loadingStats = []
+ if (playerStats) {
+ this.userStats[Player.getUUID().toString().replace(/-/g, "")] = playerStats
+ }
+
+ this.lastWorldLoad = Date.now()
+
+ this.loadPlayerStatsCache(Player.getUUID().toString().replace(/-/g, ""), Player.getName())
+ }
+
+ playerJoined(player) {
+ if (player.getUUID().toString().replace(/-/g, "") === Player.getUUID().toString().replace(/-/g, "")) return
+ if (this.userStats[player.getUUID().toString().replace(/-/g, "")]) return
+ if (Player.getUUID().replace(/-/g, "").toString().substr(12, 1) !== "4") return
+
+ this.loadPlayerStatsCache(player.getUUID().toString().replace(/-/g, ""), player.getName())
+ }
+
+ updatePlayerNametag(player) {
+ let stats = this.userStats[player.getUUID().toString().replace(/-/g, "")]
+
+ let nameTagString = player.getName()
+
+ nameTagString += " &2["
+ if (stats.usingSoopyv2) nameTagString += "&d⚝&2"
+ if (stats.exists && stats[this.statToShow.getValue()] !== undefined && stats[this.statToShow.getValue()] !== null) {
+ if (this.decimals[this.statToShow.getValue()] === "small") {
+ nameTagString += numberUtils.addNotation("oneLetters", Math.round(stats[this.statToShow.getValue()]))
+ } else {
+ nameTagString += numberUtils.numberWithCommas(stats[this.statToShow.getValue()].toFixed(this.decimals[this.statToShow.getValue()]))
+ }
+ } else {
+ nameTagString += "?"
+ }
+ nameTagString += "]"
+ player.setNametagName(new TextComponent(nameTagString));
+ }
+
+ loadPlayerStats(uuid, username) {
+ // console.log("loading stats for " + uuid)
+ soopyV2Server.requestPlayerStats(uuid, username)
+ this.loadingStats.push(uuid)
+ }
+
+ loadPlayerStatsCache(uuid, username) {
+ // console.log("loading stats for " + uuid)
+ soopyV2Server.requestPlayerStatsCache(uuid, username)
+ }
+
+ playerStatsLoaded(stats) {
+ stats.bestiary /= 10
+ this.userStats[stats.uuid] = stats
+
+
+ World.getAllPlayers().forEach(player => {
+ if (player.getUUID().toString().replace(/-/g, "") === stats.uuid) {
+ this.updatePlayerNametag(player)
+ return
+ }
+ })
+ }
+
+ onDisable() {
+ this.userStats = undefined
+ }
+}
+
+module.exports = {
+ class: new StatNextToName()
+} \ No newline at end of file
diff --git a/src/features/stat_next_to_name/metadata.json b/src/features/stat_next_to_name/metadata.json
new file mode 100644
index 0000000..cd2f79c
--- /dev/null
+++ b/src/features/stat_next_to_name/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Stat next to name",
+ "description": "Shows a players skyblock stat next to their name in the world",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/streamsGUI/index.js b/src/features/streamsGUI/index.js
new file mode 100644
index 0000000..e47bbc3
--- /dev/null
+++ b/src/features/streamsGUI/index.js
@@ -0,0 +1,181 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement";
+import SoopyImageElement from "../../../guimanager/GuiElement/SoopyImageElement";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import BoxWithText from "../../../guimanager/GuiElement/BoxWithText";
+import { fetch } from "../../utils/networkUtils";
+
+class StreamsGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new StreamPage()
+
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class StreamPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Skyblock Streams"
+
+ this.pages = [this.newPage()]
+
+
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Skyblock Streams").setMaxTextScale(3).setLocation(0.1, 0.05, 0.6, 0.1))
+
+ let button = new ButtonWithArrow().setText("§0Open in browser").setLocation(0.7, 0.05, 0.2, 0.1)
+ this.pages[0].addChild(button)
+
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(
+ new java.net.URI("https://soopy.dev/skyblockstreams")
+ );
+ }))
+
+ this.streamsBox = new SoopyGuiElement().setLocation(0.1, 0.15, 0.8, 0.85)
+
+ this.streamsBox.setScrollable(true)
+
+ this.pages[0].addChild(this.streamsBox)
+
+ this.finaliseLoading()
+ }
+
+ async updateStreams() {
+ let streams = await fetch("http://soopy.dev/api/skyblockstreams").json()
+ this.streamsBox.clearChildren()
+
+ let y = 0
+
+ Object.keys(streams.twitch).forEach((channel, i) => {
+ let stream = streams.twitch[channel]
+
+ if (i % 2 === 0) {
+ let element = new StreamElement().setLocation(0, y, 0.45, 0.4).setStreamPage(this)
+ this.streamsBox.addChild(element)
+ element.setStream(stream, true)
+ }
+ if (i % 2 === 1) {
+ let element = new StreamElement().setLocation(0.55, y, 0.45, 0.4).setStreamPage(this)
+ this.streamsBox.addChild(element)
+ element.setStream(stream, true)
+ y += 0.45
+ }
+ })
+ }
+
+ openStreamSidebar(data) {
+ let sidebar = new SoopyGuiElement().setLocation(0, 0, 1, 1)
+
+ this.openSidebarPage(sidebar)
+
+ sidebar.addChild(new SoopyTextElement().setText("§0" + data.user_name).setMaxTextScale(3).setLocation(0.1, 0.05, 0.8, 0.1))
+
+ let title = new SoopyMarkdownElement().setText(data.title).setLocation(0.1, 0.15, 0.8, 0.1)
+ sidebar.addChild(title)
+
+ let image = new SoopyImageElement().setLocation(0.1, 0.15 + title.getHeight(), 0.8, 0.4).setImage(data.image)
+ sidebar.addChild(image)
+
+
+ let button = new ButtonWithArrow().setText("§0Watch on Twitch").setLocation(0.1, 0.15 + image.location.size.y.get() + title.getHeight(), 0.8, 0.1)
+ sidebar.addChild(button)
+
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(
+ new java.net.URI("https://twitch.tv/" + data.user_login)
+ );
+ }))
+
+ let language
+ if (data.language) {
+ language = new BoxWithText().setText("§0Language: " + data.language).setLocation(0.3, 0.25 + image.location.size.y.get() + title.getHeight(), 0.4, 0.1)
+
+ sidebar.addChild(language)
+ }
+
+ image.onImageHeightChange(() => {
+ button.location.location.y.set(0.15 + image.location.size.y.get() + title.getHeight())
+ if (language) language.location.location.y.set(0.25 + image.location.size.y.get() + title.getHeight())
+ }, this)
+ image.loadHeightFromImage()
+ }
+
+ onOpen() {
+ this.updateStreams()
+ }
+}
+
+class StreamElement extends SoopyBoxElement {
+ constructor() {
+ super()
+
+ this.streamData = undefined
+
+ this.channelElement = new SoopyTextElement().setLocation(0.1, 0.025, 0.8, 0.1).setMaxTextScale(10)
+
+ this.channelImg = new SoopyImageElement().setLocation(0.1, 0.125, 0.8, 0.2)
+
+ this.titleElement = new SoopyMarkdownElement().setLocation(0.1, 0.45, 0.8, 0.1)
+
+ this.channelImg.onImageHeightChange(() => {
+ this.titleElement.location.location.y.set(0.15 + this.channelImg.location.size.y.get())
+ }, this)
+
+ this.streamPage = undefined
+
+ this.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.streamPage.openStreamSidebar(this.streamData)
+ }))
+
+ this.addChild(this.channelElement)
+ this.addChild(this.titleElement)
+ this.addChild(this.channelImg)
+ }
+
+ setStream(stream, twitch) {
+ this.streamData = stream
+
+ this.titleElement.setText(stream.title)
+
+ this.channelElement.setText((twitch ? "§0" + stream.user_name : "§0" + stream.channelTitle) + (twitch ? "&7 - " + stream.viewer_count + " viewer" + (stream.viewer_count !== 1 ? "s" : "") : ""))
+
+ this.streamData.image = twitch ? `https://static-cdn.jtvnw.net/previews-ttv/live_user_${stream.user_login}-640x360.jpg` : stream.thumbnails.high.url
+ this.channelImg.setImage(this.streamData.image)
+ this.channelImg.loadHeightFromImage()
+
+ return this
+ }
+
+ setStreamPage(page) {
+ this.streamPage = page
+
+ return this
+ }
+}
+
+module.exports = {
+ class: new StreamsGui()
+} \ No newline at end of file
diff --git a/src/features/streamsGUI/metadata.json b/src/features/streamsGUI/metadata.json
new file mode 100644
index 0000000..3123559
--- /dev/null
+++ b/src/features/streamsGUI/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Streams gui",
+ "description": "Gui for skyblock streams",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/suggestionsGui/index.js b/src/features/suggestionsGui/index.js
new file mode 100644
index 0000000..4a31b11
--- /dev/null
+++ b/src/features/suggestionsGui/index.js
@@ -0,0 +1,217 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import SoopyTextElement from "../../../guimanager/GuiElement/SoopyTextElement";
+import Feature from "../../featureClass/class";
+import GuiPage from "../soopyGui/GuiPage";
+import BoxWithLoading from "../../../guimanager/GuiElement/BoxWithLoading";
+import BoxWithTextAndDescription from "../../../guimanager/GuiElement/BoxWithTextAndDescription";
+import SoopyGuiElement from "../../../guimanager/GuiElement/SoopyGuiElement";
+import PasswordInput from "../../../guimanager/GuiElement/PasswordInput";
+import SoopyKeyPressEvent from "../../../guimanager/EventListener/SoopyKeyPressEvent";
+import { numberWithCommas } from "../../utils/numberUtils";
+import { firstLetterWordCapital } from "../../utils/stringUtils";
+import SoopyBoxElement from "../../../guimanager/GuiElement/SoopyBoxElement";
+import SoopyMarkdownElement from "../../../guimanager/GuiElement/SoopyMarkdownElement";
+import SoopyMouseClickEvent from "../../../guimanager/EventListener/SoopyMouseClickEvent";
+import ButtonWithArrow from "../../../guimanager/GuiElement/ButtonWithArrow";
+import Dropdown from "../../../guimanager/GuiElement/Dropdown";
+import SoopyContentChangeEvent from "../../../guimanager/EventListener/SoopyContentChangeEvent";
+import { fetch } from "../../utils/networkUtils";
+
+let allowed = new Set(["dc8c39647b294e03ae9ed13ebd65dd29", "83c5626ede2d4754b86064d558809615"])
+
+class SuggestionGui extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ this.GuiPage = new SuggestionPage()
+
+ }
+
+ initVariables() {
+ this.GuiPage = undefined
+ }
+
+ onDisable() {
+ this.initVariables()
+ }
+}
+
+
+class SuggestionPage extends GuiPage {
+ constructor() {
+ super(7)
+
+ this.name = "Suggestions"
+
+ this.pages = [this.newPage()]
+
+ this.password = ""
+
+ if (allowed.has(Player.getUUID().toString().replace(/-/g, ""))) {
+ let elm = new PasswordInput().setPlaceholder("Suggestions").setLocation(0.125, 0.05, 0.3, 0.1)
+ this.pages[0].addChild(elm)
+
+ elm.addEvent(new SoopyKeyPressEvent().setHandler((key, keyId) => {
+ if (elm.text.selected && keyId === 28) {
+ this.password = elm.text.text
+ elm.setText("")
+ elm.text.selected = false
+ }
+ }))
+ } else {
+ this.pages[0].addChild(new SoopyTextElement().setText("§0Suggestions").setMaxTextScale(3).setLocation(0.125, 0.05, 0.3, 0.1))
+ }
+
+ let button = new ButtonWithArrow().setText("§0Suggest feature (Opens in browser)").setLocation(0.45, 0.05, 0.5, 0.1)
+ this.pages[0].addChild(button)
+
+ button.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ java.awt.Desktop.getDesktop().browse(
+ new java.net.URI("https://soopy.dev/soopyv2suggestion?uuid=" + Player.getUUID().toString().replace(/-/g, ""))
+ );
+ }))
+
+ this.suggestionElements = {}
+
+ this.suggestionsArea = new SoopyGuiElement().setLocation(0.05, 0.2, 0.9, 0.8).setScrollable(true)
+ this.pages[0].addChild(this.suggestionsArea)
+
+
+ fetch("http://soopy.dev/api/soopyv2/suggestionTags.json").json().then(data => {
+ this.tags = data
+ })
+
+ this.finaliseLoading()
+ }
+
+ async loadSuggestionPage() {
+ let data = await fetch("http://soopy.dev/api/soopyv2/suggestion/new").json()
+
+ this.suggestionElements = {}
+ this.suggestionsArea.clearChildren()
+
+ let i = 0
+
+ data.suggestions.forEach((suggestion) => {
+
+ if (suggestion.status === "pending_review" || suggestion.status === "closed") {
+ if (suggestion.uuid !== Player.getUUID().toString().replace(/-/g, "") && !allowed.has(Player.getUUID().toString().replace(/-/g, ""))) return
+ }
+
+ let box = new SoopyBoxElement().setLocation(0, 0.175 * i, 1, 0.15).setLore(["Click for more information + vote buttons"])
+ this.suggestionsArea.addChild(box)
+
+ let title = new SoopyTextElement().setText("§0" + suggestion.title + " §7(" + this.tags.suggestionTags[suggestion.tag] + ")").setMaxTextScale(2).setLocation(0, 0, 0.8, 1)
+ box.addChild(title)
+
+ let popularity = new SoopyTextElement().setText("§0Opinion: " + numberWithCommas(suggestion.likes - suggestion.dislikes)).setMaxTextScale(2).setLocation(0.85, 0, 0.1, 1)
+ box.addChild(popularity)
+
+ this.suggestionElements[suggestion._id] = {
+ title: title,
+ popularity: popularity
+ }
+
+ box.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.loadSuggestion(suggestion._id)
+ }))
+
+ i++
+ })
+ }
+
+ async loadSuggestion(id) {
+ let data = await fetch("http://soopy.dev/api/soopyv2/suggestion/" + id + "/user/" + Player.getUUID().toString().replace(/-/g, "")).json()
+ let sideBarElm = new SoopyGuiElement().setLocation(0, 0, 1, 1).setScrollable(true)
+ if (!data.success) {
+ sideBarElm.addChild(new SoopyTextElement().setText("§cError loading suggestion").setMaxTextScale(3).setLocation(0.5, 0.5, 0.5, 0.5))
+ this.openSidebarPage(sideBarElm)
+ return
+ }
+
+ this.suggestionElements[id].title.setText("§0" + data.suggestion.title + " §7(" + this.tags.suggestionTags[data.suggestion.tag] + ")")
+ this.suggestionElements[id].popularity.setText("§0Opinion: " + numberWithCommas(data.suggestion.likes - data.suggestion.dislikes))
+
+
+ let title = new SoopyTextElement().setText("§0" + data.suggestion.title + " §7(" + this.tags.suggestionTags[data.suggestion.tag] + ")").setMaxTextScale(2).setLocation(0.05, 0.05, 0.9, 0.1)
+ sideBarElm.addChild(title)
+
+ if (!allowed.has(Player.getUUID().toString().replace(/-/g, ""))) {
+ let suggestor = new SoopyTextElement().setText("§7Suggested by " + data.suggestion.username + " | Status: " + this.tags.statusTags[data.suggestion.status]).setMaxTextScale(1).setLocation(0.05, 0.15, 0.9, 0.05)
+ sideBarElm.addChild(suggestor)
+ } else {
+ let suggestor = new SoopyTextElement().setText("§7Suggested by " + data.suggestion.username + " | Status: ").setMaxTextScale(1).setLocation(0.05, 0.15, 0.6, 0.05)
+ sideBarElm.addChild(suggestor)
+
+ let drop = new Dropdown().setLocation(0.65, 0.13, 0.3, 0.09).setOptions({ ...this.tags.statusTags, "delete": "Delete" }).setSelectedOption(data.suggestion.status)
+ sideBarElm.addChild(drop)
+
+ drop.addEvent(new SoopyContentChangeEvent().setHandler((newVal) => {
+ if (newVal === "delete") {
+ fetch("http://soopy.dev/api/soopyv2/suggestion/" + id + "/delete/" + this.password).load()
+
+ this.loadSuggestionPage()
+ this.closeSidebarPage()
+ return
+ }
+ fetch("http://soopy.dev/api/soopyv2/suggestion/" + id + "/status/" + newVal + "/" + this.password).load()
+
+ this.loadSuggestion(id)
+ }))
+ }
+
+ let likesText = new SoopyTextElement().setText("§0Dislikes: " + numberWithCommas(data.suggestion.dislikes) + " Likes: " + numberWithCommas(data.suggestion.likes)).setMaxTextScale(1).setLocation(0.35, 0.225, 0.3, 0.1)
+ sideBarElm.addChild(likesText)
+ if (!data.suggestion.hasDisliked) {
+ let dislikeButton = new ButtonWithArrow().setDirectionRight(false).setText("§cDislike").setLocation(0.05, 0.225, 0.275, 0.1)
+ sideBarElm.addChild(dislikeButton)
+ dislikeButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.voteSuggestion(id, "dislike")
+ }))
+ } else {
+ let dislikeButton = new ButtonWithArrow().setDirectionRight(false).setText("§cUndislike").setLocation(0.05, 0.225, 0.275, 0.1)
+ sideBarElm.addChild(dislikeButton)
+ dislikeButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.voteSuggestion(id, "clear")
+ }))
+ }
+ if (!data.suggestion.hasLiked) {
+ let likeButton = new ButtonWithArrow().setText("§aLike").setLocation(0.675, 0.225, 0.275, 0.1)
+ sideBarElm.addChild(likeButton)
+ likeButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.voteSuggestion(id, "like")
+ }))
+ } else {
+ let likeButton = new ButtonWithArrow().setText("§aUnlike").setLocation(0.675, 0.225, 0.275, 0.1)
+ sideBarElm.addChild(likeButton)
+ likeButton.addEvent(new SoopyMouseClickEvent().setHandler(() => {
+ this.voteSuggestion(id, "clear")
+ }))
+ }
+
+
+ let description = new SoopyMarkdownElement().setText(data.suggestion.description).setLocation(0.05, 0.325, 0.9, 0.6)
+ sideBarElm.addChild(description)
+
+ this.openSidebarPage(sideBarElm)
+ }
+
+ voteSuggestion(id, type) {
+ fetch("http://soopy.dev/api/soopyv2/suggestion/" + id + "/vote/" + (type) + "/" + Player.getUUID().toString().replace(/-/g, "")).load()
+
+ this.loadSuggestion(id)
+ }
+
+ onOpen() {
+ this.loadSuggestionPage()
+ }
+}
+
+module.exports = {
+ class: new SuggestionGui()
+} \ No newline at end of file
diff --git a/src/features/suggestionsGui/metadata.json b/src/features/suggestionsGui/metadata.json
new file mode 100644
index 0000000..818b1c8
--- /dev/null
+++ b/src/features/suggestionsGui/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Suggestions gui",
+ "description": "Gui for voting on SoopyV2 suggestions",
+ "isHidden": true,
+ "isTogglable": false,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/waypoints/index.js b/src/features/waypoints/index.js
new file mode 100644
index 0000000..66bf0f5
--- /dev/null
+++ b/src/features/waypoints/index.js
@@ -0,0 +1,323 @@
+/// <reference types="../../../../CTAutocomplete" />
+/// <reference lib="es2015" />
+import { m } from "../../../mappings/mappings";
+import Feature from "../../featureClass/class";
+import { Waypoint } from "../../utils/renderJavaUtils";
+import { drawCoolWaypoint } from "../../utils/renderUtils";
+import { calculateDistanceQuick } from "../../utils/utils";
+import SettingBase from "../settings/settingThings/settingBase";
+import ToggleSetting from "../settings/settingThings/toggle";
+import minewaypoints_socket from "./minewaypoints_socket";
+
+
+let areas = {
+ "MinesofDivan": "Mines of Divan",
+ "LostPrecursorCity": "Lost Precursor City",
+ "JungleTemple": "Jungle Temple",
+ "GoblinQueensDen": "Goblin Queen's Den",
+ "Khazaddm": "Khazad-dûm",
+ "KingYolkar": "§6King Yolkar",
+ "BossCorleone": "§cBoss Corleone"
+}
+
+class Waypoints extends Feature {
+ constructor() {
+ super()
+ }
+
+ onEnable() {
+ this.initVariables()
+
+ new SettingBase("/addwaypoint [name] [x] [y] [z] [r?] [g?] [b?] [area?]", "Allows you to create a waypoint", undefined, "create_waypoint", this)
+ new SettingBase("/delwaypoint [name]", "Allows you to delete a waypoint", undefined, "delete_waypoint", this)
+ new SettingBase("/clearwaypoints", "Allows you to clear all the waypoints", undefined, "clear_waypoints", this)
+ new SettingBase("/savewaypoints", "Copys the waypoints to your clipboard", undefined, "save_waypoints", this)
+ new SettingBase("/loadwaypoints", "Loads waypoints from your clipboard", undefined, "load_waypoints", this)
+ this.showInfoInChat = new ToggleSetting("Show info in chat", "Should chat msg be sent when theres waypoint added/cleared/removed", true, "waypoints_send_message", this);
+
+ this.loadWaypointsFromSendCoords = new ToggleSetting("Load waypoints from /patcher sendcoords messages", "Will dissapear after 1min", true, "load_waypoints_from_sendcoords", this)
+ this.mineWaypointsSetting = new ToggleSetting("CH waypoints", "Will sync between users", true, "minwaypoints", this)
+
+ try {
+ this.userWaypoints = JSON.parse(FileLib.read("soopyAddonsData", "soopyv2userwaypoints.json") || "{}")
+ } catch (e) {
+ ChatLib.chat(this.messagePrefix + "&cYour waypoints file corrupted and could not be read! Resetting to defaults.")
+ this.userWaypoints = {}
+ }
+ this.userWaypointsHash = {}
+ this.userWaypointsAll = []
+ this.lastArea = undefined
+ this.userWaypointsArr = Object.values(this.userWaypoints)
+ this.updateWaypointsHashes()
+ this.waypointsChanged = false
+
+ this.patcherWaypoints = []
+
+ this.registerCommand("addwaypoint", (name, x = Math.floor(Player.getX()).toString(), y = Math.floor(Player.getY()).toString(), z = Math.floor(Player.getZ()).toString(), r = "0", g = "255", b = "0", area = "") => {
+ let lx = 0
+ let ly = 0
+ let lz = 0
+
+ if (Player.lookingAt().getX) {
+ lx = Player.lookingAt().getX()
+ ly = Player.lookingAt().getY()
+ lz = Player.lookingAt().getZ()
+
+ if (Player.lookingAt().getWidth) {
+ lx += -0.5
+ lz += -0.5
+ }
+ }
+
+ this.userWaypoints[name] = {
+ x: parseFloat(x.replace("l", lx).replace('p', Math.floor(Player.getX()))),
+ y: parseFloat(y.replace("l", ly).replace('p', Math.floor(Player.getY()))),
+ z: parseFloat(z.replace("l", lz).replace('p', Math.floor(Player.getZ()))),
+ r: parseInt(r) / 255,
+ g: parseInt(g) / 255,
+ b: parseInt(b) / 255,
+ area: area === "a" ? this.FeatureManager.features["dataLoader"].class.area : area.replace(/_/g, " "),
+ options: { name: ChatLib.addColor(name.replace(/_/g, " ")) }
+ }
+
+ this.userWaypointsArr = Object.values(this.userWaypoints)
+ this.waypointsChanged = true
+ this.updateWaypointsHashes()
+ if (this.showInfoInChat.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + "Added waypoint " + name + "!")
+ })
+
+ this.registerCommand("delwaypoint", (name) => {
+ delete this.userWaypoints[name]
+ this.userWaypointsArr = Object.values(this.userWaypoints)
+ this.waypointsChanged = true
+ this.updateWaypointsHashes()
+ if (this.showInfoInChat.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + "Deleted waypoint " + name + "!")
+ })
+ this.registerCommand("clearwaypoints", () => {
+ this.userWaypointsAll.forEach(w => w.stopRender())
+ Object.values(this.userWaypointsHash).forEach(a => a.forEach(w => w.stopRender()))
+ this.userWaypoints = {}
+ this.userWaypointsArr = []
+ this.waypointsChanged = true
+ this.updateWaypointsHashes()
+ if (this.showInfoInChat.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + "Cleared waypoints!")
+ })
+ this.registerCommand("savewaypoints", () => {
+ Java.type("net.minecraft.client.gui.GuiScreen")[m.setClipboardString](JSON.stringify(this.userWaypoints))
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Saved waypoints to clipboard!")
+ })
+ this.registerCommand("loadwaypoints", () => {
+ try {
+ this.userWaypoints = JSON.parse(Java.type("net.minecraft.client.gui.GuiScreen")[m.getClipboardString]())
+
+ this.userWaypointsArr = Object.values(this.userWaypoints)
+ this.waypointsChanged = true
+ this.updateWaypointsHashes()
+ if (this.showInfoInChat.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + "Loaded waypoints from clipboard!")
+ } catch (e) {
+ if (this.showInfoInChat.getValue()) ChatLib.chat(this.FeatureManager.messagePrefix + "Error loading from clipboard!")
+ console.log(JSON.stringify(e, undefined, 2))
+ console.log(e.stack)
+ }
+ })
+
+ this.ignorePlayers = new Set()
+
+ this.registerCommand("waypointignoreadd", (player) => {
+ this.ignorePlayers.add(player)
+ ChatLib.chat(this.FeatureManager.messagePrefix + "Added " + player + " to waypoint ignore list, this will be cleared next game start!")
+
+ this.patcherWaypoints = this.patcherWaypoints.filter(w => {
+ if (ChatLib.removeFormatting(w[1].params.name).trim().split(" ").pop() === player) {
+ w[1].stopRender()
+ return false
+ }
+
+ return true
+ })
+ })
+
+ this.registerChat("&r${*} &8> ${player}&f: &rx: ${x}, y: ${y}, z: ${z}", (player, x, y, z, e) => {
+ let p = ChatLib.removeFormatting(player).trim().split(" ").pop()
+ if (this.loadWaypointsFromSendCoords.getValue() && !this.ignorePlayers.has(p)) {
+ new TextComponent(this.FeatureManager.messagePrefix + "Loaded waypoint from &6" + p + "&7, &cCLICK HERE &7to ignore waypoints from them.").setClick("run_command", "/waypointignoreadd " + p).chat()
+ this.patcherWaypoints.push([Date.now(), new Waypoint(parseInt(x), parseInt(y), parseInt(ChatLib.removeFormatting(z)), 0, 0, 1, { name: ChatLib.addColor(player), showDist: true }).startRender()])
+ if (this.patcherWaypoints.length > 10) this.patcherWaypoints.shift()[1].stopRender()
+ }
+ })
+ this.registerChat("${player}&r&f: x: ${x}, y: ${y}, z: ${z}", (player, x, y, z, e) => {
+ if (player.includes(">")) return
+ let p = ChatLib.removeFormatting(player).trim().split(" ").pop()
+ if (this.loadWaypointsFromSendCoords.getValue() && !this.ignorePlayers.has(p)) {//parseInt(x), parseInt(y), parseInt(ChatLib.removeFormatting(z)), ChatLib.addColor(player)
+ new TextComponent(this.FeatureManager.messagePrefix + "Loaded waypoint from &6" + p + "&7, &cCLICK HERE &7to ignore waypoints from them.").setClick("run_command", "/waypointignoreadd " + p).chat()
+ this.patcherWaypoints.push([Date.now(), new Waypoint(parseInt(x), parseInt(y), parseInt(ChatLib.removeFormatting(z)), 0, 0, 1, { name: ChatLib.addColor(player), showDist: true }).startRender()])
+ if (this.patcherWaypoints.length > 10) this.patcherWaypoints.shift()[1].stopRender()
+ }
+ })
+
+ this.registerStep(false, 5, () => {
+ while (this.patcherWaypoints[0]?.[0] < Date.now() - 60000) {
+ this.patcherWaypoints.shift()[1].stopRender()
+ }
+ })
+
+ let lastX = 0
+ let lastY = 0
+ let lastZ = 0
+ let lastTick = 0
+ this.registerEvent("renderWorld", () => {
+ if (Math.round(Player.getX()) !== lastX
+ || Math.round(Player.getY()) !== lastY
+ || Math.round(Player.getZ()) !== lastZ
+ || Date.now() - lastTick > 500) {
+ lastX = Math.round(Player.getX())
+ lastY = Math.round(Player.getY())
+ lastZ = Math.round(Player.getZ())
+ lastTick = Date.now()
+
+ this.tickWaypoints()
+ }
+ })
+
+
+ this.lastSend = 0
+ this.locations = {}
+ minewaypoints_socket.setLocationHandler = (area, loc) => {
+ this.locations[area || loc[0].area] = loc;
+ }
+ let lastLoc = [0, 0, 0]
+ this.lastTp = 0
+ this.registerEvent("tick", () => {
+ try {
+ if (Scoreboard.getLines().length < 2) return;
+ let server = ChatLib.removeFormatting(Scoreboard.getLineByIndex(Scoreboard.getLines().length - 1)).split(" ")
+
+ if (server.length === 2) {
+ server = server[1].replace(/[^0-9A-z]/g, "")
+ } else {
+ return;
+ }
+
+ minewaypoints_socket.setServer(server, World.getWorld().func_82737_E())
+
+ let loc = [Player.getX(), Player.getY(), Player.getZ()]
+ if (calculateDistanceQuick(lastLoc, loc) > 20 ** 2) {
+ this.lastTp = Date.now()
+ }
+ lastLoc = loc
+
+ if (Date.now() - this.lastSend > 1000 && Date.now() - this.lastTp > 5000) {
+ Scoreboard.getLines().forEach(line => {
+ line = ChatLib.removeFormatting(line.getName()).replace(/[^0-9A-z]/g, "")
+ if (Object.keys(areas).includes(line)) {
+ minewaypoints_socket.setLocation(line, { x: Math.floor(Player.getX()), y: Math.floor(Player.getY()), z: Math.floor(Player.getZ()) })
+ }
+ })
+ this.lastSend = Date.now()
+ }
+ } catch (e) {
+ console.log("SOOPYV2MINEWAYPOINTS ERROR")
+ console.log(JSON.stringify(e, undefined, 2))
+ }
+ })
+
+ this.registerStep(false, 5, () => {
+
+ World.getAllEntities().forEach(e => {
+ if (Math.max(Math.abs(Player.getX() - e.getX()), Math.abs(Player.getY() - e.getY()), Math.abs(Player.getZ() - e.getZ())) > 20) return;
+
+ if (!this.locations["KingYolkar"]) {
+ if (ChatLib.removeFormatting(e.getName()) === "King Yolkar") {
+ minewaypoints_socket.setLocation("KingYolkar", { x: e.getX(), y: e.getY() + 3.5, z: e.getZ() })
+ }
+ }
+ if (ChatLib.removeFormatting(e.getName()).includes("Boss Corleone")) {
+ minewaypoints_socket.setLocation("BossCorleone", { x: e.getX(), y: e.getY() + 3.5, z: e.getZ() })
+ }
+ })
+ // console.log(JSON.stringify(locations, undefined, 2))
+ })
+
+ this.registerEvent("renderWorld", () => {
+ if (!this.mineWaypointsSetting.getValue()) return
+ if (this.FeatureManager.features["dataLoader"].class.area !== "Crystal Hollows") return
+ Object.values(this.locations).forEach(item => {
+ if (!item) return;
+ item.forEach(loc => {
+ // console.log(JSON.stringify(loc, undefined, 2))
+ if (loc.loc.x) {
+ drawCoolWaypoint(loc.loc.x, loc.loc.y, loc.loc.z, 0, 255, 0, { name: areas[loc.area] })
+ } else {
+ drawCoolWaypoint(loc.loc.minX / 2 + loc.loc.maxX / 2, loc.loc.minY / 2 + loc.loc.maxY / 2, loc.loc.minZ / 2 + loc.loc.maxZ / 2, 0, 255, 0, { name: areas[loc.area] })
+ }
+ })
+ })
+ }).registeredWhen(() => this.mineWaypointsSetting.getValue())
+
+ this.registerEvent("worldLoad", () => {
+ this.locations = {}
+ })
+ }
+
+ tickWaypoints() {
+ for (let waypoint of this.userWaypointsAll) {
+ waypoint.update()
+ }
+ for (let waypoint of this.patcherWaypoints) {
+ waypoint[1].update()
+ }
+ let area = this.FeatureManager.features["dataLoader"] ? this.FeatureManager.features["dataLoader"].class.area : "NONE"
+ if (this.lastArea && this.lastArea !== area) {
+ if (this.userWaypointsHash[this.lastArea]) {
+ for (let waypoint of this.userWaypointsHash[lastArea]) {
+ waypoint.stopRender()
+ }
+
+ }
+ }
+ this.lastArea = area
+
+ if (this.userWaypointsHash[area]) {
+ for (let waypoint of this.userWaypointsHash[area]) {
+ waypoint.update()
+ waypoint.startRender()
+ }
+ }
+ }
+
+ updateWaypointsHashes() {
+ this.userWaypointsAll.forEach(w => w.stopRender())
+ Object.values(this.userWaypointsHash).forEach(a => a.forEach(w => w.stopRender()))
+
+ this.userWaypointsAll = []
+ this.userWaypointsHash = {}
+ for (let waypoint of this.userWaypointsArr) {
+ if (!waypoint.area) {
+ this.userWaypointsAll.push(new Waypoint(waypoint.x, waypoint.y, waypoint.z, waypoint.r, waypoint.g, waypoint.b, waypoint.options).startRender())
+ } else {
+ if (!this.userWaypointsHash[waypoint.area]) this.userWaypointsHash[waypoint.area] = []
+ this.userWaypointsHash[waypoint.area].push(new Waypoint(waypoint.x, waypoint.y, waypoint.z, waypoint.r, waypoint.g, waypoint.b, waypoint.options))
+ }
+ }
+ }
+
+ initVariables() {
+
+ }
+
+ onDisable() {
+ this.userWaypointsAll.forEach(w => w.stopRender())
+ Object.values(this.userWaypointsHash).forEach(a => a.forEach(w => w.stopRender()))
+ this.patcherWaypoints.forEach(p => p[1].stopRender())
+
+ if (this.waypointsChanged) {
+ FileLib.write("soopyAddonsData", "soopyv2userwaypoints.json", JSON.stringify(this.userWaypoints))
+ }
+
+ this.initVariables()
+ }
+}
+
+module.exports = {
+ class: new Waypoints()
+}
diff --git a/src/features/waypoints/metadata.json b/src/features/waypoints/metadata.json
new file mode 100644
index 0000000..c162b72
--- /dev/null
+++ b/src/features/waypoints/metadata.json
@@ -0,0 +1,8 @@
+{
+ "name": "Waypoints",
+ "description": "Allows rendering + creation of custom waypoints",
+ "isHidden": false,
+ "isTogglable": true,
+ "defaultEnabled": true,
+ "sortA": 0
+} \ No newline at end of file
diff --git a/src/features/waypoints/minewaypoints_socket.js b/src/features/waypoints/minewaypoints_socket.js
new file mode 100644
index 0000000..0cc31e5
--- /dev/null
+++ b/src/features/waypoints/minewaypoints_socket.js
@@ -0,0 +1,49 @@
+import socketData from "../../../soopyApis/socketData";
+import WebsiteCommunicator from "../../../soopyApis/websiteCommunicator";
+
+class MineWayPointsServer extends WebsiteCommunicator {
+ constructor() {
+ super(socketData.serverNameToId.minewaypoints);
+
+ this.setLocationHandler = undefined
+ this.hypixelServer = undefined
+ this.lastSend = Date.now()
+ }
+
+ onConnect() {
+ this.hypixelServer = undefined
+ }
+
+ onData(data) {
+ switch (data.type) {
+ case "setLocation":
+ if (this.setLocationHandler) {
+ this.setLocationHandler(data.area, data.location);
+ }
+ break;
+ }
+ }
+
+ setLocation(area, loc) {
+ this.sendData({
+ type: "setLocation",
+ area: area,
+ location: loc
+ });
+ }
+
+ setServer(server, worldTime) {
+ if (this.hypixelServer === server && Date.now() - this.lastSend < 10000) return;
+
+ this.lastSend = Date.now()
+ this.hypixelServer = server
+ this.sendData({
+ type: "setServer",
+ server: server,
+ time: worldTime
+ });
+ }
+}
+
+global.soopyV2mineWayPointsServer = new MineWayPointsServer()
+export default global.soopyV2mineWayPointsServer; \ No newline at end of file
diff --git a/src/features/waypoints/pathfind.js b/src/features/waypoints/pathfind.js
new file mode 100644
index 0000000..42135d8
--- /dev/null
+++ b/src/features/waypoints/pathfind.js
@@ -0,0 +1,96 @@
+import FlatQueue from "../../datastructures/flatqueue"
+
+const directions = [
+ [1,0,0,1],
+ [0,1,0,1],
+ [0,0,1,1],
+ [-1,0,0,1],
+ [0,-1,0,1],
+ [0,0,-1,1],
+ [1,1,0,Math.SQRT2],
+ [1,-1,0,Math.SQRT2],
+ [-1,1,0,Math.SQRT2],
+ [-1,-1,0,Math.SQRT2],
+ [1,0,1,Math.SQRT2],
+ [1,0,-1,Math.SQRT2],
+ [-1,0,1,Math.SQRT2],
+ [-1,0,-1,Math.SQRT2],
+ [0,1,1,Math.SQRT2],
+ [0,1,-1,Math.SQRT2],
+ [0,-1,1,Math.SQRT2],
+ [0,-1,-1,Math.SQRT2]
+]
+
+class Path{
+ constructor(startLocation, endLocation){
+ this.startLocation = startLocation
+ this.endLocation = endLocation
+
+ this.points = []
+ }
+
+ getPoints(){
+ return this.points
+ }
+
+ calculate(){
+ this.locs = {}
+ this.points = []
+ this.locs[this.startLocation.x+","+this.startLocation.y+","+this.startLocation.z]= {
+ location: this.startLocation,
+ parent: null,
+ distance: 0,
+ huristic: (Math.abs(this.startLocation.x-this.endLocation.x)+Math.abs(this.startLocation.y-this.endLocation.y)+Math.abs(this.startLocation.z-this.endLocation.z))
+ }
+
+ this.heap = new FlatQueue()
+
+ this.heap.push(this.startLocation.x+","+this.startLocation.y+","+this.startLocation.z, this.locs[this.startLocation.x+","+this.startLocation.y+","+this.startLocation.z].huristic)
+
+ let i = 0
+ while(!this._iterate() && i<100000){
+ i++
+ }
+ }
+
+ _iterate(){
+ let currentKey = this.heap.pop()
+ let current = this.locs[currentKey]
+ if(current.location.x === this.endLocation.x && current.location.y === this.endLocation.y && current.location.z === this.endLocation.z){
+ this.points.push([current.location.x, current.location.y, current.location.z])
+ while(current.parent){
+ current = this.locs[current.parent]
+
+ this.points.push([current.location.x, current.location.y, current.location.z])
+ }
+ return true
+ }
+
+ for(let dir of directions){
+
+ let newLoc = {
+ x: current.location.x + dir[0],
+ y: current.location.y + dir[1],
+ z: current.location.z + dir[2]
+ }
+
+ if(!this.locs[newLoc.x + "," + newLoc.y + "," + newLoc.z]){
+
+ if(World.getBlockAt(newLoc.x, newLoc.y, newLoc.z).getType().getID() === 0 && World.getBlockAt(newLoc.x, newLoc.y+1, newLoc.z).getType().getID() === 0){
+ this.locs[newLoc.x + "," + newLoc.y + "," + newLoc.z] = {
+ location: newLoc,
+ parent: currentKey,
+ distance: current.distance+1,
+ huristic: Math.hypot((newLoc.x-this.endLocation.x),(newLoc.y-this.endLocation.y),(newLoc.z-this.endLocation.z))+(current.distance+dir[3])/2
+ }
+
+ this.heap.push(newLoc.x + "," + newLoc.y + "," + newLoc.z, this.locs[newLoc.x + "," + newLoc.y + "," + newLoc.z].huristic)
+ }
+ }
+ }
+
+ return false
+ }
+}
+
+export default Path \ No newline at end of file