Upload files to "/"
This commit is contained in:
+548
-4
@@ -21,7 +21,10 @@ local trackedEvents = {
|
||||
"BANKFRAME_OPENED", "TRAINER_SHOW", "MAIL_SEND_SUCCESS", "PLAYER_EQUIPMENT_CHANGED",
|
||||
"BAG_UPDATE", "CHARACTER_POINTS_CHANGED", "SPELLS_CHANGED", "UNIT_HEALTH", "PLAYER_MONEY",
|
||||
-- Vanilla 1.12 event names (PLAYER_EQUIPMENT_CHANGED does not exist here).
|
||||
"UNIT_INVENTORY_CHANGED", "ADDON_LOADED", "MERCHANT_SHOW", "MERCHANT_CLOSED"
|
||||
"UNIT_INVENTORY_CHANGED", "ADDON_LOADED", "MERCHANT_SHOW", "MERCHANT_CLOSED",
|
||||
"TIME_PLAYED_MSG", "UNIT_PET", "UNIT_MODEL_CHANGED", "LEARNED_SPELL_IN_TAB",
|
||||
-- OctoWoW / Turtle backported Companion API (Pet menu "P" -> Companions tab)
|
||||
"COMPANION_LEARNED", "COMPANION_UPDATE", "COMPANION_UNLEARNED", "PET_UI_UPDATE"
|
||||
}
|
||||
for i = 1, table.getn(trackedEvents) do SafeRegister(trackedEvents[i]) end
|
||||
|
||||
@@ -157,6 +160,128 @@ end
|
||||
local RA_ScanTip = CreateFrame("GameTooltip", "RelationshipsAchievementsScanTip", UIParent, "GameTooltipTemplate")
|
||||
RA_ScanTip:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
|
||||
-- ============ Known companion names (Vanilla + TurtleWoW / OctoWoW) ============
|
||||
-- Any spell whose name matches one of these entries is credited as a
|
||||
-- non-combat pet even when its tooltip doesn't contain the standard
|
||||
-- keywords. Extend this list as new companions are added to the server.
|
||||
local RA_KNOWN_COMPANIONS_LIST = {
|
||||
-- Vanilla non-combat pets
|
||||
"Ancona Chicken", "Azure Whelpling", "Bombay Cat", "Black Kingsnake",
|
||||
"Black Tabby", "Blue Dragonhawk Hatchling", "Brown Prairie Dog",
|
||||
"Brown Rabbit", "Brown Snake", "Calico Cat", "Cat Carrier (Bombay)",
|
||||
"Cat Carrier (Black Tabby)", "Cat Carrier (Cornish Rex)",
|
||||
"Cat Carrier (Orange Tabby)", "Cat Carrier (Siamese)",
|
||||
"Cat Carrier (Silver Tabby)", "Cat Carrier (White Kitten)",
|
||||
"Chicken Egg", "Cockroach", "Cornish Rex", "Crimson Whelpling",
|
||||
"Dark Whelpling", "Disgusting Oozeling", "Emerald Whelpling",
|
||||
"Firefly", "Frog", "Golden Dragonhawk Hatchling", "Great Horned Owl",
|
||||
"Green Wing Macaw", "Hawk Owl", "Hyacinth Macaw", "Island Frog",
|
||||
"Jubling", "Lifelike Mechanical Toad", "Lifelike Toad",
|
||||
"Little Fawn", "Mana Wyrmling", "Mechanical Squirrel Box",
|
||||
"Mini Diablo", "Mulgore Hatchling", "Orange Tabby",
|
||||
"Panda Cub", "Parrot Cage (Green Wing Macaw)",
|
||||
"Parrot Cage (Hyacinth Macaw)", "Parrot Cage (Senegal)",
|
||||
"Peddlefeet", "Personal World Destroyer", "Prairie Dog Whistle",
|
||||
"Red Dragonhawk Hatchling", "Sprite Darter Hatchling",
|
||||
"Senegal", "Siamese", "Silver Tabby", "Smolderweb Hatchling",
|
||||
"Snowshoe Rabbit", "Snowy Owl", "Speedy", "Stinker",
|
||||
"Tiny Green Dragon", "Tiny Red Dragon", "Tranquil Mechanical Yeti",
|
||||
"Tree Frog Box", "Turquoise Turtle", "Wanderer's Companion",
|
||||
"Westfall Chicken", "White Kitten", "Whiskers the Rat",
|
||||
"Winter Reindeer", "Winter's Little Helper", "Worg Carrier",
|
||||
"Worg Pup", "Wolpertinger", "Zergling",
|
||||
-- Turtle WoW / OctoWoW additions (community-known companions)
|
||||
"Baby Blizzard Bear", "Baby Crocolisk", "Baby Murloc",
|
||||
"Baby Ogre", "Baby Shark", "Baby Tallstrider", "Baby Turtle",
|
||||
"Bat Cub", "Bloodsail Cannonball", "Blue Moth", "Bronze Whelpling",
|
||||
"Captured Firefly", "Clockwork Rocket Bot", "Cobra Hatchling",
|
||||
"Coral Whelpling", "Corrupt Whelpling", "Deviate Hatchling",
|
||||
"Elekk Plushie", "Ethereal Soul-Trader", "Fel Whelpling",
|
||||
"Forest Frog", "Fox Kit", "Ghost Cat", "Ghostly Skull",
|
||||
"Gorilla Hatchling", "Grunty", "Hippogryph Hatchling",
|
||||
"Ivory Raptor", "Jade Whelpling", "Lil' Alexstrasza",
|
||||
"Lil' Deathwing", "Lil' K.T.", "Lil' Ragnaros", "Lil' Smoky",
|
||||
"Lil' Tarecgosa", "Lil' Timmy", "Lil' Wickerman", "Lil' XT",
|
||||
"Lucky Cricket", "Magical Crawdad", "Mechanical Chicken",
|
||||
"Micro Sentry", "Mini Tyrael", "Moonkin Hatchling",
|
||||
"Murky", "Naga Hatchling", "Netherwhelp", "Nightsaber Cub",
|
||||
"Onyx Whelpling", "Pandaren Monk", "Peanut", "Perky Pug",
|
||||
"Piglet's Collar", "Pink Elekk", "Plague Rat", "Polar Bear Cub",
|
||||
"Purple Turtle", "Quilen Cub", "Raptor Hatchling", "Red Panda",
|
||||
"Reef Wanderer", "Riding Turtle Hatchling", "Sand Kitten",
|
||||
"Sandbox Tiger", "Sandstone Drake", "Sea Pony", "Shore Crab",
|
||||
"Silithid Hatchling", "Snowball", "Snowy Rabbit",
|
||||
"Spectral Tiger Cub", "Stormwind Rat", "Swamp Frog",
|
||||
"Tiny Emerald Whelpling", "Tiny Sporebat", "Tricorne",
|
||||
"Turkey", "Undercity Cockroach", "Wisp", "Withers",
|
||||
"Ziggy the Sifaka",
|
||||
-- OctoWoW Companions tab (as shown in the "P" menu)
|
||||
"Green Steam Tonk", "Purple Steam Tonk", "Blue Steam Tonk",
|
||||
"Red Steam Tonk", "Yellow Steam Tonk", "Steam Tonk",
|
||||
"Steam Tonk Controller", "Control Console",
|
||||
"Lulu", "Mr. Wiggles", "Pet Bombling", "Lifelike Mechanical Toad",
|
||||
"Tranquil Mechanical Yeti", "Mechanical Squirrel",
|
||||
"Green Kitten", "Black Kitten", "Orange Kitten", "Cornish Rex Cat",
|
||||
"Bombay Cat", "Siamese Cat", "Silver Tabby Cat",
|
||||
}
|
||||
local RA_KNOWN_COMPANIONS_SET = {}
|
||||
for _, n in ipairs(RA_KNOWN_COMPANIONS_LIST) do
|
||||
RA_KNOWN_COMPANIONS_SET[string.lower(n)] = true
|
||||
end
|
||||
|
||||
-- Substring patterns that identify a companion regardless of colour / prefix.
|
||||
-- Any spellbook or summoned pet name containing one of these (case-insensitive)
|
||||
-- is treated as a known companion. Covers the OctoWoW / Turtle families where
|
||||
-- every colour variant shares the same base name (e.g. "Green Steam Tonk",
|
||||
-- "Purple Steam Tonk", "White Steam Tonk" all match "steam tonk").
|
||||
local RA_COMPANION_PATTERNS = {
|
||||
"steam tonk", "kitten", "whelpling", "hatchling", "cub",
|
||||
"prairie dog", "rabbit", "squirrel", "toad", "frog",
|
||||
"cockroach", "cat carrier", "mechanical squirrel",
|
||||
"lil'", "lil ", "mini ", "tiny ", "baby ",
|
||||
"companion", "critter",
|
||||
}
|
||||
|
||||
function RA_IsKnownCompanionName(name)
|
||||
if type(name) ~= "string" or name == "" then return false end
|
||||
local lower = string.lower(name)
|
||||
if RA_KNOWN_COMPANIONS_SET[lower] == true then return true end
|
||||
for _, pat in ipairs(RA_COMPANION_PATTERNS) do
|
||||
if string.find(lower, pat, 1, true) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function RA_AddKnownCompanion(name)
|
||||
if type(name) == "string" and name ~= "" then
|
||||
RA_KNOWN_COMPANIONS_SET[string.lower(name)] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Credit a companion by name into the same store the tooltip path uses so
|
||||
-- the total petCount stays consistent no matter which detection path found it.
|
||||
function RA_CreditCompanionByName(name)
|
||||
if not RA_IsKnownCompanionName(name) then return end
|
||||
if not RelationshipsAchievementsCharDB then return end
|
||||
if not RelationshipsAchievementsCharDB.countedPets then
|
||||
RelationshipsAchievementsCharDB.countedPets = {}
|
||||
end
|
||||
RelationshipsAchievementsCharDB.countedPets[name] = true
|
||||
local n = 0
|
||||
for _ in pairs(RelationshipsAchievementsCharDB.countedPets) do n = n + 1 end
|
||||
local companionPets = 0
|
||||
if RelationshipsAchievementsCharDB.countedCompanions then
|
||||
for _ in pairs(RelationshipsAchievementsCharDB.countedCompanions) do
|
||||
companionPets = companionPets + 1
|
||||
end
|
||||
end
|
||||
local total = n
|
||||
if companionPets > total then total = companionPets end
|
||||
RelationshipsAchievements:UpdateStat("petCount", total)
|
||||
if total >= 1 then RelationshipsAchievements:Unlock(1204) end
|
||||
if total >= 10 then RelationshipsAchievements:Unlock(1205) end
|
||||
end
|
||||
|
||||
local function TooltipText(index, book)
|
||||
RA_ScanTip:ClearLines()
|
||||
RA_ScanTip:SetSpell(index, book)
|
||||
@@ -171,6 +296,123 @@ local function TooltipText(index, book)
|
||||
return string.lower(text)
|
||||
end
|
||||
|
||||
-- ============ Known mount names (Vanilla + TurtleWoW / OctoWoW) ============
|
||||
-- Same rationale as the companion list: some private-server mounts (e.g. the
|
||||
-- OctoWoW "Zebra") have tooltips that don't include the classic "mount speed"
|
||||
-- / "rideable" phrasing, so tooltip scanning misses them. A name whitelist
|
||||
-- guarantees credit whenever such a spell appears in the spellbook or is
|
||||
-- summoned by the player.
|
||||
local RA_KNOWN_MOUNTS_LIST = {
|
||||
-- Horde racial ground mounts (Vanilla)
|
||||
"Brown Wolf", "Dire Wolf", "Timber Wolf", "Red Wolf", "Winter Wolf",
|
||||
"Frostwolf", "Black Wolf", "Arctic Wolf", "Swift Brown Wolf",
|
||||
"Swift Timber Wolf", "Swift Gray Wolf", "Swift Frostwolf",
|
||||
"Black Skeletal Horse", "Blue Skeletal Horse", "Brown Skeletal Horse",
|
||||
"Red Skeletal Horse", "Green Skeletal Warhorse", "Purple Skeletal Warhorse",
|
||||
"Red Skeletal Warhorse", "Grey Kodo", "Brown Kodo", "Green Kodo",
|
||||
"Teal Kodo", "Great White Kodo", "Great Grey Kodo", "Great Brown Kodo",
|
||||
"Great Green Kodo", "Ivory Raptor", "Emerald Raptor", "Turquoise Raptor",
|
||||
"Violet Raptor", "Obsidian Raptor", "Swift Blue Raptor", "Swift Olive Raptor",
|
||||
"Swift Orange Raptor", "Whistle of the Emerald Raptor",
|
||||
"Whistle of the Ivory Raptor", "Whistle of the Turquoise Raptor",
|
||||
"Whistle of the Violet Raptor", "Whistle of the Mottled Red Raptor",
|
||||
-- Alliance racial ground mounts (Vanilla)
|
||||
"Pinto", "Palomino", "Chestnut Mare", "Brown Horse", "White Stallion",
|
||||
"Black Stallion", "Swift Brown Steed", "Swift Palomino", "Swift White Steed",
|
||||
"Gray Ram", "Brown Ram", "White Ram", "Black Ram", "Frost Ram",
|
||||
"Swift Gray Ram", "Swift Brown Ram", "Swift White Ram",
|
||||
"Mechanostrider", "Blue Mechanostrider", "Green Mechanostrider",
|
||||
"Red Mechanostrider", "White Mechanostrider", "Yellow Mechanostrider",
|
||||
"Unpainted Mechanostrider", "Swift Green Mechanostrider",
|
||||
"Swift White Mechanostrider", "Swift Yellow Mechanostrider",
|
||||
"Striped Frostsaber", "Striped Nightsaber", "Spotted Frostsaber",
|
||||
"Striped Dawnsaber", "Swift Frostsaber", "Swift Mistsaber",
|
||||
"Swift Stormsaber", "Reins of the Striped Frostsaber",
|
||||
"Reins of the Striped Nightsaber", "Reins of the Spotted Frostsaber",
|
||||
-- Class mounts (Vanilla)
|
||||
"Summon Warhorse", "Summon Charger", "Summon Felsteed", "Summon Dreadsteed",
|
||||
"Warhorse", "Charger", "Felsteed", "Dreadsteed",
|
||||
-- PvP / faction / reputation / rare mounts (Vanilla)
|
||||
"Black Battlestrider", "Black War Kodo", "Black War Ram", "Black War Raptor",
|
||||
"Black War Steed", "Black War Tiger", "Black War Wolf", "Horn of the Black War Wolf",
|
||||
"Horn of the Black Wolf", "Horn of the Brown Wolf", "Horn of the Dire Wolf",
|
||||
"Horn of the Red Wolf", "Horn of the Timber Wolf", "Horn of the Arctic Wolf",
|
||||
"Horn of the Frostwolf", "Horn of the Swift Brown Wolf",
|
||||
"Horn of the Swift Gray Wolf", "Horn of the Swift Timber Wolf",
|
||||
"Rivendare's Deathcharger", "Deathcharger's Reins", "Reins of the Rivendare's Deathcharger",
|
||||
"Winterspring Frostsaber", "Reins of the Winterspring Frostsaber",
|
||||
"Swift Zulian Tiger", "Reins of the Swift Zulian Tiger",
|
||||
"Swift Razzashi Raptor", "Reins of the Swift Razzashi Raptor",
|
||||
"Qiraji Battle Tank", "Reins of the Blue Qiraji Battle Tank",
|
||||
"Reins of the Green Qiraji Battle Tank", "Reins of the Red Qiraji Battle Tank",
|
||||
"Reins of the Yellow Qiraji Battle Tank",
|
||||
"Tawny Sabercat", "Reins of the Tawny Sabercat",
|
||||
"Cenarion War Hippogryph", "Reins of the Cenarion War Hippogryph",
|
||||
-- Turtle WoW / OctoWoW custom mounts (community-known)
|
||||
"Zebra", "Reins of the Zebra", "Whistle of the Zebra",
|
||||
"Zhevra", "Reins of the Zhevra",
|
||||
"Silver Riding Turtle", "Reins of the Silver Riding Turtle",
|
||||
"Riding Turtle", "Reins of the Riding Turtle",
|
||||
"Sea Turtle", "Reins of the Sea Turtle",
|
||||
"Brown Ostrich", "White Ostrich", "Black Ostrich", "Reins of the Ostrich",
|
||||
"Swift Ostrich", "Reins of the Swift Ostrich",
|
||||
"Camel", "Brown Camel", "Tan Camel", "White Camel", "Reins of the Camel",
|
||||
"Swift Camel", "Reins of the Swift Camel",
|
||||
"Reindeer", "Winter Reindeer", "Reins of the Reindeer",
|
||||
"Great Elk", "Reins of the Great Elk",
|
||||
"Moose", "Reins of the Moose", "Swift Moose",
|
||||
"Boar", "War Boar", "Reins of the War Boar", "Swift War Boar",
|
||||
"Bear Mount", "Riding Bear", "Reins of the Riding Bear",
|
||||
"Swift Bear", "Polar Bear", "Reins of the Polar Bear",
|
||||
"Cheetah", "Reins of the Cheetah", "Swift Cheetah",
|
||||
"Lion", "White Lion", "Reins of the White Lion",
|
||||
"Steam Tank Mount", "Steamscale", "Mechano-Hog",
|
||||
"Goblin Trike", "Trike", "Reins of the Trike",
|
||||
"Snow Leopard", "Reins of the Snow Leopard",
|
||||
"Ochre Skeletal Warhorse", "Ancona Chicken Mount",
|
||||
"Turtle Mount", "Great Sea Turtle",
|
||||
"Black Panther", "Reins of the Black Panther",
|
||||
"Giraffe", "Reins of the Giraffe",
|
||||
"Hyena", "Reins of the Hyena",
|
||||
"Ram of Ironforge", "Kodo of Thunder Bluff",
|
||||
}
|
||||
local RA_KNOWN_MOUNTS_SET = {}
|
||||
for _, n in ipairs(RA_KNOWN_MOUNTS_LIST) do
|
||||
RA_KNOWN_MOUNTS_SET[string.lower(n)] = true
|
||||
end
|
||||
|
||||
function RA_IsKnownMountName(name)
|
||||
if type(name) ~= "string" or name == "" then return false end
|
||||
return RA_KNOWN_MOUNTS_SET[string.lower(name)] == true
|
||||
end
|
||||
|
||||
function RA_AddKnownMount(name)
|
||||
if type(name) == "string" and name ~= "" then
|
||||
RA_KNOWN_MOUNTS_SET[string.lower(name)] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Persistent per-character store so name-detected mounts survive reloads and
|
||||
-- merge with tooltip-detected ones without double counting.
|
||||
function RA_CreditMountByName(name)
|
||||
if not RA_IsKnownMountName(name) then return end
|
||||
if not RelationshipsAchievementsCharDB then return end
|
||||
if not RelationshipsAchievementsCharDB.countedMounts then
|
||||
RelationshipsAchievementsCharDB.countedMounts = {}
|
||||
end
|
||||
RelationshipsAchievementsCharDB.countedMounts[string.lower(name)] = true
|
||||
end
|
||||
|
||||
local function CountStoredMounts()
|
||||
if not RelationshipsAchievementsCharDB
|
||||
or not RelationshipsAchievementsCharDB.countedMounts then
|
||||
return 0
|
||||
end
|
||||
local n = 0
|
||||
for _ in pairs(RelationshipsAchievementsCharDB.countedMounts) do n = n + 1 end
|
||||
return n
|
||||
end
|
||||
|
||||
local function ScanSpellbook()
|
||||
if not GetSpellName then return end
|
||||
local book = BOOKTYPE_SPELL or "spell"
|
||||
@@ -194,19 +436,48 @@ local function ScanSpellbook()
|
||||
-- Non-combat pets: tooltip contains "right click to dismiss"
|
||||
-- or the classic "companion" descriptor. Explicitly exclude
|
||||
-- combat pets (hunter/warlock), which say "summons your pet".
|
||||
local isPet = (string.find(tip, "companion", 1, true)
|
||||
or string.find(tip, "right click to dismiss", 1, true))
|
||||
and not string.find(tip, "summons your", 1, true)
|
||||
-- Non-combat pets: several tooltip phrasings exist across
|
||||
-- Vanilla / TurtleWoW / OctoWoW. Match any of them, then filter
|
||||
-- out combat pets (hunter/warlock) which say "summons your pet".
|
||||
local isPet = (
|
||||
string.find(tip, "companion", 1, true)
|
||||
or string.find(tip, "right click to dismiss", 1, true)
|
||||
or string.find(tip, "small pet", 1, true)
|
||||
or string.find(tip, "critter", 1, true)
|
||||
or string.find(tip, "summons and dismisses a", 1, true)
|
||||
or string.find(tip, "summons and dismisses your", 1, true)
|
||||
or string.find(tip, "summon a small", 1, true)
|
||||
or string.find(tip, "vanity pet", 1, true)
|
||||
or string.find(tip, "non-combat pet", 1, true)
|
||||
)
|
||||
and not string.find(tip, "summons your pet", 1, true)
|
||||
and not string.find(tip, "combat pet", 1, true)
|
||||
and not isMount
|
||||
if isMount then
|
||||
mounts = mounts + 1
|
||||
-- Epic ground mounts grant +100% speed in vanilla.
|
||||
if string.find(tip, "100%", 1, true) or string.find(tip, "epic", 1, true) then
|
||||
hasEpicMount = true
|
||||
end
|
||||
if RA_CreditMountByName then RA_CreditMountByName(name) end
|
||||
elseif isPet then
|
||||
pets = pets + 1
|
||||
end
|
||||
-- Name-based fast path: known vanilla + Turtle/Octo companions.
|
||||
-- Ensures custom pets whose tooltips don't match keywords still count.
|
||||
if (not isPet) and (not isMount)
|
||||
and RA_IsKnownCompanionName and RA_IsKnownCompanionName(name) then
|
||||
pets = pets + 1
|
||||
if RA_CreditCompanionByName then RA_CreditCompanionByName(name) end
|
||||
end
|
||||
-- Name-based fast path: known vanilla + Turtle/Octo mounts.
|
||||
-- Catches custom mounts (e.g. Zebra) whose tooltips omit the
|
||||
-- classic "mount speed" / "rideable" keywords.
|
||||
if (not isMount) and (not isPet)
|
||||
and RA_IsKnownMountName and RA_IsKnownMountName(name) then
|
||||
mounts = mounts + 1
|
||||
if RA_CreditMountByName then RA_CreditMountByName(name) end
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
@@ -223,6 +494,12 @@ local function ScanSpellbook()
|
||||
end
|
||||
end
|
||||
|
||||
-- Merge with the persistent name-detected mount store so counts never
|
||||
-- regress if a mount was seen previously but is currently unavailable
|
||||
-- from the spellbook scan (e.g. server API hiccup).
|
||||
local storedMounts = CountStoredMounts()
|
||||
if storedMounts > mounts then mounts = storedMounts end
|
||||
|
||||
RelationshipsAchievements:UpdateStat("mountCount", mounts)
|
||||
RelationshipsAchievements:UpdateStat("petCount", pets)
|
||||
if mounts >= 1 then RelationshipsAchievements:Unlock(211) end
|
||||
@@ -232,6 +509,213 @@ local function ScanSpellbook()
|
||||
if pets >= 1 then RelationshipsAchievements:Unlock(1204) end
|
||||
end
|
||||
|
||||
-- Expose so slash command / minimap can force a rescan.
|
||||
RelationshipsAchievements_ScanSpellbook = ScanSpellbook
|
||||
|
||||
-- ============ Companion API scanning (OctoWoW / Turtle) ============
|
||||
-- OctoWoW's Pet menu ("P") has a Companions tab powered by the backported
|
||||
-- Companion API (GetNumCompanions/GetCompanionInfo/CallCompanion). This is
|
||||
-- the authoritative source for non-combat pet ownership on those clients
|
||||
-- and is far more reliable than tooltip scanning the spellbook.
|
||||
local function EnsureCountedCompanions()
|
||||
if not RelationshipsAchievementsCharDB then return nil end
|
||||
if not RelationshipsAchievementsCharDB.countedCompanions then
|
||||
RelationshipsAchievementsCharDB.countedCompanions = {}
|
||||
end
|
||||
return RelationshipsAchievementsCharDB.countedCompanions
|
||||
end
|
||||
|
||||
local function CountTable(t)
|
||||
local n = 0
|
||||
if t then for _ in pairs(t) do n = n + 1 end end
|
||||
return n
|
||||
end
|
||||
|
||||
local function ScanCompanions()
|
||||
if not GetNumCompanions then return end
|
||||
local counted = EnsureCountedCompanions()
|
||||
if not counted then return end
|
||||
-- Different builds/servers register the companion tab under different
|
||||
-- type strings. Try each so we always sync with the "P" menu Companions
|
||||
-- tab regardless of which naming OctoWoW / Turtle uses.
|
||||
local types = { "CRITTER", "COMPANION", "COMPANIONS", "PET", "MINIPET" }
|
||||
for _, ctype in ipairs(types) do
|
||||
local ok, num = pcall(GetNumCompanions, ctype)
|
||||
if ok and num and num > 0 then
|
||||
for i = 1, num do
|
||||
local ok2, _, name = pcall(GetCompanionInfo, ctype, i)
|
||||
if ok2 and name and name ~= "" then
|
||||
counted[name] = true
|
||||
else
|
||||
counted[ctype.."_"..i] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local total = CountTable(counted)
|
||||
-- Merge with tooltip-scanned pets so we always report the higher value.
|
||||
local spellPets = (RelationshipsAchievementsCharDB.stats and RelationshipsAchievementsCharDB.stats.petCount) or 0
|
||||
if total < spellPets then total = spellPets end
|
||||
RelationshipsAchievements:UpdateStat("petCount", total)
|
||||
if total >= 1 then RelationshipsAchievements:Unlock(1204) end
|
||||
if total >= 10 then RelationshipsAchievements:Unlock(1205) end
|
||||
end
|
||||
RelationshipsAchievements_ScanCompanions = ScanCompanions
|
||||
|
||||
-- Backup path: hook CallCompanion so a summon always credits the pet, even
|
||||
-- if the addon loaded before the Companion API populated.
|
||||
if CallCompanion then
|
||||
local _origCallCompanion = CallCompanion
|
||||
CallCompanion = function(ctype, index)
|
||||
_origCallCompanion(ctype, index)
|
||||
if ctype == "CRITTER" then
|
||||
local counted = EnsureCountedCompanions()
|
||||
if counted then
|
||||
local ok, _, name = pcall(GetCompanionInfo, "CRITTER", index)
|
||||
local key = (ok and name and name ~= "") and name or ("companion_"..index)
|
||||
counted[key] = true
|
||||
local n = CountTable(counted)
|
||||
RelationshipsAchievements:UpdateStat("petCount", n)
|
||||
RelationshipsAchievements:Unlock(1204)
|
||||
if n >= 10 then RelationshipsAchievements:Unlock(1205) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Backup pet-count tracking: if a player casts a spell whose tooltip matches
|
||||
-- pet criteria, ensure it counts. This catches Turtle/Octo custom companions
|
||||
-- whose tooltips ScanSpellbook may not have visited yet.
|
||||
local function EnsureCountedPets()
|
||||
if not RelationshipsAchievementsCharDB then return nil end
|
||||
if not RelationshipsAchievementsCharDB.countedPets then
|
||||
RelationshipsAchievementsCharDB.countedPets = {}
|
||||
end
|
||||
return RelationshipsAchievementsCharDB.countedPets
|
||||
end
|
||||
|
||||
local function TooltipTextByName(spellName)
|
||||
if not spellName or spellName == "" then return "" end
|
||||
RA_ScanTip:ClearLines()
|
||||
-- SetSpell by index only; iterate spellbook once to find the name.
|
||||
if not GetSpellName then return "" end
|
||||
local book = BOOKTYPE_SPELL or "spell"
|
||||
local i = 1
|
||||
while true do
|
||||
local name = GetSpellName(i, book)
|
||||
if not name then break end
|
||||
if name == spellName then
|
||||
return TooltipText(i, book)
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function CreditPetSpell(spellName)
|
||||
if not spellName or spellName == "" then return end
|
||||
local counted = EnsureCountedPets()
|
||||
if not counted then return end
|
||||
if counted[spellName] then return end
|
||||
-- Known-companion fast path (no tooltip required).
|
||||
if RA_IsKnownCompanionName and RA_IsKnownCompanionName(spellName) then
|
||||
counted[spellName] = true
|
||||
local n = 0
|
||||
for _ in pairs(counted) do n = n + 1 end
|
||||
local companionPets = 0
|
||||
if RelationshipsAchievementsCharDB and RelationshipsAchievementsCharDB.countedCompanions then
|
||||
for _ in pairs(RelationshipsAchievementsCharDB.countedCompanions) do
|
||||
companionPets = companionPets + 1
|
||||
end
|
||||
end
|
||||
local total = n
|
||||
if companionPets > total then total = companionPets end
|
||||
RelationshipsAchievements:UpdateStat("petCount", total)
|
||||
RelationshipsAchievements:Unlock(1204)
|
||||
if total >= 10 then RelationshipsAchievements:Unlock(1205) end
|
||||
return
|
||||
end
|
||||
local tip = TooltipTextByName(spellName)
|
||||
if tip == "" then return end
|
||||
local isPet = (
|
||||
string.find(tip, "companion", 1, true)
|
||||
or string.find(tip, "right click to dismiss", 1, true)
|
||||
or string.find(tip, "small pet", 1, true)
|
||||
or string.find(tip, "critter", 1, true)
|
||||
or string.find(tip, "summons and dismisses a", 1, true)
|
||||
or string.find(tip, "summons and dismisses your", 1, true)
|
||||
or string.find(tip, "vanity pet", 1, true)
|
||||
or string.find(tip, "non-combat pet", 1, true)
|
||||
)
|
||||
and not string.find(tip, "summons your pet", 1, true)
|
||||
and not string.find(tip, "mount speed", 1, true)
|
||||
and not string.find(tip, "rideable", 1, true)
|
||||
if isPet then
|
||||
counted[spellName] = true
|
||||
local n = 0
|
||||
for _ in pairs(counted) do n = n + 1 end
|
||||
RelationshipsAchievements:UpdateStat("petCount", n)
|
||||
RelationshipsAchievements:Unlock(1204)
|
||||
end
|
||||
end
|
||||
|
||||
-- Credit a mount cast by name. Mirrors CreditPetSpell but for mounts,
|
||||
-- using the known-mount whitelist so custom Turtle/OctoWoW mounts (e.g.
|
||||
-- "Zebra") count the moment the player summons them, even if the client's
|
||||
-- spellbook scan hadn't picked them up yet.
|
||||
local function CreditMountSpell(spellName)
|
||||
if not spellName or spellName == "" then return end
|
||||
if not RA_IsKnownMountName or not RA_IsKnownMountName(spellName) then return end
|
||||
if not RelationshipsAchievementsCharDB then return end
|
||||
if not RelationshipsAchievementsCharDB.countedMounts then
|
||||
RelationshipsAchievementsCharDB.countedMounts = {}
|
||||
end
|
||||
local key = string.lower(spellName)
|
||||
if RelationshipsAchievementsCharDB.countedMounts[key] then return end
|
||||
RelationshipsAchievementsCharDB.countedMounts[key] = true
|
||||
local n = 0
|
||||
for _ in pairs(RelationshipsAchievementsCharDB.countedMounts) do n = n + 1 end
|
||||
-- Never regress the stat: prefer whichever count is higher.
|
||||
local prev = 0
|
||||
if RelationshipsAchievementsCharDB.stats
|
||||
and RelationshipsAchievementsCharDB.stats.mountCount then
|
||||
prev = RelationshipsAchievementsCharDB.stats.mountCount
|
||||
end
|
||||
if n < prev then n = prev end
|
||||
RelationshipsAchievements:UpdateStat("mountCount", n)
|
||||
RelationshipsAchievements:Unlock(211)
|
||||
end
|
||||
|
||||
local _origCastSpellByName = CastSpellByName
|
||||
if _origCastSpellByName then CastSpellByName = function(name, onSelf)
|
||||
_origCastSpellByName(name, onSelf)
|
||||
CreditPetSpell(name)
|
||||
CreditMountSpell(name)
|
||||
end end
|
||||
|
||||
local _origCastSpell = CastSpell
|
||||
if _origCastSpell then CastSpell = function(spellId, bookType)
|
||||
_origCastSpell(spellId, bookType)
|
||||
if GetSpellName then
|
||||
local n = GetSpellName(spellId, bookType or BOOKTYPE_SPELL or "spell")
|
||||
CreditPetSpell(n)
|
||||
CreditMountSpell(n)
|
||||
end
|
||||
end end
|
||||
|
||||
local _origUseAction = UseAction
|
||||
if _origUseAction then UseAction = function(slot, checkCursor, onSelf)
|
||||
_origUseAction(slot, checkCursor, onSelf)
|
||||
if GetActionText then
|
||||
local txt = GetActionText(slot)
|
||||
if txt then
|
||||
CreditPetSpell(txt)
|
||||
CreditMountSpell(txt)
|
||||
end
|
||||
end
|
||||
end end
|
||||
|
||||
|
||||
function RelationshipsAchievements_ScanCharacter()
|
||||
RelationshipsAchievements:Init()
|
||||
|
||||
@@ -374,6 +858,9 @@ function RelationshipsAchievements_ScanCharacter()
|
||||
ScanTalents()
|
||||
ScanEquipmentAndBags()
|
||||
ScanSpellbook()
|
||||
if RelationshipsAchievements_ScanCompanions then
|
||||
RelationshipsAchievements_ScanCompanions()
|
||||
end
|
||||
|
||||
RelationshipsAchievements_RefreshUI = RelationshipsAchievements_RefreshUI or function() end
|
||||
if RelationshipsAchievementFrame and RelationshipsAchievementFrame:IsVisible() then
|
||||
@@ -642,8 +1129,31 @@ local function OnEnemyDeath(msg)
|
||||
end
|
||||
end
|
||||
|
||||
-- Match Turtle/Octo /played extension lines that expose the character's
|
||||
-- completed-quest total. Multiple phrasings observed across custom builds.
|
||||
local function TryParseQuestCount(msg)
|
||||
if not msg then return end
|
||||
local patterns = {
|
||||
"[Qq]uests? [Cc]ompleted:?%s*(%d+)",
|
||||
"[Cc]ompleted [Qq]uests?:?%s*(%d+)",
|
||||
"[Tt]otal [Qq]uests?:?%s*(%d+)",
|
||||
"[Qq]uests?[^%d]-(%d+)%s*completed",
|
||||
}
|
||||
for i = 1, table.getn(patterns) do
|
||||
local _, _, num = string.find(msg, patterns[i])
|
||||
if num then
|
||||
local n = tonumber(num)
|
||||
if n and n > 0 then
|
||||
RelationshipsAchievements:UpdateStat("questCount", n)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnSystemMessage(msg)
|
||||
if not msg then return end
|
||||
TryParseQuestCount(msg)
|
||||
local _, _, winner = string.find(msg, "^(.-) has defeated ")
|
||||
if winner and winner == UnitName("player") then
|
||||
RelationshipsAchievements:AddStat("duelWins", 1)
|
||||
@@ -787,5 +1297,39 @@ frame:SetScript("OnEvent", function()
|
||||
RelationshipsAchievements:Unlock(103)
|
||||
if GetNumGuildMembers and GetNumGuildMembers() >= 10 then RelationshipsAchievements:Unlock(1703) end
|
||||
end
|
||||
elseif event == "TIME_PLAYED_MSG" then
|
||||
-- Some /played variants (Turtle/Octo) also emit "Quests completed: N"
|
||||
-- as separate CHAT_MSG_SYSTEM lines around this event. Rescan the
|
||||
-- spellbook too so any pets learned since last scan are counted.
|
||||
if RelationshipsAchievements_ScanSpellbook then
|
||||
RelationshipsAchievements_ScanSpellbook()
|
||||
end
|
||||
elseif event == "UNIT_PET" or event == "UNIT_MODEL_CHANGED" then
|
||||
-- A pet/companion just spawned or despawned. Rescan the spellbook so
|
||||
-- newly-learned companion spells count immediately.
|
||||
if arg1 == "player" and RelationshipsAchievements_ScanSpellbook then
|
||||
RelationshipsAchievements_ScanSpellbook()
|
||||
if RelationshipsAchievements_ScanCompanions then
|
||||
RelationshipsAchievements_ScanCompanions()
|
||||
end
|
||||
-- Name-based fallback: if the summoned pet's unit name matches a
|
||||
-- known companion, credit it directly.
|
||||
if UnitName and RA_CreditCompanionByName then
|
||||
local petName = UnitName("pet")
|
||||
if petName then RA_CreditCompanionByName(petName) end
|
||||
end
|
||||
end
|
||||
elseif event == "COMPANION_LEARNED" or event == "COMPANION_UPDATE"
|
||||
or event == "COMPANION_UNLEARNED" or event == "PET_UI_UPDATE" then
|
||||
if RelationshipsAchievements_ScanCompanions then
|
||||
RelationshipsAchievements_ScanCompanions()
|
||||
end
|
||||
elseif event == "LEARNED_SPELL_IN_TAB" then
|
||||
if RelationshipsAchievements_ScanSpellbook then
|
||||
RelationshipsAchievements_ScanSpellbook()
|
||||
end
|
||||
if RelationshipsAchievements_ScanCompanions then
|
||||
RelationshipsAchievements_ScanCompanions()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user