aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsilinoron <none@none>2010-09-05 13:12:24 -0700
committersilinoron <none@none>2010-09-05 13:12:24 -0700
commitbe7d94f24e77d3c5e98fd8790c810bb2f95327e9 (patch)
tree21916cced4665501c3d2bdcdddeca91c15dbacfe
parentde59ac3bc36ebdd9cd87f6f12d640f5fc12ded5e (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
-rw-r--r--THANKS3
-rw-r--r--sql/base/world_database.sql100
-rw-r--r--sql/updates/9784_world_command.sql4
-rw-r--r--sql/updates/9784_world_player_factionchange_achievement.sql6
-rw-r--r--sql/updates/9784_world_player_factionchange_items.sql10
-rw-r--r--sql/updates/9784_world_player_factionchange_reputations.sql6
-rw-r--r--sql/updates/9784_world_player_factionchange_spells.sql6
-rw-r--r--src/server/game/Chat/Chat.cpp18
-rw-r--r--src/server/game/Chat/Chat.h2
-rw-r--r--src/server/game/Chat/Commands/Level2.cpp55
-rw-r--r--src/server/game/DataStores/DBCStores.cpp17
-rw-r--r--src/server/game/DataStores/DBCStores.h3
-rw-r--r--src/server/game/Entities/Player/Player.cpp11
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp180
-rw-r--r--src/server/game/Globals/ObjectMgr.h12
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h6
-rw-r--r--src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp317
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp4
-rw-r--r--src/server/game/Server/WorldSession.h1
-rw-r--r--src/server/game/World/World.cpp12
21 files changed, 760 insertions, 15 deletions
diff --git a/THANKS b/THANKS
index 19e310f8a52..78d4025ce09 100644
--- a/THANKS
+++ b/THANKS
@@ -71,4 +71,5 @@ BlackCat0110, JuliuSZS, n4rk0, filip.havlicek, m.ax, laviniu, LordJZ, Scazzato88
svannon, jurkovic.nikola, Willian Krueger, BioHazard, Ille000, Erocoloco,
terrorbringer, antihrists, Havenard, scarymovie87, D3VIL, FaTe753, PrinceCreed,
spgm, Dakeyras, sombre88, 19Maxx83, moriquendu, Ille, breakerfly,
-zthoreen, clement.roussel, p.alexej, Ceris, Nayre, Kiper, announce, thmarth \ No newline at end of file
+zthoreen, clement.roussel, p.alexej, Ceris, Nayre, Kiper, announce, thmarth,
+Ner'zhul, DarkXuan
diff --git a/sql/base/world_database.sql b/sql/base/world_database.sql
index 9ee43533c03..bff37f7818c 100644
--- a/sql/base/world_database.sql
+++ b/sql/base/world_database.sql
@@ -361,6 +361,8 @@ INSERT INTO `command` VALUES
('character rename',2,'Syntax: .character rename [$name]\r\n\r\nMark selected in game or by $name in command character for rename at next login.'),
('character reputation',2,'Syntax: .character reputation [$player_name]\r\n\r\nShow reputation information for selected player or player find by $player_name.'),
('character titles',2,'Syntax: .character titles [$player_name]\r\n\r\nShow known titles list for selected player or player find by $player_name.'),
+('character changefaction',2,'Syntax: .character changefaction $name\r\n\r\nChange character faction.'),
+('character changerace',2,'Syntax: .character changerace $name\r\n\r\nChange character race.'),
('channel set public', 3, 'Syntax: .channel set public $channel $public\r\n\r\nChange password-changing ability for a channel. 1 for possible, 0 for GM only.'),
('combatstop',2,'Syntax: .combatstop [$playername]\r\nStop combat for selected character. If selected non-player then command applied to self. If $playername provided then attempt applied to online player $playername.'),
('cometome',3,'SYntax: .cometome $parameter\nMake selected creature come to your current location (new position not saved to DB).'),
@@ -436,7 +438,7 @@ INSERT INTO `command` VALUES
('instance listbinds',3,'Syntax: .instance listbinds\r\n Lists the binds of the selected player.'),
('instance savedata',3,'Syntax: .instance savedata\r\n Save the InstanceData for the current player''s map to the DB.'),
('instance stats',3,'Syntax: .instance stats\r\n Shows statistics about instances.'),
-('instance unbind',3,'Syntax: .instance unbind <mapid|all> [difficulty]\r\n Clear all/some of player\'s binds'),
+('instance unbind',3,'Syntax: .instance unbind <mapid|all> [difficulty]\r\n Clear all/some of player''s binds'),
('itemmove',2,'Syntax: .itemmove #sourceslotid #destinationslotid\r\n\r\nMove an item from slots #sourceslotid to #destinationslotid in your inventory\r\n\r\nNot yet implemented'),
('kick',2,'Syntax: .kick [$charactername] [$reason]\r\n\r\nKick the given character name from the world with or without reason. If no character name is provided then the selected player (except for yourself) will be kicked. If no reason is provided, default is \"No Reason\".'),
('learn',3,'Syntax: .learn #spell [all]\r\n\r\nSelected character learn a spell of id #spell. If ''all'' provided then all ranks learned.'),
@@ -7741,6 +7743,102 @@ INSERT INTO `player_classlevelstats` VALUES
UNLOCK TABLES;
--
+-- Table structure for table `player_factionchange_achievement`
+--
+
+DROP TABLE IF EXISTS `player_factionchange_achievement`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `player_factionchange_achievement` (
+ `alliance_id` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `player_factionchange_achievement`
+--
+
+LOCK TABLES `player_factionchange_achievement` WRITE;
+/*!40000 ALTER TABLE `player_factionchange_achievement` DISABLE KEYS */;
+/*!40000 ALTER TABLE `player_factionchange_achievement` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `player_factionchange_items`
+--
+
+DROP TABLE IF EXISTS `player_factionchange_items`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `player_factionchange_items` (
+ `race_A` int(8) NOT NULL,
+ `alliance_id` int(8) NOT NULL,
+ `commentA` text,
+ `race_H` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ `commentH` text,
+PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `player_factionchange_items`
+--
+
+LOCK TABLES `player_factionchange_items` WRITE;
+/*!40000 ALTER TABLE `player_factionchange_items` DISABLE KEYS */;
+/*!40000 ALTER TABLE `player_factionchange_items` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `player_factionchange_reputations`
+--
+
+DROP TABLE IF EXISTS `player_factionchange_reputations`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `player_factionchange_reputations` (
+ `alliance_id` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `player_factionchange_reputations`
+--
+
+LOCK TABLES `player_factionchange_reputations` WRITE;
+/*!40000 ALTER TABLE `player_factionchange_reputations` DISABLE KEYS */;
+/*!40000 ALTER TABLE `player_factionchange_reputations` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `player_factionchange_spells`
+--
+
+DROP TABLE IF EXISTS `player_factionchange_spells`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `player_factionchange_spells` (
+ `alliance_id` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `player_factionchange_spells`
+--
+
+LOCK TABLES `player_factionchange_spells` WRITE;
+/*!40000 ALTER TABLE `player_factionchange_spells` DISABLE KEYS */;
+/*!40000 ALTER TABLE `player_factionchange_spells` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
-- Table structure for table `player_levelstats`
--
diff --git a/sql/updates/9784_world_command.sql b/sql/updates/9784_world_command.sql
new file mode 100644
index 00000000000..94fee31f4f4
--- /dev/null
+++ b/sql/updates/9784_world_command.sql
@@ -0,0 +1,4 @@
+DELETE FROM `command` WHERE `name` IN ('character changefaction','character changerace');
+INSERT INTO `command` VALUES
+('character changefaction',2,'Syntax: .character changefaction $name\r\n\r\nChange character faction.'),
+('character changerace',2,'Syntax: .character changerace $name\r\n\r\nChange character race.');
diff --git a/sql/updates/9784_world_player_factionchange_achievement.sql b/sql/updates/9784_world_player_factionchange_achievement.sql
new file mode 100644
index 00000000000..c1cce249b1b
--- /dev/null
+++ b/sql/updates/9784_world_player_factionchange_achievement.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS `player_factionchange_achievement`;
+CREATE TABLE `player_factionchange_achievement` (
+ `alliance_id` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
diff --git a/sql/updates/9784_world_player_factionchange_items.sql b/sql/updates/9784_world_player_factionchange_items.sql
new file mode 100644
index 00000000000..8cb815849f2
--- /dev/null
+++ b/sql/updates/9784_world_player_factionchange_items.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS `player_factionchange_items`;
+CREATE TABLE `player_factionchange_items` (
+ `race_A` int(8) NOT NULL,
+ `alliance_id` int(8) NOT NULL,
+ `commentA` text,
+ `race_H` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ `commentH` text,
+PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
diff --git a/sql/updates/9784_world_player_factionchange_reputations.sql b/sql/updates/9784_world_player_factionchange_reputations.sql
new file mode 100644
index 00000000000..2dd02b29727
--- /dev/null
+++ b/sql/updates/9784_world_player_factionchange_reputations.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS `player_factionchange_reputations`;
+CREATE TABLE `player_factionchange_reputations` (
+ `alliance_id` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
diff --git a/sql/updates/9784_world_player_factionchange_spells.sql b/sql/updates/9784_world_player_factionchange_spells.sql
new file mode 100644
index 00000000000..83879ede5ac
--- /dev/null
+++ b/sql/updates/9784_world_player_factionchange_spells.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS `player_factionchange_spells`;
+CREATE TABLE `player_factionchange_spells` (
+ `alliance_id` int(8) NOT NULL,
+ `horde_id` int(8) NOT NULL,
+ PRIMARY KEY (`alliance_id`,`horde_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
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();