Fractured: Paragon core hooks, mod-paragon, mod-ale, Docker build cap
- Track mod-paragon and mod-ale (un-ignore modules in .gitignore). - Ship docker-compose.override.yml with CMAKE_EXTRA_OPTIONS for LuaJIT (mod-ale). - Dockerfile: CBUILD_PARALLEL default to limit OOM under Docker/WSL2. - Core: CLASS_PARAGON sticky combo points (DetachComboTarget), selection rebind, Spell::CheckPower rune path for multi-resource Paragon. - spell_dk_death_rune: IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY) for Blood of the North / Reaping / DRM on Paragon. - Remove temporary Paragon CheckPower logging. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 2 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// From SC
|
||||
void AddSC_ALE();
|
||||
|
||||
// Add all
|
||||
void Addmod_aleScripts()
|
||||
{
|
||||
AddSC_ALE();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,63 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
AC-Eluna:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
repository: azerothcore/azerothcore-wotlk
|
||||
ref: 'master'
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: false
|
||||
repository: azerothcore/mod-eluna-lua-engine
|
||||
path: modules/mod-eluna-lua-engine
|
||||
- uses: actions/checkout@v2
|
||||
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 <<CONFIG_SH
|
||||
MTHREADS=4
|
||||
CWARNINGS=ON
|
||||
CDEBUG=OFF
|
||||
CTYPE=Release
|
||||
CSCRIPTS=static
|
||||
CUNIT_TESTS=ON
|
||||
CSERVERS=ON
|
||||
CTOOLS=ON
|
||||
CSCRIPTPCH=ON
|
||||
CCOREPCH=ON
|
||||
CCUSTOMOPTIONS='-DCMAKE_C_FLAGS="-Werror" -DCMAKE_CXX_FLAGS="-Werror"'
|
||||
DB_CHARACTERS_CONF="MYSQL_USER='root'; MYSQL_PASS='root'; MYSQL_HOST='localhost';"
|
||||
DB_AUTH_CONF="MYSQL_USER='root'; MYSQL_PASS='root'; MYSQL_HOST='localhost';"
|
||||
DB_WORLD_CONF="MYSQL_USER='root'; MYSQL_PASS='root'; MYSQL_HOST='localhost';"
|
||||
CONFIG_SH
|
||||
|
||||
time sudo apt-get update -y
|
||||
# time sudo apt-get upgrade -y
|
||||
time sudo apt-get install -y git lsb-release sudo ccache
|
||||
time ./acore.sh install-deps
|
||||
|
||||
time sudo apt-get install -y clang-11
|
||||
echo "CCOMPILERC=\"clang-11\"" >> ./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
|
||||
@@ -0,0 +1,36 @@
|
||||
# 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"
|
||||
|
||||
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 .
|
||||
if git status | 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
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Documentation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'master'
|
||||
jobs:
|
||||
Push-Docs-To-AzerothCore-Website:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
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: 'azerothcore/azerothcore.github.io'
|
||||
DESTINATION_FOLDER: 'pages/eluna'
|
||||
DESTINATION_BASE_BRANCH: 'master'
|
||||
DESTINATION_HEAD_BRANCH: 'eluna-docs'
|
||||
PR_TITLE: 'chore: update eluna documentation'
|
||||
COMMIT_MESSAGE: 'chore: update eluna documentation'
|
||||
USER_EMAIL: 'ax.cocat@gmail.com'
|
||||
USER_NAME: 'r-o-b-o-t-o'
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "ALECompat.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
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef ALECOMPAT_H
|
||||
#define ALECOMPAT_H
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
};
|
||||
|
||||
/* 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)
|
||||
|
||||
#ifndef LUA_OK
|
||||
#define LUA_OK 0
|
||||
#endif
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "ALEConfig.h"
|
||||
|
||||
ALEConfig& ALEConfig::GetInstance()
|
||||
{
|
||||
static ALEConfig instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
ALEConfig::ALEConfig() : ConfigValueCache<ALEConfigValues>(ALEConfigValues::CONFIG_VALUE_COUNT)
|
||||
{
|
||||
}
|
||||
|
||||
void ALEConfig::Initialize(bool reload)
|
||||
{
|
||||
ConfigValueCache<ALEConfigValues>::Initialize(reload);
|
||||
}
|
||||
|
||||
void ALEConfig::BuildConfigCache()
|
||||
{
|
||||
SetConfigValue<bool>(ALEConfigValues::ENABLED, "ALE.Enabled", "false");
|
||||
SetConfigValue<bool>(ALEConfigValues::TRACEBACK_ENABLED, "ALE.TraceBack", "false");
|
||||
SetConfigValue<bool>(ALEConfigValues::AUTORELOAD_ENABLED, "ALE.AutoReload", "false");
|
||||
SetConfigValue<bool>(ALEConfigValues::BYTECODE_CACHE_ENABLED, "ALE.BytecodeCache", "false");
|
||||
|
||||
SetConfigValue<std::string>(ALEConfigValues::SCRIPT_PATH, "ALE.ScriptPath", "lua_scripts");
|
||||
SetConfigValue<std::string>(ALEConfigValues::REQUIRE_PATH, "ALE.RequirePaths", "");
|
||||
SetConfigValue<std::string>(ALEConfigValues::REQUIRE_CPATH, "ALE.RequireCPaths", "");
|
||||
|
||||
SetConfigValue<uint32>(ALEConfigValues::AUTORELOAD_INTERVAL, "ALE.AutoReloadInterval", 1);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef ALE_CONFIG_HPP
|
||||
#define ALE_CONFIG_HPP
|
||||
|
||||
#include "ConfigValueCache.h"
|
||||
|
||||
enum class ALEConfigValues : uint32
|
||||
{
|
||||
// Boolean
|
||||
ENABLED = 0,
|
||||
TRACEBACK_ENABLED,
|
||||
AUTORELOAD_ENABLED,
|
||||
BYTECODE_CACHE_ENABLED,
|
||||
|
||||
// String
|
||||
SCRIPT_PATH,
|
||||
REQUIRE_PATH,
|
||||
REQUIRE_CPATH,
|
||||
|
||||
// Number
|
||||
AUTORELOAD_INTERVAL,
|
||||
|
||||
CONFIG_VALUE_COUNT
|
||||
};
|
||||
|
||||
class ALEConfig final : public ConfigValueCache<ALEConfigValues>
|
||||
{
|
||||
public:
|
||||
static ALEConfig& GetInstance();
|
||||
|
||||
void Initialize(bool reload = false);
|
||||
|
||||
bool IsALEEnabled() const { return GetConfigValue<bool>(ALEConfigValues::ENABLED); }
|
||||
bool IsTraceBackEnabled() const { return GetConfigValue<bool>(ALEConfigValues::TRACEBACK_ENABLED); }
|
||||
bool IsAutoReloadEnabled() const { return GetConfigValue<bool>(ALEConfigValues::AUTORELOAD_ENABLED); }
|
||||
bool IsByteCodeCacheEnabled() const { return GetConfigValue<bool>(ALEConfigValues::BYTECODE_CACHE_ENABLED); }
|
||||
|
||||
std::string_view GetScriptPath() const { return GetConfigValue(ALEConfigValues::SCRIPT_PATH); }
|
||||
std::string_view GetRequirePath() const { return GetConfigValue(ALEConfigValues::REQUIRE_PATH); }
|
||||
std::string_view GetRequireCPath() const { return GetConfigValue(ALEConfigValues::REQUIRE_CPATH); }
|
||||
|
||||
uint32 GetAutoReloadInterval() const { return GetConfigValue<uint32>(ALEConfigValues::AUTORELOAD_INTERVAL); }
|
||||
|
||||
protected:
|
||||
void BuildConfigCache() override;
|
||||
|
||||
private:
|
||||
ALEConfig();
|
||||
~ALEConfig() = default;
|
||||
ALEConfig(const ALEConfig&) = delete;
|
||||
ALEConfig& operator=(const ALEConfig&) = delete;
|
||||
};
|
||||
|
||||
#endif // ALE_CONFIG_H
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef _ALE_CREATURE_AI_H
|
||||
#define _ALE_CREATURE_AI_H
|
||||
|
||||
#include "LuaEngine.h"
|
||||
|
||||
struct ScriptedAI;
|
||||
|
||||
struct ALECreatureAI : ScriptedAI
|
||||
{
|
||||
// used to delay the spawn hook triggering on AI creation
|
||||
bool justSpawned;
|
||||
// used to delay movementinform hook (WP hook)
|
||||
std::vector< std::pair<uint32, uint32> > movepoints;
|
||||
|
||||
ALECreatureAI(Creature* creature) : ScriptedAI(creature), justSpawned(true)
|
||||
{
|
||||
}
|
||||
~ALECreatureAI() { }
|
||||
|
||||
//Called at World update tick
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (justSpawned)
|
||||
{
|
||||
justSpawned = false;
|
||||
|
||||
JustRespawned();
|
||||
}
|
||||
|
||||
if (!movepoints.empty())
|
||||
{
|
||||
for (auto& point : movepoints)
|
||||
{
|
||||
if (!sALE->MovementInform(me, point.first, point.second))
|
||||
ScriptedAI::MovementInform(point.first, point.second);
|
||||
}
|
||||
movepoints.clear();
|
||||
}
|
||||
|
||||
if (!sALE->UpdateAI(me, diff))
|
||||
{
|
||||
if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC))
|
||||
ScriptedAI::UpdateAI(diff);
|
||||
}
|
||||
}
|
||||
|
||||
// Called for reaction when initially engaged - this will always happen _after_ JustEnteredCombat
|
||||
// Called at creature aggro either by MoveInLOS or Attack Start
|
||||
void JustEngagedWith(Unit* target) override
|
||||
{
|
||||
if (!sALE->EnterCombat(me, target))
|
||||
ScriptedAI::JustEngagedWith(target);
|
||||
}
|
||||
|
||||
// Called at any Damage from any attacker (before damage apply)
|
||||
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override
|
||||
{
|
||||
if (!sALE->DamageTaken(me, attacker, damage))
|
||||
{
|
||||
ScriptedAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
|
||||
}
|
||||
}
|
||||
|
||||
//Called at creature death
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
if (!sALE->JustDied(me, killer))
|
||||
ScriptedAI::JustDied(killer);
|
||||
}
|
||||
|
||||
//Called at creature killing another unit
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (!sALE->KilledUnit(me, victim))
|
||||
ScriptedAI::KilledUnit(victim);
|
||||
}
|
||||
|
||||
// Called when the creature summon successfully other creature
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
if (!sALE->JustSummoned(me, summon))
|
||||
ScriptedAI::JustSummoned(summon);
|
||||
}
|
||||
|
||||
// Called when a summoned creature is despawned
|
||||
void SummonedCreatureDespawn(Creature* summon) override
|
||||
{
|
||||
if (!sALE->SummonedCreatureDespawn(me, summon))
|
||||
ScriptedAI::SummonedCreatureDespawn(summon);
|
||||
}
|
||||
|
||||
//Called at waypoint reached or PointMovement end
|
||||
void MovementInform(uint32 type, uint32 id) override
|
||||
{
|
||||
// delayed since hook triggers before actually reaching the point
|
||||
// and starting new movement would bug
|
||||
movepoints.push_back(std::make_pair(type, id));
|
||||
}
|
||||
|
||||
// Called before EnterCombat even before the creature is in combat.
|
||||
void AttackStart(Unit* target) override
|
||||
{
|
||||
if (!sALE->AttackStart(me, target))
|
||||
ScriptedAI::AttackStart(target);
|
||||
}
|
||||
|
||||
// Called for reaction at stopping attack at no attackers or targets
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
{
|
||||
if (!sALE->EnterEvadeMode(me))
|
||||
ScriptedAI::EnterEvadeMode();
|
||||
}
|
||||
|
||||
// Called when creature is spawned or respawned (for reseting variables)
|
||||
void JustRespawned() override
|
||||
{
|
||||
if (!sALE->JustRespawned(me))
|
||||
ScriptedAI::JustRespawned();
|
||||
}
|
||||
|
||||
// Called at reaching home after evade
|
||||
void JustReachedHome() override
|
||||
{
|
||||
if (!sALE->JustReachedHome(me))
|
||||
ScriptedAI::JustReachedHome();
|
||||
}
|
||||
|
||||
// Called at text emote receive from player
|
||||
void ReceiveEmote(Player* player, uint32 emoteId) override
|
||||
{
|
||||
if (!sALE->ReceiveEmote(me, player, emoteId))
|
||||
ScriptedAI::ReceiveEmote(player, emoteId);
|
||||
}
|
||||
|
||||
// called when the corpse of this creature gets removed
|
||||
void CorpseRemoved(uint32& respawnDelay) override
|
||||
{
|
||||
if (!sALE->CorpseRemoved(me, respawnDelay))
|
||||
ScriptedAI::CorpseRemoved(respawnDelay);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!sALE->MoveInLineOfSight(me, who))
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
// Called when hit by a spell
|
||||
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
||||
{
|
||||
if (!sALE->SpellHit(me, caster, spell))
|
||||
ScriptedAI::SpellHit(caster, spell);
|
||||
}
|
||||
|
||||
// Called when spell hits a target
|
||||
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
|
||||
{
|
||||
if (!sALE->SpellHitTarget(me, target, spell))
|
||||
ScriptedAI::SpellHitTarget(target, spell);
|
||||
}
|
||||
|
||||
// Called when the creature is summoned successfully by other creature
|
||||
void IsSummonedBy(WorldObject* summoner) override
|
||||
{
|
||||
if (!summoner->ToUnit() || !sALE->OnSummoned(me, summoner->ToUnit()))
|
||||
ScriptedAI::IsSummonedBy(summoner);
|
||||
}
|
||||
|
||||
void SummonedCreatureDies(Creature* summon, Unit* killer) override
|
||||
{
|
||||
if (!sALE->SummonedCreatureDies(me, summon, killer))
|
||||
ScriptedAI::SummonedCreatureDies(summon, killer);
|
||||
}
|
||||
|
||||
// Called when owner takes damage
|
||||
void OwnerAttackedBy(Unit* attacker) override
|
||||
{
|
||||
if (!sALE->OwnerAttackedBy(me, attacker))
|
||||
ScriptedAI::OwnerAttackedBy(attacker);
|
||||
}
|
||||
|
||||
// Called when owner attacks something
|
||||
void OwnerAttacked(Unit* target) override
|
||||
{
|
||||
if (!sALE->OwnerAttacked(me, target))
|
||||
ScriptedAI::OwnerAttacked(target);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "ALEDBCRegistry.h"
|
||||
|
||||
std::vector<DBCDefinition> dbcRegistry = {
|
||||
REGISTER_DBC(GemProperties, GemPropertiesEntry, sGemPropertiesStore),
|
||||
REGISTER_DBC(Spell, SpellEntry, sSpellStore),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef ALEDBCREGISTRY_H
|
||||
#define ALEDBCREGISTRY_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "DBCStores.h"
|
||||
#include "LuaEngine.h"
|
||||
|
||||
struct DBCDefinition
|
||||
{
|
||||
std::string name;
|
||||
void* storage;
|
||||
const std::type_info& type;
|
||||
std::function<const void*(uint32)> lookupFunction;
|
||||
std::function<void(lua_State*, const void*)> pushFunction;
|
||||
};
|
||||
|
||||
extern std::vector<DBCDefinition> dbcRegistry;
|
||||
|
||||
#define REGISTER_DBC(dbcName, entryType, store) \
|
||||
{ \
|
||||
#dbcName, \
|
||||
reinterpret_cast<void*>(&store), \
|
||||
typeid(DBCStorage<entryType>), \
|
||||
[](uint32 id) -> const void* { \
|
||||
return store.LookupEntry(id); \
|
||||
}, \
|
||||
[](lua_State* L, const void* entry) { \
|
||||
auto cast_entry = static_cast<const entryType*>(entry); \
|
||||
ALE::Push(L, *cast_entry); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif // ALEDBCREGISTRY_H
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "ALEEventMgr.h"
|
||||
#include "LuaEngine.h"
|
||||
#include "Object.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
};
|
||||
|
||||
ALEEventProcessor::ALEEventProcessor(ALE** _E, WorldObject* _obj) : m_time(0), obj(_obj), E(_E)
|
||||
{
|
||||
// can be called from multiple threads
|
||||
if (obj)
|
||||
{
|
||||
EventMgr::Guard guard((*E)->eventMgr->GetLock());
|
||||
(*E)->eventMgr->processors.insert(this);
|
||||
}
|
||||
}
|
||||
|
||||
ALEEventProcessor::~ALEEventProcessor()
|
||||
{
|
||||
// can be called from multiple threads
|
||||
{
|
||||
LOCK_ALE;
|
||||
RemoveEvents_internal();
|
||||
}
|
||||
|
||||
if (obj && ALE::IsInitialized())
|
||||
{
|
||||
EventMgr::Guard guard((*E)->eventMgr->GetLock());
|
||||
(*E)->eventMgr->processors.erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ALEEventProcessor::Update(uint32 diff)
|
||||
{
|
||||
m_time += diff;
|
||||
for (EventList::iterator it = eventList.begin(); it != eventList.end() && it->first <= m_time; 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); // Reschedule before calling incase RemoveEvents used
|
||||
|
||||
// Call the timed event
|
||||
(*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);
|
||||
}
|
||||
}
|
||||
|
||||
void ALEEventProcessor::SetStates(LuaEventState state)
|
||||
{
|
||||
for (EventList::iterator it = eventList.begin(); it != eventList.end(); ++it)
|
||||
it->second->SetState(state);
|
||||
if (state == LUAEVENT_STATE_ERASE)
|
||||
eventMap.clear();
|
||||
}
|
||||
|
||||
void ALEEventProcessor::RemoveEvents_internal()
|
||||
{
|
||||
//if (!final)
|
||||
//{
|
||||
// for (EventList::iterator it = eventList.begin(); it != eventList.end(); ++it)
|
||||
// it->second->to_Abort = true;
|
||||
// return;
|
||||
//}
|
||||
|
||||
for (EventList::iterator it = eventList.begin(); it != eventList.end(); ++it)
|
||||
RemoveEvent(it->second);
|
||||
|
||||
eventList.clear();
|
||||
eventMap.clear();
|
||||
}
|
||||
|
||||
void ALEEventProcessor::SetState(int eventId, LuaEventState state)
|
||||
{
|
||||
if (eventMap.find(eventId) != eventMap.end())
|
||||
eventMap[eventId]->SetState(state);
|
||||
if (state == LUAEVENT_STATE_ERASE)
|
||||
eventMap.erase(eventId);
|
||||
}
|
||||
|
||||
void ALEEventProcessor::AddEvent(LuaEvent* luaEvent)
|
||||
{
|
||||
luaEvent->GenerateDelay();
|
||||
eventList.insert(std::pair<uint64, LuaEvent*>(m_time + luaEvent->delay, luaEvent));
|
||||
eventMap[luaEvent->funcRef] = luaEvent;
|
||||
}
|
||||
|
||||
void ALEEventProcessor::AddEvent(int funcRef, uint32 min, uint32 max, uint32 repeats)
|
||||
{
|
||||
AddEvent(new LuaEvent(funcRef, min, max, repeats));
|
||||
}
|
||||
|
||||
void ALEEventProcessor::RemoveEvent(LuaEvent* luaEvent)
|
||||
{
|
||||
// Unreference if should and if ALE was not yet uninitialized and if the lua state still exists
|
||||
if (luaEvent->state != LUAEVENT_STATE_ERASE && ALE::IsInitialized() && (*E)->HasLuaState())
|
||||
{
|
||||
// Free lua function ref
|
||||
luaL_unref((*E)->L, LUA_REGISTRYINDEX, luaEvent->funcRef);
|
||||
}
|
||||
delete luaEvent;
|
||||
}
|
||||
|
||||
EventMgr::EventMgr(ALE** _E) : globalProcessor(new ALEEventProcessor(_E, NULL)), E(_E)
|
||||
{
|
||||
}
|
||||
|
||||
EventMgr::~EventMgr()
|
||||
{
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
if (!processors.empty())
|
||||
for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors
|
||||
(*it)->RemoveEvents_internal();
|
||||
globalProcessor->RemoveEvents_internal();
|
||||
}
|
||||
delete globalProcessor;
|
||||
globalProcessor = NULL;
|
||||
}
|
||||
|
||||
void EventMgr::SetStates(LuaEventState state)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
if (!processors.empty())
|
||||
for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors
|
||||
(*it)->SetStates(state);
|
||||
globalProcessor->SetStates(state);
|
||||
}
|
||||
|
||||
void EventMgr::SetState(int eventId, LuaEventState state)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
if (!processors.empty())
|
||||
for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors
|
||||
(*it)->SetState(eventId, state);
|
||||
globalProcessor->SetState(eventId, state);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef _ALE_EVENT_MGR_H
|
||||
#define _ALE_EVENT_MGR_H
|
||||
|
||||
#include "ALEUtility.h"
|
||||
#include "Common.h"
|
||||
#include "Util.h"
|
||||
#include <map>
|
||||
|
||||
#include "Define.h"
|
||||
|
||||
class ALE;
|
||||
class EventMgr;
|
||||
class ALEEventProcessor;
|
||||
class WorldObject;
|
||||
|
||||
enum LuaEventState
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
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 ALEEventProcessor
|
||||
{
|
||||
friend class EventMgr;
|
||||
|
||||
public:
|
||||
typedef std::multimap<uint64, LuaEvent*> EventList;
|
||||
typedef std::unordered_map<int, LuaEvent*> EventMap;
|
||||
|
||||
ALEEventProcessor(ALE** _E, WorldObject* _obj);
|
||||
~ALEEventProcessor();
|
||||
|
||||
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);
|
||||
EventMap eventMap;
|
||||
|
||||
private:
|
||||
void RemoveEvents_internal();
|
||||
void AddEvent(LuaEvent* luaEvent);
|
||||
void RemoveEvent(LuaEvent* luaEvent);
|
||||
EventList eventList;
|
||||
uint64 m_time;
|
||||
WorldObject* obj;
|
||||
ALE** E;
|
||||
};
|
||||
|
||||
class EventMgr : public ALEUtil::Lockable
|
||||
{
|
||||
public:
|
||||
typedef std::unordered_set<ALEEventProcessor*> ProcessorSet;
|
||||
ProcessorSet processors;
|
||||
ALEEventProcessor* globalProcessor;
|
||||
ALE** E;
|
||||
|
||||
EventMgr(ALE** _E);
|
||||
~EventMgr();
|
||||
|
||||
// Set the state of all timed events
|
||||
// Execute only in safe env
|
||||
void SetStates(LuaEventState state);
|
||||
|
||||
// Sets the eventId's state in all processors
|
||||
// Execute only in safe env
|
||||
void SetState(int eventId, LuaEventState state);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "ALEFileWatcher.h"
|
||||
#include "LuaEngine.h"
|
||||
#include "ALEUtility.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
ALEFileWatcher::ALEFileWatcher() : running(false), checkInterval(1)
|
||||
{
|
||||
}
|
||||
|
||||
ALEFileWatcher::~ALEFileWatcher()
|
||||
{
|
||||
StopWatching();
|
||||
}
|
||||
|
||||
void ALEFileWatcher::StartWatching(const std::string& scriptPath, uint32 intervalSeconds)
|
||||
{
|
||||
if (running.load())
|
||||
{
|
||||
ALE_LOG_DEBUG("[ALEFileWatcher]: Already watching files");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scriptPath.empty())
|
||||
{
|
||||
ALE_LOG_ERROR("[ALEFileWatcher]: Cannot start watching - script path is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
watchPath = scriptPath;
|
||||
checkInterval = intervalSeconds;
|
||||
running.store(true);
|
||||
|
||||
ScanDirectory(watchPath);
|
||||
|
||||
watcherThread = std::thread(&ALEFileWatcher::WatchLoop, this);
|
||||
|
||||
ALE_LOG_INFO("[ALEFileWatcher]: Started watching '{}' (interval: {}s)", watchPath, checkInterval);
|
||||
}
|
||||
|
||||
void ALEFileWatcher::StopWatching()
|
||||
{
|
||||
if (!running.load())
|
||||
return;
|
||||
|
||||
running.store(false);
|
||||
|
||||
if (watcherThread.joinable())
|
||||
watcherThread.join();
|
||||
|
||||
fileTimestamps.clear();
|
||||
|
||||
ALE_LOG_INFO("[ALEFileWatcher]: Stopped watching files");
|
||||
}
|
||||
|
||||
void ALEFileWatcher::WatchLoop()
|
||||
{
|
||||
while (running.load())
|
||||
{
|
||||
try
|
||||
{
|
||||
CheckForChanges();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALEFileWatcher]: Error during file watching: {}", e.what());
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(checkInterval));
|
||||
}
|
||||
}
|
||||
|
||||
bool ALEFileWatcher::IsWatchedFileType(const std::string& filename) {
|
||||
return (filename.length() >= 4 && filename.substr(filename.length() - 4) == ".lua") ||
|
||||
(filename.length() >= 4 && filename.substr(filename.length() - 4) == ".ext") ||
|
||||
(filename.length() >= 5 && filename.substr(filename.length() - 5) == ".moon");
|
||||
}
|
||||
|
||||
void ALEFileWatcher::ScanDirectory(const std::string& path)
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::filesystem::path dir(path);
|
||||
|
||||
if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir))
|
||||
return;
|
||||
|
||||
boost::filesystem::directory_iterator end_iter;
|
||||
|
||||
for (boost::filesystem::directory_iterator dir_iter(dir); dir_iter != end_iter; ++dir_iter)
|
||||
{
|
||||
std::string fullpath = dir_iter->path().generic_string();
|
||||
|
||||
if (boost::filesystem::is_directory(dir_iter->status()))
|
||||
{
|
||||
ScanDirectory(fullpath);
|
||||
}
|
||||
else if (boost::filesystem::is_regular_file(dir_iter->status()))
|
||||
{
|
||||
std::string filename = dir_iter->path().filename().generic_string();
|
||||
|
||||
if (IsWatchedFileType(filename))
|
||||
{
|
||||
fileTimestamps[fullpath] = boost::filesystem::last_write_time(dir_iter->path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALEFileWatcher]: Error scanning directory '{}': {}", path, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ALEFileWatcher::CheckForChanges()
|
||||
{
|
||||
bool hasChanges = false;
|
||||
|
||||
try
|
||||
{
|
||||
boost::filesystem::path dir(watchPath);
|
||||
|
||||
if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir))
|
||||
return;
|
||||
|
||||
boost::filesystem::directory_iterator end_iter;
|
||||
|
||||
for (boost::filesystem::directory_iterator dir_iter(dir); dir_iter != end_iter; ++dir_iter)
|
||||
{
|
||||
if (ShouldReloadFile(dir_iter->path().generic_string()))
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
for (auto it = fileTimestamps.begin(); it != fileTimestamps.end();)
|
||||
{
|
||||
if (!boost::filesystem::exists(it->first))
|
||||
{
|
||||
ALE_LOG_DEBUG("[ALEFileWatcher]: File deleted: {}", it->first);
|
||||
it = fileTimestamps.erase(it);
|
||||
hasChanges = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALEFileWatcher]: Error checking for changes: {}", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasChanges)
|
||||
{
|
||||
ALE_LOG_INFO("[ALEFileWatcher]: Lua script changes detected - triggering reload");
|
||||
ALE::ReloadALE();
|
||||
|
||||
ScanDirectory(watchPath);
|
||||
}
|
||||
}
|
||||
|
||||
bool ALEFileWatcher::ShouldReloadFile(const std::string& filepath)
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::filesystem::path file(filepath);
|
||||
|
||||
if (boost::filesystem::is_directory(file))
|
||||
{
|
||||
boost::filesystem::directory_iterator end_iter;
|
||||
|
||||
for (boost::filesystem::directory_iterator dir_iter(file); dir_iter != end_iter; ++dir_iter)
|
||||
{
|
||||
if (ShouldReloadFile(dir_iter->path().generic_string()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!boost::filesystem::is_regular_file(file))
|
||||
return false;
|
||||
|
||||
std::string filename = file.filename().generic_string();
|
||||
|
||||
if (!IsWatchedFileType(filename)) return false;
|
||||
|
||||
auto currentTime = boost::filesystem::last_write_time(file);
|
||||
auto it = fileTimestamps.find(filepath);
|
||||
|
||||
if (it == fileTimestamps.end())
|
||||
{
|
||||
ALE_LOG_DEBUG("[ALEFileWatcher]: New file detected: {}", filepath);
|
||||
fileTimestamps[filepath] = currentTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (it->second != currentTime)
|
||||
{
|
||||
ALE_LOG_DEBUG("[ALEFileWatcher]: File modified: {}", filepath);
|
||||
it->second = currentTime;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALEFileWatcher]: Error checking file '{}': {}", filepath, e.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef ALE_FILE_WATCHER_H
|
||||
#define ALE_FILE_WATCHER_H
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Common.h"
|
||||
|
||||
class ALEFileWatcher
|
||||
{
|
||||
public:
|
||||
ALEFileWatcher();
|
||||
~ALEFileWatcher();
|
||||
|
||||
void StartWatching(const std::string& scriptPath, uint32 intervalSeconds = 1);
|
||||
void StopWatching();
|
||||
bool IsWatching() const { return running.load(); }
|
||||
|
||||
private:
|
||||
void WatchLoop();
|
||||
void ScanDirectory(const std::string& path);
|
||||
void CheckForChanges();
|
||||
bool ShouldReloadFile(const std::string& filepath);
|
||||
bool IsWatchedFileType(const std::string& filename);
|
||||
|
||||
std::thread watcherThread;
|
||||
std::atomic<bool> running;
|
||||
std::string watchPath;
|
||||
uint32 checkInterval;
|
||||
|
||||
std::map<std::string, std::time_t> fileTimestamps;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef _ALE_INCLUDES_H
|
||||
#define _ALE_INCLUDES_H
|
||||
|
||||
// Required
|
||||
#include "AccountMgr.h"
|
||||
#include "AuctionHouseMgr.h"
|
||||
#include "Cell.h"
|
||||
#include "CellImpl.h"
|
||||
#include "Chat.h"
|
||||
#include "Channel.h"
|
||||
#include "DBCStores.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"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "Pet.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Spell.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "Transport.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "MapMgr.h"
|
||||
#include "Config.h"
|
||||
#include "GameEventMgr.h"
|
||||
#include "GitRevision.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "WeatherMgr.h"
|
||||
#include "Battleground.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Bag.h"
|
||||
#include "Vehicle.h"
|
||||
#include "ArenaTeam.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
typedef Opcodes OpcodesList;
|
||||
|
||||
/*
|
||||
* Note: if you add or change a CORE_NAME or CORE_VERSION #define,
|
||||
* please update LuaGlobalFunctions::GetCoreName or LuaGlobalFunctions::GetCoreVersion documentation example string.
|
||||
*/
|
||||
#define CORE_NAME "AzerothCore"
|
||||
|
||||
#define CORE_VERSION (GitRevision::GetFullVersion())
|
||||
#define eWorldSessionMgr (sWorldSessionMgr)
|
||||
#define eWorld (sWorld)
|
||||
#define eMapMgr (sMapMgr)
|
||||
#define eConfigMgr (sConfigMgr)
|
||||
#define eGuildMgr (sGuildMgr)
|
||||
#define eObjectMgr (sObjectMgr)
|
||||
#define eAccountMgr (sAccountMgr)
|
||||
#define eAuctionMgr (sAuctionMgr)
|
||||
#define eGameEventMgr (sGameEventMgr)
|
||||
#define eObjectAccessor() ObjectAccessor::
|
||||
|
||||
#endif // _ALE_INCLUDES_H
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "ALEInstanceAI.h"
|
||||
#include "ALEUtility.h"
|
||||
#include "lmarshal.h"
|
||||
|
||||
|
||||
void ALEInstanceAI::Initialize()
|
||||
{
|
||||
LOCK_ALE;
|
||||
|
||||
ASSERT(!sALE->HasInstanceData(instance));
|
||||
|
||||
// Create a new table for instance data.
|
||||
lua_State* L = sALE->L;
|
||||
lua_newtable(L);
|
||||
sALE->CreateInstanceData(instance);
|
||||
|
||||
sALE->OnInitialize(this);
|
||||
}
|
||||
|
||||
void ALEInstanceAI::Load(const char* data)
|
||||
{
|
||||
LOCK_ALE;
|
||||
|
||||
// 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(!sALE->HasInstanceData(instance));
|
||||
|
||||
// Create a new table for instance data.
|
||||
lua_State* L = sALE->L;
|
||||
lua_newtable(L);
|
||||
sALE->CreateInstanceData(instance);
|
||||
|
||||
sALE->OnLoad(this);
|
||||
// Stack: (empty)
|
||||
return;
|
||||
}
|
||||
|
||||
size_t decodedLength;
|
||||
const unsigned char* decodedData = ALEUtil::DecodeData(data, &decodedLength);
|
||||
lua_State* L = sALE->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))
|
||||
{
|
||||
sALE->CreateInstanceData(instance);
|
||||
// Stack: (empty)
|
||||
sALE->OnLoad(this);
|
||||
// WARNING! lastSaveData might be different after `OnLoad` if the Lua code saved data.
|
||||
}
|
||||
else
|
||||
{
|
||||
ALE_LOG_ERROR("Error while loading instance data: Expected data to be a table (type 5), got type {} instead", lua_type(L, -1));
|
||||
lua_pop(L, 1);
|
||||
// Stack: (empty)
|
||||
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stack: error_message
|
||||
ALE_LOG_ERROR("Error while parsing instance data with lua-marshal: {}", lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
// Stack: (empty)
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
delete[] decodedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALE_LOG_ERROR("Error while decoding instance data: Data is not valid base-64");
|
||||
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
const char* ALEInstanceAI::Save() const
|
||||
{
|
||||
LOCK_ALE;
|
||||
lua_State* L = sALE->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.
|
||||
*/
|
||||
ALEInstanceAI* self = const_cast<ALEInstanceAI*>(this);
|
||||
|
||||
lua_pushcfunction(L, mar_encode);
|
||||
sALE->PushInstanceData(L, self, false);
|
||||
// Stack: mar_encode, instance_data
|
||||
|
||||
if (lua_pcall(L, 1, 1, 0) != 0)
|
||||
{
|
||||
// Stack: error_message
|
||||
ALE_LOG_ERROR("Error while saving: {}", 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);
|
||||
ALEUtil::EncodeData(data, dataLength, self->lastSaveData);
|
||||
|
||||
lua_pop(L, 1);
|
||||
// Stack: (empty)
|
||||
|
||||
return lastSaveData.c_str();
|
||||
}
|
||||
|
||||
uint32 ALEInstanceAI::GetData(uint32 key) const
|
||||
{
|
||||
LOCK_ALE;
|
||||
lua_State* L = sALE->L;
|
||||
// Stack: (empty)
|
||||
|
||||
sALE->PushInstanceData(L, const_cast<ALEInstanceAI*>(this), false);
|
||||
// Stack: instance_data
|
||||
|
||||
ALE::Push(L, key);
|
||||
// Stack: instance_data, key
|
||||
|
||||
lua_gettable(L, -2);
|
||||
// Stack: instance_data, value
|
||||
|
||||
uint32 value = ALE::CHECKVAL<uint32>(L, -1, 0);
|
||||
lua_pop(L, 2);
|
||||
// Stack: (empty)
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ALEInstanceAI::SetData(uint32 key, uint32 value)
|
||||
{
|
||||
LOCK_ALE;
|
||||
lua_State* L = sALE->L;
|
||||
// Stack: (empty)
|
||||
|
||||
sALE->PushInstanceData(L, this, false);
|
||||
// Stack: instance_data
|
||||
|
||||
ALE::Push(L, key);
|
||||
ALE::Push(L, value);
|
||||
// Stack: instance_data, key, value
|
||||
|
||||
lua_settable(L, -3);
|
||||
// Stack: instance_data
|
||||
|
||||
lua_pop(L, 1);
|
||||
// Stack: (empty)
|
||||
}
|
||||
|
||||
uint64 ALEInstanceAI::GetData64(uint32 key) const
|
||||
{
|
||||
LOCK_ALE;
|
||||
lua_State* L = sALE->L;
|
||||
// Stack: (empty)
|
||||
|
||||
sALE->PushInstanceData(L, const_cast<ALEInstanceAI*>(this), false);
|
||||
// Stack: instance_data
|
||||
|
||||
ALE::Push(L, key);
|
||||
// Stack: instance_data, key
|
||||
|
||||
lua_gettable(L, -2);
|
||||
// Stack: instance_data, value
|
||||
|
||||
uint64 value = ALE::CHECKVAL<uint64>(L, -1, 0);
|
||||
lua_pop(L, 2);
|
||||
// Stack: (empty)
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ALEInstanceAI::SetData64(uint32 key, uint64 value)
|
||||
{
|
||||
LOCK_ALE;
|
||||
lua_State* L = sALE->L;
|
||||
// Stack: (empty)
|
||||
|
||||
sALE->PushInstanceData(L, this, false);
|
||||
// Stack: instance_data
|
||||
|
||||
ALE::Push(L, key);
|
||||
ALE::Push(L, value);
|
||||
// Stack: instance_data, key, value
|
||||
|
||||
lua_settable(L, -3);
|
||||
// Stack: instance_data
|
||||
|
||||
lua_pop(L, 1);
|
||||
// Stack: (empty)
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef _ALE_INSTANCE_DATA_H
|
||||
#define _ALE_INSTANCE_DATA_H
|
||||
|
||||
#include "LuaEngine.h"
|
||||
#include "InstanceScript.h"
|
||||
|
||||
/*
|
||||
* This class is a small wrapper around `InstanceData`,
|
||||
* allowing instances to be scripted with ALE.
|
||||
*
|
||||
*
|
||||
* Note 1
|
||||
* ======
|
||||
*
|
||||
* Instances of `ALEInstanceAI` are owned by the core, so they
|
||||
* are not deleted when ALE is reloaded. Thus `Load` is only called
|
||||
* by the core once, no matter how many times ALE is reloaded.
|
||||
*
|
||||
* However, when ALE reloads, all instance data in ALE 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 ALE.
|
||||
*
|
||||
* 2. When retrieving instance data from ALE, check if it's missing.
|
||||
*
|
||||
* The data will be missing if ALE 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 ALE, 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 ALEInstanceAI : public InstanceData
|
||||
{
|
||||
private:
|
||||
// The last save data to pass through this class,
|
||||
// either through `Load` or `Save`.
|
||||
std::string lastSaveData;
|
||||
|
||||
public:
|
||||
ALEInstanceAI(Map* map) : InstanceData(map)
|
||||
{
|
||||
}
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
/*
|
||||
* These are responsible for serializing/deserializing the instance's
|
||||
* data table to/from the core.
|
||||
*/
|
||||
void Load(const char* data) override;
|
||||
// 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;
|
||||
|
||||
|
||||
/*
|
||||
* Calls `Load` with the last save data that was passed to
|
||||
* or from ALE.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
uint32 GetData(uint32 key) const override;
|
||||
void SetData(uint32 key, uint32 value) override;
|
||||
|
||||
uint64 GetData64(uint32 key) const override;
|
||||
void SetData64(uint32 key, uint64 value) override;
|
||||
|
||||
/*
|
||||
* These methods are just thin wrappers around ALE.
|
||||
*/
|
||||
void Update(uint32 diff) override
|
||||
{
|
||||
// If ALE 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 (!sALE->HasInstanceData(instance))
|
||||
Reload();
|
||||
|
||||
sALE->OnUpdateInstance(this, diff);
|
||||
}
|
||||
|
||||
bool IsEncounterInProgress() const override
|
||||
{
|
||||
return sALE->OnCheckEncounterInProgress(const_cast<ALEInstanceAI*>(this));
|
||||
}
|
||||
|
||||
void OnPlayerEnter(Player* player) override
|
||||
{
|
||||
sALE->OnPlayerEnterInstance(this, player);
|
||||
}
|
||||
|
||||
void OnGameObjectCreate(GameObject* gameobject) override
|
||||
{
|
||||
sALE->OnGameObjectCreate(this, gameobject);
|
||||
}
|
||||
|
||||
void OnCreatureCreate(Creature* creature) override
|
||||
{
|
||||
sALE->OnCreatureCreate(this, creature);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _ALE_INSTANCE_DATA_H
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef _ALE_TEMPLATE_H
|
||||
#define _ALE_TEMPLATE_H
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
};
|
||||
#include "LuaEngine.h"
|
||||
#include "ALECompat.h"
|
||||
#include "ALEUtility.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
class ALEGlobal
|
||||
{
|
||||
public:
|
||||
static int thunk(lua_State* L)
|
||||
{
|
||||
luaL_Reg* l = static_cast<luaL_Reg*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||
int top = lua_gettop(L);
|
||||
int expected = l->func(L);
|
||||
int args = lua_gettop(L) - top;
|
||||
if (args < 0 || args > expected)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALE]: {} returned unexpected amount of arguments {} out of {}. Report to devs", l->name, args, expected);
|
||||
ASSERT(false);
|
||||
}
|
||||
lua_settop(L, top + expected);
|
||||
return expected;
|
||||
}
|
||||
|
||||
static void SetMethods(ALE* E, luaL_Reg* methodTable)
|
||||
{
|
||||
ASSERT(E);
|
||||
ASSERT(methodTable);
|
||||
|
||||
lua_pushglobaltable(E->L);
|
||||
|
||||
for (; methodTable && methodTable->name && methodTable->func; ++methodTable)
|
||||
{
|
||||
lua_pushstring(E->L, methodTable->name);
|
||||
lua_pushlightuserdata(E->L, (void*)methodTable);
|
||||
lua_pushcclosure(E->L, thunk, 1);
|
||||
lua_rawset(E->L, -3);
|
||||
}
|
||||
|
||||
lua_remove(E->L, -1);
|
||||
}
|
||||
};
|
||||
|
||||
class ALEObject
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
ALEObject(T * obj, bool manageMemory);
|
||||
|
||||
~ALEObject()
|
||||
{
|
||||
}
|
||||
|
||||
// Get wrapped object pointer
|
||||
void* GetObj() const { return object; }
|
||||
// Returns whether the object is valid or not
|
||||
bool IsValid() const { return !callstackid || callstackid == sALE->GetCallstackId(); }
|
||||
// Returns whether the object can be invalidated or not
|
||||
bool CanInvalidate() const { return _invalidate; }
|
||||
// Returns pointer to the wrapped object's type name
|
||||
const char* GetTypeName() const { return type_name; }
|
||||
|
||||
// Sets the object pointer that is wrapped
|
||||
void SetObj(void* obj)
|
||||
{
|
||||
ASSERT(obj);
|
||||
object = obj;
|
||||
SetValid(true);
|
||||
}
|
||||
// Sets the object pointer to valid or invalid
|
||||
void SetValid(bool valid)
|
||||
{
|
||||
ASSERT(!valid || (valid && object));
|
||||
if (valid)
|
||||
if (CanInvalidate())
|
||||
callstackid = sALE->GetCallstackId();
|
||||
else
|
||||
callstackid = 0;
|
||||
else
|
||||
callstackid = 1;
|
||||
}
|
||||
// Sets whether the pointer will be invalidated at end of calls
|
||||
void SetValidation(bool invalidate)
|
||||
{
|
||||
_invalidate = invalidate;
|
||||
}
|
||||
// Invalidates the pointer if it should be invalidated
|
||||
void Invalidate()
|
||||
{
|
||||
if (CanInvalidate())
|
||||
callstackid = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64 callstackid;
|
||||
bool _invalidate;
|
||||
void* object;
|
||||
const char* type_name;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ALERegister
|
||||
{
|
||||
const char* name;
|
||||
int(*mfunc)(lua_State*, T*);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ALETemplate
|
||||
{
|
||||
public:
|
||||
static const char* tname;
|
||||
static bool manageMemory;
|
||||
|
||||
// name will be used as type name
|
||||
// If gc is true, lua will handle the memory management for object pushed
|
||||
// gc should be used if pushing for example WorldPacket,
|
||||
// that will only be needed on lua side and will not be managed by TC/mangos/<core>
|
||||
static void Register(ALE* E, const char* name, bool gc = false)
|
||||
{
|
||||
ASSERT(E);
|
||||
ASSERT(name);
|
||||
|
||||
// check that metatable isn't already there
|
||||
lua_getglobal(E->L, name);
|
||||
ASSERT(lua_isnoneornil(E->L, -1));
|
||||
|
||||
// pop nil
|
||||
lua_pop(E->L, 1);
|
||||
|
||||
tname = name;
|
||||
manageMemory = gc;
|
||||
|
||||
// create metatable for userdata of this type
|
||||
luaL_newmetatable(E->L, tname);
|
||||
int metatable = lua_gettop(E->L);
|
||||
|
||||
// push methodtable to stack to be accessed and modified by users
|
||||
lua_pushvalue(E->L, metatable);
|
||||
lua_setglobal(E->L, tname);
|
||||
|
||||
// tostring
|
||||
lua_pushcfunction(E->L, ToString);
|
||||
lua_setfield(E->L, metatable, "__tostring");
|
||||
|
||||
// garbage collecting
|
||||
lua_pushcfunction(E->L, CollectGarbage);
|
||||
lua_setfield(E->L, metatable, "__gc");
|
||||
|
||||
// make methods accessible through metatable
|
||||
lua_pushvalue(E->L, metatable);
|
||||
lua_setfield(E->L, metatable, "__index");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Add);
|
||||
lua_setfield(E->L, metatable, "__add");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Substract);
|
||||
lua_setfield(E->L, metatable, "__sub");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Multiply);
|
||||
lua_setfield(E->L, metatable, "__mul");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Divide);
|
||||
lua_setfield(E->L, metatable, "__div");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Mod);
|
||||
lua_setfield(E->L, metatable, "__mod");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Pow);
|
||||
lua_setfield(E->L, metatable, "__pow");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, UnaryMinus);
|
||||
lua_setfield(E->L, metatable, "__unm");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Concat);
|
||||
lua_setfield(E->L, metatable, "__concat");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Length);
|
||||
lua_setfield(E->L, metatable, "__len");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Equal);
|
||||
lua_setfield(E->L, metatable, "__eq");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Less);
|
||||
lua_setfield(E->L, metatable, "__lt");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, LessOrEqual);
|
||||
lua_setfield(E->L, metatable, "__le");
|
||||
|
||||
// make new indexes saved to methods
|
||||
lua_pushcfunction(E->L, Call);
|
||||
lua_setfield(E->L, metatable, "__call");
|
||||
|
||||
// special method to get the object type
|
||||
lua_pushcfunction(E->L, GetType);
|
||||
lua_setfield(E->L, metatable, "GetObjectType");
|
||||
|
||||
// special method to decide object invalidation at end of call
|
||||
lua_pushcfunction(E->L, SetInvalidation);
|
||||
lua_setfield(E->L, metatable, "SetInvalidation");
|
||||
|
||||
// pop metatable
|
||||
lua_pop(E->L, 1);
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
static void SetMethods(ALE* E, ALERegister<C>* methodTable)
|
||||
{
|
||||
ASSERT(E);
|
||||
ASSERT(tname);
|
||||
ASSERT(methodTable);
|
||||
|
||||
// get metatable
|
||||
lua_pushstring(E->L, tname);
|
||||
lua_rawget(E->L, LUA_REGISTRYINDEX);
|
||||
ASSERT(lua_istable(E->L, -1));
|
||||
|
||||
for (; methodTable && methodTable->name && methodTable->mfunc; ++methodTable)
|
||||
{
|
||||
lua_pushstring(E->L, methodTable->name);
|
||||
lua_pushlightuserdata(E->L, (void*)methodTable);
|
||||
lua_pushcclosure(E->L, CallMethod, 1);
|
||||
lua_rawset(E->L, -3);
|
||||
}
|
||||
|
||||
lua_pop(E->L, 1);
|
||||
}
|
||||
|
||||
static int Push(lua_State* L, T const* obj)
|
||||
{
|
||||
if (!obj)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create new userdata
|
||||
ALEObject** ptrHold = static_cast<ALEObject**>(lua_newuserdata(L, sizeof(ALEObject*)));
|
||||
if (!ptrHold)
|
||||
{
|
||||
ALE_LOG_ERROR("{} could not create new userdata", tname);
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
*ptrHold = new ALEObject(const_cast<T*>(obj), manageMemory);
|
||||
|
||||
// Set metatable for it
|
||||
lua_pushstring(L, tname);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
if (!lua_istable(L, -1))
|
||||
{
|
||||
ALE_LOG_ERROR("{} missing metatable", tname);
|
||||
lua_pop(L, 2);
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static T* Check(lua_State* L, int narg, bool error = true)
|
||||
{
|
||||
ALEObject* ALEObj = ALE::CHECKTYPE(L, narg, tname, error);
|
||||
if (!ALEObj)
|
||||
return NULL;
|
||||
|
||||
if (!ALEObj->IsValid())
|
||||
{
|
||||
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
|
||||
{
|
||||
ALE_LOG_ERROR("{}", buff);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return static_cast<T*>(ALEObj->GetObj());
|
||||
}
|
||||
|
||||
static int GetType(lua_State* L)
|
||||
{
|
||||
lua_pushstring(L, tname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int SetInvalidation(lua_State* L)
|
||||
{
|
||||
ALEObject* ALEObj = ALE::CHECKOBJ<ALEObject>(L, 1);
|
||||
bool invalidate = ALE::CHECKVAL<bool>(L, 2);
|
||||
|
||||
ALEObj->SetValidation(invalidate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CallMethod(lua_State* L)
|
||||
{
|
||||
T* obj = ALE::CHECKOBJ<T>(L, 1); // get self
|
||||
if (!obj)
|
||||
return 0;
|
||||
ALERegister<T>* l = static_cast<ALERegister<T>*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||
int top = lua_gettop(L);
|
||||
int expected = l->mfunc(L, obj);
|
||||
int args = lua_gettop(L) - top;
|
||||
if (args < 0 || args > expected)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALE]: {} returned unexpected amount of arguments {} out of {}. Report to devs", l->name, args, expected);
|
||||
ASSERT(false);
|
||||
}
|
||||
lua_settop(L, top + expected);
|
||||
return expected;
|
||||
}
|
||||
|
||||
// Metamethods ("virtual")
|
||||
|
||||
// Remember special cases like ALETemplate<Vehicle>::CollectGarbage
|
||||
static int CollectGarbage(lua_State* L)
|
||||
{
|
||||
// Get object pointer (and check type, no error)
|
||||
ALEObject* obj = ALE::CHECKOBJ<ALEObject>(L, 1, false);
|
||||
if (obj && manageMemory)
|
||||
delete static_cast<T*>(obj->GetObj());
|
||||
delete obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ToString(lua_State* L)
|
||||
{
|
||||
T* obj = ALE::CHECKOBJ<T>(L, 1, true); // get self
|
||||
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 Substract(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) { ALE::Push(L, ALE::CHECKOBJ<T>(L, 1) == ALE::CHECKOBJ<T>(L, 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); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
ALEObject::ALEObject(T * obj, bool manageMemory) : callstackid(1), _invalidate(!manageMemory), object(obj), type_name(ALETemplate<T>::tname)
|
||||
{
|
||||
SetValid(true);
|
||||
}
|
||||
|
||||
template<typename T> const char* ALETemplate<T>::tname = NULL;
|
||||
template<typename T> bool ALETemplate<T>::manageMemory = false;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "ALEUtility.h"
|
||||
#include "World.h"
|
||||
#include "Object.h"
|
||||
#include "Unit.h"
|
||||
#include "GameObject.h"
|
||||
#include "DBCStores.h"
|
||||
|
||||
uint32 ALEUtil::GetCurrTime()
|
||||
{
|
||||
return getMSTime();
|
||||
}
|
||||
|
||||
uint32 ALEUtil::GetTimeDiff(uint32 oldMSTime)
|
||||
{
|
||||
return GetMSTimeDiffToNow(oldMSTime);
|
||||
}
|
||||
|
||||
ALEUtil::ObjectGUIDCheck::ObjectGUIDCheck(ObjectGuid guid) : _guid(guid)
|
||||
{
|
||||
}
|
||||
|
||||
bool ALEUtil::ObjectGUIDCheck::operator()(WorldObject* object)
|
||||
{
|
||||
return object->GET_GUID() == _guid;
|
||||
}
|
||||
|
||||
ALEUtil::ObjectDistanceOrderPred::ObjectDistanceOrderPred(WorldObject const* pRefObj, bool ascending) : m_refObj(pRefObj), m_ascending(ascending)
|
||||
{
|
||||
}
|
||||
bool ALEUtil::ObjectDistanceOrderPred::operator()(WorldObject const* pLeft, WorldObject const* pRight) const
|
||||
{
|
||||
return m_ascending ? m_refObj->GetDistanceOrder(pLeft, pRight) : !m_refObj->GetDistanceOrder(pLeft, pRight);
|
||||
}
|
||||
|
||||
ALEUtil::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)
|
||||
i_obj_fact = sFactionTemplateStore.LookupEntry(14);
|
||||
}
|
||||
WorldObject const& ALEUtil::WorldObjectInRangeCheck::GetFocusObject() const
|
||||
{
|
||||
return *i_obj;
|
||||
}
|
||||
bool ALEUtil::WorldObjectInRangeCheck::operator()(WorldObject* u)
|
||||
{
|
||||
if (i_typeMask && !u->isType(TypeMask(i_typeMask)))
|
||||
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 ((i_obj_fact->IsHostileTo(*target->GetFactionTemplateEntry())) != (i_hostile == 1))
|
||||
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 ALEUtil::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* ALEUtil::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;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef _ALE_UTIL_H
|
||||
#define _ALE_UTIL_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include "Common.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Database/QueryResult.h"
|
||||
#include "Log.h"
|
||||
|
||||
typedef QueryResult ALEQuery;
|
||||
#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
|
||||
|
||||
#define ALE_LOG_INFO(...) LOG_INFO("ALE", __VA_ARGS__);
|
||||
#define ALE_LOG_ERROR(...) LOG_ERROR("ALE", __VA_ARGS__);
|
||||
#define ALE_LOG_DEBUG(...) LOG_DEBUG("ALE", __VA_ARGS__);
|
||||
|
||||
#ifndef MAKE_NEW_GUID
|
||||
#define MAKE_NEW_GUID(l, e, h) ObjectGuid(h, e, l)
|
||||
#endif
|
||||
#ifndef GUID_ENPART
|
||||
#define GUID_ENPART(guid) ObjectGuid(guid).GetEntry()
|
||||
#endif
|
||||
#ifndef GUID_LOPART
|
||||
#define GUID_LOPART(guid) ObjectGuid(guid).GetCounter()
|
||||
#endif
|
||||
#ifndef GUID_HIPART
|
||||
#define GUID_HIPART(guid) ObjectGuid(guid).GetHigh()
|
||||
#endif
|
||||
|
||||
class Unit;
|
||||
class WorldObject;
|
||||
struct FactionTemplateEntry;
|
||||
|
||||
namespace ALEUtil
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* Inherit this class, then when needing lock, use
|
||||
* Guard guard(GetLock());
|
||||
*
|
||||
* The lock is automatically released at end of scope
|
||||
*/
|
||||
class Lockable
|
||||
{
|
||||
public:
|
||||
typedef std::mutex LockType;
|
||||
typedef std::lock_guard<LockType> Guard;
|
||||
|
||||
LockType& GetLock() { return _lock; }
|
||||
|
||||
private:
|
||||
LockType _lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <memory>
|
||||
#include "Common.h"
|
||||
#include "ALEUtility.h"
|
||||
#include <type_traits>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A set of bindings from keys of type `K` to Lua references.
|
||||
*/
|
||||
template<typename K>
|
||||
class BindingMap : public ALEUtil::Lockable
|
||||
{
|
||||
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<Binding> > BindingList;
|
||||
|
||||
std::unordered_map<K, BindingList> 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<uint64, BindingList*> id_lookup_table;
|
||||
|
||||
public:
|
||||
BindingMap(lua_State* L) :
|
||||
L(L),
|
||||
maxBindingID(0)
|
||||
{ }
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
|
||||
uint64 id = (++maxBindingID);
|
||||
BindingList& list = bindings[key];
|
||||
list.push_back(std::unique_ptr<Binding>(new Binding(L, id, ref, shots)));
|
||||
id_lookup_table[id] = &list;
|
||||
return id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all bindings for `key`.
|
||||
*/
|
||||
void Clear(const K& key)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
|
||||
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>& binding = *i;
|
||||
id_lookup_table.erase(binding->id);
|
||||
}
|
||||
|
||||
bindings.erase(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all bindings for all keys.
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
|
||||
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)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
|
||||
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>& 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)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
|
||||
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)
|
||||
{
|
||||
Guard guard(GetLock());
|
||||
|
||||
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>& 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 <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T1, typename T2, typename... T>
|
||||
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 <typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
|
||||
static inline result_type hash(T const & t)
|
||||
{
|
||||
return std::hash<typename std::underlying_type<T>::type>()(t);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
|
||||
static inline result_type hash(T const & t)
|
||||
{
|
||||
return std::hash<T>()(t);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
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 <typename H, typename T1, typename... T>
|
||||
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<typename T>
|
||||
struct equal_to < EventKey<T> >
|
||||
{
|
||||
bool operator()(EventKey<T> const& lhs, EventKey<T> const& rhs) const
|
||||
{
|
||||
return lhs.event_id == rhs.event_id;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct equal_to < EntryKey<T> >
|
||||
{
|
||||
bool operator()(EntryKey<T> const& lhs, EntryKey<T> const& rhs) const
|
||||
{
|
||||
return lhs.event_id == rhs.event_id
|
||||
&& lhs.entry == rhs.entry;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct equal_to < UniqueObjectKey<T> >
|
||||
{
|
||||
bool operator()(UniqueObjectKey<T> const& lhs, UniqueObjectKey<T> const& rhs) const
|
||||
{
|
||||
return lhs.event_id == rhs.event_id
|
||||
&& lhs.guid == rhs.guid
|
||||
&& lhs.instance_id == rhs.instance_id;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash < EventKey<T> >
|
||||
{
|
||||
typedef EventKey<T> argument_type;
|
||||
|
||||
hash_helper::result_type operator()(argument_type const& k) const
|
||||
{
|
||||
return hash_helper::hash(k.event_id);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash < EntryKey<T> >
|
||||
{
|
||||
typedef EntryKey<T> argument_type;
|
||||
|
||||
hash_helper::result_type operator()(argument_type const& k) const
|
||||
{
|
||||
return hash_helper::hash(k.event_id, k.entry);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash < UniqueObjectKey<T> >
|
||||
{
|
||||
typedef UniqueObjectKey<T> argument_type;
|
||||
|
||||
hash_helper::result_type operator()(argument_type const& k) const
|
||||
{
|
||||
return hash_helper::hash(k.event_id, k.instance_id, k.guid.GetRawValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // _BINDING_MAP_H
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEUtility.h"
|
||||
|
||||
/*
|
||||
* Sets up the stack so that event handlers can be called.
|
||||
*
|
||||
* Returns the number of functions that were pushed onto the stack.
|
||||
*/
|
||||
template<typename K1, typename K2>
|
||||
int ALE::SetupStack(BindingMap<K1>* bindings1, BindingMap<K2>* 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]
|
||||
|
||||
Push(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<typename T>
|
||||
void ALE::ReplaceArgument(T value, uint8 index)
|
||||
{
|
||||
ASSERT(index < lua_gettop(L) && index > 0);
|
||||
// Stack: event_id, [arguments], [functions], [results]
|
||||
|
||||
ALE::Push(L, value);
|
||||
// Stack: event_id, [arguments], [functions], [results], value
|
||||
|
||||
lua_replace(L, index + 1);
|
||||
// Stack: event_id, [arguments and value], [functions], [results]
|
||||
}
|
||||
|
||||
/*
|
||||
* Call all event handlers registered to the event ID/entry combination and ignore any results.
|
||||
*/
|
||||
template<typename K1, typename K2>
|
||||
void ALE::CallAllFunctions(BindingMap<K1>* bindings1, BindingMap<K2>* 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<typename K1, typename K2>
|
||||
bool ALE::CallAllFunctionsBool(BindingMap<K1>* bindings1, BindingMap<K2>* 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;
|
||||
}
|
||||
|
||||
#endif // _HOOK_HELPERS_H
|
||||
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
|
||||
/*
|
||||
* A hook should be written in one of the following forms:
|
||||
*
|
||||
* A. If results will be IGNORED:
|
||||
*
|
||||
* // Return early if there are no bindings.
|
||||
* if (!WhateverBindings->HasBindingsFor(SOME_EVENT_TYPE))
|
||||
* return;
|
||||
*
|
||||
* // Lock out any other threads.
|
||||
* LOCK_ALE;
|
||||
*
|
||||
* // Push extra arguments, if any.
|
||||
* Push(a);
|
||||
* Push(b);
|
||||
* Push(c);
|
||||
*
|
||||
* // Call all event handlers.
|
||||
* CallAllFunctions(WhateverBindings, SOME_EVENT_TYPE);
|
||||
*
|
||||
*
|
||||
* B. If results will be USED:
|
||||
*
|
||||
* // Return early if there are no bindings.
|
||||
* if (!WhateverBindings->HasBindingsFor(SOME_EVENT_TYPE))
|
||||
* return;
|
||||
*
|
||||
* // Lock out any other threads.
|
||||
* LOCK_ALE;
|
||||
*
|
||||
* // Push extra arguments, if any.
|
||||
* Push(a);
|
||||
* Push(b);
|
||||
* Push(c);
|
||||
*
|
||||
* // Setup the stack and get the number of functions pushed.
|
||||
* // Last argument is 3 because we did 3 Pushes.
|
||||
* int n = SetupStack(WhateverBindings, SOME_EVENT_TYPE, 3);
|
||||
*
|
||||
* // Call each event handler in order and check results.
|
||||
* while (n > 0)
|
||||
* {
|
||||
* // Call an event handler and decrement the function counter afterward.
|
||||
* // Second-last argument is 3 because we did 3 Pushes.
|
||||
* // Last argument is 2 because we want 2 results.
|
||||
* int r = CallOneFunction(n--, 3, 2);
|
||||
*
|
||||
* // Results can be popped using `r`.
|
||||
* int first = CHECKVAL<int>(L, r + 0);
|
||||
* int second = CHECKVAL<int>(L, r + 1);
|
||||
*
|
||||
* // Pop the results off the stack.
|
||||
* lua_pop(L, 2);
|
||||
* }
|
||||
*
|
||||
* // Clean-up the stack. Argument is 3 because we did 3 Pushes.
|
||||
* CleanUpStack(3);
|
||||
*/
|
||||
|
||||
namespace Hooks
|
||||
{
|
||||
enum RegisterTypes
|
||||
{
|
||||
REGTYPE_PACKET,
|
||||
REGTYPE_SERVER,
|
||||
REGTYPE_PLAYER,
|
||||
REGTYPE_GUILD,
|
||||
REGTYPE_GROUP,
|
||||
REGTYPE_CREATURE,
|
||||
REGTYPE_VEHICLE,
|
||||
REGTYPE_CREATURE_GOSSIP,
|
||||
REGTYPE_GAMEOBJECT,
|
||||
REGTYPE_GAMEOBJECT_GOSSIP,
|
||||
REGTYPE_ITEM,
|
||||
REGTYPE_ITEM_GOSSIP,
|
||||
REGTYPE_PLAYER_GOSSIP,
|
||||
REGTYPE_BG,
|
||||
REGTYPE_MAP,
|
||||
REGTYPE_INSTANCE,
|
||||
REGTYPE_TICKET,
|
||||
REGTYPE_SPELL,
|
||||
REGTYPE_ALL_CREATURE,
|
||||
REGTYPE_COUNT
|
||||
};
|
||||
|
||||
enum PacketEvents
|
||||
{
|
||||
PACKET_EVENT_ON_PACKET_RECEIVE = 5, // (event, packet, player) - Player only if accessible. Can return false, newPacket
|
||||
PACKET_EVENT_ON_PACKET_RECEIVE_UNKNOWN = 6, // Not Implemented
|
||||
PACKET_EVENT_ON_PACKET_SEND = 7, // (event, packet, player) - Player only if accessible. Can return false, newPacket
|
||||
|
||||
PACKET_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum ServerEvents
|
||||
{
|
||||
// Server
|
||||
SERVER_EVENT_ON_NETWORK_START = 1, // Not Implemented
|
||||
SERVER_EVENT_ON_NETWORK_STOP = 2, // Not Implemented
|
||||
SERVER_EVENT_ON_SOCKET_OPEN = 3, // Not Implemented
|
||||
SERVER_EVENT_ON_SOCKET_CLOSE = 4, // Not Implemented
|
||||
SERVER_EVENT_ON_PACKET_RECEIVE = 5, // (event, packet, player) - Player only if accessible. Can return false, newPacket
|
||||
SERVER_EVENT_ON_PACKET_RECEIVE_UNKNOWN = 6, // Not Implemented
|
||||
SERVER_EVENT_ON_PACKET_SEND = 7, // (event, packet, player) - Player only if accessible. Can return false, newPacket
|
||||
|
||||
// World
|
||||
WORLD_EVENT_ON_OPEN_STATE_CHANGE = 8, // (event, open) - Needs core support on Mangos
|
||||
WORLD_EVENT_ON_CONFIG_LOAD = 9, // (event, reload)
|
||||
// UNUSED = 10,
|
||||
WORLD_EVENT_ON_SHUTDOWN_INIT = 11, // (event, code, mask)
|
||||
WORLD_EVENT_ON_SHUTDOWN_CANCEL = 12, // (event)
|
||||
WORLD_EVENT_ON_UPDATE = 13, // (event, diff)
|
||||
WORLD_EVENT_ON_STARTUP = 14, // (event)
|
||||
WORLD_EVENT_ON_SHUTDOWN = 15, // (event)
|
||||
|
||||
// ALE
|
||||
ALE_EVENT_ON_LUA_STATE_CLOSE = 16, // (event) - triggers just before shutting down ALE (on shutdown and restart)
|
||||
|
||||
// Map
|
||||
MAP_EVENT_ON_CREATE = 17, // (event, map)
|
||||
MAP_EVENT_ON_DESTROY = 18, // (event, map)
|
||||
MAP_EVENT_ON_GRID_LOAD = 19, // Not Implemented
|
||||
MAP_EVENT_ON_GRID_UNLOAD = 20, // Not Implemented
|
||||
MAP_EVENT_ON_PLAYER_ENTER = 21, // (event, map, player)
|
||||
MAP_EVENT_ON_PLAYER_LEAVE = 22, // (event, map, player)
|
||||
MAP_EVENT_ON_UPDATE = 23, // (event, map, diff)
|
||||
|
||||
// Area trigger
|
||||
TRIGGER_EVENT_ON_TRIGGER = 24, // (event, player, triggerId) - Can return true
|
||||
|
||||
// Weather
|
||||
WEATHER_EVENT_ON_CHANGE = 25, // (event, zoneId, state, grade)
|
||||
|
||||
// Auction house
|
||||
AUCTION_EVENT_ON_ADD = 26, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow)
|
||||
AUCTION_EVENT_ON_REMOVE = 27, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow)
|
||||
AUCTION_EVENT_ON_SUCCESSFUL = 28, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow)
|
||||
AUCTION_EVENT_ON_EXPIRE = 29, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow)
|
||||
|
||||
// AddOns
|
||||
ADDON_EVENT_ON_MESSAGE = 30, // (event, sender, type, prefix, msg, target) - target can be nil/whisper_target/guild/group/channel. Can return false
|
||||
|
||||
WORLD_EVENT_ON_DELETE_CREATURE = 31, // (event, creature)
|
||||
WORLD_EVENT_ON_DELETE_GAMEOBJECT = 32, // (event, gameobject)
|
||||
|
||||
// ALE
|
||||
ALE_EVENT_ON_LUA_STATE_OPEN = 33, // (event) - triggers after all scripts are loaded
|
||||
|
||||
GAME_EVENT_START = 34, // (event, gameeventid)
|
||||
GAME_EVENT_STOP = 35, // (event, gameeventid)
|
||||
|
||||
SERVER_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum PlayerEvents
|
||||
{
|
||||
PLAYER_EVENT_ON_CHARACTER_CREATE = 1, // (event, player)
|
||||
PLAYER_EVENT_ON_CHARACTER_DELETE = 2, // (event, guid)
|
||||
PLAYER_EVENT_ON_LOGIN = 3, // (event, player)
|
||||
PLAYER_EVENT_ON_LOGOUT = 4, // (event, player)
|
||||
PLAYER_EVENT_ON_SPELL_CAST = 5, // (event, player, spell, skipCheck)
|
||||
PLAYER_EVENT_ON_KILL_PLAYER = 6, // (event, killer, killed)
|
||||
PLAYER_EVENT_ON_KILL_CREATURE = 7, // (event, killer, killed)
|
||||
PLAYER_EVENT_ON_KILLED_BY_CREATURE = 8, // (event, killer, killed)
|
||||
PLAYER_EVENT_ON_DUEL_REQUEST = 9, // (event, target, challenger)
|
||||
PLAYER_EVENT_ON_DUEL_START = 10, // (event, player1, player2)
|
||||
PLAYER_EVENT_ON_DUEL_END = 11, // (event, winner, loser, type)
|
||||
PLAYER_EVENT_ON_GIVE_XP = 12, // (event, player, amount, victim, source) - Can return new XP amount
|
||||
PLAYER_EVENT_ON_LEVEL_CHANGE = 13, // (event, player, oldLevel)
|
||||
PLAYER_EVENT_ON_MONEY_CHANGE = 14, // (event, player, amount) - Can return new money amount
|
||||
PLAYER_EVENT_ON_REPUTATION_CHANGE = 15, // (event, player, factionId, standing, incremental) - Can return new standing -> if standing == -1, it will prevent default action (rep gain)
|
||||
PLAYER_EVENT_ON_TALENTS_CHANGE = 16, // (event, player, points)
|
||||
PLAYER_EVENT_ON_TALENTS_RESET = 17, // (event, player, noCost)
|
||||
PLAYER_EVENT_ON_CHAT = 18, // (event, player, msg, Type, lang) - Can return false, newMessage
|
||||
PLAYER_EVENT_ON_WHISPER = 19, // (event, player, msg, Type, lang, receiver) - Can return false, newMessage
|
||||
PLAYER_EVENT_ON_GROUP_CHAT = 20, // (event, player, msg, Type, lang, group) - Can return false, newMessage
|
||||
PLAYER_EVENT_ON_GUILD_CHAT = 21, // (event, player, msg, Type, lang, guild) - Can return false, newMessage
|
||||
PLAYER_EVENT_ON_CHANNEL_CHAT = 22, // (event, player, msg, Type, lang, channel) - channel is negative for custom channels. Can return false, newMessage
|
||||
PLAYER_EVENT_ON_EMOTE = 23, // (event, player, emote) - Not triggered on any known emote
|
||||
PLAYER_EVENT_ON_TEXT_EMOTE = 24, // (event, player, textEmote, emoteNum, guid)
|
||||
PLAYER_EVENT_ON_SAVE = 25, // (event, player)
|
||||
PLAYER_EVENT_ON_BIND_TO_INSTANCE = 26, // (event, player, difficulty, mapid, permanent)
|
||||
PLAYER_EVENT_ON_UPDATE_ZONE = 27, // (event, player, newZone, newArea)
|
||||
PLAYER_EVENT_ON_MAP_CHANGE = 28, // (event, player)
|
||||
|
||||
// Custom
|
||||
PLAYER_EVENT_ON_EQUIP = 29, // (event, player, item, bag, slot)
|
||||
PLAYER_EVENT_ON_FIRST_LOGIN = 30, // (event, player)
|
||||
PLAYER_EVENT_ON_CAN_USE_ITEM = 31, // (event, player, itemEntry) - Can return InventoryResult enum value
|
||||
PLAYER_EVENT_ON_LOOT_ITEM = 32, // (event, player, item, count)
|
||||
PLAYER_EVENT_ON_ENTER_COMBAT = 33, // (event, player, enemy)
|
||||
PLAYER_EVENT_ON_LEAVE_COMBAT = 34, // (event, player)
|
||||
PLAYER_EVENT_ON_REPOP = 35, // (event, player)
|
||||
PLAYER_EVENT_ON_RESURRECT = 36, // (event, player)
|
||||
PLAYER_EVENT_ON_LOOT_MONEY = 37, // (event, player, amount)
|
||||
PLAYER_EVENT_ON_QUEST_ABANDON = 38, // (event, player, questId)
|
||||
PLAYER_EVENT_ON_LEARN_TALENTS = 39, // (event, player, talentId, talentRank, spellid)
|
||||
// UNUSED = 40, // (event, player)
|
||||
// UNUSED = 41, // (event, player)
|
||||
PLAYER_EVENT_ON_COMMAND = 42, // (event, player, command, chatHandler) - player is nil if command used from console. Can return false
|
||||
PLAYER_EVENT_ON_PET_ADDED_TO_WORLD = 43, // (event, player, pet)
|
||||
PLAYER_EVENT_ON_LEARN_SPELL = 44, // (event, player, spellId)
|
||||
PLAYER_EVENT_ON_ACHIEVEMENT_COMPLETE = 45, // (event, player, achievement)
|
||||
PLAYER_EVENT_ON_FFAPVP_CHANGE = 46, // (event, player, hasFfaPvp)
|
||||
PLAYER_EVENT_ON_UPDATE_AREA = 47, // (event, player, oldArea, newArea)
|
||||
PLAYER_EVENT_ON_CAN_INIT_TRADE = 48, // (event, player, target) - Can return false to prevent the trade
|
||||
PLAYER_EVENT_ON_CAN_SEND_MAIL = 49, // (event, player, receiverGuid, mailbox, subject, body, money, cod, item) - Can return false to prevent sending the mail
|
||||
PLAYER_EVENT_ON_CAN_JOIN_LFG = 50, // (event, player, roles, dungeons, comment) - Can return false to prevent queueing
|
||||
PLAYER_EVENT_ON_QUEST_REWARD_ITEM = 51, // (event, player, item, count)
|
||||
PLAYER_EVENT_ON_CREATE_ITEM = 52, // (event, player, item, count)
|
||||
PLAYER_EVENT_ON_STORE_NEW_ITEM = 53, // (event, player, item, count)
|
||||
PLAYER_EVENT_ON_COMPLETE_QUEST = 54, // (event, player, quest)
|
||||
PLAYER_EVENT_ON_CAN_GROUP_INVITE = 55, // (event, player, memberName) - Can return false to prevent inviting
|
||||
PLAYER_EVENT_ON_GROUP_ROLL_REWARD_ITEM = 56, // (event, player, item, count, voteType, roll)
|
||||
PLAYER_EVENT_ON_BG_DESERTION = 57, // (event, player, type)
|
||||
PLAYER_EVENT_ON_PET_KILL = 58, // (event, player, killer)
|
||||
PLAYER_EVENT_ON_CAN_RESURRECT = 59, // (event, player)
|
||||
PLAYER_EVENT_ON_CAN_UPDATE_SKILL = 60, // (event, player, skill_id) - Can return true or false
|
||||
PLAYER_EVENT_ON_BEFORE_UPDATE_SKILL = 61, // (event, player, skill_id, value, max, step) -- Can return new amount
|
||||
PLAYER_EVENT_ON_UPDATE_SKILL = 62, // (event, player, skill_id, value, max, step, new_value)
|
||||
PLAYER_EVENT_ON_QUEST_ACCEPT = 63, // (event, player, quest)
|
||||
PLAYER_EVENT_ON_AURA_APPLY = 64, // (event, player, aura)
|
||||
PLAYER_EVENT_ON_HEAL = 65, // (event, player, target, gain) - Can return new heal amount
|
||||
PLAYER_EVENT_ON_DAMAGE = 66, // (event, player, target, damage) - Can return new damage amount
|
||||
PLAYER_EVENT_ON_AURA_REMOVE = 67, // (event, player, aura, remove_mode)
|
||||
PLAYER_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK = 68, // (event, player, target, damage, spellInfo) - Can return new damage amount
|
||||
PLAYER_EVENT_ON_MODIFY_MELEE_DAMAGE = 69, // (event, player, target, damage) - Can return new damage amount
|
||||
PLAYER_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN = 70, // (event, player, target, damage, spellInfo) - Can return new damage amount
|
||||
PLAYER_EVENT_ON_MODIFY_HEAL_RECEIVED = 71, // (event, player, target, heal, spellInfo) - Can return new heal amount
|
||||
PLAYER_EVENT_ON_DEAL_DAMAGE = 72, // (event, player, target, damage, damagetype) - Can return new damage amount
|
||||
PLAYER_EVENT_ON_RELEASED_GHOST = 73, // (event, player)
|
||||
|
||||
PLAYER_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum GuildEvents
|
||||
{
|
||||
// Guild
|
||||
GUILD_EVENT_ON_ADD_MEMBER = 1, // (event, guild, player, rank)
|
||||
GUILD_EVENT_ON_REMOVE_MEMBER = 2, // (event, guild, player, isDisbanding)
|
||||
GUILD_EVENT_ON_MOTD_CHANGE = 3, // (event, guild, newMotd)
|
||||
GUILD_EVENT_ON_INFO_CHANGE = 4, // (event, guild, newInfo)
|
||||
GUILD_EVENT_ON_CREATE = 5, // (event, guild, leader, name) // Not on TC
|
||||
GUILD_EVENT_ON_DISBAND = 6, // (event, guild)
|
||||
GUILD_EVENT_ON_MONEY_WITHDRAW = 7, // (event, guild, player, amount, isRepair) - Can return new money amount
|
||||
GUILD_EVENT_ON_MONEY_DEPOSIT = 8, // (event, guild, player, amount) - Can return new money amount
|
||||
GUILD_EVENT_ON_ITEM_MOVE = 9, // (event, guild, player, item, isSrcBank, srcContainer, srcSlotId, isDestBank, destContainer, destSlotId) // TODO
|
||||
GUILD_EVENT_ON_EVENT = 10, // (event, guild, eventType, plrGUIDLow1, plrGUIDLow2, newRank) // TODO
|
||||
GUILD_EVENT_ON_BANK_EVENT = 11, // (event, guild, eventType, tabId, playerGUIDLow, itemOrMoney, itemStackCount, destTabId)
|
||||
|
||||
GUILD_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum GroupEvents
|
||||
{
|
||||
// Group
|
||||
GROUP_EVENT_ON_MEMBER_ADD = 1, // (event, group, guid)
|
||||
GROUP_EVENT_ON_MEMBER_INVITE = 2, // (event, group, guid)
|
||||
GROUP_EVENT_ON_MEMBER_REMOVE = 3, // (event, group, guid, method, kicker, reason)
|
||||
GROUP_EVENT_ON_LEADER_CHANGE = 4, // (event, group, newLeaderGuid, oldLeaderGuid)
|
||||
GROUP_EVENT_ON_DISBAND = 5, // (event, group)
|
||||
GROUP_EVENT_ON_CREATE = 6, // (event, group, leaderGuid, groupType)
|
||||
|
||||
GROUP_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum VehicleEvents
|
||||
{
|
||||
VEHICLE_EVENT_ON_INSTALL = 1, // (event, vehicle)
|
||||
VEHICLE_EVENT_ON_UNINSTALL = 2, // (event, vehicle)
|
||||
// UNUSED = 3, // (event, vehicle)
|
||||
VEHICLE_EVENT_ON_INSTALL_ACCESSORY = 4, // (event, vehicle, creature)
|
||||
VEHICLE_EVENT_ON_ADD_PASSENGER = 5, // (event, vehicle, unit, seatId)
|
||||
VEHICLE_EVENT_ON_REMOVE_PASSENGER = 6, // (event, vehicle, unit)
|
||||
|
||||
VEHICLE_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum CreatureEvents
|
||||
{
|
||||
CREATURE_EVENT_ON_ENTER_COMBAT = 1, // (event, creature, target) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_LEAVE_COMBAT = 2, // (event, creature) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_TARGET_DIED = 3, // (event, creature, victim) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_DIED = 4, // (event, creature, killer) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_SPAWN = 5, // (event, creature) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_REACH_WP = 6, // (event, creature, type, id) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_AIUPDATE = 7, // (event, creature, diff) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_RECEIVE_EMOTE = 8, // (event, creature, player, emoteid) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_DAMAGE_TAKEN = 9, // (event, creature, attacker, damage) - Can return true to stop normal action, can return new damage as second return value.
|
||||
CREATURE_EVENT_ON_PRE_COMBAT = 10, // (event, creature, target) - Can return true to stop normal action
|
||||
// UNUSED
|
||||
CREATURE_EVENT_ON_OWNER_ATTACKED = 12, // (event, creature, target) - Can return true to stop normal action // Not on mangos
|
||||
CREATURE_EVENT_ON_OWNER_ATTACKED_AT = 13, // (event, creature, attacker) - Can return true to stop normal action // Not on mangos
|
||||
CREATURE_EVENT_ON_HIT_BY_SPELL = 14, // (event, creature, caster, spellid) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_SPELL_HIT_TARGET = 15, // (event, creature, target, spellid) - Can return true to stop normal action
|
||||
// UNUSED = 16, // (event, creature)
|
||||
// UNUSED = 17, // (event, creature)
|
||||
// UNUSED = 18, // (event, creature)
|
||||
CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE = 19, // (event, creature, summon) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN = 20, // (event, creature, summon) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED = 21, // (event, creature, summon, killer) - Can return true to stop normal action // Not on mangos
|
||||
CREATURE_EVENT_ON_SUMMONED = 22, // (event, creature, summoner) - Can return true to stop normal action
|
||||
CREATURE_EVENT_ON_RESET = 23, // (event, creature)
|
||||
CREATURE_EVENT_ON_REACH_HOME = 24, // (event, creature) - Can return true to stop normal action
|
||||
// UNUSED = 25, // (event, creature)
|
||||
CREATURE_EVENT_ON_CORPSE_REMOVED = 26, // (event, creature, respawndelay) - Can return true to stop normal action, can return new respawndelay as second return value
|
||||
CREATURE_EVENT_ON_MOVE_IN_LOS = 27, // (event, creature, unit) - Can return true to stop normal action. Does not actually check LOS, just uses the sight range
|
||||
// UNUSED = 28, // (event, creature)
|
||||
// UNUSED = 29, // (event, creature)
|
||||
CREATURE_EVENT_ON_DUMMY_EFFECT = 30, // (event, caster, spellid, effindex, creature)
|
||||
CREATURE_EVENT_ON_QUEST_ACCEPT = 31, // (event, player, creature, quest) - Can return true
|
||||
// UNUSED = 32, // (event, creature)
|
||||
// UNUSED = 33, // (event, creature)
|
||||
CREATURE_EVENT_ON_QUEST_REWARD = 34, // (event, player, creature, quest, opt) - Can return true
|
||||
CREATURE_EVENT_ON_DIALOG_STATUS = 35, // (event, player, creature)
|
||||
CREATURE_EVENT_ON_ADD = 36, // (event, creature)
|
||||
CREATURE_EVENT_ON_REMOVE = 37, // (event, creature)
|
||||
CREATURE_EVENT_ON_AURA_APPLY = 38, // (event, creature, aura)
|
||||
CREATURE_EVENT_ON_HEAL = 39, // (event, creature, target, gain) - Can return new heal amount
|
||||
CREATURE_EVENT_ON_DAMAGE = 40, // (event, creature, target, damage) - Can return new damage amount
|
||||
CREATURE_EVENT_ON_AURA_REMOVE = 41, // (event, creature, aura, remove_mode)
|
||||
CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK = 42, // (event, creature, target, damage, spellInfo) - Can return new damage amount
|
||||
CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE = 43, // (event, creature, target, damage) - Can return new damage amount
|
||||
CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN = 44, // (event, creature, target, damage, spellInfo) - Can return new damage amount
|
||||
CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED = 45, // (event, creature, target, heal, spellInfo) - Can return new heal amount
|
||||
CREATURE_EVENT_ON_DEAL_DAMAGE = 46, // (event, creature, target, damage, damagetype) - Can return new damage amount
|
||||
CREATURE_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum GameObjectEvents
|
||||
{
|
||||
GAMEOBJECT_EVENT_ON_AIUPDATE = 1, // (event, go, diff)
|
||||
GAMEOBJECT_EVENT_ON_SPAWN = 2, // (event, go)
|
||||
GAMEOBJECT_EVENT_ON_DUMMY_EFFECT = 3, // (event, caster, spellid, effindex, go) - Can return true to stop normal action
|
||||
GAMEOBJECT_EVENT_ON_QUEST_ACCEPT = 4, // (event, player, go, quest) - Can return true to stop normal action
|
||||
GAMEOBJECT_EVENT_ON_QUEST_REWARD = 5, // (event, player, go, quest, opt) - Can return true to stop normal action
|
||||
GAMEOBJECT_EVENT_ON_DIALOG_STATUS = 6, // (event, player, go)
|
||||
GAMEOBJECT_EVENT_ON_DESTROYED = 7, // (event, go, attacker)
|
||||
GAMEOBJECT_EVENT_ON_DAMAGED = 8, // (event, go, attacker)
|
||||
GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE = 9, // (event, go, state)
|
||||
GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED = 10, // (event, go, state)
|
||||
// UNUSED = 11, // (event, gameobject)
|
||||
GAMEOBJECT_EVENT_ON_ADD = 12, // (event, gameobject)
|
||||
GAMEOBJECT_EVENT_ON_REMOVE = 13, // (event, gameobject)
|
||||
GAMEOBJECT_EVENT_ON_USE = 14, // (event, go, player) - Can return true to stop normal action
|
||||
GAMEOBJECT_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum ItemEvents
|
||||
{
|
||||
ITEM_EVENT_ON_DUMMY_EFFECT = 1, // (event, caster, spellid, effindex, item)
|
||||
ITEM_EVENT_ON_USE = 2, // (event, player, item, target) - Can return false to stop the spell casting
|
||||
ITEM_EVENT_ON_QUEST_ACCEPT = 3, // (event, player, item, quest) - Can return true
|
||||
ITEM_EVENT_ON_EXPIRE = 4, // (event, player, itemid) - Can return true
|
||||
ITEM_EVENT_ON_REMOVE = 5, // (event, player, item) - Can return true
|
||||
ITEM_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum GossipEvents
|
||||
{
|
||||
GOSSIP_EVENT_ON_HELLO = 1, // (event, player, object) - Object is the Creature/GameObject/Item. Can return false to do default action. For item gossip can return false to stop spell casting.
|
||||
GOSSIP_EVENT_ON_SELECT = 2, // (event, player, object, sender, intid, code, menu_id) - Object is the Creature/GameObject/Item/Player, menu_id is only for player gossip. Can return false to do default action.
|
||||
GOSSIP_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum BGEvents
|
||||
{
|
||||
BG_EVENT_ON_START = 1, // (event, bg, bgId, instanceId) - Needs to be added to TC
|
||||
BG_EVENT_ON_END = 2, // (event, bg, bgId, instanceId, winner) - Needs to be added to TC
|
||||
BG_EVENT_ON_CREATE = 3, // (event, bg, bgId, instanceId) - Needs to be added to TC
|
||||
BG_EVENT_ON_PRE_DESTROY = 4, // (event, bg, bgId, instanceId) - Needs to be added to TC
|
||||
BG_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum InstanceEvents
|
||||
{
|
||||
INSTANCE_EVENT_ON_INITIALIZE = 1, // (event, instance_data, map)
|
||||
INSTANCE_EVENT_ON_LOAD = 2, // (event, instance_data, map)
|
||||
INSTANCE_EVENT_ON_UPDATE = 3, // (event, instance_data, map, diff)
|
||||
INSTANCE_EVENT_ON_PLAYER_ENTER = 4, // (event, instance_data, map, player)
|
||||
INSTANCE_EVENT_ON_CREATURE_CREATE = 5, // (event, instance_data, map, creature)
|
||||
INSTANCE_EVENT_ON_GAMEOBJECT_CREATE = 6, // (event, instance_data, map, go)
|
||||
INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS = 7, // (event, instance_data, map)
|
||||
INSTANCE_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum TicketEvents
|
||||
{
|
||||
TICKET_EVENT_ON_CREATE = 1, // (event, ticket)
|
||||
TICKET_EVENT_UPDATE_LAST_CHANGE = 2, // (event, ticket, message)
|
||||
TICKET_EVENT_ON_CLOSE = 3, // (event, ticket)
|
||||
TICKET_EVENT_ON_RESOLVE = 4, // (event, ticket)
|
||||
TICKET_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum SpellEvents
|
||||
{
|
||||
SPELL_EVENT_ON_PREPARE = 1, // (event, caster, spell)
|
||||
SPELL_EVENT_ON_CAST = 2, // (event, caster, spell, skipCheck)
|
||||
SPELL_EVENT_ON_CAST_CANCEL = 3, // (event, caster, spell, bySelf)
|
||||
SPELL_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum AllCreatureEvents
|
||||
{
|
||||
ALL_CREATURE_EVENT_ON_ADD = 1, // (event, creature)
|
||||
ALL_CREATURE_EVENT_ON_REMOVE = 2, // (event, creature)
|
||||
ALL_CREATURE_EVENT_ON_SELECT_LEVEL = 3, // (event, creature_template, creature)
|
||||
ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL = 4, // (event, creature_template, creature, level) - Can return the new level
|
||||
ALL_CREATURE_EVENT_ON_AURA_APPLY = 5, // (event, creature, aura)
|
||||
ALL_CREATURE_EVENT_ON_HEAL = 6, // (event, creature, target, gain) - Can return new heal amount
|
||||
ALL_CREATURE_EVENT_ON_DAMAGE = 7, // (event, creature, target, damage) - Can return new damage amount
|
||||
ALL_CREATURE_EVENT_ON_AURA_REMOVE = 8, // (event, creature, aura, remove_mode)
|
||||
ALL_CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK = 9, // (event, creature, target, damage, spellInfo) - Can return new damage amount
|
||||
ALL_CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE = 10, // (event, creature, target, damage) - Can return new damage amount
|
||||
ALL_CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN = 11, // (event, creature, target, damage, spellInfo) - Can return new damage amount
|
||||
ALL_CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED = 12, // (event, creature, target, heal, spellInfo) - Can return new heal amount
|
||||
ALL_CREATURE_EVENT_ON_DEAL_DAMAGE = 13, // (event, creature, target, damage, damagetype) - Can return new damage amount
|
||||
ALL_CREATURE_EVENT_COUNT
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _HOOKS_H
|
||||
@@ -0,0 +1,275 @@
|
||||
#include <thread>
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
};
|
||||
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
|
||||
#include "libs/httplib.h"
|
||||
#include "HttpManager.h"
|
||||
#include "LuaEngine.h"
|
||||
|
||||
HttpWorkItem::HttpWorkItem(int funcRef, const std::string& httpVerb, const std::string& url, const std::string& body, const std::string& contentType, const httplib::Headers& headers)
|
||||
: funcRef(funcRef),
|
||||
httpVerb(httpVerb),
|
||||
url(url),
|
||||
body(body),
|
||||
contentType(contentType),
|
||||
headers(headers)
|
||||
{ }
|
||||
|
||||
HttpResponse::HttpResponse(int funcRef, int statusCode, const std::string& body, const httplib::Headers& headers)
|
||||
: funcRef(funcRef),
|
||||
statusCode(statusCode),
|
||||
body(body),
|
||||
headers(headers)
|
||||
{ }
|
||||
|
||||
HttpManager::HttpManager()
|
||||
: workQueue(16),
|
||||
responseQueue(16),
|
||||
startedWorkerThread(false),
|
||||
cancelationToken(false),
|
||||
condVar(),
|
||||
condVarMutex(),
|
||||
parseUrlRegex("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?")
|
||||
{
|
||||
StartHttpWorker();
|
||||
}
|
||||
|
||||
HttpManager::~HttpManager()
|
||||
{
|
||||
StopHttpWorker();
|
||||
}
|
||||
|
||||
void HttpManager::PushRequest(HttpWorkItem* item)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(condVarMutex);
|
||||
workQueue.push(item);
|
||||
condVar.notify_one();
|
||||
}
|
||||
|
||||
void HttpManager::StartHttpWorker()
|
||||
{
|
||||
ClearQueues();
|
||||
|
||||
if (!startedWorkerThread)
|
||||
{
|
||||
cancelationToken.store(false);
|
||||
workerThread = std::thread(&HttpManager::HttpWorkerThread, this);
|
||||
startedWorkerThread = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpManager::ClearQueues()
|
||||
{
|
||||
while (workQueue.front())
|
||||
{
|
||||
HttpWorkItem* item = *workQueue.front();
|
||||
if (item != nullptr)
|
||||
{
|
||||
delete item;
|
||||
}
|
||||
workQueue.pop();
|
||||
}
|
||||
|
||||
while (responseQueue.front())
|
||||
{
|
||||
HttpResponse* item = *responseQueue.front();
|
||||
if (item != nullptr)
|
||||
{
|
||||
delete item;
|
||||
}
|
||||
responseQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpManager::StopHttpWorker()
|
||||
{
|
||||
if (!startedWorkerThread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cancelationToken.store(true);
|
||||
condVar.notify_one();
|
||||
workerThread.join();
|
||||
ClearQueues();
|
||||
startedWorkerThread = false;
|
||||
}
|
||||
|
||||
void HttpManager::HttpWorkerThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(condVarMutex);
|
||||
condVar.wait(lock, [&] { return workQueue.front() != nullptr || cancelationToken.load(); });
|
||||
}
|
||||
|
||||
if (cancelationToken.load())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!workQueue.front())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HttpWorkItem* req = *workQueue.front();
|
||||
workQueue.pop();
|
||||
if (!req)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::string host;
|
||||
std::string path;
|
||||
|
||||
if (!ParseUrl(req->url, host, path)) {
|
||||
ALE_LOG_ERROR("[ALE]: Could not parse URL {}", req->url);
|
||||
continue;
|
||||
}
|
||||
|
||||
httplib::Client cli(host);
|
||||
cli.set_connection_timeout(0, 3000000); // 3 seconds
|
||||
cli.set_read_timeout(5, 0); // 5 seconds
|
||||
cli.set_write_timeout(5, 0); // 5 seconds
|
||||
|
||||
httplib::Result res = DoRequest(cli, req, path);
|
||||
httplib::Error err = res.error();
|
||||
if (err != httplib::Error::Success)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALE]: HTTP request error: {}", httplib::to_string(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res->status == 301)
|
||||
{
|
||||
std::string location = res->get_header_value("Location");
|
||||
std::string host;
|
||||
std::string path;
|
||||
|
||||
if (!ParseUrl(location, host, path))
|
||||
{
|
||||
ALE_LOG_ERROR("[ALE]: Could not parse URL after redirect: {}", location);
|
||||
continue;
|
||||
}
|
||||
httplib::Client cli2(host);
|
||||
cli2.set_connection_timeout(0, 3000000); // 3 seconds
|
||||
cli2.set_read_timeout(5, 0); // 5 seconds
|
||||
cli2.set_write_timeout(5, 0); // 5 seconds
|
||||
res = DoRequest(cli2, req, path);
|
||||
}
|
||||
|
||||
responseQueue.push(new HttpResponse(req->funcRef, res->status, res->body, res->headers));
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
ALE_LOG_ERROR("[ALE]: HTTP request error: {}", ex.what());
|
||||
}
|
||||
|
||||
delete req;
|
||||
}
|
||||
}
|
||||
|
||||
httplib::Result HttpManager::DoRequest(httplib::Client& client, HttpWorkItem* req, const std::string& urlPath)
|
||||
{
|
||||
const char* path = urlPath.c_str();
|
||||
if (req->httpVerb == "GET")
|
||||
{
|
||||
return client.Get(path, req->headers);
|
||||
}
|
||||
if (req->httpVerb == "HEAD")
|
||||
{
|
||||
return client.Head(path, req->headers);
|
||||
}
|
||||
if (req->httpVerb == "POST")
|
||||
{
|
||||
return client.Post(path, req->headers, req->body, req->contentType.c_str());
|
||||
}
|
||||
if (req->httpVerb == "PUT")
|
||||
{
|
||||
return client.Put(path, req->headers, req->body, req->contentType.c_str());
|
||||
}
|
||||
if (req->httpVerb == "PATCH")
|
||||
{
|
||||
return client.Patch(path, req->headers, req->body, req->contentType.c_str());
|
||||
}
|
||||
if (req->httpVerb == "DELETE")
|
||||
{
|
||||
return client.Delete(path, req->headers);
|
||||
}
|
||||
if (req->httpVerb == "OPTIONS")
|
||||
{
|
||||
return client.Options(path, req->headers);
|
||||
}
|
||||
|
||||
ALE_LOG_ERROR("[ALE]: HTTP request error: invalid HTTP verb {}", req->httpVerb);
|
||||
return client.Get(path, req->headers);
|
||||
}
|
||||
|
||||
bool HttpManager::ParseUrl(const std::string& url, std::string& host, std::string& path)
|
||||
{
|
||||
std::smatch matches;
|
||||
|
||||
if (!std::regex_search(url, matches, parseUrlRegex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string scheme = matches[2];
|
||||
std::string authority = matches[4];
|
||||
std::string query = matches[7];
|
||||
host = scheme + "://" + authority;
|
||||
path = matches[5];
|
||||
if (path.empty())
|
||||
{
|
||||
path = "/";
|
||||
}
|
||||
path += (query.empty() ? "" : "?") + query;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpManager::HandleHttpResponses()
|
||||
{
|
||||
while (!responseQueue.empty())
|
||||
{
|
||||
HttpResponse* res = *responseQueue.front();
|
||||
responseQueue.pop();
|
||||
|
||||
if (res == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LOCK_ALE;
|
||||
|
||||
lua_State* L = ALE::GALE->L;
|
||||
|
||||
// Get function
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, res->funcRef);
|
||||
|
||||
// Push parameters
|
||||
ALE::Push(L, res->statusCode);
|
||||
ALE::Push(L, res->body);
|
||||
lua_newtable(L);
|
||||
for (const auto& item : res->headers) {
|
||||
ALE::Push(L, item.first);
|
||||
ALE::Push(L, item.second);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
// Call function
|
||||
ALE::GALE->ExecuteCall(3, 0);
|
||||
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, res->funcRef);
|
||||
|
||||
delete res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef ALE_HTTP_MANAGER_H
|
||||
#define ALE_HTTP_MANAGER_H
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "libs/httplib.h"
|
||||
#include "libs/rigtorp/SPSCQueue.h"
|
||||
|
||||
struct HttpWorkItem
|
||||
{
|
||||
public:
|
||||
HttpWorkItem(int funcRef, const std::string& httpVerb, const std::string& url, const std::string& body, const std::string &contentType, const httplib::Headers& headers);
|
||||
|
||||
int funcRef;
|
||||
std::string httpVerb;
|
||||
std::string url;
|
||||
std::string body;
|
||||
std::string contentType;
|
||||
httplib::Headers headers;
|
||||
};
|
||||
|
||||
struct HttpResponse
|
||||
{
|
||||
public:
|
||||
HttpResponse(int funcRef, int statusCode, const std::string& body, const httplib::Headers& headers);
|
||||
|
||||
int funcRef;
|
||||
int statusCode;
|
||||
std::string body;
|
||||
httplib::Headers headers;
|
||||
};
|
||||
|
||||
|
||||
class HttpManager
|
||||
{
|
||||
public:
|
||||
HttpManager();
|
||||
~HttpManager();
|
||||
|
||||
void StartHttpWorker();
|
||||
void StopHttpWorker();
|
||||
void PushRequest(HttpWorkItem* item);
|
||||
void HandleHttpResponses();
|
||||
|
||||
private:
|
||||
void ClearQueues();
|
||||
void HttpWorkerThread();
|
||||
bool ParseUrl(const std::string& url, std::string& host, std::string& path);
|
||||
httplib::Result DoRequest(httplib::Client& client, HttpWorkItem* req, const std::string& path);
|
||||
|
||||
rigtorp::SPSCQueue<HttpWorkItem*> workQueue;
|
||||
rigtorp::SPSCQueue<HttpResponse*> responseQueue;
|
||||
std::thread workerThread;
|
||||
bool startedWorkerThread;
|
||||
std::atomic_bool cancelationToken;
|
||||
std::condition_variable condVar;
|
||||
std::mutex condVarMutex;
|
||||
std::regex parseUrlRegex;
|
||||
};
|
||||
|
||||
#endif // #ifndef ALE_HTTP_MANAGER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,627 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "SharedDefines.h"
|
||||
#include "DBCEnums.h"
|
||||
|
||||
#include "Group.h"
|
||||
#include "Item.h"
|
||||
#include "Chat.h"
|
||||
#include "Player.h"
|
||||
#include "Weather.h"
|
||||
#include "World.h"
|
||||
#include "Hooks.h"
|
||||
#include "LFG.h"
|
||||
#include "ALEUtility.h"
|
||||
#include "HttpManager.h"
|
||||
#include "EventEmitter.h"
|
||||
#include "TicketMgr.h"
|
||||
#include "LootMgr.h"
|
||||
#include "ALEFileWatcher.h"
|
||||
#include "ALEConfig.h"
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <lua.h>
|
||||
};
|
||||
|
||||
struct ItemTemplate;
|
||||
typedef BattlegroundTypeId BattleGroundTypeId;
|
||||
typedef AreaTrigger AreaTriggerEntry;
|
||||
class AuctionHouseObject;
|
||||
struct AuctionEntry;
|
||||
class Battleground;
|
||||
typedef Battleground BattleGround;
|
||||
class Channel;
|
||||
class Corpse;
|
||||
class Creature;
|
||||
class CreatureAI;
|
||||
class GameObject;
|
||||
class GameObjectAI;
|
||||
class Guild;
|
||||
class Group;
|
||||
class InstanceScript;
|
||||
typedef InstanceScript InstanceData;
|
||||
class ALEInstanceAI;
|
||||
class Item;
|
||||
class Pet;
|
||||
class Player;
|
||||
class Quest;
|
||||
class Spell;
|
||||
class SpellCastTargets;
|
||||
class TempSummon;
|
||||
// class Transport;
|
||||
class Unit;
|
||||
class Weather;
|
||||
class WorldPacket;
|
||||
class Vehicle;
|
||||
|
||||
struct lua_State;
|
||||
class EventMgr;
|
||||
class ALEObject;
|
||||
template<typename T> class ALETemplate;
|
||||
|
||||
template<typename K> class BindingMap;
|
||||
template<typename T> struct EventKey;
|
||||
template<typename T> struct EntryKey;
|
||||
template<typename T> struct UniqueObjectKey;
|
||||
|
||||
// Type definition for bytecode buffer
|
||||
typedef std::vector<uint8> BytecodeBuffer;
|
||||
|
||||
// Global bytecode cache entry
|
||||
struct GlobalCacheEntry
|
||||
{
|
||||
BytecodeBuffer bytecode;
|
||||
std::time_t last_modified;
|
||||
std::string filepath;
|
||||
|
||||
GlobalCacheEntry() : last_modified(0) {}
|
||||
GlobalCacheEntry(const BytecodeBuffer& code, std::time_t modTime, const std::string& path)
|
||||
: bytecode(code), last_modified(modTime), filepath(path) {}
|
||||
};
|
||||
|
||||
struct LuaScript
|
||||
{
|
||||
std::string fileext;
|
||||
std::string filename;
|
||||
std::string filepath;
|
||||
std::string modulepath;
|
||||
LuaScript() {}
|
||||
};
|
||||
|
||||
#define ALE_STATE_PTR "ALE State Ptr"
|
||||
#define LOCK_ALE ALE::Guard __guard(ALE::GetLock())
|
||||
|
||||
#define ALE_GAME_API AC_GAME_API
|
||||
|
||||
class ALE_GAME_API ALE
|
||||
{
|
||||
public:
|
||||
typedef std::list<LuaScript> ScriptList;
|
||||
|
||||
typedef std::recursive_mutex LockType;
|
||||
typedef std::lock_guard<LockType> Guard;
|
||||
|
||||
const std::string& GetRequirePath() const { return lua_requirepath; }
|
||||
const std::string& GetRequireCPath() const { return lua_requirecpath; }
|
||||
|
||||
private:
|
||||
static bool reload;
|
||||
static bool initialized;
|
||||
static LockType lock;
|
||||
static std::unique_ptr<ALEFileWatcher> fileWatcher;
|
||||
|
||||
// Lua script locations
|
||||
static ScriptList lua_scripts;
|
||||
static ScriptList lua_extensions;
|
||||
|
||||
// Lua script folder path
|
||||
static std::string lua_folderpath;
|
||||
// lua path variable for require() function
|
||||
static std::string lua_requirepath;
|
||||
static std::string lua_requirecpath;
|
||||
|
||||
// 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;
|
||||
// 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 from instance ID -> Lua table ref
|
||||
std::unordered_map<uint32, int> instanceDataRefs;
|
||||
// Map from map ID -> Lua table ref
|
||||
std::unordered_map<uint32, int> continentDataRefs;
|
||||
|
||||
ALE();
|
||||
~ALE();
|
||||
|
||||
// Prevent copy
|
||||
ALE(ALE const&) = delete;
|
||||
ALE& operator=(const ALE&) = delete;
|
||||
|
||||
void OpenLua();
|
||||
void CloseLua();
|
||||
void DestroyBindStores();
|
||||
void CreateBindStores();
|
||||
void InvalidateObjects();
|
||||
|
||||
// Use ReloadALE() to make ALE reload
|
||||
// This is called on world update to reload ALE
|
||||
static void _ReloadALE();
|
||||
static void LoadScriptPaths();
|
||||
static void GetScripts(std::string path);
|
||||
static void AddScriptPath(std::string filename, const std::string& fullpath);
|
||||
static int LoadCompiledScript(lua_State* L, const std::string& filepath);
|
||||
static std::time_t GetFileModTime(const std::string& filepath);
|
||||
static std::time_t GetFileModTimeWithCache(const std::string& filepath);
|
||||
|
||||
// Global cache management
|
||||
static bool CompileScriptToGlobalCache(const std::string& filepath);
|
||||
static bool CompileMoonScriptToGlobalCache(const std::string& filepath);
|
||||
static int TryLoadFromGlobalCache(lua_State* L, const std::string& filepath);
|
||||
static int LoadScriptWithCache(lua_State* L, const std::string& filepath, bool isMoonScript, uint32* compiledCount = nullptr, uint32* cachedCount = nullptr);
|
||||
static void ClearGlobalCache();
|
||||
static void ClearTimestampCache();
|
||||
static size_t GetGlobalCacheSize();
|
||||
|
||||
static int StackTrace(lua_State *_L);
|
||||
static void Report(lua_State* _L);
|
||||
|
||||
// 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<typename K1, typename K2> int SetupStack(BindingMap<K1>* bindings1, BindingMap<K2>* 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<typename T> void ReplaceArgument(T value, uint8 index);
|
||||
template<typename K1, typename K2> void CallAllFunctions(BindingMap<K1>* bindings1, BindingMap<K2>* bindings2, const K1& key1, const K2& key2);
|
||||
template<typename K1, typename K2> bool CallAllFunctionsBool(BindingMap<K1>* bindings1, BindingMap<K2>* bindings2, const K1& key1, const K2& key2, bool default_value = false);
|
||||
|
||||
// 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<typename K> int SetupStack(BindingMap<K>* bindings, const K& key, int number_of_arguments)
|
||||
{
|
||||
return SetupStack<K, K>(bindings, NULL, key, key, number_of_arguments);
|
||||
}
|
||||
template<typename K> void CallAllFunctions(BindingMap<K>* bindings, const K& key)
|
||||
{
|
||||
CallAllFunctions<K, K>(bindings, NULL, key, key);
|
||||
}
|
||||
template<typename K> bool CallAllFunctionsBool(BindingMap<K>* bindings, const K& key, bool default_value = false)
|
||||
{
|
||||
return CallAllFunctionsBool<K, K>(bindings, NULL, key, key, default_value);
|
||||
}
|
||||
|
||||
// Non-static pushes, to be used in hooks.
|
||||
// These just call the correct static version with the main thread's Lua state.
|
||||
void Push() { Push(L); ++push_counter; }
|
||||
void Push(const long long value) { Push(L, value); ++push_counter; }
|
||||
void Push(const unsigned long long value) { Push(L, value); ++push_counter; }
|
||||
void Push(const long value) { Push(L, value); ++push_counter; }
|
||||
void Push(const unsigned long value) { Push(L, value); ++push_counter; }
|
||||
void Push(const int value) { Push(L, value); ++push_counter; }
|
||||
void Push(const unsigned int value) { Push(L, value); ++push_counter; }
|
||||
void Push(const bool value) { Push(L, value); ++push_counter; }
|
||||
void Push(const float value) { Push(L, value); ++push_counter; }
|
||||
void Push(const double value) { Push(L, value); ++push_counter; }
|
||||
void Push(const std::string& value) { Push(L, value); ++push_counter; }
|
||||
void Push(const char* value) { Push(L, value); ++push_counter; }
|
||||
void Push(ObjectGuid const value) { Push(L, value); ++push_counter; }
|
||||
void Push(const CreatureTemplate* value) { Push(L, value); ++push_counter; }
|
||||
template<typename T>
|
||||
void Push(T const* ptr) { Push(L, ptr); ++push_counter; }
|
||||
|
||||
public:
|
||||
static ALE* GALE;
|
||||
|
||||
lua_State* L;
|
||||
EventMgr* eventMgr;
|
||||
HttpManager httpManager;
|
||||
QueryCallbackProcessor queryProcessor;
|
||||
EventEmitter<void(std::string)> OnError;
|
||||
|
||||
BindingMap< EventKey<Hooks::ServerEvents> >* ServerEventBindings;
|
||||
BindingMap< EventKey<Hooks::PlayerEvents> >* PlayerEventBindings;
|
||||
BindingMap< EventKey<Hooks::GuildEvents> >* GuildEventBindings;
|
||||
BindingMap< EventKey<Hooks::GroupEvents> >* GroupEventBindings;
|
||||
BindingMap< EventKey<Hooks::VehicleEvents> >* VehicleEventBindings;
|
||||
BindingMap< EventKey<Hooks::BGEvents> >* BGEventBindings;
|
||||
BindingMap< EventKey<Hooks::AllCreatureEvents> >* AllCreatureEventBindings;
|
||||
|
||||
BindingMap< EntryKey<Hooks::PacketEvents> >* PacketEventBindings;
|
||||
BindingMap< EntryKey<Hooks::CreatureEvents> >* CreatureEventBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* CreatureGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::GameObjectEvents> >* GameObjectEventBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* GameObjectGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::ItemEvents> >* ItemEventBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* ItemGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* PlayerGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::InstanceEvents> >* MapEventBindings;
|
||||
BindingMap< EntryKey<Hooks::InstanceEvents> >* InstanceEventBindings;
|
||||
BindingMap< EventKey<Hooks::TicketEvents> >* TicketEventBindings;
|
||||
BindingMap< EntryKey<Hooks::SpellEvents> >* SpellEventBindings;
|
||||
|
||||
BindingMap< UniqueObjectKey<Hooks::CreatureEvents> >* CreatureUniqueBindings;
|
||||
|
||||
static void Initialize();
|
||||
static void Uninitialize();
|
||||
// This function is used to make ALE reload
|
||||
static void ReloadALE() { LOCK_ALE; reload = true; }
|
||||
static LockType& GetLock() { return lock; };
|
||||
static bool IsInitialized() { return initialized; }
|
||||
// Never returns nullptr
|
||||
static ALE* GetALE(lua_State* L)
|
||||
{
|
||||
lua_pushstring(L, ALE_STATE_PTR);
|
||||
lua_rawget(L, LUA_REGISTRYINDEX);
|
||||
ASSERT(lua_islightuserdata(L, -1));
|
||||
ALE* E = static_cast<ALE*>(lua_touserdata(L, -1));
|
||||
lua_pop(L, 1);
|
||||
ASSERT(E);
|
||||
return E;
|
||||
}
|
||||
|
||||
// Static pushes, can be used by anything, including methods.
|
||||
static void Push(lua_State* luastate); // nil
|
||||
static void Push(lua_State* luastate, const long long);
|
||||
static void Push(lua_State* luastate, const unsigned long long);
|
||||
static void Push(lua_State* luastate, const long);
|
||||
static void Push(lua_State* luastate, const unsigned long);
|
||||
static void Push(lua_State* luastate, const int);
|
||||
static void Push(lua_State* luastate, const unsigned int);
|
||||
static void Push(lua_State* luastate, const bool);
|
||||
static void Push(lua_State* luastate, const float);
|
||||
static void Push(lua_State* luastate, const double);
|
||||
static void Push(lua_State* luastate, const std::string&);
|
||||
static void Push(lua_State* luastate, const char*);
|
||||
static void Push(lua_State* luastate, Object const* obj);
|
||||
static void Push(lua_State* luastate, WorldObject const* obj);
|
||||
static void Push(lua_State* luastate, Unit const* unit);
|
||||
static void Push(lua_State* luastate, Pet const* pet);
|
||||
static void Push(lua_State* luastate, TempSummon const* summon);
|
||||
static void Push(lua_State* luastate, ObjectGuid const guid);
|
||||
static void Push(lua_State* luastate, GemPropertiesEntry const& gemProperties);
|
||||
static void Push(lua_State* luastate, SpellEntry const& spell);
|
||||
static void Push(lua_State* luastate, CreatureTemplate const* creatureTemplate);
|
||||
template<typename T>
|
||||
static void Push(lua_State* luastate, T const* ptr)
|
||||
{
|
||||
ALETemplate<T>::Push(luastate, ptr);
|
||||
}
|
||||
|
||||
static std::string FormatQuery(lua_State* L, const char* query);
|
||||
|
||||
bool ExecuteCall(int params, int res);
|
||||
|
||||
/*
|
||||
* Returns `true` if ALE 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 `ALEInstanceAI` is needed because the instance data might
|
||||
* not exist (i.e. ALE 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(lua_State* L, ALEInstanceAI* ai, bool incrementCounter = true);
|
||||
|
||||
void RunScripts();
|
||||
bool ShouldReload() const { return reload; }
|
||||
bool HasLuaState() const { return L != NULL; }
|
||||
uint64 GetCallstackId() const { return callstackid; }
|
||||
int Register(lua_State* L, uint8 reg, uint32 entry, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots);
|
||||
|
||||
// Checks
|
||||
template<typename T> static T CHECKVAL(lua_State* luastate, int narg);
|
||||
template<typename T> static T CHECKVAL(lua_State* luastate, int narg, T def)
|
||||
{
|
||||
return lua_isnoneornil(luastate, narg) ? def : CHECKVAL<T>(luastate, narg);
|
||||
}
|
||||
template<typename T> static T* CHECKOBJ(lua_State* luastate, int narg, bool error = true)
|
||||
{
|
||||
return ALETemplate<T>::Check(luastate, narg, error);
|
||||
}
|
||||
static ALEObject* CHECKTYPE(lua_State* luastate, int narg, const char *tname, bool error = true);
|
||||
|
||||
CreatureAI* GetAI(Creature* creature);
|
||||
InstanceData* GetInstanceData(Map* map);
|
||||
void FreeInstanceId(uint32 instanceId);
|
||||
|
||||
/* Custom */
|
||||
void OnTimedEvent(int funcRef, uint32 delay, uint32 calls, WorldObject* obj);
|
||||
bool OnCommand(ChatHandler& handler, 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 OnLearnTalents(Player* pPlayer, uint32 talentId, uint32 talentRank, 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);
|
||||
void OnPetAddedToWorld(Player* player, Creature* pet);
|
||||
void OnQuestRewardItem(Player* player, Item* item, uint32 count);
|
||||
void OnCreateItem(Player* player, Item* item, uint32 count);
|
||||
void OnStoreNewItem(Player* player, Item* item, uint32 count);
|
||||
void OnPlayerCompleteQuest(Player* player, Quest const* quest);
|
||||
|
||||
/* 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 HandleGossipSelectOption(Player* pPlayer, Item* item, uint32 sender, uint32 action, const std::string& code);
|
||||
|
||||
/* 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);
|
||||
void OnCreatureAuraApply(Creature* me, Aura* aura);
|
||||
void OnCreatureHeal(Creature* me, Unit* target, uint32& gain);
|
||||
void OnCreatureDamage(Creature* me, Unit* target, uint32& gain);
|
||||
void OnCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode);
|
||||
void OnCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo);
|
||||
void OnCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage);
|
||||
void OnCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo);
|
||||
void OnCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo);
|
||||
uint32 OnCreatureDealDamage(Creature* me, Unit* pVictim, uint32 damage, DamageEffectType damagetype);
|
||||
|
||||
/* 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);
|
||||
void OnDestroyed(GameObject* pGameObject, WorldObject* attacker);
|
||||
void OnDamaged(GameObject* pGameObject, WorldObject* attacker);
|
||||
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 const& packet);
|
||||
void OnPacketReceiveAny(Player* player, WorldPacket const& packet, bool& result);
|
||||
void OnPacketReceiveOne(Player* player, WorldPacket const& 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 OnLevelChanged(Player* pPlayer, uint8 oldLevel);
|
||||
void OnFreeTalentPointsChanged(Player* pPlayer, uint32 newPoints);
|
||||
void OnTalentsReset(Player* pPlayer, bool noCost);
|
||||
void OnMoneyChanged(Player* pPlayer, int32& amount);
|
||||
void OnGiveXP(Player* pPlayer, uint32& amount, Unit* pVictim, uint8 xpSource);
|
||||
bool 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 OnPlayerSpellCast(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 OnUpdateArea(Player* pPlayer, uint32 oldArea, uint32 newArea);
|
||||
void OnUpdateZone(Player* pPlayer, uint32 newZone, uint32 newArea);
|
||||
void OnMapChanged(Player* pPlayer);
|
||||
void HandleGossipSelectOption(Player* pPlayer, uint32 menuId, uint32 sender, uint32 action, const std::string& code);
|
||||
void OnLearnSpell(Player* player, uint32 spellId);
|
||||
void OnAchiComplete(Player* player, AchievementEntry const* achievement);
|
||||
void OnFfaPvpStateUpdate(Player* player, bool hasFfaPvp);
|
||||
bool OnCanInitTrade(Player* player, Player* target);
|
||||
bool OnCanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid mailbox, std::string& subject, std::string& body, uint32 money, uint32 cod, Item* item);
|
||||
bool OnCanJoinLfg(Player* player, uint8 roles, lfg::LfgDungeonSet& dungeons, const std::string& comment);
|
||||
bool OnCanGroupInvite(Player* player, std::string& memberName);
|
||||
void OnGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll);
|
||||
void OnBattlegroundDesertion(Player* player, const BattlegroundDesertionType type);
|
||||
void OnCreatureKilledByPet(Player* player, Creature* killed);
|
||||
bool OnPlayerCanUpdateSkill(Player* player, uint32 skill_id);
|
||||
void OnPlayerBeforeUpdateSkill(Player* player, uint32 skill_id, uint32& value, uint32 max, uint32 step);
|
||||
void OnPlayerUpdateSkill(Player* player, uint32 skill_id, uint32 value, uint32 max, uint32 step, uint32 new_value);
|
||||
bool CanPlayerResurrect(Player* player);
|
||||
void OnPlayerQuestAccept(Player* player, Quest const* quest);
|
||||
void OnPlayerAuraApply(Player* player, Aura* aura);
|
||||
void OnPlayerAuraRemove(Player* player, Aura* aura, AuraRemoveMode mode);
|
||||
void OnPlayerHeal(Player* player, Unit* target, uint32& gain);
|
||||
void OnPlayerDamage(Player* player, Unit* target, uint32& damage);
|
||||
void OnPlayerModifyPeriodicDamageAurasTick(Player* player, Unit* target, uint32& damage, SpellInfo const* spellInfo);
|
||||
void OnPlayerModifyMeleeDamage(Player* player, Unit* target, uint32& damage);
|
||||
void OnPlayerModifySpellDamageTaken(Player* player, Unit* target, int32& damage, SpellInfo const* spellInfo);
|
||||
void OnPlayerModifyHealReceived(Player* player, Unit* target, uint32& heal, SpellInfo const* spellInfo);
|
||||
uint32 OnPlayerDealDamage(Player* player, Unit* pVictim, uint32 damage, DamageEffectType damagetype);
|
||||
void OnPlayerReleasedGhost(Player* player);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
/* Map */
|
||||
void OnCreate(Map* map);
|
||||
void OnDestroy(Map* map);
|
||||
void OnPlayerEnter(Map* map, Player* player);
|
||||
void OnPlayerLeave(Map* map, Player* player);
|
||||
void OnUpdate(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(ALEInstanceAI* ai);
|
||||
void OnLoad(ALEInstanceAI* ai);
|
||||
void OnUpdateInstance(ALEInstanceAI* ai, uint32 diff);
|
||||
void OnPlayerEnterInstance(ALEInstanceAI* ai, Player* player);
|
||||
void OnCreatureCreate(ALEInstanceAI* ai, Creature* creature);
|
||||
void OnGameObjectCreate(ALEInstanceAI* ai, GameObject* gameobject);
|
||||
bool OnCheckEncounterInProgress(ALEInstanceAI* ai);
|
||||
|
||||
/* World */
|
||||
void OnOpenStateChange(bool open);
|
||||
void OnConfigLoad(bool reload, bool isBefore);
|
||||
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, TeamId winner);
|
||||
void OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId);
|
||||
void OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId);
|
||||
|
||||
/* Ticket */
|
||||
void OnTicketCreate(GmTicket* ticket);
|
||||
void OnTicketClose(GmTicket* ticket);
|
||||
void OnTicketUpdateLastChange(GmTicket* ticket);
|
||||
void OnTicketResolve(GmTicket* ticket);
|
||||
|
||||
/* Spell */
|
||||
void OnSpellPrepare(Unit* caster, Spell* spell, SpellInfo const* spellInfo);
|
||||
void OnSpellCast(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool skipCheck);
|
||||
void OnSpellCastCancel(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool bySelf);
|
||||
|
||||
/* AllCreature */
|
||||
void OnAllCreatureAddToWorld(Creature* creature);
|
||||
void OnAllCreatureRemoveFromWorld(Creature* creature);
|
||||
void OnAllCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature);
|
||||
void OnAllCreatureBeforeSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level);
|
||||
void OnAllCreatureAuraApply(Creature* me, Aura* aura);
|
||||
void OnAllCreatureHeal(Creature* me, Unit* target, uint32& gain);
|
||||
void OnAllCreatureDamage(Creature* me, Unit* target, uint32& gain);
|
||||
void OnAllCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode);
|
||||
void OnAllCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo);
|
||||
void OnAllCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage);
|
||||
void OnAllCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo);
|
||||
void OnAllCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo);
|
||||
uint32 OnAllCreatureDealDamage(Creature* me, Unit* pVictim, uint32 damage, DamageEffectType damagetype);
|
||||
};
|
||||
template<> Unit* ALE::CHECKOBJ<Unit>(lua_State* L, int narg, bool error);
|
||||
template<> Object* ALE::CHECKOBJ<Object>(lua_State* L, int narg, bool error);
|
||||
template<> WorldObject* ALE::CHECKOBJ<WorldObject>(lua_State* L, int narg, bool error);
|
||||
template<> ALEObject* ALE::CHECKOBJ<ALEObject>(lua_State* L, int narg, bool error);
|
||||
|
||||
#define sALE ALE::GALE
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
# Ignore the temporary "build" folder.
|
||||
build
|
||||
@@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
.idea
|
||||
@@ -0,0 +1,169 @@
|
||||
import os
|
||||
import shutil
|
||||
import typing
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from typedecorator import params, returns
|
||||
from ALEDoc.parser import ClassParser, MethodDoc
|
||||
import glob
|
||||
import time
|
||||
|
||||
|
||||
@returns([(str, typing.IO)])
|
||||
@params(search_path=str)
|
||||
def find_class_files(search_path):
|
||||
"""Find and open all files containing ALE class methods in `search_path`.
|
||||
|
||||
:param search_path: the path to search for ALE methods in
|
||||
:return: a list of all files containing ALE methods, and the name of their respective classes
|
||||
"""
|
||||
# Search for all files ending in "Methods.h".
|
||||
method_file_names = glob.glob(os.path.join(search_path, '**', '*Methods.h'))
|
||||
# Open each file.
|
||||
method_files = [open(file_name, 'r') for file_name in method_file_names]
|
||||
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."""
|
||||
|
||||
# Set up jinja2 environment to load templates from the templates folder.
|
||||
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') 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
|
||||
|
||||
|
||||
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('ALEDoc/static', 'build/static')
|
||||
|
||||
# Load up all files with methods we need to parse.
|
||||
print('Finding ALE method files...')
|
||||
class_files = find_class_files('../')
|
||||
|
||||
# Parse all the method files.
|
||||
classes = []
|
||||
for f in class_files:
|
||||
print(f'Parsing file {f.name}...')
|
||||
classes.append(ClassParser.parse_file(f))
|
||||
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 a function that parses content for refs to other classes, methods, or enums,
|
||||
and automatically inserts the correct link.
|
||||
"""
|
||||
# Make lists of all class names and method names.
|
||||
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 all occurrencies of &Class:Function and then &Class with a link to given func or class
|
||||
|
||||
for name in method_names:
|
||||
# Take the [] off the front of the method's name.
|
||||
full_name = name[1:-1]
|
||||
# Split "Class:Method" into "Class" and "Method".
|
||||
class_name, method_name = full_name.split(':')
|
||||
url = '{}{}/{}.html'.format(('../' * level), class_name, method_name)
|
||||
# Replace occurrencies of &Class:Method with the url created
|
||||
content = content.replace(name, '<a class="fn" href="{}">{}</a>'.format(url, full_name))
|
||||
|
||||
for name in class_names:
|
||||
# Take the [] off the front of the class's name.
|
||||
class_name = name[1:-1]
|
||||
url = '{}{}/index.html'.format(('../' * level), class_name)
|
||||
# Replace occurrencies of &Class:Method with the url created
|
||||
content = content.replace(name, '<a class="mod" href="{}">{}</a>'.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 '<strong><a href="{}">{}</a></strong>'.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 '<strong><a class="mod" href="{}">{}</a></strong>'.format(url, class_name)
|
||||
|
||||
# Case for enums to direct to a search on github
|
||||
enum_name = content[1:-1]
|
||||
url = 'https://github.com/azerothcore/azerothcore-wotlk/search?l=cpp&q=%22enum+{}%22&type=Code&utf8=%E2%9C%93'.format(enum_name)
|
||||
return '<strong><a href="{}">{}</a></strong>'.format(url, enum_name)
|
||||
|
||||
# By default we just return the name without the [] around it
|
||||
return content[1:-1]
|
||||
|
||||
return link_parser, data_type_parser
|
||||
|
||||
# Create the render function with the template path and parser maker.
|
||||
render = make_renderer('ALEDoc/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)
|
||||
@@ -0,0 +1,334 @@
|
||||
import os
|
||||
import re
|
||||
import typing
|
||||
import markdown
|
||||
from typedecorator import params, returns, Nullable
|
||||
|
||||
|
||||
class ParameterDoc(object):
|
||||
"""The documentation data of a parameter or return value for an ALE 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'), # This should be -32767..32767, but it's pretty safe to assume 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 += '<p><em>Valid numbers</em>: integers from {0} to {1}.</p>'.format(range[0], range[1])
|
||||
else:
|
||||
self.description += '<p><em>Valid numbers</em>: all decimal numbers.</p>'
|
||||
|
||||
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 not self.data_type 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}`")
|
||||
|
||||
|
||||
class MethodDoc(object):
|
||||
"""The documentation data of an ALE method."""
|
||||
@params(self=object, name=str, description=str, prototypes=[str], parameters=[ParameterDoc], returned=[ParameterDoc])
|
||||
def __init__(self, name, description, prototypes, parameters, returned):
|
||||
self.name = name
|
||||
self.prototypes = prototypes
|
||||
self.parameters = parameters
|
||||
self.returned = returned
|
||||
|
||||
# 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('</p>')[0][3:]
|
||||
# If it has a description, it is "documented".
|
||||
self.documented = self.description != ''
|
||||
|
||||
|
||||
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('</p>')[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
|
||||
|
||||
# In the same vein, if any of our methods are documented, we aren't fully *un*documented.
|
||||
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."""
|
||||
|
||||
# Various regular expressions to parse different parts of the doc string.
|
||||
# There are used to parse the class's description.
|
||||
class_start_regex = re.compile(r"\s*/\*\*\*") # The start of class documentation, i.e. /***
|
||||
class_body_regex = re.compile(r"\s*\*\s*(.*)") # The "body", i.e. a * and optionally some descriptive text.
|
||||
class_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */
|
||||
|
||||
# These are used to parse method documentation.
|
||||
start_regex = re.compile(r"\s*/\*\*") # The start of documentation, i.e. /**
|
||||
body_regex = re.compile(r"\s*\s?\*\s?(.*)") # The "body", i.e. a * and optionally some descriptive text.
|
||||
# An extra optional space (\s?) was thrown in to make it different from `class_body_regex`.
|
||||
|
||||
param_regex = re.compile(r"""\s*\*\s@param\s # The @param tag starts with opt. whitespace followed by "* @param ".
|
||||
([^\s]+)\s(\w+)? # The data type, a space, and the name of the param.
|
||||
(?:\s=\s(\w+))? # The default value: a = surrounded by spaces, followed by text.
|
||||
(?:\s:\s(.+))? # The description: a colon surrounded by spaces, followed by text.
|
||||
""", re.X)
|
||||
# This is the same as the @param tag, minus the default value part.
|
||||
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,]+)? # The list of arguments.
|
||||
(?:=\s)? # An equals sign and a space separate the args and returns.
|
||||
(?:\(([\w\s,]+)\))? # The list of return values, in parens.
|
||||
""", re.X)
|
||||
|
||||
comment_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */
|
||||
end_regex = re.compile(r"\s*int\s(\w+)\s*\(") # The end of the documentation, i.e. int MethodName(
|
||||
|
||||
def __init__(self, class_name):
|
||||
assert ClassParser.class_body_regex is not ClassParser.body_regex
|
||||
# The methods that have been parsed.
|
||||
self.methods = []
|
||||
# The name of the class being parsed.
|
||||
self.class_name = class_name
|
||||
# The description of the class being parsed.
|
||||
self.class_description = ''
|
||||
# Reset the parser's state machine.
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
# What the last handled regex was, to determine what the next should be.
|
||||
self.last_regex = None
|
||||
|
||||
# These are used to piece together the next `Method`.
|
||||
self.description = ''
|
||||
self.params = []
|
||||
self.returned = []
|
||||
self.method_name = None
|
||||
self.prototypes = []
|
||||
|
||||
def handle_class_body(self, match):
|
||||
text = match.group(1)
|
||||
self.class_description += text + '\n'
|
||||
|
||||
def handle_body(self, match):
|
||||
text = match.group(1)
|
||||
self.description += text + '\n'
|
||||
|
||||
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):
|
||||
if parameters != '':
|
||||
parameters = ' ' + parameters + ' '
|
||||
|
||||
if self.class_name == 'Global':
|
||||
if self.returned:
|
||||
return_values = ', '.join([param.name for param in self.returned])
|
||||
prototype = '{0} = {1}({2})'.format(return_values, self.method_name, parameters)
|
||||
else:
|
||||
prototype = '{0}({1})'.format(self.method_name, parameters)
|
||||
else:
|
||||
if self.returned:
|
||||
return_values = ', '.join([param.name for param in self.returned])
|
||||
prototype = '{0} = {1}:{2}({3})'.format(return_values, self.class_name, self.method_name, parameters)
|
||||
else:
|
||||
prototype = '{0}:{1}({2})'.format(self.class_name, self.method_name, parameters)
|
||||
|
||||
return prototype
|
||||
|
||||
# If there's no prototype, make one with all params and returns.
|
||||
if not self.prototypes:
|
||||
# A list of all parameters with default values.
|
||||
params_with_default = []
|
||||
# The index of the last non-default parameter.
|
||||
last_non_default_i = 0
|
||||
# If False, a parameter WITHOUT a default value follows one WITH a default value.
|
||||
# In this case, don't bother generating prototypes.
|
||||
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:
|
||||
# Just generate one prototype with all the parameters.
|
||||
parameters = ', '.join([param.name for param in self.params])
|
||||
self.prototypes.append(make_prototype(parameters))
|
||||
else:
|
||||
# Generate a prototype for all the non-default parameters,
|
||||
# then one for each default parameter with all the previous parameters.
|
||||
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:
|
||||
# Format the method name into each prototype.
|
||||
self.prototypes = [proto.format(self.method_name) for proto in self.prototypes]
|
||||
|
||||
self.methods.append(MethodDoc(self.method_name, self.description, self.prototypes, self.params, self.returned))
|
||||
|
||||
# Table of which handler is used to handle each regular expressions.
|
||||
regex_handlers = {
|
||||
class_start_regex: None,
|
||||
class_body_regex: handle_class_body,
|
||||
class_end_regex: None,
|
||||
start_regex: None,
|
||||
body_regex: handle_body,
|
||||
param_regex: handle_param,
|
||||
return_regex: handle_return,
|
||||
proto_regex: handle_proto,
|
||||
comment_end_regex: None,
|
||||
end_regex: handle_end,
|
||||
}
|
||||
|
||||
# Table of which regular expressions can follow the last handled regex.
|
||||
# `body_regex` must always come LAST when used, since it also matches param, return, and comment_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: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex],
|
||||
body_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex],
|
||||
proto_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex],
|
||||
param_regex: [param_regex, return_regex, comment_end_regex, body_regex],
|
||||
return_regex: [return_regex, comment_end_regex],
|
||||
comment_end_regex: [end_regex],
|
||||
end_regex: [],
|
||||
}
|
||||
|
||||
@returns(Nullable(MethodDoc))
|
||||
@params(self=object, line=str)
|
||||
def next_line(self, line):
|
||||
"""Parse the next line of the file.
|
||||
|
||||
This method returns a `Method` when enough data to form a `Method` has been parsed.
|
||||
Otherwise, it returns None.
|
||||
"""
|
||||
# Get the list of expected regular expressions using the last one handled.
|
||||
valid_regexes = self.next_regexes[self.last_regex]
|
||||
|
||||
# Try to find a match.
|
||||
for regex in valid_regexes:
|
||||
match = regex.match(line)
|
||||
|
||||
if match:
|
||||
handler = self.regex_handlers[regex]
|
||||
|
||||
if handler:
|
||||
handler(self, match)
|
||||
|
||||
# Not every regex has a handler, but keep track of where we are anyway.
|
||||
self.last_regex = regex
|
||||
# Break at the first match.
|
||||
break
|
||||
else:
|
||||
# No valid regex was found, reset everything.
|
||||
self.reset()
|
||||
|
||||
@returns(MangosClassDoc)
|
||||
def to_class_doc(self):
|
||||
"""Create an instance of `MangosClassDoc` from the parser's data.
|
||||
|
||||
Is called by `parse_file` once parsing is finished.
|
||||
"""
|
||||
return MangosClassDoc(self.class_name, self.class_description, self.methods)
|
||||
|
||||
@staticmethod
|
||||
@returns(MangosClassDoc)
|
||||
@params(file=typing.IO)
|
||||
def parse_file(file):
|
||||
"""Parse the file `file` into a documented class."""
|
||||
# Get the class name from "ClassMethods.h" by stripping off "Methods.h".
|
||||
class_name = os.path.basename(file.name)[:-len('Methods.h')]
|
||||
parser = ClassParser(class_name)
|
||||
|
||||
line = file.readline()
|
||||
|
||||
while line:
|
||||
parser.next_line(line)
|
||||
line = file.readline()
|
||||
|
||||
return parser.to_class_doc()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
_______
|
||||
/ \
|
||||
.==. .==.
|
||||
(( ))==(( ))
|
||||
/ "==" "=="\
|
||||
/____|| || ||___\
|
||||
________ ____ ________ ___ ___
|
||||
| ___ \ / \ | ___ \ | | / /
|
||||
| | \ \ / /\ \ | | \ \| |_/ /
|
||||
| | ) / /__\ \ | |__/ /| ___ \
|
||||
| |__/ / ______ \| ____ \| | \ \
|
||||
_______|_______/__/ ____ \__\__|___\__\__|___\__\____
|
||||
| ___ \ | ____/ / \ | ___ \ | ____| ___ \
|
||||
| | \ \| |___ / /\ \ | | \ \| |___| | \ \
|
||||
| |__/ /| ____/ /__\ \ | | ) | ____| |__/ /
|
||||
| ____ \| |__/ ______ \| |__/ /| |___| ____ \
|
||||
|__| \__\____/__/ \__\_______/ |______|__| \__\
|
||||
https://darkreader.org
|
||||
*/
|
||||
|
||||
/*! Dark reader generated CSS | Licensed under MIT https://github.com/darkreader/darkreader/blob/main/LICENSE */
|
||||
|
||||
/* User-Agent Style */
|
||||
html {
|
||||
background-color: #181a1b !important;
|
||||
}
|
||||
html {
|
||||
color-scheme: dark !important;
|
||||
}
|
||||
html, body, input, textarea, select, button, dialog {
|
||||
background-color: #181a1b;
|
||||
}
|
||||
html, body, input, textarea, select, button {
|
||||
border-color: #736b5e;
|
||||
color: #e8e6e3;
|
||||
}
|
||||
a {
|
||||
color: #3391ff;
|
||||
}
|
||||
table {
|
||||
border-color: #545b5e;
|
||||
}
|
||||
::placeholder {
|
||||
color: #b2aba1;
|
||||
}
|
||||
input:-webkit-autofill,
|
||||
textarea:-webkit-autofill,
|
||||
select:-webkit-autofill {
|
||||
background-color: #404400 !important;
|
||||
color: #e8e6e3 !important;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
background-color: #202324;
|
||||
color: #aba499;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #454a4d;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #575e62;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background-color: #484e51;
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: #181a1b;
|
||||
}
|
||||
::selection {
|
||||
background-color: #004daa !important;
|
||||
color: #e8e6e3 !important;
|
||||
}
|
||||
::-moz-selection {
|
||||
background-color: #004daa !important;
|
||||
color: #e8e6e3 !important;
|
||||
}
|
||||
|
||||
/* Invert Style */
|
||||
.jfk-bubble.gtx-bubble, .captcheck_answer_label > input + img, span#closed_text > img[src^="https://www.gstatic.com/images/branding/googlelogo"], span[data-href^="https://www.hcaptcha.com/"] > #icon, #bit-notification-bar-iframe, ::-webkit-calendar-picker-indicator {
|
||||
filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
|
||||
}
|
||||
|
||||
/* Variables Style */
|
||||
:root {
|
||||
--darkreader-neutral-background: #131516;
|
||||
--darkreader-neutral-text: #d8d4cf;
|
||||
--darkreader-selection-background: #004daa;
|
||||
--darkreader-selection-text: #e8e6e3;
|
||||
}
|
||||
|
||||
/* Modified CSS */
|
||||
body {
|
||||
color: rgb(200, 195, 188);
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3:not(.impl):not(.method),
|
||||
h4:not(.method) {
|
||||
color: rgb(232, 230, 227);
|
||||
}
|
||||
h1.fqn {
|
||||
border-bottom-color: rgb(60, 65, 67);
|
||||
}
|
||||
h2,
|
||||
h3:not(.impl):not(.method),
|
||||
h4:not(.method) {
|
||||
border-bottom-color: rgb(58, 62, 65);
|
||||
}
|
||||
.docblock code {
|
||||
background-color: rgb(30, 32, 33);
|
||||
}
|
||||
pre {
|
||||
background-color: rgb(30, 32, 33);
|
||||
}
|
||||
.sidebar .location {
|
||||
background-image: initial;
|
||||
background-color: rgb(41, 44, 46);
|
||||
color: rgb(200, 195, 188);
|
||||
}
|
||||
.block a:hover {
|
||||
background-image: initial;
|
||||
background-color: rgb(30, 32, 33);
|
||||
}
|
||||
.content pre.line-numbers {
|
||||
border-color: initial;
|
||||
}
|
||||
.line-numbers span {
|
||||
color: rgb(214, 149, 75);
|
||||
}
|
||||
.line-numbers .line-highlighted {
|
||||
background-color: rgb(68, 73, 2);
|
||||
}
|
||||
.content .highlighted {
|
||||
background-color: rgb(53, 57, 59);
|
||||
color: rgb(232, 230, 227) !important;
|
||||
}
|
||||
.content .highlighted a {
|
||||
color: rgb(232, 230, 227) !important;
|
||||
}
|
||||
.content .highlighted.trait {
|
||||
background-color: rgb(128, 80, 1);
|
||||
}
|
||||
.content .highlighted.mod {
|
||||
background-color: rgb(29, 54, 86);
|
||||
}
|
||||
.content .highlighted.enum {
|
||||
background-color: rgb(47, 77, 58);
|
||||
}
|
||||
.content .highlighted.struct {
|
||||
background-color: rgb(98, 42, 25);
|
||||
}
|
||||
.content .highlighted.fn {
|
||||
background-color: rgb(78, 55, 59);
|
||||
}
|
||||
.docblock h1,
|
||||
.docblock h2,
|
||||
.docblock h3,
|
||||
.docblock h4,
|
||||
.docblock h5 {
|
||||
border-bottom-color: rgb(58, 62, 65);
|
||||
}
|
||||
nav {
|
||||
border-bottom-color: rgb(57, 61, 64);
|
||||
}
|
||||
nav.main .current {
|
||||
border-top-color: rgb(140, 130, 115);
|
||||
border-bottom-color: rgb(140, 130, 115);
|
||||
}
|
||||
nav.main .separator {
|
||||
border-color: rgb(140, 130, 115);
|
||||
}
|
||||
a {
|
||||
text-decoration-color: initial;
|
||||
color: rgb(232, 230, 227);
|
||||
background-image: initial;
|
||||
background-color: transparent;
|
||||
}
|
||||
p a {
|
||||
color: rgb(92, 155, 206);
|
||||
}
|
||||
p a:hover {
|
||||
text-decoration-color: initial;
|
||||
}
|
||||
.content a.trait,
|
||||
.block a.current.trait {
|
||||
color: rgb(252, 173, 39);
|
||||
}
|
||||
.content a.mod,
|
||||
.block a.current.mod {
|
||||
color: rgb(115, 156, 193);
|
||||
}
|
||||
.content a.enum,
|
||||
.block a.current.enum {
|
||||
color: rgb(155, 146, 133);
|
||||
}
|
||||
.content a.struct,
|
||||
.block a.current.struct {
|
||||
color: rgb(255, 94, 44);
|
||||
}
|
||||
.content a.fn,
|
||||
.block a.current.fn {
|
||||
color: rgb(157, 149, 136);
|
||||
}
|
||||
.content .fnname {
|
||||
color: rgb(157, 149, 136);
|
||||
}
|
||||
.search-input {
|
||||
outline-color: initial;
|
||||
border-color: initial;
|
||||
color: rgb(178, 172, 162);
|
||||
box-shadow: rgb(42, 45, 47) 0px 0px 0px 1px,
|
||||
rgba(0, 0, 0, 0) 0px 0px 0px 2px;
|
||||
}
|
||||
.search-input:focus {
|
||||
border-color: initial;
|
||||
outline-color: initial;
|
||||
box-shadow: rgb(6, 113, 173) 0px 0px 8px;
|
||||
}
|
||||
#help {
|
||||
background-image: initial;
|
||||
background-color: rgb(36, 39, 41);
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 6px;
|
||||
border-color: rgb(66, 72, 74);
|
||||
}
|
||||
#help dt {
|
||||
border-color: rgb(66, 72, 74);
|
||||
background-image: initial;
|
||||
background-color: rgb(24, 26, 27);
|
||||
}
|
||||
.stability {
|
||||
border-left-color: initial;
|
||||
}
|
||||
.stability.Deprecated {
|
||||
border-color: rgb(100, 65, 106);
|
||||
color: rgb(179, 123, 188);
|
||||
}
|
||||
.stability.Experimental {
|
||||
border-color: rgb(124, 38, 36);
|
||||
color: rgb(202, 101, 98);
|
||||
}
|
||||
.stability.Unstable {
|
||||
border-color: rgb(124, 95, 36);
|
||||
color: rgb(202, 167, 98);
|
||||
}
|
||||
.stability.Stable {
|
||||
border-color: rgb(60, 120, 64);
|
||||
color: rgb(123, 211, 128);
|
||||
}
|
||||
.stability.Frozen {
|
||||
border-color: rgb(0, 211, 70);
|
||||
color: rgb(121, 255, 164);
|
||||
}
|
||||
.stability.Locked {
|
||||
border-color: rgb(0, 145, 200);
|
||||
color: rgb(106, 215, 255);
|
||||
}
|
||||
.stability.Unmarked {
|
||||
border-color: rgb(67, 73, 76);
|
||||
}
|
||||
.summary.Deprecated {
|
||||
background-color: rgb(110, 72, 117);
|
||||
}
|
||||
.summary.Experimental {
|
||||
background-color: rgb(129, 40, 37);
|
||||
}
|
||||
.summary.Unstable {
|
||||
background-color: rgb(97, 74, 28);
|
||||
}
|
||||
.summary.Stable {
|
||||
background-color: rgb(67, 134, 71);
|
||||
}
|
||||
.summary.Unmarked {
|
||||
background-color: rgb(62, 68, 70);
|
||||
}
|
||||
:target {
|
||||
background-image: initial;
|
||||
background-color: rgb(56, 58, 0);
|
||||
}
|
||||
pre.rust .kw {
|
||||
color: rgb(149, 105, 177);
|
||||
}
|
||||
pre.rust .kw-2,
|
||||
pre.rust .prelude-ty {
|
||||
color: rgb(114, 160, 201);
|
||||
}
|
||||
pre.rust .number,
|
||||
pre.rust .string {
|
||||
color: rgb(226, 255, 106);
|
||||
}
|
||||
pre.rust .self,
|
||||
pre.rust .boolval,
|
||||
pre.rust .prelude-val,
|
||||
pre.rust .attribute,
|
||||
pre.rust .attribute .ident {
|
||||
color: rgb(219, 73, 73);
|
||||
}
|
||||
pre.rust .comment {
|
||||
color: rgb(161, 152, 140);
|
||||
}
|
||||
pre.rust .doccomment {
|
||||
color: rgb(184, 178, 168);
|
||||
}
|
||||
pre.rust .macro,
|
||||
pre.rust .macro-nonterminal {
|
||||
color: rgb(107, 192, 197);
|
||||
}
|
||||
pre.rust .lifetime {
|
||||
color: rgb(236, 158, 81);
|
||||
}
|
||||
.methods .section-header {
|
||||
border-bottom-color: initial !important;
|
||||
}
|
||||
.collapse-toggle {
|
||||
color: rgb(168, 160, 149);
|
||||
}
|
||||
.toggle-label {
|
||||
color: rgb(168, 160, 149);
|
||||
}
|
||||
|
||||
/* Override Style */
|
||||
.vimvixen-hint {
|
||||
background-color: #7b5300 !important;
|
||||
border-color: #d8b013 !important;
|
||||
color: #f3e8c8 !important;
|
||||
}
|
||||
::placeholder {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
#edge-translate-panel-body,
|
||||
.MuiTypography-body1,
|
||||
.nfe-quote-text {
|
||||
color: var(--darkreader-neutral-text) !important;
|
||||
}
|
||||
gr-main-header {
|
||||
background-color: #0f3a48 !important;
|
||||
}
|
||||
.tou-z65h9k,
|
||||
.tou-mignzq,
|
||||
.tou-1b6i2ox,
|
||||
.tou-lnqlqk {
|
||||
background-color: var(--darkreader-neutral-background) !important;
|
||||
}
|
||||
.tou-75mvi {
|
||||
background-color: #032029 !important;
|
||||
}
|
||||
.tou-ta9e87,
|
||||
.tou-1w3fhi0,
|
||||
.tou-1b8t2us,
|
||||
.tou-py7lfi,
|
||||
.tou-1lpmd9d,
|
||||
.tou-1frrtv8,
|
||||
.tou-17ezmgn {
|
||||
background-color: #0a0a0a !important;
|
||||
}
|
||||
.tou-uknfeu {
|
||||
background-color: #231603 !important;
|
||||
}
|
||||
.tou-6i3zyv {
|
||||
background-color: #19576c !important;
|
||||
}
|
||||
embed[type="application/pdf"][src="about:blank"] { filter: invert(100%) contrast(90%); }
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
File diff suppressed because one or more lines are too long
@@ -0,0 +1,520 @@
|
||||
/**
|
||||
* 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 <LICENSE-APACHE or
|
||||
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 */
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
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;
|
||||
}
|
||||
|
||||
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: black;
|
||||
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: #F5F5F5;
|
||||
border-radius: 3px;
|
||||
/*padding: 0 0.2em;*/
|
||||
}
|
||||
pre {
|
||||
background-color: #F5F5F5;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.content, nav { max-width: 960px; }
|
||||
|
||||
/* 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: #F5F5F5;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.content .highlighted {
|
||||
cursor: pointer;
|
||||
color: #000 !important;
|
||||
background-color: #ccc;
|
||||
}
|
||||
.content .highlighted a { color: #000 !important; }
|
||||
.content .highlighted.trait { background-color: #fece7e; }
|
||||
.content .highlighted.mod { background-color: #afc6e4; }
|
||||
.content .highlighted.enum { background-color: #b4d1b9; }
|
||||
.content .highlighted.struct { background-color: #e7b1a0; }
|
||||
.content .highlighted.fn { background-color: #c6afb3; }
|
||||
|
||||
.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.sub form { display: inline; }
|
||||
|
||||
nav, .content {
|
||||
margin-left: 230px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
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: #8c6067; }
|
||||
.content .fnname { color: #8c6067; }
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
/* 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: #555;
|
||||
margin-top: 5px;
|
||||
padding: 10px 16px;
|
||||
font-size: 17px;
|
||||
box-shadow: 0 0 0 1px #e0e0e0, 0 0 0 2px transparent;
|
||||
transition: border-color 300ms ease;
|
||||
transition: border-radius 300ms ease-in-out;
|
||||
transition: box-shadow 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: #66afe9;
|
||||
border-radius: 2px;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
box-shadow: 0 0 8px #078dd8;
|
||||
}
|
||||
|
||||
.search-results .desc {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#help {
|
||||
background: #e9e9e9;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 6px rgba(0,0,0,.2);
|
||||
position: absolute;
|
||||
top: 300px;
|
||||
left: 50%;
|
||||
margin-top: -125px;
|
||||
margin-left: -275px;
|
||||
width: 550px;
|
||||
height: 300px;
|
||||
border: 1px solid #bfbfbf;
|
||||
}
|
||||
|
||||
#help dt {
|
||||
float: left;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #bfbfbf;
|
||||
background: #fff;
|
||||
width: 23px;
|
||||
text-align: center;
|
||||
clear: left;
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
}
|
||||
#help dd { margin: 5px 33px; }
|
||||
#help .infos { padding-left: 0; }
|
||||
#help h1 { margin-top: 0; }
|
||||
#help div {
|
||||
width: 50%;
|
||||
float: left;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
nav.sub {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -0,0 +1,782 @@
|
||||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
|
||||
}).on('mouseover', function() {
|
||||
var $el = $(this);
|
||||
clearTimeout(hoverTimeout);
|
||||
hoverTimeout = setTimeout(function() {
|
||||
$results.removeClass('highlighted');
|
||||
$el.addClass('highlighted');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
$(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 $('<h1/>').text(content).html();
|
||||
}
|
||||
|
||||
function showResults(results) {
|
||||
var output, shown, query = getQuery();
|
||||
|
||||
currentResults = query.id;
|
||||
output = '<h1>Results for ' + escape(query.query) +
|
||||
(query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>';
|
||||
output += '<table class="search-results">';
|
||||
|
||||
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 += '<tr class="' + type + ' result"><td>';
|
||||
|
||||
if (type === 'mod') {
|
||||
output += item.path +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') + // '/' +
|
||||
name + '/index.html" class="' +
|
||||
type + '">' + name + '</a>';
|
||||
} else if (type === 'static' || type === 'reexport') {
|
||||
output += item.path +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') +
|
||||
'/index.html" class="' + type +
|
||||
'">' + name + '</a>';
|
||||
} else if (item.parent !== undefined) {
|
||||
var myparent = item.parent;
|
||||
var anchor = '#' + type + '.' + name;
|
||||
output += item.path + '::' + myparent.name +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') +
|
||||
'/' + myparent.name +
|
||||
'.html' + anchor +
|
||||
'" class="' + type +
|
||||
'">' + name + '</a>';
|
||||
} else {
|
||||
output += item.path +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') +
|
||||
'/' + name +
|
||||
'.html" class="' + type +
|
||||
'">' + name + '</a>';
|
||||
}
|
||||
|
||||
output += '</td><td><span class="desc">' + item.desc +
|
||||
'</span></td></tr>';
|
||||
});
|
||||
} else {
|
||||
output += 'No results - Request function at <a href="https://github.com/azerothcore/mod-ale/issues">ALE issue tracker</a>';
|
||||
}
|
||||
|
||||
output += "</p>";
|
||||
$('#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 = $('<div>').attr('class', 'block crate');
|
||||
div.append($('<h2>').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($('<a>', {'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 = $('<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 = $('<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 = "<a href='javascript:void(0)'"
|
||||
+ "class='collapse-toggle'>[<span class='inner'>-</span>]</a>";
|
||||
|
||||
$(".method").each(function() {
|
||||
if ($(this).next().is(".docblock")) {
|
||||
$(this).children().first().after(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
var mainToggle = $(toggle);
|
||||
mainToggle.append("<span class='toggle-label' style='display:none'>"
|
||||
+ " Expand description</span></a>")
|
||||
var wrapper = $("<div class='toggle-wrapper'>");
|
||||
wrapper.append(mainToggle);
|
||||
$("#main > .docblock").before(wrapper);
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -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}
|
||||
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{% block description %}API documentation for the ALE engine.{% endblock %}">
|
||||
<meta name="keywords" content="ALE, lua, lua engine, azerothcore, script, scripting, doc, docs, documentation">
|
||||
|
||||
<title>{% block title %}ALE API{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('main.css') }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('dark.css') }}">
|
||||
|
||||
<link rel="shortcut icon" href="{{ static('favicon.ico') }}">
|
||||
|
||||
</head>
|
||||
<body class="rustdoc">
|
||||
<!--[if lte IE 8]>
|
||||
<div class="warning">
|
||||
This old browser is unsupported and will most likely display funky
|
||||
things.
|
||||
</div>
|
||||
<![endif]-->
|
||||
|
||||
<section class="sidebar">
|
||||
<a href='{{ root('index.html') }}'><img src='{{ static('eluna-logo.png') }}' alt='ALE Logo' width='100'></a>
|
||||
<div class="block">
|
||||
{% block sidebar %}
|
||||
<h2>All Classes</h2>
|
||||
{% for class in classes -%}
|
||||
<a class="mod {{ 'current' if class == current_class }}" href="{{ root(class.name + '/index.html') }}">{{ class.name }}</a>
|
||||
{%- endfor %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<nav class="sub">
|
||||
<form class="search-form js-only">
|
||||
<div class="search-container">
|
||||
<input class="search-input" name="search"
|
||||
autocomplete="off"
|
||||
placeholder="Click or press 'S' to search, '?' for more options..."
|
||||
type="search">
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<section id='main' class="content mod">
|
||||
<h1 class='fqn'>
|
||||
{% block document_title %}Title Missing{% endblock %}
|
||||
<span class='out-of-band'>
|
||||
<span id='render-detail'>
|
||||
<a id="collapse-all" href="#">[-]</a>
|
||||
<a id="expand-all" href="#">[+]</a>
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
{% block content %}<h2>Content missing.</h2>{% endblock %}
|
||||
</section>
|
||||
|
||||
<section id='search' class="content hidden"></section>
|
||||
|
||||
<section class="footer"></section>
|
||||
|
||||
<div id="help" class="hidden">
|
||||
<div class="shortcuts">
|
||||
<h1>Keyboard shortcuts</h1>
|
||||
<dl>
|
||||
<dt>?</dt>
|
||||
<dd>Show this help dialog</dd>
|
||||
<dt>S</dt>
|
||||
<dd>Focus the search field</dd>
|
||||
<dt>⇤</dt>
|
||||
<dd>Move up in search results</dd>
|
||||
<dt>⇥</dt>
|
||||
<dd>Move down in search results</dd>
|
||||
<dt>⏎</dt>
|
||||
<dd>Go to active search result</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="infos">
|
||||
<h1>Search tricks</h1>
|
||||
<p>
|
||||
Prefix searches with a type followed by a colon (e.g.
|
||||
<code>fn:</code>) to restrict the search to a given type.
|
||||
</p>
|
||||
<p>
|
||||
Accepted types are: <code>fn</code>, <code>mod</code>,
|
||||
<code>struct</code> (or <code>str</code>), <code>enum</code>,
|
||||
<code>trait</code>, <code>typedef</code> (or
|
||||
<code>tdef</code>).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.rootPath = "{{ ('../' * level) if level }}";
|
||||
</script>
|
||||
<script src="{{ static('jquery.js') }}"></script>
|
||||
<script src="{{ static('main.js') }}"></script>
|
||||
<script async src="{{ root('search-index.js') }}"></script>
|
||||
<center>Generated on <script src="{{ root('date.js') }}"></script></center>
|
||||
<center>©2026 - ALE Lua Engine</center>
|
||||
</body>
|
||||
</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 ALE engine.
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block document_title -%}
|
||||
Class <a class="mod" href="">{{ current_class.name }}</a>
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block sidebar %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{{ current_class.description|parse_links }}
|
||||
|
||||
<h2 id='methods' class='section-header'><a href="#methods">Methods</a></h2>
|
||||
<table>
|
||||
{%- for method in current_class.methods %}
|
||||
<tr>
|
||||
<td>
|
||||
<a class='stability {{ 'Stable' if method.documented else 'Experimental' }}' title='{{ 'Documented' if method.documented else 'Undocumented' }}'></a>
|
||||
<a class='fn' href='{{ method.name }}.html'>{{ method.name }}</a>
|
||||
</td>
|
||||
<td class='docblock short'>
|
||||
<p>{{ method.short_description|parse_links }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
document.write("{{ currdate }}");
|
||||
@@ -0,0 +1 @@
|
||||
{% extends '_base.html' %}
|
||||
@@ -0,0 +1,87 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
|
||||
{% block document_title -%}
|
||||
ALE API Documentation
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class='docblock'>
|
||||
<h1 id="the-ale-engine-api" class='section-header'>
|
||||
<a href="#the-ale-engine-api">The ALE Lua Engine© API</a>
|
||||
</h1>
|
||||
<p>
|
||||
The ALE Lua Engine© API allows you to add your own Lua code to be executed when certain events (called "hooks") occur.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h2 id="about-ale" class='section-header'>
|
||||
<a href="#about-ale">About ALE</a>
|
||||
</h2>
|
||||
<p>
|
||||
ALE is a <a href="http://www.lua.org/">Lua</a> engine for World of Warcraft emulators.
|
||||
These pages are for <a href="https://www.azerothcore.org/">AzerothCore</a>'s version, but ALE also supports
|
||||
<a href="https://cmangos.net/">CMaNGOS</a>/<a href="https://www.getmangos.eu/">MaNGOS</a> and <a href="https://www.trinitycore.org/">TrinityCore</a>.
|
||||
</p>
|
||||
<p>
|
||||
If you come from the TypeScript / JavaScript world, or would prefer to use a typed language instead of Lua, check out <a href="https://github.com/azerothcore/eluna-ts">ale-ts</a>!
|
||||
</p>
|
||||
<p>
|
||||
You can get support in the <code>#ale-ac</code> channel of <a href="https://discord.com/invite/sqkPb623">AzerothCore's Discord server</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="installation" class='section-header'>
|
||||
<a href="#installation">How to Install</a>
|
||||
</h2>
|
||||
<p>
|
||||
<ol>
|
||||
<li>If you haven't already, clone AzerothCore from <a href="https://github.com/azerothcore/azerothcore-wotlk">our GitHub repository</a></li>
|
||||
<li>Go to the modules directory and run the following command: <pre>git clone https://github.com/azerothcore/mod-ale.git mod-ale</pre></li>
|
||||
<li>Run CMake</li>
|
||||
<li>Build AzerothCore</li>
|
||||
</ol>
|
||||
</p>
|
||||
|
||||
<!-- <h2 id="tutorials-and-guides" class='section-header'>
|
||||
<a href="#tutorials-and-guides">Tutorials & Guides</a>
|
||||
</h2>
|
||||
<p>
|
||||
We haven't written tutorials yet, but when we do, we'll put the links here.
|
||||
</p> -->
|
||||
|
||||
<h2 id="about-this-documentation" class='section-header'>
|
||||
<a href="#about-this-documentation">About this Documentation</a>
|
||||
</h2>
|
||||
<p>
|
||||
The layout, CSS, and Javascript code for this documentation was borrowed from <a href="http://doc.rust-lang.org/">doc.rust-lang.org</a>.
|
||||
</p>
|
||||
<p>
|
||||
The documentation generator was originally written by <a href="https://github.com/Patman64">Patman64</a> and is maintained by the ALE team.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 id='modules' class='section-header'><a href="#modules">Classes</a></h2>
|
||||
<table>
|
||||
{%- for class in classes %}
|
||||
<tr>
|
||||
<td>
|
||||
{%- if class.fully_documented %}
|
||||
<a class='stability Stable' title='Fully Documented'></a>
|
||||
{%- elif class.fully_undocumented %}
|
||||
<a class='stability Experimental' title='Fully Undocumented'></a>
|
||||
{%- else %}
|
||||
<a class='stability Unstable' title='Partially Documented'></a>
|
||||
{%- endif %}
|
||||
<a class='mod' href='{{ root(class.name + '/index.html') }}'>{{ class.name }}</a>
|
||||
</td>
|
||||
<td class='docblock short'>
|
||||
<p>{{ class.short_description|parse_links }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,95 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
|
||||
{% block title -%}
|
||||
{{ current_class.name }}:{{ current_method.name }} - ALE
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block description -%}
|
||||
API documentation for the {{ current_class.name }}:{{ current_method.name }} method in the ALE engine.
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block document_title -%}
|
||||
Method
|
||||
<a class="mod" href="{{ root(current_class.name + '/index.html') }}">
|
||||
{{- current_class.name -}}
|
||||
</a>:<a class="fn" href="{{ root(current_class.name + '/' + current_method.name + '.html') }}">
|
||||
{{- current_method.name -}}
|
||||
</a>
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block sidebar %}
|
||||
<h2>{{ current_class.name }} Methods</h2>
|
||||
<script src="sidebar.js"></script>
|
||||
<script>
|
||||
// Highlight current method by adding "current" class to it
|
||||
var element = document.getElementById("{{ current_class.name + ':' + current_method.name }}");
|
||||
if (element) {
|
||||
var classname = "current";
|
||||
arr = element.className.split(" ");
|
||||
if (arr.indexOf(classname) == -1) {
|
||||
element.className += " " + classname;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class='docblock'>
|
||||
{%- if current_method.documented %}
|
||||
{{ current_method.description|parse_links }}
|
||||
{%- else %}
|
||||
<p>This method is <em>undocumented</em>. <strong>Use at your own risk.</strong></p>
|
||||
<p>For temporary documentation, please check the <a href="https://github.com/azerothcore/mod-ale/blob/master/src/LuaEngine/LuaFunctions.cpp">LuaFunctions</a> source file.</p>
|
||||
{%- endif %}
|
||||
|
||||
<h2 id="synopsis" class='section-header'>
|
||||
<a href="#synopsis">Synopsis</a>
|
||||
</h2>
|
||||
{%- for prototype in current_method.prototypes %}
|
||||
<p>
|
||||
<code>{{ prototype }}</code>
|
||||
</p>
|
||||
{%- endfor %}
|
||||
|
||||
<h2 id="arguments" class='section-header'>
|
||||
<a href="#arguments">Arguments</a>
|
||||
</h2>
|
||||
<p>
|
||||
{%- if current_method.parameters|length > 0 %}
|
||||
{%- for param in current_method.parameters %}
|
||||
<dl>
|
||||
<dt><code>{{ param.data_type|escape|parse_data_type }} {{ param.name if param.data_type != '...' }} {{- ' (' + param.default_value + ')' if param.default_value }}</code></dt>
|
||||
<dd class="docblock">{{ param.description|parse_links if param.description else '<em>See method description.</em>' }}</dd>
|
||||
</dl>
|
||||
{%- endfor %}
|
||||
{%- elif not current_method.documented %}
|
||||
Unknown.
|
||||
{%- else %}
|
||||
None.
|
||||
{%- endif %}
|
||||
</p>
|
||||
|
||||
<h2 id="returns" class='section-header'>
|
||||
<a href="#returns">Returns</a>
|
||||
</h2>
|
||||
<p>
|
||||
{%- if current_method.returned|length > 0 %}
|
||||
{%- for returned in current_method.returned %}
|
||||
<dl>
|
||||
<dt><code>{{ returned.data_type|escape|parse_data_type }} {{ returned.name }}</code></dt>
|
||||
<dd class="docblock">{{ returned.description|parse_links if returned.description else '<em>See method description.</em>' }}</dd>
|
||||
</dl>
|
||||
{%- endfor %}
|
||||
{%- elif not current_method.documented %}
|
||||
Unknown.
|
||||
{%- else %}
|
||||
Nothing.
|
||||
{%- endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,5 @@
|
||||
document.write(`
|
||||
{%- for method in current_class.methods %}
|
||||
<a id="{{ current_class.name + ':' + method.name }}" class="fn" href="{{ root(current_class.name + '/' + method.name + '.html') }}">{{ method.name }}</a>
|
||||
{%- endfor %}
|
||||
`);
|
||||
@@ -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 ALEDoc` 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]
|
||||
```
|
||||
@@ -0,0 +1,115 @@
|
||||
--
|
||||
-- Copyright (C) 2010 - 2016 Eluna Lua Engine <http://emudevs.com/>
|
||||
-- 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
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010 Ignacio Burgueño
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,128 @@
|
||||
# StackTracePlus #
|
||||
|
||||
[](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 <D:\trunk_git\sources\stacktraceplus\test\test.lua:7>
|
||||
(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/
|
||||
@@ -0,0 +1,411 @@
|
||||
-- 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")
|
||||
|
||||
|
||||
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 ("<failed to get printable value>: '%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
|
||||
@@ -0,0 +1,14 @@
|
||||
--
|
||||
-- Copyright (C) 2010 - 2016 Eluna Lua Engine <http://emudevs.com/>
|
||||
-- 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
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<AllCreatureEvents>(EVENT);\
|
||||
if (!AllCreatureEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EventKey<AllCreatureEvents>(EVENT);\
|
||||
if (!AllCreatureEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnAllCreatureAddToWorld(Creature* creature)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_ADD);
|
||||
Push(creature);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureRemoveFromWorld(Creature* creature)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_REMOVE);
|
||||
Push(creature);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_SELECT_LEVEL);
|
||||
Push(cinfo);
|
||||
Push(creature);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureBeforeSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL);
|
||||
Push(cinfo);
|
||||
Push(creature);
|
||||
Push(level);
|
||||
int levelIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
level = CHECKVAL<uint8>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(level, levelIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureAuraApply(Creature* me, Aura* aura)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_AURA_APPLY);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_AURA_REMOVE);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
Push(mode);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureHeal(Creature* me, Unit* target, uint32& gain)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_HEAL);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(gain);
|
||||
|
||||
int gainIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
gain = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(gain, gainIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_DAMAGE);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<int32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(heal);
|
||||
Push(spellInfo);
|
||||
|
||||
int healIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
heal = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(heal, healIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
uint32 ALE::OnAllCreatureDealDamage(Creature* me, Unit* target, uint32 damage, DamageEffectType damagetype)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ALL_CREATURE_EVENT_ON_DEAL_DAMAGE, damage);
|
||||
uint32 result = damage;
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(damagetype);
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
result = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(result, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<BGEvents>(EVENT);\
|
||||
if (!BGEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnBGStart(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_START);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBGEnd(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId, TeamId winner)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_END);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
Push(winner);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_CREATE);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_PRE_DESTROY);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT, CREATURE) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto entry_key = EntryKey<CreatureEvents>(EVENT, CREATURE->GetEntry());\
|
||||
auto unique_key = UniqueObjectKey<CreatureEvents>(EVENT, CREATURE->GET_GUID(), CREATURE->GetInstanceId());\
|
||||
if (!CreatureEventBindings->HasBindingsFor(entry_key))\
|
||||
if (!CreatureUniqueBindings->HasBindingsFor(unique_key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, CREATURE, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto entry_key = EntryKey<CreatureEvents>(EVENT, CREATURE->GetEntry());\
|
||||
auto unique_key = UniqueObjectKey<CreatureEvents>(EVENT, CREATURE->GET_GUID(), CREATURE->GetInstanceId());\
|
||||
if (!CreatureEventBindings->HasBindingsFor(entry_key))\
|
||||
if (!CreatureUniqueBindings->HasBindingsFor(unique_key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Creature* pTarget)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_DUMMY_EFFECT, pTarget);
|
||||
Push(pCaster);
|
||||
Push(spellId);
|
||||
Push(effIndex);
|
||||
Push(pTarget);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_QUEST_ACCEPT, pCreature, false);
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
Push(pQuest);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_QUEST_REWARD, pCreature, false);
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
Push(pQuest);
|
||||
Push(opt);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::GetDialogStatus(const Player* pPlayer, const Creature* pCreature)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_DIALOG_STATUS, pCreature);
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnAddToWorld(Creature* pCreature)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_ADD, pCreature);
|
||||
Push(pCreature);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnRemoveFromWorld(Creature* pCreature)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_REMOVE, pCreature);
|
||||
Push(pCreature);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::OnSummoned(Creature* pCreature, Unit* pSummoner)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED, pCreature, false);
|
||||
Push(pCreature);
|
||||
Push(pSummoner);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::UpdateAI(Creature* me, const uint32 diff)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_AIUPDATE, me, false);
|
||||
Push(me);
|
||||
Push(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 ALE::EnterCombat(Creature* me, Unit* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_ENTER_COMBAT, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called at any Damage from any attacker (before damage apply)
|
||||
bool ALE::DamageTaken(Creature* me, Unit* attacker, uint32& damage)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DAMAGE_TAKEN, me, false);
|
||||
bool result = false;
|
||||
Push(me);
|
||||
Push(attacker);
|
||||
Push(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 = ALE::CHECKVAL<uint32>(L, r + 1);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
//Called at creature death
|
||||
bool ALE::JustDied(Creature* me, Unit* killer)
|
||||
{
|
||||
On_Reset(me);
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DIED, me, false);
|
||||
Push(me);
|
||||
Push(killer);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
//Called at creature killing another unit
|
||||
bool ALE::KilledUnit(Creature* me, Unit* victim)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_TARGET_DIED, me, false);
|
||||
Push(me);
|
||||
Push(victim);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when the creature summon successfully other creature
|
||||
bool ALE::JustSummoned(Creature* me, Creature* summon)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, me, false);
|
||||
Push(me);
|
||||
Push(summon);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when a summoned creature is despawned
|
||||
bool ALE::SummonedCreatureDespawn(Creature* me, Creature* summon)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, me, false);
|
||||
Push(me);
|
||||
Push(summon);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
//Called at waypoint reached or PointMovement end
|
||||
bool ALE::MovementInform(Creature* me, uint32 type, uint32 id)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_REACH_WP, me, false);
|
||||
Push(me);
|
||||
Push(type);
|
||||
Push(id);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called before EnterCombat even before the creature is in combat.
|
||||
bool ALE::AttackStart(Creature* me, Unit* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_PRE_COMBAT, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called for reaction at stopping attack at no attackers or targets
|
||||
bool ALE::EnterEvadeMode(Creature* me)
|
||||
{
|
||||
On_Reset(me);
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_LEAVE_COMBAT, me, false);
|
||||
Push(me);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when creature is spawned or respawned (for reseting variables)
|
||||
bool ALE::JustRespawned(Creature* me)
|
||||
{
|
||||
On_Reset(me);
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SPAWN, me, false);
|
||||
Push(me);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called at reaching home after evade
|
||||
bool ALE::JustReachedHome(Creature* me)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_REACH_HOME, me, false);
|
||||
Push(me);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called at text emote receive from player
|
||||
bool ALE::ReceiveEmote(Creature* me, Player* player, uint32 emoteId)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_RECEIVE_EMOTE, me, false);
|
||||
Push(me);
|
||||
Push(player);
|
||||
Push(emoteId);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// called when the corpse of this creature gets removed
|
||||
bool ALE::CorpseRemoved(Creature* me, uint32& respawnDelay)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_CORPSE_REMOVED, me, false);
|
||||
bool result = false;
|
||||
Push(me);
|
||||
Push(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 = ALE::CHECKVAL<uint32>(L, r + 1);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(respawnDelay, respawnDelayIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
CleanUpStack(2);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ALE::MoveInLineOfSight(Creature* me, Unit* who)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_MOVE_IN_LOS, me, false);
|
||||
Push(me);
|
||||
Push(who);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called on creature initial spawn, respawn, death, evade (leave combat)
|
||||
void ALE::On_Reset(Creature* me) // Not an override, custom
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_RESET, me);
|
||||
Push(me);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when hit by a spell
|
||||
bool ALE::SpellHit(Creature* me, WorldObject* caster, SpellInfo const* spell)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_HIT_BY_SPELL, me, false);
|
||||
Push(me);
|
||||
Push(caster);
|
||||
Push(spell->Id); // Pass spell object?
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when spell hits a target
|
||||
bool ALE::SpellHitTarget(Creature* me, WorldObject* target, SpellInfo const* spell)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SPELL_HIT_TARGET, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(spell->Id); // Pass spell object?
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::SummonedCreatureDies(Creature* me, Creature* summon, Unit* killer)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, me, false);
|
||||
Push(me);
|
||||
Push(summon);
|
||||
Push(killer);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when owner takes damage
|
||||
bool ALE::OwnerAttackedBy(Creature* me, Unit* attacker)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_OWNER_ATTACKED_AT, me, false);
|
||||
Push(me);
|
||||
Push(attacker);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when owner attacks something
|
||||
bool ALE::OwnerAttacked(Creature* me, Unit* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_OWNER_ATTACKED, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureAuraApply(Creature* me, Aura* aura)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_AURA_APPLY, me);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_AURA_REMOVE, me);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
Push(mode);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureHeal(Creature* me, Unit* target, uint32& gain)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_HEAL, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(gain);
|
||||
|
||||
int gainIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
gain = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(gain, gainIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_DAMAGE, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<int32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(heal);
|
||||
Push(spellInfo);
|
||||
|
||||
int healIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
heal = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(heal, healIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
uint32 ALE::OnCreatureDealDamage(Creature* me, Unit* target, uint32 damage, DamageEffectType damagetype)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DEAL_DAMAGE, me, damage);
|
||||
uint32 result = damage;
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(damagetype);
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
result = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(result, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALEEventMgr.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT, ENTRY) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EntryKey<GameObjectEvents>(EVENT, ENTRY);\
|
||||
if (!GameObjectEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, ENTRY, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EntryKey<GameObjectEvents>(EVENT, ENTRY);\
|
||||
if (!GameObjectEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, GameObject* pTarget)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry());
|
||||
Push(pCaster);
|
||||
Push(spellId);
|
||||
Push(effIndex);
|
||||
Push(pTarget);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::UpdateAI(GameObject* pGameObject, uint32 diff)
|
||||
{
|
||||
pGameObject->ALEEvents->Update(diff);
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_AIUPDATE, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
Push(diff);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(GAMEOBJECT_EVENT_ON_QUEST_ACCEPT, pGameObject->GetEntry(), false);
|
||||
Push(pPlayer);
|
||||
Push(pGameObject);
|
||||
Push(pQuest);
|
||||
return CallAllFunctionsBool(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestReward(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest, uint32 opt)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(GAMEOBJECT_EVENT_ON_QUEST_REWARD, pGameObject->GetEntry(), false);
|
||||
Push(pPlayer);
|
||||
Push(pGameObject);
|
||||
Push(pQuest);
|
||||
Push(opt);
|
||||
return CallAllFunctionsBool(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::GetDialogStatus(const Player* pPlayer, const GameObject* pGameObject)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_DIALOG_STATUS, pGameObject->GetEntry());
|
||||
Push(pPlayer);
|
||||
Push(pGameObject);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDestroyed(GameObject* pGameObject, WorldObject* attacker)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_DESTROYED, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
Push(attacker);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDamaged(GameObject* pGameObject, WorldObject* attacker)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_DAMAGED, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
Push(attacker);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLootStateChanged(GameObject* pGameObject, uint32 state)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
Push(state);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnGameObjectStateChanged(GameObject* pGameObject, uint32 state)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
Push(state);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnSpawn(GameObject* pGameObject)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_SPAWN, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAddToWorld(GameObject* pGameObject)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_ADD, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemoveFromWorld(GameObject* pGameObject)
|
||||
{
|
||||
START_HOOK(GAMEOBJECT_EVENT_ON_REMOVE, pGameObject->GetEntry());
|
||||
Push(pGameObject);
|
||||
CallAllFunctions(GameObjectEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnGameObjectUse(Player* pPlayer, GameObject* pGameObject)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(GAMEOBJECT_EVENT_ON_USE, pGameObject->GetEntry(), false);
|
||||
Push(pGameObject);
|
||||
Push(pPlayer);
|
||||
return CallAllFunctionsBool(GameObjectEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(BINDINGS, EVENT, ENTRY) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EntryKey<GossipEvents>(EVENT, ENTRY);\
|
||||
if (!BINDINGS->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(BINDINGS, EVENT, ENTRY, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EntryKey<GossipEvents>(EVENT, ENTRY);\
|
||||
if (!BINDINGS->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
bool ALE::OnGossipHello(Player* pPlayer, GameObject* pGameObject)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(GameObjectGossipBindings, GOSSIP_EVENT_ON_HELLO, pGameObject->GetEntry(), false);
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pGameObject);
|
||||
return CallAllFunctionsBool(GameObjectGossipBindings, key, true);
|
||||
}
|
||||
|
||||
bool ALE::OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(GameObjectGossipBindings, GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry(), false);
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pGameObject);
|
||||
Push(sender);
|
||||
Push(action);
|
||||
return CallAllFunctionsBool(GameObjectGossipBindings, key, true);
|
||||
}
|
||||
|
||||
bool ALE::OnGossipSelectCode(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(GameObjectGossipBindings, GOSSIP_EVENT_ON_SELECT, pGameObject->GetEntry(), false);
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pGameObject);
|
||||
Push(sender);
|
||||
Push(action);
|
||||
Push(code);
|
||||
return CallAllFunctionsBool(GameObjectGossipBindings, key, true);
|
||||
}
|
||||
|
||||
void ALE::HandleGossipSelectOption(Player* pPlayer, uint32 menuId, uint32 sender, uint32 action, const std::string& code)
|
||||
{
|
||||
START_HOOK(PlayerGossipBindings, GOSSIP_EVENT_ON_SELECT, menuId);
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
|
||||
Push(pPlayer); // receiver
|
||||
Push(pPlayer); // sender, just not to mess up the amount of args.
|
||||
Push(sender);
|
||||
Push(action);
|
||||
if (code.empty())
|
||||
Push();
|
||||
else
|
||||
Push(code);
|
||||
|
||||
CallAllFunctions(PlayerGossipBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnItemGossip(Player* pPlayer, Item* pItem, SpellCastTargets const& /*targets*/)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ItemGossipBindings, GOSSIP_EVENT_ON_HELLO, pItem->GetEntry(), true);
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
return CallAllFunctionsBool(ItemGossipBindings, key, true);
|
||||
}
|
||||
|
||||
void ALE::HandleGossipSelectOption(Player* pPlayer, Item* pItem, uint32 sender, uint32 action, const std::string& code)
|
||||
{
|
||||
START_HOOK(ItemGossipBindings, GOSSIP_EVENT_ON_SELECT, pItem->GetEntry());
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
Push(sender);
|
||||
Push(action);
|
||||
if (code.empty())
|
||||
Push();
|
||||
else
|
||||
Push(code);
|
||||
|
||||
CallAllFunctions(ItemGossipBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnGossipHello(Player* pPlayer, Creature* pCreature)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CreatureGossipBindings, GOSSIP_EVENT_ON_HELLO, pCreature->GetEntry(), false);
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
return CallAllFunctionsBool(CreatureGossipBindings, key, true);
|
||||
}
|
||||
|
||||
bool ALE::OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CreatureGossipBindings, GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry(), false);
|
||||
auto originalMenu = *pPlayer->PlayerTalkClass;
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
Push(sender);
|
||||
Push(action);
|
||||
auto preventDefault = CallAllFunctionsBool(CreatureGossipBindings, key, true);
|
||||
if (!preventDefault) {
|
||||
*pPlayer->PlayerTalkClass = originalMenu;
|
||||
}
|
||||
return preventDefault;
|
||||
}
|
||||
|
||||
bool ALE::OnGossipSelectCode(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CreatureGossipBindings, GOSSIP_EVENT_ON_SELECT, pCreature->GetEntry(), false);
|
||||
auto originalMenu = *pPlayer->PlayerTalkClass;
|
||||
pPlayer->PlayerTalkClass->ClearMenus();
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
Push(sender);
|
||||
Push(action);
|
||||
Push(code);
|
||||
auto preventDefault = CallAllFunctionsBool(CreatureGossipBindings, key, true);
|
||||
if (!preventDefault) {
|
||||
*pPlayer->PlayerTalkClass = originalMenu;
|
||||
}
|
||||
return preventDefault;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<GroupEvents>(EVENT);\
|
||||
if (!GroupEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnAddMember(Group* group, ObjectGuid guid)
|
||||
{
|
||||
START_HOOK(GROUP_EVENT_ON_MEMBER_ADD);
|
||||
Push(group);
|
||||
Push(guid);
|
||||
CallAllFunctions(GroupEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnInviteMember(Group* group, ObjectGuid guid)
|
||||
{
|
||||
START_HOOK(GROUP_EVENT_ON_MEMBER_INVITE);
|
||||
Push(group);
|
||||
Push(guid);
|
||||
CallAllFunctions(GroupEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemoveMember(Group* group, ObjectGuid guid, uint8 method)
|
||||
{
|
||||
START_HOOK(GROUP_EVENT_ON_MEMBER_REMOVE);
|
||||
Push(group);
|
||||
Push(guid);
|
||||
Push(method);
|
||||
CallAllFunctions(GroupEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid)
|
||||
{
|
||||
START_HOOK(GROUP_EVENT_ON_LEADER_CHANGE);
|
||||
Push(group);
|
||||
Push(newLeaderGuid);
|
||||
Push(oldLeaderGuid);
|
||||
CallAllFunctions(GroupEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDisband(Group* group)
|
||||
{
|
||||
START_HOOK(GROUP_EVENT_ON_DISBAND);
|
||||
Push(group);
|
||||
CallAllFunctions(GroupEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnCreate(Group* group, ObjectGuid leaderGuid, GroupType groupType)
|
||||
{
|
||||
START_HOOK(GROUP_EVENT_ON_CREATE);
|
||||
Push(group);
|
||||
Push(leaderGuid);
|
||||
Push(groupType);
|
||||
CallAllFunctions(GroupEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<GuildEvents>(EVENT);\
|
||||
if (!GuildEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnAddMember(Guild* guild, Player* player, uint32 plRank)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_ADD_MEMBER);
|
||||
Push(guild);
|
||||
Push(player);
|
||||
Push(plRank);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemoveMember(Guild* guild, Player* player, bool isDisbanding)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_REMOVE_MEMBER);
|
||||
Push(guild);
|
||||
Push(player);
|
||||
Push(isDisbanding);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnMOTDChanged(Guild* guild, const std::string& newMotd)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_MOTD_CHANGE);
|
||||
Push(guild);
|
||||
Push(newMotd);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnInfoChanged(Guild* guild, const std::string& newInfo)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_INFO_CHANGE);
|
||||
Push(guild);
|
||||
Push(newInfo);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnCreate(Guild* guild, Player* leader, const std::string& name)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_CREATE);
|
||||
Push(guild);
|
||||
Push(leader);
|
||||
Push(name);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDisband(Guild* guild)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_DISBAND);
|
||||
Push(guild);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnMemberWitdrawMoney(Guild* guild, Player* player, uint32& amount, bool isRepair)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_MONEY_WITHDRAW);
|
||||
Push(guild);
|
||||
Push(player);
|
||||
Push(amount);
|
||||
Push(isRepair); // isRepair not a part of Mangos, implement?
|
||||
int amountIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(GuildEventBindings, key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
amount = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(amount, amountIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnMemberDepositMoney(Guild* guild, Player* player, uint32& amount)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_MONEY_DEPOSIT);
|
||||
Push(guild);
|
||||
Push(player);
|
||||
Push(amount);
|
||||
int amountIndex = lua_gettop(L);
|
||||
int n = SetupStack(GuildEventBindings, key, 3);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
amount = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(amount, amountIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::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);
|
||||
Push(guild);
|
||||
Push(player);
|
||||
Push(pItem);
|
||||
Push(isSrcBank);
|
||||
Push(srcContainer);
|
||||
Push(srcSlotId);
|
||||
Push(isDestBank);
|
||||
Push(destContainer);
|
||||
Push(destSlotId);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnEvent(Guild* guild, uint8 eventType, uint32 playerGuid1, uint32 playerGuid2, uint8 newRank)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_EVENT);
|
||||
Push(guild);
|
||||
Push(eventType);
|
||||
Push(playerGuid1);
|
||||
Push(playerGuid2);
|
||||
Push(newRank);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBankEvent(Guild* guild, uint8 eventType, uint8 tabId, uint32 playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId)
|
||||
{
|
||||
START_HOOK(GUILD_EVENT_ON_BANK_EVENT);
|
||||
Push(guild);
|
||||
Push(eventType);
|
||||
Push(tabId);
|
||||
Push(playerGuid);
|
||||
Push(itemOrMoney);
|
||||
Push(itemStackCount);
|
||||
Push(destTabId);
|
||||
CallAllFunctions(GuildEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
#include "ALEInstanceAI.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT, AI) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto mapKey = EntryKey<InstanceEvents>(EVENT, AI->instance->GetId());\
|
||||
auto instanceKey = EntryKey<InstanceEvents>(EVENT, AI->instance->GetInstanceId());\
|
||||
if (!MapEventBindings->HasBindingsFor(mapKey) && !InstanceEventBindings->HasBindingsFor(instanceKey))\
|
||||
return;\
|
||||
LOCK_ALE;\
|
||||
PushInstanceData(L, AI);\
|
||||
Push(AI->instance)
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, AI, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto mapKey = EntryKey<InstanceEvents>(EVENT, AI->instance->GetId());\
|
||||
auto instanceKey = EntryKey<InstanceEvents>(EVENT, AI->instance->GetInstanceId());\
|
||||
if (!MapEventBindings->HasBindingsFor(mapKey) && !InstanceEventBindings->HasBindingsFor(instanceKey))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE;\
|
||||
PushInstanceData(L, AI);\
|
||||
Push(AI->instance)
|
||||
|
||||
void ALE::OnInitialize(ALEInstanceAI* ai)
|
||||
{
|
||||
START_HOOK(INSTANCE_EVENT_ON_INITIALIZE, ai);
|
||||
CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
|
||||
void ALE::OnLoad(ALEInstanceAI* ai)
|
||||
{
|
||||
START_HOOK(INSTANCE_EVENT_ON_LOAD, ai);
|
||||
CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
|
||||
void ALE::OnUpdateInstance(ALEInstanceAI* ai, uint32 diff)
|
||||
{
|
||||
START_HOOK(INSTANCE_EVENT_ON_UPDATE, ai);
|
||||
Push(diff);
|
||||
CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerEnterInstance(ALEInstanceAI* ai, Player* player)
|
||||
{
|
||||
START_HOOK(INSTANCE_EVENT_ON_PLAYER_ENTER, ai);
|
||||
Push(player);
|
||||
CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureCreate(ALEInstanceAI* ai, Creature* creature)
|
||||
{
|
||||
START_HOOK(INSTANCE_EVENT_ON_CREATURE_CREATE, ai);
|
||||
Push(creature);
|
||||
CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
|
||||
void ALE::OnGameObjectCreate(ALEInstanceAI* ai, GameObject* gameobject)
|
||||
{
|
||||
START_HOOK(INSTANCE_EVENT_ON_GAMEOBJECT_CREATE, ai);
|
||||
Push(gameobject);
|
||||
CallAllFunctions(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
|
||||
bool ALE::OnCheckEncounterInProgress(ALEInstanceAI* ai)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS, ai, false);
|
||||
return CallAllFunctionsBool(MapEventBindings, InstanceEventBindings, mapKey, instanceKey);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT, ENTRY) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EntryKey<ItemEvents>(EVENT, ENTRY);\
|
||||
if (!ItemEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, ENTRY, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EntryKey<ItemEvents>(EVENT, ENTRY);\
|
||||
if (!ItemEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Item* pTarget)
|
||||
{
|
||||
START_HOOK(ITEM_EVENT_ON_DUMMY_EFFECT, pTarget->GetEntry());
|
||||
Push(pCaster);
|
||||
Push(spellId);
|
||||
Push(effIndex);
|
||||
Push(pTarget);
|
||||
CallAllFunctions(ItemEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_QUEST_ACCEPT, pItem->GetEntry(), false);
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
Push(pQuest);
|
||||
return CallAllFunctionsBool(ItemEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::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
|
||||
WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, 18);
|
||||
data << uint8(59); // EQUIP_ERR_NONE / EQUIP_ERR_CANT_BE_DISENCHANTED
|
||||
data << guid;
|
||||
data << ObjectGuid(uint64(0));
|
||||
data << uint8(0);
|
||||
pPlayer->GetSession()->SendPacket(&data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ALE::OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_USE, pItem->GetEntry(), true);
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
|
||||
if (GameObject* target = targets.GetGOTarget())
|
||||
Push(target);
|
||||
else if (Item* target = targets.GetItemTarget())
|
||||
Push(target);
|
||||
else if (Corpse* target = targets.GetCorpseTarget())
|
||||
Push(target);
|
||||
else if (Unit* target = targets.GetUnitTarget())
|
||||
Push(target);
|
||||
else if (WorldObject* target = targets.GetObjectTarget())
|
||||
Push(target);
|
||||
else
|
||||
Push();
|
||||
|
||||
return CallAllFunctionsBool(ItemEventBindings, key, true);
|
||||
}
|
||||
|
||||
bool ALE::OnExpire(Player* pPlayer, ItemTemplate const* pProto)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_EXPIRE, pProto->ItemId, false);
|
||||
Push(pPlayer);
|
||||
Push(pProto->ItemId);
|
||||
return CallAllFunctionsBool(ItemEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnRemove(Player* pPlayer, Item* pItem)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ITEM_EVENT_ON_REMOVE, pItem->GetEntry(), false);
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
return CallAllFunctionsBool(ItemEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK_SERVER(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<ServerEvents>(EVENT);\
|
||||
if (!ServerEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_PACKET(EVENT, OPCODE) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EntryKey<PacketEvents>(EVENT, OPCODE);\
|
||||
if (!PacketEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
bool ALE::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 ALE::OnPacketSendAny(Player* player, const WorldPacket& packet, bool& result)
|
||||
{
|
||||
START_HOOK_SERVER(SERVER_EVENT_ON_PACKET_SEND);
|
||||
Push(new WorldPacket(packet));
|
||||
Push(player);
|
||||
int n = SetupStack(ServerEventBindings, 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 ALE::OnPacketSendOne(Player* player, const WorldPacket& packet, bool& result)
|
||||
{
|
||||
START_HOOK_PACKET(PACKET_EVENT_ON_PACKET_SEND, packet.GetOpcode());
|
||||
Push(new WorldPacket(packet));
|
||||
Push(player);
|
||||
int n = SetupStack(PacketEventBindings, 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 ALE::OnPacketReceive(WorldSession* session, WorldPacket const& packet)
|
||||
{
|
||||
bool result = true;
|
||||
Player* player = NULL;
|
||||
if (session)
|
||||
player = session->GetPlayer();
|
||||
OnPacketReceiveAny(player, packet, result);
|
||||
OnPacketReceiveOne(player, packet, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ALE::OnPacketReceiveAny(Player* player, WorldPacket const& packet, bool& result)
|
||||
{
|
||||
START_HOOK_SERVER(SERVER_EVENT_ON_PACKET_RECEIVE);
|
||||
Push(new WorldPacket(packet));
|
||||
Push(player);
|
||||
int n = SetupStack(ServerEventBindings, 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 ALE::OnPacketReceiveOne(Player* player, WorldPacket const& packet, bool& result)
|
||||
{
|
||||
START_HOOK_PACKET(PACKET_EVENT_ON_PACKET_RECEIVE, packet.GetOpcode());
|
||||
Push(new WorldPacket(packet));
|
||||
Push(player);
|
||||
int n = SetupStack(PacketEventBindings, 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);
|
||||
}
|
||||
@@ -0,0 +1,976 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<PlayerEvents>(EVENT);\
|
||||
if (!PlayerEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EventKey<PlayerEvents>(EVENT);\
|
||||
if (!PlayerEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnLearnTalents(Player* pPlayer, uint32 talentId, uint32 talentRank, uint32 spellid)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LEARN_TALENTS);
|
||||
Push(pPlayer);
|
||||
Push(talentId);
|
||||
Push(talentRank);
|
||||
Push(spellid);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnCommand(ChatHandler& handler, const char* text)
|
||||
{
|
||||
Player* player = handler.IsConsole() ? nullptr : handler.GetSession()->GetPlayer();
|
||||
// If from console, player is NULL
|
||||
if (!player || player->GetSession()->GetSecurity() >= SEC_ADMINISTRATOR)
|
||||
{
|
||||
std::string reload = text;
|
||||
std::transform(reload.begin(), reload.end(), reload.begin(), ::tolower);
|
||||
if (reload.find("reload ale") == 0)
|
||||
{
|
||||
ReloadALE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_COMMAND, true);
|
||||
Push(player);
|
||||
Push(text);
|
||||
Push(&handler);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key, true);
|
||||
}
|
||||
|
||||
void ALE::OnLootItem(Player* pPlayer, Item* pItem, uint32 count, ObjectGuid guid)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LOOT_ITEM);
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
Push(count);
|
||||
Push(guid);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLootMoney(Player* pPlayer, uint32 amount)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LOOT_MONEY);
|
||||
Push(pPlayer);
|
||||
Push(amount);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnFirstLogin(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_FIRST_LOGIN);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRepop(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_REPOP);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnResurrect(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_RESURRECT);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnQuestAbandon(Player* pPlayer, uint32 questId)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_QUEST_ABANDON);
|
||||
Push(pPlayer);
|
||||
Push(questId);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnEquip(Player* pPlayer, Item* pItem, uint8 bag, uint8 slot)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_EQUIP);
|
||||
Push(pPlayer);
|
||||
Push(pItem);
|
||||
Push(bag);
|
||||
Push(slot);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
InventoryResult ALE::OnCanUseItem(const Player* pPlayer, uint32 itemEntry)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_USE_ITEM, EQUIP_ERR_OK);
|
||||
InventoryResult result = EQUIP_ERR_OK;
|
||||
Push(pPlayer);
|
||||
Push(itemEntry);
|
||||
int n = SetupStack(PlayerEventBindings, key, 2);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 2, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
result = (InventoryResult)CHECKVAL<uint32>(L, r);
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(2);
|
||||
return result;
|
||||
}
|
||||
void ALE::OnPlayerEnterCombat(Player* pPlayer, Unit* pEnemy)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_ENTER_COMBAT);
|
||||
Push(pPlayer);
|
||||
Push(pEnemy);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerLeaveCombat(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LEAVE_COMBAT);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPVPKill(Player* pKiller, Player* pKilled)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_KILL_PLAYER);
|
||||
Push(pKiller);
|
||||
Push(pKilled);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureKill(Player* pKiller, Creature* pKilled)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_KILL_CREATURE);
|
||||
Push(pKiller);
|
||||
Push(pKilled);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerKilledByCreature(Creature* pKiller, Player* pKilled)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_KILLED_BY_CREATURE);
|
||||
Push(pKiller);
|
||||
Push(pKilled);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLevelChanged(Player* pPlayer, uint8 oldLevel)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LEVEL_CHANGE);
|
||||
Push(pPlayer);
|
||||
Push(oldLevel);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnFreeTalentPointsChanged(Player* pPlayer, uint32 newPoints)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_TALENTS_CHANGE);
|
||||
Push(pPlayer);
|
||||
Push(newPoints);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnTalentsReset(Player* pPlayer, bool noCost)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_TALENTS_RESET);
|
||||
Push(pPlayer);
|
||||
Push(noCost);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnMoneyChanged(Player* pPlayer, int32& amount)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_MONEY_CHANGE);
|
||||
Push(pPlayer);
|
||||
Push(amount);
|
||||
int amountIndex = lua_gettop(L);
|
||||
int n = SetupStack(PlayerEventBindings, key, 2);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 2, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
amount = CHECKVAL<int32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(amount, amountIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(2);
|
||||
}
|
||||
|
||||
void ALE::OnGiveXP(Player* pPlayer, uint32& amount, Unit* pVictim, uint8 xpSource)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_GIVE_XP);
|
||||
Push(pPlayer);
|
||||
Push(amount);
|
||||
Push(pVictim);
|
||||
Push(xpSource);
|
||||
int amountIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(PlayerEventBindings, key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
amount = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(amount, amountIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
bool ALE::OnReputationChange(Player* pPlayer, uint32 factionID, int32& standing, bool incremental)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_REPUTATION_CHANGE, true);
|
||||
bool result = true;
|
||||
Push(pPlayer);
|
||||
Push(factionID);
|
||||
Push(standing);
|
||||
Push(incremental);
|
||||
int standingIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(PlayerEventBindings, key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
standing = CHECKVAL<int32>(L, r);
|
||||
if (standing == -1)
|
||||
result = false;
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(standing, standingIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ALE::OnDuelRequest(Player* pTarget, Player* pChallenger)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_DUEL_REQUEST);
|
||||
Push(pTarget);
|
||||
Push(pChallenger);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDuelStart(Player* pStarter, Player* pChallenger)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_DUEL_START);
|
||||
Push(pStarter);
|
||||
Push(pChallenger);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDuelEnd(Player* pWinner, Player* pLoser, DuelCompleteType type)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_DUEL_END);
|
||||
Push(pWinner);
|
||||
Push(pLoser);
|
||||
Push(type);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnEmote(Player* pPlayer, uint32 emote)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_EMOTE);
|
||||
Push(pPlayer);
|
||||
Push(emote);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, ObjectGuid guid)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_TEXT_EMOTE);
|
||||
Push(pPlayer);
|
||||
Push(textEmote);
|
||||
Push(emoteNum);
|
||||
Push(guid);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_SPELL_CAST);
|
||||
Push(pPlayer);
|
||||
Push(pSpell);
|
||||
Push(skipCheck);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLogin(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LOGIN);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLogout(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LOGOUT);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnCreate(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_CHARACTER_CREATE);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDelete(uint32 guidlow)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_CHARACTER_DELETE);
|
||||
Push(guidlow);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnSave(Player* pPlayer)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_SAVE);
|
||||
Push(pPlayer);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBindToInstance(Player* pPlayer, Difficulty difficulty, uint32 mapid, bool permanent)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_BIND_TO_INSTANCE);
|
||||
Push(pPlayer);
|
||||
Push(difficulty);
|
||||
Push(mapid);
|
||||
Push(permanent);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnUpdateArea(Player* pPlayer, uint32 oldArea, uint32 newArea)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_UPDATE_AREA);
|
||||
Push(pPlayer);
|
||||
Push(oldArea);
|
||||
Push(newArea);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnUpdateZone(Player* pPlayer, uint32 newZone, uint32 newArea)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_UPDATE_ZONE);
|
||||
Push(pPlayer);
|
||||
Push(newZone);
|
||||
Push(newArea);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnMapChanged(Player* player)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_MAP_CHANGE);
|
||||
Push(player);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::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;
|
||||
Push(pPlayer);
|
||||
Push(msg);
|
||||
Push(type);
|
||||
Push(lang);
|
||||
int n = SetupStack(PlayerEventBindings, 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 ALE::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;
|
||||
Push(pPlayer);
|
||||
Push(msg);
|
||||
Push(type);
|
||||
Push(lang);
|
||||
Push(pGroup);
|
||||
int n = SetupStack(PlayerEventBindings, 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 ALE::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;
|
||||
Push(pPlayer);
|
||||
Push(msg);
|
||||
Push(type);
|
||||
Push(lang);
|
||||
Push(pGuild);
|
||||
int n = SetupStack(PlayerEventBindings, 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 ALE::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;
|
||||
Push(pPlayer);
|
||||
Push(msg);
|
||||
Push(type);
|
||||
Push(lang);
|
||||
Push(pChannel->IsConstant() ? static_cast<int32>(pChannel->GetChannelId()) : -static_cast<int32>(pChannel->GetChannelDBId()));
|
||||
int n = SetupStack(PlayerEventBindings, 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 ALE::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;
|
||||
Push(pPlayer);
|
||||
Push(msg);
|
||||
Push(type);
|
||||
Push(lang);
|
||||
Push(pReceiver);
|
||||
int n = SetupStack(PlayerEventBindings, 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;
|
||||
}
|
||||
|
||||
void ALE::OnPetAddedToWorld(Player* player, Creature* pet)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_PET_ADDED_TO_WORLD);
|
||||
Push(player);
|
||||
Push(pet);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLearnSpell(Player* player, uint32 spellId)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_LEARN_SPELL);
|
||||
Push(player);
|
||||
Push(spellId);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAchiComplete(Player* player, AchievementEntry const* achievement)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_ACHIEVEMENT_COMPLETE);
|
||||
Push(player);
|
||||
Push(achievement);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnFfaPvpStateUpdate(Player* player, bool hasFfaPvp)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_FFAPVP_CHANGE);
|
||||
Push(player);
|
||||
Push(hasFfaPvp);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnCanInitTrade(Player* player, Player* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_INIT_TRADE, true);
|
||||
Push(player);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnCanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid mailbox, std::string& subject, std::string& body, uint32 money, uint32 cod, Item* item)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_SEND_MAIL, true);
|
||||
Push(player);
|
||||
Push(receiverGuid);
|
||||
Push(mailbox);
|
||||
Push(subject);
|
||||
Push(body);
|
||||
Push(money);
|
||||
Push(cod);
|
||||
Push(item);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnCanJoinLfg(Player* player, uint8 roles, lfg::LfgDungeonSet& dungeons, const std::string& comment)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_JOIN_LFG, true);
|
||||
Push(player);
|
||||
Push(roles);
|
||||
|
||||
lua_newtable(L);
|
||||
int table = lua_gettop(L);
|
||||
uint32 counter = 1;
|
||||
for (uint32 dungeon : dungeons)
|
||||
{
|
||||
ALE::Push(L, dungeon);
|
||||
lua_rawseti(L, table, counter);
|
||||
++counter;
|
||||
}
|
||||
lua_settop(L, table);
|
||||
++push_counter;
|
||||
|
||||
Push(comment);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnQuestRewardItem(Player* player, Item* item, uint32 count)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_QUEST_REWARD_ITEM);
|
||||
Push(player);
|
||||
Push(item);
|
||||
Push(count);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnCreateItem(Player* player, Item* item, uint32 count)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_CREATE_ITEM);
|
||||
Push(player);
|
||||
Push(item);
|
||||
Push(count);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnStoreNewItem(Player* player, Item* item, uint32 count)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_STORE_NEW_ITEM);
|
||||
Push(player);
|
||||
Push(item);
|
||||
Push(count);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerCompleteQuest(Player* player, Quest const* quest)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_COMPLETE_QUEST);
|
||||
Push(player);
|
||||
Push(quest);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnCanGroupInvite(Player* player, std::string& memberName)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_GROUP_INVITE, true);
|
||||
Push(player);
|
||||
Push(memberName);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_GROUP_ROLL_REWARD_ITEM);
|
||||
Push(player);
|
||||
Push(item);
|
||||
Push(count);
|
||||
Push(voteType);
|
||||
Push(roll);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBattlegroundDesertion(Player* player, const BattlegroundDesertionType type)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_BG_DESERTION);
|
||||
Push(player);
|
||||
Push(type);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureKilledByPet(Player* player, Creature* killed)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_PET_KILL);
|
||||
Push(player);
|
||||
Push(killed);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::OnPlayerCanUpdateSkill(Player* player, uint32 skill_id)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_UPDATE_SKILL, true);
|
||||
Push(player);
|
||||
Push(skill_id);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerBeforeUpdateSkill(Player* player, uint32 skill_id, uint32& value, uint32 max, uint32 step)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_BEFORE_UPDATE_SKILL);
|
||||
Push(player);
|
||||
Push(skill_id);
|
||||
Push(value);
|
||||
Push(max);
|
||||
Push(step);
|
||||
|
||||
int valueIndex = lua_gettop(L) -2;
|
||||
int n = SetupStack(PlayerEventBindings, key, 5);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 5, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
value = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(value, valueIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(5);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerUpdateSkill(Player* player, uint32 skill_id, uint32 value, uint32 max, uint32 step, uint32 new_value)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_UPDATE_SKILL);
|
||||
Push(player);
|
||||
Push(skill_id);
|
||||
Push(value);
|
||||
Push(max);
|
||||
Push(step);
|
||||
Push(new_value);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
bool ALE::CanPlayerResurrect(Player* player)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_CAN_RESURRECT, true);
|
||||
Push(player);
|
||||
return CallAllFunctionsBool(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerReleasedGhost(Player* player)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_RELEASED_GHOST);
|
||||
Push(player);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerQuestAccept(Player* player, Quest const* quest)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_QUEST_ACCEPT);
|
||||
Push(player);
|
||||
Push(quest);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerAuraApply(Player* player, Aura* aura)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_AURA_APPLY);
|
||||
Push(player);
|
||||
Push(aura);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerHeal(Player* player, Unit* target, uint32& gain)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_HEAL);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(gain);
|
||||
|
||||
int gainIndex = lua_gettop(L);
|
||||
int n = SetupStack(PlayerEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
gain = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(gain, gainIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerDamage(Player* player, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_DAMAGE);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(PlayerEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerAuraRemove(Player* player, Aura* aura, AuraRemoveMode mode)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_AURA_REMOVE);
|
||||
Push(player);
|
||||
Push(aura);
|
||||
Push(mode);
|
||||
CallAllFunctions(PlayerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerModifyPeriodicDamageAurasTick(Player* player, Unit* target, uint32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(PlayerEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerModifyMeleeDamage(Player* player, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_MODIFY_MELEE_DAMAGE);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(PlayerEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerModifySpellDamageTaken(Player* player, Unit* target, int32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(PlayerEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<int32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerModifyHealReceived(Player* player, Unit* target, uint32& heal, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(PLAYER_EVENT_ON_MODIFY_HEAL_RECEIVED);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(heal);
|
||||
Push(spellInfo);
|
||||
|
||||
int healIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(PlayerEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
heal = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(heal, healIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
uint32 ALE::OnPlayerDealDamage(Player* player, Unit* target, uint32 damage, DamageEffectType damagetype)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_DEAL_DAMAGE, damage);
|
||||
Push(player);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(damagetype);
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(PlayerEventBindings, key, 4);
|
||||
|
||||
uint32 result = damage;
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
result = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(result, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEEventMgr.h"
|
||||
#include "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<ServerEvents>(EVENT);\
|
||||
if (!ServerEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EventKey<ServerEvents>(EVENT);\
|
||||
if (!ServerEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
bool ALE::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);
|
||||
Push(sender);
|
||||
Push(type);
|
||||
|
||||
auto delimeter_position = msg.find('\t');
|
||||
if (delimeter_position == std::string::npos)
|
||||
{
|
||||
Push(msg); // prefix
|
||||
Push(); // msg
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string prefix = msg.substr(0, delimeter_position);
|
||||
std::string content = msg.substr(delimeter_position + 1, std::string::npos);
|
||||
Push(prefix);
|
||||
Push(content);
|
||||
}
|
||||
|
||||
if (receiver)
|
||||
Push(receiver);
|
||||
else if (guild)
|
||||
Push(guild);
|
||||
else if (group)
|
||||
Push(group);
|
||||
else if (channel)
|
||||
Push(channel->GetChannelId());
|
||||
else
|
||||
Push();
|
||||
|
||||
return CallAllFunctionsBool(ServerEventBindings, key, true);
|
||||
}
|
||||
|
||||
void ALE::OnTimedEvent(int funcRef, uint32 delay, uint32 calls, WorldObject* obj)
|
||||
{
|
||||
LOCK_ALE;
|
||||
ASSERT(!event_level);
|
||||
|
||||
// Get function
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef);
|
||||
|
||||
// Push parameters
|
||||
Push(L, funcRef);
|
||||
Push(L, delay);
|
||||
Push(L, calls);
|
||||
Push(L, obj);
|
||||
|
||||
// Call function
|
||||
ExecuteCall(4, 0);
|
||||
|
||||
ASSERT(!event_level);
|
||||
InvalidateObjects();
|
||||
}
|
||||
|
||||
void ALE::OnGameEventStart(uint32 eventid)
|
||||
{
|
||||
START_HOOK(GAME_EVENT_START);
|
||||
Push(eventid);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnGameEventStop(uint32 eventid)
|
||||
{
|
||||
START_HOOK(GAME_EVENT_STOP);
|
||||
Push(eventid);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLuaStateClose()
|
||||
{
|
||||
START_HOOK(ALE_EVENT_ON_LUA_STATE_CLOSE);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnLuaStateOpen()
|
||||
{
|
||||
START_HOOK(ALE_EVENT_ON_LUA_STATE_OPEN);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
// AreaTrigger
|
||||
bool ALE::OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* pTrigger)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(TRIGGER_EVENT_ON_TRIGGER, false);
|
||||
Push(pPlayer);
|
||||
Push(pTrigger->entry);
|
||||
|
||||
return CallAllFunctionsBool(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
// Weather
|
||||
void ALE::OnChange(Weather* /*weather*/, uint32 zone, WeatherState state, float grade)
|
||||
{
|
||||
START_HOOK(WEATHER_EVENT_ON_CHANGE);
|
||||
Push(zone);
|
||||
Push(state);
|
||||
Push(grade);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
// Auction House
|
||||
void ALE::OnAdd(AuctionHouseObject* /*ah*/, AuctionEntry* entry)
|
||||
{
|
||||
Player* owner = eObjectAccessor()FindPlayer(entry->owner);
|
||||
|
||||
Item* item = eAuctionMgr->GetAItem(entry->item_guid);
|
||||
uint32 expiretime = entry->expire_time;
|
||||
|
||||
if (!owner || !item)
|
||||
return;
|
||||
|
||||
START_HOOK(AUCTION_EVENT_ON_ADD);
|
||||
Push(entry->Id);
|
||||
Push(owner);
|
||||
Push(item);
|
||||
Push(expiretime);
|
||||
Push(entry->buyout);
|
||||
Push(entry->startbid);
|
||||
Push(entry->bid);
|
||||
Push(entry->bidder);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemove(AuctionHouseObject* /*ah*/, AuctionEntry* entry)
|
||||
{
|
||||
Player* owner = eObjectAccessor()FindPlayer(entry->owner);
|
||||
|
||||
Item* item = eAuctionMgr->GetAItem(entry->item_guid);
|
||||
uint32 expiretime = entry->expire_time;
|
||||
|
||||
if (!owner || !item)
|
||||
return;
|
||||
|
||||
START_HOOK(AUCTION_EVENT_ON_REMOVE);
|
||||
Push(entry->Id);
|
||||
Push(owner);
|
||||
Push(item);
|
||||
Push(expiretime);
|
||||
Push(entry->buyout);
|
||||
Push(entry->startbid);
|
||||
Push(entry->bid);
|
||||
Push(entry->bidder);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnSuccessful(AuctionHouseObject* /*ah*/, AuctionEntry* entry)
|
||||
{
|
||||
Player* owner = eObjectAccessor()FindPlayer(entry->owner);
|
||||
|
||||
Item* item = eAuctionMgr->GetAItem(entry->item_guid);
|
||||
uint32 expiretime = entry->expire_time;
|
||||
|
||||
if (!owner || !item)
|
||||
return;
|
||||
|
||||
START_HOOK(AUCTION_EVENT_ON_SUCCESSFUL);
|
||||
Push(entry->Id);
|
||||
Push(owner);
|
||||
Push(item);
|
||||
Push(expiretime);
|
||||
Push(entry->buyout);
|
||||
Push(entry->startbid);
|
||||
Push(entry->bid);
|
||||
Push(entry->bidder);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnExpire(AuctionHouseObject* /*ah*/, AuctionEntry* entry)
|
||||
{
|
||||
Player* owner = eObjectAccessor()FindPlayer(entry->owner);
|
||||
|
||||
Item* item = eAuctionMgr->GetAItem(entry->item_guid);
|
||||
uint32 expiretime = entry->expire_time;
|
||||
|
||||
if (!owner || !item)
|
||||
return;
|
||||
|
||||
START_HOOK(AUCTION_EVENT_ON_EXPIRE);
|
||||
Push(entry->Id);
|
||||
Push(owner);
|
||||
Push(item);
|
||||
Push(expiretime);
|
||||
Push(entry->buyout);
|
||||
Push(entry->startbid);
|
||||
Push(entry->bid);
|
||||
Push(entry->bidder);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnOpenStateChange(bool open)
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_OPEN_STATE_CHANGE);
|
||||
Push(open);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnConfigLoad(bool reload, bool isBefore)
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_CONFIG_LOAD);
|
||||
Push(reload);
|
||||
Push(isBefore);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask)
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_SHUTDOWN_INIT);
|
||||
Push(code);
|
||||
Push(mask);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnShutdownCancel()
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_SHUTDOWN_CANCEL);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnWorldUpdate(uint32 diff)
|
||||
{
|
||||
{
|
||||
LOCK_ALE;
|
||||
if (ShouldReload())
|
||||
_ReloadALE();
|
||||
}
|
||||
|
||||
eventMgr->globalProcessor->Update(diff);
|
||||
httpManager.HandleHttpResponses();
|
||||
queryProcessor.ProcessReadyCallbacks();
|
||||
|
||||
START_HOOK(WORLD_EVENT_ON_UPDATE);
|
||||
Push(diff);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnStartup()
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_STARTUP);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnShutdown()
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_SHUTDOWN);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
/* Map */
|
||||
void ALE::OnCreate(Map* map)
|
||||
{
|
||||
START_HOOK(MAP_EVENT_ON_CREATE);
|
||||
Push(map);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnDestroy(Map* map)
|
||||
{
|
||||
START_HOOK(MAP_EVENT_ON_DESTROY);
|
||||
Push(map);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerEnter(Map* map, Player* player)
|
||||
{
|
||||
START_HOOK(MAP_EVENT_ON_PLAYER_ENTER);
|
||||
Push(map);
|
||||
Push(player);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnPlayerLeave(Map* map, Player* player)
|
||||
{
|
||||
START_HOOK(MAP_EVENT_ON_PLAYER_LEAVE);
|
||||
Push(map);
|
||||
Push(player);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnUpdate(Map* map, uint32 diff)
|
||||
{
|
||||
START_HOOK(MAP_EVENT_ON_UPDATE);
|
||||
// enable this for multithread
|
||||
// eventMgr->globalProcessor->Update(diff);
|
||||
Push(map);
|
||||
Push(diff);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemove(GameObject* gameobject)
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_DELETE_GAMEOBJECT);
|
||||
Push(gameobject);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemove(Creature* creature)
|
||||
{
|
||||
START_HOOK(WORLD_EVENT_ON_DELETE_CREATURE);
|
||||
Push(creature);
|
||||
CallAllFunctions(ServerEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT, ENTRY) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EntryKey<SpellEvents>(EVENT, ENTRY);\
|
||||
if (!SpellEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, ENTRY, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EntryKey<SpellEvents>(EVENT, ENTRY);\
|
||||
if (!SpellEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnSpellCastCancel(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool bySelf)
|
||||
{
|
||||
START_HOOK(SPELL_EVENT_ON_CAST_CANCEL, spellInfo->Id);
|
||||
Push(caster);
|
||||
Push(spell);
|
||||
Push(bySelf);
|
||||
|
||||
CallAllFunctions(SpellEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnSpellCast(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool skipCheck)
|
||||
{
|
||||
START_HOOK(SPELL_EVENT_ON_CAST, spellInfo->Id);
|
||||
Push(caster);
|
||||
Push(spell);
|
||||
Push(skipCheck);
|
||||
|
||||
CallAllFunctions(SpellEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnSpellPrepare(Unit* caster, Spell* spell, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(SPELL_EVENT_ON_PREPARE, spellInfo->Id);
|
||||
Push(caster);
|
||||
Push(spell);
|
||||
|
||||
CallAllFunctions(SpellEventBindings, key);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<TicketEvents>(EVENT);\
|
||||
if (!TicketEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnTicketCreate(GmTicket* ticket)
|
||||
{
|
||||
START_HOOK(TICKET_EVENT_ON_CREATE);
|
||||
Push(ticket);
|
||||
CallAllFunctions(TicketEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnTicketUpdateLastChange(GmTicket* ticket)
|
||||
{
|
||||
START_HOOK(TICKET_EVENT_UPDATE_LAST_CHANGE);
|
||||
Push(ticket);
|
||||
CallAllFunctions(TicketEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnTicketClose(GmTicket* ticket)
|
||||
{
|
||||
START_HOOK(TICKET_EVENT_ON_CLOSE);
|
||||
Push(ticket);
|
||||
CallAllFunctions(TicketEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnTicketResolve(GmTicket* ticket)
|
||||
{
|
||||
START_HOOK(TICKET_EVENT_ON_RESOLVE);
|
||||
Push(ticket);
|
||||
CallAllFunctions(TicketEventBindings, key);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<VehicleEvents>(EVENT);\
|
||||
if (!VehicleEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnInstall(Vehicle* vehicle)
|
||||
{
|
||||
START_HOOK(VEHICLE_EVENT_ON_INSTALL);
|
||||
Push(vehicle);
|
||||
CallAllFunctions(VehicleEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnUninstall(Vehicle* vehicle)
|
||||
{
|
||||
START_HOOK(VEHICLE_EVENT_ON_UNINSTALL);
|
||||
Push(vehicle);
|
||||
CallAllFunctions(VehicleEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnInstallAccessory(Vehicle* vehicle, Creature* accessory)
|
||||
{
|
||||
START_HOOK(VEHICLE_EVENT_ON_INSTALL_ACCESSORY);
|
||||
Push(vehicle);
|
||||
Push(accessory);
|
||||
CallAllFunctions(VehicleEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAddPassenger(Vehicle* vehicle, Unit* passenger, int8 seatId)
|
||||
{
|
||||
START_HOOK(VEHICLE_EVENT_ON_ADD_PASSENGER);
|
||||
Push(vehicle);
|
||||
Push(passenger);
|
||||
Push(seatId);
|
||||
CallAllFunctions(VehicleEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnRemovePassenger(Vehicle* vehicle, Unit* passenger)
|
||||
{
|
||||
START_HOOK(VEHICLE_EVENT_ON_REMOVE_PASSENGER);
|
||||
Push(vehicle);
|
||||
Push(passenger);
|
||||
CallAllFunctions(VehicleEventBindings, key);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <memory> // std::allocator
|
||||
#include <new> // std::hardware_destructive_interference_size
|
||||
#include <stdexcept>
|
||||
#include <type_traits> // std::enable_if, std::is_*_constructible
|
||||
|
||||
namespace rigtorp {
|
||||
|
||||
template <typename T, typename Allocator = std::allocator<T>> class SPSCQueue {
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t)
|
||||
template <typename Alloc2, typename = void>
|
||||
struct has_allocate_at_least : std::false_type {};
|
||||
|
||||
template <typename Alloc2>
|
||||
struct has_allocate_at_least<
|
||||
Alloc2, std::void_t<typename Alloc2::value_type,
|
||||
decltype(std::declval<Alloc2 &>().allocate_at_least(
|
||||
size_t{}))>> : std::true_type {};
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit SPSCQueue(const size_t capacity,
|
||||
const Allocator &allocator = Allocator())
|
||||
: capacity_(capacity), allocator_(allocator) {
|
||||
// The queue needs at least one element
|
||||
if (capacity_ < 1) {
|
||||
capacity_ = 1;
|
||||
}
|
||||
capacity_++; // Needs one slack element
|
||||
// Prevent overflowing size_t
|
||||
if (capacity_ > SIZE_MAX - 2 * kPadding) {
|
||||
capacity_ = SIZE_MAX - 2 * kPadding;
|
||||
}
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t)
|
||||
if constexpr (has_allocate_at_least<Allocator>::value) {
|
||||
auto res = allocator_.allocate_at_least(capacity_ + 2 * kPadding);
|
||||
slots_ = res.ptr;
|
||||
capacity_ = res.count - 2 * kPadding;
|
||||
} else {
|
||||
slots_ = std::allocator_traits<Allocator>::allocate(
|
||||
allocator_, capacity_ + 2 * kPadding);
|
||||
}
|
||||
#else
|
||||
slots_ = std::allocator_traits<Allocator>::allocate(
|
||||
allocator_, capacity_ + 2 * kPadding);
|
||||
#endif
|
||||
|
||||
static_assert(alignof(SPSCQueue<T>) == kCacheLineSize, "");
|
||||
static_assert(sizeof(SPSCQueue<T>) >= 3 * kCacheLineSize, "");
|
||||
assert(reinterpret_cast<char *>(&readIdx_) -
|
||||
reinterpret_cast<char *>(&writeIdx_) >=
|
||||
static_cast<std::ptrdiff_t>(kCacheLineSize));
|
||||
}
|
||||
|
||||
~SPSCQueue() {
|
||||
while (front()) {
|
||||
pop();
|
||||
}
|
||||
std::allocator_traits<Allocator>::deallocate(allocator_, slots_,
|
||||
capacity_ + 2 * kPadding);
|
||||
}
|
||||
|
||||
// non-copyable and non-movable
|
||||
SPSCQueue(const SPSCQueue &) = delete;
|
||||
SPSCQueue &operator=(const SPSCQueue &) = delete;
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args) noexcept(
|
||||
std::is_nothrow_constructible<T, Args &&...>::value) {
|
||||
static_assert(std::is_constructible<T, Args &&...>::value,
|
||||
"T must be constructible with Args&&...");
|
||||
auto const writeIdx = writeIdx_.load(std::memory_order_relaxed);
|
||||
auto nextWriteIdx = writeIdx + 1;
|
||||
if (nextWriteIdx == capacity_) {
|
||||
nextWriteIdx = 0;
|
||||
}
|
||||
while (nextWriteIdx == readIdxCache_) {
|
||||
readIdxCache_ = readIdx_.load(std::memory_order_acquire);
|
||||
}
|
||||
new (&slots_[writeIdx + kPadding]) T(std::forward<Args>(args)...);
|
||||
writeIdx_.store(nextWriteIdx, std::memory_order_release);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool try_emplace(Args &&...args) noexcept(
|
||||
std::is_nothrow_constructible<T, Args &&...>::value) {
|
||||
static_assert(std::is_constructible<T, Args &&...>::value,
|
||||
"T must be constructible with Args&&...");
|
||||
auto const writeIdx = writeIdx_.load(std::memory_order_relaxed);
|
||||
auto nextWriteIdx = writeIdx + 1;
|
||||
if (nextWriteIdx == capacity_) {
|
||||
nextWriteIdx = 0;
|
||||
}
|
||||
if (nextWriteIdx == readIdxCache_) {
|
||||
readIdxCache_ = readIdx_.load(std::memory_order_acquire);
|
||||
if (nextWriteIdx == readIdxCache_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
new (&slots_[writeIdx + kPadding]) T(std::forward<Args>(args)...);
|
||||
writeIdx_.store(nextWriteIdx, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
void push(const T &v) noexcept(std::is_nothrow_copy_constructible<T>::value) {
|
||||
static_assert(std::is_copy_constructible<T>::value,
|
||||
"T must be copy constructible");
|
||||
emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = typename std::enable_if<
|
||||
std::is_constructible<T, P &&>::value>::type>
|
||||
void push(P &&v) noexcept(std::is_nothrow_constructible<T, P &&>::value) {
|
||||
emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
bool
|
||||
try_push(const T &v) noexcept(std::is_nothrow_copy_constructible<T>::value) {
|
||||
static_assert(std::is_copy_constructible<T>::value,
|
||||
"T must be copy constructible");
|
||||
return try_emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = typename std::enable_if<
|
||||
std::is_constructible<T, P &&>::value>::type>
|
||||
bool try_push(P &&v) noexcept(std::is_nothrow_constructible<T, P &&>::value) {
|
||||
return try_emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
T *front() noexcept {
|
||||
auto const readIdx = readIdx_.load(std::memory_order_relaxed);
|
||||
if (readIdx == writeIdxCache_) {
|
||||
writeIdxCache_ = writeIdx_.load(std::memory_order_acquire);
|
||||
if (writeIdxCache_ == readIdx) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return &slots_[readIdx + kPadding];
|
||||
}
|
||||
|
||||
void pop() noexcept {
|
||||
static_assert(std::is_nothrow_destructible<T>::value,
|
||||
"T must be nothrow destructible");
|
||||
auto const readIdx = readIdx_.load(std::memory_order_relaxed);
|
||||
assert(writeIdx_.load(std::memory_order_acquire) != readIdx);
|
||||
slots_[readIdx + kPadding].~T();
|
||||
auto nextReadIdx = readIdx + 1;
|
||||
if (nextReadIdx == capacity_) {
|
||||
nextReadIdx = 0;
|
||||
}
|
||||
readIdx_.store(nextReadIdx, std::memory_order_release);
|
||||
}
|
||||
|
||||
size_t size() const noexcept {
|
||||
std::ptrdiff_t diff = writeIdx_.load(std::memory_order_acquire) -
|
||||
readIdx_.load(std::memory_order_acquire);
|
||||
if (diff < 0) {
|
||||
diff += capacity_;
|
||||
}
|
||||
return static_cast<size_t>(diff);
|
||||
}
|
||||
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
size_t capacity() const noexcept { return capacity_ - 1; }
|
||||
|
||||
private:
|
||||
#if defined(__cpp_lib_hardware_interference_size) && !defined(__APPLE__)
|
||||
static constexpr size_t kCacheLineSize =
|
||||
std::hardware_destructive_interference_size;
|
||||
#else
|
||||
static constexpr size_t kCacheLineSize = 64;
|
||||
#endif
|
||||
|
||||
// Padding to avoid false sharing between slots_ and adjacent allocations
|
||||
static constexpr size_t kPadding = (kCacheLineSize - 1) / sizeof(T) + 1;
|
||||
|
||||
private:
|
||||
size_t capacity_;
|
||||
T *slots_;
|
||||
#if defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address)
|
||||
Allocator allocator_ [[no_unique_address]];
|
||||
#else
|
||||
Allocator allocator_;
|
||||
#endif
|
||||
|
||||
// Align to cache line size in order to avoid false sharing
|
||||
// readIdxCache_ and writeIdxCache_ is used to reduce the amount of cache
|
||||
// coherency traffic
|
||||
alignas(kCacheLineSize) std::atomic<size_t> writeIdx_ = {0};
|
||||
alignas(kCacheLineSize) size_t readIdxCache_ = 0;
|
||||
alignas(kCacheLineSize) std::atomic<size_t> readIdx_ = {0};
|
||||
alignas(kCacheLineSize) size_t writeIdxCache_ = 0;
|
||||
|
||||
// Padding to avoid adjacent allocations to share cache line with
|
||||
// writeIdxCache_
|
||||
char padding_[kCacheLineSize - sizeof(writeIdxCache_)];
|
||||
};
|
||||
} // namespace rigtorp
|
||||
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
* lmarshal.c
|
||||
* A Lua library for serializing and deserializing Lua values
|
||||
* Richard Hundt <richardhundt@gmail.com>, Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
#include "ALECompat.h"
|
||||
|
||||
#if LUA_VERSION_NUM == 501 && !defined(luaL_setfuncs)
|
||||
#define luaL_setfuncs(L, l, n) luaL_register(L, NULL, l)
|
||||
#endif
|
||||
|
||||
#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;
|
||||
unsigned char i;
|
||||
lua_Debug ar;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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);
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 field = ALE::CHECKVAL<uint32>(L, 2);
|
||||
uint32 count = RESULT->GetFieldCount();
|
||||
if (field >= count)
|
||||
{
|
||||
char arr[256];
|
||||
snprintf(arr, sizeof(arr), "trying to access invalid field index %u. There are %u fields available and the indexes start from 0", field, count);
|
||||
luaL_argerror(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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
|
||||
ALE::Push(L, RESULT->Fetch()[col].IsNull());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns in the result set.
|
||||
*
|
||||
* @return uint32 columnCount
|
||||
*/
|
||||
int GetColumnCount(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
ALE::Push(L, RESULT->GetFieldCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the result set.
|
||||
*
|
||||
* @return uint32 rowCount
|
||||
*/
|
||||
int GetRowCount(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
if (RESULT->GetRowCount() > (uint32)-1)
|
||||
ALE::Push(L, (uint32)-1);
|
||||
else
|
||||
ALE::Push(L, (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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<bool>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<uint8>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<uint16>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<uint32>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<uint64>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<int8>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<int16>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<int32>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<int64>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<float>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<double>());
|
||||
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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = ALE::CHECKVAL<uint32>(L, 2);
|
||||
CheckFields(L, result);
|
||||
ALE::Push(L, RESULT->Fetch()[col].Get<std::string>());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the [ALEQuery] 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(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
ALE::Push(L, 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 [ALEQuery:NextRow].
|
||||
*
|
||||
* @return table rowData : table filled with row columns and data where `T[column] = data`
|
||||
*/
|
||||
int GetRow(lua_State* L, ALEQuery* result)
|
||||
{
|
||||
uint32 col = RESULT->GetFieldCount();
|
||||
Field* row = RESULT->Fetch();
|
||||
|
||||
lua_createtable(L, 0, col);
|
||||
int tbl = lua_gettop(L);
|
||||
|
||||
for (uint32 i = 0; i < col; ++i)
|
||||
{
|
||||
ALE::Push(L, RESULT->GetFieldName(i));
|
||||
|
||||
std::string _str = row[i].Get<std::string>();
|
||||
const char* str = _str.c_str();
|
||||
if (row[i].IsNull() || !str)
|
||||
ALE::Push(L);
|
||||
else
|
||||
{
|
||||
// MYSQL_TYPE_LONGLONG Interpreted as string for lua
|
||||
switch (row[i].GetType())
|
||||
{
|
||||
case DatabaseFieldTypes::Int8:
|
||||
case DatabaseFieldTypes::Int16:
|
||||
case DatabaseFieldTypes::Int32:
|
||||
case DatabaseFieldTypes::Int64:
|
||||
case DatabaseFieldTypes::Float:
|
||||
case DatabaseFieldTypes::Double:
|
||||
ALE::Push(L, strtod(str, NULL));
|
||||
break;
|
||||
default:
|
||||
ALE::Push(L, str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lua_rawset(L, tbl);
|
||||
}
|
||||
|
||||
lua_settop(L, tbl);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#undef RESULT
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef ACHIEVEMENTMETHODS_H
|
||||
#define ACHIEVEMENTMETHODS_H
|
||||
|
||||
/***
|
||||
* Represents an entry from the game's achievement database (e.g., achievement earned for completing certain tasks).
|
||||
*
|
||||
* Inherits all methods from: none
|
||||
*/
|
||||
namespace LuaAchievement
|
||||
{
|
||||
/**
|
||||
* Returns the [Achievement]'s ID.
|
||||
*
|
||||
* @return uint32 id
|
||||
*/
|
||||
int GetId(lua_State* L, AchievementEntry* const achievement)
|
||||
{
|
||||
ALE::Push(L, achievement->ID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Achievement]'s name.
|
||||
*
|
||||
* enum LocaleConstant
|
||||
* {
|
||||
* LOCALE_enUS = 0,
|
||||
* LOCALE_koKR = 1,
|
||||
* LOCALE_frFR = 2,
|
||||
* LOCALE_deDE = 3,
|
||||
* LOCALE_zhCN = 4,
|
||||
* LOCALE_zhTW = 5,
|
||||
* LOCALE_esES = 6,
|
||||
* LOCALE_esMX = 7,
|
||||
* LOCALE_ruRU = 8
|
||||
* };
|
||||
*
|
||||
* @param [LocaleConstant] locale = DEFAULT_LOCALE : locale to return the [Achievement] name in
|
||||
* @return string name
|
||||
*/
|
||||
int GetName(lua_State* L, AchievementEntry* const achievement)
|
||||
{
|
||||
uint8 locale = ALE::CHECKVAL<uint8>(L, 2, DEFAULT_LOCALE);
|
||||
if (locale >= TOTAL_LOCALES)
|
||||
{
|
||||
return luaL_argerror(L, 2, "valid LocaleConstant expected");
|
||||
}
|
||||
|
||||
ALE::Push(L, achievement->name[locale]);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, 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
|
||||
*/
|
||||
int GetCasterLevel(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, aura->GetCaster()->GetLevel());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of time left until the [Aura] expires.
|
||||
*
|
||||
* @return int32 duration : amount of time left in milliseconds
|
||||
*/
|
||||
int GetDuration(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, aura->GetDuration());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the [Spell] that caused this [Aura] to be applied.
|
||||
*
|
||||
* @return uint32 aura_id
|
||||
*/
|
||||
int GetAuraId(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, aura->GetStackAmount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Unit] that the [Aura] has been applied to.
|
||||
*
|
||||
* @return [Unit] owner
|
||||
*/
|
||||
int GetOwner(lua_State* L, Aura* aura)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Aura* aura)
|
||||
{
|
||||
int32 duration = ALE::CHECKVAL<int32>(L, 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(lua_State* L, Aura* aura)
|
||||
{
|
||||
int32 duration = ALE::CHECKVAL<int32>(L, 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 uint32 amount
|
||||
*/
|
||||
int SetStackAmount(lua_State* L, Aura* aura)
|
||||
{
|
||||
uint8 amount = ALE::CHECKVAL<uint8>(L, 2);
|
||||
aura->SetStackAmount(amount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this [Aura] from the [Unit] it is applied to.
|
||||
*/
|
||||
int Remove(lua_State* L, Aura* aura)
|
||||
{
|
||||
aura->Remove();
|
||||
ALE::CHECKOBJ<ALEObject>(L, 1)->Invalidate();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
uint32 team = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
ALE::Push(L, bg->GetAlivePlayersCountByTeam((TeamId)team));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Map] of the [BattleGround].
|
||||
*
|
||||
* @return [Map] map
|
||||
*/
|
||||
int GetMap(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
uint32 kills = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
ALE::Push(L, bg->GetBonusHonorFromKill(kills));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end time of the [BattleGround].
|
||||
*
|
||||
* @return uint32 endTime
|
||||
*/
|
||||
int GetEndTime(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetEndTime());
|
||||
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(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
uint32 team = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
ALE::Push(L, bg->GetFreeSlotsForTeam((TeamId)team));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance ID of the [BattleGround].
|
||||
*
|
||||
* @return uint32 instanceId
|
||||
*/
|
||||
int GetInstanceId(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetInstanceID());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map ID of the [BattleGround].
|
||||
*
|
||||
* @return uint32 mapId
|
||||
*/
|
||||
int GetMapId(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMapId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type ID of the [BattleGround].
|
||||
*
|
||||
* @return [BattleGroundTypeId] typeId
|
||||
*/
|
||||
int GetTypeId(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetBgTypeID());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max allowed [Player] level of the specific [BattleGround].
|
||||
*
|
||||
* @return uint32 maxLevel
|
||||
*/
|
||||
int GetMaxLevel(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMaxLevel());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum allowed [Player] level of the specific [BattleGround].
|
||||
*
|
||||
* @return uint32 minLevel
|
||||
*/
|
||||
int GetMinLevel(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMinLevel());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum allowed [Player] count of the specific [BattleGround].
|
||||
*
|
||||
* @return uint32 maxPlayerCount
|
||||
*/
|
||||
int GetMaxPlayers(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMaxPlayersPerTeam() * 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum allowed [Player] count of the specific [BattleGround].
|
||||
*
|
||||
* @return uint32 minPlayerCount
|
||||
*/
|
||||
int GetMinPlayers(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMaxPlayersPerTeam() * 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum allowed [Player] count per team of the specific [BattleGround].
|
||||
*
|
||||
* @return uint32 maxTeamPlayerCount
|
||||
*/
|
||||
int GetMaxPlayersPerTeam(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMaxPlayersPerTeam());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum allowed [Player] count per team of the specific [BattleGround].
|
||||
*
|
||||
* @return uint32 minTeamPlayerCount
|
||||
*/
|
||||
int GetMinPlayersPerTeam(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetMinPlayersPerTeam());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the winning team of the specific [BattleGround].
|
||||
*
|
||||
* @return [Team] team
|
||||
*/
|
||||
int GetWinner(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetWinner());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the specific [BattleGround].
|
||||
*
|
||||
* @return [BattleGroundStatus] status
|
||||
*/
|
||||
int GetStatus(lua_State* L, BattleGround* bg)
|
||||
{
|
||||
ALE::Push(L, bg->GetStatus());
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef CHATHANDLERMETHODS_H
|
||||
#define CHATHANDLERMETHODS_H
|
||||
|
||||
#include "Chat.h"
|
||||
|
||||
/***
|
||||
* Provides access to in-game and console chat commands, messages, and selection context for command execution.
|
||||
*
|
||||
* Used primarily in GM scripts or command handlers to send messages, check permissions, and access selected targets.
|
||||
*
|
||||
* Inherits all methods from: none
|
||||
*/
|
||||
namespace LuaChatHandler
|
||||
{
|
||||
/**
|
||||
* Sends text to the chat handler
|
||||
*
|
||||
* @proto (text)
|
||||
* @proto (entry)
|
||||
* @param string text : text to display in chat or console
|
||||
* @param uint32 entry : id of the string to display
|
||||
*/
|
||||
int SendSysMessage(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
if (lua_isnumber(L, 2))
|
||||
{
|
||||
uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
|
||||
handler->SendSysMessage(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string text = ALE::CHECKVAL<std::string>(L, 2);
|
||||
handler->SendSysMessage(text);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [ChatHandler] comes from the console, `false` if it comes from a player
|
||||
*
|
||||
* @return bool isConsole
|
||||
*/
|
||||
int IsConsole(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->IsConsole());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] associated with the handler. Returns `nil` in the case of a console handler
|
||||
*
|
||||
* @return [Player] player
|
||||
*/
|
||||
int GetPlayer(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->GetPlayer());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to all connected players
|
||||
*
|
||||
* @param string text : text to send
|
||||
*/
|
||||
int SendGlobalSysMessage(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
std::string text = ALE::CHECKVAL<std::string>(L, 2);
|
||||
handler->SendGlobalSysMessage(text.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to all connected Game Masters
|
||||
*
|
||||
* @param string text : text to send
|
||||
*/
|
||||
int SendGlobalGMSysMessage(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
std::string text = ALE::CHECKVAL<std::string>(L, 2);
|
||||
handler->SendGlobalGMSysMessage(text.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current security level is lower than the specified [Player]'s account
|
||||
*
|
||||
* @param [Player] player
|
||||
* @param [bool] strong = false : Forces non-player accounts (security level greater than `0`) to go through the regular check if set to `true`.<br>Also, if set to `true`, the current security level will be considered as lower than the [Player]'s security level if the two levels are equal
|
||||
* @return [bool] lower
|
||||
*/
|
||||
int HasLowerSecurity(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
bool strong = ALE::CHECKVAL<bool>(L, 3);
|
||||
ALE::Push(L, handler->HasLowerSecurity(player, ObjectGuid::Empty, strong));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current security level is lower than the specified `account`'s level
|
||||
*
|
||||
* @param [uint32] account : the target account ID to compare security levels with
|
||||
* @param [bool] strong = false : Forces non-player accounts (security level greater than `0`) to go through the regular check if set to `true`.<br>Also, if set to `true`, the current security level will be considered as lower than the `account`'s security level if the two levels are equal
|
||||
* @return [bool] lower
|
||||
*/
|
||||
int HasLowerSecurityAccount(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
uint32 account = ALE::CHECKVAL<uint32>(L, 2);
|
||||
bool strong = ALE::CHECKVAL<bool>(L, 3);
|
||||
ALE::Push(L, handler->HasLowerSecurityAccount(nullptr, account, strong));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected [Player]
|
||||
*
|
||||
* @return [Player] player
|
||||
*/
|
||||
int GetSelectedPlayer(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->getSelectedPlayer());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected [Creature]
|
||||
*
|
||||
* @return [Creature] creature
|
||||
*/
|
||||
int GetSelectedCreature(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->getSelectedCreature());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected [Unit]
|
||||
*
|
||||
* @return [Unit] unit
|
||||
*/
|
||||
int GetSelectedUnit(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->getSelectedUnit());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected [WorldObject]
|
||||
*
|
||||
* @return [WorldObject] object
|
||||
*/
|
||||
int GetSelectedObject(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->getSelectedObject());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected [Player] or the current [Player] if nothing is targeted or the target is not a player
|
||||
*
|
||||
* @return [Player] player
|
||||
*/
|
||||
int GetSelectedPlayerOrSelf(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->getSelectedPlayerOrSelf());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the `securityLevel` is available
|
||||
*
|
||||
* @param [uint32] securityLevel
|
||||
* @return [bool] isAvailable
|
||||
*/
|
||||
int IsAvailable(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
uint32 securityLevel = ALE::CHECKVAL<uint32>(L, 2);
|
||||
ALE::Push(L, handler->IsAvailable(securityLevel));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if other previously called [ChatHandler] methods sent an error
|
||||
*
|
||||
* @return [bool] sentErrorMessage
|
||||
*/
|
||||
int HasSentErrorMessage(lua_State* L, ChatHandler* handler)
|
||||
{
|
||||
ALE::Push(L, handler->HasSentErrorMessage());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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(lua_State* L, Corpse* corpse)
|
||||
{
|
||||
ALE::Push(L, corpse->GetOwnerGUID());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time when the [Player] became a ghost and spawned this [Corpse].
|
||||
*
|
||||
* @return uint32 ghostTime
|
||||
*/
|
||||
int GetGhostTime(lua_State* L, Corpse* corpse)
|
||||
{
|
||||
ALE::Push(L, corpse->GetGhostTime());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [CorpseType] of a [Corpse].
|
||||
*
|
||||
* enum CorpseType
|
||||
* {
|
||||
* CORPSE_BONES = 0,
|
||||
* CORPSE_RESURRECTABLE_PVE = 1,
|
||||
* CORPSE_RESURRECTABLE_PVP = 2
|
||||
* };
|
||||
*
|
||||
* @return [CorpseType] corpseType
|
||||
*/
|
||||
int GetType(lua_State* L, Corpse* corpse)
|
||||
{
|
||||
ALE::Push(L, corpse->GetType());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "ghost time" to the current time.
|
||||
*
|
||||
* See [Corpse:GetGhostTime].
|
||||
*/
|
||||
int ResetGhostTime(lua_State* /*L*/, Corpse* corpse)
|
||||
{
|
||||
corpse->ResetGhostTime();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the [Corpse] to the database.
|
||||
*/
|
||||
int SaveToDB(lua_State* /*L*/, Corpse* corpse)
|
||||
{
|
||||
corpse->SaveToDB();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
|
||||
/***
|
||||
* Represents a game object in the world, such as doors, chests, and other interactive objects.
|
||||
*
|
||||
* 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(lua_State* L, GameObject* go)
|
||||
{
|
||||
uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
ALE::Push(L, go->hasQuest(questId));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [GameObject] is spawned
|
||||
*
|
||||
* @return bool isSpawned
|
||||
*/
|
||||
int IsSpawned(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->isSpawned());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [GameObject] is a transport
|
||||
*
|
||||
* @return bool isTransport
|
||||
*/
|
||||
int IsTransport(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->IsTransport());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [GameObject] is active
|
||||
*
|
||||
* @return bool isActive
|
||||
*/
|
||||
int IsActive(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->isActiveObject());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*int IsDestructible(lua_State* L, GameObject* go) // TODO: Implementation core side
|
||||
{
|
||||
ALE::Push(L, go->IsDestructibleBuilding());
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Returns display ID of the [GameObject]
|
||||
*
|
||||
* @return uint32 displayId
|
||||
*/
|
||||
int GetDisplayId(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->GetDisplayId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of a [GameObject]
|
||||
* Below are client side [GOState]s off of 3.3.5a
|
||||
*
|
||||
* <pre>
|
||||
* enum GOState
|
||||
* {
|
||||
* GO_STATE_ACTIVE = 0, // show in world as used and not reset (closed door open)
|
||||
* GO_STATE_READY = 1, // show in world as ready (closed door close)
|
||||
* GO_STATE_ACTIVE_ALTERNATIVE = 2 // show in world as used in alt way and not reset (closed door open by cannon fire)
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @return [GOState] goState
|
||||
*/
|
||||
int GetGoState(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->GetGoState());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [LootState] of a [GameObject]
|
||||
* Below are [LootState]s off of 3.3.5a
|
||||
*
|
||||
* <pre>
|
||||
* enum LootState
|
||||
* {
|
||||
* GO_NOT_READY = 0,
|
||||
* GO_READY, // can be ready but despawned, and then not possible activate until spawn
|
||||
* GO_ACTIVATED,
|
||||
* GO_JUST_DEACTIVATED
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @return [LootState] lootState
|
||||
*/
|
||||
int GetLootState(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->GetLootRecipientGroup());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the spawn ID for this [GameObject].
|
||||
*
|
||||
* @return uint32 spawnId
|
||||
*/
|
||||
int GetSpawnId(lua_State* L, GameObject* go)
|
||||
{
|
||||
ALE::Push(L, go->GetSpawnId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of a [GameObject]
|
||||
*
|
||||
* <pre>
|
||||
* enum GOState
|
||||
* {
|
||||
* GO_STATE_ACTIVE = 0, // show in world as used and not reset (closed door open)
|
||||
* GO_STATE_READY = 1, // show in world as ready (closed door close)
|
||||
* GO_STATE_ACTIVE_ALTERNATIVE = 2 // show in world as used in alt way and not reset (closed door open by cannon fire)
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @param [GOState] state : all available go states can be seen above
|
||||
*/
|
||||
int SetGoState(lua_State* L, GameObject* go)
|
||||
{
|
||||
uint32 state = ALE::CHECKVAL<uint32>(L, 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_ACTIVE_ALTERNATIVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [LootState] of a [GameObject]
|
||||
* Below are [LootState]s off of 3.3.5a
|
||||
*
|
||||
* <pre>
|
||||
* enum LootState
|
||||
* {
|
||||
* GO_NOT_READY = 0,
|
||||
* GO_READY, // can be ready but despawned, and then not possible activate until spawn
|
||||
* GO_ACTIVATED,
|
||||
* GO_JUST_DEACTIVATED
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @param [LootState] state : all available loot states can be seen above
|
||||
*/
|
||||
int SetLootState(lua_State* L, GameObject* go)
|
||||
{
|
||||
uint32 state = ALE::CHECKVAL<uint32>(L, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an [Item] to the loot of a [GameObject]
|
||||
* Requires an gameobject with loot_template set to 0.
|
||||
*
|
||||
* @param uint32 entry : The entry of the [Item]
|
||||
* @param uint32 amount = 1 : amount of the [Item] to add to the loot
|
||||
* @return uint32 itemGUIDlow : low GUID of the [Item]
|
||||
*/
|
||||
int AddLoot(lua_State* L, GameObject* go)
|
||||
{
|
||||
int i = 1;
|
||||
int argAmount = lua_gettop(L);
|
||||
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
uint8 addedItems = 0;
|
||||
while (i + 2 <= argAmount)
|
||||
{
|
||||
uint32 entry = ALE::CHECKVAL<uint32>(L, ++i);
|
||||
uint32 amount = ALE::CHECKVAL<uint32>(L, ++i);
|
||||
|
||||
ItemTemplate const* item_proto = eObjectMgr->GetItemTemplate(entry);
|
||||
if (!item_proto)
|
||||
{
|
||||
luaL_error(L, "Item entry %d does not exist", entry);
|
||||
continue;
|
||||
}
|
||||
if (amount < 1 || (item_proto->MaxCount > 0 && amount > uint32(item_proto->MaxCount)))
|
||||
{
|
||||
luaL_error(L, "Item entry %d has invalid amount %d", entry, amount);
|
||||
continue;
|
||||
}
|
||||
if (Item* item = Item::CreateItem(entry, amount))
|
||||
{
|
||||
item->SaveToDB(trans);
|
||||
LootStoreItem storeItem(item->GetEntry(), 0, 100, 0, LOOT_MODE_DEFAULT, 0, item->GetCount(), item->GetCount());
|
||||
go->loot.AddItem(storeItem);
|
||||
ALE::Push(L, item->GetGUID().GetCounter());
|
||||
++addedItems;
|
||||
}
|
||||
}
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
return addedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves [GameObject] to the database
|
||||
*
|
||||
*/
|
||||
int SaveToDB(lua_State* /*L*/, GameObject* go)
|
||||
{
|
||||
go->SaveToDB();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(lua_State* L, GameObject* go)
|
||||
{
|
||||
bool deldb = ALE::CHECKVAL<bool>(L, 2, false);
|
||||
|
||||
// cs_gobject.cpp copy paste
|
||||
ObjectGuid ownerGuid = go->GetOwnerGUID();
|
||||
if (ownerGuid)
|
||||
{
|
||||
Unit* owner = eObjectAccessor()GetUnit(*go, ownerGuid);
|
||||
if (!owner || !ownerGuid.IsPlayer())
|
||||
return 0;
|
||||
|
||||
owner->RemoveGameObject(go, false);
|
||||
}
|
||||
|
||||
if (deldb)
|
||||
go->DeleteFromDB();
|
||||
|
||||
go->SetRespawnTime(0);
|
||||
go->Delete();
|
||||
|
||||
ALE::CHECKOBJ<ALEObject>(L, 1)->Invalidate();
|
||||
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(lua_State* L, GameObject* go)
|
||||
{
|
||||
uint32 delay = ALE::CHECKVAL<uint32>(L, 2, 0);
|
||||
|
||||
go->UseDoorOrButton(delay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Despawns a [GameObject]
|
||||
*
|
||||
* The gameobject may be automatically respawned by the core
|
||||
*/
|
||||
int Despawn(lua_State* /*L*/, GameObject* go)
|
||||
{
|
||||
go->SetLootState(GO_JUST_DEACTIVATED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Respawns a [GameObject]
|
||||
*/
|
||||
int Respawn(lua_State* /*L*/, 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(lua_State* L, GameObject* go)
|
||||
{
|
||||
int32 respawn = ALE::CHECKVAL<int32>(L, 2);
|
||||
|
||||
go->SetRespawnTime(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 SetRespawnDelay(lua_State* L, GameObject* go)
|
||||
{
|
||||
int32 respawn = ALE::CHECKVAL<int32>(L, 2);
|
||||
|
||||
go->SetRespawnDelay(respawn);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef GEMPROPERTIESENTRYMETHODS_H
|
||||
#define GEMPROPERTIESENTRYMETHODS_H
|
||||
|
||||
/***
|
||||
* Represents static gem data used in item enhancement, including spell enchantments triggered by socketed gems.
|
||||
*
|
||||
* Provides access to gem-related properties from the DBC table `GemProperties.dbc`.
|
||||
*
|
||||
* Inherits all methods from: none
|
||||
*/
|
||||
namespace LuaGemPropertiesEntry
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the ID of a [GemPropertiesEntry].
|
||||
*
|
||||
* This method retrieves the ID from a given GemPropertiesEntry instance
|
||||
* and pushes it onto the Lua stack.
|
||||
*
|
||||
* @return uint32 id : The ID of the specified GemPropertiesEntry.
|
||||
*/
|
||||
int GetId(lua_State* L, GemPropertiesEntry* gemProperties)
|
||||
{
|
||||
ALE::Push(L, gemProperties->ID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the spell item enchantment of a [GemPropertiesEntry].
|
||||
*
|
||||
* This function retrieves the `spellitemenchantement` attribute from the provided `GemPropertiesEntry`.
|
||||
*
|
||||
* @return uint32 spellitemenchantement : The spell item enchantment ID.
|
||||
*/
|
||||
int GetSpellItemEnchantement(lua_State* L, GemPropertiesEntry* entry)
|
||||
{
|
||||
ALE::Push(L, entry->spellitemenchantement);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
|
||||
/***
|
||||
* Represents a player group in the game, such as a party or raid.
|
||||
*
|
||||
* 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(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
ALE::Push(L, group->IsLeader(guid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Group] is full
|
||||
*
|
||||
* @return bool isFull
|
||||
*/
|
||||
int IsFull(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, group->IsFull());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Group] is a LFG group
|
||||
*
|
||||
* @return bool isLFGGroup
|
||||
*/
|
||||
int IsLFGGroup(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, group->isLFGGroup());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Group] is a raid [Group]
|
||||
*
|
||||
* @return bool isRaid
|
||||
*/
|
||||
int IsRaidGroup(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, group->isRaidGroup());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Group] is a battleground [Group]
|
||||
*
|
||||
* @return bool isBG
|
||||
*/
|
||||
int IsBGGroup(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
ALE::Push(L, 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(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
ALE::Push(L, 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(lua_State* L, Group* group)
|
||||
{
|
||||
Player* player1 = ALE::CHECKOBJ<Player>(L, 2);
|
||||
Player* player2 = ALE::CHECKOBJ<Player>(L, 3);
|
||||
ALE::Push(L, 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(lua_State* L, Group* group)
|
||||
{
|
||||
uint8 subGroup = ALE::CHECKVAL<uint8>(L, 2);
|
||||
|
||||
if (subGroup >= MAX_RAID_SUBGROUPS)
|
||||
{
|
||||
luaL_argerror(L, 2, "valid subGroup ID expected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ALE::Push(L, group->HasFreeSlotSubGroup(subGroup));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new member to the [Group]
|
||||
*
|
||||
* @param [Player] player : [Player] to add to the group
|
||||
* @return bool added : true if member was added
|
||||
*/
|
||||
int AddMember(lua_State* L, Group* group)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
|
||||
if (player->GetGroup() || !group->IsCreated() || group->IsFull())
|
||||
{
|
||||
ALE::Push(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (player->GetGroupInvite())
|
||||
player->UninviteFromGroup();
|
||||
|
||||
bool success = group->AddMember(player);
|
||||
if (success)
|
||||
group->BroadcastGroupUpdate();
|
||||
|
||||
ALE::Push(L, success);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*int IsLFGGroup(lua_State* L, Group* group) // TODO: Implementation
|
||||
{
|
||||
ALE::Push(L, group->isLFGGroup());
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
/*int IsBFGroup(lua_State* L, Group* group) // TODO: Implementation
|
||||
{
|
||||
ALE::Push(L, group->isBFGroup());
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Returns a table with the [Player]s in this [Group]
|
||||
*
|
||||
* @return table groupPlayers : table of [Player]s
|
||||
*/
|
||||
int GetMembers(lua_State* L, Group* group)
|
||||
{
|
||||
lua_newtable(L);
|
||||
int tbl = lua_gettop(L);
|
||||
uint32 i = 0;
|
||||
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
|
||||
if (!member || !member->GetSession())
|
||||
continue;
|
||||
|
||||
ALE::Push(L, member);
|
||||
lua_rawseti(L, tbl, ++i);
|
||||
}
|
||||
|
||||
lua_settop(L, tbl); // push table to top of stack
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [Group] leader GUID
|
||||
*
|
||||
* @return ObjectGuid leaderGUID
|
||||
*/
|
||||
int GetLeaderGUID(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, group->GetLeaderGUID());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Group]'s GUID
|
||||
*
|
||||
* @return ObjectGuid groupGUID
|
||||
*/
|
||||
int GetGUID(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Group* group)
|
||||
{
|
||||
const char* name = ALE::CHECKVAL<const char*>(L, 2);
|
||||
|
||||
ALE::Push(L, group->GetMemberGUID(name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the member count of this [Group]
|
||||
*
|
||||
* @return uint32 memberCount
|
||||
*/
|
||||
int GetMembersCount(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, group->GetMembersCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this [Group]
|
||||
*
|
||||
* <pre>
|
||||
* enum GroupType
|
||||
* {
|
||||
* GROUPTYPE_NORMAL = 0,
|
||||
* GROUPTYPE_BG = 1,
|
||||
* GROUPTYPE_RAID = 2,
|
||||
* GROUPTYPE_LFG_RESTRICTED = 4,
|
||||
* GROUPTYPE_LFG = 8
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @return [GroupType] groupType
|
||||
*/
|
||||
int GetGroupType(lua_State* L, Group* group)
|
||||
{
|
||||
ALE::Push(L, group->GetGroupType());
|
||||
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(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
ALE::Push(L, group->GetMemberGroup(guid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the leader of this [Group]
|
||||
*
|
||||
* @param ObjectGuid guid : guid of the new leader
|
||||
*/
|
||||
int SetLeader(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 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(lua_State* L, Group* group)
|
||||
{
|
||||
WorldPacket* data = ALE::CHECKOBJ<WorldPacket>(L, 2);
|
||||
bool ignorePlayersInBg = ALE::CHECKVAL<bool>(L, 3);
|
||||
ObjectGuid ignore = ALE::CHECKVAL<ObjectGuid>(L, 4);
|
||||
|
||||
group->BroadcastPacket(data, ignorePlayersInBg, -1, ignore);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a [Player] from this [Group] and returns 'true' if successful
|
||||
*
|
||||
* <pre>
|
||||
* enum RemoveMethod
|
||||
* {
|
||||
* GROUP_REMOVEMETHOD_DEFAULT = 0,
|
||||
* GROUP_REMOVEMETHOD_KICK = 1,
|
||||
* GROUP_REMOVEMETHOD_LEAVE = 2,
|
||||
* GROUP_REMOVEMETHOD_KICK_LFG = 3
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @param ObjectGuid guid : guid of the player to remove
|
||||
* @param [RemoveMethod] method : method used to remove the player
|
||||
* @return bool removed
|
||||
*/
|
||||
int RemoveMember(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
uint32 method = ALE::CHECKVAL<uint32>(L, 3, 0);
|
||||
|
||||
ALE::Push(L, group->RemoveMember(guid, (RemoveMethod)method));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disbands this [Group]
|
||||
*
|
||||
*/
|
||||
int Disband(lua_State* /*L*/, Group* group)
|
||||
{
|
||||
group->Disband();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this [Group] to a raid [Group]
|
||||
*
|
||||
*/
|
||||
int ConvertToRaid(lua_State* /*L*/, Group* group)
|
||||
{
|
||||
group->ConvertToRaid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the member's subGroup
|
||||
*
|
||||
* @param ObjectGuid guid : guid of the player to move
|
||||
* @param uint8 groupID : the subGroup's ID
|
||||
*/
|
||||
int SetMembersGroup(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
uint8 subGroup = ALE::CHECKVAL<uint8>(L, 3);
|
||||
|
||||
if (subGroup >= MAX_RAID_SUBGROUPS)
|
||||
{
|
||||
luaL_argerror(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]
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
int SetTargetIcon(lua_State* L, Group* group)
|
||||
{
|
||||
uint8 icon = ALE::CHECKVAL<uint8>(L, 2);
|
||||
ObjectGuid target = ALE::CHECKVAL<ObjectGuid>(L, 3);
|
||||
ObjectGuid setter = ALE::CHECKVAL<ObjectGuid>(L, 4, ObjectGuid());
|
||||
|
||||
if (icon >= TARGETICONCOUNT)
|
||||
return luaL_argerror(L, 2, "valid target icon expected");
|
||||
|
||||
group->SetTargetIcon(icon, setter, target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or removes a flag for a [Group] member
|
||||
*
|
||||
* <pre>
|
||||
* enum GroupMemberFlags
|
||||
* {
|
||||
* MEMBER_FLAG_ASSISTANT = 0x01,
|
||||
* MEMBER_FLAG_MAINTANK = 0x02,
|
||||
* MEMBER_FLAG_MAINASSIST = 0x04,
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @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(lua_State* L, Group* group)
|
||||
{
|
||||
ObjectGuid target = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
bool apply = ALE::CHECKVAL<bool>(L, 3);
|
||||
GroupMemberFlags flag = static_cast<GroupMemberFlags>(ALE::CHECKVAL<uint32>(L, 4));
|
||||
|
||||
group->SetGroupMemberFlag(target, apply, flag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*int ConvertToLFG(lua_State* L, Group* group) // TODO: Implementation
|
||||
{
|
||||
group->ConvertToLFG();
|
||||
return 0;
|
||||
}*/
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
|
||||
/***
|
||||
* Represents a player guild. Used to manage guild members, ranks, guild bank.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @return table guildPlayers : table of [Player]s
|
||||
*/
|
||||
int GetMembers(lua_State* L, Guild* guild)
|
||||
{
|
||||
lua_newtable(L);
|
||||
int tbl = lua_gettop(L);
|
||||
uint32 i = 0;
|
||||
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(*HashMapHolder<Player>::GetLock());
|
||||
const HashMapHolder<Player>::MapType& m = eObjectAccessor()GetPlayers();
|
||||
for (HashMapHolder<Player>::MapType::const_iterator it = m.begin(); it != m.end(); ++it)
|
||||
{
|
||||
if (Player* player = it->second)
|
||||
{
|
||||
if (player->IsInWorld() && player->GetGuildId() == guild->GetId())
|
||||
{
|
||||
ALE::Push(L, player);
|
||||
lua_rawseti(L, tbl, ++i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua_settop(L, tbl); // push table to top of stack
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the member count of this [Guild]
|
||||
*
|
||||
* @return uint32 memberCount
|
||||
*/
|
||||
int GetMemberCount(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetMemberCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the [Guild] leader by their GUID if logged in
|
||||
*
|
||||
* @return [Player] leader
|
||||
*/
|
||||
int GetLeader(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, eObjectAccessor()FindPlayer(guild->GetLeaderGUID()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [Guild] leader GUID
|
||||
*
|
||||
* @return ObjectGuid leaderGUID
|
||||
*/
|
||||
int GetLeaderGUID(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetLeaderGUID());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Guild]s entry ID
|
||||
*
|
||||
* @return uint32 entryId
|
||||
*/
|
||||
int GetId(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Guild]s name
|
||||
*
|
||||
* @return string guildName
|
||||
*/
|
||||
int GetName(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetName());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Guild]s current Message Of The Day
|
||||
*
|
||||
* @return string guildMOTD
|
||||
*/
|
||||
int GetMOTD(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetMOTD());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Guild]s current info
|
||||
*
|
||||
* @return string guildInfo
|
||||
*/
|
||||
int GetInfo(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetInfo());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the leader of this [Guild]
|
||||
*
|
||||
* @param [Player] leader : the [Player] leader to change
|
||||
*/
|
||||
int SetLeader(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
|
||||
guild->HandleSetLeader(player->GetSession(), player->GetName());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the information of the bank tab specified
|
||||
*
|
||||
* @param uint8 tabId : the ID of the tab specified
|
||||
* @param string info : the information to be set to the bank tab
|
||||
*/
|
||||
int SetBankTabText(lua_State* L, Guild* guild)
|
||||
{
|
||||
uint8 tabId = ALE::CHECKVAL<uint8>(L, 2);
|
||||
const char* text = ALE::CHECKVAL<const char*>(L, 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(lua_State* L, Guild* guild)
|
||||
{
|
||||
WorldPacket* data = ALE::CHECKOBJ<WorldPacket>(L, 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(lua_State* L, Guild* guild)
|
||||
{
|
||||
WorldPacket* data = ALE::CHECKOBJ<WorldPacket>(L, 2);
|
||||
uint8 ranked = ALE::CHECKVAL<uint8>(L, 3);
|
||||
|
||||
guild->BroadcastPacketToRank(data, ranked);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disbands the [Guild]
|
||||
*/
|
||||
int Disband(lua_State* /*L*/, 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.
|
||||
*
|
||||
* @param [Player] player : the [Player] to be added to the guild
|
||||
* @param uint8 rankId : the rank ID
|
||||
*/
|
||||
int AddMember(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
uint8 rankId = ALE::CHECKVAL<uint8>(L, 3, GUILD_RANK_NONE);
|
||||
|
||||
guild->AddMember(player->GET_GUID(), rankId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified [Player] from the [Guild].
|
||||
*
|
||||
* @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(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
bool isDisbanding = ALE::CHECKVAL<bool>(L, 3, false);
|
||||
|
||||
guild->DeleteMember(player->GET_GUID(), isDisbanding);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes/demotes the [Player] to the specified rank.
|
||||
*
|
||||
* @param [Player] player : the [Player] to be promoted/demoted
|
||||
* @param uint8 rankId : the rank ID
|
||||
*/
|
||||
int SetMemberRank(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
uint8 newRank = ALE::CHECKVAL<uint8>(L, 3);
|
||||
|
||||
guild->ChangeMemberRank(player->GET_GUID(), newRank);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new name of the specified [Guild].
|
||||
*
|
||||
* @param string name : new name of this guild
|
||||
*/
|
||||
int SetName(lua_State* L, Guild* guild)
|
||||
{
|
||||
std::string name = ALE::CHECKVAL<std::string>(L, 2);
|
||||
|
||||
guild->SetName(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update [Player] data in [Guild] member list.
|
||||
*
|
||||
* enum GuildMemberData
|
||||
* {
|
||||
* GUILD_MEMBER_DATA_ZONEID = 0
|
||||
* GUILD_MEMBER_DATA_LEVEL = 1
|
||||
* };
|
||||
*
|
||||
* @param [Player] player : plkayer you need to update data
|
||||
* @param [GuildMemberData] dataid : data you need to update
|
||||
* @param uint32 value
|
||||
*/
|
||||
int UpdateMemberData(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
uint8 dataid = ALE::CHECKVAL<uint8>(L, 3);
|
||||
uint32 value = ALE::CHECKVAL<uint32>(L, 4);
|
||||
|
||||
guild->UpdateMemberData(player, dataid, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to [Guild] from specific [Player].
|
||||
*
|
||||
* @param [Player] player : the [Player] is the author of the message
|
||||
* @param bool officerOnly : send message only on officer channel
|
||||
* @param string msg : the message you need to send
|
||||
* @param uint32 lang : language the [Player] will speak
|
||||
*/
|
||||
int SendMessage(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
bool officerOnly = ALE::CHECKVAL<bool>(L, 3, false);
|
||||
std::string msg = ALE::CHECKVAL<std::string>(L, 4);
|
||||
uint32 language = ALE::CHECKVAL<uint32>(L, 5, false);
|
||||
|
||||
guild->BroadcastToGuild(player->GetSession(), officerOnly, msg, language);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invites [Guild] members to events based on level and rank filters.
|
||||
*
|
||||
* @param [Player] player : who sends the invitation
|
||||
* @param uint32 minLevel : the required min level
|
||||
* @param uint32 maxLevel : the required max level
|
||||
* @param uint32 minRank : the required min rank
|
||||
*/
|
||||
int MassInviteToEvent(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
uint32 minLevel = ALE::CHECKVAL<uint32>(L, 3);
|
||||
uint32 maxLevel = ALE::CHECKVAL<uint32>(L, 4);
|
||||
uint32 minRank = ALE::CHECKVAL<uint32>(L, 5);
|
||||
|
||||
guild->MassInviteToEvent(player->GetSession(), minLevel, maxLevel, minRank);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap item from a specific tab and slot [Guild] bank to another one.
|
||||
*
|
||||
* @param [Player] player : who Swap the item
|
||||
* @param uint8 tabId : source tab id
|
||||
* @param uint8 slotId : source slot id
|
||||
* @param uint8 destTabId : destination tab id
|
||||
* @param uint8 destSlotId : destination slot id
|
||||
* @param uint8 splitedAmount : if the item is stackable, how much should be swaped
|
||||
*/
|
||||
int SwapItems(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
uint8 tabId = ALE::CHECKVAL<uint32>(L, 3);
|
||||
uint8 slotId = ALE::CHECKVAL<uint32>(L, 4);
|
||||
uint8 destTabId = ALE::CHECKVAL<uint32>(L, 5);
|
||||
uint8 destSlotId = ALE::CHECKVAL<uint32>(L, 6);
|
||||
uint32 splitedAmount = ALE::CHECKVAL<uint32>(L, 7);
|
||||
|
||||
guild->SwapItems(player, tabId, slotId, destTabId, destSlotId, splitedAmount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap an item from a specific tab and location in the [guild] bank to the bags and locations in the inventory of a specific [player] and vice versa.
|
||||
*
|
||||
* @param [Player] player : who Swap the item
|
||||
* @param bool toChar : the item goes to the [Player]'s inventory or comes from the [Player]'s inventory
|
||||
* @param uint8 tabId : tab id
|
||||
* @param uint8 slotId : slot id
|
||||
* @param uint8 playerBag : bag id
|
||||
* @param uint8 playerSlotId : slot id
|
||||
* @param uint32 splitedAmount : if the item is stackable, how much should be swaped
|
||||
*/
|
||||
int SwapItemsWithInventory(lua_State* L, Guild* guild)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
bool toChar = ALE::CHECKVAL<bool>(L, 3, false);
|
||||
uint8 tabId = ALE::CHECKVAL<uint8>(L, 4);
|
||||
uint8 slotId = ALE::CHECKVAL<uint8>(L, 5);
|
||||
uint8 playerBag = ALE::CHECKVAL<uint8>(L, 6);
|
||||
uint8 playerSlotId = ALE::CHECKVAL<uint8>(L, 7);
|
||||
uint32 splitedAmount = ALE::CHECKVAL<uint32>(L, 8);
|
||||
|
||||
guild->SwapItemsWithInventory(player, toChar, tabId, slotId, playerBag, playerSlotId, splitedAmount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total bank money.
|
||||
*
|
||||
* @return number totalBankMoney
|
||||
*/
|
||||
int GetTotalBankMoney(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetTotalBankMoney());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the created date.
|
||||
*
|
||||
* @return uint64 created date
|
||||
*/
|
||||
int GetCreatedDate(lua_State* L, Guild* guild)
|
||||
{
|
||||
ALE::Push(L, guild->GetCreatedDate());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the number of item withdraw in all tab's for all [Guild] members.
|
||||
*/
|
||||
int ResetTimes(lua_State* /*L*/, Guild* guild)
|
||||
{
|
||||
guild->ResetTimes();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the [Guild] bank money. You can deposit or withdraw.
|
||||
*
|
||||
* @param uint64 amount : amount to add or remove
|
||||
* @param bool add : true (add money) | false (withdraw money)
|
||||
* @return bool is_applied
|
||||
*/
|
||||
int ModifyBankMoney(lua_State* L, Guild* guild)
|
||||
{
|
||||
uint64 amount = ALE::CHECKVAL<uint64>(L, 2);
|
||||
bool add = ALE::CHECKVAL<bool>(L, 2);
|
||||
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
ALE::Push(L, guild->ModifyBankMoney(trans, amount, add));
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
|
||||
/***
|
||||
* Represents an instance of an item in the game world.
|
||||
*
|
||||
* Inherits all methods from: [Object]
|
||||
*/
|
||||
namespace LuaItem
|
||||
{
|
||||
/**
|
||||
* Returns 'true' if the [Item] is soulbound, 'false' otherwise
|
||||
*
|
||||
* @return bool isSoulBound
|
||||
*/
|
||||
int IsSoulBound(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsSoulBound());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is account bound, 'false' otherwise
|
||||
*
|
||||
* @return bool isAccountBound
|
||||
*/
|
||||
int IsBoundAccountWide(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsBoundAccountWide());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is bound to a [Player] by an enchant, 'false' otehrwise
|
||||
*
|
||||
* @return bool isBoundByEnchant
|
||||
*/
|
||||
int IsBoundByEnchant(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Item* item)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
|
||||
ALE::Push(L, item->IsBindedNotWith(player));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is locked, 'false' otherwise
|
||||
*
|
||||
* @return bool isLocked
|
||||
*/
|
||||
int IsLocked(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsLocked());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is a bag, 'false' otherwise
|
||||
*
|
||||
* @return bool isBag
|
||||
*/
|
||||
int IsBag(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsBag());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is a currency token, 'false' otherwise
|
||||
*
|
||||
* @return bool isCurrencyToken
|
||||
*/
|
||||
int IsCurrencyToken(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsCurrencyToken());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is a not an empty bag, 'false' otherwise
|
||||
*
|
||||
* @return bool isNotEmptyBag
|
||||
*/
|
||||
int IsNotEmptyBag(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsNotEmptyBag());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is broken, 'false' otherwise
|
||||
*
|
||||
* @return bool isBroken
|
||||
*/
|
||||
int IsBroken(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsBroken());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] can be traded, 'false' otherwise
|
||||
*
|
||||
* @return bool isTradeable
|
||||
*/
|
||||
int CanBeTraded(lua_State* L, Item* item)
|
||||
{
|
||||
bool mail = ALE::CHECKVAL<bool>(L, 2, false);
|
||||
ALE::Push(L, item->CanBeTraded(mail));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is currently in a trade window, 'false' otherwise
|
||||
*
|
||||
* @return bool isInTrade
|
||||
*/
|
||||
int IsInTrade(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsInTrade());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is currently in a bag, 'false' otherwise
|
||||
*
|
||||
* @return bool isInBag
|
||||
*/
|
||||
int IsInBag(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsInBag());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is currently equipped, 'false' otherwise
|
||||
*
|
||||
* @return bool isEquipped
|
||||
*/
|
||||
int IsEquipped(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 quest = ALE::CHECKVAL<uint32>(L, 2);
|
||||
ALE::Push(L, item->hasQuest(quest));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is a potion, 'false' otherwise
|
||||
*
|
||||
* @return bool isPotion
|
||||
*/
|
||||
int IsPotion(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsPotion());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is a weapon vellum, 'false' otherwise
|
||||
*
|
||||
* @return bool isWeaponVellum
|
||||
*/
|
||||
int IsWeaponVellum(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsWeaponVellum());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is an armor vellum, 'false' otherwise
|
||||
*
|
||||
* @return bool isArmorVellum
|
||||
*/
|
||||
int IsArmorVellum(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsArmorVellum());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Item] is a conjured consumable, 'false' otherwise
|
||||
*
|
||||
* @return bool isConjuredConsumable
|
||||
*/
|
||||
int IsConjuredConsumable(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->IsConjuredConsumable());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*int IsRefundExpired(lua_State* L, Item* item)// TODO: Implement core support
|
||||
{
|
||||
ALE::Push(L, item->IsRefundExpired());
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Returns the chat link of the [Item]
|
||||
*
|
||||
* <pre>
|
||||
* enum LocaleConstant
|
||||
* {
|
||||
* LOCALE_enUS = 0,
|
||||
* LOCALE_koKR = 1,
|
||||
* LOCALE_frFR = 2,
|
||||
* LOCALE_deDE = 3,
|
||||
* LOCALE_zhCN = 4,
|
||||
* LOCALE_zhTW = 5,
|
||||
* LOCALE_esES = 6,
|
||||
* LOCALE_esMX = 7,
|
||||
* LOCALE_ruRU = 8
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @param [LocaleConstant] locale = DEFAULT_LOCALE : locale to return the [Item]'s name in
|
||||
* @return string itemLink
|
||||
*/
|
||||
int GetItemLink(lua_State* L, Item* item)
|
||||
{
|
||||
uint8 locale = ALE::CHECKVAL<uint8>(L, 2, DEFAULT_LOCALE);
|
||||
if (locale >= TOTAL_LOCALES)
|
||||
return luaL_argerror(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<LocaleConstant>(locale), name);
|
||||
}
|
||||
|
||||
if (int32 itemRandPropId = item->GetItemRandomPropertyId())
|
||||
{
|
||||
std::array<char const*, 16> 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)
|
||||
{
|
||||
const char* suffixName = (*suffix)[(name != temp->Name1) ? locale : uint8(DEFAULT_LOCALE)];
|
||||
if (strcmp(suffixName, "") != 0)
|
||||
{
|
||||
name += ' ';
|
||||
name += suffixName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Player* owner = item->GetOwner();
|
||||
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)(owner ? owner->GetLevel() : 0) << "|h[" << name << "]|h|r";
|
||||
|
||||
ALE::Push(L, oss.str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GUID of the [Player] who owns the specified [Item].
|
||||
*
|
||||
* @param [Item] item
|
||||
* @return uint64 ownerGUID
|
||||
*/
|
||||
int GetOwnerGUID(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetOwnerGUID());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] who currently owns the [Item]
|
||||
*
|
||||
* @return [Player] player : the [Player] who owns the [Item]
|
||||
*/
|
||||
int GetOwner(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetOwner());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Item]s stack count
|
||||
*
|
||||
* @return uint32 count
|
||||
*/
|
||||
int GetCount(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Item]s max stack count
|
||||
*
|
||||
* @return uint32 maxCount
|
||||
*/
|
||||
int GetMaxStackCount(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetMaxStackCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Item]s current slot
|
||||
*
|
||||
* @return uint8 slot
|
||||
*/
|
||||
int GetSlot(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetSlot());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Item]s current bag slot
|
||||
*
|
||||
* @return uint8 bagSlot
|
||||
*/
|
||||
int GetBagSlot(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 enchant_slot = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
if (enchant_slot >= MAX_INSPECTED_ENCHANTMENT_SLOT)
|
||||
return luaL_argerror(L, 2, "valid EnchantmentSlot expected");
|
||||
|
||||
ALE::Push(L, 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(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 index = ALE::CHECKVAL<uint32>(L, 2);
|
||||
if (index >= MAX_ITEM_PROTO_SPELLS)
|
||||
return luaL_argerror(L, 2, "valid SpellIndex expected");
|
||||
|
||||
ALE::Push(L, item->GetTemplate()->Spells[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(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 index = ALE::CHECKVAL<uint32>(L, 2);
|
||||
if (index >= MAX_ITEM_PROTO_SPELLS)
|
||||
return luaL_argerror(L, 2, "valid SpellIndex expected");
|
||||
|
||||
ALE::Push(L, item->GetTemplate()->Spells[index].SpellTrigger);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class of the [Item]
|
||||
*
|
||||
* @return uint32 class
|
||||
*/
|
||||
int GetClass(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->Class);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subclass of the [Item]
|
||||
*
|
||||
* @return uint32 subClass
|
||||
*/
|
||||
int GetSubClass(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->SubClass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the [Item]
|
||||
*
|
||||
* @return string name
|
||||
*/
|
||||
int GetName(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->Name1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display ID of the [Item]
|
||||
*
|
||||
* @return uint32 displayId
|
||||
*/
|
||||
int GetDisplayId(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->DisplayInfoID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quality of the [Item]
|
||||
*
|
||||
* @return uint32 quality
|
||||
*/
|
||||
int GetQuality(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->Quality);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default purchase count of the [Item]
|
||||
*
|
||||
* @return uint32 count
|
||||
*/
|
||||
int GetBuyCount(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->BuyCount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the purchase price of the [Item]
|
||||
*
|
||||
* @return uint32 price
|
||||
*/
|
||||
int GetBuyPrice(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->BuyPrice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sell price of the [Item]
|
||||
*
|
||||
* @return uint32 price
|
||||
*/
|
||||
int GetSellPrice(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->SellPrice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inventory type of the [Item]
|
||||
*
|
||||
* @return uint32 inventoryType
|
||||
*/
|
||||
int GetInventoryType(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->InventoryType);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] classes allowed to use this [Item]
|
||||
*
|
||||
* @return uint32 allowableClass
|
||||
*/
|
||||
int GetAllowableClass(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->AllowableClass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] races allowed to use this [Item]
|
||||
*
|
||||
* @return uint32 allowableRace
|
||||
*/
|
||||
int GetAllowableRace(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->AllowableRace);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Item]s level
|
||||
*
|
||||
* @return uint32 itemLevel
|
||||
*/
|
||||
int GetItemLevel(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->ItemLevel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum level required to use this [Item]
|
||||
*
|
||||
* @return uint32 requiredLevel
|
||||
*/
|
||||
int GetRequiredLevel(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->RequiredLevel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of stat entries defined on the [Item]'s [ItemTemplate]. This reflects how many stat slots (e.g., Strength, Stamina, etc.) are defined for the item.
|
||||
*
|
||||
* @param [Item] item
|
||||
* @return uint32 statsCount
|
||||
*/
|
||||
int GetStatsCount(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->StatsCount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the random property ID of this [Item]
|
||||
*
|
||||
* @return uint32 randomPropertyId
|
||||
*/
|
||||
int GetRandomProperty(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->RandomProperty);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the random suffix ID of the specified [Item]. This corresponds to the `RandomSuffix` field from the item's [ItemTemplate], which controls the applied suffix (e.g., "of the Bear", "of the Eagle").
|
||||
*
|
||||
* @param [Item] item
|
||||
* @return uint32 randomSuffixId
|
||||
*/
|
||||
int GetRandomSuffix(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->RandomSuffix);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item set ID of this [Item]
|
||||
*
|
||||
* @return uint32 itemSetId
|
||||
*/
|
||||
int GetItemSet(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate()->ItemSet);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bag size of this [Item], 0 if [Item] is not a bag
|
||||
*
|
||||
* @return uint32 bagSize
|
||||
*/
|
||||
int GetBagSize(lua_State* L, Item* item)
|
||||
{
|
||||
if (Bag* bag = item->ToBag())
|
||||
ALE::Push(L, bag->GetBagSize());
|
||||
else
|
||||
ALE::Push(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate] for this [Item].
|
||||
*
|
||||
* @return [ItemTemplate] itemTemplate
|
||||
*/
|
||||
int GetItemTemplate(lua_State* L, Item* item)
|
||||
{
|
||||
ALE::Push(L, item->GetTemplate());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [Player] specified as the owner of the [Item]
|
||||
*
|
||||
* @param [Player] player : the [Player] specified
|
||||
*/
|
||||
int SetOwner(lua_State* L, Item* item)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
item->SetOwnerGUID(player->GET_GUID());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the binding of the [Item] to 'true' or 'false'
|
||||
*
|
||||
* @param bool setBinding
|
||||
*/
|
||||
int SetBinding(lua_State* L, Item* item)
|
||||
{
|
||||
bool soulbound = ALE::CHECKVAL<bool>(L, 2);
|
||||
|
||||
item->SetBinding(soulbound);
|
||||
item->SetState(ITEM_CHANGED, item->GetOwner());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stack count of the [Item]
|
||||
*
|
||||
* @param uint32 count
|
||||
*/
|
||||
int SetCount(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 count = ALE::CHECKVAL<uint32>(L, 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(lua_State* L, Item* item)
|
||||
{
|
||||
Player* owner = item->GetOwner();
|
||||
if (!owner)
|
||||
{
|
||||
ALE::Push(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 enchant = ALE::CHECKVAL<uint32>(L, 2);
|
||||
if (!sSpellItemEnchantmentStore.LookupEntry(enchant))
|
||||
{
|
||||
ALE::Push(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
EnchantmentSlot slot = (EnchantmentSlot)ALE::CHECKVAL<uint32>(L, 3);
|
||||
if (slot >= MAX_INSPECTED_ENCHANTMENT_SLOT)
|
||||
return luaL_argerror(L, 2, "valid EnchantmentSlot expected");
|
||||
|
||||
owner->ApplyEnchantment(item, slot, false);
|
||||
item->SetEnchantment(slot, enchant, 0, 0);
|
||||
owner->ApplyEnchantment(item, slot, true);
|
||||
ALE::Push(L, 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.
|
||||
*/
|
||||
int SetRandomProperty(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 randomPropId = ALE::CHECKVAL<uint32>(L, 2);
|
||||
item->SetItemRandomProperties(randomPropId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the random suffix for the [Item] from a given random suffix ID.
|
||||
*
|
||||
* @param uint32 randomSuffixId : The ID of the random suffix to be applied.
|
||||
*/
|
||||
int SetRandomSuffix(lua_State* L, Item* item)
|
||||
{
|
||||
uint32 randomPropId = ALE::CHECKVAL<uint32>(L, 2);
|
||||
item->SetItemRandomProperties(-randomPropId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* 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(lua_State* L, Item* item)
|
||||
{
|
||||
Player* owner = item->GetOwner();
|
||||
if (!owner)
|
||||
{
|
||||
ALE::Push(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
EnchantmentSlot slot = (EnchantmentSlot)ALE::CHECKVAL<uint32>(L, 2);
|
||||
if (slot >= MAX_INSPECTED_ENCHANTMENT_SLOT)
|
||||
return luaL_argerror(L, 2, "valid EnchantmentSlot expected");
|
||||
|
||||
if (!item->GetEnchantmentId(slot))
|
||||
{
|
||||
ALE::Push(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
owner->ApplyEnchantment(item, slot, false);
|
||||
item->ClearEnchantment(slot);
|
||||
ALE::Push(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the [Item] to the database
|
||||
*/
|
||||
int SaveToDB(lua_State* /*L*/, Item* item)
|
||||
{
|
||||
CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr);
|
||||
item->SaveToDB(trans);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef ITEMTEMPLATEMETHODS_H
|
||||
#define ITEMTEMPLATEMETHODS_H
|
||||
|
||||
#include "Chat.h"
|
||||
|
||||
/***
|
||||
* Represents item data defined in the database and DBCs, such as stats, quality, class restrictions, and display info.
|
||||
*
|
||||
* Used to access read-only metadata about items (not specific item instances in bags or equipment).
|
||||
*
|
||||
* Inherits all methods from: none
|
||||
*/
|
||||
namespace LuaItemTemplate
|
||||
{
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s ID.
|
||||
*
|
||||
* @return uint32 itemId
|
||||
*/
|
||||
int GetItemId(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->ItemId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s class.
|
||||
*
|
||||
* @return uint32 class
|
||||
*/
|
||||
int GetClass(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->Class);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s subclass.
|
||||
*
|
||||
* @return uint32 subClass
|
||||
*/
|
||||
int GetSubClass(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->SubClass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s name in the [Player]'s locale.
|
||||
*
|
||||
* @param [LocaleConstant] locale = DEFAULT_LOCALE : locale to return the [ItemTemplate] name in (it's optional default: LOCALE_enUS)
|
||||
*
|
||||
* @return string name
|
||||
*/
|
||||
int GetName(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
uint32 loc_idx = ALE::CHECKVAL<uint32>(L, 2, LocaleConstant::LOCALE_enUS);
|
||||
|
||||
const ItemLocale* itemLocale = eObjectMgr->GetItemLocale(itemTemplate->ItemId);
|
||||
std::string name = itemTemplate->Name1;
|
||||
|
||||
if (itemLocale && !itemLocale->Name[loc_idx].empty())
|
||||
name = itemLocale->Name[loc_idx];
|
||||
|
||||
ALE::Push(L, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s display ID.
|
||||
*
|
||||
* @return uint32 displayId
|
||||
*/
|
||||
int GetDisplayId(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->DisplayInfoID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s quality.
|
||||
*
|
||||
* @return uint32 quality
|
||||
*/
|
||||
int GetQuality(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->Quality);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s flags.
|
||||
*
|
||||
* @return uint32 flags
|
||||
*/
|
||||
int GetFlags(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->Flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s extra flags.
|
||||
*
|
||||
* @return uint32 flags
|
||||
*/
|
||||
int GetExtraFlags(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->Flags2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s default purchase count.
|
||||
*
|
||||
* @return uint32 buyCount
|
||||
*/
|
||||
int GetBuyCount(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->BuyCount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s purchase price.
|
||||
*
|
||||
* @return int32 buyPrice
|
||||
*/
|
||||
int GetBuyPrice(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->BuyPrice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s sell price.
|
||||
*
|
||||
* @return uint32 sellPrice
|
||||
*/
|
||||
int GetSellPrice(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->SellPrice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s inventory type.
|
||||
*
|
||||
* @return uint32 inventoryType
|
||||
*/
|
||||
int GetInventoryType(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->InventoryType);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] classes allowed to use this [ItemTemplate].
|
||||
*
|
||||
* @return uint32 allowableClass
|
||||
*/
|
||||
int GetAllowableClass(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->AllowableClass);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] races allowed to use this [ItemTemplate].
|
||||
*
|
||||
* @return uint32 allowableRace
|
||||
*/
|
||||
int GetAllowableRace(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->AllowableRace);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ItemTemplate]'s item level.
|
||||
*
|
||||
* @return uint32 itemLevel
|
||||
*/
|
||||
int GetItemLevel(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->ItemLevel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum level required to use this [ItemTemplate].
|
||||
*
|
||||
* @return uint32 requiredLevel
|
||||
*/
|
||||
int GetRequiredLevel(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
ALE::Push(L, itemTemplate->RequiredLevel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon is used by this [ItemTemplate].
|
||||
*
|
||||
* @return string itemIcon
|
||||
*/
|
||||
int GetIcon(lua_State* L, ItemTemplate* itemTemplate)
|
||||
{
|
||||
uint32 display_id = itemTemplate->DisplayInfoID;
|
||||
|
||||
ItemDisplayInfoEntry const* displayInfo = sItemDisplayInfoStore.LookupEntry(display_id);
|
||||
const char* icon = displayInfo->inventoryIcon;
|
||||
|
||||
ALE::Push(L, icon);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#ifndef LOOTMETHODS_H
|
||||
#define LOOTMETHODS_H
|
||||
|
||||
/***
|
||||
* Represents loot that can be obtained from various sources like creatures, gameobjects, or items.
|
||||
*
|
||||
* Contains information about items that can be looted, their quantities, money, and loot state.
|
||||
*
|
||||
* Inherits all methods from: none
|
||||
*/
|
||||
namespace LuaLoot
|
||||
{
|
||||
/**
|
||||
* Returns `true` if all loot has been taken from this [Loot], returns `false` otherwise.
|
||||
*
|
||||
* @return bool isLooted
|
||||
*/
|
||||
int IsLooted(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->isLooted());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the [Loot] with the specified parameters.
|
||||
*
|
||||
* If an item with the same ID already exists and its count is less than 255, the count will be increased instead of adding a new entry.
|
||||
*
|
||||
* @param uint32 itemId : the ID of the item to add
|
||||
* @param uint8 minCount : minimum count of the item
|
||||
* @param uint8 maxCount : maximum count of the item
|
||||
* @param float chance : chance for the item to drop (0-100)
|
||||
* @param uint16 lootMode : loot mode for the item
|
||||
* @param bool needsQuest = false : if `true`, the item requires a quest to be looted
|
||||
* @param bool allowStacking = true : if `true`, allow items to stack in the loot window
|
||||
*/
|
||||
int AddItem(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 itemid = ALE::CHECKVAL<uint32>(L, 2);
|
||||
uint8 min_count = ALE::CHECKVAL<uint8>(L, 3);
|
||||
uint8 max_count = ALE::CHECKVAL<uint8>(L, 4);
|
||||
float chance = ALE::CHECKVAL<float>(L, 5);
|
||||
uint16 loot_mode = ALE::CHECKVAL<uint16>(L, 6);
|
||||
bool needs_quest = ALE::CHECKVAL<bool>(L, 7, false);
|
||||
bool allow_stacking = ALE::CHECKVAL<bool>(L, 8, true);
|
||||
|
||||
if (allow_stacking)
|
||||
{
|
||||
auto& container = needs_quest ? loot->quest_items : loot->items;
|
||||
|
||||
for (LootItem& lootitem : container)
|
||||
{
|
||||
if (lootitem.itemid == itemid && lootitem.count < 255)
|
||||
{
|
||||
uint32 add = std::max<uint32>(1u, min_count);
|
||||
uint32 newCount = std::min<uint32>(255u, lootitem.count + add);
|
||||
lootitem.count = static_cast<uint8>(newCount);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LootStoreItem newLootStoreItem(itemid, 0, chance, needs_quest, loot_mode, 0, min_count, max_count);
|
||||
loot->AddItem(newLootStoreItem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Loot] contains the specified item, and returns `false` otherwise.
|
||||
*
|
||||
* @param uint32 itemId = 0 : the ID of the item to check for. If 0, checks if any item exists
|
||||
* @param uint32 count = 0 : specific count to check for. If 0, ignores count
|
||||
* @return bool hasItem
|
||||
*/
|
||||
int HasItem(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 itemid = ALE::CHECKVAL<uint32>(L, 2, false);
|
||||
uint32 count = ALE::CHECKVAL<uint32>(L, 3, false);
|
||||
bool has_item = false;
|
||||
|
||||
if (itemid)
|
||||
{
|
||||
for (const LootItem &lootitem : loot->items)
|
||||
{
|
||||
if (lootitem.itemid == itemid && (count == 0 || lootitem.count == count))
|
||||
{
|
||||
has_item = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const LootItem &lootitem : loot->items)
|
||||
{
|
||||
if (lootitem.itemid != 0)
|
||||
{
|
||||
has_item = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALE::Push(L, has_item);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified item from the [Loot].
|
||||
*
|
||||
* If count is specified, removes only that amount. Otherwise removes all items with the ID.
|
||||
*
|
||||
* @param uint32 itemId : the ID of the item to remove
|
||||
* @param bool isCountSpecified = false : if `true`, only removes the specified count
|
||||
* @param uint32 count = 0 : amount to remove when isCountSpecified is true
|
||||
*/
|
||||
int RemoveItem(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 itemid = ALE::CHECKVAL<uint32>(L, 2);
|
||||
bool isCountSpecified = ALE::CHECKVAL<bool>(L, 3, false);
|
||||
uint32 count = isCountSpecified ? ALE::CHECKVAL<uint32>(L, 4) : 0;
|
||||
|
||||
auto removeFromContainer = [&](auto& container, uint32& remaining)
|
||||
{
|
||||
for (auto it = container.begin(); it != container.end(); )
|
||||
{
|
||||
if (it->itemid == itemid)
|
||||
{
|
||||
if (isCountSpecified)
|
||||
{
|
||||
if (it->count > remaining)
|
||||
{
|
||||
it->count -= static_cast<uint8>(remaining);
|
||||
remaining = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
remaining -= it->count;
|
||||
it = container.erase(it);
|
||||
if (remaining == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
it = container.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
};
|
||||
|
||||
// Remove from regular items
|
||||
removeFromContainer(loot->items, count);
|
||||
|
||||
// Remove from quest items as well
|
||||
if (!isCountSpecified || count > 0)
|
||||
removeFromContainer(loot->quest_items, count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of money in this [Loot].
|
||||
*
|
||||
* @return uint32 money : the amount of money in copper
|
||||
*/
|
||||
int GetMoney(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->gold);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount of money in this [Loot].
|
||||
*
|
||||
* @param uint32 money : the amount of money to set in copper
|
||||
*/
|
||||
int SetMoney(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 gold = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
loot->gold = gold;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random amount of money for this [Loot] within the specified range.
|
||||
*
|
||||
* @param uint32 minGold : minimum amount of money in copper
|
||||
* @param uint32 maxGold : maximum amount of money in copper
|
||||
*/
|
||||
int GenerateMoney(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 min_gold = ALE::CHECKVAL<uint32>(L, 2);
|
||||
uint32 max_gold = ALE::CHECKVAL<uint32>(L, 3);
|
||||
|
||||
loot->generateMoneyLoot(min_gold, max_gold);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all items and money from this [Loot].
|
||||
*/
|
||||
int Clear(lua_State* /*L*/, Loot* loot)
|
||||
{
|
||||
loot->clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of unlooted items in this [Loot].
|
||||
*
|
||||
* @param uint32 count : the number of unlooted items
|
||||
*/
|
||||
int SetUnlootedCount(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 count = ALE::CHECKVAL<uint32>(L, 2);
|
||||
|
||||
loot->unlootedCount = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of unlooted items in this [Loot].
|
||||
*
|
||||
* @return uint32 unlootedCount
|
||||
*/
|
||||
int GetUnlootedCount(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->unlootedCount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a table containing all items in this [Loot].
|
||||
*
|
||||
* Each item is represented as a table with the following fields:
|
||||
* - id: item ID
|
||||
* - index: item index in the loot list
|
||||
* - count: quantity of the item
|
||||
* - needs_quest: whether the item requires a quest
|
||||
* - is_looted: whether the item has already been looted
|
||||
* - roll_winner_guid: GUID of the player who won the item roll
|
||||
*
|
||||
* @return table items : array of item tables
|
||||
*/
|
||||
int GetItems(lua_State* L, Loot* loot)
|
||||
{
|
||||
lua_createtable(L, loot->items.size(), 0);
|
||||
int tbl = lua_gettop(L);
|
||||
|
||||
for (unsigned int i = 0; i < loot->items.size(); i++)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
ALE::Push(L, loot->items[i].itemid);
|
||||
lua_setfield(L, -2, "id");
|
||||
|
||||
ALE::Push(L, loot->items[i].itemIndex);
|
||||
lua_setfield(L, -2, "index");
|
||||
|
||||
ALE::Push(L, loot->items[i].count);
|
||||
lua_setfield(L, -2, "count");
|
||||
|
||||
ALE::Push(L, loot->items[i].needs_quest);
|
||||
lua_setfield(L, -2, "needs_quest");
|
||||
|
||||
ALE::Push(L, loot->items[i].is_looted);
|
||||
lua_setfield(L, -2, "is_looted");
|
||||
|
||||
ALE::Push(L, loot->items[i].rollWinnerGUID);
|
||||
lua_setfield(L, -2, "roll_winner_guid");
|
||||
|
||||
lua_rawseti(L, tbl, i + 1);
|
||||
}
|
||||
|
||||
lua_settop(L, tbl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a table containing all quest items in this [Loot].
|
||||
*
|
||||
* Each quest item is represented as a table with the following fields:
|
||||
* - id: item ID
|
||||
* - index: item index in the quest loot list
|
||||
* - count: quantity of the item
|
||||
* - needs_quest: whether the item requires a quest
|
||||
* - is_looted: whether the item has already been looted
|
||||
* - roll_winner_guid: GUID of the player who won the item roll
|
||||
*
|
||||
* @return table quest_items : array of quest item tables
|
||||
*/
|
||||
int GetQuestItems(lua_State* L, Loot* loot)
|
||||
{
|
||||
lua_createtable(L, loot->quest_items.size(), 0);
|
||||
int tbl = lua_gettop(L);
|
||||
|
||||
for (unsigned int i = 0; i < loot->quest_items.size(); i++)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
ALE::Push(L, loot->quest_items[i].itemid);
|
||||
lua_setfield(L, -2, "id");
|
||||
|
||||
ALE::Push(L, loot->quest_items[i].itemIndex);
|
||||
lua_setfield(L, -2, "index");
|
||||
|
||||
ALE::Push(L, loot->quest_items[i].count);
|
||||
lua_setfield(L, -2, "count");
|
||||
|
||||
ALE::Push(L, loot->quest_items[i].needs_quest);
|
||||
lua_setfield(L, -2, "needs_quest");
|
||||
|
||||
ALE::Push(L, loot->quest_items[i].is_looted);
|
||||
lua_setfield(L, -2, "is_looted");
|
||||
|
||||
ALE::Push(L, loot->quest_items[i].rollWinnerGUID);
|
||||
lua_setfield(L, -2, "roll_winner_guid");
|
||||
|
||||
lua_rawseti(L, tbl, i + 1);
|
||||
}
|
||||
|
||||
lua_settop(L, tbl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the index of all items in this [Loot] to match their position in the list.
|
||||
*
|
||||
* This should be called after removing items to ensure indices are sequential.
|
||||
*/
|
||||
int UpdateItemIndex(lua_State* /*L*/, Loot* loot)
|
||||
{
|
||||
uint32 index = 0;
|
||||
|
||||
for (unsigned int i = 0; i < loot->items.size(); ++i)
|
||||
loot->items[i].itemIndex = index++;
|
||||
|
||||
for (unsigned int i = 0; i < loot->quest_items.size(); ++i)
|
||||
loot->quest_items[i].itemIndex = index++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the looted status of a specific item in this [Loot].
|
||||
*
|
||||
* @param uint32 itemId : the ID of the item
|
||||
* @param uint32 count : specific count to match. If 0, ignores count
|
||||
* @param bool looted = true : `true` to mark as looted, `false` to mark as unlooted
|
||||
*/
|
||||
int SetItemLooted(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 itemid = ALE::CHECKVAL<uint32>(L, 2);
|
||||
uint32 count = ALE::CHECKVAL<uint32>(L, 3);
|
||||
bool looted = ALE::CHECKVAL<bool>(L, 4, true);
|
||||
|
||||
for (auto &lootItem : loot->items)
|
||||
{
|
||||
if (lootItem.itemid == itemid && (count == 0 || lootItem.count == count))
|
||||
{
|
||||
lootItem.is_looted = looted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Loot] is completely empty (no items and no money), returns `false` otherwise.
|
||||
*
|
||||
* @return bool isEmpty
|
||||
*/
|
||||
int IsEmpty(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->empty());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Loot] type.
|
||||
*
|
||||
* @return [LootType] lootType
|
||||
*/
|
||||
int GetLootType(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->loot_type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [Loot] type.
|
||||
*
|
||||
* <pre>
|
||||
* enum LootType
|
||||
* {
|
||||
* LOOT_NONE = 0,
|
||||
* LOOT_CORPSE = 1,
|
||||
* LOOT_PICKPOCKETING = 2,
|
||||
* LOOT_FISHING = 3,
|
||||
* LOOT_DISENCHANTING = 4,
|
||||
* LOOT_SKINNING = 6,
|
||||
* LOOT_PROSPECTING = 7,
|
||||
* LOOT_MILLING = 8,
|
||||
* LOOT_FISHINGHOLE = 20,
|
||||
* LOOT_INSIGNIA = 21,
|
||||
* LOOT_FISHING_JUNK = 22
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @param [LootType] lootType : the loot type to set
|
||||
*/
|
||||
int SetLootType(lua_State* L, Loot* loot)
|
||||
{
|
||||
uint32 lootType = ALE::CHECKVAL<uint32>(L, 2);
|
||||
loot->loot_type = static_cast<LootType>(lootType);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] GUID that owns this loot for round robin distribution.
|
||||
*
|
||||
* @return ObjectGuid roundRobinPlayer : the player GUID
|
||||
*/
|
||||
int GetRoundRobinPlayer(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->roundRobinPlayer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [Player] GUID for round robin loot distribution.
|
||||
*
|
||||
* @param ObjectGuid playerGUID : the player GUID
|
||||
*/
|
||||
int SetRoundRobinPlayer(lua_State* L, Loot* loot)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
loot->roundRobinPlayer = guid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Player] GUID that owns this loot.
|
||||
*
|
||||
* @return ObjectGuid lootOwner : the player GUID
|
||||
*/
|
||||
int GetLootOwner(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->lootOwnerGUID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [Player] GUID that owns this loot.
|
||||
*
|
||||
* @param ObjectGuid playerGUID : the player GUID
|
||||
*/
|
||||
int SetLootOwner(lua_State* L, Loot* loot)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
loot->lootOwnerGUID = guid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container GUID that holds this loot.
|
||||
*
|
||||
* @return ObjectGuid containerGUID : the container GUID
|
||||
*/
|
||||
int GetContainer(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->containerGUID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the container GUID that holds this loot.
|
||||
*
|
||||
* @param ObjectGuid containerGUID : the container GUID
|
||||
*/
|
||||
int SetContainer(lua_State* L, Loot* loot)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
loot->containerGUID = guid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source [WorldObject] GUID for this loot.
|
||||
*
|
||||
* @return ObjectGuid sourceGUID : the source [WorldObject] GUID
|
||||
*/
|
||||
int GetSourceWorldObject(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->sourceWorldObjectGUID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source [WorldObject] GUID for this loot.
|
||||
*
|
||||
* @param ObjectGuid sourceGUID : the source [WorldObject] GUID
|
||||
*/
|
||||
int SetSourceWorldObject(lua_State* L, Loot* loot)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
loot->sourceWorldObjectGUID = guid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Loot] contains quest items and returns `false` otherwise.
|
||||
*
|
||||
* @return bool hasQuestItems
|
||||
*/
|
||||
int HasQuestItems(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, !loot->quest_items.empty());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Loot] has items available for all players and returns `false` otherwise.
|
||||
*
|
||||
* @return bool hasItemForAll
|
||||
*/
|
||||
int HasItemForAll(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->hasItemForAll());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Loot] has items that are over the group loot threshold and returns `false` otherwise.
|
||||
*
|
||||
* @return bool hasOverThresholdItem
|
||||
*/
|
||||
int HasOverThresholdItem(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, loot->hasOverThresholdItem());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of items (regular + quest items) in this [Loot].
|
||||
*
|
||||
* @return uint32 itemCount
|
||||
*/
|
||||
int GetItemCount(lua_State* L, Loot* loot)
|
||||
{
|
||||
ALE::Push(L, static_cast<uint32>(loot->items.size() + loot->quest_items.size()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum loot slot index available for the specified [Player].
|
||||
*
|
||||
* @param [Player] player : the player to check slots for
|
||||
* @return uint32 maxSlot
|
||||
*/
|
||||
int GetMaxSlotForPlayer(lua_State* L, Loot* loot)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
ALE::Push(L, loot->GetMaxSlotInLootFor(player));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a [Player] to the list of players currently looting this [Loot].
|
||||
*
|
||||
* @param [Player] player : the player to add as a looter
|
||||
*/
|
||||
int AddLooter(lua_State* L, Loot* loot)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
loot->AddLooter(player->GetGUID());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a [Player] from the list of players currently looting this [Loot].
|
||||
*
|
||||
* @param [Player] player : the player to remove from looters
|
||||
*/
|
||||
int RemoveLooter(lua_State* L, Loot* loot)
|
||||
{
|
||||
Player* player = ALE::CHECKOBJ<Player>(L, 2);
|
||||
loot->RemoveLooter(player->GetGUID());
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
#endif // LOOTMETHODS_H
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 "ALEInstanceAI.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(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->IsBattleArena());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Map] is a non-arena [BattleGround], `false` otherwise.
|
||||
*
|
||||
* @return bool isBattleGround
|
||||
*/
|
||||
int IsBattleground(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->IsBattleground());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Map] is a dungeon, `false` otherwise.
|
||||
*
|
||||
* @return bool isDungeon
|
||||
*/
|
||||
int IsDungeon(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->IsDungeon());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Map] has no [Player]s, `false` otherwise.
|
||||
*
|
||||
* @return bool IsEmpty
|
||||
*/
|
||||
int IsEmpty(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->IsEmpty());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Map] is a heroic, `false` otherwise.
|
||||
*
|
||||
* @return bool isHeroic
|
||||
*/
|
||||
int IsHeroic(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->IsHeroic());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Map] is a raid, `false` otherwise.
|
||||
*
|
||||
* @return bool isRaid
|
||||
*/
|
||||
int IsRaid(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->IsRaid());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the [Map].
|
||||
*
|
||||
* @return string mapName
|
||||
*/
|
||||
int GetName(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Map* map)
|
||||
{
|
||||
float x = ALE::CHECKVAL<float>(L, 2);
|
||||
float y = ALE::CHECKVAL<float>(L, 3);
|
||||
uint32 phasemask = ALE::CHECKVAL<uint32>(L, 4, 1);
|
||||
float z = map->GetHeight(phasemask, x, y, MAX_HEIGHT);
|
||||
if (z != INVALID_HEIGHT)
|
||||
ALE::Push(L, z);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difficulty of the [Map].
|
||||
*
|
||||
* Always returns 0 if the expansion is pre-TBC.
|
||||
*
|
||||
* @return int32 difficulty
|
||||
*/
|
||||
int GetDifficulty(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->GetDifficulty());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance ID of the [Map].
|
||||
*
|
||||
* @return uint32 instanceId
|
||||
*/
|
||||
int GetInstanceId(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->GetInstanceId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player count currently on the [Map] (excluding GMs).
|
||||
*
|
||||
* @return uint32 playerCount
|
||||
*/
|
||||
int GetPlayerCount(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, map->GetPlayersCountExceptGMs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the [Map].
|
||||
*
|
||||
* @return uint32 mapId
|
||||
*/
|
||||
int GetMapId(lua_State* L, Map* map)
|
||||
{
|
||||
ALE::Push(L, 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
|
||||
*/
|
||||
int GetAreaId(lua_State* L, Map* map)
|
||||
{
|
||||
float x = ALE::CHECKVAL<float>(L, 2);
|
||||
float y = ALE::CHECKVAL<float>(L, 3);
|
||||
float z = ALE::CHECKVAL<float>(L, 4);
|
||||
float phasemask = ALE::CHECKVAL<uint32>(L, 5, PHASEMASK_NORMAL);
|
||||
|
||||
ALE::Push(L, map->GetAreaId(phasemask, x, y, z));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [WorldObject] by its GUID from the map if it is spawned.
|
||||
*
|
||||
* @param ObjectGuid guid
|
||||
* @return [WorldObject] object
|
||||
*/
|
||||
int GetWorldObject(lua_State* L, Map* map)
|
||||
{
|
||||
ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);
|
||||
|
||||
switch (guid.GetHigh())
|
||||
{
|
||||
case HIGHGUID_PLAYER:
|
||||
ALE::Push(L, eObjectAccessor()GetPlayer(map, guid));
|
||||
break;
|
||||
case HIGHGUID_TRANSPORT:
|
||||
case HIGHGUID_MO_TRANSPORT:
|
||||
case HIGHGUID_GAMEOBJECT:
|
||||
ALE::Push(L, map->GetGameObject(guid));
|
||||
break;
|
||||
case HIGHGUID_VEHICLE:
|
||||
case HIGHGUID_UNIT:
|
||||
ALE::Push(L, map->GetCreature(guid));
|
||||
break;
|
||||
case HIGHGUID_PET:
|
||||
ALE::Push(L, map->GetPet(guid));
|
||||
break;
|
||||
case HIGHGUID_DYNAMICOBJECT:
|
||||
ALE::Push(L, map->GetDynamicObject(guid));
|
||||
break;
|
||||
case HIGHGUID_CORPSE:
|
||||
ALE::Push(L, map->GetCorpse(guid));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [Weather] type based on [WeatherType] and grade supplied.
|
||||
*
|
||||
* enum WeatherType
|
||||
* {
|
||||
* WEATHER_TYPE_FINE = 0,
|
||||
* WEATHER_TYPE_RAIN = 1,
|
||||
* WEATHER_TYPE_SNOW = 2,
|
||||
* WEATHER_TYPE_STORM = 3,
|
||||
* WEATHER_TYPE_THUNDERS = 86,
|
||||
* 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(lua_State* L, Map* map)
|
||||
{
|
||||
uint32 zoneId = ALE::CHECKVAL<uint32>(L, 2);
|
||||
uint32 weatherType = ALE::CHECKVAL<uint32>(L, 3);
|
||||
float grade = ALE::CHECKVAL<float>(L, 4);
|
||||
|
||||
Weather* weather = map->GetOrGenerateZoneDefaultWeather(zoneId);
|
||||
if (weather)
|
||||
weather->SetWeather((WeatherType)weatherType, grade);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instance data table for the [Map], if it exists.
|
||||
*
|
||||
* The instance must be scripted using ALE for this to succeed.
|
||||
* If the instance is scripted in C++ this will return `nil`.
|
||||
*
|
||||
* @return table instance_data : instance data table, or `nil`
|
||||
*/
|
||||
int GetInstanceData(lua_State* L, Map* map)
|
||||
{
|
||||
ALEInstanceAI* iAI = NULL;
|
||||
if (InstanceMap* inst = map->ToInstanceMap())
|
||||
iAI = dynamic_cast<ALEInstanceAI*>(inst->GetInstanceScript());
|
||||
|
||||
if (iAI)
|
||||
ALE::GetALE(L)->PushInstanceData(L, iAI, false);
|
||||
else
|
||||
ALE::Push(L); // nil
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the [Map]'s instance data to the database.
|
||||
*/
|
||||
int SaveInstanceData(lua_State* /*L*/, Map* map)
|
||||
{
|
||||
ALEInstanceAI* iAI = NULL;
|
||||
if (InstanceMap* inst = map->ToInstanceMap())
|
||||
iAI = dynamic_cast<ALEInstanceAI*>(inst->GetInstanceScript());
|
||||
|
||||
if (iAI)
|
||||
iAI->SaveToDB();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a table with all the current [Player]s in the map
|
||||
*
|
||||
* enum TeamId
|
||||
* {
|
||||
* TEAM_ALLIANCE = 0,
|
||||
* TEAM_HORDE = 1,
|
||||
* TEAM_NEUTRAL = 2
|
||||
* };
|
||||
*
|
||||
* @param [TeamId] team : optional check team of the [Player], Alliance, Horde or Neutral (All)
|
||||
* @return table mapPlayers
|
||||
*/
|
||||
int GetPlayers(lua_State* L, Map* map)
|
||||
{
|
||||
uint32 team = ALE::CHECKVAL<uint32>(L, 2, TEAM_NEUTRAL);
|
||||
|
||||
lua_newtable(L);
|
||||
int tbl = lua_gettop(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 || player->GetTeamId() == team))
|
||||
{
|
||||
ALE::Push(L, player);
|
||||
lua_rawseti(L, tbl, ++i);
|
||||
}
|
||||
}
|
||||
|
||||
lua_settop(L, tbl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a table with all the current [Creature]s in the map
|
||||
*
|
||||
* @return table mapCreatures
|
||||
*/
|
||||
int GetCreatures(lua_State* L, Map* map)
|
||||
{
|
||||
const auto& creatures = map->GetCreatureBySpawnIdStore();
|
||||
|
||||
lua_createtable(L, creatures.size(), 0);
|
||||
int tbl = lua_gettop(L);
|
||||
|
||||
for (const auto& pair : creatures)
|
||||
{
|
||||
Creature* creature = pair.second;
|
||||
|
||||
ALE::Push(L, creature);
|
||||
lua_rawseti(L, tbl, creature->GetSpawnId());
|
||||
}
|
||||
|
||||
lua_settop(L, tbl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a table with all the current [Creature]s in the specific area id
|
||||
*
|
||||
* @param number areaId : specific area id
|
||||
* @return table mapCreatures
|
||||
*/
|
||||
int GetCreaturesByAreaId(lua_State* L, Map* map)
|
||||
{
|
||||
int32 areaId = ALE::CHECKVAL<int32>(L, 2, -1);
|
||||
std::vector<Creature*> filteredCreatures;
|
||||
|
||||
for (const auto& pair : map->GetCreatureBySpawnIdStore())
|
||||
{
|
||||
Creature* creature = pair.second;
|
||||
if (areaId == -1 || creature->GetAreaId() == (uint32)areaId)
|
||||
{
|
||||
filteredCreatures.push_back(creature);
|
||||
}
|
||||
}
|
||||
|
||||
lua_createtable(L, filteredCreatures.size(), 0);
|
||||
int tbl = lua_gettop(L);
|
||||
|
||||
for (Creature* creature : filteredCreatures)
|
||||
{
|
||||
ALE::Push(L, creature);
|
||||
lua_rawseti(L, tbl, creature->GetSpawnId());
|
||||
}
|
||||
|
||||
lua_settop(L, tbl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a table of all [Transport]s on the [Map]
|
||||
*
|
||||
* @return table transports
|
||||
*/
|
||||
int GetTransports(lua_State* L, Map* map)
|
||||
{
|
||||
TransportsContainer const& transports = map->GetAllTransports();
|
||||
lua_createtable(L, transports.size(), 0);
|
||||
int i = 1;
|
||||
for (Transport* transport : transports)
|
||||
{
|
||||
ALE::Push(L, transport);
|
||||
lua_rawseti(L, -2, i++);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint32 flag = ALE::CHECKVAL<uint32>(L, 3);
|
||||
|
||||
ALE::Push(L, obj->HasFlag(index, flag));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the [Object] has been added to its [Map], otherwise `false`.
|
||||
*
|
||||
* @return bool inWorld
|
||||
*/
|
||||
int IsInWorld(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, obj->IsInWorld());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 'true' if the [Object] is a player, 'false' otherwise.
|
||||
*
|
||||
* @return bool IsPlayer
|
||||
*/
|
||||
int IsPlayer(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, obj->IsPlayer());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data at the specified index, casted to a signed 32-bit integer.
|
||||
*
|
||||
* @param uint16 index
|
||||
* @return int32 value
|
||||
*/
|
||||
int GetInt32Value(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint8 offset = ALE::CHECKVAL<uint8>(L, 3);
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint8 offset = ALE::CHECKVAL<uint8>(L, 3);
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, obj->GetFloatValue(OBJECT_FIELD_SCALE_X));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry of the [Object].
|
||||
*
|
||||
* [Player]s do not have an "entry".
|
||||
*
|
||||
* @return uint32 entry
|
||||
*/
|
||||
int GetEntry(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, obj->GetGUID().GetCounter());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TypeId of the [Object].
|
||||
*
|
||||
* enum TypeID
|
||||
* {
|
||||
* TYPEID_OBJECT = 0,
|
||||
* TYPEID_ITEM = 1,
|
||||
* TYPEID_CONTAINER = 2,
|
||||
* TYPEID_UNIT = 3,
|
||||
* TYPEID_PLAYER = 4,
|
||||
* TYPEID_GAMEOBJECT = 5,
|
||||
* TYPEID_DYNAMICOBJECT = 6,
|
||||
* TYPEID_CORPSE = 7
|
||||
* };
|
||||
*
|
||||
* @return uint8 typeID
|
||||
*/
|
||||
int GetTypeId(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint32 flag = ALE::CHECKVAL<uint32>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
int32 value = ALE::CHECKVAL<int32>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint32 value = ALE::CHECKVAL<uint32>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint32 value = ALE::CHECKVAL<uint32>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
float value = ALE::CHECKVAL<float>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint8 offset = ALE::CHECKVAL<uint8>(L, 3);
|
||||
uint8 value = ALE::CHECKVAL<uint8>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint8 offset = ALE::CHECKVAL<uint8>(L, 3);
|
||||
uint16 value = ALE::CHECKVAL<uint16>(L, 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
|
||||
*/
|
||||
int SetInt16Value(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint8 offset = ALE::CHECKVAL<uint8>(L, 3);
|
||||
int16 value = ALE::CHECKVAL<int16>(L, 4);
|
||||
obj->SetInt16Value(index, offset, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [Object]'s scale/size to the given value.
|
||||
*
|
||||
* @param float scale
|
||||
*/
|
||||
int SetScale(lua_State* L, Object* obj)
|
||||
{
|
||||
float size = ALE::CHECKVAL<float>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint64 value = ALE::CHECKVAL<uint64>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
uint16 index = ALE::CHECKVAL<uint16>(L, 2);
|
||||
uint32 flag = ALE::CHECKVAL<uint32>(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, 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(lua_State* L, Object* obj)
|
||||
{
|
||||
ALE::Push(L, obj->ToPlayer());
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user