Ported .Net Core commits:
hondacrx: - Initial commit: Switch to .Net Core 2.0 - Fix build and removed not needed files Fabi: - Updated solution platforms. - Changed folder structure. - Change library target framework to netstandard2.0. - Updated solution platforms again... - Removed windows specific kernel32 function usage (Ctrl-C handler).
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Game.Loots;
|
||||
using Game.Spells;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
public partial class Creature
|
||||
{
|
||||
CreatureTemplate m_creatureInfo;
|
||||
CreatureData m_creatureData;
|
||||
|
||||
Spell _focusSpell; // Locks the target during spell cast for proper facing
|
||||
uint _focusDelay;
|
||||
bool m_shouldReacquireTarget;
|
||||
ObjectGuid m_suppressedTarget; // Stores the creature's "real" target while casting
|
||||
float m_suppressedOrientation; // Stores the creature's "real" orientation while casting
|
||||
|
||||
MultiMap<byte, byte> m_textRepeat = new MultiMap<byte, byte>();
|
||||
|
||||
public ulong m_PlayerDamageReq;
|
||||
public float m_SightDistance;
|
||||
public float m_CombatDistance;
|
||||
public bool m_isTempWorldObject; //true when possessed
|
||||
|
||||
ReactStates reactState; // for AI, not charmInfo
|
||||
public MovementGeneratorType m_defaultMovementType { get; set; }
|
||||
public ulong m_spawnId;
|
||||
byte m_equipmentId;
|
||||
sbyte m_originalEquipmentId; // can be -1
|
||||
|
||||
bool m_AlreadyCallAssistance;
|
||||
bool m_AlreadySearchedAssistance;
|
||||
bool m_regenHealth;
|
||||
bool m_cannotReachTarget;
|
||||
uint m_cannotReachTimer;
|
||||
bool m_AI_locked;
|
||||
|
||||
SpellSchoolMask m_meleeDamageSchoolMask;
|
||||
public uint m_originalEntry;
|
||||
|
||||
Position m_homePosition;
|
||||
Position m_transportHomePosition = new Position();
|
||||
|
||||
bool DisableReputationGain;
|
||||
|
||||
LootModes m_LootMode; // Bitmask (default: LOOT_MODE_DEFAULT) that determines what loot will be lootable
|
||||
|
||||
//WaypointMovementGenerator vars
|
||||
uint m_waypointID;
|
||||
uint m_path_id;
|
||||
|
||||
//Formation var
|
||||
CreatureGroup m_formation;
|
||||
bool TriggerJustRespawned;
|
||||
|
||||
public uint[] m_spells = new uint[SharedConst.MaxCreatureSpells];
|
||||
|
||||
// Timers
|
||||
long _pickpocketLootRestore;
|
||||
public long m_corpseRemoveTime; // (msecs)timer for death or corpse disappearance
|
||||
long m_respawnTime; // (secs) time of next respawn
|
||||
uint m_respawnDelay; // (secs) delay between corpse disappearance and respawning
|
||||
uint m_corpseDelay; // (secs) delay between death and corpse disappearance
|
||||
float m_respawnradius;
|
||||
uint m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check
|
||||
uint m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse
|
||||
uint m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons)
|
||||
|
||||
// vendor items
|
||||
List<VendorItemCount> m_vendorItemCounts = new List<VendorItemCount>();
|
||||
|
||||
public Loot loot = new Loot();
|
||||
public uint m_groupLootTimer; // (msecs)timer used for group loot
|
||||
public ObjectGuid lootingGroupLowGUID; // used to find group which is looting corpse
|
||||
ObjectGuid m_lootRecipient;
|
||||
ObjectGuid m_lootRecipientGroup;
|
||||
ObjectGuid _skinner;
|
||||
}
|
||||
|
||||
public enum ObjectCellMoveState
|
||||
{
|
||||
None, // not in move list
|
||||
Active, // in move list
|
||||
Inactive // in move list but should not move
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Collections;
|
||||
using Framework.Constants;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Framework.Dynamic;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
public class CreatureTemplate
|
||||
{
|
||||
public uint Entry;
|
||||
public uint[] DifficultyEntry = new uint[SharedConst.MaxCreatureDifficulties];
|
||||
public uint[] KillCredit = new uint[SharedConst.MaxCreatureKillCredit];
|
||||
public uint ModelId1;
|
||||
public uint ModelId2;
|
||||
public uint ModelId3;
|
||||
public uint ModelId4;
|
||||
public string Name;
|
||||
public string FemaleName;
|
||||
public string SubName;
|
||||
public string IconName;
|
||||
public uint GossipMenuId;
|
||||
public short Minlevel;
|
||||
public Optional<CreatureLevelScaling> levelScaling;
|
||||
public short Maxlevel;
|
||||
public int HealthScalingExpansion;
|
||||
public uint RequiredExpansion;
|
||||
public uint VignetteID; // @todo Read Vignette.db2
|
||||
public uint Faction;
|
||||
public NPCFlags Npcflag;
|
||||
public float SpeedWalk;
|
||||
public float SpeedRun;
|
||||
public float Scale;
|
||||
public CreatureEliteType Rank;
|
||||
public uint DmgSchool;
|
||||
public uint BaseAttackTime;
|
||||
public uint RangeAttackTime;
|
||||
public float BaseVariance;
|
||||
public float RangeVariance;
|
||||
public uint UnitClass;
|
||||
public UnitFlags UnitFlags;
|
||||
public uint UnitFlags2;
|
||||
public uint UnitFlags3;
|
||||
public uint DynamicFlags;
|
||||
public CreatureFamily Family;
|
||||
public Class TrainerClass;
|
||||
public CreatureType CreatureType;
|
||||
public CreatureTypeFlags TypeFlags;
|
||||
public uint TypeFlags2;
|
||||
public uint LootId;
|
||||
public uint PickPocketId;
|
||||
public uint SkinLootId;
|
||||
public int[] Resistance = new int[7];
|
||||
public uint[] Spells = new uint[8];
|
||||
public uint VehicleId;
|
||||
public uint MinGold;
|
||||
public uint MaxGold;
|
||||
public string AIName;
|
||||
public uint MovementType;
|
||||
public InhabitType InhabitType;
|
||||
public float HoverHeight;
|
||||
public float ModHealth;
|
||||
public float ModHealthExtra;
|
||||
public float ModMana;
|
||||
public float ModManaExtra;
|
||||
public float ModArmor;
|
||||
public float ModDamage;
|
||||
public float ModExperience;
|
||||
public bool RacialLeader;
|
||||
public uint MovementId;
|
||||
public bool RegenHealth;
|
||||
public uint MechanicImmuneMask;
|
||||
public CreatureFlagsExtra FlagsExtra;
|
||||
public uint ScriptID;
|
||||
|
||||
public uint GetRandomValidModelId()
|
||||
{
|
||||
byte c = 0;
|
||||
uint[] modelIDs = new uint[4];
|
||||
|
||||
if (ModelId1 != 0)
|
||||
modelIDs[c++] = ModelId1;
|
||||
if (ModelId2 != 0)
|
||||
modelIDs[c++] = ModelId2;
|
||||
if (ModelId3 != 0)
|
||||
modelIDs[c++] = ModelId3;
|
||||
if (ModelId4 != 0)
|
||||
modelIDs[c++] = ModelId4;
|
||||
|
||||
return c > 0 ? modelIDs[RandomHelper.IRand(0, c - 1)] : 0;
|
||||
}
|
||||
public uint GetFirstValidModelId()
|
||||
{
|
||||
if (ModelId1 != 0)
|
||||
return ModelId1;
|
||||
if (ModelId2 != 0)
|
||||
return ModelId2;
|
||||
if (ModelId3 != 0)
|
||||
return ModelId3;
|
||||
if (ModelId4 != 0)
|
||||
return ModelId4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint GetFirstInvisibleModel()
|
||||
{
|
||||
CreatureModelInfo modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId1);
|
||||
if (modelInfo != null && modelInfo.IsTrigger)
|
||||
return ModelId1;
|
||||
|
||||
modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId2);
|
||||
if (modelInfo != null && modelInfo.IsTrigger)
|
||||
return ModelId2;
|
||||
|
||||
modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId3);
|
||||
if (modelInfo != null && modelInfo.IsTrigger)
|
||||
return ModelId3;
|
||||
|
||||
modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId4);
|
||||
if (modelInfo != null && modelInfo.IsTrigger)
|
||||
return ModelId4;
|
||||
|
||||
return 11686;
|
||||
}
|
||||
|
||||
public uint GetFirstVisibleModel()
|
||||
{
|
||||
CreatureModelInfo modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId1);
|
||||
if (modelInfo != null && !modelInfo.IsTrigger)
|
||||
return ModelId1;
|
||||
|
||||
modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId2);
|
||||
if (modelInfo != null && !modelInfo.IsTrigger)
|
||||
return ModelId2;
|
||||
|
||||
modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId3);
|
||||
if (modelInfo != null && !modelInfo.IsTrigger)
|
||||
return ModelId3;
|
||||
|
||||
modelInfo = Global.ObjectMgr.GetCreatureModelInfo(ModelId4);
|
||||
if (modelInfo != null && !modelInfo.IsTrigger)
|
||||
return ModelId4;
|
||||
|
||||
return 17519;
|
||||
}
|
||||
|
||||
public SkillType GetRequiredLootSkill()
|
||||
{
|
||||
if (TypeFlags.HasAnyFlag(CreatureTypeFlags.HerbSkinningSkill))
|
||||
return SkillType.Herbalism;
|
||||
else if (TypeFlags.HasAnyFlag(CreatureTypeFlags.MiningSkinningSkill))
|
||||
return SkillType.Mining;
|
||||
else if (TypeFlags.HasAnyFlag(CreatureTypeFlags.EngineeringSkinningSkill))
|
||||
return SkillType.Engineering;
|
||||
else
|
||||
return SkillType.Skinning; // normal case
|
||||
}
|
||||
|
||||
public bool IsExotic()
|
||||
{
|
||||
return (TypeFlags & CreatureTypeFlags.ExoticPet) != 0;
|
||||
}
|
||||
public bool IsTameable(bool canTameExotic)
|
||||
{
|
||||
if (CreatureType != CreatureType.Beast || Family == CreatureFamily.None || !TypeFlags.HasAnyFlag(CreatureTypeFlags.TameablePet))
|
||||
return false;
|
||||
|
||||
// if can tame exotic then can tame any tameable
|
||||
return canTameExotic || !IsExotic();
|
||||
}
|
||||
|
||||
public static int DifficultyIDToDifficultyEntryIndex(uint difficulty)
|
||||
{
|
||||
switch ((Difficulty)difficulty)
|
||||
{
|
||||
case Difficulty.None:
|
||||
case Difficulty.Normal:
|
||||
case Difficulty.Raid10N:
|
||||
case Difficulty.Raid40:
|
||||
case Difficulty.Scenario3ManN:
|
||||
case Difficulty.NormalRaid:
|
||||
return -1;
|
||||
case Difficulty.Heroic:
|
||||
case Difficulty.Raid25N:
|
||||
case Difficulty.Scenario3ManHC:
|
||||
case Difficulty.HeroicRaid:
|
||||
return 0;
|
||||
case Difficulty.Raid10HC:
|
||||
case Difficulty.MythicKeystone:
|
||||
case Difficulty.MythicRaid:
|
||||
return 1;
|
||||
case Difficulty.Raid25HC:
|
||||
return 2;
|
||||
case Difficulty.LFR:
|
||||
case Difficulty.LFRNew:
|
||||
case Difficulty.EventRaid:
|
||||
case Difficulty.EventDungeon:
|
||||
case Difficulty.EventScenario:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CreatureBaseStats
|
||||
{
|
||||
public uint[] BaseHealth = new uint[(int)Expansion.Max];
|
||||
public uint BaseMana;
|
||||
public uint BaseArmor;
|
||||
public uint AttackPower;
|
||||
public uint RangedAttackPower;
|
||||
public float[] BaseDamage = new float[(int)Expansion.Max];
|
||||
|
||||
// Helpers
|
||||
public uint GenerateHealth(CreatureTemplate info)
|
||||
{
|
||||
return (uint)Math.Ceiling(BaseHealth[info.HealthScalingExpansion] * info.ModHealth * info.ModHealthExtra);
|
||||
}
|
||||
|
||||
public uint GenerateMana(CreatureTemplate info)
|
||||
{
|
||||
// Mana can be 0.
|
||||
if (BaseMana == 0)
|
||||
return 0;
|
||||
|
||||
return (uint)Math.Ceiling(BaseMana * info.ModMana * info.ModManaExtra);
|
||||
}
|
||||
|
||||
public float GenerateArmor(CreatureTemplate info)
|
||||
{
|
||||
return (float)Math.Ceiling(BaseArmor * info.ModArmor);
|
||||
}
|
||||
|
||||
public float GenerateBaseDamage(CreatureTemplate info)
|
||||
{
|
||||
return BaseDamage[info.HealthScalingExpansion];
|
||||
}
|
||||
}
|
||||
|
||||
public class CreatureLocale
|
||||
{
|
||||
public StringArray Name = new StringArray((int)LocaleConstant.Total);
|
||||
public StringArray NameAlt = new StringArray((int)LocaleConstant.Total);
|
||||
public StringArray Title = new StringArray((int)LocaleConstant.Total);
|
||||
public StringArray TitleAlt = new StringArray((int)LocaleConstant.Total);
|
||||
}
|
||||
|
||||
public struct EquipmentItem
|
||||
{
|
||||
public uint ItemId;
|
||||
public ushort AppearanceModId;
|
||||
public ushort ItemVisual;
|
||||
}
|
||||
|
||||
public class EquipmentInfo
|
||||
{
|
||||
public EquipmentItem[] Items = new EquipmentItem[SharedConst.MaxEquipmentItems];
|
||||
}
|
||||
|
||||
public class CreatureData
|
||||
{
|
||||
public uint id; // entry in creature_template
|
||||
public ushort mapid;
|
||||
public uint phaseMask;
|
||||
public uint displayid;
|
||||
public int equipmentId;
|
||||
public float posX;
|
||||
public float posY;
|
||||
public float posZ;
|
||||
public float orientation;
|
||||
public uint spawntimesecs;
|
||||
public float spawndist;
|
||||
public uint currentwaypoint;
|
||||
public uint curhealth;
|
||||
public uint curmana;
|
||||
public byte movementType;
|
||||
public uint spawnMask;
|
||||
public ulong npcflag;
|
||||
public uint unit_flags; // enum UnitFlags mask values
|
||||
public uint unit_flags2; // enum UnitFlags2 mask values
|
||||
public uint unit_flags3; // enum UnitFlags3 mask values
|
||||
public uint dynamicflags;
|
||||
public uint phaseid;
|
||||
public uint phaseGroup;
|
||||
public uint ScriptId;
|
||||
public bool dbData;
|
||||
}
|
||||
|
||||
public class CreatureModelInfo
|
||||
{
|
||||
public float BoundingRadius;
|
||||
public float CombatReach;
|
||||
public sbyte gender;
|
||||
public uint DisplayIdOtherGender;
|
||||
public bool IsTrigger;
|
||||
}
|
||||
|
||||
public class CreatureAddon
|
||||
{
|
||||
public uint path_id;
|
||||
public uint mount;
|
||||
public uint bytes1;
|
||||
public uint bytes2;
|
||||
public uint emote;
|
||||
public ushort aiAnimKit;
|
||||
public ushort movementAnimKit;
|
||||
public ushort meleeAnimKit;
|
||||
public uint[] auras;
|
||||
}
|
||||
|
||||
public class VendorItem
|
||||
{
|
||||
public VendorItem() { }
|
||||
public VendorItem(uint _item, int _maxcount, uint _incrtime, uint _ExtendedCost, ItemVendorType _Type)
|
||||
{
|
||||
item = _item;
|
||||
maxcount = (uint)_maxcount;
|
||||
incrtime = _incrtime;
|
||||
ExtendedCost = _ExtendedCost;
|
||||
Type = _Type;
|
||||
}
|
||||
|
||||
public uint item;
|
||||
public uint maxcount; // 0 for infinity item amount
|
||||
public uint incrtime; // time for restore items amount if maxcount != 0
|
||||
public uint ExtendedCost;
|
||||
public ItemVendorType Type;
|
||||
public List<uint> BonusListIDs = new List<uint>();
|
||||
public uint PlayerConditionId;
|
||||
public bool IgnoreFiltering;
|
||||
|
||||
//helpers
|
||||
public bool IsGoldRequired(ItemTemplate pProto) { return Convert.ToBoolean(pProto.GetFlags2() & ItemFlags2.DontIgnoreBuyPrice) || ExtendedCost == 0; }
|
||||
}
|
||||
|
||||
public class VendorItemData
|
||||
{
|
||||
List<VendorItem> m_items = new List<VendorItem>();
|
||||
|
||||
public VendorItem GetItem(uint slot)
|
||||
{
|
||||
if (slot >= m_items.Count)
|
||||
return null;
|
||||
|
||||
return m_items[(int)slot];
|
||||
}
|
||||
public bool Empty()
|
||||
{
|
||||
return m_items.Count == 0;
|
||||
}
|
||||
public int GetItemCount()
|
||||
{
|
||||
return m_items.Count;
|
||||
}
|
||||
public void AddItem(VendorItem vItem)
|
||||
{
|
||||
m_items.Add(vItem);
|
||||
}
|
||||
public bool RemoveItem(uint item_id, ItemVendorType type)
|
||||
{
|
||||
int i = m_items.RemoveAll(p => p.item == item_id && p.Type == type);
|
||||
if (i == 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
public VendorItem FindItemCostPair(uint item_id, uint extendedCost, ItemVendorType type)
|
||||
{
|
||||
return m_items.Find(p => p.item == item_id && p.ExtendedCost == extendedCost && p.Type == type);
|
||||
}
|
||||
public void Clear()
|
||||
{
|
||||
m_items.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public struct CreatureLevelScaling
|
||||
{
|
||||
public ushort MinLevel;
|
||||
public ushort MaxLevel;
|
||||
public short DeltaLevel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Database;
|
||||
using Game.Maps;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Framework.Constants;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
public class FormationMgr
|
||||
{
|
||||
public FormationMgr() { }
|
||||
|
||||
public static void AddCreatureToGroup(uint groupId, Creature member)
|
||||
{
|
||||
Map map = member.GetMap();
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
var creatureGroup = map.CreatureGroupHolder.LookupByKey(groupId);
|
||||
|
||||
//Add member to an existing group
|
||||
if (creatureGroup != null)
|
||||
{
|
||||
Log.outDebug(LogFilter.Unit, "Group found: {0}, inserting creature GUID: {1}, Group InstanceID {2}", groupId, member.GetGUID().ToString(), member.GetInstanceId());
|
||||
creatureGroup.AddMember(member);
|
||||
}
|
||||
//Create new group
|
||||
else
|
||||
{
|
||||
Log.outDebug(LogFilter.Unit, "Group not found: {0}. Creating new group.", groupId);
|
||||
CreatureGroup group = new CreatureGroup(groupId);
|
||||
map.CreatureGroupHolder[groupId] = group;
|
||||
group.AddMember(member);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveCreatureFromGroup(CreatureGroup group, Creature member)
|
||||
{
|
||||
Log.outDebug(LogFilter.Unit, "Deleting member GUID: {0} from group {1}", group.GetId(), member.GetSpawnId());
|
||||
group.RemoveMember(member);
|
||||
|
||||
if (group.isEmpty())
|
||||
{
|
||||
Map map = member.GetMap();
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
Log.outDebug(LogFilter.Unit, "Deleting group with InstanceID {0}", member.GetInstanceId());
|
||||
map.CreatureGroupHolder.Remove(group.GetId());
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadCreatureFormations()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
CreatureGroupMap.Clear();
|
||||
|
||||
//Get group data
|
||||
SQLResult result = DB.World.Query("SELECT leaderGUID, memberGUID, dist, angle, groupAI, point_1, point_2 FROM creature_formations ORDER BY leaderGUID");
|
||||
|
||||
if (result.IsEmpty())
|
||||
{
|
||||
Log.outError(LogFilter.ServerLoading, "Loaded 0 creatures in formations. DB table `creature_formations` is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
uint count = 0;
|
||||
FormationInfo group_member;
|
||||
do
|
||||
{
|
||||
//Load group member data
|
||||
group_member = new FormationInfo();
|
||||
group_member.leaderGUID = result.Read<uint>(0);
|
||||
uint memberGUID = result.Read<uint>(1);
|
||||
group_member.groupAI = result.Read<uint>(4);
|
||||
group_member.point_1 = result.Read<ushort>(5);
|
||||
group_member.point_2 = result.Read<ushort>(6);
|
||||
//If creature is group leader we may skip loading of dist/angle
|
||||
if (group_member.leaderGUID != memberGUID)
|
||||
{
|
||||
group_member.follow_dist = result.Read<float>(2);
|
||||
group_member.follow_angle = result.Read<float>(3) * MathFunctions.PI / 180;
|
||||
}
|
||||
else
|
||||
{
|
||||
group_member.follow_dist = 0;
|
||||
group_member.follow_angle = 0;
|
||||
}
|
||||
|
||||
// check data correctness
|
||||
{
|
||||
if (Global.ObjectMgr.GetCreatureData(group_member.leaderGUID) == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "creature_formations table leader guid {0} incorrect (not exist)", group_member.leaderGUID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Global.ObjectMgr.GetCreatureData(memberGUID) == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "creature_formations table member guid {0} incorrect (not exist)", memberGUID);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureGroupMap[memberGUID] = group_member;
|
||||
++count;
|
||||
}
|
||||
while (result.NextRow());
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} creatures in formations in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
public static Dictionary<ulong, FormationInfo> CreatureGroupMap = new Dictionary<ulong, FormationInfo>();
|
||||
}
|
||||
|
||||
public class FormationInfo
|
||||
{
|
||||
public uint leaderGUID;
|
||||
public float follow_dist;
|
||||
public float follow_angle;
|
||||
public uint groupAI;
|
||||
public ushort point_1;
|
||||
public ushort point_2;
|
||||
}
|
||||
|
||||
public class CreatureGroup
|
||||
{
|
||||
public CreatureGroup(uint id)
|
||||
{
|
||||
m_groupID = id;
|
||||
}
|
||||
|
||||
public void AddMember(Creature member)
|
||||
{
|
||||
Log.outDebug(LogFilter.Unit, "CreatureGroup.AddMember: Adding unit GUID: {0}.", member.GetGUID().ToString());
|
||||
|
||||
//Check if it is a leader
|
||||
if (member.GetSpawnId() == m_groupID)
|
||||
{
|
||||
Log.outDebug(LogFilter.Unit, "Unit GUID: {0} is formation leader. Adding group.", member.GetGUID().ToString());
|
||||
m_leader = member;
|
||||
}
|
||||
|
||||
m_members[member] = FormationMgr.CreatureGroupMap.LookupByKey(member.GetSpawnId());
|
||||
member.SetFormation(this);
|
||||
}
|
||||
|
||||
public void RemoveMember(Creature member)
|
||||
{
|
||||
if (m_leader == member)
|
||||
m_leader = null;
|
||||
|
||||
m_members.Remove(member);
|
||||
member.SetFormation(null);
|
||||
}
|
||||
|
||||
public void MemberAttackStart(Creature member, Unit target)
|
||||
{
|
||||
GroupAIFlags groupAI = (GroupAIFlags)FormationMgr.CreatureGroupMap[member.GetSpawnId()].groupAI;
|
||||
if (groupAI == 0)
|
||||
return;
|
||||
|
||||
if (member == m_leader)
|
||||
{
|
||||
if (!groupAI.HasAnyFlag(GroupAIFlags.MembersAssistLeader))
|
||||
return;
|
||||
}
|
||||
else if (!groupAI.HasAnyFlag(GroupAIFlags.LeaderAssistsMember))
|
||||
return;
|
||||
|
||||
foreach (var pair in m_members)
|
||||
{
|
||||
if (m_leader) // avoid crash if leader was killed and reset.
|
||||
Log.outDebug(LogFilter.Unit, "GROUP ATTACK: group instance id {0} calls member instid {1}", m_leader.GetInstanceId(), member.GetInstanceId());
|
||||
|
||||
Creature other = pair.Key;
|
||||
|
||||
// Skip self
|
||||
if (other == member)
|
||||
continue;
|
||||
|
||||
if (!other.IsAlive())
|
||||
continue;
|
||||
|
||||
if (other.GetVictim())
|
||||
continue;
|
||||
|
||||
if (((other != m_leader && groupAI.HasAnyFlag(GroupAIFlags.MembersAssistLeader)) || (other == m_leader && groupAI.HasAnyFlag(GroupAIFlags.LeaderAssistsMember))) && other.IsValidAttackTarget(target))
|
||||
other.GetAI().AttackStart(target);
|
||||
}
|
||||
}
|
||||
|
||||
public void FormationReset(bool dismiss)
|
||||
{
|
||||
foreach (var creature in m_members.Keys)
|
||||
{
|
||||
if (creature != m_leader && creature.IsAlive())
|
||||
{
|
||||
if (dismiss)
|
||||
creature.GetMotionMaster().Initialize();
|
||||
else
|
||||
creature.GetMotionMaster().MoveIdle();
|
||||
Log.outDebug(LogFilter.Unit, "Set {0} movement for member GUID: {1}", dismiss ? "default" : "idle", creature.GetGUID().ToString());
|
||||
}
|
||||
}
|
||||
m_Formed = !dismiss;
|
||||
}
|
||||
|
||||
public void LeaderMoveTo(float x, float y, float z)
|
||||
{
|
||||
//! To do: This should probably get its own movement generator or use WaypointMovementGenerator.
|
||||
//! If the leader's path is known, member's path can be plotted as well using formation offsets.
|
||||
if (!m_leader)
|
||||
return;
|
||||
|
||||
float pathangle = (float)Math.Atan2(m_leader.GetPositionY() - y, m_leader.GetPositionX() - x);
|
||||
|
||||
foreach (var pair in m_members)
|
||||
{
|
||||
Creature member = pair.Key;
|
||||
if (member == m_leader || !member.IsAlive() || member.GetVictim() || !pair.Value.groupAI.HasAnyFlag((uint)GroupAIFlags.IdleInFormation))
|
||||
continue;
|
||||
|
||||
if (pair.Value.point_1 != 0)
|
||||
if (m_leader.GetCurrentWaypointID() == pair.Value.point_1 - 1 || m_leader.GetCurrentWaypointID() == pair.Value.point_2 - 1)
|
||||
pair.Value.follow_angle = (float)Math.PI * 2 - pair.Value.follow_angle;
|
||||
|
||||
float angle = pair.Value.follow_angle;
|
||||
float dist = pair.Value.follow_dist;
|
||||
|
||||
float dx = x + (float)Math.Cos(angle + pathangle) * dist;
|
||||
float dy = y + (float)Math.Sin(angle + pathangle) * dist;
|
||||
float dz = z;
|
||||
|
||||
GridDefines.NormalizeMapCoord(ref dx);
|
||||
GridDefines.NormalizeMapCoord(ref dy);
|
||||
|
||||
if (!member.IsFlying())
|
||||
member.UpdateGroundPositionZ(dx, dy, ref dz);
|
||||
|
||||
if (member.IsWithinDist(m_leader, dist + 5.0f))
|
||||
member.SetUnitMovementFlags(m_leader.GetUnitMovementFlags());
|
||||
else
|
||||
member.SetWalk(false);
|
||||
|
||||
member.GetMotionMaster().MovePoint(0, dx, dy, dz);
|
||||
member.SetHomePosition(dx, dy, dz, pathangle);
|
||||
}
|
||||
}
|
||||
|
||||
public Creature getLeader() { return m_leader; }
|
||||
public uint GetId() { return m_groupID; }
|
||||
public bool isEmpty() { return m_members.Empty(); }
|
||||
public bool isFormed() { return m_Formed; }
|
||||
|
||||
Creature m_leader;
|
||||
Dictionary<Creature, FormationInfo> m_members = new Dictionary<Creature, FormationInfo>();
|
||||
|
||||
uint m_groupID;
|
||||
bool m_Formed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Collections;
|
||||
using Framework.Constants;
|
||||
using Framework.GameMath;
|
||||
using Game.Conditions;
|
||||
using Game.DataStorage;
|
||||
using Game.Entities;
|
||||
using Game.Network.Packets;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
|
||||
namespace Game.Misc
|
||||
{
|
||||
public class GossipMenu
|
||||
{
|
||||
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 (optionIndex == -1)
|
||||
{
|
||||
optionIndex = 0;
|
||||
if (!_menuItems.Empty())
|
||||
{
|
||||
foreach (var item in _menuItems)
|
||||
{
|
||||
if (item.Key > optionIndex)
|
||||
break;
|
||||
|
||||
optionIndex = (int)item.Key + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GossipMenuItem menuItem = new GossipMenuItem();
|
||||
|
||||
menuItem.MenuItemIcon = (byte)icon;
|
||||
menuItem.Message = message;
|
||||
menuItem.IsCoded = coded;
|
||||
menuItem.Sender = sender;
|
||||
menuItem.OptionType = action;
|
||||
menuItem.BoxMessage = boxMessage;
|
||||
menuItem.BoxMoney = boxMoney;
|
||||
|
||||
_menuItems[(uint)optionIndex] = menuItem;
|
||||
return (uint)optionIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a localized gossip menu item from db by menu id and menu item id.
|
||||
/// </summary>
|
||||
/// <param name="menuId">menuId Gossip menu id.</param>
|
||||
/// <param name="menuItemId">menuItemId Gossip menu item id.</param>
|
||||
/// <param name="sender">sender Identifier of the current menu.</param>
|
||||
/// <param name="action">action Custom action given to OnGossipHello.</param>
|
||||
public void AddMenuItem(uint menuId, uint menuItemId, uint sender, uint action)
|
||||
{
|
||||
/// Find items for given menu id.
|
||||
var bounds = Global.ObjectMgr.GetGossipMenuItemsMapBounds(menuId);
|
||||
/// Return if there are none.
|
||||
if (bounds.Empty())
|
||||
return;
|
||||
|
||||
/// Iterate over each of them.
|
||||
foreach (var item in bounds)
|
||||
{
|
||||
// Find the one with the given menu item id.
|
||||
if (item.OptionIndex != menuItemId)
|
||||
continue;
|
||||
|
||||
/// Store texts for localization.
|
||||
string strOptionText = "", strBoxText = "";
|
||||
BroadcastTextRecord optionBroadcastText = CliDB.BroadcastTextStorage.LookupByKey(item.OptionBroadcastTextId);
|
||||
BroadcastTextRecord boxBroadcastText = CliDB.BroadcastTextStorage.LookupByKey(item.BoxBroadcastTextId);
|
||||
|
||||
// OptionText
|
||||
if (optionBroadcastText != null)
|
||||
strOptionText = Global.DB2Mgr.GetBroadcastTextValue(optionBroadcastText, GetLocale());
|
||||
else
|
||||
strOptionText = item.OptionText;
|
||||
|
||||
// BoxText
|
||||
if (boxBroadcastText != null)
|
||||
strBoxText = Global.DB2Mgr.GetBroadcastTextValue(boxBroadcastText, GetLocale());
|
||||
else
|
||||
strBoxText = item.BoxText;
|
||||
|
||||
// Check need of localization.
|
||||
if (GetLocale() != LocaleConstant.enUS)
|
||||
{
|
||||
if (optionBroadcastText == null)
|
||||
{
|
||||
// Find localizations from database.
|
||||
GossipMenuItemsLocale gossipMenuLocale = Global.ObjectMgr.GetGossipMenuItemsLocale(menuId, menuItemId);
|
||||
if (gossipMenuLocale != null)
|
||||
ObjectManager.GetLocaleString(gossipMenuLocale.OptionText, GetLocale(), ref strOptionText);
|
||||
}
|
||||
|
||||
if (boxBroadcastText == null)
|
||||
{
|
||||
// Find localizations from database.
|
||||
GossipMenuItemsLocale gossipMenuLocale = Global.ObjectMgr.GetGossipMenuItemsLocale(menuId, menuItemId);
|
||||
if (gossipMenuLocale != null)
|
||||
ObjectManager.GetLocaleString(gossipMenuLocale.BoxText, GetLocale(), ref strBoxText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add menu item with existing method. Menu item id -1 is also used in ADD_GOSSIP_ITEM macro.
|
||||
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 optionIndex, uint gossipActionMenuId, uint gossipActionPoi, uint trainerId)
|
||||
{
|
||||
GossipMenuItemData itemData = new GossipMenuItemData();
|
||||
|
||||
itemData.GossipActionMenuId = gossipActionMenuId;
|
||||
itemData.GossipActionPoi = gossipActionPoi;
|
||||
itemData.TrainerId = trainerId;
|
||||
|
||||
_menuItemData[optionIndex] = itemData;
|
||||
}
|
||||
|
||||
public uint GetMenuItemSender(uint menuItemId)
|
||||
{
|
||||
if (_menuItems.ContainsKey(menuItemId))
|
||||
return _menuItems.LookupByKey(menuItemId).Sender;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public uint GetMenuItemAction(uint menuItemId)
|
||||
{
|
||||
if (_menuItems.ContainsKey(menuItemId))
|
||||
return _menuItems.LookupByKey(menuItemId).OptionType;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool IsMenuItemCoded(uint menuItemId)
|
||||
{
|
||||
if (_menuItems.ContainsKey(menuItemId))
|
||||
return _menuItems.LookupByKey(menuItemId).IsCoded;
|
||||
else
|
||||
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();
|
||||
_menuItemData.Clear();
|
||||
}
|
||||
|
||||
public void SetMenuId(uint menu_id) { _menuId = menu_id; }
|
||||
public uint GetMenuId() { return _menuId; }
|
||||
public void SetLocale(LocaleConstant locale) { _locale = locale; }
|
||||
LocaleConstant GetLocale() { return _locale; }
|
||||
|
||||
public int GetMenuItemCount()
|
||||
{
|
||||
return _menuItems.Count;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return _menuItems.Empty();
|
||||
}
|
||||
|
||||
public GossipMenuItem GetItem(uint id)
|
||||
{
|
||||
return _menuItems.LookupByKey(id);
|
||||
}
|
||||
|
||||
public GossipMenuItemData GetItemData(uint indexId)
|
||||
{
|
||||
return _menuItemData.LookupByKey(indexId);
|
||||
}
|
||||
|
||||
public Dictionary<uint, GossipMenuItem> GetMenuItems()
|
||||
{
|
||||
return _menuItems;
|
||||
}
|
||||
|
||||
Dictionary<uint, GossipMenuItem> _menuItems = new Dictionary<uint, GossipMenuItem>();
|
||||
Dictionary<uint, GossipMenuItemData> _menuItemData = new Dictionary<uint, GossipMenuItemData>();
|
||||
uint _menuId;
|
||||
LocaleConstant _locale;
|
||||
}
|
||||
|
||||
public class InteractionData
|
||||
{
|
||||
public void Reset()
|
||||
{
|
||||
SourceGuid.Clear();
|
||||
TrainerId = 0;
|
||||
}
|
||||
|
||||
public ObjectGuid SourceGuid;
|
||||
public uint TrainerId;
|
||||
}
|
||||
|
||||
public class PlayerMenu
|
||||
{
|
||||
public PlayerMenu(WorldSession session)
|
||||
{
|
||||
_session = session;
|
||||
if (_session != null)
|
||||
_gossipMenu.SetLocale(_session.GetSessionDbLocaleIndex());
|
||||
}
|
||||
|
||||
public void ClearMenus()
|
||||
{
|
||||
_gossipMenu.ClearMenu();
|
||||
_questMenu.ClearMenu();
|
||||
}
|
||||
|
||||
public void SendGossipMenu(uint titleTextId, ObjectGuid objectGUID)
|
||||
{
|
||||
_interactionData.Reset();
|
||||
_interactionData.SourceGuid = objectGUID;
|
||||
|
||||
GossipMessagePkt packet = new GossipMessagePkt();
|
||||
packet.GossipGUID = objectGUID;
|
||||
packet.GossipID = (int)_gossipMenu.GetMenuId();
|
||||
packet.TextID = (int)titleTextId;
|
||||
|
||||
uint count = 0;
|
||||
foreach (var pair in _gossipMenu.GetMenuItems())
|
||||
{
|
||||
ClientGossipOptions opt = new ClientGossipOptions();
|
||||
GossipMenuItem item = pair.Value;
|
||||
opt.ClientOption = (int)pair.Key;
|
||||
opt.OptionNPC = item.MenuItemIcon;
|
||||
opt.OptionFlags = (byte)(item.IsCoded ? 1 : 0); // makes pop up box password
|
||||
opt.OptionCost = (int)item.BoxMoney; // money required to open menu, 2.0.3
|
||||
opt.Text = item.Message; // text for gossip item
|
||||
opt.Confirm = item.BoxMessage; // accept text (related to money) pop up box, 2.0.3
|
||||
packet.GossipOptions.Add(opt);
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (byte i = 0; i < _questMenu.GetMenuItemCount(); ++i)
|
||||
{
|
||||
QuestMenuItem item = _questMenu.GetItem(i);
|
||||
uint questID = item.QuestId;
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questID);
|
||||
if (quest != null)
|
||||
{
|
||||
ClientGossipText text = new ClientGossipText();
|
||||
text.QuestID = (int)questID;
|
||||
text.QuestType = item.QuestIcon;
|
||||
text.QuestLevel = quest.Level;
|
||||
text.QuestFlags = (int)quest.Flags;
|
||||
text.QuestFlagsEx = (int)quest.FlagsEx;
|
||||
text.Repeatable = quest.IsRepeatable();
|
||||
|
||||
text.QuestTitle = quest.LogTitle;
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestTemplateLocale localeData = Global.ObjectMgr.GetQuestLocale(quest.Id);
|
||||
if (localeData != null)
|
||||
ObjectManager.GetLocaleString(localeData.LogTitle, locale, ref text.QuestTitle);
|
||||
}
|
||||
|
||||
packet.GossipText.Add(text);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendCloseGossip()
|
||||
{
|
||||
_interactionData.Reset();
|
||||
|
||||
_session.SendPacket(new GossipComplete());
|
||||
}
|
||||
|
||||
public void SendPointOfInterest(uint id)
|
||||
{
|
||||
PointOfInterest pointOfInterest = Global.ObjectMgr.GetPointOfInterest(id);
|
||||
if (pointOfInterest == null)
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Request to send non-existing PointOfInterest (Id: {0}), ignored.", id);
|
||||
return;
|
||||
}
|
||||
|
||||
GossipPOI packet = new GossipPOI();
|
||||
packet.Name = pointOfInterest.Name;
|
||||
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
PointOfInterestLocale localeData = Global.ObjectMgr.GetPointOfInterestLocale(id);
|
||||
if (localeData != null)
|
||||
ObjectManager.GetLocaleString(localeData.Name, locale, ref packet.Name);
|
||||
}
|
||||
|
||||
packet.Flags = pointOfInterest.Flags;
|
||||
packet.Pos = pointOfInterest.Pos;
|
||||
packet.Icon = pointOfInterest.Icon;
|
||||
packet.Importance = pointOfInterest.Importance;
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendQuestGiverQuestListMessage(ObjectGuid guid)
|
||||
{
|
||||
QuestGiverQuestListMessage questList = new QuestGiverQuestListMessage();
|
||||
questList.QuestGiverGUID = guid;
|
||||
|
||||
QuestGreeting questGreeting = Global.ObjectMgr.GetQuestGreeting(guid);
|
||||
if (questGreeting != null)
|
||||
{
|
||||
questList.GreetEmoteDelay = questGreeting.greetEmoteDelay;
|
||||
questList.GreetEmoteType = questGreeting.greetEmoteType;
|
||||
questList.Greeting = questGreeting.greeting;
|
||||
}
|
||||
else
|
||||
Log.outError(LogFilter.Server, "Guid: {0} - No quest greeting found.", guid.ToString());
|
||||
|
||||
for (var i = 0; i < _questMenu.GetMenuItemCount(); ++i)
|
||||
{
|
||||
QuestMenuItem questMenuItem = _questMenu.GetItem(i);
|
||||
|
||||
uint questID = questMenuItem.QuestId;
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questID);
|
||||
if (quest != null)
|
||||
{
|
||||
string title = quest.LogTitle;
|
||||
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestTemplateLocale localeData = Global.ObjectMgr.GetQuestLocale(quest.Id);
|
||||
if (localeData != null)
|
||||
ObjectManager.GetLocaleString(localeData.LogTitle, locale, ref title);
|
||||
}
|
||||
|
||||
GossipText text = new GossipText();
|
||||
text.QuestID = questID;
|
||||
text.QuestType = questMenuItem.QuestIcon;
|
||||
text.QuestLevel = (uint)quest.Level;
|
||||
text.QuestFlags = (uint)quest.Flags;
|
||||
text.QuestFlagsEx = (uint)quest.FlagsEx;
|
||||
text.Repeatable = false; // NYI
|
||||
text.QuestTitle = title;
|
||||
questList.QuestDataText.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
_session.SendPacket(questList);
|
||||
}
|
||||
|
||||
public void SendQuestGiverStatus(QuestGiverStatus questStatus, ObjectGuid npcGUID)
|
||||
{
|
||||
var packet = new QuestGiverStatusPkt();
|
||||
packet.QuestGiver.Guid = npcGUID;
|
||||
packet.QuestGiver.Status = questStatus;
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendQuestGiverQuestDetails(Quest quest, ObjectGuid npcGUID, bool activateAccept)
|
||||
{
|
||||
QuestGiverQuestDetails packet = new QuestGiverQuestDetails();
|
||||
|
||||
packet.QuestTitle = quest.LogTitle;
|
||||
packet.LogDescription = quest.LogDescription;
|
||||
packet.DescriptionText = quest.QuestDescription;
|
||||
packet.PortraitGiverText = quest.PortraitGiverText;
|
||||
packet.PortraitGiverName = quest.PortraitGiverName;
|
||||
packet.PortraitTurnInText = quest.PortraitTurnInText;
|
||||
packet.PortraitTurnInName = quest.PortraitTurnInName;
|
||||
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestTemplateLocale localeData = Global.ObjectMgr.GetQuestLocale(quest.Id);
|
||||
if (localeData != null)
|
||||
{
|
||||
ObjectManager.GetLocaleString(localeData.LogTitle, locale, ref packet.QuestTitle);
|
||||
ObjectManager.GetLocaleString(localeData.LogDescription, locale, ref packet.LogDescription);
|
||||
ObjectManager.GetLocaleString(localeData.QuestDescription, locale, ref packet.DescriptionText);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitGiverText, locale, ref packet.PortraitGiverText);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitGiverName, locale, ref packet.PortraitGiverName);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitTurnInText, locale, ref packet.PortraitTurnInText);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitTurnInName, locale, ref packet.PortraitTurnInName);
|
||||
}
|
||||
}
|
||||
|
||||
packet.QuestGiverGUID = npcGUID;
|
||||
packet.InformUnit = _session.GetPlayer().GetDivider();
|
||||
packet.QuestID = quest.Id;
|
||||
packet.PortraitGiver = quest.QuestGiverPortrait;
|
||||
packet.PortraitTurnIn = quest.QuestTurnInPortrait;
|
||||
packet.AutoLaunched = activateAccept;
|
||||
packet.QuestFlags[0] = (uint)quest.Flags;
|
||||
packet.QuestFlags[1] = (uint)quest.FlagsEx;
|
||||
packet.SuggestedPartyMembers = quest.SuggestedPlayers;
|
||||
|
||||
if (quest.SourceSpellID != 0)
|
||||
packet.LearnSpells.Add(quest.SourceSpellID);
|
||||
|
||||
quest.BuildQuestRewards(packet.Rewards, _session.GetPlayer());
|
||||
|
||||
for (int i = 0; i < SharedConst.QuestEmoteCount; ++i)
|
||||
{
|
||||
var emote = new QuestDescEmote(quest.DetailsEmote[i], quest.DetailsEmoteDelay[i]);
|
||||
packet.DescEmotes.Add(emote);
|
||||
}
|
||||
|
||||
var objs = quest.Objectives;
|
||||
for (int i = 0; i < objs.Count; ++i)
|
||||
{
|
||||
var obj = new QuestObjectiveSimple();
|
||||
obj.ID = objs[i].ID;
|
||||
obj.ObjectID = objs[i].ObjectID;
|
||||
obj.Amount = objs[i].Amount;
|
||||
obj.Type = (byte)objs[i].Type;
|
||||
packet.Objectives.Add(obj);
|
||||
}
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendQuestQueryResponse(Quest quest)
|
||||
{
|
||||
QueryQuestInfoResponse packet = new QueryQuestInfoResponse();
|
||||
|
||||
packet.Allow = true;
|
||||
packet.QuestID = quest.Id;
|
||||
|
||||
packet.Info.LogTitle = quest.LogTitle;
|
||||
packet.Info.LogDescription = quest.LogDescription;
|
||||
packet.Info.QuestDescription = quest.QuestDescription;
|
||||
packet.Info.AreaDescription = quest.AreaDescription;
|
||||
packet.Info.QuestCompletionLog = quest.QuestCompletionLog;
|
||||
packet.Info.PortraitGiverText = quest.PortraitGiverText;
|
||||
packet.Info.PortraitGiverName = quest.PortraitGiverName;
|
||||
packet.Info.PortraitTurnInText = quest.PortraitTurnInText;
|
||||
packet.Info.PortraitTurnInName = quest.PortraitTurnInName;
|
||||
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestTemplateLocale questTemplateLocale = Global.ObjectMgr.GetQuestLocale(quest.Id);
|
||||
if (questTemplateLocale != null)
|
||||
{
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.LogTitle, locale, ref packet.Info.LogTitle);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.LogDescription, locale, ref packet.Info.LogDescription);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.QuestDescription, locale, ref packet.Info.QuestDescription);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.AreaDescription, locale, ref packet.Info.AreaDescription);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.QuestCompletionLog, locale, ref packet.Info.QuestCompletionLog);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.PortraitGiverText, locale, ref packet.Info.PortraitGiverText);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.PortraitGiverName, locale, ref packet.Info.PortraitGiverName);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.PortraitTurnInText, locale, ref packet.Info.PortraitTurnInText);
|
||||
ObjectManager.GetLocaleString(questTemplateLocale.PortraitTurnInName, locale, ref packet.Info.PortraitTurnInName);
|
||||
}
|
||||
}
|
||||
|
||||
packet.Info.QuestID = quest.Id;
|
||||
packet.Info.QuestType = (int)quest.Type;
|
||||
packet.Info.QuestLevel = quest.Level;
|
||||
packet.Info.QuestPackageID = quest.PackageID;
|
||||
packet.Info.QuestMinLevel = quest.MinLevel;
|
||||
packet.Info.QuestSortID = quest.QuestSortID;
|
||||
packet.Info.QuestInfoID = quest.QuestInfoID;
|
||||
packet.Info.SuggestedGroupNum = quest.SuggestedPlayers;
|
||||
packet.Info.RewardNextQuest = quest.NextQuestInChain;
|
||||
packet.Info.RewardXPDifficulty = quest.RewardXPDifficulty;
|
||||
packet.Info.RewardXPMultiplier = quest.RewardXPMultiplier;
|
||||
|
||||
if (!quest.HasFlag(QuestFlags.HiddenRewards))
|
||||
packet.Info.RewardMoney = quest.RewardMoney < 0 ? quest.RewardMoney : (int)_session.GetPlayer().GetQuestMoneyReward(quest);
|
||||
|
||||
packet.Info.RewardMoneyDifficulty = quest.RewardMoneyDifficulty;
|
||||
packet.Info.RewardMoneyMultiplier = quest.RewardMoneyMultiplier;
|
||||
packet.Info.RewardBonusMoney = quest.RewardBonusMoney;
|
||||
for (byte i = 0; i < SharedConst.QuestRewardDisplaySpellCount; ++i)
|
||||
packet.Info.RewardDisplaySpell[i] = quest.RewardDisplaySpell[i];
|
||||
|
||||
packet.Info.RewardSpell = quest.RewardSpell;
|
||||
|
||||
packet.Info.RewardHonor = quest.RewardHonor;
|
||||
packet.Info.RewardKillHonor = quest.RewardKillHonor;
|
||||
|
||||
packet.Info.RewardArtifactXPDifficulty = (int)quest.RewardArtifactXPDifficulty;
|
||||
packet.Info.RewardArtifactXPMultiplier = quest.RewardArtifactXPMultiplier;
|
||||
packet.Info.RewardArtifactCategoryID = (int)quest.RewardArtifactCategoryID;
|
||||
|
||||
packet.Info.StartItem = quest.SourceItemId;
|
||||
packet.Info.Flags = (uint)quest.Flags;
|
||||
packet.Info.FlagsEx = (uint)quest.FlagsEx;
|
||||
packet.Info.RewardTitle = quest.RewardTitleId;
|
||||
packet.Info.RewardArenaPoints = quest.RewardArenaPoints;
|
||||
packet.Info.RewardSkillLineID = quest.RewardSkillId;
|
||||
packet.Info.RewardNumSkillUps = quest.RewardSkillPoints;
|
||||
packet.Info.RewardFactionFlags = quest.RewardReputationMask;
|
||||
packet.Info.PortraitGiver = quest.QuestGiverPortrait;
|
||||
packet.Info.PortraitTurnIn = quest.QuestTurnInPortrait;
|
||||
|
||||
for (byte i = 0; i < SharedConst.QuestItemDropCount; ++i)
|
||||
{
|
||||
packet.Info.ItemDrop[i] = (int)quest.ItemDrop[i];
|
||||
packet.Info.ItemDropQuantity[i] = (int)quest.ItemDropQuantity[i];
|
||||
}
|
||||
|
||||
if (!quest.HasFlag(QuestFlags.HiddenRewards))
|
||||
{
|
||||
for (byte i = 0; i < SharedConst.QuestRewardItemCount; ++i)
|
||||
{
|
||||
packet.Info.RewardItems[i] = quest.RewardItemId[i];
|
||||
packet.Info.RewardAmount[i] = quest.RewardItemCount[i];
|
||||
}
|
||||
for (byte i = 0; i < SharedConst.QuestRewardChoicesCount; ++i)
|
||||
{
|
||||
packet.Info.UnfilteredChoiceItems[i].ItemID = quest.RewardChoiceItemId[i];
|
||||
packet.Info.UnfilteredChoiceItems[i].Quantity = quest.RewardChoiceItemCount[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (byte i = 0; i < SharedConst.QuestRewardReputationsCount; ++i)
|
||||
{
|
||||
packet.Info.RewardFactionID[i] = quest.RewardFactionId[i];
|
||||
packet.Info.RewardFactionValue[i] = quest.RewardFactionValue[i];
|
||||
packet.Info.RewardFactionOverride[i] = quest.RewardFactionOverride[i];
|
||||
packet.Info.RewardFactionCapIn[i] = (int)quest.RewardFactionCapIn[i];
|
||||
}
|
||||
|
||||
packet.Info.POIContinent = quest.POIContinent;
|
||||
packet.Info.POIx = quest.POIx;
|
||||
packet.Info.POIy = quest.POIy;
|
||||
packet.Info.POIPriority = quest.POIPriority;
|
||||
|
||||
packet.Info.AllowableRaces = quest.AllowableRaces;
|
||||
packet.Info.QuestRewardID = (int)quest.QuestRewardID;
|
||||
packet.Info.Expansion = quest.Expansion;
|
||||
|
||||
foreach (QuestObjective questObjective in quest.Objectives)
|
||||
{
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestObjectivesLocale questObjectivesLocaleData = Global.ObjectMgr.GetQuestObjectivesLocale(questObjective.ID);
|
||||
if (questObjectivesLocaleData != null)
|
||||
ObjectManager.GetLocaleString(questObjectivesLocaleData.Description, locale, ref questObjective.Description);
|
||||
}
|
||||
|
||||
packet.Info.Objectives.Add(questObjective);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SharedConst.QuestRewardCurrencyCount; ++i)
|
||||
{
|
||||
packet.Info.RewardCurrencyID[i] = quest.RewardCurrencyId[i];
|
||||
packet.Info.RewardCurrencyQty[i] = quest.RewardCurrencyCount[i];
|
||||
}
|
||||
|
||||
packet.Info.AcceptedSoundKitID = quest.SoundAccept;
|
||||
packet.Info.CompleteSoundKitID = quest.SoundTurnIn;
|
||||
packet.Info.AreaGroupID = quest.AreaGroupID;
|
||||
packet.Info.TimeAllowed = quest.LimitTime;
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendQuestGiverOfferReward(Quest quest, ObjectGuid npcGUID, bool enableNext)
|
||||
{
|
||||
QuestGiverOfferRewardMessage packet = new QuestGiverOfferRewardMessage();
|
||||
|
||||
packet.QuestTitle = quest.LogTitle;
|
||||
packet.RewardText = quest.OfferRewardText;
|
||||
packet.PortraitGiverText = quest.PortraitGiverText;
|
||||
packet.PortraitGiverName = quest.PortraitGiverName;
|
||||
packet.PortraitTurnInText = quest.PortraitTurnInText;
|
||||
packet.PortraitTurnInName = quest.PortraitTurnInName;
|
||||
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestTemplateLocale localeData = Global.ObjectMgr.GetQuestLocale(quest.Id);
|
||||
if (localeData != null)
|
||||
{
|
||||
ObjectManager.GetLocaleString(localeData.LogTitle, locale, ref packet.QuestTitle);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitGiverText, locale, ref packet.PortraitGiverText);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitGiverName, locale, ref packet.PortraitGiverName);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitTurnInText, locale, ref packet.PortraitTurnInText);
|
||||
ObjectManager.GetLocaleString(localeData.PortraitTurnInName, locale, ref packet.PortraitTurnInName);
|
||||
}
|
||||
|
||||
QuestOfferRewardLocale questOfferRewardLocale = Global.ObjectMgr.GetQuestOfferRewardLocale(quest.Id);
|
||||
if (questOfferRewardLocale != null)
|
||||
ObjectManager.GetLocaleString(questOfferRewardLocale.RewardText, locale, ref packet.RewardText);
|
||||
}
|
||||
|
||||
QuestGiverOfferReward offer = new QuestGiverOfferReward();
|
||||
|
||||
quest.BuildQuestRewards(offer.Rewards, _session.GetPlayer());
|
||||
offer.QuestGiverGUID = npcGUID;
|
||||
|
||||
// Is there a better way? what about game objects?
|
||||
Creature creature = ObjectAccessor.GetCreature(_session.GetPlayer(), npcGUID);
|
||||
if (creature)
|
||||
offer.QuestGiverCreatureID = creature.GetCreatureTemplate().Entry;
|
||||
|
||||
offer.QuestID = quest.Id;
|
||||
offer.AutoLaunched = enableNext;
|
||||
offer.SuggestedPartyMembers = quest.SuggestedPlayers;
|
||||
|
||||
for (uint i = 0; i < SharedConst.QuestEmoteCount && quest.OfferRewardEmote[i] != 0; ++i)
|
||||
offer.Emotes.Add(new QuestDescEmote(quest.OfferRewardEmote[i], quest.OfferRewardEmoteDelay[i]));
|
||||
|
||||
offer.QuestFlags[0] = (uint)quest.Flags;
|
||||
offer.QuestFlags[1] = (uint)quest.FlagsEx;
|
||||
|
||||
packet.PortraitTurnIn = quest.QuestTurnInPortrait;
|
||||
packet.PortraitGiver = quest.QuestGiverPortrait;
|
||||
packet.QuestPackageID = quest.PackageID;
|
||||
|
||||
packet.QuestData = offer;
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendQuestGiverRequestItems(Quest quest, ObjectGuid npcGUID, bool canComplete, bool closeOnCancel)
|
||||
{
|
||||
// We can always call to RequestItems, but this packet only goes out if there are actually
|
||||
// items. Otherwise, we'll skip straight to the OfferReward
|
||||
|
||||
if (!quest.HasSpecialFlag(QuestSpecialFlags.Deliver) && canComplete)
|
||||
{
|
||||
SendQuestGiverOfferReward(quest, npcGUID, true);
|
||||
return;
|
||||
}
|
||||
|
||||
QuestGiverRequestItems packet = new QuestGiverRequestItems();
|
||||
|
||||
packet.QuestTitle = quest.LogTitle;
|
||||
packet.CompletionText = quest.RequestItemsText;
|
||||
|
||||
LocaleConstant locale = _session.GetSessionDbLocaleIndex();
|
||||
if (locale != LocaleConstant.enUS)
|
||||
{
|
||||
QuestTemplateLocale localeData = Global.ObjectMgr.GetQuestLocale(quest.Id);
|
||||
if (localeData != null)
|
||||
ObjectManager.GetLocaleString(localeData.LogTitle, locale, ref packet.QuestTitle);
|
||||
|
||||
QuestRequestItemsLocale questRequestItemsLocale = Global.ObjectMgr.GetQuestRequestItemsLocale(quest.Id);
|
||||
if (questRequestItemsLocale != null)
|
||||
ObjectManager.GetLocaleString(questRequestItemsLocale.CompletionText, locale, ref packet.CompletionText);
|
||||
}
|
||||
|
||||
packet.QuestGiverGUID = npcGUID;
|
||||
|
||||
// Is there a better way? what about game objects?
|
||||
Creature creature = ObjectAccessor.GetCreature(_session.GetPlayer(), npcGUID);
|
||||
if (creature)
|
||||
packet.QuestGiverCreatureID = creature.GetCreatureTemplate().Entry;
|
||||
|
||||
packet.QuestID = quest.Id;
|
||||
|
||||
if (canComplete)
|
||||
{
|
||||
packet.CompEmoteDelay = quest.EmoteOnCompleteDelay;
|
||||
packet.CompEmoteType = quest.EmoteOnComplete;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.CompEmoteDelay = quest.EmoteOnIncompleteDelay;
|
||||
packet.CompEmoteType = quest.EmoteOnIncomplete;
|
||||
}
|
||||
|
||||
packet.QuestFlags[0] = (uint)quest.Flags;
|
||||
packet.QuestFlags[1] = (uint)quest.FlagsEx;
|
||||
packet.SuggestPartyMembers = quest.SuggestedPlayers;
|
||||
|
||||
// incomplete: FD
|
||||
// incomplete quest with item objective but item objective is complete DD
|
||||
packet.StatusFlags = canComplete ? 0xFF : 0xFD;
|
||||
|
||||
packet.MoneyToGet = 0;
|
||||
foreach (QuestObjective obj in quest.Objectives)
|
||||
{
|
||||
switch (obj.Type)
|
||||
{
|
||||
case QuestObjectiveType.Item:
|
||||
packet.Collect.Add(new QuestObjectiveCollect((uint)obj.ObjectID, obj.Amount, (uint)obj.Flags));
|
||||
break;
|
||||
case QuestObjectiveType.Currency:
|
||||
packet.Currency.Add(new QuestCurrency((uint)obj.ObjectID, obj.Amount));
|
||||
break;
|
||||
case QuestObjectiveType.Money:
|
||||
packet.MoneyToGet += obj.Amount;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
packet.AutoLaunched = closeOnCancel;
|
||||
|
||||
_session.SendPacket(packet);
|
||||
}
|
||||
|
||||
public GossipMenu GetGossipMenu() { return _gossipMenu; }
|
||||
public QuestMenu GetQuestMenu() { return _questMenu; }
|
||||
public InteractionData GetInteractionData() { return _interactionData; }
|
||||
|
||||
bool IsEmpty() { return _gossipMenu.IsEmpty() && _questMenu.IsEmpty(); }
|
||||
|
||||
public uint GetGossipOptionSender(uint selection) { return _gossipMenu.GetMenuItemSender(selection); }
|
||||
public uint GetGossipOptionAction(uint selection) { return _gossipMenu.GetMenuItemAction(selection); }
|
||||
public bool IsGossipOptionCoded(uint selection) { return _gossipMenu.IsMenuItemCoded(selection); }
|
||||
|
||||
GossipMenu _gossipMenu = new GossipMenu();
|
||||
QuestMenu _questMenu = new QuestMenu();
|
||||
WorldSession _session;
|
||||
InteractionData _interactionData = new InteractionData();
|
||||
}
|
||||
|
||||
public class QuestMenu
|
||||
{
|
||||
public QuestMenu() { }
|
||||
|
||||
public void AddMenuItem(uint QuestId, byte Icon)
|
||||
{
|
||||
if (Global.ObjectMgr.GetQuestTemplate(QuestId) == null)
|
||||
return;
|
||||
|
||||
QuestMenuItem questMenuItem = new QuestMenuItem();
|
||||
|
||||
questMenuItem.QuestId = QuestId;
|
||||
questMenuItem.QuestIcon = Icon;
|
||||
|
||||
_questMenuItems.Add(questMenuItem);
|
||||
}
|
||||
|
||||
bool HasItem(uint questId)
|
||||
{
|
||||
foreach (var item in _questMenuItems)
|
||||
if (item.QuestId == questId)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearMenu()
|
||||
{
|
||||
_questMenuItems.Clear();
|
||||
}
|
||||
|
||||
public int GetMenuItemCount()
|
||||
{
|
||||
return _questMenuItems.Count();
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return _questMenuItems.Empty();
|
||||
}
|
||||
|
||||
public QuestMenuItem GetItem(int index)
|
||||
{
|
||||
return _questMenuItems.LookupByIndex(index);
|
||||
}
|
||||
|
||||
List<QuestMenuItem> _questMenuItems = new List<QuestMenuItem>();
|
||||
}
|
||||
|
||||
public struct QuestMenuItem
|
||||
{
|
||||
public uint QuestId;
|
||||
public byte QuestIcon;
|
||||
}
|
||||
|
||||
public class GossipMenuItem
|
||||
{
|
||||
public byte MenuItemIcon;
|
||||
public bool IsCoded;
|
||||
public string Message;
|
||||
public uint Sender;
|
||||
public uint OptionType;
|
||||
public string BoxMessage;
|
||||
public uint BoxMoney;
|
||||
}
|
||||
|
||||
public class GossipMenuItemData
|
||||
{
|
||||
public uint GossipActionMenuId; // MenuId of the gossip triggered by this action
|
||||
public uint GossipActionPoi;
|
||||
public uint TrainerId;
|
||||
}
|
||||
|
||||
public struct NpcTextData
|
||||
{
|
||||
public float Probability;
|
||||
public uint BroadcastTextID;
|
||||
}
|
||||
|
||||
public class NpcText
|
||||
{
|
||||
public NpcTextData[] Data = new NpcTextData[SharedConst.MaxNpcTextOptions];
|
||||
}
|
||||
|
||||
public class PageTextLocale
|
||||
{
|
||||
public StringArray Text = new StringArray((int)LocaleConstant.Total);
|
||||
}
|
||||
|
||||
public class GossipMenuItems
|
||||
{
|
||||
public uint MenuId;
|
||||
public uint OptionIndex;
|
||||
public GossipOptionIcon OptionIcon;
|
||||
public string OptionText;
|
||||
public uint OptionBroadcastTextId;
|
||||
public GossipOption OptionType;
|
||||
public NPCFlags OptionNpcflag;
|
||||
public uint ActionMenuId;
|
||||
public uint ActionPoiId;
|
||||
public bool BoxCoded;
|
||||
public uint BoxMoney;
|
||||
public string BoxText;
|
||||
public uint BoxBroadcastTextId;
|
||||
public uint TrainerId;
|
||||
public List<Condition> Conditions = new List<Condition>();
|
||||
}
|
||||
|
||||
public class PointOfInterest
|
||||
{
|
||||
public uint ID;
|
||||
public Vector2 Pos;
|
||||
public uint Icon;
|
||||
public uint Flags;
|
||||
public uint Importance;
|
||||
public string Name;
|
||||
}
|
||||
|
||||
public class PointOfInterestLocale
|
||||
{
|
||||
public StringArray Name = new StringArray((int)LocaleConstant.Total);
|
||||
}
|
||||
|
||||
public class GossipMenus
|
||||
{
|
||||
public uint entry;
|
||||
public uint text_id;
|
||||
public List<Condition> conditions = new List<Condition>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 LearnedSpellId;
|
||||
public bool IsCastable() { return LearnedSpellId != SpellId; }
|
||||
}
|
||||
|
||||
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.IsCastable())
|
||||
player.CastSpell(player, trainerSpell.SpellId, 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 ranks
|
||||
uint previousRankSpellId = Global.SpellMgr.GetPrevSpellInChain(trainerSpell.LearnedSpellId);
|
||||
if (previousRankSpellId != 0)
|
||||
if (!player.HasSpell(previousRankSpellId))
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user