local lib = {} local player = require("player") local json = require("json") local DATA_PATH = "config/neoscripts/scripts/data/hypixel-player.json" local saveTimer = 0 -- Кеш для хранения последних успешно распарсенных значений local cache = { health = 0, maxHealth = 0, mana = 0, maxMana = 0, defense = 0, purse = 0, bits = 0, copper = 0, sowdust = 0, location = "", -- Статы из Tab speed = 0, strength = 0, critChance = 0, critDamage = 0, attackSpeed = 0, lastBar = nil, lastScore = nil, } local function saveData() local file, err = io.open(DATA_PATH, "w") if file then local data = { health = cache.health, maxHealth = cache.maxHealth, mana = cache.mana, maxMana = cache.maxMana, defense = cache.defense, purse = cache.purse, bits = cache.bits, copper = cache.copper, sowdust = cache.sowdust, location = cache.location, speed = cache.speed, strength = cache.strength, critChance = cache.critChance, critDamage = cache.critDamage, attackSpeed = cache.attackSpeed, } file:write(json.encode(data, true)) file:close() end end local function loadData() local file, err = io.open(DATA_PATH, "r") if file then local content = file:read("*all") file:close() if content and #content > 0 then local ok, data = pcall(json.decode, content) if ok and type(data) == "table" then for k, v in pairs(data) do if cache[k] ~= nil then cache[k] = v end end end end end end loadData() -- Вспомогательная функция: удаляет запятые из строки числа и преобразует в число local function parseNumber(str) if not str then return nil end local cleaned = str:gsub(",", "") return tonumber(cleaned) end -- Удаляет все цветовые коды Minecraft (вида §a, §l и т.д.) local function stripColorCodes(str) if not str then return "" end return str:gsub("§[0-9a-fk-or]", "") end -- Основная функция парсинга action bar local function parseActionBar() if not player.entity then return end local bar = player.getActionBar() if not bar or bar == "" then return end if cache.lastBar == bar then return end cache.lastBar = bar -- 1. Поиск всех пар "число/число" (текущее/максимальное) local matches = {} for num1, num2 in bar:gmatch "(%d+,?%d*)/(%d+,?%d*)" do table.insert(matches, { num1, num2 }) end -- 2. Здоровье (первая пара) if #matches >= 1 then local h = parseNumber(matches[1][1]) local mh = parseNumber(matches[1][2]) if h and mh then cache.health = h cache.maxHealth = mh end end -- 3. Мана (последняя пара, если есть вторая или более) if #matches >= 2 then local manaPair = matches[#matches] local m = parseNumber(manaPair[1]) local mm = parseNumber(manaPair[2]) if m and mm then cache.mana = m cache.maxMana = mm end end -- 4. Защита (ищем по цвету §a или по слову Defense) local def = bar:match("§a(%d+)") or bar:match("(%d+)%s*Defense") if def then local d = parseNumber(def) if d then cache.defense = d end end end -- Парсинг локации и валют из скорборда local function parseScoreboard() if not player.entity then return end local scoreboard = player.getScoreBoardLines() if not scoreboard or type(scoreboard) ~= "table" then return end -- Ищем нужные строки local locationLine = nil local purseLine = nil local bitsLine = nil local copperLine = nil local sowdustLine = nil for _, line in ipairs(scoreboard) do if line and line:find("") then locationLine = line elseif line and line:find("Purse:") then purseLine = line elseif line and line:find("Bits:") then bitsLine = line elseif line and line:find("Copper:") then copperLine = line elseif line and line:find("Sowdust:") then sowdustLine = line end end -- Обновляем локацию (если строка изменилась) if locationLine then if cache.lastScore ~= locationLine then cache.lastScore = locationLine -- Извлекаем название локации после "§7 " или просто "" local location = locationLine:match("§7%s*(.*)") or locationLine:match("%s*(.*)") if location then location = stripColorCodes(location) -- Удаляем суффиксы типа " x7" и " (E)" local suffix1 = location:match(" x%d+$") if suffix1 then location = location:sub(1, #location - #suffix1) end local suffix2 = location:match(" %(E%)$") if suffix2 then location = location:sub(1, #location - #suffix2) end location = location:match("^%s*(.-)%s*$") or location cache.location = location end end end -- Парсим валюты (обновляем всегда, даже если локация не изменилась) if purseLine then local purseStr = purseLine:match("Purse:%s*(.+)$") if purseStr then local num = parseNumber(stripColorCodes(purseStr)) if num then cache.purse = num end end end if bitsLine then local bitsStr = bitsLine:match("Bits:%s*(.+)$") if bitsStr then local num = parseNumber(stripColorCodes(bitsStr)) if num then cache.bits = num end end end if copperLine then local copperStr = copperLine:match("Copper:%s*(.+)$") if copperStr then local num = parseNumber(stripColorCodes(copperStr)) if num then cache.copper = num end end end if sowdustLine then local sowdustStr = sowdustLine:match("Sowdust:%s*(.+)$") if sowdustStr then local num = parseNumber(stripColorCodes(sowdustStr)) if num then cache.sowdust = num end end end end -- Парсинг статов из Tab local function parseTab() if not player.entity then return end local tab = player.getTab() if not tab or type(tab) ~= "table" then return end if tab.body then for _, line in ipairs(tab.body) do if line then -- Ищем каждый известный стат по ключевому слову local speed = line:match("Speed:.*?(%d+)") if speed then cache.speed = parseNumber(speed) or 0 end local strength = line:match("Strength:.*?(%d+)") if strength then cache.strength = parseNumber(strength) or 0 end local critChance = line:match("Crit Chance:.*?(%d+)") if critChance then cache.critChance = parseNumber(critChance) or 0 end local critDamage = line:match("Crit Damage:.*?(%d+)") if critDamage then cache.critDamage = parseNumber(critDamage) or 0 end local attackSpeed = line:match("Attack Speed:.*?(%d+)") if attackSpeed then cache.attackSpeed = parseNumber(attackSpeed) or 0 end end end end end -- Автоматическое обновление кеша каждый тик registerClientTick(function() parseActionBar() parseScoreboard() parseTab() saveTimer = saveTimer + 1 if saveTimer >= 1 then saveTimer = 0 saveData() end end) -- Геттеры для action bar function lib.getHealth() parseActionBar() return cache.health end function lib.getMaxHealth() parseActionBar() return cache.maxHealth end function lib.getMana() parseActionBar() return cache.mana end function lib.getMaxMana() parseActionBar() return cache.maxMana end function lib.getDefense() parseActionBar() return cache.defense end -- Геттеры для валют и локации function lib.getPurse() parseScoreboard() return cache.purse end function lib.getBits() parseScoreboard() return cache.bits end function lib.getCopper() parseScoreboard() return cache.copper end function lib.getSowdust() parseScoreboard() return cache.sowdust end function lib.getLocation() parseScoreboard() return cache.location end -- Геттеры для статов из Tab function lib.getSpeed() parseTab() return cache.speed end function lib.getStrength() parseTab() return cache.strength end function lib.getCritChance() parseTab() return cache.critChance end function lib.getCritDamage() parseTab() return cache.critDamage end function lib.getAttackSpeed() parseTab() return cache.attackSpeed end -- Общая функция, возвращающая все значения function lib.getStats() parseActionBar() parseScoreboard() parseTab() return { health = cache.health, maxHealth = cache.maxHealth, mana = cache.mana, maxMana = cache.maxMana, defense = cache.defense, purse = cache.purse, bits = cache.bits, copper = cache.copper, sowdust = cache.sowdust, location = cache.location, speed = cache.speed, strength = cache.strength, critChance = cache.critChance, critDamage = cache.critDamage, attackSpeed = cache.attackSpeed, } end return lib