// AzerothAI: inspect actual trainer eligibility; never synthesize spell requirements. int GetClassTrainerStatus(lua_State* L, PlayerbotAI* botAI) { uint32 entry = ALE::CHECKVAL(L, 2); Player* bot = GetBotOrNull(botAI); auto* trainer = sObjectMgr->GetTrainer(entry); auto const* info = sObjectMgr->GetCreatureTemplate(entry); uint32 pending = 0, cheapest = 0, total = 0; if (bot && trainer && info && trainer->GetTrainerType() == Trainer::Type::Class && trainer->IsTrainerValidForPlayer(bot)) { auto const* faction = sFactionTemplateStore.LookupEntry(info->faction); float discount = faction ? bot->GetReputationPriceDiscount(faction) : 1.0f; for (auto const& spell : trainer->GetSpells()) { if (!trainer->CanTeachSpell(bot, &spell)) continue; uint32 cost = uint32(spell.MoneyCost * discount); if (!pending || cost < cheapest) cheapest = cost; ++pending; total += cost; } } ALE::Push(L, pending); ALE::Push(L, cheapest); ALE::Push(L, total); return 3; } // Same transaction as a player's trainer purchase, at an interactable class trainer. int TrainAtClassTrainer(lua_State* L, PlayerbotAI* botAI) { Creature* npc = ALE::CHECKOBJ(L, 2); Player* bot = GetBotOrNull(botAI); uint32 learned = 0; if (!bot || botAI->HasGameClientMaster() || !bot->IsAlive() || bot->IsInCombat() || bot->IsInFlight() || !npc || bot->GetNPCIfCanInteractWith(npc->GetGUID(), UNIT_NPC_FLAG_TRAINER) != npc) { ALE::Push(L, learned); return 1; } auto* trainer = sObjectMgr->GetTrainer(npc->GetEntry()); if (!trainer || trainer->GetTrainerType() != Trainer::Type::Class || !trainer->IsTrainerValidForPlayer(bot)) { ALE::Push(L, learned); return 1; } for (auto const& spell : trainer->GetSpells()) { if (!trainer->CanTeachSpell(bot, &spell)) continue; trainer->TeachSpell(npc, bot, spell.SpellId); if (!trainer->CanTeachSpell(bot, &spell)) ++learned; } ALE::Push(L, learned); return 1; }