diff --git a/CMakeLists.txt b/CMakeLists.txt index d195aeb597..8a262e1fae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,10 @@ set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) # Set projectname (must be done AFTER setting configurationtypes) project(DestinyCore) +if(MSVC) + add_compile_options(/utf-8) +endif() + if(POLICY CMP0144) cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case _ROOT variables endif() diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000000..d966194d45 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,54 @@ +{ + "configurations": [ + { + "name": "x64-RelWithDebInfo", + "generator": "Visual Studio 17 2022 Win64", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-m -v:minimal", + "ctestCommandArgs": "", + "variables": [ + { + "name": "MYSQL_INCLUDE_DIR", + "value": "L:/DC735/mysql-8.4.2-winx64/include", + "type": "PATH" + }, + { + "name": "MYSQL_LIBRARY", + "value": "L:/DC735/mysql-8.4.2-winx64/lib/libmysql.lib", + "type": "FILEPATH" + }, + { + "name": "BOOST_ROOT", + "value": "C:/local/boost_1_84_0", + "type": "PATH" + }, + { + "name": "OPENSSL_ROOT_DIR", + "value": "C:/Program Files/OpenSSL-Win64", + "type": "PATH" + }, + { + "name": "TOOLS_BUILD", + "value": "ON", + "type": "BOOL" + }, + + { + "name": "Boost_DIR", + "value": "C:/local/boost_1_84_0/lib64-msvc-14.3/cmake/Boost-1.84.0", + "type": "PATH" + }, + + { + "name": "SCRIPTS", + "value": "static", + "type": "STRING" + } + ] + } + ] +} \ No newline at end of file diff --git a/M.txt b/M.txt new file mode 100644 index 0000000000..44f14b6da6 --- /dev/null +++ b/M.txt @@ -0,0 +1,232 @@ +# AI机器人玩家等级自动同步机制 — 源码调查记录 + +## 核心调度处理(终点) + +**文件:** `src/server/game/Server/PlayerBotSession.cpp` + +- **第 233 行** — `CastSchedule()` 中 case `BGSType_Settting` 调用 `ProcessSetting(schedule)` +- **第 353–389 行** — `ProcessSetting()` 最终调用: + +```cpp +player->ResetPlayerToLevel(schedule.parameter2, flushTalent, needTenacity); +``` + +这里 `parameter1` 和 `parameter2` 分别代表等级的最小值和最大值,`parameter2`(即目标等级)被传入 `ResetPlayerToLevel()`。 + +--- + +## 触发路径一:加入队伍后立即同步 + +**文件:** `src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp` +- **第 304–325 行** — `ProcessSetting()` 方法 + +```cpp +void BotGroupAI::ProcessSetting() +{ + // ...前置检查... + BotGlobleSchedule schedule2(BotGlobleScheduleType::BGSType_Settting, 0); + schedule2.parameter1 = m_MasterPlayer->getLevel(); // ← 直接设为队长等级 + schedule2.parameter2 = m_MasterPlayer->getLevel(); // ← 直接设为队长等级 + schedule2.parameter3 = PlayerBotSetting::FindPlayerTalentType(me) + 1; + pSession->PushScheduleToQueue(schedule2); +} +``` + +- **第 676 行** — `ProcessCombat()` 中调用 `ProcessSetting()` +- **第 849 行** — `UpdateAI()` 的每帧更新中调用 `ProcessCombat()` + +这条路径的特点是:**无条件将机器人等级设为与队长完全一致**。 + +--- + +## 触发路径二:队伍中的持续等级检查 + +**文件:** `src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp` +- **第 1331–1379 行** — `TrySettingToMaster()` 方法 + +```cpp +bool BotGroupAI::TrySettingToMaster() +{ + static int32 gapLV = 2; // ← 允许 ±2 级差距 + if (!BotUtility::BotCanSettingToMaster) + return false; + // ...前置检查... + + int32 masterLV = m_MasterPlayer->getLevel(); + int32 minLV = (masterLV <= gapLV) ? 1 : masterLV - gapLV; + int32 maxLV = (masterLV + gapLV >= worldMaxLevel) ? worldMaxLevel : masterLV + gapLV; + + // 如果机器人等级在 [minLV, maxLV] 范围内,不调整 + if (meLV >= minLV && meLV <= maxLV) + return false; + + BotGlobleSchedule schedule2(BotGlobleScheduleType::BGSType_Settting, 0); + schedule2.parameter1 = minLV; + schedule2.parameter2 = maxLV; + pSession->PushScheduleToQueue(schedule2); +} +``` + +- **第 851 行** — `UpdateAI()` 中每帧调用 `TrySettingToMaster()` + +这条路径的特点是:**允许 ±2 级差距,超出范围才调整**。但注意 `gapLV = 2` 是 `static` 变量,且受 `BotUtility::BotCanSettingToMaster` 开关控制。 + +--- + +## 完整调用链总结 + +``` +玩家邀请机器人 → 机器人加入队伍 → AI类型切换为 BotGroupAI + │ + ├─ UpdateAI() 每帧执行 + │ ├─ ProcessCombat() → ProcessSetting() + │ │ └→ PushSchedule(BGSType_Settting, parameter1=队长等级, parameter2=队长等级) + │ │ + │ └─ TrySettingToMaster() + │ └→ PushSchedule(BGSType_Settting, parameter1=master-2, parameter2=master+2) + │ + └─ PlayerBotSession::CastSchedule() 处理调度 + └─ ProcessSetting(schedule) → player->ResetPlayerToLevel(parameter2, ...) +``` + +--- + +## 如果你想关闭这个机制,关键修改点 + +| 位置 | 文件路径(相对) | 行号 | 作用 | +|------|------------------|------|------| +| **路径一** | `src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp` | 304–325 | `ProcessSetting()` — 无条件同步到队长等级 | +| **路径二** | `src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp` | 1331–1379 | `TrySettingToMaster()` — ±2级范围内才调整 | +| **开关** | `BotUtility`(相关头文件) | - | `BotCanSettingToMaster` 布尔变量,控制路径二是否生效 | + +两条路径都会往调度队列里塞入 `BGSType_Settting`,最终都走 `ProcessSetting()` → `ResetPlayerToLevel()`。如果你想让机器人保持自己的等级不变,需要修改这两处逻辑(或者关闭 `BotCanSettingToMaster` 开关)。 + +--- + +## 其他相关触发点(非队伍场景) + +以下位置也会使用 `BGSType_Settting` 调度来调整机器人等级,但属于野外/战场等场景: + +| 文件 | 行号 | 场景 | +|------|------|------| +| `src/server/game/PlayerBot/FieldBotMgr.cpp` | 517–521 | 野战(Warfare)中传送时等级差距调整 | +| `src/server/game/PlayerBot/FieldBotMgr.cpp` | 762–766 | 跟随目标玩家时等级差距 >2 的调整 | +| `src/server/game/PlayerBot/PlayerBotMgr.cpp` | 1308–1309 | 登录时的初始等级设置 | +| `src/server/game/PlayerBot/PlayerBotMgr.cpp` | 2569–2570 / 2615–2616 | 副本需求等级匹配 | + +--- + +*调查日期:2026-06-18* + +--- + +# AI机器人玩家控制命令清单 + +## 命令触发机制 + +聊天消息通过 `ChatHandler.cpp` 第 402 行路由:当队长在队伍频道(/pl)发送消息时,调用 `Group::ProcessGroupBotCommand()`,该函数遍历队伍中所有机器人并分发命令。 + +--- + +## 一、队伍模式命令(BotGroupAI) + +**文件:** `src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp` — `ProcessBotCommand()` (第 654–739 行) + +| 命令 | 用法 | 说明 | +|------|------|------| +| `summon` | `/pl summon` | 召唤机器人到身边 | +| `attack` | `/pl attack` | 攻击队长当前选中的目标 | +| `follow` | `/pl follow` | 跟随队长(清除逃跑/停止状态) | +| `flee` | `/pl flee` | 强制逃跑 | +| `stop` | `/pl stop` | 停止移动和攻击 | +| `setting` | `/pl setting` | 立即执行等级/天赋设置同步 | +| `c` | `/pl c` | 列出机器人当前装备 | +| `e <物品ID>` | `/pl e 12345` | 给机器人穿上指定ID的装备 | +| `ue <物品栏位>` | `/pl ue 16` | 卸下机器人指定栏位的装备(16=主手等) | +| `destroy <物品ID>` | `/pl destroy 12345` | 销毁机器人身上指定ID的物品 | +| `s <物品ID>` | `/pl s 12345` | 将指定ID的物品交易给队长 | +| `u <物品ID>` | `/pl u 12345` | 让机器人使用指定ID的物品 | +| `talent` | `/pl talent [参数]` | 查看/重置天赋 | +| `rite` | `/pl rite` | 在战场等待加入阶段召唤传送门(仅限战场) | +| `<法术ID>` | `/pl 40827` | 对队长目标施放指定ID的法术(数字命令,范围1-80000) | + +--- + +## 二、战场模式命令(BotBGAI) + +**文件:** `src/server/game/AI/PlayerAI/BotAI.cpp` — `ProcessBotCommand()` (第 158–169 行) + +| 命令 | 用法 | 说明 | +|------|------|------| +| `rite` | `/pl rite` | 在战场等待阶段召唤传送门 | +| `attack` | `/pl attack` | 攻击队长当前选中的目标 | + +--- + +## 三、队伍全局命令(仅队长可用) + +**文件:** `src/server/game/Groups/Group.cpp` — `ProcessGroupBotCommand()` (第 2602–2741 行) + +| 命令 | 用法 | 说明 | +|------|------|------| +| `seduce` | `/pl seduce` | 指定一个怪物,让一个机器人去吸引仇恨(其他机器人逃跑) | +| `pulls` | `/pl pulls` | 坦克机器人拉取目标列表 | +| `formation combat` | `/pl formation combat` | 所有机器人进入战斗队形 | +| `formation ring` | `/pl formation ring` | 所有机器人进入环形队形 | +| `formation random` | `/pl formation random` | 随机移动位置 | + +--- + +## 四、角色前缀命令(配合上述命令使用) + +在命令前加 `@` + 角色类型前缀,可限定只影响特定类型的机器人: + +| 前缀 | 含义 | +|------|------| +| `@tank` | 坦克型机器人 | +| `@melee` | 近战DPS机器人 | +| `@ranged` | 远程DPS机器人 | +| `@heal` | 治疗机器人 | +| `@dps` | 所有DPS机器人(不含治疗) | + +职业缩写前缀: + +| 前缀 | 职业 | +|------|------| +| `zs` | 战士 | +| `qs` | 圣骑士 | +| `lr` | 猎人 | +| `dz` | 潜行者 | +| `ms` | 牧师 | +| `dk` | 死亡骑士 | +| `sm` | 萨满祭司 | +| `fs` | 法师 | +| `ss` | 术士 | +| `xd` | 德鲁伊 | + +**示例:** `/pl @tank attack` — 只让坦克机器人攻击队长目标;`/pl @melee formation combat` — 只有近战DPS进入战斗队形。 + +--- + +## 五、命令路由完整流程 + +``` +玩家输入 /pl <命令> + │ + ├─ ChatHandler.cpp:402 → Group::ProcessGroupBotCommand() + │ │ + │ ├─ seduce → 特殊处理(指定怪物吸引仇恨) + │ ├─ pulls/formation → 队伍全局操作 + │ └─ @前缀解析 → 限定机器人类型 + │ │ + │ └→ 遍历所有BotGroupAI/BotBGAI → ProcessBotCommand() + │ │ + │ └→ 匹配命令字符串 → 执行对应方法 + │ + └─ AI-PartyTalk(非命令)→ 数据库查询 ai_talk_group / ai_talk_group_locale + └→ 机器人随机回复聊天内容(REGEXP匹配) +``` + +--- + +*调查日期:2026-06-18* diff --git a/M2.txt b/M2.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/common/Cryptography/OpenSSLCrypto.cpp b/src/common/Cryptography/OpenSSLCrypto.cpp index db5e2e0153..cb0b8ed117 100644 --- a/src/common/Cryptography/OpenSSLCrypto.cpp +++ b/src/common/Cryptography/OpenSSLCrypto.cpp @@ -23,6 +23,12 @@ OSSL_PROVIDER* LegacyProvider; void OpenSSLCrypto::threadsSetup([[maybe_unused]] boost::filesystem::path const& providerModulePath) { + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PROVIDER_load(nullptr, "legacy"); + OSSL_PROVIDER_load(nullptr, "default"); +#endif + #ifdef VALGRIND ValgrindRandomSetup(); #endif diff --git a/src/server/game/AI/PlayerAI/BotAI.cpp b/src/server/game/AI/PlayerAI/BotAI.cpp index 013615305c..908901a489 100644 --- a/src/server/game/AI/PlayerAI/BotAI.cpp +++ b/src/server/game/AI/PlayerAI/BotAI.cpp @@ -320,9 +320,18 @@ void BotBGAI::ProcessAttackCommand(Player* srcPlayer) return; if (me->GetDistance(pMasterTarget->GetPosition()) > BOTAI_SEARCH_RANGE) return; - me->SetSelection(pMasterTarget->GetGUID()); -} + + me->SetSelection(pMasterTarget->GetGUID()); // 原有代码 + // ================== 新增代码 ================== + me->Attack(pMasterTarget, true); + if (me->GetMotionMaster()) + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(pMasterTarget); + } + // ================== 新增代码 ================== +} void BotBGAI::EachTick() { m_RangeCreatureLists.clear(); @@ -473,6 +482,13 @@ void BotBGAI::DamageEndure(Unit* attacker, uint32& damage, DamageEffectType dama void BotBGAI::UpdateAI(uint32 diff) { + + + + // 【AI 导演系统拦截】如果处于休眠状态,直接跳过所有底层运算! + if (m_isDirectorSleeping) + return; + m_UpdateTick -= diff; if (m_UpdateTick <= 0) @@ -1465,6 +1481,10 @@ NearUnitVec BotBGAI::SearchFarEnemy(float minRange, float maxRange) void BotBGAI::UpdateBotAI(uint32 diff) { + // 【AI 导演系统:基础拦截锁】 + if (m_isDirectorSleeping) + return; + EachTick(); if (me->HasUnitState(UNIT_STATE_CASTING)) { @@ -1616,7 +1636,14 @@ void BotBGAI::UpdateBotAI(uint32 diff) void BotBGAI::UpdateBotAI(uint32 diff) { - Battleground* pBattleground = me->GetBattleground(); + + + // 【AI 导演系统:基础拦截锁】 + if (m_isDirectorSleeping) + return; + + + Battleground* pBattleground = me->GetBattleground(); if (!pBattleground) return; diff --git a/src/server/game/AI/PlayerAI/BotAI.h b/src/server/game/AI/PlayerAI/BotAI.h index 5bc3e4bc5c..06de224213 100644 --- a/src/server/game/AI/PlayerAI/BotAI.h +++ b/src/server/game/AI/PlayerAI/BotAI.h @@ -91,6 +91,14 @@ class TC_GAME_API BotBGAI : public PlayerAI bool SetMovetoUseGOTarget(ObjectGuid target) { return m_MovetoUseGO.SetNeedMovetoUseGO(target); } void ProcessBotCommand(Player* srcPlayer, std::string cmd); + // ========================================== + // 【AI 导演系统专属控制接口】 + // ========================================== + void SetDirectorSleep(bool sleep) { m_isDirectorSleeping = sleep; } + bool IsDirectorSleeping() const { return m_isDirectorSleeping; } + + + protected: bool CanReciveCommand(std::string& cmd, std::string& param); void ProcessSummonRiteSpell(Player* srcPlayer); @@ -212,8 +220,15 @@ class TC_GAME_API BotBGAI : public PlayerAI UINT_SET m_FilterCreatureEntrys; - uint32 BotCommon_ClearAllCtrl;// = 59752; // + uint32 BotCommon_ClearAllCtrl;// = 59752; // uint32 m_lastClearCtrlTick; + + // ========================================== + // 【AI 导演系统变量】默认所有机器人出生即休眠! + // ========================================== + bool m_isDirectorSleeping = true; + uint32 m_directorCheckTimer = 0; // 下班检测计时器 + }; #endif // !_BOT_AI_H_ diff --git a/src/server/game/AI/PlayerAI/BotAITool.cpp b/src/server/game/AI/PlayerAI/BotAITool.cpp index cb9f32308f..f307082bec 100644 --- a/src/server/game/AI/PlayerAI/BotAITool.cpp +++ b/src/server/game/AI/PlayerAI/BotAITool.cpp @@ -634,9 +634,14 @@ void BotUtility::ProcessGroupRingMovement(Player* pCenterPlayer, BOTAI_WORKTYPE float distZ = centerPos.GetPositionZ(); distZ = pCenterUnit->GetMap()->GetHeight(pCenterUnit->GetPhaseShift(), distX, distY, distZ); Position resultPos(distX, distY, distZ, pCenterUnit->GetOrientation()); - Position pos; - pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); - pAI->SetCruxMovement(pos); + //Position pos; + //pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); + //pAI->SetCruxMovement(pos); + + // ================== 修改后 ================== + // 直接使用算好的 resultPos(底层 mmaps 寻路会自动防止穿墙) + pAI->SetCruxMovement(resultPos); + pGroupPlayer->SetSelection(ObjectGuid::Empty); pAI->ClearTankTarget(); } @@ -655,9 +660,14 @@ void BotUtility::ProcessGroupRingMovement(Player* pCenterPlayer, BOTAI_WORKTYPE float distZ = centerPos.GetPositionZ(); distZ = pCenterUnit->GetMap()->GetHeight(pCenterUnit->GetPhaseShift(), distX, distY, distZ); Position resultPos(distX, distY, distZ, pCenterUnit->GetOrientation()); - Position pos; - pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); - pAI->SetCruxMovement(pos); + //Position pos; + //pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); + //pAI->SetCruxMovement(pos); + + // ================== 修改后 ================== + // 直接使用算好的 resultPos(底层 mmaps 寻路会自动防止穿墙) + pAI->SetCruxMovement(resultPos); + pGroupPlayer->SetSelection(ObjectGuid::Empty); pAI->ClearTankTarget(); } @@ -717,9 +727,15 @@ void BotUtility::ProcessGroupCombatMovement(Player* pCenterPlayer, BOTAI_WORKTYP float distZ = centerPos.GetPositionZ(); distZ = pCenterUnit->GetMap()->GetHeight(pCenterUnit->GetPhaseShift(), distX, distY, distZ); Position resultPos(distX, distY, distZ, pCenterUnit->GetOrientation()); - Position pos; - pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); - pAI->SetCruxMovement(pos); + //Position pos; + //pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); + //pAI->SetCruxMovement(pos); + + // ================== 修改后 ================== + // 直接使用算好的 resultPos(底层 mmaps 寻路会自动防止穿墙) + pAI->SetCruxMovement(resultPos); + + pGroupPlayer->SetSelection(ObjectGuid::Empty); pAI->ClearTankTarget(); } @@ -738,9 +754,14 @@ void BotUtility::ProcessGroupCombatMovement(Player* pCenterPlayer, BOTAI_WORKTYP float distZ = centerPos.GetPositionZ(); distZ = pCenterUnit->GetMap()->GetHeight(pCenterUnit->GetPhaseShift(), distX, distY, distZ); Position resultPos(distX, distY, distZ, pCenterUnit->GetOrientation()); - Position pos; - pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); - pAI->SetCruxMovement(pos); + //Position pos; + //pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); + //pAI->SetCruxMovement(pos); + + // ================== 修改后 ================== + // 直接使用算好的 resultPos(底层 mmaps 寻路会自动防止穿墙) + pAI->SetCruxMovement(resultPos); + pGroupPlayer->SetSelection(ObjectGuid::Empty); pAI->ClearTankTarget(); } @@ -759,9 +780,15 @@ void BotUtility::ProcessGroupCombatMovement(Player* pCenterPlayer, BOTAI_WORKTYP float distZ = centerPos.GetPositionZ(); distZ = pCenterUnit->GetMap()->GetHeight(pCenterUnit->GetPhaseShift(), distX, distY, distZ); Position resultPos(distX, distY, distZ, pCenterUnit->GetOrientation()); - Position pos; - pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); - pAI->SetCruxMovement(pos); + //Position pos; + //pCenterUnit->GetFirstCollisionPosition(pCenterUnit->GetDistance(resultPos), pCenterUnit->GetRelativeAngle(&resultPos)); + //pAI->SetCruxMovement(pos); + + // ================== 修改后 ================== + // 直接使用算好的 resultPos(底层 mmaps 寻路会自动防止穿墙) + pAI->SetCruxMovement(resultPos); + + pGroupPlayer->SetSelection(ObjectGuid::Empty); pAI->ClearTankTarget(); } diff --git a/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.cpp b/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.cpp index 22d536636a..ec23b6fadc 100644 --- a/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.cpp +++ b/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.cpp @@ -106,6 +106,12 @@ m_HasReset(false) { BotUtility::PlayerBotTogglePVP(player, true); } + // ================== 新增代码开始 ================== + // 初始化自动升级定时器为 15 到 30 分钟随机触发一次(单位:毫秒) + // 注意:如果你现在是为了测试,可以暂时改成 1 分钟,比如: + // m_AutoLevelTimer = urand(1 * MINUTE * IN_MILLISECONDS, 2 * MINUTE * IN_MILLISECONDS); + m_AutoLevelTimer = urand(15 * MINUTE * IN_MILLISECONDS, 30 * MINUTE * IN_MILLISECONDS); + // ================== 新增代码结束 ================== } BotFieldAI::~BotFieldAI() @@ -115,97 +121,458 @@ BotFieldAI::~BotFieldAI() void BotFieldAI::UpdateAI(uint32 diff) { - m_UpdateTick -= diff; - if (m_UpdateTick > 0) - return; - m_UpdateTick = BOTAI_UPDATE_TICK; + // ========================================================== + // 【AI 导演系统:野生群演控制中枢 (BotFieldAI 专属)】 + // ========================================================== + if (m_isDirectorDrafted) + { + // 【0. 距离计算与防走丢判定】 + float distToAnchor = 0.0f; + bool outOfBounds = false; + + if (m_directorAnchorMapId != 0) + { + if (me->GetMapId() != m_directorAnchorMapId) + outOfBounds = true; + else + { + float dx = me->GetPositionX() - m_directorAnchorX; + float dy = me->GetPositionY() - m_directorAnchorY; + distToAnchor = std::sqrt(dx*dx + dy*dy); + + // 只有超过 150 码(确诊掉出世界或被传送走)才暴力引渡 + if (distToAnchor > 150.0f) outOfBounds = true; + } + + if (outOfBounds) + { + me->InterruptNonMeleeSpells(false); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->StopMoving(); + me->GetMotionMaster()->Clear(); + + bool teleSuccess = me->TeleportTo(m_directorAnchorMapId, m_directorAnchorX, m_directorAnchorY, m_directorAnchorZ, frand(0.0f, 6.28f)); + if (!teleSuccess && me->GetMapId() == m_directorAnchorMapId) + me->Relocate(m_directorAnchorX, m_directorAnchorY, m_directorAnchorZ, frand(0.0f, 6.28f)); + + return; // 等待传送落地 + } + } + + // 【1. 星探赎身机制】 + if (me->GetGroup() || me->GetGroupInvite()) + { + SetDirectorDrafted(false); + m_isDirectorSleeping = false; + me->SetStandState(UNIT_STAND_STATE_STAND); + TC_LOG_ERROR("server", ">>> [星探发掘] 群演 [%s] 恢复自由身!", me->GetName().c_str()); + } + else + { + // 【2. 常规群演苦力逻辑】 + m_directorCheckTimer += diff; + + // 【修复报错1】:回退为原本绝对正确的宏定义获取方式 + bool isInCity = me->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); + // 城里 300 码内不休眠(基本覆盖主城),野外保持 65 码 + float sleepRadius = isInCity ? 300.0f : 65.0f; + float wakeRadius = isInCity ? 150.0f : 50.0f; + + if (m_directorCheckTimer >= 3000) + { + m_directorCheckTimer = 0; + float nearestDist = 9999.0f; + Player* nearestP = nullptr; + + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player* p = itr->GetSource(); + if (p && !p->IsPlayerBot() && p->IsAlive()) + { + float d = p->GetDistance(me); + if (d < nearestDist) { nearestDist = d; nearestP = p; } + } + } + + if (m_isDirectorSleeping) + { + // 【优化1】:使用动态唤醒距离 + if (nearestP && nearestDist < wakeRadius) + { + m_isDirectorSleeping = false; + me->SetStandState(UNIT_STAND_STATE_STAND); + me->Dismount(); + + // 【核心掩护】:唤醒时立刻向四周散开 + m_directorWanderTimer = 6000; + } + } + else + { + // 【优化2】:使用动态休眠距离 + if (!nearestP || nearestDist >= sleepRadius) + { + m_isDirectorSleeping = true; + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->SetStandState(UNIT_STAND_STATE_SIT); + } + } + } + + // --- 提线木偶微型大脑 (终极防卡墙版) --- + if (!m_isDirectorSleeping && !me->IsInCombat()) + { + // ================= 【修复1:绝对独立的倒计时锁】 ================= + if (m_directorInteractTimer > 0) + { + if (m_directorInteractTimer > diff) + { + m_directorInteractTimer -= diff; + // 发呆期间,有极其微小的概率做个动作假装活人 + if (urand(1, 1000) <= 2) + { + uint32 emotes[] = { EMOTE_ONESHOT_TALK, EMOTE_ONESHOT_BOW, EMOTE_ONESHOT_QUESTION, EMOTE_ONESHOT_EXCLAMATION }; + me->HandleEmoteCommand(emotes[urand(0, 3)]); + } + return; // 倒计时没走完,物理切断本帧,绝对不准思考! + } + else + { + m_directorInteractTimer = 0; // 倒计时完毕,解锁大脑! + } + } + + // ================= 思考与寻路逻辑 ================= + m_directorWanderTimer += diff; + + if (m_directorWanderTimer >= 4000) + { + m_directorWanderTimer = urand(0, 1500); + bool isAttacking = false; + + bool isInCity = me->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); + + // --- 野外打野逻辑 --- + if (!isInCity && urand(1, 100) <= 30) + { + Creature* target = nullptr; + std::list creatureList; + me->GetCreatureListWithEntryInGrid(creatureList, 0, 25.0f); + for (auto c : creatureList) + { + if (c && c->IsAlive() && me->IsValidAttackTarget(c)) + { + if (c->getFaction() == 160) continue; + if (c->HasUnitFlag((UnitFlags)4)) continue; + if (c->GetMaxHealth() > 5000000 && !c->IsInCombatWith(me)) continue; + target = c; + break; + } + } + + if (target) + { + // 【破解死结1】:打怪前强行站立 + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetWalk(false); + me->Attack(target, true); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(target); + isAttacking = true; + } + } + + // --- 行为树分流 --- + if (!isAttacking) + { + // 【破解死结2】:每次移动前,绝对强行站立并清空移动队列!无论它刚才是不是坐着! + me->SetStandState(UNIT_STAND_STATE_STAND); + me->GetMotionMaster()->Clear(); + me->SetWalk(true); + + if (!isInCity && distToAnchor > 40.0f) + { + me->GetMotionMaster()->MovePoint(1, m_directorAnchorX, m_directorAnchorY, m_directorAnchorZ); + m_directorInteractTimer = urand(5000, 15000); + } + else if (!isInCity) + { + // 【破解死结3】:野外街溜子使用原生安全寻路 + me->GetMotionMaster()->MoveRandom(15.0f); + m_directorInteractTimer = urand(5000, 10000); + } + else if (isInCity) // 主城 + { + int destinyRoll = urand(1, 100); + + // 【第一层:30% 几率】去绝对坐标:银行/拍卖行 + if (destinyRoll <= 30) + { + static const struct { uint32 mapId; float x, y, z; } CityPOIs[] = { + {1, 1643.72f, -4443.32f, 18.62f}, {1, 1513.90f, -4354.57f, 20.55f}, + {0, 1588.68f, 241.05f, -52.14f}, {0, 1579.11f, 187.98f, -56.79f}, + {530, 9683.39f, -7520.52f, 15.74f}, {530, 9805.19f, -7487.27f, 13.55f}, + {530, -2000.13f, 5350.65f, -9.35f}, {530, -2023.69f, 5390.86f, -7.48f}, + {571, 5927.02f, 729.74f, 642.13f}, {571, 5627.04f, 693.37f, 651.99f}, + {0, -8888.40f, 566.25f, 93.35f}, {0, -8823.97f, 683.89f, 97.23f}, + {0, -4889.87f, -993.15f, 503.94f}, {0, -4902.07f, -973.80f, 501.52f}, + {530, -4022.18f, -11734.09f, -151.85f}, {530, -3919.18f, -11547.06f, -150.15f} + }; + + std::vector validIndices; + for (int i = 0; i < sizeof(CityPOIs) / sizeof(CityPOIs[0]); ++i) + { + if (CityPOIs[i].mapId == me->GetMapId() && me->GetDistance2d(CityPOIs[i].x, CityPOIs[i].y) < 400.0f) + validIndices.push_back(i); + } + + if (!validIndices.empty()) + { + int targetIndex = validIndices[urand(0, validIndices.size() - 1)]; + float offsetAngle = frand(0.0f, 6.28f); + // 【破解死结4】:室内坐标散布范围从 12码 极度缩小到 3码以内,绝对防止卡墙宕机! + float offsetDist = frand(1.0f, 3.0f); + + me->GetMotionMaster()->MovePoint(1, + CityPOIs[targetIndex].x + offsetDist * std::cos(offsetAngle), + CityPOIs[targetIndex].y + offsetDist * std::sin(offsetAngle), + CityPOIs[targetIndex].z); + m_directorInteractTimer = urand(15000, 45000); + return; // 正确切断 + } + } + + // 【第二层:40% 几率】在附近找NPC、邮箱或椅子 + if (destinyRoll <= 70) + { + Creature* poiNpc = nullptr; + GameObject* poiGo = nullptr; + + std::vector validNpcs; + std::list creatureList; + me->GetCreatureListWithEntryInGrid(creatureList, 0, 100.0f); + for (auto c : creatureList) + { + if (c && c->IsAlive() && c->IsFriendlyTo(me) && !c->IsPet()) + { + if (c->IsVendor() || c->IsGossip() || c->IsQuestGiver()) + validNpcs.push_back(c); + } + } + + if (!validNpcs.empty()) + poiNpc = validNpcs[urand(0, validNpcs.size() - 1)]; + + if (!poiNpc) + { + std::vector validGos; + std::list goList; + me->GetGameObjectListWithEntryInGrid(goList, 0, 80.0f); + for (auto go : goList) + { + if (go && (go->GetGoType() == GAMEOBJECT_TYPE_MAILBOX || go->GetGoType() == GAMEOBJECT_TYPE_CHAIR)) + validGos.push_back(go); + } + if (!validGos.empty()) + poiGo = validGos[urand(0, validGos.size() - 1)]; + } + + if (poiNpc) + { + float angle = frand(0.0f, 6.28f); + float dist = frand(1.5f, 3.0f); + me->GetMotionMaster()->MovePoint(1, poiNpc->GetPositionX() + dist * std::cos(angle), poiNpc->GetPositionY() + dist * std::sin(angle), poiNpc->GetPositionZ()); + me->SetFacingToObject(poiNpc); + + m_directorInteractTimer = urand(10000, 30000); + if (urand(1, 100) <= 20) me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + } + else if (poiGo) + { + float angle = frand(0.0f, 6.28f); + float dist = (poiGo->GetGoType() == GAMEOBJECT_TYPE_MAILBOX) ? frand(1.0f, 2.0f) : 0.0f; + me->GetMotionMaster()->MovePoint(1, poiGo->GetPositionX() + dist * std::cos(angle), poiGo->GetPositionY() + dist * std::sin(angle), poiGo->GetPositionZ()); + me->SetFacingToObject(poiGo); + + if (poiGo->GetGoType() == GAMEOBJECT_TYPE_MAILBOX) + { + m_directorInteractTimer = urand(10000, 25000); + me->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); + } + else + { + m_directorInteractTimer = urand(15000, 45000); + me->HandleEmoteCommand(EMOTE_STATE_SIT); + } + } + else + { + // 没找到任何东西,直接随机安全漫步 + me->GetMotionMaster()->MoveRandom(20.0f); + m_directorInteractTimer = urand(5000, 15000); + } + } + else + { + // 【第三层:剩余 30% 几率】使用核心原生安全寻路 MoveRandom + // 彻底摒弃手工三角函数带来的卡墙 bug! + me->GetMotionMaster()->MoveRandom(35.0f); + m_directorInteractTimer = urand(5000, 15000); + } + } + } + } + } + + // ================= 核心洛巴托米手术 ================= + if (m_isDirectorSleeping) return; + if (!me->IsInCombat()) return; + } + } + + // 强行驻留锁 + if (m_isCommandStopped) + { + me->StopMoving(); + return; + } + + // ================== 新增自动升级逻辑 ================== + if (me->IsAlive() && !me->IsInCombat()) + { + if (m_AutoLevelTimer <= diff) + { + uint8 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + if (me->getLevel() < maxLevel) + { + me->GiveLevel(me->getLevel() + 1); + } + m_AutoLevelTimer = urand(15 * MINUTE * IN_MILLISECONDS, 30 * MINUTE * IN_MILLISECONDS); + } + else + { + m_AutoLevelTimer -= diff; + } + } + // ====================================================== + + m_UpdateTick -= diff; + if (m_UpdateTick > 0) + return; + m_UpdateTick = BOTAI_UPDATE_TICK; + + if (!me->IsSettingFinish()) + return; + UpdateTeleport(BOTAI_UPDATE_TICK); + if (!m_Teleporting.CanMovement()) + return; + me->UpdateObjectVisibility(false); + m_Guild.UpdateGuildProcess(); + if (ProcessGroupInvite()) + return; + if (IsBGSchedule()) + { + BotUtility::TryTeleportHome(this); + return; + } + + if (!m_HasReset) + ResetBotAI(); + + if (me->IsAlive()) + { + // ================== 核心 AI 升级:全系能量无尽模式 ================== + for (uint8 i = 0; i < MAX_POWERS; ++i) + { + if (me->GetMaxPower((Powers)i) > 0 && me->GetPower((Powers)i) < me->GetMaxPower((Powers)i)) + { + me->SetPower((Powers)i, me->GetMaxPower((Powers)i)); + } + } + // ======================================================================= - if (!me->IsSettingFinish()) - return; - UpdateTeleport(BOTAI_UPDATE_TICK); - if (!m_Teleporting.CanMovement()) - return; - me->UpdateObjectVisibility(false); - m_Guild.UpdateGuildProcess(); - if (ProcessGroupInvite()) - return; - if (IsBGSchedule()) - { - BotUtility::TryTeleportHome(this); - return; - } - - if (!m_HasReset) - ResetBotAI(); - if (me->IsAlive()) - { Position pos = me->GetPosition(); - m_CheckStoped.UpdatePosition(diff); - BotUtility::TryTeleportPlayerPet(me); - ClearMechanicAura(); - if (!IsNotMovement()) - ProcessHorror(diff); - if (NeedWaitSpecialSpell(BOTAI_UPDATE_TICK)) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - if (!m_CruxMovement.HasCruxMovement() && NonCombatProcess()) - return; - - if (!me->IsInCombat() && ProcessNormalSpell()) - return; - m_Movement->SyncPosition(pos); - if (TryUpMount()) - return; - if (!me->HasAura(m_UseMountID) && !me->HasUnitState(UNIT_STATE_CASTING)) - m_UsePotion.TryUsePotion(); - if (me->IsInCombat()) - UpEnergy(); - Unit* pTarget = GetBotAIValidSelectedUnit(); - if (m_CruxMovement.HasCruxMovement()) - { - m_CruxMovement.UpdateCruxMovement(m_Movement); - } - else if (pTarget && pTarget->IsAlive() && !IsInvincible(pTarget)) - { - float distance = me->GetDistance(pTarget->GetPosition()); - if (distance < BOTAI_SEARCH_RANGE) - { - if (IsHealerBotAI() && me->getLevel() >= 10) - ProcessHealth(); - else - ProcessCombat(pTarget); - } - else if (distance > BOTAI_SEARCH_RANGE * 2.5f || me->GetMap() != pTarget->GetMap()) - { - //me->StopMoving(); - me->SetSelection(ObjectGuid::Empty); - } - else - { - m_Movement->MovementToTarget(); - } - } - else if (pTarget = GetCombatTarget()) - { - me->AttackStop(); - me->SetSelection(pTarget->GetGUID()); - } - else - { - me->SetSelection(ObjectGuid::Empty); - ProcessIDLE(); - } - } - else - { - m_CastRecords.ClearRecordSpell(); - m_WishStore.ClearStores(); - m_CruxMovement.ClearMovement(); - me->SetSelection(ObjectGuid::Empty); - m_Revive.UpdateRevive(BOTAI_UPDATE_TICK, m_Teleporting); - } + m_CheckStoped.UpdatePosition(diff); + BotUtility::TryTeleportPlayerPet(me); + ClearMechanicAura(); + if (!IsNotMovement()) + ProcessHorror(diff); + if (NeedWaitSpecialSpell(BOTAI_UPDATE_TICK)) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + if (!m_CruxMovement.HasCruxMovement() && NonCombatProcess()) + return; + + if (!me->IsInCombat() && ProcessNormalSpell()) + return; + m_Movement->SyncPosition(pos); + if (TryUpMount()) + return; + if (!me->HasAura(m_UseMountID) && !me->HasUnitState(UNIT_STATE_CASTING)) + m_UsePotion.TryUsePotion(); + if (me->IsInCombat()) + UpEnergy(); + Unit* pTarget = GetBotAIValidSelectedUnit(); + + // ================== 核心 AI 升级:野外防卫本能 ================== + if (!pTarget && me->IsInCombat()) + { + Unit* pAttacker = me->getAttackerForHelper(); + if (pAttacker && pAttacker->IsAlive() && me->IsValidAttackTarget(pAttacker)) + { + pTarget = pAttacker; + me->SetSelection(pTarget->GetGUID()); + me->Attack(pTarget, true); + } + } + // ================================================================ + + if (m_CruxMovement.HasCruxMovement()) + { + m_CruxMovement.UpdateCruxMovement(m_Movement); + } + else if (pTarget && pTarget->IsAlive() && !IsInvincible(pTarget)) + { + float distance = me->GetDistance(pTarget->GetPosition()); + if (distance < BOTAI_SEARCH_RANGE) + { + if (IsHealerBotAI() && me->getLevel() >= 10) + ProcessHealth(); + else + ProcessCombat(pTarget); + } + else if (distance > BOTAI_SEARCH_RANGE * 2.5f || me->GetMap() != pTarget->GetMap()) + { + me->SetSelection(ObjectGuid::Empty); + } + else + { + m_Movement->MovementToTarget(); + } + } + else if (pTarget = GetCombatTarget()) + { + me->AttackStop(); + me->SetSelection(pTarget->GetGUID()); + } + else + { + me->SetSelection(ObjectGuid::Empty); + ProcessIDLE(); + } + } + else + { + m_CastRecords.ClearRecordSpell(); + m_WishStore.ClearStores(); + m_CruxMovement.ClearMovement(); + me->SetSelection(ObjectGuid::Empty); + m_Revive.UpdateRevive(BOTAI_UPDATE_TICK, m_Teleporting); + } } void BotFieldAI::ResetBotAI() @@ -297,7 +664,7 @@ bool BotFieldAI::IsNotSelect(Unit* pTarget) { if (!pTarget || !pTarget->IsAlive()) return true; - if (pTarget->HasAura(27827)) // (27827 ֮ ) + if (pTarget->HasAura(27827)) // (27827 ֮ ) return true; return false; } @@ -1461,11 +1828,11 @@ bool BotFieldAI::TargetIsStealth(Player* pTarget) { if (!pTarget) return false; - // (1784 DZ || 5215 ³DZ || 66 ʦ || 58984 ҹ) + // (1784 DZ || 5215 ³ DZ || 66 ʦ || 58984 ҹ ) if (pTarget->HasAura(1784) || pTarget->HasAura(5215) || pTarget->HasAura(66) || pTarget->HasAura(58984)) { - if (!me->CanSeeOrDetect(pTarget, false, true)) // DZ + if (!me->CanSeeOrDetect(pTarget, false, true)) // DZ return true; } return false; diff --git a/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.h b/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.h index aa0114b932..4fc80cf2e1 100644 --- a/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.h +++ b/src/server/game/AI/PlayerAI/BotFieldAI/BotFieldAI.h @@ -124,6 +124,7 @@ class TC_GAME_API BotFieldAI : public PlayerAI protected: int32 m_UpdateTick; + uint32 m_AutoLevelTimer; // <-- 新增:自动升级定时器 bool m_DrivingPVP; uint32 m_UseMountID; ObjectGuid m_WarfareTargetID; diff --git a/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp b/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp index 12b552d32f..ca8638ffee 100644 --- a/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp +++ b/src/server/game/AI/PlayerAI/BotGroupAI/BotGroupAI.cpp @@ -1,4 +1,4 @@ -/* +/* * This file is part of the DestinyCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it @@ -250,29 +250,84 @@ bool BotGroupAI::CanReciveCommand(std::string& cmd, std::string& param) void BotGroupAI::ProcessSummonCommand() { - if (!m_MasterPlayer) - return; - m_ForceFlee = false; - m_StopFollow = false; - m_SeduceTarget = ObjectGuid::Empty; - me->SetSelection(ObjectGuid::Empty); - if (m_Teleporting.CanMovement() && !m_MasterPlayer->IsFlying()) - m_Teleporting.SetTeleport(m_MasterPlayer, 0); + if (!m_MasterPlayer) + return; + + // ================== 核心功能 1:脱战自动复活 ================== + // 要求:你(m_MasterPlayer)不在战斗中,且机器人(me)是死亡状态 + // 注意:如果是团灭,你释放灵魂站起来脱战后,按一下召唤,机器人就会集体复活 + if (!m_MasterPlayer->IsInCombat() && !me->IsAlive()) + { + // 满血满蓝复活 (1.0f 代表 100% 状态) + me->ResurrectPlayer(1.0f, false); + // 生成一具白骨,防止原地留下尸体模型引发视觉 Bug + me->SpawnCorpseBones(); + // 强制保存状态,防止回档 + me->SaveToDB(); + } + // ============================================================== + + // ================== 核心功能 2:把召唤变成“终极强刷按钮” ================== + // 删掉专精为0的判断!只要你主动点击召唤它,不管它处于什么状态,无条件给它洗髓伐骨! + if (me->getLevel() >= 10) + { + if (PlayerBotSession* pBotSession = dynamic_cast(me->GetSession())) + { + uint32 curTType = me->FindTalentType(); + uint32 newTType = curTType; + while (newTType == curTType) newTType = urand(0, 2); + + BotGlobleSchedule schedule2(BotGlobleScheduleType::BGSType_Settting, 0); + schedule2.parameter1 = me->getLevel(); + schedule2.parameter2 = me->getLevel(); + schedule2.parameter3 = newTType + 1; + + // 强行推入重置日程 + pBotSession->PushScheduleToQueue(schedule2); + } + } + // ==================================================================== + + // 原有的传送逻辑 + m_ForceFlee = false; + m_StopFollow = false; + m_SeduceTarget = ObjectGuid::Empty; + me->SetSelection(ObjectGuid::Empty); + + // 因为前面已经执行了复活,这里的 CanMovement() 判定就能顺利通过并执行传送了 + if (m_Teleporting.CanMovement() && !m_MasterPlayer->IsFlying()) + m_Teleporting.SetTeleport(m_MasterPlayer, 0); } + + void BotGroupAI::ProcessAttackCommand() { - Unit* pMasterTarget = m_MasterPlayer->GetSelectedUnit(); - if (!pMasterTarget || !pMasterTarget->IsAlive()) - return; - if (!me->IsValidAttackTarget(pMasterTarget)) - return; - me->SetSelection(pMasterTarget->GetGUID()); - m_ForceFlee = false; - m_StopFollow = false; - m_SeduceTarget = ObjectGuid::Empty; -} + Unit* pMasterTarget = m_MasterPlayer->GetSelectedUnit(); + if (!pMasterTarget || !pMasterTarget->IsAlive()) + return; + if (!me->IsValidAttackTarget(pMasterTarget)) + return; + + me->SetSelection(pMasterTarget->GetGUID()); // 原有代码:选中目标 + // ================== 新增代码开始 ================== + // 1. 强制进入近战/施法攻击状态 + me->Attack(pMasterTarget, true); + + // 2. 清除当前的移动状态(打断跟随发呆) + if (me->GetMotionMaster()) + { + me->GetMotionMaster()->Clear(); + // 3. 启动底层的追击寻路系统,让机器人跑向目标 + me->GetMotionMaster()->MoveChase(pMasterTarget); + } + // ================== 新增代码结束 ================== + + m_ForceFlee = false; + m_StopFollow = false; + m_SeduceTarget = ObjectGuid::Empty; +} void BotGroupAI::ProcessFleeCommand() { me->SetSelection(ObjectGuid::Empty); @@ -282,11 +337,38 @@ void BotGroupAI::ProcessFleeCommand() void BotGroupAI::ProcessStopCommand() { - me->SetSelection(ObjectGuid::Empty); - m_SeduceTarget = ObjectGuid::Empty; - m_StopFollow = true; + // 1. 状态加锁:锁住大脑,不准再思考跟随和逃跑 + m_StopFollow = true; + m_ForceFlee = false; + + // 2. 战斗打断:这一步能完美打断 Attack 的 MoveChase + me->AttackStop(); + me->InterruptNonMeleeSpells(false); + me->SetSelection(ObjectGuid::Empty); + + // ================== 核心黑科技:击碎跟随与逃跑引擎 ================== + if (me->GetMotionMaster()) + { + // 先常规清理一下 + me->GetMotionMaster()->Clear(); + + // 【终极必杀技】:强行派发一个“走到自己当前脚下”的任务! + // 这会瞬间把底层的 MoveFollow 和 MoveFlee 引擎直接覆盖挤掉! + me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + } + + // 强制关闭位移状态 + me->StopMoving(); + // ===================================================================== + + // 3. 语音反馈 + //me->Say("原地待命!", LANG_UNIVERSAL); } + + + + void BotGroupAI::ProcessSetting() { if (!m_MasterPlayer || !me->IsInWorld() || !me->IsAlive() || me->IsInCombat() || !m_Teleporting.CanMovement() || m_UseFood.HasFoodState()) @@ -300,16 +382,31 @@ void BotGroupAI::ProcessSetting() return; if (pSession->HasSchedules()) return; + BotGlobleSchedule schedule2(BotGlobleScheduleType::BGSType_Settting, 0); - schedule2.parameter1 = m_MasterPlayer->getLevel(); - schedule2.parameter2 = m_MasterPlayer->getLevel(); + + // ================== 修改代码开始 ================== + // 根据配置文件决定重置时使用的等级 + if (sConfigMgr->GetBoolDefault("PlayerBot.SyncLevelWithGroup", false)) + { + // 如果开启了同步,使用队长的等级 + schedule2.parameter1 = m_MasterPlayer->getLevel(); + schedule2.parameter2 = m_MasterPlayer->getLevel(); + } + else + { + // 如果关闭了同步,使用机器人自己的等级(保留真实等级) + schedule2.parameter1 = me->getLevel(); + schedule2.parameter2 = me->getLevel(); + } + // ================== 修改代码结束 ================== + schedule2.parameter3 = PlayerBotSetting::FindPlayerTalentType(me) + 1; //schedule2.parameter4 = 1; pSession->PushScheduleToQueue(schedule2); me->ResetTalents(true); m_HasReset = false; } - void BotGroupAI::ProcessListEquip(Player* srcPlayer) { std::lock_guard lock(m_ItemLock); @@ -393,7 +490,7 @@ void BotGroupAI::ProcessUpequip(Player* srcPlayer, std::string equipLink) if (msg != EQUIP_ERR_OK) { std::string outString; - consoleToUtf8(std::string("ʧװ"), outString); + consoleToUtf8(std::string("ʧ װ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } @@ -407,7 +504,7 @@ void BotGroupAI::ProcessUpequip(Player* srcPlayer, std::string equipLink) me->GetSession()->HandleAutoEquipItemOpcode(packet); } std::string outString; - consoleToUtf8(std::string("ɹװ"), outString); + consoleToUtf8(std::string(" ɹ װ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } @@ -446,19 +543,29 @@ void BotGroupAI::ProcessUnequip(Player* srcPlayer, std::string& equipLink) if (msg != EQUIP_ERR_OK) { std::string outString; - consoleToUtf8(std::string("ȡʧ"), outString); + consoleToUtf8(std::string("ȡ ʧ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } me->RemoveItem(255, slot, true); me->StoreItem(dest, pItem, true); std::string outString; - consoleToUtf8(std::string("ɹȡ"), outString); + consoleToUtf8(std::string(" ɹ ȡ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } - std::string outString; - consoleToUtf8(std::string("ȡʧ"), outString); + // ================== 多语言兼容性处理 ================== + // 获取发送命令的玩家的客户端语言 + LocaleConstant loc = srcPlayer->GetSession()->GetSessionDbLocaleIndex(); + + std::string outString = "Equip successful!"; // 官方仓库标准:默认英文 + + // 如果识别到是简体中文 (zhCN) 或繁体中文 (zhTW) 客户端 + if (loc == LOCALE_zhCN || loc == LOCALE_zhTW) + { + outString = "装备成功!"; + } + me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } @@ -477,7 +584,7 @@ void BotGroupAI::ProcessDestroyItem(Player* srcPlayer, std::string& equipLink) return; BotUtility::FindItemFromAllBag(me, entry, true); std::string outString; - consoleToUtf8(std::string("ɹ"), outString); + consoleToUtf8(std::string(" ɹ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } @@ -495,7 +602,7 @@ void BotGroupAI::ProcessTradeItem(Player* srcPlayer, std::string& equipLink) if (entry == 0) { std::string outString; - consoleToUtf8(std::string("û"), outString); + consoleToUtf8(std::string(" û "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } @@ -503,7 +610,7 @@ void BotGroupAI::ProcessTradeItem(Player* srcPlayer, std::string& equipLink) if (!pItem) { std::string outString; - consoleToUtf8(std::string("û"), outString); + consoleToUtf8(std::string(" û "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } @@ -511,7 +618,7 @@ void BotGroupAI::ProcessTradeItem(Player* srcPlayer, std::string& equipLink) if (!pTrade) { std::string outString; - consoleToUtf8(std::string("ûпʼ"), outString); + consoleToUtf8(std::string("û п ʼ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } @@ -519,7 +626,7 @@ void BotGroupAI::ProcessTradeItem(Player* srcPlayer, std::string& equipLink) if (!pTradePlayer) { std::string outString; - consoleToUtf8(std::string("ûпʼ"), outString); + consoleToUtf8(std::string("û п ʼ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } @@ -532,27 +639,27 @@ void BotGroupAI::ProcessTradeItem(Player* srcPlayer, std::string& equipLink) if (pTrade->HasItem(pItem->GetGUID())) { std::string outString; - consoleToUtf8(std::string("Ѿȥ"), outString); + consoleToUtf8(std::string(" Ѿ ȥ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } //if (!pItem->CanBeTraded(false, true)) //{ // std::string outString; - // consoleToUtf8(std::string("޷"), outString); + // consoleToUtf8(std::string(" ޷ "), outString); // me->Whisper(outString, Language::LANG_COMMON, srcPlayer); // return; //} if (pTrade->SetItemAtNullSlot(pItem, true)) { std::string outString; - consoleToUtf8(std::string("ȥ"), outString); + consoleToUtf8(std::string(" ȥ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } else { std::string outString; - consoleToUtf8(std::string("ûٷŶ"), outString); + consoleToUtf8(std::string("û ٷŶ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } } @@ -578,12 +685,12 @@ void BotGroupAI::ProcessUseItem(Player* srcPlayer, std::string& equipLink) if (!me->CastItemUseSpell(pItem, targets, ObjectGuid::Empty, 0)) { std::string outString; - consoleToUtf8(std::string("ʧʹ"), outString); + consoleToUtf8(std::string("ʧ ʹ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } std::string outString; - consoleToUtf8(std::string("ɹʹ"), outString); + consoleToUtf8(std::string(" ɹ ʹ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } @@ -601,8 +708,18 @@ void BotGroupAI::ProcessTalent(Player* srcPlayer, std::string& talentText) OnLevelUp(talentType); m_HasReset = false; - std::string outString; - consoleToUtf8(std::string("л츳"), outString); + // ================== 多语言兼容性处理 ================== + // 获取发送命令的玩家的客户端语言 + LocaleConstant loc = srcPlayer->GetSession()->GetSessionDbLocaleIndex(); + + std::string outString = "Talent switch successful!"; // 官方仓库标准:默认英文 + + // 如果识别到是简体中文 (zhCN) 或繁体中文 (zhTW) 客户端 + if (loc == LOCALE_zhCN || loc == LOCALE_zhTW) + { + outString = "天赋切换成功!"; + } + me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } @@ -617,7 +734,7 @@ void BotGroupAI::ProcessSummonRiteSpell(Player* srcPlayer) if (!m_MovetoUseGO.CanCastSummonRite()) { std::string outString; - consoleToUtf8(std::string("Ŀǰ޷ʼٻʽ"), outString); + consoleToUtf8(std::string("Ŀǰ ޷ ʼ ٻ ʽ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); return; } @@ -626,13 +743,13 @@ void BotGroupAI::ProcessSummonRiteSpell(Player* srcPlayer) { m_MovetoUseGO.StartSummonRite(castSpellID); std::string outString; - consoleToUtf8(std::string("ٻʽ"), outString); + consoleToUtf8(std::string(" ٻ ʽ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } else { std::string outString; - consoleToUtf8(std::string("Ŀǰ޷ʼٻʽ"), outString); + consoleToUtf8(std::string("Ŀǰ ޷ ʼ ٻ ʽ "), outString); me->Whisper(outString, Language::LANG_COMMON, srcPlayer); } } @@ -653,7 +770,27 @@ void BotGroupAI::ProcessBotCommand(Player* srcPlayer, std::string cmd) me->SetSelection(ObjectGuid::Empty); m_ForceFlee = false; m_StopFollow = false; + + // ================== 核心升级:智能环形阵型 ================== + if (srcPlayer && me->GetMotionMaster()) + { + me->GetMotionMaster()->Clear(false); + + // 1. 生成独一无二的跟随角度: + // 将 360 度 (2*PI) 分成 6 个方位。利用机器人的 GUID 取模,给每个人分配一个固定方位。 + float followAngle = (me->GetGUID().GetCounter() % 6) * (M_PI / 3.0f); + + // 2. 生成错落有致的跟随距离: + // 不要全都挤在 2 码,让它们在 2码 到 4码 之间错开 + float followDist = 2.0f + (me->GetGUID().GetCounter() % 3) * 1.0f; + + // 3. 执行个性化跟随 + me->GetMotionMaster()->MoveFollow(srcPlayer, followDist, followAngle); + } + // ============================================================ + } + else if (cmd == "flee") ProcessFleeCommand(); else if (cmd == "stop") @@ -816,7 +953,209 @@ void BotGroupAI::DamageEndure(Unit* attacker, uint32& damage, DamageEffectType d void BotGroupAI::UpdateAI(uint32 diff) { - m_UpdateTick -= diff; + + + // ========================================================== + // 【AI 导演系统:群演智能休眠与唤醒 (最终生产净化版)】 + // ========================================================== + m_directorCheckTimer += diff; + + // 【核心修复 1】:重新声明雷达监控变量 + float nearestDist = 9999.0f; + Player* nearestP = nullptr; + + // 每 3 秒执行一次雷达扫描 + if (m_directorCheckTimer >= 3000) + { + // 【核心修复 2】:补回被漏掉的真人玩家扫描循环 + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player* p = itr->GetSource(); + if (p && !p->IsPlayerBot() && p->IsAlive()) + { + float d = p->GetDistance(me); + if (d < nearestDist) + { + nearestDist = d; + nearestP = p; + } + } + } + } + + // 状态机逻辑判定 + if (me->GetGroup()) + { + // ================= 主演区 ================= + // 特权:有队伍的机器人,强制解除休眠并站起! + if (m_directorCheckTimer >= 3000) + { + if (m_isDirectorSleeping) + { + m_isDirectorSleeping = false; + me->SetStandState(UNIT_STAND_STATE_STAND); + } + } + // 注意:这里没有 return,主演必须往下执行原生打本和跟随逻辑! + } + else + { + // ================= 群演区 ================= + if (m_directorCheckTimer >= 3000) + { + m_directorCheckTimer = 0; // 定时器在这里清零 + + // 【深度优化 1:主城超大视野,不轻易休眠】 + // 只要身上有“休息状态”(在主城或旅店),就判定在城里 + bool isInCity = me->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); + // 城里 300 码内不休眠(基本覆盖半个主城),野外保持 65 码 + float sleepRadius = isInCity ? 300.0f : 65.0f; + float wakeRadius = isInCity ? 150.0f : 50.0f; + + if (m_isDirectorSleeping) + { + if (nearestP && nearestDist < wakeRadius) + { + m_isDirectorSleeping = false; + me->SetStandState(UNIT_STAND_STATE_STAND); + me->Dismount(); + me->SetWalk(true); + me->GetMotionMaster()->Clear(); + TC_LOG_ERROR("server", ">>> [动作] [%s] 玩家靠近,起立激活!", me->GetName().c_str()); + } + } + else + { + if (!nearestP || nearestDist >= sleepRadius) + { + m_isDirectorSleeping = true; + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->SetStandState(UNIT_STAND_STATE_SIT); + TC_LOG_ERROR("server", ">>> [动作] [%s] 玩家离开区域,休眠!", me->GetName().c_str()); + } + // 【深度优化 2:活着就要动!持续寻路逻辑】 + // 只要醒着,且当前没有在移动,就立刻给自己找点事做,而不是永远发呆! + else if (!me->HasUnitState(UNIT_STATE_MOVING)) + { + me->SetWalk(true); + + // 70% 几率找功能 NPC,30% 几率随便溜达 + if (urand(1, 100) <= 70) + { + std::vector validNpcs; + std::list creatureList; + // 搜索半径拉大到 80 码!足够覆盖大半个力量谷 + me->GetCreatureListWithEntryInGrid(creatureList, 0, 80.0f); + + for (auto c : creatureList) + { + if (c && c->IsAlive() && c->IsFriendlyTo(me) && !c->IsPet()) + { + // 重点寻找:拍卖师、银行职员、商人 + if (c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER | UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_VENDOR)) + { + validNpcs.push_back(c); + } + } + } + + if (!validNpcs.empty()) + { + // 【深度优化 3:随机挑选目标防扎堆】 + // 把它发现的所有 NPC 放进池子里随机挑一个,大家就不会全去同一个地方了! + Creature* poiNpc = validNpcs[urand(0, validNpcs.size() - 1)]; + + // 【深度优化 4:社交距离防堆叠】 + // 不再死板地站在 NPC 正前方。在 NPC 身边 1.5 到 4 码的 360 度范围内随机站位! + float offsetAngle = frand(0.0f, 6.28f); + float offsetDist = frand(1.5f, 4.0f); + + float nx = poiNpc->GetPositionX() + offsetDist * std::cos(offsetAngle); + float ny = poiNpc->GetPositionY() + offsetDist * std::sin(offsetAngle); + + me->GetMotionMaster()->MovePoint(1, nx, ny, poiNpc->GetPositionZ()); + me->SetFacingToObject(poiNpc); + } + else + { + // 如果 80 码内连个商人都没找到,就在 60 码内大范围溜达 + me->GetMotionMaster()->MoveRandom(60.0f); + } + } + else + { + // 30% 几率闲逛 + me->GetMotionMaster()->MoveRandom(80.0f); + } + } + } + } // 结束 3000ms 定时器 + + // 【核弹级洛巴托米手术】: + // 只要你是野生的群演,每帧直接物理切断!绝对不允许继续往下执行任何原生大脑逻辑! + return; + } + + + + + // 强行驻留锁 (兼容 Stop 命令) + if (m_isCommandStopped) + { + me->StopMoving(); + return; + } + // ========================================================== + + + + + + + + // ================== 核心修复:执行 Stop 原地待命 ================== + if (m_StopFollow) + { + // 哪怕在待命,如果怪物贴脸打它,它必须能原地还手! + if (me->IsInCombat() && me->GetVictim()) + { + // 保持原地,但可以执行基础的攻击动作 (不要 return) + } + else + { + // 如果没进战斗,直接拦截所有动作! + // 坚决不准去寻找远处的怪,坚决不准去跟随队长! + return; + } + } + // ================================================================= + + + + // ================== 核心 AI 升级:强制战斗状态同步(暴力护主) ================== + // 只要队长在战斗中,不管机器人自己在干嘛,强行把它拉进战斗并拔刀! + if (m_MasterPlayer && m_MasterPlayer->IsInCombat() && !me->IsInCombat()) + { + Unit* masterVictim = m_MasterPlayer->GetVictim(); + if (!masterVictim) // 如果队长没有目标,检查谁在打队长 + masterVictim = m_MasterPlayer->getAttackerForHelper(); + + if (masterVictim && masterVictim->IsAlive() && me->IsValidAttackTarget(masterVictim)) + { + me->SetInCombatWith(masterVictim); + me->Attack(masterVictim, true); + if (me->GetMotionMaster()) + me->GetMotionMaster()->MoveChase(masterVictim); + } + } + // ============================================================================== + + + + + m_UpdateTick -= diff; if (m_UpdateTick > 0) return; m_UpdateTick = BOTAI_UPDATE_TICK; @@ -867,6 +1206,20 @@ void BotGroupAI::UpdateAI(uint32 diff) if (me->IsAlive()) { + + // ================== 核心 AI 升级:全系能量无尽模式 ================== + // 解决 7.3.5 机器人因为缺少星界能量、漩涡值或蓝量导致的“施法动作被打断”问题 + for (uint8 i = 0; i < MAX_POWERS; ++i) + { + if (me->GetMaxPower((Powers)i) > 0 && me->GetPower((Powers)i) < me->GetMaxPower((Powers)i)) + { + me->SetPower((Powers)i, me->GetMaxPower((Powers)i)); + } + } + // ======================================================================= + + + if (bothp==0) { int32 isok = sConfigMgr->GetIntDefault("pbot_hp", 1); @@ -935,6 +1288,29 @@ me->SetPower(POWER_MANA, (me->GetMaxPower(POWER_MANA))); if (me->IsInCombat()) UpEnergy(); Unit* pTarget = GetBotAIValidSelectedUnit(); + + // ================== 核心 AI 升级:防卫本能与自动还击 ================== + // 如果当前没有合法目标,但自身处于战斗状态,立刻反查是谁在打我 + if (!pTarget && me->IsInCombat()) + { + Unit* pAttacker = me->getAttackerForHelper(); + if (pAttacker && pAttacker->IsAlive() && me->IsValidAttackTarget(pAttacker)) + { + pTarget = pAttacker; + me->SetSelection(pTarget->GetGUID()); + me->Attack(pTarget, true); + + // 唤醒寻路系统,如果是近战则主动冲向攻击者 + if (me->GetMotionMaster()) + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(pTarget); + } + } + } + // ======================================================================= + + if (m_ForceFlee) { me->AttackStop(); @@ -1093,7 +1469,7 @@ bool BotGroupAI::IsNotSelect(Unit* pTarget) { if (!pTarget || !pTarget->IsAlive()) return true; - if (pTarget->HasAura(27827)) // (27827 ֮ ) + if (pTarget->HasAura(27827)) // (27827 ֮ ) return true; if (pTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) return true; @@ -1316,6 +1692,12 @@ bool BotGroupAI::UpdateMasterPlayer() bool BotGroupAI::TrySettingToMaster() { + // ================== 新增代码开始 ================== + // 读取配置:如果设置为 0 (false),则直接关闭队伍自动等级同步 + if (!sConfigMgr->GetBoolDefault("PlayerBot.SyncLevelWithGroup", false)) + return false; + // ================== 新增代码结束 ================== + static int32 gapLV = 2; if (!BotUtility::BotCanSettingToMaster) return false; @@ -1401,6 +1783,22 @@ bool BotGroupAI::TryTeleportToMaster() Unit* BotGroupAI::GetCombatTarget(float range) { + + + // ================== 核心 AI 升级:暴力护主判定 ================== + // 只要队长(你)在战斗中,无视一切网格扫描,直接锁定你的目标! + if (m_MasterPlayer && m_MasterPlayer->IsInCombat()) + { + Unit* masterVictim = m_MasterPlayer->GetVictim(); + if (masterVictim && masterVictim->IsAlive() && me->IsValidAttackTarget(masterVictim)) + { + return masterVictim; + } + } + // ================================================================ + + + Group* pGroup = me->GetGroup(); if (!pGroup) return NULL; @@ -1447,7 +1845,24 @@ Unit* BotGroupAI::GetCombatTarget(float range) validTarget.push_back(pCreature); } if (validTarget.empty()) - return NULL; + + { + // === 新增修复代码开始 === + // 找不到正在打队伍的怪时,如果队长正在战斗中,直接去打队长的目标 + if (m_MasterPlayer && m_MasterPlayer->IsInCombat()) + { + Unit* masterVictim = m_MasterPlayer->GetVictim(); + if (masterVictim && masterVictim->IsAlive() && me->IsValidAttackTarget(masterVictim)) + { + return masterVictim; + } + } + // === 新增修复代码结束 === + return NULL; + } + + + return validTarget[urand(0, validTarget.size() - 1)]; } @@ -1468,20 +1883,35 @@ void BotGroupAI::ProcessFollowToMaster() Position targetPos = BotUtility::GetPositionFromGroup(m_MasterPlayer, me->GetGUID(), me->GetGroup()); m_Movement->MovementTo(targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), 0); float distance = me->GetDistance(m_MasterPlayer->GetPosition()); - if (distance <= NEEDFLEE_CHECKRANGE && distance > 0.1f) - { - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFollow(m_MasterPlayer, 1.0f, m_MasterPlayer->GetOrientation()); - return; - } - if (me->IsWithinLOSInMap(m_MasterPlayer)) - { - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFollow(m_MasterPlayer, 1.0f, m_MasterPlayer->GetOrientation()); - } - else - m_Movement->MovementTo(m_MasterPlayer->GetPositionX(), m_MasterPlayer->GetPositionY(), m_MasterPlayer->GetPositionZ(), 1); + + // ================== 核心升级:动态环形阵型计算 ================== + // 1. 利用机器人的唯一 ID 取模,将 360 度 (2*PI) 分成 6 个不同方位 + float followAngle = (me->GetGUID().GetCounter() % 6) * (M_PI / 3.0f); + + // 2. 距离也错开,不要全挤在 1 码,分配到 2码 到 4码 之间 + float followDist = 2.0f + (me->GetGUID().GetCounter() % 3) * 1.0f; + // ================================================================ + + if (distance <= NEEDFLEE_CHECKRANGE && distance > 0.1f) + { + me->GetMotionMaster()->Clear(); + // 替换参数:使用动态距离和角度 + me->GetMotionMaster()->MoveFollow(m_MasterPlayer, followDist, followAngle); + return; + } + + if (me->IsWithinLOSInMap(m_MasterPlayer)) + { + me->GetMotionMaster()->Clear(); + // 替换参数:使用动态距离和角度 + me->GetMotionMaster()->MoveFollow(m_MasterPlayer, followDist, followAngle); + } + else + { + // 如果卡了视角(没在视野内),跑向队长 + m_Movement->MovementTo(m_MasterPlayer->GetPositionX(), m_MasterPlayer->GetPositionY(), m_MasterPlayer->GetPositionZ(), 1); + } } bool BotGroupAI::NonCombatProcess() @@ -1508,6 +1938,125 @@ bool BotGroupAI::NonCombatProcess() return true; m_WishStore.UpdateWishStore(); BotUtility::TryTeleportPlayerPet(me); + + // ================== 核心 AI 升级:7.3.5 全团一键秒刷 Buff 矩阵 (终极防抽风版) ================== + if (me->IsAlive() && !me->IsMounted() && !me->IsInCombat() && !me->HasUnitState(UNIT_STATE_CASTING)) + { + bool hasCasted = false; + + // --- 第一优先级:只给自己上的单体护盾 --- + switch (me->getClass()) + { + case CLASS_MAGE: + if (!me->HasAura(36881)) { + if (!me->HasSpell(36881)) me->LearnSpell(36881, false); + me->CastSpell(me, 36881, true); + hasCasted = true; + } + break; + case CLASS_SHAMAN: + if (!me->HasAura(192106)) { + if (!me->HasSpell(192106)) me->LearnSpell(192106, false); + me->CastSpell(me, 192106, true); + hasCasted = true; + } + break; + } + + // --- 第二优先级:给全团在一瞬间刷齐群体 Buff --- + uint32 checkSpellId = 0; + switch (me->getClass()) + { + case CLASS_PRIEST: checkSpellId = 23948; break; // 真言术:韧 + case CLASS_MAGE: checkSpellId = 36880; break; // 奥术智慧 + case CLASS_PALADIN:checkSpellId = 56525; break; // 王者祝福 + } + + if (checkSpellId > 0) + { + if (!me->HasSpell(checkSpellId)) me->LearnSpell(checkSpellId, false); + + Group* pGroup = me->GetGroup(); + if (pGroup) + { + Group::MemberSlotList const& memList = pGroup->GetMemberSlots(); + for (Group::MemberSlot const& slot : memList) + { + Player* member = ObjectAccessor::FindPlayer(slot.guid); + // 目标必须存活、在视野内、且距离不超过 30 码 + if (!member || !member->IsAlive() || !me->IsWithinLOSInMap(member) || me->GetDistance(member) > 30.0f) + continue; + + // 只要队友身上没这个 Buff,立刻刷! + if (!member->HasAura(checkSpellId)) + { + me->SetFacingToObject(member); + + // 1. 面子工程:法师对目标播放施法动作 + me->CastSpell(member, checkSpellId, true); + + // 2. 【终极破死循环黑科技】: + // 因为 36880 等老法术在 7.3.5 底层不能给别人加, + // 我们强制让队友在后台“自己对自己”瞬发一次!100% 成功挂上真实 Buff! + if (me->GetGUID() != member->GetGUID()) + { + member->CastSpell(member, checkSpellId, true); + } + + hasCasted = true; // 标记已施法,但不跳出循环,继续瞬间给下一个人刷! + } + } + } + else // 没有队伍时,给自己刷 + { + if (!me->HasAura(checkSpellId)) + { + me->CastSpell(me, checkSpellId, true); + hasCasted = true; + } + } + } + + // --- 第三优先级:一瞬间给全团伤员挂满恢复 (小德回春 / 牧师补盾) --- + if (!hasCasted) // 只有主属性 Buff 都刷齐了,才去补血,防止卡动作 + { + Group* pGroup = me->GetGroup(); + if (pGroup) + { + Group::MemberSlotList const& memList = pGroup->GetMemberSlots(); + for (Group::MemberSlot const& slot : memList) + { + Player* member = ObjectAccessor::FindPlayer(slot.guid); + if (!member || !member->IsAlive() || me->GetDistance(member) > 30.0f) continue; + + if (member->GetHealthPct() < 100.0f) + { + if (me->getClass() == CLASS_DRUID && !member->HasAura(8936)) + { + if (!me->HasSpell(8936)) me->LearnSpell(8936, false); + me->CastSpell(member, 8936, true); + hasCasted = true; + } + if (me->getClass() == CLASS_PRIEST && !member->HasAura(17) && !member->HasAura(6788)) + { + if (!me->HasSpell(17)) me->LearnSpell(17, false); + me->CastSpell(member, 17, true); + hasCasted = true; + } + } + } + } + } + + // --- 统一结算:如果这回合刷了任何 Buff,就停顿一下 --- + if (hasCasted) + { + m_Movement->ClearMovement(); + return true; + } + } + // ======================================================================= + } return false; } @@ -3036,11 +3585,11 @@ bool BotGroupAI::TargetIsStealth(Player* pTarget) { if (!pTarget) return false; - // (1784 DZ || 5215 ³DZ || 66 ʦ || 58984 ҹ) + // (1784 DZ || 5215 ³ DZ || 66 ʦ || 58984 ҹ ) if (pTarget->HasAura(1784) || pTarget->HasAura(5215) || pTarget->HasAura(66) || pTarget->HasAura(58984)) { - if (!me->CanSeeOrDetect(pTarget, false, true)) // DZ + if (!me->CanSeeOrDetect(pTarget, false, true)) // DZ return true; } return false; diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h index f4a1f4efa0..da4ae62e39 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.h +++ b/src/server/game/AI/PlayerAI/PlayerAI.h @@ -26,6 +26,23 @@ class Spell; class TC_GAME_API PlayerAI : public UnitAI { public: + + + // ========================================== + // 【AI 导演系统专属控制接口】 + // ========================================== + void SetDirectorSleep(bool sleep) { m_isDirectorSleeping = sleep; } + bool IsDirectorSleeping() const { return m_isDirectorSleeping; } + void SetDirectorDrafted(bool drafted) { m_isDirectorDrafted = drafted; } + // 【修改】加上 map 参数 + void SetDirectorAnchor(uint32 map, float x, float y, float z) { + m_directorAnchorMapId = map; + m_directorAnchorX = x; + m_directorAnchorY = y; + m_directorAnchorZ = z; + } + + explicit PlayerAI(Player* player); Creature* GetCharmer() const; @@ -40,6 +57,32 @@ class TC_GAME_API PlayerAI : public UnitAI bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); } protected: + + // ========================================== + // 【AI 导演系统变量】默认所有机器人出生即休眠! + // ========================================== + bool m_isDirectorSleeping = true; + uint32 m_directorCheckTimer = 3000; + + + // 【手动停留开关】 + bool m_isCommandStopped = false; + // 【新增】:群演卖身契标记 (默认是 false,代表原生自由人) + bool m_isDirectorDrafted = false; + + // 【新增】:提线木偶漫步系统 + float m_directorAnchorX = 0.0f; + float m_directorAnchorY = 0.0f; + float m_directorAnchorZ = 0.0f; + uint32 m_directorWanderTimer = 0; // 专属溜达计时器 + uint32 m_directorAnchorMapId = 0; // 【新增】剧组所在地图 + + // 【新增】:高级 RPG 交互系统 + uint32 m_directorInteractTimer = 0; // 驻留交互倒计时 + uint64 m_directorInteractGuid = 0; // 正在交互的目标 GUID + + + struct TargetedSpell : public std::pair { TargetedSpell() : pair() { } diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index bc700216ea..a3e2345b3e 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -3,16 +3,13 @@ # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. CollectSourceFiles( ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE_SOURCES # Exclude - ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) + ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders +) if (USE_COREPCH) set(PRIVATE_PCH_HEADER PrecompiledHeaders/gamePCH.h) @@ -22,14 +19,16 @@ GroupSources(${CMAKE_CURRENT_SOURCE_DIR}) add_definitions(-DTRINITY_API_EXPORT_GAME) +# 强制告诉Eluna这是Trinity架构,防止API识别错乱 +add_definitions(-DELUNA_TRINITY) + CollectIncludeDirectories( ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC_INCLUDES # Exclude - ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) + ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders +) -# Provide an interface target for the game project to allow -# dependent projects to build meanwhile. add_library(game-interface INTERFACE) target_include_directories(game-interface @@ -41,19 +40,33 @@ target_link_libraries(game-interface shared Detour) +# ========================================== +# [关键修复]: 提取 Lua C 源码,并开启免检通道 +# ========================================== +file(GLOB_RECURSE LUA_C_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/LuaEngine/lua_src/*.c") +if(LUA_C_SOURCES) + set_source_files_properties(${LUA_C_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON) +endif() + +# 注意:整个文件里,add_library(game ...) 只能出现这一次! add_library(game ${PRIVATE_SOURCES}) target_include_directories(game PRIVATE - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/dep + ${CMAKE_SOURCE_DIR}/dep/efsw/include # [新增这一行 + ${CMAKE_CURRENT_SOURCE_DIR}/LuaEngine/lua_src # 确保 Eluna 能找到 lua.h +) target_link_libraries(game PRIVATE trinity-core-interface efsw PUBLIC - game-interface) + game-interface +) set_target_properties(game PROPERTIES @@ -72,7 +85,6 @@ if( BUILD_SHARED_LIBS ) endif() endif() -# Generate precompiled header if (USE_COREPCH) add_cxx_pch(game ${PRIVATE_PCH_HEADER}) -endif () +endif () \ No newline at end of file diff --git a/src/server/game/Entities/Player/CollectionMgr.cpp b/src/server/game/Entities/Player/CollectionMgr.cpp index e3c0856db8..d762e9af65 100644 --- a/src/server/game/Entities/Player/CollectionMgr.cpp +++ b/src/server/game/Entities/Player/CollectionMgr.cpp @@ -27,6 +27,7 @@ #include "TransmogrificationPackets.h" #include "WorldSession.h" #include +#include "PlayerBotSession.h" namespace { @@ -520,6 +521,20 @@ void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearan void CollectionMgr::SaveAccountItemAppearances(LoginDatabaseTransaction& trans) { + + // ================= 幻化防死锁拦截器 (完美版) ================= + // 绝不允许机器人将它们随机生成的装备写入战网幻化数据库! + // 既然 _owner 就是 WorldSession 指针,我们直接调用判断即可! + if (_owner && _owner->IsBotSession()) + { + return; + } + // ==================================================== + + + + + uint16 blockIndex = 0; boost::to_block_range(*_appearances, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue) { diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0e1f3d7446..bdd8734f69 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -133,6 +133,7 @@ #include "PlayerBotSession.h" #include "BotAITool.h" + #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS) #define SHOP_UPDATE_INTERVAL (30*IN_MILLISECONDS) @@ -18671,20 +18672,21 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol SetUInt32Value(PLAYER_FLAGS_EX, fields[21].GetUInt32()); SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[54].GetUInt32()); - if (!ValidateAppearance( - fields[3].GetUInt8(), // race - fields[4].GetUInt8(), // class - gender, - GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID), - GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID), - GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID), - GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE), - GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID), - customDisplay)) - { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", guid.ToString().c_str()); - return false; - } + // if (!ValidateAppearance( + // fields[3].GetUInt8(), // race + // fields[4].GetUInt8(), // class + // gender, + // GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID), + // GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID), + // GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID), + // GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE), + // GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID), + // customDisplay)) + // { + //TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", //guid.ToString().c_str()); + //return false; + //======================================================================== + //} // set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise) SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[68].GetUInt8()); @@ -21548,9 +21550,28 @@ void Player::SaveToDB(bool create /*=false*/) _SaveGlyphs(trans); _SaveTalents(trans); _SaveSpells(trans); - GetSpellHistory()->SaveToDB(trans); + + + + // 【减负优化】:如果是机器人,绝不保存法术冷却和历史记录,彻底根除进副本死锁! + if (!IsPlayerBot()) + { + GetSpellHistory()->SaveToDB(trans); + } + + _SaveActions(trans); - _SaveAuras(trans); + + + + + // 【极致减负】:机器人不需要保存身上的 Buff 和 Debuff + if (!IsPlayerBot()) + { + _SaveAuras(trans); + } + + _SaveSkills(trans); m_achievementMgr->SaveToDB(trans); m_reputationMgr->SaveToDB(trans); @@ -28788,6 +28809,12 @@ void Player::_LoadPvpTalents(PreparedQueryResult result) void Player::_SaveTalents(CharacterDatabaseTransaction& trans) { + + // 【减负优化】:如果是机器人,直接跳过天赋保存,根除死锁! + if (IsPlayerBot()) + return; + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TALENT); stmt->setUInt64(0, GetGUID().GetCounter()); trans->Append(stmt); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index e728b011dc..fbdff8e73a 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -65,6 +65,9 @@ #include "BotMovementAI.h" #include +#include "LuaEngine.h" +extern class Eluna* sEluna; + class LoginQueryHolder : public CharacterDatabaseQueryHolder { private: @@ -1184,6 +1187,19 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder) _player->RemoveOnLogAuras(); sScriptMgr->OnPlayerLogin(pCurrChar, firstLogin); + // =================================== + + + if (sEluna) + { + sEluna->OnLogin(pCurrChar); + + } + else + { + printf("[C++ 物理探针] 致命异常:sEluna 指针为 NULL!大脑根本没连上这条神经!\n"); + } + TC_METRIC_EVENT("player_events", "Login", pCurrChar->GetName()); diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index 95ea64b248..40f00dae3d 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -39,6 +39,7 @@ #include "Util.h" #include "World.h" #include "WorldPacket.h" +#include "LuaEngine/LuaEngine.h" void WorldSession::HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage) { @@ -94,16 +95,26 @@ void WorldSession::HandleChatMessageEmoteOpcode(WorldPackets::Chat::ChatMessageE } void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, std::string target /*= ""*/) + { + Player* sender = GetPlayer(); + + + + if (lang == LANG_UNIVERSAL && type != CHAT_MSG_EMOTE) + { + TC_LOG_ERROR("entities.player.cheat", "CMSG_MESSAGECHAT: Possible hacking-attempt: %s tried to send a message in universal language", GetPlayerInfo().c_str()); + SendNotification(LANG_UNKNOWN_LANGUAGE); + return; - } + } // prevent talking at unknown language (cheating) LanguageDesc const* langDesc = GetLanguageDescByID(lang); if (!langDesc) @@ -184,6 +195,25 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, if (msg.empty()) return; + + // ========================================== + // Fix: Dispatch Chat events to Eluna Engine + // ========================================== + if (type == CHAT_MSG_SAY || type == CHAT_MSG_YELL || type == CHAT_MSG_WHISPER || type == CHAT_MSG_EMOTE) + { + if (sEluna) + { + sEluna->OnChat(sender, (uint32)type, (uint32)lang, msg); + + // If Lua script returns false, Eluna clears the message. + if (msg.empty()) + return; + } + } + // ========================================== + + + if (ChatHandler(this).ParseCommands(msg.c_str())) return; @@ -202,6 +232,9 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, return; } + // ========== + + switch (type) { case CHAT_MSG_SAY: @@ -450,6 +483,13 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, WorldPackets::Chat::Chat packet; packet.Initialize(ChatMsg(type), Language(lang), sender, nullptr, msg); group->BroadcastPacket(packet.Write(), false); + + // ================== 新增:打通团队频道机器人指令 ================== + // 团队人多眼杂,建议这里只允许“团长” (RAID_LEADER) 发号施令 + if (type == CHAT_MSG_RAID_LEADER) + group->ProcessGroupBotCommand(GetPlayer(), msg); + // ================================================================= + break; } case CHAT_MSG_RAID_WARNING: @@ -498,6 +538,14 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, WorldPackets::Chat::Chat packet; packet.Initialize(ChatMsg(type), Language(lang), sender, nullptr, msg); group->BroadcastPacket(packet.Write(), false); + + // ================== 新增:打通副本频道机器人指令 ================== + // 这里为了方便,我们允许副本内的任何玩家(不仅仅是队长)都能指挥机器人 + if (type == CHAT_MSG_INSTANCE_CHAT || type == CHAT_MSG_INSTANCE_CHAT_LEADER) + group->ProcessGroupBotCommand(GetPlayer(), msg); + // ================================================================= + + break; } default: diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index a888ca6936..e343288fb7 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -67,7 +67,15 @@ void WorldSession::HandlePartyInviteOpcode(WorldPackets::Party::PartyInviteClien // no player if (!player) { + // ================== 修改代码开始 ================== + // 尝试去机器人池子里唤醒这个假在线的名字 + sPlayerBotMgr->AllPlayerBotRandomLogin(packet.TargetName.c_str()); + SendPartyResult(PARTY_OP_INVITE, packet.TargetName, ERR_BAD_PLAYER_NAME_S); + + // 发送个黄字提示,告诉自己机器人正在上线 + // ChatHandler(GetPlayer()->GetSession()).PSendSysMessage("正在尝试唤醒 [%s],由于在冷启动,请稍等2秒后再邀请一次!", packet.TargetName.c_str()); + // ================== 修改代码结束 ================== return; } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 19bbae9fcb..035818a45d 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -46,7 +46,8 @@ #include "SpellMgr.h" #include "Trainer.h" #include "WorldPacket.h" - +#include "LuaEngine/LuaEngine.h" +#include "SpellHistory.h" void WorldSession::HandleTabardVendorActivateOpcode(WorldPackets::NPC::Hello& packet) { Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_TABARDDESIGNER); @@ -350,6 +351,17 @@ void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) if (!sScriptMgr->OnGossipHello(_player, unit)) { + + // ========================================== + if (unit->GetEntry() == 3061) + { + if (auto history = _player->GetSpellHistory()) + { + history->ResetCooldown(8690, true); + } + } + // ========================================== + _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID()); _player->PrepareGossipMenu(unit, unit->GetCreatureTemplate()->GossipMenuId, true); _player->SendPreparedGossip(unit); @@ -362,53 +374,41 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPackets::NPC::GossipSelec if (!_player->PlayerTalkClass->GetGossipMenu().GetItem(packet.GossipIndex)) return; - // Prevent cheating on C++ scripted menus if (_player->PlayerTalkClass->GetInteractionData().SourceGuid != packet.GossipUnit) return; Creature* unit = nullptr; GameObject* go = nullptr; + if (packet.GossipUnit.IsCreatureOrVehicle()) { unit = GetPlayer()->GetNPCIfCanInteractWith(packet.GossipUnit, UNIT_NPC_FLAG_GOSSIP); if (!unit) - { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with him.", packet.GossipUnit.ToString().c_str()); return; - } } else if (packet.GossipUnit.IsGameObject()) { go = _player->GetGameObjectIfCanInteractWith(packet.GossipUnit); if (!go) - { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", packet.GossipUnit.ToString().c_str()); return; - } } else { - - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - unsupported %s.", packet.GossipUnit.ToString().c_str()); return; } - // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if ((unit && unit->GetScriptId() != unit->LastUsedScriptID) || (go && go->GetScriptId() != go->LastUsedScriptID)) { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - Script reloaded while in use, ignoring and set new scipt id"); - if (unit) - unit->LastUsedScriptID = unit->GetScriptId(); - - if (go) - go->LastUsedScriptID = go->GetScriptId(); + if (unit) unit->LastUsedScriptID = unit->GetScriptId(); + if (go) go->LastUsedScriptID = go->GetScriptId(); _player->PlayerTalkClass->SendCloseGossip(); return; } + // --- Core execution logic --- if (!packet.PromotionCode.empty()) { if (unit) @@ -428,8 +428,21 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPackets::NPC::GossipSelec { if (unit) { + uint32 optSender = _player->PlayerTalkClass->GetGossipOptionSender(packet.GossipIndex); + uint32 optAction = _player->PlayerTalkClass->GetGossipOptionAction(packet.GossipIndex); + unit->AI()->sGossipSelect(_player, packet.GossipID, packet.GossipIndex); - if (!sScriptMgr->OnGossipSelect(_player, unit, _player->PlayerTalkClass->GetGossipOptionSender(packet.GossipIndex), _player->PlayerTalkClass->GetGossipOptionAction(packet.GossipIndex))) + + // ========================================== + // Fix: Dispatch GossipSelect to Eluna Engine + // ========================================== + if (sEluna) + { + sEluna->OnGossipSelect(_player, unit, optSender, optAction); + } + // ========================================== + + if (!sScriptMgr->OnGossipSelect(_player, unit, optSender, optAction)) _player->OnGossipSelect(unit, packet.GossipIndex, packet.GossipID); } else @@ -439,9 +452,7 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPackets::NPC::GossipSelec _player->OnGossipSelect(go, packet.GossipIndex, packet.GossipID); } } -} - -void WorldSession::HandleSpiritHealerActivate(WorldPackets::NPC::SpiritHealerActivate& packet) +}void WorldSession::HandleSpiritHealerActivate(WorldPackets::NPC::SpiritHealerActivate& packet) { Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Healer, UNIT_NPC_FLAG_SPIRITHEALER); if (!unit) diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 08eade5b0b..8f19eb5d2e 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -162,10 +162,20 @@ bool WorldSession::SendLearnNewTaxiNode(Creature* unit) void WorldSession::SendDiscoverNewTaxiNode(uint32 nodeid) { - if (GetPlayer()->m_taxi.SetTaximaskNode(nodeid)) - SendPacket(WorldPackets::Taxi::NewTaxiPath().Write()); -} + Player* player = GetPlayer(); + if (!player) + return; + // 先让玩家/机器人正常记录这个飞行点数据 + if (player->m_taxi.SetTaximaskNode(nodeid)) + { + // 关键防爆盾:只有当它不是机器人时,才发送网络封包给客户端 + if (!player->IsPlayerBot()) + { + SendPacket(WorldPackets::Taxi::NewTaxiPath().Write()); + } + } +} void WorldSession::HandleActivateTaxiOpcode(WorldPackets::Taxi::ActivateTaxi& activateTaxi) { Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(activateTaxi.Vendor, UNIT_NPC_FLAG_FLIGHTMASTER); diff --git a/src/server/game/LuaEngine/.editorconfig b/src/server/game/LuaEngine/.editorconfig new file mode 100644 index 0000000000..a34283ac17 --- /dev/null +++ b/src/server/game/LuaEngine/.editorconfig @@ -0,0 +1,7 @@ +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +tab_width = 4 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/src/server/game/LuaEngine/.github/workflows/build.yml b/src/server/game/LuaEngine/.github/workflows/build.yml new file mode 100644 index 0000000000..03eca24255 --- /dev/null +++ b/src/server/game/LuaEngine/.github/workflows/build.yml @@ -0,0 +1,254 @@ +name: build + +on: + push: + workflow_call: + +jobs: + TC-Eluna: + strategy: + fail-fast: false + matrix: + include: + - eluna: OFF + lua: lua52 + - eluna: ON + lua: lua51 + - eluna: ON + lua: lua52 + - eluna: ON + lua: lua53 + - eluna: ON + lua: lua54 + - eluna: ON + lua: luajit + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: false + repository: ElunaLuaEngine/ElunaTrinityWotlk + - uses: actions/checkout@v4 + with: + path: src/server/game/LuaEngine + - name: Dependencies + run: | + sudo apt-get update && sudo apt-get install -yq libboost-all-dev g++-11 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-11 + - name: Setup + env: + ELUNA: ${{ matrix.eluna }} + LUA: ${{ matrix.lua }} + run: | + mkdir bin + cd bin + cmake ../ -DELUNA=$ELUNA -DLUA_VERSION=$LUA -DWITH_WARNINGS=1 -DWITH_COREDEBUG=0 -DUSE_COREPCH=1 -DUSE_SCRIPTPCH=1 -DTOOLS=1 -DSCRIPTS=dynamic -DSERVERS=1 -DNOJEM=0 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Werror" -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_C_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_CXX_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_INSTALL_PREFIX=check_install -DBUILD_TESTING=1 + cd .. + - name: Build + run: | + cd bin + make -j 4 -k && make install + - name: Unit tests + run: | + cd bin + make test + - name: Check executables + run: | + cd bin/check_install/bin + ./authserver --version + ./worldserver --version + +# Cata preservation project has been abandoned, so no need for further build checks on this project +# CataPres-Eluna: +# strategy: +# fail-fast: false +# matrix: +# eluna: [ON] +# runs-on: ubuntu-20.04 +# steps: +# - uses: actions/checkout@v4 +# with: +# submodules: false +# repository: Niam5/ElunaCataPreservation +# - uses: actions/checkout@v4 +# with: +# path: src/server/game/LuaEngine +# - name: Dependencies +# run: | +# sudo apt-get update && sudo apt-get install -yq libboost-all-dev +# sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-10 +# - name: Setup +# env: +# ELUNA: ${{ matrix.eluna }} +# run: | +# mkdir bin +# cd bin +# cmake ../ -DELUNA=$ELUNA -DUSE_COREPCH=1 -DUSE_SCRIPTPCH=1 -DTOOLS=1 -DSERVERS=1 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_CXX_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_INSTALL_PREFIX=check_install -DBUILD_TESTING=1 +# cd .. +# - name: Build +# run: | +# cd bin +# make -j 4 -k && make install +# - name: Unit tests +# run: | +# cd bin +# make test +# - name: Check executables +# run: | +# cd bin/check_install/bin +# ./bnetserver --version +# ./worldserver --version + +# AC-Eluna: +# strategy: +# fail-fast: false +# runs-on: ubuntu-20.04 +# steps: +# - uses: actions/checkout@v4 +# with: +# submodules: recursive +# repository: azerothcore/azerothcore-wotlk +# ref: 'master' +# - uses: actions/checkout@v4 +# with: +# submodules: false +# repository: azerothcore/mod-eluna-lua-engine +# path: modules/mod-eluna-lua-engine +# - uses: actions/checkout@v4 +# with: +# path: modules/mod-eluna-lua-engine/LuaEngine +# - name: Configure OS +# run: | +# # Copy paste of https://github.com/azerothcore/azerothcore-wotlk/blob/master/apps/ci/ci-install.sh +# +# cat >>conf/config.sh <> ./conf/config.sh +# echo "CCOMPILERCXX=\"clang++-11\"" >> ./conf/config.sh +# - name: Import db +# run: source ./apps/ci/ci-import-db.sh +# - name: Build +# run: source ./apps/ci/ci-compile.sh +# - name: Dry run +# run: source ./apps/ci/ci-worldserver-dry-run.sh +# - name: Check startup errors +# run: source ./apps/ci/ci-error-check.sh + + mangos-Eluna: + strategy: + fail-fast: false + matrix: + eluna: [ON, OFF] + patch: [zero, one, two, three] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + repository: mangos${{ matrix.patch }}/server + ref: master + - uses: actions/checkout@v4 + with: + path: src/game/LuaEngine + - name: Dependencies and Environment + run: | + sudo apt-get update -y + sudo apt-get install -y git make cmake clang libssl-dev libbz2-dev build-essential default-libmysqlclient-dev libace-dev libreadline-dev + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang 100 + - name: Configure + env: + ELUNA: ${{ matrix.eluna }} + run: | + test -d _build || mkdir _build + test -d _install || mkdir _install + cd _build + cmake .. -DCMAKE_INSTALL_PREFIX=../_install -DBUILD_TOOLS:BOOL=1 -DBUILD_MANGOSD:BOOL=1 -DBUILD_REALMD:BOOL=1 -DSOAP:BOOL=1 -DSCRIPT_LIB_ELUNA:BOOL=$ELUNA -DSCRIPT_LIB_SD3:BOOL=1 -DPLAYERBOTS:BOOL=0 -DUSE_STORMLIB:BOOL=1 + - name: Build + run: | + cd _build + make -j 6 + + cmangos-Eluna: + strategy: + fail-fast: false + matrix: + eluna: [ON, OFF] + patch: [Classic, TBC, WotLK, Cata] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: false + repository: Niam5/Eluna-CMaNGOS-${{ matrix.patch }} + ref: master + - uses: actions/checkout@v4 + with: + path: src/game/LuaEngine + - name: Dependencies and Environment + run: | + echo "CC=gcc-12" >> $GITHUB_ENV + echo "CXX=g++-12" >> $GITHUB_ENV + sudo apt-get update && sudo apt-get install -yq libboost-all-dev + - name: Configure + env: + ELUNA: ${{ matrix.eluna }} + run: | + mkdir bin + cd bin + cmake .. -DBUILD_ELUNA=$ELUNA -DCMAKE_INSTALL_PREFIX=install -DBUILD_PLAYERBOT=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/ + cd .. + - name: Build + run: | + cd bin + make -j4 + make install + + vmangos-Eluna: + strategy: + fail-fast: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + repository: Eluna-Ports/Eluna-VMaNGOS + ref: development + - uses: actions/checkout@v4 + with: + path: src/modules/Eluna + + - name: Dependencies + run: | + sudo apt-get -qq update + sudo apt-get -qq install build-essential cmake cppcheck git libace-dev libiberty-dev libmysql++-dev libssl-dev libtbb-dev make openssl libreadline-dev + + - name: Configure and Build + run: | + mkdir build + mkdir _install + cd build + cmake ../ -DCMAKE_INSTALL_PREFIX=../_install -DWITH_WARNINGS=0 -DUSE_EXTRACTORS=1 + make -j2 + make install \ No newline at end of file diff --git a/src/server/game/LuaEngine/.github/workflows/create-pr.sh b/src/server/game/LuaEngine/.github/workflows/create-pr.sh new file mode 100644 index 0000000000..eb8c00324f --- /dev/null +++ b/src/server/game/LuaEngine/.github/workflows/create-pr.sh @@ -0,0 +1,41 @@ +# Adapted from https://github.com/paygoc6/action-pull-request-another-repo + +CLONE_DIR=$(mktemp -d) + +echo "Setting git variables" +export GITHUB_TOKEN=$API_TOKEN_GITHUB +git config --global user.email "$USER_EMAIL" +git config --global user.name "$USER_NAME" + +date=$(date '+%Y-%m-%d_%H-%M') +DESTINATION_HEAD_BRANCH="$DESTINATION_HEAD_BRANCH-$date" + +echo "Cloning destination git repository" +git clone "https://$API_TOKEN_GITHUB@github.com/$DESTINATION_REPO.git" "$CLONE_DIR" +cd "$CLONE_DIR" +git checkout "$DESTINATION_BASE_BRANCH" +git pull origin "$DESTINATION_BASE_BRANCH" +git checkout -b "$DESTINATION_HEAD_BRANCH" + +echo "Copying contents to git repo" +mkdir -p "$CLONE_DIR/$DESTINATION_FOLDER" +cp -r "$SOURCE_FOLDER/." "$CLONE_DIR/$DESTINATION_FOLDER/" + +echo "Adding files" +git add . +echo "Git status:" +git status -- ":!date.js" +if git status -- ":!date.js" | grep -q "Changes to be committed" +then + echo "Adding git commit" + git commit -m "$COMMIT_MESSAGE" + echo "Pushing git commit" + git push -u origin "$DESTINATION_HEAD_BRANCH" + echo "Creating a pull request" + gh pr create -t "$PR_TITLE" \ + -B "$DESTINATION_BASE_BRANCH" \ + -b "" \ + -H "$DESTINATION_HEAD_BRANCH" +else + echo "No changes detected" +fi diff --git a/src/server/game/LuaEngine/.github/workflows/documentation.yml b/src/server/game/LuaEngine/.github/workflows/documentation.yml new file mode 100644 index 0000000000..287258f8bd --- /dev/null +++ b/src/server/game/LuaEngine/.github/workflows/documentation.yml @@ -0,0 +1,38 @@ +name: documentation +on: + push: + branches: + - 'main' + - 'master' +jobs: + Push-Docs-To-Website: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + architecture: 'x64' + - name: Install Python dependencies + run: pip install jinja2 typedecorator markdown + - name: Compile documentation + run: | + cd ${{ github.workspace }}/docs/ + python -m ElunaDoc + - name: Create pull request + run: | + chmod +x "${GITHUB_WORKSPACE}/.github/workflows/create-pr.sh" + "${GITHUB_WORKSPACE}/.github/workflows/create-pr.sh" + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + SOURCE_FOLDER: '${{ github.workspace }}/docs/build' + DESTINATION_REPO: 'elunaluaengine/elunaluaengine.github.io' + DESTINATION_FOLDER: '' + DESTINATION_BASE_BRANCH: 'master' + DESTINATION_HEAD_BRANCH: 'master' + PR_TITLE: 'Update Eluna documentation' + COMMIT_MESSAGE: 'Update Eluna documentation' + USER_EMAIL: 'foe@elunatech.com' + USER_NAME: 'Foereaper' diff --git a/src/server/game/LuaEngine/.github/workflows/pr-on-label.yml b/src/server/game/LuaEngine/.github/workflows/pr-on-label.yml new file mode 100644 index 0000000000..3d53d68894 --- /dev/null +++ b/src/server/game/LuaEngine/.github/workflows/pr-on-label.yml @@ -0,0 +1,12 @@ +name: PR Build + +on: + workflow_dispatch: + pull_request: + types: [ labeled ] + +jobs: + call-build: + if: ${{ github.event.label.name == 'build' }} + name: Build + uses: ./.github/workflows/build.yml \ No newline at end of file diff --git a/src/server/game/LuaEngine/.gitignore b/src/server/game/LuaEngine/.gitignore new file mode 100644 index 0000000000..865c51d498 --- /dev/null +++ b/src/server/game/LuaEngine/.gitignore @@ -0,0 +1,5 @@ +*.orig +*.rej +*.bak +*.patch +*.diff diff --git a/src/server/game/LuaEngine/BindingMap.h b/src/server/game/LuaEngine/BindingMap.h new file mode 100644 index 0000000000..ab59371c35 --- /dev/null +++ b/src/server/game/LuaEngine/BindingMap.h @@ -0,0 +1,370 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _BINDING_MAP_H +#define _BINDING_MAP_H + +#include +#include "Common.h" +#include "ElunaUtility.h" +#include + +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +}; + +class BaseBindingMap +{ +public: + virtual ~BaseBindingMap() = default; +}; + +/* + * A set of bindings from keys of type `K` to Lua references. + */ +template +class BindingMap : public BaseBindingMap +{ +private: + lua_State* L; + uint64 maxBindingID; + + struct Binding + { + uint64 id; + lua_State* L; + uint32 remainingShots; + int functionReference; + + Binding(lua_State* L, uint64 id, int functionReference, uint32 remainingShots) : + id(id), + L(L), + remainingShots(remainingShots), + functionReference(functionReference) + { } + + ~Binding() + { + luaL_unref(L, LUA_REGISTRYINDEX, functionReference); + } + }; + + typedef std::vector< std::unique_ptr > BindingList; + + std::unordered_map bindings; + /* + * This table is for fast removal of bindings by ID. + * + * Instead of having to look through (potentially) every BindingList to find + * the Binding with the right ID, this allows you to go directly to the + * BindingList that might have the Binding with that ID. + * + * However, you must be careful not to store pointers to BindingLists + * that no longer exist (see `void Clear(const K& key)` implementation). + */ + std::unordered_map id_lookup_table; + +public: + BindingMap(lua_State* L) : + L(L), + maxBindingID(0) + { } + + ~BindingMap() noexcept override = default; + + /* + * Insert a new binding from `key` to `ref`, which lasts for `shots`-many pushes. + * + * If `shots` is 0, it will never automatically expire, but can still be + * removed with `Clear` or `Remove`. + */ + uint64 Insert(const K& key, int ref, uint32 shots) + { + uint64 id = (++maxBindingID); + BindingList& list = bindings[key]; + list.push_back(std::unique_ptr(new Binding(L, id, ref, shots))); + id_lookup_table[id] = &list; + return id; + } + + /* + * Clear all bindings for `key`. + */ + void Clear(const K& key) + { + if (bindings.empty()) + return; + + auto iter = bindings.find(key); + if (iter == bindings.end()) + return; + + BindingList& list = iter->second; + + // Remove all pointers to `list` from `id_lookup_table`. + for (auto i = list.begin(); i != list.end(); ++i) + { + std::unique_ptr& binding = *i; + id_lookup_table.erase(binding->id); + } + + bindings.erase(key); + } + + /* + * Clear all bindings for all keys. + */ + void Clear() + { + if (bindings.empty()) + return; + + id_lookup_table.clear(); + bindings.clear(); + } + + /* + * Remove a specific binding identified by `id`. + * + * If `id` in invalid, nothing is removed. + */ + void Remove(uint64 id) + { + auto iter = id_lookup_table.find(id); + if (iter == id_lookup_table.end()) + return; + + BindingList* list = iter->second; + auto i = list->begin(); + + for (; i != list->end(); ++i) + { + std::unique_ptr& binding = *i; + if (binding->id == id) + break; + } + + if (i != list->end()) + list->erase(i); + + // Unconditionally erase the ID in the lookup table because + // it was either already invalid, or it's no longer valid. + id_lookup_table.erase(id); + } + + /* + * Check whether `key` has any bindings. + */ + bool HasBindingsFor(const K& key) + { + if (bindings.empty()) + return false; + + auto result = bindings.find(key); + if (result == bindings.end()) + return false; + + BindingList& list = result->second; + return !list.empty(); + } + + /* + * Push all Lua references for `key` onto the stack. + */ + void PushRefsFor(const K& key) + { + if (bindings.empty()) + return; + + auto result = bindings.find(key); + if (result == bindings.end()) + return; + + BindingList& list = result->second; + for (auto i = list.begin(); i != list.end();) + { + std::unique_ptr& binding = (*i); + auto i_prev = (i++); + + lua_rawgeti(L, LUA_REGISTRYINDEX, binding->functionReference); + + if (binding->remainingShots > 0) + { + binding->remainingShots -= 1; + + if (binding->remainingShots == 0) + { + id_lookup_table.erase(binding->id); + list.erase(i_prev); + } + } + } + } +}; + + +/* + * A `BindingMap` key type for simple event ID bindings + * (ServerEvents, GuildEvents, etc.). + */ +template +struct EventKey +{ + T event_id; + + EventKey(T event_id) : + event_id(event_id) + { } +}; + +/* + * A `BindingMap` key type for event ID/Object entry ID bindings + * (CreatureEvents, GameObjectEvents, etc.). + */ +template +struct EntryKey +{ + T event_id; + uint32 entry; + + EntryKey(T event_id, uint32 entry) : + event_id(event_id), + entry(entry) + { } +}; + +/* + * A `BindingMap` key type for event ID/unique Object bindings + * (currently just CreatureEvents). + */ +template +struct UniqueObjectKey +{ + T event_id; + ObjectGuid guid; + uint32 instance_id; + + UniqueObjectKey(T event_id, ObjectGuid guid, uint32 instance_id) : + event_id(event_id), + guid(guid), + instance_id(instance_id) + { } +}; + +class hash_helper +{ +public: + typedef std::size_t result_type; + + template + static inline result_type hash(T1 const & t1, T2 const & t2, T const &... t) + { + result_type seed = 0; + _hash_combine(seed, t1, t2, t...); + return seed; + } + + template ::value>::type* = nullptr> + static inline result_type hash(T const & t) + { + return std::hash::type>()(t); + } + + template ::value>::type* = nullptr> + static inline result_type hash(T const & t) + { + return std::hash()(t); + } + +private: + template + static inline void _hash_combine(result_type& seed, T const & v) + { + // from http://www.boost.org/doc/libs/1_40_0/boost/functional/hash/hash.hpp + seed ^= hash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + + template + static inline void _hash_combine(result_type& seed, H const & h, T1 const & t1, T const &... t) + { + _hash_combine(seed, h); + _hash_combine(seed, t1, t...); + } +}; + +/* + * Implementations of various std functions on the above key types, + * so that they can be used within an unordered_map. + */ +namespace std +{ + template + struct equal_to < EventKey > + { + bool operator()(EventKey const& lhs, EventKey const& rhs) const + { + return lhs.event_id == rhs.event_id; + } + }; + + template + struct equal_to < EntryKey > + { + bool operator()(EntryKey const& lhs, EntryKey const& rhs) const + { + return lhs.event_id == rhs.event_id + && lhs.entry == rhs.entry; + } + }; + + template + struct equal_to < UniqueObjectKey > + { + bool operator()(UniqueObjectKey const& lhs, UniqueObjectKey const& rhs) const + { + return lhs.event_id == rhs.event_id + && lhs.guid == rhs.guid + && lhs.instance_id == rhs.instance_id; + } + }; + + template + struct hash < EventKey > + { + typedef EventKey argument_type; + + hash_helper::result_type operator()(argument_type const& k) const + { + return hash_helper::hash(k.event_id); + } + }; + + template + struct hash < EntryKey > + { + typedef EntryKey argument_type; + + hash_helper::result_type operator()(argument_type const& k) const + { + return hash_helper::hash(k.event_id, k.entry); + } + }; + + template + struct hash < UniqueObjectKey > + { + typedef UniqueObjectKey argument_type; + + hash_helper::result_type operator()(argument_type const& k) const + { + return hash_helper::hash(k.event_id, k.instance_id, k.guid); + } + }; +} + +#endif // _BINDING_MAP_H diff --git a/src/server/game/LuaEngine/CMakeLists.txt b/src/server/game/LuaEngine/CMakeLists.txt new file mode 100644 index 0000000000..308a0be7c4 --- /dev/null +++ b/src/server/game/LuaEngine/CMakeLists.txt @@ -0,0 +1,90 @@ +set(CUSTOM_METHODS_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/methods/Custom/CustomMethods.h") +if(EXISTS "${CUSTOM_METHODS_HEADER}") + if(NOT ${LUA_VERSION} MATCHES "luajit") + target_compile_definitions(lualib PUBLIC ELUNA_USE_CUSTOM_METHODS) + else() + target_compile_definitions(lualib INTERFACE ELUNA_USE_CUSTOM_METHODS) + endif() +endif() + +# Define variables for paths +set(MODULES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/modules") +if(UNIX) + set(MODULES_OUTPUT_DIR "bin/lua_scripts/modules") +elseif(WIN32) + set(MODULES_OUTPUT_DIR "${CMAKE_BINARY_DIR}/bin/lua_scripts/modules") + set(MODULES_OUTPUT_DIR_CONF "${CMAKE_BINARY_DIR}/bin/$/lua_scripts/modules") +endif() + +# Add Lua compatibility definition +if(NOT ${LUA_VERSION} MATCHES "luajit") + target_compile_definitions(lualib PUBLIC LUA_COMPAT_MODULE) +endif() + +# Collect module directories +file(GLOB_RECURSE modules_list LIST_DIRECTORIES true ${MODULES_DIR}/*) +foreach(dir ${modules_list}) + if(IS_DIRECTORY ${dir}) + get_filename_component(module_name ${dir} NAME) + + # Gather all module source files + file(GLOB sources_module + ${dir}/*.c + ${dir}/*.cpp + ${dir}/*.h + ${dir}/*.hpp) + + # Add module target and properties + if(sources_module) + add_library(${module_name} MODULE ${sources_module}) + target_include_directories(${module_name} INTERFACE ${PUBLIC_INCLUDES}) + target_link_libraries(${module_name} PUBLIC lualib) + set_target_properties(${module_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${MODULES_OUTPUT_DIR}) + + if(WIN32) + set_target_properties(${module_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${MODULES_OUTPUT_DIR}) + if(MSVC) + foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) + string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) + set_target_properties(${module_name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${MODULES_OUTPUT_DIR_CONF} + LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${MODULES_OUTPUT_DIR_CONF}) + endforeach() + endif() + endif() + + message(STATUS "Added Eluna custom module: ${dir}") + endif() + + # Append to lists + list(APPEND list_module_names ${module_name}) + list(APPEND list_module_sources ${sources_module}) + list(APPEND list_module_includes ${dir}) + endif() +endforeach() + +# Safeguard to remove module sources from all other build targets than the modules themselves +macro(remove_module_sources target) + get_target_property(_sources ${target} SOURCES) + if(_sources) + foreach(item ${list_module_sources}) + list(REMOVE_ITEM _sources ${item}) + endforeach() + set_property(TARGET ${target} PROPERTY SOURCES ${_sources}) + endif() + + get_target_property(_includes ${target} INCLUDE_DIRECTORIES) + if(_includes) + foreach(dir ${list_module_includes}) + list(REMOVE_ITEM _includes ${dir}) + endforeach() + set_property(TARGET ${target} PROPERTY INCLUDE_DIRECTORIES ${_includes}) + endif() +endmacro() + +foreach(target ${all_targets}) + if (NOT ${target} IN_LIST list_module_names) + remove_module_sources(${target}) + endif() +endforeach() diff --git a/src/server/game/LuaEngine/ElunaCompat.cpp b/src/server/game/LuaEngine/ElunaCompat.cpp new file mode 100644 index 0000000000..c7c5a62862 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaCompat.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "ElunaCompat.h" + +#if LUA_VERSION_NUM == 501 +const char* luaL_tolstring(lua_State* L, int idx, size_t* len) { + if (!luaL_callmeta(L, idx, "__tostring")) { + int t = lua_type(L, idx), tt = 0; + char const* name = NULL; + switch (t) { + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + case LUA_TSTRING: + case LUA_TNUMBER: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) + lua_pushliteral(L, "true"); + else + lua_pushliteral(L, "false"); + break; + default: + tt = luaL_getmetafield(L, idx, "__name"); + name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); + lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_replace(L, -2); + break; + } + } + else { + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + return lua_tolstring(L, -1, len); +} + +int luaL_getsubtable(lua_State* L, int i, const char* name) { + int abs_i = lua_absindex(L, i); + luaL_checkstack(L, 3, "not enough stack slots"); + lua_pushstring(L, name); + lua_gettable(L, abs_i); + if (lua_istable(L, -1)) + return 1; + lua_pop(L, 1); + lua_newtable(L); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + lua_settable(L, abs_i); + return 0; +} + +int lua_absindex(lua_State* L, int i) { + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} + +#if !defined LUAJIT_VERSION +void* luaL_testudata(lua_State* L, int index, const char* tname) { + void* ud = lua_touserdata(L, index); + if (ud) + { + if (lua_getmetatable(L, index)) + { + luaL_getmetatable(L, tname); + if (!lua_rawequal(L, -1, -2)) + ud = NULL; + lua_pop(L, 2); + return ud; + } + } + return NULL; +} + +void luaL_setmetatable(lua_State* L, const char* tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +} +#endif +#endif diff --git a/src/server/game/LuaEngine/ElunaCompat.h b/src/server/game/LuaEngine/ElunaCompat.h new file mode 100644 index 0000000000..9053a35946 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaCompat.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef ELUNACOMPAT_H +#define ELUNACOMPAT_H + +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +#if __has_include() +#include +#endif +}; + +/* Compatibility layer for compiling with Lua 5.1 or LuaJIT */ +#if LUA_VERSION_NUM == 501 + int luaL_getsubtable(lua_State* L, int i, const char* name); + const char* luaL_tolstring(lua_State* L, int idx, size_t* len); + int lua_absindex(lua_State* L, int i); + #define lua_pushglobaltable(L) \ + lua_pushvalue((L), LUA_GLOBALSINDEX) + #define lua_rawlen(L, idx) \ + lua_objlen(L, idx) + #define lua_pushunsigned(L, u) \ + lua_pushinteger(L, u) + #define lua_load(L, buf_read, dec_buf, str, NULL) \ + lua_load(L, buf_read, dec_buf, str) + +#if !defined LUAJIT_VERSION + void* luaL_testudata(lua_State* L, int index, const char* tname); + void luaL_setmetatable(lua_State* L, const char* tname); + #define luaL_setfuncs(L, l, n) luaL_register(L, NULL, l) +#endif +#endif + +#if LUA_VERSION_NUM > 502 + #define lua_dump(L, writer, data) \ + lua_dump(L, writer, data, 0) + #define lua_pushunsigned(L, u) \ + lua_pushinteger(L, u) +#endif +#endif diff --git a/src/server/game/LuaEngine/ElunaConfig.cpp b/src/server/game/LuaEngine/ElunaConfig.cpp new file mode 100644 index 0000000000..f8c3aefd9a --- /dev/null +++ b/src/server/game/LuaEngine/ElunaConfig.cpp @@ -0,0 +1,125 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +#include "Config.h" +#else +#include "Config/Config.h" +#endif +#include "ElunaConfig.h" + +#include +#include +#include + +ElunaConfig::ElunaConfig() +{ +} + +ElunaConfig* ElunaConfig::instance() +{ + static ElunaConfig instance; + return &instance; +} + +ElunaConfig::~ElunaConfig() +{ +} + +void ElunaConfig::Initialize() +{ + // Load bools + SetConfig(CONFIG_ELUNA_ENABLED, "Eluna.Enabled", true); + SetConfig(CONFIG_ELUNA_TRACEBACK, "Eluna.TraceBack", false); + SetConfig(CONFIG_ELUNA_SCRIPT_RELOADER, "Eluna.ScriptReloader", false); + SetConfig(CONFIG_ELUNA_ENABLE_UNSAFE, "Eluna.UseUnsafeMethods", true); + SetConfig(CONFIG_ELUNA_ENABLE_DEPRECATED, "Eluna.UseDeprecatedMethods", true); + SetConfig(CONFIG_ELUNA_ENABLE_RELOAD_COMMAND, "Eluna.ReloadCommand", true); + + // Load strings + SetConfig(CONFIG_ELUNA_SCRIPT_PATH, "Eluna.ScriptPath", "lua_scripts"); + SetConfig(CONFIG_ELUNA_ONLY_ON_MAPS, "Eluna.OnlyOnMaps", ""); + SetConfig(CONFIG_ELUNA_REQUIRE_PATH_EXTRA, "Eluna.RequirePaths", ""); + SetConfig(CONFIG_ELUNA_REQUIRE_CPATH_EXTRA, "Eluna.RequireCPaths", ""); + + // Load ints + SetConfig(CONFIG_ELUNA_RELOAD_SECURITY_LEVEL, "Eluna.ReloadSecurityLevel", 3); + + // Call extra functions + TokenizeAllowedMaps(); +} + +void ElunaConfig::SetConfig(ElunaConfigBoolValues index, char const* fieldname, bool defvalue) +{ +#if defined ELUNA_TRINITY + SetConfig(index, sConfigMgr->GetBoolDefault(fieldname, defvalue)); +#elif defined ELUNA_AZEROTHCORE + SetConfig(index, sConfigMgr->GetOption(fieldname, defvalue)); +#else + SetConfig(index, sConfig.GetBoolDefault(fieldname, defvalue)); +#endif +} + +void ElunaConfig::SetConfig(ElunaConfigStringValues index, char const* fieldname, std::string defvalue) +{ +#if defined ELUNA_TRINITY + SetConfig(index, sConfigMgr->GetStringDefault(fieldname, defvalue)); +#elif defined ELUNA_CMANGOS + SetConfig(index, sConfig.GetStringDefault(fieldname, defvalue)); +#elif defined ELUNA_AZEROTHCORE + SetConfig(index, sConfigMgr->GetOption(fieldname, defvalue)); +#else + SetConfig(index, sConfig.GetStringDefault(fieldname, defvalue.c_str())); +#endif +} + +void ElunaConfig::SetConfig(ElunaConfigUInt32Values index, char const* fieldname, uint32 defvalue) +{ +#if defined ELUNA_TRINITY + SetConfig(index, sConfigMgr->GetIntDefault(fieldname, defvalue)); +#elif defined ELUNA_AZEROTHCORE + SetConfig(index, sConfigMgr->GetOption(fieldname, defvalue)); +#else + SetConfig(index, sConfig.GetIntDefault(fieldname, defvalue)); +#endif +} + +bool ElunaConfig::ShouldMapLoadEluna(uint32 id) +{ + // if the set is empty (all maps), return true + if (m_allowedMaps.empty()) + return true; + + // Check if the map ID is in the set + return (m_allowedMaps.find(id) != m_allowedMaps.end()); +} + +void ElunaConfig::TokenizeAllowedMaps() +{ + // clear allowed maps + m_allowedMaps.clear(); + + // read the configuration value into stringstream + std::istringstream maps(GetConfig(CONFIG_ELUNA_ONLY_ON_MAPS)); + + // tokenize maps and add to allowed maps + std::string mapIdStr; + while (std::getline(maps, mapIdStr, ',')) + { + // remove spaces + mapIdStr.erase(std::remove_if(mapIdStr.begin(), mapIdStr.end(), [](char c) { + return std::isspace(static_cast(c)); + }), mapIdStr.end()); + + try { + uint32 mapId = std::stoul(mapIdStr); + m_allowedMaps.emplace(mapId); + } + catch (std::exception&) { + ELUNA_LOG_ERROR("[Eluna]: Error tokenizing Eluna.OnlyOnMaps, invalid config value '%s'", mapIdStr.c_str()); + } + } +} diff --git a/src/server/game/LuaEngine/ElunaConfig.h b/src/server/game/LuaEngine/ElunaConfig.h new file mode 100644 index 0000000000..87acc3820e --- /dev/null +++ b/src/server/game/LuaEngine/ElunaConfig.h @@ -0,0 +1,82 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNACONFIG_H +#define _ELUNACONFIG_H + +#include "ElunaUtility.h" + +enum ElunaConfigBoolValues +{ + CONFIG_ELUNA_ENABLED, + CONFIG_ELUNA_TRACEBACK, + CONFIG_ELUNA_SCRIPT_RELOADER, + CONFIG_ELUNA_ENABLE_UNSAFE, + CONFIG_ELUNA_ENABLE_DEPRECATED, + CONFIG_ELUNA_ENABLE_RELOAD_COMMAND, + CONFIG_ELUNA_BOOL_COUNT +}; + +enum ElunaConfigStringValues +{ + CONFIG_ELUNA_SCRIPT_PATH, + CONFIG_ELUNA_ONLY_ON_MAPS, + CONFIG_ELUNA_REQUIRE_PATH_EXTRA, + CONFIG_ELUNA_REQUIRE_CPATH_EXTRA, + CONFIG_ELUNA_STRING_COUNT +}; + +enum ElunaConfigUInt32Values +{ + CONFIG_ELUNA_RELOAD_SECURITY_LEVEL, + CONFIG_ELUNA_INT_COUNT +}; + +class ElunaConfig +{ +private: + ElunaConfig(); + ~ElunaConfig(); + ElunaConfig(ElunaConfig const&) = delete; + ElunaConfig& operator=(ElunaConfig const&) = delete; + +public: + static ElunaConfig* instance(); + + void Initialize(); + + bool GetConfig(ElunaConfigBoolValues index) const { return _configBoolValues[index]; } + const std::string& GetConfig(ElunaConfigStringValues index) const { return _configStringValues[index]; } + const uint32& GetConfig(ElunaConfigUInt32Values index) const { return _configUInt32Values[index]; } + + bool IsElunaEnabled() { return GetConfig(CONFIG_ELUNA_ENABLED); } + bool UnsafeMethodsEnabled() { return GetConfig(CONFIG_ELUNA_ENABLE_UNSAFE); } + bool DeprecatedMethodsEnabled() { return GetConfig(CONFIG_ELUNA_ENABLE_DEPRECATED); } + bool IsReloadCommandEnabled() { return GetConfig(CONFIG_ELUNA_ENABLE_RELOAD_COMMAND); } + AccountTypes GetReloadSecurityLevel() { return static_cast(GetConfig(CONFIG_ELUNA_RELOAD_SECURITY_LEVEL)); } + bool ShouldMapLoadEluna(uint32 mapId); + +private: + bool _configBoolValues[CONFIG_ELUNA_BOOL_COUNT]; + std::string _configStringValues[CONFIG_ELUNA_STRING_COUNT]; + uint32 _configUInt32Values[CONFIG_ELUNA_INT_COUNT]; + + void SetConfig(ElunaConfigBoolValues index, bool value) { _configBoolValues[index] = value; } + void SetConfig(ElunaConfigStringValues index, std::string value) { _configStringValues[index] = value; } + void SetConfig(ElunaConfigUInt32Values index, uint32 value) { _configUInt32Values[index] = value; } + + void SetConfig(ElunaConfigBoolValues index, char const* fieldname, bool defvalue); + void SetConfig(ElunaConfigStringValues index, char const* fieldname, std::string defvalue); + void SetConfig(ElunaConfigUInt32Values index, char const* fieldname, uint32 defvalue); + + void TokenizeAllowedMaps(); + + std::unordered_set m_allowedMaps; +}; + +#define sElunaConfig ElunaConfig::instance() + +#endif //_ELUNACONFIG_H diff --git a/src/server/game/LuaEngine/ElunaCreatureAI.h b/src/server/game/LuaEngine/ElunaCreatureAI.h new file mode 100644 index 0000000000..f4ebcdd6b2 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaCreatureAI.h @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef _ELUNA_CREATURE_AI_H +#define _ELUNA_CREATURE_AI_H + +#include "LuaEngine.h" + +// [新增]: 强制声明全局唯一引擎 +extern class Eluna* sEluna; + +#if defined ELUNA_CMANGOS +#include "AI/BaseAI/CreatureAI.h" +#endif + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +struct ScriptedAI; +typedef ScriptedAI NativeScriptedAI; +#elif defined ELUNA_CMANGOS || ELUNA_MANGOS +class CreatureAI; +typedef CreatureAI NativeScriptedAI; +#elif defined ELUNA_VMANGOS +class BasicAI; +typedef BasicAI NativeScriptedAI; +#endif + +struct ElunaCreatureAI : NativeScriptedAI +{ + bool justSpawned; + std::vector< std::pair > movepoints; +#if !defined ELUNA_TRINITY && !defined ELUNA_AZEROTHCORE +#define me m_creature +#endif + ElunaCreatureAI(Creature* creature) : NativeScriptedAI(creature), justSpawned(true) + { + } + ~ElunaCreatureAI() { } + +#if !defined ELUNA_TRINITY + void UpdateAI(const uint32 diff) override +#else + void UpdateAI(uint32 diff) override +#endif + { +#if !defined ELUNA_TRINITY + if (justSpawned) + { + justSpawned = false; + JustRespawned(); + } +#endif + if (!movepoints.empty()) + { + for (auto& point : movepoints) + { + if (!sEluna->MovementInform(me, point.first, point.second)) // [修复]: sEluna + NativeScriptedAI::MovementInform(point.first, point.second); + } + movepoints.clear(); + } + + if (!sEluna->UpdateAI(me, diff)) // [修复]: sEluna + { +#if !defined ELUNA_MANGOS + if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) +#else + if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE)) +#endif + NativeScriptedAI::UpdateAI(diff); + } + } + +/* [屏蔽] 7.3.5 参数不兼容: JustEngagedWith / EnterCombat +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + void JustEngagedWith(Unit* target) + { + if (!sEluna->EnterCombat(me, target)) + NativeScriptedAI::JustEngagedWith(target); + } +#else + void EnterCombat(Unit* target) override + { + if (!sEluna->EnterCombat(me, target)) + NativeScriptedAI::EnterCombat(target); + } +#endif +*/ + +/* [屏蔽] 7.3.5 参数不兼容: DamageTaken +#if defined ELUNA_TRINITY || defined ELUNA_CMANGOS + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellInfo const* spellInfo) +#elif defined ELUNA_AZEROTHCORE + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) +#else + void DamageTaken(Unit* attacker, uint32& damage) +#endif + { + if (!sEluna->DamageTaken(me, attacker, damage)) + { +#if defined ELUNA_TRINITY || defined ELUNA_CMANGOS + NativeScriptedAI::DamageTaken(attacker, damage, damageType, spellInfo); +#elif defined ELUNA_AZEROTHCORE + NativeScriptedAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask); +#else + NativeScriptedAI::DamageTaken(attacker, damage); +#endif + } + } +*/ + + void JustDied(Unit* killer) override + { + if (!sEluna->JustDied(me, killer)) // [修复]: sEluna + NativeScriptedAI::JustDied(killer); + } + + void KilledUnit(Unit* victim) override + { + if (!sEluna->KilledUnit(me, victim)) // [修复]: sEluna + NativeScriptedAI::KilledUnit(victim); + } + + void JustSummoned(Creature* summon) override + { + if (!sEluna->JustSummoned(me, summon)) // [修复]: sEluna + NativeScriptedAI::JustSummoned(summon); + } + + void SummonedCreatureDespawn(Creature* summon) override + { + if (!sEluna->SummonedCreatureDespawn(me, summon)) // [修复]: sEluna + NativeScriptedAI::SummonedCreatureDespawn(summon); + } + + void MovementInform(uint32 type, uint32 id) override + { + movepoints.push_back(std::make_pair(type, id)); + } + + void AttackStart(Unit* target) override + { + if (!sEluna->AttackStart(me, target)) // [修复]: sEluna + NativeScriptedAI::AttackStart(target); + } + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + void EnterEvadeMode(EvadeReason /*why*/) override +#else + void EnterEvadeMode() override +#endif + { + if (!sEluna->EnterEvadeMode(me)) // [修复]: sEluna + NativeScriptedAI::EnterEvadeMode(); + } + +/* [屏蔽] 7.3.5 参数不兼容: JustAppeared / JustRespawned +#if defined ELUNA_TRINITY + void JustAppeared() + { + if (!sEluna->JustRespawned(me)) + NativeScriptedAI::JustAppeared(); + } +#else + void JustRespawned() override + { + if (!sEluna->JustRespawned(me)) + NativeScriptedAI::JustRespawned(); + } +#endif +*/ + + void JustReachedHome() override + { + if (!sEluna->JustReachedHome(me)) // [修复]: sEluna + NativeScriptedAI::JustReachedHome(); + } + + void ReceiveEmote(Player* player, uint32 emoteId) override + { + if (!sEluna->ReceiveEmote(me, player, emoteId)) // [修复]: sEluna + NativeScriptedAI::ReceiveEmote(player, emoteId); + } + + void CorpseRemoved(uint32& respawnDelay) override + { + if (!sEluna->CorpseRemoved(me, respawnDelay)) // [修复]: sEluna + NativeScriptedAI::CorpseRemoved(respawnDelay); + } + +#if !defined ELUNA_TRINITY && !defined ELUNA_VMANGOS && !defined ELUNA_AZEROTHCORE + bool IsVisible(Unit* who) const override + { + return true; + } +#endif + + void MoveInLineOfSight(Unit* who) override + { + if (!sEluna->MoveInLineOfSight(me, who)) // [修复]: sEluna + NativeScriptedAI::MoveInLineOfSight(who); + } + +/* [屏蔽] 7.3.5 参数不兼容: SpellHit +#if defined ELUNA_TRINITY + void SpellHit(WorldObject* caster, SpellInfo const* spell) +#elif defined ELUNA_VMANGOS + void SpellHit(Unit* caster, SpellInfo const* spell) +#else + void SpellHit(Unit* caster, SpellInfo const* spell) +#endif + { + if (!sEluna->SpellHit(me, caster, spell)) + NativeScriptedAI::SpellHit(caster, spell); + } +*/ + +/* [屏蔽] 7.3.5 参数不兼容: SpellHitTarget +#if defined ELUNA_TRINITY + void SpellHitTarget(WorldObject* target, SpellInfo const* spell) +#else + void SpellHitTarget(Unit* target, SpellInfo const* spell) +#endif + { + if (!sEluna->SpellHitTarget(me, target, spell)) + NativeScriptedAI::SpellHitTarget(target, spell); + } +*/ + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +/* [屏蔽] 7.3.5 参数不兼容: IsSummonedBy + void IsSummonedBy(WorldObject* summoner) + { + if (!summoner->ToUnit() || !sEluna->OnSummoned(me, summoner->ToUnit())) + NativeScriptedAI::IsSummonedBy(summoner); + } +*/ + + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + if (!sEluna->SummonedCreatureDies(me, summon, killer)) // [修复]: sEluna + NativeScriptedAI::SummonedCreatureDies(summon, killer); + } + + void OwnerAttackedBy(Unit* attacker) override + { + if (!sEluna->OwnerAttackedBy(me, attacker)) // [修复]: sEluna + NativeScriptedAI::OwnerAttackedBy(attacker); + } + + void OwnerAttacked(Unit* target) override + { + if (!sEluna->OwnerAttacked(me, target)) // [修复]: sEluna + NativeScriptedAI::OwnerAttacked(target); + } +#endif + +#if !defined ELUNA_TRINITY && !defined ELUNA_AZEROTHCORE +#undef me +#endif +}; + +#endif \ No newline at end of file diff --git a/src/server/game/LuaEngine/ElunaEventMgr.cpp b/src/server/game/LuaEngine/ElunaEventMgr.cpp new file mode 100644 index 0000000000..9f0b4121e9 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaEventMgr.cpp @@ -0,0 +1,279 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ElunaEventMgr.h" +#include "LuaEngine.h" +#if !defined ELUNA_CMANGOS +#include "Object.h" +#else +#include "Entities/Object.h" +#endif + +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +}; + +ElunaEventProcessor::~ElunaEventProcessor() +{ + ClearAllEvents(); +} + +void ElunaEventProcessor::Update(uint32 diff) +{ + isUpdating = true; + + m_time += diff; + while (!eventList.empty() && eventList.begin()->first <= m_time) + { + auto it = eventList.begin(); + LuaEvent* luaEvent = it->second; + eventList.erase(it); + + if (luaEvent->state != LUAEVENT_STATE_ERASE) + eventMap.erase(luaEvent->funcRef); + + if (luaEvent->state == LUAEVENT_STATE_RUN) + { + uint32 delay = luaEvent->delay; + bool remove = luaEvent->repeats == 1; + if (!remove) + AddEvent(luaEvent); // may be deferred if we recurse into Update + + // Call the timed event + if (!obj || (obj && obj->IsInWorld())) + mgr->E->OnTimedEvent(luaEvent->funcRef, delay, luaEvent->repeats ? luaEvent->repeats-- : luaEvent->repeats, obj); + + if (!remove) + continue; + } + + // Event should be deleted (executed last time or set to be aborted) + RemoveEvent(luaEvent); + } + + isUpdating = false; + ProcessDeferredOps(); +} + +void ElunaEventProcessor::SetStates(LuaEventState state) +{ + if (isUpdating) + { + QueueDeferredOp(DeferredOpType::SetStates, nullptr, 0, state); + return; + } + + for (auto& [time, event] : eventList) + event->SetState(state); + + if (state == LUAEVENT_STATE_ERASE) + eventMap.clear(); +} + +void ElunaEventProcessor::ClearAllEvents() +{ + if (isUpdating) + { + QueueDeferredOp(DeferredOpType::ClearAll); + return; + } + + for (auto& [time, event] : eventList) + RemoveEvent(event); + + deferredOps.clear(); + eventList.clear(); + eventMap.clear(); +} + +void ElunaEventProcessor::SetState(int eventId, LuaEventState state) +{ + if (isUpdating) + { + QueueDeferredOp(DeferredOpType::SetState, nullptr, eventId, state); + return; + } + + auto itr = eventMap.find(eventId); + if (itr != eventMap.end()) + itr->second->SetState(state); + + if (state == LUAEVENT_STATE_ERASE) + eventMap.erase(eventId); +} + +void ElunaEventProcessor::AddEvent(LuaEvent* luaEvent) +{ + if (isUpdating) + { + QueueDeferredOp(DeferredOpType::AddEvent, luaEvent); + return; + } + + luaEvent->GenerateDelay(); + eventList.emplace(m_time + luaEvent->delay, luaEvent); + eventMap[luaEvent->funcRef] = luaEvent; +} + +void ElunaEventProcessor::AddEvent(int funcRef, uint32 min, uint32 max, uint32 repeats) +{ + AddEvent(new LuaEvent(funcRef, min, max, repeats)); +} + +void ElunaEventProcessor::RemoveEvent(LuaEvent* luaEvent) +{ + // Unreference if should and if Eluna was not yet uninitialized and if the lua state still exists + if (luaEvent->state != LUAEVENT_STATE_ERASE && mgr->E->HasLuaState()) + { + // Free lua function ref + luaL_unref(mgr->E->L, LUA_REGISTRYINDEX, luaEvent->funcRef); + } + delete luaEvent; +} + +void ElunaEventProcessor::QueueDeferredOp(DeferredOpType type, LuaEvent* event, int eventId, LuaEventState state) +{ + DeferredOp op; + op.type = type; + op.event = event; + op.eventId = eventId; + op.state = state; + deferredOps.push_back(op); +} + +void ElunaEventProcessor::ProcessDeferredOps() +{ + if (deferredOps.empty()) + return; + + std::vector ops; + ops.swap(deferredOps); + + using Handler = void(*)(ElunaEventProcessor*, DeferredOp&); + static constexpr Handler handlers[] = + { + [](ElunaEventProcessor* self, DeferredOp& op) { self->AddEvent(op.event); }, + [](ElunaEventProcessor* self, DeferredOp& op) { self->SetState(op.eventId, op.state); }, + [](ElunaEventProcessor* self, DeferredOp& op) { self->SetStates(op.state); }, + [](ElunaEventProcessor* self, DeferredOp& /*op*/) { self->ClearAllEvents(); } + }; + + for (DeferredOp& op : ops) + { + handlers[op.type](this, op); + } +} + +ElunaProcessorInfo::~ElunaProcessorInfo() +{ + if (mgr) + mgr->FlagObjectProcessorForDeletion(processorId); +} + +EventMgr::EventMgr(Eluna* _E) : E(_E) +{ + auto gp = std::make_unique(this, nullptr); + processors.insert(gp.get()); + globalProcessors.emplace(GLOBAL_EVENTS, std::move(gp)); +} + +EventMgr::~EventMgr() +{ + globalProcessors.clear(); + objectProcessors.clear(); + processors.clear(); + objectProcessorsPendingDelete.clear(); +} + +void EventMgr::UpdateProcessors(uint32 diff) +{ + // iterate a copy because processors may be destroyed during update (creature removed by a script, etc) + ProcessorSet copy = processors; + + for (auto* processor : copy) + { + if (processors.find(processor) != processors.end()) + if (!processor->pendingDeletion) + processor->Update(diff); + } + + CleanupObjectProcessors(); +} + +void EventMgr::SetAllEventStates(LuaEventState state) +{ + for (auto* processor : processors) + processor->SetStates(state); +} + +void EventMgr::SetEventState(int eventId, LuaEventState state) +{ + for (auto* processor : processors) + processor->SetState(eventId, state); +} + +ElunaEventProcessor* EventMgr::GetGlobalProcessor(GlobalEventSpace space) +{ + auto it = globalProcessors.find(space); + return (it != globalProcessors.end()) ? it->second.get() : nullptr; +} + +uint64 EventMgr::CreateObjectProcessor(WorldObject* obj) +{ +#if !ELUNA_CMANGOS && !ELUNA_VMANGOS + uint64 id = obj->GetGUID().GetCounter(); // [修改这里] +#else + uint64 id = obj->GetObjectGuid().GetCounter(); // [修改这里] +#endif + auto proc = std::make_unique(this, obj); + ElunaEventProcessor* raw = proc.get(); + + processors.insert(raw); + objectProcessors.emplace(id, std::move(proc)); + + return id; +} + +ElunaEventProcessor* EventMgr::GetObjectProcessor(uint64 processorId) +{ + auto it = objectProcessors.find(processorId); + return (it != objectProcessors.end()) ? it->second.get() : nullptr; +} + +void EventMgr::FlagObjectProcessorForDeletion(uint64 processorId) +{ + auto it = objectProcessors.find(processorId); + if (it == objectProcessors.end()) + return; + + ElunaEventProcessor* p = it->second.get(); + if (!p->pendingDeletion) + { + p->pendingDeletion = true; + p->obj = nullptr; + objectProcessorsPendingDelete.insert(processorId); + } +} + +void EventMgr::CleanupObjectProcessors() +{ + for (uint64 processorId : objectProcessorsPendingDelete) + { + auto it = objectProcessors.find(processorId); + if (it == objectProcessors.end()) + continue; + + ElunaEventProcessor* p = it->second.get(); + p->SetStates(LUAEVENT_STATE_ERASE); + + processors.erase(p); + objectProcessors.erase(it); + } + + objectProcessorsPendingDelete.clear(); +} diff --git a/src/server/game/LuaEngine/ElunaEventMgr.h b/src/server/game/LuaEngine/ElunaEventMgr.h new file mode 100644 index 0000000000..7a677b6a80 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaEventMgr.h @@ -0,0 +1,170 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNA_EVENT_MGR_H +#define _ELUNA_EVENT_MGR_H + +#include "ElunaUtility.h" +#include "Common.h" +#if defined ELUNA_TRINITY +#include "Random.h" +#elif defined ELUNA_CMANGOS +#include "Util/Util.h" +#else +#include "Util.h" +#endif +#include + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +#include "Define.h" +#else +#include "Platform/Define.h" +#endif + +class Eluna; +class EventMgr; +class ElunaEventProcessor; +class WorldObject; + +enum LuaEventState : uint8 +{ + LUAEVENT_STATE_RUN, // On next call run the function normally + LUAEVENT_STATE_ABORT, // On next call unregisters reffed function and erases the data + LUAEVENT_STATE_ERASE, // On next call just erases the data +}; + +enum DeferredOpType : uint8 +{ + AddEvent, + SetState, + SetStates, + ClearAll +}; + +enum GlobalEventSpace : uint8 +{ + GLOBAL_EVENTS +}; + +struct LuaEvent +{ + LuaEvent(int _funcRef, uint32 _min, uint32 _max, uint32 _repeats) : min(_min), max(_max), delay(0), repeats(_repeats), funcRef(_funcRef), state(LUAEVENT_STATE_RUN) { } + + void SetState(LuaEventState _state) + { + if (state != LUAEVENT_STATE_ERASE) + state = _state; + } + + void GenerateDelay() + { + delay = urand(min, max); + } + + uint32 min; // Minimum delay between event calls + uint32 max; // Maximum delay between event calls + uint32 delay; // The currently used waiting time + uint32 repeats; // Amount of repeats to make, 0 for infinite + int funcRef; // Lua function reference ID, also used as event ID + LuaEventState state; // State for next call +}; + +class ElunaEventProcessor +{ + friend class EventMgr; + +public: + typedef std::multimap EventList; + typedef std::unordered_map EventMap; + + ElunaEventProcessor(EventMgr* mgr, WorldObject* obj) : m_time(0), obj(obj), mgr(mgr) { } + ~ElunaEventProcessor(); + + void Update(uint32 diff); + // removes all timed events on next tick or at tick end + void SetStates(LuaEventState state); + // set the event to be removed when executing + void SetState(int eventId, LuaEventState state); + void AddEvent(int funcRef, uint32 min, uint32 max, uint32 repeats); + +private: + struct DeferredOp + { + int eventId = 0; + DeferredOpType type; + LuaEventState state = LUAEVENT_STATE_RUN; + LuaEvent* event = nullptr; + }; + + void ClearAllEvents(); + void AddEvent(LuaEvent* luaEvent); + void RemoveEvent(LuaEvent* luaEvent); + + void QueueDeferredOp(DeferredOpType type, LuaEvent* event = nullptr, int eventId = 0, LuaEventState state = LUAEVENT_STATE_RUN); + void ProcessDeferredOps(); + bool isUpdating = false; + std::vector deferredOps; + + EventList eventList; + EventMap eventMap; + uint64 m_time; + + bool pendingDeletion = false; + + WorldObject* obj; + EventMgr* mgr; +}; + +class ElunaProcessorInfo +{ +public: + ElunaProcessorInfo(EventMgr* mgr, uint64 processorId) : mgr(mgr), processorId(processorId) { } + ~ElunaProcessorInfo(); + + uint64 GetProcessorId() const { return processorId; } + +private: + EventMgr* mgr; + uint64 processorId; +}; + +class EventMgr +{ +public: + EventMgr(Eluna* _E); + ~EventMgr(); + + void UpdateProcessors(uint32 diff); + void SetAllEventStates(LuaEventState state); + void SetEventState(int eventId, LuaEventState state); + + // Global (per state) processors + ElunaEventProcessor* GetGlobalProcessor(GlobalEventSpace space); + + // Per-object processors + uint64 CreateObjectProcessor(WorldObject* obj); + ElunaEventProcessor* GetObjectProcessor(uint64 processorId); + void FlagObjectProcessorForDeletion(uint64 processorId); + +private: + typedef std::unordered_set ProcessorSet; + typedef std::unordered_map> ObjectProcessorMap; + typedef std::unordered_map> GlobalProcessorsMap; + + ProcessorSet processors; // tracks ALL processors (object + global) + GlobalProcessorsMap globalProcessors; + ObjectProcessorMap objectProcessors; + std::unordered_set objectProcessorsPendingDelete; + + Eluna* E; + + void CleanupObjectProcessors(); + + friend class ElunaEventProcessor; + friend class ElunaProcessorInfo; +}; + +#endif diff --git a/src/server/game/LuaEngine/ElunaIncludes.h b/src/server/game/LuaEngine/ElunaIncludes.h new file mode 100644 index 0000000000..f60ed9097c --- /dev/null +++ b/src/server/game/LuaEngine/ElunaIncludes.h @@ -0,0 +1,230 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNA_INCLUDES_H +#define _ELUNA_INCLUDES_H + +// Required +#if !defined ELUNA_CMANGOS +#include "AccountMgr.h" +#include "AuctionHouseMgr.h" +#include "Bag.h" +#include "Cell.h" +#include "CellImpl.h" +#include "Channel.h" +#include "Chat.h" +#include "DB2Stores.h" +#include "GameEventMgr.h" +#include "GossipDef.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "Group.h" +#include "Guild.h" +#include "GuildMgr.h" +#include "Language.h" +#include "Mail.h" +#if defined ELUNA_AZEROTHCORE +#include "MapMgr.h" +#else +#include "MapManager.h" +#endif +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "Opcodes.h" +#include "Pet.h" +#include "Player.h" +#include "ReputationMgr.h" +#include "ScriptMgr.h" +#include "Spell.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" +#include "SpellMgr.h" +#include "TemporarySummon.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +#include "Battleground.h" +#include "Config.h" +#include "DatabaseEnv.h" +#include "GitRevision.h" +#include "GroupMgr.h" +#include "MiscPackets.h" +#include "MotionMaster.h" +#include "ScriptedCreature.h" +#if defined ELUNA_TRINITY +#include "SpellHistory.h" +#endif +#if defined ELUNA_AZEROTHCORE +#include "WorldSessionMgr.h" +#endif +#include "SpellInfo.h" +#include "WeatherMgr.h" +#elif defined ELUNA_VMANGOS +#include "BasicAI.h" +#include "SQLStorages.h" +#elif defined ELUNA_MANGOS +#include "SQLStorages.h" +#endif // ELUNA_TRINITY +#if ELUNA_EXPANSION > EXP_CLASSIC +#include "ArenaTeam.h" +#endif +#if ELUNA_EXPANSION >= EXP_WOTLK +#include "Vehicle.h" +#endif +#else +#include "Accounts/AccountMgr.h" +#include "AuctionHouse/AuctionHouseMgr.h" +#include "Chat/Channel.h" +#include "Chat/Chat.h" +#include "DBScripts/ScriptMgr.h" +#include "Entities/GossipDef.h" +#include "Entities/Pet.h" +#include "Entities/Player.h" +#include "Entities/TemporarySpawn.h" +#include "GameEvents/GameEventMgr.h" +#include "Globals/ObjectAccessor.h" +#include "Globals/ObjectMgr.h" +#include "Grids/Cell.h" +#include "Grids/CellImpl.h" +#include "Grids/GridNotifiers.h" +#include "Grids/GridNotifiersImpl.h" +#include "Groups/Group.h" +#include "Guilds/Guild.h" +#include "Guilds/GuildMgr.h" +#include "Mails/Mail.h" +#include "Maps/MapManager.h" +#include "Reputation/ReputationMgr.h" +#include "Server/DBCStores.h" +#include "Server/Opcodes.h" +#include "Server/WorldPacket.h" +#include "Server/WorldSession.h" +#include "Spells/Spell.h" +#include "Spells/SpellAuras.h" +#include "Spells/SpellMgr.h" +#include "Tools/Language.h" +#include "Server/SQLStorages.h" +#if ELUNA_EXPANSION > EXP_CLASSIC +#include "Arena/ArenaTeam.h" +#endif +#if ELUNA_EXPANSION >= EXP_WOTLK +#include "Entities/Vehicle.h" +#endif +#if ELUNA_EXPANSION >= EXP_CATA +#include "AI/BaseAI/AggressorAI.h" +#else +#include "AI/BaseAI/UnitAI.h" +#endif +#endif + +#if !defined ELUNA_TRINITY && !defined ELUNA_AZEROTHCORE +#include "Config/Config.h" +#include "BattleGroundMgr.h" +#if !defined ELUNA_MANGOS +#include "revision.h" +#else +#include "GitRevision.h" +#include "revision_data.h" +#endif +#endif + +#if !defined ELUNA_MANGOS +#if ELUNA_EXPANSION > EXP_CLASSIC +typedef Opcodes OpcodesList; +#endif +#endif + +/* + * Note: if you add or change a CORE_NAME or CORE_VERSION #define, + * please update LuaGlobalFunctions::GetCoreName or LuaGlobalFunctions::GetCoreVersion documentation example string. + */ +#if defined ELUNA_CMANGOS +#define CORE_NAME "cMaNGOS" +#define CORE_VERSION REVISION_DATE " " REVISION_ID +#if ELUNA_EXPANSION == EXP_CATA +#define NUM_MSG_TYPES MAX_OPCODE_TABLE_SIZE +#endif +#endif + +#if defined ELUNA_VMANGOS +#define CORE_NAME "vMaNGOS" +#define CORE_VERSION REVISION_HASH +#define DEFAULT_LOCALE LOCALE_enUS +#endif + +#if defined ELUNA_MANGOS +#define CORE_NAME "MaNGOS" +#define CORE_VERSION PROJECT_REVISION_NR +#endif + +#if defined ELUNA_TRINITY +#define CORE_NAME "TrinityCore" +#define REGEN_TIME_FULL +#endif + +#if defined ELUNA_AZEROTHCORE +#define CORE_NAME "AzerothCore" +#define REGEN_TIME_FULL +#endif + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +#define CORE_VERSION (GitRevision::GetFullVersion()) +#define eWorld (sWorld) +#if defined ELUNA_AZEROTHCORE +#define eWorldSessionMgr (sWorldSessionMgr) +#endif +#define eMapMgr (sMapMgr) +#define eGuildMgr (sGuildMgr) +#define eObjectMgr (sObjectMgr) +#if defined ELUNA_AZEROTHCORE +#define eAccountMgr() AccountMgr:: +#else +#define eAccountMgr (sAccountMgr) +#endif +#define eAuctionMgr (sAuctionMgr) +#define eGameEventMgr (sGameEventMgr) +#define eObjectAccessor() ObjectAccessor:: +#else +#define eWorld (&sWorld) +#define eMapMgr (&sMapMgr) +#define eConfigMgr (&sConfig) +#define eGuildMgr (&sGuildMgr) +#define eObjectMgr (&sObjectMgr) +#define eAccountMgr (&sAccountMgr) +#define eAuctionMgr (&sAuctionMgr) +#define eGameEventMgr (&sGameEventMgr) +#define eObjectAccessor() sObjectAccessor. +#define SERVER_MSG_STRING SERVER_MSG_CUSTOM +#define TOTAL_LOCALES MAX_LOCALE +#define TARGETICONCOUNT TARGET_ICON_COUNT +#define MAX_TALENT_SPECS MAX_TALENT_SPEC_COUNT +#if !defined ELUNA_VMANGOS +#define TEAM_NEUTRAL TEAM_INDEX_NEUTRAL +#endif + + +#if defined ELUNA_VMANGOS +#define PLAYER_FIELD_LIFETIME_HONORABLE_KILLS PLAYER_FIELD_LIFETIME_HONORBALE_KILLS +#endif + +#if ELUNA_EXPANSION == EXP_TBC +#define SPELL_AURA_MOD_KILL_XP_PCT SPELL_AURA_MOD_XP_PCT +#endif + +#if !defined ELUNA_MANGOS +#if ELUNA_EXPANSION >= EXP_WOTLK +#define UNIT_BYTE2_FLAG_SANCTUARY UNIT_BYTE2_FLAG_SUPPORTABLE +#endif +#endif + +#if !defined ELUNA_CMANGOS +typedef TemporarySummon TempSummon; +#else +typedef TemporarySpawn TempSummon; +#endif +typedef SpellEntry SpellInfo; +#endif // ELUNA_TRINITY + +#endif // _ELUNA_INCLUDES_H diff --git a/src/server/game/LuaEngine/ElunaInstanceAI.cpp b/src/server/game/LuaEngine/ElunaInstanceAI.cpp new file mode 100644 index 0000000000..8cdf3e2fc6 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaInstanceAI.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ +#include "LuaEngine.h" +#include "ElunaInstanceAI.h" +#include "ElunaUtility.h" +#include "lmarshal.h" + + +#if !defined ELUNA_TRINITY +void ElunaInstanceAI::Initialize() +{ + ASSERT(!sEluna->HasInstanceData(instance)); + + // Create a new table for instance data. + lua_State* L = sEluna->L; + lua_newtable(L); + sEluna->CreateInstanceData(instance); + + sEluna->OnInitialize(this); +} +#endif + +void ElunaInstanceAI::Load(const char* data) +{ + // If we get passed NULL (i.e. `Reload` was called) then use + // the last known save data (or maybe just an empty string). + if (!data) + { + data = lastSaveData.c_str(); + } + else // Otherwise, copy the new data into our buffer. + { + lastSaveData.assign(data); + } + + if (data[0] == '\0') + { + ASSERT(!sEluna->HasInstanceData(instance)); + + // Create a new table for instance data. + lua_State* L = sEluna->L; + lua_newtable(L); + sEluna->CreateInstanceData(instance); + + sEluna->OnLoad(this); + // Stack: (empty) + return; + } + + size_t decodedLength; + const unsigned char* decodedData = ElunaUtil::DecodeData(data, &decodedLength); + lua_State* L = sEluna->L; + + if (decodedData) + { + // Stack: (empty) + + lua_pushcfunction(L, mar_decode); + lua_pushlstring(L, (const char*)decodedData, decodedLength); + // Stack: mar_decode, decoded_data + + // Call `mar_decode` and check for success. + if (lua_pcall(L, 1, 1, 0) == 0) + { + // Stack: data + // Only use the data if it's a table. + if (lua_istable(L, -1)) + { + sEluna->CreateInstanceData(instance); + // Stack: (empty) + sEluna->OnLoad(this); + // WARNING! lastSaveData might be different after `OnLoad` if the Lua code saved data. + } + else + { + ELUNA_LOG_ERROR("Error while loading instance data: Expected data to be a table (type 5), got type %d instead", lua_type(L, -1)); + lua_pop(L, 1); + // Stack: (empty) + +#if !defined ELUNA_TRINITY + Initialize(); +#endif + } + } + else + { + // Stack: error_message + ELUNA_LOG_ERROR("Error while parsing instance data with lua-marshal: %s", lua_tostring(L, -1)); + lua_pop(L, 1); + // Stack: (empty) + +#if !defined ELUNA_TRINITY + Initialize(); +#endif + } + + delete[] decodedData; + } + else + { + ELUNA_LOG_ERROR("Error while decoding instance data: Data is not valid base-64"); + +#if !defined ELUNA_TRINITY + Initialize(); +#endif + } +} + +const char* ElunaInstanceAI::Save() const +{ + lua_State* L = sEluna->L; + // Stack: (empty) + + /* + * Need to cheat because this method actually does modify this instance, + * even though it's declared as `const`. + * + * Declaring virtual methods as `const` is BAD! + * Don't dictate to children that their methods must be pure. + */ + ElunaInstanceAI* self = const_cast(this); + + lua_pushcfunction(L, mar_encode); + sEluna->PushInstanceData(self, false); + // Stack: mar_encode, instance_data + + if (lua_pcall(L, 1, 1, 0) != 0) + { + // Stack: error_message + ELUNA_LOG_ERROR("Error while saving: %s", lua_tostring(L, -1)); + lua_pop(L, 1); + return NULL; + } + + // Stack: data + size_t dataLength; + const unsigned char* data = (const unsigned char*)lua_tolstring(L, -1, &dataLength); + ElunaUtil::EncodeData(data, dataLength, self->lastSaveData); + + lua_pop(L, 1); + // Stack: (empty) + + return lastSaveData.c_str(); +} + +uint32 ElunaInstanceAI::GetData(uint32 key) const +{ + Eluna* E = sEluna; + lua_State* L = E->L; + // Stack: (empty) + + E->PushInstanceData(const_cast(this), false); + // Stack: instance_data + + E->Push(key); + // Stack: instance_data, key + + lua_gettable(L, -2); + // Stack: instance_data, value + + uint32 value = E->CHECKVAL(-1, 0); + lua_pop(L, 2); + // Stack: (empty) + + return value; +} + +void ElunaInstanceAI::SetData(uint32 key, uint32 value) +{ + Eluna* E = sEluna; + lua_State* L = E->L; + // Stack: (empty) + + E->PushInstanceData(this, false); + // Stack: instance_data + + E->Push(key); + E->Push(value); + // Stack: instance_data, key, value + + lua_settable(L, -3); + // Stack: instance_data + + lua_pop(L, 1); + // Stack: (empty) +} + +uint64 ElunaInstanceAI::GetData64(uint32 key) const +{ + Eluna* E = sEluna; + lua_State* L = E->L; + // Stack: (empty) + + E->PushInstanceData(const_cast(this), false); + // Stack: instance_data + + E->Push(key); + // Stack: instance_data, key + + lua_gettable(L, -2); + // Stack: instance_data, value + + uint64 value = E->CHECKVAL(-1, 0); + lua_pop(L, 2); + // Stack: (empty) + + return value; +} + +void ElunaInstanceAI::SetData64(uint32 key, uint64 value) +{ + Eluna* E = sEluna; + lua_State* L = E->L; + // Stack: (empty) + + E->PushInstanceData(this, false); + // Stack: instance_data + + E->Push(key); + E->Push(value); + // Stack: instance_data, key, value + + lua_settable(L, -3); + // Stack: instance_data + + lua_pop(L, 1); + // Stack: (empty) +} diff --git a/src/server/game/LuaEngine/ElunaInstanceAI.h b/src/server/game/LuaEngine/ElunaInstanceAI.h new file mode 100644 index 0000000000..133bb4b78a --- /dev/null +++ b/src/server/game/LuaEngine/ElunaInstanceAI.h @@ -0,0 +1,166 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNA_INSTANCE_DATA_H +#define _ELUNA_INSTANCE_DATA_H + +#include "LuaEngine.h" +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +#include "InstanceScript.h" +#include "Map.h" +#elif defined ELUNA_CMANGOS +#include "Maps/InstanceData.h" +#else +#include "InstanceData.h" +#endif + +/* + * This class is a small wrapper around `InstanceData`, + * allowing instances to be scripted with Eluna. + * + * + * Note 1 + * ====== + * + * Instances of `ElunaInstanceAI` are owned by the core, so they + * are not deleted when Eluna is reloaded. Thus `Load` is only called + * by the core once, no matter how many times Eluna is reloaded. + * + * However, when Eluna reloads, all instance data in Eluna is lost. + * So the solution is as follows: + * + * 1. Store the last save data in the member var `lastSaveData`. + * + * At first this is just the data given to us by the core when it calls `Load`, + * but later on once we start saving new data this is from Eluna. + * + * 2. When retrieving instance data from Eluna, check if it's missing. + * + * The data will be missing if Eluna is reloaded, since a new Lua state is created. + * + * 3. If it *is* missing, call `Reload`. + * + * This reloads the last known instance save data into Eluna, and calls the appropriate hooks. + * + * + * Note 2 + * ====== + * + * CMaNGOS expects some of these methods to be `const`. However, any of these + * methods are free to call `Save`, resulting in mutation of `lastSaveData`. + * + * Therefore, none of the hooks are `const`-safe, and `const_cast` is used + * to escape from these restrictions. + */ +class ElunaInstanceAI : public InstanceData +{ +private: + // The last save data to pass through this class, + // either through `Load` or `Save`. + std::string lastSaveData; + +public: +#if defined ELUNA_TRINITY + ElunaInstanceAI(Map* map) : InstanceData(map->ToInstanceMap()) + { + } +#else + ElunaInstanceAI(Map* map) : InstanceData(map) + { + } +#endif + +#if !defined ELUNA_TRINITY + void Initialize() override; +#endif + + /* + * These are responsible for serializing/deserializing the instance's + * data table to/from the core. + */ + void Load(const char* data) override; +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + // Simply calls Save, since the functions are a bit different in name and data types on different cores + std::string GetSaveData() override + { + return Save(); + } + const char* Save() const; +#elif defined ELUNA_VMANGOS + const char* Save() const; +#else + const char* Save() const override; +#endif + + + /* + * Calls `Load` with the last save data that was passed to + * or from Eluna. + * + * See: big documentation blurb at the top of this class. + */ + void Reload() + { + Load(NULL); + } + + /* + * These methods allow non-Lua scripts (e.g. DB, C++) to get/set instance data. + */ +#if !defined ELUNA_VMANGOS + uint32 GetData(uint32 key) const override; +#else + uint32 GetData(uint32 key) const; +#endif + void SetData(uint32 key, uint32 value) override; + +#if !defined ELUNA_VMANGOS + uint64 GetData64(uint32 key) const override; +#else + uint64 GetData64(uint32 key) const; +#endif + void SetData64(uint32 key, uint64 value) override; + + /* + * These methods are just thin wrappers around Eluna. + */ + void Update(uint32 diff) override + { + // If Eluna is reloaded, it will be missing our instance data. + // Reload here instead of waiting for the next hook call (possibly never). + // This avoids having to have an empty Update hook handler just to trigger the reload. + if (!sEluna->HasInstanceData(instance)) + Reload(); + + sEluna->OnUpdateInstance(this, diff); + } + + bool IsEncounterInProgress() const override + { + return sEluna->OnCheckEncounterInProgress(const_cast(this)); + } + + void OnPlayerEnter(Player* player) override + { + sEluna->OnPlayerEnterInstance(this, player); + } + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + void OnGameObjectCreate(GameObject* gameobject) override +#else + void OnObjectCreate(GameObject* gameobject) override +#endif + { + sEluna->OnGameObjectCreate(this, gameobject); + } + + void OnCreatureCreate(Creature* creature) override + { + sEluna->OnCreatureCreate(this, creature); + } +}; + +#endif // _ELUNA_INSTANCE_DATA_H diff --git a/src/server/game/LuaEngine/ElunaLoader.cpp b/src/server/game/LuaEngine/ElunaLoader.cpp new file mode 100644 index 0000000000..210e04c508 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaLoader.cpp @@ -0,0 +1,309 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* Copyright (C) 2022 - 2022 Hour of Twilight +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ElunaCompat.h" +#include "ElunaConfig.h" +#include "ElunaLoader.h" +#include "ElunaUtility.h" +#include "LuaEngine.h" +#include +#include +#include +#include + +// [恢复]: 强制声明全局唯一引擎 sEluna +extern class Eluna* sEluna; + +#if defined USING_BOOST +#include +namespace fs = boost::filesystem; +#else +#include +namespace fs = std::filesystem; +#endif + +#if defined ELUNA_WINDOWS +#include +#endif + +#if defined ELUNA_TRINITY || ELUNA_MANGOS +#include "MapManager.h" +#elif defined ELUNA_CMANGOS +#include "Maps/MapManager.h" +#endif + +extern "C" { +#include +#include +#include +} + +ElunaLoader::ElunaLoader() : m_cacheState(SCRIPT_CACHE_NONE) +{ +} + +ElunaLoader* ElunaLoader::instance() +{ + static ElunaLoader instance; + return &instance; +} + +ElunaLoader::~ElunaLoader() +{ + if (m_reloadThread.joinable()) + m_reloadThread.join(); +} + +void ElunaLoader::ReloadScriptCache() +{ + // [关键修复]:如果是第一次启动,直接强行“同步加载”,确保把文件读完再继续! + if (m_cacheState == SCRIPT_CACHE_NONE) + { + LoadScripts(); + return; + } + + if (m_cacheState != SCRIPT_CACHE_READY) + { + ELUNA_LOG_DEBUG("[Eluna]: Script cache not ready, skipping reload"); + return; + } + + if (m_reloadThread.joinable()) + m_reloadThread.join(); + + m_cacheState = SCRIPT_CACHE_REINIT; + + m_reloadThread = std::thread(&ElunaLoader::LoadScripts, this); + ELUNA_LOG_DEBUG("[Eluna]: Script cache reload thread started"); +} + +void ElunaLoader::LoadScripts() +{ + if (m_cacheState != SCRIPT_CACHE_REINIT && m_cacheState != SCRIPT_CACHE_NONE) + return; + + m_cacheState = SCRIPT_CACHE_LOADING; + + uint32 oldMSTime = ElunaUtil::GetCurrTime(); + + std::string lua_folderpath = sElunaConfig->GetConfig(CONFIG_ELUNA_SCRIPT_PATH); + const std::string& lua_path_extra = sElunaConfig->GetConfig(CONFIG_ELUNA_REQUIRE_PATH_EXTRA); + const std::string& lua_cpath_extra = sElunaConfig->GetConfig(CONFIG_ELUNA_REQUIRE_CPATH_EXTRA); + +#if !defined ELUNA_WINDOWS + if (lua_folderpath[0] == '~') + if (const char* home = getenv("HOME")) + lua_folderpath.replace(0, 1, home); +#endif + + printf("\n======================================================\n"); + printf("[Eluna 物理打印]: 正在搜索此目录下的 Lua 脚本: %s\n", lua_folderpath.c_str()); + printf("======================================================\n"); + + lua_State* L = luaL_newstate(); + luaL_openlibs(L); + + m_requirePath.clear(); + m_requirecPath.clear(); + + ReadFiles(L, lua_folderpath); + + lua_close(L); + + CombineLists(); + + if (!lua_path_extra.empty()) + m_requirePath += lua_path_extra; + + if (!lua_cpath_extra.empty()) + m_requirecPath += lua_cpath_extra; + + if (!m_requirePath.empty()) + m_requirePath.erase(m_requirePath.end() - 1); + + if (!m_requirecPath.empty()) + m_requirecPath.erase(m_requirecPath.end() - 1); + + printf("[Eluna 物理打印]: 成功加载并编译了 %u 个脚本!耗时: %u 毫秒\n", uint32(m_scriptCache.size()), ElunaUtil::GetTimeDiff(oldMSTime)); + printf("======================================================\n\n"); + + m_cacheState = SCRIPT_CACHE_READY; +} + +int ElunaLoader::LoadBytecodeChunk(lua_State* /*L*/, uint8* bytes, size_t len, BytecodeBuffer* buffer) +{ + buffer->insert(buffer->end(), bytes, bytes + len); + return 0; +} + +void ElunaLoader::ReadFiles(lua_State* L, std::string path) +{ + std::string lua_folderpath = sElunaConfig->GetConfig(CONFIG_ELUNA_SCRIPT_PATH); + + ELUNA_LOG_DEBUG("[Eluna]: ReadFiles from path `%s`", path.c_str()); + + fs::path someDir(path); + fs::directory_iterator end_iter; + + if (fs::exists(someDir) && fs::is_directory(someDir) && !fs::is_empty(someDir)) + { + m_requirePath += + path + "/?.lua;" + + path + "/?.ext;" + + path + "/?.moon;"; + + m_requirecPath += + path + "/?.dll;" + + path + "/?.so;"; + + for (fs::directory_iterator dir_iter(someDir); dir_iter != end_iter; ++dir_iter) + { + std::string fullpath = dir_iter->path().generic_string(); +#if defined ELUNA_WINDOWS + DWORD dwAttrib = GetFileAttributes(fullpath.c_str()); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_HIDDEN)) + continue; +#else + std::string name = dir_iter->path().filename().generic_string().c_str(); + if (name[0] == '.') + continue; +#endif + + if (fs::is_directory(dir_iter->status())) + { + ReadFiles(L, fullpath); + continue; + } + + if (fs::is_regular_file(dir_iter->status())) + { + int32 mapId = -1; + + std::string subfolder = dir_iter->path().generic_string(); + subfolder = subfolder.erase(0, lua_folderpath.size() + 1); + + auto [ptr, ec] = std::from_chars(subfolder.data(), subfolder.data() + subfolder.size(), mapId); + + if (ec == std::errc::invalid_argument || ec == std::errc::result_out_of_range || mapId < -1) + mapId = -1; + + std::string filename = dir_iter->path().filename().generic_string(); + size_t filesize = fs::file_size(dir_iter->path()); + ProcessScript(L, filename, filesize, fullpath, mapId); + } + } + } +} + +bool ElunaLoader::CompileScript(lua_State* L, LuaScript& script) +{ + int err = 0; + if (script.fileext == ".moon") + { + std::string str = "return require('moonscript').loadfile([[" + script.filepath+ "]])"; + err = luaL_dostring(L, str.c_str()); + } else + err = luaL_loadfile(L, script.filepath.c_str()); + + if (err != 0) + { + ELUNA_LOG_ERROR("[Eluna]: CompileScript failed to load the Lua script `%s`.", script.filename.c_str()); + Eluna::Report(L); + return false; + } + ELUNA_LOG_DEBUG("[Eluna]: CompileScript loaded Lua script `%s`", script.filename.c_str()); + + err = lua_dump(L, (lua_Writer)LoadBytecodeChunk, &script.bytecode); + if (err || script.bytecode.empty()) + { + ELUNA_LOG_ERROR("[Eluna]: CompileScript failed to dump the Lua script `%s` to bytecode.", script.filename.c_str()); + Eluna::Report(L); + return false; + } + ELUNA_LOG_DEBUG("[Eluna]: CompileScript dumped Lua script `%s` to bytecode.", script.filename.c_str()); + + lua_pop(L, 1); + return true; +} + +void ElunaLoader::ProcessScript(lua_State* L, std::string filename, const size_t& filesize, const std::string& fullpath, int32 mapId) +{ + ELUNA_LOG_DEBUG("[Eluna]: ProcessScript checking file `%s`", fullpath.c_str()); + + std::size_t extDot = filename.find_last_of('.'); + if (extDot == std::string::npos) + return; + std::string ext = filename.substr(extDot); + filename = filename.substr(0, extDot); + + if (ext != ".lua" && ext != ".ext" && ext != ".moon") + return; + bool extension = ext == ".ext"; + + LuaScript script; + script.fileext = ext; + script.filename = filename; + script.filepath = fullpath; + script.modulepath = fullpath.substr(0, fullpath.length() - filename.length() - ext.length()); + script.bytecode.reserve(filesize); + script.mapId = mapId; + + if (!CompileScript(L, script)) + return; + + if (extension) + m_extensions.push_back(script); + else + m_scripts.push_back(script); + + ELUNA_LOG_DEBUG("[Eluna]: ProcessScript processed `%s` successfully", fullpath.c_str()); +} + +static bool ScriptPathComparator(const LuaScript& first, const LuaScript& second) +{ + return first.filepath < second.filepath; +} + +void ElunaLoader::CombineLists() +{ + m_extensions.sort(ScriptPathComparator); + m_scripts.sort(ScriptPathComparator); + + m_scriptCache.clear(); + m_scriptCache.reserve(m_extensions.size() + m_scripts.size()); + + std::move(m_extensions.begin(), m_extensions.end(), std::back_inserter(m_scriptCache)); + std::move(m_scripts.begin(), m_scripts.end(), std::back_inserter(m_scriptCache)); + + m_extensions.clear(); + m_scripts.clear(); +} + +void ElunaLoader::ReloadElunaForMap(int mapId) +{ + ReloadScriptCache(); + + if (mapId != RELOAD_CACHE_ONLY) + { + if (mapId == RELOAD_GLOBAL_STATE || mapId == RELOAD_ALL_STATES) + if (Eluna* e = sEluna) // [恢复]: 用 sEluna 替换 sWorld->GetEluna() + e->ReloadEluna(); + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + sMapMgr->DoForAllMaps([&](Map* map) +#else + sMapMgr.DoForAllMaps([&](Map* map) +#endif + { + if (mapId == RELOAD_ALL_STATES || mapId == static_cast(map->GetId())) + if (Eluna* e = sEluna) // [恢复]: 用 sEluna 替换 map->GetEluna() + e->ReloadEluna(); + } + ); + } +} \ No newline at end of file diff --git a/src/server/game/LuaEngine/ElunaLoader.h b/src/server/game/LuaEngine/ElunaLoader.h new file mode 100644 index 0000000000..5f4c3c79dc --- /dev/null +++ b/src/server/game/LuaEngine/ElunaLoader.h @@ -0,0 +1,84 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* Copyright (C) 2022 - 2022 Hour of Twilight +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNALOADER_H +#define _ELUNALOADER_H + +#include "LuaEngine.h" + +#if defined ELUNA_TRINITY + +#endif + +extern "C" +{ +#include "lua.h" +}; + +enum ElunaReloadActions +{ + RELOAD_CACHE_ONLY = -3, + RELOAD_ALL_STATES = -2, + RELOAD_GLOBAL_STATE = -1 +}; + +enum ElunaScriptCacheState +{ + SCRIPT_CACHE_NONE = 0, + SCRIPT_CACHE_REINIT = 1, + SCRIPT_CACHE_LOADING = 2, + SCRIPT_CACHE_READY = 3 +}; + +struct LuaScript; + +class ElunaLoader +{ +private: + ElunaLoader(); + ~ElunaLoader(); + +public: + ElunaLoader(ElunaLoader const&) = delete; + ElunaLoader(ElunaLoader&&) = delete; + + ElunaLoader& operator= (ElunaLoader const&) = delete; + ElunaLoader& operator= (ElunaLoader&&) = delete; + static ElunaLoader* instance(); + + void LoadScripts(); + void ReloadElunaForMap(int mapId); + + uint8 GetCacheState() const { return m_cacheState; } + const std::vector& GetLuaScripts() const { return m_scriptCache; } + const std::string& GetRequirePath() const { return m_requirePath; } + const std::string& GetRequireCPath() const { return m_requirecPath; } + + + +private: + void ReloadScriptCache(); + void ReadFiles(lua_State* L, std::string path); + void CombineLists(); + void ProcessScript(lua_State* L, std::string filename, const size_t& filesize, const std::string& fullpath, int32 mapId); + bool CompileScript(lua_State* L, LuaScript& script); + static int LoadBytecodeChunk(lua_State* L, uint8* bytes, size_t len, BytecodeBuffer* buffer); + + std::atomic m_cacheState; + std::vector m_scriptCache; + std::string m_requirePath; + std::string m_requirecPath; + std::list m_scripts; + std::list m_extensions; + std::thread m_reloadThread; +}; + + + +#define sElunaLoader ElunaLoader::instance() + +#endif diff --git a/src/server/game/LuaEngine/ElunaMgr.cpp b/src/server/game/LuaEngine/ElunaMgr.cpp new file mode 100644 index 0000000000..2ddddc7772 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaMgr.cpp @@ -0,0 +1,88 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ElunaMgr.h" +#include "LuaEngine.h" + +ElunaMgr::ElunaMgr() +{ +} + +ElunaMgr* ElunaMgr::instance() +{ + static ElunaMgr instance; + return &instance; +} + +ElunaMgr::~ElunaMgr() +{ +} + +void ElunaMgr::Create(Map* map, ElunaInfo const& info) +{ + // If already exists, do nothing + bool keyExists = info.IsValid() && (_elunaMap.find(info.key) != _elunaMap.end()); + if (keyExists) + return; + + _elunaMap.emplace(info.key, std::make_unique(map)); +} + +Eluna* ElunaMgr::Get(ElunaInfoKey key) const +{ + auto it = _elunaMap.find(key); + if (it != _elunaMap.end()) + return it->second.get(); + + return nullptr; +} + +Eluna* ElunaMgr::Get(ElunaInfo const& info) const +{ + return Get(info.key); +} + +void ElunaMgr::Destroy(ElunaInfoKey key) +{ + _elunaMap.erase(key); +} + +void ElunaMgr::Destroy(ElunaInfo const& info) +{ + Destroy(info.key); +} + +ElunaInfo::~ElunaInfo() +{ +} + +bool ElunaInfo::IsValid() const +{ + return key.IsValid(); +} + +bool ElunaInfo::IsGlobal() const +{ + return key.IsGlobal(); +} + +uint32 ElunaInfo::GetMapId() const +{ + return key.GetMapId(); +} + +uint32 ElunaInfo::GetInstanceId() const +{ + return key.GetInstanceId(); +} + +Eluna* ElunaInfo::GetEluna() const +{ + if (IsValid() && sElunaMgr) + return sElunaMgr->Get(key); + + return nullptr; +} diff --git a/src/server/game/LuaEngine/ElunaMgr.h b/src/server/game/LuaEngine/ElunaMgr.h new file mode 100644 index 0000000000..97063462d0 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaMgr.h @@ -0,0 +1,111 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNAMGR_H +#define _ELUNAMGR_H + +#include "Common.h" + +#include +#include +#include + +class Eluna; +class Map; + +struct ElunaInfoKey +{ +public: + constexpr ElunaInfoKey() : value(INVALID_KEY_VALUE) {} + constexpr ElunaInfoKey(uint64 key) : value(key) {} + constexpr ElunaInfoKey(uint32 mapId, uint32 instanceId) : value(instanceId | (static_cast(mapId) << 32)) {} + + constexpr bool operator==(const ElunaInfoKey& other) const { return value == other.value; } + constexpr bool operator!=(const ElunaInfoKey& other) const { return !(*this == other); } + +public: + static constexpr ElunaInfoKey MakeKey(uint32 mapId, uint32 instanceId) + { + return ElunaInfoKey(instanceId | (static_cast(mapId) << 32)); + } + static constexpr ElunaInfoKey MakeGlobalKey(uint32 instanceId) + { + return MakeKey(GLOBAL_MAP_ID, instanceId); + } + + constexpr bool IsValid() const { return value != INVALID_KEY_VALUE; }; + constexpr bool IsGlobal() const { return IsValid() && GetMapId() == GLOBAL_MAP_ID; }; + + constexpr uint32 GetMapId() const { return value >> 32; }; + constexpr uint32 GetInstanceId() const { return value & 0xFFFFFFFF; }; + +public: + static uint64 const INVALID_KEY_VALUE = std::numeric_limits().max(); + static uint32 const GLOBAL_MAP_ID = std::numeric_limits().max(); + + uint64 value; +}; + +// Specialize ElunaInfoKey for use in unordered_map +namespace std +{ + template <> + struct hash + { + size_t operator()(const ElunaInfoKey& key) const + { + return key.value; + } + }; +} + +struct ElunaInfo +{ +public: + ElunaInfo() : key() {} + ElunaInfo(ElunaInfoKey key) : key(key) {} + ~ElunaInfo(); + +public: + bool IsValid() const; + bool IsGlobal() const; + + uint32 GetMapId() const; + uint32 GetInstanceId() const; + + // Getter to fetch Eluna object + Eluna* GetEluna() const; + +public: + ElunaInfoKey key; +}; + +class ElunaMgr +{ +private: + ElunaMgr(); + ~ElunaMgr(); + ElunaMgr(ElunaMgr const&) = delete; + ElunaMgr& operator=(ElunaMgr const&) = delete; + +public: + static ElunaMgr* instance(); + + void Create(Map* map, ElunaInfo const& info); + + Eluna* Get(ElunaInfoKey key) const; + Eluna* Get(ElunaInfo const& info) const; + + void Destroy(ElunaInfoKey key); + void Destroy(ElunaInfo const& info); + +private: + std::unordered_map> _elunaMap; +}; + +#define sElunaMgr ElunaMgr::instance() + +#endif //_ELUNAMGR_H diff --git a/src/server/game/LuaEngine/ElunaSpellWrapper.cpp b/src/server/game/LuaEngine/ElunaSpellWrapper.cpp new file mode 100644 index 0000000000..db778adc26 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaSpellWrapper.cpp @@ -0,0 +1,132 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ +#include "ElunaSpellWrapper.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" +#include +ElunaProcInfo::ElunaProcInfo(Unit* actor, Unit* actionTarget, uint32 typeMask, + uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, + Spell* spell, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, Map* map) + : _actor(actor), _actionTarget(actionTarget), _typeMask(typeMask), _spellTypeMask(spellTypeMask), _spellPhaseMask(spellPhaseMask) + , _hitMask(hitMask), _spell(spell), _spellInfo(spellInfo), _schoolMask(schoolMask), _damage(0) + , _damageType(DIRECT_DAMAGE), _attackType(BASE_ATTACK), _damageAbsorb(0), _resist(0), _block(0) + , _heal(0), _effectiveHeal(0), _healAbsorb(0), _map(map) +#ifdef TRACKABLE_PTR_NAMESPACE + , m_scriptRef(this, NoopAuraDeleter()) +#endif +{ +} + +ElunaProcInfo::ElunaProcInfo(ProcEventInfo& procInfo, Map* map) + : _actor(procInfo.GetActor()), _actionTarget(procInfo.GetActionTarget()), _typeMask(procInfo.GetTypeMask()), _spellTypeMask(procInfo.GetSpellTypeMask()), _spellPhaseMask(procInfo.GetSpellPhaseMask()) + , _hitMask(procInfo.GetHitMask()), _spell(const_cast(procInfo.GetProcSpell())), _spellInfo(procInfo.GetSpellInfo()), _schoolMask(procInfo.GetSchoolMask()), _damage(0) + , _damageType(DIRECT_DAMAGE), _attackType(BASE_ATTACK), _damageAbsorb(0), _resist(0), _block(0) + , _heal(0), _effectiveHeal(0), _healAbsorb(0), _map(map) +#ifdef TRACKABLE_PTR_NAMESPACE + , m_scriptRef(this, NoopAuraDeleter()) +#endif +{ + if (DamageInfo* damageInfo = procInfo.GetDamageInfo()) + { + _damage = damageInfo->GetDamage(); + _damageType = damageInfo->GetDamageType(); + _attackType = damageInfo->GetAttackType(); + _damageAbsorb = damageInfo->GetAbsorb(); + _resist = damageInfo->GetResist(); + _block = damageInfo->GetBlock(); + + if (damageInfo->GetSpellInfo()) + { + _spellInfo = damageInfo->GetSpellInfo(); + _schoolMask = damageInfo->GetSchoolMask(); + } + } + + if (HealInfo* healInfo = procInfo.GetHealInfo()) + { + _heal = healInfo->GetHeal(); + _effectiveHeal = healInfo->GetEffectiveHeal(); + _healAbsorb = healInfo->GetAbsorb(); + + if (healInfo->GetSpellInfo() && !_spellInfo) + { + _spellInfo = healInfo->GetSpellInfo(); + _schoolMask = healInfo->GetSchoolMask(); + } + } +} + +SpellInfo const* ElunaProcInfo::GetSpellInfo() const +{ + if (_spellInfo) + return _spellInfo; + if (_spell) + return _spell->GetSpellInfo(); + return nullptr; +} + +void ElunaProcInfo::SetDamage(uint32 damage, DamageEffectType damageType, WeaponAttackType attackType) +{ + _damage = damage; + _damageType = damageType; + _attackType = attackType; +} + +void ElunaProcInfo::SetHeal(uint32 heal) +{ + _heal = heal; + _effectiveHeal = heal; +} + +void ElunaProcInfo::ApplyToProcEventInfo(ProcEventInfo& procInfo) const +{ + if (DamageInfo* damageInfo = procInfo.GetDamageInfo()) + { + if (HasDamage()) + { + int32 damageDiff = static_cast(_damage) - static_cast(damageInfo->GetDamage()); + if (damageDiff != 0) + damageInfo->ModifyDamage(damageDiff); + + uint32 currentAbsorb = damageInfo->GetAbsorb(); + uint32 currentResist = damageInfo->GetResist(); + uint32 currentBlock = damageInfo->GetBlock(); + + uint32 absorbToAdd = (_damageAbsorb > currentAbsorb) ? (_damageAbsorb - currentAbsorb) : 0; + uint32 resistToAdd = (_resist > currentResist) ? (_resist - currentResist) : 0; + uint32 blockToAdd = (_block > currentBlock) ? (_block - currentBlock) : 0; + + if (absorbToAdd > 0) + damageInfo->AbsorbDamage(absorbToAdd); + if (resistToAdd > 0) + damageInfo->ResistDamage(resistToAdd); + if (blockToAdd > 0) + damageInfo->BlockDamage(blockToAdd); + } + } + + if (HealInfo* healInfo = procInfo.GetHealInfo()) + { + if (HasHeal()) + { + uint32 currentAbsorb = healInfo->GetAbsorb(); + uint32 absorbToAdd = (_healAbsorb > currentAbsorb) ? (_healAbsorb - currentAbsorb) : 0; + + if (absorbToAdd > 0) + healInfo->AbsorbHeal(absorbToAdd); + + healInfo->SetEffectiveHeal(_effectiveHeal); + } + } +} + +ElunaSpellInfo::ElunaSpellInfo(uint32 spellId) : _spellInfo(sSpellMgr->GetSpellInfo(spellId)) +{ +#ifdef ELUNA_TRINITY + if (_spellInfo) + m_scriptRef = std::shared_ptr(this, NoopAuraDeleter()); +#endif +} diff --git a/src/server/game/LuaEngine/ElunaSpellWrapper.h b/src/server/game/LuaEngine/ElunaSpellWrapper.h new file mode 100644 index 0000000000..6a07241859 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaSpellWrapper.h @@ -0,0 +1,145 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ +#include + +#ifndef _ELUNA_PROCINFO_H +#define _ELUNA_PROCINFO_H +#include "LuaEngine.h" +class Unit; +class Spell; +class Map; +class SpellInfo; +class ProcEventInfo; +class DamageInfo; +class HealInfo; +#ifdef ELUNA_TRINITY +enum SpellSchoolMask : uint16; +#else +enum SpellSchoolMask; +#endif +enum DamageEffectType : uint8; +enum WeaponAttackType : uint8; +#ifdef ELUNA_TRINITY +namespace Trinity +{ + template + class unique_trackable_ptr; + + template + class unique_weak_ptr; +} +#endif + +class ElunaProcInfo +{ +private: + Unit* _actor; + Unit* _actionTarget; + uint32 _typeMask; + uint32 _spellTypeMask; + uint32 _spellPhaseMask; + uint32 _hitMask; + Spell* _spell; + + SpellInfo const* _spellInfo; + SpellSchoolMask _schoolMask; + + uint32 _damage; + DamageEffectType _damageType; + WeaponAttackType _attackType; + uint32 _damageAbsorb; + uint32 _resist; + uint32 _block; + + uint32 _heal; + uint32 _effectiveHeal; + uint32 _healAbsorb; + Map* _map; + + struct NoopAuraDeleter { void operator()(ElunaProcInfo*) const { } }; +#ifdef ELUNA_TRINITY + std::shared_ptr m_scriptRef; +#endif + +public: + ElunaProcInfo(Unit* actor, Unit* actionTarget, uint32 typeMask, + uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, + Spell* spell, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, Map* map); + + explicit ElunaProcInfo(ProcEventInfo& procInfo, Map* map); + ~ElunaProcInfo() + { +#ifdef TRACKABLE_PTR_NAMESPACE + m_scriptRef = nullptr; +#endif + } + Unit* GetActor() const { return _actor; } + Unit* GetActionTarget() const { return _actionTarget; } + uint32 GetTypeMask() const { return _typeMask; } + uint32 GetSpellTypeMask() const { return _spellTypeMask; } + uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } + uint32 GetHitMask() const { return _hitMask; } + Spell const* GetProcSpell() const { return _spell; } + + SpellInfo const* GetSpellInfo() const; + SpellSchoolMask GetSchoolMask() const { return _schoolMask; } + + uint32 GetDamage() const { return _damage; } + DamageEffectType GetDamageType() const { return _damageType; } + WeaponAttackType GetAttackType() const { return _attackType; } + uint32 GetDamageAbsorb() const { return _damageAbsorb; } + uint32 GetResist() const { return _resist; } + uint32 GetBlock() const { return _block; } + + uint32 GetHeal() const { return _heal; } + uint32 GetEffectiveHeal() const { return _effectiveHeal; } + uint32 GetHealAbsorb() const { return _healAbsorb; } + + bool HasDamage() const { return _damage > 0; } + bool HasHeal() const { return _heal > 0; } + + void SetDamage(int32 amount) { _damage = amount; } + void SetAbsorbDamage(uint32 amount) { _damageAbsorb = amount; } + void SetResistDamage(uint32 amount) { _resist = amount; } + void SetBlockDamage(uint32 amount) { _block = amount; } + + void SetAbsorbHeal(uint32 amount) { _healAbsorb = amount; } + void SetEffectiveHeal(uint32 amount) { _effectiveHeal = amount; } + void SetHeal(int32 amount) { _heal = amount; } + + void SetDamage(uint32 damage, DamageEffectType damageType, WeaponAttackType attackType); + void SetHeal(uint32 heal); + + const Map* GetMap() const { return _map; } +#ifdef ELUNA_TRINITY + std::weak_ptr GetWeakPtr() const { return m_scriptRef; } +#endif + void ApplyToProcEventInfo(ProcEventInfo& procInfo) const; +}; + +class ElunaSpellInfo +{ +private: + SpellInfo const* _spellInfo; + struct NoopAuraDeleter { void operator()(ElunaSpellInfo*) const {} }; +#ifdef ELUNA_TRINITY + std::shared_ptr m_scriptRef; +#endif +public: + ElunaSpellInfo(uint32 spellId); + ~ElunaSpellInfo() + { +#ifdef TRACKABLE_PTR_NAMESPACE + m_scriptRef = nullptr; +#endif + } + SpellInfo const* GetSpellInfo() const { return _spellInfo; } +#ifdef ELUNA_TRINITY + std::weak_ptr GetWeakPtr() const { return m_scriptRef; } +#endif +}; + +#endif diff --git a/src/server/game/LuaEngine/ElunaTemplate.cpp b/src/server/game/LuaEngine/ElunaTemplate.cpp new file mode 100644 index 0000000000..3d565976ad --- /dev/null +++ b/src/server/game/LuaEngine/ElunaTemplate.cpp @@ -0,0 +1,58 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +// Eluna +#include "LuaEngine.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" +#include "ElunaUtility.h" + +#if defined TRACKABLE_PTR_NAMESPACE +ElunaConstrainedObjectRef GetWeakPtrFor(Aura const* obj) +{ +#if defined ELUNA_TRINITY + Map* map = obj->GetOwner()->GetMap(); +#elif defined ELUNA_CMANGOS + Map* map = obj->GetTarget()->GetMap(); +#endif + return { const_cast(obj), map }; +} + +ElunaConstrainedObjectRef GetWeakPtrFor(AuraEffect const* obj) +{ + Map* map = obj->GetBase()->GetOwner()->GetMap(); + return { const_cast(obj), map }; +} + +ElunaConstrainedObjectRef GetWeakPtrFor(ElunaProcInfo const* obj) +{ + return { const_cast(obj), obj->GetMap()}; +} +ElunaConstrainedObjectRef GetWeakPtrFor(BattleGround const* obj) { return { const_cast(obj), obj->GetBgMap() }; } +ElunaConstrainedObjectRef GetWeakPtrFor(Group const* obj) { return { const_cast(obj), nullptr }; } +ElunaConstrainedObjectRef GetWeakPtrFor(Guild const* obj) { return { const_cast(obj), nullptr }; } +ElunaConstrainedObjectRef GetWeakPtrFor(Map const* obj) { return { const_cast(obj), obj }; } + +ElunaConstrainedObjectRef GetWeakPtrForObjectImpl(Object const* obj) +{ + // [关键修改]: 使用 dynamic_cast 绕过 7.3.5 缺失的宏和方法 + if (WorldObject const* worldObj = dynamic_cast(obj)) + return { const_cast(obj), worldObj->GetMap() }; + + if (obj->GetTypeId() == TYPEID_ITEM) + if (Player const* player = static_cast(obj)->GetOwner()) + return { const_cast(obj), player->GetMap() }; + + // possibly dangerous item + return { const_cast(obj), nullptr }; +} + +ElunaConstrainedObjectRef GetWeakPtrFor(Quest const* obj) { return { const_cast(obj), nullptr }; } +ElunaConstrainedObjectRef GetWeakPtrFor(Spell const* obj) { return { const_cast(obj), obj->GetCaster()->GetMap() }; } +ElunaConstrainedObjectRef GetWeakPtrFor(ElunaSpellInfo const* obj) { return { const_cast(obj), nullptr }; } + + +#endif \ No newline at end of file diff --git a/src/server/game/LuaEngine/ElunaTemplate.h b/src/server/game/LuaEngine/ElunaTemplate.h new file mode 100644 index 0000000000..10bcfbdcb8 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaTemplate.h @@ -0,0 +1,476 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNA_TEMPLATE_H +#define _ELUNA_TEMPLATE_H + +extern "C" +{ +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +}; +#include "LuaEngine.h" +#include "ElunaUtility.h" +#include "ElunaCompat.h" +#include "ElunaConfig.h" +#include "ElunaSpellWrapper.h" +#if !defined ELUNA_CMANGOS +#include "SharedDefines.h" +#else +#include "Globals/SharedDefines.h" +#include "Util/UniqueTrackablePtr.h" +#endif + +#if defined ELUNA_TRINITY +#include +#endif + +class ElunaObject +{ +public: + ElunaObject(Eluna* E, char const* tname) : E(E), type_name(tname) + { + } + + virtual ~ElunaObject() + { + } + + // Get wrapped object pointer + virtual void* GetObjIfValid() const = 0; + // Returns pointer to the wrapped object's type name + const char* GetTypeName() const { return type_name; } +#if !defined TRACKABLE_PTR_NAMESPACE + // Invalidates the pointer if it should be invalidated + virtual void Invalidate() = 0; +#endif + +protected: + Eluna* E; + const char* type_name; +}; + +#if defined TRACKABLE_PTR_NAMESPACE +template +struct ElunaConstrainedObjectRef +{ + T* Obj; // [关键修改]: 将隐藏在宏后面的 unique_weak_ptr 降级为最原始的裸指针 T* + Map const* BoundMap = nullptr; +}; + +ElunaConstrainedObjectRef GetWeakPtrFor(Aura const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(AuraEffect const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(ElunaProcInfo const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(BattleGround const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(Group const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(Guild const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(Map const* obj); +ElunaConstrainedObjectRef GetWeakPtrForObjectImpl(Object const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(Quest const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(Spell const* obj); +ElunaConstrainedObjectRef GetWeakPtrFor(ElunaSpellInfo const* obj); + + +template +ElunaConstrainedObjectRef GetWeakPtrFor(T const* obj) +{ + ElunaConstrainedObjectRef ref = GetWeakPtrForObjectImpl(obj); + // [关键修改]: 去掉 static_pointer_cast 智能指针转换,改为原生的 static_cast + return { static_cast(ref.Obj), ref.BoundMap }; +} + +#endif + +template +class ElunaObjectImpl : public ElunaObject +{ +public: +#if defined TRACKABLE_PTR_NAMESPACE + ElunaObjectImpl(Eluna* E, T const* obj, char const* tname) : ElunaObject(E, tname), _obj(const_cast(obj)) + { + } + + void* GetObjIfValid() const override + { + // 直接返回裸指针,无视任何地图判定和安全拦截! + + return _obj; + } +#else + ElunaObjectImpl(Eluna* E, T* obj, char const* tname) : ElunaObject(E, tname), _obj(obj), callstackid(E->GetCallstackId()) + { + } + + void* GetObjIfValid() const override + { + if (callstackid == E->GetCallstackId()) + return _obj; + + return nullptr; + } + + void Invalidate() override { callstackid = 1; } +#endif + +private: +#if defined TRACKABLE_PTR_NAMESPACE + T* _obj; // [关键修改]:从结构体改回纯纯的裸指针 +#else + void* _obj; + uint64 callstackid; +#endif +}; + +template +class ElunaObjectValueImpl : public ElunaObject +{ +public: + ElunaObjectValueImpl(Eluna* E, T const* obj, char const* tname) : ElunaObject(E, tname), _obj(*obj /*always a copy, what gets passed here might be pointing to something not owned by us*/) + { + } + + void* GetObjIfValid() const override { return const_cast(&_obj); } + +#if !defined TRACKABLE_PTR_NAMESPACE + void Invalidate() override { } +#endif + +private: + T _obj; +}; + +#define MAKE_ELUNA_OBJECT_VALUE_IMPL(type) \ +template <> \ +class ElunaObjectImpl : public ElunaObjectValueImpl \ +{ \ +public: \ + using ElunaObjectValueImpl::ElunaObjectValueImpl; \ +} + +MAKE_ELUNA_OBJECT_VALUE_IMPL(long long); +MAKE_ELUNA_OBJECT_VALUE_IMPL(unsigned long long); +MAKE_ELUNA_OBJECT_VALUE_IMPL(ObjectGuid); +MAKE_ELUNA_OBJECT_VALUE_IMPL(WorldPacket); +MAKE_ELUNA_OBJECT_VALUE_IMPL(ElunaQuery); + +template +struct ElunaRegister +{ + const char* name; + typename std::conditional, int(*)(Eluna*), int(*)(Eluna*, T*)>::type mfunc; + MethodRegisterState regState; + MethodFlags flags; + + // constructor for class methods + ElunaRegister(const char* name, int(*func)(Eluna*, T*), MethodRegisterState state = METHOD_REG_ALL, uint32 flags = METHOD_FLAG_NONE) + : name(name), mfunc(func), regState(state), flags(static_cast(flags)) {} + + // constructor for global methods + ElunaRegister(const char* name, int(*func)(Eluna*), MethodRegisterState state = METHOD_REG_ALL, uint32 flags = METHOD_FLAG_NONE) + : name(name), mfunc(func), regState(state), flags(static_cast(flags)) {} + + // constructor for unimplemented methods + ElunaRegister(const char* name, MethodRegisterState state = METHOD_REG_NONE, uint32 flags = METHOD_FLAG_NONE) + : name(name), mfunc(nullptr), regState(state), flags(static_cast(flags)) {} +}; + +template +class ElunaTemplate +{ +public: + static const char* tname; + + static void Register(Eluna* E, const char* name) + { + ASSERT(E); + ASSERT(name); + + lua_State* L = E->L; + + lua_getglobal(L, name); + ASSERT(lua_isnoneornil(L, -1)); + + lua_pop(L, 1); + + tname = name; + + luaL_newmetatable(L, tname); + int metatable = lua_gettop(L); + + lua_pushvalue(L, metatable); + lua_setglobal(L, tname); + + lua_pushcfunction(L, ToString); + lua_setfield(L, metatable, "__tostring"); + + lua_pushcfunction(L, CollectGarbage); + lua_setfield(L, metatable, "__gc"); + + lua_pushvalue(L, metatable); + lua_setfield(L, metatable, "__index"); + + lua_pushcfunction(L, Add); + lua_setfield(L, metatable, "__add"); + + lua_pushcfunction(L, Subtract); + lua_setfield(L, metatable, "__sub"); + + lua_pushcfunction(L, Multiply); + lua_setfield(L, metatable, "__mul"); + + lua_pushcfunction(L, Divide); + lua_setfield(L, metatable, "__div"); + + lua_pushcfunction(L, Mod); + lua_setfield(L, metatable, "__mod"); + + lua_pushcfunction(L, Pow); + lua_setfield(L, metatable, "__pow"); + + lua_pushcfunction(L, UnaryMinus); + lua_setfield(L, metatable, "__unm"); + + lua_pushcfunction(L, Concat); + lua_setfield(L, metatable, "__concat"); + + lua_pushcfunction(L, Length); + lua_setfield(L, metatable, "__len"); + + lua_pushcfunction(L, Equal); + lua_setfield(L, metatable, "__eq"); + + lua_pushcfunction(L, Less); + lua_setfield(L, metatable, "__lt"); + + lua_pushcfunction(L, LessOrEqual); + lua_setfield(L, metatable, "__le"); + + lua_pushcfunction(L, Call); + lua_setfield(L, metatable, "__call"); + + lua_pushcfunction(L, GetType); + lua_setfield(L, metatable, "GetObjectType"); + + lua_pop(L, 1); + } + + template + static void SetMethods(Eluna* E, ElunaRegister const (&methodTable)[N]) + { + ASSERT(E); + ASSERT(methodTable); + + lua_State* L = E->L; + + constexpr bool isGlobal = std::is_same_v; + + if constexpr (isGlobal) + { + lua_pushglobaltable(L); + } + else + { + ASSERT(tname); + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); + ASSERT(lua_istable(L, -1)); + } + + for (std::size_t i = 0; i < N; i++) + { + const auto& method = methodTable + i; + + lua_pushstring(L, method->name); + + if (method->regState == METHOD_REG_NONE) + { + lua_pushstring(L, method->name); + lua_pushcclosure(L, MethodUnimpl, 1); + lua_rawset(L, -3); + continue; + } + + if (method->flags & METHOD_FLAG_UNSAFE && !sElunaConfig->UnsafeMethodsEnabled()) + { + lua_pushstring(L, method->name); + lua_pushcclosure(L, MethodUnsafe, 1); + lua_rawset(L, -3); + continue; + } + + if (method->flags & METHOD_FLAG_DEPRECATED && !sElunaConfig->DeprecatedMethodsEnabled()) + { + lua_pushstring(L, method->name); + lua_pushcclosure(L, MethodDeprecated, 1); + lua_rawset(L, -3); + continue; + } + + if (method->regState != METHOD_REG_ALL) + { + int32 mapId = E->GetBoundMapId(); + + if ((mapId == -1 && method->regState == METHOD_REG_MAP) || + (mapId != -1 && method->regState == METHOD_REG_WORLD)) + { + lua_pushstring(L, method->name); + lua_pushinteger(L, mapId); + lua_pushcclosure(L, MethodWrongState, 2); + lua_rawset(L, -3); + continue; + } + } + + lua_pushlightuserdata(L, (void*)method); + lua_pushcclosure(L, thunk, 1); + lua_rawset(L, -3); + } + + lua_pop(L, 1); + } + + static int Push(Eluna* E, T const* obj) + { + lua_State* L = E->L; + if (!obj) + { + lua_pushnil(L); + return 1; + } + + typedef ElunaObjectImpl ElunaObjectType; + + ElunaObjectType* elunaObject = static_cast(lua_newuserdata(L, sizeof(ElunaObjectType))); + if (!elunaObject) + { + ELUNA_LOG_ERROR("%s could not create new userdata", tname); + lua_pushnil(L); + return 1; + } + new (elunaObject) ElunaObjectType(E, const_cast(obj), tname); + + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) + { + ELUNA_LOG_ERROR("%s missing metatable", tname); + lua_pop(L, 2); + lua_pushnil(L); + return 1; + } + lua_setmetatable(L, -2); + return 1; + } + + static T* Check(Eluna* E, int narg, bool error = true) + { + lua_State* L = E->L; + + ElunaObject* elunaObj = E->CHECKTYPE(narg, tname, error); + if (!elunaObj) + return NULL; + + void* obj = elunaObj->GetObjIfValid(); + if (!obj) + { + char buff[256]; + snprintf(buff, 256, "%s expected, got pointer to nonexisting (invalidated) object (%s). Check your code.", tname, luaL_typename(L, narg)); + if (error) + { + luaL_argerror(L, narg, buff); + } + else + { + ELUNA_LOG_ERROR("%s", buff); + } + return NULL; + } + return static_cast(obj); + } + + static int GetType(lua_State* L) + { + lua_pushstring(L, tname); + return 1; + } + + static int thunk(lua_State* L) + { + ElunaRegister* l = static_cast*>(lua_touserdata(L, lua_upvalueindex(1))); + Eluna* E = Eluna::GetEluna(L); + + constexpr bool isGlobal = std::is_same_v; + + T* obj; + if constexpr (!isGlobal) + { + obj = E->CHECKOBJ(1); + if (!obj) + return 0; + } + + int top = lua_gettop(L); + + int expected = 0; + if constexpr (isGlobal) + expected = l->mfunc(E); + else + expected = l->mfunc(E, obj); + + int args = lua_gettop(L) - top; + if (args < 0 || args > expected) + { + ELUNA_LOG_ERROR("[Eluna]: %s returned unexpected amount of arguments %i out of %i. Report to devs", l->name, args, expected); + ASSERT(false); + } + lua_settop(L, top + expected); + return expected; + } + + static int CollectGarbage(lua_State* L) + { + Eluna* E = Eluna::GetEluna(L); + + ElunaObject* obj = E->CHECKOBJ(1, false); + obj->~ElunaObject(); + return 0; + } + + static int ToString(lua_State* L) + { + Eluna* E = Eluna::GetEluna(L); + + T* obj = E->CHECKOBJ(1, true); + lua_pushfstring(L, "%s: %p", tname, obj); + return 1; + } + + static int ArithmeticError(lua_State* L) { return luaL_error(L, "attempt to perform arithmetic on a %s value", tname); } + static int CompareError(lua_State* L) { return luaL_error(L, "attempt to compare %s", tname); } + static int Add(lua_State* L) { return ArithmeticError(L); } + static int Subtract(lua_State* L) { return ArithmeticError(L); } + static int Multiply(lua_State* L) { return ArithmeticError(L); } + static int Divide(lua_State* L) { return ArithmeticError(L); } + static int Mod(lua_State* L) { return ArithmeticError(L); } + static int Pow(lua_State* L) { return ArithmeticError(L); } + static int UnaryMinus(lua_State* L) { return ArithmeticError(L); } + static int Concat(lua_State* L) { return luaL_error(L, "attempt to concatenate a %s value", tname); } + static int Length(lua_State* L) { return luaL_error(L, "attempt to get length of a %s value", tname); } + static int Equal(lua_State* L) { Eluna* E = Eluna::GetEluna(L); E->Push(E->CHECKOBJ(1) == E->CHECKOBJ(2)); return 1; } + static int Less(lua_State* L) { return CompareError(L); } + static int LessOrEqual(lua_State* L) { return CompareError(L); } + static int Call(lua_State* L) { return luaL_error(L, "attempt to call a %s value", tname); } + + static int MethodWrongState(lua_State* L) { luaL_error(L, "attempt to call method '%s' that does not exist for state: %d", lua_tostring(L, lua_upvalueindex(1)), lua_tointeger(L, lua_upvalueindex(2))); return 0; } + static int MethodUnimpl(lua_State* L) { luaL_error(L, "attempt to call method '%s' that is not implemented for this emulator", lua_tostring(L, lua_upvalueindex(1))); return 0; } + static int MethodUnsafe(lua_State* L) { luaL_error(L, "attempt to call method '%s' that is flagged as unsafe! to use this method, enable unsafe methods in the config file", lua_tostring(L, lua_upvalueindex(1))); return 0; } + static int MethodDeprecated(lua_State* L) { luaL_error(L, "attempt to call method '%s' that is flagged as deprecated! this method will be removed in the future. to use this method, enable deprecated methods in the config file", lua_tostring(L, lua_upvalueindex(1))); return 0; } +}; + +template const char* ElunaTemplate::tname = NULL; + +#endif \ No newline at end of file diff --git a/src/server/game/LuaEngine/ElunaUtility.cpp b/src/server/game/LuaEngine/ElunaUtility.cpp new file mode 100644 index 0000000000..7fe7daf44d --- /dev/null +++ b/src/server/game/LuaEngine/ElunaUtility.cpp @@ -0,0 +1,215 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ElunaUtility.h" +#if !defined ELUNA_CMANGOS +#include "World.h" +#include "Object.h" +#include "Unit.h" +#include "GameObject.h" +#include "DB2Stores.h" +#else +#include "World/World.h" +#include "Entities/Object.h" +#include "Entities/Unit.h" +#include "Entities/GameObject.h" +#include "Server/DBCStores.h" +#include "Util/Timer.h" +#endif + +uint32 ElunaUtil::GetCurrTime() +{ +#if defined ELUNA_TRINITY || defined ELUNA_MANGOS || defined ELUNA_AZEROTHCORE + return getMSTime(); +#else + return WorldTimer::getMSTime(); +#endif +} + +uint32 ElunaUtil::GetTimeDiff(uint32 oldMSTime) +{ +#if defined ELUNA_TRINITY || defined ELUNA_MANGOS || defined ELUNA_AZEROTHCORE + return GetMSTimeDiffToNow(oldMSTime); +#else + return WorldTimer::getMSTimeDiff(oldMSTime, WorldTimer::getMSTime()); +#endif +} + +ElunaUtil::ObjectGUIDCheck::ObjectGUIDCheck(ObjectGuid guid) : _guid(guid) +{ +} + +bool ElunaUtil::ObjectGUIDCheck::operator()(WorldObject* object) +{ + return object->GET_GUID() == _guid; +} + +ElunaUtil::ObjectDistanceOrderPred::ObjectDistanceOrderPred(WorldObject const* pRefObj, bool ascending) : m_refObj(pRefObj), m_ascending(ascending) +{ +} +bool ElunaUtil::ObjectDistanceOrderPred::operator()(WorldObject const* pLeft, WorldObject const* pRight) const +{ + return m_ascending ? m_refObj->GetDistanceOrder(pLeft, pRight) : !m_refObj->GetDistanceOrder(pLeft, pRight); +} + +ElunaUtil::WorldObjectInRangeCheck::WorldObjectInRangeCheck(bool nearest, WorldObject const* obj, float range, + uint16 typeMask, uint32 entry, uint32 hostile, uint32 dead) : + i_obj(obj), i_obj_unit(nullptr), i_obj_fact(nullptr), i_hostile(hostile), i_entry(entry), i_range(range), i_typeMask(typeMask), i_dead(dead), i_nearest(nearest) +{ + i_obj_unit = i_obj->ToUnit(); + if (!i_obj_unit) + if (GameObject const* go = i_obj->ToGameObject()) + i_obj_unit = go->GetOwner(); + if (!i_obj_unit) +#if !defined ELUNA_VMANGOS + i_obj_fact = sFactionTemplateStore.LookupEntry(14); +#else + i_obj_fact = sObjectMgr.GetFactionTemplateEntry(14); +#endif +} +WorldObject const& ElunaUtil::WorldObjectInRangeCheck::GetFocusObject() const +{ + return *i_obj; +} +bool ElunaUtil::WorldObjectInRangeCheck::operator()(WorldObject* u) +{ +#if !defined ELUNA_VMANGOS + if (i_typeMask && !u->isType(TypeMask(i_typeMask))) +#else + if (i_typeMask && !u->IsType(TypeMask(i_typeMask))) +#endif + return false; + if (i_entry && u->GetEntry() != i_entry) + return false; + if (i_obj->GET_GUID() == u->GET_GUID()) + return false; + if (!i_obj->IsWithinDistInMap(u, i_range)) + return false; + Unit const* target = u->ToUnit(); + if (!target) + if (GameObject const* go = u->ToGameObject()) + target = go->GetOwner(); + if (target) + { + if (i_dead && (i_dead == 1) != target->IsAlive()) + return false; + if (i_hostile) + { + if (!i_obj_unit) + { + if (i_obj_fact) + { +#if !defined ELUNA_MANGOS + if ((i_obj_fact->IsHostileTo(target->GetFactionTemplateEntry())) != (i_hostile == 1)) // [删除了星号 *] +#else + if ((i_obj_fact->IsHostileTo(target->getFactionTemplateEntry())) != (i_hostile == 1)) // [删除了星号 *] +#endif + return false; + } + else if (i_hostile == 1) + return false; + } + else if ((i_hostile == 1) != i_obj_unit->IsHostileTo(target)) + return false; + } + } + if (i_nearest) + i_range = i_obj->GetDistance(u); + return true; +} +static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; +static char decoding_table[256]; +static int mod_table[] = {0, 2, 1}; + +static void build_decoding_table() +{ + for (int i = 0; i < 64; i++) + decoding_table[(unsigned char)encoding_table[i]] = i; +} + +void ElunaUtil::EncodeData(const unsigned char* data, size_t input_length, std::string& output) +{ + size_t output_length = 4 * ((input_length + 2) / 3); + char* buffer = new char[output_length]; + + for (size_t i = 0, j = 0; i < input_length;) + { + uint32 octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32 octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32 octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32 triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + buffer[j++] = encoding_table[(triple >> (3 * 6)) & 0x3F]; + buffer[j++] = encoding_table[(triple >> (2 * 6)) & 0x3F]; + buffer[j++] = encoding_table[(triple >> (1 * 6)) & 0x3F]; + buffer[j++] = encoding_table[(triple >> (0 * 6)) & 0x3F]; + } + + for (int i = 0; i < mod_table[input_length % 3]; i++) + buffer[output_length - 1 - i] = '='; + + output.assign(buffer, output_length); // Need length because `buffer` is not terminated! + delete[] buffer; +} + +unsigned char* ElunaUtil::DecodeData(const char *data, size_t *output_length) +{ + if (decoding_table[(unsigned char)'B'] == 0) + build_decoding_table(); + + size_t input_length = strlen(data); + + if (input_length % 4 != 0) + return NULL; + + // Make sure there's no invalid characters in the data. + for (size_t i = 0; i < input_length; ++i) + { + unsigned char byte = data[i]; + + if (byte == '=') + continue; + + // Every invalid character (and 'A') will map to 0 (due to `calloc`). + if (decoding_table[byte] == 0 && byte != 'A') + return NULL; + } + + *output_length = input_length / 4 * 3; + if (data[input_length - 1] == '=') (*output_length)--; + if (data[input_length - 2] == '=') (*output_length)--; + + unsigned char *decoded_data = new unsigned char[*output_length]; + if (!decoded_data) + return NULL; + + for (size_t i = 0, j = 0; i < input_length;) + { + uint32 sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + uint32 sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + uint32 sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + uint32 sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + + uint32 triple = (sextet_a << (3 * 6)) + + (sextet_b << (2 * 6)) + + (sextet_c << (1 * 6)) + + (sextet_d << (0 * 6)); + + if (j < *output_length) decoded_data[j++] = (triple >> (2 * 8)) & 0xFF; + if (j < *output_length) decoded_data[j++] = (triple >> (1 * 8)) & 0xFF; + if (j < *output_length) decoded_data[j++] = (triple >> (0 * 8)) & 0xFF; + } + + return decoded_data; +} diff --git a/src/server/game/LuaEngine/ElunaUtility.h b/src/server/game/LuaEngine/ElunaUtility.h new file mode 100644 index 0000000000..c0cb1b6688 --- /dev/null +++ b/src/server/game/LuaEngine/ElunaUtility.h @@ -0,0 +1,202 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ELUNA_UTIL_H +#define _ELUNA_UTIL_H + +#include "Common.h" +#include +#define EXP_CLASSIC 0 +#define EXP_TBC 1 +#define EXP_WOTLK 2 +#define EXP_CATA 3 + +#if !defined ELUNA_CMANGOS +#include "SharedDefines.h" +#include "ObjectGuid.h" +#include "Log.h" +#if defined ELUNA_TRINITY +#include "QueryResult.h" +#else +#include "Database/QueryResult.h" +#endif +#else +#include "Globals/SharedDefines.h" +#include "Entities/ObjectGuid.h" +#include "Database/QueryResult.h" +#include "Log/Log.h" +#endif + +#include +#include +#include +#include + +#if defined ELUNA_TRINITY || ELUNA_CMANGOS || ELUNA_AZEROTHCORE +#define USING_BOOST +#endif + +#if defined TRINITY_PLATFORM && defined TRINITY_PLATFORM_WINDOWS +#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS +#define ELUNA_WINDOWS +#endif +#elif defined AC_PLATFORM && defined AC_PLATFORM_WINDOWS +#if AC_PLATFORM == AC_PLATFORM_WINDOWS +#define ELUNA_WINDOWS +#endif +#elif defined PLATFORM && defined PLATFORM_WINDOWS +#if PLATFORM == PLATFORM_WINDOWS +#define ELUNA_WINDOWS +#endif +#else +#error Eluna could not determine platform +#endif + +#if defined ELUNA_TRINITY || ELUNA_AZEROTHCORE +typedef QueryResult ElunaQuery; +#define GET_GUID GetGUID +#define HIGHGUID_PLAYER HighGuid::Player +#define HIGHGUID_UNIT HighGuid::Unit +#define HIGHGUID_ITEM HighGuid::Item +#define HIGHGUID_GAMEOBJECT HighGuid::GameObject +#define HIGHGUID_PET HighGuid::Pet +#define HIGHGUID_TRANSPORT HighGuid::Transport +#define HIGHGUID_VEHICLE HighGuid::Vehicle +#define HIGHGUID_CONTAINER HighGuid::Container +#define HIGHGUID_DYNAMICOBJECT HighGuid::DynamicObject +#define HIGHGUID_CORPSE HighGuid::Corpse +#define HIGHGUID_MO_TRANSPORT HighGuid::Mo_Transport +#define HIGHGUID_INSTANCE HighGuid::Instance +#define HIGHGUID_GROUP HighGuid::Group +#endif + +#if defined ELUNA_TRINITY +#include "fmt/printf.h" +#define ELUNA_LOG_TC_FMT(TC_LOG_MACRO, ...) \ + try { \ + std::string message = fmt::sprintf(__VA_ARGS__); \ + TC_LOG_MACRO("eluna", "{}", message); \ + } catch (const std::exception& e) { \ + TC_LOG_MACRO("eluna", "Failed to format log message: {}", e.what()); \ + } +// [Eluna 现代日志翻译官]:拦截老式 %s,翻译为现代 TC 格式! +#define ELUNA_LOG_INFO(...) TC_LOG_INFO("server.loading", "{}", fmt::sprintf(__VA_ARGS__)); +#define ELUNA_LOG_ERROR(...) TC_LOG_ERROR("server.loading", "{}", fmt::sprintf(__VA_ARGS__)); +#define ELUNA_LOG_FATAL(...) TC_LOG_FATAL("server.loading", "{}", fmt::sprintf(__VA_ARGS__)); +#define ELUNA_LOG_DEBUG(...) TC_LOG_DEBUG("server.loading", "{}", fmt::sprintf(__VA_ARGS__)); +#elif defined ELUNA_AZEROTHCORE +#include "fmt/printf.h" +#define ELUNA_LOG_AC_FMT(AC_LOG_MACRO, ...) \ + try { \ + std::string message = fmt::sprintf(__VA_ARGS__); \ + AC_LOG_MACRO("eluna", "{}", message); \ + } catch (const std::exception& e) { \ + AC_LOG_MACRO("eluna", "Failed to format log message: {}", e.what()); \ + } +#define ELUNA_LOG_INFO(...) ELUNA_LOG_AC_FMT(LOG_INFO, __VA_ARGS__); +#define ELUNA_LOG_ERROR(...) ELUNA_LOG_AC_FMT(LOG_ERROR, __VA_ARGS__); +#define ELUNA_LOG_DEBUG(...) ELUNA_LOG_AC_FMT(LOG_DEBUG, __VA_ARGS__); +#elif defined ELUNA_VMANGOS +typedef std::shared_ptr ElunaQuery; +#define ASSERT MANGOS_ASSERT +#define ELUNA_LOG_INFO(...) sLog.Out(LOG_ELUNA, LOG_LVL_BASIC,__VA_ARGS__); +#define ELUNA_LOG_ERROR(...) sLog.Out(LOG_ELUNA, LOG_LVL_ERROR,__VA_ARGS__); +#define ELUNA_LOG_DEBUG(...) sLog.Out(LOG_ELUNA, LOG_LVL_DEBUG,__VA_ARGS__); +#define GET_GUID GetObjectGuid +#define GetGameObjectTemplate GetGameObjectInfo +#define GetItemTemplate GetItemPrototype +#define GetTemplate GetProto +#else +typedef std::shared_ptr ElunaQuery; +#define ASSERT MANGOS_ASSERT +#define ELUNA_LOG_INFO(...) sLog.outString(__VA_ARGS__); +#define ELUNA_LOG_ERROR(...) sLog.outErrorEluna(__VA_ARGS__); +#define ELUNA_LOG_DEBUG(...) sLog.outDebug(__VA_ARGS__); +#define GET_GUID GetObjectGuid +#define GetGameObjectTemplate GetGameObjectInfo +#define GetItemTemplate GetItemPrototype +#define GetTemplate GetProto +#endif + +#if !defined MAKE_NEW_GUID +#define MAKE_NEW_GUID(l, e, h) ObjectGuid(h, e, l) +#endif +#if !defined GUID_ENPART +#define GUID_ENPART(guid) ObjectGuid(guid).GetEntry() +#endif +#if !defined GUID_LOPART +#define GUID_LOPART(guid) ObjectGuid(guid).GetCounter() +#endif +#if !defined GUID_HIPART +#define GUID_HIPART(guid) ObjectGuid(guid).GetHigh() +#endif + +typedef std::vector BytecodeBuffer; + +class Unit; +class WorldObject; +struct FactionTemplateEntry; + +namespace ElunaUtil +{ + uint32 GetCurrTime(); + + uint32 GetTimeDiff(uint32 oldMSTime); + + class ObjectGUIDCheck + { + public: + ObjectGUIDCheck(ObjectGuid guid); + bool operator()(WorldObject* object); + + ObjectGuid _guid; + }; + + // Binary predicate to sort WorldObjects based on the distance to a reference WorldObject + class ObjectDistanceOrderPred + { + public: + ObjectDistanceOrderPred(WorldObject const* pRefObj, bool ascending = true); + bool operator()(WorldObject const* pLeft, WorldObject const* pRight) const; + + WorldObject const* m_refObj; + const bool m_ascending; + }; + + // Doesn't get self + class WorldObjectInRangeCheck + { + public: + WorldObjectInRangeCheck(bool nearest, WorldObject const* obj, float range, + uint16 typeMask = 0, uint32 entry = 0, uint32 hostile = 0, uint32 dead = 0); + WorldObject const& GetFocusObject() const; + bool operator()(WorldObject* u); + + WorldObject const* const i_obj; + Unit const* i_obj_unit; + FactionTemplateEntry const* i_obj_fact; + uint32 const i_hostile; // 0 both, 1 hostile, 2 friendly + uint32 const i_entry; + float i_range; + uint16 const i_typeMask; + uint32 const i_dead; // 0 both, 1 alive, 2 dead + bool const i_nearest; + }; + + /* + * Encodes `data` in Base-64 and store the result in `output`. + */ + void EncodeData(const unsigned char* data, size_t input_length, std::string& output); + + /* + * Decodes `data` from Base-64 and returns a pointer to the result, or `NULL` on error. + * + * The returned result buffer must be `delete[]`ed by the caller. + */ + unsigned char* DecodeData(const char* data, size_t *output_length); +}; + +#endif diff --git a/src/server/game/LuaEngine/LICENSE b/src/server/game/LuaEngine/LICENSE new file mode 100644 index 0000000000..9cecc1d466 --- /dev/null +++ b/src/server/game/LuaEngine/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/server/game/LuaEngine/LuaEngine.cpp b/src/server/game/LuaEngine/LuaEngine.cpp new file mode 100644 index 0000000000..f7c4f40c9e --- /dev/null +++ b/src/server/game/LuaEngine/LuaEngine.cpp @@ -0,0 +1,1084 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "Hooks.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaCompat.h" +#include "ElunaConfig.h" +#include "ElunaEventMgr.h" +#include "ElunaIncludes.h" +#include "ElunaLoader.h" +#include "ElunaTemplate.h" +#include "ElunaUtility.h" +#include "ElunaCreatureAI.h" +#include "ElunaInstanceAI.h" +Eluna* sEluna = nullptr; +extern "C" +{ +// Base lua libraries +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" + +// Additional lua libraries +}; + +extern void RegisterMethods(Eluna* E); + +void Eluna::_ReloadEluna() +{ + // Remove all timed events + eventMgr->SetAllEventStates(LUAEVENT_STATE_ERASE); + +#if defined ELUNA_TRINITY + // Cancel all pending async queries + //GetQueryProcessor().CancelAll(); +#endif + + // Close lua + CloseLua(); + + // Open new lua and libraries + OpenLua(); + + // Run scripts from loaded paths + RunScripts(); + + reload = false; +} + +Eluna::Eluna(Map* map) : +event_level(0), +push_counter(0), +boundMap(map), +L(NULL) +{ + OpenLua(); + eventMgr = std::make_unique(this); + + // if the script cache is ready, run scripts, otherwise flag state for reload + if (sElunaLoader->GetCacheState() == SCRIPT_CACHE_READY) + RunScripts(); + else + reload = true; +} + +Eluna::~Eluna() +{ + CloseLua(); +} + +void Eluna::CloseLua() +{ + OnLuaStateClose(); + + DestroyBindStores(); + + // Must close lua state after deleting stores and mgr + if (L) + lua_close(L); + L = NULL; + + instanceDataRefs.clear(); + continentDataRefs.clear(); +} + +static int PrecompiledLoader(lua_State* L) +{ + const char* modname = lua_tostring(L, 1); + if (modname == NULL) + return 0; + + const std::vector& scripts = sElunaLoader->GetLuaScripts(); + + auto it = std::find_if(scripts.begin(), scripts.end(), [modname](const LuaScript& script) { return script.filename == modname; }); + if (it == scripts.end()) { + lua_pushfstring(L, "\n\tno precompiled script '%s' found", modname); + return 1; + } + if (luaL_loadbuffer(L, reinterpret_cast(&it->bytecode[0]), it->bytecode.size(), it->filename.c_str())) + { + // Stack: modname, errmsg + return lua_error(L); + } + // Stack: modname, filefunction + lua_pushstring(L, it->filepath.c_str()); + // Stack: modname, filefunction, modpath + return 2; +} + +void Eluna::OpenLua() +{ + L = luaL_newstate(); + + lua_pushlightuserdata(L, this); + lua_setfield(L, LUA_REGISTRYINDEX, ELUNA_STATE_PTR); + + CreateBindStores(); + + // open base lua libraries + luaL_openlibs(L); + + // Register methods and functions + RegisterMethods(this); + + // Register event ID lookup table + RegisterHookGlobals(L); + + // get require paths + const std::string& requirepath = sElunaLoader->GetRequirePath(); + const std::string& requirecpath = sElunaLoader->GetRequireCPath(); + + // Set lua require folder paths (scripts folder structure) + lua_getglobal(L, "package"); + lua_pushstring(L, requirepath.c_str()); + lua_setfield(L, -2, "path"); + lua_pushstring(L, requirecpath.c_str()); + lua_setfield(L, -2, "cpath"); + // Set package.loaders loader for precompiled scripts + lua_getfield(L, -1, "loaders"); + if (lua_isnil(L, -1)) { + // Lua 5.2+ uses searchers instead of loaders + lua_pop(L, 1); + lua_getfield(L, -1, "searchers"); + } + // insert the new loader to the loaders table by shifting other elements down by one + const int newLoaderIndex = 1; + for (int i = lua_rawlen(L, -1); i >= newLoaderIndex; --i) { + lua_rawgeti(L, -1, i); + lua_rawseti(L, -2, i + 1); + } + lua_pushcfunction(L, &PrecompiledLoader); + lua_rawseti(L, -2, newLoaderIndex); + lua_pop(L, 2); // pop loaders/searchers table, pop package table +} + +void Eluna::CreateBindStores() +{ + DestroyBindStores(); + + CreateBinding>(Hooks::REGTYPE_SERVER); + CreateBinding>(Hooks::REGTYPE_PLAYER); + CreateBinding>(Hooks::REGTYPE_GUILD); + CreateBinding>(Hooks::REGTYPE_GROUP); + CreateBinding>(Hooks::REGTYPE_VEHICLE); + CreateBinding>(Hooks::REGTYPE_BG); + + CreateBinding>(Hooks::REGTYPE_PACKET); + CreateBinding>(Hooks::REGTYPE_CREATURE); + CreateBinding>(Hooks::REGTYPE_CREATURE_GOSSIP); + CreateBinding>(Hooks::REGTYPE_GAMEOBJECT); + CreateBinding>(Hooks::REGTYPE_GAMEOBJECT_GOSSIP); + CreateBinding>(Hooks::REGTYPE_SPELL); + CreateBinding>(Hooks::REGTYPE_ITEM); + CreateBinding>(Hooks::REGTYPE_ITEM_GOSSIP); + CreateBinding>(Hooks::REGTYPE_PLAYER_GOSSIP); + CreateBinding>(Hooks::REGTYPE_MAP); + CreateBinding>(Hooks::REGTYPE_INSTANCE); + + CreateBinding>(Hooks::REGTYPE_CREATURE_UNIQUE); +} + +void Eluna::DestroyBindStores() +{ + for (auto& binding : bindingMaps) + binding.reset(); +} + +void Eluna::RegisterHookGlobals(lua_State* _L) +{ + lua_newtable(_L); + auto const& [hookData, hookCount] = Hooks::getHooks(); + for (size_t i = 0; i < hookCount; ++i) { + const HookStorage& hs = hookData[i]; + + lua_newtable(_L); // subtable for category + + for (size_t j = 0; j < hs.eventCount; ++j) + { + lua_pushinteger(_L, hs.events[j].id); + lua_setfield(_L, -2, hs.events[j].name); + } + + lua_setfield(_L, -2, hs.category); // events[category] = subtable + } + lua_setglobal(_L, "events"); +} + +void Eluna::RunScripts() +{ + int32 const boundMapId = GetBoundMapId(); + uint32 const boundInstanceId = GetBoundInstanceId(); + ELUNA_LOG_DEBUG("[Eluna]: Running scripts for state: %i, instance: %u", boundMapId, boundInstanceId); + + uint32 oldMSTime = ElunaUtil::GetCurrTime(); + uint32 count = 0; + + std::unordered_map loaded; // filename, path + + lua_getglobal(L, "require"); + // Stack: require + + const std::vector& scripts = sElunaLoader->GetLuaScripts(); + + for (auto it = scripts.begin(); it != scripts.end(); ++it) + { + // check that the script file is either global or meant to be loaded for this map + if (it->mapId != -1 && it->mapId != boundMapId) + { + ELUNA_LOG_DEBUG("[Eluna]: `%s` is tagged %i and will not load for map: %i", it->filename.c_str(), it->mapId, boundMapId); + continue; + } + + // Check that no duplicate names exist + if (loaded.find(it->filename) != loaded.end()) + { + ELUNA_LOG_ERROR("[Eluna]: Error loading `%s`. File with same name already loaded from `%s`, rename either file", it->filepath.c_str(), loaded[it->filename].c_str()); + continue; + } + loaded[it->filename] = it->filepath; + + // We call require on the filename to load the script + // A custom loader is used to load the script from the combined_scripts table + // The loader is set up in Eluna::OpenLua + lua_pushvalue(L, -1); // Stack: require, require + lua_pushstring(L, it->filename.c_str()); // Stack: require, require, filename + if (ExecuteCall(1, 0)) + { + // Successfully called require on the script + ELUNA_LOG_DEBUG("[Eluna]: Successfully loaded `%s`", it->filepath.c_str()); + ++count; + continue; + } + // Stack: require + } + // Stack: require + lua_pop(L, 1); + ELUNA_LOG_INFO("[Eluna]: Executed %u Lua scripts in %u ms for map: %i, instance: %u", count, ElunaUtil::GetTimeDiff(oldMSTime), boundMapId, boundInstanceId); + + OnLuaStateOpen(); +} + +#if !defined TRACKABLE_PTR_NAMESPACE +void Eluna::InvalidateObjects() +{ + ++callstackid; + ASSERT(callstackid && "Callstackid overflow"); +} +#endif + +void Eluna::Report(lua_State* _L) +{ + const char* msg = lua_tostring(_L, -1); + ELUNA_LOG_ERROR("%s", msg); + lua_pop(_L, 1); +} + +// Borrowed from http://stackoverflow.com/questions/12256455/print-stacktrace-from-c-code-with-embedded-lua +int Eluna::StackTrace(lua_State* _L) +{ + // Stack: errmsg + if (!lua_isstring(_L, -1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + // Stack: errmsg, debug + lua_getglobal(_L, "debug"); + if (!lua_istable(_L, -1)) + { + lua_pop(_L, 1); + return 1; + } + // Stack: errmsg, debug, traceback + lua_getfield(_L, -1, "traceback"); + if (!lua_isfunction(_L, -1)) + { + lua_pop(_L, 2); + return 1; + } + lua_pushvalue(_L, -3); /* pass error message */ + lua_pushinteger(_L, 1); /* skip this function and traceback */ + // Stack: errmsg, debug, traceback, errmsg, 2 + lua_call(_L, 2, 1); /* call debug.traceback */ + + // dirty stack? + // Stack: errmsg, debug, tracemsg + return 1; +} + +bool Eluna::ExecuteCall(int params, int res) +{ + int top = lua_gettop(L); + int base = top - params; + + // Expected: function, [parameters] + ASSERT(base > 0); + + // Check function type + if (!lua_isfunction(L, base)) + { + ELUNA_LOG_ERROR("[Eluna]: Cannot execute call: registered value is %s, not a function.", luaL_tolstring(L, base, NULL)); + ASSERT(false); // stack probably corrupt + } + + bool usetrace = sElunaConfig->GetConfig(CONFIG_ELUNA_TRACEBACK); + if (usetrace) + { + lua_pushcfunction(L, &StackTrace); + // Stack: function, [parameters], traceback + lua_insert(L, base); + // Stack: traceback, function, [parameters] + } + + // Objects are invalidated when event_level hits 0 + ++event_level; + int result = lua_pcall(L, params, res, usetrace ? base : 0); + --event_level; + + if (usetrace) + { + // Stack: traceback, [results or errmsg] + lua_remove(L, base); + } + // Stack: [results or errmsg] + + // lua_pcall returns 0 on success. + // On error print the error and push nils for expected amount of returned values + if (result) + { + // Stack: errmsg + Report(L); + + // Force garbage collect + lua_gc(L, LUA_GCCOLLECT, 0); + + // Push nils for expected amount of results + for (int i = 0; i < res; ++i) + lua_pushnil(L); + // Stack: [nils] + return false; + } + + // Stack: [results] + return true; +} + +void Eluna::Push() +{ + lua_pushnil(L); +} +void Eluna::Push(const long long l) +{ + // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + ElunaTemplate::Push(this, &l); +} +void Eluna::Push(const unsigned long long l) +{ + // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + ElunaTemplate::Push(this, &l); +} +void Eluna::Push(const long l) +{ + Push(static_cast(l)); +} +void Eluna::Push(const unsigned long l) +{ + Push(static_cast(l)); +} +void Eluna::Push(const int i) +{ + lua_pushinteger(L, i); +} +void Eluna::Push(const unsigned int u) +{ + lua_pushunsigned(L, u); +} +void Eluna::Push(const double d) +{ + lua_pushnumber(L, d); +} +void Eluna::Push(const float f) +{ + lua_pushnumber(L, f); +} +void Eluna::Push(const bool b) +{ + lua_pushboolean(L, b); +} +void Eluna::Push(const std::string& str) +{ + lua_pushstring(L, str.c_str()); +} +void Eluna::Push(const char* str) +{ + lua_pushstring(L, str); +} +void Eluna::Push(Pet const* pet) +{ + Push(pet); +} +void Eluna::Push(TempSummon const* summon) +{ + Push(summon); +} +void Eluna::Push(Unit const* unit) +{ + if (!unit) + { + Push(); + return; + } + switch (unit->GetTypeId()) + { + case TYPEID_UNIT: + Push(unit->ToCreature()); + break; + case TYPEID_PLAYER: + Push(unit->ToPlayer()); + break; + default: + ElunaTemplate::Push(this, unit); + } +} +void Eluna::Push(WorldObject const* obj) +{ + if (!obj) + { + Push(); + return; + } + switch (obj->GetTypeId()) + { + case TYPEID_UNIT: + Push(obj->ToCreature()); + break; + case TYPEID_PLAYER: + Push(obj->ToPlayer()); + break; + case TYPEID_GAMEOBJECT: + Push(obj->ToGameObject()); + break; + case TYPEID_CORPSE: + Push(obj->ToCorpse()); + break; + default: + ElunaTemplate::Push(this, obj); + } +} +void Eluna::Push(Object const* obj) +{ + if (!obj) + { + Push(); + return; + } + switch (obj->GetTypeId()) + { + case TYPEID_UNIT: + Push(obj->ToCreature()); + break; + case TYPEID_PLAYER: + Push(obj->ToPlayer()); + break; + case TYPEID_GAMEOBJECT: + Push(obj->ToGameObject()); + break; + case TYPEID_CORPSE: + Push(obj->ToCorpse()); + break; + default: + ElunaTemplate::Push(this, obj); + } +} +void Eluna::Push(ObjectGuid const guid) +{ + // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + ElunaTemplate::Push(this, &guid); +} + +static int CheckIntegerRange(lua_State* luastate, int narg, int min, int max) +{ + double value = luaL_checknumber(luastate, narg); + char error_buffer[64]; + + if (value > max) + { + snprintf(error_buffer, 64, "value must be less than or equal to %i", max); + return luaL_argerror(luastate, narg, error_buffer); + } + + if (value < min) + { + snprintf(error_buffer, 64, "value must be greater than or equal to %i", min); + return luaL_argerror(luastate, narg, error_buffer); + } + + return static_cast(value); +} + +static unsigned int CheckUnsignedRange(lua_State* luastate, int narg, unsigned int max) +{ + double value = luaL_checknumber(luastate, narg); + + if (value < 0) + return luaL_argerror(luastate, narg, "value must be greater than or equal to 0"); + + if (value > max) + { + char error_buffer[64]; + snprintf(error_buffer, 64, "value must be less than or equal to %u", max); + return luaL_argerror(luastate, narg, error_buffer); + } + + return static_cast(value); +} + +template<> bool Eluna::CHECKVAL(int narg) +{ + return lua_toboolean(L, narg) != 0; +} +template<> float Eluna::CHECKVAL(int narg) +{ + return static_cast(luaL_checknumber(L, narg)); +} +template<> double Eluna::CHECKVAL(int narg) +{ + return luaL_checknumber(L, narg); +} +template<> signed char Eluna::CHECKVAL(int narg) +{ + return CheckIntegerRange(L, narg, SCHAR_MIN, SCHAR_MAX); +} +template<> unsigned char Eluna::CHECKVAL(int narg) +{ + return CheckUnsignedRange(L, narg, UCHAR_MAX); +} +template<> short Eluna::CHECKVAL(int narg) +{ + return CheckIntegerRange(L, narg, SHRT_MIN, SHRT_MAX); +} +template<> unsigned short Eluna::CHECKVAL(int narg) +{ + return CheckUnsignedRange(L, narg, USHRT_MAX); +} +template<> int Eluna::CHECKVAL(int narg) +{ + return CheckIntegerRange(L, narg, INT_MIN, INT_MAX); +} +template<> unsigned int Eluna::CHECKVAL(int narg) +{ + return CheckUnsignedRange(L, narg, UINT_MAX); +} +template<> const char* Eluna::CHECKVAL(int narg) +{ + return luaL_checkstring(L, narg); +} +template<> std::string Eluna::CHECKVAL(int narg) +{ + return luaL_checkstring(L, narg); +} +template<> long long Eluna::CHECKVAL(int narg) +{ + if (lua_isnumber(L, narg)) + return static_cast(CHECKVAL(narg)); + return *(Eluna::CHECKOBJ(narg, true)); +} +template<> unsigned long long Eluna::CHECKVAL(int narg) +{ + if (lua_isnumber(L, narg)) + return static_cast(CHECKVAL(narg)); + return *(Eluna::CHECKOBJ(narg, true)); +} +template<> long Eluna::CHECKVAL(int narg) +{ + return static_cast(CHECKVAL(narg)); +} +template<> unsigned long Eluna::CHECKVAL(int narg) +{ + return static_cast(CHECKVAL(narg)); +} +template<> ObjectGuid Eluna::CHECKVAL(int narg) +{ + ObjectGuid* guid = CHECKOBJ(narg, true); + return guid ? *guid : ObjectGuid(); +} + +template<> Object* Eluna::CHECKOBJ(int narg, bool error) +{ + Object* obj = CHECKOBJ(narg, false); + if (!obj) + obj = CHECKOBJ(narg, false); + if (!obj) + obj = ElunaTemplate::Check(this, narg, error); + return obj; +} +template<> WorldObject* Eluna::CHECKOBJ(int narg, bool error) +{ + WorldObject* obj = CHECKOBJ(narg, false); + if (!obj) + obj = CHECKOBJ(narg, false); + if (!obj) + obj = CHECKOBJ(narg, false); + if (!obj) + obj = ElunaTemplate::Check(this, narg, error); + return obj; +} +template<> Unit* Eluna::CHECKOBJ(int narg, bool error) +{ + Unit* obj = CHECKOBJ(narg, false); + if (!obj) + obj = CHECKOBJ(narg, false); + if (!obj) + obj = ElunaTemplate::Check(this, narg, error); + return obj; +} + +template<> ElunaObject* Eluna::CHECKOBJ(int narg, bool error) +{ + return CHECKTYPE(narg, NULL, error); +} + +ElunaObject* Eluna::CHECKTYPE(int narg, const char* tname, bool error) +{ + if (lua_islightuserdata(L, narg)) + { + if (error) + luaL_argerror(L, narg, "bad argument : userdata expected, got lightuserdata"); + return NULL; + } + + ElunaObject* elunaObject = static_cast(lua_touserdata(L, narg)); + + if (!elunaObject || (tname && elunaObject->GetTypeName() != tname)) + { + if (error) + { + char buff[256]; + snprintf(buff, 256, "bad argument : %s expected, got %s", tname ? tname : "ElunaObject", elunaObject ? elunaObject->GetTypeName() : luaL_typename(L, narg)); + luaL_argerror(L, narg, buff); + } + return NULL; + } + return elunaObject; +} + +template +static int cancelBinding(lua_State* L) +{ + Eluna* E = Eluna::GetEluna(L); + + uint64 bindingID = E->CHECKVAL(lua_upvalueindex(1)); + + BindingMap* bindings = (BindingMap*)lua_touserdata(L, lua_upvalueindex(2)); + ASSERT(bindings != NULL); + + bindings->Remove(bindingID); + + return 0; +} + +template +static void createCancelCallback(Eluna* e, uint64 bindingID, BindingMap* bindings) +{ + e->Push(bindingID); + lua_pushlightuserdata(e->L, bindings); + // Stack: bindingID, bindings + + lua_pushcclosure(e->L, &cancelBinding, 2); + // Stack: cancel_callback +} + +template +int RegisterBasicBinding(Eluna* e, std::underlying_type_t regtype, uint32 event_id, int functionRef, uint32 shots) +{ + typedef EventKey Key; + auto binding = e->GetBinding(regtype); + auto key = Key(static_cast(event_id)); + uint64 bindingID = binding->Insert(key, functionRef, shots); + createCancelCallback(e, bindingID, binding); + return 1; // Stack: callback +} + +template +int RegisterEntryBinding(Eluna* e, std::underlying_type_t regtype, uint32 entry, uint32 event_id, int functionRef, uint32 shots) +{ + typedef EntryKey Key; + auto binding = e->GetBinding(regtype); + auto key = Key(static_cast(event_id), entry); + uint64 bindingID = binding->Insert(key, functionRef, shots); + createCancelCallback(e, bindingID, binding); + return 1; // Stack: callback +} + +template +int RegisterUniqueBinding(Eluna* e, std::underlying_type_t regtype, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots) +{ + typedef UniqueObjectKey Key; + auto binding = e->GetBinding(regtype); + auto key = Key(static_cast(event_id), guid, instanceId); + uint64 bindingID = binding->Insert(key, functionRef, shots); + createCancelCallback(e, bindingID, binding); + return 1; // Stack: callback +} + +// Saves the function reference ID given to the register type's store for given entry under the given event +int Eluna::Register(std::underlying_type_t regtype, uint32 entry, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots) +{ + switch (regtype) + { + case Hooks::REGTYPE_SERVER: + if (event_id < Hooks::SERVER_EVENT_COUNT) + return RegisterBasicBinding(this, regtype, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_PLAYER: + if (event_id < Hooks::PLAYER_EVENT_COUNT) + return RegisterBasicBinding(this, regtype, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_GUILD: + if (event_id < Hooks::GUILD_EVENT_COUNT) + return RegisterBasicBinding(this, regtype, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_GROUP: + if (event_id < Hooks::GROUP_EVENT_COUNT) + return RegisterBasicBinding(this, regtype, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_VEHICLE: + if (event_id < Hooks::VEHICLE_EVENT_COUNT) + return RegisterBasicBinding(this, regtype, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_BG: + if (event_id < Hooks::BG_EVENT_COUNT) + return RegisterBasicBinding(this, regtype, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_PACKET: + if (event_id < Hooks::PACKET_EVENT_COUNT) + { + if (entry >= NUM_OPCODE_HANDLERS) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a creature with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_CREATURE: + if (event_id < Hooks::CREATURE_EVENT_COUNT) + { + if (!eObjectMgr->GetCreatureTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a creature with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_CREATURE_UNIQUE: + if (event_id < Hooks::CREATURE_EVENT_COUNT) + { + if (guid.IsEmpty()) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "guid was 0!"); + return 0; // Stack: (empty) + } + return RegisterUniqueBinding(this, regtype, guid, instanceId, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_CREATURE_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + if (!eObjectMgr->GetCreatureTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a creature with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_GAMEOBJECT: + if (event_id < Hooks::GAMEOBJECT_EVENT_COUNT) + { + if (!eObjectMgr->GetGameObjectTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_GAMEOBJECT_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + if (!eObjectMgr->GetGameObjectTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_SPELL: + if (event_id < Hooks::SPELL_EVENT_COUNT) + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_ITEM: + if (event_id < Hooks::ITEM_EVENT_COUNT) + { + if (!eObjectMgr->GetItemTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a item with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_ITEM_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + if (!eObjectMgr->GetItemTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a item with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + } + break; + + case Hooks::REGTYPE_PLAYER_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + break; + + case Hooks::REGTYPE_MAP: + case Hooks::REGTYPE_INSTANCE: + if (event_id < Hooks::INSTANCE_EVENT_COUNT) + return RegisterEntryBinding(this, regtype, entry, event_id, functionRef, shots); + break; + } + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + std::ostringstream oss; + oss << "regtype " << static_cast(regtype) << ", event " << event_id << ", entry " << entry << ", guid " << +#if defined ELUNA_TRINITY + guid.ToString() +#else + guid.GetRawValue() +#endif + << ", instance " << instanceId; + luaL_error(L, "Unknown event type (%s)", oss.str().c_str()); + return 0; +} + +void Eluna::UpdateEluna(uint32 diff) +{ + if (reload && sElunaLoader->GetCacheState() == SCRIPT_CACHE_READY) +#if defined ELUNA_TRINITY + //if (GetQueryProcessor().Empty()) +#endif + _ReloadEluna(); + + eventMgr->UpdateProcessors(diff); +#if defined ELUNA_TRINITY + GetQueryProcessor().ProcessReadyCallbacks(); +#endif +} + +/* + * Cleans up the stack, effectively undoing all Push calls and the Setup call. + */ +void Eluna::CleanUpStack(int number_of_arguments) +{ + // Stack: event_id, [arguments] + + lua_pop(L, number_of_arguments + 1); // Add 1 because the caller doesn't know about `event_id`. + // Stack: (empty) + +#if !defined TRACKABLE_PTR_NAMESPACE + if (event_level == 0) + InvalidateObjects(); +#endif +} + +/* + * Call a single event handler that was put on the stack with `Setup` and removes it from the stack. + * + * The caller is responsible for keeping track of how many times this should be called. + */ +int Eluna::CallOneFunction(int number_of_functions, int number_of_arguments, int number_of_results) +{ + ++number_of_arguments; // Caller doesn't know about `event_id`. + ASSERT(number_of_functions > 0 && number_of_arguments > 0 && number_of_results >= 0); + // Stack: event_id, [arguments], [functions] + + int functions_top = lua_gettop(L); + int first_function_index = functions_top - number_of_functions + 1; + int arguments_top = first_function_index - 1; + int first_argument_index = arguments_top - number_of_arguments + 1; + + // Copy the arguments from the bottom of the stack to the top. + for (int argument_index = first_argument_index; argument_index <= arguments_top; ++argument_index) + { + lua_pushvalue(L, argument_index); + } + // Stack: event_id, [arguments], [functions], event_id, [arguments] + + ExecuteCall(number_of_arguments, number_of_results); + --functions_top; + // Stack: event_id, [arguments], [functions - 1], [results] + + return functions_top + 1; // Return the location of the first result (if any exist). +} + +CreatureAI* Eluna::GetAI(Creature* creature) +{ + for (int i = 1; i < Hooks::CREATURE_EVENT_COUNT; ++i) + { + Hooks::CreatureEvents event_id = (Hooks::CreatureEvents)i; + + typedef EntryKey EKey; + typedef UniqueObjectKey UKey; + + auto entryKey = EKey(event_id, creature->GetEntry()); + auto uniqueKey = UKey(event_id, creature->GET_GUID(), creature->GetInstanceId()); + + auto CreatureEBindings = GetBinding(Hooks::REGTYPE_CREATURE); + auto CreatureUBindings = GetBinding(Hooks::REGTYPE_CREATURE_UNIQUE); + + if (CreatureEBindings->HasBindingsFor(entryKey) || + CreatureUBindings->HasBindingsFor(uniqueKey)) + return new ElunaCreatureAI(creature); + } + + return NULL; +} + +InstanceData* Eluna::GetInstanceData(Map* map) +{ + for (int i = 1; i < Hooks::INSTANCE_EVENT_COUNT; ++i) + { + Hooks::InstanceEvents event_id = (Hooks::InstanceEvents)i; + + typedef EntryKey Key; + + auto key = Key(event_id, map->GetId()); + + auto MapBindings = GetBinding(Hooks::REGTYPE_MAP); + auto InstanceBindings = GetBinding(Hooks::REGTYPE_INSTANCE); + + if (MapBindings->HasBindingsFor(key) || + InstanceBindings->HasBindingsFor(key)) + return new ElunaInstanceAI(map); + } + + return NULL; +} + +bool Eluna::HasInstanceData(Map const* map) +{ + if (!map->Instanceable()) + return continentDataRefs.find(map->GetId()) != continentDataRefs.end(); + else + return instanceDataRefs.find(map->GetInstanceId()) != instanceDataRefs.end(); +} + +void Eluna::CreateInstanceData(Map const* map) +{ + ASSERT(lua_istable(L, -1)); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + + if (!map->Instanceable()) + { + uint32 mapId = map->GetId(); + + // If there's another table that was already stored for the map, unref it. + auto mapRef = continentDataRefs.find(mapId); + if (mapRef != continentDataRefs.end()) + { + luaL_unref(L, LUA_REGISTRYINDEX, mapRef->second); + } + + continentDataRefs[mapId] = ref; + } + else + { + uint32 instanceId = map->GetInstanceId(); + + // If there's another table that was already stored for the instance, unref it. + auto instRef = instanceDataRefs.find(instanceId); + if (instRef != instanceDataRefs.end()) + { + luaL_unref(L, LUA_REGISTRYINDEX, instRef->second); + } + + instanceDataRefs[instanceId] = ref; + } +} + +/* + * Unrefs the instanceId related events and data + * Does all required actions for when an instance is freed. + */ +void Eluna::FreeInstanceId(uint32 instanceId) +{ + for (int i = 1; i < Hooks::INSTANCE_EVENT_COUNT; ++i) + { + typedef EntryKey Key; + + auto key = Key((Hooks::InstanceEvents)i, instanceId); + + auto MapEventBindings = GetBinding(Hooks::REGTYPE_MAP); + auto InstanceEventBindings = GetBinding(Hooks::REGTYPE_INSTANCE); + + if (MapEventBindings->HasBindingsFor(key)) + MapEventBindings->Clear(key); + + if (InstanceEventBindings->HasBindingsFor(key)) + InstanceEventBindings->Clear(key); + + if (instanceDataRefs.find(instanceId) != instanceDataRefs.end()) + { + luaL_unref(L, LUA_REGISTRYINDEX, instanceDataRefs[instanceId]); + instanceDataRefs.erase(instanceId); + } + } +} + +void Eluna::PushInstanceData(ElunaInstanceAI* ai, bool incrementCounter) +{ + // Check if the instance data is missing (i.e. someone reloaded Eluna). + if (!HasInstanceData(ai->instance)) + ai->Reload(); + + // Get the instance data table from the registry. + if (!ai->instance->Instanceable()) + lua_rawgeti(L, LUA_REGISTRYINDEX, continentDataRefs[ai->instance->GetId()]); + else + lua_rawgeti(L, LUA_REGISTRYINDEX, instanceDataRefs[ai->instance->GetInstanceId()]); + + ASSERT(lua_istable(L, -1)); + + if (incrementCounter) + ++push_counter; +} diff --git a/src/server/game/LuaEngine/LuaEngine.h b/src/server/game/LuaEngine/LuaEngine.h new file mode 100644 index 0000000000..d69cc42707 --- /dev/null +++ b/src/server/game/LuaEngine/LuaEngine.h @@ -0,0 +1,671 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _LUA_ENGINE_H +#define _LUA_ENGINE_H + +#include "Common.h" +#include "ElunaUtility.h" +#include "Hooks.h" + +#if !defined ELUNA_CMANGOS +#include "DBCEnums.h" +#include "Group.h" +#include "Item.h" +#include "Map.h" +#include "SharedDefines.h" +#include "Spell.h" +#include "Weather.h" +#include "World.h" +#if defined ELUNA_VMANGOS +#include "Player.h" +#endif +#else +#include "Entities/Item.h" +#include "Globals/SharedDefines.h" +#include "Groups/Group.h" +#include "Maps/Map.h" +#include "Server/DBCEnums.h" +#include "Weather/Weather.h" +#include "World/World.h" +#include "Entities/Player.h" +#endif + +#include +#include +#include "ElunaSpellWrapper.h" + +extern "C" +{ +#include "lua.h" +}; + +class AuctionHouseObject; +class Channel; +class Corpse; +class Creature; +class CreatureAI; +class ElunaInstanceAI; +class GameObject; +class Group; +class Guild; +class Item; +class Pet; +class Player; +class Quest; +class Spell; +class SpellCastTargets; +class Unit; +class Weather; +class WorldPacket; +#if !defined ELUNA_AZEROTHCORE +struct AreaTriggerEntry; +#endif +struct AuctionEntry; + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE +class Battleground; +class GameObjectAI; +class InstanceScript; +class TempSummon; +class Vehicle; +struct ItemTemplate; +typedef Battleground BattleGround; +typedef BattlegroundTypeId BattleGroundTypeId; +typedef InstanceScript InstanceData; +#if defined ELUNA_AZEROTHCORE +typedef AreaTrigger AreaTriggerEntry; +#endif +#else +class InstanceData; +struct ItemPrototype; +struct SpellEntry; +typedef ItemPrototype ItemTemplate; +typedef SpellEffectIndex SpellEffIndex; +typedef SpellEntry SpellInfo; + +#if defined ELUNA_CMANGOS +class TemporarySpawn; +typedef TemporarySpawn TempSummon; +#endif + +#if defined ELUNA_VMANGOS || ELUNA_MANGOS +class TemporarySummon; +typedef TemporarySummon TempSummon; +#endif + +#if ELUNA_EXPANSION == EXP_CLASSIC +typedef int Difficulty; +#endif + +#if ELUNA_EXPANSION >= EXP_WOTLK +class VehicleInfo; +typedef VehicleInfo Vehicle; +#endif +#endif + +struct lua_State; +class EventMgr; +class ElunaObject; +class BaseBindingMap; +template class ElunaTemplate; + +template class BindingMap; +template struct EventKey; +template struct EntryKey; +template struct UniqueObjectKey; + +struct LuaScript +{ + std::string fileext; + std::string filename; + std::string filepath; + std::string modulepath; + BytecodeBuffer bytecode; + int32 mapId; +}; + +enum MethodRegisterState +{ + METHOD_REG_NONE = 0, + METHOD_REG_MAP, + METHOD_REG_WORLD, + METHOD_REG_ALL +}; + +enum MethodFlags : uint32 +{ + METHOD_FLAG_NONE = 0x0, + METHOD_FLAG_UNSAFE = 0x1, + METHOD_FLAG_DEPRECATED = 0x2 +}; + +#define ELUNA_STATE_PTR "Eluna State Ptr" + +#if defined ELUNA_TRINITY +#define ELUNA_GAME_API TC_GAME_API +#define TRACKABLE_PTR_NAMESPACE ::Trinity:: +#elif defined ELUNA_AZEROTHCORE +#define ELUNA_GAME_API AC_GAME_API +#else +#define ELUNA_GAME_API +#if defined ELUNA_CMANGOS +#define TRACKABLE_PTR_NAMESPACE ::MaNGOS:: +#endif +#endif + +class ELUNA_GAME_API Eluna +{ +public: + + void ReloadEluna() { reload = true; } + bool ExecuteCall(int params, int res); + +private: + + // Indicates that the lua state should be reloaded + bool reload = false; + +#if !defined TRACKABLE_PTR_NAMESPACE + // A counter for lua event stacks that occur (see event_level). + // This is used to determine whether an object belongs to the current call stack or not. + // 0 is reserved for always belonging to the call stack + // 1 is reserved for a non valid callstackid + uint64 callstackid = 2; +#endif + // A counter for the amount of nested events. When the event_level + // reaches 0 we are about to return back to C++. At this point the + // objects used during the event stack are invalidated. + uint32 event_level; + // When a hook pushes arguments to be passed to event handlers, + // this is used to keep track of how many arguments were pushed. + uint8 push_counter; + + Map* const boundMap; + + // Map from instance ID -> Lua table ref + std::unordered_map instanceDataRefs; + // Map from map ID -> Lua table ref + std::unordered_map continentDataRefs; + + std::array, Hooks::REGTYPE_COUNT> bindingMaps; + + template + void CreateBinding(Hooks::RegisterTypes type) + { + auto index = static_cast>(type); + bindingMaps[index] = std::make_unique>(L); + } + + void OpenLua(); + void CloseLua(); + void DestroyBindStores(); + void CreateBindStores(); + void RegisterHookGlobals(lua_State* _L); +#if !defined TRACKABLE_PTR_NAMESPACE + void InvalidateObjects(); +#endif + + // Use ReloadEluna() to make eluna reload + // This is called on world update to reload eluna + void _ReloadEluna(); + + // Some helpers for hooks to call event handlers. + // The bodies of the templates are in HookHelpers.h, so if you want to use them you need to #include "HookHelpers.h". + template int SetupStack(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, int number_of_arguments); + int CallOneFunction(int number_of_functions, int number_of_arguments, int number_of_results); + void CleanUpStack(int number_of_arguments); + template void ReplaceArgument(T value, int index); + template void CallAllFunctions(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2); + template bool CallAllFunctionsBool(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, bool default_value = false); + template int32 CallAllFunctionsInt(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, int32 default_value = 0); + template void ApplyMultiReturnsImpl(int r, std::tuple& outs, const std::array& indices, std::index_sequence); + template void CallAllFunctionsMultiReturn(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, std::tuple outs, const std::array& out_arg_indices); + template + void CallAllFunctionsTable(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, std::list& list); // Same as above but for only one binding instead of two. + // `key` is passed twice because there's no NULL for references, but it's not actually used if `bindings2` is NULL. + template int SetupStack(BindingMap* bindings, const K& key, int number_of_arguments) + { + return SetupStack(bindings, NULL, key, key, number_of_arguments); + } + template void CallAllFunctions(BindingMap* bindings, const K& key) + { + CallAllFunctions(bindings, NULL, key, key); + } + template bool CallAllFunctionsBool(BindingMap* bindings, const K& key, bool default_value = false) + { + return CallAllFunctionsBool(bindings, NULL, key, key, default_value); + } + template int32 CallAllFunctionsInt(BindingMap* bindings, const K& key, int default_value = 0) + { + return CallAllFunctionsInt(bindings, NULL, key, key, default_value); + } + template void CallAllFunctionsMultiReturn(BindingMap* bindings, const K& key, std::tuple outs, const std::array& out_arg_indices) + { + CallAllFunctionsMultiReturn(bindings, NULL, key, key, outs, out_arg_indices); + } + template + void CallAllFunctionsTable(BindingMap* bindings, const K& key, std::list& list) + { + CallAllFunctionsTable(bindings, NULL, key, key, list); + } + // Non-static pushes, to be used in hooks. + // They up the pushed value counter for hook helper functions. + void HookPush() { Push(); ++push_counter; } + void HookPush(const long long value) { Push(value); ++push_counter; } + void HookPush(const unsigned long long value) { Push(value); ++push_counter; } + void HookPush(const long value) { Push(value); ++push_counter; } + void HookPush(const unsigned long value) { Push(value); ++push_counter; } + void HookPush(const int value) { Push(value); ++push_counter; } + void HookPush(const unsigned int value) { Push(value); ++push_counter; } + void HookPush(const bool value) { Push(value); ++push_counter; } + void HookPush(const float value) { Push(value); ++push_counter; } + void HookPush(const double value) { Push(value); ++push_counter; } + void HookPush(const std::string& value) { Push(value); ++push_counter; } + void HookPush(const char* value) { Push(value); ++push_counter; } + void HookPush(ObjectGuid const value) { Push(value); ++push_counter; } + template + void HookPush(T const* ptr) { Push(ptr); ++push_counter; } + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + QueryCallbackProcessor queryProcessor; +#endif +public: + + lua_State* L; + std::unique_ptr eventMgr; + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + QueryCallbackProcessor& GetQueryProcessor() { return queryProcessor; } +#endif + + static int StackTrace(lua_State* _L); + static void Report(lua_State* _L); + + // Never returns nullptr + static Eluna* GetEluna(lua_State* L) + { + lua_pushstring(L, ELUNA_STATE_PTR); + lua_rawget(L, LUA_REGISTRYINDEX); + ASSERT(lua_islightuserdata(L, -1)); + Eluna* E = static_cast(lua_touserdata(L, -1)); + lua_pop(L, 1); + ASSERT(E); + return E; + } + + // can be used by anything, including methods. + void Push(); // nil + void Push(const long long); + void Push(const unsigned long long); + void Push(const long); + void Push(const unsigned long); + void Push(const int); + void Push(const unsigned int); + void Push(const bool); + void Push(const float); + void Push(const double); + void Push(const std::string&); + void Push(const char*); + void Push(Object const* obj); + void Push(WorldObject const* obj); + void Push(Unit const* unit); + void Push(Pet const* pet); + void Push(TempSummon const* summon); + void Push(ObjectGuid const guid); + template + void Push(T const* ptr) + { + ElunaTemplate::Push(this, ptr); + } + + /* + * Returns `true` if Eluna has instance data for `map`. + */ + bool HasInstanceData(Map const* map); + + /* + * Use the top element of the stack as the instance data table for `map`, + * then pops it off the stack. + */ + void CreateInstanceData(Map const* map); + + /* + * Retrieve the instance data for the `Map` scripted by `ai` and push it + * onto the stack. + * + * An `ElunaInstanceAI` is needed because the instance data might + * not exist (i.e. Eluna has been reloaded). + * + * In that case, the AI is "reloaded" (new instance data table is created + * and loaded with the last known save state, and `Load`/`Initialize` + * hooks are called). + */ + void PushInstanceData(ElunaInstanceAI* ai, bool incrementCounter = true); + + void RunScripts(); + bool HasLuaState() const { return L != NULL; } +#if !defined TRACKABLE_PTR_NAMESPACE + uint64 GetCallstackId() const { return callstackid; } +#endif + int Register(std::underlying_type_t regtype, uint32 entry, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots); + void UpdateEluna(uint32 diff); + + // Checks + template T CHECKVAL(int narg); + template T CHECKVAL(int narg, T def) + { + return lua_isnoneornil(L, narg) ? def : CHECKVAL(narg); + } + template T* CHECKOBJ(int narg, bool error = true) + { + return ElunaTemplate::Check(this, narg, error); + } + ElunaObject* CHECKTYPE(int narg, const char* tname, bool error = true); + + CreatureAI* GetAI(Creature* creature); + InstanceData* GetInstanceData(Map* map); + void FreeInstanceId(uint32 instanceId); + + Map* GetBoundMap() const { return boundMap; } + + int32 GetBoundMapId() const + { + if(const Map * map = GetBoundMap()) + return map->GetId(); + + return -1; + } + + uint32 GetBoundInstanceId() const + { + if(const Map * map = GetBoundMap()) + return map->GetInstanceId(); + + return 0; + } + + template + BindingMap* GetBinding(std::underlying_type_t type) + { + if (type >= Hooks::REGTYPE_COUNT) + return nullptr; + + auto& binding = bindingMaps[type]; + if (!binding) + return nullptr; + + return dynamic_cast*>(binding.get()); + } + + template + BindingMap* GetBinding(Hooks::RegisterTypes type) + { + return GetBinding(static_cast>(type)); + } + + Eluna(Map * map); + ~Eluna(); + + // Prevent copy + Eluna(Eluna const&) = delete; + Eluna& operator=(const Eluna&) = delete; + + /* Custom */ + void OnTimedEvent(int funcRef, uint32 delay, uint32 calls, WorldObject* obj); + bool OnCommand(Player* player, const char* text); + void OnWorldUpdate(uint32 diff); + void OnLootItem(Player* pPlayer, Item* pItem, uint32 count, ObjectGuid guid); + void OnLootMoney(Player* pPlayer, uint32 amount); + void OnFirstLogin(Player* pPlayer); + void OnEquip(Player* pPlayer, Item* pItem, uint8 bag, uint8 slot); + void OnRepop(Player* pPlayer); + void OnResurrect(Player* pPlayer); + void OnQuestAbandon(Player* pPlayer, uint32 questId); + void OnQuestStatusChanged(Player* pPlayer, uint32 questId, uint8 status); + void OnLearnTalents(Player* pPlayer, uint32 talentId, uint32 talentRank, uint32 spellid); + void OnSkillChange(Player* pPlayer, uint32 skillId, uint32 skillValue); + void OnLearnSpell(Player* pPlayer, uint32 spellid); + InventoryResult OnCanUseItem(const Player* pPlayer, uint32 itemEntry); + void OnLuaStateClose(); + void OnLuaStateOpen(); + bool OnAddonMessage(Player* sender, uint32 type, std::string& msg, Player* receiver, Guild* guild, Group* group, Channel* channel); + bool OnTradeInit(Player* trader, Player* tradee); + bool OnTradeAccept(Player* trader, Player* tradee); + bool OnSendMail(Player* sender, ObjectGuid recipientGuid); + void OnDiscoverArea(Player* player, uint32 area); + + /* Item */ + void OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Item* pTarget); + bool OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest); + bool OnUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); + bool OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); + bool OnItemGossip(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); + bool OnExpire(Player* pPlayer, ItemTemplate const* pProto); + bool OnRemove(Player* pPlayer, Item* item); + void OnAdd(Player* pPlayer, Item* item); + void HandleGossipSelectOption(Player* pPlayer, Item* item, uint32 sender, uint32 action, const std::string& code); + void OnItemEquip(Player* pPlayer, Item* pItem, uint8 slot); + void OnItemUnEquip(Player* pPlayer, Item* pItem, uint8 slot); + + /* Creature */ + void OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Creature* pTarget); + bool OnGossipHello(Player* pPlayer, Creature* pCreature); + bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action); + bool OnGossipSelectCode(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code); + bool OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest); + bool OnQuestReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt); + void GetDialogStatus(const Player* pPlayer, const Creature* pCreature); + + bool OnSummoned(Creature* creature, Unit* summoner); + bool UpdateAI(Creature* me, const uint32 diff); + bool EnterCombat(Creature* me, Unit* target); + bool DamageTaken(Creature* me, Unit* attacker, uint32& damage); + bool JustDied(Creature* me, Unit* killer); + bool KilledUnit(Creature* me, Unit* victim); + bool JustSummoned(Creature* me, Creature* summon); + bool SummonedCreatureDespawn(Creature* me, Creature* summon); + bool MovementInform(Creature* me, uint32 type, uint32 id); + bool AttackStart(Creature* me, Unit* target); + bool EnterEvadeMode(Creature* me); + bool JustRespawned(Creature* me); + bool JustReachedHome(Creature* me); + bool ReceiveEmote(Creature* me, Player* player, uint32 emoteId); + bool CorpseRemoved(Creature* me, uint32& respawnDelay); + bool MoveInLineOfSight(Creature* me, Unit* who); + bool SpellHit(Creature* me, WorldObject* caster, SpellInfo const* spell); + bool SpellHitTarget(Creature* me, WorldObject* target, SpellInfo const* spell); + bool SummonedCreatureDies(Creature* me, Creature* summon, Unit* killer); + bool OwnerAttackedBy(Creature* me, Unit* attacker); + bool OwnerAttacked(Creature* me, Unit* target); + void On_Reset(Creature* me); + + /* GameObject */ + void OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, GameObject* pTarget); + bool OnGameObjectUse(Player* pPlayer, GameObject* pGameObject); + bool OnGossipHello(Player* pPlayer, GameObject* pGameObject); + bool OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action); + bool OnGossipSelectCode(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code); + bool OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest); + bool OnQuestReward(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest, uint32 opt); + void GetDialogStatus(const Player* pPlayer, const GameObject* pGameObject); +#if ELUNA_EXPANSION >= EXP_WOTLK +#ifndef ELUNA_AZEROTHCORE + void OnDestroyed(GameObject* pGameObject, WorldObject* attacker); + void OnDamaged(GameObject* pGameObject, WorldObject* attacker); +#else + void OnDestroyed(GameObject* pGameObject, Player* attacker); + void OnDamaged(GameObject* pGameObject, Player* attacker); +#endif +#endif + void OnLootStateChanged(GameObject* pGameObject, uint32 state); + void OnGameObjectStateChanged(GameObject* pGameObject, uint32 state); + void UpdateAI(GameObject* pGameObject, uint32 diff); + void OnSpawn(GameObject* gameobject); + + /* Packet */ + bool OnPacketSend(WorldSession* session, const WorldPacket& packet); + void OnPacketSendAny(Player* player, const WorldPacket& packet, bool& result); + void OnPacketSendOne(Player* player, const WorldPacket& packet, bool& result); + bool OnPacketReceive(WorldSession* session, WorldPacket& packet); + void OnPacketReceiveAny(Player* player, WorldPacket& packet, bool& result); + void OnPacketReceiveOne(Player* player, WorldPacket& packet, bool& result); + + /* Player */ + void OnPlayerEnterCombat(Player* pPlayer, Unit* pEnemy); + void OnPlayerLeaveCombat(Player* pPlayer); + void OnPVPKill(Player* pKiller, Player* pKilled); + void OnCreatureKill(Player* pKiller, Creature* pKilled); + void OnPlayerKilledByCreature(Creature* pKiller, Player* pKilled); + void OnPlayerKilledByEnvironment(Player* pKilled, uint8 damageType); + void OnLevelChanged(Player* pPlayer, uint8 oldLevel); + void OnFreeTalentPointsChanged(Player* pPlayer, uint32 newPoints); + void OnTalentsReset(Player* pPlayer, bool noCost); + void OnMoneyChanged(Player* pPlayer, int32& amount); +#if ELUNA_EXPANSION >= EXP_CATA + void OnMoneyChanged(Player* pPlayer, int64& amount); +#endif + void OnGiveXP(Player* pPlayer, uint32& amount, Unit* pVictim); + void OnReputationChange(Player* pPlayer, uint32 factionID, int32& standing, bool incremental); + void OnDuelRequest(Player* pTarget, Player* pChallenger); + void OnDuelStart(Player* pStarter, Player* pChallenger); + void OnDuelEnd(Player* pWinner, Player* pLoser, DuelCompleteType type); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Group* pGroup); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Guild* pGuild); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Channel* pChannel); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Player* pReceiver); + void OnEmote(Player* pPlayer, uint32 emote); + void OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, ObjectGuid guid); + void OnSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck); + void OnLogin(Player* pPlayer); + void OnLogout(Player* pPlayer); + void OnCreate(Player* pPlayer); + void OnDelete(uint32 guid); + void OnSave(Player* pPlayer); + void OnBindToInstance(Player* pPlayer, Difficulty difficulty, uint32 mapid, bool permanent); + void OnUpdateZone(Player* pPlayer, uint32 newZone, uint32 newArea); + void OnUpdateArea(Player* pPlayer, uint32 oldArea, uint32 newArea); + void OnMapChanged(Player* pPlayer); + void HandleGossipSelectOption(Player* pPlayer, uint32 menuId, uint32 sender, uint32 action, const std::string& code); + void OnAchievementComplete(Player* pPlayer, uint32 achievementId); + +#if ELUNA_EXPANSION >= EXP_WOTLK + /* Vehicle */ + void OnInstall(Vehicle* vehicle); + void OnUninstall(Vehicle* vehicle); + void OnInstallAccessory(Vehicle* vehicle, Creature* accessory); + void OnAddPassenger(Vehicle* vehicle, Unit* passenger, int8 seatId); + void OnRemovePassenger(Vehicle* vehicle, Unit* passenger); +#endif + + /* AreaTrigger */ + bool OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* pTrigger); + + /* Weather */ + void OnChange(Weather* weather, uint32 zone, WeatherState state, float grade); + + /* Auction House */ + void OnAdd(AuctionHouseObject* ah, AuctionEntry* entry); + void OnRemove(AuctionHouseObject* ah, AuctionEntry* entry); + void OnSuccessful(AuctionHouseObject* ah, AuctionEntry* entry); + void OnExpire(AuctionHouseObject* ah, AuctionEntry* entry); + + /* Guild */ + void OnAddMember(Guild* guild, Player* player, uint32 plRank); + void OnRemoveMember(Guild* guild, Player* player, bool isDisbanding); + void OnMOTDChanged(Guild* guild, const std::string& newMotd); + void OnInfoChanged(Guild* guild, const std::string& newInfo); + void OnCreate(Guild* guild, Player* leader, const std::string& name); + void OnDisband(Guild* guild); + void OnMemberWitdrawMoney(Guild* guild, Player* player, uint32& amount, bool isRepair); + void OnMemberDepositMoney(Guild* guild, Player* player, uint32& amount); +#if ELUNA_EXPANSION >= EXP_CATA + void OnMemberWitdrawMoney(Guild* guild, Player* player, uint64& amount, bool isRepair); + void OnMemberDepositMoney(Guild* guild, Player* player, uint64& amount); +#endif + void OnItemMove(Guild* guild, Player* player, Item* pItem, bool isSrcBank, uint8 srcContainer, uint8 srcSlotId, bool isDestBank, uint8 destContainer, uint8 destSlotId); + void OnEvent(Guild* guild, uint8 eventType, uint32 playerGuid1, uint32 playerGuid2, uint8 newRank); + void OnBankEvent(Guild* guild, uint8 eventType, uint8 tabId, uint32 playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId); + + /* Group */ + void OnAddMember(Group* group, ObjectGuid guid); + void OnInviteMember(Group* group, ObjectGuid guid); + void OnRemoveMember(Group* group, ObjectGuid guid, uint8 method); + void OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid); + void OnDisband(Group* group); + void OnCreate(Group* group, ObjectGuid leaderGuid, GroupType groupType); + bool OnMemberAccept(Group* group, Player* player); + + /* Map */ + void OnCreate(Map* map); + void OnDestroy(Map* map); + void OnPlayerEnter(Map* map, Player* player); + void OnPlayerLeave(Map* map, Player* player); + void OnMapUpdate(Map* map, uint32 diff); + void OnAddToWorld(Creature* creature); + void OnRemoveFromWorld(Creature* creature); + void OnAddToWorld(GameObject* gameobject); + void OnRemoveFromWorld(GameObject* gameobject); + void OnRemove(Creature* creature); + void OnRemove(GameObject* gameobject); + + /* Instance */ + void OnInitialize(ElunaInstanceAI* ai); + void OnLoad(ElunaInstanceAI* ai); + void OnUpdateInstance(ElunaInstanceAI* ai, uint32 diff); + void OnPlayerEnterInstance(ElunaInstanceAI* ai, Player* player); + void OnCreatureCreate(ElunaInstanceAI* ai, Creature* creature); + void OnGameObjectCreate(ElunaInstanceAI* ai, GameObject* gameobject); + bool OnCheckEncounterInProgress(ElunaInstanceAI* ai); + + /* World */ + void OnOpenStateChange(bool open); + void OnConfigLoad(bool reload); + void OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask); + void OnShutdownCancel(); + void OnStartup(); + void OnShutdown(); + void OnGameEventStart(uint32 eventid); + void OnGameEventStop(uint32 eventid); + + /* Battle Ground */ + void OnBGStart(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + void OnBGEnd(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId, Team winner); + void OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + void OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + + /* Spell */ + void OnSpellCast(Spell* pSpell, bool skipCheck); + bool OnAuraApplication(Aura* aura, AuraEffect const* auraEff, Unit* target, uint8 mode, bool apply); + void OnAuraDispel(Aura* aura, DispelInfo* dispelInfo); + bool OnPeriodicTick(Aura* aura, AuraEffect const* auraEff, Unit* target); + void OnPeriodicUpdate(Aura* aura, AuraEffect const* auraEff); + void OnAuraCalcAmount(Aura* aura, AuraEffect const* auraEff, int32& amount, bool& canBeRecalculated); + void OnCalcPeriodic(Aura* aura, AuraEffect const* auraEff, bool& isPeriodic, int32& amplitude); + bool OnAuraCanProc(Aura* aura, ProcEventInfo& procInfo); + bool OnAuraProc(Aura* aura, ProcEventInfo& procInfo); + uint32 OnCheckCast(Spell* pSpell); + void OnBeforeCast(Spell* pSpell); + void OnAfterCast(Spell* pSpell); + void OnObjectAreaTargetSelect(Spell* pSpell, uint8 effIndex, std::list& targets); + void OnObjectTargetSelect(Spell* pSpell, uint8 effIndex, WorldObject*& target); + void OnDestinationTargetSelect(Spell* pSpell, uint8 effIndex, SpellDestination& target); + bool OnEffectLaunch(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault); + bool OnEffectLaunchTarget(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault); + bool OnEffectHit(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault); + bool OnEffectHitTarget(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault); + void OnBeforeSpellHit(Spell* pSpell, uint8 missInfo); + void OnSpellHit(Spell* pSpell); + void OnAfterSpellHit(Spell* pSpell); + void OnEffectCalcAbsorb(Spell* pSpell, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount); +}; +template<> Unit* Eluna::CHECKOBJ(int narg, bool error); +template<> Object* Eluna::CHECKOBJ(int narg, bool error); +template<> WorldObject* Eluna::CHECKOBJ(int narg, bool error); +template<> ElunaObject* Eluna::CHECKOBJ(int narg, bool error); +extern class Eluna* sEluna; +#endif diff --git a/src/server/game/LuaEngine/LuaValue.cpp b/src/server/game/LuaEngine/LuaValue.cpp new file mode 100644 index 0000000000..39c28f23b0 --- /dev/null +++ b/src/server/game/LuaEngine/LuaValue.cpp @@ -0,0 +1,282 @@ +// BSD-3-Clause +// Copyright (c) 2022, Rochet2 +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "ElunaCompat.h" +#include "LuaValue.h" +#include // snprintf + +extern "C" +{ +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} + +LuaVal* LuaVal::GetLuaVal(lua_State* L, int index) { + return static_cast(luaL_testudata(L, index, LUAVAL_MT_NAME)); +} + +LuaVal* LuaVal::GetCheckLuaVal(lua_State* L, int index) { + return static_cast(luaL_checkudata(L, index, LUAVAL_MT_NAME)); +} + +int LuaVal::PushLuaVal(lua_State* L, LuaVal const& lv) { + LuaVal* ud = static_cast(lua_newuserdata(L, sizeof(LuaVal))); + new (ud) LuaVal(lv.reference()); + luaL_setmetatable(L, LUAVAL_MT_NAME); + return 1; +} + +void LuaVal::Register(lua_State* L) { + luaL_newmetatable(L, LUAVAL_MT_NAME); + // mt + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, &LuaVal::lua_to_string); + lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, &LuaVal::lua_gc); + lua_setfield(L, -2, "__gc"); + + lua_pushcfunction(L, &LuaVal::lua_get); + lua_setfield(L, -2, "Get"); + lua_pushcfunction(L, &LuaVal::lua_set); + lua_setfield(L, -2, "Set"); + lua_pushcfunction(L, &LuaVal::lua_AsLuaVal); + lua_setfield(L, -2, "New"); + lua_pushcfunction(L, &LuaVal::lua_asLua); + lua_setfield(L, -2, "AsTable"); + + lua_setglobal(L, "LuaVal"); +} + +int LuaVal::lua_get(lua_State* L) { + LuaVal* self = GetCheckLuaVal(L, 1); + int arguments = std::max(2, lua_gettop(L)); + WrappedMap const* p = std::get_if(&self->v); + if (!p) + luaL_argerror(L, 1, "trying to index a non-table LuaVal"); + for (int i = 2; i <= arguments; ++i) { + auto& map = (*p); + auto klv = AsLuaVal(L, i); + if (std::holds_alternative(klv.v)) + luaL_argerror(L, i, "trying to use nil as key"); + auto it = map->find(klv); + if (it == map->end()) { + if (i < arguments) { + luaL_argerror(L, i, "trying to index a nil value within a LuaVal"); + } + break; + } + else if (i == arguments) { + auto& val = it->second; + return val.asObject(L); + } + p = std::get_if(&it->second.v); + if (!p) + luaL_argerror(L, i, "trying to index a non-table LuaVal"); + } + lua_pushnil(L); + return 1; +} + +int LuaVal::lua_set(lua_State* L) { + LuaVal* self = GetCheckLuaVal(L, 1); + int arguments = std::max(3, lua_gettop(L)); + WrappedMap const* p = std::get_if(&self->v); + if (!p) + luaL_argerror(L, 1, "trying to index a non-table LuaVal"); + for (int i = 2; i <= arguments - 2; ++i) { + auto& map = (*p); + auto klv = AsLuaVal(L, i); + if (std::holds_alternative(klv.v)) + luaL_argerror(L, i, "trying to use nil as key"); + auto it = map->find(klv); + if (it == map->end()) { + if (i < arguments) { + luaL_argerror(L, i, "trying to index a nil value within a LuaVal"); + } + break; + } + p = std::get_if(&it->second.v); + if (!p) + luaL_argerror(L, i, "trying to index a non-table LuaVal"); + } + auto kk = AsLuaVal(L, arguments - 1); + auto vv = AsLuaVal(L, arguments); + if (std::holds_alternative(kk.v)) + luaL_argerror(L, arguments - 1, "trying to use nil as key"); + if (std::holds_alternative(vv.v)) { + (**p).erase(kk); + } + else { + auto const& [it, _] = (**p).insert_or_assign(std::move(kk), std::move(vv)); + return it->second.asObject(L); + } + return 0; +} + +std::string LuaVal::to_string_map(MapType const* ptr) +{ + std::string out = "[\n"; + for (std::pair const& pair : *ptr) + out += " { key: " + pair.first.to_string() + ", value: " + pair.second.to_string() + " },\n"; + out += ']'; + return out; +} + +int LuaVal::lua_to_string(lua_State* L) +{ + LuaVal* self = GetCheckLuaVal(L, 1); + std::string str = self->to_string(); + lua_pushlstring(L, str.c_str(), str.size()); + return 1; +} + +int LuaVal::lua_gc(lua_State* L) +{ + LuaVal* self = GetCheckLuaVal(L, 1); + self->~LuaVal(); + return 0; +} + +int LuaVal::asObject(lua_State* L) const +{ + return std::visit([&](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + lua_pushnil(L); + return 1; + } + else if constexpr (std::is_same_v) { + lua_pushlstring(L, arg.c_str(), arg.size()); + return 1; + } + else if constexpr (std::is_same_v) { + return PushLuaVal(L, *this); + } + else if constexpr (std::is_same_v) { + lua_pushboolean(L, arg); + return 1; + } + else if constexpr (std::is_same_v) { + lua_pushnumber(L, arg); + return 1; + } + else { + static_assert(always_false::value, "non-exhaustive visitor!"); + } + }, v); +} + +int LuaVal::asLua(lua_State* L, unsigned int depth) const +{ + WrappedMap const* p = std::get_if(&v); + if (p) + { + lua_newtable(L); + for (auto& it : **p) { + if (depth == 1) { + it.first.asObject(L); + it.second.asObject(L); + lua_rawset(L, -3); + } + else if (depth == 0) { + it.first.asLua(L, depth); + it.second.asLua(L, depth); + lua_rawset(L, -3); + } + else { + it.first.asLua(L, depth - 1); + it.second.asLua(L, depth - 1); + lua_rawset(L, -3); + } + } + return 1; + } + return asObject(L); +} + +int LuaVal::lua_asLua(lua_State* L) +{ + LuaVal* self = GetCheckLuaVal(L, 1); + int depth = luaL_optinteger(L, 2, 0); + return self->asLua(L, static_cast(depth)); +} + +LuaVal LuaVal::AsLuaVal(lua_State* L, int index) +{ + auto t = lua_type(L, index); + switch (t) + { + case LUA_TBOOLEAN: + return LuaVal(static_cast(lua_toboolean(L, index))); + case LUA_TNIL: + return LuaVal(); + case LUA_TNUMBER: + return LuaVal(lua_tonumber(L, index)); + case LUA_TSTRING: { + size_t len; + const char* str = lua_tolstring(L, index, &len); + return LuaVal(std::string(str, len)); + } + case LUA_TTABLE: + return FromTable(L, index); + case LUA_TUSERDATA: + if (LuaVal* ptr = GetLuaVal(L, index)) + return ptr->reference(); + break; + } + luaL_argerror(L, index, "Trying to use unsupported type"); + return LuaVal(); +} + +int LuaVal::lua_AsLuaVal(lua_State* L) +{ + return PushLuaVal(L, AsLuaVal(L, 1)); +} + +LuaVal LuaVal::FromTable(lua_State* L, int index) +{ + // Assumed we know index is a table already + LuaVal m({}); + auto& t = std::get(m.v); + int top = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, index) != 0) { + t->emplace(AsLuaVal(L, top + 1), AsLuaVal(L, top + 2)); + lua_pop(L, 1); + } + return m; +} + +size_t LuaValHash(LuaVal const& k) +{ + return std::hash{}(k.v); +} diff --git a/src/server/game/LuaEngine/LuaValue.h b/src/server/game/LuaEngine/LuaValue.h new file mode 100644 index 0000000000..fdb654efe5 --- /dev/null +++ b/src/server/game/LuaEngine/LuaValue.h @@ -0,0 +1,153 @@ +// BSD-3-Clause +// Copyright (c) 2022, Rochet2 +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include // std::unordered_map +#include // std::to_string, std::string +#include // std::monostate, std::variant, std::visit +#include // std::unique_ptr, std::shared_ptr +#include // std::decay_t, std::is_same_v, std::false_type +#include // std::initializer_list +#include // std::pair, std::make_pair, std::move + +constexpr const char* LUAVAL_MT_NAME = "LuaVal"; +class LuaVal; +struct lua_State; + +size_t LuaValHash(LuaVal const& k); + +namespace std { + + template <> + struct hash + { + std::size_t operator()(const LuaVal& k) const + { + return LuaValHash(k); + } + }; +} + +class LuaVal +{ +public: + typedef std::unordered_map MapType; + typedef std::monostate NIL; + template struct always_false : std::false_type {}; + typedef std::shared_ptr WrappedMap; + typedef std::variant LuaValVariant; + + static int lua_get(lua_State* L); + static int lua_set(lua_State* L); + + static std::string to_string_map(MapType const* ptr); + int asObject(lua_State* L) const; + int asLua(lua_State* L, unsigned int depth) const; + static LuaVal AsLuaVal(lua_State* L, int index); + static LuaVal FromTable(lua_State* L, int index); + static int lua_asLua(lua_State* L); + static int lua_AsLuaVal(lua_State* L); + static int lua_gc(lua_State* L); + static int lua_to_string(lua_State* L); + + static LuaVal* GetLuaVal(lua_State* L, int index); + static LuaVal* GetCheckLuaVal(lua_State* L, int index); + static int PushLuaVal(lua_State* L, LuaVal const& lv); + static void Register(lua_State* L); + + bool operator<(LuaVal const& b) const + { + return v < b.v; + } + + bool operator==(LuaVal const& b) const + { + return v == b.v; + } + + std::string to_string() const + { + return std::visit([](auto&& arg) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) + return "nil"; + else if constexpr (std::is_same_v) + return arg; + else if constexpr (std::is_same_v) + return LuaVal::to_string_map(arg.get()); + else if constexpr (std::is_same_v) + return arg ? "true" : "false"; + else if constexpr (std::is_same_v) + return std::to_string(arg); + else + static_assert(always_false::value, "non-exhaustive visitor!"); + }, v); + } + + LuaVal() : v() {} + LuaVal(std::string const& s) : v(s) {} + LuaVal(bool b) : v(b) {} + LuaVal(double d) : v(d) {} + LuaVal(MapType const& t) : v(std::make_shared(t)) {} + LuaVal(std::initializer_list /* MapType::value_type */> const& l) : v(std::make_shared(l)) {} + + LuaVal(LuaVal&& b) noexcept : v(std::move(b.v)) { + } + LuaVal(const LuaVal& b) : v(b.v) { + } + ~LuaVal() { + } + LuaVal& operator=(LuaVal&& b) noexcept + { + v = std::move(b.v); + return *this; + } + LuaVal& operator=(const LuaVal& b) { + v = b.v; + return *this; + } + LuaVal clone() const { + LuaVal lv; + lv.v = std::visit([&](auto&& arg) -> LuaValVariant + { + using T = std::decay_t; + if constexpr (std::is_same_v) + return std::make_shared(*arg); + else + return arg; + }, v); + return lv; + } + LuaVal reference() const { + return *this; + } + + LuaValVariant v; +}; diff --git a/src/server/game/LuaEngine/README.md b/src/server/game/LuaEngine/README.md new file mode 100644 index 0000000000..b07171c2d1 --- /dev/null +++ b/src/server/game/LuaEngine/README.md @@ -0,0 +1,59 @@ +### [![Eluna](docs/Eluna.png)](https://github.com/ElunaLuaEngine/Eluna) +# Eluna Lua Engine + +__Eluna Lua Engine__ is an embedded Lua scripting engine designed for World of Warcraft emulators. It provides powerful scripting capabilities and supports several popular emulators, including MaNGOS, CMaNGOS and TrinityCore. + +We are continually working to improve Eluna's functionality and performance, and strive to deliver an extensive, intuitive and unified scripting experience across emulators. + +If you encounter any issues during installation or while working on scripts, please feel free to [open an issue](https://github.com/ElunaLuaEngine/Eluna/issues) or join our community Discord server. + +## Community + +Join the official Eluna Discord server to connect with other community members, access resources and releases, and receive support. + + + + + +## Documentation + +For comprehensive information on using Eluna, please refer to the resources below: + +* [Eluna API Documentation](http://elunaluaengine.github.io/) – Detailed API documentation. +* [Lua Reference Manual](http://www.lua.org/manual/5.2/) – Official Lua 5.2 reference manual. + +### Additional Resources + +* [Installation Guide](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/INSTALL.md) – Step-by-step installation instructions. +* [Getting Started](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/USAGE.md) – Basic usage and examples. +* [Eluna Features](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/IMPL_DETAILS.md) – Overview of key features and implementation details. +* [Hook Documentation](https://github.com/ElunaLuaEngine/Eluna/blob/master/hooks/Hooks.h) – Documentation of available hooks. +* [Example Scripts](https://github.com/ElunaLuaEngine/Scripts) – Sample scripts to get you started. +* [Contributing Guide](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/CONTRIBUTING.md) – Instructions for contributing to Eluna. + +## Source + +The Eluna source code is available on GitHub: + +- [Eluna Source](https://github.com/ElunaLuaEngine/Eluna) + +### Emulator sources and forks + +Below are the emulator sources and specific forks that include the required modifications for Eluna compatibility: + +- **TrinityCore with Eluna** - Maintained by us! + - [WotLK](https://github.com/ElunaLuaEngine/ElunaTrinityWotlk) [![automerge](https://github.com/ElunaLuaEngine/ElunaTrinityWotlk/actions/workflows/auto-merge.yml/badge.svg)](https://github.com/ElunaLuaEngine/ElunaTrinityWotlk/actions/workflows/auto-merge.yml) + +- **MaNGOS with Eluna** + - [Vanilla](https://github.com/mangoszero/server) + - [TBC](https://github.com/mangosone/server) + - [WoTLK](https://github.com/mangostwo/server) + +- **cMaNGOS with Eluna** – Maintained by __[Niam5](https://github.com/Niam5)__ + - [Vanilla](https://github.com/Niam5/Eluna-CMaNGOS-Classic) + - [TBC](https://github.com/Niam5/Eluna-CMaNGOS-TBC) + - [WoTLK](https://github.com/Niam5/Eluna-CMaNGOS-WotLK) + +## License + +This project is licensed under the terms described in the [LICENSE](https://github.com/ElunaLuaEngine/Eluna/blob/master/LICENSE) file. diff --git a/src/server/game/LuaEngine/docs/.gitignore b/src/server/game/LuaEngine/docs/.gitignore new file mode 100644 index 0000000000..3fd1ebacb8 --- /dev/null +++ b/src/server/game/LuaEngine/docs/.gitignore @@ -0,0 +1,2 @@ +# Ignore the temporary "build" folder. +build \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/CONTRIBUTING.md b/src/server/game/LuaEngine/docs/CONTRIBUTING.md new file mode 100644 index 0000000000..2a287f4c80 --- /dev/null +++ b/src/server/game/LuaEngine/docs/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contributing +Eluna uses C for the Lua engine, C++ for the server modifications and system code, Lua for scripting side code and scripts, python for the web documentation generation - but you do not have to be able to code to help. + +You can contribute to Eluna in various ways: +* Improve our documentation: [Documentation generation](DOC_GEN.md) +* Create new features or enhance old features: [Eluna source](https://github.com/ElunaLuaEngine/Eluna) +* Notify us about your concerns, problems and needs regarding Eluna: [Issue tracker](https://github.com/ElunaLuaEngine/Eluna/issues) +* Create and improve Lua scripts, systems, releases and guides: [Eluna forum section](https://www.getmangos.eu/forums/forum/118-eluna-lua-engine/) + +### Features and documentation +To contribute to the source code and documentation within it, create a pull request for our github repository: + +1. [Set up git](https://help.github.com/articles/set-up-git/) +2. [Fork](https://help.github.com/articles/fork-a-repo/) our repository: [Eluna repository](https://github.com/ElunaLuaEngine/Eluna) +3. Create a branch: `git checkout -b mybranch` +4. Make your contribution changes +5. Commit your changes `git commit -a -m "commit message"` +6. Push your commit to github: `git push` +7. Open a [pull request](https://help.github.com/articles/using-pull-requests/) diff --git a/src/server/game/LuaEngine/docs/DOC_GEN.md b/src/server/game/LuaEngine/docs/DOC_GEN.md new file mode 100644 index 0000000000..6acac22bbf --- /dev/null +++ b/src/server/game/LuaEngine/docs/DOC_GEN.md @@ -0,0 +1,165 @@ +# Documentation generation +Eluna uses a custom made documentation generator to create it's [web documentation](http://elunaluaengine.github.io/). +The generator is written in python by Patman. It works by parsing Eluna's source files for comments and then generates the HTML and javascript for the documentation based on them. + +This page guides you through generating the web documentation locally and explains the standards of the documentation comments for you to help us improve our documentation. To contribute with your documentation changes, create a [pull request](https://help.github.com/articles/using-pull-requests/) + +# Generating locally +- install [python](https://www.python.org/)(2) + - when installing, tick to install the path variable + - you may need restart afterwards for the installation to properly take effect +- install a package manager like [pip](https://pip.pypa.io/en/latest/) + - if you installed pip and it does not work, restart or try easy_install command +- install the dependencies with manager + - [Jinja2](https://pypi.python.org/pypi/Jinja2) + - [typedecorator](https://pypi.python.org/pypi/typedecorator) + - [markdown](https://pypi.python.org/pypi/Markdown) +- Run in cmd `python -m ElunaDoc` when at `\LuaEngine\docs\` + +# Documenting +You can document functions in the Eluna source code. To find examples simply open a method header file like `PlayerMethods.h` and see the comments. + +## Templates +Here are some basic templates for a function documentation. When defining a parameter or a return value the type and value name are mandatory, unless the parameter type is `...`, which is used for variable arguments; do not include a name in this case. + +```c++ +/** + * Short description (about 80 characters long). + * + * @param Type paramName + * @return Type returnName + */ +``` + +```c++ +/** + * Short description (about 80 characters long). + * + * @param Type paramName = defaultValue : parameter description + * @return Type returnName : return value description + */ +``` + +This is a template for a function that takes in different parameters. When defining a parameter or a return value, the type and value name are mandatory. + +```c++ +/** + * Short description (about 80 characters long). + * + * @proto returnValue = (object) + * @proto returnValue = (x, y, z) + * @param [WorldObject] object = defaultValue : parameter description + * @param float x = defaultValue : parameter description + * @param float y = defaultValue : parameter description + * @param float z = defaultValue : parameter description + * @return Type returnName : return value description + */ +``` + +## Standard +A documentation comment block will always start with `/**` and end with `*/`. +All lines start with `*` character followed by one space before any content. + +The first paragrph is used as a short description of the function/class, so it should be kept to about 80 characters. The other paragraphs can be as long as desired. + +All paragraphs in the description (including the first) should start with a capital letter and end with a period. +**Paragraphs must be separated by an empty line**, e.g.: + +```c++ +/** + * This is a short description (about 80 characters). + * + * Here's another paragraph with more info. NOTE THE EMPTY LINE BETWEEN THE PARAGRAPHS. + * This does need to be short, and this line is still part of the same paragraph because + * there is no empty line. + */ + ``` + +The parameter and return value descriptions should start with a lowercase letter and not end with a period. If more than one sentence is needed, start the *first* without a capital letter and end the *last* without a period. + +Any class, enum or function can be referenced (made a link to) with square brackets. +`[Player]` will reference a player. `[WeatherType]` will reference an enum. `[Player:GetName]` will reference a function. + +Use correct indentation with documentation comments. + +```c++ +/** + * Correct indentation. + */ +``` + +```c++ +/** +* Invalid indentation. +*/ +``` + +## Markdown +You can use [markdown](http://pythonhosted.org//Markdown/) in your descriptions. +For syntax see http://daringfireball.net/projects/markdown/syntax and http://pythonhosted.org//Markdown/#differences + +``` +/** + * Description. + * + * - list item + * - list item + * - list item + * + * + * // Codeblock + * // Code goes here. + * // Note the 4-space indent. + * + * + * `code line` + * + * *italic* + * **bold** + */ +``` + +**The above markdown code produces the output below:** + +Description. + +- list item +- list item +- list item + +``` +// Codeblock +// Code goes here. +// Note the 4-space indent. +``` + +`code line` + +*italic* +**bold** + +## Types +Here are some examples of possible types and most commonly used ones: + +``` +string +uint64 +uint32 +uint16 +uint8 +int64 +int32 +int16 +int8 +double +float +... +[EnumName] +[Player] +[Creature] +[GameObject] +[Item] +[Unit] +[WorldObject] +[Object] +``` diff --git a/src/server/game/LuaEngine/docs/Eluna.png b/src/server/game/LuaEngine/docs/Eluna.png new file mode 100644 index 0000000000..b450d336e2 Binary files /dev/null and b/src/server/game/LuaEngine/docs/Eluna.png differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/.gitignore b/src/server/game/LuaEngine/docs/ElunaDoc/.gitignore new file mode 100644 index 0000000000..b32d8aa7ac --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/.gitignore @@ -0,0 +1,2 @@ +*.pyc +.idea \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/__init__.py b/src/server/game/LuaEngine/docs/ElunaDoc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/__main__.py b/src/server/game/LuaEngine/docs/ElunaDoc/__main__.py new file mode 100644 index 0000000000..c3315bdae6 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/__main__.py @@ -0,0 +1,272 @@ +import os +import shutil +import typing +import glob +import time +import re +from jinja2 import Environment, FileSystemLoader +from typedecorator import params, returns + +from ElunaDoc.parser import ClassParser + + +@returns([(str, typing.IO)]) +@params(search_path=str) +def find_class_files(search_path): + """Find and open all files containing Eluna class methods in `search_path`.""" + old_dir = os.getcwd() + os.chdir(search_path) + method_file_names = [file_name for file_name in glob.glob("*Methods.h") if file_name != "BigIntMethods.h"] + method_files = [open(file_name, "r", encoding="utf-8") for file_name in method_file_names] + os.chdir(old_dir) + return method_files + + +def make_renderer(template_path, link_parser_factory): + """Return a function that can be used to render Jinja2 templates from the `template_path` directory.""" + env = Environment(loader=FileSystemLoader(template_path)) + + def inner(template_name, output_path, level, **kwargs): + env.filters["parse_links"], env.filters["parse_data_type"] = link_parser_factory(level) + template = env.get_template(template_name) + static = make_static(level) + root = make_root(level) + + with open("build/" + output_path, "w", encoding="utf-8") as out: + out.write(template.render(level=level, static=static, root=root, **kwargs)) + + return inner + + +def make_static(level): + return lambda file_name: ("../" * level) + "static/" + file_name + + +def make_root(level): + return lambda file_name: ("../" * level) + file_name + + +# ---------------- Hooks.h parsing (events..) ---------------- + +_macro_start = re.compile(r"^\s*#define\s+([A-Z0-9_]+)_EVENTS_LIST\(X\)\s*\\\s*$") +_macro_item = re.compile(r'^\s*X\(\s*([A-Z0-9_]+)\s*,\s*([0-9]+)\s*,\s*"([^"]+)"\s*\)\s*\\?\s*$') + +# Capture: { "category", SomeEventsTable, CountOf(SomeEventsTable) }, +_hook_table_entry = re.compile( + r'^\s*\{\s*"([^"]+)"\s*,\s*([A-Za-z0-9_]+)\s*,\s*CountOf\(\2\)\s*\}\s*,?\s*$' +) + + +def _table_name_to_macro_key(events_table_name: str) -> str: + """ + Converts 'InstanceEventsTable' -> 'instance' + 'GameObjectEventsTable' -> 'gameobject' + 'PacketEventsTable' -> 'packet' + """ + suffix = "EventsTable" + if events_table_name.endswith(suffix): + return events_table_name[: -len(suffix)].lower() + return events_table_name.lower() + + +def parse_macro_lists(hooks_h_path: str) -> dict[str, dict[str, dict]]: + """ + Return mapping per macro_category (derived from *_EVENTS_LIST macro name): + { + "spell": { + "by_id": { 1: "on_cast", ... }, + "by_enum": { "SPELL_EVENT_ON_CAST": (1, "on_cast"), ... } + }, + ... + } + """ + hooks: dict[str, dict[str, dict]] = {} + current: str | None = None + + with open(hooks_h_path, "r", encoding="utf-8") as f: + for line in f: + m = _macro_start.match(line) + if m: + current = m.group(1).lower() # e.g. SPELL -> "spell", INSTANCE -> "instance" + hooks.setdefault(current, {"by_id": {}, "by_enum": {}}) + continue + + if current: + mi = _macro_item.match(line) + if mi: + enum_name = mi.group(1) + id_value = int(mi.group(2)) + lua_name = mi.group(3) + hooks[current]["by_id"][id_value] = lua_name + hooks[current]["by_enum"][enum_name] = (id_value, lua_name) + continue + + # End macro block when we hit a line not continuing with '\' + if not line.rstrip().endswith("\\"): + current = None + + return hooks + + +def build_exported_hook_map(hooks_h_path: str) -> dict[str, dict]: + """ + Builds a map keyed by exported Lua category from HookTypeTable. + + IMPORTANT: the macro key is derived from the backing EventsTable name, not the exported category. + Example: + { "map", InstanceEventsTable, CountOf(InstanceEventsTable) } + -> macro key = "instance" -> INSTANCE_EVENTS_LIST + """ + macro_lists = parse_macro_lists(hooks_h_path) + + exported: dict[str, dict] = {} + in_hook_table = False + + with open(hooks_h_path, "r", encoding="utf-8") as f: + for line in f: + if "static constexpr HookStorage HookTypeTable" in line: + in_hook_table = True + continue + if in_hook_table and "};" in line: + in_hook_table = False + continue + if not in_hook_table: + continue + + m = _hook_table_entry.match(line) + if not m: + continue + + exported_category = m.group(1) # e.g. "map" + events_table_name = m.group(2) # e.g. "InstanceEventsTable" + + macro_key = _table_name_to_macro_key(events_table_name) # e.g. "instance" + table_map = macro_lists.get(macro_key) + + if not table_map: + exported[exported_category] = {"by_id": {}, "by_enum": {}} + print( + f"[docs] Warning: HookTypeTable exports '{exported_category}' ({events_table_name}) " + f"but no matching *_EVENTS_LIST was found for key '{macro_key}'" + ) + continue + + exported[exported_category] = table_map + + return exported + + +# ---------------- Main ---------------- + +if __name__ == "__main__": + # Recreate the build folder and copy static files over. + if os.path.exists("build"): + shutil.rmtree("build") + os.mkdir("build") + shutil.copytree("ElunaDoc/static", "build/static") + + # Load hook globals (events..) from Hooks.h + hooks_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", "hooks", "Hooks.h")) + print(f"Loading hook globals from: {hooks_path}") + exported_hooks = build_exported_hook_map(hooks_path) + + # Load up all files with methods we need to parse. + print("Finding Eluna method files...") + class_files = find_class_files("../methods/TrinityCore/") + + # Parse all the method files. + classes = [] + for f in class_files: + print(f"Parsing file {f.name}...") + classes.append(ClassParser.parse_file(f, exported_hooks=exported_hooks)) + f.close() + + # Sort the classes so they are in the correct order in lists. + classes.sort(key=lambda c: c.name) + + def make_parsers(level): + """Returns filters that parse refs to other classes/methods/enums and insert links.""" + class_names = [] + method_names = [] + + for class_ in classes: + class_names.append("[" + class_.name + "]") + for method in class_.methods: + method_names.append("[" + class_.name + ":" + method.name + "]") + + def link_parser(content): + # Replace [Class:Method] and [Class] with links + for name in method_names: + full_name = name[1:-1] + class_name, method_name = full_name.split(":") + url = "{}{}/{}.html".format(("../" * level), class_name, method_name) + content = content.replace(name, '{}'.format(url, full_name)) + + for name in class_names: + class_name = name[1:-1] + url = "{}{}/index.html".format(("../" * level), class_name) + content = content.replace(name, '{}'.format(url, class_name)) + + return content + + # Links to the "Programming in Lua" documentation for each Lua type. + lua_type_documentation = { + "nil": "http://www.lua.org/pil/2.1.html", + "boolean": "http://www.lua.org/pil/2.2.html", + "number": "http://www.lua.org/pil/2.3.html", + "string": "http://www.lua.org/pil/2.4.html", + "table": "http://www.lua.org/pil/2.5.html", + "function": "http://www.lua.org/pil/2.6.html", + "...": "http://www.lua.org/pil/5.2.html", + } + + def data_type_parser(content): + # If the type is a Lua type, return a link to Lua documentation. + if content in lua_type_documentation: + url = lua_type_documentation[content] + return '{}'.format(url, content) + + # Otherwise try to build a link to the proper page. + if content in class_names: + class_name = content[1:-1] + url = "{}{}/index.html".format(("../" * level), class_name) + return '{}'.format(url, class_name) + + # Case for enums to direct to a search on github + enum_name = content[1:-1] + url = ( + 'https://github.com/ElunaLuaEngine/ElunaTrinityWotlk/search?l=cpp&q=%22enum+{}%22' + "&type=Code&utf8=%E2%9C%93" + ).format(enum_name) + return '{}'.format(url, enum_name) + + return link_parser, data_type_parser + + # Create the render function with the template path and parser maker. + render = make_renderer("ElunaDoc/templates", make_parsers) + + # Render the index. + render("index.html", "index.html", level=0, classes=classes) + # Render the search index. + render("search-index.js", "search-index.js", level=0, classes=classes) + # Render the date. + render("date.js", "date.js", level=0, currdate=time.strftime("%d/%m/%Y")) + + for class_ in classes: + print(f"Rendering pages for class {class_.name}...") + + # Make a folder for the class. + os.mkdir("build/" + class_.name) + index_path = "{}/index.html".format(class_.name) + sidebar_path = "{}/sidebar.js".format(class_.name) + + # Render the class's index page. + render("class.html", index_path, level=1, classes=classes, current_class=class_) + + # Render the class's sidebar script. + render("sidebar.js", sidebar_path, level=1, classes=classes, current_class=class_) + + # Render each method's page. + for method in class_.methods: + method_path = "{}/{}.html".format(class_.name, method.name) + render("method.html", method_path, level=1, current_class=class_, current_method=method) \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/parser.py b/src/server/game/LuaEngine/docs/ElunaDoc/parser.py new file mode 100644 index 0000000000..536a524938 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/parser.py @@ -0,0 +1,506 @@ +import re +import typing +import markdown +from typedecorator import params, returns, Nullable +from typing import Any, Dict, List, Optional, Tuple, TypedDict + + +class TableDict(TypedDict): + columns: List[str] + values: List[List[Any]] + + +class ParameterDoc(object): + """The documentation data of a parameter or return value for an Eluna method.""" + + # The integer ranges that each C++ type is valid for. None means valid for all numbers. + valid_ranges = { + "float": None, + "double": None, + "int": ("-2,147,483,647", "2,147,483,647"), # safe assumption 32-bit + "int8": ("-127", "127"), + "uint8": ("0", "255"), + "int16": ("-32,767", "32,767"), + "uint16": ("0", "65,535"), + "int32": ("-2,147,483,647", "2,147,483,647"), + "uint32": ("0", "4,294,967,295"), + "int64": ("-9,223,372,036,854,775,808", "9,223,372,036,854,775,807"), + "uint64": ("0", "18,446,744,073,709,551,615"), + "ObjectGuid": ("0", "18,446,744,073,709,551,615"), + } + + @params(self=object, name=Nullable(str), data_type=str, description=str, default_value=Nullable(str)) + def __init__(self, name, data_type, description, default_value=None): + """If `name` is not provided, the Parameter is a returned value instead of a parameter.""" + self.name = name + self.data_type = data_type + self.default_value = default_value + + if self.data_type == "...": + self.name = "..." + else: + assert self.name is not None + + if description: + # Capitalize the first letter, add a period, and parse as Markdown. + self.description = "{}{}. ".format(description[0].capitalize(), description[1:]) + self.description = markdown.markdown(self.description) + else: + self.description = "" + + # If the data type is a C++ number, convert to Lua number and add range info to description. + if self.data_type in self.valid_ranges.keys(): + range_ = ParameterDoc.valid_ranges[self.data_type] + if range_: + self.description += '

Valid numbers: integers from {0} to {1}.

'.format( + range_[0], range_[1] + ) + else: + self.description += "

Valid numbers: all decimal numbers.

" + + self.data_type = "number" + + elif self.data_type == "bool": + self.data_type = "boolean" + + elif self.data_type == "int64" or self.data_type == "uint64": + self.data_type = "[" + self.data_type + "]" + + elif ( + self.data_type not in ["nil", "boolean", "number", "string", "table", "function", "..."] + and self.data_type[:1] != "[" + ): + print(f"Missing angle brackets [] around the data type name: `{self.data_type}`") + + +def _apply_hook_lua_names_to_tables( + tables: List[TableDict], + hook_category: Optional[str], + exported_hooks: Optional[Dict[str, Dict[str, Dict]]], +) -> List[TableDict]: + """ + Rewrites the first value in each @values row to the Lua global: + events.. + and keeps the numeric id as a tooltip by using dict format: + { "events.category.name": "123" } + + This function ONLY runs when hook_category is provided via @hook. + + It supports row[0] being either: + - a numeric id (e.g. "1") + - an enum token (e.g. "SPELL_EVENT_ON_CAST") + """ + if not tables or not hook_category or not exported_hooks: + return tables + + cat = exported_hooks.get(hook_category) + if not cat: + return tables + + by_id = cat.get("by_id", {}) + by_enum = cat.get("by_enum", {}) + + for t in tables: + for row in t.get("values", []): + if not row: + continue + + key = row[0] + + # 1) Numeric ID style + id_int: Optional[int] = None + lua_name: Optional[str] = None + try: + id_int = int(key) + lua_name = by_id.get(id_int) + except (TypeError, ValueError): + pass + + # 2) Enum token style + if lua_name is None and isinstance(key, str): + enum_info = by_enum.get(key) + if enum_info: + id_int, lua_name = enum_info # (int, str) + + if lua_name is None or id_int is None: + continue + + lua_global = f"events.{hook_category}.{lua_name}" + row[0] = {lua_global: str(id_int)} + + return tables + + +class MethodDoc(object): + """The documentation data of an Eluna method.""" + + @params( + self=object, + name=str, + description=str, + tables=[TableDict], + prototypes=[str], + warning=[str], + parameters=[ParameterDoc], + returned=[ParameterDoc], + hook_category=Nullable(str), + exported_hooks=Nullable(object), + ) + def __init__( + self, + name: str, + description: str, + tables: List[TableDict], + prototypes: List[str], + warning: str, + parameters: List[ParameterDoc], + returned: List[ParameterDoc], + hook_category: Optional[str] = None, + exported_hooks: Optional[Dict[str, Dict[str, Dict]]] = None, + ): + self.name = name + self.prototypes = prototypes + self.parameters = parameters + self.returned = returned + self.hook_category = hook_category + + # If this method has @hook, rewrite its table Event column to events.. + if tables and hook_category: + tables = _apply_hook_lua_names_to_tables(tables, hook_category, exported_hooks) + + # Convert any parsed tables into HTML (Markdown tables → HTML) + if tables: + html_tables = [] + for table in tables: + md_table = "| " + " | ".join(table["columns"]) + " |\n" + md_table += "| " + " | ".join(["---"] * len(table["columns"])) + " |\n" + + for row in table["values"]: + md_row = "| " + for value in row: + if isinstance(value, dict): + md_row += self._format_dict_values(value) + else: + md_row += str(value) + md_row += " | " + md_table += md_row + "\n" + + html_table = markdown.markdown(md_table, extensions=["tables"]) + html_tables.append(html_table) + + self.tables = "".join(html_tables) + else: + self.tables = "" + + # Parse the description as Markdown. + self.description = markdown.markdown(description) + # Pull the first paragraph out of the description as the short description. + self.short_description = self.description.split("

")[0][3:] + # If it has a description, it is "documented". + self.documented = self.description != "" + # Parse the warning as Markdown, but remove

tags + self.warning = markdown.markdown(warning) + self.warning = re.sub(r"^

(.*?)

$", r"\1", self.warning, flags=re.DOTALL) + + """Helper function to parse table dictionaries. Only used in Register methods for now.""" + + def _format_dict_values(self, d: Dict[str, str]) -> str: + html_parts = [] + for key, value in d.items(): + html_parts.append(f'{key}') + return ", ".join(html_parts) + + +class MangosClassDoc(object): + """The documentation of a MaNGOS class that has Lua methods.""" + + @params(self=object, name=str, description=str, methods=[MethodDoc]) + def __init__(self, name, description, methods): + self.name = name + # Parse the description as Markdown. + self.description = markdown.markdown(description) + # Pull the first paragraph out of the description as the short description. + self.short_description = self.description.split("

")[0][3:] + # Sort the methods by their names. + self.methods = sorted(methods, key=lambda m: m.name) + + # If any of our methods are not documented, we aren't fully documented. + for method in methods: + if not method.documented: + self.fully_documented = False + break + else: + self.fully_documented = True + + # If any of our methods are documented, we aren't fully undocumented. + for method in methods: + if method.documented: + self.fully_undocumented = False + break + else: + self.fully_undocumented = True + + +class ClassParser(object): + """Parses a file line-by-line and returns methods when enough information is received to build them.""" + + # Class documentation + class_start_regex = re.compile(r"\s*/\*\*\*") + class_body_regex = re.compile(r"\s*\*\s*(.*)") + class_end_regex = re.compile(r"\s*\*/") + + # Method documentation + start_regex = re.compile(r"\s*/\*\*") + body_regex = re.compile(r"\s*\s?\*\s?(.*)") + + # Hook tag (explicit only) + hook_regex = re.compile(r"\s*\*\s@hook\s+(\w+)") + + # Table parsing + table_regex = re.compile(r"\s*\*\s@table") + table_columns_regex = re.compile(r"\s*\*\s@columns\s*\[(.+)\]") + table_values_regex = re.compile(r"\s*\*\s@values\s*\[(.+?)\]") + + # Warnings + warning_regex = re.compile(r"""\s*\*\s*@warning\s+(.+)""", re.X) + + param_regex = re.compile( + r"""\s*\*\s@param\s + ([^\s]+)\s(\w+)? # type + name + (?:\s=\s([^\s:]+))? # default + (?:\s:\s(.+))? # description + """, + re.X, + ) + + return_regex = re.compile( + r"""\s*\*\s@return\s + ([\[\]\w]+)\s(\w+) + (?:\s:\s(.+))? + """, + re.X, + ) + + proto_regex = re.compile( + r"""\s*\*\s@proto\s + ([\w\s,]+)? # args + (?:=\s)? # equals separator + (?:\(([\w\s,]+)\))? # returns + """, + re.X, + ) + + comment_end_regex = re.compile(r"\s*\*/") + end_regex = re.compile(r"\s*int\s(\w+)\s*\(") + + @params(self=object, class_name=str, exported_hooks=Nullable(object)) + def __init__(self, class_name: str, exported_hooks: Optional[Dict[str, Dict[str, Dict]]] = None): + assert ClassParser.class_body_regex is not ClassParser.body_regex + self.methods: List[MethodDoc] = [] + self.class_name = class_name + self.class_description = "" + self.exported_hooks = exported_hooks + self.reset() + + def reset(self): + self.last_regex = None + + self.description = "" + self.warning = "" + self.params: List[ParameterDoc] = [] + self.returned: List[ParameterDoc] = [] + self.method_name: Optional[str] = None + self.prototypes: List[str] = [] + self.tables: List[TableDict] = [] + + # Explicit only; NO inference. + self.hook_category: Optional[str] = None + + def handle_class_body(self, match): + self.class_description += match.group(1) + "\n" + + def handle_body(self, match): + self.description += match.group(1) + "\n" + + def handle_hook(self, match): + self.hook_category = match.group(1).lower() + + def handle_table(self, _match): + self.tables.append({"columns": [], "values": []}) + + def handle_table_columns(self, match): + if self.tables: + self.tables[-1]["columns"] = match.group(1).split(", ") + + def handle_table_values(self, match): + if not self.tables: + return + + # Split values while respecting quoted strings and <> blobs. + values = re.findall(r'(?:[^,<>"]|"(?:\\.|[^"])*"|<[^>]*>)+', match.group(1)) + processed_values: List[Any] = [] + + for value in values: + stripped_value = value.strip(' "') + + # Parse the content inside < > + if stripped_value.startswith("<") and stripped_value.endswith(">"): + inner_content = stripped_value[1:-1] + + # Convert inner key-value pairs to a dict + pair_regex = re.compile(r"(\w+):\s*([\w\s]+)") + stripped_value = dict(pair_regex.findall(inner_content)) + + processed_values.append(stripped_value) + + self.tables[-1]["values"].append(processed_values) + + def handle_warning(self, match): + self.warning += match.group(1) + + def handle_param(self, match): + data_type, name, default, description = match.group(1), match.group(2), match.group(3), match.group(4) + self.params.append(ParameterDoc(name, data_type, description, default)) + + def handle_return(self, match): + data_type, name, description = match.group(1), match.group(2), match.group(3) + self.returned.append(ParameterDoc(name, data_type, description)) + + def handle_proto(self, match): + return_values, parameters = match.group(1), match.group(2) + parameters = " " + parameters + " " if parameters else "" + return_values = return_values + "= " if return_values else "" + + if self.class_name == "Global": + prototype = "{0}{{0}}({1})".format(return_values, parameters) + else: + prototype = "{0}{1}:{{0}}({2})".format(return_values, self.class_name, parameters) + + self.prototypes.append(prototype) + + def handle_end(self, match): + self.method_name = match.group(1) + + def make_prototype(parameters: str) -> str: + if parameters != "": + parameters = " " + parameters + " " + + if self.class_name == "Global": + if self.returned: + return_values = ", ".join([param.name for param in self.returned]) + return "{0} = {1}({2})".format(return_values, self.method_name, parameters) + return "{0}({1})".format(self.method_name, parameters) + + if self.returned: + return_values = ", ".join([param.name for param in self.returned]) + return "{0} = {1}:{2}({3})".format(return_values, self.class_name, self.method_name, parameters) + return "{0}:{1}({2})".format(self.class_name, self.method_name, parameters) + + if not self.prototypes: + params_with_default = [] + last_non_default_i = 0 + simple_order = True + + for i, param in enumerate(self.params): + if param.default_value: + params_with_default.append(param) + else: + last_non_default_i = i + if params_with_default: + simple_order = False + + if not params_with_default or not simple_order: + parameters = ", ".join([param.name for param in self.params]) + self.prototypes.append(make_prototype(parameters)) + else: + for i in range(last_non_default_i, len(self.params)): + parameters = ", ".join([param.name for param in self.params[: i + 1]]) + self.prototypes.append(make_prototype(parameters)) + else: + self.prototypes = [proto.format(self.method_name) for proto in self.prototypes] + + self.methods.append( + MethodDoc( + self.method_name, + self.description, + self.tables, + self.prototypes, + self.warning, + self.params, + self.returned, + hook_category=self.hook_category, + exported_hooks=self.exported_hooks, + ) + ) + + regex_handlers = { + class_start_regex: None, + class_body_regex: handle_class_body, + class_end_regex: None, + start_regex: None, + body_regex: handle_body, + hook_regex: handle_hook, + table_regex: handle_table, + table_columns_regex: handle_table_columns, + table_values_regex: handle_table_values, + warning_regex: handle_warning, + param_regex: handle_param, + return_regex: handle_return, + proto_regex: handle_proto, + comment_end_regex: None, + end_regex: handle_end, + } + + next_regexes = { + None: [class_start_regex, start_regex, end_regex], + class_start_regex: [class_end_regex, class_body_regex], + class_body_regex: [class_end_regex, class_body_regex], + class_end_regex: [], + start_regex: [hook_regex, table_regex, warning_regex, param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + body_regex: [hook_regex, table_regex, warning_regex, param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + proto_regex: [hook_regex, table_regex, warning_regex, param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + table_regex: [hook_regex, table_regex, table_columns_regex, warning_regex, param_regex, return_regex, comment_end_regex, body_regex], + table_columns_regex: [hook_regex, table_values_regex, warning_regex, param_regex, return_regex, comment_end_regex, body_regex], + table_values_regex: [hook_regex, table_values_regex, table_regex, warning_regex, param_regex, return_regex, comment_end_regex, body_regex], + warning_regex: [hook_regex, table_values_regex, table_regex, warning_regex, param_regex, return_regex, comment_end_regex, body_regex], + param_regex: [hook_regex, table_regex, warning_regex, param_regex, return_regex, comment_end_regex, body_regex], + return_regex: [hook_regex, return_regex, comment_end_regex], + comment_end_regex: [end_regex], + end_regex: [], + hook_regex: [hook_regex, table_regex, warning_regex, param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + } + + @returns(Nullable(MethodDoc)) + @params(self=object, line=str) + def next_line(self, line): + valid_regexes = self.next_regexes[self.last_regex] + + for regex in valid_regexes: + match = regex.match(line) + if match: + handler = self.regex_handlers[regex] + if handler: + handler(self, match) + self.last_regex = regex + break + else: + self.reset() + + @returns(MangosClassDoc) + def to_class_doc(self): + return MangosClassDoc(self.class_name, self.class_description, self.methods) + + @staticmethod + @returns(MangosClassDoc) + @params(file=object, exported_hooks=Nullable(object)) + def parse_file(file, exported_hooks=None): + """Parse the file `file` into a documented class.""" + class_name = file.name[:-len("Methods.h")] + parser = ClassParser(class_name, exported_hooks=exported_hooks) + + line = file.readline() + while line: + parser.next_line(line) + line = file.readline() + + return parser.to_class_doc() \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/FiraSans-Medium.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/FiraSans-Medium.woff new file mode 100644 index 0000000000..5627227744 Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/FiraSans-Medium.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/FiraSans-Regular.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/FiraSans-Regular.woff new file mode 100644 index 0000000000..9ff40445bf Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/FiraSans-Regular.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/Heuristica-Italic.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/Heuristica-Italic.woff new file mode 100644 index 0000000000..b0cebf01de Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/Heuristica-Italic.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceCodePro-Regular.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceCodePro-Regular.woff new file mode 100644 index 0000000000..5576670903 Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceCodePro-Regular.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceCodePro-Semibold.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceCodePro-Semibold.woff new file mode 100644 index 0000000000..ca972a11dc Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceCodePro-Semibold.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceSerifPro-Bold.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceSerifPro-Bold.woff new file mode 100644 index 0000000000..ac1b1b3a0b Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceSerifPro-Bold.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceSerifPro-Regular.woff b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceSerifPro-Regular.woff new file mode 100644 index 0000000000..e8c43b852e Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/SourceSerifPro-Regular.woff differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/eluna-logo.png b/src/server/game/LuaEngine/docs/ElunaDoc/static/eluna-logo.png new file mode 100644 index 0000000000..0470a5a908 Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/eluna-logo.png differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/favicon.ico b/src/server/game/LuaEngine/docs/ElunaDoc/static/favicon.ico new file mode 100644 index 0000000000..9c83cadf1e Binary files /dev/null and b/src/server/game/LuaEngine/docs/ElunaDoc/static/favicon.ico differ diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/highlight.css b/src/server/game/LuaEngine/docs/ElunaDoc/static/highlight.css new file mode 100644 index 0000000000..157fdeff23 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/highlight.css @@ -0,0 +1,311 @@ +/*! + Theme: Mexico Light + Author: Sheldon Johnson + License: ~ MIT (or more permissive) [via base16-schemes-source] + Maintainer: @highlightjs/core-team + Version: 2021.09.0 +*/ +/* + WARNING: DO NOT EDIT THIS FILE DIRECTLY. + + This theme file was auto-generated from the Base16 scheme mexico-light + by the Highlight.js Base16 template builder. + + - https://github.com/highlightjs/base16-highlightjs +*/ +/* +base00 #f8f8f8 Default Background +base01 #e8e8e8 Lighter Background (Used for status bars, line number and folding marks) +base02 #d8d8d8 Selection Background +base03 #b8b8b8 Comments, Invisibles, Line Highlighting +base04 #585858 Dark Foreground (Used for status bars) +base05 #383838 Default Foreground, Caret, Delimiters, Operators +base06 #282828 Light Foreground (Not often used) +base07 #181818 Light Background (Not often used) +base08 #ab4642 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted +base09 #dc9656 Integers, Boolean, Constants, XML Attributes, Markup Link Url +base0A #f79a0e Classes, Markup Bold, Search Text Background +base0B #538947 Strings, Inherited Class, Markup Code, Diff Inserted +base0C #4b8093 Support, Regular Expressions, Escape Characters, Markup Quotes +base0D #7cafc2 Functions, Methods, Attribute IDs, Headings +base0E #96609e Keywords, Storage, Selector, Markup Italic, Diff Changed +base0F #a16946 Deprecated, Opening/Closing Embedded Language Tags, e.g. +*/ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +code.hljs { + padding: 3px 5px +} +.hljs { + color: #383838; + background: #f8f8f8 +} +.hljs::selection, +.hljs ::selection { + background-color: #d8d8d8; + color: #383838 +} +/* purposely do not highlight these things */ +.hljs-formula, +.hljs-params, +.hljs-property { + +} +/* base03 - #b8b8b8 - Comments, Invisibles, Line Highlighting */ +.hljs-comment { + color: #b8b8b8 +} +/* base04 - #585858 - Dark Foreground (Used for status bars) */ +.hljs-tag { + color: #585858 +} +/* base05 - #383838 - Default Foreground, Caret, Delimiters, Operators */ +.hljs-subst, +.hljs-punctuation, +.hljs-operator { + color: #383838 +} +.hljs-operator { + opacity: 0.7 +} +/* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ +.hljs-bullet, +.hljs-variable, +.hljs-template-variable, +.hljs-selector-tag, +.hljs-name, +.hljs-deletion { + color: #ab4642 +} +/* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ +.hljs-symbol, +.hljs-number, +.hljs-link, +.hljs-attr, +.hljs-variable.constant_, +.hljs-literal { + color: #dc9656 +} +/* base0A - Classes, Markup Bold, Search Text Background */ +.hljs-title, +.hljs-class .hljs-title, +.hljs-title.class_ { + color: #f79a0e +} +.hljs-strong { + font-weight: bold; + color: #f79a0e +} +/* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ +.hljs-code, +.hljs-addition, +.hljs-title.class_.inherited__, +.hljs-string { + color: #538947 +} +/* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ +/* guessing */ +.hljs-built_in, +.hljs-doctag, +.hljs-quote, +.hljs-keyword.hljs-atrule, +.hljs-regexp { + color: #4b8093 +} +/* base0D - Functions, Methods, Attribute IDs, Headings */ +.hljs-function .hljs-title, +.hljs-attribute, +.ruby .hljs-property, +.hljs-title.function_, +.hljs-section { + color: #7cafc2 +} +/* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ +/* .hljs-selector-id, */ +/* .hljs-selector-class, */ +/* .hljs-selector-attr, */ +/* .hljs-selector-pseudo, */ +.hljs-type, +.hljs-template-tag, +.diff .hljs-meta, +.hljs-keyword { + color: #96609e +} +.hljs-emphasis { + color: #96609e; + font-style: italic +} +/* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ +/* + prevent top level .keyword and .string scopes + from leaking into meta by accident +*/ +.hljs-meta, +.hljs-meta .hljs-keyword, +.hljs-meta .hljs-string { + color: #a16946 +} +/* for v10 compatible themes */ +.hljs-meta .hljs-keyword, +.hljs-meta-keyword { + font-weight: bold +} + +/*! + Theme: Apprentice + Author: romainl + License: ~ MIT (or more permissive) [via base16-schemes-source] + Maintainer: @highlightjs/core-team + Version: 2021.09.0 +*/ +/* + WARNING: DO NOT EDIT THIS FILE DIRECTLY. + + This theme file was auto-generated from the Base16 scheme apprentice + by the Highlight.js Base16 template builder. + + - https://github.com/highlightjs/base16-highlightjs +*/ +/* +base00 #262626 Default Background +base01 #303030 Lighter Background (Used for status bars, line number and folding marks) +base02 #333333 Selection Background +base03 #6C6C6C Comments, Invisibles, Line Highlighting +base04 #787878 Dark Foreground (Used for status bars) +base05 #BCBCBC Default Foreground, Caret, Delimiters, Operators +base06 #C9C9C9 Light Foreground (Not often used) +base07 #FFFFFF Light Background (Not often used) +base08 #5F8787 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted +base09 #FF8700 Integers, Boolean, Constants, XML Attributes, Markup Link Url +base0A #5F8787 Classes, Markup Bold, Search Text Background +base0B #87AF87 Strings, Inherited Class, Markup Code, Diff Inserted +base0C #5F875F Support, Regular Expressions, Escape Characters, Markup Quotes +base0D #FFFFAF Functions, Methods, Attribute IDs, Headings +base0E #87AFD7 Keywords, Storage, Selector, Markup Italic, Diff Changed +base0F #5F87AF Deprecated, Opening/Closing Embedded Language Tags, e.g. +*/ +.dark-mode pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} +.dark-mode code.hljs { + padding: 3px 5px +} +.dark-mode .hljs { + color: #BCBCBC; + background: #262626 +} +.dark-mode .hljs::selection, +.dark-mode .hljs ::selection { + background-color: #333333; + color: #BCBCBC +} +/* purposely do not highlight these things */ +.dark-mode .hljs-formula, +.dark-mode .hljs-params, +.dark-mode .hljs-property { + +} +/* base03 - #6C6C6C - Comments, Invisibles, Line Highlighting */ +.dark-mode .hljs-comment { + color: #6C6C6C +} +/* base04 - #787878 - Dark Foreground (Used for status bars) */ +.dark-mode .hljs-tag { + color: #787878 +} +/* base05 - #BCBCBC - Default Foreground, Caret, Delimiters, Operators */ +.dark-mode .hljs-subst, +.dark-mode .hljs-punctuation, +.dark-mode .hljs-operator { + color: #BCBCBC +} +.dark-mode .hljs-operator { + opacity: 0.7 +} +/* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ +.dark-mode .hljs-bullet, +.dark-mode .hljs-variable, +.dark-mode .hljs-template-variable, +.dark-mode .hljs-selector-tag, +.dark-mode .hljs-name, +.dark-mode .hljs-deletion { + color: #5F8787 +} +/* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ +.dark-mode .hljs-symbol, +.dark-mode .hljs-number, +.dark-mode .hljs-link, +.dark-mode .hljs-attr, +.dark-mode .hljs-variable.constant_, +.dark-mode .hljs-literal { + color: #FF8700 +} +/* base0A - Classes, Markup Bold, Search Text Background */ +.dark-mode .hljs-title, +.dark-mode .hljs-class .hljs-title, +.dark-mode .hljs-title.class_ { + color: #5F8787 +} +.dark-mode .hljs-strong { + font-weight: bold; + color: #5F8787 +} +/* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ +.dark-mode .hljs-code, +.dark-mode .hljs-addition, +.dark-mode .hljs-title.class_.inherited__, +.dark-mode .hljs-string { + color: #87AF87 +} +/* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ +/* guessing */ +.dark-mode .hljs-built_in, +.dark-mode .hljs-doctag, +.dark-mode .hljs-quote, +.dark-mode .hljs-keyword.hljs-atrule, +.dark-mode .hljs-regexp { + color: #5F875F +} +/* base0D - Functions, Methods, Attribute IDs, Headings */ +.dark-mode .hljs-function .hljs-title, +.dark-mode .hljs-attribute, +.dark-mode .ruby .hljs-property, +.dark-mode .hljs-title.function_, +.dark-mode .hljs-section { + color: #FFFFAF +} +/* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ +/* .hljs-selector-id, */ +/* .hljs-selector-class, */ +/* .hljs-selector-attr, */ +/* .hljs-selector-pseudo, */ +.dark-mode .hljs-type, +.dark-mode .hljs-template-tag, +.dark-mode .diff .hljs-meta, +.dark-mode .hljs-keyword { + color: #87AFD7 +} +.dark-mode .hljs-emphasis { + color: #87AFD7; + font-style: italic +} +/* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ +/* + prevent top level .keyword and .string scopes + from leaking into meta by accident +*/ +.dark-mode .hljs-meta, +.dark-mode .hljs-meta .hljs-keyword, +.dark-mode .hljs-meta .hljs-string { + color: #5F87AF +} +/* for v10 compatible themes */ +.dark-mode .hljs-meta .hljs-keyword, +.dark-mode .hljs-meta-keyword { + font-weight: bold +} \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/highlight.min.js b/src/server/game/LuaEngine/docs/ElunaDoc/static/highlight.min.js new file mode 100644 index 0000000000..4d04c1f0ef --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/highlight.min.js @@ -0,0 +1,323 @@ +/*! + Highlight.js v11.10.0 (git: 366a8bd012) + (c) 2006-2024 Josh Goebel and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(t){ +return t instanceof Map?t.clear=t.delete=t.set=()=>{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{ +const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) +})),t}class t{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope +;class o{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)} +closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const r=(e={})=>{const t={children:[]} +;return Object.assign(t,e),t};class a{constructor(){ +this.rootNode=r(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t=r({scope:e}) +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,t){const n=e.root +;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ +return new o(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function l(e){ +return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} +function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} +function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], +"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)} +const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={ +begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, +contains:[]},n);s.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s +},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({ +__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ +scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N, +C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number", +begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, +NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function L(e,t){ +Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function P(e,t){ +void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ +relevance:0,contains:[Object.assign(n,{endsParent:!0})] +},e.relevance=0,delete n.beforeMatch +},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword" +;function $(e,t,n=C){const i=Object.create(null) +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,$(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ +return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{ +console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{ +z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) +},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={} +;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1]) +;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"), +K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"), +K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){ +function t(t,n){ +return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) +}class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o +;if(o.isCompiled)return a +;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))), +o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null +;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords), +c=o.keywords.$pattern, +delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)), +a.keywordPatternRe=t(c,!0), +r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/), +o.end&&(a.endRe=t(a.end)), +a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)), +o.illegal&&(a.illegalRe=t(o.illegal)), +o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{ +starts:e.starts?i(e.starts):null +}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a) +})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ +return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ +constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} +const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{ +const i=Object.create(null),s=Object.create(null),o=[];let r=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function b(e){ +return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" +;"object"==typeof t?(i=e, +n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."), +G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o) +;const r=o.result?o.result:E(o.language,o.code,n) +;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){ +const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R) +;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n="" +;for(;t;){n+=R.substring(e,t.index) +;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){ +const[e,i]=o +;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0] +;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i +;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{ +if(""===R)return;let e=null;if("string"==typeof N.subLanguage){ +if(!i[N.subLanguage])return void M.addText(R) +;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top +}else e=x(R,N.subLanguage.length?N.subLanguage:null) +;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language) +})():l(),R=""}function u(e,t){ +""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1 +;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} +const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}} +function h(e,t){ +return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{ +value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) +;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return f(e.parent,n,i)}function b(e){ +return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){ +const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N +;N.endScope&&N.endScope._wrap?(g(), +u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(), +d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t), +g(),o.excludeEnd&&(R=t));do{ +N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent +}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length} +let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0 +;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){ +if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=w.rule,t}return 1} +if(w=o,"begin"===o.type)return(e=>{ +const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]] +;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n) +;return i.skip?R+=n:(i.excludeBegin&&(R+=n), +g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o) +;if("illegal"===o.type&&!s){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"') +;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e} +if("illegal"===o.type&&""===a)return 1 +;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches") +;return R+=a,a.length}const _=O(e) +;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[] +;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{ +if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){ +I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A +;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e) +;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e, +value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A, +context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{ +language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N} +;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ +const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} +;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1))) +;s.unshift(n);const o=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 +;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r +;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) +;return t||(X(a.replace("{}",n[1])), +X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return +;if(N("before:highlightElement",{el:e,language:n +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) +;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i) +;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,o.language),e.result={language:o.language,re:o.relevance, +relevance:o.relevance},o.secondBest&&(e.secondBest={ +language:o.secondBest.language,relevance:o.secondBest.relevance +}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0 +}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} +function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=O(e) +;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_, +highlightElement:w, +highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"), +G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)}, +initHighlighting:()=>{ +_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ +if(W("Language definition for '{}' could not be registered.".replace("{}",e)), +!r)throw t;W(t),s=l} +s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete i[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v, +autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)}, +removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{ +r=!1},n.safeMode=()=>{r=!0},n.versionString="11.10.0",n.regex={concat:h, +lookahead:g,either:f,optional:d,anyNumberOfTimes:u} +;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n +},ne=te({});return ne.newInstance=()=>te({}),ne}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `lua` grammar compiled for Highlight.js 11.10.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const t="\\[=*\\[",a="\\]=*\\]",n={ +begin:t,end:a,contains:["self"] +},o=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,a,{contains:[n],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:o}].concat(o) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:t,end:a,contains:[n],relevance:5}])}}})();hljs.registerLanguage("lua",e) +})(); \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/jquery.js b/src/server/game/LuaEngine/docs/ElunaDoc/static/jquery.js new file mode 100644 index 0000000000..7f37b5d991 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0{var e=(()=>{"use strict";return e=>{const t="\\[=*\\[",a="\\]=*\\]",n={ +begin:t,end:a,contains:["self"] +},o=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,a,{contains:[n],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:o}].concat(o) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:t,end:a,contains:[n],relevance:5}])}}})();hljs.registerLanguage("lua",e) +})(); \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/main.css b/src/server/game/LuaEngine/docs/ElunaDoc/static/main.css new file mode 100644 index 0000000000..847090f851 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/main.css @@ -0,0 +1,658 @@ +/** + * Copyright 2013 The Rust Project Developers. See the COPYRIGHT + * file at the top-level directory of this distribution and at + * http://rust-lang.org/COPYRIGHT. + * + * Licensed under the Apache License, Version 2.0 or the MIT license + * , at your + * option. This file may not be copied, modified, or distributed + * except according to those terms. + */ +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 400; + src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff'); +} +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 500; + src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff'); +} +@font-face { + font-family: 'Source Serif Pro'; + font-style: normal; + font-weight: 400; + src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff'); +} +@font-face { + font-family: 'Source Serif Pro'; + font-style: italic; + font-weight: 400; + src: url("Heuristica-Italic.woff") format('woff'); +} +@font-face { + font-family: 'Source Serif Pro'; + font-style: normal; + font-weight: 700; + src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff'); +} +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 400; + src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff'); +} +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 600; + src: local('Source Code Pro Semibold'), url("SourceCodePro-Semibold.woff") format('woff'); +} + +@import "normalize.css"; + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +/* General structure and fonts */ + +/* base color defines */ +:root { + --clr-light-main: #fff; + --clr-light-searchbox-back: #fff; + --clr-light-searchbox-text: #555; + --clr-light-element-highlight: #F5F5F5; + --clr-light-current-selection: #8c6067; + --clr-light-table-border: #ddd; + --clr-light-table-header: #f5f5f5; + --clr-light-table-nth: #f9f9f9; + --clr-light-content-highlight: #c6afb3; + + --clr-dark-main: #333; + --clr-dark-searchbox-back: #2f2f2f; + --clr-dark-searchbox-text: #fff; + --clr-dark-element-highlight: #2d2d2d; + --clr-dark-current-selection: #4d76ae; + --clr-dark-table: #2f2f2f; + --clr-dark-table-border: #444; + --clr-dark-table-header: #3a3a3a; + --clr-dark-table-nth: #333; + --clr-dark-content-highlight: #d5eeff; +} + +body { + background-color: var(--clr-light-main); + color: var(--clr-dark-main); + min-width: 500px; + font: 16px/1.4 "Source Serif Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 0; + position: relative; + padding: 10px 15px 20px 15px; + transition: background-color 0.3s, color 0.3s; +} + +h1 { + font-size: 1.5em; +} + +h2 { + font-size: 1.4em; +} + +h3 { + font-size: 1.3em; +} + +h1, h2, h3:not(.impl):not(.method), h4:not(.method) { + color: var(--clr-dark-main); + font-weight: 500; + margin: 20px 0 15px 0; + padding-bottom: 6px; +} + +h1.fqn { + border-bottom: 1px dashed #D5D5D5; + margin-top: 0; +} + +h2, h3:not(.impl):not(.method), h4:not(.method) { + border-bottom: 1px solid #DDDDDD; +} + +h3.impl, h3.method, h4.method { + font-weight: 600; + margin-top: 10px; + margin-bottom: 10px; +} + +h3.impl, h3.method { + margin-top: 15px; +} + +h1, h2, h3, h4, section.sidebar, a.source, .search-input, .content table a, .collapse-toggle { + font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +ol, ul { + padding-left: 25px; +} + +ul ul, ol ul, ul ol, ol ol { + margin-bottom: 0; +} + +p { + margin: 0 0 .6em 0; +} + +code, pre { + font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace; + white-space: pre-wrap; +} + +.docblock code { + background-color: var(--clr-light-element-highlight); + border-radius: 4px; + transition: background-color 0.3s; +} + +pre { + background-color: var(--clr-light-element-highlight); + margin: 0px 0px; +} + +.source pre { + padding: 20px; +} + +.content.source { + margin-top: 50px; + max-width: none; + overflow: visible; + margin-left: 0px; + min-width: 70em; +} + +nav.sub { + font-size: 16px; + text-transform: uppercase; + flex-flow: row nowrap; + display: flex; +} + +.sidebar { + width: 200px; + position: absolute; + left: 0; + top: 0; + min-height: 100%; +} + +.content, nav { max-width: 80vw; } + +.docblock .table-container { + width: 100%; + overflow-x: auto; + margin-bottom: 20px; +} + +.docblock table { + max-width: 50vw; + border-collapse: collapse !important; + table-layout: auto; + margin-bottom: 20px; + font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.docblock th, .docblock td { + padding: 10px; + text-align: left; + border: 1px solid var(--clr-light-table-border); + white-space: nowrap; +} + +.docblock th { + background-color: var(--clr-light-table-header); + font-weight: bold; +} + +.docblock tr:nth-child(even) { + background-color: var(--clr-light-table-nth); +} + +.docblock th, .docblock td, .docblock tr { + transition: background-color 0.3s; +} + +/* Everything else */ + +.js-only, .hidden { display: none; } + +.sidebar { + padding: 10px; +} +.sidebar img { + margin: 20px auto; + display: block; +} + +.sidebar .location { + font-size: 17px; + margin: 30px 0 20px 0; + background: #e1e1e1; + text-align: center; + color: #333; +} + +.block { + padding: 0 10px; + margin-bottom: 14px; +} + +.block h2 { + margin-top: 0; + margin-bottom: 8px; + text-align: center; +} + +.block a { + display: block; + text-overflow: ellipsis; + overflow: hidden; + line-height: 15px; + padding: 7px 5px; + font-size: 14px; + font-weight: 300; + transition: border 500ms ease-out; +} + +.block a:hover { + background: var(--clr-light-element-highlight); +} + +.content { + padding: 15px 0; +} + +.content.source pre.rust { + white-space: pre; + overflow: auto; + padding-left: 0; +} +.content pre.line-numbers { float: left; border: none; } +.line-numbers span { color: #c67e2d; } +.line-numbers .line-highlighted { + background-color: #f6fdb0; +} + +.docblock.short.nowrap { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.docblock.short p { + overflow: hidden; + text-overflow: ellipsis; + margin: 0; +} +.docblock.short code { white-space: nowrap; } + +.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 { + border-bottom: 1px solid #DDD; +} + +.docblock h1 { font-size: 1.3em; } +.docblock h2 { font-size: 1.15em; } +.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; } + +.content .out-of-band { + float: right; + font-size: 23px; +} + +.content table { + border-spacing: 0 5px; + border-collapse: separate; +} +.content td { vertical-align: top; } +.content td:first-child { padding-right: 20px; } +.content td p:first-child { margin-top: 0; } +.content td h1, .content td h2 { margin-left: 0; font-size: 1.1em; } + +.content .item-list { + list-style-type: none; + padding: 0; +} + +.content .item-list li { margin-bottom: 3px; } + +.content .multi-column { + -moz-column-count: 5; + -moz-column-gap: 2.5em; + -webkit-column-count: 5; + -webkit-column-gap: 2.5em; + column-count: 5; + column-gap: 2.5em; +} +.content .multi-column li { width: 100%; display: inline-block; } + +.content .method { + font-size: 1em; + position: relative; +} +.content .methods .docblock { margin-left: 40px; } + +.content .impl-methods .docblock { margin-left: 40px; } + +nav { + border-bottom: 1px solid #e0e0e0; + padding-bottom: 10px; + margin-bottom: 10px; +} +nav.main { + padding: 20px 0; + text-align: center; +} +nav.main .current { + border-top: 1px solid #000; + border-bottom: 1px solid #000; +} +nav.main .separator { + border: 1px solid #000; + display: inline-block; + height: 23px; + margin: 0 20px; +} +nav.sum { text-align: right; } + +nav, .content { + margin-left: 230px; +} + +a { + text-decoration: none; + color: var(--clr-dark-main); + background: transparent; +} +p a { color: #4e8bca; } +p a:hover { text-decoration: underline; } + +.content a.trait, .block a.current.trait { color: #ed9603; } +.content a.mod, .block a.current.mod { color: #4d76ae; } +.content a.enum, .block a.current.enum { color: #5e9766; } +.content a.struct, .block a.current.struct { color: #e53700; } +.content a.fn, .block a.current.fn { color: var(--clr-light-current-selection); } +.content .fnname { color: var(--clr-light-current-selection); } + +.search-form { + position: relative; + display: flex; + flex-grow: 1; +} + +.search-input { + /* Override Normalize.css: we have margins and do + not want to overflow - the `moz` attribute is necessary + until Firefox 29, too early to drop at this point */ + -moz-box-sizing: border-box !important; + box-sizing: border-box !important; + outline: none; + border: none; + border-radius: 1px; + color: var(--clr-light-searchbox-text); + background-color: var(--clr-light-searchbox-back); + margin-top: 5px; + padding: 10px 16px; + font-size: 17px; + box-shadow: 0 0 0 1px rgba(224, 224, 224, 1), 0 0 0 2px transparent; + transition: border-color 300ms ease; + transition: border-radius 300ms ease-in-out; + transition: box-shadow 300ms ease-in-out; + transition: background-color 0.3s, color 0.3s; + flex-grow: 1; + height: 35px; +} + +.search-input:focus { + border-radius: 2px; + border: 0; + outline: 0; + box-shadow: 0 0 0 1px rgba(224, 224, 224, 0.5), 0 0 8px 2px #078dd8; +} + +.search-results .desc { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: block; +} + +tr.result:hover { + background-color: var(--clr-light-element-highlight); +} + +.toggle-container { + margin: auto; + text-align: center; + height: 25px; + width: 35px; +} + +.stability { + border-left: 6px solid; + padding: 3px 6px; + border-radius: 3px; +} + +h1 .stability { + text-transform: lowercase; + font-weight: 400; + margin-left: 14px; + padding: 4px 10px; +} + +.impl-methods .stability, .methods .stability { + margin-right: 20px; +} + +.stability.Deprecated { border-color: #A071A8; color: #82478C; } +.stability.Experimental { border-color: #D46D6A; color: #AA3C39; } +.stability.Unstable { border-color: #D4B16A; color: #AA8439; } +.stability.Stable { border-color: #54A759; color: #2D8632; } +.stability.Frozen { border-color: #009431; color: #007726; } +.stability.Locked { border-color: #0084B6; color: #00668c; } +.stability.Unmarked { border-color: #BBBBBB; } + +.summary { + padding-right: 0px; +} +.summary.Deprecated { background-color: #A071A8; } +.summary.Experimental { background-color: #D46D6A; } +.summary.Unstable { background-color: #D4B16A; } +.summary.Stable { background-color: #54A759; } +.summary.Unmarked { background-color: #BBBBBB; } + +:target { background: #FDFFD3; } + +/* Code highlighting */ +pre.rust .kw { color: #8959A8; } +pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } +pre.rust .number, pre.rust .string { color: #718C00; } +pre.rust .self, pre.rust .boolval, pre.rust .prelude-val, +pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } +pre.rust .comment { color: #8E908C; } +pre.rust .doccomment { color: #4D4D4C; } +pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } +pre.rust .lifetime { color: #B76514; } + +.rusttest { display: none; } +pre.rust { position: relative; } +.test-arrow { + display: inline-block; + position: absolute; + top: 0; + right: 10px; + font-size: 150%; + -webkit-transform: scaleX(-1); + transform: scaleX(-1); +} + +.methods .section-header { + /* Override parent class attributes. */ + border-bottom: none !important; + font-size: 1.1em !important; + margin: 0 0 -5px; + padding: 0; +} +.section-header:hover a:after { + content: '\2002\00a7\2002'; +} + +/* Media Queries */ + +@media (max-width: 700px) { + .sidebar { + display: none; + } + + nav, .content { + margin-left: 0px; + max-width: 100vw; + } +} + +.collapse-toggle { + font-weight: 100; + position: absolute; + left: 13px; + color: #999; + margin-top: 2px; +} + +.toggle-wrapper > .collapse-toggle { + left: -24px; + margin-top: 0px; +} + +.toggle-wrapper { + position: relative; +} + +.toggle-wrapper.collapsed { + height: 1em; + transition: height .2s; +} + +.collapse-toggle > .inner { + display: inline-block; + width: 1ch; + text-align: center; +} + +.toggle-label { + color: #999; + font-style: italic; +} + +/* dark mode */ + +.dark-mode body { + background-color: var(--clr-dark-main); + color: var(--clr-light-main); +} + +.dark-mode h1 { + color: var(--clr-light-main); +} + +.dark-mode h2 { + color: var(--clr-light-main); +} + +.dark-mode h3 { + color: var(--clr-light-main); +} + +.dark-mode h4 { + color: var(--clr-light-main); +} + +.dark-mode .docblock code { + background-color: var(--clr-dark-element-highlight); +} + +.dark-mode pre { + background-color: var(--clr-dark-element-highlight); +} + +.dark-mode .docblock table { + background-color: var(--clr-dark-table); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); +} + +.dark-mode .docblock th, .dark-mode .docblock td { + border: 1px solid var(--clr-dark-table-border); +} + +.dark-mode .docblock th { + background-color: var(--clr-dark-table-header); +} + +.dark-mode .docblock tr:nth-child(even) { + background-color: var(--clr-dark-table-nth); +} + +.dark-mode .block a:hover { + background: var(--clr-dark-element-highlight); +} + +.dark-mode a { + color: var(--clr-light-main); +} + +.dark-mode p a { color: #4e8bca; } + +.dark-mode .content a.fn, .dark-mode .block a.current.fn { color: var(--clr-dark-current-selection); } +.dark-mode .content .fnname { color: var(--clr-dark-current-selection); } + +.dark-mode :target { background: var(--clr-dark-current-selection); } + +.dark-mode .search-input { + color: var(--clr-dark-searchbox-text); + background-color: var(--clr-dark-searchbox-back); +} + +.dark-mode tr.result:hover { + background-color: var(--clr-dark-element-highlight); +} + +.sun-icon, .moon-icon { + width: 24px; + height: 24px; + transition: opacity 0.3s; + fill: none; + stroke: currentColor; + stroke-width: 2; + stroke-linecap: round; +} + +.moon-icon { + display: none; +} + +.dark-mode .sun-icon { + display: none; +} + +.dark-mode .moon-icon { + display: inline-block; +} \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/main.js b/src/server/game/LuaEngine/docs/ElunaDoc/static/main.js new file mode 100644 index 0000000000..c2fb62ef3d --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/main.js @@ -0,0 +1,775 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*jslint browser: true, es5: true */ +/*globals $: true, rootPath: true */ + +(function() { + "use strict"; + var resizeTimeout, interval; + + $('.js-only').removeClass('js-only'); + + function getQueryStringParams() { + var params = {}; + window.location.search.substring(1).split("&"). + map(function(s) { + var pair = s.split("="); + params[decodeURIComponent(pair[0])] = + typeof pair[1] === "undefined" ? + null : decodeURIComponent(pair[1]); + }); + return params; + } + + function browserSupportsHistoryApi() { + return window.history && typeof window.history.pushState === "function"; + } + + function resizeShortBlocks() { + if (resizeTimeout) { + clearTimeout(resizeTimeout); + } + resizeTimeout = setTimeout(function() { + var contentWidth = $('.content').width(); + $('.docblock.short').width(function() { + return contentWidth - 40 - $(this).prev().width(); + }).addClass('nowrap'); + $('.summary-column').width(function() { + return contentWidth - 40 - $(this).prev().width(); + }) + }, 150); + } + resizeShortBlocks(); + $(window).on('resize', resizeShortBlocks); + + function highlightSourceLines() { + var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/); + if (match) { + from = parseInt(match[1], 10); + to = Math.min(50000, parseInt(match[2] || match[1], 10)); + from = Math.min(from, to); + if ($('#' + from).length === 0) { + return; + } + $('#' + from)[0].scrollIntoView(); + $('.line-numbers span').removeClass('line-highlighted'); + for (i = from; i <= to; ++i) { + $('#' + i).addClass('line-highlighted'); + } + } + } + highlightSourceLines(); + $(window).on('hashchange', highlightSourceLines); + + $(document).on('keyup', function(e) { + if (document.activeElement.tagName === 'INPUT') { + return; + } + + if (e.which === 191 && $('#help').hasClass('hidden')) { // question mark + e.preventDefault(); + $('#help').removeClass('hidden'); + } else if (e.which === 27) { // esc + if (!$('#help').hasClass('hidden')) { + e.preventDefault(); + $('#help').addClass('hidden'); + } else if (!$('#search').hasClass('hidden')) { + e.preventDefault(); + $('#search').addClass('hidden'); + $('#main').removeClass('hidden'); + } + } else if (e.which === 83) { // S + e.preventDefault(); + $('.search-input').focus(); + } + }).on('click', function(e) { + if (!$(e.target).closest('#help').length) { + $('#help').addClass('hidden'); + } + }); + + $('.version-selector').on('change', function() { + var i, match, + url = document.location.href, + stripped = '', + len = rootPath.match(/\.\.\//g).length + 1; + + for (i = 0; i < len; ++i) { + match = url.match(/\/[^\/]*$/); + if (i < len - 1) { + stripped = match[0] + stripped; + } + url = url.substring(0, url.length - match[0].length); + } + + url += '/' + $('.version-selector').val() + stripped; + + document.location.href = url; + }); + /** + * A function to compute the Levenshtein distance between two strings + * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported + * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode + * This code is an unmodified version of the code written by Marco de Wit + * and was found at http://stackoverflow.com/a/18514751/745719 + */ + var levenshtein = (function() { + var row2 = []; + return function(s1, s2) { + if (s1 === s2) { + return 0; + } else { + var s1_len = s1.length, s2_len = s2.length; + if (s1_len && s2_len) { + var i1 = 0, i2 = 0, a, b, c, c2, row = row2; + while (i1 < s1_len) + row[i1] = ++i1; + while (i2 < s2_len) { + c2 = s2.charCodeAt(i2); + a = i2; + ++i2; + b = i2; + for (i1 = 0; i1 < s1_len; ++i1) { + c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); + a = row[i1]; + b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); + row[i1] = b; + } + } + return b; + } else { + return s1_len + s2_len; + } + } + }; + })(); + + function initSearch(rawSearchIndex) { + var currentResults, index, searchIndex; + var MAX_LEV_DISTANCE = 3; + var params = getQueryStringParams(); + + // Populate search bar with query string search term when provided, + // but only if the input bar is empty. This avoid the obnoxious issue + // where you start trying to do a search, and the index loads, and + // suddenly your search is gone! + if ($(".search-input")[0].value === "") { + $(".search-input")[0].value = params.search || ''; + } + + /** + * Executes the query and builds an index of results + * @param {[Object]} query [The user query] + * @param {[type]} max [The maximum results returned] + * @param {[type]} searchWords [The list of search words to query + * against] + * @return {[type]} [A search index of results] + */ + function execQuery(query, max, searchWords) { + var valLower = query.query.toLowerCase(), + val = valLower, + typeFilter = itemTypeFromName(query.type), + results = [], + split = valLower.split("::"); + + //remove empty keywords + for (var j = 0; j < split.length; ++j) { + split[j].toLowerCase(); + if (split[j] === "") { + split.splice(j, 1); + } + } + + // quoted values mean literal search + var nSearchWords = searchWords.length; + if ((val.charAt(0) === "\"" || val.charAt(0) === "'") && + val.charAt(val.length - 1) === val.charAt(0)) + { + val = val.substr(1, val.length - 2); + for (var i = 0; i < nSearchWords; ++i) { + if (searchWords[i] === val) { + // filter type: ... queries + if (typeFilter < 0 || typeFilter === searchIndex[i].ty) { + results.push({id: i, index: -1}); + } + } + if (results.length === max) { + break; + } + } + } else { + // gather matching search results up to a certain maximum + val = val.replace(/\_/g, ""); + for (var i = 0; i < split.length; ++i) { + for (var j = 0; j < nSearchWords; ++j) { + var lev_distance; + if (searchWords[j].indexOf(split[i]) > -1 || + searchWords[j].indexOf(val) > -1 || + searchWords[j].replace(/_/g, "").indexOf(val) > -1) + { + // filter type: ... queries + if (typeFilter < 0 || typeFilter === searchIndex[j].ty) { + results.push({ + id: j, + index: searchWords[j].replace(/_/g, "").indexOf(val), + lev: 0, + }); + } + } else if ( + (lev_distance = levenshtein(searchWords[j], val)) <= + MAX_LEV_DISTANCE) { + if (typeFilter < 0 || typeFilter === searchIndex[j].ty) { + results.push({ + id: j, + index: 0, + // we want lev results to go lower than others + lev: lev_distance, + }); + } + } + if (results.length === max) { + break; + } + } + } + } + + var nresults = results.length; + for (var i = 0; i < nresults; ++i) { + results[i].word = searchWords[results[i].id]; + results[i].item = searchIndex[results[i].id] || {}; + } + // if there are no results then return to default and fail + if (results.length === 0) { + return []; + } + + results.sort(function(aaa, bbb) { + var a, b; + + // Sort by non levenshtein results and then levenshtein results by the distance + // (less changes required to match means higher rankings) + a = (aaa.lev); + b = (bbb.lev); + if (a !== b) return a - b; + + // sort by crate (non-current crate goes later) + a = (aaa.item.crate !== window.currentCrate); + b = (bbb.item.crate !== window.currentCrate); + if (a !== b) return a - b; + + // sort by exact match (mismatch goes later) + a = (aaa.word !== valLower); + b = (bbb.word !== valLower); + if (a !== b) return a - b; + + // sort by item name length (longer goes later) + a = aaa.word.length; + b = bbb.word.length; + if (a !== b) return a - b; + + // sort by item name (lexicographically larger goes later) + a = aaa.word; + b = bbb.word; + if (a !== b) return (a > b ? +1 : -1); + + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) return a - b; + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) return a - b; + + // sort by description (no description goes later) + a = (aaa.item.desc === ''); + b = (bbb.item.desc === ''); + if (a !== b) return a - b; + + // sort by type (later occurrence in `itemTypes` goes later) + a = aaa.item.ty; + b = bbb.item.ty; + if (a !== b) return a - b; + + // sort by path (lexicographically larger goes later) + a = aaa.item.path; + b = bbb.item.path; + if (a !== b) return (a > b ? +1 : -1); + + // que sera, sera + return 0; + }); + + // remove duplicates, according to the data provided + for (var i = results.length - 1; i > 0; i -= 1) { + if (results[i].word === results[i - 1].word && + results[i].item.ty === results[i - 1].item.ty && + results[i].item.path === results[i - 1].item.path) + { + results[i].id = -1; + } + } + for (var i = 0; i < results.length; ++i) { + var result = results[i], + name = result.item.name.toLowerCase(), + path = result.item.path.toLowerCase(), + parent = result.item.parent; + + var valid = validateResult(name, path, split, parent); + if (!valid) { + result.id = -1; + } + } + return results; + } + + /** + * Validate performs the following boolean logic. For example: + * "File::open" will give IF A PARENT EXISTS => ("file" && "open") + * exists in (name || path || parent) OR => ("file" && "open") exists in + * (name || path ) + * + * This could be written functionally, but I wanted to minimise + * functions on stack. + * + * @param {[string]} name [The name of the result] + * @param {[string]} path [The path of the result] + * @param {[string]} keys [The keys to be used (["file", "open"])] + * @param {[object]} parent [The parent of the result] + * @return {[boolean]} [Whether the result is valid or not] + */ + function validateResult(name, path, keys, parent) { + for (var i=0; i < keys.length; ++i) { + // each check is for validation so we negate the conditions and invalidate + if (!( + // check for an exact name match + name.toLowerCase().indexOf(keys[i]) > -1 || + // then an exact path match + path.toLowerCase().indexOf(keys[i]) > -1 || + // next if there is a parent, check for exact parent match + (parent !== undefined && + parent.name.toLowerCase().indexOf(keys[i]) > -1) || + // lastly check to see if the name was a levenshtein match + levenshtein(name.toLowerCase(), keys[i]) <= + MAX_LEV_DISTANCE)) { + return false; + } + } + return true; + } + + function getQuery() { + var matches, type, query = $('.search-input').val(); + + matches = query.match(/^(fn|mod|str(uct)?|enum|trait|t(ype)?d(ef)?)\s*:\s*/i); + if (matches) { + type = matches[1].replace(/^td$/, 'typedef') + .replace(/^str$/, 'struct') + .replace(/^tdef$/, 'typedef') + .replace(/^typed$/, 'typedef'); + query = query.substring(matches[0].length); + } + + return { + query: query, + type: type, + id: query + type, + }; + } + + function initSearchNav() { + var hoverTimeout, $results = $('.search-results .result'); + + $results.on('click', function() { + var dst = $(this).find('a')[0]; + if (window.location.pathname == dst.pathname) { + $('#search').addClass('hidden'); + $('#main').removeClass('hidden'); + } + document.location.href = dst.href; + }); + + $(document).off('keydown.searchnav'); + $(document).on('keydown.searchnav', function(e) { + var $active = $results.filter('.highlighted'); + + if (e.which === 38) { // up + e.preventDefault(); + if (!$active.length || !$active.prev()) { + return; + } + + $active.prev().addClass('highlighted'); + $active.removeClass('highlighted'); + } else if (e.which === 40) { // down + e.preventDefault(); + if (!$active.length) { + $results.first().addClass('highlighted'); + } else if ($active.next().length) { + $active.next().addClass('highlighted'); + $active.removeClass('highlighted'); + } + } else if (e.which === 13) { // return + e.preventDefault(); + if ($active.length) { + document.location.href = $active.find('a').prop('href'); + } + } + }); + } + + function escape(content) { + return $('

').text(content).html(); + } + + function showResults(results) { + var output, shown, query = getQuery(); + + currentResults = query.id; + output = '

Results for ' + escape(query.query) + + (query.type ? ' (type: ' + escape(query.type) + ')' : '') + '

'; + output += ''; + + if (results.length > 0) { + shown = []; + + results.forEach(function(item) { + var name, type; + + if (shown.indexOf(item) !== -1) { + return; + } + + shown.push(item); + name = item.name; + type = itemTypes[item.ty]; + + output += ''; + }); + } else { + output += 'No results - Request function at Eluna issue tracker'; + } + + output += "

"; + $('#main.content').addClass('hidden'); + $('#search.content').removeClass('hidden').html(output); + $('#search .desc').width($('#search').width() - 40 - + $('#search td:first-child').first().width()); + initSearchNav(); + } + + function search(e) { + var query, + filterdata = [], + obj, i, len, + results = [], + maxResults = 200, + resultIndex; + var params = getQueryStringParams(); + + query = getQuery(); + if (e) { + e.preventDefault(); + } + + if (!query.query || query.id === currentResults) { + return; + } + + // Because searching is incremental by character, only the most + // recent search query is added to the browser history. + // Do not do this on local due to chrome security errors. + // http://stackoverflow.com/a/32454237/3586583 + if (browserSupportsHistoryApi() && window.location.protocol != "file:") { + if (!history.state && !params.search) { + history.pushState(query, "", "?search=" + + encodeURIComponent(query.query)); + } else { + history.replaceState(query, "", "?search=" + + encodeURIComponent(query.query)); + } + } + + resultIndex = execQuery(query, 20000, index); + len = resultIndex.length; + for (i = 0; i < len; ++i) { + if (resultIndex[i].id > -1) { + obj = searchIndex[resultIndex[i].id]; + filterdata.push([obj.name, obj.ty, obj.path, obj.desc]); + results.push(obj); + } + if (results.length >= maxResults) { + break; + } + } + + showResults(results); + } + + // This mapping table should match the discriminants of + // `rustdoc::html::item_type::ItemType` type in Rust. + var itemTypes = ["mod", + "struct", + "type", + "fn", + "type", + "static", + "trait", + "impl", + "viewitem", + "tymethod", + "method", + "structfield", + "variant", + "ffi", + "ffs", + "macro", + "primitive"]; + + function itemTypeFromName(typename) { + for (var i = 0; i < itemTypes.length; ++i) { + if (itemTypes[i] === typename) return i; + } + return -1; + } + + function buildIndex(rawSearchIndex) { + searchIndex = []; + var searchWords = []; + for (var crate in rawSearchIndex) { + if (!rawSearchIndex.hasOwnProperty(crate)) { continue } + + // an array of [(Number) item type, + // (String) name, + // (String) full path or empty string for previous path, + // (String) description, + // (optional Number) the parent path index to `paths`] + var items = rawSearchIndex[crate].items; + // an array of [(Number) item type, + // (String) name] + var paths = rawSearchIndex[crate].paths; + + // convert `paths` into an object form + var len = paths.length; + for (var i = 0; i < len; ++i) { + paths[i] = {ty: paths[i][0], name: paths[i][1]}; + } + + // convert `items` into an object form, and construct word indices. + // + // before any analysis is performed lets gather the search terms to + // search against apart from the rest of the data. This is a quick + // operation that is cached for the life of the page state so that + // all other search operations have access to this cached data for + // faster analysis operations + var len = items.length; + var lastPath = ""; + for (var i = 0; i < len; ++i) { + var rawRow = items[i]; + var row = {crate: crate, ty: rawRow[0], name: rawRow[1], + path: rawRow[2] || lastPath, desc: rawRow[3], + parent: paths[rawRow[4]]}; + searchIndex.push(row); + if (typeof row.name === "string") { + var word = row.name.toLowerCase(); + searchWords.push(word); + } else { + searchWords.push(""); + } + lastPath = row.path; + } + } + return searchWords; + } + + function startSearch() { + var keyUpTimeout; + $('.do-search').on('click', search); + $('.search-input').on('keyup', function() { + clearTimeout(keyUpTimeout); + keyUpTimeout = setTimeout(search, 100); + }); + + // Push and pop states are used to add search results to the browser + // history. + if (browserSupportsHistoryApi()) { + $(window).on('popstate', function(e) { + var params = getQueryStringParams(); + // When browsing back from search results the main page + // visibility must be reset. + if (!params.search) { + $('#main.content').removeClass('hidden'); + $('#search.content').addClass('hidden'); + } + // When browsing forward to search results the previous + // search will be repeated, so the currentResults are + // cleared to ensure the search is successful. + currentResults = null; + // Synchronize search bar with query string state and + // perform the search. This will empty the bar if there's + // nothing there, which lets you really go back to a + // previous state with nothing in the bar. + $('.search-input').val(params.search); + // Some browsers fire 'onpopstate' for every page load + // (Chrome), while others fire the event only when actually + // popping a state (Firefox), which is why search() is + // called both here and at the end of the startSearch() + // function. + search(); + }); + } + search(); + } + + index = buildIndex(rawSearchIndex); + startSearch(); + + // Draw a convenient sidebar of known crates if we have a listing + if (rootPath == '../') { + var sidebar = $('.sidebar'); + var div = $('
').attr('class', 'block crate'); + div.append($('

').text('All Classes')); + + var crates = []; + for (var crate in rawSearchIndex) { + if (!rawSearchIndex.hasOwnProperty(crate)) { continue } + crates.push(crate); + } + crates.sort(); + for (var i = 0; i < crates.length; ++i) { + var klass = 'crate'; + if (crates[i] == window.currentCrate) { + klass += ' current'; + } + div.append($('', {'href': '../' + crates[i] + '/index.html', + 'class': klass}).text(crates[i])); + } + sidebar.append(div); + } + } + + window.initSearch = initSearch; + + window.register_implementors = function(imp) { + var list = $('#implementors-list'); + var libs = Object.getOwnPropertyNames(imp); + for (var i = 0; i < libs.length; ++i) { + if (libs[i] == currentCrate) continue; + var structs = imp[libs[i]]; + for (var j = 0; j < structs.length; ++j) { + var code = $('').append(structs[j]); + $.each(code.find('a'), function(idx, a) { + var href = $(a).attr('href'); + if (!href.startsWith('http')) { + $(a).attr('href', rootPath + $(a).attr('href')); + } + }); + var li = $('
  • ').append(code); + list.append(li); + } + } + }; + if (window.pending_implementors) { + window.register_implementors(window.pending_implementors); + } + + // See documentation in html/render.rs for what this is doing. + var query = getQueryStringParams(); + if (query['gotosrc']) { + window.location = $('#src-' + query['gotosrc']).attr('href'); + } + + $("#expand-all").on("click", function() { + $(".docblock").show(); + $(".toggle-label").hide(); + $(".toggle-wrapper").removeClass("collapsed"); + $(".collapse-toggle").children(".inner").html("-"); + }); + + $("#collapse-all").on("click", function() { + $(".docblock").hide(); + $(".toggle-label").show(); + $(".toggle-wrapper").addClass("collapsed"); + $(".collapse-toggle").children(".inner").html("+"); + }); + + $(document).on("click", ".collapse-toggle", function() { + var toggle = $(this); + var relatedDoc = toggle.parent().next(); + if (relatedDoc.is(".docblock")) { + if (relatedDoc.is(":visible")) { + relatedDoc.slideUp({duration:'fast', easing:'linear'}); + toggle.parent(".toggle-wrapper").addClass("collapsed"); + toggle.children(".inner").html("+"); + toggle.children(".toggle-label").fadeIn(); + } else { + relatedDoc.slideDown({duration:'fast', easing:'linear'}); + toggle.parent(".toggle-wrapper").removeClass("collapsed"); + toggle.children(".inner").html("-"); + toggle.children(".toggle-label").hide(); + } + } + }); + + $(function() { + var toggle = "[-]"; + + $(".method").each(function() { + if ($(this).next().is(".docblock")) { + $(this).children().first().after(toggle); + } + }); + + var mainToggle = $(toggle); + mainToggle.append("") + var wrapper = $("
    "); + wrapper.append(mainToggle); + $("#main > .docblock").before(wrapper); + }); + +}()); diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/normalize.css b/src/server/game/LuaEngine/docs/ElunaDoc/static/normalize.css new file mode 100644 index 0000000000..2804c26a29 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/normalize.css @@ -0,0 +1 @@ +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/static/theme.js b/src/server/game/LuaEngine/docs/ElunaDoc/static/theme.js new file mode 100644 index 0000000000..b125cb2929 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/static/theme.js @@ -0,0 +1,22 @@ +const themeToggle = document.getElementById('themeToggle'); + +// Check if user preference exists in local storage +const currentTheme = localStorage.getItem('theme'); +if (currentTheme) { + document.documentElement.classList.add(currentTheme); +} + +themeToggle.addEventListener('click', () => { + document.documentElement.classList.toggle('dark-mode'); + + // Store user preference in local storage + if (document.documentElement.classList.contains('dark-mode')) { + localStorage.setItem('theme', 'dark-mode'); + } else { + localStorage.setItem('theme', ''); + } + + hljs.highlightAll(); +}); + +hljs.highlightAll(); \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/_base.html b/src/server/game/LuaEngine/docs/ElunaDoc/templates/_base.html new file mode 100644 index 0000000000..bcc14089da --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/_base.html @@ -0,0 +1,126 @@ + + + + + + + + + {% block title %}Eluna API{% endblock %} + + + + + + + + + + + + + + + +
    +

    + {% block document_title %}Title Missing{% endblock %} + + + [-] + [+] + + +

    + + {% block content %}

    Content missing.

    {% endblock %} +
    + + + + + + + + + + + + +
    Generated on
    +
    © 2010 - 2024 Eluna Lua Engine
    + + diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/class.html b/src/server/game/LuaEngine/docs/ElunaDoc/templates/class.html new file mode 100644 index 0000000000..93ce464dae --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/class.html @@ -0,0 +1,40 @@ +{% extends '_base.html' %} + + +{% block title -%} + {{ current_class.name }} - {{ super() }} +{%- endblock %} + + +{% block description -%} + API documentation for the {{ current_class.name }} class in the Eluna engine. +{%- endblock %} + + +{% block document_title -%} + Class {{ current_class.name }} +{%- endblock %} + + +{% block sidebar %} +{% endblock %} + + +{% block content %} + {{ current_class.description|parse_links }} + +

    Methods

    +
  • '; + + if (type === 'mod') { + output += item.path + + '::' + name + ''; + } else if (type === 'static' || type === 'reexport') { + output += item.path + + '::' + name + ''; + } else if (item.parent !== undefined) { + var myparent = item.parent; + var anchor = '#' + type + '.' + name; + output += item.path + '::' + myparent.name + + '::' + name + ''; + } else { + output += item.path + + '::' + name + ''; + } + + output += '' + item.desc + + '
    + {%- for method in current_class.methods %} + + + + + {%- endfor %} +
    + + {{ method.name }} + +

    {{ method.short_description|parse_links }}

    +
    +{% endblock %} \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/date.js b/src/server/game/LuaEngine/docs/ElunaDoc/templates/date.js new file mode 100644 index 0000000000..d736d99977 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/date.js @@ -0,0 +1 @@ +document.write("{{ currdate }}"); \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/enum.html b/src/server/game/LuaEngine/docs/ElunaDoc/templates/enum.html new file mode 100644 index 0000000000..505f8cc22e --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/enum.html @@ -0,0 +1 @@ +{% extends '_base.html' %} \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/index.html b/src/server/game/LuaEngine/docs/ElunaDoc/templates/index.html new file mode 100644 index 0000000000..04fb3467a5 --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/index.html @@ -0,0 +1,77 @@ +{% extends '_base.html' %} + + +{% block document_title -%} + Eluna API Documentation +{%- endblock %} + + +{% block content %} +
    +

    + The Eluna Lua Engine© API +

    +

    + The Eluna Lua Engine© API allows you to add your own Lua code to be executed when certain events (called "hooks") occur. +

    +

    + Add a new in-game command, give life to creatures with new AI, or even light players who try to duel on fire! + If the hook exists, you can script it. +

    + +

    + About Eluna +

    +

    + Eluna is a Lua engine for World of Warcraft emulators. + Eluna supports CMaNGOS/MaNGOS + and TrinityCore. +

    +

    + To get Eluna, simply clone your favorite version of MaNGOS or Trinity from + our Github account. + Each fork there has Eluna already integrated, so you just need to compile and go! +

    +

    + Join our community Discord server to keep up with Eluna development and user provided support. +

    + +

    + Tutorials & Guides +

    +

    + We haven't written tutorials yet, but when we do, we'll put the links here. +

    + +

    + About this documentation +

    +

    + The layout, CSS, and Javascript code for this documentation was borrowed from doc.rust-lang.org. +

    +

    + The documentation generator was originally written by Patman64 and is maintained by the Eluna team. +

    +
    + +

    Classes

    + + {%- for class in classes %} + + + + + {%- endfor %} +
    + {%- if class.fully_documented %} + + {%- elif class.fully_undocumented %} + + {%- else %} + + {%- endif %} + {{ class.name }} + +

    {{ class.short_description|parse_links }}

    +
    +{% endblock %} diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/method.html b/src/server/game/LuaEngine/docs/ElunaDoc/templates/method.html new file mode 100644 index 0000000000..823956ae4d --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/method.html @@ -0,0 +1,107 @@ +{% extends '_base.html' %} + + +{% block title -%} + {{ current_class.name }}:{{ current_method.name }} - Eluna +{%- endblock %} + + +{% block description -%} + API documentation for the {{ current_class.name }}:{{ current_method.name }} method in the Eluna engine. +{%- endblock %} + + +{% block document_title -%} + Method + + {{- current_class.name -}} + : + {{- current_method.name -}} + +{%- endblock %} + + +{% block sidebar %} +

    {{ current_class.name }} Methods

    + + +{% endblock %} + + +{% block content %} + {%- if current_method.warning %} +
    + Warning: {{ current_method.warning }} +
    + {%- endif %} +
    + {%- if current_method.documented %} + {{ current_method.description|parse_links }} + {%- else %} +

    This method is undocumented. Use at your own risk.

    +

    For temporary documentation, please check the LuaFunctions source file.

    + {%- endif %} + + {%- if current_method.tables %} +
    + {{ current_method.tables }} +
    + {%- endif %} + + +

    + Synopsis +

    + {%- for prototype in current_method.prototypes %} +

    + {{ prototype }} +

    + {%- endfor %} + +

    + Arguments +

    +

    + {%- if current_method.parameters|length > 0 %} + {%- for param in current_method.parameters %} +

    +
    {{ param.data_type|escape|parse_data_type }} {{ param.name if param.data_type != '...' }} {{- ' (' + param.default_value + ')' if param.default_value }}
    +
    {{ param.description|parse_links if param.description else 'See method description.' }}
    +
    + {%- endfor %} + {%- elif not current_method.documented %} + Unknown. + {%- else %} + None. + {%- endif %} +

    + +

    + Returns +

    +

    + {%- if current_method.returned|length > 0 %} + {%- for returned in current_method.returned %} +

    +
    {{ returned.data_type|escape|parse_data_type }} {{ returned.name }}
    +
    {{ returned.description|parse_links if returned.description else 'See method description.' }}
    +
    + {%- endfor %} + {%- elif not current_method.documented %} + Unknown. + {%- else %} + Nothing. + {%- endif %} +

    +
    +{% endblock %} diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/search-index.js b/src/server/game/LuaEngine/docs/ElunaDoc/templates/search-index.js new file mode 100644 index 0000000000..965ac590ac --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/search-index.js @@ -0,0 +1,15 @@ +var searchIndex = {}; + +{% for class in classes -%} +searchIndex["{{ class.name }}"] = { + "items": [ + [0, "", "{{ class.name }}", "{{ class.short_description|replace('\n', ' ')|replace('\"', '"')|parse_links|replace('"', '\\"') }}"], + {%- for method in class.methods %} + [3, "{{ method.name }}", "", "{{ method.short_description|replace('\n', ' ')|replace('\"', '"')|parse_links|replace('"', '\\"') }}"], + {%- endfor %} + ], + "paths": [] +}; +{%- endfor %} + +initSearch(searchIndex); \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/ElunaDoc/templates/sidebar.js b/src/server/game/LuaEngine/docs/ElunaDoc/templates/sidebar.js new file mode 100644 index 0000000000..700f2ea3aa --- /dev/null +++ b/src/server/game/LuaEngine/docs/ElunaDoc/templates/sidebar.js @@ -0,0 +1,5 @@ +document.write(` +{%- for method in current_class.methods %} + {{ method.name }} +{%- endfor %} +`); \ No newline at end of file diff --git a/src/server/game/LuaEngine/docs/IMPL_DETAILS.md b/src/server/game/LuaEngine/docs/IMPL_DETAILS.md new file mode 100644 index 0000000000..2041dae4f4 --- /dev/null +++ b/src/server/game/LuaEngine/docs/IMPL_DETAILS.md @@ -0,0 +1,102 @@ +# Eluna features +This article contains information about features and important notes regarding Eluna. + +## Settings +Eluna has some settings in the server configuration file. +It is important that you use the new configuration file that you get from compiling after adding Eluna. If the new configuration file is not used you will not receive any error log or output to console. + +The configuration file includes at least the following settings: +- enable and disable Eluna +- enable and disable traceback function - this adds extra debug information if you have the default Eluna extensions. +- configure script folder location +- configure Eluna logging settings + +## Reloading +To make testing easier it is good to know that Eluna scripts can be reloaded by using the command `.reload eluna`. +However this command should be used for development purposes __ONLY__. If you are having issues getting something working __restart__ the server. + +It is important to know that reloading does not trigger for example the login hook for players that are already logged in when reloading. + +## Script loading +Eluna loads scripts from the `lua_scripts` folder by default. You can configure the folder name and location in the server configuration file. +Any hidden folders are not loaded. All script files must have an unique name, otherwise an error is printed and only the first file found is loaded. + +The loading order is not guaranteed to be alphabetic. +Any file having `.ext` extension, for example `test.ext`, is loaded before normal lua files. + +Instead of the ext special feature however it is recommended to use the basic lua `require` function. +The whole script folder structure is added automatically to the lua require path so using require is as simple as providing the file name without any extension for example `require("runfirst")` to require the file `runfirst.lua`. + +## Automatic conversion +In C++ level code you have types like `Unit` and `Creature` and `Player`. +When in code you have an object of type `Unit` you need to convert it to a `Creature` or a `Player` object to be able to access the methods of the subclass. + +In Eluna this is automatic. All objects are automatically converted to the correct type and you will always have full access to all member functions of an object. + +## Storing userdata +Storing userdata objects over time that are memory managed by C++ is a bad idea. +For example you should never save a player to a global variable and then try access it in a timed event. The reason is that the player object in C++ is a pointer to an object that C++ can delete at any time. When time passes the player may have logged out and using the pointer after player object no longer exists can be catastrophic. + +To prevent users from doing this objects that are memory managed by C++ are automatically turned into nil when they are no longer safe to be accessed - this means usually after the hooked function ends. +Instead of storing the object itself you can use store guids `player:GetGUID()` and fetch the object by the guid with `map:GetWorldObject(guid)`. + +Any userdata object that is memory managed by lua is safe to store over time. These objects include but are not limited to: query results, worldpackets, uint64 and int64 numbers. + +## Userdata metamethods +All userdata objects in Eluna have tostring metamethod implemented. +This allows you to print the player object for example and to use `tostring(player)`. + +The userdata uses metatables that contain the methods and functions it uses. +These tables are globally accessible by using the type name. For example `Player` is a global table containing all Player methods. + +You can define new methods in lua for a class using these global tables. +```lua +function Player:CustomFunc(param1) + -- self is the player the method is used on + self:SendBroadcastMessage(param1) +end + +function GameObject:CustomFunc(param1) + -- self is the gameobject the method is used on + print(self:GetName()) +end + +-- Example use: +player:CustomFunc("test") +gob:CustomFunc("test2") +``` + +It is recommended that in normal code these global tables and their names (variables starting with capital letters like Player, Creature, GameObject, Spell..) are avoided so they are not unintentionally edited or deleted causing other scripts possibly not to function. + +## Database +Database is a great thing, but it has it's own issues. + +### Querying +Database queries are slow. The whole server has to wait for the script to fetch the data from disk before continuing. Compared to reading cache or RAM reading from disk is the same as going to the moon to fetch the data (no pun intended). + +Depending on what you need, prefer database Execute over Query when not selecting anything from the database. Database Executes are made asynchronously and they will not keep the server waiting. + +Move all database queries possible to the script loading, server startup or similar one time event and use cache tables to manage the data in scripts. + +### Types +__Database types should be followed strictly.__ +Mysql does math in bigint and decimal formats which is why a simple select like `SELECT 1;` actually returns a bigint. +If you fetch a bigint or decimal using a function for a smaller type it is possible the value is read incorrectly. + +For example the same code for fetching the result of `SELECT 1;` returned 1 on one machine and 0 on another. Using the correct function, in this case GetInt64, the right result was returned on both. https://github.com/ElunaLuaEngine/Eluna/issues/89#issuecomment-64121361 + +| base type | defined type | database type | +|---------------------------|--------------|-----------------------| +| char | int8 | tinyint(3) | +| short int | int16 | smallint(5) | +| (long int / int) | int32 | mediumint(8) | +| (long int / int) | int32 | int(10) | +| long long int | int64 | bigint(20) | +| unsigned char | uint8 | tinyint(3) unsigned | +| unsigned short int | uint16 | smallint(5) unsigned | +| unsigned (long int / int) | uint32 | mediumint(8) unsigned | +| unsigned (long int / int) | uint32 | int(10) unsigned | +| unsigned long long int | uint64 | bigint(20) unsigned | +| float | float | float | +| double | double | double and decimal | +| std::string | std::string | any text type | diff --git a/src/server/game/LuaEngine/docs/INSTALL.md b/src/server/game/LuaEngine/docs/INSTALL.md new file mode 100644 index 0000000000..06051457a2 --- /dev/null +++ b/src/server/game/LuaEngine/docs/INSTALL.md @@ -0,0 +1,42 @@ +# Installing and updating +This page will help you get a cMaNGOS and a TrinityCore source with Eluna. + +If you are looking to get MaNGOS source with Eluna head over to [MaNGOS forum](http://getmangos.eu/) for the installation and updating instructions - however read this page also as it contains important information. + +If you are having trouble with the installation or updating the core source, head over to our [support forum](../README.md#documentation). +If you are looking for a way to merge eluna with a fork of the official repositories see [merging](MERGING.md). + +### Requirements and dependencies: +**Eluna uses `C++11` so you need a compiler that supports it.** +**Eluna can use ACE or BOOST for filesystem library.** +Additionally see you desired core's documentation and installation instructions for it's requirements and dependencies. + +### Installation +1. Open [git bash](http://git-scm.com/) and navigate to where you want the core source +2. Choose the git address of your desired core and patch below and clone the core with `git clone
    `. +For example `git clone https://github.com/ElunaLuaEngine/ElunaTrinityWotlk.git` + * TrinityCore WoTLK: `https://github.com/ElunaLuaEngine/ElunaTrinityWotlk.git` + * cMaNGOS Classic: `https://github.com/ElunaLuaEngine/ElunaMangosClassic.git` + * cMaNGOS TBC: `https://github.com/ElunaLuaEngine/ElunaMangosTbc.git` + * cMaNGOS WoTLK: `https://github.com/ElunaLuaEngine/ElunaMangosWotlk.git` +3. Navigate to the newly created source folder with `git bash` +4. Use the git command `git submodule init` followed by `git submodule update` + * If you really do not get how to use git bash (and do try!) you can navigate to the `LuaEngine` folder and clone the [eluna repository](https://github.com/ElunaLuaEngine/Eluna) there. This is not recommended though. +4. Continue compiling the core normally using the official instructions + * [TrinityCore](https://trinitycore.info/install/Core-Installation) + * [cMaNGOS](https://github.com/cmangos/issues/wiki/Installation-Instructions) + +__Important!__ After compiling use the new configuration files. They contain Eluna settings and without them Eluna may not function correctly. For example you do not get any error messages or error log. + +After installing Eluna you should check out these: +- [Eluna getting started](USAGE.md) +- [Eluna features](IMPL_DETAILS.md) + +### Updating +Updating is essentially handled in the same manner as you would normally update the core and database. +To get the newest core source code open `git bash` and navigate to your local source folder. +Then execute use `git pull` followed by `git submodule init` and `git submodule update`. +After updating the source you need to recompile the core normally. Simply use `CMake` if needed and compile. +To update the databases refer to the core's or database's official updating documents: + * [TrinityCore](http://collab.kpsn.org/display/tc/Databases+Installation) + * [cMaNGOS](https://github.com/cmangos/issues/wiki/Installation-Instructions) diff --git a/src/server/game/LuaEngine/docs/MERGING.md b/src/server/game/LuaEngine/docs/MERGING.md new file mode 100644 index 0000000000..2e488e44e8 --- /dev/null +++ b/src/server/game/LuaEngine/docs/MERGING.md @@ -0,0 +1,42 @@ +# Merging Eluna +Eluna can be added to various sources by applying the core changes required for Eluna to function. +Below you find the guides for merging Eluna with each core or a fork of it. +If you choose to merge you should be able to maintain and update yourself - we do not maintain your core. View Unofficial Merging below. +We also do not fix any merging errors you may have, but you are free to ask about them on the [support forum](../README.md#documentation) and we may assist. + +We recommend using the [installation guide](INSTALL.md) especially if you are not familiar with git and updating the code. +It allows you to simply use `git pull` followed by `git submodule update` to update your source and we will handle the merging and maintenance with the official core source. Naturally you still need to handle updating the database as instructed by the core's wiki or instructions. + +### Merging Eluna with MaNGOS +Eluna is merged with [official MaNGOS](http://getmangos.eu/) by default. + +### Merging Eluna with cMaNGOS +``` +git clone https://github.com/cmangos/mangos-wotlk.git +cd mangos-wotlk +git pull --recurse-submodules https://github.com/ElunaLuaEngine/ElunaMangosWotlk.git +``` +Steps explained: +1. clone the core or fork source or get the it by other means +2. navigate to the source folder +3. pull the Eluna fork. This will fetch the repository and merge it with your source. + * `--recurse-submodules` will automatically pull the submodules (Eluna repository). You may need to use `git submodule init` followed by `git submodule update` if your Eluna folder is empty + * it is important that you choose the correct Eluna fork for your core andpatch: + * [Eluna cMaNGOS Classic](https://github.com/ElunaLuaEngine/ElunaMangosClassic) + * [Eluna cMaNGOS TBC](https://github.com/ElunaLuaEngine/ElunaMangosTbc) + * [Eluna cMaNGOS WotLK](https://github.com/ElunaLuaEngine/ElunaMangosWotlk) + +### Merging Eluna with TrinityCore +``` +git clone https://github.com/TrinityCore/TrinityCore.git -b3.3.5 +cd TrinityCore +git pull --recurse-submodules https://github.com/ElunaLuaEngine/ElunaTrinityWotlk.git +``` +Steps explained: +1. clone the core or fork source or get the it by other means +2. navigate to the source folder +3. pull the Eluna fork. This will fetch the repository and merge it with your source. + * `--recurse-submodules` will automatically pull the submodules (Eluna repository). You may need to use `git submodule init` followed by `git submodule update` if your Eluna folder is empty + * it is important that you choose the correct Eluna fork for your core and patch: + * [Eluna TrinityCore WotLK](https://github.com/ElunaLuaEngine/ElunaTrinityWotlk) + * [Eluna TrinityCore Cataclysm](https://github.com/ElunaLuaEngine/ElunaTrinityCata) diff --git a/src/server/game/LuaEngine/docs/USAGE.md b/src/server/game/LuaEngine/docs/USAGE.md new file mode 100644 index 0000000000..12a899c287 --- /dev/null +++ b/src/server/game/LuaEngine/docs/USAGE.md @@ -0,0 +1,116 @@ +# Using Eluna +Eluna is a lua engine implementation for world of warcraft emulators. +It can be used to create different kind of scripts from AI to events. +This article helps you to get started with Eluna. We go through adding a simple script, where to get information from and a few language basics. + +This article assumes you have already installed Eluna successfully. If you have not, see [installation](INSTALL.md). + +## Basic script +Here is a simple "Hello world" example. +Create a file named `hello world.lua` that contains the following code and place the file inside the scripts folder in your server folder. By default the scripts folder is called `lua_scripts`. The server folder is the folder which contains server executables. +```lua +local PLAYER_EVENT_ON_LOGIN = 3 + +local function OnLogin(event, player) + player:SendBroadcastMessage("Hello world") +end + +RegisterPlayerEvent(PLAYER_EVENT_ON_LOGIN, OnLogin) +``` +If you now restart your server and log in game you are greeted with "Hello world" in your chat. + +### What happened +As you have witnessed here no core compiling was needed and your script runs from the file you just created. + +The file is compiled and run by the lua engine when the server starts up or Eluna is reloaded. +The code in the file registers a function to be run when a player logs in and the function sends a message to the player that logged in. + +## Lua basics +It is good to get to know a bit of lua to code lua scripts. In this article we do not go that much into explaining lua syntax. Here are some pointers to important sources of information and things to get to know about. + +### Sources of information +- lua users wiki + - http://lua-users.org/wiki/LuaDirectory + - http://lua-users.org/wiki/TutorialDirectory + - http://lua-users.org/wiki/SampleCode +- programming in lua http://www.lua.org/pil/1.html +- lua reference manual http://www.lua.org/manual/5.2/ + +### some highlights +- Print function outputs to server console. Very useful for simple debugging `print("anything here")`. +- control structures - especially loops: + - http://lua-users.org/wiki/ControlStructureTutorial + - http://www.lua.org/manual/5.2/manual.html#3.3.5 +- lua string library: + - http://lua-users.org/wiki/StringLibraryTutorial + - http://www.wowwiki.com/Pattern_matching +- Lua tables are the only container in lua and they are essential for good code. Lua tables can be compared to arrays and hash maps. +Table functions and tutorials: + - http://www.lua.org/manual/5.2/manual.html#6.5 + - http://www.lua.org/manual/5.2/manual.html#4.3 + - http://lua-users.org/wiki/TablesTutorial + - http://www.lua.org/pil/2.5.html +- prefer local variables over global. While global variables may work they can create issues with other scripts that use same variable names. +All local variables outside of functions in a script are shared by everything running the same script - the variables are locally global. + +## Eluna basics +It is good to know where you can find information about Eluna and Eluna's API as well as the basic elements of a script. Here are links to the main sources of information: + +- Eluna features [Eluna details](IMPL_DETAILS.md) +- Eluna documentation http://elunaluaengine.github.io/ + +### Error messages +If Eluna is installed correctly, the default installation should make errors output to the console as well as a log file in the server folder. If you can not get your script to work, be sure to check the log file for any errors you might have missed. + +Check out the configuration file for settings if you want to tweak the logging settings. + +### Global functions +Global functions are functions you can run from anywhere in a script file and they do not require any object to be run. +In addition to normal global functions lua provides like `print` Eluna has it's own gobal functions. You can find them in the documentation under `global` class: [global functions](http://elunaluaengine.github.io/Global/index.html). + +```lua +-- print the return value of GetLuaEngine function +print(GetLuaEngine()) +``` + +### Member functions +Member functions, also called methods, are functions that require an userdata object to run. There are several different classes of objects that have different member functions. You can find all the member functions and their documentations from the [Eluna documentation](http://elunaluaengine.github.io/). + +Classes in C++ inherit each other. In Eluna member functions are also inherited. For example objects of classes `Player` and `Creature` inherit all methods from `Unit` class. + +Methods are called by using `:` notation on the object. For example to get the player name you can call the GetName methods like this: `player:GetName()` + +```lua +local entry = 6 +local on_combat = 1 +local function OnCombat(event, creature, target) + -- creature is of type Creature + -- target is of type Creature or Player depending on who the creature is attacking + print(creature:GetLevel()) + print(target:GetLevel()) +end + +RegisterCreatureEvent(entry, on_combat, OnCombat) +``` + +### Registering functions to events +Scripts register functions to events and the functions are executed when the event happens. +There are special global functions in Eluna API for registering functions for events. +You should be able to find all such functions from [Eluna documentation](http://elunaluaengine.github.io/) by searching `register`. + +Functions used to register other functions for events need the ID of the event you want the hook to be registered for passed to them. You can find these ID numbers from the registering function documentation page. + +Eluna passes some arguments to the functions executed. The arguments are always in same order. You can name them in any way you want. In the above script example the event `PLAYER_EVENT_ON_LOGIN` passes the event id and the player who logs in to the registered function. This is why the registered function has these parameters defined: `(event, player)`. + +Some events allow the registered function to return different values. Sometimes you can return more than one value. The possibility to return is documented on the registering function's documentation page. Simply using the `return` keyword returns normally as if the function would end. + +For example in this script we register the function `OnCombat` to be run on event `1`, which triggers on combat, for the creature entry `6`. All needed information can be found here: http://elunaluaengine.github.io/Global/RegisterCreatureEvent.html +```lua +local entry = 6 +local on_combat = 1 +local function OnCombat(event, creature, target) + creature:SendUnitYell("Yiee, me run!", 0) +end + +RegisterCreatureEvent(entry, on_combat, OnCombat) +``` diff --git a/src/server/game/LuaEngine/extensions/ObjectVariables.ext b/src/server/game/LuaEngine/extensions/ObjectVariables.ext new file mode 100644 index 0000000000..8edb47072a --- /dev/null +++ b/src/server/game/LuaEngine/extensions/ObjectVariables.ext @@ -0,0 +1,115 @@ +-- +-- Copyright (C) 2010 - 2024 Eluna Lua Engine +-- This program is free software licensed under GPL version 3 +-- Please see the included DOCS/LICENSE.md for more information +-- + +-- filename.ext files are loaded before normal .lua files + +-- +-- This extension allows saving data to specific object for it's lifetime in current runtime session +-- Supports Map, Player, Creature, GameObject +-- +-- SetData sets a value +-- obj:SetData(key, val) +-- +-- GetData gets the data table or a specific value by key from it +-- local tbl = obj:GetData() +-- local val = obj:GetData(key) +-- + +local pairs = pairs + +local variableStores = { + Map = {}, + Player = {}, + Creature = {}, + GameObject = {}, +} + +local function DestroyMapData(event, obj) + local map = obj:GetMapId() + local inst = obj:GetInstanceId() + for k,v in pairs(variableStores) do + local mapdata = v[map] + if mapdata then + mapdata[inst] = nil + end + end +end + +local function DestroyObjData(event, obj) + local otype = obj:GetObjectType() + local guid = otype == "Map" and 1 or obj:GetGUIDLow() + + if otype == "Player" then + variableStores[otype][guid] = nil + return + end + + local map = obj:GetMapId() + local inst = obj:GetInstanceId() + local mapdata = variableStores[otype][map] + if mapdata then + local instancedata = mapdata[inst] + if instancedata then + instancedata[guid] = nil + end + end +end + +local function GetData(self, field) + local otype = self:GetObjectType() + local guid = otype == "Map" and 1 or self:GetGUIDLow() + local varStore = variableStores[otype] + + if otype == "Player" then + varStore[guid] = varStore[guid] or {} + if field ~= nil then + return varStore[guid][field] + end + return varStore[guid] + end + + local map = self:GetMapId() + local inst = self:GetInstanceId() + varStore[map] = varStore[map] or {} + varStore[map][inst] = varStore[map][inst] or {} + varStore[map][inst][guid] = varStore[map][inst][guid] or {} + + if field ~= nil then + return varStore[map][inst][guid][field] + end + return varStore[map][inst][guid] +end + +local function SetData(self, field, val) + local otype = self:GetObjectType() + local guid = otype == "Map" and 1 or self:GetGUIDLow() + local varStore = variableStores[otype] + + if otype == "Player" then + varStore[guid] = varStore[guid] or {} + varStore[guid][field] = val + return + end + + local map = self:GetMapId() + local inst = self:GetInstanceId() + varStore[map] = varStore[map] or {} + varStore[map][inst] = varStore[map][inst] or {} + varStore[map][inst][guid] = varStore[map][inst][guid] or {} + + varStore[map][inst][guid][field] = val +end + +for k,v in pairs(variableStores) do + _G[k].GetData = GetData + _G[k].SetData = SetData +end + +RegisterPlayerEvent(4, DestroyObjData) -- logout +RegisterServerEvent(31, DestroyObjData) -- creature delete +RegisterServerEvent(32, DestroyObjData) -- gameobject delete +RegisterServerEvent(17, DestroyMapData) -- map create +RegisterServerEvent(18, DestroyMapData) -- map destroy diff --git a/src/server/game/LuaEngine/extensions/StackTracePlus/LICENSE b/src/server/game/LuaEngine/extensions/StackTracePlus/LICENSE new file mode 100644 index 0000000000..49afdbbc39 --- /dev/null +++ b/src/server/game/LuaEngine/extensions/StackTracePlus/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2010 Ignacio Burgueo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/server/game/LuaEngine/extensions/StackTracePlus/README.md b/src/server/game/LuaEngine/extensions/StackTracePlus/README.md new file mode 100644 index 0000000000..8216333f40 --- /dev/null +++ b/src/server/game/LuaEngine/extensions/StackTracePlus/README.md @@ -0,0 +1,128 @@ +# StackTracePlus # + +[![Build Status](https://travis-ci.org/ignacio/StackTracePlus.png?branch=master)](https://travis-ci.org/ignacio/StackTracePlus) + +[StackTracePlus](https://github.com/ignacio/StackTracePlus) provides enhanced stack traces for [Lua 5.1, Lua 5.2][1] and [LuaJIT][2]. + +StackTracePlus can be used as a replacement for debug.traceback. It gives detailed information about locals, tries to guess +function names when they're not available, etc, so, instead of + + lua5.1.exe: D:\trunk_git\sources\stacktraceplus\test\test.lua:10: attempt to concatenate a nil value + stack traceback: + D:\trunk_git\sources\stacktraceplus\test\test.lua:10: in function + (tail call): ? + D:\trunk_git\sources\stacktraceplus\test\test.lua:15: in main chunk + [C]: ? + +you'll get + + lua5.1.exe: D:\trunk_git\sources\stacktraceplus\test\test.lua:10: attempt to concatenate a nil value + Stack Traceback + =============== + (2) C function 'function: 00A8F418' + (3) Lua function 'g' at file 'D:\trunk_git\sources\stacktraceplus\test\test.lua:10' (best guess) + Local variables: + fun = table module + str = string: "hey" + tb = table: 027DCBE0 {dummy:1, blah:true, foo:bar} + (*temporary) = nil + (*temporary) = string: "text" + (*temporary) = string: "attempt to concatenate a nil value" + (4) tail call + (5) main chunk of file 'D:\trunk_git\sources\stacktraceplus\test\test.lua' at line 15 + (6) C function 'function: 002CA480' + +## Usage # + +StackTracePlus can be used as a replacement for `debug.traceback`, as an `xpcall` error handler or even from C code. Note that +only the Lua 5.1 interpreter allows the traceback function to be replaced "on the fly". LuaJIT and Lua 5.2 always calls luaL_traceback internally so there is no easy way to override that. + +```lua +local STP = require "StackTracePlus" + +debug.traceback = STP.stacktrace +function test() + local s = "this is a string" + local n = 42 + local t = { foo = "bar" } + local co = coroutine + local cr = coroutine.create + + error("an error") +end +test() +``` + +That script will output (only with Lua 5.1): + + lua5.1: example.lua:11: an error + Stack Traceback + =============== + (2) C function 'function: 006B5758' + (3) global C function 'error' + (4) Lua global 'test' at file 'example.lua:11' + Local variables: + s = string: "this is a string" + n = number: 42 + t = table: 006E5220 {foo:bar} + co = coroutine table + cr = C function: 003C7080 + (5) main chunk of file 'example.lua' at line 14 + (6) C function 'function: 00637B30' + +**StackTracePlus** is aware of the usual Lua libraries, like *coroutine*, *table*, *string*, *io*, etc and functions like +*print*, *pcall*, *assert*, and so on. + +You can also make STP aware of your own tables and functions by calling *add_known_function* and *add_known_table*. + +```lua +local STP = require "StackTracePlus" + +debug.traceback = STP.stacktrace +local my_table = { + f = function() end +} +function my_function() +end + +function test(data, func) + local s = "this is a string" + + error("an error") +end + +STP.add_known_table(my_table, "A description for my_table") +STP.add_known_function(my_function, "A description for my_function") + +test( my_table, my_function ) +``` + +Will output: + + lua5.1: ..\test\example2.lua:13: an error + Stack Traceback + =============== + (2) C function 'function: 0073AAA8' + (3) global C function 'error' + (4) Lua global 'test' at file '..\test\example2.lua:13' + Local variables: + data = A description for my_table + func = Lua function 'A description for my_function' (defined at line 7 of chunk ..\test\example2.lua) + s = string: "this is a string" + (5) main chunk of file '..\test\example2.lua' at line 19 + (6) C function 'function: 00317B30' + + +## Installation # +The easiest way to install is with [LuaRocks][3]. + + - luarocks install stacktraceplus + +If you don't want to use LuaRocks, just copy StackTracePlus.lua to Lua's path. + +## License # +**StackTracePlus** is available under the MIT license. + +[1]: http://www.lua.org/ +[2]: http://luajit.org/ +[3]: http://luarocks.org/ diff --git a/src/server/game/LuaEngine/extensions/StackTracePlus/StackTracePlus.ext b/src/server/game/LuaEngine/extensions/StackTracePlus/StackTracePlus.ext new file mode 100644 index 0000000000..e538639c5e --- /dev/null +++ b/src/server/game/LuaEngine/extensions/StackTracePlus/StackTracePlus.ext @@ -0,0 +1,415 @@ +-- tables +local _G = _G +local string, io, debug, coroutine = string, io, debug, coroutine + +-- functions +local tostring, print, require = tostring, print, require +local next, assert = next, assert +local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs +local error = error + +assert(debug, "debug table must be available at this point") + +local io_open = io.open +local string_gmatch = string.gmatch +local string_sub = string.sub +local table_concat = table.concat + +local _M = { + max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)' +} + +-- this tables should be weak so the elements in them won't become uncollectable +local m_known_tables = { [_G] = "_G (global table)" } +local function add_known_module(name, desc) + local ok, mod = pcall(require, name) + if ok then + m_known_tables[mod] = desc + end +end + +add_known_module("string", "string module") +add_known_module("io", "io module") +add_known_module("os", "os module") +add_known_module("table", "table module") +add_known_module("math", "math module") +add_known_module("package", "package module") +add_known_module("debug", "debug module") +add_known_module("coroutine", "coroutine module") + +-- lua5.2 +add_known_module("bit32", "bit32 module") +-- luajit +add_known_module("bit", "bit module") +add_known_module("jit", "jit module") +-- lua5.3 +if _VERSION >= "Lua 5.3" then + add_known_module("utf8", "utf8 module") +end + + +local m_user_known_tables = {} + +local m_known_functions = {} +for _, name in ipairs{ + -- Lua 5.2, 5.1 + "assert", + "collectgarbage", + "dofile", + "error", + "getmetatable", + "ipairs", + "load", + "loadfile", + "next", + "pairs", + "pcall", + "print", + "rawequal", + "rawget", + "rawlen", + "rawset", + "require", + "select", + "setmetatable", + "tonumber", + "tostring", + "type", + "xpcall", + + -- Lua 5.1 + "gcinfo", + "getfenv", + "loadstring", + "module", + "newproxy", + "setfenv", + "unpack", + -- TODO: add table.* etc functions +} do + if _G[name] then + m_known_functions[_G[name]] = name + end +end + + + +local m_user_known_functions = {} + +local function safe_tostring (value) + local ok, err = pcall(tostring, value) + if ok then return err else return (": '%s'"):format(err) end +end + +-- Private: +-- Parses a line, looking for possible function definitions (in a very na?ve way) +-- Returns '(anonymous)' if no function name was found in the line +local function ParseLine(line) + assert(type(line) == "string") + --print(line) + local match = line:match("^%s*function%s+(%w+)") + if match then + --print("+++++++++++++function", match) + return match + end + match = line:match("^%s*local%s+function%s+(%w+)") + if match then + --print("++++++++++++local", match) + return match + end + match = line:match("^%s*local%s+(%w+)%s+=%s+function") + if match then + --print("++++++++++++local func", match) + return match + end + match = line:match("%s*function%s*%(") -- this is an anonymous function + if match then + --print("+++++++++++++function2", match) + return "(anonymous)" + end + return "(anonymous)" +end + +-- Private: +-- Tries to guess a function's name when the debug info structure does not have it. +-- It parses either the file or the string where the function is defined. +-- Returns '?' if the line where the function is defined is not found +local function GuessFunctionName(info) + --print("guessing function name") + if type(info.source) == "string" and info.source:sub(1,1) == "@" then + local file, err = io_open(info.source:sub(2), "r") + if not file then + print("file not found: "..tostring(err)) -- whoops! + return "?" + end + local line + for i = 1, info.linedefined do + line = file:read("*l") + end + if not line then + print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + else + local line + local lineNumber = 0 + for l in string_gmatch(info.source, "([^\n]+)\n-") do + lineNumber = lineNumber + 1 + if lineNumber == info.linedefined then + line = l + break + end + end + if not line then + print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + end +end + +--- +-- Dumper instances are used to analyze stacks and collect its information. +-- +local Dumper = {} + +Dumper.new = function(thread) + local t = { lines = {} } + for k,v in pairs(Dumper) do t[k] = v end + + t.dumping_same_thread = (thread == coroutine.running()) + + -- if a thread was supplied, bind it to debug.info and debug.get + -- we also need to skip this additional level we are introducing in the callstack (only if we are running + -- in the same thread we're inspecting) + if type(thread) == "thread" then + t.getinfo = function(level, what) + if t.dumping_same_thread and type(level) == "number" then + level = level + 1 + end + return debug.getinfo(thread, level, what) + end + t.getlocal = function(level, loc) + if t.dumping_same_thread then + level = level + 1 + end + return debug.getlocal(thread, level, loc) + end + else + t.getinfo = debug.getinfo + t.getlocal = debug.getlocal + end + + return t +end + +-- helpers for collecting strings to be used when assembling the final trace +function Dumper:add (text) + self.lines[#self.lines + 1] = text +end +function Dumper:add_f (fmt, ...) + self:add(fmt:format(...)) +end +function Dumper:concat_lines () + return table_concat(self.lines) +end + +--- +-- Private: +-- Iterates over the local variables of a given function. +-- +-- @param level The stack level where the function is. +-- +function Dumper:DumpLocals (level) + local prefix = "\t " + local i = 1 + + if self.dumping_same_thread then + level = level + 1 + end + + local name, value = self.getlocal(level, i) + if not name then + return + end + self:add("\tLocal variables:\r\n") + while name do + if type(value) == "number" then + self:add_f("%s%s = number: %g\r\n", prefix, name, value) + elseif type(value) == "boolean" then + self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value)) + elseif type(value) == "string" then + self:add_f("%s%s = string: %q\r\n", prefix, name, value) + elseif type(value) == "userdata" then + self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value)) + elseif type(value) == "nil" then + self:add_f("%s%s = nil\r\n", prefix, name) + elseif type(value) == "table" then + if m_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value]) + elseif m_user_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value]) + else + local txt = "{" + for k,v in pairs(value) do + txt = txt..safe_tostring(k)..":"..safe_tostring(v) + if #txt > _M.max_tb_output_len then + txt = txt.." (more...)" + break + end + if next(value, k) then txt = txt..", " end + end + self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}") + end + elseif type(value) == "function" then + local info = self.getinfo(value, "nS") + local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value] + if info.what == "C" then + self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value))) + else + local source = info.short_src + if source:sub(2,7) == "string" then + source = source:sub(9) -- uno m?s, por el espacio que viene (string "Baragent.Main", por ejemplo) + end + --for k,v in pairs(info) do print(k,v) end + fun_name = fun_name or GuessFunctionName(info) + self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source) + end + elseif type(value) == "thread" then + self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value)) + end + i = i + 1 + name, value = self.getlocal(level, i) + end +end + + +--- +-- Public: +-- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc. +-- This function is suitable to be used as an error handler with pcall or xpcall +-- +-- @param thread An optional thread whose stack is to be inspected (defaul is the current thread) +-- @param message An optional error string or object. +-- @param level An optional number telling at which level to start the traceback (default is 1) +-- +-- Returns a string with the stack trace and a string with the original error. +-- +function _M.stacktrace(thread, message, level) + if type(thread) ~= "thread" then + -- shift parameters left + thread, message, level = nil, thread, message + end + + thread = thread or coroutine.running() + + level = level or 1 + + local dumper = Dumper.new(thread) + + local original_error + + if type(message) == "table" then + dumper:add("an error object {\r\n") + local first = true + for k,v in pairs(message) do + if first then + dumper:add(" ") + first = false + else + dumper:add(",\r\n ") + end + dumper:add(safe_tostring(k)) + dumper:add(": ") + dumper:add(safe_tostring(v)) + end + dumper:add("\r\n}") + original_error = dumper:concat_lines() + elseif type(message) == "string" then + dumper:add(message) + original_error = message + end + + dumper:add("\r\n") + dumper:add[[ +Stack Traceback +=============== +]] + --print(error_message) + + local level_to_show = level + if dumper.dumping_same_thread then level = level + 1 end + + local info = dumper.getinfo(level, "nSlf") + while info do + if info.what == "main" then + if string_sub(info.source, 1, 1) == "@" then + dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, string_sub(info.source, 2), info.currentline) + else + dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.short_src, info.currentline) + end + elseif info.what == "C" then + --print(info.namewhat, info.name) + --for k,v in pairs(info) do print(k,v, type(v)) end + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func) + dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name) + --dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value))) + elseif info.what == "tail" then + --print("tail") + --for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name) + dumper:add_f("(%d) tail call\r\n", level_to_show) + dumper:DumpLocals(level) + elseif info.what == "Lua" then + local source = info.short_src + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name + if source:sub(2, 7) == "string" then + source = source:sub(9) + end + local was_guessed = false + if not function_name or function_name == "?" then + --for k,v in pairs(info) do print(k,v, type(v)) end + function_name = GuessFunctionName(info) + was_guessed = true + end + -- test if we have a file name + local function_type = (info.namewhat == "") and "function" or info.namewhat + if info.source and info.source:sub(1, 1) == "@" then + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + elseif info.source and info.source:sub(1,1) == '#' then + dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, function_name, info.currentline, source) + end + dumper:DumpLocals(level) + else + dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what) + end + + level = level + 1 + level_to_show = level_to_show + 1 + info = dumper.getinfo(level, "nSlf") + end + + return dumper:concat_lines(), original_error +end + +-- +-- Adds a table to the list of known tables +function _M.add_known_table(tab, description) + if m_known_tables[tab] then + error("Cannot override an already known table") + end + m_user_known_tables[tab] = description +end + +-- +-- Adds a function to the list of known functions +function _M.add_known_function(fun, description) + if m_known_functions[fun] then + error("Cannot override an already known function") + end + m_user_known_functions[fun] = description +end + +return _M diff --git a/src/server/game/LuaEngine/extensions/_Misc.ext b/src/server/game/LuaEngine/extensions/_Misc.ext new file mode 100644 index 0000000000..39b17bfa58 --- /dev/null +++ b/src/server/game/LuaEngine/extensions/_Misc.ext @@ -0,0 +1,14 @@ +-- +-- Copyright (C) 2010 - 2024 Eluna Lua Engine +-- This program is free software licensed under GPL version 3 +-- Please see the included DOCS/LICENSE.md for more information +-- + +-- filename.ext files are loaded before normal .lua files + +-- Randomize random +math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6))) + +-- Set debug.traceback to use StackTracePlus to print full stack trace +local trace = require("StackTracePlus") +debug.traceback = trace.stacktrace diff --git a/src/server/game/LuaEngine/hooks/BattleGroundHooks.cpp b/src/server/game/LuaEngine/hooks/BattleGroundHooks.cpp new file mode 100644 index 0000000000..117727fad3 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/BattleGroundHooks.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT) \ + auto binding = GetBinding>(REGTYPE_BG);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +void Eluna::OnBGStart(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId) +{ + START_HOOK(BG_EVENT_ON_START); + HookPush(bg); + HookPush(bgId); + HookPush(instanceId); + CallAllFunctions(binding, key); +} + +void Eluna::OnBGEnd(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId, Team winner) +{ + START_HOOK(BG_EVENT_ON_END); + HookPush(bg); + HookPush(bgId); + HookPush(instanceId); + HookPush(winner); + CallAllFunctions(binding, key); +} + +void Eluna::OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId) +{ + START_HOOK(BG_EVENT_ON_CREATE); + HookPush(bg); + HookPush(bgId); + HookPush(instanceId); + CallAllFunctions(binding, key); +} + +void Eluna::OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId) +{ + START_HOOK(BG_EVENT_ON_PRE_DESTROY); + HookPush(bg); + HookPush(bgId); + HookPush(instanceId); + CallAllFunctions(binding, key); +} diff --git a/src/server/game/LuaEngine/hooks/CreatureHooks.cpp b/src/server/game/LuaEngine/hooks/CreatureHooks.cpp new file mode 100644 index 0000000000..e46e8eb5b9 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/CreatureHooks.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT, CREATURE) \ + auto CreatureEventBindings = GetBinding>(REGTYPE_CREATURE);\ + auto CreatureUniqueBindings = GetBinding>(REGTYPE_CREATURE_UNIQUE);\ + auto entry_key = EntryKey(EVENT, CREATURE->GetEntry());\ + auto unique_key = UniqueObjectKey(EVENT, CREATURE->GET_GUID(), CREATURE->GetInstanceId());\ + if (!CreatureEventBindings->HasBindingsFor(entry_key))\ + if (!CreatureUniqueBindings->HasBindingsFor(unique_key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, CREATURE, RETVAL) \ + auto CreatureEventBindings = GetBinding>(REGTYPE_CREATURE);\ + auto CreatureUniqueBindings = GetBinding>(REGTYPE_CREATURE_UNIQUE);\ + auto entry_key = EntryKey(EVENT, CREATURE->GetEntry());\ + auto unique_key = UniqueObjectKey(EVENT, CREATURE->GET_GUID(), CREATURE->GetInstanceId());\ + if (!CreatureEventBindings->HasBindingsFor(entry_key))\ + if (!CreatureUniqueBindings->HasBindingsFor(unique_key))\ + return RETVAL; + +void Eluna::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Creature* pTarget) +{ + START_HOOK(CREATURE_EVENT_ON_DUMMY_EFFECT, pTarget); + HookPush(pCaster); + HookPush(spellId); + HookPush(effIndex); + HookPush(pTarget); + CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +bool Eluna::OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_QUEST_ACCEPT, pCreature, false); + HookPush(pPlayer); + HookPush(pCreature); + HookPush(pQuest); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +bool Eluna::OnQuestReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_QUEST_REWARD, pCreature, false); + HookPush(pPlayer); + HookPush(pCreature); + HookPush(pQuest); + HookPush(opt); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +void Eluna::GetDialogStatus(const Player* pPlayer, const Creature* pCreature) +{ + START_HOOK(CREATURE_EVENT_ON_DIALOG_STATUS, pCreature); + HookPush(pPlayer); + HookPush(pCreature); + CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +void Eluna::OnAddToWorld(Creature* pCreature) +{ + START_HOOK(CREATURE_EVENT_ON_ADD, pCreature); + HookPush(pCreature); + CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +void Eluna::OnRemoveFromWorld(Creature* pCreature) +{ + START_HOOK(CREATURE_EVENT_ON_REMOVE, pCreature); + HookPush(pCreature); + CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +bool Eluna::OnSummoned(Creature* pCreature, Unit* pSummoner) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED, pCreature, false); + HookPush(pCreature); + HookPush(pSummoner); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +bool Eluna::UpdateAI(Creature* me, const uint32 diff) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_AIUPDATE, me, false); + HookPush(me); + HookPush(diff); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +//Called for reaction at enter to combat if not in combat yet (enemy can be NULL) +//Called at creature aggro either by MoveInLOS or Attack Start +bool Eluna::EnterCombat(Creature* me, Unit* target) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_ENTER_COMBAT, me, false); + HookPush(me); + HookPush(target); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called at any Damage from any attacker (before damage apply) +bool Eluna::DamageTaken(Creature* me, Unit* attacker, uint32& damage) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DAMAGE_TAKEN, me, false); + bool result = false; + HookPush(me); + HookPush(attacker); + HookPush(damage); + int damageIndex = lua_gettop(L); + int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3); + + while (n > 0) + { + int r = CallOneFunction(n--, 3, 2); + + if (lua_isboolean(L, r + 0) && lua_toboolean(L, r + 0)) + result = true; + + if (lua_isnumber(L, r + 1)) + { + damage = CHECKVAL(r + 1); + // Update the stack for subsequent calls. + ReplaceArgument(damage, damageIndex); + } + + lua_pop(L, 2); + } + + CleanUpStack(3); + return result; +} + +//Called at creature death +bool Eluna::JustDied(Creature* me, Unit* killer) +{ + On_Reset(me); + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DIED, me, false); + HookPush(me); + HookPush(killer); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +//Called at creature killing another unit +bool Eluna::KilledUnit(Creature* me, Unit* victim) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_TARGET_DIED, me, false); + HookPush(me); + HookPush(victim); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when the creature summon successfully other creature +bool Eluna::JustSummoned(Creature* me, Creature* summon) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, me, false); + HookPush(me); + HookPush(summon); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when a summoned creature is despawned +bool Eluna::SummonedCreatureDespawn(Creature* me, Creature* summon) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, me, false); + HookPush(me); + HookPush(summon); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +//Called at waypoint reached or PointMovement end +bool Eluna::MovementInform(Creature* me, uint32 type, uint32 id) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_REACH_WP, me, false); + HookPush(me); + HookPush(type); + HookPush(id); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called before EnterCombat even before the creature is in combat. +bool Eluna::AttackStart(Creature* me, Unit* target) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_PRE_COMBAT, me, false); + HookPush(me); + HookPush(target); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called for reaction at stopping attack at no attackers or targets +bool Eluna::EnterEvadeMode(Creature* me) +{ + On_Reset(me); + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_LEAVE_COMBAT, me, false); + HookPush(me); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when creature is spawned or respawned (for reseting variables) +bool Eluna::JustRespawned(Creature* me) +{ + On_Reset(me); + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SPAWN, me, false); + HookPush(me); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called at reaching home after evade +bool Eluna::JustReachedHome(Creature* me) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_REACH_HOME, me, false); + HookPush(me); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called at text emote receive from player +bool Eluna::ReceiveEmote(Creature* me, Player* player, uint32 emoteId) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_RECEIVE_EMOTE, me, false); + HookPush(me); + HookPush(player); + HookPush(emoteId); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// called when the corpse of this creature gets removed +bool Eluna::CorpseRemoved(Creature* me, uint32& respawnDelay) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_CORPSE_REMOVED, me, false); + bool result = false; + HookPush(me); + HookPush(respawnDelay); + int respawnDelayIndex = lua_gettop(L); + int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && lua_toboolean(L, r + 0)) + result = true; + + if (lua_isnumber(L, r + 1)) + { + respawnDelay = CHECKVAL(r + 1); + // Update the stack for subsequent calls. + ReplaceArgument(respawnDelay, respawnDelayIndex); + } + + lua_pop(L, 2); + } + + CleanUpStack(2); + return result; +} + +bool Eluna::MoveInLineOfSight(Creature* me, Unit* who) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_MOVE_IN_LOS, me, false); + HookPush(me); + HookPush(who); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called on creature initial spawn, respawn, death, evade (leave combat) +void Eluna::On_Reset(Creature* me) // Not an override, custom +{ + START_HOOK(CREATURE_EVENT_ON_RESET, me); + HookPush(me); + CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when hit by a spell +bool Eluna::SpellHit(Creature* me, WorldObject* caster, SpellInfo const* spell) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_HIT_BY_SPELL, me, false); + HookPush(me); + HookPush(caster); + HookPush(spell->Id); // Pass spell object? + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when spell hits a target +bool Eluna::SpellHitTarget(Creature* me, WorldObject* target, SpellInfo const* spell) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SPELL_HIT_TARGET, me, false); + HookPush(me); + HookPush(target); + HookPush(spell->Id); // Pass spell object? + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + +bool Eluna::SummonedCreatureDies(Creature* me, Creature* summon, Unit* killer) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, me, false); + HookPush(me); + HookPush(summon); + HookPush(killer); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when owner takes damage +bool Eluna::OwnerAttackedBy(Creature* me, Unit* attacker) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_OWNER_ATTACKED_AT, me, false); + HookPush(me); + HookPush(attacker); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +// Called when owner attacks something +bool Eluna::OwnerAttacked(Creature* me, Unit* target) +{ + START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_OWNER_ATTACKED, me, false); + HookPush(me); + HookPush(target); + return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key); +} + +#endif // ELUNA_TRINITY diff --git a/src/server/game/LuaEngine/hooks/GameObjectHooks.cpp b/src/server/game/LuaEngine/hooks/GameObjectHooks.cpp new file mode 100644 index 0000000000..6d886e5f23 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/GameObjectHooks.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaEventMgr.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT, ENTRY) \ + auto binding = GetBinding>(REGTYPE_GAMEOBJECT);\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, ENTRY, RETVAL) \ + auto binding = GetBinding>(REGTYPE_GAMEOBJECT);\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +void Eluna::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, GameObject* pTarget) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry()); + HookPush(pCaster); + HookPush(spellId); + HookPush(effIndex); + HookPush(pTarget); + CallAllFunctions(binding, key); +} + +void Eluna::UpdateAI(GameObject* pGameObject, uint32 diff) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_AIUPDATE, pGameObject->GetEntry()); + HookPush(pGameObject); + HookPush(diff); + CallAllFunctions(binding, key); +} + +bool Eluna::OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest) +{ + START_HOOK_WITH_RETVAL(GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, pGameObject->GetEntry(), false); + HookPush(pPlayer); + HookPush(pGameObject); + HookPush(pQuest); + return CallAllFunctionsBool(binding, key); +} + +bool Eluna::OnQuestReward(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest, uint32 opt) +{ + START_HOOK_WITH_RETVAL(GAMEOBJECT_EVENT_ON_QUEST_REWARD, pGameObject->GetEntry(), false); + HookPush(pPlayer); + HookPush(pGameObject); + HookPush(pQuest); + HookPush(opt); + return CallAllFunctionsBool(binding, key); +} + +void Eluna::GetDialogStatus(const Player* pPlayer, const GameObject* pGameObject) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_DIALOG_STATUS, pGameObject->GetEntry()); + HookPush(pPlayer); + HookPush(pGameObject); + CallAllFunctions(binding, key); +} + +#if ELUNA_EXPANSION >= EXP_WOTLK +#ifndef ELUNA_AZEROTHCORE +void Eluna::OnDestroyed(GameObject* pGameObject, WorldObject* attacker) +#else +void Eluna::OnDestroyed(GameObject* pGameObject, Player* attacker) +#endif +{ + START_HOOK(GAMEOBJECT_EVENT_ON_DESTROYED, pGameObject->GetEntry()); + HookPush(pGameObject); + HookPush(attacker); + CallAllFunctions(binding, key); +} + +#ifndef ELUNA_AZEROTHCORE +void Eluna::OnDamaged(GameObject* pGameObject, WorldObject* attacker) +#else +void Eluna::OnDamaged(GameObject* pGameObject, Player* attacker) +#endif +{ + START_HOOK(GAMEOBJECT_EVENT_ON_DAMAGED, pGameObject->GetEntry()); + HookPush(pGameObject); + HookPush(attacker); + CallAllFunctions(binding, key); +} +#endif + +void Eluna::OnLootStateChanged(GameObject* pGameObject, uint32 state) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, pGameObject->GetEntry()); + HookPush(pGameObject); + HookPush(state); + CallAllFunctions(binding, key); +} + +void Eluna::OnGameObjectStateChanged(GameObject* pGameObject, uint32 state) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, pGameObject->GetEntry()); + HookPush(pGameObject); + HookPush(state); + CallAllFunctions(binding, key); +} + +void Eluna::OnSpawn(GameObject* pGameObject) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_SPAWN, pGameObject->GetEntry()); + HookPush(pGameObject); + CallAllFunctions(binding, key); +} + +void Eluna::OnAddToWorld(GameObject* pGameObject) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_ADD, pGameObject->GetEntry()); + HookPush(pGameObject); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemoveFromWorld(GameObject* pGameObject) +{ + START_HOOK(GAMEOBJECT_EVENT_ON_REMOVE, pGameObject->GetEntry()); + HookPush(pGameObject); + CallAllFunctions(binding, key); +} + +bool Eluna::OnGameObjectUse(Player* pPlayer, GameObject* pGameObject) +{ + START_HOOK_WITH_RETVAL(GAMEOBJECT_EVENT_ON_USE, pGameObject->GetEntry(), false); + HookPush(pGameObject); + HookPush(pPlayer); + return CallAllFunctionsBool(binding, key); +} diff --git a/src/server/game/LuaEngine/hooks/GossipHooks.cpp b/src/server/game/LuaEngine/hooks/GossipHooks.cpp new file mode 100644 index 0000000000..748a50ec2e --- /dev/null +++ b/src/server/game/LuaEngine/hooks/GossipHooks.cpp @@ -0,0 +1,189 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(REGTYPE, EVENT, ENTRY) \ + auto binding = GetBinding>(REGTYPE);\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(REGTYPE, EVENT, ENTRY, RETVAL) \ + auto binding = GetBinding>(REGTYPE);\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +bool Eluna::OnGossipHello(Player* pPlayer, GameObject* pGameObject) +{ + START_HOOK_WITH_RETVAL(REGTYPE_GAMEOBJECT_GOSSIP, GOSSIP_EVENT_ON_HELLO, pGameObject->GetEntry(), false); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pGameObject); + return CallAllFunctionsBool(binding, key, true); +} + +bool Eluna::OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action) +{ + START_HOOK_WITH_RETVAL(REGTYPE_GAMEOBJECT_GOSSIP, GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry(), false); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pGameObject); + HookPush(sender); + HookPush(action); + return CallAllFunctionsBool(binding, key, true); +} + +bool Eluna::OnGossipSelectCode(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code) +{ + START_HOOK_WITH_RETVAL(REGTYPE_GAMEOBJECT_GOSSIP, GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry(), false); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pGameObject); + HookPush(sender); + HookPush(action); + HookPush(code); + return CallAllFunctionsBool(binding, key, true); +} + +void Eluna::HandleGossipSelectOption(Player* pPlayer, uint32 menuId, uint32 sender, uint32 action, const std::string& code) +{ + START_HOOK(REGTYPE_PLAYER_GOSSIP, GOSSIP_EVENT_ON_SELECT, menuId); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + + HookPush(pPlayer); // receiver + HookPush(pPlayer); // sender, just not to mess up the amount of args. + HookPush(sender); + HookPush(action); + if (code.empty()) + HookPush(); + else + HookPush(code); + + CallAllFunctions(binding, key); +} + +bool Eluna::OnItemGossip(Player* pPlayer, Item* pItem, SpellCastTargets const& /*targets*/) +{ + START_HOOK_WITH_RETVAL(REGTYPE_ITEM_GOSSIP, GOSSIP_EVENT_ON_HELLO, pItem->GetEntry(), true); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pItem); + return CallAllFunctionsBool(binding, key, true); +} + +void Eluna::HandleGossipSelectOption(Player* pPlayer, Item* pItem, uint32 sender, uint32 action, const std::string& code) +{ + START_HOOK(REGTYPE_ITEM_GOSSIP, GOSSIP_EVENT_ON_SELECT, pItem->GetEntry()); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + + HookPush(pPlayer); + HookPush(pItem); + HookPush(sender); + HookPush(action); + if (code.empty()) + HookPush(); + else + HookPush(code); + + CallAllFunctions(binding, key); +} + +bool Eluna::OnGossipHello(Player* pPlayer, Creature* pCreature) +{ + START_HOOK_WITH_RETVAL(REGTYPE_CREATURE_GOSSIP, GOSSIP_EVENT_ON_HELLO, pCreature->GetEntry(), false); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pCreature); + return CallAllFunctionsBool(binding, key, true); +} + +bool Eluna::OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action) +{ + START_HOOK_WITH_RETVAL(REGTYPE_CREATURE_GOSSIP, GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry(), false); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + auto original_menu = *pPlayer->GetPlayerMenu(); + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + auto original_menu = *pPlayer->PlayerTalkClass; + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pCreature); + HookPush(sender); + HookPush(action); + auto preventDefault = CallAllFunctionsBool(binding, key, true); + if (!preventDefault) { +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + *pPlayer->GetPlayerMenu() = original_menu; +#else + *pPlayer->PlayerTalkClass = original_menu; +#endif + } + return preventDefault; +} + +bool Eluna::OnGossipSelectCode(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code) +{ + START_HOOK_WITH_RETVAL(REGTYPE_CREATURE_GOSSIP, GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry(), false); +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + auto original_menu = *pPlayer->GetPlayerMenu(); + pPlayer->GetPlayerMenu()->ClearMenus(); +#else + auto original_menu = *pPlayer->PlayerTalkClass; + pPlayer->PlayerTalkClass->ClearMenus(); +#endif + HookPush(pPlayer); + HookPush(pCreature); + HookPush(sender); + HookPush(action); + HookPush(code); + auto preventDefault = CallAllFunctionsBool(binding, key, true); + if (!preventDefault) { +#if defined ELUNA_CMANGOS && ELUNA_EXPANSION < EXP_CATA + *pPlayer->GetPlayerMenu() = original_menu; +#else + *pPlayer->PlayerTalkClass = original_menu; +#endif + } + return preventDefault; +} diff --git a/src/server/game/LuaEngine/hooks/GroupHooks.cpp b/src/server/game/LuaEngine/hooks/GroupHooks.cpp new file mode 100644 index 0000000000..91948ae7cd --- /dev/null +++ b/src/server/game/LuaEngine/hooks/GroupHooks.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT) \ + auto binding = GetBinding>(REGTYPE_GROUP);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \ + auto binding = GetBinding>(REGTYPE_GROUP);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +void Eluna::OnAddMember(Group* group, ObjectGuid guid) +{ + START_HOOK(GROUP_EVENT_ON_MEMBER_ADD); + HookPush(group); + HookPush(guid); + CallAllFunctions(binding, key); +} + +void Eluna::OnInviteMember(Group* group, ObjectGuid guid) +{ + START_HOOK(GROUP_EVENT_ON_MEMBER_INVITE); + HookPush(group); + HookPush(guid); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemoveMember(Group* group, ObjectGuid guid, uint8 method) +{ + START_HOOK(GROUP_EVENT_ON_MEMBER_REMOVE); + HookPush(group); + HookPush(guid); + HookPush(method); + CallAllFunctions(binding, key); +} + +void Eluna::OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid) +{ + START_HOOK(GROUP_EVENT_ON_LEADER_CHANGE); + HookPush(group); + HookPush(newLeaderGuid); + HookPush(oldLeaderGuid); + CallAllFunctions(binding, key); +} + +void Eluna::OnDisband(Group* group) +{ + START_HOOK(GROUP_EVENT_ON_DISBAND); + HookPush(group); + CallAllFunctions(binding, key); +} + +void Eluna::OnCreate(Group* group, ObjectGuid leaderGuid, GroupType groupType) +{ + START_HOOK(GROUP_EVENT_ON_CREATE); + HookPush(group); + HookPush(leaderGuid); + HookPush(groupType); + CallAllFunctions(binding, key); +} + +bool Eluna::OnMemberAccept(Group* group, Player* player) +{ + START_HOOK_WITH_RETVAL(GROUP_EVENT_ON_MEMBER_ACCEPT, true); + HookPush(group); + HookPush(player); + return CallAllFunctionsBool(binding, key, true); +} diff --git a/src/server/game/LuaEngine/hooks/GuildHooks.cpp b/src/server/game/LuaEngine/hooks/GuildHooks.cpp new file mode 100644 index 0000000000..75bcefb457 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/GuildHooks.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT) \ + auto binding = GetBinding>(REGTYPE_GUILD);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +void Eluna::OnAddMember(Guild* guild, Player* player, uint32 plRank) +{ + START_HOOK(GUILD_EVENT_ON_ADD_MEMBER); + HookPush(guild); + HookPush(player); + HookPush(plRank); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemoveMember(Guild* guild, Player* player, bool isDisbanding) +{ + START_HOOK(GUILD_EVENT_ON_REMOVE_MEMBER); + HookPush(guild); + HookPush(player); + HookPush(isDisbanding); + CallAllFunctions(binding, key); +} + +void Eluna::OnMOTDChanged(Guild* guild, const std::string& newMotd) +{ + START_HOOK(GUILD_EVENT_ON_MOTD_CHANGE); + HookPush(guild); + HookPush(newMotd); + CallAllFunctions(binding, key); +} + +void Eluna::OnInfoChanged(Guild* guild, const std::string& newInfo) +{ + START_HOOK(GUILD_EVENT_ON_INFO_CHANGE); + HookPush(guild); + HookPush(newInfo); + CallAllFunctions(binding, key); +} + +void Eluna::OnCreate(Guild* guild, Player* leader, const std::string& name) +{ + START_HOOK(GUILD_EVENT_ON_CREATE); + HookPush(guild); + HookPush(leader); + HookPush(name); + CallAllFunctions(binding, key); +} + +void Eluna::OnDisband(Guild* guild) +{ + START_HOOK(GUILD_EVENT_ON_DISBAND); + HookPush(guild); + CallAllFunctions(binding, key); +} + +void Eluna::OnMemberWitdrawMoney(Guild* guild, Player* player, uint32& amount, bool isRepair) +{ + START_HOOK(GUILD_EVENT_ON_MONEY_WITHDRAW); + HookPush(guild); + HookPush(player); + HookPush(amount); + HookPush(isRepair); // isRepair not a part of Mangos, implement? + int amountIndex = lua_gettop(L) - 1; + int n = SetupStack(binding, key, 4); + + while (n > 0) + { + int r = CallOneFunction(n--, 4, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(4); +} + +#if ELUNA_EXPANSION >= EXP_CATA +void Eluna::OnMemberWitdrawMoney(Guild* guild, Player* player, uint64& amount, bool isRepair) +{ + START_HOOK(GUILD_EVENT_ON_MONEY_WITHDRAW); + HookPush(guild); + HookPush(player); + HookPush(amount); + HookPush(isRepair); // isRepair not a part of Mangos, implement? + int amountIndex = lua_gettop(L) - 1; + int n = SetupStack(binding, key, 4); + + while (n > 0) + { + int r = CallOneFunction(n--, 4, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(4); +} +#endif + +void Eluna::OnMemberDepositMoney(Guild* guild, Player* player, uint32& amount) +{ + START_HOOK(GUILD_EVENT_ON_MONEY_DEPOSIT); + HookPush(guild); + HookPush(player); + HookPush(amount); + int amountIndex = lua_gettop(L); + int n = SetupStack(binding, key, 3); + + while (n > 0) + { + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(3); +} + +#if ELUNA_EXPANSION >= EXP_CATA +void Eluna::OnMemberDepositMoney(Guild* guild, Player* player, uint64& amount) +{ + START_HOOK(GUILD_EVENT_ON_MONEY_DEPOSIT); + HookPush(guild); + HookPush(player); + HookPush(amount); + int amountIndex = lua_gettop(L); + int n = SetupStack(binding, key, 3); + + while (n > 0) + { + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(3); +} +#endif + +void Eluna::OnItemMove(Guild* guild, Player* player, Item* pItem, bool isSrcBank, uint8 srcContainer, uint8 srcSlotId, + bool isDestBank, uint8 destContainer, uint8 destSlotId) +{ + START_HOOK(GUILD_EVENT_ON_ITEM_MOVE); + HookPush(guild); + HookPush(player); + HookPush(pItem); + HookPush(isSrcBank); + HookPush(srcContainer); + HookPush(srcSlotId); + HookPush(isDestBank); + HookPush(destContainer); + HookPush(destSlotId); + CallAllFunctions(binding, key); +} + +void Eluna::OnEvent(Guild* guild, uint8 eventType, uint32 playerGuid1, uint32 playerGuid2, uint8 newRank) +{ + START_HOOK(GUILD_EVENT_ON_EVENT); + HookPush(guild); + HookPush(eventType); + HookPush(playerGuid1); + HookPush(playerGuid2); + HookPush(newRank); + CallAllFunctions(binding, key); +} + +void Eluna::OnBankEvent(Guild* guild, uint8 eventType, uint8 tabId, uint32 playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) +{ + START_HOOK(GUILD_EVENT_ON_BANK_EVENT); + HookPush(guild); + HookPush(eventType); + HookPush(tabId); + HookPush(playerGuid); + HookPush(itemOrMoney); + HookPush(itemStackCount); + HookPush(destTabId); + CallAllFunctions(binding, key); +} diff --git a/src/server/game/LuaEngine/hooks/HookHelpers.h b/src/server/game/LuaEngine/hooks/HookHelpers.h new file mode 100644 index 0000000000..abec43dceb --- /dev/null +++ b/src/server/game/LuaEngine/hooks/HookHelpers.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef _HOOK_HELPERS_H +#define _HOOK_HELPERS_H + +#include "LuaEngine.h" +#include "ElunaUtility.h" + +template +struct LuaRet; + +template<> +struct LuaRet +{ + static bool Is(lua_State* L, int idx) { return lua_isboolean(L, idx); } + static bool Get(Eluna* /*E*/, lua_State* L, int idx) { return lua_toboolean(L, idx) != 0; } +}; + +template<> +struct LuaRet +{ + static bool Is(lua_State* L, int idx) { return lua_isnumber(L, idx); } + static int32 Get(Eluna* E, lua_State* /*L*/, int idx) { return E->CHECKVAL(idx); } +}; + +template<> +struct LuaRet +{ + static bool Is(lua_State* L, int idx) { return lua_isnumber(L, idx); } + static uint32 Get(Eluna* E, lua_State* /*L*/, int idx) { return E->CHECKVAL(idx); } +}; + +template<> +struct LuaRet +{ + static bool Is(lua_State* L, int idx) { return lua_isnumber(L, idx); } + static float Get(Eluna* /*E*/, lua_State* L, int idx) { return static_cast(lua_tonumber(L, idx)); } +}; + +template<> +struct LuaRet +{ + static bool Is(lua_State* L, int idx) { return lua_isnumber(L, idx); } + static double Get(Eluna* /*E*/, lua_State* L, int idx) { return lua_tonumber(L, idx); } +}; + +template<> +struct LuaRet +{ + static bool Is(lua_State* L, int idx) { return lua_isstring(L, idx); } + static std::string Get(Eluna* /*E*/, lua_State* L, int idx) + { + const char* s = lua_tostring(L, idx); + return s ? std::string(s) : std::string(); + } +}; + +/* + * Sets up the stack so that event handlers can be called. + * + * Returns the number of functions that were pushed onto the stack. + */ +template +int Eluna::SetupStack(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, int number_of_arguments) +{ + ASSERT(number_of_arguments == this->push_counter); + ASSERT(key1.event_id == key2.event_id); + // Stack: [arguments] + + HookPush(key1.event_id); + this->push_counter = 0; + ++number_of_arguments; + // Stack: [arguments], event_id + + int arguments_top = lua_gettop(L); + int first_argument_index = arguments_top - number_of_arguments + 1; + ASSERT(arguments_top >= number_of_arguments); + + lua_insert(L, first_argument_index); + // Stack: event_id, [arguments] + + bindings1->PushRefsFor(key1); + if (bindings2) + bindings2->PushRefsFor(key2); + // Stack: event_id, [arguments], [functions] + + int number_of_functions = lua_gettop(L) - arguments_top; + return number_of_functions; +} + +/* + * Replace one of the arguments pushed before `SetupStack` with a new value. + */ +template +void Eluna::ReplaceArgument(T value, int index) +{ + ASSERT(index > 0 && index <= lua_gettop(L)); + // Stack: event_id, [arguments], [functions], [results] + + Push(value); + // Stack: event_id, [arguments], [functions], [results], value + + lua_replace(L, index + 1); + // Stack: event_id, [arguments and value], [functions], [results] +} + +/* + * indices[i] = stack index captured BEFORE SetupStack() inserts event_id. + * If indices[i] == 0, we won't ReplaceArgument for that output. + */ +template +void Eluna::ApplyMultiReturnsImpl(int r, std::tuple& outs, const std::array& indices, std::index_sequence) +{ + ( [&] { + using T = std::remove_reference_t>>; + const int idx = r + static_cast(Is); + + if (LuaRet::Is(L, idx)) + { + std::get(outs) = LuaRet::Get(this, L, idx); + + if (indices[Is] != 0) + ReplaceArgument(std::get(outs), indices[Is]); + } + }(), ... ); +} + +/* + * Call all event handlers registered to the event ID/entry combination and ignore any results. + */ +template +void Eluna::CallAllFunctions(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2) +{ + int number_of_arguments = this->push_counter; + // Stack: [arguments] + + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + CallOneFunction(number_of_functions, number_of_arguments, 0); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + + CleanUpStack(number_of_arguments); + // Stack: (empty) +} + +/* + * Call all event handlers registered to the event ID/entry combination, + * and returns `default_value` if ALL event handlers returned `default_value`, + * otherwise returns the opposite of `default_value`. + */ +template +bool Eluna::CallAllFunctionsBool(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, bool default_value/* = false*/) +{ + bool result = default_value; + // Note: number_of_arguments here does not count in eventID, which is pushed in SetupStack + int number_of_arguments = this->push_counter; + // Stack: [arguments] + + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + int r = CallOneFunction(number_of_functions, number_of_arguments, 1); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1], result + + if (lua_isboolean(L, r) && (lua_toboolean(L, r) == 1) != default_value) + result = !default_value; + + lua_pop(L, 1); + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + + CleanUpStack(number_of_arguments); + // Stack: (empty) + return result; +} + +/* + * Call all event handlers registered to the event ID/entry combination, + * requesting N returns (N = sizeof...(Outs)), and decoding each return into + * the corresponding out reference if the Lua type matches. + * + * If `out_arg_indices[i]` is non-zero, the corresponding argument slot is + * replaced in-place so subsequent handlers receive the updated value. + */ +template +void Eluna::CallAllFunctionsMultiReturn(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, std::tuple outs, const std::array& out_arg_indices) +{ + constexpr int number_of_returns = static_cast(sizeof...(Outs)); + const int number_of_arguments = this->push_counter; + + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + int r = CallOneFunction(number_of_functions, number_of_arguments, number_of_returns); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1], [ret0..retN-1] + + ApplyMultiReturnsImpl(r, outs, out_arg_indices, std::index_sequence_for{}); + + lua_pop(L, number_of_returns); + // Stack: event_id, [arguments], [functions - 1] + } + + CleanUpStack(number_of_arguments); +} + +/* + * Call all event handlers registered to the event ID/entry combination, + * and returns `default_value` if ALL event handlers returned `default_value`, + * otherwise returns the most recent non-`default_value` return value. + */ +template +int Eluna::CallAllFunctionsInt(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, int32 default_value/* = 0*/) +{ + int result = default_value; + int number_of_arguments = this->push_counter; + // Stack: [arguments] + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + while (number_of_functions > 0) + { + int r = CallOneFunction(number_of_functions, number_of_arguments, 1); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1], result + if (lua_isnumber(L, r)) + { + int32 ret = static_cast(lua_tointeger(L, r)); + if (ret != default_value) + result = ret; + } + lua_pop(L, 1); + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + CleanUpStack(number_of_arguments); + // Stack: (empty) + return result; +} + +template +void Eluna::CallAllFunctionsTable(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, std::list& list) +{ + const int number_of_arguments = this->push_counter; + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + // Build table from list at a known stack position + lua_newtable(L); + int tableIndex = lua_gettop(L); + int i = 1; + for (T* entry : list) + { + Push(entry); + lua_rawseti(L, tableIndex, i++); + } + + while (number_of_functions > 0) + { + lua_pushvalue(L, tableIndex); + CallOneFunction(number_of_functions, number_of_arguments + 1, 0); + --number_of_functions; + } + + // Read modified table back into list + list.clear(); + lua_pushnil(L); + while (lua_next(L, tableIndex) != 0) + { + if (T* obj = CHECKOBJ(lua_gettop(L), false)) + list.push_back(obj); + lua_pop(L, 1); + } + + lua_pop(L, 1); // pop table + CleanUpStack(number_of_arguments); +} + +#endif // _HOOK_HELPERS_H diff --git a/src/server/game/LuaEngine/hooks/Hooks.h b/src/server/game/LuaEngine/hooks/Hooks.h new file mode 100644 index 0000000000..a238512faf --- /dev/null +++ b/src/server/game/LuaEngine/hooks/Hooks.h @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef _HOOKS_H +#define _HOOKS_H + +#if defined ELUNA_CMANGOS +#include "Platform/Define.h" +#endif +#include + +struct EventEntry +{ + uint8 id; + const char* name; +}; + +struct HookStorage +{ + const char* category; + const EventEntry* events; + size_t eventCount; +}; + +template +constexpr size_t CountOf(const T(&)[N]) { return N; } + +namespace Hooks +{ + enum RegisterTypes : uint8 + { + REGTYPE_PACKET, + REGTYPE_SERVER, + REGTYPE_PLAYER, + REGTYPE_GUILD, + REGTYPE_GROUP, + REGTYPE_CREATURE, + REGTYPE_CREATURE_UNIQUE, + REGTYPE_VEHICLE, + REGTYPE_CREATURE_GOSSIP, + REGTYPE_GAMEOBJECT, + REGTYPE_GAMEOBJECT_GOSSIP, + REGTYPE_SPELL, + REGTYPE_ITEM, + REGTYPE_ITEM_GOSSIP, + REGTYPE_PLAYER_GOSSIP, + REGTYPE_BG, + REGTYPE_MAP, + REGTYPE_INSTANCE, + REGTYPE_COUNT + }; + + // PACKET EVENTS + #define PACKET_EVENTS_LIST(X) \ + X(PACKET_EVENT_ON_PACKET_RECEIVE, 5, "on_receive") \ + X(PACKET_EVENT_ON_PACKET_RECEIVE_UNKNOWN, 6, "on_receive_unk") \ + X(PACKET_EVENT_ON_PACKET_SEND, 7, "on_send") + + enum PacketEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + PACKET_EVENTS_LIST(X) + #undef X + PACKET_EVENT_COUNT + }; + + static constexpr EventEntry PacketEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + PACKET_EVENTS_LIST(X) + #undef X + }; + + // SERVER EVENTS + #define SERVER_EVENTS_LIST(X) \ + /* Server */ \ + X(SERVER_EVENT_ON_NETWORK_START, 1, "on_network_start") \ + X(SERVER_EVENT_ON_NETWORK_STOP, 2, "on_network_stop") \ + X(SERVER_EVENT_ON_SOCKET_OPEN, 3, "on_socket_open") \ + X(SERVER_EVENT_ON_SOCKET_CLOSE, 4, "on_socket_close") \ + X(SERVER_EVENT_ON_PACKET_RECEIVE, 5, "on_packet_receive") \ + X(SERVER_EVENT_ON_PACKET_RECEIVE_UNKNOWN, 6, "on_packet_receive_unk") \ + X(SERVER_EVENT_ON_PACKET_SEND, 7, "on_packet_send") \ + /* World */ \ + X(WORLD_EVENT_ON_OPEN_STATE_CHANGE, 8, "on_open_state_change") \ + X(WORLD_EVENT_ON_CONFIG_LOAD, 9, "on_config_load") \ + /* 10 unused */ \ + X(WORLD_EVENT_ON_SHUTDOWN_INIT, 11, "on_shutdown_init") \ + X(WORLD_EVENT_ON_SHUTDOWN_CANCEL, 12, "on_shutdown_cancel") \ + X(WORLD_EVENT_ON_UPDATE, 13, "on_world_update") \ + X(WORLD_EVENT_ON_STARTUP, 14, "on_world_startup") \ + X(WORLD_EVENT_ON_SHUTDOWN, 15, "on_world_shutdown") \ + /* Eluna */ \ + X(ELUNA_EVENT_ON_LUA_STATE_CLOSE, 16, "on_lua_state_close") \ + /* Map */ \ + X(MAP_EVENT_ON_CREATE, 17, "on_map_create") \ + X(MAP_EVENT_ON_DESTROY, 18, "on_map_destroy") \ + X(MAP_EVENT_ON_GRID_LOAD, 19, "on_map_grid_load") \ + X(MAP_EVENT_ON_GRID_UNLOAD, 20, "on_map_grid_unload") \ + X(MAP_EVENT_ON_PLAYER_ENTER, 21, "on_map_player_enter") \ + X(MAP_EVENT_ON_PLAYER_LEAVE, 22, "on_map_player_leave") \ + X(MAP_EVENT_ON_UPDATE, 23, "on_map_update") \ + /* Area trigger */ \ + X(TRIGGER_EVENT_ON_TRIGGER, 24, "on_event_trigger") \ + /* Weather */ \ + X(WEATHER_EVENT_ON_CHANGE, 25, "on_weather_change") \ + /* Auction house */ \ + X(AUCTION_EVENT_ON_ADD, 26, "on_auction_add") \ + X(AUCTION_EVENT_ON_REMOVE, 27, "on_auction_remove") \ + X(AUCTION_EVENT_ON_SUCCESSFUL, 28, "on_auction_successful") \ + X(AUCTION_EVENT_ON_EXPIRE, 29, "on_auction_expire") \ + /* AddOns */ \ + X(ADDON_EVENT_ON_MESSAGE, 30, "on_addon_message") \ + X(WORLD_EVENT_ON_DELETE_CREATURE, 31, "on_world_delete_creature") \ + X(WORLD_EVENT_ON_DELETE_GAMEOBJECT, 32, "on_world_delete_gameobject") \ + /* Eluna */ \ + X(ELUNA_EVENT_ON_LUA_STATE_OPEN, 33, "on_lua_state_open") \ + /* Game events */ \ + X(GAME_EVENT_START, 34, "on_game_start") \ + X(GAME_EVENT_STOP, 35, "on_game_stop") + + enum ServerEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + SERVER_EVENTS_LIST(X) + #undef X + SERVER_EVENT_COUNT + }; + + static constexpr EventEntry ServerEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + SERVER_EVENTS_LIST(X) + #undef X + }; + + // PLAYER EVENTS + #define PLAYER_EVENTS_LIST(X) \ + X(PLAYER_EVENT_ON_CHARACTER_CREATE, 1, "on_character_create") \ + X(PLAYER_EVENT_ON_CHARACTER_DELETE, 2, "on_character_delete") \ + X(PLAYER_EVENT_ON_LOGIN, 3, "on_login") \ + X(PLAYER_EVENT_ON_LOGOUT, 4, "on_logout") \ + X(PLAYER_EVENT_ON_SPELL_CAST, 5, "on_spell_cast") \ + X(PLAYER_EVENT_ON_KILL_PLAYER, 6, "on_kill_player") \ + X(PLAYER_EVENT_ON_KILL_CREATURE, 7, "on_kill_creature") \ + X(PLAYER_EVENT_ON_KILLED_BY_CREATURE, 8, "on_killed_by_creature") \ + X(PLAYER_EVENT_ON_DUEL_REQUEST, 9, "on_duel_request") \ + X(PLAYER_EVENT_ON_DUEL_START, 10, "on_duel_start") \ + X(PLAYER_EVENT_ON_DUEL_END, 11, "on_duel_end") \ + X(PLAYER_EVENT_ON_GIVE_XP, 12, "on_give_xp") \ + X(PLAYER_EVENT_ON_LEVEL_CHANGE, 13, "on_level_change") \ + X(PLAYER_EVENT_ON_MONEY_CHANGE, 14, "on_money_change") \ + X(PLAYER_EVENT_ON_REPUTATION_CHANGE, 15, "on_reputation_change") \ + X(PLAYER_EVENT_ON_TALENTS_CHANGE, 16, "on_talents_change") \ + X(PLAYER_EVENT_ON_TALENTS_RESET, 17, "on_talents_reset") \ + X(PLAYER_EVENT_ON_CHAT, 18, "on_chat") \ + X(PLAYER_EVENT_ON_WHISPER, 19, "on_whisper") \ + X(PLAYER_EVENT_ON_GROUP_CHAT, 20, "on_group_chat") \ + X(PLAYER_EVENT_ON_GUILD_CHAT, 21, "on_guild_chat") \ + X(PLAYER_EVENT_ON_CHANNEL_CHAT, 22, "on_channel_chat") \ + X(PLAYER_EVENT_ON_EMOTE, 23, "on_emote") \ + X(PLAYER_EVENT_ON_TEXT_EMOTE, 24, "on_text_emote") \ + X(PLAYER_EVENT_ON_SAVE, 25, "on_save") \ + X(PLAYER_EVENT_ON_BIND_TO_INSTANCE, 26, "on_bind_to_instance") \ + X(PLAYER_EVENT_ON_UPDATE_ZONE, 27, "on_update_zone") \ + X(PLAYER_EVENT_ON_MAP_CHANGE, 28, "on_map_change") \ + X(PLAYER_EVENT_ON_EQUIP, 29, "on_equip") \ + X(PLAYER_EVENT_ON_FIRST_LOGIN, 30, "on_first_login") \ + X(PLAYER_EVENT_ON_CAN_USE_ITEM, 31, "on_can_use_item") \ + X(PLAYER_EVENT_ON_LOOT_ITEM, 32, "on_loot_item") \ + X(PLAYER_EVENT_ON_ENTER_COMBAT, 33, "on_enter_combat") \ + X(PLAYER_EVENT_ON_LEAVE_COMBAT, 34, "on_leave_combat") \ + X(PLAYER_EVENT_ON_REPOP, 35, "on_repop") \ + X(PLAYER_EVENT_ON_RESURRECT, 36, "on_resurrect") \ + X(PLAYER_EVENT_ON_LOOT_MONEY, 37, "on_loot_money") \ + X(PLAYER_EVENT_ON_QUEST_ABANDON, 38, "on_quest_abandon") \ + X(PLAYER_EVENT_ON_LEARN_TALENTS, 39, "on_learn_talents") \ + X(PLAYER_EVENT_ON_ENVIRONMENTAL_DEATH, 40, "on_environmental_death") \ + X(PLAYER_EVENT_ON_TRADE_ACCEPT, 41, "on_trade_accept") \ + X(PLAYER_EVENT_ON_COMMAND, 42, "on_command") \ + X(PLAYER_EVENT_ON_SKILL_CHANGE, 43, "on_skill_change") \ + X(PLAYER_EVENT_ON_LEARN_SPELL, 44, "on_learn_spell") \ + X(PLAYER_EVENT_ON_ACHIEVEMENT_COMPLETE, 45, "on_achievement_complete") \ + X(PLAYER_EVENT_ON_DISCOVER_AREA, 46, "on_discover_area") \ + X(PLAYER_EVENT_ON_UPDATE_AREA, 47, "on_update_area") \ + X(PLAYER_EVENT_ON_TRADE_INIT, 48, "on_trade_init") \ + X(PLAYER_EVENT_ON_SEND_MAIL, 49, "on_send_mail") \ + /* 50–53 unused */ \ + X(PLAYER_EVENT_ON_QUEST_STATUS_CHANGED, 54, "on_quest_status_changed") + + enum PlayerEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + PLAYER_EVENTS_LIST(X) + #undef X + PLAYER_EVENT_COUNT + }; + + static constexpr EventEntry PlayerEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + PLAYER_EVENTS_LIST(X) + #undef X + }; + + // GUILD EVENTS + #define GUILD_EVENTS_LIST(X) \ + X(GUILD_EVENT_ON_ADD_MEMBER, 1, "on_add_member") \ + X(GUILD_EVENT_ON_REMOVE_MEMBER, 2, "on_remove_member") \ + X(GUILD_EVENT_ON_MOTD_CHANGE, 3, "on_motd_change") \ + X(GUILD_EVENT_ON_INFO_CHANGE, 4, "on_info_change") \ + X(GUILD_EVENT_ON_CREATE, 5, "on_create") \ + X(GUILD_EVENT_ON_DISBAND, 6, "on_disband") \ + X(GUILD_EVENT_ON_MONEY_WITHDRAW, 7, "on_money_withdraw") \ + X(GUILD_EVENT_ON_MONEY_DEPOSIT, 8, "on_money_deposit") \ + X(GUILD_EVENT_ON_ITEM_MOVE, 9, "on_item_move") \ + X(GUILD_EVENT_ON_EVENT, 10, "on_event") \ + X(GUILD_EVENT_ON_BANK_EVENT, 11, "on_bank_event") + + enum GuildEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + GUILD_EVENTS_LIST(X) + #undef X + GUILD_EVENT_COUNT + }; + + static constexpr EventEntry GuildEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + GUILD_EVENTS_LIST(X) + #undef X + }; + + // GROUP EVENTS + #define GROUP_EVENTS_LIST(X) \ + X(GROUP_EVENT_ON_MEMBER_ADD, 1, "on_add_member") \ + X(GROUP_EVENT_ON_MEMBER_INVITE, 2, "on_invite_member") \ + X(GROUP_EVENT_ON_MEMBER_REMOVE, 3, "on_remove_member") \ + X(GROUP_EVENT_ON_LEADER_CHANGE, 4, "on_leader_change") \ + X(GROUP_EVENT_ON_DISBAND, 5, "on_disband") \ + X(GROUP_EVENT_ON_CREATE, 6, "on_create") \ + X(GROUP_EVENT_ON_MEMBER_ACCEPT, 7, "on_member_accept") + + enum GroupEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + GROUP_EVENTS_LIST(X) + #undef X + GROUP_EVENT_COUNT + }; + + static constexpr EventEntry GroupEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + GROUP_EVENTS_LIST(X) + #undef X + }; + + // VEHICLE EVENTS + #define VEHICLE_EVENTS_LIST(X) \ + X(VEHICLE_EVENT_ON_INSTALL, 1, "on_install") \ + X(VEHICLE_EVENT_ON_UNINSTALL, 2, "on_uninstall") \ + /* 3 unused */ \ + X(VEHICLE_EVENT_ON_INSTALL_ACCESSORY, 4, "on_install_accessory") \ + X(VEHICLE_EVENT_ON_ADD_PASSENGER, 5, "on_add_passenger") \ + X(VEHICLE_EVENT_ON_REMOVE_PASSENGER, 6, "on_remove_passenger") + + enum VehicleEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + VEHICLE_EVENTS_LIST(X) + #undef X + VEHICLE_EVENT_COUNT + }; + + static constexpr EventEntry VehicleEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + VEHICLE_EVENTS_LIST(X) + #undef X + }; + + // CREATURE EVENTS + #define CREATURE_EVENTS_LIST(X) \ + X(CREATURE_EVENT_ON_ENTER_COMBAT, 1, "on_enter_combat") \ + X(CREATURE_EVENT_ON_LEAVE_COMBAT, 2, "on_leave_combat") \ + X(CREATURE_EVENT_ON_TARGET_DIED, 3, "on_target_died") \ + X(CREATURE_EVENT_ON_DIED, 4, "on_died") \ + X(CREATURE_EVENT_ON_SPAWN, 5, "on_spawn") \ + X(CREATURE_EVENT_ON_REACH_WP, 6, "on_reach_wp") \ + X(CREATURE_EVENT_ON_AIUPDATE, 7, "on_ai_update") \ + X(CREATURE_EVENT_ON_RECEIVE_EMOTE, 8, "on_receive_emote") \ + X(CREATURE_EVENT_ON_DAMAGE_TAKEN, 9, "on_damage_taken") \ + X(CREATURE_EVENT_ON_PRE_COMBAT, 10, "on_pre_combat") \ + /* 11 unused */ \ + X(CREATURE_EVENT_ON_OWNER_ATTACKED, 12, "on_owner_attacked") \ + X(CREATURE_EVENT_ON_OWNER_ATTACKED_AT, 13, "on_owner_attacked_at") \ + X(CREATURE_EVENT_ON_HIT_BY_SPELL, 14, "on_hit_by_spell") \ + X(CREATURE_EVENT_ON_SPELL_HIT_TARGET, 15, "on_spell_hit_target") \ + /* 16,17,18 unused */ \ + X(CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, 19, "on_just_summoned_creature") \ + X(CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, 20, "on_summoned_creature_despawn") \ + X(CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, 21, "on_summoned_creature_died") \ + X(CREATURE_EVENT_ON_SUMMONED, 22, "on_summoned") \ + X(CREATURE_EVENT_ON_RESET, 23, "on_reset") \ + X(CREATURE_EVENT_ON_REACH_HOME, 24, "on_reach_home") \ + /* 25 unused */ \ + X(CREATURE_EVENT_ON_CORPSE_REMOVED, 26, "on_corpse_removed") \ + X(CREATURE_EVENT_ON_MOVE_IN_LOS, 27, "on_move_in_los") \ + /* 28,29 unused */ \ + X(CREATURE_EVENT_ON_DUMMY_EFFECT, 30, "on_dummy_effect") \ + X(CREATURE_EVENT_ON_QUEST_ACCEPT, 31, "on_quest_accept") \ + /* 32,33 unused */ \ + X(CREATURE_EVENT_ON_QUEST_REWARD, 34, "on_quest_reward") \ + X(CREATURE_EVENT_ON_DIALOG_STATUS, 35, "on_dialog_status") \ + X(CREATURE_EVENT_ON_ADD, 36, "on_add") \ + X(CREATURE_EVENT_ON_REMOVE, 37, "on_remove") + + enum CreatureEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + CREATURE_EVENTS_LIST(X) + #undef X + CREATURE_EVENT_COUNT + }; + + static constexpr EventEntry CreatureEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + CREATURE_EVENTS_LIST(X) + #undef X + }; + + // GAMEOBJECT EVENTS + #define GAMEOBJECT_EVENTS_LIST(X) \ + X(GAMEOBJECT_EVENT_ON_AIUPDATE, 1, "on_ai_update") \ + X(GAMEOBJECT_EVENT_ON_SPAWN, 2, "on_spawn") \ + X(GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, 3, "on_dummy_effect") \ + X(GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, 4, "on_quest_accept") \ + X(GAMEOBJECT_EVENT_ON_QUEST_REWARD, 5, "on_quest_reward") \ + X(GAMEOBJECT_EVENT_ON_DIALOG_STATUS, 6, "on_dialog_status") \ + X(GAMEOBJECT_EVENT_ON_DESTROYED, 7, "on_destroyed") \ + X(GAMEOBJECT_EVENT_ON_DAMAGED, 8, "on_damaged") \ + X(GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, 9, "on_loot_state_change") \ + X(GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, 10, "on_go_state_changed") \ + /* 11 unused */ \ + X(GAMEOBJECT_EVENT_ON_ADD, 12, "on_add") \ + X(GAMEOBJECT_EVENT_ON_REMOVE, 13, "on_remove") \ + X(GAMEOBJECT_EVENT_ON_USE, 14, "on_use") + + enum GameObjectEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + GAMEOBJECT_EVENTS_LIST(X) + #undef X + GAMEOBJECT_EVENT_COUNT + }; + + static constexpr EventEntry GameObjectEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + GAMEOBJECT_EVENTS_LIST(X) + #undef X + }; + + // SPELL EVENTS + #define SPELL_EVENTS_LIST(X) \ + X(SPELL_EVENT_ON_CAST, 1, "on_cast") \ + X(SPELL_EVENT_ON_AURA_APPLICATION, 2, "on_aura_application") \ + X(SPELL_EVENT_ON_DISPEL, 3, "on_dispel") \ + X(SPELL_EVENT_ON_PERIODIC_TICK, 4, "on_periodic_tick") \ + X(SPELL_EVENT_ON_PERIODIC_UPDATE, 5, "on_periodic_update") \ + X(SPELL_EVENT_ON_AURA_CALC_AMOUNT, 6, "on_aura_calc_amount") \ + X(SPELL_EVENT_ON_CALC_PERIODIC, 7, "on_calc_periodic") \ + X(SPELL_EVENT_ON_CHECK_PROC, 8, "on_check_proc") \ + X(SPELL_EVENT_ON_PROC, 9, "on_proc") \ + X(SPELL_EVENT_ON_CHECK_CAST, 10, "on_check_cast") \ + X(SPELL_EVENT_ON_BEFORE_CAST, 11, "on_before_cast") \ + X(SPELL_EVENT_ON_AFTER_CAST, 12, "on_after_cast") \ + X(SPELL_EVENT_ON_OBJECT_AREA_TARGET, 13, "on_object_area_target") \ + X(SPELL_EVENT_ON_OBJECT_TARGET, 14, "on_object_target") \ + X(SPELL_EVENT_ON_DEST_TARGET, 15, "on_dest_target") \ + X(SPELL_EVENT_ON_EFFECT_LAUNCH, 16, "on_effect_launch") \ + X(SPELL_EVENT_ON_EFFECT_LAUNCH_TARGET, 17, "on_effect_launch_target") \ + X(SPELL_EVENT_ON_EFFECT_CALC_ABSORB, 18, "on_effect_calc_absorb") \ + X(SPELL_EVENT_ON_EFFECT_HIT, 19, "on_effect_hit") \ + X(SPELL_EVENT_ON_BEFORE_HIT, 20, "on_before_hit") \ + X(SPELL_EVENT_ON_EFFECT_HIT_TARGET, 21, "on_effect_hit_target") \ + X(SPELL_EVENT_ON_HIT, 22, "on_hit") \ + X(SPELL_EVENT_ON_AFTER_HIT, 23, "on_after_hit") + + enum SpellEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + SPELL_EVENTS_LIST(X) + #undef X + SPELL_EVENT_COUNT + }; + + static constexpr EventEntry SpellEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + SPELL_EVENTS_LIST(X) + #undef X + }; + + // ITEM EVENTS + #define ITEM_EVENTS_LIST(X) \ + X(ITEM_EVENT_ON_DUMMY_EFFECT, 1, "on_dummy_effect") \ + X(ITEM_EVENT_ON_USE, 2, "on_use") \ + X(ITEM_EVENT_ON_QUEST_ACCEPT, 3, "on_quest_accept") \ + X(ITEM_EVENT_ON_EXPIRE, 4, "on_expire") \ + X(ITEM_EVENT_ON_REMOVE, 5, "on_remove") \ + /* Custom */ \ + X(ITEM_EVENT_ON_ADD, 6, "on_add") \ + X(ITEM_EVENT_ON_EQUIP, 7, "on_equip") \ + X(ITEM_EVENT_ON_UNEQUIP, 8, "on_unequip") + + enum ItemEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + ITEM_EVENTS_LIST(X) + #undef X + ITEM_EVENT_COUNT + }; + + static constexpr EventEntry ItemEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + ITEM_EVENTS_LIST(X) + #undef X + }; + + // GOSSIP EVENTS + #define GOSSIP_EVENTS_LIST(X) \ + X(GOSSIP_EVENT_ON_HELLO, 1, "on_hello") \ + X(GOSSIP_EVENT_ON_SELECT, 2, "on_select") + + enum GossipEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + GOSSIP_EVENTS_LIST(X) + #undef X + GOSSIP_EVENT_COUNT + }; + + static constexpr EventEntry GossipEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + GOSSIP_EVENTS_LIST(X) + #undef X + }; + + // BG EVENTS + #define BG_EVENTS_LIST(X) \ + X(BG_EVENT_ON_START, 1, "on_start") \ + X(BG_EVENT_ON_END, 2, "on_end") \ + X(BG_EVENT_ON_CREATE, 3, "on_create") \ + X(BG_EVENT_ON_PRE_DESTROY,4, "on_pre_destroy") + + enum BGEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + BG_EVENTS_LIST(X) + #undef X + BG_EVENT_COUNT + }; + + static constexpr EventEntry BGEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + BG_EVENTS_LIST(X) + #undef X + }; + + // INSTANCE EVENTS + #define INSTANCE_EVENTS_LIST(X) \ + X(INSTANCE_EVENT_ON_INITIALIZE, 1, "on_initialize") \ + X(INSTANCE_EVENT_ON_LOAD, 2, "on_load") \ + X(INSTANCE_EVENT_ON_UPDATE, 3, "on_update") \ + X(INSTANCE_EVENT_ON_PLAYER_ENTER, 4, "on_player_enter") \ + X(INSTANCE_EVENT_ON_CREATURE_CREATE, 5, "on_creature_create") \ + X(INSTANCE_EVENT_ON_GAMEOBJECT_CREATE, 6, "on_gameobject_create") \ + X(INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS, 7, "on_check_encounter_in_progress") + + enum InstanceEvents + { + #define X(ID, VALUE, NAME) ID = VALUE, + INSTANCE_EVENTS_LIST(X) + #undef X + INSTANCE_EVENT_COUNT + }; + + static constexpr EventEntry InstanceEventsTable[] = { + #define X(ID, VALUE, NAME) { Hooks::ID, NAME }, + INSTANCE_EVENTS_LIST(X) + #undef X + }; + + // Per-category global event table + static constexpr HookStorage HookTypeTable[] = + { + { "packet", PacketEventsTable, CountOf(PacketEventsTable) }, + { "server", ServerEventsTable, CountOf(ServerEventsTable) }, + { "player", PlayerEventsTable, CountOf(PlayerEventsTable) }, + { "guild", GuildEventsTable, CountOf(GuildEventsTable) }, + { "group", GroupEventsTable, CountOf(GroupEventsTable) }, + { "vehicle", VehicleEventsTable, CountOf(VehicleEventsTable) }, + { "creature", CreatureEventsTable, CountOf(CreatureEventsTable) }, + { "gameobject", GameObjectEventsTable, CountOf(GameObjectEventsTable) }, + { "spell", SpellEventsTable, CountOf(SpellEventsTable) }, + { "item", ItemEventsTable, CountOf(ItemEventsTable) }, + { "gossip", GossipEventsTable, CountOf(GossipEventsTable) }, + { "bg", BGEventsTable, CountOf(BGEventsTable) }, + { "map", InstanceEventsTable, CountOf(InstanceEventsTable) }, + { "instance", InstanceEventsTable, CountOf(InstanceEventsTable) }, + }; + + static constexpr std::pair getHooks() + { + return { HookTypeTable, CountOf(HookTypeTable) }; + } +}; + +#endif // _HOOKS_H diff --git a/src/server/game/LuaEngine/hooks/InstanceHooks.cpp b/src/server/game/LuaEngine/hooks/InstanceHooks.cpp new file mode 100644 index 0000000000..a592b8565e --- /dev/null +++ b/src/server/game/LuaEngine/hooks/InstanceHooks.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" +#include "ElunaInstanceAI.h" + +using namespace Hooks; + +#define START_HOOK(EVENT, AI) \ + auto MapEventBindings = GetBinding>(REGTYPE_MAP);\ + auto InstanceEventBindings = GetBinding>(REGTYPE_INSTANCE);\ + auto mapKey = EntryKey(EVENT, AI->instance->GetId());\ + auto instanceKey = EntryKey(EVENT, AI->instance->GetInstanceId());\ + if (!MapEventBindings->HasBindingsFor(mapKey) && !InstanceEventBindings->HasBindingsFor(instanceKey))\ + return;\ + PushInstanceData(AI);\ + HookPush(AI->instance) + +#define START_HOOK_WITH_RETVAL(EVENT, AI, RETVAL) \ + auto MapEventBindings = GetBinding>(REGTYPE_MAP);\ + auto InstanceEventBindings = GetBinding>(REGTYPE_INSTANCE);\ + auto mapKey = EntryKey(EVENT, AI->instance->GetId());\ + auto instanceKey = EntryKey(EVENT, AI->instance->GetInstanceId());\ + if (!MapEventBindings->HasBindingsFor(mapKey) && !InstanceEventBindings->HasBindingsFor(instanceKey))\ + return RETVAL;\ + PushInstanceData(AI);\ + HookPush(AI->instance) + +void Eluna::OnInitialize(ElunaInstanceAI* ai) +{ + START_HOOK(INSTANCE_EVENT_ON_INITIALIZE, ai); + CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} + +void Eluna::OnLoad(ElunaInstanceAI* ai) +{ + START_HOOK(INSTANCE_EVENT_ON_LOAD, ai); + CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} + +void Eluna::OnUpdateInstance(ElunaInstanceAI* ai, uint32 diff) +{ + START_HOOK(INSTANCE_EVENT_ON_UPDATE, ai); + HookPush(diff); + CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} + +void Eluna::OnPlayerEnterInstance(ElunaInstanceAI* ai, Player* player) +{ + START_HOOK(INSTANCE_EVENT_ON_PLAYER_ENTER, ai); + HookPush(player); + CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} + +void Eluna::OnCreatureCreate(ElunaInstanceAI* ai, Creature* creature) +{ + START_HOOK(INSTANCE_EVENT_ON_CREATURE_CREATE, ai); + HookPush(creature); + CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} + +void Eluna::OnGameObjectCreate(ElunaInstanceAI* ai, GameObject* gameobject) +{ + START_HOOK(INSTANCE_EVENT_ON_GAMEOBJECT_CREATE, ai); + HookPush(gameobject); + CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} + +bool Eluna::OnCheckEncounterInProgress(ElunaInstanceAI* ai) +{ + START_HOOK_WITH_RETVAL(INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS, ai, false); + return CallAllFunctionsBool(MapEventBindings, InstanceEventBindings, mapKey, instanceKey); +} diff --git a/src/server/game/LuaEngine/hooks/ItemHooks.cpp b/src/server/game/LuaEngine/hooks/ItemHooks.cpp new file mode 100644 index 0000000000..21187e0ecc --- /dev/null +++ b/src/server/game/LuaEngine/hooks/ItemHooks.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT, ENTRY) \ + auto binding = GetBinding>(REGTYPE_ITEM);\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, ENTRY, RETVAL) \ + auto binding = GetBinding>(REGTYPE_ITEM);\ + auto key = EntryKey(EVENT, ENTRY);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +void Eluna::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Item* pTarget) +{ + START_HOOK(ITEM_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry()); + HookPush(pCaster); + HookPush(spellId); + HookPush(effIndex); + HookPush(pTarget); + CallAllFunctions(binding, key); +} + +bool Eluna::OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest) +{ + START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_QUEST_ACCEPT, pItem->GetEntry(), false); + HookPush(pPlayer); + HookPush(pItem); + HookPush(pQuest); + return CallAllFunctionsBool(binding, key); +} + +bool Eluna::OnUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) +{ + ObjectGuid guid = pItem->GET_GUID(); + bool castSpell = true; + + if (!OnItemUse(pPlayer, pItem, targets)) + castSpell = false; + + pItem = pPlayer->GetItemByGuid(guid); + if (pItem) + { + if (!OnItemGossip(pPlayer, pItem, targets)) + castSpell = false; + pItem = pPlayer->GetItemByGuid(guid); + } + + if (pItem && castSpell) + return true; + + // Send equip error that shows no message + // This is a hack fix to stop spell casting visual bug when a spell is not cast on use + pPlayer->SendEquipError(EQUIP_ERR_OK, pItem, nullptr); + return false; +} + +bool Eluna::OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) +{ + START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_USE, pItem->GetEntry(), true); + HookPush(pPlayer); + HookPush(pItem); +#if defined ELUNA_TRINITY || defined ELUNA_AZEROTHCORE + if (GameObject* target = targets.GetGOTarget()) + HookPush(target); + else if (Item* target = targets.GetItemTarget()) + HookPush(target); + else if (Corpse* target = targets.GetCorpseTarget()) + HookPush(target); + else if (Unit* target = targets.GetUnitTarget()) + HookPush(target); + else if (WorldObject* target = targets.GetObjectTarget()) + HookPush(target); + else + HookPush(); +#else + if (GameObject* target = targets.getGOTarget()) + HookPush(target); + else if (Item* target = targets.getItemTarget()) + HookPush(target); + else if (Corpse* target = pPlayer->GetMap()->GetCorpse(targets.getCorpseTargetGuid())) + HookPush(target); + else if (Unit* target = targets.getUnitTarget()) + HookPush(target); + else + HookPush(); +#endif + + return CallAllFunctionsBool(binding, key, true); +} + +bool Eluna::OnExpire(Player* pPlayer, ItemTemplate const* pProto) +{ + START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_EXPIRE, pProto->GetId(), false); + HookPush(pPlayer); + HookPush(pProto->GetId()); + return CallAllFunctionsBool(binding, key); +} + +bool Eluna::OnRemove(Player* pPlayer, Item* pItem) +{ + START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_REMOVE, pItem->GetEntry(), false); + HookPush(pPlayer); + HookPush(pItem); + return CallAllFunctionsBool(binding, key); +} + +void Eluna::OnAdd(Player* pPlayer, Item* pItem) +{ + START_HOOK(ITEM_EVENT_ON_ADD, pItem->GetEntry()); + HookPush(pPlayer); + HookPush(pItem); + CallAllFunctions(binding, key); +} + +void Eluna::OnItemEquip(Player* pPlayer, Item* pItem, uint8 slot) +{ + START_HOOK(ITEM_EVENT_ON_EQUIP, pItem->GetEntry()); + HookPush(pPlayer); + HookPush(pItem); + HookPush(slot); + CallAllFunctions(binding, key); +} + +void Eluna::OnItemUnEquip(Player* pPlayer, Item* pItem, uint8 slot) +{ + START_HOOK(ITEM_EVENT_ON_UNEQUIP, pItem->GetEntry()); + HookPush(pPlayer); + HookPush(pItem); + HookPush(slot); + CallAllFunctions(binding, key); +} diff --git a/src/server/game/LuaEngine/hooks/PacketHooks.cpp b/src/server/game/LuaEngine/hooks/PacketHooks.cpp new file mode 100644 index 0000000000..60c0f8f84f --- /dev/null +++ b/src/server/game/LuaEngine/hooks/PacketHooks.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK_SERVER(EVENT) \ + auto binding = GetBinding>(REGTYPE_SERVER);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_PACKET(EVENT, OPCODE) \ + auto binding = GetBinding>(REGTYPE_PACKET);\ + auto key = EntryKey(EVENT, OPCODE);\ + if (!binding->HasBindingsFor(key))\ + return; + +bool Eluna::OnPacketSend(WorldSession* session, const WorldPacket& packet) +{ + bool result = true; + Player* player = NULL; + if (session) + player = session->GetPlayer(); + OnPacketSendAny(player, packet, result); + OnPacketSendOne(player, packet, result); + return result; +} +void Eluna::OnPacketSendAny(Player* player, const WorldPacket& packet, bool& result) +{ + START_HOOK_SERVER(SERVER_EVENT_ON_PACKET_SEND); + HookPush(&packet); // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + HookPush(player); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 1); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + lua_pop(L, 1); + } + + CleanUpStack(2); +} + +void Eluna::OnPacketSendOne(Player* player, const WorldPacket& packet, bool& result) +{ + START_HOOK_PACKET(PACKET_EVENT_ON_PACKET_SEND, packet.GetOpcode()); + HookPush(&packet); // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + HookPush(player); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 1); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + lua_pop(L, 1); + } + + CleanUpStack(2); +} + +bool Eluna::OnPacketReceive(WorldSession* session, WorldPacket& packet) +{ + bool result = true; + Player* player = NULL; + if (session) + player = session->GetPlayer(); + OnPacketReceiveAny(player, packet, result); + OnPacketReceiveOne(player, packet, result); + return result; +} + +void Eluna::OnPacketReceiveAny(Player* player, WorldPacket& packet, bool& result) +{ + START_HOOK_SERVER(SERVER_EVENT_ON_PACKET_RECEIVE); + HookPush(&packet); // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + HookPush(player); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isuserdata(L, r + 1)) + if (WorldPacket* data = CHECKOBJ(r + 1, false)) + { +#if defined ELUNA_TRINITY || defined ELUNA_VMANGOS + packet = std::move(*data); +#else + packet = *data; +#endif + } + + lua_pop(L, 2); + } + + CleanUpStack(2); +} + +void Eluna::OnPacketReceiveOne(Player* player, WorldPacket& packet, bool& result) +{ + START_HOOK_PACKET(PACKET_EVENT_ON_PACKET_RECEIVE, packet.GetOpcode()); + HookPush(&packet); // pushing pointer to local is fine, a copy of value will be stored, not pointer itself + HookPush(player); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isuserdata(L, r + 1)) + if (WorldPacket* data = CHECKOBJ(r + 1, false)) + { +#if defined ELUNA_TRINITY || defined ELUNA_VMANGOS + packet = std::move(*data); +#else + packet = *data; +#endif + } + + lua_pop(L, 2); + } + + CleanUpStack(2); +} diff --git a/src/server/game/LuaEngine/hooks/PlayerHooks.cpp b/src/server/game/LuaEngine/hooks/PlayerHooks.cpp new file mode 100644 index 0000000000..cb11b76c18 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/PlayerHooks.cpp @@ -0,0 +1,679 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" +#include "ElunaLoader.h" +#include // std::transform +#include // strtol + +using namespace Hooks; + +#define START_HOOK(EVENT) \ + auto binding = GetBinding>(REGTYPE_PLAYER);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \ + auto binding = GetBinding>(REGTYPE_PLAYER);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +void Eluna::OnLearnTalents(Player* pPlayer, uint32 talentId, uint32 talentRank, uint32 spellid) +{ + START_HOOK(PLAYER_EVENT_ON_LEARN_TALENTS); + HookPush(pPlayer); + HookPush(talentId); + HookPush(talentRank); + HookPush(spellid); + CallAllFunctions(binding, key); +} + +void Eluna::OnSkillChange(Player* pPlayer, uint32 skillId, uint32 skillValue) +{ + START_HOOK(PLAYER_EVENT_ON_SKILL_CHANGE); + HookPush(pPlayer); + HookPush(skillId); + HookPush(skillValue); + int valueIndex = lua_gettop(L) - 1; + int n = SetupStack(binding, key, 3); + + while (n > 0) + { + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + skillValue = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(skillValue, valueIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(3); +} + +void Eluna::OnLearnSpell(Player* pPlayer, uint32 spellId) +{ + START_HOOK(PLAYER_EVENT_ON_LEARN_SPELL); + HookPush(pPlayer); + HookPush(spellId); + CallAllFunctions(binding, key); +} + +bool Eluna::OnCommand(Player* player, const char* text) +{ + // If from console, player is NULL + if (sElunaConfig->IsReloadCommandEnabled() && (!player || player->GetSession()->GetSecurity() >= sElunaConfig->GetReloadSecurityLevel())) + { + std::string reload = text; + std::transform(reload.begin(), reload.end(), reload.begin(), ::tolower); + const std::string reload_command = "reload eluna"; + if (reload.find(reload_command) == 0) + { + int mapId = RELOAD_ALL_STATES; + std::string args = reload.substr(reload_command.length()); + if (!args.empty()) + mapId = strtol(args.c_str(), nullptr, 10); + + sElunaLoader->ReloadElunaForMap(mapId); + + return false; + } + } + + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_COMMAND, true); + HookPush(player); + HookPush(text); + return CallAllFunctionsBool(binding, key, true); +} + +void Eluna::OnLootItem(Player* pPlayer, Item* pItem, uint32 count, ObjectGuid guid) +{ + START_HOOK(PLAYER_EVENT_ON_LOOT_ITEM); + HookPush(pPlayer); + HookPush(pItem); + HookPush(count); + HookPush(guid); + CallAllFunctions(binding, key); +} + +void Eluna::OnLootMoney(Player* pPlayer, uint32 amount) +{ + START_HOOK(PLAYER_EVENT_ON_LOOT_MONEY); + HookPush(pPlayer); + HookPush(amount); + CallAllFunctions(binding, key); +} + +void Eluna::OnFirstLogin(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_FIRST_LOGIN); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnRepop(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_REPOP); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnResurrect(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_RESURRECT); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnQuestAbandon(Player* pPlayer, uint32 questId) +{ + START_HOOK(PLAYER_EVENT_ON_QUEST_ABANDON); + HookPush(pPlayer); + HookPush(questId); + CallAllFunctions(binding, key); +} + +void Eluna::OnQuestStatusChanged(Player* pPlayer, uint32 questId, uint8 status) +{ + START_HOOK(PLAYER_EVENT_ON_QUEST_STATUS_CHANGED); + HookPush(pPlayer); + HookPush(questId); + HookPush(status); + CallAllFunctions(binding, key); +} + +void Eluna::OnEquip(Player* pPlayer, Item* pItem, uint8 bag, uint8 slot) +{ + START_HOOK(PLAYER_EVENT_ON_EQUIP); + HookPush(pPlayer); + HookPush(pItem); + HookPush(bag); + HookPush(slot); + CallAllFunctions(binding, key); +} + +InventoryResult Eluna::OnCanUseItem(const Player* pPlayer, uint32 itemEntry) +{ + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_USE_ITEM, EQUIP_ERR_OK); + InventoryResult result = EQUIP_ERR_OK; + HookPush(pPlayer); + HookPush(itemEntry); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 1); + + if (lua_isnumber(L, r)) + result = (InventoryResult)CHECKVAL(r); + + lua_pop(L, 1); + } + + CleanUpStack(2); + return result; +} +void Eluna::OnPlayerEnterCombat(Player* pPlayer, Unit* pEnemy) +{ + START_HOOK(PLAYER_EVENT_ON_ENTER_COMBAT); + HookPush(pPlayer); + HookPush(pEnemy); + CallAllFunctions(binding, key); +} + +void Eluna::OnPlayerLeaveCombat(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_LEAVE_COMBAT); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnPVPKill(Player* pKiller, Player* pKilled) +{ + START_HOOK(PLAYER_EVENT_ON_KILL_PLAYER); + HookPush(pKiller); + HookPush(pKilled); + CallAllFunctions(binding, key); +} + +void Eluna::OnCreatureKill(Player* pKiller, Creature* pKilled) +{ + START_HOOK(PLAYER_EVENT_ON_KILL_CREATURE); + HookPush(pKiller); + HookPush(pKilled); + CallAllFunctions(binding, key); +} + +void Eluna::OnPlayerKilledByCreature(Creature* pKiller, Player* pKilled) +{ + START_HOOK(PLAYER_EVENT_ON_KILLED_BY_CREATURE); + HookPush(pKiller); + HookPush(pKilled); + CallAllFunctions(binding, key); +} + +void Eluna::OnPlayerKilledByEnvironment(Player* pKilled, uint8 damageType) +{ + START_HOOK(PLAYER_EVENT_ON_ENVIRONMENTAL_DEATH); + HookPush(pKilled); + HookPush(damageType); + CallAllFunctions(binding, key); +} + +void Eluna::OnLevelChanged(Player* pPlayer, uint8 oldLevel) +{ + START_HOOK(PLAYER_EVENT_ON_LEVEL_CHANGE); + HookPush(pPlayer); + HookPush(oldLevel); + CallAllFunctions(binding, key); +} + +void Eluna::OnFreeTalentPointsChanged(Player* pPlayer, uint32 newPoints) +{ + START_HOOK(PLAYER_EVENT_ON_TALENTS_CHANGE); + HookPush(pPlayer); + HookPush(newPoints); + CallAllFunctions(binding, key); +} + +void Eluna::OnTalentsReset(Player* pPlayer, bool noCost) +{ + START_HOOK(PLAYER_EVENT_ON_TALENTS_RESET); + HookPush(pPlayer); + HookPush(noCost); + CallAllFunctions(binding, key); +} + +void Eluna::OnMoneyChanged(Player* pPlayer, int32& amount) +{ + START_HOOK(PLAYER_EVENT_ON_MONEY_CHANGE); + HookPush(pPlayer); + HookPush(amount); + int amountIndex = lua_gettop(L); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(2); +} + +#if ELUNA_EXPANSION >= EXP_CATA +void Eluna::OnMoneyChanged(Player* pPlayer, int64& amount) +{ + START_HOOK(PLAYER_EVENT_ON_MONEY_CHANGE); + HookPush(pPlayer); + HookPush(amount); + int amountIndex = lua_gettop(L); + int n = SetupStack(binding, key, 2); + + while (n > 0) + { + int r = CallOneFunction(n--, 2, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(2); +} +#endif + +void Eluna::OnGiveXP(Player* pPlayer, uint32& amount, Unit* pVictim) +{ + START_HOOK(PLAYER_EVENT_ON_GIVE_XP); + HookPush(pPlayer); + HookPush(amount); + HookPush(pVictim); + int amountIndex = lua_gettop(L) - 1; + int n = SetupStack(binding, key, 3); + + while (n > 0) + { + int r = CallOneFunction(n--, 3, 1); + + if (lua_isnumber(L, r)) + { + amount = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(amount, amountIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(3); +} + +void Eluna::OnReputationChange(Player* pPlayer, uint32 factionID, int32& standing, bool incremental) +{ + START_HOOK(PLAYER_EVENT_ON_REPUTATION_CHANGE); + HookPush(pPlayer); + HookPush(factionID); + HookPush(standing); + HookPush(incremental); + int standingIndex = lua_gettop(L) - 1; + int n = SetupStack(binding, key, 4); + + while (n > 0) + { + int r = CallOneFunction(n--, 4, 1); + + if (lua_isnumber(L, r)) + { + standing = CHECKVAL(r); + // Update the stack for subsequent calls. + ReplaceArgument(standing, standingIndex); + } + + lua_pop(L, 1); + } + + CleanUpStack(4); +} + +void Eluna::OnDuelRequest(Player* pTarget, Player* pChallenger) +{ + START_HOOK(PLAYER_EVENT_ON_DUEL_REQUEST); + HookPush(pTarget); + HookPush(pChallenger); + CallAllFunctions(binding, key); +} + +void Eluna::OnDuelStart(Player* pStarter, Player* pChallenger) +{ + START_HOOK(PLAYER_EVENT_ON_DUEL_START); + HookPush(pStarter); + HookPush(pChallenger); + CallAllFunctions(binding, key); +} + +void Eluna::OnDuelEnd(Player* pWinner, Player* pLoser, DuelCompleteType type) +{ + START_HOOK(PLAYER_EVENT_ON_DUEL_END); + HookPush(pWinner); + HookPush(pLoser); + HookPush(type); + CallAllFunctions(binding, key); +} + +void Eluna::OnEmote(Player* pPlayer, uint32 emote) +{ + START_HOOK(PLAYER_EVENT_ON_EMOTE); + HookPush(pPlayer); + HookPush(emote); + CallAllFunctions(binding, key); +} + +void Eluna::OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, ObjectGuid guid) +{ + START_HOOK(PLAYER_EVENT_ON_TEXT_EMOTE); + HookPush(pPlayer); + HookPush(textEmote); + HookPush(emoteNum); + HookPush(guid); + CallAllFunctions(binding, key); +} + +void Eluna::OnSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck) +{ + START_HOOK(PLAYER_EVENT_ON_SPELL_CAST); + HookPush(pPlayer); + HookPush(pSpell); + HookPush(skipCheck); + CallAllFunctions(binding, key); +} + +void Eluna::OnLogin(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_LOGIN); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnLogout(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_LOGOUT); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnCreate(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_CHARACTER_CREATE); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnDelete(uint32 guidlow) +{ + START_HOOK(PLAYER_EVENT_ON_CHARACTER_DELETE); + HookPush(guidlow); + CallAllFunctions(binding, key); +} + +void Eluna::OnSave(Player* pPlayer) +{ + START_HOOK(PLAYER_EVENT_ON_SAVE); + HookPush(pPlayer); + CallAllFunctions(binding, key); +} + +void Eluna::OnBindToInstance(Player* pPlayer, Difficulty difficulty, uint32 mapid, bool permanent) +{ + START_HOOK(PLAYER_EVENT_ON_BIND_TO_INSTANCE); + HookPush(pPlayer); + HookPush(difficulty); + HookPush(mapid); + HookPush(permanent); + CallAllFunctions(binding, key); +} + +void Eluna::OnUpdateZone(Player* pPlayer, uint32 newZone, uint32 newArea) +{ + START_HOOK(PLAYER_EVENT_ON_UPDATE_ZONE); + HookPush(pPlayer); + HookPush(newZone); + HookPush(newArea); + CallAllFunctions(binding, key); +} + +void Eluna::OnUpdateArea(Player* pPlayer, uint32 oldArea, uint32 newArea) +{ + START_HOOK(PLAYER_EVENT_ON_UPDATE_AREA); + HookPush(pPlayer); + HookPush(oldArea); + HookPush(newArea); + CallAllFunctions(binding, key); +} + +void Eluna::OnMapChanged(Player* player) +{ + START_HOOK(PLAYER_EVENT_ON_MAP_CHANGE); + HookPush(player); + CallAllFunctions(binding, key); +} + +void Eluna::OnAchievementComplete(Player* player, uint32 achievementId) +{ + START_HOOK(PLAYER_EVENT_ON_ACHIEVEMENT_COMPLETE); + HookPush(player); + HookPush(achievementId); + CallAllFunctions(binding, key); +} + +bool Eluna::OnTradeInit(Player* trader, Player* tradee) +{ + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_TRADE_INIT, true); + HookPush(trader); + HookPush(tradee); + return CallAllFunctionsBool(binding, key, true); +} + +bool Eluna::OnTradeAccept(Player* trader, Player* tradee) +{ + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_TRADE_ACCEPT, true); + HookPush(trader); + HookPush(tradee); + return CallAllFunctionsBool(binding, key, true); +} + +bool Eluna::OnSendMail(Player* sender, ObjectGuid recipientGuid) +{ + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_SEND_MAIL, true); + HookPush(sender); + HookPush(recipientGuid); + return CallAllFunctionsBool(binding, key, true); +} + +void Eluna::OnDiscoverArea(Player* player, uint32 area) +{ + START_HOOK(PLAYER_EVENT_ON_DISCOVER_AREA); + HookPush(player); + HookPush(area); + CallAllFunctions(binding, key); +} + +bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg) +{ + if (lang == LANG_ADDON) + return OnAddonMessage(pPlayer, type, msg, NULL, NULL, NULL, NULL); + + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CHAT, true); + bool result = true; + HookPush(pPlayer); + HookPush(msg); + HookPush(type); + HookPush(lang); + int n = SetupStack(binding, key, 4); + + while (n > 0) + { + int r = CallOneFunction(n--, 4, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); + } + + CleanUpStack(4); + return result; +} + +bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Group* pGroup) +{ + if (lang == LANG_ADDON) + return OnAddonMessage(pPlayer, type, msg, NULL, NULL, pGroup, NULL); + + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_GROUP_CHAT, true); + bool result = true; + HookPush(pPlayer); + HookPush(msg); + HookPush(type); + HookPush(lang); + HookPush(pGroup); + int n = SetupStack(binding, key, 5); + + while (n > 0) + { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); + } + + CleanUpStack(5); + return result; +} + +bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Guild* pGuild) +{ + if (lang == LANG_ADDON) + return OnAddonMessage(pPlayer, type, msg, NULL, pGuild, NULL, NULL); + + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_GUILD_CHAT, true); + bool result = true; + HookPush(pPlayer); + HookPush(msg); + HookPush(type); + HookPush(lang); + HookPush(pGuild); + int n = SetupStack(binding, key, 5); + + while (n > 0) + { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); + } + + CleanUpStack(5); + return result; +} + +bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Channel* pChannel) +{ + if (lang == LANG_ADDON) + return OnAddonMessage(pPlayer, type, msg, NULL, NULL, NULL, pChannel); + + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CHANNEL_CHAT, true); + bool result = true; + HookPush(pPlayer); + HookPush(msg); + HookPush(type); + HookPush(lang); + HookPush(pChannel->GetChannelId()); + int n = SetupStack(binding, key, 5); + + while (n > 0) + { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); + } + + CleanUpStack(5); + return result; +} + +bool Eluna::OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Player* pReceiver) +{ + if (lang == LANG_ADDON) + return OnAddonMessage(pPlayer, type, msg, pReceiver, NULL, NULL, NULL); + + START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_WHISPER, true); + bool result = true; + HookPush(pPlayer); + HookPush(msg); + HookPush(type); + HookPush(lang); + HookPush(pReceiver); + int n = SetupStack(binding, key, 5); + + while (n > 0) + { + int r = CallOneFunction(n--, 5, 2); + + if (lua_isboolean(L, r + 0) && !lua_toboolean(L, r + 0)) + result = false; + + if (lua_isstring(L, r + 1)) + msg = std::string(lua_tostring(L, r + 1)); + + lua_pop(L, 2); + } + + CleanUpStack(5); + return result; +} diff --git a/src/server/game/LuaEngine/hooks/ServerHooks.cpp b/src/server/game/LuaEngine/hooks/ServerHooks.cpp new file mode 100644 index 0000000000..04180c2d52 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/ServerHooks.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaEventMgr.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT) \ + auto binding = GetBinding>(REGTYPE_SERVER);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \ + auto binding = GetBinding>(REGTYPE_SERVER);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +bool Eluna::OnAddonMessage(Player* sender, uint32 type, std::string& msg, Player* receiver, Guild* guild, Group* group, Channel* channel) +{ + START_HOOK_WITH_RETVAL(ADDON_EVENT_ON_MESSAGE, true); + HookPush(sender); + HookPush(type); + + auto delimeter_position = msg.find('\t'); + if (delimeter_position == std::string::npos) + { + HookPush(msg); // prefix + HookPush(); // msg + } + else + { + std::string prefix = msg.substr(0, delimeter_position); + std::string content = msg.substr(delimeter_position + 1, std::string::npos); + HookPush(prefix); + HookPush(content); + } + + if (receiver) + HookPush(receiver); + else if (guild) + HookPush(guild); + else if (group) + HookPush(group); + else if (channel) + HookPush(channel->GetChannelId()); + else + HookPush(); + + return CallAllFunctionsBool(binding, key, true); +} + +void Eluna::OnTimedEvent(int funcRef, uint32 delay, uint32 calls, WorldObject* obj) +{ + ASSERT(!event_level); + + // Get function + lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); + + // Push parameters + Push(funcRef); + Push(delay); + Push(calls); + Push(obj); + + // Call function + ExecuteCall(4, 0); + + ASSERT(!event_level); +#if !defined TRACKABLE_PTR_NAMESPACE + InvalidateObjects(); +#endif +} + +void Eluna::OnGameEventStart(uint32 eventid) +{ + START_HOOK(GAME_EVENT_START); + HookPush(eventid); + CallAllFunctions(binding, key); +} + +void Eluna::OnGameEventStop(uint32 eventid) +{ + START_HOOK(GAME_EVENT_STOP); + HookPush(eventid); + CallAllFunctions(binding, key); +} + +void Eluna::OnLuaStateClose() +{ + START_HOOK(ELUNA_EVENT_ON_LUA_STATE_CLOSE); + CallAllFunctions(binding, key); +} + +void Eluna::OnLuaStateOpen() +{ + START_HOOK(ELUNA_EVENT_ON_LUA_STATE_OPEN); + CallAllFunctions(binding, key); +} + +// AreaTrigger +bool Eluna::OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* pTrigger) +{ + START_HOOK_WITH_RETVAL(TRIGGER_EVENT_ON_TRIGGER, false); + HookPush(pPlayer); +#if defined ELUNA_TRINITY + HookPush(pTrigger->ID); +#elif defined ELUNA_AZEROTHCORE + HookPush(pTrigger->entry); +#else + HookPush(pTrigger->id); +#endif + + return CallAllFunctionsBool(binding, key); +} + +// Weather +void Eluna::OnChange(Weather* /*weather*/, uint32 zone, WeatherState state, float grade) +{ + START_HOOK(WEATHER_EVENT_ON_CHANGE); + HookPush(zone); + HookPush(state); + HookPush(grade); + CallAllFunctions(binding, key); +} + +// Auction House +void Eluna::OnAdd(AuctionHouseObject* /*ah*/, AuctionEntry* entry) +{ +#if defined ELUNA_TRINITY + Player* owner = nullptr; + Item* item = eAuctionMgr->GetAItem(entry->itemGUIDLow); + uint32 expiretime = entry->expire_time; +#elif defined ELUNA_AZEROTHCORE + Player* owner = eObjectAccessor()FindPlayer(entry->owner); + Item* item = eAuctionMgr->GetAItem(entry->item_guid); + uint32 expiretime = entry->expire_time; +#else + Player* owner = eObjectAccessor()FindPlayer(MAKE_NEW_GUID(entry->owner, 0, HIGHGUID_PLAYER)); + Item* item = eAuctionMgr->GetAItem(entry->itemGuidLow); + uint32 expiretime = entry->expireTime; +#endif + + if (!owner || !item) + return; + + START_HOOK(AUCTION_EVENT_ON_ADD); + HookPush(entry->Id); + HookPush(owner); + HookPush(item); + HookPush(expiretime); + HookPush(entry->buyout); + HookPush(entry->startbid); + HookPush(entry->bid); + HookPush(entry->bidder); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemove(AuctionHouseObject* /*ah*/, AuctionEntry* entry) +{ +#if defined ELUNA_TRINITY + Player* owner = nullptr; + Item* item = eAuctionMgr->GetAItem(entry->itemGUIDLow); + uint32 expiretime = entry->expire_time; +#elif defined ELUNA_AZEROTHCORE + Player* owner = eObjectAccessor()FindPlayer(entry->owner); + Item* item = eAuctionMgr->GetAItem(entry->item_guid); + uint32 expiretime = entry->expire_time; +#else + Player* owner = eObjectAccessor()FindPlayer(MAKE_NEW_GUID(entry->owner, 0, HIGHGUID_PLAYER)); + Item* item = eAuctionMgr->GetAItem(entry->itemGuidLow); + uint32 expiretime = entry->expireTime; +#endif + + + if (!owner || !item) + return; + + START_HOOK(AUCTION_EVENT_ON_REMOVE); + HookPush(entry->Id); + HookPush(owner); + HookPush(item); + HookPush(expiretime); + HookPush(entry->buyout); + HookPush(entry->startbid); + HookPush(entry->bid); + HookPush(entry->bidder); + CallAllFunctions(binding, key); +} + +void Eluna::OnSuccessful(AuctionHouseObject* /*ah*/, AuctionEntry* entry) +{ +#if defined ELUNA_TRINITY + Player* owner = nullptr; + Item* item = eAuctionMgr->GetAItem(entry->itemGUIDLow); + uint32 expiretime = entry->expire_time; +#elif defined ELUNA_AZEROTHCORE + Player* owner = eObjectAccessor()FindPlayer(entry->owner); + Item* item = eAuctionMgr->GetAItem(entry->item_guid); + uint32 expiretime = entry->expire_time; +#else + Player* owner = eObjectAccessor()FindPlayer(MAKE_NEW_GUID(entry->owner, 0, HIGHGUID_PLAYER)); + Item* item = eAuctionMgr->GetAItem(entry->itemGuidLow); + uint32 expiretime = entry->expireTime; +#endif + + if (!owner || !item) + return; + + START_HOOK(AUCTION_EVENT_ON_SUCCESSFUL); + HookPush(entry->Id); + HookPush(owner); + HookPush(item); + HookPush(expiretime); + HookPush(entry->buyout); + HookPush(entry->startbid); + HookPush(entry->bid); + HookPush(entry->bidder); + CallAllFunctions(binding, key); +} + +void Eluna::OnExpire(AuctionHouseObject* /*ah*/, AuctionEntry* entry) +{ +#if defined ELUNA_TRINITY + Player* owner = nullptr; + Item* item = eAuctionMgr->GetAItem(entry->itemGUIDLow); + uint32 expiretime = entry->expire_time; +#elif defined ELUNA_AZEROTHCORE + Player* owner = eObjectAccessor()FindPlayer(entry->owner); + Item* item = eAuctionMgr->GetAItem(entry->item_guid); + uint32 expiretime = entry->expire_time; +#else + Player* owner = eObjectAccessor()FindPlayer(MAKE_NEW_GUID(entry->owner, 0, HIGHGUID_PLAYER)); + Item* item = eAuctionMgr->GetAItem(entry->itemGuidLow); + uint32 expiretime = entry->expireTime; +#endif + + if (!owner || !item) + return; + + START_HOOK(AUCTION_EVENT_ON_EXPIRE); + HookPush(entry->Id); + HookPush(owner); + HookPush(item); + HookPush(expiretime); + HookPush(entry->buyout); + HookPush(entry->startbid); + HookPush(entry->bid); + HookPush(entry->bidder); + CallAllFunctions(binding, key); +} + +void Eluna::OnOpenStateChange(bool open) +{ + START_HOOK(WORLD_EVENT_ON_OPEN_STATE_CHANGE); + HookPush(open); + CallAllFunctions(binding, key); +} + +void Eluna::OnConfigLoad(bool reload) +{ + START_HOOK(WORLD_EVENT_ON_CONFIG_LOAD); + HookPush(reload); + CallAllFunctions(binding, key); +} + +void Eluna::OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask) +{ + START_HOOK(WORLD_EVENT_ON_SHUTDOWN_INIT); + HookPush(code); + HookPush(mask); + CallAllFunctions(binding, key); +} + +void Eluna::OnShutdownCancel() +{ + START_HOOK(WORLD_EVENT_ON_SHUTDOWN_CANCEL); + CallAllFunctions(binding, key); +} + +void Eluna::OnWorldUpdate(uint32 diff) +{ + START_HOOK(WORLD_EVENT_ON_UPDATE); + HookPush(diff); + CallAllFunctions(binding, key); +} + +void Eluna::OnStartup() +{ + START_HOOK(WORLD_EVENT_ON_STARTUP); + CallAllFunctions(binding, key); +} + +void Eluna::OnShutdown() +{ + START_HOOK(WORLD_EVENT_ON_SHUTDOWN); + CallAllFunctions(binding, key); +} + +/* Map */ +void Eluna::OnCreate(Map* map) +{ + START_HOOK(MAP_EVENT_ON_CREATE); + HookPush(map); + CallAllFunctions(binding, key); +} + +void Eluna::OnDestroy(Map* map) +{ + START_HOOK(MAP_EVENT_ON_DESTROY); + HookPush(map); + CallAllFunctions(binding, key); +} + +void Eluna::OnPlayerEnter(Map* map, Player* player) +{ + START_HOOK(MAP_EVENT_ON_PLAYER_ENTER); + HookPush(map); + HookPush(player); + CallAllFunctions(binding, key); +} + +void Eluna::OnPlayerLeave(Map* map, Player* player) +{ + START_HOOK(MAP_EVENT_ON_PLAYER_LEAVE); + HookPush(map); + HookPush(player); + CallAllFunctions(binding, key); +} + +void Eluna::OnMapUpdate(Map* map, uint32 diff) +{ + START_HOOK(MAP_EVENT_ON_UPDATE); + HookPush(map); + HookPush(diff); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemove(GameObject* gameobject) +{ + START_HOOK(WORLD_EVENT_ON_DELETE_GAMEOBJECT); + HookPush(gameobject); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemove(Creature* creature) +{ + START_HOOK(WORLD_EVENT_ON_DELETE_CREATURE); + HookPush(creature); + CallAllFunctions(binding, key); +} diff --git a/src/server/game/LuaEngine/hooks/SpellHooks.cpp b/src/server/game/LuaEngine/hooks/SpellHooks.cpp new file mode 100644 index 0000000000..08399067c8 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/SpellHooks.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" + +using namespace Hooks; + +#define START_HOOK(EVENT, SPELL) \ + auto binding = GetBinding>(REGTYPE_SPELL);\ + auto key = EntryKey(EVENT, SPELL->GetSpellInfo()->Id);\ + if (!binding->HasBindingsFor(key))\ + return; + +#define START_HOOK_WITH_RETVAL(EVENT, SPELL, RETVAL) \ + auto binding = GetBinding>(REGTYPE_SPELL);\ + auto key = EntryKey(EVENT, SPELL->GetSpellInfo()->Id);\ + if (!binding->HasBindingsFor(key))\ + return RETVAL; + +void Eluna::OnSpellCast(Spell* pSpell, bool skipCheck) +{ + START_HOOK(SPELL_EVENT_ON_CAST, pSpell); + HookPush(pSpell); + HookPush(skipCheck); + CallAllFunctions(binding, key); +} + +bool Eluna::OnAuraApplication(Aura* aura, AuraEffect const* auraEff, Unit* target, uint8 mode, bool apply) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_AURA_APPLICATION, aura, false); + HookPush(aura); + HookPush(auraEff); + HookPush(target); + HookPush(mode); + HookPush(apply); + return CallAllFunctionsBool(binding, key, false); +} + +void Eluna::OnAuraDispel(Aura* aura, DispelInfo* dispelInfo) +{ + START_HOOK(SPELL_EVENT_ON_DISPEL, aura); + HookPush(aura); + HookPush(dispelInfo->GetDispeller()); + HookPush(dispelInfo->GetDispellerSpellId()); + HookPush(dispelInfo->GetRemovedCharges()); + CallAllFunctions(binding, key); +} + +bool Eluna::OnPeriodicTick(Aura* aura, AuraEffect const* auraEff, Unit* target) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_PERIODIC_TICK, aura, false); + HookPush(aura); + HookPush(auraEff); + HookPush(target); + return CallAllFunctionsBool(binding, key, false); +} + +void Eluna::OnPeriodicUpdate(Aura* aura, AuraEffect const* auraEff) +{ + START_HOOK(SPELL_EVENT_ON_PERIODIC_UPDATE, aura); + HookPush(aura); + HookPush(auraEff); + CallAllFunctions(binding, key); +} + +void Eluna::OnAuraCalcAmount(Aura* aura, AuraEffect const* auraEff, int32& amount, bool& canBeRecalculated) +{ + START_HOOK(SPELL_EVENT_ON_AURA_CALC_AMOUNT, aura); + + HookPush(aura); + HookPush(auraEff); + + HookPush(amount); + int amountIndex = lua_gettop(L); + + HookPush(canBeRecalculated); + int canBeRecalculatedIndex = lua_gettop(L); + + CallAllFunctionsMultiReturn( + binding, + key, + std::tie(amount, canBeRecalculated), + std::array{ amountIndex, canBeRecalculatedIndex } + ); +} + +void Eluna::OnCalcPeriodic(Aura* aura, AuraEffect const* auraEff, bool& isPeriodic, int32& amplitude) +{ + START_HOOK(SPELL_EVENT_ON_CALC_PERIODIC, aura); + + HookPush(aura); + HookPush(auraEff); + + HookPush(isPeriodic); + int isPeriodicIndex = lua_gettop(L); + + HookPush(amplitude); + int amplitudeIndex = lua_gettop(L); + + CallAllFunctionsMultiReturn( + binding, + key, + std::tie(isPeriodic, amplitude), + std::array{ isPeriodicIndex, amplitudeIndex } + ); +} + +bool Eluna::OnAuraCanProc(Aura* aura, ProcEventInfo& procInfo) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_CHECK_PROC, aura, true); + ElunaProcInfo luaProcInfo(procInfo, aura->GetCaster()->GetMap()); + HookPush(aura); + HookPush(&luaProcInfo); + bool retVal = CallAllFunctionsBool(binding, key, true); + luaProcInfo.ApplyToProcEventInfo(procInfo); + return retVal; +} + +bool Eluna::OnAuraProc(Aura* aura, ProcEventInfo& procInfo) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_CHECK_PROC, aura, false); + ElunaProcInfo luaProcInfo(procInfo, aura->GetCaster()->GetMap()); + HookPush(aura); + HookPush(&luaProcInfo); + bool defaultPrevented = CallAllFunctionsBool(binding, key, false); + luaProcInfo.ApplyToProcEventInfo(procInfo); + return defaultPrevented; +} + +uint32 Eluna::OnCheckCast(Spell* pSpell) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_CHECK_CAST, pSpell, SPELL_CAST_OK); + HookPush(pSpell); + return static_cast(CallAllFunctionsInt(binding, key, int32(SPELL_CAST_OK))); +} + +void Eluna::OnBeforeCast(Spell* pSpell) +{ + START_HOOK(SPELL_EVENT_ON_BEFORE_CAST, pSpell); + HookPush(pSpell); + CallAllFunctions(binding, key); +} + +void Eluna::OnAfterCast(Spell* pSpell) +{ + START_HOOK(SPELL_EVENT_ON_AFTER_CAST, pSpell); + HookPush(pSpell); + CallAllFunctions(binding, key); +} + +void Eluna::OnObjectAreaTargetSelect(Spell* pSpell, uint8 effIndex, std::list& targets) +{ + START_HOOK(SPELL_EVENT_ON_OBJECT_AREA_TARGET, pSpell); + HookPush(pSpell); + HookPush(effIndex); + CallAllFunctionsTable(binding, key, targets); +} + +void Eluna::OnObjectTargetSelect(Spell* pSpell, uint8 effIndex, WorldObject*& target) +{ + START_HOOK(SPELL_EVENT_ON_OBJECT_TARGET, pSpell); + HookPush(pSpell); + HookPush(effIndex); + HookPush(target); + CallAllFunctions(binding, key); +} + +void Eluna::OnDestinationTargetSelect(Spell* pSpell, uint8 effIndex, SpellDestination& target) +{ + START_HOOK(SPELL_EVENT_ON_DEST_TARGET, pSpell); + HookPush(pSpell); + HookPush(effIndex); + HookPush(target._position.m_mapId); + int mapIdIndex = lua_gettop(L); + HookPush(target._position.m_positionX); + int posXIndex = lua_gettop(L); + HookPush(target._position.m_positionY); + int posYIndex = lua_gettop(L); + HookPush(target._position.m_positionZ); + int posZIndex = lua_gettop(L); + HookPush(target._position.GetOrientation()); + int orientationIndex = lua_gettop(L); + + uint32 mapId = target._position.m_mapId; + float posX = target._position.m_positionX; + float posY = target._position.m_positionY; + float posZ = target._position.m_positionZ; + float orientation = target._position.GetOrientation(); + + CallAllFunctionsMultiReturn( + binding, + key, + std::tie(mapId, posX, posY, posZ, orientation), + std::array{ mapIdIndex, posXIndex, posYIndex, posZIndex, orientationIndex } + ); + + target._position.m_mapId = mapId; + target._position.m_positionX = posX; + target._position.m_positionY = posY; + target._position.m_positionZ = posZ; + target._position.SetOrientation(orientation); +} + +bool Eluna::OnEffectLaunch(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_EFFECT_LAUNCH, pSpell, preventDefault); + HookPush(pSpell); + HookPush(effIndex); + HookPush(mode); + return CallAllFunctionsBool(binding, key, preventDefault); +} + +bool Eluna::OnEffectLaunchTarget(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_EFFECT_LAUNCH_TARGET, pSpell, preventDefault); + HookPush(pSpell); + HookPush(effIndex); + HookPush(mode); + preventDefault = CallAllFunctionsBool(binding, key, preventDefault); + return preventDefault; +} + +bool Eluna::OnEffectHit(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_EFFECT_HIT, pSpell, preventDefault); + HookPush(pSpell); + HookPush(effIndex); + HookPush(mode); + preventDefault = CallAllFunctionsBool(binding, key, preventDefault); + return preventDefault; +} + +bool Eluna::OnEffectHitTarget(Spell* pSpell, uint8 effIndex, uint8 mode, bool preventDefault) +{ + START_HOOK_WITH_RETVAL(SPELL_EVENT_ON_EFFECT_HIT_TARGET, pSpell, preventDefault); + HookPush(pSpell); + HookPush(effIndex); + HookPush(mode); + preventDefault = CallAllFunctionsBool(binding, key, preventDefault); + return preventDefault; +} + +void Eluna::OnBeforeSpellHit(Spell* pSpell, uint8 missInfo) +{ + START_HOOK(SPELL_EVENT_ON_BEFORE_HIT, pSpell); + HookPush(pSpell); + HookPush(missInfo); + CallAllFunctions(binding, key); +} + +void Eluna::OnSpellHit(Spell* pSpell) +{ + START_HOOK(SPELL_EVENT_ON_HIT, pSpell); + HookPush(pSpell); + CallAllFunctions(binding, key); +} + +void Eluna::OnAfterSpellHit(Spell* pSpell) +{ + START_HOOK(SPELL_EVENT_ON_AFTER_HIT, pSpell); + HookPush(pSpell); + CallAllFunctions(binding, key); +} + +void Eluna::OnEffectCalcAbsorb(Spell* pSpell, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount) +{ + START_HOOK(SPELL_EVENT_ON_EFFECT_CALC_ABSORB, pSpell); + HookPush(pSpell); + HookPush(damageInfo.GetAttacker()); + HookPush(damageInfo.GetVictim()); + HookPush(damageInfo.GetDamage()); + HookPush(damageInfo.GetAbsorb()); + HookPush(damageInfo.GetResist()); + HookPush(damageInfo.GetBlock()); + HookPush(static_cast(damageInfo.GetSchoolMask())); + HookPush(static_cast(damageInfo.GetDamageType())); + HookPush(static_cast(damageInfo.GetAttackType())); + HookPush(damageInfo.GetHitMask()); + HookPush(resistAmount); + int resistIndex = lua_gettop(L); + HookPush(absorbAmount); + int absorbIndex = lua_gettop(L); + CallAllFunctionsMultiReturn( + binding, + key, + std::tie(resistAmount, absorbAmount), + std::array{ resistIndex, absorbIndex } + ); +} diff --git a/src/server/game/LuaEngine/hooks/VehicleHooks.cpp b/src/server/game/LuaEngine/hooks/VehicleHooks.cpp new file mode 100644 index 0000000000..3bbc6d5024 --- /dev/null +++ b/src/server/game/LuaEngine/hooks/VehicleHooks.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "Hooks.h" +#include "HookHelpers.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "ElunaTemplate.h" + +#if ELUNA_EXPANSION >= EXP_WOTLK + +using namespace Hooks; + +#define START_HOOK(EVENT) \ + auto binding = GetBinding>(REGTYPE_VEHICLE);\ + auto key = EventKey(EVENT);\ + if (!binding->HasBindingsFor(key))\ + return; + +void Eluna::OnInstall(Vehicle* vehicle) +{ + START_HOOK(VEHICLE_EVENT_ON_INSTALL); + HookPush(vehicle); + CallAllFunctions(binding, key); +} + +void Eluna::OnUninstall(Vehicle* vehicle) +{ + START_HOOK(VEHICLE_EVENT_ON_UNINSTALL); + HookPush(vehicle); + CallAllFunctions(binding, key); +} + +void Eluna::OnInstallAccessory(Vehicle* vehicle, Creature* accessory) +{ + START_HOOK(VEHICLE_EVENT_ON_INSTALL_ACCESSORY); + HookPush(vehicle); + HookPush(accessory); + CallAllFunctions(binding, key); +} + +void Eluna::OnAddPassenger(Vehicle* vehicle, Unit* passenger, int8 seatId) +{ + START_HOOK(VEHICLE_EVENT_ON_ADD_PASSENGER); + HookPush(vehicle); + HookPush(passenger); + HookPush(seatId); + CallAllFunctions(binding, key); +} + +void Eluna::OnRemovePassenger(Vehicle* vehicle, Unit* passenger) +{ + START_HOOK(VEHICLE_EVENT_ON_REMOVE_PASSENGER); + HookPush(vehicle); + HookPush(passenger); + CallAllFunctions(binding, key); +} + +#endif diff --git a/src/server/game/LuaEngine/lmarshal.cpp b/src/server/game/LuaEngine/lmarshal.cpp new file mode 100644 index 0000000000..1edbd0ca11 --- /dev/null +++ b/src/server/game/LuaEngine/lmarshal.cpp @@ -0,0 +1,584 @@ +/* + * lmarshal.c + * A Lua library for serializing and deserializing Lua values + * Richard Hundt , Eluna Lua Engine + * + * License: MIT + * + * Copyright (c) 2010 Richard Hundt + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "ElunaCompat.h" + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} + +#define MAR_TREF 1 +#define MAR_TVAL 2 +#define MAR_TUSR 3 + +#define MAR_CHR 1 +#define MAR_I32 4 +#define MAR_I64 8 + +#define MAR_MAGIC 0x8f +#define SEEN_IDX 3 + +#define MAR_ENV_IDX_KEY "E" +#define MAR_NUPS_IDX_KEY "n" + +typedef struct mar_Buffer { + size_t size; + size_t seek; + size_t head; + char* data; +} mar_Buffer; + +static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx); +static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx); + +static void buf_init(lua_State *L, mar_Buffer *buf) +{ + buf->size = 128; + buf->seek = 0; + buf->head = 0; + if (!(buf->data = (char*)malloc(buf->size))) luaL_error(L, "Out of memory!"); +} + +static void buf_done(lua_State* /*L*/, mar_Buffer *buf) +{ + free(buf->data); +} + +static int buf_write(lua_State* L, const char* str, size_t len, mar_Buffer *buf) +{ + if (len > UINT32_MAX) luaL_error(L, "buffer too long"); + if (buf->size - buf->head < len) { + size_t new_size = buf->size << 1; + size_t cur_head = buf->head; + while (new_size - cur_head <= len) { + new_size = new_size << 1; + } + char* data = (char*)realloc(buf->data, new_size); + if (!data) { + return luaL_error(L, "Out of memory!"); + } + buf->data = data; + buf->size = new_size; + } + memcpy(&buf->data[buf->head], str, len); + buf->head += len; + return 0; +} + +static const char* buf_read(lua_State* /*L*/, mar_Buffer *buf, size_t *len) +{ + if (buf->seek < buf->head) { + buf->seek = buf->head; + *len = buf->seek; + return buf->data; + } + *len = 0; + return NULL; +} + +static void mar_encode_value(lua_State *L, mar_Buffer *buf, int val, size_t *idx) +{ + size_t l; + int val_type = lua_type(L, val); + lua_pushvalue(L, val); + + buf_write(L, (const char*)&val_type, MAR_CHR, buf); + switch (val_type) { + case LUA_TBOOLEAN: { + int int_val = lua_toboolean(L, -1); + buf_write(L, (const char*)&int_val, MAR_CHR, buf); + break; + } + case LUA_TSTRING: { + const char *str_val = lua_tolstring(L, -1, &l); + buf_write(L, (const char*)&l, MAR_I32, buf); + buf_write(L, str_val, l, buf); + break; + } + case LUA_TNUMBER: { + lua_Number num_val = lua_tonumber(L, -1); + buf_write(L, (const char*)&num_val, MAR_I64, buf); + break; + } + case LUA_TTABLE: { + int tag, ref; + lua_pushvalue(L, -1); + lua_rawget(L, SEEN_IDX); + if (!lua_isnil(L, -1)) { + ref = lua_tointeger(L, -1); + tag = MAR_TREF; + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&ref, MAR_I32, buf); + lua_pop(L, 1); + } + else { + mar_Buffer rec_buf; + lua_pop(L, 1); /* pop nil */ + if (luaL_getmetafield(L, -1, "__persist")) { + tag = MAR_TUSR; + + lua_pushvalue(L, -2); /* self */ + lua_call(L, 1, 1); + if (!lua_isfunction(L, -1)) { + luaL_error(L, "__persist must return a function"); + } + + lua_remove(L, -2); /* __persist */ + + lua_newtable(L); + lua_pushvalue(L, -2); /* callback */ + lua_rawseti(L, -2, 1); + + buf_init(L, &rec_buf); + mar_encode_table(L, &rec_buf, idx); + + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf); + buf_write(L, rec_buf.data, rec_buf.head, buf); + buf_done(L, &rec_buf); + lua_pop(L, 1); + } + else { + tag = MAR_TVAL; + + lua_pushvalue(L, -1); + lua_pushinteger(L, (*idx)++); + lua_rawset(L, SEEN_IDX); + + lua_pushvalue(L, -1); + buf_init(L, &rec_buf); + mar_encode_table(L, &rec_buf, idx); + lua_pop(L, 1); + + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf); + buf_write(L, rec_buf.data,rec_buf.head, buf); + buf_done(L, &rec_buf); + } + } + break; + } + case LUA_TFUNCTION: { + int tag, ref; + lua_pushvalue(L, -1); + lua_rawget(L, SEEN_IDX); + if (!lua_isnil(L, -1)) { + ref = lua_tointeger(L, -1); + tag = MAR_TREF; + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&ref, MAR_I32, buf); + lua_pop(L, 1); + } + else { + mar_Buffer rec_buf; + lua_Debug ar; + decltype(ar.nups) i; + lua_pop(L, 1); /* pop nil */ + + lua_pushvalue(L, -1); + lua_getinfo(L, ">nuS", &ar); + if (ar.what[0] != 'L') { + luaL_error(L, "attempt to persist a C function '%s'", ar.name); + } + tag = MAR_TVAL; + lua_pushvalue(L, -1); + lua_pushinteger(L, (*idx)++); + lua_rawset(L, SEEN_IDX); + + lua_pushvalue(L, -1); + buf_init(L, &rec_buf); + lua_dump(L, (lua_Writer)buf_write, &rec_buf); + + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf); + buf_write(L, rec_buf.data, rec_buf.head, buf); + buf_done(L, &rec_buf); + lua_pop(L, 1); + + lua_createtable(L, ar.nups, 0); + for (i = 1; i <= ar.nups; i++) { + const char* upvalue_name = lua_getupvalue(L, -2, i); + if (strcmp("_ENV", upvalue_name) == 0) { + lua_pop(L, 1); + // Mark where _ENV is expected. + lua_pushstring(L, MAR_ENV_IDX_KEY); + lua_pushinteger(L, i); + lua_rawset(L, -3); + } + else { + lua_rawseti(L, -2, i); + } + } + lua_pushstring(L, MAR_NUPS_IDX_KEY); + lua_pushnumber(L, ar.nups); + lua_rawset(L, -3); + + buf_init(L, &rec_buf); + mar_encode_table(L, &rec_buf, idx); + + buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf); + buf_write(L, rec_buf.data, rec_buf.head, buf); + buf_done(L, &rec_buf); + lua_pop(L, 1); + } + + break; + } + case LUA_TUSERDATA: { + int tag, ref; + lua_pushvalue(L, -1); + lua_rawget(L, SEEN_IDX); + if (!lua_isnil(L, -1)) { + ref = lua_tointeger(L, -1); + tag = MAR_TREF; + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&ref, MAR_I32, buf); + lua_pop(L, 1); + } + else { + mar_Buffer rec_buf; + lua_pop(L, 1); /* pop nil */ + if (luaL_getmetafield(L, -1, "__persist")) { + tag = MAR_TUSR; + + lua_pushvalue(L, -2); + lua_pushinteger(L, (*idx)++); + lua_rawset(L, SEEN_IDX); + + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + if (!lua_isfunction(L, -1)) { + luaL_error(L, "__persist must return a function"); + } + lua_newtable(L); + lua_pushvalue(L, -2); + lua_rawseti(L, -2, 1); + lua_remove(L, -2); + + buf_init(L, &rec_buf); + mar_encode_table(L, &rec_buf, idx); + + buf_write(L, (const char*)&tag, MAR_CHR, buf); + buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf); + buf_write(L, rec_buf.data, rec_buf.head, buf); + buf_done(L, &rec_buf); + } + else { + luaL_error(L, "attempt to encode userdata (no __persist hook)"); + } + lua_pop(L, 1); + } + break; + } + case LUA_TNIL: break; + default: + luaL_error(L, "invalid value type (%s)", lua_typename(L, val_type)); + } + lua_pop(L, 1); +} + +static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx) +{ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + mar_encode_value(L, buf, -2, idx); + mar_encode_value(L, buf, -1, idx); + lua_pop(L, 1); + } + return 1; +} + +#define mar_incr_ptr(l) \ + if (((*p)-buf)+(ptrdiff_t)(l) > (ptrdiff_t)len) \ + luaL_error(L, "bad code"); \ + (*p) += (l); + +#define mar_next_len(l,T) \ + if (((*p)-buf)+(ptrdiff_t)sizeof(T) > (ptrdiff_t)len) \ + luaL_error(L, "bad code"); \ + l = *(T*)*p; (*p) += sizeof(T); + +static void mar_decode_value(lua_State *L, const char *buf, size_t len, const char **p, size_t *idx) +{ + size_t l; + char val_type = **p; + mar_incr_ptr(MAR_CHR); + switch (val_type) { + case LUA_TBOOLEAN: + lua_pushboolean(L, *(char*)*p); + mar_incr_ptr(MAR_CHR); + break; + case LUA_TNUMBER: + lua_pushnumber(L, *(lua_Number*)*p); + mar_incr_ptr(MAR_I64); + break; + case LUA_TSTRING: + mar_next_len(l, uint32_t); + lua_pushlstring(L, *p, l); + mar_incr_ptr(l); + break; + case LUA_TTABLE: { + char tag = *(char*)*p; + mar_incr_ptr(MAR_CHR); + if (tag == MAR_TREF) { + int ref; + mar_next_len(ref, int); + lua_rawgeti(L, SEEN_IDX, ref); + } + else if (tag == MAR_TVAL) { + mar_next_len(l, uint32_t); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, SEEN_IDX, (*idx)++); + mar_decode_table(L, *p, l, idx); + mar_incr_ptr(l); + } + else if (tag == MAR_TUSR) { + mar_next_len(l, uint32_t); + lua_newtable(L); + mar_decode_table(L, *p, l, idx); + lua_rawgeti(L, -1, 1); + lua_call(L, 0, 1); + lua_remove(L, -2); + lua_pushvalue(L, -1); + lua_rawseti(L, SEEN_IDX, (*idx)++); + mar_incr_ptr(l); + } + else { + luaL_error(L, "bad encoded data"); + } + break; + } + case LUA_TFUNCTION: { + unsigned int nups; + unsigned int i; + mar_Buffer dec_buf; + char tag = *(char*)*p; + mar_incr_ptr(1); + if (tag == MAR_TREF) { + int ref; + mar_next_len(ref, int); + lua_rawgeti(L, SEEN_IDX, ref); + } + else { + mar_next_len(l, uint32_t); + dec_buf.data = (char*)*p; + dec_buf.size = l; + dec_buf.head = l; + dec_buf.seek = 0; + lua_load(L, (lua_Reader)buf_read, &dec_buf, "=marshal", NULL); + mar_incr_ptr(l); + + lua_pushvalue(L, -1); + lua_rawseti(L, SEEN_IDX, (*idx)++); + + mar_next_len(l, uint32_t); + lua_newtable(L); + mar_decode_table(L, *p, l, idx); + + lua_pushstring(L, MAR_ENV_IDX_KEY); + lua_rawget(L, -2); + if (lua_isnumber(L, -1)) { + lua_pushglobaltable(L); + lua_rawset(L, -3); + } + else { + lua_pop(L, 1); + } + + lua_pushstring(L, MAR_NUPS_IDX_KEY); + lua_rawget(L, -2); + nups = luaL_checknumber(L, -1); + lua_pop(L, 1); + + for (i = 1; i <= nups; i++) { + lua_rawgeti(L, -1, i); + lua_setupvalue(L, -3, i); + } + + lua_pop(L, 1); + mar_incr_ptr(l); + } + break; + } + case LUA_TUSERDATA: { + char tag = *(char*)*p; + mar_incr_ptr(MAR_CHR); + if (tag == MAR_TREF) { + int ref; + mar_next_len(ref, int); + lua_rawgeti(L, SEEN_IDX, ref); + } + else if (tag == MAR_TUSR) { + mar_next_len(l, uint32_t); + lua_newtable(L); + mar_decode_table(L, *p, l, idx); + lua_rawgeti(L, -1, 1); + lua_call(L, 0, 1); + lua_remove(L, -2); + lua_pushvalue(L, -1); + lua_rawseti(L, SEEN_IDX, (*idx)++); + mar_incr_ptr(l); + } + else { /* tag == MAR_TVAL */ + lua_pushnil(L); + } + break; + } + case LUA_TNIL: + case LUA_TTHREAD: + lua_pushnil(L); + break; + default: + luaL_error(L, "bad code"); + } +} + +static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx) +{ + const char* p; + p = buf; + while (p - buf < (ptrdiff_t)len) { + mar_decode_value(L, buf, len, &p, idx); + mar_decode_value(L, buf, len, &p, idx); + lua_rawset(L, -3); + } + return 1; +} + +int mar_encode(lua_State* L) +{ + const unsigned char m = MAR_MAGIC; + size_t idx, len; + mar_Buffer buf; + + if (lua_isnone(L, 1)) { + lua_pushnil(L); + } + if (lua_isnoneornil(L, 2)) { + lua_newtable(L); + } + else if (!lua_istable(L, 2)) { + luaL_error(L, "bad argument #2 to encode (expected table)"); + } + lua_settop(L, 2); + + len = lua_rawlen(L, 2); + lua_newtable(L); + for (idx = 1; idx <= len; idx++) { + lua_rawgeti(L, 2, idx); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + continue; + } + lua_pushinteger(L, idx); + lua_rawset(L, SEEN_IDX); + } + lua_pushvalue(L, 1); + + buf_init(L, &buf); + buf_write(L, (const char*)&m, 1, &buf); + + mar_encode_value(L, &buf, -1, &idx); + + lua_pop(L, 1); + + lua_pushlstring(L, buf.data, buf.head); + + buf_done(L, &buf); + + lua_remove(L, SEEN_IDX); + + return 1; +} + +int mar_decode(lua_State* L) +{ + size_t l, idx, len; + const char *p; + const char *s = luaL_checklstring(L, 1, &l); + + if (l < 1) luaL_error(L, "bad header"); + if (*(unsigned char *)s++ != MAR_MAGIC) luaL_error(L, "bad magic"); + l -= 1; + + if (lua_isnoneornil(L, 2)) { + lua_newtable(L); + } + else if (!lua_istable(L, 2)) { + luaL_error(L, "bad argument #2 to decode (expected table)"); + } + lua_settop(L, 2); + + len = lua_rawlen(L, 2); + lua_newtable(L); + for (idx = 1; idx <= len; idx++) { + lua_rawgeti(L, 2, idx); + lua_rawseti(L, SEEN_IDX, idx); + } + + p = s; + mar_decode_value(L, s, l, &p, &idx); + + lua_remove(L, SEEN_IDX); + lua_remove(L, 2); + + return 1; +} + +int mar_clone(lua_State* L) +{ + mar_encode(L); + lua_replace(L, 1); + mar_decode(L); + return 1; +} + +static const luaL_Reg R[] = +{ + {"encode", mar_encode}, + {"decode", mar_decode}, + {"clone", mar_clone}, + {NULL, NULL} +}; + +int luaopen_marshal(lua_State *L) +{ + lua_newtable(L); + luaL_setfuncs(L, R, 0); + return 1; +} + diff --git a/src/server/game/LuaEngine/lmarshal.h b/src/server/game/LuaEngine/lmarshal.h new file mode 100644 index 0000000000..a56558fee2 --- /dev/null +++ b/src/server/game/LuaEngine/lmarshal.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2010 - 2024 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +extern "C" { +#include "lua.h" +} + +int mar_encode(lua_State* L); +int mar_decode(lua_State* L); diff --git a/src/server/game/LuaEngine/lua_src/Makefile b/src/server/game/LuaEngine/lua_src/Makefile new file mode 100644 index 0000000000..e0d4c9fa64 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/Makefile @@ -0,0 +1,182 @@ +# makefile for building Lua +# see ../INSTALL for installation instructions +# see ../Makefile and luaconf.h for further customization + +# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= + +# Your platform. See PLATS for possible values. +PLAT= none + +CC= gcc +CFLAGS= -O2 -Wall $(MYCFLAGS) +AR= ar rcu +RANLIB= ranlib +RM= rm -f +LIBS= -lm $(MYLIBS) + +MYCFLAGS= +MYLDFLAGS= +MYLIBS= + +# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= + +PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris + +LUA_A= liblua.a +CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \ + lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ + lundump.o lvm.o lzio.o +LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ + lstrlib.o loadlib.o linit.o + +LUA_T= lua +LUA_O= lua.o + +LUAC_T= luac +LUAC_O= luac.o print.o + +ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O) +ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) +ALL_A= $(LUA_A) + +default: $(PLAT) + +all: $(ALL_T) + +o: $(ALL_O) + +a: $(ALL_A) + +$(LUA_A): $(CORE_O) $(LIB_O) + $(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files + $(RANLIB) $@ + +$(LUA_T): $(LUA_O) $(LUA_A) + $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) + +$(LUAC_T): $(LUAC_O) $(LUA_A) + $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) + +clean: + $(RM) $(ALL_T) $(ALL_O) + +depend: + @$(CC) $(CFLAGS) -MM l*.c print.c + +echo: + @echo "PLAT = $(PLAT)" + @echo "CC = $(CC)" + @echo "CFLAGS = $(CFLAGS)" + @echo "AR = $(AR)" + @echo "RANLIB = $(RANLIB)" + @echo "RM = $(RM)" + @echo "MYCFLAGS = $(MYCFLAGS)" + @echo "MYLDFLAGS = $(MYLDFLAGS)" + @echo "MYLIBS = $(MYLIBS)" + +# convenience targets for popular platforms + +none: + @echo "Please choose a platform:" + @echo " $(PLATS)" + +aix: + $(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall" + +ansi: + $(MAKE) all MYCFLAGS=-DLUA_ANSI + +bsd: + $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E" + +freebsd: + $(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline" + +generic: + $(MAKE) all MYCFLAGS= + +linux: + $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses" + +macosx: + $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline" +# use this on Mac OS X 10.3- +# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX + +mingw: + $(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \ + "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ + "MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe + $(MAKE) "LUAC_T=luac.exe" luac.exe + +posix: + $(MAKE) all MYCFLAGS=-DLUA_USE_POSIX + +solaris: + $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" + +# list targets that do not create files (but not all makes understand .PHONY) +.PHONY: all $(PLATS) default o a clean depend echo none + +# DO NOT DELETE + +lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \ + lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \ + lundump.h lvm.h +lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h +lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h +lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ + lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \ + ltable.h +ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h +ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \ + llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ + lfunc.h lstring.h lgc.h ltable.h lvm.h +ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \ + ltable.h lundump.h lvm.h +ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \ + lzio.h lmem.h lundump.h +lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \ + lstate.h ltm.h lzio.h +lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h +linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h +liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h +llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \ + lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h +lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h +lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h +loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h +lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \ + ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h +lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h +loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h +lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ + lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ + lfunc.h lstring.h lgc.h ltable.h +lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h +lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \ + ltm.h lzio.h lstring.h lgc.h +lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h +ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h +ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h +ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \ + lmem.h lstring.h lgc.h ltable.h +lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h +luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \ + lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \ + lundump.h +lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h +lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h +lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \ + lzio.h +print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h lopcodes.h lundump.h + +# (end of Makefile) diff --git a/src/server/game/LuaEngine/lua_src/lapi.c b/src/server/game/LuaEngine/lua_src/lapi.c new file mode 100644 index 0000000000..5d5145d2eb --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lapi.c @@ -0,0 +1,1087 @@ +/* +** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $ +** Lua API +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define lapi_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" + + + +const char lua_ident[] = + "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n" + "$Authors: " LUA_AUTHORS " $\n" + "$URL: www.lua.org $\n"; + + + +#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) + +#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject) + +#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} + + + +static TValue *index2adr (lua_State *L, int idx) { + if (idx > 0) { + TValue *o = L->base + (idx - 1); + api_check(L, idx <= L->ci->top - L->base); + if (o >= L->top) return cast(TValue *, luaO_nilobject); + else return o; + } + else if (idx > LUA_REGISTRYINDEX) { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } + else switch (idx) { /* pseudo-indices */ + case LUA_REGISTRYINDEX: return registry(L); + case LUA_ENVIRONINDEX: { + Closure *func = curr_func(L); + sethvalue(L, &L->env, func->c.env); + return &L->env; + } + case LUA_GLOBALSINDEX: return gt(L); + default: { + Closure *func = curr_func(L); + idx = LUA_GLOBALSINDEX - idx; + return (idx <= func->c.nupvalues) + ? &func->c.upvalue[idx-1] + : cast(TValue *, luaO_nilobject); + } + } +} + + +static Table *getcurrenv (lua_State *L) { + if (L->ci == L->base_ci) /* no enclosing function? */ + return hvalue(gt(L)); /* use global table as environment */ + else { + Closure *func = curr_func(L); + return func->c.env; + } +} + + +void luaA_pushobject (lua_State *L, const TValue *o) { + setobj2s(L, L->top, o); + api_incr_top(L); +} + + +LUA_API int lua_checkstack (lua_State *L, int size) { + int res = 1; + lua_lock(L); + if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) + res = 0; /* stack overflow */ + else if (size > 0) { + luaD_checkstack(L, size); + if (L->ci->top < L->top + size) + L->ci->top = L->top + size; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to)); + api_check(from, to->ci->top - to->top >= n); + from->top -= n; + for (i = 0; i < n; i++) { + setobj2s(to, to->top++, from->top + i); + } + lua_unlock(to); +} + + +LUA_API void lua_setlevel (lua_State *from, lua_State *to) { + to->nCcalls = from->nCcalls; +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + L1 = luaE_newthread(L); + setthvalue(L, L->top, L1); + api_incr_top(L); + lua_unlock(L); + luai_userstatethread(L, L1); + return L1; +} + + + +/* +** basic stack manipulation +*/ + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top - L->base); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + lua_lock(L); + if (idx >= 0) { + api_check(L, idx <= L->stack_last - L->base); + while (L->top < L->base + idx) + setnilvalue(L->top++); + L->top = L->base + idx; + } + else { + api_check(L, -(idx+1) <= (L->top - L->base)); + L->top += idx+1; /* `subtract' index (index is negative) */ + } + lua_unlock(L); +} + + +LUA_API void lua_remove (lua_State *L, int idx) { + StkId p; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + while (++p < L->top) setobjs2s(L, p-1, p); + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_insert (lua_State *L, int idx) { + StkId p; + StkId q; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + for (q = L->top; q>p; q--) setobjs2s(L, q, q-1); + setobjs2s(L, p, L->top); + lua_unlock(L); +} + + +LUA_API void lua_replace (lua_State *L, int idx) { + StkId o; + lua_lock(L); + /* explicit test for incompatible code */ + if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci) + luaG_runerror(L, "no calling environment"); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + if (idx == LUA_ENVIRONINDEX) { + Closure *func = curr_func(L); + api_check(L, ttistable(L->top - 1)); + func->c.env = hvalue(L->top - 1); + luaC_barrier(L, func, L->top - 1); + } + else { + setobj(L, o, L->top - 1); + if (idx < LUA_GLOBALSINDEX) /* function upvalue? */ + luaC_barrier(L, curr_func(L), L->top - 1); + } + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top, index2adr(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (o == luaO_nilobject) ? LUA_TNONE : ttype(o); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + return (t == LUA_TNONE) ? "no value" : luaT_typenames[t]; +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return iscfunction(o); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + int t = lua_type(L, idx); + return (t == LUA_TSTRING || t == LUA_TNUMBER); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return (ttisuserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + StkId o1 = index2adr(L, index1); + StkId o2 = index2adr(L, index2); + return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaO_rawequalObj(o1, o2); +} + + +LUA_API int lua_equal (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2); + lua_unlock(L); + return i; +} + + +LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaV_lessthan(L, o1, o2); + lua_unlock(L); + return i; +} + + + +LUA_API lua_Number lua_tonumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) + return nvalue(o); + else + return 0; +} + + +LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) { + lua_Integer res; + lua_Number num = nvalue(o); + lua_number2integer(res, num); + return res; + } + else + return 0; +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + StkId o = index2adr(L, idx); + if (!ttisstring(o)) { + lua_lock(L); /* `luaV_tostring' may create a new string */ + if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaC_checkGC(L); + o = index2adr(L, idx); /* previous call may reallocate the stack */ + lua_unlock(L); + } + if (len != NULL) *len = tsvalue(o)->len; + return svalue(o); +} + + +LUA_API size_t lua_objlen (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TSTRING: return tsvalue(o)->len; + case LUA_TUSERDATA: return uvalue(o)->len; + case LUA_TTABLE: return luaH_getn(hvalue(o)); + case LUA_TNUMBER: { + size_t l; + lua_lock(L); /* `luaV_tostring' may create a new string */ + l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0); + lua_unlock(L); + return l; + } + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!iscfunction(o)) ? NULL : clvalue(o)->c.f; +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TUSERDATA: return (rawuvalue(o) + 1); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +LUA_API const void *lua_topointer (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TTABLE: return hvalue(o); + case LUA_TFUNCTION: return clvalue(o); + case LUA_TTHREAD: return thvalue(o); + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + return lua_touserdata(L, idx); + default: return NULL; + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setnvalue(L->top, n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setnvalue(L->top, cast_num(n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { + lua_lock(L); + luaC_checkGC(L); + setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushstring (lua_State *L, const char *s) { + if (s == NULL) + lua_pushnil(L); + else + lua_pushlstring(L, s, strlen(s)); +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + luaC_checkGC(L); + ret = luaO_pushvfstring(L, fmt, argp); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + luaC_checkGC(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + Closure *cl; + lua_lock(L); + luaC_checkGC(L); + api_checknelems(L, n); + cl = luaF_newCclosure(L, n, getcurrenv(L)); + cl->c.f = fn; + L->top -= n; + while (n--) + setobj2n(L, &cl->c.upvalue[n], L->top+n); + setclvalue(L, L->top, cl); + lua_assert(iswhite(obj2gco(cl))); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, L->top, L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +LUA_API void lua_gettable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_gettable(L, t, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue(L, &key, luaS_new(L, k)); + luaV_gettable(L, t, &key, L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_rawget (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); + lua_unlock(L); +} + + +LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + setobj2s(L, L->top, luaH_getnum(hvalue(o), n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + lua_lock(L); + luaC_checkGC(L); + sethvalue(L, L->top, luaH_new(L, narray, nrec)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt = NULL; + int res; + lua_lock(L); + obj = index2adr(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt == NULL) + res = 0; + else { + sethvalue(L, L->top, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_getfenv (lua_State *L, int idx) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + switch (ttype(o)) { + case LUA_TFUNCTION: + sethvalue(L, L->top, clvalue(o)->c.env); + break; + case LUA_TUSERDATA: + sethvalue(L, L->top, uvalue(o)->env); + break; + case LUA_TTHREAD: + setobj2s(L, L->top, gt(thvalue(o))); + break; + default: + setnilvalue(L->top); + break; + } + api_incr_top(L); + lua_unlock(L); +} + + +/* +** set functions (stack -> Lua) +*/ + + +LUA_API void lua_settable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_settable(L, t, L->top - 2, L->top - 1); + L->top -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + api_checknelems(L, 1); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue(L, &key, luaS_new(L, k)); + luaV_settable(L, t, &key, L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); + luaC_barriert(L, hvalue(t), L->top-1); + L->top -= 2; + lua_unlock(L); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1); + luaC_barriert(L, hvalue(o), L->top-1); + L->top--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checknelems(L, 1); + obj = index2adr(L, objindex); + api_checkvalidindex(L, obj); + if (ttisnil(L->top - 1)) + mt = NULL; + else { + api_check(L, ttistable(L->top - 1)); + mt = hvalue(L->top - 1); + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) + luaC_objbarriert(L, hvalue(obj), mt); + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) + luaC_objbarrier(L, rawuvalue(obj), mt); + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top--; + lua_unlock(L); + return 1; +} + + +LUA_API int lua_setfenv (lua_State *L, int idx) { + StkId o; + int res = 1; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + api_check(L, ttistable(L->top - 1)); + switch (ttype(o)) { + case LUA_TFUNCTION: + clvalue(o)->c.env = hvalue(L->top - 1); + break; + case LUA_TUSERDATA: + uvalue(o)->env = hvalue(L->top - 1); + break; + case LUA_TTHREAD: + sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1)); + break; + default: + res = 0; + break; + } + if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); + L->top--; + lua_unlock(L); + return res; +} + + +/* +** `load' and `call' functions (run Lua code) +*/ + + +#define adjustresults(L,nres) \ + { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; } + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na))) + + +LUA_API void lua_call (lua_State *L, int nargs, int nresults) { + StkId func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + func = L->top - (nargs+1); + luaD_call(L, func, nresults); + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to `f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_call(L, c->func, c->nresults); +} + + + +LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2adr(L, errfunc); + api_checkvalidindex(L, o); + func = savestack(L, o); + } + c.func = L->top - (nargs+1); /* function to be called */ + c.nresults = nresults; + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +/* +** Execute a protected C call. +*/ +struct CCallS { /* data to `f_Ccall' */ + lua_CFunction func; + void *ud; +}; + + +static void f_Ccall (lua_State *L, void *ud) { + struct CCallS *c = cast(struct CCallS *, ud); + Closure *cl; + cl = luaF_newCclosure(L, 0, getcurrenv(L)); + cl->c.f = c->func; + setclvalue(L, L->top, cl); /* push function */ + api_incr_top(L); + setpvalue(L->top, c->ud); /* push only argument */ + api_incr_top(L); + luaD_call(L, L->top - 2, 0); +} + + +LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) { + struct CCallS c; + int status; + lua_lock(L); + c.func = func; + c.ud = ud; + status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0); + lua_unlock(L); + return status; +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname); + lua_unlock(L); + return status; +} + + +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = L->top - 1; + if (isLfunction(o)) + status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ + +LUA_API int lua_gc (lua_State *L, int what, int data) { + int res = 0; + global_State *g; + lua_lock(L); + g = G(L); + switch (what) { + case LUA_GCSTOP: { + g->GCthreshold = MAX_LUMEM; + break; + } + case LUA_GCRESTART: { + g->GCthreshold = g->totalbytes; + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(g->totalbytes >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(g->totalbytes & 0x3ff); + break; + } + case LUA_GCSTEP: { + lu_mem a = (cast(lu_mem, data) << 10); + if (a <= g->totalbytes) + g->GCthreshold = g->totalbytes - a; + else + g->GCthreshold = 0; + while (g->GCthreshold <= g->totalbytes) { + luaC_step(L); + if (g->gcstate == GCSpause) { /* end of cycle? */ + res = 1; /* signal it */ + break; + } + } + break; + } + case LUA_GCSETPAUSE: { + res = g->gcpause; + g->gcpause = data; + break; + } + case LUA_GCSETSTEPMUL: { + res = g->gcstepmul; + g->gcstepmul = data; + break; + } + default: res = -1; /* invalid option */ + } + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + lua_lock(L); + api_checknelems(L, 1); + luaG_errormsg(L); + lua_unlock(L); + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + StkId t; + int more; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + more = luaH_next(L, hvalue(t), L->top - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n >= 2) { + luaC_checkGC(L); + luaV_concat(L, n, cast_int(L->top - L->base) - 1); + L->top -= (n-1); + } + else if (n == 0) { /* push empty string */ + setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); + api_incr_top(L); + } + /* else n == 1; nothing to do */ + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +LUA_API void *lua_newuserdata (lua_State *L, size_t size) { + Udata *u; + lua_lock(L); + luaC_checkGC(L); + u = luaS_newudata(L, size, getcurrenv(L)); + setuvalue(L, L->top, u); + api_incr_top(L); + lua_unlock(L); + return u + 1; +} + + + + +static const char *aux_upvalue (StkId fi, int n, TValue **val) { + Closure *f; + if (!ttisfunction(fi)) return NULL; + f = clvalue(fi); + if (f->c.isC) { + if (!(1 <= n && n <= f->c.nupvalues)) return NULL; + *val = &f->c.upvalue[n-1]; + return ""; + } + else { + Proto *p = f->l.p; + if (!(1 <= n && n <= p->sizeupvalues)) return NULL; + *val = f->l.upvals[n-1]->v; + return getstr(p->upvalues[n-1]); + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + lua_lock(L); + name = aux_upvalue(index2adr(L, funcindex), n, &val); + if (name) { + setobj2s(L, L->top, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + StkId fi; + lua_lock(L); + fi = index2adr(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val); + if (name) { + L->top--; + setobj(L, val, L->top); + luaC_barrier(L, clvalue(fi), L->top); + } + lua_unlock(L); + return name; +} + diff --git a/src/server/game/LuaEngine/lua_src/lapi.h b/src/server/game/LuaEngine/lua_src/lapi.h new file mode 100644 index 0000000000..2c3fab244e --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lapi.h @@ -0,0 +1,16 @@ +/* +** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "lobject.h" + + +LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o); + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lauxlib.c b/src/server/game/LuaEngine/lua_src/lauxlib.c new file mode 100644 index 0000000000..10f14e2c08 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lauxlib.c @@ -0,0 +1,652 @@ +/* +** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include +#include + + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" + + +#define FREELIST_REF 0 /* free list of references */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, narg, def) : + luaL_checkstring(L, narg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, narg, + lua_pushfstring(L, "invalid option " LUA_QS, name)); +} + + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + } + luaL_typerror(L, ud, tname); /* else error */ + return NULL; /* to avoid warnings */ +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tolstring(L, narg, len); + if (!s) tag_error(L, narg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, narg, def); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { + lua_Integer d = lua_tointeger(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, narg, def); +} + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l) { + luaI_openlib(L, libname, l, 0); +} + + +static int libsize (const luaL_Reg *l) { + int size = 0; + for (; l->name; l++) size++; + return size; +} + + +LUALIB_API void luaI_openlib (lua_State *L, const char *libname, + const luaL_Reg *l, int nup) { + if (libname) { + int size = libsize(l); + /* check whether lib already exists */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, libname); /* get _LOADED[libname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) + luaL_error(L, "name conflict for module " LUA_QS, libname); + lua_pushvalue(L, -1); + lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ + } + lua_remove(L, -2); /* remove _LOADED table */ + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + for (i=0; ifunc, nup); + lua_setfield(L, -(nup+2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +#if defined(LUA_COMPAT_GETN) + +static int checkint (lua_State *L, int topop) { + int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1; + lua_pop(L, topop); + return n; +} + + +static void getsizes (lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */ + } +} + + +LUALIB_API void luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushinteger(L, n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushinteger(L, n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +LUALIB_API int luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + return (int)lua_objlen(L, t); +} + +#endif + +/* }====================================================== */ + + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, + const char *r) { + const char *wild; + size_t l = strlen(p); + luaL_Buffer b; + luaL_buffinit(L, &b); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(&b, s, wild - s); /* push prefix */ + luaL_addstring(&b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after `p' */ + } + luaL_addstring(&b, s); /* push last suffix */ + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +LUALIB_API const char *luaL_findtable (lua_State *L, int idx, + const char *fname, int szhint) { + const char *e; + lua_pushvalue(L, idx); + do { + e = strchr(fname, '.'); + if (e == NULL) e = fname + strlen(fname); + lua_pushlstring(L, fname, e - fname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { /* no such field? */ + lua_pop(L, 1); /* remove this nil */ + lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ + lua_pushlstring(L, fname, e - fname); + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } + else if (!lua_istable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2); /* remove table and value */ + return fname; /* return problematic part of the name */ + } + lua_remove(L, -2); /* remove previous table */ + fname = e + 1; + } while (*e == '.'); + return NULL; +} + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_addchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl; + const char *s = lua_tolstring(L, -1, &vl); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, s, vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = (int)lua_objlen(L, t); + ref++; /* create new reference */ + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + int extraline; + FILE *f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (lf->extraline) { + lf->extraline = 0; + *size = 1; + return "\n"; + } + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); + return (*size > 0) ? lf->buff : NULL; +} + + +static int errfile (lua_State *L, const char *what, int fnameindex) { + const char *serr = strerror(errno); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + lf.extraline = 0; + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + c = getc(lf.f); + if (c == '#') { /* Unix exec. file? */ + lf.extraline = 1; + while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ + if (c == '\n') c = getc(lf.f); + } + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + /* skip eventual `#!...' */ + while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; + lf.extraline = 0; + } + ungetc(c, lf.f); + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + + +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + + + +/* }====================================================== */ + + +static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + (void)ud; + (void)osize; + if (nsize == 0) { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} + + +static int panic (lua_State *L) { + (void)L; /* to avoid warnings */ + fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); + return 0; +} + + +LUALIB_API lua_State *luaL_newstate (void) { + lua_State *L = lua_newstate(l_alloc, NULL); + if (L) lua_atpanic(L, &panic); + return L; +} + diff --git a/src/server/game/LuaEngine/lua_src/lauxlib.h b/src/server/game/LuaEngine/lua_src/lauxlib.h new file mode 100644 index 0000000000..34258235db --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lauxlib.h @@ -0,0 +1,174 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + +#if defined(LUA_COMPAT_GETN) +LUALIB_API int (luaL_getn) (lua_State *L, int t); +LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); +#else +#define luaL_getn(L,i) ((int)lua_objlen(L, i)) +#define luaL_setn(L,i,j) ((void)0) /* no op! */ +#endif + +#if defined(LUA_COMPAT_OPENLIB) +#define luaI_openlib luaL_openlib +#endif + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + + +LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + + +/* compatibility with ref system */ + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ + (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) + +#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) + +#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) + + +#define luaL_reg luaL_Reg + +#endif + + diff --git a/src/server/game/LuaEngine/lua_src/lbaselib.c b/src/server/game/LuaEngine/lua_src/lbaselib.c new file mode 100644 index 0000000000..2ab550bd48 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lbaselib.c @@ -0,0 +1,653 @@ +/* +** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $ +** Basic library +** See Copyright Notice in lua.h +*/ + + + +#include +#include +#include +#include + +#define lbaselib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, LUA_QL("tostring") " must return a string to " + LUA_QL("print")); + if (i>1) fputs("\t", stdout); + fputs(s, stdout); + lua_pop(L, 1); /* pop result */ + } + fputs("\n", stdout); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + lua_settop(L, 1); + if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void getfunc (lua_State *L, int opt) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int luaB_getfenv (lua_State *L) { + getfunc(L, 1); + if (lua_iscfunction(L, -1)) /* is a C function? */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ + else + lua_getfenv(L, -1); + return 1; +} + + +static int luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L, 0); + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) { + /* change environment of current thread */ + lua_pushthread(L); + lua_insert(L, -2); + lua_setfenv(L, -2); + return 0; + } + else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0) + luaL_error(L, + LUA_QL("setfenv") " cannot change environment of given object"); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushinteger(L, lua_getgccount(L)); + return 1; +} + + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL}; + int o = luaL_checkoption(L, 1, "collect", opts); + int ex = luaL_optint(L, 2, 0); + int res = lua_gc(L, optsnum[o], ex); + switch (optsnum[o]) { + case LUA_GCCOUNT: { + int b = lua_gc(L, LUA_GCCOUNTB, 0); + lua_pushnumber(L, res + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + lua_pushboolean(L, res); + return 1; + } + default: { + lua_pushnumber(L, res); + return 1; + } + } +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, luaL_typename(L, 1)); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int ipairsaux (lua_State *L) { + int i = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + i++; /* next value */ + lua_pushinteger(L, i); + lua_rawgeti(L, 1, i); + return (lua_isnil(L, -1)) ? 0 : 2; +} + + +static int luaB_ipairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushinteger(L, 0); /* and initial value */ + return 3; +} + + +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + return load_aux(L, luaL_loadfile(L, fname)); +} + + +/* +** Reader for generic `load' function: `lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *generic_reader (lua_State *L, void *ud, size_t *size) { + (void)ud; /* to avoid warnings */ + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + *size = 0; + return NULL; + } + else if (lua_isstring(L, -1)) { + lua_replace(L, 3); /* save string in a reserved stack slot */ + return lua_tolstring(L, 3, size); + } + else luaL_error(L, "reader function must return a string"); + return NULL; /* to avoid warnings */ +} + + +static int luaB_load (lua_State *L) { + int status; + const char *cname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 3); /* function, eventual name, plus one reserved slot */ + status = lua_load(L, generic_reader, NULL, cname); + return load_aux(L, status); +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int n = lua_gettop(L); + if (luaL_loadfile(L, fname) != 0) lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + return lua_gettop(L); +} + + +static int luaB_unpack (lua_State *L) { + int i, e, n; + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_optint(L, 2, 1); + e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); + if (i > e) return 0; /* empty range */ + n = e - i + 1; /* number of elements */ + if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ + return luaL_error(L, "too many results to unpack"); + lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ + while (i++ < e) /* push arg[i + 1...e] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + int i = luaL_checkint(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - i; + } +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + break; + case LUA_TSTRING: + lua_pushvalue(L, 1); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: + lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); + break; + } + return 1; +} + + +static int luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"dofile", luaB_dofile}, + {"error", luaB_error}, + {"gcinfo", luaB_gcinfo}, + {"getfenv", luaB_getfenv}, + {"getmetatable", luaB_getmetatable}, + {"loadfile", luaB_loadfile}, + {"load", luaB_load}, + {"loadstring", luaB_loadstring}, + {"next", luaB_next}, + {"pcall", luaB_pcall}, + {"print", luaB_print}, + {"rawequal", luaB_rawequal}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setfenv", luaB_setfenv}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"unpack", luaB_unpack}, + {"xpcall", luaB_xpcall}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +#define CO_RUN 0 /* running */ +#define CO_SUS 1 /* suspended */ +#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ +#define CO_DEAD 3 + +static const char *const statnames[] = + {"running", "suspended", "normal", "dead"}; + +static int costatus (lua_State *L, lua_State *co) { + if (L == co) return CO_RUN; + switch (lua_status(co)) { + case LUA_YIELD: + return CO_SUS; + case 0: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ + return CO_NOR; /* it is running */ + else if (lua_gettop(co) == 0) + return CO_DEAD; + else + return CO_SUS; /* initial state */ + } + default: /* some error occured */ + return CO_DEAD; + } +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + lua_pushstring(L, statnames[costatus(L, co)]); + return 1; +} + + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status = costatus(L, co); + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + if (status != CO_SUS) { + lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + lua_setlevel(L, co); + status = lua_resume(co, narg); + if (status == 0 || status == LUA_YIELD) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres + 1)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_corunning (lua_State *L) { + if (lua_pushthread(L)) + lua_pushnil(L); /* main thread is not a coroutine */ + return 1; +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {NULL, NULL} +}; + +/* }====================================================== */ + + +static void auxopen (lua_State *L, const char *name, + lua_CFunction f, lua_CFunction u) { + lua_pushcfunction(L, u); + lua_pushcclosure(L, f, 1); + lua_setfield(L, -2, name); +} + + +static void base_open (lua_State *L) { + /* set global _G */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setglobal(L, "_G"); + /* open lib into global table */ + luaL_register(L, "_G", base_funcs); + lua_pushliteral(L, LUA_VERSION); + lua_setglobal(L, "_VERSION"); /* set global _VERSION */ + /* `ipairs' and `pairs' need auxiliary functions as upvalues */ + auxopen(L, "ipairs", luaB_ipairs, ipairsaux); + auxopen(L, "pairs", luaB_pairs, luaB_next); + /* `newproxy' needs a weaktable as upvalue */ + lua_createtable(L, 0, 1); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_setglobal(L, "newproxy"); /* set global `newproxy' */ +} + + +LUALIB_API int luaopen_base (lua_State *L) { + base_open(L); + luaL_register(L, LUA_COLIBNAME, co_funcs); + return 2; +} + diff --git a/src/server/game/LuaEngine/lua_src/lcode.c b/src/server/game/LuaEngine/lua_src/lcode.c new file mode 100644 index 0000000000..679cb9cfd9 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lcode.c @@ -0,0 +1,831 @@ +/* +** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + + +#include + +#define lcode_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "ltable.h" + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int isnumeral(expdesc *e) { + return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +} + + +void luaK_nil (FuncState *fs, int from, int n) { + Instruction *previous; + if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ + if (fs->pc == 0) { /* function start? */ + if (from >= fs->nactvar) + return; /* positions are already clean */ + } + else { + previous = &fs->f->code[fs->pc-1]; + if (GET_OPCODE(*previous) == OP_LOADNIL) { + int pfrom = GETARG_A(*previous); + int pto = GETARG_B(*previous); + if (pfrom <= from && from <= pto+1) { /* can connect both? */ + if (from+n-1 > pto) + SETARG_B(*previous, from+n-1); + return; + } + } + } + } + luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */ +} + + +int luaK_jump (FuncState *fs) { + int jpc = fs->jpc; /* save list of jumps to here */ + int j; + fs->jpc = NO_JUMP; + j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); + luaK_concat(fs, &j, jpc); /* keep them on hold */ + return j; +} + + +void luaK_ret (FuncState *fs, int first, int nret) { + luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); +} + + +static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { + luaK_codeABC(fs, op, A, B, C); + return luaK_jump(fs); +} + + +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest-(pc+1); + lua_assert(dest != NO_JUMP); + if (abs(offset) > MAXARG_sBx) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_sBx(*jmp, offset); +} + + +/* +** returns current `pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sBx(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** check whether list has any jump that do not produce a value +** (or produce an inverted value) +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else /* no register to put value or register already has the value */ + *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); + + return 1; +} + + +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +static void dischargejpc (FuncState *fs) { + patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); + fs->jpc = NO_JUMP; +} + + +void luaK_patchlist (FuncState *fs, int list, int target) { + if (target == fs->pc) + luaK_patchtohere(fs, list); + else { + lua_assert(target < fs->pc); + patchlistaux(fs, list, target, NO_REG, target); + } +} + + +void luaK_patchtohere (FuncState *fs, int list) { + luaK_getlabel(fs); + luaK_concat(fs, &fs->jpc, list); +} + + +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; + else if (*l1 == NO_JUMP) + *l1 = l2; + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); + } +} + + +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXSTACK) + luaX_syntaxerror(fs->ls, "function or expression too complex"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +static void freereg (FuncState *fs, int reg) { + if (!ISK(reg) && reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.s.info); +} + + +static int addk (FuncState *fs, TValue *k, TValue *v) { + lua_State *L = fs->L; + TValue *idx = luaH_set(L, fs->h, k); + Proto *f = fs->f; + int oldsize = f->sizek; + if (ttisnumber(idx)) { + lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); + return cast_int(nvalue(idx)); + } + else { /* constant not found; create a new entry */ + setnvalue(idx, cast_num(fs->nk)); + luaM_growvector(L, f->k, fs->nk, f->sizek, TValue, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[fs->nk], v); + luaC_barrier(L, f, v); + return fs->nk++; + } +} + + +int luaK_stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->L, &o, s); + return addk(fs, &o, &o); +} + + +int luaK_numberK (FuncState *fs, lua_Number r) { + TValue o; + setnvalue(&o, r); + return addk(fs, &o, &o); +} + + +static int boolK (FuncState *fs, int b) { + TValue o; + setbvalue(&o, b); + return addk(fs, &o, &o); +} + + +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->L, &k, fs->h); + return addk(fs, &k, &v); +} + + +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + if (e->k == VCALL) { /* expression is an open function call? */ + SETARG_C(getcode(fs, e), nresults+1); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), nresults+1); + SETARG_A(getcode(fs, e), fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + e->k = VNONRELOC; + e->u.s.info = GETARG_A(getcode(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), 2); + e->k = VRELOCABLE; /* can relocate its simple result */ + } +} + + +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VLOCAL: { + e->k = VNONRELOC; + break; + } + case VUPVAL: { + e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + case VGLOBAL: { + e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info); + e->k = VRELOCABLE; + break; + } + case VINDEXED: { + freereg(fs, e->u.s.aux); + freereg(fs, e->u.s.info); + e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux); + e->k = VRELOCABLE; + break; + } + case VVARARG: + case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +static int code_label (FuncState *fs, int A, int b, int jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); +} + + +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: case VTRUE: { + luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); + break; + } + case VK: { + luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); + break; + } + case VKNUM: { + luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); + break; + } + case VRELOCABLE: { + Instruction *pc = &getcode(fs, e); + SETARG_A(*pc, reg); + break; + } + case VNONRELOC: { + if (reg != e->u.s.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0); + break; + } + default: { + lua_assert(e->k == VVOID || e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { + luaK_reserveregs(fs, 1); + discharge2reg(fs, e, fs->freereg-1); + } +} + + +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) + luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_label(fs, reg, 0, 1); + p_t = code_label(fs, reg, 1, 0); + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { + if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ + if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.s.info); /* put value on it */ + return e->u.s.info; + } + } + luaK_exp2nextreg(fs, e); /* default */ + return e->u.s.info; +} + + +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +int luaK_exp2RK (FuncState *fs, expdesc *e) { + luaK_exp2val(fs, e); + switch (e->k) { + case VKNUM: + case VTRUE: + case VFALSE: + case VNIL: { + if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */ + e->u.s.info = (e->k == VNIL) ? nilK(fs) : + (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) : + boolK(fs, (e->k == VTRUE)); + e->k = VK; + return RKASK(e->u.s.info); + } + else break; + } + case VK: { + if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */ + return RKASK(e->u.s.info); + else break; + } + default: break; + } + /* not a constant in the right range: put it in a register */ + return luaK_exp2anyreg(fs, e); +} + + +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.s.info); + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); + break; + } + case VGLOBAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); + break; + } + case VINDEXED: { + int e = luaK_exp2RK(fs, ex); + luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); + break; + } + default: { + lua_assert(0); /* invalid var kind to store */ + break; + } + } + freeexp(fs, ex); +} + + +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int func; + luaK_exp2anyreg(fs, e); + freeexp(fs, e); + func = fs->freereg; + luaK_reserveregs(fs, 2); + luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key)); + freeexp(fs, key); + e->u.s.info = func; + e->k = VNONRELOC; +} + + +static void invertjump (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.s.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_A(*pc, !(GETARG_A(*pc))); +} + + +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOCABLE) { + Instruction ie = getcode(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + fs->pc--; /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond); +} + + +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VK: case VKNUM: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + case VJMP: { + invertjump(fs, e); + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 0); + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ + luaK_patchtohere(fs, e->t); + e->t = NO_JUMP; +} + + +static void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + case VJMP: { + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 1); + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ + luaK_patchtohere(fs, e->f); + e->f = NO_JUMP; +} + + +static void codenot (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; + break; + } + case VK: case VKNUM: case VTRUE: { + e->k = VFALSE; + break; + } + case VJMP: { + invertjump(fs, e); + break; + } + case VRELOCABLE: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + default: { + lua_assert(0); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); + removevalues(fs, e->t); +} + + +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + t->u.s.aux = luaK_exp2RK(fs, k); + t->k = VINDEXED; +} + + +static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { + lua_Number v1, v2, r; + if (!isnumeral(e1) || !isnumeral(e2)) return 0; + v1 = e1->u.nval; + v2 = e2->u.nval; + switch (op) { + case OP_ADD: r = luai_numadd(v1, v2); break; + case OP_SUB: r = luai_numsub(v1, v2); break; + case OP_MUL: r = luai_nummul(v1, v2); break; + case OP_DIV: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_numdiv(v1, v2); break; + case OP_MOD: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_nummod(v1, v2); break; + case OP_POW: r = luai_numpow(v1, v2); break; + case OP_UNM: r = luai_numunm(v1); break; + case OP_LEN: return 0; /* no constant folding for 'len' */ + default: lua_assert(0); r = 0; break; + } + if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */ + e1->u.nval = r; + return 1; +} + + +static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { + if (constfolding(op, e1, e2)) + return; + else { + int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; + int o1 = luaK_exp2RK(fs, e1); + if (o1 > o2) { + freeexp(fs, e1); + freeexp(fs, e2); + } + else { + freeexp(fs, e2); + freeexp(fs, e1); + } + e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2); + e1->k = VRELOCABLE; + } +} + + +static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, + expdesc *e2) { + int o1 = luaK_exp2RK(fs, e1); + int o2 = luaK_exp2RK(fs, e2); + freeexp(fs, e2); + freeexp(fs, e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1->u.s.info = condjump(fs, op, cond, o1, o2); + e1->k = VJMP; +} + + +void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { + expdesc e2; + e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + switch (op) { + case OPR_MINUS: { + if (!isnumeral(e)) + luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ + codearith(fs, OP_UNM, e, &e2); + break; + } + case OPR_NOT: codenot(fs, e); break; + case OPR_LEN: { + luaK_exp2anyreg(fs, e); /* cannot operate on constants */ + codearith(fs, OP_LEN, e, &e2); + break; + } + default: lua_assert(0); + } +} + + +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ + break; + } + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + if (!isnumeral(v)) luaK_exp2RK(fs, v); + break; + } + default: { + luaK_exp2RK(fs, v); + break; + } + } +} + + +void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { + switch (op) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { + luaK_exp2val(fs, e2); + if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { + lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1); + freeexp(fs, e1); + SETARG_B(getcode(fs, e2), e1->u.s.info); + e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info; + } + else { + luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ + codearith(fs, OP_CONCAT, e1, e2); + } + break; + } + case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; + case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; + case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; + case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; + case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; + case OPR_POW: codearith(fs, OP_POW, e1, e2); break; + case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; + case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; + case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; + case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; + case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; + case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; + default: lua_assert(0); + } +} + + +void luaK_fixline (FuncState *fs, int line) { + fs->f->lineinfo[fs->pc - 1] = line; +} + + +static int luaK_code (FuncState *fs, Instruction i, int line) { + Proto *f = fs->f; + dischargejpc(fs); /* `pc' will change */ + /* put new instruction in code array */ + luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "code size overflow"); + f->code[fs->pc] = i; + /* save corresponding line information */ + luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, + MAX_INT, "code size overflow"); + f->lineinfo[fs->pc] = line; + return fs->pc++; +} + + +int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { + lua_assert(getOpMode(o) == iABC); + lua_assert(getBMode(o) != OpArgN || b == 0); + lua_assert(getCMode(o) != OpArgN || c == 0); + return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline); +} + + +int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); + lua_assert(getCMode(o) == OpArgN); + return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline); +} + + +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + lua_assert(tostore != 0); + if (c <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, b, c); + else { + luaK_codeABC(fs, OP_SETLIST, base, b, 0); + luaK_code(fs, cast(Instruction, c), fs->ls->lastline); + } + fs->freereg = base + 1; /* free registers with list values */ +} + diff --git a/src/server/game/LuaEngine/lua_src/lcode.h b/src/server/game/LuaEngine/lua_src/lcode.h new file mode 100644 index 0000000000..b941c60721 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lcode.h @@ -0,0 +1,76 @@ +/* +** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums +*/ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + OPR_CONCAT, + OPR_NE, OPR_EQ, + OPR_LT, OPR_LE, OPR_GT, OPR_GE, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info]) + +#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); +LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/ldblib.c b/src/server/game/LuaEngine/lua_src/ldblib.c new file mode 100644 index 0000000000..2027eda598 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ldblib.c @@ -0,0 +1,398 @@ +/* +** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define ldblib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static int db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + lua_settop(L, 2); + lua_pushboolean(L, lua_setmetatable(L, 1)); + return 1; +} + + +static int db_getfenv (lua_State *L) { + luaL_checkany(L, 1); + lua_getfenv(L, 1); + return 1; +} + + +static int db_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); + if (lua_setfenv(L, 1) == 0) + luaL_error(L, LUA_QL("setfenv") + " cannot change environment of given object"); + return 1; +} + + +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, i); +} + + +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, i); +} + + +static lua_State *getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; + } +} + + +static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) { + lua_pushvalue(L, -2); + lua_remove(L, -3); + } + else + lua_xmove(L1, L, 1); + lua_setfield(L, -2, fname); +} + + +static int db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSu"); + if (lua_isnumber(L, arg+1)) { + if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, arg+1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, arg+1); + lua_xmove(L, L1, 1); + } + else + return luaL_argerror(L, arg+1, "function or level expected"); + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_createtable(L, 0, 2); + if (strchr(options, 'S')) { + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (strchr(options, 'u')) + settabsi(L, "nups", ar.nups); + if (strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + const char *name; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); + if (name) { + lua_xmove(L1, L, 1); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int db_setlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + lua_xmove(L, L1, 1); + lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); + return 1; +} + + +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, L); + lua_rawget(L, -2); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } +} + + +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static void gethooktable (lua_State *L) { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_createtable(L, 0, 1); + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } +} + + +static int db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = luaL_optint(L, arg+3, 0); + func = hookf; mask = makemask(smask, count); + } + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_pushvalue(L, arg+1); + lua_rawset(L, -3); /* set new hook */ + lua_pop(L, 1); /* remove hook table */ + lua_sethook(L1, func, mask, count); /* set hooks */ + return 0; +} + + +static int db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_rawget(L, -2); /* get hook */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushinteger(L, lua_gethookcount(L1)); + return 3; +} + + +static int db_debug (lua_State *L) { + for (;;) { + char buffer[250]; + fputs("lua_debug> ", stderr); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) { + fputs(lua_tostring(L, -1), stderr); + fputs("\n", stderr); + } + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int db_errorfb (lua_State *L) { + int level; + int firstpart = 1; /* still before eventual `...' */ + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (lua_isnumber(L, arg+2)) { + level = (int)lua_tointeger(L, arg+2); + lua_pop(L, 1); + } + else + level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ + if (lua_gettop(L) == arg) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L1, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L1, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + if (*ar.namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, " in function " LUA_QS, ar.name); + else { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + lua_concat(L, lua_gettop(L) - arg); + } + lua_concat(L, lua_gettop(L) - arg); + return 1; +} + + +static const luaL_Reg dblib[] = { + {"debug", db_debug}, + {"getfenv", db_getfenv}, + {"gethook", db_gethook}, + {"getinfo", db_getinfo}, + {"getlocal", db_getlocal}, + {"getregistry", db_getregistry}, + {"getmetatable", db_getmetatable}, + {"getupvalue", db_getupvalue}, + {"setfenv", db_setfenv}, + {"sethook", db_sethook}, + {"setlocal", db_setlocal}, + {"setmetatable", db_setmetatable}, + {"setupvalue", db_setupvalue}, + {"traceback", db_errorfb}, + {NULL, NULL} +}; + + +LUALIB_API int luaopen_debug (lua_State *L) { + luaL_register(L, LUA_DBLIBNAME, dblib); + return 1; +} + diff --git a/src/server/game/LuaEngine/lua_src/ldebug.c b/src/server/game/LuaEngine/lua_src/ldebug.c new file mode 100644 index 0000000000..50ad3d3803 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ldebug.c @@ -0,0 +1,638 @@ +/* +** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + + +#define ldebug_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); + + +static int currentpc (lua_State *L, CallInfo *ci) { + if (!isLua(ci)) return -1; /* function is not a Lua function? */ + if (ci == L->ci) + ci->savedpc = L->savedpc; + return pcRel(ci->savedpc, ci_func(ci)->l.p); +} + + +static int currentline (lua_State *L, CallInfo *ci) { + int pc = currentpc(L, ci); + if (pc < 0) + return -1; /* only active lua functions have current-line information */ + else + return getline(ci_func(ci)->l.p, pc); +} + + +/* +** this function can be called asynchronous (e.g. during a signal) +*/ +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + return 1; +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + lua_lock(L); + for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) { + level--; + if (f_isLua(ci)) /* Lua function? */ + level -= ci->tailcalls; /* skip lost tail calls */ + } + if (level == 0 && ci > L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = cast_int(ci - L->base_ci); + } + else if (level < 0) { /* level is of a lost tail call? */ + status = 1; + ar->i_ci = 0; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static Proto *getluaproto (CallInfo *ci) { + return (isLua(ci) ? ci_func(ci)->l.p : NULL); +} + + +static const char *findlocal (lua_State *L, CallInfo *ci, int n) { + const char *name; + Proto *fp = getluaproto(ci); + if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL) + return name; /* is a local variable in a Lua function */ + else { + StkId limit = (ci == L->ci) ? L->top : (ci+1)->func; + if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */ + return "(*temporary)"; + else + return NULL; + } +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + luaA_pushobject(L, ci->base + (n - 1)); + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + setobjs2s(L, ci->base + (n - 1), L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (cl->c.isC) { + ar->source = "=[C]"; + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + ar->source = getstr(cl->l.p->source); + ar->linedefined = cl->l.p->linedefined; + ar->lastlinedefined = cl->l.p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); +} + + +static void info_tailcall (lua_Debug *ar) { + ar->name = ar->namewhat = ""; + ar->what = "tail"; + ar->lastlinedefined = ar->linedefined = ar->currentline = -1; + ar->source = "=(tail call)"; + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); + ar->nups = 0; +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (f == NULL || f->c.isC) { + setnilvalue(L->top); + } + else { + Table *t = luaH_new(L, 0, 0); + int *lineinfo = f->l.p->lineinfo; + int i; + for (i=0; il.p->sizelineinfo; i++) + setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); + sethvalue(L, L->top, t); + } + incr_top(L); +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + if (f == NULL) { + info_tailcall(ar); + return status; + } + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci) ? currentline(L, ci) : -1; + break; + } + case 'u': { + ar->nups = f->c.nupvalues; + break; + } + case 'n': { + ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *f = NULL; + CallInfo *ci = NULL; + lua_lock(L); + if (*what == '>') { + StkId func = L->top - 1; + luai_apicheck(L, ttisfunction(func)); + what++; /* skip the '>' */ + f = clvalue(func); + L->top--; /* pop function */ + } + else if (ar->i_ci != 0) { /* no tail call? */ + ci = L->base_ci + ar->i_ci; + lua_assert(ttisfunction(ci->func)); + f = clvalue(ci->func); + } + status = auxgetinfo(L, what, ar, f, ci); + if (strchr(what, 'f')) { + if (f == NULL) setnilvalue(L->top); + else setclvalue(L, L->top, f); + incr_top(L); + } + if (strchr(what, 'L')) + collectvalidlines(L, f); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution and code checker +** ======================================================= +*/ + +#define check(x) if (!(x)) return 0; + +#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) + +#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) + + + +static int precheck (const Proto *pt) { + check(pt->maxstacksize <= MAXSTACK); + check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); + check(!(pt->is_vararg & VARARG_NEEDSARG) || + (pt->is_vararg & VARARG_HASARG)); + check(pt->sizeupvalues <= pt->nups); + check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); + check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); + return 1; +} + + +#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1]) + +int luaG_checkopenop (Instruction i) { + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + case OP_RETURN: + case OP_SETLIST: { + check(GETARG_B(i) == 0); + return 1; + } + default: return 0; /* invalid instruction after an open call */ + } +} + + +static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) { + switch (mode) { + case OpArgN: check(r == 0); break; + case OpArgU: break; + case OpArgR: checkreg(pt, r); break; + case OpArgK: + check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize); + break; + } + return 1; +} + + +static Instruction symbexec (const Proto *pt, int lastpc, int reg) { + int pc; + int last; /* stores position of last instruction that changed `reg' */ + last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ + check(precheck(pt)); + for (pc = 0; pc < lastpc; pc++) { + Instruction i = pt->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int b = 0; + int c = 0; + check(op < NUM_OPCODES); + checkreg(pt, a); + switch (getOpMode(op)) { + case iABC: { + b = GETARG_B(i); + c = GETARG_C(i); + check(checkArgMode(pt, b, getBMode(op))); + check(checkArgMode(pt, c, getCMode(op))); + break; + } + case iABx: { + b = GETARG_Bx(i); + if (getBMode(op) == OpArgK) check(b < pt->sizek); + break; + } + case iAsBx: { + b = GETARG_sBx(i); + if (getBMode(op) == OpArgR) { + int dest = pc+1+b; + check(0 <= dest && dest < pt->sizecode); + if (dest > 0) { + int j; + /* check that it does not jump to a setlist count; this + is tricky, because the count from a previous setlist may + have the same value of an invalid setlist; so, we must + go all the way back to the first of them (if any) */ + for (j = 0; j < dest; j++) { + Instruction d = pt->code[dest-1-j]; + if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break; + } + /* if 'j' is even, previous value is not a setlist (even if + it looks like one) */ + check((j&1) == 0); + } + } + break; + } + } + if (testAMode(op)) { + if (a == reg) last = pc; /* change register `a' */ + } + if (testTMode(op)) { + check(pc+2 < pt->sizecode); /* check skip */ + check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); + } + switch (op) { + case OP_LOADBOOL: { + if (c == 1) { /* does it jump? */ + check(pc+2 < pt->sizecode); /* check its jump */ + check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST || + GETARG_C(pt->code[pc+1]) != 0); + } + break; + } + case OP_LOADNIL: { + if (a <= reg && reg <= b) + last = pc; /* set registers from `a' to `b' */ + break; + } + case OP_GETUPVAL: + case OP_SETUPVAL: { + check(b < pt->nups); + break; + } + case OP_GETGLOBAL: + case OP_SETGLOBAL: { + check(ttisstring(&pt->k[b])); + break; + } + case OP_SELF: { + checkreg(pt, a+1); + if (reg == a+1) last = pc; + break; + } + case OP_CONCAT: { + check(b < c); /* at least two operands */ + break; + } + case OP_TFORLOOP: { + check(c >= 1); /* at least one result (control variable) */ + checkreg(pt, a+2+c); /* space for results */ + if (reg >= a+2) last = pc; /* affect all regs above its base */ + break; + } + case OP_FORLOOP: + case OP_FORPREP: + checkreg(pt, a+3); + /* go through */ + case OP_JMP: { + int dest = pc+1+b; + /* not full check and jump is forward and do not skip `lastpc'? */ + if (reg != NO_REG && pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case OP_CALL: + case OP_TAILCALL: { + if (b != 0) { + checkreg(pt, a+b-1); + } + c--; /* c = num. returns */ + if (c == LUA_MULTRET) { + check(checkopenop(pt, pc)); + } + else if (c != 0) + checkreg(pt, a+c-1); + if (reg >= a) last = pc; /* affect all registers above base */ + break; + } + case OP_RETURN: { + b--; /* b = num. returns */ + if (b > 0) checkreg(pt, a+b-1); + break; + } + case OP_SETLIST: { + if (b > 0) checkreg(pt, a + b); + if (c == 0) { + pc++; + check(pc < pt->sizecode - 1); + } + break; + } + case OP_CLOSURE: { + int nup, j; + check(b < pt->sizep); + nup = pt->p[b]->nups; + check(pc + nup < pt->sizecode); + for (j = 1; j <= nup; j++) { + OpCode op1 = GET_OPCODE(pt->code[pc + j]); + check(op1 == OP_GETUPVAL || op1 == OP_MOVE); + } + if (reg != NO_REG) /* tracing? */ + pc += nup; /* do not 'execute' these pseudo-instructions */ + break; + } + case OP_VARARG: { + check((pt->is_vararg & VARARG_ISVARARG) && + !(pt->is_vararg & VARARG_NEEDSARG)); + b--; + if (b == LUA_MULTRET) check(checkopenop(pt, pc)); + checkreg(pt, a+b-1); + break; + } + default: break; + } + } + return pt->code[last]; +} + +#undef check +#undef checkjump +#undef checkreg + +/* }====================================================== */ + + +int luaG_checkcode (const Proto *pt) { + return (symbexec(pt, pt->sizecode, NO_REG) != 0); +} + + +static const char *kname (Proto *p, int c) { + if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) + return svalue(&p->k[INDEXK(c)]); + else + return "?"; +} + + +static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos, + const char **name) { + if (isLua(ci)) { /* a Lua function? */ + Proto *p = ci_func(ci)->l.p; + int pc = currentpc(L, ci); + Instruction i; + *name = luaF_getlocalname(p, stackpos+1, pc); + if (*name) /* is a local? */ + return "local"; + i = symbexec(p, pc, stackpos); /* try symbolic execution */ + lua_assert(pc != -1); + switch (GET_OPCODE(i)) { + case OP_GETGLOBAL: { + int g = GETARG_Bx(i); /* global index */ + lua_assert(ttisstring(&p->k[g])); + *name = svalue(&p->k[g]); + return "global"; + } + case OP_MOVE: { + int a = GETARG_A(i); + int b = GETARG_B(i); /* move from `b' to `a' */ + if (b < a) + return getobjname(L, ci, b, name); /* get name for `b' */ + break; + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "field"; + } + case OP_GETUPVAL: { + int u = GETARG_B(i); /* upvalue index */ + *name = p->upvalues ? getstr(p->upvalues[u]) : "?"; + return "upvalue"; + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "method"; + } + default: break; + } + } + return NULL; /* no useful name found */ +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + Instruction i; + if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) + return NULL; /* calling function is not Lua (or is unknown) */ + ci--; /* calling function */ + i = ci_func(ci)->l.p->code[currentpc(L, ci)]; + if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || + GET_OPCODE(i) == OP_TFORLOOP) + return getobjname(L, ci, GETARG_A(i), name); + else + return NULL; /* no useful name can be found */ +} + + +/* only ANSI way to check whether a pointer points to an array */ +static int isinstack (CallInfo *ci, const TValue *o) { + StkId p; + for (p = ci->base; p < ci->top; p++) + if (o == p) return 1; + return 0; +} + + +void luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + const char *name = NULL; + const char *t = luaT_typenames[ttype(o)]; + const char *kind = (isinstack(L->ci, o)) ? + getobjname(L, L->ci, cast_int(o - L->base), &name) : + NULL; + if (kind) + luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", + op, kind, name, t); + else + luaG_runerror(L, "attempt to %s a %s value", op, t); +} + + +void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { + if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; + lua_assert(!ttisstring(p1) && !ttisnumber(p1)); + luaG_typeerror(L, p1, "concatenate"); +} + + +void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { + TValue temp; + if (luaV_tonumber(p1, &temp) == NULL) + p2 = p1; /* first operand is wrong */ + luaG_typeerror(L, p2, "perform arithmetic on"); +} + + +int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_typenames[ttype(p1)]; + const char *t2 = luaT_typenames[ttype(p2)]; + if (t1[2] == t2[2]) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); + return 0; +} + + +static void addinfo (lua_State *L, const char *msg) { + CallInfo *ci = L->ci; + if (isLua(ci)) { /* is Lua code? */ + char buff[LUA_IDSIZE]; /* add file:line information */ + int line = currentline(L, ci); + luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); + luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); + } +} + + +void luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); + setobjs2s(L, L->top, L->top - 1); /* move argument */ + setobjs2s(L, L->top - 1, errfunc); /* push function */ + incr_top(L); + luaD_call(L, L->top - 2, 1); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +void luaG_runerror (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + addinfo(L, luaO_pushvfstring(L, fmt, argp)); + va_end(argp); + luaG_errormsg(L); +} + diff --git a/src/server/game/LuaEngine/lua_src/ldebug.h b/src/server/game/LuaEngine/lua_src/ldebug.h new file mode 100644 index 0000000000..ba28a97248 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ldebug.h @@ -0,0 +1,33 @@ +/* +** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) + +#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) + +#define resethookcount(L) (L->hookcount = L->basehookcount) + + +LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2); +LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_checkcode (const Proto *pt); +LUAI_FUNC int luaG_checkopenop (Instruction i); + +#endif diff --git a/src/server/game/LuaEngine/lua_src/ldo.c b/src/server/game/LuaEngine/lua_src/ldo.c new file mode 100644 index 0000000000..d1bf786cb7 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ldo.c @@ -0,0 +1,519 @@ +/* +** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define ldo_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { + setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG)); + break; + } + case LUA_ERRERR: { + setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); + break; + } + case LUA_ERRSYNTAX: + case LUA_ERRRUN: { + setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + break; + } + } + L->top = oldtop + 1; +} + + +static void restore_stack_limit (lua_State *L) { + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ + int inuse = cast_int(L->ci - L->base_ci); + if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ + luaD_reallocCI(L, LUAI_MAXCALLS); + } +} + + +static void resetstack (lua_State *L, int status) { + L->ci = L->base_ci; + L->base = L->ci->base; + luaF_close(L, L->base); /* close eventual pending closures */ + luaD_seterrorobj(L, status, L->base); + L->nCcalls = L->baseCcalls; + L->allowhook = 1; + restore_stack_limit(L); + L->errfunc = 0; + L->errorJmp = NULL; +} + + +void luaD_throw (lua_State *L, int errcode) { + if (L->errorJmp) { + L->errorJmp->status = errcode; + LUAI_THROW(L, L->errorJmp); + } + else { + L->status = cast_byte(errcode); + if (G(L)->panic) { + resetstack(L, errcode); + lua_unlock(L); + G(L)->panic(L); + } + exit(EXIT_FAILURE); + } +} + + +int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + struct lua_longjmp lj; + lj.status = 0; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + return lj.status; +} + +/* }====================================================== */ + + +static void correctstack (lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + + +void luaD_reallocstack (lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack+newsize; + correctstack(L, oldstack); +} + + +void luaD_reallocCI (lua_State *L, int newsize) { + CallInfo *oldci = L->base_ci; + luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); + L->size_ci = newsize; + L->ci = (L->ci - oldci) + L->base_ci; + L->end_ci = L->base_ci + L->size_ci - 1; +} + + +void luaD_growstack (lua_State *L, int n) { + if (n <= L->stacksize) /* double size is enough? */ + luaD_reallocstack(L, 2*L->stacksize); + else + luaD_reallocstack(L, L->stacksize + n); +} + + +static CallInfo *growCI (lua_State *L) { + if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */ + luaD_throw(L, LUA_ERRERR); + else { + luaD_reallocCI(L, 2*L->size_ci); + if (L->size_ci > LUAI_MAXCALLS) + luaG_runerror(L, "stack overflow"); + } + return ++L->ci; +} + + +void luaD_callhook (lua_State *L, int event, int line) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { + ptrdiff_t top = savestack(L, L->top); + ptrdiff_t ci_top = savestack(L, L->ci->top); + lua_Debug ar; + ar.event = event; + ar.currentline = line; + if (event == LUA_HOOKTAILRET) + ar.i_ci = 0; /* tail call; no debug information about it */ + else + ar.i_ci = cast_int(L->ci - L->base_ci); + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + L->ci->top = L->top + LUA_MINSTACK; + lua_assert(L->ci->top <= L->stack_last); + L->allowhook = 0; /* cannot call hooks inside a hook */ + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + L->ci->top = restorestack(L, ci_top); + L->top = restorestack(L, top); + } +} + + +static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { + int i; + int nfixargs = p->numparams; + Table *htab = NULL; + StkId base, fixed; + for (; actual < nfixargs; ++actual) + setnilvalue(L->top++); +#if defined(LUA_COMPAT_VARARG) + if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */ + int nvar = actual - nfixargs; /* number of extra arguments */ + lua_assert(p->is_vararg & VARARG_HASARG); + luaC_checkGC(L); + luaD_checkstack(L, p->maxstacksize); + htab = luaH_new(L, nvar, 1); /* create `arg' table */ + for (i=0; itop - nvar + i); + /* store counter in field `n' */ + setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar)); + } +#endif + /* move fixed parameters to final position */ + fixed = L->top - actual; /* first fixed argument */ + base = L->top; /* final position of first argument */ + for (i=0; itop++, fixed+i); + setnilvalue(fixed+i); + } + /* add `arg' parameter */ + if (htab) { + sethvalue(L, L->top++, htab); + lua_assert(iswhite(obj2gco(htab))); + } + return base; +} + + +static StkId tryfuncTM (lua_State *L, StkId func) { + const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); + StkId p; + ptrdiff_t funcr = savestack(L, func); + if (!ttisfunction(tm)) + luaG_typeerror(L, func, "call"); + /* Open a hole inside the stack at `func' */ + for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); + incr_top(L); + func = restorestack(L, funcr); /* previous call may change stack */ + setobj2s(L, func, tm); /* tag method is the new function to be called */ + return func; +} + + + +#define inc_ci(L) \ + ((L->ci == L->end_ci) ? growCI(L) : \ + (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci)) + + +int luaD_precall (lua_State *L, StkId func, int nresults) { + LClosure *cl; + ptrdiff_t funcr; + if (!ttisfunction(func)) /* `func' is not a function? */ + func = tryfuncTM(L, func); /* check the `function' tag method */ + funcr = savestack(L, func); + cl = &clvalue(func)->l; + L->ci->savedpc = L->savedpc; + if (!cl->isC) { /* Lua function? prepare its call */ + CallInfo *ci; + StkId st, base; + Proto *p = cl->p; + luaD_checkstack(L, p->maxstacksize); + func = restorestack(L, funcr); + if (!p->is_vararg) { /* no varargs? */ + base = func + 1; + if (L->top > base + p->numparams) + L->top = base + p->numparams; + } + else { /* vararg function */ + int nargs = cast_int(L->top - func) - 1; + base = adjust_varargs(L, p, nargs); + func = restorestack(L, funcr); /* previous call may change the stack */ + } + ci = inc_ci(L); /* now `enter' new function */ + ci->func = func; + L->base = ci->base = base; + ci->top = L->base + p->maxstacksize; + lua_assert(ci->top <= L->stack_last); + L->savedpc = p->code; /* starting point */ + ci->tailcalls = 0; + ci->nresults = nresults; + for (st = L->top; st < ci->top; st++) + setnilvalue(st); + L->top = ci->top; + if (L->hookmask & LUA_MASKCALL) { + L->savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_callhook(L, LUA_HOOKCALL, -1); + L->savedpc--; /* correct 'pc' */ + } + return PCRLUA; + } + else { /* if is a C function, call it */ + CallInfo *ci; + int n; + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + ci = inc_ci(L); /* now `enter' new function */ + ci->func = restorestack(L, funcr); + L->base = ci->base = ci->func + 1; + ci->top = L->top + LUA_MINSTACK; + lua_assert(ci->top <= L->stack_last); + ci->nresults = nresults; + if (L->hookmask & LUA_MASKCALL) + luaD_callhook(L, LUA_HOOKCALL, -1); + lua_unlock(L); + n = (*curr_func(L)->c.f)(L); /* do the actual call */ + lua_lock(L); + if (n < 0) /* yielding? */ + return PCRYIELD; + else { + luaD_poscall(L, L->top - n); + return PCRC; + } + } +} + + +static StkId callrethooks (lua_State *L, StkId firstResult) { + ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ + luaD_callhook(L, LUA_HOOKRET, -1); + if (f_isLua(L->ci)) { /* Lua function? */ + while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */ + luaD_callhook(L, LUA_HOOKTAILRET, -1); + } + return restorestack(L, fr); +} + + +int luaD_poscall (lua_State *L, StkId firstResult) { + StkId res; + int wanted, i; + CallInfo *ci; + if (L->hookmask & LUA_MASKRET) + firstResult = callrethooks(L, firstResult); + ci = L->ci--; + res = ci->func; /* res == final position of 1st result */ + wanted = ci->nresults; + L->base = (ci - 1)->base; /* restore base */ + L->savedpc = (ci - 1)->savedpc; /* restore savedpc */ + /* move results to correct place */ + for (i = wanted; i != 0 && firstResult < L->top; i--) + setobjs2s(L, res++, firstResult++); + while (i-- > 0) + setnilvalue(res++); + L->top = res; + return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ +} + + +/* +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + if (++L->nCcalls >= LUAI_MAXCCALLS) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + } + if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ + luaV_execute(L, 1); /* call it */ + L->nCcalls--; + luaC_checkGC(L); +} + + +static void resume (lua_State *L, void *ud) { + StkId firstArg = cast(StkId, ud); + CallInfo *ci = L->ci; + if (L->status == 0) { /* start coroutine? */ + lua_assert(ci == L->base_ci && firstArg > L->base); + if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA) + return; + } + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = 0; + if (!f_isLua(ci)) { /* `common' yield? */ + /* finish interrupted execution of `OP_CALL' */ + lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || + GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); + if (luaD_poscall(L, firstArg)) /* complete it... */ + L->top = L->ci->top; /* and correct top if not multiple results */ + } + else /* yielded inside a hook: just continue its execution */ + L->base = L->ci->base; + } + luaV_execute(L, cast_int(L->ci - L->base_ci)); +} + + +static int resume_error (lua_State *L, const char *msg) { + L->top = L->ci->base; + setsvalue2s(L, L->top, luaS_new(L, msg)); + incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +LUA_API int lua_resume (lua_State *L, int nargs) { + int status; + lua_lock(L); + if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci)) + return resume_error(L, "cannot resume non-suspended coroutine"); + if (L->nCcalls >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow"); + luai_userstateresume(L, nargs); + lua_assert(L->errfunc == 0); + L->baseCcalls = ++L->nCcalls; + status = luaD_rawrunprotected(L, resume, L->top - nargs); + if (status != 0) { /* error? */ + L->status = cast_byte(status); /* mark thread as `dead' */ + luaD_seterrorobj(L, status, L->top); + L->ci->top = L->top; + } + else { + lua_assert(L->nCcalls == L->baseCcalls); + status = L->status; + } + --L->nCcalls; + lua_unlock(L); + return status; +} + + +LUA_API int lua_yield (lua_State *L, int nresults) { + luai_userstateyield(L, nresults); + lua_lock(L); + if (L->nCcalls > L->baseCcalls) + luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); + L->base = L->top - nresults; /* protect stack slots below */ + L->status = LUA_YIELD; + lua_unlock(L); + return -1; +} + + +int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + unsigned short oldnCcalls = L->nCcalls; + ptrdiff_t old_ci = saveci(L, L->ci); + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (status != 0) { /* an error occurred? */ + StkId oldtop = restorestack(L, old_top); + luaF_close(L, oldtop); /* close eventual pending closures */ + luaD_seterrorobj(L, status, oldtop); + L->nCcalls = oldnCcalls; + L->ci = restoreci(L, old_ci); + L->base = L->ci->base; + L->savedpc = L->ci->savedpc; + L->allowhook = old_allowhooks; + restore_stack_limit(L); + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to `f_parser' */ + ZIO *z; + Mbuffer buff; /* buffer to be used by the scanner */ + const char *name; +}; + +static void f_parser (lua_State *L, void *ud) { + int i; + Proto *tf; + Closure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = luaZ_lookahead(p->z); + luaC_checkGC(L); + tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, + &p->buff, p->name); + cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); + cl->l.p = tf; + for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ + cl->l.upvals[i] = luaF_newupval(L); + setclvalue(L, L->top, cl); + incr_top(L); +} + + +int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { + struct SParser p; + int status; + p.z = z; p.name = name; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + luaZ_freebuffer(L, &p.buff); + return status; +} + + diff --git a/src/server/game/LuaEngine/lua_src/ldo.h b/src/server/game/LuaEngine/lua_src/ldo.h new file mode 100644 index 0000000000..98fddac59f --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ldo.h @@ -0,0 +1,57 @@ +/* +** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +#define luaD_checkstack(L,n) \ + if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ + luaD_growstack(L, n); \ + else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); + + +#define incr_top(L) {luaD_checkstack(L,1); L->top++;} + +#define savestack(L,p) ((char *)(p) - (char *)L->stack) +#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) + +#define saveci(L,p) ((char *)(p) - (char *)L->base_ci) +#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n))) + + +/* results from luaD_precall */ +#define PCRLUA 0 /* initiated a call to a Lua function */ +#define PCRC 1 /* did a call to a C function */ +#define PCRYIELD 2 /* C funtion yielded */ + + +/* type of protected functions, to be ran by `runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); +LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); +LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); +LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize); +LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); +LUAI_FUNC void luaD_growstack (lua_State *L, int n); + +LUAI_FUNC void luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); + +#endif + diff --git a/src/server/game/LuaEngine/lua_src/ldump.c b/src/server/game/LuaEngine/lua_src/ldump.c new file mode 100644 index 0000000000..c9d3d4870f --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ldump.c @@ -0,0 +1,164 @@ +/* +** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include + +#define ldump_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lundump.h" + +typedef struct { + lua_State* L; + lua_Writer writer; + void* data; + int strip; + int status; +} DumpState; + +#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) +#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) + +static void DumpBlock(const void* b, size_t size, DumpState* D) +{ + if (D->status==0) + { + lua_unlock(D->L); + D->status=(*D->writer)(D->L,b,size,D->data); + lua_lock(D->L); + } +} + +static void DumpChar(int y, DumpState* D) +{ + char x=(char)y; + DumpVar(x,D); +} + +static void DumpInt(int x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpNumber(lua_Number x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpVector(const void* b, int n, size_t size, DumpState* D) +{ + DumpInt(n,D); + DumpMem(b,n,size,D); +} + +static void DumpString(const TString* s, DumpState* D) +{ + if (s==NULL || getstr(s)==NULL) + { + size_t size=0; + DumpVar(size,D); + } + else + { + size_t size=s->tsv.len+1; /* include trailing '\0' */ + DumpVar(size,D); + DumpBlock(getstr(s),size,D); + } +} + +#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D); + +static void DumpConstants(const Proto* f, DumpState* D) +{ + int i,n=f->sizek; + DumpInt(n,D); + for (i=0; ik[i]; + DumpChar(ttype(o),D); + switch (ttype(o)) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + DumpChar(bvalue(o),D); + break; + case LUA_TNUMBER: + DumpNumber(nvalue(o),D); + break; + case LUA_TSTRING: + DumpString(rawtsvalue(o),D); + break; + default: + lua_assert(0); /* cannot happen */ + break; + } + } + n=f->sizep; + DumpInt(n,D); + for (i=0; ip[i],f->source,D); +} + +static void DumpDebug(const Proto* f, DumpState* D) +{ + int i,n; + n= (D->strip) ? 0 : f->sizelineinfo; + DumpVector(f->lineinfo,n,sizeof(int),D); + n= (D->strip) ? 0 : f->sizelocvars; + DumpInt(n,D); + for (i=0; ilocvars[i].varname,D); + DumpInt(f->locvars[i].startpc,D); + DumpInt(f->locvars[i].endpc,D); + } + n= (D->strip) ? 0 : f->sizeupvalues; + DumpInt(n,D); + for (i=0; iupvalues[i],D); +} + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D) +{ + DumpString((f->source==p || D->strip) ? NULL : f->source,D); + DumpInt(f->linedefined,D); + DumpInt(f->lastlinedefined,D); + DumpChar(f->nups,D); + DumpChar(f->numparams,D); + DumpChar(f->is_vararg,D); + DumpChar(f->maxstacksize,D); + DumpCode(f,D); + DumpConstants(f,D); + DumpDebug(f,D); +} + +static void DumpHeader(DumpState* D) +{ + char h[LUAC_HEADERSIZE]; + luaU_header(h); + DumpBlock(h,LUAC_HEADERSIZE,D); +} + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) +{ + DumpState D; + D.L=L; + D.writer=w; + D.data=data; + D.strip=strip; + D.status=0; + DumpHeader(&D); + DumpFunction(f,NULL,&D); + return D.status; +} diff --git a/src/server/game/LuaEngine/lua_src/lfunc.c b/src/server/game/LuaEngine/lua_src/lfunc.c new file mode 100644 index 0000000000..813e88f583 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lfunc.c @@ -0,0 +1,174 @@ +/* +** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + + +#include + +#define lfunc_c +#define LUA_CORE + +#include "lua.h" + +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->c.isC = 1; + c->c.env = e; + c->c.nupvalues = cast_byte(nelems); + return c; +} + + +Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->l.isC = 0; + c->l.env = e; + c->l.nupvalues = cast_byte(nelems); + while (nelems--) c->l.upvals[nelems] = NULL; + return c; +} + + +UpVal *luaF_newupval (lua_State *L) { + UpVal *uv = luaM_new(L, UpVal); + luaC_link(L, obj2gco(uv), LUA_TUPVAL); + uv->v = &uv->u.value; + setnilvalue(uv->v); + return uv; +} + + +UpVal *luaF_findupval (lua_State *L, StkId level) { + global_State *g = G(L); + GCObject **pp = &L->openupval; + UpVal *p; + UpVal *uv; + while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) { + lua_assert(p->v != &p->u.value); + if (p->v == level) { /* found a corresponding upvalue? */ + if (isdead(g, obj2gco(p))) /* is it dead? */ + changewhite(obj2gco(p)); /* ressurect it */ + return p; + } + pp = &p->next; + } + uv = luaM_new(L, UpVal); /* not found: create a new one */ + uv->tt = LUA_TUPVAL; + uv->marked = luaC_white(g); + uv->v = level; /* current value lives in the stack */ + uv->next = *pp; /* chain it in the proper position */ + *pp = obj2gco(uv); + uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ + uv->u.l.next = g->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + g->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + return uv; +} + + +static void unlinkupval (UpVal *uv) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ + uv->u.l.prev->u.l.next = uv->u.l.next; +} + + +void luaF_freeupval (lua_State *L, UpVal *uv) { + if (uv->v != &uv->u.value) /* is it open? */ + unlinkupval(uv); /* remove from open list */ + luaM_free(L, uv); /* free upvalue */ +} + + +void luaF_close (lua_State *L, StkId level) { + UpVal *uv; + global_State *g = G(L); + while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { + GCObject *o = obj2gco(uv); + lua_assert(!isblack(o) && uv->v != &uv->u.value); + L->openupval = uv->next; /* remove from `open' list */ + if (isdead(g, o)) + luaF_freeupval(L, uv); /* free upvalue */ + else { + unlinkupval(uv); + setobj(L, &uv->u.value, uv->v); + uv->v = &uv->u.value; /* now current value lives here */ + luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ + } + } +} + + +Proto *luaF_newproto (lua_State *L) { + Proto *f = luaM_new(L, Proto); + luaC_link(L, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->code, f->sizecode, Instruction); + luaM_freearray(L, f->p, f->sizep, Proto *); + luaM_freearray(L, f->k, f->sizek, TValue); + luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); + luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); + luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); + luaM_free(L, f); +} + + +void luaF_freeclosure (lua_State *L, Closure *c) { + int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) : + sizeLclosure(c->l.nupvalues); + luaM_freemem(L, c, size); +} + + +/* +** Look for n-th local variable at line `line' in function `func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + diff --git a/src/server/game/LuaEngine/lua_src/lfunc.h b/src/server/game/LuaEngine/lua_src/lfunc.h new file mode 100644 index 0000000000..a68cf5151c --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lfunc.h @@ -0,0 +1,34 @@ +/* +** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + + +#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ + cast(int, sizeof(TValue)*((n)-1))) + +#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ + cast(int, sizeof(TValue *)*((n)-1))) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC UpVal *luaF_newupval (lua_State *L); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_close (lua_State *L, StkId level); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c); +LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lgc.c b/src/server/game/LuaEngine/lua_src/lgc.c new file mode 100644 index 0000000000..e909c79a96 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lgc.c @@ -0,0 +1,710 @@ +/* +** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#include + +#define lgc_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#define GCSTEPSIZE 1024u +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 10 +#define GCFINALIZECOST 100 + + +#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) + +#define makewhite(g,x) \ + ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) + +#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) + +#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) + + +#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) +#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) + + +#define KEYWEAK bitmask(KEYWEAKBIT) +#define VALUEWEAK bitmask(VALUEWEAKBIT) + + + +#define markvalue(g,o) { checkconsistency(o); \ + if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } + +#define markobject(g,t) { if (iswhite(obj2gco(t))) \ + reallymarkobject(g, obj2gco(t)); } + + +#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) + + +static void removeentry (Node *n) { + lua_assert(ttisnil(gval(n))); + if (iscollectable(gkey(n))) + setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ +} + + +static void reallymarkobject (global_State *g, GCObject *o) { + lua_assert(iswhite(o) && !isdead(g, o)); + white2gray(o); + switch (o->gch.tt) { + case LUA_TSTRING: { + return; + } + case LUA_TUSERDATA: { + Table *mt = gco2u(o)->metatable; + gray2black(o); /* udata are never gray */ + if (mt) markobject(g, mt); + markobject(g, gco2u(o)->env); + return; + } + case LUA_TUPVAL: { + UpVal *uv = gco2uv(o); + markvalue(g, uv->v); + if (uv->v == &uv->u.value) /* closed? */ + gray2black(o); /* open upvalues are never black */ + return; + } + case LUA_TFUNCTION: { + gco2cl(o)->c.gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTABLE: { + gco2h(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTHREAD: { + gco2th(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TPROTO: { + gco2p(o)->gclist = g->gray; + g->gray = o; + break; + } + default: lua_assert(0); + } +} + + +static void marktmu (global_State *g) { + GCObject *u = g->tmudata; + if (u) { + do { + u = u->gch.next; + makewhite(g, u); /* may be marked, if left from previous GC */ + reallymarkobject(g, u); + } while (u != g->tmudata); + } +} + + +/* move `dead' udata that need finalization to list `tmudata' */ +size_t luaC_separateudata (lua_State *L, int all) { + global_State *g = G(L); + size_t deadmem = 0; + GCObject **p = &g->mainthread->next; + GCObject *curr; + while ((curr = *p) != NULL) { + if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) + p = &curr->gch.next; /* don't bother with them */ + else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { + markfinalized(gco2u(curr)); /* don't need finalization */ + p = &curr->gch.next; + } + else { /* must call its gc method */ + deadmem += sizeudata(gco2u(curr)); + markfinalized(gco2u(curr)); + *p = curr->gch.next; + /* link `curr' at the end of `tmudata' list */ + if (g->tmudata == NULL) /* list is empty? */ + g->tmudata = curr->gch.next = curr; /* creates a circular list */ + else { + curr->gch.next = g->tmudata->gch.next; + g->tmudata->gch.next = curr; + g->tmudata = curr; + } + } + } + return deadmem; +} + + +static int traversetable (global_State *g, Table *h) { + int i; + int weakkey = 0; + int weakvalue = 0; + const TValue *mode; + if (h->metatable) + markobject(g, h->metatable); + mode = gfasttm(g, h->metatable, TM_MODE); + if (mode && ttisstring(mode)) { /* is there a weak mode? */ + weakkey = (strchr(svalue(mode), 'k') != NULL); + weakvalue = (strchr(svalue(mode), 'v') != NULL); + if (weakkey || weakvalue) { /* is really weak? */ + h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ + h->marked |= cast_byte((weakkey << KEYWEAKBIT) | + (weakvalue << VALUEWEAKBIT)); + h->gclist = g->weak; /* must be cleared after GC, ... */ + g->weak = obj2gco(h); /* ... so put in the appropriate list */ + } + } + if (weakkey && weakvalue) return 1; + if (!weakvalue) { + i = h->sizearray; + while (i--) + markvalue(g, &h->array[i]); + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); + if (ttisnil(gval(n))) + removeentry(n); /* remove empty entries */ + else { + lua_assert(!ttisnil(gkey(n))); + if (!weakkey) markvalue(g, gkey(n)); + if (!weakvalue) markvalue(g, gval(n)); + } + } + return weakkey || weakvalue; +} + + +/* +** All marks are conditional because a GC may happen while the +** prototype is still being created +*/ +static void traverseproto (global_State *g, Proto *f) { + int i; + if (f->source) stringmark(f->source); + for (i=0; isizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i=0; isizeupvalues; i++) { /* mark upvalue names */ + if (f->upvalues[i]) + stringmark(f->upvalues[i]); + } + for (i=0; isizep; i++) { /* mark nested protos */ + if (f->p[i]) + markobject(g, f->p[i]); + } + for (i=0; isizelocvars; i++) { /* mark local-variable names */ + if (f->locvars[i].varname) + stringmark(f->locvars[i].varname); + } +} + + + +static void traverseclosure (global_State *g, Closure *cl) { + markobject(g, cl->c.env); + if (cl->c.isC) { + int i; + for (i=0; ic.nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->c.upvalue[i]); + } + else { + int i; + lua_assert(cl->l.nupvalues == cl->l.p->nups); + markobject(g, cl->l.p); + for (i=0; il.nupvalues; i++) /* mark its upvalues */ + markobject(g, cl->l.upvals[i]); + } +} + + +static void checkstacksizes (lua_State *L, StkId max) { + int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ + int s_used = cast_int(max - L->stack); /* part of stack in use */ + if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ + return; /* do not touch the stacks */ + if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) + luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ + condhardstacktests(luaD_reallocCI(L, ci_used + 1)); + if (4*s_used < L->stacksize && + 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) + luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ + condhardstacktests(luaD_reallocstack(L, s_used)); +} + + +static void traversestack (global_State *g, lua_State *l) { + StkId o, lim; + CallInfo *ci; + markvalue(g, gt(l)); + lim = l->top; + for (ci = l->base_ci; ci <= l->ci; ci++) { + lua_assert(ci->top <= l->stack_last); + if (lim < ci->top) lim = ci->top; + } + for (o = l->stack; o < l->top; o++) + markvalue(g, o); + for (; o <= lim; o++) + setnilvalue(o); + checkstacksizes(l, lim); +} + + +/* +** traverse one gray object, turning it to black. +** Returns `quantity' traversed. +*/ +static l_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + lua_assert(isgray(o)); + gray2black(o); + switch (o->gch.tt) { + case LUA_TTABLE: { + Table *h = gco2h(o); + g->gray = h->gclist; + if (traversetable(g, h)) /* table is weak? */ + black2gray(o); /* keep it gray */ + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * sizenode(h); + } + case LUA_TFUNCTION: { + Closure *cl = gco2cl(o); + g->gray = cl->c.gclist; + traverseclosure(g, cl); + return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : + sizeLclosure(cl->l.nupvalues); + } + case LUA_TTHREAD: { + lua_State *th = gco2th(o); + g->gray = th->gclist; + th->gclist = g->grayagain; + g->grayagain = o; + black2gray(o); + traversestack(g, th); + return sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->size_ci; + } + case LUA_TPROTO: { + Proto *p = gco2p(o); + g->gray = p->gclist; + traverseproto(g, p); + return sizeof(Proto) + sizeof(Instruction) * p->sizecode + + sizeof(Proto *) * p->sizep + + sizeof(TValue) * p->sizek + + sizeof(int) * p->sizelineinfo + + sizeof(LocVar) * p->sizelocvars + + sizeof(TString *) * p->sizeupvalues; + } + default: lua_assert(0); return 0; + } +} + + +static size_t propagateall (global_State *g) { + size_t m = 0; + while (g->gray) m += propagatemark(g); + return m; +} + + +/* +** The next function tells whether a key or value can be cleared from +** a weak table. Non-collectable objects are never removed from weak +** tables. Strings behave as `values', so are never removed too. for +** other objects: if really collected, cannot keep them; for userdata +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (const TValue *o, int iskey) { + if (!iscollectable(o)) return 0; + if (ttisstring(o)) { + stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ + return 0; + } + return iswhite(gcvalue(o)) || + (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +} + + +/* +** clear collected entries from weaktables +*/ +static void cleartable (GCObject *l) { + while (l) { + Table *h = gco2h(l); + int i = h->sizearray; + lua_assert(testbit(h->marked, VALUEWEAKBIT) || + testbit(h->marked, KEYWEAKBIT)); + if (testbit(h->marked, VALUEWEAKBIT)) { + while (i--) { + TValue *o = &h->array[i]; + if (iscleared(o, 0)) /* value was collected? */ + setnilvalue(o); /* remove value */ + } + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + if (!ttisnil(gval(n)) && /* non-empty entry? */ + (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { + setnilvalue(gval(n)); /* remove value ... */ + removeentry(n); /* remove entry from table */ + } + } + l = h->gclist; + } +} + + +static void freeobj (lua_State *L, GCObject *o) { + switch (o->gch.tt) { + case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; + case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; + case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; + case LUA_TTABLE: luaH_free(L, gco2h(o)); break; + case LUA_TTHREAD: { + lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); + luaE_freethread(L, gco2th(o)); + break; + } + case LUA_TSTRING: { + G(L)->strt.nuse--; + luaM_freemem(L, o, sizestring(gco2ts(o))); + break; + } + case LUA_TUSERDATA: { + luaM_freemem(L, o, sizeudata(gco2u(o))); + break; + } + default: lua_assert(0); + } +} + + + +#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) + + +static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { + GCObject *curr; + global_State *g = G(L); + int deadmask = otherwhite(g); + while ((curr = *p) != NULL && count-- > 0) { + if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ + sweepwholelist(L, &gco2th(curr)->openupval); + if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ + lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); + makewhite(g, curr); /* make it white (for next cycle) */ + p = &curr->gch.next; + } + else { /* must erase `curr' */ + lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); + *p = curr->gch.next; + if (curr == g->rootgc) /* is the first element of the list? */ + g->rootgc = curr->gch.next; /* adjust first */ + freeobj(L, curr); + } + } + return p; +} + + +static void checkSizes (lua_State *L) { + global_State *g = G(L); + /* check size of string hash */ + if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && + g->strt.size > MINSTRTABSIZE*2) + luaS_resize(L, g->strt.size/2); /* table is too big */ + /* check size of buffer */ + if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ + size_t newsize = luaZ_sizebuffer(&g->buff) / 2; + luaZ_resizebuffer(L, &g->buff, newsize); + } +} + + +static void GCTM (lua_State *L) { + global_State *g = G(L); + GCObject *o = g->tmudata->gch.next; /* get first element */ + Udata *udata = rawgco2u(o); + const TValue *tm; + /* remove udata from `tmudata' */ + if (o == g->tmudata) /* last element? */ + g->tmudata = NULL; + else + g->tmudata->gch.next = udata->uv.next; + udata->uv.next = g->mainthread->next; /* return it to `root' list */ + g->mainthread->next = o; + makewhite(g, o); + tm = fasttm(L, udata->uv.metatable, TM_GC); + if (tm != NULL) { + lu_byte oldah = L->allowhook; + lu_mem oldt = g->GCthreshold; + L->allowhook = 0; /* stop debug hooks during GC tag method */ + g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ + setobj2s(L, L->top, tm); + setuvalue(L, L->top+1, udata); + L->top += 2; + luaD_call(L, L->top - 2, 0); + L->allowhook = oldah; /* restore hooks */ + g->GCthreshold = oldt; /* restore threshold */ + } +} + + +/* +** Call all GC tag methods +*/ +void luaC_callGCTM (lua_State *L) { + while (G(L)->tmudata) + GCTM(L); +} + + +void luaC_freeall (lua_State *L) { + global_State *g = G(L); + int i; + g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ + sweepwholelist(L, &g->rootgc); + for (i = 0; i < g->strt.size; i++) /* free all string lists */ + sweepwholelist(L, &g->strt.hash[i]); +} + + +static void markmt (global_State *g) { + int i; + for (i=0; imt[i]) markobject(g, g->mt[i]); +} + + +/* mark root set */ +static void markroot (lua_State *L) { + global_State *g = G(L); + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + markobject(g, g->mainthread); + /* make global table be traversed before main stack */ + markvalue(g, gt(g->mainthread)); + markvalue(g, registry(L)); + markmt(g); + g->gcstate = GCSpropagate; +} + + +static void remarkupvals (global_State *g) { + UpVal *uv; + for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + if (isgray(obj2gco(uv))) + markvalue(g, uv->v); + } +} + + +static void atomic (lua_State *L) { + global_State *g = G(L); + size_t udsize; /* total size of userdata to be finalized */ + /* remark occasional upvalues of (maybe) dead threads */ + remarkupvals(g); + /* traverse objects cautch by write barrier and by 'remarkupvals' */ + propagateall(g); + /* remark weak tables */ + g->gray = g->weak; + g->weak = NULL; + lua_assert(!iswhite(obj2gco(g->mainthread))); + markobject(g, L); /* mark running thread */ + markmt(g); /* mark basic metatables (again) */ + propagateall(g); + /* remark gray again */ + g->gray = g->grayagain; + g->grayagain = NULL; + propagateall(g); + udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ + marktmu(g); /* mark `preserved' userdata */ + udsize += propagateall(g); /* remark, to propagate `preserveness' */ + cleartable(g->weak); /* remove collected objects from weak tables */ + /* flip current white */ + g->currentwhite = cast_byte(otherwhite(g)); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gcstate = GCSsweepstring; + g->estimate = g->totalbytes - udsize; /* first estimate */ +} + + +static l_mem singlestep (lua_State *L) { + global_State *g = G(L); + /*lua_checkmemory(L);*/ + switch (g->gcstate) { + case GCSpause: { + markroot(L); /* start a new collection */ + return 0; + } + case GCSpropagate: { + if (g->gray) + return propagatemark(g); + else { /* no more `gray' objects */ + atomic(L); /* finish mark phase */ + return 0; + } + } + case GCSsweepstring: { + lu_mem old = g->totalbytes; + sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); + if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ + g->gcstate = GCSsweep; /* end sweep-string phase */ + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPCOST; + } + case GCSsweep: { + lu_mem old = g->totalbytes; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); + if (*g->sweepgc == NULL) { /* nothing more to sweep? */ + checkSizes(L); + g->gcstate = GCSfinalize; /* end sweep phase */ + } + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPMAX*GCSWEEPCOST; + } + case GCSfinalize: { + if (g->tmudata) { + GCTM(L); + if (g->estimate > GCFINALIZECOST) + g->estimate -= GCFINALIZECOST; + return GCFINALIZECOST; + } + else { + g->gcstate = GCSpause; /* end collection */ + g->gcdept = 0; + return 0; + } + } + default: lua_assert(0); return 0; + } +} + + +void luaC_step (lua_State *L) { + global_State *g = G(L); + l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; + if (lim == 0) + lim = (MAX_LUMEM-1)/2; /* no limit */ + g->gcdept += g->totalbytes - g->GCthreshold; + do { + lim -= singlestep(L); + if (g->gcstate == GCSpause) + break; + } while (lim > 0); + if (g->gcstate != GCSpause) { + if (g->gcdept < GCSTEPSIZE) + g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ + else { + g->gcdept -= GCSTEPSIZE; + g->GCthreshold = g->totalbytes; + } + } + else { + setthreshold(g); + } +} + + +void luaC_fullgc (lua_State *L) { + global_State *g = G(L); + if (g->gcstate <= GCSpropagate) { + /* reset sweep marks to sweep all elements (returning them to white) */ + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + /* reset other collector lists */ + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->gcstate = GCSsweepstring; + } + lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); + /* finish any pending sweep phase */ + while (g->gcstate != GCSfinalize) { + lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); + singlestep(L); + } + markroot(L); + while (g->gcstate != GCSpause) { + singlestep(L); + } + setthreshold(g); +} + + +void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + lua_assert(ttype(&o->gch) != LUA_TTABLE); + /* must keep invariant? */ + if (g->gcstate == GCSpropagate) + reallymarkobject(g, v); /* restore invariant */ + else /* don't mind */ + makewhite(g, o); /* mark as white just to avoid other barriers */ +} + + +void luaC_barrierback (lua_State *L, Table *t) { + global_State *g = G(L); + GCObject *o = obj2gco(t); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + black2gray(o); /* make table gray (again) */ + t->gclist = g->grayagain; + g->grayagain = o; +} + + +void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { + global_State *g = G(L); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + + +void luaC_linkupval (lua_State *L, UpVal *uv) { + global_State *g = G(L); + GCObject *o = obj2gco(uv); + o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ + g->rootgc = o; + if (isgray(o)) { + if (g->gcstate == GCSpropagate) { + gray2black(o); /* closed upvalues need barrier */ + luaC_barrier(L, uv, uv->v); + } + else { /* sweep phase: sweep it (turning it into white) */ + makewhite(g, o); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + } + } +} + diff --git a/src/server/game/LuaEngine/lua_src/lgc.h b/src/server/game/LuaEngine/lua_src/lgc.h new file mode 100644 index 0000000000..5a8dc605b3 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lgc.h @@ -0,0 +1,110 @@ +/* +** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include "lobject.h" + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpause 0 +#define GCSpropagate 1 +#define GCSsweepstring 2 +#define GCSsweep 3 +#define GCSfinalize 4 + + +/* +** some userful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) +#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) +#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) +#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) + + + +/* +** Layout for bit use in `marked' field: +** bit 0 - object is white (type 0) +** bit 1 - object is white (type 1) +** bit 2 - object is black +** bit 3 - for userdata: has been finalized +** bit 3 - for tables: has weak keys +** bit 4 - for tables: has weak values +** bit 5 - object is fixed (should not be collected) +** bit 6 - object is "super" fixed (only the main thread) +*/ + + +#define WHITE0BIT 0 +#define WHITE1BIT 1 +#define BLACKBIT 2 +#define FINALIZEDBIT 3 +#define KEYWEAKBIT 3 +#define VALUEWEAKBIT 4 +#define FIXEDBIT 5 +#define SFIXEDBIT 6 +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define isblack(x) testbit((x)->gch.marked, BLACKBIT) +#define isgray(x) (!isblack(x) && !iswhite(x)) + +#define otherwhite(g) (g->currentwhite ^ WHITEBITS) +#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) + +#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) +#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) + + +#define luaC_checkGC(L) { \ + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ + if (G(L)->totalbytes >= G(L)->GCthreshold) \ + luaC_step(L); } + + +#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),gcvalue(v)); } + +#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \ + luaC_barrierback(L,t); } + +#define luaC_objbarrier(L,p,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),obj2gco(o)); } + +#define luaC_objbarriert(L,t,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } + +LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); +LUAI_FUNC void luaC_callGCTM (lua_State *L); +LUAI_FUNC void luaC_freeall (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_fullgc (lua_State *L); +LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); +LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv); +LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t); + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/linit.c b/src/server/game/LuaEngine/lua_src/linit.c new file mode 100644 index 0000000000..c1f90dfab7 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/linit.c @@ -0,0 +1,38 @@ +/* +** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ +** Initialization of libraries for lua.c +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + +#include "lua.h" + +#include "lualib.h" +#include "lauxlib.h" + + +static const luaL_Reg lualibs[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {NULL, NULL} +}; + + +LUALIB_API void luaL_openlibs (lua_State *L) { + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } +} + diff --git a/src/server/game/LuaEngine/lua_src/liolib.c b/src/server/game/LuaEngine/lua_src/liolib.c new file mode 100644 index 0000000000..649f9a5951 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/liolib.c @@ -0,0 +1,556 @@ +/* +** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define liolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +#define IO_INPUT 1 +#define IO_OUTPUT 2 + + +static const char *const fnames[] = {"input", "output"}; + + +static int pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + else + lua_pushfstring(L, "%s", strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static void fileerror (lua_State *L, int arg, const char *filename) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, arg, lua_tostring(L, -1)); +} + + +#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + + +static int io_type (lua_State *L) { + void *ud; + luaL_checkany(L, 1); + ud = lua_touserdata(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) + lua_pushnil(L); /* not a file */ + else if (*((FILE **)ud) == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L) { + FILE **f = tofilep(L); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, LUA_FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +/* +** function to close 'popen' files +*/ +static int io_pclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = lua_pclose(L, *p); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = (fclose(*p) == 0); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +static int aux_close (lua_State *L) { + lua_getfenv(L, 1); + lua_getfield(L, -1, "__close"); + return (lua_tocfunction(L, -1))(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); + tofile(L); /* make sure argument is a file */ + return aux_close(L); +} + + +static int io_gc (lua_State *L) { + FILE *f = *tofilep(L); + /* ignore closed files */ + if (f != NULL) + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + FILE *f = *tofilep(L); + if (f == NULL) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", f); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +/* +** this function has a separated environment, which defines the +** correct __close for 'popen' files +*/ +static int io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = lua_popen(L, filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, int findex) { + FILE *f; + lua_rawgeti(L, LUA_ENVIRONINDEX, findex); + f = *(FILE **)lua_touserdata(L, -1); + if (f == NULL) + luaL_error(L, "standard %s file is closed", fnames[findex - 1]); + return f; +} + + +static int g_iofile (lua_State *L, int f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) + fileerror(L, 1, filename); + } + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawseti(L, LUA_ENVIRONINDEX, f); + } + /* return current value */ + lua_rawgeti(L, LUA_ENVIRONINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int toclose) { + lua_pushvalue(L, idx); + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 2); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + /* will iterate over default input */ + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + if (*pf == NULL) + fileerror(L, 1, filename); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else { + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (l == 0 || p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_objlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return pushresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); + int sucess; + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + sucess = read_line(L, f); + if (ferror(f)) + return luaL_error(L, "%s", strerror(errno)); + if (sucess) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + long offset = luaL_optlong(L, 3, 0); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], sz); + return pushresult(L, res == 0, NULL); +} + + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L)) == 0, NULL); +} + + +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_Reg flib[] = { + {"close", io_close}, + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, + {"seek", f_seek}, + {"setvbuf", f_setvbuf}, + {"write", f_write}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ + lua_pushvalue(L, -1); /* push metatable */ + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_register(L, NULL, flib); /* file methods */ +} + + +static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { + *newfile(L) = f; + if (k > 0) { + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_ENVIRONINDEX, k); + } + lua_pushvalue(L, -2); /* copy environment */ + lua_setfenv(L, -2); /* set it */ + lua_setfield(L, -3, fname); +} + + +static void newfenv (lua_State *L, lua_CFunction cls) { + lua_createtable(L, 0, 1); + lua_pushcfunction(L, cls); + lua_setfield(L, -2, "__close"); +} + + +LUALIB_API int luaopen_io (lua_State *L) { + createmeta(L); + /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ + newfenv(L, io_fclose); + lua_replace(L, LUA_ENVIRONINDEX); + /* open library */ + luaL_register(L, LUA_IOLIBNAME, iolib); + /* create (and set) default files */ + newfenv(L, io_noclose); /* close function for default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, 0, "stderr"); + lua_pop(L, 1); /* pop environment for default files */ + lua_getfield(L, -1, "popen"); + newfenv(L, io_pclose); /* create environment for 'popen' */ + lua_setfenv(L, -2); /* set fenv for 'popen' */ + lua_pop(L, 1); /* pop 'popen' */ + return 1; +} + diff --git a/src/server/game/LuaEngine/lua_src/llex.c b/src/server/game/LuaEngine/lua_src/llex.c new file mode 100644 index 0000000000..88c6790c07 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/llex.c @@ -0,0 +1,463 @@ +/* +** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define llex_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "..", "...", "==", ">=", "<=", "~=", + "", "", "", "", + NULL +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (b->n + 1 > b->buffsize) { + size_t newsize; + if (b->buffsize >= MAX_SIZET/2) + luaX_lexerror(ls, "lexical element too long", 0); + newsize = b->buffsize * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[b->n++] = cast(char, c); +} + + +void luaX_init (lua_State *L) { + int i; + for (i=0; itsv.reserved = cast_byte(i+1); /* reserved word */ + } +} + + +#define MAXSRC 80 + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { + lua_assert(token == cast(unsigned char, token)); + return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) : + luaO_pushfstring(ls->L, "%c", token); + } + else + return luaX_tokens[token-FIRST_RESERVED]; +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + save(ls, '\0'); + return luaZ_buffer(ls->buff); + default: + return luaX_token2str(ls, token); + } +} + + +void luaX_lexerror (LexState *ls, const char *msg, int token) { + char buff[MAXSRC]; + luaO_chunkid(buff, getstr(ls->source), MAXSRC); + msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + if (token) + luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +void luaX_syntaxerror (LexState *ls, const char *msg) { + luaX_lexerror(ls, msg, ls->t.token); +} + + +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TString *ts = luaS_newlstr(L, str, l); + TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ + if (ttisnil(o)) { + setbvalue(o, 1); /* make sure `str' will not be collected */ + luaC_checkGC(L); + } + return ts; +} + + +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip `\n' or `\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip `\n\r' or `\r\n' */ + if (++ls->linenumber >= MAX_INT) + luaX_syntaxerror(ls, "chunk has too many lines"); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) { + ls->decpoint = '.'; + ls->L = L; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ + next(ls); /* read first char */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + + +static int check_next (LexState *ls, const char *set) { + if (!strchr(set, ls->current)) + return 0; + save_and_next(ls); + return 1; +} + + +static void buffreplace (LexState *ls, char from, char to) { + size_t n = luaZ_bufflen(ls->buff); + char *p = luaZ_buffer(ls->buff); + while (n--) + if (p[n] == from) p[n] = to; +} + + +static void trydecpoint (LexState *ls, SemInfo *seminfo) { + /* format error: try to update decimal point separator */ + struct lconv *cv = localeconv(); + char old = ls->decpoint; + ls->decpoint = (cv ? cv->decimal_point[0] : '.'); + buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { + /* format error with correct decimal point: no more options */ + buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ + luaX_lexerror(ls, "malformed number", TK_NUMBER); + } +} + + +/* LUA_NUMBER */ +static void read_numeral (LexState *ls, SemInfo *seminfo) { + lua_assert(isdigit(ls->current)); + do { + save_and_next(ls); + } while (isdigit(ls->current) || ls->current == '.'); + if (check_next(ls, "Ee")) /* `E'? */ + check_next(ls, "+-"); /* optional exponent sign */ + while (isalnum(ls->current) || ls->current == '_') + save_and_next(ls); + save(ls, '\0'); + buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ + trydecpoint(ls, seminfo); /* try to update decimal point separator */ +} + + +static int skip_sep (LexState *ls) { + int count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count : (-count) - 1; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { + int cont = 0; + (void)(cont); /* avoid warnings when `cont' is not used */ + save_and_next(ls); /* skip 2nd `[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, (seminfo) ? "unfinished long string" : + "unfinished long comment", TK_EOS); + break; /* to avoid warnings */ +#if defined(LUA_COMPAT_LSTR) + case '[': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `[' */ + cont++; +#if LUA_COMPAT_LSTR == 1 + if (sep == 0) + luaX_lexerror(ls, "nesting of [[...]] is deprecated", '['); +#endif + } + break; + } +#endif + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `]' */ +#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2 + cont--; + if (sep == 0 && cont >= 0) break; +#endif + goto endloop; + } + break; + } + case '\n': + case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), + luaZ_bufflen(ls->buff) - 2*(2 + sep)); +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); + while (ls->current != del) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, "unfinished string", TK_EOS); + continue; /* to avoid warnings */ + case '\n': + case '\r': + luaX_lexerror(ls, "unfinished string", TK_STRING); + continue; /* to avoid warnings */ + case '\\': { + int c; + next(ls); /* do not save the `\' */ + switch (ls->current) { + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\n': /* go through */ + case '\r': save(ls, '\n'); inclinenumber(ls); continue; + case EOZ: continue; /* will raise an error next loop */ + default: { + if (!isdigit(ls->current)) + save_and_next(ls); /* handles \\, \", \', and \? */ + else { /* \xxx */ + int i = 0; + c = 0; + do { + c = 10*c + (ls->current-'0'); + next(ls); + } while (++i<3 && isdigit(ls->current)); + if (c > UCHAR_MAX) + luaX_lexerror(ls, "escape sequence too large", TK_STRING); + save(ls, c); + } + continue; + } + } + save(ls, c); + next(ls); + continue; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': + case '\r': { + inclinenumber(ls); + continue; + } + case '-': { + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { + int sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(ls, NULL, sep); /* long comment */ + luaZ_resetbuffer(ls->buff); + continue; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); + continue; + } + case '[': { + int sep = skip_sep(ls); + if (sep >= 0) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == -1) return '['; + else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); + } + case '=': { + next(ls); + if (ls->current != '=') return '='; + else { next(ls); return TK_EQ; } + } + case '<': { + next(ls); + if (ls->current != '=') return '<'; + else { next(ls); return TK_LE; } + } + case '>': { + next(ls); + if (ls->current != '=') return '>'; + else { next(ls); return TK_GE; } + } + case '~': { + next(ls); + if (ls->current != '=') return '~'; + else { next(ls); return TK_NE; } + } + case '"': + case '\'': { + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { + save_and_next(ls); + if (check_next(ls, ".")) { + if (check_next(ls, ".")) + return TK_DOTS; /* ... */ + else return TK_CONCAT; /* .. */ + } + else if (!isdigit(ls->current)) return '.'; + else { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + } + case EOZ: { + return TK_EOS; + } + default: { + if (isspace(ls->current)) { + lua_assert(!currIsNewline(ls)); + next(ls); + continue; + } + else if (isdigit(ls->current)) { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + else if (isalpha(ls->current) || ls->current == '_') { + /* identifier or reserved word */ + TString *ts; + do { + save_and_next(ls); + } while (isalnum(ls->current) || ls->current == '_'); + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + if (ts->tsv.reserved > 0) /* reserved word? */ + return ts->tsv.reserved - 1 + FIRST_RESERVED; + else { + seminfo->ts = ts; + return TK_NAME; + } + } + else { + int c = ls->current; + next(ls); + return c; /* single-char tokens (+ - / ...) */ + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +void luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); +} + diff --git a/src/server/game/LuaEngine/lua_src/llex.h b/src/server/game/LuaEngine/lua_src/llex.h new file mode 100644 index 0000000000..a9201cee48 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/llex.h @@ -0,0 +1,81 @@ +/* +** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include "lobject.h" +#include "lzio.h" + + +#define FIRST_RESERVED 257 + +/* maximum length of a reserved word */ +#define TOKEN_LEN (sizeof("function")/sizeof(char)) + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, + TK_NAME, TK_STRING, TK_EOS +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) + + +/* array with token `names' */ +LUAI_DATA const char *const luaX_tokens []; + + +typedef union { + lua_Number r; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token `consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* `FuncState' is private to the parser */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + TString *source; /* current source name */ + char decpoint; /* locale decimal point */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC void luaX_lookahead (LexState *ls); +LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token); +LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/llimits.h b/src/server/game/LuaEngine/lua_src/llimits.h new file mode 100644 index 0000000000..ca8dcb7224 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/llimits.h @@ -0,0 +1,128 @@ +/* +** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $ +** Limits, basic types, and some other `installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include +#include + + +#include "lua.h" + + +typedef LUAI_UINT32 lu_int32; + +typedef LUAI_UMEM lu_mem; + +typedef LUAI_MEM l_mem; + + + +/* chars used as small naturals (so that `char' is reserved for characters) */ +typedef unsigned char lu_byte; + + +#define MAX_SIZET ((size_t)(~(size_t)0)-2) + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) + + +#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ + +/* +** conversion of pointer to integer +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define IntPoint(p) ((unsigned int)(lu_mem)(p)) + + + +/* type to ensure maximum alignment */ +typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; + + +/* result of a `usual argument conversion' over lua_Number */ +typedef LUAI_UACNUMBER l_uacNumber; + + +/* internal assertions for in-house debugging */ +#ifdef lua_assert + +#define check_exp(c,e) (lua_assert(c), (e)) +#define api_check(l,e) lua_assert(e) + +#else + +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define api_check luai_apicheck + +#endif + + +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#endif + + +#ifndef cast +#define cast(t, exp) ((t)(exp)) +#endif + +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) + + + +/* +** type for virtual-machine instructions +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +typedef lu_int32 Instruction; + + + +/* maximum stack for a Lua function */ +#define MAXSTACK 250 + + + +/* minimum size for the string table (must be power of 2) */ +#ifndef MINSTRTABSIZE +#define MINSTRTABSIZE 32 +#endif + + +/* minimum size for string buffer */ +#ifndef LUA_MINBUFFER +#define LUA_MINBUFFER 32 +#endif + + +#ifndef lua_lock +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +#ifndef luai_threadyield +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#ifndef HARDSTACKTESTS +#define condhardstacktests(x) ((void)0) +#else +#define condhardstacktests(x) x +#endif + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lmathlib.c b/src/server/game/LuaEngine/lua_src/lmathlib.c new file mode 100644 index 0000000000..441fbf736c --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lmathlib.c @@ -0,0 +1,263 @@ +/* +** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + + +#include +#include + +#define lmathlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, sinh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, cosh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, tanh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, asin(luaL_checknumber(L, 1))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, acos(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_pushnumber(L, atan(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int math_fmod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_modf (lua_State *L) { + double ip; + double fp = modf(luaL_checknumber(L, 1), &ip); + lua_pushnumber(L, ip); + lua_pushnumber(L, fp); + return 2; +} + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +static int math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + + +static int math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + + +static const luaL_Reg mathlib[] = { + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan2", math_atan2}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cosh", math_cosh}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, + {"floor", math_floor}, + {"fmod", math_fmod}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"pow", math_pow}, + {"rad", math_rad}, + {"random", math_random}, + {"randomseed", math_randomseed}, + {"sinh", math_sinh}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tanh", math_tanh}, + {"tan", math_tan}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUALIB_API int luaopen_math (lua_State *L) { + luaL_register(L, LUA_MATHLIBNAME, mathlib); + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); + lua_pushnumber(L, HUGE_VAL); + lua_setfield(L, -2, "huge"); +#if defined(LUA_COMPAT_MOD) + lua_getfield(L, -1, "fmod"); + lua_setfield(L, -2, "mod"); +#endif + return 1; +} + diff --git a/src/server/game/LuaEngine/lua_src/lmem.c b/src/server/game/LuaEngine/lua_src/lmem.c new file mode 100644 index 0000000000..ae7d8c965f --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lmem.c @@ -0,0 +1,86 @@ +/* +** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + + +#include + +#define lmem_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +/* +** About the realloc function: +** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** (`osize' is the old size, `nsize' is the new size) +** +** Lua ensures that (ptr == NULL) iff (osize == 0). +** +** * frealloc(ud, NULL, 0, x) creates a new block of size `x' +** +** * frealloc(ud, p, x, 0) frees the block `p' +** (in this specific case, frealloc must return NULL). +** particularly, frealloc(ud, NULL, 0, 0) does nothing +** (which is equivalent to free(NULL) in ANSI C) +** +** frealloc returns NULL if it cannot create or reallocate the area +** (any reallocation to an equal or smaller size cannot fail!) +*/ + + + +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, + int limit, const char *errormsg) { + void *newblock; + int newsize; + if (*size >= limit/2) { /* cannot double it? */ + if (*size >= limit) /* cannot grow even a little? */ + luaG_runerror(L, errormsg); + newsize = limit; /* still have at least one free place */ + } + else { + newsize = (*size)*2; + if (newsize < MINSIZEARRAY) + newsize = MINSIZEARRAY; /* minimum size */ + } + newblock = luaM_reallocv(L, block, *size, newsize, size_elems); + *size = newsize; /* update only when everything else is OK */ + return newblock; +} + + +void *luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); + return NULL; /* to avoid warnings */ +} + + + +/* +** generic allocation routine. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + block = (*g->frealloc)(g->ud, block, osize, nsize); + if (block == NULL && nsize > 0) + luaD_throw(L, LUA_ERRMEM); + lua_assert((nsize == 0) == (block == NULL)); + g->totalbytes = (g->totalbytes - osize) + nsize; + return block; +} + diff --git a/src/server/game/LuaEngine/lua_src/lmem.h b/src/server/game/LuaEngine/lua_src/lmem.h new file mode 100644 index 0000000000..7c2dcb3220 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lmem.h @@ -0,0 +1,49 @@ +/* +** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#include + +#include "llimits.h" +#include "lua.h" + +#define MEMERRMSG "not enough memory" + + +#define luaM_reallocv(L,b,on,n,e) \ + ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \ + luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ + luaM_toobig(L)) + +#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) +#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) +#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t)) + +#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t)) +#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) +#define luaM_newvector(L,n,t) \ + cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + if ((nelems)+1 > (size)) \ + ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) + + +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_toobig (lua_State *L); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, + size_t size_elem, int limit, + const char *errormsg); + +#endif + diff --git a/src/server/game/LuaEngine/lua_src/loadlib.c b/src/server/game/LuaEngine/lua_src/loadlib.c new file mode 100644 index 0000000000..6158c5353d --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/loadlib.c @@ -0,0 +1,666 @@ +/* +** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Darwin (Mac OS X), an +** implementation for Windows, and a stub for other systems. +*/ + + +#include +#include + + +#define loadlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +#define LIBPREFIX "LOADLIB: " + +#define POF LUA_POF +#define LIB_FAIL "open" + + +/* error codes for ll_loadfunc */ +#define ERRLIB 1 +#define ERRFUNC 2 + +#define setprogdir(L) ((void)0) + + +static void ll_unloadlib (void *lib); +static void *ll_load (lua_State *L, const char *path); +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); + + + +#if defined(LUA_DL_DLOPEN) +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include + +static void ll_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + void *lib = dlopen(path, RTLD_NOW); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)dlsym(lib, sym); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include + + +#undef setprogdir + +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; + luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void ll_unloadlib (void *lib) { + FreeLibrary((HINSTANCE)lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + HINSTANCE lib = LoadLibraryA(path); + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DYLD) +/* +** {====================================================================== +** Native Mac OS X / Darwin Implementation +** ======================================================================= +*/ + +#include + + +/* Mac appends a `_' before C function names */ +#undef POF +#define POF "_" LUA_POF + + +static void pusherror (lua_State *L) { + const char *err_str; + const char *err_file; + NSLinkEditErrors err; + int err_num; + NSLinkEditError(&err, &err_num, &err_file, &err_str); + lua_pushstring(L, err_str); +} + + +static const char *errorfromcode (NSObjectFileImageReturnCode ret) { + switch (ret) { + case NSObjectFileImageInappropriateFile: + return "file is not a bundle"; + case NSObjectFileImageArch: + return "library is for wrong CPU type"; + case NSObjectFileImageFormat: + return "bad format"; + case NSObjectFileImageAccess: + return "cannot access file"; + case NSObjectFileImageFailure: + default: + return "unable to load library"; + } +} + + +static void ll_unloadlib (void *lib) { + NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); +} + + +static void *ll_load (lua_State *L, const char *path) { + NSObjectFileImage img; + NSObjectFileImageReturnCode ret; + /* this would be a rare case, but prevents crashing if it happens */ + if(!_dyld_present()) { + lua_pushliteral(L, "dyld not present"); + return NULL; + } + ret = NSCreateObjectFileImageFromFile(path, &img); + if (ret == NSObjectFileImageSuccess) { + NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(img); + if (mod == NULL) pusherror(L); + return mod; + } + lua_pushstring(L, errorfromcode(ret)); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); + if (nss == NULL) { + lua_pushfstring(L, "symbol " LUA_QS " not found", sym); + return NULL; + } + return (lua_CFunction)NSAddressOfSymbol(nss); +} + +/* }====================================================== */ + + + +#else +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void ll_unloadlib (void *lib) { + (void)lib; /* to avoid warnings */ +} + + +static void *ll_load (lua_State *L, const char *path) { + (void)path; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + (void)lib; (void)sym; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif + + + +static void **ll_register (lua_State *L, const char *path) { + void **plib; + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ + if (!lua_isnil(L, -1)) /* is there an entry? */ + plib = (void **)lua_touserdata(L, -1); + else { /* no entry yet; create one */ + lua_pop(L, 1); + plib = (void **)lua_newuserdata(L, sizeof(const void *)); + *plib = NULL; + luaL_getmetatable(L, "_LOADLIB"); + lua_setmetatable(L, -2); + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + return plib; +} + + +/* +** __gc tag method: calls library's `ll_unloadlib' function with the lib +** handle +*/ +static int gctm (lua_State *L) { + void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); + if (*lib) ll_unloadlib(*lib); + *lib = NULL; /* mark library as closed */ + return 0; +} + + +static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { + void **reg = ll_register(L, path); + if (*reg == NULL) *reg = ll_load(L, path); + if (*reg == NULL) + return ERRLIB; /* unable to load library */ + else { + lua_CFunction f = ll_sym(L, *reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); + return 0; /* return function */ + } +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = ll_loadfunc(L, path, init); + if (stat == 0) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + lua_pushnil(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return nil, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ + + +static int readable (const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + + +static const char *pushnexttemplate (lua_State *L, const char *path) { + const char *l; + while (*path == *LUA_PATHSEP) path++; /* skip separators */ + if (*path == '\0') return NULL; /* no more templates */ + l = strchr(path, *LUA_PATHSEP); /* find next separator */ + if (l == NULL) l = path + strlen(path); + lua_pushlstring(L, path, l - path); /* template */ + return l; +} + + +static const char *findfile (lua_State *L, const char *name, + const char *pname) { + const char *path; + name = luaL_gsub(L, name, ".", LUA_DIRSEP); + lua_getfield(L, LUA_ENVIRONINDEX, pname); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + lua_pushliteral(L, ""); /* error accumulator */ + while ((path = pushnexttemplate(L, path)) != NULL) { + const char *filename; + filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); + lua_remove(L, -2); /* remove path template */ + if (readable(filename)) /* does file exist and is readable? */ + return filename; /* return that file name */ + lua_pushfstring(L, "\n\tno file " LUA_QS, filename); + lua_remove(L, -2); /* remove file name */ + lua_concat(L, 2); /* add entry to possible error message */ + } + return NULL; /* not found */ +} + + +static void loaderror (lua_State *L, const char *filename) { + luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int loader_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path"); + if (filename == NULL) return 1; /* library not found in this path */ + if (luaL_loadfile(L, filename) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static const char *mkfuncname (lua_State *L, const char *modname) { + const char *funcname; + const char *mark = strchr(modname, *LUA_IGMARK); + if (mark) modname = mark + 1; + funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); + funcname = lua_pushfstring(L, POF"%s", funcname); + lua_remove(L, -2); /* remove 'gsub' result */ + return funcname; +} + + +static int loader_C (lua_State *L) { + const char *funcname; + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath"); + if (filename == NULL) return 1; /* library not found in this path */ + funcname = mkfuncname(L, name); + if (ll_loadfunc(L, filename, funcname) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static int loader_Croot (lua_State *L) { + const char *funcname; + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, p - name); + filename = findfile(L, lua_tostring(L, -1), "cpath"); + if (filename == NULL) return 1; /* root not found */ + funcname = mkfuncname(L, name); + if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { + if (stat != ERRFUNC) loaderror(L, filename); /* real error */ + lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, + name, filename); + return 1; /* function not found */ + } + return 1; +} + + +static int loader_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_ENVIRONINDEX, "preload"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.preload") " must be a table"); + lua_getfield(L, -1, name); + if (lua_isnil(L, -1)) /* not found? */ + lua_pushfstring(L, "\n\tno field package.preload['%s']", name); + return 1; +} + + +static const int sentinel_ = 0; +#define sentinel ((void *)&sentinel_) + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + int i; + lua_settop(L, 1); /* _LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, 2, name); + if (lua_toboolean(L, -1)) { /* is it there? */ + if (lua_touserdata(L, -1) == sentinel) /* check loops */ + luaL_error(L, "loop or previous error loading module " LUA_QS, name); + return 1; /* package is already loaded */ + } + /* else must load it; iterate over available loaders */ + lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.loaders") " must be a table"); + lua_pushliteral(L, ""); /* error message accumulator */ + for (i=1; ; i++) { + lua_rawgeti(L, -2, i); /* get a loader */ + if (lua_isnil(L, -1)) + luaL_error(L, "module " LUA_QS " not found:%s", + name, lua_tostring(L, -2)); + lua_pushstring(L, name); + lua_call(L, 1, 1); /* call it */ + if (lua_isfunction(L, -1)) /* did it find module? */ + break; /* module loaded successfully */ + else if (lua_isstring(L, -1)) /* loader returned error message? */ + lua_concat(L, 2); /* accumulate it */ + else + lua_pop(L, 1); + } + lua_pushlightuserdata(L, sentinel); + lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ + lua_pushstring(L, name); /* pass name as argument to module */ + lua_call(L, 1, 1); /* run loaded module */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ + lua_getfield(L, 2, name); + if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_setfield(L, 2, name); /* _LOADED[name] = true */ + } + return 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** 'module' function +** ======================================================= +*/ + + +static void setfenv (lua_State *L) { + lua_Debug ar; + if (lua_getstack(L, 1, &ar) == 0 || + lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ + lua_iscfunction(L, -1)) + luaL_error(L, LUA_QL("module") " not called from a Lua function"); + lua_pushvalue(L, -2); + lua_setfenv(L, -2); + lua_pop(L, 1); +} + + +static void dooptions (lua_State *L, int n) { + int i; + for (i = 2; i <= n; i++) { + lua_pushvalue(L, i); /* get option (a function) */ + lua_pushvalue(L, -2); /* module */ + lua_call(L, 1, 0); + } +} + + +static void modinit (lua_State *L, const char *modname) { + const char *dot; + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_M"); /* module._M = module */ + lua_pushstring(L, modname); + lua_setfield(L, -2, "_NAME"); + dot = strrchr(modname, '.'); /* look for last dot in module name */ + if (dot == NULL) dot = modname; + else dot++; + /* set _PACKAGE as package name (full module name minus last part) */ + lua_pushlstring(L, modname, dot - modname); + lua_setfield(L, -2, "_PACKAGE"); +} + + +static int ll_module (lua_State *L) { + const char *modname = luaL_checkstring(L, 1); + int loaded = lua_gettop(L) + 1; /* index of _LOADED table */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, loaded, modname); /* get _LOADED[modname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL) + return luaL_error(L, "name conflict for module " LUA_QS, modname); + lua_pushvalue(L, -1); + lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */ + } + /* check whether table already has a _NAME field */ + lua_getfield(L, -1, "_NAME"); + if (!lua_isnil(L, -1)) /* is table an initialized module? */ + lua_pop(L, 1); + else { /* no; initialize it */ + lua_pop(L, 1); + modinit(L, modname); + } + lua_pushvalue(L, -1); + setfenv(L); + dooptions(L, loaded - 1); + return 0; +} + + +static int ll_seeall (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + if (!lua_getmetatable(L, 1)) { + lua_createtable(L, 0, 1); /* create new metatable */ + lua_pushvalue(L, -1); + lua_setmetatable(L, 1); + } + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* mt.__index = _G */ + return 0; +} + + +/* }====================================================== */ + + + +/* auxiliary mark (for internal use) */ +#define AUXMARK "\1" + +static void setpath (lua_State *L, const char *fieldname, const char *envname, + const char *def) { + const char *path = getenv(envname); + if (path == NULL) /* no environment variable? */ + lua_pushstring(L, def); /* use default */ + else { + /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ + path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, + LUA_PATHSEP AUXMARK LUA_PATHSEP); + luaL_gsub(L, path, AUXMARK, def); + lua_remove(L, -2); + } + setprogdir(L); + lua_setfield(L, -2, fieldname); +} + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"seeall", ll_seeall}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"module", ll_module}, + {"require", ll_require}, + {NULL, NULL} +}; + + +static const lua_CFunction loaders[] = + {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; + + +LUALIB_API int luaopen_package (lua_State *L) { + int i; + /* create new type _LOADLIB */ + luaL_newmetatable(L, "_LOADLIB"); + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); + /* create `package' table */ + luaL_register(L, LUA_LOADLIBNAME, pk_funcs); +#if defined(LUA_COMPAT_LOADLIB) + lua_getfield(L, -1, "loadlib"); + lua_setfield(L, LUA_GLOBALSINDEX, "loadlib"); +#endif + lua_pushvalue(L, -1); + lua_replace(L, LUA_ENVIRONINDEX); + /* create `loaders' table */ + lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0); + /* fill it with pre-defined loaders */ + for (i=0; loaders[i] != NULL; i++) { + lua_pushcfunction(L, loaders[i]); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ + setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ + setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" + LUA_EXECDIR "\n" LUA_IGMARK); + lua_setfield(L, -2, "config"); + /* set field `loaded' */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2); + lua_setfield(L, -2, "loaded"); + /* set field `preload' */ + lua_newtable(L); + lua_setfield(L, -2, "preload"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_register(L, NULL, ll_funcs); /* open lib into global table */ + lua_pop(L, 1); + return 1; /* return 'package' table */ +} + diff --git a/src/server/game/LuaEngine/lua_src/lobject.c b/src/server/game/LuaEngine/lua_src/lobject.c new file mode 100644 index 0000000000..4ff50732a4 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lobject.c @@ -0,0 +1,214 @@ +/* +** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#include +#include +#include +#include +#include + +#define lobject_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + + +const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL}; + + +/* +** converts an integer to a "floating point byte", represented as +** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if +** eeeee != 0 and (xxx) otherwise. +*/ +int luaO_int2fb (unsigned int x) { + int e = 0; /* expoent */ + while (x >= 16) { + x = (x+1) >> 1; + e++; + } + if (x < 8) return x; + else return ((e+1) << 3) | (cast_int(x) - 8); +} + + +/* converts back */ +int luaO_fb2int (int x) { + int e = (x >> 3) & 31; + if (e == 0) return x; + else return ((x & 7)+8) << (e - 1); +} + + +int luaO_log2 (unsigned int x) { + static const lu_byte log_2[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = -1; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; + +} + + +int luaO_rawequalObj (const TValue *t1, const TValue *t2) { + if (ttype(t1) != ttype(t2)) return 0; + else switch (ttype(t1)) { + case LUA_TNIL: + return 1; + case LUA_TNUMBER: + return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: + return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ + case LUA_TLIGHTUSERDATA: + return pvalue(t1) == pvalue(t2); + default: + lua_assert(iscollectable(t1)); + return gcvalue(t1) == gcvalue(t2); + } +} + + +int luaO_str2d (const char *s, lua_Number *result) { + char *endptr; + *result = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* conversion failed */ + if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ + *result = cast_num(strtoul(s, &endptr, 16)); + if (*endptr == '\0') return 1; /* most common case */ + while (isspace(cast(unsigned char, *endptr))) endptr++; + if (*endptr != '\0') return 0; /* invalid trailing characters? */ + return 1; +} + + + +static void pushstr (lua_State *L, const char *str) { + setsvalue2s(L, L->top, luaS_new(L, str)); + incr_top(L); +} + + +/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + int n = 1; + pushstr(L, ""); + for (;;) { + const char *e = strchr(fmt, '%'); + if (e == NULL) break; + setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt)); + incr_top(L); + switch (*(e+1)) { + case 's': { + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + pushstr(L, s); + break; + } + case 'c': { + char buff[2]; + buff[0] = cast(char, va_arg(argp, int)); + buff[1] = '\0'; + pushstr(L, buff); + break; + } + case 'd': { + setnvalue(L->top, cast_num(va_arg(argp, int))); + incr_top(L); + break; + } + case 'f': { + setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); + incr_top(L); + break; + } + case 'p': { + char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ + sprintf(buff, "%p", va_arg(argp, void *)); + pushstr(L, buff); + break; + } + case '%': { + pushstr(L, "%"); + break; + } + default: { + char buff[3]; + buff[0] = '%'; + buff[1] = *(e+1); + buff[2] = '\0'; + pushstr(L, buff); + break; + } + } + n += 2; + fmt = e+2; + } + pushstr(L, fmt); + luaV_concat(L, n+1, cast_int(L->top - L->base) - 1); + L->top -= n; + return svalue(L->top - 1); +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + + +void luaO_chunkid (char *out, const char *source, size_t bufflen) { + if (*source == '=') { + strncpy(out, source+1, bufflen); /* remove first char */ + out[bufflen-1] = '\0'; /* ensures null termination */ + } + else { /* out = "source", or "...source" */ + if (*source == '@') { + size_t l; + source++; /* skip the `@' */ + bufflen -= sizeof(" '...' "); + l = strlen(source); + strcpy(out, ""); + if (l > bufflen) { + source += (l-bufflen); /* get last part of file name */ + strcat(out, "..."); + } + strcat(out, source); + } + else { /* out = [string "string"] */ + size_t len = strcspn(source, "\n\r"); /* stop at first newline */ + bufflen -= sizeof(" [string \"...\"] "); + if (len > bufflen) len = bufflen; + strcpy(out, "[string \""); + if (source[len] != '\0') { /* must truncate? */ + strncat(out, source, len); + strcat(out, "..."); + } + else + strcat(out, source); + strcat(out, "\"]"); + } + } +} diff --git a/src/server/game/LuaEngine/lua_src/lobject.h b/src/server/game/LuaEngine/lua_src/lobject.h new file mode 100644 index 0000000000..f1e447ef3b --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lobject.h @@ -0,0 +1,381 @@ +/* +** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include + + +#include "llimits.h" +#include "lua.h" + + +/* tags for values visible from Lua */ +#define LAST_TAG LUA_TTHREAD + +#define NUM_TAGS (LAST_TAG+1) + + +/* +** Extra tags for non-values +*/ +#define LUA_TPROTO (LAST_TAG+1) +#define LUA_TUPVAL (LAST_TAG+2) +#define LUA_TDEADKEY (LAST_TAG+3) + + +/* +** Union of all collectable objects +*/ +typedef union GCObject GCObject; + + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked + + +/* +** Common header in struct form +*/ +typedef struct GCheader { + CommonHeader; +} GCheader; + + + + +/* +** Union of all Lua values +*/ +typedef union { + GCObject *gc; + void *p; + lua_Number n; + int b; +} Value; + + +/* +** Tagged Values +*/ + +#define TValuefields Value value; int tt + +typedef struct lua_TValue { + TValuefields; +} TValue; + + +/* Macros to test type */ +#define ttisnil(o) (ttype(o) == LUA_TNIL) +#define ttisnumber(o) (ttype(o) == LUA_TNUMBER) +#define ttisstring(o) (ttype(o) == LUA_TSTRING) +#define ttistable(o) (ttype(o) == LUA_TTABLE) +#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION) +#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN) +#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) +#define ttisthread(o) (ttype(o) == LUA_TTHREAD) +#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) + +/* Macros to access values */ +#define ttype(o) ((o)->tt) +#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) +#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) +#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) +#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) +#define tsvalue(o) (&rawtsvalue(o)->tsv) +#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u) +#define uvalue(o) (&rawuvalue(o)->uv) +#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) +#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h) +#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b) +#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th) + +#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) + +/* +** for internal debug only +*/ +#define checkconsistency(obj) \ + lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt)) + +#define checkliveness(g,obj) \ + lua_assert(!iscollectable(obj) || \ + ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) + + +/* Macros to set values */ +#define setnilvalue(obj) ((obj)->tt=LUA_TNIL) + +#define setnvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; } + +#define setpvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; } + +#define setbvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; } + +#define setsvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \ + checkliveness(G(L),i_o); } + +#define setuvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \ + checkliveness(G(L),i_o); } + +#define setthvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \ + checkliveness(G(L),i_o); } + +#define setclvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \ + checkliveness(G(L),i_o); } + +#define sethvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \ + checkliveness(G(L),i_o); } + +#define setptvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \ + checkliveness(G(L),i_o); } + + + + +#define setobj(L,obj1,obj2) \ + { const TValue *o2=(obj2); TValue *o1=(obj1); \ + o1->value = o2->value; o1->tt=o2->tt; \ + checkliveness(G(L),o1); } + + +/* +** different types of sets, according to destination +*/ + +/* from stack to (same) stack */ +#define setobjs2s setobj +/* to stack (not from same stack) */ +#define setobj2s setobj +#define setsvalue2s setsvalue +#define sethvalue2s sethvalue +#define setptvalue2s setptvalue +/* from table to same table */ +#define setobjt2t setobj +/* to table */ +#define setobj2t setobj +/* to new object */ +#define setobj2n setobj +#define setsvalue2n setsvalue + +#define setttype(obj, tt) (ttype(obj) = (tt)) + + +#define iscollectable(o) (ttype(o) >= LUA_TSTRING) + + + +typedef TValue *StkId; /* index to stack elements */ + + +/* +** String headers for string table +*/ +typedef union TString { + L_Umaxalign dummy; /* ensures maximum alignment for strings */ + struct { + CommonHeader; + lu_byte reserved; + unsigned int hash; + size_t len; + } tsv; +} TString; + + +#define getstr(ts) cast(const char *, (ts) + 1) +#define svalue(o) getstr(rawtsvalue(o)) + + + +typedef union Udata { + L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ + struct { + CommonHeader; + struct Table *metatable; + struct Table *env; + size_t len; + } uv; +} Udata; + + + + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + TValue *k; /* constants used by the function */ + Instruction *code; + struct Proto **p; /* functions defined inside the function */ + int *lineinfo; /* map from opcodes to source lines */ + struct LocVar *locvars; /* information about local variables */ + TString **upvalues; /* upvalue names */ + TString *source; + int sizeupvalues; + int sizek; /* size of `k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of `p' */ + int sizelocvars; + int linedefined; + int lastlinedefined; + GCObject *gclist; + lu_byte nups; /* number of upvalues */ + lu_byte numparams; + lu_byte is_vararg; + lu_byte maxstacksize; +} Proto; + + +/* masks for new-style vararg */ +#define VARARG_HASARG 1 +#define VARARG_ISVARARG 2 +#define VARARG_NEEDSARG 4 + + +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + + +/* +** Upvalues +*/ + +typedef struct UpVal { + CommonHeader; + TValue *v; /* points to stack or to its own value */ + union { + TValue value; /* the value (when closed) */ + struct { /* double linked list (when open) */ + struct UpVal *prev; + struct UpVal *next; + } l; + } u; +} UpVal; + + +/* +** Closures +*/ + +#define ClosureHeader \ + CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \ + struct Table *env + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC) +#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC) + + +/* +** Tables +*/ + +typedef union TKey { + struct { + TValuefields; + struct Node *next; /* for chaining */ + } nk; + TValue tvk; +} TKey; + + +typedef struct Node { + TValue i_val; + TKey i_key; +} Node; + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<

    lsizenode)) + + +#define luaO_nilobject (&luaO_nilobject_) + +LUAI_DATA const TValue luaO_nilobject_; + +#define ceillog2(x) (luaO_log2((x)-1) + 1) + +LUAI_FUNC int luaO_log2 (unsigned int x); +LUAI_FUNC int luaO_int2fb (unsigned int x); +LUAI_FUNC int luaO_fb2int (int x); +LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); +LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); + + +#endif + diff --git a/src/server/game/LuaEngine/lua_src/lopcodes.c b/src/server/game/LuaEngine/lua_src/lopcodes.c new file mode 100644 index 0000000000..4cc745230b --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lopcodes.c @@ -0,0 +1,102 @@ +/* +** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** See Copyright Notice in lua.h +*/ + + +#define lopcodes_c +#define LUA_CORE + + +#include "lopcodes.h" + + +/* ORDER OP */ + +const char *const luaP_opnames[NUM_OPCODES+1] = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + NULL +}; + + +#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) + +const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* T A B C mode opcode */ + opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ + ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ + ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ + ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */ + ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ + ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ + ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ +}; + diff --git a/src/server/game/LuaEngine/lua_src/lopcodes.h b/src/server/game/LuaEngine/lua_src/lopcodes.h new file mode 100644 index 0000000000..41224d6ee1 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lopcodes.h @@ -0,0 +1,268 @@ +/* +** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" + + +/*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 9 +#define SIZE_B 9 +#define SIZE_Bx (SIZE_C + SIZE_B) +#define SIZE_A 8 + +#define SIZE_OP 6 + +#define POS_OP 0 +#define POS_A (POS_OP + SIZE_OP) +#define POS_C (POS_A + SIZE_A) +#define POS_B (POS_C + SIZE_C) +#define POS_Bx POS_C + + +/* +** limits for opcode arguments. +** we use (signed) int to manipulate most arguments, +** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) +*/ +#if SIZE_Bx < LUAI_BITSINT-1 +#define MAXARG_Bx ((1<>1) /* `sBx' is signed */ +#else +#define MAXARG_Bx MAX_INT +#define MAXARG_sBx MAX_INT +#endif + + +#define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0))) +#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ + ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0))) +#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ + ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0))) +#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \ + ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0))) +#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \ + ((cast(Instruction, b)< C) then pc++ */ +OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + +OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ + +OP_FORLOOP,/* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ +OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + +OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ +} OpCode; + + +#define NUM_OPCODES (cast(int, OP_VARARG) + 1) + + + +/*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-1: op mode +** bits 2-3: C arg mode +** bits 4-5: B arg mode +** bit 6: instruction set register A +** bit 7: operator is a test +*/ + +enum OpArgMask { + OpArgN, /* argument is not used */ + OpArgU, /* argument is used */ + OpArgR, /* argument is a register or a jump offset */ + OpArgK /* argument is a constant or register/constant */ +}; + +LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) +#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) +#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) + + +LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/loslib.c b/src/server/game/LuaEngine/lua_src/loslib.c new file mode 100644 index 0000000000..da06a572ac --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/loslib.c @@ -0,0 +1,243 @@ +/* +** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include + +#define loslib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +static int os_pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static int os_execute (lua_State *L) { + lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); + return 1; +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return os_pushresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return os_pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (err) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushinteger(L, value); + lua_setfield(L, -2, key); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_getfield(L, -1, key); + res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_getfield(L, -1, key); + if (lua_isnumber(L, -1)) + res = (int)lua_tointeger(L, -1); + else { + if (d < 0) + return luaL_error(L, "field " LUA_QS " missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int os_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + struct tm *stm; + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char cc[3]; + luaL_Buffer b; + cc[0] = '%'; cc[2] = '\0'; + luaL_buffinit(L, &b); + for (; *s; s++) { + if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ + luaL_addchar(&b, *s); + else { + size_t reslen; + char buff[200]; /* should be big enough for any conversion result */ + cc[1] = *(++s); + reslen = strftime(buff, sizeof(buff), cc, stm); + luaL_addlstring(&b, buff, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -1); + ts.tm_mon = getfield(L, "month", -1) - 1; + ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + } + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, (lua_Number)t); + return 1; +} + + +static int os_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + exit(luaL_optint(L, 1, EXIT_SUCCESS)); +} + +static const luaL_Reg syslib[] = { + {"clock", os_clock}, + {"date", os_date}, + {"difftime", os_difftime}, + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, + {"tmpname", os_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_os (lua_State *L) { + luaL_register(L, LUA_OSLIBNAME, syslib); + return 1; +} + diff --git a/src/server/game/LuaEngine/lua_src/lparser.c b/src/server/game/LuaEngine/lua_src/lparser.c new file mode 100644 index 0000000000..dda7488dca --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lparser.c @@ -0,0 +1,1339 @@ +/* +** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + + +#include + +#define lparser_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + +#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]]) + +#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int breaklist; /* list of jumps out of this loop */ + lu_byte nactvar; /* # active locals outside the breakable structure */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isbreakable; /* true if `block' is a loop */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void chunk (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static void anchor_token (LexState *ls) { + if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { + TString *ts = ls->t.seminfo.ts; + luaX_newstring(ls, getstr(ts), ts->tsv.len); + } +} + + +static void error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token))); +} + + +static void errorlimit (FuncState *fs, int limit, const char *what) { + const char *msg = (fs->f->linedefined == 0) ? + luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) : + luaO_pushfstring(fs->L, "function at line %d has more than %d %s", + fs->f->linedefined, limit, what); + luaX_lexerror(fs->ls, msg, 0); +} + + +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + + +static void check_match (LexState *ls, int what, int who, int where) { + if (!testnext(ls, what)) { + if (where == ls->linenumber) + error_expected(ls, what); + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + LUA_QS " expected (to close " LUA_QS " at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.s.info = i; +} + + +static void codestring (LexState *ls, expdesc *e, TString *s) { + init_exp(e, VK, luaK_stringK(ls->fs, s)); +} + + +static void checkname(LexState *ls, expdesc *e) { + codestring(ls, e, str_checkname(ls)); +} + + +static int registerlocalvar (LexState *ls, TString *varname) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + LocVar, SHRT_MAX, "too many local variables"); + while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; + f->locvars[fs->nlocvars].varname = varname; + luaC_objbarrier(ls->L, f, varname); + return fs->nlocvars++; +} + + +#define new_localvarliteral(ls,v,n) \ + new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) + + +static void new_localvar (LexState *ls, TString *name, int n) { + FuncState *fs = ls->fs; + luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); + fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); +} + + +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + fs->nactvar = cast_byte(fs->nactvar + nvars); + for (; nvars; nvars--) { + getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; + } +} + + +static void removevars (LexState *ls, int tolevel) { + FuncState *fs = ls->fs; + while (fs->nactvar > tolevel) + getlocvar(fs, --fs->nactvar).endpc = fs->pc; +} + + +static int indexupvalue (FuncState *fs, TString *name, expdesc *v) { + int i; + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + for (i=0; inups; i++) { + if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { + lua_assert(f->upvalues[i] == name); + return i; + } + } + /* new one */ + luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues"); + luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues, + TString *, MAX_INT, ""); + while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL; + f->upvalues[f->nups] = name; + luaC_objbarrier(fs->L, f, name); + lua_assert(v->k == VLOCAL || v->k == VUPVAL); + fs->upvalues[f->nups].k = cast_byte(v->k); + fs->upvalues[f->nups].info = cast_byte(v->u.s.info); + return f->nups++; +} + + +static int searchvar (FuncState *fs, TString *n) { + int i; + for (i=fs->nactvar-1; i >= 0; i--) { + if (n == getlocvar(fs, i).varname) + return i; + } + return -1; /* not found */ +} + + +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl && bl->nactvar > level) bl = bl->previous; + if (bl) bl->upval = 1; +} + + +static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) { /* no more levels? */ + init_exp(var, VGLOBAL, NO_REG); /* default is global variable */ + return VGLOBAL; + } + else { + int v = searchvar(fs, n); /* look up at current level */ + if (v >= 0) { + init_exp(var, VLOCAL, v); + if (!base) + markupval(fs, v); /* local will be used as an upval */ + return VLOCAL; + } + else { /* not found at current level; try upper one */ + if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL) + return VGLOBAL; + var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */ + var->k = VUPVAL; /* upvalue in this level */ + return VUPVAL; + } + } +} + + +static void singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + if (singlevaraux(fs, varname, var, 1) == VGLOBAL) + var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */ +} + + +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int extra = nvars - nexps; + if (hasmultret(e->k)) { + extra++; /* includes call itself */ + if (extra < 0) extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + if (extra > 1) luaK_reserveregs(fs, extra-1); + } + else { + if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ + if (extra > 0) { + int reg = fs->freereg; + luaK_reserveregs(fs, extra); + luaK_nil(fs, reg, extra); + } + } +} + + +static void enterlevel (LexState *ls) { + if (++ls->L->nCcalls > LUAI_MAXCCALLS) + luaX_lexerror(ls, "chunk has too many syntax levels", 0); +} + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { + bl->breaklist = NO_JUMP; + bl->isbreakable = isbreakable; + bl->nactvar = fs->nactvar; + bl->upval = 0; + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + fs->bl = bl->previous; + removevars(fs->ls, bl->nactvar); + if (bl->upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + /* a block either controls scope or breaks (never both) */ + lua_assert(!bl->isbreakable || !bl->upval); + lua_assert(bl->nactvar == fs->nactvar); + fs->freereg = fs->nactvar; /* free registers */ + luaK_patchtohere(fs, bl->breaklist); +} + + +static void pushclosure (LexState *ls, FuncState *func, expdesc *v) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizep; + int i; + luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizep) f->p[oldsize++] = NULL; + f->p[fs->np++] = func->f; + luaC_objbarrier(ls->L, f, func->f); + init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); + for (i=0; if->nups; i++) { + OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; + luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); + } +} + + +static void open_func (LexState *ls, FuncState *fs) { + lua_State *L = ls->L; + Proto *f = luaF_newproto(L); + fs->f = f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + fs->L = L; + ls->fs = fs; + fs->pc = 0; + fs->lasttarget = -1; + fs->jpc = NO_JUMP; + fs->freereg = 0; + fs->nk = 0; + fs->np = 0; + fs->nlocvars = 0; + fs->nactvar = 0; + fs->bl = NULL; + f->source = ls->source; + f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->h = luaH_new(L, 0, 0); + /* anchor table of constants and prototype (to avoid being collected) */ + sethvalue2s(L, L->top, fs->h); + incr_top(L); + setptvalue2s(L, L->top, f); + incr_top(L); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + removevars(ls, 0); + luaK_ret(fs, 0, 0); /* final return */ + luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); + f->sizecode = fs->pc; + luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); + f->sizelineinfo = fs->pc; + luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); + f->sizek = fs->nk; + luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); + f->sizep = fs->np; + luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); + f->sizelocvars = fs->nlocvars; + luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *); + f->sizeupvalues = f->nups; + lua_assert(luaG_checkcode(f)); + lua_assert(fs->bl == NULL); + ls->fs = fs->prev; + /* last token read was anchored in defunct function; must reanchor it */ + if (fs) anchor_token(ls); + L->top -= 2; /* remove table and prototype from the stack */ +} + + +Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { + struct LexState lexstate; + struct FuncState funcstate; + lexstate.buff = buff; + luaX_setinput(L, &lexstate, z, luaS_new(L, name)); + open_func(&lexstate, &funcstate); + funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ + luaX_next(&lexstate); /* read first token */ + chunk(&lexstate); + check(&lexstate, TK_EOS); + close_func(&lexstate); + lua_assert(funcstate.prev == NULL); + lua_assert(funcstate.f->nups == 0); + lua_assert(lexstate.fs == NULL); + return funcstate.f; +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +static void field (LexState *ls, expdesc *v) { + /* field -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyreg(fs, v); + luaX_next(ls); /* skip the dot or colon */ + checkname(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ +}; + + +static void recfield (LexState *ls, struct ConsControl *cc) { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc key, val; + int rkkey; + if (ls->t.token == TK_NAME) { + luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + checkname(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + rkkey = luaK_exp2RK(fs, &key); + expr(ls, &val); + luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val)); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */ + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); + } +} + + +static void listfield (LexState *ls, struct ConsControl *cc) { + expr(ls, &cc->v); + luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); + cc->na++; + cc->tostore++; +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> ?? */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + struct ConsControl cc; + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VRELOCABLE, pc); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + switch(ls->t.token) { + case TK_NAME: { /* may be listfields or recfields */ + luaX_lookahead(ls); + if (ls->lookahead.token != '=') /* expression? */ + listfield(ls, &cc); + else + recfield(ls, &cc); + break; + } + case '[': { /* constructor_item -> recfield */ + recfield(ls, &cc); + break; + } + default: { /* constructor_part -> listfield */ + listfield(ls, &cc); + break; + } + } + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ + SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ +} + +/* }====================================================================== */ + + + +static void parlist (LexState *ls) { + /* parlist -> [ param { `,' param } ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + f->is_vararg = 0; + if (ls->t.token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { /* param -> NAME */ + new_localvar(ls, str_checkname(ls), nparams++); + break; + } + case TK_DOTS: { /* param -> `...' */ + luaX_next(ls); +#if defined(LUA_COMPAT_VARARG) + /* use `arg' as default name */ + new_localvarliteral(ls, "arg", nparams++); + f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG; +#endif + f->is_vararg |= VARARG_ISVARARG; + break; + } + default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); + } + } while (!f->is_vararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG)); + luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ +} + + +static void body (LexState *ls, expdesc *e, int needself, int line) { + /* body -> `(' parlist `)' chunk END */ + FuncState new_fs; + open_func(ls, &new_fs); + new_fs.f->linedefined = line; + checknext(ls, '('); + if (needself) { + new_localvarliteral(ls, "self", 0); + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + chunk(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + close_func(ls); + pushclosure(ls, &new_fs, e); +} + + +static int explist1 (LexState *ls, expdesc *v) { + /* explist1 -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + int line = ls->linenumber; + switch (ls->t.token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != ls->lastline) + luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist1(ls, &args); + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(ls, &args, ls->t.seminfo.ts); + luaX_next(ls); /* must use `seminfo' before `next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + return; + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.s.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void prefixexp (LexState *ls, expdesc *v) { + /* prefixexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + return; + } + } +} + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> + prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + prefixexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* field */ + field(ls, v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key; + luaK_exp2anyreg(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key; + luaX_next(ls); + checkname(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | + constructor | FUNCTION body | primaryexp */ + switch (ls->t.token) { + case TK_NUMBER: { + init_exp(v, VKNUM, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_STRING: { + codestring(ls, v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use " LUA_QL("...") " outside a vararg function"); + fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + primaryexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ + {10, 9}, {5, 4}, /* power and concat (right associative) */ + {3, 3}, {3, 3}, /* equality and inequality */ + {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ + {2, 2}, {1, 1} /* logical (and/or) */ +}; + +#define UNARY_PRIORITY 8 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { + luaX_next(ls); + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + luaX_next(ls); + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static int block_follow (int token) { + switch (token) { + case TK_ELSE: case TK_ELSEIF: case TK_END: + case TK_UNTIL: case TK_EOS: + return 1; + default: return 0; + } +} + + +static void block (LexState *ls) { + /* block -> chunk */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + chunk(ls); + lua_assert(bl.breaklist == NO_JUMP); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to a local variable, the local variable +** is needed in a previous assignment (to a table). If so, save original +** local value in a safe place and use this safe copy in the previous +** assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { + if (lh->v.k == VINDEXED) { + if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.info = extra; /* previous assignment will use safe copy */ + } + if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.aux = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */ + luaK_reserveregs(fs, 1); + } +} + + +static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, + "syntax error"); + if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ + struct LHS_assign nv; + nv.prev = lh; + primaryexp(ls, &nv.v); + if (nv.v.k == VLOCAL) + check_conflict(ls, lh, &nv.v); + luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, + "variables in assignment"); + assignment(ls, &nv, nvars+1); + } + else { /* assignment -> `=' explist1 */ + int nexps; + checknext(ls, '='); + nexps = explist1(ls, &e); + if (nexps != nvars) { + adjust_assign(ls, nvars, nexps, &e); + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* remove extra values */ + } + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void breakstat (LexState *ls) { + FuncState *fs = ls->fs; + BlockCnt *bl = fs->bl; + int upval = 0; + while (bl && !bl->isbreakable) { + upval |= bl->upval; + bl = bl->previous; + } + if (!bl) + luaX_syntaxerror(ls, "no loop to break"); + if (upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_patchlist(fs, luaK_jump(fs), whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + chunk(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + leaveblock(fs); /* finish scope */ + luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */ + } + else { /* complete semantics when there are upvalues */ + breakstat(ls); /* if condition then break */ + luaK_patchtohere(ls->fs, condexit); /* else... */ + leaveblock(fs); /* finish scope... */ + luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */ + } + leaveblock(fs); /* finish loop */ +} + + +static int exp1 (LexState *ls) { + expdesc e; + int k; + expr(ls, &e); + k = e.k; + luaK_exp2nextreg(ls->fs, &e); + return k; +} + + +static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { + /* forbody -> DO block */ + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + adjustlocalvars(ls, 3); /* control variables */ + checknext(ls, TK_DO); + prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + luaK_patchtohere(fs, prep); + endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : + luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); + luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ + luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for index)", 0); + new_localvarliteral(ls, "(for limit)", 1); + new_localvarliteral(ls, "(for step)", 2); + new_localvar(ls, varname, 3); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1)); + luaK_reserveregs(fs, 1); + } + forbody(ls, base, line, 1, 1); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 0; + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)", nvars++); + new_localvarliteral(ls, "(for state)", nvars++); + new_localvarliteral(ls, "(for control)", nvars++); + /* create declared variables */ + new_localvar(ls, indexname, nvars++); + while (testnext(ls, ',')) + new_localvar(ls, str_checkname(ls), nvars++); + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 3, explist1(ls, &e), &e); + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + + +static int test_then_block (LexState *ls) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + int condexit; + luaX_next(ls); /* skip IF or ELSEIF */ + condexit = cond(ls); + checknext(ls, TK_THEN); + block(ls); /* `then' part */ + return condexit; +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int flist; + int escapelist = NO_JUMP; + flist = test_then_block(ls); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + flist = test_then_block(ls); /* ELSEIF cond THEN block */ + } + if (ls->t.token == TK_ELSE) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + luaX_next(ls); /* skip ELSE (after patch, for correct line info) */ + block(ls); /* `else' part */ + } + else + luaK_concat(fs, &escapelist, flist); + luaK_patchtohere(fs, escapelist); + check_match(ls, TK_END, TK_IF, line); +} + + +static void localfunc (LexState *ls) { + expdesc v, b; + FuncState *fs = ls->fs; + new_localvar(ls, str_checkname(ls), 0); + init_exp(&v, VLOCAL, fs->freereg); + luaK_reserveregs(fs, 1); + adjustlocalvars(ls, 1); + body(ls, &b, 0, ls->linenumber); + luaK_storevar(fs, &v, &b); + /* debug information will only see the variable after this point! */ + getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + int nvars = 0; + int nexps; + expdesc e; + do { + new_localvar(ls, str_checkname(ls), nvars++); + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist1(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {field} [`:' NAME] */ + int needself = 0; + singlevar(ls, v); + while (ls->t.token == '.') + field(ls, v); + if (ls->t.token == ':') { + needself = 1; + field(ls, v); + } + return needself; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int needself; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + needself = funcname(ls, &v); + body(ls, &b, needself, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + primaryexp(ls, &v.v); + if (v.v.k == VCALL) /* stat -> func */ + SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + else { /* stat -> assignment */ + v.prev = NULL; + assignment(ls, &v, 1); + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN explist */ + FuncState *fs = ls->fs; + expdesc e; + int first, nret; /* registers with returned values */ + luaX_next(ls); /* skip RETURN */ + if (block_follow(ls->t.token) || ls->t.token == ';') + first = nret = 0; /* return no values */ + else { + nret = explist1(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + SET_OPCODE(getcode(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); + } + first = fs->nactvar; + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); + else { + luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ + first = fs->nactvar; /* return all `active' values */ + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); +} + + +static int statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + switch (ls->t.token) { + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + return 0; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + return 0; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + return 0; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + return 0; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + return 0; + } + case TK_FUNCTION: { + funcstat(ls, line); /* stat -> funcstat */ + return 0; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + return 0; + } + case TK_RETURN: { /* stat -> retstat */ + retstat(ls); + return 1; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + luaX_next(ls); /* skip BREAK */ + breakstat(ls); + return 1; /* must be last statement */ + } + default: { + exprstat(ls); + return 0; /* to avoid warnings */ + } + } +} + + +static void chunk (LexState *ls) { + /* chunk -> { stat [`;'] } */ + int islast = 0; + enterlevel(ls); + while (!islast && !block_follow(ls->t.token)) { + islast = statement(ls); + testnext(ls, ';'); + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* free registers */ + } + leavelevel(ls); +} + +/* }====================================================================== */ diff --git a/src/server/game/LuaEngine/lua_src/lparser.h b/src/server/game/LuaEngine/lua_src/lparser.h new file mode 100644 index 0000000000..18836afd1c --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lparser.h @@ -0,0 +1,82 @@ +/* +** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression descriptor +*/ + +typedef enum { + VVOID, /* no value */ + VNIL, + VTRUE, + VFALSE, + VK, /* info = index of constant in `k' */ + VKNUM, /* nval = numerical value */ + VLOCAL, /* info = local register */ + VUPVAL, /* info = index of upvalue in `upvalues' */ + VGLOBAL, /* info = index of table; aux = index of global name in `k' */ + VINDEXED, /* info = table register; aux = index register (or `k') */ + VJMP, /* info = instruction pc */ + VRELOCABLE, /* info = instruction pc */ + VNONRELOC, /* info = result register */ + VCALL, /* info = instruction pc */ + VVARARG /* info = instruction pc */ +} expkind; + +typedef struct expdesc { + expkind k; + union { + struct { int info, aux; } s; + lua_Number nval; + } u; + int t; /* patch list of `exit when true' */ + int f; /* patch list of `exit when false' */ +} expdesc; + + +typedef struct upvaldesc { + lu_byte k; + lu_byte info; +} upvaldesc; + + +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + Table *h; /* table to find (and reuse) elements in `k' */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct lua_State *L; /* copy of the Lua state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to `ncode') */ + int lasttarget; /* `pc' of last `jump target' */ + int jpc; /* list of pending jumps to `pc' */ + int freereg; /* first free register */ + int nk; /* number of elements in `k' */ + int np; /* number of elements in `p' */ + short nlocvars; /* number of elements in `locvars' */ + lu_byte nactvar; /* number of active local variables */ + upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ + unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ +} FuncState; + + +LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + const char *name); + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lstate.c b/src/server/game/LuaEngine/lua_src/lstate.c new file mode 100644 index 0000000000..4313b83a0c --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lstate.c @@ -0,0 +1,214 @@ +/* +** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + + +#include + +#define lstate_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE) +#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE) +#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE)) + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + lua_State l; + global_State g; +} LG; + + + +static void stack_init (lua_State *L1, lua_State *L) { + /* initialize CallInfo array */ + L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo); + L1->ci = L1->base_ci; + L1->size_ci = BASIC_CI_SIZE; + L1->end_ci = L1->base_ci + L1->size_ci - 1; + /* initialize stack array */ + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue); + L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; + L1->top = L1->stack; + L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1; + /* initialize first ci */ + L1->ci->func = L1->top; + setnilvalue(L1->top++); /* `function' entry for this `ci' */ + L1->base = L1->ci->base = L1->top; + L1->ci->top = L1->top + LUA_MINSTACK; +} + + +static void freestack (lua_State *L, lua_State *L1) { + luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo); + luaM_freearray(L, L1->stack, L1->stacksize, TValue); +} + + +/* +** open parts that may cause memory-allocation errors +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ + sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ + luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaT_init(L); + luaX_init(L); + luaS_fix(luaS_newliteral(L, MEMERRMSG)); + g->GCthreshold = 4*g->totalbytes; +} + + +static void preinit_state (lua_State *L, global_State *g) { + G(L) = g; + L->stack = NULL; + L->stacksize = 0; + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->size_ci = 0; + L->nCcalls = L->baseCcalls = 0; + L->status = 0; + L->base_ci = L->ci = NULL; + L->savedpc = NULL; + L->errfunc = 0; + setnilvalue(gt(L)); +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_freeall(L); /* collect all objects */ + lua_assert(g->rootgc == obj2gco(L)); + lua_assert(g->strt.nuse == 0); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); + luaZ_freebuffer(L, &g->buff); + freestack(L, L); + lua_assert(g->totalbytes == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0); +} + + +lua_State *luaE_newthread (lua_State *L) { + lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); + luaC_link(L, obj2gco(L1), LUA_TTHREAD); + preinit_state(L1, G(L)); + stack_init(L1, L); /* init stack */ + setobj2n(L, gt(L1), gt(L)); /* share table of globals */ + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + lua_assert(iswhite(obj2gco(L1))); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + luaF_close(L1, L1->stack); /* close all upvalues for this thread */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L1); + freestack(L, L1); + luaM_freemem(L, fromstate(L1), state_size(lua_State)); +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + void *l = (*f)(ud, NULL, 0, state_size(LG)); + if (l == NULL) return NULL; + L = tostate(l); + g = &((LG *)L)->g; + L->next = NULL; + L->tt = LUA_TTHREAD; + g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + L->marked = luaC_white(g); + set2bits(L->marked, FIXEDBIT, SFIXEDBIT); + preinit_state(L, g); + g->frealloc = f; + g->ud = ud; + g->mainthread = L; + g->uvhead.u.l.prev = &g->uvhead; + g->uvhead.u.l.next = &g->uvhead; + g->GCthreshold = 0; /* mark it as unfinished state */ + g->strt.size = 0; + g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(registry(L)); + luaZ_initbuffer(L, &g->buff); + g->panic = NULL; + g->gcstate = GCSpause; + g->rootgc = obj2gco(L); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->tmudata = NULL; + g->totalbytes = sizeof(LG); + g->gcpause = LUAI_GCPAUSE; + g->gcstepmul = LUAI_GCMUL; + g->gcdept = 0; + for (i=0; imt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + else + luai_userstateopen(L); + return L; +} + + +static void callallgcTM (lua_State *L, void *ud) { + UNUSED(ud); + luaC_callGCTM(L); /* call GC metamethods for all udata */ +} + + +LUA_API void lua_close (lua_State *L) { + L = G(L)->mainthread; /* only the main thread can be closed */ + lua_lock(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_separateudata(L, 1); /* separate udata that have GC metamethods */ + L->errfunc = 0; /* no error function during GC metamethods */ + do { /* repeat until no more errors */ + L->ci = L->base_ci; + L->base = L->top = L->ci->base; + L->nCcalls = L->baseCcalls = 0; + } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); + lua_assert(G(L)->tmudata == NULL); + luai_userstateclose(L); + close_state(L); +} + diff --git a/src/server/game/LuaEngine/lua_src/lstate.h b/src/server/game/LuaEngine/lua_src/lstate.h new file mode 100644 index 0000000000..3bc575b6bc --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lstate.h @@ -0,0 +1,169 @@ +/* +** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* table of globals */ +#define gt(L) (&L->l_gt) + +/* registry */ +#define registry(L) (&G(L)->l_registry) + + +/* extra stack space to handle TM calls and some other extras */ +#define EXTRA_STACK 5 + + +#define BASIC_CI_SIZE 8 + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + + + +typedef struct stringtable { + GCObject **hash; + lu_int32 nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** informations about a call +*/ +typedef struct CallInfo { + StkId base; /* base for this function */ + StkId func; /* function index in the stack */ + StkId top; /* top for this function */ + const Instruction *savedpc; + int nresults; /* expected number of results from this function */ + int tailcalls; /* number of tail calls lost under this entry */ +} CallInfo; + + + +#define curr_func(L) (clvalue(L->ci->func)) +#define ci_func(ci) (clvalue((ci)->func)) +#define f_isLua(ci) (!ci_func(ci)->c.isC) +#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) + + +/* +** `global state', shared by all threads of this state +*/ +typedef struct global_State { + stringtable strt; /* hash table for strings */ + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to `frealloc' */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + int sweepstrgc; /* position of sweep in `strt' */ + GCObject *rootgc; /* list of all collectable objects */ + GCObject **sweepgc; /* position of sweep in `rootgc' */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of weak tables (to be cleared) */ + GCObject *tmudata; /* last element of list of userdata to be GC */ + Mbuffer buff; /* temporary buffer for string concatentation */ + lu_mem GCthreshold; + lu_mem totalbytes; /* number of bytes currently allocated */ + lu_mem estimate; /* an estimate of number of bytes actually in use */ + lu_mem gcdept; /* how much GC is `behind schedule' */ + int gcpause; /* size of pause between successive GCs */ + int gcstepmul; /* GC `granularity' */ + lua_CFunction panic; /* to be called in unprotected errors */ + TValue l_registry; + struct lua_State *mainthread; + UpVal uvhead; /* head of double-linked list of all open upvalues */ + struct Table *mt[NUM_TAGS]; /* metatables for basic types */ + TString *tmname[TM_N]; /* array with tag-method names */ +} global_State; + + +/* +** `per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + StkId top; /* first free slot in the stack */ + StkId base; /* base of current function */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + const Instruction *savedpc; /* `savedpc' of current function */ + StkId stack_last; /* last free slot in the stack */ + StkId stack; /* stack base */ + CallInfo *end_ci; /* points after end of ci array*/ + CallInfo *base_ci; /* array of CallInfo's */ + int stacksize; + int size_ci; /* size of array `base_ci' */ + unsigned short nCcalls; /* number of nested C calls */ + unsigned short baseCcalls; /* nested C calls when resuming coroutine */ + lu_byte hookmask; + lu_byte allowhook; + int basehookcount; + int hookcount; + lua_Hook hook; + TValue l_gt; /* table of globals */ + TValue env; /* temporary place for environments */ + GCObject *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_longjmp *errorJmp; /* current error recover point */ + ptrdiff_t errfunc; /* current error handling function (stack index) */ +}; + + +#define G(L) (L->l_G) + + +/* +** Union of all collectable objects +*/ +union GCObject { + GCheader gch; + union TString ts; + union Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct UpVal uv; + struct lua_State th; /* thread */ +}; + + +/* macros to convert a GCObject into a specific value */ +#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) +#define gco2ts(o) (&rawgco2ts(o)->tsv) +#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) +#define gco2u(o) (&rawgco2u(o)->uv) +#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) +#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) +#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) +#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define ngcotouv(o) \ + check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + +/* macro to convert any Lua object into a GCObject */ +#define obj2gco(v) (cast(GCObject *, (v))) + + +LUAI_FUNC lua_State *luaE_newthread (lua_State *L); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); + +#endif + diff --git a/src/server/game/LuaEngine/lua_src/lstring.c b/src/server/game/LuaEngine/lua_src/lstring.c new file mode 100644 index 0000000000..49113151cc --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lstring.c @@ -0,0 +1,111 @@ +/* +** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + + +#include + +#define lstring_c +#define LUA_CORE + +#include "lua.h" + +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + + + +void luaS_resize (lua_State *L, int newsize) { + GCObject **newhash; + stringtable *tb; + int i; + if (G(L)->gcstate == GCSsweepstring) + return; /* cannot resize during GC traverse */ + newhash = luaM_newvector(L, newsize, GCObject *); + tb = &G(L)->strt; + for (i=0; isize; i++) { + GCObject *p = tb->hash[i]; + while (p) { /* for each node in the list */ + GCObject *next = p->gch.next; /* save next */ + unsigned int h = gco2ts(p)->hash; + int h1 = lmod(h, newsize); /* new position */ + lua_assert(cast_int(h%newsize) == lmod(h, newsize)); + p->gch.next = newhash[h1]; /* chain it */ + newhash[h1] = p; + p = next; + } + } + luaM_freearray(L, tb->hash, tb->size, TString *); + tb->size = newsize; + tb->hash = newhash; +} + + +static TString *newlstr (lua_State *L, const char *str, size_t l, + unsigned int h) { + TString *ts; + stringtable *tb; + if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + luaM_toobig(L); + ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); + ts->tsv.len = l; + ts->tsv.hash = h; + ts->tsv.marked = luaC_white(G(L)); + ts->tsv.tt = LUA_TSTRING; + ts->tsv.reserved = 0; + memcpy(ts+1, str, l*sizeof(char)); + ((char *)(ts+1))[l] = '\0'; /* ending 0 */ + tb = &G(L)->strt; + h = lmod(h, tb->size); + ts->tsv.next = tb->hash[h]; /* chain new entry */ + tb->hash[h] = obj2gco(ts); + tb->nuse++; + if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) + luaS_resize(L, tb->size*2); /* too crowded */ + return ts; +} + + +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + GCObject *o; + unsigned int h = cast(unsigned int, l); /* seed */ + size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ + size_t l1; + for (l1=l; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; + o != NULL; + o = o->gch.next) { + TString *ts = rawgco2ts(o); + if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { + /* string may be dead */ + if (isdead(G(L), o)) changewhite(o); + return ts; + } + } + return newlstr(L, str, l, h); /* not found */ +} + + +Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { + Udata *u; + if (s > MAX_SIZET - sizeof(Udata)) + luaM_toobig(L); + u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); + u->uv.marked = luaC_white(G(L)); /* is not finalized */ + u->uv.tt = LUA_TUSERDATA; + u->uv.len = s; + u->uv.metatable = NULL; + u->uv.env = e; + /* chain it on udata list (after main thread) */ + u->uv.next = G(L)->mainthread->next; + G(L)->mainthread->next = obj2gco(u); + return u; +} + diff --git a/src/server/game/LuaEngine/lua_src/lstring.h b/src/server/game/LuaEngine/lua_src/lstring.h new file mode 100644 index 0000000000..73a2ff8b38 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lstring.h @@ -0,0 +1,31 @@ +/* +** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) + +#define sizeudata(u) (sizeof(union Udata)+(u)->len) + +#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s))) +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + +#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) + +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lstrlib.c b/src/server/game/LuaEngine/lua_src/lstrlib.c new file mode 100644 index 0000000000..7a03489beb --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lstrlib.c @@ -0,0 +1,871 @@ +/* +** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include + +#define lstrlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* macro to `unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, l); + return 1; +} + + +static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { + /* relative string position: negative means back from end */ + if (pos < 0) pos += (ptrdiff_t)len + 1; + return (pos >= 0) ? pos : 0; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); + ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + while (l--) luaL_addchar(&b, s[l]); + luaL_pushresult(&b); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); + ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi <= 0) posi = 1; + if ((size_t)pose > l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* overflow? */ + luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + if (*(p++) == L_ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case L_ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing " LUA_QL("[") " after " + LUA_QL("%%f") " in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p+1))); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = classend(ms, p); /* points to what is next */ + int m = ssrc_end && singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + if (i >= ms->level) { + if (i == 0) /* ms->level == 0, too */ + lua_pushlstring(ms->L, s, e - s); /* add whole match */ + else + luaL_error(ms->L, "invalid capture index"); + } + else { + ptrdiff_t l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); + } +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +static int str_find_aux (lua_State *L, int find) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; + if (find && (lua_toboolean(L, 4) || /* explicit request? */ + strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushinteger(L, s2-s+1); + lua_pushinteger(L, s2-s+l2); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, s1-s+1); /* start */ + lua_pushinteger(L, res-s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + lua_pushnil(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +static int gmatch_aux (lua_State *L) { + MatchState ms; + size_t ls; + const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); + const char *p = lua_tostring(L, lua_upvalueindex(2)); + const char *src; + ms.L = L; + ms.src_init = s; + ms.src_end = s+ls; + for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); + src <= ms.src_end; + src++) { + const char *e; + ms.level = 0; + if ((e = match(&ms, src, p)) != NULL) { + lua_Integer newstart = e-s; + if (e == src) newstart++; /* empty match? go at least one position */ + lua_pushinteger(L, newstart); + lua_replace(L, lua_upvalueindex(3)); + return push_captures(&ms, src, e); + } + } + return 0; /* not found */ +} + + +static int gmatch (lua_State *L) { + luaL_checkstring(L, 1); + luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_pushinteger(L, 0); + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static int gfind_nodef (lua_State *L) { + return luaL_error(L, LUA_QL("string.gfind") " was renamed to " + LUA_QL("string.gmatch")); +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l, i; + const char *news = lua_tolstring(ms->L, 3, &l); + for (i = 0; i < l; i++) { + if (news[i] != L_ESC) + luaL_addchar(b, news[i]); + else { + i++; /* skip ESC */ + if (!isdigit(uchar(news[i]))) + luaL_addchar(b, news[i]); + else if (news[i] == '0') + luaL_addlstring(b, s, e - s); + else { + push_onecapture(ms, news[i] - '1', s, e); + luaL_addvalue(b); /* add capture to accumulated result */ + } + } + } +} + + +static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + lua_State *L = ms->L; + switch (lua_type(L, 3)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + add_s(ms, b, s, e); + return; + } + case LUA_TFUNCTION: { + int n; + lua_pushvalue(L, 3); + n = push_captures(ms, s, e); + lua_call(L, n, 1); + break; + } + case LUA_TTABLE: { + push_onecapture(ms, 0, s, e); + lua_gettable(L, 3); + break; + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); + lua_pushlstring(L, s, e - s); /* keep original text */ + } + else if (!lua_isstring(L, -1)) + luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); + luaL_addvalue(b); /* add result to accumulator */ +} + + +static int str_gsub (lua_State *L) { + size_t srcl; + const char *src = luaL_checklstring(L, 1, &srcl); + const char *p = luaL_checkstring(L, 2); + int tr = lua_type(L, 3); + int max_s = luaL_optint(L, 4, srcl+1); + int anchor = (*p == '^') ? (p++, 1) : 0; + int n = 0; + MatchState ms; + luaL_Buffer b; + luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_value(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_addchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* valid flags in a format specification */ +#define FLAGS "-+ #0" +/* +** maximum size of each format specification (such as '%-099.99d') +** (+10 accounts for %99.99x plus margin of error) +*/ +#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) + + +static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_addchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + break; + } + case '\r': { + luaL_addlstring(b, "\\r", 2); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_addchar(b, *s); + break; + } + } + s++; + } + luaL_addchar(b, '"'); +} + +static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { + const char *p = strfrmt; + while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ + if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) + luaL_error(L, "invalid format (repeated flags)"); + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + *(form++) = '%'; + strncpy(form, strfrmt, p - strfrmt + 1); + form += p - strfrmt + 1; + *form = '\0'; + return p; +} + + +static void addintlen (char *form) { + size_t l = strlen(form); + char spec = form[l - 1]; + strcpy(form + l - 1, LUA_INTFRMLEN); + form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; + form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; +} + + +static int str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + if (++arg > top) + luaL_argerror(L, arg, "no value"); + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + sprintf(buff, form, (int)luaL_checknumber(L, arg)); + break; + } + case 'd': case 'i': { + addintlen(form); + sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + addintlen(form); + sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'e': case 'E': case 'f': + case 'g': case 'G': { + sprintf(buff, form, (double)luaL_checknumber(L, arg)); + break; + } + case 'q': { + addquoted(L, &b, arg); + continue; /* skip the 'addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " + LUA_QL("format"), *(strfrmt - 1)); + } + } + luaL_addlstring(&b, buff, strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gfind", gfind_nodef}, + {"gmatch", gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + lua_createtable(L, 0, 1); /* create metatable for strings */ + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); + lua_setmetatable(L, -2); /* set string metatable */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* string library... */ + lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUALIB_API int luaopen_string (lua_State *L) { + luaL_register(L, LUA_STRLIBNAME, strlib); +#if defined(LUA_COMPAT_GFIND) + lua_getfield(L, -1, "gmatch"); + lua_setfield(L, -2, "gfind"); +#endif + createmetatable(L); + return 1; +} + diff --git a/src/server/game/LuaEngine/lua_src/ltable.c b/src/server/game/LuaEngine/lua_src/ltable.c new file mode 100644 index 0000000000..ec84f4fabc --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ltable.c @@ -0,0 +1,588 @@ +/* +** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest `n' such that at +** least half the slots between 0 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include +#include + +#define ltable_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "ltable.h" + + +/* +** max size of array part is 2^MAXBITS +*/ +#if LUAI_BITSINT > 26 +#define MAXBITS 26 +#else +#define MAXBITS (LUAI_BITSINT-2) +#endif + +#define MAXASIZE (1 << MAXBITS) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) +#define hashboolean(t,p) hashpow2(t, p) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, IntPoint(p)) + + +/* +** number of ints inside a lua_Number +*/ +#define numints cast_int(sizeof(lua_Number)/sizeof(int)) + + + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + {{NULL}, LUA_TNIL}, /* value */ + {{{NULL}, LUA_TNIL, NULL}} /* key */ +}; + + +/* +** hash for lua_Numbers +*/ +static Node *hashnum (const Table *t, lua_Number n) { + unsigned int a[numints]; + int i; + if (luai_numeq(n, 0)) /* avoid problems with -0 */ + return gnode(t, 0); + memcpy(a, &n, sizeof(a)); + for (i = 1; i < numints; i++) a[0] += a[i]; + return hashmod(t, a[0]); +} + + + +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +static Node *mainposition (const Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNUMBER: + return hashnum(t, nvalue(key)); + case LUA_TSTRING: + return hashstr(t, rawtsvalue(key)); + case LUA_TBOOLEAN: + return hashboolean(t, bvalue(key)); + case LUA_TLIGHTUSERDATA: + return hashpointer(t, pvalue(key)); + default: + return hashpointer(t, gcvalue(key)); + } +} + + +/* +** returns the index for `key' if `key' is an appropriate key to live in +** the array part of the table, -1 otherwise. +*/ +static int arrayindex (const TValue *key) { + if (ttisnumber(key)) { + lua_Number n = nvalue(key); + int k; + lua_number2int(k, n); + if (luai_numeq(cast_num(k), n)) + return k; + } + return -1; /* `key' did not match some condition */ +} + + +/* +** returns the index of a `key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signalled by -1. +*/ +static int findindex (lua_State *L, Table *t, StkId key) { + int i; + if (ttisnil(key)) return -1; /* first iteration */ + i = arrayindex(key); + if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ + return i-1; /* yes; that's the index (corrected to C) */ + else { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ + if (luaO_rawequalObj(key2tval(n), key) || + (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && + gcvalue(gkey(n)) == gcvalue(key))) { + i = cast_int(n - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return i + t->sizearray; + } + else n = gnext(n); + } while (n); + luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + return 0; /* to avoid warnings */ + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + int i = findindex(L, t, key); /* find original element */ + for (i++; i < t->sizearray; i++) { /* try first array part */ + if (!ttisnil(&t->array[i])) { /* a non-nil value? */ + setnvalue(key, cast_num(i+1)); + setobj2s(L, key+1, &t->array[i]); + return 1; + } + } + for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ + setobj2s(L, key, key2tval(gnode(t, i))); + setobj2s(L, key+1, gval(gnode(t, i))); + return 1; + } + } + return 0; /* no more elements */ +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + + +static int computesizes (int nums[], int *narray) { + int i; + int twotoi; /* 2^i */ + int a = 0; /* number of elements smaller than 2^i */ + int na = 0; /* number of elements to go to array part */ + int n = 0; /* optimal size for array part */ + for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + n = twotoi; /* optimal size (till now) */ + na = a; /* all elements smaller than n will go to array part */ + } + } + if (a == *narray) break; /* all elements already counted */ + } + *narray = n; + lua_assert(*narray/2 <= na && na <= *narray); + return na; +} + + +static int countint (const TValue *key, int *nums) { + int k = arrayindex(key); + if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ + nums[ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +static int numusearray (const Table *t, int *nums) { + int lg; + int ttlg; /* 2^lg */ + int ause = 0; /* summation of `nums' */ + int i = 1; /* count to traverse all array keys */ + for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ + int lc = 0; /* counter */ + int lim = ttlg; + if (lim > t->sizearray) { + lim = t->sizearray; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg-1), 2^lg] */ + for (; i <= lim; i++) { + if (!ttisnil(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int numusehash (const Table *t, int *nums, int *pnasize) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* summation of `nums' */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!ttisnil(gval(n))) { + ause += countint(key2tval(n), nums); + totaluse++; + } + } + *pnasize += ause; + return totaluse; +} + + +static void setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; iarray[i]); + t->sizearray = size; +} + + +static void setnodevector (lua_State *L, Table *t, int size) { + int lsize; + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common `dummynode' */ + lsize = 0; + } + else { + int i; + lsize = ceillog2(size); + if (lsize > MAXBITS) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + t->node = luaM_newvector(L, size, Node); + for (i=0; ilsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ +} + + +static void resize (lua_State *L, Table *t, int nasize, int nhsize) { + int i; + int oldasize = t->sizearray; + int oldhsize = t->lsizenode; + Node *nold = t->node; /* save old hash ... */ + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(L, t, nasize); + /* create new hash part with appropriate size */ + setnodevector(L, t, nhsize); + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ + for (i=nasize; iarray[i])) + setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); + } + /* shrink array */ + luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + } + /* re-insert elements from hash part */ + for (i = twoto(oldhsize) - 1; i >= 0; i--) { + Node *old = nold+i; + if (!ttisnil(gval(old))) + setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old)); + } + if (nold != dummynode) + luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */ +} + + +void luaH_resizearray (lua_State *L, Table *t, int nasize) { + int nsize = (t->node == dummynode) ? 0 : sizenode(t); + resize(L, t, nasize, nsize); +} + + +static void rehash (lua_State *L, Table *t, const TValue *ek) { + int nasize, na; + int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ + int i; + int totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + /* count extra key */ + nasize += countint(ek, nums); + totaluse++; + /* compute new size for array part */ + na = computesizes(nums, &nasize); + /* resize the table to new computed sizes */ + resize(L, t, nasize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L, int narray, int nhash) { + Table *t = luaM_new(L, Table); + luaC_link(L, obj2gco(t), LUA_TTABLE); + t->metatable = NULL; + t->flags = cast_byte(~0); + /* temporary values (kept only if some malloc fails) */ + t->array = NULL; + t->sizearray = 0; + t->lsizenode = 0; + t->node = cast(Node *, dummynode); + setarrayvector(L, t, narray); + setnodevector(L, t, nhash); + return t; +} + + +void luaH_free (lua_State *L, Table *t) { + if (t->node != dummynode) + luaM_freearray(L, t->node, sizenode(t), Node); + luaM_freearray(L, t->array, t->sizearray, TValue); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + while (t->lastfree-- > t->node) { + if (ttisnil(gkey(t->lastfree))) + return t->lastfree; + } + return NULL; /* could not find a free place */ +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +static TValue *newkey (lua_State *L, Table *t, const TValue *key) { + Node *mp = mainposition(t, key); + if (!ttisnil(gval(mp)) || mp == dummynode) { + Node *othern; + Node *n = getfreepos(t); /* get a free place */ + if (n == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + return luaH_set(L, t, key); /* re-insert key into grown table */ + } + lua_assert(n != dummynode); + othern = mainposition(t, key2tval(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ + gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + gnext(mp) = NULL; /* now `mp' is free */ + setnilvalue(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + gnext(n) = gnext(mp); /* chain new position */ + gnext(mp) = n; + mp = n; + } + } + gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; + luaC_barriert(L, t, key); + lua_assert(ttisnil(gval(mp))); + return gval(mp); +} + + +/* +** search function for integers +*/ +const TValue *luaH_getnum (Table *t, int key) { + /* (1 <= key && key <= t->sizearray) */ + if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) + return &t->array[key-1]; + else { + lua_Number nk = cast_num(key); + Node *n = hashnum(t, nk); + do { /* check whether `key' is somewhere in the chain */ + if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } +} + + +/* +** search function for strings +*/ +const TValue *luaH_getstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; +} + + +/* +** main search function +*/ +const TValue *luaH_get (Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNIL: return luaO_nilobject; + case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); + case LUA_TNUMBER: { + int k; + lua_Number n = nvalue(key); + lua_number2int(k, n); + if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ + return luaH_getnum(t, k); /* use specialized version */ + /* else go through */ + } + default: { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (luaO_rawequalObj(key2tval(n), key)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } + } +} + + +TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { + const TValue *p = luaH_get(t, key); + t->flags = 0; + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + if (ttisnil(key)) luaG_runerror(L, "table index is nil"); + else if (ttisnumber(key) && luai_numisnan(nvalue(key))) + luaG_runerror(L, "table index is NaN"); + return newkey(L, t, key); + } +} + + +TValue *luaH_setnum (lua_State *L, Table *t, int key) { + const TValue *p = luaH_getnum(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setnvalue(&k, cast_num(key)); + return newkey(L, t, &k); + } +} + + +TValue *luaH_setstr (lua_State *L, Table *t, TString *key) { + const TValue *p = luaH_getstr(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setsvalue(L, &k, key); + return newkey(L, t, &k); + } +} + + +static int unbound_search (Table *t, unsigned int j) { + unsigned int i = j; /* i is zero or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while (!ttisnil(luaH_getnum(t, j))) { + i = j; + j *= 2; + if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while (!ttisnil(luaH_getnum(t, i))) i++; + return i - 1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(luaH_getnum(t, m))) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +*/ +int luaH_getn (Table *t) { + unsigned int j = t->sizearray; + if (j > 0 && ttisnil(&t->array[j - 1])) { + /* there is a boundary in the array part: (binary) search for it */ + unsigned int i = 0; + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(&t->array[m - 1])) j = m; + else i = m; + } + return i; + } + /* else must find a boundary in hash part */ + else if (t->node == dummynode) /* hash part is empty? */ + return j; /* that is easy... */ + else return unbound_search(t, j); +} + + + +#if defined(LUA_DEBUG) + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainposition(t, key); +} + +int luaH_isdummy (Node *n) { return n == dummynode; } + +#endif diff --git a/src/server/game/LuaEngine/lua_src/ltable.h b/src/server/game/LuaEngine/lua_src/ltable.h new file mode 100644 index 0000000000..f5b9d5ead0 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ltable.h @@ -0,0 +1,40 @@ +/* +** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gkey(n) (&(n)->i_key.nk) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->i_key.nk.next) + +#define key2tval(n) (&(n)->i_key.tvk) + + +LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); +LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC int luaH_getn (Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +LUAI_FUNC int luaH_isdummy (Node *n); +#endif + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/ltablib.c b/src/server/game/LuaEngine/lua_src/ltablib.c new file mode 100644 index 0000000000..b6d9cb4ac7 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ltablib.c @@ -0,0 +1,287 @@ +/* +** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + + +#include + +#define ltablib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + for (i=1; i <= n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushinteger(L, i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } + return 0; +} + + +static int maxn (lua_State *L) { + lua_Number max = 0; + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ + if (lua_type(L, -1) == LUA_TNUMBER) { + lua_Number v = lua_tonumber(L, -1); + if (v > max) max = v; + } + } + lua_pushnumber(L, max); + return 1; +} + + +static int getn (lua_State *L) { + lua_pushinteger(L, aux_getn(L, 1)); + return 1; +} + + +static int setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); +#ifndef luaL_setn + luaL_setn(L, 1, luaL_checkint(L, 2)); +#else + luaL_error(L, LUA_QL("setn") " is obsolete"); +#endif + lua_pushvalue(L, 1); + return 1; +} + + +static int tinsert (lua_State *L) { + int e = aux_getn(L, 1) + 1; /* first empty element */ + int pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + int i; + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > e) e = pos; /* `grow' array if necessary */ + for (i = e; i > pos; i--) { /* move up elements */ + lua_rawgeti(L, 1, i-1); + lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + } + } + luaL_setn(L, 1, e); /* new size */ + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + int e = aux_getn(L, 1); + int pos = luaL_optint(L, 2, e); + if (!(1 <= pos && pos <= e)) /* position is outside bounds? */ + return 0; /* nothing to remove */ + luaL_setn(L, 1, e - 1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j + +#define ltm_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + + +const char *const luaT_typenames[] = { + "nil", "boolean", "userdata", "number", + "string", "table", "function", "userdata", "thread", + "proto", "upval" +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__eq", + "__add", "__sub", "__mul", "__div", "__mod", + "__pow", "__unm", "__len", "__lt", "__le", + "__concat", "__call" + }; + int i; + for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); + luaS_fix(G(L)->tmname[i]); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_getstr(events, ename); + lua_assert(event <= TM_EQ); + if (ttisnil(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); +} + diff --git a/src/server/game/LuaEngine/lua_src/ltm.h b/src/server/game/LuaEngine/lua_src/ltm.h new file mode 100644 index 0000000000..64343b781b --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/ltm.h @@ -0,0 +1,54 @@ +/* +** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_EQ, /* last tag method with `fast' access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_DIV, + TM_MOD, + TM_POW, + TM_UNM, + TM_LEN, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_N /* number of elements in the enum */ +} TMS; + + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +LUAI_DATA const char *const luaT_typenames[]; + + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lua.h b/src/server/game/LuaEngine/lua_src/lua.h new file mode 100644 index 0000000000..a4b73e743e --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lua.h @@ -0,0 +1,388 @@ +/* +** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.5" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status; 0 is OK */ +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); + +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/luaconf.h b/src/server/game/LuaEngine/lua_src/luaconf.h new file mode 100644 index 0000000000..e2cb26163a --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/luaconf.h @@ -0,0 +1,763 @@ +/* +** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include +#include + + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) +#define LUA_WIN +#endif + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_DL_DYLD /* does not need extra library */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionallity listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#endif + + +/* +@@ LUA_PATH and LUA_CPATH are the names of the environment variables that +@* Lua check to set its paths. +@@ LUA_INIT is the name of the environment variable that Lua +@* checks for initialization code. +** CHANGE them if you want different names. +*/ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" + +#else +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/5.1/" +#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" +#define LUA_PATH_DEFAULT \ + "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" +#define LUA_CPATH_DEFAULT \ + "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" +#endif + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_PATHSEP is the character that separates templates in a path. +@@ LUA_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ LUA_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ LUA_IGMARK is a mark to ignore all before it when bulding the +@* luaopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ +#define LUA_INTEGER ptrdiff_t + + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all standard library functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) + +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif + +#else + +#define LUA_API extern + +#endif + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DATA is a mark for all extern (const) variables that are not to +@* be exported to outside modules. +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. +*/ +#if defined(luaall_c) +#define LUAI_FUNC static +#define LUAI_DATA /* empty */ + +#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DATA LUAI_FUNC + +#else +#define LUAI_FUNC extern +#define LUAI_DATA extern +#endif + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +** {================================================================== +** Stand-alone configuration +** =================================================================== +*/ + +#if defined(lua_c) || defined(luaall_c) + +/* +@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that +@* is, whether we're running lua interactively). +** CHANGE it if you have a better definition for non-POSIX/non-Windows +** systems. +*/ +#if defined(LUA_USE_ISATTY) +#include +#define lua_stdin_is_tty() isatty(0) +#elif defined(LUA_WIN) +#include +#include +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +#endif + + +/* +@@ LUA_PROMPT is the default prompt used by stand-alone Lua. +@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. +** CHANGE them if you want different prompts. (You can also change the +** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) +*/ +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " + + +/* +@@ LUA_PROGNAME is the default name for the stand-alone Lua program. +** CHANGE it if your stand-alone interpreter has a different name and +** your system is not able to detect that name automatically. +*/ +#define LUA_PROGNAME "lua" + + +/* +@@ LUA_MAXINPUT is the maximum length for an input line in the +@* stand-alone interpreter. +** CHANGE it if you need longer lines. +*/ +#define LUA_MAXINPUT 512 + + +/* +@@ lua_readline defines how to show a prompt and then read a line from +@* the standard input. +@@ lua_saveline defines how to "save" a read line in a "history". +@@ lua_freeline defines how to free a line read by lua_readline. +** CHANGE them if you want to improve this functionality (e.g., by using +** GNU readline and history facilities). +*/ +#if defined(LUA_USE_READLINE) +#include +#include +#include +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,idx) \ + if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ + add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_freeline(L,b) ((void)L, free(b)) +#else +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } +#endif + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ + + +/* +@@ LUAI_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ + + + +/* +@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. +** CHANGE it (define it) if you want exact compatibility with the +** behavior of setn/getn in Lua 5.0. +*/ +#undef LUA_COMPAT_GETN + +/* +@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. +** CHANGE it to undefined as soon as you do not need a global 'loadlib' +** function (the function is still available as 'package.loadlib'). +*/ +#undef LUA_COMPAT_LOADLIB + +/* +@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. +** CHANGE it to undefined as soon as your programs use only '...' to +** access vararg parameters (instead of the old 'arg' table). +*/ +#define LUA_COMPAT_VARARG + +/* +@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. +** CHANGE it to undefined as soon as your programs use 'math.fmod' or +** the new '%' operator instead of 'math.mod'. +*/ +#define LUA_COMPAT_MOD + +/* +@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting +@* facility. +** CHANGE it to 2 if you want the old behaviour, or undefine it to turn +** off the advisory error when nesting [[...]]. +*/ +#define LUA_COMPAT_LSTR 1 + +/* +@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. +** CHANGE it to undefined as soon as you rename 'string.gfind' to +** 'string.gmatch'. +*/ +#define LUA_COMPAT_GFIND + +/* +@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' +@* behavior. +** CHANGE it to undefined as soon as you replace to 'luaL_register' +** your uses of 'luaL_openlib' +*/ +#define LUA_COMPAT_OPENLIB + + + +/* +@@ luai_apicheck is the assert macro used by the Lua-C API. +** CHANGE luai_apicheck if you want Lua to perform some checks in the +** parameters it gets from API calls. This may slow down the interpreter +** a bit, but may be quite useful when debugging C code that interfaces +** with Lua. A useful redefinition is to use assert.h. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(L,o) { (void)L; assert(o); } +#else +#define luai_apicheck(L,o) { (void)L; } +#endif + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif + + +/* +@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. +@@ LUAI_INT32 is an signed integer with at least 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. (The definitions in the 'else' +** part always works, but may waste space on machines with 64-bit +** longs.) Probably you do not need to change this. +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_UINT32 unsigned int +#define LUAI_INT32 int +#define LUAI_MAXINT32 INT_MAX +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else +/* 16-bit ints */ +#define LUAI_UINT32 unsigned long +#define LUAI_INT32 long +#define LUAI_MAXINT32 LONG_MAX +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif + + +/* +@@ LUAI_MAXCALLS limits the number of nested calls. +** CHANGE it if you need really deep recursive calls. This limit is +** arbitrary; its only purpose is to stop infinite recursion before +** exhausting memory. +*/ +#define LUAI_MAXCALLS 20000 + + +/* +@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function +@* can use. +** CHANGE it if you need lots of (Lua) stack space for your C +** functions. This limit is arbitrary; its only purpose is to stop C +** functions to consume unlimited stack space. (must be smaller than +** -LUA_REGISTRYINDEX) +*/ +#define LUAI_MAXCSTACK 8000 + + + +/* +** {================================================================== +** CHANGE (to smaller values) the following definitions if your system +** has a small C stack. (Or you may want to change them to larger +** values if your system has a large C stack and these limits are +** too rigid for you.) Some of these constants control the size of +** stack-allocated arrays used by the compiler or the interpreter, while +** others limit the maximum number of recursive calls that the compiler +** or the interpreter can perform. Values too large may cause a C stack +** overflow for some forms of deep constructs. +** =================================================================== +*/ + + +/* +@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and +@* syntactical nested non-terminals in a program. +*/ +#define LUAI_MAXCCALLS 200 + + +/* +@@ LUAI_MAXVARS is the maximum number of local variables per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXVARS 200 + + +/* +@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXUPVALUES 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#define LUAL_BUFFERSIZE BUFSIZ + +/* }================================================================== */ + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER double + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_str2number converts a string to a number. +*/ +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#define lua_str2number(s,p) strtod((s), (p)) + + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ +#if defined(LUA_CORE) +#include +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) +#define luai_numdiv(a,b) ((a)/(b)) +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) +#define luai_numpow(a,b) (pow(a,b)) +#define luai_numunm(a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + +/* +@@ lua_number2int is a macro to convert lua_Number to int. +@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. +** CHANGE them if you know a faster way to convert a lua_Number to +** int (with any rounding method and without throwing errors) in your +** system. In Pentium machines, a naive typecast from double to int +** in C is extremely slow, so any alternative is worth trying. +*/ + +/* On a Pentium, resort to a trick */ +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ + (defined(__i386) || defined (_M_IX86) || defined(__i386__)) + +/* On a Microsoft compiler, use assembler */ +#if defined(_MSC_VER) + +#define lua_number2int(i,d) __asm fld d __asm fistp i +#define lua_number2integer(i,n) lua_number2int(i, n) + +/* the next trick should work on any Pentium, but sometimes clashes + with a DirectX idiosyncrasy */ +#else + +union luai_Cast { double l_d; long l_l; }; +#define lua_number2int(i,d) \ + { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } +#define lua_number2integer(i,n) lua_number2int(i, n) + +#endif + + +/* this option always works, but may be slow */ +#else +#define lua_number2int(i,d) ((i)=(int)(d)) +#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. +** CHANGE it if your system requires alignments larger than double. (For +** instance, if your system supports long doubles and they must be +** aligned in 16-byte boundaries, then you should add long double in the +** union.) Probably you do not need to change this. +*/ +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + + +/* +@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. +** CHANGE them if you prefer to use longjmp/setjmp even with C++ +** or if want/don't to use _longjmp/_setjmp instead of regular +** longjmp/setjmp. By default, Lua handles errors with exceptions when +** compiling as C++ code, with _longjmp/_setjmp when asked to use them, +** and with longjmp/setjmp otherwise. +*/ +#if defined(__cplusplus) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) try { a } catch(...) \ + { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + + +/* +@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern +@* can do during pattern-matching. +** CHANGE it if you need more captures. This limit is arbitrary. +*/ +#define LUA_MAXCAPTURES 32 + + +/* +@@ lua_tmpnam is the function that the OS library uses to create a +@* temporary name. +@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. +** CHANGE them if you have an alternative to tmpnam (which is considered +** insecure) or if you want the original tmpnam anyway. By default, Lua +** uses tmpnam except when POSIX is available, where it uses mkstemp. +*/ +#if defined(loslib_c) || defined(luaall_c) + +#if defined(LUA_USE_MKSTEMP) +#include +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { \ + strcpy(b, "/tmp/lua_XXXXXX"); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#endif + +#endif + + +/* +@@ lua_popen spawns a new process connected to the current one through +@* the file streams. +** CHANGE it if you have a way to implement it in your system. +*/ +#if defined(LUA_USE_POPEN) + +#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) + +#elif defined(LUA_WIN) + +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) + +#else + +#define lua_popen(L,c,m) ((void)((void)c, m), \ + luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) +#define lua_pclose(L,file) ((void)((void)L, file), 0) + +#endif + +/* +@@ LUA_DL_* define which dynamic-library system Lua should use. +** CHANGE here if Lua has problems choosing the appropriate +** dynamic-library system for your platform (either Windows' DLL, Mac's +** dyld, or Unix's dlopen). If your system is some kind of Unix, there +** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for +** it. To use dlopen you also need to adapt the src/Makefile (probably +** adding -ldl to the linker options), so Lua does not select it +** automatically. (When you change the makefile to add -ldl, you must +** also add -DLUA_USE_DLOPEN.) +** If you do not want any kind of dynamic library, undefine all these +** options. +** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. +*/ +#if defined(LUA_USE_DLOPEN) +#define LUA_DL_DLOPEN +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#endif + + +/* +@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State +@* (the data goes just *before* the lua_State pointer). +** CHANGE (define) this if you really need that. This value must be +** a multiple of the maximum alignment required for your machine. +*/ +#define LUAI_EXTRASPACE 0 + + +/* +@@ luai_userstate* allow user-specific actions on threads. +** CHANGE them if you defined LUAI_EXTRASPACE and need to do something +** extra when a thread is created/deleted/resumed/yielded. +*/ +#define luai_userstateopen(L) ((void)L) +#define luai_userstateclose(L) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatefree(L) ((void)L) +#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) + + +/* +@@ LUA_INTFRMLEN is the length modifier for integer conversions +@* in 'string.format'. +@@ LUA_INTFRM_T is the integer type correspoding to the previous length +@* modifier. +** CHANGE them if your system supports long long or does not support long. +*/ + +#if defined(LUA_USELONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + diff --git a/src/server/game/LuaEngine/lua_src/lualib.h b/src/server/game/LuaEngine/lua_src/lualib.h new file mode 100644 index 0000000000..469417f670 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lualib.h @@ -0,0 +1,53 @@ +/* +** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* Key to file-handle type */ +#define LUA_FILEHANDLE "FILE*" + + +#define LUA_COLIBNAME "coroutine" +LUALIB_API int (luaopen_base) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUALIB_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUALIB_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUALIB_API int (luaopen_string) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUALIB_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUALIB_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUALIB_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lundump.c b/src/server/game/LuaEngine/lua_src/lundump.c new file mode 100644 index 0000000000..8010a45795 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lundump.c @@ -0,0 +1,227 @@ +/* +** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#include + +#define lundump_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "lundump.h" +#include "lzio.h" + +typedef struct { + lua_State* L; + ZIO* Z; + Mbuffer* b; + const char* name; +} LoadState; + +#ifdef LUAC_TRUST_BINARIES +#define IF(c,s) +#define error(S,s) +#else +#define IF(c,s) if (c) error(S,s) + +static void error(LoadState* S, const char* why) +{ + luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); + luaD_throw(S->L,LUA_ERRSYNTAX); +} +#endif + +#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) +#define LoadByte(S) (lu_byte)LoadChar(S) +#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) +#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) + +static void LoadBlock(LoadState* S, void* b, size_t size) +{ + size_t r=luaZ_read(S->Z,b,size); + IF (r!=0, "unexpected end"); +} + +static int LoadChar(LoadState* S) +{ + char x; + LoadVar(S,x); + return x; +} + +static int LoadInt(LoadState* S) +{ + int x; + LoadVar(S,x); + IF (x<0, "bad integer"); + return x; +} + +static lua_Number LoadNumber(LoadState* S) +{ + lua_Number x; + LoadVar(S,x); + return x; +} + +static TString* LoadString(LoadState* S) +{ + size_t size; + LoadVar(S,size); + if (size==0) + return NULL; + else + { + char* s=luaZ_openspace(S->L,S->b,size); + LoadBlock(S,s,size); + return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ + } +} + +static void LoadCode(LoadState* S, Proto* f) +{ + int n=LoadInt(S); + f->code=luaM_newvector(S->L,n,Instruction); + f->sizecode=n; + LoadVector(S,f->code,n,sizeof(Instruction)); +} + +static Proto* LoadFunction(LoadState* S, TString* p); + +static void LoadConstants(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->k=luaM_newvector(S->L,n,TValue); + f->sizek=n; + for (i=0; ik[i]); + for (i=0; ik[i]; + int t=LoadChar(S); + switch (t) + { + case LUA_TNIL: + setnilvalue(o); + break; + case LUA_TBOOLEAN: + setbvalue(o,LoadChar(S)!=0); + break; + case LUA_TNUMBER: + setnvalue(o,LoadNumber(S)); + break; + case LUA_TSTRING: + setsvalue2n(S->L,o,LoadString(S)); + break; + default: + error(S,"bad constant"); + break; + } + } + n=LoadInt(S); + f->p=luaM_newvector(S->L,n,Proto*); + f->sizep=n; + for (i=0; ip[i]=NULL; + for (i=0; ip[i]=LoadFunction(S,f->source); +} + +static void LoadDebug(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->lineinfo=luaM_newvector(S->L,n,int); + f->sizelineinfo=n; + LoadVector(S,f->lineinfo,n,sizeof(int)); + n=LoadInt(S); + f->locvars=luaM_newvector(S->L,n,LocVar); + f->sizelocvars=n; + for (i=0; ilocvars[i].varname=NULL; + for (i=0; ilocvars[i].varname=LoadString(S); + f->locvars[i].startpc=LoadInt(S); + f->locvars[i].endpc=LoadInt(S); + } + n=LoadInt(S); + f->upvalues=luaM_newvector(S->L,n,TString*); + f->sizeupvalues=n; + for (i=0; iupvalues[i]=NULL; + for (i=0; iupvalues[i]=LoadString(S); +} + +static Proto* LoadFunction(LoadState* S, TString* p) +{ + Proto* f; + if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep"); + f=luaF_newproto(S->L); + setptvalue2s(S->L,S->L->top,f); incr_top(S->L); + f->source=LoadString(S); if (f->source==NULL) f->source=p; + f->linedefined=LoadInt(S); + f->lastlinedefined=LoadInt(S); + f->nups=LoadByte(S); + f->numparams=LoadByte(S); + f->is_vararg=LoadByte(S); + f->maxstacksize=LoadByte(S); + LoadCode(S,f); + LoadConstants(S,f); + LoadDebug(S,f); + IF (!luaG_checkcode(f), "bad code"); + S->L->top--; + S->L->nCcalls--; + return f; +} + +static void LoadHeader(LoadState* S) +{ + char h[LUAC_HEADERSIZE]; + char s[LUAC_HEADERSIZE]; + luaU_header(h); + LoadBlock(S,s,LUAC_HEADERSIZE); + IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); +} + +/* +** load precompiled chunk +*/ +Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) +{ + LoadState S; + if (*name=='@' || *name=='=') + S.name=name+1; + else if (*name==LUA_SIGNATURE[0]) + S.name="binary string"; + else + S.name=name; + S.L=L; + S.Z=Z; + S.b=buff; + LoadHeader(&S); + return LoadFunction(&S,luaS_newliteral(L,"=?")); +} + +/* +* make header +*/ +void luaU_header (char* h) +{ + int x=1; + memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); + h+=sizeof(LUA_SIGNATURE)-1; + *h++=(char)LUAC_VERSION; + *h++=(char)LUAC_FORMAT; + *h++=(char)*(char*)&x; /* endianness */ + *h++=(char)sizeof(int); + *h++=(char)sizeof(size_t); + *h++=(char)sizeof(Instruction); + *h++=(char)sizeof(lua_Number); + *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */ +} diff --git a/src/server/game/LuaEngine/lua_src/lundump.h b/src/server/game/LuaEngine/lua_src/lundump.h new file mode 100644 index 0000000000..c80189dbff --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lundump.h @@ -0,0 +1,36 @@ +/* +** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include "lobject.h" +#include "lzio.h" + +/* load one chunk; from lundump.c */ +LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); + +/* make header; from lundump.c */ +LUAI_FUNC void luaU_header (char* h); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); + +#ifdef luac_c +/* print one chunk; from print.c */ +LUAI_FUNC void luaU_print (const Proto* f, int full); +#endif + +/* for header of binary files -- this is Lua 5.1 */ +#define LUAC_VERSION 0x51 + +/* for header of binary files -- this is the official format */ +#define LUAC_FORMAT 0 + +/* size of header of binary files */ +#define LUAC_HEADERSIZE 12 + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lvm.c b/src/server/game/LuaEngine/lua_src/lvm.c new file mode 100644 index 0000000000..e0a0cd8521 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lvm.c @@ -0,0 +1,767 @@ +/* +** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include + +#define lvm_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +/* limit for table tag-method chains (to avoid loops) */ +#define MAXTAGLOOP 100 + + +const TValue *luaV_tonumber (const TValue *obj, TValue *n) { + lua_Number num; + if (ttisnumber(obj)) return obj; + if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { + setnvalue(n, num); + return n; + } + else + return NULL; +} + + +int luaV_tostring (lua_State *L, StkId obj) { + if (!ttisnumber(obj)) + return 0; + else { + char s[LUAI_MAXNUMBER2STR]; + lua_Number n = nvalue(obj); + lua_number2str(s, n); + setsvalue2s(L, obj, luaS_new(L, s)); + return 1; + } +} + + +static void traceexec (lua_State *L, const Instruction *pc) { + lu_byte mask = L->hookmask; + const Instruction *oldpc = L->savedpc; + L->savedpc = pc; + if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { + resethookcount(L); + luaD_callhook(L, LUA_HOOKCOUNT, -1); + } + if (mask & LUA_MASKLINE) { + Proto *p = ci_func(L->ci)->l.p; + int npc = pcRel(pc, p); + int newline = getline(p, npc); + /* call linehook when enter a new function, when jump back (loop), + or when enter a new line */ + if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) + luaD_callhook(L, LUA_HOOKLINE, newline); + } +} + + +static void callTMres (lua_State *L, StkId res, const TValue *f, + const TValue *p1, const TValue *p2) { + ptrdiff_t result = savestack(L, res); + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + luaD_checkstack(L, 3); + L->top += 3; + luaD_call(L, L->top - 3, 1); + res = restorestack(L, result); + L->top--; + setobjs2s(L, res, L->top); +} + + + +static void callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + setobj2s(L, L->top+3, p3); /* 3th argument */ + luaD_checkstack(L, 4); + L->top += 4; + luaD_call(L, L->top - 4, 0); +} + + +void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + const TValue *res = luaH_get(h, key); /* do a primitive get */ + if (!ttisnil(res) || /* result is no nil? */ + (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ + setobj2s(L, val, res); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTMres(L, val, tm, t, key); + return; + } + t = tm; /* else repeat with `tm' */ + } + luaG_runerror(L, "loop in gettable"); +} + + +void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + TValue temp; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ + if (!ttisnil(oldval) || /* result is no nil? */ + (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ + setobj2t(L, oldval, val); + h->flags = 0; + luaC_barriert(L, h, val); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTM(L, tm, t, key, val); + return; + } + /* else repeat with `tm' */ + setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ + t = &temp; + } + luaG_runerror(L, "loop in settable"); +} + + +static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (ttisnil(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (ttisnil(tm)) return 0; + callTMres(L, res, tm, p1, p2); + return 1; +} + + +static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, + TMS event) { + const TValue *tm1 = fasttm(L, mt1, event); + const TValue *tm2; + if (tm1 == NULL) return NULL; /* no metamethod */ + if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ + tm2 = fasttm(L, mt2, event); + if (tm2 == NULL) return NULL; /* no metamethod */ + if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ + return tm1; + return NULL; +} + + +static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + const TValue *tm1 = luaT_gettmbyobj(L, p1, event); + const TValue *tm2; + if (ttisnil(tm1)) return -1; /* no metamethod? */ + tm2 = luaT_gettmbyobj(L, p2, event); + if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ + return -1; + callTMres(L, L->top, tm1, p1, p2); + return !l_isfalse(L->top); +} + + +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = ls->tsv.len; + const char *r = getstr(rs); + size_t lr = rs->tsv.len; + for (;;) { + int temp = strcoll(l, r); + if (temp != 0) return temp; + else { /* strings are equal up to a `\0' */ + size_t len = strlen(l); /* index of first `\0' in both strings */ + if (len == lr) /* r is finished? */ + return (len == ll) ? 0 : 1; + else if (len == ll) /* l is finished? */ + return -1; /* l is smaller than r (because r is not finished) */ + /* both strings longer than `len'; go on comparing (after the `\0') */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numlt(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; + else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) + return res; + return luaG_ordererror(L, l, r); +} + + +static int lessequal (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numle(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; + else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ + return res; + else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ + return !res; + return luaG_ordererror(L, l, r); +} + + +int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + lua_assert(ttype(t1) == ttype(t2)); + switch (ttype(t1)) { + case LUA_TNIL: return 1; + case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ + case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_TUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, + TM_EQ); + break; /* will try TM */ + } + case LUA_TTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) return 0; /* no TM? */ + callTMres(L, L->top, tm, t1, t2); /* call TM */ + return !l_isfalse(L->top); +} + + +void luaV_concat (lua_State *L, int total, int last) { + do { + StkId top = L->base + last + 1; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { + if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) + luaG_concaterror(L, top-2, top-1); + } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ + (void)tostring(L, top - 2); /* result is first op (as string) */ + else { + /* at least two string values; get as many as possible */ + size_t tl = tsvalue(top-1)->len; + char *buffer; + int i; + /* collect total length */ + for (n = 1; n < total && tostring(L, top-n-1); n++) { + size_t l = tsvalue(top-n-1)->len; + if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); + tl += l; + } + buffer = luaZ_openspace(L, &G(L)->buff, tl); + tl = 0; + for (i=n; i>0; i--) { /* concat all strings */ + size_t l = tsvalue(top-i)->len; + memcpy(buffer+tl, svalue(top-i), l); + tl += l; + } + setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + } + total -= n-1; /* got `n' strings to create 1 new */ + last -= n-1; + } while (total > 1); /* repeat until only 1 result left */ +} + + +static void Arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op) { + TValue tempb, tempc; + const TValue *b, *c; + if ((b = luaV_tonumber(rb, &tempb)) != NULL && + (c = luaV_tonumber(rc, &tempc)) != NULL) { + lua_Number nb = nvalue(b), nc = nvalue(c); + switch (op) { + case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; + case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; + case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; + case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; + case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; + case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; + case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; + default: lua_assert(0); break; + } + } + else if (!call_binTM(L, rb, rc, ra, op)) + luaG_aritherror(L, rb, rc); +} + + + +/* +** some macros for common tasks in `luaV_execute' +*/ + +#define runtime_check(L, c) { if (!(c)) break; } + +#define RA(i) (base+GETARG_A(i)) +/* to be used after possible stack reallocation */ +#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) +#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) +#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) +#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) +#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) + + +#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} + + +#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } + + +#define arith_op(op,tm) { \ + TValue *rb = RKB(i); \ + TValue *rc = RKC(i); \ + if (ttisnumber(rb) && ttisnumber(rc)) { \ + lua_Number nb = nvalue(rb), nc = nvalue(rc); \ + setnvalue(ra, op(nb, nc)); \ + } \ + else \ + Protect(Arith(L, ra, rb, rc, tm)); \ + } + + + +void luaV_execute (lua_State *L, int nexeccalls) { + LClosure *cl; + StkId base; + TValue *k; + const Instruction *pc; + reentry: /* entry point */ + lua_assert(isLua(L->ci)); + pc = L->savedpc; + cl = &clvalue(L->ci->func)->l; + base = L->base; + k = cl->p->k; + /* main loop of interpreter */ + for (;;) { + const Instruction i = *pc++; + StkId ra; + if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && + (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { + traceexec(L, pc); + if (L->status == LUA_YIELD) { /* did hook yield? */ + L->savedpc = pc - 1; + return; + } + base = L->base; + } + /* warning!! several calls may realloc the stack and invalidate `ra' */ + ra = RA(i); + lua_assert(base == L->base && L->base == L->ci->base); + lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); + lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); + switch (GET_OPCODE(i)) { + case OP_MOVE: { + setobjs2s(L, ra, RB(i)); + continue; + } + case OP_LOADK: { + setobj2s(L, ra, KBx(i)); + continue; + } + case OP_LOADBOOL: { + setbvalue(ra, GETARG_B(i)); + if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ + continue; + } + case OP_LOADNIL: { + TValue *rb = RB(i); + do { + setnilvalue(rb--); + } while (rb >= ra); + continue; + } + case OP_GETUPVAL: { + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v); + continue; + } + case OP_GETGLOBAL: { + TValue g; + TValue *rb = KBx(i); + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(rb)); + Protect(luaV_gettable(L, &g, rb, ra)); + continue; + } + case OP_GETTABLE: { + Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + continue; + } + case OP_SETGLOBAL: { + TValue g; + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(KBx(i))); + Protect(luaV_settable(L, &g, KBx(i), ra)); + continue; + } + case OP_SETUPVAL: { + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v, ra); + luaC_barrier(L, uv, ra); + continue; + } + case OP_SETTABLE: { + Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + continue; + } + case OP_NEWTABLE: { + int b = GETARG_B(i); + int c = GETARG_C(i); + sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); + Protect(luaC_checkGC(L)); + continue; + } + case OP_SELF: { + StkId rb = RB(i); + setobjs2s(L, ra+1, rb); + Protect(luaV_gettable(L, rb, RKC(i), ra)); + continue; + } + case OP_ADD: { + arith_op(luai_numadd, TM_ADD); + continue; + } + case OP_SUB: { + arith_op(luai_numsub, TM_SUB); + continue; + } + case OP_MUL: { + arith_op(luai_nummul, TM_MUL); + continue; + } + case OP_DIV: { + arith_op(luai_numdiv, TM_DIV); + continue; + } + case OP_MOD: { + arith_op(luai_nummod, TM_MOD); + continue; + } + case OP_POW: { + arith_op(luai_numpow, TM_POW); + continue; + } + case OP_UNM: { + TValue *rb = RB(i); + if (ttisnumber(rb)) { + lua_Number nb = nvalue(rb); + setnvalue(ra, luai_numunm(nb)); + } + else { + Protect(Arith(L, ra, rb, rb, TM_UNM)); + } + continue; + } + case OP_NOT: { + int res = l_isfalse(RB(i)); /* next assignment may change this value */ + setbvalue(ra, res); + continue; + } + case OP_LEN: { + const TValue *rb = RB(i); + switch (ttype(rb)) { + case LUA_TTABLE: { + setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); + break; + } + case LUA_TSTRING: { + setnvalue(ra, cast_num(tsvalue(rb)->len)); + break; + } + default: { /* try metamethod */ + Protect( + if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) + luaG_typeerror(L, rb, "get length of"); + ) + } + } + continue; + } + case OP_CONCAT: { + int b = GETARG_B(i); + int c = GETARG_C(i); + Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); + setobjs2s(L, RA(i), base+b); + continue; + } + case OP_JMP: { + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_EQ: { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + Protect( + if (equalobj(L, rb, rc) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LT: { + Protect( + if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LE: { + Protect( + if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_TEST: { + if (l_isfalse(ra) != GETARG_C(i)) + dojump(L, pc, GETARG_sBx(*pc)); + pc++; + continue; + } + case OP_TESTSET: { + TValue *rb = RB(i); + if (l_isfalse(rb) != GETARG_C(i)) { + setobjs2s(L, ra, rb); + dojump(L, pc, GETARG_sBx(*pc)); + } + pc++; + continue; + } + case OP_CALL: { + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + switch (luaD_precall(L, ra, nresults)) { + case PCRLUA: { + nexeccalls++; + goto reentry; /* restart luaV_execute over new Lua function */ + } + case PCRC: { + /* it was a C function (`precall' called it); adjust results */ + if (nresults >= 0) L->top = L->ci->top; + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_TAILCALL: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); + switch (luaD_precall(L, ra, LUA_MULTRET)) { + case PCRLUA: { + /* tail call: put new frame in place of previous one */ + CallInfo *ci = L->ci - 1; /* previous frame */ + int aux; + StkId func = ci->func; + StkId pfunc = (ci+1)->func; /* previous function index */ + if (L->openupval) luaF_close(L, ci->base); + L->base = ci->base = ci->func + ((ci+1)->base - pfunc); + for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ + setobjs2s(L, func+aux, pfunc+aux); + ci->top = L->top = func+aux; /* correct top */ + lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); + ci->savedpc = L->savedpc; + ci->tailcalls++; /* one more call lost */ + L->ci--; /* remove new frame */ + goto reentry; + } + case PCRC: { /* it was a C function (`precall' called it) */ + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_RETURN: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b-1; + if (L->openupval) luaF_close(L, base); + L->savedpc = pc; + b = luaD_poscall(L, ra); + if (--nexeccalls == 0) /* was previous function running `here'? */ + return; /* no: return */ + else { /* yes: continue its execution */ + if (b) L->top = L->ci->top; + lua_assert(isLua(L->ci)); + lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); + goto reentry; + } + } + case OP_FORLOOP: { + lua_Number step = nvalue(ra+2); + lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ + lua_Number limit = nvalue(ra+1); + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + dojump(L, pc, GETARG_sBx(i)); /* jump back */ + setnvalue(ra, idx); /* update internal index... */ + setnvalue(ra+3, idx); /* ...and external index */ + } + continue; + } + case OP_FORPREP: { + const TValue *init = ra; + const TValue *plimit = ra+1; + const TValue *pstep = ra+2; + L->savedpc = pc; /* next steps may throw errors */ + if (!tonumber(init, ra)) + luaG_runerror(L, LUA_QL("for") " initial value must be a number"); + else if (!tonumber(plimit, ra+1)) + luaG_runerror(L, LUA_QL("for") " limit must be a number"); + else if (!tonumber(pstep, ra+2)) + luaG_runerror(L, LUA_QL("for") " step must be a number"); + setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_TFORLOOP: { + StkId cb = ra + 3; /* call base */ + setobjs2s(L, cb+2, ra+2); + setobjs2s(L, cb+1, ra+1); + setobjs2s(L, cb, ra); + L->top = cb+3; /* func. + 2 args (state and index) */ + Protect(luaD_call(L, cb, GETARG_C(i))); + L->top = L->ci->top; + cb = RA(i) + 3; /* previous call may change the stack */ + if (!ttisnil(cb)) { /* continue loop? */ + setobjs2s(L, cb-1, cb); /* save control variable */ + dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ + } + pc++; + continue; + } + case OP_SETLIST: { + int n = GETARG_B(i); + int c = GETARG_C(i); + int last; + Table *h; + if (n == 0) { + n = cast_int(L->top - ra) - 1; + L->top = L->ci->top; + } + if (c == 0) c = cast_int(*pc++); + runtime_check(L, ttistable(ra)); + h = hvalue(ra); + last = ((c-1)*LFIELDS_PER_FLUSH) + n; + if (last > h->sizearray) /* needs more space? */ + luaH_resizearray(L, h, last); /* pre-alloc it at once */ + for (; n > 0; n--) { + TValue *val = ra+n; + setobj2t(L, luaH_setnum(L, h, last--), val); + luaC_barriert(L, h, val); + } + continue; + } + case OP_CLOSE: { + luaF_close(L, ra); + continue; + } + case OP_CLOSURE: { + Proto *p; + Closure *ncl; + int nup, j; + p = cl->p->p[GETARG_Bx(i)]; + nup = p->nups; + ncl = luaF_newLclosure(L, nup, cl->env); + ncl->l.p = p; + for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)]; + else { + lua_assert(GET_OPCODE(*pc) == OP_MOVE); + ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); + } + } + setclvalue(L, ra, ncl); + Protect(luaC_checkGC(L)); + continue; + } + case OP_VARARG: { + int b = GETARG_B(i) - 1; + int j; + CallInfo *ci = L->ci; + int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; + if (b == LUA_MULTRET) { + Protect(luaD_checkstack(L, n)); + ra = RA(i); /* previous call may change the stack */ + b = n; + L->top = ra + n; + } + for (j = 0; j < b; j++) { + if (j < n) { + setobjs2s(L, ra + j, ci->base - n + j); + } + else { + setnilvalue(ra + j); + } + } + continue; + } + } + } +} + diff --git a/src/server/game/LuaEngine/lua_src/lvm.h b/src/server/game/LuaEngine/lua_src/lvm.h new file mode 100644 index 0000000000..bfe4f5678d --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lvm.h @@ -0,0 +1,36 @@ +/* +** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) + +#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \ + (((o) = luaV_tonumber(o,n)) != NULL)) + +#define equalobj(L,o1,o2) \ + (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) + + +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); +LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); +LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); +LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); + +#endif diff --git a/src/server/game/LuaEngine/lua_src/lzio.c b/src/server/game/LuaEngine/lua_src/lzio.c new file mode 100644 index 0000000000..293edd59b0 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lzio.c @@ -0,0 +1,82 @@ +/* +** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** a generic input stream interface +** See Copyright Notice in lua.h +*/ + + +#include + +#define lzio_c +#define LUA_CORE + +#include "lua.h" + +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) return EOZ; + z->n = size - 1; + z->p = buff; + return char2int(*(z->p++)); +} + + +int luaZ_lookahead (ZIO *z) { + if (z->n == 0) { + if (luaZ_fill(z) == EOZ) + return EOZ; + else { + z->n++; /* luaZ_fill removed first byte; put back it */ + z->p--; + } + } + return char2int(*z->p); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (luaZ_lookahead(z) == EOZ) + return n; /* return number of missing bytes */ + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { + if (n > buff->buffsize) { + if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; + luaZ_resizebuffer(L, buff, n); + } + return buff->buffer; +} + + diff --git a/src/server/game/LuaEngine/lua_src/lzio.h b/src/server/game/LuaEngine/lua_src/lzio.h new file mode 100644 index 0000000000..51d695d8c1 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/lzio.h @@ -0,0 +1,67 @@ +/* +** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define char2int(c) cast(int, cast(unsigned char, (c))) + +#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z)) + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ +LUAI_FUNC int luaZ_lookahead (ZIO *z); + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; + void* data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif diff --git a/src/server/game/LuaEngine/lua_src/print.c b/src/server/game/LuaEngine/lua_src/print.c new file mode 100644 index 0000000000..e240cfc3c6 --- /dev/null +++ b/src/server/game/LuaEngine/lua_src/print.c @@ -0,0 +1,227 @@ +/* +** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $ +** print bytecodes +** See Copyright Notice in lua.h +*/ + +#include +#include + +#define luac_c +#define LUA_CORE + +#include "ldebug.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lundump.h" + +#define PrintFunction luaU_print + +#define Sizeof(x) ((int)sizeof(x)) +#define VOID(p) ((const void*)(p)) + +static void PrintString(const TString* ts) +{ + const char* s=getstr(ts); + size_t i,n=ts->tsv.len; + putchar('"'); + for (i=0; ik[i]; + switch (ttype(o)) + { + case LUA_TNIL: + printf("nil"); + break; + case LUA_TBOOLEAN: + printf(bvalue(o) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf(LUA_NUMBER_FMT,nvalue(o)); + break; + case LUA_TSTRING: + PrintString(rawtsvalue(o)); + break; + default: /* cannot happen */ + printf("? type=%d",ttype(o)); + break; + } +} + +static void PrintCode(const Proto* f) +{ + const Instruction* code=f->code; + int pc,n=f->sizecode; + for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); + printf("%-9s\t",luaP_opnames[o]); + switch (getOpMode(o)) + { + case iABC: + printf("%d",a); + if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); + if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); + break; + case iABx: + if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); + break; + case iAsBx: + if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); + break; + } + switch (o) + { + case OP_LOADK: + printf("\t; "); PrintConstant(f,bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); + break; + case OP_GETGLOBAL: + case OP_SETGLOBAL: + printf("\t; %s",svalue(&f->k[bx])); + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) + { + printf("\t; "); + if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); + printf(" "); + if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + printf("\t; to %d",sbx+pc+2); + break; + case OP_CLOSURE: + printf("\t; %p",VOID(f->p[bx])); + break; + case OP_SETLIST: + if (c==0) printf("\t; %d",(int)code[++pc]); + else printf("\t; %d",c); + break; + default: + break; + } + printf("\n"); + } +} + +#define SS(x) (x==1)?"":"s" +#define S(x) x,SS(x) + +static void PrintHeader(const Proto* f) +{ + const char* s=getstr(f->source); + if (*s=='@' || *s=='=') + s++; + else if (*s==LUA_SIGNATURE[0]) + s="(bstring)"; + else + s="(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", + (f->linedefined==0)?"main":"function",s, + f->linedefined,f->lastlinedefined, + S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", + f->numparams,f->is_vararg?"+":"",SS(f->numparams), + S(f->maxstacksize),S(f->nups)); + printf("%d local%s, %d constant%s, %d function%s\n", + S(f->sizelocvars),S(f->sizek),S(f->sizep)); +} + +static void PrintConstants(const Proto* f) +{ + int i,n=f->sizek; + printf("constants (%d) for %p:\n",n,VOID(f)); + for (i=0; isizelocvars; + printf("locals (%d) for %p:\n",n,VOID(f)); + for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); + } +} + +static void PrintUpvalues(const Proto* f) +{ + int i,n=f->sizeupvalues; + printf("upvalues (%d) for %p:\n",n,VOID(f)); + if (f->upvalues==NULL) return; + for (i=0; iupvalues[i])); + } +} + +void PrintFunction(const Proto* f, int full) +{ + int i,n=f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) + { + PrintConstants(f); + PrintLocals(f); + PrintUpvalues(f); + } + for (i=0; ip[i],full); +} diff --git a/src/server/game/LuaEngine/methods/CustomMethodsInterface.h b/src/server/game/LuaEngine/methods/CustomMethodsInterface.h new file mode 100644 index 0000000000..2541b1653d --- /dev/null +++ b/src/server/game/LuaEngine/methods/CustomMethodsInterface.h @@ -0,0 +1,19 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef CUSTOMMETHODSINT_H +#define CUSTOMMETHODSINT_H + +#ifdef ELUNA_USE_CUSTOM_METHODS + #include "CustomMethods.h" +#else +namespace LuaCustom +{ + inline void RegisterCustomMethods([[maybe_unused]] Eluna* E) {} +} +#endif + +#endif diff --git a/src/server/game/LuaEngine/methods/Methods.cpp b/src/server/game/LuaEngine/methods/Methods.cpp new file mode 100644 index 0000000000..056cdc0ed9 --- /dev/null +++ b/src/server/game/LuaEngine/methods/Methods.cpp @@ -0,0 +1,130 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +// Eluna +#include "LuaEngine.h" +#include "ElunaEventMgr.h" +#include "ElunaIncludes.h" +#include "ElunaTemplate.h" +#include "ElunaUtility.h" + +// Method includes +#include "GlobalMethods.h" +#include "ObjectMethods.h" +#include "WorldObjectMethods.h" +#include "UnitMethods.h" +#include "PlayerMethods.h" +#include "CreatureMethods.h" + + +#include "GameObjectMethods.h" + +#include "AuraMethods.h" +#include "AuraEffectMethods.h" + + + + + + +#include "MapMethods.h" + + + +#include "BigIntMethods.h" +#include "CustomMethodsInterface.h" + +void RegisterMethods(Eluna* E) +{ + ElunaTemplate<>::SetMethods(E, LuaGlobalFunctions::GlobalMethods); + + ElunaTemplate::Register(E, "Object"); + ElunaTemplate::SetMethods(E, LuaObject::ObjectMethods); + + ElunaTemplate::Register(E, "WorldObject"); + ElunaTemplate::SetMethods(E, LuaObject::ObjectMethods); + ElunaTemplate::SetMethods(E, LuaWorldObject::WorldObjectMethods); + + ElunaTemplate::Register(E, "Unit"); + ElunaTemplate::SetMethods(E, LuaObject::ObjectMethods); + ElunaTemplate::SetMethods(E, LuaWorldObject::WorldObjectMethods); + ElunaTemplate::SetMethods(E, LuaUnit::UnitMethods); + + ElunaTemplate::Register(E, "Player"); + ElunaTemplate::SetMethods(E, LuaObject::ObjectMethods); + ElunaTemplate::SetMethods(E, LuaWorldObject::WorldObjectMethods); + ElunaTemplate::SetMethods(E, LuaUnit::UnitMethods); + ElunaTemplate::SetMethods(E, LuaPlayer::PlayerMethods); + + ElunaTemplate::Register(E, "Creature"); + ElunaTemplate::SetMethods(E, LuaObject::ObjectMethods); + ElunaTemplate::SetMethods(E, LuaWorldObject::WorldObjectMethods); + ElunaTemplate::SetMethods(E, LuaUnit::UnitMethods); + ElunaTemplate::SetMethods(E, LuaCreature::CreatureMethods); + + ElunaTemplate::Register(E, "GameObject"); + ElunaTemplate::SetMethods(E, LuaObject::ObjectMethods); + ElunaTemplate::SetMethods(E, LuaWorldObject::WorldObjectMethods); + ElunaTemplate::SetMethods(E, LuaGameObject::GameObjectMethods); + + + + + + + + + + + + + + + + + ElunaTemplate::Register(E, "Aura"); + ElunaTemplate::SetMethods(E, LuaAura::AuraMethods); + + ElunaTemplate::Register(E, "AuraEffect"); + ElunaTemplate::SetMethods(E, LuaAuraEffects::AuraEffectMethods); + + + + + + + + + + + + + + ElunaTemplate::Register(E, "Map"); + ElunaTemplate::SetMethods(E, LuaMap::MapMethods); + + + + + + + + + + + ElunaTemplate::Register(E, "long long"); + ElunaTemplate::SetMethods(E, LuaBigInt::LongLongMethods); + + ElunaTemplate::Register(E, "unsigned long long"); + ElunaTemplate::SetMethods(E, LuaBigInt::ULongLongMethods); + + ElunaTemplate::Register(E, "ObjectGuid"); + ElunaTemplate::SetMethods(E, LuaBigInt::ObjectGuidMethods); + + LuaCustom::RegisterCustomMethods(E); + + LuaVal::Register(E->L); +} diff --git a/src/server/game/LuaEngine/methods/README.md b/src/server/game/LuaEngine/methods/README.md new file mode 100644 index 0000000000..8dcf205f88 --- /dev/null +++ b/src/server/game/LuaEngine/methods/README.md @@ -0,0 +1,40 @@ +## How to Add Custom Methods + +You can extend the available methods by creating a directory named `Custom` in the `Methods` directory, and adding a header file named `CustomMethods.h` inside it. + +Once this file is added, re-run cmake and recompile. + +## Example: `Custom/CustomMethods.h` + +```cpp +#include "ElunaTemplate.h" +#include "ElunaIncludes.h" + +#ifndef CUSTOMMETHODS_H +#define CUSTOMMETHODS_H + +namespace LuaCustom +{ + // Define a custom method that returns the players name + int CustomPlayerMethod(Eluna* E, Player* player) + { + E->Push(player->GetName()); + return 1; + } + + // Create a custom player method registry + ElunaRegister CustomPlayerMethods[] = + { + // Add the custom player method to the registry + { "CustomPlayerMethod", &LuaCustom::CustomPlayerMethod }, + }; + + inline void RegisterCustomMethods([[maybe_unused]] Eluna* E) + { + // Append all the custom Player methods to the Player object + ElunaTemplate::SetMethods(E, CustomPlayerMethods); + }; +}; + +#endif +``` \ No newline at end of file diff --git a/src/server/game/LuaEngine/methods/TrinityCore/AuraEffectMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/AuraEffectMethods.h new file mode 100644 index 0000000000..0b1a9867be --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/AuraEffectMethods.h @@ -0,0 +1,326 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef AURAEFFECTMETHODS_H +#define AURAEFFECTMETHODS_H + + +namespace LuaAuraEffects +{ + /** + * Returns the [Aura] that this [AuraEffect] belongs to. + * + * @return [Aura] base + */ + int GetBase(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetBase()); + return 1; + } + + /** + * Returns the base amount of the [AuraEffect]. + * + * @return int32 baseAmount + */ + int GetBaseAmount(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetBaseAmount()); + return 1; + } + + /** + * Returns the amplitude in milliseconds of the [AuraEffect]. + * + * @return uint32 amplitude + */ + int GetAmplitude(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetPeriod()); + return 1; + } + + /** + * Returns the current amount of the [AuraEffect]. + * + * @return int32 amount + */ + int GetAmount(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetAmount()); + return 1; + } + + /** + * Sets the amount of the [AuraEffect]. + * + * @param int32 amount : the amount to set + */ + int SetAmount(Eluna* E, AuraEffect* aurEff) + { + int32 amount = E->CHECKVAL(2); + aurEff->SetAmount(amount); + return 0; + } + + /** + * Returns the remaining time in milliseconds until the next tick of the [AuraEffect]. + * + * @return int32 periodicTimer + */ + int GetPeriodicTimer(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetPeriodicTimer()); + return 1; + } + + /** + * Sets the periodic timer of the [AuraEffect]. + * + * @param int32 timer : the timer value in milliseconds to set + */ + int SetPeriodicTimer(Eluna* E, AuraEffect* aurEff) + { + int32 timer = E->CHECKVAL(2); + aurEff->SetPeriodicTimer(timer); + return 0; + } + + /** + * Calculates and returns the amount of the [AuraEffect] for the given caster. + * + * @param [Unit] caster : the caster to calculate the amount for + * @return int32 amount + */ + int CalculateAmount(Eluna* E, AuraEffect* aurEff) + { + Unit* caster = E->CHECKOBJ(2); + E->Push(aurEff->CalculateAmount(caster)); + return 1; + } + + /** + * Calculates the periodic properties of the [AuraEffect] for the given caster. + * + * @param [Unit] caster : the caster to calculate for + * @param bool resetPeriodicTimer = false : whether to reset the periodic timer + * @param bool load = false : whether this is being loaded from the database + */ + int CalculatePeriodic(Eluna* E, AuraEffect* aurEff) + { + Unit* caster = E->CHECKOBJ(2); + bool resetPeriodicTimer = E->CHECKVAL(3, false); + bool load = E->CHECKVAL(4, false); + aurEff->CalculatePeriodic(caster, resetPeriodicTimer, load); + return 0; + } + + /** + * Changes the amount of the [AuraEffect]. + * + * @param int32 newAmount : the new amount to set + * @param bool mark = true : whether to mark the effect as changed + * @param bool onStackOrReapply = false : whether this change is due to a stack or reapply + */ + int ChangeAmount(Eluna* E, AuraEffect* aurEff) + { + int32 newAmount = E->CHECKVAL(2); + bool mark = E->CHECKVAL(3, true); + bool onStackOrReapply = E->CHECKVAL(4, false); + aurEff->ChangeAmount(newAmount, mark, onStackOrReapply); + return 0; + } + + /** + * Recalculates the amount of the [AuraEffect]. + */ + int RecalculateAmount(Eluna* /*E*/, AuraEffect* aurEff) + { + aurEff->RecalculateAmount(); + return 0; + } + + /** + * Returns `true` if the [AuraEffect] amount can be recalculated, `false` otherwise. + * + * @return bool canBeRecalculated + */ + int CanBeRecalculated(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->CanBeRecalculated()); + return 1; + } + + /** + * Sets whether the [AuraEffect] amount can be recalculated. + * + * @param bool val = true : set to 'true' to allow recalculation + */ + int SetCanBeRecalculated(Eluna* E, AuraEffect* aurEff) + { + bool val = E->CHECKVAL(2, true); + aurEff->SetCanBeRecalculated(val); + return 0; + } + + /** + * Returns the current tick number of the [AuraEffect]. + * + * @return uint32 tickNumber + */ + int GetTickNumber(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetTickNumber()); + return 1; + } + + /** + * Returns the number of remaining ticks of the [AuraEffect]. + * + * @return uint32 remainingTicks + */ + #if 0 + int GetRemainingTicks(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetRemainingTicks()); + return 1; + } + #endif + /** + * Returns the total number of ticks of the [AuraEffect]. + * + * @return uint32 totalTicks + */ + int GetTotalTicks(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetTotalTicks()); + return 1; + } + + /** + * Returns a table of all [Unit]s currently affected by this [AuraEffect]. + * + * @return table targets : a table of [Unit] objects + */ + int GetTargetList(Eluna* E, AuraEffect* aurEff) + { + std::list list; + aurEff->GetTargetList(list); + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + /** + * Returns the spell ID of the [AuraEffect]. + * + * @return uint32 id + */ + int GetId(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetId()); + return 1; + } + + /** + * Returns the effect index of the [AuraEffect]. + * + * @return uint8 effIndex + */ + int GetEffIndex(Eluna* E, AuraEffect* aurEff) + { + E->Push(uint8(aurEff->GetEffIndex())); + return 1; + } + + /** + * Returns the aura type of the [AuraEffect]. + * + * @return uint16 auraType + */ + int GetAuraType(Eluna* E, AuraEffect* aurEff) + { + E->Push(uint16(aurEff->GetAuraType())); + return 1; + } + + /** + * Returns the [Unit] that cast the [AuraEffect]. + * + * @return [Unit] caster + */ + int GetCaster(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetCaster()); + return 1; + } + + /** + * Returns the GUID of the [Unit] that cast the [AuraEffect]. + * + * @return ObjectGuid casterGUID + */ + int GetCasterGUID(Eluna* E, AuraEffect* aurEff) + { + E->Push(aurEff->GetCasterGUID()); + return 1; + } + + /** + * Returns the [SpellInfo] of the spell that created this [AuraEffect]. + * + * @return [SpellInfo] spellInfo + */ + int GetSpellInfo(Eluna* E, AuraEffect* aurEff) + { + ElunaSpellInfo info(aurEff->GetId()); + E->Push(&info); + return 1; + } + + ElunaRegister AuraEffectMethods[] = + { + // Getters + { "GetBase", &LuaAuraEffects::GetBase }, + { "GetBaseAmount", &LuaAuraEffects::GetBaseAmount }, + { "GetAmplitude", &LuaAuraEffects::GetAmplitude }, + { "GetAmount", &LuaAuraEffects::GetAmount }, + { "GetPeriodicTimer", &LuaAuraEffects::GetPeriodicTimer }, + { "GetTickNumber", &LuaAuraEffects::GetTickNumber }, + + { "GetTotalTicks", &LuaAuraEffects::GetTotalTicks }, + { "GetTargetList", &LuaAuraEffects::GetTargetList }, + { "GetId", &LuaAuraEffects::GetId }, + { "GetEffIndex", &LuaAuraEffects::GetEffIndex }, + { "GetAuraType", &LuaAuraEffects::GetAuraType }, + { "GetCaster", &LuaAuraEffects::GetCaster }, + { "GetCasterGUID", &LuaAuraEffects::GetCasterGUID }, + { "GetSpellInfo", &LuaAuraEffects::GetSpellInfo }, + // Setters + { "SetAmount", &LuaAuraEffects::SetAmount }, + { "SetPeriodicTimer", &LuaAuraEffects::SetPeriodicTimer }, + { "SetCanBeRecalculated", &LuaAuraEffects::SetCanBeRecalculated }, + // Booleans + { "CanBeRecalculated", &LuaAuraEffects::CanBeRecalculated }, + // Other + { "CalculateAmount", &LuaAuraEffects::CalculateAmount }, + { "CalculatePeriodic", &LuaAuraEffects::CalculatePeriodic }, + { "ChangeAmount", &LuaAuraEffects::ChangeAmount }, + { "RecalculateAmount", &LuaAuraEffects::RecalculateAmount }, + }; +} + +#endif + diff --git a/src/server/game/LuaEngine/methods/TrinityCore/AuraMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/AuraMethods.h new file mode 100644 index 0000000000..3975895c5c --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/AuraMethods.h @@ -0,0 +1,206 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef AURAMETHODS_H +#define AURAMETHODS_H + +/*** + * The persistent effect of a [Spell] that remains on a [Unit] after the [Spell] + * has been cast. + * + * As an example, if you cast a damage-over-time spell on a target, an [Aura] is + * put on the target that deals damage continuously. + * + * [Aura]s on your player are displayed in-game as a series of icons to the left + * of the mini-map. + * + * Inherits all methods from: none + */ +namespace LuaAura +{ + /** + * Returns the [Unit] that casted the [Spell] that caused this [Aura] to be applied. + * + * @return [Unit] caster + */ + int GetCaster(Eluna* E, Aura* aura) + { + E->Push(aura->GetCaster()); + return 1; + } + + /** + * Returns the GUID of the [Unit] that casted the [Spell] that caused this [Aura] to be applied. + * + * @return string caster_guid : the GUID of the Unit as a decimal string + */ + int GetCasterGUID(Eluna* E, Aura* aura) + { + E->Push(aura->GetCasterGUID()); + return 1; + } + + /** + * Returns the level of the [Unit] that casted the [Spell] that caused this [Aura] to be applied. + * + * @return uint32 caster_level + */ + #if 0 + int GetCasterLevel(Eluna* E, Aura* aura) + { + E->Push(aura->GetCaster()->GetLevel()); + return 1; + } + #endif + /** + * Returns the amount of time left until the [Aura] expires. + * + * @return int32 duration : amount of time left in milliseconds + */ + int GetDuration(Eluna* E, Aura* aura) + { + E->Push(aura->GetDuration()); + return 1; + } + + /** + * Returns the ID of the [Spell] that caused this [Aura] to be applied. + * + * @return uint32 aura_id + */ + int GetAuraId(Eluna* E, Aura* aura) + { + E->Push(aura->GetId()); + return 1; + } + + /** + * Returns the amount of time this [Aura] lasts when applied. + * + * To determine how much time has passed since this Aura was applied, + * subtract the result of [Aura]:GetDuration from the result of this method. + * + * @return int32 max_duration : the maximum duration of the Aura, in milliseconds + */ + int GetMaxDuration(Eluna* E, Aura* aura) + { + E->Push(aura->GetMaxDuration()); + return 1; + } + + /** + * Returns the number of times the [Aura] has "stacked". + * + * This is the same as the number displayed on the [Aura]'s icon in-game. + * + * @return uint32 stack_amount + */ + int GetStackAmount(Eluna* E, Aura* aura) + { + E->Push(aura->GetStackAmount()); + return 1; + } + + /** + * Returns the [Unit] that the [Aura] has been applied to. + * + * @return [Unit] owner + */ + int GetOwner(Eluna* E, Aura* aura) + { + E->Push(aura->GetOwner()); + return 1; + } + + /** + * Change the amount of time before the [Aura] expires. + * + * @param int32 duration : the new duration of the Aura, in milliseconds + */ + int SetDuration(Eluna* E, Aura* aura) + { + int32 duration = E->CHECKVAL(2); + + aura->SetDuration(duration); + return 0; + } + + /** + * Change the maximum amount of time before the [Aura] expires. + * + * This does not affect the current duration of the [Aura], but if the [Aura] + * is reset to the maximum duration, it will instead change to `duration`. + * + * @param int32 duration : the new maximum duration of the Aura, in milliseconds + */ + int SetMaxDuration(Eluna* E, Aura* aura) + { + int32 duration = E->CHECKVAL(2); + + aura->SetMaxDuration(duration); + return 0; + } + + /** + * Change the amount of times the [Aura] has "stacked" on the [Unit]. + * + * If `amount` is greater than or equal to the current number of stacks, + * then the [Aura] has its duration reset to the maximum duration. + * + * @param uint8 amount + */ + int SetStackAmount(Eluna* E, Aura* aura) + { + uint8 amount = E->CHECKVAL(2); + + aura->SetStackAmount(amount); + return 0; + } + + /** + * Remove this [Aura] from the [Unit] it is applied to. + */ + int Remove(Eluna* /*E*/, Aura* aura) + { + aura->Remove(); + return 0; + } + + /** + * Returns the [SpellInfo] of the spell that created this [Aura]. + * + * @return [SpellInfo] spellInfo + */ + int GetSpellInfo(Eluna* E, Aura* aura) + { + ElunaSpellInfo info(aura->GetId()); + E->Push(&info); + return 1; + } + + ElunaRegister AuraMethods[] = + { + // Getters + { "GetCaster", &LuaAura::GetCaster }, + { "GetCasterGUID", &LuaAura::GetCasterGUID }, + + { "GetDuration", &LuaAura::GetDuration }, + { "GetMaxDuration", &LuaAura::GetMaxDuration }, + { "GetAuraId", &LuaAura::GetAuraId }, + { "GetStackAmount", &LuaAura::GetStackAmount }, + { "GetOwner", &LuaAura::GetOwner }, + { "GetSpellInfo", &LuaAura::GetSpellInfo }, + + // Setters + { "SetDuration", &LuaAura::SetDuration }, + { "SetMaxDuration", &LuaAura::SetMaxDuration }, + { "SetStackAmount", &LuaAura::SetStackAmount }, + + // Other + { "Remove", &LuaAura::Remove } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/BattleGroundMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/BattleGroundMethods.h new file mode 100644 index 0000000000..8eff793fb5 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/BattleGroundMethods.h @@ -0,0 +1,248 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef BATTLEGROUNDMETHODS_H +#define BATTLEGROUNDMETHODS_H + +/*** + * Contains the state of a battleground, e.g. Warsong Gulch, Arathi Basin, etc. + * + * Inherits all methods from: none + */ +namespace LuaBattleGround +{ + /** + * Returns the name of the [BattleGround]. + * + * @return string name + */ + int GetName(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetName()); + return 1; + } + + /** + * Returns the amount of alive players in the [BattleGround] by the team ID. + * + * @param [Team] team : team ID + * @return uint32 count + */ + int GetAlivePlayersCountByTeam(Eluna* E, BattleGround* bg) + { + uint32 team = E->CHECKVAL(2); + + E->Push(bg->GetAlivePlayersCountByTeam((Team)team)); + return 1; + } + + /** + * Returns the [Map] of the [BattleGround]. + * + * @return [Map] map + */ + int GetMap(Eluna* E, BattleGround* bg) + { + E->Push(static_cast(bg->GetBgMap())); + return 1; + } + + /** + * Returns the bonus honor given by amount of kills in the specific [BattleGround]. + * + * @param uint32 kills : amount of kills + * @return uint32 bonusHonor + */ + int GetBonusHonorFromKillCount(Eluna* E, BattleGround* bg) + { + uint32 kills = E->CHECKVAL(2); + + E->Push(bg->GetBonusHonorFromKill(kills)); + return 1; + } + + /** + * Returns the bracket ID of the specific [BattleGround]. + * + * @return [BattleGroundBracketId] bracketId + */ + int GetBracketId(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetBracketId()); + return 1; + } + + /** + * Returns the time remaining in milliseconds until [BattleGround] closes and removes all players. + * This can be after battleground ends normally or when there are not enough players + * + * @return uint32 endTime + */ + int GetEndTime(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetRemainingTime()); + return 1; + } + + /** + * Returns the amount of free slots for the selected team in the specific [BattleGround]. + * + * @param [Team] team : team ID + * @return uint32 freeSlots + */ + int GetFreeSlotsForTeam(Eluna* E, BattleGround* bg) + { + uint32 team = E->CHECKVAL(2); + + E->Push(bg->GetFreeSlotsForTeam((Team)team)); + return 1; + } + + /** + * Returns the instance ID of the [BattleGround]. + * + * @return uint32 instanceId + */ + int GetInstanceId(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetInstanceID()); + return 1; + } + + /** + * Returns the map ID of the [BattleGround]. + * + * @return uint32 mapId + */ + int GetMapId(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMapId()); + return 1; + } + + /** + * Returns the type ID of the [BattleGround]. + * + * @return [BattleGroundTypeId] typeId + */ + int GetTypeId(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetTypeID()); + return 1; + } + + /** + * Returns the max allowed [Player] level of the specific [BattleGround]. + * + * @return uint32 maxLevel + */ + int GetMaxLevel(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMaxLevel()); + return 1; + } + + /** + * Returns the minimum allowed [Player] level of the specific [BattleGround]. + * + * @return uint32 minLevel + */ + int GetMinLevel(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMinLevel()); + return 1; + } + + /** + * Returns the maximum allowed [Player] count of the specific [BattleGround]. + * + * @return uint32 maxPlayerCount + */ + int GetMaxPlayers(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMaxPlayers()); + return 1; + } + + /** + * Returns the minimum allowed [Player] count of the specific [BattleGround]. + * + * @return uint32 minPlayerCount + */ + int GetMinPlayers(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMinPlayers()); + return 1; + } + + /** + * Returns the maximum allowed [Player] count per team of the specific [BattleGround]. + * + * @return uint32 maxTeamPlayerCount + */ + int GetMaxPlayersPerTeam(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMaxPlayersPerTeam()); + return 1; + } + + /** + * Returns the minimum allowed [Player] count per team of the specific [BattleGround]. + * + * @return uint32 minTeamPlayerCount + */ + int GetMinPlayersPerTeam(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetMinPlayersPerTeam()); + return 1; + } + + /** + * Returns the winning team of the specific [BattleGround]. + * + * @return [Team] team + */ + int GetWinner(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetWinner()); + return 1; + } + + /** + * Returns the status of the specific [BattleGround]. + * + * @return [BattleGroundStatus] status + */ + int GetStatus(Eluna* E, BattleGround* bg) + { + E->Push(bg->GetStatus()); + return 1; + } + + ElunaRegister BattleGroundMethods[] = + { + // Getters + { "GetName", &LuaBattleGround::GetName }, + { "GetAlivePlayersCountByTeam", &LuaBattleGround::GetAlivePlayersCountByTeam }, + { "GetMap", &LuaBattleGround::GetMap }, + { "GetBonusHonorFromKillCount", &LuaBattleGround::GetBonusHonorFromKillCount }, + { "GetBracketId", &LuaBattleGround::GetBracketId }, + { "GetEndTime", &LuaBattleGround::GetEndTime }, + { "GetFreeSlotsForTeam", &LuaBattleGround::GetFreeSlotsForTeam }, + { "GetInstanceId", &LuaBattleGround::GetInstanceId }, + { "GetMapId", &LuaBattleGround::GetMapId }, + { "GetTypeId", &LuaBattleGround::GetTypeId }, + { "GetMaxLevel", &LuaBattleGround::GetMaxLevel }, + { "GetMinLevel", &LuaBattleGround::GetMinLevel }, + { "GetMaxPlayers", &LuaBattleGround::GetMaxPlayers }, + { "GetMinPlayers", &LuaBattleGround::GetMinPlayers }, + { "GetMaxPlayersPerTeam", &LuaBattleGround::GetMaxPlayersPerTeam }, + { "GetMinPlayersPerTeam", &LuaBattleGround::GetMinPlayersPerTeam }, + { "GetWinner", &LuaBattleGround::GetWinner }, + { "GetStatus", &LuaBattleGround::GetStatus } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/BigIntMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/BigIntMethods.h new file mode 100644 index 0000000000..a69ed5009f --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/BigIntMethods.h @@ -0,0 +1,143 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef BIGINTMETHODS_H +#define BIGINTMETHODS_H + +namespace LuaBigInt +{ + template + constexpr int PerformOp(Eluna* E, Op op) + { + T val1 = E->CHECKVAL(1); + T val2 = E->CHECKVAL(2); + E->Push(op(val1, val2)); + return 1; + } + + template + int Add(Eluna* E, T*) + { + return PerformOp(E, std::plus{}); + } + + template + int Subtract(Eluna* E, T*) + { + return PerformOp(E, std::minus{}); + } + + template + int Multiply(Eluna* E, T*) + { + return PerformOp(E, std::multiplies{}); + } + + template + int Divide(Eluna* E, T*) + { + return PerformOp(E, std::divides{}); + } + + template + int Mod(Eluna* E, T*) + { + return PerformOp(E, std::modulus{}); + } + + template + int UnaryMinus(Eluna* E, T*) + { + T val = E->CHECKVAL(1); + E->Push(std::negate{}(val)); + return 1; + } + + template + int Equal(Eluna* E, T*) + { + return PerformOp(E, std::equal_to{}); + } + + template + int Less(Eluna* E, T*) + { + return PerformOp(E, std::less{}); + } + + template + int LessOrEqual(Eluna* E, T*) + { + return PerformOp(E, std::less_equal{}); + } + + template + int ToString(Eluna* E, T*) + { + T val = E->CHECKVAL(1); + std::ostringstream ss; + ss << val; + E->Push(ss.str()); + return 1; + } + + template + int Pow(Eluna* E, T*) + { + T val1 = E->CHECKVAL(1); + T val2 = E->CHECKVAL(2); + E->Push(static_cast(powl(static_cast(val1), static_cast(val2)))); + return 1; + } + + int Equal(Eluna* E, ObjectGuid*) + { + E->Push(E->CHECKVAL(1) == E->CHECKVAL(2)); + return 1; + } + + int ToString(Eluna* E, ObjectGuid*) + { + E->Push(E->CHECKVAL(1).ToString()); + return 1; + } + + ElunaRegister LongLongMethods[] = + { + { "__add", &LuaBigInt::Add }, + { "__sub", &LuaBigInt::Subtract }, + { "__mul", &LuaBigInt::Multiply }, + { "__div", &LuaBigInt::Divide }, + { "__mod", &LuaBigInt::Mod }, + { "__unm", &LuaBigInt::UnaryMinus }, + { "__eq", &LuaBigInt::Equal }, + { "__lt", &LuaBigInt::Less }, + { "__le", &LuaBigInt::LessOrEqual }, + { "__tostring", &LuaBigInt::ToString }, + { "__pow", &LuaBigInt::Pow }, + }; + + ElunaRegister ULongLongMethods[] = + { + { "__sub", &LuaBigInt::Subtract }, + { "__mul", &LuaBigInt::Multiply }, + { "__div", &LuaBigInt::Divide }, + { "__mod", &LuaBigInt::Mod }, + { "__eq", &LuaBigInt::Equal }, + { "__lt", &LuaBigInt::Less }, + { "__le", &LuaBigInt::LessOrEqual }, + { "__tostring", &LuaBigInt::ToString }, + { "__pow", &LuaBigInt::Pow }, + }; + + ElunaRegister ObjectGuidMethods[] = + { + { "__tostring", &LuaBigInt::ToString }, + { "__eq", &LuaBigInt::Equal }, + }; +}; + +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/CorpseMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/CorpseMethods.h new file mode 100644 index 0000000000..e1683fb9f5 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/CorpseMethods.h @@ -0,0 +1,88 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef CORPSEMETHODS_H +#define CORPSEMETHODS_H + +/*** + * The remains of a [Player] that has died. + * + * Inherits all methods from: [Object], [WorldObject] + */ +namespace LuaCorpse +{ + /** + * Returns the GUID of the [Player] that left the [Corpse] behind. + * + * @return ObjectGuid ownerGUID + */ + int GetOwnerGUID(Eluna* E, Corpse* corpse) + { + E->Push(corpse->GetOwnerGUID()); + return 1; + } + + /** + * Returns the time when the [Player] became a ghost and spawned this [Corpse]. + * + * @return uint32 ghostTime + */ + int GetGhostTime(Eluna* E, Corpse* corpse) + { + E->Push(corpse->GetGhostTime()); + return 1; + } + + /** + * Returns the [CorpseType] of a [Corpse]. + * + * @table + * @columns [CorpseType, ID] + * @values [CORPSE_BONES, 0] + * @values [CORPSE_RESURRECTABLE_PVE, 1] + * @values [CORPSE_RESURRECTABLE_PVP, 2] + * + * @return [CorpseType] corpseType + */ + int GetType(Eluna* E, Corpse* corpse) + { + E->Push(corpse->GetType()); + return 1; + } + + /** + * Sets the "ghost time" to the current time. + * + * See [Corpse:GetGhostTime]. + */ + int ResetGhostTime(Eluna* /*E*/, Corpse* corpse) + { + corpse->ResetGhostTime(); + return 0; + } + + /** + * Saves the [Corpse] to the database. + */ + int SaveToDB(Eluna* /*E*/, Corpse* corpse) + { + corpse->SaveToDB(); + return 0; + } + + ElunaRegister CorpseMethods[] = + { + // Getters + { "GetOwnerGUID", &LuaCorpse::GetOwnerGUID }, + { "GetGhostTime", &LuaCorpse::GetGhostTime }, + { "GetType", &LuaCorpse::GetType }, + + // Other + { "ResetGhostTime", &LuaCorpse::ResetGhostTime }, + { "SaveToDB", &LuaCorpse::SaveToDB } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h new file mode 100644 index 0000000000..f2e9454b64 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h @@ -0,0 +1,1488 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef CREATUREMETHODS_H +#define CREATUREMETHODS_H + +/*** + * Non-[Player] controlled [Unit]s (i.e. NPCs). + * + * Inherits all methods from: [Object], [WorldObject], [Unit] + */ +namespace LuaCreature +{ + /** + * Returns `true` if the [Creature] can regenerate health, + * and returns `false` otherwise. + * + * @return bool isRegenerating + */ + #if 0 + int IsRegeneratingHealth(Eluna* E, Creature* creature) + { + E->Push(creature->CanRegenerateHealth()); + return 1; + } + #endif + /** + * Sets whether the [Creature] can regenerate health or not. + * + * @param bool enable = true : `true` to enable health regeneration, `false` to disable it + */ + #if 0 + int SetRegeneratingHealth(Eluna* E, Creature* creature) + { + bool enable = E->CHECKVAL(2, true); + + creature->SetRegenerateHealth(enable); + return 0; + } + #endif + /** + * Returns `true` if the [Creature] is set to not give reputation when killed, + * and returns `false` otherwise. + * + * @return bool reputationDisabled + */ + int IsReputationGainDisabled(Eluna* E, Creature* creature) + { + E->Push(creature->IsReputationGainDisabled()); + return 1; + } + + /** + * Returns `true` if the [Creature] completes the [Quest] with the ID `questID`, + * and returns `false` otherwise. + * + * @param uint32 questID : the ID of a [Quest] + * @return bool completesQuest + */ + int CanCompleteQuest(Eluna* E, Creature* creature) + { + uint32 quest_id = E->CHECKVAL(2); + + E->Push(creature->hasInvolvedQuest(quest_id)); + return 1; + } + + /** + * Returns `true` if the [Creature] can be targeted for attack, + * and returns `false` otherwise. + * + * @param bool mustBeDead = false : if `true`, only returns `true` if the [Creature] is also dead. Otherwise, it must be alive. + * @return bool targetable + */ + int IsTargetableForAttack(Eluna* E, Creature* creature) + { + bool mustBeDead = E->CHECKVAL(2, false); + + E->Push(creature->isTargetableForAttack(mustBeDead)); + return 1; + } + + /** + * Returns `true` if the [Creature] can assist `friend` in combat against `enemy`, + * and returns `false` otherwise. + * + * @param [Unit] friend : the Unit we will be assisting + * @param [Unit] enemy : the Unit that we would attack if we assist `friend` + * @param bool checkFaction = true : if `true`, the [Creature] must be the same faction as `friend` to assist + * @return bool canAssist + */ + int CanAssistTo(Eluna* E, Creature* creature) + { + Unit* u = E->CHECKOBJ(2); + Unit* enemy = E->CHECKOBJ(3); + bool checkfaction = E->CHECKVAL(4, true); + + E->Push(creature->CanAssistTo(u, enemy, checkfaction)); + return 1; + } + + /** + * Returns `true` if the [Creature] has searched for combat assistance already, + * and returns `false` otherwise. + * + * @return bool searchedForAssistance + */ + int HasSearchedAssistance(Eluna* E, Creature* creature) + { + E->Push(creature->HasSearchedAssistance()); + return 1; + } + + /** + * Returns `true` if the [Creature] will give its loot to `player`, + * and returns `false` otherwise. + * + * @return bool tapped + */ + int IsTappedBy(Eluna* E, Creature* creature) + { + Player* player = E->CHECKOBJ(2); + + E->Push(creature->isTappedBy(player)); + return 1; + } + + /** + * Returns `true` if the [Creature] will give its loot to a [Player] or [Group], + * and returns `false` otherwise. + * + * @return bool hasLootRecipient + */ + int HasLootRecipient(Eluna* E, Creature* creature) + { + E->Push(creature->hasLootRecipient()); + return 1; + } + + /** + * Returns `true` if the [Creature] can start attacking nearby hostile [Unit]s, + * and returns `false` otherwise. + * + * @return bool canAggro + */ + int CanAggro(Eluna* E, Creature* creature) + { + E->Push(!creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)); + return 1; + } + + /** + * Returns `true` if the [Creature] can move through deep water, + * and returns `false` otherwise. + * + * @return bool canSwim + */ + int CanSwim(Eluna* E, Creature* creature) + { + E->Push(creature->CanSwim()); + return 1; + } + + /** + * Returns `true` if the [Creature] can move on land, + * and returns `false` otherwise. + * + * @return bool canWalk + */ + int CanWalk(Eluna* E, Creature* creature) + { + E->Push(creature->CanWalk()); + return 1; + } + + /** + * Returns `true` if the [Creature] is returning to its spawn position from combat, + * and returns `false` otherwise. + * + * @return bool inEvadeMode + */ + int IsInEvadeMode(Eluna* E, Creature* creature) + { + E->Push(creature->IsInEvadeMode()); + return 1; + } + + /** + * Returns `true` if the [Creature]'s rank is Elite or Rare Elite, + * and returns `false` otherwise. + * + * @return bool isElite + */ + int IsElite(Eluna* E, Creature* creature) + { + E->Push(creature->isElite()); + return 1; + } + + /** + * Returns `true` if the [Creature] is a city guard, + * and returns `false` otherwise. + * + * @return bool isGuard + */ + int IsGuard(Eluna* E, Creature* creature) + { + E->Push(creature->IsGuard()); + return 1; + } + + /** + * Returns `true` if the [Creature] is a civilian, + * and returns `false` otherwise. + * + * @return bool isCivilian + */ + int IsCivilian(Eluna* E, Creature* creature) + { + E->Push(creature->IsCivilian()); + return 1; + } + + /** + * Returns `true` if the [Creature] is the leader of a player faction, + * and returns `false` otherwise. + * + * @return bool isLeader + */ + int IsRacialLeader(Eluna* E, Creature* creature) + { + E->Push(creature->IsRacialLeader()); + return 1; + } + + /** + * Returns `true` if the [Creature]'s flags_extra includes Dungeon Boss (0x1000000), + * and returns `false` otherwise. + * + * @return bool isDungeonBoss + */ + int IsDungeonBoss(Eluna* E, Creature* creature) + { + E->Push(creature->IsDungeonBoss()); + return 1; + } + + + /** + * Returns `true` if the [Creature]'s rank is Boss, + * and returns `false` otherwise. + * + * @return bool isWorldBoss + */ + int IsWorldBoss(Eluna* E, Creature* creature) + { + E->Push(creature->isWorldBoss()); + return 1; + } + + /** + * Returns `true` if the [Creature] cannot cast `spellId` due to a category cooldown, + * and returns `false` otherwise. + * + * @param uint32 spellId : the ID of a [Spell] + * @return bool hasCooldown + */ + int HasCategoryCooldown(Eluna* E, Creature* creature) + { + uint32 spell = E->CHECKVAL(2); + + if (const SpellInfo* info = sSpellMgr->GetSpellInfo(spell)) + E->Push(info->GetCategory() && creature->GetSpellHistory()->HasCooldown(spell)); + else + E->Push(false); + + return 1; + } + + /** + * Returns `true` if the [Creature] can cast `spellId` when mind-controlled, + * and returns `false` otherwise. + * + * @param uint32 spellId : the ID of a [Spell] + * @return bool hasSpell + */ + int HasSpell(Eluna* E, Creature* creature) + { + uint32 id = E->CHECKVAL(2); + + E->Push(creature->HasSpell(id)); + return 1; + } + + /** + * Returns `true` if the [Creature] starts the [Quest] `questId`, + * and returns `false` otherwise. + * + * @param uint32 questId : the ID of a [Quest] + * @return bool hasQuest + */ + int HasQuest(Eluna* E, Creature* creature) + { + uint32 questId = E->CHECKVAL(2); + + E->Push(creature->hasQuest(questId)); + return 1; + } + + /** + * Returns `true` if the [Creature] has `spellId` on cooldown, + * and returns `false` otherwise. + * + * @param uint32 spellId : the ID of a [Spell] + * @return bool hasCooldown + */ + int HasSpellCooldown(Eluna* E, Creature* creature) + { + uint32 spellId = E->CHECKVAL(2); + + E->Push(creature->GetSpellHistory()->HasCooldown(spellId)); + return 1; + } + + /** + * Returns `true` if the [Creature] can fly, + * and returns `false` otherwise. + * + * @return bool canFly + */ + int CanFly(Eluna* E, Creature* creature) + { + E->Push(creature->CanFly()); + return 1; + } + + /** + * Returns `true` if the [Creature] is an invisible trigger, + * and returns `false` otherwise. + * + * @return bool canFly + */ + int IsTrigger(Eluna* E, Creature* creature) + { + E->Push(creature->IsTrigger()); + return 1; + } + + /** + * Returns true if the [Creature] is damaged enough for looting + * + * @return bool isDamagedEnough + */ + int IsDamageEnoughForLootingAndReward(Eluna* E, Creature* creature) + { + E->Push(creature->IsDamageEnoughForLootingAndReward()); + return 1; + } + + /** + * Returns true if the [Creature] can start attacking specified target + * + * Does not work on most targets + * + * @param [Unit] target + * @param bool force = true : force [Creature] to attack + */ + int CanStartAttack(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + bool force = E->CHECKVAL(3, true); + + E->Push(creature->CanStartAttack(target, force)); + return 1; + } + + /** + * Returns true if [Creature] has the specified loot mode + * + * @param uint16 lootMode + * @return bool hasLootMode + */ + int HasLootMode(Eluna* E, Creature* creature) + { + uint16 lootMode = E->CHECKVAL(2); + + E->Push(creature->HasLootMode(lootMode)); + return 1; + } + + /** + * Returns the time it takes for this [Creature] to respawn once killed. + * + * This value does not usually change over a [Creature]'s lifespan, + * but can be modified by [Creature:SetRespawnDelay]. + * + * @return uint32 respawnDelay : the respawn delay, in seconds + */ + int GetRespawnDelay(Eluna* E, Creature* creature) + { + E->Push(creature->GetRespawnDelay()); + return 1; + } + + /** + * Returns the radius the [Creature] is permitted to wander from its + * respawn point. + * + * @return float wanderRadius + */ + #if 0 + int GetWanderRadius(Eluna* E, Creature* creature) + { + E->Push(creature->GetWanderDistance()); + return 1; + } + #endif + /** + * Returns the current waypoint path ID of the [Creature]. + * + * @return uint32 pathId + */ + int GetWaypointPath(Eluna* E, Creature* creature) + { + E->Push(creature->GetWaypointPath()); + return 1; + } + + /** + * Returns the current waypoint ID of the [Creature]. + * + * @return uint32 wpId + */ + #if 0 + int GetCurrentWaypointId(Eluna* E, Creature* creature) + { + E->Push(creature->GetCurrentWaypointInfo().first); + return 1; + } + #endif + /** + * Returns the default movement type for this [Creature]. + * + * @return [MovementGeneratorType] defaultMovementType + */ + int GetDefaultMovementType(Eluna* E, Creature* creature) + { + E->Push(creature->GetDefaultMovementType()); + return 1; + } + + /** + * Returns the aggro range of the [Creature] for `target`. + * + * @param [Unit] target + * @return float aggroRange + */ + int GetAggroRange(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + + E->Push(creature->GetAggroRange(target)); + return 1; + } + + /** + * Returns the effective aggro range of the [Creature] for `target`. + * + * If this is smaller than the minimum aggro range set in the config file, + * that is used as the aggro range instead. + * + * @param [Unit] target + * @return float attackDistance + */ + int GetAttackDistance(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + + E->Push(creature->GetAttackDistance(target)); + return 1; + } + + + /** + * Returns the [Group] that can loot this [Creature]. + * + * @return [Group] lootRecipientGroup : the group or `nil` + */ + int GetLootRecipientGroup(Eluna* E, Creature* creature) + { + E->Push(creature->GetLootRecipientGroup()); + return 1; + } + + /** + * Returns the [Player] that can loot this [Creature]. + * + * @return [Player] lootRecipient : the player or `nil` + */ + int GetLootRecipient(Eluna* E, Creature* creature) + { + E->Push(creature->GetLootRecipient()); + return 1; + } + + /** + * Returns the [Creature]'s script name. + * + * This is used by the core to apply C++ scripts to the Creature. + * + * It is not used by Eluna. Eluna will override AI scripts. + * + * @return string scriptName + */ + int GetScriptName(Eluna* E, Creature* creature) + { + E->Push(creature->GetScriptName()); + return 1; + } + + /** + * Returns the [Creature]'s AI name. + * + * This is used by the core to assign the Creature's default AI. + * + * If the Creature is scripted by Eluna, the AI is overriden. + * + * @return string AIName + */ + int GetAIName(Eluna* E, Creature* creature) + { + E->Push(creature->GetAIName()); + return 1; + } + + /** + * Returns the [Creature]'s script ID. + * + * Every C++ script name is assigned a unique ID by the core. + * This returns the ID for this [Creature]'s script name. + * + * @return uint32 scriptID + */ + int GetScriptId(Eluna* E, Creature* creature) + { + E->Push(creature->GetScriptId()); + return 1; + } + + /** + * Returns the [Creature]'s cooldown for `spellID`. + * + * @param uint32 spellID + * @return uint32 cooldown : the cooldown, in milliseconds + */ + int GetCreatureSpellCooldownDelay(Eluna* E, Creature* creature) + { + uint32 spell = E->CHECKVAL(2); + + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell)) + E->Push(creature->GetSpellHistory()->GetRemainingCooldown(spellInfo)); + else + E->Push(0); + + return 1; + } + + /** + * Returns the delay between when the [Creature] dies and when its body despawns. + * + * @return uint32 corpseDelay : the delay, in seconds + */ + int GetCorpseDelay(Eluna* E, Creature* creature) + { + E->Push(creature->GetCorpseDelay()); + return 1; + } + + /** + * Returns position the [Creature] returns to when evading from combat + * or respawning. + * + * @return float x + * @return float y + * @return float z + * @return float o + */ + int GetHomePosition(Eluna* E, Creature* creature) + { + float x, y, z, o; + creature->GetHomePosition(x, y, z, o); + + E->Push(x); + E->Push(y); + E->Push(z); + E->Push(o); + return 4; + } + + /** + * Sets the position the [Creature] returns to when evading from combat + * or respawning. + * + * @param float x + * @param float y + * @param float z + * @param float o + */ + int SetHomePosition(Eluna* E, Creature* creature) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + float o = E->CHECKVAL(5); + + creature->SetHomePosition(x, y, z, o); + return 0; + } + + enum SelectAggroTarget + { + SELECT_TARGET_RANDOM = 0, // Just selects a random target + SELECT_TARGET_TOPAGGRO, // Selects targes from top aggro to bottom + SELECT_TARGET_BOTTOMAGGRO, // Selects targets from bottom aggro to top + SELECT_TARGET_NEAREST, + SELECT_TARGET_FARTHEST + }; + + /** + * Returns a target from the [Creature]'s threat list based on the + * supplied arguments. + * + * @table + * @columns [SelectAggroTarget, ID, Comment] + * @values [SELECT_TARGET_RANDOM, 0, "Just selects a random target"] + * @values [SELECT_TARGET_TOPAGGRO, 1, "Sorts targets from top aggro to bottom"] + * @values [SELECT_TARGET_BOTTOMAGGRO, 2, "Sorts targets from bottom aggro to top"] + * @values [SELECT_TARGET_NEAREST, 3, "Sorts targets from the nearest to the furthest"] + * @values [SELECT_TARGET_FARTHEST, 4, "Sorts targets from the furthest to the nearest"] + * + * For example, if you wanted to select the third-farthest [Player] + * within 50 yards that has the [Aura] "Corrupted Blood" (ID 24328), + * you could use this function like so: + * + * target = creature:GetAITarget(4, true, 3, 50, 24328) + * + * @param [SelectAggroTarget] targetType : how the threat list should be sorted + * @param bool playerOnly = false : if `true`, skips targets that aren't [Player]s + * @param uint32 position = 0 : used as an offset into the threat list. If `targetType` is random, used as the number of players from top of aggro to choose from + * @param float distance = 0.0 : if positive, the maximum distance for the target. If negative, the minimum distance + * @param int32 aura = 0 : if positive, the target must have this [Aura]. If negative, the the target must not have this Aura + * @return [Unit] target : the target, or `nil` + */ + #if 0 + int GetAITarget(Eluna* E, Creature* creature) + { + uint32 targetType = E->CHECKVAL(2); + bool playerOnly = E->CHECKVAL(3, false); + uint32 position = E->CHECKVAL(4, 0); + float dist = E->CHECKVAL(5, 0.0f); + int32 aura = E->CHECKVAL(6, 0); + + auto const& threatlist = creature->GetThreatManager().GetSortedThreatList(); + std::list targetList; + for (ThreatReference const* itr : threatlist) + { + Unit* target = itr->GetVictim(); + if (!target) + continue; + if (playerOnly && target->GetTypeId() != TYPEID_PLAYER) + continue; + if (aura > 0 && !target->HasAura(aura)) + continue; + else if (aura < 0 && target->HasAura(-aura)) + continue; + if (dist > 0.0f && !creature->IsWithinDist(target, dist)) + continue; + else if (dist < 0.0f && creature->IsWithinDist(target, -dist)) + continue; + targetList.push_back(target); + } + + if (targetList.empty()) + return 1; + if (position >= targetList.size()) + return 1; + + if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) + targetList.sort(ElunaUtil::ObjectDistanceOrderPred(creature)); + + switch (targetType) + { + case SELECT_TARGET_NEAREST: + case SELECT_TARGET_TOPAGGRO: + { + std::list::const_iterator itr = targetList.begin(); + if (position) + std::advance(itr, position); + E->Push(*itr); + } + break; + case SELECT_TARGET_FARTHEST: + case SELECT_TARGET_BOTTOMAGGRO: + { + std::list::reverse_iterator ritr = targetList.rbegin(); + if (position) + std::advance(ritr, position); + E->Push(*ritr); + } + break; + case SELECT_TARGET_RANDOM: + { + std::list::const_iterator itr = targetList.begin(); + if (position) + std::advance(itr, urand(0, position)); + else + std::advance(itr, urand(0, targetList.size() - 1)); + E->Push(*itr); + } + break; + default: + luaL_argerror(E->L, 2, "SelectAggroTarget expected"); + break; + } + + return 1; + } + #endif + /** + * Returns all [Unit]s in the [Creature]'s threat list. + * + * @return table targets + */ + #if 0 + int GetAITargets(Eluna* E, Creature* creature) + { + auto const& threatlist = creature->GetThreatManager().GetSortedThreatList(); + + lua_createtable(E->L, creature->GetThreatManager().GetThreatListSize(), 0); + int tbl = lua_gettop(E->L); + + uint32 i = 0; + for (ThreatReference const* itr : threatlist) + { + Unit* target = itr->GetVictim(); + if (!target) + continue; + E->Push(target); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + #endif + /** + * Returns the number of [Unit]s in this [Creature]'s threat list. + * + * @return double targetsCount + */ + #if 0 + int GetAITargetsCount(Eluna* E, Creature* creature) + { + E->Push((double)creature->GetThreatManager().GetThreatListSize()); + return 1; + } + #endif + #if 0 + int AddThreat(Eluna* E, Creature* creature) + { + Unit* victim = E->CHECKOBJ(2); + float threat = E->CHECKVAL(3, true); + uint32 spell = E->CHECKVAL(4, 0); + + creature->GetThreatManager().AddThreat(victim, threat, spell ? sSpellMgr->GetSpellInfo(spell) : NULL, true, true); + return 0; + } + #endif + /** + * Returns the threat of a [Unit] in this [Creature]'s threat list. + * + * @param [Unit] target + * @return float threat + */ + #if 0 + int GetThreat(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + + E->Push(creature->GetThreatManager().GetThreat(target)); + return 1; + } + #endif + #if 0 + int ClearThreat(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + + creature->GetThreatManager().ClearThreat(target); + return 0; + } + #endif + #if 0 + int ClearThreatList(Eluna* /*E*/, Creature* creature) + { + creature->GetThreatManager().ClearAllThreat(); + return 0; + } + #endif + #if 0 + int ResetAllThreat(Eluna* /*E*/, Creature* creature) + { + creature->GetThreatManager().ResetAllThreat(); + return 0; + } + #endif + #if 0 + int FixateTarget(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + + creature->GetThreatManager().FixateTarget(target); + return 0; + } + #endif + /** + * Clears the [Creature]'s fixated target. + */ + #if 0 + int ClearFixate(Eluna* /*E*/, Creature* creature) + { + creature->GetThreatManager().ClearFixate(); + return 0; + } + #endif + /** + * Returns the [Creature]'s NPC flags. + * + * These are used to control whether the NPC is a vendor, can repair items, + * can give quests, etc. + * + * @return [NPCFlags] npcFlags + */ + int GetNPCFlags(Eluna* E, Creature* creature) + { + E->Push(creature->GetUInt32Value(UNIT_NPC_FLAGS)); + return 1; + } + + /** + * Returns the [Creature]'s Extra flags. + * + * These are used to control whether the NPC is a civilian, uses pathfinding, + * if it's a guard, etc. + * + * @return [ExtraFlags] extraFlags + */ + int GetExtraFlags(Eluna* E, Creature* creature) + { + E->Push(creature->GetCreatureTemplate()->flags_extra); + return 1; + } + + /** + * Returns the [Creature]'s rank as defined in the creature template. + * + * @return uint32 rank + */ + int GetRank(Eluna* E, Creature* creature) + { + E->Push(creature->GetCreatureTemplate()->rank); + return 1; + } + + /** + * Returns the [Creature]'s shield block value. + * + * @return uint32 shieldBlockValue + */ + #if 0 + int GetShieldBlockValue(Eluna* E, Creature* creature) + { + E->Push(creature->GetShieldBlockValue()); + return 1; + } + #endif + /** + * Returns the loot mode for the [Creature]. + * + * @table + * @columns [Mode, ID] + * @values [LOOT_MODE_DEFAULT, 1] + * @values [LOOT_MODE_HARD_MODE_1, 2] + * @values [LOOT_MODE_HARD_MODE_2, 4] + * @values [LOOT_MODE_HARD_MODE_3, 8] + * @values [LOOT_MODE_HARD_MODE_4, 16] + * @values [LOOT_MODE_JUNK_FISH, 32768] + * + * @return uint16 lootMode + */ + int GetLootMode(Eluna* E, Creature* creature) + { + E->Push(creature->GetLootMode()); + return 1; + } + + /** + * Returns the guid of the [Creature] that is used as the ID in the database + * + * @return uint32 dbguid + */ + int GetDBTableGUIDLow(Eluna* E, Creature* creature) + { + E->Push(creature->GetSpawnId()); + return 1; + } + + /** + * Sets the [Creature]'s NPC flags to `flags`. + * + * @param [NPCFlags] flags + */ + int SetNPCFlags(Eluna* E, Creature* creature) + { + uint32 flags = E->CHECKVAL(2); + + creature->SetUInt32Value(UNIT_NPC_FLAGS, flags); + return 0; + } + + /** + * Sets the [Creature]'s ReactState to `state`. + * + * @param [ReactState] state + */ + int SetReactState(Eluna* E, Creature* creature) + { + uint32 state = E->CHECKVAL(2); + + creature->SetReactState((ReactStates)state); + return 0; + } + + /** + * Makes the [Creature] able to fly if enabled. + * + * @param bool disable + */ + int SetDisableGravity(Eluna* E, Creature* creature) + { + bool disable = E->CHECKVAL(2); + + creature->SetDisableGravity(disable); + return 0; + } + + /** + * Sets the loot mode for the [Creature]. + * + * @table + * @columns [Mode, ID] + * @values [LOOT_MODE_DEFAULT, 1] + * @values [LOOT_MODE_HARD_MODE_1, 2] + * @values [LOOT_MODE_HARD_MODE_2, 4] + * @values [LOOT_MODE_HARD_MODE_3, 8] + * @values [LOOT_MODE_HARD_MODE_4, 16] + * @values [LOOT_MODE_JUNK_FISH, 32768] + * + * @param uint16 lootMode + */ + int SetLootMode(Eluna* E, Creature* creature) + { + uint16 lootMode = E->CHECKVAL(2); + + creature->SetLootMode(lootMode); + return 0; + } + + /** + * Sets the [Creature]'s death state to `deathState`. + * + * @param [DeathState] deathState + */ + int SetDeathState(Eluna* E, Creature* creature) + { + int32 state = E->CHECKVAL(2); + + creature->setDeathState((DeathState)state); + return 0; + } + + /** + * Sets whether the [Creature] is currently walking or running. + * + * @param bool enable = true : `true` to enable walking, `false` for running + */ + int SetWalk(Eluna* E, Creature* creature) // TODO: Move same to Player ? + { + bool enable = E->CHECKVAL(2, true); + + creature->SetWalk(enable); + return 0; + } + + /** + * Equips given [Item]s to the [Unit]. Using 0 removes the equipped [Item] + * + * @param uint32 main_hand : main hand [Item]'s entry + * @param uint32 off_hand : off hand [Item]'s entry + * @param uint32 ranged : ranged [Item]'s entry + */ + int SetEquipmentSlots(Eluna* E, Creature* creature) + { + uint32 main_hand = E->CHECKVAL(2); + uint32 off_hand = E->CHECKVAL(3); + uint32 ranged = E->CHECKVAL(4); + + creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, main_hand); + creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, off_hand); + creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, ranged); + return 0; + } + + /** + * Sets whether the [Creature] can be aggroed. + * + * @param bool allow = true : `true` to allow aggro, `false` to disable aggro + */ + int SetAggroEnabled(Eluna* E, Creature* creature) + { + bool allow = E->CHECKVAL(2, true); + + if (allow) + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + else + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + + return 0; + } + + /** + * Sets whether the [Creature] gives reputation or not. + * + * @param bool disable = true : `true` to disable reputation, `false` to enable + */ + int SetDisableReputationGain(Eluna* E, Creature* creature) + { + bool disable = E->CHECKVAL(2, true); + + creature->SetDisableReputationGain(disable); + return 0; + } + + /** + * Sets the [Creature] as in combat with all [Player]s in the dungeon instance. + * + * This is used by raid bosses to prevent Players from using out-of-combat + * actions once the encounter has begun. + */ + #if 0 + int SetInCombatWithZone(Eluna* /*E*/, Creature* creature) + { + if (creature->IsAIEnabled()) + creature->AI()->DoZoneInCombat(); + + return 0; + } + #endif + /** + * Sets the distance the [Creature] can wander from it's spawn point. + * + * @param float distance + */ + int SetWanderRadius(Eluna* E, Creature* creature) + { + float dist = E->CHECKVAL(2); + + //creature->SetWanderDistance(dist); + return 0; + } + + /** + * Sets the time it takes for the [Creature] to respawn when killed. + * + * @param uint32 delay : the delay, in seconds + */ + int SetRespawnDelay(Eluna* E, Creature* creature) + { + uint32 delay = E->CHECKVAL(2); + + creature->SetRespawnDelay(delay); + return 0; + } + + /** + * Sets the default movement type of the [Creature]. + * + * @param [MovementGeneratorType] type + */ + int SetDefaultMovementType(Eluna* E, Creature* creature) + { + int32 type = E->CHECKVAL(2); + + creature->SetDefaultMovementType((MovementGeneratorType)type); + return 0; + } + + /** + * Sets whether the [Creature] can search for assistance at low health or not. + * + * @param bool enable = true : `true` to disable searching, `false` to allow + */ + int SetNoSearchAssistance(Eluna* E, Creature* creature) + { + bool val = E->CHECKVAL(2, true); + + creature->SetNoSearchAssistance(val); + return 0; + } + + /** + * Sets whether the [Creature] can call nearby enemies for help in combat or not. + * + * @param bool enable = true : `true` to disable calling for help, `false` to enable + */ + int SetNoCallAssistance(Eluna* E, Creature* creature) + { + bool val = E->CHECKVAL(2, true); + + creature->SetNoCallAssistance(val); + return 0; + } + + /** + * Sets whether the creature is hovering / levitating or not. + * + * @param bool enable = true : `true` to enable hovering, `false` to disable + */ + int SetHover(Eluna* E, Creature* creature) + { + bool enable = E->CHECKVAL(2, true); + + creature->SetHover(enable); + return 0; + } + + /** + * Despawn this [Creature]. + * + * @param uint32 delay = 0 : dely to despawn in milliseconds + */ + int DespawnOrUnsummon(Eluna* E, Creature* creature) + { + uint32 msTimeToDespawn = E->CHECKVAL(2, 0); + + creature->DespawnOrUnsummon(Milliseconds(msTimeToDespawn)); + return 0; + } + + /** + * Respawn this [Creature]. + */ + int Respawn(Eluna* /*E*/, Creature* creature) + { + creature->Respawn(); + return 0; + } + + /** + * Remove this [Creature]'s corpse. + */ + int RemoveCorpse(Eluna* /*E*/, Creature* creature) + { + creature->RemoveCorpse(); + return 0; + } + + /** + * Make the [Creature] start following its waypoint path. + */ + int MoveWaypoint(Eluna* /*E*/, Creature* creature) + { + creature->GetMotionMaster()->MovePath(creature->GetWaypointPath(), true); + return 0; + } + + /** + * Make the [Creature] call for assistance in combat from other nearby [Creature]s. + */ + int CallAssistance(Eluna* /*E*/, Creature* creature) + { + creature->CallAssistance(); + return 0; + } + + /** + * Make the [Creature] call for help in combat from friendly [Creature]s within `radius`. + * + * @param float radius + */ + int CallForHelp(Eluna* E, Creature* creature) + { + float radius = E->CHECKVAL(2); + + creature->CallForHelp(radius); + return 0; + } + + /** + * Make the [Creature] flee combat to get assistance from a nearby friendly [Creature]. + */ + int FleeToGetAssistance(Eluna* /*E*/, Creature* creature) + { + creature->DoFleeToGetAssistance(); + return 0; + } + + /** + * Make the [Creature] attack `target`. + * + * @param [Unit] target + */ + int AttackStart(Eluna* E, Creature* creature) + { + Unit* target = E->CHECKOBJ(2); + + creature->AI()->AttackStart(target); + return 0; + } + + /** + * Save the [Creature] in the database. + */ + #if 0 + int SaveToDB(Eluna* /*E*/, Creature* creature) + { + creature->SaveToDB(); + return 0; + } + #endif + /** + * Make the [Creature] try to find a new target. + * + * This should be called every update cycle for the Creature's AI. + */ + int SelectVictim(Eluna* E, Creature* creature) + { + E->Push(creature->SelectVictim()); + return 1; + } + + /** + * Transform the [Creature] into another Creature. + * + * @param uint32 entry : the Creature ID to transform into + * @param uint32 dataGUIDLow = 0 : use this Creature's model and equipment instead of the defaults + */ + int UpdateEntry(Eluna* E, Creature* creature) + { + uint32 entry = E->CHECKVAL(2); + uint32 dataGuidLow = E->CHECKVAL(3, 0); + + creature->UpdateEntry(entry, dataGuidLow ? eObjectMgr->GetCreatureData(dataGuidLow) : NULL); + return 0; + } + + /** + * Resets [Creature]'s loot mode to default + */ + int ResetLootMode(Eluna* /*E*/, Creature* creature) + { + creature->ResetLootMode(); + return 0; + } + + /** + * Removes specified loot mode from [Creature] + * + * @param uint16 lootMode + */ + int RemoveLootMode(Eluna* E, Creature* creature) + { + uint16 lootMode = E->CHECKVAL(2); + + creature->RemoveLootMode(lootMode); + return 0; + } + + /** + * Adds a loot mode to the [Creature] + * + * @param uint16 lootMode + */ + int AddLootMode(Eluna* E, Creature* creature) + { + uint16 lootMode = E->CHECKVAL(2); + + creature->AddLootMode(lootMode); + return 0; + } + + /** + * Returns the [Creature]'s creature family ID (enumerated in CreatureFamily.dbc). + * + * @table + * @columns [CreatureFamily, ID, Comment] + * @values [CREATURE_FAMILY_NONE, 0, "TrinityCore only"] + * @values [CREATURE_FAMILY_WOLF, 1, ""] + * @values [CREATURE_FAMILY_CAT, 2, ""] + * @values [CREATURE_FAMILY_SPIDER, 3, ""] + * @values [CREATURE_FAMILY_BEAR, 4, ""] + * @values [CREATURE_FAMILY_BOAR, 5, ""] + * @values [CREATURE_FAMILY_CROCOLISK, 6, ""] + * @values [CREATURE_FAMILY_CARRION_BIRD, 7, ""] + * @values [CREATURE_FAMILY_CRAB, 8, ""] + * @values [CREATURE_FAMILY_GORILLA, 9, ""] + * @values [CREATURE_FAMILY_HORSE_CUSTOM, 10, "Does not exist in DBC but used for horse like beasts in DB"] + * @values [CREATURE_FAMILY_RAPTOR, 11, ""] + * @values [CREATURE_FAMILY_TALLSTRIDER, 12, ""] + * @values [CREATURE_FAMILY_FELHUNTER, 15, ""] + * @values [CREATURE_FAMILY_VOIDWALKER, 16, ""] + * @values [CREATURE_FAMILY_SUCCUBUS, 17, ""] + * @values [CREATURE_FAMILY_DOOMGUARD, 19, ""] + * @values [CREATURE_FAMILY_SCORPID, 20, ""] + * @values [CREATURE_FAMILY_TURTLE, 21, ""] + * @values [CREATURE_FAMILY_IMP, 23, ""] + * @values [CREATURE_FAMILY_BAT, 24, ""] + * @values [CREATURE_FAMILY_HYENA, 25, ""] + * @values [CREATURE_FAMILY_BIRD_OF_PREY, 26, "Named CREATURE_FAMILY_OWL in Mangos"] + * @values [CREATURE_FAMILY_WIND_SERPENT, 27, ""] + * @values [CREATURE_FAMILY_REMOTE_CONTROL, 28, ""] + * @values [CREATURE_FAMILY_FELGUARD, 29, "This and below is TBC+"] + * @values [CREATURE_FAMILY_DRAGONHAWK, 30, ""] + * @values [CREATURE_FAMILY_RAVAGER, 31, ""] + * @values [CREATURE_FAMILY_WARP_STALKER, 32, ""] + * @values [CREATURE_FAMILY_SPOREBAT, 33, ""] + * @values [CREATURE_FAMILY_NETHER_RAY, 34, ""] + * @values [CREATURE_FAMILY_SERPENT, 35, ""] + * @values [CREATURE_FAMILY_SEA_LION, 36, "TBC only"] + * @values [CREATURE_FAMILY_MOTH, 37, "This and below is WotLK+"] + * @values [CREATURE_FAMILY_CHIMAERA, 38, ""] + * @values [CREATURE_FAMILY_DEVILSAUR, 39, ""] + * @values [CREATURE_FAMILY_GHOUL, 40, ""] + * @values [CREATURE_FAMILY_SILITHID, 41, ""] + * @values [CREATURE_FAMILY_WORM, 42, ""] + * @values [CREATURE_FAMILY_RHINO, 43, ""] + * @values [CREATURE_FAMILY_WASP, 44, ""] + * @values [CREATURE_FAMILY_CORE_HOUND, 45, ""] + * @values [CREATURE_FAMILY_SPIRIT_BEAST, 46, ""] + * + * @return [CreatureFamily] creatureFamily + */ + int GetCreatureFamily(Eluna* E, Creature* creature) + { + uint32 entry = creature->GetEntry(); + + CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(entry); + if (cInfo) + E->Push(cInfo->family); + + return 1; + } + + /** + * Removes [Creature] from the world + * + * The object is no longer reachable after this and it is not respawned. + * + * @param bool deleteFromDB : if true, it will delete the [Creature] from the database + */ + int RemoveFromWorld(Eluna* E, Creature* creature) + { + bool deldb = E->CHECKVAL(2, false); + if (deldb) + { + ObjectGuid::LowType spawnId = creature->GetSpawnId(); + //Creature::DeleteFromDB(spawnId); + } + + creature->RemoveFromWorld(); + return 0; + } + + ElunaRegister CreatureMethods[] = + { + // Getters + + + + { "GetHomePosition", &LuaCreature::GetHomePosition }, + { "GetCorpseDelay", &LuaCreature::GetCorpseDelay }, + { "GetCreatureSpellCooldownDelay", &LuaCreature::GetCreatureSpellCooldownDelay }, + { "GetScriptId", &LuaCreature::GetScriptId }, + { "GetAIName", &LuaCreature::GetAIName }, + { "GetScriptName", &LuaCreature::GetScriptName }, + { "GetAttackDistance", &LuaCreature::GetAttackDistance }, + { "GetAggroRange", &LuaCreature::GetAggroRange }, + { "GetDefaultMovementType", &LuaCreature::GetDefaultMovementType }, + { "GetRespawnDelay", &LuaCreature::GetRespawnDelay }, + + + { "GetWaypointPath", &LuaCreature::GetWaypointPath }, + { "GetLootMode", &LuaCreature::GetLootMode }, + { "GetLootRecipient", &LuaCreature::GetLootRecipient }, + { "GetLootRecipientGroup", &LuaCreature::GetLootRecipientGroup }, + { "GetNPCFlags", &LuaCreature::GetNPCFlags }, + { "GetExtraFlags", &LuaCreature::GetExtraFlags }, + { "GetRank", &LuaCreature::GetRank }, + + { "GetDBTableGUIDLow", &LuaCreature::GetDBTableGUIDLow }, + { "GetCreatureFamily", &LuaCreature::GetCreatureFamily }, + + + // Setters + + { "SetHover", &LuaCreature::SetHover }, + { "SetDisableGravity", &LuaCreature::SetDisableGravity }, + { "SetAggroEnabled", &LuaCreature::SetAggroEnabled }, + { "SetNoCallAssistance", &LuaCreature::SetNoCallAssistance }, + { "SetNoSearchAssistance", &LuaCreature::SetNoSearchAssistance }, + { "SetDefaultMovementType", &LuaCreature::SetDefaultMovementType }, + { "SetRespawnDelay", &LuaCreature::SetRespawnDelay }, + { "SetWanderRadius", &LuaCreature::SetWanderRadius }, + + { "SetDisableReputationGain", &LuaCreature::SetDisableReputationGain }, + { "SetLootMode", &LuaCreature::SetLootMode }, + { "SetNPCFlags", &LuaCreature::SetNPCFlags }, + { "SetReactState", &LuaCreature::SetReactState }, + { "SetDeathState", &LuaCreature::SetDeathState }, + { "SetWalk", &LuaCreature::SetWalk }, + { "SetHomePosition", &LuaCreature::SetHomePosition }, + { "SetEquipmentSlots", &LuaCreature::SetEquipmentSlots }, + + // Boolean + + { "IsDungeonBoss", &LuaCreature::IsDungeonBoss }, + { "IsWorldBoss", &LuaCreature::IsWorldBoss }, + { "IsRacialLeader", &LuaCreature::IsRacialLeader }, + { "IsCivilian", &LuaCreature::IsCivilian }, + { "IsTrigger", &LuaCreature::IsTrigger }, + { "IsGuard", &LuaCreature::IsGuard }, + { "IsElite", &LuaCreature::IsElite }, + { "IsInEvadeMode", &LuaCreature::IsInEvadeMode }, + { "HasCategoryCooldown", &LuaCreature::HasCategoryCooldown }, + { "CanWalk", &LuaCreature::CanWalk }, + { "CanSwim", &LuaCreature::CanSwim }, + { "CanAggro", &LuaCreature::CanAggro }, + { "CanStartAttack", &LuaCreature::CanStartAttack }, + { "HasSearchedAssistance", &LuaCreature::HasSearchedAssistance }, + { "IsTappedBy", &LuaCreature::IsTappedBy }, + { "HasLootRecipient", &LuaCreature::HasLootRecipient }, + { "CanAssistTo", &LuaCreature::CanAssistTo }, + { "IsTargetableForAttack", &LuaCreature::IsTargetableForAttack }, + { "CanCompleteQuest", &LuaCreature::CanCompleteQuest }, + { "IsReputationGainDisabled", &LuaCreature::IsReputationGainDisabled }, + { "IsDamageEnoughForLootingAndReward", &LuaCreature::IsDamageEnoughForLootingAndReward }, + { "HasLootMode", &LuaCreature::HasLootMode }, + { "HasSpell", &LuaCreature::HasSpell }, + { "HasQuest", &LuaCreature::HasQuest }, + { "HasSpellCooldown", &LuaCreature::HasSpellCooldown }, + { "CanFly", &LuaCreature::CanFly }, + + // Other + { "FleeToGetAssistance", &LuaCreature::FleeToGetAssistance }, + { "CallForHelp", &LuaCreature::CallForHelp }, + { "CallAssistance", &LuaCreature::CallAssistance }, + { "RemoveCorpse", &LuaCreature::RemoveCorpse }, + { "DespawnOrUnsummon", &LuaCreature::DespawnOrUnsummon }, + { "Respawn", &LuaCreature::Respawn }, + { "AttackStart", &LuaCreature::AttackStart }, + { "AddLootMode", &LuaCreature::AddLootMode }, + { "ResetLootMode", &LuaCreature::ResetLootMode }, + { "RemoveLootMode", &LuaCreature::RemoveLootMode }, + + { "SelectVictim", &LuaCreature::SelectVictim }, + { "MoveWaypoint", &LuaCreature::MoveWaypoint }, + { "UpdateEntry", &LuaCreature::UpdateEntry }, + + + + + { "RemoveFromWorld", &LuaCreature::RemoveFromWorld } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/ElunaProcInfoMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/ElunaProcInfoMethods.h new file mode 100644 index 0000000000..c2a5f179e4 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/ElunaProcInfoMethods.h @@ -0,0 +1,364 @@ +#ifndef ELUNAPROCINFOMETHODS_H +#define ELUNAPROCINFOMETHODS_H + +namespace LuaElunaProcInfo +{ + /** + * Returns the [Unit] that triggered the proc event. + * + * @return [Unit] actor + */ + int GetActor(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetActor()); + return 1; + } + + /** + * Returns the target [Unit] of the proc event action. + * + * @return [Unit] actionTarget + */ + int GetActionTarget(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetActionTarget()); + return 1; + } + + /** + * Returns the type mask of the proc event. + * + * @return uint32 typeMask + */ + int GetTypeMask(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetTypeMask()); + return 1; + } + + /** + * Returns the spell type mask of the proc event. + * + * @return uint32 spellTypeMask + */ + int GetSpellTypeMask(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetSpellTypeMask()); + return 1; + } + + /** + * Returns the spell phase mask of the proc event. + * + * @return uint32 spellPhaseMask + */ + int GetSpellPhaseMask(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetSpellPhaseMask()); + return 1; + } + + /** + * Returns the hit mask of the proc event. + * + * @return uint32 hitMask + */ + int GetHitMask(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetHitMask()); + return 1; + } + + /** + * Returns the [Spell] that triggered the proc event, or nil if the proc was not triggered by a spell. + * + * @return [Spell] procSpell + */ + int GetProcSpell(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetProcSpell()); + return 1; + } + + /** + * Returns the [SpellInfo] of the spell that triggered the proc event. + * + * @return [SpellInfo] spellInfo + */ + int GetSpellInfo(Eluna* E, ElunaProcInfo* procInfo) + { + if (!procInfo->GetSpellInfo()) + { + E->Push(); + return 1; + } + + ElunaSpellInfo info(procInfo->GetSpellInfo()->Id); + E->Push(&info); + return 1; + } + + /** + * Returns the school mask of the proc event. + * + * @return uint32 schoolMask + */ + int GetSchoolMask(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetSchoolMask()); + return 1; + } + + /** + * Returns the damage dealt in the proc event. + * + * @return uint32 damage + */ + int GetDamage(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetDamage()); + return 1; + } + + /** + * Returns the damage effect type of the proc event. + * + * @return uint32 damageType + */ + int GetDamageType(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetDamageType()); + return 1; + } + + /** + * Returns the weapon attack type of the proc event. + * + * @return uint32 attackType + */ + int GetAttackType(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetAttackType()); + return 1; + } + + /** + * Returns the amount of damage absorbed in the proc event. + * + * @return uint32 damageAbsorb + */ + int GetDamageAbsorb(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetDamageAbsorb()); + return 1; + } + + /** + * Returns the amount of damage resisted in the proc event. + * + * @return uint32 resist + */ + int GetResist(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetResist()); + return 1; + } + + /** + * Returns the amount of damage blocked in the proc event. + * + * @return uint32 block + */ + int GetBlock(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetBlock()); + return 1; + } + + /** + * Returns the amount of healing done in the proc event. + * + * @return uint32 heal + */ + int GetHeal(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetHeal()); + return 1; + } + + /** + * Returns the effective healing done in the proc event, after overhealing is subtracted. + * + * @return uint32 effectiveHeal + */ + int GetEffectiveHeal(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetEffectiveHeal()); + return 1; + } + + /** + * Returns the amount of healing absorbed in the proc event. + * + * @return uint32 healAbsorb + */ + int GetHealAbsorb(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetHealAbsorb()); + return 1; + } + + /** + * Returns `true` if the proc event involved damage, `false` otherwise. + * + * @return bool hasDamage + */ + int HasDamage(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->HasDamage()); + return 1; + } + + /** + * Returns `true` if the proc event involved healing, `false` otherwise. + * + * @return bool hasHeal + */ + int HasHeal(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->HasHeal()); + return 1; + } + + /** + * Sets the damage of the proc event. + * + * @param int32 damage : the damage value to set + */ + int SetDamage(Eluna* E, ElunaProcInfo* procInfo) + { + int32 damage = E->CHECKVAL(2); + procInfo->SetDamage(damage); + return 0; + } + + /** + * Sets the absorbed damage of the proc event. + * + * @param uint32 absorb : the absorb value to set + */ + int SetAbsorbDamage(Eluna* E, ElunaProcInfo* procInfo) + { + uint32 absorb = E->CHECKVAL(2); + procInfo->SetAbsorbDamage(absorb); + return 0; + } + + /** + * Sets the resisted damage of the proc event. + * + * @param uint32 resist : the resist value to set + */ + int SetResistDamage(Eluna* E, ElunaProcInfo* procInfo) + { + uint32 resist = E->CHECKVAL(2); + procInfo->SetResistDamage(resist); + return 0; + } + + /** + * Sets the blocked damage of the proc event. + * + * @param uint32 block : the block value to set + */ + int SetBlockDamage(Eluna* E, ElunaProcInfo* procInfo) + { + uint32 block = E->CHECKVAL(2); + procInfo->SetBlockDamage(block); + return 0; + } + + /** + * Sets the healing of the proc event. + * + * @param int32 heal : the heal value to set + */ + int SetHeal(Eluna* E, ElunaProcInfo* procInfo) + { + int32 heal = E->CHECKVAL(2); + procInfo->SetHeal(heal); + return 0; + } + + /** + * Sets the absorbed healing of the proc event. + * + * @param uint32 absorb : the absorb value to set + */ + int SetAbsorbHeal(Eluna* E, ElunaProcInfo* procInfo) + { + uint32 absorb = E->CHECKVAL(2); + procInfo->SetAbsorbHeal(absorb); + return 0; + } + + /** + * Sets the effective healing of the proc event. + * + * @param uint32 effectiveHeal : the effective heal value to set + */ + int SetEffectiveHeal(Eluna* E, ElunaProcInfo* procInfo) + { + uint32 effectiveHeal = E->CHECKVAL(2); + procInfo->SetEffectiveHeal(effectiveHeal); + return 0; + } + + /** + * Returns the [Map] the proc event occurred on. + * + * @return [Map] map + */ + int GetMap(Eluna* E, ElunaProcInfo* procInfo) + { + E->Push(procInfo->GetMap()); + return 1; + } + + ElunaRegister ElunaProcInfoMethods[] = + { + // Getters + { "GetActor", &LuaElunaProcInfo::GetActor }, + { "GetActionTarget", &LuaElunaProcInfo::GetActionTarget }, + { "GetTypeMask", &LuaElunaProcInfo::GetTypeMask }, + { "GetSpellTypeMask", &LuaElunaProcInfo::GetSpellTypeMask }, + { "GetSpellPhaseMask", &LuaElunaProcInfo::GetSpellPhaseMask }, + { "GetHitMask", &LuaElunaProcInfo::GetHitMask }, + { "GetProcSpell", &LuaElunaProcInfo::GetProcSpell }, + { "GetSpellInfo", &LuaElunaProcInfo::GetSpellInfo }, + { "GetSchoolMask", &LuaElunaProcInfo::GetSchoolMask }, + { "GetDamage", &LuaElunaProcInfo::GetDamage }, + { "GetDamageType", &LuaElunaProcInfo::GetDamageType }, + { "GetAttackType", &LuaElunaProcInfo::GetAttackType }, + { "GetDamageAbsorb", &LuaElunaProcInfo::GetDamageAbsorb }, + { "GetResist", &LuaElunaProcInfo::GetResist }, + { "GetBlock", &LuaElunaProcInfo::GetBlock }, + { "GetHeal", &LuaElunaProcInfo::GetHeal }, + { "GetEffectiveHeal", &LuaElunaProcInfo::GetEffectiveHeal }, + { "GetHealAbsorb", &LuaElunaProcInfo::GetHealAbsorb }, + { "GetMap", &LuaElunaProcInfo::GetMap }, + // Setters + { "SetDamage", &LuaElunaProcInfo::SetDamage }, + { "SetAbsorbDamage", &LuaElunaProcInfo::SetAbsorbDamage }, + { "SetResistDamage", &LuaElunaProcInfo::SetResistDamage }, + { "SetBlockDamage", &LuaElunaProcInfo::SetBlockDamage }, + { "SetHeal", &LuaElunaProcInfo::SetHeal }, + { "SetAbsorbHeal", &LuaElunaProcInfo::SetAbsorbHeal }, + { "SetEffectiveHeal", &LuaElunaProcInfo::SetEffectiveHeal }, + // Booleans + { "HasDamage", &LuaElunaProcInfo::HasDamage }, + { "HasHeal", &LuaElunaProcInfo::HasHeal }, + }; +} + +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/ElunaQueryMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/ElunaQueryMethods.h new file mode 100644 index 0000000000..f27ceb3af7 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/ElunaQueryMethods.h @@ -0,0 +1,358 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef QUERYMETHODS_H +#define QUERYMETHODS_H + +#define RESULT (*result) + +/*** + * The result of a database query. + * + * E.g. the return value of [Global:WorldDBQuery]. + * + * Inherits all methods from: none + */ +namespace LuaQuery +{ + static void CheckFields(Eluna* E, ElunaQuery* result) + { + uint32 field = E->CHECKVAL(2); + uint32 count = RESULT->GetFieldCount(); + if (field >= count) + { + char arr[256]; + sprintf(arr, "trying to access invalid field index %u. There are %u fields available and the indexes start from 0", field, count); + luaL_argerror(E->L, 2, arr); + } + } + + /** + * Returns `true` if the specified column of the current row is `NULL`, otherwise `false`. + * + * @param uint32 column + * @return bool isNull + */ + int IsNull(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + + E->Push(RESULT->Fetch()[col].IsNull()); + return 1; + } + + /** + * Returns the number of columns in the result set. + * + * @return uint32 columnCount + */ + int GetColumnCount(Eluna* E, ElunaQuery* result) + { + E->Push(RESULT->GetFieldCount()); + return 1; + } + + /** + * Returns the number of rows in the result set. + * + * @return uint32 rowCount + */ + int GetRowCount(Eluna* E, ElunaQuery* result) + { + if (RESULT->GetRowCount() > (uint32)-1) + E->Push((uint32)-1); + else + E->Push((uint32)(RESULT->GetRowCount())); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a boolean. + * + * @param uint32 column + * @return bool data + */ + int GetBool(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetBool()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to an unsigned 8-bit integer. + * + * @param uint32 column + * @return uint8 data + */ + int GetUInt8(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetUInt8()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to an unsigned 16-bit integer. + * + * @param uint32 column + * @return uint16 data + */ + int GetUInt16(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetUInt16()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to an unsigned 32-bit integer. + * + * @param uint32 column + * @return uint32 data + */ + int GetUInt32(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetUInt32()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to an unsigned 64-bit integer. + * + * @param uint32 column + * @return uint64 data + */ + int GetUInt64(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetUInt64()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a signed 8-bit integer. + * + * @param uint32 column + * @return int8 data + */ + int GetInt8(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetInt8()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a signed 16-bit integer. + * + * @param uint32 column + * @return int16 data + */ + int GetInt16(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetInt16()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a signed 32-bit integer. + * + * @param uint32 column + * @return int32 data + */ + int GetInt32(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetInt32()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a signed 64-bit integer. + * + * @param uint32 column + * @return int64 data + */ + int GetInt64(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetInt64()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a 32-bit floating point value. + * + * @param uint32 column + * @return float data + */ + int GetFloat(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetFloat()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a 64-bit floating point value. + * + * @param uint32 column + * @return double data + */ + int GetDouble(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + E->Push(RESULT->Fetch()[col].GetDouble()); + return 1; + } + + /** + * Returns the data in the specified column of the current row, casted to a string. + * + * @param uint32 column + * @return string data + */ + int GetString(Eluna* E, ElunaQuery* result) + { + uint32 col = E->CHECKVAL(2); + CheckFields(E, result); + + E->Push(RESULT->Fetch()[col].GetCString()); + return 1; + } + + /** + * Advances the [ElunaQuery] to the next row in the result set. + * + * *Do not* call this immediately after a query, or you'll skip the first row. + * + * Returns `false` if there was no new row, otherwise `true`. + * + * @return bool hadNextRow + */ + int NextRow(Eluna* E, ElunaQuery* result) + { + E->Push(RESULT->NextRow()); + return 1; + } + + /** + * Returns a table from the current row where keys are field names and values are the row's values. + * + * All numerical values will be numbers and everything else is returned as a string. + * + * **For example,** the query: + * + * SELECT entry, name FROM creature_template + * + * would result in a table like: + * + * { entry = 123, name = "some creature name" } + * + * To move to next row use [ElunaQuery:NextRow]. + * + * @return table rowData : table filled with row columns and data where `T[column] = data` + */ + int GetRow(Eluna* E, ElunaQuery* result) + { + uint32 col = RESULT->GetFieldCount(); + Field* row = RESULT->Fetch(); + + lua_createtable(E->L, 0, col); + int tbl = lua_gettop(E->L); + + for (uint32 i = 0; i < col; ++i) + { + QueryResultFieldMetadata const& fieldMetadata = RESULT->GetFieldMetadata(i); + + E->Push(fieldMetadata.Alias); + + if (row[i].IsNull()) + E->Push(); + else + { + switch (fieldMetadata.Type) + { + case DatabaseFieldTypes::UInt8: + case DatabaseFieldTypes::UInt16: + case DatabaseFieldTypes::UInt32: + E->Push(row[i].GetUInt32()); + break; + case DatabaseFieldTypes::Int8: + case DatabaseFieldTypes::Int16: + case DatabaseFieldTypes::Int32: + E->Push(row[i].GetInt32()); + break; + case DatabaseFieldTypes::UInt64: + E->Push(row[i].GetUInt64()); + break; + case DatabaseFieldTypes::Int64: + E->Push(row[i].GetInt64()); + break; + case DatabaseFieldTypes::Float: + case DatabaseFieldTypes::Double: + case DatabaseFieldTypes::Decimal: + E->Push(row[i].GetDouble()); + break; + case DatabaseFieldTypes::Date: + case DatabaseFieldTypes::Time: + case DatabaseFieldTypes::Binary: + E->Push(row[i].GetCString()); + break; + default: + E->Push(); + break; + } + } + lua_rawset(E->L, tbl); + } + + lua_settop(E->L, tbl); + return 1; + } + + ElunaRegister QueryMethods[] = + { + // Getters + { "GetColumnCount", &LuaQuery::GetColumnCount }, + { "GetRowCount", &LuaQuery::GetRowCount }, + { "GetRow", &LuaQuery::GetRow }, + { "GetBool", &LuaQuery::GetBool }, + { "GetUInt8", &LuaQuery::GetUInt8 }, + { "GetUInt16", &LuaQuery::GetUInt16 }, + { "GetUInt32", &LuaQuery::GetUInt32 }, + { "GetUInt64", &LuaQuery::GetUInt64 }, + { "GetInt8", &LuaQuery::GetInt8 }, + { "GetInt16", &LuaQuery::GetInt16 }, + { "GetInt32", &LuaQuery::GetInt32 }, + { "GetInt64", &LuaQuery::GetInt64 }, + { "GetFloat", &LuaQuery::GetFloat }, + { "GetDouble", &LuaQuery::GetDouble }, + { "GetString", &LuaQuery::GetString }, + + // Boolean + { "NextRow", &LuaQuery::NextRow }, + { "IsNull", &LuaQuery::IsNull } + }; +}; +#undef RESULT + +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/GameObjectMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/GameObjectMethods.h new file mode 100644 index 0000000000..3207205b62 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/GameObjectMethods.h @@ -0,0 +1,350 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef GAMEOBJECTMETHODS_H +#define GAMEOBJECTMETHODS_H + +/*** + * Inherits all methods from: [Object], [WorldObject] + */ +namespace LuaGameObject +{ + /** + * Returns 'true' if the [GameObject] can give the specified [Quest] + * + * @param uint32 questId : quest entry Id to check + * @return bool hasQuest + */ + int HasQuest(Eluna* E, GameObject* go) + { + uint32 questId = E->CHECKVAL(2); + + E->Push(go->hasQuest(questId)); + return 1; + } + + /** + * Returns 'true' if the [GameObject] is spawned + * + * @return bool isSpawned + */ + int IsSpawned(Eluna* E, GameObject* go) + { + E->Push(go->isSpawned()); + return 1; + } + + /** + * Returns 'true' if the [GameObject] is a transport + * + * @return bool isTransport + */ + int IsTransport(Eluna* E, GameObject* go) + { + E->Push(go->IsTransport()); + return 1; + } + + /** + * Returns 'true' if the [GameObject] is active + * + * @return bool isActive + */ + int IsActive(Eluna* E, GameObject* go) + { + E->Push(go->isActiveObject()); + return 1; + } + + /** + * Returns true if the [GameObject] is a destructible, false otherwise. + * + * @return bool isDestructible + */ + int IsDestructible(Eluna* E, GameObject* go) + { + E->Push(go->IsDestructibleBuilding()); + return 1; + } + + /** + * Returns display ID of the [GameObject] + * + * @return uint32 displayId + */ + int GetDisplayId(Eluna* E, GameObject* go) + { + E->Push(go->GetDisplayId()); + return 1; + } + + /** + * Returns the state of a [GameObject] + * Below are client side [GOState]s off of 3.3.5a + * + * @table + * @columns [GOState, ID, Comment] + * @values [GO_STATE_ACTIVE, 0, "show in world as used and not reset (closed door open)"] + * @values [GO_STATE_READY, 1, "show in world as ready (closed door close)"] + * @values [GO_STATE_ACTIVE_ALTERNATIVE, 2, "show in world as used in alt way and not reset (closed door open by cannon fire)"] + * + * @return [GOState] goState + */ + #if 0 + int GetGoState(Eluna* E, GameObject* go) + { + E->Push(go->GetGoState()); + return 1; + } + #endif + /** + * Returns the [LootState] of a [GameObject] + * Below are [LootState]s off of 3.3.5a + * + * @table + * @columns [LootState, ID, Comment] + * @values [GO_NOT_READY, 0, ""] + * @values [GO_READY, 1, "can be ready but despawned, and then not possible activate until spawn"] + * @values [GO_ACTIVATED, 2, ""] + * @values [GO_JUST_DEACTIVATED, 3, ""] + * + * @return [LootState] lootState + */ + int GetLootState(Eluna* E, GameObject* go) + { + E->Push(go->getLootState()); + return 1; + } + + /** + * Returns the [Player] that can loot the [GameObject] + * + * Not the original looter and may be nil. + * + * @return [Player] player + */ + int GetLootRecipient(Eluna* E, GameObject* go) + { + E->Push(go->GetLootRecipient()); + return 1; + } + + /** + * Returns the [Group] that can loot the [GameObject] + * + * Not the original looter and may be nil. + * + * @return [Group] group + */ + int GetLootRecipientGroup(Eluna* E, GameObject* go) + { + E->Push(go->GetLootRecipientGroup()); + return 1; + } + + /** + * Returns the guid of the [GameObject] that is used as the ID in the database + * + * @return uint32 dbguid + */ + int GetDBTableGUIDLow(Eluna* E, GameObject* go) + { + E->Push(go->GetSpawnId()); + return 1; + } + + /** + * Sets the state of a [GameObject] + * + * @table + * @columns [GOState, ID, Comment] + * @values [GO_STATE_ACTIVE, 0, "show in world as used and not reset (closed door open)"] + * @values [GO_STATE_READY, 1, "show in world as ready (closed door close)"] + * @values [GO_STATE_ACTIVE_ALTERNATIVE, 2, "show in world as used in alt way and not reset (closed door open by cannon fire)"] + * + * @param [GOState] state : all available go states can be seen above + */ + #if 0 + int SetGoState(Eluna* E, GameObject* go) + { + uint32 state = E->CHECKVAL(2, 0); + + if (state == 0) + go->SetGoState(GO_STATE_ACTIVE); + else if (state == 1) + go->SetGoState(GO_STATE_READY); + else if (state == 2) + go->SetGoState(GO_STATE_DESTROYED); + + return 0; + } + #endif + /** + * Sets the [LootState] of a [GameObject] + * Below are [LootState]s off of 3.3.5a + * + * @table + * @columns [LootState, ID, Comment] + * @values [GO_NOT_READY, 0, ""] + * @values [GO_READY, 1, "can be ready but despawned, and then not possible activate until spawn"] + * @values [GO_ACTIVATED, 2, ""] + * @values [GO_JUST_DEACTIVATED, 3, ""] + * + * @param [LootState] state : all available loot states can be seen above + */ + int SetLootState(Eluna* E, GameObject* go) + { + uint32 state = E->CHECKVAL(2, 0); + + if (state == 0) + go->SetLootState(GO_NOT_READY); + else if (state == 1) + go->SetLootState(GO_READY); + else if (state == 2) + go->SetLootState(GO_ACTIVATED); + else if (state == 3) + go->SetLootState(GO_JUST_DEACTIVATED); + + return 0; + } + + /** + * Saves [GameObject] to the database + * + */ + #if 0 + int SaveToDB(Eluna* /*E*/, GameObject* go) + { + go->SaveToDB(); + return 0; + } + #endif + /** + * Removes [GameObject] from the world + * + * The object is no longer reachable after this and it is not respawned. + * + * @param bool deleteFromDB : if true, it will delete the [GameObject] from the database + */ + int RemoveFromWorld(Eluna* E, GameObject* go) + { + bool deldb = E->CHECKVAL(2, false); + + // cs_gobject.cpp copy paste + ObjectGuid ownerGuid = go->GetOwnerGUID(); + if (!ownerGuid.IsEmpty()) + { + Unit* owner = eObjectAccessor()GetUnit(*go, ownerGuid); + if (!owner || !ownerGuid.IsPlayer()) + return 0; + + owner->RemoveGameObject(go, false); + } + + //if (deldb) + // GameObject::DeleteFromDB(go->GetSpawnId()); + + go->SetRespawnTime(0); + //go->Delete(); + + return 0; + } + + /** + * Activates a door or a button/lever + * + * @param uint32 delay = 0 : cooldown time in seconds to restore the [GameObject] back to normal. 0 for infinite duration + */ + int UseDoorOrButton(Eluna* E, GameObject* go) + { + uint32 delay = E->CHECKVAL(2, 0); + + go->UseDoorOrButton(delay); + return 0; + } + + /** + * Despawns a [GameObject] + * + * The gameobject may be automatically respawned by the core + */ + int Despawn(Eluna* /*E*/, GameObject* go) + { + go->SetLootState(GO_JUST_DEACTIVATED); + return 0; + } + + /** + * Respawns a [GameObject] + */ + int Respawn(Eluna* /*E*/, GameObject* go) + { + go->Respawn(); + return 0; + } + + /** + * Sets the respawn or despawn time for the gameobject. + * + * Respawn time is also used as despawn time depending on gameobject settings + * + * @param int32 delay = 0 : cooldown time in seconds to respawn or despawn the object. 0 means never + */ + int SetRespawnTime(Eluna* E, GameObject* go) + { + int32 respawn = E->CHECKVAL(2); + + go->SetRespawnTime(respawn); + return 0; + } + + /** + * Sets whether or not the [GameObject] will be spawned by default + * + * Primarily used for temporary spawns. + * + * @param bool spawn = true : if false, it will prevent the [GameObject] from respawning by default + */ + int SetSpawnedByDefault(Eluna* E, GameObject* go) + { + bool spawn = E->CHECKVAL(2, true); + + go->SetSpawnedByDefault(spawn); + return 0; + } + + ElunaRegister GameObjectMethods[] = + { + // Getters + { "GetDisplayId", &LuaGameObject::GetDisplayId }, + + { "GetLootState", &LuaGameObject::GetLootState }, + { "GetLootRecipient", &LuaGameObject::GetLootRecipient }, + { "GetLootRecipientGroup", &LuaGameObject::GetLootRecipientGroup }, + { "GetDBTableGUIDLow", &LuaGameObject::GetDBTableGUIDLow }, + + // Setters + + { "SetLootState", &LuaGameObject::SetLootState }, + { "SetRespawnTime", &LuaGameObject::SetRespawnTime }, + { "SetSpawnedByDefault", &LuaGameObject::SetSpawnedByDefault }, + + // Boolean + { "IsTransport", &LuaGameObject::IsTransport }, + { "IsDestructible", &LuaGameObject::IsDestructible }, + { "IsActive", &LuaGameObject::IsActive }, + { "HasQuest", &LuaGameObject::HasQuest }, + { "IsSpawned", &LuaGameObject::IsSpawned }, + + // Other + { "RemoveFromWorld", &LuaGameObject::RemoveFromWorld }, + { "UseDoorOrButton", &LuaGameObject::UseDoorOrButton }, + { "Despawn", &LuaGameObject::Despawn }, + { "Respawn", &LuaGameObject::Respawn }, + + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/GlobalMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/GlobalMethods.h new file mode 100644 index 0000000000..b1956b8c80 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/GlobalMethods.h @@ -0,0 +1,3218 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef GLOBALMETHODS_H +#define GLOBALMETHODS_H + +#include "BindingMap.h" + +/*** + * These functions can be used anywhere at any time, including at start-up. + */ +namespace LuaGlobalFunctions +{ + /** + * Returns Lua engine's name. + * + * Always returns "ElunaEngine" on Eluna. + * + * @return string engineName + */ + int GetLuaEngine(Eluna* E) + { + E->Push("ElunaEngine"); + return 1; + } + + /** + * Returns emulator's name. + * + * The result will be either `MaNGOS`, `cMaNGOS`, or `TrinityCore`. + * + * @return string coreName + */ + int GetCoreName(Eluna* E) + { + E->Push(CORE_NAME); + return 1; + } + + /** + * Returns emulator .conf RealmID + * + * - for MaNGOS returns the realmID as it is stored in the core. + * - for TrinityCore returns the realmID as it is in the conf file. + * + * @return uint32 realm ID + */ + int GetRealmID(Eluna* E) + { + E->Push(sConfigMgr->GetIntDefault("RealmID", 1)); + return 1; + } + + /** + * Returns emulator version + * + * - For TrinityCore returns the date of the last revision, e.g. `2015-08-26 22:53:12 +0300` + * - For cMaNGOS returns the date and time of the last revision, e.g. `2015-09-06 13:18:50` + * - for MaNGOS returns the version number as string, e.g. `21000` + * + * @return string version + */ + int GetCoreVersion(Eluna* E) + { + E->Push(CORE_VERSION); + return 1; + } + + /** + * Returns emulator's supported expansion. + * + * Expansion is 0 for pre-TBC, 1 for TBC, 2 for WotLK, and 3 for Cataclysm. + * + * @return int32 expansion + */ + int GetCoreExpansion(Eluna* E) + { + E->Push(2); + return 1; + } + + /** + * Returns the [Map] pointer of the Lua state. Returns null for the "World" state. + * + * @return [Map] map + */ + int GetStateMap(Eluna* E) + { + E->Push(E->GetBoundMap()); + return 1; + } + + /** + * Returns the map ID of the Lua state. Returns -1 for the "World" state. + * + * @return int32 mapId + */ + int GetStateMapId(Eluna* E) + { + E->Push(E->GetBoundMapId()); + return 1; + } + + /** + * Returns the instance ID of the Lua state. Returns 0 for continent maps and the world state. + * + * @return uint32 instanceId + */ + int GetStateInstanceId(Eluna* E) + { + E->Push(E->GetBoundInstanceId()); + return 1; + } + + /** + * Returns [Quest] template + * + * @param uint32 questId : [Quest] entry ID + * @return [Quest] quest + */ + int GetQuest(Eluna* E) + { + uint32 questId = E->CHECKVAL(1); + + E->Push(eObjectMgr->GetQuestTemplate(questId)); + return 1; + } + + /** + * Finds and Returns [Player] by guid if found + * + * In multistate, this method is only available in the WORLD state + * + * @param ObjectGuid guid : guid of the [Player], you can get it with [Object:GetGUID] + * @return [Player] player + */ + int GetPlayerByGUID(Eluna* E) + { + ObjectGuid guid = E->CHECKVAL(1); + E->Push(eObjectAccessor()FindPlayer(guid)); + return 1; + } + + /** + * Finds and Returns [Player] by name if found + * + * In multistate, this method is only available in the WORLD state + * + * @param string name : name of the [Player] + * @return [Player] player + */ + int GetPlayerByName(Eluna* E) + { + const char* name = E->CHECKVAL(1); + E->Push(eObjectAccessor()FindPlayerByName(name)); + return 1; + } + + /** + * Returns game time in seconds + * + * @return uint32 time + */ + #if 0 + int GetGameTime(Eluna* E) + { + E->Push(uint32(GameTime::GetGameTime())); + return 1; + } + #endif + /** + * Returns a table with all the current [Player]s in the world + * + * Does not return players that may be teleporting or otherwise not on any map. + * + * @table + * @columns [Team, ID] + * @values [ALLIANCE, 0] + * @values [HORDE, 1] + * @values [NEUTRAL, 2] + * + * In multistate, this method is only available in the WORLD state + * + * @param [TeamId] team = TEAM_NEUTRAL : optional check team of the [Player], Alliance, Horde or Neutral (All) + * @param bool onlyGM = false : optional check if GM only + * @return table worldPlayers + */ + #if 0 + int GetPlayersInWorld(Eluna* E) + { + uint32 team = E->CHECKVAL(1, TEAM_NEUTRAL); + bool onlyGM = E->CHECKVAL(2, false); + + lua_newtable(E->L); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + std::shared_lock lock(*HashMapHolder::GetLock()); + const HashMapHolder::MapType& m = eObjectAccessor()GetPlayers(); + for (HashMapHolder::MapType::const_iterator it = m.begin(); it != m.end(); ++it) + { + if (Player* player = it->second) + { + if (!player->IsInWorld()) + continue; + + if ((team == TEAM_NEUTRAL || uint32(player->GetTeamId()) == team) && (!onlyGM || player->IsGameMaster())) + { + E->Push(player); + lua_rawseti(E->L, tbl, ++i); + } + } + } + + lua_settop(E->L, tbl); // push table to top of stack + return 1; + } + #endif + /** + * Returns a table with all the current [Player]s on the states map. + * + * Does not return players that may be teleporting or otherwise not on the map. + * + * @table + * @columns [Team, ID] + * @values [ALLIANCE, 0] + * @values [HORDE, 1] + * @values [NEUTRAL, 2] + * + * In multistate, this method is only available in the MAP state + * + * @param [TeamId] team = TEAM_NEUTRAL : optional check team of the [Player], Alliance, Horde or Neutral (All) + * @param bool onlyGM = false : optional check if GM only + * @return table mapPlayers + */ + #if 0 + int GetPlayersOnMap(Eluna* E) + { + uint32 team = E->CHECKVAL(1, TEAM_NEUTRAL); + bool onlyGM = E->CHECKVAL(2, false); + + lua_newtable(E->L); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + Map::PlayerList const& players = E->GetBoundMap()->GetPlayers(); + if (!players.isEmpty()) + { + for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) + { + if (Player* player = it->GetSource()) + { + if (!player->IsInWorld()) + continue; + + if ((team == TEAM_NEUTRAL || uint32(player->GetTeamId()) == team) && (!onlyGM || player->IsGameMaster())) + { + E->Push(player); + lua_rawseti(E->L, tbl, ++i); + } + } + } + } + + lua_settop(E->L, tbl); // push table to top of stack + return 1; + } + #endif + /** + * Returns a [Guild] by name. + * + * @param string name + * @return [Guild] guild : the Guild, or `nil` if it doesn't exist + */ + int GetGuildByName(Eluna* E) + { + const char* name = E->CHECKVAL(1); + E->Push(eGuildMgr->GetGuildByName(name)); + return 1; + } + + /** + * Returns a [Map] by ID. + * + * In multistate, this method is only available in the WORLD state + * + * @param uint32 mapId : see [Map.dbc](https://github.com/cmangos/issues/wiki/Map.dbc) + * @param uint32 instanceId = 0 : required if the map is an instance, otherwise don't pass anything + * @return [Map] map : the Map, or `nil` if it doesn't exist + */ + int GetMapById(Eluna* E) + { + uint32 mapid = E->CHECKVAL(1); + uint32 instance = E->CHECKVAL(2, 0); + + E->Push(eMapMgr->FindMap(mapid, instance)); + return 1; + } + + /** + * Returns [Guild] by the leader's GUID + * + * @param ObjectGuid guid : the guid of a [Guild] leader + * @return [Guild] guild, or `nil` if it doesn't exist + */ + int GetGuildByLeaderGUID(Eluna* E) + { + ObjectGuid guid = E->CHECKVAL(1); + + E->Push(eGuildMgr->GetGuildByLeader(guid)); + return 1; + } + + /** + * Returns the amount of [Player]s in the world. + * + * @return uint32 count + */ + int GetPlayerCount(Eluna* E) + { + E->Push(eWorld->GetActiveSessionCount()); + return 1; + } + + /** + * Builds a [Player]'s GUID + * + * [Player] GUID consist of low GUID and type ID + * + * [Player] and [Creature] for example can have the same low GUID but not GUID. + * + * @param uint32 lowguid : low GUID of the [Player] + * @return ObjectGuid guid + */ + #if 0 + int GetPlayerGUID(Eluna* E) + { + uint32 lowguid = E->CHECKVAL(1); + E->Push(ObjectGuid::Create(lowguid)); + return 1; + } + #endif + /** + * Builds an [Item]'s GUID. + * + * [Item] GUID consist of low GUID and type ID + * [Player] and [Item] for example can have the same low GUID but not GUID. + * + * @param uint32 lowguid : low GUID of the [Item] + * @return ObjectGuid guid + */ + #if 0 + int GetItemGUID(Eluna* E) + { + uint32 lowguid = E->CHECKVAL(1); + E->Push(ObjectGuid::Create(lowguid)); + return 1; + } + #endif + /** + * Builds a [GameObject]'s GUID. + * + * A GameObject's GUID consist of entry ID, low GUID and type ID + * + * A [Player] and GameObject for example can have the same low GUID but not GUID. + * + * @param uint32 lowguid : low GUID of the [GameObject] + * @param uint32 entry : entry ID of the [GameObject] + * @return ObjectGuid guid + */ + + #if 0 + int GetObjectGUID(Eluna* E) + { + uint32 lowguid = E->CHECKVAL(1); + uint32 entry = E->CHECKVAL(2); + E->Push(ObjectGuid::Create(entry, lowguid)); + return 1; + } + #endif + /** + * Builds a [Creature]'s GUID. + * + * [Creature] GUID consist of entry ID, low GUID and type ID + * + * [Player] and [Creature] for example can have the same low GUID but not GUID. + * + * @param uint32 lowguid : low GUID of the [Creature] + * @param uint32 entry : entry ID of the [Creature] + * @return ObjectGuid guid + */ + #if 0 + int GetUnitGUID(Eluna* E) + { + uint32 lowguid = E->CHECKVAL(1); + uint32 entry = E->CHECKVAL(2); + E->Push(ObjectGuid::Create(entry, lowguid)); + return 1; + } + #endif + /** + * Returns the low GUID from a GUID. + * + * A GUID consists of a low GUID, type ID, and possibly an entry ID depending on the type ID. + * + * Low GUID is an ID to distinct the objects of the same type. + * + * [Player] and [Creature] for example can have the same low GUID but not GUID. + * + * On TrinityCore all low GUIDs are different for all objects of the same type. + * For example creatures in instances are assigned new GUIDs when the Map is created. + * + * On MaNGOS and cMaNGOS low GUIDs are unique only on the same map. + * For example creatures in instances use the same low GUID assigned for that spawn in the database. + * This is why to identify a creature you have to know the instanceId and low GUID. See [Map:GetIntstanceId] + * + * @param ObjectGuid guid : GUID of an [Object] + * @return uint32 lowguid : low GUID of the [Object] + */ + #if 0 + int GetGUIDLow(Eluna* E) + { + ObjectGuid guid = E->CHECKVAL(1); + + E->Push(guid.GetCounter()); + return 1; + } + #endif + /** + * Returns a chat link for an [Item]. + * + * @table + * @columns [Locale, Value] + * @values [enUS, 0] + * @values [koKR, 1] + * @values [frFR, 2] + * @values [deDE, 3] + * @values [zhCN, 4] + * @values [zhTW, 5] + * @values [esES, 6] + * @values [esMX, 7] + * @values [ruRU, 8] + * + * @param uint32 entry : entry ID of an [Item] + * @param [LocaleConstant] locale = DEFAULT_LOCALE : locale to return the [Item] name in + * @return string itemLink + */ + #if 0 + int GetItemLink(Eluna* E) + { + uint32 entry = E->CHECKVAL(1); + uint8 locale = E->CHECKVAL(2, DEFAULT_LOCALE); + if (locale >= TOTAL_LOCALES) + return luaL_argerror(E->L, 2, "valid LocaleConstant expected"); + + const ItemTemplate* temp = eObjectMgr->GetItemTemplate(entry); + if (!temp) + return luaL_argerror(E->L, 1, "valid ItemEntry expected"); + + std::string name = temp->Name1; + if (ItemLocale const* il = eObjectMgr->GetItemLocale(entry)) + ObjectMgr::GetLocaleString(il->Name, static_cast(locale), name); + + std::ostringstream oss; + oss << "|c" << std::hex << ItemQualityColors[temp->Quality] << std::dec << + "|Hitem:" << entry << ":0:" << + "0:0:0:0:" << + "0:0:0:0|h[" << name << "]|h|r"; + + E->Push(oss.str()); + return 1; + } + #endif + /** + * Returns the type ID from a GUID. + * + * Type ID is different for each type ([Player], [Creature], [GameObject], etc.). + * + * GUID consist of entry ID, low GUID, and type ID. + * + * @param ObjectGuid guid : GUID of an [Object] + * @return int32 typeId : type ID of the [Object] + */ + #if 0 + int GetGUIDType(Eluna* E) + { + ObjectGuid guid = E->CHECKVAL(1); + E->Push(static_cast(guid.GetHigh())); + return 1; + } + #endif + /** + * Returns the entry ID from a GUID. + * + * GUID consist of entry ID, low GUID, and type ID. + * + * @param ObjectGuid guid : GUID of an [Creature] or [GameObject] + * @return uint32 entry : entry ID, or `0` if `guid` is not a [Creature] or [GameObject] + */ + #if 0 + int GetGUIDEntry(Eluna* E) + { + ObjectGuid guid = E->CHECKVAL(1); + E->Push(guid.GetEntry()); + return 1; + } + #endif + /** + * Returns the area or zone's name. + * + * @table + * @columns [Locale, Value] + * @values [enUS, 0] + * @values [koKR, 1] + * @values [frFR, 2] + * @values [deDE, 3] + * @values [zhCN, 4] + * @values [zhTW, 5] + * @values [esES, 6] + * @values [esMX, 7] + * @values [ruRU, 8] + * + * @param uint32 areaOrZoneId : area ID or zone ID + * @param [LocaleConstant] locale = DEFAULT_LOCALE : locale to return the name in + * @return string areaOrZoneName + */ + #if 0 + int GetAreaName(Eluna* E) + { + uint32 areaOrZoneId = E->CHECKVAL(1); + uint8 locale = E->CHECKVAL(2, DEFAULT_LOCALE); + if (locale >= TOTAL_LOCALES) + return luaL_argerror(E->L, 2, "valid LocaleConstant expected"); + + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaOrZoneId); + if (!areaEntry) + return luaL_argerror(E->L, 1, "valid Area or Zone ID expected"); + + E->Push(areaEntry->AreaName[locale]); + return 1; + } + #endif + /** + * Returns the currently active game events. + * + * @return table activeEvents + */ + int GetActiveGameEvents(Eluna* E) + { + lua_newtable(E->L); + int tbl = lua_gettop(E->L); + uint32 counter = 1; + GameEventMgr::ActiveEvents const& activeEvents = eGameEventMgr->GetActiveEventList(); + + for (GameEventMgr::ActiveEvents::const_iterator i = activeEvents.begin(); i != activeEvents.end(); ++i) + { + E->Push(*i); + lua_rawseti(E->L, tbl, counter); + + counter++; + } + + lua_settop(E->L, tbl); + return 1; + } + + static int RegisterEntryHelper(Eluna* E, int regtype) + { + uint32 id = E->CHECKVAL(1); + uint32 ev = E->CHECKVAL(2); + luaL_checktype(E->L, 3, LUA_TFUNCTION); + uint32 shots = E->CHECKVAL(4, 0); + + lua_pushvalue(E->L, 3); + int functionRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + if (functionRef >= 0) + return E->Register(regtype, id, ObjectGuid(), 0, ev, functionRef, shots); + else + luaL_argerror(E->L, 3, "unable to make a ref to function"); + return 0; + } + + static int RegisterEventHelper(Eluna* E, int regtype) + { + uint32 ev = E->CHECKVAL(1); + luaL_checktype(E->L, 2, LUA_TFUNCTION); + uint32 shots = E->CHECKVAL(3, 0); + + lua_pushvalue(E->L, 2); + int functionRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + if (functionRef >= 0) + return E->Register(regtype, 0, ObjectGuid(), 0, ev, functionRef, shots); + else + luaL_argerror(E->L, 2, "unable to make a ref to function"); + return 0; + } + + static int RegisterUniqueHelper(Eluna* E, int regtype) + { + ObjectGuid guid = E->CHECKVAL(1); + uint32 instanceId = E->CHECKVAL(2); + uint32 ev = E->CHECKVAL(3); + luaL_checktype(E->L, 4, LUA_TFUNCTION); + uint32 shots = E->CHECKVAL(5, 0); + + lua_pushvalue(E->L, 4); + int functionRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + if (functionRef >= 0) + return E->Register(regtype, 0, guid, instanceId, ev, functionRef, shots); + else + luaL_argerror(E->L, 4, "unable to make a ref to function"); + return 0; + } + + /** + * Registers a server event handler. + * + * @hook server + * @table + * @columns [Event, State, Parameters, Comment] + * @values [SERVER_EVENT_ON_NETWORK_START, "WORLD", , "Not Implemented"] + * @values [SERVER_EVENT_ON_NETWORK_STOP, "WORLD", , "Not Implemented"] + * @values [SERVER_EVENT_ON_SOCKET_OPEN, "WORLD", , "Not Implemented"] + * @values [SERVER_EVENT_ON_SOCKET_CLOSE, "WORLD", , "Not Implemented"] + * @values [SERVER_EVENT_ON_PACKET_RECEIVE, "WORLD", , "Player only if accessible. Can return false, newPacket"] + * @values [SERVER_EVENT_ON_PACKET_RECEIVE_UNKNOWN, "WORLD", , "Not Implemented"] + * @values [SERVER_EVENT_ON_PACKET_SEND, "WORLD", , "Player only if accessible. Can return false"] + * @values [WORLD_EVENT_ON_OPEN_STATE_CHANGE, "WORLD", , "Needs core support on Mangos"] + * @values [WORLD_EVENT_ON_CONFIG_LOAD, "WORLD", , ""] + * @values [WORLD_EVENT_ON_SHUTDOWN_INIT, "WORLD", , ""] + * @values [WORLD_EVENT_ON_SHUTDOWN_CANCEL, "WORLD", , ""] + * @values [WORLD_EVENT_ON_UPDATE, "WORLD", , ""] + * @values [WORLD_EVENT_ON_STARTUP, "WORLD", , ""] + * @values [WORLD_EVENT_ON_SHUTDOWN, "WORLD", , ""] + * @values [ELUNA_EVENT_ON_LUA_STATE_CLOSE, "ALL", , "Triggers just before shutting down the Eluna state (on shutdown, restart and reload)"] + * @values [MAP_EVENT_ON_CREATE, "MAP", , ""] + * @values [MAP_EVENT_ON_DESTROY, "MAP", , ""] + * @values [MAP_EVENT_ON_GRID_LOAD, "MAP", , "Not Implemented"] + * @values [MAP_EVENT_ON_GRID_UNLOAD, "MAP", , "Not Implemented"] + * @values [MAP_EVENT_ON_PLAYER_ENTER, "MAP", , ""] + * @values [MAP_EVENT_ON_PLAYER_LEAVE, "MAP", , ""] + * @values [MAP_EVENT_ON_UPDATE, "MAP", , ""] + * @values [TRIGGER_EVENT_ON_TRIGGER, "MAP", , "Can return true"] + * @values [WEATHER_EVENT_ON_CHANGE, "WORLD", , ""] + * @values [AUCTION_EVENT_ON_ADD, "WORLD", , ""] + * @values [AUCTION_EVENT_ON_REMOVE, "WORLD", , ""] + * @values [AUCTION_EVENT_ON_SUCCESSFUL, "WORLD", , ""] + * @values [AUCTION_EVENT_ON_EXPIRE, "WORLD", , ""] + * @values [ADDON_EVENT_ON_MESSAGE, "WORLD", , "Target can be nil/whisper_target/guild/group/channel. Can return false"] + * @values [WORLD_EVENT_ON_DELETE_CREATURE, "MAP", , ""] + * @values [WORLD_EVENT_ON_DELETE_GAMEOBJECT, "MAP", , ""] + * @values [ELUNA_EVENT_ON_LUA_STATE_OPEN, "ALL", , "Triggers after all scripts are loaded"] + * @values [GAME_EVENT_START, "WORLD", , ""] + * @values [GAME_EVENT_STOP, "WORLD", , ""] + * + * @proto cancel = (event, function) + * @proto cancel = (event, function, shots) + * + * @param uint32 event : server event ID, refer to table above + * @param function function : function that will be called when the event occurs + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterServerEvent(Eluna* E) + { + return RegisterEventHelper(E, Hooks::REGTYPE_SERVER); + } + + /** + * Registers a [Player] event handler. + * + * @hook player + * @table + * @columns [Event, State, Parameters, Comment] + * @values [PLAYER_EVENT_ON_CHARACTER_CREATE, "WORLD", , ""] + * @values [PLAYER_EVENT_ON_CHARACTER_DELETE, "WORLD", , ""] + * @values [PLAYER_EVENT_ON_LOGIN, "WORLD", , ""] + * @values [PLAYER_EVENT_ON_LOGOUT, "WORLD", , ""] + * @values [PLAYER_EVENT_ON_SPELL_CAST, "MAP", , ""] + * @values [PLAYER_EVENT_ON_KILL_PLAYER, "MAP", , ""] + * @values [PLAYER_EVENT_ON_KILL_CREATURE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_KILLED_BY_CREATURE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_DUEL_REQUEST, "MAP", , ""] + * @values [PLAYER_EVENT_ON_DUEL_START, "MAP", , ""] + * @values [PLAYER_EVENT_ON_DUEL_END, "MAP", , ""] + * @values [PLAYER_EVENT_ON_GIVE_XP, "MAP", , "Can return new XP amount"] + * @values [PLAYER_EVENT_ON_LEVEL_CHANGE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_MONEY_CHANGE, "MAP", , "Can return new money amount"] + * @values [PLAYER_EVENT_ON_REPUTATION_CHANGE, "MAP", , "Can return new standing"] + * @values [PLAYER_EVENT_ON_TALENTS_CHANGE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_TALENTS_RESET, "MAP", , ""] + * @values [PLAYER_EVENT_ON_CHAT, "WORLD", , "Can return false, newMessage"] + * @values [PLAYER_EVENT_ON_WHISPER, "WORLD", , "Can return false, newMessage"] + * @values [PLAYER_EVENT_ON_GROUP_CHAT, "WORLD", , "Can return false, newMessage"] + * @values [PLAYER_EVENT_ON_GUILD_CHAT, "WORLD", , "Can return false, newMessage"] + * @values [PLAYER_EVENT_ON_CHANNEL_CHAT, "WORLD", , "Can return false, newMessage"] + * @values [PLAYER_EVENT_ON_EMOTE, "MAP", , "Not triggered on any known emote"] + * @values [PLAYER_EVENT_ON_TEXT_EMOTE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_SAVE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_BIND_TO_INSTANCE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_UPDATE_ZONE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_MAP_CHANGE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_EQUIP, "MAP", , ""] + * @values [PLAYER_EVENT_ON_FIRST_LOGIN, "WORLD", , ""] + * @values [PLAYER_EVENT_ON_CAN_USE_ITEM, "MAP", , "Can return InventoryResult enum value"] + * @values [PLAYER_EVENT_ON_LOOT_ITEM, "MAP", , ""] + * @values [PLAYER_EVENT_ON_ENTER_COMBAT, "MAP", , ""] + * @values [PLAYER_EVENT_ON_LEAVE_COMBAT, "MAP", , ""] + * @values [PLAYER_EVENT_ON_REPOP, "MAP", , ""] + * @values [PLAYER_EVENT_ON_RESURRECT, "MAP", , ""] + * @values [PLAYER_EVENT_ON_LOOT_MONEY, "MAP", , ""] + * @values [PLAYER_EVENT_ON_QUEST_ABANDON, "MAP", , ""] + * @values [PLAYER_EVENT_ON_LEARN_TALENTS, "MAP", , ""] + * @values [PLAYER_EVENT_ON_ENVIRONMENTAL_DEATH, "MAP", , ""] + * @values [PLAYER_EVENT_ON_TRADE_ACCEPT, "MAP", , "Can return false to interrupt trade"] + * @values [PLAYER_EVENT_ON_COMMAND, "WORLD", , "Player is nil if command used from console. Can return false"] + * @values [PLAYER_EVENT_ON_SKILL_CHANGE, "MAP", , "Returns new skill level value"] + * @values [PLAYER_EVENT_ON_LEARN_SPELL, "MAP", , ""] + * @values [PLAYER_EVENT_ON_ACHIEVEMENT_COMPLETE, "MAP", , ""] + * @values [PLAYER_EVENT_ON_DISCOVER_AREA, "MAP", , ""] + * @values [PLAYER_EVENT_ON_UPDATE_AREA, "MAP", , ""] + * @values [PLAYER_EVENT_ON_TRADE_INIT, "MAP", , "Can return false to interrupt trade"] + * @values [PLAYER_EVENT_ON_SEND_MAIL, "MAP", , "Can return false to interrupt sending"] + * @values [PLAYER_EVENT_ON_QUEST_STATUS_CHANGED, "MAP", , ""] + * + * @proto cancel = (event, function) + * @proto cancel = (event, function, shots) + * + * @param uint32 event : [Player] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterPlayerEvent(Eluna* E) + { + return RegisterEventHelper(E, Hooks::REGTYPE_PLAYER); + } + + /** + * Registers a [Guild] event handler. + * + * @hook guild + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GUILD_EVENT_ON_ADD_MEMBER, "WORLD", , ""] + * @values [GUILD_EVENT_ON_REMOVE_MEMBER, "WORLD", , ""] + * @values [GUILD_EVENT_ON_MOTD_CHANGE, "WORLD", , ""] + * @values [GUILD_EVENT_ON_INFO_CHANGE, "WORLD", , ""] + * @values [GUILD_EVENT_ON_CREATE, "WORLD", , "Not on TC"] + * @values [GUILD_EVENT_ON_DISBAND, "WORLD", , ""] + * @values [GUILD_EVENT_ON_MONEY_WITHDRAW, "WORLD", , "Can return new money amount"] + * @values [GUILD_EVENT_ON_MONEY_DEPOSIT, "WORLD", , "Can return new money amount"] + * @values [GUILD_EVENT_ON_ITEM_MOVE, "WORLD", , "TODO"] + * @values [GUILD_EVENT_ON_EVENT, "WORLD", , "TODO"] + * @values [GUILD_EVENT_ON_BANK_EVENT, "WORLD", , ""] + * + * @proto cancel = (event, function) + * @proto cancel = (event, function, shots) + * + * @param uint32 event : [Guild] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterGuildEvent(Eluna* E) + { + return RegisterEventHelper(E, Hooks::REGTYPE_GUILD); + } + + /** + * Registers a [Group] event handler. + * + * @hook group + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GROUP_EVENT_ON_MEMBER_ADD, "WORLD", , ""] + * @values [GROUP_EVENT_ON_MEMBER_INVITE, "WORLD", , ""] + * @values [GROUP_EVENT_ON_MEMBER_REMOVE, "WORLD", , ""] + * @values [GROUP_EVENT_ON_LEADER_CHANGE, "WORLD", , ""] + * @values [GROUP_EVENT_ON_DISBAND, "WORLD", , ""] + * @values [GROUP_EVENT_ON_CREATE, "WORLD", , ""] + * @values [GROUP_EVENT_ON_MEMBER_ACCEPT, "WORLD", , "Can return false to disable accepting"] + * + * @proto cancel = (event, function) + * @proto cancel = (event, function, shots) + * + * @param uint32 event : [Group] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterGroupEvent(Eluna* E) + { + return RegisterEventHelper(E, Hooks::REGTYPE_GROUP); + } + + /** + * Registers a [BattleGround] event handler. + * + * @hook bg + * @table + * @columns [Event, State, Parameters, Comment] + * @values [BG_EVENT_ON_START, "MAP", , "Needs to be added to TC"] + * @values [BG_EVENT_ON_END, "MAP", , "Needs to be added to TC"] + * @values [BG_EVENT_ON_CREATE, "MAP", , "Needs to be added to TC"] + * @values [BG_EVENT_ON_PRE_DESTROY, "MAP", , "Needs to be added to TC"] + * + * @proto cancel = (event, function) + * @proto cancel = (event, function, shots) + * + * @param uint32 event : [BattleGround] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterBGEvent(Eluna* E) + { + return RegisterEventHelper(E, Hooks::REGTYPE_BG); + } + + /** + * Registers a [WorldPacket] event handler. + * + * @hook packet + * @table + * @columns [Event, State, Parameters, Comment] + * @values [PACKET_EVENT_ON_PACKET_RECEIVE, "WORLD", , "Player only if accessible. Can return false, newPacket"] + * @values [PACKET_EVENT_ON_PACKET_SEND, "WORLD", , "Player only if accessible. Can return false"] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : opcode + * @param uint32 event : packet event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterPacketEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_PACKET); + } + + /** + * Registers a [Creature] gossip event handler. + * + * @hook gossip + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GOSSIP_EVENT_ON_HELLO, "MAP", , "Object is the Creature/GameObject/Item. Can return false to do default action."] + * @values [GOSSIP_EVENT_ON_SELECT, "MAP", , "Object is the Creature/GameObject/Item/Player, menu_id is only for player gossip. Can return false to do default action."] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : [Creature] entry Id + * @param uint32 event : [Creature] gossip event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterCreatureGossipEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_CREATURE_GOSSIP); + } + + /** + * Registers a [GameObject] gossip event handler. + * + * @hook gossip + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GOSSIP_EVENT_ON_HELLO, "MAP", , "Object is the Creature/GameObject/Item. Can return false to do default action."] + * @values [GOSSIP_EVENT_ON_SELECT, "MAP", , "Object is the Creature/GameObject/Item/Player, menu_id is only for player gossip. Can return false to do default action."] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : [GameObject] entry Id + * @param uint32 event : [GameObject] gossip event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterGameObjectGossipEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_GAMEOBJECT_GOSSIP); + } + + /** + * Registers an [Item] event handler. + * + * @hook item + * @table + * @columns [Event, State, Parameters, Comment] + * @values [ITEM_EVENT_ON_DUMMY_EFFECT, "MAP", , ""] + * @values [ITEM_EVENT_ON_USE, "MAP", , "Can return false to stop the spell casting"] + * @values [ITEM_EVENT_ON_QUEST_ACCEPT, "MAP", , "Can return true"] + * @values [ITEM_EVENT_ON_EXPIRE, "MAP", , "Can return true"] + * @values [ITEM_EVENT_ON_REMOVE, "MAP", , "Can return true"] + * @values [ITEM_EVENT_ON_ADD, "MAP", , ""] + * @values [ITEM_EVENT_ON_EQUIP, "MAP", , ""] + * @values [ITEM_EVENT_ON_UNEQUIP, "MAP", , ""] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : [Item] entry Id + * @param uint32 event : [Item] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterItemEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_ITEM); + } + + /** + * Registers an [Item] gossip event handler. + * + * @hook gossip + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GOSSIP_EVENT_ON_HELLO, "MAP", , "Object is the Creature/GameObject/Item. Can return false to do default action. For item gossip can return false to stop spell casting."] + * @values [GOSSIP_EVENT_ON_SELECT, "MAP", , "Object is the Creature/GameObject/Item/Player, menu_id is only for player gossip. Can return false to do default action."] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : [Item] entry Id + * @param uint32 event : [Item] gossip event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterItemGossipEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_ITEM_GOSSIP); + } + + /** + * Registers a [Map] event handler for all instances of a [Map]. + * + * @hook map + * @table + * @columns [Event, State, Parameters, Comment] + * @values [INSTANCE_EVENT_ON_INITIALIZE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_LOAD, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_UPDATE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_PLAYER_ENTER, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_CREATURE_CREATE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_GAMEOBJECT_CREATE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS, "MAP", , ""] + * + * @param uint32 map_id : ID of a [Map] + * @param uint32 event : [Map] event ID, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + */ + int RegisterMapEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_MAP); + } + + /** + * Registers a [Map] event handler for one instance of a [Map]. + * + * @hook instance + * @table + * @columns [Event, State, Parameters, Comment] + * @values [INSTANCE_EVENT_ON_INITIALIZE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_LOAD, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_UPDATE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_PLAYER_ENTER, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_CREATURE_CREATE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_GAMEOBJECT_CREATE, "MAP", , ""] + * @values [INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS, "MAP", , ""] + * + * @param uint32 instance_id : ID of an instance of a [Map] + * @param uint32 event : [Map] event ID, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + */ + int RegisterInstanceEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_INSTANCE); + } + + /** + * Registers a [Player] gossip event handler. + * + * @hook player + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GOSSIP_EVENT_ON_HELLO, "MAP", <...>, "Not applicable for player gossip (players have no hello)."] + * @values [GOSSIP_EVENT_ON_SELECT, "MAP", , "Object is the Creature/GameObject/Item/Player, menu_id is only for player gossip. Can return false to do default action."] + * + * @proto cancel = (menu_id, event, function) + * @proto cancel = (menu_id, event, function, shots) + * + * @param uint32 menu_id : [Player] gossip menu Id + * @param uint32 event : [Player] gossip event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterPlayerGossipEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_PLAYER_GOSSIP); + } + + /** + * Registers a [Creature] event handler. + * + * @hook creature + * @table + * @columns [Event, State, Parameters, Comment] + * @values [CREATURE_EVENT_ON_ENTER_COMBAT, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_LEAVE_COMBAT, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_TARGET_DIED, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_DIED, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SPAWN, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_REACH_WP, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_AIUPDATE, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_RECEIVE_EMOTE, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_DAMAGE_TAKEN, "MAP", , "Can return true to stop normal action, can return new damage as second return value."] + * @values [CREATURE_EVENT_ON_PRE_COMBAT, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_OWNER_ATTACKED, "MAP", , "Can return true to stop normal action. Not on mangos"] + * @values [CREATURE_EVENT_ON_OWNER_ATTACKED_AT, "MAP", , "Can return true to stop normal action. Not on mangos"] + * @values [CREATURE_EVENT_ON_HIT_BY_SPELL, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SPELL_HIT_TARGET, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, "MAP", , "Can return true to stop normal action. Not on mangos"] + * @values [CREATURE_EVENT_ON_SUMMONED, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_RESET, "MAP", , ""] + * @values [CREATURE_EVENT_ON_REACH_HOME, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_CORPSE_REMOVED, "MAP", , "Can return true to stop normal action, can return new respawndelay as second return value"] + * @values [CREATURE_EVENT_ON_MOVE_IN_LOS, "MAP", , "Can return true to stop normal action. Does not actually check LOS, just uses the sight range"] + * @values [CREATURE_EVENT_ON_DUMMY_EFFECT, "MAP", , ""] + * @values [CREATURE_EVENT_ON_QUEST_ACCEPT, "MAP", , "Can return true"] + * @values [CREATURE_EVENT_ON_QUEST_REWARD, "MAP", , "Can return true"] + * @values [CREATURE_EVENT_ON_DIALOG_STATUS, "MAP", , ""] + * @values [CREATURE_EVENT_ON_ADD, "MAP", , ""] + * @values [CREATURE_EVENT_ON_REMOVE, "MAP", , ""] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : the ID of one or more [Creature]s + * @param uint32 event : refer to table above + * @param function function : function that will be called when the event occurs + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterCreatureEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_CREATURE); + } + + /** + * Registers a [Creature] event handler for a *single* [Creature]. + * + * @hook creature + * @table + * @columns [Event, State, Parameters, Comment] + * @values [CREATURE_EVENT_ON_ENTER_COMBAT, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_LEAVE_COMBAT, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_TARGET_DIED, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_DIED, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SPAWN, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_REACH_WP, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_AIUPDATE, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_RECEIVE_EMOTE, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_DAMAGE_TAKEN, "MAP", , "Can return true to stop normal action, can return new damage as second return value."] + * @values [CREATURE_EVENT_ON_PRE_COMBAT, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_OWNER_ATTACKED, "MAP", , "Can return true to stop normal action. Not on mangos"] + * @values [CREATURE_EVENT_ON_OWNER_ATTACKED_AT, "MAP", , "Can return true to stop normal action. Not on mangos"] + * @values [CREATURE_EVENT_ON_HIT_BY_SPELL, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SPELL_HIT_TARGET, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, "MAP", , "Can return true to stop normal action. Not on mangos"] + * @values [CREATURE_EVENT_ON_SUMMONED, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_RESET, "MAP", , ""] + * @values [CREATURE_EVENT_ON_REACH_HOME, "MAP", , "Can return true to stop normal action"] + * @values [CREATURE_EVENT_ON_CORPSE_REMOVED, "MAP", , "Can return true to stop normal action, can return new respawndelay as second return value"] + * @values [CREATURE_EVENT_ON_MOVE_IN_LOS, "MAP", , "Can return true to stop normal action. Does not actually check LOS, just uses the sight range"] + * @values [CREATURE_EVENT_ON_DUMMY_EFFECT, "MAP", , ""] + * @values [CREATURE_EVENT_ON_QUEST_ACCEPT, "MAP", , "Can return true"] + * @values [CREATURE_EVENT_ON_QUEST_REWARD, "MAP", , "Can return true"] + * @values [CREATURE_EVENT_ON_DIALOG_STATUS, "MAP", , ""] + * @values [CREATURE_EVENT_ON_ADD, "MAP", , ""] + * @values [CREATURE_EVENT_ON_REMOVE, "MAP", , ""] + * + * @proto cancel = (guid, instance_id, event, function) + * @proto cancel = (guid, instance_id, event, function, shots) + * + * @param ObjectGuid guid : the GUID of a single [Creature] + * @param uint32 instance_id : the instance ID of a single [Creature] + * @param uint32 event : refer to table above + * @param function function : function that will be called when the event occurs + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterUniqueCreatureEvent(Eluna* E) + { + return RegisterUniqueHelper(E, Hooks::REGTYPE_CREATURE_UNIQUE); + } + + /** + * Registers a [GameObject] event handler. + * + * @hook gameobject + * @table + * @columns [Event, State, Parameters, Comment] + * @values [GAMEOBJECT_EVENT_ON_AIUPDATE, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_SPAWN, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, "MAP", , "Can return true to stop normal action"] + * @values [GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, "MAP", , "Can return true to stop normal action"] + * @values [GAMEOBJECT_EVENT_ON_QUEST_REWARD, "MAP", , "Can return true to stop normal action"] + * @values [GAMEOBJECT_EVENT_ON_DIALOG_STATUS, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_DESTROYED, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_DAMAGED, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_ADD, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_REMOVE, "MAP", , ""] + * @values [GAMEOBJECT_EVENT_ON_USE, "MAP", , "Can return true to stop normal action"] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : [GameObject] entry Id + * @param uint32 event : [GameObject] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterGameObjectEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_GAMEOBJECT); + } + + /** + * Registers a [Spell] event handler. + * + * @hook spell + * @table + * @columns [Event, State, Parameters, Comment] + * @values [SPELL_EVENT_ON_CAST, "MAP", , ""] + * @values [SPELL_EVENT_ON_AURA_APPLICATION, "MAP", , "Can return true to prevent application"] + * @values [SPELL_EVENT_ON_DISPEL, "MAP", , ""] + * @values [SPELL_EVENT_ON_PERIODIC_TICK, "MAP", , "Can return true to prevent tick"] + * @values [SPELL_EVENT_ON_PERIODIC_UPDATE, "MAP", , ""] + * @values [SPELL_EVENT_ON_AURA_CALC_AMOUNT, "MAP", , "Can return amount, canBeRecalculated to override"] + * @values [SPELL_EVENT_ON_CALC_PERIODIC, "MAP", , "Can return isPeriodic, amplitude to override"] + * @values [SPELL_EVENT_ON_CHECK_PROC, "MAP", , "Can return false to prevent proc"] + * @values [SPELL_EVENT_ON_PROC, "MAP", , "Can return true to prevent default proc handling"] + * @values [SPELL_EVENT_ON_CHECK_CAST, "MAP", , "Can return SpellCastResult to override cast result"] + * @values [SPELL_EVENT_ON_BEFORE_CAST, "MAP", , ""] + * @values [SPELL_EVENT_ON_AFTER_CAST, "MAP", , ""] + * @values [SPELL_EVENT_ON_OBJECT_AREA_TARGET, "MAP", , "Modify targets table in place to change targets"] + * @values [SPELL_EVENT_ON_OBJECT_TARGET, "MAP", , ""] + * @values [SPELL_EVENT_ON_DEST_TARGET, "MAP", , "Can return mapId, x, y, z, orientation to override destination"] + * @values [SPELL_EVENT_ON_EFFECT_LAUNCH, "MAP", , "Can return true to prevent default launch handling"] + * @values [SPELL_EVENT_ON_EFFECT_LAUNCH_TARGET, "MAP", , "Can return true to prevent default launch target handling"] + * @values [SPELL_EVENT_ON_EFFECT_CALC_ABSORB, "MAP", , "Can return resistAmount, absorbAmount to override"] + * @values [SPELL_EVENT_ON_EFFECT_HIT, "MAP", , "Can return true to prevent default hit handling"] + * @values [SPELL_EVENT_ON_BEFORE_HIT, "MAP", , ""] + * @values [SPELL_EVENT_ON_EFFECT_HIT_TARGET, "MAP", , "Can return true to prevent default hit target handling"] + * @values [SPELL_EVENT_ON_HIT, "MAP", , ""] + * @values [SPELL_EVENT_ON_AFTER_HIT, "MAP", , ""] + * + * @proto cancel = (entry, event, function) + * @proto cancel = (entry, event, function, shots) + * + * @param uint32 entry : [Spell] entry Id + * @param uint32 event : [Spell] event Id, refer to table above + * @param function function : function to register + * @param uint32 shots = 0 : the number of times the function will be called, 0 means "always call this function" + * + * @return function cancel : a function that cancels the binding when called + */ + int RegisterSpellEvent(Eluna* E) + { + return RegisterEntryHelper(E, Hooks::REGTYPE_SPELL); + } + + /** + * Reloads the Lua engine. + */ + int ReloadEluna(Eluna* E) + { + E->ReloadEluna(); + return 0; + } + + /** + * Runs a command. + * + * @param string command : the command to run + */ + #if 0 + int RunCommand(Eluna* E) + { + const char* command = E->CHECKVAL(1); + // ignores output of the command + eWorld->QueueCliCommand(new CliCommandHolder(nullptr, command, [](void*, std::string_view) {}, [](void*, bool) {})); + return 0; + } + #endif + /** + * Sends a message to all [Player]s online. + * + * @param string message : message to send + */ + int SendWorldMessage(Eluna* E) + { + const char* message = E->CHECKVAL(1); + eWorld->SendServerMessage(SERVER_MSG_STRING, message); + return 0; + } + + /** + * Executes a SQL query on the world database and returns an [ElunaQuery]. + * + * The query is always executed synchronously + * (i.e. execution halts until the query has finished and then results are returned). + * + * local Q = WorldDBQuery("SELECT entry, name FROM creature_template LIMIT 10") + * if Q then + * repeat + * local entry, name = Q:GetUInt32(0), Q:GetString(1) + * print(entry, name) + * until not Q:NextRow() + * end + * + * @warning This method is flagged as **unsafe** and is **disabled by default**. Use with caution, or transition to Async queries. + * + * @param string sql : query to execute + * @return [ElunaQuery] results or nil if no rows found or nil if no rows found + */ + int WorldDBQuery(Eluna* E) + { + const char* query = E->CHECKVAL(1); + + ElunaQuery result = WorldDatabase.Query(query); + if (result) + E->Push(&result); + else + E->Push(); + + return 1; + } + + /** + * Executes a SQL query on the world database. + * + * The query may be executed *asynchronously* (at a later, unpredictable time). + * If you need to execute the query synchronously, use [Global:WorldDBQuery] instead. + * + * Any results produced are ignored. + * If you need results from the query, use [Global:WorldDBQuery] instead. + * + * WorldDBExecute("DELETE FROM my_table") + * + * @param string sql : query to execute + */ + int WorldDBExecute(Eluna* E) + { + const char* query = E->CHECKVAL(1); + WorldDatabase.Execute(query); + return 0; + } + + + /** + * Initiates an asynchronous SQL query on the world database with a callback function. + * + * The query is executed asynchronously, and the provided Lua function is called when the query completes. + * The callback function parameter is the query result (an [ElunaQuery] or nil if no rows found). + * + * WorldDBQueryAsync("SELECT entry, name FROM creature_template LIMIT 10", function(results) + * if results then + * repeat + * local entry, name = results:GetUInt32(0), results:GetString(1) + * print(entry, name) + * until not results:NextRow() + * end + * end) + * + * @param string sql : query to execute asynchronously + * @param function callback : the callback function to be called with the query results + */ + int WorldDBQueryAsync(Eluna* E) + { + const char* query = E->CHECKVAL(1); + luaL_checktype(E->L, 2, LUA_TFUNCTION); + + // Push the Lua function onto the stack and create a reference + lua_pushvalue(E->L, 2); + int funcRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + + // Validate the function reference + if (funcRef == LUA_REFNIL || funcRef == LUA_NOREF) + { + luaL_argerror(E->L, 2, "unable to make a ref to function"); + return 0; + } + + // Add an asynchronous query callback + E->GetQueryProcessor().AddCallback(WorldDatabase.AsyncQuery(query).WithCallback([E, funcRef](QueryResult result) + { + ElunaQuery* eq = result ? &result : nullptr; + + // Get the Lua function from the registry + lua_rawgeti(E->L, LUA_REGISTRYINDEX, funcRef); + + // Push the query results as a parameter + E->Push(eq); + + // Call the Lua function + E->ExecuteCall(1, 0); + + // Unreference the Lua function + luaL_unref(E->L, LUA_REGISTRYINDEX, funcRef); + })); + return 0; + } + + /** + * Executes a SQL query on the character database and returns an [ElunaQuery]. + * + * The query is always executed synchronously + * (i.e. execution halts until the query has finished and then results are returned). + * + * For an example see [Global:WorldDBQuery]. + * + * @warning This method is flagged as **unsafe** and is **disabled by default**. Use with caution, or transition to Async queries. + * + * @param string sql : query to execute + * @return [ElunaQuery] results or nil if no rows found + */ + int CharDBQuery(Eluna* E) + { + const char* query = E->CHECKVAL(1); + + ElunaQuery result = CharacterDatabase.Query(query); + if (result) + E->Push(&result); + else + E->Push(); + + return 1; + } + + /** + * Executes a SQL query on the character database. + * + * The query may be executed *asynchronously* (at a later, unpredictable time). + * If you need to execute the query synchronously, use [Global:CharDBQuery] instead. + * + * Any results produced are ignored. + * If you need results from the query, use [Global:CharDBQuery] instead. + * + * CharDBExecute("DELETE FROM my_table") + * + * @param string sql : query to execute + */ + int CharDBExecute(Eluna* E) + { + const char* query = E->CHECKVAL(1); + CharacterDatabase.Execute(query); + return 0; + } + + /** + * Initiates an asynchronous SQL query on the character database with a callback function. + * + * The query is executed asynchronously, and the provided Lua function is called when the query completes. + * The callback function parameter is the query result (an [ElunaQuery] or nil if no rows found). + * + * For an example see [Global:WorldDBQueryAsync]. + * + * @param string sql : query to execute asynchronously + * @param function callback : the callback function to be called with the query results + */ + int CharDBQueryAsync(Eluna* E) + { + const char* query = E->CHECKVAL(1); + luaL_checktype(E->L, 2, LUA_TFUNCTION); + + // Push the Lua function onto the stack and create a reference + lua_pushvalue(E->L, 2); + int funcRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + + // Validate the function reference + if (funcRef == LUA_REFNIL || funcRef == LUA_NOREF) + { + luaL_argerror(E->L, 2, "unable to make a ref to function"); + return 0; + } + + // Add an asynchronous query callback + E->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(query).WithCallback([E, funcRef](QueryResult result) + { + ElunaQuery* eq = result ? &result : nullptr; + + // Get the Lua function from the registry + lua_rawgeti(E->L, LUA_REGISTRYINDEX, funcRef); + + // Push the query results as a parameter + E->Push(eq); + + // Call the Lua function + E->ExecuteCall(1, 0); + + // Unreference the Lua function + luaL_unref(E->L, LUA_REGISTRYINDEX, funcRef); + })); + return 0; + } + + /** + * Executes a SQL query on the login database and returns an [ElunaQuery]. + * + * The query is always executed synchronously + * (i.e. execution halts until the query has finished and then results are returned). + * + * For an example see [Global:WorldDBQuery]. + * + * @warning This method is flagged as **unsafe** and is **disabled by default**. Use with caution, or transition to Async queries. + * + * @param string sql : query to execute + * @return [ElunaQuery] results or nil if no rows found + */ + int AuthDBQuery(Eluna* E) + { + const char* query = E->CHECKVAL(1); + + ElunaQuery result = LoginDatabase.Query(query); + if (result) + E->Push(&result); + else + E->Push(); + + return 1; + } + + /** + * Executes a SQL query on the login database. + * + * The query may be executed *asynchronously* (at a later, unpredictable time). + * If you need to execute the query synchronously, use [Global:AuthDBQuery] instead. + * + * Any results produced are ignored. + * If you need results from the query, use [Global:AuthDBQuery] instead. + * + * AuthDBExecute("DELETE FROM my_table") + * + * @param string sql : query to execute + */ + int AuthDBExecute(Eluna* E) + { + const char* query = E->CHECKVAL(1); + LoginDatabase.Execute(query); + return 0; + } + + /** + * Initiates an asynchronous SQL query on the login database with a callback function. + * + * The query is executed asynchronously, and the provided Lua function is called when the query completes. + * The callback function parameter is the query result (an [ElunaQuery] or nil if no rows found). + * + * For an example see [Global:WorldDBQueryAsync]. + * + * @param string sql : query to execute asynchronously + * @param function callback : the callback function to be called with the query results + */ + int AuthDBQueryAsync(Eluna* E) + { + const char* query = E->CHECKVAL(1); + luaL_checktype(E->L, 2, LUA_TFUNCTION); + + // Push the Lua function onto the stack and create a reference + lua_pushvalue(E->L, 2); + int funcRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + + // Validate the function reference + if (funcRef == LUA_REFNIL || funcRef == LUA_NOREF) + { + luaL_argerror(E->L, 2, "unable to make a ref to function"); + return 0; + } + + // Add an asynchronous query callback + E->GetQueryProcessor().AddCallback(LoginDatabase.AsyncQuery(query).WithCallback([E, funcRef](QueryResult result) + { + ElunaQuery* eq = result ? &result : nullptr; + + // Get the Lua function from the registry + lua_rawgeti(E->L, LUA_REGISTRYINDEX, funcRef); + + // Push the query results as a parameter + E->Push(eq); + + // Call the Lua function + E->ExecuteCall(1, 0); + + // Unreference the Lua function + luaL_unref(E->L, LUA_REGISTRYINDEX, funcRef); + })); + return 0; + } + + /** + * Registers a global timed event. + * + * When the passed function is called, the parameters `(eventId, delay, repeats)` are passed to it. + * + * Repeats will decrease on each call if the event does not repeat indefinitely + * + * @proto eventId = (function, delay) + * @proto eventId = (function, delaytable) + * @proto eventId = (function, delay, repeats) + * @proto eventId = (function, delaytable, repeats) + * + * @param function function : function to trigger when the time has passed + * @param uint32 delay : set time in milliseconds for the event to trigger + * @param table delaytable : a table `{min, max}` containing the minimum and maximum delay time + * @param uint32 repeats = 1 : how many times for the event to repeat, 0 is infinite + * @return int eventId : unique ID for the timed event used to cancel it or nil + */ + int CreateLuaEvent(Eluna* E) + { + luaL_checktype(E->L, 1, LUA_TFUNCTION); + uint32 min, max; + if (lua_istable(E->L, 2)) + { + E->Push(1); + lua_gettable(E->L, 2); + min = E->CHECKVAL(-1); + E->Push(2); + lua_gettable(E->L, 2); + max = E->CHECKVAL(-1); + lua_pop(E->L, 2); + } + else + min = max = E->CHECKVAL(2); + uint32 repeats = E->CHECKVAL(3, 1); + + if (min > max) + return luaL_argerror(E->L, 2, "min is bigger than max delay"); + + lua_pushvalue(E->L, 1); + int functionRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + if (functionRef != LUA_REFNIL && functionRef != LUA_NOREF) + { + E->eventMgr->GetGlobalProcessor(GLOBAL_EVENTS)->AddEvent(functionRef, min, max, repeats); + E->Push(functionRef); + } + return 1; + } + + /** + * Removes a global timed event specified by ID. + * + * @param int eventId : event Id to remove + * @param bool all_Events = false : remove from all events, not just global + */ + int RemoveEventById(Eluna* E) + { + int eventId = E->CHECKVAL(1); + bool all_Events = E->CHECKVAL(2, false); + + // not thread safe + if (all_Events) + E->eventMgr->SetEventState(eventId, LUAEVENT_STATE_ABORT); + else + E->eventMgr->GetGlobalProcessor(GLOBAL_EVENTS)->SetState(eventId, LUAEVENT_STATE_ABORT); + return 0; + } + + /** + * Removes all global timed events. + * + * @param bool all_Events = false : remove all events, not just global + */ + int RemoveEvents(Eluna* E) + { + bool all_Events = E->CHECKVAL(1, false); + + // not thread safe + if (all_Events) + E->eventMgr->SetAllEventStates(LUAEVENT_STATE_ABORT); + else + E->eventMgr->GetGlobalProcessor(GLOBAL_EVENTS)->SetStates(LUAEVENT_STATE_ABORT); + return 0; + } + + /** + * Performs an in-game spawn and returns the [Creature] or [GameObject] spawned. + * + * @param int32 spawnType : type of object to spawn, 1 = [Creature], 2 = [GameObject] + * @param uint32 entry : entry ID of the [Creature] or [GameObject] + * @param uint32 mapId : map ID to spawn the [Creature] or [GameObject] in + * @param uint32 instanceId : instance ID to put the [Creature] or [GameObject] in. Non instance is 0 + * @param float x : x coordinate of the [Creature] or [GameObject] + * @param float y : y coordinate of the [Creature] or [GameObject] + * @param float z : z coordinate of the [Creature] or [GameObject] + * @param float o : o facing/orientation of the [Creature] or [GameObject] + * @param bool save = false : optional to save the [Creature] or [GameObject] to the database + * @param uint32 durorresptime = 0 : despawn time of the [Creature] if it's not saved or respawn time of [GameObject] + * @param uint32 phase = 1 : phase to put the [Creature] or [GameObject] in + * @return [WorldObject] worldObject : returns [Creature] or [GameObject] + */ + +int PerformIngameSpawn(Eluna* E) + { + int spawntype = E->CHECKVAL(1); + uint32 entry = E->CHECKVAL(2); + uint32 mapID = E->CHECKVAL(3); + uint32 instanceID = E->CHECKVAL(4); + float x = E->CHECKVAL(5); + float y = E->CHECKVAL(6); + float z = E->CHECKVAL(7); + float o = E->CHECKVAL(8); + bool save = E->CHECKVAL(9, false); + uint32 durorresptime = E->CHECKVAL(10, 0); + + // 获取参数,忽略不需要的位面 + E->CHECKVAL(11, 0); + + Map* map = eMapMgr->FindMap(mapID, instanceID); + if (!map) + { + E->Push(); + return 1; + } + + Position pos = { x, y, z, o }; + + // [完美匹配 SaveToDB]: 构建动态数组 + std::vector spawnDifficulties; + spawnDifficulties.push_back(Difficulty(map->GetDifficultyID())); + + if (spawntype == 1) // spawn creature (召唤怪物) + { + if (save) + { + Creature* creature = new Creature(); + uint32 spawnId = sObjectMgr->GenerateCreatureSpawnId(); + + // 完美匹配 Creature::Create 的 9 个参数 + if (!creature->Create(spawnId, map, entry, x, y, z, o, nullptr, 0)) + { + delete creature; + E->Push(); + return 1; + } + + creature->SaveToDB(map->GetId(), spawnDifficulties); + uint32 db_guid = creature->GetSpawnId(); + + creature->CleanupsBeforeDelete(); + delete creature; + creature = new Creature(); + + if (!creature->LoadFromDB(db_guid, map)) + { + delete creature; + E->Push(); + return 1; + } + + map->AddToMap(creature); + E->Push(creature); + } + else + { + TempSummon* creature = map->SummonCreature(entry, pos, NULL, durorresptime); + if (!creature) + { + E->Push(); + return 1; + } + + if (durorresptime) + creature->SetTempSummonType(TEMPSUMMON_TIMED_OR_DEAD_DESPAWN); + else + creature->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN); + + E->Push(creature); + } + + return 1; + } + + if (spawntype == 2) // Spawn object (召唤游戏物体) + { + const GameObjectTemplate* objectInfo = eObjectMgr->GetGameObjectTemplate(entry); + if (!objectInfo) + { + E->Push(); + return 1; + } + + if (objectInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(objectInfo->displayId)) + { + E->Push(); + return 1; + } + + QuaternionData rot = QuaternionData::fromEulerAnglesZYX(o, 0.f, 0.f); + + // [终极破局点]:使用 public static 工厂方法直接创建!不需要 new GameObject()! + GameObject* object = GameObject::CreateGameObject(objectInfo->entry, map, pos, rot, 255, GO_STATE_READY, 0); + + if (!object) + { + E->Push(); + return 1; + } + + if (durorresptime) + object->SetRespawnTime(durorresptime); + + if (save) + { + object->SaveToDB(map->GetId(), spawnDifficulties); + uint32 spawnId = object->GetSpawnId(); + + delete object; + + object = new GameObject(); + if (!object->LoadFromDB(spawnId, map)) + { + delete object; + E->Push(); + return 1; + } + map->AddToMap(object); + } + else + { + map->AddToMap(object); + } + + E->Push(object); + return 1; + } + + E->Push(); + return 1; + } + /** + * Creates a [WorldPacket]. + * + * @param [Opcodes] opcode : the opcode of the packet + * @param uint32 size : the size of the packet + * @return [WorldPacket] packet + */ + #if 0 + int CreatePacket(Eluna* E) + { + uint32 opcode = E->CHECKVAL(1); + size_t size = E->CHECKVAL(2); + if (opcode >= NUM_MSG_TYPES) + return luaL_argerror(E->L, 1, "valid opcode expected"); + + E->Push(new WorldPacket((OpcodesList)opcode, size)); + return 1; + } + #endif + /** + * Adds an [Item] to a vendor and updates the world database. + * + * @param uint32 entry : [Creature] entry Id + * @param uint32 item : [Item] entry Id + * @param int32 maxcount : max [Item] stack count + * @param uint32 incrtime : combined with maxcount, incrtime tells how often (in seconds) the vendor list is refreshed and the limited [Item] copies are restocked + * @param uint32 extendedcost : unique cost of an [Item], such as conquest points for example + */ + #if 0 + int AddVendorItem(Eluna* E) + { + uint32 entry = E->CHECKVAL(1); + uint32 item = E->CHECKVAL(2); + int maxcount = E->CHECKVAL(3); + uint32 incrtime = E->CHECKVAL(4); + uint32 extendedcost = E->CHECKVAL(5); + + if (!eObjectMgr->IsVendorItemValid(entry, item, maxcount, incrtime, extendedcost)) + return 0; + + eObjectMgr->AddVendorItem(entry, item, maxcount, incrtime, extendedcost); + + return 0; + } + #endif + /** + * Removes an [Item] from a vendor and updates the database. + * + * @param uint32 entry : [Creature] entry Id + * @param uint32 item : [Item] entry Id + */ + #if 0 + int VendorRemoveItem(Eluna* E) + { + uint32 entry = E->CHECKVAL(1); + uint32 item = E->CHECKVAL(2); + if (!eObjectMgr->GetCreatureTemplate(entry)) + return luaL_argerror(E->L, 1, "valid CreatureEntry expected"); + + eObjectMgr->RemoveVendorItem(entry, item); + + return 0; + } + #endif + /** + * Removes all [Item]s from a vendor and updates the database. + * + * @param uint32 entry : [Creature] entry Id + */ + + #if 0 + int VendorRemoveAllItems(Eluna* E) + { + uint32 entry = E->CHECKVAL(1); + + VendorItemData const* items = eObjectMgr->GetNpcVendorItemList(entry); + if (!items || items->Empty()) + return 0; + + auto const itemlist = items->m_items; + for (auto itr = itemlist.begin(); itr != itemlist.end(); ++itr) + eObjectMgr->RemoveVendorItem(entry, itr->item); + + return 0; + } + #endif + /** + * Kicks a [Player] from the server. + * + * @param [Player] player : [Player] to kick + */ + #if 0 + int Kick(Eluna* E) + { + Player* player = E->CHECKOBJ(1); + + player->GetSession()->KickPlayer("GlobalMethods::Kick Kick the player"); + return 0; + } + #endif + /** + * Ban's a [Player]'s account, character or IP + * + * @table + * @columns [Mode, Value] + * @values [ACCOUNT, 0] + * @values [CHARACTER, 1] + * @values [IP, 2] + * + * @param [BanMode] banMode : method of ban, refer to table above + * @param string nameOrIP : If BanMode is 0 then accountname, if 1 then charactername if 2 then ip + * @param uint32 duration : duration (in seconds) of the ban + * @param string reason = "" : ban reason, this is optional + * @param string whoBanned = "" : the [Player]'s name that banned the account, character or IP, this is optional + * @return int result : status of the ban. 0 if success, 1 if syntax error, 2 if target not found, 3 if a longer ban already exists, nil if unknown result + */ + int Ban(Eluna* E) + { + int banMode = E->CHECKVAL(1); + std::string nameOrIP = E->CHECKVAL(2); + uint32 duration = E->CHECKVAL(3); + const char* reason = E->CHECKVAL(4, ""); + const char* whoBanned = E->CHECKVAL(5, ""); + + const int BAN_ACCOUNT = 0; + const int BAN_CHARACTER = 1; + const int BAN_IP = 2; + + BanMode mode = BanMode::BAN_ACCOUNT; + + switch (banMode) + { + case BAN_ACCOUNT: + if (!Utf8ToUpperOnlyLatin(nameOrIP)) + return luaL_argerror(E->L, 2, "invalid account name"); + mode = BanMode::BAN_ACCOUNT; + break; + case BAN_CHARACTER: + if (!normalizePlayerName(nameOrIP)) + return luaL_argerror(E->L, 2, "invalid character name"); + mode = BanMode::BAN_CHARACTER; + break; + case BAN_IP: + if (!IsIPAddress(nameOrIP.c_str())) + return luaL_argerror(E->L, 2, "invalid ip"); + mode = BanMode::BAN_IP; + break; + default: + return luaL_argerror(E->L, 1, "unknown banmode"); + } + + BanReturn result; + //result = eWorld->BanAccount(mode, nameOrIP, duration, reason, whoBanned); + result = BanReturn::BAN_NOTFOUND; // 强行返回找不到,让下游顺畅执行 + switch (result) + { + case BanReturn::BAN_SUCCESS: + E->Push(0); + break; + case BanReturn::BAN_SYNTAX_ERROR: + E->Push(1); + break; + case BanReturn::BAN_NOTFOUND: + E->Push(2); + break; + case BanReturn::BAN_EXISTS: + E->Push(3); + break; + } + return 1; + } + + /** + * Saves all [Player]s. + */ + int SaveAllPlayers(Eluna* /*E*/) + { + eObjectAccessor()SaveAllPlayers(); + return 0; + } + + /** + * Sends mail to a [Player]. + * + * There can be several item entry-amount pairs at the end of the function. + * There can be maximum of 12 different items. + * + * @table + * @columns [Stationery, ID, Comment] + * @values [TEST, 1, ""] + * @values [DEFAULT, 41, ""] + * @values [GM, 61, ""] + * @values [AUCTION, 62, ""] + * @values [VAL, 64, "Valentine"] + * @values [CHR, 65, "Christmas"] + * @values [ORP, 67, "Orphan"] + * + * @param string subject : title (subject) of the mail + * @param string text : contents of the mail + * @param uint32 receiverGUIDLow : low GUID of the receiver + * @param uint32 senderGUIDLow = 0 : low GUID of the sender + * @param [MailStationery] stationary = MAIL_STATIONERY_DEFAULT : type of mail that is being sent as, refer to table above + * @param uint32 delay = 0 : mail send delay in milliseconds + * @param uint32 money = 0 : money to send + * @param uint32 cod = 0 : cod money amount + * @param uint32 entry = 0 : entry of an [Item] to send with mail + * @param uint32 amount = 0 : amount of the [Item] to send with mail + * @return uint32 itemGUIDlow : low GUID of the item. Up to 12 values returned, returns nil if no further items are sent + */ + #if 0 + int SendMail(Eluna* E) + { + int i = 0; + std::string subject = E->CHECKVAL(++i); + std::string text = E->CHECKVAL(++i); + uint32 receiverGUIDLow = E->CHECKVAL(++i); + uint32 senderGUIDLow = E->CHECKVAL(++i, 0); + uint32 stationary = E->CHECKVAL(++i, MAIL_STATIONERY_DEFAULT); + uint32 delay = E->CHECKVAL(++i, 0); + uint32 money = E->CHECKVAL(++i, 0); + uint32 cod = E->CHECKVAL(++i, 0); + int argAmount = lua_gettop(E->L); + + MailSender sender(MAIL_NORMAL, senderGUIDLow, (MailStationery)stationary); + MailDraft draft(subject, text); + + if (cod) + draft.AddCOD(cod); + if (money) + draft.AddMoney(money); + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + uint8 addedItems = 0; + while (addedItems <= MAX_MAIL_ITEMS && i + 2 <= argAmount) + { + uint32 entry = E->CHECKVAL(++i); + uint32 amount = E->CHECKVAL(++i); + + ItemTemplate const* item_proto = eObjectMgr->GetItemTemplate(entry); + if (!item_proto) + { + luaL_error(E->L, "Item entry %d does not exist", entry); + continue; + } + + if (amount < 1 || (item_proto->MaxCount > 0 && amount > uint32(item_proto->MaxCount))) + { + luaL_error(E->L, "Item entry %d has invalid amount %d", entry, amount); + continue; + } + if (Item* item = Item::CreateItem(entry, amount)) + { + item->SaveToDB(trans); + draft.AddItem(item); + E->Push(item->GetGUID().GetCounter()); + ++addedItems; + } + } + + Player* receiverPlayer = eObjectAccessor()FindPlayerByLowGUID(receiverGUIDLow); + draft.SendMailTo(trans, MailReceiver(receiverPlayer, receiverGUIDLow), sender, MAIL_CHECK_MASK_NONE, delay); + CharacterDatabase.CommitTransaction(trans); + + return addedItems; + } + #endif + /** + * Performs a bitwise AND (a & b). + * + * @param uint32 a + * @param uint32 b + * @return uint32 result + */ + int bit_and(Eluna* E) + { + uint32 a = E->CHECKVAL(1); + uint32 b = E->CHECKVAL(2); + E->Push(a & b); + return 1; + } + + /** + * Performs a bitwise OR (a | b). + * + * @param uint32 a + * @param uint32 b + * @return uint32 result + */ + int bit_or(Eluna* E) + { + uint32 a = E->CHECKVAL(1); + uint32 b = E->CHECKVAL(2); + E->Push(a | b); + return 1; + } + + /** + * Performs a bitwise left-shift (a << b). + * + * @param uint32 a + * @param uint32 b + * @return uint32 result + */ + int bit_lshift(Eluna* E) + { + uint32 a = E->CHECKVAL(1); + uint32 b = E->CHECKVAL(2); + E->Push(a << b); + return 1; + } + + /** + * Performs a bitwise right-shift (a >> b). + * + * @param uint32 a + * @param uint32 b + * @return uint32 result + */ + int bit_rshift(Eluna* E) + { + uint32 a = E->CHECKVAL(1); + uint32 b = E->CHECKVAL(2); + E->Push(a >> b); + return 1; + } + + /** + * Performs a bitwise XOR (a ^ b). + * + * @param uint32 a + * @param uint32 b + * @return uint32 result + */ + int bit_xor(Eluna* E) + { + uint32 a = E->CHECKVAL(1); + uint32 b = E->CHECKVAL(2); + E->Push(a ^ b); + return 1; + } + + /** + * Performs a bitwise NOT (~a). + * + * @param uint32 a + * @return uint32 result + */ + int bit_not(Eluna* E) + { + uint32 a = E->CHECKVAL(1); + E->Push(~a); + return 1; + } + + /** + * Adds a taxi path to a specified map, returns the used pathId. + * + * Note that the first taxi point needs to be near the player when he starts the taxi path. + * The function should also be used only **once** per path added so use it on server startup for example. + * + * Related function: [Player:StartTaxi] + * + * -- Execute on startup + * local pathTable = {{mapid, x, y, z}, {mapid, x, y, z}} + * local path = AddTaxiPath(pathTable, 28135, 28135) + * + * -- Execute when the player should fly + * player:StartTaxi(path) + * + * @param table waypoints : table containing waypoints: {map, x, y, z[, actionFlag, delay]} + * @param uint32 mountA : alliance [Creature] entry + * @param uint32 mountH : horde [Creature] entry + * @param uint32 price = 0 : price of the taxi path + * @param uint32 pathId = 0 : path Id of the taxi path + * @return uint32 actualPathId + */ + #if 0 + int AddTaxiPath(Eluna* E) + { + luaL_checktype(E->L, 1, LUA_TTABLE); + uint32 mountA = E->CHECKVAL(2); + uint32 mountH = E->CHECKVAL(3); + uint32 price = E->CHECKVAL(4, 0); + uint32 pathId = E->CHECKVAL(5, 0); + lua_pushvalue(E->L, 1); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes} + + std::list nodes; + + int start = lua_gettop(E->L); + int end = start; + + E->Push(); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, nil + while (lua_next(E->L, -2) != 0) + { + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, key, value + luaL_checktype(E->L, -1, LUA_TTABLE); + E->Push(); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, key, value, nil + while (lua_next(E->L, -2) != 0) + { + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, key, value, key2, value2 + lua_insert(E->L, end++); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, value2, key, value, key2 + } + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, value2, key, value + if (start == end) + continue; + if (end - start < 4) // no mandatory args, dont add + return luaL_argerror(E->L, 1, "all waypoints do not have mandatory arguments"); + + while (end - start < 8) // fill optional args with 0 + { + E->Push(0); + lua_insert(E->L, end++); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, node, key, value + } + TaxiPathNodeEntry entry; + + // mandatory + entry.ContinentID = E->CHECKVAL(start); + entry.Loc.X = E->CHECKVAL(start + 1); + entry.Loc.Y = E->CHECKVAL(start + 2); + entry.Loc.Z = E->CHECKVAL(start + 3); + // optional + entry.Flags = E->CHECKVAL(start + 4, 0); + entry.Delay = E->CHECKVAL(start + 5, 0); + + nodes.push_back(entry); + + while (end != start) // remove args + if (!lua_isnone(E->L, --end)) + lua_remove(E->L, end); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, key, value + + lua_pop(E->L, 1); + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes}, key + } + // Stack: {nodes}, mountA, mountH, price, pathid, {nodes} + lua_pop(E->L, 1); + // Stack: {nodes}, mountA, mountH, price, pathid + + if (nodes.size() < 2) + return 1; + if (!pathId) + pathId = sTaxiPathNodesByPath.size(); + if (sTaxiPathNodesByPath.size() <= pathId) + sTaxiPathNodesByPath.resize(pathId + 1); + sTaxiPathNodesByPath[pathId].clear(); + sTaxiPathNodesByPath[pathId].resize(nodes.size()); + static uint32 nodeId = 500; + uint32 startNode = nodeId; + uint32 index = 0; + for (std::list::iterator it = nodes.begin(); it != nodes.end(); ++it) + { + TaxiPathNodeEntry& entry = *it; + TaxiNodesEntry* nodeEntry = new TaxiNodesEntry(); + + entry.PathID = pathId; + entry.NodeIndex = nodeId; + nodeEntry->ID = index; + nodeEntry->ContinentID = entry.ContinentID; + nodeEntry->Pos.X = entry.Loc.X; + nodeEntry->Pos.Y = entry.Loc.Y; + nodeEntry->Pos.Z = entry.Loc.Z; + nodeEntry->MountCreatureID[0] = mountH; + nodeEntry->MountCreatureID[1] = mountA; + sTaxiNodesStore.SetEntry(nodeId++, nodeEntry); + sTaxiPathNodesByPath[pathId][index++] = new TaxiPathNodeEntry(entry); + } + if (startNode >= nodeId) + return 1; + sTaxiPathSetBySource[startNode][nodeId - 1] = TaxiPathBySourceAndDestination(pathId, price); + TaxiPathEntry* pathEntry = new TaxiPathEntry(); + + pathEntry->FromTaxiNode = startNode; + pathEntry->ToTaxiNode = nodeId - 1; + pathEntry->Cost = price; + pathEntry->ID = pathId; + + sTaxiPathStore.SetEntry(pathId, pathEntry); + + E->Push(pathId); + return 1; + } + #endif + /** + * Returns `true` if Eluna is in compatibility mode, `false` if in multistate. + * + * @return bool isCompatibilityMode + */ + int IsCompatibilityMode(Eluna* E) + { + E->Push(false); + return 1; + } + + /** + * Returns `true` if the bag and slot is a valid inventory position, otherwise `false`. + * + * Some commonly used combinations: + * + * *Bag 255 (common character inventory)* + * + * - Slots 0-18: equipment + * - Slots 19-22: bag slots + * - Slots 23-38: backpack + * - Slots 39-66: bank main slots + * - Slots 67-74: bank bag slots + * - Slots 86-117: keyring + * + * *Bags 19-22 (equipped bags)* + * + * - Slots 0-35 + * + * *Bags 67-74 (bank bags)* + * + * - Slots 0-35 + * + * @param uint8 bag : the bag the [Item] is in, you can get this with [Item:GetBagSlot] + * @param uint8 slot : the slot the [Item] is in within the bag, you can get this with [Item:GetSlot] + * @return bool isInventoryPos + */ + int IsInventoryPos(Eluna* E) + { + uint8 bag = E->CHECKVAL(1); + uint8 slot = E->CHECKVAL(2); + + E->Push(Player::IsInventoryPos(bag, slot)); + return 1; + } + + /** + * Returns `true` if the bag and slot is a valid equipment position, otherwise `false`. + * + * See [Global:IsInventoryPos] for bag/slot combination examples. + * + * @param uint8 bag : the bag the [Item] is in, you can get this with [Item:GetBagSlot] + * @param uint8 slot : the slot the [Item] is in within the bag, you can get this with [Item:GetSlot] + * @return bool isEquipmentPosition + */ + int IsEquipmentPos(Eluna* E) + { + uint8 bag = E->CHECKVAL(1); + uint8 slot = E->CHECKVAL(2); + + E->Push(Player::IsEquipmentPos(bag, slot)); + return 1; + } + + /** + * Returns `true` if the bag and slot is a valid bank position, otherwise `false`. + * + * See [Global:IsInventoryPos] for bag/slot combination examples. + * + * @param uint8 bag : the bag the [Item] is in, you can get this with [Item:GetBagSlot] + * @param uint8 slot : the slot the [Item] is in within the bag, you can get this with [Item:GetSlot] + * @return bool isBankPosition + */ + int IsBankPos(Eluna* E) + { + uint8 bag = E->CHECKVAL(1); + uint8 slot = E->CHECKVAL(2); + + E->Push(Player::IsBankPos(bag, slot)); + return 1; + } + + /** + * Returns `true` if the bag and slot is a valid bag position, otherwise `false`. + * + * See [Global:IsInventoryPos] for bag/slot combination examples. + * + * @param uint8 bag : the bag the [Item] is in, you can get this with [Item:GetBagSlot] + * @param uint8 slot : the slot the [Item] is in within the bag, you can get this with [Item:GetSlot] + * @return bool isBagPosition + */ + int IsBagPos(Eluna* E) + { + uint8 bag = E->CHECKVAL(1); + uint8 slot = E->CHECKVAL(2); + + E->Push(Player::IsBagPos((bag << 8) + slot)); + return 1; + } + + /** + * Returns `true` if the event is currently active, otherwise `false`. + * + * @param uint16 eventId : the event id to check. + * @return bool isActive + */ + int IsGameEventActive(Eluna* E) + { + uint16 eventId = E->CHECKVAL(1); + + E->Push(eGameEventMgr->IsActiveEvent(eventId)); + return 1; + } + + /** + * Returns the server's current time. + * + * @return uint32 currTime : the current time, in milliseconds + */ + int GetCurrTime(Eluna* E) + { + E->Push(ElunaUtil::GetCurrTime()); + return 1; + } + + /** + * Returns the difference between an old timestamp and the current time. + * + * @param uint32 oldTime : an old timestamp, in milliseconds + * @return uint32 timeDiff : the difference, in milliseconds + */ + int GetTimeDiff(Eluna* E) + { + uint32 oldtimems = E->CHECKVAL(1); + + E->Push(ElunaUtil::GetTimeDiff(oldtimems)); + return 1; + } + + static std::string GetStackAsString(Eluna* E) + { + std::string output; + int top = lua_gettop(E->L); + for (int i = 1; i <= top; ++i) + { + if (lua_isstring(E->L, i)) + { + output += lua_tostring(E->L, i); + } + else + { + lua_getglobal(E->L, "tostring"); + lua_pushvalue(E->L, i); + lua_call(E->L, 1, 1); + output += lua_tostring(E->L, -1); + lua_pop(E->L, 1); + } + + if (i < top) + output += "\t"; + } + return output; + } + + /** + * Prints given parameters to the info log. + * + * @param ... + */ + int PrintInfo(Eluna* E) + { + ELUNA_LOG_INFO("%s", GetStackAsString(E).c_str()); + return 0; + } + + /** + * Prints given parameters to the error log. + * + * @param ... + */ + int PrintError(Eluna* E) + { + ELUNA_LOG_ERROR("%s", GetStackAsString(E).c_str()); + return 0; + } + + /** + * Prints given parameters to the debug log. + * + * @param ... + */ + int PrintDebug(Eluna* E) + { + ELUNA_LOG_DEBUG("%s", GetStackAsString(E).c_str()); + return 0; + } + + /** + * Starts the event by eventId, if force is set, the event will force start regardless of previous event state. + * + * @param uint16 eventId : the event id to start. + * @param bool force = false : set `true` to force start the event. + */ + int StartGameEvent(Eluna* E) + { + uint16 eventId = E->CHECKVAL(1); + bool force = E->CHECKVAL(2, false); + + eGameEventMgr->StartEvent(eventId, force); + return 0; + } + + /** + * Stops the event by eventId, if force is set, the event will force stop regardless of previous event state. + * + * @param uint16 eventId : the event id to stop. + * @param bool force = false : set `true` to force stop the event. + */ + int StopGameEvent(Eluna* E) + { + uint16 eventId = E->CHECKVAL(1); + bool force = E->CHECKVAL(2, false); + + eGameEventMgr->StopEvent(eventId, force); + return 0; + } + + /** + * Returns an object representing a `long long` (64-bit) value. + * + * The value by default is 0, but can be initialized to a value by passing a number or long long as a string. + * + * @proto value = () + * @proto value = (n) + * @proto value = (n_ll) + * @proto value = (n_str) + * @param int32 n + * @param int64 n_ll + * @param string n_str + * @return int64 value + */ + int CreateLongLong(Eluna* E) + { + long long init = 0; + if (lua_isstring(E->L, 1)) + { + std::string str = E->CHECKVAL(1); + std::istringstream iss(str); + iss >> init; + if (iss.bad()) + return luaL_argerror(E->L, 1, "long long (as string) could not be converted"); + } + else if (!lua_isnoneornil(E->L, 1)) + init = E->CHECKVAL(1); + + E->Push(init); + return 1; + } + + /** + * Returns an object representing an `unsigned long long` (64-bit) value. + * + * The value by default is 0, but can be initialized to a value by passing a number or unsigned long long as a string. + * + * @proto value = () + * @proto value = (n) + * @proto value = (n_ull) + * @proto value = (n_str) + * @param uint32 n + * @param uint64 n_ull + * @param string n_str + * @return uint64 value + */ + int CreateULongLong(Eluna* E) + { + unsigned long long init = 0; + if (lua_isstring(E->L, 1)) + { + std::string str = E->CHECKVAL(1); + std::istringstream iss(str); + iss >> init; + if (iss.bad()) + return luaL_argerror(E->L, 1, "unsigned long long (as string) could not be converted"); + } + else if (!lua_isnoneornil(E->L, 1)) + init = E->CHECKVAL(1); + + E->Push(init); + return 1; + } + + /** + * Unbinds event handlers for either all [BattleGround] events, or one type of event. + * + * If `event_type` is `nil`, all [BattleGround] event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto () + * @proto (event_type) + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterBGEvent] + */ + int ClearBattleGroundEvents(Eluna* E) + { + typedef EventKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_BG); + + if (lua_isnoneornil(E->L, 1)) + { + binding->Clear(); + } + else + { + uint32 event_type = E->CHECKVAL(1); + binding->Clear(Key((Hooks::BGEvents)event_type)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [Creature]'s events, or one type of event. + * + * If `event_type` is `nil`, all the [Creature]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect all instances of the [Creature], not just one. + * To bind and unbind events to a single [Creature], see [Global:RegisterUniqueCreatureEvent] and [Global:ClearUniqueCreatureEvents]. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of one or more [Creature]s whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterCreatureEvent] + */ + int ClearCreatureEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_CREATURE); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::CREATURE_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::CreatureEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::CreatureEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [Creature]'s events, or one type of event. + * + * If `event_type` is `nil`, all the [Creature]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect only a single [Creature]. + * To bind and unbind events to all instances of a [Creature], see [Global:RegisterCreatureEvent] and [Global:ClearCreatureEvent]. + * + * @proto (entry) + * @proto (entry, event_type) + * @param ObjectGuid guid : the GUID of a single [Creature] whose handlers will be cleared + * @param uint32 instance_id : the instance ID of a single [Creature] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterCreatureEvent] + */ + int ClearUniqueCreatureEvents(Eluna* E) + { + typedef UniqueObjectKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_CREATURE_UNIQUE); + + if (lua_isnoneornil(E->L, 3)) + { + ObjectGuid guid = E->CHECKVAL(1); + uint32 instanceId = E->CHECKVAL(2); + + for (uint32 i = 1; i < Hooks::CREATURE_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::CreatureEvents)i, guid, instanceId)); + } + else + { + ObjectGuid guid = E->CHECKVAL(1); + uint32 instanceId = E->CHECKVAL(2); + uint32 event_type = E->CHECKVAL(3); + binding->Clear(Key((Hooks::CreatureEvents)event_type, guid, instanceId)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [Creature]'s gossip events, or one type of event. + * + * If `event_type` is `nil`, all the [Creature]'s gossip event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect all instances of the [Creature], not just one. + * To bind and unbind gossip events to a single [Creature], tell the Eluna developers to implement that. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of a [Creature] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterCreatureGossipEvent] + */ + int ClearCreatureGossipEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_CREATURE_GOSSIP); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::GOSSIP_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::GossipEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::GossipEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [GameObject]'s events, or one type of event. + * + * If `event_type` is `nil`, all the [GameObject]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect all instances of the [GameObject], not just one. + * To bind and unbind events to a single [GameObject], tell the Eluna developers to implement that. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of a [GameObject] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterGameObjectEvent] + */ + int ClearGameObjectEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_GAMEOBJECT); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::GAMEOBJECT_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::GameObjectEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::GameObjectEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [GameObject]'s gossip events, or one type of event. + * + * If `event_type` is `nil`, all the [GameObject]'s gossip event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect all instances of the [GameObject], not just one. + * To bind and unbind gossip events to a single [GameObject], tell the Eluna developers to implement that. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of a [GameObject] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterGameObjectGossipEvent] + */ + int ClearGameObjectGossipEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_GAMEOBJECT_GOSSIP); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::GOSSIP_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::GossipEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::GossipEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all [Group] events, or one type of [Group] event. + * + * If `event_type` is `nil`, all [Group] event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto () + * @proto (event_type) + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterGroupEvent] + */ + int ClearGroupEvents(Eluna* E) + { + typedef EventKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_GROUP); + + if (lua_isnoneornil(E->L, 1)) + { + binding->Clear(); + } + else + { + uint32 event_type = E->CHECKVAL(1); + binding->Clear(Key((Hooks::GroupEvents)event_type)); + } + return 0; + } + + /** + * Unbinds event handlers for either all [Guild] events, or one type of [Guild] event. + * + * If `event_type` is `nil`, all [Guild] event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto () + * @proto (event_type) + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterGuildEvent] + */ + int ClearGuildEvents(Eluna* E) + { + typedef EventKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_GUILD); + + if (lua_isnoneornil(E->L, 1)) + { + binding->Clear(); + } + else + { + uint32 event_type = E->CHECKVAL(1); + binding->Clear(Key((Hooks::GuildEvents)event_type)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of an [Item]'s events, or one type of event. + * + * If `event_type` is `nil`, all the [Item]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect all instances of the [Item], not just one. + * To bind and unbind events to a single [Item], tell the Eluna developers to implement that. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of an [Item] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterItemEvent] + */ + int ClearItemEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_ITEM); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::ITEM_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::ItemEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::ItemEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of an [Item]'s gossip events, or one type of event. + * + * If `event_type` is `nil`, all the [Item]'s gossip event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * **NOTE:** this will affect all instances of the [Item], not just one. + * To bind and unbind gossip events to a single [Item], tell the Eluna developers to implement that. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the ID of an [Item] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterItemGossipEvent] + */ + int ClearItemGossipEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_ITEM_GOSSIP); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::GOSSIP_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::GossipEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::GossipEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [WorldPacket] opcode's events, or one type of event. + * + * If `event_type` is `nil`, all the [WorldPacket] opcode's event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto (opcode) + * @proto (opcode, event_type) + * @param uint32 opcode : the type of [WorldPacket] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterPacketEvent] + */ + int ClearPacketEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_PACKET); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::PACKET_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::PacketEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::PacketEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all [Player] events, or one type of [Player] event. + * + * If `event_type` is `nil`, all [Player] event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto () + * @proto (event_type) + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterPlayerEvent] + */ + int ClearPlayerEvents(Eluna* E) + { + typedef EventKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_PLAYER); + + if (lua_isnoneornil(E->L, 1)) + { + binding->Clear(); + } + else + { + uint32 event_type = E->CHECKVAL(1); + binding->Clear(Key((Hooks::PlayerEvents)event_type)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a [Player]'s gossip events, or one type of event. + * + * If `event_type` is `nil`, all the [Player]'s gossip event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto (entry) + * @proto (entry, event_type) + * @param uint32 entry : the low GUID of a [Player] whose handlers will be cleared + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterPlayerGossipEvent] + */ + int ClearPlayerGossipEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_PLAYER_GOSSIP); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::GOSSIP_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::GossipEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::GossipEvents)event_type, entry)); + } + return 0; + } + + /** + * Unbinds event handlers for either all server events, or one type of event. + * + * If `event_type` is `nil`, all server event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto () + * @proto (event_type) + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterServerEvent] + */ + int ClearServerEvents(Eluna* E) + { + typedef EventKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_SERVER); + + if (lua_isnoneornil(E->L, 1)) + { + binding->Clear(); + } + else + { + uint32 event_type = E->CHECKVAL(1); + binding->Clear(Key((Hooks::ServerEvents)event_type)); + } + return 0; + } + + /** + * Unbinds event handlers for either all of a non-instanced [Map]'s events, or one type of event. + * + * If `event_type` is `nil`, all the non-instanced [Map]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto (map_id) + * @proto (map_id, event_type) + * @param uint32 map_id : the ID of a [Map] + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterPlayerGossipEvent] + */ + int ClearMapEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_MAP); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::INSTANCE_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::InstanceEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::InstanceEvents)event_type, entry)); + } + + return 0; + } + + /** + * Unbinds event handlers for either all of an instanced [Map]'s events, or one type of event. + * + * If `event_type` is `nil`, all the instanced [Map]'s event handlers are cleared. + * + * Otherwise, only event handlers for `event_type` are cleared. + * + * @proto (instance_id) + * @proto (instance_id, event_type) + * @param uint32 entry : the ID of an instance of a [Map] + * @param uint32 event_type : the event whose handlers will be cleared, see [Global:RegisterInstanceEvent] + */ + int ClearInstanceEvents(Eluna* E) + { + typedef EntryKey Key; + auto binding = E->GetBinding(Hooks::REGTYPE_INSTANCE); + + if (lua_isnoneornil(E->L, 2)) + { + uint32 entry = E->CHECKVAL(1); + + for (uint32 i = 1; i < Hooks::INSTANCE_EVENT_COUNT; ++i) + binding->Clear(Key((Hooks::InstanceEvents)i, entry)); + } + else + { + uint32 entry = E->CHECKVAL(1); + uint32 event_type = E->CHECKVAL(2); + binding->Clear(Key((Hooks::InstanceEvents)event_type, entry)); + } + + return 0; + } + + /** + * Returns the [SpellInfo] for the given spell ID, or nil if the spell does not exist. + * + * @param uint32 spellId : the spell ID to look up + * @return [SpellInfo] spellInfo + */ + int GetSpellInfo(Eluna* E) + { + uint32 spellId = E->CHECKVAL(1); + if (!sSpellMgr->GetSpellInfo(spellId)) + return luaL_argerror(E->L, 1, "invalid spell id"); + ElunaSpellInfo info(spellId); + E->Push(&info); + return 1; + } + + ElunaRegister<> GlobalMethods[] = + { + // Hooks + { "RegisterPacketEvent", &LuaGlobalFunctions::RegisterPacketEvent }, + { "RegisterServerEvent", &LuaGlobalFunctions::RegisterServerEvent }, + { "RegisterPlayerEvent", &LuaGlobalFunctions::RegisterPlayerEvent }, + { "RegisterGuildEvent", &LuaGlobalFunctions::RegisterGuildEvent }, + { "RegisterGroupEvent", &LuaGlobalFunctions::RegisterGroupEvent }, + { "RegisterCreatureEvent", &LuaGlobalFunctions::RegisterCreatureEvent }, + { "RegisterUniqueCreatureEvent", &LuaGlobalFunctions::RegisterUniqueCreatureEvent }, + { "RegisterCreatureGossipEvent", &LuaGlobalFunctions::RegisterCreatureGossipEvent }, + { "RegisterGameObjectEvent", &LuaGlobalFunctions::RegisterGameObjectEvent }, + { "RegisterGameObjectGossipEvent", &LuaGlobalFunctions::RegisterGameObjectGossipEvent }, + { "RegisterSpellEvent", &LuaGlobalFunctions::RegisterSpellEvent }, + { "RegisterItemEvent", &LuaGlobalFunctions::RegisterItemEvent }, + { "RegisterItemGossipEvent", &LuaGlobalFunctions::RegisterItemGossipEvent }, + { "RegisterPlayerGossipEvent", &LuaGlobalFunctions::RegisterPlayerGossipEvent }, + { "RegisterBGEvent", &LuaGlobalFunctions::RegisterBGEvent }, + { "RegisterMapEvent", &LuaGlobalFunctions::RegisterMapEvent }, + { "RegisterInstanceEvent", &LuaGlobalFunctions::RegisterInstanceEvent }, + + { "ClearBattleGroundEvents", &LuaGlobalFunctions::ClearBattleGroundEvents }, + { "ClearCreatureEvents", &LuaGlobalFunctions::ClearCreatureEvents }, + { "ClearUniqueCreatureEvents", &LuaGlobalFunctions::ClearUniqueCreatureEvents }, + { "ClearCreatureGossipEvents", &LuaGlobalFunctions::ClearCreatureGossipEvents }, + { "ClearGameObjectEvents", &LuaGlobalFunctions::ClearGameObjectEvents }, + { "ClearGameObjectGossipEvents", &LuaGlobalFunctions::ClearGameObjectGossipEvents }, + { "ClearGroupEvents", &LuaGlobalFunctions::ClearGroupEvents }, + { "ClearGuildEvents", &LuaGlobalFunctions::ClearGuildEvents }, + { "ClearItemEvents", &LuaGlobalFunctions::ClearItemEvents }, + { "ClearItemGossipEvents", &LuaGlobalFunctions::ClearItemGossipEvents }, + { "ClearPacketEvents", &LuaGlobalFunctions::ClearPacketEvents }, + { "ClearPlayerEvents", &LuaGlobalFunctions::ClearPlayerEvents }, + { "ClearPlayerGossipEvents", &LuaGlobalFunctions::ClearPlayerGossipEvents }, + { "ClearServerEvents", &LuaGlobalFunctions::ClearServerEvents }, + { "ClearMapEvents", &LuaGlobalFunctions::ClearMapEvents }, + { "ClearInstanceEvents", &LuaGlobalFunctions::ClearInstanceEvents }, + + // Getters + { "GetLuaEngine", &LuaGlobalFunctions::GetLuaEngine }, + { "GetCoreName", &LuaGlobalFunctions::GetCoreName }, + { "GetRealmID", &LuaGlobalFunctions::GetRealmID }, + { "GetCoreVersion", &LuaGlobalFunctions::GetCoreVersion }, + { "GetCoreExpansion", &LuaGlobalFunctions::GetCoreExpansion }, + { "GetStateMap", &LuaGlobalFunctions::GetStateMap, METHOD_REG_MAP }, // Map state method only in multistate + { "GetStateMapId", &LuaGlobalFunctions::GetStateMapId }, + { "GetStateInstanceId", &LuaGlobalFunctions::GetStateInstanceId }, + { "GetQuest", &LuaGlobalFunctions::GetQuest }, + { "GetPlayerByGUID", &LuaGlobalFunctions::GetPlayerByGUID, METHOD_REG_WORLD }, // World state method only in multistate + { "GetPlayerByName", &LuaGlobalFunctions::GetPlayerByName, METHOD_REG_WORLD }, // World state method only in multistate + + + + { "GetGuildByName", &LuaGlobalFunctions::GetGuildByName }, + { "GetGuildByLeaderGUID", &LuaGlobalFunctions::GetGuildByLeaderGUID }, + { "GetPlayerCount", &LuaGlobalFunctions::GetPlayerCount }, + + + + + + + + + { "bit_not", &LuaGlobalFunctions::bit_not }, + { "bit_xor", &LuaGlobalFunctions::bit_xor }, + { "bit_rshift", &LuaGlobalFunctions::bit_rshift }, + { "bit_lshift", &LuaGlobalFunctions::bit_lshift }, + { "bit_or", &LuaGlobalFunctions::bit_or }, + { "bit_and", &LuaGlobalFunctions::bit_and }, + + { "GetMapById", &LuaGlobalFunctions::GetMapById, METHOD_REG_WORLD }, // World state method only in multistate + { "GetCurrTime", &LuaGlobalFunctions::GetCurrTime }, + { "GetTimeDiff", &LuaGlobalFunctions::GetTimeDiff }, + { "PrintInfo", &LuaGlobalFunctions::PrintInfo }, + { "PrintError", &LuaGlobalFunctions::PrintError }, + { "PrintDebug", &LuaGlobalFunctions::PrintDebug }, + { "GetActiveGameEvents", &LuaGlobalFunctions::GetActiveGameEvents }, + { "GetSpellInfo", &LuaGlobalFunctions::GetSpellInfo }, + + // Boolean + { "IsCompatibilityMode", &LuaGlobalFunctions::IsCompatibilityMode }, + { "IsInventoryPos", &LuaGlobalFunctions::IsInventoryPos }, + { "IsEquipmentPos", &LuaGlobalFunctions::IsEquipmentPos }, + { "IsBankPos", &LuaGlobalFunctions::IsBankPos }, + { "IsBagPos", &LuaGlobalFunctions::IsBagPos }, + { "IsGameEventActive", &LuaGlobalFunctions::IsGameEventActive }, + + // Other + { "ReloadEluna", &LuaGlobalFunctions::ReloadEluna }, + + { "SendWorldMessage", &LuaGlobalFunctions::SendWorldMessage }, + { "WorldDBQuery", &LuaGlobalFunctions::WorldDBQuery, METHOD_REG_ALL, METHOD_FLAG_UNSAFE }, + { "WorldDBExecute", &LuaGlobalFunctions::WorldDBExecute }, + { "WorldDBQueryAsync", &LuaGlobalFunctions::WorldDBQueryAsync }, + { "CharDBQuery", &LuaGlobalFunctions::CharDBQuery, METHOD_REG_ALL, METHOD_FLAG_UNSAFE }, + { "CharDBExecute", &LuaGlobalFunctions::CharDBExecute }, + { "CharDBQueryAsync", &LuaGlobalFunctions::CharDBQueryAsync }, + { "AuthDBQuery", &LuaGlobalFunctions::AuthDBQuery, METHOD_REG_ALL, METHOD_FLAG_UNSAFE }, + { "AuthDBExecute", &LuaGlobalFunctions::AuthDBExecute }, + { "AuthDBQueryAsync", &LuaGlobalFunctions::AuthDBQueryAsync }, + { "CreateLuaEvent", &LuaGlobalFunctions::CreateLuaEvent }, + { "RemoveEventById", &LuaGlobalFunctions::RemoveEventById }, + { "RemoveEvents", &LuaGlobalFunctions::RemoveEvents }, + + + + + + { "Ban", &LuaGlobalFunctions::Ban }, + { "SaveAllPlayers", &LuaGlobalFunctions::SaveAllPlayers }, + + + { "CreateInt64", &LuaGlobalFunctions::CreateLongLong }, + { "CreateUint64", &LuaGlobalFunctions::CreateULongLong }, + { "StartGameEvent", &LuaGlobalFunctions::StartGameEvent }, + { "StopGameEvent", &LuaGlobalFunctions::StopGameEvent } + }; +} +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/GroupMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/GroupMethods.h new file mode 100644 index 0000000000..81750126d4 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/GroupMethods.h @@ -0,0 +1,484 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef GROUPMETHODS_H +#define GROUPMETHODS_H + +/*** + * Inherits all methods from: none + */ +namespace LuaGroup +{ + /** + * Returns 'true' if the [Player] is the [Group] leader + * + * @param ObjectGuid guid : guid of a possible leader + * @return bool isLeader + */ + int IsLeader(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + E->Push(group->IsLeader(guid)); + return 1; + } + + /** + * Returns 'true' if the [Group] is full + * + * @return bool isFull + */ + int IsFull(Eluna* E, Group* group) + { + E->Push(group->IsFull()); + return 1; + } + + /** + * Returns 'true' if the [Group] is a LFG group + * + * @return bool isLFGGroup + */ + int IsLFGGroup(Eluna* E, Group* group) + { + E->Push(group->isLFGGroup()); + return 1; + } + + /** + * Returns 'true' if the [Group] is a raid [Group] + * + * @return bool isRaid + */ + int IsRaidGroup(Eluna* E, Group* group) + { + E->Push(group->isRaidGroup()); + return 1; + } + + /** + * Returns 'true' if the [Group] is a battleground [Group] + * + * @return bool isBG + */ + int IsBGGroup(Eluna* E, Group* group) + { + E->Push(group->isBGGroup()); + return 1; + } + + /** + * Returns 'true' if the [Player] is a member of this [Group] + * + * @param ObjectGuid guid : guid of a player + * @return bool isMember + */ + int IsMember(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + E->Push(group->IsMember(guid)); + return 1; + } + + /** + * Returns 'true' if the [Player] is an assistant of this [Group] + * + * @param ObjectGuid guid : guid of a player + * @return bool isAssistant + */ + int IsAssistant(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + E->Push(group->IsAssistant(guid)); + return 1; + } + + /** + * Returns 'true' if the [Player]s are in the same subgroup in this [Group] + * + * @param [Player] player1 : first [Player] to check + * @param [Player] player2 : second [Player] to check + * @return bool sameSubGroup + */ + int SameSubGroup(Eluna* E, Group* group) + { + Player* player1 = E->CHECKOBJ(2); + Player* player2 = E->CHECKOBJ(3); + E->Push(group->SameSubGroup(player1, player2)); + return 1; + } + + /** + * Returns 'true' if the subgroup has free slots in this [Group] + * + * @param uint8 subGroup : subGroup ID to check + * @return bool hasFreeSlot + */ + int HasFreeSlotSubGroup(Eluna* E, Group* group) + { + uint8 subGroup = E->CHECKVAL(2); + + if (subGroup >= MAX_RAID_SUBGROUPS) + { + luaL_argerror(E->L, 2, "valid subGroup ID expected"); + return 0; + } + + E->Push(group->HasFreeSlotSubGroup(subGroup)); + return 1; + } + + /** + * Adds a new member to the [Group] + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] player : [Player] to add to the group + * @return bool added : true if member was added + */ + int AddMember(Eluna* E, Group* group) + { + Player* player = E->CHECKOBJ(2); + + if (player->GetGroup() || !group->IsCreated() || group->IsFull()) + { + E->Push(false); + return 1; + } + + if (player->GetGroupInvite()) + player->UninviteFromGroup(); + + bool success = group->AddMember(player); + if (success) + group->BroadcastGroupUpdate(); + + E->Push(success); + return 1; + } + + /** + * Returns true if the [Group] is a battlefield group, false otherwise + * + * @return bool isBFGroup + */ + int IsBFGroup(Eluna* E, Group* group) + { + E->Push(group->isBFGroup()); + return 1; + } + + /** + * Returns a table with the [Player]s in this [Group] + * + * In multistate, this method is only available in the WORLD state + * + * @return table groupPlayers : table of [Player]s + */ + int GetMembers(Eluna* E, Group* group) + { + lua_newtable(E->L); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->GetSession()) + continue; + + E->Push(member); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); // push table to top of stack + return 1; + } + + /** + * Returns [Group] leader GUID + * + * @return ObjectGuid leaderGUID + */ + int GetLeaderGUID(Eluna* E, Group* group) + { + E->Push(group->GetLeaderGUID()); + return 1; + } + + /** + * Returns the [Group]'s GUID + * + * @return ObjectGuid groupGUID + */ + int GetGUID(Eluna* E, Group* group) + { + E->Push(group->GET_GUID()); + return 1; + } + + /** + * Returns a [Group] member's GUID by their name + * + * @param string name : the [Player]'s name + * @return ObjectGuid memberGUID + */ + int GetMemberGUID(Eluna* E, Group* group) + { + const char* name = E->CHECKVAL(2); + E->Push(group->GetMemberGUID(name)); + return 1; + } + + /** + * Returns the member count of this [Group] + * + * @return uint32 memberCount + */ + int GetMembersCount(Eluna* E, Group* group) + { + E->Push(group->GetMembersCount()); + return 1; + } + + /** + * Returns the [Player]'s subgroup ID of this [Group] + * + * @param ObjectGuid guid : guid of the player + * @return uint8 subGroupID : a valid subgroup ID or MAX_RAID_SUBGROUPS+1 + */ + int GetMemberGroup(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + E->Push(group->GetMemberGroup(guid)); + return 1; + } + + /** + * Returns the [Group] members' flags + * + * @table + * @columns [GroupMemberFlags, ID] + * @values [MEMBER_FLAG_ASSISTANT, 1] + * @values [MEMBER_FLAG_MAINTANK, 2] + * @values [MEMBER_FLAG_MAINASSIST, 4] + * + * @param ObjectGuid guid : guid of the player + * @return uint8 flags + */ + #if 0 + int GetMemberFlags(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + E->Push(group->GetMemberFlags(guid)); + return 1; + } + #endif + /** + * Sets the leader of this [Group] + * + * In multistate, this method is only available in the WORLD state + * + * @param ObjectGuid guid : guid of the new leader + */ + int SetLeader(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + group->ChangeLeader(guid); + group->SendUpdate(); + return 0; + } + + /** + * Sends a specified [WorldPacket] to this [Group] + * + * @param [WorldPacket] packet : the [WorldPacket] to send + * @param bool ignorePlayersInBg : ignores [Player]s in a battleground + * @param ObjectGuid ignore : ignore a [Player] by their GUID + */ + int SendPacket(Eluna* E, Group* group) + { + WorldPacket* data = E->CHECKOBJ(2); + bool ignorePlayersInBg = E->CHECKVAL(3); + ObjectGuid ignore = E->CHECKVAL(4); + + group->BroadcastPacket(data, ignorePlayersInBg, -1, ignore); + return 0; + } + + /** + * Removes a [Player] from this [Group] and returns 'true' if successful + * + * In multistate, this method is only available in the WORLD state + * + * @table + * @columns [RemoveMethod, ID] + * @values [GROUP_REMOVEMETHOD_DEFAULT, 0] + * @values [GROUP_REMOVEMETHOD_KICK, 1] + * @values [GROUP_REMOVEMETHOD_LEAVE, 2] + * @values [GROUP_REMOVEMETHOD_KICK_LFG, 3] + * + * @param ObjectGuid guid : guid of the player to remove + * @param [RemoveMethod] method : method used to remove the player + * @return bool removed + */ + int RemoveMember(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + uint32 method = E->CHECKVAL(3, 0); + + E->Push(group->RemoveMember(guid, (RemoveMethod)method)); + return 1; + } + + /** + * Disbands this [Group] + * + * In multistate, this method is only available in the WORLD state + * + */ + int Disband(Eluna* /*E*/, Group* group) + { + group->Disband(); + return 0; + } + + /** + * Converts this [Group] to a raid [Group] + * + * In multistate, this method is only available in the WORLD state + * + */ + int ConvertToRaid(Eluna* /*E*/, Group* group) + { + group->ConvertToRaid(); + return 0; + } + + /** + * Sets the member's subGroup + * + * In multistate, this method is only available in the WORLD state + * + * @param ObjectGuid guid : guid of the player to move + * @param uint8 groupID : the subGroup's ID + */ + int SetMembersGroup(Eluna* E, Group* group) + { + ObjectGuid guid = E->CHECKVAL(2); + uint8 subGroup = E->CHECKVAL(3); + + if (subGroup >= MAX_RAID_SUBGROUPS) + { + luaL_argerror(E->L, 3, "valid subGroup ID expected"); + return 0; + } + + if (!group->HasFreeSlotSubGroup(subGroup)) + return 0; + + group->ChangeMembersGroup(guid, subGroup); + return 0; + } + + /** + * Sets the target icon of an object for the [Group] + * + * In multistate, this method is only available in the WORLD state + * + * @param uint8 icon : the icon (Skull, Square, etc) + * @param ObjectGuid target : GUID of the icon target, 0 is to clear the icon + * @param ObjectGuid setter : GUID of the icon setter + */ + #if 0 + int SetTargetIcon(Eluna* E, Group* group) + { + uint8 icon = E->CHECKVAL(2); + ObjectGuid target = E->CHECKVAL(3); + ObjectGuid setter = E->CHECKVAL(4, ObjectGuid()); + + if (icon >= TARGET_ICONS_COUNT) + return luaL_argerror(E->L, 2, "valid target icon expected"); + + group->SetTargetIcon(icon, setter, target); + return 0; + } + #endif + /** + * Converts the [Group] to a LFG group + * + * In multistate, this method is only available in the WORLD state + */ + int ConvertToLFG(Eluna* /*E*/, Group* group) + { + group->ConvertToLFG(); + return 0; + } + + /** + * Sets or removes a flag for a [Group] member + * + * In multistate, this method is only available in the WORLD state + * + * @table + * @columns [GroupMemberFlags, ID] + * @values [MEMBER_FLAG_ASSISTANT, 1] + * @values [MEMBER_FLAG_MAINTANK, 2] + * @values [MEMBER_FLAG_MAINASSIST, 4] + * + * @param ObjectGuid target : GUID of the target + * @param bool apply : add the `flag` if `true`, remove the `flag` otherwise + * @param [GroupMemberFlags] flag : the flag to set or unset + */ + int SetMemberFlag(Eluna* E, Group* group) + { + ObjectGuid target = E->CHECKVAL(2); + bool apply = E->CHECKVAL(3); + GroupMemberFlags flag = static_cast(E->CHECKVAL(4)); + + group->SetGroupMemberFlag(target, apply, flag); + return 0; + } + + ElunaRegister GroupMethods[] = + { + // Getters + { "GetMembers", &LuaGroup::GetMembers, METHOD_REG_WORLD }, // World state method only in multistate + { "GetLeaderGUID", &LuaGroup::GetLeaderGUID }, + { "GetGUID", &LuaGroup::GetGUID }, + { "GetMemberGroup", &LuaGroup::GetMemberGroup }, + { "GetMemberGUID", &LuaGroup::GetMemberGUID }, + { "GetMembersCount", &LuaGroup::GetMembersCount }, + + + // Setters + { "SetLeader", &LuaGroup::SetLeader, METHOD_REG_WORLD }, // World state method only in multistate + { "SetMembersGroup", &LuaGroup::SetMembersGroup, METHOD_REG_WORLD }, // World state method only in multistate + + { "SetMemberFlag", &LuaGroup::SetMemberFlag, METHOD_REG_WORLD }, // World state method only in multistate + + // Boolean + { "IsLeader", &LuaGroup::IsLeader }, + { "AddMember", &LuaGroup::AddMember, METHOD_REG_WORLD }, // World state method only in multistate + { "RemoveMember", &LuaGroup::RemoveMember, METHOD_REG_WORLD }, // World state method only in multistate + { "Disband", &LuaGroup::Disband, METHOD_REG_WORLD }, // World state method only in multistate + { "IsFull", &LuaGroup::IsFull }, + { "IsLFGGroup", &LuaGroup::IsLFGGroup }, + { "IsRaidGroup", &LuaGroup::IsRaidGroup }, + { "IsBGGroup", &LuaGroup::IsBGGroup }, + { "IsBFGroup", &LuaGroup::IsBFGroup }, + { "IsMember", &LuaGroup::IsMember }, + { "IsAssistant", &LuaGroup::IsAssistant }, + { "SameSubGroup", &LuaGroup::SameSubGroup }, + { "HasFreeSlotSubGroup", &LuaGroup::HasFreeSlotSubGroup }, + + // Other + { "SendPacket", &LuaGroup::SendPacket }, + { "ConvertToLFG", &LuaGroup::ConvertToLFG, METHOD_REG_WORLD }, // World state method only in multistate + { "ConvertToRaid", &LuaGroup::ConvertToRaid, METHOD_REG_WORLD } // World state method only in multistate + }; +}; + +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/GuildMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/GuildMethods.h new file mode 100644 index 0000000000..1b59270af9 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/GuildMethods.h @@ -0,0 +1,287 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef GUILDMETHODS_H +#define GUILDMETHODS_H + +/*** + * Inherits all methods from: none + */ +namespace LuaGuild +{ + /** + * Returns a table with the [Player]s in this [Guild] + * + * Only the players that are online and on some map. + * + * In multistate, this method is only available in the WORLD state + * + * @return table guildPlayers : table of [Player]s + */ + #if 0 + int GetMembers(Eluna* E, Guild* guild) + { + lua_newtable(E->L); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + std::shared_lock lock(*HashMapHolder::GetLock()); + const HashMapHolder::MapType& m = eObjectAccessor()GetPlayers(); + for (HashMapHolder::MapType::const_iterator it = m.begin(); it != m.end(); ++it) + { + if (Player* player = it->second) + { + if (player->IsInWorld() && player->GetGuildId() == guild->GetId()) + { + E->Push(player); + lua_rawseti(E->L, tbl, ++i); + } + } + } + + lua_settop(E->L, tbl); // push table to top of stack + return 1; + } + #endif + /** + * Returns the member count of this [Guild] + * + * @return uint32 memberCount + */ + int GetMemberCount(Eluna* E, Guild* guild) + { + E->Push(guild->GetMemberCount()); + return 1; + } + + /** + * Finds and returns the [Guild] leader by their GUID if logged in + * + * In multistate, this method is only available in the WORLD state + * + * @return [Player] leader + */ + int GetLeader(Eluna* E, Guild* guild) + { + E->Push(eObjectAccessor()FindPlayer(guild->GetLeaderGUID())); + return 1; + } + + /** + * Returns [Guild] leader GUID + * + * @return ObjectGuid leaderGUID + */ + int GetLeaderGUID(Eluna* E, Guild* guild) + { + E->Push(guild->GetLeaderGUID()); + return 1; + } + + /** + * Returns the [Guild]s entry ID + * + * @return uint32 entryId + */ + int GetId(Eluna* E, Guild* guild) + { + E->Push(guild->GetId()); + return 1; + } + + /** + * Returns the [Guild]s name + * + * @return string guildName + */ + int GetName(Eluna* E, Guild* guild) + { + E->Push(guild->GetName()); + return 1; + } + + /** + * Returns the [Guild]s current Message Of The Day + * + * @return string guildMOTD + */ + int GetMOTD(Eluna* E, Guild* guild) + { + E->Push(guild->GetMOTD()); + return 1; + } + + /** + * Returns the [Guild]s current info + * + * @return string guildInfo + */ + int GetInfo(Eluna* E, Guild* guild) + { + E->Push(guild->GetInfo()); + return 1; + } + + /** + * Sets the leader of this [Guild] + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] leader : the [Player] leader to change + */ + #if 0 + int SetLeader(Eluna* E, Guild* guild) + { + Player* player = E->CHECKOBJ(2); + + guild->HandleSetNewGuildMaster(player->GetSession(), player->GetName()); + return 0; + } + #endif + /** + * Sets the information of the bank tab specified + * + * In multistate, this method is only available in the WORLD state + * + * @param uint8 tabId : the ID of the tab specified + * @param string info : the information to be set to the bank tab + */ + int SetBankTabText(Eluna* E, Guild* guild) + { + uint8 tabId = E->CHECKVAL(2); + const char* text = E->CHECKVAL(3); + + guild->SetBankTabText(tabId, text); + return 0; + } + + // SendPacketToGuild(packet) + /** + * Sends a [WorldPacket] to all the [Player]s in the [Guild] + * + * @param [WorldPacket] packet : the [WorldPacket] to be sent to the [Player]s + */ + int SendPacket(Eluna* E, Guild* guild) + { + WorldPacket* data = E->CHECKOBJ(2); + + guild->BroadcastPacket(data); + return 0; + } + + // SendPacketToRankedInGuild(packet, rankId) + /** + * Sends a [WorldPacket] to all the [Player]s at the specified rank in the [Guild] + * + * @param [WorldPacket] packet : the [WorldPacket] to be sent to the [Player]s + * @param uint8 rankId : the rank ID + */ + int SendPacketToRanked(Eluna* E, Guild* guild) + { + WorldPacket* data = E->CHECKOBJ(2); + uint8 ranked = E->CHECKVAL(3); + + guild->BroadcastPacketToRank(data, ranked); + return 0; + } + + /** + * Disbands the [Guild] + * + * In multistate, this method is only available in the WORLD state + * + */ + int Disband(Eluna* /*E*/, Guild* guild) + { + guild->Disband(); + return 0; + } + + /** + * Adds the specified [Player] to the [Guild] at the specified rank. + * + * If no rank is specified, defaults to none. + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] player : the [Player] to be added to the guild + * @param uint8 rankId : the rank ID + */ + int AddMember(Eluna* E, Guild* guild) + { + Player* player = E->CHECKOBJ(2); + uint8 rankId = E->CHECKVAL(3, GUILD_RANK_NONE); + + CharacterDatabaseTransaction trans(nullptr); + + guild->AddMember(trans, player->GET_GUID(), rankId); + return 0; + } + + /** + * Removes the specified [Player] from the [Guild]. + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] player : the [Player] to be removed from the guild + * @param bool isDisbanding : default 'false', should only be set to 'true' if the guild is triggered to disband + */ + int DeleteMember(Eluna* E, Guild* guild) + { + Player* player = E->CHECKOBJ(2); + bool isDisbanding = E->CHECKVAL(3, false); + + CharacterDatabaseTransaction trans(nullptr); + + guild->DeleteMember(trans, player->GET_GUID(), isDisbanding); + return 0; + } + + /** + * Promotes/demotes the [Player] to the specified rank. + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] player : the [Player] to be promoted/demoted + * @param uint8 rankId : the rank ID + */ + int SetMemberRank(Eluna* E, Guild* guild) + { + Player* player = E->CHECKOBJ(2); + uint8 newRank = E->CHECKVAL(3); + + CharacterDatabaseTransaction trans(nullptr); + + guild->ChangeMemberRank(trans, player->GET_GUID(), newRank); + return 0; + } + + ElunaRegister GuildMethods[] = + { + // Getters + { "GetMembers", &LuaGuild::GetMembers, METHOD_REG_WORLD }, // World state method only in multistate + { "GetLeader", &LuaGuild::GetLeader, METHOD_REG_WORLD }, // World state method only in multistate + { "GetLeaderGUID", &LuaGuild::GetLeaderGUID }, + { "GetId", &LuaGuild::GetId }, + { "GetName", &LuaGuild::GetName }, + { "GetMOTD", &LuaGuild::GetMOTD }, + { "GetInfo", &LuaGuild::GetInfo }, + { "GetMemberCount", &LuaGuild::GetMemberCount }, + + // Setters + { "SetBankTabText", &LuaGuild::SetBankTabText, METHOD_REG_WORLD }, // World state method only in multistate + { "SetMemberRank", &LuaGuild::SetMemberRank, METHOD_REG_WORLD }, // World state method only in multistate + + + // Other + { "SendPacket", &LuaGuild::SendPacket }, + { "SendPacketToRanked", &LuaGuild::SendPacketToRanked }, + { "Disband", &LuaGuild::Disband, METHOD_REG_WORLD }, // World state method only in multistate + { "AddMember", &LuaGuild::AddMember, METHOD_REG_WORLD }, // World state method only in multistate + { "DeleteMember", &LuaGuild::DeleteMember, METHOD_REG_WORLD } // World state method only in multistate + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/ItemMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/ItemMethods.h new file mode 100644 index 0000000000..008b75dc56 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/ItemMethods.h @@ -0,0 +1,985 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef ITEMMETHODS_H +#define ITEMMETHODS_H + +/*** + * Inherits all methods from: [Object] + */ +namespace LuaItem +{ + /** + * Returns 'true' if the [Item] is soulbound, 'false' otherwise + * + * @return bool isSoulBound + */ + int IsSoulBound(Eluna* E, Item* item) + { + E->Push(item->IsSoulBound()); + return 1; + } + + /** + * Returns 'true' if the [Item] is account bound, 'false' otherwise + * + * @return bool isAccountBound + */ + int IsBoundAccountWide(Eluna* E, Item* item) + { + E->Push(item->IsBoundAccountWide()); + return 1; + } + + /** + * Returns 'true' if the [Item] is bound to a [Player] by an enchant, 'false' otehrwise + * + * @return bool isBoundByEnchant + */ + int IsBoundByEnchant(Eluna* E, Item* item) + { + E->Push(item->IsBoundByEnchant()); + return 1; + } + + /** + * Returns 'true' if the [Item] is not bound to the [Player] specified, 'false' otherwise + * + * @param [Player] player : the [Player] object to check the item against + * @return bool isNotBound + */ + int IsNotBoundToPlayer(Eluna* E, Item* item) + { + Player* player = E->CHECKOBJ(2); + + E->Push(item->IsBindedNotWith(player)); + return 1; + } + + /** + * Returns 'true' if the [Item] is locked, 'false' otherwise + * + * @return bool isLocked + */ + int IsLocked(Eluna* E, Item* item) + { + E->Push(item->IsLocked()); + return 1; + } + + /** + * Returns 'true' if the [Item] is a bag, 'false' otherwise + * + * @return bool isBag + */ + int IsBag(Eluna* E, Item* item) + { + E->Push(item->IsBag()); + return 1; + } + + /** + * Returns 'true' if the [Item] is a currency token, 'false' otherwise + * + * @return bool isCurrencyToken + */ + int IsCurrencyToken(Eluna* E, Item* item) + { + E->Push(item->IsCurrencyToken()); + return 1; + } + + /** + * Returns 'true' if the [Item] is a not an empty bag, 'false' otherwise + * + * @return bool isNotEmptyBag + */ + int IsNotEmptyBag(Eluna* E, Item* item) + { + E->Push(item->IsNotEmptyBag()); + return 1; + } + + /** + * Returns 'true' if the [Item] is broken, 'false' otherwise + * + * @return bool isBroken + */ + int IsBroken(Eluna* E, Item* item) + { + E->Push(item->IsBroken()); + return 1; + } + + /** + * Returns 'true' if the [Item] can be traded, 'false' otherwise + * + * @return bool isTradeable + */ + int CanBeTraded(Eluna* E, Item* item) + { + bool mail = E->CHECKVAL(2, false); + + E->Push(item->CanBeTraded(mail)); + return 1; + } + + /** + * Returns 'true' if the [Item] is currently in a trade window, 'false' otherwise + * + * @return bool isInTrade + */ + int IsInTrade(Eluna* E, Item* item) + { + E->Push(item->IsInTrade()); + return 1; + } + + /** + * Returns 'true' if the [Item] is currently in a bag, 'false' otherwise + * + * @return bool isInBag + */ + int IsInBag(Eluna* E, Item* item) + { + E->Push(item->IsInBag()); + return 1; + } + + /** + * Returns 'true' if the [Item] is currently equipped, 'false' otherwise + * + * @return bool isEquipped + */ + int IsEquipped(Eluna* E, Item* item) + { + E->Push(item->IsEquipped()); + return 1; + } + + /** + * Returns 'true' if the [Item] has the [Quest] specified tied to it, 'false' otherwise + * + * @param uint32 questId : the [Quest] id to be checked + * @return bool hasQuest + */ + int HasQuest(Eluna* E, Item* item) + { + uint32 quest = E->CHECKVAL(2); + + E->Push(item->hasQuest(quest)); + return 1; + } + + /** + * Returns 'true' if the [Item] is a potion, 'false' otherwise + * + * @return bool isPotion + */ + int IsPotion(Eluna* E, Item* item) + { + E->Push(item->IsPotion()); + return 1; + } + + #if 0 + int IsWeaponVellum(Eluna* E, Item* item) + { + E->Push(item->IsWeaponVellum()); + return 1; + } + #endif + #if 0 + int IsArmorVellum(Eluna* E, Item* item) + { + E->Push(item->IsArmorVellum()); + return 1; + } + #endif + /** + * Returns 'true' if the [Item] is a conjured consumable, 'false' otherwise + * + * @return bool isConjuredConsumable + */ + int IsConjuredConsumable(Eluna* E, Item* item) + { + E->Push(item->IsConjuredConsumable()); + return 1; + } + + /** + * Returns 'true' if the refund period has expired for this [Item], 'false' otherwise + * + * @return bool isRefundExpired + */ + int IsRefundExpired(Eluna* E, Item* item) + { + E->Push(item->IsRefundExpired()); + return 1; + } + + /** + * Returns the chat link of the [Item] + * + * @table + * @columns [Locale, ID] + * @values [LOCALE_enUS, 0] + * @values [LOCALE_koKR, 1] + * @values [LOCALE_frFR, 2] + * @values [LOCALE_deDE, 3] + * @values [LOCALE_zhCN, 4] + * @values [LOCALE_zhTW, 5] + * @values [LOCALE_esES, 6] + * @values [LOCALE_esMX, 7] + * @values [LOCALE_ruRU, 8] + * + * @param [LocaleConstant] locale = DEFAULT_LOCALE : locale to return the [Item]'s name in + * @return string itemLink + */ + int GetItemLink(Eluna* E, Item* item) + { + uint8 locale = E->CHECKVAL(2, DEFAULT_LOCALE); + if (locale >= TOTAL_LOCALES) + return luaL_argerror(E->L, 2, "valid LocaleConstant expected"); + + const ItemTemplate* temp = item->GetTemplate(); + + std::string name = temp->Name1; + if (ItemLocale const* il = eObjectMgr->GetItemLocale(temp->ItemId)) + ObjectMgr::GetLocaleString(il->Name, static_cast(locale), name); + + if (int32 itemRandPropId = item->GetItemRandomPropertyId()) + { + std::array const* suffix = NULL; + if (itemRandPropId < 0) + { + const ItemRandomSuffixEntry* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-item->GetItemRandomPropertyId()); + if (itemRandEntry) + suffix = &itemRandEntry->Name; + } + else + { + const ItemRandomPropertiesEntry* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(item->GetItemRandomPropertyId()); + if (itemRandEntry) + suffix = &itemRandEntry->Name; + } + if (suffix) + { + name += ' '; + name += (*suffix)[(name != temp->Name1) ? locale : uint8(DEFAULT_LOCALE)]; + } + } + + std::ostringstream oss; + oss << "|c" << std::hex << ItemQualityColors[temp->Quality] << std::dec << + "|Hitem:" << temp->ItemId << ":" << + item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT) << ":" << + item->GetEnchantmentId(SOCK_ENCHANTMENT_SLOT) << ":" << + item->GetEnchantmentId(SOCK_ENCHANTMENT_SLOT_2) << ":" << + item->GetEnchantmentId(SOCK_ENCHANTMENT_SLOT_3) << ":" << + item->GetEnchantmentId(BONUS_ENCHANTMENT_SLOT) << ":" << + item->GetItemRandomPropertyId() << ":" << item->GetItemSuffixFactor() << ":" << + (uint32)item->GetOwner()->GetLevel() << "|h[" << name << "]|h|r"; + + E->Push(oss.str()); + return 1; + } + + /** + * Returns GUID of the [Player] who currently owns the [Item] + * + * @return ObjectGuid guid : guid of the [Player] who owns the [Item] + */ + int GetOwnerGUID(Eluna* E, Item* item) + { + E->Push(item->GetOwnerGUID()); + return 1; + } + + /** + * Returns the [Player] who currently owns the [Item] + * + * @return [Player] player : the [Player] who owns the [Item] + */ + int GetOwner(Eluna* E, Item* item) + { + E->Push(item->GetOwner()); + return 1; + } + + /** + * Returns the [Item]s stack count + * + * @return uint32 count + */ + int GetCount(Eluna* E, Item* item) + { + E->Push(item->GetCount()); + return 1; + } + + /** + * Returns the [Item]s max stack count + * + * @return uint32 maxCount + */ + int GetMaxStackCount(Eluna* E, Item* item) + { + E->Push(item->GetMaxStackCount()); + return 1; + } + + /** + * Returns the [Item]s current slot + * + * @return uint8 slot + */ + int GetSlot(Eluna* E, Item* item) + { + E->Push(item->GetSlot()); + return 1; + } + + /** + * Returns the [Item]s current bag slot + * + * @return uint8 bagSlot + */ + int GetBagSlot(Eluna* E, Item* item) + { + E->Push(item->GetBagSlot()); + return 1; + } + + /** + * Returns the [Item]s enchantment ID by enchant slot specified + * + * @param [EnchantmentSlot] enchantSlot : the enchant slot specified + * @return uint32 enchantId : the id of the enchant slot specified + */ + int GetEnchantmentId(Eluna* E, Item* item) + { + uint32 enchant_slot = E->CHECKVAL(2); + + if (enchant_slot >= MAX_INSPECTED_ENCHANTMENT_SLOT) + return luaL_argerror(E->L, 2, "valid EnchantmentSlot expected"); + + E->Push(item->GetEnchantmentId(EnchantmentSlot(enchant_slot))); + return 1; + } + + /** + * Returns the spell ID tied to the [Item] by spell index + * + * @param uint32 spellIndex : the spell index specified + * @return uint32 spellId : the id of the spell + */ + int GetSpellId(Eluna* E, Item* item) + { + uint32 index = E->CHECKVAL(2); + if (index >= MAX_ITEM_PROTO_SPELLS) + return luaL_argerror(E->L, 2, "valid SpellIndex expected"); + + E->Push(item->GetTemplate()->Effects[index].SpellID); + return 1; + } + + /** + * Returns the spell trigger tied to the [Item] by spell index + * + * @param uint32 spellIndex : the spell index specified + * @return uint32 spellTrigger : the spell trigger of the specified index + */ + int GetSpellTrigger(Eluna* E, Item* item) + { + uint32 index = E->CHECKVAL(2); + if (index >= MAX_ITEM_PROTO_SPELLS) + return luaL_argerror(E->L, 2, "valid SpellIndex expected"); + + E->Push(item->GetTemplate()->Effects[index].TriggerType); + return 1; + } + + /** + * Returns class of the [Item] + * + * @return uint32 class + */ + #if 0 + int GetClass(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Class); + return 1; + } + #endif + /** + * Returns subclass of the [Item] + * + * @return uint32 subClass + */ + #if 0 + int GetSubClass(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->SubClass); + return 1; + } + #endif + /** + * Returns the ID of the [Item] + * + * @return uint32 itemId + */ + #if 0 + int GetItemId(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->ItemId); + return 1; + } + #endif + /** + * Returns the name of the [Item] + * + * @return string name + */ + #if 0 + int GetName(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Name1); + return 1; + } + #endif + /** + * Returns the display ID of the [Item] + * + * @return uint32 displayId + */ + #if 0 + int GetDisplayId(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->DisplayInfoID); + return 1; + } + #endif + /** + * Returns the quality of the [Item] + * + * @return uint32 quality + */ + #if 0 + int GetQuality(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Quality); + return 1; + } + #endif + /** + * Returns the flags of the [Item] + * + * @return uint32 flags + */ + #if 0 + int GetFlags(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Flags[0]); + return 1; + } + #endif + /** + * Returns the flags2 of the [Item] + * + * @return uint32 flags2 + */ + #if 0 + int GetFlags2(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Flags[1]); + return 1; + } + #endif + /** + * Returns the extraFlags of the [Item] + * + * @return uint32 extraFlags + */ + int GetExtraFlags(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->FlagsCu); + return 1; + } + + /** + * Returns the default purchase count of the [Item] + * + * @return uint32 count + */ + #if 0 + int GetBuyCount(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->BuyCount); + return 1; + } + #endif + /** + * Returns the purchase price of the [Item] + * + * @return uint32 price + */ + #if 0 + int GetBuyPrice(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->BuyPrice); + return 1; + } + #endif + /** + * Returns the sell price of the [Item] + * + * @return uint32 price + */ + #if 0 + int GetSellPrice(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->SellPrice); + return 1; + } + #endif + /** + * Returns the inventory type of the [Item] + * + * @return uint32 inventoryType + */ + #if 0 + int GetInventoryType(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->InventoryType); + return 1; + } + #endif + /** + * Returns the [Player] classes allowed to use this [Item] + * + * @return uint32 allowableClass + */ + #if 0 + int GetAllowableClass(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->AllowableClass); + return 1; + } + #endif + /** + * Returns the [Player] races allowed to use this [Item] + * + * @return uint32 allowableRace + */ + #if 0 + int GetAllowableRace(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->AllowableRace); + return 1; + } + #endif + /** + * Returns the [Item]s level + * + * @return uint32 itemLevel + */ + #if 0 + int GetItemLevel(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->ItemLevel); + return 1; + } + #endif + /** + * Returns the minimum level required to use this [Item] + * + * @return uint32 requiredLevel + */ + #if 0 + int GetRequiredLevel(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->RequiredLevel); + return 1; + } + #endif + /** + * Returns the amount of stat values on this [Item] + * + * @return uint32 statsCount + */ + + #if 0 + int GetStatsCount(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->StatsCount); + return 1; + } + #endif + /** + * Returns the stat info of the specified stat slot of this [Item] + * + * @param uint8 statSlot : the stat slot specified + * @return int32 statValue + * @return int32 statType + */ + int GetStatInfo(Eluna* E, Item* item) + { + uint8 statSlot = E->CHECKVAL(2); + int32 statValue = 0; + int32 statType = 0; + + if (statSlot > 0 && statSlot <= item->GetTemplate()->StatsCount) + { + auto& statEntry = item->GetTemplate()->ItemStat[statSlot - 1]; + statValue = statEntry.ItemStatValue; + statType = statEntry.ItemStatType; + } + + E->Push(statValue); + E->Push(statType); + return 2; + } + + /** + * Returns the damage info of the specified damage slot of this [Item] + * + * @param uint8 damageSlot : the damage slot specified (1 or 2) + * @return uint32 damageType + * @return float minDamage + * @return float maxDamage + */ + + #if 0 + int GetDamageInfo(Eluna* E, Item* item) + { + uint8 damageSlot = E->CHECKVAL(2); + uint32 damageType = 0; + float damageMin = 0; + float damageMax = 0; + + if (damageSlot > 0 && damageSlot <= MAX_ITEM_PROTO_DAMAGES) + { + auto& damageEntry = item->GetTemplate()->Damage[damageSlot - 1]; + damageType = damageEntry.DamageType; + damageMin = damageEntry.DamageMin; + damageMax = damageEntry.DamageMax; + } + + E->Push(damageType); + E->Push(damageMin); + E->Push(damageMax); + return 3; + } + #endif + /** + * Returns the base attack speed of this [Item] + * + * @return uint32 speed + */ + int GetSpeed(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Delay); + return 1; + } + + /** + * Returns the base armor of this [Item] + * + * @return uint32 armor + */ + #if 0 + int GetArmor(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->Armor); + return 1; + } + #endif + /** + * Returns the max durability of this [Item] + * + * @return uint32 maxDurability + */ + int GetMaxDurability(Eluna* E, Item* item) + { + E->Push(item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)); + return 1; + } + + /** + * Returns the current durability of this [Item] + * + * @return uint32 durabiliy + */ + int GetDurability(Eluna* E, Item* item) + { + E->Push(item->GetUInt32Value(ITEM_FIELD_DURABILITY)); + return 1; + } + + /** + * Returns the random property ID of this [Item] + * + * @return uint32 randomPropertyId + */ + #if 0 + int GetRandomProperty(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->RandomProperty); + return 1; + } + #endif + /** + * Returns the random suffix ID of this [Item] + * + * @return uint32 suffixId + */ + #if 0 + int GetRandomSuffix(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->RandomSuffix); + return 1; + } + #endif + /** + * Returns the item set ID of this [Item] + * + * @return uint32 itemSetId + */ + #if 0 + int GetItemSet(Eluna* E, Item* item) + { + E->Push(item->GetTemplate()->ItemSet); + return 1; + } + #endif + /** + * Returns the bag size of this [Item], 0 if [Item] is not a bag + * + * @return uint32 bagSize + */ + int GetBagSize(Eluna* E, Item* item) + { + if (Bag* bag = item->ToBag()) + E->Push(bag->GetBagSize()); + else + E->Push(0); + return 1; + } + + /** + * Sets the [Player] specified as the owner of the [Item] + * + * @param [Player] player : the [Player] specified + */ + int SetOwner(Eluna* E, Item* item) + { + Player* player = E->CHECKOBJ(2); + + item->SetOwnerGUID(player->GET_GUID()); + return 0; + } + + /** + * Sets the binding of the [Item] to 'true' or 'false' + * + * @param bool setBinding + */ + int SetBinding(Eluna* E, Item* item) + { + bool soulbound = E->CHECKVAL(2); + + item->SetBinding(soulbound); + item->SetState(ITEM_CHANGED, item->GetOwner()); + + return 0; + } + + /** + * Sets the stack count of the [Item] + * + * @param uint32 count + */ + int SetCount(Eluna* E, Item* item) + { + uint32 count = E->CHECKVAL(2); + item->SetCount(count); + return 0; + } + + /** + * Sets the specified enchantment of the [Item] to the specified slot + * + * @param uint32 enchantId : the ID of the enchant to be applied + * @param uint32 enchantSlot : the slot for the enchant to be applied to + * @return bool enchantmentSuccess : if enchantment is successfully set to specified slot, returns 'true', otherwise 'false' + */ + int SetEnchantment(Eluna* E, Item* item) + { + Player* owner = item->GetOwner(); + if (!owner) + { + E->Push(false); + return 1; + } + + uint32 enchant = E->CHECKVAL(2); + if (!sSpellItemEnchantmentStore.LookupEntry(enchant)) + { + E->Push(false); + return 1; + } + + EnchantmentSlot slot = (EnchantmentSlot)E->CHECKVAL(3); + if (slot >= MAX_INSPECTED_ENCHANTMENT_SLOT) + return luaL_argerror(E->L, 2, "valid EnchantmentSlot expected"); + + owner->ApplyEnchantment(item, slot, false); + item->SetEnchantment(slot, enchant, 0, 0); + owner->ApplyEnchantment(item, slot, true); + E->Push(true); + return 1; + } + + /** + * Sets the random properties for the [Item] from a given random property ID. + * + * @param uint32 randomPropId : The ID of the random property to be applied. + */ + #if 0 + int SetRandomProperty(Eluna* E, Item* item) + { + uint32 randomPropId = E->CHECKVAL(2); + item->SetItemRandomProperties(randomPropId); + return 0; + } + #endif + + #if 0 + int SetRandomSuffix(Eluna* E, Item* item) + { + uint32 randomPropId = E->CHECKVAL(2); + item->SetItemRandomProperties(-(int32)randomPropId); + return 0; + } + #endif + /* OTHER */ + /** + * Removes an enchant from the [Item] by the specified slot + * + * @param uint32 enchantSlot : the slot for the enchant to be removed from + * @return bool enchantmentRemoved : if enchantment is successfully removed from specified slot, returns 'true', otherwise 'false' + */ + int ClearEnchantment(Eluna* E, Item* item) + { + Player* owner = item->GetOwner(); + if (!owner) + { + E->Push(false); + return 1; + } + + EnchantmentSlot slot = (EnchantmentSlot)E->CHECKVAL(2); + if (slot >= MAX_INSPECTED_ENCHANTMENT_SLOT) + return luaL_argerror(E->L, 2, "valid EnchantmentSlot expected"); + + if (!item->GetEnchantmentId(slot)) + { + E->Push(false); + return 1; + } + + owner->ApplyEnchantment(item, slot, false); + item->ClearEnchantment(slot); + E->Push(true); + return 1; + } + + /** + * Saves the [Item] to the database + */ + int SaveToDB(Eluna* /*E*/, Item* item) + { + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); + item->SaveToDB(trans); + return 0; + } + + ElunaRegister ItemMethods[] = + { + // Getters + { "GetOwnerGUID", &LuaItem::GetOwnerGUID }, + { "GetOwner", &LuaItem::GetOwner }, + { "GetCount", &LuaItem::GetCount }, + { "GetMaxStackCount", &LuaItem::GetMaxStackCount }, + { "GetSlot", &LuaItem::GetSlot }, + { "GetBagSlot", &LuaItem::GetBagSlot }, + { "GetEnchantmentId", &LuaItem::GetEnchantmentId }, + { "GetSpellId", &LuaItem::GetSpellId }, + { "GetSpellTrigger", &LuaItem::GetSpellTrigger }, + { "GetItemLink", &LuaItem::GetItemLink }, + + + + + + + + { "GetExtraFlags", &LuaItem::GetExtraFlags }, + + + + + + + + + + + + + { "GetBagSize", &LuaItem::GetBagSize }, + { "GetStatInfo", &LuaItem::GetStatInfo }, + + { "GetSpeed", &LuaItem::GetSpeed }, + + { "GetMaxDurability", &LuaItem::GetMaxDurability }, + { "GetDurability", &LuaItem::GetDurability }, + + // Setters + { "SetOwner", &LuaItem::SetOwner }, + { "SetBinding", &LuaItem::SetBinding }, + { "SetCount", &LuaItem::SetCount }, + + + // Boolean + { "IsSoulBound", &LuaItem::IsSoulBound }, + { "IsBoundAccountWide", &LuaItem::IsBoundAccountWide }, + { "IsBoundByEnchant", &LuaItem::IsBoundByEnchant }, + { "IsNotBoundToPlayer", &LuaItem::IsNotBoundToPlayer }, + { "IsLocked", &LuaItem::IsLocked }, + { "IsBag", &LuaItem::IsBag }, + { "IsCurrencyToken", &LuaItem::IsCurrencyToken }, + { "IsNotEmptyBag", &LuaItem::IsNotEmptyBag }, + { "IsBroken", &LuaItem::IsBroken }, + { "CanBeTraded", &LuaItem::CanBeTraded }, + { "IsInTrade", &LuaItem::IsInTrade }, + { "IsInBag", &LuaItem::IsInBag }, + { "IsEquipped", &LuaItem::IsEquipped }, + { "HasQuest", &LuaItem::HasQuest }, + { "IsPotion", &LuaItem::IsPotion }, + + + { "IsRefundExpired", &LuaItem::IsRefundExpired }, + { "IsConjuredConsumable", &LuaItem::IsConjuredConsumable }, + { "SetEnchantment", &LuaItem::SetEnchantment }, + { "ClearEnchantment", &LuaItem::ClearEnchantment }, + + // Other + { "SaveToDB", &LuaItem::SaveToDB } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/MapMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/MapMethods.h new file mode 100644 index 0000000000..87dd97044d --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/MapMethods.h @@ -0,0 +1,362 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef MAPMETHODS_H +#define MAPMETHODS_H + +#include "ElunaInstanceAI.h" +#include "LuaValue.h" + +/*** + * A game map, e.g. Azeroth, Eastern Kingdoms, the Molten Core, etc. + * + * Inherits all methods from: none + */ +namespace LuaMap +{ + + /** + * Returns `true` if the [Map] is an arena [BattleGround], `false` otherwise. + * + * @return bool isArena + */ + int IsArena(Eluna* E, Map* map) + { + E->Push(map->IsBattleArena()); + return 1; + } + + /** + * Returns `true` if the [Map] is a non-arena [BattleGround], `false` otherwise. + * + * @return bool isBattleGround + */ + int IsBattleground(Eluna* E, Map* map) + { + E->Push(map->IsBattleground()); + return 1; + } + + /** + * Returns `true` if the [Map] is a dungeon, `false` otherwise. + * + * @return bool isDungeon + */ + int IsDungeon(Eluna* E, Map* map) + { + E->Push(map->IsDungeon()); + return 1; + } + + /** + * Returns `true` if the [Map] has no [Player]s, `false` otherwise. + * + * @return bool isEmpty + */ + int IsEmpty(Eluna* E, Map* map) + { + E->Push(map->isEmpty()); + return 1; + } + + /** + * Returns `true` if the [Map] is a heroic, `false` otherwise. + * + * @return bool isHeroic + */ + int IsHeroic(Eluna* E, Map* map) + { + E->Push(map->IsHeroic()); + return 1; + } + + /** + * Returns `true` if the [Map] is a raid, `false` otherwise. + * + * @return bool isRaid + */ + int IsRaid(Eluna* E, Map* map) + { + E->Push(map->IsRaid()); + return 1; + } + + /** + * Returns the name of the [Map]. + * + * @return string mapName + */ + int GetName(Eluna* E, Map* map) + { + E->Push(map->GetMapName()); + return 1; + } + + /** + * Returns the height of the [Map] at the given X and Y coordinates. + * + * In case of no height found nil is returned + * + * @param float x + * @param float y + * @return float z + */ + int GetHeight(Eluna* E, Map* map) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + uint32 phasemask = E->CHECKVAL(4, 1); + + float z = map->GetHeight(phasemask, x, y, MAX_HEIGHT); + if (z != INVALID_HEIGHT) + E->Push(z); + return 1; + } + + /** + * Returns the difficulty of the [Map]. + * + * Always returns 0 if the expansion is pre-TBC. + * + * @return int32 difficulty + */ + #if 0 + int GetDifficulty(Eluna* E, Map* map) + { + E->Push(map->GetDifficulty()); + return 1; + } + #endif + /** + * Returns the instance ID of the [Map]. + * + * @return uint32 instanceId + */ + int GetInstanceId(Eluna* E, Map* map) + { + E->Push(map->GetInstanceId()); + return 1; + } + + /** + * Returns the player count currently on the [Map] (excluding GMs). + * + * @return uint32 playerCount + */ + int GetPlayerCount(Eluna* E, Map* map) + { + E->Push(map->GetPlayersCountExceptGMs()); + return 1; + } + + /** + * Returns the ID of the [Map]. + * + * @return uint32 mapId + */ + int GetMapId(Eluna* E, Map* map) + { + E->Push(map->GetId()); + return 1; + } + + /** + * Returns the area ID of the [Map] at the specified X, Y, and Z coordinates. + * + * @param float x + * @param float y + * @param float z + * @param uint32 phasemask = PHASEMASK_NORMAL + * @return uint32 areaId + */ + #if 0 + int GetAreaId(Eluna* E, Map* map) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + float phasemask = E->CHECKVAL(5, PHASEMASK_NORMAL); + + E->Push(map->GetAreaId(phasemask, x, y, z)); + return 1; + } + #endif + + /** + * Returns a [WorldObject] by its GUID from the map if it is spawned. + * + * @param ObjectGuid guid + * @return [WorldObject] object + */ + #if 0 + int GetWorldObject(Eluna* E, Map* map) + { + ObjectGuid guid = E->CHECKVAL(2); + + switch (guid.GetHigh()) + { + case HIGHGUID_PLAYER: + E->Push(eObjectAccessor()GetPlayer(map, guid)); + break; + case HIGHGUID_TRANSPORT: + case HIGHGUID_MO_TRANSPORT: + case HIGHGUID_GAMEOBJECT: + E->Push(map->GetGameObject(guid)); + break; + case HIGHGUID_VEHICLE: + case HIGHGUID_UNIT: + E->Push(map->GetCreature(guid)); + break; + case HIGHGUID_PET: + E->Push(map->GetPet(guid)); + break; + case HIGHGUID_DYNAMICOBJECT: + E->Push(map->GetDynamicObject(guid)); + break; + case HIGHGUID_CORPSE: + E->Push(map->GetCorpse(guid)); + break; + default: + break; + } + + return 1; + } + #endif + /** + * Sets the [Weather] type based on [WeatherType] and grade supplied. + * + * @table + * @columns [WeatherType, ID] + * @values [WEATHER_TYPE_FINE, 0] + * @values [WEATHER_TYPE_RAIN, 1] + * @values [WEATHER_TYPE_SNOW, 2] + * @values [WEATHER_TYPE_STORM, 3] + * @values [WEATHER_TYPE_THUNDERS, 86] + * @values [WEATHER_TYPE_BLACKRAIN, 90] + * + * @param uint32 zone : id of the zone to set the weather for + * @param [WeatherType] type : the [WeatherType], see above available weather types + * @param float grade : the intensity/grade of the [Weather], ranges from 0 to 1 + */ + int SetWeather(Eluna* E, Map* map) + { + (void)map; // ensure that the variable is referenced in order to pass compiler checks + uint32 zoneId = E->CHECKVAL(2); + uint32 weatherType = E->CHECKVAL(3); + float grade = E->CHECKVAL(4); + + if (Weather * weather = map->GetOrGenerateZoneDefaultWeather(zoneId)) + weather->SetWeather((WeatherType)weatherType, grade); + return 0; + } + + + #if 0 + int GetInstanceData(Eluna* E, Map* map) + { + ElunaInstanceAI* iAI = NULL; + if (InstanceMap* inst = map->ToInstanceMap()) + iAI = dynamic_cast(inst->GetInstanceScript()); + + if (iAI) + E->PushInstanceData(iAI, false); + else + E->Push(); // nil + + return 1; + } + #endif + + #if 0 + int SaveInstanceData(Eluna* /*E*/, Map* map) + { + ElunaInstanceAI* iAI = NULL; + if (InstanceMap* inst = map->ToInstanceMap()) + iAI = dynamic_cast(inst->GetInstanceScript()); + + if (iAI) + iAI->SaveToDB(); + + return 0; + } + #endif + /** + * Returns a table with all the current [Player]s in the map + * + * @table + * @columns [Team, ID] + * @values [ALLIANCE, 0] + * @values [HORDE, 1] + * @values [NEUTRAL, 2] + * + * @param [TeamId] team : optional check team of the [Player], Alliance, Horde or Neutral (All) + * @return table mapPlayers + */ + #if 0 + int GetPlayers(Eluna* E, Map* map) + { + uint32 team = E->CHECKVAL(2, TEAM_NEUTRAL); + + lua_newtable(E->L); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + Map::PlayerList const& players = map->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player* player = itr->GetSource(); + if (!player) + continue; + + if (player->GetSession() && (team >= TEAM_NEUTRAL || uint32(player->GetTeamId()) == team)) + { + E->Push(player); + lua_rawseti(E->L, tbl, ++i); + } + } + + lua_settop(E->L, tbl); + return 1; + } + #endif + + #if 0 + int Data(Eluna* E, Map* map) + { + return LuaVal::PushLuaVal(E->L, map->lua_data); + } + #endif + ElunaRegister MapMethods[] = + { + // Getters + { "GetName", &LuaMap::GetName }, + + { "GetInstanceId", &LuaMap::GetInstanceId }, + + { "GetPlayerCount", &LuaMap::GetPlayerCount }, + + { "GetMapId", &LuaMap::GetMapId }, + + { "GetHeight", &LuaMap::GetHeight }, + + + // Setters + { "SetWeather", &LuaMap::SetWeather }, + + // Boolean + { "IsArena", &LuaMap::IsArena }, + { "IsBattleground", &LuaMap::IsBattleground }, + { "IsDungeon", &LuaMap::IsDungeon }, + { "IsEmpty", &LuaMap::IsEmpty }, + { "IsHeroic", &LuaMap::IsHeroic }, + { "IsRaid", &LuaMap::IsRaid } + + + + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/ObjectMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/ObjectMethods.h new file mode 100644 index 0000000000..7b558cfe54 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/ObjectMethods.h @@ -0,0 +1,497 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef OBJECTMETHODS_H +#define OBJECTMETHODS_H + +/*** + * A basic game object (either an [Item] or a [WorldObject]). + * + * Objects in MaNGOS/Trinity are stored an a giant block of "values". + * Subclasses of Object, like [WorldObject], extend the block with more data specific to that subclass. + * Further subclasses, like [Player], extend it even further. + * + * A detailed map of all the fields in this data block can be found in the UpdateFields.h file of your emulator + * (it varies depending on the expansion supported). + * + * The GetValue methods in this class (e.g. [Object:GetInt32Value]) provide low-level access to the data block. + * Other methods, like [Object:HasFlag] and [Object:GetScale], merely wrap the GetValue methods and provide a simpler interface. + * + * Inherits all methods from: none + */ +namespace LuaObject +{ + /** + * Returns `true` if the specified flag is set, otherwise `false`. + * + * @param uint16 index : the index of the flags data in the [Object] + * @param uint32 flag : the flag to check for in the flags data + * @return bool hasFlag + */ + int HasFlag(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint32 flag = E->CHECKVAL(3); + + E->Push(obj->HasFlag(index, flag)); + return 1; + } + + /** + * Returns `true` if the [Object] has been added to its [Map], otherwise `false`. + * + * @return bool inWorld + */ + int IsInWorld(Eluna* E, Object* obj) + { + E->Push(obj->IsInWorld()); + return 1; + } + + /** + * Returns the data at the specified index, casted to a signed 32-bit integer. + * + * @param uint16 index + * @return int32 value + */ + int GetInt32Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + E->Push(obj->GetInt32Value(index)); + return 1; + } + + /** + * Returns the data at the specified index, casted to a unsigned 32-bit integer. + * + * @param uint16 index + * @return uint32 value + */ + int GetUInt32Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + E->Push(obj->GetUInt32Value(index)); + return 1; + } + + /** + * Returns the data at the specified index, casted to a single-precision floating point value. + * + * @param uint16 index + * @return float value + */ + int GetFloatValue(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + E->Push(obj->GetFloatValue(index)); + return 1; + } + + /** + * Returns the data at the specified index and offset, casted to an unsigned 8-bit integer. + * + * E.g. if you want the second byte at index 10, you would pass in 1 as the offset. + * + * @param uint16 index + * @param uint8 offset : should be 0, 1, 2, or 3 + * @return uint8 value + */ + int GetByteValue(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint8 offset = E->CHECKVAL(3); + E->Push(obj->GetByteValue(index, offset)); + return 1; + } + + /** + * Returns the data at the specified index and offset, casted to a signed 16-bit integer. + * + * E.g. if you want the second half-word at index 10, you would pass in 1 as the offset. + * + * @param uint16 index + * @param uint8 offset : should be 0 or 1 + * @return uint16 value + */ + int GetUInt16Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint8 offset = E->CHECKVAL(3); + E->Push(obj->GetUInt16Value(index, offset)); + return 1; + } + + /** + * Returns the scale/size of the [Object]. + * + * This affects the size of a [WorldObject] in-game, but [Item]s don't have a "scale". + * + * @return float scale + */ + int GetScale(Eluna* E, Object* obj) + { + E->Push(obj->GetObjectScale()); + return 1; + } + + /** + * Returns the entry of the [Object]. + * + * [Player]s do not have an "entry". + * + * @return uint32 entry + */ + int GetEntry(Eluna* E, Object* obj) + { + E->Push(obj->GetEntry()); + return 1; + } + + /** + * Returns the GUID of the [Object]. + * + * GUID is an unique identifier for the object. + * + * However on MaNGOS and cMangos creatures and gameobjects inside different maps can share + * the same GUID but not on the same map. + * + * On TrinityCore this value is unique across all maps + * + * @return ObjectGuid guid + */ + int GetGUID(Eluna* E, Object* obj) + { + E->Push(obj->GET_GUID()); + return 1; + } + + /** + * Returns the low-part of the [Object]'s GUID. + * + * On TrinityCore all low GUIDs are different for all objects of the same type. + * For example creatures in instances are assigned new GUIDs when the Map is created. + * + * On MaNGOS and cMaNGOS low GUIDs are unique only on the same map. + * For example creatures in instances use the same low GUID assigned for that spawn in the database. + * This is why to identify a creature you have to know the instanceId and low GUID. See [Map:GetIntstanceId] + * + * @return uint32 guidLow + */ + int GetGUIDLow(Eluna* E, Object* obj) + { + E->Push(obj->GetGUID().GetCounter()); + return 1; + } + + /** + * Returns the TypeId of the [Object]. + * + * @table + * @columns [TypeID, ID] + * @values [TYPEID_OBJECT, 0] + * @values [TYPEID_ITEM, 1] + * @values [TYPEID_CONTAINER, 2] + * @values [TYPEID_UNIT, 3] + * @values [TYPEID_PLAYER, 4] + * @values [TYPEID_GAMEOBJECT, 5] + * @values [TYPEID_DYNAMICOBJECT, 6] + * @values [TYPEID_CORPSE, 7] + * + * @return uint8 typeID + */ + int GetTypeId(Eluna* E, Object* obj) + { + E->Push(obj->GetTypeId()); + return 1; + } + + /** + * Returns the data at the specified index, casted to an unsigned 64-bit integer. + * + * @param uint16 index + * @return uint64 value + */ + int GetUInt64Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + E->Push(obj->GetUInt64Value(index)); + return 1; + } + + /** + * Sets the specified flag in the data value at the specified index. + * + * If the flag was already set, it remains set. + * + * To remove a flag, use [Object:RemoveFlag]. + * + * @param uint16 index + * @param uint32 value + */ + int SetFlag(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint32 flag = E->CHECKVAL(3); + + obj->SetFlag(index, flag); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to a signed 32-bit integer. + * + * @param uint16 index + * @param int32 value + */ + int SetInt32Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + int32 value = E->CHECKVAL(3); + obj->SetInt32Value(index, value); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to an unsigned 32-bit integer. + * + * @param uint16 index + * @param uint32 value + */ + int SetUInt32Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint32 value = E->CHECKVAL(3); + obj->SetUInt32Value(index, value); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to an unsigned 32-bit integer. + * + * @param uint16 index + * @param uint32 value + */ + int UpdateUInt32Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint32 value = E->CHECKVAL(3); + obj->UpdateUInt32Value(index, value); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to a single-precision floating point value. + * + * @param uint16 index + * @param float value + */ + int SetFloatValue(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + float value = E->CHECKVAL(3); + + obj->SetFloatValue(index, value); + return 0; + } + + /** + * Sets the data at the specified index and offset to the given value, converted to an unsigned 8-bit integer. + * + * @param uint16 index + * @param uint8 offset : should be 0, 1, 2, or 3 + * @param uint8 value + */ + int SetByteValue(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint8 offset = E->CHECKVAL(3); + uint8 value = E->CHECKVAL(4); + obj->SetByteValue(index, offset, value); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to an unsigned 16-bit integer. + * + * @param uint16 index + * @param uint8 offset : should be 0 or 1 + * @param uint16 value + */ + int SetUInt16Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint8 offset = E->CHECKVAL(3); + uint16 value = E->CHECKVAL(4); + obj->SetUInt16Value(index, offset, value); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to a signed 16-bit integer. + * + * @param uint16 index + * @param uint8 offset : should be 0 or 1 + * @param int16 value + */ + + #if 0 + int SetInt16Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint8 offset = E->CHECKVAL(3); + int16 value = E->CHECKVAL(4); + obj->SetInt16Value(index, offset, value); + return 0; + } + #endif + /** + * Sets the [Object]'s scale/size to the given value. + * + * @param float scale + */ + int SetScale(Eluna* E, Object* obj) + { + float size = E->CHECKVAL(2); + + obj->SetObjectScale(size); + return 0; + } + + /** + * Sets the data at the specified index to the given value, converted to an unsigned 64-bit integer. + * + * @param uint16 index + * @param uint64 value + */ + int SetUInt64Value(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint64 value = E->CHECKVAL(3); + obj->SetUInt64Value(index, value); + return 0; + } + + /** + * Removes a flag from the value at the specified index. + * + * @param uint16 index + * @param uint32 flag + */ + int RemoveFlag(Eluna* E, Object* obj) + { + uint16 index = E->CHECKVAL(2); + uint32 flag = E->CHECKVAL(3); + + obj->RemoveFlag(index, flag); + return 0; + } + + /** + * Attempts to convert the [Object] to a [Corpse]. + * + * If the [Object] is not a [Corpse], returns `nil`. + * + * @return [Corpse] corpse : the [Object] as a [Corpse], or `nil` + */ + int ToCorpse(Eluna* E, Object* obj) + { + E->Push(obj->ToCorpse()); + return 1; + } + + /** + * Attempts to convert the [Object] to a [GameObject]. + * + * If the [Object] is not a [GameObject], returns `nil`. + * + * @return [GameObject] gameObject : the [Object] as a [GameObject], or `nil` + */ + int ToGameObject(Eluna* E, Object* obj) + { + E->Push(obj->ToGameObject()); + return 1; + } + + /** + * Attempts to convert the [Object] to a [Unit]. + * + * If the [Object] is not a [Unit], returns `nil`. + * + * @return [Unit] unit : the [Object] as a [Unit], or `nil` + */ + int ToUnit(Eluna* E, Object* obj) + { + E->Push(obj->ToUnit()); + return 1; + } + + /** + * Attempts to convert the [Object] to a [Creature]. + * + * If the [Object] is not a [Creature], returns `nil`. + * + * @return [Creature] creature : the [Object] as a [Creature], or `nil` + */ + int ToCreature(Eluna* E, Object* obj) + { + E->Push(obj->ToCreature()); + return 1; + } + + /** + * Attempts to convert the [Object] to a [Player]. + * + * If the [Object] is not a [Player], returns `nil`. + * + * @return [Player] player : the [Object] as a [Player], or `nil` + */ + int ToPlayer(Eluna* E, Object* obj) + { + E->Push(obj->ToPlayer()); + return 1; + } + + ElunaRegister ObjectMethods[] = + { + // Getters + { "GetEntry", &LuaObject::GetEntry }, + { "GetGUID", &LuaObject::GetGUID }, + { "GetGUIDLow", &LuaObject::GetGUIDLow }, + { "GetInt32Value", &LuaObject::GetInt32Value }, + { "GetUInt32Value", &LuaObject::GetUInt32Value }, + { "GetFloatValue", &LuaObject::GetFloatValue }, + { "GetByteValue", &LuaObject::GetByteValue }, + { "GetUInt16Value", &LuaObject::GetUInt16Value }, + { "GetUInt64Value", &LuaObject::GetUInt64Value }, + { "GetScale", &LuaObject::GetScale }, + { "GetTypeId", &LuaObject::GetTypeId }, + + // Setters + { "SetInt32Value", &LuaObject::SetInt32Value }, + { "SetUInt32Value", &LuaObject::SetUInt32Value }, + { "UpdateUInt32Value", &LuaObject::UpdateUInt32Value }, + { "SetFloatValue", &LuaObject::SetFloatValue }, + { "SetByteValue", &LuaObject::SetByteValue }, + { "SetUInt16Value", &LuaObject::SetUInt16Value }, + + { "SetUInt64Value", &LuaObject::SetUInt64Value }, + { "SetScale", &LuaObject::SetScale }, + { "SetFlag", &LuaObject::SetFlag }, + + // Boolean + { "IsInWorld", &LuaObject::IsInWorld }, + { "HasFlag", &LuaObject::HasFlag }, + + // Other + { "ToGameObject", &LuaObject::ToGameObject }, + { "ToUnit", &LuaObject::ToUnit }, + { "ToCreature", &LuaObject::ToCreature }, + { "ToPlayer", &LuaObject::ToPlayer }, + { "ToCorpse", &LuaObject::ToCorpse }, + { "RemoveFlag", &LuaObject::RemoveFlag } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/PlayerMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/PlayerMethods.h new file mode 100644 index 0000000000..be8cfe3d01 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/PlayerMethods.h @@ -0,0 +1,4146 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef PLAYERMETHODS_H +#define PLAYERMETHODS_H + +#include "LuaValue.h" +#include "ChatPackets.h" +#include "NPCPackets.h" +#include "PartyPackets.h" +#include "Unit.h" +#include + +/*** + * Inherits all methods from: [Object], [WorldObject], [Unit] + */ +namespace LuaPlayer +{ + #if 0 + int CanTitanGrip(Eluna* E, Player* player) + { + Item* item = E->CHECKOBJ(2); + + E->Push(player->CanTitanGrip(item)); + return 1; + } + #endif + /** + * Returns 'true' if the [Player] has a talent by ID in specified spec, 'false' otherwise. + * + * @param uint32 spellId : talent spellId to check + * @param uint8 spec : specified spec. 0 for primary, 1 for secondary. + * @return bool hasTalent + */ + #if 0 + int HasTalent(Eluna* E, Player* player) + { + uint32 spellId = E->CHECKVAL(2); + uint8 maxSpecs = MAX_TALENT_GROUPS; + uint8 spec = E->CHECKVAL(3); + + if (spec >= maxSpecs) + return 1; + + E->Push(player->HasTalent(spellId, spec)); + return 1; + } + #endif + /** + * Returns 'true' if the [Player] has completed the specified achievement, 'false' otherwise. + * + * @param uint32 achievementId + * @return bool hasAchieved + */ + int HasAchieved(Eluna* E, Player* player) + { + uint32 achievementId = E->CHECKVAL(2); + + E->Push(player->HasAchieved(achievementId)); + return 1; + } + + /** + * Returns 'true' if the [Player] has an active [Quest] by specific ID, 'false' otherwise. + * + * @param uint32 questId + * @return bool hasQuest + */ + int HasQuest(Eluna* E, Player* player) + { + uint32 quest = E->CHECKVAL(2); + + E->Push(player->IsActiveQuest(quest)); + return 1; + } + + /** + * Returns 'true' if the [Player] has a skill by specific ID, 'false' otherwise. + * + * @param uint32 skill + * @return bool hasSkill + */ + int HasSkill(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->HasSkill(skill)); + return 1; + } + + /** + * Returns 'true' if the [Player] has a [Spell] by specific ID, 'false' otherwise. + * + * @param uint32 spellId + * @return bool hasSpell + */ + int HasSpell(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + + E->Push(player->HasSpell(id)); + return 1; + } + + /** + * Returns true if [Player] has specified login flag + * + * @param uint32 flag + * @return bool hasLoginFlag + */ + int HasAtLoginFlag(Eluna* E, Player* player) + { + uint32 flag = E->CHECKVAL(2); + + E->Push(player->HasAtLoginFlag((AtLoginFlags)flag)); + return 1; + } + + /** + * Returns true if [Player] has [Quest] for [GameObject] + * + * @param int32 entry : entry of a [GameObject] + * @return bool hasQuest + */ + #if 0 + int HasQuestForGO(Eluna* E, Player* player) + { + int32 entry = E->CHECKVAL(2); + + E->Push(player->HasQuestForGO(entry)); + return 1; + } + #endif + /** + * Returns 'true' if the [Player] has a title by specific ID, 'false' otherwise. + * + * @param uint32 titleId + * @return bool hasTitle + */ + int HasTitle(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(id); + if (titleInfo) + E->Push(player->HasTitle(titleInfo)); + return 1; + } + + /** + * Returns 'true' if the [Player] has the given amount of item entry specified, 'false' otherwise. + * + * @param uint32 itemId : entry of the item + * @param uint32 count = 1 : amount of items the player needs should have + * @param bool check_bank = false : determines if the item can be in player bank + * @return bool hasItem + */ + int HasItem(Eluna* E, Player* player) + { + uint32 itemId = E->CHECKVAL(2); + uint32 count = E->CHECKVAL(3, 1); + bool check_bank = E->CHECKVAL(4, false); + E->Push(player->HasItemCount(itemId, count, check_bank)); + return 1; + } + + /** + * Returns 'true' if the [Player] has a quest for the item entry specified, 'false' otherwise. + * + * @param uint32 entry : entry of the item + * @return bool hasQuest + */ + #if 0 + int HasQuestForItem(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + E->Push(player->HasQuestForItem(entry)); + return 1; + } + #endif + /** + * Returns 'true' if the [Player] can use the item or item entry specified, 'false' otherwise. + * + * @proto canUse = (item) + * @proto canUse = (entry) + * @param [Item] item : an instance of an item + * @param uint32 entry : entry of the item + * @return bool canUse + */ + int CanUseItem(Eluna* E, Player* player) + { + Item* item = E->CHECKOBJ(2, false); + if (item) + E->Push(player->CanUseItem(item) == EQUIP_ERR_OK); + else + { + uint32 entry = E->CHECKVAL(2); + const ItemTemplate* temp = eObjectMgr->GetItemTemplate(entry); + if (temp) + E->Push(player->CanUseItem(temp) == EQUIP_ERR_OK); + else + E->Push(false); + } + return 1; + } + + /** + * Returns 'true' if the [Spell] specified by ID is currently on cooldown for the [Player], 'false' otherwise. + * + * @param uint32 spellId + * @return bool hasSpellCooldown + */ + int HasSpellCooldown(Eluna* E, Player* player) + { + uint32 spellId = E->CHECKVAL(2); + + E->Push(player->GetSpellHistory()->HasCooldown(spellId)); + return 1; + } + + /** + * Returns 'true' if the [Player] can share [Quest] specified by ID, 'false' otherwise. + * + * @param uint32 entryId + * @return bool hasSpellCooldown + */ + int CanShareQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + E->Push(player->CanShareQuest(entry)); + return 1; + } + + #if 0 + int CanSpeak(Eluna* E, Player* player) + { + E->Push(player->GetSession()->CanSpeak()); + return 1; + } + #endif + /** + * Returns 'true' if the [Player] has permission to uninvite others from the current group, 'false' otherwise. + * + * @return bool canUninviteFromGroup + */ + int CanUninviteFromGroup(Eluna* E, Player* player) + { + E->Push(player->CanUninviteFromGroup() == ERR_PARTY_RESULT_OK); + return 1; + } + + /** + * Returns 'true' if the [Player] can fly, 'false' otherwise. + * + * @return bool canFly + */ + int CanFly(Eluna* E, Player* player) + { + E->Push(player->CanFly()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently in water, 'false' otherwise. + * + * @return bool isInWater + */ + int IsInWater(Eluna* E, Player* player) + { + E->Push(player->IsInWater()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently moving, 'false' otherwise. + * + * @return bool isMoving + */ + int IsMoving(Eluna* E, Player* player) // enable for unit when mangos support it + { + E->Push(player->isMoving()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently flying, 'false' otherwise. + * + * @return bool isFlying + */ + int IsFlying(Eluna* E, Player* player) // enable for unit when mangos support it + { + E->Push(player->IsFlying()); + return 1; + } + + /** + * Returns 'true' if the [Player] is in a [Group], 'false' otherwise. + * + * @return bool isInGroup + */ + int IsInGroup(Eluna* E, Player* player) + { + E->Push((player->GetGroup() != NULL)); + return 1; + } + + /** + * Returns 'true' if the [Player] is in a [Guild], 'false' otherwise. + * + * @return bool isInGuild + */ + int IsInGuild(Eluna* E, Player* player) + { + E->Push((player->GetGuildId() != 0)); + return 1; + } + + /** + * Returns 'true' if the [Player] is a Game Master, 'false' otherwise. + * + * Note: This is only true when GM tag is activated! For alternative see [Player:GetGMRank] + * + * @return bool isGM + */ + int IsGM(Eluna* E, Player* player) + { + E->Push(player->IsGameMaster()); + return 1; + } + + + #if 0 + int IsInArenaTeam(Eluna* E, Player* player) + { + uint32 type = E->CHECKVAL(2); + if (type < MAX_ARENA_SLOT && player->GetArenaTeamId(type)) + E->Push(true); + else + E->Push(false); + return 1; + } + #endif + + int IsImmuneToDamage(Eluna* E, Player* player) + { + E->Push(player->isTotalImmune()); + return 1; + } + + /** + * Returns 'true' if the [Player] satisfies all requirements to complete the quest entry. + * + * @param uint32 entry + * @return bool canComplete + */ + int CanCompleteQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + E->Push(player->CanCompleteQuest(entry)); + return 1; + } + + /** + * Returns 'true' if the [Player] satisfies all requirements to complete the repeatable quest entry. + * + * @param uint32 entry + * @return bool canComplete + */ + int CanCompleteRepeatableQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + Quest const* qInfo = sObjectMgr->GetQuestTemplate(entry); + if (qInfo) + E->Push(player->CanCompleteRepeatableQuest(qInfo)); + else + E->Push(false); + + return 1; + } + + /** + * Returns 'true' if the [Player] satisfies all requirements to turn in the quest. + * + * @param uint32 entry + * @return bool canReward + */ + int CanRewardQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + Quest const* qInfo = sObjectMgr->GetQuestTemplate(entry); + if (qInfo) + E->Push(player->CanRewardQuest(qInfo, true)); + else + E->Push(false); + + return 1; + } + + /** + * Returns 'true' if the [Player] is a part of the Horde faction, 'false' otherwise. + * + * @return bool isHorde + */ + int IsHorde(Eluna* E, Player* player) + { + E->Push((player->GetTeam() == HORDE)); + return 1; + } + + /** + * Returns 'true' if the [Player] is a part of the Alliance faction, 'false' otherwise. + * + * @return bool isAlliance + */ + int IsAlliance(Eluna* E, Player* player) + { + E->Push((player->GetTeam() == ALLIANCE)); + return 1; + } + + /** + * Returns 'true' if the [Player] is 'Do Not Disturb' flagged, 'false' otherwise. + * + * @return bool isDND + */ + int IsDND(Eluna* E, Player* player) + { + E->Push(player->isDND()); + return 1; + } + + /** + * Returns 'true' if the [Player] is 'Away From Keyboard' flagged, 'false' otherwise. + * + * @return bool isAFK + */ + int IsAFK(Eluna* E, Player* player) + { + E->Push(player->isAFK()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently falling, 'false' otherwise. + * + * @return bool isFalling + */ + int IsFalling(Eluna* E, Player* player) + { + E->Push(player->IsFalling()); + return 1; + } + + /** + * Returns whether or not the [Player]s [Group] is visible for the other specific [Player]. + * + * @param [Player] player + * @return bool isGroupVisible + */ + int IsGroupVisibleFor(Eluna* E, Player* player) + { + Player* target = E->CHECKOBJ(2); + E->Push(player->IsGroupVisibleFor(target)); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently in the same raid as another [Player] by object, 'false' otherwise. + * + * @param [Player] player + * @return bool isInSameRaidWith + */ + int IsInSameRaidWith(Eluna* E, Player* player) + { + Player* target = E->CHECKOBJ(2); + E->Push(player->IsInSameRaidWith(target)); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently in the same [Group] as another [Player] by object, 'false' otherwise. + * + * @param [Player] player + * @return bool isInSameGroupWith + */ + int IsInSameGroupWith(Eluna* E, Player* player) + { + Player* target = E->CHECKOBJ(2); + E->Push(player->IsInSameGroupWith(target)); + return 1; + } + + /** + * Returns 'true' if the [Player] is eligible for Honor or XP gain by [Unit] specified, 'false' otherwise. + * + * @param [Unit] unit + * @return bool isHonorOrXPTarget + */ + int IsHonorOrXPTarget(Eluna* E, Player* player) + { + Unit* victim = E->CHECKOBJ(2); + + E->Push(player->isHonorOrXPTarget(victim)); + return 1; + } + + /** + * Returns 'true' if the [Player] can see anoter [Player] specified by object, 'false' otherwise. + * + * @param [Player] player + * @return bool isVisibleForPlayer + */ + int IsVisibleForPlayer(Eluna* E, Player* player) + { + Player* target = E->CHECKOBJ(2); + + E->Push(player->IsVisibleGloballyFor(target)); + return 1; + } + + /** + * Returns whether or not the [Player] has GM invisibility active + * + * @return bool isGMVisible + */ + int IsGMVisible(Eluna* E, Player* player) + { + E->Push(player->isGMVisible()); + return 1; + } + + /** + * Returns 'true' if the [Player] has taxi cheat activated, 'false' otherwise. + * + * @return bool isTaxiCheater + */ + int IsTaxiCheater(Eluna* E, Player* player) + { + E->Push(player->isTaxiCheater()); + return 1; + } + + /** + * Returns whether or not the [Player] has the GM chat flag active + * + * @return bool isGMChatActive + */ + int IsGMChat(Eluna* E, Player* player) + { + E->Push(player->isGMChat()); + return 1; + } + + /** + * Returns 'true' if the [Player] is accepting whispers, 'false' otherwise. + * + * @return bool isAcceptingWhispers + */ + int IsAcceptingWhispers(Eluna* E, Player* player) + { + E->Push(player->isAcceptWhispers()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently rested, 'false' otherwise. + * + * @return bool isRested + */ + #if 0 + int IsRested(Eluna* E, Player* player) + { + E->Push(player->GetRestBonus() > 0.0f); + return 1; + } + #endif + /** + * Returns 'true' if the [Player] is currently in a [BattleGround] queue, 'false' otherwise. + * + * @return bool inBattlegroundQueue + */ + int InBattlegroundQueue(Eluna* E, Player* player) + { + E->Push(player->InBattlegroundQueue()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently in an arena, 'false' otherwise. + * + * @return bool inArena + */ + int InArena(Eluna* E, Player* player) + { + E->Push(player->InArena()); + return 1; + } + + /** + * Returns 'true' if the [Player] is currently in a [BattleGround], 'false' otherwise. + * + * @return bool inBattleGround + */ + int InBattleground(Eluna* E, Player* player) + { + E->Push(player->InBattleground()); + return 1; + } + + /** + * Returns 'true' if the [Player] can block incomming attacks, 'false' otherwise. + * + * @return bool canBlock + */ + int CanBlock(Eluna* E, Player* player) + { + E->Push(player->CanBlock()); + return 1; + } + + /** + * Returns 'true' if the [Player] can parry incomming attacks, 'false' otherwise. + * + * @return bool canParry + */ + int CanParry(Eluna* E, Player* player) + { + E->Push(player->CanParry()); + return 1; + } + + /** + * Returns whether or not the [Player] has received the reward for a specific [Quest] ID + * + * @param uint32 questId + * @return bool isQuestRewarded + */ + int HasReceivedQuestReward(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + E->Push(player->IsQuestRewarded(entry)); + return 1; + } + + /** + * Returns whether or not the [Player] is currently flagged for outdoors PvP + * + * @return bool isPvPActive + */ + int IsOutdoorPvPActive(Eluna* E, Player* player) + { + E->Push(player->IsOutdoorPvPActive()); + return 1; + } + + /** + * Returns whether or not the [Player] is currently immune to environmental damage + * + * @return bool isImmuneToEnv + */ + int IsImmuneToEnvironmentalDamage(Eluna* E, Player* player) + { + E->Push(player->IsImmuneToEnvironmentalDamage()); + return 1; + } + + /** + * Returns whether or not the [Player] is currently in a random LFG dungeon + * + * @return bool isInRandomLFG + */ + int InRandomLfgDungeon(Eluna* E, Player* player) + { + E->Push(player->inRandomLfgDungeon()); + return 1; + } + + /** + * Returns whether or not the [Player] is currently queued in LFG + * + * @return bool isUsingLFG + */ + int IsUsingLfg(Eluna* E, Player* player) + { + E->Push(player->isUsingLfg()); + return 1; + } + + #if 0 + int IsNeverVisible(Eluna* E, Player* player) + { + E->Push(player->IsNeverVisible(true)); + return 1; + } + #endif + /** + * Returns whether or not the [Player] has any pending dungeon bind + * + * @return bool hasPendingBind + */ + int HasPendingBind(Eluna* E, Player* player) + { + E->Push(player->HasPendingBind()); + return 1; + } + + /** + * Returns whether or not the [Player] is a recruiter + * + * @return bool isARecruiter + */ + int IsARecruiter(Eluna* E, Player* player) + { + E->Push(player->GetSession()->IsARecruiter()); + return 1; + } + + /** + * Returns whether or not the [Player] has been recruited + * + * @return bool isRecruited + */ + int IsRecruited(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetRecruiterId() != 0); + return 1; + } + + /** + * Returns whether or not the [Player] recruited the other [Player] + * + * @param [Player] recruit + * @return bool hasRecruited : returns 'true' if the [Player] recruited the other [Player], false otherwise + */ + int HasRecruited(Eluna* E, Player* player) + { + Player* recruit = E->CHECKOBJ(2); + E->Push(recruit->GetSession()->GetRecruiterId() == player->GetSession()->GetAccountId()); + return 1; + } + + /** + * Returns the amount of available specs the [Player] currently has + * + * @return uint8 specCount + */ + #if 0 + int GetSpecsCount(Eluna* E, Player* player) + { + E->Push(player->GetTalentGroupsCount()); + return 1; + } + #endif + /** + * Returns the [Player]s active spec ID + * + * @return uint32 specId + */ + #if 0 + int GetActiveSpec(Eluna* E, Player* player) + { + E->Push(player->GetActiveTalentGroup()); + return 1; + } + #endif + /** + * Returns the normal phase of the player instead of the actual phase possibly containing GM phase + * + * @return uint32 phasemask + */ + #if 0 + int GetPhaseMaskForSpawn(Eluna* E, Player* player) + { + E->Push(player->GetPhaseMaskForSpawn()); + return 1; + } + #endif + + #if 0 + int GetArenaPoints(Eluna* E, Player* player) + { + E->Push(player->GetArenaPoints()); + return 1; + } + #endif + #if 0 + int GetHonorPoints(Eluna* E, Player* player) + { + E->Push(player->GetHonorPoints()); + return 1; + } + #endif + + #if 0 + int GetShieldBlockValue(Eluna* E, Player* player) + { + E->Push(player->GetShieldBlockValue()); + return 1; + } + #endif + /** + * Returns the [Player]s cooldown delay by specified [Spell] ID + * + * @param uint32 spellId + * @return uint32 spellCooldownDelay + */ + int GetSpellCooldownDelay(Eluna* E, Player* player) + { + uint32 spellId = E->CHECKVAL(2); + + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) + E->Push(player->GetSpellHistory()->GetRemainingCooldown(spellInfo)); + else + E->Push(0); + + return 1; + } + + /** + * Returns the [Player]s current latency in MS + * + * @return uint32 latency + */ + int GetLatency(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetLatency()); + return 1; + } + + /** + * Returns the faction ID the [Player] is currently flagged as champion for + * + * @return uint32 championingFaction + */ + int GetChampioningFaction(Eluna* E, Player* player) + { + E->Push(player->GetChampioningFaction()); + return 1; + } + + /** + * Returns [Player]s original sub group + * + * @return uint8 subGroup + */ + int GetOriginalSubGroup(Eluna* E, Player* player) + { + E->Push(player->GetOriginalSubGroup()); + return 1; + } + + /** + * Returns [Player]s original [Group] object + * + * @return [Group] group + */ + int GetOriginalGroup(Eluna* E, Player* player) + { + E->Push(player->GetOriginalGroup()); + return 1; + } + + /** + * Returns a random Raid Member [Player] object within radius specified of [Player] + * + * @param float radius + * @return [Player] player + */ + int GetNextRandomRaidMember(Eluna* E, Player* player) + { + float radius = E->CHECKVAL(2); + + E->Push(player->GetNextRandomRaidMember(radius)); + return 1; + } + + /** + * Returns [Player]s current sub group + * + * @return uint8 subGroup + */ + int GetSubGroup(Eluna* E, Player* player) + { + E->Push(player->GetSubGroup()); + return 1; + } + + /** + * Returns [Group] invitation + * + * @return [Group] group + */ + int GetGroupInvite(Eluna* E, Player* player) + { + E->Push(player->GetGroupInvite()); + return 1; + } + + /** + * Returns rested experience bonus + * + * @param uint32 xp + * @return uint32 xpBonus + */ + #if 0 + int GetXPRestBonus(Eluna* E, Player* player) + { + uint32 xp = E->CHECKVAL(2); + + E->Push(player->GetXPRestBonus(xp)); + return 1; + } + #endif + /** + * Returns the [Player]s current [BattleGround] type ID + * + * @return [BattleGroundTypeId] typeId + */ + int GetBattlegroundTypeId(Eluna* E, Player* player) + { + E->Push(player->GetBattlegroundTypeId()); + return 1; + } + + /** + * Returns the [Player]s current [BattleGround] ID + * + * @return uint32 battleGroundId + */ + int GetBattlegroundId(Eluna* E, Player* player) + { + E->Push(player->GetBattlegroundId()); + return 1; + } + + /** + * Returns the [Player]s reputation rank of faction specified + * + * @param uint32 faction + * @return [ReputationRank] rank + */ + int GetReputationRank(Eluna* E, Player* player) + { + uint32 faction = E->CHECKVAL(2); + + E->Push(player->GetReputationRank(faction)); + return 1; + } + + /** + * Returns the [Player]s current level of intoxication + * + * @return uint16 drunkValue + */ + int GetDrunkValue(Eluna* E, Player* player) + { + E->Push(player->GetDrunkValue()); + return 1; + } + + /** + * Returns skill temporary bonus value + * + * @param uint32 skill + * @return int16 bonusVal + */ + int GetSkillTempBonusValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetSkillTempBonusValue(skill)); + return 1; + } + + /** + * Returns skill permanent bonus value + * + * @param uint32 skill + * @return int16 bonusVal + */ + int GetSkillPermBonusValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetSkillPermBonusValue(skill)); + return 1; + } + + /** + * Returns skill value without bonus' + * + * @param uint32 skill + * @return uint16 pureVal + */ + int GetPureSkillValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetPureSkillValue(skill)); + return 1; + } + + /** + * Returns base skill value + * + * @param uint32 skill + * @return uint16 baseVal + */ + int GetBaseSkillValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetBaseSkillValue(skill)); + return 1; + } + + /** + * Returns skill value + * + * @param uint32 skill + * @return uint16 val + */ + int GetSkillValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetSkillValue(skill)); + return 1; + } + + /** + * Returns max value of specified skill without bonus' + * + * @param uint32 skill + * @return uint16 pureVal + */ + int GetPureMaxSkillValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetPureMaxSkillValue(skill)); + return 1; + } + + /** + * Returns max value of specified skill + * + * @param uint32 skill + * @return uint16 val + */ + int GetMaxSkillValue(Eluna* E, Player* player) + { + uint32 skill = E->CHECKVAL(2); + + E->Push(player->GetMaxSkillValue(skill)); + return 1; + } + + /** + * Returns mana bonus from amount of intellect + * + * @return float bonus + */ + #if 0 + int GetManaBonusFromIntellect(Eluna* E, Player* player) + { + E->Push(player->GetManaBonusFromIntellect()); + return 1; + } + #endif + /** + * Returns health bonus from amount of stamina + * + * @return float bonus + */ + int GetHealthBonusFromStamina(Eluna* E, Player* player) + { + E->Push(player->GetHealthBonusFromStamina()); + return 1; + } + + /** + * Returns raid or dungeon difficulty + * + * @param bool isRaid = true : argument is TrinityCore only + * @return int32 difficulty + */ + #if 0 + int GetDifficulty(Eluna* E, Player* player) + { + bool isRaid = E->CHECKVAL(2, true); + + E->Push(player->GetDifficulty(isRaid)); + return 1; + } + #endif + /** + * Returns the [Player]s current guild rank + * + * @return uint32 guildRank + */ + int GetGuildRank(Eluna* E, Player* player) // TODO: Move to Guild Methods + { + E->Push(player->GetGuildRank()); + return 1; + } + + /** + * Returns the [Player]s free talent point amount + * + * @return uint32 freeTalentPointAmt + */ + #if 0 + int GetFreeTalentPoints(Eluna* E, Player* player) + { + E->Push(player->GetFreeTalentPoints()); + return 1; + } + #endif + + /** + * Returns the name of the [Player]s current [Guild] + * + * @return string guildName + */ + int GetGuildName(Eluna* E, Player* player) + { + if (!player->GetGuildId()) + return 1; + E->Push(eGuildMgr->GetGuildNameById(player->GetGuildId())); + return 1; + } + + /** + * Returns the amount of reputation the [Player] has with the faction specified + * + * @param uint32 faction + * @return int32 reputationAmt + */ + int GetReputation(Eluna* E, Player* player) + { + uint32 faction = E->CHECKVAL(2); + + E->Push(player->GetReputationMgr().GetReputation(faction)); + return 1; + } + + /** + * Returns [Unit] target combo points are on + * + * @return [Unit] target + */ + #if 0 + int GetComboTarget(Eluna* E, Player* player) + { + E->Push(player->GetComboTarget()); + return 1; + } + #endif + /** + * Returns [Player]'s combo points + * + * @return uint8 comboPoints + */ + int GetComboPoints(Eluna* E, Player* player) + { + E->Push(player->GetComboPoints()); + return 1; + } + + /** + * Returns the amount of time the [Player] has spent ingame + * + * @return uint32 inGameTime + */ + int GetInGameTime(Eluna* E, Player* player) + { + E->Push(player->GetInGameTime()); + return 1; + } + + /** + * Returns the status of the [Player]s [Quest] specified by entry ID + * + * @param uint32 questId + * @return [QuestStatus] questStatus + */ + int GetQuestStatus(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + E->Push(player->GetQuestStatus(entry)); + return 1; + } + + /** + * Returns 'true' if the [Player]s [Quest] specified by entry ID has been rewarded, 'false' otherwise. + * + * @param uint32 questId + * @return bool questRewardStatus + */ + int GetQuestRewardStatus(Eluna* E, Player* player) + { + uint32 questId = E->CHECKVAL(2); + + E->Push(player->GetQuestRewardStatus(questId)); + return 1; + } + + /** + * Returns [Quest] required [Creature] or [GameObject] count + * + * @param uint32 quest : entry of a quest + * @param int32 entry : entry of required [Creature] + * @return uint16 count + */ + int GetReqKillOrCastCurrentCount(Eluna* E, Player* player) + { + uint32 questId = E->CHECKVAL(2); + int32 entry = E->CHECKVAL(3); + + E->Push(player->GetReqKillOrCastCurrentCount(questId, entry)); + return 1; + } + + /** + * Returns the quest level of the [Player]s [Quest] specified by object + * + * @param uint32 questId + * @return [QuestStatus] questRewardStatus + */ + int GetQuestLevel(Eluna* E, Player* player) + { + Quest* quest = E->CHECKOBJ(2); + + E->Push(player->GetQuestLevel(quest)); + return 1; + } + + /** + * Returns a [Player]s [Item] object by gear slot specified + * + * @param uint8 slot + * @return [Item] item + */ + int GetEquippedItemBySlot(Eluna* E, Player* player) + { + uint8 slot = E->CHECKVAL(2); + if (slot >= EQUIPMENT_SLOT_END) + return 1; + + Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + E->Push(item); + return 1; + } + + /** + * Returns the [Player]s current resting bonus + * + * @return float restBonus + */ + #if 0 + int GetRestBonus(Eluna* E, Player* player) + { + E->Push(player->GetRestBonus()); + return 1; + } + #endif + /** + * Returns active GM chat tag + * + * @return uint8 tag + */ + #if 0 + int GetChatTag(Eluna* E, Player* player) + { + E->Push(player->GetChatTag()); + return 1; + } + #endif + /** + * Returns an item in given bag on given slot. + * + *
    +     * Possible and most commonly used combinations:
    +     *
    +     * bag = 255
    +     * slots 0-18 equipment
    +     * slots 19-22 equipped bag slots
    +     * slots 23-38 backpack
    +     * slots 39-66 bank main slots
    +     * slots 67-74 bank bag slots
    +     * slots 86-117 keyring
    +     *
    +     * bag = 19-22
    +     * slots 0-35 for equipped bags
    +     *
    +     * bag = 67-74
    +     * slots 0-35 for bank bags
    +     * 
    + * + * @param uint8 bag : the bag the [Item] is in, you can get this with [Item:GetBagSlot] + * @param uint8 slot : the slot the [Item] is in within the bag, you can get this with [Item:GetSlot] + * @return [Item] item : [Item] or nil + */ + int GetItemByPos(Eluna* E, Player* player) + { + uint8 bag = E->CHECKVAL(2); + uint8 slot = E->CHECKVAL(3); + + E->Push(player->GetItemByPos(bag, slot)); + return 1; + } + + /** + * Returns an [Item] from the player by guid. + * + * The item can be equipped, in bags or in bank. + * + * @param ObjectGuid guid : an item guid + * @return [Item] item + */ + int GetItemByGUID(Eluna* E, Player* player) + { + ObjectGuid guid = E->CHECKVAL(2); + + E->Push(player->GetItemByGuid(guid)); + return 1; + } + + /** + * Returns a mailed [Item] by guid. + * + * @param ObjectGuid guid : an item guid + * @return [Item] item + */ + int GetMailItem(Eluna* E, Player* player) + { + ObjectGuid guid = E->CHECKVAL(2); + + E->Push(player->GetMItem(guid.GetCounter())); + return 1; + } + + /** + * Returns an [Item] from the player by entry. + * + * The item can be equipped, in bags or in bank. + * + * @param uint32 entryId + * @return [Item] item + */ + int GetItemByEntry(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + E->Push(player->GetItemByEntry(entry)); + return 1; + } + + /** + * Returns the database textID of the [WorldObject]'s gossip header text for the [Player] + * + * @param [WorldObject] object + * @return uint32 textId : key to npc_text database table + */ + int GetGossipTextId(Eluna* E, Player* player) + { + WorldObject* obj = E->CHECKOBJ(2); + E->Push(player->GetGossipTextId(obj)); + return 1; + } + + /** + * Returns the [Player]s currently selected [Unit] object + * + * @return [Unit] unit + */ + int GetSelection(Eluna* E, Player* player) + { + E->Push(player->GetSelectedUnit()); + return 1; + } + + /** + * Returns the [Player]s GM Rank + * + * @return [AccountTypes] gmRank + */ + int GetGMRank(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetSecurity()); + return 1; + } + + /** + * Returns the [Player]s amount of money in copper + * + * @return uint32 coinage + */ + int GetCoinage(Eluna* E, Player* player) + { + E->Push(player->GetMoney()); + return 1; + } + + /** + * Returns the [Player]s current [Guild] ID + * + * @return uint32 guildId + */ + int GetGuildId(Eluna* E, Player* player) + { + E->Push(player->GetGuildId()); + return 1; + } + + /** + * Returns the [Player]s [TeamId] + * + * @return [TeamId] teamId + */ + int GetTeam(Eluna* E, Player* player) + { + E->Push(player->GetTeamId()); + return 1; + } + + /** + * Returns amount of the specified [Item] the [Player] has. + * + * @param uint32 entry : entry of the item + * @param bool checkinBank = false : also counts the items in player's bank if true + * @return uint32 itemamount + */ + int GetItemCount(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + bool checkinBank = E->CHECKVAL(3, false); + E->Push(player->GetItemCount(entry, checkinBank)); + return 1; + } + + /** + * Returns the [Player]s lifetime Honorable Kills + * + * @return uint32 lifeTimeKils + */ + int GetLifetimeKills(Eluna* E, Player* player) + { + E->Push(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS)); + return 1; + } + + /** + * Returns the [Player]s IP address + * + * @return string ip + */ + int GetPlayerIP(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetRemoteAddress()); + return 1; + } + + /** + * Returns the [Player]s time played at current level + * + * @return uint32 currLevelPlayTime + */ + int GetLevelPlayedTime(Eluna* E, Player* player) + { + E->Push(player->GetLevelPlayedTime()); + return 1; + } + + /** + * Returns the [Player]s total time played + * + * @return uint32 totalPlayTime + */ + int GetTotalPlayedTime(Eluna* E, Player* player) + { + E->Push(player->GetTotalPlayedTime()); + return 1; + } + + /** + * Returns the [Player]s [Guild] object + * + * @return [Guild] guild + */ + int GetGuild(Eluna* E, Player* player) + { + E->Push(eGuildMgr->GetGuildById(player->GetGuildId())); + return 1; + } + + /** + * Returns the [Player]s [Group] object + * + * @return [Group] group + */ + int GetGroup(Eluna* E, Player* player) + { + E->Push(player->GetGroup()); + return 1; + } + + /** + * Returns the [Player]s account ID + * + * @return uint32 accountId + */ + int GetAccountId(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetAccountId()); + return 1; + } + + /** + * Returns the [Player]s account name + * + * @return string accountName + */ + int GetAccountName(Eluna* E, Player* player) + { + std::string accName; + if (eAccountMgr->GetName(player->GetSession()->GetAccountId(), accName)) + E->Push(accName); + + return 1; + } + + /** + * Returns the [Player]s [Corpse] object + * + * @return [Corpse] corpse + */ + int GetCorpse(Eluna* E, Player* player) + { + E->Push(player->GetCorpse()); + return 1; + } + + /** + * Returns the [Player]s database locale index + * + * @return int localeIndex + */ + int GetDbLocaleIndex(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetSessionDbLocaleIndex()); + return 1; + } + + /** + * Returns the [Player]s game client locale + * + * @return [LocaleConstant] locale + */ + int GetDbcLocale(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetSessionDbcLocale()); + return 1; + } + + /** + * Returns the [Player]s recruit-a-friend recruiter account ID + * + * @return uint32 recruiterId + */ + int GetRecruiterId(Eluna* E, Player* player) + { + E->Push(player->GetSession()->GetRecruiterId()); + return 1; + } + + /** + * Returns the [Player]s selected [Player] or nil. + * + * @return [Player] selection + */ + int GetSelectedPlayer(Eluna* E, Player* player) + { + E->Push(player->GetSelectedPlayer()); + return 1; + } + + /** + * Returns the [Player]s selected [Unit]. + * + * @return [Unit] selection + */ + int GetSelectedUnit(Eluna* E, Player* player) + { + E->Push(player->GetSelectedUnit()); + return 1; + } + + /** + * Returns the closest [GameObject] to the [Player]. + * + * @return [GameObject] gameobject + */ + int GetNearbyGameObject(Eluna* E, Player* player) + { + E->Push(ChatHandler(player->GetSession()).GetNearbyGameObject()); + return 1; + } + + /** + * Returns the amount of mails in the [Player]s mailbox + * + * @return uint32 count + */ + int GetMailCount(Eluna* E, Player* player) + { + E->Push(player->GetMailSize()); + return 1; + } + + /** + * Returns the [Player]s current experience points + * + * @return uint32 xp + */ + #if 0 + int GetXP(Eluna* E, Player* player) + { + E->Push(player->GetXP()); + return 1; + } + #endif + /** + * Returns the [Player]s required experience points for next level + * + * @return uint32 xp + */ + #if 0 + int GetXPForNextLevel(Eluna* E, Player* player) + { + E->Push(player->GetXPForNextLevel()); + return 1; + } + #endif + /** + * Locks the player controls and disallows all movement and casting. + * + * @param bool apply = true : lock if true and unlock if false + */ + int SetPlayerLock(Eluna* E, Player* player) + { + bool apply = E->CHECKVAL(2, true); + + if (apply) + { + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED | UNIT_FLAG_SILENCED); + player->SetClientControl(player, 0); + } + else + { + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED | UNIT_FLAG_SILENCED); + player->SetClientControl(player, 1); + } + return 0; + } + + /** + * Sets the [Player]s login flag to the flag specified + * + * @param uint32 flag + */ + int SetAtLoginFlag(Eluna* E, Player* player) + { + uint32 flag = E->CHECKVAL(2); + + player->SetAtLoginFlag((AtLoginFlags)flag); + return 0; + } + + /** + * Sets the [Player]s sheathe state to the state specified + * + * @param uint32 sheatheState + */ + int SetSheath(Eluna* E, Player* player) + { + uint32 sheathed = E->CHECKVAL(2); + if (sheathed >= MAX_SHEATH_STATE) + return 0; + + player->SetSheath((SheathState)sheathed); + return 0; + } + + /** + * Sets the [Player]s intoxication level to the level specified + * + * @param uint8 drunkValue + */ + int SetDrunkValue(Eluna* E, Player* player) + { + uint8 newDrunkValue = E->CHECKVAL(2); + + player->SetDrunkValue(newDrunkValue); + return 0; + } + + /** + * Sets the [Player]s faction standing to that of the race specified + * + * @param uint8 raceId + */ + #if 0 + int SetFactionForRace(Eluna* E, Player* player) + { + uint8 race = E->CHECKVAL(2); + + player->SetFactionForRace(race); + return 0; + } + #endif + /** + * Sets (increases) skill of the [Player] + * + * @param uint16 id + * @param uint16 step + * @param uint16 currVal + * @param uint16 maxVal + */ + int SetSkill(Eluna* E, Player* player) + { + uint16 id = E->CHECKVAL(2); + uint16 step = E->CHECKVAL(3); + uint16 currVal = E->CHECKVAL(4); + uint16 maxVal = E->CHECKVAL(5); + + player->SetSkill(id, step, currVal, maxVal); + return 0; + } + + /** + * Sets the [Player]s guild rank to the rank specified + * + * @param uint8 rank + */ + int SetGuildRank(Eluna* E, Player* player) // TODO: Move to Guild Methods + { + uint8 rank = E->CHECKVAL(2); + + if (!player->GetGuildId()) + return 0; + + player->SetGuildRank(rank); + return 0; + } + + /** + * Sets the [Player]s free talent points to the amount specified for the current spec + * + * @param uint32 talentPointAmt + */ + #if 0 + int SetFreeTalentPoints(Eluna* E, Player* player) + { + uint32 points = E->CHECKVAL(2); + + player->SetFreeTalentPoints(points); + player->SendTalentsInfoData(false); + return 0; + } + #endif + /** + * Sets the [Player]s reputation amount for the faction specified + * + * @param uint32 factionId + * @param int32 reputationValue + */ + int SetReputation(Eluna* E, Player* player) + { + uint32 faction = E->CHECKVAL(2); + int32 value = E->CHECKVAL(3); + FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction); + + player->GetReputationMgr().SetReputation(factionEntry, value); + return 0; + } + + /** + * Sets [Quest] state + * + * @param uint32 entry : entry of a quest + * @param uint32 status + */ + int SetQuestStatus(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + uint32 status = E->CHECKVAL(3); + if (status >= MAX_QUEST_STATUS) + return 0; + + player->SetQuestStatus(entry, (QuestStatus)status); + return 0; + } + + /** + * Sets the [Player]s rest bonus to the amount specified + * + * @param float restBonus + */ + #if 0 + int SetRestBonus(Eluna* E, Player* player) + { + float bonus = E->CHECKVAL(2); + + player->SetRestBonus(bonus); + return 0; + } + #endif + /** + * Toggles whether the [Player] accepts whispers or not + * + * @param bool acceptWhispers = true + */ + int SetAcceptWhispers(Eluna* E, Player* player) + { + bool on = E->CHECKVAL(2, true); + + player->SetAcceptWhispers(on); + return 0; + } + + /** + * Toggles PvP Death + * + * @param bool on = true + */ + int SetPvPDeath(Eluna* E, Player* player) + { + bool on = E->CHECKVAL(2, true); + + player->SetPvPDeath(on); + return 0; + } + + /** + * Toggles whether the [Player] has GM visibility on or off + * + * @param bool gmVisible = true + */ + int SetGMVisible(Eluna* E, Player* player) + { + bool on = E->CHECKVAL(2, true); + + player->SetGMVisible(on); + return 0; + } + + /** + * Toggles whether the [Player] has taxi cheat enabled or not + * + * @param bool taxiCheat = true + */ + int SetTaxiCheat(Eluna* E, Player* player) + { + bool on = E->CHECKVAL(2, true); + + player->SetTaxiCheater(on); + return 0; + } + + /** + * Toggle Blizz (GM) tag + * + * @param bool on = true + */ + int SetGMChat(Eluna* E, Player* player) + { + bool on = E->CHECKVAL(2, true); + + player->SetGMChat(on); + return 0; + } + + /** + * Toggles the [Player]s GM mode on or off + * + * @param bool setGmMode = true + */ + int SetGameMaster(Eluna* E, Player* player) + { + bool on = E->CHECKVAL(2, true); + + player->SetGameMaster(on); + return 0; + } + + /** + * Sets the [Player]s gender to gender specified + * + * @table + * @columns [Gender, ID] + * @values [GENDER_MALE, 0] + * @values [GENDER_FEMALE, 1] + * + * @param [Gender] gender + */ + int SetGender(Eluna* E, Player* player) + { + uint32 _gender = E->CHECKVAL(2); + + Gender gender; + switch (_gender) + { + case 0: + gender = GENDER_MALE; + break; + case 1: + gender = GENDER_FEMALE; + break; + default: + return luaL_argerror(E->L, 2, "valid Gender expected"); + } + + player->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender); + player->SetByteValue(PLAYER_BYTES_3, 0, gender); + player->InitDisplayIds(); + return 0; + } + + + #if 0 + int SetArenaPoints(Eluna* E, Player* player) + { + uint32 arenaP = E->CHECKVAL(2); + player->SetArenaPoints(arenaP); + return 0; + } + #endif + #if 0 + int SetHonorPoints(Eluna* E, Player* player) + { + uint32 honorP = E->CHECKVAL(2); + player->SetHonorPoints(honorP); + return 0; + } + #endif + /** + * Sets the [Player]s amount of Lifetime Honorable Kills to the value specified + * + * @param uint32 honorableKills + */ + int SetLifetimeKills(Eluna* E, Player* player) + { + uint32 val = E->CHECKVAL(2); + player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, val); + return 0; + } + + /** + * Sets the [Player]s amount of money to copper specified + * + * @param uint32 copperAmt + */ + int SetCoinage(Eluna* E, Player* player) + { + using MoneyType = std::tuple_element_t<1, boost::callable_traits::args_t>; + + MoneyType amt = E->CHECKVAL(2); + player->SetMoney(amt); + return 0; + } + + /** + * Sets the [Player]s home location to the location specified + * + * @param float x : X Coordinate + * @param float y : Y Coordinate + * @param float z : Z Coordinate + * @param uint32 mapId : Map ID + * @param uint32 areaId : Area ID + */ + int SetBindPoint(Eluna* E, Player* player) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + uint32 mapId = E->CHECKVAL(5); + uint32 areaId = E->CHECKVAL(6); + + WorldLocation loc(mapId, x, y, z); + + player->SetHomebind(loc, areaId); + return 0; + } + + /** + * Adds the specified title to the [Player]s list of known titles + * + * @param uint32 titleId + */ + int SetKnownTitle(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + CharTitlesEntry const* t = sCharTitlesStore.LookupEntry(id); + if (t) + player->SetTitle(t, false); + return 0; + } + + /** + * Adds the specified achievement to the [Player]s + * + * @param uint32 achievementid + */ + int SetAchievement(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + AchievementEntry const* t = sAchievementStore.LookupEntry(id); + if (t) + player->CompletedAchievement(t); + return 0; + } + + /** + * Toggle the [Player]s FFA flag + * + * @param bool applyFFA = true + */ + #if 0 + int SetFFA(Eluna* E, Player* player) + { + bool apply = E->CHECKVAL(2, true); + + if(apply) + player->SetPvpFlag(UNIT_BYTE2_FLAG_FFA_PVP); + else + player->RemovePvpFlag(UNIT_BYTE2_FLAG_FFA_PVP); + return 0; + } + #endif + /** + * Sets the [Player]s movement to the provided movement type + * + * @table + * @columns [movementType, ID] + * @values [MOVE_ROOT, 1] + * @values [MOVE_UNROOT, 2] + * @values [MOVE_WATER_WALK, 3] + * @values [MOVE_LAND_WALK, 4] + * + * @param int32 movementType + */ + int SetMovement(Eluna* E, Player* player) + { + int32 pType = E->CHECKVAL(2); + + switch (pType) + { + case 1: // MOVE_ROOT + player->SetControlled(true, UNIT_STATE_ROOT); + break; + case 2: // MOVE_UNROOT + player->SetControlled(false, UNIT_STATE_ROOT); + break; + case 3: // MOVE_WATER_WALK + player->SetWaterWalking(true); + break; + case 4: // MOVE_LAND_WALK + player->SetWaterWalking(false); + break; + default: + break; + } + + return 0; + } + + /** + * Resets the [Player]s pets talent points + */ + #if 0 + int ResetPetTalents(Eluna* /*E*/, Player* player) + { + player->ResetPetTalents(); + player->SendTalentsInfoData(true); + return 0; + } + #endif + /** + * Reset the [Player]s completed achievements + */ + int ResetAchievements(Eluna* /*E*/, Player* player) + { + player->ResetAchievements(); + return 0; + } + + /** + * Shows the mailbox window to the player from specified guid. + * + * @param ObjectGuid guid = playerguid : guid of the mailbox window sender + */ + int SendShowMailBox(Eluna* E, Player* player) + { + ObjectGuid guid = E->CHECKVAL(2, player->GET_GUID()); + + player->GetSession()->SendShowMailBox(guid); + return 0; + } + + /** + * Adds or detracts from the [Player]s current Arena Points + * + * @param int32 amount + */ + #if 0 + int ModifyArenaPoints(Eluna* E, Player* player) + { + int32 amount = E->CHECKVAL(2); + + player->ModifyArenaPoints(amount); + return 0; + } + #endif + /** + * Adds or detracts from the [Player]s current Honor Points + * + * @param int32 amount + */ + #if 0 + int ModifyHonorPoints(Eluna* E, Player* player) + { + int32 amount = E->CHECKVAL(2); + + player->ModifyHonorPoints(amount); + return 0; + } + #endif + /** + * Saves the [Player] to the database + */ + int SaveToDB(Eluna* /*E*/, Player* player) + { + player->SaveToDB(); + return 0; + } + + /** + * Sends a summon request to the player from the given summoner + * + * @param [Unit] summoner + */ + int SummonPlayer(Eluna* E, Player* player) + { + Unit* summoner = E->CHECKOBJ(2); + + player->SendSummonRequestFrom(summoner); + return 0; + } + + /** + * Mutes the [Player] for the amount of seconds specified + * + * @param uint32 muteTime + */ + int Mute(Eluna* E, Player* player) + { + uint32 muteseconds = E->CHECKVAL(2); + + time_t muteTime = time(NULL) + muteseconds; + player->GetSession()->m_muteTime = muteTime; + LoginDatabase.PExecute("UPDATE account SET mutetime = {} WHERE id = {}", muteTime, player->GetSession()->GetAccountId()); + return 0; + } + + /** + * Rewards the given quest entry for the [Player] if he has completed it. + * + * @param uint32 entry : quest entry + */ + int RewardQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + // If player doesn't have the quest + if (!quest || player->GetQuestStatus(entry) != QUEST_STATUS_COMPLETE) + return 0; + + player->RewardQuest(quest, 0, player); + return 0; + } + + /** + * Sends an auction house window to the [Player] from the [Unit] specified + * + * @param [Unit] sender + */ + #if 0 + int SendAuctionMenu(Eluna* E, Player* player) + { + Unit* unit = E->CHECKOBJ(2); + + player->GetSession()->SendAuctionHello(unit->GET_GUID(), unit); + return 0; + } + #endif + /** + * Sends a flightmaster window to the [Player] from the [Creature] specified + * + * @param [Creature] sender + */ + int SendTaxiMenu(Eluna* E, Player* player) + { + Creature* creature = E->CHECKOBJ(2); + + player->GetSession()->SendTaxiMenu(creature); + return 0; + } + + /** + * Sends a spirit resurrection request to the [Player] + */ + int SendSpiritResurrect(Eluna* /*E*/, Player* player) + { + player->GetSession()->SendSpiritResurrect(); + return 0; + } + + /** + * Sends a tabard vendor window to the [Player] from the [WorldObject] specified + * + * @param [WorldObject] sender + */ + int SendTabardVendorActivate(Eluna* E, Player* player) + { + WorldObject* obj = E->CHECKOBJ(2); + + player->GetSession()->SendTabardVendorActivate(obj->GET_GUID()); + return 0; + } + + /** + * Sends a bank window to the [Player] from the [WorldObject] specified. + * + * @param [WorldObject] sender + */ + int SendShowBank(Eluna* E, Player* player) + { + WorldObject* obj = E->CHECKOBJ(2); + + player->GetSession()->SendShowBank(obj->GET_GUID()); + return 0; + } + + /** + * Sends a vendor window to the [Player] from the [WorldObject] specified. + * + * @param [WorldObject] sender + */ + int SendListInventory(Eluna* E, Player* player) + { + WorldObject* obj = E->CHECKOBJ(2); + + player->GetSession()->SendListInventory(obj->GET_GUID()); + return 0; + } + + /** + * Sends a trainer window to the [Player] from the [Creature] specified + * + * @param [Creature] sender + */ + + #if 0 + int SendTrainerList(Eluna* E, Player* player) + { + Creature* obj = E->CHECKOBJ(2); + + player->GetSession()->SendTrainerList(obj); + return 0; + } + #endif + /** + * Sends a guild invitation from the [Player]s [Guild] to the [Player] object specified + * + * @param [Player] invitee + */ + int SendGuildInvite(Eluna* E, Player* player) + { + Player* plr = E->CHECKOBJ(2); + + if (Guild* guild = player->GetGuild()) + guild->HandleInviteMember(player->GetSession(), plr->GetName()); + + return 0; + } + + /** + * Sends an update for the world state to the [Player] + * + * @param uint32 field + * @param uint32 value + */ + int SendUpdateWorldState(Eluna* E, Player* player) + { + uint32 field = E->CHECKVAL(2); + uint32 value = E->CHECKVAL(3); + + player->SendUpdateWorldState(field, value); + return 0; + } + + /** + * Forces the [Player] to log out + * + * @param bool saveToDb = true + */ + int LogoutPlayer(Eluna* E, Player* player) + { + bool save = E->CHECKVAL(2, true); + + player->GetSession()->LogoutPlayer(save); + return 0; + } + + /** + * Forcefully removes the [Player] from a [BattleGround] raid group + */ + int RemoveFromBattlegroundRaid(Eluna* /*E*/, Player* player) + { + player->RemoveFromBattlegroundOrBattlefieldRaid(); + return 0; + } + + /** + * Unbinds the [Player] from his instances except the one he currently is in. + * + * Difficulty is not used on classic. + * + * @param uint32 map = true + * @param uint32 difficulty = 0 + */ + #if 0 + int UnbindInstance(Eluna* E, Player* player) + { + uint32 map = E->CHECKVAL(2); + uint32 difficulty = E->CHECKVAL(3, 0); + + if (difficulty < MAX_DIFFICULTY) + player->UnbindInstance(map, (Difficulty)difficulty); + + return 0; + } + #endif + /** + * Unbinds the [Player] from his instances except the one he currently is in. + */ + #if 0 + int UnbindAllInstances(Eluna* /*E*/, Player* player) + { + for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) + { + Player::BoundInstancesMap& binds = player->GetBoundInstances(Difficulty(i)); + for (Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end();) + { + if (itr->first != player->GetMapId()) + player->UnbindInstance(itr, Difficulty(i)); + else + ++itr; + } + } + return 0; + } + #endif + /** + * Forces the [Player] to leave a [BattleGround] + * + * @param bool teleToEntry = true + */ + int LeaveBattleground(Eluna* E, Player* player) + { + bool teleToEntryPoint = E->CHECKVAL(2, true); + + player->LeaveBattleground(teleToEntryPoint); + return 0; + } + + /** + * Repairs [Item] at specified position. + * + * @param uint16 position + * @param bool cost = true + * @param float discountMod = 1.0 + */ + #if 0 + int DurabilityRepair(Eluna* E, Player* player) + { + uint16 position = E->CHECKVAL(2); + bool takeCost = E->CHECKVAL(3, true); + float discountMod = E->CHECKVAL(4, 1.0f); + + player->DurabilityRepair(position, takeCost, discountMod); + return 0; + } + #endif + /** + * Repairs all [Item]s. + * + * @param bool takeCost = true + * @param float discountMod = 1.0 + * @param bool guidBank = false + */ + #if 0 + int DurabilityRepairAll(Eluna* E, Player* player) + { + bool takeCost = E->CHECKVAL(2, true); + float discountMod = E->CHECKVAL(3, 1.0f); + bool guildBank = E->CHECKVAL(4, false); + + player->DurabilityRepairAll(takeCost, discountMod, guildBank); + return 0; + } + #endif + /** + * Sets durability loss for an [Item] in the specified slot + * + * @param int32 slot + */ + int DurabilityPointLossForEquipSlot(Eluna* E, Player* player) + { + int32 slot = E->CHECKVAL(2); + + if (slot >= EQUIPMENT_SLOT_START && slot < EQUIPMENT_SLOT_END) + player->DurabilityPointLossForEquipSlot((EquipmentSlots)slot); + return 0; + } + + /** + * Sets durability loss on all [Item]s equipped + * + * If inventory is true, sets durability loss for [Item]s in bags + * + * @param int32 points + * @param bool inventory = true + */ + int DurabilityPointsLossAll(Eluna* E, Player* player) + { + int32 points = E->CHECKVAL(2); + bool inventory = E->CHECKVAL(3, true); + + player->DurabilityPointsLossAll(points, inventory); + return 0; + } + + /** + * Sets durability loss for the specified [Item] + * + * @param [Item] item + * @param int32 points + */ + int DurabilityPointsLoss(Eluna* E, Player* player) + { + Item* item = E->CHECKOBJ(2); + int32 points = E->CHECKVAL(3); + + player->DurabilityPointsLoss(item, points); + return 0; + } + + /** + * Damages specified [Item] + * + * @param [Item] item + * @param double percent + */ + int DurabilityLoss(Eluna* E, Player* player) + { + Item* item = E->CHECKOBJ(2); + double percent = E->CHECKVAL(3); + + player->DurabilityLoss(item, percent); + return 0; + } + + /** + * Damages all [Item]s equipped. If inventory is true, damages [Item]s in bags + * + * @param double percent + * @param bool inventory = true + */ + int DurabilityLossAll(Eluna* E, Player* player) + { + double percent = E->CHECKVAL(2); + bool inventory = E->CHECKVAL(3, true); + + player->DurabilityLossAll(percent, inventory); + return 0; + } + + /** + * Kills the [Player] + */ + int KillPlayer(Eluna* /*E*/, Player* player) + { + player->KillPlayer(); + return 0; + } + + /** + * Forces the [Player] to leave a [Group] + */ + int RemoveFromGroup(Eluna* /*E*/, Player* player) + { + if (!player->GetGroup()) + return 0; + + player->RemoveFromGroup(); + return 0; + } + + /** + * Returns the [Player]s accumulated talent reset cost + * + * @return uint32 resetCost + */ + #if 0 + int ResetTalentsCost(Eluna* E, Player* player) + { + E->Push(player->ResetTalentsCost()); + return 1; + } + #endif + /** + * Resets the [Player]s talents + * + * @param bool noCost = true + */ + #if 0 + int ResetTalents(Eluna* E, Player* player) + { + bool no_cost = E->CHECKVAL(2, true); + + player->ResetTalents(no_cost); + player->SendTalentsInfoData(false); + return 0; + } + #endif + /** + * Removes the [Spell] from the [Player] + * + * @param uint32 entry : entry of a [Spell] + */ + int RemoveSpell(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + player->RemoveSpell(entry); + return 0; + } + + /** + * Clears the [Player]s combo points + */ + int ClearComboPoints(Eluna* /*E*/, Player* player) + { + player->ClearComboPoints(); + return 0; + } + + /** + * Adds combo points to the [Player] + * + * @param [Unit] target + * @param int8 count + */ + #if 0 + int AddComboPoints(Eluna* E, Player* player) + { + Unit* target = E->CHECKOBJ(2); + int8 count = E->CHECKVAL(3); + + player->AddComboPoints(target, count); + return 0; + } + #endif + /** + * Gives [Quest] monster talked to credit + * + * @param uint32 entry : entry of a [Creature] + * @param [Creature] creature + */ + int TalkedToCreature(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + Creature* creature = E->CHECKOBJ(3); + + player->TalkedToCreature(entry, creature->GET_GUID()); + return 0; + } + + /** + * Gives [Quest] monster killed credit + * + * @param uint32 entry : entry of a [Creature] + */ + int KilledMonsterCredit(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + player->KilledMonsterCredit(entry, player->GET_GUID()); + return 0; + } + + /** + * Completes a [Quest] if in a [Group] + * + * @param uint32 quest : entry of a quest + * @param [WorldObject] obj + */ + int GroupEventHappens(Eluna* E, Player* player) + { + uint32 questId = E->CHECKVAL(2); + WorldObject* obj = E->CHECKOBJ(3); + + player->GroupEventHappens(questId, obj); + return 0; + } + + /** + * Completes the [Quest] if a [Quest] area is explored, or completes the [Quest] + * + * @param uint32 quest : entry of a [Quest] + */ + int AreaExploredOrEventHappens(Eluna* E, Player* player) + { + uint32 questId = E->CHECKVAL(2); + + player->AreaExploredOrEventHappens(questId); + return 0; + } + + /** + * Sets the given [Quest] entry failed for the [Player]. + * + * @param uint32 entry : entry of a [Quest] + */ + int FailQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + player->FailQuest(entry); + return 0; + } + + /** + * Sets the given quest entry incomplete for the [Player]. + * + * @param uint32 entry : quest entry + */ + int IncompleteQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + player->IncompleteQuest(entry); + return 0; + } + + /** + * Completes the given quest entry for the [Player] and tries to satisfy all quest requirements. + * + * The player should have the quest to complete it. + * + * @param uint32 entry : quest entry + */ + int CompleteQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + // If player doesn't have the quest + if (!quest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE) + return 0; + + // Add quest items for quests that require items + #if 0 + for (uint8 x = 0; x < QUEST_ITEM_OBJECTIVES_COUNT; ++x) + { + uint32 id = quest->RequiredItemId[x]; + uint32 count = quest->RequiredItemCount[x]; + + if (!id || !count) + continue; + + uint32 curItemCount = player->GetItemCount(id, true); + + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, id, count - curItemCount); + if (msg == EQUIP_ERR_OK) + { + Item* item = player->StoreNewItem(dest, id, true); + player->SendNewItem(item, count - curItemCount, true, false); + } + } + #endif + // All creature/GO slain/cast (not required, but otherwise it will display "Creature slain 0/10") + #if 0 + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + { + int32 creature = quest->RequiredNpcOrGo[i]; + uint32 creatureCount = quest->RequiredNpcOrGoCount[i]; + + if (creature > 0) + { + if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creature)) + for (uint16 z = 0; z < creatureCount; ++z) + player->KilledMonster(creatureInfo, ObjectGuid::Empty); + } + else if (creature < 0) + for (uint16 z = 0; z < creatureCount; ++z) + player->KillCreditGO(creature); + } + #endif + + // If the quest requires reputation to complete + #if 0 + if (uint32 repFaction = quest->GetRepObjectiveFaction()) + { + uint32 repValue = quest->GetRepObjectiveValue(); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); + if (curRep < repValue) + if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry, repValue); + } + #endif + // If the quest requires a SECOND reputation to complete + #if 0 + if (uint32 repFaction = quest->GetRepObjectiveFaction2()) + { + uint32 repValue2 = quest->GetRepObjectiveValue2(); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); + if (curRep < repValue2) + if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry, repValue2); + } + #endif + // If the quest requires money + // int32 ReqOrRewMoney = quest->GetRewOrReqMoney(); + //if (ReqOrRewMoney < 0) + //player->ModifyMoney(-ReqOrRewMoney); + + if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled + { + // prepare Quest Tracker datas + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); + stmt->setUInt32(0, quest->GetQuestId()); + stmt->setUInt32(1, player->GetGUID().GetCounter()); + + // add to Quest Tracker + CharacterDatabase.Execute(stmt); + } + + player->CompleteQuest(entry); + return 0; + } + + /** + * Tries to add the given quest entry for the [Player]. + * + * @param uint32 entry : quest entry + */ + #if 0 + int AddQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + if (!quest) + return 0; + + // check item starting quest (it can work incorrectly if added without item in inventory) + ItemTemplateContainer const& itc = sObjectMgr->GetItemTemplateStore(); + auto itr = std::find_if(std::begin(itc), std::end(itc), [quest](ItemTemplateContainer::value_type const& value) + { + return value.second.StartQuest == quest->GetQuestId(); + }); + + if (itr != std::end(itc)) + return 0; + + // ok, normal (creature/GO starting) quest + if (player->CanAddQuest(quest, true)) + player->AddQuestAndCheckCompletion(quest, NULL); + + return 0; + } + #endif + /** + * Removes the given quest entry from the [Player]. + * + * @param uint32 entry : quest entry + */ + #if 0 + int RemoveQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + Quest const* quest = eObjectMgr->GetQuestTemplate(entry); + + if (!quest) + return 0; + + // remove all quest entries for 'entry' from quest log + for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) + { + uint32 logQuest = player->GetQuestSlotQuestId(slot); + if (logQuest == entry) + { + player->SetQuestSlot(slot, 0); + + // we ignore unequippable quest items in this case, its' still be equipped + player->TakeQuestSourceItem(logQuest, false); + + if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP)) + { + player->pvpInfo.IsHostile = player->pvpInfo.IsInHostileArea || player->HasPvPForcingQuest(); + player->UpdatePvPState(); + } + } + } + + player->RemoveActiveQuest(entry, false); + player->RemoveRewardedQuest(entry); + return 0; + } + #endif + /** + * Sends whisper text from the [Player] + * + * @param string text + * @param uint32 lang : language the [Player] will speak + * @param [Player] receiver : is the [Player] that will receive the whisper, if TrinityCore + * @param ObjectGuid guid : is the GUID of a [Player] that will receive the whisper, not TrinityCore + */ + int Whisper(Eluna* E, Player* player) + { + std::string text = E->CHECKVAL(2); + uint32 lang = E->CHECKVAL(3); + Player* receiver = E->CHECKOBJ(4); + + player->Whisper(text, (Language)lang, receiver); + return 0; + } + + /** + * Sends a text emote from the [Player] + * + * @param string emoteText + */ + int TextEmote(Eluna* E, Player* player) + { + std::string text = E->CHECKVAL(2); + + player->TextEmote(text); + return 0; + } + + /** + * Sends yell text from the [Player] + * + * @param string text : text for the [Player] to yells + * @param uint32 lang : language the [Player] will speak + */ + int Yell(Eluna* E, Player* player) + { + std::string text = E->CHECKVAL(2); + uint32 lang = E->CHECKVAL(3); + + player->Yell(text, (Language)lang); + return 0; + } + + /** + * Sends say text from the [Player] + * + * @param string text : text for the [Player] to say + * @param uint32 lang : language the [Player] will speak + */ + int Say(Eluna* E, Player* player) + { + std::string text = E->CHECKVAL(2); + uint32 lang = E->CHECKVAL(3); + + player->Say(text, (Language)lang); + return 0; + } + + /** + * Gives the [Player] experience + * + * @param uint32 xp : experience to give + * @param [Unit] victim = nil + */ + int GiveXP(Eluna* E, Player* player) + { + uint32 xp = E->CHECKVAL(2); + Unit* victim = E->CHECKOBJ(3, false); + + player->GiveXP(xp, victim); + return 0; + } + + /** + * Toggle the [Player]s 'Do Not Disturb' flag + */ + int ToggleDND(Eluna* /*E*/, Player* player) + { + player->ToggleDND(); + return 0; + } + + /** + * Toggle the [Player]s 'Away From Keyboard' flag + */ + int ToggleAFK(Eluna* /*E*/, Player* player) + { + player->ToggleAFK(); + return 0; + } + + /** + * Equips the given item or item entry to the given slot. Returns the equipped item or nil. + * + * @table + * @columns [EquipSlot, ID] + * @values [EQUIPMENT_SLOT_HEAD, 0] + * @values [EQUIPMENT_SLOT_NECK, 1] + * @values [EQUIPMENT_SLOT_SHOULDERS, 2] + * @values [EQUIPMENT_SLOT_BODY, 3] + * @values [EQUIPMENT_SLOT_CHEST, 4] + * @values [EQUIPMENT_SLOT_WAIST, 5] + * @values [EQUIPMENT_SLOT_LEGS, 6] + * @values [EQUIPMENT_SLOT_FEET, 7] + * @values [EQUIPMENT_SLOT_WRISTS, 8] + * @values [EQUIPMENT_SLOT_HANDS, 9] + * @values [EQUIPMENT_SLOT_FINGER1, 10] + * @values [EQUIPMENT_SLOT_FINGER2, 11] + * @values [EQUIPMENT_SLOT_TRINKET1, 12] + * @values [EQUIPMENT_SLOT_TRINKET2, 13] + * @values [EQUIPMENT_SLOT_BACK, 14] + * @values [EQUIPMENT_SLOT_MAINHAND, 15] + * @values [EQUIPMENT_SLOT_OFFHAND, 16] + * @values [EQUIPMENT_SLOT_RANGED, 17] + * @values [EQUIPMENT_SLOT_TABARD, 18] + * + * @table + * @columns [BagSlot, ID] + * @values [INVENTORY_SLOT_BAG_START, 19] + * @values [INVENTORY_SLOT_BAG_END, 23] + * + * @proto equippedItem = (item, slot) + * @proto equippedItem = (entry, slot) + * @param [Item] item : item to equip + * @param uint32 entry : entry of the item to equip + * @param uint32 slot : equipment slot to equip the item to The slot can be [EquipmentSlots] or [InventorySlots] + * @return [Item] equippedItem : item or nil if equipping failed + */ + + int EquipItem(Eluna* E, Player* player) + { + uint16 dest = 0; + Item* item = E->CHECKOBJ(2, false); + uint32 slot = E->CHECKVAL(3); + + if (slot >= INVENTORY_SLOT_BAG_END) + return 1; + + if (!item) + { + uint32 entry = E->CHECKVAL(2); + item = Item::CreateItem(entry, 1, player); + if (!item) + return 1; + + InventoryResult result = player->CanEquipItem(slot, dest, item, false); + if (result != EQUIP_ERR_OK) + { + delete item; + return 1; + } + player->ItemAddedQuestCheck(entry, 1); + //player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, entry, 1); + } + else + { + InventoryResult result = player->CanEquipItem(slot, dest, item, false); + if (result != EQUIP_ERR_OK) + return 1; + player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true); + } + + E->Push(player->EquipItem(dest, item, true)); + player->AutoUnequipOffhandIfNeed(); + return 1; + } + + /** + * Returns true if the player can equip the given [Item] or item entry to the given slot, false otherwise. + * + * @proto canEquip = (item, slot) + * @proto canEquip = (entry, slot) + * @param [Item] item : item to equip + * @param uint32 entry : entry of the item to equip + * @param uint32 slot : equipment slot to test + * @return bool canEquip + */ + int CanEquipItem(Eluna* E, Player* player) + { + Item* item = E->CHECKOBJ(2, false); + uint32 slot = E->CHECKVAL(3); + if (slot >= EQUIPMENT_SLOT_END) + { + E->Push(false); + return 1; + } + + if (!item) + { + uint32 entry = E->CHECKVAL(2); + uint16 dest; + InventoryResult msg = player->CanEquipNewItem(slot, dest, entry, false); + if (msg != EQUIP_ERR_OK) + { + E->Push(false); + return 1; + } + } + else + { + uint16 dest; + InventoryResult msg = player->CanEquipItem(slot, dest, item, false); + if (msg != EQUIP_ERR_OK) + { + E->Push(false); + return 1; + } + } + E->Push(true); + return 1; + } + + /** + * Removes a title by ID from the [Player]s list of known titles + * + * @param uint32 titleId + */ + int UnsetKnownTitle(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + CharTitlesEntry const* t = sCharTitlesStore.LookupEntry(id); + if (t) + player->SetTitle(t, true); + return 0; + } + + /** + * Advances all of the [Player]s weapon skills to the maximum amount available + */ + #if 0 + int AdvanceSkillsToMax(Eluna* /*E*/, Player* player) + { + player->UpdateWeaponsSkillsToMaxSkillsForLevel(); + return 0; + } + #endif + /** + * Advances all of the [Player]s skills to the amount specified + * + * @param uint32 skillStep + */ + int AdvanceAllSkills(Eluna* E, Player* player) + { + uint32 step = E->CHECKVAL(2); + + if (!step) + return 0; + + for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i) + { + if (SkillLineEntry const* entry = sSkillLineStore.LookupEntry(i)) + { + if (entry->CategoryID == SKILL_CATEGORY_LANGUAGES || entry->CategoryID == SKILL_CATEGORY_GENERIC) + continue; + + if (player->HasSkill(entry->ID)) + player->UpdateSkill(entry->ID, step); + } + } + + return 0; + } + + /** + * Advances a [Player]s specific skill to the amount specified + * + * @param uint32 skillId + * @param uint32 skillStep + */ + int AdvanceSkill(Eluna* E, Player* player) + { + uint32 _skillId = E->CHECKVAL(2); + uint32 _step = E->CHECKVAL(3); + if (_skillId && _step) + { + if (player->HasSkill(_skillId)) + player->UpdateSkill(_skillId, _step); + } + return 0; + } + + /** + * Teleports a [Player] to the location specified + * + * @param uint32 mappId + * @param float xCoord + * @param float yCoord + * @param float zCoord + * @param float orientation + */ + int Teleport(Eluna* E, Player* player) + { + uint32 mapId = E->CHECKVAL(2); + float x = E->CHECKVAL(3); + float y = E->CHECKVAL(4); + float z = E->CHECKVAL(5); + float o = E->CHECKVAL(6); + + + player->SaveRecallPosition(); + + E->Push(player->TeleportTo(mapId, x, y, z, o)); + return 1; + } + + /** + * Adds or detracts from the [Player]s current lifetime kill count + * + * @param int32 kills : Positive number to add, negative number to detract + */ + int AddLifetimeKills(Eluna* E, Player* player) + { + int32 val = E->CHECKVAL(2); + uint32 currentKills = player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS); + uint32 kills = static_cast((static_cast(currentKills) + val) < 0 ? 0 : currentKills + val); + + player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, kills); + return 0; + } + + /** + * Adds the given amount of the specified item entry to the player. + * + * @param uint32 entry : entry of the item to add + * @param uint32 itemCount = 1 : amount of the item to add + * @return [Item] item : the item that was added or nil + */ + int AddItem(Eluna* E, Player* player) + { + uint32 itemId = E->CHECKVAL(2); + uint32 itemCount = E->CHECKVAL(3, 1); + + uint32 noSpaceForCount = 0; + ItemPosCountVec dest; + InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, itemCount, &noSpaceForCount); + if (msg != EQUIP_ERR_OK) + itemCount -= noSpaceForCount; + + if (itemCount == 0 || dest.empty()) + return 1; + + Item* item = player->StoreNewItem(dest, itemId, true, GenerateItemRandomPropertyId(itemId)); + + if (item) + player->SendNewItem(item, itemCount, true, false); + + E->Push(item); + return 1; + } + + /** + * Removes the given amount of the specified [Item] from the player. + * + * @proto (item, itemCount) + * @proto (entry, itemCount) + * @param [Item] item : item to remove + * @param uint32 entry : entry of the item to remove + * @param uint32 itemCount = 1 : amount of the item to remove + */ + int RemoveItem(Eluna* E, Player* player) + { + Item* item = E->CHECKOBJ(2, false); + uint32 itemCount = E->CHECKVAL(3); + if (!item) + { + uint32 itemId = E->CHECKVAL(2); + player->DestroyItemCount(itemId, itemCount, true); + } + else + { + player->DestroyItemCount(item, itemCount, true); + } + return 0; + } + + /** + * Removes specified amount of lifetime kills + * + * @param uint32 val : kills to remove + */ + int RemoveLifetimeKills(Eluna* E, Player* player) + { + uint32 val = E->CHECKVAL(2); + uint32 currentKills = player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS); + if (val > currentKills) + val = currentKills; + player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, currentKills - val); + return 0; + } + + /** + * Resets cooldown of the specified spell + * + * @param uint32 spellId + * @param bool update = true + */ + int ResetSpellCooldown(Eluna* E, Player* player) + { + uint32 spellId = E->CHECKVAL(2); + bool update = E->CHECKVAL(3, true); + + player->GetSpellHistory()->ResetCooldown(spellId, update); + return 0; + } + + /** + * Resets cooldown of the specified category + * + * @param uint32 category + * @param bool update = true + */ + int ResetTypeCooldowns(Eluna* E, Player* player) + { + uint32 category = E->CHECKVAL(2); + bool update = E->CHECKVAL(3, true); + (void)update; // ensure that the variable is referenced in order to pass compiler checks + + player->GetSpellHistory()->ResetCooldowns([category](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + return spellInfo && spellInfo->GetCategory() == category; + }, update); + + return 0; + } + + /** + * Resets all of the [Player]'s cooldowns + */ + int ResetAllCooldowns(Eluna* /*E*/, Player* player) + { + player->GetSpellHistory()->ResetAllCooldowns(); + return 0; + } + + /** + * Sends a Broadcast Message to the [Player] + * + * @param string message + */ + int SendBroadcastMessage(Eluna* E, Player* player) + { + const char* message = E->CHECKVAL(2); + if (std::string(message).length() > 0) + ChatHandler(player->GetSession()).SendSysMessage(message); + return 0; + } + + /** + * Sends an Area Trigger Message to the [Player] + * + * @param string message + */ + #if 0 + int SendAreaTriggerMessage(Eluna* E, Player* player) + { + std::string msg = E->CHECKVAL(2); + if (msg.length() > 0) + player->GetSession()->SendAreaTriggerMessage("%s", msg.c_str()); + return 0; + } + #endif + /** + * Sends a Notification to the [Player] + * + * @param string message + */ + int SendNotification(Eluna* E, Player* player) + { + std::string msg = E->CHECKVAL(2); + if (msg.length() > 0) + player->GetSession()->SendNotification("%s", msg.c_str()); + return 0; + } + + /** + * Sends a [WorldPacket] to the [Player] + * + * @param [WorldPacket] packet + * @param bool selfOnly = true + */ + int SendPacket(Eluna* E, Player* player) + { + WorldPacket* data = E->CHECKOBJ(2); + bool selfOnly = E->CHECKVAL(3, true); + + if (selfOnly) + player->GetSession()->SendPacket(data); + else + player->SendMessageToSet(data, true); + + return 0; + } + + /** + * Sends addon message to the [Player] receiver + * + * @param string prefix + * @param string message + * @param [ChatMsg] channel + * @param [Player] receiver + * + */ + int SendAddonMessage(Eluna* E, Player* player) + { + std::string prefix = E->CHECKVAL(2); + std::string message = E->CHECKVAL(3); + ChatMsg channel = ChatMsg(E->CHECKVAL(4)); + Player* receiver = E->CHECKOBJ(5); + WorldPackets::Chat::Chat chat; + chat.Initialize(channel, LANG_ADDON, player, receiver, message, 0, "", LOCALE_enUS, prefix); + receiver->GetSession()->SendPacket(chat.Write()); + return 0; + } + + #if 0 + int KickPlayer(Eluna* /*E*/, Player* player) + { + player->GetSession()->KickPlayer("PlayerMethods::KickPlayer Kick the player"); + return 0; + } + #endif + /** + * Adds or subtracts from the [Player]s money in copper + * + * @param int32 copperAmt : negative to remove, positive to add + */ + int ModifyMoney(Eluna* E, Player* player) + { + using MoneyType = std::tuple_element_t<1, boost::callable_traits::args_t>; + + MoneyType amt = E->CHECKVAL(2); + + player->ModifyMoney(amt); + return 1; + } + + /** + * Teaches the [Player] the [Spell] specified by entry ID + * + * @param uint32 spellId + */ + int LearnSpell(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + + player->LearnSpell(id, false); + return 0; + } + + /** + * Learn the [Player] the talent specified by talent_id and talentRank + * + * @param uint32 talent_id + * @param uint32 talentRank + */ + #if 0 + int LearnTalent(Eluna* E, Player* player) + { + uint32 id = E->CHECKVAL(2); + uint32 rank = E->CHECKVAL(3); + + player->LearnTalent(id, rank); + player->SendTalentsInfoData(false); + return 0; + } + #endif + /** + * Remove cooldowns on spells that have less than 10 minutes of cooldown from the [Player], similarly to when you enter an arena. + */ + int RemoveArenaSpellCooldowns(Eluna* /*E*/, Player* player) + { + player->RemoveArenaSpellCooldowns(); + return 0; + } + + /** + * Resurrects the [Player]. + * + * @param float healthPercent = 100.0f + * @param bool ressSickness = false + */ + int ResurrectPlayer(Eluna* E, Player* player) + { + float percent = E->CHECKVAL(2, 100.0f); + bool sickness = E->CHECKVAL(3, false); + player->ResurrectPlayer(percent, sickness); + player->SpawnCorpseBones(); + return 0; + } + + + int GossipMenuAddItem(Eluna* E, Player* player) + { + uint32 _icon = E->CHECKVAL(2); + const char* msg = E->CHECKVAL(3); + uint32 _sender = E->CHECKVAL(4); + uint32 _intid = E->CHECKVAL(5); + bool _code = E->CHECKVAL(6, false); + const char* _promptMsg = E->CHECKVAL(7, ""); + uint32 _money = E->CHECKVAL(8, 0); + + player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, GossipOptionIcon(_icon), msg, _sender, _intid, _promptMsg, _money, _code); + return 0; + } + + + int GossipComplete(Eluna* /*E*/, Player* player) + { + player->PlayerTalkClass->SendCloseGossip(); + return 0; + } + + + int GossipSendMenu(Eluna* E, Player* player) + { + uint32 npc_text = E->CHECKVAL(2); + Object* sender = E->CHECKOBJ(3); + if (sender->GetTypeId() == TYPEID_PLAYER) + { + uint32 menu_id = E->CHECKVAL(4); + player->PlayerTalkClass->GetGossipMenu().SetMenuId(menu_id); + } + + player->PlayerTalkClass->SendGossipMenu(npc_text, sender->GET_GUID()); + return 0; + } + + + int GossipClearMenu(Eluna* /*E*/, Player* player) + { + player->PlayerTalkClass->ClearMenus(); + return 0; + } + + /** + * Attempts to start the taxi/flying to the given pathID + * + * @param uint32 pathId : pathId from DBC or [Global:AddTaxiPath] + */ + int StartTaxi(Eluna* E, Player* player) + { + uint32 pathId = E->CHECKVAL(2); + + player->ActivateTaxiPathTo(pathId); + return 0; + } + + /** + * Sends POI to the location on your map + * + * @param float x + * @param float y + * @param uint32 icon : map icon to show + * @param uint32 flags + * @param uint32 data + * @param string iconText + */ + int GossipSendPOI(Eluna* E, Player* player) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + uint32 icon = E->CHECKVAL(4); + uint32 flags = E->CHECKVAL(5); + uint32 data = E->CHECKVAL(6); + std::string iconText = E->CHECKVAL(7); + + WorldPackets::NPC::GossipPOI packet; + packet.Name = iconText; + packet.Flags = flags; + packet.Pos.Pos.Relocate(x, y); + packet.Icon = icon; + packet.Importance = data; + + player->SendDirectMessage(packet.Write()); + return 0; + } + + #if 0 + int GossipAddQuests(Eluna* E, Player* player) + { + WorldObject* source = E->CHECKOBJ(2); + + if (source->GetTypeId() == TYPEID_UNIT) + { + if (source->GetUInt32Value(UNIT_NPC_FLAGS) & UNIT_NPC_FLAG_QUESTGIVER) + player->PrepareQuestMenu(source->GET_GUID()); + } + else if (source->GetTypeId() == TYPEID_GAMEOBJECT) + { + if (source->ToGameObject()->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + player->PrepareQuestMenu(source->GET_GUID()); + } + return 0; + } + #endif + + #if 0 + int SendQuestTemplate(Eluna* E, Player* player) + { + uint32 questId = E->CHECKVAL(2); + bool activateAccept = E->CHECKVAL(3, true); + + Quest const* quest = eObjectMgr->GetQuestTemplate(questId); + if (!quest) + return 0; + + player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GET_GUID(), activateAccept); + return 0; + } + #endif + /** + * Converts [Player]'s corpse to bones + */ + int SpawnBones(Eluna* /*E*/, Player* player) + { + player->SpawnCorpseBones(); + return 0; + } + + /** + * Loots [Player]'s bones for insignia + * + * @param [Player] looter + */ + int RemovedInsignia(Eluna* E, Player* player) + { + Player* looter = E->CHECKOBJ(2); + player->RemovedInsignia(looter); + return 0; + } + + /** + * Makes the [Player] invite another player to a group. + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] invited : player to invite to group + * @return bool success : true if the player was invited to a group + */ + int GroupInvite(Eluna* E, Player* player) + { + Player* invited = E->CHECKOBJ(2); + + if (invited->GetGroup() || invited->GetGroupInvite()) + { + E->Push(false); + return 1; + } + + // Get correct existing group if any + Group* group = player->GetGroup(); + + if (group && group->isBGGroup()) + group = player->GetOriginalGroup(); + + bool success = false; + + // Try invite if group found + if (group) + success = !group->IsFull() && group->AddInvite(invited); + else + { + // Create new group if one not found + group = new Group; + success = group->AddLeaderInvite(player) && group->AddInvite(invited); + if (!success) + delete group; + } + + if (success) + { + WorldPackets::Party::PartyInvite partyInvite; + partyInvite.Initialize(player, 0, true); + invited->SendDirectMessage(partyInvite.Write()); + } + + E->Push(success); + return 1; + } + + /** + * Creates a new [Group] with the creator [Player] as leader. + * + * In multistate, this method is only available in the WORLD state + * + * @param [Player] invited : player to add to group + * @return [Group] createdGroup : the created group or nil + */ + int GroupCreate(Eluna* E, Player* player) + { + Player* invited = E->CHECKOBJ(2); + + if (player->GetGroup() || invited->GetGroup()) + return 0; + + if (player->GetGroupInvite()) + player->UninviteFromGroup(); + if (invited->GetGroupInvite()) + invited->UninviteFromGroup(); + + // Try create new group + Group* group = new Group; + if (!group->AddLeaderInvite(player)) + { + delete group; + return 0; + } + + // Forming a new group, create it + if (!group->IsCreated()) + { + group->RemoveInvite(player); + group->Create(player); + sGroupMgr->AddGroup(group); + } + + if (!group->AddMember(invited)) + return 0; + + group->BroadcastGroupUpdate(); + + E->Push(group); + return 1; + } + + /** + * Starts a cinematic for the [Player] + * + * @param uint32 CinematicSequenceId : entry of a cinematic + */ + int SendCinematicStart(Eluna* E, Player* player) + { + uint32 CinematicSequenceId = E->CHECKVAL(2); + + player->SendCinematicStart(CinematicSequenceId); + return 0; + } + + /** + * Starts a movie for the [Player] + * + * @param uint32 MovieId : entry of a movie + */ + int SendMovieStart(Eluna* E, Player* player) + { + uint32 MovieId = E->CHECKVAL(2); + + player->SendMovieStart(MovieId); + return 0; + } + + /** + * Binds the [Player] to their current instance. + */ + int BindToInstance(Eluna* /*E*/, Player* player) + { + player->BindToInstance(); + return 0; + } + + /** + * Adds a talent to the [Player] for the specified spec and learning status. + * + * @param uint32 spellId : ID of the spell for the talent + * @param uint8 spec : The spec to which the talent applies + * @param bool learning = true : Whether the talent is being learned + * @return bool success : True if the talent was added, false otherwise + */ + #if 0 + int AddTalent(Eluna* E, Player* player) + { + uint32 spellId = E->CHECKVAL(2); + uint8 spec = E->CHECKVAL(3); + bool learning = E->CHECKVAL(4, true); + + if (spec >= MAX_TALENT_GROUPS) + E->Push(false); + else + E->Push(player->AddTalent(spellId, spec, learning)); + + return 1; + } + #endif + /** + * Grants kill credit for a specific [Craeture] or [GameObject]. + * + * @param uint32 entryId : the ID of the [Creature] or [GameObject] to award credit for. + */ + int KillGOCredit(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + ObjectGuid guid = E->CHECKVAL(3, ObjectGuid::Empty); + + player->KillCreditGO(entry, guid); + return 0; + } + + /** + * Grants a player kill credit. + */ + int KilledPlayerCredit(Eluna* /*E*/, Player* player) + { + player->KilledPlayerCredit(); + return 0; + } + + /** + * Removes a quest from the rewarded quests for the [Player]. + * + * @param uint32 questId : the ID of the quest to remove. + */ + int RemoveRewardedQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + player->RemoveRewardedQuest(entry); + return 0; + } + + /** + * Removes an active quest from the [Player]. + * + * @param uint32 questId : the ID of the quest to remove. + */ + #if 0 + int RemoveActiveQuest(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + + player->RemoveActiveQuest(entry); + return 0; + } + #endif + /** + * Summons a pet for the [Player]. + * + * @table + * @columns [Summon Type, ID] + * @values [SUMMON_PET, 0] + * @values [HUNTER_PET, 1] + * + * @param uint32 entryId : the ID of the pet to summon. + * @param float x + * @param float y + * @param float z + * @param float o + * @param uint32 petType + * @param uint32 despawnTime + */ + int SummonPet(Eluna* E, Player* player) + { + uint32 entry = E->CHECKVAL(2); + float x = E->CHECKVAL(3); + float y = E->CHECKVAL(4); + float z = E->CHECKVAL(5); + float o = E->CHECKVAL(6); + uint32 petType = E->CHECKVAL(7); + uint32 despwtime = E->CHECKVAL(8); + + if (petType >= MAX_PET_TYPE) + return 0; + + player->SummonPet(entry, x, y, z, o, (PetType)petType, despwtime); + return 0; + } + + /** + * Removes the [Player]'s active pet. + * + * @table + * @columns [Save mode, ID, Comment] + * @values [PET_SAVE_AS_DELETED, -1, "Not saved"] + * @values [PET_SAVE_AS_CURRENT, 0, "In current slot (with the player)"] + * @values [PET_SAVE_FIRST_STABLE_SLOT, 1, ""] + * @values [PET_SAVE_SECOND_STABLE_SLOT, 2, ""] + * @values [PET_SAVE_THIRD_STABLE_SLOT, 3, ""] + * @values [PET_SAVE_LAST_STABLE_SLOT, 4, ""] + * + * @param int saveMode = -1 + * @param bool returnReagent = false + * + */ + int RemovePet(Eluna* E, Player* player) + { + int mode = E->CHECKVAL(2, PET_SAVE_AS_DELETED); + bool returnreagent = E->CHECKVAL(3, false); + + if (!player->GetPet()) + return 0; + + player->RemovePet(player->GetPet(), (PetSaveMode)mode, returnreagent); + return 0; + } + + /** + * Runs a command as the [Player]. + * + * @param string command : the command to run + */ + + int RunCommand(Eluna* E, Player* player) + { + const char* command = E->CHECKVAL(2); + + return 0; + } + + ElunaRegister PlayerMethods[] = + { + // Getters + { "GetSelection", &LuaPlayer::GetSelection }, + { "GetGMRank", &LuaPlayer::GetGMRank }, + { "GetGuildId", &LuaPlayer::GetGuildId }, + { "GetCoinage", &LuaPlayer::GetCoinage }, + { "GetTeam", &LuaPlayer::GetTeam }, + { "GetItemCount", &LuaPlayer::GetItemCount }, + { "GetGroup", &LuaPlayer::GetGroup }, + { "GetGuild", &LuaPlayer::GetGuild }, + { "GetAccountId", &LuaPlayer::GetAccountId }, + { "GetAccountName", &LuaPlayer::GetAccountName }, + + + { "GetLifetimeKills", &LuaPlayer::GetLifetimeKills }, + { "GetPlayerIP", &LuaPlayer::GetPlayerIP }, + { "GetLevelPlayedTime", &LuaPlayer::GetLevelPlayedTime }, + { "GetTotalPlayedTime", &LuaPlayer::GetTotalPlayedTime }, + { "GetItemByPos", &LuaPlayer::GetItemByPos }, + { "GetItemByEntry", &LuaPlayer::GetItemByEntry }, + { "GetItemByGUID", &LuaPlayer::GetItemByGUID }, + { "GetMailItem", &LuaPlayer::GetMailItem }, + { "GetReputation", &LuaPlayer::GetReputation }, + { "GetEquippedItemBySlot", &LuaPlayer::GetEquippedItemBySlot }, + { "GetQuestLevel", &LuaPlayer::GetQuestLevel }, + + + + { "GetReqKillOrCastCurrentCount", &LuaPlayer::GetReqKillOrCastCurrentCount }, + { "GetQuestStatus", &LuaPlayer::GetQuestStatus }, + { "GetInGameTime", &LuaPlayer::GetInGameTime }, + { "GetComboPoints", &LuaPlayer::GetComboPoints }, + + { "GetGuildName", &LuaPlayer::GetGuildName }, + + + + { "GetSpellCooldownDelay", &LuaPlayer::GetSpellCooldownDelay }, + { "GetGuildRank", &LuaPlayer::GetGuildRank }, + + { "GetHealthBonusFromStamina", &LuaPlayer::GetHealthBonusFromStamina }, + + { "GetMaxSkillValue", &LuaPlayer::GetMaxSkillValue }, + { "GetPureMaxSkillValue", &LuaPlayer::GetPureMaxSkillValue }, + { "GetSkillValue", &LuaPlayer::GetSkillValue }, + { "GetBaseSkillValue", &LuaPlayer::GetBaseSkillValue }, + { "GetPureSkillValue", &LuaPlayer::GetPureSkillValue }, + { "GetSkillPermBonusValue", &LuaPlayer::GetSkillPermBonusValue }, + { "GetSkillTempBonusValue", &LuaPlayer::GetSkillTempBonusValue }, + { "GetReputationRank", &LuaPlayer::GetReputationRank }, + { "GetDrunkValue", &LuaPlayer::GetDrunkValue }, + { "GetBattlegroundId", &LuaPlayer::GetBattlegroundId }, + { "GetBattlegroundTypeId", &LuaPlayer::GetBattlegroundTypeId }, + + { "GetGroupInvite", &LuaPlayer::GetGroupInvite }, + { "GetSubGroup", &LuaPlayer::GetSubGroup }, + { "GetNextRandomRaidMember", &LuaPlayer::GetNextRandomRaidMember }, + { "GetOriginalGroup", &LuaPlayer::GetOriginalGroup }, + { "GetOriginalSubGroup", &LuaPlayer::GetOriginalSubGroup }, + { "GetChampioningFaction", &LuaPlayer::GetChampioningFaction }, + { "GetLatency", &LuaPlayer::GetLatency }, + { "GetRecruiterId", &LuaPlayer::GetRecruiterId }, + { "GetDbLocaleIndex", &LuaPlayer::GetDbLocaleIndex }, + { "GetDbcLocale", &LuaPlayer::GetDbcLocale }, + { "GetCorpse", &LuaPlayer::GetCorpse }, + { "GetGossipTextId", &LuaPlayer::GetGossipTextId }, + { "GetQuestRewardStatus", &LuaPlayer::GetQuestRewardStatus }, + + { "GetMailCount", &LuaPlayer::GetMailCount }, + + + + // Setters + + { "AdvanceSkill", &LuaPlayer::AdvanceSkill }, + { "AdvanceAllSkills", &LuaPlayer::AdvanceAllSkills }, + { "AddLifetimeKills", &LuaPlayer::AddLifetimeKills }, + { "SetCoinage", &LuaPlayer::SetCoinage }, + { "SetKnownTitle", &LuaPlayer::SetKnownTitle }, + { "UnsetKnownTitle", &LuaPlayer::UnsetKnownTitle }, + { "SetBindPoint", &LuaPlayer::SetBindPoint }, + + + { "SetLifetimeKills", &LuaPlayer::SetLifetimeKills }, + { "SetGameMaster", &LuaPlayer::SetGameMaster }, + { "SetGMChat", &LuaPlayer::SetGMChat }, + { "SetTaxiCheat", &LuaPlayer::SetTaxiCheat }, + { "SetGMVisible", &LuaPlayer::SetGMVisible }, + { "SetPvPDeath", &LuaPlayer::SetPvPDeath }, + { "SetAcceptWhispers", &LuaPlayer::SetAcceptWhispers }, + + { "SetQuestStatus", &LuaPlayer::SetQuestStatus }, + { "SetReputation", &LuaPlayer::SetReputation }, + + { "SetGuildRank", &LuaPlayer::SetGuildRank }, + { "SetMovement", &LuaPlayer::SetMovement }, + { "SetSkill", &LuaPlayer::SetSkill }, + + { "SetDrunkValue", &LuaPlayer::SetDrunkValue }, + { "SetAtLoginFlag", &LuaPlayer::SetAtLoginFlag }, + { "SetPlayerLock", &LuaPlayer::SetPlayerLock }, + { "SetGender", &LuaPlayer::SetGender }, + { "SetSheath", &LuaPlayer::SetSheath }, + + + // Boolean + { "IsInGroup", &LuaPlayer::IsInGroup }, + { "IsInGuild", &LuaPlayer::IsInGuild }, + { "IsGM", &LuaPlayer::IsGM }, + { "IsImmuneToDamage", &LuaPlayer::IsImmuneToDamage }, + { "IsAlliance", &LuaPlayer::IsAlliance }, + { "IsHorde", &LuaPlayer::IsHorde }, + { "HasTitle", &LuaPlayer::HasTitle }, + { "HasItem", &LuaPlayer::HasItem }, + { "Teleport", &LuaPlayer::Teleport }, + { "AddItem", &LuaPlayer::AddItem }, + + { "CanCompleteQuest", &LuaPlayer::CanCompleteQuest }, + { "CanEquipItem", &LuaPlayer::CanEquipItem }, + { "IsFalling", &LuaPlayer::IsFalling }, + { "ToggleAFK", &LuaPlayer::ToggleAFK }, + { "ToggleDND", &LuaPlayer::ToggleDND }, + { "IsAFK", &LuaPlayer::IsAFK }, + { "IsDND", &LuaPlayer::IsDND }, + { "IsAcceptingWhispers", &LuaPlayer::IsAcceptingWhispers }, + { "IsGMChat", &LuaPlayer::IsGMChat }, + { "IsTaxiCheater", &LuaPlayer::IsTaxiCheater }, + { "IsGMVisible", &LuaPlayer::IsGMVisible }, + { "HasQuest", &LuaPlayer::HasQuest }, + { "InBattlegroundQueue", &LuaPlayer::InBattlegroundQueue }, + { "IsImmuneToEnvironmentalDamage", &LuaPlayer::IsImmuneToEnvironmentalDamage }, + + { "HasAtLoginFlag", &LuaPlayer::HasAtLoginFlag }, + { "InRandomLfgDungeon", &LuaPlayer::InRandomLfgDungeon }, + { "HasPendingBind", &LuaPlayer::HasPendingBind }, + { "HasAchieved", &LuaPlayer::HasAchieved }, + { "SetAchievement", &LuaPlayer::SetAchievement }, + { "CanUninviteFromGroup", &LuaPlayer::CanUninviteFromGroup }, + + + { "IsVisibleForPlayer", &LuaPlayer::IsVisibleForPlayer }, + { "IsUsingLfg", &LuaPlayer::IsUsingLfg }, + + + { "CanShareQuest", &LuaPlayer::CanShareQuest }, + { "HasReceivedQuestReward", &LuaPlayer::HasReceivedQuestReward }, + + { "IsInSameGroupWith", &LuaPlayer::IsInSameGroupWith }, + { "IsInSameRaidWith", &LuaPlayer::IsInSameRaidWith }, + { "IsGroupVisibleFor", &LuaPlayer::IsGroupVisibleFor }, + { "HasSkill", &LuaPlayer::HasSkill }, + { "IsHonorOrXPTarget", &LuaPlayer::IsHonorOrXPTarget }, + { "CanParry", &LuaPlayer::CanParry }, + { "CanBlock", &LuaPlayer::CanBlock }, + + { "InBattleground", &LuaPlayer::InBattleground }, + { "InArena", &LuaPlayer::InArena }, + { "IsOutdoorPvPActive", &LuaPlayer::IsOutdoorPvPActive }, + { "IsARecruiter", &LuaPlayer::IsARecruiter }, + { "CanUseItem", &LuaPlayer::CanUseItem }, + { "HasSpell", &LuaPlayer::HasSpell }, + { "HasSpellCooldown", &LuaPlayer::HasSpellCooldown }, + { "IsInWater", &LuaPlayer::IsInWater }, + { "CanFly", &LuaPlayer::CanFly }, + { "IsMoving", &LuaPlayer::IsMoving }, + { "IsFlying", &LuaPlayer::IsFlying }, + { "CanCompleteRepeatableQuest", &LuaPlayer::CanCompleteRepeatableQuest }, + { "CanRewardQuest", &LuaPlayer::CanRewardQuest }, + { "HasRecruited", &LuaPlayer::HasRecruited }, + { "IsRecruited", &LuaPlayer::IsRecruited }, + + // Gossip + { "GossipMenuAddItem", &LuaPlayer::GossipMenuAddItem }, + { "GossipMenuAddItemData", METHOD_REG_NONE }, // not implemented + { "GossipSendMenu", &LuaPlayer::GossipSendMenu }, + { "GossipComplete", &LuaPlayer::GossipComplete }, + { "GossipClearMenu", &LuaPlayer::GossipClearMenu }, + + // Other + { "SendBroadcastMessage", &LuaPlayer::SendBroadcastMessage }, + + { "SendNotification", &LuaPlayer::SendNotification }, + { "SendPacket", &LuaPlayer::SendPacket }, + { "SendAddonMessage", &LuaPlayer::SendAddonMessage }, + { "ModifyMoney", &LuaPlayer::ModifyMoney }, + { "LearnSpell", &LuaPlayer::LearnSpell }, + + { "RemoveArenaSpellCooldowns", &LuaPlayer::RemoveArenaSpellCooldowns }, + { "RemoveItem", &LuaPlayer::RemoveItem }, + { "RemoveLifetimeKills", &LuaPlayer::RemoveLifetimeKills }, + { "ResurrectPlayer", &LuaPlayer::ResurrectPlayer }, + { "EquipItem", &LuaPlayer::EquipItem }, + { "ResetSpellCooldown", &LuaPlayer::ResetSpellCooldown }, + { "ResetTypeCooldowns", &LuaPlayer::ResetTypeCooldowns }, + { "ResetAllCooldowns", &LuaPlayer::ResetAllCooldowns }, + { "GiveXP", &LuaPlayer::GiveXP }, + { "RemovePet", &LuaPlayer::RemovePet }, + { "SummonPet", &LuaPlayer::SummonPet }, + { "Say", &LuaPlayer::Say }, + { "Yell", &LuaPlayer::Yell }, + { "TextEmote", &LuaPlayer::TextEmote }, + { "Whisper", &LuaPlayer::Whisper }, + { "CompleteQuest", &LuaPlayer::CompleteQuest }, + { "IncompleteQuest", &LuaPlayer::IncompleteQuest }, + { "FailQuest", &LuaPlayer::FailQuest }, + + + + { "RemoveRewardedQuest", &LuaPlayer::RemoveRewardedQuest }, + { "AreaExploredOrEventHappens", &LuaPlayer::AreaExploredOrEventHappens }, + { "GroupEventHappens", &LuaPlayer::GroupEventHappens }, + { "KilledMonsterCredit", &LuaPlayer::KilledMonsterCredit }, + { "KilledPlayerCredit", &LuaPlayer::KilledPlayerCredit }, + { "KillGOCredit", &LuaPlayer::KillGOCredit }, + { "TalkedToCreature", &LuaPlayer::TalkedToCreature }, + + + { "ClearComboPoints", &LuaPlayer::ClearComboPoints }, + { "RemoveSpell", &LuaPlayer::RemoveSpell }, + + + + { "RemoveFromGroup", &LuaPlayer::RemoveFromGroup }, + { "KillPlayer", &LuaPlayer::KillPlayer }, + { "DurabilityLossAll", &LuaPlayer::DurabilityLossAll }, + { "DurabilityLoss", &LuaPlayer::DurabilityLoss }, + { "DurabilityPointsLoss", &LuaPlayer::DurabilityPointsLoss }, + { "DurabilityPointsLossAll", &LuaPlayer::DurabilityPointsLossAll }, + { "DurabilityPointLossForEquipSlot", &LuaPlayer::DurabilityPointLossForEquipSlot }, + + + + + { "LeaveBattleground", &LuaPlayer::LeaveBattleground }, + { "BindToInstance", &LuaPlayer::BindToInstance }, + + + { "RemoveFromBattlegroundRaid", &LuaPlayer::RemoveFromBattlegroundRaid }, + { "ResetAchievements", &LuaPlayer::ResetAchievements }, + + { "LogoutPlayer", &LuaPlayer::LogoutPlayer }, + + { "SendListInventory", &LuaPlayer::SendListInventory }, + { "SendShowBank", &LuaPlayer::SendShowBank }, + { "SendTabardVendorActivate", &LuaPlayer::SendTabardVendorActivate }, + { "SendSpiritResurrect", &LuaPlayer::SendSpiritResurrect }, + { "SendTaxiMenu", &LuaPlayer::SendTaxiMenu }, + { "SendUpdateWorldState", &LuaPlayer::SendUpdateWorldState }, + { "RewardQuest", &LuaPlayer::RewardQuest }, + + { "SendShowMailBox", &LuaPlayer::SendShowMailBox }, + { "StartTaxi", &LuaPlayer::StartTaxi }, + { "GossipSendPOI", &LuaPlayer::GossipSendPOI }, + + + { "SpawnBones", &LuaPlayer::SpawnBones }, + { "RemovedInsignia", &LuaPlayer::RemovedInsignia }, + { "SendGuildInvite", &LuaPlayer::SendGuildInvite }, + { "Mute", &LuaPlayer::Mute }, + { "SummonPlayer", &LuaPlayer::SummonPlayer }, + { "SaveToDB", &LuaPlayer::SaveToDB }, + { "GroupInvite", &LuaPlayer::GroupInvite, METHOD_REG_WORLD }, // World state method only in multistate + { "GroupCreate", &LuaPlayer::GroupCreate, METHOD_REG_WORLD }, // World state method only in multistate + { "SendCinematicStart", &LuaPlayer::SendCinematicStart }, + { "SendMovieStart", &LuaPlayer::SendMovieStart }, + { "RunCommand", &LuaPlayer::RunCommand }, + + // Not implemented methods + { "GetHonorStoredKills", METHOD_REG_NONE }, // classic only + { "GetRankPoints", METHOD_REG_NONE }, // classic only + { "GetHonorLastWeekStandingPos", METHOD_REG_NONE }, // classic only + + { "SetHonorStoredKills", METHOD_REG_NONE }, // classic only + { "SetRankPoints", METHOD_REG_NONE }, // classic only + { "SetHonorLastWeekStandingPos", METHOD_REG_NONE }, // classic only + + { "CanFlyInZone", METHOD_REG_NONE }, // not implemented + + { "UpdateHonor", METHOD_REG_NONE }, // classic only + { "ResetHonor", METHOD_REG_NONE }, // classic only + { "ClearHonorInfo", METHOD_REG_NONE }, // classic only + { "GainSpellComboPoints", METHOD_REG_NONE } // not implemented + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/QuestMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/QuestMethods.h new file mode 100644 index 0000000000..6725daa67d --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/QuestMethods.h @@ -0,0 +1,196 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef QUESTMETHODS_H +#define QUESTMETHODS_H + +/*** + * Inherits all methods from: none + */ +namespace LuaQuest +{ + /** + * Returns 'true' if the [Quest] has the specified flag, false otherwise. + * Below flags are based off of 3.3.5a. Subject to change. + * + * @table + * @columns [QuestFlags, ID, Comment] + * @values [QUEST_FLAGS_NONE, 0x0, ""] + * @values [QUEST_FLAGS_STAY_ALIVE, 0x1, "Not used currently"] + * @values [QUEST_FLAGS_PARTY_ACCEPT, 0x2, "Not used currently. If player in party, all players that can accept this quest will receive confirmation box to accept quest CMSG_QUEST_CONFIRM_ACCEPT/SMSG_QUEST_CONFIRM_ACCEPT"] + * @values [QUEST_FLAGS_EXPLORATION, 0x4, "Not used currently"] + * @values [QUEST_FLAGS_SHARABLE, 0x8, "Can be shared: Player::CanShareQuest()"] + * @values [QUEST_FLAGS_HAS_CONDITION, 0x10, "Not used currently"] + * @values [QUEST_FLAGS_HIDE_REWARD_POI, 0x20, "Not used currently: Unsure of content"] + * @values [QUEST_FLAGS_RAID, 0x40, "Not used currently"] + * @values [QUEST_FLAGS_TBC, 0x80, "Not used currently: Available if TBC expansion enabled only"] + * @values [QUEST_FLAGS_NO_MONEY_FROM_XP, 0x100, "Not used currently: Experience is not converted to gold at max level"] + * @values [QUEST_FLAGS_HIDDEN_REWARDS, 0x200, "Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))"] + * @values [QUEST_FLAGS_TRACKING, 0x400, "These quests are automatically rewarded on quest complete and they will never appear in quest log client side."] + * @values [QUEST_FLAGS_DEPRECATE_REPUTATION, 0x800, "Not used currently"] + * @values [QUEST_FLAGS_DAILY, 0x1000, "Used to know quest is Daily one"] + * @values [QUEST_FLAGS_FLAGS_PVP, 0x2000, "Having this quest in log forces PvP flag"] + * @values [QUEST_FLAGS_UNAVAILABLE, 0x4000, "Used on quests that are not generically available"] + * @values [QUEST_FLAGS_WEEKLY, 0x8000, ""] + * @values [QUEST_FLAGS_AUTOCOMPLETE, 0x10000, "auto complete"] + * @values [QUEST_FLAGS_DISPLAY_ITEM_IN_TRACKER, 0x20000, "Displays usable item in quest tracker"] + * @values [QUEST_FLAGS_OBJ_TEXT, 0x40000, "use Objective text as Complete text"] + * @values [QUEST_FLAGS_AUTO_ACCEPT, 0x80000, "The client recognizes this flag as auto-accept. However, NONE of the current quests (3.3.5a) have this flag. Maybe blizz used to use it, or will use it in the future."] + * + * @param [QuestFlags] flag : all available flags can be seen above + * @return bool hasFlag + */ + int HasFlag(Eluna* E, Quest* quest) + { + uint32 flag = E->CHECKVAL(2); + + E->Push(quest->HasFlag(flag)); + return 1; + } + + /** + * Returns 'true' if the [Quest] is a daily quest, false otherwise. + * + * @return bool isDaily + */ + int IsDaily(Eluna* E, Quest* quest) + { + E->Push(quest->IsDaily()); + return 1; + } + + /** + * Returns 'true' if the [Quest] is repeatable, false otherwise. + * + * @return bool isRepeatable + */ + int IsRepeatable(Eluna* E, Quest* quest) + { + E->Push(quest->IsRepeatable()); + return 1; + } + + /** + * Returns entry ID of the [Quest]. + * + * @return uint32 entryId + */ + int GetId(Eluna* E, Quest* quest) + { + E->Push(quest->GetQuestId()); + return 1; + } + + /** + * Returns the [Quest]'s level. + * + * @return uint32 level + */ + int GetLevel(Eluna* E, Quest* quest) + { + E->Push(quest->GetQuestLevel()); + return 1; + } + + /** + * Returns the minimum level required to pick up the [Quest]. + * + * @return uint32 minLevel + */ + int GetMinLevel(Eluna* E, Quest* quest) + { + E->Push(quest->GetMinLevel()); + return 1; + } + + /** + * Returns the next [Quest] entry ID. + * + * @return int32 entryId + */ + int GetNextQuestId(Eluna* E, Quest* quest) + { + E->Push(quest->GetNextQuestId()); + return 1; + } + + /** + * Returns the previous [Quest] entry ID. + * + * @return int32 entryId + */ + int GetPrevQuestId(Eluna* E, Quest* quest) + { + E->Push(quest->GetPrevQuestId()); + return 1; + } + + /** + * Returns the next [Quest] entry ID in the specific [Quest] chain. + * + * @return int32 entryId + */ + int GetNextQuestInChain(Eluna* E, Quest* quest) + { + E->Push(quest->GetNextQuestInChain()); + return 1; + } + + /** + * Returns the [Quest]'s flags. + * + * @return [QuestFlags] flags + */ + int GetFlags(Eluna* E, Quest* quest) + { + E->Push(quest->GetFlags()); + return 1; + } + + /** + * Returns the [Quest]'s type. + * + * TODO: Document types available. + * + * @return uint32 type + */ + int GetType(Eluna* E, Quest* quest) + { + E->Push(quest->GetType()); + return 1; + } + + /** + * Returns the maximum level where the [Quest] can still be picked up. + * + * @return uint32 maxLevel + */ + int GetMaxLevel(Eluna* E, Quest* quest) + { + E->Push(quest->GetMaxLevel()); + return 1; + } + + ElunaRegister QuestMethods[] = + { + // Getters + { "GetId", &LuaQuest::GetId }, + { "GetLevel", &LuaQuest::GetLevel }, + { "GetMaxLevel", &LuaQuest::GetMaxLevel }, + { "GetMinLevel", &LuaQuest::GetMinLevel }, + { "GetNextQuestId", &LuaQuest::GetNextQuestId }, + { "GetPrevQuestId", &LuaQuest::GetPrevQuestId }, + { "GetNextQuestInChain", &LuaQuest::GetNextQuestInChain }, + { "GetFlags", &LuaQuest::GetFlags }, + { "GetType", &LuaQuest::GetType }, + + // Boolean + { "HasFlag", &LuaQuest::HasFlag }, + { "IsDaily", &LuaQuest::IsDaily }, + { "IsRepeatable", &LuaQuest::IsRepeatable } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/SpellInfoMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/SpellInfoMethods.h new file mode 100644 index 0000000000..eb2045fa29 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/SpellInfoMethods.h @@ -0,0 +1,2587 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ +#ifndef SPELLINFO_METHODS +#define SPELLINFO_METHODS +namespace LuaSpellInfo +{ + /** + * Returns the ID of the [SpellInfo]. + * + * @return uint32 id + */ + int GetId(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Id); + return 1; + } + + /** + * Returns the dispel type of the [SpellInfo]. + * + * @return uint32 dispel + */ + int GetDispel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Dispel); + return 1; + } + + /** + * Returns the mechanic of the [SpellInfo]. + * + * @return uint32 mechanic + */ + int GetMechanic(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Mechanic); + return 1; + } + + /** + * Returns the attributes of the [SpellInfo]. + * + * @return uint32 attributes + */ + int GetAttributes(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Attributes); + return 1; + } + + /** + * Returns the first extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx + */ + int GetAttributesEx(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx); + return 1; + } + + /** + * Returns the second extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx2 + */ + int GetAttributesEx2(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx2); + return 1; + } + + /** + * Returns the third extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx3 + */ + int GetAttributesEx3(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx3); + return 1; + } + + /** + * Returns the fourth extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx4 + */ + int GetAttributesEx4(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx4); + return 1; + } + + /** + * Returns the fifth extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx5 + */ + int GetAttributesEx5(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx5); + return 1; + } + + /** + * Returns the sixth extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx6 + */ + int GetAttributesEx6(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx6); + return 1; + } + + /** + * Returns the seventh extended attributes of the [SpellInfo]. + * + * @return uint32 attributesEx7 + */ + int GetAttributesEx7(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesEx7); + return 1; + } + + /** + * Returns the custom attributes of the [SpellInfo]. + * + * @return uint32 attributesCu + */ + int GetAttributesCu(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AttributesCu); + return 1; + } + + /** + * Returns the stances bitmask of the [SpellInfo] as uint32. + * + * @return uint32 stances + */ + int GetStances(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(static_cast(spellInfo->GetSpellInfo()->Stances)); + return 1; + } + + /** + * Returns the stances not bitmask of the [SpellInfo] as uint32. + * + * @return uint32 stancesNot + */ + int GetStancesNot(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(static_cast(spellInfo->GetSpellInfo()->StancesNot)); + return 1; + } + + /** + * Returns the targets bitmask of the [SpellInfo]. + * + * @return uint32 targets + */ + int GetTargets(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Targets); + return 1; + } + + /** + * Returns the target creature type of the [SpellInfo]. + * + * @return uint32 targetCreatureType + */ + int GetTargetCreatureType(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->TargetCreatureType); + return 1; + } + + /** + * Returns the requires spell focus entry of the [SpellInfo]. + * + * @return uint32 requiresSpellFocus + */ + int GetRequiresSpellFocus(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->RequiresSpellFocus); + return 1; + } + + /** + * Returns the facing caster flags of the [SpellInfo]. + * + * @return uint32 facingCasterFlags + */ + int GetFacingCasterFlags(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->FacingCasterFlags); + return 1; + } + + /** + * Returns the caster aura state required to cast the [SpellInfo]. + * + * @return uint32 casterAuraState + */ + int GetCasterAuraState(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->CasterAuraState); + return 1; + } + + /** + * Returns the target aura state required to cast the [SpellInfo]. + * + * @return uint32 targetAuraState + */ + int GetTargetAuraState(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->TargetAuraState); + return 1; + } + + /** + * Returns the caster aura state that prevents casting the [SpellInfo]. + * + * @return uint32 casterAuraStateNot + */ + int GetCasterAuraStateNot(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ExcludeCasterAuraState); + return 1; + } + + /** + * Returns the target aura state that prevents casting the [SpellInfo]. + * + * @return uint32 targetAuraStateNot + */ + int GetTargetAuraStateNot(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ExcludeTargetAuraState); + return 1; + } + + /** + * Returns the spell ID that must be active on the caster to cast the [SpellInfo]. + * + * @return uint32 casterAuraSpell + */ + int GetCasterAuraSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->CasterAuraSpell); + return 1; + } + + /** + * Returns the spell ID that must be active on the target to cast the [SpellInfo]. + * + * @return uint32 targetAuraSpell + */ + int GetTargetAuraSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->TargetAuraSpell); + return 1; + } + + /** + * Returns the spell ID that must not be active on the caster to cast the [SpellInfo]. + * + * @return uint32 excludeCasterAuraSpell + */ + int GetExcludeCasterAuraSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ExcludeCasterAuraSpell); + return 1; + } + + /** + * Returns the spell ID that must not be active on the target to cast the [SpellInfo]. + * + * @return uint32 excludeTargetAuraSpell + */ + int GetExcludeTargetAuraSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ExcludeTargetAuraSpell); + return 1; + } + + /** + * Returns the recovery time in milliseconds of the [SpellInfo]. + * + * @return uint32 recoveryTime + */ + int GetRecoveryTime(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->RecoveryTime); + return 1; + } + + /** + * Returns the category recovery time in milliseconds of the [SpellInfo]. + * + * @return uint32 categoryRecoveryTime + */ + int GetCategoryRecoveryTime(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->CategoryRecoveryTime); + return 1; + } + + /** + * Returns the start recovery category of the [SpellInfo]. + * + * @return uint32 startRecoveryCategory + */ + int GetStartRecoveryCategory(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->StartRecoveryCategory); + return 1; + } + + /** + * Returns the start recovery time in milliseconds of the [SpellInfo]. + * + * @return uint32 startRecoveryTime + */ + int GetStartRecoveryTime(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->StartRecoveryTime); + return 1; + } + + /** + * Returns the interrupt flags of the [SpellInfo]. + * + * @return uint32 interruptFlags + */ + int GetInterruptFlags(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->InterruptFlags); + return 1; + } + + /** + * Returns the aura interrupt flags of the [SpellInfo]. + * + * @return uint32 auraInterruptFlags + */ + int GetAuraInterruptFlags(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->AuraInterruptFlags); + return 1; + } + + /** + * Returns the channel interrupt flags of the [SpellInfo]. + * + * @return uint32 channelInterruptFlags + */ + int GetChannelInterruptFlags(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ChannelInterruptFlags); + return 1; + } + + /** + * Returns the proc flags of the [SpellInfo]. + * + * @return uint32 procFlags + */ + int GetProcFlags(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ProcFlags); + return 1; + } + + /** + * Returns the proc chance of the [SpellInfo]. + * + * @return uint32 procChance + */ + int GetProcChance(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ProcChance); + return 1; + } + + /** + * Returns the proc charges of the [SpellInfo]. + * + * @return uint32 procCharges + */ + int GetProcCharges(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ProcCharges); + return 1; + } + + /** + * Returns the maximum level of the [SpellInfo]. + * + * @return uint32 maxLevel + */ + int GetMaxLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->MaxLevel); + return 1; + } + + /** + * Returns the base level required to cast the [SpellInfo]. + * + * @return uint32 baseLevel + */ + int GetBaseLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->BaseLevel); + return 1; + } + + /** + * Returns the spell level of the [SpellInfo]. + * + * @return uint32 spellLevel + */ + int GetSpellLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->SpellLevel); + return 1; + } + + /** + * Returns the power type of the [SpellInfo]. + * + * @return uint32 powerType + */ + #if 0 + int GetPowerType(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->PowerType); + return 1; + } + #endif + /** + * Returns the mana cost of the [SpellInfo]. + * + * @return uint32 manaCost + */ + #if 0 + int GetManaCost(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ManaCost); + return 1; + } + #endif + /** + * Returns the mana cost per level of the [SpellInfo]. + * + * @return uint32 manaCostPerlevel + */ + #if 0 + int GetManaCostPerlevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ManaCostPerlevel); + return 1; + } + #endif + /** + * Returns the mana per second drain of the [SpellInfo]. + * + * @return uint32 manaPerSecond + */ + #if 0 + int GetManaPerSecond(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ManaPerSecond); + return 1; + } + #endif + /** + * Returns the mana per second per level drain of the [SpellInfo]. + * + * @return uint32 manaPerSecondPerLevel + */ + #if 0 + int GetManaPerSecondPerLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ManaPerSecondPerLevel); + return 1; + } + #endif + /** + * Returns the mana cost percentage of the [SpellInfo]. + * + * @return uint32 manaCostPercentage + */ + #if 0 + int GetManaCostPercentage(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ManaCostPercentage); + return 1; + } + #endif + /** + * Returns the rune cost ID of the [SpellInfo]. + * + * @return uint32 runeCostID + */ + int GetRuneCostID(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->RuneCostID); + return 1; + } + + /** + * Returns the projectile speed of the [SpellInfo]. + * + * @return float speed + */ + int GetSpeed(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Speed); + return 1; + } + + /** + * Returns the stack amount of the [SpellInfo]. + * + * @return uint32 stackAmount + */ + int GetStackAmount(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->StackAmount); + return 1; + } + + /** + * Returns the totem entry at the given index for the [SpellInfo]. + * + * @param uint32 index : the totem index (0-1) + * @return uint32 totem + */ + int GetTotem(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 index = E->CHECKVAL(2); + if (index >= 2) + return luaL_argerror(E->L, 2, "index out of range (0-1)"); + E->Push(spellInfo->GetSpellInfo()->Totem[index]); + return 1; + } + + /** + * Returns the reagent entry at the given index for the [SpellInfo]. + * + * @param uint32 index : the reagent index (0-MAX_SPELL_REAGENTS-1) + * @return int32 reagent + */ + int GetReagent(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 index = E->CHECKVAL(2); + if (index >= MAX_SPELL_REAGENTS) + return luaL_argerror(E->L, 2, "index out of range (0-MAX_SPELL_REAGENTS-1)"); + E->Push(spellInfo->GetSpellInfo()->Reagent[index]); + return 1; + } + + /** + * Returns the reagent count at the given index for the [SpellInfo]. + * + * @param uint32 index : the reagent index (0-MAX_SPELL_REAGENTS-1) + * @return uint32 reagentCount + */ + int GetReagentCount(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 index = E->CHECKVAL(2); + if (index >= MAX_SPELL_REAGENTS) + return luaL_argerror(E->L, 2, "index out of range (0-MAX_SPELL_REAGENTS-1)"); + E->Push(spellInfo->GetSpellInfo()->ReagentCount[index]); + return 1; + } + + /** + * Returns the required equipped item class of the [SpellInfo]. + * + * @return int32 equippedItemClass + */ + int GetEquippedItemClass(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->EquippedItemClass); + return 1; + } + + /** + * Returns the required equipped item subclass mask of the [SpellInfo]. + * + * @return int32 equippedItemSubClassMask + */ + int GetEquippedItemSubClassMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->EquippedItemSubClassMask); + return 1; + } + + /** + * Returns the required equipped item inventory type mask of the [SpellInfo]. + * + * @return int32 equippedItemInventoryTypeMask + */ + int GetEquippedItemInventoryTypeMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->EquippedItemInventoryTypeMask); + return 1; + } + + /** + * Returns the totem category entry at the given index for the [SpellInfo]. + * + * @param uint32 index : the totem category index (0-1) + * @return uint32 totemCategory + */ + int GetTotemCategory(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 index = E->CHECKVAL(2); + if (index >= 2) + return luaL_argerror(E->L, 2, "index out of range (0-1)"); + E->Push(spellInfo->GetSpellInfo()->TotemCategory[index]); + return 1; + } + + /** + * Returns the spell visual ID at the given index for the [SpellInfo]. + * + * @param uint32 index : the visual index (0-1) + * @return uint32 spellVisual + */ + int GetSpellVisual(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 index = E->CHECKVAL(2); + if (index >= 2) + return luaL_argerror(E->L, 2, "index out of range (0-1)"); + E->Push(spellInfo->GetSpellInfo()->SpellVisual[index]); + return 1; + } + + /** + * Returns the spell icon ID of the [SpellInfo]. + * + * @return uint32 spellIconID + */ + int GetSpellIconID(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->SpellIconID); + return 1; + } + + /** + * Returns the active icon ID of the [SpellInfo]. + * + * @return uint32 activeIconID + */ + int GetActiveIconID(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->ActiveIconID); + return 1; + } + + /** + * Returns the priority of the [SpellInfo]. + * + * @return uint32 priority + */ + int GetPriority(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->Priority); + return 1; + } + + /** + * Returns the maximum target level of the [SpellInfo]. + * + * @return uint32 maxTargetLevel + */ + int GetMaxTargetLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->MaxTargetLevel); + return 1; + } + + /** + * Returns the maximum number of affected targets of the [SpellInfo]. + * + * @return uint32 maxAffectedTargets + */ + int GetMaxAffectedTargets(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->MaxAffectedTargets); + return 1; + } + + /** + * Returns the spell family name of the [SpellInfo]. + * + * @return uint32 spellFamilyName + */ + int GetSpellFamilyName(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->SpellFamilyName); + return 1; + } + + /** + * Returns the spell family flags of the [SpellInfo] at the given index. + * The flags are a 96-bit value split into three uint32 components (index 0-2). + * + * @param uint32 index : the flag index (0-2) + * @return uint32 spellFamilyFlags + */ + int GetSpellFamilyFlags(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 index = E->CHECKVAL(2); + if (index >= 3) + return luaL_argerror(E->L, 2, "index out of range (0-2)"); + E->Push(spellInfo->GetSpellInfo()->SpellFamilyFlags[index]); + return 1; + } + + /** + * Returns the damage class of the [SpellInfo]. + * + * @return uint32 dmgClass + */ + int GetDmgClass(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->DmgClass); + return 1; + } + + /** + * Returns the prevention type of the [SpellInfo]. + * + * @return uint32 preventionType + */ + int GetPreventionType(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->PreventionType); + return 1; + } + + /** + * Returns the area group ID of the [SpellInfo]. + * + * @return int32 areaGroupId + */ + int GetAreaGroupId(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->RequiredAreasID); + return 1; + } + + /** + * Returns the school mask of the [SpellInfo]. + * + * @return uint32 schoolMask + */ + int GetSchoolMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->SchoolMask); + return 1; + } + + /** + * Returns the duration in milliseconds of the [SpellInfo]. + * + * @return int32 duration + */ + int GetDuration(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetDuration()); + return 1; + } + + /** + * Returns the maximum duration in milliseconds of the [SpellInfo]. + * + * @return int32 maxDuration + */ + int GetMaxDuration(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetMaxDuration()); + return 1; + } + + /** + * Returns the maximum range of the [SpellInfo]. + * + * @return float maxRange + */ + int GetMaxRange(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetMaxRange()); + return 1; + } + + /** + * Returns the minimum range of the [SpellInfo]. + * + * @return float minRange + */ + int GetMinRange(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetMinRange()); + return 1; + } + + /** + * Returns the maximum number of ticks of the [SpellInfo]. + * + * @return uint32 maxTicks + */ + int GetMaxTicks(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetMaxTicks()); + return 1; + } + + /** + * Returns the category of the [SpellInfo]. + * + * @return uint32 category + */ + int GetCategory(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetCategory()); + return 1; + } + + /** + * Returns the rank of the [SpellInfo]. + * + * @return uint8 rank + */ + int GetRank(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetRank()); + return 1; + } + + /** + * Returns a bitmask of all mechanics used across all effects of the [SpellInfo]. + * + * @return uint32 mechanicMask + */ + int GetAllEffectsMechanicMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetAllEffectsMechanicMask()); + return 1; + } + + /** + * Returns the allowed mechanic mask of the [SpellInfo]. + * + * @return uint32 allowedMechanicMask + */ + int GetAllowedMechanicMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetAllowedMechanicMask()); + return 1; + } + + /** + * Returns the explicit target mask of the [SpellInfo]. + * + * @return uint32 explicitTargetMask + */ + int GetExplicitTargetMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetExplicitTargetMask()); + return 1; + } + + /** + * Returns the aura state of the [SpellInfo]. + * + * @return uint32 auraState + */ + int GetAuraState(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetAuraState()); + return 1; + } + + /** + * Returns the spell specific type of the [SpellInfo]. + * + * @return uint32 spellSpecific + */ + int GetSpellSpecific(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetSpellSpecific()); + return 1; + } + + /** + * Returns the weapon attack type of the [SpellInfo]. + * + * @return uint32 attackType + */ + int GetAttackType(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->GetAttackType()); + return 1; + } + + /** + * Returns the mechanic mask for the given effect mask of the [SpellInfo]. + * + * @param uint32 effectMask : bitmask of effect indices to check + * @return uint32 mechanicMask + */ + int GetSpellMechanicMaskByEffectMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effectMask = E->CHECKVAL(2); + E->Push(spellInfo->GetSpellInfo()->GetSpellMechanicMaskByEffectMask(effectMask)); + return 1; + } + + /** + * Returns the mechanic mask for the given effect index of the [SpellInfo]. + * + * @param uint32 effIndex : the effect index (0-MAX_SPELL_EFFECTS-1) + * @return uint32 mechanicMask + */ + int GetEffectMechanicMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffectMechanicMask(static_cast(effIndex))); + return 1; + } + + /** + * Returns the mechanic for the given effect index of the [SpellInfo]. + * + * @param uint32 effIndex : the effect index (0-MAX_SPELL_EFFECTS-1) + * @return uint32 mechanic + */ + int GetEffectMechanic(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffectMechanic(static_cast(effIndex))); + return 1; + } + + /** + * Returns the diminishing returns group for the [SpellInfo]. + * + * @param bool triggered : whether the spell is triggered + * @return uint32 diminishingGroup + */ + int GetDiminishingReturnsGroupForSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + bool triggered = E->CHECKVAL(2); + E->Push(spellInfo->GetSpellInfo()->GetDiminishingReturnsGroupForSpell(triggered)); + return 1; + } + + /** + * Returns the diminishing returns group type for the [SpellInfo]. + * + * @param bool triggered : whether the spell is triggered + * @return uint32 diminishingGroupType + */ + int GetDiminishingReturnsGroupType(Eluna* E, ElunaSpellInfo* spellInfo) + { + bool triggered = E->CHECKVAL(2); + E->Push(spellInfo->GetSpellInfo()->GetDiminishingReturnsGroupType(triggered)); + return 1; + } + + /** + * Returns the diminishing returns max level for the [SpellInfo]. + * + * @param bool triggered : whether the spell is triggered + * @return uint32 diminishingMaxLevel + */ + int GetDiminishingReturnsMaxLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + bool triggered = E->CHECKVAL(2); + E->Push(spellInfo->GetSpellInfo()->GetDiminishingReturnsMaxLevel(triggered)); + return 1; + } + + /** + * Returns the diminishing returns limit duration in milliseconds for the [SpellInfo]. + * + * @param bool triggered : whether the spell is triggered + * @return int32 diminishingLimitDuration + */ + int GetDiminishingReturnsLimitDuration(Eluna* E, ElunaSpellInfo* spellInfo) + { + bool triggered = E->CHECKVAL(2); + E->Push(spellInfo->GetSpellInfo()->GetDiminishingReturnsLimitDuration(triggered)); + return 1; + } + + /** + * Returns the calculated cast time in milliseconds of the [SpellInfo]. + * + * @return uint32 castTime + */ + int CalcCastTime(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->CalcCastTime()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a passive spell, `false` otherwise. + * + * @return bool isPassive + */ + int IsPassive(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsPassive()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is autocastable, `false` otherwise. + * + * @return bool isAutocastable + */ + int IsAutocastable(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsAutocastable()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is stackable with ranks, `false` otherwise. + * + * @return bool isStackableWithRanks + */ + int IsStackableWithRanks(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsStackableWithRanks()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a passive spell stackable with ranks, `false` otherwise. + * + * @return bool isPassiveStackableWithRanks + */ + int IsPassiveStackableWithRanks(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsPassiveStackableWithRanks()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] occupies multiple aura slots, `false` otherwise. + * + * @return bool isMultiSlotAura + */ + int IsMultiSlotAura(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsMultiSlotAura()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] can stack on one slot from different casters, `false` otherwise. + * + * @return bool isStackableOnOneSlotWithDifferentCasters + */ + int IsStackableOnOneSlotWithDifferentCasters(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsStackableOnOneSlotWithDifferentCasters()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] cooldown is started on event, `false` otherwise. + * + * @return bool isCooldownStartedOnEvent + */ + int IsCooldownStartedOnEvent(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsCooldownStartedOnEvent()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] persists through death, `false` otherwise. + * + * @return bool isDeathPersistent + */ + int IsDeathPersistent(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsDeathPersistent()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] requires the target to be dead, `false` otherwise. + * + * @return bool isRequiringDeadTarget + */ + int IsRequiringDeadTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsRequiringDeadTarget()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] allows targeting dead units, `false` otherwise. + * + * @return bool isAllowingDeadTarget + */ + int IsAllowingDeadTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsAllowingDeadTarget()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a group buff, `false` otherwise. + * + * @return bool isGroupBuff + */ + int IsGroupBuff(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsGroupBuff()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] can be used in combat, `false` otherwise. + * + * @return bool canBeUsedInCombat + */ + int CanBeUsedInCombat(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->CanBeUsedInCombat()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a positive spell, `false` otherwise. + * + * @return bool isPositive + */ + #if 0 + int IsPositive(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsPositive()); + return 1; + } + #endif + /** + * Returns `true` if the effect at the given index of the [SpellInfo] is positive, `false` otherwise. + * + * @param uint8 effIndex : the effect index (0-MAX_SPELL_EFFECTS-1) + * @return bool isPositiveEffect + */ + #if 0 + int IsPositiveEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint8 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->IsPositiveEffect(effIndex)); + return 1; + } + #endif + /** + * Returns `true` if the [SpellInfo] is a channeled spell, `false` otherwise. + * + * @return bool isChanneled + */ + int IsChanneled(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsChanneled()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] allows movement while channeling, `false` otherwise. + * + * @return bool isMoveAllowedChannel + */ + int IsMoveAllowedChannel(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsMoveAllowedChannel()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] requires combo points, `false` otherwise. + * + * @return bool needsComboPoints + */ + int NeedsComboPoints(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->NeedsComboPoints()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a next melee swing spell, `false` otherwise. + * + * @return bool isNextMeleeSwingSpell + */ + int IsNextMeleeSwingSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsNextMeleeSwingSpell()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] breaks stealth, `false` otherwise. + * + * @return bool isBreakingStealth + */ + int IsBreakingStealth(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsBreakingStealth()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a ranged weapon spell, `false` otherwise. + * + * @return bool isRangedWeaponSpell + */ + int IsRangedWeaponSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsRangedWeaponSpell()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is an auto repeat ranged spell, `false` otherwise. + * + * @return bool isAutoRepeatRangedSpell + */ + int IsAutoRepeatRangedSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsAutoRepeatRangedSpell()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] has initial aggro, `false` otherwise. + * + * @return bool hasInitialAggro + */ + int HasInitialAggro(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->HasInitialAggro()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is ranked, `false` otherwise. + * + * @return bool isRanked + */ + int IsRanked(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsRanked()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] affects an area, `false` otherwise. + * + * @return bool isAffectingArea + */ + int IsAffectingArea(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsAffectingArea()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] targets an area, `false` otherwise. + * + * @return bool isTargetingArea + */ + int IsTargetingArea(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsTargetingArea()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] needs an explicit unit target, `false` otherwise. + * + * @return bool needsExplicitUnitTarget + */ + int NeedsExplicitUnitTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->NeedsExplicitUnitTarget()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a self cast spell, `false` otherwise. + * + * @return bool isSelfCast + */ + int IsSelfCast(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsSelfCast()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] can only have a single target active at a time, `false` otherwise. + * + * @return bool isSingleTarget + */ + int IsSingleTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsSingleTarget()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is an explicit discovery spell, `false` otherwise. + * + * @return bool isExplicitDiscovery + */ + int IsExplicitDiscovery(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsExplicitDiscovery()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a loot crafting spell, `false` otherwise. + * + * @return bool isLootCrafting + */ + int IsLootCrafting(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsLootCrafting()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a profession or riding spell, `false` otherwise. + * + * @return bool isProfessionOrRiding + */ + int IsProfessionOrRiding(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsProfessionOrRiding()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a profession spell, `false` otherwise. + * + * @return bool isProfession + */ + int IsProfession(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsProfession()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a primary profession spell, `false` otherwise. + * + * @return bool isPrimaryProfession + */ + int IsPrimaryProfession(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsPrimaryProfession()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is the first rank of a primary profession spell, `false` otherwise. + * + * @return bool isPrimaryProfessionFirstRank + */ + int IsPrimaryProfessionFirstRank(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsPrimaryProfessionFirstRank()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is an ability learned with a profession, `false` otherwise. + * + * @return bool isAbilityLearnedWithProfession + */ + int IsAbilityLearnedWithProfession(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsAbilityLearnedWithProfession()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is affected by spell mods, `false` otherwise. + * + * @return bool isAffectedBySpellMods + */ + int IsAffectedBySpellMods(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->IsAffectedBySpellMods()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] has the given spell effect, `false` otherwise. + * + * @param uint32 effect : the [SpellEffects] to check for + * @return bool hasEffect + */ + int HasEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + SpellEffects effect = static_cast(E->CHECKVAL(2)); + E->Push(spellInfo->GetSpellInfo()->HasEffect(effect)); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] has the given aura type, `false` otherwise. + * + * @param uint32 aura : the [AuraType] to check for + * @return bool hasAura + */ + int HasAura(Eluna* E, ElunaSpellInfo* spellInfo) + { + AuraType aura = static_cast(E->CHECKVAL(2)); + E->Push(spellInfo->GetSpellInfo()->HasAura(aura)); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] has an area aura effect, `false` otherwise. + * + * @return bool hasAreaAuraEffect + */ + int HasAreaAuraEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->HasAreaAuraEffect()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] only has damage effects, `false` otherwise. + * + * @return bool hasOnlyDamageEffects + */ + int HasOnlyDamageEffects(Eluna* E, ElunaSpellInfo* spellInfo) + { + E->Push(spellInfo->GetSpellInfo()->HasOnlyDamageEffects()); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is affected by the given spell family and flags, `false` otherwise. + * The flags are a 96-bit value split into three uint32 components. + * + * @param uint32 familyName : the spell family name to check + * @param uint32 flag0 : the first 32 bits of the spell family flags + * @param uint32 flag1 : the second 32 bits of the spell family flags + * @param uint32 flag2 : the third 32 bits of the spell family flags + * @return bool isAffected + */ + int IsAffected(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 familyName = E->CHECKVAL(2); + uint32 f0 = E->CHECKVAL(3); + uint32 f1 = E->CHECKVAL(4); + uint32 f2 = E->CHECKVAL(5); + E->Push(spellInfo->GetSpellInfo()->IsAffected(familyName, flag96(f0, f1, f2))); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is affected by the given [SpellModifier], `false` otherwise. + * + * @param [SpellModifier] mod : the spell modifier to check + * @return bool isAffectedBySpellMod + */ + int IsAffectedBySpellMod(Eluna* E, ElunaSpellInfo* spellInfo) + { + SpellModifier* mod = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsAffectedBySpellMod(mod)); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is the same rank as the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] spellInfo : the spell info to compare against + * @return bool isRankOf + */ + int IsRankOf(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsRankOf(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a different rank of the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] spellInfo : the spell info to compare against + * @return bool isDifferentRankOf + */ + int IsDifferentRankOf(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsDifferentRankOf(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is a higher rank than the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] spellInfo : the spell info to compare against + * @return bool isHighRankOf + */ + int IsHighRankOf(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsHighRankOf(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] can pierce the immunity provided by the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] auraSpellInfo : the aura spell info to check against + * @return bool canPierceImmuneAura + */ + int CanPierceImmuneAura(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->CanPierceImmuneAura(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] can dispel the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] auraSpellInfo : the aura spell info to check against + * @return bool canDispelAura + */ + int CanDispelAura(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->CanDispelAura(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] can provide immunity against the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] auraSpellInfo : the aura spell info to check against + * @return bool canSpellProvideImmunityAgainstAura + */ + int CanSpellProvideImmunityAgainstAura(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->CanSpellProvideImmunityAgainstAura(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is exclusively stacking with the given [SpellInfo] by specific aura, `false` otherwise. + * + * @param [SpellInfo] spellInfo : the spell info to compare against + * @return bool isAuraExclusiveBySpecificWith + */ + int IsAuraExclusiveBySpecificWith(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsAuraExclusiveBySpecificWith(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is exclusively stacking per caster with the given [SpellInfo] by specific aura, `false` otherwise. + * + * @param [SpellInfo] spellInfo : the spell info to compare against + * @return bool isAuraExclusiveBySpecificPerCasterWith + */ + int IsAuraExclusiveBySpecificPerCasterWith(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* other = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsAuraExclusiveBySpecificPerCasterWith(other->GetSpellInfo())); + return 1; + } + + /** + * Returns `true` if the given [Item] fits the requirements of the [SpellInfo], `false` otherwise. + * + * @param [Item] item : the item to check + * @return bool isItemFitToSpellRequirements + */ + int IsItemFitToSpellRequirements(Eluna* E, ElunaSpellInfo* spellInfo) + { + Item* item = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->IsItemFitToSpellRequirements(item)); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] is an ability of the given skill type, `false` otherwise. + * + * @param uint32 skillType : the skill type to check + * @return bool isAbilityOfSkillType + */ + int IsAbilityOfSkillType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 skillType = E->CHECKVAL(2); + E->Push(spellInfo->GetSpellInfo()->IsAbilityOfSkillType(skillType)); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] needs to be triggered by the given triggering [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] triggeringSpell : the triggering spell info to check against + * @return bool needsToBeTriggeredByCaster + */ + int NeedsToBeTriggeredByCaster(Eluna* E, ElunaSpellInfo* spellInfo) + { + ElunaSpellInfo* triggeringSpell = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->NeedsToBeTriggeredByCaster(triggeringSpell->GetSpellInfo())); + return 1; + } + + /** + * Checks whether the [SpellInfo] can be cast on the given target by the given caster. + * Returns the [SpellCastResult] of the check. + * + * @param [WorldObject] caster : the caster to check from + * @param [WorldObject] target : the target to check against + * @return uint32 spellCastResult + */ + int CheckTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + WorldObject* caster = E->CHECKOBJ(2); + WorldObject* target = E->CHECKOBJ(3); + E->Push(spellInfo->GetSpellInfo()->CheckTarget(caster, target)); + return 1; + } + + /** + * Returns `true` if the given [Unit] matches the required creature type for the [SpellInfo], `false` otherwise. + * + * @param [Unit] target : the unit to check + * @return bool checkTargetCreatureType + */ + int CheckTargetCreatureType(Eluna* E, ElunaSpellInfo* spellInfo) + { + Unit* target = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->CheckTargetCreatureType(target)); + return 1; + } + + /** + * Returns `true` if the [SpellInfo] cancels the given [AuraEffect], `false` otherwise. + * + * @param [AuraEffect] aurEff : the aura effect to check against + * @return bool spellCancelsAuraEffect + */ + int SpellCancelsAuraEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + AuraEffect* aurEff = E->CHECKOBJ(2); + E->Push(spellInfo->GetSpellInfo()->SpellCancelsAuraEffect(aurEff)); + return 1; + } + + /** + * Returns the effect index of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 effectIndex + */ + int GetEffectIndex(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).EffectIndex)); + return 1; + } + + /** + * Returns the effect type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 effectType + */ + int GetEffectType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).Effect)); + return 1; + } + + /** + * Returns the aura type applied by the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 applyAuraName + */ + int GetEffectApplyAuraName(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).ApplyAuraName)); + return 1; + } + + /** + * Returns the amplitude in milliseconds of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 amplitude + */ + int GetEffectAmplitude(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).Amplitude); + return 1; + } + + /** + * Returns the die sides of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return int32 dieSides + */ + int GetEffectDieSides(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).DieSides); + return 1; + } + + /** + * Returns the real points per level of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float realPointsPerLevel + */ + int GetEffectRealPointsPerLevel(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).RealPointsPerLevel); + return 1; + } + + /** + * Returns the base points of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return int32 basePoints + */ + int GetEffectBasePoints(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).BasePoints); + return 1; + } + + /** + * Returns the points per combo point of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float pointsPerComboPoint + */ + int GetEffectPointsPerComboPoint(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).PointsPerComboPoint); + return 1; + } + + /** + * Returns the value multiplier of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float valueMultiplier + */ + int GetEffectValueMultiplier(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).Amplitude); + return 1; + } + + /** + * Returns the damage multiplier of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float damageMultiplier + */ + int GetEffectDamageMultiplier(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).ChainAmplitude); + return 1; + } + + /** + * Returns the bonus multiplier of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float bonusMultiplier + */ + int GetEffectBonusMultiplier(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).BonusCoefficient); + return 1; + } + + /** + * Returns the misc value of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return int32 miscValue + */ + int GetEffectMiscValue(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).MiscValue); + return 1; + } + + /** + * Returns the secondary misc value of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return int32 miscValueB + */ + int GetEffectMiscValueB(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).MiscValueB); + return 1; + } + + /** + * Returns the chain target count of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 chainTarget + */ + int GetEffectChainTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).ChainTargets); + return 1; + } + + /** + * Returns the item type entry of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 itemType + */ + int GetEffectItemType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).ItemType); + return 1; + } + + /** + * Returns the trigger spell ID of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 triggerSpell + */ + int GetEffectTriggerSpell(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TriggerSpell); + return 1; + } + + /** + * Returns the spell class mask component at the given mask index for the given effect slot of the [SpellInfo]. + * The mask is a 96-bit value split into three uint32 components (index 0-2). + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @param uint32 maskIndex : the mask component index (0-2) + * @return uint32 spellClassMask + */ + int GetEffectSpellClassMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + uint32 maskIndex = E->CHECKVAL(3); + if (maskIndex >= 3) + return luaL_argerror(E->L, 3, "mask index out of range (0-2)"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).SpellClassMask[maskIndex]); + return 1; + } + + /** + * Returns the calculated value for the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return int32 calcValue + */ + int GetEffectCalcValue(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).CalcValue()); + return 1; + } + + /** + * Returns the calculated radius for the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float calcRadius + */ + int GetEffectCalcRadius(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).CalcRadius()); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] has a radius, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool hasRadius + */ + int GetEffectHasRadius(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).HasRadius()); + return 1; + } + + /** + * Returns the provided target mask for the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 providedTargetMask + */ + int GetEffectProvidedTargetMask(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).GetProvidedTargetMask()); + return 1; + } + + /** + * Returns the implicit target type for the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 implicitTargetType + */ + int GetEffectImplicitTargetType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).GetImplicitTargetType())); + return 1; + } + + /** + * Returns the used target object type for the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 usedTargetObjectType + */ + int GetEffectUsedTargetObjectType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).GetUsedTargetObjectType())); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] is an effect, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isEffect + */ + int EffectIsEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsEffect()); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] is the given effect type, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @param uint32 effect : the [SpellEffects] to check for + * @return bool isEffectType + */ + int EffectIsEffectType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + SpellEffects effect = static_cast(E->CHECKVAL(3)); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsEffect(effect)); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] applies an aura, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isAura + */ + int EffectIsAura(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsAura()); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] applies the given aura type, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @param uint32 aura : the [AuraType] to check for + * @return bool isAuraType + */ + int EffectIsAuraType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + AuraType aura = static_cast(E->CHECKVAL(3)); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsAura(aura)); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] is targeting an area, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isTargetingArea + */ + int EffectIsTargetingArea(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsTargetingArea()); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] is an area aura effect, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isAreaAuraEffect + */ + int EffectIsAreaAuraEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsAreaAuraEffect()); + return 1; + } + + /** + * Returns `true` if the given effect slot of the [SpellInfo] is a unit owned aura effect, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isUnitOwnedAuraEffect + */ + int EffectIsUnitOwnedAuraEffect(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).IsUnitOwnedAuraEffect()); + return 1; + } + + /** + * Returns the target A type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 targetA + */ + int GetEffectTargetATarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.GetTarget())); + return 1; + } + + /** + * Returns the target A selection category of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 selectionCategory + */ + int GetEffectTargetASelectionCategory(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.GetSelectionCategory())); + return 1; + } + + /** + * Returns the target A reference type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 referenceType + */ + int GetEffectTargetAReferenceType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.GetReferenceType())); + return 1; + } + + /** + * Returns the target A object type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 objectType + */ + int GetEffectTargetAObjectType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.GetObjectType())); + return 1; + } + + /** + * Returns the target A check type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 checkType + */ + int GetEffectTargetACheckType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.GetCheckType())); + return 1; + } + + /** + * Returns the target A direction type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 directionType + */ + int GetEffectTargetADirectionType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.GetDirectionType())); + return 1; + } + + /** + * Returns `true` if target A of the given effect slot of the [SpellInfo] is an area target, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isArea + */ + int GetEffectTargetAIsArea(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.IsArea()); + return 1; + } + + /** + * Returns the calculated direction angle for target A of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float directionAngle + */ + int GetEffectTargetADirectionAngle(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetA.CalcDirectionAngle()); + return 1; + } + + /** + * Returns the target B type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 targetB + */ + int GetEffectTargetBTarget(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.GetTarget())); + return 1; + } + + /** + * Returns the target B selection category of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 selectionCategory + */ + int GetEffectTargetBSelectionCategory(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.GetSelectionCategory())); + return 1; + } + + /** + * Returns the target B reference type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 referenceType + */ + int GetEffectTargetBReferenceType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.GetReferenceType())); + return 1; + } + + /** + * Returns the target B object type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 objectType + */ + int GetEffectTargetBObjectType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.GetObjectType())); + return 1; + } + + /** + * Returns the target B check type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 checkType + */ + int GetEffectTargetBCheckType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.GetCheckType())); + return 1; + } + + /** + * Returns the target B direction type of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return uint32 directionType + */ + int GetEffectTargetBDirectionType(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(static_cast(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.GetDirectionType())); + return 1; + } + + /** + * Returns `true` if target B of the given effect slot of the [SpellInfo] is an area target, `false` otherwise. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return bool isArea + */ + int GetEffectTargetBIsArea(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.IsArea()); + return 1; + } + + /** + * Returns the calculated direction angle for target B of the given effect slot of the [SpellInfo]. + * + * @param uint32 effIndex : the effect slot (0-MAX_SPELL_EFFECTS-1) + * @return float directionAngle + */ + int GetEffectTargetBDirectionAngle(Eluna* E, ElunaSpellInfo* spellInfo) + { + uint32 effIndex = E->CHECKVAL(2); + if (effIndex >= MAX_SPELL_EFFECTS) + return luaL_argerror(E->L, 2, "effect index out of range"); + E->Push(spellInfo->GetSpellInfo()->GetEffect(static_cast(effIndex)).TargetB.CalcDirectionAngle()); + return 1; + } + + + ElunaRegister SpellInfoMethods[] = + { + { "GetId", &LuaSpellInfo::GetId }, + { "GetDispel", &LuaSpellInfo::GetDispel }, + { "GetMechanic", &LuaSpellInfo::GetMechanic }, + { "GetAttributes", &LuaSpellInfo::GetAttributes }, + { "GetAttributesEx", &LuaSpellInfo::GetAttributesEx }, + { "GetAttributesEx2", &LuaSpellInfo::GetAttributesEx2 }, + { "GetAttributesEx3", &LuaSpellInfo::GetAttributesEx3 }, + { "GetAttributesEx4", &LuaSpellInfo::GetAttributesEx4 }, + { "GetAttributesEx5", &LuaSpellInfo::GetAttributesEx5 }, + { "GetAttributesEx6", &LuaSpellInfo::GetAttributesEx6 }, + { "GetAttributesEx7", &LuaSpellInfo::GetAttributesEx7 }, + { "GetAttributesCu", &LuaSpellInfo::GetAttributesCu }, + { "GetStances", &LuaSpellInfo::GetStances }, + { "GetStancesNot", &LuaSpellInfo::GetStancesNot }, + { "GetTargets", &LuaSpellInfo::GetTargets }, + { "GetTargetCreatureType", &LuaSpellInfo::GetTargetCreatureType }, + { "GetRequiresSpellFocus", &LuaSpellInfo::GetRequiresSpellFocus }, + { "GetFacingCasterFlags", &LuaSpellInfo::GetFacingCasterFlags }, + { "GetCasterAuraState", &LuaSpellInfo::GetCasterAuraState }, + { "GetTargetAuraState", &LuaSpellInfo::GetTargetAuraState }, + { "GetCasterAuraStateNot", &LuaSpellInfo::GetCasterAuraStateNot }, + { "GetTargetAuraStateNot", &LuaSpellInfo::GetTargetAuraStateNot }, + { "GetCasterAuraSpell", &LuaSpellInfo::GetCasterAuraSpell }, + { "GetTargetAuraSpell", &LuaSpellInfo::GetTargetAuraSpell }, + { "GetExcludeCasterAuraSpell", &LuaSpellInfo::GetExcludeCasterAuraSpell }, + { "GetExcludeTargetAuraSpell", &LuaSpellInfo::GetExcludeTargetAuraSpell }, + { "GetRecoveryTime", &LuaSpellInfo::GetRecoveryTime }, + { "GetCategoryRecoveryTime", &LuaSpellInfo::GetCategoryRecoveryTime }, + { "GetStartRecoveryCategory", &LuaSpellInfo::GetStartRecoveryCategory }, + { "GetStartRecoveryTime", &LuaSpellInfo::GetStartRecoveryTime }, + { "GetInterruptFlags", &LuaSpellInfo::GetInterruptFlags }, + { "GetAuraInterruptFlags", &LuaSpellInfo::GetAuraInterruptFlags }, + { "GetChannelInterruptFlags", &LuaSpellInfo::GetChannelInterruptFlags }, + { "GetProcFlags", &LuaSpellInfo::GetProcFlags }, + { "GetProcChance", &LuaSpellInfo::GetProcChance }, + { "GetProcCharges", &LuaSpellInfo::GetProcCharges }, + { "GetMaxLevel", &LuaSpellInfo::GetMaxLevel }, + { "GetBaseLevel", &LuaSpellInfo::GetBaseLevel }, + { "GetSpellLevel", &LuaSpellInfo::GetSpellLevel }, + + + + + { "GetRuneCostID", &LuaSpellInfo::GetRuneCostID }, + { "GetSpeed", &LuaSpellInfo::GetSpeed }, + { "GetStackAmount", &LuaSpellInfo::GetStackAmount }, + { "GetTotem", &LuaSpellInfo::GetTotem }, + { "GetReagent", &LuaSpellInfo::GetReagent }, + { "GetReagentCount", &LuaSpellInfo::GetReagentCount }, + { "GetEquippedItemClass", &LuaSpellInfo::GetEquippedItemClass }, + { "GetEquippedItemSubClassMask", &LuaSpellInfo::GetEquippedItemSubClassMask }, + { "GetEquippedItemInventoryTypeMask", &LuaSpellInfo::GetEquippedItemInventoryTypeMask }, + { "GetTotemCategory", &LuaSpellInfo::GetTotemCategory }, + { "GetSpellVisual", &LuaSpellInfo::GetSpellVisual }, + { "GetSpellIconID", &LuaSpellInfo::GetSpellIconID }, + { "GetActiveIconID", &LuaSpellInfo::GetActiveIconID }, + { "GetPriority", &LuaSpellInfo::GetPriority }, + { "GetMaxTargetLevel", &LuaSpellInfo::GetMaxTargetLevel }, + { "GetMaxAffectedTargets", &LuaSpellInfo::GetMaxAffectedTargets }, + { "GetSpellFamilyName", &LuaSpellInfo::GetSpellFamilyName }, + { "GetSpellFamilyFlags", &LuaSpellInfo::GetSpellFamilyFlags }, + { "GetDmgClass", &LuaSpellInfo::GetDmgClass }, + { "GetPreventionType", &LuaSpellInfo::GetPreventionType }, + { "GetAreaGroupId", &LuaSpellInfo::GetAreaGroupId }, + { "GetSchoolMask", &LuaSpellInfo::GetSchoolMask }, + { "GetDuration", &LuaSpellInfo::GetDuration }, + { "GetMaxDuration", &LuaSpellInfo::GetMaxDuration }, + { "GetMaxRange", &LuaSpellInfo::GetMaxRange }, + { "GetMinRange", &LuaSpellInfo::GetMinRange }, + { "GetMaxTicks", &LuaSpellInfo::GetMaxTicks }, + { "GetCategory", &LuaSpellInfo::GetCategory }, + { "GetRank", &LuaSpellInfo::GetRank }, + { "GetAllEffectsMechanicMask", &LuaSpellInfo::GetAllEffectsMechanicMask }, + { "GetAllowedMechanicMask", &LuaSpellInfo::GetAllowedMechanicMask }, + { "GetExplicitTargetMask", &LuaSpellInfo::GetExplicitTargetMask }, + { "GetAuraState", &LuaSpellInfo::GetAuraState }, + { "GetSpellSpecific", &LuaSpellInfo::GetSpellSpecific }, + { "GetAttackType", &LuaSpellInfo::GetAttackType }, + { "GetEffectMechanicMask", &LuaSpellInfo::GetEffectMechanicMask }, + { "GetEffectMechanic", &LuaSpellInfo::GetEffectMechanic }, + { "GetSpellMechanicMaskByEffectMask", &LuaSpellInfo::GetSpellMechanicMaskByEffectMask }, + { "GetDiminishingReturnsGroupForSpell", &LuaSpellInfo::GetDiminishingReturnsGroupForSpell }, + { "GetDiminishingReturnsGroupType", &LuaSpellInfo::GetDiminishingReturnsGroupType }, + { "GetDiminishingReturnsMaxLevel", &LuaSpellInfo::GetDiminishingReturnsMaxLevel }, + { "GetDiminishingReturnsLimitDuration", &LuaSpellInfo::GetDiminishingReturnsLimitDuration }, + { "CalcCastTime", &LuaSpellInfo::CalcCastTime }, + { "IsPassive", &LuaSpellInfo::IsPassive }, + { "IsAutocastable", &LuaSpellInfo::IsAutocastable }, + { "IsStackableWithRanks", &LuaSpellInfo::IsStackableWithRanks }, + { "IsPassiveStackableWithRanks", &LuaSpellInfo::IsPassiveStackableWithRanks }, + { "IsMultiSlotAura", &LuaSpellInfo::IsMultiSlotAura }, + { "IsStackableOnOneSlotWithDifferentCasters", &LuaSpellInfo::IsStackableOnOneSlotWithDifferentCasters }, + { "IsCooldownStartedOnEvent", &LuaSpellInfo::IsCooldownStartedOnEvent }, + { "IsDeathPersistent", &LuaSpellInfo::IsDeathPersistent }, + { "IsRequiringDeadTarget", &LuaSpellInfo::IsRequiringDeadTarget }, + { "IsAllowingDeadTarget", &LuaSpellInfo::IsAllowingDeadTarget }, + { "IsGroupBuff", &LuaSpellInfo::IsGroupBuff }, + { "CanBeUsedInCombat", &LuaSpellInfo::CanBeUsedInCombat }, + + { "IsChanneled", &LuaSpellInfo::IsChanneled }, + { "IsMoveAllowedChannel", &LuaSpellInfo::IsMoveAllowedChannel }, + { "NeedsComboPoints", &LuaSpellInfo::NeedsComboPoints }, + { "IsNextMeleeSwingSpell", &LuaSpellInfo::IsNextMeleeSwingSpell }, + { "IsBreakingStealth", &LuaSpellInfo::IsBreakingStealth }, + { "IsRangedWeaponSpell", &LuaSpellInfo::IsRangedWeaponSpell }, + { "IsAutoRepeatRangedSpell", &LuaSpellInfo::IsAutoRepeatRangedSpell }, + { "HasInitialAggro", &LuaSpellInfo::HasInitialAggro }, + { "IsRanked", &LuaSpellInfo::IsRanked }, + { "IsAffectingArea", &LuaSpellInfo::IsAffectingArea }, + { "IsTargetingArea", &LuaSpellInfo::IsTargetingArea }, + { "NeedsExplicitUnitTarget", &LuaSpellInfo::NeedsExplicitUnitTarget }, + { "IsSelfCast", &LuaSpellInfo::IsSelfCast }, + { "IsSingleTarget", &LuaSpellInfo::IsSingleTarget }, + { "IsExplicitDiscovery", &LuaSpellInfo::IsExplicitDiscovery }, + { "IsLootCrafting", &LuaSpellInfo::IsLootCrafting }, + { "IsProfessionOrRiding", &LuaSpellInfo::IsProfessionOrRiding }, + { "IsProfession", &LuaSpellInfo::IsProfession }, + { "IsPrimaryProfession", &LuaSpellInfo::IsPrimaryProfession }, + { "IsPrimaryProfessionFirstRank", &LuaSpellInfo::IsPrimaryProfessionFirstRank }, + { "IsAbilityLearnedWithProfession", &LuaSpellInfo::IsAbilityLearnedWithProfession }, + { "IsAffectedBySpellMods", &LuaSpellInfo::IsAffectedBySpellMods }, + { "HasAreaAuraEffect", &LuaSpellInfo::HasAreaAuraEffect }, + { "HasOnlyDamageEffects", &LuaSpellInfo::HasOnlyDamageEffects }, + + { "HasEffect", &LuaSpellInfo::HasEffect }, + { "HasAura", &LuaSpellInfo::HasAura }, + { "IsAffected", &LuaSpellInfo::IsAffected }, + { "IsAffectedBySpellMod", &LuaSpellInfo::IsAffectedBySpellMod }, + { "IsRankOf", &LuaSpellInfo::IsRankOf }, + { "IsDifferentRankOf", &LuaSpellInfo::IsDifferentRankOf }, + { "IsHighRankOf", &LuaSpellInfo::IsHighRankOf }, + { "CanPierceImmuneAura", &LuaSpellInfo::CanPierceImmuneAura }, + { "CanDispelAura", &LuaSpellInfo::CanDispelAura }, + { "CanSpellProvideImmunityAgainstAura", &LuaSpellInfo::CanSpellProvideImmunityAgainstAura }, + { "IsAuraExclusiveBySpecificWith", &LuaSpellInfo::IsAuraExclusiveBySpecificWith }, + { "IsAuraExclusiveBySpecificPerCasterWith", &LuaSpellInfo::IsAuraExclusiveBySpecificPerCasterWith }, + { "IsItemFitToSpellRequirements", &LuaSpellInfo::IsItemFitToSpellRequirements }, + { "IsAbilityOfSkillType", &LuaSpellInfo::IsAbilityOfSkillType }, + { "NeedsToBeTriggeredByCaster", &LuaSpellInfo::NeedsToBeTriggeredByCaster }, + { "CheckTarget", &LuaSpellInfo::CheckTarget }, + { "CheckTargetCreatureType", &LuaSpellInfo::CheckTargetCreatureType }, + { "SpellCancelsAuraEffect", &LuaSpellInfo::SpellCancelsAuraEffect }, + { "GetEffectIndex", &LuaSpellInfo::GetEffectIndex }, + { "GetEffectType", &LuaSpellInfo::GetEffectType }, + { "GetEffectApplyAuraName", &LuaSpellInfo::GetEffectApplyAuraName }, + { "GetEffectAmplitude", &LuaSpellInfo::GetEffectAmplitude }, + { "GetEffectDieSides", &LuaSpellInfo::GetEffectDieSides }, + { "GetEffectRealPointsPerLevel", &LuaSpellInfo::GetEffectRealPointsPerLevel }, + { "GetEffectBasePoints", &LuaSpellInfo::GetEffectBasePoints }, + { "GetEffectPointsPerComboPoint", &LuaSpellInfo::GetEffectPointsPerComboPoint }, + { "GetEffectValueMultiplier", &LuaSpellInfo::GetEffectValueMultiplier }, + { "GetEffectDamageMultiplier", &LuaSpellInfo::GetEffectDamageMultiplier }, + { "GetEffectBonusMultiplier", &LuaSpellInfo::GetEffectBonusMultiplier }, + { "GetEffectMiscValue", &LuaSpellInfo::GetEffectMiscValue }, + { "GetEffectMiscValueB", &LuaSpellInfo::GetEffectMiscValueB }, + { "GetEffectChainTarget", &LuaSpellInfo::GetEffectChainTarget }, + { "GetEffectItemType", &LuaSpellInfo::GetEffectItemType }, + { "GetEffectTriggerSpell", &LuaSpellInfo::GetEffectTriggerSpell }, + { "GetEffectSpellClassMask", &LuaSpellInfo::GetEffectSpellClassMask }, + { "GetEffectCalcValue", &LuaSpellInfo::GetEffectCalcValue }, + { "GetEffectCalcRadius", &LuaSpellInfo::GetEffectCalcRadius }, + { "GetEffectHasRadius", &LuaSpellInfo::GetEffectHasRadius }, + { "GetEffectProvidedTargetMask", &LuaSpellInfo::GetEffectProvidedTargetMask }, + { "GetEffectImplicitTargetType", &LuaSpellInfo::GetEffectImplicitTargetType }, + { "GetEffectUsedTargetObjectType", &LuaSpellInfo::GetEffectUsedTargetObjectType }, + { "EffectIsEffect", &LuaSpellInfo::EffectIsEffect }, + { "EffectIsEffectType", &LuaSpellInfo::EffectIsEffectType }, + { "EffectIsAura", &LuaSpellInfo::EffectIsAura }, + { "EffectIsAuraType", &LuaSpellInfo::EffectIsAuraType }, + { "EffectIsTargetingArea", &LuaSpellInfo::EffectIsTargetingArea }, + { "EffectIsAreaAuraEffect", &LuaSpellInfo::EffectIsAreaAuraEffect }, + { "EffectIsUnitOwnedAuraEffect", &LuaSpellInfo::EffectIsUnitOwnedAuraEffect }, + { "GetEffectTargetATarget", &LuaSpellInfo::GetEffectTargetATarget }, + { "GetEffectTargetASelectionCategory", &LuaSpellInfo::GetEffectTargetASelectionCategory }, + { "GetEffectTargetAReferenceType", &LuaSpellInfo::GetEffectTargetAReferenceType }, + { "GetEffectTargetAObjectType", &LuaSpellInfo::GetEffectTargetAObjectType }, + { "GetEffectTargetACheckType", &LuaSpellInfo::GetEffectTargetACheckType }, + { "GetEffectTargetADirectionType", &LuaSpellInfo::GetEffectTargetADirectionType }, + { "GetEffectTargetAIsArea", &LuaSpellInfo::GetEffectTargetAIsArea }, + { "GetEffectTargetADirectionAngle", &LuaSpellInfo::GetEffectTargetADirectionAngle }, + { "GetEffectTargetBTarget", &LuaSpellInfo::GetEffectTargetBTarget }, + { "GetEffectTargetBSelectionCategory", &LuaSpellInfo::GetEffectTargetBSelectionCategory }, + { "GetEffectTargetBReferenceType", &LuaSpellInfo::GetEffectTargetBReferenceType }, + { "GetEffectTargetBObjectType", &LuaSpellInfo::GetEffectTargetBObjectType }, + { "GetEffectTargetBCheckType", &LuaSpellInfo::GetEffectTargetBCheckType }, + { "GetEffectTargetBDirectionType", &LuaSpellInfo::GetEffectTargetBDirectionType }, + { "GetEffectTargetBIsArea", &LuaSpellInfo::GetEffectTargetBIsArea }, + { "GetEffectTargetBDirectionAngle", &LuaSpellInfo::GetEffectTargetBDirectionAngle } + }; +} +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/SpellMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/SpellMethods.h new file mode 100644 index 0000000000..f61b641aa4 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/SpellMethods.h @@ -0,0 +1,551 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef SPELLMETHODS_H +#define SPELLMETHODS_H + +/*** + * An instance of a spell, created when the spell is cast by a [Unit]. + * + * Inherits all methods from: none + */ +namespace LuaSpell +{ + /** + * Returns `true` if the [Spell] is automatically repeating, `false` otherwise. + * + * @return bool isAutoRepeating + */ + int IsAutoRepeat(Eluna* E, Spell* spell) + { + E->Push(spell->IsAutoRepeat()); + return 1; + } + + /** + * Returns the [Unit] that casted the [Spell]. + * + * @return [Unit] caster + */ + int GetCaster(Eluna* E, Spell* spell) + { + E->Push(spell->GetCaster()); + return 1; + } + + /** + * Returns the cast time of the [Spell]. + * + * @return int32 castTime + */ + int GetCastTime(Eluna* E, Spell* spell) + { + E->Push(spell->GetCastTime()); + return 1; + } + + /** + * Returns the entry ID of the [Spell]. + * + * @return uint32 entryId + */ + int GetEntry(Eluna* E, Spell* spell) + { + E->Push(spell->m_spellInfo->Id); + return 1; + } + + /** + * Returns the power cost of the [Spell]. + * + * @return uint32 powerCost + */ + int GetPowerCost(Eluna* E, Spell* spell) + { + E->Push(spell->GetPowerCost()); + return 1; + } + + /** + * Returns the spell duration of the [Spell]. + * + * @return int32 duration + */ + int GetDuration(Eluna* E, Spell* spell) + { + E->Push(spell->GetSpellInfo()->GetDuration()); + return 1; + } + + /** + * Returns the target destination coordinates of the [Spell]. + * + * @return float x : x coordinate of the [Spell] + * @return float y : y coordinate of the [Spell] + * @return float z : z coordinate of the [Spell] + */ + int GetTargetDest(Eluna* E, Spell* spell) + { + if (!spell->m_targets.HasDst()) + return 3; + + float x, y, z; + spell->m_targets.GetDstPos()->GetPosition(x, y, z); + + E->Push(x); + E->Push(y); + E->Push(z); + return 3; + } + + /** + * Returns the target [Object] of the [Spell]. + * + * The target can be any of the following [Object] types: + * - [Player] + * - [Creature] + * - [GameObject] + * - [Item] + * - [Corpse] + * + * @return [Object] target + */ + int GetTarget(Eluna* E, Spell* spell) + { + if (GameObject* target = spell->m_targets.GetGOTarget()) + E->Push(target); + else if (Item* target = spell->m_targets.GetItemTarget()) + E->Push(target); + else if (Corpse* target = spell->m_targets.GetCorpseTarget()) + E->Push(target); + else if (Unit* target = spell->m_targets.GetUnitTarget()) + E->Push(target); + else if (WorldObject* target = spell->m_targets.GetObjectTarget()) + E->Push(target); + return 1; + } + + /** + * Sets the [Spell] to automatically repeat. + * + * @param bool repeat : set variable to 'true' for spell to automatically repeat + */ + int SetAutoRepeat(Eluna* E, Spell* spell) + { + bool repeat = E->CHECKVAL(2); + spell->SetAutoRepeat(repeat); + return 0; + } + + /** + * Casts the [Spell]. + * + * @param bool skipCheck = false : skips initial checks to see if the [Spell] can be casted or not, this is optional + */ + int Cast(Eluna* E, Spell* spell) + { + bool skipCheck = E->CHECKVAL(2, false); + spell->cast(skipCheck); + return 0; + } + + /** + * Cancels the [Spell]. + */ + int Cancel(Eluna* /*E*/, Spell* spell) + { + spell->cancel(); + return 0; + } + + /** + * Finishes the [Spell]. + */ + int Finish(Eluna* /*E*/, Spell* spell) + { + spell->finish(); + return 0; + } + + /** + * Returns the [SpellInfo] of the spell that created this [Spell]. + * + * @return [SpellInfo] spellInfo + */ + int GetSpellInfo(Eluna* E, Spell* spell) + { + ElunaSpellInfo info(spell->GetSpellInfo()->Id); + E->Push(&info); + return 1; + } + + /** + * Returns `true` if the [Spell] is triggered, `false` otherwise. + * + * @return bool isTriggered + */ + int IsTriggered(Eluna* E, Spell* spell) + { + E->Push(spell->IsTriggered()); + return 1; + } + + /** + * Returns `true` if the [Spell] is ignoring cooldowns, `false` otherwise. + * + * @return bool isIgnoringCooldowns + */ + int IsIgnoringCooldowns(Eluna* E, Spell* spell) + { + E->Push(spell->IsIgnoringCooldowns()); + return 1; + } + + /** + * Returns `true` if the [Spell] proc is disabled, `false` otherwise. + * + * @return bool isProcDisabled + */ + int IsProcDisabled(Eluna* E, Spell* spell) + { + E->Push(spell->IsProcDisabled()); + return 1; + } + + /** + * Returns `true` if the [Spell] channel is active, `false` otherwise. + * + * @return bool isChannelActive + */ + int IsChannelActive(Eluna* E, Spell* spell) + { + E->Push(spell->IsChannelActive()); + return 1; + } + + /** + * Returns `true` if the [Spell] is an auto action reset spell, `false` otherwise. + * + * @return bool isAutoActionResetSpell + */ + int IsAutoActionResetSpell(Eluna* E, Spell* spell) + { + E->Push(spell->IsAutoActionResetSpell()); + return 1; + } + + /** + * Returns `true` if the [Spell] is positive, `false` otherwise. + * + * @return bool isPositive + */ + #if 0 + int IsPositive(Eluna* E, Spell* spell) + { + E->Push(spell->IsPositive()); + return 1; + } + #endif + /** + * Returns `true` if the [Spell] is deletable, `false` otherwise. + * + * @return bool isDeletable + */ + int IsDeletable(Eluna* E, Spell* spell) + { + E->Push(spell->IsDeletable()); + return 1; + } + + /** + * Returns `true` if the [Spell] is interruptable, `false` otherwise. + * + * @return bool isInterruptable + */ + int IsInterruptable(Eluna* E, Spell* spell) + { + E->Push(spell->IsInterruptable()); + return 1; + } + + /** + * Returns `true` if the [Spell] needs to be sent to the client, `false` otherwise. + * + * @return bool isNeedSendToClient + */ + int IsNeedSendToClient(Eluna* E, Spell* spell) + { + E->Push(spell->IsNeedSendToClient()); + return 1; + } + + /** + * Returns `true` if the [Spell] was triggered by the given [SpellInfo], `false` otherwise. + * + * @param [SpellInfo] spellInfo : the aura spell info to check against + * @return bool isTriggeredByAura + */ + int IsTriggeredByAura(Eluna* E, Spell* spell) + { + ElunaSpellInfo* info = E->CHECKOBJ(2); + E->Push(spell->IsTriggeredByAura(info->GetSpellInfo())); + return 1; + } + + /** + * Returns the original caster [Unit] of the [Spell]. + * + * @return [Unit] originalCaster + */ + int GetOriginalCaster(Eluna* E, Spell* spell) + { + E->Push(spell->GetOriginalCaster()); + return 1; + } + + /** + * Returns the current state of the [Spell]. + * + * @return uint32 state + */ + int GetState(Eluna* E, Spell* spell) + { + E->Push(spell->getState()); + return 1; + } + + /** + * Returns the delay start time of the [Spell]. + * + * @return uint64 delayStart + */ + int GetDelayStart(Eluna* E, Spell* spell) + { + E->Push(spell->GetDelayStart()); + return 1; + } + + /** + * Returns the delay moment of the [Spell]. + * + * @return uint64 delayMoment + */ + int GetDelayMoment(Eluna* E, Spell* spell) + { + E->Push(spell->GetDelayMoment()); + return 1; + } + + /** + * Returns the rune state of the [Spell]. + * + * @return uint8 runeState + */ + #if 0 + int GetRuneState(Eluna* E, Spell* spell) + { + E->Push(spell->GetRuneState()); + return 1; + } + #endif + /** + * Returns the number of unit targets for the given effect index of the [Spell]. + * + * @param uint8 effIndex : the effect index to check + * @return int32 count + */ + #if 0 + int GetUnitTargetCountForEffect(Eluna* E, Spell* spell) + { + uint8 effIndex = E->CHECKVAL(2); + E->Push(static_cast(spell->GetUnitTargetCountForEffect(static_cast(effIndex)))); + return 1; + } + #endif + /** + * Returns the number of GameObject targets for the given effect index of the [Spell]. + * + * @param uint8 effIndex : the effect index to check + * @return int32 count + */ + int GetGameObjectTargetCountForEffect(Eluna* E, Spell* spell) + { + uint8 effIndex = E->CHECKVAL(2); + E->Push(static_cast(spell->GetGameObjectTargetCountForEffect(static_cast(effIndex)))); + return 1; + } + + /** + * Returns the number of item targets for the given effect index of the [Spell]. + * + * @param uint8 effIndex : the effect index to check + * @return int32 count + */ + int GetItemTargetCountForEffect(Eluna* E, Spell* spell) + { + uint8 effIndex = E->CHECKVAL(2); + E->Push(static_cast(spell->GetItemTargetCountForEffect(static_cast(effIndex)))); + return 1; + } + + /** + * Calculates the damage for the given effect index of the [Spell]. + * + * @param uint8 effIndex : the effect index to calculate damage for + * @return int32 damage + */ + #if 0 + int CalculateDamage(Eluna* E, Spell* spell) + { + uint8 effIndex = E->CHECKVAL(2); + E->Push(spell->CalculateDamage(spell->GetSpellInfo()->GetEffect(static_cast(effIndex)))); + return 1; + } + #endif + /** + * Sets the state of the [Spell]. + * + * @param uint32 state : the state to set + */ + int SetState(Eluna* E, Spell* spell) + { + uint32 state = E->CHECKVAL(2); + spell->setState(state); + return 0; + } + + /** + * Sets the delay start time of the [Spell]. + * + * @param uint64 time : the delay start time to set + */ + int SetDelayStart(Eluna* E, Spell* spell) + { + uint64 time = E->CHECKVAL(2); + spell->SetDelayStart(time); + return 0; + } + + /** + * Sets the rune state of the [Spell]. + * + * @param uint8 value : the rune state to set + */ + #if 0 + int SetRuneState(Eluna* E, Spell* spell) + { + uint8 value = E->CHECKVAL(2); + spell->SetRuneState(value); + return 0; + } + #endif + /** + * Sets whether the [Spell] is currently being executed. + * + * @param bool yes : set to 'true' if the spell is currently being executed + */ + int SetExecutedCurrently(Eluna* E, Spell* spell) + { + bool yes = E->CHECKVAL(2); + spell->SetExecutedCurrently(yes); + return 0; + } + + /** + * Sets whether the [Spell] is referenced from the current spell. + * + * @param bool yes : set to 'true' if the spell is referenced from the current spell + */ + int SetReferencedFromCurrent(Eluna* E, Spell* spell) + { + bool yes = E->CHECKVAL(2); + spell->SetReferencedFromCurrent(yes); + return 0; + } + + /** + * Sets a spell value modifier on the [Spell]. + * + * @param uint8 mod : the [SpellValueMod] to set + * @param int32 value : the value to set + */ + int SetSpellValue(Eluna* E, Spell* spell) + { + uint8 mod = E->CHECKVAL(2); + int32 value = E->CHECKVAL(3); + spell->SetSpellValue(static_cast(mod), value); + return 0; + } + + /** + * Updates the [Spell]'s pointers. Must be used after time delays on non-triggered spell casts. + * + * @return bool success + */ + int UpdatePointers(Eluna* E, Spell* spell) + { + E->Push(spell->UpdatePointers()); + return 1; + } + + /** + * Cleans up the target list of the [Spell]. + */ + int CleanupTargetList(Eluna* /*E*/, Spell* spell) + { + spell->CleanupTargetList(); + return 0; + } + + ElunaRegister SpellMethods[] = + { + // Getters + { "GetCaster", &LuaSpell::GetCaster }, + { "GetOriginalCaster", &LuaSpell::GetOriginalCaster }, + { "GetCastTime", &LuaSpell::GetCastTime }, + { "GetEntry", &LuaSpell::GetEntry }, + { "GetDuration", &LuaSpell::GetDuration }, + { "GetPowerCost", &LuaSpell::GetPowerCost }, + { "GetTargetDest", &LuaSpell::GetTargetDest }, + { "GetTarget", &LuaSpell::GetTarget }, + { "GetState", &LuaSpell::GetState }, + { "GetDelayStart", &LuaSpell::GetDelayStart }, + { "GetDelayMoment", &LuaSpell::GetDelayMoment }, + + + { "GetGameObjectTargetCountForEffect", &LuaSpell::GetGameObjectTargetCountForEffect }, + { "GetItemTargetCountForEffect", &LuaSpell::GetItemTargetCountForEffect }, + { "GetSpellInfo", &LuaSpell::GetSpellInfo }, + + // Setters + { "SetAutoRepeat", &LuaSpell::SetAutoRepeat }, + { "SetSpellValue", &LuaSpell::SetSpellValue }, + { "SetState", &LuaSpell::SetState }, + { "SetDelayStart", &LuaSpell::SetDelayStart }, + + { "SetExecutedCurrently", &LuaSpell::SetExecutedCurrently }, + { "SetReferencedFromCurrent", &LuaSpell::SetReferencedFromCurrent }, + // Booleans + { "IsAutoRepeat", &LuaSpell::IsAutoRepeat }, + { "IsTriggered", &LuaSpell::IsTriggered }, + { "IsIgnoringCooldowns", &LuaSpell::IsIgnoringCooldowns }, + { "IsProcDisabled", &LuaSpell::IsProcDisabled }, + { "IsChannelActive", &LuaSpell::IsChannelActive }, + { "IsAutoActionResetSpell", &LuaSpell::IsAutoActionResetSpell }, + + { "IsDeletable", &LuaSpell::IsDeletable }, + { "IsInterruptable", &LuaSpell::IsInterruptable }, + { "IsNeedSendToClient", &LuaSpell::IsNeedSendToClient }, + { "IsTriggeredByAura", &LuaSpell::IsTriggeredByAura }, + { "UpdatePointers", &LuaSpell::UpdatePointers }, + // Other + { "Cancel", &LuaSpell::Cancel }, + { "Cast", &LuaSpell::Cast }, + { "Finish", &LuaSpell::Finish }, + { "CleanupTargetList", &LuaSpell::CleanupTargetList }, + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/UnitMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/UnitMethods.h new file mode 100644 index 0000000000..f3428965c2 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/UnitMethods.h @@ -0,0 +1,2174 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef UNITMETHODS_H +#define UNITMETHODS_H + +#include "ChatPackets.h" + +/*** + * Inherits all methods from: [Object], [WorldObject] + */ +namespace LuaUnit +{ + /** + * Sets a mechanic immunity for the [Unit]. + * + * @table + * @columns [Mechanic, ID, Comment] + * @values [MECHANIC_NONE, 0, ""] + * @values [MECHANIC_CHARM, 1, ""] + * @values [MECHANIC_DISORIENTED, 2, ""] + * @values [MECHANIC_DISARM, 3, ""] + * @values [MECHANIC_DISTRACT, 4, ""] + * @values [MECHANIC_FEAR, 5, ""] + * @values [MECHANIC_GRIP, 6, ""] + * @values [MECHANIC_ROOT, 7, ""] + * @values [MECHANIC_SLOW_ATTACK, 8, ""] + * @values [MECHANIC_SILENCE, 9, ""] + * @values [MECHANIC_SLEEP, 10, ""] + * @values [MECHANIC_SNARE, 11, ""] + * @values [MECHANIC_STUN, 12, ""] + * @values [MECHANIC_FREEZE, 13, ""] + * @values [MECHANIC_KNOCKOUT, 14, ""] + * @values [MECHANIC_BLEED, 15, ""] + * @values [MECHANIC_BANDAGE, 16, ""] + * @values [MECHANIC_POLYMORPH, 17, ""] + * @values [MECHANIC_BANISH, 18, ""] + * @values [MECHANIC_SHIELD, 19, ""] + * @values [MECHANIC_SHACKLE, 20, ""] + * @values [MECHANIC_MOUNT, 21, ""] + * @values [MECHANIC_INFECTED, 22, ""] + * @values [MECHANIC_TURN, 23, ""] + * @values [MECHANIC_HORROR, 24, ""] + * @values [MECHANIC_INVULNERABILITY, 25, ""] + * @values [MECHANIC_INTERRUPT, 26, ""] + * @values [MECHANIC_DAZE, 27, ""] + * @values [MECHANIC_DISCOVERY, 28, ""] + * @values [MECHANIC_IMMUNE_SHIELD, 29, "Divine (Blessing) Shield/Protection and Ice Block"] + * @values [MECHANIC_SAPPED, 30, ""] + * @values [MECHANIC_ENRAGED, 31, ""] + * + * @param int32 immunity : new value for the immunity mask + * @param bool apply = true : if true, the immunity is applied, otherwise it is removed + */ + int SetImmuneTo(Eluna* E, Unit* unit) + { + int32 immunity = E->CHECKVAL(2); + bool apply = E->CHECKVAL(3, true); + + unit->ApplySpellImmune(0, 5, immunity, apply); + return 0; + } + /** + * The [Unit] tries to attack a given target + * + * @param [Unit] who : [Unit] to attack + * @param bool meleeAttack = false: attack with melee or not + * @return didAttack : if the [Unit] did not attack + */ + int Attack(Eluna* E, Unit* unit) + { + Unit* who = E->CHECKOBJ(2); + bool meleeAttack = E->CHECKVAL(3, false); + + E->Push(unit->Attack(who, meleeAttack)); + return 1; + } + + /** + * The [Unit] stops attacking its target + * + * @return bool isAttacking : if the [Unit] wasn't attacking already + */ + int AttackStop(Eluna* E, Unit* unit) + { + E->Push(unit->AttackStop()); + return 1; + } + + /** + * Returns true if the [Unit] is standing. + * + * @return bool isStanding + */ + int IsStandState(Eluna* E, Unit* unit) + { + E->Push(unit->IsStandState()); + return 1; + } + + /** + * Returns true if the [Unit] is mounted. + * + * @return bool isMounted + */ + int IsMounted(Eluna* E, Unit* unit) + { + E->Push(unit->IsMounted()); + return 1; + } + + /** + * Returns true if the [Unit] is rooted. + * + * @return bool isRooted + */ + #if 0 + int IsRooted(Eluna* E, Unit* unit) + { + E->Push(unit->IsRooted() || unit->HasUnitMovementFlag(MOVEMENTFLAG_ROOT)); + return 1; + } + #endif + /** + * Returns true if the [Unit] has full health. + * + * @return bool hasFullHealth + */ + int IsFullHealth(Eluna* E, Unit* unit) + { + E->Push(unit->IsFullHealth()); + return 1; + } + + /** + * Returns true if the [Unit] is in an accessible place for the given [Creature]. + * + * @param [WorldObject] obj + * @param float radius + * @return bool isAccessible + */ + int IsInAccessiblePlaceFor(Eluna* E, Unit* unit) + { + Creature* creature = E->CHECKOBJ(2); + + E->Push(unit->isInAccessiblePlaceFor(creature)); + return 1; + } + + /** + * Returns true if the [Unit] an auctioneer. + * + * @return bool isAuctioneer + */ + int IsAuctioneer(Eluna* E, Unit* unit) + { + E->Push(unit->IsAuctioner()); + return 1; + } + + /** + * Returns true if the [Unit] a guild master. + * + * @return bool isGuildMaster + */ + int IsGuildMaster(Eluna* E, Unit* unit) + { + E->Push(unit->IsGuildMaster()); + return 1; + } + + /** + * Returns true if the [Unit] an innkeeper. + * + * @return bool isInnkeeper + */ + int IsInnkeeper(Eluna* E, Unit* unit) + { + E->Push(unit->IsInnkeeper()); + return 1; + } + + /** + * Returns true if the [Unit] a trainer. + * + * @return bool isTrainer + */ + int IsTrainer(Eluna* E, Unit* unit) + { + E->Push(unit->IsTrainer()); + return 1; + } + + /** + * Returns true if the [Unit] is able to show a gossip window. + * + * @return bool hasGossip + */ + int IsGossip(Eluna* E, Unit* unit) + { + E->Push(unit->IsGossip()); + return 1; + } + + /** + * Returns true if the [Unit] is a taxi master. + * + * @return bool isTaxi + */ + int IsTaxi(Eluna* E, Unit* unit) + { + E->Push(unit->IsTaxi()); + return 1; + } + + /** + * Returns true if the [Unit] is a spirit healer. + * + * @return bool isSpiritHealer + */ + int IsSpiritHealer(Eluna* E, Unit* unit) + { + E->Push(unit->IsSpiritHealer()); + return 1; + } + + /** + * Returns true if the [Unit] is a spirit guide. + * + * @return bool isSpiritGuide + */ + int IsSpiritGuide(Eluna* E, Unit* unit) + { + E->Push(unit->IsSpiritGuide()); + return 1; + } + + /** + * Returns true if the [Unit] is a tabard designer. + * + * @return bool isTabardDesigner + */ + int IsTabardDesigner(Eluna* E, Unit* unit) + { + E->Push(unit->IsTabardDesigner()); + return 1; + } + + /** + * Returns true if the [Unit] provides services like vendor, training and auction. + * + * @return bool isTabardDesigner + */ + int IsServiceProvider(Eluna* E, Unit* unit) + { + E->Push(unit->IsServiceProvider()); + return 1; + } + + /** + * Returns true if the [Unit] is a spirit guide or spirit healer. + * + * @return bool isSpiritService + */ + int IsSpiritService(Eluna* E, Unit* unit) + { + E->Push(unit->IsSpiritService()); + return 1; + } + + /** + * Returns true if the [Unit] is alive. + * + * @return bool isAlive + */ + int IsAlive(Eluna* E, Unit* unit) + { + E->Push(unit->IsAlive()); + return 1; + } + + /** + * Returns true if the [Unit] is dead. + * + * @return bool isDead + */ + int IsDead(Eluna* E, Unit* unit) + { + E->Push(unit->isDead()); + return 1; + } + + /** + * Returns true if the [Unit] is dying. + * + * @return bool isDying + */ + int IsDying(Eluna* E, Unit* unit) + { + E->Push(unit->isDying()); + return 1; + } + + /** + * Returns true if the [Unit] is a banker. + * + * @return bool isBanker + */ + int IsBanker(Eluna* E, Unit* unit) + { + E->Push(unit->IsBanker()); + return 1; + } + + /** + * Returns true if the [Unit] is a vendor. + * + * @return bool isVendor + */ + int IsVendor(Eluna* E, Unit* unit) + { + E->Push(unit->IsVendor()); + return 1; + } + + /** + * Returns true if the [Unit] is a battle master. + * + * @return bool isBattleMaster + */ + int IsBattleMaster(Eluna* E, Unit* unit) + { + E->Push(unit->IsBattleMaster()); + return 1; + } + + /** + * Returns true if the [Unit] is a charmed. + * + * @return bool isCharmed + */ + int IsCharmed(Eluna* E, Unit* unit) + { + E->Push(unit->IsCharmed()); + return 1; + } + + /** + * Returns true if the [Unit] is an armorer and can repair equipment. + * + * @return bool isArmorer + */ + int IsArmorer(Eluna* E, Unit* unit) + { + E->Push(unit->IsArmorer()); + return 1; + } + + /** + * Returns true if the [Unit] is attacking a player. + * + * @return bool isAttackingPlayer + */ + int IsAttackingPlayer(Eluna* E, Unit* unit) + { + E->Push(unit->isAttackingPlayer()); + return 1; + } + + /** + * Returns true if the [Unit] flagged for PvP. + * + * @return bool isPvP + */ + int IsPvPFlagged(Eluna* E, Unit* unit) + { + E->Push(unit->IsPvP()); + return 1; + } + + /** + * Returns true if the [Unit] is on a [Vehicle]. + * + * @return bool isOnVehicle + */ + #if 0 + int IsOnVehicle(Eluna* E, Unit* unit) + { + E->Push(unit->GetVehicle()); + return 1; + } + #endif + /** + * Returns true if the [Unit] is in combat. + * + * @return bool inCombat + */ + int IsInCombat(Eluna* E, Unit* unit) + { + E->Push(unit->IsInCombat()); + return 1; + } + + /** + * Returns true if the [Unit] is under water. + * + * @return bool underWater + */ + int IsUnderWater(Eluna* E, Unit* unit) + { + E->Push(unit->IsUnderWater()); + return 1; + } + + /** + * Returns true if the [Unit] is in water. + * + * @return bool inWater + */ + int IsInWater(Eluna* E, Unit* unit) + { + E->Push(unit->IsInWater()); + return 1; + } + + /** + * Returns true if the [Unit] is not moving. + * + * @return bool notMoving + */ + int IsStopped(Eluna* E, Unit* unit) + { + E->Push(unit->IsStopped()); + return 1; + } + + /** + * Returns true if the [Unit] is a quest giver. + * + * @return bool questGiver + */ + int IsQuestGiver(Eluna* E, Unit* unit) + { + E->Push(unit->IsQuestGiver()); + return 1; + } + + /** + * Returns true if the [Unit]'s health is below the given percentage. + * + * @param int32 healthpct : percentage in integer from + * @return bool isBelow + */ + int HealthBelowPct(Eluna* E, Unit* unit) + { + E->Push(unit->HealthBelowPct(E->CHECKVAL(2))); + return 1; + } + + /** + * Returns true if the [Unit]'s health is above the given percentage. + * + * @param int32 healthpct : percentage in integer from + * @return bool isAbove + */ + int HealthAbovePct(Eluna* E, Unit* unit) + { + E->Push(unit->HealthAbovePct(E->CHECKVAL(2))); + return 1; + } + + /** + * Returns true if the [Unit] has an aura from the given spell entry. + * + * @param uint32 spell : entry of the aura spell + * @return bool hasAura + */ + int HasAura(Eluna* E, Unit* unit) + { + uint32 spell = E->CHECKVAL(2); + + E->Push(unit->HasAura(spell)); + return 1; + } + + /** + * Returns true if the [Unit] is casting a spell + * + * @return bool isCasting + */ + int IsCasting(Eluna* E, Unit* unit) + { + E->Push(unit->HasUnitState(UNIT_STATE_CASTING)); + return 1; + } + + /** + * Returns true if the [Unit] has the given unit state. + * + * @param [UnitState] state : an unit state + * @return bool hasState + */ + int HasUnitState(Eluna* E, Unit* unit) + { + uint32 state = E->CHECKVAL(2); + + E->Push(unit->HasUnitState(state)); + return 1; + } + + /** + * Returns true if the [Unit] is visible, false otherwise. + * + * @return bool isVisible + */ + int IsVisible(Eluna* E, Unit* unit) + { + E->Push(unit->IsVisible()); + return 1; + } + + /** + * Returns true if the [Unit] is moving, false otherwise. + * + * @return bool isMoving + */ + int IsMoving(Eluna* E, Unit* unit) + { + E->Push(unit->isMoving()); + return 1; + } + + /** + * Returns true if the [Unit] is flying, false otherwise. + * + * @return bool isFlying + */ + int IsFlying(Eluna* E, Unit* unit) + { + E->Push(unit->IsFlying()); + return 1; + } + + /** + * Returns the [Unit]'s owner. + * + * @return [Unit] owner + */ + int GetOwner(Eluna* E, Unit* unit) + { + E->Push(unit->GetOwner()); + return 1; + } + + /** + * Returns the [Unit]'s owner's GUID. + * + * @return ObjectGuid ownerGUID + */ + int GetOwnerGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetOwnerGUID()); + return 1; + } + + + #if 0 + int GetMountId(Eluna* E, Unit* unit) + { + E->Push(unit->GetMountDisplayId()); + return 1; + } + #endif + + int GetCreatorGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetCreatorGUID()); + return 1; + } + + + int GetCharmerGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetCharmerGUID()); + return 1; + } + + + #if 0 + int GetCharmGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetCharmedGUID()); + return 1; + } + #endif + + int GetPetGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetPetGUID()); + return 1; + } + + + int GetControllerGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetCharmerOrOwnerGUID()); + return 1; + } + + + int GetControllerGUIDS(Eluna* E, Unit* unit) + { + E->Push(unit->GetCharmerOrOwnerOrOwnGUID()); + return 1; + } + + + int GetStat(Eluna* E, Unit* unit) + { + uint32 stat = E->CHECKVAL(2); + + if (stat >= MAX_STATS) + return 1; + + E->Push(unit->GetStat((Stats)stat)); + return 1; + } + + + int GetBaseSpellPower(Eluna* E, Unit* unit) + { + uint32 spellschool = E->CHECKVAL(2); + + if (spellschool >= MAX_SPELL_SCHOOL) + return 1; + + E->Push(unit->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + spellschool)); + return 1; + } + + + int GetVictim(Eluna* E, Unit* unit) + { + E->Push(unit->GetVictim()); + return 1; + } + + + int GetCurrentSpell(Eluna* E, Unit* unit) + { + uint32 type = E->CHECKVAL(2); + if (type >= CURRENT_MAX_SPELL) + return luaL_argerror(E->L, 2, "valid CurrentSpellTypes expected"); + + E->Push(unit->GetCurrentSpell(type)); + return 1; + } + + + int GetStandState(Eluna* E, Unit* unit) + { + E->Push(unit->GetStandState()); + return 1; + } + + + int GetDisplayId(Eluna* E, Unit* unit) + { + E->Push(unit->GetDisplayId()); + return 1; + } + + + int GetNativeDisplayId(Eluna* E, Unit* unit) + { + E->Push(unit->GetNativeDisplayId()); + return 1; + } + + + #if 0 + int GetLevel(Eluna* E, Unit* unit) + { + E->Push(unit->GetLevel()); + return 1; + } + #endif + + int GetHealth(Eluna* E, Unit* unit) + { + E->Push(unit->GetHealth()); + return 1; + } + + Powers PowerSelectorHelper(Eluna* E, Unit* unit, int powerType = -1) + { + if (powerType == -1) + return unit->GetPowerType(); + + if (powerType < 0 || powerType >= int(MAX_POWERS)) + luaL_argerror(E->L, 2, "valid Powers expected"); + + return (Powers)powerType; + } + + + int GetPower(Eluna* E, Unit* unit) + { + int type = E->CHECKVAL(2, -1); + Powers power = PowerSelectorHelper(E, unit, type); + + E->Push(unit->GetPower(power)); + return 1; + } + + + int GetMaxPower(Eluna* E, Unit* unit) + { + int type = E->CHECKVAL(2, -1); + Powers power = PowerSelectorHelper(E, unit, type); + + E->Push(unit->GetMaxPower(power)); + return 1; + } + + + int GetPowerPct(Eluna* E, Unit* unit) + { + int type = E->CHECKVAL(2, -1); + Powers power = PowerSelectorHelper(E, unit, type); + + float percent = ((float)unit->GetPower(power) / (float)unit->GetMaxPower(power)) * 100.0f; + + E->Push(percent); + return 1; + } + + + int GetPowerType(Eluna* E, Unit* unit) + { + E->Push(unit->GetPowerType()); + return 1; + } + + + int GetMaxHealth(Eluna* E, Unit* unit) + { + E->Push(unit->GetMaxHealth()); + return 1; + } + + + int GetHealthPct(Eluna* E, Unit* unit) + { + E->Push(unit->GetHealthPct()); + return 1; + } + + + #if 0 + int GetGender(Eluna* E, Unit* unit) + { + E->Push(unit->GetGender()); + return 1; + } + #endif + + #if 0 + int GetRace(Eluna* E, Unit* unit) + { + E->Push(unit->GetRace()); + return 1; + } + #endif + + #if 0 + int GetClass(Eluna* E, Unit* unit) + { + E->Push(unit->GetClass()); + return 1; + } + #endif + + #if 0 + int GetRaceMask(Eluna* E, Unit* unit) + { + E->Push(unit->GetRaceMask()); + return 1; + } + #endif + + #if 0 + int GetClassMask(Eluna* E, Unit* unit) + { + E->Push(unit->GetClassMask()); + return 1; + } + #endif + + int GetCreatureType(Eluna* E, Unit* unit) + { + E->Push(unit->GetCreatureType()); + return 1; + } + + + #if 0 + int GetClassAsString(Eluna* E, Unit* unit) + { + uint8 locale = E->CHECKVAL(2, DEFAULT_LOCALE); + if (locale >= TOTAL_LOCALES) + return luaL_argerror(E->L, 2, "valid LocaleConstant expected"); + + const ChrClassesEntry* entry = sChrClassesStore.LookupEntry(unit->GetClass()); + if (!entry) + return 1; + + E->Push(entry->Name[locale]); + return 1; + } + #endif + + #if 0 + int GetRaceAsString(Eluna* E, Unit* unit) + { + uint8 locale = E->CHECKVAL(2, DEFAULT_LOCALE); + if (locale >= TOTAL_LOCALES) + return luaL_argerror(E->L, 2, "valid LocaleConstant expected"); + + const ChrRacesEntry* entry = sChrRacesStore.LookupEntry(unit->GetRace()); + if (!entry) + return 1; + + E->Push(entry->Name[locale]); + return 1; + } + #endif + + #if 0 + int GetFaction(Eluna* E, Unit* unit) + { + E->Push(unit->GetFaction()); + return 1; + } + #endif + + int GetAura(Eluna* E, Unit* unit) + { + uint32 spellId = E->CHECKVAL(2); + ObjectGuid caster = E->CHECKVAL(3, ObjectGuid::Empty); + ObjectGuid itemCaster = E->CHECKVAL(4, ObjectGuid::Empty); + uint8 reqEffMask = E->CHECKVAL(5, 0); + E->Push(unit->GetAura(spellId, caster, itemCaster, reqEffMask)); + return 1; + } + + + int GetOwnedAura(Eluna* E, Unit* unit) + { + uint32 spellId = E->CHECKVAL(2); + ObjectGuid caster = E->CHECKVAL(3, ObjectGuid::Empty); + ObjectGuid itemCaster = E->CHECKVAL(4, ObjectGuid::Empty); + uint8 reqEffMask = E->CHECKVAL(5, 0); + Aura* exceptAura = E->CHECKOBJ(6, false); + E->Push(unit->GetOwnedAura(spellId, caster, itemCaster, reqEffMask, exceptAura)); + return 1; + } + + + int GetFriendlyUnitsInRange(Eluna* E, Unit* unit) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + + std::list list; + Trinity::AnyFriendlyUnitInObjectRangeCheck checker(unit, unit, range); + Trinity::UnitListSearcher searcher(unit, list, checker); + Cell::VisitAllObjects(unit, searcher, range); + + ElunaUtil::ObjectGUIDCheck guidCheck(unit->GET_GUID()); + list.remove_if(guidCheck); + + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + + int GetUnfriendlyUnitsInRange(Eluna* E, Unit* unit) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + + std::list list; + Trinity::AnyUnfriendlyUnitInObjectRangeCheck checker(unit, unit, range); + Trinity::UnitListSearcher searcher(unit, list, checker); + Cell::VisitAllObjects(unit, searcher, range); + + ElunaUtil::ObjectGUIDCheck guidCheck(unit->GET_GUID()); + list.remove_if(guidCheck); + + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + #if 0 + int GetVehicleKit(Eluna* E, Unit* unit) + { + E->Push(unit->GetVehicleKit()); + return 1; + } + + + int GetVehicle(Eluna* E, Unit* unit) + { + E->Push(unit->GetVehicle()); + return 1; + } + #endif + + int GetCritterGUID(Eluna* E, Unit* unit) + { + E->Push(unit->GetCritterGUID()); + return 1; + } + + + int GetSpeed(Eluna* E, Unit* unit) + { + uint32 type = E->CHECKVAL(2); + if (type >= MAX_MOVE_TYPE) + return luaL_argerror(E->L, 2, "valid UnitMoveType expected"); + + E->Push(unit->GetSpeed((UnitMoveType)type)); + return 1; + } + + + int GetMovementType(Eluna* E, Unit* unit) + { + E->Push(unit->GetMotionMaster()->GetCurrentMovementGeneratorType()); + return 1; + } + + /** + * Sets the [Unit]'s owner GUID to given GUID. + * + * @param ObjectGuid guid : new owner guid + */ + int SetOwnerGUID(Eluna* E, Unit* unit) + { + ObjectGuid guid = E->CHECKVAL(2); + + unit->SetOwnerGUID(guid); + return 0; + } + + + int SetPvP(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + unit->SetPvP(apply); + return 0; + } + + + int SetSheath(Eluna* E, Unit* unit) + { + uint32 sheathed = E->CHECKVAL(2); + if (sheathed >= MAX_SHEATH_STATE) + return luaL_argerror(E->L, 2, "valid SheathState expected"); + + unit->SetSheath((SheathState)sheathed); + return 0; + } + + + int SetName(Eluna* E, Unit* unit) + { + const char* name = E->CHECKVAL(2); + if (std::string(name).length() > 0) + unit->SetName(name); + return 0; + } + + + int SetSpeed(Eluna* E, Unit* unit) + { + uint32 type = E->CHECKVAL(2); + float rate = E->CHECKVAL(3); + bool forced = E->CHECKVAL(4, false); + (void)forced; // ensure that the variable is referenced in order to pass compiler checks + if (type >= MAX_MOVE_TYPE) + return luaL_argerror(E->L, 2, "valid UnitMoveType expected"); + + unit->SetSpeedRate((UnitMoveType)type, rate); + return 0; + } + + + + + + int SetLevel(Eluna* E, Unit* unit) + { + uint8 newlevel = E->CHECKVAL(2); + + if (newlevel < 1) + return luaL_argerror(E->L, 2, "level cannot be below 1"); + + if (Player* player = unit->ToPlayer()) + { + player->GiveLevel(newlevel); + player->InitTalentForLevel(); + player->SetUInt32Value(PLAYER_XP, 0); + } + else + unit->SetLevel(newlevel); + + return 0; + } + + + int SetHealth(Eluna* E, Unit* unit) + { + uint32 amt = E->CHECKVAL(2); + unit->SetHealth(amt); + return 0; + } + + + int SetMaxHealth(Eluna* E, Unit* unit) + { + uint32 amt = E->CHECKVAL(2); + unit->SetMaxHealth(amt); + return 0; + } + + + int SetPower(Eluna* E, Unit* unit) + { + uint32 amt = E->CHECKVAL(2); + int type = E->CHECKVAL(3, -1); + Powers power = PowerSelectorHelper(E, unit, type); + + unit->SetPower(power, amt); + return 0; + } + + + int ModifyPower(Eluna* E, Unit* unit) + { + int32 amt = E->CHECKVAL(2); + int type = E->CHECKVAL(3, -1); + Powers power = PowerSelectorHelper(E, unit, type); + + unit->ModifyPower(power, amt); + return 0; + } + + + int SetMaxPower(Eluna* E, Unit* unit) + { + int type = E->CHECKVAL(2, -1); + uint32 amt = E->CHECKVAL(3); + Powers power = PowerSelectorHelper(E, unit, type); + + unit->SetMaxPower(power, amt); + return 0; + } + + + int SetPowerType(Eluna* E, Unit* unit) + { + uint32 type = E->CHECKVAL(2); + if (type >= int(MAX_POWERS)) + return luaL_argerror(E->L, 2, "valid Powers expected"); + + unit->SetPowerType((Powers)type); + return 0; + } + + + int SetDisplayId(Eluna* E, Unit* unit) + { + uint32 model = E->CHECKVAL(2); + unit->SetDisplayId(model); + return 0; + } + + + int SetNativeDisplayId(Eluna* E, Unit* unit) + { + uint32 model = E->CHECKVAL(2); + unit->SetNativeDisplayId(model); + return 0; + } + + + int SetFacing(Eluna* E, Unit* unit) + { + float o = E->CHECKVAL(2); + unit->SetFacingTo(o); + return 0; + } + + + int SetFacingToObject(Eluna* E, Unit* unit) + { + WorldObject* obj = E->CHECKOBJ(2); + unit->SetFacingToObject(obj); + return 0; + } + + + int SetCreatorGUID(Eluna* E, Unit* unit) + { + ObjectGuid guid = E->CHECKVAL(2); + + unit->SetCreatorGUID(guid); + return 0; + } + + + int SetPetGUID(Eluna* E, Unit* unit) + { + ObjectGuid guid = E->CHECKVAL(2); + + unit->SetPetGUID(guid); + return 0; + } + + + int SetWaterWalk(Eluna* E, Unit* unit) + { + bool enable = E->CHECKVAL(2, true); + + unit->SetWaterWalking(enable); + return 0; + } + + + int SetStandState(Eluna* E, Unit* unit) + { + uint8 state = E->CHECKVAL(2); + + unit->SetStandState(UnitStandStateType(state)); + return 0; + } + + + int SetInCombatWith(Eluna* E, Unit* unit) + { + Unit* enemy = E->CHECKOBJ(2); + unit->SetInCombatWith(enemy); + return 0; + } + + + int SetFFA(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + if (apply) + { + unit->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + for (Unit::ControlList::iterator itr = unit->m_Controlled.begin(); itr != unit->m_Controlled.end(); ++itr) + (*itr)->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + } + else + { + unit->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + for (Unit::ControlList::iterator itr = unit->m_Controlled.begin(); itr != unit->m_Controlled.end(); ++itr) + (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + } + + return 0; + } + + int Sanctuary(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + if (apply) + { + unit->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + unit->CombatStop(); + unit->CombatStopWithPets(); + } + else + unit->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + + return 0; + } + + + int SetCritterGUID(Eluna* E, Unit* unit) + { + ObjectGuid guid = E->CHECKVAL(2); + unit->SetCritterGUID(guid); + return 0; + } + + + int SetStunned(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + unit->SetControlled(apply, UNIT_STATE_STUNNED); + return 0; + } + + + int SetRooted(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + unit->SetControlled(apply, UNIT_STATE_ROOT); + return 0; + } + + + int SetConfused(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + unit->SetControlled(apply, UNIT_STATE_CONFUSED); + return 0; + } + + + int SetFeared(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + unit->SetControlled(apply, UNIT_STATE_FLEEING); + return 0; + } + + + int SetCanFly(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + unit->SetCanFly(apply); + return 0; + } + + + int SetVisible(Eluna* E, Unit* unit) + { + bool x = E->CHECKVAL(2, true); + + unit->SetVisible(x); + return 0; + } + + /** + * Mounts the [Unit] on the given displayID/modelID. + * + * @param uint32 displayId + */ + int Mount(Eluna* E, Unit* unit) + { + uint32 displayId = E->CHECKVAL(2); + + unit->Mount(displayId); + return 0; + } + + /** + * Dismounts the [Unit]. + */ + int Dismount(Eluna* /*E*/, Unit* unit) + { + if (unit->IsMounted()) + { + unit->Dismount(); + unit->RemoveAurasByType(SPELL_AURA_MOUNTED); + } + + return 0; + } + + /** + * Makes the [Unit] perform the given emote. + * + * @param uint32 emoteId + */ + int PerformEmote(Eluna* E, Unit* unit) + { + Emote emote = static_cast(E->CHECKVAL(2)); + unit->HandleEmoteCommand(emote); + return 0; + } + + /** + * Makes the [Unit] perform the given emote continuously. + * + * @param uint32 emoteId + */ + int EmoteState(Eluna* E, Unit* unit) + { + uint32 emoteId = E->CHECKVAL(2); + + unit->SetUInt32Value(UNIT_NPC_EMOTESTATE, emoteId); + return 0; + } + + /** + * Returns calculated percentage from Health + * + * @return int32 percentage + */ + int CountPctFromCurHealth(Eluna* E, Unit* unit) + { + E->Push(unit->CountPctFromCurHealth(E->CHECKVAL(2))); + return 1; + } + + /** + * Returns calculated percentage from Max Health + * + * @return int32 percentage + */ + int CountPctFromMaxHealth(Eluna* E, Unit* unit) + { + E->Push(unit->CountPctFromMaxHealth(E->CHECKVAL(2))); + return 1; + } + + /** + * Sends chat message to [Player] + * + * @param uint8 type : chat, whisper, etc + * @param uint32 lang : language to speak + * @param string msg + * @param [Player] target + */ + int SendChatMessageToPlayer(Eluna* E, Unit* unit) + { + uint8 type = E->CHECKVAL(2); + uint32 lang = E->CHECKVAL(3); + std::string msg = E->CHECKVAL(4); + Player* target = E->CHECKOBJ(5); + + if (type >= MAX_CHAT_MSG_TYPE) + return luaL_argerror(E->L, 2, "valid ChatMsg expected"); + if (lang >= LANGUAGES_COUNT) + return luaL_argerror(E->L, 3, "valid Language expected"); + + WorldPackets::Chat::Chat chat; + chat.Initialize(ChatMsg(type), Language(lang), unit, target, msg); + + target->GetSession()->SendPacket(chat.Write()); + return 0; + } + + /** + * Stops the [Unit]'s movement + */ + int MoveStop(Eluna* /*E*/, Unit* unit) + { + unit->StopMoving(); + return 0; + } + + /** + * The [Unit]'s movement expires and clears movement + * + * @param bool reset = true : cleans movement + */ + int MoveExpire(Eluna* /*E*/, Unit* unit) + { + unit->GetMotionMaster()->Clear(); + return 0; + } + + /** + * Clears the [Unit]'s movement + * + * @param bool reset = true : clean movement + */ + int MoveClear(Eluna* /*E*/, Unit* unit) + { + unit->GetMotionMaster()->Clear(); + return 0; + } + + /** + * The [Unit] will be idle + */ + int MoveIdle(Eluna* /*E*/, Unit* unit) + { + unit->GetMotionMaster()->MoveIdle(); + return 0; + } + + /** + * The [Unit] will move at random + * + * @param float radius : limit on how far the [Unit] will move at random + */ + int MoveRandom(Eluna* E, Unit* unit) + { + float radius = E->CHECKVAL(2); + float x, y, z; + unit->GetPosition(x, y, z); + unit->GetMotionMaster()->MoveRandom(radius); + return 0; + } + + /** + * The [Unit] will move to its set home location + */ + int MoveHome(Eluna* /*E*/, Unit* unit) + { + unit->GetMotionMaster()->MoveTargetedHome(); + return 0; + } + + /** + * The [Unit] will follow the target + * + * @param [Unit] target : target to follow + * @param float dist = 0 : distance to start following + * @param float angle = 0 + */ + int MoveFollow(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2); + float dist = E->CHECKVAL(3, 0.0f); + float angle = E->CHECKVAL(4, 0.0f); + unit->GetMotionMaster()->MoveFollow(target, dist, angle); + return 0; + } + + /** + * The [Unit] will chase the target + * + * @param [Unit] target : target to chase + * @param float dist = 0 : distance start chasing + * @param float angle = 0 + */ + int MoveChase(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2); + float dist = E->CHECKVAL(3, 0.0f); + float angle = E->CHECKVAL(4, 0.0f); + unit->GetMotionMaster()->MoveChase(target, dist, angle); + return 0; + } + + /** + * The [Unit] will move confused + */ + int MoveConfused(Eluna* /*E*/, Unit* unit) + { + unit->GetMotionMaster()->MoveConfused(); + return 0; + } + + /** + * The [Unit] will flee + * + * @param [Unit] target + * @param uint32 time = 0 : flee delay + */ + int MoveFleeing(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2); + uint32 time = E->CHECKVAL(3, 0); + unit->GetMotionMaster()->MoveFleeing(target, time); + return 0; + } + + /** + * The [Unit] will move to the coordinates + * + * @param uint32 id : unique waypoint Id + * @param float x + * @param float y + * @param float z + * @param bool genPath = true : if true, generates path + */ + int MoveTo(Eluna* E, Unit* unit) + { + uint32 id = E->CHECKVAL(2); + float x = E->CHECKVAL(3); + float y = E->CHECKVAL(4); + float z = E->CHECKVAL(5); + bool genPath = E->CHECKVAL(6, true); + + unit->GetMotionMaster()->MovePoint(id, x, y, z, genPath); + return 0; + } + + /** + * Makes the [Unit] jump to the coordinates + * + * @param float x + * @param float y + * @param float z + * @param float zSpeed : start velocity + * @param float maxHeight : maximum height + * @param uint32 id = 0 : unique movement Id + */ + int MoveJump(Eluna* E, Unit* unit) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + float zSpeed = E->CHECKVAL(5); + float maxHeight = E->CHECKVAL(6); + uint32 id = E->CHECKVAL(7, 0); + + Position pos(x, y, z); + + unit->GetMotionMaster()->MoveJump(pos, zSpeed, maxHeight, id); + return 0; + } + + /** + * The [Unit] will whisper the message to a [Player] + * + * @param string msg : message for the [Unit] to emote + * @param uint32 lang : language for the [Unit] to speak + * @param [Player] receiver : specific [Unit] to receive the message + * @param bool bossWhisper = false : is a boss whisper + */ + int SendUnitWhisper(Eluna* E, Unit* unit) + { + const char* msg = E->CHECKVAL(2); + uint32 lang = E->CHECKVAL(3); + (void)lang; // ensure that the variable is referenced in order to pass compiler checks + Player* receiver = E->CHECKOBJ(4); + bool bossWhisper = E->CHECKVAL(5, false); + + if (std::string(msg).length() > 0) + unit->Whisper(msg, (Language)lang, receiver, bossWhisper); + + return 0; + } + + /** + * The [Unit] will emote the message + * + * @param string msg : message for the [Unit] to emote + * @param [Unit] receiver = nil : specific [Unit] to receive the message + * @param bool bossEmote = false : is a boss emote + */ + int SendUnitEmote(Eluna* E, Unit* unit) + { + const char* msg = E->CHECKVAL(2); + Unit* receiver = E->CHECKOBJ(3, false); + bool bossEmote = E->CHECKVAL(4, false); + + if (std::string(msg).length() > 0) + unit->TextEmote(msg, receiver, bossEmote); + + return 0; + } + + /** + * The [Unit] will say the message + * + * @param string msg : message for the [Unit] to say + * @param uint32 language : language for the [Unit] to speak + */ + int SendUnitSay(Eluna* E, Unit* unit) + { + const char* msg = E->CHECKVAL(2); + uint32 language = E->CHECKVAL(3); + + if (std::string(msg).length() > 0) + unit->Say(msg, (Language)language, unit); + + return 0; + } + + /** + * The [Unit] will yell the message + * + * @param string msg : message for the [Unit] to yell + * @param uint32 language : language for the [Unit] to speak + */ + int SendUnitYell(Eluna* E, Unit* unit) + { + const char* msg = E->CHECKVAL(2); + uint32 language = E->CHECKVAL(3); + + if (std::string(msg).length() > 0) + unit->Yell(msg, (Language)language, unit); + + return 0; + } + + /** + * Unmorphs the [Unit] setting it's display ID back to the native display ID. + */ + int DeMorph(Eluna* /*E*/, Unit* unit) + { + unit->DeMorph(); + return 0; + } + + /** + * Makes the [Unit] cast the spell on the target. + * + * @param [Unit] target = nil : can be self or another unit + * @param uint32 spell : entry of a spell + * @param bool triggered = false : if true the spell is instant and has no cost + */ + + int CastSpell(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2, false); + uint32 spell = E->CHECKVAL(3); + bool triggered = E->CHECKVAL(4, false); + + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell); + if (!spellEntry) + return 0; + + unit->CastSpell(target, spell, triggered); + return 0; + } + + + #if 0 + int CastCustomSpell(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2, false); + uint32 spell = E->CHECKVAL(3); + bool triggered = E->CHECKVAL(4, false); + bool has_bp0 = !lua_isnoneornil(E->L, 5); + int32 bp0 = E->CHECKVAL(5, 0); + bool has_bp1 = !lua_isnoneornil(E->L, 6); + int32 bp1 = E->CHECKVAL(6, 0); + bool has_bp2 = !lua_isnoneornil(E->L, 7); + int32 bp2 = E->CHECKVAL(7, 0); + Item* castItem = E->CHECKOBJ(8, false); + ObjectGuid originalCaster = E->CHECKVAL(9, ObjectGuid()); + + CastSpellExtraArgs args; + if (has_bp0) + args.AddSpellMod(SPELLVALUE_BASE_POINT0, bp0); + if (has_bp1) + args.AddSpellMod(SPELLVALUE_BASE_POINT1, bp1); + if (has_bp2) + args.AddSpellMod(SPELLVALUE_BASE_POINT2, bp2); + if (triggered) + args.TriggerFlags = TRIGGERED_FULL_MASK; + if (castItem) + args.SetCastItem(castItem); + if (!originalCaster.IsEmpty()) + args.SetOriginalCaster(originalCaster); + + unit->CastSpell(target, spell, args); + return 0; + } + #endif + + #if 0 + int CastSpellAoF(Eluna* E, Unit* unit) + { + float _x = E->CHECKVAL(2); + float _y = E->CHECKVAL(3); + float _z = E->CHECKVAL(4); + uint32 spell = E->CHECKVAL(5); + bool triggered = E->CHECKVAL(6, true); + + CastSpellExtraArgs args; + if (triggered) + args.TriggerFlags = TRIGGERED_FULL_MASK; + + unit->CastSpell(Position(_x, _y, _z), spell, args); + return 0; + } + #endif + + int StopSpellCast(Eluna* E, Unit* unit) + { + uint32 spellId = E->CHECKVAL(2, 0); + unit->CastStop(spellId); + return 0; + } + + + int InterruptSpell(Eluna* E, Unit* unit) + { + int spellType = E->CHECKVAL(2); + bool delayed = E->CHECKVAL(3, true); + switch (spellType) + { + case 0: + spellType = CURRENT_MELEE_SPELL; + break; + case 1: + spellType = CURRENT_GENERIC_SPELL; + break; + case 2: + spellType = CURRENT_CHANNELED_SPELL; + break; + case 3: + spellType = CURRENT_AUTOREPEAT_SPELL; + break; + default: + return luaL_argerror(E->L, 2, "valid CurrentSpellTypes expected"); + } + + unit->InterruptSpell((CurrentSpellTypes)spellType, delayed); + return 0; + } + + + int AddAura(Eluna* E, Unit* unit) + { + uint32 spell = E->CHECKVAL(2); + Unit* target = E->CHECKOBJ(3); + + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell); + if (!spellEntry) + return 1; + + E->Push(unit->AddAura(spell, target)); + return 1; + } + + + int RemoveAura(Eluna* E, Unit* unit) + { + uint32 spellId = E->CHECKVAL(2); + unit->RemoveAurasDueToSpell(spellId); + return 0; + } + + + int RemoveAllAuras(Eluna* /*E*/, Unit* unit) + { + unit->RemoveAllAuras(); + return 0; + } + + + int RemoveArenaAuras(Eluna* /*E*/, Unit* unit) + { + unit->RemoveArenaAuras(); + return 0; + } + + + int AddUnitState(Eluna* E, Unit* unit) + { + uint32 state = E->CHECKVAL(2); + + unit->AddUnitState(state); + return 0; + } + + + int ClearUnitState(Eluna* E, Unit* unit) + { + uint32 state = E->CHECKVAL(2); + + unit->ClearUnitState(state); + return 0; + } + + + int NearTeleport(Eluna* E, Unit* unit) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + float o = E->CHECKVAL(5); + + unit->NearTeleportTo(x, y, z, o); + return 0; + } + + + #if 0 + int DealDamage(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2); + uint32 damage = E->CHECKVAL(3); + bool durabilityloss = E->CHECKVAL(4, true); + uint32 school = E->CHECKVAL(5, MAX_SPELL_SCHOOL); + uint32 spell = E->CHECKVAL(6, 0); + if (school > MAX_SPELL_SCHOOL) + return luaL_argerror(E->L, 6, "valid SpellSchool expected"); + + // flat melee damage without resistence/etc reduction + if (school == MAX_SPELL_SCHOOL) + { + Unit::DealDamage(unit, target, damage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, durabilityloss); + unit->SendAttackStateUpdate(HITINFO_AFFECTS_VICTIM, target, 1, SPELL_SCHOOL_MASK_NORMAL, damage, 0, 0, VICTIMSTATE_HIT, 0); + return 0; + } + + SpellSchoolMask schoolmask = SpellSchoolMask(1 << school); + + if (Unit::IsDamageReducedByArmor(schoolmask)) + damage = Unit::CalcArmorReducedDamage(unit, target, damage, NULL, BASE_ATTACK); + + // melee damage by specific school + if (!spell) + { + DamageInfo dmgInfo(unit, target, damage, nullptr, schoolmask, SPELL_DIRECT_DAMAGE, BASE_ATTACK); + unit->CalcAbsorbResist(dmgInfo); + + if (!dmgInfo.GetDamage()) + damage = 0; + else + damage = dmgInfo.GetDamage(); + + uint32 absorb = dmgInfo.GetAbsorb(); + uint32 resist = dmgInfo.GetResist(); + unit->DealDamageMods(target, damage, &absorb); + + Unit::DealDamage(unit, target, damage, NULL, DIRECT_DAMAGE, schoolmask, NULL, false); + unit->SendAttackStateUpdate(HITINFO_AFFECTS_VICTIM, target, 0, schoolmask, damage, absorb, resist, VICTIMSTATE_HIT, 0); + return 0; + } + + if (!spell) + return 0; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell); + if (!spellInfo) + return 0; + + SpellNonMeleeDamage dmgInfo(unit, target, spell, spellInfo->GetSchoolMask()); + Unit::DealDamageMods(dmgInfo.target, dmgInfo.damage, &dmgInfo.absorb); + + unit->SendSpellNonMeleeDamageLog(&dmgInfo); + unit->DealSpellDamage(&dmgInfo, true); + return 0; + } + #endif + + int DealHeal(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2); + uint32 spell = E->CHECKVAL(3); + uint32 amount = E->CHECKVAL(4); + bool critical = E->CHECKVAL(5, false); + + if (const SpellInfo* info = sSpellMgr->GetSpellInfo(spell)) + { + HealInfo healInfo(unit, target, amount, info, info->GetSchoolMask()); + unit->HealBySpell(healInfo, critical); + } + + return 0; + } + + + #if 0 + int Kill(Eluna* E, Unit* unit) + { + Unit* target = E->CHECKOBJ(2); + bool durLoss = E->CHECKVAL(3, true); + + Unit::Kill(unit, target, durLoss); + return 0; + } + #endif + + int RestoreDisplayId(Eluna* /*E*/, Unit* unit) + { + unit->RestoreDisplayId(); + return 0; + } + + + int RestoreFaction(Eluna* /*E*/, Unit* unit) + { + unit->RestoreFaction(); + return 0; + } + + + int RemoveBindSightAuras(Eluna* /*E*/, Unit* unit) + { + unit->RemoveBindSightAuras(); + return 0; + } + + + int RemoveCharmAuras(Eluna* /*E*/, Unit* unit) + { + unit->RemoveCharmAuras(); + return 0; + } + + + int DisableMelee(Eluna* E, Unit* unit) + { + bool apply = E->CHECKVAL(2, true); + + if (apply) + unit->AddUnitState(UNIT_STATE_CANNOT_AUTOATTACK); + else + unit->ClearUnitState(UNIT_STATE_CANNOT_AUTOATTACK); + + return 0; + } + + + int CanModifyStats(Eluna* E, Unit* unit) + { + E->Push(unit->CanModifyStats()); + return 1; + } + + + #if 0 + int AddFlatStatModifier(Eluna* E, Unit* unit) + { + uint32 statType = E->CHECKVAL(2); + uint8 modType = E->CHECKVAL(3); + float value = E->CHECKVAL(4); + bool apply = E->CHECKVAL(5, true); + + unit->HandleStatFlatModifier(UnitMods(UNIT_MOD_STAT_START + statType), (UnitModifierFlatType)modType, value, apply); + return 0; + } + #endif + + #if 0 + int AddPctStatModifier(Eluna* E, Unit* unit) + { + uint32 statType = E->CHECKVAL(2); + uint8 modType = E->CHECKVAL(3); + float value = E->CHECKVAL(4); + + unit->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + statType), (UnitModifierPctType)modType, value); + return 0; + } + #endif + + // ========================================== + // [修复补丁] 还原被阉割的 SpawnCreature / SummonCreature + // ========================================== + static int SpawnCreature(Eluna* E, Unit* unit) + { + // 严格按顺序读取 Lua 传进来的所有 7 个参数(因为参数 1 是 player 自己) + uint32 entry = E->CHECKVAL(2); + float x = E->CHECKVAL(3); + float y = E->CHECKVAL(4); + float z = E->CHECKVAL(5); + float o = E->CHECKVAL(6); + uint32 spawnType = E->CHECKVAL(7); + uint32 despawnTimer = E->CHECKVAL(8); + + // 调用 C++ 原生的召唤方法 + Creature* creature = unit->SummonCreature(entry, x, y, z, o, (TempSummonType)spawnType, despawnTimer); + + // 将召唤出来的实体返回给 Lua + if (creature) + E->Push(creature); + else + E->Push(); // 返回 nil + + return 1; + } + + + + + + ElunaRegister UnitMethods[] = + { + // Getters + { "GetHealth", &LuaUnit::GetHealth }, + { "GetDisplayId", &LuaUnit::GetDisplayId }, + { "GetNativeDisplayId", &LuaUnit::GetNativeDisplayId }, + { "GetPower", &LuaUnit::GetPower }, + { "GetMaxPower", &LuaUnit::GetMaxPower }, + { "GetPowerType", &LuaUnit::GetPowerType }, + { "GetMaxHealth", &LuaUnit::GetMaxHealth }, + { "GetHealthPct", &LuaUnit::GetHealthPct }, + { "GetPowerPct", &LuaUnit::GetPowerPct }, + + + { "SpawnCreature", &LuaUnit::SpawnCreature }, + { "SummonCreature", &LuaUnit::SpawnCreature }, // 顺便起个别名以防万一 + + + + + { "GetAura", &LuaUnit::GetAura }, + { "GetOwnedAura", &LuaUnit::GetOwnedAura }, + + { "GetCurrentSpell", &LuaUnit::GetCurrentSpell }, + { "GetCreatureType", &LuaUnit::GetCreatureType }, + + { "GetOwner", &LuaUnit::GetOwner }, + { "GetFriendlyUnitsInRange", &LuaUnit::GetFriendlyUnitsInRange }, + { "GetUnfriendlyUnitsInRange", &LuaUnit::GetUnfriendlyUnitsInRange }, + { "GetOwnerGUID", &LuaUnit::GetOwnerGUID }, + { "GetCreatorGUID", &LuaUnit::GetCreatorGUID }, + { "GetMinionGUID", &LuaUnit::GetPetGUID }, + { "GetCharmerGUID", &LuaUnit::GetCharmerGUID }, + + { "GetPetGUID", &LuaUnit::GetPetGUID }, + { "GetCritterGUID", &LuaUnit::GetCritterGUID }, + { "GetControllerGUID", &LuaUnit::GetControllerGUID }, + { "GetControllerGUIDS", &LuaUnit::GetControllerGUIDS }, + { "GetStandState", &LuaUnit::GetStandState }, + { "GetVictim", &LuaUnit::GetVictim }, + { "GetSpeed", &LuaUnit::GetSpeed }, + { "GetStat", &LuaUnit::GetStat }, + { "GetBaseSpellPower", &LuaUnit::GetBaseSpellPower }, + + { "GetMovementType", &LuaUnit::GetMovementType }, + + // Setters + + { "SetLevel", &LuaUnit::SetLevel }, + { "SetHealth", &LuaUnit::SetHealth }, + { "SetMaxHealth", &LuaUnit::SetMaxHealth }, + { "SetPower", &LuaUnit::SetPower }, + { "SetMaxPower", &LuaUnit::SetMaxPower }, + { "SetPowerType", &LuaUnit::SetPowerType }, + { "SetDisplayId", &LuaUnit::SetDisplayId }, + { "SetNativeDisplayId", &LuaUnit::SetNativeDisplayId }, + { "SetFacing", &LuaUnit::SetFacing }, + { "SetFacingToObject", &LuaUnit::SetFacingToObject }, + { "SetSpeed", &LuaUnit::SetSpeed }, + { "SetStunned", &LuaUnit::SetStunned }, + { "SetRooted", &LuaUnit::SetRooted }, + { "SetConfused", &LuaUnit::SetConfused }, + { "SetFeared", &LuaUnit::SetFeared }, + { "SetPvP", &LuaUnit::SetPvP }, + { "SetFFA", &LuaUnit::SetFFA }, + + { "SetCanFly", &LuaUnit::SetCanFly }, + { "SetVisible", &LuaUnit::SetVisible }, + { "SetOwnerGUID", &LuaUnit::SetOwnerGUID }, + { "SetName", &LuaUnit::SetName }, + { "SetSheath", &LuaUnit::SetSheath }, + { "SetCreatorGUID", &LuaUnit::SetCreatorGUID }, + { "SetMinionGUID", &LuaUnit::SetPetGUID }, + { "SetPetGUID", &LuaUnit::SetPetGUID }, + { "SetCritterGUID", &LuaUnit::SetCritterGUID }, + { "SetWaterWalk", &LuaUnit::SetWaterWalk }, + { "SetStandState", &LuaUnit::SetStandState }, + { "SetInCombatWith", &LuaUnit::SetInCombatWith }, + { "ModifyPower", &LuaUnit::ModifyPower }, + { "SetImmuneTo", &LuaUnit::SetImmuneTo }, + + // Boolean + { "IsAlive", &LuaUnit::IsAlive }, + { "IsDead", &LuaUnit::IsDead }, + { "IsDying", &LuaUnit::IsDying }, + { "IsPvPFlagged", &LuaUnit::IsPvPFlagged }, + { "IsInCombat", &LuaUnit::IsInCombat }, + { "IsBanker", &LuaUnit::IsBanker }, + { "IsBattleMaster", &LuaUnit::IsBattleMaster }, + { "IsCharmed", &LuaUnit::IsCharmed }, + { "IsArmorer", &LuaUnit::IsArmorer }, + { "IsAttackingPlayer", &LuaUnit::IsAttackingPlayer }, + { "IsInWater", &LuaUnit::IsInWater }, + { "IsUnderWater", &LuaUnit::IsUnderWater }, + { "IsAuctioneer", &LuaUnit::IsAuctioneer }, + { "IsGuildMaster", &LuaUnit::IsGuildMaster }, + { "IsInnkeeper", &LuaUnit::IsInnkeeper }, + { "IsTrainer", &LuaUnit::IsTrainer }, + { "IsGossip", &LuaUnit::IsGossip }, + { "IsTaxi", &LuaUnit::IsTaxi }, + { "IsSpiritHealer", &LuaUnit::IsSpiritHealer }, + { "IsSpiritGuide", &LuaUnit::IsSpiritGuide }, + { "IsTabardDesigner", &LuaUnit::IsTabardDesigner }, + { "IsServiceProvider", &LuaUnit::IsServiceProvider }, + { "IsSpiritService", &LuaUnit::IsSpiritService }, + { "HealthBelowPct", &LuaUnit::HealthBelowPct }, + { "HealthAbovePct", &LuaUnit::HealthAbovePct }, + { "IsMounted", &LuaUnit::IsMounted }, + { "AttackStop", &LuaUnit::AttackStop }, + { "Attack", &LuaUnit::Attack }, + { "IsVisible", &LuaUnit::IsVisible }, + { "IsMoving", &LuaUnit::IsMoving }, + { "IsFlying", &LuaUnit::IsFlying }, + { "IsStopped", &LuaUnit::IsStopped }, + { "HasUnitState", &LuaUnit::HasUnitState }, + { "IsQuestGiver", &LuaUnit::IsQuestGiver }, + { "IsInAccessiblePlaceFor", &LuaUnit::IsInAccessiblePlaceFor }, + { "IsVendor", &LuaUnit::IsVendor }, + + { "IsFullHealth", &LuaUnit::IsFullHealth }, + { "HasAura", &LuaUnit::HasAura }, + { "IsCasting", &LuaUnit::IsCasting }, + { "IsStandState", &LuaUnit::IsStandState }, + + { "CanModifyStats", &LuaUnit::CanModifyStats }, + + // Other + { "AddAura", &LuaUnit::AddAura }, + { "RemoveAura", &LuaUnit::RemoveAura }, + { "RemoveAllAuras", &LuaUnit::RemoveAllAuras }, + { "RemoveArenaAuras", &LuaUnit::RemoveArenaAuras }, + + { "DeMorph", &LuaUnit::DeMorph }, + { "SendUnitWhisper", &LuaUnit::SendUnitWhisper }, + { "SendUnitEmote", &LuaUnit::SendUnitEmote }, + { "SendUnitSay", &LuaUnit::SendUnitSay }, + { "SendUnitYell", &LuaUnit::SendUnitYell }, + + { "CastSpell", &LuaUnit::CastSpell }, + + + + { "StopSpellCast", &LuaUnit::StopSpellCast }, + { "InterruptSpell", &LuaUnit::InterruptSpell }, + { "SendChatMessageToPlayer", &LuaUnit::SendChatMessageToPlayer }, + { "PerformEmote", &LuaUnit::PerformEmote }, + { "EmoteState", &LuaUnit::EmoteState }, + { "CountPctFromCurHealth", &LuaUnit::CountPctFromCurHealth }, + { "CountPctFromMaxHealth", &LuaUnit::CountPctFromMaxHealth }, + { "Dismount", &LuaUnit::Dismount }, + { "Mount", &LuaUnit::Mount }, + { "RestoreDisplayId", &LuaUnit::RestoreDisplayId }, + { "RestoreFaction", &LuaUnit::RestoreFaction }, + { "RemoveBindSightAuras", &LuaUnit::RemoveBindSightAuras }, + { "RemoveCharmAuras", &LuaUnit::RemoveCharmAuras }, + { "ClearUnitState", &LuaUnit::ClearUnitState }, + { "AddUnitState", &LuaUnit::AddUnitState }, + { "DisableMelee", &LuaUnit::DisableMelee }, + { "NearTeleport", &LuaUnit::NearTeleport }, + { "MoveIdle", &LuaUnit::MoveIdle }, + { "MoveRandom", &LuaUnit::MoveRandom }, + { "MoveHome", &LuaUnit::MoveHome }, + { "MoveFollow", &LuaUnit::MoveFollow }, + { "MoveChase", &LuaUnit::MoveChase }, + { "MoveConfused", &LuaUnit::MoveConfused }, + { "MoveFleeing", &LuaUnit::MoveFleeing }, + { "MoveTo", &LuaUnit::MoveTo }, + { "MoveJump", &LuaUnit::MoveJump }, + { "MoveStop", &LuaUnit::MoveStop }, + { "MoveExpire", &LuaUnit::MoveExpire }, + { "MoveClear", &LuaUnit::MoveClear }, + + { "DealHeal", &LuaUnit::DealHeal }, + + + + // Not implemented methods + { "SummonGuardian", METHOD_REG_NONE } // not implemented + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/VehicleMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/VehicleMethods.h new file mode 100644 index 0000000000..7b8ab0cc62 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/VehicleMethods.h @@ -0,0 +1,108 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef VEHICLEMETHODS_H +#define VEHICLEMETHODS_H + +/*** + * Inherits all methods from: none + */ +namespace LuaVehicle +{ + /** + * Returns true if the [Unit] passenger is on board + * + * @param [Unit] passenger + * @return bool isOnBoard + */ + int IsOnBoard(Eluna* E, Vehicle* vehicle) + { + Unit* passenger = E->CHECKOBJ(2); + + E->Push(passenger->IsOnVehicle(vehicle->GetBase())); + return 1; + } + + /** + * Returns the [Vehicle]'s owner + * + * @return [Unit] owner + */ + int GetOwner(Eluna* E, Vehicle* vehicle) + { + E->Push(vehicle->GetBase()); + return 1; + } + + /** + * Returns the [Vehicle]'s entry + * + * @return uint32 entry + */ + int GetEntry(Eluna* E, Vehicle* vehicle) + { + E->Push(vehicle->GetVehicleInfo()->ID); + return 1; + } + + /** + * Returns the [Vehicle]'s passenger in the specified seat + * + * @param int8 seat + * @return [Unit] passenger + */ + int GetPassenger(Eluna* E, Vehicle* vehicle) + { + int8 seatId = E->CHECKVAL(2); + E->Push(vehicle->GetPassenger(seatId)); + return 1; + } + + /** + * Adds [Unit] passenger to a specified seat in the [Vehicle] + * + * @param [Unit] passenger + * @param int8 seat + */ + int AddPassenger(Eluna* E, Vehicle* vehicle) + { + Unit* passenger = E->CHECKOBJ(2); + int8 seatId = E->CHECKVAL(3); + + vehicle->AddPassenger(passenger, seatId); + return 0; + } + + /** + * Removes [Unit] passenger from the [Vehicle] + * + * @param [Unit] passenger + */ + int RemovePassenger(Eluna* E, Vehicle* vehicle) + { + Unit* passenger = E->CHECKOBJ(2); + + vehicle->RemovePassenger(passenger); + return 0; + } + + ElunaRegister VehicleMethods[] = + { + // Getters + { "GetOwner", &LuaVehicle::GetOwner }, + { "GetEntry", &LuaVehicle::GetEntry }, + { "GetPassenger", &LuaVehicle::GetPassenger }, + + // Boolean + { "IsOnBoard", &LuaVehicle::IsOnBoard }, + + // Other + { "AddPassenger", &LuaVehicle::AddPassenger }, + { "RemovePassenger", &LuaVehicle::RemovePassenger } + }; +} + +#endif // VEHICLEMETHODS_H diff --git a/src/server/game/LuaEngine/methods/TrinityCore/WorldObjectMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/WorldObjectMethods.h new file mode 100644 index 0000000000..7dab89cae5 --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/WorldObjectMethods.h @@ -0,0 +1,1145 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef WORLDOBJECTMETHODS_H +#define WORLDOBJECTMETHODS_H + +#include "LuaValue.h" + +/*** + * Inherits all methods from: [Object] + */ +namespace LuaWorldObject +{ + /** + * Returns the name of the [WorldObject] + * + * @return string name + */ + int GetName(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetName()); + return 1; + } + + /** + * Returns the current [Map] object of the [WorldObject] + * + * @return [Map] mapObject + */ + int GetMap(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetMap()); + return 1; + } + + /** + * Returns the current phase of the [WorldObject] + * + * @return uint32 phase + */ + + + + /** + * Sets the [WorldObject]'s phase mask. + * + * @param uint32 phaseMask + * @param bool update = true : update visibility to nearby objects + */ + + + + /** + * Returns the current instance ID of the [WorldObject] + * + * @return uint32 instanceId + */ + int GetInstanceId(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetInstanceId()); + return 1; + } + + /** + * Returns the current area ID of the [WorldObject] + * + * @return uint32 areaId + */ + int GetAreaId(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetAreaId()); + return 1; + } + + /** + * Returns the current zone ID of the [WorldObject] + * + * @return uint32 zoneId + */ + int GetZoneId(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetZoneId()); + return 1; + } + + /** + * Returns the current map ID of the [WorldObject] + * + * @return uint32 mapId + */ + int GetMapId(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetMapId()); + return 1; + } + + /** + * Returns the current X coordinate of the [WorldObject] + * + * @return float x + */ + int GetX(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetPositionX()); + return 1; + } + + /** + * Returns the current Y coordinate of the [WorldObject] + * + * @return float y + */ + int GetY(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetPositionY()); + return 1; + } + + /** + * Returns the current Z coordinate of the [WorldObject] + * + * @return float z + */ + int GetZ(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetPositionZ()); + return 1; + } + + /** + * Returns the current orientation of the [WorldObject] + * + * @return float orientation / facing + */ + int GetO(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetOrientation()); + return 1; + } + + /** + * Returns the coordinates and orientation of the [WorldObject] + * + * @return float x : x coordinate of the [WorldObject] + * @return float y : y coordinate of the [WorldObject] + * @return float z : z coordinate (height) of the [WorldObject] + * @return float o : facing / orientation of the [WorldObject] + */ + int GetLocation(Eluna* E, WorldObject* obj) + { + E->Push(obj->GetPositionX()); + E->Push(obj->GetPositionY()); + E->Push(obj->GetPositionZ()); + E->Push(obj->GetOrientation()); + return 4; + } + + /** + * Returns the nearest [Player] object in sight of the [WorldObject] or within the given range + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param uint32 hostile = 0 : 0 both, 1 hostile, 2 friendly + * @param uint32 dead = 1 : 0 both, 1 alive, 2 dead + * + * @return [Player] nearestPlayer + */ + int GetNearestPlayer(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint32 hostile = E->CHECKVAL(3, 0); + uint32 dead = E->CHECKVAL(4, 1); + + Unit* target = NULL; + ElunaUtil::WorldObjectInRangeCheck checker(true, obj, range, TYPEMASK_PLAYER, 0, hostile, dead); + Trinity::UnitLastSearcher searcher(obj, target, checker); + Cell::VisitAllObjects(obj, searcher, range); + + E->Push(target); + return 1; + } + + /** + * Returns the nearest [GameObject] object in sight of the [WorldObject] or within the given range and/or with a specific entry ID + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param uint32 entryId = 0 : optionally set entry ID of game object to find + * @param uint32 hostile = 0 : 0 both, 1 hostile, 2 friendly + * + * @return [GameObject] nearestGameObject + */ + int GetNearestGameObject(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint32 entry = E->CHECKVAL(3, 0); + uint32 hostile = E->CHECKVAL(4, 0); + + GameObject* target = NULL; + ElunaUtil::WorldObjectInRangeCheck checker(true, obj, range, TYPEMASK_GAMEOBJECT, entry, hostile); + Trinity::GameObjectLastSearcher searcher(obj, target, checker); + Cell::VisitAllObjects(obj, searcher, range); + + E->Push(target); + return 1; + } + + /** + * Returns the nearest [Creature] object in sight of the [WorldObject] or within the given range and/or with a specific entry ID + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param uint32 entryId = 0 : optionally set entry ID of creature to find + * @param uint32 hostile = 0 : 0 both, 1 hostile, 2 friendly + * @param uint32 dead = 1 : 0 both, 1 alive, 2 dead + * + * @return [Creature] nearestCreature + */ + int GetNearestCreature(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint32 entry = E->CHECKVAL(3, 0); + uint32 hostile = E->CHECKVAL(4, 0); + uint32 dead = E->CHECKVAL(5, 1); + + Creature* target = NULL; + ElunaUtil::WorldObjectInRangeCheck checker(true, obj, range, TYPEMASK_UNIT, entry, hostile, dead); + Trinity::CreatureLastSearcher searcher(obj, target, checker); + Cell::VisitAllObjects(obj, searcher, range); + + E->Push(target); + return 1; + } + + /** + * Returns a table of [Player] objects in sight of the [WorldObject] or within the given range + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param uint32 hostile = 0 : 0 both, 1 hostile, 2 friendly + * @param uint32 dead = 1 : 0 both, 1 alive, 2 dead + * + * @return table playersInRange : table of [Player]s + */ + int GetPlayersInRange(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint32 hostile = E->CHECKVAL(3, 0); + uint32 dead = E->CHECKVAL(4, 1); + + std::list list; + ElunaUtil::WorldObjectInRangeCheck checker(false, obj, range, TYPEMASK_PLAYER, 0, hostile, dead); + Trinity::PlayerListSearcher searcher(obj, list, checker); + Cell::VisitAllObjects(obj, searcher, range); + + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + /** + * Returns a table of [Creature] objects in sight of the [WorldObject] or within the given range and/or with a specific entry ID + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param uint32 entryId = 0 : optionally set entry ID of creatures to find + * @param uint32 hostile = 0 : 0 both, 1 hostile, 2 friendly + * @param uint32 dead = 1 : 0 both, 1 alive, 2 dead + * + * @return table creaturesInRange : table of [Creature]s + */ + int GetCreaturesInRange(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint32 entry = E->CHECKVAL(3, 0); + uint32 hostile = E->CHECKVAL(4, 0); + uint32 dead = E->CHECKVAL(5, 1); + + std::list list; + ElunaUtil::WorldObjectInRangeCheck checker(false, obj, range, TYPEMASK_UNIT, entry, hostile, dead); + Trinity::CreatureListSearcher searcher(obj, list, checker); + Cell::VisitAllObjects(obj, searcher, range); + + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + /** + * Returns a table of [GameObject] objects in sight of the [WorldObject] or within the given range and/or with a specific entry ID + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param uint32 entryId = 0 : optionally set entry ID of game objects to find + * @param uint32 hostile = 0 : 0 both, 1 hostile, 2 friendly + * + * @return table gameObjectsInRange : table of [GameObject]s + */ + int GetGameObjectsInRange(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint32 entry = E->CHECKVAL(3, 0); + uint32 hostile = E->CHECKVAL(4, 0); + + std::list list; + ElunaUtil::WorldObjectInRangeCheck checker(false, obj, range, TYPEMASK_GAMEOBJECT, entry, hostile); + Trinity::GameObjectListSearcher searcher(obj, list, checker); + Cell::VisitAllObjects(obj, searcher, range); + + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + /** + * Returns nearest [WorldObject] in sight of the [WorldObject]. + * The distance, type, entry and hostility requirements the [WorldObject] must match can be passed. + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param [TypeMask] type = 0 : the [TypeMask] that the [WorldObject] must be. This can contain multiple types. 0 will be ingored + * @param uint32 entry = 0 : the entry of the [WorldObject], 0 will be ingored + * @param uint32 hostile = 0 : specifies whether the [WorldObject] needs to be 1 hostile, 2 friendly or 0 either + * @param uint32 dead = 1 : 0 both, 1 alive, 2 dead + * + * @return [WorldObject] worldObject + */ + int GetNearObject(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint16 type = E->CHECKVAL(3, 0); // TypeMask + uint32 entry = E->CHECKVAL(4, 0); + uint32 hostile = E->CHECKVAL(5, 0); // 0 none, 1 hostile, 2 friendly + uint32 dead = E->CHECKVAL(6, 1); // 0 both, 1 alive, 2 dead + + float x, y, z; + obj->GetPosition(x, y, z); + ElunaUtil::WorldObjectInRangeCheck checker(true, obj, range, type, entry, hostile, dead); + + WorldObject* target = NULL; + Trinity::WorldObjectLastSearcher searcher(obj, target, checker); + Cell::VisitAllObjects(obj, searcher, range); + + E->Push(target); + return 1; + } + + /** + * Returns a table of [WorldObject]s in sight of the [WorldObject]. + * The distance, type, entry and hostility requirements the [WorldObject] must match can be passed. + * + * @param float range = 533.33333 : optionally set range. Default range is grid size + * @param [TypeMask] type = 0 : the [TypeMask] that the [WorldObject] must be. This can contain multiple types. 0 will be ingored + * @param uint32 entry = 0 : the entry of the [WorldObject], 0 will be ingored + * @param uint32 hostile = 0 : specifies whether the [WorldObject] needs to be 1 hostile, 2 friendly or 0 either + * @param uint32 dead = 1 : 0 both, 1 alive, 2 dead + * + * @return table worldObjectList : table of [WorldObject]s + */ + int GetNearObjects(Eluna* E, WorldObject* obj) + { + float range = E->CHECKVAL(2, SIZE_OF_GRIDS); + uint16 type = E->CHECKVAL(3, 0); // TypeMask + uint32 entry = E->CHECKVAL(4, 0); + uint32 hostile = E->CHECKVAL(5, 0); // 0 none, 1 hostile, 2 friendly + uint32 dead = E->CHECKVAL(6, 1); // 0 both, 1 alive, 2 dead + + float x, y, z; + obj->GetPosition(x, y, z); + ElunaUtil::WorldObjectInRangeCheck checker(false, obj, range, type, entry, hostile, dead); + + std::list list; + Trinity::WorldObjectListSearcher searcher(obj, list, checker); + Cell::VisitAllObjects(obj, searcher, range); + + lua_createtable(E->L, list.size(), 0); + int tbl = lua_gettop(E->L); + uint32 i = 0; + + for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + E->Push(*it); + lua_rawseti(E->L, tbl, ++i); + } + + lua_settop(E->L, tbl); + return 1; + } + + /** + * Returns the distance from this [WorldObject] to another [WorldObject], or from this [WorldObject] to a point in 3d space. + * + * The function takes into account the given object sizes. See also [WorldObject:GetExactDistance], [WorldObject:GetDistance2d] + * + * @proto dist = (obj) + * @proto dist = (x, y, z) + * + * @param [WorldObject] obj + * @param float x : the X-coordinate of the point + * @param float y : the Y-coordinate of the point + * @param float z : the Z-coordinate of the point + * + * @return float dist : the distance in yards + */ + int GetDistance(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2, false); + if (target) + E->Push(obj->GetDistance(target)); + else + { + float X = E->CHECKVAL(2); + float Y = E->CHECKVAL(3); + float Z = E->CHECKVAL(4); + E->Push(obj->GetDistance(X, Y, Z)); + } + return 1; + } + + /** + * Returns the distance from this [WorldObject] to another [WorldObject], or from this [WorldObject] to a point in 3d space. + * + * The function does not take into account the given object sizes, which means only the object coordinates are compared. See also [WorldObject:GetDistance], [WorldObject:GetDistance2d] + * + * @proto dist = (obj) + * @proto dist = (x, y, z) + * + * @param [WorldObject] obj + * @param float x : the X-coordinate of the point + * @param float y : the Y-coordinate of the point + * @param float z : the Z-coordinate of the point + * + * @return float dist : the distance in yards + */ + int GetExactDistance(Eluna* E, WorldObject* obj) + { + float x, y, z; + obj->GetPosition(x, y, z); + WorldObject* target = E->CHECKOBJ(2, false); + if (target) + { + float x2, y2, z2; + target->GetPosition(x2, y2, z2); + x -= x2; + y -= y2; + z -= z2; + } + else + { + x -= E->CHECKVAL(2); + y -= E->CHECKVAL(3); + z -= E->CHECKVAL(4); + } + + E->Push(std::sqrt(x*x + y*y + z*z)); + return 1; + } + + /** + * Returns the distance from this [WorldObject] to another [WorldObject], or from this [WorldObject] to a point in 2d space. + * + * The function takes into account the given object sizes. See also [WorldObject:GetDistance], [WorldObject:GetExactDistance2d] + * + * @proto dist = (obj) + * @proto dist = (x, y) + * + * @param [WorldObject] obj + * @param float x : the X-coordinate of the point + * @param float y : the Y-coordinate of the point + * + * @return float dist : the distance in yards + */ + int GetDistance2d(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2, false); + if (target) + E->Push(obj->GetDistance2d(target)); + else + { + float X = E->CHECKVAL(2); + float Y = E->CHECKVAL(3); + E->Push(obj->GetDistance2d(X, Y)); + } + return 1; + } + + /** + * Returns the distance from this [WorldObject] to another [WorldObject], or from this [WorldObject] to a point in 2d space. + * + * The function does not take into account the given object sizes, which means only the object coordinates are compared. See also [WorldObject:GetDistance], [WorldObject:GetDistance2d] + * + * @proto dist = (obj) + * @proto dist = (x, y) + * + * @param [WorldObject] obj + * @param float x : the X-coordinate of the point + * @param float y : the Y-coordinate of the point + * + * @return float dist : the distance in yards + */ + int GetExactDistance2d(Eluna* E, WorldObject* obj) + { + float x, y, z; + obj->GetPosition(x, y, z); + WorldObject* target = E->CHECKOBJ(2, false); + if (target) + { + float x2, y2, z2; + target->GetPosition(x2, y2, z2); + x -= x2; + y -= y2; + } + else + { + x -= E->CHECKVAL(2); + y -= E->CHECKVAL(3); + } + + E->Push(std::sqrt(x*x + y*y)); + return 1; + } + + /** + * Returns the x, y and z of a point dist away from the [WorldObject]. + * + * @param float distance : specifies the distance of the point from the [WorldObject] in yards + * @param float angle : specifies the angle of the point relative to the orientation / facing of the [WorldObject] in radians + * + * @return float x + * @return float y + * @return float z + */ + int GetRelativePoint(Eluna* E, WorldObject* obj) + { + float dist = E->CHECKVAL(2); + float rad = E->CHECKVAL(3); + + float x, y, z; + obj->GetClosePoint(x, y, z, 0.0f, dist, rad); + + E->Push(x); + E->Push(y); + E->Push(z); + return 3; + } + + /** + * Returns the angle between this [WorldObject] and another [WorldObject] or a point. + * + * The angle is the angle between two points and orientation will be ignored. + * + * @proto dist = (obj) + * @proto dist = (x, y) + * + * @param [WorldObject] object + * @param float x + * @param float y + * + * @return float angle : angle in radians in range 0..2*pi + */ + #if 0 + int GetAngle(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2, false); + + if (target) + E->Push(obj->GetAbsoluteAngle(target)); + else + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + E->Push(obj->GetAbsoluteAngle(x, y)); + } + + return 1; + } + #endif + /** + * Sends a [WorldPacket] to [Player]s in sight of the [WorldObject]. + * + * @param [WorldPacket] packet + */ + int SendPacket(Eluna* E, WorldObject* obj) + { + WorldPacket* data = E->CHECKOBJ(2); + + obj->SendMessageToSet(data, true); + return 0; + } + + /** + * Spawns a [GameObject] at specified location. + * + * @param uint32 entry : [GameObject] entry ID + * @param float x + * @param float y + * @param float z + * @param float o + * @param uint32 respawnDelay = 30 : respawn time in seconds + * @return [GameObject] gameObject + */ + #if 0 + int SummonGameObject(Eluna* E, WorldObject* obj) + { + uint32 entry = E->CHECKVAL(2); + float x = E->CHECKVAL(3); + float y = E->CHECKVAL(4); + float z = E->CHECKVAL(5); + float o = E->CHECKVAL(6); + uint32 respawnDelay = E->CHECKVAL(7, 30); + + QuaternionData rot = QuaternionData::fromEulerAnglesZYX(o, 0.f, 0.f); + + E->Push(obj->SummonGameObject(entry, Position(x, y, z, o), rot, Seconds(respawnDelay))); + return 1; + } + #endif + /** + * Spawns the creature at specified location. + * + * @table + * @columns [TempSummonType, ID, Comment] + * @values [TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 1, "despawns after a specified time OR when the creature disappears"] + * @values [TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 2, "despawns after a specified time OR when the creature dies"] + * @values [TEMPSUMMON_TIMED_DESPAWN, 3, "despawns after a specified time"] + * @values [TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 4, "despawns after a specified time after the creature is out of combat"] + * @values [TEMPSUMMON_CORPSE_DESPAWN, 5, "despawns instantly after death"] + * @values [TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6, "despawns after a specified time after death"] + * @values [TEMPSUMMON_DEAD_DESPAWN, 7, "despawns when the creature disappears"] + * @values [TEMPSUMMON_MANUAL_DESPAWN, 8, "despawns when UnSummon() is called"] + * @values [TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN, 9, "despawns after a specified time (OOC) OR when the creature dies"] + * @values [TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, 10, "despawns after a specified time (OOC) OR when the creature disappears"] + * + * @param uint32 entry : [Creature]'s entry ID + * @param float x + * @param float y + * @param float z + * @param float o + * @param [TempSummonType] spawnType = MANUAL_DESPAWN : defines how and when the creature despawns + * @param uint32 despawnTimer = 0 : despawn time in milliseconds + * @return [Creature] spawnedCreature + */ + #if 0 + int SpawnCreature(Eluna* E, WorldObject* obj) + { + uint32 entry = E->CHECKVAL(2); + float x = E->CHECKVAL(3); + float y = E->CHECKVAL(4); + float z = E->CHECKVAL(5); + float o = E->CHECKVAL(6); + uint32 spawnType = E->CHECKVAL(7, 8); + uint32 despawnTimer = E->CHECKVAL(8, 0); + + TempSummonType type; + switch (spawnType) + { + case 1: + type = TEMPSUMMON_TIMED_OR_DEAD_DESPAWN; + break; + case 2: + type = TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN; + break; + case 3: + type = TEMPSUMMON_TIMED_DESPAWN; + break; + case 4: + type = TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT; + break; + case 5: + type = TEMPSUMMON_CORPSE_DESPAWN; + break; + case 6: + type = TEMPSUMMON_CORPSE_TIMED_DESPAWN; + break; + case 7: + type = TEMPSUMMON_DEAD_DESPAWN; + break; + case 8: + type = TEMPSUMMON_MANUAL_DESPAWN; + break; + default: + return luaL_argerror(E->L, 7, "valid SpawnType expected"); + } + + E->Push(obj->SummonCreature(entry, x, y, z, o, type, Milliseconds(despawnTimer))); + return 1; + } + #endif + /** + * Registers a timed event to the [WorldObject] + * When the passed function is called, the parameters `(eventId, delay, repeats, worldobject)` are passed to it. + * Repeats will decrease on each call if the event does not repeat indefinitely + * + * Note that for [Creature] and [GameObject] the timed event timer ticks only if the creature is in sight of someone + * For all [WorldObject]s the timed events are removed when the object is destoryed. This means that for example a [Player]'s events are removed on logout. + * + * local function Timed(eventid, delay, repeats, worldobject) + * print(worldobject:GetName()) + * end + * worldobject:RegisterEvent(Timed, 1000, 5) -- do it after 1 second 5 times + * worldobject:RegisterEvent(Timed, {1000, 10000}, 0) -- do it after 1 to 10 seconds forever + * + * @proto eventId = (function, delay) + * @proto eventId = (function, delaytable) + * @proto eventId = (function, delay, repeats) + * @proto eventId = (function, delaytable, repeats) + * + * @param function function : function to trigger when the time has passed + * @param uint32 delay : set time in milliseconds for the event to trigger + * @param table delaytable : a table `{min, max}` containing the minimum and maximum delay time + * @param uint32 repeats = 1 : how many times for the event to repeat, 0 is infinite + * @return int eventId : unique ID for the timed event used to cancel it or nil + */ + #if 0 + int RegisterEvent(Eluna* E, WorldObject* obj) + { + luaL_checktype(E->L, 2, LUA_TFUNCTION); + uint32 min, max; + if (lua_istable(E->L, 3)) + { + E->Push(1); + lua_gettable(E->L, 3); + min = E->CHECKVAL(-1); + E->Push(2); + lua_gettable(E->L, 3); + max = E->CHECKVAL(-1); + lua_pop(E->L, 2); + } + else + min = max = E->CHECKVAL(3); + uint32 repeats = E->CHECKVAL(4, 1); + + if (min > max) + return luaL_argerror(E->L, 3, "min is bigger than max delay"); + + lua_pushvalue(E->L, 2); + int functionRef = luaL_ref(E->L, LUA_REGISTRYINDEX); + if (functionRef != LUA_REFNIL && functionRef != LUA_NOREF) + { + ElunaEventProcessor* proc = obj->GetElunaEvents(E->GetBoundMapId()); + if (!proc) + { + luaL_unref(E->L, LUA_REGISTRYINDEX, functionRef); + E->Push(); + return 1; + } + + proc->AddEvent(functionRef, min, max, repeats); + E->Push(functionRef); + } + return 1; + } + #endif + /** + * Removes the timed event from a [WorldObject] by the specified event ID + * + * @param int eventId : event Id to remove + */ + #if 0 + int RemoveEventById(Eluna* E, WorldObject* obj) + { + int eventId = E->CHECKVAL(2); + + ElunaEventProcessor* proc = obj->GetElunaEvents(E->GetBoundMapId()); + if (!proc) + return 0; + + proc->SetState(eventId, LUAEVENT_STATE_ABORT); + return 0; + } + #endif + /** + * Removes all timed events from a [WorldObject] + */ + #if 0 + int RemoveEvents(Eluna* E, WorldObject* obj) + { + ElunaEventProcessor* proc = obj->GetElunaEvents(E->GetBoundMapId()); + if (!proc) + return 0; + + proc->SetStates(LUAEVENT_STATE_ABORT); + return 0; + } + #endif + /** + * Returns true if the given [WorldObject] or coordinates are in the [WorldObject]'s line of sight + * + * @proto isInLoS = (worldobject) + * @proto isInLoS = (x, y, z) + * + * @param [WorldObject] worldobject + * @param float x + * @param float y + * @param float z + * @return bool isInLoS + */ + int IsWithinLoS(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2, false); + + if (target) + E->Push(obj->IsWithinLOSInMap(target)); + else + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + E->Push(obj->IsWithinLOS(x, y, z)); + } + + return 1; + } + + /** + * Returns true if the [WorldObject]s are on the same map + * + * @param [WorldObject] worldobject + * @return bool isInMap + */ + int IsInMap(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2, true); + E->Push(obj->IsInMap(target)); + return 1; + } + + /** + * Returns true if the point is in the given distance of the [WorldObject] + * + * Notice that the distance is measured from the edge of the [WorldObject]. + * + * @param float x + * @param float y + * @param float z + * @param float distance + * @return bool isInDistance + */ + int IsWithinDist3d(Eluna* E, WorldObject* obj) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + float dist = E->CHECKVAL(5); + E->Push(obj->IsWithinDist3d(x, y, z, dist)); + return 1; + } + + /** + * Returns true if the point is in the given distance of the [WorldObject] + * + * The distance is measured only in x,y coordinates. + * Notice that the distance is measured from the edge of the [WorldObject]. + * + * @param float x + * @param float y + * @param float distance + * @return bool isInDistance + */ + int IsWithinDist2d(Eluna* E, WorldObject* obj) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float dist = E->CHECKVAL(4); + E->Push(obj->IsWithinDist2d(x, y, dist)); + return 1; + } + + /** + * Returns true if the target is in the given distance of the [WorldObject] + * + * Notice that the distance is measured from the edge of the [WorldObject]s. + * + * @param [WorldObject] target + * @param float distance + * @param bool is3D = true : if false, only x,y coordinates used for checking + * @return bool isInDistance + */ + int IsWithinDist(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2, true); + float distance = E->CHECKVAL(3); + bool is3D = E->CHECKVAL(4, true); + E->Push(obj->IsWithinDist(target, distance, is3D)); + return 1; + } + + /** + * Returns true if the [WorldObject] is on the same map and within given distance + * + * Notice that the distance is measured from the edge of the [WorldObject]s. + * + * @param [WorldObject] target + * @param float distance + * @param bool is3D = true : if false, only x,y coordinates used for checking + * @return bool isInDistance + */ + int IsWithinDistInMap(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2); + float distance = E->CHECKVAL(3); + bool is3D = E->CHECKVAL(4, true); + + E->Push(obj->IsWithinDistInMap(target, distance, is3D)); + return 1; + } + + /** + * Returns true if the target is within given range + * + * Notice that the distance is measured from the edge of the [WorldObject]s. + * + * @param [WorldObject] target + * @param float minrange + * @param float maxrange + * @param bool is3D = true : if false, only x,y coordinates used for checking + * @return bool isInDistance + */ + int IsInRange(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2); + float minrange = E->CHECKVAL(3); + float maxrange = E->CHECKVAL(4); + bool is3D = E->CHECKVAL(5, true); + + E->Push(obj->IsInRange(target, minrange, maxrange, is3D)); + return 1; + } + + /** + * Returns true if the point is within given range + * + * Notice that the distance is measured from the edge of the [WorldObject]. + * + * @param float x + * @param float y + * @param float minrange + * @param float maxrange + * @return bool isInDistance + */ + int IsInRange2d(Eluna* E, WorldObject* obj) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float minrange = E->CHECKVAL(4); + float maxrange = E->CHECKVAL(5); + + E->Push(obj->IsInRange2d(x, y, minrange, maxrange)); + return 1; + } + + /** + * Returns true if the point is within given range + * + * Notice that the distance is measured from the edge of the [WorldObject]. + * + * @param float x + * @param float y + * @param float z + * @param float minrange + * @param float maxrange + * @return bool isInDistance + */ + int IsInRange3d(Eluna* E, WorldObject* obj) + { + float x = E->CHECKVAL(2); + float y = E->CHECKVAL(3); + float z = E->CHECKVAL(4); + float minrange = E->CHECKVAL(5); + float maxrange = E->CHECKVAL(6); + + E->Push(obj->IsInRange3d(x, y, z, minrange, maxrange)); + return 1; + } + + /** + * Returns true if the target is in the given arc in front of the [WorldObject] + * + * @param [WorldObject] target + * @param float arc = pi + * @return bool isInFront + */ + int IsInFront(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2); + float arc = E->CHECKVAL(3, static_cast(M_PI)); + + E->Push(obj->isInFront(target, arc)); + return 1; + } + + /** + * Returns true if the target is in the given arc behind the [WorldObject] + * + * @param [WorldObject] target + * @param float arc = pi + * @return bool isInBack + */ + int IsInBack(Eluna* E, WorldObject* obj) + { + WorldObject* target = E->CHECKOBJ(2); + float arc = E->CHECKVAL(3, static_cast(M_PI)); + + E->Push(obj->isInBack(target, arc)); + return 1; + } + + #if 0 + int PlayMusic(Eluna* E, WorldObject* obj) + { + uint32 musicid = E->CHECKVAL(2); + Player* player = E->CHECKOBJ(3, false); + + if (!sSoundEntriesStore.LookupEntry(musicid)) + musicid = 0; + + WorldPackets::Misc::PlayMusic playMusic(musicid); + const WorldPacket* data = playMusic.Write(); + + if (player) + player->SendDirectMessage(data); + else + obj->SendMessageToSet(data, true); + + return 0; + } + #endif + + #if 0 + int PlayDirectSound(Eluna* E, WorldObject* obj) + { + uint32 soundId = E->CHECKVAL(2); + Player* player = E->CHECKOBJ(3, false); + if (!sSoundEntriesStore.LookupEntry(soundId)) + return 0; + + if (player) + obj->PlayDirectSound(soundId, player); + else + obj->PlayDirectSound(soundId); + + return 0; + } + #endif + + #if 0 + int PlayDistanceSound(Eluna* E, WorldObject* obj) + { + uint32 soundId = E->CHECKVAL(2); + Player* player = E->CHECKOBJ(3, false); + if (!sSoundEntriesStore.LookupEntry(soundId)) + return 0; + + if (player) + obj->PlayDistanceSound(soundId, player); + else + obj->PlayDistanceSound(soundId); + + return 0; + } + #endif + #if 0 + int Data(Eluna* E, WorldObject* obj) + { + return LuaVal::PushLuaVal(E->L, obj->lua_data); + } + #endif + ElunaRegister WorldObjectMethods[] = + { + // Getters + { "GetName", &LuaWorldObject::GetName }, + { "GetMap", &LuaWorldObject::GetMap }, + + { "GetInstanceId", &LuaWorldObject::GetInstanceId }, + { "GetAreaId", &LuaWorldObject::GetAreaId }, + { "GetZoneId", &LuaWorldObject::GetZoneId }, + { "GetMapId", &LuaWorldObject::GetMapId }, + { "GetX", &LuaWorldObject::GetX }, + { "GetY", &LuaWorldObject::GetY }, + { "GetZ", &LuaWorldObject::GetZ }, + { "GetO", &LuaWorldObject::GetO }, + { "GetLocation", &LuaWorldObject::GetLocation }, + { "GetPlayersInRange", &LuaWorldObject::GetPlayersInRange }, + { "GetCreaturesInRange", &LuaWorldObject::GetCreaturesInRange }, + { "GetGameObjectsInRange", &LuaWorldObject::GetGameObjectsInRange }, + { "GetNearestPlayer", &LuaWorldObject::GetNearestPlayer }, + { "GetNearestGameObject", &LuaWorldObject::GetNearestGameObject }, + { "GetNearestCreature", &LuaWorldObject::GetNearestCreature }, + { "GetNearObject", &LuaWorldObject::GetNearObject }, + { "GetNearObjects", &LuaWorldObject::GetNearObjects }, + { "GetDistance", &LuaWorldObject::GetDistance }, + { "GetExactDistance", &LuaWorldObject::GetExactDistance }, + { "GetDistance2d", &LuaWorldObject::GetDistance2d }, + { "GetExactDistance2d", &LuaWorldObject::GetExactDistance2d }, + { "GetRelativePoint", &LuaWorldObject::GetRelativePoint }, + + + // Boolean + { "IsWithinLoS", &LuaWorldObject::IsWithinLoS }, + { "IsInMap", &LuaWorldObject::IsInMap }, + { "IsWithinDist3d", &LuaWorldObject::IsWithinDist3d }, + { "IsWithinDist2d", &LuaWorldObject::IsWithinDist2d }, + { "IsWithinDist", &LuaWorldObject::IsWithinDist }, + { "IsWithinDistInMap", &LuaWorldObject::IsWithinDistInMap }, + { "IsInRange", &LuaWorldObject::IsInRange }, + { "IsInRange2d", &LuaWorldObject::IsInRange2d }, + { "IsInRange3d", &LuaWorldObject::IsInRange3d }, + { "IsInFront", &LuaWorldObject::IsInFront }, + { "IsInBack", &LuaWorldObject::IsInBack }, + + // Other + + + { "SendPacket", &LuaWorldObject::SendPacket } + + + //{ "PlayMusic", &LuaWorldObject::PlayMusic } + + //{ "Data", &LuaWorldObject::Data } + }; +}; +#endif diff --git a/src/server/game/LuaEngine/methods/TrinityCore/WorldPacketMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/WorldPacketMethods.h new file mode 100644 index 0000000000..6fe027d78d --- /dev/null +++ b/src/server/game/LuaEngine/methods/TrinityCore/WorldPacketMethods.h @@ -0,0 +1,347 @@ +/* +* Copyright (C) 2010 - 2024 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef WORLDPACKETMETHODS_H +#define WORLDPACKETMETHODS_H + +/*** + * A packet used to pass messages between the server and a client. + * + * Each packet has an opcode that determines the type of message being sent, + * e.g. if a CMSG_LOGOUT_REQUEST packet is sent to the server, + * the client has sent a message that its [Player] wants to logout. + * + * The packet can contain further data, the format of which depends on the opcode. + * + * Inherits all methods from: none + */ +namespace LuaPacket +{ + /** + * Returns the opcode of the [WorldPacket]. + * + * @return uint16 opcode + */ + #if 0 + int GetOpcode(Eluna* E, WorldPacket* packet) + { + E->Push(packet->GetOpcode()); + return 1; + } + #endif + /** + * Returns the size of the [WorldPacket]. + * + * @return uint32 size + */ + int GetSize(Eluna* E, WorldPacket* packet) + { + E->Push(packet->size()); + return 1; + } + + /** + * Sets the opcode of the [WorldPacket] to the specified opcode. + * + * @param [Opcodes] opcode : see Opcodes.h for all known opcodes + */ + #if 0 + int SetOpcode(Eluna* E, WorldPacket* packet) + { + uint32 opcode = E->CHECKVAL(2); + if (opcode >= NUM_MSG_TYPES) + return luaL_argerror(E->L, 2, "valid opcode expected"); + + packet->SetOpcode((OpcodesList)opcode); + return 0; + } + #endif + /** + * Reads and returns a signed 8-bit integer value from the [WorldPacket]. + * + * @return int8 value + */ + int ReadByte(Eluna* E, WorldPacket* packet) + { + int8 _byte; + (*packet) >> _byte; + E->Push(_byte); + return 1; + } + + /** + * Reads and returns an unsigned 8-bit integer value from the [WorldPacket]. + * + * @return uint8 value + */ + int ReadUByte(Eluna* E, WorldPacket* packet) + { + uint8 _ubyte; + (*packet) >> _ubyte; + E->Push(_ubyte); + return 1; + } + + /** + * Reads and returns a signed 16-bit integer value from the [WorldPacket]. + * + * @return int16 value + */ + int ReadShort(Eluna* E, WorldPacket* packet) + { + int16 _short; + (*packet) >> _short; + E->Push(_short); + return 1; + } + + /** + * Reads and returns an unsigned 16-bit integer value from the [WorldPacket]. + * + * @return uint16 value + */ + int ReadUShort(Eluna* E, WorldPacket* packet) + { + uint16 _ushort; + (*packet) >> _ushort; + E->Push(_ushort); + return 1; + } + + /** + * Reads and returns a signed 32-bit integer value from the [WorldPacket]. + * + * @return int32 value + */ + int ReadLong(Eluna* E, WorldPacket* packet) + { + int32 _long; + (*packet) >> _long; + E->Push(_long); + return 1; + } + + /** + * Reads and returns an unsigned 32-bit integer value from the [WorldPacket]. + * + * @return uint32 value + */ + int ReadULong(Eluna* E, WorldPacket* packet) + { + uint32 _ulong; + (*packet) >> _ulong; + E->Push(_ulong); + return 1; + } + + /** + * Reads and returns a single-precision floating-point value from the [WorldPacket]. + * + * @return float value + */ + int ReadFloat(Eluna* E, WorldPacket* packet) + { + float _val; + (*packet) >> _val; + E->Push(_val); + return 1; + } + + /** + * Reads and returns a double-precision floating-point value from the [WorldPacket]. + * + * @return double value + */ + int ReadDouble(Eluna* E, WorldPacket* packet) + { + double _val; + (*packet) >> _val; + E->Push(_val); + return 1; + } + + /** + * Reads and returns an unsigned 64-bit integer value from the [WorldPacket]. + * + * @return ObjectGuid value : value returned as string + */ + int ReadGUID(Eluna* E, WorldPacket* packet) + { + ObjectGuid guid; + (*packet) >> guid; + E->Push(guid); + return 1; + } + + /** + * Reads and returns a string value from the [WorldPacket]. + * + * @return string value + */ + int ReadString(Eluna* E, WorldPacket* packet) + { + std::string _val; + (*packet) >> _val; + E->Push(_val); + return 1; + } + + /** + * Writes an unsigned 64-bit integer value to the [WorldPacket]. + * + * @param ObjectGuid value : the value to be written to the [WorldPacket] + */ + int WriteGUID(Eluna* E, WorldPacket* packet) + { + ObjectGuid guid = E->CHECKVAL(2); + (*packet) << guid; + return 0; + } + + /** + * Writes a string to the [WorldPacket]. + * + * @param string value : the string to be written to the [WorldPacket] + */ + int WriteString(Eluna* E, WorldPacket* packet) + { + std::string _val = E->CHECKVAL(2); + (*packet) << _val; + return 0; + } + + /** + * Writes a signed 8-bit integer value to the [WorldPacket]. + * + * @param int8 value : the int8 value to be written to the [WorldPacket] + */ + int WriteByte(Eluna* E, WorldPacket* packet) + { + int8 byte = E->CHECKVAL(2); + (*packet) << byte; + return 0; + } + + /** + * Writes an unsigned 8-bit integer value to the [WorldPacket]. + * + * @param uint8 value : the uint8 value to be written to the [WorldPacket] + */ + int WriteUByte(Eluna* E, WorldPacket* packet) + { + uint8 byte = E->CHECKVAL(2); + (*packet) << byte; + return 0; + } + + /** + * Writes a signed 16-bit integer value to the [WorldPacket]. + * + * @param int16 value : the int16 value to be written to the [WorldPacket] + */ + int WriteShort(Eluna* E, WorldPacket* packet) + { + int16 _short = E->CHECKVAL(2); + (*packet) << _short; + return 0; + } + + /** + * Writes an unsigned 16-bit integer value to the [WorldPacket]. + * + * @param uint16 value : the uint16 value to be written to the [WorldPacket] + */ + int WriteUShort(Eluna* E, WorldPacket* packet) + { + uint16 _ushort = E->CHECKVAL(2); + (*packet) << _ushort; + return 0; + } + + /** + * Writes a signed 32-bit integer value to the [WorldPacket]. + * + * @param int32 value : the int32 value to be written to the [WorldPacket] + */ + int WriteLong(Eluna* E, WorldPacket* packet) + { + int32 _long = E->CHECKVAL(2); + (*packet) << _long; + return 0; + } + + /** + * Writes an unsigned 32-bit integer value to the [WorldPacket]. + * + * @param uint32 value : the uint32 value to be written to the [WorldPacket] + */ + int WriteULong(Eluna* E, WorldPacket* packet) + { + uint32 _ulong = E->CHECKVAL(2); + (*packet) << _ulong; + return 0; + } + + /** + * Writes a 32-bit floating-point value to the [WorldPacket]. + * + * @param float value : the float value to be written to the [WorldPacket] + */ + int WriteFloat(Eluna* E, WorldPacket* packet) + { + float _val = E->CHECKVAL(2); + (*packet) << _val; + return 0; + } + + /** + * Writes a 64-bit floating-point value to the [WorldPacket]. + * + * @param double value : the double value to be written to the [WorldPacket] + */ + int WriteDouble(Eluna* E, WorldPacket* packet) + { + double _val = E->CHECKVAL(2); + (*packet) << _val; + return 0; + } + + ElunaRegister PacketMethods[] = + { + // Getters + + { "GetSize", &LuaPacket::GetSize }, + + // Setters + + + // Readers + { "ReadByte", &LuaPacket::ReadByte }, + { "ReadUByte", &LuaPacket::ReadUByte }, + { "ReadShort", &LuaPacket::ReadShort }, + { "ReadUShort", &LuaPacket::ReadUShort }, + { "ReadLong", &LuaPacket::ReadLong }, + { "ReadULong", &LuaPacket::ReadULong }, + { "ReadGUID", &LuaPacket::ReadGUID }, + { "ReadString", &LuaPacket::ReadString }, + { "ReadFloat", &LuaPacket::ReadFloat }, + { "ReadDouble", &LuaPacket::ReadDouble }, + + // Writers + { "WriteByte", &LuaPacket::WriteByte }, + { "WriteUByte", &LuaPacket::WriteUByte }, + { "WriteShort", &LuaPacket::WriteShort }, + { "WriteUShort", &LuaPacket::WriteUShort }, + { "WriteLong", &LuaPacket::WriteLong }, + { "WriteULong", &LuaPacket::WriteULong }, + { "WriteGUID", &LuaPacket::WriteGUID }, + { "WriteString", &LuaPacket::WriteString }, + { "WriteFloat", &LuaPacket::WriteFloat }, + { "WriteDouble", &LuaPacket::WriteDouble } + }; +}; + +#endif diff --git a/src/server/game/LuaEngine/modules/.gitignore b/src/server/game/LuaEngine/modules/.gitignore new file mode 100644 index 0000000000..5c9682fc47 --- /dev/null +++ b/src/server/game/LuaEngine/modules/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!readme.md \ No newline at end of file diff --git a/src/server/game/LuaEngine/modules/readme.md b/src/server/game/LuaEngine/modules/readme.md new file mode 100644 index 0000000000..166367d1ba --- /dev/null +++ b/src/server/game/LuaEngine/modules/readme.md @@ -0,0 +1,75 @@ +**This directory is used for custom C modules.** + +*Thanks to [@anzz1](https://github.com/anzz1) for the original work!* + +**Quick guide below:** +- Create a subdirectory with your module name. +- Add your module files (.c, .h etc). +- Run cmake, this will automatically add a new project which compiles the appropriate .dll or .so file. +- Place the .dll or .so file inside your lua_scripts directory. +- Use "require modulename" in whichever script you use the module. + + +**Below is an example module:** + +example_module.c +```C +#include +#include + +#if defined(WIN32) +#define MODULEAPI __declspec(dllexport) int +#else +#define MODULEAPI int +#endif + +/* + * example_module.say_hello() will return the below defined message. + */ +const char MESSAGE[] = "hello world!"; +int say_hello(lua_State *L) { + lua_pushstring(L, MESSAGE); + return 1; +} + +/* + * the unload function will run on the Lua state close. + * this can then be used to save, exit cleanly, etc. + */ +int Unload(lua_State* L) { + luaL_dostring(L, "print('goodbye world!');"); + return 0; +} + +/* + * functions is a list of C functions and their bound function name in the Lua namespace + */ +static const struct luaL_Reg functions [] = { + {"say_hello", say_hello}, + {"unload", Unload}, + {NULL, NULL} +}; + +/* + * all modules need a luaopen_xxxx function, where xxxx is the name of the module. + * this is automatically executed on require. + */ +MODULEAPI luaopen_example_module(lua_State* L) { + // register the example module and its function list in the global namespace + luaL_register(L, "example_module", functions); + + // run the Unload function on ELUNA_EVENT_ON_LUA_STATE_CLOSE + if (luaL_loadstring(L, "RegisterServerEvent(16, example_module.unload)") != 0) + return lua_error(L); + lua_call(L, 0, 0); + return 1; +} +``` +This would generate into example_module.dll/.so, and you could call this as shown below: + +```Lua +require "example_module" + +print("Testing say_hello()") +print(example_module.say_hello()) +``` diff --git a/src/server/game/PlayerBot/FieldBotMgr.cpp b/src/server/game/PlayerBot/FieldBotMgr.cpp index a240ec25ad..e46846c7a6 100644 --- a/src/server/game/PlayerBot/FieldBotMgr.cpp +++ b/src/server/game/PlayerBot/FieldBotMgr.cpp @@ -1,4 +1,4 @@ -/* +/* * This file is part of the DestinyCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it @@ -55,16 +55,16 @@ void WorldPoster::SendOnceGlobalPoster() void WorldPoster::InitializePoster() { m_AllPosterContent.clear(); - PushPoster("ʹõԱ̡ӱƼħֱӪ꡿Ʒ"); - PushPoster("Ա ӱƼ88ƷֻŰϣս"); - PushPoster("Ա ӱƼ88Ʒֻսʿʿʦְҵ"); - PushPoster("Ա ӱƼ88ƷҰսϵͳ"); - PushPoster("Ա ӱƼ88Ʒ³ϵͳ"); - PushPoster("Ա ӱƼ88Ʒž"); - PushPoster("Ա ӱƼ88Ʒ޷лŶӣ޷ŶӸ"); - PushPoster("ϲǵIJƷ빺"); - PushPoster("ʽԱϵӱƼ88QQϵ277922486"); - PushPoster("κϵʽƭ"); + PushPoster(" ʹ õ Ա ̡ ӱ Ƽ ħ ֱӪ ꡿ Ʒ"); + PushPoster(" Ա ӱ Ƽ 88 Ʒֻ Ű ϣս "); + PushPoster(" Ա ӱ Ƽ 88 Ʒֻ սʿ ʿ ʦ ְҵ"); + PushPoster(" Ա ӱ Ƽ 88 Ʒ Ұ սϵͳ"); + PushPoster(" Ա ӱ Ƽ 88 Ʒ ³ ϵͳ"); + PushPoster(" Ա ӱ Ƽ 88 Ʒ ž "); + PushPoster(" Ա ӱ Ƽ 88 Ʒ ޷ л Ŷӣ ޷ ŶӸ "); + PushPoster(" ϲ ǵIJ Ʒ 빺 "); + PushPoster(" ʽ Ա ϵ ӱ Ƽ 88 QQ ϵ277922486"); + PushPoster(" κ ϵ ʽ ƭ "); } void WorldPoster::PushPoster(std::string content) diff --git a/src/server/game/PlayerBot/PlayerBotMgr.cpp b/src/server/game/PlayerBot/PlayerBotMgr.cpp index 5ecdfd422f..6535758bfd 100644 --- a/src/server/game/PlayerBot/PlayerBotMgr.cpp +++ b/src/server/game/PlayerBot/PlayerBotMgr.cpp @@ -38,6 +38,65 @@ #include //#include + + +#include // 确保引入了 atof 和 atoi + +// ================== 新增:读取配置并按概率生成等级的函数 ================== +static uint32 GenerateRandomBotLevel() +{ + // 读取配置文件,如果没配,默认生成 110 级 (概率 1.0) + std::string distStr = sConfigMgr->GetStringDefault("PlayerBot.LevelDistribution", "110-110:1.0"); + + // 按照逗号分割字符串 + std::vector ranges; + boost::split(ranges, distStr, boost::is_any_of(",")); + + // 生成 0.0000 到 1.0000 之间的随机浮点数 + float randVal = (float)urand(0, 10000) / 10000.0f; + float cumulative = 0.0f; + + for (const std::string& rangeStr : ranges) + { + std::vector pair; + boost::split(pair, rangeStr, boost::is_any_of(":")); + + if (pair.size() == 2) + { + // 获取该区间的概率 + float prob = (float)atof(pair[1].c_str()); + cumulative += prob; + + // 如果随机数落在这个概率区间内 + if (randVal <= cumulative) + { + std::vector minmax; + boost::split(minmax, pair[0], boost::is_any_of("-")); + + if (minmax.size() == 2) + { + uint32 minLvl = atoi(minmax[0].c_str()); + uint32 maxLvl = atoi(minmax[1].c_str()); + if (minLvl > maxLvl) std::swap(minLvl, maxLvl); + return urand(minLvl, maxLvl); // 在该区间内再进行一次平均随机 + } + else if (minmax.size() == 1) + { + return atoi(minmax[0].c_str()); + } + } + } + } + // 如果由于概率填写错误导致没命中,保底返回 110 级 + return 110; +} +// ========================================================================== + + + + + + PlayerBotCharBaseInfo PlayerBotBaseInfo::empty; std::map > PlayerBotMgr::m_DelayDestroyAIs; std::mutex PlayerBotMgr::g_uniqueLock; @@ -53,39 +112,50 @@ std::string PlayerBotCharBaseInfo::GetNameANDClassesText() clsEntry += 1; break; case 2: - //clsName = " ʥʿ : "; + //clsName = " ʥ ʿ : "; clsEntry += 2; break; case 3: - //clsName = " : "; + //clsName = " : "; clsEntry += 3; break; case 4: - //clsName = " : "; + //clsName = " : "; clsEntry += 4; break; case 5: - //clsName = " ʦ : "; + //clsName = " ʦ : "; clsEntry += 5; break; case 6: - //clsName = " : "; + //clsName = " : "; clsEntry += 6; break; case 7: - //clsName = " : "; + //clsName = " : "; clsEntry += 7; break; case 8: - //clsName = " ʦ : "; + //clsName = " ʦ : "; clsEntry += 8; break; case 9: - //clsName = " ʿ : "; + //clsName = " ʿ : "; clsEntry += 9; break; + + // ================== 新增:武僧与恶魔猎手户口 ================== + case 10: + clsEntry += 11; // 对应 TrinityString 里的武僧文本偏移 + break; + case 12: + clsEntry += 12; // 对应恶魔猎手文本偏移 + break; + // ============================================================= + + case 11: - //clsName = " ³ : "; + //clsName = " ³ : "; clsEntry += 10; break; } @@ -116,9 +186,15 @@ uint32 PlayerBotBaseInfo::GetCharIDByNoArenaType(bool faction, uint32 prof, uint } PlayerBotMgr::PlayerBotMgr() : - m_BotAccountAmount(90), + // 1. 账号数量:原版配置没有这个参数,我们增加读取 PlayerBot.AccountAmount, + // 如果你在 worldserver.conf 里没写这行,就默认生成 200 个账号(足够装下 3000+ 机器人了) + m_BotAccountAmount(sConfigMgr->GetIntDefault("PlayerBot.AccountAmount", 200)), + m_LastBotAccountIndex(0), - m_MaxOnlineBot(180), + + // 2. 最大在线人数:精确对应配置文件里的 pbotasl 参数 + m_MaxOnlineBot(sConfigMgr->GetIntDefault("pbotasl", 88)), + m_BotOnlineCount(0), m_LFGSearchTick(0), m_ArenaSearchTick(0) @@ -151,6 +227,44 @@ void PlayerBotMgr::SwitchPlayerBotAI(Player* player, PlayerBotAIType aiType, boo if (force && player->IsInCombat()) player->ClearInCombat(); player->SetSelection(ObjectGuid::Empty); + + // ================== 核心升级:二合一“完美武装与洗髓”机制 ================== + if (PlayerBotSession* pBotSession = dynamic_cast(player->GetSession())) + { + bool needTalents = (player->getLevel() >= 10 && player->GetSpecializationId() == 0); + + if (!player->EquipIsTidiness() || needTalents || aiType == PlayerBotAIType::PBAIT_GROUP) + { + uint32 curTType = player->FindTalentType(); + uint32 newTType = curTType; + + if (player->getLevel() >= 10) { + while (newTType == curTType) newTType = urand(0, 2); + } else { + newTType = urand(0, 2); + } + + // 【找回的遗失机制1:野外排队上线防拥堵分流】 + uint32 asyncDelay = 500; + if (aiType == PlayerBotAIType::PBAIT_FIELD) + { + // 如果是野外闲逛的机器人上线,将生成装备的动作打散到 20秒~120秒 以后执行 + asyncDelay = urand(20000, 120000); + } + + BotGlobleSchedule schedule2(BotGlobleScheduleType::BGSType_Settting, asyncDelay); + schedule2.parameter1 = player->getLevel(); + schedule2.parameter2 = player->getLevel(); + schedule2.parameter3 = newTType + 1; + + pBotSession->PushScheduleToQueue(schedule2); + + if (aiType == PlayerBotAIType::PBAIT_GROUP) + TC_LOG_INFO("playerbot", "机器人 %s 进组,强制触发全套武装与洗髓!", player->GetName().c_str()); + } + } + // ========================================================================= + UnitAI* pAI = player->GetAI(); player->IsAIEnabled = false; switch (aiType) @@ -251,11 +365,6 @@ void PlayerBotMgr::SwitchPlayerBotAI(Player* player, PlayerBotAIType aiType, boo case PlayerBotAIType::PBAIT_DUNGEON: if (pAI) { - //if (dynamic_cast(pAI) != NULL) - //{ - // player->IsAIEnabled = true; - // return; - //} PlayerBotMgr::m_DelayDestroyAIs[getMSTime()].push_back(pAI); player->SetAI(NULL); } @@ -263,6 +372,9 @@ void PlayerBotMgr::SwitchPlayerBotAI(Player* player, PlayerBotAIType aiType, boo } } + + + std::string PlayerBotMgr::GetPlayerLinkText(Player const* player) const { const std::string& name = player->GetName(); @@ -415,6 +527,17 @@ bool PlayerBotMgr::ExistClassByRace(uint8 race, uint8 prof) return (race == 1 || race == 5 || race == 7 || race == 8 || race == 10 || race == 11); case 9: return (race == 1 || race == 2 || race == 5 || race == 7 || race == 10); + + // ================== 新增:武僧与恶魔猎手种族合法性 ================== + case 10: + // 武僧:除了地精(9)和狼人(22),其他经典种族和熊猫人都可以 + return (race != 9 && race != 22); + case 12: + // 恶魔猎手:仅限暗夜精灵(4)和血精灵(10) + return (race == 4 || race == 10); + // =================================================================== + + case 11: return (race == 4 || race == 6); } @@ -690,7 +813,7 @@ WorldPacket PlayerBotMgr::BuildCreatePlayerData(bool group, uint8 prof) uint8 race = RandomRace(group, prof); uint8 gender = irand(0, 1); - // Safe defaults, damit Player::Create nicht wegen ungltiger Appearance ablehnt + // Safe defaults, damit Player::Create nicht wegen ung ltiger Appearance ablehnt uint8 skinColor = 0; uint8 faceID = 0; uint8 hairID = 0; @@ -783,6 +906,13 @@ void PlayerBotMgr::CreateOncePlayerBot() { TC_LOG_INFO("server.loading", ">> Character created successfully!"); + // ================== 修改代码开始 ================== + // 调用概率解析函数获取等级 + uint32 startLevel = GenerateRandomBotLevel(); + if (startLevel > 1) + newChar.SetLevel(startLevel); + // ================== 修改代码结束 ================== + newChar.setCinematic(2); newChar.SetAtLoginFlag(AT_LOGIN_FIRST); newChar.SaveToDB(true); @@ -870,6 +1000,13 @@ bool PlayerBotMgr::CreateQueuedPlayerBotForSession(PlayerBotBaseInfo* pInfo, Wor return false; } + // ================== 修改代码开始 ================== + // 调用概率解析函数获取等级 + uint32 startLevel = GenerateRandomBotLevel(); + if (startLevel > 1) + newChar.GiveLevel(startLevel); + // ================== 修改代码结束 ================== + newChar.setCinematic(2); newChar.SetAtLoginFlag(AT_LOGIN_FIRST); newChar.SaveToDB(true); @@ -1194,6 +1331,7 @@ void PlayerBotMgr::OnAccountBotDelete(ObjectGuid& guid, uint32 accountId) void PlayerBotMgr::OnPlayerBotLogin(WorldSession* pSession, Player* pPlayer) { ++m_BotOnlineCount; + Group* pGroup = pPlayer->GetGroup(); if (pGroup) { @@ -1219,21 +1357,105 @@ void PlayerBotMgr::OnPlayerBotLogin(WorldSession* pSession, Player* pPlayer) if (PlayerBotSession* pBotSession = dynamic_cast(pSession)) { - if (!pSession->HasSchedules() && !pPlayer->EquipIsTidiness()) + // ================== 核心 AI 优化:全职业起手技能豪华灌顶 ================== + // 【找回的遗失机制2:骑术门卫防死锁 + SaveToDB(false)】 + if (pPlayer->getLevel() > 0 && !pPlayer->HasSkill(762)) { - uint32 curTType = pPlayer->FindTalentType(); - uint32 newTType = curTType; - while (newTType == curTType) - newTType = urand(0, 2); - BotGlobleSchedule schedule2(BotGlobleScheduleType::BGSType_Settting, 0); - schedule2.parameter1 = pPlayer->getLevel(); - schedule2.parameter2 = pPlayer->getLevel(); - schedule2.parameter3 = newTType + 1; - pBotSession->PushScheduleToQueue(schedule2); + switch (pPlayer->getClass()) + { + case CLASS_WARRIOR: // 1 战士 + pPlayer->LearnSpell(100, false); + pPlayer->LearnSpell(12294, false); + pPlayer->LearnSpell(163201, false); + pPlayer->LearnSpell(1680, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_PALADIN: // 2 圣骑士 + pPlayer->LearnSpell(35395, false); + pPlayer->LearnSpell(20271, false); + pPlayer->LearnSpell(85256, false); + pPlayer->LearnSpell(19750, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_HUNTER: // 3 猎人 + pPlayer->LearnSpell(193455, false); + pPlayer->LearnSpell(185358, false); + pPlayer->LearnSpell(2643, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_ROGUE: // 4 盗贼 + pPlayer->LearnSpell(1752, false); + pPlayer->LearnSpell(196819, false); + pPlayer->LearnSpell(1784, false); + pPlayer->LearnSpell(1766, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_PRIEST: // 5 牧师 + pPlayer->LearnSpell(585, false); + pPlayer->LearnSpell(589, false); + pPlayer->LearnSpell(8092, false); + pPlayer->LearnSpell(2061, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_DEATH_KNIGHT: // 6 DK + pPlayer->LearnSpell(49998, false); + pPlayer->LearnSpell(47541, false); + pPlayer->LearnSpell(49020, false); + pPlayer->LearnSpell(50842, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_SHAMAN: // 7 萨满 + pPlayer->LearnSpell(403, false); + pPlayer->LearnSpell(188196, false); + pPlayer->LearnSpell(8050, false); + pPlayer->LearnSpell(8004, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_MAGE: // 8 法师 + pPlayer->LearnSpell(133, false); + pPlayer->LearnSpell(116, false); + pPlayer->LearnSpell(44425, false); + pPlayer->LearnSpell(122, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_WARLOCK: // 9 术士 + pPlayer->LearnSpell(686, false); + pPlayer->LearnSpell(172, false); + pPlayer->LearnSpell(980, false); + pPlayer->LearnSpell(234153, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_MONK: // 10 武僧 + pPlayer->LearnSpell(100780, false); + pPlayer->LearnSpell(100784, false); + pPlayer->LearnSpell(116694, false); + pPlayer->LearnSpell(115151, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_DRUID: // 11 德鲁伊 + pPlayer->LearnSpell(5176, false); + pPlayer->LearnSpell(8921, false); + pPlayer->LearnSpell(8936, false); + pPlayer->LearnSpell(1822, false); + pPlayer->LearnSpell(5221, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + case CLASS_DEMON_HUNTER: // 12 恶魔猎手 + pPlayer->LearnSpell(162243, false); + pPlayer->LearnSpell(162794, false); + pPlayer->LearnSpell(185123, false); + pPlayer->SetSkill(762, 0, 300, 300); + break; + } + // 写入数据库,仅执行一生一次!单参数! + pPlayer->SaveToDB(false); } + // ======================================================================= } } + + void PlayerBotMgr::OnPlayerBotLogout(WorldSession* pSession) { --m_BotOnlineCount; @@ -1294,7 +1516,7 @@ void PlayerBotMgr::LoginFriendBotByPlayer(Player* pPlayer) // } //#else // std::string allonlineText; - // consoleToUtf8(std::string("|cffff8800޷ٻѻߡ|r"), allonlineText); + // consoleToUtf8(std::string("|cffff8800 ޷ ٻ ѻ ߡ |r"), allonlineText); // sWorld->SendGlobalText(allonlineText.c_str(), NULL); //#endif } @@ -1356,13 +1578,18 @@ void PlayerBotMgr::AllPlayerBotRandomLogin(const char* name) if (name[0] != '\0') { - for (auto i = 0; i < pInfo->characters.size(); ++i) + // ================== 修复 Bug ================== + // 原代码:for (auto i = 0; i < pInfo->characters.size(); ++i) + // 原代码:if (strcmp(name, pInfo->characters[i].name.c_str()) == 0) + // 修复为使用迭代器遍历 map: + for (auto itChar = pInfo->characters.begin(); itChar != pInfo->characters.end(); ++itChar) { - if (strcmp(name, pInfo->characters[i].name.c_str()) == 0) + if (strcmp(name, itChar->second.name.c_str()) == 0) { - PlayerBotCharBaseInfo& charInfo = pInfo->characters[i]; + PlayerBotCharBaseInfo& charInfo = itChar->second; WorldPacket _worldPacket(CMSG_PLAYER_LOGIN); WorldPackets::Character::PlayerLogin cmd(std::move(_worldPacket)); + // ========================================================== cmd.Guid = ObjectGuid::Create(charInfo.guid); cmd.FarClip = 0.0f; pSession->HandlePlayerLoginOpcode(cmd); @@ -1479,10 +1706,10 @@ void PlayerBotMgr::SupplementPlayerBot() TC_LOG_INFO("server.loading", ">> FirstName extracted: %s", firstName.c_str()); - for (int i = 1; i < 10; i++) + // ================== 修复:完美覆盖 1-12 所有职业 ================== + // 直接从 1 循环到 12,包含死亡骑士(6)、武僧(10)、德鲁伊(11)、恶魔猎手(12) + for (int i = 1; i <= 12; i++) { - if (i == 6) continue; - if (!pInfo->ExistClass(true, i)) { memset(botname, 0, 30); @@ -1499,22 +1726,7 @@ void PlayerBotMgr::SupplementPlayerBot() pInfo->needCreateBots.push(BuildCreatePlayerData(false, i)); } } - - if (!pInfo->ExistClass(true, 11)) - { - memset(botname, 0, 30); - sprintf(botname, "%sA11", firstName.c_str()); - TC_LOG_INFO("server.loading", ">> Pushing Alliance Druid bot: %s", botname); - pInfo->needCreateBots.push(BuildCreatePlayerData(true, 11)); - } - - if (!pInfo->ExistClass(false, 11)) - { - memset(botname, 0, 30); - sprintf(botname, "%sB11", firstName.c_str()); - TC_LOG_INFO("server.loading", ">> Pushing Horde Druid bot: %s", botname); - pInfo->needCreateBots.push(BuildCreatePlayerData(false, 11)); - } + // ================================================================== TC_LOG_INFO("server.loading", ">> Total bots in queue: %u", (uint32)pInfo->needCreateBots.size()); } @@ -1529,7 +1741,8 @@ void PlayerBotMgr::SupplementOneRandomPlayerBotPerAccount() // Valid classes for this core/expansion. Death Knight (6) is intentionally skipped, // same as SupplementPlayerBot(), because these are normal starter bots. - static uint8 const botClasses[] = { 1, 2, 3, 4, 5, 7, 8, 9, 11 }; + // ================== 修复:让系统随机生成所有的 12 个职业 ================== + static uint8 const botClasses[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; uint32 queuedCreateCount = 0; @@ -2066,7 +2279,7 @@ void PlayerBotMgr::AddNewPlayerBot(bool faction, Classes prof, uint32 count) if (count > 0) { std::string allonlineText; - consoleToUtf8(std::string("|cffff8800л˺Ѿȫߣ޷»ˡ|r"), allonlineText); + consoleToUtf8(std::string("|cffff8800 л ˺ Ѿ ȫ ߣ ޷ » ˡ |r"), allonlineText); sWorld->SendGlobalText(allonlineText.c_str(), NULL); } } @@ -2114,7 +2327,7 @@ void PlayerBotMgr::AddNewAccountBot(bool faction, Classes prof) } std::string allonlineText; #ifdef INCOMPLETE_BOT - consoleToUtf8(std::string("|cffff8800޷ٻԽ˺Žɫ|r"), allonlineText); + consoleToUtf8(std::string("|cffff8800 ޷ ٻ Խ ˺Ž ɫ|r"), allonlineText); sWorld->SendGlobalText(allonlineText.c_str(), NULL); return; #endif @@ -2179,7 +2392,7 @@ void PlayerBotMgr::AddNewAccountBot(bool faction, Classes prof) } } - consoleToUtf8(std::string("|cffff8800ûҵͬӪְָҵԽ˺Žɫ|r"), allonlineText); + consoleToUtf8(std::string("|cffff8800û ҵ ͬ Ӫ ָ ְҵ Խ ˺Ž ɫ|r"), allonlineText); sWorld->SendGlobalText(allonlineText.c_str(), NULL); } @@ -2279,7 +2492,7 @@ void PlayerBotMgr::AddNewPlayerBotByClass(uint32 count, Classes prof) if (allianceCount > 0 || hordeCount > 0) { std::string allonlineText; - consoleToUtf8(std::string("|cffff8800л˺Ѿȫߣ޷»ˡ|r"), allonlineText); + consoleToUtf8(std::string("|cffff8800 л ˺ Ѿ ȫ ߣ ޷ » ˡ |r"), allonlineText); sWorld->SendGlobalText(allonlineText.c_str(), NULL); } } @@ -2405,7 +2618,7 @@ void PlayerBotMgr::AddNewPlayerBotToBG(TeamId team, uint32 minLV, uint32 maxLV, } std::string allonlineText; - consoleToUtf8(std::string("|cffff8800л˺Ѿȫߣ޷»˵սС|r"), allonlineText); + consoleToUtf8(std::string("|cffff8800 л ˺ Ѿ ȫ ߣ ޷ » ˵ ս С |r"), allonlineText); sWorld->SendGlobalText(allonlineText.c_str(), NULL); } @@ -2663,7 +2876,7 @@ void PlayerBotMgr::AddNewPlayerBotToAA(TeamId team, BattlegroundTypeId bgTypeID, } std::string allonlineText; - consoleToUtf8(std::string("|cffff8800л˺Ѿȫߣ޷»˵С|r"), allonlineText); + consoleToUtf8(std::string("|cffff8800 л ˺ Ѿ ȫ ߣ ޷ » ˵ С |r"), allonlineText); sWorld->SendGlobalText(allonlineText.c_str(), NULL); } @@ -2914,6 +3127,19 @@ lfg::LfgRoles PlayerBotMgr::GetPlayerBotCurrentLFGRoles(Player* player) case 9: return lfg::LfgRoles::PLAYER_ROLE_DAMAGE; break; + + // ================== 新增:武僧与恶魔猎手职责 ================== + case 10: + if (talentType == 0) return lfg::LfgRoles::PLAYER_ROLE_TANK; // 酒仙 + else if (talentType == 1) return lfg::LfgRoles::PLAYER_ROLE_HEALER;// 织雾 + else return lfg::LfgRoles::PLAYER_ROLE_DAMAGE; // 踏风 + case 12: + if (talentType == 1) return lfg::LfgRoles::PLAYER_ROLE_TANK; // 复仇 + else return lfg::LfgRoles::PLAYER_ROLE_DAMAGE; // 浩劫 + // ============================================================== + + + case 5: if (talentType == 2) return lfg::LfgRoles::PLAYER_ROLE_DAMAGE; @@ -3327,6 +3553,51 @@ uint32 PlayerBotMgr::GetOnlineBotCount2(TeamId team, bool hasReal) void PlayerBotMgr::Update() { + uint32 currentMs = getMSTime(); + + // ================== 终极修复:平滑登录 + 真实玩家 VIP 通道 ================== + static uint32 s_loginTimer = 0; + + if (currentMs >= s_loginTimer) + { + int32 isAuto = sConfigMgr->GetIntDefault("pbotall", 1); + if (isAuto != 0 && m_BotOnlineCount < (uint32)m_MaxOnlineBot) + { + uint32 busyCount = 0; + SessionMap const& sessions = sWorld->GetAllSessions(); + for (auto const& pair : sessions) + { + // 【找回的遗失机制3:只检测真实玩家!】 + if (!pair.second->IsBotSession()) + { + if (pair.second->PlayerLoading() || !pair.second->GetPlayer()) + { + busyCount++; + } + } + } + + if (busyCount == 0) + { + uint32 batchSize = 1; + uint32 delay = urand(1000, 2500); + + int32 originalMax = m_MaxOnlineBot; + m_MaxOnlineBot = m_BotOnlineCount + batchSize; + AllPlayerBotRandomLogin(""); + m_MaxOnlineBot = originalMax; + + s_loginTimer = currentMs + delay; + } + else + { + s_loginTimer = currentMs + 3000; + } + } + } + // ======================================================================= + + OnlinePlayerBotByGUIDQueue(); @@ -3376,4 +3647,6 @@ void PlayerBotMgr::Update() { m_DelayDestroyAIs.erase(*itDel); } + + } diff --git a/src/server/game/PlayerBot/PlayerBotSetting.cpp b/src/server/game/PlayerBot/PlayerBotSetting.cpp index 092ac6ef76..194b4b1b94 100644 --- a/src/server/game/PlayerBot/PlayerBotSetting.cpp +++ b/src/server/game/PlayerBot/PlayerBotSetting.cpp @@ -165,6 +165,46 @@ bool PlayerBotSetting::IsEquipByClasses(uint32 cls, const ItemTemplate* itemTemp return IsMageEquip(itemTemplate); case 9: return IsWarlockEquip(itemTemplate); + + // ================== 新增:武僧与恶魔猎手通用装备判定 ================== + case 10: // 武僧 + { + if (itemTemplate->GetClass() == ItemClass::ITEM_CLASS_WEAPON) { + switch (itemTemplate->GetSubClass()) { + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_MACE: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_MACE2: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_SWORD: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_SWORD2: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_STAFF: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_POLEARM: + return true; + default: return false; + } + } else if (itemTemplate->GetClass() == ItemClass::ITEM_CLASS_ARMOR) { + return itemTemplate->GetSubClass() == ItemSubclassArmor::ITEM_SUBCLASS_ARMOR_LEATHER; + } + return false; + } + case 12: // 恶魔猎手 + { + if (itemTemplate->GetClass() == ItemClass::ITEM_CLASS_WEAPON) { + switch (itemTemplate->GetSubClass()) { + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_AXE: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_AXE2: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_SWORD: + case ItemSubclassWeapon::ITEM_SUBCLASS_WEAPON_SWORD2: + case 9: // 7.3.5 中的战刃 (Warglaives) + return true; + default: return false; + } + } else if (itemTemplate->GetClass() == ItemClass::ITEM_CLASS_ARMOR) { + return itemTemplate->GetSubClass() == ItemSubclassArmor::ITEM_SUBCLASS_ARMOR_LEATHER; + } + return false; + } + // =================================================================== + + case 11: return IsDruidEquip(itemTemplate); default: @@ -175,7 +215,9 @@ bool PlayerBotSetting::IsEquipByClasses(uint32 cls, const ItemTemplate* itemTemp bool PlayerBotSetting::IsEquipByClsAndTal(uint32 cls, uint32 tal, const ItemTemplate* itemTemplate, int32 rndPropID) { - if (tal > 2 || !itemTemplate || cls < 1 || cls == 10 || cls > 11) + // ================== 修复:允许武僧(10)和恶魔猎手(12)获取装备 ================== + if (tal > 2 || !itemTemplate || cls < 1 || cls > 12) + return false; return false; if (!itemTemplate || itemTemplate->ExtendedData->AllowableClass == 0) return false; @@ -297,6 +339,34 @@ bool PlayerBotSetting::IsEquipByClsAndTal(uint32 cls, uint32 tal, const ItemTemp if (IsCommonEquip(itemTemplate)) return true; return IsWarlockEquip(itemTemplate); + + // ================== 新增:武僧与恶魔猎手专精属性过滤 ================== + case 10: + if (tal == 0) { // 酒仙 (坦克) + if (IsTankRandomAttributeByEquip(enchants) || IsTankAttributeEquip(itemTemplate)) return true; + if (!IsOnlyPhysicsRandomAttributeByEquip(enchants, true) && !IsOnlyPhysicsAttributeEquip(itemTemplate, true)) return false; + } else if (tal == 1) { // 织雾 (治疗) + if (IsTankRandomAttributeByEquip(enchants) || IsTankAttributeEquip(itemTemplate)) return false; + if (!IsOnlyMagicRandomAttributeByEquip(enchants) && !IsOnlyMagicAttributeEquip(itemTemplate)) return false; + } else { // 踏风 (近战输出) + if (IsTankRandomAttributeByEquip(enchants) || IsTankAttributeEquip(itemTemplate)) return false; + if (!IsOnlyPhysicsRandomAttributeByEquip(enchants, true) && !IsOnlyPhysicsAttributeEquip(itemTemplate, true)) return false; + } + if (IsCommonEquip(itemTemplate)) return true; + return IsEquipByClasses(cls, itemTemplate); + case 12: + if (tal == 1) { // 复仇 (坦克) + if (IsTankRandomAttributeByEquip(enchants) || IsTankAttributeEquip(itemTemplate)) return true; + } else { // 浩劫 (近战输出) + if (IsTankRandomAttributeByEquip(enchants) || IsTankAttributeEquip(itemTemplate)) return false; + } + if (!IsOnlyPhysicsRandomAttributeByEquip(enchants, true) && !IsOnlyPhysicsAttributeEquip(itemTemplate, true)) return false; + if (IsCommonEquip(itemTemplate)) return true; + return IsEquipByClasses(cls, itemTemplate); + // ===================================================================== + + + case 11: if (IsTankRandomAttributeByEquip(enchants) || IsTankAttributeEquip(itemTemplate)) return false; @@ -1384,7 +1454,7 @@ uint32 PlayerBotSetting::FindPlayerTalentType(Player* player) { if (!player) return 0; - uint32 spec = player->GetSpecializationId(); + uint32 cls = player->getClass(); uint32 pageTalents[3] = { 0 }; for (uint32 page = 0; page < 3; page++) @@ -1487,6 +1557,21 @@ void PlayerBotSetting::Initialize() classesTrainersGUID[Classes::CLASS_PRIEST][0] = 5141; classesTrainersGUID[Classes::CLASS_PRIEST][1] = 4606; + + // ================== 新增:给武僧和瞎子分配导师和通用技能(骑马等) ================== + classesTrainersGUID[10][0] = 5165; // 借用盗贼导师,防止无导师崩溃 + classesTrainersGUID[10][1] = 4584; + classesTrainersGUID[12][0] = 5165; + classesTrainersGUID[12][1] = 4584; + + // 复制盗贼的通用技能(回城、骑马、上马动作等)给武僧和瞎子 + classesCommonSpells[10] = classesCommonSpells[4]; + classesCommonSpells[12] = classesCommonSpells[4]; + // ============================================================================== + + + + for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); @@ -1710,7 +1795,7 @@ uint32 PlayerBotSetting::UpdateTalentType() { if (m_Player->IsPlayerBot()) { - uint32 lv = (m_Player->getLevel() < 9) ? 9 : m_Player->getLevel(); + } if (m_ActiveTalentType >= 3) @@ -1743,14 +1828,78 @@ bool PlayerBotSetting::ResetPlayerToLevel(uint32 level, uint32 talent, bool tena m_ResetStep = 1; m_Finish = false; m_TenacitySetting = tenacity; + + // ================== 终极防护:专精锁定与天赋自愈 ================== + // 将这段代码插在 return true; 之前,确保装备状态机正常运行 + if (m_Player->getLevel() >= 10) + { + uint32 currentSpecId = m_Player->GetSpecializationId(); + if (currentSpecId == 0) + { + // 纯白板,按随机参数分配专精(减1是为了将1/2/3转为底层的0/1/2索引) + uint32 specIndex = (talent > 0) ? (talent - 1) : 0; + SwitchPlayerTalent(specIndex); + } + else + { + // 已有专精,绝对不改变当前专精!只负责点亮天赋! + LearnTalents(); + m_ActiveTalentType = m_Player->FindTalentType(); + } + } + // ========================================================================= + + return true; } + uint32 PlayerBotSetting::SwitchPlayerTalent(uint32 talent) { + if (!m_Player) + return m_ActiveTalentType; + + // 保存当前的专精索引 (0=第一专精, 1=第二专精, 2=第三专精, 3=德鲁伊第四专精) + if (talent <= 2) + m_ActiveTalentType = talent; + else + m_ActiveTalentType = 3; + + // 使用核心自带的枚举进行映射,彻底杜绝无符号匹配等警告 + uint32 specId = 0; + switch (m_Player->getClass()) + { + case CLASS_WARRIOR: specId = (talent == 0) ? TALENT_SPEC_WARRIOR_ARMS : ((talent == 1) ? TALENT_SPEC_WARRIOR_FURY : TALENT_SPEC_WARRIOR_PROTECTION); break; + case CLASS_PALADIN: specId = (talent == 0) ? TALENT_SPEC_PALADIN_HOLY : ((talent == 1) ? TALENT_SPEC_PALADIN_PROTECTION : TALENT_SPEC_PALADIN_RETRIBUTION); break; + case CLASS_HUNTER: specId = (talent == 0) ? TALENT_SPEC_HUNTER_BEASTMASTER : ((talent == 1) ? TALENT_SPEC_HUNTER_MARKSMAN : TALENT_SPEC_HUNTER_SURVIVAL); break; + case CLASS_ROGUE: specId = (talent == 0) ? TALENT_SPEC_ROGUE_ASSASSINATION : ((talent == 1) ? TALENT_SPEC_ROGUE_COMBAT : TALENT_SPEC_ROGUE_SUBTLETY); break; + case CLASS_PRIEST: specId = (talent == 0) ? TALENT_SPEC_PRIEST_DISCIPLINE : ((talent == 1) ? TALENT_SPEC_PRIEST_HOLY : TALENT_SPEC_PRIEST_SHADOW); break; + case CLASS_DEATH_KNIGHT: specId = (talent == 0) ? TALENT_SPEC_DEATHKNIGHT_BLOOD : ((talent == 1) ? TALENT_SPEC_DEATHKNIGHT_FROST : TALENT_SPEC_DEATHKNIGHT_UNHOLY); break; + case CLASS_SHAMAN: specId = (talent == 0) ? TALENT_SPEC_SHAMAN_ELEMENTAL : ((talent == 1) ? TALENT_SPEC_SHAMAN_ENHANCEMENT : TALENT_SPEC_SHAMAN_RESTORATION); break; + case CLASS_MAGE: specId = (talent == 0) ? TALENT_SPEC_MAGE_ARCANE : ((talent == 1) ? TALENT_SPEC_MAGE_FIRE : TALENT_SPEC_MAGE_FROST); break; + case CLASS_WARLOCK: specId = (talent == 0) ? TALENT_SPEC_WARLOCK_AFFLICTION : ((talent == 1) ? TALENT_SPEC_WARLOCK_DEMONOLOGY : TALENT_SPEC_WARLOCK_DESTRUCTION); break; + case CLASS_MONK: specId = (talent == 0) ? TALENT_SPEC_MONK_BREWMASTER : ((talent == 1) ? TALENT_SPEC_MONK_BATTLEDANCER : TALENT_SPEC_MONK_MISTWEAVER); break; + case CLASS_DRUID: specId = (talent == 0) ? TALENT_SPEC_DRUID_BALANCE : ((talent == 1) ? TALENT_SPEC_DRUID_CAT : ((talent == 2) ? TALENT_SPEC_DRUID_BEAR : TALENT_SPEC_DRUID_RESTORATION)); break; + case CLASS_DEMON_HUNTER: specId = (talent == 0) ? TALENT_SPEC_DEMON_HUNTER_HAVOC : TALENT_SPEC_DEMON_HUNTER_VENGEANCE; break; + } + + if (specId != 0) + { + // 1. 更新服务端后端的专精记录 + m_Player->SetPrimarySpecialization(specId); + + // 2. 强行更新前端 UI 绑定的字段,让你的客户端立刻看到新专精的图标 + m_Player->SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, specId); + } + + // 触发机器人的“重置状态机”,后台会自动清空旧技能,并学习新专精技能和换装 + m_ResetStep = 1; + m_Finish = false; + return m_ActiveTalentType; } + void PlayerBotSetting::SupplementAmmo() { @@ -1839,7 +1988,60 @@ void PlayerBotSetting::UpdateReset() void PlayerBotSetting::LearnTalents() { - + if (!m_Player) + return; + + uint8 level = m_Player->getLevel(); + uint32 specId = m_Player->GetSpecializationId(); // 获取我们上次修复的真实专精ID + + // 1. 判断 7.3.5 的网格天赋解锁到了第几层 (TierID: 0 到 6) + int8 maxTier = -1; + if (level >= 15) maxTier = 0; + if (level >= 30) maxTier = 1; + if (level >= 45) maxTier = 2; + if (level >= 60) maxTier = 3; + if (level >= 75) maxTier = 4; + if (level >= 90) maxTier = 5; + if (level >= 100) maxTier = 6; + + // 如果等级不到15级,什么天赋都不学,直接返回 + if (maxTier < 0) + return; + + // 2. 为这 7 层天赋,提前随机掷骰子(每层在 0, 1, 2 三个列中随机选一个) + uint8 randomCol[7]; + for (int i = 0; i < 7; ++i) + { + randomCol[i] = urand(0, 2); + } + + // 3. 遍历底层 Talent.db2 数据库,寻找匹配的天赋 + for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) + { + TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); + if (!talentInfo) + continue; + + // 过滤 1:必须是当前机器人的职业 + if (talentInfo->ClassID != m_Player->getClass()) + continue; + + // 过滤 2:必须是当前专精(SpecID 为 0 代表三系通用,非 0 代表专属) + if (talentInfo->SpecID != 0 && talentInfo->SpecID != specId) + continue; + + // 过滤 3:必须在当前等级允许的层数范围内 + if ((int8)talentInfo->TierID > maxTier || talentInfo->TierID > 6) + continue; + + // 命中!如果这个天赋恰好位于我们刚刚随机选中的那一列 + if (talentInfo->ColumnIndex == randomCol[talentInfo->TierID]) + { + // 让机器人正式学会这个天赋! + // 【修复】:第二个参数传入 nullptr,忽略技能冷却返回 + m_Player->LearnTalent(talentId, nullptr); + } + } } void PlayerBotSetting::LearnCommonSpells() @@ -2064,6 +2266,39 @@ void PlayerBotSetting::AddEquipFromAll() case 9: RandomWeaponByWarlock(); break; + + // ================== 新增:武器分发逻辑 ================== + case 10: // 武僧武器分发 + { + if (m_ActiveTalentType == 1) { // 织雾:优先发法杖 + ItemTemplate* item1 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_2HWEAPON, level); + if (!item1) item1 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPONMAINHAND, level); + AddOnceEquip(item1); + } else if (m_ActiveTalentType == 0) { // 酒仙:发双手敏捷武器 + AddOnceEquip(GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_2HWEAPON, level)); + } else { // 踏风:发双持单手 + ItemTemplate* item1 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPON, level); + if (!item1) item1 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPONMAINHAND, level); + AddOnceEquip(item1); + ItemTemplate* item2 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPON, level, item1); + if (!item2) item2 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPONOFFHAND, level); + AddOnceEquip(item2); + } + break; + } + case 12: // 恶魔猎手武器分发(双持战刃/单手剑) + { + ItemTemplate* item1 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPON, level); + if (!item1) item1 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPONMAINHAND, level); + AddOnceEquip(item1); + ItemTemplate* item2 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPON, level, item1); + if (!item2) item2 = (ItemTemplate*)GetRandomItemFromLoopLV(prof, InventoryType::INVTYPE_WEAPONOFFHAND, level); + AddOnceEquip(item2); + break; + } + // ======================================================= + + case 11: RandomWeaponByDruid(); break; diff --git a/src/server/game/PlayerBot/PlayerBotTalkMgr.cpp b/src/server/game/PlayerBot/PlayerBotTalkMgr.cpp index c1cf5adf84..fab4ba758d 100644 --- a/src/server/game/PlayerBot/PlayerBotTalkMgr.cpp +++ b/src/server/game/PlayerBot/PlayerBotTalkMgr.cpp @@ -1,4 +1,4 @@ -/* +/* * This file is part of the DestinyCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it @@ -216,7 +216,7 @@ bool PlayerBotTalkMgr::IsValidStoryStep(uint32 id, uint32 step) std::string PlayerBotTalkMgr::GetDefaultChannelName() { std::string defaultChannelName; - consoleToUtf8(std::string("Ƶ"), defaultChannelName); + consoleToUtf8(std::string(" Ƶ "), defaultChannelName); return defaultChannelName; } diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index d56a3598a3..86cc713cef 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -639,6 +639,15 @@ struct AccountInfo void WorldSocket::HandleAuthSession(std::shared_ptr authSession) { + + + // ================= 真实玩家登录雷达:阶段 1 ================= + TC_LOG_ERROR("network", ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + TC_LOG_ERROR("network", "[真实玩家雷达] 阶段1:收到真实玩家的握手请求!来源IP: %s", GetRemoteIpAddress().to_string().c_str()); + TC_LOG_ERROR("network", ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + // ============================================================ + + // Get the account information from the auth database LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); stmt->setInt32(0, int32(realm.Id.Realm)); @@ -669,6 +678,13 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptrFetch()); + + // ================= 真实玩家登录雷达:阶段 2 ================= + TC_LOG_ERROR("network", "[真实玩家雷达] 阶段2:Auth数据库验证秒回!真实账号名: %s", account.BattleNet.Name.c_str()); + // ============================================================ + + + // For hook purposes, we get Remoteaddress at this point. std::string address = GetRemoteIpAddress().to_string(); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index bae87d0c85..ce34893652 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ - +#include "LuaEngine/LuaEngine.h" #include "Spell.h" #include "Battlefield.h" #include "BattlefieldMgr.h" @@ -3161,6 +3161,25 @@ bool Spell::UpdateChanneledTargetList() SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura) { + // ========================================== + // Custom: Hearthstone (8690) interception via Eluna OnChat + // ========================================== + if (m_spellInfo->Id == 8690 && m_caster->GetTypeId() == TYPEID_PLAYER) + { + // Exclude return casts triggered by Lua + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) + { + if (sEluna) + { + std::string secretMsg = "__HEARTHSTONE_START__"; + sEluna->OnChat(m_caster->ToPlayer(), 0, 0, secretMsg); + } + } + } + // ========================================== + + + if (m_CastItem) { m_castItemGUID = m_CastItem->GetGUID(); @@ -3310,7 +3329,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && + if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER &&!m_caster->IsPlayerBot() && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel @@ -3437,20 +3456,38 @@ void Spell::cast(bool skipCheck) if (Player* playerCaster = m_caster->ToPlayer()) { - // now that we've done the basic check, now run the scripts - // should be done before the spell is actually executed + // 1. 原本的脚本管理器调用(官方原版自带,必须保留) sScriptMgr->OnPlayerSpellCast(playerCaster, this, skipCheck); - // As of 3.0.2 pets begin attacking their owner's target immediately - // Let any pets know we've attacked something. Check DmgClass for harmful spells only - // This prevents spells such as Hunter's Mark from triggering pet attack + // ========================================== + // 2. [Eluna 物理并联] 全局修复 Event 5 (法术施放事件) + // 彻底接管所有法术!如果 Lua 返回 false,立刻体面地掐断读条! + // ========================================== +#if defined(ELUNA) + if (sEluna) + { + // 这里没有写死 8690,这意味着以后你能用 Lua 拦截任何职业的任何技能! + if (!sEluna->OnSpellCast(playerCaster, this, skipCheck)) + { + SendCastResult(SPELL_FAILED_INTERRUPTED); // 通知客户端法术中断 + finish(false); // 清理服务端法术状态 + return; // 彻底截杀! + } + } +#endif + // ========================================== + + // 3. As of 3.0.2 pets begin attacking their owner's target immediately + // (下面是官方自带的宠物攻击代码,保持原样) if (this->GetSpellInfo()->DmgClass != SPELL_DAMAGE_CLASS_NONE) if (Pet* playerPet = playerCaster->GetPet()) if (playerPet->IsAlive() && playerPet->isControlled() && (m_targets.GetTargetMask() & TARGET_FLAG_UNIT)) playerPet->AI()->OwnerAttacked(m_targets.GetUnitTarget()); } + SetExecutedCurrently(true); + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING)) if (m_caster->GetTypeId() == TYPEID_UNIT && m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index b3dcbf3592..9823f19df7 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -18,7 +18,9 @@ /** \file \ingroup world */ - +#include "ElunaConfig.h" +#include "LuaEngine.h" +#include "ElunaLoader.h" #include "World.h" #include "BattlePetDataStore.h" #include "WildBattlePet.h" @@ -2678,6 +2680,17 @@ void World::SetInitialWorldSettings() TC_METRIC_EVENT("events", "World initialized", "World initialized in " + std::to_string(startupDuration / 60000) + " minutes " + std::to_string((startupDuration % 60000) / 1000) + " seconds"); sLog->SetRealmId(realm.Id.Realm, realm.Name); + // ========================================== + // ?? Eluna Lua ??? + TC_LOG_INFO("server.loading", "Igniting Eluna Lua Engine..."); + sElunaConfig->Initialize(); + sEluna = new Eluna(nullptr); + sElunaLoader->ReloadElunaForMap(RELOAD_ALL_STATES); + // ========================================== + // [?????]???????????????????? + TC_LOG_INFO("server.loading", "Auto-Starting Playerbots..."); + sWorld->QueueCliCommand(new CliCommandHolder(nullptr, "pbot loginall", nullptr, nullptr)); + // ========================================== } void World::ResetTimeDiffRecord() @@ -3000,6 +3013,11 @@ void World::Update(uint32 diff) // Stats logger update sMetric->Update(); TC_METRIC_VALUE("update_time_diff", diff); + // =================================== + // [Eluna 心脏跳动] 让 Lua 引擎的时间流动起来! + if (sEluna) + sEluna->UpdateEluna(diff); + // =================================== } void World::ForceGameEventUpdate() diff --git a/src/server/scripts/Custom/cs_ai_director.cpp b/src/server/scripts/Custom/cs_ai_director.cpp new file mode 100644 index 0000000000..d9795a2723 --- /dev/null +++ b/src/server/scripts/Custom/cs_ai_director.cpp @@ -0,0 +1,291 @@ +#include "ScriptMgr.h" +#include "Player.h" +#include "Log.h" +#include "WorldSession.h" +#include "World.h" +#include "Map.h" +#include "AI/PlayerAI/BotAI.h" +#include "AI/PlayerAI/PlayerAI.h" +#include "Phasing/PhasingHandler.h" + +// ========================================== +// 地图等级鉴定中心 (Zone Level Dictionary) +// ========================================== +struct ZoneLevelRange { uint32 minLevel; uint32 maxLevel; }; + +ZoneLevelRange GetZoneRealLevel(uint32 zoneId) +{ + // 这里列举了魔兽经典的地图真实等级。你可以根据需要自由添加! + switch (zoneId) + { + // --- 新手村 (1-10) --- + case 12: return { 1, 10 }; // 艾尔文森林 + case 14: return { 1, 10 }; // 杜隆塔尔 + case 85: return { 1, 10 }; // 提瑞斯法林地 + case 215: return { 1, 10 }; // 莫高雷 + case 1: return { 1, 10 }; // 丹莫罗 + case 141: return { 1, 10 }; // 泰达希尔 + case 3430: return { 1, 10 }; // 永歌森林 + case 3524: return { 1, 10 }; // 秘蓝岛 + + // --- 前期野外 (10-20) --- + case 17: return { 10, 25 }; // 贫瘠之地 + case 40: return { 10, 20 }; // 西部荒野 + case 130: return { 10, 20 }; // 银松森林 + case 38: return { 10, 20 }; // 洛克莫丹 + case 148: return { 10, 20 }; // 黑海岸 + case 3433: return { 10, 20 }; // 塔伦米尔/幽魂之地 + + // --- 中期野外 (30-50) --- + case 33: return { 30, 45 }; // 荆棘谷 + case 440: return { 40, 50 }; // 塔纳利斯 + case 400: return { 50, 55 }; // 安戈洛环形山 + + // --- 外域 (58-70) --- + case 3483: return { 58, 63 }; // 地狱火半岛 + case 3518: return { 64, 68 }; // 纳格兰 + + // --- 诺森德 (68-80) --- + case 3537: return { 68, 72 }; // 北风苔原 + case 3536: return { 68, 72 }; // 嚎风峡湾 + case 210: return { 77, 80 }; // 冰冠冰川 + + // 默认返回值:如果字典里没写,返回 0,系统会走另一种默认逻辑 + default: return { 0, 0 }; + } +} + + +// ========================================== +// AI 导演核心调度器 (单例) +// ========================================== +class AIDirectorMgr +{ +public: + static AIDirectorMgr* GetInstance() + { + static AIDirectorMgr instance; + return &instance; + } + + // 【新增】:5秒全屏雷达扫描补给线 + void Update(uint32 diff) + { + m_radarTimer += diff; + // 每 5 秒雷达扫一圈 + if (m_radarTimer >= 5000) + { + m_radarTimer = 0; + + SessionMap const& sessions = sWorld->GetAllSessions(); + for (auto const& pair : sessions) + { + Player* p = pair.second->GetPlayer(); + // 找到真实玩家,并且不在副本、不在飞行的 + if (p && p->IsInWorld() && !p->IsPlayerBot() && !p->GetMap()->IsDungeon() && !p->IsInFlight()) + { + // 检测玩家周围 50 码内有几个清醒的群演 + uint32 activeBotsNearby = 0; + for (auto const& botPair : sessions) + { + Player* b = botPair.second->GetPlayer(); + if (b && b->IsPlayerBot() && b->GetMapId() == p->GetMapId()) + { + if (p->GetDistance(b) <= 50.0f) + { + PlayerAI* bai = dynamic_cast(b->GetAI()); + if (bai && !bai->IsDirectorSleeping()) + activeBotsNearby++; + } + } + } + + // 主城要求热闹 (比如 15 个),野外要求真实 (比如 5 个) + bool isInCity = p->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING); + uint32 desiredBots = isInCity ? 15 : 5; + + // 如果雷达发现周围群演不够,立刻呼叫空投支援! + if (activeBotsNearby < desiredBots) + { + uint32 needCount = desiredBots - activeBotsNearby; + DispatchActorsToPlayer(p, needCount); + } + } + } + } + } + + // 核心投放逻辑:在玩家周围部署群演 + void DispatchActorsToPlayer(Player* realPlayer, uint32 actorsNeeded = 50) + { + if (!realPlayer || !realPlayer->IsInWorld()) return; + + uint32 currentMapId = realPlayer->GetMapId(); + uint32 currentZoneId = realPlayer->GetZoneId(); + uint32 playerLevel = realPlayer->getLevel(); + uint32 teamId = realPlayer->GetTeamId(); + + bool isInCity = realPlayer->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING); + + // 查字典,获取当前地图的真实等级 + ZoneLevelRange zoneLvl = GetZoneRealLevel(currentZoneId); + + uint32 currentDispatched = 0; + + SessionMap const& sessions = sWorld->GetAllSessions(); + for (auto const& pair : sessions) + { + if (currentDispatched >= actorsNeeded) break; + + Player* bot = pair.second->GetPlayer(); + if (!bot || !bot->IsInWorld() || !bot->IsPlayerBot() || bot->isDead() || bot->IsInCombat()) + continue; + + PlayerAI* botAI = dynamic_cast(bot->GetAI()); + if (!botAI) continue; + + // 1. 只挑选当前正在休眠的机器人 + if (!botAI->IsDirectorSleeping()) continue; + // 2. 绝对不抓有队伍的机器人! + if (bot->GetGroup()) continue; + // 3. 阵营海关! + if (bot->GetTeamId() != teamId) continue; + // 4. 衣着防呆 + if (!bot->EquipIsTidiness() || (bot->getLevel() >= 10 && bot->GetSpecializationId() == 0)) continue; + + // ================= 等级智能过滤 ================= + uint32 botLevel = bot->getLevel(); + + if (isInCity) + { + // 如果在主城:任何等级都可以出现 (老少皆宜,大城气象) + } + else + { + // 如果在野外:启用极其真实的等级过滤网 + int destinyRoll = urand(1, 100); + + if (destinyRoll <= 90) // 90% 的群演必须严格符合地图等级 + { + if (zoneLvl.minLevel != 0) + { + // 字典里有的地图:严格按字典等级过滤 (允许上下浮动 2 级) + if (botLevel < (zoneLvl.minLevel > 2 ? zoneLvl.minLevel - 2 : 1) || botLevel > zoneLvl.maxLevel + 2) + continue; + } + else + { + // 字典里没写的地图:以前的原生逻辑,按玩家等级上下 5 级过滤 + if (botLevel + 5 < playerLevel || botLevel > playerLevel + 5) + continue; + } + } + else + { + // 10% 的群演是“路过的大佬”或者“满级采药大军” + // 只要大于等于地图等级都可以,制造惊喜感 + if (botLevel < playerLevel) + continue; + } + } + // =============================================== + + // 【验尸官打印】:空投日志 + TC_LOG_ERROR("server", ">>> [导演筹备] 为玩家 [%s] 空投 %u级 群演 [%s]! Map:%u, Zone:%u", + realPlayer->GetName().c_str(), botLevel, bot->GetName().c_str(), currentMapId, currentZoneId); + + // ================= 视野外散布空投计算 (防叠罗汉机制) ================= + // 绝不砸在玩家头顶!在玩家周围 20~45 码外进行环形空投,让他们自己“走入”视线! + float dropAngle = frand(0.0f, 6.28f); + float dropDist = frand(20.0f, 45.0f); + + float x = realPlayer->GetPositionX() + dropDist * std::cos(dropAngle); + float y = realPlayer->GetPositionY() + dropDist * std::sin(dropAngle); + float z = realPlayer->GetPositionZ(); + float o = frand(0.0f, 6.28f); + + // ================= 场务执行 ================= + if (bot->IsInFlight()) continue; + + bot->InterruptNonMeleeSpells(false); + bot->SetStandState(UNIT_STAND_STATE_STAND); + bot->StopMoving(); + bot->GetMotionMaster()->Clear(); + bot->Dismount(); + + // 传送到计算好的散布点 + bool teleSuccess = bot->TeleportTo(currentMapId, x, y, z, o); + if (!teleSuccess && bot->GetMapId() == currentMapId) + { + bot->Relocate(x, y, z, o); + teleSuccess = true; + } + + if (!teleSuccess) continue; + + // 完美位面继承 + PhasingHandler::InheritPhaseShift(bot, realPlayer); + bot->UpdateObjectVisibility(true); + + botAI->SetDirectorDrafted(true); + botAI->SetDirectorAnchor(currentMapId, realPlayer->GetPositionX(), realPlayer->GetPositionY(), realPlayer->GetPositionZ()); + + currentDispatched++; + } + } + +private: + uint32 m_radarTimer = 0; // 雷达计时器 +}; + +// ========================================== +// 玩家事件监听器 (保留:切地图瞬间爆发空投) +// ========================================== +class AIDirectorPlayerScript : public PlayerScript +{ +public: + AIDirectorPlayerScript() : PlayerScript("AIDirectorPlayerScript") { } + + void OnUpdateZone(Player* player, Area* oldArea, Area* newArea) override + { + if (!player || player->IsPlayerBot()) return; + TC_LOG_INFO("server", "[AI Director] 侦测到真实玩家 [%s] 跨越了区域边界。开始调拨群演...", player->GetName().c_str()); + + // 切图时,瞬间调拨 50 人填满视野 + AIDirectorMgr::GetInstance()->DispatchActorsToPlayer(player, 50); + } + + void OnLogin(Player* player, bool firstLogin) override + { + if (!player || player->IsPlayerBot()) return; + TC_LOG_INFO("server", "[AI Director] 真实玩家 [%s] 登录游戏。正在预热周边环境...", player->GetName().c_str()); + + AIDirectorMgr::GetInstance()->DispatchActorsToPlayer(player, 50); + } +}; + +// ========================================== +// 世界事件监听器 (全局 5秒 雷达心脏) +// ========================================== +class AIDirectorWorldScript : public WorldScript +{ +public: + AIDirectorWorldScript() : WorldScript("AIDirectorWorldScript") { } + + void OnStartup() override + { + TC_LOG_INFO("server", "[AI Director] 导演系统已上线!动态视锥与全域 AI 场务调度就绪..."); + } + + // 底层主循环每次心跳都会触发这个 + void OnUpdate(uint32 diff) override + { + AIDirectorMgr::GetInstance()->Update(diff); + } +}; + +void AddSC_ai_director() +{ + new AIDirectorPlayerScript(); + new AIDirectorWorldScript(); +} \ No newline at end of file diff --git a/src/server/scripts/Custom/cs_mod_ollama.cpp b/src/server/scripts/Custom/cs_mod_ollama.cpp new file mode 100644 index 0000000000..475c0c71d3 --- /dev/null +++ b/src/server/scripts/Custom/cs_mod_ollama.cpp @@ -0,0 +1,481 @@ +/* + * DestinyCore 7.3.5 - AI Bot Bridge (Ultimate Commander Edition) + * 完美修复头文件、正则表达式抓取、多频道路由与并发控制 + */ + +#include "ScriptMgr.h" +#include "Player.h" +#include "Config.h" +#include "Chat.h" +#include "Group.h" +#include "Guild.h" +#include "GuildMgr.h" // 用于获取公会名称 +#include "World.h" // 引入 sWorld 和 SessionMap +#include "WorldSession.h" // 引入 WorldSession 解决 ChatHandler 连带报错 +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" +#include "QuestDef.h" +#include "ObjectMgr.h" +#include +#include +#include +#include +#include +#include + +struct AIReplyMsg { + ObjectGuid botGuid; + std::string text; + uint32 chatType; + ObjectGuid senderGuid; +}; + +std::mutex g_aiReplyMutex; +std::vector g_aiReplyQueue; + +class cs_mod_ollama : public PlayerScript +{ +public: + cs_mod_ollama() : PlayerScript("cs_mod_ollama") { } + + std::string DecodeUnicodeEscape(const std::string& input) const + { + std::string output; + for (size_t i = 0; i < input.length(); ) + { + if (input[i] == '\\' && i + 1 < input.length() && input[i+1] == 'u' && i + 5 < input.length()) + { + std::string hexStr = input.substr(i + 2, 4); + try { + int codePoint = std::stoi(hexStr, nullptr, 16); + if (codePoint <= 0x7F) { output += static_cast(codePoint); } + else if (codePoint <= 0x7FF) { + output += static_cast(0xC0 | ((codePoint >> 6) & 0x1F)); + output += static_cast(0x80 | (codePoint & 0x3F)); + } else if (codePoint <= 0xFFFF) { + output += static_cast(0xE0 | ((codePoint >> 12) & 0x0F)); + output += static_cast(0x80 | ((codePoint >> 6) & 0x3F)); + output += static_cast(0x80 | (codePoint & 0x3F)); + } + } catch (...) { output += "\\u" + hexStr; } + i += 6; + } + else if (input[i] == '\\' && i + 1 < input.length() && input[i+1] == 'n') { output += '\n'; i += 2; } + else if (input[i] == '\\' && i + 1 < input.length() && input[i+1] == '"') { output += '"'; i += 2; } + else { output += input[i]; i++; } + } + return output; + } + + std::string EscapeJsonString(const std::string& input) const + { + std::ostringstream ss; + for (char ch : input) + { + unsigned char c = (unsigned char)ch; + if (c == '"') ss << "\\\""; + else if (c == '\\') ss << "\\\\"; + else if (c == '\b') ss << "\\b"; + else if (c == '\f') ss << "\\f"; + else if (c == '\n') ss << "\\n"; + else if (c == '\r') ss << "\\r"; + else if (c == '\t') ss << "\\t"; + else if (c < 32) { } + else ss << ch; + } + return ss.str(); + } + + std::string GetClassName(uint8 classId) const + { + switch (classId) + { + case 1: return "warrior"; case 2: return "paladin"; case 3: return "hunter"; + case 4: return "rogue"; case 5: return "priest"; case 6: return "death knight"; + case 7: return "shaman"; case 8: return "mage"; case 9: return "warlock"; + case 10: return "monk"; case 11: return "druid"; case 12: return "demon hunter"; + default: return "unknown"; + } + } + + std::string GetRaceName(uint8 raceId) const + { + switch (raceId) + { + case 1: return "Human"; case 2: return "Orc"; case 3: return "Dwarf"; + case 4: return "Night Elf"; case 5: return "Undead"; case 6: return "Tauren"; + case 7: return "Gnome"; case 8: return "Troll"; case 9: return "Goblin"; + case 10: return "Blood Elf"; case 11: return "Draenei"; case 22: return "Worgen"; + case 24: case 25: case 26: return "Pandaren"; + default: return "Unknown"; + } + } + + // 包含 7.3.5 全职业 36 系专精 ID,并带有职业兜底机制 + std::string GetSpecName(uint32 specId, uint8 classId) const + { + switch (specId) + { + // 法师 Mage + case 62: return "arcane"; case 63: return "fire"; case 64: return "frost"; + // 圣骑士 Paladin + case 65: return "holy"; case 66: return "protection"; case 70: return "retribution"; + // 战士 Warrior + case 71: return "arms"; case 72: return "fury"; case 73: return "protection"; + // 德鲁伊 Druid + case 102: return "balance"; case 103: return "feral combat"; case 104: return "guardian"; case 105: return "restoration"; + // 死亡骑士 DK + case 250: return "blood"; case 251: return "frost"; case 252: return "unholy"; + // 猎人 Hunter + case 253: return "beast mastery"; case 254: return "marksmanship"; case 255: return "survival"; + // 牧师 Priest + case 256: return "discipline"; case 257: return "holy"; case 258: return "shadow"; + // 盗贼 Rogue (7.3.5 的狂徒对应 3.3.5 的战斗,这里传 combat 以兼容你的 Python 设定) + case 259: return "assassination"; case 260: return "combat"; case 261: return "subtlety"; + // 萨满 Shaman + case 262: return "elemental"; case 263: return "enhancement"; case 264: return "restoration"; + // 术士 Warlock + case 265: return "affliction"; case 266: return "demonology"; case 267: return "destruction"; + // 武僧 Monk + case 268: return "brewmaster"; case 269: return "windwalker"; case 270: return "mistweaver"; + // 恶魔猎手 DH + case 577: return "havoc"; case 581: return "vengeance"; + default: + // 【终极兜底】:如果机器人还没学专精(或者核心返回 0),根据职业强制给一个默认天赋,防止 AI 懵逼 + switch (classId) { + case 1: return "arms"; case 2: return "holy"; case 3: return "marksmanship"; + case 4: return "combat"; case 5: return "holy"; case 6: return "unholy"; + case 7: return "restoration"; case 8: return "frost"; case 9: return "destruction"; + case 10: return "windwalker"; case 11: return "restoration"; case 12: return "havoc"; + default: return "unknown"; + } + } + } + + std::string GetGuildNameSafe(Player* p) const + { + if (!p) return "无"; + if (uint32 guildId = p->GetGuildId()) + { + if (Guild* guild = sGuildMgr->GetGuildById(guildId)) + return guild->GetName(); + } + return "无"; + } + + std::string BuildEnvironmentSnapshot(Player* bot, Player* sender, const std::string& msg, bool isWhisper) + { + std::ostringstream prompt; + std::string botClass = GetClassName(bot->getClass()); + std::string senderClass = sender ? GetClassName(sender->getClass()) : "unknown"; + std::string botGuild = GetGuildNameSafe(bot); + std::string senderGuild = GetGuildNameSafe(sender); + + prompt << "你是一名魔兽世界军团再临(7.3.5)版本的玩家。在当前版本中,“天赋”就是指你的“专精”(Specialization)。当别人问你的专精或天赋时,请直接回答你当前的专精名称,绝对不要去虚构主点或副点。"; + // 【关键修复:强转 uint32,解决等级变字母的乱码失忆 Bug】 + prompt << "名字:" << bot->GetName() << ",等级:" << (uint32)bot->getLevel() << " " << botClass << ","; + prompt << "请务必使用你的个性进行回应,你的个性是:LONE_WOLF:Keep responses short, direct, and avoid unnecessary chatter.。 "; + + if (sender) + { + prompt << "一名等级" << (uint32)sender->getLevel() << "的" << senderClass << "玩家" << sender->GetName(); + if (isWhisper) prompt << "悄悄对你说:'" << msg << "'。"; + else prompt << "说:'" << msg << "'。"; + } + + std::string botGender = bot->getGender() == GENDER_MALE ? "Male" : "Female"; + + prompt << "你的信息:" << GetRaceName(bot->getRace()) << " " << botGender << "," + << "天赋:" << GetSpecName(bot->GetSpecializationId(), bot->getClass()) << "(这是你唯一且固定的专精,绝对不要说自己主点或副点!) " << botClass << "," + << "阵营:" << (bot->GetTeamId() == TEAM_ALLIANCE ? "Alliance" : "Horde") << "," + << "公会:" << botGuild << "," + << "队伍:" << (bot->GetGroup() ? "Group" : "Solo") << "," + << "金币:" << (bot->GetMoney() / 10000) << "。"; + + if (sender) + { + std::string senderGender = sender->getGender() == GENDER_MALE ? "Male" : "Female"; + prompt << "玩家信息:" << GetRaceName(sender->getRace()) << " " << senderGender << "," + << "天赋:" << GetSpecName(sender->GetSpecializationId(), sender->getClass()) << " " << senderClass << "," + << "阵营:" << (sender->GetTeamId() == TEAM_ALLIANCE ? "Alliance" : "Horde") << "," + << "公会:" << senderGuild << "," + << "队伍:" << (sender->GetGroup() ? "Group" : "Solo") << "," + << "金币:" << (sender->GetMoney() / 10000) << "," + << "距离:" << std::fixed << std::setprecision(2) << bot->GetDistance(sender) << "码。"; + } + + prompt << "位置:" << bot->GetMapAreaAndZoneString() << ",区域:" << bot->GetMapAreaAndZoneString() << ",地图:未知。"; + + prompt << "只回应新消息。不要评论,不要元对话,不要前缀——直接回复。 用自然语气回复。使用真实的魔兽世界语调。如果被挑衅要直接回应。如果提供方向要精确。永远不要与你的职业、种族或位置相矛盾。永远不要像叙述者一样行动——就像玩家一样回应。当前上下文:\n"; + prompt << (bot->IsInCombat() ? "IN COMBAT." : "NOT IN COMBAT.") << " , Mana: " << bot->GetPower(POWER_MANA) << "/" << bot->GetMaxPower(POWER_MANA) << "\n"; + + prompt << "\n法术:\n**普通攻击** - Costs 0 mana\n"; + + prompt << "\n任务:\n"; + auto const& questMap = bot->getQuestStatusMap(); + for (auto const& qPair : questMap) + { + if (qPair.second.Status == QUEST_STATUS_INCOMPLETE) + prompt << "Quest \"ID:" << qPair.first << "\" is in progress\n"; + } + + prompt << "\n可见物体:\n"; + Trinity::AnyUnitInObjectRangeCheck u_check(bot, 40.0f); + std::list creatures; + Trinity::CreatureListSearcher searcher(bot, creatures, u_check); + Cell::VisitGridObjects(bot, searcher, 40.0f); + uint8 creatureCount = 0; + for (auto c : creatures) + { + // 【关键修复:过滤隐形怪和尸体,解决 AI 幻觉】 + if (!c->IsAlive() || !c->IsVisible()) continue; + + if (creatureCount++ > 15) break; + prompt << "- " << (c->IsHostileTo(bot) ? "ENEMY: " : "FRIENDLY: ") + << c->GetName() << ", Level: " << (uint32)c->getLevel() + << ", HP: " << c->GetHealth() << "/" << c->GetMaxHealth() + << ", Distance: " << std::fixed << std::setprecision(2) << bot->GetDistance(c) << "\n"; + } + + prompt << "\n附近玩家:\n"; + std::list players; + Trinity::PlayerListSearcher p_searcher(bot, players, u_check); + Cell::VisitGridObjects(bot, p_searcher, 40.0f); + for (auto p : players) + { + if (!p->IsAlive() || !p->IsVisible()) continue; + + if (p != bot && (!bot->GetGroup() || !bot->GetGroup()->IsMember(p->GetGUID()))) + prompt << "[Ollama Chat] Player: " << p->GetName() << " (Level: " << (uint32)p->getLevel() << ")\n"; + } + + return prompt.str(); + } + + void SendToPythonProxy(Player* bot, Player* sender, const std::string& promptStr, uint32 chatType) + { + ObjectGuid botGuid = bot->GetGUID(); + ObjectGuid senderGuid = sender ? sender->GetGUID() : ObjectGuid::Empty; + std::string safePrompt = EscapeJsonString(promptStr); + std::string sName = sender ? EscapeJsonString(sender->GetName()) : "Unknown"; + std::string bName = EscapeJsonString(bot->GetName()); + std::string jsonPayload = "{\"prompt\": \"" + safePrompt + "\", \"player_name\": \"" + sName + "\", \"bot_name\": \"" + bName + "\"}"; + + std::thread([this, botGuid, senderGuid, chatType, jsonPayload]() + { + try + { + boost::asio::io_service io_service; + boost::asio::ip::tcp::resolver resolver(io_service); + boost::asio::ip::tcp::resolver::query query("127.0.0.1", "5000"); + boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + + boost::asio::ip::tcp::socket socket(io_service); + boost::asio::connect(socket, endpoint_iterator); + + boost::asio::streambuf request; + std::ostream request_stream(&request); + request_stream << "POST /api/generate HTTP/1.1\r\n" + << "Host: 127.0.0.1:5000\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << jsonPayload.length() << "\r\n" + << "Connection: close\r\n\r\n" + << jsonPayload; + + boost::asio::write(socket, request); + + boost::asio::streambuf response; + boost::system::error_code error; + std::ostringstream response_stream; + while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) + { + response_stream << &response; + } + + std::string fullResponse = response_stream.str(); + std::string replyText = ""; + size_t pos = fullResponse.find("\"response\":"); + if (pos != std::string::npos) + { + size_t start = fullResponse.find("\"", pos + 11); + if (start != std::string::npos) + { + size_t end = fullResponse.find("\"", start + 1); + while (end != std::string::npos && fullResponse[end-1] == '\\') end = fullResponse.find("\"", end + 1); + if (end != std::string::npos) replyText = fullResponse.substr(start + 1, end - start - 1); + } + } + + if (!replyText.empty()) + { + std::string finalChineseReply = DecodeUnicodeEscape(replyText); + std::lock_guard lock(g_aiReplyMutex); + g_aiReplyQueue.push_back({botGuid, finalChineseReply, chatType, senderGuid}); + } + } + catch (std::exception& e) + { + TC_LOG_ERROR("scripts.custom", "[AI_BRIDGE] Async HTTP Error: %s", e.what()); + } + }).detach(); + } + + void OnUpdate(Player* player, uint32 /*diff*/) override + { + if (g_aiReplyQueue.empty()) return; + + std::lock_guard lock(g_aiReplyMutex); + for (auto it = g_aiReplyQueue.begin(); it != g_aiReplyQueue.end(); ) + { + if (it->botGuid == player->GetGUID()) + { + uint32 cType = it->chatType; + std::string replyText = it->text; + ObjectGuid senderGuid = it->senderGuid; + + if (cType == CHAT_MSG_WHISPER || cType == 7) + { + if (Player* sender = ObjectAccessor::FindPlayer(senderGuid)) + ChatHandler(sender->GetSession()).PSendSysMessage("|cffFF80FF[密语] %s: %s|r", player->GetName().c_str(), replyText.c_str()); + } + else if (cType == CHAT_MSG_PARTY || cType == 49 || cType == 2) + { + if (Group* group = player->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next()) + if (Player* member = ref->GetSource()) + ChatHandler(member->GetSession()).PSendSysMessage("|cffAAAAFF[小队] [%s]: %s|r", player->GetName().c_str(), replyText.c_str()); + } + else player->Say(replyText, LANG_UNIVERSAL); + } + else if (cType == CHAT_MSG_RAID || cType == 39 || cType == 3 || cType == CHAT_MSG_RAID_WARNING || cType == CHAT_MSG_RAID_LEADER) + { + if (Group* group = player->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next()) + if (Player* member = ref->GetSource()) + ChatHandler(member->GetSession()).PSendSysMessage("|cffFF7F00[团队] [%s]: %s|r", player->GetName().c_str(), replyText.c_str()); + } + else player->Say(replyText, LANG_UNIVERSAL); + } + else if (cType == CHAT_MSG_GUILD || cType == 4) + { + if (uint32 guildId = player->GetGuildId()) + { + for (auto const& sessionPair : sWorld->GetAllSessions()) + { + if (Player* onlinePlayer = sessionPair.second->GetPlayer()) + { + if (onlinePlayer->GetGuildId() == guildId) + ChatHandler(onlinePlayer->GetSession()).PSendSysMessage("|cff40FF40[公会] [%s]: %s|r", player->GetName().c_str(), replyText.c_str()); + } + } + } + } + else + { + player->Say(replyText, LANG_UNIVERSAL); + } + + it = g_aiReplyQueue.erase(it); + } + else + { + ++it; + } + } + } + + void HandleAreaOrGroupChat(Player* player, const std::string& msg, uint32 chatType, Group* explicitGroup = nullptr) + { + bool foundBot = false; + + if (chatType == CHAT_MSG_PARTY || chatType == CHAT_MSG_RAID || chatType == CHAT_MSG_RAID_LEADER || + chatType == CHAT_MSG_RAID_WARNING || chatType == 49 || chatType == 39 || chatType == 2 || chatType == 3) + { + Group* checkGroup = explicitGroup ? explicitGroup : player->GetGroup(); + if (checkGroup) + { + for (GroupReference* itr = checkGroup->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsPlayerBot() && member != player) + { + std::string snapshot = BuildEnvironmentSnapshot(member, player, msg, false); + SendToPythonProxy(member, player, snapshot, chatType); + foundBot = true; + } + } + } + } + else if (chatType == CHAT_MSG_GUILD || chatType == 4) + { + if (uint32 guildId = player->GetGuildId()) + { + for (auto const& sessionPair : sWorld->GetAllSessions()) + { + if (Player* member = sessionPair.second->GetPlayer()) + { + if (member->IsPlayerBot() && member->GetGuildId() == guildId && member != player) + { + std::string snapshot = BuildEnvironmentSnapshot(member, player, msg, false); + SendToPythonProxy(member, player, snapshot, chatType); + foundBot = true; + } + } + } + } + } + else if (chatType == CHAT_MSG_SAY || chatType == CHAT_MSG_YELL || chatType == 1) + { + Trinity::AnyUnitInObjectRangeCheck u_check(player, 40.0f); + std::list players; + Trinity::PlayerListSearcher p_searcher(player, players, u_check); + Cell::VisitGridObjects(player, p_searcher, 40.0f); + + for (Player* member : players) + { + if (member && member->IsPlayerBot() && member != player) + { + std::string snapshot = BuildEnvironmentSnapshot(member, player, msg, false); + SendToPythonProxy(member, player, snapshot, chatType); + foundBot = true; + } + } + } + + if (foundBot) { + TC_LOG_ERROR("scripts.custom", "[AI_BRIDGE] 频道 %d 的信息已完成向所有关联机器人的并发推送!", chatType); + } + } + + void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg) override + { + HandleAreaOrGroupChat(player, msg, type); + } + + void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg, Group* group) override + { + HandleAreaOrGroupChat(player, msg, type, group); + } + + void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg, Guild* guild) override + { + HandleAreaOrGroupChat(player, msg, type); + } + + void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg, Player* receiver) override + { + if (type == CHAT_MSG_WHISPER && receiver && receiver->IsPlayerBot()) + { + std::string snapshot = BuildEnvironmentSnapshot(receiver, player, msg, true); + SendToPythonProxy(receiver, player, snapshot, type); + } + } +}; + +void AddSC_mod_ollama_chat() +{ + new cs_mod_ollama(); +} \ No newline at end of file diff --git a/src/server/scripts/Custom/custom_script_loader.cpp b/src/server/scripts/Custom/custom_script_loader.cpp index e9231d6495..b36ebe7dac 100644 --- a/src/server/scripts/Custom/custom_script_loader.cpp +++ b/src/server/scripts/Custom/custom_script_loader.cpp @@ -17,15 +17,20 @@ // This is where scripts' loading functions should be declared: +void AddSC_mod_ollama_chat(); void AddSC_custom_npcs(); void AddSC_quest_conversation(); void AddSC_solocraft(); void AddLfgSoloScripts(); - +void AddSC_ai_director(); void AddCustomScripts() { AddSC_custom_npcs(); AddSC_quest_conversation(); AddSC_solocraft(); - AddLfgSoloScripts(); + AddLfgSoloScripts(); + AddSC_mod_ollama_chat(); + AddSC_ai_director(); } + + diff --git a/src/server/scripts/DarkmoonIsland/darkmoon_faire.cpp b/src/server/scripts/DarkmoonIsland/darkmoon_faire.cpp index 8fdff6f77c..4e7f5350dc 100644 --- a/src/server/scripts/DarkmoonIsland/darkmoon_faire.cpp +++ b/src/server/scripts/DarkmoonIsland/darkmoon_faire.cpp @@ -1,4 +1,4 @@ - + #include "ScriptMgr.h" #include "Cell.h" #include "CellImpl.h" @@ -33,8 +33,8 @@ class npc_darkmoon_faire_mystic_mage : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON = "Llvame a la zona de escala de la feria."; - BOX_TEXT = "Viajar a la zona de escala de la feria te costar:"; + GOSSIP_BUTTON = "Ll vame a la zona de escala de la feria."; + BOX_TEXT = "Viajar a la zona de escala de la feria te costar :"; break; default: GOSSIP_BUTTON = "Take me to the faire staging area."; @@ -142,11 +142,11 @@ class npc_selina_dourman : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Gua del aventurero de la Luna Negra?"; - GOSSIP_BUTTON_2 = "Qu puedo comprar?"; - GOSSIP_BUTTON_3 = "Vales para la Feria de la Luna Negra?"; - GOSSIP_BUTTON_4 = "Cartas de la Luna Negra?"; - GOSSIP_BUTTON_5 = "Atracciones?"; + GOSSIP_BUTTON_1 = " Gu a del aventurero de la Luna Negra?"; + GOSSIP_BUTTON_2 = " Qu puedo comprar?"; + GOSSIP_BUTTON_3 = " Vales para la Feria de la Luna Negra?"; + GOSSIP_BUTTON_4 = " Cartas de la Luna Negra?"; + GOSSIP_BUTTON_5 = " Atracciones?"; break; default: GOSSIP_BUTTON_1 = "Darkmoon Adventurer's Guide?"; @@ -187,19 +187,19 @@ class npc_selina_dourman : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Gua del aventurero de la Luna Negra?"; - GOSSIP_BUTTON_2 = "Qu puedo comprar?"; - GOSSIP_BUTTON_3 = "Vales para la Feria de la Luna Negra?"; - GOSSIP_BUTTON_4 = "Cartas de la Luna Negra?"; - GOSSIP_BUTTON_5 = "Atracciones?"; - GOSSIP_BUTTON_6 = "Me puedes dar una gua del aventurero de la Luna Negra?"; - GOSSIP_BUTTON_7 = "Cuntame ms."; - GOSSIP_BUTTON_8 = "Tonques?"; - GOSSIP_BUTTON_9 = "Can?"; - GOSSIP_BUTTON_10 = "Golpear al gnoll?"; - GOSSIP_BUTTON_11 = "Lanzamiento de anillos?"; - GOSSIP_BUTTON_12 = "Galera de tiro?"; - GOSSIP_BUTTON_13 = "Clarividente?"; + GOSSIP_BUTTON_1 = " Gu a del aventurero de la Luna Negra?"; + GOSSIP_BUTTON_2 = " Qu puedo comprar?"; + GOSSIP_BUTTON_3 = " Vales para la Feria de la Luna Negra?"; + GOSSIP_BUTTON_4 = " Cartas de la Luna Negra?"; + GOSSIP_BUTTON_5 = " Atracciones?"; + GOSSIP_BUTTON_6 = " Me puedes dar una gu a del aventurero de la Luna Negra?"; + GOSSIP_BUTTON_7 = "Cu ntame m s."; + GOSSIP_BUTTON_8 = " Tonques?"; + GOSSIP_BUTTON_9 = " Ca n?"; + GOSSIP_BUTTON_10 = " Golpear al gnoll?"; + GOSSIP_BUTTON_11 = " Lanzamiento de anillos?"; + GOSSIP_BUTTON_12 = " Galer a de tiro?"; + GOSSIP_BUTTON_13 = " Clarividente?"; break; default: GOSSIP_BUTTON_1 = "Darkmoon Adventurer's Guide?"; @@ -544,8 +544,8 @@ class npc_mola : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo puedo jugar a Golpea al gnoll?"; - GOSSIP_BUTTON_2 = "Listo para aporrear! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo puedo jugar a Golpea al gnoll?"; + GOSSIP_BUTTON_2 = " Listo para aporrear! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; break; default: GOSSIP_BUTTON_1 = "How do I play Whack-a-gnoll?"; @@ -572,8 +572,8 @@ class npc_mola : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo puedo jugar a Golpea al gnoll?"; - GOSSIP_BUTTON_2 = "Listo para aporrear! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo puedo jugar a Golpea al gnoll?"; + GOSSIP_BUTTON_2 = " Listo para aporrear! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; GOSSIP_BUTTON_3 = "Comprendo."; break; default: @@ -707,8 +707,8 @@ class npc_maxima_blastenheimer : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo se usa el can?"; - GOSSIP_BUTTON_2 = "Lnzame! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo se usa el ca n?"; + GOSSIP_BUTTON_2 = " L nzame! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; break; default: GOSSIP_BUTTON_1 = "How do I use the cannon?"; @@ -735,12 +735,12 @@ class npc_maxima_blastenheimer : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo uso el can?"; - GOSSIP_BUTTON_2 = "Lnzame! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo uso el ca n?"; + GOSSIP_BUTTON_2 = " L nzame! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; GOSSIP_BUTTON_3 = "Comprendo."; break; default: - GOSSIP_BUTTON_1 = "How do I use the cannon?"; + GOSSIP_BUTTON_1 = " How do I use the cannon?"; GOSSIP_BUTTON_2 = "Launch me! |cFF0000FF(Darkmoon Game Token)|r"; GOSSIP_BUTTON_3 = "Alright."; break; @@ -889,8 +889,8 @@ class npc_teleportologist_fozlebub : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON = "Teletransprtame al can."; - BOX_TEXT = "El teletransporte al can te costar:"; + GOSSIP_BUTTON = "Teletransp rtame al ca n."; + BOX_TEXT = "El teletransporte al ca n te costar :"; break; default: GOSSIP_BUTTON = "Teleport me to the cannon."; @@ -1169,8 +1169,8 @@ class npc_finlay_coolshot : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo puedo jugar a la batalla de tonques?"; - GOSSIP_BUTTON_2 = "Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo puedo jugar a la batalla de tonques?"; + GOSSIP_BUTTON_2 = " Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; break; default: GOSSIP_BUTTON_1 = "How do I play the Tonk Challenge?"; @@ -1197,8 +1197,8 @@ class npc_finlay_coolshot : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo puedo jugar a la batalla de tonques?"; - GOSSIP_BUTTON_2 = "Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo puedo jugar a la batalla de tonques?"; + GOSSIP_BUTTON_2 = " Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; GOSSIP_BUTTON_3 = "Comprendo."; break; default: @@ -1274,8 +1274,8 @@ class npc_jessica_rogers : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo puedo jugar al lanzamiento de anillo?"; - GOSSIP_BUTTON_2 = "Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo puedo jugar al lanzamiento de anillo?"; + GOSSIP_BUTTON_2 = " Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; break; default: GOSSIP_BUTTON_1 = "How do I play the Ring Toss?"; @@ -1302,8 +1302,8 @@ class npc_jessica_rogers : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo puedo jugar al lanzamiento de anillo?"; - GOSSIP_BUTTON_2 = "Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo puedo jugar al lanzamiento de anillo?"; + GOSSIP_BUTTON_2 = " Estoy listo para jugar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; GOSSIP_BUTTON_3 = "Comprendo."; break; default: @@ -1454,8 +1454,8 @@ class npc_rinling : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo funciona la galera de tiro?"; - GOSSIP_BUTTON_2 = "Estoy listo para disparar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo funciona la galer a de tiro?"; + GOSSIP_BUTTON_2 = " Estoy listo para disparar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; break; default: GOSSIP_BUTTON_1 = "How does the Shooting Gallery work?"; @@ -1482,8 +1482,8 @@ class npc_rinling : public CreatureScript { case LOCALE_esES: case LOCALE_esMX: - GOSSIP_BUTTON_1 = "Cmo funciona la galera de tiro?"; - GOSSIP_BUTTON_2 = "Estoy listo para disparar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; + GOSSIP_BUTTON_1 = " C mo funciona la galer a de tiro?"; + GOSSIP_BUTTON_2 = " Estoy listo para disparar! |cFF0000FF(Ficha de juego de la Luna Negra)|r"; GOSSIP_BUTTON_3 = "Comprendo."; break; default: @@ -1975,7 +1975,7 @@ class spell_ring_toss : public SpellScriptLoader }; // -// BRUTAL HACK - Puesto hasta que se arregle el achievementcriteria, que me he peleado con l y no consigo arreglarlo. +// BRUTAL HACK - Puesto hasta que se arregle el achievementcriteria, que me he peleado con l y no consigo arreglarlo. // class item_darkmoon_faire_fireworks : public ItemScript