Work in progress for 0.9.9 so far (making its own branch for now)

- Fixed a repeated rubberband like behavior that Spawn's would run forward toward their target in combat or on their return run to their starting point.
- WS_HearCastSpell for 546-561 version has an extra byte at the end of the packet we were missing (might be in later clients too have to confirm)
- PlayerScripts support added, new function calls, AddTimer support for Players.
- Static Zones / Special Zones will now silently check to startup zones without reporting the log message, log message only on startup or taking on peer leadership.
- Broadcast and Global Announcement are now supported through peering.
- Fix #22 identified a number of loose spawn pointers and changed to int32 spawn id reference.
- Fix #1 support for all known chat codes for various channels, spell casting and damage.
- In conjunction with Fix #1 spell combat messages are fixed, no longer 'YOU cast' when another spawn casts, added last_tell_name to track the chat code %RT server side.
- size_mod added to InfoStruct Float, supports shrinking and growing Non Player's. InfoStruct also has a UINT ignore_size_mod_calc set to 0 by default, when 1 it will let you set the size_mod and items/spells will not override it from stat calculation.
- XP Table is now static (global) in the Player class so we do not constantly call the database each time the player needs to know a level's XP requirement.
- Removal of duplicate spell cast success and effect messages.
- Fix #21 blue xp bar for KoS and DoF displays properly now.
- GetExpRequiredByLevel(level) added to return the EXP required for to reach the level.
- Fix #25 teleporters cleaned up during /reload spawns to avoid crash
- Fix #16 /reload items supported in peering mode.
This commit is contained in:
Emagi 2025-06-08 14:53:52 -04:00
parent 4e43c73f9c
commit 82a5e96000
27 changed files with 964 additions and 234 deletions

View File

@ -3712,6 +3712,12 @@ to zero and treated like placeholders." />
<Data ElementName="damage" Type="int16" /> <Data ElementName="damage" Type="int16" />
<Data ElementName="spell_name" Type="EQ2_8Bit_String" Size="1" /> <Data ElementName="spell_name" Type="EQ2_8Bit_String" Size="1" />
</Struct> </Struct>
<Struct Name="WS_SetSocialMsg" ClientVersion="1" OpcodeName="OP_SetSocialMsg">
<Data ElementName="num_socials" Type="int8" />
<Data ElementName="social_array" Type="Array" ArraySizeVariable="num_socials">
<Data ElementName="social_name" Type="EQ2_8Bit_String" Size="1" />
</Data>
</Struct>
<Struct Name="WS_HearMultipleDamage" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd"> <Struct Name="WS_HearMultipleDamage" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
<Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" /> <Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" />
<Data ElementName="num_dmg" Type="int8" /> <Data ElementName="num_dmg" Type="int8" />
@ -7642,6 +7648,7 @@ to zero and treated like placeholders." />
<Data ElementName="spell_visual" Type="int16" /> <Data ElementName="spell_visual" Type="int16" />
<Data ElementName="cast_time" Type="float" /> <Data ElementName="cast_time" Type="float" />
<Data ElementName="spell_level" Type="int8" /> <Data ElementName="spell_level" Type="int8" />
<Data ElementName="spell_crit" Type="int8" />
</Struct> </Struct>
<Struct Name="WS_HearCastSpell" ClientVersion="562" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearSpellCastCmd"> <Struct Name="WS_HearCastSpell" ClientVersion="562" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearSpellCastCmd">
<Data ElementName="spawn_id" Type="int32" /> <Data ElementName="spawn_id" Type="int32" />
@ -21034,4 +21041,11 @@ to zero and treated like placeholders." />
<Struct Name="WS_CreateBoatTransportMsg" ClientVersion="1" OpcodeName="OP_CreateBoatTransportsMsg"> <Struct Name="WS_CreateBoatTransportMsg" ClientVersion="1" OpcodeName="OP_CreateBoatTransportsMsg">
<Data ElementName="path_id" Type="int8" /> <Data ElementName="path_id" Type="int8" />
</Struct> </Struct>
<Struct Name="WS_SetSocialMsg" ClientVersion="1" OpcodeName="OP_SetSocialMsg">
<Data ElementName="num_socials" Type="int8" />
<Data ElementName="social_array" Type="Array" ArraySizeVariable="num_socials">
<Data ElementName="social_name" Type="EQ2_8Bit_String" Size="1" />
<Data ElementName="social_message" Type="EQ2_8Bit_String" Size="1" />
</Data>
</Struct>
</EQ2Emulator> </EQ2Emulator>

View File

@ -140,6 +140,10 @@ bool Entity::AttackAllowed(Entity* target, float distance, bool range_attack) {
return false; return false;
} }
} }
if(attacker->IsNPC() && target->IsNPC() && attacker->GetFactionID() > 10 && attacker->GetFactionID() == target->GetFactionID()) {
return false;
}
if (attacker->IsPlayer() && target->IsPlayer()) if (attacker->IsPlayer() && target->IsPlayer())
{ {
@ -495,26 +499,6 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8
CheckProcs(PROC_TYPE_OFFENSIVE, victim); CheckProcs(PROC_TYPE_OFFENSIVE, victim);
CheckProcs(PROC_TYPE_MAGICAL_OFFENSIVE, victim); CheckProcs(PROC_TYPE_MAGICAL_OFFENSIVE, victim);
if(spell->GetSpellData()->success_message.length() > 0){
Client* client = nullptr;
if(IsPlayer())
client = ((Player*)this)->GetClient();
if(client){
string success_message = spell->GetSpellData()->success_message;
if(success_message.find("%t") < 0xFFFFFFFF)
success_message.replace(success_message.find("%t"), 2, victim->GetName());
client->Message(CHANNEL_YOU_CAST, success_message.c_str());
//commented out the following line as it was causing a duplicate message EmemJR 5/4/2019
//GetZone()->SendDamagePacket(this, victim, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, hit_result, damage_type, 0, spell->GetName());
}
}
if(spell->GetSpellData()->effect_message.length() > 0){
string effect_message = spell->GetSpellData()->effect_message;
if(effect_message.find("%t") < 0xFFFFFFFF)
effect_message.replace(effect_message.find("%t"), 2, victim->GetName());
GetZone()->SimpleMessage(CHANNEL_SPELLS, effect_message.c_str(), victim, 50);
}
} }
else { else {
successful_hit = false; successful_hit = false;
@ -603,15 +587,15 @@ bool Entity::ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32
if(IsPlayer()) if(IsPlayer())
client = ((Player*)this)->GetClient(); client = ((Player*)this)->GetClient();
if(client) { if(client) {
if(success_msg.find("%t") < 0xFFFFFFFF) std::string castMsg = std::string(success_msg);
success_msg.replace(success_msg.find("%t"), 2, victim->GetName()); SpellProcess::ReplaceEffectTokens(castMsg, (Spawn*)this, victim);
client->Message(CHANNEL_YOU_CAST, success_msg.c_str()); client->Message(CHANNEL_YOU_CAST, castMsg.c_str());
} }
} }
if (effect_msg.length() > 0) { if (effect_msg.length() > 0) {
if(effect_msg.find("%t") < 0xFFFFFFFF) std::string effectMsg = std::string(effect_msg);
effect_msg.replace(effect_msg.find("%t"), 2, victim->GetName()); SpellProcess::ReplaceEffectTokens(effectMsg, (Spawn*)this, victim);
GetZone()->SimpleMessage(CHANNEL_SPELLS, effect_msg.c_str(), victim, 50); GetZone()->SimpleMessage(CHANNEL_SPELLS, effectMsg.c_str(), victim, 50);
} }
} }
else { else {

View File

@ -1,22 +1,23 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful, EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -1987,6 +1988,18 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break; break;
} }
case COMMAND_RELOAD_PLAYERSCRIPTS: {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Player Scripts...");
world.SetReloadingSubsystem("PlayerScripts");
world.ResetPlayerScripts();
world.LoadPlayerScripts();
if (lua_interface)
lua_interface->DestroyPlayerScripts();
world.RemoveReloadingSubSystem("PlayerScripts");
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
case COMMAND_RELOAD_ENTITYCOMMANDS: { case COMMAND_RELOAD_ENTITYCOMMANDS: {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Entity Commands..."); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Entity Commands...");
world.SetReloadingSubsystem("EntityCommands"); world.SetReloadingSubsystem("EntityCommands");
@ -2679,7 +2692,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
break; break;
} }
case COMMAND_BANK_DEPOSIT:{ case COMMAND_BANK_DEPOSIT:{
if(client->GetBanker() && sep && sep->arg[0]){ Spawn* banker = client->GetCurrentZone()->GetSpawnByID(client->GetBanker());
if(banker && sep && sep->arg[0]){
int64 amount = 0; int64 amount = 0;
string deposit = string(sep->arg[0]); string deposit = string(sep->arg[0]);
amount = atoi64(deposit.c_str()); amount = atoi64(deposit.c_str());
@ -2688,7 +2702,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
break; break;
} }
case COMMAND_BANK_WITHDRAWAL:{ case COMMAND_BANK_WITHDRAWAL:{
if(client->GetBanker() && sep && sep->arg[0] && sep->IsNumber(0)){ Spawn* banker = client->GetCurrentZone()->GetSpawnByID(client->GetBanker());
if(banker && sep && sep->arg[0] && sep->IsNumber(0)){
int64 amount = 0; int64 amount = 0;
string deposit = string(sep->arg[0]); string deposit = string(sep->arg[0]);
amount = atoi64(deposit.c_str()); amount = atoi64(deposit.c_str());
@ -2891,7 +2906,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
} }
case COMMAND_CLASS:{ case COMMAND_CLASS:{
if(sep && sep->arg[ndx][0]){ if(sep && sep->arg[ndx][0]){
client->GetPlayer()->SetPlayerAdventureClass(atoi(sep->arg[ndx])); client->GetPlayer()->SetPlayerAdventureClass(atoi(sep->arg[ndx]), true);
}else }else
client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /class {class_id}"); client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /class {class_id}");
break; break;
@ -3137,6 +3152,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
world.ResetZoneScripts(); world.ResetZoneScripts();
database.LoadZoneScriptData(); database.LoadZoneScriptData();
world.ResetPlayerScripts();
world.LoadPlayerScripts();
if(lua_interface) { if(lua_interface) {
lua_interface->DestroySpawnScripts(); lua_interface->DestroySpawnScripts();
@ -3144,6 +3162,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
lua_interface->DestroyQuests(); lua_interface->DestroyQuests();
lua_interface->DestroyItemScripts(); lua_interface->DestroyItemScripts();
lua_interface->DestroyZoneScripts(); lua_interface->DestroyZoneScripts();
lua_interface->DestroyPlayerScripts();
} }
int32 quest_count = database.LoadQuests(); int32 quest_count = database.LoadQuests();
@ -5372,14 +5391,14 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
} }
case COMMAND_BROADCAST: { case COMMAND_BROADCAST: {
if (sep && sep->arg[0]) if (sep && sep->arg[0])
zone_list.HandleGlobalBroadcast(sep->argplus[0]); zone_list.TransmitBroadcast(sep->argplus[0]);
else else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /broadcast {message}"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /broadcast {message}");
break; break;
} }
case COMMAND_ANNOUNCE: { case COMMAND_ANNOUNCE: {
if (sep && sep->arg[0]) if (sep && sep->arg[0])
zone_list.HandleGlobalAnnouncement(sep->argplus[0]); zone_list.TransmitGlobalAnnouncement(sep->argplus[0]);
else else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /announce {message}"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /announce {message}");
break; break;
@ -5401,6 +5420,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
database.LoadMerchantInformation(); // we skip if there is only a reload of single item not all items database.LoadMerchantInformation(); // we skip if there is only a reload of single item not all items
} }
peer_manager.sendPeersMessage("/reloadcommand", command->handler, item_id);
if(item_id > 0) { if(item_id > 0) {
client->Message(CHANNEL_COLOR_YELLOW, "Reloaded item %u.", item_id); client->Message(CHANNEL_COLOR_YELLOW, "Reloaded item %u.", item_id);
} }
@ -6184,11 +6205,6 @@ void Commands::Command_DuelSurrender(Client* client, Seperator* sep)
PrintSep(sep, "COMMAND_DUEL_SURRENDER"); PrintSep(sep, "COMMAND_DUEL_SURRENDER");
LogWrite(MISC__TODO, 1, "Command", "TODO-Command: Surrender Duel Command"); LogWrite(MISC__TODO, 1, "Command", "TODO-Command: Surrender Duel Command");
client->Message(CHANNEL_COLOR_YELLOW, "You cannot duel other players (Not Implemented)"); client->Message(CHANNEL_COLOR_YELLOW, "You cannot duel other players (Not Implemented)");
// JA, just messin around ;)
char surrender[64];
sprintf(surrender, "%s surrendered like a cowardly dog!", client->GetPlayer()->GetName());
zone_list.HandleGlobalAnnouncement(surrender);
} }
/* /*

View File

@ -1,6 +1,6 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __EQ2_COMMANDS__ #ifndef __EQ2_COMMANDS__
#define __EQ2_COMMANDS__ #define __EQ2_COMMANDS__
#include "../../common/DataBuffer.h" #include "../../common/DataBuffer.h"
@ -981,8 +982,8 @@ private:
#define CANCEL_AA_PROFILE 757 #define CANCEL_AA_PROFILE 757
#define SAVE_AA_PROFILE 758 #define SAVE_AA_PROFILE 758
#define COMMAND_MOOD 800 #define COMMAND_MOOD 800
#define COMMAND_RELOAD_PLAYERSCRIPTS 801
#define COMMAND_MODIFY 1000 // INSERT INTO `commands`(`id`,`type`,`command`,`subcommand`,`handler`,`required_status`) VALUES ( NULL,'1','modify','','1000','200'); #define COMMAND_MODIFY 1000 // INSERT INTO `commands`(`id`,`type`,`command`,`subcommand`,`handler`,`required_status`) VALUES ( NULL,'1','modify','','1000','200');
#define COMMAND_MODIFY_CHARACTER 1001 #define COMMAND_MODIFY_CHARACTER 1001

View File

@ -291,6 +291,7 @@ void Entity::MapInfoStruct()
get_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::get_spell_reuse_speed, &info_struct); get_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::get_spell_reuse_speed, &info_struct);
get_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::get_spell_multi_attack, &info_struct); get_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::get_spell_multi_attack, &info_struct);
get_float_funcs["size_mod"] = l::bind(&InfoStruct::get_size_mod, &info_struct); get_float_funcs["size_mod"] = l::bind(&InfoStruct::get_size_mod, &info_struct);
get_int8_funcs["ignore_size_mod_calc"] = l::bind(&InfoStruct::get_ignore_size_mod_calc, &info_struct);
get_float_funcs["dps"] = l::bind(&InfoStruct::get_dps, &info_struct); get_float_funcs["dps"] = l::bind(&InfoStruct::get_dps, &info_struct);
get_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::get_dps_multiplier, &info_struct); get_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::get_dps_multiplier, &info_struct);
get_float_funcs["attackspeed"] = l::bind(&InfoStruct::get_attackspeed, &info_struct); get_float_funcs["attackspeed"] = l::bind(&InfoStruct::get_attackspeed, &info_struct);
@ -500,6 +501,7 @@ void Entity::MapInfoStruct()
set_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::set_spell_reuse_speed, &info_struct, l::_1); set_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::set_spell_reuse_speed, &info_struct, l::_1);
set_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::set_spell_multi_attack, &info_struct, l::_1); set_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::set_spell_multi_attack, &info_struct, l::_1);
set_float_funcs["size_mod"] = l::bind(&InfoStruct::set_size_mod, &info_struct, l::_1); set_float_funcs["size_mod"] = l::bind(&InfoStruct::set_size_mod, &info_struct, l::_1);
set_int8_funcs["ignore_size_mod_calc"] = l::bind(&InfoStruct::set_ignore_size_mod_calc, &info_struct, l::_1);
set_float_funcs["dps"] = l::bind(&InfoStruct::set_dps, &info_struct, l::_1); set_float_funcs["dps"] = l::bind(&InfoStruct::set_dps, &info_struct, l::_1);
set_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::set_dps_multiplier, &info_struct, l::_1); set_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::set_dps_multiplier, &info_struct, l::_1);
set_float_funcs["attackspeed"] = l::bind(&InfoStruct::set_attackspeed, &info_struct, l::_1); set_float_funcs["attackspeed"] = l::bind(&InfoStruct::set_attackspeed, &info_struct, l::_1);
@ -1462,7 +1464,9 @@ void Entity::CalculateBonuses(){
info->set_spell_multi_attack(0); info->set_spell_multi_attack(0);
float previous_size_mod = info->get_size_mod(); float previous_size_mod = info->get_size_mod();
info->set_size_mod(0.0f);
if(!info->get_ignore_size_mod_calc())
info->set_size_mod(0.0f);
info->set_dps(0); info->set_dps(0);
info->set_dps_multiplier(0); info->set_dps_multiplier(0);
info->set_haste(0); info->set_haste(0);
@ -1548,7 +1552,8 @@ void Entity::CalculateBonuses(){
info->add_recovery_speed(values->abilityrecoveryspeed); info->add_recovery_speed(values->abilityrecoveryspeed);
info->add_spell_reuse_speed(values->spellreusespeed); info->add_spell_reuse_speed(values->spellreusespeed);
info->add_spell_multi_attack(values->spellmultiattackchance); info->add_spell_multi_attack(values->spellmultiattackchance);
info->add_size_mod(values->size_mod); if(!info->get_ignore_size_mod_calc())
info->add_size_mod(values->size_mod);
info->add_dps(values->dps); info->add_dps(values->dps);
info->add_dps_multiplier(CalculateDPSMultiplier()); info->add_dps_multiplier(CalculateDPSMultiplier());
info->add_haste(values->attackspeed); info->add_haste(values->attackspeed);

View File

@ -210,6 +210,8 @@ struct InfoStruct{
recovery_speed_ = 0; recovery_speed_ = 0;
spell_reuse_speed_ = 0; spell_reuse_speed_ = 0;
spell_multi_attack_ = 0; spell_multi_attack_ = 0;
size_mod_ = 0.0f;
ignore_size_mod_calc_ = 0;
dps_ = 0; dps_ = 0;
dps_multiplier_ = 0; dps_multiplier_ = 0;
attackspeed_ = 0; attackspeed_ = 0;
@ -420,6 +422,10 @@ struct InfoStruct{
spell_reuse_speed_ = oldStruct->get_spell_reuse_speed(); spell_reuse_speed_ = oldStruct->get_spell_reuse_speed();
spell_multi_attack_ = oldStruct->get_spell_multi_attack(); spell_multi_attack_ = oldStruct->get_spell_multi_attack();
dps_ = oldStruct->get_dps(); dps_ = oldStruct->get_dps();
size_mod_ = oldStruct->get_size_mod();
ignore_size_mod_calc_ = oldStruct->get_ignore_size_mod_calc();
dps_multiplier_ = oldStruct->get_dps_multiplier(); dps_multiplier_ = oldStruct->get_dps_multiplier();
attackspeed_ = oldStruct->get_attackspeed(); attackspeed_ = oldStruct->get_attackspeed();
haste_ = oldStruct->get_haste(); haste_ = oldStruct->get_haste();
@ -637,6 +643,7 @@ struct InfoStruct{
float get_spell_reuse_speed() { std::lock_guard<std::mutex> lk(classMutex); return spell_reuse_speed_; } float get_spell_reuse_speed() { std::lock_guard<std::mutex> lk(classMutex); return spell_reuse_speed_; }
float get_spell_multi_attack() { std::lock_guard<std::mutex> lk(classMutex); return spell_multi_attack_; } float get_spell_multi_attack() { std::lock_guard<std::mutex> lk(classMutex); return spell_multi_attack_; }
float get_size_mod() { std::lock_guard<std::mutex> lk(classMutex); return size_mod_; } float get_size_mod() { std::lock_guard<std::mutex> lk(classMutex); return size_mod_; }
int8 get_ignore_size_mod_calc() { std::lock_guard<std::mutex> lk(classMutex); return ignore_size_mod_calc_; }
float get_dps() { std::lock_guard<std::mutex> lk(classMutex); return dps_; } float get_dps() { std::lock_guard<std::mutex> lk(classMutex); return dps_; }
float get_dps_multiplier() { std::lock_guard<std::mutex> lk(classMutex); return dps_multiplier_; } float get_dps_multiplier() { std::lock_guard<std::mutex> lk(classMutex); return dps_multiplier_; }
float get_attackspeed() { std::lock_guard<std::mutex> lk(classMutex); return attackspeed_; } float get_attackspeed() { std::lock_guard<std::mutex> lk(classMutex); return attackspeed_; }
@ -917,6 +924,7 @@ struct InfoStruct{
void set_spell_reuse_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); spell_reuse_speed_ = value; } void set_spell_reuse_speed(float value) { std::lock_guard<std::mutex> lk(classMutex); spell_reuse_speed_ = value; }
void set_spell_multi_attack(float value) { std::lock_guard<std::mutex> lk(classMutex); spell_multi_attack_ = value; } void set_spell_multi_attack(float value) { std::lock_guard<std::mutex> lk(classMutex); spell_multi_attack_ = value; }
void set_size_mod(float value) { std::lock_guard<std::mutex> lk(classMutex); size_mod_ = value; } void set_size_mod(float value) { std::lock_guard<std::mutex> lk(classMutex); size_mod_ = value; }
void set_ignore_size_mod_calc(int8 value) { std::lock_guard<std::mutex> lk(classMutex); ignore_size_mod_calc_ = value; }
void set_dps(float value) { std::lock_guard<std::mutex> lk(classMutex); dps_ = value; } void set_dps(float value) { std::lock_guard<std::mutex> lk(classMutex); dps_ = value; }
void set_dps_multiplier(float value) { std::lock_guard<std::mutex> lk(classMutex); dps_multiplier_ = value; } void set_dps_multiplier(float value) { std::lock_guard<std::mutex> lk(classMutex); dps_multiplier_ = value; }
void set_attackspeed(float value) { std::lock_guard<std::mutex> lk(classMutex); attackspeed_ = value; } void set_attackspeed(float value) { std::lock_guard<std::mutex> lk(classMutex); attackspeed_ = value; }
@ -1191,6 +1199,7 @@ private:
float spell_reuse_speed_; float spell_reuse_speed_;
float spell_multi_attack_; float spell_multi_attack_;
float size_mod_; float size_mod_;
int8 ignore_size_mod_calc_;
float dps_; float dps_;
float dps_multiplier_; float dps_multiplier_;
float attackspeed_; float attackspeed_;

View File

@ -187,7 +187,7 @@ void Guild::AddEXPCurrent(sint64 exp, bool send_packet) {
else else
strncpy(adjective, "too uber for cheerios", sizeof(adjective) - 1); strncpy(adjective, "too uber for cheerios", sizeof(adjective) - 1);
sprintf(message, "The %s guild <%s> has attained level %u", adjective, name, level); sprintf(message, "The %s guild <%s> has attained level %u", adjective, name, level);
zone_list.HandleGlobalAnnouncement(message); zone_list.TransmitGlobalAnnouncement(message);
} }
} }
save_needed = true; save_needed = true;

View File

@ -14652,4 +14652,14 @@ int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state) {
} }
return 1; return 1;
} }
int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state) {
int8 level = lua_interface->GetInt16Value(state);
lua_interface->ResetFunctionStack(state);
int32 exp_required = Player::GetNeededXPByLevel(level);
lua_interface->SetInt32Value(state, exp_required);
return 1;
}

View File

@ -680,4 +680,6 @@ int EQ2Emu_lua_GetZonePlayerAvgLevel(lua_State* state);
int EQ2Emu_lua_GetZonePlayerFirstLevel(lua_State* state); int EQ2Emu_lua_GetZonePlayerFirstLevel(lua_State* state);
int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state); int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state);
int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state);
#endif #endif

View File

@ -1,6 +1,6 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "LuaInterface.h" #include "LuaInterface.h"
#include "LuaFunctions.h" #include "LuaFunctions.h"
#include "WorldDatabase.h" #include "WorldDatabase.h"
@ -45,6 +46,7 @@ LuaInterface::LuaInterface() {
MSpells.SetName("LuaInterface::MSpells"); MSpells.SetName("LuaInterface::MSpells");
MSpawnScripts.SetName("LuaInterface::MSpawnScripts"); MSpawnScripts.SetName("LuaInterface::MSpawnScripts");
MZoneScripts.SetName("LuaInterface::MZoneScripts"); MZoneScripts.SetName("LuaInterface::MZoneScripts");
MPlayerScripts.SetName("LuaInterface::MPlayerScripts");
MQuests.SetName("LuaInterface::MQuests"); MQuests.SetName("LuaInterface::MQuests");
MLUAMain.SetName("LuaInterface::MLUAMain"); MLUAMain.SetName("LuaInterface::MLUAMain");
MItemScripts.SetName("LuaInterface::MItemScripts"); MItemScripts.SetName("LuaInterface::MItemScripts");
@ -119,6 +121,7 @@ LuaInterface::~LuaInterface() {
DestroyQuests(); DestroyQuests();
DestroyItemScripts(); DestroyItemScripts();
DestroyZoneScripts(); DestroyZoneScripts();
DestroyPlayerScripts();
DestroyRegionScripts(); DestroyRegionScripts();
DeleteUserDataPtrs(true); DeleteUserDataPtrs(true);
DeletePendingSpells(true); DeletePendingSpells(true);
@ -241,6 +244,25 @@ void LuaInterface::DestroyZoneScripts() {
MZoneScripts.releasewritelock(__FUNCTION__, __LINE__); MZoneScripts.releasewritelock(__FUNCTION__, __LINE__);
} }
void LuaInterface::DestroyPlayerScripts() {
map<string, map<lua_State*, bool> >::iterator itr;
map<lua_State*, bool>::iterator state_itr;
Mutex* mutex = 0;
MPlayerScripts.writelock(__FUNCTION__, __LINE__);
for (itr = player_scripts.begin(); itr != player_scripts.end(); itr++){
mutex = GetPlayerScriptMutex(itr->first.c_str());
mutex->writelock(__FUNCTION__, __LINE__);
for(state_itr = itr->second.begin(); state_itr != itr->second.end(); state_itr++)
lua_close(state_itr->first);
mutex->releasewritelock(__FUNCTION__, __LINE__);
safe_delete(mutex);
}
player_scripts.clear();
player_inverse_scripts.clear();
player_scripts_mutex.clear();
MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__);
}
void LuaInterface::DestroyRegionScripts() { void LuaInterface::DestroyRegionScripts() {
map<string, map<lua_State*, bool> >::iterator itr; map<string, map<lua_State*, bool> >::iterator itr;
map<lua_State*, bool>::iterator state_itr; map<lua_State*, bool>::iterator state_itr;
@ -306,6 +328,20 @@ bool LuaInterface::LoadZoneScript(const char* name) {
return ret; return ret;
} }
bool LuaInterface::LoadPlayerScript(const char* name) {
bool ret = false;
if (name) {
lua_State* state = LoadLuaFile(name);
if (state) {
MPlayerScripts.writelock(__FUNCTION__, __LINE__);
player_scripts[name][state] = false;
MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__);
ret = true;
}
}
return ret;
}
bool LuaInterface::LoadRegionScript(const char* name) { bool LuaInterface::LoadRegionScript(const char* name) {
bool ret = false; bool ret = false;
if (name) { if (name) {
@ -469,6 +505,16 @@ const char* LuaInterface::GetScriptName(lua_State* state)
} }
MZoneScripts.releasewritelock(__FUNCTION__, __LINE__); MZoneScripts.releasewritelock(__FUNCTION__, __LINE__);
MPlayerScripts.writelock(__FUNCTION__, __LINE__);
itr = player_inverse_scripts.find(state);
if (itr != player_inverse_scripts.end())
{
const char* scriptName = itr->second.c_str();
MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__);
return scriptName;
}
MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__);
MRegionScripts.writelock(__FUNCTION__, __LINE__); MRegionScripts.writelock(__FUNCTION__, __LINE__);
itr = region_inverse_scripts.find(state); itr = region_inverse_scripts.find(state);
if (itr != region_inverse_scripts.end()) if (itr != region_inverse_scripts.end())
@ -500,6 +546,10 @@ bool LuaInterface::LoadZoneScript(string name) {
return LoadZoneScript(name.c_str()); return LoadZoneScript(name.c_str());
} }
bool LuaInterface::LoadPlayerScript(string name) {
return LoadPlayerScript(name.c_str());
}
bool LuaInterface::LoadRegionScript(string name) { bool LuaInterface::LoadRegionScript(string name) {
return LoadRegionScript(name.c_str()); return LoadRegionScript(name.c_str());
} }
@ -1632,6 +1682,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state,"GetZonePlayerFirstLevel", EQ2Emu_lua_GetZonePlayerFirstLevel); lua_register(state,"GetZonePlayerFirstLevel", EQ2Emu_lua_GetZonePlayerFirstLevel);
lua_register(state,"GetSpellRequiredLevel", EQ2Emu_lua_GetSpellRequiredLevel); lua_register(state,"GetSpellRequiredLevel", EQ2Emu_lua_GetSpellRequiredLevel);
lua_register(state,"GetExpRequiredByLevel", EQ2Emu_lua_GetExpRequiredByLevel);
} }
void LuaInterface::LogError(const char* error, ...) { void LuaInterface::LogError(const char* error, ...) {
@ -2172,6 +2223,17 @@ Mutex* LuaInterface::GetZoneScriptMutex(const char* name) {
return mutex; return mutex;
} }
Mutex* LuaInterface::GetPlayerScriptMutex(const char* name) {
Mutex* mutex = 0;
if(player_scripts_mutex.count(name) > 0)
mutex = player_scripts_mutex[name];
if(!mutex){
mutex = new Mutex();
player_scripts_mutex[name] = mutex;
}
return mutex;
}
Mutex* LuaInterface::GetRegionScriptMutex(const char* name) { Mutex* LuaInterface::GetRegionScriptMutex(const char* name) {
Mutex* mutex = 0; Mutex* mutex = 0;
if(region_scripts_mutex.count(name) > 0) if(region_scripts_mutex.count(name) > 0)
@ -2216,6 +2278,14 @@ void LuaInterface::UseZoneScript(const char* name, lua_State* state, bool val) {
MZoneScripts.releasewritelock(__FUNCTION__, __LINE__); MZoneScripts.releasewritelock(__FUNCTION__, __LINE__);
} }
void LuaInterface::UsePlayerScript(const char* name, lua_State* state, bool val) {
MPlayerScripts.writelock(__FUNCTION__, __LINE__);
player_scripts[name][state] = val;
player_inverse_scripts[state] = name;
MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__);
}
void LuaInterface::UseRegionScript(const char* name, lua_State* state, bool val) { void LuaInterface::UseRegionScript(const char* name, lua_State* state, bool val) {
MRegionScripts.writelock(__FUNCTION__, __LINE__); MRegionScripts.writelock(__FUNCTION__, __LINE__);
@ -2328,6 +2398,40 @@ lua_State* LuaInterface::GetZoneScript(const char* name, bool create_new, bool u
return ret; return ret;
} }
lua_State* LuaInterface::GetPlayerScript(const char* name, bool create_new, bool use) {
map<string, map<lua_State*, bool> >::iterator itr;
map<lua_State*, bool>::iterator zone_script_itr;
lua_State* ret = 0;
Mutex* mutex = 0;
itr = player_scripts.find(name);
if(itr != player_scripts.end()) {
mutex = GetPlayerScriptMutex(name);
mutex->readlock(__FUNCTION__, __LINE__);
for(zone_script_itr = itr->second.begin(); zone_script_itr != itr->second.end(); zone_script_itr++){
if(!zone_script_itr->second){ //not in use
ret = zone_script_itr->first;
if (use)
{
zone_script_itr->second = true;
break; // don't keep iterating, we already have our result
}
}
}
mutex->releasereadlock(__FUNCTION__, __LINE__);
}
if(!ret && create_new){
if(name && LoadPlayerScript(name))
ret = GetPlayerScript(name, create_new, use);
else{
LogError("Error LUA Zone Script '%s'", name);
return 0;
}
}
return ret;
}
lua_State* LuaInterface::GetRegionScript(const char* name, bool create_new, bool use) { lua_State* LuaInterface::GetRegionScript(const char* name, bool create_new, bool use) {
map<string, map<lua_State*, bool> >::iterator itr; map<string, map<lua_State*, bool> >::iterator itr;
map<lua_State*, bool>::iterator region_script_itr; map<lua_State*, bool>::iterator region_script_itr;
@ -2699,6 +2803,57 @@ bool LuaInterface::RunZoneScriptWithReturn(string script_name, const char* funct
return false; return false;
} }
bool LuaInterface::RunPlayerScriptWithReturn(const string script_name, const char* function_name, const std::vector<LuaArg>& args, sint32* returnValue) {
lua_State* state = GetPlayerScript(script_name.c_str(), true, true);
if (state) {
Mutex* mutex = GetPlayerScriptMutex(script_name.c_str());
if (mutex)
mutex->readlock(__FUNCTION__, __LINE__);
else {
LogError("Error getting lock for '%s'", script_name.c_str());
UsePlayerScript(script_name.c_str(), state, false);
return false;
}
lua_getglobal(state, function_name);
if (!lua_isfunction(state, lua_gettop(state))) {
lua_pop(state, 1);
mutex->releasereadlock(__FUNCTION__);
UsePlayerScript(script_name.c_str(), state, false);
return false;
}
int8 num_params = 0;
for (const LuaArg& arg : args) {
switch (arg.type) {
case LuaArgType::SINT64: SetSInt64Value(state, arg.si); num_params++; break;
case LuaArgType::SINT: SetSInt32Value(state, arg.low_si); num_params++; break;
case LuaArgType::INT64: SetInt64Value(state, arg.i); num_params++; break;
case LuaArgType::INT: SetInt32Value(state, arg.low_i); num_params++; break;
case LuaArgType::FLOAT: SetFloatValue(state, arg.f); num_params++; break;
case LuaArgType::STRING: SetStringValue(state, arg.s.c_str()); num_params++; break;
case LuaArgType::BOOL: SetBooleanValue(state, arg.b); num_params++; break;
case LuaArgType::SPAWN: SetSpawnValue(state, arg.spawn); num_params++; break;
case LuaArgType::ZONE: SetZoneValue(state, arg.zone); num_params++; break;
case LuaArgType::SKILL: SetSkillValue(state, arg.skill); num_params++; break;
case LuaArgType::ITEM: SetItemValue(state, arg.item); num_params++; break;
case LuaArgType::QUEST: SetQuestValue(state, arg.quest); num_params++; break;
case LuaArgType::SPELL: SetSpellValue(state, arg.spell); num_params++; break;
}
}
if (!CallScriptSInt32(state, num_params, returnValue)) {
if (mutex)
mutex->releasereadlock(__FUNCTION__, __LINE__);
UsePlayerScript(script_name.c_str(), state, false);
return false;
}
if (mutex)
mutex->releasereadlock(__FUNCTION__, __LINE__);
UsePlayerScript(script_name.c_str(), state, false);
return true;
}
else
return false;
}
bool LuaInterface::RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, sint32 int32_arg1, int32* returnValue) { bool LuaInterface::RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, sint32 int32_arg1, int32* returnValue) {
if (!zone) if (!zone)

View File

@ -108,6 +108,45 @@ struct LuaSpell{
int16 initial_caster_level; int16 initial_caster_level;
}; };
enum class LuaArgType { SINT64, INT64, SINT, INT, FLOAT, STRING, BOOL, SPAWN, ZONE, SKILL, ITEM, QUEST, SPELL /* etc */ };
struct LuaArg {
LuaArgType type;
union {
sint64 si;
sint64 i;
int32 low_i;
sint32 low_si;
float f;
bool b;
};
std::string s;
Spawn* spawn = nullptr;
ZoneServer* zone = nullptr;
Skill* skill = nullptr;
Item* item = nullptr;
Quest* quest = nullptr;
LuaSpell* spell = nullptr;
LuaArg(sint64 val) : type(LuaArgType::SINT64), si(val) {}
LuaArg(sint32 val) : type(LuaArgType::SINT), low_si(val) {}
LuaArg(sint16 val) : type(LuaArgType::SINT), low_si(val) {}
LuaArg(sint8 val) : type(LuaArgType::SINT), low_si(val) {}
LuaArg(int64 val) : type(LuaArgType::INT64), i(val) {}
LuaArg(int32 val) : type(LuaArgType::INT), low_i(val) {}
LuaArg(int16 val) : type(LuaArgType::INT), low_i(val) {}
LuaArg(int8 val) : type(LuaArgType::INT), low_i(val) {}
LuaArg(float val) : type(LuaArgType::FLOAT), f(val) {}
LuaArg(bool val) : type(LuaArgType::BOOL), b(val) {}
LuaArg(const std::string& val) : type(LuaArgType::STRING), s(val) {}
LuaArg(Spawn* val) : type(LuaArgType::SPAWN), spawn(val) {}
LuaArg(ZoneServer* val) : type(LuaArgType::ZONE), zone(val) {}
LuaArg(Skill* val) : type(LuaArgType::SKILL), skill(val) {}
LuaArg(Item* val) : type(LuaArgType::ITEM), item(val) {}
LuaArg(Quest* val) : type(LuaArgType::QUEST), quest(val) {}
LuaArg(LuaSpell* val) : type(LuaArgType::SPELL), spell(val) {}
};
class LUAUserData{ class LUAUserData{
public: public:
LUAUserData(); LUAUserData();
@ -192,6 +231,8 @@ public:
bool LoadSpawnScript(const char* name); bool LoadSpawnScript(const char* name);
bool LoadZoneScript(string name); bool LoadZoneScript(string name);
bool LoadZoneScript(const char* name); bool LoadZoneScript(const char* name);
bool LoadPlayerScript(string name);
bool LoadPlayerScript(const char* name);
bool LoadRegionScript(string name); bool LoadRegionScript(string name);
bool LoadRegionScript(const char* name); bool LoadRegionScript(const char* name);
LuaSpell* LoadSpellScript(string name); LuaSpell* LoadSpellScript(string name);
@ -242,10 +283,12 @@ public:
void UseItemScript(const char* name, lua_State* state, bool val); void UseItemScript(const char* name, lua_State* state, bool val);
void UseSpawnScript(const char* name, lua_State* state, bool val); void UseSpawnScript(const char* name, lua_State* state, bool val);
void UseZoneScript(const char* name, lua_State* state, bool val); void UseZoneScript(const char* name, lua_State* state, bool val);
void UsePlayerScript(const char* name, lua_State* state, bool val);
void UseRegionScript(const char* name, lua_State* state, bool val); void UseRegionScript(const char* name, lua_State* state, bool val);
lua_State* GetItemScript(const char* name, bool create_new = true, bool use = false); lua_State* GetItemScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetSpawnScript(const char* name, bool create_new = true, bool use = false); lua_State* GetSpawnScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetZoneScript(const char* name, bool create_new = true, bool use = false); lua_State* GetZoneScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetPlayerScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetRegionScript(const char* name, bool create_new = true, bool use = false); lua_State* GetRegionScript(const char* name, bool create_new = true, bool use = false);
LuaSpell* GetSpellScript(const char* name, bool create_new = true, bool use = true); LuaSpell* GetSpellScript(const char* name, bool create_new = true, bool use = true);
LuaSpell* CreateSpellScript(const char* name, lua_State* existState); LuaSpell* CreateSpellScript(const char* name, lua_State* existState);
@ -263,6 +306,7 @@ public:
bool CallSpawnScript(lua_State* state, int8 num_parameters); bool CallSpawnScript(lua_State* state, int8 num_parameters);
bool RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 int32_arg1 = 0, const char* str_arg1 = 0, Spawn* spawn_arg1 = 0, int32 int32_arg2 = 0, const char* str_arg2 = 0, Spawn* spawn_arg2 = 0); bool RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 int32_arg1 = 0, const char* str_arg1 = 0, Spawn* spawn_arg1 = 0, int32 int32_arg2 = 0, const char* str_arg2 = 0, Spawn* spawn_arg2 = 0);
bool RunZoneScriptWithReturn(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, int32 int32_arg1, int32 int32_arg2, int32 int32_arg3, int32* returnValue = 0); bool RunZoneScriptWithReturn(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, int32 int32_arg1, int32 int32_arg2, int32 int32_arg3, int32* returnValue = 0);
bool RunPlayerScriptWithReturn(const string script_name, const char* function_name, const std::vector<LuaArg>& args, sint32* returnValue = 0);
bool CallScriptInt32(lua_State* state, int8 num_parameters, int32* returnValue = 0); bool CallScriptInt32(lua_State* state, int8 num_parameters, int32* returnValue = 0);
bool CallScriptSInt32(lua_State* state, int8 num_parameters, sint32* returnValue = 0); bool CallScriptSInt32(lua_State* state, int8 num_parameters, sint32* returnValue = 0);
bool RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, sint32 int32_arg1 = 0, int32* returnValue = 0); bool RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, sint32 int32_arg1 = 0, int32* returnValue = 0);
@ -273,6 +317,7 @@ public:
void DestroyItemScripts(); void DestroyItemScripts();
void DestroyQuests(bool reload = false); void DestroyQuests(bool reload = false);
void DestroyZoneScripts(); void DestroyZoneScripts();
void DestroyPlayerScripts();
void DestroyRegionScripts(); void DestroyRegionScripts();
void SimpleLogError(const char* error); void SimpleLogError(const char* error);
void LogError(const char* error, ...); void LogError(const char* error, ...);
@ -290,6 +335,7 @@ public:
Mutex* GetSpawnScriptMutex(const char* name); Mutex* GetSpawnScriptMutex(const char* name);
Mutex* GetItemScriptMutex(const char* name); Mutex* GetItemScriptMutex(const char* name);
Mutex* GetZoneScriptMutex(const char* name); Mutex* GetZoneScriptMutex(const char* name);
Mutex* GetPlayerScriptMutex(const char* name);
Mutex* GetRegionScriptMutex(const char* name); Mutex* GetRegionScriptMutex(const char* name);
Mutex* GetSpellScriptMutex(const char* name); Mutex* GetSpellScriptMutex(const char* name);
Mutex* GetQuestMutex(Quest* quest); Mutex* GetQuestMutex(Quest* quest);
@ -330,6 +376,7 @@ private:
map<string, map<lua_State*, bool> > item_scripts; map<string, map<lua_State*, bool> > item_scripts;
map<string, map<lua_State*, bool> > spawn_scripts; map<string, map<lua_State*, bool> > spawn_scripts;
map<string, map<lua_State*, bool> > zone_scripts; map<string, map<lua_State*, bool> > zone_scripts;
map<string, map<lua_State*, bool> > player_scripts;
map<string, map<lua_State*, bool> > region_scripts; map<string, map<lua_State*, bool> > region_scripts;
map<string, map<lua_State*, LuaSpell*> > spell_scripts; map<string, map<lua_State*, LuaSpell*> > spell_scripts;
@ -339,11 +386,13 @@ private:
map<lua_State*, string> item_inverse_scripts; map<lua_State*, string> item_inverse_scripts;
map<lua_State*, string> spawn_inverse_scripts; map<lua_State*, string> spawn_inverse_scripts;
map<lua_State*, string> zone_inverse_scripts; map<lua_State*, string> zone_inverse_scripts;
map<lua_State*, string> player_inverse_scripts;
map<lua_State*, string> region_inverse_scripts; map<lua_State*, string> region_inverse_scripts;
map<string, Mutex*> item_scripts_mutex; map<string, Mutex*> item_scripts_mutex;
map<string, Mutex*> spawn_scripts_mutex; map<string, Mutex*> spawn_scripts_mutex;
map<string, Mutex*> zone_scripts_mutex; map<string, Mutex*> zone_scripts_mutex;
map<string, Mutex*> player_scripts_mutex;
map<int32, Mutex*> quests_mutex; map<int32, Mutex*> quests_mutex;
map<string, Mutex*> region_scripts_mutex; map<string, Mutex*> region_scripts_mutex;
map<string, Mutex*> spell_scripts_mutex; map<string, Mutex*> spell_scripts_mutex;
@ -353,6 +402,7 @@ private:
Mutex MSpawnScripts; Mutex MSpawnScripts;
Mutex MItemScripts; Mutex MItemScripts;
Mutex MZoneScripts; Mutex MZoneScripts;
Mutex MPlayerScripts;
Mutex MQuests; Mutex MQuests;
Mutex MLUAMain; Mutex MLUAMain;
Mutex MSpellDelete; Mutex MSpellDelete;

View File

@ -1,6 +1,6 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "Player.h" #include "Player.h"
#include "../common/MiscFunctions.h" #include "../common/MiscFunctions.h"
#include "World.h" #include "World.h"
@ -46,6 +47,7 @@ extern MasterItemList master_item_list;
extern RuleManager rule_manager; extern RuleManager rule_manager;
extern MasterTitlesList master_titles_list; extern MasterTitlesList master_titles_list;
extern MasterLanguagesList master_languages_list; extern MasterLanguagesList master_languages_list;
std::map<int8, int32> Player::m_levelXPReq;
Player::Player(){ Player::Player(){
tutorial_step = 0; tutorial_step = 0;
@ -998,8 +1000,8 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
packet->setDataByName("decrease_falling_dmg", 169); packet->setDataByName("decrease_falling_dmg", 169);
if (version <= 561) { if (version <= 561) {
packet->setDataByName("exp_yellow", info_struct->get_xp_yellow() / 10); packet->setDataByName("exp_yellow", info_struct->get_xp_yellow() / 10);
packet->setDataByName("exp_blue", info_struct->get_xp_blue()/100); packet->setDataByName("exp_blue", ((int16)info_struct->get_xp_yellow() % 100) + (info_struct->get_xp_blue() / 100));
} }
else { else {
packet->setDataByName("exp_yellow", info_struct->get_xp_yellow()); packet->setDataByName("exp_yellow", info_struct->get_xp_yellow());
@ -2136,7 +2138,7 @@ bool Player::AddItemToBank(Item* item) {
} }
EQ2Packet* Player::SendInventoryUpdate(int16 version) { EQ2Packet* Player::SendInventoryUpdate(int16 version) {
// assure any inventory updates are reflected in sell window // assure any inventory updates are reflected in sell window
if(GetClient() && GetClient()->GetMerchantTransaction()) if(GetClient() && GetClient()->GetMerchantTransactionID())
GetClient()->SendSellMerchantList(); GetClient()->SendSellMerchantList();
return item_list.serialize(this, version); return item_list.serialize(this, version);
@ -4419,11 +4421,20 @@ void Player::SetNeededXP(){
//GetInfoStruct()->xp_needed = GetLevel() * 100; //GetInfoStruct()->xp_needed = GetLevel() * 100;
// Get xp needed to get to the next level // Get xp needed to get to the next level
int16 level = GetLevel() + 1; int16 level = GetLevel() + 1;
// If next level is beyond what we have in the map multiply the last value we have by how many levels we are over plus one SetNeededXP(GetNeededXPByLevel(level));
if (level > 95) }
SetNeededXP(database.GetMysqlExpCurve(95)* ((level - 95) + 1));
int32 Player::GetNeededXPByLevel(int8 level) {
int32 exp_required = 0;
if (!Player::m_levelXPReq.count(level) && level > 95 && Player::m_levelXPReq.count(95)) {
exp_required = (Player::m_levelXPReq[95] * ((level - 95) + 1));
}
else if(Player::m_levelXPReq.count(level))
exp_required = Player::m_levelXPReq[level];
else else
SetNeededXP(database.GetMysqlExpCurve(level)); exp_required = 0;
return exp_required;
} }
void Player::SetXP(int32 val){ void Player::SetXP(int32 val){
@ -4519,10 +4530,24 @@ bool Player::AddXP(int32 xp_amount){
SetCharSheetChanged(true); SetCharSheetChanged(true);
return false; return false;
} }
int32 prev_xp_amount = xp_amount;
xp_amount -= GetNeededXP() - GetXP(); xp_amount -= GetNeededXP() - GetXP();
SetLevel(GetLevel() + 1); if(GetClient()->ChangeLevel(GetLevel(), GetLevel()+1, prev_xp_amount))
SetLevel(GetLevel() + 1);
else {
SetXP(GetXP() + prev_xp_amount);
SetCharSheetChanged(true);
return false;
}
} }
// set the actual end xp_amount result
SetXP(GetXP() + xp_amount); SetXP(GetXP() + xp_amount);
if(GetClient()) {
GetClient()->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp_amount);
}
GetPlayerInfo()->CalculateXPPercentages(); GetPlayerInfo()->CalculateXPPercentages();
current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100; current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100;
if(miniding_min_percent > 0.0f && current_xp_percent >= miniding_min_percent){ if(miniding_min_percent > 0.0f && current_xp_percent >= miniding_min_percent){
@ -4532,15 +4557,8 @@ bool Player::AddXP(int32 xp_amount){
SetPower(GetTotalPower()); SetPower(GetTotalPower());
GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect
} }
if(GetClient()) {
GetClient()->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp_amount);
if (prev_level != GetLevel())
GetClient()->ChangeLevel(prev_level, GetLevel());
}
SetCharSheetChanged(true); SetCharSheetChanged(true);
return true; return true;
} }
@ -4550,7 +4568,16 @@ bool Player::AddTSXP(int32 xp_amount){
MStats.unlock(); MStats.unlock();
float current_xp_percent = ((float)GetTSXP()/(float)GetNeededTSXP())*100; float current_xp_percent = ((float)GetTSXP()/(float)GetNeededTSXP())*100;
float miniding_min_percent = ((int)(current_xp_percent/10)+1)*10;
int32 mini_ding_pct = rule_manager.GetGlobalRule(R_Player, MiniDingPercentage)->GetInt32();
float miniding_min_percent = 0.0f;
if(mini_ding_pct < 10 || mini_ding_pct > 50) {
mini_ding_pct = 0;
}
else {
miniding_min_percent = ((int)(current_xp_percent/mini_ding_pct)+1)*mini_ding_pct;
}
while((xp_amount + GetTSXP()) >= GetNeededTSXP()){ while((xp_amount + GetTSXP()) >= GetNeededTSXP()){
if (!CheckLevelStatus(GetTSLevel() + 1)) { if (!CheckLevelStatus(GetTSLevel() + 1)) {
if(GetClient()) { if(GetClient()) {
@ -4558,10 +4585,18 @@ bool Player::AddTSXP(int32 xp_amount){
} }
return false; return false;
} }
int32 prev_xp_amount = xp_amount;
xp_amount -= GetNeededTSXP() - GetTSXP(); xp_amount -= GetNeededTSXP() - GetTSXP();
SetTSLevel(GetTSLevel() + 1); if(GetClient()->ChangeTSLevel(GetLevel(), GetLevel()+1, prev_xp_amount)) {
SetTSXP(0); SetTSLevel(GetTSLevel() + 1);
SetNeededTSXP(); SetTSXP(0);
SetNeededTSXP();
}
else {
SetTSXP(GetTSXP() + prev_xp_amount);
SetCharSheetChanged(true);
return false;
}
} }
SetTSXP(GetTSXP() + xp_amount); SetTSXP(GetTSXP() + xp_amount);
GetPlayerInfo()->CalculateXPPercentages(); GetPlayerInfo()->CalculateXPPercentages();
@ -4577,6 +4612,8 @@ bool Player::AddTSXP(int32 xp_amount){
GetInfoStruct()->set_tradeskill_class2(1); GetInfoStruct()->set_tradeskill_class2(1);
GetInfoStruct()->set_tradeskill_class3(1); GetInfoStruct()->set_tradeskill_class3(1);
} }
SetCharSheetChanged(true);
return true; return true;
} }
@ -6423,7 +6460,8 @@ int32 CharacterInstances::GetInstanceCount() {
return instanceList.size(); return instanceList.size();
} }
void Player::SetPlayerAdventureClass(int8 new_class){ void Player::SetPlayerAdventureClass(int8 new_class, bool set_by_gm_command ){
int8 old_class = GetAdventureClass();
SetAdventureClass(new_class); SetAdventureClass(new_class);
GetInfoStruct()->set_class1(classes.GetBaseClass(new_class)); GetInfoStruct()->set_class1(classes.GetBaseClass(new_class));
GetInfoStruct()->set_class2(classes.GetSecondaryBaseClass(new_class)); GetInfoStruct()->set_class2(classes.GetSecondaryBaseClass(new_class));
@ -6433,6 +6471,23 @@ void Player::SetPlayerAdventureClass(int8 new_class){
GetZone()->TriggerCharSheetTimer(); GetZone()->TriggerCharSheetTimer();
if(GetClient()) if(GetClient())
GetClient()->UpdateTimeStampFlag ( CLASS_UPDATE_FLAG ); GetClient()->UpdateTimeStampFlag ( CLASS_UPDATE_FLAG );
const char* playerScript = world.GetPlayerScript(0); // 0 = global script
const char* playerZoneScript = world.GetPlayerScript(GetZoneID()); // zone script
if(playerScript || playerZoneScript) {
std::vector<LuaArg> args = {
LuaArg(GetZone()),
LuaArg(this),
LuaArg(old_class),
LuaArg(new_class)
};
if(playerScript) {
lua_interface->RunPlayerScriptWithReturn(playerScript, "on_class_change", args);
}
if(playerZoneScript) {
lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_class_change", args);
}
}
} }
void Player::AddSkillBonus(int32 spell_id, int32 skill_id, float value) { void Player::AddSkillBonus(int32 spell_id, int32 skill_id, float value) {

View File

@ -1,6 +1,6 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __EQ2_PLAYER__ #ifndef __EQ2_PLAYER__
#define __EQ2_PLAYER__ #define __EQ2_PLAYER__
@ -608,6 +609,7 @@ public:
bool TradeskillXPEnabled(); bool TradeskillXPEnabled();
void SetNeededXP(int32 val); void SetNeededXP(int32 val);
void SetNeededXP(); void SetNeededXP();
static int32 GetNeededXPByLevel(int8 level);
void SetXP(int32 val); void SetXP(int32 val);
void SetNeededTSXP(int32 val); void SetNeededTSXP(int32 val);
void SetNeededTSXP(); void SetNeededTSXP();
@ -789,7 +791,7 @@ public:
bool GetIsTracking() const { return is_tracking; } bool GetIsTracking() const { return is_tracking; }
void SetBiography(string new_biography) { biography = new_biography; } void SetBiography(string new_biography) { biography = new_biography; }
string GetBiography() const { return biography; } string GetBiography() const { return biography; }
void SetPlayerAdventureClass(int8 new_class); void SetPlayerAdventureClass(int8 new_class, bool set_by_gm_command = false);
void SetGuild(Guild* new_guild) { guild = new_guild; } void SetGuild(Guild* new_guild) { guild = new_guild; }
Guild* GetGuild() { return guild; } Guild* GetGuild() { return guild; }
void AddSkillBonus(int32 spell_id, int32 skill_id, float value); void AddSkillBonus(int32 spell_id, int32 skill_id, float value);
@ -1112,8 +1114,8 @@ public:
mutable std::shared_mutex trait_mutex; mutable std::shared_mutex trait_mutex;
std::atomic<bool> need_trait_update; std::atomic<bool> need_trait_update;
void InitXPTable(); static void InitXPTable();
map<int8, int32> m_levelXPReq; static map<int8, int32> m_levelXPReq;
mutable std::shared_mutex spell_packet_update_mutex; mutable std::shared_mutex spell_packet_update_mutex;
mutable std::shared_mutex raid_update_mutex; mutable std::shared_mutex raid_update_mutex;
@ -1214,8 +1216,6 @@ private:
void AddSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0); void AddSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0);
void RemoveSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0); void RemoveSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0);
void SetSpellEntryRecast(SpellBookEntry* spell, bool modify_recast, int16 recast); void SetSpellEntryRecast(SpellBookEntry* spell, bool modify_recast, int16 recast);
// void InitXPTable();
// map<int8, int32> m_levelXPReq;
//The following variables are for serializing spawn packets //The following variables are for serializing spawn packets
PacketStruct* spawn_pos_struct; PacketStruct* spawn_pos_struct;

View File

@ -2197,18 +2197,25 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b
if (size == 0) if (size == 0)
size = 32; size = 32;
float scaled = 0.0f;
sint16 new_size = size;
if(IsEntity() && !IsPlayer()) { // doesn't work correctly for Player, only non-Player (NPC/Object/etc)
scaled = static_cast<float>(size) + (((Entity*)this)->GetInfoStruct()->get_size_mod() * size);
// Round and cast to sint16
new_size = static_cast<sint16>(std::round(scaled));
// Enforce minimum size of 1
new_size = std::max<sint16>(new_size, 1);
}
if (version <= 910) { if (version <= 910) {
packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32); packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32);
packet->setDataByName("pos_size", size > 0 ? size : 32); packet->setDataByName("pos_size", new_size > 0 ? new_size : 32);
} }
else { else {
packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32); packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32);
packet->setDataByName("pos_size", new_size > 0 ? (((float)new_size) / 32.0f) : 1.0f); // float not an integer
if(!IsPlayer())
packet->setDataByName("pos_size", size > 0 ? (((float)size) / 32.0f) : 1.0f); // float not an integer
else
packet->setDataByName("pos_size", 1.0f);
// please do not remove! This makes it so NPCs for example do not resize large/small when you are in combat with them! // please do not remove! This makes it so NPCs for example do not resize large/small when you are in combat with them!
packet->setDataByName("pos_size_ratio", 1.0f); packet->setDataByName("pos_size_ratio", 1.0f);
} }
@ -2487,11 +2494,6 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
packet->setDataByName("npc", 1); packet->setDataByName("npc", 1);
if (GetMerchantID() > 0) if (GetMerchantID() > 0)
packet->setDataByName("merchant", 1); packet->setDataByName("merchant", 1);
if(IsEntity() && ((Entity*)this)->GetInfoStruct()->get_size_mod() != 0.0f) {
float mod = ((Entity*)this)->GetInfoStruct()->get_size_mod(); // e.g., -0.25 or +0.25
//packet->setDataByName("temporary_scale", mod); //TODO: Understand what these mod values should be, anything we send makes the client shrink to nothing
}
packet->setDataByName("effective_level", IsEntity() && ((Entity*)this)->GetInfoStruct()->get_effective_level() != 0 ? (int8)((Entity*)this)->GetInfoStruct()->get_effective_level() : (int8)GetLevel()); packet->setDataByName("effective_level", IsEntity() && ((Entity*)this)->GetInfoStruct()->get_effective_level() != 0 ? (int8)((Entity*)this)->GetInfoStruct()->get_effective_level() : (int8)GetLevel());
packet->setDataByName("level", (int8)GetLevel()); packet->setDataByName("level", (int8)GetLevel());
@ -3360,8 +3362,9 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){
} }
if (!movementCase && IsRunning() && !IsPauseMovementTimerActive()) { if (!movementCase && IsRunning() && !IsPauseMovementTimerActive()) {
CalculateRunningLocation(); // 6/7/2025: Fixes glitchy movement when spawns are close together and keep warping forward and back to the original position
//last_movement_update = Timer::GetCurrentTime2(); CalculateRunningLocation(true);
last_movement_update = Timer::GetCurrentTime2();
} }
else if(movementCase) else if(movementCase)
{ {

View File

@ -22,6 +22,7 @@
#include "Tradeskills/Tradeskills.h" #include "Tradeskills/Tradeskills.h"
#include "ClientPacketFunctions.h" #include "ClientPacketFunctions.h"
#include "Rules/Rules.h" #include "Rules/Rules.h"
#include "races.h"
extern MasterSpellList master_spell_list; extern MasterSpellList master_spell_list;
extern MasterSkillList master_skill_list; extern MasterSkillList master_skill_list;
@ -30,6 +31,7 @@ extern LuaInterface* lua_interface;
extern Commands commands; extern Commands commands;
extern World world; extern World world;
extern RuleManager rule_manager; extern RuleManager rule_manager;
extern Races races;
SpellProcess::SpellProcess(){ SpellProcess::SpellProcess(){
last_checked_time = 0; last_checked_time = 0;
@ -510,32 +512,18 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removi
if(target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0){ if(target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0){
Client* client = ((Player*)target)->GetClient(); Client* client = ((Player*)target)->GetClient();
if(client){ if(client){
bool send_to_sender = true;
string fade_message = spell->spell->GetSpellData()->fade_message; string fade_message = spell->spell->GetSpellData()->fade_message;
if(fade_message.find("%t") != string::npos) ReplaceEffectTokens(fade_message, spell->caster, target);
fade_message.replace(fade_message.find("%t"), 2, target->GetName());
client->Message(CHANNEL_SPELLS_OTHER, fade_message.c_str()); client->Message(CHANNEL_SPELLS_OTHER, fade_message.c_str());
} }
} }
if (target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0) { if (target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message_others.length() > 0) {
Client* client = ((Player*)target)->GetClient(); Client* client = ((Player*)target)->GetClient();
if (client) { if (client) {
bool send_to_sender = true;
string fade_message_others = spell->spell->GetSpellData()->fade_message_others; string fade_message_others = spell->spell->GetSpellData()->fade_message_others;
if (fade_message_others.find("%t") != string::npos) ReplaceEffectTokens(fade_message_others, spell->caster, target);
fade_message_others.replace(fade_message_others.find("%t"), 2, target->GetName());
if (fade_message_others.find("%c") != string::npos)
fade_message_others.replace(fade_message_others.find("%c"), 2, spell->caster->GetName());
if (fade_message_others.find("%T") != string::npos) {
fade_message_others.replace(fade_message_others.find("%T"), 2, target->GetName());
send_to_sender = false;
}
if (fade_message_others.find("%C") != string::npos) {
fade_message_others.replace(fade_message_others.find("%C"), 2, spell->caster->GetName());
send_to_sender = false;
}
if(spell->caster->GetZone()) { if(spell->caster->GetZone()) {
spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, fade_message_others.c_str(), target, 50, send_to_sender); spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, fade_message_others.c_str(), target, 50);
} }
} }
} }
@ -1803,29 +1791,16 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
if(spell->spell->GetSpellData()->success_message.length() > 0){ if(spell->spell->GetSpellData()->success_message.length() > 0){
if(client){ if(client){
string success_message = spell->spell->GetSpellData()->success_message; string success_message = spell->spell->GetSpellData()->success_message;
if(success_message.find("%t") != string::npos) ReplaceEffectTokens(success_message, spell->caster, target);
success_message.replace(success_message.find("%t"), 2, spell->caster->GetName());
client->Message(CHANNEL_SPELLS, success_message.c_str()); client->Message(CHANNEL_SPELLS, success_message.c_str());
} }
} }
if(spell->spell->GetSpellData()->effect_message.length() > 0){ if(spell->spell->GetSpellData()->effect_message.length() > 0){
string effect_message = spell->spell->GetSpellData()->effect_message; string effect_message = spell->spell->GetSpellData()->effect_message;
bool send_to_sender = true; ReplaceEffectTokens(effect_message, spell->caster, target);
if(effect_message.find("%t") != string::npos)
effect_message.replace(effect_message.find("%t"), 2, target->GetName());
if (effect_message.find("%c") != string::npos)
effect_message.replace(effect_message.find("%c"), 2, spell->caster->GetName());
if (effect_message.find("%T") != string::npos) {
effect_message.replace(effect_message.find("%T"), 2, target->GetName());
send_to_sender = false;
}
if (effect_message.find("%C") != string::npos) {
effect_message.replace(effect_message.find("%C"), 2, spell->caster->GetName());
send_to_sender = false;
}
if(spell->caster && spell->caster->GetZone()) { if(spell->caster && spell->caster->GetZone()) {
spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50, send_to_sender); spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50);
} }
} }
if(target->GetZone()) { if(target->GetZone()) {
@ -3099,4 +3074,193 @@ bool SpellProcess::AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_sp
lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
return ret; return ret;
}
void SpellProcess::ReplaceEffectTokens(std::string& message, Spawn* in_caster, Spawn* in_target) {
std::string caster = in_caster ? std::string(in_caster->GetName()) : "Unknown";
std::string target = in_target ? std::string(in_target->GetName()) : "Unknown";
size_t pos;
if(message.find("%", 0) == std::string::npos)
return;
// Replace all %C and suppress send
pos = 0;
while ((pos = message.find("%C", pos)) != std::string::npos) {
message.replace(pos, 2, caster);
pos += caster.length();
}
// Replace all %c (no suppression)
pos = 0;
while ((pos = message.find("%c", pos)) != std::string::npos) {
message.replace(pos, 2, caster);
pos += caster.length();
}
// Replace all %T and suppress send
pos = 0;
while ((pos = message.find("%T", pos)) != std::string::npos) {
message.replace(pos, 2, target);
pos += target.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%t", pos)) != std::string::npos) {
message.replace(pos, 2, target);
pos += target.length();
}
std::string target_assist = in_target && in_target->GetTarget() ? std::string(in_target->GetTarget()->GetName()) : "Unknown";
if(target_assist == "Unknown" && target != "Unknown")
target_assist = target;
// Replace all %T and suppress send
pos = 0;
while ((pos = message.find("%A", pos)) != std::string::npos) {
message.replace(pos, 2, target_assist);
pos += target_assist.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%a", pos)) != std::string::npos) {
message.replace(pos, 2, target_assist);
pos += target_assist.length();
}
std::string gender_name = "Unknown";
std::string gender_oname = "It";
std::string gender_sname = "It";
std::string gender_pname = "Its";
if(in_target) {
if(in_target->GetGender() == 1) {
gender_name = "Male";
gender_oname = "Him";
gender_sname = "He";
gender_pname = "His";
}
else if(in_target->GetGender() == 0) {
gender_name = "Female";
gender_oname = "Her";
gender_sname = "She";
gender_pname = "Her";
}
}
// Replace all %T and suppress send
pos = 0;
while ((pos = message.find("%G", pos)) != std::string::npos) {
message.replace(pos, 2, gender_name);
pos += gender_name.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%g", pos)) != std::string::npos) {
message.replace(pos, 2, gender_name);
pos += gender_name.length();
}
pos = 0;
while ((pos = message.find("%O", pos)) != std::string::npos) {
message.replace(pos, 2, gender_oname);
pos += gender_oname.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%o", pos)) != std::string::npos) {
message.replace(pos, 2, gender_oname);
pos += gender_oname.length();
}
pos = 0;
while ((pos = message.find("%S", pos)) != std::string::npos) {
message.replace(pos, 2, gender_sname);
pos += gender_sname.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%s", pos)) != std::string::npos) {
message.replace(pos, 2, gender_sname);
pos += gender_sname.length();
}
pos = 0;
while ((pos = message.find("%P", pos)) != std::string::npos) {
message.replace(pos, 2, gender_pname);
pos += gender_pname.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%p", pos)) != std::string::npos) {
message.replace(pos, 2, gender_pname);
pos += gender_pname.length();
}
std::string raceName = "Unknown";
if(in_target) {
const char* raceCaseName = races.GetRaceNameCase(in_target->GetRace());
if(raceCaseName)
raceName = std::string(raceCaseName);
}
std::string tell_name = "Unknown";
if(in_caster && in_caster->IsPlayer()) {
if(((Player*)in_caster)->GetClient() && ((Player*)in_caster)->GetClient()->GetLastTellName().size() > 0)
tell_name = ((Player*)in_caster)->GetClient()->GetLastTellName();
}
pos = 0;
while ((pos = message.find("%RT", pos)) != std::string::npos) {
message.replace(pos, 3, tell_name);
pos += tell_name.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%rt", pos)) != std::string::npos) {
message.replace(pos, 3, tell_name);
pos += tell_name.length();
}
pos = 0;
while ((pos = message.find("%R", pos)) != std::string::npos) {
message.replace(pos, 2, raceName);
pos += raceName.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%r", pos)) != std::string::npos) {
message.replace(pos, 2, raceName);
pos += raceName.length();
}
std::string petName = "Unknown";
if(in_caster && in_caster->IsEntity() && ((Entity*)in_caster)->GetPet()) {
petName = std::string(((Entity*)in_caster)->GetPet()->GetName());
}
pos = 0;
while ((pos = message.find("%M", pos)) != std::string::npos) {
message.replace(pos, 2, petName);
pos += petName.length();
}
// Replace all %t (no suppression)
pos = 0;
while ((pos = message.find("%m", pos)) != std::string::npos) {
message.replace(pos, 2, petName);
pos += petName.length();
}
} }

View File

@ -401,6 +401,8 @@ public:
void DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent = false); void DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent = false);
static bool AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_spell_targets = true); static bool AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_spell_targets = true);
mutable std::shared_mutex MSpellProcess; mutable std::shared_mutex MSpellProcess;
static void ReplaceEffectTokens(std::string& message, Spawn* in_caster, Spawn* in_target);
private: private:
MutexMap<Entity*,Spell*> spell_que; MutexMap<Entity*,Spell*> spell_que;
MutexList<LuaSpell*> active_spells; MutexList<LuaSpell*> active_spells;

View File

@ -178,8 +178,8 @@ public:
int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers, bool isCityZone, void* zonePtr = nullptr); int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers, bool isCityZone, void* zonePtr = nullptr);
bool IsClientConnectedPeer(int32 account_id); bool IsClientConnectedPeer(int32 account_id);
std::string GetCharacterPeerId(std::string charName); std::string GetCharacterPeerId(std::string charName);
void SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0); void SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0, int8 custom_type = 0);
void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0); void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0, int8 custom_type = 0);
void sendZonePeerList(Client* client); void sendZonePeerList(Client* client);
std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false, int32 matchDuplicatedId = 0); std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false, int32 matchDuplicatedId = 0);
int32 getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value = true); int32 getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value = true);

View File

@ -273,6 +273,15 @@ void World::Web_worldhandle_reloadcommand(const http::request<http::string_body>
world.RemoveReloadingSubSystem("ZoneScripts"); world.RemoveReloadingSubSystem("ZoneScripts");
break; break;
} }
case COMMAND_RELOAD_PLAYERSCRIPTS: {
world.SetReloadingSubsystem("PlayerScripts");
world.ResetPlayerScripts();
world.LoadPlayerScripts();
if (lua_interface)
lua_interface->DestroyPlayerScripts();
world.RemoveReloadingSubSystem("PlayerScripts");
break;
}
case COMMAND_RELOAD_FACTIONS: { case COMMAND_RELOAD_FACTIONS: {
world.SetReloadingSubsystem("Factions"); world.SetReloadingSubsystem("Factions");
master_faction_list.Clear(); master_faction_list.Clear();
@ -368,6 +377,16 @@ void World::Web_worldhandle_reloadcommand(const http::request<http::string_body>
break; break;
} }
case COMMAND_RELOAD_ITEMS: {
LogWrite(COMMAND__INFO, 0, "Command", "Reloading items..");
database.ReloadItemList(sub_command);
if(!item_id) {
database.LoadMerchantInformation(); // we skip if there is only a reload of single item not all items
}
break;
}
default: { default: {
success = 0; success = 0;
break; break;
@ -723,6 +742,7 @@ void World::Web_worldhandle_sendglobalmessage(const http::request<http::string_b
std::string toName(""), fromName(""), msg(""); std::string toName(""), fromName(""), msg("");
int32 group_id = 0; int32 group_id = 0;
int32 guild_id = 0; int32 guild_id = 0;
int8 custom_type = 0;
if (auto name = json_tree.get_optional<std::string>("to_name")) { if (auto name = json_tree.get_optional<std::string>("to_name")) {
toName = name.get(); toName = name.get();
} }
@ -744,6 +764,9 @@ void World::Web_worldhandle_sendglobalmessage(const http::request<http::string_b
if (auto guildID = json_tree.get_optional<int32>("guild_id")) { if (auto guildID = json_tree.get_optional<int32>("guild_id")) {
guild_id = guildID.get(); guild_id = guildID.get();
} }
if (auto customType = json_tree.get_optional<int8>("custom_type")) {
custom_type = customType.get();
}
Client* find_client = zone_list.GetClientByCharName(toName.c_str()); Client* find_client = zone_list.GetClientByCharName(toName.c_str());
if (find_client && find_client->GetPlayer()->IsIgnored(fromName.c_str())) if (find_client && find_client->GetPlayer()->IsIgnored(fromName.c_str()))
@ -754,6 +777,7 @@ void World::Web_worldhandle_sendglobalmessage(const http::request<http::string_b
if (find_client && find_client->GetPlayer()) { if (find_client && find_client->GetPlayer()) {
success = 1; success = 1;
toName = std::string(find_client->GetPlayer()->GetName()); toName = std::string(find_client->GetPlayer()->GetName());
find_client->SetLastTellName(fromName);
find_client->HandleTellMessage(fromName.c_str(), msg.c_str(), toName.c_str(), language); find_client->HandleTellMessage(fromName.c_str(), msg.c_str(), toName.c_str(), language);
if (find_client->GetPlayer()->get_character_flag(CF_AFK)) { if (find_client->GetPlayer()->get_character_flag(CF_AFK)) {
find_client->HandleTellMessage(toName.c_str(), find_client->GetPlayer()->GetAwayMessage().c_str(), fromName.c_str(), find_client->GetPlayer()->GetCurrentLanguage()); find_client->HandleTellMessage(toName.c_str(), find_client->GetPlayer()->GetAwayMessage().c_str(), fromName.c_str(), find_client->GetPlayer()->GetCurrentLanguage());
@ -819,6 +843,17 @@ void World::Web_worldhandle_sendglobalmessage(const http::request<http::string_b
} }
break; break;
} }
case CHANNEL_BROADCAST: {
switch(custom_type) {
case 0:
zone_list.HandleGlobalBroadcast(msg.c_str());
break;
case 1:
zone_list.HandleGlobalAnnouncement(msg.c_str());
break;
}
break;
}
} }
} }
@ -827,6 +862,7 @@ void World::Web_worldhandle_sendglobalmessage(const http::request<http::string_b
pt.put("to_name", toName); pt.put("to_name", toName);
pt.put("message", msg); pt.put("message", msg);
pt.put("from_language", language); pt.put("from_language", language);
pt.put("custom_type", custom_type);
pt.put("channel", in_channel); pt.put("channel", in_channel);
std::ostringstream oss; std::ostringstream oss;
boost::property_tree::write_json(oss, pt); boost::property_tree::write_json(oss, pt);

View File

@ -17,6 +17,12 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
#include <filesystem>
#include <vector>
#include <assert.h> #include <assert.h>
#include "World.h" #include "World.h"
#include "Items/Items.h" #include "Items/Items.h"
@ -44,10 +50,10 @@
#include "Chat/Chat.h" #include "Chat/Chat.h"
#include "Tradeskills/Tradeskills.h" #include "Tradeskills/Tradeskills.h"
#include "AltAdvancement/AltAdvancement.h" #include "AltAdvancement/AltAdvancement.h"
#include "LuaInterface.h"
#include "HeroicOp/HeroicOp.h" #include "HeroicOp/HeroicOp.h"
#include "RaceTypes/RaceTypes.h" #include "RaceTypes/RaceTypes.h"
#include "LuaInterface.h" #include "LuaInterface.h"
#include "SpellProcess.h"
#include "../common/version.h" #include "../common/version.h"
#include "Player.h" #include "Player.h"
@ -55,10 +61,6 @@
#include "Web/PeerManager.h" #include "Web/PeerManager.h"
#include "Web/HTTPSClientPool.h" #include "Web/HTTPSClientPool.h"
#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
MasterQuestList master_quest_list; MasterQuestList master_quest_list;
MasterItemList master_item_list; MasterItemList master_item_list;
MasterSpellList master_spell_list; MasterSpellList master_spell_list;
@ -90,7 +92,6 @@ map<int16, int16> EQOpcodeVersions;
WorldDatabase database; WorldDatabase database;
GuildList guild_list; GuildList guild_list;
Chat chat; Chat chat;
Player player;
extern ConfigReader configReader; extern ConfigReader configReader;
extern LoginServer loginserver; extern LoginServer loginserver;
@ -103,6 +104,8 @@ extern sint32 numclients;
extern PeerManager peer_manager; extern PeerManager peer_manager;
extern HTTPSClientPool peer_https_pool; extern HTTPSClientPool peer_https_pool;
namespace fs = std::filesystem;
World::World() : save_time_timer(300000), time_tick_timer(3000), vitality_timer(3600000), player_stats_timer(60000), server_stats_timer(60000), /*remove_grouped_player(30000),*/ guilds_timer(60000), lotto_players_timer(500), watchdog_timer(10000) { World::World() : save_time_timer(300000), time_tick_timer(3000), vitality_timer(3600000), player_stats_timer(60000), server_stats_timer(60000), /*remove_grouped_player(30000),*/ guilds_timer(60000), lotto_players_timer(500), watchdog_timer(10000) {
save_time_timer.Start(); save_time_timer.Start();
time_tick_timer.Start(); time_tick_timer.Start();
@ -195,7 +198,7 @@ void World::init(std::string web_ipaddr, int16 web_port, std::string cert_file,
LoadVoiceOvers(); LoadVoiceOvers();
LogWrite(WORLD__DEBUG, 1, "World", "-Loading EXP Curve From DB..."); LogWrite(WORLD__DEBUG, 1, "World", "-Loading EXP Curve From DB...");
player.InitXPTable(); Player::InitXPTable();
LogWrite(WORLD__DEBUG, 1, "World", "-Loading EXP Curve From DB Complete!"); LogWrite(WORLD__DEBUG, 1, "World", "-Loading EXP Curve From DB Complete!");
LogWrite(WORLD__DEBUG, 1, "World", "-Setting system parameters..."); LogWrite(WORLD__DEBUG, 1, "World", "-Setting system parameters...");
@ -537,6 +540,9 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
LogWrite(WORLD__ERROR, 0, "World", "HandleGlobalChatMessage() called with an invalid client"); LogWrite(WORLD__ERROR, 0, "World", "HandleGlobalChatMessage() called with an invalid client");
return false; return false;
} }
std::string tokenedMsg = std::string(message);
SpellProcess::ReplaceEffectTokens(tokenedMsg, from->GetPlayer(), from->GetPlayer()->GetTarget());
if(channel == CHANNEL_PRIVATE_TELL){ if(channel == CHANNEL_PRIVATE_TELL){
Client* find_client = zone_list.GetClientByCharName(to); Client* find_client = zone_list.GetClientByCharName(to);
if(find_client && find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName())) if(find_client && find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName()))
@ -549,7 +555,7 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
boost::property_tree::ptree root; boost::property_tree::ptree root;
root.put("from_name", from->GetPlayer()->GetName()); root.put("from_name", from->GetPlayer()->GetName());
root.put("to_name", to); root.put("to_name", to);
root.put("message", message); root.put("message", tokenedMsg.c_str());
root.put("from_language", current_language_id); root.put("from_language", current_language_id);
root.put("channel", channel); root.put("channel", channel);
std::ostringstream jsonStream; std::ostringstream jsonStream;
@ -572,8 +578,9 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
else else
{ {
const char* whoto = find_client->GetPlayer()->GetName(); const char* whoto = find_client->GetPlayer()->GetName();
find_client->HandleTellMessage(from->GetPlayer()->GetName(), message, whoto, from->GetPlayer()->GetCurrentLanguage()); find_client->SetLastTellName(std::string(from->GetPlayer()->GetName()));
from->HandleTellMessage(from->GetPlayer()->GetName(), message, whoto, from->GetPlayer()->GetCurrentLanguage()); find_client->HandleTellMessage(from->GetPlayer()->GetName(), tokenedMsg.c_str(), whoto, from->GetPlayer()->GetCurrentLanguage());
from->HandleTellMessage(from->GetPlayer()->GetName(), tokenedMsg.c_str(), whoto, from->GetPlayer()->GetCurrentLanguage());
if (find_client->GetPlayer()->get_character_flag(CF_AFK)) { if (find_client->GetPlayer()->get_character_flag(CF_AFK)) {
find_client->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage()); find_client->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage());
from->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage()); from->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage());
@ -641,6 +648,11 @@ void ZoneList::DeleteSpellProcess(){
MZoneList.releasereadlock(__FUNCTION__, __LINE__); MZoneList.releasereadlock(__FUNCTION__, __LINE__);
} }
void ZoneList::TransmitBroadcast(const char* message) {
HandleGlobalBroadcast(message);
peer_manager.SendPeersChannelMessage(0, "", std::string(message), CHANNEL_BROADCAST, 0);
}
void ZoneList::HandleGlobalBroadcast(const char* message) { void ZoneList::HandleGlobalBroadcast(const char* message) {
list<ZoneServer*>::iterator zone_iter; list<ZoneServer*>::iterator zone_iter;
ZoneServer* zone = 0; ZoneServer* zone = 0;
@ -653,6 +665,11 @@ void ZoneList::HandleGlobalBroadcast(const char* message) {
MZoneList.releasereadlock(__FUNCTION__, __LINE__); MZoneList.releasereadlock(__FUNCTION__, __LINE__);
} }
void ZoneList::TransmitGlobalAnnouncement(const char* message) {
HandleGlobalAnnouncement(message);
peer_manager.SendPeersChannelMessage(0, "", std::string(message), CHANNEL_BROADCAST, 1);
}
void ZoneList::HandleGlobalAnnouncement(const char* message) { void ZoneList::HandleGlobalAnnouncement(const char* message) {
list<ZoneServer*>::iterator zone_iter; list<ZoneServer*>::iterator zone_iter;
ZoneServer* zone = 0; ZoneServer* zone = 0;
@ -945,12 +962,13 @@ std::string PeerManager::GetCharacterPeerId(std::string charName) {
return ""; return "";
} }
void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id) { void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id, int8 custom_type) {
boost::property_tree::ptree root; boost::property_tree::ptree root;
root.put("message", message); root.put("message", message);
root.put("channel", channel); root.put("channel", channel);
root.put("group_id", group_id); root.put("group_id", group_id);
root.put("from_language", language_id); root.put("from_language", language_id);
root.put("custom_type", custom_type);
root.put("from_name", fromName); root.put("from_name", fromName);
std::ostringstream jsonStream; std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root); boost::property_tree::write_json(jsonStream, root);
@ -979,13 +997,14 @@ void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName,
} }
} }
void PeerManager::SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id) { void PeerManager::SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id, int8 custom_type) {
boost::property_tree::ptree root; boost::property_tree::ptree root;
root.put("message", message); root.put("message", message);
root.put("channel", channel); root.put("channel", channel);
root.put("guild_id", guild_id); root.put("guild_id", guild_id);
root.put("from_language", language_id); root.put("from_language", language_id);
root.put("from_name", fromName); root.put("from_name", fromName);
root.put("custom_type", custom_type);
std::ostringstream jsonStream; std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root); boost::property_tree::write_json(jsonStream, root);
std::string jsonPayload = jsonStream.str(); std::string jsonPayload = jsonStream.str();
@ -1794,6 +1813,39 @@ void World::AddZoneScript(int32 id, const char* name) {
MZoneScripts.unlock(); MZoneScripts.unlock();
} }
void World::LoadPlayerScripts() {
const fs::path scriptDir = "PlayerScripts";
bool hasGlobalLua = false;
if (!fs::exists(scriptDir) || !fs::is_directory(scriptDir)) {
std::cerr << "Directory does not exist: " << scriptDir << std::endl;
return;
}
for (const auto& entry : fs::directory_iterator(scriptDir)) {
if (entry.is_regular_file() && entry.path().extension() == ".lua") {
std::string baseName = entry.path().stem().string(); // Strips extension
const std::string filename = entry.path().string();
int32 zoneID = database.GetZoneID(filename.c_str());
std::cout << " - Load File " << filename << " with base name: " << baseName << "\n";
if(zoneID) {
AddPlayerScript(zoneID, filename.c_str());
}
else if (baseName == "global") {
AddPlayerScript(0, filename.c_str());
hasGlobalLua = true;
}
}
}
}
void World::AddPlayerScript(int32 zone_id, const char* zone_name) {
MPlayerScripts.lock();
if (zone_name)
player_scripts[zone_id] = string(zone_name);
MPlayerScripts.unlock();
}
const char* World::GetSpawnScript(int32 id){ const char* World::GetSpawnScript(int32 id){
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__); LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
const char* ret = 0; const char* ret = 0;
@ -1836,6 +1888,15 @@ const char* World::GetZoneScript(int32 id) {
return ret; return ret;
} }
const char* World::GetPlayerScript(int32 zone_id) {
const char* ret = 0;
MPlayerScripts.lock();
if (player_scripts.count(zone_id) > 0)
ret = player_scripts[zone_id].c_str();
MPlayerScripts.unlock();
return ret;
}
void World::ResetSpawnScripts(){ void World::ResetSpawnScripts(){
MSpawnScripts.lock(); MSpawnScripts.lock();
spawn_scripts.clear(); spawn_scripts.clear();
@ -1850,7 +1911,11 @@ void World::ResetZoneScripts() {
MZoneScripts.unlock(); MZoneScripts.unlock();
} }
void World::ResetPlayerScripts() {
MPlayerScripts.lock();
player_scripts.clear();
MPlayerScripts.unlock();
}
vector<MerchantItemInfo>* World::GetMerchantItemList(int32 merchant_id, int8 merchant_type, Player* player) vector<MerchantItemInfo>* World::GetMerchantItemList(int32 merchant_id, int8 merchant_type, Player* player)
{ {
@ -2595,10 +2660,10 @@ void World::CheckLottoPlayers() {
memset(announcement, 0, sizeof(announcement)); memset(announcement, 0, sizeof(announcement));
sprintf(coin_message, "%s", client->GetCoinMessage(jackpot).c_str()); sprintf(coin_message, "%s", client->GetCoinMessage(jackpot).c_str());
sprintf(message, "Congratulations! You have won %s!", coin_message); sprintf(message, "Congratulations! You have won %s!", coin_message);
sprintf(announcement, "%s as won the jackpot containing a total of %s!", client->GetPlayer()->GetName(), coin_message); sprintf(announcement, "%s has won the jackpot containing a total of %s!", client->GetPlayer()->GetName(), coin_message);
client->Message(CHANNEL_COLOR_YELLOW, "You receive %s.", coin_message); client->Message(CHANNEL_COLOR_YELLOW, "You receive %s.", coin_message);
client->SendPopupMessage(0, message, "", 2, 0xFF, 0xFF, 0x99); client->SendPopupMessage(0, message, "", 2, 0xFF, 0xFF, 0x99);
zone_list.HandleGlobalAnnouncement(announcement); zone_list.TransmitGlobalAnnouncement(announcement);
client->GetPlayer()->AddCoins(jackpot); client->GetPlayer()->AddCoins(jackpot);
client->GetPlayer()->GetZone()->SendCastSpellPacket(843, client->GetPlayer()); client->GetPlayer()->GetZone()->SendCastSpellPacket(843, client->GetPlayer());
client->GetPlayer()->GetZone()->SendCastSpellPacket(1413, client->GetPlayer()); client->GetPlayer()->GetZone()->SendCastSpellPacket(1413, client->GetPlayer());

View File

@ -1,6 +1,6 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef EQ2_WORLD_H #ifndef EQ2_WORLD_H
#define EQ2_WORLD_H #define EQ2_WORLD_H
@ -476,6 +477,10 @@ class ZoneList {
void CheckFriendZoned(Client* client); void CheckFriendZoned(Client* client);
// move to Chat/Chat.h? // move to Chat/Chat.h?
void TransmitBroadcast(const char* message);
void TransmitGlobalAnnouncement(const char* message);
// these are handled internally by the transmit and peering
bool HandleGlobalChatMessage(Client* from, char* to, int16 channel, const char* message, const char* channel_name = 0, int32 current_language_id = 0); bool HandleGlobalChatMessage(Client* from, char* to, int16 channel, const char* message, const char* channel_name = 0, int32 current_language_id = 0);
void HandleGlobalBroadcast(const char* message); void HandleGlobalBroadcast(const char* message);
void HandleGlobalAnnouncement(const char* message); void HandleGlobalAnnouncement(const char* message);
@ -589,12 +594,16 @@ public:
void AddSpawnEntryScript(int32 id, const char* name); void AddSpawnEntryScript(int32 id, const char* name);
void AddSpawnLocationScript(int32 id, const char* name); void AddSpawnLocationScript(int32 id, const char* name);
void AddZoneScript(int32 id, const char* name); void AddZoneScript(int32 id, const char* name);
void LoadPlayerScripts();
void AddPlayerScript(int32 zone_id, const char* zone_name);
const char* GetSpawnScript(int32 id); const char* GetSpawnScript(int32 id);
const char* GetSpawnEntryScript(int32 id); const char* GetSpawnEntryScript(int32 id);
const char* GetSpawnLocationScript(int32 id); const char* GetSpawnLocationScript(int32 id);
const char* GetZoneScript(int32 id); const char* GetZoneScript(int32 id);
const char* GetPlayerScript(int32 zone_id);
void ResetSpawnScripts(); void ResetSpawnScripts();
void ResetZoneScripts(); void ResetZoneScripts();
void ResetPlayerScripts();
int16 GetMerchantItemQuantity(int32 merchant_id, int32 item_id); int16 GetMerchantItemQuantity(int32 merchant_id, int32 item_id);
void DecreaseMerchantQuantity(int32 merchant_id, int32 item_id, int16 amount); void DecreaseMerchantQuantity(int32 merchant_id, int32 item_id, int16 amount);
int32 GetInventoryID(int32 merchant_id, int32 item_id); int32 GetInventoryID(int32 merchant_id, int32 item_id);
@ -765,6 +774,7 @@ private:
Mutex MMerchantList; Mutex MMerchantList;
Mutex MSpawnScripts; Mutex MSpawnScripts;
Mutex MZoneScripts; Mutex MZoneScripts;
Mutex MPlayerScripts;
//Mutex MGroups; //Mutex MGroups;
mutable std::shared_mutex MNPCSpells; mutable std::shared_mutex MNPCSpells;
@ -783,6 +793,7 @@ private:
map<int32, string> spawnentry_scripts; map<int32, string> spawnentry_scripts;
map<int32, string> spawnlocation_scripts; map<int32, string> spawnlocation_scripts;
map<int32, string> zone_scripts; map<int32, string> zone_scripts;
map<int32, string> player_scripts;
//vector<PlayerGroup*> player_groups; //vector<PlayerGroup*> player_groups;
//map<GroupMemberInfo*, int32> group_removal_pending; //map<GroupMemberInfo*, int32> group_removal_pending;
//map<string, string> pending_groups; //map<string, string> pending_groups;

View File

@ -3251,8 +3251,9 @@ string WorldDatabase::GetExpansionIDByVersion(int16 version)
} }
void WorldDatabase::LoadSpecialZones(){ void WorldDatabase::LoadSpecialZones(bool silent){
LogWrite(ZONE__INFO, 0, "Zone", "Starting static zones..."); if(!silent)
LogWrite(ZONE__INFO, 0, "Zone", "Starting static zones...");
Query query; Query query;
ZoneServer* zone = 0; ZoneServer* zone = 0;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded, peer_priority FROM zones where always_loaded = 1"); MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded, peer_priority FROM zones where always_loaded = 1");
@ -5589,6 +5590,7 @@ string WorldDatabase::GetMerchantDescription(int32 merchant_id) {
void WorldDatabase::LoadTransporters(ZoneServer* zone){ void WorldDatabase::LoadTransporters(ZoneServer* zone){
int32 total = 0; int32 total = 0;
zone->DeleteTransporters();
zone->DeleteGlobalTransporters(); zone->DeleteGlobalTransporters();
Query query; Query query;
MYSQL_ROW row; MYSQL_ROW row;

View File

@ -208,7 +208,7 @@ public:
int32 SaveCharacter(PacketStruct* create, int32 loginID); int32 SaveCharacter(PacketStruct* create, int32 loginID);
int32 LoadNPCAppearanceEquipmentData(ZoneServer* zone); int32 LoadNPCAppearanceEquipmentData(ZoneServer* zone);
void SaveNPCAppearanceEquipment(int32 spawn_id, int8 slot_id, int16 type, int8 red=0, int8 green=0, int8 blue=0, int8 hred=0, int8 hgreen=0, int8 hblue=0); void SaveNPCAppearanceEquipment(int32 spawn_id, int8 slot_id, int16 type, int8 red=0, int8 green=0, int8 blue=0, int8 hred=0, int8 hgreen=0, int8 hblue=0);
void LoadSpecialZones(); void LoadSpecialZones(bool silent=false);
void SaveCharacterSkills(Client* client); void SaveCharacterSkills(Client* client);
void SaveCharacterQuests(Client* client); void SaveCharacterQuests(Client* client);
void SaveCharacterQuestProgress(Client* client, Quest* quest); void SaveCharacterQuestProgress(Client* client, Quest* quest);

View File

@ -1,22 +1,23 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful, EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "../common/debug.h" #include "../common/debug.h"
#include "../common/Log.h" #include "../common/Log.h"
#include <iostream> #include <iostream>
@ -136,7 +137,7 @@ Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125
eqs = ieqs; eqs = ieqs;
ip = eqs->GetrIP(); ip = eqs->GetrIP();
port = ntohs(eqs->GetrPort()); port = ntohs(eqs->GetrPort());
merchant_transaction = nullptr; merchant_transaction_id = 0;
mail_window.item = nullptr; // don't want this to be set(loose ptr) when using ResetSendMail to provide rest of the defaults mail_window.item = nullptr; // don't want this to be set(loose ptr) when using ResetSendMail to provide rest of the defaults
ResetSendMail(false); ResetSendMail(false);
timestamp_flag = 0; timestamp_flag = 0;
@ -190,7 +191,7 @@ Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125
dead_z = 0.0f; dead_z = 0.0f;
dead_h = 0.0f; dead_h = 0.0f;
lua_debug_timer.Disable(); lua_debug_timer.Disable();
transport_spawn = 0; transport_spawn_id = 0;
MBuyBack.SetName("Client::MBuyBack"); MBuyBack.SetName("Client::MBuyBack");
MDeletePlayer.SetName("Client::MDeletePlayer"); MDeletePlayer.SetName("Client::MDeletePlayer");
MQuestPendingUpdates.SetName("Client::MQuestPendingUpdates"); MQuestPendingUpdates.SetName("Client::MQuestPendingUpdates");
@ -5428,12 +5429,29 @@ void Client::SendPopupMessage(int8 unknown, const char* text, const char* type,
} }
void Client::ChangeLevel(int16 old_level, int16 new_level) { bool Client::ChangeLevel(int16 old_level, int16 new_level, int32 xp_earned) {
if (new_level < 1) { if (new_level < 1) {
SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!"); SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!");
return; return false;
}
const char* playerScript = world.GetPlayerScript(0); // 0 = global script
const char* playerZoneScript = world.GetPlayerScript(GetCurrentZoneID()); // zone script
sint32 returnValue = 0;
if(playerScript || playerZoneScript) {
std::vector<LuaArg> args = {
LuaArg(GetPlayer()->GetZone()),
LuaArg(GetPlayer()),
LuaArg(old_level),
LuaArg(new_level),
LuaArg(xp_earned)
};
if(playerScript && lua_interface->RunPlayerScriptWithReturn(playerScript, "on_level_up", args, &returnValue) && returnValue == -1) {
return false;
}
if(playerZoneScript && lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_level_up", args, &returnValue) && returnValue == -1) {
return false;
}
} }
if (player->GetLevel() != new_level) { if (player->GetLevel() != new_level) {
player->SetLevel(new_level); player->SetLevel(new_level);
if (player->GetGroupMemberInfo()) { if (player->GetGroupMemberInfo()) {
@ -5625,17 +5643,38 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower()) if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower())
GetPlayer()->GetZone()->AddDamagedSpawn(GetPlayer()); GetPlayer()->GetZone()->AddDamagedSpawn(GetPlayer());
return true;
} }
void Client::ChangeTSLevel(int16 old_level, int16 new_level) { bool Client::ChangeTSLevel(int16 old_level, int16 new_level, int32 xp_earned) {
if (new_level < 1) { if (new_level < 1) {
SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!"); SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!");
return; return false;
} }
if ((player->GetTSLevel() >= 9 && player->GetTradeskillClass() == 1) || (player->GetTSLevel() >= 19 && (player->GetTradeskillClass() == 1 || player->GetTradeskillClass() == 2 || player->GetTradeskillClass() == 6 || player->GetTradeskillClass() == 10))) { if ((player->GetTSLevel() >= 9 && player->GetTradeskillClass() == 1) || (player->GetTSLevel() >= 19 && (player->GetTradeskillClass() == 1 || player->GetTradeskillClass() == 2 || player->GetTradeskillClass() == 6 || player->GetTradeskillClass() == 10))) {
SimpleMessage(CHANNEL_COLOR_YELLOW, "You can not gain levels until you select your next class!"); SimpleMessage(CHANNEL_COLOR_YELLOW, "You can not gain levels until you select your next class!");
return; return false;
} }
const char* playerScript = world.GetPlayerScript(0); // 0 = global script
const char* playerZoneScript = world.GetPlayerScript(GetCurrentZoneID()); // zone script
sint32 returnValue = 0;
if(playerScript || playerZoneScript) {
std::vector<LuaArg> args = {
LuaArg(GetPlayer()->GetZone()),
LuaArg(GetPlayer()),
LuaArg(old_level),
LuaArg(new_level),
LuaArg(xp_earned)
};
if(playerScript && lua_interface->RunPlayerScriptWithReturn(playerScript, "on_tradeskill_level_up", args, &returnValue) && returnValue == -1) {
return false;
}
if(playerZoneScript && lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_tradeskill_level_up", args, &returnValue) && returnValue == -1) {
return false;
}
}
if (new_level > old_level) if (new_level > old_level)
player->UpdatePlayerHistory(HISTORY_TYPE_XP, HISTORY_SUBTYPE_TRADESKILL, new_level, player->GetTradeskillClass()); player->UpdatePlayerHistory(HISTORY_TYPE_XP, HISTORY_SUBTYPE_TRADESKILL, new_level, player->GetTradeskillClass());
@ -5820,6 +5859,7 @@ void Client::ChangeTSLevel(int16 old_level, int16 new_level) {
// to when you are actually able to select traits. // to when you are actually able to select traits.
QueuePacket(GetPlayer()->GetPlayerInfo()->serialize(GetVersion())); QueuePacket(GetPlayer()->GetPlayerInfo()->serialize(GetVersion()));
QueuePacket(master_trait_list.GetTraitListPacket(this)); QueuePacket(master_trait_list.GetTraitListPacket(this));
return true;
} }
void Client::CloseLoot(int32 spawn_id) { void Client::CloseLoot(int32 spawn_id) {
@ -6381,19 +6421,18 @@ void Client::CastGroupOrSelf(Entity* source, uint32 spellID, uint32 spellTier, f
} }
} }
Spawn* Client::GetBanker() { int32 Client::GetBanker() {
return banker; return banker_id;
} }
void Client::SetBanker(Spawn* in_banker) { void Client::SetBanker(int32 in_banker) {
banker = in_banker; banker_id = in_banker;
} }
void Client::Bank(Spawn* banker, bool cancel) { void Client::Bank(Spawn* banker, bool cancel) {
if (banker && banker->primary_command_list.size() > 0 && banker->primary_command_list[0]->command == "bank") { if (banker && banker->primary_command_list.size() > 0 && banker->primary_command_list[0]->command == "bank") {
if (!cancel) if (!cancel)
SetBanker(banker); SetBanker(banker->GetID());
else else
SetBanker(0); SetBanker(0);
PacketStruct* packet = configReader.getStruct("WS_UpdateBank", GetVersion()); PacketStruct* packet = configReader.getStruct("WS_UpdateBank", GetVersion());
@ -6550,7 +6589,8 @@ bool Client::BankWithdrawalNoBanker(int64 amount) {
void Client::BankWithdrawal(int64 amount) { void Client::BankWithdrawal(int64 amount) {
bool cheater = false; bool cheater = false;
if (GetBanker() && amount > 0) { Spawn* banker = GetCurrentZone()->GetSpawnByID(GetBanker());
if (banker && amount > 0) {
string withdrawal = ""; string withdrawal = "";
char withdrawal_data[512] = { 0 }; char withdrawal_data[512] = { 0 };
int32 tmp = 0; int32 tmp = 0;
@ -6638,7 +6678,8 @@ void Client::BankWithdrawal(int64 amount) {
void Client::BankDeposit(int64 amount) { void Client::BankDeposit(int64 amount) {
bool cheater = false; bool cheater = false;
if (GetBanker() && amount > 0) { Spawn* banker = GetCurrentZone()->GetSpawnByID(GetBanker());
if (banker && amount > 0) {
int32 tmp = 0; int32 tmp = 0;
char deposit_data[512] = { 0 }; char deposit_data[512] = { 0 };
string deposit = ""; string deposit = "";
@ -8129,12 +8170,17 @@ void Client::SetLuaDebugClient(bool val) {
} }
void Client::SetMerchantTransaction(Spawn* spawn) { void Client::SetMerchantTransaction(Spawn* spawn) {
merchant_transaction = spawn; if(spawn) {
merchant_transaction_id = spawn->GetID();
}
else {
merchant_transaction_id = 0;
}
} }
Spawn* Client::GetMerchantTransaction() { int32 Client::GetMerchantTransactionID() {
return merchant_transaction; return merchant_transaction_id;
} }
void Client::SetMailTransaction(Spawn* spawn) { void Client::SetMailTransaction(Spawn* spawn) {
@ -8199,7 +8245,7 @@ float Client::CalculateSellMultiplier(int32 merchant_id) {
} }
void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) { void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
Guild* guild = GetPlayer()->GetGuild(); Guild* guild = GetPlayer()->GetGuild();
if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) && if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) &&
spawn->IsClientInMerchantLevelRange(this)) { spawn->IsClientInMerchantLevelRange(this)) {
@ -8317,7 +8363,7 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
} }
void Client::BuyBack(int32 item_id, int16 quantity) { void Client::BuyBack(int32 item_id, int16 quantity) {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) && if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) &&
spawn->IsClientInMerchantLevelRange(this)) { spawn->IsClientInMerchantLevelRange(this)) {
deque<BuyBackItem*>::iterator itr; deque<BuyBackItem*>::iterator itr;
@ -8391,7 +8437,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
void Client::BuyItem(int32 item_id, int16 quantity) { void Client::BuyItem(int32 item_id, int16 quantity) {
// Get the merchant we are buying from // Get the merchant we are buying from
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
// Make sure the spawn has a merchant list // Make sure the spawn has a merchant list
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
int32 total_buy_price = 0; int32 total_buy_price = 0;
@ -8585,7 +8631,7 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
} }
void Client::RepairItem(int32 item_id) { void Client::RepairItem(int32 item_id) {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) { if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) {
Item* item = player->item_list.GetItemFromID(item_id); Item* item = player->item_list.GetItemFromID(item_id);
if (!item) if (!item)
@ -8623,7 +8669,7 @@ void Client::RepairItem(int32 item_id) {
} }
void Client::RepairAllItems() { void Client::RepairAllItems() {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) { if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) {
vector<Item*>* repairable_items = GetRepairableItems(); vector<Item*>* repairable_items = GetRepairableItems();
if (repairable_items && repairable_items->size() > 0) { if (repairable_items && repairable_items->size() > 0) {
@ -8771,7 +8817,7 @@ void Client::SendAchievementUpdate(bool first_login) {
} }
void Client::SendBuyMerchantList(bool sell) { void Client::SendBuyMerchantList(bool sell) {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
vector<MerchantItemInfo>* items = world.GetMerchantItemList(spawn->GetMerchantID(), spawn->GetMerchantType(), player); vector<MerchantItemInfo>* items = world.GetMerchantItemList(spawn->GetMerchantID(), spawn->GetMerchantType(), player);
if (items) { if (items) {
@ -8918,7 +8964,7 @@ void Client::SendBuyMerchantList(bool sell) {
} }
void Client::SendSellMerchantList(bool sell) { void Client::SendSellMerchantList(bool sell) {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (!spawn || (spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) || (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) if (!spawn || (spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) || (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
return; return;
@ -9054,7 +9100,7 @@ void Client::SendSellMerchantList(bool sell) {
void Client::SendBuyBackList(bool sell) { void Client::SendBuyBackList(bool sell) {
if (GetVersion() <= 561) //this wasn't added until LU37 on July 31st 2007, well after the DoF client if (GetVersion() <= 561) //this wasn't added until LU37 on July 31st 2007, well after the DoF client
return; return;
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
deque<BuyBackItem*>::iterator itr; deque<BuyBackItem*>::iterator itr;
int i = 0; int i = 0;
@ -9127,7 +9173,7 @@ void Client::SendBuyBackList(bool sell) {
} }
void Client::SendRepairList() { void Client::SendRepairList() {
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn) { if (spawn) {
vector<Item*>* repairable_items = GetRepairableItems(); vector<Item*>* repairable_items = GetRepairableItems();
PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion()); PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
@ -9202,7 +9248,7 @@ void Client::ShowLottoWindow() {
SimpleMessage(CHANNEL_COLOR_RED, "This client does not support the gambler UI, only Desert of Flames or later client."); SimpleMessage(CHANNEL_COLOR_RED, "This client does not support the gambler UI, only Desert of Flames or later client.");
return; return;
} }
Spawn* spawn = GetMerchantTransaction(); Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
if (spawn) { if (spawn) {
int32 item_id = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, GamblingTokenItemID)->GetInt32(); int32 item_id = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, GamblingTokenItemID)->GetInt32();
@ -9554,6 +9600,25 @@ void Client::DisplayMailMessage(int32 mail_id) {
safe_delete(update); safe_delete(update);
} }
if (!mail->already_read) { if (!mail->already_read) {
const char* playerScript = world.GetPlayerScript(0); // 0 = global script
const char* playerZoneScript = world.GetPlayerScript(GetCurrentZoneID()); // zone script
if(playerScript || playerZoneScript) {
std::vector<LuaArg> args = {
LuaArg(GetCurrentZone()),
LuaArg(GetPlayer()),
LuaArg(GetMailTransaction()),
LuaArg(mail->player_from),
LuaArg(mail->subject),
LuaArg(mail->mail_body),
LuaArg(mail->char_item_id)
};
if(playerScript) {
lua_interface->RunPlayerScriptWithReturn(playerScript, "on_mail_first_read", args);
}
if(playerZoneScript) {
lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_mail_first_read", args);
}
}
mail->already_read = true; mail->already_read = true;
SendMailList(); SendMailList();
} }
@ -9577,13 +9642,15 @@ void Client::DisplayMailMessage(int32 mail_id) {
if (mail->stack || mail->char_item_id) if (mail->stack || mail->char_item_id)
{ {
Item* item = master_item_list.GetItem(mail->char_item_id); Item* item = master_item_list.GetItem(mail->char_item_id);
item->stack_count = mail->stack > 1 ? mail->stack : 0; if(item) {
if (version < 860) item->stack_count = mail->stack > 1 ? mail->stack : 0;
packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1); if (version < 860)
else if (version < 1193) packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1);
packet->setItemByName("item", item, player, 0, 0); else if (version < 1193)
else packet->setItemByName("item", item, player, 0, 0);
packet->setItemByName("item", item, player, 0, 2); else
packet->setItemByName("item", item, player, 0, 2);
}
} }
else else
{ {
@ -10033,7 +10100,7 @@ void Client::ProcessTeleport(Spawn* spawn, vector<TransportDestination*>* destin
if (transport_id > 0) if (transport_id > 0)
has_map = GetCurrentZone()->TransportHasMap(transport_id); has_map = GetCurrentZone()->TransportHasMap(transport_id);
transport_spawn = spawn; transport_spawn_id = spawn->GetID();
vector<TransportDestination*> transport_list; vector<TransportDestination*> transport_list;
vector<TransportDestination*>::iterator itr; vector<TransportDestination*>::iterator itr;
TransportDestination* destination = 0; TransportDestination* destination = 0;
@ -10190,7 +10257,7 @@ void Client::ProcessTeleportLocation(EQApplicationPacket* app) {
zone_name = zone_name.substr(0, lastSpacePos); zone_name = zone_name.substr(0, lastSpacePos);
} }
} }
if (this->GetTemporaryTransportID() || (spawn && spawn == transport_spawn && spawn->GetTransporterID())) if (this->GetTemporaryTransportID() || (spawn && spawn->GetID() == transport_spawn_id && spawn->GetTransporterID()))
GetCurrentZone()->GetTransporters(&destinations, this, this->GetTemporaryTransportID() ? this->GetTemporaryTransportID() : spawn->GetTransporterID()); GetCurrentZone()->GetTransporters(&destinations, this, this->GetTemporaryTransportID() ? this->GetTemporaryTransportID() : spawn->GetTransporterID());
vector<TransportDestination*>::iterator itr; vector<TransportDestination*>::iterator itr;
for (itr = destinations.begin(); itr != destinations.end(); itr++) { for (itr = destinations.begin(); itr != destinations.end(); itr++) {

View File

@ -285,8 +285,8 @@ public:
void HandleQuickbarUpdateRequest(EQApplicationPacket* app); void HandleQuickbarUpdateRequest(EQApplicationPacket* app);
void SendPopupMessage(int8 unknown, const char* text, const char* type, float size, int8 red, int8 green, int8 blue); void SendPopupMessage(int8 unknown, const char* text, const char* type, float size, int8 red, int8 green, int8 blue);
void PopulateSkillMap(); void PopulateSkillMap();
void ChangeLevel(int16 old_level, int16 new_level); bool ChangeLevel(int16 old_level, int16 new_level, int32 xp_earned = 0);
void ChangeTSLevel(int16 old_level, int16 new_level); bool ChangeTSLevel(int16 old_level, int16 new_level, int32 xp_earned = 0);
bool Summon(const char* search_name); bool Summon(const char* search_name);
std::string IdentifyInstanceLockout(int32 zoneID, bool displayClient = true); std::string IdentifyInstanceLockout(int32 zoneID, bool displayClient = true);
bool IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID); bool IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID);
@ -299,8 +299,8 @@ public:
bool BankWithdrawalNoBanker(int64 amount); bool BankWithdrawalNoBanker(int64 amount);
bool BankHasCoin(int64 amount); bool BankHasCoin(int64 amount);
void BankDeposit(int64 amount); void BankDeposit(int64 amount);
Spawn* GetBanker(); int32 GetBanker();
void SetBanker(Spawn* in_banker); void SetBanker(int32 in_banker);
bool AddItem(int32 item_id, int16 quantity = 0, AddItemType type = AddItemType::NOT_SET); bool AddItem(int32 item_id, int16 quantity = 0, AddItemType type = AddItemType::NOT_SET);
bool AddItem(Item* item, bool* item_deleted = 0, AddItemType type = AddItemType::NOT_SET); bool AddItem(Item* item, bool* item_deleted = 0, AddItemType type = AddItemType::NOT_SET);
bool AddItemToBank(int32 item_id, int16 quantity = 0); bool AddItemToBank(int32 item_id, int16 quantity = 0);
@ -401,13 +401,14 @@ public:
void RemoveCombineSpawn(Spawn* spawn); void RemoveCombineSpawn(Spawn* spawn);
void SaveCombineSpawns(const char* name = 0); void SaveCombineSpawns(const char* name = 0);
Spawn* GetCombineSpawn(); Spawn* GetCombineSpawn();
void SetCombineSpawn(Spawn* spawn) { combine_spawn = spawn; }
bool ShouldTarget(); bool ShouldTarget();
void TargetSpawn(Spawn* spawn); void TargetSpawn(Spawn* spawn);
void ReloadQuests(); void ReloadQuests();
int32 GetCurrentQuestID() { return current_quest_id; } int32 GetCurrentQuestID() { return current_quest_id; }
void SetLuaDebugClient(bool val); void SetLuaDebugClient(bool val);
void SetMerchantTransaction(Spawn* spawn); void SetMerchantTransaction(Spawn* spawn);
Spawn* GetMerchantTransaction(); int32 GetMerchantTransactionID();
void SetMailTransaction(Spawn* spawn); void SetMailTransaction(Spawn* spawn);
Spawn* GetMailTransaction(); Spawn* GetMailTransaction();
void PlaySound(const char* name); void PlaySound(const char* name);
@ -715,6 +716,12 @@ public:
void SendReceiveOffer(Client* client_target, int8 type, std::string name, int8 unknown2); void SendReceiveOffer(Client* client_target, int8 type, std::string name, int8 unknown2);
bool SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt, const std::string& acceptText, const std::string& acceptCommand, const std::string& declineText, const std::string& declineCommand, int32 time, int8 textBox, int8 textBoxRequired, int32 maxLength); bool SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt, const std::string& acceptText, const std::string& acceptCommand, const std::string& declineText, const std::string& declineCommand, int32 time, int8 textBox, int8 textBoxRequired, int32 maxLength);
void SetTransportSpawnID(int32 id) { transport_spawn_id = id; }
int32 GetTransportSpawnID() { return transport_spawn_id; }
void SetLastTellName(std::string tellName) { last_tell_name = tellName; }
std::string GetLastTellName() { return last_tell_name; }
DialogManager dialog_manager; DialogManager dialog_manager;
private: private:
void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i); void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i);
@ -737,10 +744,10 @@ private:
vector<Item*>* search_items; vector<Item*>* search_items;
int32 waypoint_id = 0; int32 waypoint_id = 0;
map<string, WaypointInfo> waypoints; map<string, WaypointInfo> waypoints;
Spawn* transport_spawn; int32 transport_spawn_id;
Mutex MBuyBack; Mutex MBuyBack;
deque<BuyBackItem*> buy_back_items; deque<BuyBackItem*> buy_back_items;
Spawn* merchant_transaction; int32 merchant_transaction_id;
Spawn* mail_transaction; Spawn* mail_transaction;
mutable std::shared_mutex MPendingQuestAccept; mutable std::shared_mutex MPendingQuestAccept;
vector<int32> pending_quest_accept; vector<int32> pending_quest_accept;
@ -754,7 +761,7 @@ private:
mutable std::shared_mutex MConversation; mutable std::shared_mutex MConversation;
map<int32, map<int8, string> > conversation_map; map<int32, map<int8, string> > conversation_map;
int32 current_quest_id; int32 current_quest_id;
Spawn* banker; int32 banker_id;
map<int32, int32> sent_spell_details; map<int32, int32> sent_spell_details;
map<int32, bool> sent_item_details; map<int32, bool> sent_item_details;
Player* player; Player* player;
@ -879,6 +886,8 @@ private:
uchar* recipe_xor_packet; uchar* recipe_xor_packet;
int recipe_packet_count; int recipe_packet_count;
int recipe_orig_packet_size; int recipe_orig_packet_size;
std::string last_tell_name;
}; };
class ClientList { class ClientList {

View File

@ -339,6 +339,9 @@ int main(int argc, char** argv) {
LogWrite(LUA__INFO, 0, "LUA", "Loading Zone Scripts..."); LogWrite(LUA__INFO, 0, "LUA", "Loading Zone Scripts...");
database.LoadZoneScriptData(); database.LoadZoneScriptData();
LogWrite(LUA__INFO, 0, "LUA", "Loading Player Scripts...");
world.LoadPlayerScripts();
LogWrite(WORLD__INFO, 0, "World", "Loading House Zone Data..."); LogWrite(WORLD__INFO, 0, "World", "Loading House Zone Data...");
database.LoadHouseZones(); database.LoadHouseZones();
database.LoadPlayerHouses(); database.LoadPlayerHouses();
@ -642,7 +645,7 @@ ThreadReturnType StartPeerPoll (void* tmp)
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if(check_zone > 60) { if(check_zone > 60) {
check_zone = 0; check_zone = 0;
database.LoadSpecialZones(); database.LoadSpecialZones(true);
} }
check_zone++; check_zone++;
} }

View File

@ -1,20 +1,21 @@
/* /*
EQ2Emulator: Everquest II Server Emulator EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator. This file is part of EQ2Emulator.
EQ2Emulator 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 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful, EQ2Emulator is free software: you can redistribute it and/or modify
but WITHOUT ANY WARRANTY; without even the implied warranty of it under the terms of the GNU General Public License as published by
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the the Free Software Foundation, either version 3 of the License, or
GNU General Public License for more details. (at your option) any later version.
You should have received a copy of the GNU General Public License EQ2Emulator is distributed in the hope that it will be useful,
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "../common/debug.h" #include "../common/debug.h"
@ -810,6 +811,7 @@ void ZoneServer::ProcessDepop(bool respawns_allowed, bool repop) {
if(!client) if(!client)
continue; continue;
client->GetPlayer()->SetTarget(0); client->GetPlayer()->SetTarget(0);
client->SetMailTransaction(0);
if(repop) if(repop)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Zone Repop in progress..."); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Zone Repop in progress...");
else{ else{
@ -3910,12 +3912,14 @@ void ZoneServer::HandleChatMessage(Client* client, std::string fromName, const c
void ZoneServer::HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language){ void ZoneServer::HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language){
vector<Client*>::iterator client_itr; vector<Client*>::iterator client_itr;
Client* client = 0; Client* client = 0;
std::string tokenedMsg = std::string(message);
SpellProcess::ReplaceEffectTokens(tokenedMsg, from, from->GetTarget());
MClientList.readlock(__FUNCTION__, __LINE__); MClientList.readlock(__FUNCTION__, __LINE__);
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
client = *client_itr; client = *client_itr;
if(client && client->IsConnected()) if(client && client->IsConnected())
HandleChatMessage(client, from, to, channel, message, distance, channel_name, show_bubble, language); HandleChatMessage(client, from, to, channel, tokenedMsg.c_str(), distance, channel_name, show_bubble, language);
} }
MClientList.releasereadlock(__FUNCTION__, __LINE__); MClientList.releasereadlock(__FUNCTION__, __LINE__);
} }
@ -4607,7 +4611,33 @@ void ZoneServer::CheckSpawnScriptTimers(){
vector<SpawnScriptTimer>::iterator itr; vector<SpawnScriptTimer>::iterator itr;
for(itr = call_timers.begin(); itr != call_timers.end(); itr++){ for(itr = call_timers.begin(); itr != call_timers.end(); itr++){
SpawnScriptTimer tmpTimer = (SpawnScriptTimer)*itr; SpawnScriptTimer tmpTimer = (SpawnScriptTimer)*itr;
CallSpawnScript(GetSpawnByID(tmpTimer.spawn), SPAWN_SCRIPT_TIMER, tmpTimer.player > 0 ? GetSpawnByID(tmpTimer.player) : 0, tmpTimer.function.c_str()); Spawn* callSpawn = GetSpawnByID(tmpTimer.spawn);
Spawn* target = nullptr;
if(tmpTimer.player) {
target = GetSpawnByID(tmpTimer.player);
}
if(callSpawn) {
if(!callSpawn->IsPlayer()) {
CallSpawnScript(GetSpawnByID(tmpTimer.spawn), SPAWN_SCRIPT_TIMER, target, tmpTimer.function.c_str());
}
else {
const char* playerScript = world.GetPlayerScript(0); // 0 = global script
const char* playerZoneScript = world.GetPlayerScript(GetZoneID()); // zone script
if(playerScript || playerZoneScript) {
std::vector<LuaArg> args = {
LuaArg(this),
LuaArg(callSpawn),
LuaArg(target)
};
if(playerScript) {
lua_interface->RunPlayerScriptWithReturn(playerScript, tmpTimer.function.c_str(), args);
}
if(playerZoneScript) {
lua_interface->RunPlayerScriptWithReturn(playerZoneScript, tmpTimer.function.c_str(), args);
}
}
}
}
} }
} }
} }
@ -4714,11 +4744,24 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
client = *client_itr; client = *client_itr;
if (client && (client->GetVersion() > 373 || !client->IsZoning() || client->GetPlayer() != spawn)) { //don't send destroy ghost of 283 client when zoning if (client && (!client->IsZoning() || client->GetPlayer() != spawn)) {
if (client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn) if (client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn)
client->GetPlayer()->SetTarget(0); client->GetPlayer()->SetTarget(0);
if(client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID())) if(client->GetMailTransaction() == spawn)
client->SetMailTransaction(0);
if(client->GetBanker() == spawn->GetID())
client->SetBanker(0);
if(client->GetTransportSpawnID() == spawn->GetID())
client->SetTransportSpawnID(0);
if(client->GetTempPlacementSpawn() == spawn)
client->SetTempPlacementSpawn(nullptr);
if(client->GetCombineSpawn() == spawn)
client->SetCombineSpawn(nullptr);
//don't send destroy ghost of 283 client when zoning
if((client->GetVersion() > 373 || !client->IsZoning()) && (client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID())))
SendRemoveSpawn(client, spawn, packet, delete_spawn); SendRemoveSpawn(client, spawn, packet, delete_spawn);
if (spawn_range_map.count(client) > 0) if (spawn_range_map.count(client) > 0)
spawn_range_map.Get(client)->erase(spawn->GetID()); spawn_range_map.Get(client)->erase(spawn->GetID());
} }
@ -5375,6 +5418,29 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
((NPC*)dead)->Brain()->ClearHate(); ((NPC*)dead)->Brain()->ClearHate();
safe_delete(encounter); safe_delete(encounter);
const char* functionToCall = "on_player_death";
bool isPlayerDead = true;
if(dead->IsPlayer() || (killer && killer->IsPlayer() && (isPlayerDead = false))) {
if(!isPlayerDead) {
functionToCall = "on_player_kill";
}
const char* playerScript = world.GetPlayerScript(0); // 0 = global script
const char* playerZoneScript = world.GetPlayerScript(GetZoneID()); // zone script
if(playerScript || playerZoneScript) {
std::vector<LuaArg> args = {
LuaArg(this),
LuaArg(dead),
LuaArg(killer)
};
if(playerScript) {
lua_interface->RunPlayerScriptWithReturn(playerScript, functionToCall, args);
}
if(playerZoneScript) {
lua_interface->RunPlayerScriptWithReturn(playerZoneScript, functionToCall, args);
}
}
}
} }
void ZoneServer::SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, int8 type2, int8 damage_type, int16 damage, const char* spell_name) { void ZoneServer::SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, int8 type2, int8 damage_type, int16 damage, const char* spell_name) {
@ -8658,6 +8724,7 @@ void ZoneServer::DeleteGlobalSpawns() {
ClearEntityCommands(); ClearEntityCommands();
DeleteGroundSpawnItems(); DeleteGroundSpawnItems();
DeleteTransporters();
DeleteGlobalTransporters(); DeleteGlobalTransporters();
DeleteTransporterMaps(); DeleteTransporterMaps();
} }

View File

@ -38,11 +38,11 @@
#endif #endif
#if defined(LOGIN) #if defined(LOGIN)
#define CURRENT_VERSION "0.9.8-thetascorpii-DR1" #define CURRENT_VERSION "0.9.9-Nebula"
#elif defined(WORLD) #elif defined(WORLD)
#define CURRENT_VERSION "0.9.8-thetascorpii-DR1" #define CURRENT_VERSION "0.9.9-Nebula"
#else #else
#define CURRENT_VERSION "0.9.8-thetascorpii-DR1" #define CURRENT_VERSION "0.9.9-Nebula"
#endif #endif
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__