diff --git a/Framework/Constants/SharedConst.cs b/Framework/Constants/SharedConst.cs index f5a8797cd..5bf189f5f 100644 --- a/Framework/Constants/SharedConst.cs +++ b/Framework/Constants/SharedConst.cs @@ -406,6 +406,7 @@ namespace Framework.Constants } public enum FactionTemplateFlags { + EnemySpar = 0x20, // guessed, sparring with enemies? PVP = 0x800, // flagged for PvP ContestedGuard = 0x1000, // faction will attack players that were involved in PvP combats HostileByDefault = 0x2000 diff --git a/Game/AI/CoreAI/UnitAI.cs b/Game/AI/CoreAI/UnitAI.cs index 7a5e7f6a2..d120ccc10 100644 --- a/Game/AI/CoreAI/UnitAI.cs +++ b/Game/AI/CoreAI/UnitAI.cs @@ -73,16 +73,25 @@ namespace Game.AI if (!me.IsWithinMeleeRange(victim)) return; + bool sparAttack = me.GetFactionTemplateEntry().ShouldSparAttack() && victim.GetFactionTemplateEntry().ShouldSparAttack(); //Make sure our attack is ready and we aren't currently casting before checking distance if (me.isAttackReady()) { - me.AttackerStateUpdate(victim); + if (sparAttack) + me.FakeAttackerStateUpdate(victim); + else + me.AttackerStateUpdate(victim); + me.resetAttackTimer(); } if (me.haveOffhandWeapon() && me.isAttackReady(WeaponAttackType.OffAttack)) { - me.AttackerStateUpdate(victim, WeaponAttackType.OffAttack); + if (sparAttack) + me.FakeAttackerStateUpdate(victim, WeaponAttackType.OffAttack); + else + me.AttackerStateUpdate(victim, WeaponAttackType.OffAttack); + me.resetAttackTimer(WeaponAttackType.OffAttack); } } diff --git a/Game/AI/ScriptedAI/ScriptedAI.cs b/Game/AI/ScriptedAI/ScriptedAI.cs index b92043793..3523c2082 100644 --- a/Game/AI/ScriptedAI/ScriptedAI.cs +++ b/Game/AI/ScriptedAI/ScriptedAI.cs @@ -594,6 +594,7 @@ namespace Game.AI if (!who) who = me; + TempSummon whoSummon = who.ToTempSummon(); if (whoSummon) { @@ -602,7 +603,7 @@ namespace Game.AI return; } - me.DespawnOrUnsummon(0, TimeSpan.FromSeconds(delayToRespawn)); + who.DespawnOrUnsummon(0, TimeSpan.FromSeconds(delayToRespawn)); if (instance != null && who == me) instance.SetBossState(_bossId, EncounterState.Fail); diff --git a/Game/AI/SmartScripts/SmartScript.cs b/Game/AI/SmartScripts/SmartScript.cs index b24efa0d5..325b7c80f 100644 --- a/Game/AI/SmartScripts/SmartScript.cs +++ b/Game/AI/SmartScripts/SmartScript.cs @@ -1069,12 +1069,7 @@ namespace Game.AI if (obj.IsTypeId(TypeId.Unit)) { Creature target = obj.ToCreature(); - if (target.IsAlive() && IsSmart(target)) - { - ((SmartAI)target.GetAI()).SetDespawnTime(e.Action.forceDespawn.delay + 1, e.Action.forceDespawn.respawn); // Next tick - ((SmartAI)target.GetAI()).StartDespawn(); - } - else + if (target) target.DespawnOrUnsummon(e.Action.forceDespawn.delay, TimeSpan.FromSeconds(e.Action.forceDespawn.respawn)); } else if (obj.IsTypeId(TypeId.GameObject)) diff --git a/Game/DataStorage/DB2Manager.cs b/Game/DataStorage/DB2Manager.cs index 515cf6753..617d828f2 100644 --- a/Game/DataStorage/DB2Manager.cs +++ b/Game/DataStorage/DB2Manager.cs @@ -499,7 +499,7 @@ namespace Game.DataStorage public string GetBroadcastTextValue(BroadcastTextRecord broadcastText, LocaleConstant locale = LocaleConstant.enUS, Gender gender = Gender.Male, bool forceGender = false) { - if (gender == Gender.Female && (forceGender || broadcastText.FemaleText.HasString(SharedConst.DefaultLocale))) + if ((gender == Gender.Female || gender == Gender.None) && (forceGender || broadcastText.FemaleText.HasString(SharedConst.DefaultLocale))) { if (broadcastText.FemaleText.HasString(locale)) return broadcastText.FemaleText[locale]; diff --git a/Game/DataStorage/Structs/F_Records.cs b/Game/DataStorage/Structs/F_Records.cs index c1f7dc1c9..a1f955de2 100644 --- a/Game/DataStorage/Structs/F_Records.cs +++ b/Game/DataStorage/Structs/F_Records.cs @@ -98,5 +98,6 @@ namespace Game.DataStorage return EnemyMask == 0 && FriendMask == 0; } public bool IsContestedGuardFaction() { return Flags.HasAnyFlag((ushort)FactionTemplateFlags.ContestedGuard); } + public bool ShouldSparAttack() { return Flags.HasAnyFlag((ushort)FactionTemplateFlags.EnemySpar); } } } diff --git a/Game/Entities/Unit/Unit.Combat.cs b/Game/Entities/Unit/Unit.Combat.cs index 18ab721b5..ea47916de 100644 --- a/Game/Entities/Unit/Unit.Combat.cs +++ b/Game/Entities/Unit/Unit.Combat.cs @@ -2921,11 +2921,6 @@ namespace Game.Entities || (target.IsTypeId(TypeId.Player) && target.ToPlayer().IsGameMaster())) return false; - // can't attack own vehicle or passenger - if (m_vehicle != null) - if (IsOnVehicle(target) || m_vehicle.GetBase().IsOnVehicle(target)) - return false; - // can't attack invisible (ignore stealth for aoe spells) also if the area being looked at is from a spell use the dynamic object created instead of the casting unit. Ignore stealth if target is player and unit in combat with same player if ((bySpell == null || !bySpell.HasAttribute(SpellAttr6.CanTargetInvisible)) && (obj ? !obj.CanSeeOrDetect(target, bySpell != null && bySpell.IsAffectingArea(GetMap().GetDifficultyID())) : !CanSeeOrDetect(target, (bySpell != null && bySpell.IsAffectingArea(GetMap().GetDifficultyID())) || (target.IsTypeId(TypeId.Player) && target.HasStealthAura() && target.IsInCombat() && IsInCombatWith(target))))) return false; diff --git a/Scripts/Spells/Generic.cs b/Scripts/Spells/Generic.cs index bbb08a37f..863be72c2 100644 --- a/Scripts/Spells/Generic.cs +++ b/Scripts/Spells/Generic.cs @@ -1063,9 +1063,17 @@ namespace Scripts.Spells.Generic target.ToCreature().SetReactState(ReactStates.Passive); } + void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode) + { + Unit target = GetTarget(); + target.RemoveFlag(ObjectFields.DynamicFlags, UnitDynFlags.Dead); + target.RemoveFlag(UnitFields.Flags2, UnitFlags2.FeignDeath); + } + public override void Register() { OnEffectApply.Add(new EffectApplyHandler(HandleEffectApply, 0, AuraType.Dummy, AuraEffectHandleModes.Real)); + OnEffectRemove.Add(new EffectApplyHandler(OnRemove, 0, AuraType.Dummy, AuraEffectHandleModes.Real)); } }