This is a read only copy without any forum functionality of the old Modcraft forum.
If there is anything that you would like to have removed, message me on Discord via Kaev#5208.
Big thanks to Alastor for making this copy!

Menu

Author Topic: [TRINITY] Spells Scale from Level  (Read 3974 times)

Phabulous

  • Registred Member
  • BLP Convertor
  • *****
  • Posts: 11
    • View Profile
[TRINITY] Spells Scale from Level
« on: August 30, 2011, 05:38:34 am »
Ello Modcraft,

If anyone would be so kind as to help me, I'm trying my darndest to enable players to learn spells Cata-style, in that they learn a spell once and it scales as they level.
I think I MIGHT be able to retro-fit something like this from Skyfire to work, but my skills in C++ don't extend past modest boss encounters.

Could anyone assist me or point me to someone who could?
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

Laniax

  • Registred Member
  • Polygonshifter
  • *****
  • Posts: 62
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #1 on: August 30, 2011, 09:35:17 pm »
I think, from a realistic point of view, your best of to let players auto-learn spellranks.

Example:

Warrior level 50 learns mortal strike, and on level 53, the second rank of mortal strike is available. Normally the player would need to visit his/her class trainer to get this second rank, but you could teach it automatically on level-up.

Do your research, check how functions like Player::giveLevel work and Player::addSpell
« Last Edit: January 01, 1970, 01:00:00 am by Admin »
Current project: AIO Emulation solution.

Phabulous

  • Registred Member
  • BLP Convertor
  • *****
  • Posts: 11
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #2 on: September 14, 2011, 07:44:38 am »
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.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

Laniax

  • Registred Member
  • Polygonshifter
  • *****
  • Posts: 62
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #3 on: September 14, 2011, 05:04:34 pm »
Quote from: "Phabulous"
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.

Create a pointer to a unit which is a trainer that will be used to get the available spells from. So my advice is to make a global trainer which can learn every spell and create a Unit* pointer to him.

Then you can use unit->GetTrainerSpells() to get all the spells he learns, and then in a for loop you can check if each spell is available to be learned by player->GetTrainerSpellState.

Something like:

Code: [Select]

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
}

I only wrote this out of the top of my head, without an IDE so beware for errors, its just for pointing in the right direction.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »
Current project: AIO Emulation solution.

schlumpf

  • Administrator
  • Creator of Worlds
  • *****
  • Posts: 2967
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #4 on: September 14, 2011, 07:14:26 pm »
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.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

Laniax

  • Registred Member
  • Polygonshifter
  • *****
  • Posts: 62
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #5 on: September 16, 2011, 10:01:21 pm »
Quote from: "schlumpf"
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.

Interesting, i didn't know that function existed, yet you still need a source of potential spells. It's unrealistic to do a class and level check of every spell known in the dbcs. A virtual trainer isn't the best solution either but im not sure there is a way to get a class-only spellmap without checking every spell in the dbc. Please correct me if im wrong.
« Last Edit: January 01, 1970, 01:00:00 am by Admin »
Current project: AIO Emulation solution.

schlumpf

  • Administrator
  • Creator of Worlds
  • *****
  • Posts: 2967
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #6 on: September 16, 2011, 11:02:50 pm »
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 like

Code: [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);
  }
}
« Last Edit: January 01, 1970, 01:00:00 am by Admin »

Laniax

  • Registred Member
  • Polygonshifter
  • *****
  • Posts: 62
    • View Profile
Re: [TRINITY] Spells Scale from Level
« Reply #7 on: September 17, 2011, 02:51:35 pm »
Quote from: "schlumpf"
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 like

Code: [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);
  }
}

Then you have to create a virtual trainer for every class ingame. (thats 10 classes). Trinity bad designed trainer system does give us the opportunity to put all these classes inside 1 trainer. Not only will you end up with only 1 virtual trainer (instead of 10) but it is also already made for you. If were doing it a hacky way, atleast hack it 'cleanly'.

A global trainer, which is a trainer that can be used by every class. Now im not sure how they check the class condition in the trainer (haven't really checked the source) but you might want to include a classcheck in the core.

I'm not linking to other forums that are full of trolls, but this link should help you:
http://tinyurl.com/3k9hlqr
« Last Edit: January 01, 1970, 01:00:00 am by Admin »
Current project: AIO Emulation solution.