diff --git a/Alliance.Client/_Module/SubModule.xml b/Alliance.Client/_Module/SubModule.xml index 87186c7..97fee4e 100644 --- a/Alliance.Client/_Module/SubModule.xml +++ b/Alliance.Client/_Module/SubModule.xml @@ -2,7 +2,7 @@ - + diff --git a/Alliance.Common/CommonProps.props b/Alliance.Common/CommonProps.props index 31ff47b..e193bc0 100644 --- a/Alliance.Common/CommonProps.props +++ b/Alliance.Common/CommonProps.props @@ -1,5 +1,5 @@ - 0.1.8.9 + 0.1.9.0 diff --git a/Alliance.Editor/_Module/SubModule.xml b/Alliance.Editor/_Module/SubModule.xml index 85ca67e..12d5b46 100644 --- a/Alliance.Editor/_Module/SubModule.xml +++ b/Alliance.Editor/_Module/SubModule.xml @@ -2,7 +2,7 @@ - + diff --git a/Alliance.Server/Core/Utils/CoreUtils.cs b/Alliance.Server/Core/Utils/CoreUtils.cs index 8d7208c..1714373 100644 --- a/Alliance.Server/Core/Utils/CoreUtils.cs +++ b/Alliance.Server/Core/Utils/CoreUtils.cs @@ -32,10 +32,12 @@ public static void TakeDamage(Agent victim, Agent attacker, int damage, float ma if (victim.Health <= 0) return; - if (attacker?.MissionPeer != null) + // Retrieve peer from either agent or rider agent + MissionPeer peer = attacker?.MissionPeer != null ? attacker.MissionPeer : attacker.RiderAgent?.MissionPeer != null ? attacker.RiderAgent.MissionPeer : null; + if (peer != null) { //Communicate damage to client to display PersonalKillFeed - GameNetwork.BeginModuleEventAsServer(attacker.MissionPeer.GetNetworkPeer()); + GameNetwork.BeginModuleEventAsServer(peer.GetNetworkPeer()); GameNetwork.WriteMessage(new SyncPersonalKillFeedNotification(attacker, victim, false, false, false, victim.Health <= damage, false, false, damage)); GameNetwork.EndModuleEventAsServer(); } diff --git a/Alliance.Server/Extensions/WargAttack/Behavior/BoneCheckDuringAnimationBehavior.cs b/Alliance.Server/Extensions/WargAttack/Behavior/BoneCheckDuringAnimationBehavior.cs index 4c0cf8a..1435c78 100644 --- a/Alliance.Server/Extensions/WargAttack/Behavior/BoneCheckDuringAnimationBehavior.cs +++ b/Alliance.Server/Extensions/WargAttack/Behavior/BoneCheckDuringAnimationBehavior.cs @@ -27,6 +27,7 @@ public class BoneCheckDuringAnimationBehavior : MissionBehavior private Agent target; private List boneIds; private float minDistanceSquared; + private float startDelay; private float duration; private bool isChecking; @@ -37,12 +38,13 @@ public class BoneCheckDuringAnimationBehavior : MissionBehavior public Action OnBoneFoundParryCallback; // Constructor callback - public BoneCheckDuringAnimationBehavior(Agent agent, Agent target, List boneIds, float duration, float minDistanceSquared, Action onBoneFoundCallback, Action onBoneFoundParryCallback) + public BoneCheckDuringAnimationBehavior(Agent agent, Agent target, List boneIds, float startDelay, float duration, float minDistanceSquared, Action onBoneFoundCallback, Action onBoneFoundParryCallback) { this.agent = agent; this.target = target; this.boneIds = boneIds; this.minDistanceSquared = minDistanceSquared; + this.startDelay = startDelay; this.duration = duration; isChecking = true; OnBoneFoundCallback = onBoneFoundCallback; // register callback @@ -62,6 +64,8 @@ public override void OnMissionTick(float dt) return; } + if (time < startDelay) return; + sbyte boneInRange = SearchBoneInRange(agent, target, boneIds, minDistanceSquared); // If a bone is found, call the callback. diff --git a/Alliance.Server/Extensions/WargAttack/Behavior/WargBehavior.cs b/Alliance.Server/Extensions/WargAttack/Behavior/WargBehavior.cs index 8685bec..42f0b9f 100644 --- a/Alliance.Server/Extensions/WargAttack/Behavior/WargBehavior.cs +++ b/Alliance.Server/Extensions/WargAttack/Behavior/WargBehavior.cs @@ -1,8 +1,10 @@ using Alliance.Common.Extensions.AnimationPlayer; using Alliance.Common.Extensions.FormationEnforcer.Component; +using Alliance.Server.Core.Utils; using Alliance.Server.Extensions.WargAttack.Models; using Alliance.Server.Extensions.WargAttack.Utilities; using System; +using System.Collections.Generic; using System.Linq; using TaleWorlds.Core; using TaleWorlds.Engine; @@ -34,11 +36,14 @@ public override void OnAgentBuild(Agent agent, Banner banner) public class WargComponent : AgentComponent { private Agent _target; + private Agent _lastAttacker; private float _refreshDelay; private float _targetChangeDelay; + private float _lastAttackDelay; private MonsterState _currentState; private bool _isWounded; private ChaseTrajectory _chaseTrajectory; + private float _fearOfTarget; public enum ChaseTrajectory { @@ -76,6 +81,14 @@ public WargComponent(Agent agent) : base(agent) } } + public override void OnHit(Agent affectorAgent, int damage, in MissionWeapon affectorWeapon) + { + if (affectorAgent != null) + { + _lastAttacker = affectorAgent; + } + } + public override void OnTickAsAI(float dt) { try @@ -83,7 +96,9 @@ public override void OnTickAsAI(float dt) // Tick once every RefreshTime or CloseRangeRefreshTime if near target _refreshDelay += dt; _targetChangeDelay += dt; - if ((_currentState == MonsterState.Chase || _currentState == MonsterState.Careful) && _target != null && (_target.Position - Agent.Position).Length < WargConstants.THREAT_RANGE) + _lastAttackDelay += dt; + _fearOfTarget = Math.Max(0, _fearOfTarget - (0.01f * dt)); + if (_currentState == MonsterState.Attack || (_currentState == MonsterState.Chase || _currentState == MonsterState.Careful) && _target != null && (_target.Position - Agent.Position).Length < WargConstants.THREAT_RANGE) { if (_refreshDelay <= WargConstants.CLOSE_RANGE_AI_REFRESH_TIME) return; } @@ -112,23 +127,23 @@ public override void OnTickAsAI(float dt) case MonsterState.None: break; case MonsterState.Idle: RandomIdleBehavior(); - Log("Warg is idling", LogLevel.Debug); + Log($"Warg {Agent.Index} is idling", LogLevel.Debug); break; case MonsterState.Chase: ChaseTarget(); - Log("Warg is chasing " + _target?.Name, LogLevel.Debug); + Log($"Warg {Agent.Index} is chasing {_target?.Name} ({_chaseTrajectory})", LogLevel.Debug); break; case MonsterState.Attack: AttackTarget(); - Log("Warg is attacking", LogLevel.Debug); + Log($"Warg {Agent.Index} is attacking", LogLevel.Debug); break; case MonsterState.Flee: FleeBehavior(); - Log("Warg is fleeing", LogLevel.Debug); + Log($"Warg {Agent.Index} is fleeing", LogLevel.Debug); break; case MonsterState.Careful: MaintainDistanceFromTarget(); - Log("Warg is keeping its distance from target with torch", LogLevel.Debug); + Log($"Warg {Agent.Index} is keeping its distance from target {_target?.Name} (fear level : {_fearOfTarget})", LogLevel.Debug); break; } } @@ -141,9 +156,8 @@ public override void OnTickAsAI(float dt) private void DetermineTargetAndStateWithAIRider() { // Check if there is enemy close by - MBList nearbyAgents = new MBList(); - Mission.Current.GetNearbyAgents(Agent.Position.AsVec2, 5f, nearbyAgents); - _target = nearbyAgents.FirstOrDefault(agt => agt != Agent.RiderAgent); + List nearbyAgents = CoreUtils.GetNearAliveAgentsInRange(5f, Agent); + _target = nearbyAgents.FirstOrDefault(agt => agt != Agent.RiderAgent && agt.Team != Agent.RiderAgent.Team); if (_target == null) { @@ -151,7 +165,7 @@ private void DetermineTargetAndStateWithAIRider() return; } - if (ShouldAttack(50)) + if (ShouldAttack(80)) { _currentState = MonsterState.Attack; return; @@ -172,13 +186,12 @@ private void DetermineTargetAndState() // 95% chance to be careful when nearby unit is dangerous/wield torch if (ShouldBeCareful(0.95f, out Agent _threat)) { - if (_target != _threat && _threat != null) + ChangeTarget(_threat); + if (_fearOfTarget > 0) { - _target = _threat; - _targetChangeDelay = 0; + _currentState = MonsterState.Careful; + return; } - _currentState = MonsterState.Careful; - return; } // 50% chance of attacking target if possible @@ -191,10 +204,7 @@ private void DetermineTargetAndState() // 25% chance of targetting someone if no current target if (ShouldChaseSomeone(0.25f, out Agent _bestTarget)) { - if (_target != _bestTarget && _bestTarget != null) - { - _target = _bestTarget; - } + ChangeTarget(_bestTarget); _currentState = MonsterState.Chase; return; } @@ -203,15 +213,26 @@ private void DetermineTargetAndState() _currentState = MonsterState.Idle; } + private void ChangeTarget(Agent _threat) + { + if (_target != _threat && _threat != null) + { + _target = _threat; + _targetChangeDelay = 0; + // Random initial fear value between 0.25 and 1 + _fearOfTarget = 0.25f + MBRandom.RandomFloat / 2 + (_isWounded ? 0.25f : 0); + } + } + private bool ShouldAttack(float probability) { - if (_target == null || _target.Health <= 0) return false; + if (_target == null || _target.Health <= 0 || _lastAttackDelay < WargConstants.ATTACK_COOLDOWN) return false; float distanceToTarget = (_target.Position - Agent.Position).Length; - float distanceForAttack = Agent.MovementVelocity.Y >= 4 ? 5f : 3f; + float distanceForAttack = 1.5f + Agent.MovementVelocity.Y; bool closeEnoughForAttack = distanceToTarget <= distanceForAttack; bool frontAttack = WargAttackHelper.IsInFrontCone(Agent, _target, 45); - Log($"close enough ? {closeEnoughForAttack} ({distanceForAttack}/{distanceToTarget}) front ? {frontAttack}", LogLevel.Debug); + //Log($"Close enough for attack ? {closeEnoughForAttack} ({distanceForAttack}/{distanceToTarget}) front ? {frontAttack}", LogLevel.Debug); // Close enough to attack and target is in front if (MBRandom.RandomFloat < probability && closeEnoughForAttack && frontAttack) @@ -228,7 +249,7 @@ private bool ShouldChaseSomeone(float probability, out Agent _bestTarget) { if (MBRandom.RandomFloat < probability) { - _bestTarget = GetBestTarget(); + _bestTarget = GetBestTarget(WargConstants.CHASE_RADIUS); _targetChangeDelay = 0; return true; } @@ -269,39 +290,41 @@ private bool ShouldBeCareful(float probability, out Agent _threat) private bool ShouldFlee(float probability) { - return _currentState == MonsterState.Flee || _isWounded && MBRandom.RandomFloat < probability; + return (_currentState == MonsterState.Flee && MBRandom.RandomFloat < 0.99f) || _isWounded && MBRandom.RandomFloat < probability; } - private Agent GetBestTarget() + private Agent GetBestTarget(float range) { - MBList nearbyAgents = new MBList(); - Mission.Current.GetNearbyAgents(Agent.Position.AsVec2, WargConstants.CHASE_RADIUS, nearbyAgents); + List nearbyAgents = CoreUtils.GetNearAliveAgentsInRange(range, Agent); Agent bestTarget = null; float bestScore = float.MinValue; foreach (Agent potentialTarget in nearbyAgents) { - if (potentialTarget == Agent || !potentialTarget.IsActive()) + if (potentialTarget == Agent || !potentialTarget.IsActive() || (potentialTarget.Monster.StringId == "warg" && MBRandom.RandomFloat < 0.9999f)) continue; float score = 0; FormationComponent formationComponent = potentialTarget.MissionPeer?.GetComponent(); + if (potentialTarget == _lastAttacker) + score += 100; // Prefer to hit back its last attacker + if (formationComponent != null && formationComponent.State == FormationState.Rambo) score += 40; // Prefer rambos if (potentialTarget.Team != Agent.Team) score += 40; // Prefer enemies - if (potentialTarget.Monster.StringId != "warg" && potentialTarget.MountAgent?.Monster.StringId != "warg") - score += 30; // Prefer non-wargs - if (potentialTarget.Health / potentialTarget.HealthLimit < WargConstants.LOW_HEALTH_THRESHOLD) score += 20; // Prefer wounded targets - if (IsWieldingTorch(potentialTarget)) - score -= 40; // Avoid targets with torch + if (potentialTarget.MountAgent?.Monster.StringId == "warg") + score -= 40; // Avoid warg riders + + if (IsThreat(potentialTarget)) + score -= 200; // Avoid dangerous targets float distance = (potentialTarget.Position - Agent.Position).Length; score -= distance; // Prefer closer targets @@ -318,10 +341,11 @@ private Agent GetBestTarget() private static bool IsWieldingTorch(Agent potentialTarget) { - return potentialTarget.WieldedWeapon.Item != null + return potentialTarget.IsHuman && ( + potentialTarget.WieldedWeapon.Item != null && potentialTarget.WieldedWeapon.Item.StringId.ToLower().Contains("torch") || potentialTarget.WieldedOffhandWeapon.Item != null - && potentialTarget.WieldedOffhandWeapon.Item.StringId.ToLower().Contains("torch"); + && potentialTarget.WieldedOffhandWeapon.Item.StringId.ToLower().Contains("torch")); } private Agent NearbyThreat() @@ -331,20 +355,22 @@ private Agent NearbyThreat() foreach (Agent agent in nearbyAgents) { - if (agent != Agent && agent.IsActive() && IsWieldingTorch(agent)) - { - return agent; - } + if (IsThreat(agent)) return agent; } return null; } + private bool IsThreat(Agent agent) + { + return agent != Agent && agent.IsActive() && IsWieldingTorch(agent); + } + private void RandomIdleBehavior() { if (MBRandom.RandomFloat < 0.3f) // 30% chance to play a random animation while idling { - AnimationSystem.Instance.PlayAnimation(Agent, WargConstants.IdleAnimations.GetRandomElement(), true); + AnimationSystem.Instance.PlayAnimation(Agent, WargConstants.IdleAnimations.GetRandomElement(), false); } else { @@ -352,7 +378,7 @@ private void RandomIdleBehavior() randomDirection.RotateAboutZ(MBRandom.RandomFloat * 2 - 1f); Vec3 destinationVec3 = Agent.Position + randomDirection * MBRandom.RandomFloat * 10; WorldPosition destination = new WorldPosition(Mission.Current.Scene, destinationVec3); - Log($"Warg moving from {Agent.Position.AsVec2} to {destination.AsVec2}", LogLevel.Debug); + //Log($"Warg moving from {Agent.Position.AsVec2} to {destination.AsVec2}", LogLevel.Debug); Agent.SetScriptedPosition(ref destination, false, Agent.AIScriptedFrameFlags.None); } } @@ -362,16 +388,16 @@ private void FleeBehavior() WorldPosition worldPosition = Agent.Mission.GetClosestFleePositionForAgent(Agent); if (Agent.IsRetreating() && Agent.MovementVelocity.Y <= 0.5f) { - Log($"Fleeing warg is stuck", LogLevel.Debug); + //Log($"Fleeing warg is stuck", LogLevel.Debug); Vec3 randomDirection = new Vec3(Agent.LookDirection); randomDirection.RotateAboutZ(MBRandom.RandomFloat * 2 - 1f); Vec3 destinationVec3 = Agent.Position + randomDirection * MBRandom.RandomFloat * 100; WorldPosition destination = new WorldPosition(Mission.Current.Scene, destinationVec3); - Log($"To unstuck, warg moving from {Agent.Position.AsVec2} to {destination.AsVec2}", LogLevel.Debug); + //Log($"To unstuck, warg moving from {Agent.Position.AsVec2} to {destination.AsVec2}", LogLevel.Debug); Agent.SetScriptedPosition(ref destination, false, Agent.AIScriptedFrameFlags.None); } Agent.Retreat(worldPosition); - Log($"Warg is fleeing from {Agent.Position.AsVec2} to {worldPosition.AsVec2}", LogLevel.Debug); + //Log($"Warg is fleeing from {Agent.Position.AsVec2} to {worldPosition.AsVec2}", LogLevel.Debug); } private void ChaseTarget() @@ -380,26 +406,38 @@ private void ChaseTarget() float distanceToTarget = (_target.Position - Agent.Position).Length; - // Determine chase behavior - initialize trajectory on first chase - if (_chaseTrajectory == default) + // Set speed depending on distance to target + if (distanceToTarget > WargConstants.CHASE_RADIUS / 2 || MBRandom.RandomFloat < 0.25f) // 25% chance to charge, or if far from target { - float randomValue = MBRandom.RandomFloat; - if (randomValue < 0.33f) - { - _chaseTrajectory = ChaseTrajectory.Straight; - } - else if (randomValue < 0.66f) - { - _chaseTrajectory = ChaseTrajectory.CurveLeft; - } - else - { - _chaseTrajectory = ChaseTrajectory.CurveRight; - } + Agent.SetMaximumSpeedLimit(Agent.Monster.WalkingSpeedLimit * 3f, false); // Charge speed + } + else + { + Agent.SetMaximumSpeedLimit(Agent.Monster.WalkingSpeedLimit, false); // Slow closing in + } + + WorldPosition destination = _target.Position.ToWorldPosition(); + + if (distanceToTarget < 2) + { + Vec3 offset = new(Agent.GetMovementDirection() * 10); + destination = (Agent.Position + offset).ToWorldPosition(); + } + else if (distanceToTarget > 8) + { + Vec3 offset = new(_target.GetMovementDirection() * distanceToTarget); + offset.RotateAboutZ(MBRandom.RandomInt(-2, 2)); + destination = (_target.Position + offset).ToWorldPosition(); } + //Log($"Warg chasing target to {targetPos.GetGroundVec3()} ({_chaseTrajectory})", LogLevel.Debug); + Agent.SetScriptedPosition(ref destination, false, Agent.AIScriptedFrameFlags.None); + return; + + + // Set speed depending on distance to target - if (distanceToTarget > 20f || MBRandom.RandomFloat < 0.4f) // 40% chance to charge, or if far from target + if (distanceToTarget > WargConstants.CHASE_RADIUS / 2 || MBRandom.RandomFloat < 0.25f) // 25% chance to charge, or if far from target { Agent.SetMaximumSpeedLimit(Agent.Monster.WalkingSpeedLimit * 3f, false); // Charge speed } @@ -411,28 +449,32 @@ private void ChaseTarget() // Adjust facing direction to keep track of the target Vec3 directionToTarget = (_target.Position - Agent.Position).NormalizedCopy(); Vec3 curveOffset = Vec3.Zero; + Vec3 extensionBeyondTarget = Vec3.Zero; // Extend the position beyond the target - // Apply the chosen trajectory to determine the path to target + // Define curve offset and extension beyond target based on chosen trajectory switch (_chaseTrajectory) { case ChaseTrajectory.CurveLeft: - curveOffset = new Vec3(-directionToTarget.z, 0, directionToTarget.x) * 100f; // Curve left + curveOffset = new Vec3(-directionToTarget.y, directionToTarget.x, 0) * distanceToTarget / 5; // Modified to curve left correctly break; case ChaseTrajectory.CurveRight: - curveOffset = new Vec3(directionToTarget.z, 0, -directionToTarget.x) * 100f; // Curve right + curveOffset = new Vec3(directionToTarget.y, -directionToTarget.x, 0) * distanceToTarget / 5; // Modified to curve right correctly break; case ChaseTrajectory.Straight: - curveOffset = Vec3.Zero; // No offset for straight approach break; } - // Calculate the desired position - Vec3 desiredPosition = _target.Position - directionToTarget * 3f + curveOffset; + if (WargAttackHelper.IsInFrontCone(Agent, _target, 30)) + { + extensionBeyondTarget = directionToTarget * (1 + distanceToTarget + Agent.MovementVelocity.Y); + } + else + { + extensionBeyondTarget = -directionToTarget; + } - // Overshoot to ensure warg gets close enough to engage the target properly - Vec3 overshootPosition = desiredPosition + directionToTarget * 20f; - WorldPosition targetPos = overshootPosition.ToWorldPosition(); - Log($"Warg chasing target to {targetPos.GetGroundVec3()} ({_chaseTrajectory})", LogLevel.Debug); + WorldPosition targetPos = (_target.Position + curveOffset + extensionBeyondTarget).ToWorldPosition(); + //Log($"Warg chasing target to {targetPos.GetGroundVec3()} ({_chaseTrajectory})", LogLevel.Debug); Agent.SetScriptedPosition(ref targetPos, false, Agent.AIScriptedFrameFlags.None); } @@ -445,12 +487,14 @@ private void AttackTarget() { // Move slightly beyond the target to make sure the running attack reaches Vec3 directionToTarget = (_target.Position - Agent.Position).NormalizedCopy(); - Vec3 overshootPosition = _target.Position + directionToTarget * 2f; // Move 2 meters past the target + Vec3 overshootPosition = _target.Position + directionToTarget * 10f; // Move 2 meters past the target WorldPosition overshootWorldPosition = overshootPosition.ToWorldPosition(); Agent.SetScriptedPosition(ref overshootWorldPosition, false, Agent.AIScriptedFrameFlags.None); } Agent.WargAttack(); + + _lastAttackDelay = 0; } private void MaintainDistanceFromTarget() @@ -458,19 +502,21 @@ private void MaintainDistanceFromTarget() if (_target == null) return; float distanceToTarget = (_target.Position - Agent.Position).Length; + // Target should maintain distance towards target relative to its fear + float desiredRange = WargConstants.THREAT_RANGE * (0.5f + _fearOfTarget); // Calculate the direction from the warg to the target Vec3 directionToTarget = (_target.Position - Agent.Position).NormalizedCopy(); // If target is too close, walkback or run away - if (distanceToTarget < WargConstants.THREAT_RANGE * 0.6f) + if (distanceToTarget < desiredRange * 0.6f) { float dotProduct = Vec3.DotProduct(Agent.LookDirection, directionToTarget); bool targetInFront = dotProduct > 0.7f; // Target is walking, use TP to walkback - if (_target.MovementVelocity.Y < 3 && distanceToTarget > WargConstants.THREAT_RANGE * 0.4f && targetInFront) + if (_target.MovementVelocity.Y < 3 && distanceToTarget > desiredRange * 0.4f && targetInFront) { - float proportionalDistance = distanceToTarget + (WargConstants.THREAT_RANGE * 0.6f - distanceToTarget) * 0.25f; + float proportionalDistance = distanceToTarget + (desiredRange * 0.6f - distanceToTarget) * 0.25f; Vec3 fallbackPosition = _target.Position - (directionToTarget * proportionalDistance); WorldPosition fallbackWorldPosition = fallbackPosition.ToWorldPosition(); // Check if path to destination exist before teleporting to avoid obstacles @@ -479,14 +525,14 @@ private void MaintainDistanceFromTarget() Agent.SetScriptedPosition(ref fallbackWorldPosition, true, Agent.AIScriptedFrameFlags.None); Agent.TeleportToPosition(fallbackPosition); Agent.SetMovementDirection(directionToTarget.AsVec2); - AnimationSystem.Instance.PlayAnimation(Agent, WargConstants.WalkBackAnimation, false); + //AnimationSystem.Instance.PlayAnimation(Agent, WargConstants.WalkBackAnimation, false); } else { - Log($"No path possible to fallback", LogLevel.Warning); + Log($"No path possible for fallback", LogLevel.Warning); } } - // Target is running, run back + // Target is running, run away else { Vec3 fallbackPosition = _target.Position - (directionToTarget * WargConstants.THREAT_RANGE); @@ -496,9 +542,9 @@ private void MaintainDistanceFromTarget() } } // If threat is too far, slowly close in - else if (distanceToTarget > WargConstants.THREAT_RANGE * 0.7f) + else if (distanceToTarget > desiredRange * 0.7f) { - Vec3 desiredPosition = _target.Position - (directionToTarget * (WargConstants.THREAT_RANGE * 0.6f)); + Vec3 desiredPosition = _target.Position - (directionToTarget * (desiredRange * 0.6f)); WorldPosition desiredWorldPosition = desiredPosition.ToWorldPosition(); Agent.SetScriptedPosition(ref desiredWorldPosition, true, Agent.AIScriptedFrameFlags.DoNotRun); } diff --git a/Alliance.Server/Extensions/WargAttack/Models/WargConstants.cs b/Alliance.Server/Extensions/WargAttack/Models/WargConstants.cs index 2376096..6fde09d 100644 --- a/Alliance.Server/Extensions/WargAttack/Models/WargConstants.cs +++ b/Alliance.Server/Extensions/WargAttack/Models/WargConstants.cs @@ -30,6 +30,7 @@ public static class WargConstants public const float LOW_HEALTH_THRESHOLD = 0.3f; // Threshold to consider warg as wounded public const float MAINTAIN_DISTANCE = 5f; // Distance to keep when being careful of target public const float TARGET_LOSE_INTEREST_TIME = 30; // Delay before warg lose interest in target + public const float ATTACK_COOLDOWN = 4; // Cooldown between attacks static WargConstants() { diff --git a/Alliance.Server/Extensions/WargAttack/Utilities/WargAttackHelper.cs b/Alliance.Server/Extensions/WargAttack/Utilities/WargAttackHelper.cs index 098b669..6b5c446 100644 --- a/Alliance.Server/Extensions/WargAttack/Utilities/WargAttackHelper.cs +++ b/Alliance.Server/Extensions/WargAttack/Utilities/WargAttackHelper.cs @@ -18,6 +18,7 @@ public static void WargAttack(this Agent warg) float radius = 10f; float impactDistance = 0.1f; float attackDuration = 0f; + float startDelay = 0f; List nearbyAllAgents = CoreUtils.GetNearAliveAgentsInRange(radius, warg); @@ -25,21 +26,24 @@ public static void WargAttack(this Agent warg) { AnimationSystem.Instance.PlayAnimation(warg, WargConstants.AttackRunningAnimation, true); attackDuration = WargConstants.AttackRunningAnimation.MaxDuration; + startDelay = 0.2f; } else { AnimationSystem.Instance.PlayAnimation(warg, WargConstants.AttackAnimation, true); attackDuration = WargConstants.AttackAnimation.MaxDuration; + startDelay = 0.1f; } foreach (Agent agent in nearbyAllAgents) { - if (agent != null && agent != warg && agent.IsActive()) + if (agent != null && agent != warg && agent.IsActive() && agent != warg.RiderAgent) { Mission.Current.AddMissionBehavior(new BoneCheckDuringAnimationBehavior( warg, agent, WargConstants.WargBoneIds, + startDelay, attackDuration, impactDistance, (agent, target) => HandleCollision(target, warg), @@ -65,19 +69,23 @@ public static void HandleCollision(Agent target, Agent damager) return; var damagerAgent = damager != null ? damager : target; + int damage = target.Monster.StringId == "warg" ? 25 : 50; // Wargs do less damage to each other // If target is AI controlled, chance of reverse damage to simulate target defending if (target.IsAIControlled && target.IsHuman && MBRandom.RandomFloat < 0.5f) { // Target deals damage to warg - target.SetMovementDirection((damagerAgent.Position - target.Position).AsVec2); - AnimationSystem.Instance.PlayAnimation(target, WargConstants.DefendAnimation, true); - CoreUtils.TakeDamage(damagerAgent, target, 50); + if (!target.HasMount) + { + target.SetMovementDirection((damagerAgent.Position - target.Position).AsVec2); + AnimationSystem.Instance.PlayAnimation(target, WargConstants.DefendAnimation, true); + } + CoreUtils.TakeDamage(damagerAgent, target, damage); } else { // Warg deals damage to target - CoreUtils.TakeDamage(target, damagerAgent, 50); + CoreUtils.TakeDamage(target, damagerAgent, damage); } } } @@ -105,7 +113,7 @@ public static void HandleParriedCollision(Agent target, Agent damager) // TODO : if target parried with a shield, reduce shield durability, otherwise project target var damagerAgent = damager != null ? damager : target; - CoreUtils.TakeDamage(target, damagerAgent, 0, 30); // Application of the blow. + CoreUtils.TakeDamage(target, damagerAgent, 0); // Application of the blow. } } catch (Exception e) diff --git a/Alliance.Server/_Module/SubModule.xml b/Alliance.Server/_Module/SubModule.xml index e6c5963..5ab15a9 100644 --- a/Alliance.Server/_Module/SubModule.xml +++ b/Alliance.Server/_Module/SubModule.xml @@ -2,7 +2,7 @@ - +