/* * Copyright (C) 2012-2020 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 . */ using Framework.Constants; using Framework.Dynamic; using Framework.GameMath; using Game.AI; using Game.BattleGrounds; using Game.Chat; using Game.Combat; using Game.DataStorage; using Game.Maps; using Game.Movement; using Game.Networking.Packets; using Game.Spells; using System; using System.Collections.Generic; using System.Linq; using Game.Networking; namespace Game.Entities { public partial class Unit : WorldObject { public Unit(bool isWorldObject) : base(isWorldObject) { MoveSpline = new MoveSpline(); i_motionMaster = new MotionMaster(this); threatManager = new ThreatManager(this); UnitTypeMask = UnitTypeMask.None; hostileRefManager = new HostileRefManager(this); _spellHistory = new SpellHistory(this); m_FollowingRefManager = new RefManager(); ObjectTypeId = TypeId.Unit; ObjectTypeMask |= TypeMask.Unit; m_updateFlag.MovementUpdate = true; m_modAttackSpeedPct = new float[] { 1.0f, 1.0f, 1.0f }; m_deathState = DeathState.Alive; for (byte i = 0; i < (int)SpellImmunity.Max; ++i) m_spellImmune[i] = new MultiMap(); for (byte i = 0; i < (int)UnitMods.End; ++i) { m_auraFlatModifiersGroup[i] = new float[(int)UnitModifierFlatType.End]; m_auraFlatModifiersGroup[i][(int)UnitModifierFlatType.Base] = 0.0f; m_auraFlatModifiersGroup[i][(int)UnitModifierFlatType.BasePCTExcludeCreate] = 100.0f; m_auraFlatModifiersGroup[i][(int)UnitModifierFlatType.Total] = 0.0f; m_auraPctModifiersGroup[i] = new float[(int)UnitModifierPctType.End]; m_auraPctModifiersGroup[i][(int)UnitModifierPctType.Base] = 1.0f; m_auraPctModifiersGroup[i][(int)UnitModifierPctType.Total] = 1.0f; } m_auraPctModifiersGroup[(int)UnitMods.DamageOffHand][(int)UnitModifierPctType.Total] = 0.5f; foreach (AuraType auraType in Enum.GetValues(typeof(AuraType))) m_modAuras[auraType] = new List(); for (byte i = 0; i < (int)WeaponAttackType.Max; ++i) m_weaponDamage[i] = new float[] { 1.0f, 2.0f }; if (IsTypeId(TypeId.Player)) { ModMeleeHitChance = 7.5f; ModRangedHitChance = 7.5f; ModSpellHitChance = 15.0f; } BaseSpellCritChance = 5; for (byte i = 0; i < (int)SpellSchools.Max; ++i) m_threatModifier[i] = 1.0f; for (byte i = 0; i < (int)UnitMoveType.Max; ++i) m_speed_rate[i] = 1.0f; _redirectThreatInfo = new RedirectThreatInfo(); m_serverSideVisibility.SetValue(ServerSideVisibilityType.Ghost, GhostVisibilityType.Alive); movesplineTimer = new TimeTrackerSmall(); m_unitData = new UnitData(); } public override void Dispose() { // set current spells as deletable for (CurrentSpellTypes i = 0; i < CurrentSpellTypes.Max; ++i) { if (m_currentSpells.ContainsKey(i)) { if (m_currentSpells[i] != null) { m_currentSpells[i].SetReferencedFromCurrent(false); m_currentSpells[i] = null; } } } m_Events.KillAllEvents(true); _DeleteRemovedAuras(); //i_motionMaster = null; m_charmInfo = null; MoveSpline = null; _spellHistory = null; /*ASSERT(!m_duringRemoveFromWorld); ASSERT(!m_attacking); ASSERT(m_attackers.empty()); ASSERT(m_sharedVision.empty()); ASSERT(m_Controlled.empty()); ASSERT(m_appliedAuras.empty()); ASSERT(m_ownedAuras.empty()); ASSERT(m_removedAuras.empty()); ASSERT(m_gameObj.empty()); ASSERT(m_dynObj.empty());*/ base.Dispose(); } public override void Update(uint diff) { // WARNING! Order of execution here is important, do not change. // Spells must be processed with event system BEFORE they go to _UpdateSpells. m_Events.Update(diff); if (!IsInWorld) return; _UpdateSpells(diff); // If this is set during update SetCantProc(false) call is missing somewhere in the code // Having this would prevent spells from being proced, so let's crash Cypher.Assert(m_procDeep == 0); if (CanHaveThreatList() && GetThreatManager().IsNeedUpdateToClient(diff)) SendThreatListUpdate(); // update combat timer only for players and pets (only pets with PetAI) if (IsInCombat() && (IsTypeId(TypeId.Player) || (IsPet() && IsControlledByPlayer()))) { // Check UNIT_STATE_MELEE_ATTACKING or UNIT_STATE_CHASE (without UNIT_STATE_FOLLOW in this case) so pets can reach far away // targets without stopping half way there and running off. // These flags are reset after target dies or another command is given. if (hostileRefManager.IsEmpty()) { // m_CombatTimer set at aura start and it will be freeze until aura removing if (combatTimer <= diff) ClearInCombat(); else combatTimer -= diff; } } uint att; // not implemented before 3.0.2 if ((att = GetAttackTimer(WeaponAttackType.BaseAttack)) != 0) SetAttackTimer(WeaponAttackType.BaseAttack, (diff >= att ? 0 : att - diff)); if ((att = GetAttackTimer(WeaponAttackType.RangedAttack)) != 0) SetAttackTimer(WeaponAttackType.RangedAttack, (diff >= att ? 0 : att - diff)); if ((att = GetAttackTimer(WeaponAttackType.OffAttack)) != 0) SetAttackTimer(WeaponAttackType.OffAttack, (diff >= att ? 0 : att - diff)); // update abilities available only for fraction of time UpdateReactives(diff); if (IsAlive()) { ModifyAuraState(AuraStateType.HealthLess20Percent, HealthBelowPct(20)); ModifyAuraState(AuraStateType.HealthLess35Percent, HealthBelowPct(35)); ModifyAuraState(AuraStateType.HealthAbove75Percent, HealthAbovePct(75)); } UpdateSplineMovement(diff); GetMotionMaster().UpdateMotion(diff); } void _UpdateSpells(uint diff) { if (GetCurrentSpell(CurrentSpellTypes.AutoRepeat) != null) _UpdateAutoRepeatSpell(); for (CurrentSpellTypes i = 0; i < CurrentSpellTypes.Max; ++i) { if (GetCurrentSpell(i) != null && m_currentSpells[i].GetState() == SpellState.Finished) { m_currentSpells[i].SetReferencedFromCurrent(false); m_currentSpells[i] = null; } } foreach (var app in GetOwnedAuras()) { Aura i_aura = app.Value; if (i_aura == null) continue; i_aura.UpdateOwner(diff, this); } // remove expired auras - do that after updates(used in scripts?) foreach (var pair in GetOwnedAuras()) { if (pair.Value != null && pair.Value.IsExpired()) RemoveOwnedAura(pair, AuraRemoveMode.Expire); } foreach (var aura in m_visibleAurasToUpdate) aura.ClientUpdate(); m_visibleAurasToUpdate.Clear(); _DeleteRemovedAuras(); if (!m_gameObj.Empty()) { for (var i = 0; i < m_gameObj.Count; ++i) { GameObject go = m_gameObj[i]; if (!go.IsSpawned()) { go.SetOwnerGUID(ObjectGuid.Empty); go.SetRespawnTime(0); go.Delete(); m_gameObj.Remove(go); } } } _spellHistory.Update(); } public void HandleEmoteCommand(Emote anim_id) { EmoteMessage packet = new EmoteMessage(); packet.Guid = GetGUID(); packet.EmoteID = (int)anim_id; SendMessageToSet(packet, true); } public void SendDurabilityLoss(Player receiver, uint percent) { DurabilityDamageDeath packet = new DurabilityDamageDeath(); packet.Percent = percent; receiver.SendPacket(packet); } public bool IsInDisallowedMountForm() { return IsDisallowedMountForm(GetTransForm(), GetShapeshiftForm(), GetDisplayId()); } public bool IsDisallowedMountForm(uint spellId, ShapeShiftForm form, uint displayId) { SpellInfo transformSpellInfo = Global.SpellMgr.GetSpellInfo(spellId, GetMap().GetDifficultyID()); if (transformSpellInfo != null) if (transformSpellInfo.HasAttribute(SpellAttr0.CastableWhileMounted)) return false; if (form != 0) { SpellShapeshiftFormRecord shapeshift = CliDB.SpellShapeshiftFormStorage.LookupByKey(form); if (shapeshift == null) return true; if (!shapeshift.Flags.HasAnyFlag(SpellShapeshiftFormFlags.IsNotAShapeshift)) return true; } if (displayId == GetNativeDisplayId()) return false; CreatureDisplayInfoRecord display = CliDB.CreatureDisplayInfoStorage.LookupByKey(displayId); if (display == null) return true; CreatureDisplayInfoExtraRecord displayExtra = CliDB.CreatureDisplayInfoExtraStorage.LookupByKey(display.ExtendedDisplayInfoID); if (displayExtra == null) return true; CreatureModelDataRecord model = CliDB.CreatureModelDataStorage.LookupByKey(display.ModelID); ChrRacesRecord race = CliDB.ChrRacesStorage.LookupByKey(displayExtra.DisplayRaceID); if (model != null && !Convert.ToBoolean(model.Flags & 0x80)) if (race != null && !Convert.ToBoolean(race.Flags & 0x4)) return true; return false; } public void SendClearTarget() { BreakTarget breakTarget = new BreakTarget(); breakTarget.UnitGUID = GetGUID(); SendMessageToSet(breakTarget, false); } public virtual bool IsLoading() { return false; } public bool IsDuringRemoveFromWorld() { return m_duringRemoveFromWorld; } //SharedVision public bool HasSharedVision() { return !m_sharedVision.Empty(); } public List GetSharedVisionList() { return m_sharedVision; } public void AddPlayerToVision(Player player) { if (m_sharedVision.Empty()) { SetActive(true); SetWorldObject(true); } m_sharedVision.Add(player); } // only called in Player.SetSeer public void RemovePlayerFromVision(Player player) { m_sharedVision.Remove(player); if (m_sharedVision.Empty()) { SetActive(false); SetWorldObject(false); } } public virtual void Talk(string text, ChatMsg msgType, Language language, float textRange, WorldObject target) { var builder = new CustomChatTextBuilder(this, msgType, text, language, target); var localizer = new LocalizedPacketDo(builder); var worker = new PlayerDistWorker(this, textRange, localizer); Cell.VisitWorldObjects(this, worker, textRange); } public virtual void Say(string text, Language language, WorldObject target = null) { Talk(text, ChatMsg.MonsterSay, language, WorldConfig.GetFloatValue(WorldCfg.ListenRangeSay), target); } public virtual void Yell(string text, Language language, WorldObject target = null) { Talk(text, ChatMsg.MonsterYell, language, WorldConfig.GetFloatValue(WorldCfg.ListenRangeYell), target); } public virtual void TextEmote(string text, WorldObject target = null, bool isBossEmote = false) { Talk(text, isBossEmote ? ChatMsg.RaidBossEmote : ChatMsg.MonsterEmote, Language.Universal, WorldConfig.GetFloatValue(WorldCfg.ListenRangeTextemote), target); } public virtual void Whisper(string text, Language language, Player target, bool isBossWhisper = false) { if (!target) return; Locale locale = target.GetSession().GetSessionDbLocaleIndex(); ChatPkt data = new ChatPkt(); data.Initialize(isBossWhisper ? ChatMsg.RaidBossWhisper : ChatMsg.MonsterWhisper, Language.Universal, this, target, text, 0, "", locale); target.SendPacket(data); } public void Talk(uint textId, ChatMsg msgType, float textRange, WorldObject target) { if (!CliDB.BroadcastTextStorage.ContainsKey(textId)) { Log.outError(LogFilter.Unit, "Unit.Talk: `broadcast_text` (Id: {0}) was not found", textId); return; } var builder = new BroadcastTextBuilder(this, msgType, textId, GetGender(), target); var localizer = new LocalizedPacketDo(builder); var worker = new PlayerDistWorker(this, textRange, localizer); Cell.VisitWorldObjects(this, worker, textRange); } public virtual void Say(uint textId, WorldObject target = null) { Talk(textId, ChatMsg.MonsterSay, WorldConfig.GetFloatValue(WorldCfg.ListenRangeSay), target); } public virtual void Yell(uint textId, WorldObject target = null) { Talk(textId, ChatMsg.MonsterYell, WorldConfig.GetFloatValue(WorldCfg.ListenRangeYell), target); } public virtual void TextEmote(uint textId, WorldObject target = null, bool isBossEmote = false) { Talk(textId, isBossEmote ? ChatMsg.RaidBossEmote : ChatMsg.MonsterEmote, WorldConfig.GetFloatValue(WorldCfg.ListenRangeTextemote), target); } public virtual void Whisper(uint textId, Player target, bool isBossWhisper = false) { if (!target) return; BroadcastTextRecord bct = CliDB.BroadcastTextStorage.LookupByKey(textId); if (bct == null) { Log.outError(LogFilter.Unit, "Unit.Whisper: `broadcast_text` was not {0} found", textId); return; } Locale locale = target.GetSession().GetSessionDbLocaleIndex(); ChatPkt data = new ChatPkt(); data.Initialize(isBossWhisper ? ChatMsg.RaidBossWhisper : ChatMsg.MonsterWhisper, Language.Universal, this, target, Global.DB2Mgr.GetBroadcastTextValue(bct, locale, GetGender()), 0, "", locale); target.SendPacket(data); } public override void UpdateObjectVisibility(bool forced = true) { if (!forced) AddToNotify(NotifyFlags.VisibilityChanged); else { base.UpdateObjectVisibility(true); // call MoveInLineOfSight for nearby creatures AIRelocationNotifier notifier = new AIRelocationNotifier(this); Cell.VisitAllObjects(this, notifier, GetVisibilityRange()); } } public override void RemoveFromWorld() { // cleanup if (IsInWorld) { m_duringRemoveFromWorld = true; if (IsVehicle()) RemoveVehicleKit(true); RemoveCharmAuras(); RemoveAurasByType(AuraType.BindSight); RemoveNotOwnSingleTargetAuras(); RemoveAllGameObjects(); RemoveAllDynObjects(); RemoveAllAreaTriggers(); ExitVehicle(); // Remove applied auras with SPELL_AURA_CONTROL_VEHICLE UnsummonAllTotems(); RemoveAllControlled(); RemoveAreaAurasDueToLeaveWorld(); if (!GetCharmerGUID().IsEmpty()) { Log.outFatal(LogFilter.Unit, "Unit {0} has charmer guid when removed from world", GetEntry()); } Unit owner = GetOwner(); if (owner != null) { if (owner.m_Controlled.Contains(this)) { Log.outFatal(LogFilter.Unit, "Unit {0} is in controlled list of {1} when removed from world", GetEntry(), owner.GetEntry()); } } base.RemoveFromWorld(); m_duringRemoveFromWorld = false; } } public void CleanupBeforeRemoveFromMap(bool finalCleanup) { // This needs to be before RemoveFromWorld to make GetCaster() return a valid for aura removal InterruptNonMeleeSpells(true); if (IsInWorld) RemoveFromWorld(); // A unit may be in removelist and not in world, but it is still in grid // and may have some references during delete RemoveAllAuras(); RemoveAllGameObjects(); if (finalCleanup) m_cleanupDone = true; m_Events.KillAllEvents(false); // non-delatable (currently casted spells) will not deleted now but it will deleted at call in Map.RemoveAllObjectsInRemoveList CombatStop(); GetThreatManager().ClearAllThreat(); GetHostileRefManager().DeleteReferences(); } public override void CleanupsBeforeDelete(bool finalCleanup = true) { CleanupBeforeRemoveFromMap(finalCleanup); base.CleanupsBeforeDelete(finalCleanup); } public void SetTransForm(uint spellid) { m_transform = spellid; } public uint GetTransForm() { return m_transform; } public Vehicle GetVehicleKit() { return VehicleKit; } public Vehicle GetVehicle() { return m_vehicle; } public void SetVehicle(Vehicle vehicle) { m_vehicle = vehicle; } public Unit GetVehicleBase() { return m_vehicle != null ? m_vehicle.GetBase() : null; } public Creature GetVehicleCreatureBase() { Unit veh = GetVehicleBase(); if (veh != null) { Creature c = veh.ToCreature(); if (c != null) return c; } return null; } public ITransport GetDirectTransport() { Vehicle veh = GetVehicle(); if (veh != null) return veh; return GetTransport(); } public void _RegisterDynObject(DynamicObject dynObj) { m_dynObj.Add(dynObj); if (IsTypeId(TypeId.Unit) && IsAIEnabled) ToCreature().GetAI().JustRegisteredDynObject(dynObj); } public void _UnregisterDynObject(DynamicObject dynObj) { m_dynObj.Remove(dynObj); if (IsTypeId(TypeId.Unit) && IsAIEnabled) ToCreature().GetAI().JustUnregisteredDynObject(dynObj); } public DynamicObject GetDynObject(uint spellId) { return GetDynObjects(spellId).FirstOrDefault(); } List GetDynObjects(uint spellId) { List dynamicobjects = new List(); foreach (var obj in m_dynObj) if (obj.GetSpellId() == spellId) dynamicobjects.Add(obj); return dynamicobjects; } public void RemoveDynObject(uint spellId) { for (var i = 0; i < m_dynObj.Count; ++i) { var dynObj = m_dynObj[i]; if (dynObj.GetSpellId() == spellId) dynObj.Remove(); } } public void RemoveAllDynObjects() { while (!m_dynObj.Empty()) m_dynObj.First().Remove(); } public GameObject GetGameObject(uint spellId) { return GetGameObjects(spellId).FirstOrDefault(); } List GetGameObjects(uint spellId) { List gameobjects = new List(); foreach (var obj in m_gameObj) if (obj.GetSpellId() == spellId) gameobjects.Add(obj); return gameobjects; } public void AddGameObject(GameObject gameObj) { if (gameObj == null || !gameObj.GetOwnerGUID().IsEmpty()) return; m_gameObj.Add(gameObj); gameObj.SetOwnerGUID(GetGUID()); if (gameObj.GetSpellId() != 0) { SpellInfo createBySpell = Global.SpellMgr.GetSpellInfo(gameObj.GetSpellId(), GetMap().GetDifficultyID()); // Need disable spell use for owner if (createBySpell != null && createBySpell.HasAttribute(SpellAttr0.DisabledWhileActive)) // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases) GetSpellHistory().StartCooldown(createBySpell, 0, null, true); } if (IsTypeId(TypeId.Unit) && ToCreature().IsAIEnabled) ToCreature().GetAI().JustSummonedGameobject(gameObj); } public void RemoveGameObject(GameObject gameObj, bool del) { if (gameObj == null || gameObj.GetOwnerGUID() != GetGUID()) return; gameObj.SetOwnerGUID(ObjectGuid.Empty); for (byte i = 0; i < SharedConst.MaxGameObjectSlot; ++i) { if (m_ObjectSlot[i] == gameObj.GetGUID()) { m_ObjectSlot[i].Clear(); break; } } // GO created by some spell uint spellid = gameObj.GetSpellId(); if (spellid != 0) { RemoveAurasDueToSpell(spellid); SpellInfo createBySpell = Global.SpellMgr.GetSpellInfo(spellid, GetMap().GetDifficultyID()); // Need activate spell use for owner if (createBySpell != null && createBySpell.IsCooldownStartedOnEvent()) // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases) GetSpellHistory().SendCooldownEvent(createBySpell); } m_gameObj.Remove(gameObj); if (IsTypeId(TypeId.Unit) && ToCreature().IsAIEnabled) ToCreature().GetAI().SummonedGameobjectDespawn(gameObj); if (del) { gameObj.SetRespawnTime(0); gameObj.Delete(); } } public void RemoveGameObject(uint spellid, bool del) { if (m_gameObj.Empty()) return; foreach (var obj in m_gameObj) { if (spellid == 0 || obj.GetSpellId() == spellid) { obj.SetOwnerGUID(ObjectGuid.Empty); if (del) { obj.SetRespawnTime(0); obj.Delete(); } } } } public void RemoveAllGameObjects() { // remove references to unit while (!m_gameObj.Empty()) { var obj = m_gameObj.First(); obj.SetOwnerGUID(ObjectGuid.Empty); obj.SetRespawnTime(0); obj.Delete(); m_gameObj.Remove(obj); } } public void _RegisterAreaTrigger(AreaTrigger areaTrigger) { m_areaTrigger.Add(areaTrigger); if (IsTypeId(TypeId.Unit) && IsAIEnabled) ToCreature().GetAI().JustRegisteredAreaTrigger(areaTrigger); } public void _UnregisterAreaTrigger(AreaTrigger areaTrigger) { m_areaTrigger.Remove(areaTrigger); if (IsTypeId(TypeId.Unit) && IsAIEnabled) ToCreature().GetAI().JustUnregisteredAreaTrigger(areaTrigger); } AreaTrigger GetAreaTrigger(uint spellId) { List areaTriggers = GetAreaTriggers(spellId); return areaTriggers.Empty() ? null : areaTriggers[0]; } public List GetAreaTriggers(uint spellId) { return m_areaTrigger.Where(trigger => trigger.GetSpellId() == spellId).ToList(); } public void RemoveAreaTrigger(uint spellId) { if (m_areaTrigger.Empty()) return; for (var i = 0; i < m_areaTrigger.Count; ++i) { AreaTrigger areaTrigger = m_areaTrigger[i]; if (areaTrigger.GetSpellId() == spellId) areaTrigger.Remove(); } } public void RemoveAreaTrigger(AuraEffect aurEff) { if (m_areaTrigger.Empty()) return; foreach (AreaTrigger areaTrigger in m_areaTrigger) { if (areaTrigger.GetAuraEffect() == aurEff) { areaTrigger.Remove(); break; // There can only be one AreaTrigger per AuraEffect } } } public void RemoveAllAreaTriggers() { while (!m_areaTrigger.Empty()) m_areaTrigger[0].Remove(); } public bool HasNpcFlag(NPCFlags flags) { return (m_unitData.NpcFlags[0] & (uint)flags) != 0; } public void AddNpcFlag(NPCFlags flags) { SetUpdateFieldFlagValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NpcFlags, 0), (uint)flags); } public void RemoveNpcFlag(NPCFlags flags) { RemoveUpdateFieldFlagValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NpcFlags, 0), (uint)flags); } public void SetNpcFlags(NPCFlags flags) { SetUpdateFieldValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NpcFlags, 0), (uint)flags); } public bool HasNpcFlag2(NPCFlags2 flags) { return (m_unitData.NpcFlags[1] & (uint)flags) != 0; } public void AddNpcFlag2(NPCFlags2 flags) { SetUpdateFieldFlagValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NpcFlags, 1), (uint)flags); } public void RemoveNpcFlag2(NPCFlags2 flags) { RemoveUpdateFieldFlagValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NpcFlags, 1), (uint)flags); } public void SetNpcFlags2(NPCFlags2 flags) { SetUpdateFieldValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NpcFlags, 1), (uint)flags); } public bool IsVendor() { return HasNpcFlag(NPCFlags.Vendor); } public bool IsTrainer() { return HasNpcFlag(NPCFlags.Trainer); } public bool IsQuestGiver() { return HasNpcFlag(NPCFlags.QuestGiver); } public bool IsGossip() { return HasNpcFlag(NPCFlags.Gossip); } public bool IsTaxi() { return HasNpcFlag(NPCFlags.FlightMaster); } public bool IsGuildMaster() { return HasNpcFlag(NPCFlags.Petitioner); } public bool IsBattleMaster() { return HasNpcFlag(NPCFlags.BattleMaster); } public bool IsBanker() { return HasNpcFlag(NPCFlags.Banker); } public bool IsInnkeeper() { return HasNpcFlag(NPCFlags.Innkeeper); } public bool IsSpiritHealer() { return HasNpcFlag(NPCFlags.SpiritHealer); } public bool IsSpiritGuide() { return HasNpcFlag(NPCFlags.SpiritGuide); } public bool IsTabardDesigner() { return HasNpcFlag(NPCFlags.TabardDesigner); } public bool IsAuctioner() { return HasNpcFlag(NPCFlags.Auctioneer); } public bool IsArmorer() { return HasNpcFlag(NPCFlags.Repair); } public bool IsServiceProvider() { return HasNpcFlag(NPCFlags.Vendor | NPCFlags.Trainer | NPCFlags.FlightMaster | NPCFlags.Petitioner | NPCFlags.BattleMaster | NPCFlags.Banker | NPCFlags.Innkeeper | NPCFlags.SpiritHealer | NPCFlags.SpiritGuide | NPCFlags.TabardDesigner | NPCFlags.Auctioneer); } public bool IsSpiritService() { return HasNpcFlag(NPCFlags.SpiritHealer | NPCFlags.SpiritGuide); } public bool IsCritter() { return GetCreatureType() == CreatureType.Critter; } public bool IsInFlight() { return HasUnitState(UnitState.InFlight); } public void SetHoverHeight(float hoverHeight) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.HoverHeight), hoverHeight); } public Guardian GetGuardianPet() { ObjectGuid pet_guid = GetPetGUID(); if (!pet_guid.IsEmpty()) { Creature pet = ObjectAccessor.GetCreatureOrPetOrVehicle(this, pet_guid); if (pet != null) if (pet.HasUnitTypeMask(UnitTypeMask.Guardian)) return (Guardian)pet; Log.outFatal(LogFilter.Unit, "Unit:GetGuardianPet: Guardian {0} not exist.", pet_guid); SetPetGUID(ObjectGuid.Empty); } return null; } public Unit SelectNearbyTarget(Unit exclude = null, float dist = SharedConst.NominalMeleeRange) { List targets = new List(); var u_check = new AnyUnfriendlyUnitInObjectRangeCheck(this, this, dist); var searcher = new UnitListSearcher(this, targets, u_check); Cell.VisitAllObjects(this, searcher, dist); // remove current target if (GetVictim()) targets.Remove(GetVictim()); if (exclude) targets.Remove(exclude); // remove not LoS targets foreach (var unit in targets) { if (!IsWithinLOSInMap(unit) || unit.IsTotem() || unit.IsSpiritService() || unit.IsCritter()) targets.Remove(unit); } // no appropriate targets if (targets.Empty()) return null; // select random return targets.SelectRandom(); } public void EnterVehicle(Unit Base, sbyte seatId = -1) { CastCustomSpell(SharedConst.VehicleSpellRideHardcoded, SpellValueMod.BasePoint0, seatId + 1, Base, TriggerCastFlags.IgnoreCasterMountedOrOnVehicle); } public void _EnterVehicle(Vehicle vehicle, sbyte seatId, AuraApplication aurApp) { // Must be called only from aura handler Cypher.Assert(aurApp != null); if (!IsAlive() || GetVehicleKit() == vehicle || vehicle.GetBase().IsOnVehicle(this)) return; if (m_vehicle != null) { if (m_vehicle != vehicle) { Log.outDebug(LogFilter.Vehicle, "EnterVehicle: {0} exit {1} and enter {2}.", GetEntry(), m_vehicle.GetBase().GetEntry(), vehicle.GetBase().GetEntry()); ExitVehicle(); } else if (seatId >= 0 && seatId == GetTransSeat()) return; } if (aurApp.HasRemoveMode()) return; Player player = ToPlayer(); if (player != null) { if (vehicle.GetBase().IsTypeId(TypeId.Player) && player.IsInCombat()) { vehicle.GetBase().RemoveAura(aurApp); return; } } Cypher.Assert(!m_vehicle); vehicle.AddPassenger(this, seatId); } public void ChangeSeat(sbyte seatId, bool next = true) { if (m_vehicle == null) return; // Don't change if current and new seat are identical if (seatId == GetTransSeat()) return; var seat = (seatId < 0 ? m_vehicle.GetNextEmptySeat(GetTransSeat(), next) : m_vehicle.Seats.LookupByKey(seatId)); // The second part of the check will only return true if seatId >= 0. @Vehicle.GetNextEmptySeat makes sure of that. if (seat == null || !seat.IsEmpty()) return; AuraEffect rideVehicleEffect = null; var vehicleAuras = m_vehicle.GetBase().GetAuraEffectsByType(AuraType.ControlVehicle); foreach (var eff in vehicleAuras) { if (eff.GetCasterGUID() != GetGUID()) continue; // Make sure there is only one ride vehicle aura on target cast by the unit changing seat Cypher.Assert(rideVehicleEffect == null); rideVehicleEffect = eff; } // Unit riding a vehicle must always have control vehicle aura on target Cypher.Assert(rideVehicleEffect != null); rideVehicleEffect.ChangeAmount((seatId < 0 ? GetTransSeat() : seatId) + 1); } public void ExitVehicle(Position exitPosition = null) { //! This function can be called at upper level code to initialize an exit from the passenger's side. if (m_vehicle == null) return; GetVehicleBase().RemoveAurasByType(AuraType.ControlVehicle, GetGUID()); //! The following call would not even be executed successfully as the //! SPELL_AURA_CONTROL_VEHICLE unapply handler already calls _ExitVehicle without //! specifying an exitposition. The subsequent call below would return on if (!m_vehicle). //! To do: //! We need to allow SPELL_AURA_CONTROL_VEHICLE unapply handlers in spellscripts //! to specify exit coordinates and either store those per passenger, or we need to //! init spline movement based on those coordinates in unapply handlers, and //! relocate exiting passengers based on Unit.moveSpline data. Either way, //! Coming Soon(TM) } public void _ExitVehicle(Position exitPosition = null) { // It's possible m_vehicle is NULL, when this function is called indirectly from @VehicleJoinEvent.Abort. // In that case it was not possible to add the passenger to the vehicle. The vehicle aura has already been removed // from the target in the aforementioned function and we don't need to do anything else at this point. if (m_vehicle == null) return; // This should be done before dismiss, because there may be some aura removal Vehicle vehicle = m_vehicle.RemovePassenger(this); Player player = ToPlayer(); // If the player is on mounted duel and exits the mount, he should immediatly lose the duel if (player && player.duel != null && player.duel.isMounted) player.DuelComplete(DuelCompleteType.Fled); SetControlled(false, UnitState.Root); // SMSG_MOVE_FORCE_UNROOT, ~MOVEMENTFLAG_ROOT Position pos; if (exitPosition == null) // Exit position not specified pos = vehicle.GetBase().GetPosition(); // This should use passenger's current position, leaving it as it is now // because we calculate positions incorrect (sometimes under map) else pos = exitPosition; AddUnitState(UnitState.Move); if (player != null) player.SetFallInformation(0, GetPositionZ()); float height = pos.GetPositionZ(); MoveSplineInit init = new MoveSplineInit(this); // Creatures without inhabit type air should begin falling after exiting the vehicle if (IsTypeId(TypeId.Unit) && !ToCreature().CanFly() && height > GetMap().GetWaterOrGroundLevel(GetPhaseShift(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), ref height) + 0.1f) init.SetFall(); init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), height, false); init.SetFacing(GetOrientation()); init.SetTransportExit(); init.Launch(); if (player != null) player.ResummonPetTemporaryUnSummonedIfAny(); if (vehicle.GetBase().HasUnitTypeMask(UnitTypeMask.Minion) && vehicle.GetBase().IsTypeId(TypeId.Unit)) if (((Minion)vehicle.GetBase()).GetOwner() == this) vehicle.GetBase().ToCreature().DespawnOrUnsummon(vehicle.GetDespawnDelay()); if (HasUnitTypeMask(UnitTypeMask.Accessory)) { // Vehicle just died, we die too if (vehicle.GetBase().GetDeathState() == DeathState.JustDied) SetDeathState(DeathState.JustDied); // If for other reason we as minion are exiting the vehicle (ejected, master dismounted) - unsummon else ToTempSummon().UnSummon(2000); // Approximation } } void SendCancelOrphanSpellVisual(uint id) { CancelOrphanSpellVisual cancelOrphanSpellVisual = new CancelOrphanSpellVisual(); cancelOrphanSpellVisual.SpellVisualID = id; SendMessageToSet(cancelOrphanSpellVisual, true); } void SendPlayOrphanSpellVisual(ObjectGuid target, uint spellVisualId, float travelSpeed, bool speedAsTime = false, bool withSourceOrientation = false) { PlayOrphanSpellVisual playOrphanSpellVisual = new PlayOrphanSpellVisual(); playOrphanSpellVisual.SourceLocation = GetPosition(); if (withSourceOrientation) playOrphanSpellVisual.SourceRotation = new Vector3(0.0f, 0.0f, GetOrientation()); playOrphanSpellVisual.Target = target; // exclusive with TargetLocation playOrphanSpellVisual.SpellVisualID = spellVisualId; playOrphanSpellVisual.TravelSpeed = travelSpeed; playOrphanSpellVisual.SpeedAsTime = speedAsTime; playOrphanSpellVisual.LaunchDelay = 0.0f; SendMessageToSet(playOrphanSpellVisual, true); } void SendPlayOrphanSpellVisual(Vector3 targetLocation, uint spellVisualId, float travelSpeed, bool speedAsTime = false, bool withSourceOrientation = false) { PlayOrphanSpellVisual playOrphanSpellVisual = new PlayOrphanSpellVisual(); playOrphanSpellVisual.SourceLocation = GetPosition(); if (withSourceOrientation) playOrphanSpellVisual.SourceRotation = new Vector3(0.0f, 0.0f, GetOrientation()); playOrphanSpellVisual.TargetLocation = targetLocation; // exclusive with Target playOrphanSpellVisual.SpellVisualID = spellVisualId; playOrphanSpellVisual.TravelSpeed = travelSpeed; playOrphanSpellVisual.SpeedAsTime = speedAsTime; playOrphanSpellVisual.LaunchDelay = 0.0f; SendMessageToSet(playOrphanSpellVisual, true); } void SendCancelSpellVisual(uint id) { CancelSpellVisual cancelSpellVisual = new CancelSpellVisual(); cancelSpellVisual.Source = GetGUID(); cancelSpellVisual.SpellVisualID = id; SendMessageToSet(cancelSpellVisual, true); } public void SendPlaySpellVisual(ObjectGuid targetGuid, uint spellVisualId, uint missReason, uint reflectStatus, float travelSpeed, bool speedAsTime = false) { PlaySpellVisual playSpellVisual = new PlaySpellVisual(); playSpellVisual.Source = GetGUID(); playSpellVisual.Target = targetGuid; // exclusive with TargetPosition playSpellVisual.SpellVisualID = spellVisualId; playSpellVisual.TravelSpeed = travelSpeed; playSpellVisual.MissReason = (ushort)missReason; playSpellVisual.ReflectStatus = (ushort)reflectStatus; playSpellVisual.SpeedAsTime = speedAsTime; SendMessageToSet(playSpellVisual, true); } public void SendPlaySpellVisual(Vector3 targetPosition, float launchDelay, uint spellVisualId, uint missReason, uint reflectStatus, float travelSpeed, bool speedAsTime = false) { PlaySpellVisual playSpellVisual = new PlaySpellVisual(); playSpellVisual.Source = GetGUID(); playSpellVisual.TargetPosition = targetPosition; // exclusive with Target playSpellVisual.LaunchDelay = launchDelay; playSpellVisual.SpellVisualID = spellVisualId; playSpellVisual.TravelSpeed = travelSpeed; playSpellVisual.MissReason = (ushort)missReason; playSpellVisual.ReflectStatus = (ushort)reflectStatus; playSpellVisual.SpeedAsTime = speedAsTime; SendMessageToSet(playSpellVisual, true); } void SendCancelSpellVisualKit(uint id) { CancelSpellVisualKit cancelSpellVisualKit = new CancelSpellVisualKit(); cancelSpellVisualKit.Source = GetGUID(); cancelSpellVisualKit.SpellVisualKitID = id; SendMessageToSet(cancelSpellVisualKit, true); } public void SendPlaySpellVisualKit(uint id, uint type, uint duration) { PlaySpellVisualKit playSpellVisualKit = new PlaySpellVisualKit(); playSpellVisualKit.Unit = GetGUID(); playSpellVisualKit.KitRecID = id; playSpellVisualKit.KitType = type; playSpellVisualKit.Duration = duration; SendMessageToSet(playSpellVisualKit, true); } void CancelSpellMissiles(uint spellId, bool reverseMissile = false) { bool hasMissile = false; foreach (var pair in m_Events.GetEvents()) { Spell spell = Spell.ExtractSpellFromEvent(pair.Value); if (spell != null) { if (spell.GetSpellInfo().Id == spellId) { pair.Value.ScheduleAbort(); hasMissile = true; } } } if (hasMissile) { MissileCancel packet = new MissileCancel(); packet.OwnerGUID = GetGUID(); packet.SpellID = spellId; packet.Reverse = reverseMissile; SendMessageToSet(packet, false); } } public void UnsummonAllTotems() { for (byte i = 0; i < SharedConst.MaxSummonSlot; ++i) { if (m_SummonSlot[i].IsEmpty()) continue; Creature OldTotem = GetMap().GetCreature(m_SummonSlot[i]); if (OldTotem != null) if (OldTotem.IsSummon()) OldTotem.ToTempSummon().UnSummon(); } } public bool IsOnVehicle(Unit vehicle) { return m_vehicle != null && m_vehicle == vehicle.GetVehicleKit(); } public virtual UnitAI GetAI() { return i_AI; } public void SetAI(UnitAI newAI) { i_AI = newAI; } public bool IsPossessing() { Unit u = GetCharm(); if (u != null) return u.IsPossessed(); else return false; } public Unit GetCharm() { ObjectGuid charm_guid = GetCharmGUID(); if (!charm_guid.IsEmpty()) { Unit pet = Global.ObjAccessor.GetUnit(this, charm_guid); if (pet != null) return pet; Log.outError(LogFilter.Unit, "Unit.GetCharm: Charmed creature {0} not exist.", charm_guid); SetCharmGUID(ObjectGuid.Empty); } return null; } public bool IsCharmed() { return !GetCharmerGUID().IsEmpty(); } public bool IsPossessed() { return HasUnitState(UnitState.Possessed); } public HostileRefManager GetHostileRefManager() { return hostileRefManager; } public void OnPhaseChange() { if (!IsInWorld) return; if (IsTypeId(TypeId.Unit) || !ToPlayer().GetSession().PlayerLogout()) { HostileRefManager refManager = GetHostileRefManager(); HostileReference refe = refManager.GetFirst(); while (refe != null) { Unit unit = refe.GetSource().GetOwner(); if (unit != null) { Creature creature = unit.ToCreature(); if (creature != null) refManager.SetOnlineOfflineState(creature, creature.IsInPhase(this)); } refe = refe.Next(); } // modify threat lists for new phasemask if (!IsTypeId(TypeId.Player)) { List threatList = GetThreatManager().GetThreatList(); List offlineThreatList = GetThreatManager().GetOfflineThreatList(); // merge expects sorted lists threatList.Sort(); offlineThreatList.Sort(); threatList.AddRange(offlineThreatList); foreach (var host in threatList) { Unit unit = host.GetTarget(); if (unit != null) unit.GetHostileRefManager().SetOnlineOfflineState(ToCreature(), unit.IsInPhase(this)); } } } } public uint GetModelForForm(ShapeShiftForm form, uint spellId) { // Hardcoded cases switch (spellId) { case 7090: // Bear Form return 29414; case 35200: // Roc Form return 4877; default: break; } Player thisPlayer = ToPlayer(); if (thisPlayer != null) { Aura artifactAura = GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); if (artifactAura != null) { Item artifact = ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID()); if (artifact != null) { ArtifactAppearanceRecord artifactAppearance = CliDB.ArtifactAppearanceStorage.LookupByKey(artifact.GetModifier(ItemModifier.ArtifactAppearanceId)); if (artifactAppearance != null) if ((ShapeShiftForm)artifactAppearance.OverrideShapeshiftFormID == form) return artifactAppearance.OverrideShapeshiftDisplayID; } } ShapeshiftFormModelData formModelData = Global.DB2Mgr.GetShapeshiftFormModelData(GetRace(), thisPlayer.GetNativeSex(), form); if (formModelData != null) { bool useRandom = false; switch (form) { case ShapeShiftForm.CatForm: useRandom = HasAura(210333); break; // Glyph of the Feral Chameleon case ShapeShiftForm.TravelForm: useRandom = HasAura(344336); break; // Glyph of the Swift Chameleon case ShapeShiftForm.AquaticForm: useRandom = HasAura(344338); break; // Glyph of the Aquatic Chameleon case ShapeShiftForm.BearForm: useRandom = HasAura(107059); break; // Glyph of the Ursol Chameleon case ShapeShiftForm.FlightForm: useRandom = HasAura(344342); break; // Glyph of the Aerial Chameleon default: break; } if (useRandom) { List displayIds = new List(); for (var i = 0; i < formModelData.Choices.Count; ++i) { ChrCustomizationDisplayInfoRecord displayInfo = formModelData.Displays[i]; if (displayInfo != null) { ChrCustomizationReqRecord choiceReq = CliDB.ChrCustomizationReqStorage.LookupByKey(formModelData.Choices[i].ChrCustomizationReqID); if (choiceReq == null || thisPlayer.GetSession().MeetsChrCustomizationReq(choiceReq, GetClass(), false, thisPlayer.m_playerData.Customizations)) displayIds.Add(displayInfo.DisplayID); } } if (!displayIds.Empty()) return displayIds.SelectRandom(); } else { uint formChoice = thisPlayer.GetCustomizationChoice(formModelData.OptionID); if (formChoice != 0) { var choiceIndex = formModelData.Choices.FindIndex(choice => { return choice.Id == formChoice; }); if (choiceIndex != -1) { ChrCustomizationDisplayInfoRecord displayInfo = formModelData.Displays[choiceIndex]; if (displayInfo != null) return displayInfo.DisplayID; } } } } switch (form) { case ShapeShiftForm.GhostWolf: if (HasAura(58135)) // Glyph of Spectral Wolf return 60247; break; default: break; } } uint modelid = 0; SpellShapeshiftFormRecord formEntry = CliDB.SpellShapeshiftFormStorage.LookupByKey(form); if (formEntry != null && formEntry.CreatureDisplayID[0] != 0) { // Take the alliance modelid as default if (GetTypeId() != TypeId.Player) return formEntry.CreatureDisplayID[0]; else { if (Player.TeamForRace(GetRace()) == Team.Alliance) modelid = formEntry.CreatureDisplayID[0]; else modelid = formEntry.CreatureDisplayID[1]; // If the player is horde but there are no values for the horde modelid - take the alliance modelid if (modelid == 0 && Player.TeamForRace(GetRace()) == Team.Horde) modelid = formEntry.CreatureDisplayID[0]; } } return modelid; } public Totem ToTotem() { return IsTotem() ? (this as Totem) : null; } public TempSummon ToTempSummon() { return IsSummon() ? (this as TempSummon) : null; } public virtual void SetDeathState(DeathState s) { // Death state needs to be updated before RemoveAllAurasOnDeath() is called, to prevent entering combat m_deathState = s; if (s != DeathState.Alive && s != DeathState.JustRespawned) { CombatStop(); GetThreatManager().ClearAllThreat(); GetHostileRefManager().DeleteReferences(); if (IsNonMeleeSpellCast(false)) InterruptNonMeleeSpells(false); ExitVehicle(); // Exit vehicle before calling RemoveAllControlled // vehicles use special type of charm that is not removed by the next function // triggering an assert UnsummonAllTotems(); RemoveAllControlled(); RemoveAllAurasOnDeath(); } if (s == DeathState.JustDied) { // remove aurastates allowing special moves ClearAllReactives(); m_Diminishing.Clear(); if (IsInWorld) { // Only clear MotionMaster for entities that exists in world // Avoids crashes in the following conditions : // * Using 'call pet' on dead pets // * Using 'call stabled pet' // * Logging in with dead pets GetMotionMaster().Clear(false); GetMotionMaster().MoveIdle(); } StopMoving(); DisableSpline(); // without this when removing IncreaseMaxHealth aura player may stuck with 1 hp // do not why since in IncreaseMaxHealth currenthealth is checked SetHealth(0); SetPower(GetPowerType(), 0); SetEmoteState(Emote.OneshotNone); // players in instance don't have ZoneScript, but they have InstanceScript ZoneScript zoneScript = GetZoneScript() != null ? GetZoneScript() : GetInstanceScript(); if (zoneScript != null) zoneScript.OnUnitDeath(this); } else if (s == DeathState.JustRespawned) RemoveUnitFlag(UnitFlags.Skinnable); // clear skinnable for creature and player (at Battleground) } public bool IsVisible() { return (m_serverSideVisibility.GetValue(ServerSideVisibilityType.GM) > (uint)AccountTypes.Player) ? false : true; } public void SetVisible(bool val) { if (!val) m_serverSideVisibility.SetValue(ServerSideVisibilityType.GM, AccountTypes.GameMaster); else m_serverSideVisibility.SetValue(ServerSideVisibilityType.GM, AccountTypes.Player); UpdateObjectVisibility(); } public bool IsMagnet() { // Grounding Totem if (m_unitData.CreatedBySpell == 8177) /// @todo: find a more generic solution return true; return false; } public void SetShapeshiftForm(ShapeShiftForm form) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ShapeshiftForm), (byte)form); } public int CalcSpellDuration(SpellInfo spellProto) { sbyte comboPoints = (sbyte)(m_playerMovingMe != null ? m_playerMovingMe.GetComboPoints() : 0); int minduration = spellProto.GetDuration(); int maxduration = spellProto.GetMaxDuration(); int duration; if (comboPoints != 0 && minduration != -1 && minduration != maxduration) duration = minduration + (maxduration - minduration) * comboPoints / 5; else duration = minduration; return duration; } public int ModSpellDuration(SpellInfo spellProto, Unit target, int duration, bool positive, uint effectMask) { // don't mod permanent auras duration if (duration < 0) return duration; // some auras are not affected by duration modifiers if (spellProto.HasAttribute(SpellAttr7.IgnoreDurationMods)) return duration; // cut duration only of negative effects if (!positive) { uint mechanic = spellProto.GetSpellMechanicMaskByEffectMask(effectMask); int durationMod; int durationMod_always = 0; int durationMod_not_stack = 0; for (byte i = 1; i <= (int)Mechanics.Enraged; ++i) { if (!Convert.ToBoolean(mechanic & 1 << i)) continue; // Find total mod value (negative bonus) int new_durationMod_always = target.GetTotalAuraModifierByMiscValue(AuraType.MechanicDurationMod, i); // Find max mod (negative bonus) int new_durationMod_not_stack = target.GetMaxNegativeAuraModifierByMiscValue(AuraType.MechanicDurationModNotStack, i); // Check if mods applied before were weaker if (new_durationMod_always < durationMod_always) durationMod_always = new_durationMod_always; if (new_durationMod_not_stack < durationMod_not_stack) durationMod_not_stack = new_durationMod_not_stack; } // Select strongest negative mod if (durationMod_always > durationMod_not_stack) durationMod = durationMod_not_stack; else durationMod = durationMod_always; if (durationMod != 0) MathFunctions.AddPct(ref duration, durationMod); // there are only negative mods currently durationMod_always = target.GetTotalAuraModifierByMiscValue(AuraType.ModAuraDurationByDispel, (int)spellProto.Dispel); durationMod_not_stack = target.GetMaxNegativeAuraModifierByMiscValue(AuraType.ModAuraDurationByDispelNotStack, (int)spellProto.Dispel); durationMod = 0; if (durationMod_always > durationMod_not_stack) durationMod += durationMod_not_stack; else durationMod += durationMod_always; if (durationMod != 0) MathFunctions.AddPct(ref duration, durationMod); } else { // else positive mods here, there are no currently // when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue // Mixology - duration boost if (target.IsTypeId(TypeId.Player)) { if (spellProto.SpellFamilyName == SpellFamilyNames.Potion && ( Global.SpellMgr.IsSpellMemberOfSpellGroup(spellProto.Id, SpellGroup.ElixirBattle) || Global.SpellMgr.IsSpellMemberOfSpellGroup(spellProto.Id, SpellGroup.ElixirGuardian))) { SpellEffectInfo effect = spellProto.GetEffect(0); if (target.HasAura(53042) && effect != null && target.HasSpell(effect.TriggerSpell)) duration *= 2; } } } return Math.Max(duration, 0); } // creates aura application instance and registers it in lists // aura application effects are handled separately to prevent aura list corruption public AuraApplication _CreateAuraApplication(Aura aura, uint effMask) { // can't apply aura on unit which is going to be deleted - to not create a memory leak Cypher.Assert(!m_cleanupDone); // aura musn't be removed Cypher.Assert(!aura.IsRemoved()); // aura mustn't be already applied on target Cypher.Assert(!aura.IsAppliedOnTarget(GetGUID()), "Unit._CreateAuraApplication: aura musn't be applied on target"); SpellInfo aurSpellInfo = aura.GetSpellInfo(); uint aurId = aurSpellInfo.Id; // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) if (!IsAlive() && !aurSpellInfo.IsDeathPersistent() && (!IsTypeId(TypeId.Player) || !ToPlayer().GetSession().PlayerLoading())) return null; Unit caster = aura.GetCaster(); AuraApplication aurApp = new AuraApplication(this, caster, aura, effMask); m_appliedAuras.Add(aurId, aurApp); if (aurSpellInfo.HasAnyAuraInterruptFlag()) { m_interruptableAuras.Add(aurApp); AddInterruptMask(aurSpellInfo.AuraInterruptFlags); } AuraStateType aState = aura.GetSpellInfo().GetAuraState(); if (aState != 0) m_auraStateAuras.Add(aState, aurApp); aura._ApplyForTarget(this, caster, aurApp); return aurApp; } public void AddInterruptMask(uint[] mask) { for (int i = 0; i < m_interruptMask.Length; ++i) m_interruptMask[i] |= mask[i]; } void _UpdateAutoRepeatSpell() { SpellInfo autoRepeatSpellInfo = m_currentSpells[CurrentSpellTypes.AutoRepeat].m_spellInfo; // check "realtime" interrupts // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect if (((IsTypeId(TypeId.Player) && ToPlayer().IsMoving()) || IsNonMeleeSpellCast(false, false, true, autoRepeatSpellInfo.Id == 75)) && !HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, autoRepeatSpellInfo)) { // cancel wand shoot if (autoRepeatSpellInfo.Id != 75) InterruptSpell(CurrentSpellTypes.AutoRepeat); m_AutoRepeatFirstCast = true; return; } // apply delay (Auto Shot (spellID 75) not affected) if (m_AutoRepeatFirstCast && GetAttackTimer(WeaponAttackType.RangedAttack) < 500 && autoRepeatSpellInfo.Id != 75) SetAttackTimer(WeaponAttackType.RangedAttack, 500); m_AutoRepeatFirstCast = false; // castroutine if (IsAttackReady(WeaponAttackType.RangedAttack)) { // Check if able to cast SpellCastResult result = m_currentSpells[CurrentSpellTypes.AutoRepeat].CheckCast(true); if (result != SpellCastResult.SpellCastOk) { if (autoRepeatSpellInfo.Id != 75) InterruptSpell(CurrentSpellTypes.AutoRepeat); else if (GetTypeId() == TypeId.Player) Spell.SendCastResult(ToPlayer(), autoRepeatSpellInfo, m_currentSpells[CurrentSpellTypes.AutoRepeat].m_SpellVisual, m_currentSpells[CurrentSpellTypes.AutoRepeat].m_castId, result); return; } // we want to shoot Spell spell = new Spell(this, autoRepeatSpellInfo, TriggerCastFlags.FullMask); spell.Prepare(m_currentSpells[CurrentSpellTypes.AutoRepeat].m_targets); // all went good, reset attack ResetAttackTimer(WeaponAttackType.RangedAttack); } } public void UpdateDisplayPower() { PowerType displayPower = PowerType.Mana; switch (GetShapeshiftForm()) { case ShapeShiftForm.Ghoul: case ShapeShiftForm.CatForm: displayPower = PowerType.Energy; break; case ShapeShiftForm.BearForm: displayPower = PowerType.Rage; break; case ShapeShiftForm.TravelForm: case ShapeShiftForm.GhostWolf: displayPower = PowerType.Mana; break; default: { var powerTypeAuras = GetAuraEffectsByType(AuraType.ModPowerDisplay); if (!powerTypeAuras.Empty()) { AuraEffect powerTypeAura = powerTypeAuras.First(); displayPower = (PowerType)powerTypeAura.GetMiscValue(); } else if (GetTypeId() == TypeId.Player) { ChrClassesRecord cEntry = CliDB.ChrClassesStorage.LookupByKey(GetClass()); if (cEntry != null && cEntry.DisplayPower < PowerType.Max) displayPower = cEntry.DisplayPower; } else if (GetTypeId() == TypeId.Unit) { Vehicle vehicle = GetVehicleKit(); if (vehicle) { PowerDisplayRecord powerDisplay = CliDB.PowerDisplayStorage.LookupByKey(vehicle.GetVehicleInfo().PowerDisplayID[0]); if (powerDisplay != null) displayPower = (PowerType)powerDisplay.ActualType; else if (GetClass() == Class.Rogue) displayPower = PowerType.Energy; } else { Pet pet = ToPet(); if (pet) { if (pet.GetPetType() == PetType.Hunter) // Hunter pets have focus displayPower = PowerType.Focus; else if (pet.IsPetGhoul() || pet.IsPetAbomination()) // DK pets have energy displayPower = PowerType.Energy; } } } break; } } SetPowerType(displayPower); } public FactionTemplateRecord GetFactionTemplateEntry() { FactionTemplateRecord entry = CliDB.FactionTemplateStorage.LookupByKey(GetFaction()); if (entry == null) { Player player = ToPlayer(); if (player != null) Log.outError(LogFilter.Unit, "Player {0} has invalid faction (faction template id) #{1}", player.GetName(), GetFaction()); else { Creature creature = ToCreature(); if (creature != null) Log.outError(LogFilter.Unit, "Creature (template id: {0}) has invalid faction (faction template id) #{1}", creature.GetCreatureTemplate().Entry, GetFaction()); else Log.outError(LogFilter.Unit, "Unit (name={0}, type={1}) has invalid faction (faction template id) #{2}", GetName(), GetTypeId(), GetFaction()); } } return entry; } public bool IsInFeralForm() { ShapeShiftForm form = GetShapeshiftForm(); return form == ShapeShiftForm.CatForm || form == ShapeShiftForm.BearForm || form == ShapeShiftForm.DireBearForm || form == ShapeShiftForm.GhostWolf; } public bool IsControlledByPlayer() { return m_ControlledByPlayer; } public bool IsCharmedOwnedByPlayerOrPlayer() { return GetCharmerOrOwnerOrOwnGUID().IsPlayer(); } public void AddFollower(FollowerReference pRef) { m_FollowingRefManager.InsertFirst(pRef); } public void RemoveFollower(FollowerReference pRef) { } //nothing to do yet public uint GetCreatureTypeMask() { uint creatureType = (uint)GetCreatureType(); return (uint)(creatureType >= 1 ? (1 << (int)(creatureType - 1)) : 0); } public Pet ToPet() { return IsPet() ? (this as Pet) : null; } public MotionMaster GetMotionMaster() { return i_motionMaster; } public void PlayOneShotAnimKitId(ushort animKitId) { if (!CliDB.AnimKitStorage.ContainsKey(animKitId)) { Log.outError(LogFilter.Unit, "Unit.PlayOneShotAnimKitId using invalid AnimKit ID: {0}", animKitId); return; } PlayOneShotAnimKit packet = new PlayOneShotAnimKit(); packet.Unit = GetGUID(); packet.AnimKitID = animKitId; SendMessageToSet(packet, true); } public void SetAIAnimKitId(ushort animKitId) { if (_aiAnimKitId == animKitId) return; if (animKitId != 0 && !CliDB.AnimKitStorage.ContainsKey(animKitId)) return; _aiAnimKitId = animKitId; SetAIAnimKit data = new SetAIAnimKit(); data.Unit = GetGUID(); data.AnimKitID = animKitId; SendMessageToSet(data, true); } public override ushort GetAIAnimKitId() { return _aiAnimKitId; } public void SetMovementAnimKitId(ushort animKitId) { if (_movementAnimKitId == animKitId) return; if (animKitId != 0 && !CliDB.AnimKitStorage.ContainsKey(animKitId)) return; _movementAnimKitId = animKitId; SetMovementAnimKit data = new SetMovementAnimKit(); data.Unit = GetGUID(); data.AnimKitID = animKitId; SendMessageToSet(data, true); } public override ushort GetMovementAnimKitId() { return _movementAnimKitId; } public void SetMeleeAnimKitId(ushort animKitId) { if (_meleeAnimKitId == animKitId) return; if (animKitId != 0 && !CliDB.AnimKitStorage.ContainsKey(animKitId)) return; _meleeAnimKitId = animKitId; SetMeleeAnimKit data = new SetMeleeAnimKit(); data.Unit = GetGUID(); data.AnimKitID = animKitId; SendMessageToSet(data, true); } public override ushort GetMeleeAnimKitId() { return _meleeAnimKitId; } public uint GetVirtualItemId(int slot) { if (slot >= SharedConst.MaxEquipmentItems) return 0; return m_unitData.VirtualItems[slot].ItemID; } public ushort GetVirtualItemAppearanceMod(uint slot) { if (slot >= SharedConst.MaxEquipmentItems) return 0; return m_unitData.VirtualItems[(int)slot].ItemAppearanceModID; } public void SetVirtualItem(uint slot, uint itemId, ushort appearanceModId = 0, ushort itemVisual = 0) { if (slot >= SharedConst.MaxEquipmentItems) return; var virtualItemField = m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.VirtualItems, (int)slot); SetUpdateFieldValue(virtualItemField.ModifyValue(virtualItemField.ItemID), itemId); SetUpdateFieldValue(virtualItemField.ModifyValue(virtualItemField.ItemAppearanceModID), appearanceModId); SetUpdateFieldValue(virtualItemField.ModifyValue(virtualItemField.ItemVisual), itemVisual); } //Unit public void SetLevel(uint lvl) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Level), lvl); Player player = ToPlayer(); if (player != null) { if (player.GetGroup()) player.SetGroupUpdateFlag(GroupUpdateFlags.Level); Global.CharacterCacheStorage.UpdateCharacterLevel(ToPlayer().GetGUID(), (byte)lvl); } } public uint GetLevel() { return m_unitData.Level; } public override uint GetLevelForTarget(WorldObject target) { return GetLevel(); } public Race GetRace() { return (Race)(byte)m_unitData.Race; } public void SetRace(Race race) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Race), (byte)race); } public Class GetClass() { return (Class)(byte)m_unitData.ClassId; } public void SetClass(Class classId) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ClassId), (byte)classId); } public uint GetClassMask() { return (uint)(1 << ((int)GetClass() - 1)); } public Gender GetGender() { return (Gender)(byte)m_unitData.Sex; } public void SetGender(Gender sex) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Sex), (byte)sex); } public uint GetDisplayId() { return m_unitData.DisplayID; } public virtual void SetDisplayId(uint modelId, float displayScale = 1f) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.DisplayID), modelId); SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.DisplayScale), displayScale); // Set Gender by modelId CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelInfo(modelId); if (minfo != null) SetGender((Gender)minfo.gender); } public void RestoreDisplayId(bool ignorePositiveAurasPreventingMounting = false) { AuraEffect handledAura = null; // try to receive model from transform auras var transforms = GetAuraEffectsByType(AuraType.Transform); if (!transforms.Empty()) { // iterate over already applied transform auras - from newest to oldest foreach (var eff in transforms) { AuraApplication aurApp = eff.GetBase().GetApplicationOfTarget(GetGUID()); if (aurApp != null) { if (handledAura == null) { if (!ignorePositiveAurasPreventingMounting) handledAura = eff; else { CreatureTemplate ci = Global.ObjectMgr.GetCreatureTemplate((uint)eff.GetMiscValue()); if (ci != null) if (!IsDisallowedMountForm(eff.GetId(), ShapeShiftForm.None, ObjectManager.ChooseDisplayId(ci).CreatureDisplayID)) handledAura = eff; } } // prefer negative auras if (!aurApp.IsPositive()) { handledAura = eff; break; } } } } var shapeshiftAura = GetAuraEffectsByType(AuraType.ModShapeshift); // transform aura was found if (handledAura != null) { handledAura.HandleEffect(this, AuraEffectHandleModes.SendForClient, true); return; } // we've found shapeshift else if (!shapeshiftAura.Empty()) // we've found shapeshift { // only one such aura possible at a time uint modelId = GetModelForForm(GetShapeshiftForm(), shapeshiftAura[0].GetId()); if (modelId != 0) { if (!ignorePositiveAurasPreventingMounting || !IsDisallowedMountForm(0, GetShapeshiftForm(), modelId)) SetDisplayId(modelId); else SetDisplayId(GetNativeDisplayId()); return; } } // no auras found - set modelid to default SetDisplayId(GetNativeDisplayId()); } public uint GetNativeDisplayId() { return m_unitData.NativeDisplayID; } public void SetNativeDisplayId(uint displayId, float displayScale = 1f) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NativeDisplayID), displayId); SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.NativeXDisplayScale), displayScale); } public float GetNativeDisplayScale() { return m_unitData.NativeXDisplayScale; } public bool IsMounted() { return HasUnitFlag(UnitFlags.Mount); } public uint GetMountDisplayId() { return m_unitData.MountDisplayID; } public void SetMountDisplayId(uint mountDisplayId) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.MountDisplayID), mountDisplayId); } public virtual Unit GetOwner() { ObjectGuid ownerid = GetOwnerGUID(); if (!ownerid.IsEmpty()) return Global.ObjAccessor.GetUnit(this, ownerid); return null; } public virtual float GetFollowAngle() { return MathFunctions.PiOver2; } public ObjectGuid GetOwnerGUID() { return m_unitData.SummonedBy; } public void SetOwnerGUID(ObjectGuid owner) { if (GetOwnerGUID() == owner) return; SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.SummonedBy), owner); if (owner.IsEmpty()) return; // Update owner dependent fields Player player = Global.ObjAccessor.GetPlayer(this, owner); if (player == null || !player.HaveAtClient(this)) // if player cannot see this unit yet, he will receive needed data with create object return; UpdateData udata = new UpdateData(GetMapId()); UpdateObject packet; BuildValuesUpdateBlockForPlayerWithFlag(udata, UpdateFieldFlag.Owner, player); udata.BuildPacket(out packet); player.SendPacket(packet); } public ObjectGuid GetCreatorGUID() { return m_unitData.CreatedBy; } public void SetCreatorGUID(ObjectGuid creator) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.CreatedBy), creator); } public ObjectGuid GetMinionGUID() { return m_unitData.Summon; } public void SetMinionGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Summon), guid); } public ObjectGuid GetCharmerGUID() { return m_unitData.CharmedBy; } public void SetCharmerGUID(ObjectGuid owner) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.CharmedBy), owner); } public ObjectGuid GetCharmGUID() { return m_unitData.Charm; } public void SetCharmGUID(ObjectGuid charm) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Charm), charm); } public ObjectGuid GetPetGUID() { return m_SummonSlot[0]; } public void SetPetGUID(ObjectGuid guid) { m_SummonSlot[0] = guid; } public ObjectGuid GetCritterGUID() { return m_unitData.Critter; } public void SetCritterGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Critter), guid); } public ObjectGuid GetBattlePetCompanionGUID() { return m_unitData.BattlePetCompanionGUID; } public void SetBattlePetCompanionGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.BattlePetCompanionGUID), guid); } public ObjectGuid GetCharmerOrOwnerGUID() { return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID(); } public ObjectGuid GetCharmerOrOwnerOrOwnGUID() { ObjectGuid guid = GetCharmerOrOwnerGUID(); if (!guid.IsEmpty()) return guid; return GetGUID(); } public Unit GetCharmer() { ObjectGuid charmerid = GetCharmerGUID(); if (!charmerid.IsEmpty()) return Global.ObjAccessor.GetUnit(this, charmerid); return null; } public Unit GetCharmerOrOwnerOrSelf() { Unit u = GetCharmerOrOwner(); if (u != null) return u; return this; } public Player GetCharmerOrOwnerPlayerOrPlayerItself() { ObjectGuid guid = GetCharmerOrOwnerGUID(); if (guid.IsPlayer()) return Global.ObjAccessor.FindPlayer(guid); return IsTypeId(TypeId.Player) ? ToPlayer() : null; } public Unit GetCharmerOrOwner() { return !GetCharmerGUID().IsEmpty() ? GetCharmer() : GetOwner(); } public bool HasUnitFlag(UnitFlags flags) { return (m_unitData.Flags & (uint)flags) != 0; } public void AddUnitFlag(UnitFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags), (uint)flags); } public void RemoveUnitFlag(UnitFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags), (uint)flags); } public void SetUnitFlags(UnitFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags), (uint)flags); } public bool HasUnitFlag2(UnitFlags2 flags) { return (m_unitData.Flags2 & (uint)flags) != 0; } public void AddUnitFlag2(UnitFlags2 flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags2), (uint)flags); } public void RemoveUnitFlag2(UnitFlags2 flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags2), (uint)flags); } public void SetUnitFlags2(UnitFlags2 flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags2), (uint)flags); } public bool HasUnitFlag3(UnitFlags3 flags) { return (m_unitData.Flags3 & (uint)flags) != 0; } public void AddUnitFlag3(UnitFlags3 flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags3), (uint)flags); } public void RemoveUnitFlag3(UnitFlags3 flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags3), (uint)flags); } public void SetUnitFlags3(UnitFlags3 flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags3), (uint)flags); } public void SetCreatedBySpell(uint spellId) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.CreatedBySpell), spellId); } public Emote GetEmoteState() { return (Emote)(int)m_unitData.EmoteState; } public void SetEmoteState(Emote emote) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.EmoteState), (int)emote); } public SheathState GetSheath() { return (SheathState)(byte)m_unitData.SheatheState; } public void SetSheath(SheathState sheathed) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.SheatheState), (byte)sheathed); } public uint GetCombatTimer() { return combatTimer; } public UnitPVPStateFlags GetPvpFlags() { return (UnitPVPStateFlags)(byte)m_unitData.PvpFlags; } public bool HasPvpFlag(UnitPVPStateFlags flags) { return (m_unitData.PvpFlags & (uint)flags) != 0; } public void AddPvpFlag(UnitPVPStateFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PvpFlags), (byte)flags); } public void RemovePvpFlag(UnitPVPStateFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PvpFlags), (byte)flags); } public void SetPvpFlags(UnitPVPStateFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PvpFlags), (byte)flags); } public bool IsInSanctuary() { return HasPvpFlag(UnitPVPStateFlags.Sanctuary); } public bool IsPvP() { return HasPvpFlag(UnitPVPStateFlags.PvP); } public bool IsFFAPvP() { return HasPvpFlag(UnitPVPStateFlags.FFAPvp); } public UnitPetFlags GetPetFlags() { return (UnitPetFlags)(byte)m_unitData.PetFlags; } public bool HasPetFlag(UnitPetFlags flags) { return (m_unitData.PetFlags & (byte)flags) != 0; } public void AddPetFlag(UnitPetFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PetFlags), (byte)flags); } public void RemovePetFlag(UnitPetFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PetFlags), (byte)flags); } public void SetPetFlags(UnitPetFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PetFlags), (byte)flags); } public void SetPetNumberForClient(uint petNumber) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PetNumber), petNumber); } public void SetPetNameTimestamp(uint timestamp) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.PetNameTimestamp), timestamp); } public ShapeShiftForm GetShapeshiftForm() { return (ShapeShiftForm)(byte)m_unitData.ShapeshiftForm; } public CreatureType GetCreatureType() { if (IsTypeId(TypeId.Player)) { ShapeShiftForm form = GetShapeshiftForm(); var ssEntry = CliDB.SpellShapeshiftFormStorage.LookupByKey((uint)form); if (ssEntry != null && ssEntry.CreatureType > 0) return (CreatureType)ssEntry.CreatureType; else return CreatureType.Humanoid; } else return ToCreature().GetCreatureTemplate().CreatureType; } public Player GetAffectingPlayer() { if (GetCharmerOrOwnerGUID().IsEmpty()) return IsTypeId(TypeId.Player) ? ToPlayer() : null; Unit owner = GetCharmerOrOwner(); if (owner != null) return owner.GetCharmerOrOwnerPlayerOrPlayerItself(); return null; } public void DeMorph() { SetDisplayId(GetNativeDisplayId()); } public bool HasUnitTypeMask(UnitTypeMask mask) { return Convert.ToBoolean(mask & UnitTypeMask); } public void AddUnitTypeMask(UnitTypeMask mask) { UnitTypeMask |= mask; } public bool IsAlive() { return m_deathState == DeathState.Alive; } public bool IsDying() { return m_deathState == DeathState.JustDied; } public bool IsDead() { return (m_deathState == DeathState.Dead || m_deathState == DeathState.Corpse); } public bool IsSummon() { return UnitTypeMask.HasAnyFlag(UnitTypeMask.Summon); } public bool IsGuardian() { return UnitTypeMask.HasAnyFlag(UnitTypeMask.Guardian); } public bool IsPet() { return UnitTypeMask.HasAnyFlag(UnitTypeMask.Pet); } public bool IsHunterPet() { return UnitTypeMask.HasAnyFlag(UnitTypeMask.HunterPet); } public bool IsTotem() { return UnitTypeMask.HasAnyFlag(UnitTypeMask.Totem); } public bool IsVehicle() { return UnitTypeMask.HasAnyFlag(UnitTypeMask.Vehicle); } public void AddUnitState(UnitState f) { m_state |= f; } public bool HasUnitState(UnitState f) { return Convert.ToBoolean(m_state & f); } public void ClearUnitState(UnitState f) { m_state &= ~f; } public override bool IsAlwaysVisibleFor(WorldObject seer) { if (base.IsAlwaysVisibleFor(seer)) return true; // Always seen by owner ObjectGuid guid = GetCharmerOrOwnerGUID(); if (!guid.IsEmpty()) if (seer.GetGUID() == guid) return true; Player seerPlayer = seer.ToPlayer(); if (seerPlayer != null) { Unit owner = GetOwner(); if (owner != null) { Player ownerPlayer = owner.ToPlayer(); if (ownerPlayer) if (ownerPlayer.IsGroupVisibleFor(seerPlayer)) return true; } } return false; } //Faction public bool IsNeutralToAll() { var my_faction = GetFactionTemplateEntry(); if (my_faction == null || my_faction.Faction == 0) return true; var raw_faction = CliDB.FactionStorage.LookupByKey(my_faction.Faction); if (raw_faction != null && raw_faction.ReputationIndex >= 0) return false; return my_faction.IsNeutralToAll(); } public bool IsHostileTo(Unit unit) { return GetReactionTo(unit) <= ReputationRank.Hostile; } public bool IsFriendlyTo(Unit unit) { return GetReactionTo(unit) >= ReputationRank.Friendly; } public ReputationRank GetReactionTo(Unit target) { // always friendly to self if (this == target) return ReputationRank.Friendly; // always friendly to charmer or owner if (GetCharmerOrOwnerOrSelf() == target.GetCharmerOrOwnerOrSelf()) return ReputationRank.Friendly; if (HasUnitFlag(UnitFlags.PvpAttackable)) { if (target.HasUnitFlag(UnitFlags.PvpAttackable)) { Player selfPlayerOwner = GetAffectingPlayer(); Player targetPlayerOwner = target.GetAffectingPlayer(); if (selfPlayerOwner != null && targetPlayerOwner != null) { // always friendly to other unit controlled by player, or to the player himself if (selfPlayerOwner == targetPlayerOwner) return ReputationRank.Friendly; // duel - always hostile to opponent if (selfPlayerOwner.duel != null && selfPlayerOwner.duel.opponent == targetPlayerOwner && selfPlayerOwner.duel.startTime != 0) return ReputationRank.Hostile; // same group - checks dependant only on our faction - skip FFA_PVP for example if (selfPlayerOwner.IsInRaidWith(targetPlayerOwner)) return ReputationRank.Friendly; // return true to allow config option AllowTwoSide.Interaction.Group to work } // check FFA_PVP if (IsFFAPvP() && target.IsFFAPvP()) return ReputationRank.Hostile; if (selfPlayerOwner != null) { var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); if (targetFactionTemplateEntry != null) { if (!selfPlayerOwner.HasUnitFlag2(UnitFlags2.IgnoreReputation)) { var targetFactionEntry = CliDB.FactionStorage.LookupByKey(targetFactionTemplateEntry.Faction); if (targetFactionEntry != null) { if (targetFactionEntry.CanHaveReputation()) { // check contested flags if (Convert.ToBoolean(targetFactionTemplateEntry.Flags & (uint)FactionTemplateFlags.ContestedGuard) && selfPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP)) return ReputationRank.Hostile; // if faction has reputation, hostile state depends only from AtWar state if (selfPlayerOwner.GetReputationMgr().IsAtWar(targetFactionEntry)) return ReputationRank.Hostile; return ReputationRank.Friendly; } } } } } } } // do checks dependant only on our faction return GetFactionReactionTo(GetFactionTemplateEntry(), target); } ReputationRank GetFactionReactionTo(FactionTemplateRecord factionTemplateEntry, Unit target) { // always neutral when no template entry found if (factionTemplateEntry == null) return ReputationRank.Neutral; var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); if (targetFactionTemplateEntry == null) return ReputationRank.Neutral; Player targetPlayerOwner = target.GetAffectingPlayer(); if (targetPlayerOwner != null) { // check contested flags if (Convert.ToBoolean(factionTemplateEntry.Flags & (uint)FactionTemplateFlags.ContestedGuard) && targetPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP)) return ReputationRank.Hostile; ReputationRank repRank = targetPlayerOwner.GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry); if (repRank != ReputationRank.None) return repRank; if (!target.HasUnitFlag2(UnitFlags2.IgnoreReputation)) { var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplateEntry.Faction); if (factionEntry != null) { if (factionEntry.CanHaveReputation()) { // CvP case - check reputation, don't allow state higher than neutral when at war repRank = targetPlayerOwner.GetReputationMgr().GetRank(factionEntry); if (targetPlayerOwner.GetReputationMgr().IsAtWar(factionEntry)) repRank = (ReputationRank)Math.Min((int)ReputationRank.Neutral, (int)repRank); return repRank; } } } } // common faction based check if (factionTemplateEntry.IsHostileTo(targetFactionTemplateEntry)) return ReputationRank.Hostile; if (factionTemplateEntry.IsFriendlyTo(targetFactionTemplateEntry)) return ReputationRank.Friendly; if (targetFactionTemplateEntry.IsFriendlyTo(factionTemplateEntry)) return ReputationRank.Friendly; if (Convert.ToBoolean(factionTemplateEntry.Flags & (uint)FactionTemplateFlags.HostileByDefault)) return ReputationRank.Hostile; // neutral by default return ReputationRank.Neutral; } public uint GetFaction() { return m_unitData.FactionTemplate; } public void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), faction); } public void RestoreFaction() { if (IsTypeId(TypeId.Player)) ToPlayer().SetFactionForRace(GetRace()); else { if (HasUnitTypeMask(UnitTypeMask.Minion)) { Unit owner = GetOwner(); if (owner) { SetFaction(owner.GetFaction()); return; } } CreatureTemplate cinfo = ToCreature().GetCreatureTemplate(); if (cinfo != null) // normal creature SetFaction(cinfo.Faction); } } public bool IsInPartyWith(Unit unit) { if (this == unit) return true; Unit u1 = GetCharmerOrOwnerOrSelf(); Unit u2 = unit.GetCharmerOrOwnerOrSelf(); if (u1 == u2) return true; if (u1.IsTypeId(TypeId.Player) && u2.IsTypeId(TypeId.Player)) return u1.ToPlayer().IsInSameGroupWith(u2.ToPlayer()); else if ((u2.IsTypeId(TypeId.Player) && u1.IsTypeId(TypeId.Unit) && u1.ToCreature().GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.TreatAsRaidUnit)) || (u1.IsTypeId(TypeId.Player) && u2.IsTypeId(TypeId.Unit) && u2.ToCreature().GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.TreatAsRaidUnit))) return true; return u1.GetTypeId() == TypeId.Unit && u2.GetTypeId() == TypeId.Unit && u1.GetFaction() == u2.GetFaction(); } public bool IsInRaidWith(Unit unit) { if (this == unit) return true; Unit u1 = GetCharmerOrOwnerOrSelf(); Unit u2 = unit.GetCharmerOrOwnerOrSelf(); if (u1 == u2) return true; if (u1.IsTypeId(TypeId.Player) && u2.IsTypeId(TypeId.Player)) return u1.ToPlayer().IsInSameRaidWith(u2.ToPlayer()); else if ((u2.IsTypeId(TypeId.Player) && u1.IsTypeId(TypeId.Unit) && u1.ToCreature().GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.TreatAsRaidUnit)) || (u1.IsTypeId(TypeId.Player) && u2.IsTypeId(TypeId.Unit) && u2.ToCreature().GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.TreatAsRaidUnit))) return true; // else u1.GetTypeId() == u2.GetTypeId() == TYPEID_UNIT return u1.GetFaction() == u2.GetFaction(); } public UnitStandStateType GetStandState() { return (UnitStandStateType)(byte)m_unitData.StandState; } public void AddVisFlags(UnitVisFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.VisFlags), (byte)flags); } public void RemoveVisFlags(UnitVisFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.VisFlags), (byte)flags); } public void SetVisFlags(UnitVisFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.VisFlags), (byte)flags); } public bool IsSitState() { UnitStandStateType s = GetStandState(); return s == UnitStandStateType.SitChair || s == UnitStandStateType.SitLowChair || s == UnitStandStateType.SitMediumChair || s == UnitStandStateType.SitHighChair || s == UnitStandStateType.Sit; } public bool IsStandState() { UnitStandStateType s = GetStandState(); return !IsSitState() && s != UnitStandStateType.Sleep && s != UnitStandStateType.Kneel; } public void SetStandState(UnitStandStateType state, uint animKitId = 0) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StandState), (byte)state); if (IsStandState()) RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.NotSeated); if (IsTypeId(TypeId.Player)) { StandStateUpdate packet = new StandStateUpdate(state, animKitId); ToPlayer().SendPacket(packet); } } public void SetAnimTier(UnitBytes1Flags animTier, bool notifyClient) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.AnimTier), (byte)animTier); if (notifyClient) { SetAnimTier setAnimTier = new SetAnimTier(); setAnimTier.Unit = GetGUID(); setAnimTier.Tier = (int)animTier; SendMessageToSet(setAnimTier, true); } } public uint GetChannelSpellId() { return ((UnitChannel)m_unitData.ChannelData).SpellID; } public void SetChannelSpellId(uint channelSpellId) { SetUpdateFieldValue(ref m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelData)._value.SpellID, channelSpellId); } public uint GetChannelSpellXSpellVisualId() { return m_unitData.ChannelData.GetValue().SpellVisual.SpellXSpellVisualID; } public uint GetChannelScriptVisualId() { return m_unitData.ChannelData.GetValue().SpellVisual.ScriptVisualID; } public void SetChannelVisual(SpellCastVisualField channelVisual) { UnitChannel unitChannel = m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelData); SetUpdateFieldValue(ref unitChannel.SpellVisual, channelVisual); } public void AddChannelObject(ObjectGuid guid) { AddDynamicUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects), guid); } public void SetChannelObject(int slot, ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects, slot), guid); } public void ClearChannelObjects() { ClearDynamicUpdateFieldValues(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects)); } public bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, sbyte effIndex = -1) { // only physical spells damage gets reduced by armor if ((schoolMask & SpellSchoolMask.Normal) == 0) return false; if (spellInfo != null) { // there are spells with no specific attribute but they have "ignores armor" in tooltip if (spellInfo.HasAttribute(SpellCustomAttributes.IgnoreArmor)) return false; if (effIndex != -1) { // bleeding effects are not reduced by armor SpellEffectInfo effect = spellInfo.GetEffect((uint)effIndex); if (effect != null) { if (effect.ApplyAuraName == AuraType.PeriodicDamage || effect.Effect == SpellEffectName.SchoolDamage) if (spellInfo.GetEffectMechanicMask((byte)effIndex).HasAnyFlag((1u << (int)Mechanics.Bleed))) return false; } } } return true; } public override UpdateFieldFlag GetUpdateFieldFlagsFor(Player target) { UpdateFieldFlag flags = UpdateFieldFlag.None; if (target == this || GetOwnerGUID() == target.GetGUID()) flags |= UpdateFieldFlag.Owner; if (HasDynamicFlag(UnitDynFlags.SpecialInfo)) if (HasAuraTypeWithCaster(AuraType.Empathy, target.GetGUID())) flags |= UpdateFieldFlag.Empath; return flags; } public override void BuildValuesCreate(WorldPacket data, Player target) { UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); WorldPacket buffer = new WorldPacket(); buffer.WriteUInt8((byte)flags); m_objectData.WriteCreate(buffer, flags, this, target); m_unitData.WriteCreate(buffer, flags, this, target); data.WriteUInt32(buffer.GetSize()); data.WriteBytes(buffer); } public override void BuildValuesUpdate(WorldPacket data, Player target) { UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); WorldPacket buffer = new WorldPacket(); buffer.WriteUInt32(m_values.GetChangedObjectTypeMask()); if (m_values.HasChanged(TypeId.Object)) m_objectData.WriteUpdate(buffer, flags, this, target); if (m_values.HasChanged(TypeId.Unit)) m_unitData.WriteUpdate(buffer, flags, this, target); data.WriteUInt32(buffer.GetSize()); data.WriteBytes(buffer); } public override void BuildValuesUpdateWithFlag(WorldPacket data, UpdateFieldFlag flags, Player target) { UpdateMask valuesMask = new UpdateMask(14); valuesMask.Set((int)TypeId.Unit); WorldPacket buffer = new WorldPacket(); UpdateMask mask = new UpdateMask(191); m_unitData.AppendAllowedFieldsMaskForFlag(mask, flags); m_unitData.WriteUpdate(buffer, mask, true, this, target); data.WriteUInt32(buffer.GetSize()); data.WriteUInt32(valuesMask.GetBlock(0)); data.WriteBytes(buffer); } public void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedUnitMask, Player target) { UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new UpdateMask((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); m_unitData.FilterDisallowedFieldsMaskForFlag(requestedUnitMask, flags); if (requestedUnitMask.IsAnySet()) valuesMask.Set((int)TypeId.Unit); WorldPacket buffer = new WorldPacket(); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) m_objectData.WriteUpdate(buffer, requestedObjectMask, true, this, target); if (valuesMask[(int)TypeId.Unit]) m_unitData.WriteUpdate(buffer, requestedUnitMask, true, this, target); WorldPacket buffer1 = new WorldPacket(); buffer1.WriteUInt8((byte)UpdateType.Values); buffer1.WritePackedGuid(GetGUID()); buffer1.WriteUInt32(buffer.GetSize()); buffer1.WriteBytes(buffer.GetData()); data.AddUpdateBlock(buffer1); } public override void ClearUpdateMask(bool remove) { m_values.ClearChangesMask(m_unitData); base.ClearUpdateMask(remove); } public override void DestroyForPlayer(Player target) { Battleground bg = target.GetBattleground(); if (bg != null) { if (bg.IsArena()) { DestroyArenaUnit destroyArenaUnit = new DestroyArenaUnit(); destroyArenaUnit.Guid = GetGUID(); target.SendPacket(destroyArenaUnit); } } base.DestroyForPlayer(target); } } }