Merge remote-tracking branch 'upstream/master' into develop

This commit is contained in:
EvilFactory
2024-12-11 10:44:53 -03:00
257 changed files with 4793 additions and 1653 deletions
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
private Gap linkedGap;
private bool isOpen;
private float openState;
private float openState, lastOpenState;
private readonly Sprite doorSprite, weldedSprite, brokenSprite;
private readonly bool scaleBrokenSprite, fadeBrokenSprite;
private readonly bool autoOrientGap;
@@ -218,6 +218,7 @@ namespace Barotrauma.Items.Components
get { return openState; }
set
{
lastOpenState = openState;
openState = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
float size = IsHorizontal ? item.Rect.Width : item.Rect.Height;
@@ -329,13 +330,24 @@ namespace Barotrauma.Items.Components
private readonly LocalizedString cannotOpenText = TextManager.Get("DoorMsgCannotOpen");
public override bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
{
Msg = HasAccess(character) ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar";
if (IsBroken)
{
return false;
}
if (isOpen)
{
Msg = HasAccess(character) ? "ItemMsgClose" : "ItemMsgForceCloseCrowbar";
}
else
{
Msg = HasAccess(character) ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar";
}
ParseMsg();
if (addMessage)
{
msg = msg ?? (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText).Value;
msg ??= (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText).Value;
}
return isBroken || base.HasRequiredItems(character, addMessage, msg);
return base.HasRequiredItems(character, addMessage, msg);
}
public override bool Pick(Character picker)
@@ -461,12 +473,12 @@ namespace Barotrauma.Items.Components
if (PredictedState == null)
{
OpenState += deltaTime * (isOpen ? OpeningSpeed : -ClosingSpeed);
isClosing = openState > 0.0f && openState < 1.0f && !isOpen;
isClosing = openState is > 0.0f and < 1.0f && !isOpen;
}
else
{
OpenState += deltaTime * ((bool)PredictedState ? OpeningSpeed : -ClosingSpeed);
isClosing = openState > 0.0f && openState < 1.0f && !(bool)PredictedState;
OpenState += deltaTime * (PredictedState.Value ? OpeningSpeed : -ClosingSpeed);
isClosing = openState is > 0.0f and < 1.0f && !PredictedState.Value;
resetPredictionTimer -= deltaTime;
if (resetPredictionTimer <= 0.0f)
@@ -479,7 +491,11 @@ namespace Barotrauma.Items.Components
if (isClosing)
{
if (OpenState < 0.9f) { PushCharactersAway(); }
//server gives the clients more leeway on moving through closing doors
//latency can often otherwise make a client get blocked by a closing door server-side even if it seemed like they made it through client-side
float pushCharactersAwayThreshold = GameMain.NetworkMember is { IsServer: true } ? 0.1f : 0.9f;
if (OpenState < pushCharactersAwayThreshold) { PushCharactersAway(); }
if (CheckSubmarinesInDoorWay())
{
PredictedState = null;
@@ -771,11 +787,11 @@ namespace Barotrauma.Items.Components
{
if (IsHorizontal)
{
body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * doorRectSimSize.Y * 2.0f), body.Rotation);
body.SetTransformIgnoreContacts(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * doorRectSimSize.Y * 2.0f), body.Rotation);
}
else
{
body.SetTransform(new Vector2(item.SimPosition.X + dir * doorRectSimSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
body.SetTransformIgnoreContacts(new Vector2(item.SimPosition.X + dir * doorRectSimSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
}
}
@@ -14,13 +14,15 @@ namespace Barotrauma.Items.Components
{
partial class Holdable : Pickable, IServerSerializable, IClientSerializable
{
private readonly struct EventData : IEventData
private readonly struct AttachEventData : IEventData
{
public readonly Vector2 AttachPos;
public EventData(Vector2 attachPos)
public readonly Character Attacher;
public AttachEventData(Vector2 attachPos, Character attacher)
{
AttachPos = attachPos;
Attacher = attacher;
}
}
@@ -225,6 +227,44 @@ namespace Barotrauma.Items.Components
set;
}
/// <summary>
/// For setting the handle positions using status effects
/// </summary>
public Vector2 Handle1
{
get { return ConvertUnits.ToDisplayUnits(handlePos[0]); }
set
{
handlePos[0] = ConvertUnits.ToSimUnits(value);
if (item.FlippedX)
{
handlePos[0].X = -handlePos[0].X;
}
if (!secondHandlePosDefined)
{
Handle2 = value;
}
}
}
/// <summary>
/// For setting the handle positions using status effects
/// </summary>
public Vector2 Handle2
{
get { return ConvertUnits.ToDisplayUnits(handlePos[1]); }
set
{
handlePos[1] = ConvertUnits.ToSimUnits(value);
if (item.FlippedX)
{
handlePos[1].X = -handlePos[1].X;
}
}
}
private bool secondHandlePosDefined;
public Holdable(Item item, ContentXElement element)
: base(item, element)
{
@@ -254,9 +294,14 @@ namespace Barotrauma.Items.Components
{
int index = i - 1;
string attributeName = "handle" + i;
var attribute = element.GetAttribute(attributeName);
// If no value is defind for handle2, use the value of handle1.
var value = attribute != null ? ConvertUnits.ToSimUnits(XMLExtensions.ParseVector2(attribute.Value)) : previousValue;
Vector2 value = previousValue;
var attribute = element.GetAttribute(attributeName);
if (attribute != null)
{
secondHandlePosDefined = i > 1;
value = ConvertUnits.ToSimUnits(XMLExtensions.ParseVector2(attribute.Value));
}
handlePos[index] = value;
previousValue = value;
}
@@ -755,21 +800,14 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember != null)
{
if (character != Character.Controlled)
{
return false;
}
else if (GameMain.NetworkMember.IsServer)
{
return false;
}
else
{
#if CLIENT
if (character == Character.Controlled)
{
Vector2 attachPos = ConvertUnits.ToSimUnits(GetAttachPosition(character));
item.CreateClientEvent(this, new EventData(attachPos));
#endif
item.CreateClientEvent(this, new AttachEventData(attachPos, character));
}
#endif
//don't attach at this point in MP: instead rely on the network events created above
return false;
}
else
@@ -824,9 +862,13 @@ namespace Barotrauma.Items.Components
if (user.Submarine != null)
{
//we must add some "padding" to the raycast to ensure it reaches all the way to a wall
//otherwise the cursor might be outside a wall, but the grid cell it's in might be partially inside
Vector2 padding = Submarine.GridSize * new Vector2(Math.Sign(mouseDiff.X), Math.Sign(mouseDiff.Y));
if (Submarine.PickBody(
ConvertUnits.ToSimUnits(user.Position),
ConvertUnits.ToSimUnits(user.Position + mouseDiff), collisionCategory: Physics.CollisionWall) != null)
ConvertUnits.ToSimUnits(user.Position + mouseDiff + padding), collisionCategory: Physics.CollisionWall) != null)
{
attachPos = userPos + mouseDiff * Submarine.LastPickedFraction + offset;
@@ -420,7 +420,7 @@ namespace Barotrauma.Items.Components
Limb targetLimb = target.UserData as Limb;
Character targetCharacter = targetLimb?.character ?? target.UserData as Character;
Structure targetStructure = target.UserData as Structure ?? targetFixture.UserData as Structure;
Item targetItem = target.UserData as Item ?? targetFixture.UserData as Item;
Item targetItem = target.UserData is Holdable h ? h.Item : target.UserData as Item ?? targetFixture.UserData as Item;
Entity targetEntity = targetCharacter ?? targetStructure ?? targetItem ?? target.UserData as Entity;
GameMain.LuaCs.Hook.Call("meleeWeapon.handleImpact", this, target);
if (Attack != null)
@@ -461,10 +461,9 @@ namespace Barotrauma.Items.Components
}
#endif
}
else if (target.UserData is Holdable holdable && holdable.CanPush)
else if (target.UserData is Holdable { CanPush: true } holdable)
{
if (holdable.Item.Removed) { return; }
Attack.DoDamage(user, holdable.Item, item.WorldPosition, 1.0f);
RestoreCollision();
hitting = false;
User = null;
@@ -202,12 +202,26 @@ namespace Barotrauma.Items.Components
#if CLIENT
if (requiredTime < float.MaxValue && picker == Character.Controlled)
{
string text = string.Empty;
if (!string.IsNullOrWhiteSpace(PickingMsg))
{
text = PickingMsg;
}
else if (this is Door door)
{
text = door.IsClosed ? "progressbar.opening" : "progressbar.closing";
}
else
{
text = "progressbar.deattaching";
}
Character.Controlled?.UpdateHUDProgressBar(
this,
item.WorldPosition,
pickTimer / requiredTime,
GUIStyle.Red, GUIStyle.Green,
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
text);
}
#endif
picker.AnimController.UpdateUseItem(!picker.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
@@ -296,7 +296,9 @@ namespace Barotrauma.Items.Components
//which doesn't support multiple attached ropes (see Holdable.GetRope and the references to it)
lastProjectile?.Item.GetComponent<Rope>()?.Snap();
}
float damageMultiplier = (1f + item.GetQualityModifier(Quality.StatType.FirepowerMultiplier)) * WeaponDamageModifier;
float rangedAttackMultiplier = character?.GetStatValue(StatTypes.RangedAttackMultiplier) ?? 0;
float damageMultiplier = (1f + item.GetQualityModifier(Quality.StatType.FirepowerMultiplier) + rangedAttackMultiplier) * WeaponDamageModifier;
projectile.Launcher = item;
ignoredBodies.Clear();
@@ -306,6 +308,9 @@ namespace Barotrauma.Items.Components
{
if (l.IsSevered) { continue; }
ignoredBodies.Add(l.body.FarseerBody);
#if SERVER
ignoredBodies.Add(l.LagCompensatedBody.FarseerBody);
#endif
}
foreach (Item heldItem in character.HeldItems)
@@ -320,7 +320,7 @@ namespace Barotrauma.Items.Components
private readonly List<FireSource> fireSourcesInRange = new List<FireSource>();
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
{
var collisionCategories = Physics.CollisionWall | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepairableWall;
var collisionCategories = Physics.CollisionWall | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepairableWall | Physics.CollisionItemBlocking;
if (!IgnoreCharacters)
{
collisionCategories |= Physics.CollisionCharacter;
@@ -654,8 +654,9 @@ namespace Barotrauma.Items.Components
FixCharacterProjSpecific(user, deltaTime, targetLimb.character);
return true;
}
else if (targetBody.UserData is Item targetItem)
else if (targetBody.UserData is Barotrauma.Item or Holdable)
{
Item targetItem = targetBody.UserData is Holdable holdable ? holdable.Item : (Item)targetBody.UserData;
if (!HitItems || !targetItem.IsInteractable(user)) { return false; }
var levelResource = targetItem.GetComponent<LevelResource>();
@@ -77,6 +77,9 @@ namespace Barotrauma.Items.Components
public readonly ContentXElement originalElement;
/// <summary>
/// The default delay for delayed client-side corrections (see <see cref="StartDelayedCorrection"/>.
/// </summary>
protected const float CorrectionDelay = 1.0f;
protected CoroutineHandle delayedCorrectionCoroutine;
@@ -669,8 +672,7 @@ namespace Barotrauma.Items.Components
#endif
}
protected string GetTextureDirectory(ContentXElement subElement)
=> subElement.DoesAttributeReferenceFileNameAlone("texture") ? Path.GetDirectoryName(item.Prefab.FilePath) : string.Empty;
protected string GetTextureDirectory(ContentXElement subElement) => item.Prefab.GetTexturePath(subElement, item.Prefab.ParentPrefab);
public bool HasRequiredSkills(Character character)
{
@@ -108,7 +108,7 @@ 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(true, IsPropertySaveable.No, description: "Should the inventory of this item be visible when the item is selected.")]
[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
{
get;
@@ -923,6 +923,8 @@ namespace Barotrauma.Items.Components
#warning There's some code duplication here and in DrawContainedItems() method, but it's not straightforward to get rid of it, because of slightly different logic and the usage of draw positions vs. positions etc. Should probably be splitted into smaller methods.
public void SetContainedItemPositions()
{
if (containedItems.Count == 0) { return; }
var rootBody = item.RootContainer?.body ?? item.body;
Vector2 transformedItemPos = GetContainedPosition(
@@ -989,8 +991,7 @@ namespace Barotrauma.Items.Components
rotation += -item.RotationRad;
}
contained.Item.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, rotation);
contained.Item.body.SetPrevTransform(contained.Item.body.SimPosition, contained.Item.body.Rotation);
contained.Item.body.UpdateDrawPosition();
contained.Item.body.UpdateDrawPosition(interpolate: false);
}
catch (Exception e)
{
@@ -220,6 +220,10 @@ namespace Barotrauma.Items.Components
/// </summary>
private bool forceSelectNextFrame;
private float userCanInteractCheckTimer;
private const float UserCanInteractCheckInterval = 1.0f;
public override void Update(float deltaTime, Camera cam)
{
this.cam = cam;
@@ -238,13 +242,15 @@ namespace Barotrauma.Items.Components
}
forceSelectNextFrame = false;
userCanInteractCheckTimer -= deltaTime;
if (user == null
|| user.Removed
|| !user.IsAnySelectedItem(item)
|| (item.ParentInventory != null && !IsAttachedUser(user))
|| !user.CanInteractWith(item)
|| (UsableIn == UseEnvironment.Water && !user.AnimController.InWater)
|| (UsableIn == UseEnvironment.Air && user.AnimController.InWater))
|| (UsableIn == UseEnvironment.Air && user.AnimController.InWater)
|| !CheckUserCanInteract())
{
if (user != null)
{
@@ -368,6 +374,22 @@ namespace Barotrauma.Items.Components
}
}
private bool CheckUserCanInteract()
{
//optimization: CanInteractWith is relatively heavy (can involve visibility checks for example), let's not do it every frame
if (user != null)
{
if (userCanInteractCheckTimer <= 0.0f)
{
userCanInteractCheckTimer = UserCanInteractCheckInterval;
return user.CanInteractWith(item);
}
}
//we only do the actual check every UserCanInteractCheckInterval seconds
//can mean the component can stay selected for <1s after the user no longer has access to it
return true;
}
private double lastUsed;
public override bool Use(float deltaTime, Character activator = null)
@@ -499,12 +499,13 @@ namespace Barotrauma.Items.Components
{
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
}
InvSlotType invSlot = fabricatedItem.MoveToSlot;
if (i < amountFittingContainer)
{
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
onSpawned: (Item spawnedItem) =>
{
onItemSpawned(spawnedItem, tempUser);
onItemSpawned(spawnedItem, tempUser, invSlot);
spawnedItem.Quality = quality;
spawnedItem.StolenDuringRound = ingredientsStolen;
spawnedItem.AllowStealing = ingredientsAllowStealing;
@@ -517,7 +518,7 @@ namespace Barotrauma.Items.Components
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality,
onSpawned: (Item spawnedItem) =>
{
onItemSpawned(spawnedItem, tempUser);
onItemSpawned(spawnedItem, tempUser, invSlot);
spawnedItem.Quality = quality;
spawnedItem.StolenDuringRound = ingredientsStolen;
spawnedItem.AllowStealing = ingredientsAllowStealing;
@@ -527,15 +528,28 @@ namespace Barotrauma.Items.Components
}
}
void onItemSpawned(Item spawnedItem, Character user)
void onItemSpawned(Item spawnedItem, Character user, InvSlotType slot)
{
if (user != null && user.TeamID != CharacterTeamType.None)
CharacterTeamType teamID = CharacterTeamType.None;
if (user != null)
{
teamID = user.TeamID;
}
else if (item.Submarine != null)
{
teamID = item.Submarine.TeamID;
}
if (teamID != CharacterTeamType.None)
{
foreach (WifiComponent wifiComponent in spawnedItem.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = user.TeamID;
wifiComponent.TeamID = teamID;
}
}
if (slot != InvSlotType.None)
{
user?.Inventory.TryPutItem(spawnedItem, user, slot.ToEnumerable());
}
OnItemFabricated?.Invoke(spawnedItem, user);
}
if (user?.Info != null && !user.Removed)
@@ -562,7 +576,6 @@ namespace Barotrauma.Items.Components
StartFabricating(prevFabricatedItem, prevUser, addToServerLog: false);
}
}
}
/// <summary>
@@ -80,6 +80,7 @@ namespace Barotrauma.Items.Components
private set
{
if (lastUser == value) { return; }
if (Screen.Selected.IsEditor) { return; }
lastUser = value;
if (lastUser == null)
{
@@ -246,6 +247,13 @@ namespace Barotrauma.Items.Components
}
}
//rapidly adjust the reactor in the first few seconds of the round to prevent overvoltages if the load changed between rounds
//(unless the reactor is being operated by a player)
if (GameMain.GameSession is { RoundDuration: <5 } && lastUser is not { IsPlayer: true })
{
UpdateAutoTemp(100.0f, (float)(Timing.Step * 10.0f));
}
#if CLIENT
if (PowerOn && AvailableFuel < 1)
{
@@ -8,6 +8,8 @@ namespace Barotrauma.Items.Components
{
partial class Sonar : Powered, IServerSerializable, IClientSerializable
{
public static List<Sonar> SonarList = new List<Sonar>();
public enum Mode
{
Active,
@@ -167,6 +169,7 @@ namespace Barotrauma.Items.Components
IsActive = true;
InitProjSpecific(element);
CurrentMode = Mode.Passive;
SonarList.Add(this);
}
partial void InitProjSpecific(ContentXElement element);
@@ -379,6 +382,29 @@ namespace Barotrauma.Items.Components
}
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
#if CLIENT
sonarBlip?.Remove();
pingCircle?.Remove();
directionalPingCircle?.Remove();
screenOverlay?.Remove();
screenBackground?.Remove();
lineSprite?.Remove();
foreach (var t in targetIcons.Values)
{
t.Item1.Remove();
}
targetIcons.Clear();
MineralClusters = null;
#endif
SonarList.Remove(this);
}
public void ServerEventRead(IReadMessage msg, Client c)
{
bool isActive = msg.ReadBoolean();
@@ -232,7 +232,7 @@ namespace Barotrauma.Items.Components
float maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
Overload = Voltage > maxOverVoltage;
Overload = Voltage > maxOverVoltage && GameMain.GameSession is not { RoundDuration: < 5 };
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
@@ -157,6 +157,13 @@ namespace Barotrauma.Items.Components
{
if (powerOut?.Grid != null) { return powerOut.Grid.Voltage; }
}
if (this is PowerTransfer && item.Condition <= 0.0f)
{
//if the junction box or other power transfer device is broken,
//it cannot be supplying any power (voltage = 0)
return 0.0f;
}
return PowerConsumption <= 0.0f ? 1.0f : voltage;
}
set
@@ -440,6 +440,14 @@ namespace Barotrauma.Items.Components
//can't launch if already launched
if (StickTarget != null || IsActive) { return false; }
#if SERVER
var owner = GameMain.Server.ConnectedClients.FirstOrDefault(c => c.Character == User);
if (owner != null)
{
Limb.SetLagCompensatedBodyPositions(owner);
}
#endif
float initialRotation = item.body.Rotation;
//if the item is being launched from an inventory, assume it's being fired by a gun that handles setting the rotation correctly
//but if the item is e.g. being thrown by a character, we need to take the direction into account
@@ -461,10 +469,10 @@ namespace Barotrauma.Items.Components
spreadIndex++;
Vector2 launchDir = new Vector2((float)Math.Cos(launchAngle), (float)Math.Sin(launchAngle));
Vector2 prevSimpos = item.SimPosition;
item.body.SetTransformIgnoreContacts(item.body.SimPosition, launchAngle);
if (Hitscan)
{
Vector2 prevSimpos = item.SimPosition;
item.body.SetTransformIgnoreContacts(item.body.SimPosition, launchAngle);
DoHitscan(launchDir);
if (i < HitScanCount - 1)
{
@@ -473,7 +481,6 @@ namespace Barotrauma.Items.Components
}
else
{
item.body.SetTransform(item.body.SimPosition, launchAngle);
float modifiedLaunchImpulse = (LaunchImpulse + launchImpulseModifier) * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
DoLaunch(launchDir * modifiedLaunchImpulse);
}
@@ -670,8 +677,6 @@ namespace Barotrauma.Items.Components
}
if (fixture.Body.UserData is VineTile) { return true; }
if (fixture.CollidesWith == Category.None) { return true; }
//only collides with characters = probably an "outsideCollisionBlocker" created by a gap
if (fixture.CollidesWith == Physics.CollisionCharacter) { return true; }
if (fixture.Body.UserData as string == "ruinroom" || fixture.Body.UserData is Hull || fixture.UserData is Hull) { return true; }
@@ -690,6 +695,11 @@ namespace Barotrauma.Items.Components
if (item.Condition <= 0) { return true; }
if (!item.Prefab.DamagedByProjectiles && item.GetComponent<Door>() == null) { return true; }
}
else if (fixture.Body.UserData is Gap)
{
//an "outsideCollisionBlocker" created by a gap, should never collide
return true;
}
else if (fixture.Body.UserData is Holdable { CanPush: false })
{
// Ignore holdables that can't push -> shouldn't block
@@ -724,14 +734,17 @@ namespace Barotrauma.Items.Components
return -1;
}
if (fixture.Body.UserData is VineTile) { return -1; }
if (fixture.CollidesWith == Category.None) { return -1; }
//only collides with characters = probably an "outsideCollisionBlocker" created by a gap
if (fixture.CollidesWith == Physics.CollisionCharacter) { return -1; }
if (fixture.CollidesWith == Category.None && fixture.CollisionCategories != Physics.CollisionLagCompensationBody) { return -1; }
if (fixture.Body.UserData is Item item)
{
if (item.Condition <= 0) { return -1; }
if (!item.Prefab.DamagedByProjectiles && item.GetComponent<Door>() == null) { return -1; }
}
else if (fixture.Body.UserData is Gap)
{
//an "outsideCollisionBlocker" created by a gap, should never collide
return -1;
}
if (fixture.Body.UserData as string == "ruinroom" || fixture.Body?.UserData is Hull || fixture.UserData is Hull) { return -1; }
//if doing the raycast in a submarine's coordinate space, ignore anything that's not in that sub
@@ -779,7 +792,7 @@ namespace Barotrauma.Items.Components
hits.Add(new HitscanResult(fixture, point, normal, fraction));
return 1;
}, rayStart, rayEnd, Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking | Physics.CollisionProjectile);
}, rayStart, rayEnd, Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking | Physics.CollisionProjectile | Physics.CollisionLagCompensationBody);
return hits;
}
@@ -15,6 +15,9 @@ namespace Barotrauma.Items.Components
private Vector2 detectOffset;
private float updateTimer;
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
public bool MotionDetected { get; set; }
[Flags]
public enum TargetType
@@ -26,14 +29,25 @@ namespace Barotrauma.Items.Components
Any = Human | Monster | Wall | Pet,
}
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
public bool MotionDetected { get; set; }
private bool triggerFromHumans = true;
private bool triggerFromPets = true;
private bool triggerFromMonsters = true;
private TargetType _target;
[InGameEditable, Serialize(TargetType.Any, IsPropertySaveable.Yes, description: "Which kind of targets can trigger the sensor?", alwaysUseInstanceValues: true)]
public TargetType Target
{
get;
set;
get => _target;
set
{
if (_target != value)
{
_target = value;
triggerFromHumans = Target.HasFlag(TargetType.Human);
triggerFromPets = Target.HasFlag(TargetType.Pet);
triggerFromMonsters = Target.HasFlag(TargetType.Monster);
}
}
}
[Editable, Serialize("", IsPropertySaveable.Yes, description: "Does the sensor react only to certain characters (species names, groups or tags)? Doesn't have an effect, if the Target Type is incorrect.", alwaysUseInstanceValues: true)]
@@ -263,10 +277,7 @@ namespace Barotrauma.Items.Components
}
}
}
bool triggerFromHumans = Target.HasFlag(TargetType.Human);
bool triggerFromPets = Target.HasFlag(TargetType.Pet);
bool triggerFromMonsters = Target.HasFlag(TargetType.Monster);
bool hasTriggers = triggerFromHumans || triggerFromPets || triggerFromMonsters;
if (!hasTriggers) { return; }
foreach (Character character in Character.CharacterList)
@@ -299,9 +310,6 @@ namespace Barotrauma.Items.Components
public bool TriggersOn(Character character)
{
bool triggerFromHumans = Target.HasFlag(TargetType.Human);
bool triggerFromPets = Target.HasFlag(TargetType.Pet);
bool triggerFromMonsters = Target.HasFlag(TargetType.Monster);
bool hasTriggers = triggerFromHumans || triggerFromPets || triggerFromMonsters;
if (!hasTriggers) { return false; }
return TriggersOn(character, triggerFromHumans, triggerFromPets, triggerFromMonsters);
@@ -80,7 +80,6 @@ namespace Barotrauma.Items.Components
set
{
isOn = value;
CanTransfer = value;
if (!isOn)
{
currPowerConsumption = 0.0f;
@@ -96,8 +96,20 @@ namespace Barotrauma.Items.Components
[Editable, Serialize("> ", IsPropertySaveable.Yes)]
public string LineStartSymbol { get; set; }
[Editable, Serialize(false, IsPropertySaveable.No)]
public bool Readonly { get; set; }
private bool _readonly;
[Editable, Serialize(false, IsPropertySaveable.Yes)]
public bool Readonly
{
get => _readonly;
set
{
_readonly = value;
#if CLIENT
RefreshInputElements();
#endif
}
}
[Serialize(true, IsPropertySaveable.No)]
public bool AutoScrollToBottom { get; set; }
@@ -231,17 +231,22 @@ namespace Barotrauma.Items.Components
{
var should = GameMain.LuaCs.Hook.Call<bool?>("wifiSignalTransmitted", this, signal, sentFromChat);
if (should != null && should.Value)
return;
if (sentFromChat)
{
item.LastSentSignalRecipients.Clear();
}
if (should != null && should.Value) { return; }
bool chatMsgSent = false;
var receivers = GetReceiversInRange();
if (sentFromChat)
{
//if sent from chat, we need to reset the "signal chain" at this point
//so we can correctly detect which components the signal has already passed through to avoid infinite loops
//only relevant for signals originating from the chat - normally this is handled in Item.SendSignal
item.LastSentSignalRecipients.Clear();
foreach (WifiComponent receiver in receivers)
{
receiver.item.LastSentSignalRecipients.Clear();
}
}
foreach (WifiComponent wifiComp in receivers)
{
if (sentFromChat && !wifiComp.LinkToChat) { continue; }
@@ -1051,7 +1051,7 @@ namespace Barotrauma.Items.Components
}
if (TargetItems)
{
foreach (Item targetItem in Item.ItemList)
foreach (Item targetItem in Item.TurretTargetItems)
{
if (!IsValidTarget(targetItem)) { continue; }
float priority = isSlowTurret ? targetItem.Prefab.AISlowTurretPriority : targetItem.Prefab.AITurretPriority;
@@ -1395,7 +1395,7 @@ namespace Barotrauma.Items.Components
closestDistance = dist / priority;
currentTarget = closestEnemy;
}
foreach (Item targetItem in Item.ItemList)
foreach (Item targetItem in Item.TurretTargetItems)
{
if (!IsValidTarget(targetItem)) { continue; }
float priority = isSlowTurret ? targetItem.Prefab.AISlowTurretPriority : targetItem.Prefab.AITurretPriority;
@@ -1767,8 +1767,15 @@ namespace Barotrauma.Items.Components
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; }
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; }
}
else
{
if (sub.TeamID == Item.Submarine.TeamID) { return false; }
}
}
else if (targetBody.UserData is not Voronoi2.VoronoiCell { IsDestructible: true })
{
@@ -1786,6 +1793,8 @@ namespace Barotrauma.Items.Components
customPredicate: (Fixture f) =>
{
if (f.UserData is Item i && i.GetComponent<Turret>() != null) { return false; }
if (f.CollidesWith == Physics.CollisionNone) { return false; }
if (f.Body.UserData == item) { return false; }
if (f.UserData is Hull) { return false; }
return !item.StaticFixtures.Contains(f);
});
@@ -165,10 +165,11 @@ namespace Barotrauma
{
if (element.DoesAttributeReferenceFileNameAlone("texture"))
{
var basePrefab = WearableComponent.Item.Prefab.ParentPrefab ?? WearableComponent.Item.Prefab;
string textureName = element.GetAttributeString("texture", "");
return ContentPath.FromRaw(
element.ContentPackage,
$"{Path.GetDirectoryName(WearableComponent.Item.Prefab.FilePath)}/{textureName}");
$"{Path.GetDirectoryName(basePrefab.FilePath)}/{textureName}");
}
else
{