mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 01:23:52 +00:00
- Refactor Lua addon integration: - Update CMakeLists.txt for addon build paths - Enhance addons API headers and Lua engine interface - Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`) - Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
734 lines
28 KiB
C++
734 lines
28 KiB
C++
// lua_action_api.cpp — Action bar, cursor/pickup, keyboard input, key bindings, and pet actions Lua API bindings.
|
|
// Extracted from lua_engine.cpp as part of §5.1 (Tame LuaEngine).
|
|
#include "addons/lua_api_helpers.hpp"
|
|
#include "imgui.h"
|
|
|
|
namespace wowee::addons {
|
|
|
|
enum class CursorType { NONE, SPELL, ITEM, ACTION };
|
|
static CursorType s_cursorType = CursorType::NONE;
|
|
static uint32_t s_cursorId = 0; // spellId, itemId, or action slot
|
|
static int s_cursorSlot = 0; // source slot for placement
|
|
static int s_cursorBag = -1; // source bag for container items
|
|
|
|
static int lua_HasAction(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { return luaReturnFalse(L); }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1; // WoW uses 1-indexed slots
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size())) {
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
lua_pushboolean(L, !bar[slot].isEmpty());
|
|
return 1;
|
|
}
|
|
|
|
// GetActionTexture(slot) → texturePath or nil
|
|
static int lua_GetActionTexture(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { return luaReturnNil(L); }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
const auto& action = bar[slot];
|
|
if (action.type == game::ActionBarSlot::SPELL) {
|
|
std::string icon = gh->getSpellIconPath(action.id);
|
|
if (!icon.empty()) {
|
|
lua_pushstring(L, icon.c_str());
|
|
return 1;
|
|
}
|
|
} else if (action.type == game::ActionBarSlot::ITEM && action.id != 0) {
|
|
const auto* info = gh->getItemInfo(action.id);
|
|
if (info && info->displayInfoId != 0) {
|
|
std::string icon = gh->getItemIconPath(info->displayInfoId);
|
|
if (!icon.empty()) {
|
|
lua_pushstring(L, icon.c_str());
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
// IsCurrentAction(slot) → boolean
|
|
static int lua_IsCurrentAction(lua_State* L) {
|
|
// Currently no "active action" tracking; return false
|
|
(void)L;
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
// IsUsableAction(slot) → usable, notEnoughMana
|
|
static int lua_IsUsableAction(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { lua_pushboolean(L, 0); lua_pushboolean(L, 0); return 2; }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) {
|
|
lua_pushboolean(L, 0);
|
|
lua_pushboolean(L, 0);
|
|
return 2;
|
|
}
|
|
const auto& action = bar[slot];
|
|
bool usable = action.isReady();
|
|
bool noMana = false;
|
|
if (action.type == game::ActionBarSlot::SPELL) {
|
|
usable = usable && gh->getKnownSpells().count(action.id);
|
|
// Check power cost
|
|
if (usable && action.id != 0) {
|
|
auto spellData = gh->getSpellData(action.id);
|
|
if (spellData.manaCost > 0) {
|
|
auto pe = gh->getEntityManager().getEntity(gh->getPlayerGuid());
|
|
if (pe) {
|
|
auto* unit = dynamic_cast<game::Unit*>(pe.get());
|
|
if (unit && unit->getPower() < spellData.manaCost) {
|
|
noMana = true;
|
|
usable = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
lua_pushboolean(L, usable ? 1 : 0);
|
|
lua_pushboolean(L, noMana ? 1 : 0);
|
|
return 2;
|
|
}
|
|
|
|
// IsActionInRange(slot) → 1 if in range, 0 if out, nil if no range check applicable
|
|
static int lua_IsActionInRange(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { return luaReturnNil(L); }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
const auto& action = bar[slot];
|
|
uint32_t spellId = 0;
|
|
if (action.type == game::ActionBarSlot::SPELL) {
|
|
spellId = action.id;
|
|
} else {
|
|
// Items/macros: no range check for now
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
if (spellId == 0) { return luaReturnNil(L); }
|
|
|
|
auto data = gh->getSpellData(spellId);
|
|
if (data.maxRange <= 0.0f) {
|
|
// Melee or self-cast spells: no range indicator
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
// Need a target to check range against
|
|
uint64_t targetGuid = gh->getTargetGuid();
|
|
if (targetGuid == 0) { return luaReturnNil(L); }
|
|
auto targetEnt = gh->getEntityManager().getEntity(targetGuid);
|
|
auto playerEnt = gh->getEntityManager().getEntity(gh->getPlayerGuid());
|
|
if (!targetEnt || !playerEnt) { return luaReturnNil(L); }
|
|
|
|
float dx = playerEnt->getX() - targetEnt->getX();
|
|
float dy = playerEnt->getY() - targetEnt->getY();
|
|
float dz = playerEnt->getZ() - targetEnt->getZ();
|
|
float dist = std::sqrt(dx*dx + dy*dy + dz*dz);
|
|
lua_pushnumber(L, dist <= data.maxRange ? 1 : 0);
|
|
return 1;
|
|
}
|
|
|
|
// GetActionInfo(slot) → actionType, id, subType
|
|
static int lua_GetActionInfo(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { return 0; }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) {
|
|
return 0;
|
|
}
|
|
const auto& action = bar[slot];
|
|
switch (action.type) {
|
|
case game::ActionBarSlot::SPELL:
|
|
lua_pushstring(L, "spell");
|
|
lua_pushnumber(L, action.id);
|
|
lua_pushstring(L, "spell");
|
|
return 3;
|
|
case game::ActionBarSlot::ITEM:
|
|
lua_pushstring(L, "item");
|
|
lua_pushnumber(L, action.id);
|
|
lua_pushstring(L, "item");
|
|
return 3;
|
|
case game::ActionBarSlot::MACRO:
|
|
lua_pushstring(L, "macro");
|
|
lua_pushnumber(L, action.id);
|
|
lua_pushstring(L, "macro");
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// GetActionCount(slot) → count (item stack count or 0)
|
|
static int lua_GetActionCount(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { return luaReturnZero(L); }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) {
|
|
lua_pushnumber(L, 0);
|
|
return 1;
|
|
}
|
|
const auto& action = bar[slot];
|
|
if (action.type == game::ActionBarSlot::ITEM && action.id != 0) {
|
|
// Count items across backpack + bags
|
|
uint32_t count = 0;
|
|
const auto& inv = gh->getInventory();
|
|
for (int i = 0; i < inv.getBackpackSize(); ++i) {
|
|
const auto& s = inv.getBackpackSlot(i);
|
|
if (!s.empty() && s.item.itemId == action.id)
|
|
count += (s.item.stackCount > 0 ? s.item.stackCount : 1);
|
|
}
|
|
for (int b = 0; b < game::Inventory::NUM_BAG_SLOTS; ++b) {
|
|
int bagSize = inv.getBagSize(b);
|
|
for (int i = 0; i < bagSize; ++i) {
|
|
const auto& s = inv.getBagSlot(b, i);
|
|
if (!s.empty() && s.item.itemId == action.id)
|
|
count += (s.item.stackCount > 0 ? s.item.stackCount : 1);
|
|
}
|
|
}
|
|
lua_pushnumber(L, count);
|
|
} else {
|
|
lua_pushnumber(L, 0);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// GetActionCooldown(slot) → start, duration, enable
|
|
static int lua_GetActionCooldown(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) { lua_pushnumber(L, 0); lua_pushnumber(L, 0); lua_pushnumber(L, 1); return 3; }
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) {
|
|
lua_pushnumber(L, 0);
|
|
lua_pushnumber(L, 0);
|
|
lua_pushnumber(L, 1);
|
|
return 3;
|
|
}
|
|
const auto& action = bar[slot];
|
|
if (action.cooldownRemaining > 0.0f) {
|
|
// WoW returns GetTime()-based start time; approximate
|
|
double now = 0;
|
|
lua_getglobal(L, "GetTime");
|
|
if (lua_isfunction(L, -1)) {
|
|
lua_call(L, 0, 1);
|
|
now = lua_tonumber(L, -1);
|
|
lua_pop(L, 1);
|
|
} else {
|
|
lua_pop(L, 1);
|
|
}
|
|
double start = now - (action.cooldownTotal - action.cooldownRemaining);
|
|
lua_pushnumber(L, start);
|
|
lua_pushnumber(L, action.cooldownTotal);
|
|
lua_pushnumber(L, 1);
|
|
} else if (action.type == game::ActionBarSlot::SPELL && gh->isGCDActive()) {
|
|
// No individual cooldown but GCD is active — show GCD sweep
|
|
float gcdRem = gh->getGCDRemaining();
|
|
float gcdTotal = gh->getGCDTotal();
|
|
double now = 0;
|
|
lua_getglobal(L, "GetTime");
|
|
if (lua_isfunction(L, -1)) { lua_call(L, 0, 1); now = lua_tonumber(L, -1); lua_pop(L, 1); }
|
|
else lua_pop(L, 1);
|
|
double elapsed = gcdTotal - gcdRem;
|
|
lua_pushnumber(L, now - elapsed);
|
|
lua_pushnumber(L, gcdTotal);
|
|
lua_pushnumber(L, 1);
|
|
} else {
|
|
lua_pushnumber(L, 0);
|
|
lua_pushnumber(L, 0);
|
|
lua_pushnumber(L, 1);
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
// UseAction(slot, checkCursor, onSelf) — activate action bar slot (1-indexed)
|
|
static int lua_UseAction(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) return 0;
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1)) - 1;
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 0 || slot >= static_cast<int>(bar.size()) || bar[slot].isEmpty()) return 0;
|
|
const auto& action = bar[slot];
|
|
if (action.type == game::ActionBarSlot::SPELL && action.isReady()) {
|
|
uint64_t target = gh->hasTarget() ? gh->getTargetGuid() : 0;
|
|
gh->castSpell(action.id, target);
|
|
} else if (action.type == game::ActionBarSlot::ITEM && action.id != 0) {
|
|
gh->useItemById(action.id);
|
|
}
|
|
// Macro execution requires GameScreen context; not available from pure Lua API
|
|
return 0;
|
|
}
|
|
|
|
// --- Cursor / Drag-Drop System ---
|
|
// Tracks what the player is "holding" on the cursor (spell, item, action).
|
|
|
|
|
|
static int lua_ClearCursor(lua_State* L) {
|
|
(void)L;
|
|
s_cursorType = CursorType::NONE;
|
|
s_cursorId = 0;
|
|
s_cursorSlot = 0;
|
|
s_cursorBag = -1;
|
|
return 0;
|
|
}
|
|
|
|
static int lua_GetCursorInfo(lua_State* L) {
|
|
switch (s_cursorType) {
|
|
case CursorType::SPELL:
|
|
lua_pushstring(L, "spell");
|
|
lua_pushnumber(L, 0); // bookSlotIndex
|
|
lua_pushstring(L, "spell"); // bookType
|
|
lua_pushnumber(L, s_cursorId); // spellId
|
|
return 4;
|
|
case CursorType::ITEM:
|
|
lua_pushstring(L, "item");
|
|
lua_pushnumber(L, s_cursorId);
|
|
return 2;
|
|
case CursorType::ACTION:
|
|
lua_pushstring(L, "action");
|
|
lua_pushnumber(L, s_cursorSlot);
|
|
return 2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int lua_CursorHasItem(lua_State* L) {
|
|
lua_pushboolean(L, s_cursorType == CursorType::ITEM ? 1 : 0);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_CursorHasSpell(lua_State* L) {
|
|
lua_pushboolean(L, s_cursorType == CursorType::SPELL ? 1 : 0);
|
|
return 1;
|
|
}
|
|
|
|
// PickupAction(slot) — picks up an action from the action bar
|
|
static int lua_PickupAction(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) return 0;
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
|
const auto& bar = gh->getActionBar();
|
|
if (slot < 1 || slot > static_cast<int>(bar.size())) return 0;
|
|
const auto& action = bar[slot - 1];
|
|
if (action.isEmpty()) {
|
|
// Empty slot — if cursor has something, place it
|
|
if (s_cursorType == CursorType::SPELL && s_cursorId != 0) {
|
|
gh->setActionBarSlot(slot - 1, game::ActionBarSlot::SPELL, s_cursorId);
|
|
s_cursorType = CursorType::NONE;
|
|
s_cursorId = 0;
|
|
}
|
|
} else {
|
|
// Pick up existing action
|
|
s_cursorType = (action.type == game::ActionBarSlot::SPELL) ? CursorType::SPELL :
|
|
(action.type == game::ActionBarSlot::ITEM) ? CursorType::ITEM :
|
|
CursorType::ACTION;
|
|
s_cursorId = action.id;
|
|
s_cursorSlot = slot;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// PlaceAction(slot) — places cursor content into an action bar slot
|
|
static int lua_PlaceAction(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) return 0;
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
|
if (slot < 1 || slot > static_cast<int>(gh->getActionBar().size())) return 0;
|
|
if (s_cursorType == CursorType::SPELL && s_cursorId != 0) {
|
|
gh->setActionBarSlot(slot - 1, game::ActionBarSlot::SPELL, s_cursorId);
|
|
} else if (s_cursorType == CursorType::ITEM && s_cursorId != 0) {
|
|
gh->setActionBarSlot(slot - 1, game::ActionBarSlot::ITEM, s_cursorId);
|
|
}
|
|
s_cursorType = CursorType::NONE;
|
|
s_cursorId = 0;
|
|
return 0;
|
|
}
|
|
|
|
// PickupSpell(bookSlot, bookType) — picks up a spell from the spellbook
|
|
static int lua_PickupSpell(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) return 0;
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
|
const auto& tabs = gh->getSpellBookTabs();
|
|
int idx = slot;
|
|
for (const auto& tab : tabs) {
|
|
if (idx <= static_cast<int>(tab.spellIds.size())) {
|
|
s_cursorType = CursorType::SPELL;
|
|
s_cursorId = tab.spellIds[idx - 1];
|
|
return 0;
|
|
}
|
|
idx -= static_cast<int>(tab.spellIds.size());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// PickupSpellBookItem(bookSlot, bookType) — alias for PickupSpell
|
|
static int lua_PickupSpellBookItem(lua_State* L) {
|
|
return lua_PickupSpell(L);
|
|
}
|
|
|
|
// PickupContainerItem(bag, slot) — picks up an item from a bag
|
|
static int lua_PickupContainerItem(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) return 0;
|
|
int bag = static_cast<int>(luaL_checknumber(L, 1));
|
|
int slot = static_cast<int>(luaL_checknumber(L, 2));
|
|
const auto& inv = gh->getInventory();
|
|
const game::ItemSlot* itemSlot = nullptr;
|
|
if (bag == 0 && slot >= 1 && slot <= inv.getBackpackSize()) {
|
|
itemSlot = &inv.getBackpackSlot(slot - 1);
|
|
} else if (bag >= 1 && bag <= 4) {
|
|
int bagSize = inv.getBagSize(bag - 1);
|
|
if (slot >= 1 && slot <= bagSize) {
|
|
itemSlot = &inv.getBagSlot(bag - 1, slot - 1);
|
|
}
|
|
}
|
|
if (itemSlot && !itemSlot->empty()) {
|
|
s_cursorType = CursorType::ITEM;
|
|
s_cursorId = itemSlot->item.itemId;
|
|
s_cursorBag = bag;
|
|
s_cursorSlot = slot;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// PickupInventoryItem(slot) — picks up an equipped item
|
|
static int lua_PickupInventoryItem(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (!gh) return 0;
|
|
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
|
if (slot < 1 || slot > 19) return 0;
|
|
const auto& inv = gh->getInventory();
|
|
const auto& eq = inv.getEquipSlot(static_cast<game::EquipSlot>(slot - 1));
|
|
if (!eq.empty()) {
|
|
s_cursorType = CursorType::ITEM;
|
|
s_cursorId = eq.item.itemId;
|
|
s_cursorSlot = slot;
|
|
s_cursorBag = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// DeleteCursorItem() — destroys the item on cursor
|
|
static int lua_DeleteCursorItem(lua_State* L) {
|
|
(void)L;
|
|
s_cursorType = CursorType::NONE;
|
|
s_cursorId = 0;
|
|
return 0;
|
|
}
|
|
|
|
// AutoEquipCursorItem() — equip item from cursor
|
|
static int lua_AutoEquipCursorItem(lua_State* L) {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && s_cursorType == CursorType::ITEM && s_cursorId != 0) {
|
|
gh->useItemById(s_cursorId);
|
|
}
|
|
s_cursorType = CursorType::NONE;
|
|
s_cursorId = 0;
|
|
return 0;
|
|
}
|
|
|
|
// --- Frame System ---
|
|
// Minimal WoW-compatible frame objects with RegisterEvent/SetScript/GetScript.
|
|
// Frames are Lua tables with a metatable that provides methods.
|
|
|
|
// Frame method: frame:RegisterEvent("EVENT")
|
|
|
|
static int lua_IsShiftKeyDown(lua_State* L) {
|
|
lua_pushboolean(L, ImGui::GetIO().KeyShift ? 1 : 0);
|
|
return 1;
|
|
}
|
|
static int lua_IsControlKeyDown(lua_State* L) {
|
|
lua_pushboolean(L, ImGui::GetIO().KeyCtrl ? 1 : 0);
|
|
return 1;
|
|
}
|
|
static int lua_IsAltKeyDown(lua_State* L) {
|
|
lua_pushboolean(L, ImGui::GetIO().KeyAlt ? 1 : 0);
|
|
return 1;
|
|
}
|
|
|
|
// IsModifiedClick(action) → boolean
|
|
// Checks if a modifier key combo matches a named click action.
|
|
// Common actions: "CHATLINK" (shift-click), "DRESSUP" (ctrl-click),
|
|
// "SPLITSTACK" (shift-click), "SELFCAST" (alt-click)
|
|
static int lua_IsModifiedClick(lua_State* L) {
|
|
const char* action = luaL_optstring(L, 1, "");
|
|
std::string act(action);
|
|
for (char& c : act) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
|
const auto& io = ImGui::GetIO();
|
|
bool result = false;
|
|
if (act == "CHATLINK" || act == "SPLITSTACK")
|
|
result = io.KeyShift;
|
|
else if (act == "DRESSUP" || act == "COMPAREITEMS")
|
|
result = io.KeyCtrl;
|
|
else if (act == "SELFCAST" || act == "FOCUSCAST")
|
|
result = io.KeyAlt;
|
|
else if (act == "STICKYCAMERA")
|
|
result = io.KeyCtrl;
|
|
else
|
|
result = io.KeyShift; // Default: shift for unknown actions
|
|
lua_pushboolean(L, result ? 1 : 0);
|
|
return 1;
|
|
}
|
|
|
|
// GetModifiedClick(action) → key name ("SHIFT", "CTRL", "ALT", "NONE")
|
|
static int lua_GetModifiedClick(lua_State* L) {
|
|
const char* action = luaL_optstring(L, 1, "");
|
|
std::string act(action);
|
|
for (char& c : act) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
|
if (act == "CHATLINK" || act == "SPLITSTACK")
|
|
lua_pushstring(L, "SHIFT");
|
|
else if (act == "DRESSUP" || act == "COMPAREITEMS")
|
|
lua_pushstring(L, "CTRL");
|
|
else if (act == "SELFCAST" || act == "FOCUSCAST")
|
|
lua_pushstring(L, "ALT");
|
|
else
|
|
lua_pushstring(L, "SHIFT");
|
|
return 1;
|
|
}
|
|
static int lua_SetModifiedClick(lua_State* L) { (void)L; return 0; }
|
|
|
|
// --- Keybinding API ---
|
|
// Maps WoW binding names like "ACTIONBUTTON1" to key display strings like "1"
|
|
|
|
// GetBindingKey(command) → key1, key2 (or nil)
|
|
static int lua_GetBindingKey(lua_State* L) {
|
|
const char* cmd = luaL_checkstring(L, 1);
|
|
std::string command(cmd);
|
|
// Return intuitive default bindings for action buttons
|
|
if (command.find("ACTIONBUTTON") == 0) {
|
|
std::string num = command.substr(12);
|
|
int n = 0;
|
|
try { n = std::stoi(num); } catch(...) {}
|
|
if (n >= 1 && n <= 9) {
|
|
lua_pushstring(L, num.c_str());
|
|
return 1;
|
|
} else if (n == 10) {
|
|
lua_pushstring(L, "0");
|
|
return 1;
|
|
} else if (n == 11) {
|
|
lua_pushstring(L, "-");
|
|
return 1;
|
|
} else if (n == 12) {
|
|
lua_pushstring(L, "=");
|
|
return 1;
|
|
}
|
|
}
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
// GetBindingAction(key) → command (or nil)
|
|
static int lua_GetBindingAction(lua_State* L) {
|
|
const char* key = luaL_checkstring(L, 1);
|
|
std::string k(key);
|
|
// Simple reverse mapping for number keys
|
|
if (k.size() == 1 && k[0] >= '1' && k[0] <= '9') {
|
|
lua_pushstring(L, ("ACTIONBUTTON" + k).c_str());
|
|
return 1;
|
|
} else if (k == "0") {
|
|
lua_pushstring(L, "ACTIONBUTTON10");
|
|
return 1;
|
|
}
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_GetNumBindings(lua_State* L) { return luaReturnZero(L); }
|
|
static int lua_GetBinding(lua_State* L) { (void)L; lua_pushnil(L); return 1; }
|
|
static int lua_SetBinding(lua_State* L) { (void)L; return 0; }
|
|
static int lua_SaveBindings(lua_State* L) { (void)L; return 0; }
|
|
static int lua_SetOverrideBindingClick(lua_State* L) { (void)L; return 0; }
|
|
static int lua_ClearOverrideBindings(lua_State* L) { (void)L; return 0; }
|
|
|
|
// Frame methods: SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter, SetAlpha, GetAlpha
|
|
|
|
void registerActionLuaAPI(lua_State* L) {
|
|
static const struct { const char* name; lua_CFunction func; } api[] = {
|
|
{"HasAction", lua_HasAction},
|
|
{"GetActionTexture", lua_GetActionTexture},
|
|
{"IsCurrentAction", lua_IsCurrentAction},
|
|
{"IsUsableAction", lua_IsUsableAction},
|
|
{"IsActionInRange", lua_IsActionInRange},
|
|
{"GetActionInfo", lua_GetActionInfo},
|
|
{"GetActionCount", lua_GetActionCount},
|
|
{"GetActionCooldown", lua_GetActionCooldown},
|
|
{"UseAction", lua_UseAction},
|
|
{"PickupAction", lua_PickupAction},
|
|
{"PlaceAction", lua_PlaceAction},
|
|
{"PickupSpell", lua_PickupSpell},
|
|
{"PickupSpellBookItem", lua_PickupSpellBookItem},
|
|
{"PickupContainerItem", lua_PickupContainerItem},
|
|
{"PickupInventoryItem", lua_PickupInventoryItem},
|
|
{"ClearCursor", lua_ClearCursor},
|
|
{"GetCursorInfo", lua_GetCursorInfo},
|
|
{"CursorHasItem", lua_CursorHasItem},
|
|
{"CursorHasSpell", lua_CursorHasSpell},
|
|
{"DeleteCursorItem", lua_DeleteCursorItem},
|
|
{"AutoEquipCursorItem", lua_AutoEquipCursorItem},
|
|
{"IsShiftKeyDown", lua_IsShiftKeyDown},
|
|
{"IsControlKeyDown", lua_IsControlKeyDown},
|
|
{"IsAltKeyDown", lua_IsAltKeyDown},
|
|
{"IsModifiedClick", lua_IsModifiedClick},
|
|
{"GetModifiedClick", lua_GetModifiedClick},
|
|
{"SetModifiedClick", lua_SetModifiedClick},
|
|
{"GetBindingKey", lua_GetBindingKey},
|
|
{"GetBindingAction", lua_GetBindingAction},
|
|
{"GetNumBindings", lua_GetNumBindings},
|
|
{"GetBinding", lua_GetBinding},
|
|
{"SetBinding", lua_SetBinding},
|
|
{"SaveBindings", lua_SaveBindings},
|
|
{"SetOverrideBindingClick", lua_SetOverrideBindingClick},
|
|
{"ClearOverrideBindings", lua_ClearOverrideBindings},
|
|
{"GetActionBarPage", [](lua_State* L) -> int {
|
|
// Return current action bar page (1-6)
|
|
lua_getglobal(L, "__WoweeActionBarPage");
|
|
if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_pushnumber(L, 1); }
|
|
return 1;
|
|
}},
|
|
{"ChangeActionBarPage", [](lua_State* L) -> int {
|
|
int page = static_cast<int>(luaL_checknumber(L, 1));
|
|
if (page < 1) page = 1;
|
|
if (page > 6) page = 6;
|
|
lua_pushnumber(L, page);
|
|
lua_setglobal(L, "__WoweeActionBarPage");
|
|
// Fire ACTIONBAR_PAGE_CHANGED via the frame event system
|
|
lua_getglobal(L, "__WoweeEvents");
|
|
if (!lua_isnil(L, -1)) {
|
|
lua_getfield(L, -1, "ACTIONBAR_PAGE_CHANGED");
|
|
if (!lua_isnil(L, -1)) {
|
|
int n = static_cast<int>(lua_objlen(L, -1));
|
|
for (int i = 1; i <= n; i++) {
|
|
lua_rawgeti(L, -1, i);
|
|
if (lua_isfunction(L, -1)) {
|
|
lua_pushstring(L, "ACTIONBAR_PAGE_CHANGED");
|
|
if (lua_pcall(L, 1, 0, 0) != 0) {
|
|
LOG_ERROR("LuaEngine: ACTIONBAR_PAGE_CHANGED handler error: ",
|
|
lua_tostring(L, -1) ? lua_tostring(L, -1) : "(unknown)");
|
|
lua_pop(L, 1);
|
|
}
|
|
} else lua_pop(L, 1);
|
|
}
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
lua_pop(L, 1);
|
|
return 0;
|
|
}},
|
|
{"HasPetUI", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
lua_pushboolean(L, gh && gh->hasPet() ? 1 : 0);
|
|
return 1;
|
|
}},
|
|
{"GetPetActionInfo", [](lua_State* L) -> int {
|
|
// GetPetActionInfo(index) → name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled
|
|
auto* gh = getGameHandler(L);
|
|
int index = static_cast<int>(luaL_checknumber(L, 1));
|
|
if (!gh || index < 1 || index > game::GameHandler::PET_ACTION_BAR_SLOTS) {
|
|
return luaReturnNil(L);
|
|
}
|
|
uint32_t packed = gh->getPetActionSlot(index - 1);
|
|
uint32_t spellId = packed & 0x00FFFFFF;
|
|
uint8_t actionType = static_cast<uint8_t>((packed >> 24) & 0xFF);
|
|
if (spellId == 0) { return luaReturnNil(L); }
|
|
const std::string& name = gh->getSpellName(spellId);
|
|
std::string iconPath = gh->getSpellIconPath(spellId);
|
|
lua_pushstring(L, name.empty() ? "Unknown" : name.c_str()); // name
|
|
lua_pushstring(L, ""); // subtext
|
|
lua_pushstring(L, iconPath.empty() ? "Interface\\Icons\\INV_Misc_QuestionMark" : iconPath.c_str()); // texture
|
|
lua_pushboolean(L, 0); // isToken
|
|
lua_pushboolean(L, (actionType & 0xC0) != 0 ? 1 : 0); // isActive
|
|
lua_pushboolean(L, 1); // autoCastAllowed
|
|
lua_pushboolean(L, gh->isPetSpellAutocast(spellId) ? 1 : 0); // autoCastEnabled
|
|
return 7;
|
|
}},
|
|
{"GetPetActionCooldown", [](lua_State* L) -> int {
|
|
lua_pushnumber(L, 0); lua_pushnumber(L, 0); lua_pushnumber(L, 1);
|
|
return 3;
|
|
}},
|
|
{"PetAttack", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && gh->hasPet() && gh->hasTarget())
|
|
gh->sendPetAction(0x00000007 | (2u << 24), gh->getTargetGuid()); // CMD_ATTACK
|
|
return 0;
|
|
}},
|
|
{"PetFollow", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && gh->hasPet())
|
|
gh->sendPetAction(0x00000007 | (1u << 24), 0); // CMD_FOLLOW
|
|
return 0;
|
|
}},
|
|
{"PetWait", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && gh->hasPet())
|
|
gh->sendPetAction(0x00000007 | (0u << 24), 0); // CMD_STAY
|
|
return 0;
|
|
}},
|
|
{"PetPassiveMode", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && gh->hasPet())
|
|
gh->sendPetAction(0x00000007 | (0u << 16), 0); // REACT_PASSIVE
|
|
return 0;
|
|
}},
|
|
{"CastPetAction", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
int index = static_cast<int>(luaL_checknumber(L, 1));
|
|
if (!gh || !gh->hasPet() || index < 1 || index > game::GameHandler::PET_ACTION_BAR_SLOTS) return 0;
|
|
uint32_t packed = gh->getPetActionSlot(index - 1);
|
|
uint32_t spellId = packed & 0x00FFFFFF;
|
|
if (spellId != 0) {
|
|
uint64_t target = gh->hasTarget() ? gh->getTargetGuid() : gh->getPetGuid();
|
|
gh->sendPetAction(packed, target);
|
|
}
|
|
return 0;
|
|
}},
|
|
{"TogglePetAutocast", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
int index = static_cast<int>(luaL_checknumber(L, 1));
|
|
if (!gh || !gh->hasPet() || index < 1 || index > game::GameHandler::PET_ACTION_BAR_SLOTS) return 0;
|
|
uint32_t packed = gh->getPetActionSlot(index - 1);
|
|
uint32_t spellId = packed & 0x00FFFFFF;
|
|
if (spellId != 0) gh->togglePetSpellAutocast(spellId);
|
|
return 0;
|
|
}},
|
|
{"PetDismiss", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && gh->hasPet())
|
|
gh->sendPetAction(0x00000007 | (3u << 24), 0); // CMD_DISMISS
|
|
return 0;
|
|
}},
|
|
{"IsPetAttackActive", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
lua_pushboolean(L, gh && gh->getPetCommand() == 2 ? 1 : 0); // 2=attack
|
|
return 1;
|
|
}},
|
|
{"PetDefensiveMode", [](lua_State* L) -> int {
|
|
auto* gh = getGameHandler(L);
|
|
if (gh && gh->hasPet())
|
|
gh->sendPetAction(0x00000007 | (1u << 16), 0); // REACT_DEFENSIVE
|
|
return 0;
|
|
}},
|
|
};
|
|
for (const auto& [name, func] : api) {
|
|
lua_pushcfunction(L, func);
|
|
lua_setglobal(L, name);
|
|
}
|
|
}
|
|
|
|
} // namespace wowee::addons
|