using System; using System.Collections.Generic; using System.Numerics; using System.Linq; using ACE.Entity.Enum; using ACE.Entity.Enum.Properties; using ACE.Server.Entity; using ACE.Server.Physics; namespace ACE.Server.WorldObjects { /// /// Creature melee combat for players and monsters /// partial class Creature { /// /// Returns TRUE for DualWieldCombat mode /// public bool IsDualWieldAttack { get => CurrentMotionState?.Stance == MotionStance.DualWieldCombat; } /// /// Dual wield alternate will be true if *next* attack is offhand /// public bool DualWieldAlternate; /// /// Returns TRUE for TwoHandedCombat mode /// public bool TwoHandedCombat { get => CurrentMotionState?.Stance == MotionStance.TwoHandedSwordCombat || CurrentMotionState?.Stance == MotionStance.TwoHandedStaffCombat; } /// /// Determines if a weapon can double or triple strike, /// and appends the appropriate multistrike prefix to a MotionCommand /// public string MultiStrike(AttackType attackType, string action) { if ((attackType & AttackType.MultiStrike) == 0) return action; var doubleStrike = action.EndsWith("Thrust") ? AttackType.DoubleThrust : AttackType.DoubleSlash; var tripleStrike = (AttackType)((int)doubleStrike * 2); if (attackType.HasFlag(tripleStrike)) return $"Triple{action}"; if (attackType.HasFlag(doubleStrike)) return $"Double{action}"; else return action; } /// /// Returns the attack types for a weapon /// public AttackType GetWeaponAttackType(WorldObject weapon) { if (weapon == null) return AttackType.Undef; return weapon.W_AttackType; } /// /// Returns the number of strikes for a weapon /// between 1-3 strikes /// public int GetNumStrikes(WorldObject weapon) { var attackType = GetWeaponAttackType(weapon); return GetNumStrikes(attackType); } /// /// Returns the number of strikes for an AttackType /// between 1-3 strikes /// public int GetNumStrikes(AttackType attackType) { if (CurrentMotionState.Stance == MotionStance.TwoHandedSwordCombat || CurrentMotionState.Stance == MotionStance.TwoHandedStaffCombat) return 2; if ((attackType & AttackType.MultiStrike) == 0) return 1; if (attackType.HasFlag(AttackType.TripleSlash) || attackType.HasFlag(AttackType.TripleThrust) || attackType.HasFlag(AttackType.OffhandTripleSlash) || attackType.HasFlag(AttackType.OffhandTripleThrust)) return 3; else if (attackType.HasFlag(AttackType.DoubleSlash) || attackType.HasFlag(AttackType.DoubleThrust) || attackType.HasFlag(AttackType.OffhandDoubleSlash) || attackType.HasFlag(AttackType.OffhandDoubleThrust)) return 2; else return 1; } public int DistanceComparator(PhysicsObj a, PhysicsObj b) { // use square distance to make things a bit faster var dist1 = Location.SquaredDistanceTo(a.WeenieObj.WorldObject.Location); var dist2 = Location.SquaredDistanceTo(b.WeenieObj.WorldObject.Location); return dist1.CompareTo(dist2); } public const float CleaveRange = 5.0f; public const float CleaveRangeSq = CleaveRange * CleaveRange; public const float CleaveAngle = 180.0f; public const float CleaveCylRange = 2.0f; /// /// Performs a cleaving attack for two-handed weapons /// /// The list of cleave targets to hit with this attack public List GetCleaveTarget(Creature target, WorldObject weapon) { var player = this as Player; if (!weapon.IsCleaving) return null; // sort visible objects by ascending distance var visible = PhysicsObj.ObjMaint.GetVisibleObjectsValuesWhere(o => o.WeenieObj.WorldObject != null); visible.Sort(DistanceComparator); var cleaveTargets = new List(); var totalCleaves = weapon.CleaveTargets; foreach (var obj in visible) { // cleaving skips original target if (obj.ID == target.PhysicsObj.ID) continue; // only cleave creatures var creature = obj.WeenieObj.WorldObject as Creature; if (creature == null || creature.Teleporting || creature.IsDead) continue; if (player != null && player.CheckPKStatusVsTarget(creature, null) != null) continue; if (!creature.Attackable && creature.TargetingTactic == TargetingTactic.None || creature.Teleporting) continue; if (creature is CombatPet && (player != null || this is CombatPet)) continue; // no objects in cleave range var cylDist = GetCylinderDistance(creature); if (cylDist > CleaveCylRange) return cleaveTargets; // only cleave in front of attacker var angle = GetAngle(creature); if (Math.Abs(angle) > CleaveAngle / 2.0f) continue; // found cleavable object cleaveTargets.Add(creature); if (cleaveTargets.Count == totalCleaves) break; } return cleaveTargets; } } }