diff --git a/Conditionals.lua b/Conditionals.lua index 9cd9673..51713ef 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -8780,20 +8780,26 @@ CleveRoids.STATIC_CONDITIONALS = { --- @param priority string Priority type (e.g., "nearest", "highesthp") --- @param currentTargetGuid string|nil GUID of player's current target (exempt from combat check) --- @param specifiedUnitGuid string|nil GUID of @unit specified in macro (also exempt from combat check) +--- @param friendly boolean|nil If true, scan friendly units instead of enemies --- @return number|nil Score (lower = better) or nil if invalid candidate -function CleveRoids.GetMultiscanScore(unit, priority, currentTargetGuid, specifiedUnitGuid) +function CleveRoids.GetMultiscanScore(unit, priority, currentTargetGuid, specifiedUnitGuid, friendly) if not unit or not UnitExists(unit) then return nil end if CleveRoids.IsUnitDeadOrGhost(unit) then return nil end - -- Must be attackable - if not UnitCanAttack("player", unit) then return nil end + if friendly then + -- Must be friendly + if not UnitIsFriend("player", unit) then return nil end + else + -- Must be attackable + if not UnitCanAttack("player", unit) then return nil end - -- Combat check: must be in combat with player, UNLESS it's current target OR specified @unit - local unitGuid = CleveRoids.GetGUID(unit) - local isCurrentTarget = currentTargetGuid and unitGuid == currentTargetGuid - local isSpecifiedUnit = specifiedUnitGuid and unitGuid == specifiedUnitGuid - if not isCurrentTarget and not isSpecifiedUnit and not UnitAffectingCombat(unit) then - return nil + -- Combat check: must be in combat with player, UNLESS it's current target OR specified @unit + local unitGuid = CleveRoids.GetGUID(unit) + local isCurrentTarget = currentTargetGuid and unitGuid == currentTargetGuid + local isSpecifiedUnit = specifiedUnitGuid and unitGuid == specifiedUnitGuid + if not isCurrentTarget and not isSpecifiedUnit and not UnitAffectingCombat(unit) then + return nil + end end -- Calculate score based on priority @@ -8880,8 +8886,9 @@ end --- @param specifiedUnit string|nil The @unit specified in the macro (exempt from combat check) --- @return string|nil GUID of best target, or nil if none found function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit) - -- Require UnitXP for enemy scanning - if not CleveRoids.hasUnitXP then + -- Require UnitXP for enemy scanning (friendly mode doesn't need it) + local friendly = conditionals.help and true or false + if not friendly and not CleveRoids.hasUnitXP then return nil end @@ -8920,6 +8927,49 @@ function CleveRoids.ResolveMultiscanTarget(conditionals, specifiedUnit) specifiedUnitGuid = CleveRoids.GetGUID(specifiedUnit) end + -- Friendly mode: scan party/raid members instead of enemies + if friendly then + local candidates = {} + local seenGuids = {} + + local function evaluateFriendly(unit) + if not UnitExists(unit) then return end + local guid = CleveRoids.GetGUID(unit) + if not guid or seenGuids[guid] then return end + seenGuids[guid] = true + + local score = CleveRoids.GetMultiscanScore(unit, priorityType, currentTargetGuid, specifiedUnitGuid, true) + if not score then return end + + if CleveRoids.ValidateMultiscanCandidate(conditionals, guid) then + table.insert(candidates, { guid = guid, score = score }) + end + end + + -- Always consider player + evaluateFriendly("player") + + -- Scan party or raid members + if GetNumRaidMembers() > 0 then + for i = 1, 40 do + evaluateFriendly("raid" .. i) + evaluateFriendly("raidpet" .. i) + end + else + for i = 1, 4 do + evaluateFriendly("party" .. i) + evaluateFriendly("partypet" .. i) + end + end + + if table.getn(candidates) == 0 then + return nil + end + + table.sort(candidates, function(a, b) return a.score < b.score end) + return candidates[1].guid + end + -- Handle raid mark priorities (direct unit reference) if type(priorityType) == "number" then local markUnit = "mark" .. priorityType diff --git a/Core.lua b/Core.lua index b71844b..2f00021 100644 --- a/Core.lua +++ b/Core.lua @@ -162,26 +162,21 @@ requirementCheckFrame:SetScript("OnEvent", function() and CleveRoids.NampowerAPI.HasMinimumVersion(3, 0, 0) if not hasNampower30 or not hasUnitXP then - -- Show errors + -- Show warnings (don't disable — tearing down a partially-initialized addon causes hangs) if not hasNampower then - CleveRoids.Print("|cFFFF0000SuperCleveRoidMacros|r requires |cFF00FFFFAvitasia's Nampower v3.0.0+|r:") + CleveRoids.Print("|cFFFF9900WARNING:|r |cFF00FFFFAvitasia's Nampower v3.0.0+|r is required:") CleveRoids.Print("https://gitea.com/avitasia/nampower") + CleveRoids.Print("Many features will be unavailable without Nampower.") elseif not hasNampower30 then local major, minor, patch = CleveRoids.NampowerAPI.GetVersion() - CleveRoids.Print(format("|cFFFF0000SuperCleveRoidMacros|r requires |cFF00FFFFNampower v3.0.0+|r (you have v%d.%d.%d):", major, minor, patch)) + CleveRoids.Print(format("|cFFFF9900WARNING:|r |cFF00FFFFNampower v3.0.0+|r is required (you have v%d.%d.%d):", major, minor, patch)) CleveRoids.Print("https://gitea.com/avitasia/nampower") + CleveRoids.Print("Some features may be unavailable with this version.") end if not hasUnitXP then - CleveRoids.Print("|cFFFF0000SuperCleveRoidMacros|r requires |cFF00FFFFKonaka's UnitXP_SP3|r:") + CleveRoids.Print("|cFFFF9900WARNING:|r |cFF00FFFFKonaka's UnitXP_SP3|r is required:") CleveRoids.Print("https://codeberg.org/konaka/UnitXP_SP3") end - - -- Disable immediately - CleveRoids.DisableAddon("Missing Requirements") - - -- Unregister this check frame - this:UnregisterAllEvents() - return end -- Requirements met - allow normal initialization