diff --git a/src/game/Chat.cpp b/src/game/Chat.cppindex 449267f..b8ebd3b 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -505,6 +505,8 @@ ChatCommand * ChatHandler::getCommandTable()
{ "aura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAuraCommand, "", NULL },
{ "unaura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnAuraCommand, "", NULL },
+ { "changerace", SEC_ADMINISTRATOR, false &ChatHandler::HandleChangeRaceCommand, "", NULL },
+ { "changeclass", SEC_ADMINISTRATOR, false &ChatHandler::HandleChangeClassCommand, "", NULL },
{ "announce", SEC_MODERATOR, true, &ChatHandler::HandleAnnounceCommand, "", NULL },
{ "notify", SEC_MODERATOR, true, &ChatHandler::HandleNotifyCommand, "", NULL },
{ "goname", SEC_MODERATOR, false, &ChatHandler::HandleGonameCommand, "", NULL },
diff --git a/src/game/Chat.h b/src/game/Chat.h
index 12352f8..40c3ff4 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -413,6 +413,8 @@ class ChatHandler
bool HandleWritePDumpCommand(const char *args);
bool HandleCastCommand(const char *args);
bool HandleCastBackCommand(const char *args);
+ bool HandleChangeRaceCommand(const char* args);
+ bool HandleChangeClassCommand(const char* args);
bool HandleCastDistCommand(const char *args);
bool HandleCastSelfCommand(const char *args);
bool HandleCastTargetCommand(const char *args);
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
index 44f6262..9b1b365 100644
--- a/src/game/Level3.cpp
+++ b/src/game/Level3.cpp
@@ -6548,3 +6548,329 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args)
return true;
}
+
+void LoadTaxiMask(const char* data, TaxiMask& m_taximask)
+{
+ Tokens tokens = StrSplit(data, " ");
+
+ int index;
+ Tokens::iterator iter;
+ for (iter = tokens.begin(), index = 0;
+ (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
+ {
+ // load and set bits only for existed taxi nodes
+ m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
+ }
+}
+
+struct SkillLearnInfo {
+ SkillLearnInfo(uint16 skillId_, uint16 curValue_, uint16 maxValue_)
+ :skillId(skillId_), curValue(curValue_), maxValue(maxValue_){}
+ uint16 skillId;
+ uint16 curValue;
+ uint16 maxValue;
+};
+
+bool ChatHandler::HandleChangeRaceCommand(const char* args)
+{
+ // check parameters
+ if(!args)
+ return false;
+
+ // get char names
+ std::string nameFrom = strtok((char*)args, " ");
+ normalizePlayerName(nameFrom);
+
+ if(nameFrom.empty())
+ return false;
+ std::string nameTo = strtok(NULL, " ");
+ normalizePlayerName(nameTo);
+ if(nameTo.empty())
+ return false;
+
+ // check chars status
+ Player *chrFrom = objmgr.GetPlayer(nameFrom.data());
+ Player *chrTo = objmgr.GetPlayer(nameTo.data());
+ if (chrFrom || chrTo)
+ {
+ PSendSysMessage("Character's should be offline");
+ return true;
+ }
+
+ // get char guids by name
+ uint64 pfrom = objmgr.GetPlayerGUIDByName(nameFrom);
+ uint64 pto = objmgr.GetPlayerGUIDByName(nameTo);
+ if (!pfrom || !pto)
+ {
+ PSendSysMessage("Characters not found. Check names.");
+ return true;
+ }
+
+ // get class/race
+ QueryResult* result = sDatabase.PQuery("SELECT `race`, `class` FROM `character` WHERE `guid`=%u;", pfrom);
+ if(!result)
+ {
+ PSendSysMessage("Couldn't get race for first character");
+ return true;
+ }
+ Field* fields = result->Fetch();
+ uint32 raceFrom = fields[0].GetUInt32();
+ uint32 classFrom = fields[1].GetUInt32();
+ delete result;
+ result = sDatabase.PQuery("SELECT `race`, `class`, `taximask` FROM `character` WHERE `guid`=%u;", pto);
+ if(!result)
+ {
+ PSendSysMessage("Couldn't get race for second character");
+ return true;
+ }
+ fields = result->Fetch();
+ uint32 raceTo = fields[0].GetUInt32();
+ uint32 classTo = fields[1].GetUInt32();
+ TaxiMask taximask;
+ LoadTaxiMask(fields[2].GetString(), taximask);
+ delete result;
+
+ // check class
+ if (classFrom != classTo)
+ {
+ PSendSysMessage("Characters should be the same classes");
+ return true;
+ }
+
+ // change data fields
+ uint16 flags[] = { PLAYER_BYTES,
+ PLAYER_BYTES_3,
+ UNIT_FIELD_DISPLAYID,
+ UNIT_FIELD_NATIVEDISPLAYID,
+ UNIT_FIELD_FACTIONTEMPLATE,
+ UNIT_FIELD_BYTES_0,
+ 0
+ };
+ Tokens tokens;
+ if(!Player::LoadValuesArrayFromDB(tokens, pto))
+ return false;
+ for (int i = 0; flags[i] != 0; i++) {
+ uint16 index = flags[i];
+ if(index >= tokens.size())
+ return false;
+ uint32 value = Player::GetUInt32ValueFromDB(index, pfrom);
+ char buf[11];
+ snprintf(buf,11, "%u", value);
+ tokens[index] = buf;
+ }
+ // save data about opened bank slot
+ uint32 value = Player::GetUInt32ValueFromDB(PLAYER_BYTES_2, pfrom);
+ uint32 bank = (uint32) atoi(tokens[PLAYER_BYTES_2].c_str());
+ Player::SetUInt32ValueInArray(tokens, PLAYER_BYTES_2, (value & ~0x70000) | (bank & 0x70000));
+
+ // get class/race info
+ PlayerInfo const* infoFrom = objmgr.GetPlayerInfo(raceFrom, classFrom);
+ PlayerInfo const* infoTo = objmgr.GetPlayerInfo(raceTo, classFrom);
+ std::list<SkillLearnInfo> exclusiveFrom;
+ std::list<uint16> exclusiveTo;
+
+ // change spells
+ if (raceTo != raceFrom) {
+ // delete last race spells
+ std::ostringstream sssp;
+ sssp << "DELETE FROM `character_spell` WHERE `guid`=" << pto << " AND `spell` IN (";
+ for (std::list<CreateSpellPair>::const_iterator spell_itr = infoTo->spell.begin();
+ spell_itr != infoTo->spell.end(); ++spell_itr) {
+ uint16 spell_id = spell_itr->first;
+ sssp << spell_id << ", ";
+ ObjectMgr::SpellLearnSkillNode const* spellLearnSkill = objmgr.GetSpellLearnSkill(spell_id);
+ if (spellLearnSkill)exclusiveTo.push_back(spellLearnSkill->skill);
+ }
+ sssp << "0)";
+ sDatabase.PExecute(sssp.str().c_str());
+ // shift if need slot number
+ result = sDatabase.PQuery("SELECT MIN(`slot`) FROM `character_spell` WHERE `guid`=%u;", pto);
+ if(!result)
+ {
+ PSendSysMessage("Couldn't get min slot from character_spell");
+ return true;
+ }
+ fields = result->Fetch();
+ uint32 minSlotNumber = fields[0].GetUInt32();
+ if (infoFrom->spell.size() >= minSlotNumber)
+ { // shift slot number
+ uint32 count = infoFrom->spell.size() + 1 - fields[0].GetUInt32();
+ sDatabase.PExecute("UPDATE `character_spell` SET `slot`=`slot`+%u WHERE `guid`=%u", count, pto);
+ }
+ delete result;
+ // insert new spells
+ std::ostringstream ssspi;
+ ssspi << "INSERT INTO `character_spell` (`guid`, `spell`, `slot`, `active`) VALUES ";
+ std::list<CreateSpellPair>::const_iterator spell_itr = infoFrom->spell.begin();
+ int index = 1;
+ for (; index < infoFrom->spell.size(); ++spell_itr, index++) {
+ uint16 spell_id = spell_itr->first;
+ ssspi << "(" << pto << ", " << spell_id << ", " << index << ", " << spell_itr->second << "),";
+ ObjectMgr::SpellLearnSkillNode const* spellLearnSkill = objmgr.GetSpellLearnSkill(spell_id);
+ if (spellLearnSkill)
+ {
+ uint32 skill_value = spellLearnSkill->value;
+ std::list<uint16>::iterator itr = find(exclusiveTo.begin(), exclusiveTo.end(), skill_value);
+ if (itr != exclusiveTo.end())
+ exclusiveTo.erase(itr);
+ else {
+ uint16 maxskill = 5 * Player::GetUInt32ValueFromArray(tokens, UNIT_FIELD_LEVEL);
+ uint32 skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
+ exclusiveFrom.push_back(SkillLearnInfo(spellLearnSkill->skill, skill_value, skill_max_value));
+ }
+ }
+ }
+ ssspi << "(" << pto << ", " << spell_itr->first << ", " << index << ", " << spell_itr->second << ")";
+ sDatabase.PExecute(ssspi.str().c_str());
+
+ // change skills
+ const std::list<uint16>* skillsFrom = &(infoFrom->skill);
+ const std::list<uint16>* skillsTo = &(infoTo->skill);
+ for (std::list<uint16>::const_iterator itr = skillsFrom->begin(); itr != skillsFrom->end(); ++itr) {
+ uint16 value = (*itr);
+ if (std::find(skillsTo->begin(), skillsTo->end(), value) == skillsTo->end())
+ exclusiveFrom.push_back(SkillLearnInfo(value, 5, 5));
+ }
+ for (std::list<uint16>::const_iterator itr = skillsTo->begin(); itr != skillsTo->end(); ++itr) {
+ uint16 value = (*itr);
+ if (std::find(skillsFrom->begin(), skillsFrom->end(), value) == skillsFrom->end())
+ exclusiveTo.push_back(value);
+ }
+ for (int i = 0; i < PLAYER_MAX_SKILLS; i++) {
+ uint16 index = PLAYER_SKILL(i);
+ uint16 skillId = 0x0000FFFF & (uint32)atoi(tokens[index].c_str());
+ if (!skillId)
+ {
+ if (!exclusiveFrom.empty())
+ {
+ std::list<SkillLearnInfo>::iterator itr = exclusiveFrom.begin();
+ Player::SetUInt32ValueInArray(tokens, index, itr->skillId);
+ Player::SetUInt32ValueInArray(tokens, index + 1, itr->maxValue*0x10000 + itr->curValue);
+ tokens[index + 2] = "0";
+ exclusiveFrom.erase(itr);
+ }
+ continue;
+ }
+ if (find(exclusiveTo.begin(), exclusiveTo.end(), skillId) != exclusiveTo.end())
+ { // change skill
+ if (exclusiveFrom.empty()) { // remove
+ tokens[index] = "0";
+ tokens[index + 1] = "0";
+ tokens[index + 2] = "0";
+ } else { // replace
+ std::list<SkillLearnInfo>::iterator itr = exclusiveFrom.begin();
+ Player::SetUInt32ValueInArray(tokens, index, itr->skillId);
+ Player::SetUInt32ValueInArray(tokens, index + 1, itr->maxValue*0x10000 + itr->curValue);
+ tokens[index + 2] = "0";
+ exclusiveFrom.erase(itr);
+ }
+ }
+ }
+ }
+ Player::SaveValuesArrayInDB(tokens, pto);
+ if (Player::TeamForRace(raceFrom) != Player::TeamForRace(raceTo))
+ {
+ // Taxi nodes setup
+ memset(taximask, 0, sizeof(taximask));
+ // capital and taxi hub masks
+ switch(raceFrom)
+ {
+ case 1: taximask[0]= 1 << ( 2-1); break; // Human
+ case 2: taximask[0]= 1 << (23-1); break; // Orc
+ case 3: taximask[0]= 1 << ( 6-1); break; // Dwarf
+ // Night Elf
+ case 4: taximask[0]= (1 << (26-1)) | (1 << (27-1)); break;
+ case 5: taximask[0]= 1 << (11-1); break; // Undead
+ case 6: taximask[0]= 1 << (22-1); break; // Tauren
+ case 7: taximask[0]= 1 << ( 6-1); break; // Gnome
+ case 8: taximask[0]= 1 << (23-1); break; // Troll
+ //case 10: m_taximask[0]= 1 << (1-1); break; // Blood Elf
+ case 11: taximask[0+94/32]= 1 << (94%32-1); break;// Draenei
+ }
+ // new continent starting masks (It will be accessible only at new map
+ switch(Player::TeamForRace(raceFrom))
+ {
+ case ALLIANCE: taximask[3]= 1 << (4-1); break;
+ case HORDE: taximask[3]= 1 << (3-1); break;
+ }
+ }
+ // save taximask
+ std::ostringstream sst;
+ sst << "UPDATE `character` SET `taximask`='";
+ for (int i = 0; i < 8; i++)
+ sst << taximask[i] << " ";
+ sst << "' WHERE `guid`=";
+ sst << pto;
+ sDatabase.Execute(sst.str().c_str());
+
+ if (raceFrom != raceTo)
+ sDatabase.PExecute("UPDATE `character` SET `race`=%u WHERE `guid`=%u", raceFrom, pto);
+ return true;
+}
+
+bool ChatHandler::HandleChangeClassCommand(const char* args)
+{
+ // check parameters
+ if(!args)
+ return false;
+
+ // get char names
+ std::string name = strtok((char*)args, " ");
+ normalizePlayerName(name);
+ if(name.empty())
+ return false;
+ // get class
+ std::string classNumStr = strtok(NULL, " ");
+ uint32 classDst = atoi(classNumStr.c_str());
+ if(classDst > 9 && classDst != 11)
+ return false;
+
+ // check chars status
+ Player *chr = objmgr.GetPlayer(name.data());
+ if (chr) {
+ PSendSysMessage("Character should be offline");
+ return true;
+ }
+
+ // get char guids by name
+ uint64 pguid = objmgr.GetPlayerGUIDByName(name);
+ if (!pguid) {
+ PSendSysMessage("Characters not found. Check names.");
+ return true;
+ }
+
+ // get class/race
+ QueryResult* result = sDatabase.PQuery("SELECT `race`, `class` FROM `character` WHERE `guid`=%u;", pguid);
+ if(!result) {
+ PSendSysMessage("Couldn't get race for character");
+ return true;
+ }
+ Field* fields = result->Fetch();
+ uint32 race = fields[0].GetUInt32();
+ uint32 classSrc = fields[1].GetUInt32();
+ delete result;
+
+ // check class
+ if (classSrc == classDst) {
+ PSendSysMessage("Nothing to change");
+ return true;
+ }
+
+ // change data fields
+ Tokens tokens;
+ if(!Player::LoadValuesArrayFromDB(tokens, pguid))
+ {
+ PSendSysMessage("Couldn't load data of character");
+ return true;
+ }
+ ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(classDst);
+ uint32 value = (uint32) atoi(tokens[UNIT_FIELD_BYTES_0].c_str());
+ value = (value & ~0xFF00FF00) | ((classDst << 8) & 0x0000FF00) | ((cEntry->powerType << 24) & 0xFF000000);
+ Player::SetUInt32ValueInArray(tokens, UNIT_FIELD_BYTES_0, value);
+ Player::SaveValuesArrayInDB(tokens, pguid);
+ sDatabase.PExecute("UPDATE `character` SET `class`=%u, `at_login` = `at_login` | '%u' WHERE `guid`=%u",
+ classDst, uint32(AT_LOGIN_RESET_SPELLS | AT_LOGIN_RESET_TALENTS), pguid);
+
+ PSendSysMessage("Success");
+ return true;
+}
\ No newline at end of file
|