mirror of
https://github.com/Lacustat/Freewood-OctoWoW-Core.git
synced 2026-10-30 15:20:56 +00:00
Freewood-OctoWoW-Core 0.1.136
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016onJCSHLwhs9udhC9ZLzk9
This commit is contained in:
+303
@@ -0,0 +1,303 @@
|
||||
-- Freewood-OctoWoW-Core: Hilfsfunktionen
|
||||
-- Kopieren/Zusammenfuehren von Tabellen, Geld, Items (asynchron), Pruefsumme,
|
||||
-- NPC-ID aus GUID, Spielerposition.
|
||||
|
||||
local ADDON, FW = ...
|
||||
|
||||
local Util = {}
|
||||
FW.Util = Util
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Tabellen
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function Util.CopyTable(src, seen)
|
||||
if type(src) ~= "table" then
|
||||
return src
|
||||
end
|
||||
seen = seen or {}
|
||||
if seen[src] then
|
||||
return seen[src]
|
||||
end
|
||||
local dst = {}
|
||||
seen[src] = dst
|
||||
for k, v in pairs(src) do
|
||||
dst[k] = Util.CopyTable(v, seen)
|
||||
end
|
||||
return dst
|
||||
end
|
||||
|
||||
-- Geheime Werte (secret values): das Spiel gibt manche Angaben, vor allem zu fremden Einheiten,
|
||||
-- als "geheimen" Wert zurueck. Damit darf Addon-Code nichts rechnen, vergleichen oder indizieren.
|
||||
function Util.IsSecret(v)
|
||||
local fn = _G.issecretvalue
|
||||
if type(fn) == "function" then
|
||||
local ok, res = pcall(fn, v)
|
||||
return ok and res and true or false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Gibt v zurueck, aber nil, wenn der Wert geheim ist
|
||||
function Util.Clean(v)
|
||||
if Util.IsSecret(v) then
|
||||
return nil
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- Ergaenzt fehlende Schluessel in target aus defaults. Vorhandene Werte bleiben.
|
||||
function Util.MergeDefaults(target, defaults)
|
||||
for k, v in pairs(defaults) do
|
||||
if type(v) == "table" then
|
||||
if type(target[k]) ~= "table" then
|
||||
target[k] = Util.CopyTable(v)
|
||||
else
|
||||
Util.MergeDefaults(target[k], v)
|
||||
end
|
||||
elseif target[k] == nil then
|
||||
target[k] = v
|
||||
end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
function Util.Count(t)
|
||||
local n = 0
|
||||
for _ in pairs(t) do
|
||||
n = n + 1
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Zahlen und Geld
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function Util.Round(n, decimals)
|
||||
local mult = 10 ^ (decimals or 0)
|
||||
return math.floor(n * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
function Util.Clamp(n, lo, hi)
|
||||
if n < lo then
|
||||
return lo
|
||||
end
|
||||
if n > hi then
|
||||
return hi
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
function Util.FormatNumber(n)
|
||||
local text = string.format("%.0f", math.floor(math.abs(n) + 0.5))
|
||||
local sep = (FW.activeLocale == "deDE") and "." or ","
|
||||
local count
|
||||
repeat
|
||||
text, count = text:gsub("^(%d+)(%d%d%d)", "%1" .. sep .. "%2")
|
||||
until count == 0
|
||||
if n < 0 then
|
||||
text = "-" .. text
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
local MONEY_COLORS = { g = "f0bf5c", s = "c4c8d0", c = "c8793c" }
|
||||
|
||||
-- Kupfer -> "12G 34S 56K". opts.plain: ohne Farben, opts.noCopper: Kupfer weglassen
|
||||
function Util.FormatMoney(copper, opts)
|
||||
opts = opts or {}
|
||||
copper = tonumber(copper) or 0
|
||||
local negative = copper < 0
|
||||
if negative then
|
||||
copper = -copper
|
||||
end
|
||||
copper = math.floor(copper)
|
||||
|
||||
local gold = math.floor(copper / 10000)
|
||||
local silver = math.floor((copper % 10000) / 100)
|
||||
local rest = copper % 100
|
||||
local L = FW.L
|
||||
|
||||
local function unit(key, symbol)
|
||||
if opts.plain then
|
||||
return symbol
|
||||
end
|
||||
return "|cff" .. MONEY_COLORS[key] .. symbol .. "|r"
|
||||
end
|
||||
|
||||
local parts = {}
|
||||
if gold > 0 then
|
||||
parts[#parts + 1] = Util.FormatNumber(gold) .. unit("g", L.MONEY_G)
|
||||
end
|
||||
if silver > 0 then
|
||||
parts[#parts + 1] = silver .. unit("s", L.MONEY_S)
|
||||
end
|
||||
if rest > 0 and not (opts.noCopper and gold > 0) then
|
||||
parts[#parts + 1] = rest .. unit("c", L.MONEY_C)
|
||||
end
|
||||
if #parts == 0 then
|
||||
parts[1] = "0" .. unit("c", L.MONEY_C)
|
||||
end
|
||||
|
||||
local text = table.concat(parts, " ")
|
||||
if negative then
|
||||
text = "-" .. text
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Pruefsumme (Adler-32-Variante, nur Rechnen mit kleinen Zahlen)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function Util.Hash(text)
|
||||
local a, b = 1, 0
|
||||
for i = 1, #text do
|
||||
a = (a + text:byte(i)) % 65521
|
||||
b = (b + a) % 65521
|
||||
end
|
||||
return string.format("%04x%04x", b, a)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Items (mit asynchronem Nachladen)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local function RawItemInfo(idOrLink)
|
||||
if C_Item and C_Item.GetItemInfo then
|
||||
return C_Item.GetItemInfo(idOrLink)
|
||||
end
|
||||
return GetItemInfo(idOrLink)
|
||||
end
|
||||
|
||||
-- Liefert eine Tabelle oder nil, wenn die Daten noch nicht im Cache sind.
|
||||
function Util.GetItem(idOrLink)
|
||||
local name, link, quality, itemLevel, minLevel, itemType, itemSubType,
|
||||
stackCount, equipLoc, icon, sellPrice, classID, subClassID = RawItemInfo(idOrLink)
|
||||
if not name then
|
||||
return nil
|
||||
end
|
||||
local id = tonumber(idOrLink)
|
||||
if not id and type(link) == "string" then
|
||||
id = tonumber(link:match("item:(%d+)"))
|
||||
end
|
||||
return {
|
||||
id = id,
|
||||
name = name,
|
||||
link = link,
|
||||
quality = quality or 1,
|
||||
itemLevel = itemLevel,
|
||||
minLevel = minLevel,
|
||||
type = itemType,
|
||||
subType = itemSubType,
|
||||
stackCount = stackCount,
|
||||
equipLoc = equipLoc,
|
||||
icon = icon,
|
||||
sellPrice = sellPrice or 0,
|
||||
classID = classID,
|
||||
subClassID = subClassID,
|
||||
}
|
||||
end
|
||||
|
||||
local pendingItems = {}
|
||||
|
||||
-- Ruft callback(info) auf, sobald die Item-Daten da sind (sofort, falls schon im Cache).
|
||||
-- info ist nil, wenn das Item nicht geladen werden konnte.
|
||||
function Util.RequestItem(itemID, callback)
|
||||
local info = Util.GetItem(itemID)
|
||||
if info then
|
||||
FW.SafeCall(callback, info)
|
||||
return
|
||||
end
|
||||
local list = pendingItems[itemID]
|
||||
if not list then
|
||||
list = {}
|
||||
pendingItems[itemID] = list
|
||||
end
|
||||
list[#list + 1] = callback
|
||||
if C_Item and C_Item.RequestLoadItemDataByID then
|
||||
C_Item.RequestLoadItemDataByID(itemID)
|
||||
end
|
||||
end
|
||||
|
||||
FW:RegisterEvent("GET_ITEM_INFO_RECEIVED", function(itemID, success)
|
||||
local list = pendingItems[itemID]
|
||||
if not list then
|
||||
return
|
||||
end
|
||||
pendingItems[itemID] = nil
|
||||
local info = success and Util.GetItem(itemID) or nil
|
||||
for _, callback in ipairs(list) do
|
||||
FW.SafeCall(callback, info)
|
||||
end
|
||||
end)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Kreaturen und Orte
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- GUID "Creature-0-4621-1-27730-4107-0000477262" -> 4107 (sechstes Feld)
|
||||
-- Octo/1.12: GUID als Hex "0xF130003A5C0012AB" = Typ (F13 Kreatur, F14 Begleiter,
|
||||
-- F11 Objekt, F15 Fahrzeug), Eintrags-ID (6 Hex), Zaehler (6 Hex).
|
||||
-- Rueckgabe: "Creature" / "Pet" / "GameObject" / "Vehicle", ID; sonst nil.
|
||||
local HEX_KIND = { F13 = "Creature", F14 = "Pet", F11 = "GameObject", F15 = "Vehicle" }
|
||||
function Util.ParseHexGUID(guid)
|
||||
if type(guid) ~= "string" then
|
||||
return nil
|
||||
end
|
||||
local hex = guid:match("^0[xX](%x+)$")
|
||||
if not hex or #hex ~= 16 then
|
||||
return nil
|
||||
end
|
||||
local kind = HEX_KIND[hex:sub(1, 3):upper()]
|
||||
if not kind then
|
||||
return nil
|
||||
end
|
||||
return kind, tonumber(hex:sub(5, 10), 16)
|
||||
end
|
||||
|
||||
function Util.GetNpcID(guid)
|
||||
if type(guid) ~= "string" then
|
||||
return nil
|
||||
end
|
||||
local hk, hid = Util.ParseHexGUID(guid)
|
||||
if hk then
|
||||
if hk == "Creature" or hk == "Vehicle" then
|
||||
return hid
|
||||
end
|
||||
return nil
|
||||
end
|
||||
local kind, _, _, _, _, id = strsplit("-", guid)
|
||||
if kind == "Creature" or kind == "Vehicle" then
|
||||
return tonumber(id)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Aktuelle Position des Spielers. x und y sind 0..1 auf der Karte, koennen
|
||||
-- in Instanzen fehlen. Die Koordinaten sind die des Spielers, nicht des Ziels.
|
||||
function Util.GetPlayerLocation()
|
||||
local mapID = C_Map and C_Map.GetBestMapForUnit and C_Map.GetBestMapForUnit("player")
|
||||
local x, y
|
||||
if mapID then
|
||||
local pos = C_Map.GetPlayerMapPosition(mapID, "player")
|
||||
if pos then
|
||||
x, y = pos:GetXY()
|
||||
end
|
||||
end
|
||||
return {
|
||||
mapID = mapID,
|
||||
x = x and Util.Round(x, 4) or nil,
|
||||
y = y and Util.Round(y, 4) or nil,
|
||||
zone = GetZoneText(),
|
||||
subzone = GetSubZoneText(),
|
||||
t = time(),
|
||||
}
|
||||
end
|
||||
|
||||
function Util.GetPlayerKey()
|
||||
local name = UnitName("player")
|
||||
local realm = GetRealmName() or ""
|
||||
return name .. "-" .. (realm:gsub("%s", ""))
|
||||
end
|
||||
Reference in New Issue
Block a user