Rewrite trainer handling to properly support multiple trainers on the same creature

This commit is contained in:
hondacrx
2017-07-27 17:29:22 -04:00
parent 0d8729ae32
commit 0aa7df2f53
18 changed files with 434 additions and 508 deletions
+2 -9
View File
@@ -860,11 +860,9 @@ namespace Game.Entities
return true;
}
public bool isCanTrainingAndResetTalentsOf(Player player)
public bool CanResetTalents(Player player)
{
return player.getLevel() >= 10
&& GetCreatureTemplate().TrainerType == TrainerType.Class
&& player.GetClass() == GetCreatureTemplate().TrainerClass;
return player.getLevel() >= 15 && player.GetClass() == GetCreatureTemplate().TrainerClass;
}
public void SetTextRepeatId(byte textGroup, byte id)
@@ -2457,11 +2455,6 @@ namespace Game.Entities
return vCount.count;
}
public TrainerSpellData GetTrainerSpells()
{
return Global.ObjectMgr.GetNpcTrainerSpells(GetEntry());
}
public override string GetName(LocaleConstant locale_idx = LocaleConstant.enUS)
{
if (locale_idx != LocaleConstant.enUS)
-36
View File
@@ -58,9 +58,7 @@ namespace Game.Entities
public uint UnitFlags3;
public uint DynamicFlags;
public CreatureFamily Family;
public TrainerType TrainerType;
public Class TrainerClass;
public Race TrainerRace;
public CreatureType CreatureType;
public CreatureTypeFlags TypeFlags;
public uint TypeFlags2;
@@ -386,38 +384,4 @@ namespace Game.Entities
m_items.Clear();
}
}
public class TrainerSpell
{
public uint SpellID;
public uint MoneyCost;
public uint ReqSkillLine;
public uint ReqSkillRank;
public uint ReqLevel;
public uint[] ReqAbility = new uint[SharedConst.MaxTrainerspellAbilityReqs];
public uint Index;
// helpers
public bool IsCastable()
{
return ReqAbility[0] != SpellID;
}
}
public class TrainerSpellData
{
public TrainerSpellData()
{
trainerType = 0;
}
public Dictionary<uint, TrainerSpell> spellList = new Dictionary<uint, TrainerSpell>();
public uint trainerType; // trainer type based at trainer spells, can be different from creature_template value.
// req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2.
public TrainerSpell Find(uint spell_id)
{
return spellList.LookupByKey(spell_id);
}
}
}
+23 -9
View File
@@ -30,22 +30,22 @@ namespace Game.Misc
{
public class GossipMenu
{
public void AddMenuItem(int menuItemId, GossipOptionIcon icon, string message, uint sender, uint action, string boxMessage, uint boxMoney, bool coded = false)
public uint AddMenuItem(int optionIndex, GossipOptionIcon icon, string message, uint sender, uint action, string boxMessage, uint boxMoney, bool coded = false)
{
Contract.Assert(_menuItems.Count <= SharedConst.MaxGossipMenuItems);
// Find a free new id - script case
if (menuItemId == -1)
if (optionIndex == -1)
{
menuItemId = 0;
optionIndex = 0;
if (!_menuItems.Empty())
{
foreach (var item in _menuItems)
{
if (item.Key > menuItemId)
if (item.Key > optionIndex)
break;
menuItemId = (int)item.Key + 1;
optionIndex = (int)item.Key + 1;
}
}
}
@@ -60,7 +60,8 @@ namespace Game.Misc
menuItem.BoxMessage = boxMessage;
menuItem.BoxMoney = boxMoney;
_menuItems[(uint)menuItemId] = menuItem;
_menuItems[(uint)optionIndex] = menuItem;
return (uint)optionIndex;
}
/// <summary>
@@ -124,18 +125,20 @@ namespace Game.Misc
}
// Add menu item with existing method. Menu item id -1 is also used in ADD_GOSSIP_ITEM macro.
AddMenuItem(-1, item.OptionIcon, strOptionText, sender, action, strBoxText, item.BoxMoney, item.BoxCoded);
uint optionIndex = AddMenuItem(-1, item.OptionIcon, strOptionText, sender, action, strBoxText, item.BoxMoney, item.BoxCoded);
AddGossipMenuItemData(optionIndex, item.ActionMenuId, item.ActionPoiId, item.TrainerId);
}
}
public void AddGossipMenuItemData(uint menuItemId, uint gossipActionMenuId, uint gossipActionPoi)
public void AddGossipMenuItemData(uint optionIndex, uint gossipActionMenuId, uint gossipActionPoi, uint trainerId)
{
GossipMenuItemData itemData = new GossipMenuItemData();
itemData.GossipActionMenuId = gossipActionMenuId;
itemData.GossipActionPoi = gossipActionPoi;
itemData.TrainerId = trainerId;
_menuItemData[menuItemId] = itemData;
_menuItemData[optionIndex] = itemData;
}
public uint GetMenuItemSender(uint menuItemId)
@@ -162,6 +165,15 @@ namespace Game.Misc
return false;
}
public bool HasMenuItemType(uint optionType)
{
foreach (var menuItemPair in _menuItems)
if (menuItemPair.Value.OptionType == optionType)
return true;
return false;
}
public void ClearMenu()
{
_menuItems.Clear();
@@ -796,6 +808,7 @@ namespace Game.Misc
{
public uint GossipActionMenuId; // MenuId of the gossip triggered by this action
public uint GossipActionPoi;
public uint TrainerId;
}
public struct NpcTextData
@@ -829,6 +842,7 @@ namespace Game.Misc
public uint BoxMoney;
public string BoxText;
public uint BoxBroadcastTextId;
public uint TrainerId;
public List<Condition> Conditions = new List<Condition>();
}
+167
View File
@@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Framework.Constants;
using Game.Network.Packets;
using Game.Spells;
namespace Game.Entities
{
public class TrainerSpell
{
public uint SpellId;
public uint MoneyCost;
public uint ReqSkillLine;
public uint ReqSkillRank;
public Array<uint> ReqAbility = new Array<uint>(3);
public byte ReqLevel;
public uint CastSpellId;
}
public class Trainer
{
public Trainer(uint id, TrainerType type, string greeting, List<TrainerSpell> spells)
{
_id = id;
_type = type;
_spells = spells;
_greeting[(int)LocaleConstant.enUS] = greeting;
}
public void SendSpells(Creature npc, Player player, LocaleConstant locale)
{
float reputationDiscount = player.GetReputationPriceDiscount(npc);
TrainerList trainerList = new TrainerList();
trainerList.TrainerGUID = npc.GetGUID();
trainerList.TrainerType = (int)_type;
trainerList.TrainerID = (int)_id;
trainerList.Greeting = GetGreeting(locale);
foreach (TrainerSpell trainerSpell in _spells)
{
if (!player.IsSpellFitByClassAndRace(trainerSpell.SpellId))
continue;
TrainerListSpell trainerListSpell = new TrainerListSpell();
trainerListSpell.SpellID = trainerSpell.SpellId;
trainerListSpell.MoneyCost = (uint)(trainerSpell.MoneyCost * reputationDiscount);
trainerListSpell.ReqSkillLine = trainerSpell.ReqSkillLine;
trainerListSpell.ReqSkillRank = trainerSpell.ReqSkillRank;
trainerListSpell.ReqAbility = trainerSpell.ReqAbility.ToArray();
trainerListSpell.Usable = GetSpellState(player, trainerSpell);
trainerListSpell.ReqLevel = trainerSpell.ReqLevel;
trainerList.Spells.Add(trainerListSpell);
}
player.SendPacket(trainerList);
}
public void TeachSpell(Creature npc, Player player, uint spellId)
{
TrainerSpell trainerSpell = GetSpell(spellId);
if (trainerSpell == null || !CanTeachSpell(player, trainerSpell))
{
SendTeachFailure(npc, player, spellId, TrainerFailReason.Unavailable);
return;
}
float reputationDiscount = player.GetReputationPriceDiscount(npc);
long moneyCost = (long)(trainerSpell.MoneyCost * reputationDiscount);
if (!player.HasEnoughMoney(moneyCost))
{
SendTeachFailure(npc, player, spellId, TrainerFailReason.NotEnoughMoney);
return;
}
player.ModifyMoney(-moneyCost);
npc.SendPlaySpellVisualKit(179, 0, 0); // 53 SpellCastDirected
player.SendPlaySpellVisualKit(362, 1, 0); // 113 EmoteSalute
// learn explicitly or cast explicitly
if (trainerSpell.CastSpellId != 0)
player.CastSpell(player, trainerSpell.CastSpellId, true);
else
player.LearnSpell(trainerSpell.SpellId, false);
}
TrainerSpell GetSpell(uint spellId)
{
return _spells.Find(trainerSpell => trainerSpell.SpellId == spellId);
}
bool CanTeachSpell(Player player, TrainerSpell trainerSpell)
{
TrainerSpellState state = GetSpellState(player, trainerSpell);
if (state != TrainerSpellState.Available)
return false;
SpellInfo trainerSpellInfo = Global.SpellMgr.GetSpellInfo(trainerSpell.SpellId);
if (trainerSpellInfo.IsPrimaryProfessionFirstRank() && player.GetFreePrimaryProfessionPoints() == 0)
return false;
return true;
}
TrainerSpellState GetSpellState(Player player, TrainerSpell trainerSpell)
{
if (player.HasSpell(trainerSpell.SpellId))
return TrainerSpellState.Known;
// check race/class requirement
if (!player.IsSpellFitByClassAndRace(trainerSpell.SpellId))
return TrainerSpellState.Unavailable;
// check skill requirement
if (trainerSpell.ReqSkillLine != 0 && player.GetBaseSkillValue((SkillType)trainerSpell.ReqSkillLine) < trainerSpell.ReqSkillRank)
return TrainerSpellState.Unavailable;
foreach (uint reqAbility in trainerSpell.ReqAbility)
if (reqAbility != 0 && !player.HasSpell(reqAbility))
return TrainerSpellState.Unavailable;
// check level requirement
if (player.getLevel() < trainerSpell.ReqLevel)
return TrainerSpellState.Unavailable;
// check additional spell requirement
foreach (var spellId in Global.SpellMgr.GetSpellsRequiredForSpellBounds(trainerSpell.SpellId))
if (!player.HasSpell(spellId))
return TrainerSpellState.Unavailable;
return TrainerSpellState.Available;
}
void SendTeachFailure(Creature npc, Player player, uint spellId, TrainerFailReason reason)
{
TrainerBuyFailed trainerBuyFailed = new TrainerBuyFailed();
trainerBuyFailed.TrainerGUID = npc.GetGUID();
trainerBuyFailed.SpellID = spellId;
trainerBuyFailed.TrainerFailedReason = reason;
player.SendPacket(trainerBuyFailed);
}
string GetGreeting(LocaleConstant locale)
{
if (_greeting[(int)locale].IsEmpty())
return _greeting[(int)LocaleConstant.enUS];
return _greeting[(int)locale];
}
public void AddGreetingLocale(LocaleConstant locale, string greeting)
{
_greeting[(int)locale] = greeting;
}
uint _id;
TrainerType _type;
List<TrainerSpell> _spells;
Array<string> _greeting = new Array<string>((int)LocaleConstant.Total);
}
}