2f107db...5202af9

This commit is contained in:
Joonas Rikkonen
2019-03-18 21:42:26 +02:00
parent 58c92888b7
commit 044fd3344b
395 changed files with 27417 additions and 19754 deletions
@@ -12,7 +12,17 @@ namespace Barotrauma
private AIState state;
protected AITarget selectedAiTarget;
protected AITarget _previousAiTarget;
protected AITarget _selectedAiTarget;
public AITarget SelectedAiTarget
{
get { return _selectedAiTarget; }
protected set
{
_previousAiTarget = _selectedAiTarget;
_selectedAiTarget = value;
}
}
protected SteeringManager steeringManager;
@@ -57,11 +67,6 @@ namespace Barotrauma
get { return null; }
}
public AITarget SelectedAiTarget
{
get { return selectedAiTarget; }
}
public AIState State
{
get { return state; }
@@ -27,7 +27,7 @@ namespace Barotrauma
public float SightRange
{
get { return sightRange; }
set { sightRange = MathHelper.Clamp(value, MinSightRange, MaxSoundRange); }
set { sightRange = MathHelper.Clamp(value, MinSightRange, MaxSightRange); }
}
private float sectorRad = MathHelper.TwoPi;
@@ -58,8 +58,8 @@ namespace Barotrauma
public bool Enabled = true;
public readonly float MinSoundRange, MinSightRange;
public readonly float MaxSoundRange = float.MaxValue, MaxSightRange = float.MaxValue;
public float MinSoundRange, MinSightRange;
public float MaxSoundRange = float.MaxValue, MaxSightRange = float.MaxValue;
public Vector2 WorldPosition
{
@@ -101,19 +101,12 @@ namespace Barotrauma
public AITarget(Entity e, XElement element) : this(e)
{
SightRange = MinSightRange = element.GetAttributeFloat("sightrange", 0.0f);
SoundRange = MinSoundRange = element.GetAttributeFloat("soundrange", 0.0f);
// Use the min and max definitions if found.
if (element.Attribute("minsightrange") != null)
{
MinSightRange = element.GetAttributeFloat("minsightrange", MinSightRange);
}
if (element.Attribute("minsoundrange") != null)
{
MinSoundRange = element.GetAttributeFloat("minsoundrange", MinSoundRange);
}
MaxSightRange = element.GetAttributeFloat("maxsightrange", MaxSightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", MaxSoundRange);
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
MinSightRange = element.GetAttributeFloat("minsightrange", SightRange);
MinSoundRange = element.GetAttributeFloat("minsoundrange", SoundRange);
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
SonarLabel = element.GetAttributeString("sonarlabel", "");
}
@@ -88,9 +88,7 @@ namespace Barotrauma
private AITargetMemory selectedTargetMemory;
private float targetValue;
private float eatTimer;
private Dictionary<AITarget, AITargetMemory> targetMemories;
//the eyesight of the NPC (0.0 = blind, 1.0 = sees every target within sightRange)
@@ -250,7 +248,7 @@ namespace Barotrauma
public override void SelectTarget(AITarget target)
{
selectedAiTarget = target;
SelectedAiTarget = target;
selectedTargetMemory = FindTargetMemory(target);
targetValue = 100.0f;
@@ -292,7 +290,7 @@ namespace Barotrauma
UpdateTargets(Character, out targetingPriority);
updateTargetsTimer = UpdateTargetsInterval;
if (selectedAiTarget == null)
if (SelectedAiTarget == null)
{
State = AIState.Idle;
}
@@ -308,7 +306,7 @@ namespace Barotrauma
latchOntoAI?.Update(this, deltaTime);
if (selectedAiTarget != null && (selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed))
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
{
State = AIState.Idle;
return;
@@ -368,9 +366,9 @@ namespace Barotrauma
if (wallTarget != null) return;
if (selectedAiTarget != null)
if (SelectedAiTarget != null)
{
Vector2 targetSimPos = Character.Submarine == null ? ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;
Vector2 targetSimPos = Character.Submarine == null ? ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition) : SelectedAiTarget.SimPosition;
steeringManager.SteeringAvoid(deltaTime, colliderSize * 3.0f, speed);
steeringManager.SteeringSeek(targetSimPos, speed);
@@ -392,13 +390,13 @@ namespace Barotrauma
private void UpdateEscape(float deltaTime)
{
if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
if (SelectedAiTarget == null || SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)
{
State = AIState.Idle;
return;
}
Vector2 escapeDir = Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition);
Vector2 escapeDir = Vector2.Normalize(SimPosition - SelectedAiTarget.SimPosition);
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
float speed = Character.AnimController.GetCurrentSpeed(useMaxSpeed: true);
SteeringManager.SteeringManual(deltaTime, escapeDir * speed);
@@ -415,7 +413,7 @@ namespace Barotrauma
private void UpdateAttack(float deltaTime)
{
if (selectedAiTarget == null)
if (SelectedAiTarget == null)
{
State = AIState.Idle;
return;
@@ -425,23 +423,23 @@ namespace Barotrauma
selectedTargetMemory.Priority -= deltaTime * 0.1f;
Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;
Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition) : SelectedAiTarget.SimPosition;
if (Character.Submarine != null && selectedAiTarget.Entity.Submarine != null && Character.Submarine != selectedAiTarget.Entity.Submarine)
if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine != null && Character.Submarine != SelectedAiTarget.Entity.Submarine)
{
attackSimPosition = ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition - Character.Submarine.Position);
attackSimPosition = ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition - Character.Submarine.Position);
}
if (selectedAiTarget.Entity is Item item)
if (SelectedAiTarget.Entity is Item item)
{
// If the item is held by a character, attack the character instead.
var pickable = item.components.Select(c => c as Pickable).FirstOrDefault();
var pickable = item.GetComponent<Pickable>();
if (pickable != null)
{
var target = pickable.Picker?.AiTarget;
if (target != null)
{
selectedAiTarget = target;
SelectedAiTarget = target;
}
}
}
@@ -449,13 +447,13 @@ namespace Barotrauma
if (wallTarget != null)
{
attackSimPosition = ConvertUnits.ToSimUnits(wallTarget.Position);
if (Character.Submarine == null && selectedAiTarget.Entity?.Submarine != null) attackSimPosition += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
if (Character.Submarine == null && SelectedAiTarget.Entity?.Submarine != null) attackSimPosition += ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
}
else if (selectedAiTarget.Entity is Character)
else if (SelectedAiTarget.Entity is Character)
{
//target the closest limb if the target is a character
float closestDist = Vector2.DistanceSquared(selectedAiTarget.SimPosition, SimPosition) * 10.0f;
foreach (Limb limb in ((Character)selectedAiTarget.Entity).AnimController.Limbs)
float closestDist = Vector2.DistanceSquared(SelectedAiTarget.SimPosition, SimPosition) * 10.0f;
foreach (Limb limb in ((Character)SelectedAiTarget.Entity).AnimController.Limbs)
{
if (limb == null) continue;
float dist = Vector2.DistanceSquared(limb.SimPosition, SimPosition) / Math.Max(limb.AttackPriority, 0.1f);
@@ -514,9 +512,9 @@ namespace Barotrauma
return;
}
}
else if (selectedAiTarget.Entity is Item)
else if (SelectedAiTarget.Entity is Item)
{
var door = ((Item)selectedAiTarget.Entity).GetComponent<Door>();
var door = ((Item)SelectedAiTarget.Entity).GetComponent<Door>();
//steer through the door manually if it's open or broken
if (door?.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom && (door.IsOpen || door.Item.Condition <= 0.0f))
{
@@ -566,24 +564,39 @@ namespace Barotrauma
{
if (attackingLimb.attack.SecondaryCoolDownTimer <= 0)
{
// If the secondary cooldown is defined and expired, check if we can switch the attack
var previousLimb = attackingLimb;
var newLimb = GetAttackLimb(attackSimPosition, previousLimb);
if (newLimb != null)
// Don't allow attacking when the attack target has changed.
if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget)
{
attackingLimb = newLimb;
canAttack = false;
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack)
{
// Fall back if cannot attack.
UpdateFallBack(attackSimPosition, deltaTime);
return;
}
attackingLimb = null;
}
else
{
// No new limb was found.
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
// If the secondary cooldown is defined and expired, check if we can switch the attack
var previousLimb = attackingLimb;
var newLimb = GetAttackLimb(attackSimPosition, previousLimb);
if (newLimb != null)
{
canAttack = false;
attackingLimb = newLimb;
}
else
{
UpdateFallBack(attackSimPosition, deltaTime);
return;
// No new limb was found.
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
{
canAttack = false;
}
else
{
UpdateFallBack(attackSimPosition, deltaTime);
return;
}
}
}
}
@@ -648,12 +661,12 @@ namespace Barotrauma
else if (indoorsSteering.CurrentPath.CurrentNode?.ConnectedDoor != null)
{
wallTarget = null;
selectedAiTarget = indoorsSteering.CurrentPath.CurrentNode.ConnectedDoor.Item.AiTarget;
SelectedAiTarget = indoorsSteering.CurrentPath.CurrentNode.ConnectedDoor.Item.AiTarget;
}
else if (indoorsSteering.CurrentPath.NextNode?.ConnectedDoor != null)
{
wallTarget = null;
selectedAiTarget = indoorsSteering.CurrentPath.NextNode.ConnectedDoor.Item.AiTarget;
SelectedAiTarget = indoorsSteering.CurrentPath.NextNode.ConnectedDoor.Item.AiTarget;
}
}
}
@@ -668,7 +681,7 @@ namespace Barotrauma
private Limb GetAttackLimb(Vector2 attackSimPosition, Limb ignoredLimb = null)
{
AttackContext currentContext = Character.GetAttackContext();
var target = wallTarget != null ? wallTarget.Structure : selectedAiTarget.Entity;
var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity;
var limbs = Character.AnimController.Limbs
.Where(l =>
l != ignoredLimb &&
@@ -677,7 +690,7 @@ namespace Barotrauma
!l.IsStuck &&
l.attack.IsValidContext(currentContext) &&
l.attack.IsValidTarget(target) &&
l.attack.Conditionals.All(c => (target is ISerializableEntity se && c.Matches(se)) || !(target is ISerializableEntity)))
l.attack.Conditionals.All(c => (target is ISerializableEntity se && c.Matches(se)) || !(target is ISerializableEntity) || !(target is Character)))
.OrderByDescending(l => l.attack.Priority)
.ThenBy(l => ConvertUnits.ToDisplayUnits(Vector2.Distance(l.SimPosition, attackSimPosition)));
// TODO: priority should probably not override the distance -> use values instead of booleans
@@ -695,11 +708,11 @@ namespace Barotrauma
//check if there's a wall between the target and the Character
Vector2 rayStart = Character.SimPosition;
Vector2 rayEnd = selectedAiTarget.SimPosition;
Vector2 rayEnd = SelectedAiTarget.SimPosition;
if (selectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
if (SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
{
rayStart -= ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
rayStart -= ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
}
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd);
@@ -795,7 +808,7 @@ namespace Barotrauma
private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition, float distance = -1)
{
var damageTarget = wallTarget != null ? wallTarget.Structure : selectedAiTarget.Entity as IDamageable;
var damageTarget = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity as IDamageable;
if (damageTarget == null) return;
float prevHealth = damageTarget.Health;
@@ -834,7 +847,7 @@ namespace Barotrauma
private void UpdateEating(float deltaTime)
{
if (selectedAiTarget == null)
if (SelectedAiTarget == null)
{
State = AIState.Idle;
return;
@@ -850,13 +863,13 @@ namespace Barotrauma
}
Vector2 mouthPos = Character.AnimController.GetMouthPosition().Value;
Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;
Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(SelectedAiTarget.WorldPosition) : SelectedAiTarget.SimPosition;
Vector2 limbDiff = attackSimPosition - mouthPos;
float limbDist = limbDiff.Length();
if (limbDist < 2.0f)
{
Character.SelectCharacter(selectedAiTarget.Entity as Character);
Character.SelectCharacter(SelectedAiTarget.Entity as Character);
steeringManager.SteeringManual(deltaTime, limbDiff);
Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
}
@@ -875,10 +888,8 @@ namespace Barotrauma
//sight/hearing range
public void UpdateTargets(Character character, out TargetingPriority targetingPriority)
{
var prevAiTarget = selectedAiTarget;
targetingPriority = null;
selectedAiTarget = null;
SelectedAiTarget = null;
selectedTargetMemory = null;
targetValue = 0.0f;
@@ -936,11 +947,9 @@ namespace Barotrauma
//skip the target if it's a room and the character is already inside a sub
if (character.CurrentHull != null && target.Entity is Hull) continue;
//multiply the priority of the target if it's a door from outside to inside and the AI is an aggressive boarder
Door door = null;
if (target.Entity is Item item)
{
//item inside and we're outside -> attack the hull
if (item.CurrentHull != null && character.CurrentHull == null)
{
@@ -963,21 +972,20 @@ namespace Barotrauma
}
if (door != null)
{
{
//increase priority if the character is outside and an aggressive boarder, and the door is from outside to inside
if (character.CurrentHull == null && aggressiveBoarding && !door.LinkedGap.IsRoomToRoom)
{
valueModifier = door.IsOpen ? 5.0f : 3.0f;
valueModifier = door.IsOpen ? 10 : 5;
}
else if (door.IsOpen || door.Item.Condition <= 0.0f) //ignore broken and open doors
{
continue;
}
}
else
else if (target.Entity is IDamageable targetDamageable && targetDamageable.Health <= 0.0f)
{
IDamageable targetDamageable = target.Entity as IDamageable;
if (targetDamageable != null && targetDamageable.Health <= 0.0f) continue;
continue;
}
}
@@ -988,7 +996,8 @@ namespace Barotrauma
if (valueModifier == 0.0f) continue;
dist = Vector2.Distance(character.WorldPosition, target.WorldPosition);
Vector2 toTarget = target.WorldPosition - character.WorldPosition;
dist = toTarget.Length();
//if the target has been within range earlier, the character will notice it more easily
//(i.e. remember where the target was)
@@ -1003,21 +1012,27 @@ namespace Barotrauma
dist = Math.Max(dist, 100.0f);
AITargetMemory targetMemory = FindTargetMemory(target);
if (Character.CurrentHull != null && Math.Abs(toTarget.Y) > Character.CurrentHull.Size.Y)
{
// Inside the sub, treat objects that are up or down, as they were farther away.
dist *= 3;
}
valueModifier = valueModifier * targetMemory.Priority / (float)Math.Sqrt(dist);
if (valueModifier > targetValue)
{
selectedAiTarget = target;
SelectedAiTarget = target;
selectedTargetMemory = targetMemory;
targetingPriority = targetingPriorities[targetingTag];
targetValue = valueModifier;
}
}
if (selectedAiTarget != prevAiTarget)
if (SelectedAiTarget != _previousAiTarget)
{
wallTarget = null;
}
}
_previousAiTarget = SelectedAiTarget;
}
//find the targetMemory that corresponds to some AItarget or create if there isn't one yet
@@ -57,7 +57,7 @@ namespace Barotrauma
{
if (DisableCrewAI || Character.IsUnconscious) return;
if (Character.Submarine != null || selectedAiTarget?.Entity?.Submarine != null)
if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null)
{
if (steeringManager != insideSteering) insideSteering.Reset();
steeringManager = insideSteering;
@@ -122,9 +122,11 @@ namespace Barotrauma
//apply speed multiplier if
// a. it's boosting the movement speed and the character is trying to move fast (= running)
// b. it's a debuff that decreases movement speed
if (run || Character.SpeedMultiplier <= 0.0f) targetMovement *= Character.SpeedMultiplier;
Character.SpeedMultiplier = 1.0f; // Reset, items will set the value before the next update
float speedMultiplier = Character.SpeedMultiplier;
if (run || speedMultiplier <= 0.0f) targetMovement *= speedMultiplier;
Character.ResetSpeedMultiplier(); // Reset, items will set the value before the next update
Character.AnimController.TargetMovement = targetMovement;
}
@@ -225,6 +227,8 @@ namespace Barotrauma
}
}
partial void ReportProblems();
private void UpdateSpeaking()
{
if (Character.Oxygen < 20.0f)
@@ -277,7 +281,23 @@ namespace Barotrauma
public override void SelectTarget(AITarget target)
{
selectedAiTarget = target;
SelectedAiTarget = target;
}
private void CheckCrouching(float deltaTime)
{
crouchRaycastTimer -= deltaTime;
if (crouchRaycastTimer > 0.0f) return;
crouchRaycastTimer = CrouchRaycastInterval;
//start the raycast in front of the character in the direction it's heading to
Vector2 startPos = Character.SimPosition;
startPos.X += MathHelper.Clamp(Character.AnimController.TargetMovement.X, -1.0f, 1.0f);
//do a raycast upwards to find any walls
float minCeilingDist = Character.AnimController.Collider.height / 2 + Character.AnimController.Collider.radius + 0.1f;
shouldCrouch = Submarine.PickBody(startPos, startPos + Vector2.UnitY * minCeilingDist, null, Physics.CollisionWall) != null;
}
private void CheckCrouching(float deltaTime)
@@ -18,6 +18,10 @@ namespace Barotrauma
private float findPathTimer;
private float buttonPressCooldown;
const float ButtonPressInterval = 0.5f;
public SteeringPath CurrentPath
{
get { return currentPath; }
@@ -57,6 +61,7 @@ namespace Barotrauma
{
base.Update(speed);
buttonPressCooldown -= 1.0f / 60.0f;
findPathTimer -= 1.0f / 60.0f;
}
@@ -127,7 +132,7 @@ namespace Barotrauma
return currentTarget - pos2;
}
if (canOpenDoors && !character.LockHands)
if (canOpenDoors && !character.LockHands && buttonPressCooldown <= 0.0f)
{
CheckDoorsInPath();
}
@@ -311,6 +316,7 @@ namespace Barotrauma
}
closestButton.Item.TryInteract(character, false, true, false);
buttonPressCooldown = ButtonPressInterval;
break;
}
}
@@ -236,7 +236,11 @@ namespace Barotrauma
foreach (Character potentialSpeaker in availableSpeakers)
{
//check if the character has an appropriate job to say the line
if (selectedConversation.AllowedJobs.Count > 0 && !selectedConversation.AllowedJobs.Contains(potentialSpeaker.Info?.Job.Prefab)) continue;
if ((potentialSpeaker.Info?.Job != null && potentialSpeaker.Info.Job.Prefab.OnlyJobSpecificDialog) ||
selectedConversation.AllowedJobs.Count > 0)
{
if (!selectedConversation.AllowedJobs.Contains(potentialSpeaker.Info?.Job.Prefab)) continue;
}
//check if the character has all required flags to say the line
if (!ignoreFlags)
@@ -310,6 +314,81 @@ namespace Barotrauma
return 1.0f - 1.0f / (index + 1);
}
#if DEBUG
public static void WriteToCSV()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < list.Count; i++)
{
NPCConversation current = list[i];
WriteConversation(sb, current, 0);
WriteSubConversations(sb, current.Responses, 1);
WriteEmptyRow(sb);
}
StreamWriter file = new StreamWriter(@"NPCConversations.csv");
file.WriteLine(sb.ToString());
file.Close();
}
private static void WriteConversation(System.Text.StringBuilder sb, NPCConversation conv, int depthIndex)
{
sb.Append(conv.speakerIndex); // Speaker index
sb.Append('*');
sb.Append(depthIndex); // Depth index
sb.Append('*');
sb.Append(conv.Line); // Original
sb.Append('*');
// Translated
sb.Append('*');
sb.Append(string.Join(",", conv.Flags)); // Flags
sb.Append('*');
for (int i = 0; i < conv.AllowedJobs.Count; i++) // Jobs
{
sb.Append(conv.AllowedJobs[i].Identifier);
if (i < conv.AllowedJobs.Count - 1)
{
sb.Append(",");
}
}
sb.Append('*');
sb.Append(string.Join(",", conv.allowedSpeakerTags)); // Traits
sb.Append('*');
sb.Append(conv.minIntensity); // Minimum intensity
sb.Append('*');
sb.Append(conv.maxIntensity); // Maximum intensity
sb.Append('*');
// Comments
sb.AppendLine();
}
private static void WriteSubConversations(System.Text.StringBuilder sb, List<NPCConversation> responses, int depthIndex)
{
for (int i = 0; i < responses.Count; i++)
{
WriteConversation(sb, responses[i], depthIndex);
if (responses[i].Responses != null && responses[i].Responses.Count > 0)
{
WriteSubConversations(sb, responses[i].Responses, depthIndex + 1);
}
}
}
private static void WriteEmptyRow(System.Text.StringBuilder sb)
{
for (int i = 0; i < 8; i++)
{
sb.Append('*');
}
sb.AppendLine();
}
#endif
}
}
@@ -60,10 +60,10 @@ namespace Barotrauma
weapon.GetComponent<RepairTool>() as ItemComponent;
if (weaponComponent != null && weaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
{
Item[] containedItems = weapon.ContainedItems;
var containedItems = weapon.ContainedItems;
foreach (RelatedItem requiredItem in weaponComponent.requiredItems[RelatedItem.RelationType.Contained])
{
Item containedItem = Array.Find(containedItems, it => it != null && it.Condition > 0.0f && requiredItem.MatchesItem(it));
Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
if (containedItem == null)
{
var newReloadWeaponObjective = new AIObjectiveContainItem(character, requiredItem.Identifiers, weapon.GetComponent<ItemContainer>());
@@ -1,7 +1,6 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace Barotrauma
{
@@ -1,5 +1,6 @@
using Barotrauma.Items.Components;
using System;
using System.Linq;
namespace Barotrauma
{
@@ -19,7 +20,7 @@ namespace Barotrauma
var containedItems = character.Inventory.Items[i].ContainedItems;
if (containedItems == null) continue;
var oxygenTank = Array.Find(containedItems, it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f);
var oxygenTank = containedItems.FirstOrDefault(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f);
if (oxygenTank != null) return true;
}
}
@@ -2,6 +2,7 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace Barotrauma
{
@@ -60,7 +61,7 @@ namespace Barotrauma
var containedItems = weldingTool.ContainedItems;
if (containedItems == null) return;
var fuelTank = Array.Find(containedItems, i => i.HasTag("weldingfueltank") && i.Condition > 0.0f);
var fuelTank = containedItems.FirstOrDefault(i => i.HasTag("weldingfueltank") && i.Condition > 0.0f);
if (fuelTank == null)
{
AddSubObjective(new AIObjectiveContainItem(character, "weldingfueltank", weldingTool.GetComponent<ItemContainer>()));
@@ -108,16 +108,20 @@ namespace Barotrauma
//not linked to a hull -> ignore
if (gap.linkedTo.All(l => l == null)) { continue; }
if (character.TeamID == 0)
if (character.TeamID == Character.TeamType.None)
{
//TODO: make sure the gap isn't in another sub (outpost, respawn shuttle...?)
if (gap.Submarine == null) continue;
}
else
{
//prevent characters from attempting to fix leaks in the enemy sub
//team 1 plays in sub 0, team 2 in sub 1
Submarine mySub = character.TeamID < 1 || character.TeamID > Submarine.MainSubs.Length ?
Submarine.MainSub : Submarine.MainSubs[character.TeamID - 1];
Submarine mySub = Submarine.MainSub;
if (character.TeamID == Character.TeamType.Team2 && Submarine.MainSubs.Length > 1)
{
mySub = Submarine.MainSubs[1];
}
if (gap.Submarine != mySub) continue;
}
@@ -24,8 +24,9 @@ namespace Barotrauma
public override float GetPriority(AIObjectiveManager objectiveManager)
{
if (Target != null && Target.Removed) return 0.0f;
if (IgnoreIfTargetDead && Target is Character character && character.IsDead) return 0.0f;
if (FollowControlledCharacter && Character.Controlled == null) { return 0.0f; }
if (Target != null && Target.Removed) { return 0.0f; }
if (IgnoreIfTargetDead && Target is Character character && character.IsDead) { return 0.0f; }
if (objectiveManager.CurrentOrder == this)
{
@@ -39,15 +40,17 @@ namespace Barotrauma
{
get
{
if (Target != null && Target.Removed) return false;
if (FollowControlledCharacter && Character.Controlled == null) { return false; }
if (repeat || waitUntilPathUnreachable > 0.0f) return true;
if (Target != null && Target.Removed) { return false; }
if (repeat || waitUntilPathUnreachable > 0.0f) { return true; }
var pathSteering = character.AIController.SteeringManager as IndoorsSteeringManager;
//path doesn't exist (= hasn't been searched for yet), assume for now that the target is reachable
if (pathSteering?.CurrentPath == null) return true;
if (pathSteering?.CurrentPath == null) { return true; }
if (!AllowGoingOutside && pathSteering.CurrentPath.HasOutdoorsNodes) return false;
if (!AllowGoingOutside && pathSteering.CurrentPath.HasOutdoorsNodes) { return false; }
return !pathSteering.CurrentPath.Unreachable;
}
@@ -55,6 +58,8 @@ namespace Barotrauma
public Entity Target { get; private set; }
public bool FollowControlledCharacter;
public AIObjectiveGoTo(Entity target, Character character, bool repeat = false, bool getDivingGearIfNeeded = true)
: base (character, "")
{
@@ -78,12 +83,18 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
if (FollowControlledCharacter)
{
if (Character.Controlled == null) { return; }
Target = Character.Controlled;
}
if (Target == character)
{
character.AIController.SteeringManager.Reset();
return;
}
waitUntilPathUnreachable -= deltaTime;
if (character.SelectedConstruction != null && character.SelectedConstruction.GetComponent<Ladder>() == null)
@@ -132,7 +132,8 @@ namespace Barotrauma
{
CloseEnough = 1.5f,
AllowGoingOutside = true,
IgnoreIfTargetDead = true
IgnoreIfTargetDead = true,
FollowControlledCharacter = orderGiver == character
};
break;
case "wait":
@@ -28,7 +28,7 @@ namespace Barotrauma
if (!repairablesFound) { return 0.0f; }
float priority = 100.0f - item.Condition;
float priority = item.Prefab.Health - item.Condition;
//vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
float dist =
Math.Abs(character.WorldPosition.X - item.WorldPosition.X) +
@@ -48,7 +48,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList)
{
//ignore items that are in full condition
if (item.Condition >= 100.0f) continue;
if (item.Condition >= item.Prefab.Health) continue;
foreach (Repairable repairable in item.Repairables)
{
//ignore ones that are already fixed
@@ -194,7 +194,7 @@ namespace Barotrauma
var targetLimb = targetCharacter.CharacterHealth.GetAfflictionLimb(affliction);
bool remove = false;
foreach (ItemComponent ic in item.components)
foreach (ItemComponent ic in item.Components)
{
if (!ic.HasRequiredContainedItems(addMessage: false)) continue;
#if CLIENT
@@ -161,16 +161,16 @@ namespace Barotrauma
return false;
}
public string GetChatMessage(string targetCharacterName, string targetRoomName, string orderOption = "")
public string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, string orderOption = "")
{
orderOption = orderOption ?? "";
string messageTag = "OrderDialog." + AITag;
string messageTag = (givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf." : "OrderDialog.") + AITag;
if (!string.IsNullOrEmpty(orderOption)) messageTag += "." + orderOption;
string msg = TextManager.Get(messageTag, true);
if (msg == null) return "";
if (targetCharacterName == null) targetCharacterName = "";
if (targetRoomName == null) targetRoomName = "";
return msg.Replace("[name]", targetCharacterName).Replace("[roomname]", targetRoomName);