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
|
||||
{
|
||||
|
||||
@@ -331,13 +331,16 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<Item> GetAllItems(bool checkForDuplicates)
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
foreach (var item in slots[i].Items)
|
||||
var items = slots[i].Items;
|
||||
// ReSharper disable once ForCanBeConvertedToForeach, because this is performance-sensitive code.
|
||||
for (int j = 0; j < items.Count; j++)
|
||||
{
|
||||
var item = items[j];
|
||||
if (item == null)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -349,9 +352,9 @@ namespace Barotrauma
|
||||
if (checkForDuplicates)
|
||||
{
|
||||
bool duplicateFound = false;
|
||||
for (int j = 0; j < i; j++)
|
||||
for (int s = 0; s < i; s++)
|
||||
{
|
||||
if (slots[j].Items.Contains(item))
|
||||
if (slots[s].Items.Contains(item))
|
||||
{
|
||||
duplicateFound = true;
|
||||
break;
|
||||
@@ -364,7 +367,7 @@ namespace Barotrauma
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifyItemComponentsOfChange()
|
||||
@@ -420,12 +423,17 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsIndexInRange(int index)
|
||||
{
|
||||
return index >= 0 && index < slots.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the item stored in the specified inventory slot. If the slot contains a stack of items, returns the first item in the stack.
|
||||
/// </summary>
|
||||
public Item GetItemAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= slots.Length) { return null; }
|
||||
if (!IsIndexInRange(index)) { return null; }
|
||||
return slots[index].FirstOrDefault();
|
||||
}
|
||||
|
||||
@@ -434,14 +442,13 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public IEnumerable<Item> GetItemsAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= slots.Length) { return Enumerable.Empty<Item>(); }
|
||||
if (!IsIndexInRange(index)) { return Enumerable.Empty<Item>(); }
|
||||
return slots[index].Items;
|
||||
}
|
||||
|
||||
public int GetItemStackSlotIndex(Item item, int index)
|
||||
{
|
||||
if (index < 0 || index >= slots.Length) { return -1; }
|
||||
|
||||
if (!IsIndexInRange(index)) { return -1; }
|
||||
return slots[index].Items.IndexOf(item);
|
||||
}
|
||||
|
||||
@@ -476,7 +483,7 @@ namespace Barotrauma
|
||||
public virtual bool ItemOwnsSelf(Item item)
|
||||
{
|
||||
if (Owner == null) { return false; }
|
||||
if (!(Owner is Item)) { return false; }
|
||||
if (Owner is not Item) { return false; }
|
||||
Item ownerItem = Owner as Item;
|
||||
if (ownerItem == item) { return true; }
|
||||
if (ownerItem.ParentInventory == null) { return false; }
|
||||
@@ -519,7 +526,7 @@ namespace Barotrauma
|
||||
public virtual bool CanBePutInSlot(Item item, int i, bool ignoreCondition = false)
|
||||
{
|
||||
if (ItemOwnsSelf(item)) { return false; }
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
if (!IsIndexInRange(i)) { return false; }
|
||||
return slots[i].CanBePut(item, ignoreCondition);
|
||||
}
|
||||
|
||||
@@ -539,7 +546,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition = null, int? quality = null)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
if (!IsIndexInRange(i)) { return false; }
|
||||
return slots[i].CanProbablyBePut(itemPrefab, condition, quality);
|
||||
}
|
||||
|
||||
@@ -555,7 +562,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return 0; }
|
||||
if (!IsIndexInRange(i)) { return 0; }
|
||||
return slots[i].HowManyCanBePut(itemPrefab, condition: condition, ignoreItemsInSlot: ignoreItemsInSlot);
|
||||
}
|
||||
|
||||
@@ -573,7 +580,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool ignoreCondition = false)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length)
|
||||
if (!IsIndexInRange(i))
|
||||
{
|
||||
string thisItemStr = item?.Prefab.Identifier.Value ?? "null";
|
||||
string ownerStr = "null";
|
||||
@@ -637,7 +644,7 @@ namespace Barotrauma
|
||||
|
||||
protected virtual void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length)
|
||||
if (!IsIndexInRange(i))
|
||||
{
|
||||
string errorMsg = "Inventory.PutItem failed: index was out of range(" + i + ").\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
GameAnalyticsManager.AddErrorEventOnce("Inventory.PutItem:IndexOutOfRange", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
@@ -1097,10 +1104,16 @@ namespace Barotrauma
|
||||
|
||||
public bool IsInSlot(Item item, int index)
|
||||
{
|
||||
if (index < 0 || index >= slots.Length) { return false; }
|
||||
if (!IsIndexInRange(index)) { return false; }
|
||||
return slots[index].Contains(item);
|
||||
}
|
||||
|
||||
public bool IsSlotEmpty(int index)
|
||||
{
|
||||
if (!IsIndexInRange(index)) { return false; }
|
||||
return slots[index].Empty();
|
||||
}
|
||||
|
||||
public void SharedRead(IReadMessage msg, List<ushort>[] receivedItemIds, out bool readyToApply)
|
||||
{
|
||||
byte start = msg.ReadByte();
|
||||
|
||||
@@ -24,42 +24,66 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Item : MapEntity, IDamageable, IIgnorable, ISerializableEntity, IServerPositionSync, IClientSerializable
|
||||
{
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// A list of every item that exists somewhere in the world. Note that there can be a huge number of items in the list,
|
||||
/// and you probably shouldn't be enumerating it to find some that match some specific criteria (unless that's done very, very sparsely or during initialization).
|
||||
/// </summary>
|
||||
public static readonly List<Item> ItemList = new List<Item>();
|
||||
|
||||
private static readonly HashSet<Item> dangerousItems = new HashSet<Item>();
|
||||
private static readonly HashSet<Item> _dangerousItems = new HashSet<Item>();
|
||||
|
||||
public static IReadOnlyCollection<Item> DangerousItems { get { return dangerousItems; } }
|
||||
public static IReadOnlyCollection<Item> DangerousItems => _dangerousItems;
|
||||
|
||||
private static readonly List<Item> repairableItems = new List<Item>();
|
||||
private static readonly List<Item> _repairableItems = new List<Item>();
|
||||
|
||||
/// <summary>
|
||||
/// Items that have one more more Repairable component
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Item> RepairableItems => repairableItems;
|
||||
public static IReadOnlyCollection<Item> RepairableItems => _repairableItems;
|
||||
|
||||
private static readonly List<Item> cleanableItems = new List<Item>();
|
||||
private static readonly List<Item> _cleanableItems = new List<Item>();
|
||||
|
||||
/// <summary>
|
||||
/// Items that may potentially need to be cleaned up (pickable, not attached to a wall, and not inside a valid container)
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Item> CleanableItems => cleanableItems;
|
||||
public static IReadOnlyCollection<Item> CleanableItems => _cleanableItems;
|
||||
|
||||
private static readonly HashSet<Item> deconstructItems = new HashSet<Item>();
|
||||
private static readonly HashSet<Item> _deconstructItems = new HashSet<Item>();
|
||||
|
||||
/// <summary>
|
||||
/// Items that have been marked for deconstruction
|
||||
/// </summary>
|
||||
public static HashSet<Item> DeconstructItems => deconstructItems;
|
||||
public static HashSet<Item> DeconstructItems => _deconstructItems;
|
||||
|
||||
private static readonly List<Item> sonarVisibleItems = new List<Item>();
|
||||
private static readonly List<Item> _sonarVisibleItems = new List<Item>();
|
||||
|
||||
/// <summary>
|
||||
/// Items whose <see cref="ItemPrefab.SonarSize"/> is larger than 0
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Item> SonarVisibleItems => sonarVisibleItems;
|
||||
public static IReadOnlyCollection<Item> SonarVisibleItems => _sonarVisibleItems;
|
||||
|
||||
private static readonly List<Item> _turretTargetItems = new List<Item>();
|
||||
|
||||
/// <summary>
|
||||
/// Items whose <see cref="ItemPrefab.IsAITurretTarget"/> is true.
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Item> TurretTargetItems => _turretTargetItems;
|
||||
|
||||
private static readonly List<Item> _chairItems = new List<Item>();
|
||||
|
||||
/// <summary>
|
||||
/// Items that have the tag <see cref="Tags.ChairItem"/>. Which is an oddly specific thing, but useful as an optimization for NPC AI.
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Item> ChairItems => _chairItems;
|
||||
|
||||
#endregion
|
||||
|
||||
public new ItemPrefab Prefab => base.Prefab as ItemPrefab;
|
||||
|
||||
public override ContentPackage ContentPackage => Prefab?.ContentPackage;
|
||||
|
||||
public static bool ShowLinks = true;
|
||||
|
||||
private HashSet<Identifier> tags;
|
||||
@@ -104,8 +128,9 @@ namespace Barotrauma
|
||||
#endif
|
||||
|
||||
//components that determine the functionality of the item
|
||||
private readonly Dictionary<Type, ItemComponent> componentsByType = new Dictionary<Type, ItemComponent>();
|
||||
private readonly Dictionary<Type, List<ItemComponent>> componentsByType = new Dictionary<Type, List<ItemComponent>>();
|
||||
private readonly List<ItemComponent> components;
|
||||
|
||||
/// <summary>
|
||||
/// Components that are Active or need to be updated for some other reason (status effects, sounds)
|
||||
/// </summary>
|
||||
@@ -496,6 +521,22 @@ namespace Barotrauma
|
||||
Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight);
|
||||
}
|
||||
|
||||
//need to update to get the position of the physics body to match the new center of the item
|
||||
if (body != null)
|
||||
{
|
||||
if (FullyInitialized)
|
||||
{
|
||||
//fully intialized = scaling after the item has been created
|
||||
//if this happens in the editor, refresh the transform to get the rect to match the position of the physics body
|
||||
if (Screen.Selected is { IsEditor: true }) { UpdateTransform(); }
|
||||
}
|
||||
else
|
||||
{
|
||||
//scaling during loading -> move the body to the new center of the rect
|
||||
body.SetTransformIgnoreContacts(ConvertUnits.ToSimUnits(base.Position), body.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (components != null)
|
||||
{
|
||||
foreach (ItemComponent component in components)
|
||||
@@ -1303,9 +1344,11 @@ namespace Barotrauma
|
||||
|
||||
InsertToList();
|
||||
ItemList.Add(this);
|
||||
if (Prefab.IsDangerous) { dangerousItems.Add(this); }
|
||||
if (Repairables.Any()) { repairableItems.Add(this); }
|
||||
if (Prefab.SonarSize > 0.0f) { sonarVisibleItems.Add(this); }
|
||||
if (Prefab.IsDangerous) { _dangerousItems.Add(this); }
|
||||
if (Repairables.Any()) { _repairableItems.Add(this); }
|
||||
if (Prefab.SonarSize > 0.0f) { _sonarVisibleItems.Add(this); }
|
||||
if (Prefab.IsAITurretTarget) { _turretTargetItems.Add(this); }
|
||||
if (Prefab.Tags.Contains(Barotrauma.Tags.ChairItem)) { _chairItems.Add(this); }
|
||||
CheckCleanable();
|
||||
|
||||
DebugConsole.Log("Created " + Name + " (" + ID + ")");
|
||||
@@ -1487,17 +1530,24 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
Type type = component.GetType();
|
||||
if (!componentsByType.ContainsKey(type))
|
||||
CacheComponent(type);
|
||||
Type baseType = type.BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
componentsByType.Add(type, component);
|
||||
Type baseType = type.BaseType;
|
||||
while (baseType != null && baseType != typeof(ItemComponent))
|
||||
CacheComponent(baseType);
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
|
||||
void CacheComponent(Type t)
|
||||
{
|
||||
if (!componentsByType.TryGetValue(t, out List<ItemComponent> cachedComponents))
|
||||
{
|
||||
if (!componentsByType.ContainsKey(baseType))
|
||||
{
|
||||
componentsByType.Add(baseType, component);
|
||||
}
|
||||
baseType = baseType.BaseType;
|
||||
cachedComponents = new List<ItemComponent>();
|
||||
componentsByType.Add(t, cachedComponents);
|
||||
}
|
||||
if (!cachedComponents.Contains(component))
|
||||
{
|
||||
cachedComponents.Add(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1534,15 +1584,11 @@ namespace Barotrauma
|
||||
|
||||
public T GetComponent<T>() where T : ItemComponent
|
||||
{
|
||||
if (componentsByType.TryGetValue(typeof(T), out ItemComponent component))
|
||||
if (componentsByType.TryGetValue(typeof(T), out List<ItemComponent> matchingComponents))
|
||||
{
|
||||
return (T)component;
|
||||
return (T)matchingComponents.First();
|
||||
}
|
||||
if (typeof(T) == typeof(ItemComponent))
|
||||
{
|
||||
return (T)components.FirstOrDefault();
|
||||
}
|
||||
return default;
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetComponents<T>()
|
||||
@@ -1551,8 +1597,11 @@ namespace Barotrauma
|
||||
{
|
||||
return components.Cast<T>();
|
||||
}
|
||||
if (!componentsByType.ContainsKey(typeof(T))) { return Enumerable.Empty<T>(); }
|
||||
return components.Where(c => c is T).Cast<T>();
|
||||
if (componentsByType.TryGetValue(typeof(T), out List<ItemComponent> matchingComponents))
|
||||
{
|
||||
return matchingComponents.Cast<T>();
|
||||
}
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
|
||||
public float GetQualityModifier(Quality.StatType statType)
|
||||
@@ -1563,7 +1612,7 @@ namespace Barotrauma
|
||||
public void RemoveContained(Item contained)
|
||||
{
|
||||
ownInventory?.RemoveItem(contained);
|
||||
contained.Container = null;
|
||||
contained.Container = null;
|
||||
}
|
||||
|
||||
public void SetTransform(Vector2 simPosition, float rotation, bool findNewHull = true, bool setPrevTransform = true)
|
||||
@@ -1588,14 +1637,7 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
#endif
|
||||
if (!body.PhysEnabled || Submarine.Unloading)
|
||||
{
|
||||
body.SetTransformIgnoreContacts(simPosition, rotation, setPrevTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
body.SetTransform(simPosition, rotation, setPrevTransform);
|
||||
}
|
||||
body.SetTransformIgnoreContacts(simPosition, rotation, setPrevTransform);
|
||||
#if DEBUG
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -1651,14 +1693,14 @@ namespace Barotrauma
|
||||
Prefab.PreferredContainers.Any() &&
|
||||
(container == null || container.HasTag(Barotrauma.Tags.AllowCleanup)))
|
||||
{
|
||||
if (!cleanableItems.Contains(this))
|
||||
if (!_cleanableItems.Contains(this))
|
||||
{
|
||||
cleanableItems.Add(this);
|
||||
_cleanableItems.Add(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanableItems.Remove(this);
|
||||
_cleanableItems.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1847,9 +1889,9 @@ namespace Barotrauma
|
||||
|
||||
public void SetContainedItemPositions()
|
||||
{
|
||||
foreach (ItemComponent component in components)
|
||||
foreach (var ownInventory in OwnInventories)
|
||||
{
|
||||
(component as ItemContainer)?.SetContainedItemPositions();
|
||||
ownInventory.Container.SetContainedItemPositions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2508,15 +2550,15 @@ namespace Barotrauma
|
||||
|
||||
if (Submarine == null && prevSub != null)
|
||||
{
|
||||
body.SetTransform(body.SimPosition + prevSub.SimPosition, body.Rotation);
|
||||
body.SetTransformIgnoreContacts(body.SimPosition + prevSub.SimPosition, body.Rotation);
|
||||
}
|
||||
else if (Submarine != null && prevSub == null)
|
||||
{
|
||||
body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
body.SetTransformIgnoreContacts(body.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
}
|
||||
else if (Submarine != null && prevSub != null && Submarine != prevSub)
|
||||
{
|
||||
body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
body.SetTransformIgnoreContacts(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
}
|
||||
|
||||
if (Submarine != prevSub)
|
||||
@@ -3416,7 +3458,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (setTransform)
|
||||
{
|
||||
body.SetTransform(dropper.SimPosition, 0.0f);
|
||||
body.SetTransformIgnoreContacts(dropper.SimPosition, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4091,7 +4133,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (element.GetAttributeBool("markedfordeconstruction", false)) { deconstructItems.Add(item); }
|
||||
if (element.GetAttributeBool("markedfordeconstruction", false)) { _deconstructItems.Add(item); }
|
||||
|
||||
float prevRotation = item.Rotation;
|
||||
if (element.GetAttributeBool("flippedx", false)) { item.FlipX(false); }
|
||||
@@ -4379,7 +4421,7 @@ namespace Barotrauma
|
||||
new XAttribute("name", Prefab.OriginalName),
|
||||
new XAttribute("identifier", Prefab.Identifier),
|
||||
new XAttribute("ID", ID),
|
||||
new XAttribute("markedfordeconstruction", deconstructItems.Contains(this)));
|
||||
new XAttribute("markedfordeconstruction", _deconstructItems.Contains(this)));
|
||||
|
||||
if (PendingItemSwap != null)
|
||||
{
|
||||
@@ -4583,11 +4625,13 @@ namespace Barotrauma
|
||||
private void RemoveFromLists()
|
||||
{
|
||||
ItemList.Remove(this);
|
||||
dangerousItems.Remove(this);
|
||||
repairableItems.Remove(this);
|
||||
sonarVisibleItems.Remove(this);
|
||||
cleanableItems.Remove(this);
|
||||
deconstructItems.Remove(this);
|
||||
_dangerousItems.Remove(this);
|
||||
_repairableItems.Remove(this);
|
||||
_sonarVisibleItems.Remove(this);
|
||||
_cleanableItems.Remove(this);
|
||||
_deconstructItems.Remove(this);
|
||||
_turretTargetItems.Remove(this);
|
||||
_chairItems.Remove(this);
|
||||
RemoveFromDroppedStack(allowClientExecute: true);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ namespace Barotrauma
|
||||
public readonly int Amount;
|
||||
public readonly int? Quality;
|
||||
public readonly bool HideForNonTraitors;
|
||||
public readonly InvSlotType MoveToSlot;
|
||||
|
||||
/// <summary>
|
||||
/// How many of this item the fabricator can create (< 0 = unlimited)
|
||||
@@ -257,6 +258,7 @@ namespace Barotrauma
|
||||
FabricationLimitMax = element.GetAttributeInt(nameof(FabricationLimitMax), limitDefault);
|
||||
|
||||
HideForNonTraitors = element.GetAttributeBool(nameof(HideForNonTraitors), false);
|
||||
MoveToSlot = element.GetAttributeEnum(nameof(MoveToSlot), InvSlotType.None);
|
||||
|
||||
if (element.GetAttribute(nameof(Quality)) != null)
|
||||
{
|
||||
@@ -1000,7 +1002,7 @@ namespace Barotrauma
|
||||
ParseConfigElement(variantOf: null);
|
||||
}
|
||||
|
||||
private string GetTexturePath(ContentXElement subElement, ItemPrefab variantOf)
|
||||
public string GetTexturePath(ContentXElement subElement, ItemPrefab variantOf)
|
||||
=> subElement.DoesAttributeReferenceFileNameAlone("texture")
|
||||
? Path.GetDirectoryName(variantOf?.ContentFile.Path ?? ContentFile.Path)
|
||||
: "";
|
||||
|
||||
Reference in New Issue
Block a user