Gauntlets
Key Required

Gauntlets

Miners World [Gauntlets]HippedScriptsHippedScripts 581 views 1.5K executesUpdated Jul 28, 2026
Unrated
00
Lua Script•5056 lines (122369 chars)
--//====================================================
--// OPTIMIERTES PARTICLE + MODEL/MESH + TNT ESP + ORE AUTO-FARM GUI
--//====================================================
--//
--// F4 = ESP ein/aus
--// F5 = Exportfenster öffnen/schließen
--// F6 = aktuellen Block debuggen
--// F7 = zuletzt gelernte Blockart kopieren
--// F8 = alle Presets als TXT speichern/kopieren
--// F9 = alle Mesh-Signaturen im Umkreis kopieren
--// F10 = Ore Auto-Farm ein/aus
--// Rechte Umschalttaste = Ore-Auswahl öffnen/schließen
--//
--// WICHTIG:
--// Nur dieses eine ESP-Script gleichzeitig benutzen.
--// Kein Server-Hopping. Zielauswahl, Bewegung und Mining laufen clientseitig.
--//
--//====================================================

--//====================================================
--// SETTINGS
--//====================================================

local SETTINGS = {
	ENABLED = true,

	ROOT = workspace:WaitForChild("Blocks"),
	BREAKING_NAME = "Breaking",

	-- Performance
	SCAN_RADIUS = 500,
	CELL_SIZE = 128,
	REFRESH_INTERVAL = 0.75,
	MOVE_THRESHOLD = 10,

	-- Sehr wichtig gegen FPS-Einbrüche:
	MAX_ACTIVE_HIGHLIGHTS = 120,
	MAX_ACTIVE_LABELS = 55,

	INITIAL_SCAN_BATCH_SIZE = 250,
	REINDEX_DELAY = 0.20,

	-- Rare und alles darüber anzeigen
	MINIMUM_RARITY = "Rare",

	-- Darstellung
	SHOW_HIGHLIGHT = true,
	SHOW_TEXT = true,

	-- Outline-only ist deutlich leichter als eine gefüllte Markierung.
	FILL_TRANSPARENCY = 1,
	OUTLINE_TRANSPARENCY = 0,

	BILLBOARD_SIZE = UDim2.fromOffset(210, 28),
	BILLBOARD_OFFSET = Vector3.new(0, 4, 0),
	LABEL_TEXT_SIZE = 14,

	-- Tasten
	TOGGLE_KEY = Enum.KeyCode.F4,
	EXPORT_PANEL_KEY = Enum.KeyCode.F5,
	DEBUG_KEY = Enum.KeyCode.F6,
	COPY_LAST_KEY = Enum.KeyCode.F7,
	EXPORT_ALL_KEY = Enum.KeyCode.F8,
	MESH_CATALOG_KEY = Enum.KeyCode.F9,

	-- Ore Auto-Farm
	AUTO_FARM_ENABLED = false,
	AUTO_FARM_SCAN_RADIUS = 1500,
	AUTO_FARM_MINE_DISTANCE = 14,
	AUTO_FARM_STOP_DISTANCE = 5,
	AUTO_FARM_GLIDE_SPEED = 300,
	AUTO_FARM_TARGET_REFRESH_INTERVAL = 0.22,
	AUTO_FARM_STUCK_TIMEOUT = 20,
	AUTO_FARM_IGNORE_TIME = 12,

	-- "HighestRarity" oder "Nearest"
	AUTO_FARM_PRIORITY = "HighestRarity",

	-- Bewegungs-/Zieleinstellungen
	AUTO_FARM_GLIDE_ENABLED = true,
	AUTO_FARM_NOCLIP_WHILE_GLIDING = true,
	AUTO_FARM_ROTATE_CHARACTER = true,

	-- Für das Mining-System aus deinem Beispiel muss die echte Kamera
	-- standardmäßig auf den Block zeigen. Im GUI kann dies abgeschaltet
	-- werden; MouseOnly funktioniert nur, wenn der Block bereits im Bild ist.
	AUTO_FARM_AIM_CAMERA = true,
	AUTO_FARM_RESTORE_CAMERA_AFTER_TARGET = false,

	-- Beim ersten Start werden Rare und höher ausgewählt.
	-- Werte: "RarePlus", "All" oder "None"
	AUTO_FARM_DEFAULT_SELECTION = "RarePlus",

	AUTO_FARM_TOGGLE_KEY = Enum.KeyCode.F10,
	AUTO_FARM_GUI_KEY = Enum.KeyCode.RightShift,

	-- Partikelerkennung
	REQUIRE_PARTICLE_ENABLED = false,
	PARTICLE_MAX_COLOR_DISTANCE = 0.45,

	PARTICLE_COLORS = {
		Uncommon = Color3.fromRGB(0, 255, 40),
		Rare = Color3.fromRGB(20, 80, 255),
		Epic = Color3.fromRGB(170, 20, 255),
		Legendary = Color3.fromRGB(255, 235, 0),
		Mythic = Color3.fromRGB(255, 25, 35),
	},

	-- Ziel-/Lernerkennung
	LEARN_STABILITY_TIME = 0.22,
	TARGET_SEARCH_SIZE = Vector3.new(12, 12, 12),
	TARGET_MAX_CENTER_DISTANCE = 3,

	-- Export
	AUTO_COPY_NEW_NAMED_BLOCK = true,
	EXPORT_GENERIC_PARTICLE_TYPES = true,
	EXPORT_ONLY_RARE_OR_HIGHER = false,
	EXPORT_FILE_PREFIX = "RareESP_Presets",
	MESH_CATALOG_FILE_PREFIX = "Nearby_Mesh_Catalog",
	AUTO_COPY_MESH_CATALOG_AFTER_INITIAL_SCAN = true,
	MESH_CATALOG_BATCH_SIZE = 250,
	INCLUDE_KNOWN_MESHES_IN_CATALOG = true,

	RARITY_ORDER = {
		"Common",
		"Uncommon",
		"Rare",
		"Epic",
		"Legendary",
		"Mythic",
		"Celestial",
		"Divine",
		"Godly",
		"Secret",
		"Exotic",
		"Unique",
	},

	RARITY_COLORS = {
		Common = Color3.fromRGB(180, 180, 180),
		Uncommon = Color3.fromRGB(40, 255, 40),
		Rare = Color3.fromRGB(25, 125, 255),
		Epic = Color3.fromRGB(180, 50, 255),
		Legendary = Color3.fromRGB(255, 225, 20),
		Mythic = Color3.fromRGB(255, 35, 65),
		Celestial = Color3.fromRGB(0, 255, 230),
		Divine = Color3.fromRGB(255, 255, 120),
		Godly = Color3.fromRGB(255, 210, 50),
		Secret = Color3.fromRGB(255, 255, 255),
		Exotic = Color3.fromRGB(255, 70, 210),
		Unique = Color3.fromRGB(255, 105, 120),
	},

	-- Bereits bekannte Schlüssel
	PRESETS = {
		-- Auto_Mythic_6236733 [Mythic]
		["C:A:120350511430348|75926406236733"] = {Name = "Auto_Mythic_6236733", Rarity = "Mythic"},
		["A:120350511430348|75926406236733"] = {Name = "Auto_Mythic_6236733", Rarity = "Mythic"},
		["M:120350511430348"] = {Name = "Auto_Mythic_6236733", Rarity = "Mythic"},
	
		-- Auto_Legendary_0035224 [Legendary]
		["C:A:76248862374417|106695970035224"] = {Name = "Auto_Legendary_0035224", Rarity = "Legendary"},
		["A:76248862374417|106695970035224"] = {Name = "Auto_Legendary_0035224", Rarity = "Legendary"},
		["M:76248862374417"] = {Name = "Auto_Legendary_0035224", Rarity = "Legendary"},
	
		-- Auto_Legendary_2167288 [Legendary]
		["C:A:12742167171|12742167288"] = {Name = "Auto_Legendary_2167288", Rarity = "Legendary"},
		["A:12742167171|12742167288"] = {Name = "Auto_Legendary_2167288", Rarity = "Legendary"},
		["M:12742167171"] = {Name = "Auto_Legendary_2167288", Rarity = "Legendary"},
	
		-- Kryptonite [Legendary]
		["C:A:85722211082056|73516819819176"] = {Name = "Kryptonite", Rarity = "Legendary"},
		["A:85722211082056|73516819819176"] = {Name = "Kryptonite", Rarity = "Legendary"},
		["M:85722211082056"] = {Name = "Kryptonite", Rarity = "Legendary"},
	
		-- Legenium [Legendary]
		["C:A:12668276833|;A:12668277632|"] = {Name = "Legenium", Rarity = "Legendary"},
		["A:12668276833|"] = {Name = "Legenium", Rarity = "Legendary"},
		["A:12668277632|"] = {Name = "Legenium", Rarity = "Legendary"},
		["M:12668276833"] = {Name = "Legenium", Rarity = "Legendary"},
		["M:12668277632"] = {Name = "Legenium", Rarity = "Legendary"},
	
		-- TNT [Legendary]
		["C:A:103944260415105|119829601923664"] = {Name = "TNT", Rarity = "Legendary"},
		["A:103944260415105|119829601923664"] = {Name = "TNT", Rarity = "Legendary"},
		["M:103944260415105"] = {Name = "TNT", Rarity = "Legendary"},
	
		-- Actinium [Epic]
		["C:A:139910347587420|114977117680281"] = {Name = "Actinium", Rarity = "Epic"},
		["A:139910347587420|114977117680281"] = {Name = "Actinium", Rarity = "Epic"},
		["M:139910347587420"] = {Name = "Actinium", Rarity = "Epic"},
	
		-- Adsimite [Epic]
		["C:A:12668283152|12668283419;A:12668283729|"] = {Name = "Adsimite", Rarity = "Epic"},
		["A:12668283152|12668283419"] = {Name = "Adsimite", Rarity = "Epic"},
		["A:12668283729|"] = {Name = "Adsimite", Rarity = "Epic"},
		["M:12668283152"] = {Name = "Adsimite", Rarity = "Epic"},
		["M:12668283729"] = {Name = "Adsimite", Rarity = "Epic"},
	
		-- Aridisols [Epic]
		["M:9580210470"] = {Name = "Aridisols", Rarity = "Epic"},
	
		-- Auto_Epic_2845098 [Epic]
		["C:A:12652840507|12652845098"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["C:A:12653102696|12652845098"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["C:A:12653102697|12652845098"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["A:12652840507|12652845098"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["A:12653102696|12652845098"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["A:12653102697|12652845098"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["M:12652840507"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["M:12653102696"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
		["M:12653102697"] = {Name = "Auto_Epic_2845098", Rarity = "Epic"},
	
		-- Auto_Epic_4994185 [Epic] – automatisch erkannt
		["C:A:134488664704515|109404194994185"] = {Name = "Auto_Epic_4994185", Rarity = "Epic"},
		["A:134488664704515|109404194994185"] = {Name = "Auto_Epic_4994185", Rarity = "Epic"},
		["M:134488664704515"] = {Name = "Auto_Epic_4994185", Rarity = "Epic"},
	
		-- Centromodor [Epic]
		["C:A:13063872749|;A:13063875332|13063875788;A:13063878026|;A:13063885053|;A:13063890452|"] = {Name = "Centromodor", Rarity = "Epic"},
		["A:13063872749|"] = {Name = "Centromodor", Rarity = "Epic"},
		["A:13063875332|13063875788"] = {Name = "Centromodor", Rarity = "Epic"},
		["A:13063878026|"] = {Name = "Centromodor", Rarity = "Epic"},
		["A:13063885053|"] = {Name = "Centromodor", Rarity = "Epic"},
		["A:13063890452|"] = {Name = "Centromodor", Rarity = "Epic"},
		["M:13063872749"] = {Name = "Centromodor", Rarity = "Epic"},
		["M:13063875332"] = {Name = "Centromodor", Rarity = "Epic"},
		["M:13063878026"] = {Name = "Centromodor", Rarity = "Epic"},
		["M:13063885053"] = {Name = "Centromodor", Rarity = "Epic"},
		["M:13063890452"] = {Name = "Centromodor", Rarity = "Epic"},
	
		-- Rohoccaim [Epic]
		["C:A:100274047022355|84684532261334"] = {Name = "Rohoccaim", Rarity = "Epic"},
		["A:100274047022355|84684532261334"] = {Name = "Rohoccaim", Rarity = "Epic"},
		["M:100274047022355"] = {Name = "Rohoccaim", Rarity = "Epic"},
	
		-- Sandstone [Epic]
		["C:A:12886223803|12886223929"] = {Name = "Sandstone", Rarity = "Epic"},
		["A:12886223803|12886223929"] = {Name = "Sandstone", Rarity = "Epic"},
		["M:12886223803"] = {Name = "Sandstone", Rarity = "Epic"},
	
		-- TNT [Epic]
		["C:A:97885003736109|130380890874250"] = {Name = "TNT", Rarity = "Epic"},
		["A:97885003736109|130380890874250"] = {Name = "TNT", Rarity = "Epic"},
		["M:97885003736109"] = {Name = "TNT", Rarity = "Epic"},
	
		-- Vaporeyester [Epic]
		["C:A:13063919006|13063919402"] = {Name = "Vaporeyester", Rarity = "Epic"},
		["A:13063919006|13063919402"] = {Name = "Vaporeyester", Rarity = "Epic"},
		["M:13063919006"] = {Name = "Vaporeyester", Rarity = "Epic"},
	
		-- Whihite [Epic]
		["C:A:12668274552|12668274813;A:12668275044|"] = {Name = "Whihite", Rarity = "Epic"},
		["A:12668274552|12668274813"] = {Name = "Whihite", Rarity = "Epic"},
		["A:12668275044|"] = {Name = "Whihite", Rarity = "Epic"},
		["M:12668274552"] = {Name = "Whihite", Rarity = "Epic"},
		["M:12668275044"] = {Name = "Whihite", Rarity = "Epic"},
	
		-- Wijkuled [Epic]
		["C:A:111403477018732|129898682647368"] = {Name = "Wijkuled", Rarity = "Epic"},
		["A:111403477018732|129898682647368"] = {Name = "Wijkuled", Rarity = "Epic"},
		["M:111403477018732"] = {Name = "Wijkuled", Rarity = "Epic"},
	
		-- Astral Fire [Rare]
		["C:A:125242264007895|97373159886943"] = {Name = "Astral Fire", Rarity = "Rare"},
		["A:125242264007895|97373159886943"] = {Name = "Astral Fire", Rarity = "Rare"},
		["M:125242264007895"] = {Name = "Astral Fire", Rarity = "Rare"},
	
		-- Auto_Rare_0233411 [Rare] – automatisch erkannt
		["C:A:134082039520636|117157580233411"] = {Name = "Auto_Rare_0233411", Rarity = "Rare"},
		["A:134082039520636|117157580233411"] = {Name = "Auto_Rare_0233411", Rarity = "Rare"},
		["M:134082039520636"] = {Name = "Auto_Rare_0233411", Rarity = "Rare"},
	
		-- Auto_Rare_0240139 [Rare] – automatisch erkannt
		["C:A:72779891558093|135717520240139"] = {Name = "Auto_Rare_0240139", Rarity = "Rare"},
		["A:72779891558093|135717520240139"] = {Name = "Auto_Rare_0240139", Rarity = "Rare"},
		["M:72779891558093"] = {Name = "Auto_Rare_0240139", Rarity = "Rare"},
	
		-- Auto_Rare_0601679 [Rare] – automatisch erkannt
		["C:A:137643791181455|75656910601679"] = {Name = "Auto_Rare_0601679", Rarity = "Rare"},
		["A:137643791181455|75656910601679"] = {Name = "Auto_Rare_0601679", Rarity = "Rare"},
		["M:137643791181455"] = {Name = "Auto_Rare_0601679", Rarity = "Rare"},
	
		-- Auto_Rare_0746993 [Rare] – automatisch erkannt
		["C:A:140566947873176|74815140746993"] = {Name = "Auto_Rare_0746993", Rarity = "Rare"},
		["A:140566947873176|74815140746993"] = {Name = "Auto_Rare_0746993", Rarity = "Rare"},
		["M:140566947873176"] = {Name = "Auto_Rare_0746993", Rarity = "Rare"},
	
		-- Auto_Rare_0849077 [Rare] – automatisch erkannt
		["C:A:92087522038157|87320170849077"] = {Name = "Auto_Rare_0849077", Rarity = "Rare"},
		["A:92087522038157|87320170849077"] = {Name = "Auto_Rare_0849077", Rarity = "Rare"},
		["M:92087522038157"] = {Name = "Auto_Rare_0849077", Rarity = "Rare"},
	
		-- Auto_Rare_2845098 [Rare]
		["C:A:12652840431|12652845098"] = {Name = "Auto_Rare_2845098", Rarity = "Rare"},
		["A:12652840431|12652845098"] = {Name = "Auto_Rare_2845098", Rarity = "Rare"},
		["M:12652840431"] = {Name = "Auto_Rare_2845098", Rarity = "Rare"},
	
		-- Auto_Rare_4219290 [Rare] – automatisch erkannt
		["C:A:107423985770360|125063304219290"] = {Name = "Auto_Rare_4219290", Rarity = "Rare"},
		["A:107423985770360|125063304219290"] = {Name = "Auto_Rare_4219290", Rarity = "Rare"},
		["M:107423985770360"] = {Name = "Auto_Rare_4219290", Rarity = "Rare"},
	
		-- Auto_Rare_4390009 [Rare] – automatisch erkannt
		["C:A:120892299413808|123600984390009"] = {Name = "Auto_Rare_4390009", Rarity = "Rare"},
		["A:120892299413808|123600984390009"] = {Name = "Auto_Rare_4390009", Rarity = "Rare"},
		["M:120892299413808"] = {Name = "Auto_Rare_4390009", Rarity = "Rare"},
	
		-- Auto_Rare_5700203 [Rare] – automatisch erkannt
		["C:A:137069588226924|127034325700203"] = {Name = "Auto_Rare_5700203", Rarity = "Rare"},
		["A:137069588226924|127034325700203"] = {Name = "Auto_Rare_5700203", Rarity = "Rare"},
		["M:137069588226924"] = {Name = "Auto_Rare_5700203", Rarity = "Rare"},
	
		-- Auto_Rare_6406997 [Rare] – automatisch erkannt
		["C:A:112644729587301|90954886406997"] = {Name = "Auto_Rare_6406997", Rarity = "Rare"},
		["A:112644729587301|90954886406997"] = {Name = "Auto_Rare_6406997", Rarity = "Rare"},
		["M:112644729587301"] = {Name = "Auto_Rare_6406997", Rarity = "Rare"},
	
		-- Auto_Rare_8228427 [Rare]
		["C:A:12358190837|12358228427"] = {Name = "Auto_Rare_8228427", Rarity = "Rare"},
		["A:12358190837|12358228427"] = {Name = "Auto_Rare_8228427", Rarity = "Rare"},
		["M:12358190837"] = {Name = "Auto_Rare_8228427", Rarity = "Rare"},
	
		-- Auto_Rare_8824073 [Rare] – automatisch erkannt
		["C:A:108925664768254|78184738824073"] = {Name = "Auto_Rare_8824073", Rarity = "Rare"},
		["A:108925664768254|78184738824073"] = {Name = "Auto_Rare_8824073", Rarity = "Rare"},
		["M:108925664768254"] = {Name = "Auto_Rare_8824073", Rarity = "Rare"},
	
		-- Auto_Rare_9098688 [Rare] – automatisch erkannt
		["C:A:95886161883237|75031979098688"] = {Name = "Auto_Rare_9098688", Rarity = "Rare"},
		["A:95886161883237|75031979098688"] = {Name = "Auto_Rare_9098688", Rarity = "Rare"},
		["M:95886161883237"] = {Name = "Auto_Rare_9098688", Rarity = "Rare"},
	
		-- Caliche [Rare]
		["C:A:12886224217|12886224354"] = {Name = "Caliche", Rarity = "Rare"},
		["A:12886224217|12886224354"] = {Name = "Caliche", Rarity = "Rare"},
		["M:12886224217"] = {Name = "Caliche", Rarity = "Rare"},
	
		-- Compost [Rare]
		["M:9579901695"] = {Name = "Compost", Rarity = "Rare"},
	
		-- Conglomerate [Rare]
		["C:A:12886225915|12886226011"] = {Name = "Conglomerate", Rarity = "Rare"},
		["A:12886225915|12886226011"] = {Name = "Conglomerate", Rarity = "Rare"},
		["M:12886225915"] = {Name = "Conglomerate", Rarity = "Rare"},
	
		-- Dotorite [Rare]
		["C:A:13063879215|13063947606;A:13069125646|13069126336"] = {Name = "Dotorite", Rarity = "Rare"},
		["A:13063879215|13063947606"] = {Name = "Dotorite", Rarity = "Rare"},
		["A:13069125646|13069126336"] = {Name = "Dotorite", Rarity = "Rare"},
		["M:13063879215"] = {Name = "Dotorite", Rarity = "Rare"},
		["M:13069125646"] = {Name = "Dotorite", Rarity = "Rare"},
	
		-- Flaming Hate [Rare]
		["C:A:134692958805210|77449849328610"] = {Name = "Flaming Hate", Rarity = "Rare"},
		["A:134692958805210|77449849328610"] = {Name = "Flaming Hate", Rarity = "Rare"},
		["M:134692958805210"] = {Name = "Flaming Hate", Rarity = "Rare"},
	
		-- Lava Shards [Rare]
		["C:A:12652840423|12652845098"] = {Name = "Lava Shards", Rarity = "Rare"},
		["A:12652840423|12652845098"] = {Name = "Lava Shards", Rarity = "Rare"},
		["M:12652840423"] = {Name = "Lava Shards", Rarity = "Rare"},
	
		-- Malacitea [Rare]
		["C:A:129876199640635|91880738170685"] = {Name = "Malacitea", Rarity = "Rare"},
		["A:129876199640635|91880738170685"] = {Name = "Malacitea", Rarity = "Rare"},
		["M:129876199640635"] = {Name = "Malacitea", Rarity = "Rare"},
	
		-- Molten Debris [Rare]
		["C:A:12653102669|12652845098"] = {Name = "Molten Debris", Rarity = "Rare"},
		["A:12653102669|12652845098"] = {Name = "Molten Debris", Rarity = "Rare"},
		["M:12653102669"] = {Name = "Molten Debris", Rarity = "Rare"},
	
		-- Pyrrhomomite [Rare]
		["C:A:13063886004|13063914193"] = {Name = "Pyrrhomomite", Rarity = "Rare"},
		["A:13063886004|13063914193"] = {Name = "Pyrrhomomite", Rarity = "Rare"},
	
		-- Radium [Rare]
		["C:A:124193659902922|105007020059492"] = {Name = "Radium", Rarity = "Rare"},
		["A:124193659902922|105007020059492"] = {Name = "Radium", Rarity = "Rare"},
		["M:124193659902922"] = {Name = "Radium", Rarity = "Rare"},
	
		-- Red Beryl [Rare]
		["C:A:12653102729|12652845098"] = {Name = "Red Beryl", Rarity = "Rare"},
		["A:12653102729|12652845098"] = {Name = "Red Beryl", Rarity = "Rare"},
		["M:12653102729"] = {Name = "Red Beryl", Rarity = "Rare"},
	
		-- TNT [Rare]
		["C:A:130746239003755|124961572735961"] = {Name = "TNT", Rarity = "Rare"},
		["A:130746239003755|124961572735961"] = {Name = "TNT", Rarity = "Rare"},
		["M:130746239003755"] = {Name = "TNT", Rarity = "Rare"},
	
		-- Xapotote [Rare]
		["C:A:12652840395|12652845098"] = {Name = "Xapotote", Rarity = "Rare"},
		["A:12652840395|12652845098"] = {Name = "Xapotote", Rarity = "Rare"},
		["M:12652840395"] = {Name = "Xapotote", Rarity = "Rare"},
	
		-- Zoniumite [Rare]
		["C:A:12668276298|12668276589;A:12668277136|12668277414"] = {Name = "Zoniumite", Rarity = "Rare"},
		["A:12668276298|12668276589"] = {Name = "Zoniumite", Rarity = "Rare"},
		["A:12668277136|12668277414"] = {Name = "Zoniumite", Rarity = "Rare"},
		["M:12668276298"] = {Name = "Zoniumite", Rarity = "Rare"},
		["M:12668277136"] = {Name = "Zoniumite", Rarity = "Rare"},
	
		-- Artificial Cube [Uncommon]
		["C:A:12653102631|12652845098"] = {Name = "Artificial Cube", Rarity = "Uncommon"},
		["A:12653102631|12652845098"] = {Name = "Artificial Cube", Rarity = "Uncommon"},
		["M:12653102631"] = {Name = "Artificial Cube", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_0328260 [Uncommon] – automatisch erkannt
		["C:A:100056644333200|95914030328260"] = {Name = "Auto_Uncommon_0328260", Rarity = "Uncommon"},
		["A:100056644333200|95914030328260"] = {Name = "Auto_Uncommon_0328260", Rarity = "Uncommon"},
		["M:100056644333200"] = {Name = "Auto_Uncommon_0328260", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_1596934 [Uncommon] – automatisch erkannt
		["C:A:128895384421737|123488431596934"] = {Name = "Auto_Uncommon_1596934", Rarity = "Uncommon"},
		["A:128895384421737|123488431596934"] = {Name = "Auto_Uncommon_1596934", Rarity = "Uncommon"},
		["M:128895384421737"] = {Name = "Auto_Uncommon_1596934", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_1617885 [Uncommon] – automatisch erkannt
		["C:A:126776521696833|138015221617885"] = {Name = "Auto_Uncommon_1617885", Rarity = "Uncommon"},
		["A:126776521696833|138015221617885"] = {Name = "Auto_Uncommon_1617885", Rarity = "Uncommon"},
		["M:126776521696833"] = {Name = "Auto_Uncommon_1617885", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_1777934 [Uncommon]
		["C:A:78332404521065|105123551777934"] = {Name = "Auto_Uncommon_1777934", Rarity = "Uncommon"},
		["A:78332404521065|105123551777934"] = {Name = "Auto_Uncommon_1777934", Rarity = "Uncommon"},
		["M:78332404521065"] = {Name = "Auto_Uncommon_1777934", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_2744017 [Uncommon] – automatisch erkannt
		["C:A:139660167827218|106831662744017"] = {Name = "Auto_Uncommon_2744017", Rarity = "Uncommon"},
		["A:139660167827218|106831662744017"] = {Name = "Auto_Uncommon_2744017", Rarity = "Uncommon"},
		["M:139660167827218"] = {Name = "Auto_Uncommon_2744017", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_2805260 [Uncommon]
		["C:A:93190452777443|120394192805260"] = {Name = "Auto_Uncommon_2805260", Rarity = "Uncommon"},
		["A:93190452777443|120394192805260"] = {Name = "Auto_Uncommon_2805260", Rarity = "Uncommon"},
		["M:93190452777443"] = {Name = "Auto_Uncommon_2805260", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_2845098 [Uncommon]
		["C:A:12652840329|12652845098"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["C:A:12652840443|12652845098"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["C:A:12653102765|12652845098"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["A:12652840329|12652845098"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["A:12652840443|12652845098"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["A:12653102765|12652845098"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["M:12652840329"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["M:12652840443"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
		["M:12653102765"] = {Name = "Auto_Uncommon_2845098", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_3607290 [Uncommon] – automatisch erkannt
		["C:A:128006682011565|96070513607290"] = {Name = "Auto_Uncommon_3607290", Rarity = "Uncommon"},
		["A:128006682011565|96070513607290"] = {Name = "Auto_Uncommon_3607290", Rarity = "Uncommon"},
		["M:128006682011565"] = {Name = "Auto_Uncommon_3607290", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_5021236 [Uncommon] – automatisch erkannt
		["C:A:80886366118411|136338495021236"] = {Name = "Auto_Uncommon_5021236", Rarity = "Uncommon"},
		["A:80886366118411|136338495021236"] = {Name = "Auto_Uncommon_5021236", Rarity = "Uncommon"},
		["M:80886366118411"] = {Name = "Auto_Uncommon_5021236", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_5859557 [Uncommon] – automatisch erkannt
		["C:A:130781492960049|95120547133986;A:95361935859557|"] = {Name = "Auto_Uncommon_5859557", Rarity = "Uncommon"},
		["A:130781492960049|95120547133986"] = {Name = "Auto_Uncommon_5859557", Rarity = "Uncommon"},
		["A:95361935859557|"] = {Name = "Auto_Uncommon_5859557", Rarity = "Uncommon"},
		["M:130781492960049"] = {Name = "Auto_Uncommon_5859557", Rarity = "Uncommon"},
		["M:95361935859557"] = {Name = "Auto_Uncommon_5859557", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_6054411 [Uncommon] – automatisch erkannt
		["C:A:75319093946158|136932556054411"] = {Name = "Auto_Uncommon_6054411", Rarity = "Uncommon"},
		["A:75319093946158|136932556054411"] = {Name = "Auto_Uncommon_6054411", Rarity = "Uncommon"},
		["M:75319093946158"] = {Name = "Auto_Uncommon_6054411", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_6076855 [Uncommon] – automatisch erkannt
		["C:A:76526171133879|72787136076855"] = {Name = "Auto_Uncommon_6076855", Rarity = "Uncommon"},
		["A:76526171133879|72787136076855"] = {Name = "Auto_Uncommon_6076855", Rarity = "Uncommon"},
		["M:76526171133879"] = {Name = "Auto_Uncommon_6076855", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_6285845 [Uncommon] – automatisch erkannt
		["C:A:125570261520326|74442716285845"] = {Name = "Auto_Uncommon_6285845", Rarity = "Uncommon"},
		["A:125570261520326|74442716285845"] = {Name = "Auto_Uncommon_6285845", Rarity = "Uncommon"},
		["M:125570261520326"] = {Name = "Auto_Uncommon_6285845", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_7555974 [Uncommon] – automatisch erkannt
		["C:A:86185181873503|94609717555974"] = {Name = "Auto_Uncommon_7555974", Rarity = "Uncommon"},
		["A:86185181873503|94609717555974"] = {Name = "Auto_Uncommon_7555974", Rarity = "Uncommon"},
		["M:86185181873503"] = {Name = "Auto_Uncommon_7555974", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_8208737 [Uncommon] – automatisch erkannt
		["C:A:108180040343053|118110088208737"] = {Name = "Auto_Uncommon_8208737", Rarity = "Uncommon"},
		["A:108180040343053|118110088208737"] = {Name = "Auto_Uncommon_8208737", Rarity = "Uncommon"},
		["M:108180040343053"] = {Name = "Auto_Uncommon_8208737", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_8211460 [Uncommon]
		["C:A:12358190876|12358211460"] = {Name = "Auto_Uncommon_8211460", Rarity = "Uncommon"},
		["A:12358190876|12358211460"] = {Name = "Auto_Uncommon_8211460", Rarity = "Uncommon"},
		["M:12358190876"] = {Name = "Auto_Uncommon_8211460", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_8212907 [Uncommon]
		["C:A:12358191012|12358212907"] = {Name = "Auto_Uncommon_8212907", Rarity = "Uncommon"},
		["A:12358191012|12358212907"] = {Name = "Auto_Uncommon_8212907", Rarity = "Uncommon"},
		["M:12358191012"] = {Name = "Auto_Uncommon_8212907", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_8215661 [Uncommon]
		["C:A:12358191029|12358215661"] = {Name = "Auto_Uncommon_8215661", Rarity = "Uncommon"},
		["A:12358191029|12358215661"] = {Name = "Auto_Uncommon_8215661", Rarity = "Uncommon"},
		["M:12358191029"] = {Name = "Auto_Uncommon_8215661", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_8618754 [Uncommon] – automatisch erkannt
		["C:A:123321051970192|128184928618754"] = {Name = "Auto_Uncommon_8618754", Rarity = "Uncommon"},
		["A:123321051970192|128184928618754"] = {Name = "Auto_Uncommon_8618754", Rarity = "Uncommon"},
		["M:123321051970192"] = {Name = "Auto_Uncommon_8618754", Rarity = "Uncommon"},
	
		-- Auto_Uncommon_9073645 [Uncommon] – automatisch erkannt
		["C:A:92819536052664|85098059073645"] = {Name = "Auto_Uncommon_9073645", Rarity = "Uncommon"},
		["A:92819536052664|85098059073645"] = {Name = "Auto_Uncommon_9073645", Rarity = "Uncommon"},
		["M:92819536052664"] = {Name = "Auto_Uncommon_9073645", Rarity = "Uncommon"},
	
		-- Brecca [Uncommon]
		["C:A:12886223389|12886223502"] = {Name = "Brecca", Rarity = "Uncommon"},
		["A:12886223389|12886223502"] = {Name = "Brecca", Rarity = "Uncommon"},
		["M:12886223389"] = {Name = "Brecca", Rarity = "Uncommon"},
	
		-- Calcynite [Uncommon]
		["C:A:101006617944408|109553203535107"] = {Name = "Calcynite", Rarity = "Uncommon"},
		["A:101006617944408|109553203535107"] = {Name = "Calcynite", Rarity = "Uncommon"},
		["M:101006617944408"] = {Name = "Calcynite", Rarity = "Uncommon"},
	
		-- Cracked Waste [Uncommon]
		["C:A:12652840391|12652845098"] = {Name = "Cracked Waste", Rarity = "Uncommon"},
		["A:12652840391|12652845098"] = {Name = "Cracked Waste", Rarity = "Uncommon"},
		["M:12652840391"] = {Name = "Cracked Waste", Rarity = "Uncommon"},
	
		-- Geris [Uncommon]
		["C:A:13063886004|13063886393"] = {Name = "Geris", Rarity = "Uncommon"},
		["A:13063886004|13063886393"] = {Name = "Geris", Rarity = "Uncommon"},
	
		-- Graeconogaron [Uncommon]
		["C:A:13063887922|13063888350"] = {Name = "Graeconogaron", Rarity = "Uncommon"},
		["A:13063887922|13063888350"] = {Name = "Graeconogaron", Rarity = "Uncommon"},
		["M:13063887922"] = {Name = "Graeconogaron", Rarity = "Uncommon"},
	
		-- Ignarion [Uncommon]
		["C:A:138719582249238|125536023672648"] = {Name = "Ignarion", Rarity = "Uncommon"},
		["A:138719582249238|125536023672648"] = {Name = "Ignarion", Rarity = "Uncommon"},
		["M:138719582249238"] = {Name = "Ignarion", Rarity = "Uncommon"},
	
		-- IriBit [Uncommon]
		["C:A:119560252765201|132778655427699"] = {Name = "IriBit", Rarity = "Uncommon"},
		["A:119560252765201|132778655427699"] = {Name = "IriBit", Rarity = "Uncommon"},
		["M:119560252765201"] = {Name = "IriBit", Rarity = "Uncommon"},
	
		-- Kiteanium [Uncommon]
		["C:A:72440733125431|109003192586674"] = {Name = "Kiteanium", Rarity = "Uncommon"},
		["A:72440733125431|109003192586674"] = {Name = "Kiteanium", Rarity = "Uncommon"},
		["M:72440733125431"] = {Name = "Kiteanium", Rarity = "Uncommon"},
	
		-- Limioactive [Uncommon]
		["C:A:12668277912|12668278196;A:12668278556|12668278843"] = {Name = "Limioactive", Rarity = "Uncommon"},
		["A:12668277912|12668278196"] = {Name = "Limioactive", Rarity = "Uncommon"},
		["A:12668278556|12668278843"] = {Name = "Limioactive", Rarity = "Uncommon"},
		["M:12668277912"] = {Name = "Limioactive", Rarity = "Uncommon"},
		["M:12668278556"] = {Name = "Limioactive", Rarity = "Uncommon"},
	
		-- Moptazik [Uncommon]
		["C:A:12668285296|12668285490;A:12668285756|"] = {Name = "Moptazik", Rarity = "Uncommon"},
		["A:12668285296|12668285490"] = {Name = "Moptazik", Rarity = "Uncommon"},
		["A:12668285756|"] = {Name = "Moptazik", Rarity = "Uncommon"},
		["M:12668285296"] = {Name = "Moptazik", Rarity = "Uncommon"},
		["M:12668285756"] = {Name = "Moptazik", Rarity = "Uncommon"},
	
		-- Oolite [Uncommon]
		["C:A:12886223006|12886223173"] = {Name = "Oolite", Rarity = "Uncommon"},
		["A:12886223006|12886223173"] = {Name = "Oolite", Rarity = "Uncommon"},
		["M:12886223006"] = {Name = "Oolite", Rarity = "Uncommon"},
	
		-- Opix [Uncommon]
		["C:A:85954320447314|82197802914389"] = {Name = "Opix", Rarity = "Uncommon"},
		["A:85954320447314|82197802914389"] = {Name = "Opix", Rarity = "Uncommon"},
		["M:85954320447314"] = {Name = "Opix", Rarity = "Uncommon"},
	
		-- Ovalium [Uncommon]
		["C:A:73590961225578|107438041135912"] = {Name = "Ovalium", Rarity = "Uncommon"},
		["A:73590961225578|107438041135912"] = {Name = "Ovalium", Rarity = "Uncommon"},
		["M:73590961225578"] = {Name = "Ovalium", Rarity = "Uncommon"},
	
		-- Paretium [Uncommon]
		["C:A:12668281192|12668281481;A:12668281781|"] = {Name = "Paretium", Rarity = "Uncommon"},
		["A:12668281192|12668281481"] = {Name = "Paretium", Rarity = "Uncommon"},
		["A:12668281781|"] = {Name = "Paretium", Rarity = "Uncommon"},
		["M:12668281192"] = {Name = "Paretium", Rarity = "Uncommon"},
		["M:12668281781"] = {Name = "Paretium", Rarity = "Uncommon"},
	
		-- Plumbryn [Uncommon]
		["C:A:133734976408210|124518365174430"] = {Name = "Plumbryn", Rarity = "Uncommon"},
		["A:133734976408210|124518365174430"] = {Name = "Plumbryn", Rarity = "Uncommon"},
		["M:133734976408210"] = {Name = "Plumbryn", Rarity = "Uncommon"},
	
		-- Prehistoric Bot Remnant [Uncommon]
		["C:A:12742166954|12742167017"] = {Name = "Prehistoric Bot Remnant", Rarity = "Uncommon"},
		["A:12742166954|12742167017"] = {Name = "Prehistoric Bot Remnant", Rarity = "Uncommon"},
		["M:12742166954"] = {Name = "Prehistoric Bot Remnant", Rarity = "Uncommon"},
	
		-- Shimicala [Uncommon]
		["C:A:13069125583|13063909062"] = {Name = "Shimicala", Rarity = "Uncommon"},
		["A:13069125583|13063909062"] = {Name = "Shimicala", Rarity = "Uncommon"},
		["M:13069125583"] = {Name = "Shimicala", Rarity = "Uncommon"},
	
		-- Tufa [Uncommon]
		["C:A:12886222689|12886222813"] = {Name = "Tufa", Rarity = "Uncommon"},
		["A:12886222689|12886222813"] = {Name = "Tufa", Rarity = "Uncommon"},
		["M:12886222689"] = {Name = "Tufa", Rarity = "Uncommon"},
	
		-- Uvyrite [Uncommon]
		["C:A:126622910755306|82679397478251"] = {Name = "Uvyrite", Rarity = "Uncommon"},
		["A:126622910755306|82679397478251"] = {Name = "Uvyrite", Rarity = "Uncommon"},
		["M:126622910755306"] = {Name = "Uvyrite", Rarity = "Uncommon"},
	
		-- Venomyth [Uncommon]
		["C:A:123215418713801|73632361545965"] = {Name = "Venomyth", Rarity = "Uncommon"},
		["A:123215418713801|73632361545965"] = {Name = "Venomyth", Rarity = "Uncommon"},
		["M:123215418713801"] = {Name = "Venomyth", Rarity = "Uncommon"},
	
		-- Charbit [Common]
		["C:A:136061306028050|110765726253852"] = {Name = "Charbit", Rarity = "Common"},
		["A:136061306028050|110765726253852"] = {Name = "Charbit", Rarity = "Common"},
		["M:136061306028050"] = {Name = "Charbit", Rarity = "Common"},
	
		-- Coquina [Common]
		["C:A:12886221737|12886221873"] = {Name = "Coquina", Rarity = "Common"},
		["A:12886221737|12886221873"] = {Name = "Coquina", Rarity = "Common"},
		["M:12886221737"] = {Name = "Coquina", Rarity = "Common"},
	
		-- Cracked Bones [Common]
		["C:A:12652840440|12652845098"] = {Name = "Cracked Bones", Rarity = "Common"},
		["A:12652840440|12652845098"] = {Name = "Cracked Bones", Rarity = "Common"},
		["M:12652840440"] = {Name = "Cracked Bones", Rarity = "Common"},
	
		-- Emberium [Common]
		["C:A:13069125591|13064471481"] = {Name = "Emberium", Rarity = "Common"},
		["A:13069125591|13064471481"] = {Name = "Emberium", Rarity = "Common"},
		["M:13069125591"] = {Name = "Emberium", Rarity = "Common"},
	
		-- Flamespawn [Common]
		["C:A:13063877130|;A:13063883570|;A:13069125687|13069126724"] = {Name = "Flamespawn", Rarity = "Common"},
		["A:13063877130|"] = {Name = "Flamespawn", Rarity = "Common"},
		["A:13063883570|"] = {Name = "Flamespawn", Rarity = "Common"},
		["A:13069125687|13069126724"] = {Name = "Flamespawn", Rarity = "Common"},
		["M:13063877130"] = {Name = "Flamespawn", Rarity = "Common"},
		["M:13063883570"] = {Name = "Flamespawn", Rarity = "Common"},
		["M:13069125687"] = {Name = "Flamespawn", Rarity = "Common"},
	
		-- Flint [Common]
		["C:A:12886221345|12886221476"] = {Name = "Flint", Rarity = "Common"},
		["A:12886221345|12886221476"] = {Name = "Flint", Rarity = "Common"},
		["M:12886221345"] = {Name = "Flint", Rarity = "Common"},
	
		-- Gamite [Common]
		["C:A:12668281993|12668282299;A:12668282565|12668282872"] = {Name = "Gamite", Rarity = "Common"},
		["A:12668281993|12668282299"] = {Name = "Gamite", Rarity = "Common"},
		["A:12668282565|12668282872"] = {Name = "Gamite", Rarity = "Common"},
		["M:12668281993"] = {Name = "Gamite", Rarity = "Common"},
		["M:12668282565"] = {Name = "Gamite", Rarity = "Common"},
	
		-- Magmoryx [Common]
		["C:A:85374111100261|94792408779881"] = {Name = "Magmoryx", Rarity = "Common"},
		["A:85374111100261|94792408779881"] = {Name = "Magmoryx", Rarity = "Common"},
		["M:85374111100261"] = {Name = "Magmoryx", Rarity = "Common"},
	
		-- Oil Waste [Common]
		["C:A:12652840371|12652845098"] = {Name = "Oil Waste", Rarity = "Common"},
		["A:12652840371|12652845098"] = {Name = "Oil Waste", Rarity = "Common"},
		["M:12652840371"] = {Name = "Oil Waste", Rarity = "Common"},
	
		-- Plumbit [Common]
		["C:A:135145385142821|124811282588863"] = {Name = "Plumbit", Rarity = "Common"},
		["A:135145385142821|124811282588863"] = {Name = "Plumbit", Rarity = "Common"},
		["M:135145385142821"] = {Name = "Plumbit", Rarity = "Common"},
	
		-- Pumice [Common]
		["C:A:12652840558|12652845098"] = {Name = "Pumice", Rarity = "Common"},
		["A:12652840558|12652845098"] = {Name = "Pumice", Rarity = "Common"},
		["M:12652840558"] = {Name = "Pumice", Rarity = "Common"},
	
		-- Pyrelith [Common]
		["C:A:122108929414247|124714364589675"] = {Name = "Pyrelith", Rarity = "Common"},
		["A:122108929414247|124714364589675"] = {Name = "Pyrelith", Rarity = "Common"},
		["M:122108929414247"] = {Name = "Pyrelith", Rarity = "Common"},
	
		-- Radionyx [Common]
		["C:A:79434340836195|85719510728514"] = {Name = "Radionyx", Rarity = "Common"},
		["A:79434340836195|85719510728514"] = {Name = "Radionyx", Rarity = "Common"},
		["M:79434340836195"] = {Name = "Radionyx", Rarity = "Common"},
	
		-- Reinforced Basalt [Common]
		["C:A:12652840554|12652845098"] = {Name = "Reinforced Basalt", Rarity = "Common"},
		["A:12652840554|12652845098"] = {Name = "Reinforced Basalt", Rarity = "Common"},
		["M:12652840554"] = {Name = "Reinforced Basalt", Rarity = "Common"},
	
		-- Silbit [Common]
		["C:A:105882609043952|102575372172322"] = {Name = "Silbit", Rarity = "Common"},
		["A:105882609043952|102575372172322"] = {Name = "Silbit", Rarity = "Common"},
		["M:105882609043952"] = {Name = "Silbit", Rarity = "Common"},
	
		-- Squarite [Common]
		["C:A:105796040065135|138098006031599"] = {Name = "Squarite", Rarity = "Common"},
		["A:105796040065135|138098006031599"] = {Name = "Squarite", Rarity = "Common"},
		["M:105796040065135"] = {Name = "Squarite", Rarity = "Common"},
	
		-- TNT [Common]
		["C:A:88911303348714|134126540648490"] = {Name = "TNT", Rarity = "Common"},
		["A:88911303348714|134126540648490"] = {Name = "TNT", Rarity = "Common"},
		["M:88911303348714"] = {Name = "TNT", Rarity = "Common"},
	
		-- Trianglium [Common]
		["C:A:101409784230204|127195133830804"] = {Name = "Trianglium", Rarity = "Common"},
		["A:101409784230204|127195133830804"] = {Name = "Trianglium", Rarity = "Common"},
		["M:101409784230204"] = {Name = "Trianglium", Rarity = "Common"},
	
		-- Uraniumite [Common]
		["C:A:12668279093|12668279407;A:12668279941|"] = {Name = "Uraniumite", Rarity = "Common"},
		["A:12668279093|12668279407"] = {Name = "Uraniumite", Rarity = "Common"},
		["A:12668279941|"] = {Name = "Uraniumite", Rarity = "Common"},
		["M:12668279093"] = {Name = "Uraniumite", Rarity = "Common"},
		["M:12668279941"] = {Name = "Uraniumite", Rarity = "Common"},
	
	},
}

--//====================================================
--// SERVICES UND GUI
--//====================================================

local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local VirtualInputManager = game:GetService("VirtualInputManager")

local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")

local GameGui = PlayerGui:WaitForChild("Game")
local BlockGui = GameGui:WaitForChild("BlockGui")
local BlockTextObject = BlockGui:WaitForChild("Block")


--//====================================================
--// OPTIONALE CLIPBOARD-/DATEIFUNKTIONEN
--//====================================================

local function findGlobalFunction(names)
	local environments = {
		_G,
	}

	if type(getgenv) == "function" then
		local success, environment = pcall(getgenv)

		if success and type(environment) == "table" then
			table.insert(environments, environment)
		end
	end

	if type(getfenv) == "function" then
		local success, environment = pcall(getfenv)

		if success and type(environment) == "table" then
			table.insert(environments, environment)
		end
	end

	for _, environment in ipairs(environments) do
		for _, name in ipairs(names) do
			local value = rawget(environment, name)

			if type(value) == "function" then
				return value
			end
		end
	end

	return nil
end

local ClipboardWriter = findGlobalFunction({
	"setclipboard",
	"toclipboard",
})

local FileWriter = findGlobalFunction({
	"writefile",
})

local function copyToClipboard(text)
	if not ClipboardWriter then
		return false, "Keine direkte Zwischenablage verfügbar."
	end

	local success, result = pcall(ClipboardWriter, text)

	if not success then
		return false, tostring(result)
	end

	return true
end

--//====================================================
--// ALTE ESP-OBJEKTE ENTFERNEN
--//====================================================

local OLD_FOLDER_NAMES = {
	"_OptimizedRareESP",
	"_ExportModelMeshESP",
	"_ModelParticleMeshESP",
	"_HybridParticleMeshESP",
	"_ParticleRarityESP",
	"_HybridMeshESP",
	"_MeshRareESP",
	"_NearbyRareESP",
	"_ObservedRareESP",
	"_AccurateRareESP",
	"_LearnedRareESP",
	"_BlockRarityESP",
}

for _, folderName in ipairs(OLD_FOLDER_NAMES) do
	local worldFolder = workspace:FindFirstChild(folderName)

	if worldFolder then
		worldFolder:Destroy()
	end

	local guiFolder = PlayerGui:FindFirstChild(folderName)

	if guiFolder then
		guiFolder:Destroy()
	end
end

local oldExportGui = PlayerGui:FindFirstChild("_OptimizedRareESPExport")

if oldExportGui then
	oldExportGui:Destroy()
end

local HighlightFolder = Instance.new("Folder")
HighlightFolder.Name = "_OptimizedRareESP"
HighlightFolder.Parent = workspace

local BillboardFolder = Instance.new("Folder")
BillboardFolder.Name = "_OptimizedRareESP"
BillboardFolder.Parent = PlayerGui

--//====================================================
--// RARITY-SYSTEM
--//====================================================

local RarityRanks = {}
local CanonicalRarities = {}

for index, rarityName in ipairs(SETTINGS.RARITY_ORDER) do
	local lowerName = string.lower(rarityName)

	RarityRanks[lowerName] = index
	CanonicalRarities[lowerName] = rarityName
end

local RarityAliases = {
	uncommen = "Uncommon",
	uncomon = "Uncommon",
	mythical = "Mythic",
	legend = "Legendary",
}

local function normalizeRarity(value)
	if value == nil then
		return nil
	end

	local text = string.lower(tostring(value))

	text = text:gsub("^%s+", "")
	text = text:gsub("%s+$", "")

	return RarityAliases[text]
		or CanonicalRarities[text]
end

local function getRarityRank(rarity)
	rarity = normalizeRarity(rarity)

	if not rarity then
		return 0
	end

	return RarityRanks[string.lower(rarity)] or 0
end

local function isRareEnough(rarity)
	return getRarityRank(rarity)
		>= getRarityRank(SETTINGS.MINIMUM_RARITY)
end

local function getRarityColor(rarity)
	return SETTINGS.RARITY_COLORS[rarity]
		or Color3.fromRGB(255, 255, 255)
end

--//====================================================
--// BLOCK-GUI AUSLESEN
--//====================================================

local function parseCurrentBlockText()
	local fullText = BlockTextObject.Text

	if type(fullText) ~= "string"
		or fullText == ""
	then
		return nil
	end

	local blockName, rawRarity =
		fullText:match("^%s*(.-)%s*%((.-)%)%s*$")

	local rarity = normalizeRarity(rawRarity)

	-- TNT-Sonderobjekte werden im Spiel als „Unique“ angezeigt.
	-- Der Fallback greift auch dann, wenn die GUI den Text einmal
	-- ohne sauber lesbare Klammern aktualisiert.
	if (
		not rarity
		or not blockName
		or blockName == ""
	)
		and string.find(
			string.lower(fullText),
			"tnt",
			1,
			true
		)
	then
		blockName = blockName
			or fullText:gsub("%s*%b()%s*$", "")

		rarity = "Unique"
	end

	if not blockName
		or blockName == ""
		or not rarity
	then
		return nil
	end

	return {
		FullText = fullText,
		Name = blockName,
		Rarity = rarity,
		Generic = false,
	}
end

--//====================================================
--// BLOCK-/MODEL-HILFSFUNKTIONEN
--//====================================================

local function getBlockTarget(part)
	if not part or not part:IsA("BasePart") then
		return nil
	end

	local current = part.Parent
	local nearestModel = nil

	while current and current ~= SETTINGS.ROOT do
		if current:IsA("Model") and not nearestModel then
			nearestModel = current
		end

		current = current.Parent
	end

	return nearestModel or part
end

local function getTargetBaseParts(target)
	if target:IsA("BasePart") then
		return {
			target,
		}
	end

	local parts = {}

	if target:IsA("Model") then
		for _, descendant in ipairs(target:GetDescendants()) do
			if descendant:IsA("BasePart") then
				table.insert(parts, descendant)
			end
		end
	end

	return parts
end

local function getTargetPosition(target)
	if target:IsA("BasePart") then
		return target.Position
	end

	if target:IsA("Model") then
		return target:GetPivot().Position
	end

	return Vector3.zero
end

local function getTargetLabelPart(target)
	if target:IsA("BasePart") then
		return target
	end

	if not target:IsA("Model") then
		return nil
	end

	local pivotPosition = target:GetPivot().Position
	local nearestPart = nil
	local nearestDistance = math.huge

	for _, descendant in ipairs(target:GetDescendants()) do
		if descendant:IsA("BasePart") then
			local distance =
				(descendant.Position - pivotPosition).Magnitude

			if distance < nearestDistance then
				nearestDistance = distance
				nearestPart = descendant
			end
		end
	end

	return target.PrimaryPart or nearestPart
end

--//====================================================
--// ASSET-SIGNATUREN
--//====================================================

local function normalizeAssetId(value)
	local text = tostring(value or "")
	local numericId = text:match("(%d+)")

	if not numericId or numericId == "0" then
		return nil
	end

	return numericId
end

local function addUnique(list, lookup, value)
	if not value or lookup[value] then
		return
	end

	lookup[value] = true
	table.insert(list, value)
end

local function getPartAssetData(part)
	local meshId = nil
	local textures = {}
	local textureLookup = {}

	if part:IsA("MeshPart") then
		meshId = normalizeAssetId(part.MeshId)

		addUnique(
			textures,
			textureLookup,
			normalizeAssetId(part.TextureID)
		)
	end

	local specialMesh =
		part:FindFirstChildOfClass("SpecialMesh")

	if specialMesh then
		meshId = meshId
			or normalizeAssetId(specialMesh.MeshId)

		addUnique(
			textures,
			textureLookup,
			normalizeAssetId(specialMesh.TextureId)
		)
	end

	local surfaceAppearance =
		part:FindFirstChildOfClass("SurfaceAppearance")

	if surfaceAppearance then
		addUnique(
			textures,
			textureLookup,
			normalizeAssetId(surfaceAppearance.ColorMap)
		)

		addUnique(
			textures,
			textureLookup,
			normalizeAssetId(surfaceAppearance.NormalMap)
		)

		addUnique(
			textures,
			textureLookup,
			normalizeAssetId(surfaceAppearance.MetalnessMap)
		)

		addUnique(
			textures,
			textureLookup,
			normalizeAssetId(surfaceAppearance.RoughnessMap)
		)
	end

	for _, child in ipairs(part:GetChildren()) do
		if child:IsA("Decal")
			or child:IsA("Texture")
		then
			addUnique(
				textures,
				textureLookup,
				normalizeAssetId(child.Texture)
			)
		end
	end

	table.sort(textures)

	return meshId, textures
end

local function getTargetAssetKeys(target)
	local keys = {}
	local keyLookup = {}
	local compositeEntries = {}

	for _, part in ipairs(getTargetBaseParts(target)) do
		local meshId, textures =
			getPartAssetData(part)

		if meshId then
			local meshKey = "M:" .. meshId

			addUnique(
				keys,
				keyLookup,
				meshKey
			)

			local exactKey =
				"A:"
				.. meshId
				.. "|"
				.. table.concat(textures, ",")

			addUnique(
				keys,
				keyLookup,
				exactKey
			)

			table.insert(compositeEntries, exactKey)
		end
	end

	if #compositeEntries > 0 then
		table.sort(compositeEntries)

		addUnique(
			keys,
			keyLookup,
			"C:"
				.. table.concat(
					compositeEntries,
					";"
				)
		)
	end

	return keys
end

local function getKeyPriority(key)
	local prefix = string.sub(key, 1, 2)

	if prefix == "C:" then
		return 3
	end

	if prefix == "A:" then
		return 2
	end

	if prefix == "M:" then
		return 1
	end

	return 0
end

--//====================================================
--// SPATIAL HASH
--//====================================================

local SpatialCells = {}

local function getCellCoordinates(position)
	local cellSize = SETTINGS.CELL_SIZE

	return math.floor(position.X / cellSize),
		math.floor(position.Y / cellSize),
		math.floor(position.Z / cellSize)
end

local function makeCellKey(x, y, z)
	return tostring(x)
		.. ":"
		.. tostring(y)
		.. ":"
		.. tostring(z)
end

local function removeFromCell(data)
	if not data.CellKey then
		return
	end

	local bucket = SpatialCells[data.CellKey]

	if bucket then
		bucket[data.Target] = nil

		if next(bucket) == nil then
			SpatialCells[data.CellKey] = nil
		end
	end

	data.CellKey = nil
end

local function updateTargetCell(data, position)
	local x, y, z = getCellCoordinates(position)
	local cellKey = makeCellKey(x, y, z)

	if data.CellKey == cellKey then
		return
	end

	removeFromCell(data)

	local bucket = SpatialCells[cellKey]

	if not bucket then
		bucket = {}
		SpatialCells[cellKey] = bucket
	end

	bucket[data.Target] = true
	data.CellKey = cellKey
end

--//====================================================
--// TARGET-INDEX
--//====================================================

local TargetData = {}
local ReindexScheduled = {}
local ForceRefresh = true

local function removeTarget(target)
	local data = TargetData[target]

	if not data then
		return
	end

	removeFromCell(data)

	for emitter in pairs(data.Emitters) do
		data.Emitters[emitter] = nil
	end

	TargetData[target] = nil
	ReindexScheduled[target] = nil
	ForceRefresh = true
end

local function reindexTarget(target)
	ReindexScheduled[target] = nil

	if not target
		or not target.Parent
		or not target:IsDescendantOf(SETTINGS.ROOT)
	then
		removeTarget(target)
		return
	end

	local data = TargetData[target]

	if not data then
		data = {
			Target = target,
			Keys = {},
			Emitters = {},
			ParticleRarity = nil,
			NamedInfo = nil,
			LabelPart = nil,
			Position = Vector3.zero,
			CellKey = nil,
		}

		TargetData[target] = data

		target.Destroying:Connect(function()
			removeTarget(target)
		end)
	end

	data.LabelPart = getTargetLabelPart(target)
	data.Position = getTargetPosition(target)
	data.Keys = getTargetAssetKeys(target)

	updateTargetCell(data, data.Position)
	ForceRefresh = true
end

local function queueReindex(target, delaySeconds)
	if not target or ReindexScheduled[target] then
		return
	end

	ReindexScheduled[target] = true

	task.delay(
		delaySeconds or SETTINGS.REINDEX_DELAY,
		function()
			if ReindexScheduled[target] then
				reindexTarget(target)
			end
		end
	)
end

local function registerTarget(target)
	if not target then
		return nil
	end

	if not TargetData[target] then
		TargetData[target] = {
			Target = target,
			Keys = {},
			Emitters = {},
			ParticleRarity = nil,
			NamedInfo = nil,
			LabelPart = nil,
			Position = Vector3.zero,
			CellKey = nil,
		}

		target.Destroying:Connect(function()
			removeTarget(target)
		end)
	end

	reindexTarget(target)

	return TargetData[target]
end

--//====================================================
--// BEKANNTE MESH-SCHLÜSSEL
--//====================================================

local KnownKeys = {}
local ConflictingKeys = {}
local LastExportGroupId = nil
local OreListDirty = true

local function makeGenericName(rarity, key)
	local suffix =
		key:match("(%d+)[^%d]*$")
		or tostring(#key * 7919)

	if #suffix > 7 then
		suffix = string.sub(suffix, #suffix - 6)
	end

	return "Auto_"
		.. rarity
		.. "_"
		.. suffix
end

local function sameInformation(a, b)
	if a.Rarity ~= b.Rarity then
		return false
	end

	if a.Generic or b.Generic then
		return true
	end

	return a.Name == b.Name
end

local function registerKnownKey(key, information)
	if ConflictingKeys[key] then
		return false
	end

	local previous = KnownKeys[key]

	if previous then
		if sameInformation(previous, information) then
			if previous.Generic
				and not information.Generic
			then
				previous.Name = information.Name
				previous.Generic = false
				ForceRefresh = true
				OreListDirty = true
				return true
			end

			return false
		end

		KnownKeys[key] = nil
		ConflictingKeys[key] = true
		ForceRefresh = true
		OreListDirty = true

		warn(
			"[ESP KONFLIKT]",
			key,
			"gehört zu mehreren Blockarten/Raritäten "
				.. "und wird nicht mehr verwendet."
		)

		return false
	end

	KnownKeys[key] = {
		Name = information.Name,
		Rarity = information.Rarity,
		Generic = information.Generic == true,
	}

	ForceRefresh = true
	OreListDirty = true
	return true
end

local function registerTargetKeys(data, information)
	if not data or #data.Keys == 0 then
		return false
	end

	local changed = false

	for _, key in ipairs(data.Keys) do
		if registerKnownKey(key, information) then
			changed = true
		end
	end

	return changed
end

local function getMeshInformation(data)
	local selected = nil
	local selectedPriority = -1

	for _, key in ipairs(data.Keys) do
		local information = KnownKeys[key]

		if information
			and not ConflictingKeys[key]
		then
			local priority = getKeyPriority(key)

			if priority > selectedPriority then
				selected = information
				selectedPriority = priority

			elseif priority == selectedPriority
				and selected
				and (
					selected.Rarity ~= information.Rarity
					or (
						not selected.Generic
						and not information.Generic
						and selected.Name ~= information.Name
					)
				)
			then
				return nil
			end
		end
	end

	return selected
end

for key, information in pairs(SETTINGS.PRESETS) do
	local rarity = normalizeRarity(information.Rarity)

	if rarity then
		KnownKeys[key] = {
			Name = tostring(information.Name or ""),
			Rarity = rarity,
			Generic = false,
		}
	end
end

--//====================================================
--// PARTICLE-ERKENNUNG
--//====================================================

local EmitterState = {}

local function getEmitterPart(emitter)
	local current = emitter.Parent

	while current and current ~= SETTINGS.ROOT do
		if current:IsA("BasePart") then
			return current
		end

		current = current.Parent
	end

	return nil
end

local function getColorSaturation(color)
	local maximum = math.max(
		color.R,
		color.G,
		color.B
	)

	local minimum = math.min(
		color.R,
		color.G,
		color.B
	)

	return maximum - minimum
end

local function getEmitterColor(emitter)
	local keypoints = emitter.Color.Keypoints

	if #keypoints == 0 then
		return Color3.new(1, 1, 1)
	end

	local selectedColor = keypoints[1].Value
	local selectedSaturation =
		getColorSaturation(selectedColor)

	for _, keypoint in ipairs(keypoints) do
		local color = keypoint.Value
		local saturation =
			getColorSaturation(color)

		if saturation > selectedSaturation then
			selectedColor = color
			selectedSaturation = saturation
		end
	end

	return selectedColor
end

local function colorDistance(colorA, colorB)
	local red = colorA.R - colorB.R
	local green = colorA.G - colorB.G
	local blue = colorA.B - colorB.B

	return math.sqrt(
		red * red
			+ green * green
			+ blue * blue
	)
end

local function classifyEmitter(emitter)
	if SETTINGS.REQUIRE_PARTICLE_ENABLED
		and not emitter.Enabled
	then
		return nil
	end

	local color = getEmitterColor(emitter)
	local nearestRarity = nil
	local nearestDistance = math.huge

	for rarity, expectedColor in pairs(
		SETTINGS.PARTICLE_COLORS
	) do
		local distance =
			colorDistance(color, expectedColor)

		if distance < nearestDistance then
			nearestDistance = distance
			nearestRarity = rarity
		end
	end

	if nearestDistance
		> SETTINGS.PARTICLE_MAX_COLOR_DISTANCE
	then
		return nil
	end

	return nearestRarity
end

local function recomputeTargetParticle(data)
	local bestRarity = nil
	local bestRank = 0

	for _, rarity in pairs(data.Emitters) do
		local rank = getRarityRank(rarity)

		if rank > bestRank then
			bestRank = rank
			bestRarity = rarity
		end
	end

	if data.ParticleRarity == bestRarity then
		return
	end

	data.ParticleRarity = bestRarity
	ForceRefresh = true

	if bestRarity
		and SETTINGS.EXPORT_GENERIC_PARTICLE_TYPES
		and #data.Keys > 0
	then
		local preferredKey = nil
		local preferredPriority = -1

		for _, key in ipairs(data.Keys) do
			local priority = getKeyPriority(key)

			if priority > preferredPriority then
				preferredPriority = priority
				preferredKey = key
			end
		end

		local genericInformation = {
			Name = makeGenericName(
				bestRarity,
				preferredKey or tostring(data.Target)
			),
			Rarity = bestRarity,
			Generic = true,
		}

		registerTargetKeys(data, genericInformation)
	end
end

local function updateEmitter(emitter)
	local state = EmitterState[emitter]

	if not state then
		return
	end

	if not emitter.Parent then
		return
	end

	local rarity = classifyEmitter(emitter)
	state.Rarity = rarity

	local data = TargetData[state.Target]

	if data then
		data.Emitters[emitter] = rarity
		recomputeTargetParticle(data)
	end
end

local function unregisterEmitter(emitter)
	local state = EmitterState[emitter]

	if not state then
		return
	end

	local data = TargetData[state.Target]

	if data then
		data.Emitters[emitter] = nil
		recomputeTargetParticle(data)
	end

	EmitterState[emitter] = nil
end

local function registerEmitter(emitter)
	if not emitter:IsA("ParticleEmitter")
		or EmitterState[emitter]
	then
		return
	end

	local emitterPart = getEmitterPart(emitter)

	if not emitterPart then
		return
	end

	local target = getBlockTarget(emitterPart)

	if not target then
		return
	end

	local data = TargetData[target]
		or registerTarget(target)

	if not data then
		return
	end

	EmitterState[emitter] = {
		Target = target,
		Rarity = nil,
	}

	data.Emitters[emitter] = nil

	emitter:GetPropertyChangedSignal("Color"):Connect(function()
		updateEmitter(emitter)
	end)

	emitter:GetPropertyChangedSignal("Enabled"):Connect(function()
		if SETTINGS.REQUIRE_PARTICLE_ENABLED then
			updateEmitter(emitter)
		end
	end)

	emitter.Destroying:Connect(function()
		unregisterEmitter(emitter)
	end)

	updateEmitter(emitter)
end

--//====================================================
--// ZIELBLOCK FINDEN
--//====================================================

local TargetOverlap = OverlapParams.new()
TargetOverlap.FilterType =
	Enum.RaycastFilterType.Include

TargetOverlap.FilterDescendantsInstances = {
	SETTINGS.ROOT,
}

TargetOverlap.MaxParts = 120

local function getBreakingPart()
	local breaking =
		workspace:FindFirstChild(
			SETTINGS.BREAKING_NAME
		)

	if breaking
		and breaking:IsA("BasePart")
	then
		return breaking
	end

	return nil
end

local function getCurrentTarget()
	local breaking = getBreakingPart()

	if not breaking then
		return nil, nil
	end

	local parts = workspace:GetPartBoundsInBox(
		CFrame.new(breaking.Position),
		SETTINGS.TARGET_SEARCH_SIZE,
		TargetOverlap
	)

	local bestTarget = nil
	local bestPart = nil
	local bestScore = math.huge

	for _, part in ipairs(parts) do
		if part:IsA("BasePart") then
			local target = getBlockTarget(part)

			if target then
				local partDistance =
					(
						part.Position
						- breaking.Position
					).Magnitude

				local targetDistance =
					(
						getTargetPosition(target)
						- breaking.Position
					).Magnitude

				local score = math.min(
					partDistance,
					targetDistance
				)

				if score < bestScore then
					bestScore = score
					bestTarget = target
					bestPart = part
				end
			end
		end
	end

	if bestTarget
		and bestScore
			<= SETTINGS.TARGET_MAX_CENTER_DISTANCE
	then
		return bestTarget, bestPart
	end

	return nil, nil
end

--//====================================================
--// ZIELINFORMATION AUFLÖSEN
--//====================================================

local function resolveTargetInformation(data)
	-- Sobald Partikel vorhanden sind, gelten diese als
	-- zuverlässigere Quelle als ein Mesh-Fallback.
	if data.ParticleRarity then
		if not isRareEnough(data.ParticleRarity) then
			return nil
		end

		local namedInformation = data.NamedInfo
		local meshInformation = getMeshInformation(data)

		if namedInformation
			and namedInformation.Rarity
				== data.ParticleRarity
		then
			return namedInformation
		end

		if meshInformation
			and meshInformation.Rarity
				== data.ParticleRarity
		then
			return meshInformation
		end

		return {
			Name = "",
			Rarity = data.ParticleRarity,
			Generic = true,
		}
	end

	local meshInformation = getMeshInformation(data)

	if meshInformation
		and isRareEnough(meshInformation.Rarity)
	then
		return meshInformation
	end

	return nil
end

--//====================================================
--// ESP-POOL
--//====================================================

local HighlightPool = {}
local BillboardPool = {}
local ActiveESP = {}

local function acquireHighlight()
	local highlight = table.remove(HighlightPool)

	if highlight then
		return highlight
	end

	highlight = Instance.new("Highlight")
	highlight.Name = "RareHighlight"
	highlight.DepthMode =
		Enum.HighlightDepthMode.AlwaysOnTop
	highlight.FillTransparency =
		SETTINGS.FILL_TRANSPARENCY
	highlight.OutlineTransparency =
		SETTINGS.OUTLINE_TRANSPARENCY
	highlight.Enabled = false
	highlight.Parent = HighlightFolder

	return highlight
end

local function releaseHighlight(highlight)
	if not highlight then
		return
	end

	highlight.Enabled = false
	highlight.Adornee = nil

	if #HighlightPool
		< SETTINGS.MAX_ACTIVE_HIGHLIGHTS
	then
		table.insert(HighlightPool, highlight)
	else
		highlight:Destroy()
	end
end

local function createBillboard()
	local billboard = Instance.new("BillboardGui")
	billboard.Name = "RareBillboard"
	billboard.AlwaysOnTop = true
	billboard.LightInfluence = 0
	billboard.Size = SETTINGS.BILLBOARD_SIZE
	billboard.StudsOffsetWorldSpace =
		SETTINGS.BILLBOARD_OFFSET
	billboard.MaxDistance = SETTINGS.SCAN_RADIUS
	billboard.Enabled = false
	billboard.Parent = BillboardFolder

	local label = Instance.new("TextLabel")
	label.Name = "Text"
	label.Size = UDim2.fromScale(1, 1)
	label.BackgroundTransparency = 1
	label.Font = Enum.Font.GothamBold
	label.TextSize = SETTINGS.LABEL_TEXT_SIZE
	label.TextWrapped = false
	label.TextStrokeColor3 =
		Color3.fromRGB(0, 0, 0)
	label.TextStrokeTransparency = 0
	label.Parent = billboard

	return {
		Billboard = billboard,
		Label = label,
	}
end

local function acquireBillboard()
	return table.remove(BillboardPool)
		or createBillboard()
end

local function releaseBillboard(bundle)
	if not bundle then
		return
	end

	bundle.Billboard.Enabled = false
	bundle.Billboard.Adornee = nil
	bundle.Label.Text = ""

	if #BillboardPool
		< SETTINGS.MAX_ACTIVE_LABELS
	then
		table.insert(BillboardPool, bundle)
	else
		bundle.Billboard:Destroy()
	end
end

local function deactivateTarget(target)
	local esp = ActiveESP[target]

	if not esp then
		return
	end

	releaseHighlight(esp.Highlight)
	releaseBillboard(esp.BillboardBundle)

	ActiveESP[target] = nil
end

local function clearActiveESP()
	local targets = {}

	for target in pairs(ActiveESP) do
		table.insert(targets, target)
	end

	for _, target in ipairs(targets) do
		deactivateTarget(target)
	end
end

local function buildLabelText(information)
	if information.Name
		and information.Name ~= ""
		and not information.Generic
	then
		return string.format(
			"%s [%s]",
			information.Name,
			information.Rarity
		)
	end

	return "[" .. information.Rarity .. "]"
end

local function activateTarget(
	data,
	information,
	showLabel
)
	local target = data.Target
	local esp = ActiveESP[target]

	if not esp then
		esp = {
			Highlight = nil,
			BillboardBundle = nil,
			LastInfoKey = nil,
		}

		ActiveESP[target] = esp
	end

	local color =
		getRarityColor(information.Rarity)

	if SETTINGS.SHOW_HIGHLIGHT then
		if not esp.Highlight then
			esp.Highlight = acquireHighlight()
		end

		esp.Highlight.Adornee = target
		esp.Highlight.FillColor = color
		esp.Highlight.OutlineColor = color
		esp.Highlight.Enabled = true
	elseif esp.Highlight then
		releaseHighlight(esp.Highlight)
		esp.Highlight = nil
	end

	if SETTINGS.SHOW_TEXT
		and showLabel
		and data.LabelPart
	then
		if not esp.BillboardBundle then
			esp.BillboardBundle =
				acquireBillboard()
		end

		local bundle = esp.BillboardBundle
		local infoKey =
			tostring(information.Name)
			.. "|"
			.. tostring(information.Rarity)
			.. "|"
			.. tostring(information.Generic)

		bundle.Billboard.Adornee =
			data.LabelPart
		bundle.Billboard.Enabled = true

		if esp.LastInfoKey ~= infoKey then
			bundle.Label.Text =
				buildLabelText(information)

			bundle.Label.TextColor3 = color
			esp.LastInfoKey = infoKey
		end
	elseif esp.BillboardBundle then
		releaseBillboard(esp.BillboardBundle)
		esp.BillboardBundle = nil
		esp.LastInfoKey = nil
	end
end

--//====================================================
--// UMGEBUNGSSCANNER
--//====================================================

local LastCharacterPosition = nil

local function getCharacterRoot()
	local character = LocalPlayer.Character

	if not character then
		return nil
	end

	return character:FindFirstChild(
		"HumanoidRootPart"
	)
end

local function collectNearbyCandidates(position)
	local candidates = {}
	local seenTargets = {}

	local radius = SETTINGS.SCAN_RADIUS
	local cellSize = SETTINGS.CELL_SIZE

	local minimumX =
		math.floor((position.X - radius) / cellSize)

	local maximumX =
		math.floor((position.X + radius) / cellSize)

	local minimumY =
		math.floor((position.Y - radius) / cellSize)

	local maximumY =
		math.floor((position.Y + radius) / cellSize)

	local minimumZ =
		math.floor((position.Z - radius) / cellSize)

	local maximumZ =
		math.floor((position.Z + radius) / cellSize)

	local radiusSquared = radius * radius

	for x = minimumX, maximumX do
		for y = minimumY, maximumY do
			for z = minimumZ, maximumZ do
				local bucket =
					SpatialCells[
						makeCellKey(x, y, z)
					]

				if bucket then
					for target in pairs(bucket) do
						if not seenTargets[target] then
							seenTargets[target] = true

							local data =
								TargetData[target]

							if data
								and target.Parent
							then
								local offset =
									data.Position
									- position

								local distanceSquared =
									offset:Dot(offset)

								if distanceSquared
									<= radiusSquared
								then
									local information =
										resolveTargetInformation(
											data
										)

									if information then
										table.insert(
											candidates,
											{
												Data = data,
												Information =
													information,
												DistanceSquared =
													distanceSquared,
											}
										)
									end
								end
							end
						end
					end
				end
			end
		end
	end

	table.sort(candidates, function(a, b)
		return a.DistanceSquared
			< b.DistanceSquared
	end)

	return candidates
end

local function refreshVisibleESP(force)
	if not SETTINGS.ENABLED then
		clearActiveESP()
		return
	end

	local characterRoot = getCharacterRoot()

	if not characterRoot then
		return
	end

	local currentPosition =
		characterRoot.Position

	if not force
		and not ForceRefresh
		and LastCharacterPosition
		and (
			currentPosition
			- LastCharacterPosition
		).Magnitude < SETTINGS.MOVE_THRESHOLD
	then
		return
	end

	LastCharacterPosition = currentPosition
	ForceRefresh = false

	local candidates =
		collectNearbyCandidates(currentPosition)

	local desiredTargets = {}

	local highlightCount = math.min(
		#candidates,
		SETTINGS.MAX_ACTIVE_HIGHLIGHTS
	)

	for index = 1, highlightCount do
		local candidate = candidates[index]
		local data = candidate.Data

		desiredTargets[data.Target] = true

		activateTarget(
			data,
			candidate.Information,
			index <= SETTINGS.MAX_ACTIVE_LABELS
		)
	end

	local removeList = {}

	for target in pairs(ActiveESP) do
		if not desiredTargets[target] then
			table.insert(removeList, target)
		end
	end

	for _, target in ipairs(removeList) do
		deactivateTarget(target)
	end
end

--//====================================================
--// ORE AUTO-FARM MIT AUSWAHL-GUI
--//====================================================

local AUTO_FARM_GUI_NAME = "_OptimizedOreAutoFarmGui"

local SelectedOreGroups = {}
local OreSelectionTouched = {}
local OreGroups = {}
local OreRowButtons = {}
local OreSelectionInitialized = false

local AutoFarmGui = nil
local AutoFarmMain = nil
local AutoFarmList = nil
local AutoFarmListLayout = nil
local AutoFarmSearchBox = nil
local AutoFarmToggleButton = nil
local AutoFarmPriorityButton = nil
local AutoFarmGlideButton = nil
local AutoFarmCameraButton = nil
local AutoFarmStatusLabel = nil
local AutoFarmSelectionLabel = nil

local AutoFarmState = {
	CurrentData = nil,
	CurrentInformation = nil,
	CurrentGroupId = nil,
	TargetStartedAt = 0,
	MiningStartedAt = nil,
	LastStatus = "Bereit",
	MouseDown = false,
	LastMouseX = 0,
	LastMouseY = 0,
	SavedCameraCFrame = nil,
	Moving = false,
	MoveTween = nil,
	MoveToken = 0,
	BodyVelocity = nil,
	CollisionStates = nil,
	IgnoredUntil = {},
	LastCandidateCount = 0,
	LastDistance = nil,
	InputError = nil,
}

local function getOreGroupId(information)
	local name = tostring(information.Name or "")
	local rarity = tostring(information.Rarity or "Unknown")

	if name == "" then
		name = "Auto_" .. rarity
	end

	return string.lower(name)
		.. "|"
		.. string.lower(rarity)
end

local function shouldSelectGroupByDefault(group)
	if SETTINGS.AUTO_FARM_DEFAULT_SELECTION == "All" then
		return true
	end

	if SETTINGS.AUTO_FARM_DEFAULT_SELECTION == "None" then
		return false
	end

	return getRarityRank(group.Rarity)
		>= getRarityRank("Rare")
end

local function buildOreGroups()
	local groupsById = {}

	for key, information in pairs(KnownKeys) do
		if not ConflictingKeys[key] then
			local name = tostring(information.Name or "")
			local rarity = normalizeRarity(information.Rarity)

			if name ~= "" and rarity then
				local groupId = getOreGroupId(information)
				local group = groupsById[groupId]

				if not group then
					group = {
						Id = groupId,
						Name = name,
						Rarity = rarity,
						Generic = information.Generic == true,
						KeyCount = 0,
					}

					groupsById[groupId] = group
				end

				group.KeyCount += 1
			end
		end
	end

	local groups = {}

	for _, group in pairs(groupsById) do
		table.insert(groups, group)
	end

	table.sort(groups, function(a, b)
		local rankA = getRarityRank(a.Rarity)
		local rankB = getRarityRank(b.Rarity)

		if rankA ~= rankB then
			return rankA > rankB
		end

		if a.Name ~= b.Name then
			return a.Name < b.Name
		end

		return a.Id < b.Id
	end)

	return groups
end

local function ensureOreSelectionInitialized()
	if not OreSelectionInitialized or OreListDirty then
		OreGroups = buildOreGroups()

		for _, group in ipairs(OreGroups) do
			if SelectedOreGroups[group.Id] == nil
				and not OreSelectionTouched[group.Id]
			then
				SelectedOreGroups[group.Id] =
					shouldSelectGroupByDefault(group)
			end
		end

		OreSelectionInitialized = true
		OreListDirty = false
	end
end

local function countSelectedOreGroups()
	ensureOreSelectionInitialized()

	local selected = 0

	for _, group in ipairs(OreGroups) do
		if SelectedOreGroups[group.Id] then
			selected += 1
		end
	end

	return selected, #OreGroups
end

local function isOreInformationSelected(information)
	if not information then
		return false
	end

	ensureOreSelectionInitialized()
	return SelectedOreGroups[
		getOreGroupId(information)
	] == true
end

local function setAutoFarmStatus(text)
	text = tostring(text or "")
	AutoFarmState.LastStatus = text

	if AutoFarmStatusLabel
		and AutoFarmStatusLabel.Text ~= text
	then
		AutoFarmStatusLabel.Text = text
	end
end

local function safeSendMouseMove(x, y)
	local success, result = pcall(function()
		VirtualInputManager:SendMouseMoveEvent(
			x,
			y,
			game
		)
	end)

	if not success then
		AutoFarmState.InputError = tostring(result)
	end

	return success
end

local function safeSendMouseButton(x, y, isDown)
	local success, result = pcall(function()
		VirtualInputManager:SendMouseButtonEvent(
			x,
			y,
			0,
			isDown,
			game,
			1
		)
	end)

	if not success then
		AutoFarmState.InputError = tostring(result)
	end

	return success
end

local function restoreSavedCamera()
	if not SETTINGS.AUTO_FARM_RESTORE_CAMERA_AFTER_TARGET
		or not AutoFarmState.SavedCameraCFrame
	then
		AutoFarmState.SavedCameraCFrame = nil
		return
	end

	local camera = workspace.CurrentCamera

	if camera then
		local rotation =
			AutoFarmState.SavedCameraCFrame
			- AutoFarmState.SavedCameraCFrame.Position

		camera.CFrame =
			CFrame.new(camera.CFrame.Position)
			* rotation
	end

	AutoFarmState.SavedCameraCFrame = nil
end

local function releaseMiningInput()
	if AutoFarmState.MouseDown then
		safeSendMouseButton(
			AutoFarmState.LastMouseX,
			AutoFarmState.LastMouseY,
			false
		)
	end

	AutoFarmState.MouseDown = false
	AutoFarmState.MiningStartedAt = nil
	restoreSavedCamera()
end

local function restoreCharacterCollisions()
	local collisionStates = AutoFarmState.CollisionStates

	if collisionStates then
		for part, canCollide in pairs(collisionStates) do
			if part and part.Parent then
				part.CanCollide = canCollide
			end
		end
	end

	AutoFarmState.CollisionStates = nil
end

local function stopGlide()
	AutoFarmState.MoveToken += 1

	if AutoFarmState.MoveTween then
		pcall(function()
			AutoFarmState.MoveTween:Cancel()
		end)
	end

	AutoFarmState.MoveTween = nil

	if AutoFarmState.BodyVelocity then
		AutoFarmState.BodyVelocity:Destroy()
		AutoFarmState.BodyVelocity = nil
	end

	restoreCharacterCollisions()
	AutoFarmState.Moving = false
end

local function clearAutoFarmTarget(reason)
	releaseMiningInput()
	stopGlide()

	AutoFarmState.CurrentData = nil
	AutoFarmState.CurrentInformation = nil
	AutoFarmState.CurrentGroupId = nil
	AutoFarmState.TargetStartedAt = 0
	AutoFarmState.LastDistance = nil

	if reason then
		setAutoFarmStatus(reason)
	end
end

local function getMineAimPart(data, rootPosition)
	if data.MineAimPart
		and data.MineAimPart.Parent
		and data.MineAimPart:IsDescendantOf(data.Target)
	then
		return data.MineAimPart
	end

	local target = data.Target

	if target:IsA("BasePart") then
		data.MineAimPart = target
		return target
	end

	local bestPart = nil
	local bestDistance = math.huge

	if target:IsA("Model") then
		for _, descendant in ipairs(target:GetDescendants()) do
			if descendant:IsA("BasePart") then
				local distance =
					(descendant.Position - rootPosition).Magnitude

				if distance < bestDistance then
					bestDistance = distance
					bestPart = descendant
				end
			end
		end
	end

	data.MineAimPart = bestPart or data.LabelPart
	return data.MineAimPart
end

local function getMineAimPosition(data, rootPosition)
	local aimPart = getMineAimPart(data, rootPosition)

	if aimPart and aimPart.Parent then
		return aimPart.Position, aimPart
	end

	return data.Position, nil
end

local function isTargetIgnored(target)
	local ignoredUntil = AutoFarmState.IgnoredUntil[target]

	if not ignoredUntil then
		return false
	end

	if os.clock() >= ignoredUntil then
		AutoFarmState.IgnoredUntil[target] = nil
		return false
	end

	return true
end

local function isAutoFarmDataValid(data, rootPosition)
	if not data
		or not data.Target
		or not data.Target.Parent
		or not data.Target:IsDescendantOf(SETTINGS.ROOT)
		or isTargetIgnored(data.Target)
	then
		return false, nil, nil
	end

	local information = resolveTargetInformation(data)

	if not information
		or not isOreInformationSelected(information)
	then
		return false, nil, nil
	end

	local offset = data.Position - rootPosition
	local distanceSquared = offset:Dot(offset)

	if distanceSquared
		> SETTINGS.AUTO_FARM_SCAN_RADIUS
			* SETTINGS.AUTO_FARM_SCAN_RADIUS
	then
		return false, nil, nil
	end

	if not SETTINGS.AUTO_FARM_GLIDE_ENABLED
		and distanceSquared
			> SETTINGS.AUTO_FARM_MINE_DISTANCE
				* SETTINGS.AUTO_FARM_MINE_DISTANCE
	then
		return false, nil, nil
	end

	return true, information, distanceSquared
end

local function chooseAutoFarmTarget(rootPosition)
	ensureOreSelectionInitialized()

	local bestData = nil
	local bestInformation = nil
	local bestDistanceSquared = math.huge
	local bestRank = -1
	local candidateCount = 0

	for _, data in pairs(TargetData) do
		local valid, information, distanceSquared =
			isAutoFarmDataValid(data, rootPosition)

		if valid then
			candidateCount += 1

			local rank = getRarityRank(information.Rarity)
			local choose = false

			if SETTINGS.AUTO_FARM_PRIORITY == "Nearest" then
				choose = distanceSquared < bestDistanceSquared
			else
				choose = rank > bestRank
					or (
						rank == bestRank
						and distanceSquared < bestDistanceSquared
					)
			end

			if choose then
				bestData = data
				bestInformation = information
				bestDistanceSquared = distanceSquared
				bestRank = rank
			end
		end
	end

	AutoFarmState.LastCandidateCount = candidateCount

	return bestData,
		bestInformation,
		bestDistanceSquared
end

local function startGlideToCurrentTarget()
	if not SETTINGS.AUTO_FARM_GLIDE_ENABLED
		or AutoFarmState.Moving
		or not AutoFarmState.CurrentData
	then
		return
	end

	local character = LocalPlayer.Character
	local root = getCharacterRoot()

	if not character or not root then
		return
	end

	local targetPosition = getMineAimPosition(
		AutoFarmState.CurrentData,
		root.Position
	)

	local away = root.Position - targetPosition

	if away.Magnitude < 0.01 then
		away = Vector3.new(0, 0, 1)
	end

	local destination = targetPosition
		+ away.Unit * SETTINGS.AUTO_FARM_STOP_DISTANCE

	local distance = (destination - root.Position).Magnitude

	if distance <= 0.5 then
		return
	end

	stopGlide()

	AutoFarmState.MoveToken += 1
	local token = AutoFarmState.MoveToken
	AutoFarmState.Moving = true

	if SETTINGS.AUTO_FARM_NOCLIP_WHILE_GLIDING then
		local collisionStates = {}

		for _, descendant in ipairs(character:GetDescendants()) do
			if descendant:IsA("BasePart") then
				collisionStates[descendant] = descendant.CanCollide
				descendant.CanCollide = false
			end
		end

		AutoFarmState.CollisionStates = collisionStates
	end

	local bodyVelocity = Instance.new("BodyVelocity")
	bodyVelocity.Name = "_OreAutoFarmVelocity"
	bodyVelocity.MaxForce = Vector3.new(
		math.huge,
		math.huge,
		math.huge
	)
	bodyVelocity.Velocity = Vector3.zero
	bodyVelocity.Parent = root
	AutoFarmState.BodyVelocity = bodyVelocity

	local duration = math.max(
		0.05,
		distance / SETTINGS.AUTO_FARM_GLIDE_SPEED
	)

	local destinationCFrame = CFrame.lookAt(
		destination,
		targetPosition
	)

	local tween = TweenService:Create(
		root,
		TweenInfo.new(
			duration,
			Enum.EasingStyle.Linear,
			Enum.EasingDirection.Out
		),
		{
			CFrame = destinationCFrame,
		}
	)

	AutoFarmState.MoveTween = tween

	tween.Completed:Connect(function()
		if token ~= AutoFarmState.MoveToken then
			return
		end

		AutoFarmState.MoveTween = nil

		if AutoFarmState.BodyVelocity then
			AutoFarmState.BodyVelocity:Destroy()
			AutoFarmState.BodyVelocity = nil
		end

		restoreCharacterCollisions()
		AutoFarmState.Moving = false
	end)

	tween:Play()
end

local function setAutoFarmEnabled(enabled)
	SETTINGS.AUTO_FARM_ENABLED = enabled == true

	if not SETTINGS.AUTO_FARM_ENABLED then
		clearAutoFarmTarget("Auto-Farm ausgeschaltet")
	else
		setAutoFarmStatus("Suche ausgewählte Erze …")
	end
end

local function updateCurrentAutoFarmTarget()
	if not SETTINGS.AUTO_FARM_ENABLED then
		return
	end

	local root = getCharacterRoot()

	if not root then
		clearAutoFarmTarget("Charakter wird geladen …")
		return
	end

	local data = AutoFarmState.CurrentData
	local information = AutoFarmState.CurrentInformation

	if data then
		local valid, refreshedInformation, distanceSquared =
			isAutoFarmDataValid(data, root.Position)

		if valid then
			information = refreshedInformation
			AutoFarmState.CurrentInformation = refreshedInformation
			AutoFarmState.LastDistance = math.sqrt(distanceSquared)
		else
			clearAutoFarmTarget("Suche nächstes Ziel …")
			data = nil
		end
	end

	if not data then
		local distanceSquared

		data, information, distanceSquared =
			chooseAutoFarmTarget(root.Position)

		if not data then
			releaseMiningInput()
			stopGlide()

			local selectedCount = countSelectedOreGroups()

			if selectedCount == 0 then
				setAutoFarmStatus("Keine Erzart ausgewählt")
			else
				setAutoFarmStatus("Kein ausgewähltes Erz im Radius")
			end

			return
		end

		AutoFarmState.CurrentData = data
		AutoFarmState.CurrentInformation = information
		AutoFarmState.CurrentGroupId =
			getOreGroupId(information)
		AutoFarmState.TargetStartedAt = os.clock()
		AutoFarmState.MiningStartedAt = nil
		AutoFarmState.LastDistance =
			math.sqrt(distanceSquared)
	end

	if not data.Target.Parent then
		clearAutoFarmTarget("Ziel abgebaut")
		return
	end

	local distance =
		(data.Position - root.Position).Magnitude

	AutoFarmState.LastDistance = distance

	if distance > SETTINGS.AUTO_FARM_MINE_DISTANCE then
		releaseMiningInput()

		if SETTINGS.AUTO_FARM_GLIDE_ENABLED then
			setAutoFarmStatus(string.format(
				"Fliege zu %s [%s] – %.0f Studs",
				information.Name,
				information.Rarity,
				distance
			))

			startGlideToCurrentTarget()
		else
			setAutoFarmStatus(string.format(
				"%s ist %.0f Studs entfernt",
				information.Name,
				distance
			))
		end
	else
		stopGlide()
		setAutoFarmStatus(string.format(
			"Baue %s [%s] ab – %.1f Studs",
			information.Name,
			information.Rarity,
			distance
		))
	end

	if AutoFarmState.MiningStartedAt
		and os.clock() - AutoFarmState.MiningStartedAt
			>= SETTINGS.AUTO_FARM_STUCK_TIMEOUT
	then
		AutoFarmState.IgnoredUntil[data.Target] =
			os.clock() + SETTINGS.AUTO_FARM_IGNORE_TIME

		clearAutoFarmTarget(
			"Ziel reagiert nicht – vorübergehend übersprungen"
		)
	end
end

task.spawn(function()
	while task.wait(
		SETTINGS.AUTO_FARM_TARGET_REFRESH_INTERVAL
	) do
		updateCurrentAutoFarmTarget()
	end
end)

RunService.RenderStepped:Connect(function()
	if not SETTINGS.AUTO_FARM_ENABLED then
		return
	end

	local data = AutoFarmState.CurrentData
	local information = AutoFarmState.CurrentInformation
	local root = getCharacterRoot()
	local camera = workspace.CurrentCamera

	if not data
		or not information
		or not root
		or not camera
		or not data.Target.Parent
	then
		releaseMiningInput()
		return
	end

	local targetPosition = getMineAimPosition(
		data,
		root.Position
	)

	local distance =
		(targetPosition - root.Position).Magnitude

	if distance > SETTINGS.AUTO_FARM_MINE_DISTANCE then
		releaseMiningInput()
		return
	end

	stopGlide()

	if SETTINGS.AUTO_FARM_ROTATE_CHARACTER then
		local horizontalTarget = Vector3.new(
			targetPosition.X,
			root.Position.Y,
			targetPosition.Z
		)

		if (horizontalTarget - root.Position).Magnitude > 0.01 then
			root.CFrame = CFrame.lookAt(
				root.Position,
				horizontalTarget
			)
		end
	end

	if SETTINGS.AUTO_FARM_AIM_CAMERA then
		if not AutoFarmState.SavedCameraCFrame then
			AutoFarmState.SavedCameraCFrame = camera.CFrame
		end

		camera.CFrame = CFrame.lookAt(
			camera.CFrame.Position,
			targetPosition
		)
	end

	local screenPosition, onScreen =
		camera:WorldToViewportPoint(targetPosition)

	if not SETTINGS.AUTO_FARM_AIM_CAMERA
		and not onScreen
	then
		releaseMiningInput()
		setAutoFarmStatus(
			"MouseOnly: Ziel befindet sich nicht im Bild"
		)
		return
	end

	AutoFarmState.LastMouseX = screenPosition.X
	AutoFarmState.LastMouseY = screenPosition.Y

	safeSendMouseMove(
		screenPosition.X,
		screenPosition.Y
	)

	if not AutoFarmState.MouseDown then
		local success = safeSendMouseButton(
			screenPosition.X,
			screenPosition.Y,
			true
		)

		if success then
			AutoFarmState.MouseDown = true
			AutoFarmState.MiningStartedAt = os.clock()
		else
			setAutoFarmStatus(
				"VirtualInputManager konnte nicht senden"
			)
		end
	end
end)

--//====================================================
--// ORE-AUSWAHL-GUI
--//====================================================

local function setOreGroupSelected(groupId, selected)
	SelectedOreGroups[groupId] = selected == true
	OreSelectionTouched[groupId] = true

	if AutoFarmState.CurrentGroupId == groupId
		and not SelectedOreGroups[groupId]
	then
		clearAutoFarmTarget("Ziel wurde abgewählt")
	end
end

local function updateOreRowVisual(groupId)
	local row = OreRowButtons[groupId]

	if not row then
		return
	end

	local selected = SelectedOreGroups[groupId] == true
	row.Button.Text = selected
		and "  ✓  " .. row.DisplayText
		or "      " .. row.DisplayText

	row.Button.BackgroundColor3 = selected
		and Color3.fromRGB(42, 92, 62)
		or Color3.fromRGB(43, 43, 51)
end

local function applyOreSearchFilter()
	local search = ""

	if AutoFarmSearchBox then
		search = string.lower(AutoFarmSearchBox.Text)
	end

	for _, row in pairs(OreRowButtons) do
		row.Button.Visible = search == ""
			or string.find(
				row.SearchText,
				search,
				1,
				true
			) ~= nil
	end

	task.defer(function()
		if AutoFarmList
			and AutoFarmListLayout
		then
			AutoFarmList.CanvasSize =
				UDim2.fromOffset(
					0,
					AutoFarmListLayout.AbsoluteContentSize.Y
						+ 12
				)
		end
	end)
end

local function refreshOreSelectionLabels()
	local selected, total = countSelectedOreGroups()

	if AutoFarmSelectionLabel then
		AutoFarmSelectionLabel.Text = string.format(
			"Ausgewählt: %d/%d   |   Ziele im Radius: %d",
			selected,
			total,
			AutoFarmState.LastCandidateCount
		)
	end

	if AutoFarmToggleButton then
		AutoFarmToggleButton.Text = SETTINGS.AUTO_FARM_ENABLED
			and "AUTO-FARM: AN"
			or "AUTO-FARM: AUS"

		AutoFarmToggleButton.BackgroundColor3 =
			SETTINGS.AUTO_FARM_ENABLED
			and Color3.fromRGB(38, 130, 74)
			or Color3.fromRGB(135, 50, 55)
	end

	if AutoFarmPriorityButton then
		AutoFarmPriorityButton.Text =
			SETTINGS.AUTO_FARM_PRIORITY == "Nearest"
			and "Priorität: Nächstes"
			or "Priorität: Seltenstes"
	end

	if AutoFarmGlideButton then
		AutoFarmGlideButton.Text =
			SETTINGS.AUTO_FARM_GLIDE_ENABLED
			and "Bewegen: AN"
			or "Bewegen: AUS"
	end

	if AutoFarmCameraButton then
		AutoFarmCameraButton.Text =
			SETTINGS.AUTO_FARM_AIM_CAMERA
			and "Kamera-Ziel: AN"
			or "Kamera-Ziel: AUS"
	end

	if AutoFarmStatusLabel then
		AutoFarmStatusLabel.Text =
			AutoFarmState.LastStatus
	end
end

local function rebuildOreRows()
	if not AutoFarmList then
		return
	end

	ensureOreSelectionInitialized()

	for _, row in pairs(OreRowButtons) do
		if row.Button then
			row.Button:Destroy()
		end
	end

	OreRowButtons = {}

	for _, group in ipairs(OreGroups) do
		local button = Instance.new("TextButton")
		button.Name = "Ore_" .. group.Id
		button.Size = UDim2.new(1, -8, 0, 34)
		button.BackgroundColor3 = Color3.fromRGB(43, 43, 51)
		button.BorderSizePixel = 0
		button.AutoButtonColor = true
		button.Font = Enum.Font.Gotham
		button.TextSize = 13
		button.TextColor3 = getRarityColor(group.Rarity)
		button.TextXAlignment = Enum.TextXAlignment.Left
		button.Parent = AutoFarmList

		local corner = Instance.new("UICorner")
		corner.CornerRadius = UDim.new(0, 6)
		corner.Parent = button

		local displayText = string.format(
			"%s [%s]",
			group.Name,
			group.Rarity
		)

		OreRowButtons[group.Id] = {
			Button = button,
			DisplayText = displayText,
			SearchText = string.lower(displayText),
		}

		button.MouseButton1Click:Connect(function()
			setOreGroupSelected(
				group.Id,
				not SelectedOreGroups[group.Id]
			)

			updateOreRowVisual(group.Id)
			refreshOreSelectionLabels()
		end)

		updateOreRowVisual(group.Id)
	end

	applyOreSearchFilter()
	refreshOreSelectionLabels()
end

local function selectOreGroups(mode)
	ensureOreSelectionInitialized()

	for _, group in ipairs(OreGroups) do
		local selected = false

		if mode == "All" then
			selected = true
		elseif mode == "RarePlus" then
			selected = getRarityRank(group.Rarity)
				>= getRarityRank("Rare")
		elseif mode == "TNT" then
			selected = string.find(
				string.lower(group.Name),
				"tnt",
				1,
				true
			) ~= nil
		end

		setOreGroupSelected(group.Id, selected)
		updateOreRowVisual(group.Id)
	end

	refreshOreSelectionLabels()
end

local function createGuiButton(parent, text, size)
	local button = Instance.new("TextButton")
	button.Size = size
	button.BackgroundColor3 = Color3.fromRGB(54, 75, 126)
	button.BorderSizePixel = 0
	button.Font = Enum.Font.GothamBold
	button.Text = text
	button.TextColor3 = Color3.new(1, 1, 1)
	button.TextSize = 12
	button.Parent = parent

	local corner = Instance.new("UICorner")
	corner.CornerRadius = UDim.new(0, 7)
	corner.Parent = button

	return button
end

local function makeFrameDraggable(frame, dragHandle)
	local dragging = false
	local dragStart = nil
	local startPosition = nil

	dragHandle.InputBegan:Connect(function(input)
		if input.UserInputType
			== Enum.UserInputType.MouseButton1
		or input.UserInputType
			== Enum.UserInputType.Touch
		then
			dragging = true
			dragStart = input.Position
			startPosition = frame.Position
		end
	end)

	UserInputService.InputChanged:Connect(function(input)
		if not dragging then
			return
		end

		if input.UserInputType
			== Enum.UserInputType.MouseMovement
		or input.UserInputType
			== Enum.UserInputType.Touch
		then
			local delta = input.Position - dragStart

			frame.Position = UDim2.new(
				startPosition.X.Scale,
				startPosition.X.Offset + delta.X,
				startPosition.Y.Scale,
				startPosition.Y.Offset + delta.Y
			)
		end
	end)

	UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType
			== Enum.UserInputType.MouseButton1
		or input.UserInputType
			== Enum.UserInputType.Touch
		then
			dragging = false
		end
	end)
end

local function ensureAutoFarmGui()
	if AutoFarmGui then
		return
	end

	local oldGui = PlayerGui:FindFirstChild(
		AUTO_FARM_GUI_NAME
	)

	if oldGui then
		oldGui:Destroy()
	end

	AutoFarmGui = Instance.new("ScreenGui")
	AutoFarmGui.Name = AUTO_FARM_GUI_NAME
	AutoFarmGui.ResetOnSpawn = false
	AutoFarmGui.IgnoreGuiInset = false
	AutoFarmGui.DisplayOrder = 1000
	AutoFarmGui.Parent = PlayerGui

	local main = Instance.new("Frame")
	main.Name = "Main"
	main.Position = UDim2.new(0, 18, 0.5, -290)
	main.Size = UDim2.fromOffset(470, 580)
	main.BackgroundColor3 = Color3.fromRGB(25, 25, 31)
	main.BorderSizePixel = 0
	main.Active = true
	main.Parent = AutoFarmGui
	AutoFarmMain = main

	local mainCorner = Instance.new("UICorner")
	mainCorner.CornerRadius = UDim.new(0, 11)
	mainCorner.Parent = main

	local titleBar = Instance.new("Frame")
	titleBar.Name = "TitleBar"
	titleBar.Size = UDim2.new(1, 0, 0, 40)
	titleBar.BackgroundColor3 = Color3.fromRGB(34, 34, 42)
	titleBar.BorderSizePixel = 0
	titleBar.Active = true
	titleBar.Parent = main

	local titleCorner = Instance.new("UICorner")
	titleCorner.CornerRadius = UDim.new(0, 11)
	titleCorner.Parent = titleBar

	local title = Instance.new("TextLabel")
	title.BackgroundTransparency = 1
	title.Position = UDim2.fromOffset(12, 0)
	title.Size = UDim2.new(1, -96, 1, 0)
	title.Font = Enum.Font.GothamBold
	title.Text = "Ore Auto-Farm"
	title.TextColor3 = Color3.new(1, 1, 1)
	title.TextSize = 17
	title.TextXAlignment = Enum.TextXAlignment.Left
	title.Parent = titleBar

	local hideButton = createGuiButton(
		titleBar,
		"—",
		UDim2.fromOffset(34, 28)
	)
	hideButton.Position = UDim2.new(1, -42, 0, 6)
	hideButton.BackgroundColor3 = Color3.fromRGB(61, 61, 72)

	hideButton.MouseButton1Click:Connect(function()
		AutoFarmGui.Enabled = false
	end)

	makeFrameDraggable(main, titleBar)

	local toggleButton = createGuiButton(
		main,
		"AUTO-FARM: AUS",
		UDim2.new(1, -24, 0, 40)
	)
	toggleButton.Position = UDim2.fromOffset(12, 50)
	AutoFarmToggleButton = toggleButton

	toggleButton.MouseButton1Click:Connect(function()
		setAutoFarmEnabled(
			not SETTINGS.AUTO_FARM_ENABLED
		)
		refreshOreSelectionLabels()
	end)

	local statusLabel = Instance.new("TextLabel")
	statusLabel.BackgroundTransparency = 1
	statusLabel.Position = UDim2.fromOffset(12, 94)
	statusLabel.Size = UDim2.new(1, -24, 0, 28)
	statusLabel.Font = Enum.Font.Gotham
	statusLabel.Text = AutoFarmState.LastStatus
	statusLabel.TextColor3 = Color3.fromRGB(220, 220, 225)
	statusLabel.TextSize = 12
	statusLabel.TextWrapped = true
	statusLabel.TextXAlignment = Enum.TextXAlignment.Left
	statusLabel.Parent = main
	AutoFarmStatusLabel = statusLabel

	local selectionLabel = Instance.new("TextLabel")
	selectionLabel.BackgroundTransparency = 1
	selectionLabel.Position = UDim2.fromOffset(12, 121)
	selectionLabel.Size = UDim2.new(1, -24, 0, 22)
	selectionLabel.Font = Enum.Font.Gotham
	selectionLabel.Text = ""
	selectionLabel.TextColor3 = Color3.fromRGB(160, 160, 170)
	selectionLabel.TextSize = 11
	selectionLabel.TextXAlignment = Enum.TextXAlignment.Left
	selectionLabel.Parent = main
	AutoFarmSelectionLabel = selectionLabel

	local searchBox = Instance.new("TextBox")
	searchBox.Position = UDim2.fromOffset(12, 148)
	searchBox.Size = UDim2.new(1, -24, 0, 34)
	searchBox.BackgroundColor3 = Color3.fromRGB(17, 17, 22)
	searchBox.BorderSizePixel = 0
	searchBox.ClearTextOnFocus = false
	searchBox.Font = Enum.Font.Gotham
	searchBox.PlaceholderText = "Erz suchen …"
	searchBox.Text = ""
	searchBox.TextColor3 = Color3.new(1, 1, 1)
	searchBox.PlaceholderColor3 = Color3.fromRGB(120, 120, 130)
	searchBox.TextSize = 13
	searchBox.Parent = main
	AutoFarmSearchBox = searchBox

	local searchCorner = Instance.new("UICorner")
	searchCorner.CornerRadius = UDim.new(0, 7)
	searchCorner.Parent = searchBox

	searchBox:GetPropertyChangedSignal("Text"):Connect(
		applyOreSearchFilter
	)

	local quickBar = Instance.new("Frame")
	quickBar.BackgroundTransparency = 1
	quickBar.Position = UDim2.fromOffset(12, 190)
	quickBar.Size = UDim2.new(1, -24, 0, 32)
	quickBar.Parent = main

	local quickLayout = Instance.new("UIListLayout")
	quickLayout.FillDirection = Enum.FillDirection.Horizontal
	quickLayout.Padding = UDim.new(0, 6)
	quickLayout.Parent = quickBar

	local allButton = createGuiButton(
		quickBar,
		"Alle",
		UDim2.new(0.25, -5, 1, 0)
	)

	local noneButton = createGuiButton(
		quickBar,
		"Keine",
		UDim2.new(0.25, -5, 1, 0)
	)

	local rareButton = createGuiButton(
		quickBar,
		"Rare+",
		UDim2.new(0.25, -5, 1, 0)
	)

	local tntButton = createGuiButton(
		quickBar,
		"Nur TNT",
		UDim2.new(0.25, -5, 1, 0)
	)

	allButton.MouseButton1Click:Connect(function()
		selectOreGroups("All")
	end)

	noneButton.MouseButton1Click:Connect(function()
		selectOreGroups("None")
	end)

	rareButton.MouseButton1Click:Connect(function()
		selectOreGroups("RarePlus")
	end)

	tntButton.MouseButton1Click:Connect(function()
		selectOreGroups("TNT")
	end)

	local optionBar = Instance.new("Frame")
	optionBar.BackgroundTransparency = 1
	optionBar.Position = UDim2.fromOffset(12, 229)
	optionBar.Size = UDim2.new(1, -24, 0, 32)
	optionBar.Parent = main

	local optionLayout = Instance.new("UIListLayout")
	optionLayout.FillDirection = Enum.FillDirection.Horizontal
	optionLayout.Padding = UDim.new(0, 6)
	optionLayout.Parent = optionBar

	local priorityButton = createGuiButton(
		optionBar,
		"Priorität: Seltenstes",
		UDim2.new(0.38, -4, 1, 0)
	)
	AutoFarmPriorityButton = priorityButton

	local glideButton = createGuiButton(
		optionBar,
		"Bewegen: AN",
		UDim2.new(0.29, -4, 1, 0)
	)
	AutoFarmGlideButton = glideButton

	local cameraButton = createGuiButton(
		optionBar,
		"Kamera-Ziel: AN",
		UDim2.new(0.33, -4, 1, 0)
	)
	AutoFarmCameraButton = cameraButton

	priorityButton.MouseButton1Click:Connect(function()
		SETTINGS.AUTO_FARM_PRIORITY =
			SETTINGS.AUTO_FARM_PRIORITY == "Nearest"
			and "HighestRarity"
			or "Nearest"

		clearAutoFarmTarget("Priorität geändert")
		refreshOreSelectionLabels()
	end)

	glideButton.MouseButton1Click:Connect(function()
		SETTINGS.AUTO_FARM_GLIDE_ENABLED =
			not SETTINGS.AUTO_FARM_GLIDE_ENABLED

		stopGlide()
		clearAutoFarmTarget("Bewegungsmodus geändert")
		refreshOreSelectionLabels()
	end)

	cameraButton.MouseButton1Click:Connect(function()
		SETTINGS.AUTO_FARM_AIM_CAMERA =
			not SETTINGS.AUTO_FARM_AIM_CAMERA

		releaseMiningInput()
		refreshOreSelectionLabels()
	end)

	local list = Instance.new("ScrollingFrame")
	list.Name = "OreList"
	list.Position = UDim2.fromOffset(12, 270)
	list.Size = UDim2.new(1, -24, 1, -282)
	list.BackgroundColor3 = Color3.fromRGB(17, 17, 22)
	list.BorderSizePixel = 0
	list.ScrollBarThickness = 7
	list.CanvasSize = UDim2.fromOffset(0, 0)
	list.Parent = main
	AutoFarmList = list

	local listCorner = Instance.new("UICorner")
	listCorner.CornerRadius = UDim.new(0, 8)
	listCorner.Parent = list

	local listPadding = Instance.new("UIPadding")
	listPadding.PaddingTop = UDim.new(0, 6)
	listPadding.PaddingBottom = UDim.new(0, 6)
	listPadding.PaddingLeft = UDim.new(0, 6)
	listPadding.PaddingRight = UDim.new(0, 6)
	listPadding.Parent = list

	local listLayout = Instance.new("UIListLayout")
	listLayout.Padding = UDim.new(0, 5)
	listLayout.Parent = list
	AutoFarmListLayout = listLayout

	listLayout:GetPropertyChangedSignal(
		"AbsoluteContentSize"
	):Connect(function()
		list.CanvasSize = UDim2.fromOffset(
			0,
			listLayout.AbsoluteContentSize.Y + 12
		)
	end)

	rebuildOreRows()
end

local function toggleAutoFarmGui()
	ensureAutoFarmGui()
	AutoFarmGui.Enabled = not AutoFarmGui.Enabled

	if AutoFarmGui.Enabled then
		if OreListDirty then
			rebuildOreRows()
		else
			refreshOreSelectionLabels()
		end
	end
end

task.spawn(function()
	while task.wait(0.35) do
		if AutoFarmGui and AutoFarmGui.Enabled then
			if OreListDirty then
				rebuildOreRows()
			else
				refreshOreSelectionLabels()
			end
		end
	end
end)

--//====================================================
--// EXPORT
--//====================================================

local function buildExportGroups()
	local groups = {}

	for key, information in pairs(KnownKeys) do
		if not ConflictingKeys[key]
			and (
				not SETTINGS.EXPORT_ONLY_RARE_OR_HIGHER
				or isRareEnough(information.Rarity)
			)
		then
			local name = information.Name

			if not name or name == "" then
				name = makeGenericName(
					information.Rarity,
					key
				)
			end

			local groupId =
				string.lower(name)
				.. "|"
				.. information.Rarity

			local group = groups[groupId]

			if not group then
				group = {
					Id = groupId,
					Name = name,
					Rarity = information.Rarity,
					Generic = information.Generic,
					Keys = {},
				}

				groups[groupId] = group
			end

			table.insert(group.Keys, key)
		end
	end

	local ordered = {}

	for _, group in pairs(groups) do
		table.sort(group.Keys, function(a, b)
			local priorityA = getKeyPriority(a)
			local priorityB = getKeyPriority(b)

			if priorityA == priorityB then
				return a < b
			end

			return priorityA > priorityB
		end)

		table.insert(ordered, group)
	end

	table.sort(ordered, function(a, b)
		local rankA = getRarityRank(a.Rarity)
		local rankB = getRarityRank(b.Rarity)

		if rankA ~= rankB then
			return rankA > rankB
		end

		return a.Name < b.Name
	end)

	return ordered
end

local function findExportGroup(groupId)
	for _, group in ipairs(buildExportGroups()) do
		if group.Id == groupId then
			return group
		end
	end

	return nil
end

local function buildGroupSnippet(group)
	if not group then
		return ""
	end

	local lines = {
		string.format(
			"-- %s [%s]%s",
			group.Name,
			group.Rarity,
			group.Generic
				and " – automatisch erkannt"
				or ""
		),
	}

	for _, key in ipairs(group.Keys) do
		table.insert(
			lines,
			string.format(
				"[%q] = {Name = %q, Rarity = %q},",
				key,
				group.Name,
				group.Rarity
			)
		)
	end

	return table.concat(lines, "\n")
end

local function buildAllExportText()
	local lines = {
		"-- Automatisch erzeugte Rare-ESP-Presets",
		"-- Erstellt: "
			.. os.date("%Y-%m-%d %H:%M:%S"),
		"",
		"PRESETS = {",
	}

	for _, group in ipairs(buildExportGroups()) do
		local snippet = buildGroupSnippet(group)

		for line in string.gmatch(
			snippet,
			"[^\n]+"
		) do
			table.insert(lines, "\t" .. line)
		end

		table.insert(lines, "")
	end

	table.insert(lines, "}")

	return table.concat(lines, "\n")
end

local function writeExportFile()
	if not FileWriter then
		return false,
			"Kein direkter Dateizugriff verfügbar."
	end

	local filename =
		SETTINGS.EXPORT_FILE_PREFIX
		.. "_"
		.. os.date("%Y%m%d_%H%M%S")
		.. ".txt"

	local success, result = pcall(
		FileWriter,
		filename,
		buildAllExportText()
	)

	if not success then
		return false, tostring(result)
	end

	return true, filename
end

local ExportGui = nil
local ExportState = {
	Groups = {},
	Index = 1,
	Output = nil,
	Status = nil,
}

local function selectExportText()
	if not ExportState.Output then
		return
	end

	ExportState.Output:CaptureFocus()

	task.defer(function()
		if ExportState.Output then
			ExportState.Output.SelectionStart = 1
			ExportState.Output.CursorPosition =
				#ExportState.Output.Text + 1
		end
	end)
end

local function renderCurrentExportGroup()
	ExportState.Groups = buildExportGroups()

	if #ExportState.Groups == 0 then
		ExportState.Index = 1
		ExportState.Output.Text = ""
		ExportState.Status.Text =
			"Noch keine Blockart gespeichert."
		return
	end

	ExportState.Index = math.clamp(
		ExportState.Index,
		1,
		#ExportState.Groups
	)

	local group =
		ExportState.Groups[ExportState.Index]

	ExportState.Output.Text =
		buildGroupSnippet(group)

	ExportState.Status.Text = string.format(
		"%d/%d – %s [%s]",
		ExportState.Index,
		#ExportState.Groups,
		group.Name,
		group.Rarity
	)
end

local function ensureExportGui()
	if ExportGui then
		return
	end

	ExportGui = Instance.new("ScreenGui")
	ExportGui.Name = "_OptimizedRareESPExport"
	ExportGui.ResetOnSpawn = false
	ExportGui.Enabled = false
	ExportGui.Parent = PlayerGui

	local frame = Instance.new("Frame")
	frame.AnchorPoint = Vector2.new(0.5, 0.5)
	frame.Position = UDim2.fromScale(0.5, 0.5)
	frame.Size = UDim2.fromOffset(680, 410)
	frame.BackgroundColor3 =
		Color3.fromRGB(25, 25, 30)
	frame.BorderSizePixel = 0
	frame.Parent = ExportGui

	local frameCorner = Instance.new("UICorner")
	frameCorner.CornerRadius = UDim.new(0, 10)
	frameCorner.Parent = frame

	local title = Instance.new("TextLabel")
	title.BackgroundTransparency = 1
	title.Position = UDim2.fromOffset(14, 8)
	title.Size = UDim2.new(1, -60, 0, 30)
	title.Font = Enum.Font.GothamBold
	title.Text = "Rare ESP – Export"
	title.TextColor3 = Color3.new(1, 1, 1)
	title.TextSize = 19
	title.TextXAlignment =
		Enum.TextXAlignment.Left
	title.Parent = frame

	local closeButton = Instance.new("TextButton")
	closeButton.Position =
		UDim2.new(1, -42, 0, 8)
	closeButton.Size = UDim2.fromOffset(30, 30)
	closeButton.BackgroundColor3 =
		Color3.fromRGB(55, 55, 65)
	closeButton.BorderSizePixel = 0
	closeButton.Font = Enum.Font.GothamBold
	closeButton.Text = "X"
	closeButton.TextColor3 =
		Color3.new(1, 1, 1)
	closeButton.TextSize = 15
	closeButton.Parent = frame

	local closeCorner = Instance.new("UICorner")
	closeCorner.CornerRadius = UDim.new(0, 7)
	closeCorner.Parent = closeButton

	local buttonBar = Instance.new("Frame")
	buttonBar.BackgroundTransparency = 1
	buttonBar.Position = UDim2.fromOffset(14, 46)
	buttonBar.Size = UDim2.new(1, -28, 0, 34)
	buttonBar.Parent = frame

	local buttonLayout = Instance.new("UIListLayout")
	buttonLayout.FillDirection =
		Enum.FillDirection.Horizontal
	buttonLayout.Padding = UDim.new(0, 6)
	buttonLayout.Parent = buttonBar

	local function createButton(text, width)
		local button = Instance.new("TextButton")
		button.Size = UDim2.fromOffset(width, 32)
		button.BackgroundColor3 =
			Color3.fromRGB(50, 85, 160)
		button.BorderSizePixel = 0
		button.Font = Enum.Font.GothamBold
		button.Text = text
		button.TextColor3 =
			Color3.new(1, 1, 1)
		button.TextSize = 13
		button.Parent = buttonBar

		local corner = Instance.new("UICorner")
		corner.CornerRadius = UDim.new(0, 7)
		corner.Parent = button

		return button
	end

	local previousButton =
		createButton("Zurück", 75)

	local nextButton =
		createButton("Weiter", 75)

	local copyCurrentButton =
		createButton("Aktuellen kopieren", 145)

	local copyAllButton =
		createButton("Alles kopieren", 120)

	local saveButton =
		createButton("TXT speichern", 115)

	local output = Instance.new("TextBox")
	output.Position = UDim2.fromOffset(14, 90)
	output.Size = UDim2.new(1, -28, 1, -132)
	output.BackgroundColor3 =
		Color3.fromRGB(17, 17, 21)
	output.BorderSizePixel = 0
	output.ClearTextOnFocus = false
	output.MultiLine = true
	output.Font = Enum.Font.Code
	output.Text = ""
	output.TextColor3 =
		Color3.fromRGB(235, 235, 235)
	output.TextSize = 14
	output.TextWrapped = false
	output.TextXAlignment =
		Enum.TextXAlignment.Left
	output.TextYAlignment =
		Enum.TextYAlignment.Top
	output.Parent = frame

	local outputCorner = Instance.new("UICorner")
	outputCorner.CornerRadius = UDim.new(0, 8)
	outputCorner.Parent = output

	local outputPadding = Instance.new("UIPadding")
	outputPadding.PaddingTop = UDim.new(0, 8)
	outputPadding.PaddingBottom = UDim.new(0, 8)
	outputPadding.PaddingLeft = UDim.new(0, 8)
	outputPadding.PaddingRight = UDim.new(0, 8)
	outputPadding.Parent = output

	local status = Instance.new("TextLabel")
	status.BackgroundTransparency = 1
	status.Position = UDim2.new(0, 14, 1, -34)
	status.Size = UDim2.new(1, -28, 0, 24)
	status.Font = Enum.Font.Gotham
	status.Text = ""
	status.TextColor3 =
		Color3.fromRGB(190, 190, 195)
	status.TextSize = 13
	status.TextXAlignment =
		Enum.TextXAlignment.Left
	status.Parent = frame

	ExportState.Output = output
	ExportState.Status = status

	closeButton.MouseButton1Click:Connect(function()
		ExportGui.Enabled = false
	end)

	previousButton.MouseButton1Click:Connect(function()
		ExportState.Index -= 1

		if ExportState.Index < 1 then
			ExportState.Index =
				math.max(1, #ExportState.Groups)
		end

		renderCurrentExportGroup()
	end)

	nextButton.MouseButton1Click:Connect(function()
		ExportState.Index += 1

		if ExportState.Index
			> math.max(1, #ExportState.Groups)
		then
			ExportState.Index = 1
		end

		renderCurrentExportGroup()
	end)

	copyCurrentButton.MouseButton1Click:Connect(function()
		local group =
			ExportState.Groups[
				ExportState.Index
			]

		if not group then
			status.Text =
				"Noch keine Blockart vorhanden."
			return
		end

		local text = buildGroupSnippet(group)
		local success = copyToClipboard(text)

		if success then
			status.Text =
				group.Name .. " wurde kopiert."
		else
			output.Text = text
			status.Text =
				"Text markiert – Strg + C drücken."
			selectExportText()
		end
	end)

	copyAllButton.MouseButton1Click:Connect(function()
		local text = buildAllExportText()
		local success = copyToClipboard(text)

		if success then
			status.Text =
				"Alle Presets wurden kopiert."
		else
			output.Text = text
			status.Text =
				"Text markiert – Strg + C drücken."
			selectExportText()
		end
	end)

	saveButton.MouseButton1Click:Connect(function()
		local success, result =
			writeExportFile()

		if success then
			status.Text =
				"TXT gespeichert: " .. result
		else
			output.Text = buildAllExportText()
			status.Text =
				"Dateiexport nicht verfügbar – "
				.. "Strg + C verwenden."
			selectExportText()
		end
	end)
end

local function copyLastExportGroup()
	if not LastExportGroupId then
		print(
			"[EXPORT] Noch keine benannte Blockart gelernt."
		)
		return
	end

	local group =
		findExportGroup(LastExportGroupId)

	if not group then
		print(
			"[EXPORT] Letzter Block ist nicht mehr verfügbar."
		)
		return
	end

	local text = buildGroupSnippet(group)
	local success = copyToClipboard(text)

	if success then
		print(
			"[EXPORT] Kopiert:",
			group.Name,
			"[" .. group.Rarity .. "]"
		)
		return
	end

	ensureExportGui()
	ExportGui.Enabled = true
	ExportState.Output.Text = text
	ExportState.Status.Text =
		"Text markiert – Strg + C drücken."
	selectExportText()
end

local function exportAllPresets()
	local success, result = writeExportFile()

	if success then
		print(
			"[EXPORT] TXT gespeichert:",
			result
		)
		return
	end

	local text = buildAllExportText()
	local copied = copyToClipboard(text)

	if copied then
		print(
			"[EXPORT] Alle Presets wurden kopiert."
		)
		return
	end

	ensureExportGui()
	ExportGui.Enabled = true
	ExportState.Output.Text = text
	ExportState.Status.Text =
		"Text markiert – Strg + C drücken."
	selectExportText()
end

--//====================================================
--// ROHER MESH-KATALOG OHNE ANSCHAUEN
--//====================================================
--//
--// Alle bereits geladenen Block-Modelle im SCAN_RADIUS
--// werden aus dem räumlichen Index gelesen. Dafür muss
-- kein Block anvisiert werden.
--//
--// Wichtig:
--// MeshId/Texturen lassen sich automatisch auslesen.
--// Name und Rarität sind nur sicher bekannt, wenn:
--//   1. der Schlüssel bereits in PRESETS steht,
--//   2. Partikel eine Rarität liefern oder
--//   3. der Block irgendwann über das GUI gelernt wurde.
--//
--// Unbekannte Einträge bleiben deshalb als "Unknown".
--//====================================================

local MeshCatalogExportRunning = false

local function getPreferredCatalogKey(keys)
	local preferredKey = nil
	local preferredPriority = -1

	for _, key in ipairs(keys) do
		local priority = getKeyPriority(key)

		if priority > preferredPriority
			or (
				priority == preferredPriority
				and (
					not preferredKey
					or key < preferredKey
				)
			)
		then
			preferredPriority = priority
			preferredKey = key
		end
	end

	return preferredKey
end

local function getCatalogInformation(data, identityKey)
	if data.NamedInfo then
		return {
			Name = data.NamedInfo.Name,
			Rarity = data.NamedInfo.Rarity,
			Known = true,
			Source = "GUI",
		}
	end

	local meshInformation = getMeshInformation(data)

	if meshInformation then
		return {
			Name = meshInformation.Name,
			Rarity = meshInformation.Rarity,
			Known = true,
			Source = "Preset/Mesh",
		}
	end

	if data.ParticleRarity then
		return {
			Name = makeGenericName(
				data.ParticleRarity,
				identityKey
			),
			Rarity = data.ParticleRarity,
			Known = false,
			Source = "Particle",
		}
	end

	return {
		Name = "UnknownMesh_"
			.. (
				identityKey:match("(%d+)[^%d]*$")
				or tostring(#identityKey * 7919)
			),
		Rarity = "Unknown",
		Known = false,
		Source = "MeshOnly",
	}
end

local function buildNearbyMeshCatalogText()
	local characterRoot = getCharacterRoot()

	if not characterRoot then
		return nil, "HumanoidRootPart nicht gefunden."
	end

	local origin = characterRoot.Position
	local radiusSquared =
		SETTINGS.SCAN_RADIUS * SETTINGS.SCAN_RADIUS

	local groups = {}
	local checked = 0
	local includedTargets = 0

	for target, data in pairs(TargetData) do
		if target.Parent
			and #data.Keys > 0
		then
			local offset = data.Position - origin
			local distanceSquared = offset:Dot(offset)

			if distanceSquared <= radiusSquared then
				local identityKey =
					getPreferredCatalogKey(data.Keys)

				if identityKey then
					local information =
						getCatalogInformation(
							data,
							identityKey
						)

					if SETTINGS.INCLUDE_KNOWN_MESHES_IN_CATALOG
						or not information.Known
					then
						local group = groups[identityKey]

						if not group then
							group = {
								IdentityKey = identityKey,
								Name = information.Name,
								Rarity = information.Rarity,
								Known = information.Known,
								Source = information.Source,
								Count = 0,
								NearestDistanceSquared =
									distanceSquared,
								Keys = {},
							}

							groups[identityKey] = group
						end

						group.Count += 1
						includedTargets += 1

						if distanceSquared
							< group.NearestDistanceSquared
						then
							group.NearestDistanceSquared =
								distanceSquared
						end

						for _, key in ipairs(data.Keys) do
							group.Keys[key] = true
						end
					end
				end
			end
		end

		checked += 1

		if checked
			% SETTINGS.MESH_CATALOG_BATCH_SIZE
			== 0
		then
			task.wait()
		end
	end

	local ordered = {}

	for _, group in pairs(groups) do
		local keys = {}

		for key in pairs(group.Keys) do
			table.insert(keys, key)
		end

		table.sort(keys, function(a, b)
			local priorityA = getKeyPriority(a)
			local priorityB = getKeyPriority(b)

			if priorityA == priorityB then
				return a < b
			end

			return priorityA > priorityB
		end)

		group.Keys = keys
		table.insert(ordered, group)
	end

	table.sort(ordered, function(a, b)
		if a.Known ~= b.Known then
			return not a.Known
		end

		local rankA = getRarityRank(a.Rarity)
		local rankB = getRarityRank(b.Rarity)

		if rankA ~= rankB then
			return rankA > rankB
		end

		return a.IdentityKey < b.IdentityKey
	end)

	local lines = {
		"-- Automatisch ausgelesener Mesh-Katalog",
		"-- Kein Anvisieren der Blöcke erforderlich",
		"-- Erstellt: "
			.. os.date("%Y-%m-%d %H:%M:%S"),
		"-- Radius: "
			.. tostring(SETTINGS.SCAN_RADIUS),
		"-- Erfasste Exemplare: "
			.. tostring(includedTargets),
		"-- Einzigartige Model-/Mesh-Signaturen: "
			.. tostring(#ordered),
		"--",
		"-- Unknown bedeutet nur: Name/Rarität sind nicht",
		"-- aus MeshId und Textur allein bestimmbar.",
		"",
		"RAW_MESH_CATALOG = {",
	}

	for _, group in ipairs(ordered) do
		table.insert(
			lines,
			string.format(
				"\t-- %s | %s | %dx | %.0f Studs | %s",
				group.Name,
				group.Rarity,
				group.Count,
				math.sqrt(
					group.NearestDistanceSquared
				),
				group.Source
			)
		)

		table.insert(
			lines,
			string.format(
				"\t[%q] = {",
				group.IdentityKey
			)
		)

		table.insert(
			lines,
			string.format(
				"\t\tName = %q,",
				group.Name
			)
		)

		table.insert(
			lines,
			string.format(
				"\t\tRarity = %q,",
				group.Rarity
			)
		)

		table.insert(
			lines,
			string.format(
				"\t\tKnown = %s,",
				tostring(group.Known)
			)
		)

		table.insert(
			lines,
			string.format(
				"\t\tCount = %d,",
				group.Count
			)
		)

		table.insert(lines, "\t\tKeys = {")

		for _, key in ipairs(group.Keys) do
			table.insert(
				lines,
				string.format(
					"\t\t\t%q,",
					key
				)
			)
		end

		table.insert(lines, "\t\t},")
		table.insert(lines, "\t},")
		table.insert(lines, "")
	end

	table.insert(lines, "}")

	return table.concat(lines, "\n"), nil
end

local function copyNearbyMeshCatalog(
	isAutomatic
)
	if MeshCatalogExportRunning then
		if not isAutomatic then
			print(
				"[MESH CATALOG] Export läuft bereits."
			)
		end

		return
	end

	MeshCatalogExportRunning = true

	task.spawn(function()
		local text, errorMessage =
			buildNearbyMeshCatalogText()

		if not text then
			warn(
				"[MESH CATALOG]",
				errorMessage
			)

			MeshCatalogExportRunning = false
			return
		end

		local copied, copyError =
			copyToClipboard(text)

		if copied then
			print(
				"[MESH CATALOG] Alle Meshes im Umkreis "
					.. "wurden als ein Text kopiert."
			)
		else
			ensureExportGui()
			ExportGui.Enabled = true
			ExportState.Output.Text = text
			ExportState.Status.Text =
				"Mesh-Katalog markiert – Strg + C drücken."
			selectExportText()

			print(
				"[MESH CATALOG]",
				copyError
			)
		end

		if FileWriter then
			local filename =
				SETTINGS.MESH_CATALOG_FILE_PREFIX
				.. "_"
				.. os.date("%Y%m%d_%H%M%S")
				.. ".txt"

			local success = pcall(
				FileWriter,
				filename,
				text
			)

			if success then
				print(
					"[MESH CATALOG] TXT gespeichert:",
					filename
				)
			end
		end

		MeshCatalogExportRunning = false
	end)
end

--//====================================================
--// EVENTBASIERTES LERNEN
--//====================================================

local LearnToken = 0
local BreakingPositionConnection = nil

local function attemptLearn()
	local information = parseCurrentBlockText()

	if not information then
		return
	end

	local target = getCurrentTarget()

	if not target then
		return
	end

	local data = TargetData[target]
		or registerTarget(target)

	if not data then
		return
	end

	data.NamedInfo = information

	local changed =
		registerTargetKeys(data, information)

	LastExportGroupId =
		string.lower(information.Name)
		.. "|"
		.. information.Rarity

	if changed then
		print("")
		print(
			"[BLOCK GELERNT]",
			information.Name,
			"[" .. information.Rarity .. "]"
		)

		for _, key in ipairs(data.Keys) do
			print(
				"Schlüssel:",
				key,
				ConflictingKeys[key]
					and "GESPERRT"
					or "AKTIV"
			)
		end

		if SETTINGS.AUTO_COPY_NEW_NAMED_BLOCK then
			local group =
				findExportGroup(
					LastExportGroupId
				)

			if group then
				local success =
					copyToClipboard(
						buildGroupSnippet(group)
					)

				if success then
					print(
						"[EXPORT] Automatisch kopiert."
					)
				end
			end
		end
	end

	ForceRefresh = true
end

local function scheduleLearn()
	LearnToken += 1
	local token = LearnToken

	task.delay(
		SETTINGS.LEARN_STABILITY_TIME,
		function()
			if token == LearnToken then
				attemptLearn()
			end
		end
	)
end

local function connectBreakingPart(breaking)
	if BreakingPositionConnection then
		BreakingPositionConnection:Disconnect()
		BreakingPositionConnection = nil
	end

	if breaking
		and breaking:IsA("BasePart")
	then
		BreakingPositionConnection =
			breaking:GetPropertyChangedSignal(
				"Position"
			):Connect(scheduleLearn)

		scheduleLearn()
	end
end

BlockTextObject:GetPropertyChangedSignal(
	"Text"
):Connect(scheduleLearn)

workspace.ChildAdded:Connect(function(instance)
	if instance.Name == SETTINGS.BREAKING_NAME
		and instance:IsA("BasePart")
	then
		connectBreakingPart(instance)
	end
end)

workspace.ChildRemoved:Connect(function(instance)
	if instance.Name == SETTINGS.BREAKING_NAME then
		connectBreakingPart(nil)
	end
end)

connectBreakingPart(getBreakingPart())

--//====================================================
--// ROOT-ÄNDERUNGEN
--//====================================================

SETTINGS.ROOT.DescendantAdded:Connect(function(instance)
	if instance:IsA("ParticleEmitter") then
		task.defer(registerEmitter, instance)
		return
	end

	task.defer(function()
		local basePart = nil

		if instance:IsA("BasePart") then
			basePart = instance
		else
			local current = instance.Parent

			while current and current ~= SETTINGS.ROOT do
				if current:IsA("BasePart") then
					basePart = current
					break
				end

				current = current.Parent
			end
		end

		if basePart then
			local target = getBlockTarget(basePart)

			if target then
				if not TargetData[target] then
					registerTarget(target)
				else
					queueReindex(target)
				end
			end
		end
	end)
end)

SETTINGS.ROOT.DescendantRemoving:Connect(function(instance)
	if instance:IsA("ParticleEmitter") then
		unregisterEmitter(instance)
		return
	end

	local target = nil

	if instance:IsA("Model")
		or instance:IsA("BasePart")
	then
		target = TargetData[instance]
			and instance
			or nil
	end

	if target then
		removeTarget(target)
		deactivateTarget(target)
		return
	end

	local parent = instance.Parent

	while parent and parent ~= SETTINGS.ROOT do
		if TargetData[parent] then
			queueReindex(parent)
			break
		end

		parent = parent.Parent
	end
end)

--//====================================================
--// INITIALER SCAN
--//====================================================

task.spawn(function()
	local descendants =
		SETTINGS.ROOT:GetDescendants()

	local seenTargets = {}
	local processed = 0

	-- Erst alle Modelle/Blöcke einmalig indexieren.
	for _, descendant in ipairs(descendants) do
		if descendant:IsA("BasePart") then
			local target =
				getBlockTarget(descendant)

			if target and not seenTargets[target] then
				seenTargets[target] = true
				registerTarget(target)
			end
		end

		processed += 1

		if processed
			% SETTINGS.INITIAL_SCAN_BATCH_SIZE
			== 0
		then
			task.wait()
		end
	end

	local targetCount = 0

	for _ in pairs(TargetData) do
		targetCount += 1
	end

	processed = 0
	local emitterCount = 0

	-- Danach Partikel zuordnen.
	for _, descendant in ipairs(descendants) do
		if descendant:IsA("ParticleEmitter") then
			registerEmitter(descendant)
			emitterCount += 1
		end

		processed += 1

		if processed
			% SETTINGS.INITIAL_SCAN_BATCH_SIZE
			== 0
		then
			task.wait()
		end
	end

	print("")
	print(
		"[OPTIMIZED ESP] Scan abgeschlossen:",
		targetCount,
		"Blöcke/Modelle und",
		emitterCount,
		"ParticleEmitter."
	)

	ForceRefresh = true
	refreshVisibleESP(true)

	if SETTINGS.AUTO_COPY_MESH_CATALOG_AFTER_INITIAL_SCAN then
		task.delay(0.5, function()
			copyNearbyMeshCatalog(true)
		end)
	end
end)

--//====================================================
--// REFRESH-LOOP
--//====================================================

task.spawn(function()
	while task.wait(
		SETTINGS.REFRESH_INTERVAL
	) do
		refreshVisibleESP(false)
	end
end)

--//====================================================
--// DEBUG
--//====================================================

local function printDebug()
	print("")
	print(
		"============================================================"
	)
	print("[OPTIMIZED ESP DEBUG]")
	print(
		"============================================================"
	)

	print("GUI-Text:", BlockTextObject.Text)

	local breaking = getBreakingPart()

	if breaking then
		print(
			"Breaking.Position:",
			breaking.Position
		)
	else
		print("Breaking: nicht gefunden")
	end

	local target, hitPart = getCurrentTarget()

	if not target then
		print("Zielblock: nicht gefunden")
		return
	end

	local data = TargetData[target]
		or registerTarget(target)

	print(
		"Getroffener Part:",
		hitPart and hitPart:GetFullName()
			or "nil"
	)

	print("Block-Ziel:", target:GetFullName())
	print("Position:", data.Position)
	print(
		"Particle-Rarity:",
		data.ParticleRarity or "keine"
	)

	local resolved =
		resolveTargetInformation(data)

	if resolved then
		print(
			"ESP-Ergebnis:",
			resolved.Name,
			resolved.Rarity,
			resolved.Generic
				and "(generisch)"
				or ""
		)
	else
		print("ESP-Ergebnis: keines")
	end

	print("Schlüssel:")

	for _, key in ipairs(data.Keys) do
		local known = KnownKeys[key]

		print(
			" ",
			key,
			ConflictingKeys[key]
				and "GESPERRT"
				or (
					known
						and (
							"BEKANNT – "
							.. known.Name
							.. " ["
							.. known.Rarity
							.. "]"
						)
						or "UNBEKANNT"
				)
		)
	end

	print(
		"============================================================"
	)
end

--//====================================================
--// TASTEN
--//====================================================

UserInputService.InputBegan:Connect(function(
	input,
	processed
)
	if processed then
		return
	end

	if input.KeyCode == SETTINGS.TOGGLE_KEY then
		SETTINGS.ENABLED =
			not SETTINGS.ENABLED

		print(
			"[OPTIMIZED ESP]",
			SETTINGS.ENABLED
				and "AKTIVIERT"
				or "DEAKTIVIERT"
		)

		ForceRefresh = true
		refreshVisibleESP(true)

	elseif input.KeyCode
		== SETTINGS.EXPORT_PANEL_KEY
	then
		ensureExportGui()

		ExportGui.Enabled =
			not ExportGui.Enabled

		if ExportGui.Enabled then
			ExportState.Index = 1
			renderCurrentExportGroup()
		end

	elseif input.KeyCode == SETTINGS.DEBUG_KEY then
		task.spawn(printDebug)

	elseif input.KeyCode
		== SETTINGS.COPY_LAST_KEY
	then
		copyLastExportGroup()

	elseif input.KeyCode
		== SETTINGS.EXPORT_ALL_KEY
	then
		exportAllPresets()

	elseif input.KeyCode
		== SETTINGS.MESH_CATALOG_KEY
	then
		copyNearbyMeshCatalog(false)

	elseif input.KeyCode
		== SETTINGS.AUTO_FARM_TOGGLE_KEY
	then
		setAutoFarmEnabled(
			not SETTINGS.AUTO_FARM_ENABLED
		)

		print(
			"[ORE AUTO-FARM]",
			SETTINGS.AUTO_FARM_ENABLED
				and "AKTIVIERT"
				or "DEAKTIVIERT"
		)

		refreshOreSelectionLabels()

	elseif input.KeyCode
		== SETTINGS.AUTO_FARM_GUI_KEY
	then
		toggleAutoFarmGui()
	end
end)

-- GUI direkt beim Start öffnen.
ensureAutoFarmGui()
AutoFarmGui.Enabled = true
refreshOreSelectionLabels()

--//====================================================
--// STARTAUSGABE
--//====================================================

print("")
print("[OPTIMIZED ESP] Script geladen.")
print("[OPTIMIZED ESP] Eventbasiertes Lernen aktiv.")
print("[OPTIMIZED ESP] Unique-/TNT-Erkennung aktiv.")
print("[OPTIMIZED ESP] Räumlicher Index aktiv.")
print("[OPTIMIZED ESP] Roh-Mesh-Katalog ohne Anvisieren aktiv.")
print("[OPTIMIZED ESP] Highlight-/Billboard-Pooling aktiv.")
print("[OPTIMIZED ESP] F4 = ESP ein-/ausschalten.")
print("[OPTIMIZED ESP] F5 = Exportfenster.")
print("[OPTIMIZED ESP] F6 = aktuellen Block debuggen.")
print("[OPTIMIZED ESP] F7 = letzten Block kopieren.")
print("[OPTIMIZED ESP] F8 = alle Presets exportieren.")
print("[OPTIMIZED ESP] F9 = alle Meshes im Umkreis kopieren.")
print("[OPTIMIZED ESP] F10 = Ore Auto-Farm ein-/ausschalten.")
print("[OPTIMIZED ESP] Rechte Umschalttaste = Ore-Auswahl-GUI.")
print("[OPTIMIZED ESP] Kein Server-Hopping enthalten.")
print("[OPTIMIZED ESP] Kamera-Ziel ist im GUI abschaltbar.")
print("[OPTIMIZED ESP] Keine Studs-Anzeige.")

Features & Information

Ore ESP TnT ESP And More.

Community Comments (1)

aurafarmer17aurafarmer17Oct 12, 2026

insta ban

More Scripts for Miners World [Gauntlets]