diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index dd4cb4a77..52740c35e 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -175,8 +175,8 @@ namespace Framework.Constants public const int MaxCreatureModelIds = 4; public const int MaxTrainerspellAbilityReqs = 3; public const int CreatureRegenInterval = 2 * Time.InMilliseconds; - public const int CreatureNoPathEvadeTime = 5 * Time.InMilliseconds; public const int PetFocusRegenInterval = 4 * Time.InMilliseconds; + public const int CreatureNoPathEvadeTime = 5 * Time.InMilliseconds; public const int BoundaryVisualizeCreature = 15425; public const float BoundaryVisualizeCreatureScale = 0.25f; public const int BoundaryVisualizeStepSize = 1; diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs index d87a0658f..dd3d12d44 100644 --- a/Source/Game/Entities/Creature/Creature.Fields.cs +++ b/Source/Game/Entities/Creature/Creature.Fields.cs @@ -33,6 +33,7 @@ namespace Game.Entities ObjectGuid m_suppressedTarget; // Stores the creature's "real" target while casting float m_suppressedOrientation; // Stores the creature's "real" orientation while casting + long _lastDamagedTime; // Part of Evade mechanics MultiMap m_textRepeat = new MultiMap(); public ulong m_PlayerDamageReq; diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index a41d8ae00..bed0c7acd 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -101,10 +101,7 @@ namespace Game.Entities public void DisappearAndDie() { - DestroyForNearbyPlayers(); - if (IsAlive()) - setDeathState(DeathState.JustDied); - RemoveCorpse(false); + ForcedDespawn(0); } public void SearchFormation() @@ -121,7 +118,7 @@ namespace Game.Entities FormationMgr.AddCreatureToGroup(frmdata.leaderGUID, this); } - public void RemoveCorpse(bool setSpawnTime = true) + public void RemoveCorpse(bool setSpawnTime = true, bool destroyForNearbyPlayers = true) { if (getDeathState() != DeathState.Corpse) return; @@ -135,6 +132,9 @@ namespace Game.Entities if (IsAIEnabled) GetAI().CorpseRemoved(respawnDelay); + if (destroyForNearbyPlayers) + DestroyForNearbyPlayers(); + // Should get removed later, just keep "compatibility" with scripts if (setSpawnTime) m_respawnTime = Math.Max(Time.UnixTime + respawnDelay, m_respawnTime); @@ -145,6 +145,21 @@ namespace Game.Entities float x, y, z, o; GetRespawnPosition(out x, out y, out z, out o); + + // We were spawned on transport, calculate real position + if (IsSpawnedOnTransport()) + { + Position pos = m_movementInfo.transport.pos; + pos.posX = x; + pos.posY = y; + pos.posZ = z; + pos.SetOrientation(o); + + ITransport transport = GetDirectTransport(); + if (transport != null) + transport.CalculatePassengerPosition(ref x, ref y, ref z, ref o); + } + SetHomePosition(x, y, z, o); GetMap().CreatureRelocation(this, x, y, z, o); } @@ -275,6 +290,11 @@ namespace Game.Entities SetNpcFlags((NPCFlags)(npcFlags & 0xFFFFFFFF)); SetNpcFlags2((NPCFlags2)(npcFlags >> 32)); + // if unit is in combat, keep this flag + unitFlags &= ~(uint)UnitFlags.InCombat; + if (IsInCombat()) + unitFlags |= (uint)UnitFlags.InCombat; + SetUnitFlags((UnitFlags)unitFlags); SetUnitFlags2((UnitFlags2)unitFlags2); SetUnitFlags3((UnitFlags3)unitFlags3); @@ -283,8 +303,6 @@ namespace Game.Entities SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.StateAnimID), (uint)CliDB.AnimationDataStorage.Count); - RemoveUnitFlag(UnitFlags.InCombat); - SetBaseAttackTime(WeaponAttackType.BaseAttack, cInfo.BaseAttackTime); SetBaseAttackTime(WeaponAttackType.OffAttack, cInfo.BaseAttackTime); SetBaseAttackTime(WeaponAttackType.RangedAttack, cInfo.RangeAttackTime); @@ -518,13 +536,11 @@ namespace Game.Entities if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed() || CanNotReachTarget())) // regenerate health if not in combat or if polymorphed RegenerateHealth(); - if (HasUnitFlag2(UnitFlags2.RegeneratePower)) - { - if (GetPowerType() == PowerType.Energy) - Regenerate(PowerType.Energy); - else - RegenerateMana(); - } + if (GetPowerType() == PowerType.Energy) + Regenerate(PowerType.Energy); + else + Regenerate(PowerType.Mana); + m_regenTimer = SharedConst.CreatureRegenInterval; } @@ -541,66 +557,14 @@ namespace Game.Entities } - void RegenerateMana() - { - int curValue = GetPower(PowerType.Mana); - int maxValue = GetMaxPower(PowerType.Mana); - - if (curValue >= maxValue) - return; - - int addvalue = 0; - - // Combat and any controlled creature - if (IsInCombat() || !GetCharmerOrOwnerGUID().IsEmpty()) - { - float ManaIncreaseRate = WorldConfig.GetFloatValue(WorldCfg.RatePowerMana); - addvalue = (int)((27.0f / 5.0f + 17.0f) * ManaIncreaseRate); - } - else - addvalue = maxValue / 3; - - // Apply modifiers (if any). - addvalue *= (int)GetTotalAuraMultiplierByMiscValue(AuraType.ModPowerRegenPercent, (int)PowerType.Mana); - addvalue += GetTotalAuraModifierByMiscValue(AuraType.ModPowerRegen, (int)PowerType.Mana) * SharedConst.CreatureRegenInterval / (5 * Time.InMilliseconds); - - ModifyPower(PowerType.Mana, addvalue); - } - - void RegenerateHealth() - { - if (!isRegeneratingHealth()) - return; - - ulong curValue = GetHealth(); - ulong maxValue = GetMaxHealth(); - - if (curValue >= maxValue) - return; - - long addvalue = 0; - - // Not only pet, but any controlled creature - if (!GetCharmerOrOwnerGUID().IsEmpty()) - { - float HealthIncreaseRate = WorldConfig.GetFloatValue(WorldCfg.RateHealth); - addvalue = (uint)(0.015f * GetMaxHealth() * HealthIncreaseRate); - } - else - addvalue = (long)maxValue / 3; - - // Apply modifiers (if any). - addvalue *= (int)GetTotalAuraMultiplier(AuraType.ModHealthRegenPercent); - addvalue += GetTotalAuraModifier(AuraType.ModRegen) * SharedConst.CreatureRegenInterval / (5 * Time.InMilliseconds); - - ModifyHealth(addvalue); - } - public void Regenerate(PowerType power) { int curValue = GetPower(power); int maxValue = GetMaxPower(power); + if (!HasUnitFlag2(UnitFlags2.RegeneratePower)) + return; + if (curValue >= maxValue) return; @@ -620,6 +584,19 @@ namespace Game.Entities addvalue = 20; break; } + case PowerType.Mana: + { + // Combat and any controlled creature + if (IsInCombat() || GetCharmerOrOwnerGUID().IsEmpty()) + { + float ManaIncreaseRate = WorldConfig.GetFloatValue(WorldCfg.RatePowerMana); + addvalue = (27.0f / 5.0f + 17.0f) * ManaIncreaseRate; + } + else + addvalue = maxValue / 3; + + break; + } default: return; } @@ -631,6 +608,35 @@ namespace Game.Entities ModifyPower(power, (int)addvalue); } + void RegenerateHealth() + { + if (!isRegeneratingHealth()) + return; + + ulong curValue = GetHealth(); + ulong maxValue = GetMaxHealth(); + + if (curValue >= maxValue) + return; + + long addvalue = 0; + + // Not only pet, but any controlled creature (and not polymorphed) + if (!GetCharmerOrOwnerGUID().IsEmpty() && !IsPolymorphed()) + { + float HealthIncreaseRate = WorldConfig.GetFloatValue(WorldCfg.RateHealth); + addvalue = (uint)(0.015f * GetMaxHealth() * HealthIncreaseRate); + } + else + addvalue = (long)maxValue / 3; + + // Apply modifiers (if any). + addvalue *= (int)GetTotalAuraMultiplier(AuraType.ModHealthRegenPercent); + addvalue += GetTotalAuraModifier(AuraType.ModRegen) * SharedConst.CreatureRegenInterval / (5 * Time.InMilliseconds); + + ModifyHealth(addvalue); + } + public void DoFleeToGetAssistance() { if (!GetVictim()) @@ -770,6 +776,10 @@ namespace Game.Entities return false; } + // Allow players to see those units while dead, do it here (mayby altered by addon auras) + if (cinfo.TypeFlags.HasAnyFlag(CreatureTypeFlags.GhostVisible)) + m_serverSideVisibility.SetValue(ServerSideVisibilityType.Ghost, GhostVisibilityType.Alive | GhostVisibilityType.Ghost); + if (!CreateFromProto(guidlow, entry, data, vehId)) return false; @@ -1615,7 +1625,7 @@ namespace Game.Entities setDeathState(DeathState.Corpse); } - RemoveCorpse(false); + RemoveCorpse(false, false); if (getDeathState() == DeathState.Dead) { @@ -1672,33 +1682,21 @@ namespace Game.Entities return; } + // do it before killing creature + DestroyForNearbyPlayers(); + + if (IsAlive()) + setDeathState(DeathState.JustDied); + + bool overrideRespawnTime = true; if (forceRespawnTimer > TimeSpan.Zero) { - if (IsAlive()) - { - uint respawnDelay = m_respawnDelay; - uint corpseDelay = m_corpseDelay; - m_respawnDelay = (uint)forceRespawnTimer.TotalSeconds; - m_corpseDelay = 0; - setDeathState(DeathState.JustDied); - - m_respawnDelay = respawnDelay; - m_corpseDelay = corpseDelay; - } - else - { - m_corpseRemoveTime = Time.UnixTime; - m_respawnTime = Time.UnixTime + (long)forceRespawnTimer.TotalMilliseconds; - } - - } - else - { - if (IsAlive()) - setDeathState(DeathState.JustDied); + SetRespawnTime((uint)forceRespawnTimer.TotalSeconds); + overrideRespawnTime = false; } - RemoveCorpse(false); + // Skip corpse decay time + RemoveCorpse(overrideRespawnTime, false); } public void DespawnOrUnsummon(TimeSpan time, TimeSpan forceRespawnTimer = default(TimeSpan)) { DespawnOrUnsummon((uint)time.TotalMilliseconds, forceRespawnTimer); } @@ -1979,6 +1977,14 @@ namespace Game.Entities if (!IsAlive()) return false; + // we cannot assist in evade mode + if (IsInEvadeMode()) + return false; + + // or if enemy is in evade mode + if (enemy.GetTypeId() == TypeId.Unit && enemy.ToCreature().IsInEvadeMode()) + return false; + // we don't need help from non-combatant ;) if (IsCivilian()) return false; @@ -2066,22 +2072,41 @@ namespace Game.Entities if (IsAIEnabled && !GetAI().CanAIAttack(victim)) return false; - if (GetMap().IsDungeon()) - return true; + // we cannot attack in evade mode + if (IsInEvadeMode()) + return false; - // if the mob is actively being damaged, do not reset due to distance unless it's a world boss - if (!isWorldBoss()) - if (Time.UnixTime - GetLastDamagedTime() <= SharedConst.MaxAggroResetTime) + // or if enemy is in evade mode + if (victim.GetTypeId() == TypeId.Unit && victim.ToCreature().IsInEvadeMode()) + return false; + + if (!GetCharmerOrOwnerGUID().IsPlayer()) + { + if (GetMap().IsDungeon()) return true; - //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick. - float dist = Math.Max(GetAttackDistance(victim), (WorldConfig.GetFloatValue(WorldCfg.ThreatRadius) + m_CombatDistance)); + // don't check distance to home position if recently damaged, this should include taunt auras + if (!isWorldBoss() && (GetLastDamagedTime() > Global.WorldMgr.GetGameTime() || HasAuraType(AuraType.ModTaunt))) + return true; + } + + // Map visibility range, but no more than 2*cell size + float dist = Math.Min(GetMap().GetVisibilityRange(), MapConst.SizeofCells * 2); Unit unit = GetCharmerOrOwner(); if (unit != null) return victim.IsWithinDist(unit, dist); else - return victim.IsInDist(m_homePosition, dist); + { + // include sizes for huge npcs + dist += GetObjectSize() + victim.GetObjectSize(); + + // to prevent creatures in air ignore attacks because distance is already too high... + if (GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Air)) + return victim.IsInDist2d(m_homePosition, dist); + else + return victim.IsInDist(m_homePosition, dist); + } } CreatureAddon GetCreatureAddon() @@ -2242,40 +2267,17 @@ namespace Game.Entities public void GetRespawnPosition(out float x, out float y, out float z) { - if (m_spawnId != 0) - { - CreatureData data = Global.ObjectMgr.GetCreatureData(GetSpawnId()); - if (data != null) - { - x = data.posX; - y = data.posY; - z = data.posZ; - return; - } - } - - GetPosition(out x, out y, out z); + float notUsed; + GetRespawnPosition(out x, out y, out z, out notUsed, out notUsed); } public void GetRespawnPosition(out float x, out float y, out float z, out float ori) { - if (m_spawnId != 0) - { - CreatureData data = Global.ObjectMgr.GetCreatureData(GetSpawnId()); - if (data != null) - { - x = data.posX; - y = data.posY; - z = data.posZ; - ori = data.orientation; - - return; - } - } - - GetPosition(out x, out y, out z, out ori); + float notUsed; + GetRespawnPosition(out x, out y, out z, out ori, out notUsed); } public void GetRespawnPosition(out float x, out float y, out float z, out float ori, out float dist) { + // for npcs on transport, this will return transport offset if (m_spawnId != 0) { CreatureData data = Global.ObjectMgr.GetCreatureData(GetSpawnId()); @@ -2291,10 +2293,17 @@ namespace Game.Entities } } - GetPosition(out x, out y, out z, out ori); + // changed this from current position to home position, fixes world summons with infinite duration (wg npcs for example) + Position homePos = GetHomePosition(); + x = homePos.GetPositionX(); + y = homePos.GetPositionY(); + z = homePos.GetPositionZ(); + ori = homePos.GetOrientation(); dist = 0; } + bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.mapid != GetMapId(); } + public void AllLootRemovedFromCorpse() { if (loot.loot_type != LootType.Skinning && !IsPet() && GetCreatureTemplate().SkinLootId != 0 && hasLootRecipient()) @@ -3056,6 +3065,10 @@ namespace Game.Entities public bool IsReputationGainDisabled() { return DisableReputationGain; } public bool IsDamageEnoughForLootingAndReward() { return m_creatureInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoPlayerDamageReq) || m_PlayerDamageReq == 0; } + // Part of Evade mechanics + long GetLastDamagedTime() { return _lastDamagedTime; } + public void SetLastDamagedTime(long val) { _lastDamagedTime = val; } + public void ResetPlayerDamageReq() { m_PlayerDamageReq = (uint)(GetHealth() / 2); } public uint GetOriginalEntry() diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 75c456847..6908d67b1 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -1047,13 +1047,14 @@ namespace Game.Entities victim.ModifyHealth(-(int)damage); if (damagetype == DamageEffectType.Direct || damagetype == DamageEffectType.SpellDirect) - { victim.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.DirectDamage, spellProto != null ? spellProto.Id : 0); - victim.UpdateLastDamagedTime(spellProto); - } if (!victim.IsTypeId(TypeId.Player)) { + // Part of Evade mechanics. DoT's and Thorns / Retribution Aura do not contribute to this + if (damagetype != DamageEffectType.DOT && damage > 0 && !victim.GetOwnerGUID().IsPlayer() && (spellProto == null || !spellProto.HasAura(GetMap().GetDifficultyID(), AuraType.DamageShield))) + victim.ToCreature().SetLastDamagedTime(Global.WorldMgr.GetGameTime() + SharedConst.MaxAggroResetTime); + victim.AddThreat(this, damage, damageSchoolMask, spellProto); } else // victim is a player @@ -3206,9 +3207,5 @@ namespace Game.Entities } return true; } - - // Part of Evade mechanics - public long GetLastDamagedTime() { return _lastDamagedTime; } - public void SetLastDamagedTime(long val) { _lastDamagedTime = val; } } } diff --git a/Source/Game/Entities/Unit/Unit.Fields.cs b/Source/Game/Entities/Unit/Unit.Fields.cs index 33b179897..999eef410 100644 --- a/Source/Game/Entities/Unit/Unit.Fields.cs +++ b/Source/Game/Entities/Unit/Unit.Fields.cs @@ -65,7 +65,6 @@ namespace Game.Entities public float m_modMeleeHitChance { get; set; } public float m_modRangedHitChance { get; set; } public float m_modSpellHitChance { get; set; } - long _lastDamagedTime; bool m_canDualWield; public int m_baseSpellCritChance { get; set; } public uint m_regenTimer { get; set; } diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 3d2d3d7c3..7df66d00c 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -4262,17 +4262,6 @@ namespace Game.Entities return val; } - void UpdateLastDamagedTime(SpellInfo spellProto) - { - if (!IsTypeId(TypeId.Unit) || IsPet()) - return; - - if (spellProto != null && spellProto.HasAura(Difficulty.None, AuraType.DamageShield)) - return; - - SetLastDamagedTime(Time.UnixTime); - } - public bool IsHighestExclusiveAura(Aura aura, bool removeOtherAuraApplications = false) { foreach (AuraEffect aurEff in aura.GetAuraEffects()) diff --git a/Source/Game/Handlers/NPCHandler.cs b/Source/Game/Handlers/NPCHandler.cs index b6a5093f6..466ac81ea 100644 --- a/Source/Game/Handlers/NPCHandler.cs +++ b/Source/Game/Handlers/NPCHandler.cs @@ -143,8 +143,9 @@ namespace Game GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Talk); - if (unit.IsArmorer() || unit.IsCivilian() || unit.IsQuestGiver() || unit.IsServiceProvider() || unit.IsGuard()) - unit.StopMoving(); + // and if he has pure gossip or is banker and moves or is tabard designer? + //if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard()) + unit.StopMoving(); // If spiritguide, no need for gossip menu, just put player into resurrect queue if (unit.IsSpiritGuide()) diff --git a/Source/Game/OutdoorPVP/OutdoorPvP.cs b/Source/Game/OutdoorPVP/OutdoorPvP.cs index 387448e86..c1c1d4e30 100644 --- a/Source/Game/OutdoorPVP/OutdoorPvP.cs +++ b/Source/Game/OutdoorPVP/OutdoorPvP.cs @@ -438,7 +438,7 @@ namespace Game.PvP { // Don't save respawn time creature.SetRespawnTime(0); - creature.RemoveCorpse(); + creature.DespawnOrUnsummon(); creature.AddObjectToRemoveList(); } diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 6c677e06d..4309936b7 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -2587,7 +2587,7 @@ namespace Game.Spells { target.Kill(caster); if (caster.IsTypeId(TypeId.Unit)) - caster.ToCreature().RemoveCorpse(); + caster.ToCreature().DespawnOrUnsummon(); } if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmount)) diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 0ec452057..38e384d86 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -2956,10 +2956,7 @@ namespace Game.Spells caster.RewardPlayerAndGroupAtEvent(18388, unitTarget); Creature target = unitTarget.ToCreature(); if (target != null) - { - target.setDeathState(DeathState.Corpse); - target.RemoveCorpse(); - } + target.DespawnOrUnsummon(); } break; // Mug Transformation