public override void Update(/* inout */ SteeringBlender steering, Actor owner, IAgentStateManager agent) { if (!agent.HasProperty(AgentPropertyName.ActiveOpponent)) { ZombieWaitState zws = new ZombieWaitState(6.0f); agent.CurrentState = zws; return; } Actor opponent = GameResources.ActorManager.GetActorById(agent.GetProperty <int>(AgentPropertyName.ActiveOpponent)); BipedControllerComponent opponentBcc = opponent.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); steering.Target = BepuConverter.Convert(opponentBcc.Controller.Body.Position); BipedControllerComponent bcc = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); BepuVec3 toOpponent = opponentBcc.Controller.Body.Position - bcc.Controller.Body.Position; float distance = toOpponent.Length(); ZombieSkillSet zss = owner.GetBehaviorThatImplementsType <ZombieSkillSet>(); BipedWeapon chosenAttack = distance <= zss.MeleeSkill.EffectiveRangeMax ? zss.MeleeSkill : zss.RangedSkill; Matrix attackTransform = Matrix.CreateTranslation(chosenAttack.MuzzleOffset) * Matrix.CreateWorld( BepuConverter.Convert(bcc.Controller.Body.Position), BepuConverter.Convert(bcc.Controller.ViewDirection), Vector3.Up); BepuVec3 bulletPath = opponentBcc.Controller.Body.Position - BepuConverter.Convert(attackTransform.Translation); // If we don't have a shot, we need to specify what kind of movement we need to remedy that. ZombieTacticalMovementState.MovementType movement = ZombieTacticalMovementState.MovementType.None; if (distance < chosenAttack.EffectiveRangeMin) { movement = ZombieTacticalMovementState.MovementType.Retreat; } else if (distance > chosenAttack.EffectiveRangeMax) { movement = ZombieTacticalMovementState.MovementType.Close; } else { BepuRay loeRay = new BepuRay(BepuConverter.Convert(attackTransform.Translation), bulletPath); LOSFilter filter = new LOSFilter(bcc.Controller.Body.CollisionInformation, opponentBcc.Controller.Body.CollisionInformation); RayCastResult loeResult; GameResources.ActorManager.SimSpace.RayCast(loeRay, chosenAttack.EffectiveRangeMax * 1.5f, filter.Test, out loeResult); EntityCollidable otherEntityCollidable = loeResult.HitObject as EntityCollidable; if (otherEntityCollidable != null && otherEntityCollidable.Entity != null && otherEntityCollidable.Entity.Tag != null && (int)(otherEntityCollidable.Entity.Tag) == opponent.Id) { toOpponent.Y = 0.0f; toOpponent.Normalize(); float aimTheta = (float)(Math.Acos(MathHelper.Clamp(BepuVec3.Dot(toOpponent, bcc.Controller.ViewDirection), 0.0f, 1.0f))); const float AIM_CONE_RADIANS = MathHelper.Pi / 12.0f; if (aimTheta <= AIM_CONE_RADIANS) { // TODO: P2: Add some wander to this value: bcc.WorldAim = BepuConverter.Convert(bulletPath); //chosenAttack.CurrentOperation = WeaponFunctions.TriggerPulled; BipedWeapon otherAttack = chosenAttack == zss.MeleeSkill ? zss.RangedSkill : zss.MeleeSkill; otherAttack.UpdateInputState(false, bcc); chosenAttack.UpdateInputState(true, bcc); return; } } else { movement = ZombieTacticalMovementState.MovementType.Lateral; } } if (movement != ZombieTacticalMovementState.MovementType.None) { ZombieTacticalMovementState ztms = new ZombieTacticalMovementState(movement); agent.CurrentState = ztms; } }
private void ProcessAIStepHandler(object sender, UpdateStepEventArgs e) { // Check FOV, add any new foes to memory. And update existing ones. We may also have gained new memories by other means. // Get players and mobs in field of vision: List <RayCastResult> actorsInView = new List <RayCastResult>(); ConeShape visionCone = new ConeShape(VisionDistance, VisionDistance); BipedControllerComponent bcc = Owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); RigidTransform tipOverCone = new RigidTransform(BepuVec3.Forward * VisionDistance * 0.75f, BepuQuaternion.CreateFromAxisAngle(BepuVec3.Right, MathHelper.PiOver2)); RigidTransform eyeLevelAndFacing = new RigidTransform(bcc.Controller.Body.Position - bcc.Controller.Down * bcc.Controller.Body.Height * 0.45f, BepuConverter.Convert(SpaceUtils.GetOrientation(BepuConverter.Convert(bcc.Controller.ViewDirection), Vector3.Up))); RigidTransform visionConeTransform; RigidTransform.Transform(ref tipOverCone, ref eyeLevelAndFacing, out visionConeTransform); BepuVec3 noSweep = BepuVec3.Zero; ViewInterestFilter filter = new ViewInterestFilter(bcc.Controller.Body.CollisionInformation); GameResources.ActorManager.SimSpace.ConvexCast(visionCone, ref visionConeTransform, ref noSweep, filter.Test, actorsInView); for (int a = 0; a < actorsInView.Count; ++a) { // Does this actor warrant an addition to be made to our memory? // If so, check for LOS and recheck range. If those tests pass, modify the memory. EntityCollidable otherEntityCollidable = actorsInView[a].HitObject as EntityCollidable; // We can jump to the Id in the Tag property because we know the filter has validated this. int actorId = (int)(otherEntityCollidable.Entity.Tag); Actor viewedActor = GameResources.ActorManager.GetActorById(actorId); BipedControllerComponent viewedActorBcc = viewedActor.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); BepuVec3 toSubject = viewedActorBcc.Controller.Body.Position - eyeLevelAndFacing.Position; // Check range: if (toSubject.LengthSquared() <= VisionDistance * VisionDistance) { BepuRay losRay = new BepuRay(eyeLevelAndFacing.Position, toSubject); RayCastResult losResult; LOSFilter losFilter = new LOSFilter(bcc.Controller.Body.CollisionInformation, otherEntityCollidable); GameResources.ActorManager.SimSpace.RayCast(losRay, VisionDistance, losFilter.Test, out losResult); EntityCollidable losEC = losResult.HitObject as EntityCollidable; // Test for LOS: if (losEC != null && losEC.Entity != null && losEC.Entity.Tag != null && (int)(losEC.Entity.Tag) == actorId) { // The viewed actor is either a player(foe) or a mob(ally). if (GameResources.ActorManager.IsPlayer(actorId)) { mMemory.SpotFoe(actorId); } else { IAgentStateManager agent = viewedActor.GetBehaviorThatImplementsType <IAgentStateManager>(); if (agent != null && agent.HasProperty(AgentPropertyName.ActiveOpponent)) { int mobFoe = agent.GetProperty <int>(AgentPropertyName.ActiveOpponent); mMemory.SenseFoe(mobFoe); } } } } } // Evaluate current threats and select one to engage: int enemyId = mMemory.GetLargestThreat(); if (enemyId != Actor.INVALID_ACTOR_ID) { if (mAgentProperties.ContainsKey(AgentPropertyName.ActiveOpponent)) { if ((int)(mAgentProperties[AgentPropertyName.ActiveOpponent]) != enemyId) { mAgentProperties[AgentPropertyName.ActiveOpponent] = enemyId; } } else { mAgentProperties.Add(AgentPropertyName.ActiveOpponent, enemyId); } } else { if (mAgentProperties.ContainsKey(AgentPropertyName.ActiveOpponent)) { mAgentProperties.Remove(AgentPropertyName.ActiveOpponent); } } TimeInState += e.GameTime.ElapsedGameTime; CurrentState.Update(mSteering, Owner, this); Vector2 locomotion = mSteering.ComputeForce(Owner); if (locomotion.LengthSquared() == 0.0f) { bcc.OrientationChange = Quaternion.Identity; bcc.HorizontalMovement = Vector2.Zero; } else { bcc.OrientationChange = Quaternion.CreateFromAxisAngle(Vector3.Up, (float)(Math.Atan2(-locomotion.X, locomotion.Y))); bcc.HorizontalMovement = locomotion.Length() * Vector2.UnitY; } mMemory.Fade(e.GameTime); }