diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 4a8bd12a9..bf52b9276 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -547,6 +547,12 @@ void Lua_NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_del self->SetSimpleRoamBox(box_size, move_distance, move_delay); } +void Lua_NPC::RecalculateSkills() +{ + Lua_Safe_Call_Void(); + self->RecalculateSkills(); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -657,7 +663,8 @@ luabind::scope lua_register_npc() { .def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop) .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop) .def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC) - .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating); + .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating) + .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 92ad73cd7..1c7c879f7 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -134,6 +134,7 @@ public: void SetSimpleRoamBox(float box_size); void SetSimpleRoamBox(float box_size, float move_distance); void SetSimpleRoamBox(float box_size, float move_distance, int move_delay); + void RecalculateSkills(); }; #endif diff --git a/zone/npc.cpp b/zone/npc.cpp index 96efc83a8..17e3c5957 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3209,3 +3209,28 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) } } + +void NPC::RecalculateSkills() +{ + int r; + for (r = 0; r <= EQEmu::skills::HIGHEST_SKILL; r++) { + skills[r] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)r, level); + } + + // some overrides -- really we need to be able to set skills for mobs in the DB + // There are some known low level SHM/BST pets that do not follow this, which supports + // the theory of needing to be able to set skills for each mob separately + if (!IsBot()) { + if (level > 50) { + skills[EQEmu::skills::SkillDoubleAttack] = 250; + skills[EQEmu::skills::SkillDualWield] = 250; + } + else if (level > 3) { + skills[EQEmu::skills::SkillDoubleAttack] = level * 5; + skills[EQEmu::skills::SkillDualWield] = skills[EQEmu::skills::SkillDoubleAttack]; + } + else { + skills[EQEmu::skills::SkillDoubleAttack] = level * 5; + } + } +} \ No newline at end of file diff --git a/zone/npc.h b/zone/npc.h index d0ecdf1ca..e545ffb38 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -476,6 +476,8 @@ public: inline bool IsSkipAutoScale() const { return skip_auto_scale; } + void RecalculateSkills(); + protected: const NPCType* NPCTypedata; diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 2dae9e776..1d40474f3 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2451,6 +2451,28 @@ XS(XS_NPC_SetSimpleRoamBox) { XSRETURN_EMPTY; } + +XS(XS_NPC_RecalculateSkills); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_RecalculateSkills) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RecalculateSkills(THIS)"); + { + NPC *THIS; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV *) SvRV(ST(0))); + THIS = INT2PTR(NPC *, tmp); + } else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + THIS->RecalculateSkills(); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -2565,6 +2587,7 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); newXSproto(strcpy(buf, "GetCombatState"), XS_NPC_GetCombatState, file, "$"); newXSproto(strcpy(buf, "SetSimpleRoamBox"), XS_NPC_SetSimpleRoamBox, file, "$$;$$"); + newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); XSRETURN_YES; }