--- -- @version 0.1.2 -- @location /libs/ -- @author smarrtie local player = require("player") local world = require("world") ---@class EntityUtils ---@field reach number The reach distance for entity interactions ---@field closest any The closest entity found ---@field steps number Number of raycast steps for hitbox checking (higher = more accurate but slower) ---@field filter table Keyed map of entities to filter out ---@type EntityUtils local entityUtils = {} entityUtils.reach = 3.5 entityUtils.closest = nil entityUtils.steps = 5 entityUtils.filter = { [player.entity] = false } ---Checks if an entity is in the filter list ---@param entity any The entity to check ---@return boolean returns TRUE if entity is in the filter function entityUtils.filterEntity(entity) if entity and entityUtils.filter[entity] then return true end return false end ---Adds an entity to the filter list ---@param entity any The entity to add to filter function entityUtils.addToFilter(entity) if entity then entityUtils.filter[entity] = true end end ---Removes an entity from the filter list ---@param entity any The entity to remove from filter function entityUtils.removeFromFilter(entity) if entity then entityUtils.filter[entity] = false end end ---Gets all entities matching a name (case-insensitive partial match) ---@param name string The name to search for ---@return entity[] Array of matching entities function entityUtils.getEntitiesByName(name) local entities = world.getEntities() local scanResult = {} local lowerName = string.lower(name) for _, entity in pairs(entities) do local displayName = string.lower(entity.display_name) if displayName:find(lowerName) then scanResult[#scanResult + 1] = entity end end return scanResult end ---Gets the closest valid entity to the player (excluding filtered entities) ---@return any|nil The closest entity, or nil if none found function entityUtils.getClosestEntity() local closest = nil local entities = world.getEntities() for _, entity in pairs(entities) do if not entityUtils.filterEntity(entity) then local hasValidBox = entity.box and entity.box.maxX and entity.box.minX and entity.box.maxY and entity.box.minY and entity.box.maxZ and entity.box.minZ and entity.box.maxX > entity.box.minX and entity.box.maxY > entity.box.minY and entity.box.maxZ > entity.box.minZ if entity.distance_to_player ~= 0 and entity.health and entity.health > 0 and hasValidBox then if closest == nil or entity.distance_to_player < closest.distance_to_player then closest = entity end end end end return closest end ---Gets the closest living entity to the player (excluding filtered entities) ---@return any|nil The closest living entity, or nil if none found function entityUtils.getClosestLivingEntity() local closest = nil local entities = world.getLivingEntities() for _, entity in pairs(entities) do if not entityUtils.filterEntity(entity) then local hasValidBox = entity.box and entity.box.maxX and entity.box.minX and entity.box.maxY and entity.box.minY and entity.box.maxZ and entity.box.minZ and entity.box.maxX > entity.box.minX and entity.box.maxY > entity.box.minY and entity.box.maxZ > entity.box.minZ if entity.distance_to_player ~= 0 and entity.health and entity.health > 0 and hasValidBox then if closest == nil or entity.distance_to_player < closest.distance_to_player then closest = entity end end end end return closest end ---Raycasts from entity box to find closest visible point ---@private ---@param box any The box to raycast against ---@return any|nil local function getBoxRays(box) local eyePos = player.getEyePosition() local steps = entityUtils.steps if not eyePos then return nil end local stepX = (box.maxX - box.minX) / (steps - 1) local stepY = (box.maxY - box.minY) / (steps - 1) local stepZ = (box.maxZ - box.minZ) / (steps - 1) local closestPoint = nil local minDistance = math.huge for ix = 1, steps - 2 do for iy = 1, steps - 2 do for iz = 1, steps - 2 do local px = box.minX + (ix * stepX) local py = box.minY + (iy * stepY) local pz = box.minZ + (iz * stepZ) local ray = world.raycast({ startX = eyePos.x, startY = eyePos.y, startZ = eyePos.z, endX = px, endY = py, endZ = pz, include_entity = true, }) local isVisible = true if ray ~= nil and ray.type == "entity" then isVisible = true end if isVisible then local dx = eyePos.x - px local dy = eyePos.y - py local dz = eyePos.z - pz local distSq = (dx * dx) + (dy * dy) + (dz * dz) if distSq < minDistance then minDistance = distSq closestPoint = { x = px, y = py, z = pz } end end end end end return closestPoint end ---Gets the closest visible point on an entity's hitbox using raycasting ---@param entity any The entity to check ---@return table|nil Position {x, y, z} of the closest visible hitbox point, or nil if not found function entityUtils.getClosestHitbox(entity) if not entity or not entity.box then return nil end local box = entity.box local hasValidBox = box.maxX and box.minX and box.maxY and box.minY and box.maxZ and box.minZ and box.maxX > box.minX and box.maxY > box.minY and box.maxZ > box.minZ if not hasValidBox then return nil end return getBoxRays(box) end return entityUtils