diff options
author | silinoron <none@none> | 2010-09-05 13:12:24 -0700 |
---|---|---|
committer | silinoron <none@none> | 2010-09-05 13:12:24 -0700 |
commit | be7d94f24e77d3c5e98fd8790c810bb2f95327e9 (patch) | |
tree | 21916cced4665501c3d2bdcdddeca91c15dbacfe /src | |
parent | de59ac3bc36ebdd9cd87f6f12d640f5fc12ded5e (diff) |
Add support for character race and faction transfers.
Based (loosely) on patch by Ner'zhul, DarkXuan, and darkshines@sakha.net.
Fixes issue #3429.
--HG--
branch : trunk
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Chat/Chat.cpp | 18 | ||||
-rw-r--r-- | src/server/game/Chat/Chat.h | 2 | ||||
-rw-r--r-- | src/server/game/Chat/Commands/Level2.cpp | 55 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 17 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 3 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 11 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 180 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 12 | ||||
-rw-r--r-- | src/server/game/Miscellaneous/SharedDefines.h | 6 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp | 317 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 1 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 12 |
14 files changed, 627 insertions, 13 deletions
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 581145a1e5f..c41b34d817d 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -131,14 +131,16 @@ ChatCommand * ChatHandler::getCommandTable() static ChatCommand characterCommandTable[] = { - { "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL }, - { "deleted", SEC_GAMEMASTER, true, NULL, "", characterDeletedCommandTable}, - { "erase", SEC_CONSOLE, true, &ChatHandler::HandleCharacterEraseCommand, "", NULL }, - { "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterLevelCommand, "", NULL }, - { "rename", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterRenameCommand, "", NULL }, - { "reputation", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterReputationCommand, "", NULL }, - { "titles", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterTitlesCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } + { "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL }, + { "changefaction", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterChangeFactionCommand, "", NULL }, + { "changerace", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterChangeRaceCommand, "", NULL }, + { "deleted", SEC_GAMEMASTER, true, NULL, "", characterDeletedCommandTable}, + { "erase", SEC_CONSOLE, true, &ChatHandler::HandleCharacterEraseCommand, "", NULL }, + { "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterLevelCommand, "", NULL }, + { "rename", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterRenameCommand, "", NULL }, + { "reputation", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterReputationCommand, "", NULL }, + { "titles", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterTitlesCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } }; static ChatCommand channelSetCommandTable[] = diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index b966a76c622..052d3b66c35 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -138,6 +138,8 @@ class ChatHandler bool HandleCastTargetCommand(const char *args); bool HandleCharacterCustomizeCommand(const char * args); + bool HandleCharacterChangeFactionCommand(const char * args); + bool HandleCharacterChangeRaceCommand(const char * args); bool HandleCharacterDeletedDeleteCommand(const char* args); bool HandleCharacterDeletedListCommand(const char* args); bool HandleCharacterDeletedRestoreCommand(const char* args); diff --git a/src/server/game/Chat/Commands/Level2.cpp b/src/server/game/Chat/Commands/Level2.cpp index 763726e79ae..838d3e861d9 100644 --- a/src/server/game/Chat/Commands/Level2.cpp +++ b/src/server/game/Chat/Commands/Level2.cpp @@ -3254,6 +3254,61 @@ bool ChatHandler::HandleCharacterCustomizeCommand(const char* args) return true; } +bool ChatHandler::HandleCharacterChangeFactionCommand(const char * args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + + if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + if(target) + { + // TODO : add text into database + PSendSysMessage(LANG_CUSTOMIZE_PLAYER, GetNameLink(target).c_str()); + target->SetAtLoginFlag(AT_LOGIN_CHANGE_FACTION); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '64' WHERE guid = %u", target->GetGUIDLow()); + } + else + { + std::string oldNameLink = playerLink(target_name); + + // TODO : add text into database + PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(target_guid)); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '64' WHERE guid = %u", GUID_LOPART(target_guid)); + } + + return true; +} + +bool ChatHandler::HandleCharacterChangeRaceCommand(const char * args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + if(target) + { + // TODO : add text into database + PSendSysMessage(LANG_CUSTOMIZE_PLAYER, GetNameLink(target).c_str()); + target->SetAtLoginFlag(AT_LOGIN_CHANGE_RACE); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '128' WHERE guid = %u", target->GetGUIDLow()); + } + else + { + std::string oldNameLink = playerLink(target_name); + + // TODO : add text into database + PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(target_guid)); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '128' WHERE guid = %u", GUID_LOPART(target_guid)); + } + + return true; +} + bool ChatHandler::HandleCharacterReputationCommand(const char* args) { Player* target; diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index c390f2203e2..8f772431d4c 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -170,6 +170,9 @@ static uint32 sTalentTabPages[MAX_CLASSES][3]; DBCStorage <TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt); TaxiMask sTaxiNodesMask; TaxiMask sOldContinentsNodesMask; +TaxiMask sHordeTaxiNodesMask; +TaxiMask sAllianceTaxiNodesMask; +TaxiMask sDeathKnightTaxiNodesMask; // DBC used only for initialization sTaxiPathSetBySource at startup. TaxiPathSetBySource sTaxiPathSetBySource; @@ -515,11 +518,14 @@ void LoadDBCStores(const std::string& dataPath) for (uint32 i = 1; i < sSpellStore.GetNumRows (); ++i) if (SpellEntry const* sInfo = sSpellStore.LookupEntry (i)) for (int j=0; j < 3; ++j) - if (sInfo->Effect[j] == 123 /*SPELL_EFFECT_SEND_TAXI*/) + if (sInfo->Effect[j] == SPELL_EFFECT_SEND_TAXI) spellPaths.insert(sInfo->EffectMiscValue[j]); memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask)); - memset(sOldContinentsNodesMask,0,sizeof(sTaxiNodesMask)); + memset(sOldContinentsNodesMask,0,sizeof(sOldContinentsNodesMask)); + memset(sHordeTaxiNodesMask,0,sizeof(sHordeTaxiNodesMask)); + memset(sAllianceTaxiNodesMask,0,sizeof(sAllianceTaxiNodesMask)); + memset(sDeathKnightTaxiNodesMask,0,sizeof(sDeathKnightTaxiNodesMask)); for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) { TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i); @@ -549,6 +555,13 @@ void LoadDBCStores(const std::string& dataPath) uint32 submask = 1<<((i-1)%32); sTaxiNodesMask[field] |= submask; + if (node->MountCreatureID[0] && node->MountCreatureID[0] != 32981) + sHordeTaxiNodesMask[field] |= submask; + if (node->MountCreatureID[1] && node->MountCreatureID[1] != 32981) + sAllianceTaxiNodesMask[field] |= submask; + if (node->MountCreatureID[0] == 32981 || node->MountCreatureID[1] == 32981) + sDeathKnightTaxiNodesMask[field] |= submask; + // old continent node (+ nodes virtually at old continents, check explicitly to avoid loading map files for zone info) if (node->map_id < 2 || i == 82 || i == 83 || i == 93 || i == 94) sOldContinentsNodesMask[field] |= submask; diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index bfe38f29926..49042e398cd 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -153,6 +153,9 @@ extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore; extern DBCStorage <TaxiPathEntry> sTaxiPathStore; extern TaxiMask sTaxiNodesMask; extern TaxiMask sOldContinentsNodesMask; +extern TaxiMask sHordeTaxiNodesMask; +extern TaxiMask sAllianceTaxiNodesMask; +extern TaxiMask sDeathKnightTaxiNodesMask; extern TaxiPathSetBySource sTaxiPathSetBySource; extern TaxiPathNodesByPath sTaxiPathNodesByPath; extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 1d7458dae4d..6146224b7b9 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1654,8 +1654,17 @@ bool Player::BuildEnumData(QueryResult_AutoPtr result, WorldPacket * p_data) char_flags |= CHARACTER_FLAG_DECLINED; *p_data << uint32(char_flags); // character flags + // character customize flags - *p_data << uint32(atLoginFlags & AT_LOGIN_CUSTOMIZE ? CHAR_CUSTOMIZE_FLAG_CUSTOMIZE : CHAR_CUSTOMIZE_FLAG_NONE); + if (atLoginFlags & AT_LOGIN_CUSTOMIZE) + *p_data << uint32(CHAR_CUSTOMIZE_FLAG_CUSTOMIZE); + else if (atLoginFlags & AT_LOGIN_CHANGE_FACTION) + *p_data << uint32(CHAR_CUSTOMIZE_FLAG_FACTION); + else if (atLoginFlags & AT_LOGIN_CHANGE_RACE) + *p_data << uint32(CHAR_CUSTOMIZE_FLAG_RACE); + else + *p_data << uint32(CHAR_CUSTOMIZE_FLAG_NONE); + // First login *p_data << uint8(atLoginFlags & AT_LOGIN_FIRST ? 1 : 0); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index f8d6535d5b0..de8aedf5de1 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -501,6 +501,8 @@ enum AtLoginFlags AT_LOGIN_CUSTOMIZE = 0x08, AT_LOGIN_RESET_PET_TALENTS = 0x10, AT_LOGIN_FIRST = 0x20, + AT_LOGIN_CHANGE_FACTION = 0x40, + AT_LOGIN_CHANGE_RACE = 0x80 }; typedef std::map<uint32, QuestStatusData> QuestStatusMap; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 44a6b110759..8ce16622f6a 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8931,3 +8931,183 @@ void ObjectMgr::LoadCreatureClassLevelStats() sLog.outString(); sLog.outString(">> Loaded %u creature base stats.", counter); } + +void ObjectMgr::LoadFactionChangeAchievements() +{ + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + uint32 counter = 0; + + do + { + Field *fields = result->Fetch(); + + uint32 alliance = fields[0].GetUInt32(); + uint32 horde = fields[1].GetUInt32(); + + if (!sAchievementStore.LookupEntry(alliance)) + { + sLog.outErrorDb("Achievement %u referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance); + } + else if (!sAchievementStore.LookupEntry(horde)) + { + sLog.outErrorDb("Achievement %u referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde); + } + else + { + factionchange_achievements[alliance] = horde; + } + + bar.step(); + ++counter; + } + while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u faction change achievement pairs.", counter); +} + +void ObjectMgr::LoadFactionChangeItems() +{ + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + uint32 counter = 0; + + do + { + Field *fields = result->Fetch(); + + uint32 alliance = fields[0].GetUInt32(); + uint32 horde = fields[1].GetUInt32(); + + if (!GetItemPrototype(alliance)) + { + sLog.outErrorDb("Item %u referenced in `player_factionchange_items` does not exist, pair skipped!", alliance); + } + else if (!GetItemPrototype(horde)) + { + sLog.outErrorDb("Item %u referenced in `player_factionchange_items` does not exist, pair skipped!", horde); + } + else + { + factionchange_items[alliance] = horde; + } + + bar.step(); + ++counter; + } + while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u faction change item pairs.", counter); +} + +void ObjectMgr::LoadFactionChangeSpells() +{ + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + uint32 counter = 0; + + do + { + Field *fields = result->Fetch(); + + uint32 alliance = fields[0].GetUInt32(); + uint32 horde = fields[1].GetUInt32(); + + if (!sSpellStore.LookupEntry(alliance)) + { + sLog.outErrorDb("Spell %u referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance); + } + else if (!sSpellStore.LookupEntry(horde)) + { + sLog.outErrorDb("Spell %u referenced in `player_factionchange_spells` does not exist, pair skipped!", horde); + } + else + { + factionchange_spells[alliance] = horde; + } + + bar.step(); + ++counter; + } + while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u faction change spell pairs.", counter); +} + +void ObjectMgr::LoadFactionChangeReputations() +{ + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + uint32 counter = 0; + + do + { + Field *fields = result->Fetch(); + + uint32 alliance = fields[0].GetUInt32(); + uint32 horde = fields[1].GetUInt32(); + + if (!sFactionStore.LookupEntry(alliance)) + { + sLog.outErrorDb("Reputation %u referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance); + } + else if (!sFactionStore.LookupEntry(horde)) + { + sLog.outErrorDb("Reputation %u referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde); + } + else + { + factionchange_reputations[alliance] = horde; + } + + bar.step(); + ++counter; + } + while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u faction change reputation pairs.", counter); +} diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 26009e60a02..10865250d74 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -390,6 +390,8 @@ class ObjectMgr typedef std::vector<std::string> ScriptNameMap; + typedef std::map<uint32, uint32> CharacterConversionMap; + Player* GetPlayer(const char* name) const { return sObjectAccessor.FindPlayerByName(name);} Player* GetPlayer(uint64 guid) const { return ObjectAccessor::FindPlayer(guid); } @@ -961,6 +963,16 @@ class ObjectMgr value = data[loc_idx]; } + CharacterConversionMap factionchange_achievements; + CharacterConversionMap factionchange_items; + CharacterConversionMap factionchange_spells; + CharacterConversionMap factionchange_reputations; + + void LoadFactionChangeAchievements(); + void LoadFactionChangeItems(); + void LoadFactionChangeSpells(); + void LoadFactionChangeReputations(); + protected: // first free id for selected id type diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index dc97eeb1e4a..38116e7e41a 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -87,6 +87,12 @@ enum Races (1<<(RACE_GNOME-1)) |(1<<(RACE_TROLL-1)) |(1<<(RACE_BLOODELF-1))| \ (1<<(RACE_DRAENEI-1))) +#define RACEMASK_ALLIANCE \ + ((1<<(RACE_HUMAN-1)) | (1<<(RACE_DWARF-1)) | (1<<(RACE_NIGHTELF-1)) | \ + (1<<(RACE_GNOME-1)) | (1<<(RACE_DRAENEI-1))) + +#define RACEMASK_HORDE RACEMASK_ALL_PLAYABLE & ~RACEMASK_ALLIANCE + // Class value is index in ChrClasses.dbc enum Classes { diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp index e8b50a073f3..0d25742fff1 100644 --- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp @@ -42,6 +42,7 @@ #include "UpdateMask.h" #include "Util.h" #include "ScriptMgr.h" +#include "Battleground.h" class LoginQueryHolder : public SQLQueryHolder { @@ -1327,3 +1328,319 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data) data << uint8(0); // 4 - equipment swap failed - inventory is full SendPacket(&data); } + +void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recv_data) +{ + uint64 guid; + std::string newname; + uint8 gender, skin, face, hairStyle, hairColor, facialHair, race; + recv_data >> guid; + recv_data >> newname; + recv_data >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face >> race; + + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT class, level, at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid)); + if (!result) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(CHAR_CREATE_ERROR); + SendPacket( &data ); + return; + } + + Field *fields = result->Fetch(); + uint32 playerClass = fields[0].GetUInt32(); + uint32 level = fields[1].GetUInt32(); + uint32 at_loginFlags = fields[2].GetUInt32(); + uint32 used_loginFlag = ((recv_data.GetOpcode() == CMSG_CHAR_RACE_CHANGE) ? AT_LOGIN_CHANGE_RACE : AT_LOGIN_CHANGE_FACTION); + + if (!sObjectMgr.GetPlayerInfo(race, playerClass)) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(CHAR_CREATE_ERROR); + SendPacket( &data ); + return; + } + + if (!(at_loginFlags & used_loginFlag)) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(CHAR_CREATE_ERROR); + SendPacket( &data ); + return; + } + + // prevent character rename to invalid name + if (!normalizePlayerName(newname)) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(CHAR_NAME_NO_NAME); + SendPacket( &data ); + return; + } + + uint8 res = ObjectMgr::CheckPlayerName(newname,true); + if (res != CHAR_NAME_SUCCESS) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(res); + SendPacket( &data ); + return; + } + + // check name limitations + if (GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(newname)) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(CHAR_NAME_RESERVED); + SendPacket( &data ); + return; + } + + // character with this name already exist + if (uint64 newguid = sObjectMgr.GetPlayerGUIDByName(newname)) + { + if (newguid != guid) + { + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); + data << uint8(CHAR_CREATE_NAME_IN_USE); + SendPacket( &data ); + return; + } + } + + CharacterDatabase.escape_string(newname); + Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + trans->PAppend("UPDATE `characters` SET name = '%s', race = '%u', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), race, uint32(used_loginFlag), GUID_LOPART(guid)); + trans->PAppend("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid)); + + BattlegroundTeamId team; + + // Search each faction is targeted + switch(race) + { + case RACE_ORC: + case RACE_TAUREN: + case RACE_UNDEAD_PLAYER: + case RACE_TROLL: + case RACE_BLOODELF: + team = BG_TEAM_HORDE; + break; + default: + team = BG_TEAM_ALLIANCE; + break; + } + + // Switch Languages + // delete all languages first + trans->PAppend("DELETE FROM `character_skills` WHERE `skill` IN (98, 113, 759, 111, 313, 109, 115, 315, 673, 137) AND `guid`='%u'", GUID_LOPART(guid)); + + // now add them back + if (team == BG_TEAM_ALLIANCE) + { + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 98, 300, 300)", GUID_LOPART(guid)); + switch (race) + { + case RACE_DWARF: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 111, 300, 300)", GUID_LOPART(guid)); + break; + case RACE_DRAENEI: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 759, 300, 300)", GUID_LOPART(guid)); + break; + case RACE_GNOME: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 313, 300, 300)", GUID_LOPART(guid)); + break; + case RACE_NIGHTELF: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 113, 300, 300)", GUID_LOPART(guid)); + break; + } + } + else if (team == BG_TEAM_HORDE) + { + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 109, 300, 300)", GUID_LOPART(guid)); + switch (race) + { + case RACE_UNDEAD_PLAYER: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 673, 300, 300)", GUID_LOPART(guid)); + break; + case RACE_TAUREN: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 115, 300, 300)", GUID_LOPART(guid)); + break; + case RACE_TROLL: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 315, 300, 300)", GUID_LOPART(guid)); + break; + case RACE_BLOODELF: + trans->PAppend("INSERT INTO `character_skills` (guid, skill, value, max) VALUES (%u, 137, 300, 300)", GUID_LOPART(guid)); + break; + } + } + + if(recv_data.GetOpcode() == CMSG_CHAR_FACTION_CHANGE) + { + // Delete all Flypaths + trans->PAppend("UPDATE `characters` SET taxi_path = '' WHERE guid ='%u'",GUID_LOPART(guid)); + + if (level > 7) + { + // Update Taxi path + // this doesn't seem to be 100% blizzlike... but it can't really be helped. + std::ostringstream taximaskstream; + uint32 numFullTaximasks = level / 7; + if (numFullTaximasks > 11) + numFullTaximasks = 11; + if (team == BG_TEAM_ALLIANCE) + { + if (playerClass != CLASS_DEATH_KNIGHT) + { + for (uint8 i = 0; i < numFullTaximasks; ++i) + taximaskstream << uint32(sAllianceTaxiNodesMask[i]) << " "; + } + else + { + for (uint8 i = 0; i < numFullTaximasks; ++i) + taximaskstream << uint32(sAllianceTaxiNodesMask[i] | sDeathKnightTaxiNodesMask[i]) << " "; + } + } + else + { + if (playerClass != CLASS_DEATH_KNIGHT) + { + for (uint8 i = 0; i < numFullTaximasks; ++i) + taximaskstream << uint32(sHordeTaxiNodesMask[i]) << " "; + } + else + { + for (uint8 i = 0; i < numFullTaximasks; ++i) + taximaskstream << uint32(sHordeTaxiNodesMask[i] | sDeathKnightTaxiNodesMask[i]) << " "; + } + } + + uint32 numEmptyTaximasks = 11 - numFullTaximasks; + for (uint8 i = 0; i < numEmptyTaximasks; ++i) + taximaskstream << "0 "; + taximaskstream << "0"; + std::string taximask = taximaskstream.str(); + trans->PAppend("UPDATE `characters` SET `taximask`= '%s' WHERE `guid` = '%u'", taximask.c_str(), GUID_LOPART(guid)); + } + + // Delete all current quests + trans->PAppend("DELETE FROM `character_queststatus` WHERE `status` = 3 AND guid ='%u'",GUID_LOPART(guid)); + + // Delete record of the faction old completed quests + { + std::ostringstream quests; + ObjectMgr::QuestMap const& qTemplates = sObjectMgr.GetQuestTemplates(); + for (ObjectMgr::QuestMap::const_iterator iter = qTemplates.begin(); iter != qTemplates.end(); ++iter) + { + Quest *qinfo = iter->second; + uint32 requiredRaces = qinfo->GetRequiredRaces(); + if (team == BG_TEAM_ALLIANCE) + { + if (requiredRaces & RACEMASK_ALLIANCE) + { + quests << uint32(qinfo->GetQuestId()); + quests << ","; + } + } + else // if (team == BG_TEAM_HORDE) + { + if (requiredRaces & RACEMASK_HORDE) + { + quests << uint32(qinfo->GetQuestId()); + quests << ","; + } + } + } + + std::string questsStr = quests.str(); + questsStr = questsStr.substr(0, questsStr.length() - 1); + + trans->PAppend("DELETE FROM `character_queststatus` WHERE guid= '%u' AND quest IN (%s)",GUID_LOPART(guid),questsStr.c_str()); + } + + if (!sWorld.getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + { + // Reset guild + trans->PAppend("DELETE FROM `guild_member` WHERE `guid`= '%u'",GUID_LOPART(guid)); + } + + if (!sWorld.getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND)) + { + // Delete Friend List + trans->PAppend("DELETE FROM `character_social` WHERE `guid`= '%u'",GUID_LOPART(guid)); + trans->PAppend("DELETE FROM `character_social` WHERE `friend`= '%u'",GUID_LOPART(guid)); + } + + // Leave Arena Teams + Player::LeaveAllArenaTeams(GUID_LOPART(guid)); + + // Reset homebind and position + trans->PAppend("DELETE FROM `character_homebind` WHERE guid = '%u'",GUID_LOPART(guid)); + if(team == BG_TEAM_ALLIANCE) + { + trans->PAppend("INSERT INTO `character_homebind` VALUES (%u,0,1519,-8867.68,673.373,97.9034)",GUID_LOPART(guid)); + Player::SavePositionInDB(0, -8867.68f, 673.373f, 97.9034f, 0.0f, 1519, GUID_LOPART(guid)); + } + else + { + trans->PAppend("INSERT INTO `character_homebind` VALUES (%u,1,1637,1633.33,-4439.11,15.7588)",GUID_LOPART(guid)); + Player::SavePositionInDB(1, 1633.33f, -4439.11f, 15.7588f, 0.0f, 1637, GUID_LOPART(guid)); + } + + // Achievement conversion + for (std::map<uint32, uint32>::const_iterator it = sObjectMgr.factionchange_achievements.begin(); it != sObjectMgr.factionchange_achievements.end(); ++it) + { + uint32 achiev_alliance = it->first; + uint32 achiev_horde = it->second; + trans->PAppend("UPDATE `character_achievement` SET achievement = '%u' where achievement = '%u' AND guid = '%u'", + team == BG_TEAM_ALLIANCE ? achiev_alliance : achiev_horde, team == BG_TEAM_ALLIANCE ? achiev_horde : achiev_alliance, GUID_LOPART(guid)); + } + + // Item conversion + for (std::map<uint32, uint32>::const_iterator it = sObjectMgr.factionchange_items.begin(); it != sObjectMgr.factionchange_items.end(); ++it) + { + uint32 item_alliance = it->first; + uint32 item_horde = it->second; + trans->PAppend("UPDATE `character_inventory` SET item = '%u' where item = '%u' AND guid = '%u'", + team == BG_TEAM_ALLIANCE ? item_alliance : item_horde, team == BG_TEAM_ALLIANCE ? item_horde : item_alliance, guid); + } + + // Spell conversion + for (std::map<uint32, uint32>::const_iterator it = sObjectMgr.factionchange_spells.begin(); it != sObjectMgr.factionchange_spells.end(); ++it) + { + uint32 spell_alliance = it->first; + uint32 spell_horde = it->second; + trans->PAppend("UPDATE `character_spell` SET spell = '%u' where spell = '%u' AND guid = '%u'", + team == BG_TEAM_ALLIANCE ? spell_alliance : spell_horde, team == BG_TEAM_ALLIANCE ? spell_horde : spell_alliance, GUID_LOPART(guid)); + } + + // Reputation conversion + for (std::map<uint32, uint32>::const_iterator it = sObjectMgr.factionchange_reputations.begin(); it != sObjectMgr.factionchange_reputations.end(); ++it) + { + uint32 reputation_alliance = it->first; + uint32 reputation_horde = it->second; + trans->PAppend("DELETE FROM character_reputation WHERE faction = '%u' AND guid = '%u'", + team == BG_TEAM_ALLIANCE ? reputation_alliance : reputation_horde, GUID_LOPART(guid)); + trans->PAppend("UPDATE `character_reputation` SET faction = '%u' where faction = '%u' AND guid = '%u'", + team == BG_TEAM_ALLIANCE ? reputation_alliance : reputation_horde, team == BG_TEAM_ALLIANCE ? reputation_horde : reputation_alliance, GUID_LOPART(guid)); + } + } + + CharacterDatabase.CommitTransaction(trans); + + std::string IP_str = GetRemoteAddress(); + sLog.outDebug("Account: %d (IP: %s), Character guid: %u Change Race/Faction to: %s", GetAccountId(), IP_str.c_str(), GUID_LOPART(guid), newname.c_str()); + + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1 + 8 + (newname.size() + 1) + 1 + 1 + 1 + 1 + 1 + 1 + 1); + data << uint8(RESPONSE_SUCCESS); + data << uint64(guid); + data << newname; + data << uint8(gender); + data << uint8(skin); + data << uint8(face); + data << uint8(hairStyle); + data << uint8(hairColor); + data << uint8(facialHair); + data << uint8(race); + SendPacket(&data); +} diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 74fe40a3b6f..347bb93290b 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -1269,7 +1269,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x4D6*/ { "SMSG_EQUIPMENT_SET_USE_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x4D7*/ { "UMSG_UNKNOWN_1239", STATUS_NEVER, &WorldSession::Handle_NULL }, /*0x4D8*/ { "SMSG_UNKNOWN_1240", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x4D9*/ { "CMSG_CHAR_FACTION_CHANGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4D9*/ { "CMSG_CHAR_FACTION_CHANGE", STATUS_AUTHED, &WorldSession::HandleCharFactionOrRaceChange }, /*0x4DA*/ { "SMSG_CHAR_FACTION_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x4DB*/ { "UMSG_UNKNOWN_1243", STATUS_NEVER, &WorldSession::Handle_NULL }, /*0x4DC*/ { "UMSG_UNKNOWN_1244", STATUS_NEVER, &WorldSession::Handle_NULL }, @@ -1300,7 +1300,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x4F5*/ { "UMSG_UNKNOWN_1269", STATUS_NEVER, &WorldSession::Handle_NULL }, /*0x4F6*/ { "CMSG_WORLD_STATE_UI_TIMER_UPDATE", STATUS_LOGGEDIN, &WorldSession::HandleWorldStateUITimerUpdate }, /*0x4F7*/ { "SMSG_WORLD_STATE_UI_TIMER_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x4F8*/ { "CMSG_CHAR_RACE_CHANGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4F8*/ { "CMSG_CHAR_RACE_CHANGE", STATUS_AUTHED, &WorldSession::HandleCharFactionOrRaceChange }, /*0x4F9*/ { "UMSG_UNKNOWN_1273", STATUS_NEVER, &WorldSession::Handle_NULL }, /*0x4FA*/ { "SMSG_TALENTS_INVOLUNTARILY_RESET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x4FB*/ { "UMSG_UNKNOWN_1275", STATUS_NEVER, &WorldSession::Handle_NULL }, diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 56b549e9658..01ffbe71245 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -323,6 +323,7 @@ class WorldSession void HandlePlayerLoginOpcode(WorldPacket& recvPacket); void HandleCharEnum(QueryResult_AutoPtr result); void HandlePlayerLogin(LoginQueryHolder * holder); + void HandleCharFactionOrRaceChange(WorldPacket& recv_data); // played time void HandlePlayedTime(WorldPacket& recvPacket); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3afe5bae580..0ee7c5ebe24 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1578,6 +1578,18 @@ void World::SetInitialWorldSettings() sLog.outString("Loading Conditions..."); sConditionMgr.LoadConditions(); + sLog.outString("Loading faction change achievement pairs..."); + sObjectMgr.LoadFactionChangeAchievements(); + + sLog.outString("Loading faction change spell pairs..."); + sObjectMgr.LoadFactionChangeSpells(); + + sLog.outString("Loading faction change item pairs..."); + sObjectMgr.LoadFactionChangeItems(); + + sLog.outString("Loading faction change reputation pairs..."); + sObjectMgr.LoadFactionChangeReputations(); + sLog.outString("Loading GM tickets..."); sTicketMgr.LoadGMTickets(); |