Forum > Miscellaneous
[WotLK][TC][Eluna][AIO] Custom secondary power types
(1/1)
Grymskvll:
This is a system for custom secondary powertypes, like combo points, DK runes or spriests' shadow orbs. Here's a demo video, I previously posted it in the showoff thread: [media:20r7yr4d]https://www.youtube.com/watch?v=qHSFOsNAs_A[/media:20r7yr4d]
If I forgot anything or if you have improvements, please let me know.
To use this, you need to have patched Wow.exe to load modified interface files. See http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-and-programs/501200-repost-sig-md5-protection-remover.html
First we need to add some Eluna player hooks for various stages of casting. We need 3 new hooks: PLAYER_EVENT_ON_SPELL_CAST_START (validation and cancellation: script can return false to cancel the cast) PLAYER_EVENT_ON_SPELL_CAST_SUCCESS (spell passed normal checks for range, mana, target, etc, so now we do custom validation, but NO CHANGES ARE APPLIED in case a later script that registered this hook wants to cancel the cast) PLAYER_EVENT_ON_SPELL_LAUNCH (spell passed all custom validation from the previous hook, so here we can take custom power or do anything else without bugs)
To add these hooks, we need to modify various files. Hopefully it's not too disorienting. Make sure you don't have any uncommited changes before beginning!
In gameSpellsSpell.cpp, at the top of Spell::prepare, add the Eluna hook:
--- Code: ---void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura) {
#ifdef ELUNA if (Player* playerCaster = m_caster->ToPlayer()) { if (!sEluna->OnSpellCastStart(playerCaster, this)) { finish(false); return; } } #endif
--- End code ---
In Spell::Cast, below "CallScriptOnCastHandlers();" add the Eluna hook:
--- Code: ---CallScriptOnCastHandlers();
#ifdef ELUNA if (Player* playerCaster = m_caster->ToPlayer()) { if (!sEluna->OnSpellCastSuccess(playerCaster, this)) { SendInterrupted(0);
if (playerCaster->GetTypeId() == TYPEID_PLAYER) { playerCaster->RestoreSpellMods(this); // cleanup after mod system // triggered spell pointer can be not removed in some cases playerCaster->SetSpellModTakingSpell(this, false); } finish(false); SetExecutedCurrently(false);
return; } } #endif
--- End code ---
At the top of Spell::HandleLaunchPhase(), add the Eluna hook:
--- Code: ---void Spell::HandleLaunchPhase() { if (Player* playerCaster = m_caster->ToPlayer()) { sScriptMgr->OnPlayerSpellLaunch(playerCaster, this); }
--- End code ---
In LuaEngineHeader FilesHooks.h, in "enum PlayerEvents" add:
--- Code: --- PLAYER_EVENT_ON_SPELL_CAST_START = 43, // (event, player, spell) PLAYER_EVENT_ON_SPELL_CAST_SUCCESS = 44, // (event, player, spell, skipCheck) PLAYER_EVENT_ON_SPELL_LAUNCH = 45, // (event, player, spell)
--- End code ---
In LuaEngineHeader FilesLuaEngine.h, add this under the "void OnTextEmote" declaration:
--- Code: --- bool OnSpellCastStart(Player* pPlayer, Spell* pSpell); bool OnSpellCastSuccess(Player* pPlayer, Spell* pSpell); void OnSpellLaunch(Player* pPlayer, Spell* pSpell);
--- End code ---
In LuaEngineSource FilesPlayerHooks.cpp, add the following:
--- Code: ---bool Eluna::OnSpellCastStart(Player* pPlayer, Spell* pSpell) { START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_SPELL_CAST_START, true); bool result = true; Push(pPlayer); Push(pSpell); int n = SetupStack(PlayerEventBindings, 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); return result; }
bool Eluna::OnSpellCastSuccess(Player* pPlayer, Spell* pSpell) { START_HOOK_WITH_RETVAL(PLAYER_EVENT_ON_SPELL_CAST_SUCCESS, true); bool result = true; Push(pPlayer); Push(pSpell); int n = SetupStack(PlayerEventBindings, 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); return result; }
void Eluna::OnSpellLaunch(Player* pPlayer, Spell* pSpell) { START_HOOK(PLAYER_EVENT_ON_SPELL_LAUNCH); Push(pPlayer); Push(pSpell); CallAllFunctions(PlayerEventBindings, key); }
--- End code ---
In gameScriptingScriptMgr.h, add this under the "virtual void OnTextEmote" declaration:
--- Code: --- // Called in Spell::Prepare. virtual void OnSpellCastStart(Player* /*player*/, Spell* /*spell*/) { }
// Called in Spell::Cast. virtual void OnSpellCastSuccess(Player* /*player*/, Spell* /*spell*/) { }
// Called in Spell::HandleLaunchPhase. virtual void OnSpellLaunch(Player* /*player*/, Spell* /*spell*/) { }
--- End code ---
In gameScriptingScriptMgr.cpp, add:
--- Code: ---void ScriptMgr::OnPlayerSpellCastStart(Player* player, Spell* spell) { #ifdef ELUNA sEluna->OnSpellCastStart(player, spell); #endif FOREACH_SCRIPT(PlayerScript)->OnSpellCastStart(player, spell); }
void ScriptMgr::OnPlayerSpellCastSuccess(Player* player, Spell* spell) { #ifdef ELUNA sEluna->OnSpellCastSuccess(player, spell); #endif FOREACH_SCRIPT(PlayerScript)->OnSpellCastSuccess(player, spell); }
void ScriptMgr::OnPlayerSpellLaunch(Player* player, Spell* spell) { #ifdef ELUNA sEluna->OnSpellLaunch(player, spell); #endif FOREACH_SCRIPT(PlayerScript)->OnSpellLaunch(player, spell); }
--- End code ---
Next, we need to add a new Eluna spell method: SendCastResult. This is needed so that our Eluna script can send the player's client a notification to cancel the casting state and GCD, using castresult code 0 (SPELL_FAILED_SUCCESS), which has no error notification so we can jam in our custom "Not enough powertype" message separately. In hindsight, it might've been just as good to put a call to spell->SendCastResult inside Spell::prepare and Spell::Cast... oops.
In LuaEngineMethodsSpellMethods.h, within "namespace LuaSpell" add:
--- Code: --- int SendCastResult(Eluna* /*E*/, lua_State* L, Spell* spell) { SpellCastResult result = (SpellCastResult)Eluna::CHECKVAL<uint32>(L, 2); spell->SendCastResult(result); return 0; }
--- End code ---
In LuaEngineSource FilesLuaFunctions.cpp, in ElunaRegister<Spell> SpellMethods[], add this under // Other:
--- Code: ---{ "SendCastResult", &LuaSpell::SendCastResult },
--- End code ---
To make spells that require the custom secondary power, create a spell with no power cost (the powertype shouldn't matter).
Finally, here's an archive with an MPQ required by each client that defines some custom secure vars and has some art required by the Happy Hearts example, as well as the AIO script with two example secondary powers included ("Happy Hearts" for the shaman, "Displeasure" for the warrior). The spell IDs in the examples are basic custom spells I made for testing. There's nothing special about them besides having 0 cost in Spell.dbc. Spell costs are handled in each power's own script file, in the "consuming" and "generating" tables. The Happy Hearts example has some comments. To make a new custom secondary power, copy one of the examples (Happy Hearts probably), and modify it to your needs (token, values at the top, update function, graphic setup). You don't need to change the main script file.
Mirror: https://drive.google.com/file/d/1DEcpLy3zul5Ja-mjBeAolZocmkY_7ray/view?usp=sharing Mirror: https://a.fluntcaps.me/eakwpn.zip
Rochet2:
Cute :3
Crow:
hate to be a bother but is Eluna needed? couldnt this just be done without it?
Rochet2:
--- Quote from: "Crow" ---hate to be a bother but is Eluna needed? couldnt this just be done without it? --- End quote ---
No its not.
Navigation
[0] Message Index
|