diff --git a/common/net/console_server.cpp b/common/net/console_server.cpp index d8f08b05d..ab420bd42 100644 --- a/common/net/console_server.cpp +++ b/common/net/console_server.cpp @@ -25,6 +25,23 @@ void EQ::Net::ConsoleServer::RegisterLogin(ConsoleServerLoginCallback fn) m_login = fn; } +EQ::Net::ConsoleServerConnection *EQ::Net::ConsoleServer::FindByAccountName(const std::string &acct_name) { + for (auto &iter : m_connections) { + if (iter.second->UserName().compare(acct_name) == 0) { + return iter.second.get(); + } + } + + return nullptr; +} + + +void EQ::Net::ConsoleServer::SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function onTell) { + for (auto &iter : m_connections) { + iter.second->SendChannelMessage(scm, onTell); + } +} + void EQ::Net::ConsoleServer::ConnectionDisconnected(ConsoleServerConnection *c) { auto iter = m_connections.find(c->GetUUID()); diff --git a/common/net/console_server.h b/common/net/console_server.h index 5a0c48f8e..d28f43844 100644 --- a/common/net/console_server.h +++ b/common/net/console_server.h @@ -25,7 +25,8 @@ namespace EQ void RegisterCall(const std::string& command, int status_required, const std::string& help_definition, ConsoleServerCallback fn); void RegisterLogin(ConsoleServerLoginCallback fn); - + ConsoleServerConnection *FindByAccountName(const std::string &acct_name); + void SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function onTell); private: void ConnectionDisconnected(ConsoleServerConnection *c); void ProcessCommand(ConsoleServerConnection *c, const std::string& cmd); diff --git a/common/net/console_server_connection.cpp b/common/net/console_server_connection.cpp index 8764f09b6..249817818 100644 --- a/common/net/console_server_connection.cpp +++ b/common/net/console_server_connection.cpp @@ -2,6 +2,8 @@ #include "../common/util/uuid.h" #include "../common/net/packet.h" #include "../common/eqemu_logsys.h" +#include "../common/servertalk.h" +#include "../common/rulesys.h" EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr connection) { @@ -107,6 +109,53 @@ void EQ::Net::ConsoleServerConnection::QueueMessage(const std::string &msg) } } +bool EQ::Net::ConsoleServerConnection::SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function onTell) { + if (!m_accept_messages) { + return false; + } + + switch (scm->chan_num) { + if (RuleB(Chat, ServerWideAuction)) { + case 4: { + QueueMessage(fmt::format("{0} auctions, '{1}'", scm->from, scm->message)); + break; + } + } + + if (RuleB(Chat, ServerWideOOC)) { + case 5: { + QueueMessage(fmt::format("{0} says ooc, '{1}'", scm->from, scm->message)); + break; + } + } + + case 6: { + QueueMessage(fmt::format("{0} BROADCASTS, '{1}'", scm->from, scm->message)); + break; + } + + case 7: { + QueueMessage(fmt::format("[{0}] tells you, '{1}'", scm->from, scm->message)); + if (onTell) { + onTell(); + } + + break; + } + + case 11: { + QueueMessage(fmt::format("{0} GMSAYS, '{1}'", scm->from, scm->message)); + break; + } + + default: { + return false; + } + } + + return true; +} + void EQ::Net::ConsoleServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz) { for (size_t i = 0; i < sz; ++i) { diff --git a/common/net/console_server_connection.h b/common/net/console_server_connection.h index d826ba07b..a49700841 100644 --- a/common/net/console_server_connection.h +++ b/common/net/console_server_connection.h @@ -4,6 +4,8 @@ #include #include +struct ServerChannelMessage_Struct; + namespace EQ { namespace Net @@ -42,6 +44,7 @@ namespace EQ bool AcceptMessages() const { return m_accept_messages; } void SetAcceptMessages(bool v) { m_accept_messages = v; } void QueueMessage(const std::string &msg); + bool SendChannelMessage(const ServerChannelMessage_Struct* scm, std::function onTell); private: void OnRead(TCPConnection* c, const unsigned char* data, size_t sz); void OnDisconnect(TCPConnection* c); diff --git a/utils/mods/classic_wow_experience.lua b/utils/mods/classic_wow_experience.lua new file mode 100644 index 000000000..cde740ff6 --- /dev/null +++ b/utils/mods/classic_wow_experience.lua @@ -0,0 +1,159 @@ +--Mod file to demo changing the experience tables +--In this case I used some old wow tables (roughly it's not 100%) + +function GetRequiredAAExperience(e) + e.level = 51; + return GetEXPForLevel(e); +end + +function GetExperienceForKill(e) + local ML = e.other:GetLevel(); + local CL = e.self:GetLevel(); + + if(ML > CL) then + local lmod = (ML - CL) * 0.05; + if(lmod > 1.0) then + lmod = 1.0; + end + e.ReturnValue = BaseXP(ML) * (1 + lmod); + elseif(ML < CL) then + local lmod = (CL - ML) * 0.05; + if(lmod > 1.0) then + lmod = 1.0; + end + e.ReturnValue = BaseXP(ML) * (1 - lmod); + else + e.ReturnValue = BaseXP(ML); + end + + e.IgnoreDefault = true; + return e; +end + +function BaseXP(L) + local base = L * 5; + + if(L < 60) then + base = base + 45; + elseif(L < 70) then + base = base + 235; + elseif(L < 80) then + base = base + 580; + else + base = base + 1875; + end + + return base; +end + +function GetEXPForLevel(e) + local exp_table = { + 0, + 400, + 900, + 1400, + 2100, + 2800, + 3600, + 4500, + 5400, + 6500, + 7600, + 8700, + 9800, + 11000, + 12300, + 13600, + 15000, + 16400, + 17800, + 19300, + 20800, + 22400, + 24000, + 25500, + 27200, + 28900, + 30500, + 32200, + 33900, + 36300, + 38800, + 41600, + 44600, + 48000, + 51400, + 55000, + 58700, + 62400, + 66200, + 70200, + 74300, + 78500, + 82800, + 87100, + 91600, + 96300, + 101000, + 105800, + 110700, + 115700, + 120900, + 126100, + 131500, + 137000, + 142500, + 148200, + 154000, + 159900, + 165800, + 172000, + 290000, + 317000, + 349000, + 386000, + 428000, + 475000, + 527000, + 585000, + 648000, + 717000, + 1523800, + 1539000, + 1555700, + 1571800, + 1587900, + 1604200, + 1620700, + 1637400, + 1653900, + 1670800, + 1670800, + 1670800, + 2121500, + 2669000, + 3469000, + 4583000, + 13000000, + 15080000, + 22600000, + 27300000, + 32800000 + }; + + if(e.level < 1) then + e.ReturnValue = 0; + e.IgnoreDefault = true; + return e; + end + + if(e.level > 91) then + e.ReturnValue = exp_table[91]; + e.IgnoreDefault = true; + return e; + end + + e.ReturnValue = exp_table[e.level]; + e.IgnoreDefault = true; + return e; +end diff --git a/world/net.cpp b/world/net.cpp index df3749829..09511c3f2 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -392,12 +392,12 @@ int main(int argc, char** argv) { server_connection->Listen(server_opts); Log(Logs::General, Logs::World_Server, "Server (TCP) listener started."); - server_connection->OnConnectionIdentified("Zone", [](std::shared_ptr connection) { + server_connection->OnConnectionIdentified("Zone", [&console](std::shared_ptr connection) { LogF(Logs::General, Logs::World_Server, "New Zone Server connection from {2} at {0}:{1}", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); numzones++; - zoneserver_list.Add(new ZoneServer(connection)); + zoneserver_list.Add(new ZoneServer(connection, console.get())); }); server_connection->OnConnectionRemoved("Zone", [](std::shared_ptr connection) { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index a3fc7da7f..01cca7557 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -46,7 +46,7 @@ extern UCSConnection UCSLink; extern QueryServConnection QSLink; void CatchSignal(int sig_num); -ZoneServer::ZoneServer(std::shared_ptr connection) +ZoneServer::ZoneServer(std::shared_ptr connection, EQ::Net::ConsoleServer *console) : tcpc(connection), zone_boot_timer(5000) { /* Set Process tracking variable defaults */ @@ -73,6 +73,8 @@ ZoneServer::ZoneServer(std::shared_ptr conn zone_boot_timer.Disable(); } })); + + this->console = console; } ZoneServer::~ZoneServer() { @@ -412,6 +414,27 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } if (scm->chan_num == 7 || scm->chan_num == 14) { + if (scm->deliverto[0] == '*') { + + if (console) { + auto con = console->FindByAccountName(&scm->deliverto[1]); + if (((!con) || (!con->SendChannelMessage(scm, [&scm]() { + auto pack = new ServerPacket(ServerOP_ChannelMessage, + sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1); + memcpy(pack->pBuffer, scm, pack->size); + ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*)pack->pBuffer; + strcpy(scm2->deliverto, scm2->from); + scm2->noreply = true; + client_list.SendPacket(scm->from, pack); + safe_delete(pack); + }))) && (!scm->noreply)) + { + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); + } + } + break; + } + ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { @@ -462,6 +485,20 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { cle->Server()->SendPacket(pack); } else { + if (scm->chan_num == 5 || scm->chan_num == 6 || scm->chan_num == 11) { + if (console) { + console->SendChannelMessage(scm, [&scm]() { + auto pack = new ServerPacket(ServerOP_ChannelMessage, + sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1); + memcpy(pack->pBuffer, scm, pack->size); + ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*)pack->pBuffer; + strcpy(scm2->deliverto, scm2->from); + scm2->noreply = true; + client_list.SendPacket(scm->from, pack); + safe_delete(pack); + }); + } + } zoneserver_list.SendPacket(pack); } break; diff --git a/world/zoneserver.h b/world/zoneserver.h index a6bbc8373..1f9e9516c 100644 --- a/world/zoneserver.h +++ b/world/zoneserver.h @@ -22,6 +22,7 @@ #include "../net/servertalk_server.h" #include "../event/timer.h" #include "../timer.h" +#include "console.h" #include #include @@ -31,7 +32,7 @@ class ServerPacket; class ZoneServer : public WorldTCPConnection { public: - ZoneServer(std::shared_ptr connection); + ZoneServer(std::shared_ptr connection, EQ::Net::ConsoleServer *console); ~ZoneServer(); virtual inline bool IsZoneServer() { return true; } @@ -97,6 +98,7 @@ private: uint32 zone_os_process_id; std::string launcher_name; //the launcher which started us std::string launched_name; //the name of the zone we launched. + EQ::Net::ConsoleServer *console; }; #endif diff --git a/zone/aa.cpp b/zone/aa.cpp index 51cb376da..31ad9c162 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -916,7 +916,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) { void Client::SendAlternateAdvancementStats() { auto outapp = new EQApplicationPacket(OP_AAExpUpdate, sizeof(AltAdvStats_Struct)); AltAdvStats_Struct *aps = (AltAdvStats_Struct *)outapp->pBuffer; - aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)max_AAXP); + aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)GetRequiredAAExperience()); aps->unspent = m_pp.aapoints; aps->percentage = m_epp.perAA; QueuePacket(outapp); diff --git a/zone/attack.cpp b/zone/attack.cpp index 2b5fb4a34..1f7738119 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2222,7 +2222,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil Group *kg = entity_list.GetGroupByClient(give_exp_client); Raid *kr = entity_list.GetRaidByClient(give_exp_client); - int32 finalxp = EXP_FORMULA; + int32 finalxp = give_exp_client->GetExperienceForKill(this); finalxp = give_exp_client->mod_client_xp(finalxp, this); if (kr) { diff --git a/zone/client.h b/zone/client.h index 0f0f90873..50f36ba72 100644 --- a/zone/client.h +++ b/zone/client.h @@ -571,6 +571,7 @@ public: void AddCrystals(uint32 Radiant, uint32 Ebon); void SendCrystalCounts(); + uint32 GetExperienceForKill(Mob *against); void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); uint32 CalcEXP(uint8 conlevel = 0xFF); void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); @@ -796,12 +797,12 @@ public: void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } int GetAAPoints() { return m_pp.aapoints; } int GetSpentAA() { return m_pp.aapoints_spent; } + uint32 GetRequiredAAExperience(); //old AA methods that we still use void ResetAA(); void RefundAA(); void SendClearAA(); - inline uint32 GetMaxAAXP(void) const { return max_AAXP; } inline uint32 GetAAXP() const { return m_pp.expAA; } inline uint32 GetAAPercent() const { return m_epp.perAA; } int16 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); @@ -1493,7 +1494,6 @@ private: uint32 tribute_master_id; - uint32 max_AAXP; bool npcflag; uint8 npclevel; bool feigned; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 361c67d40..9249f5266 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1355,8 +1355,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Set Total Seconds Played */ TotalSecondsPlayed = m_pp.timePlayedMin * 60; - /* Set Max AA XP */ - max_AAXP = RuleI(AA, ExpPerPoint); + /* If we can maintain intoxication across zones, check for it */ if (!RuleB(Character, MaintainIntoxicationAcrossZones)) m_pp.intoxication = 0; diff --git a/zone/exp.cpp b/zone/exp.cpp index 044a102e0..fe2cfd543 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -28,6 +28,7 @@ #include "queryserv.h" #include "quest_parser_collection.h" +#include "lua_parser.h" #include "string_ids.h" #ifdef BOTS @@ -153,6 +154,26 @@ uint32 Client::CalcEXP(uint8 conlevel) { return in_add_exp; } +uint32 Client::GetExperienceForKill(Mob *against) +{ +#ifdef LUA_EQEMU + uint32 lua_ret = 0; + bool ignoreDefault = false; + lua_ret = LuaParser::Instance()->GetExperienceForKill(this, against, ignoreDefault); + + if (ignoreDefault) { + return lua_ret; + } +#endif + + if (against && against->IsNPC()) { + uint32 level = (uint32)against->GetLevel(); + return EXP_FORMULA; + } + + return 0; +} + void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { this->EVENT_ITEM_ScriptStopReturn(); @@ -339,8 +360,8 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { Log(Logs::Detail, Logs::None, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false"); - //max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51); //GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients - max_AAXP = RuleI(AA, ExpPerPoint); //this may be redundant since we're doing this in Client::FinishConnState2() + + auto max_AAXP = GetRequiredAAExperience(); if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) { Message(13, "Error in Client::SetEXP. EXP not set."); return; // Must be invalid class/race @@ -460,14 +481,13 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //add in how many points we had m_pp.aapoints += last_unspentAA; - //set_aaxp = m_pp.expAA % max_AAXP; //figure out how many points were actually gained /*uint32 gained = m_pp.aapoints - last_unspentAA;*/ //unused //Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA); char val1[20]={0}; - Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2. + Message_StringID(MT_Experience, GAIN_ABILITY_POINT, ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2. /* QS: PlayerLogAARate */ if (RuleB(QueryServ, PlayerLogAARate)){ @@ -571,8 +591,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { char val1[20]={0}; char val2[20]={0}; char val3[20]={0}; - Message_StringID(MT_Experience, GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3). - //Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP); + Message_StringID(MT_Experience, GM_GAINXP, ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3). } } @@ -664,6 +683,15 @@ void Client::SetLevel(uint8 set_level, bool command) // Add: You can set the values you want now, client will be always sync :) - Merkur uint32 Client::GetEXPForLevel(uint16 check_level) { +#ifdef LUA_EQEMU + uint32 lua_ret = 0; + bool ignoreDefault = false; + lua_ret = LuaParser::Instance()->GetEXPForLevel(this, check_level, ignoreDefault); + + if (ignoreDefault) { + return lua_ret; + } +#endif uint16 check_levelm1 = check_level-1; float mod; @@ -933,3 +961,17 @@ uint32 Client::GetCharMaxLevelFromQGlobal() { return false; } + +uint32 Client::GetRequiredAAExperience() { +#ifdef LUA_EQEMU + uint32 lua_ret = 0; + bool ignoreDefault = false; + lua_ret = LuaParser::Instance()->GetRequiredAAExperience(this, ignoreDefault); + + if (ignoreDefault) { + return lua_ret; + } +#endif + + return RuleI(AA, ExpPerPoint); +} diff --git a/zone/lua_mod.cpp b/zone/lua_mod.cpp index f35992d94..1a97945a5 100644 --- a/zone/lua_mod.cpp +++ b/zone/lua_mod.cpp @@ -39,6 +39,9 @@ void LuaMod::Init() m_has_avoid_damage = parser_->HasFunction("AvoidDamage", package_name_); m_has_check_hit_chance = parser_->HasFunction("CheckHitChance", package_name_); m_has_try_critical_hit = parser_->HasFunction("TryCriticalHit", package_name_); + m_has_get_required_aa_experience = parser_->HasFunction("GetRequiredAAExperience", package_name_); + m_has_get_exp_for_level = parser_->HasFunction("GetEXPForLevel", package_name_); + m_has_get_experience_for_kill = parser_->HasFunction("GetExperienceForKill", package_name_); } void PutDamageHitInfo(lua_State *L, luabind::adl::object &e, DamageHitInfo &hit) { @@ -388,8 +391,6 @@ bool LuaMod::CheckHitChance(Mob *self, Mob* other, DamageHitInfo &hit, bool &ign return retval; } -//void TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault); - void LuaMod::TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault) { int start = lua_gettop(L); ignoreDefault = false; @@ -440,3 +441,161 @@ void LuaMod::TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraA lua_pop(L, n); } } + +uint32 LuaMod::GetRequiredAAExperience(Client *self, bool &ignoreDefault) +{ + int start = lua_gettop(L); + ignoreDefault = false; + uint32 retval = 0; + + try { + if (!m_has_get_required_aa_experience) { + return retval; + } + + lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str()); + lua_getfield(L, -1, "GetRequiredAAExperience"); + + Lua_Client l_self(self); + luabind::adl::object e = luabind::newtable(L); + e["self"] = l_self; + e.push(L); + + if (lua_pcall(L, 1, 1, 0)) { + std::string error = lua_tostring(L, -1); + parser_->AddError(error); + lua_pop(L, 1); + return retval; + } + + 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_TNUMBER) { + retval = 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); + } + + return retval; +} + +uint32 LuaMod::GetEXPForLevel(Client *self, uint16 level, bool &ignoreDefault) { + int start = lua_gettop(L); + ignoreDefault = false; + uint32 retval = 0; + + try { + if (!m_has_get_exp_for_level) { + return retval; + } + + lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str()); + lua_getfield(L, -1, "GetEXPForLevel"); + + Lua_Client l_self(self); + luabind::adl::object e = luabind::newtable(L); + e["self"] = l_self; + e["level"] = level; + e.push(L); + + if (lua_pcall(L, 1, 1, 0)) { + std::string error = lua_tostring(L, -1); + parser_->AddError(error); + lua_pop(L, 1); + return retval; + } + + 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_TNUMBER) { + retval = 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); + } + + return retval; +} + +uint32 LuaMod::GetExperienceForKill(Client *self, Mob *against, bool &ignoreDefault) +{ + int start = lua_gettop(L); + ignoreDefault = false; + uint32 retval = 0; + + try { + if (!m_has_get_experience_for_kill) { + return retval; + } + + lua_getfield(L, LUA_REGISTRYINDEX, package_name_.c_str()); + lua_getfield(L, -1, "GetExperienceForKill"); + + Lua_Client l_self(self); + Lua_Mob l_other(against); + luabind::adl::object e = luabind::newtable(L); + e["self"] = l_self; + e["other"] = l_other; + e.push(L); + + if (lua_pcall(L, 1, 1, 0)) { + std::string error = lua_tostring(L, -1); + parser_->AddError(error); + lua_pop(L, 1); + return retval; + } + + 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_TNUMBER) { + retval = 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); + } + + return retval; +} diff --git a/zone/lua_mod.h b/zone/lua_mod.h index 0ed017cdb..bef40bbcc 100644 --- a/zone/lua_mod.h +++ b/zone/lua_mod.h @@ -22,7 +22,9 @@ public: bool AvoidDamage(Mob *self, Mob *other, DamageHitInfo &hit, bool &ignoreDefault); bool CheckHitChance(Mob *self, Mob* other, DamageHitInfo &hit, bool &ignoreDefault); void TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault); - + uint32 GetRequiredAAExperience(Client *self, bool &ignoreDefault); + uint32 GetEXPForLevel(Client *self, uint16 level, bool &ignoreDefault); + uint32 GetExperienceForKill(Client *self, Mob *against, bool &ignoreDefault); private: LuaParser *parser_; lua_State *L; @@ -33,4 +35,7 @@ private: bool m_has_avoid_damage; bool m_has_check_hit_chance; bool m_has_try_critical_hit; + bool m_has_get_required_aa_experience; + bool m_has_get_exp_for_level; + bool m_has_get_experience_for_kill; }; diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 4aa0e9165..f77ccb4da 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -959,6 +959,13 @@ void LuaParser::ReloadQuests() { if (load_order) { char file_name[256] = { 0 }; while (fgets(file_name, 256, load_order) != nullptr) { + for (int i = 0; i < 256; ++i) { + if (file_name[i] == '\n') { + file_name[i] = 0; + break; + } + } + LoadScript("mods/" + std::string(file_name), file_name); mods_.push_back(LuaMod(L, this, file_name)); } @@ -1324,3 +1331,30 @@ void LuaParser::TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, Ext mod.TryCriticalHit(self, defender, hit, opts, ignoreDefault); } } + +uint32 LuaParser::GetRequiredAAExperience(Client *self, bool &ignoreDefault) +{ + uint32 retval = false; + for (auto &mod : mods_) { + retval = mod.GetRequiredAAExperience(self, ignoreDefault); + } + return retval; +} + +uint32 LuaParser::GetEXPForLevel(Client *self, uint16 level, bool &ignoreDefault) +{ + uint32 retval = false; + for (auto &mod : mods_) { + retval = mod.GetEXPForLevel(self, level, ignoreDefault); + } + return retval; +} + +uint32 LuaParser::GetExperienceForKill(Client *self, Mob *against, bool &ignoreDefault) +{ + uint32 retval = false; + for (auto &mod : mods_) { + retval = mod.GetExperienceForKill(self, against, ignoreDefault); + } + return retval; +} diff --git a/zone/lua_parser.h b/zone/lua_parser.h index 02519cab5..f99553ccc 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -95,6 +95,9 @@ public: bool AvoidDamage(Mob *self, Mob *other, DamageHitInfo &hit, bool &ignoreDefault); bool CheckHitChance(Mob *self, Mob* other, DamageHitInfo &hit, bool &ignoreDefault); void TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault); + uint32 GetRequiredAAExperience(Client *self, bool &ignoreDefault); + uint32 GetEXPForLevel(Client *self, uint16 level, bool &ignoreDefault); + uint32 GetExperienceForKill(Client *self, Mob *against, bool &ignoreDefault); private: LuaParser();