Dateien nach "/" hochladen
This commit is contained in:
+496
@@ -0,0 +1,496 @@
|
||||
-- Freewood-OctoWoW-Codex: Zeitmessung
|
||||
-- Kampfdauer (Time to kill): wie lange dauert ein Solo-Kampf gegen einen einzelnen Gegner,
|
||||
-- je Klasse, Spielerstufe und Stufenabstand (Gegner minus Spieler, -5 bis +5).
|
||||
-- Quest-Zeit: aktive Spielzeit vom ersten Fortschritt bis zur Abgabebereitschaft, bei mehreren
|
||||
-- gleichzeitig laufenden Quests gleichmaessig aufgeteilt.
|
||||
-- Zeiten werden als ganze Zehntelsekunden gespeichert.
|
||||
--
|
||||
-- Gespeichert:
|
||||
-- db.ttk[Klasse][Spielerstufe][Abstand] = { n, sum, min, max } (Klasse zum Beispiel "WARRIOR")
|
||||
-- units[npcID].tt[Klasse][Spielerstufe] = { n, sum, min, max } (nur lokal)
|
||||
-- quests[id].tm aufgeteilte aktive Zeit bis fertig, .tr aktive Zeit bis fertig ohne Aufteilen,
|
||||
-- quests[id].ta aktive Zeit bis zur Abgabe
|
||||
-- db.qtimer[questID] = laufende Zeitnehmer (ueberleben Neustart des Spiels)
|
||||
|
||||
local ADDON, NS = ...
|
||||
|
||||
local FW = _G.Freewood
|
||||
local Codex = NS and NS.Codex
|
||||
if not FW or not Codex then
|
||||
return
|
||||
end
|
||||
|
||||
local Util = FW.Util
|
||||
|
||||
local Time = {}
|
||||
NS.Time = Time
|
||||
Codex.Time = Time
|
||||
|
||||
local MAX_DELTA = 5
|
||||
local MIN_KILL, MAX_KILL = 0.5, 120 -- Sekunden
|
||||
local KILL_END_WINDOW = 8 -- Kampf muss kurz nach dem Kill enden
|
||||
local MAX_QUEST_SECONDS = 4 * 3600
|
||||
local TICK = 1
|
||||
|
||||
Time.stats = { fights = 0, ok = 0, skip = {}, quests = 0 }
|
||||
|
||||
local function Now()
|
||||
return GetTime and GetTime() or time()
|
||||
end
|
||||
|
||||
local function Safe(fn, ...)
|
||||
local ok, err = pcall(fn, ...)
|
||||
if not ok and FW.Debug then
|
||||
FW:Debug("Codex.Time: " .. tostring(err))
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
local function Skip(reason)
|
||||
Time.stats.skip[reason] = (Time.stats.skip[reason] or 0) + 1
|
||||
end
|
||||
|
||||
local function Sample(tbl, key, value)
|
||||
local s = tbl[key]
|
||||
if not s then
|
||||
s = { n = 0, sum = 0 }
|
||||
tbl[key] = s
|
||||
end
|
||||
s.n = s.n + 1
|
||||
s.sum = s.sum + value
|
||||
s.min = s.min and math.min(s.min, value) or value
|
||||
s.max = s.max and math.max(s.max, value) or value
|
||||
return s
|
||||
end
|
||||
|
||||
function Time.PlayerToken()
|
||||
local token
|
||||
if UnitClass then
|
||||
local _, t = UnitClass("player")
|
||||
token = t
|
||||
end
|
||||
if type(token) == "string" and token:match("^%u+$") then
|
||||
return token
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Klassenname des Spielers in der Spielsprache (fuer Anzeigen)
|
||||
function Time.ClassName()
|
||||
if UnitClass then
|
||||
local name = UnitClass("player")
|
||||
if type(name) == "string" and name ~= "" then
|
||||
return name
|
||||
end
|
||||
end
|
||||
return Time.PlayerToken() or "?"
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Kampfdauer
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local fight = nil
|
||||
|
||||
local function InGroup()
|
||||
return (IsInGroup and IsInGroup()) or (GetNumGroupMembers and GetNumGroupMembers() > 1) or false
|
||||
end
|
||||
|
||||
local function InInstance()
|
||||
if IsInInstance then
|
||||
local inside = IsInInstance()
|
||||
return inside and true or false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Time:NoteTarget()
|
||||
if not fight or not fight.ok then
|
||||
return
|
||||
end
|
||||
if not (UnitExists and UnitExists("target")) then
|
||||
return
|
||||
end
|
||||
if UnitCanAttack and not UnitCanAttack("player", "target") then
|
||||
return
|
||||
end
|
||||
local guid = UnitGUID and UnitGUID("target")
|
||||
if guid == nil or Util.IsSecret(guid) then
|
||||
fight.ok, fight.why = false, "secret"
|
||||
return
|
||||
end
|
||||
if not fight.targets[guid] then
|
||||
fight.targets[guid] = true
|
||||
fight.nTargets = fight.nTargets + 1
|
||||
if fight.nTargets > 1 then
|
||||
fight.ok, fight.why = false, "multi"
|
||||
return
|
||||
end
|
||||
local classification = UnitClassification and UnitClassification("target")
|
||||
if classification and classification ~= "normal" then
|
||||
fight.ok, fight.why = false, "elite"
|
||||
end
|
||||
local level = UnitLevel and tonumber(UnitLevel("target"))
|
||||
if level and level > 0 then
|
||||
fight.tl = level -- Stufe des Gegners, falls der Erfahrungs-Hook sie nicht kennt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Time:OnRegenDisabled()
|
||||
Safe(function()
|
||||
fight = { start = Now(), targets = {}, nTargets = 0, kills = 0, ok = true }
|
||||
if InGroup() then
|
||||
fight.ok, fight.why = false, "group"
|
||||
elseif InInstance() then
|
||||
fight.ok, fight.why = false, "instance"
|
||||
end
|
||||
Time:NoteTarget()
|
||||
end)
|
||||
end
|
||||
|
||||
function Time:OnTargetChanged()
|
||||
Safe(Time.NoteTarget, Time)
|
||||
end
|
||||
|
||||
-- vom Erfahrungs-Hook aufgerufen: ein Gegner ist gestorben (pl, ml: Stufen, id: NPC-ID oder nil)
|
||||
function Time:OnKill(pl, ml, id, classification)
|
||||
if not fight then
|
||||
return
|
||||
end
|
||||
fight.kills = fight.kills + 1
|
||||
fight.killAt = Now()
|
||||
fight.pl, fight.ml, fight.id = pl, ml or fight.ml, id or fight.id
|
||||
if classification and classification ~= "normal" and fight.ok then
|
||||
fight.ok, fight.why = false, "elite"
|
||||
end
|
||||
end
|
||||
|
||||
function Time:Record(token, pl, delta, seconds, id)
|
||||
local ds = math.floor(seconds * 10 + 0.5)
|
||||
local db = Codex.db
|
||||
db.ttk = db.ttk or {}
|
||||
db.ttk[token] = db.ttk[token] or {}
|
||||
db.ttk[token][pl] = db.ttk[token][pl] or {}
|
||||
Sample(db.ttk[token][pl], delta, ds)
|
||||
if id then
|
||||
local u = Codex:Unit(id, true)
|
||||
u.tt = u.tt or {}
|
||||
u.tt[token] = u.tt[token] or {}
|
||||
Sample(u.tt[token], pl, ds)
|
||||
end
|
||||
FW:Fire("CODEX_CHANGED", "ttk", pl)
|
||||
end
|
||||
|
||||
function Time:OnRegenEnabled()
|
||||
Safe(function()
|
||||
local f = fight
|
||||
fight = nil
|
||||
if not f then
|
||||
return
|
||||
end
|
||||
local st = Time.stats
|
||||
st.fights = st.fights + 1
|
||||
if not f.ok then
|
||||
return Skip(f.why or "other")
|
||||
end
|
||||
if f.kills ~= 1 then
|
||||
return Skip(f.kills == 0 and "nokill" or "kills")
|
||||
end
|
||||
local seconds = f.killAt - f.start
|
||||
if seconds < MIN_KILL then
|
||||
return Skip("short")
|
||||
end
|
||||
if seconds > MAX_KILL then
|
||||
return Skip("long")
|
||||
end
|
||||
if Now() - f.killAt > KILL_END_WINDOW then
|
||||
return Skip("late")
|
||||
end
|
||||
local pl = f.pl or (UnitLevel and tonumber(UnitLevel("player")))
|
||||
local ml = f.ml or f.tl
|
||||
local delta = ml and pl and (ml - pl)
|
||||
if not delta or math.abs(delta) > MAX_DELTA then
|
||||
return Skip("range")
|
||||
end
|
||||
local token = Time.PlayerToken()
|
||||
if not token then
|
||||
return Skip("class")
|
||||
end
|
||||
st.ok = st.ok + 1
|
||||
Time:Record(token, pl, delta, seconds, f.id)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Klassenname zu einem Klassenschluessel ("WARRIOR" -> "Krieger"), sonst der Schluessel selbst
|
||||
function Time.ClassLabel(token)
|
||||
local names = _G.LOCALIZED_CLASS_NAMES_MALE
|
||||
if type(names) == "table" and type(names[token]) == "string" then
|
||||
return names[token]
|
||||
end
|
||||
return tostring(token)
|
||||
end
|
||||
|
||||
-- Kampfdauer einer Kreatur je Klasse (eigene und geteilte Messungen):
|
||||
-- Liste { cls, secs, n, own } nach Dauer sortiert, Durchschnitt ueber alle Spielerstufen
|
||||
function Time.UnitClasses(u)
|
||||
local list = {}
|
||||
local mine = Time.PlayerToken()
|
||||
for cls, byPl in pairs(u and u.tt or {}) do
|
||||
local n, sum = 0, 0
|
||||
for _, s in pairs(byPl) do
|
||||
n = n + (s.n or 0)
|
||||
sum = sum + (s.sum or 0)
|
||||
end
|
||||
if n > 0 then
|
||||
list[#list + 1] = { cls = cls, secs = sum / n / 10, n = n, own = cls == mine }
|
||||
end
|
||||
end
|
||||
table.sort(list, function(a, b)
|
||||
if a.secs ~= b.secs then return a.secs < b.secs end
|
||||
return a.cls < b.cls
|
||||
end)
|
||||
return list
|
||||
end
|
||||
|
||||
-- Durchschnittliche Kampfdauer (Sekunden, Anzahl) einer Kreatur fuer die eigene Klasse
|
||||
function Time.UnitAverage(u)
|
||||
local token = Time.PlayerToken()
|
||||
local byPl = u and u.tt and token and u.tt[token]
|
||||
if not byPl then
|
||||
return nil
|
||||
end
|
||||
local n, sum = 0, 0
|
||||
for _, s in pairs(byPl) do
|
||||
n = n + (s.n or 0)
|
||||
sum = sum + (s.sum or 0)
|
||||
end
|
||||
if n < 1 then
|
||||
return nil
|
||||
end
|
||||
return sum / n / 10, n
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Quest-Zeit
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local lastTick = nil
|
||||
local ticker = nil
|
||||
|
||||
local function QTimers()
|
||||
Codex.db.qtimer = Codex.db.qtimer or {}
|
||||
return Codex.db.qtimer
|
||||
end
|
||||
|
||||
local function LogEntries()
|
||||
local list = {}
|
||||
local count
|
||||
if C_QuestLog and C_QuestLog.GetNumQuestLogEntries then
|
||||
count = C_QuestLog.GetNumQuestLogEntries()
|
||||
elseif GetNumQuestLogEntries then
|
||||
count = GetNumQuestLogEntries()
|
||||
end
|
||||
for i = 1, tonumber(count) or 0 do
|
||||
local questID, isHeader
|
||||
if C_QuestLog and C_QuestLog.GetInfo then
|
||||
local info = C_QuestLog.GetInfo(i)
|
||||
if type(info) == "table" then
|
||||
questID, isHeader = info.questID, info.isHeader
|
||||
end
|
||||
elseif GetQuestLogTitle then
|
||||
local _, _, _, h, _, _, _, id = GetQuestLogTitle(i)
|
||||
questID, isHeader = id, h
|
||||
end
|
||||
if type(questID) == "number" and questID > 0 and not isHeader then
|
||||
list[#list + 1] = questID
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
-- Fortschritt (Summe der erfuellten Ziele) und ob alles erledigt ist
|
||||
local function Progress(questID)
|
||||
local have, finished, count = 0, true, 0
|
||||
if C_QuestLog and C_QuestLog.GetQuestObjectives then
|
||||
local objs = C_QuestLog.GetQuestObjectives(questID)
|
||||
if type(objs) == "table" then
|
||||
for _, o in ipairs(objs) do
|
||||
if type(o) == "table" then
|
||||
count = count + 1
|
||||
have = have + (tonumber(o.numFulfilled) or 0)
|
||||
if not o.finished then finished = false end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if count == 0 then
|
||||
finished = false
|
||||
end
|
||||
if not finished and C_QuestLog and C_QuestLog.IsComplete then
|
||||
local ok, done = pcall(C_QuestLog.IsComplete, questID)
|
||||
if ok and done and count > 0 then finished = true end
|
||||
end
|
||||
return have, finished
|
||||
end
|
||||
|
||||
function Time:ScanQuests()
|
||||
Safe(function()
|
||||
local timers = QTimers()
|
||||
local seen = {}
|
||||
for _, questID in ipairs(LogEntries()) do
|
||||
seen[questID] = true
|
||||
local have, finished = Progress(questID)
|
||||
local t = timers[questID]
|
||||
if not t then
|
||||
-- neu im Log: nur ohne vorhandenen Fortschritt kennen wir den Beginn
|
||||
t = { eff = 0, raw = 0, all = 0, have = have, started = false, skip = have > 0 or finished }
|
||||
timers[questID] = t
|
||||
end
|
||||
if not t.skip then
|
||||
if have > (t.have or 0) and not t.started then
|
||||
t.started = true
|
||||
end
|
||||
if finished and t.started and not t.done then
|
||||
t.done = true
|
||||
end
|
||||
end
|
||||
t.have = have
|
||||
end
|
||||
for questID, t in pairs(timers) do
|
||||
if not seen[questID] then
|
||||
if t.done then
|
||||
-- fertige Quests kurz behalten: die Abgabe kann nach dem Verlassen des Logs eintreffen
|
||||
t.goneAt = t.goneAt or Now()
|
||||
if Now() - t.goneAt > 60 then timers[questID] = nil end
|
||||
else
|
||||
timers[questID] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function Time:Tick()
|
||||
Safe(function()
|
||||
local now = Now()
|
||||
local dt = lastTick and (now - lastTick) or 0
|
||||
lastTick = now
|
||||
if dt <= 0 or dt > 3 then
|
||||
return
|
||||
end
|
||||
if UnitIsAFK and UnitIsAFK("player") then
|
||||
return
|
||||
end
|
||||
local timers = QTimers()
|
||||
local active = 0
|
||||
for _, t in pairs(timers) do
|
||||
if t.started and not t.done and not t.skip then
|
||||
active = active + 1
|
||||
end
|
||||
end
|
||||
for _, t in pairs(timers) do
|
||||
if t.started and not t.skip then
|
||||
t.all = (t.all or 0) + dt
|
||||
if not t.done then
|
||||
t.eff = t.eff + dt / math.max(1, active)
|
||||
t.raw = t.raw + dt
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Quest abgegeben: Zeiten in die Quest schreiben
|
||||
function Time:OnTurnedIn(questID)
|
||||
Safe(function()
|
||||
questID = tonumber(questID)
|
||||
local t = questID and QTimers()[questID]
|
||||
if not t or t.skip or not t.started then
|
||||
if questID then QTimers()[questID] = nil end
|
||||
return
|
||||
end
|
||||
QTimers()[questID] = nil
|
||||
if not t.done or t.raw < 1 or t.all > MAX_QUEST_SECONDS then
|
||||
return
|
||||
end
|
||||
local q = Codex:Quest(questID, true)
|
||||
local function Add(field, seconds)
|
||||
local ds = math.floor(seconds * 10 + 0.5)
|
||||
q[field] = q[field] or { n = 0, sum = 0 }
|
||||
local s = q[field]
|
||||
s.n = s.n + 1
|
||||
s.sum = s.sum + ds
|
||||
s.min = s.min and math.min(s.min, ds) or ds
|
||||
s.max = s.max and math.max(s.max, ds) or ds
|
||||
end
|
||||
Add("tm", t.eff)
|
||||
Add("tr", t.raw)
|
||||
Add("ta", t.all)
|
||||
Time.stats.quests = Time.stats.quests + 1
|
||||
FW:Fire("CODEX_CHANGED", "quest", questID)
|
||||
end)
|
||||
end
|
||||
|
||||
function Time:OnQuestLog()
|
||||
if self.scanPending then
|
||||
return
|
||||
end
|
||||
self.scanPending = true
|
||||
C_Timer.After(0.5, function()
|
||||
Time.scanPending = nil
|
||||
Time:ScanQuests()
|
||||
end)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Start / Stop
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function Time:Start()
|
||||
FW:RegisterEvent("PLAYER_REGEN_DISABLED", self.OnRegenDisabled, self)
|
||||
FW:RegisterEvent("PLAYER_REGEN_ENABLED", self.OnRegenEnabled, self)
|
||||
FW:RegisterEvent("PLAYER_TARGET_CHANGED", self.OnTargetChanged, self)
|
||||
FW:RegisterEvent("QUEST_LOG_UPDATE", self.OnQuestLog, self)
|
||||
FW:RegisterEvent("UNIT_QUEST_LOG_CHANGED", self.OnQuestLog, self)
|
||||
FW:RegisterEvent("QUEST_ACCEPTED", self.OnQuestLog, self)
|
||||
FW:RegisterEvent("QUEST_TURNED_IN", self.OnTurnedIn, self)
|
||||
if not ticker and C_Timer and C_Timer.NewTicker then
|
||||
lastTick = Now()
|
||||
ticker = C_Timer.NewTicker(TICK, function() Time:Tick() end)
|
||||
end
|
||||
C_Timer.After(4, function() Time:ScanQuests() end)
|
||||
end
|
||||
|
||||
function Time:Stop()
|
||||
for _, event in ipairs({ "PLAYER_REGEN_DISABLED", "PLAYER_REGEN_ENABLED", "PLAYER_TARGET_CHANGED",
|
||||
"QUEST_LOG_UPDATE", "UNIT_QUEST_LOG_CHANGED", "QUEST_ACCEPTED", "QUEST_TURNED_IN" }) do
|
||||
FW:UnregisterEvent(event, self)
|
||||
end
|
||||
if ticker and ticker.Cancel then
|
||||
ticker:Cancel()
|
||||
end
|
||||
ticker = nil
|
||||
fight = nil
|
||||
end
|
||||
|
||||
function Time:Debug()
|
||||
local st = self.stats
|
||||
local n = 0
|
||||
for _, byPl in pairs(Codex.db.ttk or {}) do
|
||||
for _, byDelta in pairs(byPl) do
|
||||
for _ in pairs(byDelta) do n = n + 1 end
|
||||
end
|
||||
end
|
||||
local reasons = {}
|
||||
for k, v in pairs(st.skip) do reasons[#reasons + 1] = k .. "=" .. v end
|
||||
table.sort(reasons)
|
||||
local active, running = 0, 0
|
||||
for _, t in pairs(QTimers()) do
|
||||
running = running + 1
|
||||
if t.started and not t.done and not t.skip then active = active + 1 end
|
||||
end
|
||||
FW:Print(string.format("Zeit: Kaempfe %d, gewertet %d, Messreihen %d, uebersprungen: %s", st.fights, st.ok, n,
|
||||
#reasons > 0 and table.concat(reasons, " ") or "-"))
|
||||
FW:Print(string.format("Quest-Zeit: Zeitnehmer %d, davon aktiv %d, gespeicherte Abgaben %d", running, active, st.quests))
|
||||
end
|
||||
+33
-11
@@ -353,10 +353,12 @@ local function ItemIndex()
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Items aus den Startdaten, deren Itemdaten schon geladen sind (sonst nur Zahlen ohne Namen)
|
||||
-- Items aus den Startdaten, deren Name bekannt ist: aus dem Client oder aus dem mitgelieferten Katalog
|
||||
-- (Name, Stufe, Preis ohne Serverabfrage). Unbekannte erscheinen, sobald der Client sie geladen hat.
|
||||
local CatGet = Codex.Catalog and Codex.Catalog.GetItem
|
||||
if Codex.Forever and Codex.Forever.ItemIds then
|
||||
for id in pairs(Codex.Forever.ItemIds()) do
|
||||
if not index[id] and Util.GetItem(id) then
|
||||
if not index[id] and ((CatGet and CatGet(id)) or Util.GetItem(id)) then
|
||||
local it = Codex.Forever.Item(id)
|
||||
local f = { zones = {}, best = 0, fe = true }
|
||||
if it then
|
||||
@@ -1910,11 +1912,11 @@ local function Build(content)
|
||||
nameCtl = Codex.NameUI.Attach(f, function() return kind, selected end, function()
|
||||
dataVersion = dataVersion + 1
|
||||
Refresh()
|
||||
end, { x = LIST_W + 22, y = 34, relayout = function() if UpdateFavUIRef then UpdateFavUIRef() end end })
|
||||
end, { x = LIST_W + 22, y = 0, relayout = function() if UpdateFavUIRef then UpdateFavUIRef() end end })
|
||||
end
|
||||
|
||||
---------------- Favorit und Notiz ----------------
|
||||
local favButton = UI.CreateButton(f, L.CODEX_FAV_ADD, 140, 26, function()
|
||||
local favButton = UI.CreateButton(f, L.CODEX_FAV_ADD, 150, 26, function()
|
||||
if selected ~= nil and Codex.FavSupported(kind) then
|
||||
Codex.SetFav(kind, selected, not Codex.IsFav(kind, selected))
|
||||
dataVersion = dataVersion + 1
|
||||
@@ -1923,6 +1925,7 @@ local function Build(content)
|
||||
end, "default")
|
||||
favButton:SetPoint("TOPLEFT", f, "TOPLEFT", LIST_W + 22 + 108, 0)
|
||||
favButton:Hide()
|
||||
if nameCtl then nameCtl.lead = { backButton, favButton } end
|
||||
local noteBox = UI.CreatePanel(f, "bgDeep", "bronze")
|
||||
noteBox:SetHeight(24)
|
||||
noteBox:SetPoint("TOPLEFT", f, "TOPLEFT", LIST_W + 22, -34)
|
||||
@@ -1985,8 +1988,15 @@ local function Build(content)
|
||||
noteEdit:ClearFocus()
|
||||
end
|
||||
noteTarget = nil
|
||||
local devH = nameCtl and nameCtl.height or 0 -- Reihe mit den Bearbeiten-Knoepfen (nur fuer Berechtigte)
|
||||
local top = 34 + devH
|
||||
-- Favorit-Knopf zuerst ein-/ausblenden, dann die gemeinsame Knopfreihe (Zurueck, Favorit, Bearbeiten ...) legen
|
||||
if ok then
|
||||
favButton:Show()
|
||||
else
|
||||
favButton:Hide()
|
||||
end
|
||||
if nameCtl then nameCtl.Layout(true) end
|
||||
local devH = nameCtl and nameCtl.height or 32 -- eine gemeinsame Reihe von links nach rechts
|
||||
local top = devH + 2
|
||||
noteBox:ClearAllPoints()
|
||||
noteBox:SetPoint("TOPLEFT", f, "TOPLEFT", LIST_W + 22, -top)
|
||||
noteBox:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, -top)
|
||||
@@ -2142,6 +2152,11 @@ local function Build(content)
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
pcall(GameTooltip.SetItemByID, GameTooltip, self.itemID)
|
||||
GameTooltip:Show()
|
||||
elseif self.tip and GameTooltip then
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(self.tip[1] or "", 1, 1, 1)
|
||||
for ti = 2, #self.tip do GameTooltip:AddLine(self.tip[ti], 0.8, 0.8, 0.8) end
|
||||
GameTooltip:Show()
|
||||
elseif self.spellID and GameTooltip then
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
local ok = pcall(GameTooltip.SetSpellByID, GameTooltip, self.spellID)
|
||||
@@ -2283,7 +2298,7 @@ local function Build(content)
|
||||
elseif style == "heading" then h = 28; top = 12
|
||||
elseif style == "item" or style == "link" then h = 26
|
||||
elseif style == "text" then h = TextHeight(opts.text) end
|
||||
if opts.spellID then h = 26 end
|
||||
if opts.spellID or opts.tip then h = 26 end
|
||||
row:ClearAllPoints()
|
||||
row:SetPoint("TOPLEFT", detail.child, "TOPLEFT", 0, -(self.y + top))
|
||||
row:SetPoint("TOPRIGHT", detail.child, "TOPRIGHT", 0, -(self.y + top))
|
||||
@@ -2299,7 +2314,8 @@ local function Build(content)
|
||||
row.onClick = opts.onClick
|
||||
row.itemID = opts.itemID
|
||||
row.spellID = opts.spellID
|
||||
row:EnableMouse(row.clickable or opts.itemID ~= nil or opts.spellID ~= nil)
|
||||
row.tip = opts.tip
|
||||
row:EnableMouse(row.clickable or opts.itemID ~= nil or opts.spellID ~= nil or opts.tip ~= nil)
|
||||
|
||||
local leftX = 4 + indent
|
||||
if opts.icon then
|
||||
@@ -3287,10 +3303,16 @@ local function Build(content)
|
||||
D:Add({ style = "heading", text = string.format(L.CODEX_SEC_LEARN_SPELLS, #g.spells) })
|
||||
for _, sp in ipairs(g.spells) do
|
||||
local rank = sp.rk or (sp.sp and Codex.SpellRank(sp.sp)) or nil
|
||||
local icon = sp.sp and Codex.SpellIcon(sp.sp) or nil
|
||||
D:Add({ text = (sp.nm or "?") .. (rank and (" |cff9a9aa3(" .. rank .. ")|r") or ""),
|
||||
local icon = sp.sp and Codex.SpellIcon(sp.sp) or sp.ic or nil
|
||||
local tip
|
||||
if not sp.sp then
|
||||
tip = { (sp.nm or "?") .. (rank and (" (" .. rank .. ")") or "") }
|
||||
if lv > 0 then tip[#tip + 1] = string.format(L.CODEX_TRAIN_LEVEL, lv) end
|
||||
if sp.cost and sp.cost > 0 then tip[#tip + 1] = FormatMoney(sp.cost) end
|
||||
end
|
||||
D:Add({ tip = tip, text = (sp.nm or "?") .. (rank and (" |cff9a9aa3(" .. rank .. ")|r") or ""),
|
||||
right = (sp.cost and sp.cost > 0) and FormatMoney(sp.cost) or "-", rightWidth = 150,
|
||||
spellID = sp.sp, icon = icon or (sp.sp and "Interface\\Icons\\INV_Misc_QuestionMark") or nil })
|
||||
spellID = sp.sp, icon = icon or "Interface\\Icons\\INV_Misc_QuestionMark" })
|
||||
end
|
||||
D:Add({ style = "heading", text = string.format(L.CODEX_SEC_LEARN_TRAINERS, #g.trainers) })
|
||||
for i, id in ipairs(g.trainers) do
|
||||
|
||||
Reference in New Issue
Block a user