Created
April 8, 2011 15:47
-
-
Save iorlas/910152 to your computer and use it in GitHub Desktop.
DualSpec for Mangos TBC.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
World.cpp: | |
m_configs[CONFIG_CHATLOG_BGROUND] = sConfig.GetBoolDefault("ChatLogs.BattleGround", false); | |
+ m_configs[CONFIG_DUAL_SPEC_TIME_DELTA] = sConfig.GetIntDefault("DualSpecTimeDelta", 300); | |
} | |
World.h: | |
CONFIG_VALUE_COUNT | |
+ CONFIG_DUAL_SPEC_TIME_DELTA | |
}; | |
Player.h: | |
void _LoadBGData(QueryResult_AutoPtr result); | |
+void _LoadAlternativeSpec(); | |
void _SaveBGData(); | |
+void _SaveAlternativeSpec(); | |
uint32 m_oldpetspell; | |
+//Second spec info | |
+typedef std::list<uint32> SpellIDList; | |
+SpellIDList m_altspec_talents; | |
+ActionButtonList m_altspec_actionButtons; | |
+time_t m_altspec_lastswap; | |
Player.cpp: | |
m_globalCooldowns.clear(); | |
+m_altspec_lastswap = 0; | |
Level0.cpp At end: | |
bool ChatHandler::HandleSwapSpec(const char* /*args*/) | |
{ | |
uint32 res = m_session->GetPlayer()->SwapSpec(); | |
switch(res){ | |
case 3:{ | |
PSendSysMessage("Oh, wait a bit, please!"); | |
break; | |
} | |
case 2:{ | |
PSendSysMessage("Too low level"); | |
break; | |
} | |
case 1:{ | |
PSendSysMessage("Swapped!"); | |
break; | |
} | |
} | |
return true; | |
} | |
Chat.cpp: | |
{ "save", SEC_PLAYER, false, &ChatHandler::HandleSaveCommand, "", NULL }, | |
+{ "swapspec", SEC_PLAYER, false, &ChatHandler::HandleSwapSpec, "", NULL }, | |
Chat.h | |
bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd); | |
+bool HandleSwapSpec(const char* args); | |
MYSQL characters DB: | |
DROP TABLE IF EXISTS `characters`.`character_altspec`; | |
CREATE TABLE `characters`.`character_altspec` ( | |
`guid` int(11) unsigned NOT NULL default '0' COMMENT 'Global Unique Identifier', | |
`altspells` longtext, | |
PRIMARY KEY (`guid`) | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Player Dual Spec System'; | |
-- | |
-- Dumping data for table `characters_altspec` | |
-- | |
LOCK TABLES `characters`.`character_altspec` WRITE; | |
/*!40000 ALTER TABLE `characters`.`character_altspec` DISABLE KEYS */; | |
/*!40000 ALTER TABLE `characters`.`character_altspec` ENABLE KEYS */; | |
UNLOCK TABLES; | |
DROP TABLE IF EXISTS `character_altspec_action`; | |
CREATE TABLE `character_altspec_action` ( | |
`guid` int(11) unsigned NOT NULL default '0' COMMENT 'Global Unique Identifier', | |
`button` tinyint(3) unsigned NOT NULL default '0', | |
`action` smallint(5) unsigned NOT NULL default '0', | |
`type` tinyint(3) unsigned NOT NULL default '0', | |
`misc` tinyint(3) unsigned NOT NULL default '0', | |
PRIMARY KEY (`guid`,`button`) | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Player Dual Spec System, table for actions'; | |
-- | |
-- Dumping data for table `character_altspec_action` | |
-- | |
LOCK TABLES `character_altspec_action` WRITE; | |
/*!40000 ALTER TABLE `character_altspec_action` DISABLE KEYS */; | |
/*!40000 ALTER TABLE `character_altspec_action` ENABLE KEYS */; | |
UNLOCK TABLES; | |
Player.cpp end: | |
void Player::_SaveAlternativeSpec() | |
{ | |
sLog.outError("Saving spec"); | |
//At first, save spells. | |
std::ostringstream ss; | |
ss << "REPLACE INTO character_altspec (guid, altspells) VALUES ('" << GetGUIDLow() << "', '"; | |
//Okay, now serialize it into string of ids, separated by whitespace | |
for (SpellIDList::iterator it = m_altspec_talents.begin(); it != m_altspec_talents.end(); it++) | |
ss << *it << " "; | |
ss << "')"; | |
sLog.outError("Spells serialized"); | |
//Nice, it saved! | |
CharacterDatabase.PExecute(ss.str().c_str()); | |
sLog.outError("Spells saved"); | |
//Now turn of action buttons | |
//Before - remove all player keys | |
// CharacterDatabase.PExecute("DELETE FROM character_altspec_action WHERE guid = '%u'", GetGUIDLow()); | |
// sLog.outError("Old buttons removed"); | |
// //Now - seek all needed keys and insert into DB | |
// for (uint32 button = 0; button < MAX_ACTION_BUTTONS; ++button) | |
// { | |
// //Find button by button id | |
// ActionButtonList::const_iterator itr = m_altspec_actionButtons.find(button); | |
// //STL returns end() if not found. We need to skip it. | |
// if (itr != m_altspec_actionButtons.end()) | |
// { | |
// //Okay, now we're ready to insert it | |
// CharacterDatabase.PExecute("INSERT INTO character_altspec_action (guid,button,action,type,misc) VALUES ('%u', '%u', '%u', '%u', '%u')", | |
// GetGUIDLow(), button, itr->second.action, itr->second.type, itr->second.misc); | |
// } | |
// } | |
//All done | |
} | |
uint32 Player::SwapSpec() | |
{ | |
/* | |
Error codes: | |
2 - Too low level | |
3 - Too fast | |
Strategy: | |
1) save keys | |
2) save talents | |
3) load talents | |
4) load keys | |
*/ | |
//Level check | |
if (getLevel() <= 10) | |
return 2; | |
//Time check | |
if (uint32(time(NULL) - m_altspec_lastswap) < sWorld.getConfig(CONFIG_DUAL_SPEC_TIME_DELTA)) | |
return 3; | |
/*********************************************************/ | |
/*** SAVE KEYS ***/ | |
/*********************************************************/ | |
// sLog.outError("Save keys"); | |
// //Save buttons before | |
// ActionButtonList tmp_buttons = m_altspec_actionButtons; | |
// //Just copy current content | |
// m_altspec_actionButtons = m_actionButtons; | |
/*********************************************************/ | |
/*** SAVE TALENTS ***/ | |
/*********************************************************/ | |
sLog.outError("Save talents"); | |
//copy current talents list | |
SpellIDList tmp = m_altspec_talents; | |
//erese it for populating using current talents | |
m_altspec_talents.clear(); | |
//Find all talents, general idea from Player::resetTalents | |
for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i){ | |
TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); | |
if (!talentInfo) continue; | |
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); | |
if (!talentTabInfo) continue; | |
if ((getClassMask() & talentTabInfo->ClassMask) == 0) continue; | |
for (int j = 0; j < 5; ++j){ | |
for (PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();){ | |
//skip disabled talents like Pyroblast or some else | |
if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled) | |
{ | |
++itr; | |
continue; | |
} | |
//for spells, which can be updated via trainers(like Pyroblast), we can'n just compare, cuz | |
// >1 ranks are not in talens store. So, first rank it is. We can just get lowerest rank of skill | |
// and search it in the talents storage. | |
uint32 itrFirstId = spellmgr.GetFirstSpellInChain(itr->first); | |
//now - just compare. Also, it make sense to add "|| spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId)" | |
//but i have no idea what it is, it uses in the Player::resetTalents function and it may be needed. | |
//Also, there is a some spells like Prayer of Spirit, which not in talents tree, but its depends on talents. | |
//So, we need just to get required spell by current spell and find is it in player spellbook. | |
if (itrFirstId == talentInfo->RankID[j] | |
|| spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId) | |
|| HasSpell(spellmgr.GetSpellRequired(itrFirstId))) | |
m_altspec_talents.push_back(itr->first); | |
++itr; | |
} | |
} | |
} | |
/*********************************************************/ | |
/*** LOAD TALENTS ***/ | |
/*********************************************************/ | |
sLog.outError("Load talents"); | |
resetTalents(true); | |
for (SpellIDList::iterator it = tmp.begin(); it != tmp.end(); it++) | |
{ | |
learnSpell(*it); | |
} | |
InitTalentForLevel(); | |
learnSkillRewardedSpells(); | |
/*********************************************************/ | |
/*** LOAD KEYS ***/ | |
/*********************************************************/ | |
// sLog.outError("Load keys"); | |
// //Clean up | |
// for (int button = 0; button < MAX_ACTION_BUTTONS; ++button) | |
// removeActionButton(button); | |
// sLog.outError("Keys cleaned"); | |
// //Aaand... load | |
// for (int button = 0; button < MAX_ACTION_BUTTONS; ++button) | |
// { | |
// //Find button by button id | |
// ActionButtonList::const_iterator itr = m_altspec_actionButtons.find(button); | |
// //STL returns end() if not found. We need to skip it. | |
// if (itr != m_altspec_actionButtons.end()) | |
// addActionButton(button, itr->second.action, itr->second.type, itr->second.misc); | |
// } | |
// sLog.outError("Keys added"); | |
// SendInitialActionButtons();//doesnt work | |
//Drop mana and health to minimum for preventing of profit from swappings | |
SetHealth(12); | |
SetPower(POWER_MANA, 12); | |
return 1; //Okay | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi sir,
Wondering if this thing is working or early WIP ?