diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index ad02bd244..d96c5db25 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/EQ2/source/WorldServer/Commands/Commands.cpp @@ -8138,9 +8138,13 @@ void Commands::Command_Pet(Client* client, Seperator* sep) */ void Commands::Command_PetName(Client* client, Seperator* sep) { - PrintSep(sep, "COMMAND_PETNAME"); - LogWrite(MISC__TODO, 1, "Command", "TODO-Command: Pet Name Command"); - client->Message(CHANNEL_COLOR_YELLOW, "Pets are not yet implemented."); + if (sep && sep->arg[0]) { + const char* pet_name = sep->argplus[0]; + client->SetPetName(pet_name); + } + else { + client->GetPlayer()->GetInfoStruct()->set_pet_name(""); + } } /* diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index 7c02ab6da..b77908e92 100755 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/EQ2/source/WorldServer/LuaFunctions.cpp @@ -5272,18 +5272,24 @@ int EQ2Emu_lua_SummonPet(lua_State* state) { spawn->GetZone()->CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN); } - // Get a random pet name - string random_pet_name; - int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1); - random_pet_name = spawn->GetZone()->pet_names.at(rand_index); - LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", random_pet_name.c_str(), rand_index); + + std::string petName = std::string(""); + if(spawn->IsEntity()) { + petName = ((Entity*)spawn)->GetInfoStruct()->get_pet_name(); + } + + if(petName.size() < 1) { + int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1); + petName = spawn->GetZone()->pet_names.at(rand_index); + LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", petName.c_str(), rand_index); + } // If player set various values for the char sheet (pet window) if (spawn->IsPlayer()) { Player* player = (Player*)spawn; player->GetInfoStruct()->set_pet_id(player->GetIDWithPlayerSpawn(pet)); - player->GetInfoStruct()->set_pet_name(random_pet_name); + player->GetInfoStruct()->set_pet_name(petName); player->GetInfoStruct()->set_pet_movement(2); player->GetInfoStruct()->set_pet_behavior(3); player->GetInfoStruct()->set_pet_health_pct(1.0f); @@ -5293,7 +5299,7 @@ int EQ2Emu_lua_SummonPet(lua_State* state) { } // Set the pets name - pet->SetName(random_pet_name.c_str()); + pet->SetName(petName.c_str()); // Set the level of the pet to the owners level or max level(if set) if owners level is greater if (max_level > 0) pet->SetLevel(spawn->GetLevel() >= max_level ? max_level : spawn->GetLevel()); @@ -7875,14 +7881,20 @@ int EQ2Emu_lua_SummonDumbFirePet(lua_State* state) { spawn->GetZone()->CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN); } - // Get a random pet name - string random_pet_name; - int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1); - random_pet_name = spawn->GetZone()->pet_names.at(rand_index); - LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", random_pet_name.c_str(), rand_index); - + std::string petName = std::string(""); + if(spawn->IsEntity()) { + petName = ((Entity*)spawn)->GetInfoStruct()->get_pet_name(); + } + + if(petName.size() < 1) { + int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1); + petName = spawn->GetZone()->pet_names.at(rand_index); + LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", petName.c_str(), rand_index); + } + // Set the pets name - pet->SetName(random_pet_name.c_str()); + pet->SetName(petName.c_str()); + // Set the level of the pet to the owners level pet->SetLevel(spawn->GetLevel()); // Set the faction of the pet to the same faction as the owner diff --git a/EQ2/source/WorldServer/Player.cpp b/EQ2/source/WorldServer/Player.cpp index 0038489a2..3605305a1 100644 --- a/EQ2/source/WorldServer/Player.cpp +++ b/EQ2/source/WorldServer/Player.cpp @@ -441,9 +441,15 @@ PacketStruct* PlayerInfo::serialize2(int16 version){ packet->setDataByName("coins_plat", info_struct->get_coin_plat()); packet->setDataByName("weight", info_struct->get_weight()); packet->setDataByName("max_weight", info_struct->get_max_weight()); - char pet_name[32]; - strncpy(pet_name, info_struct->get_pet_name().c_str(), 32); - packet->setDataByName("pet_name", pet_name); + + if(info_struct->get_pet_id() != 0xFFFFFFFF) { + char pet_name[32]; + strncpy(pet_name, info_struct->get_pet_name().c_str(), 32); + packet->setDataByName("pet_name", pet_name); + } + else { + packet->setDataByName("pet_name", "No Pet"); + } packet->setDataByName("status_points", info_struct->get_status_points()); if(bind_zone_id > 0){ string bind_name = database.GetZoneName(bind_zone_id); @@ -1220,10 +1226,16 @@ EQ2Packet* PlayerInfo::serializePet(int16 version) { packet->setDataByName("spawn_id", info_struct->get_pet_id()); packet->setDataByName("spawn_id2", info_struct->get_pet_id()); - char pet_name[32]; - strncpy(pet_name, info_struct->get_pet_name().c_str(), 32); - packet->setDataByName("name", pet_name); - packet->setDataByName("no_pet", pet_name); + + if(info_struct->get_pet_id() != 0xFFFFFFFF) { + char pet_name[32]; + strncpy(pet_name, info_struct->get_pet_name().c_str(), 32); + packet->setDataByName("name", pet_name); + } + else { + packet->setDataByName("name", "No Pet"); + packet->setDataByName("no_pet", "No Pet"); + } if (version >= 57000) { packet->setDataByName("current_power3", pet->GetPower()); @@ -5851,7 +5863,6 @@ void Player::RemoveAllPassives() void Player::ResetPetInfo() { GetInfoStruct()->set_pet_id(0xFFFFFFFF); - GetInfoStruct()->set_pet_name(std::string("No Pet")); GetInfoStruct()->set_pet_movement(0); GetInfoStruct()->set_pet_behavior(0); GetInfoStruct()->set_pet_health_pct(0.0f); diff --git a/EQ2/source/WorldServer/WorldDatabase.cpp b/EQ2/source/WorldServer/WorldDatabase.cpp index bf6e06225..c48c7246a 100644 --- a/EQ2/source/WorldServer/WorldDatabase.cpp +++ b/EQ2/source/WorldServer/WorldDatabase.cpp @@ -24,6 +24,10 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. #include <iomanip> #include <ios> #include <assert.h> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string.hpp> + + #include "WorldDatabase.h" #include "../common/debug.h" #include "../common/packet_dump.h" @@ -49,6 +53,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. #include "SpellProcess.h" #include "races.h" + extern Classes classes; extern Commands commands; extern MasterTitlesList master_titles_list; @@ -1648,7 +1653,6 @@ bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* clien info->set_coin_silver(result.GetInt32Str("coin_silver")); info->set_coin_gold(result.GetInt32Str("coin_gold")); info->set_coin_plat(result.GetInt32Str("coin_plat")); - info->set_pet_name(result.GetStringStr("pet_name") ? std::string(result.GetStringStr("pet_name")) : std::string("")); const char* bio = result.GetStringStr("biography"); if(bio && strlen(bio) > 0) info->set_biography(std::string(bio)); @@ -1710,6 +1714,13 @@ bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* clien client->GetPlayer()->SetTotalDissonanceBase(client->GetPlayer()->GetTotalDissonance()); client->GetPlayer()->SetCurrentLanguage(result.GetInt32Str("current_language")); + + std::string petName = result.GetStringStr("pet_name"); + boost::algorithm::to_lower(petName); + + if(petName != "no pet") { // some reason we default to "no pet", the code handles an empty string as setting a random pet name when its summoned + client->GetPlayer()->GetInfoStruct()->set_pet_name(result.GetStringStr("pet_name")); + } } return true; @@ -2453,9 +2464,9 @@ int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){ return char_id; } -int8 WorldDatabase::CheckNameFilter(const char* name) { +int8 WorldDatabase::CheckNameFilter(const char* name, int8 min_length, int8 max_length) { // the minimum 4 is enforced by the client too - if(!name || strlen(name) < 4 || strlen(name) > 15) // Even 20 char length is long... + if(!name || strlen(name) < min_length || strlen(name) > max_length) // Even 20 char length is long... return BADNAMELENGTH_REPLY; uchar* checkname = (uchar*)name; for (int32 i = 0; i < strlen(name); i++) @@ -4125,13 +4136,13 @@ void WorldDatabase::Save(Client* client){ if(client->GetCurrentZone()) zone_id = client->GetCurrentZone()->GetZoneID(); query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetCharacterID()); - query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u", + query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i, pet_name = '%s' where char_id = %u", player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(), player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(), player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), player->GetStatusPoints(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), client->GetPlayer()->GetCombatVoice(), client->GetPlayer()->GetEmoteVoice(), getSafeEscapeString(client->GetPlayer()->GetBiography().c_str()).c_str(), player->GetFlags(), player->GetFlags2(), client->GetPlayer()->GetLastName(), client->GetPlayer()->GetAssignedAA(), client->GetPlayer()->GetUnassignedAA(), client->GetPlayer()->GetTradeskillAA(), client->GetPlayer()->GetUnassignedTradeskillAA(), client->GetPlayer()->GetPrestigeAA(), - client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetCharacterID()); + client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetPlayer()->GetInfoStruct()->get_pet_name().c_str(), client->GetCharacterID()); map<string, int8>::iterator itr; map<string, int8>* friends = player->GetFriends(); if(friends && friends->size() > 0){ diff --git a/EQ2/source/WorldServer/WorldDatabase.h b/EQ2/source/WorldServer/WorldDatabase.h index 305b3560d..1894a0991 100644 --- a/EQ2/source/WorldServer/WorldDatabase.h +++ b/EQ2/source/WorldServer/WorldDatabase.h @@ -357,7 +357,7 @@ public: int32 GetMaxHotBarID(); - int8 CheckNameFilter(const char* name); + int8 CheckNameFilter(const char* name, int8 min_length = 4, int8 max_length = 15); static int32 NextUniqueHotbarID(){ next_id++; return next_id; diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index 8ec3a7fab..afacda144 100755 --- a/EQ2/source/WorldServer/client.cpp +++ b/EQ2/source/WorldServer/client.cpp @@ -39,6 +39,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>. #include "Chat/Chat.h" #include "SpellProcess.h" #include "Zone/ChestTrap.h" +#include "../../common/GlobalHeaders.h" //#include "Quests.h" @@ -2039,9 +2040,8 @@ bool Client::HandlePacket(EQApplicationPacket* app) { packet->LoadPacketData(app->pBuffer, app->size); string name = packet->getType_EQ2_16BitString_ByName("pet_name").data; - if (strlen(name.c_str()) != 0) { + if (strlen(name.c_str()) != 0 && SetPetName(name.c_str())) { target->SetName(name.c_str()); - player->GetInfoStruct()->set_pet_name(name); GetCurrentZone()->SendUpdateTitles(target); change = true; } @@ -11448,3 +11448,31 @@ void Client::HandleDialogSelectMsg(int32 conversation_id, int32 response_index) CloseDialog(conversation_id); } } + + +bool Client::SetPetName(const char* petName) { + int8 result = database.CheckNameFilter(petName,4,31); + if (result == BADNAMELENGTH_REPLY) { + SimpleMessage(CHANNEL_COLOR_YELLOW, "Name length is invalid, must be greater then 3 characters and less then 16."); + return false; + } + else if (result == NAMEINVALID_REPLY) { + SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is invalid, can only contain letters."); + return false; + } + else if (result == NAMETAKEN_REPLY) { + SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is already taken, please choose another."); + return false; + } + else if (result == NAMEFILTER_REPLY) { + SimpleMessage(CHANNEL_COLOR_YELLOW, "Name failed the filter check."); + return false; + } + else if (result == UNKNOWNERROR_REPLY) { + SimpleMessage(CHANNEL_COLOR_YELLOW, "Unknown error while checking the name."); + return false; + } + + GetPlayer()->GetInfoStruct()->set_pet_name(petName); + return true; +} \ No newline at end of file diff --git a/EQ2/source/WorldServer/client.h b/EQ2/source/WorldServer/client.h index 137f378af..0151b3a73 100644 --- a/EQ2/source/WorldServer/client.h +++ b/EQ2/source/WorldServer/client.h @@ -575,6 +575,7 @@ public: int32 GetPlayerPOVGhostSpawnID() { return pov_ghost_spawn_id; } void HandleDialogSelectMsg(int32 conversation_id, int32 response_index); + bool SetPetName(const char* name); private: void SavePlayerImages(); void SkillChanged(Skill* skill, int16 previous_value, int16 new_value);