local player = require("player") local world = require("world") local creator = require("creator") local keys = require("keys") local rotations = require("rotations_v3") rotations.setRotationSpeed(14) rotations.setModifier(3) local enabled = false local step = 1.37 local minShiftDelay = 10 local maxShiftDelay = 15 -- НАСТРОЙКИ РЕСПАВНА И ОЖИДАНИЯ local defaultRespawnDelayTicks = 300 -- Время респавна мифрила на Hypixel (15 секунд / 300 тиков) local maxWaitDistance = 2.5 -- Максимальная дистанция (в блоках) от игрока до руды для ожидания респавна -- НАСТРОЙКИ ПИКОБОЛУСА (PICKOBULUS) local pickobulusEnabled = true local pickobulusIntervalTicks = 1200 -- Интервал использования в тиках (1200 тиков = 60 секунд) local minPickobulusDistance = 9.0 -- Минимальная дистанция броска (строго 8 блоков) local maxPickobulusDistance = 35.0 -- Максимальная дистанция броска (строго 15 блоков) -- Базовый список всех валидных блоков local targetIdentifiers = { ["minecraft:coal_block"] = true, ["minecraft:iron_block"] = true, ["minecraft:redstone_block"] = true, ["minecraft:gold_block"] = true, ["minecraft:diamond_block"] = true, ["minecraft:lapis_block"] = true, ["minecraft:emerald_block"] = true, ["minecraft:cyan_terracotta"] = true, ["minecraft:light_blue_wool"] = true, ["minecraft:polished_diorite"] = true, ["minecraft:dark_prismarine"] = true, ["minecraft:gray_wool"] = true, ["minecraft:prismarine"] = true, ["minecraft:prismarine_bricks"] = true, } -- Группировка: связываем разные блоки в один виртуальный тип руды local blockToGroup = { ["minecraft:cyan_terracotta"] = "mithril", ["minecraft:light_blue_wool"] = "mithril", ["minecraft:polished_diorite"] = "mithril", ["minecraft:dark_prismarine"] = "mithril", ["minecraft:gray_wool"] = "mithril", ["minecraft:prismarine"] = "mithril", ["minecraft:prismarine_bricks"] = "mithril", } -- Вспомогательная функция для удаления цветовых кодов майнкрафта (§a, §7 и т.д.) local function stripColorCodes(str) if not str then return "" end return str:gsub("§.", "") end -- Функция для получения "семейства" блока (группа или собственный ID) local function getBlockGroup(identifier) if not identifier then return nil end return blockToGroup[identifier] or identifier end -- Переменные состояния local currentTarget = nil local currentTargetTicks = 0 local lastActiveRot = nil local ignoredBlocks = {} local globalTicks = 0 local nextTargetAllowedTick = 0 local scannedBlocks = {} local nextTargetVisual = nil local switchTargetDelay = 0 -- Состояние Пикоболуса local lastPickobulusTick = 0 local pickobulusState = nil -- Может быть "aiming" или nil local pickobulusTarget = nil local pickobulusAimTicks = 0 -- КЭШ ПИКОБОЛУСА local lastPickobulusPlayerPos = { x = 0, y = 0, z = 0 } local cachedPickobulusTargets = nil -- ГЛОБАЛЬНЫЙ ТРЭКИНГ ВРЕМЕНИ И TPS local serverGameTime = 0 local lastServerTimeUpdate = 0 local currentTPS = 20.0 -- ТАБЛИЦЫ ДЛЯ АВТО-РЕСПАВНА И СТАТИСТИКИ local brokenBlocks = {} local respawnDelays = {} local activeBreaks = {} -- Переменные для глобального кеширования геометрии local lastCachedPlayerPos = { x = 0, y = 0, z = 0 } local cachedWorldMap = nil local cachedBlocksList = nil -- Текущая доминантная группа и последняя валидная группа local dominantGroup = nil local lastValidDominantGroup = nil registerServerSetTimeEvent(function(dayTime, gameTime, tickDayTime) local currentTime = os.clock() if serverGameTime > 0 and lastServerTimeUpdate > 0 then local tickDiff = gameTime - serverGameTime local timeDiff = currentTime - lastServerTimeUpdate if timeDiff > 0 and tickDiff > 0 then currentTPS = tickDiff / timeDiff if currentTPS > 20.0 then currentTPS = 20.0 end end end serverGameTime = gameTime lastServerTimeUpdate = currentTime end) registerServerSideRotationEvent(function(yaw, pitch) if yaw < -180 or yaw > 180 then return end if pitch < -90 or pitch > 90 then return end local rot = player.getRotation() if rot.yaw == yaw and rot.pitch == pitch then return end enabled = false player.addMessage("You got rotated") local pos = player.getPos() world.playSound(pos.x, pos.y, pos.z, "minecraft:block.anvil.place", 100, 1) lastActiveRot = nil end) -- Основная функция сканирования и вычисления доминантной группы (для обычного копания) local function updateWorldAndDominance(currentYaw, currentPitch) local playerPos = player.getPos() local px, py, pz = math.floor(playerPos.x), math.floor(playerPos.y), math.floor(playerPos.z) if not cachedWorldMap or lastCachedPlayerPos.x ~= px or lastCachedPlayerPos.y ~= py or lastCachedPlayerPos.z ~= pz then lastCachedPlayerPos.x = px lastCachedPlayerPos.y = py lastCachedPlayerPos.z = pz cachedWorldMap = {} local headPos = player.getEyePosition() for yaw = -180, 180, step do for pitch = 90, -90, -step do local result = world.raycastFromRotation({ startX = headPos.x, startY = headPos.y, startZ = headPos.z, yaw = yaw, pitch = pitch, range = 4.5, include_fluid = false, include_entity = true, }) if result and result.blockPos and result.location then if result.type == "entity" then goto continue end local blockPos = result.blockPos local block = world.getBlock(blockPos.x, blockPos.y, blockPos.z) if block and block.identifier then local bx, by, bz = math.floor(blockPos.x), math.floor(blockPos.y), math.floor(blockPos.z) local key = bx .. "," .. by .. "," .. bz local dyaw = yaw - currentYaw if dyaw > 180 then dyaw = dyaw - 360 end if dyaw < -180 then dyaw = dyaw + 360 end local dpitch = pitch - currentPitch local angleDiff = dyaw * dyaw + dpitch * dpitch local loc = result.location local fx, fy, fz = loc.x - bx, loc.y - by, loc.z - bz local dist_x = math.min(fx, 1 - fx) local dist_y = math.min(fy, 1 - fy) local dist_z = math.min(fz, 1 - fz) local is_safe = true local margin = 0.15 if dist_x <= dist_y and dist_x <= dist_z then if dist_y < margin or dist_z < margin then is_safe = false end elseif dist_y <= dist_x and dist_y <= dist_z then if dist_x < margin or dist_z < margin then is_safe = false end else if dist_x < margin or dist_y < margin then is_safe = false end end if not cachedWorldMap[key] then cachedWorldMap[key] = { pos = { x = bx, y = by, z = bz }, identifier = block.identifier, angles = {}, safeAngles = {}, angleDiff = angleDiff, location = loc } end local angleData = { yaw = yaw, pitch = pitch, location = loc } table.insert(cachedWorldMap[key].angles, angleData) if is_safe then table.insert(cachedWorldMap[key].safeAngles, angleData) end if angleDiff < cachedWorldMap[key].angleDiff then cachedWorldMap[key].angleDiff = angleDiff end end end ::continue:: end end end local totalCounts = {} local aliveTargetsMap = {} for key, b in pairs(cachedWorldMap) do if not ignoredBlocks[key] or globalTicks >= ignoredBlocks[key] then local currentBlock = world.getBlock(b.pos.x, b.pos.y, b.pos.z) local currentId = nil if currentBlock and targetIdentifiers[currentBlock.identifier] then currentId = currentBlock.identifier b.identifier = currentId aliveTargetsMap[key] = b if brokenBlocks[key] then brokenBlocks[key] = nil end elseif brokenBlocks[key] then currentId = brokenBlocks[key].identifier end if currentId and targetIdentifiers[currentId] then local group = getBlockGroup(currentId) totalCounts[group] = (totalCounts[group] or 0) + 1 end end end local maxCount = 0 local bestGroup = nil for group, count in pairs(totalCounts) do if count > maxCount then maxCount = count bestGroup = group end end if bestGroup ~= dominantGroup and bestGroup ~= nil then dominantGroup = bestGroup lastValidDominantGroup = bestGroup local cleanName = dominantGroup:gsub("minecraft:", "") player.addMessage("🎯 Приоритет группы: " .. cleanName .. " (" .. maxCount .. " шт.)") elseif bestGroup == nil then dominantGroup = nil end local targetGroup = dominantGroup or lastValidDominantGroup local filteredLiveList = {} if targetGroup then for key, b in pairs(aliveTargetsMap) do if getBlockGroup(b.identifier) == targetGroup then local minDiff = 999999 for _, ang in ipairs(b.angles) do local dyaw = ang.yaw - currentYaw if dyaw > 180 then dyaw = dyaw - 360 end if dyaw < -180 then dyaw = dyaw + 360 end local dpitch = ang.pitch - currentPitch local diff = dyaw * dyaw + dpitch * dpitch if diff < minDiff then minDiff = diff end end b.angleDiff = minDiff table.insert(filteredLiveList, b) end end end table.sort(filteredLiveList, function(a, b) return a.angleDiff < b.angleDiff end) cachedBlocksList = filteredLiveList return cachedBlocksList end -- ВЫДЕЛЕННЫЙ СКАНЕР ПИКОБОЛУСА (ПЕРВИЧНЫЙ СБОР ТВЕРДЫХ БЛОКОВ С КОЛЛИЗИЕЙ) local function scanPickobulusSpace(playerPos) local headPos = player.getEyePosition() local rawHits = {} local scanStep = 6.0 -- Сканируем окружение рейкастом for yaw = -180, 180, scanStep do for pitch = 90, -90, -scanStep do local result = world.raycastFromRotation({ startX = headPos.x, startY = headPos.y, startZ = headPos.z, yaw = yaw, pitch = pitch, range = maxPickobulusDistance, include_fluid = false, include_entity = true, }) -- Проверяем, что луч действительно уперся в твердый блок (коллизия взрыва) if result and result.blockPos and result.type == "block" then local bx, by, bz = math.floor(result.blockPos.x), math.floor(result.blockPos.y), math.floor(result.blockPos.z) local key = bx .. "," .. by .. "," .. bz if not rawHits[key] then local dx = (bx + 0.5) - playerPos.x local dy = (by + 0.5) - playerPos.y local dz = (bz + 0.5) - playerPos.z local dist = math.sqrt(dx * dx + dy * dy + dz * dz) -- Строгая фильтрация по дистанции (8.0 - 15.0) if dist >= minPickobulusDistance and dist <= maxPickobulusDistance then rawHits[key] = { pos = { x = bx, y = by, z = bz }, angles = { { yaw = yaw, pitch = pitch, location = result.location } }, dist = dist } end end end end end -- Собираем список всех валидных точек столкновения local targetList = {} for _, b in pairs(rawHits) do table.insert(targetList, b) end return targetList end -- БЫСТРЫЙ ДИНАМИЧЕСКИЙ ВЫБОР ЛУЧШЕЙ ТОЧКИ ИЗ КЭША (ПО КОЛИЧЕСТВУ РУДЫ РЯДОМ) local function findBestPickobulusTargetFromCache() if not cachedPickobulusTargets or #cachedPickobulusTargets == 0 then return nil end -- Генерация 123 офсетов сферы радиусом 3.0 блока вокруг точки попадания local sphereOffsets = {} for x = -3, 3 do for y = -3, 3 do for z = -3, 3 do if x*x + y*y + z*z <= 9 then table.insert(sphereOffsets, { x = x, y = y, z = z }) end end end end local bestBlock = nil local maxDensity = -1 local maxDist = -1 -- Оцениваем текущее количество живых руд возле каждой кэшированной точки коллизии for _, b in ipairs(cachedPickobulusTargets) do local density = 0 for _, offset in ipairs(sphereOffsets) do local ox, oy, oz = b.pos.x + offset.x, b.pos.y + offset.y, b.pos.z + offset.z local block = world.getBlock(ox, oy, oz) if block and targetIdentifiers[block.identifier] then density = density + 1 end end -- Выбираем точку, где взрыв заденет больше всего руды if density > 0 then if density > maxDensity then maxDensity = density maxDist = b.dist bestBlock = b elseif density == maxDensity and b.dist > maxDist then maxDist = b.dist bestBlock = b end end end if bestBlock then bestBlock.density = maxDensity else player.addMessage("❌ Ошибка: Вокруг кэшированных блоков коллизии не найдено живой руды в радиусе 3 блоков!") end return bestBlock end -- ОБРАБОТЧИК СОБЫТИЙ МИРА registerBlockUpdate(function(info) if not enabled then return end local pos = info.position local key = pos.x .. "," .. pos.y .. "," .. pos.z if cachedWorldMap and cachedWorldMap[key] then local currentBlock = info.new if currentBlock then if targetIdentifiers[currentBlock.identifier] then if brokenBlocks[key] then brokenBlocks[key] = nil end else local currentTick = (serverGameTime and serverGameTime > 0) and serverGameTime or globalTicks local anglesList = cachedWorldMap[key].angles brokenBlocks[key] = { respawnAtServerTick = currentTick + defaultRespawnDelayTicks, targetRot = anglesList[math.random(1, #anglesList)], pos = { x = pos.x, y = pos.y, z = pos.z }, identifier = cachedWorldMap[key].identifier } end end end end) registerClientTick(function() if not enabled then return end globalTicks = globalTicks + 1 if serverGameTime > 0 then serverGameTime = serverGameTime + 1 end local playerPos = player.getPos() local px, py, pz = math.floor(playerPos.x), math.floor(playerPos.y), math.floor(playerPos.z) -- Кэширование геометрии для Пикоболуса (выполняется строго при перемещении игрока) if not cachedPickobulusTargets or px ~= lastPickobulusPlayerPos.x or py ~= lastPickobulusPlayerPos.y or pz ~= lastPickobulusPlayerPos.z then lastPickobulusPlayerPos.x = px lastPickobulusPlayerPos.y = py lastPickobulusPlayerPos.z = pz cachedPickobulusTargets = scanPickobulusSpace(playerPos) end if globalTicks % 2 == 0 then local currentRot = player.getRotation() scannedBlocks = updateWorldAndDominance(currentRot.yaw, currentRot.pitch) end local eye = player.getEyePosition() local targetValid = false -- 1. Логика инициации Пикоболуса if pickobulusEnabled and (globalTicks - lastPickobulusTick >= pickobulusIntervalTicks) and not pickobulusState then local bestP = findBestPickobulusTargetFromCache() if bestP and #bestP.angles > 0 then pickobulusState = "aiming" pickobulusTarget = bestP pickobulusAimTicks = 0 -- Расчет случайного смещения от 3 до 10 градусов для броска local signYaw = (math.random(0, 1) == 0) and 1 or -1 local signPitch = (math.random(0, 1) == 0) and 1 or -1 pickobulusTarget.yawOffset = signYaw * (math.random(100, 400) / 100) pickobulusTarget.pitchOffset = signPitch * (math.random(100, 300) / 100) player.addMessage("§7[§6Hypixel Cry§7] §6☄ Alignment with a collision block with offset: " .. string.format("%.2f", pickobulusTarget.yawOffset) .. "° / " .. string.format("%.2f", pickobulusTarget.pitchOffset) .. "° (Near the ore deposits: " .. bestP.density .. " шт.)") else -- Если подходящей цели не нашли, откладываем попытку на 3 секунды (60 тиков) lastPickobulusTick = globalTicks - pickobulusIntervalTicks + 60 end end -- Стандартная проверка валидности обычной цели if currentTarget and not pickobulusState then local pos = currentTarget.pos local block = world.getBlock(pos.x, pos.y, pos.z) if currentTarget.isWaiting then if block and targetIdentifiers[block.identifier] then currentTarget.isWaiting = false currentTarget.identifier = block.identifier currentTarget.nextAimShiftTick = globalTicks + math.random(minShiftDelay, maxShiftDelay) currentTargetTicks = 0 end targetValid = (currentTarget ~= nil) else local activeDominantGroup = dominantGroup or lastValidDominantGroup local isGroupMatch = (activeDominantGroup == nil) or (getBlockGroup(block.identifier) == activeDominantGroup) if block and isGroupMatch and targetIdentifiers[block.identifier] then local dx = (pos.x + 0.5) - eye.x local dy = (pos.y + 0.5) - eye.y local dz = (pos.z + 0.5) - eye.z local distSq = dx * dx + dy * dy + dz * dz if distSq <= 5.5 * 5.5 then targetValid = true end else targetValid = false end if targetValid then currentTargetTicks = currentTargetTicks + 1 if currentTargetTicks >= 150 then local key = pos.x .. "," .. pos.y .. "," .. pos.z ignoredBlocks[key] = globalTicks + 200 targetValid = false end end if not targetValid then nextTargetAllowedTick = globalTicks + 2 currentTarget = nil currentTargetTicks = 0 end end end if currentTarget and currentTarget.isWaiting and not pickobulusState then local foundBetter = false for _, b in ipairs(scannedBlocks) do if getBlockGroup(b.identifier) == getBlockGroup(currentTarget.identifier) then local block = world.getBlock(b.pos.x, b.pos.y, b.pos.z) if block and targetIdentifiers[block.identifier] then local dx = b.pos.x - currentTarget.pos.x local dy = b.pos.y - currentTarget.pos.y local dz = b.pos.z - currentTarget.pos.z local dist = math.sqrt(dx * dx + dy * dy + dz * dz) if dist <= maxWaitDistance then foundBetter = true break end end end end if foundBetter then if switchTargetDelay <= 0 then switchTargetDelay = math.random(7, 11) elseif switchTargetDelay > 1 then switchTargetDelay = switchTargetDelay - 1 else currentTarget = nil switchTargetDelay = 0 end else switchTargetDelay = 0 end end -- Если текущая цель сброшена и мы НЕ используем Пикоболус if not currentTarget and globalTicks >= nextTargetAllowedTick and not pickobulusState then if #scannedBlocks == 0 then local currentRot = player.getRotation() scannedBlocks = updateWorldAndDominance(currentRot.yaw, currentRot.pitch) end if #scannedBlocks > 0 then currentTarget = scannedBlocks[1] currentTargetTicks = 0 currentTarget.history = {} local randomIndex = math.random(1, #currentTarget.angles) currentTarget.targetRot = currentTarget.angles[randomIndex] table.insert(currentTarget.history, currentTarget.targetRot) if #scannedBlocks > 1 and #scannedBlocks[2].angles > 0 then local nextRandomIndex = math.random(1, #scannedBlocks[2].angles) currentTarget.nextYaw = scannedBlocks[2].angles[nextRandomIndex].yaw currentTarget.nextPitch = scannedBlocks[2].angles[nextRandomIndex].pitch else currentTarget.nextYaw = nil currentTarget.nextPitch = nil end currentTarget.hasArrived = false currentTarget.nextAimShiftTick = 0 else local bestPrediction = nil local minTicksLeft = 999999 local activeGroup = dominantGroup or lastValidDominantGroup local currentTick = (serverGameTime and serverGameTime > 0) and serverGameTime or globalTicks if activeGroup then for key, data in pairs(brokenBlocks) do if getBlockGroup(data.identifier) == activeGroup then local ticksLeft = data.respawnAtServerTick - currentTick if ticksLeft < -100 then brokenBlocks[key] = nil else local dx = (data.pos.x + 0.5) - eye.x local dy = (data.pos.y + 0.5) - eye.y local dz = (data.pos.z + 0.5) - eye.z local distSq = dx * dx + dy * dy + dz * dz if distSq <= maxWaitDistance * maxWaitDistance then if ticksLeft < minTicksLeft then minTicksLeft = ticksLeft bestPrediction = data end end end end end end if bestPrediction then local key = bestPrediction.pos.x .. "," .. bestPrediction.pos.y .. "," .. bestPrediction.pos.z local chosenRot = bestPrediction.targetRot local targetAngles = {} if cachedWorldMap and cachedWorldMap[key] and cachedWorldMap[key].angles then targetAngles = cachedWorldMap[key].angles if #targetAngles > 0 then chosenRot = targetAngles[math.random(1, #targetAngles)] end end currentTarget = { pos = bestPrediction.pos, targetRot = chosenRot, identifier = bestPrediction.identifier, isWaiting = true, hasArrived = false, angles = targetAngles, history = { chosenRot }, nextAimShiftTick = 0 } end end end local targetRot = nil local shouldRotate = true -- Логика ротации и наведения if pickobulusState == "aiming" and pickobulusTarget then -- Наведение на блок для Пикоболуса local playerRot = player.getRotation() local bestAng = pickobulusTarget.angles[1] -- Применяем случайное смещение local targetYaw = bestAng.yaw + (pickobulusTarget.yawOffset or 0) local targetPitch = bestAng.pitch + (pickobulusTarget.pitchOffset or 0) -- Нормализация углов if targetYaw > 180 then targetYaw = targetYaw - 360 end if targetYaw < -180 then targetYaw = targetYaw + 360 end if targetPitch > 90 then targetPitch = 90 end if targetPitch < -90 then targetPitch = -90 end targetRot = { yaw = targetYaw, pitch = targetPitch } local dyaw = targetYaw - playerRot.yaw if dyaw > 180 then dyaw = dyaw - 360 end if dyaw < -180 then dyaw = dyaw + 360 end local dpitch = targetPitch - playerRot.pitch local diffSq = dyaw * dyaw + dpitch * dpitch -- Увеличиваем порог попадания с учетом смещения if diffSq < 0.64 then pickobulusAimTicks = pickobulusAimTicks + 1 if pickobulusAimTicks >= 4 then -- Даем 4 тика на стабилизацию взгляда player.input.useItem() -- ПКМ (Используем Пикоболус) player.addMessage("§7[§6Hypixel Cry§7] §a💥 Picobulus has been launched!") lastPickobulusTick = globalTicks pickobulusState = nil pickobulusTarget = nil currentTarget = nil -- Сбрасываем цель, чтобы возобновить обычное копание end else pickobulusAimTicks = 0 end elseif currentTarget and currentTarget.targetRot then -- Наведение в обычном режиме local playerRot = player.getRotation() local dyaw = currentTarget.targetRot.yaw - playerRot.yaw if dyaw > 180 then dyaw = dyaw - 360 end if dyaw < -180 then dyaw = dyaw + 360 end local dpitch = currentTarget.targetRot.pitch - playerRot.pitch local diffSq = dyaw * dyaw + dpitch * dpitch if not currentTarget.hasArrived then if diffSq < 0.36 then currentTarget.hasArrived = true currentTarget.nextAimShiftTick = globalTicks + math.random(minShiftDelay, maxShiftDelay) end elseif not currentTarget.isWaiting then if globalTicks >= currentTarget.nextAimShiftTick then if #currentTarget.angles > 1 then local candidates = {} for _, ang in ipairs(currentTarget.angles) do local visited = false for _, hist in ipairs(currentTarget.history or {}) do if math.abs(hist.yaw - ang.yaw) < 0.01 and math.abs(hist.pitch - ang.pitch) < 0.01 then visited = true break end end if not visited then table.insert(candidates, ang) end end if #candidates == 0 then currentTarget.history = { { yaw = currentTarget.targetRot.yaw, pitch = currentTarget.targetRot.pitch } } for _, ang in ipairs(currentTarget.angles) do if math.abs(ang.yaw - currentTarget.targetRot.yaw) >= 0.01 or math.abs(ang.pitch - currentTarget.targetRot.pitch) >= 0.01 then table.insert(candidates, ang) end end end if #candidates > 0 then local selectedAng = nil if currentTarget.nextYaw and currentTarget.nextPitch then local bestAng = nil local minDiff = 999999 for _, ang in ipairs(candidates) do local dyawDiff = ang.yaw - currentTarget.nextYaw if dyawDiff > 180 then dyawDiff = dyawDiff - 360 end if dyawDiff < -180 then dyawDiff = dyawDiff + 360 end local dpitchDiff = ang.pitch - currentTarget.nextPitch if dpitchDiff > 180 then dpitchDiff = dpitchDiff - 360 end if dpitchDiff < -180 then dpitchDiff = dpitchDiff + 360 end local diff = dyawDiff * dyawDiff + dpitchDiff * dpitchDiff if diff < minDiff then minDiff = diff bestAng = ang end end selectedAng = bestAng else selectedAng = candidates[math.random(1, #candidates)] end if selectedAng then currentTarget.targetRot = selectedAng table.insert(currentTarget.history, selectedAng) end end currentTarget.hasArrived = false end end end targetRot = currentTarget.targetRot lastActiveRot = { yaw = targetRot.yaw, pitch = targetRot.pitch } elseif lastActiveRot then targetRot = lastActiveRot end if targetRot and shouldRotate then rotations.rotateToYawPitch(targetRot.yaw, targetRot.pitch) end local isAttacking = false if pickobulusState == "aiming" then isAttacking = false -- Отключаем зажатие ЛКМ при броске Пикоболуса elseif currentTarget and shouldRotate and not currentTarget.isWaiting then local ray = player.raycast(5.0) if ray then if ray.type == "block" and ray.blockPos then local lookedBlock = world.getBlock(ray.blockPos.x, ray.blockPos.y, ray.blockPos.z) local isTargetPos = (ray.blockPos.x == currentTarget.pos.x and ray.blockPos.y == currentTarget.pos.y and ray.blockPos.z == currentTarget.pos.z) if (lookedBlock and targetIdentifiers[lookedBlock.identifier]) or isTargetPos then isAttacking = true end elseif ray.type == "entity" then isAttacking = false end end end local entities = world.getLivingEntities() for _, entity in ipairs(entities) do if entity ~= player.entity and (entity.type == "entity.minecraft.player" or entity.type == "entity.minecraft.villager") then if player.entity.box.intersects(entity.box) then isAttacking = false break end end end player.input.setPressedAttack(false) if not player.inventory.isAnyScreenOpened() then player.input.setPressedAttack(isAttacking) end local function predictNext() if not currentTarget or currentTarget.isWaiting then return nil end if #currentTarget.angles > 1 then local candidates = {} for _, ang in ipairs(currentTarget.angles) do local visited = false for _, hist in ipairs(currentTarget.history or {}) do if math.abs(hist.yaw - ang.yaw) < 0.01 and math.abs(hist.pitch - ang.pitch) < 0.01 then visited = true break end end if not visited then table.insert(candidates, ang) end end if #candidates == 0 then for _, ang in ipairs(currentTarget.angles) do if math.abs(ang.yaw - currentTarget.targetRot.yaw) >= 0.01 or math.abs(ang.pitch - currentTarget.targetRot.pitch) >= 0.01 then table.insert(candidates, ang) end end end if #candidates > 0 then if currentTarget.nextYaw and currentTarget.nextPitch then local bestAng = nil local minDiff = 999999 for _, ang in ipairs(candidates) do local dyawDiff = ang.yaw - currentTarget.nextYaw if dyawDiff > 180 then dyawDiff = dyawDiff - 360 end if dyawDiff < -180 then dyawDiff = dyawDiff + 360 end local dpitchDiff = ang.pitch - currentTarget.nextPitch if dpitchDiff > 180 then dpitchDiff = dpitchDiff - 360 end if dpitchDiff < -180 then dpitchDiff = dpitchDiff + 360 end local diff = dyawDiff * dyawDiff + dpitchDiff * dpitchDiff if diff < minDiff then minDiff = diff bestAng = ang end end return bestAng else return candidates[1] end end end for i = 1, #scannedBlocks do local b = scannedBlocks[i] if b.pos.x ~= currentTarget.pos.x or b.pos.y ~= currentTarget.pos.y or b.pos.z ~= currentTarget.pos.z then if #b.angles > 0 then return b.angles[1] end end end return nil end nextTargetVisual = predictNext() end) registerWorldRenderer(function(ctx) if not enabled then return end rotations.update() -- Отображение цели Пикоболуса в виде фиолетового заполненного блока if pickobulusState == "aiming" and pickobulusTarget then local pos = pickobulusTarget.pos if pos then local margin = 0.01 local box = creator.createBox( pos.x - margin, pos.y - margin, pos.z - margin, pos.x + 1 + margin, pos.y + 1 + margin, pos.z + 1 + margin ) ctx.renderFilled(box, 138, 43, 226, 180, true) -- Фиолетовое свечение на всю руду end end if currentTarget and currentTarget.targetRot and currentTarget.targetRot.location and not pickobulusState then local targetLoc = currentTarget.targetRot.location local offset = 0.05 local box = creator.createBox( targetLoc.x - offset, targetLoc.y - offset, targetLoc.z - offset, targetLoc.x + offset, targetLoc.y + offset, targetLoc.z + offset ) if currentTarget.isWaiting then ctx.renderFilled(box, 255, 0, 255, 140, true) else ctx.renderFilled(box, 255, 0, 0, 140, true) end end if nextTargetVisual and nextTargetVisual.location and not pickobulusState then local targetLoc = nextTargetVisual.location local offset = 0.05 local box = creator.createBox( targetLoc.x - offset, targetLoc.y - offset, targetLoc.z - offset, targetLoc.x + offset, targetLoc.y + offset, targetLoc.z + offset ) ctx.renderFilled(box, 255, 255, 0, 140, true) end local margin = 0.002 if not pickobulusState then for _, b in ipairs(scannedBlocks) do local box = creator.createBox( b.pos.x - margin, b.pos.y - margin, b.pos.z - margin, b.pos.x + 1 + margin, b.pos.y + 1 + margin, b.pos.z + 1 + margin ) ctx.renderFilled(box, 0, 0, 255, 80, false) end end end) registerKeyEvent(function(key, action) if key == keys.KEY_GRAVE_ACCENT and action == "Press" then enabled = not enabled if enabled then player.addMessage("§7[§6Hypixel Cry§7] §aEnabled") -- Сбрасываем таймер, чтобы Пикоболус сработал мгновенно при включении lastPickobulusTick = globalTicks - pickobulusIntervalTicks else player.addMessage("§7[§6Hypixel Cry§7] §cDisabled") lastCachedPlayerPos = { x = 0, y = 0, z = 0 } serverGameTime = 0 lastServerTimeUpdate = 0 player.input.setPressedAttack(false) currentTarget = nil targetRot = nil cachedWorldMap = nil cachedBlocksList = nil lastActiveRot = nil brokenBlocks = {} activeBreaks = {} dominantGroup = nil lastValidDominantGroup = nil -- Сброс состояния Пикоболуса и кэша позиций pickobulusState = nil pickobulusTarget = nil pickobulusAimTicks = 0 cachedPickobulusTargets = nil lastPickobulusPlayerPos = { x = 0, y = 0, z = 0 } end end end) registerMessageEvent(function(text, overlay, json) if not enabled then return end if text and not overlay then local cleanText = stripColorCodes(text) local cleanLower = cleanText:lower() -- Поиск ключевых слов "pickobulus" и "available" в любом регистре if cleanLower:find("pickobulus") and cleanLower:find("available") then -- Сбрасываем кулдаун для мгновенного броска на следующем тике lastPickobulusTick = globalTicks - pickobulusIntervalTicks player.addMessage("§7[§6Hypixel Cry§7] §a ⚡ Picobulus is flooded with messages from the chat!") end end end)