I haven't been able to find a pointer to check if a player has already learned a rank of a spell. I can add Mortal Strike to a warrior who reaches an appropriate level, but I haven't figured out how to check if the previous rank of a spell has already been learned before auto-learning the next rank. I appreciate the help but I'd also appreciate another nudge.
TrainerSpellData const& trainer_spells = unit->GetTrainerSpells();for (TrainerSpellMap::const_iterator itr = trainer_spells.spellList.begin(); itr != trainer_spells.spellList.end(); ++itr){// spell not found, cheat?TrainerSpell const* trainer_spell = trainer_spells->Find(itr->first);if (!trainer_spell) return;// learn nothing else then spells that are green(available) at the trainer.if (_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN) return; // then if thats good u can learn the spell here}
You may as well have a look at Player::GetTrainerSpellState(trainer_spell). That will look somewhere in Player, where you already are, if a spell is available. Therefore, there needs to be information inside of Player, if a spell is learnable. You shouldn't need to have a virtual trainer for doing this, being only indirection from Player to Trainer to Player.
QueryResult result = WorldDatabase.Query("SELECT b.entry, a.spell, a.spellcost, a.reqskill, a.reqskillvalue, a.reqlevel FROM npc_trainer AS a " "INNER JOIN npc_trainer AS b ON a.entry = -(b.spell) " "UNION SELECT * FROM npc_trainer WHERE spell > 0");
if (entry >= TRINITY_TRAINER_START_REF) return; CreatureInfo const* cInfo = GetCreatureTemplate(entry); if (!cInfo) { sLog->outErrorDb("Table `npc_trainer` contains an entry for a non-existing creature template (Entry: %u), ignoring", entry); return; } if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER)) { sLog->outErrorDb("Table `npc_trainer` contains an entry for a creature template (Entry: %u) without trainer flag, ignoring", entry); return; } SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell); if (!spellinfo) { sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u), ignoring", entry, spell); return; } if (!SpellMgr::IsSpellValid(spellinfo)) { sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a broken spell (Spell: %u), ignoring", entry, spell); return; } if (GetTalentSpellCost(spell)) { sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u) which is a talent, ignoring", entry, spell); return; }
void Player::updateSpellsOnLevelup() const{ const uint32 dummy_trainer_id_for_class (something()); TrainerSpellData const* trainer_spells (sObjectMgr->GetNpcTrainerSpells(dummy_trainer_id_for_class)); for ( TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin() ; itr != trainer_spells->spellList.end() ; ++itr ) { uint32 spellId (itr->first); // was this spell id? not sure. else get from trainer_spell. TrainerSpell const* trainer_spell = trainer_spells->Find (itr->first); if (!trainer_spell) continue; if (GetTrainerSpellState (trainer_spell) != TRAINER_SPELL_GREEN) continue; WorldPacket data (SMSG_PLAY_SPELL_VISUAL, 12); data << uint64 (GetGUID()); data << uint32 (0x016A); // index from SpellVisualKit.dbc SendPacket(&data); // learn explicitly or cast explicitly if (trainer_spell->IsCastable()) CastSpell(this, trainer_spell->spell, true); else learnSpell(spellId, false); }}
Well, as it seems after actually looking at trinity core, this is indeed the only way to do this. Sadly. They implement trainers as having a list of trainable stuff _each_, not per trainer type. (seems bad design to me).The checking if it's available to the player, is done by checking if the player knows that particular spell being learned and matching the parameters given in the trainer data.Therefore, the way to go is inserting a dummy trainer per class, but never spawning it.Code: [Select] QueryResult result = WorldDatabase.Query("SELECT b.entry, a.spell, a.spellcost, a.reqskill, a.reqskillvalue, a.reqlevel FROM npc_trainer AS a " "INNER JOIN npc_trainer AS b ON a.entry = -(b.spell) " "UNION SELECT * FROM npc_trainer WHERE spell > 0");This query as well as Code: [Select] if (entry >= TRINITY_TRAINER_START_REF) return; CreatureInfo const* cInfo = GetCreatureTemplate(entry); if (!cInfo) { sLog->outErrorDb("Table `npc_trainer` contains an entry for a non-existing creature template (Entry: %u), ignoring", entry); return; } if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER)) { sLog->outErrorDb("Table `npc_trainer` contains an entry for a creature template (Entry: %u) without trainer flag, ignoring", entry); return; } SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell); if (!spellinfo) { sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u), ignoring", entry, spell); return; } if (!SpellMgr::IsSpellValid(spellinfo)) { sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a broken spell (Spell: %u), ignoring", entry, spell); return; } if (GetTalentSpellCost(spell)) { sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u) which is a talent, ignoring", entry, spell); return; }needs to be fulfilled.You sadly need to assemble lists of all learnable spells per class yourself. You may copy that together from all trainers having the same type.You then _not_ create a unit for this but you only ask const TrainerSpellData* sObjectMgr->GetNpcTrainerSpells(dummy_trainer_id_for_class);There, you now iterate over the entries as Laniax said.you should end up with something likeCode: [Select]void Player::updateSpellsOnLevelup() const{ const uint32 dummy_trainer_id_for_class (something()); TrainerSpellData const* trainer_spells (sObjectMgr->GetNpcTrainerSpells(dummy_trainer_id_for_class)); for ( TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin() ; itr != trainer_spells->spellList.end() ; ++itr ) { uint32 spellId (itr->first); // was this spell id? not sure. else get from trainer_spell. TrainerSpell const* trainer_spell = trainer_spells->Find (itr->first); if (!trainer_spell) continue; if (GetTrainerSpellState (trainer_spell) != TRAINER_SPELL_GREEN) continue; WorldPacket data (SMSG_PLAY_SPELL_VISUAL, 12); data << uint64 (GetGUID()); data << uint32 (0x016A); // index from SpellVisualKit.dbc SendPacket(&data); // learn explicitly or cast explicitly if (trainer_spell->IsCastable()) CastSpell(this, trainer_spell->spell, true); else learnSpell(spellId, false); }}