Build 0.21.6.0

This commit is contained in:
Markus Isberg
2023-01-31 18:01:29 +02:00
parent 697ec52120
commit 25fa5a9552
145 changed files with 2317 additions and 1145 deletions
@@ -289,7 +289,16 @@ namespace Barotrauma.Items.Components
return;
}
if (!(joint is WeldJoint))
if (joint == null)
{
string errorMsg = "Error while locking a docking port (joint between submarines doesn't exist)." +
" Submarine: " + (item.Submarine?.Info.Name ?? "null") +
", target submarine: " + (DockingTarget.item.Submarine?.Info.Name ?? "null");
GameAnalyticsManager.AddErrorEventOnce("DockingPort.Lock:JointNotCreated", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return;
}
if (joint is not WeldJoint)
{
DockingDir = GetDir(DockingTarget);
DockingTarget.DockingDir = -DockingDir;
@@ -1,12 +1,9 @@
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
#if CLIENT
using Barotrauma.Lights;
#endif
@@ -206,6 +203,8 @@ namespace Barotrauma.Items.Components
IsHorizontal = element.GetAttributeBool("horizontal", false);
canBePicked = element.GetAttributeBool("canbepicked", false);
autoOrientGap = element.GetAttributeBool("autoorientgap", false);
allowedSlots.Clear();
foreach (var subElement in element.Elements())
{
@@ -359,7 +358,10 @@ namespace Barotrauma.Items.Components
{
lastBrokenTime = Timing.TotalTime;
//the door has to be restored to 50% health before collision detection on the body is re-enabled
if (item.ConditionPercentage / Math.Max(item.MaxRepairConditionMultiplier, 1.0f) > 50.0f &&
//multiply by MaxRepairConditionMultiplier so the item gets repaired at 50% of the _default max condition_
//otherwise increasing the max condition is arguably harmful, as the door needs to be repaired further to re-enable the collider
if (item.ConditionPercentage * Math.Max(item.MaxRepairConditionMultiplier, 1.0f) > 50.0f &&
(GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
IsBroken = false;
@@ -58,9 +58,6 @@ namespace Barotrauma.Items.Components
set;
}
//the angle in which the Character holds the item
protected float holdAngle;
public PhysicsBody Body
{
get { return item.body ?? body; }
@@ -143,6 +140,7 @@ namespace Barotrauma.Items.Components
set { aimPos = ConvertUnits.ToSimUnits(value); }
}
protected float holdAngle;
#if DEBUG
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "The rotation at which the character holds the item (in degrees, relative to the rotation of the character's hand).")]
#else
@@ -154,6 +152,18 @@ namespace Barotrauma.Items.Components
set { holdAngle = MathHelper.ToRadians(value); }
}
protected float aimAngle;
#if DEBUG
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "The rotation at which the character holds the item while aiming (in degrees, relative to the rotation of the character's hand).")]
#else
[Serialize(0.0f, IsPropertySaveable.No)]
#endif
public float AimAngle
{
get { return MathHelper.ToDegrees(aimAngle); }
set { aimAngle = MathHelper.ToRadians(value); }
}
private Vector2 swingAmount;
#if DEBUG
[Editable, Serialize("0.0,0.0", IsPropertySaveable.No, description: "How much the item swings around when aiming/holding it (in pixels, as an offset from AimPos/HoldPos).")]
@@ -552,6 +562,7 @@ namespace Barotrauma.Items.Components
{
return false;
}
bool wasAttached = IsAttached;
if (base.OnPicked(picker))
{
DeattachFromWall();
@@ -560,7 +571,7 @@ namespace Barotrauma.Items.Components
if (GameMain.Server != null && attachable)
{
item.CreateServerEvent(this);
if (picker != null)
if (picker != null && wasAttached)
{
GameServer.Log(GameServer.CharacterLogName(picker) + " detached " + item.Name + " from a wall", ServerLog.MessageType.ItemInteraction);
}
@@ -688,16 +699,22 @@ namespace Barotrauma.Items.Components
if (maxAttachableCount == 0)
{
#if CLIENT
GUI.AddMessage(TextManager.Get("itemmsgrequiretraining"), Color.Red);
if (character == Character.Controlled)
{
GUI.AddMessage(TextManager.Get("itemmsgrequiretraining"), Color.Red);
}
#endif
return false;
}
else if (currentlyAttachedCount >= maxAttachableCount)
{
#if CLIENT
GUI.AddMessage($"{TextManager.Get("itemmsgtotalnumberlimited")} ({currentlyAttachedCount}/{maxAttachableCount})", Color.Red);
if (character == Character.Controlled)
{
GUI.AddMessage($"{TextManager.Get("itemmsgtotalnumberlimited")} ({currentlyAttachedCount}/{maxAttachableCount})", Color.Red);
}
#endif
return false;
return false;
}
}
@@ -875,9 +892,13 @@ namespace Barotrauma.Items.Components
scaledHandlePos[0] = handlePos[0] * item.Scale;
scaledHandlePos[1] = handlePos[1] * item.Scale;
bool aim = picker.IsKeyDown(InputType.Aim) && aimPos != Vector2.Zero && picker.CanAim;
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swingPos, aimPos + swingPos, aim, holdAngle);
if (!aim)
if (aim)
{
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swingPos, aimPos + swingPos, aim, holdAngle, aimAngle);
}
else
{
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swingPos, aimPos + swingPos, aim, holdAngle);
var rope = GetRope();
if (rope != null && rope.SnapWhenNotAimed && rope.Item.ParentInventory == null)
{
@@ -223,7 +223,7 @@ namespace Barotrauma.Items.Components
{
UpdateSwingPos(deltaTime, out Vector2 swingPos);
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 3f, MathHelper.PiOver4));
ac.HoldItem(deltaTime, item, handlePos, aimPos + swingPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos, aimMelee: true);
ac.HoldItem(deltaTime, item, handlePos, aimPos + swingPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos + aimAngle, aimMelee: true);
if (ac.InWater)
{
ac.LockFlipping();
@@ -472,8 +472,8 @@ namespace Barotrauma.Items.Components
}
if (GameMain.NetworkMember is { IsServer: true } server && targetEntity != null)
{
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, targetItemComponent: null, targetCharacter, targetLimb, targetEntity));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnUse, targetItemComponent: null, targetCharacter, targetLimb, targetEntity));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, targetItemComponent: null, targetCharacter, targetLimb, useTarget: targetEntity));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnUse, targetItemComponent: null, targetCharacter, targetLimb, useTarget: targetEntity));
serverLogger ??= new System.Text.StringBuilder();
serverLogger.Clear();
serverLogger.Append($"{picker?.LogName} used {item.Name}");
@@ -26,6 +26,8 @@ namespace Barotrauma.Items.Components
get { return allowedSlots; }
}
public bool PickingDone => pickTimer >= PickingTime;
public Character Picker
{
get
@@ -205,12 +205,12 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember is { IsServer: true })
{
GameMain.NetworkMember.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnSecondaryUse, this, CurrentThrower));
GameMain.NetworkMember.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnSecondaryUse, this, targetCharacter: CurrentThrower));
}
if (!(GameMain.NetworkMember is { IsClient: true }))
{
//Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, CurrentThrower, useTarget: CurrentThrower, user: CurrentThrower);
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, character: CurrentThrower, user: CurrentThrower);
}
throwState = ThrowState.None;
}
@@ -125,8 +125,8 @@ namespace Barotrauma.Items.Components
get { return drawable; }
set
{
if (value == drawable) return;
if (!(this is IDrawableComponent))
if (value == drawable) { return; }
if (this is not IDrawableComponent)
{
DebugConsole.ThrowError("Couldn't make \"" + this + "\" drawable (the component doesn't implement the IDrawableComponent interface)");
return;
@@ -236,10 +236,7 @@ namespace Barotrauma.Items.Components
set;
}
/// <summary>
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
/// </summary>
[Serialize(0f, IsPropertySaveable.No, description: "How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).")]
[Serialize(0f, IsPropertySaveable.No, description: "How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced). Note that there's also a generic BotPriority for all item prefabs.")]
public float CombatPriority { get; private set; }
/// <summary>
@@ -697,7 +694,7 @@ namespace Barotrauma.Items.Components
public virtual void FlipY(bool relativeToSub) { }
public bool IsLoaded(Character user, bool checkContainedItems = true) =>
public bool IsNotEmpty(Character user, bool checkContainedItems = true) =>
HasRequiredContainedItems(user, addMessage: false) &&
(!checkContainedItems || Item.OwnInventory == null || Item.OwnInventory.AllItems.Any(i => i.Condition > 0));
@@ -114,7 +114,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 contents in the item's inventory be visible? Disabled on items like magazines that spawn the contents as needed.")]
[Serialize(true, IsPropertySaveable.No, description: "Should the inventory of this item be visible when the item is selected.")]
public bool DrawInventory
{
get;
@@ -142,6 +142,9 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(true, IsPropertySaveable.No)]
public bool AllowAccess { get; set; }
[Serialize(false, IsPropertySaveable.No)]
public bool AccessOnlyWhenBroken { get; set; }
@@ -534,12 +537,12 @@ namespace Barotrauma.Items.Components
public override bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
{
return DrawInventory && (!AccessOnlyWhenBroken || Item.Condition <= 0) && base.HasRequiredItems(character, addMessage, msg);
return AllowAccess && (!AccessOnlyWhenBroken || Item.Condition <= 0) && base.HasRequiredItems(character, addMessage, msg);
}
public override bool Select(Character character)
{
if (!DrawInventory) { return false; }
if (!AllowAccess) { return false; }
if (item.Container != null) { return false; }
if (AccessOnlyWhenBroken)
{
@@ -575,7 +578,7 @@ namespace Barotrauma.Items.Components
public override bool Pick(Character picker)
{
if (!DrawInventory) { return false; }
if (!AllowAccess) { return false; }
if (AccessOnlyWhenBroken)
{
if (item.Condition > 0)
@@ -112,27 +112,34 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick to the character it hits.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the projectile stick to characters.")]
public bool StickToCharacters
{
get;
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick to the structure it hits.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the projectile stick to walls.")]
public bool StickToStructures
{
get;
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick to the item it hits.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the projectile stick to items.")]
public bool StickToItems
{
get;
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Can the projectile stick to doors. Caution: may cause issues.")]
public bool StickToDoors
{
get;
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick even to deflective targets.")]
public bool StickToDeflective
{
@@ -457,36 +464,36 @@ namespace Barotrauma.Items.Components
Vector2 rayEndWorld = rayStartWorld + dir * worldDist;
List<HitscanResult> hits = new List<HitscanResult>();
hits.AddRange(DoRayCast(rayStart, rayEnd, submarine: item.Submarine));
if (item.Submarine != null)
{
//shooting indoors, do a hitscan outside as well
hits.AddRange(DoRayCast(rayStart + item.Submarine.SimPosition, rayEnd + item.Submarine.SimPosition, submarine: null));
//also in the coordinate space of docked subs
foreach (Submarine dockedSub in item.Submarine.DockedTo)
{
if (dockedSub == item.Submarine) { continue; }
hits.AddRange(DoRayCast(rayStart + item.Submarine.SimPosition - dockedSub.SimPosition, rayEnd + item.Submarine.SimPosition - dockedSub.SimPosition, dockedSub));
}
//do a hitscan in other subs' coordinate spaces
RayCastInOtherSubs(rayStart + item.Submarine.SimPosition, rayEnd + item.Submarine.SimPosition);
}
else
{
RayCastInOtherSubs(rayStart, rayEnd);
}
void RayCastInOtherSubs(Vector2 rayStart, Vector2 rayEnd)
{
//shooting outdoors, see if we can hit anything inside a sub
foreach (Submarine submarine in Submarine.Loaded)
{
if (submarine == item.Submarine) { continue; }
var inSubHits = DoRayCast(rayStart - submarine.SimPosition, rayEnd - submarine.SimPosition, submarine);
//transform back to world coordinates
for (int i = 0; i < inSubHits.Count; i++)
{
inSubHits[i] = new HitscanResult(
inSubHits[i].Fixture,
inSubHits[i].Point + submarine.SimPosition,
inSubHits[i].Normal,
inSubHits[i].Fixture,
inSubHits[i].Point + submarine.SimPosition,
inSubHits[i].Normal,
inSubHits[i].Fraction);
}
hits.AddRange(inSubHits);
}
}
@@ -993,8 +1000,8 @@ namespace Barotrauma.Items.Components
}
if (GameMain.NetworkMember is { IsServer: true } server)
{
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, this, targetLimb.character, targetLimb, null, item.WorldPosition));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnImpact, this, targetLimb.character, targetLimb, null, item.WorldPosition));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, this, targetLimb.character, targetLimb, useTarget: targetLimb.character, item.WorldPosition));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnImpact, this, targetLimb.character, targetLimb, useTarget: targetLimb.character, item.WorldPosition));
}
}
else
@@ -1003,8 +1010,8 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnImpact, 1.0f, useTarget: target.Body.UserData as Entity, user: User);
if (GameMain.NetworkMember is { IsServer: true } server)
{
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, this, null, null, target.Body.UserData as Entity, item.WorldPosition));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnImpact, this, null, null, target.Body.UserData as Entity, item.WorldPosition));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, this, useTarget: target.Body.UserData as Entity, worldPosition: item.WorldPosition));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnImpact, this, useTarget: target.Body.UserData as Entity, worldPosition: item.WorldPosition));
}
}
}
@@ -1012,13 +1019,12 @@ namespace Barotrauma.Items.Components
target.Body.ApplyLinearImpulse(velocity * item.body.Mass);
target.Body.LinearVelocity = target.Body.LinearVelocity.ClampLength(NetConfig.MaxPhysicsBodyVelocity * 0.5f);
if (hits.Count() >= MaxTargetsToHit || hits.LastOrDefault()?.UserData is VoronoiCell)
if (hits.Count >= MaxTargetsToHit || hits.LastOrDefault()?.UserData is VoronoiCell)
{
DisableProjectileCollisions();
}
if (attackResult.AppliedDamageModifiers != null &&
(attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles) && !StickToDeflective))
if (attackResult.AppliedDamageModifiers != null && attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles) && !StickToDeflective)
{
item.body.LinearVelocity *= deflectedSpeedMultiplier;
}
@@ -1028,7 +1034,7 @@ namespace Barotrauma.Items.Components
((StickToLightTargets || target.Body.Mass > item.body.Mass * 0.5f) &&
(DoesStick ||
(StickToCharacters && (target.Body.UserData is Limb || target.Body.UserData is Character)) ||
(StickToItems && target.Body.UserData is Item))))
(target.Body.UserData is Item i && (i.GetComponent<Door>() != null ? StickToDoors : StickToItems)))))
{
Vector2 dir = new Vector2(
(float)Math.Cos(item.body.Rotation),
@@ -302,33 +302,16 @@ namespace Barotrauma.Items.Components
var sourceBody = GetBodyToPull(source);
if (sourceBody != null)
{
var targetBody = GetBodyToPull(target);
if (targetBody != null && targetBody.UserData is not Character)
if (user != null && user.InWater)
{
sourceBody.ApplyForce(targetBody.LinearVelocity * sourceBody.Mass);
}
float forceMultiplier = 1;
if (user != null)
{
user.AnimController.Hang();
if (user.InWater)
if (user.IsRagdolled)
{
if (user.IsRagdolled)
{
forceMultiplier = 0;
}
}
else
{
forceMultiplier = user.IsRagdolled ? 0.1f : 0.4f;
// Prevents too easy smashing to the walls
forceDir.X /= 4;
// Prevents rubberbanding up and down
if (forceDir.Y < 0)
{
forceDir.Y = 0;
}
// Reel in towards the target.
user.AnimController.Hang();
float force = LerpForces ? MathHelper.Lerp(0, SourcePullForce, MathUtils.InverseLerp(0, MaxLength / 2, distance)) : SourcePullForce;
sourceBody.ApplyForce(forceDir * force);
}
// Take the target velocity into account.
if (targetCharacter != null)
{
var myCollider = user.AnimController.Collider;
@@ -341,9 +324,15 @@ namespace Barotrauma.Items.Components
}
}
}
else
{
var targetBody = GetBodyToPull(target);
if (targetBody != null)
{
sourceBody.ApplyForce(targetBody.LinearVelocity * sourceBody.Mass);
}
}
}
float force = LerpForces ? MathHelper.Lerp(0, SourcePullForce, MathUtils.InverseLerp(0, MaxLength / 2, distance)) * forceMultiplier : SourcePullForce * forceMultiplier;
sourceBody.ApplyForce(forceDir * force);
}
}
}
@@ -187,6 +187,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Should the light sprite be drawn on the item using alpha blending, in addition to being rendered in the light map? Can be used to make the light sprite stand out more.")]
public bool AlphaBlend
{
get;
set;
}
public float TemporaryFlickerTimer;
public override void Move(Vector2 amount, bool ignoreContacts = false)
@@ -241,6 +248,7 @@ namespace Barotrauma.Items.Components
SetLightSourceState(IsActive);
turret = item.GetComponent<Turret>();
#if CLIENT
Drawable = AlphaBlend && Light.LightSprite != null;
if (Screen.Selected.IsEditor)
{
OnMapLoaded();
@@ -25,7 +25,7 @@ namespace Barotrauma.Items.Components
private string prevSignal;
private readonly int[] channelMemory = new int[ChannelMemorySize];
private int[] channelMemory = new int[ChannelMemorySize];
private Connection signalInConnection;
private Connection signalOutConnection;
@@ -94,7 +94,17 @@ namespace Barotrauma.Items.Components
{
list.Add(this);
IsActive = true;
channelMemory = element.GetAttributeIntArray("channelmemory", new int[ChannelMemorySize]);
}
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
{
base.Load(componentElement, usePrefabValues, idRemap);
channelMemory = componentElement.GetAttributeIntArray("channelmemory", new int[ChannelMemorySize]);
if (channelMemory.Length != ChannelMemorySize)
{
DebugConsole.AddWarning($"Error when loading item {item.Prefab.Identifier}: the size of the channel memory doesn't match the default value of {ChannelMemorySize}. Resizing...");
Array.Resize(ref channelMemory, ChannelMemorySize);
}
}
public override void OnItemLoaded()
@@ -44,7 +44,16 @@ namespace Barotrauma
}
public LimbType Limb { get; private set; }
public bool HideLimb { get; private set; }
public bool HideOtherWearables { get; private set; }
public enum ObscuringMode
{
None,
Hide,
AlphaClip
}
public ObscuringMode ObscureOtherWearables { get; private set; }
public bool HideOtherWearables => ObscureOtherWearables == ObscuringMode.Hide;
public bool AlphaClipOtherWearables => ObscureOtherWearables == ObscuringMode.AlphaClip;
public bool CanBeHiddenByOtherWearables { get; private set; }
public List<WearableType> HideWearablesOfType { get; private set; }
public bool InheritLimbDepth { get; private set; }
@@ -130,7 +139,7 @@ namespace Barotrauma
case WearableType.Husk:
case WearableType.Herpes:
Limb = LimbType.Head;
HideOtherWearables = false;
ObscureOtherWearables = ObscuringMode.None;
InheritLimbDepth = true;
InheritScale = true;
InheritOrigin = true;
@@ -202,7 +211,16 @@ namespace Barotrauma
Sprite = new Sprite(SourceElement, file: SpritePath);
Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true);
HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
foreach (var mode in Enum.GetValues<ObscuringMode>())
{
if (mode == ObscuringMode.None) { continue; }
if (SourceElement.GetAttributeBool($"{mode}OtherWearables", false))
{
ObscureOtherWearables = mode;
}
}
CanBeHiddenByOtherWearables = SourceElement.GetAttributeBool("canbehiddenbyotherwearables", true);
InheritLimbDepth = SourceElement.GetAttributeBool("inheritlimbdepth", true);
var scale = SourceElement.GetAttribute("inheritscale");