public Hull(MapEntityPrefab prefab, Rectangle rectangle, Submarine submarine) : base(prefab, submarine) { rect = rectangle; OxygenPercentage = 100.0f; fireSources = new List <FireSource>(); properties = SerializableProperty.GetProperties(this); int arraySize = (rectangle.Width / WaveWidth + 1); waveY = new float[arraySize]; waveVel = new float[arraySize]; leftDelta = new float[arraySize]; rightDelta = new float[arraySize]; surface = rect.Y - rect.Height; aiTarget = new AITarget(this); hullList.Add(this); ConnectedGaps = new List <Gap>(); if (submarine == null || !submarine.Loading) { Item.UpdateHulls(); Gap.UpdateHulls(); } WaterVolume = 0.0f; InsertToList(); }
//goes through all the AItargets, evaluates how preferable it is to attack the target, //whether the Character can see/hear the target and chooses the most preferable target within //sight/hearing range public void UpdateTargets(Character character) { var prevAiTarget = selectedAiTarget; selectedAiTarget = null; selectedTargetMemory = null; targetValue = 0.0f; UpdateTargetMemories(); foreach (AITarget target in AITarget.List) { if (Level.Loaded != null && target.WorldPosition.Y > Level.Loaded.Size.Y) { continue; } float valueModifier = 0.0f; float dist = 0.0f; Character targetCharacter = target.Entity as Character; //ignore the aitarget if it is the Character itself if (targetCharacter == character) { continue; } if (targetCharacter != null) { if (targetCharacter.IsDead) { if (eatDeadPriority == 0.0f) { continue; } valueModifier = eatDeadPriority; } else if (targetCharacter.SpeciesName == "human") { if (attackHumans == 0.0f) { continue; } valueModifier = attackHumans; } else { EnemyAIController enemy = targetCharacter.AIController as EnemyAIController; if (enemy != null) { if (enemy.combatStrength > combatStrength) { valueModifier = attackStronger; } else if (enemy.combatStrength < combatStrength) { valueModifier = attackWeaker; } else { continue; } } } } else if (target.Entity != null && attackRooms != 0.0f) { IDamageable targetDamageable = target.Entity as IDamageable; if (targetDamageable != null && targetDamageable.Health <= 0.0f) { continue; } //skip the target if it's a room and the character is already inside a sub if (character.AnimController.CurrentHull != null && target.Entity is Hull) { continue; } valueModifier = attackRooms; } if (valueModifier == 0.0f) { continue; } dist = Vector2.Distance(character.WorldPosition, target.WorldPosition); //if the target has been within range earlier, the character will notice it more easily //(i.e. remember where the target was) if (targetMemories.ContainsKey(target)) { dist *= 0.5f; } //ignore target if it's too far to see or hear if (dist > target.SightRange * sight && dist > target.SoundRange * hearing) { continue; } AITargetMemory targetMemory = FindTargetMemory(target); valueModifier = valueModifier * targetMemory.Priority / dist; if (Math.Abs(valueModifier) > Math.Abs(targetValue)) { Vector2 rayStart = character.AnimController.Limbs[0].SimPosition; Vector2 rayEnd = target.SimPosition; if (target.Entity.Submarine != null && character.Submarine == null) { rayStart -= ConvertUnits.ToSimUnits(target.Entity.Submarine.Position); } Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd); Structure closestStructure = (closestBody == null) ? null : closestBody.UserData as Structure; if (selectedAiTarget == null || Math.Abs(valueModifier) > Math.Abs(targetValue)) { selectedAiTarget = target; selectedTargetMemory = targetMemory; targetValue = valueModifier; } } } if (selectedAiTarget != prevAiTarget) { wallAttackPos = Vector2.Zero; } }
private void UpdateEating(float deltaTime) { if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed) { state = AIState.None; return; } Limb mouthLimb = Array.Find(Character.AnimController.Limbs, l => l != null && l.MouthPos.HasValue); if (mouthLimb == null) { mouthLimb = Character.AnimController.GetLimb(LimbType.Head); } if (mouthLimb == null) { DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)"); state = AIState.None; return; } Character targetCharacter = selectedAiTarget.Entity as Character; float eatSpeed = Character.Mass / targetCharacter.Mass * 0.1f; eatTimer += deltaTime * eatSpeed; Vector2 mouthPos = mouthLimb.SimPosition; if (mouthLimb.MouthPos.HasValue) { float cos = (float)Math.Cos(mouthLimb.Rotation); float sin = (float)Math.Sin(mouthLimb.Rotation); mouthPos += new Vector2( mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin, mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos); } Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition; Vector2 limbDiff = attackSimPosition - mouthPos; float limbDist = limbDiff.Length(); if (limbDist < 1.0f) { //pull the target character to the position of the mouth //(+ make the force fluctuate to waggle the character a bit) targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation); targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); //pull the character's mouth to the target character (again with a fluctuating force) float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f)); steeringManager.SteeringManual(deltaTime, limbDiff * pullStrength); mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength); if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f) { //apply damage to the target character to get some blood particles flying targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), 10.0f, false); //keep severing joints until there is only one limb left LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered && l.CanBeSevered); if (nonSeveredJoints.Length == 0) { //only one limb left, the character is now full eaten Entity.Spawner.AddToRemoveQueue(targetCharacter); selectedAiTarget = null; state = AIState.None; } else //sever a random joint { targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]); } } } else if (limbDist < 2.0f) { steeringManager.SteeringManual(deltaTime, limbDiff); Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos); } else { steeringManager.SteeringSeek(attackSimPosition - (mouthPos - SimPosition), 3); } }
public override void SelectTarget(AITarget target) { SelectedAiTarget = target; }
protected virtual void OnTargetChanged(AITarget previousTarget, AITarget newTarget) { }
public virtual void SelectTarget(AITarget target) { }
public Structure(Rectangle rectangle, StructurePrefab sp, Submarine submarine) : base(sp, submarine) { if (rectangle.Width == 0 || rectangle.Height == 0) { return; } System.Diagnostics.Debug.Assert(rectangle.Width > 0 && rectangle.Height > 0); rect = rectangle; #if CLIENT TextureScale = sp.TextureScale; #endif spriteColor = prefab.SpriteColor; if (ResizeHorizontal && !ResizeVertical) { IsHorizontal = true; } else if (ResizeVertical && !ResizeHorizontal) { IsHorizontal = false; } else { if (BodyWidth > 0.0f && BodyHeight > 0.0f) { IsHorizontal = BodyWidth > BodyHeight; } else { IsHorizontal = (rect.Width > rect.Height); } } StairDirection = Prefab.StairDirection; SerializableProperties = SerializableProperty.GetProperties(this); InitProjSpecific(); if (Prefab.Body) { Bodies = new List <Body>(); WallList.Add(this); CreateSections(); UpdateSections(); } else { Sections = new WallSection[1]; Sections[0] = new WallSection(rect); if (StairDirection != Direction.None) { CreateStairBodies(); } } // Only add ai targets automatically to walls if (aiTarget == null && HasBody && Tags.Contains("wall")) { aiTarget = new AITarget(this); } InsertToList(); }
private void UpdateEating(float deltaTime) { if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed) { state = AIState.None; return; } Limb mouthLimb = Array.Find(Character.AnimController.Limbs, l => l != null && l.MouthPos.HasValue); if (mouthLimb == null) { mouthLimb = Character.AnimController.GetLimb(LimbType.Head); } if (mouthLimb == null) { DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)"); state = AIState.None; return; } Character targetCharacter = selectedAiTarget.Entity as Character; float eatSpeed = Character.Mass / targetCharacter.Mass * 0.1f; eatTimer += deltaTime * eatSpeed; Vector2 mouthPos = mouthLimb.SimPosition; if (mouthLimb.MouthPos.HasValue) { float cos = (float)Math.Cos(mouthLimb.Rotation); float sin = (float)Math.Sin(mouthLimb.Rotation); mouthPos += new Vector2( mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin, mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos); } Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition; Vector2 limbDiff = attackSimPosition - mouthPos; float limbDist = limbDiff.Length(); if (limbDist < 1.0f) { //pull the target character to the position of the mouth //(+ make the force fluctuate to waggle the character a bit) targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation); targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); //pull the character's mouth to the target character (again with a fluctuating force) float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f)); steeringManager.SteeringManual(deltaTime, limbDiff * pullStrength); mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength); //GameMain.NilMod.CreatureHealthGainEatingPercent; if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f) { //Kill living players that control the character if being actively consumed. //Before this point is reached a fish could be prevented with stun from severing limbs and thus still save a player. if (!targetCharacter.IsDead && targetCharacter.IsRemotePlayer) { targetCharacter.Kill(CauseOfDeath.Damage, true); } //apply damage to the target character to get some blood particles flying targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); //Lets make this extra bloody, perhaps a client will sync it. targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); //keep severing joints until there is only one limb left LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered && l.CanBeSevered); if (nonSeveredJoints.Length == 0) { //only one limb left, the character is now full eaten if (GameMain.Server != null) { GameMain.Server.ServerLog.WriteLine(Character.Name + " has finished eating " + targetCharacter.Name, Networking.ServerLog.MessageType.Spawns); } Entity.Spawner.AddToRemoveQueue(targetCharacter); selectedAiTarget = null; state = AIState.None; } else //sever a random joint { targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]); if (GameMain.Server != null) { GameMain.Server.CreateEntityEvent(targetCharacter, new object[] { Barotrauma.Networking.NetEntityEvent.Type.Status }); } } } } else if (limbDist < 2.0f) { steeringManager.SteeringManual(deltaTime, limbDiff); Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos); } else { steeringManager.SteeringSeek(attackSimPosition - (mouthPos - SimPosition), 3); } }
protected void ResetAITarget() { _lastAiTarget = null; _selectedAiTarget = null; }
protected override void Act(float deltaTime) { var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager; if (pathSteering == null) { return; } if (newTargetTimer <= 0.0f) { currentTarget = FindRandomTarget(); if (currentTarget != null) { Vector2 pos = character.SimPosition; if (character != null && character.Submarine == null) { pos -= Submarine.MainSub.SimPosition; } var path = pathSteering.PathFinder.FindPath(pos, currentTarget.SimPosition); if (path.Cost > 200.0f && character.AnimController.CurrentHull != null) { return; } pathSteering.SetPath(path); } newTargetTimer = currentTarget == null ? 5.0f : 15.0f; } newTargetTimer -= deltaTime; //wander randomly // - if reached the end of the path // - if the target is unreachable // - if the path requires going outside if (pathSteering == null || (pathSteering.CurrentPath != null && (pathSteering.CurrentPath.NextNode == null || pathSteering.CurrentPath.Unreachable || pathSteering.CurrentPath.HasOutdoorsNodes))) { //steer away from edges of the hull if (character.AnimController.CurrentHull != null) { float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X; float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X; if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance) { if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2) { pathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist)); } else { pathSteering.Reset(); return; } } else if (leftDist < WallAvoidDistance) { pathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance - leftDist) / WallAvoidDistance); pathSteering.WanderAngle = 0.0f; return; } else if (rightDist < WallAvoidDistance) { pathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance - rightDist) / WallAvoidDistance); pathSteering.WanderAngle = MathHelper.Pi; return; } } if (character.AnimController.InWater) { character.AIController.SteeringManager.SteeringManual(deltaTime, new Vector2(0.0f, 0.5f)); } else { character.AIController.SteeringManager.SteeringWander(); //reset vertical steering to prevent dropping down from platforms etc character.AIController.SteeringManager.ResetY(); } return; } if (currentTarget == null) { return; } character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition, 2.0f); }