local starred = {} local cache = {} -- Здесь храним только ID таргетов: cache[id] = true local SEARCH_RADIUS = 0.65 -- Настройки частоты обновлений local SCAN_INTERVAL = 4 -- Сканировать каждые 4 тика (снижает нагрузку) local tickCounter = 0 -- Радиус для повторной проверки закешированных мобов local RECHECK_RADIUS = 10.0 local RECHECK_RADIUS_SQ = RECHECK_RADIUS * RECHECK_RADIUS -- оптимизация: избегаем sqrt -- Локализация для скорости local player = require("player") local world = require("world") local ipairs = ipairs local pairs = pairs local string_find = string.find local math_sqrt = math.sqrt local world_getEntities = world.getEntities local world_getEntitiesInBox = world.getEntitiesInBox -- Константы для поиска local BOX_EXPAND_Y = 2.0 -- Функция проверки конкретной сущности local function checkIsStarred(entity) if not entity then return false end if not entity.is_alive then return false end -- Ищем сущности в коробке над мобом local box = entity.box if not box then return false end -- getEntitiesInBox возвращает список, проверяем его local entitiesAbove = world_getEntitiesInBox(box.expand(0, BOX_EXPAND_Y, 0)) if entitiesAbove then for _, ent in ipairs(entitiesAbove) do -- Проверяем, является ли сущность стойкой для брони if ent and ent.type == "entity.minecraft.armor_stand" then local name = ent.display_name or "" -- Проверяем ключевые слова if string_find(name, "§5") and string_find(name, "§d") then return true end end end end return false end -- Функция вычисления квадрата расстояния между игроком и сущностью (только по горизонтали) local function distanceSqToPlayer(entity, playerLoc) if not entity or not entity.position or not playerLoc then return math.huge end local entLoc = entity.position local dx = entLoc.x - playerLoc.x local dz = entLoc.z - playerLoc.z local dy = entLoc.y - playerLoc.y return dx * dx + dz * dz + dy * dy -- игнорируем Y для более естественного поведения end local function updateTargets(playerLoc) local entities = world_getEntities() local mobs = {} local currentTickIDs = {} -- Список ID, которые существуют сейчас for _, entity in ipairs(entities) do -- Проверяем только живых существ, исключая игроков и стойки if entity and entity.type ~= "entity.minecraft.armor_stand" and entity.type ~= "entity.minecraft.item" then local id = entity.id currentTickIDs[id] = true -- Логика обработки кеша: -- 1. Если моб в кеше И находится в радиусе 15 блоков — повторно проверяем -- 2. Если проверка не проходит — удаляем из кеша -- 3. Если моб в кеше, но ДАЛЕКО (>15 блоков) — доверяем кешу (не проверяем) -- 4. Если моба нет в кеше — проверяем обычным способом local inCache = cache[id] local nearby = inCache and (distanceSqToPlayer(entity, playerLoc) <= RECHECK_RADIUS_SQ) if inCache then if nearby then -- Моб рядом — повторно проверяем звезду if checkIsStarred(entity) then mobs[#mobs + 1] = entity else -- Звезда пропала — удаляем из кеша cache[id] = nil end else -- Моб далеко — доверяем кешу без проверки mobs[#mobs + 1] = entity end else -- Моба нет в кеше — проверяем впервые if checkIsStarred(entity) then cache[id] = true mobs[#mobs + 1] = entity end -- Если проверка не прошла — не кешируем, проверим снова через несколько тиков end end end -- Очистка памяти: удаляем из кеша мобов, которых больше нет в мире for id in pairs(cache) do if not currentTickIDs[id] then cache[id] = nil end end return mobs end registerClientTick(function() -- Получаем локацию игрока один раз за тик local playerLoc = player.getPos() local loc = player.getLocation() if loc and (loc == "DUNGEON" or string_find(loc, "Dungeon")) then if #starred > 0 then starred = {} end if next(cache) ~= nil then cache = {} end else tickCounter = tickCounter + 1 -- Запускаем поиск только раз в SCAN_INTERVAL тиков if tickCounter >= SCAN_INTERVAL then starred = updateTargets(playerLoc) tickCounter = 0 end end end) registerWorldRenderer(function(context) -- Рендерим список starred for i = 1, #starred do local entity = starred[i] -- Проверка на nil на случай рассинхрона if entity and entity.box then context.renderOutline(entity.box, 255, 52, 255, 140, 2, true) end end end)