Compare commits

..
Author SHA1 Message Date
Brues d021ae3e3d docs(faction): describe the reputation functions
Descriptors for GetFactionIDByIndex, GetFactionInfoByID,
GetFactionParentID, the 17 undocumented C_Reputation functions, the
FactionData structure, and the FACTION_STANDING_CHANGED event.
C_Reputation.IsFactionInactive and SetFactionInactive mirror stock
globals, but no Blizzard client has them under C_Reputation, so they
are marked as ClassicAPI extensions.
2026-10-30 02:34:17 -05:00
Brues f4862dc817 docs(equipmentset): describe the C_EquipmentSet functions
Descriptors for the 18 undocumented C_EquipmentSet functions,
GameTooltip:SetEquipmentSet, and the EQUIPMENT_SETS_CHANGED,
EQUIPMENT_SWAP_PENDING and EQUIPMENT_SWAP_FINISHED events. They
describe what the functions return here, which differs from retail in
places: an icon path, not a file ID; numMissing, not numLost;
GetIgnoredSlots lists slot numbers.
2026-10-30 02:34:17 -05:00
Brues 3a6f53f919 docs(aura): describe the C_UnitAuras functions
Descriptors for the 16 undocumented C_UnitAuras functions, plus the
AuraData structure that GetAuraDataByAuraInstanceID already named but
nothing defined. The four positional getters share the Classic Era
UnitAura return list.
2026-10-30 02:34:17 -05:00
Brues 19b299d3df docs(container): describe the C_Container functions
Descriptors for the 17 undocumented C_Container functions and the
ItemQuestInfo structure. SwapItems, MoveItem, AutoStoreItem,
GetContainerItemCharges, GetContainerItemRepairCost and
IsContainerItemOpenable are not in retail's API and are marked as
ClassicAPI extensions.
2026-10-30 02:34:17 -05:00
Brues 77b304ae68 feat(smf): RemoveMessagesByPredicate
ScrollingMessageFrame:RemoveMessagesByPredicate(predicate), retail's: the
predicate is called with each message's GetMessageInfo values, oldest
first, and the messages it returns true for are removed; the rest keep
their order. It covers 4.3.4's RemoveMessagesByAccessID / ByExtraData,
whose int fields the retail-shaped records here do not have.

1.12's ring has no compaction of its own; the removal is 4.3.4's
RemoveLines loop over it: every kept record moves forward in ring order to
the first free slot (freeing the text it lands on, nulling the one it
left), the position moves back by the removed count, and the
extra-argument tables move with their records. Then the engine's own
layout (0x00788750) from the record at the kept scroll offset, clamped to
the oldest, and its re-arm (0x00788B80) - a layout at the bottom keeps the
fade state, so 4.3.4 re-arms after - or its Clear (0x007882B0) when
nothing is kept.

The predicate runs for every message before anything moves, through the
engine's error handler; a predicate that errors keeps that message, as
retail's safesecurecall does. GetMessageInfo shares its record lookup and
value push with it.
2026-10-30 02:14:08 -05:00
27 changed files with 1089 additions and 100 deletions
+1 -1
View File
@@ -86,7 +86,7 @@ In the Frame rows, a name that starts with `:` is a method of the widget that th
| [Frame: StatusBar](docs/API.md#statusbarsetreversefillisreversefill--statusbargetreversefill) | `:SetReverseFill` / `:GetReverseFill`, `:SetRotatesTexture` / `:GetRotatesTexture`, `:SetFillStyle` / `:GetFillStyle`, `:SetColorFill`, `:SetStatusBarDesaturated` / `:IsStatusBarDesaturated`, `:SetStatusBarDesaturation` / `:GetStatusBarDesaturation` |
| [Frame: Slider](docs/API.md#sliderenable--sliderdisable--sliderisenabled) | `:Enable` / `:Disable` / `:IsEnabled` |
| [Frame: Cooldown](docs/API.md#createframecooldown--name-parent-template) | `CreateFrame("Cooldown")`, `:SetCooldown`, `:SetCooldownDuration`, `:SetCooldownFromExpirationTime`, `:Clear`, `:GetCooldownTimes`, `:GetCooldownDuration`, `:GetCooldownDisplayDuration`, `:Pause` / `:Resume`, `:SetPaused` / `:IsPaused`, `:SetReverse` / `:GetReverse`, `:SetDrawEdge` / `:GetDrawEdge`, `:SetDrawSwipe` / `:GetDrawSwipe`, `:SetDrawBling` / `:GetDrawBling`, `:SetSwipeColor`, `:SetEdgeColor`, `:SetSwipeTexture`, `:SetEdgeTexture`, `:SetBlingTexture`, `:SetRotation` / `:GetRotation`, `:SetHideCountdownNumbers` / `:GetHideCountdownNumbers`, `:GetCountdownFontString`, `:SetCountdownAbbrevThreshold` / `:GetCountdownAbbrevThreshold`, `:SetCountdownMillisecondsThreshold` / `:GetCountdownMillisecondsThreshold`, `:SetMinimumCountdownDuration` / `:GetMinimumCountdownDuration`, `OnCooldownDone`, `CooldownFrame_Set`, `CooldownFrame_Clear`, `CooldownFrame_SetDisplayAsPercentage` |
| [Frame: ScrollingMessageFrame](docs/API.md#smfsethyperlinksenabledenabled--smfgethyperlinksenabled) | `:SetHyperlinksEnabled` / `:GetHyperlinksEnabled`, `:GetMessageInfo` (with the extra `:AddMessage` values), `:BackFillMessage` (adds a message as the oldest), `:SetScrollOffset` / `:GetScrollOffset`, `:GetMaxScrollRange`, `:ScrollByAmount`, `:SetInsertMode` / `:GetInsertMode`, `:AddOnDisplayRefreshedCallback` |
| [Frame: ScrollingMessageFrame](docs/API.md#smfsethyperlinksenabledenabled--smfgethyperlinksenabled) | `:SetHyperlinksEnabled` / `:GetHyperlinksEnabled`, `:GetMessageInfo` (with the extra `:AddMessage` values), `:BackFillMessage` (adds a message as the oldest), `:RemoveMessagesByPredicate`, `:SetScrollOffset` / `:GetScrollOffset`, `:GetMaxScrollRange`, `:ScrollByAmount`, `:SetInsertMode` / `:GetInsertMode`, `:AddOnDisplayRefreshedCallback` |
| [Frame: scripts](docs/API.md#onenable-and-ondisable-scripts) | `OnEnable`, `OnDisable`, `OnMinMaxChanged`, `OnAttributeChanged`, `OnCharComposition`, `OnTextChanged` (`userInput`), `PreClick`, `PostClick`, `SetModernScriptArgs` / `GetModernScriptArgs` |
| [FriendList](docs/API.md#friendlist) | `AddOrRemoveFriend`, `C_FriendList.GetFriendInfo`, `C_FriendList.GetFriendInfoByIndex`, `C_FriendList.GetNumFriends`, `C_FriendList.GetNumOnlineFriends`, `C_FriendList.GetNumWhoResults`, `C_FriendList.GetWhoInfo`, `C_FriendList.IsFriend`, `C_FriendList.IsIgnored`, `C_FriendList.IsIgnoredByGuid`, `C_FriendList.IsWhoQueryPending`, `C_FriendList.SendWhoQueryByName`, `C_FriendList.SetFriendNotes`, `C_FriendList.SetFriendNotesByIndex` |
| [GameObject](docs/API.md#gameobject) | `C_GameObjectInfo.GetGameObjectInfoByID`, `C_GameObjectInfo.RequestLoadGameObjectByID`, `ClosestGameObjectPosition` |
+7 -2
View File
@@ -4090,8 +4090,13 @@ Each entry still needs the usual check of how the engine does it before building
`BackFillMessage` (writes 4.3.4's addToTop slot of the ring, the free one
before the oldest; a full frame drops it, as retail's CircularBuffer
`PushBack` does — 4.3.4 overwrites the oldest there).
- **ScrollingMessageFrame** `RemoveMessagesByAccessID` / `RemoveMessagesByExtraData` (4.3.4 C; retail
has `RemoveMessagesByPredicate`): 1.12 has no ring compaction.
- **DONE** (`src/scrollingmessageframe/MessageInfo.cpp`).
**ScrollingMessageFrame** `RemoveMessagesByPredicate` (retail's shape; it
covers 4.3.4's `RemoveMessagesByAccessID` / `RemoveMessagesByExtraData`,
whose int fields our retail-shaped records do not have). The compaction
is 4.3.4's `RemoveLines` loop over 1.12's ring, then the engine's own
layout (0x00788750) at the kept scroll offset and re-arm (0x00788B80),
or Clear (0x007882B0) when nothing is kept.
- **DONE** (`src/editbox/Focus.cpp`, `src/animation/Framerate.cpp`).
**Globals** (`SimpleFrameScript_Scripts.cpp`): `GetCurrentKeyBoardFocus()`
(the focused edit box's wrapper, or nil); `SetMaxAnimFramerate(fps)` /
+25
View File
@@ -387,6 +387,7 @@ build instructions.
- [`smf:SetHyperlinksEnabled(enabled)` / `smf:GetHyperlinksEnabled()`](#smfsethyperlinksenabledenabled--smfgethyperlinksenabled)
- [`smf:GetMessageInfo(index)`](#smfgetmessageinfoindex)
- [`smf:BackFillMessage(text, r, g, b, ...)`](#smfbackfillmessagetext-r-g-b-)
- [`smf:RemoveMessagesByPredicate(predicate)`](#smfremovemessagesbypredicatepredicate)
- [`smf:SetScrollOffset(offset)` / `smf:GetScrollOffset()` / `smf:GetMaxScrollRange()` / `smf:ScrollByAmount(amount)`](#smfsetscrolloffsetoffset--smfgetscrolloffset--smfgetmaxscrollrange--smfscrollbyamountamount)
- [`smf:SetInsertMode(mode)` / `smf:GetInsertMode()`](#smfsetinsertmodemode--smfgetinsertmode)
- [`smf:AddOnDisplayRefreshedCallback(callback)`](#smfaddondisplayrefreshedcallbackcallback)
@@ -8721,6 +8722,30 @@ ChatFrame1:BackFillMessage("older", 1, 1, 1)
ChatFrame1:GetMessageInfo(1) -- "older", 1, 1, 1
```
### `smf:RemoveMessagesByPredicate(predicate)`
On ScrollingMessageFrame. Removes the messages that `predicate` selects. The
function calls `predicate(text, r, g, b, ...)` for each message, from the
oldest to the newest, with the values that `GetMessageInfo` returns for the
message. If `predicate` returns true, the function removes the message. The
other messages keep their order. If `predicate` is not a function, the
function gives an error.
- The function calls `predicate` for all messages before it removes any
message. If `predicate` gives an error for a message, the frame keeps that
message.
- The frame keeps its scroll offset (`GetScrollOffset`). If fewer messages
remain than the offset, the offset becomes the oldest message.
- The frame shows the lines that remain at once. If no message remains, the
result is the same as `smf:Clear()`.
```lua
-- remove the messages that were added with the extra value 7
ChatFrame1:RemoveMessagesByPredicate(function(text, r, g, b, id)
return id == 7
end)
```
### `smf:SetScrollOffset(offset)` / `smf:GetScrollOffset()` / `smf:GetMaxScrollRange()` / `smf:ScrollByAmount(amount)`
On ScrollingMessageFrame. The scroll offset is the number of lines that the
+11
View File
@@ -12888,6 +12888,17 @@ enum Offsets {
// 0 when empty; the GetCurrentScroll method pushes it.
FUN_SCROLLINGMESSAGEFRAME_SCROLL_TO_OFFSET = 0x00788700,
FUN_SCROLLINGMESSAGEFRAME_SCROLL_OFFSET = 0x00788AC0,
// Clear (`void __thiscall(smf)`, 4.3.4's Clear): count 0, position 0, a
// layout from 0, +0x350 = 0, position -1. The Clear method's body.
FUN_SCROLLINGMESSAGEFRAME_CLEAR = 0x007882B0,
// RearmLines (`void __thiscall(smf)`, 4.3.4's RefreshMessages): every
// shown line back to alpha 0xFF, shown, the frame's two times, and its
// node shown (benilla-re ui/messageframe "Scrolling brings lines back").
// FUN_SMF_DISPLAY_REFRESH (LayoutLines) owns +0x348 (the scroll record)
// and +0x354 (at bottom = record == position) itself, and re-arms only
// when it moves off the bottom; a layout at the bottom keeps the fade
// state, so 4.3.4 re-arms explicitly after a removal's layout.
FUN_SCROLLINGMESSAGEFRAME_REARM_LINES = 0x00788B80,
FUN_SCRIPT_SCROLLINGMESSAGEFRAME_GET_CURRENT_SCROLL = 0x00793650,
// The frame's OnFrameUpdate override (vtable+0x38, see
// FUN_FRAME_ON_UPDATE), `void __thiscall(smf, float elapsed)`, `ret 4`:
+190 -14
View File
@@ -702,40 +702,216 @@ const Game::Doc::Function kCancelUnitBuff{
"Cancels one of your own auras by index.", kCancelUnitBuffArgs, {},
"SpellGlobals"};
const Game::Doc::Field kAuraDataFields[] = {
Game::Doc::Req("name", "string"),
Game::Doc::Req("icon", "string", "The icon's texture path."),
Game::Doc::Req("applications", "number", "The stack count."),
Game::Doc::Req("spellId", "number"),
Game::Doc::Req("dispelName", "string",
"Magic, Curse, Disease, or Poison; empty when the aura has no dispel type."),
Game::Doc::Req("isHelpful", "bool"),
Game::Doc::Req("isHarmful", "bool"),
Game::Doc::Req("duration", "number", "In seconds; 0 when the duration is not known."),
Game::Doc::Req("expirationTime", "number",
"When the aura ends, in GetTime() seconds; 0 when not known."),
Game::Doc::Opt("sourceUnit", "UnitToken", nullptr,
"Nil when the cast was not seen or the caster has no unit token."),
Game::Doc::Opt("sourceGUID", "WOWGUID", nullptr,
"The caster's GUID, a ClassicAPI extension; nil when the cast was not seen."),
Game::Doc::Opt("auraInstanceID", "number", nullptr,
"Nil for an aura that is not in the unit's aura list."),
Game::Doc::Req("charges", "number", "Always 0."),
Game::Doc::Req("maxCharges", "number", "Always 0."),
Game::Doc::Req("timeMod", "number", "Always 1."),
Game::Doc::Req("isFromPlayerOrPlayerPet", "bool",
"True when any player or a player's pet applied the aura."),
Game::Doc::Req("canApplyAura", "bool",
"True when the player or the pet knows the spell that applies the aura."),
Game::Doc::Req("isRaid", "bool", "True when the aura passes the RAID filter."),
Game::Doc::Req("isStealable", "bool",
"True when the player can remove this buff from an enemy."),
Game::Doc::Req("isBossAura", "bool", "Always false."),
Game::Doc::Req("isNameplateOnly", "bool", "Always false."),
Game::Doc::Req("nameplateShowAll", "bool", "Always false."),
Game::Doc::Req("nameplateShowPersonal", "bool", "Always false."),
Game::Doc::Req("shouldConsolidate", "bool", "Always false."),
};
const Game::Doc::Structure kAuraData{"AuraData", "UnitAuras", kAuraDataFields,
"One aura on a unit."};
// The Classic Era `UnitAura` tuple, shared by the four positional getters.
// `Data::PushPositional` is the source of the order.
const Game::Doc::Field kPositionalRets[] = {
Game::Doc::Opt("name", "string", nullptr,
"Nil, as the only return, when there is no such aura."),
Game::Doc::Opt("icon", "string"),
Game::Doc::Opt("count", "number"),
Game::Doc::Opt("dispelType", "string", nullptr,
"Empty when the aura has no dispel type."),
Game::Doc::Opt("duration", "number"),
Game::Doc::Opt("expirationTime", "number"),
Game::Doc::Opt("source", "UnitToken", nullptr, "Nil when the caster is not known."),
Game::Doc::Opt("isStealable", "bool"),
Game::Doc::Opt("nameplateShowPersonal", "bool", nullptr, "Always false."),
Game::Doc::Opt("spellId", "number"),
Game::Doc::Opt("canApplyAura", "bool"),
Game::Doc::Opt("isBossDebuff", "bool", nullptr, "Always false."),
Game::Doc::Opt("castByPlayer", "bool", nullptr,
"True when a player applied the aura; false for a pet."),
Game::Doc::Opt("nameplateShowAll", "bool", nullptr, "Always false."),
Game::Doc::Opt("timeMod", "number", nullptr, "Always 1."),
};
const Game::Doc::Field kAuraRets[] = {
Game::Doc::Opt("aura", "AuraData", nullptr, "Nil when there is no such aura."),
};
const Game::Doc::Field kByIndexArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Req("index", "luaIndex"),
Game::Doc::Opt("filter", "AuraFilters", nullptr,
"Tokens such as HELPFUL, HARMFUL, PLAYER, RAID; HELPFUL when no range is given."),
};
const Game::Doc::Function kGetAuraDataByIndex{
"Returns the aura at an index among a unit's auras that match a filter.", kByIndexArgs,
kAuraRets};
const Game::Doc::Function kUnitAura{
"Returns the aura at an index as positional values, without making a table.",
kByIndexArgs, kPositionalRets};
const Game::Doc::Field kLockedIndexArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Req("index", "luaIndex"),
};
const Game::Doc::Function kGetBuffDataByIndex{
"Returns the buff at an index on a unit.", kLockedIndexArgs, kAuraRets};
const Game::Doc::Function kGetDebuffDataByIndex{
"Returns the debuff at an index on a unit.", kLockedIndexArgs, kAuraRets};
const Game::Doc::Field kLockedFilterArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Req("index", "luaIndex"),
Game::Doc::Opt("filter", "AuraFilters", nullptr,
"Further tokens such as PLAYER or RAID; the range is fixed."),
};
const Game::Doc::Function kUnitBuff{
"Returns the buff at an index as positional values, without making a table.",
kLockedFilterArgs, kPositionalRets};
const Game::Doc::Function kUnitDebuff{
"Returns the debuff at an index as positional values, without making a table.",
kLockedFilterArgs, kPositionalRets};
const Game::Doc::Field kSlotsArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Opt("filter", "AuraFilters"),
Game::Doc::Opt("maxSlots", "number", nullptr, "Nil or 0 returns all of them."),
Game::Doc::Opt("continuationToken", "number", nullptr,
"The first return of the previous call, to get the next batch."),
Game::Doc::Opt("buf", "table", nullptr,
"A table to fill with the slot IDs instead; the call then returns the "
"token and the count. A ClassicAPI extension."),
};
const Game::Doc::Field kSlotsRets[] = {
Game::Doc::Opt("outContinuationToken", "number", nullptr,
"Nil when this batch reaches the last aura."),
Game::Doc::Vararg("number", "The slot IDs, valid only in the frame they are returned."),
};
const Game::Doc::Function kGetAuraSlots{
"Returns the slot IDs of a unit's auras that match a filter, in batches.", kSlotsArgs,
kSlotsRets};
const Game::Doc::Field kBySlotArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Req("slot", "number", "A slot ID from C_UnitAuras.GetAuraSlots."),
};
const Game::Doc::Function kGetAuraDataBySlot{
"Returns the aura that a slot ID names.", kBySlotArgs, kAuraRets};
const Game::Doc::Function kUnitAuraBySlot{
"Returns the aura that a slot ID names as positional values, without making a table.",
kBySlotArgs, kPositionalRets};
const Game::Doc::Field kBySpellIDArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Req("spellID", "number"),
Game::Doc::Opt("filter", "AuraFilters", nullptr, "Nil searches buffs, then debuffs."),
};
const Game::Doc::Function kGetUnitAuraBySpellID{
"Returns the first aura on a unit with a spell ID; the player's own aura comes first.",
kBySpellIDArgs, kAuraRets};
const Game::Doc::Field kPlayerBySpellIDArgs[] = {
Game::Doc::Req("spellID", "number"),
};
const Game::Doc::Function kGetPlayerAuraBySpellID{
"Returns the first aura on the player with a spell ID.", kPlayerBySpellIDArgs, kAuraRets};
const Game::Doc::Field kBySpellNameArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Req("spellName", "cstring", "The localized name; case does not matter."),
Game::Doc::Opt("filter", "AuraFilters", nullptr, "Nil searches buffs, then debuffs."),
};
const Game::Doc::Function kGetAuraDataBySpellName{
"Returns the first aura on a unit with a spell name; the player's own aura comes first.",
kBySpellNameArgs, kAuraRets};
const Game::Doc::Field kUnitAurasArgs[] = {
Game::Doc::Req("unit", "UnitToken"),
Game::Doc::Opt("filter", "AuraFilters", nullptr,
"With no HELPFUL or HARMFUL token, both are returned."),
};
const Game::Doc::Field kUnitAurasRets[] = {
Game::Doc::Req("auras", "table", "An array of AuraData; empty when the unit has none."),
};
const Game::Doc::Function kGetUnitAuras{
"Returns all of a unit's auras that match a filter, buffs first.", kUnitAurasArgs,
kUnitAurasRets};
const Game::Doc::Field kDispelColorArgs[] = {
Game::Doc::Opt("dispelName", "string", nullptr,
"Magic, Curse, Disease, Poison, or Enrage; any other value gives the "
"no-type color."),
};
const Game::Doc::Field kDispelColorRets[] = {
Game::Doc::Req("color", "colorRGBA"),
};
const Game::Doc::Function kGetAuraDispelTypeColor{
"Returns the border color for an aura's dispel type.", kDispelColorArgs,
kDispelColorRets};
} // namespace
static void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetAuraDataByIndex",
&Script_GetAuraDataByIndex);
&Script_GetAuraDataByIndex, &kGetAuraDataByIndex);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetBuffDataByIndex",
&Script_GetBuffDataByIndex);
&Script_GetBuffDataByIndex, &kGetBuffDataByIndex);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetDebuffDataByIndex",
&Script_GetDebuffDataByIndex);
&Script_GetDebuffDataByIndex, &kGetDebuffDataByIndex);
Game::Lua::RegisterTableFunction("C_UnitAuras", "UnitAura",
&Script_UnitAura);
&Script_UnitAura, &kUnitAura);
Game::Lua::RegisterTableFunction("C_UnitAuras", "UnitBuff",
&Script_UnitBuff);
&Script_UnitBuff, &kUnitBuff);
Game::Lua::RegisterTableFunction("C_UnitAuras", "UnitDebuff",
&Script_UnitDebuff);
&Script_UnitDebuff, &kUnitDebuff);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetAuraSlots",
&Script_GetAuraSlots);
&Script_GetAuraSlots, &kGetAuraSlots);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetAuraDataBySlot",
&Script_GetAuraDataBySlot);
&Script_GetAuraDataBySlot, &kGetAuraDataBySlot);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetAuraDataByAuraInstanceID",
&Script_GetAuraDataByAuraInstanceID,
&kGetAuraDataByAuraInstanceID);
Game::Lua::RegisterTableFunction("C_UnitAuras", "UnitAuraBySlot",
&Script_UnitAuraBySlot);
&Script_UnitAuraBySlot, &kUnitAuraBySlot);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetUnitAuraBySpellID",
&Script_GetUnitAuraBySpellID);
&Script_GetUnitAuraBySpellID, &kGetUnitAuraBySpellID);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetPlayerAuraBySpellID",
&Script_GetPlayerAuraBySpellID);
&Script_GetPlayerAuraBySpellID, &kGetPlayerAuraBySpellID);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetAuraDataBySpellName",
&Script_GetAuraDataBySpellName);
&Script_GetAuraDataBySpellName, &kGetAuraDataBySpellName);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetUnitAuras",
&Script_GetUnitAuras);
&Script_GetUnitAuras, &kGetUnitAuras);
Game::Lua::RegisterTableFunction("C_UnitAuras", "GetAuraDispelTypeColor",
&Script_GetAuraDispelTypeColor);
&Script_GetAuraDispelTypeColor, &kGetAuraDispelTypeColor);
Game::Lua::RegisterGlobalFunction("CancelUnitBuff", &Script_CancelUnitBuff,
&kCancelUnitBuff);
}
+15 -1
View File
@@ -193,9 +193,23 @@ int __fastcall Script_RegisterComboDuration(void *L) {
return 1;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("spellID", "number"),
Game::Doc::Req("baseSeconds", "number",
"The duration at zero combo points; each point adds a fifth of the "
"difference to maxSeconds."),
Game::Doc::Req("maxSeconds", "number", "The duration at five combo points."),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("success", "bool"),
};
const Game::Doc::Function kRegisterComboDuration{
"Sets the combo point durations of a finisher whose client data is missing or wrong.",
kArgs, kRets, nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_UnitAuras", "RegisterComboDuration",
&Script_RegisterComboDuration);
&Script_RegisterComboDuration, &kRegisterComboDuration);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+22 -1
View File
@@ -946,10 +946,31 @@ int __fastcall Script_RegisterAuraDurationModifierByTrigger(void *L) {
return 1;
}
const Game::Doc::Field kDurationModArgs[] = {
Game::Doc::Req("triggerFamily", "number", "The SpellFamilyName of the triggering cast."),
Game::Doc::Req("triggerSchool", "number",
"The school index the trigger must have, or below 0 for any school."),
Game::Doc::Req("affectedFamily", "number", "The SpellFamilyName of the aura to change."),
Game::Doc::Req("affectedFamilyFlags", "number",
"A SpellFamilyFlags mask the aura must overlap, or 0 to match on the icon."),
Game::Doc::Req("affectedIcon", "number",
"The SpellIconID the aura must have, or 0 for any icon."),
Game::Doc::Req("op", "string", "refresh, reduce, set, or remove."),
Game::Doc::Opt("valueSeconds", "number", nullptr, "The amount for reduce and set."),
};
const Game::Doc::Field kDurationModRets[] = {
Game::Doc::Req("success", "bool"),
};
const Game::Doc::Function kRegisterAuraDurationModifierByTrigger{
"Changes the shown duration of the caster's aura on a unit when a matching cast by "
"that caster lands.",
kDurationModArgs, kDurationModRets, nullptr, true};
void RegisterDurationModLua() {
Game::Lua::RegisterTableFunction("C_UnitAuras",
"RegisterAuraDurationModifierByTrigger",
&Script_RegisterAuraDurationModifierByTrigger);
&Script_RegisterAuraDurationModifierByTrigger,
&kRegisterAuraDurationModifierByTrigger);
}
const Game::ModuleAutoRegister _autoregDurationMod{&RegisterDurationModLua};
+15 -1
View File
@@ -62,9 +62,23 @@ int __fastcall Script_C_Container_AutoStoreItem(void *L) {
return 1;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("srcBag", "BagIndex"),
Game::Doc::Req("srcSlot", "luaIndex"),
Game::Doc::Opt("dstBag", "BagIndex", "0",
"0 for anywhere in the bags, 1 to 4 for one bag, -1 for the bank."),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("sent", "bool", "False when the server cannot be asked for this move."),
};
const Game::Doc::Function kAutoStoreItem{
"Moves an item and lets the server choose where it goes, merging it into "
"existing stacks first.",
kArgs, kRets, nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "AutoStoreItem",
&Script_C_Container_AutoStoreItem);
&Script_C_Container_AutoStoreItem, &kAutoStoreItem);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+13 -1
View File
@@ -43,9 +43,21 @@ int __fastcall Script_C_Container_GetContainerItemCharges(void *L) {
L, Item::Location::ResolveBag(L, bagID, slotIndex));
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Opt("uses", "number", nullptr, "Nil when the slot is empty."),
};
const Game::Doc::Function kGetContainerItemCharges{
"Returns the uses left on the item in a bag slot: its charges times its stack size.",
kArgs, kRets, nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetContainerItemCharges",
&Script_C_Container_GetContainerItemCharges);
&Script_C_Container_GetContainerItemCharges,
&kGetContainerItemCharges);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+14 -1
View File
@@ -33,9 +33,22 @@ int __fastcall Script_C_Container_GetItemCooldown(void *L) {
return 3;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("itemID", "number"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("startTime", "number",
"When the cooldown started, in GetTime() seconds; 0 when none."),
Game::Doc::Req("duration", "number", "Length in seconds; 0 when there is no cooldown."),
Game::Doc::Req("enable", "number",
"0 while the cooldown waits to start, as for a potion used in combat; else 1."),
};
const Game::Doc::Function kGetItemCooldown{
"Returns the cooldown of an item's use effect.", kArgs, kRets};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetItemCooldown",
&Script_C_Container_GetItemCooldown);
&Script_C_Container_GetItemCooldown, &kGetItemCooldown);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+14 -1
View File
@@ -40,9 +40,22 @@ int __fastcall Script_C_Container_GetContainerItemDurability(void *L) {
L, Item::Location::ResolveBag(L, bagID, slotIndex));
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Opt("durability", "number", nullptr,
"Nil when the slot is empty or the item has no durability."),
Game::Doc::Opt("maxDurability", "number"),
};
const Game::Doc::Function kGetContainerItemDurability{
"Returns the current and maximum durability of the item in a bag slot.", kArgs, kRets};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetContainerItemDurability",
&Script_C_Container_GetContainerItemDurability);
&Script_C_Container_GetContainerItemDurability,
&kGetContainerItemDurability);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+16 -1
View File
@@ -80,10 +80,25 @@ int __fastcall Script_C_Container_GetContainerItemEquipmentSetInfo(void *L) {
return 2;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("inSet", "bool"),
Game::Doc::Opt("setList", "string", nullptr,
"The set names joined by PLAYER_LIST_DELIMITER; nil when inSet is false."),
};
const Game::Doc::Function kGetContainerItemEquipmentSetInfo{
"Returns whether the item in a bag slot is saved in an equipment set, and the "
"names of those sets.",
kArgs, kRets};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction(
"C_Container", "GetContainerItemEquipmentSetInfo",
&Script_C_Container_GetContainerItemEquipmentSetInfo);
&Script_C_Container_GetContainerItemEquipmentSetInfo,
&kGetContainerItemEquipmentSetInfo);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+33 -3
View File
@@ -182,15 +182,45 @@ int __fastcall Script_C_Container_CalculateTotalNumberOfFreeBagSlots(void *L) {
return 1;
}
const Game::Doc::Field kBagArgs[] = {
Game::Doc::Req("bagIndex", "BagIndex", "0 for the backpack, 1 to 4 for the equipped bags."),
};
const Game::Doc::Field kNumFreeSlotsRets[] = {
Game::Doc::Req("numFreeSlots", "number", "0 when the bag does not exist."),
Game::Doc::Req("bagFamily", "number",
"The bag's family bitmask; 0 for a bag that takes any item."),
};
const Game::Doc::Function kGetContainerNumFreeSlots{
"Returns the number of empty slots in a bag and the kind of items the bag takes.",
kBagArgs, kNumFreeSlotsRets};
const Game::Doc::Field kFreeSlotsRets[] = {
Game::Doc::Opt("freeSlots", "table", nullptr,
"The empty slot numbers in ascending order; nil when the bag does not exist."),
};
const Game::Doc::Function kGetContainerFreeSlots{
"Returns the numbers of the empty slots in a bag.", kBagArgs, kFreeSlotsRets};
const Game::Doc::Field kTotalRets[] = {
Game::Doc::Req("totalFreeSlots", "number"),
};
const Game::Doc::Function kCalculateTotalNumberOfFreeBagSlots{
"Returns the number of empty slots in the backpack and the equipped bags that take "
"any item.",
{}, kTotalRets};
} // namespace
static void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetContainerNumFreeSlots",
&Script_C_Container_GetContainerNumFreeSlots);
&Script_C_Container_GetContainerNumFreeSlots,
&kGetContainerNumFreeSlots);
Game::Lua::RegisterTableFunction("C_Container", "GetContainerFreeSlots",
&Script_C_Container_GetContainerFreeSlots);
&Script_C_Container_GetContainerFreeSlots,
&kGetContainerFreeSlots);
Game::Lua::RegisterTableFunction("C_Container", "CalculateTotalNumberOfFreeBagSlots",
&Script_C_Container_CalculateTotalNumberOfFreeBagSlots);
&Script_C_Container_CalculateTotalNumberOfFreeBagSlots,
&kCalculateTotalNumberOfFreeBagSlots);
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+12 -1
View File
@@ -40,9 +40,20 @@ int __fastcall Script_C_Container_HasContainerItem(void *L) {
return 1;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("hasItem", "bool"),
};
const Game::Doc::Function kHasContainerItem{
"Returns whether a bag slot holds an item, even one whose data is not cached yet.",
kArgs, kRets};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "HasContainerItem",
&Script_C_Container_HasContainerItem);
&Script_C_Container_HasContainerItem, &kHasContainerItem);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+16 -2
View File
@@ -117,13 +117,27 @@ int __fastcall Script_C_Container_UseHearthstone(void *L) {
return 1;
}
const Game::Doc::Field kHasRets[] = {
Game::Doc::Opt("itemID", "number", nullptr, "Nil when no hearthstone is in the bags."),
};
const Game::Doc::Function kPlayerHasHearthstone{
"Returns the ID of the hearthstone in the player's bags.", {}, kHasRets};
const Game::Doc::Field kUseRets[] = {
Game::Doc::Req("used", "bool",
"True when a hearthstone was found; the cast itself can still fail."),
};
const Game::Doc::Function kUseHearthstone{"Uses the hearthstone in the player's bags.", {},
kUseRets};
} // namespace
static void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "PlayerHasHearthstone",
&Script_C_Container_PlayerHasHearthstone);
&Script_C_Container_PlayerHasHearthstone,
&kPlayerHasHearthstone);
Game::Lua::RegisterTableFunction("C_Container", "UseHearthstone",
&Script_C_Container_UseHearthstone);
&Script_C_Container_UseHearthstone, &kUseHearthstone);
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+11 -1
View File
@@ -41,9 +41,19 @@ int __fastcall Script_C_Container_GetContainerItemID(void *L) {
return 1;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Opt("itemID", "number", nullptr, "Nil when the slot is empty."),
};
const Game::Doc::Function kGetContainerItemID{"Returns the ID of the item in a bag slot.",
kArgs, kRets};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetContainerItemID",
&Script_C_Container_GetContainerItemID);
&Script_C_Container_GetContainerItemID, &kGetContainerItemID);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+16 -2
View File
@@ -57,9 +57,23 @@ int __fastcall Script_C_Container_MoveItem(void *L) {
return 1;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("srcBag", "BagIndex"),
Game::Doc::Req("srcSlot", "luaIndex"),
Game::Doc::Req("dstBag", "BagIndex"),
Game::Doc::Req("dstSlot", "luaIndex"),
Game::Doc::Req("count", "number", "From 1 to 255."),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("sent", "bool", "False when an argument is not valid."),
};
const Game::Doc::Function kMoveItem{
"Moves part of a stack to another bag slot without the cursor.", kArgs, kRets, nullptr,
true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "MoveItem",
&Script_C_Container_MoveItem);
Game::Lua::RegisterTableFunction("C_Container", "MoveItem", &Script_C_Container_MoveItem,
&kMoveItem);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+16 -1
View File
@@ -37,9 +37,24 @@ int __fastcall Script_C_Container_IsContainerItemOpenable(void *L) {
L, Item::Location::ResolveBag(L, bagID, slotIndex));
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Opt("isOpenable", "bool", nullptr,
"Nil when the slot is empty or the item's data is not cached yet."),
Game::Doc::Opt("canOpen", "bool", nullptr, "False while the item is still locked."),
};
const Game::Doc::Function kIsContainerItemOpenable{
"Returns whether the item in a bag slot can be opened, and whether the player can "
"open it now.",
kArgs, kRets, nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "IsContainerItemOpenable",
&Script_C_Container_IsContainerItemOpenable);
&Script_C_Container_IsContainerItemOpenable,
&kIsContainerItemOpenable);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+21 -1
View File
@@ -97,9 +97,29 @@ int __fastcall Script_C_Container_GetContainerItemQuestInfo(void *L) {
return 1;
}
const Game::Doc::Field kItemQuestInfoFields[] = {
Game::Doc::Req("isQuestItem", "bool"),
Game::Doc::Opt("questID", "number", nullptr,
"The quest the item begins; nil when it begins none."),
Game::Doc::Req("isActive", "bool", "True when the quest in questID is in the quest log."),
};
const Game::Doc::Structure kItemQuestInfo{"ItemQuestInfo", "Container", kItemQuestInfoFields};
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("questInfo", "ItemQuestInfo",
"Always a table; an empty slot reports no quest."),
};
const Game::Doc::Function kGetContainerItemQuestInfo{
"Returns how the item in a bag slot relates to quests.", kArgs, kRets};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetContainerItemQuestInfo",
&Script_C_Container_GetContainerItemQuestInfo);
&Script_C_Container_GetContainerItemQuestInfo,
&kGetContainerItemQuestInfo);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+13 -1
View File
@@ -37,9 +37,21 @@ int __fastcall Script_C_Container_GetContainerItemRepairCost(void *L) {
L, Item::Location::ResolveBag(L, bagID, slotIndex));
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("containerIndex", "BagIndex"),
Game::Doc::Req("slotIndex", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("repairCost", "number", "In copper; 0 when the item needs no repair."),
};
const Game::Doc::Function kGetContainerItemRepairCost{
"Returns what a merchant charges to repair the item in a bag slot.", kArgs, kRets,
nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "GetContainerItemRepairCost",
&Script_C_Container_GetContainerItemRepairCost);
&Script_C_Container_GetContainerItemRepairCost,
&kGetContainerItemRepairCost);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+14 -2
View File
@@ -49,9 +49,21 @@ int __fastcall Script_C_Container_SwapItems(void *L) {
return 1;
}
const Game::Doc::Field kArgs[] = {
Game::Doc::Req("srcBag", "BagIndex"),
Game::Doc::Req("srcSlot", "luaIndex"),
Game::Doc::Req("dstBag", "BagIndex"),
Game::Doc::Req("dstSlot", "luaIndex"),
};
const Game::Doc::Field kRets[] = {
Game::Doc::Req("sent", "bool", "False when an argument is not valid."),
};
const Game::Doc::Function kSwapItems{
"Swaps the contents of two bag slots without the cursor.", kArgs, kRets, nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Container", "SwapItems",
&Script_C_Container_SwapItems);
Game::Lua::RegisterTableFunction("C_Container", "SwapItems", &Script_C_Container_SwapItems,
&kSwapItems);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+152 -18
View File
@@ -543,46 +543,180 @@ int __fastcall Script_UseEquipmentSet(void *L) {
return 1;
}
const Game::Doc::Field kSetIDArgs[] = {
Game::Doc::Req("setID", "number"),
};
const Game::Doc::Field kSlotArgs[] = {
Game::Doc::Req("slot", "luaIndex", "An equipment slot, from 1 to 19."),
};
const Game::Doc::Field kCanUseRets[] = {
Game::Doc::Req("canUseEquipmentSets", "bool", "False while a set swap is in progress."),
};
const Game::Doc::Function kCanUseEquipmentSets{
"Returns whether an equipment set can be used now.", {}, kCanUseRets};
const Game::Doc::Field kNumRets[] = {
Game::Doc::Req("numEquipmentSets", "number"),
};
const Game::Doc::Function kGetNumEquipmentSets{
"Returns the number of equipment sets the character has.", {}, kNumRets};
const Game::Doc::Field kIDsRets[] = {
Game::Doc::Req("setIDs", "table", "The set IDs in the order the sets were created."),
};
const Game::Doc::Function kGetEquipmentSetIDs{
"Returns the IDs of all the character's equipment sets.", {}, kIDsRets};
const Game::Doc::Field kIDArgs[] = {
Game::Doc::Req("name", "cstring", "The exact name; case matters."),
};
const Game::Doc::Field kIDRets[] = {
Game::Doc::Opt("setID", "number", nullptr, "Nil when no set has that name."),
};
const Game::Doc::Function kGetEquipmentSetID{
"Returns the ID of the equipment set with a name.", kIDArgs, kIDRets};
const Game::Doc::Field kInfoRets[] = {
Game::Doc::Opt("name", "string", nullptr,
"Nil, with the rest, when the set does not exist."),
Game::Doc::Opt("icon", "string", nullptr, "The icon's texture name."),
Game::Doc::Opt("setID", "number"),
Game::Doc::Opt("isEquipped", "bool", nullptr,
"True when every slot that is not ignored holds what the set saved."),
Game::Doc::Opt("numItems", "number", nullptr,
"numEquipped plus numInInventory plus numMissing."),
Game::Doc::Opt("numEquipped", "number"),
Game::Doc::Opt("numInInventory", "number", nullptr,
"In the bags, or in the bank while the bank is open."),
Game::Doc::Opt("numMissing", "number"),
Game::Doc::Opt("numIgnored", "number"),
};
const Game::Doc::Function kGetEquipmentSetInfo{
"Returns an equipment set's name, icon, and where its items are.", kSetIDArgs,
kInfoRets};
const Game::Doc::Field kIgnoredRets[] = {
Game::Doc::Opt("ignoredSlots", "table", nullptr,
"The numbers of the ignored slots; nil when the set does not exist."),
};
const Game::Doc::Function kGetIgnoredSlots{
"Returns the slots an equipment set leaves alone.", kSetIDArgs, kIgnoredRets};
const Game::Doc::Field kItemIDsRets[] = {
Game::Doc::Opt("itemIDs", "table", nullptr,
"Keyed by slot: the item ID, 0 for an empty slot, 1 for an ignored slot, "
"or -1 when not known."),
};
const Game::Doc::Function kGetItemIDs{
"Returns the item ID an equipment set saved for each slot.", kSetIDArgs, kItemIDsRets};
const Game::Doc::Field kLocationsRets[] = {
Game::Doc::Opt("locations", "table", nullptr,
"Keyed by slot: a packed location, 0 for an empty slot, 1 for an ignored "
"slot, or -1 for a missing item."),
};
const Game::Doc::Function kGetItemLocations{
"Returns where each item of an equipment set is now.", kSetIDArgs, kLocationsRets};
const Game::Doc::Field kCreateArgs[] = {
Game::Doc::Req("name", "cstring", "Shorter than 32 characters, with no line break."),
Game::Doc::Opt("icon", "string", "\"INV_Misc_QuestionMark\"", "A texture name."),
};
const Game::Doc::Field kCreateRets[] = {
Game::Doc::Opt("setID", "number", nullptr, "Nil when the name is already in use."),
};
const Game::Doc::Function kCreateEquipmentSet{
"Saves the equipped items as a new equipment set.", kCreateArgs, kCreateRets};
const Game::Doc::Field kSaveArgs[] = {
Game::Doc::Req("setID", "number"),
Game::Doc::Opt("icon", "string", nullptr, "A new texture name; nil keeps the icon."),
};
const Game::Doc::Function kSaveEquipmentSet{
"Saves the equipped items into an existing equipment set.", kSaveArgs, {}};
const Game::Doc::Field kModifyArgs[] = {
Game::Doc::Req("setID", "number"),
Game::Doc::Req("newName", "cstring"),
};
const Game::Doc::Function kModifyEquipmentSet{
"Renames an equipment set; does nothing when another set has the name.", kModifyArgs,
{}};
const Game::Doc::Function kDeleteEquipmentSet{
"Deletes an equipment set and removes it from the action bars.", kSetIDArgs, {}};
const Game::Doc::Function kIgnoreSlotForSave{
"Makes the next save of an equipment set leave a slot alone.", kSlotArgs, {}};
const Game::Doc::Function kUnignoreSlotForSave{
"Makes the next save of an equipment set include a slot again.", kSlotArgs, {}};
const Game::Doc::Field kIsIgnoredRets[] = {
Game::Doc::Req("isSlotIgnored", "bool"),
};
const Game::Doc::Function kIsSlotIgnoredForSave{
"Returns whether the next save of an equipment set leaves a slot alone.", kSlotArgs,
kIsIgnoredRets};
const Game::Doc::Function kClearIgnoredSlotsForSave{
"Makes the next save of an equipment set include every slot.", {}, {}};
const Game::Doc::Field kLockedRets[] = {
Game::Doc::Opt("hasLockedItems", "bool", nullptr, "Nil when the set does not exist."),
};
const Game::Doc::Function kEquipmentSetContainsLockedItems{
"Returns whether an item of an equipment set is locked by a move in progress.",
kSetIDArgs, kLockedRets};
const Game::Doc::Field kUseRets[] = {
Game::Doc::Req("setWasEquipped", "bool",
"True when the swap starts; EQUIPMENT_SWAP_FINISHED gives the result."),
};
const Game::Doc::Function kUseEquipmentSet{
"Equips the items of an equipment set.", kSetIDArgs, kUseRets};
} // namespace
static void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_EquipmentSet", "CanUseEquipmentSets",
&Script_CanUseEquipmentSets);
&Script_CanUseEquipmentSets, &kCanUseEquipmentSets);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetNumEquipmentSets",
&Script_GetNumEquipmentSets);
&Script_GetNumEquipmentSets, &kGetNumEquipmentSets);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetEquipmentSetIDs",
&Script_GetEquipmentSetIDs);
&Script_GetEquipmentSetIDs, &kGetEquipmentSetIDs);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetEquipmentSetID",
&Script_GetEquipmentSetID);
&Script_GetEquipmentSetID, &kGetEquipmentSetID);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetEquipmentSetInfo",
&Script_GetEquipmentSetInfo);
&Script_GetEquipmentSetInfo, &kGetEquipmentSetInfo);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetIgnoredSlots",
&Script_GetIgnoredSlots);
&Script_GetIgnoredSlots, &kGetIgnoredSlots);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetItemIDs",
&Script_GetItemIDs);
&Script_GetItemIDs, &kGetItemIDs);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "GetItemLocations",
&Script_GetItemLocations);
&Script_GetItemLocations, &kGetItemLocations);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "CreateEquipmentSet",
&Script_CreateEquipmentSet);
&Script_CreateEquipmentSet, &kCreateEquipmentSet);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "SaveEquipmentSet",
&Script_SaveEquipmentSet);
&Script_SaveEquipmentSet, &kSaveEquipmentSet);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "ModifyEquipmentSet",
&Script_ModifyEquipmentSet);
&Script_ModifyEquipmentSet, &kModifyEquipmentSet);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "DeleteEquipmentSet",
&Script_DeleteEquipmentSet);
&Script_DeleteEquipmentSet, &kDeleteEquipmentSet);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "IgnoreSlotForSave",
&Script_IgnoreSlotForSave);
&Script_IgnoreSlotForSave, &kIgnoreSlotForSave);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "UnignoreSlotForSave",
&Script_UnignoreSlotForSave);
&Script_UnignoreSlotForSave, &kUnignoreSlotForSave);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "IsSlotIgnoredForSave",
&Script_IsSlotIgnoredForSave);
&Script_IsSlotIgnoredForSave, &kIsSlotIgnoredForSave);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "ClearIgnoredSlotsForSave",
&Script_ClearIgnoredSlotsForSave);
&Script_ClearIgnoredSlotsForSave,
&kClearIgnoredSlotsForSave);
Game::Lua::RegisterTableFunction("C_EquipmentSet",
"EquipmentSetContainsLockedItems",
&Script_EquipmentSetContainsLockedItems);
&Script_EquipmentSetContainsLockedItems,
&kEquipmentSetContainsLockedItems);
Game::Lua::RegisterTableFunction("C_EquipmentSet", "UseEquipmentSet",
&Script_UseEquipmentSet);
&Script_UseEquipmentSet, &kUseEquipmentSet);
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+25 -3
View File
@@ -151,8 +151,30 @@ constexpr int LOC_IGNORED = 1;
// when the swap settles — see
// `EquipmentSet::Pending`. `success` is
// 1 or nil, never 0 (a Lua 0 is true).
inline const Event::Custom::AutoReserve kEvtSetsChanged{"EQUIPMENT_SETS_CHANGED"};
inline const Event::Custom::AutoReserve kEvtSwapPending{"EQUIPMENT_SWAP_PENDING"};
inline const Event::Custom::AutoReserve kEvtSwapFinished{"EQUIPMENT_SWAP_FINISHED"};
inline const Game::Doc::Event kSetsChangedDoc{
"EquipmentSet",
"Fires when an equipment set is created, saved, renamed, or deleted, or when the slots "
"ignored for the next save change.",
{}};
inline const Game::Doc::Field kSwapPendingPayload[] = {
Game::Doc::Req("setID", "number"),
};
inline const Game::Doc::Event kSwapPendingDoc{
"EquipmentSet", "Fires when C_EquipmentSet.UseEquipmentSet has sent all its moves.",
kSwapPendingPayload};
inline const Game::Doc::Field kSwapFinishedPayload[] = {
Game::Doc::Opt("result", "number", nullptr, "1 when the swap succeeded, else nil."),
Game::Doc::Req("setID", "number"),
};
inline const Game::Doc::Event kSwapFinishedDoc{
"EquipmentSet", "Fires when the moves of an equipment set swap are done.",
kSwapFinishedPayload};
inline const Event::Custom::AutoReserve kEvtSetsChanged{"EQUIPMENT_SETS_CHANGED",
&kSetsChangedDoc};
inline const Event::Custom::AutoReserve kEvtSwapPending{"EQUIPMENT_SWAP_PENDING",
&kSwapPendingDoc};
inline const Event::Custom::AutoReserve kEvtSwapFinished{"EQUIPMENT_SWAP_FINISHED",
&kSwapFinishedDoc};
} // namespace EquipmentSet
+12 -1
View File
@@ -218,6 +218,16 @@ const Game::Lua::FrameMethodEntry g_methods[] = {
{"SetEquipmentSet", &Script_GameTooltipSetEquipmentSet},
};
const Game::Doc::Field kSetEquipmentSetArgs[] = {
Game::Doc::Req("name", "cstring", "The name of the equipment set."),
};
const Game::Doc::Function kSetEquipmentSet{
"Shows an equipment set in the tooltip: its name and where its items are.",
kSetEquipmentSetArgs, {}};
const Game::Doc::Method g_methodDocs[] = {
{"SetEquipmentSet", &kSetEquipmentSet},
};
} // namespace
static void RegisterLuaFunctions() {
@@ -226,7 +236,8 @@ static void RegisterLuaFunctions() {
Game::Lua::RegisterFrameMethods(
reinterpret_cast<void *>(Offsets::VAR_GAMETOOLTIP_METHOD_REGISTRY),
g_methods,
static_cast<int>(sizeof(g_methods) / sizeof(g_methods[0])));
static_cast<int>(sizeof(g_methods) / sizeof(g_methods[0])), g_methodDocs,
static_cast<int>(sizeof(g_methodDocs) / sizeof(g_methodDocs[0])));
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+207 -19
View File
@@ -669,54 +669,242 @@ static int __fastcall Script_C_Reputation_SetWatchedFactionByID(void *L) {
return 0;
}
const Game::Doc::Field kFactionDataFields[] = {
Game::Doc::Req("factionID", "number"),
Game::Doc::Req("name", "string"),
Game::Doc::Req("description", "string", "Empty when the faction has none."),
Game::Doc::Req("reaction", "luaIndex", "The rank, from 1 for Hated to 8 for Exalted."),
Game::Doc::Req("currentReactionThreshold", "number",
"The standing at the bottom of the current rank."),
Game::Doc::Req("nextReactionThreshold", "number",
"The standing at the top of the current rank."),
Game::Doc::Req("currentStanding", "number"),
Game::Doc::Req("atWarWith", "bool"),
Game::Doc::Req("canToggleAtWar", "bool",
"False below -3000 standing, when the war state is fixed, or with no "
"reputation slot."),
Game::Doc::Req("isChild", "bool", "Always false."),
Game::Doc::Req("isHeader", "bool", "True when the faction is a header in the reputation list."),
Game::Doc::Req("isHeaderWithRep", "bool", "Always false."),
Game::Doc::Req("isCollapsed", "bool", "True for a collapsed header."),
Game::Doc::Req("isWatched", "bool", "True when the faction is shown above the experience bar."),
Game::Doc::Req("hasBonusRepGain", "bool", "Always false."),
Game::Doc::Req("canSetInactive", "bool",
"True when the faction has a reputation slot and is not a header."),
Game::Doc::Req("isAccountWide", "bool", "Always false."),
};
const Game::Doc::Structure kFactionData{"FactionData", "Reputation", kFactionDataFields,
"A faction's reputation data."};
const Game::Doc::Field kFactionIDArgs[] = {
Game::Doc::Req("factionID", "number"),
};
const Game::Doc::Field kSortIndexArgs[] = {
Game::Doc::Req("factionSortIndex", "luaIndex", "A position in the reputation list."),
};
const Game::Doc::Field kFactionIDByIndexArgs[] = {
Game::Doc::Req("factionIndex", "luaIndex", "A position in the reputation list."),
};
const Game::Doc::Field kFactionIDByIndexRets[] = {
Game::Doc::Opt("factionID", "number", nullptr,
"0 for a header with no faction of its own, such as Other or Inactive; nil "
"when the index is out of range."),
};
const Game::Doc::Function kGetFactionIDByIndex{
"Returns the faction ID of the entry at a position in the reputation list.",
kFactionIDByIndexArgs, kFactionIDByIndexRets, "ReputationGlobals", true};
const Game::Doc::Field kFactionInfoByIDRets[] = {
Game::Doc::Opt("name", "string", nullptr, "Nil, with the rest, when no faction has the ID."),
Game::Doc::Opt("description", "string", nullptr, "Empty when the faction has none."),
Game::Doc::Opt("standingID", "number", nullptr, "The rank, from 1 for Hated to 8 for Exalted."),
Game::Doc::Opt("barMin", "number", nullptr, "The standing at the bottom of the current rank."),
Game::Doc::Opt("barMax", "number", nullptr, "The standing at the top of the current rank."),
Game::Doc::Opt("barValue", "number", nullptr, "The current standing."),
Game::Doc::Opt("atWarWith", "number", nullptr,
"1 when the player is at war with the faction, else nil."),
Game::Doc::Opt("canToggleAtWar", "number", nullptr,
"1 when the at-war state can be changed, else nil."),
Game::Doc::Opt("isHeader", "number", nullptr,
"1 when the faction is a header in the reputation list, else nil."),
Game::Doc::Opt("isCollapsed", "number", nullptr, "1 for a collapsed header, else nil."),
Game::Doc::Opt("isWatched", "number", nullptr,
"1 when the faction is shown above the experience bar, else nil."),
};
const Game::Doc::Function kGetFactionInfoByID{
"Returns the same values as GetFactionInfo for a faction ID, whether or not the faction "
"is in the reputation list.",
kFactionIDArgs, kFactionInfoByIDRets, "ReputationGlobals"};
const Game::Doc::Field kFactionParentIDRets[] = {
Game::Doc::Opt("parentFactionID", "number", nullptr,
"0 for a faction with no parent; nil when no faction has the ID."),
};
const Game::Doc::Function kGetFactionParentID{
"Returns the ID of a faction's parent faction, such as Alliance for Stormwind.",
kFactionIDArgs, kFactionParentIDRets, "ReputationGlobals", true};
const Game::Doc::Field kSetWatchedArgs[] = {
Game::Doc::Req("factionID", "number",
"0, or an ID with no reputation slot, clears the watched faction; a negative "
"ID does nothing."),
};
const Game::Doc::Function kSetWatchedFactionByID{
"Sets the faction shown above the experience bar.", kSetWatchedArgs, {}};
const Game::Doc::Field kSetSelectedByIDArgs[] = {
Game::Doc::Req("factionID", "number", "0 clears the selection; a negative ID does nothing."),
};
const Game::Doc::Function kSetSelectedFactionByID{
"Selects a faction in the reputation pane by ID, even when the faction is not listed.",
kSetSelectedByIDArgs, {}, nullptr, true};
const Game::Doc::Function kToggleFactionAtWarByID{
"Toggles whether the player is at war with a faction, by ID; the same limits apply as "
"for C_Reputation.ToggleFactionAtWar.",
kFactionIDArgs, {}, nullptr, true};
const Game::Doc::Field kIsActiveRets[] = {
Game::Doc::Req("isActive", "bool",
"False under the Inactive header, and for anything with no reputation slot."),
};
const Game::Doc::Function kIsFactionActive{
"Returns whether the faction at a position in the reputation list is active, that is, "
"not under the Inactive header.",
kSortIndexArgs, kIsActiveRets};
const Game::Doc::Function kIsFactionActiveByID{
"Returns whether a faction is active, that is, not under the Inactive header.",
kFactionIDArgs, kIsActiveRets, nullptr, true};
const Game::Doc::Field kIsInactiveRets[] = {
Game::Doc::Opt("isInactive", "number", nullptr,
"1 when the faction is under the Inactive header, else nil."),
};
const Game::Doc::Function kIsFactionInactive{
"Returns whether the faction at a position in the reputation list is under the Inactive "
"header, as 1 or nil.",
kSortIndexArgs, kIsInactiveRets, nullptr, true};
const Game::Doc::Function kSetFactionInactiveByID{
"Moves a faction under the Inactive header of the reputation list, by ID.",
kFactionIDArgs, {}, nullptr, true};
const Game::Doc::Function kSetFactionActiveByID{
"Moves a faction out from under the Inactive header of the reputation list, by ID.",
kFactionIDArgs, {}, nullptr, true};
const Game::Doc::Function kSetFactionInactive{
"Moves the faction at a position in the reputation list under the Inactive header.",
kSortIndexArgs, {}, nullptr, true};
const Game::Doc::Function kSetFactionActive{
"Moves the faction at a position in the reputation list out from under the Inactive "
"header.",
kSortIndexArgs, {}};
const Game::Doc::Function kToggleFactionAtWar{
"Toggles whether the player is at war with the faction at a position in the reputation "
"list.",
kSortIndexArgs, {}};
const Game::Doc::Function kSetSelectedFaction{
"Selects the faction at a position in the reputation list.", kSortIndexArgs, {}};
const Game::Doc::Field kWatchedFactionDataRets[] = {
Game::Doc::Opt("watchedFactionData", "FactionData", nullptr,
"Nil when no faction is watched."),
};
const Game::Doc::Function kGetWatchedFactionData{
"Returns the faction shown above the experience bar.", {}, kWatchedFactionDataRets};
const Game::Doc::Field kStandingsRets[] = {
Game::Doc::Req("standings", "table",
"Keyed by faction ID; each value is the faction's standing."),
};
const Game::Doc::Function kGetFactionStandings{
"Returns the standing of every faction the character has reputation with, keyed by "
"faction ID.",
{}, kStandingsRets, nullptr, true};
const Game::Doc::Field kFactionDataByIDRets[] = {
Game::Doc::Opt("factionData", "FactionData", nullptr, "Nil when no faction has the ID."),
};
const Game::Doc::Function kGetFactionDataByID{
"Returns a faction's reputation data by ID, whether or not the faction is in the "
"reputation list.",
kFactionIDArgs, kFactionDataByIDRets};
const Game::Doc::Field kFactionDataByIndexRets[] = {
Game::Doc::Opt("factionData", "FactionData", nullptr,
"Nil when the index is out of range or names the Other or Inactive header."),
};
const Game::Doc::Function kGetFactionDataByIndex{
"Returns the reputation data of the faction at a position in the reputation list.",
kSortIndexArgs, kFactionDataByIndexRets};
static void RegisterLuaFunctions() {
Game::Lua::RegisterGlobalFunction("GetFactionIDByIndex", &Script_GetFactionIDByIndex);
Game::Lua::RegisterGlobalFunction("GetFactionInfoByID", &Script_GetFactionInfoByID);
Game::Lua::RegisterGlobalFunction("GetFactionParentID", &Script_GetFactionParentID);
Game::Lua::RegisterGlobalFunction("GetFactionIDByIndex", &Script_GetFactionIDByIndex,
&kGetFactionIDByIndex);
Game::Lua::RegisterGlobalFunction("GetFactionInfoByID", &Script_GetFactionInfoByID,
&kGetFactionInfoByID);
Game::Lua::RegisterGlobalFunction("GetFactionParentID", &Script_GetFactionParentID,
&kGetFactionParentID);
Game::Lua::RegisterTableFunction("C_Reputation", "SetWatchedFactionByID",
&Script_C_Reputation_SetWatchedFactionByID);
&Script_C_Reputation_SetWatchedFactionByID,
&kSetWatchedFactionByID);
Game::Lua::RegisterTableFunction("C_Reputation", "SetSelectedFactionByID",
&Script_C_Reputation_SetSelectedFactionByID);
&Script_C_Reputation_SetSelectedFactionByID,
&kSetSelectedFactionByID);
Game::Lua::RegisterTableFunction("C_Reputation", "ToggleFactionAtWarByID",
&Script_C_Reputation_ToggleFactionAtWarByID);
&Script_C_Reputation_ToggleFactionAtWarByID,
&kToggleFactionAtWarByID);
Game::Lua::RegisterTableFunction("C_Reputation", "IsFactionActive",
&Script_C_Reputation_IsFactionActive);
&Script_C_Reputation_IsFactionActive, &kIsFactionActive);
Game::Lua::RegisterTableFunction("C_Reputation", "IsFactionActiveByID",
&Script_C_Reputation_IsFactionActiveByID);
&Script_C_Reputation_IsFactionActiveByID,
&kIsFactionActiveByID);
Game::Lua::RegisterTableFunction(
"C_Reputation", "IsFactionInactive",
reinterpret_cast<Game::Lua::CFunction>(
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_IS_FACTION_INACTIVE)));
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_IS_FACTION_INACTIVE)),
&kIsFactionInactive);
Game::Lua::RegisterTableFunction("C_Reputation", "SetFactionInactiveByID",
&Script_C_Reputation_SetFactionInactiveByID);
&Script_C_Reputation_SetFactionInactiveByID,
&kSetFactionInactiveByID);
Game::Lua::RegisterTableFunction("C_Reputation", "SetFactionActiveByID",
&Script_C_Reputation_SetFactionActiveByID);
&Script_C_Reputation_SetFactionActiveByID,
&kSetFactionActiveByID);
Game::Lua::RegisterTableFunction(
"C_Reputation", "SetFactionInactive",
reinterpret_cast<Game::Lua::CFunction>(
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_SET_FACTION_INACTIVE)));
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_SET_FACTION_INACTIVE)),
&kSetFactionInactive);
Game::Lua::RegisterTableFunction(
"C_Reputation", "SetFactionActive",
reinterpret_cast<Game::Lua::CFunction>(
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_SET_FACTION_ACTIVE)));
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_SET_FACTION_ACTIVE)),
&kSetFactionActive);
Game::Lua::RegisterTableFunction(
"C_Reputation", "ToggleFactionAtWar",
reinterpret_cast<Game::Lua::CFunction>(
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_FACTION_TOGGLE_AT_WAR)));
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_FACTION_TOGGLE_AT_WAR)),
&kToggleFactionAtWar);
// The stock global, mirrored into the namespace — the engine's own
// handler already has the Lua C ABI, so it registers as-is.
Game::Lua::RegisterTableFunction(
"C_Reputation", "SetSelectedFaction",
reinterpret_cast<Game::Lua::CFunction>(
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_SET_SELECTED_FACTION)));
static_cast<uintptr_t>(Offsets::FUN_SCRIPT_SET_SELECTED_FACTION)),
&kSetSelectedFaction);
Game::Lua::RegisterTableFunction("C_Reputation", "GetWatchedFactionData",
&Script_C_Reputation_GetWatchedFactionData);
&Script_C_Reputation_GetWatchedFactionData,
&kGetWatchedFactionData);
Game::Lua::RegisterTableFunction("C_Reputation", "GetFactionStandings",
&Script_C_Reputation_GetFactionStandings);
&Script_C_Reputation_GetFactionStandings,
&kGetFactionStandings);
Game::Lua::RegisterTableFunction("C_Reputation", "GetFactionDataByID",
&Script_C_Reputation_GetFactionDataByID);
&Script_C_Reputation_GetFactionDataByID,
&kGetFactionDataByID);
Game::Lua::RegisterTableFunction("C_Reputation", "GetFactionDataByIndex",
&Script_C_Reputation_GetFactionDataByIndex);
&Script_C_Reputation_GetFactionDataByIndex,
&kGetFactionDataByIndex);
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+23 -2
View File
@@ -50,7 +50,15 @@ using GetStanding_t = int(__fastcall *)(int factionID);
constexpr const char *kEventName = "FACTION_STANDING_CHANGED";
const Event::Custom::AutoReserve _reserve{kEventName};
const Game::Doc::Field kPayload[] = {
Game::Doc::Req("factionID", "number"),
Game::Doc::Req("newStanding", "number", "The faction's standing after the change."),
Game::Doc::Req("repGained", "number", "Negative for a loss."),
};
const Game::Doc::Event kEventDoc{"Reputation", "Fires when the player's reputation changes.",
kPayload};
const Event::Custom::AutoReserve _reserve{kEventName, &kEventDoc};
// In-flight snapshot of the current rep change. Set on hook entry,
// cleared on hook exit. WoW's main thread is the only one that runs
@@ -67,9 +75,22 @@ int __fastcall Script_C_Reputation_GetLastStandingChange(void *L) {
return 3;
}
const Game::Doc::Field kRets[] = {
Game::Doc::Opt("factionID", "number", nullptr,
"Nil, with the rest, outside a FACTION_STANDING_CHANGED or "
"CHAT_MSG_COMBAT_FACTION_CHANGE handler."),
Game::Doc::Opt("newStanding", "number", nullptr, "The faction's standing after the change."),
Game::Doc::Opt("repGained", "number", nullptr, "Negative for a loss."),
};
const Game::Doc::Function kGetLastStandingChange{
"Returns the reputation change that FACTION_STANDING_CHANGED and "
"CHAT_MSG_COMBAT_FACTION_CHANGE report, while their handlers run.",
{}, kRets, nullptr, true};
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Reputation", "GetLastStandingChange",
&Script_C_Reputation_GetLastStandingChange);
&Script_C_Reputation_GetLastStandingChange,
&kGetLastStandingChange);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
+175 -19
View File
@@ -47,6 +47,11 @@
// OLDEST — the combat log's refilter walks its history newest to oldest and
// fills the frame behind the lines it already shows (Blizzard_CombatLog's
// RefilterUpdate). It writes the ring itself; see the function.
//
// `RemoveMessagesByPredicate(predicate)`, retail's, removes the messages the
// predicate selects by their GetMessageInfo values. The ring has no
// compaction of its own; 4.3.4's RemoveLines loop is run over it, the
// extra-argument tables moving with their records. See the function.
#include "Game.h"
#include "Offsets.h"
@@ -54,6 +59,7 @@
#include <cstdint>
#include <cstring>
#include <vector>
namespace ScrollingMessageFrame::MessageInfo {
@@ -66,9 +72,9 @@ constexpr int kFirstExtra = 6; // self, text, r, g, b, then the extra arguments
constexpr int kNoRef = -2;
int g_extrasRef = kNoRef;
// Pushes the extra-argument table of record `record` of the frame whose Lua
// table is at stack slot 1, or nil when it has none and `create` is false.
void PushExtras(void *L, int record, bool create) {
// Pushes the record → extra-arguments table of the frame whose Lua table is
// at stack slot 1, or nil when it has none and `create` is false.
void PushRecords(void *L, bool create) {
using namespace Game::Lua;
if (g_extrasRef == kNoRef) {
if (!create) {
@@ -89,6 +95,13 @@ void PushExtras(void *L, int record, bool create) {
RawSet(L, -4); // frames[self] = records
}
Remove(L, -2); // [records]
}
// Pushes the extra-argument table of record `record` of the frame whose Lua
// table is at stack slot 1, or nil when it has none and `create` is false.
void PushExtras(void *L, int record, bool create) {
using namespace Game::Lua;
PushRecords(L, create); // [records]
if (Type(L, -1) != TYPE_TABLE)
return;
RawGetI(L, -1, record); // [records, extras]
@@ -241,27 +254,24 @@ int __fastcall Script_BackFillMessage(void *L) {
return 0;
}
int __fastcall Script_GetMessageInfo(void *L) {
using namespace Game::Lua;
void *frame = ResolveTypedObject(L, 1, Offsets::VAR_SCROLLINGMESSAGEFRAME_LUA_TYPE_ID);
if (frame == nullptr)
return 0;
if (!IsNumber(L, 2)) {
Error(L, "Usage: %s:GetMessageInfo(index)", ObjectDisplayName(frame));
return 0;
}
const double index = ToNumber(L, 2);
// The record of message `index`, 1 the oldest: (position - count + index),
// plus the limit when negative.
int RecordOf(void *frame, int index) {
const int count = Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_COUNT);
if (!(index >= 1 && index <= count) || index != static_cast<int>(index))
return 0;
int record = Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_POS) - count +
static_cast<int>(index);
int record = Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_POS) - count + index;
if (record < 0)
record += Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MAX_MESSAGES);
return record;
}
// Pushes a record's GetMessageInfo values — its text, r, g, b, then its extra
// arguments — for the frame whose Lua table is at stack slot 1, and returns
// how many.
int PushMessage(void *L, void *frame, int record) {
using namespace Game::Lua;
const auto *message = Game::Read<const uint8_t *>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGES) +
record * Offsets::SCROLLINGMESSAGEFRAME_MESSAGE_STRIDE;
const uint32_t color = Game::Read<uint32_t>(message, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_COLOR);
PushExtras(L, record, false);
const int extras = GetTop(L);
const int n = Type(L, extras) == TYPE_TABLE ? ExtraCount(L, extras) : 0;
@@ -276,10 +286,146 @@ int __fastcall Script_GetMessageInfo(void *L) {
return 4 + n;
}
int __fastcall Script_GetMessageInfo(void *L) {
using namespace Game::Lua;
void *frame = ResolveTypedObject(L, 1, Offsets::VAR_SCROLLINGMESSAGEFRAME_LUA_TYPE_ID);
if (frame == nullptr)
return 0;
if (!IsNumber(L, 2)) {
Error(L, "Usage: %s:GetMessageInfo(index)", ObjectDisplayName(frame));
return 0;
}
const double index = ToNumber(L, 2);
const int count = Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_COUNT);
if (!(index >= 1 && index <= count) || index != static_cast<int>(index))
return 0;
return PushMessage(L, frame, RecordOf(frame, static_cast<int>(index)));
}
// `ScrollingMessageFrame:RemoveMessagesByPredicate(predicate)`, retail's: the
// predicate is called with each message's GetMessageInfo values, oldest
// first, and the messages it returns true for are removed; the rest keep
// their order. Retail's mixin rebuilds its history buffer and re-renders at
// the kept scroll offset. The removal is 4.3.4's RemoveLines over 1.12's
// ring: every kept record moves forward in ring order to the first free slot
// (freeing the text it lands on, nulling the one it left), the position moves
// back by the removed count, and the extra-argument tables move with their
// records. Then the engine's own layout from the record at the kept offset
// (clamped to the oldest) and its re-arm — a layout at the bottom keeps the
// fade state, so 4.3.4 re-arms after — or its Clear when nothing is kept.
//
// The predicate runs for every message before anything moves (retail builds
// its new list first). A predicate that errors keeps that message, as
// retail's safesecurecall does; the engine's error handler reports it.
int __fastcall Script_RemoveMessagesByPredicate(void *L) {
using namespace Game::Lua;
using Layout_t = void(__fastcall *)(void *smf, void *edx, int newestRecord);
using Smf_t = void(__fastcall *)(void *smf);
using ScrollOffset_t = int(__fastcall *)(void *smf);
using StormFree_t = int(__stdcall *)(void *ptr, const char *file, int line, int flags);
static const char kStormFile[] = "ClassicAPI ScrollingMessageFrame:RemoveMessagesByPredicate";
void *frame = ResolveTypedObject(L, 1, Offsets::VAR_SCROLLINGMESSAGEFRAME_LUA_TYPE_ID);
if (frame == nullptr)
return 0;
if (Type(L, 2) != TYPE_FUNCTION) {
Error(L, "Usage: %s:RemoveMessagesByPredicate(predicate)", ObjectDisplayName(frame));
return 0;
}
const int count = Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_COUNT);
const int max = Game::Read<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MAX_MESSAGES);
if (count <= 0 || max <= 0)
return 0;
const int top = GetTop(L);
// The predicate over every message, through the engine's error handler.
int errIdx = 0;
const int errRef = Game::Read<int>(static_cast<uintptr_t>(Offsets::VAR_FRAMESCRIPT_ERROR_HANDLER_REF));
if (errRef > 0) {
RawGetI(L, REGISTRY_INDEX, errRef);
errIdx = GetTop(L);
}
std::vector<uint8_t> remove(static_cast<size_t>(count), 0);
int kept = count;
for (int i = 0; i < count; ++i) {
CheckStack(L, 2);
PushValue(L, 2);
const int args = PushMessage(L, frame, RecordOf(frame, i + 1));
if (PCall(L, args, 1, errIdx) == 0 && ToBoolean(L, -1) != 0) {
remove[static_cast<size_t>(i)] = 1;
--kept;
}
SetTop(L, -2);
}
SetTop(L, top);
if (kept == count)
return 0;
// 4.3.4's RemoveLines: compact the kept records forward in ring order.
const int offset = reinterpret_cast<ScrollOffset_t>(Offsets::FUN_SCROLLINGMESSAGEFRAME_SCROLL_OFFSET)(frame);
int &pos = Game::Ref<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_POS);
uint8_t *records = Game::Read<uint8_t *>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGES);
PushRecords(L, false);
const int table = GetTop(L);
const bool extras = Type(L, table) == TYPE_TABLE;
int src = pos - count + 1;
if (src < 0)
src += max;
int dst = src;
for (int i = 0; i < count; ++i) {
if (!remove[static_cast<size_t>(i)]) {
if (src != dst) {
uint8_t *to = records + dst * Offsets::SCROLLINGMESSAGEFRAME_MESSAGE_STRIDE;
uint8_t *from = records + src * Offsets::SCROLLINGMESSAGEFRAME_MESSAGE_STRIDE;
char *&landedOn = *reinterpret_cast<char **>(to + Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_TEXT);
if (landedOn != nullptr)
reinterpret_cast<StormFree_t>(Offsets::FUN_STORM_SMEM_FREE)(landedOn, kStormFile, __LINE__, 0);
std::memcpy(to, from, Offsets::SCROLLINGMESSAGEFRAME_MESSAGE_STRIDE);
*reinterpret_cast<char **>(from + Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_TEXT) = nullptr;
if (extras) {
PushNumber(L, dst);
RawGetI(L, table, src);
RawSet(L, table); // records[dst] = records[src]
PushNumber(L, src);
PushNil(L);
RawSet(L, table); // records[src] = nil
}
}
dst = (dst + 1) % max;
} else if (extras) {
PushNumber(L, src);
PushNil(L);
RawSet(L, table); // records[src] = nil
}
src = (src + 1) % max;
}
SetTop(L, top);
if (kept == 0) {
reinterpret_cast<Smf_t>(Offsets::FUN_SCROLLINGMESSAGEFRAME_CLEAR)(frame);
DisplayRefreshed::MarkDirty(frame);
return 0;
}
pos += kept - count;
if (pos < 0)
pos += max;
Game::Ref<int>(frame, Offsets::OFF_SCROLLINGMESSAGEFRAME_MESSAGE_COUNT) = kept;
// The lines again from the record at the kept offset, then re-armed.
const int shown = (offset < kept) ? (offset < 0 ? 0 : offset) : kept - 1;
int record = pos - shown;
if (record < 0)
record += max;
reinterpret_cast<Layout_t>(Offsets::FUN_SMF_DISPLAY_REFRESH)(frame, nullptr, record);
reinterpret_cast<Smf_t>(Offsets::FUN_SCROLLINGMESSAGEFRAME_REARM_LINES)(frame);
DisplayRefreshed::MarkDirty(frame);
return 0;
}
const Game::Lua::FrameMethodEntry kMethods[] = {
{"AddMessage", &Script_AddMessage},
{"BackFillMessage", &Script_BackFillMessage},
{"GetMessageInfo", &Script_GetMessageInfo},
{"RemoveMessagesByPredicate", &Script_RemoveMessagesByPredicate},
};
const Game::Doc::Field kAddMessageArgs[] = {
@@ -318,17 +464,27 @@ const Game::Doc::Function kGetMessageInfo{
"when there is no message at that index.",
kGetMessageInfoArgs, kGetMessageInfoRets};
const Game::Doc::Field kRemoveMessagesByPredicateArgs[] = {
Game::Doc::Req("predicate", "function",
"Called with each message's GetMessageInfo values, oldest first; true removes the message."),
};
const Game::Doc::Function kRemoveMessagesByPredicate{
"Removes the messages the predicate returns true for. The rest keep their order, and the frame "
"keeps its scroll offset.",
kRemoveMessagesByPredicateArgs, {}};
const Game::Doc::Method kDocs[] = {
{"AddMessage", &kAddMessage},
{"BackFillMessage", &kBackFillMessage},
{"GetMessageInfo", &kGetMessageInfo},
{"RemoveMessagesByPredicate", &kRemoveMessagesByPredicate},
};
void RegisterLuaFunctions() {
// Each registration pass is a new Lua state; the old state's ref is gone.
g_extrasRef = kNoRef;
Game::Lua::RegisterFrameMethods(reinterpret_cast<void *>(Offsets::VAR_SCROLLINGMESSAGEFRAME_METHOD_REGISTRY),
kMethods, 3, kDocs, 3);
kMethods, 4, kDocs, 4);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};