mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
refactor: move the embedded addons into src/embed/AddOns
Users saw AddOns/!!!ClassicAPI at the repository root and downloaded it, thinking the mod is only an addon. The addons are source code for the DLL, so they now live next to the resource loader in src/embed/. The README says that the DLL contains them and that the folder is not for copying into the game. Remove the AoELootTest dev addon.
This commit is contained in:
+1
-1
@@ -47,4 +47,4 @@
|
||||
|
||||
CLAUDE.md
|
||||
|
||||
/AddOns/*/.classicapi-dev
|
||||
/src/embed/AddOns/*/.classicapi-dev
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
-- AoE Loot Test
|
||||
--
|
||||
-- /aoeloot opens a picker window listing every item in every lootable
|
||||
-- corpse within click range. Clicking a row hands the (guid, itemID)
|
||||
-- pair to C_Loot.LootUnitItem and re-scans so the picker refreshes
|
||||
-- with whatever's left.
|
||||
--
|
||||
-- Known visual: each scan opens / closes loot windows in rapid
|
||||
-- succession, so the Blizzard LootFrame flickers during the scan. This
|
||||
-- addon intentionally doesn't suppress that — the goal is to exercise
|
||||
-- the C++ surface, not to ship polished UI.
|
||||
|
||||
local MAX_ROWS = 24
|
||||
local RESCAN_DELAY = 0.3 -- seconds; lets LootUnitItem complete before re-scan
|
||||
|
||||
-- ---------- UI scaffolding ----------
|
||||
|
||||
-- Frame geometry. Height is recomputed dynamically per scan so the
|
||||
-- window only takes up as much screen real estate as the result count
|
||||
-- needs — see `ResizeForRows`.
|
||||
local FRAME_WIDTH = 320
|
||||
local HEADER_HEIGHT = 64 -- title + status + insets; minimum frame height
|
||||
local ROW_HEIGHT = 22
|
||||
|
||||
local frame = CreateFrame("Frame", "AoELootTestFrame", UIParent)
|
||||
frame:SetWidth(FRAME_WIDTH)
|
||||
frame:SetHeight(HEADER_HEIGHT)
|
||||
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
||||
frame:SetBackdrop({
|
||||
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
||||
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
||||
tile = true, tileSize = 32, edgeSize = 32,
|
||||
insets = { left = 11, right = 12, top = 12, bottom = 11 },
|
||||
})
|
||||
frame:SetMovable(true)
|
||||
frame:EnableMouse(true)
|
||||
frame:RegisterForDrag("LeftButton")
|
||||
-- Vanilla 1.12 fires script handlers with the frame set as the global
|
||||
-- `this`, not as a `self` arg to the function. Passing `frame.StartMoving`
|
||||
-- directly (the modern shortcut) breaks because the C method has no
|
||||
-- `self` to operate on. Wrap in closures so `this:method()` resolves
|
||||
-- correctly inside the handler.
|
||||
frame:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
frame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||||
frame:Hide()
|
||||
|
||||
local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
title:SetPoint("TOP", frame, "TOP", 0, -16)
|
||||
title:SetText("AoE Loot")
|
||||
|
||||
local status = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
||||
status:SetPoint("TOPLEFT", frame, "TOPLEFT", 18, -36)
|
||||
status:SetText("")
|
||||
|
||||
local closeBtn = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
|
||||
closeBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -6, -6)
|
||||
|
||||
-- ---------- Row pool ----------
|
||||
|
||||
local rows = {}
|
||||
|
||||
local function CreateRow(parent, idx)
|
||||
local row = CreateFrame("Button", nil, parent)
|
||||
row:SetWidth(290)
|
||||
row:SetHeight(20)
|
||||
row:SetPoint("TOPLEFT", parent, "TOPLEFT", 15,
|
||||
-54 - (idx - 1) * ROW_HEIGHT)
|
||||
|
||||
local icon = row:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetWidth(18); icon:SetHeight(18)
|
||||
icon:SetPoint("LEFT", row, "LEFT", 0, 0)
|
||||
row.icon = icon
|
||||
|
||||
local text = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
text:SetPoint("LEFT", icon, "RIGHT", 6, 0)
|
||||
text:SetPoint("RIGHT", row, "RIGHT", 0, 0)
|
||||
text:SetJustifyH("LEFT")
|
||||
row.text = text
|
||||
|
||||
local hl = row:CreateTexture(nil, "HIGHLIGHT")
|
||||
hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
hl:SetBlendMode("ADD")
|
||||
hl:SetAllPoints(row)
|
||||
|
||||
-- Tooltip on hover. The engine's native `SetHyperlink` does a
|
||||
-- literal `SStrCmpI` of the input's first 5 chars against the
|
||||
-- string `"item:"` at `.rdata` VA `0x00842D60` — anything that
|
||||
-- starts with `|c...` (full link with color) or `|Hitem` (full
|
||||
-- link without color) fails the prefix check with "Unknown link
|
||||
-- type". Only the bare payload form survives.
|
||||
--
|
||||
-- We extract the payload from the engine's full link rather than
|
||||
-- composing `item:ID:0:0:0` ourselves, so per-instance fields
|
||||
-- (enchant / random-suffix / unique) survive round-trip and
|
||||
-- "Stringy Wolf Meat of the Bear" tooltips correctly instead of
|
||||
-- collapsing to the un-suffixed itemID.
|
||||
row:SetScript("OnEnter", function()
|
||||
if this.payload then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetHyperlink(this.payload)
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end)
|
||||
row:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
|
||||
row:Hide()
|
||||
return row
|
||||
end
|
||||
|
||||
for i = 1, MAX_ROWS do
|
||||
rows[i] = CreateRow(frame, i)
|
||||
end
|
||||
|
||||
-- ---------- Render ----------
|
||||
|
||||
-- Tail of a GUID string — last 4 hex chars of the low dword. Lets the
|
||||
-- user tell apart two rows of "Stringy Wolf Meat x1" coming from
|
||||
-- different corpses, without occupying a whole column with the full
|
||||
-- 16-digit GUID.
|
||||
local function ShortGuidSuffix(guid)
|
||||
return "[.." .. string.sub(guid, -4) .. "]"
|
||||
end
|
||||
|
||||
-- Extract the `item:ID:enchant:suffix:unique` payload out of the
|
||||
-- engine's full link string (`|cff...|Hitem:...|h[Name]|h|r`).
|
||||
-- Vanilla `SetHyperlink` requires the input to start literally with
|
||||
-- `"item:"` — so we can't pass the full link, but we also can't drop
|
||||
-- the per-instance fields (random suffix is what distinguishes "of
|
||||
-- the Bear" from "of the Eagle" on otherwise identical itemIDs).
|
||||
-- Returns the payload, or nil if extraction fails.
|
||||
local function PayloadFromLink(link)
|
||||
if not link then return nil end
|
||||
return string.match(link, "|H(item:[^|]+)|h")
|
||||
end
|
||||
|
||||
local function ClearRows()
|
||||
for i = 1, MAX_ROWS do
|
||||
rows[i]:Hide()
|
||||
rows[i]:SetScript("OnClick", nil)
|
||||
rows[i].payload = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function PopulateRows(results)
|
||||
local idx = 0
|
||||
for _, corpse in ipairs(results) do
|
||||
for _, item in ipairs(corpse.items) do
|
||||
idx = idx + 1
|
||||
if idx > MAX_ROWS then break end
|
||||
local row = rows[idx]
|
||||
local name, _, _, _, _, _, _, _, icon = GetItemInfo(item.itemID)
|
||||
row.icon:SetTexture(icon or "Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
local label = name or ("Item #" .. item.itemID)
|
||||
if item.count and item.count > 1 then
|
||||
label = label .. " x" .. item.count
|
||||
end
|
||||
label = label .. " " .. ShortGuidSuffix(corpse.guid)
|
||||
row.text:SetText(label)
|
||||
row.payload = PayloadFromLink(item.link) -- for OnEnter tooltip
|
||||
local guid, itemID = corpse.guid, item.itemID
|
||||
row:SetScript("OnClick", function()
|
||||
C_Loot.LootUnitItem(guid, itemID)
|
||||
C_Timer.After(RESCAN_DELAY, function()
|
||||
if frame:IsShown() then C_Loot.ScanNearbyLoot() end
|
||||
end)
|
||||
end)
|
||||
row:Show()
|
||||
end
|
||||
if idx > MAX_ROWS then break end
|
||||
end
|
||||
for i = idx + 1, MAX_ROWS do rows[i]:Hide() end
|
||||
local shown
|
||||
if idx == 0 then
|
||||
status:SetText("Nothing lootable in range.")
|
||||
shown = 0
|
||||
elseif idx > MAX_ROWS then
|
||||
status:SetText(MAX_ROWS .. "+ items (showing first " .. MAX_ROWS .. ")")
|
||||
shown = MAX_ROWS
|
||||
else
|
||||
status:SetText(idx .. " item(s) available")
|
||||
shown = idx
|
||||
end
|
||||
frame:SetHeight(HEADER_HEIGHT + shown * ROW_HEIGHT)
|
||||
end
|
||||
|
||||
-- ---------- Scan trigger ----------
|
||||
|
||||
local function StartScan()
|
||||
status:SetText("Scanning...")
|
||||
ClearRows()
|
||||
frame:SetHeight(HEADER_HEIGHT) -- collapse while we wait for results
|
||||
if not C_Loot.ScanNearbyLoot() then
|
||||
status:SetText("Scan busy or not in world.")
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------- Event wiring ----------
|
||||
|
||||
local evt = CreateFrame("Frame")
|
||||
evt:RegisterEvent("LOOT_SCAN_COMPLETED")
|
||||
evt:SetScript("OnEvent", function()
|
||||
-- Race guard: a delayed scan may complete after the user closed
|
||||
-- the picker. Just drop the result rather than re-showing.
|
||||
if not frame:IsShown() then return end
|
||||
PopulateRows(C_Loot.GetLastScanResults())
|
||||
end)
|
||||
|
||||
-- ---------- Slash command ----------
|
||||
|
||||
RegisterNewSlashCommand(function()
|
||||
if frame:IsShown() then
|
||||
StartScan() -- already open → just refresh
|
||||
else
|
||||
frame:Show()
|
||||
StartScan()
|
||||
end
|
||||
end, "aoeloot", "aoe")
|
||||
@@ -1,7 +0,0 @@
|
||||
## Interface: 11200
|
||||
## Title: AoE Loot Test
|
||||
## Notes: Test bed for ClassicAPI's C_Loot.ScanNearbyLoot / LootUnitItem. /aoeloot
|
||||
## Dependencies: !!!ClassicAPI
|
||||
## Version: DEV
|
||||
|
||||
AoELootTest.lua
|
||||
+2
-2
@@ -76,7 +76,7 @@ endif()
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
${SOURCES} ${HEADERS} ${TINYCBOR_SOURCES})
|
||||
|
||||
# Embed the addons under `AddOns/` and `src/bindings/Bindings.xml` as RCDATA
|
||||
# Embed the addons under `src/embed/AddOns/` and `src/bindings/Bindings.xml` as RCDATA
|
||||
# resources. The file-read hook in `src/addons/Embedded.cpp` serves the
|
||||
# addon files when the engine asks for paths under
|
||||
# `Interface\AddOns\<addon>\`, so the addons load even when the user
|
||||
@@ -86,7 +86,7 @@ add_library(${PROJECT_NAME} SHARED
|
||||
include(cmake/embed_resources.cmake)
|
||||
embed_resources(
|
||||
TARGET ${PROJECT_NAME}
|
||||
ADDONS_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/AddOns"
|
||||
ADDONS_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/src/embed/AddOns"
|
||||
ADDONS "!!!ClassicAPI" "ClassicAPI_DebugTools"
|
||||
ADDON_VERSION "${CLASSICAPI_TOC_VERSION}"
|
||||
BINDINGS_XML "${CMAKE_CURRENT_SOURCE_DIR}/src/bindings/Bindings.xml"
|
||||
|
||||
@@ -409,13 +409,15 @@ Download the prebuilt `ClassicAPI.dll` from the
|
||||
3. Add `ClassicAPI.dll` to `dlls.txt`.
|
||||
4. Launch the game with `VanillaFixes.exe`.
|
||||
|
||||
The bundled `!!!ClassicAPI` addon ships *inside* the DLL — no separate
|
||||
addon download or install step needed.
|
||||
The `!!!ClassicAPI` and `ClassicAPI_DebugTools` addons are *inside* the
|
||||
DLL. You do not download or install them. The `src/embed/AddOns/` folder
|
||||
in this repository is source code for the DLL. Do not copy it into your
|
||||
game.
|
||||
|
||||
## Bundled addon: !!!ClassicAPI
|
||||
|
||||
The Lua-side companion library lives in
|
||||
[`AddOns/!!!ClassicAPI/`](AddOns/!!!ClassicAPI/). It's a 1.12.1 /
|
||||
[`src/embed/AddOns/!!!ClassicAPI/`](src/embed/AddOns/!!!ClassicAPI/). It's a 1.12.1 /
|
||||
Lua 5.0 backport of the modern Blizzard helpers that aren't engine
|
||||
functions but that consumer code still expects to find as globals —
|
||||
`CallbackRegistryMixin`, `EventRegistry`, `ColorMixin` + `CreateColor`,
|
||||
@@ -426,7 +428,7 @@ commands and `SecureCmdOptionParse`, the parser that reads `[conditions]`
|
||||
and `@unit` for them.
|
||||
|
||||
**You don't have to install the addon manually** — the DLL embeds
|
||||
the contents of [`AddOns/!!!ClassicAPI/`](AddOns/!!!ClassicAPI/) and
|
||||
the contents of [`src/embed/AddOns/!!!ClassicAPI/`](src/embed/AddOns/!!!ClassicAPI/) and
|
||||
registers them with the engine as a synthetic addon on startup, so
|
||||
the library is always available when the DLL is loaded. It fires
|
||||
`ADDON_LOADED`, supports `SavedVariables`, and is resolvable by name —
|
||||
@@ -443,15 +445,12 @@ can't be switched off. Being hidden means it's omitted from the
|
||||
index-based `GetNumAddOns()` / `GetAddOnInfo(i)` enumeration, but every
|
||||
by-name lookup and dependency reference still resolves normally.
|
||||
|
||||
If you drop the folder into your `Interface/AddOns/` directory, **that
|
||||
copy's files win** — the DLL's embedded version only serves files when
|
||||
the engine's normal scan doesn't already have a newer entry under that
|
||||
name. Useful for editing the Lua locally without rebuilding the DLL.
|
||||
The hidden + force-enabled treatment applies either way (it's keyed to
|
||||
the addon name, not to which copy serves the files), so the local copy
|
||||
is also absent from the AddOns list. The dispatch is transparent:
|
||||
addons consuming `Mixin`, `ColorMixin`, `TableUtil`, etc. behave
|
||||
identically in both cases.
|
||||
For development, you can put a copy of the addon folder in
|
||||
`Interface/AddOns/` and add an empty `.classicapi-dev` file to it. The
|
||||
game then reads the Lua files from that copy, so you can edit them
|
||||
without a DLL rebuild. Without this file, the game uses the copy with the
|
||||
newer `## Version:`. The copy is hidden and always enabled, the same as
|
||||
the embedded addon.
|
||||
|
||||
## Debug tools
|
||||
|
||||
@@ -470,16 +469,6 @@ that you use one of these commands:
|
||||
| `/tinspect <expr>` (or `/tins`) | Opens the table inspector on the result of `<expr>`. |
|
||||
| `/luaerrors` (or `/scripterrors`) | Opens the Lua error window. |
|
||||
|
||||
### Print functions
|
||||
|
||||
`!!!ClassicAPI` adds these globals:
|
||||
|
||||
| Global | Purpose |
|
||||
|--------|---------|
|
||||
| `print(...)` | Backport of Blizzard's `print`. It joins the arguments with `" "` and sends the result to `DEFAULT_CHAT_FRAME`. It uses the handler from `setprinthandler`. If that handler fails, it uses `geterrorhandler`. |
|
||||
| `setprinthandler(func)` / `getprinthandler()` | Sets or gets the print handler. Use it to send print output to a different place in tests. |
|
||||
| `tostringall(...)` | Applies `tostring()` to each argument, and keeps the number of arguments. |
|
||||
|
||||
## Building
|
||||
|
||||
Requires CMake (3.10+) and an MSVC toolchain that can target 32-bit Windows.
|
||||
|
||||
@@ -3809,7 +3809,7 @@ of descriptors feeds the in-game browser, a page generator, and a drift check.
|
||||
registration on the first pass of each Lua state, derives systems, and
|
||||
exports `C_APIDocumentation.GetSystems()` / `GetSystem(name)` lazily in
|
||||
Blizzard's exact table shape, plus `_classicapi_UndocumentedAPI()`.
|
||||
- `AddOns/!!!ClassicAPI/APIDocumentation/` — the 8-file port of
|
||||
- `src/embed/AddOns/!!!ClassicAPI/APIDocumentation/` — the 8-file port of
|
||||
`Blizzard_APIDocumentation`, driven by `/classicapi` (short `/capi`).
|
||||
Named for what it documents: the browser holds only ClassicAPI's own
|
||||
surface, so `/api` would have over-promised.
|
||||
|
||||
+2
-2
@@ -13719,7 +13719,7 @@ end)
|
||||
the next `step` after that many seconds. `yield(0)` is the
|
||||
"resume on the next frame" form (since `C_Timer.After(0, ...)`
|
||||
fires on the next OnUpdate tick). `RunNextFrame(cb)` in
|
||||
[`AddOns/!!!ClassicAPI/Util/FunctionUtil.lua`](../AddOns/!!!ClassicAPI/Util/FunctionUtil.lua)
|
||||
[`src/embed/AddOns/!!!ClassicAPI/Util/FunctionUtil.lua`](../src/embed/AddOns/!!!ClassicAPI/Util/FunctionUtil.lua)
|
||||
wraps the same primitive for the non-coroutine case.
|
||||
|
||||
## Macros
|
||||
@@ -22066,7 +22066,7 @@ Scent of Blood to its own length — live in `src/turtle/DurationMods.cpp`, and
|
||||
Carnage's roll-gated Rip/Rake refresh in `src/turtle/Carnage.cpp`.
|
||||
|
||||
The one rule registered from Lua
|
||||
([Util/AuraDurationModifiers.lua](../AddOns/!!!ClassicAPI/Util/AuraDurationModifiers.lua))
|
||||
([Util/AuraDurationModifiers.lua](../src/embed/AddOns/!!!ClassicAPI/Util/AuraDurationModifiers.lua))
|
||||
is Shadow Weaving: for a 5/5 priest (talent `15334`, where the proc is 100%),
|
||||
any shadow-school priest cast refreshes the target's Shadow Vulnerability
|
||||
(`15258`). Below 5/5 it's roll-gated and deliberately not inferred — the client
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
// basenames; returns nothing (mutation is the contract).
|
||||
//
|
||||
// Modern WoW's `IconDataProviderMixin` (see
|
||||
// `AddOns/!!!ClassicAPI/Util/IconDataProvider.lua`) calls all four
|
||||
// `src/embed/AddOns/!!!ClassicAPI/Util/IconDataProvider.lua`) calls all four
|
||||
// in this order to assemble its `BaseIconFilenames` cache:
|
||||
//
|
||||
// GetLooseMacroIcons(spellList)
|
||||
|
||||
Reference in New Issue
Block a user