diff --git a/utils/mods/expansions_combat.lua b/utils/mods/expansions_combat.lua new file mode 100644 index 000000000..8f2ded4b9 --- /dev/null +++ b/utils/mods/expansions_combat.lua @@ -0,0 +1,173 @@ +-- Source Function: Client::CheckFizzle() +function CheckFizzle(e) + if eq.is_lost_dungeons_of_norrath_enabled() then + e.IgnoreDefault = false; + return e; + + else -- Classic Fizzle -- Based on TAKP's fizzle formula + e.IgnoreDefault = true; + local client = e.self; + local spell = e.spell; + if client:GetGM() == true then -- GMs never fizzle + e.ReturnValue = true; + return e; + end + + local caster_class = client:GetClass(); + local spell_level = spell:Classes(caster_class - 1); + local aa_bonuses = client:GetAABonuses(); + local item_bonuses = client:GetItemBonuses(); + local spell_bonuses = client:GetSpellBonuses(); + + local no_fizzle_level = math.max(unpack({ + aa_bonuses:MasteryofPast(), + item_bonuses:MasteryofPast(), + spell_bonuses:MasteryofPast() + })); + local no_fizzle_level = 0; + + if spell_level < no_fizzle_level then + e.ReturnValue = true; + return e; + end + + local fizzle_adjustment = spell:BaseDiff(); + local spell_casting_skill_effects = aa_bonuses:adjusted_casting_skill() + + item_bonuses:adjusted_casting_skill() + + spell_bonuses:adjusted_casting_skill(); + + local capped_chance = 95; + local random_penalty = 0; + local effective_spell_casting_skill = 0; + local spell_level_adjustment = 0; + local prime_stat_bonus = 0; + + if fizzle_adjustment ~= 0 or spell_casting_skill_effects < 0 then + -- If Superior Healing not cast by cleric + if spell:ID() == 9 and caster_class ~= Class.CLERIC then + fizzle_adjustment = 0 + end + + if spell_level > 55 then + fizzle_adjustment = 0; + end + + if (caster_class == Class.PALADIN or + caster_class == Class.RANGER or + caster_class == Class.SHADOWKNIGHT) and + spell_level > 40 then + fizzle_adjustment = 0; + end + + if caster_class == Class.BARD and fizzle_adjustment > 15 then + fizzle_adjustment = 15; + end + + local prime_stat = 0; + if caster_class == Class.BARD then + prime_stat = (client:GetCHA() + client:GetDEX()) / 2; + elseif caster_class == Class.CLERIC or + caster_class == Class.PALADIN or + caster_class == Class.RANGER or + caster_class == Class.DRUID or + caster_class == Class.SHAMAN or + caster_class == Class.BEASTLORD then + prime_stat = client:GetWIS(); + elseif caster_class == Class.SHADOWKNIGHT or + caster_class == Class.NECROMANCER or + caster_class == Class.WIZARD or + caster_class == Class.MAGICIAN or + caster_class == Class.ENCHANTER then + prime_stat = client:GetINT(); + end + + prime_stat_bonus = math.floor(prime_stat / 10); + + local effective_spell_level = spell_level - 1; + if effective_spell_level > 50 then + effective_spell_level = 50 + end; + + local spell_casting_skill = client:GetSkill(spell:Skill()); + + effective_spell_casting_skill = spell_casting_skill + spell_casting_skill_effects; + if effective_spell_casting_skill < 0 then + effective_spell_casting_skill = 0; + end + + random_penalty = Random.Int(0, 10); + + spell_level_adjustment = 5 * (18 - effective_spell_level); + local chance = 0 + + effective_spell_casting_skill + + (spell_level_adjustment + prime_stat_bonus) - + random_penalty - + fizzle_adjustment; + + capped_chance = chance; + if caster_class == Class.BARD then + if capped_chance < 1 then + capped_chance = 1; + elseif capped_chance > 95 then + capped_chance = 95; + end + elseif caster_class <= 16 then + if capped_chance < 5 then + capped_chance = 5; + elseif capped_chance > 95 then + capped_chance = 95; + end + else -- Unknown Class + capped_chance = 0; + end + end + + local specialize_skill = client:GetSpecializeSkillValue(spell:ID()); + local specialize_adjustment = 0; + local spell_casting_mastery_adjustment = 0; + + if specialize_skill > 0 then + specialize_adjustment = math.floor(specialize_skill / 10) + 1; + + local spell_casting_mastery_level = client:GetAA(83); -- aaSpellCastingMastery + if spell_casting_mastery_level == 1 then + spell_casting_mastery_adjustment = 2; + elseif spell_casting_mastery_level == 2 then + spell_casting_mastery_adjustment = 5; + elseif spell_casting_mastery_level == 3 then + spell_casting_mastery_adjustment = 10; + end + + capped_chance = capped_chance + specialize_adjustment + spell_casting_mastery_adjustment; + if capped_chance > 98 then + capped_chance = 98; + end + end + + local roll_100 = Random.Int(1, 100); + + if client:IsSilenced() then + roll_100 = capped_chance + 1; + end + + --client:Message(15, "CheckFizzle(LUA:expansions_combat): " .. + -- "spell_id = " .. spell:ID() .. + -- ", roll_100 = " .. roll_100 .. + -- ", capped_chance (" .. capped_chance .. ") = " .. + -- "effective_spell_casting_skill (" .. effective_spell_casting_skill .. ") + " .. + -- "spell_level_adjustment (" .. spell_level_adjustment .. ") + " .. + -- "prime_stat_bonus (" .. prime_stat_bonus .. ") - " .. + -- "random_penalty (" .. random_penalty .. ") - " .. + -- "fizzle_adjustment (" .. fizzle_adjustment .. ") + " .. + -- "specialize_adjustment (" .. specialize_adjustment .. ") + " .. + -- "spell_casting_mastery_adjustment (" .. spell_casting_mastery_adjustment .. ")"); + + if capped_chance >= roll_100 then + e.ReturnValue = true; + return e; + end + + e.ReturnValue = false; + return e; + end +end diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a6145000d..e32957823 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -485,6 +485,11 @@ int Lua_Client::GetRawSkill(int skill_id) { return self->GetRawSkill(static_cast(skill_id)); } +int Lua_Client::GetSkill(int skill_id) { + Lua_Safe_Call_Int(); + return self->GetSkill(static_cast(skill_id)); +} + bool Lua_Client::HasSkill(int skill_id) { Lua_Safe_Call_Bool(); return self->HasSkill(static_cast(skill_id)); @@ -1730,6 +1735,7 @@ luabind::scope lua_register_client() { .def("IncreaseLanguageSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseLanguageSkill) .def("IncreaseLanguageSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseLanguageSkill) .def("GetRawSkill", (int(Lua_Client::*)(int))&Lua_Client::GetRawSkill) + .def("GetSkill", (int(Lua_Client::*)(int))&Lua_Client::GetSkill) .def("HasSkill", (bool(Lua_Client::*)(int))&Lua_Client::HasSkill) .def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill) .def("SetSkill", (void(Lua_Client::*)(int,int))&Lua_Client::SetSkill) diff --git a/zone/lua_client.h b/zone/lua_client.h index 5900930f0..8d300c048 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -122,6 +122,7 @@ public: void IncreaseLanguageSkill(int skill_id); void IncreaseLanguageSkill(int skill_id, int value); int GetRawSkill(int skill_id); + int GetSkill(int skill_id); bool HasSkill(int skill_id); bool CanHaveSkill(int skill_id); void SetSkill(int skill_id, int value); diff --git a/zone/lua_mod.cpp b/zone/lua_mod.cpp index 382f8adba..955187a02 100644 --- a/zone/lua_mod.cpp +++ b/zone/lua_mod.cpp @@ -632,4 +632,59 @@ void LuaMod::GetExperienceForKill(Client *self, Mob *against, uint32 &returnValu } } +void LuaMod::CheckFizzle(Client *self, uint16 &spell_id, SPDat_Spell_Struct spell_struct, bool &returnValue, bool &ignoreDefault) { + int start = lua_gettop(L); + + try { + if (!m_has_check_fizzle) { + return; + } + + lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str()); + lua_getfield(L, -1, "CheckFizzle"); + + Lua_Client l_self(self); + luabind::adl::object e = luabind::newtable(L); + e["self"] = l_self; + e["spell_id"] = spell_id; + + e.push(L); + + Lua_Spell l_spell(&spell_struct); + auto l_spell_o = luabind::adl::object(L, l_spell); + + l_spell_o.push(L); + lua_setfield(L, -2, "spell"); + + if (lua_pcall(L, 1, 1, 0)) { + std::string error = lua_tostring(L, -1); + parser_->AddError(error); + lua_pop(L, 1); + return; + } + + if (lua_type(L, -1) == LUA_TTABLE) { + luabind::adl::object ret(luabind::from_stack(L, -1)); + auto IgnoreDefaultObj = ret["IgnoreDefault"]; + if (luabind::type(IgnoreDefaultObj) == LUA_TBOOLEAN) { + ignoreDefault = ignoreDefault || luabind::object_cast(IgnoreDefaultObj); + } + + auto returnValueObj = ret["ReturnValue"]; + if (luabind::type(returnValueObj) == LUA_TBOOLEAN) { + returnValue = returnValue || luabind::object_cast(returnValueObj); + } + } + } + catch (std::exception& ex) { + parser_->AddError(ex.what()); + } + + int end = lua_gettop(L); + int n = end - start; + if (n > 0) { + lua_pop(L, n); + } +} + #endif diff --git a/zone/lua_mod.h b/zone/lua_mod.h index defc5edab..d21062e6e 100644 --- a/zone/lua_mod.h +++ b/zone/lua_mod.h @@ -26,6 +26,7 @@ public: void GetRequiredAAExperience(Client *self, uint32 &returnValue, bool &ignoreDefault); void GetEXPForLevel(Client *self, uint16 level, uint32 &returnValue, bool &ignoreDefault); void GetExperienceForKill(Client *self, Mob *against, uint32 &returnValue, bool &ignoreDefault); + void CheckFizzle(Client *self, uint16 &spell_id, SPDat_Spell_Struct spell_struct, bool &returnValue, bool &ignoreDefault); private: LuaParser *parser_; lua_State *L; @@ -40,4 +41,5 @@ private: bool m_has_get_required_aa_experience; bool m_has_get_exp_for_level; bool m_has_get_experience_for_kill; + bool m_has_check_fizzle; }; diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index ebe88f32a..1e69a7076 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -1377,4 +1377,13 @@ uint32 LuaParser::GetExperienceForKill(Client *self, Mob *against, bool &ignoreD return retval; } +bool LuaParser::CheckFizzle(Client *self, uint16 &spell_id, SPDat_Spell_Struct spell_struct, bool &ignoreDefault) +{ + bool retValue = false; + for (auto &mod : mods_) { + mod.CheckFizzle(self, spell_id, spell_struct, retValue, ignoreDefault); + } + return retValue; +} + #endif diff --git a/zone/lua_parser.h b/zone/lua_parser.h index 8265a191f..929f3d300 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -99,6 +99,8 @@ public: uint32 GetRequiredAAExperience(Client *self, bool &ignoreDefault); uint32 GetEXPForLevel(Client *self, uint16 level, bool &ignoreDefault); uint32 GetExperienceForKill(Client *self, Mob *against, bool &ignoreDefault); + bool CheckFizzle(Client *self, uint16 &spell_id, SPDat_Spell_Struct spell_struct, bool &ignoreDefault); + private: LuaParser(); diff --git a/zone/spells.cpp b/zone/spells.cpp index ea925ecda..74cd0db8c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -82,6 +82,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "string_ids.h" #include "worldserver.h" #include "fastmath.h" +#include "lua_parser.h" #include #include @@ -734,6 +735,16 @@ bool Mob::CheckFizzle(uint16 spell_id) bool Client::CheckFizzle(uint16 spell_id) { +#ifdef LUA_EQEMU + bool ignoreDefault = false; + bool fizzle = LuaParser::Instance()->CheckFizzle(this, spell_id, spells[spell_id], ignoreDefault); + + if (!fizzle) { + return false; + } else if (ignoreDefault) { + return true; + } +#endif // GMs don't fizzle if (GetGM()) return(true);