/* * Copyright (C) 2012-2017 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 Framework.IO; using Game.AI; using Game.BattleGrounds; using Game.Chat; using Game.Combat; using Game.DataStorage; using Game.Maps; using Game.Movement; using Game.Network.Packets; using Game.Spells; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; 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); m_unitTypeMask = UnitTypeMask.None; m_HostileRefManager = new HostileRefManager(this); _spellHistory = new SpellHistory(this); m_FollowingRefManager = new RefManager(); objectTypeId = TypeId.Unit; objectTypeMask |= TypeMask.Unit; m_updateFlag = UpdateFlag.Living; m_modAttackSpeedPct = new float[] { 1.0f, 1.0f, 1.0f }; m_deathState = DeathState.Alive; for (byte i = 0; i < (int)UnitMods.End; ++i) m_auraModifiersGroup[i] = new float[] { 0.0f, 100.0f, 1.0f, 0.0f, 1.0f }; m_auraModifiersGroup[(int)UnitMods.DamageOffHand][(int)UnitModifierType.TotalPCT] = 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)) { m_modMeleeHitChance = 7.5f; m_modRangedHitChance = 7.5f; m_modSpellHitChance = 15.0f; } m_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(); } 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 Contract.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 (m_HostileRefManager.isEmpty()) { // m_CombatTimer set at aura start and it will be freeze until aura removing if (m_CombatTimer <= diff) ClearInCombat(); else m_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 aura in GetOwnedAuras()) { if (aura.Value != null && aura.Value.IsExpired()) RemoveOwnedAura(aura, AuraRemoveMode.Expire); } foreach (var aura in m_visibleAurasToUpdate) aura.ClientUpdate(); m_visibleAurasToUpdate.Clear(); _DeleteRemovedAuras(); if (!m_gameObj.Empty()) { foreach (var go in m_gameObj.ToList()) { 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() { SpellInfo transformSpellInfo = Global.SpellMgr.GetSpellInfo(getTransForm()); if (transformSpellInfo != null) if (transformSpellInfo.HasAttribute(SpellAttr0.CastableWhileMounted)) return false; ShapeShiftForm form = GetShapeshiftForm(); if (form != 0) { SpellShapeshiftFormRecord shapeshift = CliDB.SpellShapeshiftFormStorage.LookupByKey(form); if (shapeshift == null) return true; if (!shapeshift.Flags.HasAnyFlag(SpellShapeshiftFormFlags.IsNotAShapeshift)) return true; } if (GetDisplayId() == GetNativeDisplayId()) return false; CreatureDisplayInfoRecord display = CliDB.CreatureDisplayInfoStorage.LookupByKey(GetDisplayId()); 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; LocaleConstant 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` was not {0} found", textId); return; } var builder = new BroadcastTextBuilder(this, msgType, textId, 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; } LocaleConstant 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 AddToWorld() { if (!IsInWorld) { base.AddToWorld(); } RebuildTerrainSwaps(); } 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(); DeleteThreatList(); getHostileRefManager().setOnlineOfflineState(false); GetMotionMaster().Clear(false); // remove different non-standard movement generators. } 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 m_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) { foreach (var dynObj in m_dynObj.ToList()) { 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()); // 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); // 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; foreach (var areaTrigger in m_areaTrigger.ToList()) { 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 IsVendor() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Vendor); } public bool IsTrainer() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Trainer); } public bool IsQuestGiver() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.QuestGiver); } public bool IsGossip() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Gossip); } public bool IsTaxi() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.FlightMaster); } public bool IsGuildMaster() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Petitioner); } public bool IsBattleMaster() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.BattleMaster); } public bool IsBanker() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Banker); } public bool IsInnkeeper() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Innkeeper); } public bool IsSpiritHealer() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.SpiritHealer); } public bool IsSpiritGuide() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.SpiritGuide); } public bool IsTabardDesigner() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.TabardDesigner); } public bool IsAuctioner() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Auctioneer); } public bool IsArmorer() { return HasFlag64(UnitFields.NpcFlags, NPCFlags.Repair); } public bool IsServiceProvider() { return HasFlag64(UnitFields.NpcFlags, 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 HasFlag64(UnitFields.NpcFlags, NPCFlags.SpiritHealer | NPCFlags.SpiritGuide); } public bool IsCritter() { return GetCreatureType() == CreatureType.Critter; } public bool IsInFlight() { return HasUnitState(UnitState.InFlight); } 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 Contract.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; } } Contract.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 Contract.Assert(rideVehicleEffect == null); rideVehicleEffect = eff; } // Unit riding a vehicle must always have control vehicle aura on target Contract.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(GetPhases(), 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(); 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.UnkZero = 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.UnkZero = 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 o, 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.Orientation = o; 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); } 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 UnitAI GetAI() { return i_AI; } 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); SetGuidValue(UnitFields.Charm, ObjectGuid.Empty); } return null; } public bool IsCharmed() { return !GetCharmerGUID().IsEmpty(); } public bool isPossessed() { return HasUnitState(UnitState.Possessed); } public HostileRefManager getHostileRefManager() { return m_HostileRefManager; } public override bool SetInPhase(uint id, bool update, bool apply) { bool res = base.SetInPhase(id, update, apply); if (!IsInWorld) return res; RemoveNotOwnSingleTargetAuras(0, true); if (IsTypeId(TypeId.Unit) || (!ToPlayer().IsGameMaster() && !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)); } } } foreach (var unit in m_Controlled) if (unit.IsTypeId(TypeId.Unit)) unit.SetInPhase(id, true, apply); for (byte i = 0; i < SharedConst.MaxSummonSlot; ++i) { if (!m_SummonSlot[i].IsEmpty()) { Creature summon = GetMap().GetCreature(m_SummonSlot[i]); if (summon != null) summon.SetInPhase(id, true, apply); } } RemoveNotOwnSingleTargetAuras(0, true); return res; } public uint GetModelForForm(ShapeShiftForm form) { if (IsTypeId(TypeId.Player)) { Aura artifactAura = GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive); if (artifactAura != null) { Item artifact = ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID()); if (artifact) { ArtifactAppearanceRecord artifactAppearance = CliDB.ArtifactAppearanceStorage.LookupByKey(artifact.GetModifier(ItemModifier.ArtifactAppearanceId)); if (artifactAppearance != null) if ((ShapeShiftForm)artifactAppearance.ModifiesShapeshiftFormDisplay == form) return artifactAppearance.ShapeshiftDisplayID; } } byte hairColor = GetByteValue(PlayerFields.Bytes, PlayerFieldOffsets.BytesOffsetHairColorId); byte skinColor = GetByteValue(PlayerFields.Bytes, PlayerFieldOffsets.BytesOffsetSkinId); switch (form) { case ShapeShiftForm.CatForm: { if (GetRace() == Race.NightElf) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 10); switch (hairColor) { case 7: // Violet case 8: return 29405; case 3: // Light Blue return 29406; case 0: // Green case 1: // Light Green case 2: // Dark Green return 29407; case 4: // White return 29408; default: // original - Dark Blue return 892; } } else if (GetRace() == Race.Troll) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 12); switch (hairColor) { case 0: // Red case 1: return 33668; case 2: // Yellow case 3: return 33667; case 4: // Blue case 5: case 6: return 33666; case 7: // Purple case 10: return 33665; default: // original - white return 33669; } } else if (GetRace() == Race.Worgen) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 9); // Male if (GetGender() == Gender.Male) { switch (skinColor) { case 1: // Brown return 33662; case 2: // Black case 7: return 33661; case 4: // yellow return 33664; case 3: // White case 5: return 33663; default: // original - Gray return 33660; } } // Female else { switch (skinColor) { case 5: // Brown case 6: return 33662; case 7: // Black case 8: return 33661; case 3: // yellow case 4: return 33664; case 2: // White return 33663; default: // original - Gray return 33660; } } } else if (GetRace() == Race.Tauren) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 20); if (GetGender() == Gender.Male) { switch (skinColor) { case 12: // White case 13: case 14: case 18: // Completly White return 29409; case 9: // Light Brown case 10: case 11: return 29410; case 6: // Brown case 7: case 8: return 29411; case 0: // Dark case 1: case 2: case 3: // Dark Grey case 4: case 5: return 29412; default: // original - Grey return 8571; } } // Female else { switch (skinColor) { case 10: // White return 29409; case 6: // Light Brown case 7: return 29410; case 4: // Brown case 5: return 29411; case 0: // Dark case 1: case 2: case 3: return 29412; default: // original - Grey return 8571; } } } else if (Player.TeamForRace(GetRace()) == Team.Alliance) return 892; else return 8571; } case ShapeShiftForm.BearForm: { if (GetRace() == Race.NightElf) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 8); switch (hairColor) { case 0: // Green case 1: // Light Green case 2: // Dark Green return 29413; // 29415? case 6: // Dark Blue return 29414; case 4: // White return 29416; case 3: // Light Blue return 29417; default: // original - Violet return 29415; } } else if (GetRace() == Race.Troll) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 14); switch (hairColor) { case 0: // Red case 1: return 33657; case 2: // Yellow case 3: return 33659; case 7: // Purple case 10: return 33656; case 8: // White case 9: case 11: case 12: return 33658; default: // original - Blue return 33655; } } else if (GetRace() == Race.Worgen) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 8); // Male if (GetGender() == Gender.Male) { switch (skinColor) { case 1: // Brown return 33652; case 2: // Black case 7: return 33651; case 4: // Yellow return 33653; case 3: // White case 5: return 33654; default: // original - Gray return 33650; } } // Female else { switch (skinColor) { case 5: // Brown case 6: return 33652; case 7: // Black case 8: return 33651; case 3: // yellow case 4: return 33654; case 2: // White return 33653; default: // original - Gray return 33650; } } } else if (GetRace() == Race.Tauren) { if (HasAura(210333)) // Glyph of the Feral Chameleon hairColor = (byte)RandomHelper.URand(0, 20); if (GetGender() == Gender.Male) { switch (skinColor) { case 0: // Dark (Black) case 1: case 2: return 29418; case 3: // White case 4: case 5: case 12: case 13: case 14: return 29419; case 9: // Light Brown/Grey case 10: case 11: case 15: case 16: case 17: return 29420; case 18: // Completly White return 29421; default: // original - Brown return 2289; } } // Female else { switch (skinColor) { case 0: // Dark (Black) case 1: return 29418; case 2: // White case 3: return 29419; case 6: // Light Brown/Grey case 7: case 8: case 9: return 29420; case 10: // Completly White return 29421; default: // original - Brown return 2289; } } } else if (Player.TeamForRace(GetRace()) == Team.Alliance) return 29415; else return 2289; } case ShapeShiftForm.FlightForm: if (Player.TeamForRace(GetRace()) == Team.Alliance) return 20857; return 20872; case ShapeShiftForm.FlightFormEpic: if (HasAura(219062)) // Glyph of the Sentinel { switch (GetRace()) { case Race.NightElf: // Blue return 64328; case Race.Tauren: // Brown return 64329; case Race.Worgen: // Purple return 64330; case Race.Troll: // White return 64331; default: break; } } if (Player.TeamForRace(GetRace()) == Team.Alliance) return (GetRace() == Race.Worgen ? 37729 : 21243u); if (GetRace() == Race.Troll) return 37730; return 21244; case ShapeShiftForm.MoonkinForm: switch (GetRace()) { case Race.NightElf: return 15374; case Race.Tauren: return 15375; case Race.Worgen: return 37173; case Race.Troll: return 37174; default: break; } break; case ShapeShiftForm.AquaticForm: if (HasAura(114333)) // Glyph of the Orca return 4591; return 2428; case ShapeShiftForm.TravelForm: { if (HasAura(131113)) // Glyph of the Cheetah return 1043; if (HasAura(224122)) // Glyph of the Doe return 70450; switch (GetRace()) { case Race.NightElf: case Race.Worgen: return 40816; case Race.Troll: case Race.Tauren: return 45339; default: break; } break; } case ShapeShiftForm.GhostWolf: if (HasAura(58135)) // Glyph of Spectral Wolf return 60247; break; } } uint modelid = 0; SpellShapeshiftFormRecord formEntry = CliDB.SpellShapeshiftFormStorage.LookupByKey((uint)form); if (formEntry != null && formEntry.CreatureDisplayID[0] != 0) { // Take the alliance modelid as default if (!IsTypeId(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 virtual bool CanUseAttackType(WeaponAttackType attacktype) { switch (attacktype) { case WeaponAttackType.BaseAttack: return !HasFlag(UnitFields.Flags, UnitFlags.Disarmed); case WeaponAttackType.OffAttack: return !HasFlag(UnitFields.Flags2, UnitFlags2.DisarmOffhand); case WeaponAttackType.RangedAttack: return !HasFlag(UnitFields.Flags2, UnitFlags2.DisarmRanged); default: return true; } } 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(); DeleteThreatList(); 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) { ModifyAuraState(AuraStateType.HealthLess20Percent, false); ModifyAuraState(AuraStateType.HealthLess35Percent, false); // 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); SetUInt32Value(UnitFields.NpcEmotestate, 0); // 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) RemoveFlag(UnitFields.Flags, 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 IsMounted() { return HasFlag(UnitFields.Flags, UnitFlags.Mount); } public uint GetMountID() { return GetUInt32Value(UnitFields.MountDisplayId); } public void SetShapeshiftForm(ShapeShiftForm form) { SetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.ShapeshiftForm, (byte)form); } public void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[(int)unitMod][(int)modifierType] = value; } public float GetTotalAuraMultiplierByMiscValue(AuraType auratype, int miscValue) { Dictionary SameEffectSpellGroup = new Dictionary(); float multiplier = 1.0f; var mTotalAuraList = GetAuraEffectsByType(auratype); foreach (var i in mTotalAuraList) { if (i.GetMiscValue() == miscValue) if (!Global.SpellMgr.AddSameEffectStackRuleSpellGroups(i.GetSpellInfo(), i.GetAmount(), out SameEffectSpellGroup)) MathFunctions.AddPct(ref multiplier, i.GetAmount()); } foreach (var pair in SameEffectSpellGroup) MathFunctions.AddPct(ref multiplier, pair.Value); return multiplier; } public int GetTotalAuraModifierByMiscValue(AuraType auratype, int miscValue) { Dictionary SameEffectSpellGroup = new Dictionary(); int modifier = 0; var mTotalAuraList = GetAuraEffectsByType(auratype); foreach (var i in mTotalAuraList) { if (i.GetMiscValue() == miscValue) if (!Global.SpellMgr.AddSameEffectStackRuleSpellGroups(i.GetSpellInfo(), i.GetAmount(), out SameEffectSpellGroup)) modifier += i.GetAmount(); } foreach (var pair in SameEffectSpellGroup) modifier += pair.Value; return modifier; } public int GetTotalAuraModifierByMiscMask(AuraType auratype, int miscMask) { Dictionary SameEffectSpellGroup = new Dictionary(); int modifier = 0; var mTotalAuraList = GetAuraEffectsByType(auratype); foreach (var i in mTotalAuraList) if (Convert.ToBoolean(i.GetMiscValue() & miscMask)) if (!Global.SpellMgr.AddSameEffectStackRuleSpellGroups(i.GetSpellInfo(), i.GetAmount(), out SameEffectSpellGroup)) modifier += i.GetAmount(); foreach (var pair in SameEffectSpellGroup) modifier += pair.Value; return modifier; } 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(Difficulty.None, 0); if (target.HasAura(53042) && effect != null && target.HasSpell(effect.TriggerSpell)) duration *= 2; } } } // Glyphs which increase duration of selfcasted buffs if (target == this) { switch (spellProto.SpellFamilyName) { case SpellFamilyNames.Druid: if (spellProto.SpellFamilyFlags[0].HasAnyFlag(0x100u)) { // Glyph of Thorns AuraEffect aurEff = GetAuraEffect(57862, 0); if (aurEff != null) duration += aurEff.GetAmount() * Time.Minute * Time.InMilliseconds; } break; } } 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 Contract.Assert(!m_cleanupDone); // aura musn't be removed Contract.Assert(!aura.IsRemoved()); // aura mustn't be already applied on target Contract.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.AuraInterruptFlags != 0) { m_interruptableAuras.Add(aurApp); AddInterruptMask((uint)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) { m_interruptMask |= mask; } void _UpdateAutoRepeatSpell() { // 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, GetCurrentSpell(CurrentSpellTypes.AutoRepeat).m_spellInfo.Id == 75)) && !HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_currentSpells[CurrentSpellTypes.AutoRepeat].m_spellInfo)) { // cancel wand shoot if (m_currentSpells[CurrentSpellTypes.AutoRepeat].m_spellInfo.Id != 75) InterruptSpell(CurrentSpellTypes.AutoRepeat); m_AutoRepeatFirstCast = true; return; } // apply delay (Auto Shot (spellID 75) not affected) if (m_AutoRepeatFirstCast && getAttackTimer(WeaponAttackType.RangedAttack) < 500 && m_currentSpells[CurrentSpellTypes.AutoRepeat].m_spellInfo.Id != 75) setAttackTimer(WeaponAttackType.RangedAttack, 500); m_AutoRepeatFirstCast = false; // castroutine if (isAttackReady(WeaponAttackType.RangedAttack)) { // Check if able to cast if (m_currentSpells[CurrentSpellTypes.AutoRepeat].CheckCast(true) != SpellCastResult.SpellCastOk) { InterruptSpell(CurrentSpellTypes.AutoRepeat); return; } // we want to shoot Spell spell = new Spell(this, m_currentSpells[CurrentSpellTypes.AutoRepeat].m_spellInfo, 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 { ChrClassesRecord cEntry = CliDB.ChrClassesStorage.LookupByKey(GetClass()); if (cEntry != null && cEntry.PowerType < PowerType.Max) displayPower = cEntry.PowerType; } break; } } setPowerType(displayPower); } public FactionTemplateRecord GetFactionTemplateEntry() { FactionTemplateRecord entry = CliDB.FactionTemplateStorage.LookupByKey(getFaction()); if (entry == null) { ObjectGuid guid = ObjectGuid.Empty; // prevent repeating spam same faction problem if (GetGUID() != guid) { Player player = ToPlayer(); Creature creature = ToCreature(); if (player != null) Log.outError(LogFilter.Unit, "Player {0} has invalid faction (faction template id) #{1}", player.GetName(), getFaction()); else 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()); guid = GetGUID(); } } 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 GetUInt32Value(UnitFields.VirtualItemSlotId + slot * 2); } public ushort GetVirtualItemAppearanceMod(uint slot) { if (slot >= SharedConst.MaxEquipmentItems) return 0; return GetUInt16Value(UnitFields.VirtualItemSlotId + (int)slot * 2 + 1, 0); } public void SetVirtualItem(int slot, uint itemId, ushort appearanceModId = 0, ushort itemVisual = 0) { if (slot >= SharedConst.MaxEquipmentItems) return; SetUInt32Value(UnitFields.VirtualItemSlotId + slot * 2, itemId); SetUInt16Value(UnitFields.VirtualItemSlotId + slot * 2 + 1, 0, appearanceModId); SetUInt16Value(UnitFields.VirtualItemSlotId + slot * 2 + 1, 1, itemVisual); } //Unit public void SetLevel(uint lvl) { SetUInt32Value(UnitFields.Level, lvl); if (IsTypeId(TypeId.Player)) { Player player = ToPlayer(); if (player.GetGroup()) player.SetGroupUpdateFlag(GroupUpdateFlags.Level); Global.WorldMgr.UpdateCharacterInfoLevel(ToPlayer().GetGUID(), (byte)lvl); } } public uint getLevel() { return GetUInt32Value(UnitFields.Level); } public override uint GetLevelForTarget(WorldObject target) { return getLevel(); } public Race GetRace() { return (Race)GetByteValue(UnitFields.Bytes0, 0); } public uint getRaceMask() { return (uint)(1 << ((int)GetRace() - 1)); } public Class GetClass() { return (Class)GetByteValue(UnitFields.Bytes0, 1); } public uint getClassMask() { return (uint)(1 << ((int)GetClass() - 1)); } public Gender GetGender() { return (Gender)GetByteValue(UnitFields.Bytes0, 3); } public void SetNativeDisplayId(uint modelId) { SetUInt32Value(UnitFields.NativeDisplayId, modelId); } public virtual void SetDisplayId(uint modelId) { SetUInt32Value(UnitFields.DisplayId, modelId); // Set Gender by modelId CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelInfo(modelId); if (minfo != null) SetByteValue(UnitFields.Bytes0, 3, (byte)minfo.gender); } public uint GetNativeDisplayId() { return GetUInt32Value(UnitFields.NativeDisplayId); } 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 GetGuidValue(UnitFields.SummonedBy); } public ObjectGuid GetCreatorGUID() { return GetGuidValue(UnitFields.CreatedBy); } public ObjectGuid GetMinionGUID() { return GetGuidValue(UnitFields.Summon); } public ObjectGuid GetCharmerGUID() { return GetGuidValue(UnitFields.CharmedBy); } public ObjectGuid GetCharmGUID() { return GetGuidValue(UnitFields.Charm); } public ObjectGuid GetPetGUID() { return m_SummonSlot[0]; } public ObjectGuid GetCritterGUID() { return GetGuidValue(UnitFields.Critter); } public ObjectGuid GetCharmerOrOwnerGUID() { return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID(); } 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 List GetChannelObjects() { return GetDynamicStructuredValues(UnitDynamicFields.ChannelObjects); } public void AddChannelObject(ObjectGuid guid) { AddDynamicStructuredValue(UnitDynamicFields.ChannelObjects, guid); } public void SetOwnerGUID(ObjectGuid owner) { if (GetOwnerGUID() == owner) return; SetGuidValue(UnitFields.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; SetFieldNotifyFlag(UpdateFieldFlags.Owner); UpdateData udata = new UpdateData(GetMapId()); UpdateObject packet; BuildValuesUpdateBlockForPlayer(udata, player); udata.BuildPacket(out packet); player.SendPacket(packet); RemoveFieldNotifyFlag(UpdateFieldFlags.Owner); } public void SetCreatorGUID(ObjectGuid creator) { SetGuidValue(UnitFields.CreatedBy, creator); } void SetMinionGUID(ObjectGuid guid) { SetGuidValue(UnitFields.Summon, guid); } void SetCharmerGUID(ObjectGuid owner) { SetGuidValue(UnitFields.CharmedBy, owner); } public void SetPetGUID(ObjectGuid guid) { m_SummonSlot[0] = guid; } void SetCritterGUID(ObjectGuid guid) { SetGuidValue(UnitFields.Critter, guid); } public uint GetCombatTimer() { return m_CombatTimer; } public bool IsPvP() { return HasByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.PvP); } public bool IsFFAPvP() { return HasByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag, UnitBytes2Flags.FFAPvp); } public ShapeShiftForm GetShapeshiftForm() { return (ShapeShiftForm)GetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.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; } 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 uint GetModelForTotem(PlayerTotemType totemType) { switch (GetRace()) { case Race.Orc: { switch (totemType) { case PlayerTotemType.Fire: // fire return 30758; case PlayerTotemType.Earth: // earth return 30757; case PlayerTotemType.Water: // water return 30759; case PlayerTotemType.Air: // air return 30756; } break; } case Race.Dwarf: { switch (totemType) { case PlayerTotemType.Fire: // fire return 30754; case PlayerTotemType.Earth: // earth return 30753; case PlayerTotemType.Water: // water return 30755; case PlayerTotemType.Air: // air return 30736; } break; } case Race.Troll: { switch (totemType) { case PlayerTotemType.Fire: // fire return 30762; case PlayerTotemType.Earth: // earth return 30761; case PlayerTotemType.Water: // water return 30763; case PlayerTotemType.Air: // air return 30760; } break; } case Race.Tauren: { switch (totemType) { case PlayerTotemType.Fire: // fire return 4589; case PlayerTotemType.Earth: // earth return 4588; case PlayerTotemType.Water: // water return 4587; case PlayerTotemType.Air: // air return 4590; } break; } case Race.Draenei: { switch (totemType) { case PlayerTotemType.Fire: // fire return 19074; case PlayerTotemType.Earth: // earth return 19073; case PlayerTotemType.Water: // water return 19075; case PlayerTotemType.Air: // air return 19071; } break; } case Race.Goblin: { switch (totemType) { case PlayerTotemType.Fire: // fire return 30783; case PlayerTotemType.Earth: // earth return 30782; case PlayerTotemType.Water: // water return 30784; case PlayerTotemType.Air: // air return 30781; } break; } } return 0; } public bool IsStopped() { return !(HasUnitState(UnitState.Moving)); } public bool HasUnitTypeMask(UnitTypeMask mask) { return Convert.ToBoolean(mask & m_unitTypeMask); } public void AddUnitTypeMask(UnitTypeMask mask) { m_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 m_unitTypeMask.HasAnyFlag(UnitTypeMask.Summon); } public bool IsGuardian() { return m_unitTypeMask.HasAnyFlag(UnitTypeMask.Guardian); } public bool IsPet() { return m_unitTypeMask.HasAnyFlag(UnitTypeMask.Pet); } public bool IsHunterPet() { return m_unitTypeMask.HasAnyFlag(UnitTypeMask.HunterPet); } public bool IsTotem() { return m_unitTypeMask.HasAnyFlag(UnitTypeMask.Totem); } public bool IsVehicle() { return m_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 void SetStandFlags(object flags) { SetByteFlag(UnitFields.Bytes1, UnitBytes1Offsets.VisFlag, flags); } public void RemoveStandFlags(object flags) { RemoveByteFlag(UnitFields.Bytes1, UnitBytes1Offsets.VisFlag, flags); } 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 (HasFlag(UnitFields.Flags, UnitFlags.PvpAttackable)) { if (target.HasFlag(UnitFields.Flags, 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 (Convert.ToBoolean(GetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag) & (byte)UnitBytes2Flags.FFAPvp) && Convert.ToBoolean(target.GetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.PvpFlag) & (byte)UnitBytes2Flags.FFAPvp)) return ReputationRank.Hostile; if (selfPlayerOwner != null) { var targetFactionTemplateEntry = target.GetFactionTemplateEntry(); if (targetFactionTemplateEntry != null) { if (!selfPlayerOwner.HasFlag(UnitFields.Flags2, 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.HasFlag(PlayerFields.Flags, 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.HasFlag(PlayerFields.Flags, PlayerFlags.ContestedPVP)) return ReputationRank.Hostile; ReputationRank repRank = targetPlayerOwner.GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry); if (repRank != ReputationRank.None) return repRank; if (!target.HasFlag(UnitFields.Flags2, 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 GetUInt32Value(UnitFields.FactionTemplate); } public void SetFaction(uint faction) { SetUInt32Value(UnitFields.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; else return false; } 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 return false; } public SheathState GetSheath() { return (SheathState)GetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.SheathState); } public void SetSheath(SheathState sheathed) { SetByteValue(UnitFields.Bytes2, UnitBytes2Offsets.SheathState, (byte)sheathed); } public UnitStandStateType GetStandState() { return (UnitStandStateType)GetByteValue(UnitFields.Bytes1, UnitBytes1Offsets.StandState); } 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) { SetByteValue(UnitFields.Bytes1, UnitBytes1Offsets.StandState, (byte)state); if (IsStandState()) RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.NotSeated); if (IsTypeId(TypeId.Player)) { StandStateUpdate packet = new StandStateUpdate(state, animKitId); ToPlayer().SendPacket(packet); } } public uint GetDisplayId() { return GetUInt32Value(UnitFields.DisplayId); } public void RestoreDisplayId() { 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) handledAura = eff; // prefer negative auras if (!aurApp.IsPositive()) { handledAura = eff; break; } } } } uint modelId; // transform aura was found if (handledAura != null) handledAura.HandleEffect(this, AuraEffectHandleModes.SendForClient, true); // we've found shapeshift else if ((modelId = GetModelForForm(GetShapeshiftForm())) != 0) SetDisplayId(modelId); // no auras found - set modelid to default else SetDisplayId(GetNativeDisplayId()); } 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(GetMap().GetDifficultyID(), (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 void BuildValuesUpdate(UpdateType updatetype, ByteBuffer data, Player target) { if (target == null) return; ByteBuffer fieldBuffer = new ByteBuffer(); uint valCount = valuesCount; uint[] flags = UpdateFieldFlags.UnitUpdateFieldFlags; uint visibleFlag = UpdateFieldFlags.Public; if (target == this) visibleFlag |= UpdateFieldFlags.Private; else if (IsTypeId(TypeId.Player)) valCount = (int)PlayerFields.EndNotSelf; UpdateMask updateMask = new UpdateMask(valCount); Player plr = GetCharmerOrOwnerPlayerOrPlayerItself(); if (GetOwnerGUID() == target.GetGUID()) visibleFlag |= UpdateFieldFlags.Owner; if (HasFlag(ObjectFields.DynamicFlags, UnitDynFlags.SpecialInfo)) if (HasAuraTypeWithCaster(AuraType.Empathy, target.GetGUID())) visibleFlag |= UpdateFieldFlags.SpecialInfo; if (plr != null && plr.IsInSameRaidWith(target)) visibleFlag |= UpdateFieldFlags.PartyMember; Creature creature = ToCreature(); for (var index = 0; index < valCount; ++index) { if (Convert.ToBoolean(_fieldNotifyFlags & flags[index]) || Convert.ToBoolean((flags[index] & visibleFlag) & UpdateFieldFlags.SpecialInfo) || ((updatetype == UpdateType.Values ? _changesMask.Get(index) : HasUpdateValue(index)) && flags[index].HasAnyFlag(visibleFlag)) || (index == (int)UnitFields.AuraState && HasFlag(UnitFields.AuraState, AuraStateType.PerCasterAuraStateMask))) { updateMask.SetBit(index); if (index == (int)UnitFields.NpcFlags) { uint appendValue = GetUInt32Value(UnitFields.NpcFlags); if (creature != null) if (!target.CanSeeSpellClickOn(creature)) appendValue &= ~(uint)NPCFlags.SpellClick; fieldBuffer.WriteUInt32(appendValue); } else if (index == (int)UnitFields.AuraState) { // Check per caster aura states to not enable using a spell in client if specified aura is not by target fieldBuffer.WriteUInt32(BuildAuraStateUpdateForTarget(target)); } // FIXME: Some values at server stored in float format but must be sent to client in public uint format else if ((index >= (int)UnitFields.NegStat && index < (int)UnitFields.NegStat + (int)Stats.Max) || (index >= (int)UnitFields.ResistanceBuffModsPositive && index < ((int)UnitFields.ResistanceBuffModsPositive + (int)SpellSchools.Max)) || (index >= (int)UnitFields.ResistanceBuffModsNegative && index < ((int)UnitFields.ResistanceBuffModsNegative + (int)SpellSchools.Max)) || (index >= (int)UnitFields.PosStat && index < (int)UnitFields.PosStat + (int)Stats.Max)) { fieldBuffer.WriteUInt32((uint)GetFloatValue(index)); } // Gamemasters should be always able to select units - remove not selectable flag else if (index == (int)UnitFields.Flags) { UnitFlags appendValue = (UnitFlags)GetUInt32Value(index); if (target.IsGameMaster()) appendValue &= ~UnitFlags.NotSelectable; fieldBuffer.WriteUInt32((uint)appendValue); } // use modelid_a if not gm, _h if gm for CREATURE_FLAG_EXTRA_TRIGGER creatures else if (index == (int)UnitFields.DisplayId) { uint displayId = GetUInt32Value(index); if (creature != null) { CreatureTemplate cinfo = creature.GetCreatureTemplate(); // this also applies for transform auras SpellInfo transform = Global.SpellMgr.GetSpellInfo(getTransForm()); if (transform != null) { foreach (SpellEffectInfo effect in transform.GetEffectsForDifficulty(GetMap().GetDifficultyID())) { if (effect != null && effect.IsAura(AuraType.Transform)) { CreatureTemplate transformInfo = Global.ObjectMgr.GetCreatureTemplate((uint)effect.MiscValue); if (transformInfo != null) { cinfo = transformInfo; break; } } } } if (cinfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.Trigger)) if (target.IsGameMaster()) displayId = cinfo.GetFirstVisibleModel(); } fieldBuffer.WriteUInt32(displayId); } // hide lootable animation for unallowed players else if (index == (int)ObjectFields.DynamicFlags) { UnitDynFlags dynamicFlags = (UnitDynFlags)GetUInt32Value(index) & ~UnitDynFlags.Tapped; if (creature != null) { if (creature.hasLootRecipient() && !creature.isTappedBy(target)) dynamicFlags |= UnitDynFlags.Tapped; if (!target.isAllowedToLoot(creature)) dynamicFlags &= ~UnitDynFlags.Lootable; } // unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras if (dynamicFlags.HasAnyFlag(UnitDynFlags.TrackUnit)) if (!HasAuraTypeWithCaster(AuraType.ModStalked, target.GetGUID())) dynamicFlags &= ~UnitDynFlags.TrackUnit; fieldBuffer.WriteUInt32((uint)dynamicFlags); } // FG: pretend that OTHER players in own group are friendly ("blue") else if (index == (int)UnitFields.Bytes2 || index == (int)UnitFields.FactionTemplate) { if (IsControlledByPlayer() && target != this && WorldConfig.GetBoolValue(WorldCfg.AllowTwoSideInteractionGroup) && IsInRaidWith(target)) { FactionTemplateRecord ft1 = GetFactionTemplateEntry(); FactionTemplateRecord ft2 = target.GetFactionTemplateEntry(); if (ft1 != null && ft2 != null && !ft1.IsFriendlyTo(ft2)) { if (index == (int)UnitFields.Bytes2) // Allow targetting opposite faction in party when enabled in config fieldBuffer.WriteUInt32(GetUInt32Value(index) & ((int)UnitBytes2Flags.Sanctuary << 8)); // this flag is at public byte offset 1 !! else // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work) fieldBuffer.WriteUInt32(target.getFaction()); } else fieldBuffer.WriteUInt32(GetUInt32Value(index)); } else fieldBuffer.WriteUInt32(GetUInt32Value(index)); } else { // send in current format (float as float, public uint as uint32) WriteValue(fieldBuffer, index); } } } updateMask.AppendToPacket(data); data.WriteBytes(fieldBuffer); } 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); } } }