Merge remote-tracking branch 'upstream/master' into develop
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user