Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2025-04-10 10:37:09 -03:00
296 changed files with 8420 additions and 2945 deletions
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
using System;
using System.Linq;
@@ -70,6 +70,19 @@ namespace Barotrauma.Items.Components
[Editable, Serialize(false, IsPropertySaveable.Yes, "")]
public bool PreloadCharacter { get; set; }
[Editable, Serialize(false, IsPropertySaveable.Yes, "Should the \"spawn monsters\" setting affect this item in the PvP mode?")]
public bool AffectedByPvPSpawnMonstersSetting { get; set; }
/// <summary>
/// Implemented as a property and checked on the fly instead of disabling the component,
/// because the signals sent by the component might be necessary even if it can't spawn anything.
/// </summary>
private bool DisabledByByPvPSpawnMonstersSetting =>
!SpeciesName.IsNullOrEmpty() &&
AffectedByPvPSpawnMonstersSetting &&
GameMain.GameSession?.GameMode is PvPMode &&
GameMain.NetworkMember is { ServerSettings.PvPSpawnMonsters: false };
private float spawnTimer;
private float? spawnTimerGoal;
@@ -114,15 +127,24 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (PreloadCharacter && !Screen.Selected.IsEditor && !preloadInitiated)
if (DisabledByByPvPSpawnMonstersSetting)
{
SpawnCharacter(Vector2.Zero, onSpawn: (Character c) =>
CanSpawn = false;
//in most cases we could probably just disable the component here and return,
//but the state_out signal might be needed for something even if the spawning is disabled
}
else
{
if (PreloadCharacter && !Screen.Selected.IsEditor && !preloadInitiated)
{
preloadedCharacter = c;
c.DisabledByEvent = true;
});
preloadInitiated = true;
return;
SpawnCharacter(Vector2.Zero, onSpawn: (Character c) =>
{
preloadedCharacter = c;
c.DisabledByEvent = true;
});
preloadInitiated = true;
return;
}
}
base.Update(deltaTime, cam);
@@ -182,7 +204,7 @@ namespace Barotrauma.Items.Components
private bool CanSpawnMore()
{
if (!CanSpawn) { return false; }
if (!CanSpawn || DisabledByByPvPSpawnMonstersSetting) { return false; }
if (MaximumAmount > 0 && spawnedAmount >= MaximumAmount) { return false; }
if (OnlySpawnWhenCrewInRange)
@@ -244,26 +244,50 @@ namespace Barotrauma.Items.Components
if (!targetCharacter.HasEquippedItem(item) &&
(rootContainer == null || !targetCharacter.HasEquippedItem(rootContainer) || !targetCharacter.Inventory.IsInLimbSlot(rootContainer, InvSlotType.HealthInterface)))
{
item.ApplyStatusEffects(ActionType.OnSevered, 1.0f, targetCharacter);
Character prevTargetCharacter = targetCharacter;
//deactivate so the material is no longer updated or considered to be "in effect" in GetCombinedEffectStrength
IsActive = false;
var affliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
if (affliction != null)
Deactivate();
if (rootContainer != null)
{
affliction.Strength = GetCombinedEffectStrength();
foreach (var otherItem in rootContainer.ContainedItems)
{
if (otherItem != item && otherItem.GetComponent<GeneticMaterial>() is { IsActive: true } otherGeneticMaterial)
{
//we need to deactivate other genetic materials in the container too at this point,
//otherwise their effects might get triggered by the damage done by removing the gene
otherGeneticMaterial.Deactivate();
}
}
}
var taintedAffliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
if (taintedAffliction != null)
{
taintedAffliction.Strength = GetCombinedTaintedEffectStrength();
}
targetCharacter = null;
//do this after nullifying the effects, otherwise the damage from removing the genes could trigger the gene's own effects
item.ApplyStatusEffects(ActionType.OnSevered, 1.0f, prevTargetCharacter);
}
}
}
private void Deactivate()
{
IsActive = false;
if (targetCharacter != null)
{
var affliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
if (affliction != null)
{
affliction.Strength = GetCombinedEffectStrength();
}
var taintedAffliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
if (taintedAffliction != null)
{
taintedAffliction.Strength = GetCombinedTaintedEffectStrength();
}
}
NestedMaterial?.Deactivate();
targetCharacter = null;
}
public enum CombineResult
{
None,
@@ -227,6 +227,13 @@ namespace Barotrauma.Items.Components
set;
}
[Editable, Serialize("", IsPropertySaveable.Yes, translationTextTag: "ItemMsg", description: "A text displayed next to the item when it's been dropped on the floor (not attached to a wall).")]
public string MsgWhenDropped
{
get;
set;
}
/// <summary>
/// For setting the handle positions using status effects
/// </summary>
@@ -733,7 +740,19 @@ namespace Barotrauma.Items.Components
#endif
//make the item pickable with the default pick key and with no specific tools/items when it's deattached
RequiredItems.Clear();
DisplayMsg = "";
if (MsgWhenDropped.IsNullOrEmpty())
{
DisplayMsg = "";
}
else
{
DisplayMsg = TextManager.Get(MsgWhenDropped);
DisplayMsg =
DisplayMsg.Loaded ?
TextManager.ParseInputTypes(DisplayMsg) :
MsgWhenDropped;
}
PickKey = InputType.Select;
#if CLIENT
item.DrawDepthOffset = SpriteDepthWhenDropped - item.SpriteDepth;
@@ -52,7 +52,11 @@ namespace Barotrauma.Items.Components
{
if (holdable.Attached)
{
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + item.Prefab.Identifier);
//we don't need info of every collected resource, we can get a good sample size just by logging a small sample
if (GameAnalyticsManager.ShouldLogRandomSample())
{
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + item.Prefab.Identifier);
}
holdable.DeattachFromWall();
}
trigger.Enabled = false;
@@ -334,6 +334,7 @@ namespace Barotrauma.Items.Components
if (targetLimb.character.IgnoreMeleeWeapons) { return false; }
var targetCharacter = targetLimb.character;
if (targetCharacter == picker) { return false; }
if (HitFriendlyTarget(targetCharacter)) { return false; }
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetCharacter)) { return false; }
@@ -348,7 +349,7 @@ namespace Barotrauma.Items.Components
{
if (targetCharacter == picker || targetCharacter == User) { return false; }
if (targetCharacter.IgnoreMeleeWeapons) { return false; }
targetLimb = targetCharacter.AnimController.GetLimb(LimbType.Torso); //Otherwise armor can be bypassed in strange ways
if (HitFriendlyTarget(targetCharacter)) { return false; }
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetCharacter)) { return false; }
@@ -395,10 +396,22 @@ namespace Barotrauma.Items.Components
{
return false;
}
impactQueue.Enqueue(f2);
return true;
// Prevent bots from hitting friendly targets.
bool HitFriendlyTarget(Character target)
{
if (User.IsPlayer) { return false; }
if (User.AIController is HumanAIController { Enabled: true } humanAI)
{
if (humanAI.ObjectiveManager.CurrentObjective is AIObjectiveCombat combat && combat.Enemy != target)
{
if (humanAI.IsFriendly(target, onlySameTeam: true)) { return true; }
}
}
return false;
}
}
private System.Text.StringBuilder serverLogger;
@@ -322,6 +322,7 @@ namespace Barotrauma.Items.Components
}
}
}
projectile.Item.body.Dir = Item.body.Dir;
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: ignoredBodies.ToList(), createNetworkEvent: false, damageMultiplier, LaunchImpulse);
projectile.Item.GetComponent<Rope>()?.Attach(Item, projectile.Item);
if (projectile.Item.body != null)
@@ -248,7 +248,7 @@ namespace Barotrauma.Items.Components
var barrelHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(rayStartWorld), item.CurrentHull, useWorldCoordinates: true);
if (barrelHull != null && barrelHull != item.CurrentHull)
{
if (MathUtils.GetLineRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart), item.CurrentHull.Rect, out Vector2 hullIntersection))
if (MathUtils.GetLineWorldRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart), item.CurrentHull.Rect, out Vector2 hullIntersection))
{
if (!item.CurrentHull.ConnectedGaps.Any(g => g.Open > 0.0f && Submarine.RectContains(g.Rect, hullIntersection)))
{
@@ -108,6 +108,9 @@ namespace Barotrauma.Items.Components
[Serialize(100, IsPropertySaveable.No, description: "How many items are placed in a row before starting a new row.")]
public int ItemsPerRow { get; set; }
[Serialize(false, IsPropertySaveable.No, description: "Should items be drawn based on their position within the inventory?")]
public bool ItemsUseInventoryPlacement { get; set; }
[Serialize(true, IsPropertySaveable.No, description: "Should the inventory of this item be visible when the item is selected. Note that this does not prevent dragging and dropping items to the item.")]
public bool DrawInventory
{
@@ -1013,6 +1016,11 @@ namespace Barotrauma.Items.Components
contained.Item.CurrentHull = item.CurrentHull;
contained.Item.SetContainedItemPositions();
foreach (var lightComponent in contained.Item.GetComponents<LightComponent>())
{
lightComponent.SetLightSourceTransform();
}
i++;
if (Math.Abs(ItemInterval.X) > 0.001f && Math.Abs(ItemInterval.Y) > 0.001f)
{
@@ -1040,8 +1048,8 @@ namespace Barotrauma.Items.Components
transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f);
transformedItemIntervalVertical = new Vector2(0.0f, transformedItemInterval.Y);
flippedX = item.RootContainer?.FlippedX ?? item.FlippedX;
flippedY = item.RootContainer?.FlippedY ?? item.FlippedY;
flippedX = item.RootContainer?.FlippedX ?? (item.FlippedX && item.Prefab.CanSpriteFlipX);
flippedY = item.RootContainer?.FlippedY ?? (item.FlippedY && item.Prefab.CanSpriteFlipY);
var rootBody = item.RootContainer?.body ?? item.body;
bool bodyFlipped = rootBody is { Dir: -1 };
@@ -733,17 +733,24 @@ namespace Barotrauma.Items.Components
private readonly HashSet<Item> usedIngredients = new HashSet<Item>();
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
public bool MissingRequiredRecipe(FabricationRecipe fabricableItem, Character character)
{
if (fabricableItem == null) { return false; }
if (fabricableItem.RequiresRecipe)
if (fabricableItem.RequiresRecipe)
{
if (character == null) { return false; }
if (!AnyOneHasRecipeForItem(character, fabricableItem.TargetItem))
{
return false;
return true;
}
}
return false;
}
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
{
if (fabricableItem == null) { return false; }
if (MissingRequiredRecipe(fabricableItem, character)) { return false; }
if (fabricableItem.HideForNonTraitors)
{
@@ -61,6 +61,7 @@ namespace Barotrauma.Items.Components
private Sonar sonar;
private Submarine controlledSub;
public Submarine ControlledSub => controlledSub;
// AI interfacing
public Vector2 AITacticalTarget { get; set; }
@@ -75,6 +76,7 @@ namespace Barotrauma.Items.Components
private double lastReceivedSteeringSignalTime;
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes, AlwaysUseInstanceValues = true)]
public bool AutoPilot
{
get { return autoPilot; }
@@ -131,7 +131,7 @@ namespace Barotrauma.Items.Components
SuitablePlantItem plantItem = GetSuitableItem(character);
if (!plantItem.IsNull())
if (!plantItem.IsNull() && item.GetComponent<Holdable>() is not { Attachable: true, Attached: false })
{
Msg = plantItem.Type switch
{
@@ -159,7 +159,6 @@ namespace Barotrauma.Items.Components
{
SuitablePlantItem plantItem = GetSuitableItem(character);
PickingMsg = plantItem.IsNull() ? MsgUprooting : plantItem.ProgressBarMessage;
return base.Pick(character);
}
@@ -28,20 +28,24 @@ namespace Barotrauma.Items.Components
SpreadCounter = 0;
}
struct HitscanResult
readonly struct HitscanResult
{
public Fixture Fixture;
public Vector2 Point;
public Vector2 Normal;
public float Fraction;
public HitscanResult(Fixture fixture, Vector2 point, Vector2 normal, float fraction)
public readonly Fixture Fixture;
public readonly Vector2 Point;
public readonly Vector2 Normal;
public readonly float Fraction;
public readonly Submarine Submarine;
public HitscanResult(Fixture fixture, Vector2 point, Vector2 normal, float fraction, Submarine sub)
{
Fixture = fixture;
Point = point;
Normal = normal;
Fraction = fraction;
Submarine = sub;
}
}
struct Impact
{
public Fixture Fixture;
@@ -393,8 +397,6 @@ namespace Barotrauma.Items.Components
if (Item.Removed) { return; }
launchPos = simPosition;
LaunchSub = item.Submarine;
//set the rotation of the projectile again because dropping the projectile resets the rotation
Item.SetTransform(simPosition, rotation + (Item.body.Dir * LaunchRotationRadians), findNewHull: false);
if (DeactivationTime > 0)
{
deactivationTimer = DeactivationTime;
@@ -483,6 +485,13 @@ namespace Barotrauma.Items.Components
{
float modifiedLaunchImpulse = (LaunchImpulse + launchImpulseModifier) * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
DoLaunch(launchDir * modifiedLaunchImpulse);
//needs to be set after DoLaunch, because dropping the item resets the rotation and dir
float afterLaunchAngle = launchAngle + (item.body.Dir * LaunchRotationRadians);
if (item.body.Dir < 0)
{
afterLaunchAngle -= MathHelper.Pi;
}
item.SetTransform(item.body.SimPosition, afterLaunchAngle, findNewHull: false);
}
}
User = character;
@@ -607,7 +616,8 @@ namespace Barotrauma.Items.Components
inSubHits[i].Fixture,
inSubHits[i].Point + submarine.SimPosition,
inSubHits[i].Normal,
inSubHits[i].Fraction);
inSubHits[i].Fraction,
sub: null);
}
hits.AddRange(inSubHits);
}
@@ -620,6 +630,7 @@ namespace Barotrauma.Items.Components
{
var h = hits[i];
item.SetTransform(h.Point, rotation);
item.Submarine = h.Submarine;
item.UpdateTransform();
if (HandleProjectileCollision(h.Fixture, h.Normal, Vector2.Zero))
{
@@ -717,7 +728,7 @@ namespace Barotrauma.Items.Components
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
hits.Add(new HitscanResult(fixture, rayStart, -dir, 0.0f));
hits.Add(new HitscanResult(fixture, rayStart, -dir, 0.0f, submarine));
return true;
}, ref aabb);
@@ -789,7 +800,7 @@ namespace Barotrauma.Items.Components
}
}
hits.Add(new HitscanResult(fixture, point, normal, fraction));
hits.Add(new HitscanResult(fixture, point, normal, fraction, submarine));
return 1;
}, rayStart, rayEnd, Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking | Physics.CollisionProjectile | Physics.CollisionLagCompensationBody);
@@ -1101,11 +1112,11 @@ namespace Barotrauma.Items.Components
{
if (Attack != null)
{
Vector2 pos = item.WorldPosition;
if (item.Submarine == null && damageable is Structure structure && structure.Submarine != null && Vector2.DistanceSquared(item.WorldPosition, structure.WorldPosition) > 10000.0f * 10000.0f)
{
item.Submarine = structure.Submarine;
}
Vector2 pos = item.WorldPosition;
attackResult = Attack.DoDamage(User ?? Attacker, damageable, pos, 1.0f);
}
}
@@ -640,6 +640,18 @@ namespace Barotrauma.Items.Components
OnViewUpdateProjSpecific();
}
public void RemoveWire(Wire wireItem)
{
foreach (CircuitBoxWire wire in Wires.ToImmutableArray())
{
if (wire.BackingWire.TryUnwrap(out var backingWire) && backingWire == wireItem.Item)
{
RemoveWireCollectionUnsafe(wire);
}
}
OnViewUpdateProjSpecific();
}
private void RemoveWireCollectionUnsafe(CircuitBoxWire wire)
{
foreach (CircuitBoxOutputConnection output in Outputs)
@@ -198,6 +198,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize("0,0", IsPropertySaveable.No, description: "Offset of the light from the position of the item (in pixels).")]
public Vector2 LightOffset
{
get;
set;
}
/// <summary>
/// Returns true if the red component of the light is twice as bright as the blue and green. Can be used by StatusEffects.
/// </summary>
@@ -304,17 +311,18 @@ namespace Barotrauma.Items.Components
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
{
if (item.body == null || item.body.Enabled ||
(item.ParentInventory is ItemInventory itemInventory && !itemInventory.Container.HideItems))
{
lightBrightness = 1.0f;
SetLightSourceState(true, lightBrightness);
}
else
PhysicsBody body = ParentBody ?? item.body;
if ((body == null || !body.Enabled) &&
(item.FindParentInventory(static it => it is ItemInventory { Container.HideItems: true }) != null))
{
lightBrightness = 0.0f;
SetLightSourceState(false, 0.0f);
}
else
{
lightBrightness = 1.0f;
SetLightSourceState(true, lightBrightness);
}
isOn = true;
SetLightSourceTransformProjSpecific();
base.IsActive = false;
@@ -366,7 +374,7 @@ namespace Barotrauma.Items.Components
SetLightSourceTransformProjSpecific();
PhysicsBody body = ParentBody ?? item.body;
if (body != null && !body.Enabled && !visibleInContainer)
if ((body == null || !body.Enabled) && !visibleInContainer)
{
lightBrightness = 0.0f;
SetLightSourceState(false, 0.0f);
@@ -272,8 +272,15 @@ namespace Barotrauma.Items.Components
if (worldBorders.Intersects(detectRect))
{
MotionDetected = true;
return;
foreach (Structure wall in Structure.WallList)
{
if (wall.Submarine == sub &&
wall.WorldRect.Intersects(detectRect))
{
MotionDetected = true;
return;
}
}
}
}
}
@@ -8,7 +8,7 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class WifiComponent : ItemComponent, IServerSerializable
partial class WifiComponent : ItemComponent, IServerSerializable, IClientSerializable
{
private static readonly List<WifiComponent> list = new List<WifiComponent>();
@@ -56,7 +56,6 @@ namespace Barotrauma.Items.Components
}
}
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Can the component communicate with wifi components in another team's submarine (e.g. enemy sub in Combat missions, respawn shuttle). Needs to be enabled on both the component transmitting the signal and the component receiving it.", alwaysUseInstanceValues: true)]
public bool AllowCrossTeamCommunication
{
@@ -376,5 +375,25 @@ namespace Barotrauma.Items.Components
element.Add(new XAttribute("channelmemory", string.Join(',', channelMemory)));
return element;
}
protected void SharedEventWrite(IWriteMessage msg)
{
msg.WriteRangedInteger(Channel, MinChannel, MaxChannel);
for (int i = 0; i < ChannelMemorySize; i++)
{
msg.WriteRangedInteger(channelMemory[i], MinChannel, MaxChannel);
}
}
protected void SharedEventRead(IReadMessage msg)
{
Channel = msg.ReadRangedInteger(MinChannel, MaxChannel);
for (int i = 0; i < ChannelMemorySize; i++)
{
channelMemory[i] = msg.ReadRangedInteger(MinChannel, MaxChannel);
}
}
}
}
@@ -84,6 +84,13 @@ namespace Barotrauma.Items.Components
public float Length { get; private set; }
[Serialize(0.3f, IsPropertySaveable.No), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f, DecimalCount = 2)]
public float Width
{
get;
set;
}
[Serialize(5000.0f, IsPropertySaveable.No, description: "The maximum distance the wire can extend (in pixels).")]
public float MaxLength
{
@@ -885,8 +892,13 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
if (item.Container?.GetComponent<CircuitBox>() is { } circuitBox)
{
circuitBox.RemoveWire(this);
}
ClearConnections();
base.RemoveComponentSpecific();
#if CLIENT
if (DraggingWire == this) { draggingWire = null; }
overrideSprite?.Remove();
@@ -324,9 +324,19 @@ namespace Barotrauma.Items.Components
Editable(TransferToSwappedItem = true)]
public Identifier FriendlyTag { get; private set; }
[Serialize("None", IsPropertySaveable.Yes, description: "[Auto Operate] Team that the turret considers friendly. If set to None, the team the submarine/outpost belongs to is considered the friendly team."),
[Serialize("OwnSub", IsPropertySaveable.Yes, description: "[Auto Operate] Team that the turret considers friendly."),
Editable(TransferToSwappedItem = true)]
public CharacterTeamType FriendlyTeam { get; private set; }
public TeamType FriendlyTeamType { get; private set; }
public enum TeamType
{
OwnSub,
Team1,
Team2,
FriendlyNPC,
NoneTeam
}
#endregion
private const string SetAutoOperateConnection = "set_auto_operate";
@@ -336,7 +346,7 @@ namespace Barotrauma.Items.Components
: base(item, element)
{
IsActive = true;
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -388,6 +398,7 @@ namespace Barotrauma.Items.Components
base.OnMapLoaded();
if (loadedRotationLimits.HasValue) { RotationLimits = loadedRotationLimits.Value; }
if (loadedBaseRotation.HasValue) { BaseRotation = loadedBaseRotation.Value; }
if (loadedFriendlyTeamType.HasValue) { FriendlyTeamType = loadedFriendlyTeamType.Value; }
targetRotation = Rotation;
UpdateTransformedBarrelPos();
if (!AllowAutoOperateWithWiring &&
@@ -1141,7 +1152,7 @@ namespace Barotrauma.Items.Components
if (target is Hull targetHull)
{
Vector2 barrelDir = GetBarrelDir();
if (!MathUtils.GetLineRectangleIntersection(item.WorldPosition, item.WorldPosition + barrelDir * AIRange, targetHull.WorldRect, out _))
if (!MathUtils.GetLineWorldRectangleIntersection(item.WorldPosition, item.WorldPosition + barrelDir * AIRange, targetHull.WorldRect, out _))
{
return;
}
@@ -1375,7 +1386,7 @@ namespace Barotrauma.Items.Components
}
// Don't aim monsters that are inside any submarine.
if (!enemy.IsHuman && enemy.CurrentHull != null) { continue; }
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
if (HumanAIController.IsFriendly(character, enemy, ignoreHuskDisguising: true)) { continue; }
// Don't shoot at captured enemies.
if (enemy.LockHands) { continue; }
float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
@@ -1699,26 +1710,34 @@ namespace Barotrauma.Items.Components
return true;
}
private CharacterTeamType GetFriendlyTeam()
{
return FriendlyTeamType switch
{
TeamType.Team1 => CharacterTeamType.Team1,
TeamType.Team2 => CharacterTeamType.Team2,
TeamType.FriendlyNPC => CharacterTeamType.FriendlyNPC,
TeamType.NoneTeam => CharacterTeamType.None,
TeamType.OwnSub => item.Submarine?.TeamID ?? CharacterTeamType.None,
_ => throw new NotImplementedException(),
};
}
private bool IsValidTargetForAutoOperate(Character target, Identifier friendlyTag)
{
if (!friendlyTag.IsEmpty)
{
if (target.SpeciesName.Equals(friendlyTag) || target.Group.Equals(friendlyTag)) { return false; }
}
if (FriendlyTeam != CharacterTeamType.None)
{
if (target.TeamID == FriendlyTeam) { return false; }
}
CharacterTeamType friendlyTeam = GetFriendlyTeam();
if (target.TeamID == friendlyTeam) { 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).
var turretTeam = FriendlyTeam == CharacterTeamType.None ? item.Submarine.TeamID : FriendlyTeam;
return !target.IsOnFriendlyTeam(turretTeam) && TargetHumans;
}
return TargetHumans;
return !target.IsOnFriendlyTeam(friendlyTeam) && TargetHumans;
}
else
{
@@ -1747,7 +1766,7 @@ namespace Barotrauma.Items.Components
{
if (user != null)
{
if (HumanAIController.IsFriendly(user, targetCharacter))
if (HumanAIController.IsFriendly(user, targetCharacter, ignoreHuskDisguising: true))
{
return false;
}
@@ -1770,7 +1789,7 @@ namespace Barotrauma.Items.Components
if (sub.Info.IsOutpost || sub.Info.IsWreck || sub.Info.IsBeacon || sub.Info.IsRuin) { return false; }
if (item.Submarine == null)
{
if (sub.TeamID == FriendlyTeam) { return false; }
if (sub.TeamID == GetFriendlyTeam()) { return false; }
}
else
{
@@ -2013,11 +2032,27 @@ namespace Barotrauma.Items.Components
private Vector2? loadedRotationLimits;
private float? loadedBaseRotation;
private TeamType? loadedFriendlyTeamType;
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
{
base.Load(componentElement, usePrefabValues, idRemap, isItemSwap);
loadedRotationLimits = componentElement.GetAttributeVector2("rotationlimits", RotationLimits);
loadedBaseRotation = componentElement.GetAttributeFloat("baserotation", componentElement.Parent.GetAttributeFloat("rotation", BaseRotation));
//backwards compatibility: previously None made the turret consider the team the submarine/outpost belongs as the friendly team
if (componentElement.GetAttribute("FriendlyTeam") is { } friendlyTeamAttribute)
{
CharacterTeamType friendlyTeam = XMLExtensions.ParseEnumValue(friendlyTeamAttribute.Value, defaultValue: CharacterTeamType.None, friendlyTeamAttribute);
loadedFriendlyTeamType = friendlyTeam switch
{
CharacterTeamType.None => TeamType.OwnSub,
CharacterTeamType.Team1 => TeamType.Team1,
CharacterTeamType.Team2 => TeamType.Team2,
CharacterTeamType.FriendlyNPC => TeamType.FriendlyNPC,
_ => throw new NotImplementedException()
};
}
}
public override void OnItemLoaded()
@@ -2030,6 +2065,8 @@ namespace Barotrauma.Items.Components
if (item.FlippedX) { FlipX(relativeToSub: false); }
if (item.FlippedY) { FlipY(relativeToSub: false); }
}
UpdateTransformedBarrelPos();
UpdateLightComponents();
}
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)