Add support for character race and faction transfers.

Based (loosely) on patch by Ner'zhul, DarkXuan, and [email protected].
Fixes issue #3429.

--HG--
branch : trunk
This commit is contained in:
silinoron
2010-09-05 13:12:24 -07:00
parent de59ac3bc3
commit be7d94f24e
21 changed files with 760 additions and 15 deletions
+2 -1
View File
@@ -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
+99 -1
View File
@@ -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.'),
@@ -7740,6 +7742,102 @@ INSERT INTO `player_classlevelstats` VALUES
/*!40000 ALTER TABLE `player_classlevelstats` ENABLE KEYS */;
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`
--
+4
View File
@@ -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.');
@@ -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;
@@ -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;
@@ -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;
@@ -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;
+10 -8
View File
@@ -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[] =
+2
View File
@@ -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);
+55
View File
@@ -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;
+15 -2
View File
@@ -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;
+3
View File
@@ -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;
+10 -1
View File
@@ -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);
+2
View File
@@ -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;
+180
View File
@@ -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);
}
+12
View File
@@ -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
@@ -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
{
@@ -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);
}
+2 -2
View File
@@ -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 },
+1
View File
@@ -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);
+12
View File
@@ -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();