diff --git a/modules/mod-Forge/src/ForgeCache.cpp b/modules/mod-Forge/src/ForgeCache.cpp
index 44acb170807626..427d5c1e9cc02c 100644
--- a/modules/mod-Forge/src/ForgeCache.cpp
+++ b/modules/mod-Forge/src/ForgeCache.cpp
@@ -75,6 +75,7 @@ struct ForgeCharacterTalent
uint32 SpellId;
uint32 TabId;
uint8 CurrentRank;
+ uint8 type;
};
struct ForgeTalentPrereq
@@ -559,12 +560,23 @@ class ForgeCache : public DatabaseScript
for (auto& tabIdKvp : spec->Talents)
for (auto& tabTypeKvp : tabIdKvp.second)
- UpdateChacterTalentInternal(acct, charId, trans, spec->Id, tabTypeKvp.second->SpellId, tabTypeKvp.second->TabId, tabTypeKvp.second->CurrentRank);
+ if (tabTypeKvp.second->type == NodeType::CHOICE)
+ UpdateChacterChoiceNodeInternal(trans, acct, charId, spec->Id, tabTypeKvp.second->TabId, tabTypeKvp.second->SpellId, spec->ChoiceNodesChosen.at(tabTypeKvp.second->SpellId));
+ else
+ UpdateChacterTalentInternal(acct, charId, trans, spec->Id, tabTypeKvp.second->SpellId, tabTypeKvp.second->TabId, tabTypeKvp.second->CurrentRank);
-
CharacterDatabase.CommitTransaction(trans);
}
+ void UpdateChacterChoiceNodeInternal(CharacterDatabaseTransaction trans, uint32 account, uint32 guid, uint32 specId, uint32 tabId, uint32 choiceNodeId, uint32 choiceNodeSelection) {
+ if (TalentTabs[tabId]->TalentType != ACCOUNT_WIDE_TYPE)
+ trans->Append("INSERT INTO `forge_character_node_choices` (`guid`,`spec`,`tabId`,`node`,`choice`) VALUES ({},{},{},{},{}) ON DUPLICATE KEY UPDATE `choice` = {}",
+ guid, specId, choiceNodeId, tabId, choiceNodeSelection, choiceNodeSelection);
+ else
+ trans->Append("INSERT INTO `forge_character_node_choices` (`guid`,`spec`,`tabId`,`node`,`choice`) VALUES ({},{},{},{},{}) ON DUPLICATE KEY UPDATE `choice` = {}",
+ account, ACCOUNT_WIDE_KEY, choiceNodeId, tabId, choiceNodeSelection, choiceNodeSelection);
+ }
+
void UpdateCharacterSpecDetailsOnly(Player* player, ForgeCharacterSpec*& spec)
{
uint32 charId = player->GetGUID().GetCounter();
@@ -574,13 +586,6 @@ class ForgeCache : public DatabaseScript
CharacterDatabase.CommitTransaction(trans);
}
- void UpdateChacterTalent(Player* player, uint32 spec, uint32 spellId, uint32 tabId, uint8 known)
- {
- auto trans = CharacterDatabase.BeginTransaction();
- UpdateChacterTalentInternal(player->GetSession()->GetAccountId(), player->GetGUID().GetCounter(), trans, spec, spellId, tabId, known);
- CharacterDatabase.CommitTransaction(trans);
- }
-
void ApplyAccountBoundTalents(Player* player)
{
ForgeCharacterSpec* currentSpec;
@@ -871,6 +876,15 @@ class ForgeCache : public DatabaseScript
// choiceNodeId is the id of the node in forge_talents
std::unordered_map> _choiceNodes;
+ std::unordered_map _choiceNodesRev;
+
+ uint32 GetChoiceNodeFromSpell(uint32 spellId) {
+ auto out = _choiceNodesRev.find(spellId);
+ if (out != _choiceNodesRev.end())
+ return out->second;
+
+ return 0;
+ }
private:
std::unordered_map CharacterActiveSpecs;
@@ -1278,6 +1292,7 @@ class ForgeCache : public DatabaseScript
QueryResult exclTalents = WorldDatabase.Query("SELECT * FROM forge_talent_choice_nodes");
_choiceNodes.clear();
+ _choiceNodesRev.clear();
if (!exclTalents)
return;
@@ -1294,6 +1309,7 @@ class ForgeCache : public DatabaseScript
choice->spellId = spellChoice;
_choiceNodes[choiceNodeId].push_back(spellChoice);
+ _choiceNodesRev[spellChoice] = choiceNodeId;
ForgeTalent* lt = TalentTabs[talentTabId]->Talents[choiceNodeId];
if (lt != nullptr)
@@ -1478,6 +1494,7 @@ class ForgeCache : public DatabaseScript
{
ForgeTalent* ft = TalentTabs[talent->TabId]->Talents[talent->SpellId];
ForgeCharacterSpec* spec = CharacterSpecs[characterGuid][specId];
+ talent->type = ft->nodeType;
spec->Talents[talent->TabId][talent->SpellId] = talent;
}
@@ -1489,6 +1506,7 @@ class ForgeCache : public DatabaseScript
for (auto& spec : CharacterSpecs[ch])
{
ForgeTalent* ft = TalentTabs[talent->TabId]->Talents[talent->SpellId];
+ talent->type = ft->nodeType;
spec.second->Talents[talent->TabId][talent->SpellId] = talent;
}
}
diff --git a/modules/mod-Forge/src/ForgeCommonMessage.cpp b/modules/mod-Forge/src/ForgeCommonMessage.cpp
index 9aea1f7da5d251..88766400efc3b4 100644
--- a/modules/mod-Forge/src/ForgeCommonMessage.cpp
+++ b/modules/mod-Forge/src/ForgeCommonMessage.cpp
@@ -394,9 +394,9 @@ std::string ForgeCommonMessage::DoBuildRanks(std::unordered_mapTryGetTalentTab(player, tabId, tab))
+ if (fc->TryGetTalentTab(player, tabId, tab) && fc->TryGetCharacterActiveSpec(player, fs))
{
for(auto& sp : tab->Talents)
{
@@ -404,14 +404,21 @@ std::string ForgeCommonMessage::DoBuildRanks(std::unordered_mapsecond->CurrentRank == 0 && !CanLearnTalent(player, tabId, sp.second->SpellId))
- clientMsg = clientMsg + delimiter + std::to_string(sp.second->SpellId) + "~-1";
+ if (itt != spec.end()) {
+ if (itt->second->type == NodeType::CHOICE)
+ if (itt->second->CurrentRank == 0 || !CanLearnTalent(player, tabId, sp.second->SpellId))
+ clientMsg = clientMsg + delimiter + std::to_string(sp.second->SpellId) + "~-1";
+ else
+ clientMsg = clientMsg + delimiter + std::to_string(itt->second->SpellId) + "~" + std::to_string(fs->ChoiceNodesChosen.at(itt->second->SpellId));
else
- clientMsg = clientMsg + delimiter + std::to_string(itt->second->SpellId) + "~" + std::to_string(itt->second->CurrentRank);
+ if (itt->second->CurrentRank == 0 && !CanLearnTalent(player, tabId, sp.second->SpellId))
+ clientMsg = clientMsg + delimiter + std::to_string(sp.second->SpellId) + "~-1";
+ else
+ clientMsg = clientMsg + delimiter + std::to_string(itt->second->SpellId) + "~" + std::to_string(itt->second->CurrentRank);
+ }
else if (!CanLearnTalent(player, tabId, sp.second->SpellId))
clientMsg = clientMsg + delimiter + std::to_string(sp.second->SpellId) + "~-1";
else
diff --git a/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp b/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp
index 4a5870d469e2be..bb656e6454b075 100644
--- a/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp
+++ b/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp
@@ -26,11 +26,15 @@ class LearnTalentHandler : public ForgeTopicHandler
std::vector results;
boost::algorithm::split(results, iam.message, boost::is_any_of(";"));
- if (results.empty() || results.size() != 2 || !fc->isNumber(results[0]) || !fc->isNumber(results[1]))
+ if (results.empty() || results.size() != 3 || !fc->isNumber(results[0]) || !fc->isNumber(results[1]) || !fc->isNumber(results[2]))
return;
uint32 tabId = static_cast(std::stoul(results[0]));
uint32 spellId = static_cast(std::stoul(results[1]));
+ uint32 nodeType = static_cast(std::stoul(results[2]));
+
+ bool choiceNode = nodeType == NodeType::CHOICE;
+
ForgeTalentTab* tab;
ForgeCharacterSpec* spec;
CharacterPointType tabType;
@@ -52,7 +56,12 @@ class LearnTalentHandler : public ForgeTopicHandler
}
// check dependants, rank requirements
- auto talItt = tab->Talents.find(spellId);
+ uint32 choiceId = fc->GetChoiceNodeFromSpell(spellId);
+ if (!choiceId && choiceNode) {
+ iam.player->SendForgeUIMsg(ForgeTopic::LEARN_TALENT_ERROR, "Attempt to unlearn unknown choice node talent: " + std::to_string(choiceId));
+ return;
+ }
+ auto talItt = choiceNode ? tab->Talents.find(choiceId) : tab->Talents.find(spellId);
if (talItt == tab->Talents.end())
{
@@ -184,18 +193,6 @@ class LearnTalentHandler : public ForgeTopicHandler
}
}
- // TODO: FIX
- //for (auto& exclu : ft->Choices)
- //{
- // auto typeItt = skillTabs.find(exclu);
-
- // if (typeItt != skillTabs.end() && typeItt->second->CurrentRank > 0)
- // {
- // RequirementsNotMet(iam);
- // return;
- // }
- //}
-
auto spellItter = skillTabs.find(ft->SpellId);
ForgeCharacterTalent* ct = new ForgeCharacterTalent();
@@ -204,6 +201,7 @@ class LearnTalentHandler : public ForgeTopicHandler
ct->CurrentRank = 0;
ct->SpellId = ft->SpellId;
ct->TabId = tabId;
+ ct->type = ft->nodeType;
spec->Talents[tabId][ft->SpellId] = ct;
}
else
@@ -223,25 +221,39 @@ class LearnTalentHandler : public ForgeTopicHandler
auto ranksItt = ft->Ranks.find(ct->CurrentRank);
- if (ranksItt != ft->Ranks.end()) {
- auto spellInfo = sSpellMgr->GetSpellInfo(ranksItt->second);
- if (!spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
- iam.player->removeSpell(ranksItt->second, SPEC_MASK_ALL, false);
+ if (!choiceNode)
+ if (ranksItt != ft->Ranks.end()) {
+ auto spellInfo = sSpellMgr->GetSpellInfo(ranksItt->second);
+ if (!spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
+ iam.player->removeSpell(ranksItt->second, SPEC_MASK_ALL, false);
+ else
+ iam.player->RemoveAura(ranksItt->second);
+ }
else
- iam.player->RemoveAura(ranksItt->second);
- }
+ for (auto s : ft->Choices)
+ iam.player->removeSpell(s->spellId, SPEC_MASK_ALL, false);
ct->CurrentRank++;
ranksItt = ft->Ranks.find(ct->CurrentRank);
- if (ranksItt != ft->Ranks.end()) {
- auto spellInfo = sSpellMgr->GetSpellInfo(ranksItt->second);
+ if (choiceNode) {
+ auto spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
- iam.player->learnSpell(ranksItt->second);
+ iam.player->learnSpell(spellId);
else
- iam.player->AddAura(ranksItt->second, iam.player);
+ iam.player->AddAura(spellId, iam.player);
+
+ spec->ChoiceNodesChosen[choiceId] = spellId;
}
+ else
+ if (ranksItt != ft->Ranks.end()) {
+ auto spellInfo = sSpellMgr->GetSpellInfo(ranksItt->second);
+ if (!spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
+ iam.player->learnSpell(ranksItt->second);
+ else
+ iam.player->AddAura(ranksItt->second, iam.player);
+ }
fc->UpdateCharPoints(iam.player, curPoints);
fc->UpdateCharacterSpec(iam.player, spec);
diff --git a/modules/mod-Forge/src/TopicHandlers/UnlearnTalentHandler.cpp b/modules/mod-Forge/src/TopicHandlers/UnlearnTalentHandler.cpp
index a6be85b1e924ac..4b8728cda2e685 100644
--- a/modules/mod-Forge/src/TopicHandlers/UnlearnTalentHandler.cpp
+++ b/modules/mod-Forge/src/TopicHandlers/UnlearnTalentHandler.cpp
@@ -100,11 +100,27 @@ class UnlearnTalentHandler : public ForgeTopicHandler
sfp->Sum += refund;
sfp->Max -= refund;
- auto spellInfo = sSpellMgr->GetSpellInfo(spellId);
- if (spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
- iam.player->RemoveOwnedAura(tab->Talents[spellId]->Ranks[spellItt->second->CurrentRank]);
- else
- iam.player->removeSpell(tab->Talents[spellId]->Ranks[spellItt->second->CurrentRank], SPEC_MASK_ALL, false);
+ if (spellItt->second->type == NodeType::CHOICE) {
+ auto chosen = spec->ChoiceNodesChosen.find(spellId);
+
+ if (chosen == spec->ChoiceNodesChosen.end()) {
+ iam.player->SendForgeUIMsg(ForgeTopic::UNLEARN_TALENT_ERROR, "Attempted to forget unknown choice node talent.");
+ return;
+ }
+
+ auto spellInfo = sSpellMgr->GetSpellInfo(chosen->second);
+ if (spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
+ iam.player->RemoveOwnedAura(chosen->second);
+ else
+ iam.player->removeSpell(chosen->second, SPEC_MASK_ALL, false);
+ }
+ else {
+ auto spellInfo = sSpellMgr->GetSpellInfo(spellId);
+ if (spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
+ iam.player->RemoveOwnedAura(tab->Talents[spellId]->Ranks[spellItt->second->CurrentRank]);
+ else
+ iam.player->removeSpell(tab->Talents[spellId]->Ranks[spellItt->second->CurrentRank], SPEC_MASK_ALL, false);
+ }
iam.player->UpdateAllStats();
spellItt->second->CurrentRank = 0;