Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
  • Loading branch information...
commit be7d94f24e77d3c5e98fd8790c810bb2f95327e9 1 parent de59ac3
silinoron authored
View
3  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
+zthoreen, clement.roussel, p.alexej, Ceris, Nayre, Kiper, announce, thmarth,
+Ner'zhul, DarkXuan
View
100 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`
--
View
4 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.');
View
6 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;
View
10 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;
View
6 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;
View
6 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;
View
18 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[] =
View
2  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);
View
55 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;
View
17 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;
View
3  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;
View
11 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);
View
2  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;
View
180 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);
+}
View
12 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
View
6 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
{
View
317 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);
+}
View
4 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 },
View
1  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);
View
12 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();
Please sign in to comment.
Something went wrong with that request. Please try again.