Faction Test v1.0.1.0
This commit is contained in:
@@ -59,8 +59,9 @@ namespace Barotrauma.Items.Components
|
||||
private float aiTargetingGraceTimer;
|
||||
|
||||
private float aiFindTargetTimer;
|
||||
private Character currentTarget;
|
||||
const float aiFindTargetInterval = 5.0f;
|
||||
private ISpatialEntity currentTarget;
|
||||
private const float CrewAiFindTargetMaxInterval = 3.0f;
|
||||
private const float CrewAIFindTargetMinInverval = 0.2f;
|
||||
|
||||
private int currentLoaderIndex;
|
||||
|
||||
@@ -73,6 +74,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private List<LightComponent> lightComponents;
|
||||
|
||||
private readonly bool isSlowTurret;
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
@@ -320,33 +323,36 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.Yes, description:"Should the turret operate automatically using AI targeting? Comes with some optional random movement that can be adjusted below."), Editable]
|
||||
public bool AutoOperate { get; set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "[Auto Operate] How much the turret should adjust the aim off the target randomly instead of tracking the target perfectly?"), Editable]
|
||||
public float RandomAimAmount { get; private set; }
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "[Auto Operate] How much the turret should adjust the aim off the target randomly instead of tracking the target perfectly? In Degrees."), Editable]
|
||||
public float RandomAimAmount { get; set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "[Auto Operate] How often the turret should adjust the aim randomly instead of tracking the target perfectly?"), Editable]
|
||||
public float RandomAimMinTime { get; private set; }
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "[Auto Operate] How often the turret should adjust the aim randomly instead of tracking the target perfectly? Minimum wait time, in seconds."), Editable]
|
||||
public float RandomAimMinTime { get; set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "[Auto Operate] How often the turret should adjust the aim randomly instead of tracking the target perfectly?"), Editable]
|
||||
public float RandomAimMaxTime { get; private set; }
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "[Auto Operate] How often the turret should adjust the aim randomly instead of tracking the target perfectly? Maximum wait time, in seconds."), Editable]
|
||||
public float RandomAimMaxTime { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret move randomly while idle?"), Editable]
|
||||
public bool RandomMovement { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret always aim at targets without delay?"), Editable]
|
||||
public bool IgnoreAimDelay { get; set; }
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret have a delay while targeting targets or always aim prefectly?"), Editable]
|
||||
public bool AimDelay { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target characters?"), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target characters in general?"), Editable]
|
||||
public bool TargetCharacters { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target monsters?"), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target all monsters?"), Editable]
|
||||
public bool TargetMonsters { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target humans (or pets)"), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target all humans (or creatures in the same group, like pets)?"), Editable]
|
||||
public bool TargetHumans { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target other submarines?"), Editable]
|
||||
public bool TargetSubmarines { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "[Auto Operate] Should the turret target items?"), Editable]
|
||||
public bool TargetItems { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "[Auto Operate] Group or SpeciesName that the AI ignores when the turret is operated automatically."), Editable]
|
||||
public Identifier FriendlyTag { get; private set; }
|
||||
|
||||
@@ -379,6 +385,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
item.IsShootable = true;
|
||||
item.RequireAimToUse = false;
|
||||
isSlowTurret = item.HasTag("slowturret");
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
@@ -940,7 +947,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private float waitTimer;
|
||||
private float disorderTimer;
|
||||
private float randomAimTimer;
|
||||
|
||||
private float prevTargetRotation;
|
||||
private float updateTimer;
|
||||
@@ -950,10 +957,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
bool targetCharacters = TargetCharacters || TargetHumans || TargetMonsters;
|
||||
bool targetHumans = TargetCharacters && TargetHumans;
|
||||
bool targetMonsters = TargetCharacters && TargetMonsters;
|
||||
|
||||
if (friendlyTag.IsEmpty)
|
||||
{
|
||||
friendlyTag = FriendlyTag;
|
||||
@@ -977,7 +980,7 @@ namespace Barotrauma.Items.Components
|
||||
updateTimer -= deltaTime;
|
||||
}
|
||||
|
||||
if (!IgnoreAimDelay && waitTimer > 0)
|
||||
if (AimDelay && waitTimer > 0)
|
||||
{
|
||||
waitTimer -= deltaTime;
|
||||
return;
|
||||
@@ -987,30 +990,34 @@ namespace Barotrauma.Items.Components
|
||||
float shootDistance = AIRange;
|
||||
ISpatialEntity target = null;
|
||||
float closestDist = shootDistance * shootDistance;
|
||||
if (targetCharacters)
|
||||
if (TargetCharacters)
|
||||
{
|
||||
foreach (var character in Character.CharacterList)
|
||||
{
|
||||
if (character == null || character.Removed || character.IsDead) { continue; }
|
||||
if (!friendlyTag.IsEmpty && (character.SpeciesName.Equals(friendlyTag) || character.Group.Equals(friendlyTag))) { continue; }
|
||||
bool isHuman = character.IsHuman || character.Group == CharacterPrefab.HumanSpeciesName;
|
||||
if (isHuman)
|
||||
{
|
||||
if (!targetHumans)
|
||||
{
|
||||
// Don't target humans if not defined to.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!targetMonsters)
|
||||
{
|
||||
// Don't target other creatures if not defined to.
|
||||
continue;
|
||||
}
|
||||
if (!IsValidTarget(character)) { continue; }
|
||||
float priority = isSlowTurret ? character.Params.AISlowTurretPriority : character.Params.AITurretPriority;
|
||||
if (priority <= 0) { continue; }
|
||||
if (!IsValidTargetForAutoOperate(character, friendlyTag)) { continue; }
|
||||
float dist = Vector2.DistanceSquared(character.WorldPosition, item.WorldPosition);
|
||||
if (dist > closestDist) { continue; }
|
||||
if (!CheckTurretAngle(character.WorldPosition)) { continue; }
|
||||
target = character;
|
||||
closestDist = dist;
|
||||
closestDist = dist / priority;
|
||||
}
|
||||
}
|
||||
if (TargetItems)
|
||||
{
|
||||
foreach (Item targetItem in Item.ItemList)
|
||||
{
|
||||
if (!IsValidTarget(targetItem)) { continue; }
|
||||
float priority = isSlowTurret ? targetItem.Prefab.AISlowTurretPriority : targetItem.Prefab.AITurretPriority;
|
||||
if (priority <= 0) { continue; }
|
||||
float dist = Vector2.DistanceSquared(item.WorldPosition, targetItem.WorldPosition);
|
||||
if (dist > closestDist) { continue; }
|
||||
if (dist > shootDistance * shootDistance) { continue; }
|
||||
if (!CheckTurretAngle(targetItem.WorldPosition)) { continue; }
|
||||
target = targetItem;
|
||||
closestDist = dist / priority;
|
||||
}
|
||||
}
|
||||
if (TargetSubmarines)
|
||||
@@ -1020,8 +1027,11 @@ namespace Barotrauma.Items.Components
|
||||
closestDist = maxDistance * maxDistance;
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (sub == Item.Submarine) { continue; }
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
if (Character.IsOnFriendlyTeam(item.Submarine.TeamID, sub.TeamID)) { continue; }
|
||||
}
|
||||
float dist = Vector2.DistanceSquared(sub.WorldPosition, item.WorldPosition);
|
||||
if (dist > closestDist) { continue; }
|
||||
closestSub = sub;
|
||||
@@ -1035,6 +1045,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!closestSub.IsEntityFoundOnThisSub(hull, true)) { continue; }
|
||||
float dist = Vector2.DistanceSquared(hull.WorldPosition, item.WorldPosition);
|
||||
if (dist > closestDist) { continue; }
|
||||
// Don't check the angle, because it doesn't work on Thalamus spike. The angle check wouldn't be very important here anyway.
|
||||
target = hull;
|
||||
closestDist = dist;
|
||||
}
|
||||
@@ -1051,22 +1062,23 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IgnoreAimDelay)
|
||||
if (AimDelay)
|
||||
{
|
||||
if (RandomAimAmount > 0)
|
||||
{
|
||||
if (disorderTimer < 0)
|
||||
if (randomAimTimer < 0)
|
||||
{
|
||||
// Random disorder
|
||||
disorderTimer = Rand.Range(RandomAimMinTime, RandomAimMaxTime);
|
||||
// Random disorder or other flaw in the targeting.
|
||||
randomAimTimer = Rand.Range(RandomAimMinTime, RandomAimMaxTime);
|
||||
waitTimer = Rand.Range(0.25f, 1f);
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(targetRotation += Rand.Range(-RandomAimAmount, RandomAimAmount));
|
||||
float randomAim = MathHelper.ToRadians(RandomAimAmount);
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(targetRotation += Rand.Range(-randomAim, randomAim));
|
||||
updatePending = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
disorderTimer -= deltaTime;
|
||||
randomAimTimer -= deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1264,18 +1276,19 @@ namespace Barotrauma.Items.Components
|
||||
bool hadCurrentTarget = currentTarget != null;
|
||||
if (hadCurrentTarget)
|
||||
{
|
||||
if (currentTarget.Removed || currentTarget.IsDead)
|
||||
if (!IsValidTarget(currentTarget))
|
||||
{
|
||||
currentTarget = null;
|
||||
aiFindTargetTimer = CrewAIFindTargetMinInverval;
|
||||
}
|
||||
}
|
||||
|
||||
if (aiFindTargetTimer <= 0.0f || currentTarget == null)
|
||||
if (aiFindTargetTimer <= 0.0f)
|
||||
{
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
// Ignore dead, friendly, and those that are inside the same sub
|
||||
if (enemy.IsDead || !enemy.Enabled) { continue; }
|
||||
if (!IsValidTarget(enemy)) { continue; }
|
||||
float priority = isSlowTurret ? enemy.Params.AISlowTurretPriority : enemy.Params.AITurretPriority;
|
||||
if (priority <= 0) { continue; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (enemy.Submarine == character.Submarine) { continue; }
|
||||
@@ -1292,30 +1305,53 @@ namespace Barotrauma.Items.Components
|
||||
// We shouldn't check the angle when a long creature is traveling outside of the shooting range, because doing so would not allow us to shoot the limbs that might be close enough to shoot at.
|
||||
if (!CheckTurretAngle(enemy.WorldPosition)) { continue; }
|
||||
}
|
||||
targetPos = enemy.WorldPosition;
|
||||
closestEnemy = enemy;
|
||||
closestDistance = dist;
|
||||
closestDistance = dist / priority;
|
||||
currentTarget = closestEnemy;
|
||||
}
|
||||
currentTarget = closestEnemy;
|
||||
aiFindTargetTimer = aiFindTargetInterval;
|
||||
}
|
||||
else
|
||||
{
|
||||
closestEnemy = currentTarget;
|
||||
}
|
||||
|
||||
if (closestEnemy != null)
|
||||
{
|
||||
targetPos = closestEnemy.WorldPosition;
|
||||
//if the enemy is inside another sub, aim at the room they're in to make it less obvious that the enemy "knows" exactly where the target is
|
||||
if (closestEnemy.Submarine != null && closestEnemy.CurrentHull != null && closestEnemy.Submarine != item.Submarine && !closestEnemy.CanSeeTarget(Item))
|
||||
foreach (Item targetItem in Item.ItemList)
|
||||
{
|
||||
targetPos = closestEnemy.CurrentHull.WorldPosition;
|
||||
if (!IsValidTarget(targetItem)) { continue; }
|
||||
float priority = isSlowTurret ? targetItem.Prefab.AISlowTurretPriority : targetItem.Prefab.AITurretPriority;
|
||||
if (priority <= 0) { continue; }
|
||||
float dist = Vector2.DistanceSquared(item.WorldPosition, targetItem.WorldPosition);
|
||||
if (dist > closestDistance) { continue; }
|
||||
if (dist > shootDistance * shootDistance) { continue; }
|
||||
if (!CheckTurretAngle(targetItem.WorldPosition)) { continue; }
|
||||
targetPos = targetItem.WorldPosition;
|
||||
closestDistance = dist / priority;
|
||||
// Override the target character so that we can target the item instead.
|
||||
closestEnemy = null;
|
||||
currentTarget = targetItem;
|
||||
}
|
||||
if (currentTarget == null)
|
||||
{
|
||||
aiFindTargetTimer = CrewAIFindTargetMinInverval;
|
||||
}
|
||||
else
|
||||
{
|
||||
aiFindTargetTimer = CrewAiFindTargetMaxInterval;
|
||||
}
|
||||
}
|
||||
else if (currentTarget != null)
|
||||
{
|
||||
targetPos = currentTarget.WorldPosition;
|
||||
}
|
||||
bool iceSpireSpotted = false;
|
||||
// Adjust the target character position (limb or submarine)
|
||||
if (currentTarget is Character targetCharacter)
|
||||
{
|
||||
//if the enemy is inside another sub, aim at the room they're in to make it less obvious that the enemy "knows" exactly where the target is
|
||||
if (targetCharacter.Submarine != null && targetCharacter.CurrentHull != null && targetCharacter.Submarine != item.Submarine && !targetCharacter.CanSeeTarget(Item))
|
||||
{
|
||||
targetPos = targetCharacter.CurrentHull.WorldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Target the closest limb. Doesn't make much difference with smaller creatures, but enables the bots to shoot longer abyss creatures like the endworm. Otherwise they just target the main body = head.
|
||||
float closestDist = closestDistance;
|
||||
foreach (Limb limb in closestEnemy.AnimController.Limbs)
|
||||
foreach (Limb limb in targetCharacter.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.Hidden) { continue; }
|
||||
@@ -1329,13 +1365,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (closestDist > shootDistance * shootDistance)
|
||||
{
|
||||
// Not close enough to shoot
|
||||
// Not close enough to shoot.
|
||||
currentTarget = null;
|
||||
closestEnemy = null;
|
||||
targetPos = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (item.Submarine != null && Level.Loaded != null)
|
||||
else if (targetPos == null && item.Submarine != null && Level.Loaded != null)
|
||||
{
|
||||
// Check ice spires
|
||||
shootDistance = AIRange * item.OffsetOnSelectedMultiplier;
|
||||
@@ -1345,50 +1382,49 @@ namespace Barotrauma.Items.Components
|
||||
if (wall is not DestructibleLevelWall destructibleWall || destructibleWall.Destroyed) { continue; }
|
||||
foreach (var cell in wall.Cells)
|
||||
{
|
||||
if (cell.DoesDamage)
|
||||
if (!cell.DoesDamage) { continue; }
|
||||
foreach (var edge in cell.Edges)
|
||||
{
|
||||
foreach (var edge in cell.Edges)
|
||||
Vector2 p1 = edge.Point1 + cell.Translation;
|
||||
Vector2 p2 = edge.Point2 + cell.Translation;
|
||||
Vector2 closestPoint = MathUtils.GetClosestPointOnLineSegment(p1, p2, item.WorldPosition);
|
||||
if (!CheckTurretAngle(closestPoint))
|
||||
{
|
||||
Vector2 p1 = edge.Point1 + cell.Translation;
|
||||
Vector2 p2 = edge.Point2 + cell.Translation;
|
||||
Vector2 closestPoint = MathUtils.GetClosestPointOnLineSegment(p1, p2, item.WorldPosition);
|
||||
if (!CheckTurretAngle(closestPoint))
|
||||
// The closest point can't be targeted -> get a point directly in front of the turret
|
||||
Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
|
||||
if (MathUtils.GetLineIntersection(p1, p2, item.WorldPosition, item.WorldPosition + barrelDir * shootDistance, out Vector2 intersection))
|
||||
{
|
||||
// The closest point can't be targeted -> get a point directly in front of the turret
|
||||
Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
|
||||
if (MathUtils.GetLineIntersection(p1, p2, item.WorldPosition, item.WorldPosition + barrelDir * shootDistance, out Vector2 intersection))
|
||||
{
|
||||
closestPoint = intersection;
|
||||
if (!CheckTurretAngle(closestPoint)) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
closestPoint = intersection;
|
||||
if (!CheckTurretAngle(closestPoint)) { continue; }
|
||||
}
|
||||
float dist = Vector2.Distance(closestPoint, item.WorldPosition);
|
||||
|
||||
//add one px to make sure the visibility raycast doesn't miss the cell due to the end position being right at the edge of the cell
|
||||
closestPoint += (closestPoint - item.WorldPosition) / Math.Max(dist, 1);
|
||||
|
||||
if (dist > AIRange + 1000) { continue; }
|
||||
float dot = 0;
|
||||
if (!MathUtils.NearlyEqual(item.Submarine.Velocity, Vector2.Zero))
|
||||
else
|
||||
{
|
||||
dot = Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), Vector2.Normalize(closestPoint - item.Submarine.WorldPosition));
|
||||
}
|
||||
float minAngle = 0.5f;
|
||||
if (dot < minAngle && dist > 1000)
|
||||
{
|
||||
// The sub is not moving towards the target and it's not very close to the turret either -> ignore
|
||||
continue;
|
||||
}
|
||||
// Allow targeting farther when heading towards the spire (up to 1000 px)
|
||||
dist -= MathHelper.Lerp(0, 1000, MathUtils.InverseLerp(minAngle, 1, dot));
|
||||
if (dist > closestDistance) { continue; }
|
||||
targetPos = closestPoint;
|
||||
closestDistance = dist;
|
||||
}
|
||||
float dist = Vector2.Distance(closestPoint, item.WorldPosition);
|
||||
|
||||
//add one px to make sure the visibility raycast doesn't miss the cell due to the end position being right at the edge of the cell
|
||||
closestPoint += (closestPoint - item.WorldPosition) / Math.Max(dist, 1);
|
||||
|
||||
if (dist > AIRange + 1000) { continue; }
|
||||
float dot = 0;
|
||||
if (!MathUtils.NearlyEqual(item.Submarine.Velocity, Vector2.Zero))
|
||||
{
|
||||
dot = Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), Vector2.Normalize(closestPoint - item.Submarine.WorldPosition));
|
||||
}
|
||||
float minAngle = 0.5f;
|
||||
if (dot < minAngle && dist > 1000)
|
||||
{
|
||||
// The sub is not moving towards the target and it's not very close to the turret either -> ignore
|
||||
continue;
|
||||
}
|
||||
// Allow targeting farther when heading towards the spire (up to 1000 px)
|
||||
dist -= MathHelper.Lerp(0, 1000, MathUtils.InverseLerp(minAngle, 1, dot));
|
||||
if (dist > closestDistance) { continue; }
|
||||
targetPos = closestPoint;
|
||||
closestDistance = dist;
|
||||
iceSpireSpotted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1404,13 +1440,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.AIController.SelectedAiTarget == null && !hadCurrentTarget)
|
||||
{
|
||||
if (CreatureMetrics.Instance.RecentlyEncountered.Contains(closestEnemy.SpeciesName) || closestEnemy.IsHuman)
|
||||
if (CreatureMetrics.RecentlyEncountered.Contains(closestEnemy.SpeciesName) || closestEnemy.IsHuman)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogNewTargetSpotted").Value,
|
||||
identifier: "newtargetspotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
else if (CreatureMetrics.Instance.Encountered.Contains(closestEnemy.SpeciesName))
|
||||
else if (CreatureMetrics.Encountered.Contains(closestEnemy.SpeciesName))
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogIdentifiedTargetSpotted", "[speciesname]", closestEnemy.DisplayName).Value,
|
||||
identifier: "identifiedtargetspotted".ToIdentifier(),
|
||||
@@ -1423,17 +1459,17 @@ namespace Barotrauma.Items.Components
|
||||
minDurationBetweenSimilar: 5.0f);
|
||||
}
|
||||
}
|
||||
else if (!CreatureMetrics.Instance.Encountered.Contains(closestEnemy.SpeciesName))
|
||||
else if (!CreatureMetrics.Encountered.Contains(closestEnemy.SpeciesName))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogUnidentifiedTargetSpotted").Value,
|
||||
identifier: "unidentifiedtargetspotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 5.0f);
|
||||
}
|
||||
character.AddEncounter(closestEnemy);
|
||||
CreatureMetrics.AddEncounter(closestEnemy.SpeciesName);
|
||||
}
|
||||
character.AIController.SelectTarget(closestEnemy.AiTarget);
|
||||
}
|
||||
else if (closestEnemy == null && character.IsOnPlayerTeam)
|
||||
else if (iceSpireSpotted && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogIceSpireSpotted").Value,
|
||||
identifier: "icespirespotted".ToIdentifier(),
|
||||
@@ -1496,6 +1532,54 @@ namespace Barotrauma.Items.Components
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not exahustive, but helps to get rid of some code duplication
|
||||
private static bool IsValidTarget(ISpatialEntity target)
|
||||
{
|
||||
if (target == null) { return false; }
|
||||
if (target is Character targetCharacter)
|
||||
{
|
||||
if (!targetCharacter.Enabled || targetCharacter.Removed || targetCharacter.IsDead || targetCharacter.AITurretPriority <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (target is Item targetItem)
|
||||
{
|
||||
if (targetItem.Removed || targetItem.Condition <= 0 || !targetItem.Prefab.IsAITurretTarget || targetItem.Prefab.AITurretPriority <= 0 || targetItem.HiddenInGame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (targetItem.Submarine != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsValidTargetForAutoOperate(Character target, Identifier friendlyTag)
|
||||
{
|
||||
if (!friendlyTag.IsEmpty)
|
||||
{
|
||||
if (target.SpeciesName.Equals(friendlyTag) || target.Group.Equals(friendlyTag)) { return false; }
|
||||
}
|
||||
bool isHuman = target.IsHuman || target.Group == CharacterPrefab.HumanSpeciesName;
|
||||
if (isHuman)
|
||||
{
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
// Check that the target is not in the friendly team, e.g. pirate or a hostile player sub (PvP).
|
||||
return !target.IsOnFriendlyTeam(item.Submarine.TeamID) && TargetHumans;
|
||||
}
|
||||
return TargetHumans;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shouldn't check the team here, because all the enemies are in the same team (None).
|
||||
return TargetMonsters;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanShoot(Body targetBody, Character user = null, Identifier friendlyTag = default, bool targetSubmarines = true)
|
||||
{
|
||||
if (targetBody == null) { return false; }
|
||||
@@ -1508,7 +1592,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
targetCharacter = limb.character;
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
@@ -1517,27 +1601,25 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!friendlyTag.IsEmpty)
|
||||
else if (!IsValidTargetForAutoOperate(targetCharacter, friendlyTag))
|
||||
{
|
||||
if (targetCharacter.SpeciesName.Equals(friendlyTag) || targetCharacter.Group.Equals(friendlyTag))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Note that Thalamus runs this even when AutoOperate is false.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetBody.UserData is ISpatialEntity e)
|
||||
{
|
||||
if (e is Structure s && s.Indestructible) { return false; }
|
||||
Submarine sub = e.Submarine ?? e as Submarine;
|
||||
if (e is Structure { Indestructible: true }) { return false; }
|
||||
if (!targetSubmarines && e is Submarine) { return false; }
|
||||
if (sub == null) { return false; }
|
||||
Submarine sub = e.Submarine ?? e as Submarine;
|
||||
if (sub == null) { return true; }
|
||||
if (sub == Item.Submarine) { return false; }
|
||||
if (sub.Info.IsOutpost || sub.Info.IsWreck || sub.Info.IsBeacon) { return false; }
|
||||
if (sub.TeamID == Item.Submarine.TeamID) { return false; }
|
||||
}
|
||||
else if (!(targetBody.UserData is Voronoi2.VoronoiCell cell && cell.IsDestructible))
|
||||
else if (targetBody.UserData is not Voronoi2.VoronoiCell { IsDestructible: true })
|
||||
{
|
||||
// Hit something else, probably a level wall
|
||||
return false;
|
||||
@@ -1548,7 +1630,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Body CheckLineOfSight(Vector2 start, Vector2 end)
|
||||
{
|
||||
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel;
|
||||
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionProjectile;
|
||||
Body pickedBody = Submarine.PickBody(start, end, null, collisionCategories, allowInsideFixture: true,
|
||||
customPredicate: (Fixture f) =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user