From 505a06b46d0b06413c228dd8f94d5c1981ed68a8 Mon Sep 17 00:00:00 2001 From: Lacustat Date: Fri, 30 Oct 2026 09:53:10 +0100 Subject: [PATCH] 0.1.191: Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_016onJCSHLwhs9udhC9ZLzk9 --- Core/Nameplates.lua | 266 ++++++++++++++++++++++++++++++++++++++ Freewood-OctoWoW-Core.toc | 3 +- Locales.lua | 18 +++ 3 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 Core/Nameplates.lua diff --git a/Core/Nameplates.lua b/Core/Nameplates.lua new file mode 100644 index 0000000..a08fe0f --- /dev/null +++ b/Core/Nameplates.lua @@ -0,0 +1,266 @@ +-- Freewood-OctoWoW-Core: Namensplaketten anpassen (Balkenbreite, Name, Level, Rahmen) und die zugehoerigen Einstellungen. +-- Frueher im Quest-Modul (QuestTargetMarks/QuestPlateSettings); laeuft jetzt ohne Quest-Modul. Werte: FW.Plates.Get/Set, +-- gespeichert in den Core-Einstellungen. Die Quest-Symbole (Logo) bleiben im Quest-Modul. + +local FW = _G.Freewood +if not FW then return end +local UI = FW.UI + +local P = {} +FW.Plates = P + +local defaults = { plateCustom = false, plateW = 160, plateBorderOff = false, plateNameSize = 0, plateLvlGap = 2, plateY = 0 } + +local function Store() + local s = FW.settings + if not s then return nil end + if type(s.plates) ~= "table" then s.plates = {} end + return s.plates +end + +function P.Get(key) + local st = Store() + local v = st and st[key] + if v == nil then return defaults[key] end + return v +end + +function P.Set(key, value) + local st = Store() + if not st then return end + st[key] = value + FW:Fire("PLATES_CHANGED", key) + FW:Fire("QUEST_PLATE_CHANGED", key) -- Quest-Modul: Symbole neu setzen + P.dirty = true +end + +-- Alte Werte aus den Quest-Einstellungen uebernehmen (einmalig, nur wenn im Core noch nichts steht) +function P.Migrate(old) + local st = Store() + if not st or type(old) ~= "table" then return end + for k in pairs(defaults) do + if st[k] == nil and old[k] ~= nil and type(old[k]) == type(defaults[k]) then st[k] = old[k] end + end +end + +------------------------------------------------------------------------ +-- Anpassung +-- Wie ShaguPlates: Name und Level werden als EIGENE Texte auf einem eigenen Rahmen gezeichnet, die Originaltexte der Plakette nur +-- ausgeblendet (Hide). SetFont/SetScale auf den Originalteilen stuerzt den Client ab, auf eigenen Texten nicht. +-- Am Lebensbalken aendern wir nur Breite/Hoehe/Anker, am Rahmen nur Hide/Show. Ausgangswerte merkt sich das Addon pro Plakette. +------------------------------------------------------------------------ + +local BAR_W = 160 +local FALLBACK_FONT = "Fonts\\FRIZQT__.TTF" +local origs = setmetatable({}, { __mode = "k" }) +local overlays = setmetatable({}, { __mode = "k" }) +local customOff = false +P.Overlays = overlays + +local function LookParts(plate) + local bar = plate:GetChildren() + local border, glow, name, level = plate:GetRegions() + if not (bar and bar.SetWidth and name and name.GetFont and level and level.GetText) then return nil end + return bar, border, name, level, glow +end + +local function SavePoint(f) + local pt, rel, relPt, x, y = f:GetPoint(1) + return { pt, rel, relPt, x or 0, y or 0 } +end + +local function Overlay(plate, font, size, flags) + local ov = overlays[plate] + if ov then return ov end + local ok, f = pcall(_G.CreateFrame, "Frame", nil, plate) + if not ok or not f then return nil end + if f.EnableMouse then f:EnableMouse(false) end + f:SetFrameStrata("HIGH") + ov = { frame = f, size = size } + ov.hl = f:CreateTexture(nil, "BACKGROUND") + ov.hl:SetTexture(1, 1, 1, 0.3) + ov.hl:Hide() + ov.name = f:CreateFontString(nil, "OVERLAY") + ov.name:SetFont(font, size, flags) + ov.level = f:CreateFontString(nil, "OVERLAY") + ov.level:SetFont(font, size, flags) + overlays[plate] = ov + return ov +end + +local function CopyText(src, dst) + local t = src:GetText() + dst:SetText(t or "") + local r, g, b, a = src:GetTextColor() + if r then dst:SetTextColor(r, g, b, a or 1) end +end + +local function ApplyLook(plate) + local on = (not customOff) and P.Get("plateCustom") == true + local o = origs[plate] + if not on and not o then return end + local bar, border, name, level, glow = LookParts(plate) + if not bar then return end + if not o then + local path, size, flags = name:GetFont() + o = { w = bar:GetWidth(), h = bar:GetHeight(), barPt = SavePoint(bar), + font = path or FALLBACK_FONT, size = size or 12, flags = flags, + glowAlpha = glow and glow.GetAlpha and glow:GetAlpha() or 1 } + origs[plate] = o + end + local ov = overlays[plate] + if not on then + bar:SetWidth(o.w) bar:SetHeight(o.h) + local b = o.barPt + if b[1] then bar:ClearAllPoints() bar:SetPoint(b[1], b[2], b[3], b[4], b[5]) end + if border then border:Show() end + name:Show() level:Show() + if glow and glow.SetAlpha then glow:SetAlpha(o.glowAlpha or 1) end + if ov then ov.frame:Hide() end + origs[plate] = nil + return + end + local w = math.max(40, math.min(200, tonumber(P.Get("plateW")) or BAR_W)) + local h = math.floor(o.h * w / o.w * 10 + 0.5) / 10 + if math.abs(bar:GetWidth() - w) > 0.4 then bar:SetWidth(w) end + if math.abs(bar:GetHeight() - h) > 0.4 then bar:SetHeight(h) end + -- Balken mittig halten: Ausgangs-Anker um die halbe Breitendifferenz verschieben + local b = o.barPt + if b[1] then + local dx = (o.w - w) / 2 + local x = b[4] + if b[1]:find("LEFT") then x = x + dx elseif b[1]:find("RIGHT") then x = x - dx end + bar:ClearAllPoints() + bar:SetPoint(b[1], b[2], b[3], x, b[5] + (tonumber(P.Get("plateY")) or 0)) + end + if border then + if P.Get("plateBorderOff") == true then border:Hide() else border:Show() end + end + -- eigene Texte fuer Name und Level + local ns = tonumber(P.Get("plateNameSize")) or 0 + local size = (ns >= 8) and ns or o.size + ov = Overlay(plate, o.font, size, o.flags) + if not ov then return end + if ov.size ~= size then + ov.size = size + pcall(ov.name.SetFont, ov.name, o.font, size, o.flags) + pcall(ov.level.SetFont, ov.level, o.font, size, o.flags) + end + ov.frame:Show() + if name:IsShown() then name:Hide() end + if level:IsShown() then level:Hide() end + ov.name:ClearAllPoints() + ov.name:SetPoint("BOTTOM", bar, "TOP", 0, 2) + ov.level:ClearAllPoints() + ov.level:SetPoint("LEFT", bar, "RIGHT", tonumber(P.Get("plateLvlGap")) or 2, 0) + CopyText(name, ov.name) + CopyText(level, ov.level) + -- Mouseover-Leuchten: das Original (zu gross) unsichtbar, dafuer ein eigenes Rechteck genau um den Balken + if glow and glow.SetAlpha then + if glow:GetAlpha() ~= 0 then glow:SetAlpha(0) end + ov.hl:ClearAllPoints() + ov.hl:SetPoint("TOPLEFT", bar, "TOPLEFT", -2, 2) + ov.hl:SetPoint("BOTTOMRIGHT", bar, "BOTTOMRIGHT", 2, -2) + if glow:IsShown() then ov.hl:Show() else ov.hl:Hide() end + end +end + +P.ApplyLook = ApplyLook + +local function PlateList() + local NP = _G.C_NamePlate + if not (NP and NP.GetNamePlates) then return nil end + local ok, plates = pcall(NP.GetNamePlates) + if ok and type(plates) == "table" then return plates end + return nil +end + +-- Alle sichtbaren Plaketten durchgehen (wenige, alle 0,25 s); ist die Anpassung aus und nichts mehr veraendert, ruht es +local f = _G.CreateFrame("Frame") +local acc = 0 +local anyChanged = false +f:SetScript("OnUpdate", function(_, elapsed) + acc = acc + (elapsed or 0) + if acc < 0.25 then return end + acc = 0 + local on = P.Get("plateCustom") == true + if not on and not anyChanged then return end + local plates = PlateList() + if not plates then return end + for _, plate in ipairs(plates) do pcall(ApplyLook, plate) end + anyChanged = on +end) +local function Touch() anyChanged = true end +FW:On("PLATES_CHANGED", Touch, P) +if FW.RegisterEvent then + FW:RegisterEvent("NAME_PLATE_UNIT_ADDED", function() acc = 1 end, P) -- neue Plakette sofort anpassen +end + +------------------------------------------------------------------------ +-- Einstellungen (Reiter "Quests und Hinweise") +------------------------------------------------------------------------ + +if UI and UI.RegisterSettingsSection then + local function StepRow(parent, onMinus, onPlus) + local row = CreateFrame("Frame", nil, parent) + row:SetHeight(30) + local plus = UI.CreateButton(row, "+", 24, 22, onPlus, "default") + plus:SetPoint("RIGHT", row, "RIGHT", 0, 0) + local minus = UI.CreateButton(row, "-", 24, 22, onMinus, "default") + minus:SetPoint("RIGHT", plus, "LEFT", -4, 0) + local lbl = UI.CreateLabel(row, "", { size = 12 }) + lbl:SetPoint("LEFT", row, "LEFT", 0, 0) + lbl:SetPoint("RIGHT", minus, "LEFT", -8, 0) + lbl:SetWordWrap(true) + return row, lbl + end + + local function Step(key, lo, hi, def, d, zero) + local v = tonumber(P.Get(key)) or def + local n = v + d + if zero and v == 0 and d > 0 then n = lo elseif zero and v <= lo and d < 0 then n = 0 end + P.Set(key, math.max(zero and 0 or lo, math.min(hi, n))) + end + + UI:RegisterSettingsSection("plates", { + category = "quest", + order = 120, + height = 330, + build = function(parent) + local L = FW.L + local f2, stack = UI.CreateSection(parent, L.QUEST_PLATESET_HEADER) + local note = UI.CreateLabel(f2, L.QUEST_PLATESET_NOTE, { size = 11, color = "textDim", justifyV = "TOP" }) + stack:Add(note, { fit = true, gap = 8 }) + local boxes = {} + local function Box(key, label) + local cb = UI.CreateCheckbox(f2, label, function() return P.Get(key) == true end, + function(on) P.Set(key, on and true or false) end) + stack:Add(cb, { fit = true, gap = 4 }) + boxes[#boxes + 1] = cb + end + Box("plateCustom", L.QUEST_OPT_PLATECUSTOM) + Box("plateBorderOff", L.QUEST_OPT_PLATEBORDER) + local labels = {} + local function Row(key, lo, hi, def, step, zero, fmt) + local row, lbl = StepRow(f2, function() Step(key, lo, hi, def, -step, zero) end, function() Step(key, lo, hi, def, step, zero) end) + stack:Add(row, { height = 30, stretch = true, gap = 4 }) + labels[#labels + 1] = function() lbl:SetText(fmt(tonumber(P.Get(key)) or def)) end + end + Row("plateW", 40, 200, 160, 10, false, function(v) return string.format(L.QUEST_PLATEW_LABEL, v) end) + Row("plateNameSize", 8, 20, 0, 1, true, function(v) + return string.format(L.QUEST_PLATENAME_LABEL, v >= 8 and tostring(v) or L.QUEST_PLATENAME_STD) + end) + Row("plateLvlGap", -10, 20, 2, 1, false, function(v) return string.format(L.QUEST_PLATELVL_LABEL, v) end) + Row("plateY", -40, 40, 0, 2, false, function(v) return string.format(L.QUEST_PLATEY_LABEL, v) end) + function f2.Refresh() + for _, cb in ipairs(boxes) do cb:Refresh() end + for _, fn in ipairs(labels) do fn() end + stack:Layout() + end + FW:On("PLATES_CHANGED", function() if f2:IsVisible() then f2.Refresh() end end, f2) + stack:Layout() + return f2 + end, + refresh = function(frame) frame.Refresh() end, + }) +end diff --git a/Freewood-OctoWoW-Core.toc b/Freewood-OctoWoW-Core.toc index 65af520..b872cec 100644 --- a/Freewood-OctoWoW-Core.toc +++ b/Freewood-OctoWoW-Core.toc @@ -3,7 +3,7 @@ ## Notes: Base addon of the Freewood family: data storage, UI kit, export/import and the main window. ## Notes-deDE: Basis-Addon der Freewood-Familie: Datenspeicher, UI-Bausteine, Export/Import und Hauptfenster. ## Author: Lacustat -## Version: 0.1.190 +## Version: 0.1.191 ## SavedVariables: FreewoodDB ## SavedVariablesPerCharacter: FreewoodCharDB @@ -19,5 +19,6 @@ UI\Tabs.lua Core\Tooltips.lua Core\TooltipInfo.lua Core\TooltipCompare.lua +Core\Nameplates.lua UI\Minimap.lua Core\Slash.lua diff --git a/Locales.lua b/Locales.lua index 52ac620..75830c9 100644 --- a/Locales.lua +++ b/Locales.lua @@ -96,6 +96,15 @@ FW:AddLocale("enUS", { SETTINGS_CAT_DATABASE = "Database", SETTINGS_CAT_TOOLTIPS = "Tooltips", SETTINGS_CAT_QUEST = "Quests and hints", + QUEST_PLATESET_HEADER = "Nameplates", + QUEST_PLATESET_NOTE = "Resize and tidy the game's nameplates (health bar, name, level). Only plates in view are changed; the game decides how far you see them (V toggles them). The quest marks (logo) stay in the Quest tab.", + QUEST_OPT_PLATECUSTOM = "Customize nameplates (bar size, level next to bar, centered)", + QUEST_OPT_PLATEBORDER = "Hide nameplate frame", + QUEST_PLATEW_LABEL = "Nameplate bar width: %d", + QUEST_PLATENAME_LABEL = "Nameplate name size: %s", + QUEST_PLATENAME_STD = "default", + QUEST_PLATEY_LABEL = "Nameplate height (+ up): %d", + QUEST_PLATELVL_LABEL = "Nameplate level distance to bar: %d", SETTINGS_CAT_MODULES = "Modules", TIPSET_HEADER = "Tooltips", TIPSET_NOTE = "Look and extra information in item and creature tooltips. Each part can be switched off.", @@ -188,6 +197,15 @@ FW:AddLocale("deDE", { SETTINGS_CAT_DATABASE = "Datenbank", SETTINGS_CAT_TOOLTIPS = "Tooltips", SETTINGS_CAT_QUEST = "Quests und Hinweise", + QUEST_PLATESET_HEADER = "Namensplaketten", + QUEST_PLATESET_NOTE = "Größe und Anordnung der Namensplaketten des Spiels (Lebensbalken, Name, Level). Es werden nur Plaketten in Sichtweite geändert; wie weit man sie sieht, bestimmt das Spiel (V schaltet sie ein und aus). Die Quest-Symbole (Logo) bleiben im Quest-Reiter.", + QUEST_OPT_PLATECUSTOM = "Namensplaketten anpassen (Balkengröße, Level am Balken, zentriert)", + QUEST_OPT_PLATEBORDER = "Rahmen der Namensplakette ausblenden", + QUEST_PLATEW_LABEL = "Plakette: Balkenbreite %d", + QUEST_PLATENAME_LABEL = "Plakette: Namensgröße %s", + QUEST_PLATENAME_STD = "Standard", + QUEST_PLATEY_LABEL = "Plakette: Höhe (+ hoch): %d", + QUEST_PLATELVL_LABEL = "Plakette: Abstand Level zum Balken %d", SETTINGS_CAT_MODULES = "Module", TIPSET_HEADER = "Tooltips", TIPSET_NOTE = "Aussehen und Zusatzinfos in Item- und Gegner-Tooltips. Jeder Teil lässt sich einzeln ausschalten.",